Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: add IAD support to usbfs and sysfs

USB_IAD: Adds support for USB Interface Association Descriptors.

This patch adds support to the USB host stack for parsing, storing, and
displaying Interface Association Descriptors. In /proc/bus/usb/devices
lines starting with A: show the fields in an IAD. In sysfs if an
interface on a USB device is referenced by an IAD the following files
will be added to the sysfs directory for that interface:
iad_bFirstInterface, iad_bInterfaceCount, iad_bFunctionClass, and
iad_bFunctionSubClass, iad_bFunctionProtocol

Signed-off-by: Craig W. Nadler <craig@nadler.us>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Craig W. Nadler and committed by
Greg Kroah-Hartman
165fe97e 50d2dc72

+116
+15
drivers/usb/core/config.c
··· 295 295 struct usb_descriptor_header *header; 296 296 int len, retval; 297 297 u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; 298 + unsigned iad_num = 0; 298 299 299 300 memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); 300 301 if (config->desc.bDescriptorType != USB_DT_CONFIG || ··· 371 370 inums[n] = inum; 372 371 nalts[n] = 1; 373 372 ++n; 373 + } 374 + 375 + } else if (header->bDescriptorType == 376 + USB_DT_INTERFACE_ASSOCIATION) { 377 + if (iad_num == USB_MAXIADS) { 378 + dev_warn(ddev, "found more Interface " 379 + "Association Descriptors " 380 + "than allocated for in " 381 + "configuration %d\n", cfgno); 382 + } else { 383 + config->intf_assoc[iad_num] = 384 + (struct usb_interface_assoc_descriptor 385 + *)header; 386 + iad_num++; 374 387 } 375 388 376 389 } else if (header->bDescriptorType == USB_DT_DEVICE ||
+26
drivers/usb/core/devices.c
··· 102 102 /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ 103 103 "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; 104 104 105 + static const char *format_iad = 106 + /* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */ 107 + "A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n"; 108 + 105 109 static const char *format_iface = 106 110 /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ 107 111 "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; ··· 150 146 {USB_CLASS_STILL_IMAGE, "still"}, 151 147 {USB_CLASS_CSCID, "scard"}, 152 148 {USB_CLASS_CONTENT_SEC, "c-sec"}, 149 + {USB_CLASS_VIDEO, "video"}, 153 150 {-1, "unk."} /* leave as last */ 154 151 }; 155 152 ··· 291 286 return start; 292 287 } 293 288 289 + static char *usb_dump_iad_descriptor(char *start, char *end, 290 + const struct usb_interface_assoc_descriptor *iad) 291 + { 292 + if (start > end) 293 + return start; 294 + start += sprintf(start, format_iad, 295 + iad->bFirstInterface, 296 + iad->bInterfaceCount, 297 + iad->bFunctionClass, 298 + class_decode(iad->bFunctionClass), 299 + iad->bFunctionSubClass, 300 + iad->bFunctionProtocol); 301 + return start; 302 + } 303 + 294 304 /* TBD: 295 305 * 0. TBDs 296 306 * 1. marking active interface altsettings (code lists all, but should mark ··· 342 322 if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ 343 323 return start + sprintf(start, "(null Cfg. desc.)\n"); 344 324 start = usb_dump_config_descriptor(start, end, &config->desc, active); 325 + for (i = 0; i < USB_MAXIADS; i++) { 326 + if (config->intf_assoc[i] == NULL) 327 + break; 328 + start = usb_dump_iad_descriptor(start, end, 329 + config->intf_assoc[i]); 330 + } 345 331 for (i = 0; i < config->desc.bNumInterfaces; i++) { 346 332 intfc = config->intf_cache[i]; 347 333 interface = config->interface[i];
+31
drivers/usb/core/message.c
··· 1384 1384 .uevent = usb_if_uevent, 1385 1385 }; 1386 1386 1387 + static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, 1388 + struct usb_host_config *config, 1389 + u8 inum) 1390 + { 1391 + struct usb_interface_assoc_descriptor *retval = NULL; 1392 + struct usb_interface_assoc_descriptor *intf_assoc; 1393 + int first_intf; 1394 + int last_intf; 1395 + int i; 1396 + 1397 + for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { 1398 + intf_assoc = config->intf_assoc[i]; 1399 + if (intf_assoc->bInterfaceCount == 0) 1400 + continue; 1401 + 1402 + first_intf = intf_assoc->bFirstInterface; 1403 + last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); 1404 + if (inum >= first_intf && inum <= last_intf) { 1405 + if (!retval) 1406 + retval = intf_assoc; 1407 + else 1408 + dev_err(&dev->dev, "Interface #%d referenced" 1409 + " by multiple IADs\n", inum); 1410 + } 1411 + } 1412 + 1413 + return retval; 1414 + } 1415 + 1416 + 1387 1417 /* 1388 1418 * usb_set_configuration - Makes a particular device setting be current 1389 1419 * @dev: the device whose configuration is being updated ··· 1560 1530 intfc = cp->intf_cache[i]; 1561 1531 intf->altsetting = intfc->altsetting; 1562 1532 intf->num_altsetting = intfc->num_altsetting; 1533 + intf->intf_assoc = find_iad(dev, cp, i); 1563 1534 kref_get(&intfc->ref); 1564 1535 1565 1536 alt = usb_altnum_to_altsetting(intf, 0);
+34
drivers/usb/core/sysfs.c
··· 495 495 sysfs_remove_group(&dev->kobj, &dev_attr_grp); 496 496 } 497 497 498 + /* Interface Accociation Descriptor fields */ 499 + #define usb_intf_assoc_attr(field, format_string) \ 500 + static ssize_t \ 501 + show_iad_##field (struct device *dev, struct device_attribute *attr, \ 502 + char *buf) \ 503 + { \ 504 + struct usb_interface *intf = to_usb_interface (dev); \ 505 + \ 506 + return sprintf (buf, format_string, \ 507 + intf->intf_assoc->field); \ 508 + } \ 509 + static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL); 510 + 511 + usb_intf_assoc_attr (bFirstInterface, "%02x\n") 512 + usb_intf_assoc_attr (bInterfaceCount, "%02d\n") 513 + usb_intf_assoc_attr (bFunctionClass, "%02x\n") 514 + usb_intf_assoc_attr (bFunctionSubClass, "%02x\n") 515 + usb_intf_assoc_attr (bFunctionProtocol, "%02x\n") 516 + 498 517 /* Interface fields */ 499 518 #define usb_intf_attr(field, format_string) \ 500 519 static ssize_t \ ··· 577 558 } 578 559 static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); 579 560 561 + static struct attribute *intf_assoc_attrs[] = { 562 + &dev_attr_iad_bFirstInterface.attr, 563 + &dev_attr_iad_bInterfaceCount.attr, 564 + &dev_attr_iad_bFunctionClass.attr, 565 + &dev_attr_iad_bFunctionSubClass.attr, 566 + &dev_attr_iad_bFunctionProtocol.attr, 567 + NULL, 568 + }; 569 + static struct attribute_group intf_assoc_attr_grp = { 570 + .attrs = intf_assoc_attrs, 571 + }; 572 + 580 573 static struct attribute *intf_attrs[] = { 581 574 &dev_attr_bInterfaceNumber.attr, 582 575 &dev_attr_bAlternateSetting.attr, ··· 640 609 alt->string = usb_cache_string(udev, alt->desc.iInterface); 641 610 if (alt->string) 642 611 retval = device_create_file(dev, &dev_attr_interface); 612 + if (intf->intf_assoc) 613 + retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp); 643 614 usb_create_intf_ep_files(intf, udev); 644 615 return 0; 645 616 } ··· 653 620 usb_remove_intf_ep_files(intf); 654 621 device_remove_file(dev, &dev_attr_interface); 655 622 sysfs_remove_group(&dev->kobj, &intf_attr_grp); 623 + sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp); 656 624 }
+10
include/linux/usb.h
··· 146 146 * active alternate setting */ 147 147 unsigned num_altsetting; /* number of alternate settings */ 148 148 149 + /* If there is an interface association descriptor then it will list 150 + * the associated interfaces */ 151 + struct usb_interface_assoc_descriptor *intf_assoc; 152 + 149 153 int minor; /* minor number this interface is 150 154 * bound to */ 151 155 enum usb_interface_condition condition; /* state of binding */ ··· 179 175 180 176 /* this maximum is arbitrary */ 181 177 #define USB_MAXINTERFACES 32 178 + #define USB_MAXIADS USB_MAXINTERFACES/2 182 179 183 180 /** 184 181 * struct usb_interface_cache - long-term representation of a device interface ··· 250 245 struct usb_config_descriptor desc; 251 246 252 247 char *string; /* iConfiguration string, if present */ 248 + 249 + /* List of any Interface Association Descriptors in this 250 + * configuration. */ 251 + struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; 252 + 253 253 /* the interfaces associated with this configuration, 254 254 * stored in no particular order */ 255 255 struct usb_interface *interface[USB_MAXINTERFACES];