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 | 23 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS |
85aec59f AP |
24 | #include <linux/netdevice.h> |
25 | ||
c6c56008 MN |
26 | # if defined USB_ETH_RNDIS |
27 | # undef USB_ETH_RNDIS | |
28 | # endif | |
29 | # ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
30 | # define USB_ETH_RNDIS y | |
31 | # endif | |
32 | ||
f212ad4e | 33 | # include "u_ecm.h" |
85aec59f | 34 | # include "u_gether.h" |
c6c56008 | 35 | # ifdef USB_ETH_RNDIS |
6e257b14 | 36 | # include "u_rndis.h" |
cbbd14a9 | 37 | # include "rndis.h" |
c6c56008 | 38 | # endif |
f1a1823f | 39 | # include "u_ether.h" |
c6c56008 | 40 | |
f212ad4e AP |
41 | USB_ETHERNET_MODULE_PARAMETERS(); |
42 | ||
f8dae531 | 43 | # ifdef CONFIG_USB_FUNCTIONFS_ETH |
6e257b14 | 44 | static int eth_bind_config(struct usb_configuration *c); |
f212ad4e AP |
45 | static struct usb_function_instance *fi_ecm; |
46 | static struct usb_function *f_ecm; | |
85aec59f AP |
47 | static struct usb_function_instance *fi_geth; |
48 | static struct usb_function *f_geth; | |
c6c56008 | 49 | # endif |
6e257b14 AP |
50 | # ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
51 | static int bind_rndis_config(struct usb_configuration *c); | |
52 | static struct usb_function_instance *fi_rndis; | |
53 | static struct usb_function *f_rndis; | |
54 | # endif | |
c6c56008 MN |
55 | #endif |
56 | ||
57 | #include "f_fs.c" | |
58 | ||
c6c56008 MN |
59 | #define DRIVER_NAME "g_ffs" |
60 | #define DRIVER_DESC "USB Function Filesystem" | |
61 | #define DRIVER_VERSION "24 Aug 2004" | |
62 | ||
63 | MODULE_DESCRIPTION(DRIVER_DESC); | |
64 | MODULE_AUTHOR("Michal Nazarewicz"); | |
65 | MODULE_LICENSE("GPL"); | |
66 | ||
fc19de61 MN |
67 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ |
68 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ | |
c6c56008 | 69 | |
581791f5 AP |
70 | #define GFS_MAX_DEVS 10 |
71 | ||
7d16e8d3 SAS |
72 | USB_GADGET_COMPOSITE_OPTIONS(); |
73 | ||
c6c56008 MN |
74 | static struct usb_device_descriptor gfs_dev_desc = { |
75 | .bLength = sizeof gfs_dev_desc, | |
76 | .bDescriptorType = USB_DT_DEVICE, | |
77 | ||
78 | .bcdUSB = cpu_to_le16(0x0200), | |
79 | .bDeviceClass = USB_CLASS_PER_INTERFACE, | |
80 | ||
fc19de61 MN |
81 | .idVendor = cpu_to_le16(GFS_VENDOR_ID), |
82 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), | |
c6c56008 MN |
83 | }; |
84 | ||
581791f5 AP |
85 | static char *func_names[GFS_MAX_DEVS]; |
86 | static unsigned int func_num; | |
87 | ||
fc19de61 MN |
88 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); |
89 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); | |
90 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); | |
91 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); | |
92 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); | |
93 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); | |
581791f5 AP |
94 | module_param_array_named(functions, func_names, charp, &func_num, 0); |
95 | MODULE_PARM_DESC(functions, "USB Functions list"); | |
c6c56008 | 96 | |
c6c56008 MN |
97 | static const struct usb_descriptor_header *gfs_otg_desc[] = { |
98 | (const struct usb_descriptor_header *) | |
99 | &(const struct usb_otg_descriptor) { | |
100 | .bLength = sizeof(struct usb_otg_descriptor), | |
101 | .bDescriptorType = USB_DT_OTG, | |
102 | ||
fc19de61 MN |
103 | /* |
104 | * REVISIT SRP-only hardware is possible, although | |
105 | * it would not be called "OTG" ... | |
106 | */ | |
c6c56008 MN |
107 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, |
108 | }, | |
109 | ||
110 | NULL | |
111 | }; | |
112 | ||
5ab54cf7 | 113 | /* String IDs are assigned dynamically */ |
c6c56008 | 114 | static struct usb_string gfs_strings[] = { |
276e2e4f | 115 | [USB_GADGET_MANUFACTURER_IDX].s = "", |
d33f74fc | 116 | [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, |
276e2e4f | 117 | [USB_GADGET_SERIAL_IDX].s = "", |
c6c56008 | 118 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
f8dae531 | 119 | { .s = "FunctionFS + RNDIS" }, |
c6c56008 MN |
120 | #endif |
121 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | |
f8dae531 | 122 | { .s = "FunctionFS + ECM" }, |
c6c56008 MN |
123 | #endif |
124 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | |
f8dae531 | 125 | { .s = "FunctionFS" }, |
c6c56008 MN |
126 | #endif |
127 | { } /* end of list */ | |
128 | }; | |
129 | ||
130 | static struct usb_gadget_strings *gfs_dev_strings[] = { | |
131 | &(struct usb_gadget_strings) { | |
132 | .language = 0x0409, /* en-us */ | |
133 | .strings = gfs_strings, | |
134 | }, | |
135 | NULL, | |
136 | }; | |
137 | ||
f8dae531 MN |
138 | struct gfs_configuration { |
139 | struct usb_configuration c; | |
6e257b14 | 140 | int (*eth)(struct usb_configuration *c); |
f8dae531 | 141 | } gfs_configurations[] = { |
c6c56008 | 142 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
f8dae531 | 143 | { |
6e257b14 | 144 | .eth = bind_rndis_config, |
f8dae531 | 145 | }, |
c6c56008 MN |
146 | #endif |
147 | ||
c6c56008 | 148 | #ifdef CONFIG_USB_FUNCTIONFS_ETH |
f8dae531 MN |
149 | { |
150 | .eth = eth_bind_config, | |
151 | }, | |
c6c56008 MN |
152 | #endif |
153 | ||
c6c56008 | 154 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC |
f8dae531 MN |
155 | { |
156 | }, | |
c6c56008 | 157 | #endif |
f8dae531 | 158 | }; |
c6c56008 | 159 | |
c6c56008 MN |
160 | static int gfs_bind(struct usb_composite_dev *cdev); |
161 | static int gfs_unbind(struct usb_composite_dev *cdev); | |
f8dae531 | 162 | static int gfs_do_config(struct usb_configuration *c); |
c6c56008 | 163 | |
c2ec75c2 | 164 | static __refdata struct usb_composite_driver gfs_driver = { |
fc19de61 | 165 | .name = DRIVER_NAME, |
c6c56008 MN |
166 | .dev = &gfs_dev_desc, |
167 | .strings = gfs_dev_strings, | |
35a0e0bf | 168 | .max_speed = USB_SPEED_HIGH, |
03e42bd5 | 169 | .bind = gfs_bind, |
c6c56008 MN |
170 | .unbind = gfs_unbind, |
171 | }; | |
172 | ||
581791f5 AP |
173 | static DEFINE_MUTEX(gfs_lock); |
174 | static unsigned int missing_funcs; | |
581791f5 AP |
175 | static bool gfs_registered; |
176 | static bool gfs_single_func; | |
e72c39c0 | 177 | static struct ffs_dev *ffs_tab; |
c6c56008 | 178 | |
8545e603 | 179 | static int __init gfs_init(void) |
c6c56008 | 180 | { |
581791f5 AP |
181 | int i; |
182 | ||
c6c56008 MN |
183 | ENTER(); |
184 | ||
581791f5 AP |
185 | if (!func_num) { |
186 | gfs_single_func = true; | |
187 | func_num = 1; | |
188 | } | |
189 | ||
190 | ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL); | |
191 | if (!ffs_tab) | |
192 | return -ENOMEM; | |
193 | ||
194 | if (!gfs_single_func) | |
195 | for (i = 0; i < func_num; i++) | |
196 | ffs_tab[i].name = func_names[i]; | |
197 | ||
198 | missing_funcs = func_num; | |
199 | ||
c6c56008 MN |
200 | return functionfs_init(); |
201 | } | |
202 | module_init(gfs_init); | |
203 | ||
8545e603 | 204 | static void __exit gfs_exit(void) |
c6c56008 MN |
205 | { |
206 | ENTER(); | |
581791f5 | 207 | mutex_lock(&gfs_lock); |
c6c56008 | 208 | |
581791f5 | 209 | if (gfs_registered) |
c6c56008 | 210 | usb_composite_unregister(&gfs_driver); |
581791f5 | 211 | gfs_registered = false; |
c6c56008 MN |
212 | |
213 | functionfs_cleanup(); | |
581791f5 AP |
214 | |
215 | mutex_unlock(&gfs_lock); | |
216 | kfree(ffs_tab); | |
c6c56008 MN |
217 | } |
218 | module_exit(gfs_exit); | |
219 | ||
e72c39c0 | 220 | static struct ffs_dev *gfs_find_dev(const char *dev_name) |
581791f5 AP |
221 | { |
222 | int i; | |
223 | ||
224 | ENTER(); | |
225 | ||
226 | if (gfs_single_func) | |
227 | return &ffs_tab[0]; | |
228 | ||
229 | for (i = 0; i < func_num; i++) | |
230 | if (strcmp(ffs_tab[i].name, dev_name) == 0) | |
231 | return &ffs_tab[i]; | |
232 | ||
233 | return NULL; | |
234 | } | |
235 | ||
c6c56008 MN |
236 | static int functionfs_ready_callback(struct ffs_data *ffs) |
237 | { | |
e72c39c0 | 238 | struct ffs_dev *ffs_obj; |
c6c56008 MN |
239 | int ret; |
240 | ||
241 | ENTER(); | |
581791f5 AP |
242 | mutex_lock(&gfs_lock); |
243 | ||
244 | ffs_obj = ffs->private_data; | |
245 | if (!ffs_obj) { | |
246 | ret = -EINVAL; | |
247 | goto done; | |
248 | } | |
249 | ||
250 | if (WARN_ON(ffs_obj->desc_ready)) { | |
251 | ret = -EBUSY; | |
252 | goto done; | |
253 | } | |
254 | ffs_obj->desc_ready = true; | |
255 | ffs_obj->ffs_data = ffs; | |
c6c56008 | 256 | |
581791f5 AP |
257 | if (--missing_funcs) { |
258 | ret = 0; | |
259 | goto done; | |
260 | } | |
261 | ||
262 | if (gfs_registered) { | |
263 | ret = -EBUSY; | |
264 | goto done; | |
265 | } | |
266 | gfs_registered = true; | |
c6c56008 | 267 | |
03e42bd5 | 268 | ret = usb_composite_probe(&gfs_driver); |
c6c56008 | 269 | if (unlikely(ret < 0)) |
581791f5 AP |
270 | gfs_registered = false; |
271 | ||
272 | done: | |
273 | mutex_unlock(&gfs_lock); | |
c6c56008 MN |
274 | return ret; |
275 | } | |
276 | ||
277 | static void functionfs_closed_callback(struct ffs_data *ffs) | |
278 | { | |
e72c39c0 | 279 | struct ffs_dev *ffs_obj; |
581791f5 | 280 | |
c6c56008 | 281 | ENTER(); |
581791f5 | 282 | mutex_lock(&gfs_lock); |
c6c56008 | 283 | |
581791f5 AP |
284 | ffs_obj = ffs->private_data; |
285 | if (!ffs_obj) | |
286 | goto done; | |
287 | ||
288 | ffs_obj->desc_ready = false; | |
289 | missing_funcs++; | |
290 | ||
291 | if (gfs_registered) | |
c6c56008 | 292 | usb_composite_unregister(&gfs_driver); |
581791f5 AP |
293 | gfs_registered = false; |
294 | ||
295 | done: | |
296 | mutex_unlock(&gfs_lock); | |
c6c56008 MN |
297 | } |
298 | ||
581791f5 | 299 | static void *functionfs_acquire_dev_callback(const char *dev_name) |
c6c56008 | 300 | { |
e72c39c0 | 301 | struct ffs_dev *ffs_dev; |
581791f5 AP |
302 | |
303 | ENTER(); | |
304 | mutex_lock(&gfs_lock); | |
305 | ||
306 | ffs_dev = gfs_find_dev(dev_name); | |
307 | if (!ffs_dev) { | |
308 | ffs_dev = ERR_PTR(-ENODEV); | |
309 | goto done; | |
310 | } | |
311 | ||
312 | if (ffs_dev->mounted) { | |
313 | ffs_dev = ERR_PTR(-EBUSY); | |
314 | goto done; | |
315 | } | |
316 | ffs_dev->mounted = true; | |
317 | ||
318 | done: | |
319 | mutex_unlock(&gfs_lock); | |
320 | return ffs_dev; | |
c6c56008 MN |
321 | } |
322 | ||
581791f5 AP |
323 | static void functionfs_release_dev_callback(struct ffs_data *ffs_data) |
324 | { | |
e72c39c0 | 325 | struct ffs_dev *ffs_dev; |
581791f5 AP |
326 | |
327 | ENTER(); | |
328 | mutex_lock(&gfs_lock); | |
329 | ||
330 | ffs_dev = ffs_data->private_data; | |
331 | if (ffs_dev) | |
332 | ffs_dev->mounted = false; | |
333 | ||
334 | mutex_unlock(&gfs_lock); | |
335 | } | |
336 | ||
337 | /* | |
338 | * It is assumed that gfs_bind is called from a context where gfs_lock is held | |
339 | */ | |
c6c56008 MN |
340 | static int gfs_bind(struct usb_composite_dev *cdev) |
341 | { | |
6e257b14 | 342 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS |
85aec59f AP |
343 | struct net_device *net; |
344 | #endif | |
f8dae531 | 345 | int ret, i; |
c6c56008 MN |
346 | |
347 | ENTER(); | |
348 | ||
581791f5 | 349 | if (missing_funcs) |
c6c56008 | 350 | return -ENODEV; |
f212ad4e AP |
351 | #if defined CONFIG_USB_FUNCTIONFS_ETH |
352 | if (can_support_ecm(cdev->gadget)) { | |
353 | struct f_ecm_opts *ecm_opts; | |
354 | ||
355 | fi_ecm = usb_get_function_instance("ecm"); | |
356 | if (IS_ERR(fi_ecm)) | |
357 | return PTR_ERR(fi_ecm); | |
358 | ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); | |
85aec59f | 359 | net = ecm_opts->net; |
f212ad4e | 360 | } else { |
85aec59f AP |
361 | struct f_gether_opts *geth_opts; |
362 | ||
363 | fi_geth = usb_get_function_instance("geth"); | |
364 | if (IS_ERR(fi_geth)) | |
365 | return PTR_ERR(fi_geth); | |
366 | geth_opts = container_of(fi_geth, struct f_gether_opts, | |
367 | func_inst); | |
368 | net = geth_opts->net; | |
f212ad4e | 369 | } |
6e257b14 AP |
370 | #endif |
371 | ||
372 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
373 | { | |
374 | struct f_rndis_opts *rndis_opts; | |
375 | ||
376 | fi_rndis = usb_get_function_instance("rndis"); | |
377 | if (IS_ERR(fi_rndis)) { | |
378 | ret = PTR_ERR(fi_rndis); | |
379 | goto error; | |
380 | } | |
381 | rndis_opts = container_of(fi_rndis, struct f_rndis_opts, | |
382 | func_inst); | |
383 | #ifndef CONFIG_USB_FUNCTIONFS_ETH | |
384 | net = rndis_opts->net; | |
385 | #endif | |
386 | } | |
387 | #endif | |
85aec59f | 388 | |
6e257b14 AP |
389 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS |
390 | gether_set_qmult(net, qmult); | |
85aec59f AP |
391 | if (!gether_set_host_addr(net, host_addr)) |
392 | pr_info("using host ethernet address: %s", host_addr); | |
393 | if (!gether_set_dev_addr(net, dev_addr)) | |
394 | pr_info("using self ethernet address: %s", dev_addr); | |
d6a01439 | 395 | #endif |
f212ad4e AP |
396 | |
397 | #if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH | |
85aec59f AP |
398 | gether_set_gadget(net, cdev->gadget); |
399 | ret = gether_register_netdev(net); | |
400 | if (ret) | |
6e257b14 | 401 | goto error_rndis; |
85aec59f | 402 | |
f212ad4e AP |
403 | if (can_support_ecm(cdev->gadget)) { |
404 | struct f_ecm_opts *ecm_opts; | |
405 | ||
406 | ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); | |
f212ad4e | 407 | ecm_opts->bound = true; |
85aec59f AP |
408 | } else { |
409 | struct f_gether_opts *geth_opts; | |
410 | ||
411 | geth_opts = container_of(fi_geth, struct f_gether_opts, | |
412 | func_inst); | |
413 | geth_opts->bound = true; | |
d6a01439 | 414 | } |
6e257b14 AP |
415 | |
416 | rndis_borrow_net(fi_rndis, net); | |
f212ad4e | 417 | #endif |
c6c56008 | 418 | |
f8dae531 | 419 | ret = usb_string_ids_tab(cdev, gfs_strings); |
c6c56008 | 420 | if (unlikely(ret < 0)) |
6e257b14 | 421 | goto error_rndis; |
d33f74fc | 422 | gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; |
c6c56008 | 423 | |
3416905b | 424 | for (i = func_num; i--; ) { |
581791f5 AP |
425 | ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); |
426 | if (unlikely(ret < 0)) { | |
427 | while (++i < func_num) | |
428 | functionfs_unbind(ffs_tab[i].ffs_data); | |
6e257b14 | 429 | goto error_rndis; |
581791f5 AP |
430 | } |
431 | } | |
c6c56008 | 432 | |
f8dae531 MN |
433 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { |
434 | struct gfs_configuration *c = gfs_configurations + i; | |
276e2e4f | 435 | int sid = USB_GADGET_FIRST_AVAIL_IDX + i; |
c6c56008 | 436 | |
276e2e4f SAS |
437 | c->c.label = gfs_strings[sid].s; |
438 | c->c.iConfiguration = gfs_strings[sid].id; | |
f8dae531 MN |
439 | c->c.bConfigurationValue = 1 + i; |
440 | c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; | |
c6c56008 | 441 | |
c9bfff9c | 442 | ret = usb_add_config(cdev, &c->c, gfs_do_config); |
f8dae531 MN |
443 | if (unlikely(ret < 0)) |
444 | goto error_unbind; | |
445 | } | |
7d16e8d3 | 446 | usb_composite_overwrite_options(cdev, &coverwrite); |
c6c56008 MN |
447 | return 0; |
448 | ||
449 | error_unbind: | |
581791f5 AP |
450 | for (i = 0; i < func_num; i++) |
451 | functionfs_unbind(ffs_tab[i].ffs_data); | |
6e257b14 AP |
452 | error_rndis: |
453 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
454 | usb_put_function_instance(fi_rndis); | |
c6c56008 | 455 | error: |
6e257b14 | 456 | #endif |
f212ad4e AP |
457 | #if defined CONFIG_USB_FUNCTIONFS_ETH |
458 | if (can_support_ecm(cdev->gadget)) | |
459 | usb_put_function_instance(fi_ecm); | |
460 | else | |
85aec59f | 461 | usb_put_function_instance(fi_geth); |
f212ad4e | 462 | #endif |
c6c56008 MN |
463 | return ret; |
464 | } | |
465 | ||
581791f5 AP |
466 | /* |
467 | * It is assumed that gfs_unbind is called from a context where gfs_lock is held | |
468 | */ | |
c6c56008 MN |
469 | static int gfs_unbind(struct usb_composite_dev *cdev) |
470 | { | |
581791f5 AP |
471 | int i; |
472 | ||
c6c56008 MN |
473 | ENTER(); |
474 | ||
f212ad4e | 475 | |
6e257b14 AP |
476 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS |
477 | usb_put_function(f_rndis); | |
478 | usb_put_function_instance(fi_rndis); | |
479 | #endif | |
480 | ||
f212ad4e AP |
481 | #if defined CONFIG_USB_FUNCTIONFS_ETH |
482 | if (can_support_ecm(cdev->gadget)) { | |
483 | usb_put_function(f_ecm); | |
484 | usb_put_function_instance(fi_ecm); | |
485 | } else { | |
85aec59f AP |
486 | usb_put_function(f_geth); |
487 | usb_put_function_instance(fi_geth); | |
f212ad4e | 488 | } |
f212ad4e | 489 | #endif |
1ec8f00f | 490 | |
fc19de61 MN |
491 | /* |
492 | * We may have been called in an error recovery from | |
c6c56008 | 493 | * composite_bind() after gfs_unbind() failure so we need to |
1ec8f00f | 494 | * check if instance's ffs_data is not NULL since gfs_bind() handles |
c6c56008 MN |
495 | * all error recovery itself. I'd rather we werent called |
496 | * from composite on orror recovery, but what you're gonna | |
fc19de61 MN |
497 | * do...? |
498 | */ | |
3416905b | 499 | for (i = func_num; i--; ) |
581791f5 AP |
500 | if (ffs_tab[i].ffs_data) |
501 | functionfs_unbind(ffs_tab[i].ffs_data); | |
c6c56008 MN |
502 | |
503 | return 0; | |
504 | } | |
505 | ||
581791f5 AP |
506 | /* |
507 | * It is assumed that gfs_do_config is called from a context where | |
508 | * gfs_lock is held | |
509 | */ | |
f8dae531 | 510 | static int gfs_do_config(struct usb_configuration *c) |
c6c56008 | 511 | { |
f8dae531 MN |
512 | struct gfs_configuration *gc = |
513 | container_of(c, struct gfs_configuration, c); | |
581791f5 | 514 | int i; |
c6c56008 MN |
515 | int ret; |
516 | ||
581791f5 | 517 | if (missing_funcs) |
c6c56008 MN |
518 | return -ENODEV; |
519 | ||
520 | if (gadget_is_otg(c->cdev->gadget)) { | |
521 | c->descriptors = gfs_otg_desc; | |
522 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | |
523 | } | |
524 | ||
f8dae531 | 525 | if (gc->eth) { |
6e257b14 | 526 | ret = gc->eth(c); |
c6c56008 MN |
527 | if (unlikely(ret < 0)) |
528 | return ret; | |
529 | } | |
530 | ||
581791f5 AP |
531 | for (i = 0; i < func_num; i++) { |
532 | ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); | |
533 | if (unlikely(ret < 0)) | |
534 | return ret; | |
535 | } | |
c6c56008 | 536 | |
fc19de61 MN |
537 | /* |
538 | * After previous do_configs there may be some invalid | |
f588c0db MN |
539 | * pointers in c->interface array. This happens every time |
540 | * a user space function with fewer interfaces than a user | |
541 | * space function that was run before the new one is run. The | |
542 | * compasit's set_config() assumes that if there is no more | |
543 | * then MAX_CONFIG_INTERFACES interfaces in a configuration | |
544 | * then there is a NULL pointer after the last interface in | |
fc19de61 MN |
545 | * c->interface array. We need to make sure this is true. |
546 | */ | |
f588c0db MN |
547 | if (c->next_interface_id < ARRAY_SIZE(c->interface)) |
548 | c->interface[c->next_interface_id] = NULL; | |
549 | ||
c6c56008 MN |
550 | return 0; |
551 | } | |
552 | ||
c6c56008 | 553 | #ifdef CONFIG_USB_FUNCTIONFS_ETH |
fc19de61 | 554 | |
6e257b14 | 555 | static int eth_bind_config(struct usb_configuration *c) |
c6c56008 | 556 | { |
f212ad4e AP |
557 | int status = 0; |
558 | ||
559 | if (can_support_ecm(c->cdev->gadget)) { | |
560 | f_ecm = usb_get_function(fi_ecm); | |
561 | if (IS_ERR(f_ecm)) | |
562 | return PTR_ERR(f_ecm); | |
563 | ||
564 | status = usb_add_function(c, f_ecm); | |
565 | if (status < 0) | |
566 | usb_put_function(f_ecm); | |
567 | ||
568 | } else { | |
85aec59f AP |
569 | f_geth = usb_get_function(fi_geth); |
570 | if (IS_ERR(f_geth)) | |
571 | return PTR_ERR(f_geth); | |
572 | ||
573 | status = usb_add_function(c, f_geth); | |
574 | if (status < 0) | |
575 | usb_put_function(f_geth); | |
f212ad4e AP |
576 | } |
577 | return status; | |
c6c56008 | 578 | } |
fc19de61 | 579 | |
c6c56008 | 580 | #endif |
6e257b14 AP |
581 | |
582 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | |
583 | ||
584 | static int bind_rndis_config(struct usb_configuration *c) | |
585 | { | |
586 | int status = 0; | |
587 | ||
588 | f_rndis = usb_get_function(fi_rndis); | |
589 | if (IS_ERR(f_rndis)) | |
590 | return PTR_ERR(f_rndis); | |
591 | ||
592 | status = usb_add_function(c, f_rndis); | |
593 | if (status < 0) | |
594 | usb_put_function(f_rndis); | |
595 | ||
596 | return status; | |
597 | } | |
598 | ||
599 | #endif |