Commit | Line | Data |
---|---|---|
5ab54cf7 MN |
1 | /* |
2 | * g_ffs.c -- user mode file system API for USB composite function controllers | |
3 | * | |
4 | * Copyright (C) 2010 Samsung Electronics | |
54b8360f | 5 | * Author: Michal Nazarewicz <mina86@mina86.com> |
5ab54cf7 MN |
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. | |
5ab54cf7 MN |
11 | */ |
12 | ||
aa02f172 MN |
13 | #define pr_fmt(fmt) "g_ffs: " fmt |
14 | ||
c6c56008 | 15 | #include <linux/module.h> |
c6c56008 MN |
16 | /* |
17 | * kbuild is not very cooperative with respect to linking separately | |
18 | * compiled library objects into one module. So for now we won't use | |
19 | * separate compilation ... ensuring init/exit sections work to shrink | |
20 | * the runtime footprint, and giving us at least some parts of what | |
21 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | |
22 | */ | |
c6c56008 MN |
23 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS |
24 | # if defined USB_ETH_RNDIS | |
25 | # undef USB_ETH_RNDIS | |
26 | # endif | |
27 | # ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
28 | # define USB_ETH_RNDIS y | |
29 | # endif | |
30 | ||
fee562a6 | 31 | #define USBF_ECM_INCLUDED |
c6c56008 | 32 | # include "f_ecm.c" |
8cedba7c | 33 | #define USB_FSUBSET_INCLUDED |
c6c56008 MN |
34 | # include "f_subset.c" |
35 | # ifdef USB_ETH_RNDIS | |
f466c635 | 36 | # define USB_FRNDIS_INCLUDED |
c6c56008 | 37 | # include "f_rndis.c" |
cbbd14a9 | 38 | # include "rndis.h" |
c6c56008 | 39 | # endif |
f1a1823f | 40 | # include "u_ether.h" |
c6c56008 | 41 | |
f1a1823f | 42 | static u8 gfs_host_mac[ETH_ALEN]; |
d6a01439 | 43 | static struct eth_dev *the_dev; |
f8dae531 | 44 | # ifdef CONFIG_USB_FUNCTIONFS_ETH |
d6a01439 SAS |
45 | static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], |
46 | struct eth_dev *dev); | |
c6c56008 | 47 | # endif |
f8dae531 | 48 | #else |
d6a01439 SAS |
49 | # define the_dev NULL |
50 | # define gether_cleanup(dev) do { } while (0) | |
f1a1823f | 51 | # define gfs_host_mac NULL |
d6a01439 | 52 | struct eth_dev; |
c6c56008 MN |
53 | #endif |
54 | ||
55 | #include "f_fs.c" | |
56 | ||
c6c56008 MN |
57 | #define DRIVER_NAME "g_ffs" |
58 | #define DRIVER_DESC "USB Function Filesystem" | |
59 | #define DRIVER_VERSION "24 Aug 2004" | |
60 | ||
61 | MODULE_DESCRIPTION(DRIVER_DESC); | |
62 | MODULE_AUTHOR("Michal Nazarewicz"); | |
63 | MODULE_LICENSE("GPL"); | |
64 | ||
fc19de61 MN |
65 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ |
66 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ | |
c6c56008 | 67 | |
581791f5 AP |
68 | #define GFS_MAX_DEVS 10 |
69 | ||
70 | struct gfs_ffs_obj { | |
71 | const char *name; | |
72 | bool mounted; | |
73 | bool desc_ready; | |
74 | struct ffs_data *ffs_data; | |
75 | }; | |
76 | ||
7d16e8d3 SAS |
77 | USB_GADGET_COMPOSITE_OPTIONS(); |
78 | ||
e81b1a6e | 79 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS |
f1a1823f | 80 | USB_ETHERNET_MODULE_PARAMETERS(); |
e81b1a6e | 81 | #endif |
f1a1823f | 82 | |
c6c56008 MN |
83 | static struct usb_device_descriptor gfs_dev_desc = { |
84 | .bLength = sizeof gfs_dev_desc, | |
85 | .bDescriptorType = USB_DT_DEVICE, | |
86 | ||
87 | .bcdUSB = cpu_to_le16(0x0200), | |
88 | .bDeviceClass = USB_CLASS_PER_INTERFACE, | |
89 | ||
fc19de61 MN |
90 | .idVendor = cpu_to_le16(GFS_VENDOR_ID), |
91 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), | |
c6c56008 MN |
92 | }; |
93 | ||
581791f5 AP |
94 | static char *func_names[GFS_MAX_DEVS]; |
95 | static unsigned int func_num; | |
96 | ||
fc19de61 MN |
97 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); |
98 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); | |
99 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); | |
100 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); | |
101 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); | |
102 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); | |
581791f5 AP |
103 | module_param_array_named(functions, func_names, charp, &func_num, 0); |
104 | MODULE_PARM_DESC(functions, "USB Functions list"); | |
c6c56008 | 105 | |
c6c56008 MN |
106 | static const struct usb_descriptor_header *gfs_otg_desc[] = { |
107 | (const struct usb_descriptor_header *) | |
108 | &(const struct usb_otg_descriptor) { | |
109 | .bLength = sizeof(struct usb_otg_descriptor), | |
110 | .bDescriptorType = USB_DT_OTG, | |
111 | ||
fc19de61 MN |
112 | /* |
113 | * REVISIT SRP-only hardware is possible, although | |
114 | * it would not be called "OTG" ... | |
115 | */ | |
c6c56008 MN |
116 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, |
117 | }, | |
118 | ||
119 | NULL | |
120 | }; | |
121 | ||
5ab54cf7 | 122 | /* String IDs are assigned dynamically */ |
c6c56008 | 123 | static struct usb_string gfs_strings[] = { |
276e2e4f | 124 | [USB_GADGET_MANUFACTURER_IDX].s = "", |
d33f74fc | 125 | [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, |
276e2e4f | 126 | [USB_GADGET_SERIAL_IDX].s = "", |
c6c56008 | 127 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
f8dae531 | 128 | { .s = "FunctionFS + RNDIS" }, |
c6c56008 MN |
129 | #endif |
130 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | |
f8dae531 | 131 | { .s = "FunctionFS + ECM" }, |
c6c56008 MN |
132 | #endif |
133 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | |
f8dae531 | 134 | { .s = "FunctionFS" }, |
c6c56008 MN |
135 | #endif |
136 | { } /* end of list */ | |
137 | }; | |
138 | ||
139 | static struct usb_gadget_strings *gfs_dev_strings[] = { | |
140 | &(struct usb_gadget_strings) { | |
141 | .language = 0x0409, /* en-us */ | |
142 | .strings = gfs_strings, | |
143 | }, | |
144 | NULL, | |
145 | }; | |
146 | ||
f8dae531 MN |
147 | struct gfs_configuration { |
148 | struct usb_configuration c; | |
d6a01439 SAS |
149 | int (*eth)(struct usb_configuration *c, u8 *ethaddr, |
150 | struct eth_dev *dev); | |
f8dae531 | 151 | } gfs_configurations[] = { |
c6c56008 | 152 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
f8dae531 MN |
153 | { |
154 | .eth = rndis_bind_config, | |
155 | }, | |
c6c56008 MN |
156 | #endif |
157 | ||
c6c56008 | 158 | #ifdef CONFIG_USB_FUNCTIONFS_ETH |
f8dae531 MN |
159 | { |
160 | .eth = eth_bind_config, | |
161 | }, | |
c6c56008 MN |
162 | #endif |
163 | ||
c6c56008 | 164 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC |
f8dae531 MN |
165 | { |
166 | }, | |
c6c56008 | 167 | #endif |
f8dae531 | 168 | }; |
c6c56008 | 169 | |
c6c56008 MN |
170 | static int gfs_bind(struct usb_composite_dev *cdev); |
171 | static int gfs_unbind(struct usb_composite_dev *cdev); | |
f8dae531 | 172 | static int gfs_do_config(struct usb_configuration *c); |
c6c56008 | 173 | |
c2ec75c2 | 174 | static __refdata struct usb_composite_driver gfs_driver = { |
fc19de61 | 175 | .name = DRIVER_NAME, |
c6c56008 MN |
176 | .dev = &gfs_dev_desc, |
177 | .strings = gfs_dev_strings, | |
35a0e0bf | 178 | .max_speed = USB_SPEED_HIGH, |
03e42bd5 | 179 | .bind = gfs_bind, |
c6c56008 MN |
180 | .unbind = gfs_unbind, |
181 | }; | |
182 | ||
581791f5 AP |
183 | static DEFINE_MUTEX(gfs_lock); |
184 | static unsigned int missing_funcs; | |
185 | static bool gfs_ether_setup; | |
186 | static bool gfs_registered; | |
187 | static bool gfs_single_func; | |
188 | static struct gfs_ffs_obj *ffs_tab; | |
c6c56008 | 189 | |
8545e603 | 190 | static int __init gfs_init(void) |
c6c56008 | 191 | { |
581791f5 AP |
192 | int i; |
193 | ||
c6c56008 MN |
194 | ENTER(); |
195 | ||
581791f5 AP |
196 | if (!func_num) { |
197 | gfs_single_func = true; | |
198 | func_num = 1; | |
199 | } | |
200 | ||
201 | ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL); | |
202 | if (!ffs_tab) | |
203 | return -ENOMEM; | |
204 | ||
205 | if (!gfs_single_func) | |
206 | for (i = 0; i < func_num; i++) | |
207 | ffs_tab[i].name = func_names[i]; | |
208 | ||
209 | missing_funcs = func_num; | |
210 | ||
c6c56008 MN |
211 | return functionfs_init(); |
212 | } | |
213 | module_init(gfs_init); | |
214 | ||
8545e603 | 215 | static void __exit gfs_exit(void) |
c6c56008 MN |
216 | { |
217 | ENTER(); | |
581791f5 | 218 | mutex_lock(&gfs_lock); |
c6c56008 | 219 | |
581791f5 | 220 | if (gfs_registered) |
c6c56008 | 221 | usb_composite_unregister(&gfs_driver); |
581791f5 | 222 | gfs_registered = false; |
c6c56008 MN |
223 | |
224 | functionfs_cleanup(); | |
581791f5 AP |
225 | |
226 | mutex_unlock(&gfs_lock); | |
227 | kfree(ffs_tab); | |
c6c56008 MN |
228 | } |
229 | module_exit(gfs_exit); | |
230 | ||
581791f5 AP |
231 | static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) |
232 | { | |
233 | int i; | |
234 | ||
235 | ENTER(); | |
236 | ||
237 | if (gfs_single_func) | |
238 | return &ffs_tab[0]; | |
239 | ||
240 | for (i = 0; i < func_num; i++) | |
241 | if (strcmp(ffs_tab[i].name, dev_name) == 0) | |
242 | return &ffs_tab[i]; | |
243 | ||
244 | return NULL; | |
245 | } | |
246 | ||
c6c56008 MN |
247 | static int functionfs_ready_callback(struct ffs_data *ffs) |
248 | { | |
581791f5 | 249 | struct gfs_ffs_obj *ffs_obj; |
c6c56008 MN |
250 | int ret; |
251 | ||
252 | ENTER(); | |
581791f5 AP |
253 | mutex_lock(&gfs_lock); |
254 | ||
255 | ffs_obj = ffs->private_data; | |
256 | if (!ffs_obj) { | |
257 | ret = -EINVAL; | |
258 | goto done; | |
259 | } | |
260 | ||
261 | if (WARN_ON(ffs_obj->desc_ready)) { | |
262 | ret = -EBUSY; | |
263 | goto done; | |
264 | } | |
265 | ffs_obj->desc_ready = true; | |
266 | ffs_obj->ffs_data = ffs; | |
c6c56008 | 267 | |
581791f5 AP |
268 | if (--missing_funcs) { |
269 | ret = 0; | |
270 | goto done; | |
271 | } | |
272 | ||
273 | if (gfs_registered) { | |
274 | ret = -EBUSY; | |
275 | goto done; | |
276 | } | |
277 | gfs_registered = true; | |
c6c56008 | 278 | |
03e42bd5 | 279 | ret = usb_composite_probe(&gfs_driver); |
c6c56008 | 280 | if (unlikely(ret < 0)) |
581791f5 AP |
281 | gfs_registered = false; |
282 | ||
283 | done: | |
284 | mutex_unlock(&gfs_lock); | |
c6c56008 MN |
285 | return ret; |
286 | } | |
287 | ||
288 | static void functionfs_closed_callback(struct ffs_data *ffs) | |
289 | { | |
581791f5 AP |
290 | struct gfs_ffs_obj *ffs_obj; |
291 | ||
c6c56008 | 292 | ENTER(); |
581791f5 | 293 | mutex_lock(&gfs_lock); |
c6c56008 | 294 | |
581791f5 AP |
295 | ffs_obj = ffs->private_data; |
296 | if (!ffs_obj) | |
297 | goto done; | |
298 | ||
299 | ffs_obj->desc_ready = false; | |
300 | missing_funcs++; | |
301 | ||
302 | if (gfs_registered) | |
c6c56008 | 303 | usb_composite_unregister(&gfs_driver); |
581791f5 AP |
304 | gfs_registered = false; |
305 | ||
306 | done: | |
307 | mutex_unlock(&gfs_lock); | |
c6c56008 MN |
308 | } |
309 | ||
581791f5 | 310 | static void *functionfs_acquire_dev_callback(const char *dev_name) |
c6c56008 | 311 | { |
581791f5 AP |
312 | struct gfs_ffs_obj *ffs_dev; |
313 | ||
314 | ENTER(); | |
315 | mutex_lock(&gfs_lock); | |
316 | ||
317 | ffs_dev = gfs_find_dev(dev_name); | |
318 | if (!ffs_dev) { | |
319 | ffs_dev = ERR_PTR(-ENODEV); | |
320 | goto done; | |
321 | } | |
322 | ||
323 | if (ffs_dev->mounted) { | |
324 | ffs_dev = ERR_PTR(-EBUSY); | |
325 | goto done; | |
326 | } | |
327 | ffs_dev->mounted = true; | |
328 | ||
329 | done: | |
330 | mutex_unlock(&gfs_lock); | |
331 | return ffs_dev; | |
c6c56008 MN |
332 | } |
333 | ||
581791f5 AP |
334 | static void functionfs_release_dev_callback(struct ffs_data *ffs_data) |
335 | { | |
336 | struct gfs_ffs_obj *ffs_dev; | |
337 | ||
338 | ENTER(); | |
339 | mutex_lock(&gfs_lock); | |
340 | ||
341 | ffs_dev = ffs_data->private_data; | |
342 | if (ffs_dev) | |
343 | ffs_dev->mounted = false; | |
344 | ||
345 | mutex_unlock(&gfs_lock); | |
346 | } | |
347 | ||
348 | /* | |
349 | * It is assumed that gfs_bind is called from a context where gfs_lock is held | |
350 | */ | |
c6c56008 MN |
351 | static int gfs_bind(struct usb_composite_dev *cdev) |
352 | { | |
f8dae531 | 353 | int ret, i; |
c6c56008 MN |
354 | |
355 | ENTER(); | |
356 | ||
581791f5 | 357 | if (missing_funcs) |
c6c56008 | 358 | return -ENODEV; |
d6a01439 | 359 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS |
f1a1823f AP |
360 | the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac, |
361 | qmult); | |
d6a01439 SAS |
362 | #endif |
363 | if (IS_ERR(the_dev)) { | |
364 | ret = PTR_ERR(the_dev); | |
c6c56008 | 365 | goto error_quick; |
d6a01439 | 366 | } |
581791f5 | 367 | gfs_ether_setup = true; |
c6c56008 | 368 | |
f8dae531 | 369 | ret = usb_string_ids_tab(cdev, gfs_strings); |
c6c56008 MN |
370 | if (unlikely(ret < 0)) |
371 | goto error; | |
d33f74fc | 372 | gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; |
c6c56008 | 373 | |
3416905b | 374 | for (i = func_num; i--; ) { |
581791f5 AP |
375 | ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); |
376 | if (unlikely(ret < 0)) { | |
377 | while (++i < func_num) | |
378 | functionfs_unbind(ffs_tab[i].ffs_data); | |
379 | goto error; | |
380 | } | |
381 | } | |
c6c56008 | 382 | |
f8dae531 MN |
383 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { |
384 | struct gfs_configuration *c = gfs_configurations + i; | |
276e2e4f | 385 | int sid = USB_GADGET_FIRST_AVAIL_IDX + i; |
c6c56008 | 386 | |
276e2e4f SAS |
387 | c->c.label = gfs_strings[sid].s; |
388 | c->c.iConfiguration = gfs_strings[sid].id; | |
f8dae531 MN |
389 | c->c.bConfigurationValue = 1 + i; |
390 | c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; | |
c6c56008 | 391 | |
c9bfff9c | 392 | ret = usb_add_config(cdev, &c->c, gfs_do_config); |
f8dae531 MN |
393 | if (unlikely(ret < 0)) |
394 | goto error_unbind; | |
395 | } | |
7d16e8d3 | 396 | usb_composite_overwrite_options(cdev, &coverwrite); |
c6c56008 MN |
397 | return 0; |
398 | ||
399 | error_unbind: | |
581791f5 AP |
400 | for (i = 0; i < func_num; i++) |
401 | functionfs_unbind(ffs_tab[i].ffs_data); | |
c6c56008 | 402 | error: |
d6a01439 | 403 | gether_cleanup(the_dev); |
c6c56008 | 404 | error_quick: |
581791f5 | 405 | gfs_ether_setup = false; |
c6c56008 MN |
406 | return ret; |
407 | } | |
408 | ||
581791f5 AP |
409 | /* |
410 | * It is assumed that gfs_unbind is called from a context where gfs_lock is held | |
411 | */ | |
c6c56008 MN |
412 | static int gfs_unbind(struct usb_composite_dev *cdev) |
413 | { | |
581791f5 AP |
414 | int i; |
415 | ||
c6c56008 MN |
416 | ENTER(); |
417 | ||
fc19de61 MN |
418 | /* |
419 | * We may have been called in an error recovery from | |
c6c56008 MN |
420 | * composite_bind() after gfs_unbind() failure so we need to |
421 | * check if gfs_ffs_data is not NULL since gfs_bind() handles | |
422 | * all error recovery itself. I'd rather we werent called | |
423 | * from composite on orror recovery, but what you're gonna | |
fc19de61 MN |
424 | * do...? |
425 | */ | |
581791f5 | 426 | if (gfs_ether_setup) |
d6a01439 | 427 | gether_cleanup(the_dev); |
581791f5 AP |
428 | gfs_ether_setup = false; |
429 | ||
3416905b | 430 | for (i = func_num; i--; ) |
581791f5 AP |
431 | if (ffs_tab[i].ffs_data) |
432 | functionfs_unbind(ffs_tab[i].ffs_data); | |
c6c56008 MN |
433 | |
434 | return 0; | |
435 | } | |
436 | ||
581791f5 AP |
437 | /* |
438 | * It is assumed that gfs_do_config is called from a context where | |
439 | * gfs_lock is held | |
440 | */ | |
f8dae531 | 441 | static int gfs_do_config(struct usb_configuration *c) |
c6c56008 | 442 | { |
f8dae531 MN |
443 | struct gfs_configuration *gc = |
444 | container_of(c, struct gfs_configuration, c); | |
581791f5 | 445 | int i; |
c6c56008 MN |
446 | int ret; |
447 | ||
581791f5 | 448 | if (missing_funcs) |
c6c56008 MN |
449 | return -ENODEV; |
450 | ||
451 | if (gadget_is_otg(c->cdev->gadget)) { | |
452 | c->descriptors = gfs_otg_desc; | |
453 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | |
454 | } | |
455 | ||
f8dae531 | 456 | if (gc->eth) { |
f1a1823f | 457 | ret = gc->eth(c, gfs_host_mac, the_dev); |
c6c56008 MN |
458 | if (unlikely(ret < 0)) |
459 | return ret; | |
460 | } | |
461 | ||
581791f5 AP |
462 | for (i = 0; i < func_num; i++) { |
463 | ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); | |
464 | if (unlikely(ret < 0)) | |
465 | return ret; | |
466 | } | |
c6c56008 | 467 | |
fc19de61 MN |
468 | /* |
469 | * After previous do_configs there may be some invalid | |
f588c0db MN |
470 | * pointers in c->interface array. This happens every time |
471 | * a user space function with fewer interfaces than a user | |
472 | * space function that was run before the new one is run. The | |
473 | * compasit's set_config() assumes that if there is no more | |
474 | * then MAX_CONFIG_INTERFACES interfaces in a configuration | |
475 | * then there is a NULL pointer after the last interface in | |
fc19de61 MN |
476 | * c->interface array. We need to make sure this is true. |
477 | */ | |
f588c0db MN |
478 | if (c->next_interface_id < ARRAY_SIZE(c->interface)) |
479 | c->interface[c->next_interface_id] = NULL; | |
480 | ||
c6c56008 MN |
481 | return 0; |
482 | } | |
483 | ||
c6c56008 | 484 | #ifdef CONFIG_USB_FUNCTIONFS_ETH |
fc19de61 | 485 | |
d6a01439 SAS |
486 | static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], |
487 | struct eth_dev *dev) | |
c6c56008 | 488 | { |
f8dae531 | 489 | return can_support_ecm(c->cdev->gadget) |
d6a01439 SAS |
490 | ? ecm_bind_config(c, ethaddr, dev) |
491 | : geth_bind_config(c, ethaddr, dev); | |
c6c56008 | 492 | } |
fc19de61 | 493 | |
c6c56008 | 494 | #endif |