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 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 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 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 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 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 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 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 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 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 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 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