Commit 363267844637123d4fcfb924d624882b02059082
Committed by
Ye Li
1 parent
e77bc59cf1
Exists in
smarc_8mm-imx_v2019.04_4.19.35_1.1.0
and in
1 other branch
MLK-20493-1 usb: gadget: add super speed support
This patch is to add usb gadget super speed support in common driver, including BOS descriptor and select the super speed descriptor from function driver. Reviewed-by: Ye Li <ye.li@nxp.com> Reviewed-by: Peter Chen <peter.chen@nxp.com> Tested-by: faqiang.zhu <faqiang.zhu@nxp.com> Signed-off-by: Li Jun <jun.li@nxp.com> (cherry picked from commit b0bc5f6d1292158a068446771c10a4e7285f9623)
Showing 3 changed files with 139 additions and 24 deletions Side-by-side Diff
drivers/usb/gadget/composite.c
... | ... | @@ -77,6 +77,8 @@ |
77 | 77 | config->fullspeed = 1; |
78 | 78 | if (!config->highspeed && function->hs_descriptors) |
79 | 79 | config->highspeed = 1; |
80 | + if (!config->superspeed && function->ss_descriptors) | |
81 | + config->superspeed = 1; | |
80 | 82 | |
81 | 83 | done: |
82 | 84 | if (value) |
... | ... | @@ -212,7 +214,9 @@ |
212 | 214 | |
213 | 215 | /* add each function's descriptors */ |
214 | 216 | list_for_each_entry(f, &config->functions, list) { |
215 | - if (speed == USB_SPEED_HIGH) | |
217 | + if (speed == USB_SPEED_SUPER) | |
218 | + descriptors = f->ss_descriptors; | |
219 | + else if (speed == USB_SPEED_HIGH) | |
216 | 220 | descriptors = f->hs_descriptors; |
217 | 221 | else |
218 | 222 | descriptors = f->descriptors; |
... | ... | @@ -240,7 +244,9 @@ |
240 | 244 | struct usb_configuration *c; |
241 | 245 | struct list_head *pos; |
242 | 246 | |
243 | - if (gadget_is_dualspeed(gadget)) { | |
247 | + if (gadget_is_superspeed(gadget)) { | |
248 | + speed = gadget->speed; | |
249 | + } else if (gadget_is_dualspeed(gadget)) { | |
244 | 250 | if (gadget->speed == USB_SPEED_HIGH) |
245 | 251 | hs = 1; |
246 | 252 | if (type == USB_DT_OTHER_SPEED_CONFIG) |
... | ... | @@ -264,7 +270,10 @@ |
264 | 270 | continue; |
265 | 271 | |
266 | 272 | check_config: |
267 | - if (speed == USB_SPEED_HIGH) { | |
273 | + if (speed == USB_SPEED_SUPER) { | |
274 | + if (!c->superspeed) | |
275 | + continue; | |
276 | + } else if (speed == USB_SPEED_HIGH) { | |
268 | 277 | if (!c->highspeed) |
269 | 278 | continue; |
270 | 279 | } else { |
271 | 280 | |
... | ... | @@ -283,8 +292,12 @@ |
283 | 292 | struct usb_gadget *gadget = cdev->gadget; |
284 | 293 | unsigned count = 0; |
285 | 294 | int hs = 0; |
295 | + int ss = 0; | |
286 | 296 | struct usb_configuration *c; |
287 | 297 | |
298 | + if (gadget->speed == USB_SPEED_SUPER) | |
299 | + ss = 1; | |
300 | + | |
288 | 301 | if (gadget_is_dualspeed(gadget)) { |
289 | 302 | if (gadget->speed == USB_SPEED_HIGH) |
290 | 303 | hs = 1; |
... | ... | @@ -293,7 +306,10 @@ |
293 | 306 | } |
294 | 307 | list_for_each_entry(c, &cdev->configs, list) { |
295 | 308 | /* ignore configs that won't work at this speed */ |
296 | - if (hs) { | |
309 | + if (ss) { | |
310 | + if (!c->superspeed) | |
311 | + continue; | |
312 | + } else if (hs) { | |
297 | 313 | if (!c->highspeed) |
298 | 314 | continue; |
299 | 315 | } else { |
... | ... | @@ -305,6 +321,78 @@ |
305 | 321 | return count; |
306 | 322 | } |
307 | 323 | |
324 | +/** | |
325 | + * bos_desc() - prepares the BOS descriptor. | |
326 | + * @cdev: pointer to usb_composite device to generate the bos | |
327 | + * descriptor for | |
328 | + * | |
329 | + * This function generates the BOS (Binary Device Object) | |
330 | + * descriptor and its device capabilities descriptors. The BOS | |
331 | + * descriptor should be supported by a SuperSpeed device. | |
332 | + */ | |
333 | +static int bos_desc(struct usb_composite_dev *cdev) | |
334 | +{ | |
335 | + struct usb_ext_cap_descriptor *usb_ext; | |
336 | + struct usb_dcd_config_params dcd_config_params; | |
337 | + struct usb_bos_descriptor *bos = cdev->req->buf; | |
338 | + | |
339 | + bos->bLength = USB_DT_BOS_SIZE; | |
340 | + bos->bDescriptorType = USB_DT_BOS; | |
341 | + | |
342 | + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); | |
343 | + bos->bNumDeviceCaps = 0; | |
344 | + | |
345 | + /* | |
346 | + * A SuperSpeed device shall include the USB2.0 extension descriptor | |
347 | + * and shall support LPM when operating in USB2.0 HS mode. | |
348 | + */ | |
349 | + usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength); | |
350 | + bos->bNumDeviceCaps++; | |
351 | + bos->wTotalLength = cpu_to_le16(le16_to_cpu(bos->wTotalLength) + | |
352 | + USB_DT_USB_EXT_CAP_SIZE); | |
353 | + usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; | |
354 | + usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; | |
355 | + usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; | |
356 | + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT); | |
357 | + | |
358 | + /* | |
359 | + * The Superspeed USB Capability descriptor shall be implemented by all | |
360 | + * SuperSpeed devices. | |
361 | + */ | |
362 | + if (gadget_is_superspeed(cdev->gadget)) { | |
363 | + struct usb_ss_cap_descriptor *ss_cap; | |
364 | + | |
365 | + ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); | |
366 | + bos->bNumDeviceCaps++; | |
367 | + bos->wTotalLength = cpu_to_le16(le16_to_cpu(bos->wTotalLength) + | |
368 | + USB_DT_USB_SS_CAP_SIZE); | |
369 | + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; | |
370 | + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; | |
371 | + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; | |
372 | + ss_cap->bmAttributes = 0; /* LTM is not supported yet */ | |
373 | + ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION | | |
374 | + USB_FULL_SPEED_OPERATION | | |
375 | + USB_HIGH_SPEED_OPERATION | | |
376 | + USB_5GBPS_OPERATION); | |
377 | + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; | |
378 | + | |
379 | + /* Get Controller configuration */ | |
380 | + if (cdev->gadget->ops->get_config_params) { | |
381 | + cdev->gadget->ops->get_config_params( | |
382 | + &dcd_config_params); | |
383 | + } else { | |
384 | + dcd_config_params.bU1devExitLat = | |
385 | + USB_DEFAULT_U1_DEV_EXIT_LAT; | |
386 | + dcd_config_params.bU2DevExitLat = | |
387 | + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); | |
388 | + } | |
389 | + ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; | |
390 | + ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; | |
391 | + } | |
392 | + | |
393 | + return le16_to_cpu(bos->wTotalLength); | |
394 | +} | |
395 | + | |
308 | 396 | static void device_qual(struct usb_composite_dev *cdev) |
309 | 397 | { |
310 | 398 | struct usb_qualifier_descriptor *qual = cdev->req->buf; |
... | ... | @@ -377,6 +465,9 @@ |
377 | 465 | case USB_SPEED_HIGH: |
378 | 466 | speed = "high"; |
379 | 467 | break; |
468 | + case USB_SPEED_SUPER: | |
469 | + speed = "super"; | |
470 | + break; | |
380 | 471 | default: |
381 | 472 | speed = "?"; |
382 | 473 | break; |
... | ... | @@ -401,7 +492,9 @@ |
401 | 492 | * function's setup callback instead of the current |
402 | 493 | * configuration's setup callback. |
403 | 494 | */ |
404 | - if (gadget->speed == USB_SPEED_HIGH) | |
495 | + if (gadget->speed == USB_SPEED_SUPER) | |
496 | + descriptors = f->ss_descriptors; | |
497 | + else if (gadget->speed == USB_SPEED_HIGH) | |
405 | 498 | descriptors = f->hs_descriptors; |
406 | 499 | else |
407 | 500 | descriptors = f->descriptors; |
408 | 501 | |
... | ... | @@ -481,8 +574,9 @@ |
481 | 574 | list_del(&config->list); |
482 | 575 | config->cdev = NULL; |
483 | 576 | } else { |
484 | - debug("cfg %d/%p speeds:%s%s\n", | |
577 | + debug("cfg %d/%p speeds:%s%s%s\n", | |
485 | 578 | config->bConfigurationValue, config, |
579 | + config->superspeed ? " super" : "", | |
486 | 580 | config->highspeed ? " high" : "", |
487 | 581 | config->fullspeed |
488 | 582 | ? (gadget_is_dualspeed(cdev->gadget) |
489 | 583 | |
490 | 584 | |
491 | 585 | |
492 | 586 | |
... | ... | @@ -934,32 +1028,28 @@ |
934 | 1028 | cdev->desc.bNumConfigurations = |
935 | 1029 | count_configs(cdev, USB_DT_DEVICE); |
936 | 1030 | |
937 | - /* | |
938 | - * If the speed is Super speed, then the supported | |
939 | - * max packet size is 512 and it should be sent as | |
940 | - * exponent of 2. So, 9(2^9=512) should be filled in | |
941 | - * bMaxPacketSize0. Also fill USB version as 3.0 | |
942 | - * if speed is Super speed. | |
943 | - */ | |
944 | - if (cdev->gadget->speed == USB_SPEED_SUPER) { | |
1031 | + cdev->desc.bMaxPacketSize0 = | |
1032 | + cdev->gadget->ep0->maxpacket; | |
1033 | + if (gadget->speed >= USB_SPEED_SUPER) { | |
1034 | + cdev->desc.bcdUSB = cpu_to_le16(0x0310); | |
945 | 1035 | cdev->desc.bMaxPacketSize0 = 9; |
946 | - cdev->desc.bcdUSB = cpu_to_le16(0x0300); | |
947 | 1036 | } else { |
948 | - cdev->desc.bMaxPacketSize0 = | |
949 | - cdev->gadget->ep0->maxpacket; | |
1037 | + cdev->desc.bcdUSB = cpu_to_le16(0x0200); | |
950 | 1038 | } |
951 | 1039 | value = min(w_length, (u16) sizeof cdev->desc); |
952 | 1040 | memcpy(req->buf, &cdev->desc, value); |
953 | 1041 | break; |
954 | 1042 | case USB_DT_DEVICE_QUALIFIER: |
955 | - if (!gadget_is_dualspeed(gadget)) | |
1043 | + if (!gadget_is_dualspeed(gadget) || | |
1044 | + gadget->speed >= USB_SPEED_SUPER) | |
956 | 1045 | break; |
957 | 1046 | device_qual(cdev); |
958 | 1047 | value = min_t(int, w_length, |
959 | 1048 | sizeof(struct usb_qualifier_descriptor)); |
960 | 1049 | break; |
961 | 1050 | case USB_DT_OTHER_SPEED_CONFIG: |
962 | - if (!gadget_is_dualspeed(gadget)) | |
1051 | + if (!gadget_is_dualspeed(gadget) || | |
1052 | + gadget->speed >= USB_SPEED_SUPER) | |
963 | 1053 | break; |
964 | 1054 | |
965 | 1055 | case USB_DT_CONFIG: |
966 | 1056 | |
... | ... | @@ -975,11 +1065,15 @@ |
975 | 1065 | break; |
976 | 1066 | case USB_DT_BOS: |
977 | 1067 | /* |
978 | - * The USB compliance test (USB 2.0 Command Verifier) | |
979 | - * issues this request. We should not run into the | |
980 | - * default path here. But return for now until | |
981 | - * the superspeed support is added. | |
1068 | + * Super speed connection should support BOS, and | |
1069 | + * USB compliance test (USB 2.0 Command Verifier) | |
1070 | + * also issues this request, return for now for | |
1071 | + * USB 2.0 connection. | |
982 | 1072 | */ |
1073 | + if (gadget->speed >= USB_SPEED_SUPER) { | |
1074 | + value = bos_desc(cdev); | |
1075 | + value = min(w_length, (u16)value); | |
1076 | + } | |
983 | 1077 | break; |
984 | 1078 | default: |
985 | 1079 | goto unknown; |
... | ... | @@ -1354,7 +1448,7 @@ |
1354 | 1448 | } |
1355 | 1449 | |
1356 | 1450 | static struct usb_gadget_driver composite_driver = { |
1357 | - .speed = USB_SPEED_HIGH, | |
1451 | + .speed = USB_SPEED_SUPER, | |
1358 | 1452 | |
1359 | 1453 | .bind = composite_bind, |
1360 | 1454 | .unbind = composite_unbind, |
include/linux/usb/composite.h
... | ... | @@ -146,6 +146,7 @@ |
146 | 146 | struct usb_gadget_strings **strings; |
147 | 147 | struct usb_descriptor_header **descriptors; |
148 | 148 | struct usb_descriptor_header **hs_descriptors; |
149 | + struct usb_descriptor_header **ss_descriptors; | |
149 | 150 | |
150 | 151 | struct usb_configuration *config; |
151 | 152 | |
... | ... | @@ -279,6 +280,7 @@ |
279 | 280 | u8 next_interface_id; |
280 | 281 | unsigned highspeed:1; |
281 | 282 | unsigned fullspeed:1; |
283 | + unsigned superspeed:1; | |
282 | 284 | struct usb_function *interface[MAX_CONFIG_INTERFACES]; |
283 | 285 | }; |
284 | 286 | |
... | ... | @@ -292,6 +294,7 @@ |
292 | 294 | * identifiers. |
293 | 295 | * @strings: tables of strings, keyed by identifiers assigned during bind() |
294 | 296 | * and language IDs provided in control requests |
297 | + * @max_speed: Highest speed the driver supports. | |
295 | 298 | * @bind: (REQUIRED) Used to allocate resources that are shared across the |
296 | 299 | * whole device, such as string IDs, and add its configurations using |
297 | 300 | * @usb_add_config(). This may fail by returning a negative errno |
... | ... | @@ -319,6 +322,7 @@ |
319 | 322 | const char *name; |
320 | 323 | const struct usb_device_descriptor *dev; |
321 | 324 | struct usb_gadget_strings **strings; |
325 | + enum usb_device_speed max_speed; | |
322 | 326 | |
323 | 327 | /* REVISIT: bind() functions can be marked __init, which |
324 | 328 | * makes trouble for section mismatch analysis. See if |
include/linux/usb/gadget.h
... | ... | @@ -429,6 +429,13 @@ |
429 | 429 | |
430 | 430 | /*-------------------------------------------------------------------------*/ |
431 | 431 | |
432 | +struct usb_dcd_config_params { | |
433 | + __u8 bU1devExitLat; /* U1 Device exit Latency */ | |
434 | +#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ | |
435 | + __le16 bU2DevExitLat; /* U2 Device exit Latency */ | |
436 | +#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */ | |
437 | +}; | |
438 | + | |
432 | 439 | struct usb_gadget; |
433 | 440 | struct usb_gadget_driver; |
434 | 441 | |
... | ... | @@ -444,6 +451,7 @@ |
444 | 451 | int (*pullup) (struct usb_gadget *, int is_on); |
445 | 452 | int (*ioctl)(struct usb_gadget *, |
446 | 453 | unsigned code, unsigned long param); |
454 | + void (*get_config_params)(struct usb_dcd_config_params *); | |
447 | 455 | int (*udc_start)(struct usb_gadget *, |
448 | 456 | struct usb_gadget_driver *); |
449 | 457 | int (*udc_stop)(struct usb_gadget *); |
... | ... | @@ -551,6 +559,15 @@ |
551 | 559 | #else |
552 | 560 | return 0; |
553 | 561 | #endif |
562 | +} | |
563 | + | |
564 | +/** | |
565 | + * gadget_is_superspeed() - return true if the hardware handles superspeed | |
566 | + * @g: controller that might support superspeed | |
567 | + */ | |
568 | +static inline int gadget_is_superspeed(struct usb_gadget *g) | |
569 | +{ | |
570 | + return g->max_speed >= USB_SPEED_SUPER; | |
554 | 571 | } |
555 | 572 | |
556 | 573 | /** |