You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2408 lines
89 KiB
2408 lines
89 KiB
/* 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 <Arduino.h>
|
|
#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<class T>
|
|
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 <constant>
|
|
// 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;
|
|
}
|
|
|
|
|
|
}
|
|
|