SDL_hidapi_ps4.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2022 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 both simplified reports and the extended input reports enabled by Steam.
  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_events.h"
  24. #include "SDL_timer.h"
  25. #include "SDL_joystick.h"
  26. #include "SDL_gamecontroller.h"
  27. #include "../../SDL_hints_c.h"
  28. #include "../SDL_sysjoystick.h"
  29. #include "SDL_hidapijoystick_c.h"
  30. #include "SDL_hidapi_rumble.h"
  31. #ifdef SDL_JOYSTICK_HIDAPI_PS4
  32. /* Define this if you want to log all packets from the controller */
  33. /*#define DEBUG_PS4_PROTOCOL*/
  34. /* Define this if you want to log calibration data */
  35. /*#define DEBUG_PS4_CALIBRATION*/
  36. #define GYRO_RES_PER_DEGREE 1024.0f
  37. #define ACCEL_RES_PER_G 8192.0f
  38. #define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500
  39. #define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
  40. typedef enum
  41. {
  42. k_EPS4ReportIdUsbState = 1,
  43. k_EPS4ReportIdUsbEffects = 5,
  44. k_EPS4ReportIdBluetoothState1 = 17,
  45. k_EPS4ReportIdBluetoothState2 = 18,
  46. k_EPS4ReportIdBluetoothState3 = 19,
  47. k_EPS4ReportIdBluetoothState4 = 20,
  48. k_EPS4ReportIdBluetoothState5 = 21,
  49. k_EPS4ReportIdBluetoothState6 = 22,
  50. k_EPS4ReportIdBluetoothState7 = 23,
  51. k_EPS4ReportIdBluetoothState8 = 24,
  52. k_EPS4ReportIdBluetoothState9 = 25,
  53. k_EPS4ReportIdBluetoothEffects = 17,
  54. k_EPS4ReportIdDisconnectMessage = 226,
  55. } EPS4ReportId;
  56. typedef enum
  57. {
  58. k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
  59. k_ePS4FeatureReportIdCapabilities = 0x03,
  60. k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
  61. k_ePS4FeatureReportIdSerialNumber = 0x12,
  62. } EPS4FeatureReportID;
  63. typedef struct
  64. {
  65. Uint8 ucLeftJoystickX;
  66. Uint8 ucLeftJoystickY;
  67. Uint8 ucRightJoystickX;
  68. Uint8 ucRightJoystickY;
  69. Uint8 rgucButtonsHatAndCounter[ 3 ];
  70. Uint8 ucTriggerLeft;
  71. Uint8 ucTriggerRight;
  72. Uint8 _rgucPad0[ 3 ];
  73. Uint8 rgucGyroX[2];
  74. Uint8 rgucGyroY[2];
  75. Uint8 rgucGyroZ[2];
  76. Uint8 rgucAccelX[2];
  77. Uint8 rgucAccelY[2];
  78. Uint8 rgucAccelZ[2];
  79. Uint8 _rgucPad1[ 5 ];
  80. Uint8 ucBatteryLevel;
  81. Uint8 _rgucPad2[ 4 ];
  82. Uint8 ucTouchpadCounter1;
  83. Uint8 rgucTouchpadData1[ 3 ];
  84. Uint8 ucTouchpadCounter2;
  85. Uint8 rgucTouchpadData2[ 3 ];
  86. } PS4StatePacket_t;
  87. typedef struct
  88. {
  89. Uint8 ucRumbleRight;
  90. Uint8 ucRumbleLeft;
  91. Uint8 ucLedRed;
  92. Uint8 ucLedGreen;
  93. Uint8 ucLedBlue;
  94. Uint8 ucLedDelayOn;
  95. Uint8 ucLedDelayOff;
  96. Uint8 _rgucPad0[ 8 ];
  97. Uint8 ucVolumeLeft;
  98. Uint8 ucVolumeRight;
  99. Uint8 ucVolumeMic;
  100. Uint8 ucVolumeSpeaker;
  101. } DS4EffectsState_t;
  102. typedef struct {
  103. Sint16 bias;
  104. float sensitivity;
  105. } IMUCalibrationData;
  106. typedef struct {
  107. SDL_HIDAPI_Device *device;
  108. SDL_Joystick *joystick;
  109. SDL_bool is_dongle;
  110. SDL_bool is_bluetooth;
  111. SDL_bool official_controller;
  112. SDL_bool effects_supported;
  113. SDL_bool sensors_supported;
  114. SDL_bool touchpad_supported;
  115. SDL_bool enhanced_mode;
  116. SDL_bool report_sensors;
  117. SDL_bool report_touchpad;
  118. SDL_bool hardware_calibration;
  119. IMUCalibrationData calibration[6];
  120. Uint32 last_packet;
  121. int player_index;
  122. Uint8 rumble_left;
  123. Uint8 rumble_right;
  124. SDL_bool color_set;
  125. Uint8 led_red;
  126. Uint8 led_green;
  127. Uint8 led_blue;
  128. PS4StatePacket_t last_state;
  129. } SDL_DriverPS4_Context;
  130. static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size);
  131. static void
  132. HIDAPI_DriverPS4_RegisterHints(SDL_HintCallback callback, void *userdata)
  133. {
  134. SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4, callback, userdata);
  135. }
  136. static void
  137. HIDAPI_DriverPS4_UnregisterHints(SDL_HintCallback callback, void *userdata)
  138. {
  139. SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4, callback, userdata);
  140. }
  141. static SDL_bool
  142. HIDAPI_DriverPS4_IsEnabled(void)
  143. {
  144. return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4,
  145. SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI,
  146. SDL_HIDAPI_DEFAULT));
  147. }
  148. static SDL_bool
  149. HIDAPI_DriverPS4_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
  150. {
  151. return (type == SDL_CONTROLLER_TYPE_PS4) ? SDL_TRUE : SDL_FALSE;
  152. }
  153. static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
  154. {
  155. SDL_memset(report, 0, length);
  156. report[0] = report_id;
  157. return SDL_hid_get_feature_report(dev, report, length);
  158. }
  159. static void
  160. SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index)
  161. {
  162. /* This list is the same as what hid-sony.c uses in the Linux kernel.
  163. The first 4 values correspond to what the PS4 assigns.
  164. */
  165. static const Uint8 colors[7][3] = {
  166. { 0x00, 0x00, 0x40 }, /* Blue */
  167. { 0x40, 0x00, 0x00 }, /* Red */
  168. { 0x00, 0x40, 0x00 }, /* Green */
  169. { 0x20, 0x00, 0x20 }, /* Pink */
  170. { 0x02, 0x01, 0x00 }, /* Orange */
  171. { 0x00, 0x01, 0x01 }, /* Teal */
  172. { 0x01, 0x01, 0x01 } /* White */
  173. };
  174. if (player_index >= 0) {
  175. player_index %= SDL_arraysize(colors);
  176. } else {
  177. player_index = 0;
  178. }
  179. effects->ucLedRed = colors[player_index][0];
  180. effects->ucLedGreen = colors[player_index][1];
  181. effects->ucLedBlue = colors[player_index][2];
  182. }
  183. static SDL_bool
  184. HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
  185. {
  186. SDL_DriverPS4_Context *ctx;
  187. Uint8 data[USB_PACKET_LENGTH];
  188. int size;
  189. char serial[18];
  190. if (device->vendor_id == USB_VENDOR_SONY) {
  191. HIDAPI_SetDeviceName(device, "PS4 Controller");
  192. }
  193. ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
  194. if (!ctx) {
  195. SDL_OutOfMemory();
  196. return SDL_FALSE;
  197. }
  198. ctx->device = device;
  199. device->context = ctx;
  200. if (device->serial && SDL_strlen(device->serial) == 12) {
  201. int i, j;
  202. j = -1;
  203. for (i = 0; i < 12; i += 2) {
  204. j += 1;
  205. SDL_memcpy(&serial[j], &device->serial[i], 2);
  206. j += 2;
  207. serial[j] = '-';
  208. }
  209. serial[j] = '\0';
  210. } else {
  211. serial[0] = '\0';
  212. }
  213. /* Check for type of connection */
  214. ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE);
  215. if (ctx->is_dongle) {
  216. ctx->is_bluetooth = SDL_FALSE;
  217. ctx->official_controller = SDL_TRUE;
  218. ctx->enhanced_mode = SDL_TRUE;
  219. } else if (device->vendor_id == USB_VENDOR_SONY) {
  220. /* This will fail if we're on Bluetooth */
  221. size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data));
  222. if (size >= 7) {
  223. SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
  224. data[6], data[5], data[4], data[3], data[2], data[1]);
  225. ctx->is_bluetooth = SDL_FALSE;
  226. ctx->enhanced_mode = SDL_TRUE;
  227. } else {
  228. ctx->is_bluetooth = SDL_TRUE;
  229. /* Read a report to see if we're in enhanced mode */
  230. size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 16);
  231. #ifdef DEBUG_PS4_PROTOCOL
  232. if (size > 0) {
  233. HIDAPI_DumpPacket("PS4 first packet: size = %d", data, size);
  234. } else {
  235. SDL_Log("PS4 first packet: size = %d\n", size);
  236. }
  237. #endif
  238. if (size > 0 &&
  239. data[0] >= k_EPS4ReportIdBluetoothState1 &&
  240. data[0] <= k_EPS4ReportIdBluetoothState9) {
  241. ctx->enhanced_mode = SDL_TRUE;
  242. }
  243. }
  244. ctx->official_controller = SDL_TRUE;
  245. } else {
  246. /* Third party controllers appear to all be wired */
  247. ctx->is_bluetooth = SDL_FALSE;
  248. ctx->enhanced_mode = SDL_TRUE;
  249. }
  250. #ifdef DEBUG_PS4
  251. SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
  252. #endif
  253. /* Get the device capabilities */
  254. if (device->vendor_id == USB_VENDOR_SONY) {
  255. ctx->effects_supported = SDL_TRUE;
  256. ctx->sensors_supported = SDL_TRUE;
  257. ctx->touchpad_supported = SDL_TRUE;
  258. } else if ((size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdCapabilities, data, sizeof(data))) == 48 &&
  259. data[2] == 0x27) {
  260. Uint8 capabilities = data[4];
  261. #ifdef DEBUG_PS4_PROTOCOL
  262. HIDAPI_DumpPacket("PS4 capabilities: size = %d", data, size);
  263. #endif
  264. if ((capabilities & 0x0C) != 0) {
  265. ctx->effects_supported = SDL_TRUE;
  266. }
  267. if ((capabilities & 0x02) != 0) {
  268. ctx->sensors_supported = SDL_TRUE;
  269. }
  270. if ((capabilities & 0x40) != 0) {
  271. ctx->touchpad_supported = SDL_TRUE;
  272. }
  273. }
  274. HIDAPI_SetDeviceSerial(device, serial);
  275. return HIDAPI_JoystickConnected(device, NULL);
  276. }
  277. static int
  278. HIDAPI_DriverPS4_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
  279. {
  280. return -1;
  281. }
  282. static void
  283. HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
  284. {
  285. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  286. int i, tries, size;
  287. SDL_bool have_data = SDL_FALSE;
  288. Uint8 data[USB_PACKET_LENGTH];
  289. if (!ctx->official_controller) {
  290. #ifdef DEBUG_PS4_CALIBRATION
  291. SDL_Log("Not an official controller, ignoring calibration\n");
  292. #endif
  293. return;
  294. }
  295. for( tries = 0; tries < 5; ++tries ) {
  296. /* For Bluetooth controllers, this report switches them into advanced report mode */
  297. size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_USB, data, sizeof(data));
  298. if (size < 35) {
  299. #ifdef DEBUG_PS4_CALIBRATION
  300. SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
  301. #endif
  302. return;
  303. }
  304. if (ctx->is_bluetooth) {
  305. size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_BT, data, sizeof(data));
  306. if (size < 35) {
  307. #ifdef DEBUG_PS4_CALIBRATION
  308. SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
  309. #endif
  310. return;
  311. }
  312. }
  313. /* In some cases this report returns all zeros. Usually immediately after connection with the PS4 Dongle */
  314. for (i = 0; i < size; ++i) {
  315. if (data[i]) {
  316. have_data = SDL_TRUE;
  317. break;
  318. }
  319. }
  320. if (have_data) {
  321. break;
  322. }
  323. SDL_Delay(2);
  324. }
  325. if (have_data) {
  326. Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias;
  327. Sint16 sGyroPitchPlus, sGyroPitchMinus;
  328. Sint16 sGyroYawPlus, sGyroYawMinus;
  329. Sint16 sGyroRollPlus, sGyroRollMinus;
  330. Sint16 sGyroSpeedPlus, sGyroSpeedMinus;
  331. Sint16 sAccXPlus, sAccXMinus;
  332. Sint16 sAccYPlus, sAccYMinus;
  333. Sint16 sAccZPlus, sAccZMinus;
  334. float flNumerator;
  335. Sint16 sRange2g;
  336. #ifdef DEBUG_PS4_CALIBRATION
  337. HIDAPI_DumpPacket("PS4 calibration packet: size = %d", data, size);
  338. #endif
  339. sGyroPitchBias = LOAD16(data[1], data[2]);
  340. sGyroYawBias = LOAD16(data[3], data[4]);
  341. sGyroRollBias = LOAD16(data[5], data[6]);
  342. if (ctx->is_bluetooth || ctx->is_dongle) {
  343. sGyroPitchPlus = LOAD16(data[7], data[8]);
  344. sGyroYawPlus = LOAD16(data[9], data[10]);
  345. sGyroRollPlus = LOAD16(data[11], data[12]);
  346. sGyroPitchMinus = LOAD16(data[13], data[14]);
  347. sGyroYawMinus = LOAD16(data[15], data[16]);
  348. sGyroRollMinus = LOAD16(data[17], data[18]);
  349. } else {
  350. sGyroPitchPlus = LOAD16(data[7], data[8]);
  351. sGyroPitchMinus = LOAD16(data[9], data[10]);
  352. sGyroYawPlus = LOAD16(data[11], data[12]);
  353. sGyroYawMinus = LOAD16(data[13], data[14]);
  354. sGyroRollPlus = LOAD16(data[15], data[16]);
  355. sGyroRollMinus = LOAD16(data[17], data[18]);
  356. }
  357. sGyroSpeedPlus = LOAD16(data[19], data[20]);
  358. sGyroSpeedMinus = LOAD16(data[21], data[22]);
  359. sAccXPlus = LOAD16(data[23], data[24]);
  360. sAccXMinus = LOAD16(data[25], data[26]);
  361. sAccYPlus = LOAD16(data[27], data[28]);
  362. sAccYMinus = LOAD16(data[29], data[30]);
  363. sAccZPlus = LOAD16(data[31], data[32]);
  364. sAccZMinus = LOAD16(data[33], data[34]);
  365. flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE;
  366. ctx->calibration[0].bias = sGyroPitchBias;
  367. ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus);
  368. ctx->calibration[1].bias = sGyroYawBias;
  369. ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus);
  370. ctx->calibration[2].bias = sGyroRollBias;
  371. ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus);
  372. sRange2g = sAccXPlus - sAccXMinus;
  373. ctx->calibration[3].bias = sAccXPlus - sRange2g / 2;
  374. ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
  375. sRange2g = sAccYPlus - sAccYMinus;
  376. ctx->calibration[4].bias = sAccYPlus - sRange2g / 2;
  377. ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
  378. sRange2g = sAccZPlus - sAccZMinus;
  379. ctx->calibration[5].bias = sAccZPlus - sRange2g / 2;
  380. ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
  381. ctx->hardware_calibration = SDL_TRUE;
  382. for (i = 0; i < 6; ++i) {
  383. float divisor = (i < 3 ? 64.0f : 1.0f);
  384. #ifdef DEBUG_PS4_CALIBRATION
  385. SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity);
  386. #endif
  387. /* Some controllers have a bad calibration */
  388. if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabs(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) {
  389. #ifdef DEBUG_PS4_CALIBRATION
  390. SDL_Log("invalid calibration, ignoring\n");
  391. #endif
  392. ctx->hardware_calibration = SDL_FALSE;
  393. }
  394. }
  395. } else {
  396. #ifdef DEBUG_PS4_CALIBRATION
  397. SDL_Log("Calibration data not available\n");
  398. #endif
  399. }
  400. }
  401. static float
  402. HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, int index, Sint16 value)
  403. {
  404. float result;
  405. if (ctx->hardware_calibration) {
  406. IMUCalibrationData *calibration = &ctx->calibration[index];
  407. result = (value - calibration->bias) * calibration->sensitivity;
  408. } else if (index < 3) {
  409. result = value * 64.f;
  410. } else {
  411. result = value;
  412. }
  413. /* Convert the raw data to the units expected by SDL */
  414. if (index < 3) {
  415. result = (result / GYRO_RES_PER_DEGREE) * (float)M_PI / 180.0f;
  416. } else {
  417. result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY;
  418. }
  419. return result;
  420. }
  421. static int
  422. HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
  423. {
  424. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  425. DS4EffectsState_t effects;
  426. if (!ctx->enhanced_mode) {
  427. return SDL_Unsupported();
  428. }
  429. SDL_zero(effects);
  430. effects.ucRumbleLeft = ctx->rumble_left;
  431. effects.ucRumbleRight = ctx->rumble_right;
  432. /* Populate the LED state with the appropriate color from our lookup table */
  433. if (ctx->color_set) {
  434. effects.ucLedRed = ctx->led_red;
  435. effects.ucLedGreen = ctx->led_green;
  436. effects.ucLedBlue = ctx->led_blue;
  437. } else {
  438. SetLedsForPlayerIndex(&effects, ctx->player_index);
  439. }
  440. return HIDAPI_DriverPS4_SendJoystickEffect(device, ctx->joystick, &effects, sizeof(effects));
  441. }
  442. static void
  443. HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)
  444. {
  445. /* This is just a dummy packet that should have no effect, since we don't set the CRC */
  446. Uint8 data[78];
  447. SDL_zeroa(data);
  448. data[0] = k_EPS4ReportIdBluetoothEffects;
  449. data[1] = 0xC0; /* Magic value HID + CRC */
  450. SDL_HIDAPI_SendRumble(device, data, sizeof(data));
  451. }
  452. static void
  453. HIDAPI_DriverPS4_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  454. {
  455. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  456. if (!ctx->enhanced_mode) {
  457. ctx->enhanced_mode = SDL_TRUE;
  458. if (ctx->touchpad_supported) {
  459. SDL_PrivateJoystickAddTouchpad(joystick, 2);
  460. ctx->report_touchpad = SDL_TRUE;
  461. }
  462. if (ctx->sensors_supported) {
  463. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
  464. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
  465. }
  466. HIDAPI_DriverPS4_UpdateEffects(device);
  467. }
  468. }
  469. static void SDLCALL SDL_PS4RumbleHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  470. {
  471. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata;
  472. /* This is a one-way trip, you can't switch the controller back to simple report mode */
  473. if (SDL_GetStringBoolean(hint, SDL_FALSE)) {
  474. HIDAPI_DriverPS4_SetEnhancedMode(ctx->device, ctx->joystick);
  475. }
  476. }
  477. static void
  478. HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
  479. {
  480. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  481. if (!ctx->joystick) {
  482. return;
  483. }
  484. ctx->player_index = player_index;
  485. /* This will set the new LED state based on the new player index */
  486. HIDAPI_DriverPS4_UpdateEffects(device);
  487. }
  488. static SDL_bool
  489. HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  490. {
  491. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *) device->context;
  492. ctx->joystick = joystick;
  493. ctx->last_packet = SDL_GetTicks();
  494. ctx->report_sensors = SDL_FALSE;
  495. ctx->report_touchpad = SDL_FALSE;
  496. ctx->rumble_left = 0;
  497. ctx->rumble_right = 0;
  498. ctx->color_set = SDL_FALSE;
  499. SDL_zero(ctx->last_state);
  500. /* Initialize player index (needed for setting LEDs) */
  501. ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
  502. /* Initialize the joystick capabilities */
  503. joystick->nbuttons = ctx->touchpad_supported ? 16 : 15;
  504. joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
  505. joystick->epowerlevel = ctx->is_bluetooth ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
  506. if (ctx->enhanced_mode) {
  507. /* Force initialization when opening the joystick */
  508. ctx->enhanced_mode = SDL_FALSE;
  509. HIDAPI_DriverPS4_SetEnhancedMode(device, joystick);
  510. } else {
  511. SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
  512. SDL_PS4RumbleHintChanged, ctx);
  513. }
  514. return SDL_TRUE;
  515. }
  516. static int
  517. HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  518. {
  519. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  520. ctx->rumble_left = (low_frequency_rumble >> 8);
  521. ctx->rumble_right = (high_frequency_rumble >> 8);
  522. return HIDAPI_DriverPS4_UpdateEffects(device);
  523. }
  524. static int
  525. HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
  526. {
  527. return SDL_Unsupported();
  528. }
  529. static Uint32
  530. HIDAPI_DriverPS4_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  531. {
  532. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  533. Uint32 result = 0;
  534. if (ctx->enhanced_mode && ctx->effects_supported) {
  535. result |= SDL_JOYCAP_LED | SDL_JOYCAP_RUMBLE;
  536. }
  537. return result;
  538. }
  539. static int
  540. HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
  541. {
  542. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  543. ctx->color_set = SDL_TRUE;
  544. ctx->led_red = red;
  545. ctx->led_green = green;
  546. ctx->led_blue = blue;
  547. return HIDAPI_DriverPS4_UpdateEffects(device);
  548. }
  549. static int
  550. HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
  551. {
  552. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  553. Uint8 data[78];
  554. int report_size, offset;
  555. if (!ctx->effects_supported) {
  556. return SDL_Unsupported();
  557. }
  558. if (!ctx->enhanced_mode) {
  559. HIDAPI_DriverPS4_SetEnhancedMode(device, joystick);
  560. }
  561. SDL_zeroa(data);
  562. if (ctx->is_bluetooth) {
  563. data[0] = k_EPS4ReportIdBluetoothEffects;
  564. data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
  565. data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
  566. report_size = 78;
  567. offset = 6;
  568. } else {
  569. data[0] = k_EPS4ReportIdUsbEffects;
  570. data[1] = 0x07; /* Magic value */
  571. report_size = 32;
  572. offset = 4;
  573. }
  574. SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size));
  575. if (ctx->is_bluetooth) {
  576. /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
  577. Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
  578. Uint32 unCRC;
  579. unCRC = SDL_crc32(0, &ubHdr, 1);
  580. unCRC = SDL_crc32(unCRC, data, (size_t)(report_size - sizeof(unCRC)));
  581. SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
  582. }
  583. if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {
  584. return SDL_SetError("Couldn't send rumble packet");
  585. }
  586. return 0;
  587. }
  588. static int
  589. HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
  590. {
  591. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  592. if (!ctx->enhanced_mode) {
  593. return SDL_Unsupported();
  594. }
  595. if (enabled) {
  596. HIDAPI_DriverPS4_LoadCalibrationData(device);
  597. }
  598. ctx->report_sensors = enabled;
  599. return 0;
  600. }
  601. static void
  602. HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
  603. {
  604. static const float TOUCHPAD_SCALEX = 1.0f / 1920;
  605. static const float TOUCHPAD_SCALEY = 1.0f / 920; /* This is noted as being 944 resolution, but 920 feels better */
  606. Sint16 axis;
  607. Uint8 touchpad_state;
  608. int touchpad_x, touchpad_y;
  609. if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
  610. {
  611. Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
  612. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  613. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  614. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  615. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  616. }
  617. {
  618. Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
  619. SDL_bool dpad_up = SDL_FALSE;
  620. SDL_bool dpad_down = SDL_FALSE;
  621. SDL_bool dpad_left = SDL_FALSE;
  622. SDL_bool dpad_right = SDL_FALSE;
  623. switch (data) {
  624. case 0:
  625. dpad_up = SDL_TRUE;
  626. break;
  627. case 1:
  628. dpad_up = SDL_TRUE;
  629. dpad_right = SDL_TRUE;
  630. break;
  631. case 2:
  632. dpad_right = SDL_TRUE;
  633. break;
  634. case 3:
  635. dpad_right = SDL_TRUE;
  636. dpad_down = SDL_TRUE;
  637. break;
  638. case 4:
  639. dpad_down = SDL_TRUE;
  640. break;
  641. case 5:
  642. dpad_left = SDL_TRUE;
  643. dpad_down = SDL_TRUE;
  644. break;
  645. case 6:
  646. dpad_left = SDL_TRUE;
  647. break;
  648. case 7:
  649. dpad_up = SDL_TRUE;
  650. dpad_left = SDL_TRUE;
  651. break;
  652. default:
  653. break;
  654. }
  655. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
  656. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
  657. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
  658. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
  659. }
  660. }
  661. if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
  662. Uint8 data = packet->rgucButtonsHatAndCounter[1];
  663. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  664. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  665. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
  666. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
  667. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
  668. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
  669. }
  670. /* Some fightsticks, ex: Victrix FS Pro will only this these digital trigger bits and not the analog values so this needs to run whenever the
  671. trigger is evaluated
  672. */
  673. if ((packet->rgucButtonsHatAndCounter[1] & 0x0C) != 0) {
  674. Uint8 data = packet->rgucButtonsHatAndCounter[1];
  675. packet->ucTriggerLeft = (data & 0x04) && packet->ucTriggerLeft == 0 ? 255 : packet->ucTriggerLeft;
  676. packet->ucTriggerRight = (data & 0x08) && packet->ucTriggerRight == 0 ? 255 : packet->ucTriggerRight;
  677. }
  678. if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
  679. Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
  680. SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  681. SDL_PrivateJoystickButton(joystick, 15, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  682. }
  683. axis = ((int)packet->ucTriggerLeft * 257) - 32768;
  684. SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
  685. axis = ((int)packet->ucTriggerRight * 257) - 32768;
  686. SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
  687. axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
  688. SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
  689. axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
  690. SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
  691. axis = ((int)packet->ucRightJoystickX * 257) - 32768;
  692. SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
  693. axis = ((int)packet->ucRightJoystickY * 257) - 32768;
  694. SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
  695. if (packet->ucBatteryLevel & 0x10) {
  696. SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED);
  697. } else {
  698. /* Battery level ranges from 0 to 10 */
  699. int level = (packet->ucBatteryLevel & 0xF);
  700. if (level == 0) {
  701. SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
  702. } else if (level <= 2) {
  703. SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
  704. } else if (level <= 7) {
  705. SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
  706. } else {
  707. SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
  708. }
  709. }
  710. if (ctx->report_touchpad) {
  711. touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
  712. touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
  713. touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
  714. SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
  715. touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
  716. touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
  717. touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
  718. SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
  719. }
  720. if (ctx->report_sensors) {
  721. float data[3];
  722. data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
  723. data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
  724. data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1]));
  725. SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3);
  726. data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1]));
  727. data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1]));
  728. data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1]));
  729. SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3);
  730. }
  731. SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
  732. }
  733. static SDL_bool
  734. HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
  735. {
  736. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  737. SDL_Joystick *joystick = NULL;
  738. Uint8 data[USB_PACKET_LENGTH*2];
  739. int size;
  740. int packet_count = 0;
  741. if (device->num_joysticks > 0) {
  742. joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
  743. } else {
  744. return SDL_FALSE;
  745. }
  746. while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
  747. #ifdef DEBUG_PS4_PROTOCOL
  748. HIDAPI_DumpPacket("PS4 packet: size = %d", data, size);
  749. #endif
  750. ++packet_count;
  751. ctx->last_packet = SDL_GetTicks();
  752. if (!joystick) {
  753. continue;
  754. }
  755. switch (data[0]) {
  756. case k_EPS4ReportIdUsbState:
  757. HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]);
  758. break;
  759. case k_EPS4ReportIdBluetoothState1:
  760. case k_EPS4ReportIdBluetoothState2:
  761. case k_EPS4ReportIdBluetoothState3:
  762. case k_EPS4ReportIdBluetoothState4:
  763. case k_EPS4ReportIdBluetoothState5:
  764. case k_EPS4ReportIdBluetoothState6:
  765. case k_EPS4ReportIdBluetoothState7:
  766. case k_EPS4ReportIdBluetoothState8:
  767. case k_EPS4ReportIdBluetoothState9:
  768. if (!ctx->enhanced_mode) {
  769. /* This is the extended report, we can enable effects now */
  770. HIDAPI_DriverPS4_SetEnhancedMode(device, joystick);
  771. }
  772. /* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */
  773. if (data[1] & 0x80) {
  774. HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t*)&data[3]);
  775. }
  776. break;
  777. default:
  778. #ifdef DEBUG_JOYSTICK
  779. SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
  780. #endif
  781. break;
  782. }
  783. }
  784. if (ctx->is_bluetooth && packet_count == 0) {
  785. /* Check to see if it looks like the device disconnected */
  786. if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) {
  787. /* Send an empty output report to tickle the Bluetooth stack */
  788. HIDAPI_DriverPS4_TickleBluetooth(device);
  789. }
  790. }
  791. if (size < 0) {
  792. /* Read error, device is disconnected */
  793. HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
  794. }
  795. return (size >= 0);
  796. }
  797. static void
  798. HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
  799. {
  800. SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
  801. SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
  802. SDL_PS4RumbleHintChanged, ctx);
  803. ctx->joystick = NULL;
  804. }
  805. static void
  806. HIDAPI_DriverPS4_FreeDevice(SDL_HIDAPI_Device *device)
  807. {
  808. }
  809. SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
  810. {
  811. SDL_HINT_JOYSTICK_HIDAPI_PS4,
  812. SDL_TRUE,
  813. HIDAPI_DriverPS4_RegisterHints,
  814. HIDAPI_DriverPS4_UnregisterHints,
  815. HIDAPI_DriverPS4_IsEnabled,
  816. HIDAPI_DriverPS4_IsSupportedDevice,
  817. HIDAPI_DriverPS4_InitDevice,
  818. HIDAPI_DriverPS4_GetDevicePlayerIndex,
  819. HIDAPI_DriverPS4_SetDevicePlayerIndex,
  820. HIDAPI_DriverPS4_UpdateDevice,
  821. HIDAPI_DriverPS4_OpenJoystick,
  822. HIDAPI_DriverPS4_RumbleJoystick,
  823. HIDAPI_DriverPS4_RumbleJoystickTriggers,
  824. HIDAPI_DriverPS4_GetJoystickCapabilities,
  825. HIDAPI_DriverPS4_SetJoystickLED,
  826. HIDAPI_DriverPS4_SendJoystickEffect,
  827. HIDAPI_DriverPS4_SetJoystickSensorsEnabled,
  828. HIDAPI_DriverPS4_CloseJoystick,
  829. HIDAPI_DriverPS4_FreeDevice,
  830. };
  831. #endif /* SDL_JOYSTICK_HIDAPI_PS4 */
  832. #endif /* SDL_JOYSTICK_HIDAPI */
  833. /* vi: set ts=4 sw=4 expandtab: */