SDL_hidapi_switch2.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. /* This driver supports the Nintendo Switch Pro controller.
  19. Code and logic contributed by Valve Corporation under the SDL zlib license.
  20. */
  21. #include "SDL_internal.h"
  22. #ifdef SDL_JOYSTICK_HIDAPI
  23. #include "../../SDL_hints_c.h"
  24. #include "../../misc/SDL_libusb.h"
  25. #include "../SDL_sysjoystick.h"
  26. #include "SDL_hidapijoystick_c.h"
  27. #ifdef SDL_JOYSTICK_HIDAPI_SWITCH2
  28. typedef struct
  29. {
  30. Uint16 neutral;
  31. Uint16 max;
  32. Uint16 min;
  33. } Switch2_AxisCalibration;
  34. typedef struct
  35. {
  36. Switch2_AxisCalibration x;
  37. Switch2_AxisCalibration y;
  38. } Switch2_StickCalibration;
  39. typedef struct
  40. {
  41. SDL_LibUSBContext *libusb;
  42. libusb_device_handle *device_handle;
  43. bool interface_claimed;
  44. Uint8 interface_number;
  45. Uint8 out_endpoint;
  46. Uint8 in_endpoint;
  47. Switch2_StickCalibration left_stick;
  48. Switch2_StickCalibration right_stick;
  49. Uint8 left_trigger_max;
  50. Uint8 right_trigger_max;
  51. } SDL_DriverSwitch2_Context;
  52. static void ParseStickCalibration(Switch2_StickCalibration *stick_data, const Uint8 *data)
  53. {
  54. stick_data->x.neutral = data[0];
  55. stick_data->x.neutral |= (data[1] & 0x0F) << 8;
  56. stick_data->y.neutral = data[1] >> 4;
  57. stick_data->y.neutral |= data[2] << 4;
  58. stick_data->x.max = data[3];
  59. stick_data->x.max |= (data[4] & 0x0F) << 8;
  60. stick_data->y.max = data[4] >> 4;
  61. stick_data->y.max |= data[5] << 4;
  62. stick_data->x.min = data[6];
  63. stick_data->x.min |= (data[7] & 0x0F) << 8;
  64. stick_data->y.min = data[7] >> 4;
  65. stick_data->y.min |= data[8] << 4;
  66. }
  67. static int SendBulkData(SDL_DriverSwitch2_Context *ctx, const Uint8 *data, unsigned size)
  68. {
  69. int transferred;
  70. int res = ctx->libusb->bulk_transfer(ctx->device_handle,
  71. ctx->out_endpoint,
  72. (Uint8 *)data,
  73. size,
  74. &transferred,
  75. 1000);
  76. if (res < 0) {
  77. return res;
  78. }
  79. return transferred;
  80. }
  81. static int RecvBulkData(SDL_DriverSwitch2_Context *ctx, Uint8 *data, unsigned size)
  82. {
  83. int transferred;
  84. int total_transferred = 0;
  85. int res;
  86. while (size > 0) {
  87. unsigned current_read = size;
  88. if (current_read > 64) {
  89. current_read = 64;
  90. }
  91. res = ctx->libusb->bulk_transfer(ctx->device_handle,
  92. ctx->in_endpoint,
  93. data,
  94. current_read,
  95. &transferred,
  96. 100);
  97. if (res < 0) {
  98. return res;
  99. }
  100. total_transferred += transferred;
  101. size -= transferred;
  102. data += current_read;
  103. if ((unsigned) transferred < current_read) {
  104. break;
  105. }
  106. }
  107. return total_transferred;
  108. }
  109. static void MapJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, int axis, const Switch2_AxisCalibration *calib, float value)
  110. {
  111. Sint16 mapped_value;
  112. if (calib && calib->neutral && calib->min && calib->max) {
  113. value -= calib->neutral;
  114. if (value < 0) {
  115. value /= calib->min;
  116. } else {
  117. value /= calib->max;
  118. }
  119. mapped_value = (Sint16) SDL_clamp(value * SDL_MAX_SINT16, SDL_MIN_SINT16, SDL_MAX_SINT16);
  120. } else {
  121. mapped_value = (Sint16) HIDAPI_RemapVal(value, 0, 4096, SDL_MIN_SINT16, SDL_MAX_SINT16);
  122. }
  123. SDL_SendJoystickAxis(timestamp, joystick, axis, mapped_value);
  124. }
  125. static void MapTriggerAxis(Uint64 timestamp, SDL_Joystick *joystick, int axis, Uint8 max, float value)
  126. {
  127. Sint16 mapped_value = (Sint16) HIDAPI_RemapVal(
  128. SDL_clamp((value - max) / (232.f - max), 0, 1),
  129. 0, 1,
  130. SDL_MIN_SINT16, SDL_MAX_SINT16
  131. );
  132. SDL_SendJoystickAxis(timestamp, joystick, axis, mapped_value);
  133. }
  134. static void HIDAPI_DriverSwitch2_RegisterHints(SDL_HintCallback callback, void *userdata)
  135. {
  136. SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, callback, userdata);
  137. }
  138. static void HIDAPI_DriverSwitch2_UnregisterHints(SDL_HintCallback callback, void *userdata)
  139. {
  140. SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, callback, userdata);
  141. }
  142. static bool HIDAPI_DriverSwitch2_IsEnabled(void)
  143. {
  144. return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
  145. }
  146. static bool HIDAPI_DriverSwitch2_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
  147. {
  148. if (vendor_id == USB_VENDOR_NINTENDO) {
  149. switch (product_id) {
  150. case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER:
  151. case USB_PRODUCT_NINTENDO_SWITCH2_PRO:
  152. return true;
  153. }
  154. }
  155. return false;
  156. }
  157. static bool HIDAPI_DriverSwitch2_InitBluetooth(SDL_HIDAPI_Device *device)
  158. {
  159. // FIXME: Need to add Bluetooth support
  160. return SDL_SetError("Nintendo Switch2 controllers not supported over Bluetooth");
  161. }
  162. static bool FindBulkEndpoints(SDL_LibUSBContext *libusb, libusb_device_handle *handle, Uint8 *bInterfaceNumber, Uint8 *out_endpoint, Uint8 *in_endpoint)
  163. {
  164. struct libusb_config_descriptor *config;
  165. int found = 0;
  166. if (libusb->get_config_descriptor(libusb->get_device(handle), 0, &config) != 0) {
  167. return false;
  168. }
  169. for (int i = 0; i < config->bNumInterfaces; i++) {
  170. const struct libusb_interface *iface = &config->interface[i];
  171. for (int j = 0; j < iface->num_altsetting; j++) {
  172. const struct libusb_interface_descriptor *altsetting = &iface->altsetting[j];
  173. if (altsetting->bInterfaceNumber == 1) {
  174. for (int k = 0; k < altsetting->bNumEndpoints; k++) {
  175. const struct libusb_endpoint_descriptor *ep = &altsetting->endpoint[k];
  176. if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
  177. *bInterfaceNumber = altsetting->bInterfaceNumber;
  178. if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
  179. *out_endpoint = ep->bEndpointAddress;
  180. found |= 1;
  181. }
  182. if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
  183. *in_endpoint = ep->bEndpointAddress;
  184. found |= 2;
  185. }
  186. if (found == 3) {
  187. libusb->free_config_descriptor(config);
  188. return true;
  189. }
  190. }
  191. }
  192. }
  193. }
  194. }
  195. libusb->free_config_descriptor(config);
  196. return false;
  197. }
  198. static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device)
  199. {
  200. SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
  201. if (!SDL_InitLibUSB(&ctx->libusb)) {
  202. return false;
  203. }
  204. ctx->device_handle = (libusb_device_handle *)SDL_GetPointerProperty(SDL_hid_get_properties(device->dev), SDL_PROP_HIDAPI_LIBUSB_DEVICE_HANDLE_POINTER, NULL);
  205. if (!ctx->device_handle) {
  206. return SDL_SetError("Couldn't get libusb device handle");
  207. }
  208. if (!FindBulkEndpoints(ctx->libusb, ctx->device_handle, &ctx->interface_number, &ctx->out_endpoint, &ctx->in_endpoint)) {
  209. return SDL_SetError("Couldn't find bulk endpoints");
  210. }
  211. int res = ctx->libusb->claim_interface(ctx->device_handle, ctx->interface_number);
  212. if (res < 0) {
  213. return SDL_SetError("Couldn't claim interface %d: %d\n", ctx->interface_number, res);
  214. }
  215. ctx->interface_claimed = true;
  216. const unsigned char INIT_DATA[] = {
  217. 0x03, 0x91, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00,
  218. 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  219. };
  220. const unsigned char SET_LED_DATA[] = {
  221. 0x09, 0x91, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00,
  222. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  223. };
  224. unsigned char flash_read_command[] = {
  225. 0x02, 0x91, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00,
  226. 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00
  227. };
  228. unsigned char calibration_data[0x50] = {0};
  229. res = SendBulkData(ctx, INIT_DATA, sizeof(INIT_DATA));
  230. if (res < 0) {
  231. return SDL_SetError("Couldn't send initialization data: %d\n", res);
  232. }
  233. RecvBulkData(ctx, calibration_data, 0x40);
  234. res = SendBulkData(ctx, SET_LED_DATA, sizeof(SET_LED_DATA));
  235. if (res < 0) {
  236. return SDL_SetError("Couldn't set LED data: %d\n", res);
  237. }
  238. RecvBulkData(ctx, calibration_data, 0x40);
  239. flash_read_command[12] = 0x80;
  240. res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
  241. if (res < 0) {
  242. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
  243. } else {
  244. res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data));
  245. if (res < 0) {
  246. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
  247. } else {
  248. ParseStickCalibration(&ctx->left_stick, &calibration_data[0x38]);
  249. }
  250. }
  251. flash_read_command[12] = 0xC0;
  252. res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
  253. if (res < 0) {
  254. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
  255. } else {
  256. res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data));
  257. if (res < 0) {
  258. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
  259. } else {
  260. ParseStickCalibration(&ctx->right_stick, &calibration_data[0x38]);
  261. }
  262. }
  263. if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER) {
  264. flash_read_command[12] = 0x40;
  265. flash_read_command[13] = 0x31;
  266. res = SendBulkData(ctx, flash_read_command, sizeof(flash_read_command));
  267. if (res < 0) {
  268. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
  269. } else {
  270. res = RecvBulkData(ctx, calibration_data, sizeof(calibration_data));
  271. if (res < 0) {
  272. SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't read calibration data: %d", res);
  273. } else {
  274. ctx->left_trigger_max = calibration_data[0x10];
  275. ctx->right_trigger_max = calibration_data[0x11];
  276. }
  277. }
  278. }
  279. return true;
  280. }
  281. static bool HIDAPI_DriverSwitch2_InitDevice(SDL_HIDAPI_Device *device)
  282. {
  283. SDL_DriverSwitch2_Context *ctx;
  284. ctx = (SDL_DriverSwitch2_Context *)SDL_calloc(1, sizeof(*ctx));
  285. if (!ctx) {
  286. return false;
  287. }
  288. device->context = ctx;
  289. if (device->is_bluetooth) {
  290. if (!HIDAPI_DriverSwitch2_InitBluetooth(device)) {
  291. return false;
  292. }
  293. } else {
  294. if (!HIDAPI_DriverSwitch2_InitUSB(device)) {
  295. return false;
  296. }
  297. }
  298. // Sometimes the device handle isn't available during enumeration so we don't get the device name, so set it explicitly
  299. switch (device->product_id) {
  300. case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER:
  301. HIDAPI_SetDeviceName(device, "Nintendo GameCube Controller");
  302. break;
  303. case USB_PRODUCT_NINTENDO_SWITCH2_PRO:
  304. HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
  305. break;
  306. default:
  307. break;
  308. }
  309. return HIDAPI_JoystickConnected(device, NULL);
  310. }
  311. static int HIDAPI_DriverSwitch2_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
  312. {
  313. return -1;
  314. }
  315. static void HIDAPI_DriverSwitch2_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
  316. {
  317. }
  318. static bool HIDAPI_DriverSwitch2_UpdateDevice(SDL_HIDAPI_Device *device)
  319. {
  320. SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
  321. const struct {
  322. int byte;
  323. unsigned char mask;
  324. } buttons[] = {
  325. {3, 0x01}, // B
  326. {3, 0x02}, // A
  327. {3, 0x04}, // Y
  328. {3, 0x08}, // X
  329. {3, 0x10}, // R (GameCube R Click)
  330. {3, 0x20}, // ZR (GameCube Z)
  331. {3, 0x40}, // PLUS (GameCube Start)
  332. {3, 0x80}, // RS (not on GameCube)
  333. {4, 0x01}, // DPAD_DOWN
  334. {4, 0x02}, // DPAD_RIGHT
  335. {4, 0x04}, // DPAD_LEFT
  336. {4, 0x08}, // DPAD_UP
  337. {4, 0x10}, // L (GameCube L Click)
  338. {4, 0x20}, // ZL
  339. {4, 0x40}, // MINUS (not on GameCube)
  340. {4, 0x80}, // LS (not on GameCube)
  341. {5, 0x01}, // Home
  342. {5, 0x02}, // Capture
  343. {5, 0x04}, // GR (not on GameCube)
  344. {5, 0x08}, // GL (not on GameCube)
  345. {5, 0x10}, // C
  346. };
  347. SDL_Joystick *joystick = NULL;
  348. if (device->num_joysticks > 0) {
  349. joystick = SDL_GetJoystickFromID(device->joysticks[0]);
  350. }
  351. if (joystick == NULL) {
  352. return true;
  353. }
  354. // Read input packet
  355. Uint8 packet[USB_PACKET_LENGTH];
  356. int size;
  357. while ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
  358. if (size < 15) {
  359. continue;
  360. }
  361. Uint64 timestamp = SDL_GetTicksNS();
  362. for (size_t i = 0; i < SDL_arraysize(buttons); ++i) {
  363. SDL_SendJoystickButton(
  364. timestamp,
  365. joystick,
  366. (Uint8) i,
  367. (packet[buttons[i].byte] & buttons[i].mask) != 0
  368. );
  369. }
  370. MapJoystickAxis(
  371. timestamp,
  372. joystick,
  373. SDL_GAMEPAD_AXIS_LEFTX,
  374. &ctx->left_stick.x,
  375. (float) (packet[6] | ((packet[7] & 0x0F) << 8))
  376. );
  377. MapJoystickAxis(
  378. timestamp,
  379. joystick,
  380. SDL_GAMEPAD_AXIS_LEFTY,
  381. &ctx->left_stick.y,
  382. (float) ((packet[7] >> 4) | (packet[8] << 4))
  383. );
  384. MapJoystickAxis(
  385. timestamp,
  386. joystick,
  387. SDL_GAMEPAD_AXIS_RIGHTX,
  388. &ctx->right_stick.x,
  389. (float) (packet[9] | ((packet[10] & 0x0F) << 8))
  390. );
  391. MapJoystickAxis(
  392. timestamp,
  393. joystick,
  394. SDL_GAMEPAD_AXIS_RIGHTY,
  395. &ctx->right_stick.y,
  396. (float) ((packet[10] >> 4) | (packet[11] << 4))
  397. );
  398. if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER) {
  399. MapTriggerAxis(
  400. timestamp,
  401. joystick,
  402. SDL_GAMEPAD_AXIS_LEFT_TRIGGER,
  403. ctx->left_trigger_max,
  404. packet[13]
  405. );
  406. MapTriggerAxis(
  407. timestamp,
  408. joystick,
  409. SDL_GAMEPAD_AXIS_RIGHT_TRIGGER,
  410. ctx->right_trigger_max,
  411. packet[14]
  412. );
  413. }
  414. }
  415. return true;
  416. }
  417. static bool HIDAPI_DriverSwitch2_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  418. {
  419. // Initialize the joystick capabilities
  420. joystick->nbuttons = 21;
  421. if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER) {
  422. joystick->naxes = 6;
  423. } else {
  424. joystick->naxes = 4;
  425. }
  426. return true;
  427. }
  428. static bool HIDAPI_DriverSwitch2_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  429. {
  430. return SDL_Unsupported();
  431. }
  432. static bool HIDAPI_DriverSwitch2_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
  433. {
  434. return SDL_Unsupported();
  435. }
  436. static Uint32 HIDAPI_DriverSwitch2_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  437. {
  438. return 0;
  439. }
  440. static bool HIDAPI_DriverSwitch2_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
  441. {
  442. return SDL_Unsupported();
  443. }
  444. static bool HIDAPI_DriverSwitch2_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
  445. {
  446. return SDL_Unsupported();
  447. }
  448. static bool HIDAPI_DriverSwitch2_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
  449. {
  450. return SDL_Unsupported();
  451. }
  452. static void HIDAPI_DriverSwitch2_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  453. {
  454. }
  455. static void HIDAPI_DriverSwitch2_FreeDevice(SDL_HIDAPI_Device *device)
  456. {
  457. SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context;
  458. if (ctx) {
  459. if (ctx->interface_claimed) {
  460. ctx->libusb->release_interface(ctx->device_handle, ctx->interface_number);
  461. ctx->interface_claimed = false;
  462. }
  463. if (ctx->libusb) {
  464. SDL_QuitLibUSB();
  465. ctx->libusb = NULL;
  466. }
  467. }
  468. }
  469. SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch2 = {
  470. SDL_HINT_JOYSTICK_HIDAPI_SWITCH2,
  471. true,
  472. HIDAPI_DriverSwitch2_RegisterHints,
  473. HIDAPI_DriverSwitch2_UnregisterHints,
  474. HIDAPI_DriverSwitch2_IsEnabled,
  475. HIDAPI_DriverSwitch2_IsSupportedDevice,
  476. HIDAPI_DriverSwitch2_InitDevice,
  477. HIDAPI_DriverSwitch2_GetDevicePlayerIndex,
  478. HIDAPI_DriverSwitch2_SetDevicePlayerIndex,
  479. HIDAPI_DriverSwitch2_UpdateDevice,
  480. HIDAPI_DriverSwitch2_OpenJoystick,
  481. HIDAPI_DriverSwitch2_RumbleJoystick,
  482. HIDAPI_DriverSwitch2_RumbleJoystickTriggers,
  483. HIDAPI_DriverSwitch2_GetJoystickCapabilities,
  484. HIDAPI_DriverSwitch2_SetJoystickLED,
  485. HIDAPI_DriverSwitch2_SendJoystickEffect,
  486. HIDAPI_DriverSwitch2_SetJoystickSensorsEnabled,
  487. HIDAPI_DriverSwitch2_CloseJoystick,
  488. HIDAPI_DriverSwitch2_FreeDevice,
  489. };
  490. #endif // SDL_JOYSTICK_HIDAPI_SWITCH2
  491. #endif // SDL_JOYSTICK_HIDAPI