mode_ps5.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // mode_ps5.c — Sony DualSense emulated controller (ported from OpenPuck
  2. // mode_ps5.cpp). Report id 0x01 (63B) + gyro/accel + split trackpad; feature
  3. // reports 0x03/0x05/0x09/0x20; rumble on output report 0x02.
  4. //
  5. // SPDX-License-Identifier: AGPL-3.0-or-later
  6. #include "puck/emu.h"
  7. #include "gamepad_util.h"
  8. #include "puck/relay.h"
  9. #include "sys/settings.h"
  10. #include <string.h>
  11. #include "hid_reports.h"
  12. #define PS5_TOUCH_H 1080
  13. #define PS5_STATUS_USB 0x1A
  14. #define ET_DS5 3 // per-type config index (matches OpenPuck ET_DS5)
  15. static uint16_t ps5_build(int slot, uint8_t *out, uint8_t *rid)
  16. {
  17. *rid = 0x01;
  18. uint32_t b = g_in[slot].buttons;
  19. bool lt_touch = (b & TB_LPADT) || (b & TB_LPADC);
  20. bool rt_touch = (b & TB_RPADT) || (b & TB_RPADC);
  21. memset(out, 0, 63);
  22. out[0] = sw_stick(g_in[slot].lx, false);
  23. out[1] = sw_stick(g_in[slot].ly, true);
  24. out[2] = sw_stick(g_in[slot].rx, false);
  25. out[3] = sw_stick(g_in[slot].ry, true);
  26. out[4] = g_in[slot].lt;
  27. out[5] = g_in[slot].rt;
  28. static uint8_t seq;
  29. out[6] = seq++;
  30. out[7] = ps_hat_nibble(b) |
  31. ps_face_nibble(b, settings()->type[ET_DS5].ab_swap);
  32. out[8] = ps_shoulders_byte(b, g_in[slot].lt, g_in[slot].rt);
  33. out[9] = ((b & TB_STEAM) ? 0x01 : 0) |
  34. ((b & (TB_TOUCH | TB_LPADC | TB_RPADC)) ? 0x02 : 0) |
  35. ((b & TB_MUTE) ? 0x04 : 0);
  36. out[15] = g_in[slot].gx & 0xFF;
  37. out[16] = g_in[slot].gx >> 8;
  38. out[17] = g_in[slot].gz & 0xFF;
  39. out[18] = g_in[slot].gz >> 8;
  40. out[19] = (-g_in[slot].gy) & 0xFF;
  41. out[20] = (-g_in[slot].gy) >> 8;
  42. out[21] = g_in[slot].ax & 0xFF;
  43. out[22] = g_in[slot].ax >> 8;
  44. out[23] = g_in[slot].ay & 0xFF;
  45. out[24] = g_in[slot].ay >> 8;
  46. out[25] = g_in[slot].az & 0xFF;
  47. out[26] = g_in[slot].az >> 8;
  48. uint16_t lx, ly, rx, ry;
  49. steam_pads_to_touch(b, PS5_TOUCH_H, g_in[slot].lpx, g_in[slot].lpy,
  50. g_in[slot].rpx, g_in[slot].rpy, &lx, &ly, &rx, &ry);
  51. touch_pack_pads(out + 32, lt_touch, rt_touch, lx, ly, rx, ry);
  52. out[52] = PS5_STATUS_USB;
  53. return 63;
  54. }
  55. static uint16_t ps5_get(int slot, uint8_t rid, uint8_t type, uint8_t *buf,
  56. uint16_t reqlen)
  57. {
  58. if (type != PP_HID_FEATURE || reqlen == 0)
  59. return 0;
  60. memset(buf, 0, reqlen);
  61. uint8_t mac[6] = {
  62. 0x00, 0x1B, 0xDC, 0x4F, 0x55, (uint8_t)(0x60 + slot)
  63. };
  64. switch (rid) {
  65. case 0x03:
  66. if (reqlen < 47)
  67. return 0;
  68. buf[0] = 0x00;
  69. buf[1] = 0x28;
  70. buf[2] = 0x01;
  71. buf[3] = 0x00;
  72. buf[4] = 0x0E;
  73. return 47;
  74. case 0x05:
  75. if (reqlen < 40)
  76. return 0;
  77. ps_neutral_calib(buf);
  78. return 40;
  79. case 0x09:
  80. if (reqlen < 19)
  81. return 0;
  82. memcpy(buf, mac, 6);
  83. return 19;
  84. case 0x20:
  85. if (reqlen < 63)
  86. return 0;
  87. buf[23] = 0x01;
  88. buf[27] = 0x01;
  89. return 63;
  90. default:
  91. return 0;
  92. }
  93. }
  94. static void ps5_set(int slot, uint8_t rid, uint8_t type, const uint8_t *b,
  95. uint16_t n)
  96. {
  97. if (type != PP_HID_OUTPUT)
  98. return;
  99. // OUT endpoint delivers rid=0 with the id in b[0]; control SET_REPORT splits
  100. // the id into rid. Normalise to p = bytes AFTER the id.
  101. const uint8_t *p;
  102. uint16_t pn;
  103. if (rid == 0) {
  104. if (n < 1 || b[0] != 0x02)
  105. return;
  106. p = b + 1;
  107. pn = (uint16_t)(n - 1);
  108. } else if (rid == 0x02) {
  109. p = b;
  110. pn = n;
  111. } else {
  112. return;
  113. }
  114. if (pn < 4)
  115. return;
  116. // DualSense output 0x02 body: [2]=right(high) motor, [3]=left(low) motor.
  117. puck_rumble(slot, (uint16_t)p[3] * 257u, (uint16_t)p[2] * 257u);
  118. }
  119. const emu_mode_t emu_ps5 = {
  120. .vid = 0x054C,
  121. .pid = 0x0CE6,
  122. .bcd = 0x0110,
  123. .product = "DualSense Wireless Controller",
  124. .report_desc = PS5_HID_DESC,
  125. .report_desc_len = sizeof(PS5_HID_DESC),
  126. .build = ps5_build,
  127. .get_report = ps5_get,
  128. .set_report = ps5_set,
  129. .poll_ms = 4,
  130. };