hid_app.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * The MIT License (MIT)
  3. *
  4. * Copyright (c) 2021, Ha Thach (tinyusb.org)
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. *
  24. */
  25. #include "bsp/board.h"
  26. #include "tusb.h"
  27. /* From https://www.kernel.org/doc/html/latest/input/gamepad.html
  28. ____________________________ __
  29. / [__ZL__] [__ZR__] \ |
  30. / [__ TL __] [__ TR __] \ | Front Triggers
  31. __/________________________________\__ __|
  32. / _ \ |
  33. / /\ __ (N) \ |
  34. / || __ |MO| __ _ _ \ | Main Pad
  35. | <===DP===> |SE| |ST| (W) -|- (E) | |
  36. \ || ___ ___ _ / |
  37. /\ \/ / \ / \ (S) /\ __|
  38. / \________ | LS | ____ | RS | ________/ \ |
  39. | / \ \___/ / \ \___/ / \ | | Control Sticks
  40. | / \_____/ \_____/ \ | __|
  41. | / \ |
  42. \_____/ \_____/
  43. |________|______| |______|___________|
  44. D-Pad Left Right Action Pad
  45. Stick Stick
  46. |_____________|
  47. Menu Pad
  48. Most gamepads have the following features:
  49. - Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST.
  50. - D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right.
  51. - Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START.
  52. - Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also
  53. provide a digital button if you press them.
  54. - Triggers are located on the upper-side of the pad in vertical direction. The upper buttons
  55. are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right.
  56. - Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors.
  57. */
  58. // Sony DS4 report layout detail https://www.psdevwiki.com/ps4/DS4-USB
  59. typedef struct TU_ATTR_PACKED
  60. {
  61. uint8_t x, y, z, rz; // joystick
  62. struct {
  63. uint8_t dpad : 4; // (hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW)
  64. uint8_t square : 1; // west
  65. uint8_t cross : 1; // south
  66. uint8_t circle : 1; // east
  67. uint8_t triangle : 1; // north
  68. };
  69. struct {
  70. uint8_t l1 : 1;
  71. uint8_t r1 : 1;
  72. uint8_t l2 : 1;
  73. uint8_t r2 : 1;
  74. uint8_t share : 1;
  75. uint8_t option : 1;
  76. uint8_t l3 : 1;
  77. uint8_t r3 : 1;
  78. };
  79. struct {
  80. uint8_t ps : 1; // playstation button
  81. uint8_t tpad : 1; // track pad click
  82. uint8_t counter : 6; // +1 each report
  83. };
  84. // comment out since not used by this example
  85. // uint8_t l2_trigger; // 0 released, 0xff fully pressed
  86. // uint8_t r2_trigger; // as above
  87. // uint16_t timestamp;
  88. // uint8_t battery;
  89. //
  90. // int16_t gyro[3]; // x, y, z;
  91. // int16_t accel[3]; // x, y, z
  92. // there is still lots more info
  93. } sony_ds4_report_t;
  94. // check if device is Sony DualShock 4
  95. static inline bool is_sony_ds4(uint8_t dev_addr)
  96. {
  97. uint16_t vid, pid;
  98. tuh_vid_pid_get(dev_addr, &vid, &pid);
  99. return ( (vid == 0x054c && (pid == 0x09cc || pid == 0x05c4)) // Sony DualShock4
  100. || (vid == 0x0f0d && pid == 0x005e) // Hori FC4
  101. || (vid == 0x0f0d && pid == 0x00ee) // Hori PS4 Mini (PS4-099U)
  102. || (vid == 0x1f4f && pid == 0x1002) // ASW GG xrd controller
  103. );
  104. }
  105. //--------------------------------------------------------------------+
  106. // MACRO TYPEDEF CONSTANT ENUM DECLARATION
  107. //--------------------------------------------------------------------+
  108. void hid_app_task(void)
  109. {
  110. // nothing to do
  111. }
  112. //--------------------------------------------------------------------+
  113. // TinyUSB Callbacks
  114. //--------------------------------------------------------------------+
  115. // Invoked when device with hid interface is mounted
  116. // Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
  117. // can be used to parse common/simple enough descriptor.
  118. // Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
  119. // therefore report_desc = NULL, desc_len = 0
  120. void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
  121. {
  122. uint16_t vid, pid;
  123. tuh_vid_pid_get(dev_addr, &vid, &pid);
  124. printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
  125. printf("VID = %04x, PID = %04x\r\n", vid, pid);
  126. // Sony DualShock 4 [CUH-ZCT2x]
  127. if ( is_sony_ds4(dev_addr) )
  128. {
  129. // request to receive report
  130. // tuh_hid_report_received_cb() will be invoked when report is available
  131. if ( !tuh_hid_receive_report(dev_addr, instance) )
  132. {
  133. printf("Error: cannot request to receive report\r\n");
  134. }
  135. }
  136. }
  137. // Invoked when device with hid interface is un-mounted
  138. void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
  139. {
  140. printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
  141. }
  142. // check if different than 2
  143. bool diff_than_2(uint8_t x, uint8_t y)
  144. {
  145. return (x - y > 2) || (y - x > 2);
  146. }
  147. // check if 2 reports are different enough
  148. bool diff_report(sony_ds4_report_t const* rpt1, sony_ds4_report_t const* rpt2)
  149. {
  150. bool result;
  151. // x, y, z, rz must different than 2 to be counted
  152. result = diff_than_2(rpt1->x, rpt2->x) || diff_than_2(rpt1->y , rpt2->y ) ||
  153. diff_than_2(rpt1->z, rpt2->z) || diff_than_2(rpt1->rz, rpt2->rz);
  154. // check the reset with mem compare
  155. result |= memcmp(&rpt1->rz + 1, &rpt2->rz + 1, sizeof(sony_ds4_report_t)-4);
  156. return result;
  157. }
  158. void process_sony_ds4(uint8_t const* report, uint16_t len)
  159. {
  160. const char* dpad_str[] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "none" };
  161. // previous report used to compare for changes
  162. static sony_ds4_report_t prev_report = { 0 };
  163. uint8_t const report_id = report[0];
  164. report++;
  165. len--;
  166. // all buttons state is stored in ID 1
  167. if (report_id == 1)
  168. {
  169. sony_ds4_report_t ds4_report;
  170. memcpy(&ds4_report, report, sizeof(ds4_report));
  171. // counter is +1, assign to make it easier to compare 2 report
  172. prev_report.counter = ds4_report.counter;
  173. // only print if changes since it is polled ~ 5ms
  174. // Since count+1 after each report and x, y, z, rz fluctuate within 1 or 2
  175. // We need more than memcmp to check if report is different enough
  176. if ( diff_report(&prev_report, &ds4_report) )
  177. {
  178. printf("(x, y, z, rz) = (%u, %u, %u, %u)\r\n", ds4_report.x, ds4_report.y, ds4_report.z, ds4_report.rz);
  179. printf("DPad = %s ", dpad_str[ds4_report.dpad]);
  180. if (ds4_report.square ) printf("Square ");
  181. if (ds4_report.cross ) printf("Cross ");
  182. if (ds4_report.circle ) printf("Circle ");
  183. if (ds4_report.triangle ) printf("Triangle ");
  184. if (ds4_report.l1 ) printf("L1 ");
  185. if (ds4_report.r1 ) printf("R1 ");
  186. if (ds4_report.l2 ) printf("L2 ");
  187. if (ds4_report.r2 ) printf("R2 ");
  188. if (ds4_report.share ) printf("Share ");
  189. if (ds4_report.option ) printf("Option ");
  190. if (ds4_report.l3 ) printf("L3 ");
  191. if (ds4_report.r3 ) printf("R3 ");
  192. if (ds4_report.ps ) printf("PS ");
  193. if (ds4_report.tpad ) printf("TPad ");
  194. printf("\r\n");
  195. }
  196. prev_report = ds4_report;
  197. }
  198. }
  199. // Invoked when received report from device via interrupt endpoint
  200. void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
  201. {
  202. if ( is_sony_ds4(dev_addr) )
  203. {
  204. process_sony_ds4(report, len);
  205. }
  206. // continue to request to receive report
  207. if ( !tuh_hid_receive_report(dev_addr, instance) )
  208. {
  209. printf("Error: cannot request to receive report\r\n");
  210. }
  211. }