Added support for clang thread-safety analysis

The annotations have been added to SDL_mutex.h and have been made public so applications can enable this for their own code.

Clang assumes that locking and unlocking can't fail, but SDL has the concept of a NULL mutex, so the mutex functions have been changed not to report errors if a mutex hasn't been initialized. We do have mutexes that might be accessed when they are NULL, notably in the event system, so this is an important change.

This commit cleans up a bunch of rare race conditions in the joystick and game controller code so now everything should be completely protected by the joystick lock.

To test this, change the compiler to "clang -Wthread-safety -Werror=thread-safety -DSDL_THREAD_SAFETY_ANALYSIS"
This commit is contained in:
Sam Lantinga
2022-12-13 14:03:40 -08:00
parent 582fb3901a
commit d59caffe2c
42 changed files with 1667 additions and 1174 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,7 @@ extern SDL_bool SDL_JoysticksQuitting(void);
extern SDL_bool SDL_JoysticksLocked(void);
/* Make sure we currently have the joysticks locked */
extern void SDL_AssertJoysticksLocked(void);
extern void SDL_AssertJoysticksLocked(void) SDL_ASSERT_CAPABILITY(SDL_joystick_lock);
/* Function to get the next available joystick instance ID */
extern SDL_JoystickID SDL_GetNextJoystickInstanceID(void);

View File

@@ -67,68 +67,72 @@ typedef struct _SDL_JoystickSensorInfo
Uint64 timestamp_us;
} SDL_JoystickSensorInfo;
#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
struct _SDL_Joystick
{
const void *magic;
const void *magic _guarded;
SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
char *name; /* Joystick name - system dependent */
char *path; /* Joystick path - system dependent */
char *serial; /* Joystick serial */
SDL_JoystickGUID guid; /* Joystick guid */
Uint16 firmware_version; /* Firmware version, if available */
SDL_JoystickID instance_id _guarded; /* Device instance, monotonically increasing from 0 */
char *name _guarded; /* Joystick name - system dependent */
char *path _guarded; /* Joystick path - system dependent */
char *serial _guarded; /* Joystick serial */
SDL_JoystickGUID guid _guarded; /* Joystick guid */
Uint16 firmware_version _guarded; /* Firmware version, if available */
int naxes; /* Number of axis controls on the joystick */
SDL_JoystickAxisInfo *axes;
int naxes _guarded; /* Number of axis controls on the joystick */
SDL_JoystickAxisInfo *axes _guarded;
int nhats; /* Number of hats on the joystick */
Uint8 *hats; /* Current hat states */
int nhats _guarded; /* Number of hats on the joystick */
Uint8 *hats _guarded; /* Current hat states */
int nballs; /* Number of trackballs on the joystick */
int nballs _guarded; /* Number of trackballs on the joystick */
struct balldelta
{
int dx;
int dy;
} *balls; /* Current ball motion deltas */
} *balls _guarded; /* Current ball motion deltas */
int nbuttons; /* Number of buttons on the joystick */
Uint8 *buttons; /* Current button states */
int nbuttons _guarded; /* Number of buttons on the joystick */
Uint8 *buttons _guarded; /* Current button states */
int ntouchpads; /* Number of touchpads on the joystick */
SDL_JoystickTouchpadInfo *touchpads; /* Current touchpad states */
int ntouchpads _guarded; /* Number of touchpads on the joystick */
SDL_JoystickTouchpadInfo *touchpads _guarded; /* Current touchpad states */
int nsensors; /* Number of sensors on the joystick */
int nsensors_enabled;
SDL_JoystickSensorInfo *sensors;
int nsensors _guarded; /* Number of sensors on the joystick */
int nsensors_enabled _guarded;
SDL_JoystickSensorInfo *sensors _guarded;
Uint16 low_frequency_rumble;
Uint16 high_frequency_rumble;
Uint32 rumble_expiration;
Uint32 rumble_resend;
Uint16 low_frequency_rumble _guarded;
Uint16 high_frequency_rumble _guarded;
Uint32 rumble_expiration _guarded;
Uint32 rumble_resend _guarded;
Uint16 left_trigger_rumble;
Uint16 right_trigger_rumble;
Uint32 trigger_rumble_expiration;
Uint16 left_trigger_rumble _guarded;
Uint16 right_trigger_rumble _guarded;
Uint32 trigger_rumble_expiration _guarded;
Uint8 led_red;
Uint8 led_green;
Uint8 led_blue;
Uint32 led_expiration;
Uint8 led_red _guarded;
Uint8 led_green _guarded;
Uint8 led_blue _guarded;
Uint32 led_expiration _guarded;
SDL_bool attached;
SDL_bool is_game_controller;
SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */
SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
SDL_bool attached _guarded;
SDL_bool is_game_controller _guarded;
SDL_bool delayed_guide_button _guarded; /* SDL_TRUE if this device has the guide button event delayed */
SDL_JoystickPowerLevel epowerlevel _guarded; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
struct _SDL_JoystickDriver *driver;
struct _SDL_JoystickDriver *driver _guarded;
struct joystick_hwdata *hwdata; /* Driver dependent information */
struct joystick_hwdata *hwdata _guarded; /* Driver dependent information */
int ref_count; /* Reference count for multiple opens */
int ref_count _guarded; /* Reference count for multiple opens */
struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
struct _SDL_Joystick *next _guarded; /* pointer to next joystick we have allocated */
};
#undef _guarded
/* Device bus definitions */
#define SDL_HARDWARE_BUS_UNKNOWN 0x00
#define SDL_HARDWARE_BUS_USB 0x03

View File

@@ -67,6 +67,8 @@ static SDL_bool HIDAPI_DriverCombined_OpenJoystick(SDL_HIDAPI_Device *device, SD
char *serial = NULL, *new_serial;
size_t serial_length = 0, new_length;
SDL_AssertJoysticksLocked();
for (i = 0; i < device->num_children; ++i) {
SDL_HIDAPI_Device *child = device->children[i];
if (!child->driver->OpenJoystick(child, joystick)) {

View File

@@ -408,6 +408,9 @@ static SDL_bool HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_Device *device, SD
{
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
Uint8 i;
SDL_AssertJoysticksLocked();
for (i = 0; i < MAX_CONTROLLERS; i += 1) {
if (joystick->instance_id == ctx->joysticks[i]) {
joystick->nbuttons = 12;
@@ -424,6 +427,8 @@ static int HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_J
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
Uint8 i, val;
SDL_AssertJoysticksLocked();
if (ctx->pc_mode) {
return SDL_Unsupported();
}
@@ -469,6 +474,8 @@ static Uint32 HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *d
SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
Uint32 result = 0;
SDL_AssertJoysticksLocked();
if (!ctx->pc_mode) {
Uint8 i;

View File

@@ -100,6 +100,8 @@ static SDL_bool HIDAPI_DriverLuna_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Jo
{
SDL_DriverLuna_Context *ctx = (SDL_DriverLuna_Context *)device->context;
SDL_AssertJoysticksLocked();
SDL_zeroa(ctx->last_state);
/* Initialize the joystick capabilities */

View File

@@ -234,6 +234,8 @@ static SDL_bool HIDAPI_DriverPS3_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
{
SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
ctx->effects_updated = SDL_FALSE;
ctx->rumble_left = 0;
@@ -630,6 +632,8 @@ static SDL_bool HIDAPI_DriverPS3ThirdParty_OpenJoystick(SDL_HIDAPI_Device *devic
{
SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
SDL_zeroa(ctx->last_state);

View File

@@ -668,6 +668,8 @@ static SDL_bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
ctx->last_packet = SDL_GetTicks();
ctx->report_sensors = SDL_FALSE;

View File

@@ -822,6 +822,8 @@ static SDL_bool HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
{
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
ctx->last_packet = SDL_GetTicks();
ctx->report_sensors = SDL_FALSE;
@@ -961,7 +963,7 @@ static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
}
if (SDL_HIDAPI_LockRumble() < 0) {
if (SDL_HIDAPI_LockRumble() != 0) {
return -1;
}

View File

@@ -46,13 +46,13 @@ typedef struct SDL_HIDAPI_RumbleContext
SDL_atomic_t initialized;
SDL_atomic_t running;
SDL_Thread *thread;
SDL_mutex *lock;
SDL_sem *request_sem;
SDL_HIDAPI_RumbleRequest *requests_head;
SDL_HIDAPI_RumbleRequest *requests_tail;
} SDL_HIDAPI_RumbleContext;
static SDL_HIDAPI_RumbleContext rumble_context;
SDL_mutex *SDL_HIDAPI_rumble_lock;
static SDL_HIDAPI_RumbleContext rumble_context SDL_GUARDED_BY(SDL_HIDAPI_rumble_lock);
static int SDLCALL SDL_HIDAPI_RumbleThread(void *data)
{
@@ -65,7 +65,7 @@ static int SDLCALL SDL_HIDAPI_RumbleThread(void *data)
SDL_SemWait(ctx->request_sem);
SDL_LockMutex(ctx->lock);
SDL_LockMutex(SDL_HIDAPI_rumble_lock);
request = ctx->requests_tail;
if (request) {
if (request == ctx->requests_head) {
@@ -73,7 +73,7 @@ static int SDLCALL SDL_HIDAPI_RumbleThread(void *data)
}
ctx->requests_tail = request->prev;
}
SDL_UnlockMutex(ctx->lock);
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
if (request) {
SDL_LockMutex(request->device->dev_lock);
@@ -111,7 +111,7 @@ static void SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
ctx->thread = NULL;
}
SDL_LockMutex(ctx->lock);
SDL_LockMutex(SDL_HIDAPI_rumble_lock);
while (ctx->requests_tail) {
request = ctx->requests_tail;
if (request == ctx->requests_head) {
@@ -125,16 +125,16 @@ static void SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
SDL_free(request);
}
SDL_UnlockMutex(ctx->lock);
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
if (ctx->request_sem) {
SDL_DestroySemaphore(ctx->request_sem);
ctx->request_sem = NULL;
}
if (ctx->lock) {
SDL_DestroyMutex(ctx->lock);
ctx->lock = NULL;
if (SDL_HIDAPI_rumble_lock) {
SDL_DestroyMutex(SDL_HIDAPI_rumble_lock);
SDL_HIDAPI_rumble_lock = NULL;
}
SDL_AtomicSet(&ctx->initialized, SDL_FALSE);
@@ -142,8 +142,8 @@ static void SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
static int SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
{
ctx->lock = SDL_CreateMutex();
if (!ctx->lock) {
SDL_HIDAPI_rumble_lock = SDL_CreateMutex();
if (!SDL_HIDAPI_rumble_lock) {
SDL_HIDAPI_StopRumbleThread(ctx);
return -1;
}
@@ -173,7 +173,8 @@ int SDL_HIDAPI_LockRumble(void)
}
}
return SDL_LockMutex(ctx->lock);
SDL_LockMutex(SDL_HIDAPI_rumble_lock);
return 0;
}
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
@@ -241,9 +242,7 @@ int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const
void SDL_HIDAPI_UnlockRumble(void)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
SDL_UnlockMutex(ctx->lock);
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
}
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
@@ -256,7 +255,7 @@ int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size
return SDL_SetError("Tried to send rumble with invalid size");
}
if (SDL_HIDAPI_LockRumble() < 0) {
if (SDL_HIDAPI_LockRumble() != 0) {
return -1;
}

View File

@@ -25,12 +25,15 @@
/* Handle rumble on a separate thread so it doesn't block the application */
/* Advanced API */
int SDL_HIDAPI_LockRumble(void);
#ifdef SDL_THREAD_SAFETY_ANALYSIS
extern SDL_mutex *SDL_HIDAPI_rumble_lock;
#endif
int SDL_HIDAPI_LockRumble(void) SDL_TRY_ACQUIRE(0, SDL_HIDAPI_rumble_lock);
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size);
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size) SDL_RELEASE(SDL_HIDAPI_rumble_lock);
typedef void (*SDL_HIDAPI_RumbleSentCallback)(void *userdata);
int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size, SDL_HIDAPI_RumbleSentCallback callback, void *userdata);
void SDL_HIDAPI_UnlockRumble(void);
int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size, SDL_HIDAPI_RumbleSentCallback callback, void *userdata) SDL_RELEASE(SDL_HIDAPI_rumble_lock);
void SDL_HIDAPI_UnlockRumble(void) SDL_RELEASE(SDL_HIDAPI_rumble_lock);
/* Simple API, will replace any pending rumble with the new data */
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size);

View File

@@ -148,7 +148,7 @@ static int HIDAPI_DriverShield_SendCommand(SDL_HIDAPI_Device *device, Uint8 cmd,
return SDL_SetError("Command data exceeds HID report size");
}
if (SDL_HIDAPI_LockRumble() < 0) {
if (SDL_HIDAPI_LockRumble() != 0) {
return -1;
}
@@ -175,6 +175,8 @@ static SDL_bool HIDAPI_DriverShield_OpenJoystick(SDL_HIDAPI_Device *device, SDL_
{
SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->rumble_report_pending = SDL_FALSE;
ctx->rumble_update_pending = SDL_FALSE;
ctx->left_motor_amplitude = 0;

View File

@@ -96,6 +96,8 @@ static SDL_bool HIDAPI_DriverStadia_OpenJoystick(SDL_HIDAPI_Device *device, SDL_
{
SDL_DriverStadia_Context *ctx = (SDL_DriverStadia_Context *)device->context;
SDL_AssertJoysticksLocked();
SDL_zeroa(ctx->last_state);
/* Initialize the joystick capabilities */

View File

@@ -1012,6 +1012,8 @@ static SDL_bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_J
SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context;
float update_rate_in_hz = 0.0f;
SDL_AssertJoysticksLocked();
ctx->report_sensors = SDL_FALSE;
SDL_zero(ctx->m_assembler);
SDL_zero(ctx->m_state);

View File

@@ -323,7 +323,7 @@ static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int siz
return SDL_hid_write(ctx->device->dev, data, size);
#else
/* Use the rumble thread for general asynchronous writes */
if (SDL_HIDAPI_LockRumble() < 0) {
if (SDL_HIDAPI_LockRumble() != 0) {
return -1;
}
return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size);
@@ -1253,6 +1253,8 @@ static SDL_bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
Uint8 input_mode;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
ctx->m_bSyncWrite = SDL_TRUE;
@@ -1891,7 +1893,7 @@ static void HandleMiniControllerStateR(SDL_Joystick *joystick, SDL_DriverSwitch_
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
}
static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) SDL_NO_THREAD_SAFETY_ANALYSIS /* We unlock and lock the device lock to be able to change IMU state */
{
if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
if (ctx->device->parent || ctx->m_bVerticalMode) {

View File

@@ -221,7 +221,7 @@ static SDL_bool WriteOutput(SDL_DriverWii_Context *ctx, const Uint8 *data, int s
return SDL_hid_write(ctx->device->dev, data, size) >= 0;
} else {
/* Use the rumble thread for general asynchronous writes */
if (SDL_HIDAPI_LockRumble() < 0) {
if (SDL_HIDAPI_LockRumble() != 0) {
return SDL_FALSE;
}
return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size) >= 0;
@@ -770,6 +770,8 @@ static SDL_bool HIDAPI_DriverWii_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
{
SDL_DriverWii_Context *ctx = (SDL_DriverWii_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
InitializeExtension(ctx);

View File

@@ -176,6 +176,8 @@ static SDL_bool HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL
{
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->joystick = joystick;
SDL_zeroa(ctx->last_state);

View File

@@ -175,6 +175,8 @@ static SDL_bool HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SD
{
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
SDL_AssertJoysticksLocked();
SDL_zeroa(ctx->last_state);
/* Initialize player index (needed for setting LEDs) */

View File

@@ -234,7 +234,7 @@ static void SendAckIfNeeded(SDL_HIDAPI_Device *device, const Uint8 *data, int si
#ifdef DEBUG_XBOX_PROTOCOL
HIDAPI_DumpPacket("Xbox One sending ACK packet: size = %d", ack_packet, sizeof(ack_packet));
#endif
if (SDL_HIDAPI_LockRumble() < 0 ||
if (SDL_HIDAPI_LockRumble() != 0 ||
SDL_HIDAPI_SendRumbleAndUnlock(device, ack_packet, sizeof(ack_packet)) != sizeof(ack_packet)) {
SDL_SetError("Couldn't send ack packet");
}
@@ -254,7 +254,7 @@ static SDL_bool SendSerialRequest(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_C
* It will cancel the announce packet if sent before that, and will be
* ignored if sent during the negotiation.
*/
if (SDL_HIDAPI_LockRumble() < 0 ||
if (SDL_HIDAPI_LockRumble() != 0 ||
SDL_HIDAPI_SendRumbleAndUnlock(device, serial_packet, sizeof(serial_packet)) != sizeof(serial_packet)) {
SDL_SetError("Couldn't send serial packet");
return SDL_FALSE;
@@ -312,7 +312,7 @@ static SDL_bool SendControllerInit(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_
#endif
ctx->send_time = SDL_GetTicks();
if (SDL_HIDAPI_LockRumble() < 0 ||
if (SDL_HIDAPI_LockRumble() != 0 ||
SDL_HIDAPI_SendRumbleAndUnlock(device, init_packet, packet->size) != packet->size) {
SDL_SetError("Couldn't write Xbox One initialization packet");
return SDL_FALSE;
@@ -415,6 +415,8 @@ static SDL_bool HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL
{
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
SDL_AssertJoysticksLocked();
ctx->low_frequency_rumble = 0;
ctx->high_frequency_rumble = 0;
ctx->left_trigger_rumble = 0;
@@ -478,7 +480,7 @@ static int HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device)
/* We're no longer pending, even if we fail to send the rumble below */
ctx->rumble_pending = SDL_FALSE;
if (SDL_HIDAPI_LockRumble() < 0) {
if (SDL_HIDAPI_LockRumble() != 0) {
return -1;
}

View File

@@ -91,7 +91,7 @@ static int SDL_HIDAPI_numdrivers = 0;
static SDL_SpinLock SDL_HIDAPI_spinlock;
static SDL_bool SDL_HIDAPI_hints_changed = SDL_FALSE;
static Uint32 SDL_HIDAPI_change_count = 0;
static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
static SDL_HIDAPI_Device *SDL_HIDAPI_devices SDL_GUARDED_BY(SDL_joystick_lock);
static int SDL_HIDAPI_numjoysticks = 0;
static SDL_bool SDL_HIDAPI_combine_joycons = SDL_TRUE;
static SDL_bool initialized = SDL_FALSE;
@@ -264,6 +264,8 @@ static SDL_HIDAPI_Device *HIDAPI_GetDeviceByIndex(int device_index, SDL_Joystick
{
SDL_HIDAPI_Device *device;
SDL_AssertJoysticksLocked();
for (device = SDL_HIDAPI_devices; device; device = device->next) {
if (device->parent) {
continue;
@@ -285,6 +287,8 @@ static SDL_HIDAPI_Device *HIDAPI_GetJoystickByInfo(const char *path, Uint16 vend
{
SDL_HIDAPI_Device *device;
SDL_AssertJoysticksLocked();
for (device = SDL_HIDAPI_devices; device; device = device->next) {
if (device->vendor_id == vendor_id && device->product_id == product_id &&
SDL_strcmp(device->path, path) == 0) {
@@ -323,7 +327,7 @@ static void HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
SDL_UnlockMutex(device->dev_lock);
}
static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, SDL_bool *removed)
static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, SDL_bool *removed) SDL_NO_THREAD_SAFETY_ANALYSIS /* We unlock the joystick lock to be able to open the HID device on Android */
{
*removed = SDL_FALSE;
@@ -426,6 +430,8 @@ static void SDL_HIDAPI_UpdateDrivers(void)
SDL_HIDAPI_Device *device;
SDL_bool removed;
SDL_AssertJoysticksLocked();
SDL_HIDAPI_numdrivers = 0;
for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
@@ -571,6 +577,8 @@ HIDAPI_HasConnectedUSBDevice(const char *serial)
{
SDL_HIDAPI_Device *device;
SDL_AssertJoysticksLocked();
if (serial == NULL) {
return SDL_FALSE;
}
@@ -595,6 +603,8 @@ void HIDAPI_DisconnectBluetoothDevice(const char *serial)
{
SDL_HIDAPI_Device *device;
SDL_AssertJoysticksLocked();
if (serial == NULL) {
return;
}
@@ -622,6 +632,8 @@ HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
int i, j;
SDL_JoystickID joystickID;
SDL_AssertJoysticksLocked();
for (i = 0; i < device->num_children; ++i) {
SDL_HIDAPI_Device *child = device->children[i];
for (j = child->num_joysticks; j--;) {
@@ -717,6 +729,8 @@ static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *inf
SDL_HIDAPI_Device *curr, *last = NULL;
SDL_bool removed;
SDL_AssertJoysticksLocked();
for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
}
@@ -810,6 +824,8 @@ static void HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
SDL_HIDAPI_Device *curr, *last;
int i;
SDL_AssertJoysticksLocked();
#ifdef DEBUG_HIDAPI
SDL_Log("Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
#endif
@@ -849,6 +865,8 @@ static SDL_bool HIDAPI_CreateCombinedJoyCons()
SDL_HIDAPI_Device *device, *combined;
SDL_HIDAPI_Device *joycons[2] = { NULL, NULL };
SDL_AssertJoysticksLocked();
if (!SDL_HIDAPI_combine_joycons) {
return SDL_FALSE;
}
@@ -1160,6 +1178,8 @@ void HIDAPI_UpdateDevices(void)
{
SDL_HIDAPI_Device *device;
SDL_AssertJoysticksLocked();
/* Update the devices, which may change connected joysticks and send events */
/* Prepare the existing device list */
@@ -1262,6 +1282,8 @@ static int HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
struct joystick_hwdata *hwdata;
SDL_AssertJoysticksLocked();
if (device == NULL || !device->driver) {
/* This should never happen - validated before being called */
return SDL_SetError("Couldn't find HIDAPI device at index %d\n", device_index);
@@ -1299,6 +1321,8 @@ static int HIDAPI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_ru
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
@@ -1314,6 +1338,8 @@ static int HIDAPI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rum
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
@@ -1329,6 +1355,8 @@ static Uint32 HIDAPI_JoystickGetCapabilities(SDL_Joystick *joystick)
{
Uint32 result = 0;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
@@ -1342,6 +1370,8 @@ static int HIDAPI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green,
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
@@ -1357,6 +1387,8 @@ static int HIDAPI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, i
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
@@ -1372,6 +1404,8 @@ static int HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool ena
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
@@ -1388,8 +1422,10 @@ static void HIDAPI_JoystickUpdate(SDL_Joystick *joystick)
/* This is handled in SDL_HIDAPI_UpdateDevices() */
}
static void HIDAPI_JoystickClose(SDL_Joystick *joystick)
static void HIDAPI_JoystickClose(SDL_Joystick *joystick) SDL_NO_THREAD_SAFETY_ANALYSIS /* We unlock the device lock so rumble can complete */
{
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
int i;
@@ -1420,6 +1456,8 @@ static void HIDAPI_JoystickQuit(void)
{
int i;
SDL_AssertJoysticksLocked();
shutting_down = SDL_TRUE;
SDL_HIDAPI_QuitRumble();

View File

@@ -852,6 +852,8 @@ static int allocate_hatdata(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
joystick->hwdata->hats =
(struct hwdata_hat *)SDL_malloc(joystick->nhats *
sizeof(struct hwdata_hat));
@@ -869,6 +871,8 @@ static int allocate_balldata(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
joystick->hwdata->balls =
(struct hwdata_ball *)SDL_malloc(joystick->nballs *
sizeof(struct hwdata_ball));
@@ -925,6 +929,8 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd)
SDL_bool use_deadzones = SDL_GetHintBoolean(SDL_HINT_LINUX_JOYSTICK_DEADZONES, SDL_FALSE);
SDL_bool use_hat_deadzones = SDL_GetHintBoolean(SDL_HINT_LINUX_HAT_DEADZONES, SDL_TRUE);
SDL_AssertJoysticksLocked();
/* See if this device uses the new unified event API */
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
@@ -1132,6 +1138,8 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd)
on error. Returns -1 on error, 0 on success. */
static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
{
SDL_AssertJoysticksLocked();
joystick->hwdata->item = item;
joystick->hwdata->guid = item->guid;
joystick->hwdata->effect.id = -1;
@@ -1180,6 +1188,8 @@ static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
{
SDL_joylist_item *item = JoystickByDevIndex(device_index);
SDL_AssertJoysticksLocked();
if (item == NULL) {
return SDL_SetError("No such device");
}
@@ -1210,6 +1220,8 @@ static int LINUX_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rum
{
struct input_event event;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->ff_rumble) {
struct ff_effect *effect = &joystick->hwdata->effect;
@@ -1256,6 +1268,8 @@ static Uint32 LINUX_JoystickGetCapabilities(SDL_Joystick *joystick)
{
Uint32 result = 0;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->ff_rumble || joystick->hwdata->ff_sine) {
result |= SDL_JOYCAP_RUMBLE;
}
@@ -1280,7 +1294,7 @@ static int LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enab
static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
{
const int hatnum = stick->hwdata->hats_indices[hatidx];
int hatnum;
struct hwdata_hat *the_hat;
struct hat_axis_correct *correct;
const Uint8 position_map[3][3] = {
@@ -1289,6 +1303,9 @@ static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
{ SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN }
};
SDL_AssertJoysticksLocked();
hatnum = stick->hwdata->hats_indices[hatidx];
the_hat = &stick->hwdata->hats[hatnum];
correct = &stick->hwdata->hat_correct[hatidx];
/* Hopefully we detected any analog axes and left them as is rather than trying
@@ -1326,6 +1343,8 @@ static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
static void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)
{
SDL_AssertJoysticksLocked();
stick->hwdata->balls[ball].axis[axis] += value;
}
@@ -1333,6 +1352,8 @@ static int AxisCorrect(SDL_Joystick *joystick, int which, int value)
{
struct axis_correct *correct;
SDL_AssertJoysticksLocked();
correct = &joystick->hwdata->abs_correct[which];
if (correct->minimum != correct->maximum) {
if (correct->use_deadzones) {
@@ -1368,6 +1389,8 @@ static void PollAllValues(SDL_Joystick *joystick)
unsigned long keyinfo[NBITS(KEY_MAX)];
int i;
SDL_AssertJoysticksLocked();
/* Poll all axis */
for (i = ABS_X; i < ABS_MAX; i++) {
/* We don't need to test for digital hats here, they won't have has_abs[] set */
@@ -1424,6 +1447,8 @@ static void HandleInputEvents(SDL_Joystick *joystick)
struct input_event events[32];
int i, len, code, hat_index;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->fresh) {
PollAllValues(joystick);
joystick->hwdata->fresh = SDL_FALSE;
@@ -1515,6 +1540,8 @@ static void HandleClassicEvents(SDL_Joystick *joystick)
struct js_event events[32];
int i, len, code, hat_index;
SDL_AssertJoysticksLocked();
joystick->hwdata->fresh = SDL_FALSE;
while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
@@ -1557,6 +1584,8 @@ static void LINUX_JoystickUpdate(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->m_bSteamController) {
SDL_UpdateSteamController(joystick);
return;
@@ -1585,6 +1614,8 @@ static void LINUX_JoystickUpdate(SDL_Joystick *joystick)
/* Function to close a joystick after use */
static void LINUX_JoystickClose(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
if (joystick->hwdata->effect.id >= 0) {
ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
@@ -1645,6 +1676,8 @@ static SDL_bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
SDL_joylist_item *item = JoystickByDevIndex(device_index);
unsigned int mapped;
SDL_AssertJoysticksLocked();
if (item->checked_mapping) {
if (item->mapping) {
SDL_memcpy(out, item->mapping, sizeof(*out));

View File

@@ -29,32 +29,36 @@
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
static joystick_hwdata *g_VJoys = NULL;
static joystick_hwdata *g_VJoys SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static joystick_hwdata *VIRTUAL_HWDataForIndex(int device_index)
{
joystick_hwdata *vjoy = g_VJoys;
while (vjoy) {
joystick_hwdata *vjoy;
SDL_AssertJoysticksLocked();
for (vjoy = g_VJoys; vjoy; vjoy = vjoy->next) {
if (device_index == 0) {
break;
}
--device_index;
vjoy = vjoy->next;
}
return vjoy;
}
static void VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
{
joystick_hwdata *cur = g_VJoys;
joystick_hwdata *cur;
joystick_hwdata *prev = NULL;
SDL_AssertJoysticksLocked();
if (hwdata == NULL) {
return;
}
/* Remove hwdata from SDL-global list */
while (cur) {
for (cur = g_VJoys; cur; prev = cur, cur = cur->next) {
if (hwdata == cur) {
if (prev) {
prev->next = cur->next;
@@ -63,8 +67,6 @@ static void VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
}
break;
}
prev = cur;
cur = cur->next;
}
if (hwdata->joystick) {
@@ -98,6 +100,8 @@ int SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc)
int axis_triggerleft = -1;
int axis_triggerright = -1;
SDL_AssertJoysticksLocked();
if (desc == NULL) {
return SDL_InvalidParamError("desc");
}
@@ -329,11 +333,13 @@ static int VIRTUAL_JoystickInit(void)
static int VIRTUAL_JoystickGetCount(void)
{
joystick_hwdata *cur;
int count = 0;
joystick_hwdata *cur = g_VJoys;
while (cur) {
SDL_AssertJoysticksLocked();
for (cur = g_VJoys; cur; cur = cur->next) {
++count;
cur = cur->next;
}
return count;
}
@@ -392,7 +398,11 @@ static SDL_JoystickID VIRTUAL_JoystickGetDeviceInstanceID(int device_index)
static int VIRTUAL_JoystickOpen(SDL_Joystick *joystick, int device_index)
{
joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
joystick_hwdata *hwdata;
SDL_AssertJoysticksLocked();
hwdata = VIRTUAL_HWDataForIndex(device_index);
if (hwdata == NULL) {
return SDL_SetError("No such device");
}
@@ -409,6 +419,8 @@ static int VIRTUAL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_r
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
joystick_hwdata *hwdata = joystick->hwdata;
if (hwdata->desc.Rumble) {
@@ -427,6 +439,8 @@ static int VIRTUAL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_ru
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
joystick_hwdata *hwdata = joystick->hwdata;
if (hwdata->desc.RumbleTriggers) {
@@ -443,9 +457,12 @@ static int VIRTUAL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_ru
static Uint32 VIRTUAL_JoystickGetCapabilities(SDL_Joystick *joystick)
{
joystick_hwdata *hwdata = joystick->hwdata;
joystick_hwdata *hwdata;
Uint32 caps = 0;
SDL_AssertJoysticksLocked();
hwdata = joystick->hwdata;
if (hwdata) {
if (hwdata->desc.Rumble) {
caps |= SDL_JOYCAP_RUMBLE;
@@ -464,6 +481,8 @@ static int VIRTUAL_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
joystick_hwdata *hwdata = joystick->hwdata;
if (hwdata->desc.SetLED) {
@@ -482,6 +501,8 @@ static int VIRTUAL_JoystickSendEffect(SDL_Joystick *joystick, const void *data,
{
int result;
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
joystick_hwdata *hwdata = joystick->hwdata;
if (hwdata->desc.SendEffect) {
@@ -506,6 +527,8 @@ static void VIRTUAL_JoystickUpdate(SDL_Joystick *joystick)
joystick_hwdata *hwdata;
int i;
SDL_AssertJoysticksLocked();
if (joystick == NULL) {
return;
}
@@ -532,6 +555,8 @@ static void VIRTUAL_JoystickUpdate(SDL_Joystick *joystick)
static void VIRTUAL_JoystickClose(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
joystick_hwdata *hwdata = joystick->hwdata;
hwdata->joystick = NULL;
@@ -541,6 +566,8 @@ static void VIRTUAL_JoystickClose(SDL_Joystick *joystick)
static void VIRTUAL_JoystickQuit(void)
{
SDL_AssertJoysticksLocked();
while (g_VJoys) {
VIRTUAL_FreeHWData(g_VJoys);
}