aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/config.c15
-rw-r--r--drivers/usb/core/devices.c26
-rw-r--r--drivers/usb/core/message.c31
-rw-r--r--drivers/usb/core/sysfs.c34
-rw-r--r--include/linux/usb.h10
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
105static 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
105static const char *format_iface = 109static 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
294static 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
1387static 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) \
500static ssize_t \
501show_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} \
509static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL);
510
511usb_intf_assoc_attr (bFirstInterface, "%02x\n")
512usb_intf_assoc_attr (bInterfaceCount, "%02d\n")
513usb_intf_assoc_attr (bFunctionClass, "%02x\n")
514usb_intf_assoc_attr (bFunctionSubClass, "%02x\n")
515usb_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) \
500static ssize_t \ 519static ssize_t \
@@ -558,6 +577,18 @@ static ssize_t show_modalias(struct device *dev,
558} 577}
559static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); 578static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
560 579
580static 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};
588static struct attribute_group intf_assoc_attr_grp = {
589 .attrs = intf_assoc_attrs,
590};
591
561static struct attribute *intf_attrs[] = { 592static 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];