xinput.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // xinput.c — Xbox 360 wired (XInput) personality (see xinput.h).
  2. //
  3. // A custom TinyUSB class driver serves the vendor interface (0xFF/0x5D/0x01)
  4. // and its interrupt IN/OUT endpoints. The interface descriptor is emitted by
  5. // usb_descriptors.c; here we claim the endpoints in the driver's open()
  6. // callback, stream the 20-byte report from g_in, and decode host rumble on the
  7. // OUT endpoint. Single interface (the active controller) — PicoPuck presents one
  8. // emulated controller at a time, so the per-slot pool of the OpenPuck original
  9. // collapses to one state block here.
  10. //
  11. // PicoPuck's main loop is cooperative (no separate usbd task), so IN transfers
  12. // are issued directly from xinput_task() rather than deferred to a SOF drain.
  13. //
  14. // SPDX-License-Identifier: AGPL-3.0-or-later
  15. #include "puck/xinput.h"
  16. #include "puck/triton.h"
  17. #include "puck/slots.h"
  18. #include "puck/relay.h"
  19. #include "sys/settings.h"
  20. #include "config/modes.h"
  21. #include <string.h>
  22. #include "tusb.h"
  23. #include "device/usbd_pvt.h"
  24. #include "pico/time.h"
  25. #define ET_XBOX 0 // per-type config index (matches OpenPuck ET_XBOX)
  26. // XInput button bits.
  27. enum {
  28. XB_DUP = 0x0001, XB_DDOWN = 0x0002, XB_DLEFT = 0x0004, XB_DRIGHT = 0x0008,
  29. XB_START = 0x0010, XB_BACK = 0x0020, XB_L3 = 0x0040, XB_R3 = 0x0080,
  30. XB_LB = 0x0100, XB_RB = 0x0200, XB_GUIDE = 0x0400,
  31. XB_A = 0x1000, XB_B = 0x2000, XB_X = 0x4000, XB_Y = 0x8000
  32. };
  33. static struct {
  34. uint8_t itf, ep_in, ep_out;
  35. uint8_t in_buf[32], out_buf[32];
  36. bool in_use;
  37. } g_xi;
  38. // ---- custom class driver ---------------------------------------------------
  39. static void xi_init(void) { g_xi.in_use = false; }
  40. static bool xi_deinit(void) { return true; }
  41. static void xi_reset(uint8_t rhport)
  42. {
  43. (void)rhport;
  44. g_xi.itf = g_xi.ep_in = g_xi.ep_out = 0;
  45. g_xi.in_use = false;
  46. }
  47. static uint16_t xi_open(uint8_t rhport, tusb_desc_interface_t const *itf,
  48. uint16_t max_len)
  49. {
  50. if (!(itf->bInterfaceClass == 0xFF && itf->bInterfaceSubClass == 0x5D &&
  51. itf->bInterfaceProtocol == 0x01))
  52. return 0; // not the XInput interface → let another driver claim it
  53. g_xi.itf = itf->bInterfaceNumber;
  54. uint8_t const *p = (uint8_t const *)itf;
  55. uint8_t const *end = p + max_len;
  56. uint16_t used = itf->bLength;
  57. p += itf->bLength;
  58. uint8_t opened = 0;
  59. while (p < end && opened < itf->bNumEndpoints) {
  60. uint8_t blen = p[0], btype = p[1];
  61. if (btype == TUSB_DESC_ENDPOINT) {
  62. tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const *)p;
  63. usbd_edpt_open(rhport, ep);
  64. if (tu_edpt_dir(ep->bEndpointAddress) == TUSB_DIR_IN)
  65. g_xi.ep_in = ep->bEndpointAddress;
  66. else
  67. g_xi.ep_out = ep->bEndpointAddress;
  68. opened++;
  69. }
  70. used += blen;
  71. p += blen;
  72. }
  73. g_xi.in_use = true;
  74. if (g_xi.ep_out) // arm OUT (rumble/LED)
  75. usbd_edpt_xfer(rhport, g_xi.ep_out, g_xi.out_buf, sizeof(g_xi.out_buf));
  76. return used;
  77. }
  78. static bool xi_ctrl(uint8_t rhport, uint8_t stage,
  79. tusb_control_request_t const *req)
  80. {
  81. (void)rhport; (void)req;
  82. return stage != CONTROL_STAGE_SETUP;
  83. }
  84. static bool xi_xfer(uint8_t rhport, uint8_t ep, xfer_result_t res, uint32_t n)
  85. {
  86. (void)res;
  87. if (!g_xi.in_use)
  88. return true;
  89. if (ep == g_xi.ep_out) {
  90. // Rumble packet: [00][08][00][big][small][00][00][00]; LED [01][03][led].
  91. if (n >= 5 && g_xi.out_buf[0] == 0x00 && g_xi.out_buf[1] == 0x08) {
  92. int slot = -1;
  93. for (int s = 0; s < PP_NSLOT; s++)
  94. if (g_slot[s].connected) { slot = s; break; }
  95. if (slot >= 0 && settings()->type[ET_XBOX].rumble)
  96. puck_rumble(slot, (uint16_t)g_xi.out_buf[3] * 257u,
  97. (uint16_t)g_xi.out_buf[4] * 257u);
  98. }
  99. usbd_edpt_xfer(rhport, g_xi.ep_out, g_xi.out_buf, sizeof(g_xi.out_buf));
  100. }
  101. return true;
  102. }
  103. static const usbd_class_driver_t g_xi_driver = {
  104. #if CFG_TUSB_DEBUG >= 2
  105. .name = "XINPUT",
  106. #endif
  107. .init = xi_init,
  108. .deinit = xi_deinit,
  109. .reset = xi_reset,
  110. .open = xi_open,
  111. .control_xfer_cb = xi_ctrl,
  112. .xfer_cb = xi_xfer,
  113. .sof = NULL,
  114. };
  115. // TinyUSB calls this once at init to collect app class drivers. The XInput
  116. // driver's open() only claims 0xFF/0x5D/0x01 interfaces (which exist only in
  117. // MODE_XBOX), so registering it unconditionally is harmless in other modes.
  118. const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *count)
  119. {
  120. *count = 1;
  121. return &g_xi_driver;
  122. }
  123. // ---- report building -------------------------------------------------------
  124. // Back-paddle / QAM button code → XInput bit (0=none 1=A..15=Dright).
  125. static uint16_t code_to_xb(uint8_t c)
  126. {
  127. switch (c) {
  128. case 1: return XB_A;
  129. case 2: return XB_B;
  130. case 3: return XB_X;
  131. case 4: return XB_Y;
  132. case 5: return XB_LB;
  133. case 6: return XB_RB;
  134. case 7: return XB_L3;
  135. case 8: return XB_R3;
  136. case 9: return XB_BACK;
  137. case 10: return XB_START;
  138. case 11: return XB_GUIDE;
  139. case 12: return XB_DUP;
  140. case 13: return XB_DDOWN;
  141. case 14: return XB_DLEFT;
  142. case 15: return XB_DRIGHT;
  143. default: return 0;
  144. }
  145. }
  146. static int active_slot(void)
  147. {
  148. for (int s = 0; s < PP_NSLOT; s++)
  149. if (g_slot[s].connected)
  150. return s;
  151. return 0;
  152. }
  153. static void xi_build(int slot, uint8_t *r)
  154. {
  155. const pp_type_cfg_t *t = &settings()->type[ET_XBOX];
  156. uint32_t b = g_in[slot].buttons;
  157. uint16_t btn = 0;
  158. if (b & TB_DUP) btn |= XB_DUP;
  159. if (b & TB_DDN) btn |= XB_DDOWN;
  160. if (b & TB_DLF) btn |= XB_DLEFT;
  161. if (b & TB_DRT) btn |= XB_DRIGHT;
  162. if (b & TB_VIEW) btn |= XB_START;
  163. if (b & TB_MENU) btn |= XB_BACK;
  164. if (b & TB_STEAM) btn |= XB_GUIDE;
  165. if (b & TB_LB) btn |= XB_LB;
  166. if (b & TB_RB) btn |= XB_RB;
  167. if (b & TB_L3) btn |= XB_L3;
  168. if (b & TB_R3) btn |= XB_R3;
  169. uint16_t fA = t->ab_swap ? XB_B : XB_A, fB = t->ab_swap ? XB_A : XB_B;
  170. uint16_t fX = t->ab_swap ? XB_Y : XB_X, fY = t->ab_swap ? XB_X : XB_Y;
  171. if (b & TB_A) btn |= fA;
  172. if (b & TB_B) btn |= fB;
  173. if (b & TB_X) btn |= fX;
  174. if (b & TB_Y) btn |= fY;
  175. if (b & TB_L4) btn |= code_to_xb(t->back[0]);
  176. if (b & TB_R4) btn |= code_to_xb(t->back[1]);
  177. if (b & TB_L5) btn |= code_to_xb(t->back[2]);
  178. if (b & TB_R5) btn |= code_to_xb(t->back[3]);
  179. if (t->qam && (b & TB_QAM)) btn |= code_to_xb(t->qam);
  180. uint8_t lt = g_in[slot].lt, rt = g_in[slot].rt;
  181. if (b & TB_L2) lt = 0xFF;
  182. if (b & TB_R2) rt = 0xFF;
  183. // Trigger remaps: a back paddle / QAM mapped to code 19 (LT) / 20 (RT) pulls
  184. // that analog trigger to full.
  185. const uint8_t bc[4] = { t->back[0], t->back[1], t->back[2], t->back[3] };
  186. const uint32_t bm[4] = { TB_L4, TB_R4, TB_L5, TB_R5 };
  187. for (int i = 0; i < 4; i++) {
  188. if (!(b & bm[i])) continue;
  189. if (bc[i] == 19) lt = 0xFF;
  190. else if (bc[i] == 20) rt = 0xFF;
  191. }
  192. if (t->qam && (b & TB_QAM)) {
  193. if (t->qam == 19) lt = 0xFF;
  194. else if (t->qam == 20) rt = 0xFF;
  195. }
  196. int16_t lx = g_in[slot].lx, ly = g_in[slot].ly;
  197. int16_t rx = g_in[slot].rx, ry = g_in[slot].ry;
  198. r[0] = 0x00;
  199. r[1] = 0x14;
  200. r[2] = (uint8_t)(btn & 0xFF);
  201. r[3] = (uint8_t)(btn >> 8);
  202. r[4] = lt;
  203. r[5] = rt;
  204. r[6] = lx & 0xFF; r[7] = (lx >> 8) & 0xFF;
  205. r[8] = ly & 0xFF; r[9] = (ly >> 8) & 0xFF;
  206. r[10] = rx & 0xFF; r[11] = (rx >> 8) & 0xFF;
  207. r[12] = ry & 0xFF; r[13] = (ry >> 8) & 0xFF;
  208. memset(r + 14, 0, 6);
  209. }
  210. void xinput_task(void)
  211. {
  212. if (!g_xi.in_use || g_xi.ep_in == 0 || !tud_mounted())
  213. return;
  214. static uint32_t last_ms;
  215. uint32_t now = to_ms_since_boot(get_absolute_time());
  216. if (now - last_ms < 4) // ~250 Hz cap
  217. return;
  218. if (usbd_edpt_busy(0, g_xi.ep_in))
  219. return; // a transfer is still in flight — newest state next cycle
  220. last_ms = now;
  221. xi_build(active_slot(), g_xi.in_buf);
  222. if (usbd_edpt_claim(0, g_xi.ep_in)) {
  223. if (!usbd_edpt_xfer(0, g_xi.ep_in, g_xi.in_buf, 20))
  224. usbd_edpt_release(0, g_xi.ep_in);
  225. }
  226. }