diff options
author | Craig W. Nadler <craig@nadler.us> | 2007-06-15 23:14:35 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 19:34:40 -0400 |
commit | 165fe97ed6107d3cde63592d5ac36400a5eb9f6f (patch) | |
tree | 824bb475b4f36af465989c5dac62f4097a1bd01c | |
parent | 50d2dc7266573dfbdc84fc207494dd21315782ef (diff) |
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>
-rw-r--r-- | drivers/usb/core/config.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 26 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 31 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 34 | ||||
-rw-r--r-- | include/linux/usb.h | 10 |
5 files changed, 116 insertions, 0 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 5e113db41b59..cb69aa1e02e8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c | |||
@@ -295,6 +295,7 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, | |||
295 | struct usb_descriptor_header *header; | 295 | struct usb_descriptor_header *header; |
296 | int len, retval; | 296 | int len, retval; |
297 | u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; | 297 | u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; |
298 | unsigned iad_num = 0; | ||
298 | 299 | ||
299 | memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); | 300 | memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); |
300 | if (config->desc.bDescriptorType != USB_DT_CONFIG || | 301 | if (config->desc.bDescriptorType != USB_DT_CONFIG || |
@@ -372,6 +373,20 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, | |||
372 | ++n; | 373 | ++n; |
373 | } | 374 | } |
374 | 375 | ||
376 | } else if (header->bDescriptorType == | ||
377 | USB_DT_INTERFACE_ASSOCIATION) { | ||
378 | if (iad_num == USB_MAXIADS) { | ||
379 | dev_warn(ddev, "found more Interface " | ||
380 | "Association Descriptors " | ||
381 | "than allocated for in " | ||
382 | "configuration %d\n", cfgno); | ||
383 | } else { | ||
384 | config->intf_assoc[iad_num] = | ||
385 | (struct usb_interface_assoc_descriptor | ||
386 | *)header; | ||
387 | iad_num++; | ||
388 | } | ||
389 | |||
375 | } else if (header->bDescriptorType == USB_DT_DEVICE || | 390 | } else if (header->bDescriptorType == USB_DT_DEVICE || |
376 | header->bDescriptorType == USB_DT_CONFIG) | 391 | header->bDescriptorType == USB_DT_CONFIG) |
377 | dev_warn(ddev, "config %d contains an unexpected " | 392 | dev_warn(ddev, "config %d contains an unexpected " |
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 6753ca059ee4..87c794d60aa0 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c | |||
@@ -102,6 +102,10 @@ static const char *format_config = | |||
102 | /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ | 102 | /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ |
103 | "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; | 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 | static const char *format_iface = | 109 | static const char *format_iface = |
106 | /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ | 110 | /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ |
107 | "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; | 111 | "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; |
@@ -146,6 +150,7 @@ static const struct class_info clas_info[] = | |||
146 | {USB_CLASS_STILL_IMAGE, "still"}, | 150 | {USB_CLASS_STILL_IMAGE, "still"}, |
147 | {USB_CLASS_CSCID, "scard"}, | 151 | {USB_CLASS_CSCID, "scard"}, |
148 | {USB_CLASS_CONTENT_SEC, "c-sec"}, | 152 | {USB_CLASS_CONTENT_SEC, "c-sec"}, |
153 | {USB_CLASS_VIDEO, "video"}, | ||
149 | {-1, "unk."} /* leave as last */ | 154 | {-1, "unk."} /* leave as last */ |
150 | }; | 155 | }; |
151 | 156 | ||
@@ -286,6 +291,21 @@ static char *usb_dump_interface( | |||
286 | return start; | 291 | return start; |
287 | } | 292 | } |
288 | 293 | ||
294 | static char *usb_dump_iad_descriptor(char *start, char *end, | ||
295 | const struct usb_interface_assoc_descriptor *iad) | ||
296 | { | ||
297 | if (start > end) | ||
298 | return start; | ||
299 | start += sprintf(start, format_iad, | ||
300 | iad->bFirstInterface, | ||
301 | iad->bInterfaceCount, | ||
302 | iad->bFunctionClass, | ||
303 | class_decode(iad->bFunctionClass), | ||
304 | iad->bFunctionSubClass, | ||
305 | iad->bFunctionProtocol); | ||
306 | return start; | ||
307 | } | ||
308 | |||
289 | /* TBD: | 309 | /* TBD: |
290 | * 0. TBDs | 310 | * 0. TBDs |
291 | * 1. marking active interface altsettings (code lists all, but should mark | 311 | * 1. marking active interface altsettings (code lists all, but should mark |
@@ -322,6 +342,12 @@ static char *usb_dump_config ( | |||
322 | if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ | 342 | if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ |
323 | return start + sprintf(start, "(null Cfg. desc.)\n"); | 343 | return start + sprintf(start, "(null Cfg. desc.)\n"); |
324 | start = usb_dump_config_descriptor(start, end, &config->desc, active); | 344 | start = usb_dump_config_descriptor(start, end, &config->desc, active); |
345 | for (i = 0; i < USB_MAXIADS; i++) { | ||
346 | if (config->intf_assoc[i] == NULL) | ||
347 | break; | ||
348 | start = usb_dump_iad_descriptor(start, end, | ||
349 | config->intf_assoc[i]); | ||
350 | } | ||
325 | for (i = 0; i < config->desc.bNumInterfaces; i++) { | 351 | for (i = 0; i < config->desc.bNumInterfaces; i++) { |
326 | intfc = config->intf_cache[i]; | 352 | intfc = config->intf_cache[i]; |
327 | interface = config->interface[i]; | 353 | interface = config->interface[i]; |
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 4c1432314711..530e854961ce 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -1384,6 +1384,36 @@ struct device_type usb_if_device_type = { | |||
1384 | .uevent = usb_if_uevent, | 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 | * usb_set_configuration - Makes a particular device setting be current | 1418 | * usb_set_configuration - Makes a particular device setting be current |
1389 | * @dev: the device whose configuration is being updated | 1419 | * @dev: the device whose configuration is being updated |
@@ -1530,6 +1560,7 @@ free_interfaces: | |||
1530 | intfc = cp->intf_cache[i]; | 1560 | intfc = cp->intf_cache[i]; |
1531 | intf->altsetting = intfc->altsetting; | 1561 | intf->altsetting = intfc->altsetting; |
1532 | intf->num_altsetting = intfc->num_altsetting; | 1562 | intf->num_altsetting = intfc->num_altsetting; |
1563 | intf->intf_assoc = find_iad(dev, cp, i); | ||
1533 | kref_get(&intfc->ref); | 1564 | kref_get(&intfc->ref); |
1534 | 1565 | ||
1535 | alt = usb_altnum_to_altsetting(intf, 0); | 1566 | alt = usb_altnum_to_altsetting(intf, 0); |
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5dfe31bc32ba..d47ae89154a7 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -495,6 +495,25 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) | |||
495 | sysfs_remove_group(&dev->kobj, &dev_attr_grp); | 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 | /* Interface fields */ | 517 | /* Interface fields */ |
499 | #define usb_intf_attr(field, format_string) \ | 518 | #define usb_intf_attr(field, format_string) \ |
500 | static ssize_t \ | 519 | static ssize_t \ |
@@ -558,6 +577,18 @@ static ssize_t show_modalias(struct device *dev, | |||
558 | } | 577 | } |
559 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); | 578 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); |
560 | 579 | ||
580 | static struct attribute *intf_assoc_attrs[] = { | ||
581 | &dev_attr_iad_bFirstInterface.attr, | ||
582 | &dev_attr_iad_bInterfaceCount.attr, | ||
583 | &dev_attr_iad_bFunctionClass.attr, | ||
584 | &dev_attr_iad_bFunctionSubClass.attr, | ||
585 | &dev_attr_iad_bFunctionProtocol.attr, | ||
586 | NULL, | ||
587 | }; | ||
588 | static struct attribute_group intf_assoc_attr_grp = { | ||
589 | .attrs = intf_assoc_attrs, | ||
590 | }; | ||
591 | |||
561 | static struct attribute *intf_attrs[] = { | 592 | static struct attribute *intf_attrs[] = { |
562 | &dev_attr_bInterfaceNumber.attr, | 593 | &dev_attr_bInterfaceNumber.attr, |
563 | &dev_attr_bAlternateSetting.attr, | 594 | &dev_attr_bAlternateSetting.attr, |
@@ -609,6 +640,8 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf) | |||
609 | alt->string = usb_cache_string(udev, alt->desc.iInterface); | 640 | alt->string = usb_cache_string(udev, alt->desc.iInterface); |
610 | if (alt->string) | 641 | if (alt->string) |
611 | retval = device_create_file(dev, &dev_attr_interface); | 642 | retval = device_create_file(dev, &dev_attr_interface); |
643 | if (intf->intf_assoc) | ||
644 | retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp); | ||
612 | usb_create_intf_ep_files(intf, udev); | 645 | usb_create_intf_ep_files(intf, udev); |
613 | return 0; | 646 | return 0; |
614 | } | 647 | } |
@@ -620,4 +653,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf) | |||
620 | usb_remove_intf_ep_files(intf); | 653 | usb_remove_intf_ep_files(intf); |
621 | device_remove_file(dev, &dev_attr_interface); | 654 | device_remove_file(dev, &dev_attr_interface); |
622 | sysfs_remove_group(&dev->kobj, &intf_attr_grp); | 655 | sysfs_remove_group(&dev->kobj, &intf_attr_grp); |
656 | sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp); | ||
623 | } | 657 | } |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 533c32374e01..7a60946df3b6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -146,6 +146,10 @@ struct usb_interface { | |||
146 | * active alternate setting */ | 146 | * active alternate setting */ |
147 | unsigned num_altsetting; /* number of alternate settings */ | 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 | int minor; /* minor number this interface is | 153 | int minor; /* minor number this interface is |
150 | * bound to */ | 154 | * bound to */ |
151 | enum usb_interface_condition condition; /* state of binding */ | 155 | enum usb_interface_condition condition; /* state of binding */ |
@@ -175,6 +179,7 @@ void usb_put_intf(struct usb_interface *intf); | |||
175 | 179 | ||
176 | /* this maximum is arbitrary */ | 180 | /* this maximum is arbitrary */ |
177 | #define USB_MAXINTERFACES 32 | 181 | #define USB_MAXINTERFACES 32 |
182 | #define USB_MAXIADS USB_MAXINTERFACES/2 | ||
178 | 183 | ||
179 | /** | 184 | /** |
180 | * struct usb_interface_cache - long-term representation of a device interface | 185 | * struct usb_interface_cache - long-term representation of a device interface |
@@ -245,6 +250,11 @@ struct usb_host_config { | |||
245 | struct usb_config_descriptor desc; | 250 | struct usb_config_descriptor desc; |
246 | 251 | ||
247 | char *string; /* iConfiguration string, if present */ | 252 | char *string; /* iConfiguration string, if present */ |
253 | |||
254 | /* List of any Interface Association Descriptors in this | ||
255 | * configuration. */ | ||
256 | struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; | ||
257 | |||
248 | /* the interfaces associated with this configuration, | 258 | /* the interfaces associated with this configuration, |
249 | * stored in no particular order */ | 259 | * stored in no particular order */ |
250 | struct usb_interface *interface[USB_MAXINTERFACES]; | 260 | struct usb_interface *interface[USB_MAXINTERFACES]; |