aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-04-27 22:58:14 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-16 00:44:49 -0400
commit663c30d0829d556efabd5fbd98fb8473da7fe694 (patch)
tree1d2566aff3b6af6e05cf3a7a0fc1c1fb17bfe5db /drivers/usb
parentd0e96f5a71a032ced0c35f521c1cbd67e816922a (diff)
USB: Parse and store the SuperSpeed endpoint companion descriptors.
The USB 3.0 bus specification added an "Endpoint Companion" descriptor that is supposed to follow all SuperSpeed Endpoint descriptors. This descriptor is used to extend the bus protocol to allow more packets to be sent to an endpoint per "microframe". The word microframe was removed from the USB 3.0 specification because the host controller does not send Start Of Frame (SOF) symbols down the USB 3.0 wires. The descriptor defines a bMaxBurst field, which indicates the number of packets of wMaxPacketSize that a SuperSpeed device can send or recieve in a service interval. All non-control endpoints may set this value as high as 16 packets (bMaxBurst = 15). The descriptor also allows isochronous endpoints to further specify that they can send and receive multiple bursts per service interval. The bmAttributes allows them to specify a "Mult" of up to 3 (bmAttributes = 2). Bulk endpoints use bmAttributes to report the number of "Streams" they support. This was an extension of the endpoint pipe concept to allow multiple mass storage device commands to be outstanding for one bulk endpoint at a time. This should allow USB 3.0 mass storage devices to support SCSI command queueing. Bulk endpoints can say they support up to 2^16 (65,536) streams. The information in the endpoint companion descriptor must be stored with the other device, config, interface, and endpoint descriptors because the host controller needs to access them quickly, and we need to install some default values if a SuperSpeed device doesn't provide an endpoint companion descriptor. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/config.c189
1 files changed, 180 insertions, 9 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index e9426acf5682..7103758bb486 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -19,6 +19,32 @@ static inline const char *plural(int n)
19 return (n == 1 ? "" : "s"); 19 return (n == 1 ? "" : "s");
20} 20}
21 21
22/* FIXME: this is a kludge */
23static int find_next_descriptor_more(unsigned char *buffer, int size,
24 int dt1, int dt2, int dt3, int *num_skipped)
25{
26 struct usb_descriptor_header *h;
27 int n = 0;
28 unsigned char *buffer0 = buffer;
29
30 /* Find the next descriptor of type dt1 or dt2 or dt3 */
31 while (size > 0) {
32 h = (struct usb_descriptor_header *) buffer;
33 if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 ||
34 h->bDescriptorType == dt3)
35 break;
36 buffer += h->bLength;
37 size -= h->bLength;
38 ++n;
39 }
40
41 /* Store the number of descriptors skipped and return the
42 * number of bytes skipped */
43 if (num_skipped)
44 *num_skipped = n;
45 return buffer - buffer0;
46}
47
22static int find_next_descriptor(unsigned char *buffer, int size, 48static int find_next_descriptor(unsigned char *buffer, int size,
23 int dt1, int dt2, int *num_skipped) 49 int dt1, int dt2, int *num_skipped)
24{ 50{
@@ -43,6 +69,128 @@ static int find_next_descriptor(unsigned char *buffer, int size,
43 return buffer - buffer0; 69 return buffer - buffer0;
44} 70}
45 71
72static int usb_parse_endpoint_companion(struct device *ddev, int cfgno,
73 int inum, int asnum, struct usb_host_endpoint *ep,
74 int num_ep, unsigned char *buffer, int size)
75{
76 unsigned char *buffer_start = buffer;
77 struct usb_ep_comp_descriptor *desc;
78 int retval;
79 int num_skipped;
80 int max_tx;
81 int i;
82
83 /* Allocate space for the companion descriptor */
84 ep->ep_comp = kzalloc(sizeof(struct usb_host_ep_comp), GFP_KERNEL);
85 if (!ep->ep_comp)
86 return -ENOMEM;
87 desc = (struct usb_ep_comp_descriptor *) buffer;
88 if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
89 dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
90 " interface %d altsetting %d ep %d: "
91 "using minimum values\n",
92 cfgno, inum, asnum, ep->desc.bEndpointAddress);
93 ep->ep_comp->desc.bLength = USB_DT_EP_COMP_SIZE;
94 ep->ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
95 ep->ep_comp->desc.bMaxBurst = 0;
96 /*
97 * Leave bmAttributes as zero, which will mean no streams for
98 * bulk, and isoc won't support multiple bursts of packets.
99 * With bursts of only one packet, and a Mult of 1, the max
100 * amount of data moved per endpoint service interval is one
101 * packet.
102 */
103 if (usb_endpoint_xfer_isoc(&ep->desc) ||
104 usb_endpoint_xfer_int(&ep->desc))
105 ep->ep_comp->desc.wBytesPerInterval =
106 ep->desc.wMaxPacketSize;
107 /*
108 * The next descriptor is for an Endpoint or Interface,
109 * no extra descriptors to copy into the companion structure,
110 * and we didn't eat up any of the buffer.
111 */
112 retval = 0;
113 goto valid;
114 }
115 memcpy(&ep->ep_comp->desc, desc, USB_DT_EP_COMP_SIZE);
116 desc = &ep->ep_comp->desc;
117 buffer += desc->bLength;
118 size -= desc->bLength;
119
120 /* Eat up the other descriptors we don't care about */
121 ep->ep_comp->extra = buffer;
122 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
123 USB_DT_INTERFACE, &num_skipped);
124 ep->ep_comp->extralen = i;
125 buffer += i;
126 size -= i;
127 retval = buffer - buffer_start + i;
128 if (num_skipped > 0)
129 dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
130 num_skipped, plural(num_skipped),
131 "SuperSpeed endpoint companion");
132
133 /* Check the various values */
134 if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
135 dev_warn(ddev, "Control endpoint with bMaxBurst = %d in "
136 "config %d interface %d altsetting %d ep %d: "
137 "setting to zero\n", desc->bMaxBurst,
138 cfgno, inum, asnum, ep->desc.bEndpointAddress);
139 desc->bMaxBurst = 0;
140 }
141 if (desc->bMaxBurst > 15) {
142 dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
143 "config %d interface %d altsetting %d ep %d: "
144 "setting to 15\n", desc->bMaxBurst,
145 cfgno, inum, asnum, ep->desc.bEndpointAddress);
146 desc->bMaxBurst = 15;
147 }
148 if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc))
149 && desc->bmAttributes != 0) {
150 dev_warn(ddev, "%s endpoint with bmAttributes = %d in "
151 "config %d interface %d altsetting %d ep %d: "
152 "setting to zero\n",
153 usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
154 desc->bmAttributes,
155 cfgno, inum, asnum, ep->desc.bEndpointAddress);
156 desc->bmAttributes = 0;
157 }
158 if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) {
159 dev_warn(ddev, "Bulk endpoint with more than 65536 streams in "
160 "config %d interface %d altsetting %d ep %d: "
161 "setting to max\n",
162 cfgno, inum, asnum, ep->desc.bEndpointAddress);
163 desc->bmAttributes = 16;
164 }
165 if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) {
166 dev_warn(ddev, "Isoc endpoint has Mult of %d in "
167 "config %d interface %d altsetting %d ep %d: "
168 "setting to 3\n", desc->bmAttributes + 1,
169 cfgno, inum, asnum, ep->desc.bEndpointAddress);
170 desc->bmAttributes = 2;
171 }
172 if (usb_endpoint_xfer_isoc(&ep->desc)) {
173 max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *
174 (desc->bmAttributes + 1);
175 } else if (usb_endpoint_xfer_int(&ep->desc)) {
176 max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1);
177 } else {
178 goto valid;
179 }
180 if (desc->wBytesPerInterval > max_tx) {
181 dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
182 "config %d interface %d altsetting %d ep %d: "
183 "setting to %d\n",
184 usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
185 desc->wBytesPerInterval,
186 cfgno, inum, asnum, ep->desc.bEndpointAddress,
187 max_tx);
188 desc->wBytesPerInterval = max_tx;
189 }
190valid:
191 return retval;
192}
193
46static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, 194static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
47 int asnum, struct usb_host_interface *ifp, int num_ep, 195 int asnum, struct usb_host_interface *ifp, int num_ep,
48 unsigned char *buffer, int size) 196 unsigned char *buffer, int size)
@@ -50,7 +198,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
50 unsigned char *buffer0 = buffer; 198 unsigned char *buffer0 = buffer;
51 struct usb_endpoint_descriptor *d; 199 struct usb_endpoint_descriptor *d;
52 struct usb_host_endpoint *endpoint; 200 struct usb_host_endpoint *endpoint;
53 int n, i, j; 201 int n, i, j, retval;
54 202
55 d = (struct usb_endpoint_descriptor *) buffer; 203 d = (struct usb_endpoint_descriptor *) buffer;
56 buffer += d->bLength; 204 buffer += d->bLength;
@@ -162,17 +310,38 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
162 cfgno, inum, asnum, d->bEndpointAddress, 310 cfgno, inum, asnum, d->bEndpointAddress,
163 maxp); 311 maxp);
164 } 312 }
165 313 /* Allocate room for and parse any endpoint companion descriptors */
166 /* Skip over any Class Specific or Vendor Specific descriptors; 314 if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) {
167 * find the next endpoint or interface descriptor */ 315 endpoint->extra = buffer;
168 endpoint->extra = buffer; 316 i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP,
169 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, 317 USB_DT_ENDPOINT, USB_DT_INTERFACE, &n);
170 USB_DT_INTERFACE, &n); 318 endpoint->extralen = i;
171 endpoint->extralen = i; 319 buffer += i;
320 size -= i;
321
322 if (size > 0) {
323 retval = usb_parse_endpoint_companion(ddev, cfgno, inum, asnum,
324 endpoint, num_ep, buffer, size);
325 if (retval >= 0) {
326 buffer += retval;
327 retval = buffer - buffer0;
328 }
329 } else {
330 retval = buffer - buffer0;
331 }
332 } else {
333 /* Skip over any Class Specific or Vendor Specific descriptors;
334 * find the next endpoint or interface descriptor */
335 endpoint->extra = buffer;
336 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
337 USB_DT_INTERFACE, &n);
338 endpoint->extralen = i;
339 retval = buffer - buffer0 + i;
340 }
172 if (n > 0) 341 if (n > 0)
173 dev_dbg(ddev, "skipped %d descriptor%s after %s\n", 342 dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
174 n, plural(n), "endpoint"); 343 n, plural(n), "endpoint");
175 return buffer - buffer0 + i; 344 return retval;
176 345
177skip_to_next_endpoint_or_interface_descriptor: 346skip_to_next_endpoint_or_interface_descriptor:
178 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, 347 i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
@@ -453,6 +622,8 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
453 kref_init(&intfc->ref); 622 kref_init(&intfc->ref);
454 } 623 }
455 624
625 /* FIXME: parse the BOS descriptor */
626
456 /* Skip over any Class Specific or Vendor Specific descriptors; 627 /* Skip over any Class Specific or Vendor Specific descriptors;
457 * find the first interface descriptor */ 628 * find the first interface descriptor */
458 config->extra = buffer; 629 config->extra = buffer;