1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2019 Joyent, Inc.
14 */
15
16 /*
17 * Routines to access, parse, and manage the USB Binary Object Store
18 */
19
20 #define USBA_FRAMEWORK
21 #include <sys/usb/usba/usba_impl.h>
22 #include <sys/strsun.h>
23 #include <sys/sysmacros.h>
24
25 static size_t
usba_bos_parse_bos_descr(const uchar_t * buf,size_t buflen,usb_bos_descr_t * bosp,size_t rlen)26 usba_bos_parse_bos_descr(const uchar_t *buf, size_t buflen,
27 usb_bos_descr_t *bosp, size_t rlen)
28 {
29 if (buf == NULL || bosp == NULL || buflen < USB_BOS_PACKED_SIZE ||
30 buf[1] != USB_DESCR_TYPE_BOS) {
31 return (USB_PARSE_ERROR);
32 }
33
34 return (usb_parse_data("ccsc", buf, buflen, bosp, rlen));
35 }
36
37 static boolean_t
usba_bos_parse_usb2ext(const uchar_t * buf,size_t buflen,usb_bos_t * bosp)38 usba_bos_parse_usb2ext(const uchar_t *buf, size_t buflen, usb_bos_t *bosp)
39 {
40 size_t len;
41
42 if (buflen != USB_BOS_USB2EXT_PACKED_SIZE) {
43 return (B_FALSE);
44 }
45
46 len = usb_parse_data("cccl", buf, buflen, &bosp->ubos_caps.ubos_usb2,
47 sizeof (usb_bos_usb2ext_t));
48 return (len == sizeof (usb_bos_usb2ext_t));
49 }
50
51 static boolean_t
usba_bos_parse_superspeed(const uchar_t * buf,size_t buflen,usb_bos_t * bosp)52 usba_bos_parse_superspeed(const uchar_t *buf, size_t buflen, usb_bos_t *bosp)
53 {
54 size_t len;
55
56 if (buflen != USB_BOS_SSUSB_PACKED_SIZE) {
57 return (B_FALSE);
58 }
59
60 len = usb_parse_data("ccccsccs", buf, buflen,
61 &bosp->ubos_caps.ubos_ssusb, sizeof (usb_bos_ssusb_t));
62 return (len == sizeof (usb_bos_ssusb_t));
63 }
64
65 static boolean_t
usba_bos_parse_container(const uchar_t * buf,size_t buflen,usb_bos_t * bosp)66 usba_bos_parse_container(const uchar_t *buf, size_t buflen, usb_bos_t *bosp)
67 {
68 size_t len;
69
70 if (buflen != USB_BOS_CONTAINER_PACKED_SIZE) {
71 return (B_FALSE);
72 }
73
74 len = usb_parse_data("cccc16c", buf, buflen,
75 &bosp->ubos_caps.ubos_container, sizeof (usb_bos_container_t));
76 return (len == sizeof (usb_bos_container_t));
77 }
78
79 static boolean_t
usba_bos_parse_precision_time(const uchar_t * buf,size_t buflen,usb_bos_t * bosp)80 usba_bos_parse_precision_time(const uchar_t *buf, size_t buflen,
81 usb_bos_t *bosp)
82 {
83 size_t len;
84
85 if (buflen != USB_BOS_PRECISION_TIME_PACKED_SIZE) {
86 return (B_FALSE);
87 }
88
89 len = usb_parse_data("ccc", buf, buflen, &bosp->ubos_caps.ubos_time,
90 sizeof (usb_bos_precision_time_t));
91 /*
92 * The actual size of this structure will usually be rounded up to four
93 * bytes by the compiler, therefore we need to compare against the
94 * packed size.
95 */
96 return (len == USB_BOS_PRECISION_TIME_PACKED_SIZE);
97 }
98
99 /*
100 * Validate that the BOS looks reasonable. This means the following:
101 *
102 * - We read the whole length of the descriptor
103 * - The total number of capabilities doesn't exceed the expected value
104 * - The length of each device capabilities fits within our expected range
105 *
106 * After we finish that up, go through and save all of the valid BOS
107 * descriptors, unpacking the ones that we actually understand.
108 */
109 static boolean_t
usba_bos_save(usba_device_t * ud,const mblk_t * mp,usb_bos_descr_t * bdesc)110 usba_bos_save(usba_device_t *ud, const mblk_t *mp, usb_bos_descr_t *bdesc)
111 {
112 size_t len = MBLKL(mp);
113 const uchar_t *buf = mp->b_rptr;
114 uint_t ncaps, nalloc;
115 usb_bos_t *bos;
116
117 if (bdesc->bLength != USB_BOS_PACKED_SIZE ||
118 bdesc->bNumDeviceCaps == 0 || len < USB_BOS_PACKED_SIZE ||
119 len < bdesc->wTotalLength) {
120 return (B_FALSE);
121 }
122
123 len = MIN(len, bdesc->wTotalLength);
124 buf += USB_BOS_PACKED_SIZE;
125 len -= USB_BOS_PACKED_SIZE;
126
127 if (len < USB_DEV_CAP_PACKED_SIZE) {
128 return (B_FALSE);
129 }
130
131 ncaps = 0;
132 while (len > 0) {
133 usb_dev_cap_descr_t dev;
134
135 if (usb_parse_data("ccc", buf, len, &dev, sizeof (dev)) !=
136 USB_DEV_CAP_PACKED_SIZE) {
137 return (B_FALSE);
138 }
139
140 if (dev.bDescriptorType != USB_DESCR_TYPE_DEV_CAPABILITY ||
141 dev.bLength > len) {
142 return (B_FALSE);
143 }
144
145 ncaps++;
146 len -= dev.bLength;
147 buf += dev.bLength;
148 }
149
150 if (ncaps != bdesc->bNumDeviceCaps) {
151 return (B_FALSE);
152 }
153
154 nalloc = ncaps;
155 bos = kmem_zalloc(sizeof (usb_bos_t) * nalloc, KM_SLEEP);
156 buf = mp->b_rptr + USB_BOS_PACKED_SIZE;
157 len = MIN(MBLKL(mp), bdesc->wTotalLength) - USB_BOS_PACKED_SIZE;
158 ncaps = 0;
159 while (len > 0) {
160 usb_dev_cap_descr_t dev;
161 boolean_t valid;
162
163 if (usb_parse_data("ccc", buf, len, &dev, sizeof (dev)) !=
164 USB_DEV_CAP_PACKED_SIZE) {
165 goto fail;
166 }
167
168 bos[ncaps].ubos_length = dev.bLength;
169 bos[ncaps].ubos_type = dev.bDevCapabilityType;
170
171 valid = B_FALSE;
172 switch (dev.bDevCapabilityType) {
173 case USB_BOS_TYPE_USB2_EXT:
174 valid = usba_bos_parse_usb2ext(buf, dev.bLength,
175 &bos[ncaps]);
176 break;
177 case USB_BOS_TYPE_SUPERSPEED:
178 valid = usba_bos_parse_superspeed(buf, dev.bLength,
179 &bos[ncaps]);
180 break;
181 case USB_BOS_TYPE_CONTAINER:
182 valid = usba_bos_parse_container(buf, dev.bLength,
183 &bos[ncaps]);
184 break;
185 case USB_BOS_TYPE_PRECISION_TIME:
186 valid = usba_bos_parse_precision_time(buf, dev.bLength,
187 &bos[ncaps]);
188 break;
189 default:
190 /*
191 * Override the type to one that we know isn't used to
192 * indicate that the caller can't rely on the type
193 * that's present here.
194 */
195 bos[ncaps].ubos_type = USB_BOS_TYPE_INVALID;
196 bcopy(buf, bos[ncaps].ubos_caps.ubos_raw, dev.bLength);
197 valid = B_TRUE;
198 break;
199 }
200
201 if (valid) {
202 ncaps++;
203 } else {
204 bos[ncaps].ubos_length = 0;
205 bos[ncaps].ubos_type = USB_BOS_TYPE_INVALID;
206 bzero(bos[ncaps].ubos_caps.ubos_raw,
207 sizeof (bos[ncaps].ubos_caps.ubos_raw));
208 }
209 len -= dev.bLength;
210 buf += dev.bLength;
211 }
212
213 ud->usb_bos_nalloc = nalloc;
214 ud->usb_bos_nents = ncaps;
215 ud->usb_bos = bos;
216
217 return (B_TRUE);
218
219 fail:
220 kmem_free(bos, sizeof (usb_bos_t) * nalloc);
221 return (B_FALSE);
222 }
223
224 /*
225 * Read the Binary Object Store (BOS) data from the device and attempt to parse
226 * it. Do not fail to attach the device if we cannot get all of the information
227 * at this time. While certain aspects of the BOS are required for Windows,
228 * which suggests that we could actually rely on it, we haven't historically.
229 */
230 void
usba_get_binary_object_store(dev_info_t * dip,usba_device_t * ud)231 usba_get_binary_object_store(dev_info_t *dip, usba_device_t *ud)
232 {
233 int rval;
234 mblk_t *mp = NULL;
235 usb_cr_t completion_reason;
236 usb_cb_flags_t cb_flags;
237 usb_pipe_handle_t ph;
238 size_t size;
239 usb_bos_descr_t bos;
240
241 /*
242 * The BOS is only supported on USB 3.x devices. Therefore if the bcdUSB
243 * is greater than USB 2.0, we can check this. Note, USB 3.x devices
244 * that are linked on a USB device will report version 2.1 in the bcdUSB
245 * field.
246 */
247 if (ud->usb_dev_descr->bcdUSB <= 0x200) {
248 return;
249 }
250
251 ph = usba_get_dflt_pipe_handle(dip);
252
253 /*
254 * First get just the BOS descriptor itself.
255 */
256 rval = usb_pipe_sync_ctrl_xfer(dip, ph,
257 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
258 USB_REQ_GET_DESCR, /* bRequest */
259 (USB_DESCR_TYPE_BOS << 8), /* wValue */
260 0, /* wIndex */
261 USB_BOS_PACKED_SIZE, /* wLength */
262 &mp, USB_ATTRS_SHORT_XFER_OK,
263 &completion_reason, &cb_flags, 0);
264
265 if (rval != USB_SUCCESS) {
266 return;
267 }
268
269 size = usba_bos_parse_bos_descr(mp->b_rptr, MBLKL(mp), &bos,
270 sizeof (bos));
271 freemsg(mp);
272 mp = NULL;
273 if (size < USB_BOS_PACKED_SIZE) {
274 return;
275 }
276
277 /*
278 * Check to see if there are any capabilities and if it's worth getting
279 * the whole BOS.
280 */
281 if (bos.bLength != USB_BOS_PACKED_SIZE || bos.bNumDeviceCaps == 0) {
282 return;
283 }
284
285 rval = usb_pipe_sync_ctrl_xfer(dip, ph,
286 USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
287 USB_REQ_GET_DESCR, /* bRequest */
288 (USB_DESCR_TYPE_BOS << 8), /* wValue */
289 0, /* wIndex */
290 bos.wTotalLength, /* wLength */
291 &mp, USB_ATTRS_SHORT_XFER_OK,
292 &completion_reason, &cb_flags, 0);
293
294 if (rval != USB_SUCCESS) {
295 return;
296 }
297
298 size = usba_bos_parse_bos_descr(mp->b_rptr, MBLKL(mp), &bos,
299 sizeof (bos));
300 if (size < USB_BOS_PACKED_SIZE) {
301 freemsg(mp);
302 return;
303 }
304
305 if (!usba_bos_save(ud, mp, &bos)) {
306 freemsg(mp);
307 return;
308 }
309
310 ud->usb_bos_mp = mp;
311 }
312
313 static void
usba_add_superspeed_props(dev_info_t * dip,usb_bos_ssusb_t * ssusb)314 usba_add_superspeed_props(dev_info_t *dip, usb_bos_ssusb_t *ssusb)
315 {
316 char *supported[4];
317 uint_t nsup = 0;
318 char *min;
319
320 if (ssusb->wSpeedsSupported & USB_BOS_SSUSB_SPEED_LOW) {
321 supported[nsup++] = "low-speed";
322 }
323
324 if (ssusb->wSpeedsSupported & USB_BOS_SSUSB_SPEED_FULL) {
325 supported[nsup++] = "full-speed";
326 }
327
328 if (ssusb->wSpeedsSupported & USB_BOS_SSUSB_SPEED_HIGH) {
329 supported[nsup++] = "high-speed";
330 }
331
332 if (ssusb->wSpeedsSupported & USB_BOS_SSUSB_SPEED_SUPER) {
333 supported[nsup++] = "super-speed";
334 }
335
336 if (nsup != 0 && ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
337 "usb-supported-speeds", supported, nsup) != DDI_PROP_SUCCESS) {
338 USB_DPRINTF_L2(DPRINT_MASK_USBA, NULL, "failed to add "
339 "usb-supported-speeds property");
340 }
341
342 switch (ssusb->bFunctionalitySupport) {
343 case 0:
344 min = "low-speed";
345 break;
346 case 1:
347 min = "full-speed";
348 break;
349 case 2:
350 min = "high-speed";
351 break;
352 case 3:
353 min = "super-speed";
354 break;
355 default:
356 min = NULL;
357 }
358
359 if (min != NULL && ndi_prop_update_string(DDI_DEV_T_NONE, dip,
360 "usb-minimum-speed", min) != DDI_PROP_SUCCESS) {
361 USB_DPRINTF_L2(DPRINT_MASK_USBA, NULL, "failed to add "
362 "usb-minimum-speed property");
363 }
364 }
365
366 static void
usba_add_container_props(dev_info_t * dip,usb_bos_container_t * cp)367 usba_add_container_props(dev_info_t *dip, usb_bos_container_t *cp)
368 {
369 if (ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, "usb-container-id",
370 cp->ContainerId, sizeof (cp->ContainerId)) != DDI_PROP_SUCCESS) {
371 USB_DPRINTF_L2(DPRINT_MASK_USBA, NULL, "failed to add "
372 "usb-container-id property");
373 }
374 }
375
376 void
usba_add_binary_object_store_props(dev_info_t * dip,usba_device_t * ud)377 usba_add_binary_object_store_props(dev_info_t *dip, usba_device_t *ud)
378 {
379 uint_t i;
380
381 if (ud->usb_bos == NULL) {
382 return;
383 }
384
385 for (i = 0; i < ud->usb_bos_nents; i++) {
386 usb_bos_t *bos = &ud->usb_bos[i];
387
388 switch (bos->ubos_type) {
389 case USB_BOS_TYPE_SUPERSPEED:
390 usba_add_superspeed_props(dip,
391 &bos->ubos_caps.ubos_ssusb);
392 break;
393 case USB_BOS_TYPE_CONTAINER:
394 usba_add_container_props(dip,
395 &bos->ubos_caps.ubos_container);
396 break;
397 default:
398 /*
399 * This is a capability that we're not going to add
400 * devinfo properties to describe.
401 */
402 continue;
403 }
404 }
405 }
406
407 void
usba_free_binary_object_store(usba_device_t * ud)408 usba_free_binary_object_store(usba_device_t *ud)
409 {
410 if (ud->usb_bos_mp != NULL) {
411 freemsg(ud->usb_bos_mp);
412 ud->usb_bos_mp = NULL;
413 }
414
415 if (ud->usb_bos != NULL) {
416 kmem_free(ud->usb_bos, sizeof (usb_bos_t) * ud->usb_bos_nalloc);
417 ud->usb_bos = NULL;
418 ud->usb_bos_nalloc = ud->usb_bos_nents = 0;
419 }
420 }
421