Atlas - SDL_hidapi_8bitdo.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 26969 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25#include "../SDL_sysjoystick.h" 26#include "SDL_hidapijoystick_c.h" 27#include "SDL_hidapi_rumble.h" 28 29#ifdef SDL_JOYSTICK_HIDAPI_8BITDO 30 31// Define this if you want to log all packets from the controller 32#if 0 33#define DEBUG_8BITDO_PROTOCOL 34#endif 35 36enum 37{ 38 SDL_GAMEPAD_BUTTON_8BITDO_L4 = 11, 39 SDL_GAMEPAD_BUTTON_8BITDO_R4, 40 SDL_GAMEPAD_BUTTON_8BITDO_PL, 41 SDL_GAMEPAD_BUTTON_8BITDO_PR, 42 SDL_GAMEPAD_NUM_8BITDO_BUTTONS, 43}; 44 45#define SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID 0x06 46#define SDL_8BITDO_REPORTID_SDL_REPORTID 0x04 47#define SDL_8BITDO_REPORTID_NOT_SUPPORTED_SDL_REPORTID 0x03 48#define SDL_8BITDO_BT_REPORTID_SDL_REPORTID 0x01 49 50#define SDL_8BITDO_SENSOR_TIMESTAMP_ENABLE 0xAA 51#define ABITDO_ACCEL_SCALE 4096.f 52#define ABITDO_GYRO_MAX_DEGREES_PER_SECOND 2000.f 53 54 55#define LOAD32(A, B, C, D) ((((Uint32)(A)) << 0) | \ 56 (((Uint32)(B)) << 8) | \ 57 (((Uint32)(C)) << 16) | \ 58 (((Uint32)(D)) << 24)) 59 60 61typedef struct 62{ 63 bool sensors_supported; 64 bool sensors_enabled; 65 bool touchpad_01_supported; 66 bool touchpad_02_supported; 67 bool rumble_supported; 68 bool rumble_type; 69 bool rgb_supported; 70 bool player_led_supported; 71 bool powerstate_supported; 72 bool sensor_timestamp_supported; 73 Uint8 serial[6]; 74 Uint16 version; 75 Uint16 version_beta; 76 float accelScale; 77 float gyroScale; 78 Uint8 last_state[USB_PACKET_LENGTH]; 79 Uint64 sensor_timestamp; // Nanoseconds. Simulate onboard clock. Different models have different rates vs different connection styles. 80 Uint64 sensor_timestamp_interval; 81 Uint32 last_tick; 82} SDL_Driver8BitDo_Context; 83 84#pragma pack(push,1) 85typedef struct 86{ 87 bool sensors_supported; 88 bool touchpad_01_supported; 89 bool touchpad_02_supported; 90 bool rumble_supported; 91 bool rumble_type; 92 bool rgb_supported; 93 Uint8 device_type; 94 Uint8 serial[6]; 95 Uint16 version; 96 Uint16 version_beta; 97 Uint16 pid; 98} ABITDO_DEVICE_INFO; 99 100typedef struct 101{ 102 // Accelerometer values 103 short sAccelX; 104 short sAccelY; 105 short sAccelZ; 106 107 // Gyroscope values 108 short sGyroX; 109 short sGyroY; 110 short sGyroZ; 111} ABITDO_SENSORS; 112 113#pragma pack(pop) 114 115 116static void HIDAPI_Driver8BitDo_RegisterHints(SDL_HintCallback callback, void *userdata) 117{ 118 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_8BITDO, callback, userdata); 119} 120 121static void HIDAPI_Driver8BitDo_UnregisterHints(SDL_HintCallback callback, void *userdata) 122{ 123 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_8BITDO, callback, userdata); 124} 125 126static bool HIDAPI_Driver8BitDo_IsEnabled(void) 127{ 128 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_8BITDO, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 129} 130 131static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length) 132{ 133 SDL_memset(report, 0, length); 134 report[0] = report_id; 135 return SDL_hid_get_feature_report(dev, report, length); 136} 137 138static bool HIDAPI_Driver8BitDo_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) 139{ 140 if (vendor_id == USB_VENDOR_8BITDO) { 141 switch (product_id) { 142 case USB_PRODUCT_8BITDO_SF30_PRO: 143 case USB_PRODUCT_8BITDO_SF30_PRO_BT: 144 case USB_PRODUCT_8BITDO_SN30_PRO: 145 case USB_PRODUCT_8BITDO_SN30_PRO_BT: 146 case USB_PRODUCT_8BITDO_PRO_2: 147 case USB_PRODUCT_8BITDO_PRO_2_BT: 148 case USB_PRODUCT_8BITDO_PRO_3: 149 case USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS: 150 return true; 151 default: 152 break; 153 } 154 } 155 return false; 156} 157 158static bool HIDAPI_Driver8BitDo_InitDevice(SDL_HIDAPI_Device *device) 159{ 160 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)SDL_calloc(1, sizeof(*ctx)); 161 if (!ctx) { 162 return false; 163 } 164 device->context = ctx; 165 166 if (device->product_id == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS) { 167 // The Ultimate 2 Wireless v1.02 firmware has 12 byte reports, v1.03 firmware has 34 byte reports 168 const int ULTIMATE2_WIRELESS_V103_REPORT_SIZE = 34; 169 const int MAX_ATTEMPTS = 3; 170 171 for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { 172 Uint8 data[USB_PACKET_LENGTH]; 173 int size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 80); 174 if (size == 0) { 175 // Try again 176 continue; 177 } 178 if (size >= ULTIMATE2_WIRELESS_V103_REPORT_SIZE) { 179 ctx->sensors_supported = true; 180 ctx->rumble_supported = true; 181 ctx->powerstate_supported = true; 182 } 183 break; 184 } 185 } else { 186 Uint8 data[USB_PACKET_LENGTH]; 187 const int MAX_ATTEMPTS = 5; 188 for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) { 189 int size = ReadFeatureReport(device->dev, SDL_8BITDO_FEATURE_REPORTID_ENABLE_SDL_REPORTID, data, sizeof(data)); 190 if (size > 0) { 191#ifdef DEBUG_8BITDO_PROTOCOL 192 HIDAPI_DumpPacket("8BitDo features packet: size = %d", data, size); 193#endif 194 ctx->sensors_supported = true; 195 ctx->rumble_supported = true; 196 ctx->powerstate_supported = true; 197 198 if (size >= 14 && data[13] == SDL_8BITDO_SENSOR_TIMESTAMP_ENABLE) { 199 ctx->sensor_timestamp_supported = true; 200 } 201 202 // Set the serial number to the Bluetooth MAC address 203 if (size >= 12 && data[10] != 0) { 204 char serial[18]; 205 (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", 206 data[10], data[9], data[8], data[7], data[6], data[5]); 207 HIDAPI_SetDeviceSerial(device, serial); 208 } 209 break; 210 } 211 212 // Try again 213 SDL_Delay(10); 214 } 215 } 216 217 if (device->product_id == USB_PRODUCT_8BITDO_SF30_PRO || device->product_id == USB_PRODUCT_8BITDO_SF30_PRO_BT) { 218 HIDAPI_SetDeviceName(device, "8BitDo SF30 Pro"); 219 } else if (device->product_id == USB_PRODUCT_8BITDO_SN30_PRO || device->product_id == USB_PRODUCT_8BITDO_SN30_PRO_BT) { 220 HIDAPI_SetDeviceName(device, "8BitDo SN30 Pro"); 221 } else if (device->product_id == USB_PRODUCT_8BITDO_PRO_2 || device->product_id == USB_PRODUCT_8BITDO_PRO_2_BT) { 222 HIDAPI_SetDeviceName(device, "8BitDo Pro 2"); 223 } else if (device->product_id == USB_PRODUCT_8BITDO_PRO_3) { 224 HIDAPI_SetDeviceName(device, "8BitDo Pro 3"); 225 } 226 227 return HIDAPI_JoystickConnected(device, NULL); 228} 229 230static int HIDAPI_Driver8BitDo_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 231{ 232 return -1; 233} 234 235static void HIDAPI_Driver8BitDo_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 236{ 237} 238 239static Uint64 HIDAPI_Driver8BitDo_GetIMURateForProductID(SDL_HIDAPI_Device *device) 240{ 241 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)device->context; 242 243 // TODO: If sensor time stamp is sent, these fixed settings from observation can be replaced 244 switch (device->product_id) { 245 case USB_PRODUCT_8BITDO_SF30_PRO: 246 case USB_PRODUCT_8BITDO_SF30_PRO_BT: 247 case USB_PRODUCT_8BITDO_SN30_PRO: 248 case USB_PRODUCT_8BITDO_SN30_PRO_BT: 249 if (device->is_bluetooth) { 250 // Note, This is estimated by observation of Bluetooth packets received in the testcontroller tool 251 return 70; // Observed to be anywhere between 60-90 hz. Possibly lossy in current state 252 } else if (ctx->sensor_timestamp_supported) { 253 // This firmware appears to update at 200 Hz over USB 254 return 200; 255 } else { 256 // This firmware appears to update at 100 Hz over USB 257 return 100; 258 } 259 case USB_PRODUCT_8BITDO_PRO_2: 260 case USB_PRODUCT_8BITDO_PRO_2_BT: // Note, labeled as "BT" but appears this way when wired. 261 case USB_PRODUCT_8BITDO_PRO_3: 262 if (device->is_bluetooth) { 263 // Note, This is estimated by observation of Bluetooth packets received in the testcontroller tool 264 return 85; // Observed Bluetooth packet rate seems to be 80-90hz 265 } else if (ctx->sensor_timestamp_supported) { 266 // This firmware appears to update at 200 Hz over USB 267 return 200; 268 } else { 269 // This firmware appears to update at 100 Hz over USB 270 return 100; 271 } 272 case USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS: 273 if (device->is_bluetooth) { 274 // Note, This is estimated by observation of Bluetooth packets received in the testcontroller tool 275 return 120; // Observed Bluetooth packet rate seems to be 120hz 276 } else { 277 // This firmware appears to update at 1000 Hz over USB dongle 278 return 1000; 279 } 280 default: 281 return 120; 282 } 283} 284 285#ifndef DEG2RAD 286#define DEG2RAD(x) ((float)(x) * (float)(SDL_PI_F / 180.f)) 287#endif 288 289static bool HIDAPI_Driver8BitDo_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 290{ 291 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)device->context; 292 293 SDL_AssertJoysticksLocked(); 294 295 SDL_zeroa(ctx->last_state); 296 297 // Initialize the joystick capabilities 298 if (device->product_id == USB_PRODUCT_8BITDO_PRO_2 || 299 device->product_id == USB_PRODUCT_8BITDO_PRO_2_BT || 300 device->product_id == USB_PRODUCT_8BITDO_PRO_3 || 301 device->product_id == USB_PRODUCT_8BITDO_ULTIMATE2_WIRELESS) { 302 // This controller has additional buttons 303 joystick->nbuttons = SDL_GAMEPAD_NUM_8BITDO_BUTTONS; 304 } else { 305 joystick->nbuttons = 11; 306 } 307 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 308 joystick->nhats = 1; 309 310 if (ctx->sensors_supported) { 311 312 // Different 8Bitdo controllers in different connection modes have different polling rates. 313 const Uint64 imu_polling_rate = HIDAPI_Driver8BitDo_GetIMURateForProductID(device); 314 ctx->sensor_timestamp_interval = SDL_NS_PER_SECOND / imu_polling_rate; 315 316 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, (float)imu_polling_rate); 317 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, (float)imu_polling_rate); 318 319 ctx->accelScale = SDL_STANDARD_GRAVITY / ABITDO_ACCEL_SCALE; 320 // Hardware senses +/- N Degrees per second mapped to +/- INT16_MAX 321 ctx->gyroScale = DEG2RAD(ABITDO_GYRO_MAX_DEGREES_PER_SECOND) / INT16_MAX; 322 } 323 324 return true; 325} 326 327static bool HIDAPI_Driver8BitDo_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 328{ 329 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)device->context; 330 if (ctx->rumble_supported) { 331 Uint8 rumble_packet[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 }; 332 rumble_packet[1] = low_frequency_rumble >> 8; 333 rumble_packet[2] = high_frequency_rumble >> 8; 334 335 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 336 return SDL_SetError("Couldn't send rumble packet"); 337 } 338 return true; 339 } else { 340 return SDL_Unsupported(); 341 } 342} 343 344static bool HIDAPI_Driver8BitDo_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 345{ 346 return SDL_Unsupported(); 347} 348 349static Uint32 HIDAPI_Driver8BitDo_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 350{ 351 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)device->context; 352 Uint32 caps = 0; 353 if (ctx->rumble_supported) { 354 caps |= SDL_JOYSTICK_CAP_RUMBLE; 355 } 356 if (ctx->rgb_supported) { 357 caps |= SDL_JOYSTICK_CAP_RGB_LED; 358 } 359 return caps; 360} 361 362static bool HIDAPI_Driver8BitDo_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 363{ 364 return SDL_Unsupported(); 365} 366 367static bool HIDAPI_Driver8BitDo_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 368{ 369 return SDL_Unsupported(); 370} 371 372static bool HIDAPI_Driver8BitDo_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 373{ 374 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)device->context; 375 if (ctx->sensors_supported) { 376 ctx->sensors_enabled = enabled; 377 return true; 378 } 379 return SDL_Unsupported(); 380} 381 382static void HIDAPI_Driver8BitDo_HandleOldStatePacket(SDL_Joystick *joystick, SDL_Driver8BitDo_Context *ctx, Uint8 *data, int size) 383{ 384 Sint16 axis; 385 Uint64 timestamp = SDL_GetTicksNS(); 386 387 if (ctx->last_state[2] != data[2]) { 388 Uint8 hat; 389 390 switch (data[2]) { 391 case 0: 392 hat = SDL_HAT_UP; 393 break; 394 case 1: 395 hat = SDL_HAT_RIGHTUP; 396 break; 397 case 2: 398 hat = SDL_HAT_RIGHT; 399 break; 400 case 3: 401 hat = SDL_HAT_RIGHTDOWN; 402 break; 403 case 4: 404 hat = SDL_HAT_DOWN; 405 break; 406 case 5: 407 hat = SDL_HAT_LEFTDOWN; 408 break; 409 case 6: 410 hat = SDL_HAT_LEFT; 411 break; 412 case 7: 413 hat = SDL_HAT_LEFTUP; 414 break; 415 default: 416 hat = SDL_HAT_CENTERED; 417 break; 418 } 419 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 420 } 421 422 if (ctx->last_state[0] != data[0]) { 423 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[0] & 0x01) != 0)); 424 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[0] & 0x02) != 0)); 425 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[0] & 0x08) != 0)); 426 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[0] & 0x10) != 0)); 427 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[0] & 0x40) != 0)); 428 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[0] & 0x80) != 0)); 429 } 430 431 if (ctx->last_state[1] != data[1]) { 432 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[1] & 0x10) != 0)); 433 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[1] & 0x04) != 0)); 434 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[1] & 0x08) != 0)); 435 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x20) != 0)); 436 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x40) != 0)); 437 438 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (data[1] & 0x01) ? SDL_MAX_SINT16 : SDL_MIN_SINT16); 439 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (data[1] & 0x02) ? SDL_MAX_SINT16 : SDL_MIN_SINT16); 440 } 441 442#define READ_STICK_AXIS(offset) \ 443 (data[offset] == 0x7f ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x7f), -0x7f, 0xff - 0x7f, SDL_MIN_SINT16, SDL_MAX_SINT16)) 444 { 445 axis = READ_STICK_AXIS(3); 446 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 447 axis = READ_STICK_AXIS(4); 448 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 449 axis = READ_STICK_AXIS(5); 450 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 451 axis = READ_STICK_AXIS(6); 452 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 453 } 454#undef READ_STICK_AXIS 455 456 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 457} 458 459static void HIDAPI_Driver8BitDo_HandleStatePacket(SDL_Joystick *joystick, SDL_Driver8BitDo_Context *ctx, Uint8 *data, int size) 460{ 461 Sint16 axis; 462 Uint64 timestamp = SDL_GetTicksNS(); 463 464 switch (data[0]) { 465 case SDL_8BITDO_REPORTID_NOT_SUPPORTED_SDL_REPORTID: // Firmware without enhanced mode 466 case SDL_8BITDO_REPORTID_SDL_REPORTID: // Enhanced mode USB report 467 case SDL_8BITDO_BT_REPORTID_SDL_REPORTID: // Enhanced mode Bluetooth report 468 break; 469 default: 470 // We don't know how to handle this report 471 return; 472 } 473 474 if (ctx->last_state[1] != data[1]) { 475 Uint8 hat; 476 477 switch (data[1]) { 478 case 0: 479 hat = SDL_HAT_UP; 480 break; 481 case 1: 482 hat = SDL_HAT_RIGHTUP; 483 break; 484 case 2: 485 hat = SDL_HAT_RIGHT; 486 break; 487 case 3: 488 hat = SDL_HAT_RIGHTDOWN; 489 break; 490 case 4: 491 hat = SDL_HAT_DOWN; 492 break; 493 case 5: 494 hat = SDL_HAT_LEFTDOWN; 495 break; 496 case 6: 497 hat = SDL_HAT_LEFT; 498 break; 499 case 7: 500 hat = SDL_HAT_LEFTUP; 501 break; 502 default: 503 hat = SDL_HAT_CENTERED; 504 break; 505 } 506 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 507 } 508 509 510 if (ctx->last_state[8] != data[8]) { 511 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[8] & 0x01) != 0)); 512 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[8] & 0x02) != 0)); 513 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[8] & 0x08) != 0)); 514 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[8] & 0x10) != 0)); 515 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[8] & 0x40) != 0)); 516 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[8] & 0x80) != 0)); 517 518 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_PL, ((data[8] & 0x20) != 0)); 519 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_PR, ((data[8] & 0x04) != 0)); 520 } 521 522 if (ctx->last_state[9] != data[9]) { 523 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[9] & 0x10) != 0)); 524 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[9] & 0x04) != 0)); 525 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[9] & 0x08) != 0)); 526 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[9] & 0x20) != 0)); 527 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[9] & 0x40) != 0)); 528 } 529 530 if (size > 10 && ctx->last_state[10] != data[10]) { 531 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_L4, ((data[10] & 0x01) != 0)); 532 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_8BITDO_R4, ((data[10] & 0x02) != 0)); 533 } 534 535#define READ_STICK_AXIS(offset) \ 536 (data[offset] == 0x7f ? 0 : (Sint16)HIDAPI_RemapVal((float)((int)data[offset] - 0x7f), -0x7f, 0xff - 0x7f, SDL_MIN_SINT16, SDL_MAX_SINT16)) 537 { 538 axis = READ_STICK_AXIS(2); 539 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 540 axis = READ_STICK_AXIS(3); 541 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 542 axis = READ_STICK_AXIS(4); 543 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 544 axis = READ_STICK_AXIS(5); 545 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 546 } 547#undef READ_STICK_AXIS 548 549#define READ_TRIGGER_AXIS(offset) \ 550 (Sint16)(((int)data[offset] * 257) - 32768) 551 { 552 axis = READ_TRIGGER_AXIS(7); 553 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 554 axis = READ_TRIGGER_AXIS(6); 555 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 556 } 557#undef READ_TRIGGER_AXIS 558 559 if (ctx->powerstate_supported) { 560 SDL_PowerState state; 561 int percent; 562 Uint8 status = data[14] >> 7; 563 Uint8 level = (data[14] & 0x7f); 564 if (level == 100) { 565 status = 2; 566 } 567 switch (status) { 568 case 0: 569 state = SDL_POWERSTATE_ON_BATTERY; 570 percent = level; 571 break; 572 case 1: 573 state = SDL_POWERSTATE_CHARGING; 574 percent = level; 575 break; 576 case 2: 577 state = SDL_POWERSTATE_CHARGED; 578 percent = 100; 579 break; 580 default: 581 state = SDL_POWERSTATE_UNKNOWN; 582 percent = 0; 583 break; 584 } 585 SDL_SendJoystickPowerInfo(joystick, state, percent); 586 } 587 588 if (ctx->sensors_enabled) { 589 Uint64 sensor_timestamp; 590 float values[3]; 591 ABITDO_SENSORS *sensors = (ABITDO_SENSORS *)&data[15]; 592 593 if (ctx->sensor_timestamp_supported) { 594 Uint32 delta; 595 Uint32 tick = LOAD32(data[27], data[28], data[29], data[30]); 596 597 if (ctx->last_tick) { 598 if (ctx->last_tick < tick) { 599 delta = (tick - ctx->last_tick); 600 } else { 601 delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1); 602 } 603 // Sanity check the delta value 604 if (delta < 100000) { 605 ctx->sensor_timestamp_interval = SDL_US_TO_NS(delta); 606 } 607 } 608 ctx->last_tick = tick; 609 } 610 611 // Note: we cannot use the time stamp of the receiving computer due to packet delay creating "spiky" timings. 612 // The imu time stamp is intended to be the sample time of the on-board hardware. 613 // In the absence of time stamp data from the data[], we can simulate that by 614 // advancing a time stamp by the observed/known imu clock rate. This is 8ms = 125 Hz 615 sensor_timestamp = ctx->sensor_timestamp; 616 ctx->sensor_timestamp += ctx->sensor_timestamp_interval; 617 618 // This device's IMU values are reported differently from SDL 619 // Thus we perform a rotation of the coordinate system to match the SDL standard. 620 621 // By observation of this device: 622 // Hardware x is reporting roll (rotation about the power jack's axis) 623 // Hardware y is reporting pitch (rotation about the horizontal axis) 624 // Hardware z is reporting yaw (rotation about the joysticks' center axis) 625 values[0] = -sensors->sGyroY * ctx->gyroScale; // Rotation around pitch axis 626 values[1] = sensors->sGyroZ * ctx->gyroScale; // Rotation around yaw axis 627 values[2] = -sensors->sGyroX * ctx->gyroScale; // Rotation around roll axis 628 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, values, 3); 629 630 // By observation of this device: 631 // Accelerometer X is positive when front of the controller points toward the sky. 632 // Accelerometer y is positive when left side of the controller points toward the sky. 633 // Accelerometer Z is positive when sticks point toward the sky. 634 values[0] = -sensors->sAccelY * ctx->accelScale; // Acceleration along pitch axis 635 values[1] = sensors->sAccelZ * ctx->accelScale; // Acceleration along yaw axis 636 values[2] = -sensors->sAccelX * ctx->accelScale; // Acceleration along roll axis 637 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, values, 3); 638 } 639 640 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 641} 642 643static bool HIDAPI_Driver8BitDo_UpdateDevice(SDL_HIDAPI_Device *device) 644{ 645 SDL_Driver8BitDo_Context *ctx = (SDL_Driver8BitDo_Context *)device->context; 646 SDL_Joystick *joystick = NULL; 647 Uint8 data[USB_PACKET_LENGTH]; 648 int size = 0; 649 650 if (device->num_joysticks > 0) { 651 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 652 } else { 653 return false; 654 } 655 656 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 657#ifdef DEBUG_8BITDO_PROTOCOL 658 HIDAPI_DumpPacket("8BitDo packet: size = %d", data, size); 659#endif 660 if (!joystick) { 661 continue; 662 } 663 664 if (size == 9) { 665 // Old firmware USB report for the SF30 Pro and SN30 Pro controllers 666 HIDAPI_Driver8BitDo_HandleOldStatePacket(joystick, ctx, data, size); 667 } else { 668 HIDAPI_Driver8BitDo_HandleStatePacket(joystick, ctx, data, size); 669 } 670 } 671 672 if (size < 0) { 673 // Read error, device is disconnected 674 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 675 } 676 return (size >= 0); 677} 678 679static void HIDAPI_Driver8BitDo_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 680{ 681} 682 683static void HIDAPI_Driver8BitDo_FreeDevice(SDL_HIDAPI_Device *device) 684{ 685} 686 687SDL_HIDAPI_DeviceDriver SDL_HIDAPI_Driver8BitDo = { 688 SDL_HINT_JOYSTICK_HIDAPI_8BITDO, 689 true, 690 HIDAPI_Driver8BitDo_RegisterHints, 691 HIDAPI_Driver8BitDo_UnregisterHints, 692 HIDAPI_Driver8BitDo_IsEnabled, 693 HIDAPI_Driver8BitDo_IsSupportedDevice, 694 HIDAPI_Driver8BitDo_InitDevice, 695 HIDAPI_Driver8BitDo_GetDevicePlayerIndex, 696 HIDAPI_Driver8BitDo_SetDevicePlayerIndex, 697 HIDAPI_Driver8BitDo_UpdateDevice, 698 HIDAPI_Driver8BitDo_OpenJoystick, 699 HIDAPI_Driver8BitDo_RumbleJoystick, 700 HIDAPI_Driver8BitDo_RumbleJoystickTriggers, 701 HIDAPI_Driver8BitDo_GetJoystickCapabilities, 702 HIDAPI_Driver8BitDo_SetJoystickLED, 703 HIDAPI_Driver8BitDo_SendJoystickEffect, 704 HIDAPI_Driver8BitDo_SetJoystickSensorsEnabled, 705 HIDAPI_Driver8BitDo_CloseJoystick, 706 HIDAPI_Driver8BitDo_FreeDevice, 707}; 708 709#endif // SDL_JOYSTICK_HIDAPI_8BITDO 710 711#endif // SDL_JOYSTICK_HIDAPI 712
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.