Improved code to get the name and guid for joysticks on OpenBSD and NetBSD

Also dynamically allocate joysticks to reduce static memory usage
This commit is contained in:
Sam Lantinga 2022-08-28 18:17:50 -07:00
parent 32700294e2
commit a9d3935a84

View file

@ -30,6 +30,7 @@
*/ */
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
@ -87,9 +88,6 @@
#ifdef __OpenBSD__ #ifdef __OpenBSD__
#define DEV_USB 3 /* needed to get GUID from USB_GET_DEVICEINFO */
#define GUID_LEN 32 /* GUID string has length 32 */
#define HUG_DPAD_UP 0x90 #define HUG_DPAD_UP 0x90
#define HUG_DPAD_DOWN 0x91 #define HUG_DPAD_DOWN 0x91
#define HUG_DPAD_RIGHT 0x92 #define HUG_DPAD_RIGHT 0x92
@ -191,16 +189,29 @@ struct joystick_hwdata
BSDJOY_UHID, /* uhid(4) */ BSDJOY_UHID, /* uhid(4) */
BSDJOY_JOY /* joy(4) */ BSDJOY_JOY /* joy(4) */
} type; } type;
int naxes;
int nbuttons;
int nhats;
struct report_desc *repdesc; struct report_desc *repdesc;
struct report inreport; struct report inreport;
int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,.. */ int axis_map[JOYAXE_count]; /* map present JOYAXE_* to 0,1,.. */
}; };
static char *joynames[MAX_JOYS]; /* A linked list of available joysticks */
static char *joydevnames[MAX_JOYS]; typedef struct SDL_joylist_item
#ifdef __OpenBSD__ {
static char joyguids[MAX_JOYS][GUID_LEN]; SDL_JoystickID device_instance;
#endif char *path; /* "/dev/uhid0" or whatever */
char *name; /* "SideWinder 3D Pro" or whatever */
SDL_JoystickGUID guid;
dev_t devnum;
struct SDL_joylist_item *next;
} SDL_joylist_item;
static SDL_joylist_item *SDL_joylist = NULL;
static SDL_joylist_item *SDL_joylist_tail = NULL;
static int numjoysticks = 0;
static int report_alloc(struct report *, struct report_desc *, int); static int report_alloc(struct report *, struct report_desc *, int);
static void report_free(struct report *); static void report_free(struct report *);
@ -216,104 +227,6 @@ static void report_free(struct report *);
#define REP_BUF_DATA(rep) ((rep)->buf->data) #define REP_BUF_DATA(rep) ((rep)->buf->data)
#endif #endif
static int numjoysticks = 0;
static int BSD_JoystickOpen(SDL_Joystick *joy, int device_index);
static void BSD_JoystickClose(SDL_Joystick *joy);
static int
BSD_JoystickInit(void)
{
char s[16];
int i, fd;
numjoysticks = 0;
SDL_memset(joynames, 0, sizeof(joynames));
SDL_memset(joydevnames, 0, sizeof(joydevnames));
#ifdef __OpenBSD__
SDL_memset(joyguids, 0, sizeof(char) * MAX_JOYS * GUID_LEN);
#endif
for (i = 0; i < MAX_UHID_JOYS; i++) {
SDL_Joystick nj;
#if defined(__OpenBSD__) && (OpenBSD >= 202105)
SDL_snprintf(s, SDL_arraysize(s), "/dev/ujoy/%d", i);
#else
SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
#endif
joynames[numjoysticks] = SDL_strdup(s);
if (BSD_JoystickOpen(&nj, numjoysticks) == 0) {
BSD_JoystickClose(&nj);
numjoysticks++;
} else {
SDL_free(joynames[numjoysticks]);
joynames[numjoysticks] = NULL;
}
}
#ifdef SUPPORT_JOY_GAMEPORT
for (i = 0; i < MAX_JOY_JOYS; i++) {
SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
fd = open(s, O_RDONLY | O_CLOEXEC);
if (fd != -1) {
joynames[numjoysticks++] = SDL_strdup(s);
close(fd);
}
}
#endif /* SUPPORT_JOY_GAMEPORT */
/* Read the default USB HID usage table. */
hid_init(NULL);
return (numjoysticks);
}
static int
BSD_JoystickGetCount(void)
{
return numjoysticks;
}
static void
BSD_JoystickDetect(void)
{
}
static const char *
BSD_JoystickGetDeviceName(int device_index)
{
if (joydevnames[device_index] != NULL) {
return joydevnames[device_index];
}
return joynames[device_index];
}
static const char *
BSD_JoystickGetDevicePath(int device_index)
{
return joynames[device_index];
}
static int
BSD_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
}
static void
BSD_JoystickSetDevicePlayerIndex(int device_index, int player_index)
{
}
/* Function to perform the mapping from device index to the instance id for this index */
static SDL_JoystickID
BSD_JoystickGetDeviceInstanceID(int device_index)
{
return device_index;
}
static int static int
usage_to_joyaxe(unsigned usage) usage_to_joyaxe(unsigned usage)
@ -350,69 +263,62 @@ usage_to_joyaxe(unsigned usage)
return joyaxe; return joyaxe;
} }
static unsigned static void
hatval_to_sdl(Sint32 hatval) FreeJoylistItem(SDL_joylist_item *item)
{ {
static const unsigned hat_dir_map[8] = { SDL_free(item->path);
SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN, SDL_free(item->name);
SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP SDL_free(item);
};
unsigned result;
if ((hatval & 7) == hatval)
result = hat_dir_map[hatval];
else
result = SDL_HAT_CENTERED;
return result;
} }
static void
static int FreeHwData(struct joystick_hwdata *hw)
BSD_JoystickOpen(SDL_Joystick *joy, int device_index) {
if (hw->type == BSDJOY_UHID) {
report_free(&hw->inreport);
if (hw->repdesc) {
hid_dispose_report_desc(hw->repdesc);
}
}
close(hw->fd);
SDL_free(hw);
}
static struct joystick_hwdata *
CreateHwData(const char *path)
{ {
char *path = joynames[device_index];
struct joystick_hwdata *hw; struct joystick_hwdata *hw;
struct hid_item hitem; struct hid_item hitem;
struct hid_data *hdata; struct hid_data *hdata;
struct report *rep = NULL; struct report *rep = NULL;
#if defined(__NetBSD__)
usb_device_descriptor_t udd;
struct usb_string_desc usd;
#endif
int fd; int fd;
int i; int i;
#ifdef __OpenBSD__
struct usb_device_info di;
#endif
fd = open(path, O_RDONLY | O_CLOEXEC); fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd == -1) { if (fd == -1) {
return SDL_SetError("%s: %s", path, strerror(errno)); SDL_SetError("%s: %s", path, strerror(errno));
return NULL;
} }
joy->instance_id = device_index;
hw = (struct joystick_hwdata *) hw = (struct joystick_hwdata *)
SDL_malloc(sizeof(struct joystick_hwdata)); SDL_calloc(1, sizeof(struct joystick_hwdata));
if (hw == NULL) { if (hw == NULL) {
close(fd); close(fd);
return SDL_OutOfMemory(); SDL_OutOfMemory();
return NULL;
} }
joy->hwdata = hw;
hw->fd = fd; hw->fd = fd;
#ifdef SUPPORT_JOY_GAMEPORT #ifdef SUPPORT_JOY_GAMEPORT
if (SDL_strncmp(path, "/dev/joy", 8) == 0) { if (SDL_strncmp(path, "/dev/joy", 8) == 0) {
hw->type = BSDJOY_JOY; hw->type = BSDJOY_JOY;
joy->naxes = 2; hw->naxes = 2;
joy->nbuttons = 2; hw->nbuttons = 2;
joy->nhats = 0;
joy->nballs = 0;
joydevnames[device_index] = SDL_strdup("Gameport joystick");
goto usbend;
} else } else
#endif #endif
{ {
hw->type = BSDJOY_UHID; hw->type = BSDJOY_UHID;
}
{ {
int ax; int ax;
for (ax = 0; ax < JOYAXE_count; ax++) for (ax = 0; ax < JOYAXE_count; ax++)
@ -433,47 +339,6 @@ BSD_JoystickOpen(SDL_Joystick *joy, int device_index)
#endif #endif
rep->rid = -1; /* XXX */ rep->rid = -1; /* XXX */
} }
#if defined(__NetBSD__)
if (ioctl(fd, USB_GET_DEVICE_DESC, &udd) == -1)
goto desc_failed;
/* Get default language */
usd.usd_string_index = USB_LANGUAGE_TABLE;
usd.usd_language_id = 0;
if (ioctl(fd, USB_GET_STRING_DESC, &usd) == -1 || usd.usd_desc.bLength < 4) {
usd.usd_language_id = 0;
} else {
usd.usd_language_id = UGETW(usd.usd_desc.bString[0]);
}
usd.usd_string_index = udd.iProduct;
if (ioctl(fd, USB_GET_STRING_DESC, &usd) == 0) {
char str[128];
char *new_name = NULL;
int i;
for (i = 0; i < (usd.usd_desc.bLength >> 1) - 1 && i < sizeof(str) - 1; i++) {
str[i] = UGETW(usd.usd_desc.bString[i]);
}
str[i] = '\0';
SDL_asprintf(&new_name, "%s @ %s", str, path);
if (new_name != NULL) {
SDL_free(joydevnames[numjoysticks]);
joydevnames[numjoysticks] = new_name;
}
}
desc_failed:
#endif
#if defined(__OpenBSD__)
if (ioctl(fd, USB_GET_DEVICEINFO, &di) != -1) {
SDL_snprintf(joyguids[numjoysticks],
SDL_arraysize(joyguids[device_index]),
"%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000",
DEV_USB & 0xFF, DEV_USB >> 8,
di.udi_vendorNo & 0xFF, di.udi_vendorNo >> 8,
di.udi_productNo & 0xFF, di.udi_productNo >> 8,
di.udi_releaseNo & 0xFF, di.udi_releaseNo >> 8);
}
#endif
if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) { if (report_alloc(rep, hw->repdesc, REPORT_INPUT) < 0) {
goto usberr; goto usberr;
} }
@ -491,32 +356,11 @@ desc_failed:
SDL_SetError("%s: Cannot start HID parser", path); SDL_SetError("%s: Cannot start HID parser", path);
goto usberr; goto usberr;
} }
joy->naxes = 0;
joy->nbuttons = 0;
joy->nhats = 0;
joy->nballs = 0;
for (i = 0; i < JOYAXE_count; i++) for (i = 0; i < JOYAXE_count; i++)
hw->axis_map[i] = -1; hw->axis_map[i] = -1;
while (hid_get_item(hdata, &hitem) > 0) { while (hid_get_item(hdata, &hitem) > 0) {
char *sp;
const char *s;
switch (hitem.kind) { switch (hitem.kind) {
case hid_collection:
switch (HID_PAGE(hitem.usage)) {
case HUP_GENERIC_DESKTOP:
switch (HID_USAGE(hitem.usage)) {
case HUG_JOYSTICK:
case HUG_GAME_PAD:
s = hid_usage_in_page(hitem.usage);
sp = SDL_malloc(SDL_strlen(s) + 5);
SDL_snprintf(sp, SDL_strlen(s) + 5, "%s (%d)",
s, device_index);
joydevnames[device_index] = sp;
}
}
break;
case hid_input: case hid_input:
switch (HID_PAGE(hitem.usage)) { switch (HID_PAGE(hitem.usage)) {
case HUP_GENERIC_DESKTOP: case HUP_GENERIC_DESKTOP:
@ -530,12 +374,12 @@ desc_failed:
|| usage == HUG_DPAD_UP || usage == HUG_DPAD_UP
#endif #endif
) { ) {
joy->nhats++; hw->nhats++;
} }
break; break;
} }
case HUP_BUTTON: case HUP_BUTTON:
joy->nbuttons++; hw->nbuttons++;
break; break;
default: default:
break; break;
@ -548,29 +392,254 @@ desc_failed:
hid_end_parse(hdata); hid_end_parse(hdata);
for (i = 0; i < JOYAXE_count; i++) for (i = 0; i < JOYAXE_count; i++)
if (hw->axis_map[i] > 0) if (hw->axis_map[i] > 0)
hw->axis_map[i] = joy->naxes++; hw->axis_map[i] = hw->naxes++;
if (joy->naxes == 0 && joy->nbuttons == 0 && joy->nhats == 0 && joy->nballs == 0) { if (hw->naxes == 0 && hw->nbuttons == 0 && hw->nhats == 0) {
SDL_SetError("%s: Not a joystick, ignoring", path); SDL_SetError("%s: Not a joystick, ignoring", path);
goto usberr; goto usberr;
} }
}
usbend:
/* The poll blocks the event thread. */ /* The poll blocks the event thread. */
fcntl(fd, F_SETFL, O_NONBLOCK); fcntl(fd, F_SETFL, O_NONBLOCK);
#ifdef __NetBSD__ #ifdef __NetBSD__
/* Flush pending events */ /* Flush pending events */
if (rep) { if (rep) {
while (read(joy->hwdata->fd, REP_BUF_DATA(rep), rep->size) == rep->size) while (read(fd, REP_BUF_DATA(rep), rep->size) == rep->size)
; ;
} }
#endif #endif
return (0); return hw;
usberr: usberr:
close(hw->fd); FreeHwData(hw);
SDL_free(hw); return NULL;
return (-1); }
static int
MaybeAddDevice(const char *path)
{
struct stat sb;
char *name = NULL;
SDL_JoystickGUID guid;
SDL_joylist_item *item;
struct joystick_hwdata *hw;
if (path == NULL) {
return -1;
}
if (stat(path, &sb) == -1) {
return -1;
}
/* Check to make sure it's not already in list. */
for (item = SDL_joylist; item != NULL; item = item->next) {
if (sb.st_rdev == item->devnum) {
return -1; /* already have this one */
}
}
hw = CreateHwData(path);
if (!hw) {
return -1;
}
if (hw->type == BSDJOY_JOY) {
name = SDL_strdup("Gameport joystick");
guid = SDL_CreateJoystickGUIDForName(name);
} else {
#ifdef USB_GET_DEVICEINFO
struct usb_device_info di;
if (ioctl(hw->fd, USB_GET_DEVICEINFO, &di) != -1) {
name = SDL_CreateJoystickName(di.udi_vendorNo, di.udi_productNo, di.udi_vendor, di.udi_product);
guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, name, 0, 0);
#ifdef SDL_JOYSTICK_HIDAPI
if (HIDAPI_IsDevicePresent(di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, name)) {
/* The HIDAPI driver is taking care of this device */
SDL_free(name);
FreeHwData(hw);
return -1;
}
#endif
if (SDL_ShouldIgnoreJoystick(name, guid)) {
SDL_free(name);
FreeHwData(hw);
return -1;
}
}
#endif /* USB_GET_DEVICEINFO */
}
if (!name) {
name = SDL_strdup(path);
guid = SDL_CreateJoystickGUIDForName(name);
}
FreeHwData(hw);
item = (SDL_joylist_item *) SDL_calloc(1, sizeof (SDL_joylist_item));
if (item == NULL) {
SDL_free(name);
return -1;
}
item->devnum = sb.st_rdev;
item->path = SDL_strdup(path);
item->name = name;
item->guid = guid;
if ((item->path == NULL) || (item->name == NULL)) {
FreeJoylistItem(item);
return -1;
}
item->device_instance = SDL_GetNextJoystickInstanceID();
if (SDL_joylist_tail == NULL) {
SDL_joylist = SDL_joylist_tail = item;
} else {
SDL_joylist_tail->next = item;
SDL_joylist_tail = item;
}
/* Need to increment the joystick count before we post the event */
++numjoysticks;
SDL_PrivateJoystickAdded(item->device_instance);
return numjoysticks;
}
static int
BSD_JoystickInit(void)
{
char s[16];
int i;
for (i = 0; i < MAX_UHID_JOYS; i++) {
#if defined(__OpenBSD__) && (OpenBSD >= 202105)
SDL_snprintf(s, SDL_arraysize(s), "/dev/ujoy/%d", i);
#else
SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
#endif
MaybeAddDevice(s);
}
#ifdef SUPPORT_JOY_GAMEPORT
for (i = 0; i < MAX_JOY_JOYS; i++) {
SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
MaybeAddDevice(s);
}
#endif /* SUPPORT_JOY_GAMEPORT */
/* Read the default USB HID usage table. */
hid_init(NULL);
return numjoysticks;
}
static int
BSD_JoystickGetCount(void)
{
return numjoysticks;
}
static void
BSD_JoystickDetect(void)
{
}
static SDL_joylist_item *
JoystickByDevIndex(int device_index)
{
SDL_joylist_item *item = SDL_joylist;
if ((device_index < 0) || (device_index >= numjoysticks)) {
return NULL;
}
while (device_index > 0) {
SDL_assert(item != NULL);
device_index--;
item = item->next;
}
return item;
}
static const char *
BSD_JoystickGetDeviceName(int device_index)
{
return JoystickByDevIndex(device_index)->name;
}
static const char *
BSD_JoystickGetDevicePath(int device_index)
{
return JoystickByDevIndex(device_index)->path;
}
static int
BSD_JoystickGetDevicePlayerIndex(int device_index)
{
return -1;
}
static void
BSD_JoystickSetDevicePlayerIndex(int device_index, int player_index)
{
}
static SDL_JoystickGUID
BSD_JoystickGetDeviceGUID(int device_index)
{
return JoystickByDevIndex(device_index)->guid;
}
/* Function to perform the mapping from device index to the instance id for this index */
static SDL_JoystickID
BSD_JoystickGetDeviceInstanceID(int device_index)
{
return JoystickByDevIndex(device_index)->device_instance;
}
static unsigned
hatval_to_sdl(Sint32 hatval)
{
static const unsigned hat_dir_map[8] = {
SDL_HAT_UP, SDL_HAT_RIGHTUP, SDL_HAT_RIGHT, SDL_HAT_RIGHTDOWN,
SDL_HAT_DOWN, SDL_HAT_LEFTDOWN, SDL_HAT_LEFT, SDL_HAT_LEFTUP
};
unsigned result;
if ((hatval & 7) == hatval)
result = hat_dir_map[hatval];
else
result = SDL_HAT_CENTERED;
return result;
}
static int
BSD_JoystickOpen(SDL_Joystick *joy, int device_index)
{
SDL_joylist_item *item = JoystickByDevIndex(device_index);
struct joystick_hwdata *hw;
if (item == NULL) {
return SDL_SetError("No such device");
}
hw = CreateHwData(item->path);
if (!hw) {
return -1;
}
joy->instance_id = item->device_instance;
joy->hwdata = hw;
joy->naxes = hw->naxes;
joy->nbuttons = hw->nbuttons;
joy->nhats = hw->nhats;
return 0;
} }
static void static void
@ -728,39 +797,26 @@ BSD_JoystickUpdate(SDL_Joystick *joy)
static void static void
BSD_JoystickClose(SDL_Joystick *joy) BSD_JoystickClose(SDL_Joystick *joy)
{ {
if (joy->hwdata->type == BSDJOY_UHID) { if (joy->hwdata) {
report_free(&joy->hwdata->inreport); FreeHwData(joy->hwdata);
hid_dispose_report_desc(joy->hwdata->repdesc); joy->hwdata = NULL;
} }
close(joy->hwdata->fd);
SDL_free(joy->hwdata);
} }
static void static void
BSD_JoystickQuit(void) BSD_JoystickQuit(void)
{ {
int i; SDL_joylist_item *item = NULL;
SDL_joylist_item *next = NULL;
for (i = 0; i < MAX_JOYS; i++) { for (item = SDL_joylist; item; item = next) {
SDL_free(joynames[i]); next = item->next;
SDL_free(joydevnames[i]); FreeJoylistItem(item);
} }
return; SDL_joylist = SDL_joylist_tail = NULL;
}
static SDL_JoystickGUID numjoysticks = 0;
BSD_JoystickGetDeviceGUID(int device_index)
{
#ifdef __OpenBSD__
SDL_JoystickGUID guid;
guid = SDL_JoystickGetGUIDFromString(joyguids[device_index]);
return guid;
#else
/* the GUID is just the name for now */
const char *name = BSD_JoystickGetDeviceName(device_index);
return SDL_CreateJoystickGUIDForName(name);
#endif
} }
static int static int