From 68c2585a56cf5dff0319f5744d727f138eca1e7c Mon Sep 17 00:00:00 2001 From: Kurt Eckhardt Date: Thu, 11 Jan 2018 07:09:22 -0800 Subject: [PATCH] Joystick - USB enhance More Axis, Rumble, LEDS Added support for Joysticks, that on some of them we can not receive more of the Joysticks axis. So far mainly on PS3 and PS4. So you can now get the Gyro/accel stuff. You get feedback on the DS4 touch area, PS3 you can get the pressure values on several of the buttons. LIke the RT/LT ones. In addition added some support for Rumble. So far it appears to be working somewhat on the PS3 and PS4 controllers. On the PS4 you can also set the RGB LED light values and on the PS3 you can set the 4 LEDS on the front., which are normally used to say which controller it is. --- USBHost_t36.h | 48 +++++++-- examples/Mouse/Mouse.ino | 99 +++++++++++++++++-- hid.cpp | 53 ++++++++-- joystick.cpp | 207 ++++++++++++++++++++++++++++++++++++--- keywords.txt | 9 ++ 5 files changed, 380 insertions(+), 36 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index e47408d..0d406bf 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -603,7 +603,11 @@ class USBHIDParser : public USBDriver { public: USBHIDParser(USBHost &host) { init(); } 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: enum { TOPUSAGE_LIST_LEN = 4 }; enum { USAGE_LIST_LEN = 24 }; @@ -783,9 +787,19 @@ public: bool available() { return joystickEvent; } void joystickDataClear(); 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: // From USBDriver virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); @@ -798,16 +812,34 @@ protected: virtual void hid_input_data(uint32_t usage, int32_t value); virtual void hid_input_end(); virtual void disconnect_collection(Device_t *dev); + virtual bool hid_process_out_data(const Transfer_t *transfer); private: // Class specific 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; volatile bool joystickEvent = false; 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 uint8_t collections_claimed = 0; @@ -827,11 +859,13 @@ private: Pipe_t *rxpipe_; Pipe_t *txpipe_; 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 typedef struct { uint16_t idVendor; uint16_t idProduct; + joytype_t joyType; + bool hidDevice; } product_vendor_mapping_t; static product_vendor_mapping_t pid_vid_mapping[]; diff --git a/examples/Mouse/Mouse.ino b/examples/Mouse/Mouse.ino index 8950e2f..d98d00c 100644 --- a/examples/Mouse/Mouse.ino +++ b/examples/Mouse/Mouse.ino @@ -9,7 +9,6 @@ USBHub hub1(myusb); USBHub hub2(myusb); KeyboardController keyboard1(myusb); KeyboardController keyboard2(myusb); -//KeyboardHIDExtrasController hidextras(myusb); USBHIDParser hid1(myusb); USBHIDParser hid2(myusb); USBHIDParser hid3(myusb); @@ -17,6 +16,8 @@ USBHIDParser hid4(myusb); USBHIDParser hid5(myusb); MouseController mouse1(myusb); JoystickController joystick1(myusb); +int user_axis[64]; +uint32_t buttons_prev = 0; RawHIDController rawhid1(myusb); RawHIDController rawhid2(myusb, 0xffc90004); @@ -30,7 +31,11 @@ USBHIDInput *hiddrivers[] = {&mouse1, &joystick1, &rawhid1, &rawhid2}; #define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1", "RawHid1", "RawHid2"}; 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() { @@ -54,6 +59,27 @@ void loop() { 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++) { if (*drivers[i] != driver_active[i]) { if (driver_active[i]) { @@ -109,14 +135,75 @@ void loop() mouse1.mouseDataClear(); } 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(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(); joystick1.joystickDataClear(); } diff --git a/hid.cpp b/hid.cpp index 3a8bd0a..367558a 100644 --- a/hid.cpp +++ b/hid.cpp @@ -205,13 +205,12 @@ void USBHIDParser::disconnect() // Called when the HID device sends a report void USBHIDParser::in_data(const Transfer_t *transfer) { - /*Serial.print("HID: "); + /*Serial.printf("HID: "); uint8_t *pb = (uint8_t*)transfer->buffer; 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(use_report_id); @@ -249,7 +248,7 @@ void USBHIDParser::out_data(const Transfer_t *transfer) } } -bool USBHIDParser::sendPacket(const uint8_t *buffer) { +bool USBHIDParser::sendPacket(const uint8_t *buffer, int cb) { if (!out_size || !out_pipe) return false; if (!tx1) { // Was not init before, for now lets put it at end of descriptor @@ -259,22 +258,40 @@ bool USBHIDParser::sendPacket(const uint8_t *buffer) { tx2 = tx1 - out_size; } if ((txstate & 3) == 3) return false; // both transmit buffers are full + if (cb == -1) + cb = out_size; uint8_t *p = tx1; if ((txstate & 1) == 0) { txstate |= 1; } else { + if (!tx2) + return false; // only one buffer txstate |= 2; p = tx2; } // copy the users data into our out going buffer - memcpy(p, buffer, out_size); + memcpy(p, buffer, cb); 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"); 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 // HID report descriptor. It finds all the top level collections @@ -444,6 +461,7 @@ void USBHIDParser::parse(uint16_t type_and_report_id, const uint8_t *data, uint3 uint16_t report_size = 0; uint16_t report_count = 0; uint16_t usage_page = 0; + uint32_t last_usage = 0; int32_t logical_min = 0; int32_t logical_max = 0; uint32_t bitindex = 0; @@ -551,23 +569,38 @@ void USBHIDParser::parse(uint16_t type_and_report_id, const uint8_t *data, uint3 if ((val & 2)) { // ordinary variable format uint32_t uindex = 0; + uint32_t uindex_max = 0xffff; // assume no MAX 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 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; } + //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++) { uint32_t u; if (uminmax) { u = uindex; - if (uindex < usage[1]) uindex++; + if (uindex < uindex_max) uindex++; } else { u = usage[uindex++]; if (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; print(" usage = ", u, HEX); diff --git a/joystick.cpp b/joystick.cpp index 9513127..7df3a7d 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -27,6 +27,18 @@ #define print USBHost::print_ #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() { contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); @@ -36,6 +48,19 @@ void JoystickController::init() 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... //***************************************************************************** @@ -76,9 +101,109 @@ const uint8_t *JoystickController::serialNumber() } +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) @@ -90,6 +215,31 @@ hidclaim_t JoystickController::claim_collection(USBHIDParser *driver, Device_t * mydevice = dev; collections_claimed++; 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; } @@ -97,7 +247,9 @@ void JoystickController::disconnect_collection(Device_t *dev) { if (--collections_claimed == 0) { mydevice = NULL; + driver_ = nullptr; axis_mask_ = 0; + axis_changed_mask_ = 0; } } @@ -131,8 +283,32 @@ void JoystickController::hid_input_data(uint32_t usage, int32_t value) axis_mask_ |= (1 << i); // Keep record of which axis we have data on. if (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? } @@ -144,18 +320,23 @@ void JoystickController::hid_input_end() } } +bool JoystickController::hid_process_out_data(const Transfer_t *transfer) +{ + Serial.printf("JoystickController::hid_process_out_data\n"); + return true; +} + 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. //***************************************************************************** -// 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}; @@ -167,15 +348,12 @@ bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descripto if (type != 0) return false; 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 // Lets do some verifications to make sure. @@ -226,6 +404,7 @@ bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descripto queue_Data_Transfer(txpipe_, start_input, sizeof(start_input), this); memset(axis, 0, sizeof(axis)); // clear out any data. + joystickType = jtype; // remember we are an XBox One. return true; } @@ -280,6 +459,7 @@ void JoystickController::rx_data(const Transfer_t *transfer) // print("JoystickController::rx_data: "); // print_hexbytes((uint8_t*)transfer->buffer, transfer->length); 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... @@ -309,6 +489,7 @@ void JoystickController::tx_data(const Transfer_t *transfer) void JoystickController::disconnect() { axis_mask_ = 0; + axis_changed_mask_ = 0; // TODO: free resources } diff --git a/keywords.txt b/keywords.txt index 5df2d68..8cbfb19 100644 --- a/keywords.txt +++ b/keywords.txt @@ -81,6 +81,15 @@ getWheelH KEYWORD2 joystickDataClear KEYWORD2 getAxis KEYWORD2 axisMask KEYWORD2 +axisChangedMask KEYWORD2 +axisChangeNotifyMask KEYWORD2 +userAxisMask KEYWORD2 +setRumbleOn KEYWORD2 +setLEDs KEYWORD2 +joystickType KEYWORD2 +PS3 LITERAL1 +PS4 LITERAL1 +XBOXONE LITERAL1 # USBSerial USBHOST_SERIAL_7E1 LITERAL1