Nathan Hjelm | 57256dd | 2013-05-15 16:11:22 +0200 | [diff] [blame] | 1 | /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 2 | /* |
hjelmn@cs.unm.edu | 1eff220 | 2014-01-08 23:50:34 +0000 | [diff] [blame] | 3 | * USB descriptor handling functions for libusb |
Pete Batard | 791b747 | 2012-04-03 13:40:39 +0100 | [diff] [blame] | 4 | * Copyright © 2007 Daniel Drake <dsd@gentoo.org> |
| 5 | * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 6 | * |
| 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * This library is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with this library; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ |
| 21 | |
Daniel Drake | e9364d7 | 2008-01-04 00:40:49 +0000 | [diff] [blame] | 22 | #include "libusbi.h" |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 23 | |
Chris Dickens | aaff15d | 2020-01-22 15:53:17 -0800 | [diff] [blame] | 24 | #include <string.h> |
| 25 | |
Chris Dickens | e1fcd8a | 2020-04-17 12:21:46 -0700 | [diff] [blame] | 26 | #define DESC_HEADER_LENGTH 2 |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 27 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 28 | /** @defgroup libusb_desc USB descriptors |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 29 | * This page details how to examine the various standard USB descriptors |
| 30 | * for detected devices |
| 31 | */ |
| 32 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 33 | #define READ_LE16(p) ((uint16_t) \ |
| 34 | (((uint16_t)((p)[1]) << 8) | \ |
| 35 | ((uint16_t)((p)[0])))) |
| 36 | |
| 37 | #define READ_LE32(p) ((uint32_t) \ |
| 38 | (((uint32_t)((p)[3]) << 24) | \ |
| 39 | ((uint32_t)((p)[2]) << 16) | \ |
| 40 | ((uint32_t)((p)[1]) << 8) | \ |
| 41 | ((uint32_t)((p)[0])))) |
| 42 | |
| 43 | static void parse_descriptor(const void *source, const char *descriptor, void *dest) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 44 | { |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 45 | const uint8_t *sp = source; |
| 46 | uint8_t *dp = dest; |
| 47 | char field_type; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 48 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 49 | while (*descriptor) { |
| 50 | field_type = *descriptor++; |
| 51 | switch (field_type) { |
| 52 | case 'b': /* 8-bit byte */ |
| 53 | *dp++ = *sp++; |
| 54 | break; |
| 55 | case 'w': /* 16-bit word, convert from little endian to CPU */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 56 | dp += ((uintptr_t)dp & 1); /* Align to 16-bit word boundary */ |
Daniel Drake | 2b2e9c4 | 2008-05-16 22:37:56 +0100 | [diff] [blame] | 57 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 58 | *((uint16_t *)dp) = READ_LE16(sp); |
| 59 | sp += 2; |
| 60 | dp += 2; |
| 61 | break; |
| 62 | case 'd': /* 32-bit word, convert from little endian to CPU */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 63 | dp += 4 - ((uintptr_t)dp & 3); /* Align to 32-bit word boundary */ |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 64 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 65 | *((uint32_t *)dp) = READ_LE32(sp); |
| 66 | sp += 4; |
| 67 | dp += 4; |
| 68 | break; |
| 69 | case 'u': /* 16 byte UUID */ |
| 70 | memcpy(dp, sp, 16); |
| 71 | sp += 16; |
| 72 | dp += 16; |
| 73 | break; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 74 | } |
| 75 | } |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 76 | } |
| 77 | |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 78 | static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint) |
| 79 | { |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 80 | free((void *)endpoint->extra); |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 81 | } |
| 82 | |
Daniel Drake | 1df713d | 2008-06-24 23:01:51 -0500 | [diff] [blame] | 83 | static int parse_endpoint(struct libusb_context *ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 84 | struct libusb_endpoint_descriptor *endpoint, const uint8_t *buffer, int size) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 85 | { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 86 | const struct usbi_descriptor_header *header; |
| 87 | const uint8_t *begin; |
| 88 | void *extra; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 89 | int parsed = 0; |
| 90 | int len; |
| 91 | |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 92 | if (size < DESC_HEADER_LENGTH) { |
| 93 | usbi_err(ctx, "short endpoint descriptor read %d/%d", |
| 94 | size, DESC_HEADER_LENGTH); |
| 95 | return LIBUSB_ERROR_IO; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 96 | } |
| 97 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 98 | header = (const struct usbi_descriptor_header *)buffer; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 99 | if (header->bDescriptorType != LIBUSB_DT_ENDPOINT) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 100 | usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 101 | header->bDescriptorType, LIBUSB_DT_ENDPOINT); |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 102 | return parsed; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 103 | } else if (header->bLength < LIBUSB_DT_ENDPOINT_SIZE) { |
| 104 | usbi_err(ctx, "invalid endpoint bLength (%u)", header->bLength); |
| 105 | return LIBUSB_ERROR_IO; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 106 | } else if (header->bLength > size) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 107 | usbi_warn(ctx, "short endpoint descriptor read %d/%u", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 108 | size, header->bLength); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 109 | return parsed; |
| 110 | } |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 111 | |
Chris Dickens | e1fcd8a | 2020-04-17 12:21:46 -0700 | [diff] [blame] | 112 | if (header->bLength >= LIBUSB_DT_ENDPOINT_AUDIO_SIZE) |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 113 | parse_descriptor(buffer, "bbbbwbbb", endpoint); |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 114 | else |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 115 | parse_descriptor(buffer, "bbbbwb", endpoint); |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 116 | |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 117 | buffer += header->bLength; |
| 118 | size -= header->bLength; |
| 119 | parsed += header->bLength; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 120 | |
| 121 | /* Skip over the rest of the Class Specific or Vendor Specific */ |
| 122 | /* descriptors */ |
| 123 | begin = buffer; |
| 124 | while (size >= DESC_HEADER_LENGTH) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 125 | header = (const struct usbi_descriptor_header *)buffer; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 126 | if (header->bLength < DESC_HEADER_LENGTH) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 127 | usbi_err(ctx, "invalid extra ep desc len (%u)", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 128 | header->bLength); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 129 | return LIBUSB_ERROR_IO; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 130 | } else if (header->bLength > size) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 131 | usbi_warn(ctx, "short extra ep desc read %d/%u", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 132 | size, header->bLength); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 133 | return parsed; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | /* If we find another "proper" descriptor then we're done */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 137 | if (header->bDescriptorType == LIBUSB_DT_ENDPOINT || |
| 138 | header->bDescriptorType == LIBUSB_DT_INTERFACE || |
| 139 | header->bDescriptorType == LIBUSB_DT_CONFIG || |
| 140 | header->bDescriptorType == LIBUSB_DT_DEVICE) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 141 | break; |
| 142 | |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 143 | usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType); |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 144 | buffer += header->bLength; |
| 145 | size -= header->bLength; |
| 146 | parsed += header->bLength; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | /* Copy any unknown descriptors into a storage area for drivers */ |
| 150 | /* to later parse */ |
| 151 | len = (int)(buffer - begin); |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 152 | if (len <= 0) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 153 | return parsed; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 154 | |
Sean McBride | 8a05a3f | 2018-12-15 17:08:34 -0500 | [diff] [blame] | 155 | extra = malloc((size_t)len); |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 156 | if (!extra) |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 157 | return LIBUSB_ERROR_NO_MEM; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 158 | |
Daniel Drake | d9b2ce2 | 2008-04-01 23:28:32 +0100 | [diff] [blame] | 159 | memcpy(extra, begin, len); |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 160 | endpoint->extra = extra; |
Daniel Drake | 885c2a5 | 2008-05-05 21:34:31 +0100 | [diff] [blame] | 161 | endpoint->extra_length = len; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 162 | |
| 163 | return parsed; |
| 164 | } |
| 165 | |
Pete Batard | 8aceb5c | 2010-02-14 19:47:30 -0600 | [diff] [blame] | 166 | static void clear_interface(struct libusb_interface *usb_interface) |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 167 | { |
| 168 | int i; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 169 | |
Nathan Hjelm | dc7760a | 2016-03-06 15:00:31 -0700 | [diff] [blame] | 170 | if (usb_interface->altsetting) { |
| 171 | for (i = 0; i < usb_interface->num_altsetting; i++) { |
| 172 | struct libusb_interface_descriptor *ifp = |
| 173 | (struct libusb_interface_descriptor *) |
| 174 | usb_interface->altsetting + i; |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 175 | |
| 176 | free((void *)ifp->extra); |
Nathan Hjelm | 0dcc646 | 2016-03-06 15:04:02 -0700 | [diff] [blame] | 177 | if (ifp->endpoint) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 178 | uint8_t j; |
| 179 | |
Nathan Hjelm | 0dcc646 | 2016-03-06 15:04:02 -0700 | [diff] [blame] | 180 | for (j = 0; j < ifp->bNumEndpoints; j++) |
| 181 | clear_endpoint((struct libusb_endpoint_descriptor *) |
| 182 | ifp->endpoint + j); |
| 183 | } |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 184 | free((void *)ifp->endpoint); |
Nathan Hjelm | dc7760a | 2016-03-06 15:00:31 -0700 | [diff] [blame] | 185 | } |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 186 | } |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 187 | free((void *)usb_interface->altsetting); |
Gaurav | 98dd33c | 2015-06-19 08:53:59 +0530 | [diff] [blame] | 188 | usb_interface->altsetting = NULL; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 189 | } |
| 190 | |
Daniel Drake | 1df713d | 2008-06-24 23:01:51 -0500 | [diff] [blame] | 191 | static int parse_interface(libusb_context *ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 192 | struct libusb_interface *usb_interface, const uint8_t *buffer, int size) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 193 | { |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 194 | int len; |
| 195 | int r; |
| 196 | int parsed = 0; |
Hans de Goede | d9f30bc | 2013-05-24 14:03:54 +0200 | [diff] [blame] | 197 | int interface_number = -1; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 198 | const struct usbi_descriptor_header *header; |
| 199 | const struct usbi_interface_descriptor *if_desc; |
Daniel Drake | dbb3fd8 | 2008-01-04 00:54:57 +0000 | [diff] [blame] | 200 | struct libusb_interface_descriptor *ifp; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 201 | const uint8_t *begin; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 202 | |
Chris Dickens | e1fcd8a | 2020-04-17 12:21:46 -0700 | [diff] [blame] | 203 | while (size >= LIBUSB_DT_INTERFACE_SIZE) { |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 204 | struct libusb_interface_descriptor *altsetting; |
| 205 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 206 | altsetting = realloc((void *)usb_interface->altsetting, |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 207 | sizeof(*altsetting) * (size_t)(usb_interface->num_altsetting + 1)); |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 208 | if (!altsetting) { |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 209 | r = LIBUSB_ERROR_NO_MEM; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 210 | goto err; |
| 211 | } |
Pete Batard | 8aceb5c | 2010-02-14 19:47:30 -0600 | [diff] [blame] | 212 | usb_interface->altsetting = altsetting; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 213 | |
Pete Batard | 8aceb5c | 2010-02-14 19:47:30 -0600 | [diff] [blame] | 214 | ifp = altsetting + usb_interface->num_altsetting; |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 215 | parse_descriptor(buffer, "bbbbbbbbb", ifp); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 216 | if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 217 | usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 218 | ifp->bDescriptorType, LIBUSB_DT_INTERFACE); |
| 219 | return parsed; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 220 | } else if (ifp->bLength < LIBUSB_DT_INTERFACE_SIZE) { |
| 221 | usbi_err(ctx, "invalid interface bLength (%u)", |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 222 | ifp->bLength); |
| 223 | r = LIBUSB_ERROR_IO; |
| 224 | goto err; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 225 | } else if (ifp->bLength > size) { |
| 226 | usbi_warn(ctx, "short intf descriptor read %d/%u", |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 227 | size, ifp->bLength); |
| 228 | return parsed; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 229 | } else if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { |
| 230 | usbi_err(ctx, "too many endpoints (%u)", ifp->bNumEndpoints); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 231 | r = LIBUSB_ERROR_IO; |
| 232 | goto err; |
| 233 | } |
| 234 | |
| 235 | usb_interface->num_altsetting++; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 236 | ifp->extra = NULL; |
Daniel Drake | 885c2a5 | 2008-05-05 21:34:31 +0100 | [diff] [blame] | 237 | ifp->extra_length = 0; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 238 | ifp->endpoint = NULL; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 239 | |
Hans de Goede | d9f30bc | 2013-05-24 14:03:54 +0200 | [diff] [blame] | 240 | if (interface_number == -1) |
| 241 | interface_number = ifp->bInterfaceNumber; |
| 242 | |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 243 | /* Skip over the interface */ |
| 244 | buffer += ifp->bLength; |
| 245 | parsed += ifp->bLength; |
| 246 | size -= ifp->bLength; |
| 247 | |
| 248 | begin = buffer; |
| 249 | |
| 250 | /* Skip over any interface, class or vendor descriptors */ |
| 251 | while (size >= DESC_HEADER_LENGTH) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 252 | header = (const struct usbi_descriptor_header *)buffer; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 253 | if (header->bLength < DESC_HEADER_LENGTH) { |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 254 | usbi_err(ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 255 | "invalid extra intf desc len (%u)", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 256 | header->bLength); |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 257 | r = LIBUSB_ERROR_IO; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 258 | goto err; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 259 | } else if (header->bLength > size) { |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 260 | usbi_warn(ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 261 | "short extra intf desc read %d/%u", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 262 | size, header->bLength); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 263 | return parsed; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | /* If we find another "proper" descriptor then we're done */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 267 | if (header->bDescriptorType == LIBUSB_DT_INTERFACE || |
| 268 | header->bDescriptorType == LIBUSB_DT_ENDPOINT || |
| 269 | header->bDescriptorType == LIBUSB_DT_CONFIG || |
| 270 | header->bDescriptorType == LIBUSB_DT_DEVICE) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 271 | break; |
| 272 | |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 273 | buffer += header->bLength; |
| 274 | parsed += header->bLength; |
| 275 | size -= header->bLength; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 276 | } |
| 277 | |
| 278 | /* Copy any unknown descriptors into a storage area for */ |
| 279 | /* drivers to later parse */ |
| 280 | len = (int)(buffer - begin); |
Sean McBride | 8a05a3f | 2018-12-15 17:08:34 -0500 | [diff] [blame] | 281 | if (len > 0) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 282 | void *extra = malloc((size_t)len); |
| 283 | |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 284 | if (!extra) { |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 285 | r = LIBUSB_ERROR_NO_MEM; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 286 | goto err; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 287 | } |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 288 | |
| 289 | memcpy(extra, begin, len); |
| 290 | ifp->extra = extra; |
Daniel Drake | 885c2a5 | 2008-05-05 21:34:31 +0100 | [diff] [blame] | 291 | ifp->extra_length = len; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 292 | } |
| 293 | |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 294 | if (ifp->bNumEndpoints > 0) { |
Daniel Drake | d9b2ce2 | 2008-04-01 23:28:32 +0100 | [diff] [blame] | 295 | struct libusb_endpoint_descriptor *endpoint; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 296 | uint8_t i; |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 297 | |
| 298 | endpoint = calloc(ifp->bNumEndpoints, sizeof(*endpoint)); |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 299 | if (!endpoint) { |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 300 | r = LIBUSB_ERROR_NO_MEM; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 301 | goto err; |
| 302 | } |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 303 | |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 304 | ifp->endpoint = endpoint; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 305 | for (i = 0; i < ifp->bNumEndpoints; i++) { |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 306 | r = parse_endpoint(ctx, endpoint + i, buffer, size); |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 307 | if (r < 0) |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 308 | goto err; |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 309 | if (r == 0) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 310 | ifp->bNumEndpoints = i; |
Sean McBride | c1d8c8d | 2017-12-27 23:35:42 -0500 | [diff] [blame] | 311 | break; |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 312 | } |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 313 | |
| 314 | buffer += r; |
| 315 | parsed += r; |
| 316 | size -= r; |
| 317 | } |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 318 | } |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 319 | |
| 320 | /* We check to see if it's an alternate to this one */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 321 | if_desc = (const struct usbi_interface_descriptor *)buffer; |
Daniel Drake | dbb3fd8 | 2008-01-04 00:54:57 +0000 | [diff] [blame] | 322 | if (size < LIBUSB_DT_INTERFACE_SIZE || |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 323 | if_desc->bDescriptorType != LIBUSB_DT_INTERFACE || |
| 324 | if_desc->bInterfaceNumber != interface_number) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 325 | return parsed; |
| 326 | } |
| 327 | |
| 328 | return parsed; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 329 | err: |
Pete Batard | 8aceb5c | 2010-02-14 19:47:30 -0600 | [diff] [blame] | 330 | clear_interface(usb_interface); |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 331 | return r; |
| 332 | } |
| 333 | |
| 334 | static void clear_configuration(struct libusb_config_descriptor *config) |
| 335 | { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 336 | uint8_t i; |
| 337 | |
Nathan Hjelm | dc7760a | 2016-03-06 15:00:31 -0700 | [diff] [blame] | 338 | if (config->interface) { |
| 339 | for (i = 0; i < config->bNumInterfaces; i++) |
| 340 | clear_interface((struct libusb_interface *) |
| 341 | config->interface + i); |
| 342 | } |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 343 | free((void *)config->interface); |
| 344 | free((void *)config->extra); |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 345 | } |
| 346 | |
Daniel Drake | 1df713d | 2008-06-24 23:01:51 -0500 | [diff] [blame] | 347 | static int parse_configuration(struct libusb_context *ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 348 | struct libusb_config_descriptor *config, const uint8_t *buffer, int size) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 349 | { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 350 | uint8_t i; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 351 | int r; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 352 | const struct usbi_descriptor_header *header; |
Pete Batard | 8aceb5c | 2010-02-14 19:47:30 -0600 | [diff] [blame] | 353 | struct libusb_interface *usb_interface; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 354 | |
Hans de Goede | 25b3142 | 2013-05-23 15:20:12 +0200 | [diff] [blame] | 355 | if (size < LIBUSB_DT_CONFIG_SIZE) { |
| 356 | usbi_err(ctx, "short config descriptor read %d/%d", |
| 357 | size, LIBUSB_DT_CONFIG_SIZE); |
| 358 | return LIBUSB_ERROR_IO; |
| 359 | } |
| 360 | |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 361 | parse_descriptor(buffer, "bbwbbbbb", config); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 362 | if (config->bDescriptorType != LIBUSB_DT_CONFIG) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 363 | usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 364 | config->bDescriptorType, LIBUSB_DT_CONFIG); |
| 365 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 366 | } else if (config->bLength < LIBUSB_DT_CONFIG_SIZE) { |
| 367 | usbi_err(ctx, "invalid config bLength (%u)", config->bLength); |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 368 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 369 | } else if (config->bLength > size) { |
| 370 | usbi_err(ctx, "short config descriptor read %d/%u", |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 371 | size, config->bLength); |
| 372 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 373 | } else if (config->bNumInterfaces > USB_MAXINTERFACES) { |
| 374 | usbi_err(ctx, "too many interfaces (%u)", config->bNumInterfaces); |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 375 | return LIBUSB_ERROR_IO; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 376 | } |
| 377 | |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 378 | usb_interface = calloc(config->bNumInterfaces, sizeof(*usb_interface)); |
Tobias Klauser | a7e946a | 2015-04-14 17:43:05 +0200 | [diff] [blame] | 379 | if (!usb_interface) |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 380 | return LIBUSB_ERROR_NO_MEM; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 381 | |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 382 | config->interface = usb_interface; |
| 383 | |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 384 | buffer += config->bLength; |
| 385 | size -= config->bLength; |
| 386 | |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 387 | for (i = 0; i < config->bNumInterfaces; i++) { |
| 388 | int len; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 389 | const uint8_t *begin; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 390 | |
| 391 | /* Skip over the rest of the Class Specific or Vendor */ |
| 392 | /* Specific descriptors */ |
| 393 | begin = buffer; |
| 394 | while (size >= DESC_HEADER_LENGTH) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 395 | header = (const struct usbi_descriptor_header *)buffer; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 396 | if (header->bLength < DESC_HEADER_LENGTH) { |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 397 | usbi_err(ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 398 | "invalid extra config desc len (%u)", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 399 | header->bLength); |
Daniel Drake | 59c205d | 2008-05-05 21:17:03 +0100 | [diff] [blame] | 400 | r = LIBUSB_ERROR_IO; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 401 | goto err; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 402 | } else if (header->bLength > size) { |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 403 | usbi_warn(ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 404 | "short extra config desc read %d/%u", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 405 | size, header->bLength); |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 406 | config->bNumInterfaces = i; |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 407 | return size; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 408 | } |
| 409 | |
| 410 | /* If we find another "proper" descriptor then we're done */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 411 | if (header->bDescriptorType == LIBUSB_DT_ENDPOINT || |
| 412 | header->bDescriptorType == LIBUSB_DT_INTERFACE || |
| 413 | header->bDescriptorType == LIBUSB_DT_CONFIG || |
| 414 | header->bDescriptorType == LIBUSB_DT_DEVICE) |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 415 | break; |
| 416 | |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 417 | usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType); |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 418 | buffer += header->bLength; |
| 419 | size -= header->bLength; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 420 | } |
| 421 | |
| 422 | /* Copy any unknown descriptors into a storage area for */ |
| 423 | /* drivers to later parse */ |
| 424 | len = (int)(buffer - begin); |
Sean McBride | 8a05a3f | 2018-12-15 17:08:34 -0500 | [diff] [blame] | 425 | if (len > 0) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 426 | uint8_t *extra = realloc((void *)config->extra, |
| 427 | (size_t)(config->extra_length + len)); |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 428 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 429 | if (!extra) { |
| 430 | r = LIBUSB_ERROR_NO_MEM; |
| 431 | goto err; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 432 | } |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 433 | |
| 434 | memcpy(extra + config->extra_length, begin, len); |
| 435 | config->extra = extra; |
| 436 | config->extra_length += len; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 437 | } |
| 438 | |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 439 | r = parse_interface(ctx, usb_interface + i, buffer, size); |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 440 | if (r < 0) |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 441 | goto err; |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 442 | if (r == 0) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 443 | config->bNumInterfaces = i; |
Hans de Goede | b42954b | 2013-05-23 17:20:47 +0200 | [diff] [blame] | 444 | break; |
| 445 | } |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 446 | |
| 447 | buffer += r; |
| 448 | size -= r; |
| 449 | } |
| 450 | |
| 451 | return size; |
Daniel Drake | 0269c75 | 2008-04-29 13:34:26 +0100 | [diff] [blame] | 452 | |
| 453 | err: |
| 454 | clear_configuration(config); |
| 455 | return r; |
Daniel Drake | a8d2881 | 2007-12-03 23:29:22 +0000 | [diff] [blame] | 456 | } |
| 457 | |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 458 | static int raw_desc_to_config(struct libusb_context *ctx, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 459 | const uint8_t *buf, int size, struct libusb_config_descriptor **config) |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 460 | { |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 461 | struct libusb_config_descriptor *_config = calloc(1, sizeof(*_config)); |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 462 | int r; |
Harry Mallon | 85d1f36 | 2019-02-12 13:18:56 +0000 | [diff] [blame] | 463 | |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 464 | if (!_config) |
| 465 | return LIBUSB_ERROR_NO_MEM; |
| 466 | |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 467 | r = parse_configuration(ctx, _config, buf, size); |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 468 | if (r < 0) { |
| 469 | usbi_err(ctx, "parse_configuration failed with error %d", r); |
| 470 | free(_config); |
| 471 | return r; |
| 472 | } else if (r > 0) { |
| 473 | usbi_warn(ctx, "still %d bytes of descriptor data left", r); |
| 474 | } |
Harry Mallon | 85d1f36 | 2019-02-12 13:18:56 +0000 | [diff] [blame] | 475 | |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 476 | *config = _config; |
| 477 | return LIBUSB_SUCCESS; |
| 478 | } |
| 479 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 480 | static int get_active_config_descriptor(struct libusb_device *dev, |
| 481 | uint8_t *buffer, size_t size) |
| 482 | { |
| 483 | int r = usbi_backend.get_active_config_descriptor(dev, buffer, size); |
| 484 | |
| 485 | if (r < 0) |
| 486 | return r; |
| 487 | |
| 488 | if (r < LIBUSB_DT_CONFIG_SIZE) { |
| 489 | usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d", |
| 490 | r, LIBUSB_DT_CONFIG_SIZE); |
| 491 | return LIBUSB_ERROR_IO; |
| 492 | } else if (r != (int)size) { |
| 493 | usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d", |
| 494 | r, (int)size); |
| 495 | } |
| 496 | |
| 497 | return r; |
| 498 | } |
| 499 | |
| 500 | static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx, |
| 501 | uint8_t *buffer, size_t size) |
| 502 | { |
| 503 | int r = usbi_backend.get_config_descriptor(dev, config_idx, buffer, size); |
| 504 | |
| 505 | if (r < 0) |
| 506 | return r; |
| 507 | if (r < LIBUSB_DT_CONFIG_SIZE) { |
| 508 | usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d", |
| 509 | r, LIBUSB_DT_CONFIG_SIZE); |
| 510 | return LIBUSB_ERROR_IO; |
| 511 | } else if (r != (int)size) { |
| 512 | usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d", |
| 513 | r, (int)size); |
| 514 | } |
| 515 | |
| 516 | return r; |
| 517 | } |
| 518 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 519 | /** \ingroup libusb_desc |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 520 | * Get the USB device descriptor for a given device. |
Daniel Drake | 10d4e42 | 2008-05-08 23:04:52 +0100 | [diff] [blame] | 521 | * |
| 522 | * This is a non-blocking function; the device descriptor is cached in memory. |
| 523 | * |
hjelmn@cs.unm.edu | 1eff220 | 2014-01-08 23:50:34 +0000 | [diff] [blame] | 524 | * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this |
Pete Batard | b4c18fa | 2013-07-03 22:41:55 +0100 | [diff] [blame] | 525 | * function always succeeds. |
| 526 | * |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 527 | * \param dev the device |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 528 | * \param desc output location for the descriptor data |
| 529 | * \returns 0 on success or a LIBUSB_ERROR code on failure |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 530 | */ |
Pete Batard | 29f9f9e | 2010-08-13 11:59:49 +0100 | [diff] [blame] | 531 | int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 532 | struct libusb_device_descriptor *desc) |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 533 | { |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 534 | usbi_dbg(DEVICE_CTX(dev), " "); |
Chris Dickens | d21956d | 2020-04-28 12:08:08 -0700 | [diff] [blame] | 535 | static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE, |
| 536 | "struct libusb_device_descriptor is not expected size"); |
| 537 | *desc = dev->device_descriptor; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 538 | return 0; |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 539 | } |
| 540 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 541 | /** \ingroup libusb_desc |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 542 | * Get the USB configuration descriptor for the currently active configuration. |
| 543 | * This is a non-blocking function which does not involve any requests being |
| 544 | * sent to the device. |
| 545 | * |
| 546 | * \param dev a device |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 547 | * \param config output location for the USB configuration descriptor. Only |
| 548 | * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() |
| 549 | * after use. |
| 550 | * \returns 0 on success |
| 551 | * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state |
| 552 | * \returns another LIBUSB_ERROR code on error |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 553 | * \see libusb_get_config_descriptor |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 554 | */ |
Pete Batard | 29f9f9e | 2010-08-13 11:59:49 +0100 | [diff] [blame] | 555 | int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 556 | struct libusb_config_descriptor **config) |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 557 | { |
Chris Dickens | a6bfd45 | 2020-08-28 15:42:41 -0700 | [diff] [blame] | 558 | union usbi_config_desc_buf _config; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 559 | uint16_t config_len; |
| 560 | uint8_t *buf; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 561 | int r; |
| 562 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 563 | r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf)); |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 564 | if (r < 0) |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 565 | return r; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 566 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 567 | config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); |
| 568 | buf = malloc(config_len); |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 569 | if (!buf) |
| 570 | return LIBUSB_ERROR_NO_MEM; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 571 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 572 | r = get_active_config_descriptor(dev, buf, config_len); |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 573 | if (r >= 0) |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 574 | r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 575 | |
Daniel Drake | 00bb280 | 2009-05-26 15:53:26 -0400 | [diff] [blame] | 576 | free(buf); |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 577 | return r; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 578 | } |
| 579 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 580 | /** \ingroup libusb_desc |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 581 | * Get a USB configuration descriptor based on its index. |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 582 | * This is a non-blocking function which does not involve any requests being |
| 583 | * sent to the device. |
| 584 | * |
| 585 | * \param dev a device |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 586 | * \param config_index the index of the configuration you wish to retrieve |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 587 | * \param config output location for the USB configuration descriptor. Only |
| 588 | * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() |
| 589 | * after use. |
| 590 | * \returns 0 on success |
| 591 | * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist |
| 592 | * \returns another LIBUSB_ERROR code on error |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 593 | * \see libusb_get_active_config_descriptor() |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 594 | * \see libusb_get_config_descriptor_by_value() |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 595 | */ |
Pete Batard | 29f9f9e | 2010-08-13 11:59:49 +0100 | [diff] [blame] | 596 | int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 597 | uint8_t config_index, struct libusb_config_descriptor **config) |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 598 | { |
Chris Dickens | a6bfd45 | 2020-08-28 15:42:41 -0700 | [diff] [blame] | 599 | union usbi_config_desc_buf _config; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 600 | uint16_t config_len; |
| 601 | uint8_t *buf; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 602 | int r; |
| 603 | |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 604 | usbi_dbg(DEVICE_CTX(dev), "index %u", config_index); |
Chris Dickens | 15bd82e | 2020-03-16 01:01:51 -0700 | [diff] [blame] | 605 | if (config_index >= dev->device_descriptor.bNumConfigurations) |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 606 | return LIBUSB_ERROR_NOT_FOUND; |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 607 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 608 | r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf)); |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 609 | if (r < 0) |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 610 | return r; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 611 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 612 | config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); |
| 613 | buf = malloc(config_len); |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 614 | if (!buf) |
| 615 | return LIBUSB_ERROR_NO_MEM; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 616 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 617 | r = get_config_descriptor(dev, config_index, buf, config_len); |
Hans de Goede | da5b335 | 2013-05-23 19:24:48 +0200 | [diff] [blame] | 618 | if (r >= 0) |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 619 | r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 620 | |
Daniel Drake | 00bb280 | 2009-05-26 15:53:26 -0400 | [diff] [blame] | 621 | free(buf); |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 622 | return r; |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 623 | } |
| 624 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 625 | /** \ingroup libusb_desc |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 626 | * Get a USB configuration descriptor with a specific bConfigurationValue. |
| 627 | * This is a non-blocking function which does not involve any requests being |
| 628 | * sent to the device. |
| 629 | * |
| 630 | * \param dev a device |
| 631 | * \param bConfigurationValue the bConfigurationValue of the configuration you |
| 632 | * wish to retrieve |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 633 | * \param config output location for the USB configuration descriptor. Only |
| 634 | * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() |
| 635 | * after use. |
| 636 | * \returns 0 on success |
| 637 | * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist |
| 638 | * \returns another LIBUSB_ERROR code on error |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 639 | * \see libusb_get_active_config_descriptor() |
| 640 | * \see libusb_get_config_descriptor() |
| 641 | */ |
Pete Batard | 29f9f9e | 2010-08-13 11:59:49 +0100 | [diff] [blame] | 642 | int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev, |
Daniel Drake | f2ede98 | 2008-05-10 21:45:42 +0100 | [diff] [blame] | 643 | uint8_t bConfigurationValue, struct libusb_config_descriptor **config) |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 644 | { |
Chris Dickens | 8476804 | 2020-04-28 12:17:50 -0700 | [diff] [blame] | 645 | uint8_t idx; |
| 646 | int r; |
Hans de Goede | 6b41074 | 2013-05-23 19:51:07 +0200 | [diff] [blame] | 647 | |
Chris Dickens | cad7d0e | 2017-07-05 13:44:30 -0700 | [diff] [blame] | 648 | if (usbi_backend.get_config_descriptor_by_value) { |
Chris Dickens | fc5132c | 2020-04-17 14:21:44 -0700 | [diff] [blame] | 649 | void *buf; |
| 650 | |
Chris Dickens | cad7d0e | 2017-07-05 13:44:30 -0700 | [diff] [blame] | 651 | r = usbi_backend.get_config_descriptor_by_value(dev, |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 652 | bConfigurationValue, &buf); |
Hans de Goede | 6b41074 | 2013-05-23 19:51:07 +0200 | [diff] [blame] | 653 | if (r < 0) |
| 654 | return r; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 655 | |
| 656 | return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); |
Hans de Goede | 6b41074 | 2013-05-23 19:51:07 +0200 | [diff] [blame] | 657 | } |
| 658 | |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 659 | usbi_dbg(DEVICE_CTX(dev), "value %u", bConfigurationValue); |
Chris Dickens | 8476804 | 2020-04-28 12:17:50 -0700 | [diff] [blame] | 660 | for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) { |
Chris Dickens | a6bfd45 | 2020-08-28 15:42:41 -0700 | [diff] [blame] | 661 | union usbi_config_desc_buf _config; |
Chris Dickens | 8476804 | 2020-04-28 12:17:50 -0700 | [diff] [blame] | 662 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 663 | r = get_config_descriptor(dev, idx, _config.buf, sizeof(_config.buf)); |
Chris Dickens | 8476804 | 2020-04-28 12:17:50 -0700 | [diff] [blame] | 664 | if (r < 0) |
| 665 | return r; |
| 666 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 667 | if (_config.desc.bConfigurationValue == bConfigurationValue) |
Chris Dickens | 8476804 | 2020-04-28 12:17:50 -0700 | [diff] [blame] | 668 | return libusb_get_config_descriptor(dev, idx, config); |
| 669 | } |
| 670 | |
| 671 | return LIBUSB_ERROR_NOT_FOUND; |
Daniel Drake | c3844f7 | 2008-05-10 14:42:43 +0100 | [diff] [blame] | 672 | } |
| 673 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 674 | /** \ingroup libusb_desc |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 675 | * Free a configuration descriptor obtained from |
| 676 | * libusb_get_active_config_descriptor() or libusb_get_config_descriptor(). |
| 677 | * It is safe to call this function with a NULL config parameter, in which |
| 678 | * case the function simply returns. |
| 679 | * |
| 680 | * \param config the configuration descriptor to free |
| 681 | */ |
Pete Batard | 29f9f9e | 2010-08-13 11:59:49 +0100 | [diff] [blame] | 682 | void API_EXPORTED libusb_free_config_descriptor( |
Daniel Drake | fe4adcc | 2008-05-09 14:34:31 +0100 | [diff] [blame] | 683 | struct libusb_config_descriptor *config) |
| 684 | { |
| 685 | if (!config) |
| 686 | return; |
| 687 | |
| 688 | clear_configuration(config); |
| 689 | free(config); |
Daniel Drake | ead09cd | 2008-03-15 16:35:12 +0000 | [diff] [blame] | 690 | } |
| 691 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 692 | /** \ingroup libusb_desc |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 693 | * Get an endpoints superspeed endpoint companion descriptor (if any) |
| 694 | * |
| 695 | * \param ctx the context to operate on, or NULL for the default context |
| 696 | * \param endpoint endpoint descriptor from which to get the superspeed |
| 697 | * endpoint companion descriptor |
| 698 | * \param ep_comp output location for the superspeed endpoint companion |
| 699 | * descriptor. Only valid if 0 was returned. Must be freed with |
| 700 | * libusb_free_ss_endpoint_companion_descriptor() after use. |
| 701 | * \returns 0 on success |
| 702 | * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist |
| 703 | * \returns another LIBUSB_ERROR code on error |
| 704 | */ |
| 705 | int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 706 | libusb_context *ctx, |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 707 | const struct libusb_endpoint_descriptor *endpoint, |
| 708 | struct libusb_ss_endpoint_companion_descriptor **ep_comp) |
| 709 | { |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 710 | struct usbi_descriptor_header *header; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 711 | const uint8_t *buffer = endpoint->extra; |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 712 | int size = endpoint->extra_length; |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 713 | |
| 714 | *ep_comp = NULL; |
| 715 | |
| 716 | while (size >= DESC_HEADER_LENGTH) { |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 717 | header = (struct usbi_descriptor_header *)buffer; |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 718 | if (header->bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 719 | if (header->bLength < DESC_HEADER_LENGTH) { |
Chris Dickens | 0abebc5 | 2020-08-18 15:49:22 -0700 | [diff] [blame] | 720 | usbi_err(ctx, "invalid descriptor length %u", |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 721 | header->bLength); |
| 722 | return LIBUSB_ERROR_IO; |
| 723 | } |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 724 | buffer += header->bLength; |
| 725 | size -= header->bLength; |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 726 | continue; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 727 | } else if (header->bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) { |
| 728 | usbi_err(ctx, "invalid ss-ep-comp-desc length %u", |
Chris Dickens | 95b60dc | 2020-04-17 11:43:54 -0700 | [diff] [blame] | 729 | header->bLength); |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 730 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 731 | } else if (header->bLength > size) { |
| 732 | usbi_err(ctx, "short ss-ep-comp-desc read %d/%u", |
| 733 | size, header->bLength); |
| 734 | return LIBUSB_ERROR_IO; |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 735 | } |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 736 | |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 737 | *ep_comp = malloc(sizeof(**ep_comp)); |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 738 | if (!*ep_comp) |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 739 | return LIBUSB_ERROR_NO_MEM; |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 740 | parse_descriptor(buffer, "bbbbw", *ep_comp); |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 741 | return LIBUSB_SUCCESS; |
| 742 | } |
| 743 | return LIBUSB_ERROR_NOT_FOUND; |
| 744 | } |
| 745 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 746 | /** \ingroup libusb_desc |
Hans de Goede | ef698c6 | 2013-05-27 11:12:28 +0200 | [diff] [blame] | 747 | * Free a superspeed endpoint companion descriptor obtained from |
| 748 | * libusb_get_ss_endpoint_companion_descriptor(). |
| 749 | * It is safe to call this function with a NULL ep_comp parameter, in which |
| 750 | * case the function simply returns. |
| 751 | * |
| 752 | * \param ep_comp the superspeed endpoint companion descriptor to free |
| 753 | */ |
| 754 | void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor( |
| 755 | struct libusb_ss_endpoint_companion_descriptor *ep_comp) |
| 756 | { |
| 757 | free(ep_comp); |
| 758 | } |
| 759 | |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 760 | static int parse_bos(struct libusb_context *ctx, |
| 761 | struct libusb_bos_descriptor **bos, |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 762 | const uint8_t *buffer, int size) |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 763 | { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 764 | struct libusb_bos_descriptor *_bos; |
| 765 | const struct usbi_bos_descriptor *bos_desc; |
| 766 | const struct usbi_descriptor_header *header; |
| 767 | uint8_t i; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 768 | |
| 769 | if (size < LIBUSB_DT_BOS_SIZE) { |
| 770 | usbi_err(ctx, "short bos descriptor read %d/%d", |
| 771 | size, LIBUSB_DT_BOS_SIZE); |
| 772 | return LIBUSB_ERROR_IO; |
| 773 | } |
| 774 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 775 | bos_desc = (const struct usbi_bos_descriptor *)buffer; |
| 776 | if (bos_desc->bDescriptorType != LIBUSB_DT_BOS) { |
| 777 | usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", |
| 778 | bos_desc->bDescriptorType, LIBUSB_DT_BOS); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 779 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 780 | } else if (bos_desc->bLength < LIBUSB_DT_BOS_SIZE) { |
| 781 | usbi_err(ctx, "invalid bos bLength (%u)", bos_desc->bLength); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 782 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 783 | } else if (bos_desc->bLength > size) { |
| 784 | usbi_err(ctx, "short bos descriptor read %d/%u", |
| 785 | size, bos_desc->bLength); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 786 | return LIBUSB_ERROR_IO; |
| 787 | } |
| 788 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 789 | _bos = calloc(1, sizeof(*_bos) + bos_desc->bNumDeviceCaps * sizeof(void *)); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 790 | if (!_bos) |
| 791 | return LIBUSB_ERROR_NO_MEM; |
| 792 | |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 793 | parse_descriptor(buffer, "bbwb", _bos); |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 794 | buffer += _bos->bLength; |
| 795 | size -= _bos->bLength; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 796 | |
| 797 | /* Get the device capability descriptors */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 798 | for (i = 0; i < _bos->bNumDeviceCaps; i++) { |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 799 | if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { |
| 800 | usbi_warn(ctx, "short dev-cap descriptor read %d/%d", |
| 801 | size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE); |
| 802 | break; |
| 803 | } |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 804 | header = (const struct usbi_descriptor_header *)buffer; |
| 805 | if (header->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) { |
| 806 | usbi_warn(ctx, "unexpected descriptor 0x%x (expected 0x%x)", |
| 807 | header->bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 808 | break; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 809 | } else if (header->bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { |
| 810 | usbi_err(ctx, "invalid dev-cap bLength (%u)", |
| 811 | header->bLength); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 812 | libusb_free_bos_descriptor(_bos); |
| 813 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 814 | } else if (header->bLength > size) { |
| 815 | usbi_warn(ctx, "short dev-cap descriptor read %d/%u", |
| 816 | size, header->bLength); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 817 | break; |
| 818 | } |
| 819 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 820 | _bos->dev_capability[i] = malloc(header->bLength); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 821 | if (!_bos->dev_capability[i]) { |
| 822 | libusb_free_bos_descriptor(_bos); |
| 823 | return LIBUSB_ERROR_NO_MEM; |
| 824 | } |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 825 | memcpy(_bos->dev_capability[i], buffer, header->bLength); |
| 826 | buffer += header->bLength; |
| 827 | size -= header->bLength; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 828 | } |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 829 | _bos->bNumDeviceCaps = i; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 830 | *bos = _bos; |
| 831 | |
| 832 | return LIBUSB_SUCCESS; |
| 833 | } |
| 834 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 835 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 836 | * Get a Binary Object Store (BOS) descriptor |
| 837 | * This is a BLOCKING function, which will send requests to the device. |
| 838 | * |
Chris Dickens | 8a0c143 | 2016-02-24 00:23:49 -0800 | [diff] [blame] | 839 | * \param dev_handle the handle of an open libusb device |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 840 | * \param bos output location for the BOS descriptor. Only valid if 0 was returned. |
| 841 | * Must be freed with \ref libusb_free_bos_descriptor() after use. |
| 842 | * \returns 0 on success |
| 843 | * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor |
| 844 | * \returns another LIBUSB_ERROR code on error |
| 845 | */ |
Chris Dickens | 8a0c143 | 2016-02-24 00:23:49 -0800 | [diff] [blame] | 846 | int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 847 | struct libusb_bos_descriptor **bos) |
| 848 | { |
Chris Dickens | a6bfd45 | 2020-08-28 15:42:41 -0700 | [diff] [blame] | 849 | union usbi_bos_desc_buf _bos; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 850 | uint16_t bos_len; |
| 851 | uint8_t *bos_data; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 852 | int r; |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 853 | struct libusb_context *ctx = HANDLE_CTX(dev_handle); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 854 | |
| 855 | /* Read the BOS. This generates 2 requests on the bus, |
| 856 | * one for the header, and one for the full BOS */ |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 857 | r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf)); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 858 | if (r < 0) { |
Hans de Goede | f9ef58d | 2013-06-14 11:10:35 +0200 | [diff] [blame] | 859 | if (r != LIBUSB_ERROR_PIPE) |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 860 | usbi_err(ctx, "failed to read BOS (%d)", r); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 861 | return r; |
| 862 | } |
| 863 | if (r < LIBUSB_DT_BOS_SIZE) { |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 864 | usbi_err(ctx, "short BOS read %d/%d", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 865 | r, LIBUSB_DT_BOS_SIZE); |
| 866 | return LIBUSB_ERROR_IO; |
| 867 | } |
| 868 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 869 | bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength); |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 870 | usbi_dbg(ctx, "found BOS descriptor: size %u bytes, %u capabilities", |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 871 | bos_len, _bos.desc.bNumDeviceCaps); |
| 872 | bos_data = calloc(1, bos_len); |
| 873 | if (!bos_data) |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 874 | return LIBUSB_ERROR_NO_MEM; |
| 875 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 876 | r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len); |
| 877 | if (r >= 0) { |
| 878 | if (r != (int)bos_len) |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 879 | usbi_warn(ctx, "short BOS read %d/%u", r, bos_len); |
Chris Dickens | 9ececdb | 2020-04-17 12:57:49 -0700 | [diff] [blame] | 880 | r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r); |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 881 | } else { |
Nathan Hjelm | 6cae9c6 | 2021-07-20 09:31:06 -0600 | [diff] [blame] | 882 | usbi_err(ctx, "failed to read BOS (%d)", r); |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 883 | } |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 884 | |
| 885 | free(bos_data); |
| 886 | return r; |
| 887 | } |
| 888 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 889 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 890 | * Free a BOS descriptor obtained from libusb_get_bos_descriptor(). |
| 891 | * It is safe to call this function with a NULL bos parameter, in which |
| 892 | * case the function simply returns. |
| 893 | * |
| 894 | * \param bos the BOS descriptor to free |
| 895 | */ |
| 896 | void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) |
| 897 | { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 898 | uint8_t i; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 899 | |
| 900 | if (!bos) |
| 901 | return; |
| 902 | |
| 903 | for (i = 0; i < bos->bNumDeviceCaps; i++) |
| 904 | free(bos->dev_capability[i]); |
| 905 | free(bos); |
| 906 | } |
| 907 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 908 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 909 | * Get an USB 2.0 Extension descriptor |
| 910 | * |
| 911 | * \param ctx the context to operate on, or NULL for the default context |
| 912 | * \param dev_cap Device Capability descriptor with a bDevCapabilityType of |
| 913 | * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION |
| 914 | * LIBUSB_BT_USB_2_0_EXTENSION |
| 915 | * \param usb_2_0_extension output location for the USB 2.0 Extension |
| 916 | * descriptor. Only valid if 0 was returned. Must be freed with |
| 917 | * libusb_free_usb_2_0_extension_descriptor() after use. |
| 918 | * \returns 0 on success |
| 919 | * \returns a LIBUSB_ERROR code on error |
| 920 | */ |
| 921 | int API_EXPORTED libusb_get_usb_2_0_extension_descriptor( |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 922 | libusb_context *ctx, |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 923 | struct libusb_bos_dev_capability_descriptor *dev_cap, |
| 924 | struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension) |
| 925 | { |
| 926 | struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 927 | |
| 928 | if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 929 | usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 930 | dev_cap->bDevCapabilityType, |
| 931 | LIBUSB_BT_USB_2_0_EXTENSION); |
| 932 | return LIBUSB_ERROR_INVALID_PARAM; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 933 | } else if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) { |
| 934 | usbi_err(ctx, "short dev-cap descriptor read %u/%d", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 935 | dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE); |
| 936 | return LIBUSB_ERROR_IO; |
| 937 | } |
| 938 | |
| 939 | _usb_2_0_extension = malloc(sizeof(*_usb_2_0_extension)); |
| 940 | if (!_usb_2_0_extension) |
| 941 | return LIBUSB_ERROR_NO_MEM; |
| 942 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 943 | parse_descriptor(dev_cap, "bbbd", _usb_2_0_extension); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 944 | |
| 945 | *usb_2_0_extension = _usb_2_0_extension; |
| 946 | return LIBUSB_SUCCESS; |
| 947 | } |
| 948 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 949 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 950 | * Free a USB 2.0 Extension descriptor obtained from |
| 951 | * libusb_get_usb_2_0_extension_descriptor(). |
| 952 | * It is safe to call this function with a NULL usb_2_0_extension parameter, |
| 953 | * in which case the function simply returns. |
| 954 | * |
| 955 | * \param usb_2_0_extension the USB 2.0 Extension descriptor to free |
| 956 | */ |
| 957 | void API_EXPORTED libusb_free_usb_2_0_extension_descriptor( |
| 958 | struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension) |
| 959 | { |
| 960 | free(usb_2_0_extension); |
| 961 | } |
| 962 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 963 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 964 | * Get a SuperSpeed USB Device Capability descriptor |
| 965 | * |
| 966 | * \param ctx the context to operate on, or NULL for the default context |
| 967 | * \param dev_cap Device Capability descriptor with a bDevCapabilityType of |
| 968 | * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY |
| 969 | * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY |
| 970 | * \param ss_usb_device_cap output location for the SuperSpeed USB Device |
| 971 | * Capability descriptor. Only valid if 0 was returned. Must be freed with |
| 972 | * libusb_free_ss_usb_device_capability_descriptor() after use. |
| 973 | * \returns 0 on success |
| 974 | * \returns a LIBUSB_ERROR code on error |
| 975 | */ |
| 976 | int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 977 | libusb_context *ctx, |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 978 | struct libusb_bos_dev_capability_descriptor *dev_cap, |
| 979 | struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap) |
| 980 | { |
| 981 | struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 982 | |
| 983 | if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 984 | usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 985 | dev_cap->bDevCapabilityType, |
| 986 | LIBUSB_BT_SS_USB_DEVICE_CAPABILITY); |
| 987 | return LIBUSB_ERROR_INVALID_PARAM; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 988 | } else if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) { |
| 989 | usbi_err(ctx, "short dev-cap descriptor read %u/%d", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 990 | dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE); |
| 991 | return LIBUSB_ERROR_IO; |
| 992 | } |
| 993 | |
| 994 | _ss_usb_device_cap = malloc(sizeof(*_ss_usb_device_cap)); |
| 995 | if (!_ss_usb_device_cap) |
| 996 | return LIBUSB_ERROR_NO_MEM; |
| 997 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 998 | parse_descriptor(dev_cap, "bbbbwbbw", _ss_usb_device_cap); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 999 | |
| 1000 | *ss_usb_device_cap = _ss_usb_device_cap; |
| 1001 | return LIBUSB_SUCCESS; |
| 1002 | } |
| 1003 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 1004 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1005 | * Free a SuperSpeed USB Device Capability descriptor obtained from |
| 1006 | * libusb_get_ss_usb_device_capability_descriptor(). |
| 1007 | * It is safe to call this function with a NULL ss_usb_device_cap |
| 1008 | * parameter, in which case the function simply returns. |
| 1009 | * |
Harry Mallon | 85d1f36 | 2019-02-12 13:18:56 +0000 | [diff] [blame] | 1010 | * \param ss_usb_device_cap the SuperSpeed USB Device Capability descriptor |
| 1011 | * to free |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1012 | */ |
| 1013 | void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor( |
| 1014 | struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap) |
| 1015 | { |
| 1016 | free(ss_usb_device_cap); |
| 1017 | } |
| 1018 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 1019 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1020 | * Get a Container ID descriptor |
| 1021 | * |
| 1022 | * \param ctx the context to operate on, or NULL for the default context |
| 1023 | * \param dev_cap Device Capability descriptor with a bDevCapabilityType of |
| 1024 | * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID |
| 1025 | * LIBUSB_BT_CONTAINER_ID |
| 1026 | * \param container_id output location for the Container ID descriptor. |
| 1027 | * Only valid if 0 was returned. Must be freed with |
| 1028 | * libusb_free_container_id_descriptor() after use. |
| 1029 | * \returns 0 on success |
| 1030 | * \returns a LIBUSB_ERROR code on error |
| 1031 | */ |
Chris Dickens | a157b55 | 2020-04-27 18:43:42 -0700 | [diff] [blame] | 1032 | int API_EXPORTED libusb_get_container_id_descriptor(libusb_context *ctx, |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1033 | struct libusb_bos_dev_capability_descriptor *dev_cap, |
| 1034 | struct libusb_container_id_descriptor **container_id) |
| 1035 | { |
| 1036 | struct libusb_container_id_descriptor *_container_id; |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1037 | |
| 1038 | if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) { |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1039 | usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1040 | dev_cap->bDevCapabilityType, |
| 1041 | LIBUSB_BT_CONTAINER_ID); |
| 1042 | return LIBUSB_ERROR_INVALID_PARAM; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1043 | } else if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) { |
| 1044 | usbi_err(ctx, "short dev-cap descriptor read %u/%d", |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1045 | dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE); |
| 1046 | return LIBUSB_ERROR_IO; |
| 1047 | } |
| 1048 | |
| 1049 | _container_id = malloc(sizeof(*_container_id)); |
| 1050 | if (!_container_id) |
| 1051 | return LIBUSB_ERROR_NO_MEM; |
| 1052 | |
Chris Dickens | e873677 | 2020-04-17 13:22:34 -0700 | [diff] [blame] | 1053 | parse_descriptor(dev_cap, "bbbbu", _container_id); |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1054 | |
| 1055 | *container_id = _container_id; |
| 1056 | return LIBUSB_SUCCESS; |
| 1057 | } |
| 1058 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 1059 | /** \ingroup libusb_desc |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1060 | * Free a Container ID descriptor obtained from |
| 1061 | * libusb_get_container_id_descriptor(). |
| 1062 | * It is safe to call this function with a NULL container_id parameter, |
| 1063 | * in which case the function simply returns. |
| 1064 | * |
Harry Mallon | 85d1f36 | 2019-02-12 13:18:56 +0000 | [diff] [blame] | 1065 | * \param container_id the Container ID descriptor to free |
Hans de Goede | d7b796f | 2013-05-27 16:31:48 +0200 | [diff] [blame] | 1066 | */ |
| 1067 | void API_EXPORTED libusb_free_container_id_descriptor( |
| 1068 | struct libusb_container_id_descriptor *container_id) |
| 1069 | { |
| 1070 | free(container_id); |
| 1071 | } |
| 1072 | |
Chris Dickens | 7ee92df | 2016-02-24 01:07:20 -0800 | [diff] [blame] | 1073 | /** \ingroup libusb_desc |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1074 | * Retrieve a string descriptor in C style ASCII. |
| 1075 | * |
Pete Batard | 8aceb5c | 2010-02-14 19:47:30 -0600 | [diff] [blame] | 1076 | * Wrapper around libusb_get_string_descriptor(). Uses the first language |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1077 | * supported by the device. |
| 1078 | * |
Chris Dickens | 8a0c143 | 2016-02-24 00:23:49 -0800 | [diff] [blame] | 1079 | * \param dev_handle a device handle |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1080 | * \param desc_index the index of the descriptor to retrieve |
| 1081 | * \param data output buffer for ASCII string descriptor |
| 1082 | * \param length size of data buffer |
| 1083 | * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure |
| 1084 | */ |
Chris Dickens | 8a0c143 | 2016-02-24 00:23:49 -0800 | [diff] [blame] | 1085 | int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1086 | uint8_t desc_index, unsigned char *data, int length) |
| 1087 | { |
Chris Dickens | a6bfd45 | 2020-08-28 15:42:41 -0700 | [diff] [blame] | 1088 | union usbi_string_desc_buf str; |
Pete Batard | d7031ee | 2010-05-17 19:30:27 -0300 | [diff] [blame] | 1089 | int r, si, di; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1090 | uint16_t langid, wdata; |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1091 | |
| 1092 | /* Asking for the zero'th index is special - it returns a string |
Konrad Rzepecki | 0f85015 | 2010-11-13 14:09:22 +0100 | [diff] [blame] | 1093 | * descriptor that contains all the language IDs supported by the |
| 1094 | * device. Typically there aren't many - often only one. Language |
| 1095 | * IDs are 16 bit numbers, and they start at the third byte in the |
| 1096 | * descriptor. There's also no point in trying to read descriptor 0 |
| 1097 | * with this function. See USB 2.0 specification section 9.6.7 for |
| 1098 | * more information. |
| 1099 | */ |
| 1100 | |
| 1101 | if (desc_index == 0) |
| 1102 | return LIBUSB_ERROR_INVALID_PARAM; |
| 1103 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1104 | r = libusb_get_string_descriptor(dev_handle, 0, 0, str.buf, 4); |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1105 | if (r < 0) |
| 1106 | return r; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1107 | else if (r != 4 || str.desc.bLength < 4) |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1108 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1109 | else if (str.desc.bDescriptorType != LIBUSB_DT_STRING) |
| 1110 | return LIBUSB_ERROR_IO; |
| 1111 | else if (str.desc.bLength & 1) |
Tormod Volden | 78eb865 | 2021-11-01 23:40:44 +0100 | [diff] [blame] | 1112 | usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for language ID string descriptor", str.desc.bLength); |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1113 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1114 | langid = libusb_le16_to_cpu(str.desc.wData[0]); |
| 1115 | r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf)); |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1116 | if (r < 0) |
| 1117 | return r; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1118 | else if (r < DESC_HEADER_LENGTH || str.desc.bLength > r) |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1119 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1120 | else if (str.desc.bDescriptorType != LIBUSB_DT_STRING) |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1121 | return LIBUSB_ERROR_IO; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1122 | else if ((str.desc.bLength & 1) || str.desc.bLength != r) |
Tormod Volden | 78eb865 | 2021-11-01 23:40:44 +0100 | [diff] [blame] | 1123 | usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r); |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1124 | |
Sean McBride | 678a43b | 2017-12-29 13:30:24 -0500 | [diff] [blame] | 1125 | di = 0; |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1126 | for (si = 2; si < str.desc.bLength; si += 2) { |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1127 | if (di >= (length - 1)) |
| 1128 | break; |
| 1129 | |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1130 | wdata = libusb_le16_to_cpu(str.desc.wData[di]); |
| 1131 | if (wdata < 0x80) |
| 1132 | data[di++] = (unsigned char)wdata; |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1133 | else |
Chris Dickens | fa3f91e | 2020-08-13 10:31:51 -0700 | [diff] [blame] | 1134 | data[di++] = '?'; /* non-ASCII */ |
Daniel Drake | 17ecfb0 | 2008-05-04 16:20:46 +0100 | [diff] [blame] | 1135 | } |
| 1136 | |
| 1137 | data[di] = 0; |
| 1138 | return di; |
| 1139 | } |