Commit b97503ff authored by Michal Nazarewicz's avatar Michal Nazarewicz Committed by Greg Kroah-Hartman

USB: Interface Association Descriptors added to CDC & RNDIS

Without Interface Association Descriptor, the CDC serial and
RNDIS functions did not work correctly when added to a
composite gadget with other functions.  This is because, it
defined two interfaces and some hosts tried to treat each
interface separatelly.
Signed-off-by: default avatarMichal Nazarewicz <m.nazarewicz@samsung.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 9c610213
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation * Copyright (C) 2008 by Nokia Corporation
* Copyright (C) 2009 by Samsung Electronics
* Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
* *
* This software is distributed under the terms of the GNU General * This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation, * Public License ("GPL") as published by the Free Software Foundation,
...@@ -99,6 +101,20 @@ static inline struct f_acm *port_to_acm(struct gserial *p) ...@@ -99,6 +101,20 @@ static inline struct f_acm *port_to_acm(struct gserial *p)
/* interface and class descriptors: */ /* interface and class descriptors: */
static struct usb_interface_assoc_descriptor
acm_iad_descriptor = {
.bLength = sizeof acm_iad_descriptor,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
/* .bFirstInterface = DYNAMIC, */
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_CDC_PROTO_NONE,
/* .iFunction = DYNAMIC */
};
static struct usb_interface_descriptor acm_control_interface_desc __initdata = { static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
.bLength = USB_DT_INTERFACE_SIZE, .bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE, .bDescriptorType = USB_DT_INTERFACE,
...@@ -178,6 +194,7 @@ static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = { ...@@ -178,6 +194,7 @@ static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
}; };
static struct usb_descriptor_header *acm_fs_function[] __initdata = { static struct usb_descriptor_header *acm_fs_function[] __initdata = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc, (struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc, (struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor, (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
...@@ -216,6 +233,7 @@ static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = { ...@@ -216,6 +233,7 @@ static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
}; };
static struct usb_descriptor_header *acm_hs_function[] __initdata = { static struct usb_descriptor_header *acm_hs_function[] __initdata = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc, (struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc, (struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor, (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
...@@ -232,11 +250,13 @@ static struct usb_descriptor_header *acm_hs_function[] __initdata = { ...@@ -232,11 +250,13 @@ static struct usb_descriptor_header *acm_hs_function[] __initdata = {
#define ACM_CTRL_IDX 0 #define ACM_CTRL_IDX 0
#define ACM_DATA_IDX 1 #define ACM_DATA_IDX 1
#define ACM_IAD_IDX 2
/* static strings, in UTF-8 */ /* static strings, in UTF-8 */
static struct usb_string acm_string_defs[] = { static struct usb_string acm_string_defs[] = {
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
[ACM_DATA_IDX].s = "CDC ACM Data", [ACM_DATA_IDX].s = "CDC ACM Data",
[ACM_IAD_IDX ].s = "CDC Serial",
{ /* ZEROES END LIST */ }, { /* ZEROES END LIST */ },
}; };
...@@ -563,6 +583,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -563,6 +583,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0) if (status < 0)
goto fail; goto fail;
acm->ctrl_id = status; acm->ctrl_id = status;
acm_iad_descriptor.bFirstInterface = status;
acm_control_interface_desc.bInterfaceNumber = status; acm_control_interface_desc.bInterfaceNumber = status;
acm_union_desc .bMasterInterface0 = status; acm_union_desc .bMasterInterface0 = status;
...@@ -732,6 +753,13 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num) ...@@ -732,6 +753,13 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
acm_string_defs[ACM_DATA_IDX].id = status; acm_string_defs[ACM_DATA_IDX].id = status;
acm_data_interface_desc.iInterface = status; acm_data_interface_desc.iInterface = status;
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_IAD_IDX].id = status;
acm_iad_descriptor.iFunction = status;
} }
/* allocate and initialize one new instance */ /* allocate and initialize one new instance */
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation * Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 Samsung Electronics
* Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -149,8 +151,8 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { ...@@ -149,8 +151,8 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
.bDataInterface = 0x01, .bDataInterface = 0x01,
}; };
static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
.bLength = sizeof acm_descriptor, .bLength = sizeof rndis_acm_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE, .bDescriptorSubType = USB_CDC_ACM_TYPE,
...@@ -179,6 +181,20 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = { ...@@ -179,6 +181,20 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = {
/* .iInterface = DYNAMIC */ /* .iInterface = DYNAMIC */
}; };
static struct usb_interface_assoc_descriptor
rndis_iad_descriptor = {
.bLength = sizeof rndis_iad_descriptor,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
.bFunctionProtocol = USB_CDC_PROTO_NONE,
/* .iFunction = DYNAMIC */
};
/* full speed support: */ /* full speed support: */
static struct usb_endpoint_descriptor fs_notify_desc __initdata = { static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
...@@ -208,11 +224,12 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = { ...@@ -208,11 +224,12 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {
}; };
static struct usb_descriptor_header *eth_fs_function[] __initdata = { static struct usb_descriptor_header *eth_fs_function[] __initdata = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */ /* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor, (struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor, (struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &fs_notify_desc, (struct usb_descriptor_header *) &fs_notify_desc,
/* data interface has no altsetting */ /* data interface has no altsetting */
...@@ -252,11 +269,12 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = { ...@@ -252,11 +269,12 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {
}; };
static struct usb_descriptor_header *eth_hs_function[] __initdata = { static struct usb_descriptor_header *eth_hs_function[] __initdata = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */ /* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor, (struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor, (struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &hs_notify_desc, (struct usb_descriptor_header *) &hs_notify_desc,
/* data interface has no altsetting */ /* data interface has no altsetting */
...@@ -271,6 +289,7 @@ static struct usb_descriptor_header *eth_hs_function[] __initdata = { ...@@ -271,6 +289,7 @@ static struct usb_descriptor_header *eth_hs_function[] __initdata = {
static struct usb_string rndis_string_defs[] = { static struct usb_string rndis_string_defs[] = {
[0].s = "RNDIS Communications Control", [0].s = "RNDIS Communications Control",
[1].s = "RNDIS Ethernet Data", [1].s = "RNDIS Ethernet Data",
[2].s = "RNDIS",
{ } /* end of list */ { } /* end of list */
}; };
...@@ -587,6 +606,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -587,6 +606,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0) if (status < 0)
goto fail; goto fail;
rndis->ctrl_id = status; rndis->ctrl_id = status;
rndis_iad_descriptor.bFirstInterface = status;
rndis_control_intf.bInterfaceNumber = status; rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status; rndis_union_desc.bMasterInterface0 = status;
...@@ -798,6 +818,13 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ...@@ -798,6 +818,13 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return status; return status;
rndis_string_defs[1].id = status; rndis_string_defs[1].id = status;
rndis_data_intf.iInterface = status; rndis_data_intf.iInterface = status;
/* IAD iFunction label */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
rndis_string_defs[2].id = status;
rndis_iad_descriptor.iFunction = status;
} }
/* allocate and initialize one new instance */ /* allocate and initialize one new instance */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment