Get the correct USB VID/PID information for /dev/input/js* devices

This commit is contained in:
Sam Lantinga 2021-11-11 07:03:30 -08:00
parent bd92a95f22
commit dc9de1e2bd
3 changed files with 103 additions and 44 deletions

View file

@ -19,7 +19,7 @@
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
/* /*
* To list the properties of a device, try something like: * To list the properties of a device, try something like:
* udevadm info -a -n snd/hwC0D0 (for a sound card) * udevadm info -a -n snd/hwC0D0 (for a sound card)
* udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc) * udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc)
@ -115,23 +115,23 @@ int
SDL_UDEV_Init(void) SDL_UDEV_Init(void)
{ {
int retval = 0; int retval = 0;
if (_this == NULL) { if (_this == NULL) {
_this = (SDL_UDEV_PrivateData *) SDL_calloc(1, sizeof(*_this)); _this = (SDL_UDEV_PrivateData *) SDL_calloc(1, sizeof(*_this));
if(_this == NULL) { if(_this == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
retval = SDL_UDEV_LoadLibrary(); retval = SDL_UDEV_LoadLibrary();
if (retval < 0) { if (retval < 0) {
SDL_UDEV_Quit(); SDL_UDEV_Quit();
return retval; return retval;
} }
/* Set up udev monitoring /* Set up udev monitoring
* Listen for input devices (mouse, keyboard, joystick, etc) and sound devices * Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
*/ */
_this->udev = _this->syms.udev_new(); _this->udev = _this->syms.udev_new();
if (_this->udev == NULL) { if (_this->udev == NULL) {
SDL_UDEV_Quit(); SDL_UDEV_Quit();
@ -143,18 +143,18 @@ SDL_UDEV_Init(void)
SDL_UDEV_Quit(); SDL_UDEV_Quit();
return SDL_SetError("udev_monitor_new_from_netlink() failed"); return SDL_SetError("udev_monitor_new_from_netlink() failed");
} }
_this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL); _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
_this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL); _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
_this->syms.udev_monitor_enable_receiving(_this->udev_mon); _this->syms.udev_monitor_enable_receiving(_this->udev_mon);
/* Do an initial scan of existing devices */ /* Do an initial scan of existing devices */
SDL_UDEV_Scan(); SDL_UDEV_Scan();
} }
_this->ref_count += 1; _this->ref_count += 1;
return retval; return retval;
} }
@ -162,15 +162,15 @@ void
SDL_UDEV_Quit(void) SDL_UDEV_Quit(void)
{ {
SDL_UDEV_CallbackList *item; SDL_UDEV_CallbackList *item;
if (_this == NULL) { if (_this == NULL) {
return; return;
} }
_this->ref_count -= 1; _this->ref_count -= 1;
if (_this->ref_count < 1) { if (_this->ref_count < 1) {
if (_this->udev_mon != NULL) { if (_this->udev_mon != NULL) {
_this->syms.udev_monitor_unref(_this->udev_mon); _this->syms.udev_monitor_unref(_this->udev_mon);
_this->udev_mon = NULL; _this->udev_mon = NULL;
@ -179,14 +179,14 @@ SDL_UDEV_Quit(void)
_this->syms.udev_unref(_this->udev); _this->syms.udev_unref(_this->udev);
_this->udev = NULL; _this->udev = NULL;
} }
/* Remove existing devices */ /* Remove existing devices */
while (_this->first != NULL) { while (_this->first != NULL) {
item = _this->first; item = _this->first;
_this->first = _this->first->next; _this->first = _this->first->next;
SDL_free(item); SDL_free(item);
} }
SDL_UDEV_UnloadLibrary(); SDL_UDEV_UnloadLibrary();
SDL_free(_this); SDL_free(_this);
_this = NULL; _this = NULL;
@ -198,22 +198,22 @@ SDL_UDEV_Scan(void)
{ {
struct udev_enumerate *enumerate = NULL; struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *devs = NULL; struct udev_list_entry *devs = NULL;
struct udev_list_entry *item = NULL; struct udev_list_entry *item = NULL;
if (_this == NULL) { if (_this == NULL) {
return; return;
} }
enumerate = _this->syms.udev_enumerate_new(_this->udev); enumerate = _this->syms.udev_enumerate_new(_this->udev);
if (enumerate == NULL) { if (enumerate == NULL) {
SDL_UDEV_Quit(); SDL_UDEV_Quit();
SDL_SetError("udev_enumerate_new() failed"); SDL_SetError("udev_enumerate_new() failed");
return; return;
} }
_this->syms.udev_enumerate_add_match_subsystem(enumerate, "input"); _this->syms.udev_enumerate_add_match_subsystem(enumerate, "input");
_this->syms.udev_enumerate_add_match_subsystem(enumerate, "sound"); _this->syms.udev_enumerate_add_match_subsystem(enumerate, "sound");
_this->syms.udev_enumerate_scan_devices(enumerate); _this->syms.udev_enumerate_scan_devices(enumerate);
devs = _this->syms.udev_enumerate_get_list_entry(enumerate); devs = _this->syms.udev_enumerate_get_list_entry(enumerate);
for (item = devs; item; item = _this->syms.udev_list_entry_get_next(item)) { for (item = devs; item; item = _this->syms.udev_list_entry_get_next(item)) {
@ -228,6 +228,62 @@ SDL_UDEV_Scan(void)
_this->syms.udev_enumerate_unref(enumerate); _this->syms.udev_enumerate_unref(enumerate);
} }
SDL_bool
SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version)
{
struct udev_enumerate *enumerate = NULL;
struct udev_list_entry *devs = NULL;
struct udev_list_entry *item = NULL;
SDL_bool found = SDL_FALSE;
if (_this == NULL) {
return SDL_FALSE;
}
enumerate = _this->syms.udev_enumerate_new(_this->udev);
if (enumerate == NULL) {
SDL_SetError("udev_enumerate_new() failed");
return SDL_FALSE;
}
_this->syms.udev_enumerate_scan_devices(enumerate);
devs = _this->syms.udev_enumerate_get_list_entry(enumerate);
for (item = devs; item && !found; item = _this->syms.udev_list_entry_get_next(item)) {
const char *path = _this->syms.udev_list_entry_get_name(item);
struct udev_device *dev = _this->syms.udev_device_new_from_syspath(_this->udev, path);
if (dev != NULL) {
const char *val = NULL;
const char *existing_path;
existing_path = _this->syms.udev_device_get_devnode(dev);
if (existing_path && SDL_strcmp(device_path, existing_path) == 0) {
found = SDL_TRUE;
val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID");
if (val != NULL) {
*vendor = (Uint16)SDL_strtol(val, NULL, 16);
}
val = _this->syms.udev_device_get_property_value(dev, "ID_MODEL_ID");
if (val != NULL) {
*product = (Uint16)SDL_strtol(val, NULL, 16);
}
val = _this->syms.udev_device_get_property_value(dev, "ID_REVISION");
if (val != NULL) {
*version = (Uint16)SDL_strtol(val, NULL, 16);
}
}
_this->syms.udev_device_unref(dev);
}
}
_this->syms.udev_enumerate_unref(enumerate);
return found;
}
void void
SDL_UDEV_UnloadLibrary(void) SDL_UDEV_UnloadLibrary(void)
@ -235,7 +291,7 @@ SDL_UDEV_UnloadLibrary(void)
if (_this == NULL) { if (_this == NULL) {
return; return;
} }
if (_this->udev_handle != NULL) { if (_this->udev_handle != NULL) {
SDL_UnloadObject(_this->udev_handle); SDL_UnloadObject(_this->udev_handle);
_this->udev_handle = NULL; _this->udev_handle = NULL;
@ -246,11 +302,11 @@ int
SDL_UDEV_LoadLibrary(void) SDL_UDEV_LoadLibrary(void)
{ {
int retval = 0, i; int retval = 0, i;
if (_this == NULL) { if (_this == NULL) {
return SDL_SetError("UDEV not initialized"); return SDL_SetError("UDEV not initialized");
} }
/* See if there is a udev library already loaded */ /* See if there is a udev library already loaded */
if (SDL_UDEV_load_syms() == 0) { if (SDL_UDEV_load_syms() == 0) {
return 0; return 0;
@ -282,7 +338,7 @@ SDL_UDEV_LoadLibrary(void)
} }
} }
} }
if (_this->udev_handle == NULL) { if (_this->udev_handle == NULL) {
retval = -1; retval = -1;
/* Don't call SDL_SetError(): SDL_LoadObject already did. */ /* Don't call SDL_SetError(): SDL_LoadObject already did. */
@ -352,26 +408,26 @@ guess_device_class(struct udev_device *dev)
&bitmask_rel[0]); &bitmask_rel[0]);
} }
static void static void
device_event(SDL_UDEV_deviceevent type, struct udev_device *dev) device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
{ {
const char *subsystem; const char *subsystem;
const char *val = NULL; const char *val = NULL;
int devclass = 0; int devclass = 0;
const char *path; const char *path;
SDL_UDEV_CallbackList *item; SDL_UDEV_CallbackList *item;
path = _this->syms.udev_device_get_devnode(dev); path = _this->syms.udev_device_get_devnode(dev);
if (path == NULL) { if (path == NULL) {
return; return;
} }
subsystem = _this->syms.udev_device_get_subsystem(dev); subsystem = _this->syms.udev_device_get_subsystem(dev);
if (SDL_strcmp(subsystem, "sound") == 0) { if (SDL_strcmp(subsystem, "sound") == 0) {
devclass = SDL_UDEV_DEVICE_SOUND; devclass = SDL_UDEV_DEVICE_SOUND;
} else if (SDL_strcmp(subsystem, "input") == 0) { } else if (SDL_strcmp(subsystem, "input") == 0) {
/* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */ /* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"); val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
if (val != NULL && SDL_strcmp(val, "1") == 0 ) { if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
devclass |= SDL_UDEV_DEVICE_JOYSTICK; devclass |= SDL_UDEV_DEVICE_JOYSTICK;
@ -381,13 +437,13 @@ device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE) && if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE) &&
val != NULL && SDL_strcmp(val, "1") == 0 ) { val != NULL && SDL_strcmp(val, "1") == 0 ) {
devclass |= SDL_UDEV_DEVICE_JOYSTICK; devclass |= SDL_UDEV_DEVICE_JOYSTICK;
} }
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_MOUSE"); val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
if (val != NULL && SDL_strcmp(val, "1") == 0 ) { if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
devclass |= SDL_UDEV_DEVICE_MOUSE; devclass |= SDL_UDEV_DEVICE_MOUSE;
} }
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"); val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
if (val != NULL && SDL_strcmp(val, "1") == 0 ) { if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
@ -396,7 +452,7 @@ device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
/* The undocumented rule is: /* The undocumented rule is:
- All devices with keys get ID_INPUT_KEY - All devices with keys get ID_INPUT_KEY
- From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD - From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD
Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183 Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
*/ */
val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEY"); val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEY");
@ -425,14 +481,14 @@ device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
} else { } else {
return; return;
} }
/* Process callbacks */ /* Process callbacks */
for (item = _this->first; item != NULL; item = item->next) { for (item = _this->first; item != NULL; item = item->next) {
item->callback(type, devclass, path); item->callback(type, devclass, path);
} }
} }
void void
SDL_UDEV_Poll(void) SDL_UDEV_Poll(void)
{ {
struct udev_device *dev = NULL; struct udev_device *dev = NULL;
@ -456,12 +512,12 @@ SDL_UDEV_Poll(void)
device_event(SDL_UDEV_DEVICEREMOVED, dev); device_event(SDL_UDEV_DEVICEREMOVED, dev);
} }
} }
_this->syms.udev_device_unref(dev); _this->syms.udev_device_unref(dev);
} }
} }
int int
SDL_UDEV_AddCallback(SDL_UDEV_Callback cb) SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
{ {
SDL_UDEV_CallbackList *item; SDL_UDEV_CallbackList *item;
@ -469,7 +525,7 @@ SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
if (item == NULL) { if (item == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
item->callback = cb; item->callback = cb;
if (_this->last == NULL) { if (_this->last == NULL) {
@ -478,11 +534,11 @@ SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
_this->last->next = item; _this->last->next = item;
_this->last = item; _this->last = item;
} }
return 1; return 1;
} }
void void
SDL_UDEV_DelCallback(SDL_UDEV_Callback cb) SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
{ {
SDL_UDEV_CallbackList *item; SDL_UDEV_CallbackList *item;
@ -505,7 +561,6 @@ SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
} }
prev = item; prev = item;
} }
} }
const SDL_UDEV_Symbols * const SDL_UDEV_Symbols *

View file

@ -101,6 +101,7 @@ extern void SDL_UDEV_UnloadLibrary(void);
extern int SDL_UDEV_LoadLibrary(void); extern int SDL_UDEV_LoadLibrary(void);
extern void SDL_UDEV_Poll(void); extern void SDL_UDEV_Poll(void);
extern void SDL_UDEV_Scan(void); extern void SDL_UDEV_Scan(void);
extern SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version);
extern int SDL_UDEV_AddCallback(SDL_UDEV_Callback cb); extern int SDL_UDEV_AddCallback(SDL_UDEV_Callback cb);
extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb); extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb);
extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void); extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void);

View file

@ -178,7 +178,7 @@ GuessIsJoystick(int fd)
} }
static int static int
IsJoystick(int fd, char **name_return, SDL_JoystickGUID *guid) IsJoystick(const char *path, int fd, char **name_return, SDL_JoystickGUID *guid)
{ {
struct input_id inpid; struct input_id inpid;
Uint16 *guid16 = (Uint16 *)guid->data; Uint16 *guid16 = (Uint16 *)guid->data;
@ -187,6 +187,9 @@ IsJoystick(int fd, char **name_return, SDL_JoystickGUID *guid)
if (ioctl(fd, JSIOCGNAME(sizeof(product_string)), product_string) >= 0) { if (ioctl(fd, JSIOCGNAME(sizeof(product_string)), product_string) >= 0) {
SDL_zero(inpid); SDL_zero(inpid);
#if SDL_USE_LIBUDEV
SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version);
#endif
} else { } else {
/* When udev is enabled we only get joystick devices here, so there's no need to test them */ /* When udev is enabled we only get joystick devices here, so there's no need to test them */
if (enumeration_method != ENUMERATION_LIBUDEV && !GuessIsJoystick(fd)) { if (enumeration_method != ENUMERATION_LIBUDEV && !GuessIsJoystick(fd)) {
@ -326,7 +329,7 @@ MaybeAddDevice(const char *path)
SDL_Log("Checking %s\n", path); SDL_Log("Checking %s\n", path);
#endif #endif
isstick = IsJoystick(fd, &name, &guid); isstick = IsJoystick(path, fd, &name, &guid);
close(fd); close(fd);
if (!isstick) { if (!isstick) {
return -1; return -1;