diff options
author | Tatyana Brokhman <tlinder@codeaurora.org> | 2011-06-29 09:41:50 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-07-01 17:27:05 -0400 |
commit | bdb64d727216b49a18c2b8337658adc6b2db82ea (patch) | |
tree | 93215946a88413fabb656d0cb868a86b2c72c8c1 /drivers/usb/gadget/composite.c | |
parent | 35a0e0bf6f6b2b900d461e9f35c286953b2b1afc (diff) |
usb: gadget: add SuperSpeed support to the Gadget Framework
SuperSpeed USB has defined a new descriptor, called
the Binary Device Object Store (BOS) Descriptor. It
has also changed a bit the definition of SET_FEATURE
and GET_STATUS requests to add USB3-specific details.
This patch implements both changes to the Composite
Gadget Framework.
[ balbi@ti.com : slight changes to commit log
fixed a compile error on ARM ]
Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/composite.c')
-rw-r--r-- | drivers/usb/gadget/composite.c | 257 |
1 files changed, 239 insertions, 18 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 897e4f57422f..c5abe270970a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include <linux/utsname.h> | 27 | #include <linux/utsname.h> |
28 | 28 | ||
29 | #include <linux/usb/composite.h> | 29 | #include <linux/usb/composite.h> |
30 | 30 | #include <asm/unaligned.h> | |
31 | 31 | ||
32 | /* | 32 | /* |
33 | * The code in this file is utility code, used to build a gadget driver | 33 | * The code in this file is utility code, used to build a gadget driver |
@@ -128,6 +128,9 @@ int config_ep_by_speed(struct usb_gadget *g, | |||
128 | struct usb_endpoint_descriptor *chosen_desc = NULL; | 128 | struct usb_endpoint_descriptor *chosen_desc = NULL; |
129 | struct usb_descriptor_header **speed_desc = NULL; | 129 | struct usb_descriptor_header **speed_desc = NULL; |
130 | 130 | ||
131 | struct usb_ss_ep_comp_descriptor *comp_desc = NULL; | ||
132 | int want_comp_desc = 0; | ||
133 | |||
131 | struct usb_descriptor_header **d_spd; /* cursor for speed desc */ | 134 | struct usb_descriptor_header **d_spd; /* cursor for speed desc */ |
132 | 135 | ||
133 | if (!g || !f || !_ep) | 136 | if (!g || !f || !_ep) |
@@ -135,6 +138,13 @@ int config_ep_by_speed(struct usb_gadget *g, | |||
135 | 138 | ||
136 | /* select desired speed */ | 139 | /* select desired speed */ |
137 | switch (g->speed) { | 140 | switch (g->speed) { |
141 | case USB_SPEED_SUPER: | ||
142 | if (gadget_is_superspeed(g)) { | ||
143 | speed_desc = f->ss_descriptors; | ||
144 | want_comp_desc = 1; | ||
145 | break; | ||
146 | } | ||
147 | /* else: Fall trough */ | ||
138 | case USB_SPEED_HIGH: | 148 | case USB_SPEED_HIGH: |
139 | if (gadget_is_dualspeed(g)) { | 149 | if (gadget_is_dualspeed(g)) { |
140 | speed_desc = f->hs_descriptors; | 150 | speed_desc = f->hs_descriptors; |
@@ -156,7 +166,36 @@ ep_found: | |||
156 | /* commit results */ | 166 | /* commit results */ |
157 | _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize); | 167 | _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize); |
158 | _ep->desc = chosen_desc; | 168 | _ep->desc = chosen_desc; |
169 | _ep->comp_desc = NULL; | ||
170 | _ep->maxburst = 0; | ||
171 | _ep->mult = 0; | ||
172 | if (!want_comp_desc) | ||
173 | return 0; | ||
159 | 174 | ||
175 | /* | ||
176 | * Companion descriptor should follow EP descriptor | ||
177 | * USB 3.0 spec, #9.6.7 | ||
178 | */ | ||
179 | comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd); | ||
180 | if (!comp_desc || | ||
181 | (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP)) | ||
182 | return -EIO; | ||
183 | _ep->comp_desc = comp_desc; | ||
184 | if (g->speed == USB_SPEED_SUPER) { | ||
185 | switch (usb_endpoint_type(_ep->desc)) { | ||
186 | case USB_ENDPOINT_XFER_BULK: | ||
187 | case USB_ENDPOINT_XFER_INT: | ||
188 | _ep->maxburst = comp_desc->bMaxBurst; | ||
189 | break; | ||
190 | case USB_ENDPOINT_XFER_ISOC: | ||
191 | /* mult: bits 1:0 of bmAttributes */ | ||
192 | _ep->mult = comp_desc->bmAttributes & 0x3; | ||
193 | break; | ||
194 | default: | ||
195 | /* Do nothing for control endpoints */ | ||
196 | break; | ||
197 | } | ||
198 | } | ||
160 | return 0; | 199 | return 0; |
161 | } | 200 | } |
162 | 201 | ||
@@ -208,6 +247,8 @@ int usb_add_function(struct usb_configuration *config, | |||
208 | config->fullspeed = true; | 247 | config->fullspeed = true; |
209 | if (!config->highspeed && function->hs_descriptors) | 248 | if (!config->highspeed && function->hs_descriptors) |
210 | config->highspeed = true; | 249 | config->highspeed = true; |
250 | if (!config->superspeed && function->ss_descriptors) | ||
251 | config->superspeed = true; | ||
211 | 252 | ||
212 | done: | 253 | done: |
213 | if (value) | 254 | if (value) |
@@ -351,10 +392,17 @@ static int config_buf(struct usb_configuration *config, | |||
351 | list_for_each_entry(f, &config->functions, list) { | 392 | list_for_each_entry(f, &config->functions, list) { |
352 | struct usb_descriptor_header **descriptors; | 393 | struct usb_descriptor_header **descriptors; |
353 | 394 | ||
354 | if (speed == USB_SPEED_HIGH) | 395 | switch (speed) { |
396 | case USB_SPEED_SUPER: | ||
397 | descriptors = f->ss_descriptors; | ||
398 | break; | ||
399 | case USB_SPEED_HIGH: | ||
355 | descriptors = f->hs_descriptors; | 400 | descriptors = f->hs_descriptors; |
356 | else | 401 | break; |
402 | default: | ||
357 | descriptors = f->descriptors; | 403 | descriptors = f->descriptors; |
404 | } | ||
405 | |||
358 | if (!descriptors) | 406 | if (!descriptors) |
359 | continue; | 407 | continue; |
360 | status = usb_descriptor_fillbuf(next, len, | 408 | status = usb_descriptor_fillbuf(next, len, |
@@ -377,9 +425,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) | |||
377 | u8 type = w_value >> 8; | 425 | u8 type = w_value >> 8; |
378 | enum usb_device_speed speed = USB_SPEED_UNKNOWN; | 426 | enum usb_device_speed speed = USB_SPEED_UNKNOWN; |
379 | 427 | ||
380 | if (gadget_is_dualspeed(gadget)) { | 428 | if (gadget->speed == USB_SPEED_SUPER) |
381 | int hs = 0; | 429 | speed = gadget->speed; |
382 | 430 | else if (gadget_is_dualspeed(gadget)) { | |
431 | int hs = 0; | ||
383 | if (gadget->speed == USB_SPEED_HIGH) | 432 | if (gadget->speed == USB_SPEED_HIGH) |
384 | hs = 1; | 433 | hs = 1; |
385 | if (type == USB_DT_OTHER_SPEED_CONFIG) | 434 | if (type == USB_DT_OTHER_SPEED_CONFIG) |
@@ -393,13 +442,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) | |||
393 | w_value &= 0xff; | 442 | w_value &= 0xff; |
394 | list_for_each_entry(c, &cdev->configs, list) { | 443 | list_for_each_entry(c, &cdev->configs, list) { |
395 | /* ignore configs that won't work at this speed */ | 444 | /* ignore configs that won't work at this speed */ |
396 | if (speed == USB_SPEED_HIGH) { | 445 | switch (speed) { |
446 | case USB_SPEED_SUPER: | ||
447 | if (!c->superspeed) | ||
448 | continue; | ||
449 | break; | ||
450 | case USB_SPEED_HIGH: | ||
397 | if (!c->highspeed) | 451 | if (!c->highspeed) |
398 | continue; | 452 | continue; |
399 | } else { | 453 | break; |
454 | default: | ||
400 | if (!c->fullspeed) | 455 | if (!c->fullspeed) |
401 | continue; | 456 | continue; |
402 | } | 457 | } |
458 | |||
403 | if (w_value == 0) | 459 | if (w_value == 0) |
404 | return config_buf(c, speed, cdev->req->buf, type); | 460 | return config_buf(c, speed, cdev->req->buf, type); |
405 | w_value--; | 461 | w_value--; |
@@ -413,16 +469,22 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) | |||
413 | struct usb_configuration *c; | 469 | struct usb_configuration *c; |
414 | unsigned count = 0; | 470 | unsigned count = 0; |
415 | int hs = 0; | 471 | int hs = 0; |
472 | int ss = 0; | ||
416 | 473 | ||
417 | if (gadget_is_dualspeed(gadget)) { | 474 | if (gadget_is_dualspeed(gadget)) { |
418 | if (gadget->speed == USB_SPEED_HIGH) | 475 | if (gadget->speed == USB_SPEED_HIGH) |
419 | hs = 1; | 476 | hs = 1; |
477 | if (gadget->speed == USB_SPEED_SUPER) | ||
478 | ss = 1; | ||
420 | if (type == USB_DT_DEVICE_QUALIFIER) | 479 | if (type == USB_DT_DEVICE_QUALIFIER) |
421 | hs = !hs; | 480 | hs = !hs; |
422 | } | 481 | } |
423 | list_for_each_entry(c, &cdev->configs, list) { | 482 | list_for_each_entry(c, &cdev->configs, list) { |
424 | /* ignore configs that won't work at this speed */ | 483 | /* ignore configs that won't work at this speed */ |
425 | if (hs) { | 484 | if (ss) { |
485 | if (!c->superspeed) | ||
486 | continue; | ||
487 | } else if (hs) { | ||
426 | if (!c->highspeed) | 488 | if (!c->highspeed) |
427 | continue; | 489 | continue; |
428 | } else { | 490 | } else { |
@@ -434,6 +496,71 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type) | |||
434 | return count; | 496 | return count; |
435 | } | 497 | } |
436 | 498 | ||
499 | /** | ||
500 | * bos_desc() - prepares the BOS descriptor. | ||
501 | * @cdev: pointer to usb_composite device to generate the bos | ||
502 | * descriptor for | ||
503 | * | ||
504 | * This function generates the BOS (Binary Device Object) | ||
505 | * descriptor and its device capabilities descriptors. The BOS | ||
506 | * descriptor should be supported by a SuperSpeed device. | ||
507 | */ | ||
508 | static int bos_desc(struct usb_composite_dev *cdev) | ||
509 | { | ||
510 | struct usb_ext_cap_descriptor *usb_ext; | ||
511 | struct usb_ss_cap_descriptor *ss_cap; | ||
512 | struct usb_dcd_config_params dcd_config_params; | ||
513 | struct usb_bos_descriptor *bos = cdev->req->buf; | ||
514 | |||
515 | bos->bLength = USB_DT_BOS_SIZE; | ||
516 | bos->bDescriptorType = USB_DT_BOS; | ||
517 | |||
518 | bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); | ||
519 | bos->bNumDeviceCaps = 0; | ||
520 | |||
521 | /* | ||
522 | * A SuperSpeed device shall include the USB2.0 extension descriptor | ||
523 | * and shall support LPM when operating in USB2.0 HS mode. | ||
524 | */ | ||
525 | usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); | ||
526 | bos->bNumDeviceCaps++; | ||
527 | le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE); | ||
528 | usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; | ||
529 | usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; | ||
530 | usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; | ||
531 | usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT); | ||
532 | |||
533 | /* | ||
534 | * The Superspeed USB Capability descriptor shall be implemented by all | ||
535 | * SuperSpeed devices. | ||
536 | */ | ||
537 | ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); | ||
538 | bos->bNumDeviceCaps++; | ||
539 | le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE); | ||
540 | ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; | ||
541 | ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; | ||
542 | ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; | ||
543 | ss_cap->bmAttributes = 0; /* LTM is not supported yet */ | ||
544 | ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | | ||
545 | USB_FULL_SPEED_OPERATION | | ||
546 | USB_HIGH_SPEED_OPERATION | | ||
547 | USB_5GBPS_OPERATION); | ||
548 | ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; | ||
549 | |||
550 | /* Get Controller configuration */ | ||
551 | if (cdev->gadget->ops->get_config_params) | ||
552 | cdev->gadget->ops->get_config_params(&dcd_config_params); | ||
553 | else { | ||
554 | dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT; | ||
555 | dcd_config_params.bU2DevExitLat = | ||
556 | cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT); | ||
557 | } | ||
558 | ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; | ||
559 | ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; | ||
560 | |||
561 | return le16_to_cpu(bos->wTotalLength); | ||
562 | } | ||
563 | |||
437 | static void device_qual(struct usb_composite_dev *cdev) | 564 | static void device_qual(struct usb_composite_dev *cdev) |
438 | { | 565 | { |
439 | struct usb_qualifier_descriptor *qual = cdev->req->buf; | 566 | struct usb_qualifier_descriptor *qual = cdev->req->buf; |
@@ -477,20 +604,27 @@ static int set_config(struct usb_composite_dev *cdev, | |||
477 | unsigned power = gadget_is_otg(gadget) ? 8 : 100; | 604 | unsigned power = gadget_is_otg(gadget) ? 8 : 100; |
478 | int tmp; | 605 | int tmp; |
479 | 606 | ||
480 | if (cdev->config) | ||
481 | reset_config(cdev); | ||
482 | |||
483 | if (number) { | 607 | if (number) { |
484 | list_for_each_entry(c, &cdev->configs, list) { | 608 | list_for_each_entry(c, &cdev->configs, list) { |
485 | if (c->bConfigurationValue == number) { | 609 | if (c->bConfigurationValue == number) { |
610 | /* | ||
611 | * We disable the FDs of the previous | ||
612 | * configuration only if the new configuration | ||
613 | * is a valid one | ||
614 | */ | ||
615 | if (cdev->config) | ||
616 | reset_config(cdev); | ||
486 | result = 0; | 617 | result = 0; |
487 | break; | 618 | break; |
488 | } | 619 | } |
489 | } | 620 | } |
490 | if (result < 0) | 621 | if (result < 0) |
491 | goto done; | 622 | goto done; |
492 | } else | 623 | } else { /* Zero configuration value - need to reset the config */ |
624 | if (cdev->config) | ||
625 | reset_config(cdev); | ||
493 | result = 0; | 626 | result = 0; |
627 | } | ||
494 | 628 | ||
495 | INFO(cdev, "%s speed config #%d: %s\n", | 629 | INFO(cdev, "%s speed config #%d: %s\n", |
496 | ({ char *speed; | 630 | ({ char *speed; |
@@ -504,6 +638,9 @@ static int set_config(struct usb_composite_dev *cdev, | |||
504 | case USB_SPEED_HIGH: | 638 | case USB_SPEED_HIGH: |
505 | speed = "high"; | 639 | speed = "high"; |
506 | break; | 640 | break; |
641 | case USB_SPEED_SUPER: | ||
642 | speed = "super"; | ||
643 | break; | ||
507 | default: | 644 | default: |
508 | speed = "?"; | 645 | speed = "?"; |
509 | break; | 646 | break; |
@@ -528,10 +665,16 @@ static int set_config(struct usb_composite_dev *cdev, | |||
528 | * function's setup callback instead of the current | 665 | * function's setup callback instead of the current |
529 | * configuration's setup callback. | 666 | * configuration's setup callback. |
530 | */ | 667 | */ |
531 | if (gadget->speed == USB_SPEED_HIGH) | 668 | switch (gadget->speed) { |
669 | case USB_SPEED_SUPER: | ||
670 | descriptors = f->ss_descriptors; | ||
671 | break; | ||
672 | case USB_SPEED_HIGH: | ||
532 | descriptors = f->hs_descriptors; | 673 | descriptors = f->hs_descriptors; |
533 | else | 674 | break; |
675 | default: | ||
534 | descriptors = f->descriptors; | 676 | descriptors = f->descriptors; |
677 | } | ||
535 | 678 | ||
536 | for (; *descriptors; ++descriptors) { | 679 | for (; *descriptors; ++descriptors) { |
537 | struct usb_endpoint_descriptor *ep; | 680 | struct usb_endpoint_descriptor *ep; |
@@ -624,8 +767,9 @@ int usb_add_config(struct usb_composite_dev *cdev, | |||
624 | } else { | 767 | } else { |
625 | unsigned i; | 768 | unsigned i; |
626 | 769 | ||
627 | DBG(cdev, "cfg %d/%p speeds:%s%s\n", | 770 | DBG(cdev, "cfg %d/%p speeds:%s%s%s\n", |
628 | config->bConfigurationValue, config, | 771 | config->bConfigurationValue, config, |
772 | config->superspeed ? " super" : "", | ||
629 | config->highspeed ? " high" : "", | 773 | config->highspeed ? " high" : "", |
630 | config->fullspeed | 774 | config->fullspeed |
631 | ? (gadget_is_dualspeed(cdev->gadget) | 775 | ? (gadget_is_dualspeed(cdev->gadget) |
@@ -904,6 +1048,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) | |||
904 | struct usb_composite_dev *cdev = get_gadget_data(gadget); | 1048 | struct usb_composite_dev *cdev = get_gadget_data(gadget); |
905 | struct usb_request *req = cdev->req; | 1049 | struct usb_request *req = cdev->req; |
906 | int value = -EOPNOTSUPP; | 1050 | int value = -EOPNOTSUPP; |
1051 | int status = 0; | ||
907 | u16 w_index = le16_to_cpu(ctrl->wIndex); | 1052 | u16 w_index = le16_to_cpu(ctrl->wIndex); |
908 | u8 intf = w_index & 0xFF; | 1053 | u8 intf = w_index & 0xFF; |
909 | u16 w_value = le16_to_cpu(ctrl->wValue); | 1054 | u16 w_value = le16_to_cpu(ctrl->wValue); |
@@ -931,18 +1076,29 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) | |||
931 | case USB_DT_DEVICE: | 1076 | case USB_DT_DEVICE: |
932 | cdev->desc.bNumConfigurations = | 1077 | cdev->desc.bNumConfigurations = |
933 | count_configs(cdev, USB_DT_DEVICE); | 1078 | count_configs(cdev, USB_DT_DEVICE); |
1079 | cdev->desc.bMaxPacketSize0 = | ||
1080 | cdev->gadget->ep0->maxpacket; | ||
1081 | if (gadget_is_superspeed(gadget)) { | ||
1082 | if (gadget->speed >= USB_SPEED_SUPER) | ||
1083 | cdev->desc.bcdUSB = cpu_to_le16(0x0300); | ||
1084 | else | ||
1085 | cdev->desc.bcdUSB = cpu_to_le16(0x0210); | ||
1086 | } | ||
1087 | |||
934 | value = min(w_length, (u16) sizeof cdev->desc); | 1088 | value = min(w_length, (u16) sizeof cdev->desc); |
935 | memcpy(req->buf, &cdev->desc, value); | 1089 | memcpy(req->buf, &cdev->desc, value); |
936 | break; | 1090 | break; |
937 | case USB_DT_DEVICE_QUALIFIER: | 1091 | case USB_DT_DEVICE_QUALIFIER: |
938 | if (!gadget_is_dualspeed(gadget)) | 1092 | if (!gadget_is_dualspeed(gadget) || |
1093 | gadget->speed >= USB_SPEED_SUPER) | ||
939 | break; | 1094 | break; |
940 | device_qual(cdev); | 1095 | device_qual(cdev); |
941 | value = min_t(int, w_length, | 1096 | value = min_t(int, w_length, |
942 | sizeof(struct usb_qualifier_descriptor)); | 1097 | sizeof(struct usb_qualifier_descriptor)); |
943 | break; | 1098 | break; |
944 | case USB_DT_OTHER_SPEED_CONFIG: | 1099 | case USB_DT_OTHER_SPEED_CONFIG: |
945 | if (!gadget_is_dualspeed(gadget)) | 1100 | if (!gadget_is_dualspeed(gadget) || |
1101 | gadget->speed >= USB_SPEED_SUPER) | ||
946 | break; | 1102 | break; |
947 | /* FALLTHROUGH */ | 1103 | /* FALLTHROUGH */ |
948 | case USB_DT_CONFIG: | 1104 | case USB_DT_CONFIG: |
@@ -956,6 +1112,12 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) | |||
956 | if (value >= 0) | 1112 | if (value >= 0) |
957 | value = min(w_length, (u16) value); | 1113 | value = min(w_length, (u16) value); |
958 | break; | 1114 | break; |
1115 | case USB_DT_BOS: | ||
1116 | if (gadget_is_superspeed(gadget)) { | ||
1117 | value = bos_desc(cdev); | ||
1118 | value = min(w_length, (u16) value); | ||
1119 | } | ||
1120 | break; | ||
959 | } | 1121 | } |
960 | break; | 1122 | break; |
961 | 1123 | ||
@@ -1023,6 +1185,61 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) | |||
1023 | *((u8 *)req->buf) = value; | 1185 | *((u8 *)req->buf) = value; |
1024 | value = min(w_length, (u16) 1); | 1186 | value = min(w_length, (u16) 1); |
1025 | break; | 1187 | break; |
1188 | |||
1189 | /* | ||
1190 | * USB 3.0 additions: | ||
1191 | * Function driver should handle get_status request. If such cb | ||
1192 | * wasn't supplied we respond with default value = 0 | ||
1193 | * Note: function driver should supply such cb only for the first | ||
1194 | * interface of the function | ||
1195 | */ | ||
1196 | case USB_REQ_GET_STATUS: | ||
1197 | if (!gadget_is_superspeed(gadget)) | ||
1198 | goto unknown; | ||
1199 | if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) | ||
1200 | goto unknown; | ||
1201 | value = 2; /* This is the length of the get_status reply */ | ||
1202 | put_unaligned_le16(0, req->buf); | ||
1203 | if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) | ||
1204 | break; | ||
1205 | f = cdev->config->interface[intf]; | ||
1206 | if (!f) | ||
1207 | break; | ||
1208 | status = f->get_status ? f->get_status(f) : 0; | ||
1209 | if (status < 0) | ||
1210 | break; | ||
1211 | put_unaligned_le16(status & 0x0000ffff, req->buf); | ||
1212 | break; | ||
1213 | /* | ||
1214 | * Function drivers should handle SetFeature/ClearFeature | ||
1215 | * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied | ||
1216 | * only for the first interface of the function | ||
1217 | */ | ||
1218 | case USB_REQ_CLEAR_FEATURE: | ||
1219 | case USB_REQ_SET_FEATURE: | ||
1220 | if (!gadget_is_superspeed(gadget)) | ||
1221 | goto unknown; | ||
1222 | if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE)) | ||
1223 | goto unknown; | ||
1224 | switch (w_value) { | ||
1225 | case USB_INTRF_FUNC_SUSPEND: | ||
1226 | if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) | ||
1227 | break; | ||
1228 | f = cdev->config->interface[intf]; | ||
1229 | if (!f) | ||
1230 | break; | ||
1231 | value = 0; | ||
1232 | if (f->func_suspend) | ||
1233 | value = f->func_suspend(f, w_index >> 8); | ||
1234 | if (value < 0) { | ||
1235 | ERROR(cdev, | ||
1236 | "func_suspend() returned error %d\n", | ||
1237 | value); | ||
1238 | value = 0; | ||
1239 | } | ||
1240 | break; | ||
1241 | } | ||
1242 | break; | ||
1026 | default: | 1243 | default: |
1027 | unknown: | 1244 | unknown: |
1028 | VDBG(cdev, | 1245 | VDBG(cdev, |
@@ -1340,7 +1557,11 @@ composite_resume(struct usb_gadget *gadget) | |||
1340 | /*-------------------------------------------------------------------------*/ | 1557 | /*-------------------------------------------------------------------------*/ |
1341 | 1558 | ||
1342 | static struct usb_gadget_driver composite_driver = { | 1559 | static struct usb_gadget_driver composite_driver = { |
1560 | #ifdef CONFIG_USB_GADGET_SUPERSPEED | ||
1561 | .speed = USB_SPEED_SUPER, | ||
1562 | #else | ||
1343 | .speed = USB_SPEED_HIGH, | 1563 | .speed = USB_SPEED_HIGH, |
1564 | #endif | ||
1344 | 1565 | ||
1345 | .unbind = composite_unbind, | 1566 | .unbind = composite_unbind, |
1346 | 1567 | ||