mode_ps5.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #include "mode_ps5.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. Ps5Controller g_ps5Ctl;
  10. static const uint8_t PS5_HID_DESC[] = {
  11. 0x05, 0x01, 0x09, 0x05, 0xA1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31,
  12. 0x09, 0x32, 0x09, 0x35, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xFF,
  13. 0x00, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x20,
  14. 0x95, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x39, 0x15, 0x00, 0x25, 0x07,
  15. 0x35, 0x00, 0x46, 0x3B, 0x01, 0x65, 0x14, 0x75, 0x04, 0x95, 0x01, 0x81,
  16. 0x42, 0x65, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0F, 0x15, 0x00, 0x25,
  17. 0x01, 0x75, 0x01, 0x95, 0x0F, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x21,
  18. 0x95, 0x0D, 0x81, 0x02, 0x06, 0x00, 0xFF, 0x09, 0x22, 0x15, 0x00, 0x26,
  19. 0xFF, 0x00, 0x75, 0x08, 0x95, 0x34, 0x81, 0x02, 0x85, 0x02, 0x09, 0x23,
  20. 0x95, 0x2F, 0x91, 0x02, 0x85, 0x05, 0x09, 0x33, 0x95, 0x28, 0xB1, 0x02,
  21. 0x85, 0x08, 0x09, 0x34, 0x95, 0x2F, 0xB1, 0x02, 0x85, 0x09, 0x09, 0x24,
  22. 0x95, 0x13, 0xB1, 0x02, 0x85, 0x0A, 0x09, 0x25, 0x95, 0x1A, 0xB1, 0x02,
  23. 0x85, 0x20, 0x09, 0x26, 0x95, 0x3F, 0xB1, 0x02, 0x85, 0x21, 0x09, 0x27,
  24. 0x95, 0x04, 0xB1, 0x02, 0x85, 0x22, 0x09, 0x40, 0x95, 0x3F, 0xB1, 0x02,
  25. 0x85, 0x80, 0x09, 0x28, 0x95, 0x3F, 0xB1, 0x02, 0x85, 0x81, 0x09, 0x29,
  26. 0x95, 0x3F, 0xB1, 0x02, 0x85, 0x82, 0x09, 0x2A, 0x95, 0x09, 0xB1, 0x02,
  27. 0x85, 0x83, 0x09, 0x2B, 0x95, 0x3F, 0xB1, 0x02, 0x85, 0x84, 0x09, 0x2C,
  28. 0x95, 0x3F, 0xB1, 0x02, 0x85, 0x85, 0x09, 0x2D, 0x95, 0x02, 0xB1, 0x02,
  29. 0x85, 0xA0, 0x09, 0x2E, 0x95, 0x01, 0xB1, 0x02, 0x85, 0xE0, 0x09, 0x2F,
  30. 0x95, 0x3F, 0xB1, 0x02, 0x85, 0xF0, 0x09, 0x30, 0x95, 0x3F, 0xB1, 0x02,
  31. 0x85, 0xF1, 0x09, 0x31, 0x95, 0x3F, 0xB1, 0x02, 0x85, 0xF2, 0x09, 0x32,
  32. 0x95, 0x0F, 0xB1, 0x02, 0x85, 0xF4, 0x09, 0x35, 0x95, 0x3F, 0xB1, 0x02,
  33. 0x85, 0xF5, 0x09, 0x36, 0x95, 0x03, 0xB1, 0x02, 0xC0
  34. };
  35. #define PS5_TOUCH_H 1080
  36. #define PS5_STATUS_USB 0x1A // charging + level 10 (~100%)
  37. static unsigned long g_ps5LastMs = 0;
  38. static Adafruit_USBD_HID g_ps5;
  39. static const uint8_t PS5_MAC[6] = { 0x00, 0x1B, 0xDC, 0x4F, 0x55, 0x54 };
  40. // GET_FEATURE handler. SteamOS's hid-playstation reads calibration (0x05), pairing/MAC (0x09) and firmware
  41. // (0x20) during probe and ABORTS the bind unless each returns its EXACT size with the report id as the first
  42. // byte; CRC is checked only over Bluetooth. macOS/SDL are lenient, so the DualSense shows up there but not on a
  43. // Deck. TinyUSB writes the report id itself and hands us the buffer PAST it, so we fill only the PAYLOAD and
  44. // return size-1. Sizes per drivers/hid/hid-playstation.c: 0x05=41, 0x09=20, 0x20=64.
  45. static uint16_t ps5Get(uint8_t rid, hid_report_type_t type, uint8_t *buf,
  46. uint16_t reqlen)
  47. {
  48. if (type != HID_REPORT_TYPE_FEATURE || !buf || reqlen == 0)
  49. return 0;
  50. memset(buf, 0, reqlen);
  51. switch (rid) {
  52. // capabilities: identify as DualSense-capable (SDL-only probe; hid-playstation never reads 0x03)
  53. case 0x03: {
  54. if (reqlen < 47)
  55. return 0;
  56. buf[0] = 0x00;
  57. buf[1] = 0x28;
  58. buf[2] = 0x01;
  59. buf[3] = 0x00;
  60. buf[4] = 0x0E; // sensors + lightbar + vibration capability bits
  61. return 47;
  62. }
  63. case 0x05: // motion calibration (41 incl id)
  64. if (reqlen < 40)
  65. return 0;
  66. psNeutralCalib(buf);
  67. return 40;
  68. case 0x09: // pairing info / MAC (20 incl id)
  69. if (reqlen < 19)
  70. return 0;
  71. // MAC at kernel buf[1..6] = payload[0..5]
  72. memcpy(buf, PS5_MAC, 6);
  73. return 19;
  74. case 0x20: // firmware info (64 incl id)
  75. if (reqlen < 63)
  76. return 0;
  77. buf[23] = 0x01; // hw_version (le32 @ kernel buf[24]) non-zero
  78. buf[27] = 0x01; // fw_version (le32 @ kernel buf[28]) non-zero
  79. return 63;
  80. default:
  81. return 0;
  82. }
  83. }
  84. static void ps5Set(uint8_t rid, hid_report_type_t type, uint8_t const *b,
  85. uint16_t n)
  86. {
  87. if (type != HID_REPORT_TYPE_OUTPUT || n < 1)
  88. return;
  89. uint8_t id;
  90. const uint8_t *p;
  91. uint16_t pn;
  92. if (rid == 0) {
  93. id = b[0];
  94. p = b + 1;
  95. pn = (uint16_t)(n - 1);
  96. } else if (rid == 0x02 && b[0] == 0x02 && n >= 5) {
  97. id = rid;
  98. p = b + 1;
  99. pn = (uint16_t)(n - 1);
  100. } // some paths leave report id in b
  101. else {
  102. id = rid;
  103. p = b;
  104. pn = n;
  105. }
  106. if (id != 0x02 || pn < 4)
  107. return;
  108. hapticSteamRumble((uint16_t)p[3] * 257u,
  109. (uint16_t)p[2] *
  110. 257u); // DualSense: left=low, right=high
  111. }
  112. static void ps5Build(uint8_t out[63])
  113. {
  114. uint32_t b = psButtonsFromSteam(g_in.buttons);
  115. bool lTouch = (b & TB_LPADT) || (b & TB_LPADC),
  116. rTouch = (b & TB_RPADT) || (b & TB_RPADC);
  117. memset(out, 0, 63);
  118. out[0] = swStick(g_in.lx, false);
  119. out[1] = swStick(g_in.ly, true);
  120. out[2] = swStick(g_in.rx, false);
  121. out[3] = swStick(g_in.ry, true);
  122. out[4] = g_in.lt;
  123. out[5] = g_in.rt;
  124. static uint8_t seq = 0;
  125. out[6] = seq++;
  126. out[7] = psHatNibble(b) | psFaceNibble(b);
  127. out[8] = psShouldersByte(b);
  128. out[9] = ((b & TB_STEAM) ? 0x01 : 0) |
  129. ((b & TB_TOUCH || b & TB_LPADC || b & TB_RPADC) ? 0x02 : 0) |
  130. ((b & TB_MUTE) ? 0x04 : 0);
  131. out[15] = g_in.gx & 0xFF;
  132. out[16] = g_in.gx >> 8;
  133. out[17] = g_in.gz & 0xFF;
  134. out[18] = g_in.gz >> 8;
  135. out[19] = (-g_in.gy) & 0xFF;
  136. out[20] = (-g_in.gy) >> 8;
  137. out[21] = g_in.ax & 0xFF;
  138. out[22] = g_in.ax >> 8;
  139. out[23] = g_in.ay & 0xFF;
  140. out[24] = g_in.ay >> 8;
  141. out[25] = g_in.az & 0xFF;
  142. out[26] = g_in.az >> 8;
  143. uint16_t lx, ly, rx, ry;
  144. steamPadsToTouch(b, PS5_TOUCH_H, g_in.lpx, g_in.lpy, g_in.rpx, g_in.rpy,
  145. &lx, &ly, &rx, &ry);
  146. touchPackPads(out + 32, lTouch, rTouch, lx, ly, rx, ry);
  147. out[52] = PS5_STATUS_USB;
  148. }
  149. void Ps5Controller::begin()
  150. {
  151. USBDevice.setID(0x054C, 0x0CE6);
  152. // Host re-reads config by VID:PID:serial (per-mode suffix); bump invalidates any cached descriptor
  153. USBDevice.setDeviceVersion(0x0104);
  154. USBDevice.setManufacturerDescriptor("Sony Interactive Entertainment");
  155. USBDevice.setProductDescriptor("DualSense Wireless Controller");
  156. g_ps5.enableOutEndpoint(true);
  157. g_ps5.setReportCallback(ps5Get, ps5Set);
  158. g_ps5.setReportDescriptor(PS5_HID_DESC, sizeof PS5_HID_DESC);
  159. // 1ms bInterval so the RF rate is the only latency limit (matches Xbox)
  160. g_ps5.setPollInterval(1);
  161. g_ps5.begin();
  162. }
  163. void Ps5Controller::task()
  164. {
  165. if (!g_ps5.ready())
  166. return;
  167. if (millis() - g_ps5LastMs < USB_STREAM_MS)
  168. return;
  169. g_ps5LastMs = millis();
  170. uint8_t p[63];
  171. ps5Build(p);
  172. g_ps5.sendReport(0x01, p, sizeof p);
  173. }