hidapi/libusb: maintain in-memory cache of vendor/product strings The get_usb_string call is rather expensive on some USB devices, so we cache the vendor/product strings for future lookups (e.g. when hid_enumerate is invoked again later). This way, we only need to ask libusb for strings for devices we haven't seen since before we started. Signed-off-by: Steven Noonan <steven@valvesoftware.com> Signed-off-by: Sam Lantinga <slouken@libsdl.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c
index 3fd95e0..1175da9 100644
--- a/src/hidapi/libusb/hid.c
+++ b/src/hidapi/libusb/hid.c
@@ -440,6 +440,94 @@ err:
return str;
}
+struct usb_string_cache_entry {
+ uint16_t vid;
+ uint16_t pid;
+ wchar_t *vendor;
+ wchar_t *product;
+};
+
+static struct usb_string_cache_entry *usb_string_cache = NULL;
+static size_t usb_string_cache_size = 0;
+static size_t usb_string_cache_insert_pos = 0;
+
+static int usb_string_cache_grow()
+{
+ struct usb_string_cache_entry *new_cache;
+ size_t allocSize;
+ size_t new_cache_size;
+
+ new_cache_size = usb_string_cache_size + 8;
+ allocSize = sizeof(struct usb_string_cache_entry) * new_cache_size;
+ new_cache = (struct usb_string_cache_entry *)realloc(usb_string_cache, allocSize);
+ if (!new_cache)
+ return -1;
+
+ usb_string_cache = new_cache;
+ usb_string_cache_size = new_cache_size;
+
+ return 0;
+}
+
+static void usb_string_cache_destroy()
+{
+ size_t i;
+ for (i = 0; i < usb_string_cache_insert_pos; i++) {
+ free(usb_string_cache[i].vendor);
+ free(usb_string_cache[i].product);
+ }
+ free(usb_string_cache);
+
+ usb_string_cache = NULL;
+ usb_string_cache_size = 0;
+}
+
+static struct usb_string_cache_entry *usb_string_cache_insert()
+{
+ struct usb_string_cache_entry *new_entry = NULL;
+ if (usb_string_cache_insert_pos >= usb_string_cache_size) {
+ if (usb_string_cache_grow() < 0)
+ return NULL;
+ }
+ new_entry = &usb_string_cache[usb_string_cache_insert_pos];
+ usb_string_cache_insert_pos++;
+ return new_entry;
+}
+
+static const struct usb_string_cache_entry *usb_string_cache_find(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle)
+{
+ struct usb_string_cache_entry *entry = NULL;
+ size_t i;
+
+ /* Search for existing string cache entry */
+ for (i = 0; i < usb_string_cache_insert_pos; i++) {
+ entry = &usb_string_cache[i];
+ if (entry->vid != desc->idVendor)
+ continue;
+ if (entry->pid != desc->idProduct)
+ continue;
+ return entry;
+ }
+
+ /* Not found, create one. */
+ entry = usb_string_cache_insert();
+ if (!entry)
+ return NULL;
+
+ entry->vid = desc->idVendor;
+ entry->pid = desc->idProduct;
+ if (desc->iManufacturer > 0)
+ entry->vendor = get_usb_string(handle, desc->iManufacturer);
+ else
+ entry->vendor = NULL;
+ if (desc->iProduct > 0)
+ entry->product = get_usb_string(handle, desc->iProduct);
+ else
+ entry->product = NULL;
+
+ return entry;
+}
+
static char *make_path(libusb_device *dev, int interface_number)
{
char str[64];
@@ -473,6 +561,8 @@ int HID_API_EXPORT hid_init(void)
int HID_API_EXPORT hid_exit(void)
{
+ usb_string_cache_destroy();
+
if (usb_context) {
libusb_exit(usb_context);
usb_context = NULL;
@@ -617,6 +707,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
if (res >= 0) {
struct hid_device_info *tmp;
+ const struct usb_string_cache_entry *string_cache;
/* VID/PID match. Create the record. */
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
@@ -638,12 +729,20 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
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);
+ if (dev_vid && dev_pid) {
+ string_cache = usb_string_cache_find(&desc, handle);
+ if (string_cache) {
+ cur_dev->manufacturer_string = wcsdup(string_cache->vendor);
+ cur_dev->product_string = wcsdup(string_cache->product);
+ }
+ } else {
+ 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
{