mode_hidgyro.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #include "mode_hidgyro.h"
  2. #include "triton.h"
  3. #include "gamepad_util.h"
  4. #include "config.h"
  5. #include "haptics.h"
  6. #include <Adafruit_TinyUSB.h>
  7. #include <Arduino.h>
  8. #include <string.h>
  9. HidGyroController g_hidGyroCtl;
  10. static const uint8_t GYRO_HID_DESC[] = {
  11. 0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31,
  12. 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
  13. 0x04, 0x81, 0x02, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07, 0x35, 0x00, 0x46,
  14. 0x3B, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x65, 0x00,
  15. 0x05, 0x09, 0x19, 0x01, 0x29, 0x0E, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
  16. 0x95, 0x0E, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x20, 0x75, 0x06, 0x95,
  17. 0x01, 0x15, 0x00, 0x25, 0x7F, 0x81, 0x02, 0x05, 0x01, 0x09, 0x33, 0x09,
  18. 0x34, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02,
  19. 0x06, 0x00, 0xFF, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09,
  20. 0x22, 0x95, 0x1F, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24, 0xB1,
  21. 0x02, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xB1, 0x02, 0xC0
  22. };
  23. #define DS4_TOUCH_H 942
  24. #define DS4_STATUS_USB 0x1B // cable + level 11 (full)
  25. static unsigned long g_gyroLastMs = 0;
  26. static Adafruit_USBD_HID g_hidGyro;
  27. static const uint8_t DS4_MAC[6] = { 0x00, 0x1B, 0xDC, 0x4F, 0x55, 0x53 };
  28. // GET_FEATURE handler. SteamOS drives the DS4 (054C:05C4) with hid-playstation (older kernels: hid-sony); both
  29. // issue GET_FEATURE requests during probe (calibration + MAC + firmware) and ABORT the bind if any STALLs or
  30. // returns the wrong length. ps_get_report enforces, on every bus: the response is the exact report size with the
  31. // report id as its first byte (data fields then start at buf[1]); CRC is only checked over Bluetooth. macOS/SDL
  32. // are lenient (re-derive), which is why the DS4 shows up there but not on a Deck. TinyUSB writes the report id
  33. // itself and hands us the buffer PAST it, so we fill only the PAYLOAD and return size-1. We satisfy both drivers'
  34. // report ids. (Sizes per drivers/hid/hid-playstation.c: 0x02=37, 0x12=16, 0xA3=49; legacy hid-sony MAC 0x81=7.)
  35. static uint16_t hidGyroGet(uint8_t rid, hid_report_type_t type, uint8_t *buf,
  36. uint16_t reqlen)
  37. {
  38. if (type != HID_REPORT_TYPE_FEATURE || !buf || reqlen == 0)
  39. return 0;
  40. memset(buf, 0, reqlen);
  41. switch (rid) {
  42. case 0x02: // motion calibration (37 incl id)
  43. if (reqlen < 36)
  44. return 0;
  45. psNeutralCalib(buf);
  46. return 36;
  47. case 0x12: // pairing info / MAC, hid-playstation (16 incl id)
  48. if (reqlen < 15)
  49. return 0;
  50. memcpy(buf, DS4_MAC, 6);
  51. return 15;
  52. case 0x81: // MAC, legacy hid-sony USB (7 incl id)
  53. if (reqlen < 6)
  54. return 0;
  55. memcpy(buf, DS4_MAC, 6);
  56. return 6;
  57. case 0xA3: // firmware / hardware info (49 incl id)
  58. if (reqlen < 48)
  59. return 0;
  60. // non-zero version (contents not validated over USB)
  61. buf[0] = 0x01;
  62. return 48;
  63. default:
  64. return 0;
  65. }
  66. }
  67. static void hidGyroSet(uint8_t rid, hid_report_type_t type, uint8_t const *b,
  68. uint16_t n)
  69. {
  70. if (type != HID_REPORT_TYPE_OUTPUT || n < 1)
  71. return;
  72. uint8_t id;
  73. const uint8_t *p;
  74. uint16_t pn;
  75. if (rid == 0) {
  76. id = b[0];
  77. p = b + 1;
  78. pn = (uint16_t)(n - 1);
  79. } else {
  80. id = rid;
  81. p = b;
  82. pn = n;
  83. }
  84. // DS4 USB effects: report 0x05, magic at byte 0, effects block starts at byte 3.
  85. if (id != 0x05 || pn < 5)
  86. return;
  87. hapticSteamRumble((uint16_t)p[4] * 257u,
  88. (uint16_t)p[3] * 257u); // DS4: left=low, right=high
  89. }
  90. static void hidGyroBuild(uint8_t out[63])
  91. {
  92. uint32_t b = psButtonsFromSteam(g_in[0].buttons);
  93. bool lTouch = (b & TB_LPADT) || (b & TB_LPADC),
  94. rTouch = (b & TB_RPADT) || (b & TB_RPADC);
  95. memset(out, 0, 63);
  96. out[0] = swStick(g_in[0].lx, false);
  97. out[1] = swStick(g_in[0].ly, true);
  98. out[2] = swStick(g_in[0].rx, false);
  99. out[3] = swStick(g_in[0].ry, true);
  100. out[4] = psHatNibble(b) | psFaceNibble(b);
  101. out[5] = psShouldersByte(b);
  102. static uint8_t ctr = 0;
  103. out[6] = ((ctr++ & 0x0F) << 4) |
  104. ((b & TB_TOUCH || b & TB_LPADC || b & TB_RPADC) ? 0x02 : 0) |
  105. ((b & TB_STEAM) ? 0x01 : 0);
  106. out[7] = g_in[0].lt;
  107. out[8] = g_in[0].rt;
  108. out[12] = g_in[0].gx & 0xFF;
  109. out[13] = g_in[0].gx >> 8;
  110. out[14] = g_in[0].gz & 0xFF;
  111. out[15] = g_in[0].gz >> 8;
  112. out[16] = (-g_in[0].gy) & 0xFF;
  113. out[17] = (-g_in[0].gy) >> 8;
  114. out[18] = g_in[0].ax & 0xFF;
  115. out[19] = g_in[0].ax >> 8;
  116. out[20] = g_in[0].ay & 0xFF;
  117. out[21] = g_in[0].ay >> 8;
  118. out[22] = g_in[0].az & 0xFF;
  119. out[23] = g_in[0].az >> 8;
  120. out[29] = DS4_STATUS_USB;
  121. if (lTouch || rTouch) {
  122. uint16_t lx, ly, rx, ry;
  123. steamPadsToTouch(b, DS4_TOUCH_H, g_in[0].lpx, g_in[0].lpy, g_in[0].rpx,
  124. g_in[0].rpy, &lx, &ly, &rx, &ry);
  125. static uint8_t tstamp = 0;
  126. out[32] = 1;
  127. out[33] = tstamp++;
  128. touchPackPads(out + 34, lTouch, rTouch, lx, ly, rx, ry);
  129. } else {
  130. out[32] = 0;
  131. touchPackPads(
  132. out + 34, false, false, 0, 0, 0,
  133. 0); // contact 0x80 -- memset(0) reads as touch @0,0
  134. }
  135. }
  136. void HidGyroController::begin()
  137. {
  138. USBDevice.setID(0x054C, 0x05C4);
  139. // Host re-reads config by VID:PID:serial (per-mode suffix); bump invalidates any cached descriptor
  140. USBDevice.setDeviceVersion(0x0104);
  141. USBDevice.setManufacturerDescriptor("Sony Computer Entertainment");
  142. USBDevice.setProductDescriptor("Wireless Controller");
  143. g_hidGyro.enableOutEndpoint(true);
  144. g_hidGyro.setReportCallback(hidGyroGet, hidGyroSet);
  145. g_hidGyro.setReportDescriptor(GYRO_HID_DESC, sizeof GYRO_HID_DESC);
  146. // 1ms bInterval so the RF rate is the only latency limit (matches Xbox)
  147. g_hidGyro.setPollInterval(1);
  148. g_hidGyro.begin();
  149. }
  150. void HidGyroController::task()
  151. {
  152. if (!g_hidGyro.ready())
  153. return;
  154. if (millis() - g_gyroLastMs < USB_STREAM_MS)
  155. return;
  156. g_gyroLastMs = millis();
  157. uint8_t p[63];
  158. hidGyroBuild(p);
  159. g_hidGyro.sendReport(0x01, p, sizeof p);
  160. }