Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 165fe97e authored by Craig W. Nadler's avatar Craig W. Nadler Committed by Greg Kroah-Hartman
Browse files

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: default avatarCraig W. Nadler <craig@nadler.us>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 50d2dc72
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -295,6 +295,7 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
	struct usb_descriptor_header *header;
	int len, retval;
	u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
	unsigned iad_num = 0;

	memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
	if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -372,6 +373,20 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
				++n;
			}

		} else if (header->bDescriptorType ==
				USB_DT_INTERFACE_ASSOCIATION) {
			if (iad_num == USB_MAXIADS) {
				dev_warn(ddev, "found more Interface "
					       "Association Descriptors "
					       "than allocated for in "
					       "configuration %d\n", cfgno);
			} else {
				config->intf_assoc[iad_num] =
					(struct usb_interface_assoc_descriptor
					*)header;
				iad_num++;
			}

		} else if (header->bDescriptorType == USB_DT_DEVICE ||
			    header->bDescriptorType == USB_DT_CONFIG)
			dev_warn(ddev, "config %d contains an unexpected "
+26 −0
Original line number Diff line number Diff line
@@ -102,6 +102,10 @@ static const char *format_config =
/* C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
  "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
  
static const char *format_iad =
/* A:  FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
  "A:  FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";

static const char *format_iface =
/* I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
  "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[] =
	{USB_CLASS_STILL_IMAGE,		"still"},
	{USB_CLASS_CSCID,		"scard"},
	{USB_CLASS_CONTENT_SEC,		"c-sec"},
	{USB_CLASS_VIDEO,		"video"},
	{-1,				"unk."}		/* leave as last */
};

@@ -286,6 +291,21 @@ static char *usb_dump_interface(
	return start;
}

static char *usb_dump_iad_descriptor(char *start, char *end,
	const struct usb_interface_assoc_descriptor *iad)
{
	if (start > end)
		return start;
	start += sprintf(start, format_iad,
			 iad->bFirstInterface,
			 iad->bInterfaceCount,
			 iad->bFunctionClass,
			 class_decode(iad->bFunctionClass),
			 iad->bFunctionSubClass,
			 iad->bFunctionProtocol);
	return start;
}

/* TBD:
 * 0. TBDs
 * 1. marking active interface altsettings (code lists all, but should mark
@@ -322,6 +342,12 @@ static char *usb_dump_config (
	if (!config)		/* getting these some in 2.3.7; none in 2.3.6 */
		return start + sprintf(start, "(null Cfg. desc.)\n");
	start = usb_dump_config_descriptor(start, end, &config->desc, active);
	for (i = 0; i < USB_MAXIADS; i++) {
		if (config->intf_assoc[i] == NULL)
			break;
		start = usb_dump_iad_descriptor(start, end,
					config->intf_assoc[i]);
	}
	for (i = 0; i < config->desc.bNumInterfaces; i++) {
		intfc = config->intf_cache[i];
		interface = config->interface[i];
+31 −0
Original line number Diff line number Diff line
@@ -1384,6 +1384,36 @@ struct device_type usb_if_device_type = {
	.uevent =	usb_if_uevent,
};

static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
						       struct usb_host_config *config,
						       u8 inum)
{
	struct usb_interface_assoc_descriptor *retval = NULL;
	struct usb_interface_assoc_descriptor *intf_assoc;
	int first_intf;
	int last_intf;
	int i;

	for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) {
		intf_assoc = config->intf_assoc[i];
		if (intf_assoc->bInterfaceCount == 0)
			continue;

		first_intf = intf_assoc->bFirstInterface;
		last_intf = first_intf + (intf_assoc->bInterfaceCount - 1);
		if (inum >= first_intf && inum <= last_intf) {
			if (!retval)
				retval = intf_assoc;
			else
				dev_err(&dev->dev, "Interface #%d referenced"
					" by multiple IADs\n", inum);
		}
	}

	return retval;
}


/*
 * usb_set_configuration - Makes a particular device setting be current
 * @dev: the device whose configuration is being updated
@@ -1530,6 +1560,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
		intfc = cp->intf_cache[i];
		intf->altsetting = intfc->altsetting;
		intf->num_altsetting = intfc->num_altsetting;
		intf->intf_assoc = find_iad(dev, cp, i);
		kref_get(&intfc->ref);

		alt = usb_altnum_to_altsetting(intf, 0);
+34 −0
Original line number Diff line number Diff line
@@ -495,6 +495,25 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
	sysfs_remove_group(&dev->kobj, &dev_attr_grp);
}

/* Interface Accociation Descriptor fields */
#define usb_intf_assoc_attr(field, format_string)			\
static ssize_t								\
show_iad_##field (struct device *dev, struct device_attribute *attr,	\
		char *buf)						\
{									\
	struct usb_interface *intf = to_usb_interface (dev);		\
									\
	return sprintf (buf, format_string,				\
			intf->intf_assoc->field); 		\
}									\
static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL);

usb_intf_assoc_attr (bFirstInterface, "%02x\n")
usb_intf_assoc_attr (bInterfaceCount, "%02d\n")
usb_intf_assoc_attr (bFunctionClass, "%02x\n")
usb_intf_assoc_attr (bFunctionSubClass, "%02x\n")
usb_intf_assoc_attr (bFunctionProtocol, "%02x\n")

/* Interface fields */
#define usb_intf_attr(field, format_string)				\
static ssize_t								\
@@ -558,6 +577,18 @@ static ssize_t show_modalias(struct device *dev,
}
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);

static struct attribute *intf_assoc_attrs[] = {
	&dev_attr_iad_bFirstInterface.attr,
	&dev_attr_iad_bInterfaceCount.attr,
	&dev_attr_iad_bFunctionClass.attr,
	&dev_attr_iad_bFunctionSubClass.attr,
	&dev_attr_iad_bFunctionProtocol.attr,
	NULL,
};
static struct attribute_group intf_assoc_attr_grp = {
	.attrs = intf_assoc_attrs,
};

static struct attribute *intf_attrs[] = {
	&dev_attr_bInterfaceNumber.attr,
	&dev_attr_bAlternateSetting.attr,
@@ -609,6 +640,8 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf)
		alt->string = usb_cache_string(udev, alt->desc.iInterface);
	if (alt->string)
		retval = device_create_file(dev, &dev_attr_interface);
	if (intf->intf_assoc)
		retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp);
	usb_create_intf_ep_files(intf, udev);
	return 0;
}
@@ -620,4 +653,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf)
	usb_remove_intf_ep_files(intf);
	device_remove_file(dev, &dev_attr_interface);
	sysfs_remove_group(&dev->kobj, &intf_attr_grp);
	sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp);
}
+10 −0
Original line number Diff line number Diff line
@@ -146,6 +146,10 @@ struct usb_interface {
					 * active alternate setting */
	unsigned num_altsetting;	/* number of alternate settings */

	/* If there is an interface association descriptor then it will list
	 * the associated interfaces */
	struct usb_interface_assoc_descriptor *intf_assoc;

	int minor;			/* minor number this interface is
					 * bound to */
	enum usb_interface_condition condition;		/* state of binding */
@@ -175,6 +179,7 @@ void usb_put_intf(struct usb_interface *intf);

/* this maximum is arbitrary */
#define USB_MAXINTERFACES	32
#define USB_MAXIADS		USB_MAXINTERFACES/2

/**
 * struct usb_interface_cache - long-term representation of a device interface
@@ -245,6 +250,11 @@ struct usb_host_config {
	struct usb_config_descriptor	desc;

	char *string;		/* iConfiguration string, if present */

	/* List of any Interface Association Descriptors in this
	 * configuration. */
	struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];

	/* the interfaces associated with this configuration,
	 * stored in no particular order */
	struct usb_interface *interface[USB_MAXINTERFACES];