/* USB EHCI Host for Teensy 3.6 * Copyright 2017 Paul Stoffregen (paul@pjrc.com) * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "USBHost_t36.h" // Read this header first for key info #define print USBHost::print_ #define println USBHost::println_ //#define DEBUG_JOYSTICK #ifdef DEBUG_JOYSTICK #define DBGPrintf USBHDBGSerial.printf #else #define DBGPrintf(...) #endif template const T& clamp(const T& x, const T& lower, const T& upper) { return min(upper, max(x, lower)); } bool ack_rvd = false; bool JoystickController::queue_Data_Transfer_Debug(Pipe_t *pipe, void *buffer, uint32_t len, USBDriver *driver, uint32_t line) { if ((pipe == nullptr) || (driver == nullptr) || ((len > 0) && (buffer == nullptr))) { // something wrong: DBGPrintf("\n !!!!!!!!!!! JoystickController::queue_Data_Transfer called with bad data line: %u\n", line); DBGPrintf("\t pipe:%p buffer:%p len:%u driver:%p\n", pipe, buffer, len, driver); return false; } return queue_Data_Transfer(pipe, buffer, len, driver); } // PID/VID to joystick mapping - Only the XBOXOne is used to claim the USB interface directly, // The others are used after claim-hid code to know which one we have and to use it for // doing other features. JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { { 0x045e, 0x02dd, XBOXONE, false }, // Xbox One Controller { 0x045e, 0x02ea, XBOXONE, false }, // Xbox One S Controller { 0x045e, 0x0b12, XBOXONE, false }, // Xbox Core Controller (Series S/X) { 0x045e, 0x0719, XBOX360, false}, { 0x045e, 0x028E, SWITCH, false}, // Switch? { 0x057E, 0x2009, SWITCH, true}, // Switch Pro controller. // Let the swtich grab it, but... { 0x0079, 0x201C, SWITCH, false}, { 0x054C, 0x0268, PS3, true}, { 0x054C, 0x042F, PS3, true}, // PS3 Navigation controller { 0x054C, 0x03D5, PS3_MOTION, true}, // PS3 Motion controller { 0x054C, 0x05C4, PS4, true}, {0x054C, 0x09CC, PS4, true }, { 0x0A5C, 0x21E8, PS4, true}, { 0x046D, 0xC626, SpaceNav, true}, // 3d Connextion Space Navigator, 0x10008 { 0x046D, 0xC628, SpaceNav, true} // 3d Connextion Space Navigator, 0x10008 }; //----------------------------------------------------------------------------- // Switch controller Bluetooth config structure. //----------------------------------------------------------------------------- static uint8_t switch_packet_num = 0; struct SWProBTSendConfigData { uint8_t hid_hdr; uint8_t id; uint8_t gpnum; //GlobalPacketNumber uint8_t rumbleDataL[4]; uint8_t rumbleDataR[4]; uint8_t subCommand; uint8_t subCommandData[38]; } __attribute__((packed)); struct SWProBTSendConfigData1 { uint8_t hid_hdr; uint8_t id; uint8_t gpnum; //GlobalPacketNumber uint8_t subCommand; uint8_t subCommandData[38]; } __attribute__((packed)); struct SWProIMUCalibration { int16_t acc_offset[3]; int16_t acc_sensitivity[3] = {16384, 16384, 16384}; int16_t gyro_offset[3]; int16_t gyro_sensitivity[3] = {15335, 15335, 15335}; } __attribute__((packed)); struct SWProIMUCalibration SWIMUCal; struct SWProStickCalibration { int16_t rstick_center_x; int16_t rstick_center_y; int16_t rstick_x_min; int16_t rstick_x_max; int16_t rstick_y_min; int16_t rstick_y_max; int16_t lstick_center_x; int16_t lstick_center_y; int16_t lstick_x_min; int16_t lstick_x_max; int16_t lstick_y_min; int16_t lstick_y_max; int16_t deadzone_left; int16_t deadzone_right; } __attribute__((packed)); struct SWProStickCalibration SWStickCal; //----------------------------------------------------------------------------- void JoystickController::init() { contribute_Pipes(mypipes, sizeof(mypipes) / sizeof(Pipe_t)); contribute_Transfers(mytransfers, sizeof(mytransfers) / sizeof(Transfer_t)); contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs) / sizeof(strbuf_t)); driver_ready_for_device(this); USBHIDParser::driver_ready_for_hid_collection(this); BluetoothController::driver_ready_for_bluetooth(this); } //----------------------------------------------------------------------------- JoystickController::joytype_t JoystickController::mapVIDPIDtoJoystickType(uint16_t idVendor, uint16_t idProduct, bool exclude_hid_devices) { for (uint8_t i = 0; i < (sizeof(pid_vid_mapping) / sizeof(pid_vid_mapping[0])); i++) { if ((idVendor == pid_vid_mapping[i].idVendor) && (idProduct == pid_vid_mapping[i].idProduct)) { println("Match PID/VID: ", i, DEC); if (exclude_hid_devices && pid_vid_mapping[i].hidDevice) return UNKNOWN; return pid_vid_mapping[i].joyType; } } return UNKNOWN; // Not in our list } //***************************************************************************** // Some simple query functions depend on which interface we are using... //***************************************************************************** uint16_t JoystickController::idVendor() { if (device != nullptr) return device->idVendor; if (mydevice != nullptr) return mydevice->idVendor; return 0; } uint16_t JoystickController::idProduct() { if (device != nullptr) return device->idProduct; if (mydevice != nullptr) return mydevice->idProduct; return 0; } const uint8_t *JoystickController::manufacturer() { if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; //if ((btdevice != nullptr) && (btdevice->strbuf != nullptr)) return &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; return nullptr; } const uint8_t *JoystickController::product() { if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; if (btconnect != nullptr) return btconnect->remote_name_; return nullptr; } const uint8_t *JoystickController::serialNumber() { if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; if ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; return nullptr; } static uint8_t rumble_counter = 0; bool JoystickController::setRumble(uint8_t lValue, uint8_t rValue, uint8_t timeout) { // Need to know which joystick we are on. Start off with XBox support - maybe need to add some enum value for the known // joystick types. rumble_lValue_ = lValue; rumble_rValue_ = rValue; rumble_timeout_ = timeout; switch (joystickType_) { default: break; case PS3: return transmitPS3UserFeedbackMsg(); case PS3_MOTION: return transmitPS3MotionUserFeedbackMsg(); case PS4: return transmitPS4UserFeedbackMsg(); case XBOXONE: // Lets try sending a request to the XBox 1. if (btdriver_) { DBGPrintf("\nXBOXONE BT Joystick update Rumble %d %d %d\n", lValue, rValue, timeout); txbuf_[0] = 0xA2; // HID BT DATA (0xA0) | Report Type (Output 0x02) txbuf_[1] = 0x03; // ID 0x03 txbuf_[2] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) txbuf_[3] = map(lValue, 0, 1023, 0, 100); // lT force txbuf_[4] = map(rValue, 0, 1023, 0, 100); // rT force txbuf_[5] = map(lValue, 0, 1023, 0, 100); // L force txbuf_[6] = map(rValue, 0, 1023, 0, 100); // R force txbuf_[7] = 0xff; // Length of pulse txbuf_[8] = 0x00; // Period between pulses txbuf_[9] = 0x00; // Repeat btdriver_->sendL2CapCommand(txbuf_, 10, BluetoothController::INTERRUPT_SCID); return true; } txbuf_[0] = 0x9; txbuf_[1] = 0x0; txbuf_[2] = 0x0; txbuf_[3] = 0x09; // Substructure (what substructure rest of this packet has) txbuf_[4] = 0x00; // Mode txbuf_[5] = 0x0f; // Rumble mask (what motors are activated) (0000 lT rT L R) txbuf_[6] = 0x0; // lT force txbuf_[7] = 0x0; // rT force txbuf_[8] = lValue; // L force txbuf_[9] = rValue; // R force txbuf_[10] = 0xff; // Length of pulse txbuf_[11] = 0x00; // Period between pulses txbuf_[12] = 0x00; // Repeat if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 13, this, __LINE__)) { println("XBoxOne rumble transfer fail"); } return true; // case XBOX360: txbuf_[0] = 0x00; txbuf_[1] = 0x01; txbuf_[2] = 0x0F; txbuf_[3] = 0xC0; txbuf_[4] = 0x00; txbuf_[5] = lValue; txbuf_[6] = rValue; txbuf_[7] = 0x00; txbuf_[8] = 0x00; txbuf_[9] = 0x00; txbuf_[10] = 0x00; txbuf_[11] = 0x00; if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 12, this, __LINE__)) { println("XBox360 rumble transfer fail"); } return true; case SWITCH: if (btdriver_) { struct SWProBTSendConfigData *packet = (struct SWProBTSendConfigData *)txbuf_ ; memset((void*)packet, 0, sizeof(struct SWProBTSendConfigData)); packet->hid_hdr = 0xA2; // HID BT Get_report (0xA0) | Report Type (Output) packet->id = 0x10; if(switch_packet_num > 0x10) switch_packet_num = 0; packet->gpnum = switch_packet_num; switch_packet_num = (switch_packet_num + 1) & 0x0f; // 2-9 rumble data; Serial.printf("Set Rumble data: %d, %d\n", lValue, rValue); // 2-9 rumble data; uint8_t rumble_on[8] = {0x28, 0x88, 0x60, 0x61, 0x28, 0x88, 0x60, 0x61}; uint8_t rumble_off[8] = {0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40}; //if ((lValue == 0x00) && (rValue == 0x00)) { // for(uint8_t i = 0; i < 4; i++) packet->rumbleDataR[i] = rumble_off[i]; // for(uint8_t i = 4; i < 8; i++) packet->rumbleDataL[i-4] = rumble_off[i]; //} if ((lValue != 0x0) || (rValue != 0x0)) { if (lValue != 0 && rValue == 0) { for(uint8_t i = 0; i < 4; i++) packet->rumbleDataR[i] = rumble_off[i]; for(uint8_t i = 4; i < 8; i++) packet->rumbleDataL[i-4] = rumble_on[i]; } else if (rValue != 0 && lValue == 0) { for(uint8_t i = 0; i < 4; i++) packet->rumbleDataR[i] = rumble_on[i]; for(uint8_t i = 4; i < 8; i++) packet->rumbleDataL[i-4] = rumble_off[i]; } else if (rValue != 0 && lValue != 0) { for(uint8_t i = 0; i < 4; i++) packet->rumbleDataR[i] = rumble_on[i]; for(uint8_t i = 4; i < 8; i++) packet->rumbleDataL[i-4] = rumble_on[i]; } } packet->subCommand = 0x0; packet->subCommandData[0] = 0; btdriver_->sendL2CapCommand((uint8_t *)packet, sizeof(struct SWProBTSendConfigData), BluetoothController::INTERRUPT_SCID /*0x40*/); return true; } Serial.printf("Set Rumble data (USB): %d, %d\n", lValue, rValue); memset(txbuf_, 0, 18); // make sure it is cleared out //txbuf_[0] = 0x80; //txbuf_[1] = 0x92; //txbuf_[3] = 0x31; txbuf_[0] = 0x10; // Command // Now add in subcommand data: // Probably do this better soon if(switch_packet_num > 0x10) switch_packet_num = 0; txbuf_[1 + 0] = switch_packet_num; switch_packet_num = (switch_packet_num + 1) & 0x0f; // uint8_t rumble_on[8] = {0x28, 0x88, 0x60, 0x61, 0x28, 0x88, 0x60, 0x61}; uint8_t rumble_off[8] = {0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40}; //if ((lValue == 0x00) && (rValue == 0x00)) { // for(uint8_t i = 0; i < 4; i++) packet->rumbleDataR[i] = rumble_off[i]; // for(uint8_t i = 4; i < 8; i++) packet->rumbleDataL[i-4] = rumble_off[i]; //} if ((lValue != 0x0) || (rValue != 0x0)) { if (lValue != 0 && rValue == 0x00) { for(uint8_t i = 0; i < 4; i++) txbuf_[i + 2] = rumble_off[i]; for(uint8_t i = 4; i < 8; i++) txbuf_[i - 4 + 6] = rumble_on[i]; } else if (rValue != 0 && lValue == 0x00) { for(uint8_t i = 0; i < 4; i++) txbuf_[i + 2] = rumble_on[i]; for(uint8_t i = 4; i < 8; i++) txbuf_[i - 4 + 6] = rumble_off[i]; } else if (rValue != 0 && lValue != 0) { for(uint8_t i = 0; i < 4; i++) txbuf_[i + 2] = rumble_on[i]; for(uint8_t i = 4; i < 8; i++) txbuf_[i - 4 + 6] = rumble_on[i]; } } txbuf_[11] = 0x00; txbuf_[12] = 0x00; if(driver_) { driver_->sendPacket(txbuf_, 18); } else if (txpipe_) { if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 12, this, __LINE__)) { println("switch transfer fail"); Serial.printf("Switch transfer fail\n"); } } return true; } return false; } bool JoystickController::setLEDs(uint8_t lr, uint8_t lg, uint8_t lb) { // Need to know which joystick we are on. Start off with XBox support - maybe need to add some enum value for the known // joystick types. DBGPrintf("::setLEDS(%x %x %x)\n", lr, lg, lb); if ((leds_[0] != lr) || (leds_[1] != lg) || (leds_[2] != lb)) { leds_[0] = lr; leds_[1] = lg; leds_[2] = lb; switch (joystickType_) { case PS3: return transmitPS3UserFeedbackMsg(); case PS3_MOTION: return transmitPS3MotionUserFeedbackMsg(); case PS4: return transmitPS4UserFeedbackMsg(); case XBOX360: // 0: off, 1: all blink then return to before // 2-5(TL, TR, BL, BR) - blink on then stay on // 6-9() - On // ... txbuf_[1] = 0x00; txbuf_[2] = 0x08; txbuf_[3] = 0x40 + lr; txbuf_[4] = 0x00; txbuf_[5] = 0x00; txbuf_[6] = 0x00; txbuf_[7] = 0x00; txbuf_[8] = 0x00; txbuf_[9] = 0x00; txbuf_[10] = 0x00; txbuf_[11] = 0x00; if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 12, this, __LINE__)) { println("XBox360 set leds fail"); } return true; case SWITCH: if (btdriver_) { DBGPrintf("Init LEDs\n"); struct SWProBTSendConfigData *packet = (struct SWProBTSendConfigData *)txbuf_ ; memset((void*)packet, 0, sizeof(struct SWProBTSendConfigData)); packet->hid_hdr = 0xA2; // HID BT Get_report (0xA0) | Report Type (Output) packet->id = 1; packet->gpnum = switch_packet_num; switch_packet_num = (switch_packet_num + 1) & 0x0f; // 2-9 rumble data; /*packet->rumbleDataL[0] = 0x00; packet->rumbleDataL[1] = 0x01; packet->rumbleDataL[2] = 0x40; packet->rumbleDataL[3] = 0x00; packet->rumbleDataR[0] = 0x00; packet->rumbleDataR[1] = 0x01; packet->rumbleDataR[2] = 0x40; packet->rumbleDataR[3] = 0x00; */ packet->subCommand = 0x30; // Report ID packet->subCommandData[0] = lr; // try full 0x30?; // Report ID btdriver_->sendL2CapCommand((uint8_t *)packet, sizeof(struct SWProBTSendConfigData), BluetoothController::INTERRUPT_SCID /*0x40*/); return true; } memset(txbuf_, 0, 20); // make sure it is cleared out txbuf_[0] = 0x01; // Command // Now add in subcommand data: // Probably do this better soon txbuf_[1 + 0] = rumble_counter++; // txbuf_[1 + 1] = 0x00; txbuf_[1 + 2] = 0x01; txbuf_[1 + 3] = 0x40; txbuf_[1 + 4] = 0x40; txbuf_[1 + 5] = 0x00; txbuf_[1 + 6] = 0x01; txbuf_[1 + 7] = 0x40; txbuf_[1 + 8] = 0x40; txbuf_[1 + 9] = 0x30; // LED Command txbuf_[1 + 10] = lr; println("Switch set leds: driver? ", (uint32_t)driver_, HEX); print_hexbytes((uint8_t*)txbuf_, 20); if(driver_) { driver_->sendPacket(txbuf_, 20); } else if (txpipe_) { if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 20, this, __LINE__)) { println("switch transfer fail"); Serial.printf("Switch transfer fail\n"); } } case XBOXONE: default: return false; } } return false; } bool JoystickController::transmitPS4UserFeedbackMsg() { if (driver_) { uint8_t packet[32]; memset(packet, 0, sizeof(packet)); packet[0] = 0x05; // Report ID packet[1] = 0xFF; packet[4] = rumble_lValue_; // Small Rumble packet[5] = rumble_rValue_; // Big rumble packet[6] = leds_[0]; // RGB value packet[7] = leds_[1]; packet[8] = leds_[2]; // 9, 10 flash ON, OFF times in 100ths of second? 2.5 seconds = 255 DBGPrintf("Joystick update Rumble/LEDs\n"); return driver_->sendPacket(packet, 32); } else if (btdriver_) { uint8_t packet[79]; memset(packet, 0, sizeof(packet)); //0xa2, 0x11, 0xc0, 0x20, 0xf0, 0x04, 0x00 packet[0] = 0x52; packet[1] = 0x11; // Report ID packet[2] = 0x80; //packet[3] = 0x20; packet[4] = 0xFF; packet[7] = rumble_lValue_; // Small Rumble packet[8] = rumble_rValue_; // Big rumble packet[9] = leds_[0]; // RGB value packet[10] = leds_[1]; packet[11] = leds_[2]; // 12, 13 flash ON, OFF times in 100ths of sedond? 2.5 seconds = 255 DBGPrintf("Joystick update Rumble/LEDs\n"); btdriver_->sendL2CapCommand(packet, sizeof(packet), 0x40); return true; } return false; } static const uint8_t PS3_USER_FEEDBACK_INIT[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; bool JoystickController::transmitPS3UserFeedbackMsg() { if (driver_) { memcpy(txbuf_, PS3_USER_FEEDBACK_INIT, 48); txbuf_[1] = rumble_lValue_ ? rumble_timeout_ : 0; txbuf_[2] = rumble_lValue_; // Small Rumble txbuf_[3] = rumble_rValue_ ? rumble_timeout_ : 0; txbuf_[4] = rumble_rValue_; // Big rumble txbuf_[9] = leds_[2] << 1; // RGB value // using third led now... //DBGPrintf("\nJoystick update Rumble/LEDs %d %d %d %d %d\n", txbuf_[1], txbuf_[2], txbuf_[3], txbuf_[4], txbuf_[9]); return driver_->sendControlPacket(0x21, 9, 0x201, 0, 48, txbuf_); } else if (btdriver_) { txbuf_[0] = 0x52; txbuf_[1] = 0x1; memcpy(&txbuf_[2], PS3_USER_FEEDBACK_INIT, 48); txbuf_[3] = rumble_lValue_ ? rumble_timeout_ : 0; txbuf_[4] = rumble_lValue_; // Small Rumble txbuf_[5] = rumble_rValue_ ? rumble_timeout_ : 0; txbuf_[6] = rumble_rValue_; // Big rumble txbuf_[11] = leds_[2] << 1; // RGB value DBGPrintf("\nJoystick update Rumble/LEDs %d %d %d %d %d\n", txbuf_[3], txbuf_[4], txbuf_[5], txbuf_[6], txbuf_[11]); btdriver_->sendL2CapCommand(txbuf_, 50, BluetoothController::CONTROL_SCID); return true; } return false; } #define MOVE_REPORT_BUFFER_SIZE 7 #define MOVE_HID_BUFFERSIZE 50 // Size of the buffer for the Playstation Motion Controller bool JoystickController::transmitPS3MotionUserFeedbackMsg() { if (driver_) { txbuf_[0] = 0x02; // Set report ID, this is needed for Move commands to work txbuf_[2] = leds_[0]; txbuf_[3] = leds_[1]; txbuf_[4] = leds_[2]; txbuf_[6] = rumble_lValue_; // Set the rumble value into the write buffer //return driver_->sendControlPacket(0x21, 9, 0x201, 0, MOVE_REPORT_BUFFER_SIZE, txbuf_); return driver_->sendPacket(txbuf_, MOVE_REPORT_BUFFER_SIZE); } else if (btdriver_) { txbuf_[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) txbuf_[1] = 0x02; // Report ID txbuf_[3] = leds_[0]; txbuf_[4] = leds_[1]; txbuf_[5] = leds_[2]; txbuf_[7] = rumble_lValue_; btdriver_->sendL2CapCommand(txbuf_, MOVE_HID_BUFFERSIZE, BluetoothController::INTERRUPT_SCID); return true; } return false; } //***************************************************************************** // Support for Joysticks that Use HID data. //***************************************************************************** hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) { DBGPrintf("JoystickController::claim_collection(%p) Driver:%p(%u %u) Dev:%p Top:%x\n", this, driver, driver->interfaceSubClass(), driver->interfaceProtocol(), dev, topusage); // only claim Desktop/Joystick and Desktop/Gamepad if (topusage != 0x10004 && topusage != 0x10005 && topusage != 0x10008) return CLAIM_NO; // only claim from one physical device if (mydevice != NULL && dev != mydevice) return CLAIM_NO; // Also don't allow us to claim if it is used as a standard usb object (XBox...) if (device != nullptr) return CLAIM_NO; mydevice = dev; collections_claimed++; anychange = true; // always report values on first read driver_ = driver; // remember the driver. driver_->setTXBuffers(txbuf_, nullptr, sizeof(txbuf_)); connected_ = true; // remember that hardware is actually connected... // Lets see if we know what type of joystick this is. That is, is it a PS3 or PS4 or ... joystickType_ = mapVIDPIDtoJoystickType(mydevice->idVendor, mydevice->idProduct, false); DBGPrintf("JoystickController::claim_collection joystickType_=%d\n", joystickType_); switch (joystickType_) { case PS3: case PS3_MOTION: // not sure yet additional_axis_usage_page_ = 0x1; additional_axis_usage_start_ = 0x100; additional_axis_usage_count_ = 39; axis_change_notify_mask_ = (uint64_t) - 1; // Start off assume all bits break; case PS4: additional_axis_usage_page_ = 0xFF00; additional_axis_usage_start_ = 0x21; additional_axis_usage_count_ = 54; axis_change_notify_mask_ = (uint64_t)0xfffffffffffff3ffl; // Start off assume all bits - 10 and 11 break; case SWITCH: // bugbug set the hand shake... { DBGPrintf("Send Handshake\n"); sw_sendCmdUSB(0x02, SW_CMD_TIMEOUT); initialPass_ = true; connectedComplete_pending_ = 0; } // fall through default: additional_axis_usage_page_ = 0x09; additional_axis_usage_start_ = 0x21; additional_axis_usage_count_ = 5; axis_change_notify_mask_ = 0x3ff; // Start off assume only the 10 bits... } //DBGPrintf("Claim Additional axis: %x %x %d\n", additional_axis_usage_page_, additional_axis_usage_start_, additional_axis_usage_count_); USBHDBGSerial.printf("\tJoystickController claim collection\n"); return CLAIM_REPORT; } void JoystickController::disconnect_collection(Device_t *dev) { if (--collections_claimed == 0) { mydevice = NULL; driver_ = nullptr; axis_mask_ = 0; axis_changed_mask_ = 0; } } void JoystickController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) { // TODO: set up translation from logical min/max to consistent 16 bit scale } void JoystickController::hid_input_data(uint32_t usage, int32_t value) { DBGPrintf("joystickType_=%d\n", joystickType_); DBGPrintf("Joystick: usage=%X, value=%d\n", usage, value); uint32_t usage_page = usage >> 16; usage &= 0xFFFF; if (usage_page == 9 && usage >= 1 && usage <= 32) { uint32_t bit = 1 << (usage - 1); if (value == 0) { if (buttons & bit) { buttons &= ~bit; anychange = true; } } else { if (!(buttons & bit)) { buttons |= bit; anychange = true; } } } else if (usage_page == 1 && usage >= 0x30 && usage <= 0x39) { // TODO: need scaling of value to consistent API, 16 bit signed? // TODO: many joysticks repeat slider usage. Detect & map to axis? uint32_t i = usage - 0x30; axis_mask_ |= (1 << i); // Keep record of which axis we have data on. if (axis[i] != value) { axis[i] = value; axis_changed_mask_ |= (1 << i); if (axis_changed_mask_ & axis_change_notify_mask_) anychange = true; } } else if (usage_page == additional_axis_usage_page_) { // see if the usage is witin range. //DBGPrintf("UP: usage_page=%x usage=%x User: %x %d\n", usage_page, usage, user_buttons_usage_start, user_buttons_count_); if ((usage >= additional_axis_usage_start_) && (usage < (additional_axis_usage_start_ + additional_axis_usage_count_))) { // We are in the user range. uint16_t usage_index = usage - additional_axis_usage_start_ + STANDARD_AXIS_COUNT; if (usage_index < (sizeof(axis) / sizeof(axis[0]))) { if (axis[usage_index] != value) { axis[usage_index] = value; if (usage_index > 63) usage_index = 63; // don't overflow our mask axis_changed_mask_ |= ((uint64_t)1 << usage_index); // Keep track of which ones changed. if (axis_changed_mask_ & axis_change_notify_mask_) anychange = true; // We have changes... } axis_mask_ |= ((uint64_t)1 << usage_index); // Keep record of which axis we have data on. } //DBGPrintf("UB: index=%x value=%x\n", usage_index, value); } } else { DBGPrintf("UP: usage_page=%x usage=%x add: %x %x %d\n", usage_page, usage, additional_axis_usage_page_, additional_axis_usage_start_, additional_axis_usage_count_); } // TODO: hat switch? } void JoystickController::hid_input_end() { if (anychange) { joystickEvent = true; } } bool JoystickController::hid_process_out_data(const Transfer_t *transfer) { DBGPrintf("JoystickController::hid_process_out_data\n"); return true; } //--------------------------------------------------- // Try to handle some of the startup code messages for Switch controll // b bool JoystickController::sw_handle_usb_init_of_joystick(uint8_t *buffer, uint16_t cb, bool timer_event) { if (buffer) { if ((buffer[0] != 0x81) && (buffer[0] != 0x21)) return false; // was not an event message driver_->stopTimer(); uint8_t ack_rpt = buffer[0]; if (ack_rpt == 0x81) { uint8_t ack_81_subrpt = buffer[1]; DBGPrintf("\t(%u)CMD last sent: %x ack cmd: %x ", (uint32_t)em_sw_, sw_last_cmd_sent_, ack_81_subrpt); switch(ack_81_subrpt) { case 0x02: DBGPrintf("Handshake Complete......\n"); break; case 0x03: DBGPrintf("Baud Rate Change Complete......\n"); break; case 0x04: DBGPrintf("Disabled USB Timeout Complete......\n"); break; default: DBGPrintf("???"); } if (!initialPass_) return true; // don't need to process if (sw_last_cmd_sent_ == ack_rpt) { sw_last_cmd_repeat_count = 0; connectedComplete_pending_++; } else { DBGPrintf("\tcmd != ack rpt count:%u ", sw_last_cmd_repeat_count); if (sw_last_cmd_repeat_count) { DBGPrintf("Skip to next\n"); sw_last_cmd_repeat_count = 0; connectedComplete_pending_++; } else { DBGPrintf("Retry\n"); sw_last_cmd_repeat_count++; } } } else { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //21 0a 71 00 80 00 01 e8 7f 01 e8 7f 0c 80 40 00 00 00 00 00 00 ... uint8_t ack_21_subrpt = buffer[14]; sw_parseAckMsg(buffer); DBGPrintf("\t(%u)CMD Submd ack cmd: %x \n", (uint32_t)em_sw_, ack_21_subrpt); switch (ack_21_subrpt) { case 0x40: DBGPrintf("IMU Enabled......\n"); break; case 0x48: DBGPrintf("Rumbled Enabled......\n"); break; case 0x10: DBGPrintf("IMU Cal......\n"); break; case 0x30: DBGPrintf("Std Rpt Enabled......\n"); break; default: DBGPrintf("Other"); break; } if (!initialPass_) return true; // don't need to process sw_last_cmd_repeat_count = 0; connectedComplete_pending_++; } } else if (timer_event) { if (!initialPass_) return true; // don't need to process DBGPrintf("\t(%u)Timer event - advance\n", (uint32_t)em_sw_); sw_last_cmd_repeat_count = 0; connectedComplete_pending_++; } // Now lets Send out the next state // Note: we don't increment it here, but let the ack or // timeout increment it. uint8_t packet_[8]; switch(connectedComplete_pending_) { case 0: //Change Baud DBGPrintf("Change Baud\n"); sw_sendCmdUSB(0x03, SW_CMD_TIMEOUT); break; case 1: DBGPrintf("Handshake2\n"); sw_sendCmdUSB(0x02, SW_CMD_TIMEOUT); break; case 2: DBGPrintf("Try to get IMU Cal\n"); packet_[0] = 0x20; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x6037 - 0x6020 + 1); sw_sendSubCmdUSB(0x10, packet_, 5, SW_CMD_TIMEOUT); // doesnt work wired break; case 3: DBGPrintf("\nTry to Get IMU Horizontal Offset Data\n"); packet_[0] = 0x80; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x6085 - 0x6080 + 1); sw_sendSubCmdUSB(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 4: DBGPrintf("\n Read: Factory Analog stick calibration and Controller Colours\n"); packet_[0] = 0x3D; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x6055 - 0x603D + 1); sw_sendSubCmdUSB(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 5: connectedComplete_pending_++; DBGPrintf("Enable IMU\n"); packet_[0] = 0x01; sw_sendSubCmdUSB(0x40, packet_, 1, SW_CMD_TIMEOUT); break; case 6: DBGPrintf("JC_USB_CMD_NO_TIMEOUT\n"); sw_sendCmdUSB(0x04, SW_CMD_TIMEOUT); break; case 7: DBGPrintf("Enable Rumble\n"); packet_[0] = 0x01; sw_sendSubCmdUSB(0x48, packet_, 1, SW_CMD_TIMEOUT); break; case 8: DBGPrintf("Enable Std Rpt\n"); packet_[0] = 0x30; sw_sendSubCmdUSB(0x03, packet_, 1, SW_CMD_TIMEOUT); break; case 9: DBGPrintf("JC_USB_CMD_NO_TIMEOUT\n"); packet_[0] = 0x04; sw_sendCmdUSB(0x04, SW_CMD_TIMEOUT); break; case 10: connectedComplete_pending_ = 99; initialPass_ = false; } return true; } bool JoystickController::hid_process_in_data(const Transfer_t *transfer) { uint8_t *buffer = (uint8_t *)transfer->buffer; if (*buffer) report_id_ = *buffer; uint8_t cnt = transfer->length; if (!buffer || *buffer == 1) return false; // don't do report 1 DBGPrintf("hid_process_in_data %x %u %u %p %x %x:", transfer->buffer, transfer->length, joystickType_, txpipe_, initialPass_, connectedComplete_pending_); for (uint8_t i = 0; i < cnt; i++) DBGPrintf(" %02x", buffer[i]); DBGPrintf("\n"); if (joystickType_ == SWITCH) { if (sw_handle_usb_init_of_joystick(buffer, cnt, false)) return true; // the main HID parse code should handle it. sw_process_HID_data(buffer, cnt); return true; // don't let main hid code process this. } return false; } void JoystickController::hid_timer_event(USBDriverTimer *whichTimer) { DBGPrintf("JoystickController: Timer\n"); if (!driver_) return; driver_->stopTimer(); sw_handle_usb_init_of_joystick(nullptr, 0, true); } void JoystickController::bt_hid_timer_event(USBDriverTimer *whichTimer) { DBGPrintf("Bluetooth JoystickController: Timer\n"); if (!btconnect) return; btconnect->stopTimer(); sw_handle_bt_init_of_joystick(nullptr, 0, true); } bool JoystickController::hid_process_control(const Transfer_t *transfer) { Serial.printf("USBHIDParser::control msg: %x %x : %x %u :", transfer->setup.word1, transfer->setup.word2, transfer->buffer, transfer->length); if (transfer->buffer) { uint16_t cnt = transfer->length; if (cnt > 16) cnt = 16; uint8_t *pb = (uint8_t*)transfer->buffer; while (cnt--) Serial.printf(" %02x", *pb++); } Serial.printf("\n"); send_Control_packet_active_ = false; return false; } void JoystickController::joystickDataClear() { joystickEvent = false; anychange = false; axis_changed_mask_ = 0; axis_mask_ = 0; } //***************************************************************************** // Support for Joysticks that are class specific and do not use HID // Example: XBox One controller. //***************************************************************************** static uint8_t xboxone_start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00}; static uint8_t xbox360w_inquire_present[] = {0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //static uint8_t switch_start_input[] = {0x19, 0x01, 0x03, 0x07, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; static uint8_t switch_start_input[] = {0x80, 0x02}; bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) { println("JoystickController claim this=", (uint32_t)this, HEX); DBGPrintf("JoystickController::claim(%p) Dev:%p Type:%x\n", this, dev, type); // Don't try to claim if it is used as USB device or HID device if (mydevice != NULL) return false; if (device != nullptr) return false; // Try claiming at the interface level. if (type != 1) return false; print_hexbytes(descriptors, len); JoystickController::joytype_t jtype = mapVIDPIDtoJoystickType(dev->idVendor, dev->idProduct, true); DBGPrintf("\tVID:%x PID:%x Jype:%x\n", dev->idVendor, dev->idProduct, jtype); println("Jtype=", (uint8_t)jtype, DEC); if (jtype == UNKNOWN) return false; // XBOX One // 0 1 2 3 4 5 6 7 8 *9 10 1 2 3 4 5 *6 7 8 9 20 1 2 3 4 5 6 7 8 9 30 1... // 09 04 00 00 02 FF 47 D0 00 07 05 02 03 40 00 04 07 05 82 03 40 00 04 09 04 01 00 00 FF 47 D0 00 // Lets do some verifications to make sure. // XBOX 360 wireless... Has 8 interfaces. 4 joysticks (1, 3, 5, 7) and 4 headphones assume 2,4,6, 8... // Shows data for #1 only... // Also they have some unknown data type we need to ignore between interface and end points. // 0 1 2 3 4 5 6 7 8 *9 10 1 2 3 4 5 *6 7 8 9 20 1 2 3 4 5 6 7 8 // 09 04 00 00 02 FF 5D 81 00 14 22 00 01 13 81 1D 00 17 01 02 08 13 01 0C 00 0C 01 02 08 // 29 30 1 2 3 4 5 6 7 8 9 40 41 42 // 07 05 81 03 20 00 01 07 05 01 03 20 00 08 // Switch // 09 04 00 00 02 FF 5D 01 00 // 10 21 10 01 01 24 81 14 03 00 03 13 02 00 03 00 // 07 05 81 03 20 00 08 // 07 05 02 03 20 00 08 if (len < 9 + 7 + 7) return false; // Some common stuff for both XBoxs uint32_t count_end_points = descriptors[4]; if (count_end_points < 2) return false; if (descriptors[5] != 0xff) return false; // bInterfaceClass, 3 = HID rx_ep_ = 0; uint32_t txep = 0; uint8_t rx_interval = 0; uint8_t tx_interval = 0; rx_size_ = 0; tx_size_ = 0; uint32_t descriptor_index = 9; if (descriptors[descriptor_index + 1] == 0x22) { if (descriptors[descriptor_index] != 0x14) return false; // only support specific versions... descriptor_index += descriptors[descriptor_index]; // XBox360w ignore this unknown setup... } while ((rx_ep_ == 0) || txep == 0) { print(" Index:", descriptor_index, DEC); if (descriptor_index >= len) return false; // we ran off the end and did not get end points // see if the next data is an end point descript if ((descriptors[descriptor_index] == 7) && (descriptors[descriptor_index + 1] == 5)) { if ((descriptors[descriptor_index + 3] == 3) // Type 3... && (descriptors[descriptor_index + 4] <= 64) && (descriptors[descriptor_index + 5] == 0)) { // have a bulk EP size if (descriptors[descriptor_index + 2] & 0x80 ) { rx_ep_ = descriptors[descriptor_index + 2]; rx_size_ = descriptors[descriptor_index + 4]; rx_interval = descriptors[descriptor_index + 6]; } else { txep = descriptors[descriptor_index + 2]; tx_size_ = descriptors[descriptor_index + 4]; tx_interval = descriptors[descriptor_index + 6]; } } } descriptor_index += descriptors[descriptor_index]; // setup to look at next one... } if ((rx_ep_ == 0) || (txep == 0)) return false; // did not find two end points. print("JoystickController, rx_ep_=", rx_ep_ & 15); print("(", rx_size_); print("), txep=", txep); print("(", tx_size_); println(")"); rxpipe_ = new_Pipe(dev, 3, rx_ep_ & 15, 1, rx_size_, rx_interval); if (!rxpipe_) return false; txpipe_ = new_Pipe(dev, 3, txep, 0, tx_size_, tx_interval); if (!txpipe_) { //free_Pipe(rxpipe_); return false; } rxpipe_->callback_function = rx_callback; queue_Data_Transfer_Debug(rxpipe_, rxbuf_, rx_size_, this, __LINE__); txpipe_->callback_function = tx_callback; if (jtype == XBOXONE) { queue_Data_Transfer_Debug(txpipe_, xboxone_start_input, sizeof(xboxone_start_input), this, __LINE__); connected_ = true; // remember that hardware is actually connected... } else if (jtype == XBOX360) { queue_Data_Transfer_Debug(txpipe_, xbox360w_inquire_present, sizeof(xbox360w_inquire_present), this, __LINE__); connected_ = 0; // remember that hardware is actually connected... } else if (jtype == SWITCH) { queue_Data_Transfer_Debug(txpipe_, switch_start_input, sizeof(switch_start_input), this, __LINE__); connected_ = true; // remember that hardware is actually connected... } memset(axis, 0, sizeof(axis)); // clear out any data. joystickType_ = jtype; // remember we are an XBox One. DBGPrintf(" JoystickController::claim joystickType_ %d\n", joystickType_); return true; } void JoystickController::control(const Transfer_t *transfer) { } /************************************************************/ // Interrupt-based Data Movement /************************************************************/ void JoystickController::rx_callback(const Transfer_t *transfer) { if (!transfer->driver) return; ((JoystickController *)(transfer->driver))->rx_data(transfer); } void JoystickController::tx_callback(const Transfer_t *transfer) { if (!transfer->driver) return; ((JoystickController *)(transfer->driver))->tx_data(transfer); } /************************************************************/ // Interrupt-based Data Movement // XBox one input data when type == 0x20 // Information came from several places on the web including: // https://github.com/quantus/xbox-one-controller-protocol /************************************************************/ // 20 00 C5 0E 00 00 00 00 00 00 F0 06 AD FB 7A 0A DD F7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // 20 00 E0 0E 40 00 00 00 00 00 F0 06 AD FB 7A 0A DD F7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 typedef struct { uint8_t type; uint8_t const_0; uint16_t id; // From online references button order: // sync, dummy, start, back, a, b, x, y // dpad up, down left, right // lb, rb, left stick, right stick // Axis: // lt, rt, lx, ly, rx, ry // uint16_t buttons; int16_t axis[6]; } xbox1data20_t; typedef struct { uint8_t state; uint8_t id_or_type; uint16_t controller_status; uint16_t unknown; // From online references button order: // sync, dummy, start, back, a, b, x, y // dpad up, down left, right // lb, rb, left stick, right stick // Axis: // lt, rt, lx, ly, rx, ry // uint16_t buttons; uint8_t lt; uint8_t rt; int16_t axis[4]; } xbox360data_t; typedef struct { uint8_t state; uint8_t id_or_type; // From online references button order: // sync, dummy, start, back, a, b, x, y // dpad up, down left, right // lb, rb, left stick, right stick // Axis: // lt, rt, lx, ly, rx, ry // uint8_t buttons_h; uint8_t buttons_l; uint8_t lt; uint8_t rt; int16_t axis[4]; } switchdataUSB_t; static const uint8_t xbox_axis_order_mapping[] = {3, 4, 0, 1, 2, 5}; void JoystickController::rx_data(const Transfer_t *transfer) { #ifdef DEBUG_JOYSTICK print("JoystickController::rx_data (", joystickType_, DEC); print("): "); print_hexbytes((uint8_t*)transfer->buffer, transfer->length); #endif if (joystickType_ == XBOXONE) { // Process XBOX One data axis_mask_ = 0x3f; axis_changed_mask_ = 0; // assume none for now xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer; if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) { // We have a data transfer. Lets see what is new... if (xb1d->buttons != buttons) { buttons = xb1d->buttons; anychange = true; joystickEvent = true; println(" Button Change: ", buttons, HEX); } for (uint8_t i = 0; i < sizeof (xbox_axis_order_mapping); i++) { // The first two values were unsigned. int axis_value = (i < 2) ? (int)(uint16_t)xb1d->axis[i] : xb1d->axis[i]; if (axis_value != axis[xbox_axis_order_mapping[i]]) { axis[xbox_axis_order_mapping[i]] = axis_value; axis_changed_mask_ |= (1 << xbox_axis_order_mapping[i]); anychange = true; } } joystickEvent = true; } } else if (joystickType_ == XBOX360) { // First byte appears to status - if the byte is 0x8 it is a connect or disconnect of the controller. xbox360data_t *xb360d = (xbox360data_t *)transfer->buffer; if (xb360d->state == 0x08) { if (xb360d->id_or_type != connected_) { connected_ = xb360d->id_or_type; // remember it... if (connected_) { println("XBox360w - Connected type:", connected_, HEX); // rx_ep_ should be 1, 3, 5, 7 for the wireless convert to 2-5 on led setLEDs(2 + rx_ep_ / 2); // Right now hard coded to first joystick... } else { println("XBox360w - disconnected"); } } } else if ((xb360d->id_or_type == 0x00) && (xb360d->controller_status & 0x1300)) { // Controller status report - Maybe we should save away and allow the user access? println("XBox360w - controllerStatus: ", xb360d->controller_status, HEX); } else if (xb360d->id_or_type == 0x01) { // Lets only process report 1. //const uint8_t *pbuffer = (uint8_t*)transfer->buffer; //for (uint8_t i = 0; i < transfer->length; i++) DBGPrintf("%02x ", pbuffer[i]); //DBGPrintf("\n"); if (buttons != xb360d->buttons) { buttons = xb360d->buttons; anychange = true; } axis_mask_ = 0x3f; axis_changed_mask_ = 0; // assume none for now for (uint8_t i = 0; i < 4; i++) { if (axis[i] != xb360d->axis[i]) { axis[i] = xb360d->axis[i]; axis_changed_mask_ |= (1 << i); anychange = true; } } // the two triggers show up as 4 and 5 if (axis[4] != xb360d->lt) { axis[4] = xb360d->lt; axis_changed_mask_ |= (1 << 4); anychange = true; } if (axis[5] != xb360d->rt) { axis[5] = xb360d->rt; axis_changed_mask_ |= (1 << 5); anychange = true; } if (anychange) joystickEvent = true; } } else if (joystickType_ == SWITCH) { uint8_t packet[8]; if(initialPass_ == true) { switch(connectedComplete_pending_) { case 0: //setup handshake DBGPrintf("Send Handshake\n"); sw_sendCmdUSB(0x02, SW_CMD_TIMEOUT); connectedComplete_pending_ = 1; break; case 1: DBGPrintf("Send Hid only\n"); sw_sendCmdUSB(0x04, SW_CMD_TIMEOUT); connectedComplete_pending_ = 2; break; case 2: //Send report type DBGPrintf("Enable IMU\n"); packet[0] = 0x01; sw_sendSubCmdUSB(0x40, packet, 1); connectedComplete_pending_ = 3; break; case 3: DBGPrintf("Enable Rumble\n"); packet[0] = 0x01; sw_sendSubCmdUSB(0x48, packet, 1); connectedComplete_pending_ = 4; break; case 4: DBGPrintf("Enable Std Rpt\n"); packet[0] = 0x30; sw_sendSubCmdUSB(0x3f, packet, 1); connectedComplete_pending_ = 5; case 5: connectedComplete_pending_ = 0; initialPass_ = false; break; } } switchdataUSB_t *switchd = (switchdataUSB_t *)transfer->buffer; //uint32_t cur_buttons = switchd->buttons_l | (switchd->buttons_m << 8) | (switchd->buttons_h << 16); uint16_t cur_buttons = (switchd->buttons_h << 8) | switchd->buttons_l; if (buttons != cur_buttons) { buttons = cur_buttons; anychange = true; } axis_mask_ = 0x3f; axis_changed_mask_ = 0; // assume none for now for (uint8_t i = 0; i < 4; i++) { if (axis[i] != switchd->axis[i]) { axis[i] = switchd->axis[i]; axis_changed_mask_ |= (1 << i); anychange = true; } } //apply stick calibration float xout, yout; CalcAnalogStick(xout, yout, axis[0], axis[1], true); //Serial.printf("Correctd Left Stick: %f, %f\n", xout , yout); axis[0] = int(round(xout)); axis[1] = int(round(yout)); CalcAnalogStick(xout, yout, axis[2], axis[3], true); axis[2] = int(round(xout)); axis[3] = int(round(yout)); // the two triggers show up as 4 and 5 if (axis[6] != switchd->lt) { axis[6] = switchd->lt; axis_changed_mask_ |= (1 << 4); anychange = true; } if (axis[5] != switchd->rt) { axis[5] = switchd->rt; axis_changed_mask_ |= (1 << 5); anychange = true; } if (anychange) joystickEvent = true; } queue_Data_Transfer_Debug(rxpipe_, rxbuf_, rx_size_, this, __LINE__); } void JoystickController::tx_data(const Transfer_t *transfer) { } void JoystickController::disconnect() { axis_mask_ = 0; axis_changed_mask_ = 0; // TODO: free resources } hidclaim_t JoystickController::claim_bluetooth(BluetoothConnection *btconnection, uint32_t bluetooth_class, uint8_t *remoteName, int type) { USBHDBGSerial.printf("JoystickController::claim_bluetooth - Class %x %s\n", bluetooth_class, remoteName); // If we are already in use than don't grab another one. Likewise don't grab if it is used as USB or HID object if (btconnect && (btconnection != btconnect)) return CLAIM_NO; if (mydevice != NULL) return CLAIM_NO; if ((bluetooth_class & 0x0f00) == 0x500) { bool name_maps_to_joystick_type = (remoteName && mapNameToJoystickType(remoteName)); if ((bluetooth_class & 0x3C) == 0x08) { bool claim_interface = (type == 1) || (remoteName == nullptr); if (name_maps_to_joystick_type) { switch (joystickType_) { //case SWITCH: default: // others will experiment with trying for HID. break; case PS3: case PS3_MOTION: special_process_required = SP_PS3_IDS; // PS3 maybe needs different IDS. // fall through case PS4: case XBOXONE: case SWITCH: claim_interface = true; break; } } if (claim_interface) { // They are telling me to grab it now. SO say yes USBHDBGSerial.printf("JoystickController::claim_bluetooth Interface\n"); btconnect = btconnection; btdevice = (Device_t*)btconnect->btController_; // remember this way btdriver_ = btconnect->btController_; btdriver_->useHIDProtocol(true); // Another big hack try calling the connectionComplete to maybe update what reports we are working with // if (name_maps_to_joystick_type) connectionComplete(); return CLAIM_INTERFACE; } } return CLAIM_REPORT; // let them know we may be interested if there is a HID REport Descriptor } return CLAIM_NO; } bool JoystickController::process_bluetooth_HID_data(const uint8_t *data, uint16_t length) { // Example data from PS4 controller //01 7e 7f 82 84 08 00 00 00 00 // LX LY RX RY BT BT PS LT RT DBGPrintf("JoystickController::process_bluetooth_HID_data: data[0]=%x\n", data[0]); // May have to look at this one with other controllers... report_id_ = data[0]; if (data[0] == 1) { //print(" Joystick Data: "); // print_hexbytes(data, length); if (length > TOTAL_AXIS_COUNT) length = TOTAL_AXIS_COUNT; // don't overflow arrays... DBGPrintf(" Joystick Data: "); for (uint16_t i = 0; i < length; i++) DBGPrintf("%02x ", data[i]); DBGPrintf("\r\n"); if (joystickType_ == PS3) { // Quick and dirty hack to match PS3 HID data uint32_t cur_buttons = data[2] | ((uint16_t)data[3] << 8) | ((uint32_t)data[4] << 16); if (cur_buttons != buttons) { buttons = cur_buttons; joystickEvent = true; // something changed. } uint64_t mask = 0x1; axis_mask_ = 0x27; // assume bits 0, 1, 2, 5 for (uint16_t i = 0; i < 3; i++) { if (axis[i] != data[i + 6]) { axis_changed_mask_ |= mask; axis[i] = data[i + 6]; } mask <<= 1; // shift down the mask. } if (axis[5] != data[9]) { axis_changed_mask_ |= (1 << 5); axis[5] = data[9]; } if (axis[3] != data[18]) { axis_changed_mask_ |= (1 << 3); axis[3] = data[18]; } if (axis[4] != data[19]) { axis_changed_mask_ |= (1 << 4); axis[4] = data[19]; } // Then rest of data mask = 0x1 << 10; // setup for other bits for (uint16_t i = 10; i < length; i++ ) { axis_mask_ |= mask; if (data[i] != axis[i]) { axis_changed_mask_ |= mask; axis[i] = data[i]; } mask <<= 1; // shift down the mask. } } else if (joystickType_ == PS3_MOTION) { // Quick and dirty PS3_Motion data. uint32_t cur_buttons = data[1] | ((uint16_t)data[2] << 8) | ((uint32_t)data[3] << 16); if (cur_buttons != buttons) { buttons = cur_buttons; joystickEvent = true; // something changed. } // Hard to know what is best here. for now just copy raw data over... // will do this for now... Format of thought to be data. // data[1-3] Buttons (mentioned 4 as well but appears to be counter // axis[0-1] data[5] Trigger, Previous trigger value // 2-5 Unknown probably place holders for Axis like data for other PS3 // 6 - Time stamp // 7 - Battery // 8-19 - Accel: XL, XH, YL, YH, ZL, ZH, XL2, XH2, YL2, YH2, ZL2, ZH2 // 20-31 - Gyro: Xl,Xh,Yl,Yh,Zl,Zh,Xl2,Xh2,Yl2,Yh2,Zl2,Zh2 // 32 - Temp High // 33 - Temp Low (4 bits) Maybe Magneto x High on other?? uint64_t mask = 0x1; axis_mask_ = 0; // assume bits 0, 1, 2, 5 // Then rest of data mask = 0x1 << 10; // setup for other bits for (uint16_t i = 5; i < length; i++ ) { axis_mask_ |= mask; if (data[i] != axis[i - 5]) { axis_changed_mask_ |= mask; axis[i - 5] = data[i]; } mask <<= 1; // shift down the mask. } } else if (joystickType_ == XBOXONE) { // Process XBOX One data typedef struct __attribute__ ((packed)) { uint8_t report_type; // 1 int16_t axis[6]; uint32_t buttons; // From online references button order: // sync, dummy, start, back, a, b, x, y // dpad up, down left, right // lb, rb, left stick, right stick // Axis: // lt, rt, lx, ly, rx, ry // } xbox1data20bt_t; static const uint8_t xbox_bt_axis_order_mapping[] = { 0, 1, 2, 3, 4, 5}; axis_mask_ = 0x3f; axis_changed_mask_ = 0; // assume none for now xbox1data20bt_t *xb1d = (xbox1data20bt_t *)data; //if ((xb1d->type == 0x20) && (length >= sizeof (xbox1data20bt_t))) { // We have a data transfer. Lets see what is new... if (xb1d->buttons != buttons) { buttons = xb1d->buttons; anychange = true; joystickEvent = true; println(" Button Change: ", buttons, HEX); } for (uint8_t i = 0; i < sizeof (xbox_axis_order_mapping); i++) { // The first two values were unsigned. int axis_value = (i < 4) ? (int)(uint16_t)xb1d->axis[i] : xb1d->axis[i]; //DBGPrintf(" axis value [ %d ] = %d \n", i, axis_value); if (axis_value != axis[xbox_bt_axis_order_mapping[i]]) { axis[xbox_bt_axis_order_mapping[i]] = axis_value; axis_changed_mask_ |= (1 << xbox_bt_axis_order_mapping[i]); anychange = true; } } joystickEvent = true; //} } else { uint64_t mask = 0x1; axis_mask_ = 0; for (uint16_t i = 0; i < length; i++ ) { axis_mask_ |= mask; if (data[i] != axis[i]) { axis_changed_mask_ |= mask; axis[i] = data[i]; } mask <<= 1; // shift down the mask. // DBGPrintf("%02x ", axis[i]); } } if (axis_changed_mask_ & axis_change_notify_mask_) joystickEvent = true; connected_ = true; return true; } else if (data[0] == 0x11) { DBGPrintf("\n Joystick Data: "); uint64_t mask = 0x1; axis_mask_ = 0; axis_changed_mask_ = 0; //This moves data to be equivalent to what we see for //data[0] = 0x01 uint8_t tmp_data[length - 2]; for (uint16_t i = 0; i < (length - 2); i++ ) { tmp_data[i] = 0; tmp_data[i] = data[i + 2]; } /* * [1] LX, [2] = LY, [3] = RX, [4] = RY * [5] combo, tri, cir, x, sqr, D-PAD (4bits, 0-3 * [6] R3,L3, opt, share, R2, L2, R1, L1 * [7] Counter (bit7-2), T-PAD, PS * [8] Left Trigger, [9] Right Trigger * [10-11] Timestamp * [12] Battery (0 to 0xff) * [13-14] acceleration x * [15-16] acceleration y * [17-18] acceleration z * [19-20] gyro x * [21-22] gyro y * [23-24] gyro z * [25-29] unknown * [30] 0x00,phone,mic, usb, battery level (4bits) * rest is trackpad? to do implement? */ //PS Bit tmp_data[7] = (tmp_data[7] >> 0) & 1; //set arrow buttons to axis[0] tmp_data[10] = tmp_data[5] & ((1 << 4) - 1); //set buttons for last 4bits in the axis[5] tmp_data[5] = tmp_data[5] >> 4; // Lets try mapping the DPAD buttons to high bits // up up/right right R DN DOWN L DN Left LUP static const uint32_t dpad_to_buttons[] = {0x10000, 0x30000, 0x20000, 0x60000, 0x40000, 0xC0000, 0x80000, 0x90000}; // Quick and dirty hack to match PS4 HID data uint32_t cur_buttons = ((uint32_t)tmp_data[7] << 12) | (((uint32_t)tmp_data[6] * 0x10)) | ((uint16_t)tmp_data[5] ) ; if (tmp_data[10] < 8) cur_buttons |= dpad_to_buttons[tmp_data[10]]; if (cur_buttons != buttons) { buttons = cur_buttons; joystickEvent = true; // something changed. } mask = 0x1; axis_mask_ = 0x27; // assume bits 0, 1, 2, 5 for (uint16_t i = 0; i < 3; i++) { if (axis[i] != tmp_data[i + 1]) { axis_changed_mask_ |= mask; axis[i] = tmp_data[i + 1]; } mask <<= 1; // shift down the mask. } if (axis[5] != tmp_data[4]) { axis_changed_mask_ |= (1 << 5); axis[5] = tmp_data[4]; } if (axis[3] != tmp_data[8]) { axis_changed_mask_ |= (1 << 3); axis[3] = tmp_data[8]; } if (axis[4] != tmp_data[9]) { axis_changed_mask_ |= (1 << 4); axis[4] = tmp_data[9]; } //limit for masking mask = 0x1; for (uint16_t i = 6; i < (64); i++ ) { axis_mask_ |= mask; if (tmp_data[i] != axis[i]) { axis_changed_mask_ |= mask; axis[i] = tmp_data[i]; } mask <<= 1; // shift down the mask. DBGPrintf("%02x ", axis[i]); } DBGPrintf("\n"); //DBGPrintf("Axis Mask (axis_mask_, axis_changed_mask_; %d, %d\n", axis_mask_,axis_changed_mask_); joystickEvent = true; connected_ = true; } else if (joystickType_ == SWITCH) { if (sw_handle_bt_init_of_joystick(data, length, false)) return true; return sw_process_HID_data(data, length); } return false; } //----------------------------------------------------------------------------- // Process SWITCH controller data messages - split out here as used both for HID // and Bluetooth HID //----------------------------------------------------------------------------- bool JoystickController::sw_handle_bt_init_of_joystick(const uint8_t *data, uint16_t length, bool timer_event) { if (data) { if (data[0] != 0x21) return false; DBGPrintf("Joystick Acknowledge Command Rcvd! pending: %u SC: %x", connectedComplete_pending_, data[14]); if (data[13] & 0x80) DBGPrintf(" ACK(%x)\n", data[13]); else DBGPrintf(" ** NACK(%x) **\n", data[13]); DBGPrintf(" Joystick Data: "); for (uint16_t i = 0; i < length; i++) DBGPrintf("%02x ", data[i]); DBGPrintf("\r\n"); btconnect->stopTimer(); sw_parseAckMsg(data); DBGPrintf("==========> Connection Pending: %d\n",connectedComplete_pending_); if (!initialPassBT_) return true; // don't need to process // Shold maybe double check the right one... connectedComplete_pending_++; } else if (timer_event) { if (!initialPassBT_) return true; // don't need to process DBGPrintf("\t(%u)Timer event - advance\n", (uint32_t)em_sw_); connectedComplete_pending_++; } DBGPrintf("==========> Connection Pending: %d\n",connectedComplete_pending_); // only called by BT; uint8_t packet_[8]; switch (connectedComplete_pending_) { case 1: DBGPrintf("\nSet Shipment Low Power State\n"); packet_[0] = 0x00; sw_sendCmd(0x08, packet_, 1, SW_CMD_TIMEOUT ); break; case 2: DBGPrintf("\n Read Left joystick dead zone\n"); packet_[0] = 0x86; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x6097 - 0x6086 + 1); sw_sendCmd(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 3: DBGPrintf("\n Read Right stick dead zone\n"); packet_[0] = 0x98; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x60A9 - 0x6098 + 1); sw_sendCmd(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 4: DBGPrintf("\n Read: Factory Analog stick calibration\n"); packet_[0] = 0x3D; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x604E - 0x603D + 1); sw_sendCmd(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 5: DBGPrintf("\nTry to Get IMU Calibration Data\n"); packet_[0] = 0x20; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x6037 - 0x6020 + 1); sw_sendCmd(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 6: DBGPrintf("\nTry to Get IMU Horizontal Offset Data\n"); packet_[0] = 0x80; packet_[1] = 0x60; packet_[2] = 0x00; packet_[3] = 0x00; packet_[4] = (0x6097 - 0x6080 + 1); sw_sendCmd(0x10, packet_, 5, SW_CMD_TIMEOUT); break; case 7: DBGPrintf("\nTry to Enable IMU\n"); packet_[0] = 0x01; sw_sendCmd(0x40, packet_, 1, SW_CMD_TIMEOUT); /* 0x40 IMU, note: 0x00 would disable */ break; case 8: DBGPrintf("\nTry to Enable Rumble\n"); packet_[0] = 0x01; sw_sendCmd(0x48, packet_, 1, SW_CMD_TIMEOUT); break; case 9: DBGPrintf("\nTry to set LEDS\n"); setLEDs(0x1, 0, 0); break; case 10: DBGPrintf("\nSet Report Mode\n"); packet_[0] = 0x30; //0x3F; sw_sendCmd(0x03, packet_, 1, SW_CMD_TIMEOUT); break; case 11: DBGPrintf("\nTry to set Rumble\n"); setRumble(0xff, 0xff, 0xff); initialPassBT_ = false; connectedComplete_pending_ = 0xff; break; } return true; } void JoystickController::sw_update_axis(uint8_t axis_index, int new_value) { if (axis[axis_index] != new_value) { axis[axis_index] = new_value; anychange = true; axis_changed_mask_ |= (1 << axis_index); } } bool JoystickController::sw_process_HID_data(const uint8_t *data, uint16_t length) { if (data[0] == 0x3f) { // Assume switch: //<<(02 15 21):48 20 11 00 0D 00 71 00 A1 // 16 bits buttons // 4 bits hat // 4 bits // 16 bits X // 16 bits Y // 16 bits rx // 16 bits ry typedef struct __attribute__ ((packed)) { uint8_t report_type; // 1 uint16_t buttons; uint8_t hat; int16_t axis[4]; // From online references button order: // sync, dummy, start, back, a, b, x, y // dpad up, down left, right // lb, rb, left stick, right stick // Axis: // lt, rt, lx, ly, rx, ry // } switchbt_t; static const uint8_t switch_bt_axis_order_mapping[] = { 0, 1, 2, 3}; axis_mask_ = 0x1ff; axis_changed_mask_ = 0; // assume none for now switchbt_t *sw1d = (switchbt_t *)data; // We have a data transfer. Lets see what is new... if (sw1d->buttons != buttons) { buttons = sw1d->buttons; anychange = true; joystickEvent = true; println(" Button Change: ", buttons, HEX); } // We will put the HAT into axis 9 for now.. if (sw1d->hat != axis[9]) { axis[9] = sw1d->hat; axis_changed_mask_ |= (1 << 9); anychange = true; } //just a hack for a single joycon. if(buttons == 0x8000) { //ZL axis[6] = 1; } else { axis[6] = 0; } if(buttons == 0x8000) { //ZR axis[7] = 1; } else { axis[7] = 0; } for (uint8_t i = 0; i < sizeof (switch_bt_axis_order_mapping); i++) { // The first two values were unsigned. int axis_value = (uint16_t)sw1d->axis[i]; //DBGPrintf(" axis value [ %d ] = %d \n", i, axis_value); if (axis_value != axis[switch_bt_axis_order_mapping[i]]) { axis[switch_bt_axis_order_mapping[i]] = axis_value; axis_changed_mask_ |= (1 << switch_bt_axis_order_mapping[i]); anychange = true; } } joystickEvent = true; } else if (data[0] == 0x30) { // Assume switch full report // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 // 30 E0 80 00 00 00 D9 37 79 19 98 70 00 0D 0B F1 02 F0 0A 41 FE 25 FC 89 00 F8 0A F0 02 F2 0A 41 FE D9 FB 99 00 D4 0A F6 02 FC 0A 3C FE 69 FB B8 00 //<<(02 15 21):48 20 11 00 0D 00 71 00 A1 //static const uint8_t switch_bt_axis_order_mapping[] = { 0, 1, 2, 3}; axis_mask_ = 0x7fff; // have all of the fields. axis_changed_mask_ = 0; // assume none for now // We have a data transfer. Lets see what is new... uint32_t cur_buttons = data[3] | (data[4] << 8) | (data[5] << 16); //DBGPrintf("BUTTONS: %x\n", cur_buttons); if(initialPassButton_ == true) { if(cur_buttons == 0x8000) { buttonOffset_ = 0x8000; } else { buttonOffset_ = 0; } initialPassButton_ = false; } cur_buttons = cur_buttons - buttonOffset_; //Serial.printf("Buttons (3,4,5): %x, %x, %x, %x, %x, %x\n", buttonOffset_, cur_buttons, buttons, data[3], data[4], data[5]); if (cur_buttons != buttons) { buttons = cur_buttons; anychange = true; joystickEvent = true; println(" Button Change: ", buttons, HEX); } // We will put the HAT into axis 9 for now.. /* if (sw1d->hat != axis[9]) { axis[9] = sw1d->hat; axis_changed_mask_ |= (1 << 9); anychange = true; } */ uint16_t new_axis[14]; //Joystick data new_axis[0] = data[6] | ((data[7] & 0xF) << 8); //xl new_axis[1] = (data[7] >> 4) | (data[8] << 4); //yl new_axis[2] = data[9] | ((data[10] & 0xF) << 8); //xr new_axis[3] = (data[10] >> 4) | (data[11] << 4); //yr //Kludge to get trigger buttons tripping if(buttons == 0x40) { //R1 new_axis[5] = 1; } else { new_axis[5] = 0; } if(buttons == 0x400000) { //L1 new_axis[4] = 1; } else { new_axis[4] = 0; } if(buttons == 0x400040) { new_axis[4] = 0xff; new_axis[5] = 0xff; } if(buttons == 0x800000) { //ZL new_axis[6] = 0xff; } else { new_axis[6] = 0; } if(buttons == 0x80) { //ZR new_axis[7] = 0xff; } else { new_axis[7] = 0; } if(buttons == 0x800080) { new_axis[6] = 0xff; new_axis[7] = 0xff; } sw_update_axis(8, (int16_t)(data[13] | (data[14] << 8))); //ax sw_update_axis(9, (int16_t)(data[15] | (data[16] << 8))); //ay sw_update_axis(10, (int16_t)(data[17] | (data[18] << 8))); //az sw_update_axis(11, (int16_t)(data[19] | (data[20] << 8))); //gx sw_update_axis(12, (int16_t)(data[21] | (data[22] << 8))); //gy sw_update_axis(13, (int16_t)(data[23] | (data[24] << 8))); //gz sw_update_axis(14, data[2] >> 4); //Battery level, 8=full, 6=medium, 4=low, 2=critical, 0=empty //map axes for (uint8_t i = 0; i < 8; i++) { // The first two values were unsigned. if (new_axis[i] != axis[i]) { axis[i] = new_axis[i]; axis_changed_mask_ |= (1 << i); anychange = true; } } //apply stick calibration float xout, yout; CalcAnalogStick(xout, yout, new_axis[0], new_axis[1], true); //Serial.printf("Correctd Left Stick: %f, %f\n", xout , yout); axis[0] = int(round(xout)); axis[1] = int(round(yout)); CalcAnalogStick(xout, yout, new_axis[2], new_axis[3], true); axis[2] = int(round(xout)); axis[3] = int(round(yout)); joystickEvent = true; initialPass_ = false; } return false; } hidclaim_t JoystickController::bt_claim_collection(BluetoothConnection *btconnection, uint32_t bluetooth_class, uint32_t topusage) { USBHDBGSerial.printf("JoystickController::bt_claim_collection(%p) Connection:%p class:%x Top:%x\n", this, btconnection, bluetooth_class, topusage); if (mydevice != NULL) return CLAIM_NO; // claimed by some other... if (btconnect && (btconnect != btconnection)) return CLAIM_NO; // We will claim if BOOT Keyboard. if (topusage != 0x10004 && topusage != 0x10005 && topusage != 0x10008) return CLAIM_NO; // only claim from one physical device USBHDBGSerial.printf("\tJoystickController claim collection\n"); btconnect = btconnection; btdevice = (Device_t*)btconnect->btController_; // remember this way // experiment? See if we can now tell system to maybe set which report we want connectionComplete(); return CLAIM_REPORT; } void JoystickController::bt_hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) { hid_input_begin(topusage, type, lgmin, lgmax); } void JoystickController::bt_hid_input_data(uint32_t usage, int32_t value) { hid_input_data(usage, value); } void JoystickController::bt_hid_input_end() { hid_input_end(); } void JoystickController::bt_disconnect_collection(Device_t *dev) { disconnect_collection(dev); } bool JoystickController::mapNameToJoystickType(const uint8_t *remoteName) { // Sort of a hack, but try to map the name given from remote to a type... if (strncmp((const char *)remoteName, "Wireless Controller", 19) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %s - set to PS4\n", remoteName); joystickType_ = PS4; } else if (strncmp((const char *)remoteName, "PLAYSTATION(R)3", 15) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to PS3\n", (uint32_t)this, remoteName); joystickType_ = PS3; } else if (strncmp((const char *)remoteName, "Navigation Controller", 21) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to PS3\n", (uint32_t)this, remoteName); joystickType_ = PS3; } else if (strncmp((const char *)remoteName, "Motion Controller", 17) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to PS3 Motion\n", (uint32_t)this, remoteName); joystickType_ = PS3_MOTION; } else if (strncmp((const char *)remoteName, "Xbox Wireless", 13) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to XBOXONE\n", (uint32_t)this, remoteName); joystickType_ = XBOXONE; } else if (strncmp((const char *)remoteName, "Pro Controller", 13) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to Nintendo Pro Controller\n", (uint32_t)this, remoteName); joystickType_ = SWITCH; } else if(strncmp((const char *)remoteName, "Joy-Con (R)", 11) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to Nintendo Joy-Con (R) Controller\n", (uint32_t)this, remoteName); joystickType_ = SWITCH; } else if(strncmp((const char *)remoteName, "Joy-Con (L)", 11) == 0) { DBGPrintf(" JoystickController::mapNameToJoystickType %x %s - set to Nintendo Joy-Con (L) Controller\n", (uint32_t)this, remoteName); joystickType_ = SWITCH; } else { DBGPrintf(" JoystickController::mapNameToJoystickType %s - Unknown\n", remoteName); } DBGPrintf(" Joystick Type: %d\n", joystickType_); return true; } bool JoystickController::remoteNameComplete(const uint8_t *remoteName) { // Sort of a hack, but try to map the name given from remote to a type... if (mapNameToJoystickType(remoteName)) { switch (joystickType_) { case PS4: special_process_required = SP_NEED_CONNECT; break; case PS3: special_process_required = SP_PS3_IDS; break; case PS3_MOTION: special_process_required = SP_PS3_IDS; break; default: break; } } return true; } void JoystickController::connectionComplete() { connectedComplete_pending_ = 0; DBGPrintf(" JoystickController::connectionComplete %x joystick type %d\n", (uint32_t)this, joystickType_); switch (joystickType_) { case PS4: { uint8_t packet[2]; packet[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03) packet[1] = 0x02; // Report ID DBGPrintf("Set PS4 report\n"); delay(1); btdriver_->sendL2CapCommand(packet, sizeof(packet), BluetoothController::CONTROL_SCID /*0x40*/); } break; case PS3: { uint8_t packet[6]; packet[0] = 0x53; // HID BT Set_report (0x50) | Report Type (Feature 0x03) packet[1] = 0xF4; // Report ID packet[2] = 0x42; // Special PS3 Controller enable commands packet[3] = 0x03; packet[4] = 0x00; packet[5] = 0x00; DBGPrintf("enable six axis\n"); delay(1); btdriver_->sendL2CapCommand(packet, sizeof(packet), BluetoothController::CONTROL_SCID); } break; case PS3_MOTION: setLEDs(0, 0xff, 0); // Maybe try setting to green? break; case SWITCH: { // See if we can set a specific report #if 1 uint8_t packet_[8]; DBGPrintf("Request Device Info......\n"); packet_[0] = 0x00; sw_sendCmd(0x02, packet_, 1, SW_CMD_TIMEOUT); connectedComplete_pending_ = 0; DBGPrintf("Config Complete!\n"); #endif } default: break; } } void JoystickController::release_bluetooth() { btdevice = nullptr; // remember this way btdriver_ = nullptr; connected_ = false; special_process_required = false; } bool JoystickController::PS3Pair(uint8_t* bdaddr) { if (!driver_) return false; if (joystickType_ == PS3) { /* Set the internal Bluetooth address */ txbuf_[0] = 0x01; txbuf_[1] = 0x00; for (uint8_t i = 0; i < 6; i++) txbuf_[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data return driver_->sendControlPacket(0x21, 9, 0x3f5, 0, 8, txbuf_); } else if (joystickType_ == PS3_MOTION) { // Slightly different than other PS3 units... txbuf_[0] = 0x05; for (uint8_t i = 0; i < 6; i++) txbuf_[i + 1] = bdaddr[i]; // Order different looks like LSB First? txbuf_[7] = 0x10; txbuf_[8] = 0x01; txbuf_[9] = 0x02; txbuf_[10] = 0x12; // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data return driver_->sendControlPacket(0x21, 9, 0x305, 0, 11, txbuf_); } return false; } //============================================================================= // Retrieve the current pairing information for a PS4... //============================================================================= bool JoystickController::PS4GetCurrentPairing(uint8_t* bdaddr) { if (!driver_ || (joystickType_ != PS4)) return false; // Try asking PS4 for information memset(txbuf_, 0, 0x10); send_Control_packet_active_ = true; if (!driver_->sendControlPacket(0xA1, 1, 0x312, 0, 0x10, txbuf_)) return false; elapsedMillis em = 0; while ((em < 500) && send_Control_packet_active_) ; memcpy(bdaddr, &txbuf_[10], 6); return true; } bool JoystickController::PS4Pair(uint8_t* bdaddr) { if (!driver_ || (joystickType_ != PS4)) return false; // Lets try to setup a message to send... static const uint8_t ps4_pair_msg[] PROGMEM = {0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x56, 0xE8, 0x81, 0x38, 0x08, 0x06, 0x51, 0x41, 0xC0, 0x7F, 0x12, 0xAA, 0xD9, 0x66, 0x3C, 0xCE }; // Note the above 0xff sare place holders for the bdaddr memcpy(txbuf_, ps4_pair_msg, sizeof(ps4_pair_msg)); for (uint8_t i = 0; i < 6; i++) txbuf_[i + 1] = bdaddr[i]; send_Control_packet_active_ = true; return driver_->sendControlPacket(0x21, 0x09, 0x0313, 0, sizeof(ps4_pair_msg), txbuf_); } //Nintendo Switch functions void JoystickController::sw_sendCmd(uint8_t cmd, uint8_t *data, uint16_t size, uint32_t timeout) { struct SWProBTSendConfigData *packet = (struct SWProBTSendConfigData *)txbuf_ ; memset((void*)packet, 0, sizeof(struct SWProBTSendConfigData)); packet->hid_hdr = 0xA2; // HID BT Get_report (0xA0) | Report Type (Output) packet->id = 1; packet->gpnum = switch_packet_num; switch_packet_num = (switch_packet_num + 1) & 0x0f; // 2-9 rumble data; packet->rumbleDataL[0] = 0x00; packet->rumbleDataL[1] = 0x01; packet->rumbleDataL[2] = 0x40; packet->rumbleDataL[3] = 0x40; packet->rumbleDataR[0] = 0x00; packet->rumbleDataR[1] = 0x01; packet->rumbleDataR[2] = 0x40; packet->rumbleDataR[3] = 0x40; packet->subCommand = cmd; // Report ID for(uint16_t i = 0; i < size; i++) { packet->subCommandData[i] = data[i]; } if (btdriver_) { if (timeout != 0) { btconnect->startTimer(timeout); } btdriver_->sendL2CapCommand((uint8_t *)packet, sizeof(struct SWProBTSendConfigData), BluetoothController::INTERRUPT_SCID /*0x40*/); } else Serial.printf("\n####### sw_sendCMD(%x %p %u) called with btdriver_ == 0 ", cmd, data, size); em_sw_ = 0; } void JoystickController::sw_sendCmdUSB(uint8_t cmd, uint32_t timeout) { DBGPrintf("sw_sendCmdUSB: cmd:%x, timeout:%x\n", cmd, timeout); //sub-command txbuf_[0] = 0x80; txbuf_[1] = cmd; sw_last_cmd_sent_ = cmd; // remember which command we sent if(driver_) { if (timeout != 0) { driver_->startTimer(timeout); } driver_->sendPacket(txbuf_, 2); em_sw_ = 0; } else { if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 18, this, __LINE__)) { println("switch transfer fail"); } } } void JoystickController::sw_sendSubCmdUSB(uint8_t sub_cmd, uint8_t *data, uint8_t size, uint32_t timeout) { DBGPrintf("sw_sendSubCmdUSB(%x, %p, %u): ", sub_cmd, size); for (uint8_t i = 0; i < size; i++) DBGPrintf(" %02x", data[i]); DBGPrintf("\n"); memset(txbuf_, 0, 32); // make sure it is cleared out txbuf_[0] = 0x01; // Now add in subcommand data: // Probably do this better soon txbuf_[ 1] = switch_packet_num = (switch_packet_num + 1) & 0x0f; // txbuf_[ 2] = 0x00; txbuf_[ 3] = 0x01; txbuf_[ 4] = 0x40; txbuf_[ 5] = 0x40; txbuf_[ 6] = 0x00; txbuf_[ 7] = 0x01; txbuf_[ 8] = 0x40; txbuf_[ 9] = 0x40; txbuf_[ 10] = sub_cmd; //sub-command for(uint16_t i = 0; i < size; i++) { txbuf_[i + 11] = data[i]; } println("USB send sub cmd: driver? ", (uint32_t)driver_, HEX); print_hexbytes((uint8_t*)txbuf_, 32); if(driver_) { driver_->sendPacket(txbuf_, 32); if (timeout != 0) { driver_->startTimer(timeout); } } else if (txpipe_) { if (!queue_Data_Transfer_Debug(txpipe_, txbuf_, 32, this, __LINE__)) { println("switch transfer fail"); } } em_sw_ = 0; if (!timeout) delay(100); } void JoystickController::sw_parseAckMsg(const uint8_t *buf_) { int16_t data[6]; uint8_t offset = 20; uint8_t icount = 0; //uint8_t packet_[8]; if((buf_[14] == 0x10 && buf_[15] == 0x20 && buf_[16] == 0x60)) { //parse IMU calibration DBGPrintf("===> IMU Calibration \n"); for(uint8_t i = 0; i < 3; i++) { SWIMUCal.acc_offset[i] = (int16_t)(buf_[icount+offset] | (buf_[icount+offset+1] << 8)); SWIMUCal.acc_sensitivity[i] = (int16_t)(buf_[icount+offset+6] | (buf_[icount+offset+1+6] << 8)); SWIMUCal.gyro_offset[i] = (int16_t)(buf_[icount+offset+12] | (buf_[icount+offset+1+12] << 8)); SWIMUCal.gyro_sensitivity[i] = (int16_t)(buf_[icount+offset+18] | (buf_[icount+offset+1+18] << 8)); icount = i * 2; } for(uint8_t i = 0; i < 3; i++) { DBGPrintf("\t %d, %d, %d, %d\n", SWIMUCal.acc_offset[i], SWIMUCal.acc_sensitivity[i], SWIMUCal.gyro_offset[i], SWIMUCal.gyro_sensitivity[i]); } } else if((buf_[14] == 0x10 && buf_[15] == 0x80 && buf_[16] == 0x60)) { //parse IMU calibration DBGPrintf("===> IMU Calibration Offsets \n"); for(uint8_t i = 0; i < 3; i++) { SWIMUCal.acc_offset[i] = (int16_t)(buf_[i+offset] | (buf_[i+offset+1] << 8)); } for(uint8_t i = 0; i < 3; i++) { DBGPrintf("\t %d\n", SWIMUCal.acc_offset[i]); } } else if((buf_[14] == 0x10 && buf_[15] == 0x3D && buf_[16] == 0x60)){ //left stick offset = 20; data[0] = ((buf_[1+offset] << 8) & 0xF00) | buf_[0+offset]; data[1] = (buf_[2+offset] << 4) | (buf_[1+offset] >> 4); data[2] = ((buf_[4+offset] << 8) & 0xF00) | buf_[3+offset]; data[3] = (buf_[5+offset] << 4) | (buf_[4+offset] >> 4); data[4] = ((buf_[7+offset] << 8) & 0xF00) | buf_[6+offset]; data[5] = (buf_[8+offset] << 4) | (buf_[7+offset] >> 4); SWStickCal.lstick_center_x = data[2]; SWStickCal.lstick_center_y = data[3]; SWStickCal.lstick_x_min = SWStickCal.lstick_center_x - data[0]; SWStickCal.lstick_x_max = SWStickCal.lstick_center_x + data[4]; SWStickCal.lstick_y_min = SWStickCal.lstick_center_y - data[1]; SWStickCal.lstick_y_max = SWStickCal.lstick_center_y + data[5]; DBGPrintf("Left Stick Calibrataion\n"); DBGPrintf("center: %d, %d\n", SWStickCal.lstick_center_x, SWStickCal.lstick_center_y ); DBGPrintf("min/max x: %d, %d\n", SWStickCal.lstick_x_min, SWStickCal.lstick_x_max); DBGPrintf("min/max y: %d, %d\n", SWStickCal.lstick_y_min, SWStickCal.lstick_y_max); //right stick offset = 29; data[0] = ((buf_[1+offset] << 8) & 0xF00) | buf_[0+offset]; data[1] = (buf_[2+offset] << 4) | (buf_[1+offset] >> 4); data[2] = ((buf_[4+offset] << 8) & 0xF00) | buf_[3+offset]; data[3] = (buf_[5+offset] << 4) | (buf_[4+offset] >> 4); data[4] = ((buf_[7+offset] << 8) & 0xF00) | buf_[6+offset]; data[5] = (buf_[8+offset] << 4) | (buf_[7+offset] >> 4); SWStickCal.rstick_center_x = data[0]; SWStickCal.rstick_center_y = data[1]; SWStickCal.rstick_x_min = SWStickCal.rstick_center_x - data[2]; SWStickCal.rstick_x_max = SWStickCal.rstick_center_x + data[4]; SWStickCal.rstick_y_min = SWStickCal.rstick_center_y - data[3]; SWStickCal.rstick_y_max = SWStickCal.rstick_center_y + data[5]; DBGPrintf("\nRight Stick Calibrataion\n"); DBGPrintf("center: %d, %d\n", SWStickCal.rstick_center_x, SWStickCal.rstick_center_y ); DBGPrintf("min/max x: %d, %d\n", SWStickCal.rstick_x_min, SWStickCal.rstick_x_max); DBGPrintf("min/max y: %d, %d\n", SWStickCal.rstick_y_min, SWStickCal.rstick_y_max); } else if((buf_[14] == 0x10 && buf_[15] == 0x86 && buf_[16] == 0x60)){ //left stick deadzone_left offset = 20; SWStickCal.deadzone_left = (((buf_[4 + offset] << 8) & 0xF00) | buf_[3 + offset]); DBGPrintf("\nLeft Stick Deadzone\n"); DBGPrintf("deadzone: %d\n", SWStickCal.deadzone_left); } else if((buf_[14] == 0x10 && buf_[15] == 0x98 && buf_[16] == 0x60)){ //left stick deadzone_left offset = 20; SWStickCal.deadzone_left = (((buf_[4 + offset] << 8) & 0xF00) | buf_[3 + offset]); DBGPrintf("\nRight Stick Deadzone\n"); DBGPrintf("deadzone: %d\n", SWStickCal.deadzone_right); } else if((buf_[14] == 0x10 && buf_[15] == 0x10 && buf_[16] == 0x80)){ DBGPrintf("\nUser Calibration Rcvd!\n"); } } bool JoystickController::sw_getIMUCalValues(float *accel, float *gyro) { // Fail if we don't have actually have those fields. We need axis 8-13 for this if ((axis_mask_ & 0x3f00) != 0x3f00) return false; for(uint8_t i = 0; i < 3; i++) { accel[i] = (float)(axis[8+i] - SWIMUCal.acc_offset[i]) * (1.0f / (float)SWIMUCal.acc_sensitivity[i]) * 4.0f; gyro[i] = (float)(axis[11+i] - SWIMUCal.gyro_offset[i]) * (816.0f / (float)SWIMUCal.gyro_sensitivity[i]); } return true; } #define sw_scale 2048 void JoystickController::CalcAnalogStick ( float &pOutX, // out: resulting stick X value float &pOutY, // out: resulting stick Y value int16_t x, // in: initial stick X value int16_t y, // in: initial stick Y value bool isLeft // are we dealing with left or right Joystick ) { // uint16_t x_calc[3], // calc -X, CenterX, +X // uint16_t y_calc[3] // calc -Y, CenterY, +Y int16_t min_x; int16_t max_x; int16_t center_x; int16_t min_y; // analog joystick calibration int16_t max_y; int16_t center_y; if(isLeft) { min_x = SWStickCal.lstick_x_min; max_x = SWStickCal.lstick_x_max; center_x = SWStickCal.lstick_center_x; min_y = SWStickCal.lstick_y_min; max_y = SWStickCal.lstick_y_max; center_y = SWStickCal.lstick_center_y; } else { min_x = SWStickCal.rstick_x_min; max_x = SWStickCal.rstick_x_max; center_x = SWStickCal.rstick_center_x; min_y = SWStickCal.rstick_y_min; max_y = SWStickCal.rstick_y_max; center_y = SWStickCal.rstick_center_y; } float x_f, y_f; // Apply Joy-Con center deadzone. 0xAE translates approx to 15%. Pro controller has a 10% () deadzone float deadZoneCenter = 0.15f; // Add a small ammount of outer deadzone to avoid edge cases or machine variety. float deadZoneOuter = 0.0f; // convert to float based on calibration and valid ranges per +/-axis x = clamp(x, min_x, max_x); y = clamp(y, min_y, max_y); if (x >= center_x) { x_f = (float)(x - center_x) / (float)(max_x - center_x); } else { x_f = -((float)(x - center_x) / (float)(min_x - center_x)); } if (y >= center_y) { y_f = (float)(y - center_y) / (float)(max_y - center_y); } else { y_f = -((float)(y - center_y) / (float)(min_y - center_y)); } // Interpolate zone between deadzones float mag = sqrtf(x_f*x_f + y_f*y_f); if (mag > deadZoneCenter) { // scale such that output magnitude is in the range [0.0f, 1.0f] float legalRange = 1.0f - deadZoneOuter - deadZoneCenter; float normalizedMag = min(1.0f, (mag - deadZoneCenter) / legalRange); float scale = normalizedMag / mag; pOutX = (x_f * scale * sw_scale); pOutY = (y_f * scale * sw_scale); } else { // stick is in the inner dead zone pOutX = 0.0f; pOutY = 0.0f; } }