diff options
author | Amit Virdi <amit.virdi@st.com> | 2014-08-22 05:06:36 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-08-27 15:13:18 -0400 |
commit | ef11982dd7a657512c362242508bb4021e0d67b6 (patch) | |
tree | c8f164cdbca20bb1543c381793a058d77a4e64d2 /drivers/usb/gadget/function/f_sourcesink.c | |
parent | c572a217d1b81209ae5a4fe09a96db758f86f10b (diff) |
usb: gadget: zero: Add support for interrupt EP
Interrupt endpoints behave quite similar to the bulk endpoints with the
difference that the endpoints expect data sending/reception request at
particular intervals till the whole data has not been transmitted.
The interrupt EP support is added to gadget zero. A new alternate setting (=2)
has been added. It has 2 interrupt endpoints. The default parameters are set as:
bInterval: 1 ms for FS and 8 uFrames (implying 1 ms) for HS/SS
wMaxPacketSize: 64 bytes for FS and 1024 bytes for HS/SS
However, the same can be overridden through the module parameter interface.
The code is tested for HS and SS on a platform having DWC3 controller.
Signed-off-by: Amit Virdi <amit.virdi@st.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget/function/f_sourcesink.c')
-rw-r--r-- | drivers/usb/gadget/function/f_sourcesink.c | 511 |
1 files changed, 491 insertions, 20 deletions
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index d3cd52db78fe..7c091a328228 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c | |||
@@ -23,6 +23,15 @@ | |||
23 | #include "gadget_chips.h" | 23 | #include "gadget_chips.h" |
24 | #include "u_f.h" | 24 | #include "u_f.h" |
25 | 25 | ||
26 | #define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x) | ||
27 | |||
28 | enum eptype { | ||
29 | EP_CONTROL = 0, | ||
30 | EP_BULK, | ||
31 | EP_ISOC, | ||
32 | EP_INTERRUPT, | ||
33 | }; | ||
34 | |||
26 | /* | 35 | /* |
27 | * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral | 36 | * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral |
28 | * controller drivers. | 37 | * controller drivers. |
@@ -55,6 +64,8 @@ struct f_sourcesink { | |||
55 | struct usb_ep *out_ep; | 64 | struct usb_ep *out_ep; |
56 | struct usb_ep *iso_in_ep; | 65 | struct usb_ep *iso_in_ep; |
57 | struct usb_ep *iso_out_ep; | 66 | struct usb_ep *iso_out_ep; |
67 | struct usb_ep *int_in_ep; | ||
68 | struct usb_ep *int_out_ep; | ||
58 | int cur_alt; | 69 | int cur_alt; |
59 | }; | 70 | }; |
60 | 71 | ||
@@ -68,6 +79,10 @@ static unsigned isoc_interval; | |||
68 | static unsigned isoc_maxpacket; | 79 | static unsigned isoc_maxpacket; |
69 | static unsigned isoc_mult; | 80 | static unsigned isoc_mult; |
70 | static unsigned isoc_maxburst; | 81 | static unsigned isoc_maxburst; |
82 | static unsigned int_interval; /* In ms */ | ||
83 | static unsigned int_maxpacket; | ||
84 | static unsigned int_mult; | ||
85 | static unsigned int_maxburst; | ||
71 | static unsigned buflen; | 86 | static unsigned buflen; |
72 | 87 | ||
73 | /*-------------------------------------------------------------------------*/ | 88 | /*-------------------------------------------------------------------------*/ |
@@ -92,6 +107,16 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = { | |||
92 | /* .iInterface = DYNAMIC */ | 107 | /* .iInterface = DYNAMIC */ |
93 | }; | 108 | }; |
94 | 109 | ||
110 | static struct usb_interface_descriptor source_sink_intf_alt2 = { | ||
111 | .bLength = USB_DT_INTERFACE_SIZE, | ||
112 | .bDescriptorType = USB_DT_INTERFACE, | ||
113 | |||
114 | .bAlternateSetting = 2, | ||
115 | .bNumEndpoints = 2, | ||
116 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | ||
117 | /* .iInterface = DYNAMIC */ | ||
118 | }; | ||
119 | |||
95 | /* full speed support: */ | 120 | /* full speed support: */ |
96 | 121 | ||
97 | static struct usb_endpoint_descriptor fs_source_desc = { | 122 | static struct usb_endpoint_descriptor fs_source_desc = { |
@@ -130,6 +155,26 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = { | |||
130 | .bInterval = 4, | 155 | .bInterval = 4, |
131 | }; | 156 | }; |
132 | 157 | ||
158 | static struct usb_endpoint_descriptor fs_int_source_desc = { | ||
159 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
160 | .bDescriptorType = USB_DT_ENDPOINT, | ||
161 | |||
162 | .bEndpointAddress = USB_DIR_IN, | ||
163 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
164 | .wMaxPacketSize = cpu_to_le16(64), | ||
165 | .bInterval = GZERO_INT_INTERVAL, | ||
166 | }; | ||
167 | |||
168 | static struct usb_endpoint_descriptor fs_int_sink_desc = { | ||
169 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
170 | .bDescriptorType = USB_DT_ENDPOINT, | ||
171 | |||
172 | .bEndpointAddress = USB_DIR_OUT, | ||
173 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
174 | .wMaxPacketSize = cpu_to_le16(64), | ||
175 | .bInterval = GZERO_INT_INTERVAL, | ||
176 | }; | ||
177 | |||
133 | static struct usb_descriptor_header *fs_source_sink_descs[] = { | 178 | static struct usb_descriptor_header *fs_source_sink_descs[] = { |
134 | (struct usb_descriptor_header *) &source_sink_intf_alt0, | 179 | (struct usb_descriptor_header *) &source_sink_intf_alt0, |
135 | (struct usb_descriptor_header *) &fs_sink_desc, | 180 | (struct usb_descriptor_header *) &fs_sink_desc, |
@@ -140,6 +185,10 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = { | |||
140 | (struct usb_descriptor_header *) &fs_source_desc, | 185 | (struct usb_descriptor_header *) &fs_source_desc, |
141 | (struct usb_descriptor_header *) &fs_iso_sink_desc, | 186 | (struct usb_descriptor_header *) &fs_iso_sink_desc, |
142 | (struct usb_descriptor_header *) &fs_iso_source_desc, | 187 | (struct usb_descriptor_header *) &fs_iso_source_desc, |
188 | (struct usb_descriptor_header *) &source_sink_intf_alt2, | ||
189 | #define FS_ALT_IFC_2_OFFSET 8 | ||
190 | (struct usb_descriptor_header *) &fs_int_sink_desc, | ||
191 | (struct usb_descriptor_header *) &fs_int_source_desc, | ||
143 | NULL, | 192 | NULL, |
144 | }; | 193 | }; |
145 | 194 | ||
@@ -179,6 +228,24 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = { | |||
179 | .bInterval = 4, | 228 | .bInterval = 4, |
180 | }; | 229 | }; |
181 | 230 | ||
231 | static struct usb_endpoint_descriptor hs_int_source_desc = { | ||
232 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
233 | .bDescriptorType = USB_DT_ENDPOINT, | ||
234 | |||
235 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
236 | .wMaxPacketSize = cpu_to_le16(1024), | ||
237 | .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), | ||
238 | }; | ||
239 | |||
240 | static struct usb_endpoint_descriptor hs_int_sink_desc = { | ||
241 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
242 | .bDescriptorType = USB_DT_ENDPOINT, | ||
243 | |||
244 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
245 | .wMaxPacketSize = cpu_to_le16(1024), | ||
246 | .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), | ||
247 | }; | ||
248 | |||
182 | static struct usb_descriptor_header *hs_source_sink_descs[] = { | 249 | static struct usb_descriptor_header *hs_source_sink_descs[] = { |
183 | (struct usb_descriptor_header *) &source_sink_intf_alt0, | 250 | (struct usb_descriptor_header *) &source_sink_intf_alt0, |
184 | (struct usb_descriptor_header *) &hs_source_desc, | 251 | (struct usb_descriptor_header *) &hs_source_desc, |
@@ -189,6 +256,10 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = { | |||
189 | (struct usb_descriptor_header *) &hs_sink_desc, | 256 | (struct usb_descriptor_header *) &hs_sink_desc, |
190 | (struct usb_descriptor_header *) &hs_iso_source_desc, | 257 | (struct usb_descriptor_header *) &hs_iso_source_desc, |
191 | (struct usb_descriptor_header *) &hs_iso_sink_desc, | 258 | (struct usb_descriptor_header *) &hs_iso_sink_desc, |
259 | (struct usb_descriptor_header *) &source_sink_intf_alt2, | ||
260 | #define HS_ALT_IFC_2_OFFSET 8 | ||
261 | (struct usb_descriptor_header *) &hs_int_source_desc, | ||
262 | (struct usb_descriptor_header *) &hs_int_sink_desc, | ||
192 | NULL, | 263 | NULL, |
193 | }; | 264 | }; |
194 | 265 | ||
@@ -264,6 +335,42 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { | |||
264 | .wBytesPerInterval = cpu_to_le16(1024), | 335 | .wBytesPerInterval = cpu_to_le16(1024), |
265 | }; | 336 | }; |
266 | 337 | ||
338 | static struct usb_endpoint_descriptor ss_int_source_desc = { | ||
339 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
340 | .bDescriptorType = USB_DT_ENDPOINT, | ||
341 | |||
342 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
343 | .wMaxPacketSize = cpu_to_le16(1024), | ||
344 | .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), | ||
345 | }; | ||
346 | |||
347 | struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = { | ||
348 | .bLength = USB_DT_SS_EP_COMP_SIZE, | ||
349 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
350 | |||
351 | .bMaxBurst = 0, | ||
352 | .bmAttributes = 0, | ||
353 | .wBytesPerInterval = cpu_to_le16(1024), | ||
354 | }; | ||
355 | |||
356 | static struct usb_endpoint_descriptor ss_int_sink_desc = { | ||
357 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
358 | .bDescriptorType = USB_DT_ENDPOINT, | ||
359 | |||
360 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
361 | .wMaxPacketSize = cpu_to_le16(1024), | ||
362 | .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), | ||
363 | }; | ||
364 | |||
365 | struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = { | ||
366 | .bLength = USB_DT_SS_EP_COMP_SIZE, | ||
367 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
368 | |||
369 | .bMaxBurst = 0, | ||
370 | .bmAttributes = 0, | ||
371 | .wBytesPerInterval = cpu_to_le16(1024), | ||
372 | }; | ||
373 | |||
267 | static struct usb_descriptor_header *ss_source_sink_descs[] = { | 374 | static struct usb_descriptor_header *ss_source_sink_descs[] = { |
268 | (struct usb_descriptor_header *) &source_sink_intf_alt0, | 375 | (struct usb_descriptor_header *) &source_sink_intf_alt0, |
269 | (struct usb_descriptor_header *) &ss_source_desc, | 376 | (struct usb_descriptor_header *) &ss_source_desc, |
@@ -280,6 +387,12 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = { | |||
280 | (struct usb_descriptor_header *) &ss_iso_source_comp_desc, | 387 | (struct usb_descriptor_header *) &ss_iso_source_comp_desc, |
281 | (struct usb_descriptor_header *) &ss_iso_sink_desc, | 388 | (struct usb_descriptor_header *) &ss_iso_sink_desc, |
282 | (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, | 389 | (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, |
390 | (struct usb_descriptor_header *) &source_sink_intf_alt2, | ||
391 | #define SS_ALT_IFC_2_OFFSET 14 | ||
392 | (struct usb_descriptor_header *) &ss_int_source_desc, | ||
393 | (struct usb_descriptor_header *) &ss_int_source_comp_desc, | ||
394 | (struct usb_descriptor_header *) &ss_int_sink_desc, | ||
395 | (struct usb_descriptor_header *) &ss_int_sink_comp_desc, | ||
283 | NULL, | 396 | NULL, |
284 | }; | 397 | }; |
285 | 398 | ||
@@ -301,6 +414,21 @@ static struct usb_gadget_strings *sourcesink_strings[] = { | |||
301 | }; | 414 | }; |
302 | 415 | ||
303 | /*-------------------------------------------------------------------------*/ | 416 | /*-------------------------------------------------------------------------*/ |
417 | static const char *get_ep_string(enum eptype ep_type) | ||
418 | { | ||
419 | switch (ep_type) { | ||
420 | case EP_ISOC: | ||
421 | return "ISOC-"; | ||
422 | case EP_INTERRUPT: | ||
423 | return "INTERRUPT-"; | ||
424 | case EP_CONTROL: | ||
425 | return "CTRL-"; | ||
426 | case EP_BULK: | ||
427 | return "BULK-"; | ||
428 | default: | ||
429 | return "UNKNOWN-"; | ||
430 | } | ||
431 | } | ||
304 | 432 | ||
305 | static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) | 433 | static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) |
306 | { | 434 | { |
@@ -328,7 +456,8 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) | |||
328 | 456 | ||
329 | void disable_endpoints(struct usb_composite_dev *cdev, | 457 | void disable_endpoints(struct usb_composite_dev *cdev, |
330 | struct usb_ep *in, struct usb_ep *out, | 458 | struct usb_ep *in, struct usb_ep *out, |
331 | struct usb_ep *iso_in, struct usb_ep *iso_out) | 459 | struct usb_ep *iso_in, struct usb_ep *iso_out, |
460 | struct usb_ep *int_in, struct usb_ep *int_out) | ||
332 | { | 461 | { |
333 | disable_ep(cdev, in); | 462 | disable_ep(cdev, in); |
334 | disable_ep(cdev, out); | 463 | disable_ep(cdev, out); |
@@ -336,6 +465,10 @@ void disable_endpoints(struct usb_composite_dev *cdev, | |||
336 | disable_ep(cdev, iso_in); | 465 | disable_ep(cdev, iso_in); |
337 | if (iso_out) | 466 | if (iso_out) |
338 | disable_ep(cdev, iso_out); | 467 | disable_ep(cdev, iso_out); |
468 | if (int_in) | ||
469 | disable_ep(cdev, int_in); | ||
470 | if (int_out) | ||
471 | disable_ep(cdev, int_out); | ||
339 | } | 472 | } |
340 | 473 | ||
341 | static int | 474 | static int |
@@ -352,6 +485,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) | |||
352 | return id; | 485 | return id; |
353 | source_sink_intf_alt0.bInterfaceNumber = id; | 486 | source_sink_intf_alt0.bInterfaceNumber = id; |
354 | source_sink_intf_alt1.bInterfaceNumber = id; | 487 | source_sink_intf_alt1.bInterfaceNumber = id; |
488 | source_sink_intf_alt2.bInterfaceNumber = id; | ||
355 | 489 | ||
356 | /* allocate bulk endpoints */ | 490 | /* allocate bulk endpoints */ |
357 | ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); | 491 | ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); |
@@ -412,14 +546,55 @@ no_iso: | |||
412 | if (isoc_maxpacket > 1024) | 546 | if (isoc_maxpacket > 1024) |
413 | isoc_maxpacket = 1024; | 547 | isoc_maxpacket = 1024; |
414 | 548 | ||
549 | /* sanity check the interrupt module parameters */ | ||
550 | if (int_interval < 1) | ||
551 | int_interval = 1; | ||
552 | if (int_interval > 4096) | ||
553 | int_interval = 4096; | ||
554 | if (int_mult > 2) | ||
555 | int_mult = 2; | ||
556 | if (int_maxburst > 15) | ||
557 | int_maxburst = 15; | ||
558 | |||
559 | /* fill in the FS interrupt descriptors from the module parameters */ | ||
560 | fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ? | ||
561 | 64 : int_maxpacket; | ||
562 | fs_int_source_desc.bInterval = int_interval > 255 ? | ||
563 | 255 : int_interval; | ||
564 | fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ? | ||
565 | 64 : int_maxpacket; | ||
566 | fs_int_sink_desc.bInterval = int_interval > 255 ? | ||
567 | 255 : int_interval; | ||
568 | |||
569 | /* allocate int endpoints */ | ||
570 | ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc); | ||
571 | if (!ss->int_in_ep) | ||
572 | goto no_int; | ||
573 | ss->int_in_ep->driver_data = cdev; /* claim */ | ||
574 | |||
575 | ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc); | ||
576 | if (ss->int_out_ep) { | ||
577 | ss->int_out_ep->driver_data = cdev; /* claim */ | ||
578 | } else { | ||
579 | ss->int_in_ep->driver_data = NULL; | ||
580 | ss->int_in_ep = NULL; | ||
581 | no_int: | ||
582 | fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL; | ||
583 | hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL; | ||
584 | ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL; | ||
585 | } | ||
586 | |||
587 | if (int_maxpacket > 1024) | ||
588 | int_maxpacket = 1024; | ||
589 | |||
415 | /* support high speed hardware */ | 590 | /* support high speed hardware */ |
416 | hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; | 591 | hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; |
417 | hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; | 592 | hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; |
418 | 593 | ||
419 | /* | 594 | /* |
420 | * Fill in the HS isoc descriptors from the module parameters. | 595 | * Fill in the HS isoc and interrupt descriptors from the module |
421 | * We assume that the user knows what they are doing and won't | 596 | * parameters. We assume that the user knows what they are doing and |
422 | * give parameters that their UDC doesn't support. | 597 | * won't give parameters that their UDC doesn't support. |
423 | */ | 598 | */ |
424 | hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; | 599 | hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; |
425 | hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; | 600 | hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; |
@@ -432,6 +607,17 @@ no_iso: | |||
432 | hs_iso_sink_desc.bInterval = isoc_interval; | 607 | hs_iso_sink_desc.bInterval = isoc_interval; |
433 | hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; | 608 | hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; |
434 | 609 | ||
610 | hs_int_source_desc.wMaxPacketSize = int_maxpacket; | ||
611 | hs_int_source_desc.wMaxPacketSize |= int_mult << 11; | ||
612 | hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); | ||
613 | hs_int_source_desc.bEndpointAddress = | ||
614 | fs_int_source_desc.bEndpointAddress; | ||
615 | |||
616 | hs_int_sink_desc.wMaxPacketSize = int_maxpacket; | ||
617 | hs_int_sink_desc.wMaxPacketSize |= int_mult << 11; | ||
618 | hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); | ||
619 | hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; | ||
620 | |||
435 | /* support super speed hardware */ | 621 | /* support super speed hardware */ |
436 | ss_source_desc.bEndpointAddress = | 622 | ss_source_desc.bEndpointAddress = |
437 | fs_source_desc.bEndpointAddress; | 623 | fs_source_desc.bEndpointAddress; |
@@ -439,9 +625,9 @@ no_iso: | |||
439 | fs_sink_desc.bEndpointAddress; | 625 | fs_sink_desc.bEndpointAddress; |
440 | 626 | ||
441 | /* | 627 | /* |
442 | * Fill in the SS isoc descriptors from the module parameters. | 628 | * Fill in the SS isoc and interrupt descriptors from the module |
443 | * We assume that the user knows what they are doing and won't | 629 | * parameters. We assume that the user knows what they are doing and |
444 | * give parameters that their UDC doesn't support. | 630 | * won't give parameters that their UDC doesn't support. |
445 | */ | 631 | */ |
446 | ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; | 632 | ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; |
447 | ss_iso_source_desc.bInterval = isoc_interval; | 633 | ss_iso_source_desc.bInterval = isoc_interval; |
@@ -460,17 +646,37 @@ no_iso: | |||
460 | isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); | 646 | isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); |
461 | ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; | 647 | ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; |
462 | 648 | ||
649 | ss_int_source_desc.wMaxPacketSize = int_maxpacket; | ||
650 | ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); | ||
651 | ss_int_source_comp_desc.bmAttributes = int_mult; | ||
652 | ss_int_source_comp_desc.bMaxBurst = int_maxburst; | ||
653 | ss_int_source_comp_desc.wBytesPerInterval = | ||
654 | int_maxpacket * (int_mult + 1) * (int_maxburst + 1); | ||
655 | ss_int_source_desc.bEndpointAddress = | ||
656 | fs_int_source_desc.bEndpointAddress; | ||
657 | |||
658 | ss_int_sink_desc.wMaxPacketSize = int_maxpacket; | ||
659 | ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); | ||
660 | ss_int_sink_comp_desc.bmAttributes = int_mult; | ||
661 | ss_int_sink_comp_desc.bMaxBurst = int_maxburst; | ||
662 | ss_int_sink_comp_desc.wBytesPerInterval = | ||
663 | int_maxpacket * (int_mult + 1) * (int_maxburst + 1); | ||
664 | ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; | ||
665 | |||
463 | ret = usb_assign_descriptors(f, fs_source_sink_descs, | 666 | ret = usb_assign_descriptors(f, fs_source_sink_descs, |
464 | hs_source_sink_descs, ss_source_sink_descs); | 667 | hs_source_sink_descs, ss_source_sink_descs); |
465 | if (ret) | 668 | if (ret) |
466 | return ret; | 669 | return ret; |
467 | 670 | ||
468 | DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", | 671 | DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, " |
672 | "INT-IN/%s, INT-OUT/%s\n", | ||
469 | (gadget_is_superspeed(c->cdev->gadget) ? "super" : | 673 | (gadget_is_superspeed(c->cdev->gadget) ? "super" : |
470 | (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), | 674 | (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), |
471 | f->name, ss->in_ep->name, ss->out_ep->name, | 675 | f->name, ss->in_ep->name, ss->out_ep->name, |
472 | ss->iso_in_ep ? ss->iso_in_ep->name : "<none>", | 676 | ss->iso_in_ep ? ss->iso_in_ep->name : "<none>", |
473 | ss->iso_out_ep ? ss->iso_out_ep->name : "<none>"); | 677 | ss->iso_out_ep ? ss->iso_out_ep->name : "<none>", |
678 | ss->int_in_ep ? ss->int_in_ep->name : "<none>", | ||
679 | ss->int_out_ep ? ss->int_out_ep->name : "<none>"); | ||
474 | return 0; | 680 | return 0; |
475 | } | 681 | } |
476 | 682 | ||
@@ -601,14 +807,15 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) | |||
601 | } | 807 | } |
602 | 808 | ||
603 | static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, | 809 | static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, |
604 | bool is_iso, int speed) | 810 | enum eptype ep_type, int speed) |
605 | { | 811 | { |
606 | struct usb_ep *ep; | 812 | struct usb_ep *ep; |
607 | struct usb_request *req; | 813 | struct usb_request *req; |
608 | int i, size, status; | 814 | int i, size, status; |
609 | 815 | ||
610 | for (i = 0; i < 8; i++) { | 816 | for (i = 0; i < 8; i++) { |
611 | if (is_iso) { | 817 | switch (ep_type) { |
818 | case EP_ISOC: | ||
612 | switch (speed) { | 819 | switch (speed) { |
613 | case USB_SPEED_SUPER: | 820 | case USB_SPEED_SUPER: |
614 | size = isoc_maxpacket * (isoc_mult + 1) * | 821 | size = isoc_maxpacket * (isoc_mult + 1) * |
@@ -624,9 +831,28 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, | |||
624 | } | 831 | } |
625 | ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; | 832 | ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; |
626 | req = ss_alloc_ep_req(ep, size); | 833 | req = ss_alloc_ep_req(ep, size); |
627 | } else { | 834 | break; |
835 | case EP_INTERRUPT: | ||
836 | switch (speed) { | ||
837 | case USB_SPEED_SUPER: | ||
838 | size = int_maxpacket * (int_mult + 1) * | ||
839 | (int_maxburst + 1); | ||
840 | break; | ||
841 | case USB_SPEED_HIGH: | ||
842 | size = int_maxpacket * (int_mult + 1); | ||
843 | break; | ||
844 | default: | ||
845 | size = int_maxpacket > 1023 ? | ||
846 | 1023 : int_maxpacket; | ||
847 | break; | ||
848 | } | ||
849 | ep = is_in ? ss->int_in_ep : ss->int_out_ep; | ||
850 | req = ss_alloc_ep_req(ep, size); | ||
851 | break; | ||
852 | default: | ||
628 | ep = is_in ? ss->in_ep : ss->out_ep; | 853 | ep = is_in ? ss->in_ep : ss->out_ep; |
629 | req = ss_alloc_ep_req(ep, 0); | 854 | req = ss_alloc_ep_req(ep, 0); |
855 | break; | ||
630 | } | 856 | } |
631 | 857 | ||
632 | if (!req) | 858 | if (!req) |
@@ -644,12 +870,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, | |||
644 | 870 | ||
645 | cdev = ss->function.config->cdev; | 871 | cdev = ss->function.config->cdev; |
646 | ERROR(cdev, "start %s%s %s --> %d\n", | 872 | ERROR(cdev, "start %s%s %s --> %d\n", |
647 | is_iso ? "ISO-" : "", is_in ? "IN" : "OUT", | 873 | get_ep_string(ep_type), is_in ? "IN" : "OUT", |
648 | ep->name, status); | 874 | ep->name, status); |
649 | free_ep_req(ep, req); | 875 | free_ep_req(ep, req); |
650 | } | 876 | } |
651 | 877 | ||
652 | if (!is_iso) | 878 | if (!(ep_type == EP_ISOC)) |
653 | break; | 879 | break; |
654 | } | 880 | } |
655 | 881 | ||
@@ -662,7 +888,7 @@ static void disable_source_sink(struct f_sourcesink *ss) | |||
662 | 888 | ||
663 | cdev = ss->function.config->cdev; | 889 | cdev = ss->function.config->cdev; |
664 | disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, | 890 | disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, |
665 | ss->iso_out_ep); | 891 | ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep); |
666 | VDBG(cdev, "%s disabled\n", ss->function.name); | 892 | VDBG(cdev, "%s disabled\n", ss->function.name); |
667 | } | 893 | } |
668 | 894 | ||
@@ -674,6 +900,62 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, | |||
674 | int speed = cdev->gadget->speed; | 900 | int speed = cdev->gadget->speed; |
675 | struct usb_ep *ep; | 901 | struct usb_ep *ep; |
676 | 902 | ||
903 | if (alt == 2) { | ||
904 | /* Configure for periodic interrupt endpoint */ | ||
905 | ep = ss->int_in_ep; | ||
906 | if (ep) { | ||
907 | result = config_ep_by_speed(cdev->gadget, | ||
908 | &(ss->function), ep); | ||
909 | if (result) | ||
910 | return result; | ||
911 | |||
912 | result = usb_ep_enable(ep); | ||
913 | if (result < 0) | ||
914 | return result; | ||
915 | |||
916 | ep->driver_data = ss; | ||
917 | result = source_sink_start_ep(ss, true, EP_INTERRUPT, | ||
918 | speed); | ||
919 | if (result < 0) { | ||
920 | fail1: | ||
921 | ep = ss->int_in_ep; | ||
922 | if (ep) { | ||
923 | usb_ep_disable(ep); | ||
924 | ep->driver_data = NULL; | ||
925 | } | ||
926 | return result; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | /* | ||
931 | * one interrupt endpoint reads (sinks) anything OUT (from the | ||
932 | * host) | ||
933 | */ | ||
934 | ep = ss->int_out_ep; | ||
935 | if (ep) { | ||
936 | result = config_ep_by_speed(cdev->gadget, | ||
937 | &(ss->function), ep); | ||
938 | if (result) | ||
939 | goto fail1; | ||
940 | |||
941 | result = usb_ep_enable(ep); | ||
942 | if (result < 0) | ||
943 | goto fail1; | ||
944 | |||
945 | ep->driver_data = ss; | ||
946 | result = source_sink_start_ep(ss, false, EP_INTERRUPT, | ||
947 | speed); | ||
948 | if (result < 0) { | ||
949 | ep = ss->int_out_ep; | ||
950 | usb_ep_disable(ep); | ||
951 | ep->driver_data = NULL; | ||
952 | goto fail1; | ||
953 | } | ||
954 | } | ||
955 | |||
956 | goto out; | ||
957 | } | ||
958 | |||
677 | /* one bulk endpoint writes (sources) zeroes IN (to the host) */ | 959 | /* one bulk endpoint writes (sources) zeroes IN (to the host) */ |
678 | ep = ss->in_ep; | 960 | ep = ss->in_ep; |
679 | result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); | 961 | result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); |
@@ -684,7 +966,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, | |||
684 | return result; | 966 | return result; |
685 | ep->driver_data = ss; | 967 | ep->driver_data = ss; |
686 | 968 | ||
687 | result = source_sink_start_ep(ss, true, false, speed); | 969 | result = source_sink_start_ep(ss, true, EP_BULK, speed); |
688 | if (result < 0) { | 970 | if (result < 0) { |
689 | fail: | 971 | fail: |
690 | ep = ss->in_ep; | 972 | ep = ss->in_ep; |
@@ -703,7 +985,7 @@ fail: | |||
703 | goto fail; | 985 | goto fail; |
704 | ep->driver_data = ss; | 986 | ep->driver_data = ss; |
705 | 987 | ||
706 | result = source_sink_start_ep(ss, false, false, speed); | 988 | result = source_sink_start_ep(ss, false, EP_BULK, speed); |
707 | if (result < 0) { | 989 | if (result < 0) { |
708 | fail2: | 990 | fail2: |
709 | ep = ss->out_ep; | 991 | ep = ss->out_ep; |
@@ -726,7 +1008,7 @@ fail2: | |||
726 | goto fail2; | 1008 | goto fail2; |
727 | ep->driver_data = ss; | 1009 | ep->driver_data = ss; |
728 | 1010 | ||
729 | result = source_sink_start_ep(ss, true, true, speed); | 1011 | result = source_sink_start_ep(ss, true, EP_ISOC, speed); |
730 | if (result < 0) { | 1012 | if (result < 0) { |
731 | fail3: | 1013 | fail3: |
732 | ep = ss->iso_in_ep; | 1014 | ep = ss->iso_in_ep; |
@@ -749,13 +1031,14 @@ fail3: | |||
749 | goto fail3; | 1031 | goto fail3; |
750 | ep->driver_data = ss; | 1032 | ep->driver_data = ss; |
751 | 1033 | ||
752 | result = source_sink_start_ep(ss, false, true, speed); | 1034 | result = source_sink_start_ep(ss, false, EP_ISOC, speed); |
753 | if (result < 0) { | 1035 | if (result < 0) { |
754 | usb_ep_disable(ep); | 1036 | usb_ep_disable(ep); |
755 | ep->driver_data = NULL; | 1037 | ep->driver_data = NULL; |
756 | goto fail3; | 1038 | goto fail3; |
757 | } | 1039 | } |
758 | } | 1040 | } |
1041 | |||
759 | out: | 1042 | out: |
760 | ss->cur_alt = alt; | 1043 | ss->cur_alt = alt; |
761 | 1044 | ||
@@ -771,6 +1054,8 @@ static int sourcesink_set_alt(struct usb_function *f, | |||
771 | 1054 | ||
772 | if (ss->in_ep->driver_data) | 1055 | if (ss->in_ep->driver_data) |
773 | disable_source_sink(ss); | 1056 | disable_source_sink(ss); |
1057 | else if (alt == 2 && ss->int_in_ep->driver_data) | ||
1058 | disable_source_sink(ss); | ||
774 | return enable_source_sink(cdev, ss, alt); | 1059 | return enable_source_sink(cdev, ss, alt); |
775 | } | 1060 | } |
776 | 1061 | ||
@@ -883,6 +1168,10 @@ static struct usb_function *source_sink_alloc_func( | |||
883 | isoc_maxpacket = ss_opts->isoc_maxpacket; | 1168 | isoc_maxpacket = ss_opts->isoc_maxpacket; |
884 | isoc_mult = ss_opts->isoc_mult; | 1169 | isoc_mult = ss_opts->isoc_mult; |
885 | isoc_maxburst = ss_opts->isoc_maxburst; | 1170 | isoc_maxburst = ss_opts->isoc_maxburst; |
1171 | int_interval = ss_opts->int_interval; | ||
1172 | int_maxpacket = ss_opts->int_maxpacket; | ||
1173 | int_mult = ss_opts->int_mult; | ||
1174 | int_maxburst = ss_opts->int_maxburst; | ||
886 | buflen = ss_opts->bulk_buflen; | 1175 | buflen = ss_opts->bulk_buflen; |
887 | 1176 | ||
888 | ss->function.name = "source/sink"; | 1177 | ss->function.name = "source/sink"; |
@@ -1179,6 +1468,182 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = | |||
1179 | f_ss_opts_bulk_buflen_show, | 1468 | f_ss_opts_bulk_buflen_show, |
1180 | f_ss_opts_bulk_buflen_store); | 1469 | f_ss_opts_bulk_buflen_store); |
1181 | 1470 | ||
1471 | static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page) | ||
1472 | { | ||
1473 | int result; | ||
1474 | |||
1475 | mutex_lock(&opts->lock); | ||
1476 | result = sprintf(page, "%d", opts->int_interval); | ||
1477 | mutex_unlock(&opts->lock); | ||
1478 | |||
1479 | return result; | ||
1480 | } | ||
1481 | |||
1482 | static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts, | ||
1483 | const char *page, size_t len) | ||
1484 | { | ||
1485 | int ret; | ||
1486 | u8 num; | ||
1487 | |||
1488 | mutex_lock(&opts->lock); | ||
1489 | if (opts->refcnt) { | ||
1490 | ret = -EBUSY; | ||
1491 | goto end; | ||
1492 | } | ||
1493 | |||
1494 | ret = kstrtou8(page, 0, &num); | ||
1495 | if (ret) | ||
1496 | goto end; | ||
1497 | |||
1498 | if (num > 4096) { | ||
1499 | ret = -EINVAL; | ||
1500 | goto end; | ||
1501 | } | ||
1502 | |||
1503 | opts->int_interval = num; | ||
1504 | ret = len; | ||
1505 | end: | ||
1506 | mutex_unlock(&opts->lock); | ||
1507 | return ret; | ||
1508 | } | ||
1509 | |||
1510 | static struct f_ss_opts_attribute f_ss_opts_int_interval = | ||
1511 | __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR, | ||
1512 | f_ss_opts_int_interval_show, | ||
1513 | f_ss_opts_int_interval_store); | ||
1514 | |||
1515 | static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page) | ||
1516 | { | ||
1517 | int result; | ||
1518 | |||
1519 | mutex_lock(&opts->lock); | ||
1520 | result = sprintf(page, "%d", opts->int_maxpacket); | ||
1521 | mutex_unlock(&opts->lock); | ||
1522 | |||
1523 | return result; | ||
1524 | } | ||
1525 | |||
1526 | static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts, | ||
1527 | const char *page, size_t len) | ||
1528 | { | ||
1529 | int ret; | ||
1530 | u16 num; | ||
1531 | |||
1532 | mutex_lock(&opts->lock); | ||
1533 | if (opts->refcnt) { | ||
1534 | ret = -EBUSY; | ||
1535 | goto end; | ||
1536 | } | ||
1537 | |||
1538 | ret = kstrtou16(page, 0, &num); | ||
1539 | if (ret) | ||
1540 | goto end; | ||
1541 | |||
1542 | if (num > 1024) { | ||
1543 | ret = -EINVAL; | ||
1544 | goto end; | ||
1545 | } | ||
1546 | |||
1547 | opts->int_maxpacket = num; | ||
1548 | ret = len; | ||
1549 | end: | ||
1550 | mutex_unlock(&opts->lock); | ||
1551 | return ret; | ||
1552 | } | ||
1553 | |||
1554 | static struct f_ss_opts_attribute f_ss_opts_int_maxpacket = | ||
1555 | __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR, | ||
1556 | f_ss_opts_int_maxpacket_show, | ||
1557 | f_ss_opts_int_maxpacket_store); | ||
1558 | |||
1559 | static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page) | ||
1560 | { | ||
1561 | int result; | ||
1562 | |||
1563 | mutex_lock(&opts->lock); | ||
1564 | result = sprintf(page, "%d", opts->int_mult); | ||
1565 | mutex_unlock(&opts->lock); | ||
1566 | |||
1567 | return result; | ||
1568 | } | ||
1569 | |||
1570 | static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts, | ||
1571 | const char *page, size_t len) | ||
1572 | { | ||
1573 | int ret; | ||
1574 | u8 num; | ||
1575 | |||
1576 | mutex_lock(&opts->lock); | ||
1577 | if (opts->refcnt) { | ||
1578 | ret = -EBUSY; | ||
1579 | goto end; | ||
1580 | } | ||
1581 | |||
1582 | ret = kstrtou8(page, 0, &num); | ||
1583 | if (ret) | ||
1584 | goto end; | ||
1585 | |||
1586 | if (num > 2) { | ||
1587 | ret = -EINVAL; | ||
1588 | goto end; | ||
1589 | } | ||
1590 | |||
1591 | opts->int_mult = num; | ||
1592 | ret = len; | ||
1593 | end: | ||
1594 | mutex_unlock(&opts->lock); | ||
1595 | return ret; | ||
1596 | } | ||
1597 | |||
1598 | static struct f_ss_opts_attribute f_ss_opts_int_mult = | ||
1599 | __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR, | ||
1600 | f_ss_opts_int_mult_show, | ||
1601 | f_ss_opts_int_mult_store); | ||
1602 | |||
1603 | static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page) | ||
1604 | { | ||
1605 | int result; | ||
1606 | |||
1607 | mutex_lock(&opts->lock); | ||
1608 | result = sprintf(page, "%d", opts->int_maxburst); | ||
1609 | mutex_unlock(&opts->lock); | ||
1610 | |||
1611 | return result; | ||
1612 | } | ||
1613 | |||
1614 | static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts, | ||
1615 | const char *page, size_t len) | ||
1616 | { | ||
1617 | int ret; | ||
1618 | u8 num; | ||
1619 | |||
1620 | mutex_lock(&opts->lock); | ||
1621 | if (opts->refcnt) { | ||
1622 | ret = -EBUSY; | ||
1623 | goto end; | ||
1624 | } | ||
1625 | |||
1626 | ret = kstrtou8(page, 0, &num); | ||
1627 | if (ret) | ||
1628 | goto end; | ||
1629 | |||
1630 | if (num > 15) { | ||
1631 | ret = -EINVAL; | ||
1632 | goto end; | ||
1633 | } | ||
1634 | |||
1635 | opts->int_maxburst = num; | ||
1636 | ret = len; | ||
1637 | end: | ||
1638 | mutex_unlock(&opts->lock); | ||
1639 | return ret; | ||
1640 | } | ||
1641 | |||
1642 | static struct f_ss_opts_attribute f_ss_opts_int_maxburst = | ||
1643 | __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR, | ||
1644 | f_ss_opts_int_maxburst_show, | ||
1645 | f_ss_opts_int_maxburst_store); | ||
1646 | |||
1182 | static struct configfs_attribute *ss_attrs[] = { | 1647 | static struct configfs_attribute *ss_attrs[] = { |
1183 | &f_ss_opts_pattern.attr, | 1648 | &f_ss_opts_pattern.attr, |
1184 | &f_ss_opts_isoc_interval.attr, | 1649 | &f_ss_opts_isoc_interval.attr, |
@@ -1186,6 +1651,10 @@ static struct configfs_attribute *ss_attrs[] = { | |||
1186 | &f_ss_opts_isoc_mult.attr, | 1651 | &f_ss_opts_isoc_mult.attr, |
1187 | &f_ss_opts_isoc_maxburst.attr, | 1652 | &f_ss_opts_isoc_maxburst.attr, |
1188 | &f_ss_opts_bulk_buflen.attr, | 1653 | &f_ss_opts_bulk_buflen.attr, |
1654 | &f_ss_opts_int_interval.attr, | ||
1655 | &f_ss_opts_int_maxpacket.attr, | ||
1656 | &f_ss_opts_int_mult.attr, | ||
1657 | &f_ss_opts_int_maxburst.attr, | ||
1189 | NULL, | 1658 | NULL, |
1190 | }; | 1659 | }; |
1191 | 1660 | ||
@@ -1215,6 +1684,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void) | |||
1215 | ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; | 1684 | ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; |
1216 | ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; | 1685 | ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; |
1217 | ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; | 1686 | ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; |
1687 | ss_opts->int_interval = GZERO_INT_INTERVAL; | ||
1688 | ss_opts->int_maxpacket = GZERO_INT_MAXPACKET; | ||
1218 | 1689 | ||
1219 | config_group_init_type_name(&ss_opts->func_inst.group, "", | 1690 | config_group_init_type_name(&ss_opts->func_inst.group, "", |
1220 | &ss_func_type); | 1691 | &ss_func_type); |