Skip to content

Commit 90cd9b2

Browse files
committed
hidapi/linux: fixed doubled and missing report ID for BLE devices
Signed-off-by: Sam Lantinga <slouken@libsdl.org>
1 parent e71655e commit 90cd9b2

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

src/hidapi/linux/hid.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
struct hid_device_ {
7272
int device_handle;
7373
int blocking;
74+
int needs_ble_hack;
7475
wchar_t *last_error_str;
7576
struct hid_device_info* device_info;
7677
};
@@ -564,6 +565,69 @@ static int parse_uevent_info(const char *uevent, unsigned *bus_type,
564565
return (found_id && found_name && found_serial);
565566
}
566567

568+
static int is_BLE(hid_device *dev)
569+
{
570+
struct udev *udev;
571+
struct udev_device *udev_dev, *hid_dev;
572+
struct stat s;
573+
int ret;
574+
575+
/* Create the udev object */
576+
udev = udev_new();
577+
if (!udev) {
578+
printf("Can't create udev\n");
579+
return -1;
580+
}
581+
582+
/* Get the dev_t (major/minor numbers) from the file handle. */
583+
if (fstat(dev->device_handle, &s) < 0) {
584+
udev_unref(udev);
585+
return -1;
586+
}
587+
588+
/* Open a udev device from the dev_t. 'c' means character device. */
589+
ret = 0;
590+
udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
591+
if (udev_dev) {
592+
hid_dev = udev_device_get_parent_with_subsystem_devtype(
593+
udev_dev,
594+
"hid",
595+
NULL);
596+
if (hid_dev) {
597+
unsigned short dev_vid = 0;
598+
unsigned short dev_pid = 0;
599+
unsigned bus_type = 0;
600+
char *serial_number_utf8 = NULL;
601+
char *product_name_utf8 = NULL;
602+
603+
parse_uevent_info(
604+
udev_device_get_sysattr_value(hid_dev, "uevent"),
605+
&bus_type,
606+
&dev_vid,
607+
&dev_pid,
608+
&serial_number_utf8,
609+
&product_name_utf8);
610+
free(serial_number_utf8);
611+
free(product_name_utf8);
612+
613+
if (bus_type == BUS_BLUETOOTH) {
614+
/* Right now the Steam Controller is the only BLE device that we send feature reports to */
615+
if (dev_vid == 0x28de /* Valve */) {
616+
ret = 1;
617+
}
618+
}
619+
620+
/* hid_dev doesn't need to be (and can't be) unref'd.
621+
I'm not sure why, but it'll throw double-free() errors. */
622+
}
623+
udev_device_unref(udev_dev);
624+
}
625+
626+
udev_unref(udev);
627+
628+
return ret;
629+
}
630+
567631

568632
static struct hid_device_info * create_device_info_for_device(struct udev_device *raw_dev)
569633
{
@@ -1030,6 +1094,8 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
10301094
return NULL;
10311095
}
10321096

1097+
dev->needs_ble_hack = (is_BLE(dev) == 1);
1098+
10331099
return dev;
10341100
}
10351101
else {
@@ -1143,12 +1209,26 @@ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char
11431209
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
11441210
{
11451211
int res;
1212+
unsigned char report = data[0];
11461213

11471214
register_device_error(dev, NULL);
11481215

11491216
res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
11501217
if (res < 0)
11511218
register_device_error_format(dev, "ioctl (GFEATURE): %s", strerror(errno));
1219+
else if (dev->needs_ble_hack) {
1220+
/* Versions of BlueZ before 5.56 don't include the report in the data,
1221+
* and versions of BlueZ >= 5.56 include 2 copies of the report.
1222+
* We'll fix it so that there is a single copy of the report in both cases
1223+
*/
1224+
if (data[0] == report && data[1] == report) {
1225+
memmove(&data[0], &data[1], res);
1226+
} else if (data[0] != report) {
1227+
memmove(&data[1], &data[0], res);
1228+
data[0] = report;
1229+
++res;
1230+
}
1231+
}
11521232

11531233
return res;
11541234
}

0 commit comments

Comments
 (0)