Skip to content

Commit

Permalink
feat: Add hid_get_device_info
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Jun 20, 2022
1 parent 59e84ca commit 94aea4f
Show file tree
Hide file tree
Showing 6 changed files with 507 additions and 385 deletions.
12 changes: 12 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,18 @@ extern "C" {
*/
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);

/** @brief Get The stuct #hid_device_info from a HID device.
@ingroup API
@param dev A device handle returned from hid_open().
@returns
This function returns a pointer to the struct #hid_device_info
for this hid_device, or NULL in the case of failure.
This struct is valid until the device is closed with hid_close()
*/
struct hid_device_info HID_API_EXPORT *HID_API_CALL hid_get_device_info(hid_device *dev);

/** @brief Get a string from a HID device, based on its string index.
@ingroup API
Expand Down
27 changes: 19 additions & 8 deletions hidtest/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@
#endif
//

void print_device(struct hid_device_info *cur_dev) {
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
printf("\n");
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
printf(" Product: %ls\n", cur_dev->product_string);
printf(" Release: %hx\n", cur_dev->release_number);
printf(" Interface: %d\n", cur_dev->interface_number);
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
printf("\n");
}

int main(int argc, char* argv[])
{
(void)argc;
Expand Down Expand Up @@ -85,14 +96,7 @@ int main(int argc, char* argv[])
devs = hid_enumerate(0x0, 0x0);
cur_dev = devs;
while (cur_dev) {
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
printf("\n");
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
printf(" Product: %ls\n", cur_dev->product_string);
printf(" Release: %hx\n", cur_dev->release_number);
printf(" Interface: %d\n", cur_dev->interface_number);
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
printf("\n");
print_device(cur_dev);
cur_dev = cur_dev->next;
}
hid_free_enumeration(devs);
Expand Down Expand Up @@ -134,6 +138,13 @@ int main(int argc, char* argv[])
printf("Serial Number String: (%d) %ls", wstr[0], wstr);
printf("\n");

struct hid_device_info* info = hid_get_device_info(handle);
if (info == NULL) {
printf("Unable to get device info\n");
} else {
print_device(info);
}

// Read Indexed String 1
wstr[0] = 0x0000;
res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
Expand Down
228 changes: 131 additions & 97 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ struct hid_device_ {
int manufacturer_index;
int product_index;
int serial_index;
struct hid_device_info* device_info;

/* Whether blocking reads are used */
int blocking; /* boolean */
Expand Down Expand Up @@ -210,6 +211,8 @@ static void free_hid_device(hid_device *dev)
pthread_cond_destroy(&dev->condition);
pthread_mutex_destroy(&dev->mutex);

hid_free_enumeration(dev->device_info);

/* Free the device itself */
free(dev);
}
Expand Down Expand Up @@ -548,6 +551,95 @@ int HID_API_EXPORT hid_exit(void)
return 0;
}


static void fill_device_info_for_device(libusb_device_handle *handle, struct hid_device_info * cur_dev, struct libusb_device_descriptor *desc)
{
/* VID/PID */
cur_dev->vendor_id = desc->idVendor;
cur_dev->product_id = desc->idProduct;

/* Release Number */
cur_dev->release_number = desc->bcdDevice;

if (handle) {
/* Serial Number */
if (desc->iSerialNumber > 0)
cur_dev->serial_number =
get_usb_string(handle, desc->iSerialNumber);

/* Manufacturer and Product strings */
if (desc->iManufacturer > 0)
cur_dev->manufacturer_string =
get_usb_string(handle, desc->iManufacturer);
if (desc->iProduct > 0)
cur_dev->product_string =
get_usb_string(handle, desc->iProduct);

#ifdef INVASIVE_GET_USAGE
{
/*
This section is removed because it is too
invasive on the system. Getting a Usage Page
and Usage requires parsing the HID Report
descriptor. Getting a HID Report descriptor
involves claiming the interface. Claiming the
interface involves detaching the kernel driver.
Detaching the kernel driver is hard on the system
because it will unclaim interfaces (if another
app has them claimed) and the re-attachment of
the driver will sometimes change /dev entry names.
It is for these reasons that this section is
#if 0. For composite devices, use the interface
field in the hid_device_info struct to distinguish
between interfaces. */
unsigned char data[256];
#ifdef DETACH_KERNEL_DRIVER
int detached = 0;
/* Usage Page and Usage */
res = libusb_kernel_driver_active(handle, interface_num);
if (res == 1) {
res = libusb_detach_kernel_driver(handle, interface_num);
if (res < 0)
LOG("Couldn't detach kernel driver, even though a kernel driver was attached.\n");
else
detached = 1;
}
#endif
res = libusb_claim_interface(handle, interface_num);
if (res >= 0) {
/* Get the HID Report Descriptor. */
res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000);
if (res >= 0) {
unsigned short page=0, usage=0;
/* Parse the usage and usage page
out of the report descriptor. */
get_usage(data, res, &page, &usage);
cur_dev->usage_page = page;
cur_dev->usage = usage;
}
else
LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res);

/* Release the interface */
res = libusb_release_interface(handle, interface_num);
if (res < 0)
LOG("Can't release the interface.\n");
}
else
LOG("Can't claim interface %d\n", res);
#ifdef DETACH_KERNEL_DRIVER
/* Re-attach kernel driver if necessary. */
if (detached) {
res = libusb_attach_kernel_driver(handle, interface_num);
if (res < 0)
LOG("Couldn't re-attach kernel driver.\n");
}
#endif
}
#endif /* INVASIVE_GET_USAGE */
}
}

struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
libusb_device **devs;
Expand Down Expand Up @@ -608,102 +700,23 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,

res = libusb_open(dev, &handle);

if (res >= 0) {
#ifdef __ANDROID__
/* There is (a potential) libusb Android backend, in which
device descriptor is not accurate up until the device is opened.
https://github.com/libusb/libusb/pull/874#discussion_r632801373
A workaround is to re-read the descriptor again.
Even if it is not going to be accepted into libusb master,
having it here won't do any harm, since reading the device descriptor
is as cheap as copy 18 bytes of data. */
libusb_get_device_descriptor(dev, &desc);
#endif

/* Serial Number */
if (desc.iSerialNumber > 0)
cur_dev->serial_number =
get_usb_string(handle, desc.iSerialNumber);

/* Manufacturer and Product strings */
if (desc.iManufacturer > 0)
cur_dev->manufacturer_string =
get_usb_string(handle, desc.iManufacturer);
if (desc.iProduct > 0)
cur_dev->product_string =
get_usb_string(handle, desc.iProduct);

#ifdef INVASIVE_GET_USAGE
{
/*
This section is removed because it is too
invasive on the system. Getting a Usage Page
and Usage requires parsing the HID Report
descriptor. Getting a HID Report descriptor
involves claiming the interface. Claiming the
interface involves detaching the kernel driver.
Detaching the kernel driver is hard on the system
because it will unclaim interfaces (if another
app has them claimed) and the re-attachment of
the driver will sometimes change /dev entry names.
It is for these reasons that this section is
#if 0. For composite devices, use the interface
field in the hid_device_info struct to distinguish
between interfaces. */
unsigned char data[256];
#ifdef DETACH_KERNEL_DRIVER
int detached = 0;
/* Usage Page and Usage */
res = libusb_kernel_driver_active(handle, interface_num);
if (res == 1) {
res = libusb_detach_kernel_driver(handle, interface_num);
if (res < 0)
LOG("Couldn't detach kernel driver, even though a kernel driver was attached.\n");
else
detached = 1;
#ifdef __ANDROID__
if (handle) {
/* There is (a potential) libusb Android backend, in which
device descriptor is not accurate up until the device is opened.
https://github.com/libusb/libusb/pull/874#discussion_r632801373
A workaround is to re-read the descriptor again.
Even if it is not going to be accepted into libusb master,
having it here won't do any harm, since reading the device descriptor
is as cheap as copy 18 bytes of data. */
libusb_get_device_descriptor(dev, &desc);
}
#endif
res = libusb_claim_interface(handle, interface_num);
if (res >= 0) {
/* Get the HID Report Descriptor. */
res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000);
if (res >= 0) {
unsigned short page=0, usage=0;
/* Parse the usage and usage page
out of the report descriptor. */
get_usage(data, res, &page, &usage);
cur_dev->usage_page = page;
cur_dev->usage = usage;
}
else
LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res);
#endif

/* Release the interface */
res = libusb_release_interface(handle, interface_num);
if (res < 0)
LOG("Can't release the interface.\n");
}
else
LOG("Can't claim interface %d\n", res);
#ifdef DETACH_KERNEL_DRIVER
/* Re-attach kernel driver if necessary. */
if (detached) {
res = libusb_attach_kernel_driver(handle, interface_num);
if (res < 0)
LOG("Couldn't re-attach kernel driver.\n");
}
#endif
}
#endif /* INVASIVE_GET_USAGE */
fill_device_info_for_device(handle, cur_dev, &desc);

if (res >= 0)
libusb_close(handle);
}
/* VID/PID */
cur_dev->vendor_id = dev_vid;
cur_dev->product_id = dev_pid;

/* Release Number */
cur_dev->release_number = desc.bcdDevice;

/* Interface Number */
cur_dev->interface_number = interface_num;
Expand Down Expand Up @@ -909,12 +922,17 @@ static void *read_thread(void *param)
}


static int hidapi_initialize_device(hid_device *dev, const struct libusb_interface_descriptor *intf_desc)
static int hidapi_initialize_device(hid_device *dev, char* path, const struct libusb_interface_descriptor *intf_desc)
{
int i =0;
int res = 0;
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(libusb_get_device(dev->device_handle), &desc);
libusb_get_device_descriptor(libusb_get_device(dev->device_handle), &desc);

dev->device_info = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
dev->device_info->next = NULL;
dev->device_info->path = path;
fill_device_info_for_device(dev->device_handle, dev->device_info, &desc);

#ifdef DETACH_KERNEL_DRIVER
/* Detach the kernel driver, but only if the
Expand Down Expand Up @@ -1028,11 +1046,14 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
free(dev_path);
break;
}
good_open = hidapi_initialize_device(dev, intf_desc);
good_open = hidapi_initialize_device(dev, dev_path, intf_desc);
if (!good_open)
libusb_close(dev->device_handle);

// dev_path is owned by dev now
} else {
free(dev_path);
}
free(dev_path);
}
}
}
Expand Down Expand Up @@ -1062,6 +1083,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys
const struct libusb_interface_descriptor *selected_intf_desc = NULL;
int res = 0;
int j = 0, k = 0;
char* path;

if(hid_init() < 0)
return NULL;
Expand Down Expand Up @@ -1107,7 +1129,8 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys
goto err;
}

if (!hidapi_initialize_device(dev, selected_intf_desc))
path = make_path(libusb_get_device(dev->device_handle), selected_intf_desc->bInterfaceNumber, conf_desc->bConfigurationValue);
if (!hidapi_initialize_device(dev, path, selected_intf_desc))
goto err;

return dev;
Expand All @@ -1118,6 +1141,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys
if (dev->device_handle)
libusb_close(dev->device_handle);
free_hid_device(dev);
// If path is set it, ownership was given to hidapi_initialize_device
#else
(void)sys_dev;
(void)interface_num;
Expand Down Expand Up @@ -1455,6 +1479,16 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s
return hid_get_indexed_string(dev, dev->serial_index, string, maxlen);
}

struct hid_device_info *HID_API_EXPORT_CALL hid_get_device_info(hid_device *dev) {
if (!dev->device_info)
{
// register_device_error(dev, "NULL device/info");
return NULL;
}

return dev->device_info;
}

int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
wchar_t *str;
Expand Down
Loading

0 comments on commit 94aea4f

Please sign in to comment.