mode_hidgyro.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. // Genuine DualShock 4 (054C:05C4) USB HID report descriptor -- all 467 bytes, verbatim from a real pad
  14. // (same bytes ViGEmBus feeds its emulated DS4, which games accept as genuine).
  15. //
  16. // The input/output reports are the ones we actually use -- input 0x01 = 63 payload bytes (+ id = 64 on the
  17. // wire, what hidGyroBuild() sends) and output 0x05 = 31 -- but the long tail of FEATURE declarations is not
  18. // decoration, it is load-bearing on Windows: the HID class driver only routes GET_FEATURE for report ids that
  19. // appear HERE, and sizes the transfer from caps.FeatureReportByteLength. A game identifying a DS4 does that
  20. // by reading feature 0x12 (MAC) / 0xA3 (firmware info) -- the two hidGyroGetCommon() answers -- so with a
  21. // descriptor that declares only 0x02/0x04 those reads fail inside Windows, never reach us, and the game
  22. // concludes the device is not a DualShock 4: no controller at all, even though input reports flow fine
  23. // (joy.cpl, Steam and DS4Windows all work, since they read input reports and treat the feature reads as
  24. // optional). Do not trim this back to "just the reports we implement".
  25. static const uint8_t GYRO_HID_DESC[] = {
  26. 0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31,
  27. 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
  28. 0x04, 0x81, 0x02, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07, 0x35, 0x00, 0x46,
  29. 0x3B, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x65, 0x00,
  30. 0x05, 0x09, 0x19, 0x01, 0x29, 0x0E, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
  31. 0x95, 0x0E, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x20, 0x75, 0x06, 0x95,
  32. 0x01, 0x15, 0x00, 0x25, 0x7F, 0x81, 0x02, 0x05, 0x01, 0x09, 0x33, 0x09,
  33. 0x34, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02,
  34. 0x06, 0x00, 0xFF, 0x09, 0x21, 0x95, 0x36, 0x81, 0x02, 0x85, 0x05, 0x09,
  35. 0x22, 0x95, 0x1F, 0x91, 0x02, 0x85, 0x04, 0x09, 0x23, 0x95, 0x24, 0xB1,
  36. 0x02, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xB1, 0x02, 0x85, 0x08, 0x09,
  37. 0x25, 0x95, 0x03, 0xB1, 0x02, 0x85, 0x10, 0x09, 0x26, 0x95, 0x04, 0xB1,
  38. 0x02, 0x85, 0x11, 0x09, 0x27, 0x95, 0x02, 0xB1, 0x02, 0x85, 0x12, 0x06,
  39. 0x02, 0xFF, 0x09, 0x21, 0x95, 0x0F, 0xB1, 0x02, 0x85, 0x13, 0x09, 0x22,
  40. 0x95, 0x16, 0xB1, 0x02, 0x85, 0x14, 0x06, 0x05, 0xFF, 0x09, 0x20, 0x95,
  41. 0x10, 0xB1, 0x02, 0x85, 0x15, 0x09, 0x21, 0x95, 0x2C, 0xB1, 0x02, 0x06,
  42. 0x80, 0xFF, 0x85, 0x80, 0x09, 0x20, 0x95, 0x06, 0xB1, 0x02, 0x85, 0x81,
  43. 0x09, 0x21, 0x95, 0x06, 0xB1, 0x02, 0x85, 0x82, 0x09, 0x22, 0x95, 0x05,
  44. 0xB1, 0x02, 0x85, 0x83, 0x09, 0x23, 0x95, 0x01, 0xB1, 0x02, 0x85, 0x84,
  45. 0x09, 0x24, 0x95, 0x04, 0xB1, 0x02, 0x85, 0x85, 0x09, 0x25, 0x95, 0x06,
  46. 0xB1, 0x02, 0x85, 0x86, 0x09, 0x26, 0x95, 0x06, 0xB1, 0x02, 0x85, 0x87,
  47. 0x09, 0x27, 0x95, 0x23, 0xB1, 0x02, 0x85, 0x88, 0x09, 0x28, 0x95, 0x22,
  48. 0xB1, 0x02, 0x85, 0x89, 0x09, 0x29, 0x95, 0x02, 0xB1, 0x02, 0x85, 0x90,
  49. 0x09, 0x30, 0x95, 0x05, 0xB1, 0x02, 0x85, 0x91, 0x09, 0x31, 0x95, 0x03,
  50. 0xB1, 0x02, 0x85, 0x92, 0x09, 0x32, 0x95, 0x03, 0xB1, 0x02, 0x85, 0x93,
  51. 0x09, 0x33, 0x95, 0x0C, 0xB1, 0x02, 0x85, 0xA0, 0x09, 0x40, 0x95, 0x06,
  52. 0xB1, 0x02, 0x85, 0xA1, 0x09, 0x41, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xA2,
  53. 0x09, 0x42, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xA3, 0x09, 0x43, 0x95, 0x30,
  54. 0xB1, 0x02, 0x85, 0xA4, 0x09, 0x44, 0x95, 0x0D, 0xB1, 0x02, 0x85, 0xA5,
  55. 0x09, 0x45, 0x95, 0x15, 0xB1, 0x02, 0x85, 0xA6, 0x09, 0x46, 0x95, 0x15,
  56. 0xB1, 0x02, 0x85, 0xF0, 0x09, 0x47, 0x95, 0x3F, 0xB1, 0x02, 0x85, 0xF1,
  57. 0x09, 0x48, 0x95, 0x3F, 0xB1, 0x02, 0x85, 0xF2, 0x09, 0x49, 0x95, 0x0F,
  58. 0xB1, 0x02, 0x85, 0xA7, 0x09, 0x4A, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xA8,
  59. 0x09, 0x4B, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xA9, 0x09, 0x4C, 0x95, 0x08,
  60. 0xB1, 0x02, 0x85, 0xAA, 0x09, 0x4E, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xAB,
  61. 0x09, 0x4F, 0x95, 0x39, 0xB1, 0x02, 0x85, 0xAC, 0x09, 0x50, 0x95, 0x39,
  62. 0xB1, 0x02, 0x85, 0xAD, 0x09, 0x51, 0x95, 0x0B, 0xB1, 0x02, 0x85, 0xAE,
  63. 0x09, 0x52, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xAF, 0x09, 0x53, 0x95, 0x02,
  64. 0xB1, 0x02, 0x85, 0xB0, 0x09, 0x54, 0x95, 0x3F, 0xB1, 0x02, 0xC0
  65. };
  66. #define DS4_TOUCH_H 942
  67. #define DS4_STATUS_USB 0x1B // cable + level 11 (full)
  68. static unsigned long g_gyroLastMs[NSLOT] = { 0 };
  69. // NSLOT HID instances: 4 DS4 gamepads on the wire, one per bond slot. The host enumerates each as a
  70. // separate input device (hid-playstation, hid-sony, SDL, Steam Input).
  71. static Adafruit_USBD_HID g_hidGyro[NSLOT];
  72. // Per-slot MAC base: 4 distinct NICs so the host sees 4 different devices (real DS4s have unique MACs).
  73. // OUI 0x001BDC is Sony's; vary the last byte per slot.
  74. static const uint8_t DS4_MAC_BASE[5] = { 0x00, 0x1B, 0xDC, 0x4F, 0x55 };
  75. static uint8_t g_ds4Mac[NSLOT][6];
  76. static bool g_ds4MacInit = false;
  77. static void initDs4Macs()
  78. {
  79. if (g_ds4MacInit)
  80. return;
  81. for (int s = 0; s < NSLOT; s++) {
  82. memcpy(g_ds4Mac[s], DS4_MAC_BASE, 5);
  83. g_ds4Mac[s][5] = (uint8_t)(0x50 + s); // 0x50, 0x51, 0x52, 0x53
  84. }
  85. g_ds4MacInit = true;
  86. }
  87. // GET_FEATURE handler. Per-slot dispatch via per-instance callback (ps5Get##N passes the slot index).
  88. // Sizes per drivers/hid/hid-playstation.c: 0x02=37, 0x12=16, 0xA3=49; legacy hid-sony MAC 0x81=7.
  89. static uint16_t hidGyroGetCommon(uint8_t slot, uint8_t rid,
  90. hid_report_type_t type, uint8_t *buf,
  91. uint16_t reqlen)
  92. {
  93. (void)slot;
  94. if (type != HID_REPORT_TYPE_FEATURE || !buf || reqlen == 0)
  95. return 0;
  96. memset(buf, 0, reqlen);
  97. switch (rid) {
  98. case 0x02: // motion calibration (37 incl id)
  99. if (reqlen < 36)
  100. return 0;
  101. psNeutralCalib(buf);
  102. return 36;
  103. case 0x12: // pairing info / MAC, hid-playstation (16 incl id)
  104. if (reqlen < 15)
  105. return 0;
  106. memcpy(buf, g_ds4Mac[slot], 6);
  107. return 15;
  108. case 0x81: // MAC, legacy hid-sony USB (7 incl id)
  109. if (reqlen < 6)
  110. return 0;
  111. memcpy(buf, g_ds4Mac[slot], 6);
  112. return 6;
  113. case 0xA3: // firmware / hardware info (49 incl id)
  114. if (reqlen < 48)
  115. return 0;
  116. // non-zero version (contents not validated over USB)
  117. buf[0] = 0x01;
  118. return 48;
  119. default:
  120. return 0;
  121. }
  122. }
  123. static void hidGyroSetCommon(uint8_t slot, uint8_t rid, hid_report_type_t type,
  124. uint8_t const *b, uint16_t n)
  125. {
  126. if (type != HID_REPORT_TYPE_OUTPUT || n < 1)
  127. return;
  128. uint8_t id;
  129. const uint8_t *p;
  130. uint16_t pn;
  131. if (rid == 0) {
  132. id = b[0];
  133. p = b + 1;
  134. pn = (uint16_t)(n - 1);
  135. } else {
  136. id = rid;
  137. p = b;
  138. pn = n;
  139. }
  140. // DS4 USB effects: report 0x05, magic at byte 0, effects block starts at byte 3.
  141. if (id != 0x05 || pn < 5)
  142. return;
  143. // `slot` is the USB slot the report arrived on -> route rumble to its mapped bond slot.
  144. int bond = (slot < NSLOT) ? g_usbToBond[slot] : -1;
  145. if (bond < 0)
  146. return;
  147. hapticSteamRumble((uint16_t)p[4] * 257u, (uint16_t)p[3] * 257u,
  148. (uint8_t)bond); // DS4: left=low, right=high
  149. }
  150. // Per-slot callback trampolines -- the Adafruit HID class has ONE _get/_set pair shared across instances,
  151. // and the lib's tud_hid_*_cb doesn't pass the interface index to the user callback. Generate a
  152. // per-instance trampoline that closes over the slot index, matching the pattern in puck_hid.cpp.
  153. #define HIDGYROCB(N) \
  154. static uint16_t hidGyroGet##N(uint8_t r, hid_report_type_t t, \
  155. uint8_t *bf, uint16_t rl) \
  156. { \
  157. return hidGyroGetCommon(N, r, t, bf, rl); \
  158. } \
  159. static void hidGyroSet##N(uint8_t r, hid_report_type_t t, \
  160. uint8_t const *b, uint16_t n) \
  161. { \
  162. hidGyroSetCommon(N, r, t, b, n); \
  163. }
  164. // clang-format off
  165. HIDGYROCB(0)
  166. HIDGYROCB(1)
  167. HIDGYROCB(2)
  168. HIDGYROCB(3)
  169. // clang-format on
  170. typedef uint16_t (*ds4_getcb_t)(uint8_t, hid_report_type_t, uint8_t *,
  171. uint16_t);
  172. typedef void (*ds4_setcb_t)(uint8_t, hid_report_type_t, uint8_t const *,
  173. uint16_t);
  174. static ds4_getcb_t const DS4_GETCB[NSLOT] = { hidGyroGet0, hidGyroGet1,
  175. hidGyroGet2, hidGyroGet3 };
  176. static ds4_setcb_t const DS4_SETCB[NSLOT] = { hidGyroSet0, hidGyroSet1,
  177. hidGyroSet2, hidGyroSet3 };
  178. // usbSlot drives the per-HID counters; slot (bond) is the controller whose decoded input feeds the report.
  179. static void hidGyroBuild(uint8_t usbSlot, uint8_t slot, uint8_t out[63])
  180. {
  181. uint32_t b = psButtonsFromSteam(g_in[slot].buttons);
  182. // A pad mapped to a stick must NOT also report as a touchpad contact -- the host would read the same
  183. // finger twice (stick deflection AND a cursor drag).
  184. bool lTouch = g_padStick[0] == PS_OFF &&
  185. ((b & TB_LPADT) || (b & TB_LPADC)),
  186. rTouch = g_padStick[1] == PS_OFF &&
  187. ((b & TB_RPADT) || (b & TB_RPADC));
  188. memset(out, 0, 63);
  189. int16_t lx, ly, rx, ry;
  190. slotSticks(slot, &lx, &ly, &rx, &ry);
  191. out[0] = swStick(lx, false);
  192. out[1] = swStick(ly, true);
  193. out[2] = swStick(rx, false);
  194. out[3] = swStick(ry, true);
  195. out[4] = psHatNibble(b) | psFaceNibble(b);
  196. out[5] = psShouldersByte(b, g_in[slot].lt, g_in[slot].rt);
  197. static uint8_t ctr[NSLOT] = { 0 };
  198. out[6] = ((ctr[usbSlot]++ & 0x0F) << 4) |
  199. ((b & TB_TOUCH || b & TB_LPADC || b & TB_RPADC) ? 0x02 : 0) |
  200. ((b & TB_STEAM) ? 0x01 : 0);
  201. out[7] = g_in[slot].lt;
  202. out[8] = g_in[slot].rt;
  203. // gyro X/Y/Z then accel X/Y/Z, 6 x le16 (hid-sony: rd[13..] / rd[19..])
  204. psImuPack(out + 12, g_in[slot]);
  205. out[29] = DS4_STATUS_USB;
  206. if (lTouch || rTouch) {
  207. uint16_t tlx, tly, trx, trry;
  208. steamPadsToTouch(b, DS4_TOUCH_H, g_in[slot].lpx, g_in[slot].lpy,
  209. g_in[slot].rpx, g_in[slot].rpy, &tlx, &tly,
  210. &trx, &trry);
  211. static uint8_t tstamp[NSLOT] = { 0 };
  212. out[32] = 1;
  213. out[33] = tstamp[usbSlot]++;
  214. touchPackPads(out + 34, lTouch, rTouch, tlx, tly, trx, trry);
  215. } else {
  216. out[32] = 0;
  217. touchPackPads(
  218. out + 34, false, false, 0, 0, 0,
  219. 0); // contact 0x80 -- memset(0) reads as touch @0,0
  220. }
  221. }
  222. // Dynamic-mount mode: begin() is unused (setup() calls beginPool()+usbReenumerate instead).
  223. void HidGyroController::begin()
  224. {
  225. }
  226. uint8_t HidGyroController::maxSlots() const
  227. {
  228. // Clean-PS modes exist to present exactly what a host that CLASSIFIES the USB device expects of a real
  229. // Sony pad -- GameInput / Windows.Gaming.Input and the native-PlayStation paths in games look at the
  230. // whole device, not just the VID/PID, and refuse the PS glyph path for a composite. One connected
  231. // controller = one HID interface = a non-composite device (no MI_xx on Windows). A SECOND controller
  232. // would add a second interface and make it composite again, so the clean modes are deliberately
  233. // single-pad; use MODE_PS5 / MODE_HIDGYRO (composite either way, and they keep the panel + host-wake)
  234. // when you want several controllers at once.
  235. if (modeIsCleanPS(g_usbMode))
  236. return 1;
  237. uint8_t cap = (uint8_t)(CFG_TUD_HID - 1);
  238. return cap < NSLOT ? cap : (uint8_t)NSLOT;
  239. }
  240. void HidGyroController::usbIdentity()
  241. {
  242. USBDevice.setID(0x054C, 0x05C4);
  243. USBDevice.setVersion(0x0200);
  244. USBDevice.setDeviceVersion(0x0120);
  245. USBDevice.setManufacturerDescriptor("Sony Computer Entertainment");
  246. USBDevice.setProductDescriptor("Wireless Controller");
  247. }
  248. void HidGyroController::beginPool()
  249. {
  250. initDs4Macs();
  251. uint8_t pool = maxSlots();
  252. for (uint8_t s = 0; s < pool; s++) {
  253. g_hidGyro[s].enableOutEndpoint(true);
  254. g_hidGyro[s].setReportCallback(DS4_GETCB[s], DS4_SETCB[s]);
  255. g_hidGyro[s].setReportDescriptor(GYRO_HID_DESC,
  256. sizeof GYRO_HID_DESC);
  257. g_hidGyro[s].setPollInterval(1);
  258. g_hidGyro[s].begin();
  259. }
  260. }
  261. void HidGyroController::mountSlots(uint8_t k)
  262. {
  263. for (uint8_t u = 0; u < k; u++)
  264. USBDevice.addInterface(g_hidGyro[u]);
  265. }
  266. void HidGyroController::task()
  267. {
  268. for (uint8_t u = 0; u < g_usbMountCount; u++) {
  269. if (!g_hidGyro[u].ready())
  270. continue;
  271. if (millis() - g_gyroLastMs[u] < USB_STREAM_MS)
  272. continue;
  273. int bond = g_usbToBond[u];
  274. if (bond < 0)
  275. continue;
  276. g_gyroLastMs[u] = millis();
  277. uint8_t p[63];
  278. hidGyroBuild(u, (uint8_t)bond, p);
  279. usbTxHid(&g_hidGyro[u], 0x01, p, sizeof p);
  280. }
  281. }