hidapi: Add support for Wii U/Switch USB GameCube controller adapter.

Note that a single USB device is responsible for all 4 joysticks, so a large
rewrite of the DeviceDriver functions was necessary to allow a single device to
produce multiple joysticks.
This commit is contained in:
Ethan Lee 2019-03-12 20:27:54 -04:00
parent 938a5508d1
commit c528615626
13 changed files with 724 additions and 176 deletions

View file

@ -50,18 +50,10 @@
#endif
#endif
struct joystick_hwdata
{
SDL_HIDAPI_DeviceDriver *driver;
void *context;
SDL_mutex *mutex;
hid_device *dev;
};
typedef struct _SDL_HIDAPI_Device
{
SDL_JoystickID instance_id;
SDL_HIDAPI_DriverData devdata;
SDL_mutex *mutex;
char *name;
char *path;
Uint16 vendor_id;
@ -95,6 +87,9 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
&SDL_HIDAPI_DriverXboxOne,
#endif
#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE
&SDL_HIDAPI_DriverGameCube,
#endif
};
static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
static int SDL_HIDAPI_numjoysticks = 0;
@ -393,6 +388,36 @@ HIDAPI_ShutdownDiscovery()
#endif
}
static void
HIDAPI_InitDriver(SDL_HIDAPI_Device *device)
{
device->devdata.device = hid_open_path(device->path, 0);
if (!device->devdata.device) {
SDL_SetError("Couldn't open HID device %s", device->path);
device->driver = NULL;
} else {
device->driver->InitDriver(
&device->devdata,
device->vendor_id,
device->product_id,
&SDL_HIDAPI_numjoysticks
);
device->mutex = SDL_CreateMutex();
}
}
static void
HIDAPI_QuitDriver(SDL_HIDAPI_Device *device, SDL_bool send_event)
{
device->driver->QuitDriver(
&device->devdata,
send_event,
&SDL_HIDAPI_numjoysticks
);
hid_close(device->devdata.device);
SDL_DestroyMutex(device->mutex);
device->driver = NULL;
}
const char *
HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
@ -605,15 +630,17 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
}
static SDL_HIDAPI_Device *
HIDAPI_GetJoystickByIndex(int device_index)
HIDAPI_GetDeviceByIndex(int device_index)
{
SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
int joysticks;
while (device) {
if (device->driver) {
if (device_index == 0) {
joysticks = device->driver->NumJoysticks(&device->devdata);
if (device_index < joysticks) {
break;
}
--device_index;
device_index -= joysticks;
}
device = device->next;
}
@ -660,20 +687,12 @@ SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldVal
while (device) {
if (device->driver) {
if (!device->driver->enabled) {
device->driver = NULL;
--SDL_HIDAPI_numjoysticks;
SDL_PrivateJoystickRemoved(device->instance_id);
HIDAPI_QuitDriver(device, SDL_TRUE);
}
} else {
device->driver = HIDAPI_GetDeviceDriver(device);
if (device->driver) {
device->instance_id = SDL_GetNextJoystickInstanceID();
++SDL_HIDAPI_numjoysticks;
SDL_PrivateJoystickAdded(device->instance_id);
HIDAPI_InitDriver(device);
}
}
device = device->next;
@ -723,7 +742,6 @@ HIDAPI_AddDevice(struct hid_device_info *info)
if (!device) {
return;
}
device->instance_id = -1;
device->seen = SDL_TRUE;
device->vendor_id = info->vendor_id;
device->product_id = info->product_id;
@ -818,12 +836,8 @@ HIDAPI_AddDevice(struct hid_device_info *info)
}
if (device->driver) {
/* It's a joystick! */
device->instance_id = SDL_GetNextJoystickInstanceID();
++SDL_HIDAPI_numjoysticks;
SDL_PrivateJoystickAdded(device->instance_id);
/* It's a joystick device! */
HIDAPI_InitDriver(device);
}
}
@ -840,11 +854,8 @@ HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
SDL_HIDAPI_devices = curr->next;
}
if (device->driver && send_event) {
/* Need to decrement the joystick count before we post the event */
--SDL_HIDAPI_numjoysticks;
SDL_PrivateJoystickRemoved(device->instance_id);
if (device->driver) {
HIDAPI_QuitDriver(device, send_event);
}
SDL_free(device->name);
@ -931,7 +942,7 @@ HIDAPI_JoystickDetect(void)
static const char *
HIDAPI_JoystickGetDeviceName(int device_index)
{
return HIDAPI_GetJoystickByIndex(device_index)->name;
return HIDAPI_GetDeviceByIndex(device_index)->name;
}
static int
@ -943,89 +954,61 @@ HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
static SDL_JoystickGUID
HIDAPI_JoystickGetDeviceGUID(int device_index)
{
return HIDAPI_GetJoystickByIndex(device_index)->guid;
return HIDAPI_GetDeviceByIndex(device_index)->guid;
}
static SDL_JoystickID
HIDAPI_JoystickGetDeviceInstanceID(int device_index)
{
return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
int joysticks;
while (device) {
if (device->driver) {
joysticks = device->driver->NumJoysticks(&device->devdata);
if (device_index < joysticks) {
break;
}
device_index -= joysticks;
}
device = device->next;
}
return device->driver->InstanceIDForIndex(&device->devdata, device_index);
}
static int
HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
struct joystick_hwdata *hwdata;
SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index);
hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
if (!hwdata) {
return SDL_OutOfMemory();
}
hwdata->driver = device->driver;
hwdata->dev = hid_open_path(device->path, 0);
if (!hwdata->dev) {
SDL_free(hwdata);
return SDL_SetError("Couldn't open HID device %s", device->path);
}
hwdata->mutex = SDL_CreateMutex();
if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
hid_close(hwdata->dev);
SDL_free(hwdata);
if (!device->driver->OpenJoystick(&device->devdata, joystick)) {
return -1;
}
joystick->hwdata = hwdata;
joystick->hwdata = (struct joystick_hwdata *)device;
return 0;
}
static int
HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
{
struct joystick_hwdata *hwdata = joystick->hwdata;
SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
SDL_HIDAPI_Device *device = (SDL_HIDAPI_Device *)joystick->hwdata;
int result;
SDL_LockMutex(hwdata->mutex);
result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
SDL_UnlockMutex(hwdata->mutex);
SDL_LockMutex(device->mutex);
result = device->driver->Rumble(&device->devdata, joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
SDL_UnlockMutex(device->mutex);
return result;
}
static void
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
{
struct joystick_hwdata *hwdata = joystick->hwdata;
SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
SDL_bool succeeded;
SDL_LockMutex(hwdata->mutex);
succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
SDL_UnlockMutex(hwdata->mutex);
if (!succeeded) {
SDL_HIDAPI_Device *device;
for (device = SDL_HIDAPI_devices; device; device = device->next) {
if (device->instance_id == joystick->instance_id) {
HIDAPI_DelDevice(device, SDL_TRUE);
break;
}
}
}
/* No-op, all updates are done in SDL_HIDAPI_UpdateDevices */
}
static void
HIDAPI_JoystickClose(SDL_Joystick * joystick)
{
struct joystick_hwdata *hwdata = joystick->hwdata;
SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
driver->Quit(joystick, hwdata->dev, hwdata->context);
hid_close(hwdata->dev);
SDL_DestroyMutex(hwdata->mutex);
SDL_free(hwdata);
joystick->hwdata = NULL;
}
@ -1050,6 +1033,30 @@ HIDAPI_JoystickQuit(void)
hid_exit();
}
void
SDL_HIDAPI_UpdateDevices(void)
{
SDL_HIDAPI_Device *next, *device = SDL_HIDAPI_devices;
SDL_bool succeeded;
while (device) {
if (device->driver) {
SDL_LockMutex(device->mutex);
succeeded = device->driver->UpdateDriver(&device->devdata, &SDL_HIDAPI_numjoysticks);
SDL_UnlockMutex(device->mutex);
if (!succeeded) {
next = device->next;
HIDAPI_DelDevice(device, SDL_TRUE);
device = next;
} else {
device = device->next;
}
} else {
device = device->next;
}
}
}
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
{
HIDAPI_JoystickInit,