Joystick - USB enhance More Axis, Rumble, LEDSmain
public: | public: | ||||
USBHIDParser(USBHost &host) { init(); } | USBHIDParser(USBHost &host) { init(); } | ||||
static void driver_ready_for_hid_collection(USBHIDInput *driver); | static void driver_ready_for_hid_collection(USBHIDInput *driver); | ||||
bool sendPacket(const uint8_t *buffer); | |||||
bool sendPacket(const uint8_t *buffer, int cb=-1); | |||||
void setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb); | |||||
bool sendControlPacket(uint32_t bmRequestType, uint32_t bRequest, | |||||
uint32_t wValue, uint32_t wIndex, uint32_t wLength, void *buf); | |||||
protected: | protected: | ||||
enum { TOPUSAGE_LIST_LEN = 4 }; | enum { TOPUSAGE_LIST_LEN = 4 }; | ||||
enum { USAGE_LIST_LEN = 24 }; | enum { USAGE_LIST_LEN = 24 }; | ||||
bool available() { return joystickEvent; } | bool available() { return joystickEvent; } | ||||
void joystickDataClear(); | void joystickDataClear(); | ||||
uint32_t getButtons() { return buttons; } | uint32_t getButtons() { return buttons; } | ||||
int getAxis(uint32_t index) { return (index < (sizeof(axis)/sizeof(axis[0]))) ? axis[index] : 0; } | |||||
uint32_t axisMask() {return axis_mask_;} | |||||
enum { AXIS_COUNT = 10 }; | |||||
int getAxis(uint32_t index) { return (index < (sizeof(axis)/sizeof(axis[0]))) ? axis[index] : 0; } | |||||
uint64_t axisMask() {return axis_mask_;} | |||||
uint64_t axisChangedMask() { return axis_changed_mask_;} | |||||
uint64_t axisChangeNotifyMask() {return axis_change_notify_mask_;} | |||||
void axisChangeNotifyMask(uint64_t notify_mask) {axis_change_notify_mask_ = notify_mask;} | |||||
// set functions functionality depends on underlying joystick. | |||||
bool setRumble(uint8_t lValue, uint8_t rValue, uint8_t timeout=0xff); | |||||
// setLEDs on PS4(RGB), PS3 simple LED setting (only uses lr) | |||||
bool setLEDs(uint8_t lr, uint8_t lg=0, uint8_t lb=0); // sets Leds, | |||||
enum { STANDARD_AXIS_COUNT = 10, ADDITIONAL_AXIS_COUNT = 54, TOTAL_AXIS_COUNT = (STANDARD_AXIS_COUNT+ADDITIONAL_AXIS_COUNT) }; | |||||
typedef enum { UNKNOWN=0, PS3, PS4, XBOXONE} joytype_t; | |||||
joytype_t joystickType = UNKNOWN; | |||||
protected: | protected: | ||||
// From USBDriver | // From USBDriver | ||||
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | ||||
virtual void hid_input_data(uint32_t usage, int32_t value); | virtual void hid_input_data(uint32_t usage, int32_t value); | ||||
virtual void hid_input_end(); | virtual void hid_input_end(); | ||||
virtual void disconnect_collection(Device_t *dev); | virtual void disconnect_collection(Device_t *dev); | ||||
virtual bool hid_process_out_data(const Transfer_t *transfer); | |||||
private: | private: | ||||
// Class specific | // Class specific | ||||
void init(); | void init(); | ||||
USBHIDParser *driver_ = nullptr; | |||||
joytype_t mapVIDPIDtoJoystickType(uint16_t idVendor, uint16_t idProduct, bool exclude_hid_devices); | |||||
bool transmitPS4UserFeedbackMsg(); | |||||
bool transmitPS3UserFeedbackMsg(); | |||||
bool anychange = false; | bool anychange = false; | ||||
volatile bool joystickEvent = false; | volatile bool joystickEvent = false; | ||||
uint32_t buttons = 0; | uint32_t buttons = 0; | ||||
int axis[AXIS_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |||||
uint32_t axis_mask_ = 0; // which axis have valid data | |||||
int axis[TOTAL_AXIS_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | |||||
uint64_t axis_mask_ = 0; // which axis have valid data | |||||
uint64_t axis_changed_mask_ = 0; | |||||
uint64_t axis_change_notify_mask_ = 0x3ff; // assume the low 10 values only. | |||||
uint16_t additional_axis_usage_page_ = 0; | |||||
uint16_t additional_axis_usage_start_ = 0; | |||||
uint16_t additional_axis_usage_count_ = 0; | |||||
// State values to output to Joystick. | |||||
uint8_t rumble_lValue_ = 0; | |||||
uint8_t rumble_rValue_ = 0; | |||||
uint8_t rumble_timeout_ = 0; | |||||
uint8_t leds_[3] = {0,0,0}; | |||||
// Used by HID code | // Used by HID code | ||||
uint8_t collections_claimed = 0; | uint8_t collections_claimed = 0; | ||||
Pipe_t *rxpipe_; | Pipe_t *rxpipe_; | ||||
Pipe_t *txpipe_; | Pipe_t *txpipe_; | ||||
uint8_t rxbuf_[64]; // receive circular buffer | uint8_t rxbuf_[64]; // receive circular buffer | ||||
uint8_t txbuf_[64]; // buffer to use to send commands to joystick | |||||
// Mapping table to say which devices we handle | // Mapping table to say which devices we handle | ||||
typedef struct { | typedef struct { | ||||
uint16_t idVendor; | uint16_t idVendor; | ||||
uint16_t idProduct; | uint16_t idProduct; | ||||
joytype_t joyType; | |||||
bool hidDevice; | |||||
} product_vendor_mapping_t; | } product_vendor_mapping_t; | ||||
static product_vendor_mapping_t pid_vid_mapping[]; | static product_vendor_mapping_t pid_vid_mapping[]; | ||||
USBHub hub2(myusb); | USBHub hub2(myusb); | ||||
KeyboardController keyboard1(myusb); | KeyboardController keyboard1(myusb); | ||||
KeyboardController keyboard2(myusb); | KeyboardController keyboard2(myusb); | ||||
//KeyboardHIDExtrasController hidextras(myusb); | |||||
USBHIDParser hid1(myusb); | USBHIDParser hid1(myusb); | ||||
USBHIDParser hid2(myusb); | USBHIDParser hid2(myusb); | ||||
USBHIDParser hid3(myusb); | USBHIDParser hid3(myusb); | ||||
USBHIDParser hid5(myusb); | USBHIDParser hid5(myusb); | ||||
MouseController mouse1(myusb); | MouseController mouse1(myusb); | ||||
JoystickController joystick1(myusb); | JoystickController joystick1(myusb); | ||||
int user_axis[64]; | |||||
uint32_t buttons_prev = 0; | |||||
RawHIDController rawhid1(myusb); | RawHIDController rawhid1(myusb); | ||||
RawHIDController rawhid2(myusb, 0xffc90004); | RawHIDController rawhid2(myusb, 0xffc90004); | ||||
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) | #define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) | ||||
const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1", "RawHid1", "RawHid2"}; | const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1", "RawHid1", "RawHid2"}; | ||||
bool hid_driver_active[CNT_DEVICES] = {false, false}; | bool hid_driver_active[CNT_DEVICES] = {false, false}; | ||||
bool show_changed_only = false; | |||||
uint8_t joystick_left_trigger_value = 0; | |||||
uint8_t joystick_right_trigger_value = 0; | |||||
uint64_t joystick_full_notify_mask = (uint64_t)-1; | |||||
void setup() | void setup() | ||||
{ | { | ||||
{ | { | ||||
myusb.Task(); | myusb.Task(); | ||||
if (Serial.available()) { | |||||
int ch = Serial.read(); // get the first char. | |||||
while (Serial.read() != -1) ; | |||||
if ((ch == 'b') || (ch == 'B')) { | |||||
Serial.println("Only notify on Basic Axis changes"); | |||||
joystick1.axisChangeNotifyMask(0x3ff); | |||||
} else if ((ch == 'f') || (ch == 'F')) { | |||||
Serial.println("Only notify on Full Axis changes"); | |||||
joystick1.axisChangeNotifyMask(joystick_full_notify_mask); | |||||
} else { | |||||
if (show_changed_only) { | |||||
show_changed_only = false; | |||||
Serial.println("\n*** Show All fields mode ***"); | |||||
} else { | |||||
show_changed_only = true; | |||||
Serial.println("\n*** Show only changed fields mode ***"); | |||||
} | |||||
} | |||||
} | |||||
for (uint8_t i = 0; i < CNT_DEVICES; i++) { | for (uint8_t i = 0; i < CNT_DEVICES; i++) { | ||||
if (*drivers[i] != driver_active[i]) { | if (*drivers[i] != driver_active[i]) { | ||||
if (driver_active[i]) { | if (driver_active[i]) { | ||||
mouse1.mouseDataClear(); | mouse1.mouseDataClear(); | ||||
} | } | ||||
if (joystick1.available()) { | if (joystick1.available()) { | ||||
uint32_t axis_mask = joystick1.axisMask(); | |||||
uint64_t axis_mask = joystick1.axisMask(); | |||||
uint64_t axis_changed_mask = joystick1.axisChangedMask(); | |||||
Serial.print("Joystick: buttons = "); | Serial.print("Joystick: buttons = "); | ||||
Serial.print(joystick1.getButtons(), HEX); | |||||
for (uint8_t i = 0; axis_mask != 0; i++, axis_mask >>= 1) { | |||||
if (axis_mask & 1) { | |||||
Serial.printf(" %d:%d", i, joystick1.getAxis(i)); | |||||
uint32_t buttons = joystick1.getButtons(); | |||||
Serial.print(buttons, HEX); | |||||
//Serial.printf(" AMasks: %x %x:%x", axis_mask, (uint32_t)(user_axis_mask >> 32), (uint32_t)(user_axis_mask & 0xffffffff)); | |||||
//Serial.printf(" M: %lx %lx", axis_mask, joystick1.axisChangedMask()); | |||||
if (show_changed_only) { | |||||
for (uint8_t i = 0; axis_changed_mask != 0; i++, axis_changed_mask >>= 1) { | |||||
if (axis_changed_mask & 1) { | |||||
Serial.printf(" %d:%d", i, joystick1.getAxis(i)); | |||||
} | |||||
} | |||||
} else { | |||||
for (uint8_t i = 0; axis_mask != 0; i++, axis_mask >>= 1) { | |||||
if (axis_mask & 1) { | |||||
Serial.printf(" %d:%d", i, joystick1.getAxis(i)); | |||||
} | |||||
} | } | ||||
} | } | ||||
uint8_t ltv; | |||||
uint8_t rtv; | |||||
switch (joystick1.joystickType) { | |||||
default: | |||||
break; | |||||
case JoystickController::PS4: | |||||
ltv = joystick1.getAxis(3); | |||||
rtv = joystick1.getAxis(4); | |||||
if ((ltv != joystick_left_trigger_value) || (rtv != joystick_right_trigger_value)) { | |||||
joystick_left_trigger_value = ltv; | |||||
joystick_right_trigger_value = rtv; | |||||
joystick1.setRumble(ltv, rtv); | |||||
} | |||||
break; | |||||
case JoystickController::PS3: | |||||
ltv = joystick1.getAxis(18); | |||||
rtv = joystick1.getAxis(19); | |||||
if ((ltv != joystick_left_trigger_value) || (rtv != joystick_right_trigger_value)) { | |||||
joystick_left_trigger_value = ltv; | |||||
joystick_right_trigger_value = rtv; | |||||
joystick1.setRumble(ltv, rtv, 50); | |||||
} | |||||
break; | |||||
case JoystickController::XBOXONE: | |||||
ltv = joystick1.getAxis(4); | |||||
rtv = joystick1.getAxis(5); | |||||
if ((ltv != joystick_left_trigger_value) || (rtv != joystick_right_trigger_value)) { | |||||
joystick_left_trigger_value = ltv; | |||||
joystick_right_trigger_value = rtv; | |||||
joystick1.setRumble(ltv, rtv); | |||||
Serial.printf(" Set Rumble %d %d", ltv, rtv); | |||||
} | |||||
break; | |||||
} | |||||
if (buttons != buttons_prev) { | |||||
if (joystick1.joystickType == JoystickController::PS3) { | |||||
joystick1.setLEDs((buttons>>12) & 0xf); // try to get to TRI/CIR/X/SQuare | |||||
} else { | |||||
uint8_t lr = (buttons & 1)? 0xff : 0; | |||||
uint8_t lg = (buttons & 2)? 0xff : 0; | |||||
uint8_t lb = (buttons & 4)? 0xff : 0; | |||||
joystick1.setLEDs(lr, lg, lb); | |||||
} | |||||
buttons_prev = buttons; | |||||
} | |||||
Serial.println(); | Serial.println(); | ||||
joystick1.joystickDataClear(); | joystick1.joystickDataClear(); | ||||
} | } |
// Called when the HID device sends a report | // Called when the HID device sends a report | ||||
void USBHIDParser::in_data(const Transfer_t *transfer) | void USBHIDParser::in_data(const Transfer_t *transfer) | ||||
{ | { | ||||
/*Serial.print("HID: "); | |||||
/*Serial.printf("HID: "); | |||||
uint8_t *pb = (uint8_t*)transfer->buffer; | uint8_t *pb = (uint8_t*)transfer->buffer; | ||||
for (uint8_t i = 0; i < transfer->length; i++) { | for (uint8_t i = 0; i < transfer->length; i++) { | ||||
Serial.print(pb[i], HEX); | |||||
Serial.print(" "); | |||||
Serial.printf("%x ",pb[i]); | |||||
} | } | ||||
Serial.println(); */ | |||||
Serial.printf("\n"); */ | |||||
print("HID: "); | print("HID: "); | ||||
print(use_report_id); | print(use_report_id); | ||||
} | } | ||||
} | } | ||||
bool USBHIDParser::sendPacket(const uint8_t *buffer) { | |||||
bool USBHIDParser::sendPacket(const uint8_t *buffer, int cb) { | |||||
if (!out_size || !out_pipe) return false; | if (!out_size || !out_pipe) return false; | ||||
if (!tx1) { | if (!tx1) { | ||||
// Was not init before, for now lets put it at end of descriptor | // Was not init before, for now lets put it at end of descriptor | ||||
tx2 = tx1 - out_size; | tx2 = tx1 - out_size; | ||||
} | } | ||||
if ((txstate & 3) == 3) return false; // both transmit buffers are full | if ((txstate & 3) == 3) return false; // both transmit buffers are full | ||||
if (cb == -1) | |||||
cb = out_size; | |||||
uint8_t *p = tx1; | uint8_t *p = tx1; | ||||
if ((txstate & 1) == 0) { | if ((txstate & 1) == 0) { | ||||
txstate |= 1; | txstate |= 1; | ||||
} else { | } else { | ||||
if (!tx2) | |||||
return false; // only one buffer | |||||
txstate |= 2; | txstate |= 2; | ||||
p = tx2; | p = tx2; | ||||
} | } | ||||
// copy the users data into our out going buffer | // copy the users data into our out going buffer | ||||
memcpy(p, buffer, out_size); | |||||
memcpy(p, buffer, cb); | |||||
println("USBHIDParser Send packet"); | println("USBHIDParser Send packet"); | ||||
print_hexbytes(buffer, out_size); | |||||
queue_Data_Transfer(out_pipe, p, out_size, this); | |||||
print_hexbytes(buffer, cb); | |||||
queue_Data_Transfer(out_pipe, p, cb, this); | |||||
println(" Queue_data transfer returned"); | println(" Queue_data transfer returned"); | ||||
return true; | return true; | ||||
} | } | ||||
void USBHIDParser::setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb) | |||||
{ | |||||
tx1 = buffer1; | |||||
tx2 = buffer2; | |||||
} | |||||
bool USBHIDParser::sendControlPacket(uint32_t bmRequestType, uint32_t bRequest, | |||||
uint32_t wValue, uint32_t wIndex, uint32_t wLength, void *buf) | |||||
{ | |||||
// Use setup structure to build packet | |||||
mk_setup(setup, bmRequestType, bRequest, wValue, wIndex, wLength); // ps3 tell to send report 1? | |||||
return queue_Control_Transfer(device, &setup, buf, this); | |||||
} | |||||
// This no-inputs parse is meant to be used when we first get the | // This no-inputs parse is meant to be used when we first get the | ||||
// HID report descriptor. It finds all the top level collections | // HID report descriptor. It finds all the top level collections | ||||
uint16_t report_size = 0; | uint16_t report_size = 0; | ||||
uint16_t report_count = 0; | uint16_t report_count = 0; | ||||
uint16_t usage_page = 0; | uint16_t usage_page = 0; | ||||
uint32_t last_usage = 0; | |||||
int32_t logical_min = 0; | int32_t logical_min = 0; | ||||
int32_t logical_max = 0; | int32_t logical_max = 0; | ||||
uint32_t bitindex = 0; | uint32_t bitindex = 0; | ||||
if ((val & 2)) { | if ((val & 2)) { | ||||
// ordinary variable format | // ordinary variable format | ||||
uint32_t uindex = 0; | uint32_t uindex = 0; | ||||
uint32_t uindex_max = 0xffff; // assume no MAX | |||||
bool uminmax = false; | bool uminmax = false; | ||||
if (usage_count > USAGE_LIST_LEN || usage_count == 0) { | |||||
if (usage_count > USAGE_LIST_LEN) { | |||||
// usage numbers by min/max, not from list | // usage numbers by min/max, not from list | ||||
uindex = usage[0]; | uindex = usage[0]; | ||||
uindex_max = usage[1]; | |||||
uminmax = true; | |||||
} else if ((report_count > 1) && (usage_count <= 1)) { | |||||
// Special cases: Either only one or no usages specified and there are more than one | |||||
// report counts . | |||||
if (usage_count == 1) { | |||||
uindex = usage[0]; | |||||
} else { | |||||
// BUGBUG:: Not sure good place to start? maybe round up from last usage to next higher group up of 0x100? | |||||
uindex = (last_usage & 0xff00) + 0x100; | |||||
} | |||||
uminmax = true; | uminmax = true; | ||||
} | } | ||||
//Serial.printf("TU:%x US:%x %x %d %d: C:%d, %d, MM:%d, %x %x\n", topusage, usage_page, val, logical_min, logical_max, | |||||
// report_count, usage_count, uminmax, usage[0], usage[1]); | |||||
for (uint32_t i=0; i < report_count; i++) { | for (uint32_t i=0; i < report_count; i++) { | ||||
uint32_t u; | uint32_t u; | ||||
if (uminmax) { | if (uminmax) { | ||||
u = uindex; | u = uindex; | ||||
if (uindex < usage[1]) uindex++; | |||||
if (uindex < uindex_max) uindex++; | |||||
} else { | } else { | ||||
u = usage[uindex++]; | u = usage[uindex++]; | ||||
if (uindex >= USAGE_LIST_LEN-1) { | if (uindex >= USAGE_LIST_LEN-1) { | ||||
uindex = USAGE_LIST_LEN-1; | uindex = USAGE_LIST_LEN-1; | ||||
} | } | ||||
} | } | ||||
last_usage = u; // remember the last one we used... | |||||
u |= (uint32_t)usage_page << 16; | u |= (uint32_t)usage_page << 16; | ||||
print(" usage = ", u, HEX); | print(" usage = ", u, HEX); | ||||
#define print USBHost::print_ | #define print USBHost::print_ | ||||
#define println USBHost::println_ | #define println USBHost::println_ | ||||
// 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, 0x02ea, XBOXONE, false },{ 0x045e, 0x02dd, XBOXONE, false }, | |||||
{ 0x054C, 0x0268, PS3, true}, | |||||
{ 0x054C, 0x05C4, PS4, true}, {0x054C, 0x09CC, PS4, true } | |||||
}; | |||||
//----------------------------------------------------------------------------- | |||||
void JoystickController::init() | void JoystickController::init() | ||||
{ | { | ||||
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); | contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); | ||||
USBHIDParser::driver_ready_for_hid_collection(this); | USBHIDParser::driver_ready_for_hid_collection(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... | // Some simple query functions depend on which interface we are using... | ||||
//***************************************************************************** | //***************************************************************************** | ||||
} | } | ||||
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 PS4: | |||||
return transmitPS4UserFeedbackMsg(); | |||||
case XBOXONE: | |||||
// Lets try sending a request to the XBox 1. | |||||
txbuf_[0] = 0x9; | |||||
txbuf_[1] = 0x8; | |||||
txbuf_[2] = 0x0; | |||||
txbuf_[3] = 0x08; // 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] = 0x80; // Length of pulse | |||||
txbuf_[11] = 0x00; // Period between pulses | |||||
if (!queue_Data_Transfer(txpipe_, txbuf_, 12, this)) { | |||||
println("XBoxOne rumble transfer fail"); | |||||
} | |||||
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. | |||||
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 PS4: | |||||
return transmitPS4UserFeedbackMsg(); | |||||
default: | |||||
return false; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
bool JoystickController::transmitPS4UserFeedbackMsg() { | |||||
if (!driver_) return false; | |||||
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 sedond? 2.5 seconds = 255 | |||||
Serial.printf("Joystick update Rumble/LEDs"); | |||||
return driver_->sendPacket(packet, 32); | |||||
} | |||||
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_) return false; | |||||
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_[0] << 1; // RGB value | |||||
//Serial.printf("\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_); | |||||
} | |||||
//***************************************************************************** | //***************************************************************************** | ||||
// Support for Joysticks that USe HID data. | |||||
// Support for Joysticks that Use HID data. | |||||
//***************************************************************************** | //***************************************************************************** | ||||
hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) | hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage) | ||||
mydevice = dev; | mydevice = dev; | ||||
collections_claimed++; | collections_claimed++; | ||||
anychange = true; // always report values on first read | anychange = true; // always report values on first read | ||||
driver_ = driver; // remember the driver. | |||||
driver_->setTXBuffers(txbuf_, nullptr, sizeof(txbuf_)); | |||||
// 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); | |||||
switch (joystickType) { | |||||
case PS3: | |||||
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; | |||||
default: | |||||
additional_axis_usage_page_ = 0; | |||||
additional_axis_usage_start_ = 0; | |||||
additional_axis_usage_count_ = 0; | |||||
axis_change_notify_mask_ = 0x3ff; // Start off assume only the 10 bits... | |||||
} | |||||
Serial.printf("Claim Additional axis: %x %x %d\n", additional_axis_usage_page_, additional_axis_usage_start_, additional_axis_usage_count_); | |||||
return CLAIM_REPORT; | return CLAIM_REPORT; | ||||
} | } | ||||
{ | { | ||||
if (--collections_claimed == 0) { | if (--collections_claimed == 0) { | ||||
mydevice = NULL; | mydevice = NULL; | ||||
driver_ = nullptr; | |||||
axis_mask_ = 0; | axis_mask_ = 0; | ||||
axis_changed_mask_ = 0; | |||||
} | } | ||||
} | } | ||||
axis_mask_ |= (1 << i); // Keep record of which axis we have data on. | axis_mask_ |= (1 << i); // Keep record of which axis we have data on. | ||||
if (axis[i] != value) { | if (axis[i] != value) { | ||||
axis[i] = value; | axis[i] = value; | ||||
anychange = true; | |||||
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. | |||||
//Serial.printf("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. | |||||
} | |||||
//Serial.printf("UB: index=%x value=%x\n", usage_index, value); | |||||
} | |||||
} else { | |||||
Serial.printf("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? | // TODO: hat switch? | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool JoystickController::hid_process_out_data(const Transfer_t *transfer) | |||||
{ | |||||
Serial.printf("JoystickController::hid_process_out_data\n"); | |||||
return true; | |||||
} | |||||
void JoystickController::joystickDataClear() { | void JoystickController::joystickDataClear() { | ||||
joystickEvent = false; | joystickEvent = false; | ||||
anychange = false; | anychange = false; | ||||
axis_changed_mask_ = 0; | |||||
axis_mask_ = 0; | |||||
} | } | ||||
//***************************************************************************** | //***************************************************************************** | ||||
// Support for Joysticks that are class specific and do not use HID | // Support for Joysticks that are class specific and do not use HID | ||||
// Example: XBox One controller. | // Example: XBox One controller. | ||||
//***************************************************************************** | //***************************************************************************** | ||||
// Note: currently just XBOX one. | |||||
JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { | |||||
{ 0x045e, 0x02ea },{ 0x045e, 0x02dd } }; | |||||
static uint8_t start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00}; | static uint8_t start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00}; | ||||
if (type != 0) return false; | if (type != 0) return false; | ||||
print_hexbytes(descriptors, len); | print_hexbytes(descriptors, len); | ||||
uint8_t i = 0; | |||||
for (; i < (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0])); i++) { | |||||
if ((dev->idVendor == pid_vid_mapping[i].idVendor) && (dev->idProduct == pid_vid_mapping[i].idProduct)) { | |||||
break; | |||||
} | |||||
} | |||||
if (i == (sizeof(pid_vid_mapping)/sizeof(pid_vid_mapping[0]))) return false; // Not in our list | |||||
JoystickController::joytype_t jtype = mapVIDPIDtoJoystickType(dev->idVendor, dev->idProduct, true); | |||||
println("Jtype=", (uint8_t)jtype, DEC); | |||||
if (jtype == UNKNOWN) | |||||
return false; | |||||
// 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... | |||||
// 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 | // 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. | // Lets do some verifications to make sure. | ||||
queue_Data_Transfer(txpipe_, start_input, sizeof(start_input), this); | queue_Data_Transfer(txpipe_, start_input, sizeof(start_input), this); | ||||
memset(axis, 0, sizeof(axis)); // clear out any data. | memset(axis, 0, sizeof(axis)); // clear out any data. | ||||
joystickType = jtype; // remember we are an XBox One. | |||||
return true; | return true; | ||||
} | } | ||||
// print("JoystickController::rx_data: "); | // print("JoystickController::rx_data: "); | ||||
// print_hexbytes((uint8_t*)transfer->buffer, transfer->length); | // print_hexbytes((uint8_t*)transfer->buffer, transfer->length); | ||||
axis_mask_ = 0x3f; | axis_mask_ = 0x3f; | ||||
axis_changed_mask_ = 0; // assume none for now | |||||
xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer; | xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer; | ||||
if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) { | if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) { | ||||
// We have a data transfer. Lets see what is new... | // We have a data transfer. Lets see what is new... | ||||
void JoystickController::disconnect() | void JoystickController::disconnect() | ||||
{ | { | ||||
axis_mask_ = 0; | axis_mask_ = 0; | ||||
axis_changed_mask_ = 0; | |||||
// TODO: free resources | // TODO: free resources | ||||
} | } | ||||
joystickDataClear KEYWORD2 | joystickDataClear KEYWORD2 | ||||
getAxis KEYWORD2 | getAxis KEYWORD2 | ||||
axisMask KEYWORD2 | axisMask KEYWORD2 | ||||
axisChangedMask KEYWORD2 | |||||
axisChangeNotifyMask KEYWORD2 | |||||
userAxisMask KEYWORD2 | |||||
setRumbleOn KEYWORD2 | |||||
setLEDs KEYWORD2 | |||||
joystickType KEYWORD2 | |||||
PS3 LITERAL1 | |||||
PS4 LITERAL1 | |||||
XBOXONE LITERAL1 | |||||
# USBSerial | # USBSerial | ||||
USBHOST_SERIAL_7E1 LITERAL1 | USBHOST_SERIAL_7E1 LITERAL1 |