diff options
author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2014-07-15 07:09:46 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-07-16 13:50:36 -0400 |
commit | 00a2430ff07d4e0e0e7e24e02fd8adede333b797 (patch) | |
tree | fd6680b6a8941ac1a3149ae4b77f159701bb1a61 /drivers/usb/gadget/function/f_sourcesink.c | |
parent | 90fccb529d241b55829701cfb9eb3086570f38b8 (diff) |
usb: gadget: Gadget directory cleanup - group usb functions
The drivers/usb/gadget directory contains many files.
Files which are related can be distributed into separate directories.
This patch moves the USB functions implementations into a separate directory.
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.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 | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c new file mode 100644 index 000000000000..d3cd52db78fe --- /dev/null +++ b/drivers/usb/gadget/function/f_sourcesink.c | |||
@@ -0,0 +1,1247 @@ | |||
1 | /* | ||
2 | * f_sourcesink.c - USB peripheral source/sink configuration driver | ||
3 | * | ||
4 | * Copyright (C) 2003-2008 David Brownell | ||
5 | * Copyright (C) 2008 by Nokia Corporation | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | /* #define VERBOSE_DEBUG */ | ||
14 | |||
15 | #include <linux/slab.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/usb/composite.h> | ||
20 | #include <linux/err.h> | ||
21 | |||
22 | #include "g_zero.h" | ||
23 | #include "gadget_chips.h" | ||
24 | #include "u_f.h" | ||
25 | |||
26 | /* | ||
27 | * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral | ||
28 | * controller drivers. | ||
29 | * | ||
30 | * This just sinks bulk packets OUT to the peripheral and sources them IN | ||
31 | * to the host, optionally with specific data patterns for integrity tests. | ||
32 | * As such it supports basic functionality and load tests. | ||
33 | * | ||
34 | * In terms of control messaging, this supports all the standard requests | ||
35 | * plus two that support control-OUT tests. If the optional "autoresume" | ||
36 | * mode is enabled, it provides good functional coverage for the "USBCV" | ||
37 | * test harness from USB-IF. | ||
38 | * | ||
39 | * Note that because this doesn't queue more than one request at a time, | ||
40 | * some other function must be used to test queueing logic. The network | ||
41 | * link (g_ether) is the best overall option for that, since its TX and RX | ||
42 | * queues are relatively independent, will receive a range of packet sizes, | ||
43 | * and can often be made to run out completely. Those issues are important | ||
44 | * when stress testing peripheral controller drivers. | ||
45 | * | ||
46 | * | ||
47 | * This is currently packaged as a configuration driver, which can't be | ||
48 | * combined with other functions to make composite devices. However, it | ||
49 | * can be combined with other independent configurations. | ||
50 | */ | ||
51 | struct f_sourcesink { | ||
52 | struct usb_function function; | ||
53 | |||
54 | struct usb_ep *in_ep; | ||
55 | struct usb_ep *out_ep; | ||
56 | struct usb_ep *iso_in_ep; | ||
57 | struct usb_ep *iso_out_ep; | ||
58 | int cur_alt; | ||
59 | }; | ||
60 | |||
61 | static inline struct f_sourcesink *func_to_ss(struct usb_function *f) | ||
62 | { | ||
63 | return container_of(f, struct f_sourcesink, function); | ||
64 | } | ||
65 | |||
66 | static unsigned pattern; | ||
67 | static unsigned isoc_interval; | ||
68 | static unsigned isoc_maxpacket; | ||
69 | static unsigned isoc_mult; | ||
70 | static unsigned isoc_maxburst; | ||
71 | static unsigned buflen; | ||
72 | |||
73 | /*-------------------------------------------------------------------------*/ | ||
74 | |||
75 | static struct usb_interface_descriptor source_sink_intf_alt0 = { | ||
76 | .bLength = USB_DT_INTERFACE_SIZE, | ||
77 | .bDescriptorType = USB_DT_INTERFACE, | ||
78 | |||
79 | .bAlternateSetting = 0, | ||
80 | .bNumEndpoints = 2, | ||
81 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | ||
82 | /* .iInterface = DYNAMIC */ | ||
83 | }; | ||
84 | |||
85 | static struct usb_interface_descriptor source_sink_intf_alt1 = { | ||
86 | .bLength = USB_DT_INTERFACE_SIZE, | ||
87 | .bDescriptorType = USB_DT_INTERFACE, | ||
88 | |||
89 | .bAlternateSetting = 1, | ||
90 | .bNumEndpoints = 4, | ||
91 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | ||
92 | /* .iInterface = DYNAMIC */ | ||
93 | }; | ||
94 | |||
95 | /* full speed support: */ | ||
96 | |||
97 | static struct usb_endpoint_descriptor fs_source_desc = { | ||
98 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
99 | .bDescriptorType = USB_DT_ENDPOINT, | ||
100 | |||
101 | .bEndpointAddress = USB_DIR_IN, | ||
102 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
103 | }; | ||
104 | |||
105 | static struct usb_endpoint_descriptor fs_sink_desc = { | ||
106 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
107 | .bDescriptorType = USB_DT_ENDPOINT, | ||
108 | |||
109 | .bEndpointAddress = USB_DIR_OUT, | ||
110 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
111 | }; | ||
112 | |||
113 | static struct usb_endpoint_descriptor fs_iso_source_desc = { | ||
114 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
115 | .bDescriptorType = USB_DT_ENDPOINT, | ||
116 | |||
117 | .bEndpointAddress = USB_DIR_IN, | ||
118 | .bmAttributes = USB_ENDPOINT_XFER_ISOC, | ||
119 | .wMaxPacketSize = cpu_to_le16(1023), | ||
120 | .bInterval = 4, | ||
121 | }; | ||
122 | |||
123 | static struct usb_endpoint_descriptor fs_iso_sink_desc = { | ||
124 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
125 | .bDescriptorType = USB_DT_ENDPOINT, | ||
126 | |||
127 | .bEndpointAddress = USB_DIR_OUT, | ||
128 | .bmAttributes = USB_ENDPOINT_XFER_ISOC, | ||
129 | .wMaxPacketSize = cpu_to_le16(1023), | ||
130 | .bInterval = 4, | ||
131 | }; | ||
132 | |||
133 | static struct usb_descriptor_header *fs_source_sink_descs[] = { | ||
134 | (struct usb_descriptor_header *) &source_sink_intf_alt0, | ||
135 | (struct usb_descriptor_header *) &fs_sink_desc, | ||
136 | (struct usb_descriptor_header *) &fs_source_desc, | ||
137 | (struct usb_descriptor_header *) &source_sink_intf_alt1, | ||
138 | #define FS_ALT_IFC_1_OFFSET 3 | ||
139 | (struct usb_descriptor_header *) &fs_sink_desc, | ||
140 | (struct usb_descriptor_header *) &fs_source_desc, | ||
141 | (struct usb_descriptor_header *) &fs_iso_sink_desc, | ||
142 | (struct usb_descriptor_header *) &fs_iso_source_desc, | ||
143 | NULL, | ||
144 | }; | ||
145 | |||
146 | /* high speed support: */ | ||
147 | |||
148 | static struct usb_endpoint_descriptor hs_source_desc = { | ||
149 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
150 | .bDescriptorType = USB_DT_ENDPOINT, | ||
151 | |||
152 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
153 | .wMaxPacketSize = cpu_to_le16(512), | ||
154 | }; | ||
155 | |||
156 | static struct usb_endpoint_descriptor hs_sink_desc = { | ||
157 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
158 | .bDescriptorType = USB_DT_ENDPOINT, | ||
159 | |||
160 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
161 | .wMaxPacketSize = cpu_to_le16(512), | ||
162 | }; | ||
163 | |||
164 | static struct usb_endpoint_descriptor hs_iso_source_desc = { | ||
165 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
166 | .bDescriptorType = USB_DT_ENDPOINT, | ||
167 | |||
168 | .bmAttributes = USB_ENDPOINT_XFER_ISOC, | ||
169 | .wMaxPacketSize = cpu_to_le16(1024), | ||
170 | .bInterval = 4, | ||
171 | }; | ||
172 | |||
173 | static struct usb_endpoint_descriptor hs_iso_sink_desc = { | ||
174 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
175 | .bDescriptorType = USB_DT_ENDPOINT, | ||
176 | |||
177 | .bmAttributes = USB_ENDPOINT_XFER_ISOC, | ||
178 | .wMaxPacketSize = cpu_to_le16(1024), | ||
179 | .bInterval = 4, | ||
180 | }; | ||
181 | |||
182 | static struct usb_descriptor_header *hs_source_sink_descs[] = { | ||
183 | (struct usb_descriptor_header *) &source_sink_intf_alt0, | ||
184 | (struct usb_descriptor_header *) &hs_source_desc, | ||
185 | (struct usb_descriptor_header *) &hs_sink_desc, | ||
186 | (struct usb_descriptor_header *) &source_sink_intf_alt1, | ||
187 | #define HS_ALT_IFC_1_OFFSET 3 | ||
188 | (struct usb_descriptor_header *) &hs_source_desc, | ||
189 | (struct usb_descriptor_header *) &hs_sink_desc, | ||
190 | (struct usb_descriptor_header *) &hs_iso_source_desc, | ||
191 | (struct usb_descriptor_header *) &hs_iso_sink_desc, | ||
192 | NULL, | ||
193 | }; | ||
194 | |||
195 | /* super speed support: */ | ||
196 | |||
197 | static struct usb_endpoint_descriptor ss_source_desc = { | ||
198 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
199 | .bDescriptorType = USB_DT_ENDPOINT, | ||
200 | |||
201 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
202 | .wMaxPacketSize = cpu_to_le16(1024), | ||
203 | }; | ||
204 | |||
205 | static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = { | ||
206 | .bLength = USB_DT_SS_EP_COMP_SIZE, | ||
207 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
208 | |||
209 | .bMaxBurst = 0, | ||
210 | .bmAttributes = 0, | ||
211 | .wBytesPerInterval = 0, | ||
212 | }; | ||
213 | |||
214 | static struct usb_endpoint_descriptor ss_sink_desc = { | ||
215 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
216 | .bDescriptorType = USB_DT_ENDPOINT, | ||
217 | |||
218 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
219 | .wMaxPacketSize = cpu_to_le16(1024), | ||
220 | }; | ||
221 | |||
222 | static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = { | ||
223 | .bLength = USB_DT_SS_EP_COMP_SIZE, | ||
224 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
225 | |||
226 | .bMaxBurst = 0, | ||
227 | .bmAttributes = 0, | ||
228 | .wBytesPerInterval = 0, | ||
229 | }; | ||
230 | |||
231 | static struct usb_endpoint_descriptor ss_iso_source_desc = { | ||
232 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
233 | .bDescriptorType = USB_DT_ENDPOINT, | ||
234 | |||
235 | .bmAttributes = USB_ENDPOINT_XFER_ISOC, | ||
236 | .wMaxPacketSize = cpu_to_le16(1024), | ||
237 | .bInterval = 4, | ||
238 | }; | ||
239 | |||
240 | static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = { | ||
241 | .bLength = USB_DT_SS_EP_COMP_SIZE, | ||
242 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
243 | |||
244 | .bMaxBurst = 0, | ||
245 | .bmAttributes = 0, | ||
246 | .wBytesPerInterval = cpu_to_le16(1024), | ||
247 | }; | ||
248 | |||
249 | static struct usb_endpoint_descriptor ss_iso_sink_desc = { | ||
250 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
251 | .bDescriptorType = USB_DT_ENDPOINT, | ||
252 | |||
253 | .bmAttributes = USB_ENDPOINT_XFER_ISOC, | ||
254 | .wMaxPacketSize = cpu_to_le16(1024), | ||
255 | .bInterval = 4, | ||
256 | }; | ||
257 | |||
258 | static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { | ||
259 | .bLength = USB_DT_SS_EP_COMP_SIZE, | ||
260 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
261 | |||
262 | .bMaxBurst = 0, | ||
263 | .bmAttributes = 0, | ||
264 | .wBytesPerInterval = cpu_to_le16(1024), | ||
265 | }; | ||
266 | |||
267 | static struct usb_descriptor_header *ss_source_sink_descs[] = { | ||
268 | (struct usb_descriptor_header *) &source_sink_intf_alt0, | ||
269 | (struct usb_descriptor_header *) &ss_source_desc, | ||
270 | (struct usb_descriptor_header *) &ss_source_comp_desc, | ||
271 | (struct usb_descriptor_header *) &ss_sink_desc, | ||
272 | (struct usb_descriptor_header *) &ss_sink_comp_desc, | ||
273 | (struct usb_descriptor_header *) &source_sink_intf_alt1, | ||
274 | #define SS_ALT_IFC_1_OFFSET 5 | ||
275 | (struct usb_descriptor_header *) &ss_source_desc, | ||
276 | (struct usb_descriptor_header *) &ss_source_comp_desc, | ||
277 | (struct usb_descriptor_header *) &ss_sink_desc, | ||
278 | (struct usb_descriptor_header *) &ss_sink_comp_desc, | ||
279 | (struct usb_descriptor_header *) &ss_iso_source_desc, | ||
280 | (struct usb_descriptor_header *) &ss_iso_source_comp_desc, | ||
281 | (struct usb_descriptor_header *) &ss_iso_sink_desc, | ||
282 | (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, | ||
283 | NULL, | ||
284 | }; | ||
285 | |||
286 | /* function-specific strings: */ | ||
287 | |||
288 | static struct usb_string strings_sourcesink[] = { | ||
289 | [0].s = "source and sink data", | ||
290 | { } /* end of list */ | ||
291 | }; | ||
292 | |||
293 | static struct usb_gadget_strings stringtab_sourcesink = { | ||
294 | .language = 0x0409, /* en-us */ | ||
295 | .strings = strings_sourcesink, | ||
296 | }; | ||
297 | |||
298 | static struct usb_gadget_strings *sourcesink_strings[] = { | ||
299 | &stringtab_sourcesink, | ||
300 | NULL, | ||
301 | }; | ||
302 | |||
303 | /*-------------------------------------------------------------------------*/ | ||
304 | |||
305 | static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) | ||
306 | { | ||
307 | return alloc_ep_req(ep, len, buflen); | ||
308 | } | ||
309 | |||
310 | void free_ep_req(struct usb_ep *ep, struct usb_request *req) | ||
311 | { | ||
312 | kfree(req->buf); | ||
313 | usb_ep_free_request(ep, req); | ||
314 | } | ||
315 | |||
316 | static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) | ||
317 | { | ||
318 | int value; | ||
319 | |||
320 | if (ep->driver_data) { | ||
321 | value = usb_ep_disable(ep); | ||
322 | if (value < 0) | ||
323 | DBG(cdev, "disable %s --> %d\n", | ||
324 | ep->name, value); | ||
325 | ep->driver_data = NULL; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | void disable_endpoints(struct usb_composite_dev *cdev, | ||
330 | struct usb_ep *in, struct usb_ep *out, | ||
331 | struct usb_ep *iso_in, struct usb_ep *iso_out) | ||
332 | { | ||
333 | disable_ep(cdev, in); | ||
334 | disable_ep(cdev, out); | ||
335 | if (iso_in) | ||
336 | disable_ep(cdev, iso_in); | ||
337 | if (iso_out) | ||
338 | disable_ep(cdev, iso_out); | ||
339 | } | ||
340 | |||
341 | static int | ||
342 | sourcesink_bind(struct usb_configuration *c, struct usb_function *f) | ||
343 | { | ||
344 | struct usb_composite_dev *cdev = c->cdev; | ||
345 | struct f_sourcesink *ss = func_to_ss(f); | ||
346 | int id; | ||
347 | int ret; | ||
348 | |||
349 | /* allocate interface ID(s) */ | ||
350 | id = usb_interface_id(c, f); | ||
351 | if (id < 0) | ||
352 | return id; | ||
353 | source_sink_intf_alt0.bInterfaceNumber = id; | ||
354 | source_sink_intf_alt1.bInterfaceNumber = id; | ||
355 | |||
356 | /* allocate bulk endpoints */ | ||
357 | ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); | ||
358 | if (!ss->in_ep) { | ||
359 | autoconf_fail: | ||
360 | ERROR(cdev, "%s: can't autoconfigure on %s\n", | ||
361 | f->name, cdev->gadget->name); | ||
362 | return -ENODEV; | ||
363 | } | ||
364 | ss->in_ep->driver_data = cdev; /* claim */ | ||
365 | |||
366 | ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); | ||
367 | if (!ss->out_ep) | ||
368 | goto autoconf_fail; | ||
369 | ss->out_ep->driver_data = cdev; /* claim */ | ||
370 | |||
371 | /* sanity check the isoc module parameters */ | ||
372 | if (isoc_interval < 1) | ||
373 | isoc_interval = 1; | ||
374 | if (isoc_interval > 16) | ||
375 | isoc_interval = 16; | ||
376 | if (isoc_mult > 2) | ||
377 | isoc_mult = 2; | ||
378 | if (isoc_maxburst > 15) | ||
379 | isoc_maxburst = 15; | ||
380 | |||
381 | /* fill in the FS isoc descriptors from the module parameters */ | ||
382 | fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ? | ||
383 | 1023 : isoc_maxpacket; | ||
384 | fs_iso_source_desc.bInterval = isoc_interval; | ||
385 | fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ? | ||
386 | 1023 : isoc_maxpacket; | ||
387 | fs_iso_sink_desc.bInterval = isoc_interval; | ||
388 | |||
389 | /* allocate iso endpoints */ | ||
390 | ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc); | ||
391 | if (!ss->iso_in_ep) | ||
392 | goto no_iso; | ||
393 | ss->iso_in_ep->driver_data = cdev; /* claim */ | ||
394 | |||
395 | ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc); | ||
396 | if (ss->iso_out_ep) { | ||
397 | ss->iso_out_ep->driver_data = cdev; /* claim */ | ||
398 | } else { | ||
399 | ss->iso_in_ep->driver_data = NULL; | ||
400 | ss->iso_in_ep = NULL; | ||
401 | no_iso: | ||
402 | /* | ||
403 | * We still want to work even if the UDC doesn't have isoc | ||
404 | * endpoints, so null out the alt interface that contains | ||
405 | * them and continue. | ||
406 | */ | ||
407 | fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL; | ||
408 | hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL; | ||
409 | ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL; | ||
410 | } | ||
411 | |||
412 | if (isoc_maxpacket > 1024) | ||
413 | isoc_maxpacket = 1024; | ||
414 | |||
415 | /* support high speed hardware */ | ||
416 | hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; | ||
417 | hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; | ||
418 | |||
419 | /* | ||
420 | * Fill in the HS isoc descriptors from the module parameters. | ||
421 | * We assume that the user knows what they are doing and won't | ||
422 | * give parameters that their UDC doesn't support. | ||
423 | */ | ||
424 | hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; | ||
425 | hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; | ||
426 | hs_iso_source_desc.bInterval = isoc_interval; | ||
427 | hs_iso_source_desc.bEndpointAddress = | ||
428 | fs_iso_source_desc.bEndpointAddress; | ||
429 | |||
430 | hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; | ||
431 | hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11; | ||
432 | hs_iso_sink_desc.bInterval = isoc_interval; | ||
433 | hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; | ||
434 | |||
435 | /* support super speed hardware */ | ||
436 | ss_source_desc.bEndpointAddress = | ||
437 | fs_source_desc.bEndpointAddress; | ||
438 | ss_sink_desc.bEndpointAddress = | ||
439 | fs_sink_desc.bEndpointAddress; | ||
440 | |||
441 | /* | ||
442 | * Fill in the SS isoc descriptors from the module parameters. | ||
443 | * We assume that the user knows what they are doing and won't | ||
444 | * give parameters that their UDC doesn't support. | ||
445 | */ | ||
446 | ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; | ||
447 | ss_iso_source_desc.bInterval = isoc_interval; | ||
448 | ss_iso_source_comp_desc.bmAttributes = isoc_mult; | ||
449 | ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst; | ||
450 | ss_iso_source_comp_desc.wBytesPerInterval = | ||
451 | isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); | ||
452 | ss_iso_source_desc.bEndpointAddress = | ||
453 | fs_iso_source_desc.bEndpointAddress; | ||
454 | |||
455 | ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; | ||
456 | ss_iso_sink_desc.bInterval = isoc_interval; | ||
457 | ss_iso_sink_comp_desc.bmAttributes = isoc_mult; | ||
458 | ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst; | ||
459 | ss_iso_sink_comp_desc.wBytesPerInterval = | ||
460 | isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); | ||
461 | ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; | ||
462 | |||
463 | ret = usb_assign_descriptors(f, fs_source_sink_descs, | ||
464 | hs_source_sink_descs, ss_source_sink_descs); | ||
465 | if (ret) | ||
466 | return ret; | ||
467 | |||
468 | DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", | ||
469 | (gadget_is_superspeed(c->cdev->gadget) ? "super" : | ||
470 | (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), | ||
471 | f->name, ss->in_ep->name, ss->out_ep->name, | ||
472 | ss->iso_in_ep ? ss->iso_in_ep->name : "<none>", | ||
473 | ss->iso_out_ep ? ss->iso_out_ep->name : "<none>"); | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static void | ||
478 | sourcesink_free_func(struct usb_function *f) | ||
479 | { | ||
480 | struct f_ss_opts *opts; | ||
481 | |||
482 | opts = container_of(f->fi, struct f_ss_opts, func_inst); | ||
483 | |||
484 | mutex_lock(&opts->lock); | ||
485 | opts->refcnt--; | ||
486 | mutex_unlock(&opts->lock); | ||
487 | |||
488 | usb_free_all_descriptors(f); | ||
489 | kfree(func_to_ss(f)); | ||
490 | } | ||
491 | |||
492 | /* optionally require specific source/sink data patterns */ | ||
493 | static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) | ||
494 | { | ||
495 | unsigned i; | ||
496 | u8 *buf = req->buf; | ||
497 | struct usb_composite_dev *cdev = ss->function.config->cdev; | ||
498 | |||
499 | if (pattern == 2) | ||
500 | return 0; | ||
501 | |||
502 | for (i = 0; i < req->actual; i++, buf++) { | ||
503 | switch (pattern) { | ||
504 | |||
505 | /* all-zeroes has no synchronization issues */ | ||
506 | case 0: | ||
507 | if (*buf == 0) | ||
508 | continue; | ||
509 | break; | ||
510 | |||
511 | /* "mod63" stays in sync with short-terminated transfers, | ||
512 | * OR otherwise when host and gadget agree on how large | ||
513 | * each usb transfer request should be. Resync is done | ||
514 | * with set_interface or set_config. (We *WANT* it to | ||
515 | * get quickly out of sync if controllers or their drivers | ||
516 | * stutter for any reason, including buffer duplication...) | ||
517 | */ | ||
518 | case 1: | ||
519 | if (*buf == (u8)(i % 63)) | ||
520 | continue; | ||
521 | break; | ||
522 | } | ||
523 | ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf); | ||
524 | usb_ep_set_halt(ss->out_ep); | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) | ||
531 | { | ||
532 | unsigned i; | ||
533 | u8 *buf = req->buf; | ||
534 | |||
535 | switch (pattern) { | ||
536 | case 0: | ||
537 | memset(req->buf, 0, req->length); | ||
538 | break; | ||
539 | case 1: | ||
540 | for (i = 0; i < req->length; i++) | ||
541 | *buf++ = (u8) (i % 63); | ||
542 | break; | ||
543 | case 2: | ||
544 | break; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) | ||
549 | { | ||
550 | struct usb_composite_dev *cdev; | ||
551 | struct f_sourcesink *ss = ep->driver_data; | ||
552 | int status = req->status; | ||
553 | |||
554 | /* driver_data will be null if ep has been disabled */ | ||
555 | if (!ss) | ||
556 | return; | ||
557 | |||
558 | cdev = ss->function.config->cdev; | ||
559 | |||
560 | switch (status) { | ||
561 | |||
562 | case 0: /* normal completion? */ | ||
563 | if (ep == ss->out_ep) { | ||
564 | check_read_data(ss, req); | ||
565 | if (pattern != 2) | ||
566 | memset(req->buf, 0x55, req->length); | ||
567 | } | ||
568 | break; | ||
569 | |||
570 | /* this endpoint is normally active while we're configured */ | ||
571 | case -ECONNABORTED: /* hardware forced ep reset */ | ||
572 | case -ECONNRESET: /* request dequeued */ | ||
573 | case -ESHUTDOWN: /* disconnect from host */ | ||
574 | VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, | ||
575 | req->actual, req->length); | ||
576 | if (ep == ss->out_ep) | ||
577 | check_read_data(ss, req); | ||
578 | free_ep_req(ep, req); | ||
579 | return; | ||
580 | |||
581 | case -EOVERFLOW: /* buffer overrun on read means that | ||
582 | * we didn't provide a big enough | ||
583 | * buffer. | ||
584 | */ | ||
585 | default: | ||
586 | #if 1 | ||
587 | DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, | ||
588 | status, req->actual, req->length); | ||
589 | #endif | ||
590 | case -EREMOTEIO: /* short read */ | ||
591 | break; | ||
592 | } | ||
593 | |||
594 | status = usb_ep_queue(ep, req, GFP_ATOMIC); | ||
595 | if (status) { | ||
596 | ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", | ||
597 | ep->name, req->length, status); | ||
598 | usb_ep_set_halt(ep); | ||
599 | /* FIXME recover later ... somehow */ | ||
600 | } | ||
601 | } | ||
602 | |||
603 | static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, | ||
604 | bool is_iso, int speed) | ||
605 | { | ||
606 | struct usb_ep *ep; | ||
607 | struct usb_request *req; | ||
608 | int i, size, status; | ||
609 | |||
610 | for (i = 0; i < 8; i++) { | ||
611 | if (is_iso) { | ||
612 | switch (speed) { | ||
613 | case USB_SPEED_SUPER: | ||
614 | size = isoc_maxpacket * (isoc_mult + 1) * | ||
615 | (isoc_maxburst + 1); | ||
616 | break; | ||
617 | case USB_SPEED_HIGH: | ||
618 | size = isoc_maxpacket * (isoc_mult + 1); | ||
619 | break; | ||
620 | default: | ||
621 | size = isoc_maxpacket > 1023 ? | ||
622 | 1023 : isoc_maxpacket; | ||
623 | break; | ||
624 | } | ||
625 | ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; | ||
626 | req = ss_alloc_ep_req(ep, size); | ||
627 | } else { | ||
628 | ep = is_in ? ss->in_ep : ss->out_ep; | ||
629 | req = ss_alloc_ep_req(ep, 0); | ||
630 | } | ||
631 | |||
632 | if (!req) | ||
633 | return -ENOMEM; | ||
634 | |||
635 | req->complete = source_sink_complete; | ||
636 | if (is_in) | ||
637 | reinit_write_data(ep, req); | ||
638 | else if (pattern != 2) | ||
639 | memset(req->buf, 0x55, req->length); | ||
640 | |||
641 | status = usb_ep_queue(ep, req, GFP_ATOMIC); | ||
642 | if (status) { | ||
643 | struct usb_composite_dev *cdev; | ||
644 | |||
645 | cdev = ss->function.config->cdev; | ||
646 | ERROR(cdev, "start %s%s %s --> %d\n", | ||
647 | is_iso ? "ISO-" : "", is_in ? "IN" : "OUT", | ||
648 | ep->name, status); | ||
649 | free_ep_req(ep, req); | ||
650 | } | ||
651 | |||
652 | if (!is_iso) | ||
653 | break; | ||
654 | } | ||
655 | |||
656 | return status; | ||
657 | } | ||
658 | |||
659 | static void disable_source_sink(struct f_sourcesink *ss) | ||
660 | { | ||
661 | struct usb_composite_dev *cdev; | ||
662 | |||
663 | cdev = ss->function.config->cdev; | ||
664 | disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, | ||
665 | ss->iso_out_ep); | ||
666 | VDBG(cdev, "%s disabled\n", ss->function.name); | ||
667 | } | ||
668 | |||
669 | static int | ||
670 | enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, | ||
671 | int alt) | ||
672 | { | ||
673 | int result = 0; | ||
674 | int speed = cdev->gadget->speed; | ||
675 | struct usb_ep *ep; | ||
676 | |||
677 | /* one bulk endpoint writes (sources) zeroes IN (to the host) */ | ||
678 | ep = ss->in_ep; | ||
679 | result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); | ||
680 | if (result) | ||
681 | return result; | ||
682 | result = usb_ep_enable(ep); | ||
683 | if (result < 0) | ||
684 | return result; | ||
685 | ep->driver_data = ss; | ||
686 | |||
687 | result = source_sink_start_ep(ss, true, false, speed); | ||
688 | if (result < 0) { | ||
689 | fail: | ||
690 | ep = ss->in_ep; | ||
691 | usb_ep_disable(ep); | ||
692 | ep->driver_data = NULL; | ||
693 | return result; | ||
694 | } | ||
695 | |||
696 | /* one bulk endpoint reads (sinks) anything OUT (from the host) */ | ||
697 | ep = ss->out_ep; | ||
698 | result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); | ||
699 | if (result) | ||
700 | goto fail; | ||
701 | result = usb_ep_enable(ep); | ||
702 | if (result < 0) | ||
703 | goto fail; | ||
704 | ep->driver_data = ss; | ||
705 | |||
706 | result = source_sink_start_ep(ss, false, false, speed); | ||
707 | if (result < 0) { | ||
708 | fail2: | ||
709 | ep = ss->out_ep; | ||
710 | usb_ep_disable(ep); | ||
711 | ep->driver_data = NULL; | ||
712 | goto fail; | ||
713 | } | ||
714 | |||
715 | if (alt == 0) | ||
716 | goto out; | ||
717 | |||
718 | /* one iso endpoint writes (sources) zeroes IN (to the host) */ | ||
719 | ep = ss->iso_in_ep; | ||
720 | if (ep) { | ||
721 | result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); | ||
722 | if (result) | ||
723 | goto fail2; | ||
724 | result = usb_ep_enable(ep); | ||
725 | if (result < 0) | ||
726 | goto fail2; | ||
727 | ep->driver_data = ss; | ||
728 | |||
729 | result = source_sink_start_ep(ss, true, true, speed); | ||
730 | if (result < 0) { | ||
731 | fail3: | ||
732 | ep = ss->iso_in_ep; | ||
733 | if (ep) { | ||
734 | usb_ep_disable(ep); | ||
735 | ep->driver_data = NULL; | ||
736 | } | ||
737 | goto fail2; | ||
738 | } | ||
739 | } | ||
740 | |||
741 | /* one iso endpoint reads (sinks) anything OUT (from the host) */ | ||
742 | ep = ss->iso_out_ep; | ||
743 | if (ep) { | ||
744 | result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); | ||
745 | if (result) | ||
746 | goto fail3; | ||
747 | result = usb_ep_enable(ep); | ||
748 | if (result < 0) | ||
749 | goto fail3; | ||
750 | ep->driver_data = ss; | ||
751 | |||
752 | result = source_sink_start_ep(ss, false, true, speed); | ||
753 | if (result < 0) { | ||
754 | usb_ep_disable(ep); | ||
755 | ep->driver_data = NULL; | ||
756 | goto fail3; | ||
757 | } | ||
758 | } | ||
759 | out: | ||
760 | ss->cur_alt = alt; | ||
761 | |||
762 | DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt); | ||
763 | return result; | ||
764 | } | ||
765 | |||
766 | static int sourcesink_set_alt(struct usb_function *f, | ||
767 | unsigned intf, unsigned alt) | ||
768 | { | ||
769 | struct f_sourcesink *ss = func_to_ss(f); | ||
770 | struct usb_composite_dev *cdev = f->config->cdev; | ||
771 | |||
772 | if (ss->in_ep->driver_data) | ||
773 | disable_source_sink(ss); | ||
774 | return enable_source_sink(cdev, ss, alt); | ||
775 | } | ||
776 | |||
777 | static int sourcesink_get_alt(struct usb_function *f, unsigned intf) | ||
778 | { | ||
779 | struct f_sourcesink *ss = func_to_ss(f); | ||
780 | |||
781 | return ss->cur_alt; | ||
782 | } | ||
783 | |||
784 | static void sourcesink_disable(struct usb_function *f) | ||
785 | { | ||
786 | struct f_sourcesink *ss = func_to_ss(f); | ||
787 | |||
788 | disable_source_sink(ss); | ||
789 | } | ||
790 | |||
791 | /*-------------------------------------------------------------------------*/ | ||
792 | |||
793 | static int sourcesink_setup(struct usb_function *f, | ||
794 | const struct usb_ctrlrequest *ctrl) | ||
795 | { | ||
796 | struct usb_configuration *c = f->config; | ||
797 | struct usb_request *req = c->cdev->req; | ||
798 | int value = -EOPNOTSUPP; | ||
799 | u16 w_index = le16_to_cpu(ctrl->wIndex); | ||
800 | u16 w_value = le16_to_cpu(ctrl->wValue); | ||
801 | u16 w_length = le16_to_cpu(ctrl->wLength); | ||
802 | |||
803 | req->length = USB_COMP_EP0_BUFSIZ; | ||
804 | |||
805 | /* composite driver infrastructure handles everything except | ||
806 | * the two control test requests. | ||
807 | */ | ||
808 | switch (ctrl->bRequest) { | ||
809 | |||
810 | /* | ||
811 | * These are the same vendor-specific requests supported by | ||
812 | * Intel's USB 2.0 compliance test devices. We exceed that | ||
813 | * device spec by allowing multiple-packet requests. | ||
814 | * | ||
815 | * NOTE: the Control-OUT data stays in req->buf ... better | ||
816 | * would be copying it into a scratch buffer, so that other | ||
817 | * requests may safely intervene. | ||
818 | */ | ||
819 | case 0x5b: /* control WRITE test -- fill the buffer */ | ||
820 | if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) | ||
821 | goto unknown; | ||
822 | if (w_value || w_index) | ||
823 | break; | ||
824 | /* just read that many bytes into the buffer */ | ||
825 | if (w_length > req->length) | ||
826 | break; | ||
827 | value = w_length; | ||
828 | break; | ||
829 | case 0x5c: /* control READ test -- return the buffer */ | ||
830 | if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) | ||
831 | goto unknown; | ||
832 | if (w_value || w_index) | ||
833 | break; | ||
834 | /* expect those bytes are still in the buffer; send back */ | ||
835 | if (w_length > req->length) | ||
836 | break; | ||
837 | value = w_length; | ||
838 | break; | ||
839 | |||
840 | default: | ||
841 | unknown: | ||
842 | VDBG(c->cdev, | ||
843 | "unknown control req%02x.%02x v%04x i%04x l%d\n", | ||
844 | ctrl->bRequestType, ctrl->bRequest, | ||
845 | w_value, w_index, w_length); | ||
846 | } | ||
847 | |||
848 | /* respond with data transfer or status phase? */ | ||
849 | if (value >= 0) { | ||
850 | VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n", | ||
851 | ctrl->bRequestType, ctrl->bRequest, | ||
852 | w_value, w_index, w_length); | ||
853 | req->zero = 0; | ||
854 | req->length = value; | ||
855 | value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); | ||
856 | if (value < 0) | ||
857 | ERROR(c->cdev, "source/sink response, err %d\n", | ||
858 | value); | ||
859 | } | ||
860 | |||
861 | /* device either stalls (value < 0) or reports success */ | ||
862 | return value; | ||
863 | } | ||
864 | |||
865 | static struct usb_function *source_sink_alloc_func( | ||
866 | struct usb_function_instance *fi) | ||
867 | { | ||
868 | struct f_sourcesink *ss; | ||
869 | struct f_ss_opts *ss_opts; | ||
870 | |||
871 | ss = kzalloc(sizeof(*ss), GFP_KERNEL); | ||
872 | if (!ss) | ||
873 | return NULL; | ||
874 | |||
875 | ss_opts = container_of(fi, struct f_ss_opts, func_inst); | ||
876 | |||
877 | mutex_lock(&ss_opts->lock); | ||
878 | ss_opts->refcnt++; | ||
879 | mutex_unlock(&ss_opts->lock); | ||
880 | |||
881 | pattern = ss_opts->pattern; | ||
882 | isoc_interval = ss_opts->isoc_interval; | ||
883 | isoc_maxpacket = ss_opts->isoc_maxpacket; | ||
884 | isoc_mult = ss_opts->isoc_mult; | ||
885 | isoc_maxburst = ss_opts->isoc_maxburst; | ||
886 | buflen = ss_opts->bulk_buflen; | ||
887 | |||
888 | ss->function.name = "source/sink"; | ||
889 | ss->function.bind = sourcesink_bind; | ||
890 | ss->function.set_alt = sourcesink_set_alt; | ||
891 | ss->function.get_alt = sourcesink_get_alt; | ||
892 | ss->function.disable = sourcesink_disable; | ||
893 | ss->function.setup = sourcesink_setup; | ||
894 | ss->function.strings = sourcesink_strings; | ||
895 | |||
896 | ss->function.free_func = sourcesink_free_func; | ||
897 | |||
898 | return &ss->function; | ||
899 | } | ||
900 | |||
901 | static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item) | ||
902 | { | ||
903 | return container_of(to_config_group(item), struct f_ss_opts, | ||
904 | func_inst.group); | ||
905 | } | ||
906 | |||
907 | CONFIGFS_ATTR_STRUCT(f_ss_opts); | ||
908 | CONFIGFS_ATTR_OPS(f_ss_opts); | ||
909 | |||
910 | static void ss_attr_release(struct config_item *item) | ||
911 | { | ||
912 | struct f_ss_opts *ss_opts = to_f_ss_opts(item); | ||
913 | |||
914 | usb_put_function_instance(&ss_opts->func_inst); | ||
915 | } | ||
916 | |||
917 | static struct configfs_item_operations ss_item_ops = { | ||
918 | .release = ss_attr_release, | ||
919 | .show_attribute = f_ss_opts_attr_show, | ||
920 | .store_attribute = f_ss_opts_attr_store, | ||
921 | }; | ||
922 | |||
923 | static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page) | ||
924 | { | ||
925 | int result; | ||
926 | |||
927 | mutex_lock(&opts->lock); | ||
928 | result = sprintf(page, "%d", opts->pattern); | ||
929 | mutex_unlock(&opts->lock); | ||
930 | |||
931 | return result; | ||
932 | } | ||
933 | |||
934 | static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts, | ||
935 | const char *page, size_t len) | ||
936 | { | ||
937 | int ret; | ||
938 | u8 num; | ||
939 | |||
940 | mutex_lock(&opts->lock); | ||
941 | if (opts->refcnt) { | ||
942 | ret = -EBUSY; | ||
943 | goto end; | ||
944 | } | ||
945 | |||
946 | ret = kstrtou8(page, 0, &num); | ||
947 | if (ret) | ||
948 | goto end; | ||
949 | |||
950 | if (num != 0 && num != 1 && num != 2) { | ||
951 | ret = -EINVAL; | ||
952 | goto end; | ||
953 | } | ||
954 | |||
955 | opts->pattern = num; | ||
956 | ret = len; | ||
957 | end: | ||
958 | mutex_unlock(&opts->lock); | ||
959 | return ret; | ||
960 | } | ||
961 | |||
962 | static struct f_ss_opts_attribute f_ss_opts_pattern = | ||
963 | __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR, | ||
964 | f_ss_opts_pattern_show, | ||
965 | f_ss_opts_pattern_store); | ||
966 | |||
967 | static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page) | ||
968 | { | ||
969 | int result; | ||
970 | |||
971 | mutex_lock(&opts->lock); | ||
972 | result = sprintf(page, "%d", opts->isoc_interval); | ||
973 | mutex_unlock(&opts->lock); | ||
974 | |||
975 | return result; | ||
976 | } | ||
977 | |||
978 | static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts, | ||
979 | const char *page, size_t len) | ||
980 | { | ||
981 | int ret; | ||
982 | u8 num; | ||
983 | |||
984 | mutex_lock(&opts->lock); | ||
985 | if (opts->refcnt) { | ||
986 | ret = -EBUSY; | ||
987 | goto end; | ||
988 | } | ||
989 | |||
990 | ret = kstrtou8(page, 0, &num); | ||
991 | if (ret) | ||
992 | goto end; | ||
993 | |||
994 | if (num > 16) { | ||
995 | ret = -EINVAL; | ||
996 | goto end; | ||
997 | } | ||
998 | |||
999 | opts->isoc_interval = num; | ||
1000 | ret = len; | ||
1001 | end: | ||
1002 | mutex_unlock(&opts->lock); | ||
1003 | return ret; | ||
1004 | } | ||
1005 | |||
1006 | static struct f_ss_opts_attribute f_ss_opts_isoc_interval = | ||
1007 | __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR, | ||
1008 | f_ss_opts_isoc_interval_show, | ||
1009 | f_ss_opts_isoc_interval_store); | ||
1010 | |||
1011 | static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page) | ||
1012 | { | ||
1013 | int result; | ||
1014 | |||
1015 | mutex_lock(&opts->lock); | ||
1016 | result = sprintf(page, "%d", opts->isoc_maxpacket); | ||
1017 | mutex_unlock(&opts->lock); | ||
1018 | |||
1019 | return result; | ||
1020 | } | ||
1021 | |||
1022 | static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts, | ||
1023 | const char *page, size_t len) | ||
1024 | { | ||
1025 | int ret; | ||
1026 | u16 num; | ||
1027 | |||
1028 | mutex_lock(&opts->lock); | ||
1029 | if (opts->refcnt) { | ||
1030 | ret = -EBUSY; | ||
1031 | goto end; | ||
1032 | } | ||
1033 | |||
1034 | ret = kstrtou16(page, 0, &num); | ||
1035 | if (ret) | ||
1036 | goto end; | ||
1037 | |||
1038 | if (num > 1024) { | ||
1039 | ret = -EINVAL; | ||
1040 | goto end; | ||
1041 | } | ||
1042 | |||
1043 | opts->isoc_maxpacket = num; | ||
1044 | ret = len; | ||
1045 | end: | ||
1046 | mutex_unlock(&opts->lock); | ||
1047 | return ret; | ||
1048 | } | ||
1049 | |||
1050 | static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket = | ||
1051 | __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR, | ||
1052 | f_ss_opts_isoc_maxpacket_show, | ||
1053 | f_ss_opts_isoc_maxpacket_store); | ||
1054 | |||
1055 | static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page) | ||
1056 | { | ||
1057 | int result; | ||
1058 | |||
1059 | mutex_lock(&opts->lock); | ||
1060 | result = sprintf(page, "%d", opts->isoc_mult); | ||
1061 | mutex_unlock(&opts->lock); | ||
1062 | |||
1063 | return result; | ||
1064 | } | ||
1065 | |||
1066 | static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts, | ||
1067 | const char *page, size_t len) | ||
1068 | { | ||
1069 | int ret; | ||
1070 | u8 num; | ||
1071 | |||
1072 | mutex_lock(&opts->lock); | ||
1073 | if (opts->refcnt) { | ||
1074 | ret = -EBUSY; | ||
1075 | goto end; | ||
1076 | } | ||
1077 | |||
1078 | ret = kstrtou8(page, 0, &num); | ||
1079 | if (ret) | ||
1080 | goto end; | ||
1081 | |||
1082 | if (num > 2) { | ||
1083 | ret = -EINVAL; | ||
1084 | goto end; | ||
1085 | } | ||
1086 | |||
1087 | opts->isoc_mult = num; | ||
1088 | ret = len; | ||
1089 | end: | ||
1090 | mutex_unlock(&opts->lock); | ||
1091 | return ret; | ||
1092 | } | ||
1093 | |||
1094 | static struct f_ss_opts_attribute f_ss_opts_isoc_mult = | ||
1095 | __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR, | ||
1096 | f_ss_opts_isoc_mult_show, | ||
1097 | f_ss_opts_isoc_mult_store); | ||
1098 | |||
1099 | static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page) | ||
1100 | { | ||
1101 | int result; | ||
1102 | |||
1103 | mutex_lock(&opts->lock); | ||
1104 | result = sprintf(page, "%d", opts->isoc_maxburst); | ||
1105 | mutex_unlock(&opts->lock); | ||
1106 | |||
1107 | return result; | ||
1108 | } | ||
1109 | |||
1110 | static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts, | ||
1111 | const char *page, size_t len) | ||
1112 | { | ||
1113 | int ret; | ||
1114 | u8 num; | ||
1115 | |||
1116 | mutex_lock(&opts->lock); | ||
1117 | if (opts->refcnt) { | ||
1118 | ret = -EBUSY; | ||
1119 | goto end; | ||
1120 | } | ||
1121 | |||
1122 | ret = kstrtou8(page, 0, &num); | ||
1123 | if (ret) | ||
1124 | goto end; | ||
1125 | |||
1126 | if (num > 15) { | ||
1127 | ret = -EINVAL; | ||
1128 | goto end; | ||
1129 | } | ||
1130 | |||
1131 | opts->isoc_maxburst = num; | ||
1132 | ret = len; | ||
1133 | end: | ||
1134 | mutex_unlock(&opts->lock); | ||
1135 | return ret; | ||
1136 | } | ||
1137 | |||
1138 | static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst = | ||
1139 | __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR, | ||
1140 | f_ss_opts_isoc_maxburst_show, | ||
1141 | f_ss_opts_isoc_maxburst_store); | ||
1142 | |||
1143 | static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page) | ||
1144 | { | ||
1145 | int result; | ||
1146 | |||
1147 | mutex_lock(&opts->lock); | ||
1148 | result = sprintf(page, "%d", opts->bulk_buflen); | ||
1149 | mutex_unlock(&opts->lock); | ||
1150 | |||
1151 | return result; | ||
1152 | } | ||
1153 | |||
1154 | static ssize_t f_ss_opts_bulk_buflen_store(struct f_ss_opts *opts, | ||
1155 | const char *page, size_t len) | ||
1156 | { | ||
1157 | int ret; | ||
1158 | u32 num; | ||
1159 | |||
1160 | mutex_lock(&opts->lock); | ||
1161 | if (opts->refcnt) { | ||
1162 | ret = -EBUSY; | ||
1163 | goto end; | ||
1164 | } | ||
1165 | |||
1166 | ret = kstrtou32(page, 0, &num); | ||
1167 | if (ret) | ||
1168 | goto end; | ||
1169 | |||
1170 | opts->bulk_buflen = num; | ||
1171 | ret = len; | ||
1172 | end: | ||
1173 | mutex_unlock(&opts->lock); | ||
1174 | return ret; | ||
1175 | } | ||
1176 | |||
1177 | static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = | ||
1178 | __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, | ||
1179 | f_ss_opts_bulk_buflen_show, | ||
1180 | f_ss_opts_bulk_buflen_store); | ||
1181 | |||
1182 | static struct configfs_attribute *ss_attrs[] = { | ||
1183 | &f_ss_opts_pattern.attr, | ||
1184 | &f_ss_opts_isoc_interval.attr, | ||
1185 | &f_ss_opts_isoc_maxpacket.attr, | ||
1186 | &f_ss_opts_isoc_mult.attr, | ||
1187 | &f_ss_opts_isoc_maxburst.attr, | ||
1188 | &f_ss_opts_bulk_buflen.attr, | ||
1189 | NULL, | ||
1190 | }; | ||
1191 | |||
1192 | static struct config_item_type ss_func_type = { | ||
1193 | .ct_item_ops = &ss_item_ops, | ||
1194 | .ct_attrs = ss_attrs, | ||
1195 | .ct_owner = THIS_MODULE, | ||
1196 | }; | ||
1197 | |||
1198 | static void source_sink_free_instance(struct usb_function_instance *fi) | ||
1199 | { | ||
1200 | struct f_ss_opts *ss_opts; | ||
1201 | |||
1202 | ss_opts = container_of(fi, struct f_ss_opts, func_inst); | ||
1203 | kfree(ss_opts); | ||
1204 | } | ||
1205 | |||
1206 | static struct usb_function_instance *source_sink_alloc_inst(void) | ||
1207 | { | ||
1208 | struct f_ss_opts *ss_opts; | ||
1209 | |||
1210 | ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL); | ||
1211 | if (!ss_opts) | ||
1212 | return ERR_PTR(-ENOMEM); | ||
1213 | mutex_init(&ss_opts->lock); | ||
1214 | ss_opts->func_inst.free_func_inst = source_sink_free_instance; | ||
1215 | ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; | ||
1216 | ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; | ||
1217 | ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; | ||
1218 | |||
1219 | config_group_init_type_name(&ss_opts->func_inst.group, "", | ||
1220 | &ss_func_type); | ||
1221 | |||
1222 | return &ss_opts->func_inst; | ||
1223 | } | ||
1224 | DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst, | ||
1225 | source_sink_alloc_func); | ||
1226 | |||
1227 | static int __init sslb_modinit(void) | ||
1228 | { | ||
1229 | int ret; | ||
1230 | |||
1231 | ret = usb_function_register(&SourceSinkusb_func); | ||
1232 | if (ret) | ||
1233 | return ret; | ||
1234 | ret = lb_modinit(); | ||
1235 | if (ret) | ||
1236 | usb_function_unregister(&SourceSinkusb_func); | ||
1237 | return ret; | ||
1238 | } | ||
1239 | static void __exit sslb_modexit(void) | ||
1240 | { | ||
1241 | usb_function_unregister(&SourceSinkusb_func); | ||
1242 | lb_modexit(); | ||
1243 | } | ||
1244 | module_init(sslb_modinit); | ||
1245 | module_exit(sslb_modexit); | ||
1246 | |||
1247 | MODULE_LICENSE("GPL"); | ||