mode_hidgyro.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #include "mode_hidgyro.h"
  2. #include "triton.h"
  3. #include "gamepad_util.h"
  4. #include "config.h"
  5. #include "haptics.h"
  6. #include "bonds.h"
  7. #include "usb_mount.h"
  8. #include "usb_tx.h"
  9. #include <Adafruit_TinyUSB.h>
  10. #include <Arduino.h>
  11. #include <string.h>
  12. HidGyroController g_hidGyroCtl;
  13. static const uint8_t GYRO_HID_DESC[] = {
  14. 0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09,
  15. 0x31, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x25, 0x07, 0x75, 0x04,
  16. 0x95, 0x01, 0x81, 0x42, 0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29,
  17. 0x0E, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0E, 0x81, 0x02,
  18. 0x06, 0x00, 0xFF, 0x09, 0x20, 0x75, 0x06, 0x95, 0x01, 0x15, 0x00,
  19. 0x25, 0x7F, 0x81, 0x02, 0x05, 0x01, 0x09, 0x33, 0x09, 0x34, 0x15,
  20. 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0x06,
  21. 0x00, 0xFF, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09,
  22. 0x22, 0x95, 0x1F, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24,
  23. 0xB1, 0x02, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xB1, 0x02, 0xC0
  24. };
  25. #define DS4_TOUCH_H 942
  26. #define DS4_STATUS_USB 0x1B // cable + level 11 (full)
  27. static unsigned long g_gyroLastMs[NSLOT] = { 0 };
  28. // NSLOT HID instances: 4 DS4 gamepads on the wire, one per bond slot. The host enumerates each as a
  29. // separate input device (hid-playstation, hid-sony, SDL, Steam Input).
  30. static Adafruit_USBD_HID g_hidGyro[NSLOT];
  31. // Per-slot MAC base: 4 distinct NICs so the host sees 4 different devices (real DS4s have unique MACs).
  32. // OUI 0x001BDC is Sony's; vary the last byte per slot.
  33. static const uint8_t DS4_MAC_BASE[5] = { 0x00, 0x1B, 0xDC, 0x4F, 0x55 };
  34. static uint8_t g_ds4Mac[NSLOT][6];
  35. static bool g_ds4MacInit = false;
  36. static void initDs4Macs()
  37. {
  38. if (g_ds4MacInit)
  39. return;
  40. for (int s = 0; s < NSLOT; s++) {
  41. memcpy(g_ds4Mac[s], DS4_MAC_BASE, 5);
  42. g_ds4Mac[s][5] = (uint8_t)(0x50 + s); // 0x50, 0x51, 0x52, 0x53
  43. }
  44. g_ds4MacInit = true;
  45. }
  46. // GET_FEATURE handler. Per-slot dispatch via per-instance callback (ps5Get##N passes the slot index).
  47. // Sizes per drivers/hid/hid-playstation.c: 0x02=37, 0x12=16, 0xA3=49; legacy hid-sony MAC 0x81=7.
  48. static uint16_t hidGyroGetCommon(uint8_t slot, uint8_t rid,
  49. hid_report_type_t type, uint8_t *buf,
  50. uint16_t reqlen)
  51. {
  52. (void)slot;
  53. if (type != HID_REPORT_TYPE_FEATURE || !buf || reqlen == 0)
  54. return 0;
  55. memset(buf, 0, reqlen);
  56. switch (rid) {
  57. case 0x02: // motion calibration (37 incl id)
  58. if (reqlen < 36)
  59. return 0;
  60. psNeutralCalib(buf);
  61. return 36;
  62. case 0x12: // pairing info / MAC, hid-playstation (16 incl id)
  63. if (reqlen < 15)
  64. return 0;
  65. memcpy(buf, g_ds4Mac[slot], 6);
  66. return 15;
  67. case 0x81: // MAC, legacy hid-sony USB (7 incl id)
  68. if (reqlen < 6)
  69. return 0;
  70. memcpy(buf, g_ds4Mac[slot], 6);
  71. return 6;
  72. case 0xA3: // firmware / hardware info (49 incl id)
  73. if (reqlen < 48)
  74. return 0;
  75. // non-zero version (contents not validated over USB)
  76. buf[0] = 0x01;
  77. return 48;
  78. default:
  79. return 0;
  80. }
  81. }
  82. static void hidGyroSetCommon(uint8_t slot, uint8_t rid, hid_report_type_t type,
  83. uint8_t const *b, uint16_t n)
  84. {
  85. if (type != HID_REPORT_TYPE_OUTPUT || n < 1)
  86. return;
  87. uint8_t id;
  88. const uint8_t *p;
  89. uint16_t pn;
  90. if (rid == 0) {
  91. id = b[0];
  92. p = b + 1;
  93. pn = (uint16_t)(n - 1);
  94. } else {
  95. id = rid;
  96. p = b;
  97. pn = n;
  98. }
  99. // DS4 USB effects: report 0x05, magic at byte 0, effects block starts at byte 3.
  100. if (id != 0x05 || pn < 5)
  101. return;
  102. // `slot` is the USB slot the report arrived on -> route rumble to its mapped bond slot.
  103. int bond = (slot < NSLOT) ? g_usbToBond[slot] : -1;
  104. if (bond < 0)
  105. return;
  106. hapticSteamRumble((uint16_t)p[4] * 257u, (uint16_t)p[3] * 257u,
  107. (uint8_t)bond); // DS4: left=low, right=high
  108. }
  109. // Per-slot callback trampolines -- the Adafruit HID class has ONE _get/_set pair shared across instances,
  110. // and the lib's tud_hid_*_cb doesn't pass the interface index to the user callback. Generate a
  111. // per-instance trampoline that closes over the slot index, matching the pattern in puck_hid.cpp.
  112. #define HIDGYROCB(N) \
  113. static uint16_t hidGyroGet##N(uint8_t r, hid_report_type_t t, \
  114. uint8_t *bf, uint16_t rl) \
  115. { \
  116. return hidGyroGetCommon(N, r, t, bf, rl); \
  117. } \
  118. static void hidGyroSet##N(uint8_t r, hid_report_type_t t, \
  119. uint8_t const *b, uint16_t n) \
  120. { \
  121. hidGyroSetCommon(N, r, t, b, n); \
  122. }
  123. // clang-format off
  124. HIDGYROCB(0)
  125. HIDGYROCB(1)
  126. HIDGYROCB(2)
  127. HIDGYROCB(3)
  128. // clang-format on
  129. typedef uint16_t (*ds4_getcb_t)(uint8_t, hid_report_type_t, uint8_t *,
  130. uint16_t);
  131. typedef void (*ds4_setcb_t)(uint8_t, hid_report_type_t, uint8_t const *,
  132. uint16_t);
  133. static ds4_getcb_t const DS4_GETCB[NSLOT] = { hidGyroGet0, hidGyroGet1,
  134. hidGyroGet2, hidGyroGet3 };
  135. static ds4_setcb_t const DS4_SETCB[NSLOT] = { hidGyroSet0, hidGyroSet1,
  136. hidGyroSet2, hidGyroSet3 };
  137. // usbSlot drives the per-HID counters; slot (bond) is the controller whose decoded input feeds the report.
  138. static void hidGyroBuild(uint8_t usbSlot, uint8_t slot, uint8_t out[63])
  139. {
  140. uint32_t b = psButtonsFromSteam(g_in[slot].buttons);
  141. bool lTouch = (b & TB_LPADT) || (b & TB_LPADC),
  142. rTouch = (b & TB_RPADT) || (b & TB_RPADC);
  143. memset(out, 0, 63);
  144. out[0] = swStick(g_in[slot].lx, false);
  145. out[1] = swStick(g_in[slot].ly, true);
  146. out[2] = swStick(g_in[slot].rx, false);
  147. out[3] = swStick(g_in[slot].ry, true);
  148. out[4] = psHatNibble(b) | psFaceNibble(b);
  149. out[5] = psShouldersByte(b, g_in[slot].lt, g_in[slot].rt);
  150. static uint8_t ctr[NSLOT] = { 0 };
  151. out[6] = ((ctr[usbSlot]++ & 0x0F) << 4) |
  152. ((b & TB_TOUCH || b & TB_LPADC || b & TB_RPADC) ? 0x02 : 0) |
  153. ((b & TB_STEAM) ? 0x01 : 0);
  154. out[7] = g_in[slot].lt;
  155. out[8] = g_in[slot].rt;
  156. out[12] = g_in[slot].gx & 0xFF;
  157. out[13] = g_in[slot].gx >> 8;
  158. out[14] = g_in[slot].gz & 0xFF;
  159. out[15] = g_in[slot].gz >> 8;
  160. out[16] = (-g_in[slot].gy) & 0xFF;
  161. out[17] = (-g_in[slot].gy) >> 8;
  162. out[18] = g_in[slot].ax & 0xFF;
  163. out[19] = g_in[slot].ax >> 8;
  164. out[20] = g_in[slot].ay & 0xFF;
  165. out[21] = g_in[slot].ay >> 8;
  166. out[22] = g_in[slot].az & 0xFF;
  167. out[23] = g_in[slot].az >> 8;
  168. out[29] = DS4_STATUS_USB;
  169. if (lTouch || rTouch) {
  170. uint16_t lx, ly, rx, ry;
  171. steamPadsToTouch(b, DS4_TOUCH_H, g_in[slot].lpx, g_in[slot].lpy,
  172. g_in[slot].rpx, g_in[slot].rpy, &lx, &ly, &rx,
  173. &ry);
  174. static uint8_t tstamp[NSLOT] = { 0 };
  175. out[32] = 1;
  176. out[33] = tstamp[usbSlot]++;
  177. touchPackPads(out + 34, lTouch, rTouch, lx, ly, rx, ry);
  178. } else {
  179. out[32] = 0;
  180. touchPackPads(
  181. out + 34, false, false, 0, 0, 0,
  182. 0); // contact 0x80 -- memset(0) reads as touch @0,0
  183. }
  184. }
  185. // Dynamic-mount mode: begin() is unused (setup() calls beginPool()+usbReenumerate instead).
  186. void HidGyroController::begin()
  187. {
  188. }
  189. // HID budget: clean DS4 (MODE_DS4_GAME) has no wake mouse; normal HIDGYRO keeps it (1 HID).
  190. uint8_t HidGyroController::maxSlots() const
  191. {
  192. uint8_t cap = modeIsCleanPS(g_usbMode) ? (uint8_t)CFG_TUD_HID :
  193. (uint8_t)(CFG_TUD_HID - 1);
  194. return cap < NSLOT ? cap : (uint8_t)NSLOT;
  195. }
  196. void HidGyroController::usbIdentity()
  197. {
  198. USBDevice.setID(0x054C, 0x05C4);
  199. USBDevice.setDeviceVersion(0x0120);
  200. USBDevice.setManufacturerDescriptor("Sony Computer Entertainment");
  201. USBDevice.setProductDescriptor("Wireless Controller");
  202. }
  203. void HidGyroController::beginPool()
  204. {
  205. initDs4Macs();
  206. uint8_t pool = maxSlots();
  207. for (uint8_t s = 0; s < pool; s++) {
  208. g_hidGyro[s].enableOutEndpoint(true);
  209. g_hidGyro[s].setReportCallback(DS4_GETCB[s], DS4_SETCB[s]);
  210. g_hidGyro[s].setReportDescriptor(GYRO_HID_DESC,
  211. sizeof GYRO_HID_DESC);
  212. g_hidGyro[s].setPollInterval(1);
  213. g_hidGyro[s].begin();
  214. }
  215. }
  216. void HidGyroController::mountSlots(uint8_t k)
  217. {
  218. for (uint8_t u = 0; u < k; u++)
  219. USBDevice.addInterface(g_hidGyro[u]);
  220. }
  221. void HidGyroController::task()
  222. {
  223. for (uint8_t u = 0; u < g_usbMountCount; u++) {
  224. if (!g_hidGyro[u].ready())
  225. continue;
  226. if (millis() - g_gyroLastMs[u] < USB_STREAM_MS)
  227. continue;
  228. int bond = g_usbToBond[u];
  229. if (bond < 0)
  230. continue;
  231. g_gyroLastMs[u] = millis();
  232. uint8_t p[63];
  233. hidGyroBuild(u, (uint8_t)bond, p);
  234. usbTxHid(&g_hidGyro[u], 0x01, p, sizeof p);
  235. }
  236. }