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