diff options
author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2012-05-14 09:51:52 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-14 12:25:44 -0400 |
commit | 581791f5c7a480b2cc3431af9a6e799ffd51eb5e (patch) | |
tree | 3284544e5a67e91ec612fc0a715cff66b250a283 /drivers/usb/gadget/g_ffs.c | |
parent | 304f0b2453ea377b8f987aa5f9e1ccda0e3adfa7 (diff) |
FunctionFS: enable multiple functions
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Cc: Felipe Balbi <balbi@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/gadget/g_ffs.c')
-rw-r--r-- | drivers/usb/gadget/g_ffs.c | 200 |
1 files changed, 176 insertions, 24 deletions
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index a85eaf40b948..d3ace9002a6a 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c | |||
@@ -67,6 +67,15 @@ MODULE_LICENSE("GPL"); | |||
67 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ | 67 | #define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */ |
68 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ | 68 | #define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */ |
69 | 69 | ||
70 | #define GFS_MAX_DEVS 10 | ||
71 | |||
72 | struct gfs_ffs_obj { | ||
73 | const char *name; | ||
74 | bool mounted; | ||
75 | bool desc_ready; | ||
76 | struct ffs_data *ffs_data; | ||
77 | }; | ||
78 | |||
70 | static struct usb_device_descriptor gfs_dev_desc = { | 79 | static struct usb_device_descriptor gfs_dev_desc = { |
71 | .bLength = sizeof gfs_dev_desc, | 80 | .bLength = sizeof gfs_dev_desc, |
72 | .bDescriptorType = USB_DT_DEVICE, | 81 | .bDescriptorType = USB_DT_DEVICE, |
@@ -78,12 +87,17 @@ static struct usb_device_descriptor gfs_dev_desc = { | |||
78 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), | 87 | .idProduct = cpu_to_le16(GFS_PRODUCT_ID), |
79 | }; | 88 | }; |
80 | 89 | ||
90 | static char *func_names[GFS_MAX_DEVS]; | ||
91 | static unsigned int func_num; | ||
92 | |||
81 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); | 93 | module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644); |
82 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); | 94 | MODULE_PARM_DESC(bDeviceClass, "USB Device class"); |
83 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); | 95 | module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644); |
84 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); | 96 | MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass"); |
85 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); | 97 | module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644); |
86 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); | 98 | MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol"); |
99 | module_param_array_named(functions, func_names, charp, &func_num, 0); | ||
100 | MODULE_PARM_DESC(functions, "USB Functions list"); | ||
87 | 101 | ||
88 | static const struct usb_descriptor_header *gfs_otg_desc[] = { | 102 | static const struct usb_descriptor_header *gfs_otg_desc[] = { |
89 | (const struct usb_descriptor_header *) | 103 | (const struct usb_descriptor_header *) |
@@ -158,13 +172,34 @@ static struct usb_composite_driver gfs_driver = { | |||
158 | .iProduct = DRIVER_DESC, | 172 | .iProduct = DRIVER_DESC, |
159 | }; | 173 | }; |
160 | 174 | ||
161 | static struct ffs_data *gfs_ffs_data; | 175 | static DEFINE_MUTEX(gfs_lock); |
162 | static unsigned long gfs_registered; | 176 | static unsigned int missing_funcs; |
177 | static bool gfs_ether_setup; | ||
178 | static bool gfs_registered; | ||
179 | static bool gfs_single_func; | ||
180 | static struct gfs_ffs_obj *ffs_tab; | ||
163 | 181 | ||
164 | static int __init gfs_init(void) | 182 | static int __init gfs_init(void) |
165 | { | 183 | { |
184 | int i; | ||
185 | |||
166 | ENTER(); | 186 | ENTER(); |
167 | 187 | ||
188 | if (!func_num) { | ||
189 | gfs_single_func = true; | ||
190 | func_num = 1; | ||
191 | } | ||
192 | |||
193 | ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL); | ||
194 | if (!ffs_tab) | ||
195 | return -ENOMEM; | ||
196 | |||
197 | if (!gfs_single_func) | ||
198 | for (i = 0; i < func_num; i++) | ||
199 | ffs_tab[i].name = func_names[i]; | ||
200 | |||
201 | missing_funcs = func_num; | ||
202 | |||
168 | return functionfs_init(); | 203 | return functionfs_init(); |
169 | } | 204 | } |
170 | module_init(gfs_init); | 205 | module_init(gfs_init); |
@@ -172,63 +207,165 @@ module_init(gfs_init); | |||
172 | static void __exit gfs_exit(void) | 207 | static void __exit gfs_exit(void) |
173 | { | 208 | { |
174 | ENTER(); | 209 | ENTER(); |
210 | mutex_lock(&gfs_lock); | ||
175 | 211 | ||
176 | if (test_and_clear_bit(0, &gfs_registered)) | 212 | if (gfs_registered) |
177 | usb_composite_unregister(&gfs_driver); | 213 | usb_composite_unregister(&gfs_driver); |
214 | gfs_registered = false; | ||
178 | 215 | ||
179 | functionfs_cleanup(); | 216 | functionfs_cleanup(); |
217 | |||
218 | mutex_unlock(&gfs_lock); | ||
219 | kfree(ffs_tab); | ||
180 | } | 220 | } |
181 | module_exit(gfs_exit); | 221 | module_exit(gfs_exit); |
182 | 222 | ||
223 | static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) | ||
224 | { | ||
225 | int i; | ||
226 | |||
227 | ENTER(); | ||
228 | |||
229 | if (gfs_single_func) | ||
230 | return &ffs_tab[0]; | ||
231 | |||
232 | for (i = 0; i < func_num; i++) | ||
233 | if (strcmp(ffs_tab[i].name, dev_name) == 0) | ||
234 | return &ffs_tab[i]; | ||
235 | |||
236 | return NULL; | ||
237 | } | ||
238 | |||
183 | static int functionfs_ready_callback(struct ffs_data *ffs) | 239 | static int functionfs_ready_callback(struct ffs_data *ffs) |
184 | { | 240 | { |
241 | struct gfs_ffs_obj *ffs_obj; | ||
185 | int ret; | 242 | int ret; |
186 | 243 | ||
187 | ENTER(); | 244 | ENTER(); |
245 | mutex_lock(&gfs_lock); | ||
246 | |||
247 | ffs_obj = ffs->private_data; | ||
248 | if (!ffs_obj) { | ||
249 | ret = -EINVAL; | ||
250 | goto done; | ||
251 | } | ||
252 | |||
253 | if (WARN_ON(ffs_obj->desc_ready)) { | ||
254 | ret = -EBUSY; | ||
255 | goto done; | ||
256 | } | ||
257 | ffs_obj->desc_ready = true; | ||
258 | ffs_obj->ffs_data = ffs; | ||
188 | 259 | ||
189 | if (WARN_ON(test_and_set_bit(0, &gfs_registered))) | 260 | if (--missing_funcs) { |
190 | return -EBUSY; | 261 | ret = 0; |
262 | goto done; | ||
263 | } | ||
264 | |||
265 | if (gfs_registered) { | ||
266 | ret = -EBUSY; | ||
267 | goto done; | ||
268 | } | ||
269 | gfs_registered = true; | ||
191 | 270 | ||
192 | gfs_ffs_data = ffs; | ||
193 | ret = usb_composite_probe(&gfs_driver, gfs_bind); | 271 | ret = usb_composite_probe(&gfs_driver, gfs_bind); |
194 | if (unlikely(ret < 0)) | 272 | if (unlikely(ret < 0)) |
195 | clear_bit(0, &gfs_registered); | 273 | gfs_registered = false; |
274 | |||
275 | done: | ||
276 | mutex_unlock(&gfs_lock); | ||
196 | return ret; | 277 | return ret; |
197 | } | 278 | } |
198 | 279 | ||
199 | static void functionfs_closed_callback(struct ffs_data *ffs) | 280 | static void functionfs_closed_callback(struct ffs_data *ffs) |
200 | { | 281 | { |
282 | struct gfs_ffs_obj *ffs_obj; | ||
283 | |||
201 | ENTER(); | 284 | ENTER(); |
285 | mutex_lock(&gfs_lock); | ||
202 | 286 | ||
203 | if (test_and_clear_bit(0, &gfs_registered)) | 287 | ffs_obj = ffs->private_data; |
288 | if (!ffs_obj) | ||
289 | goto done; | ||
290 | |||
291 | ffs_obj->desc_ready = false; | ||
292 | missing_funcs++; | ||
293 | |||
294 | if (gfs_registered) | ||
204 | usb_composite_unregister(&gfs_driver); | 295 | usb_composite_unregister(&gfs_driver); |
296 | gfs_registered = false; | ||
297 | |||
298 | done: | ||
299 | mutex_unlock(&gfs_lock); | ||
205 | } | 300 | } |
206 | 301 | ||
207 | static int functionfs_check_dev_callback(const char *dev_name) | 302 | static void *functionfs_acquire_dev_callback(const char *dev_name) |
208 | { | 303 | { |
209 | return 0; | 304 | struct gfs_ffs_obj *ffs_dev; |
305 | |||
306 | ENTER(); | ||
307 | mutex_lock(&gfs_lock); | ||
308 | |||
309 | ffs_dev = gfs_find_dev(dev_name); | ||
310 | if (!ffs_dev) { | ||
311 | ffs_dev = ERR_PTR(-ENODEV); | ||
312 | goto done; | ||
313 | } | ||
314 | |||
315 | if (ffs_dev->mounted) { | ||
316 | ffs_dev = ERR_PTR(-EBUSY); | ||
317 | goto done; | ||
318 | } | ||
319 | ffs_dev->mounted = true; | ||
320 | |||
321 | done: | ||
322 | mutex_unlock(&gfs_lock); | ||
323 | return ffs_dev; | ||
210 | } | 324 | } |
211 | 325 | ||
326 | static void functionfs_release_dev_callback(struct ffs_data *ffs_data) | ||
327 | { | ||
328 | struct gfs_ffs_obj *ffs_dev; | ||
329 | |||
330 | ENTER(); | ||
331 | mutex_lock(&gfs_lock); | ||
332 | |||
333 | ffs_dev = ffs_data->private_data; | ||
334 | if (ffs_dev) | ||
335 | ffs_dev->mounted = false; | ||
336 | |||
337 | mutex_unlock(&gfs_lock); | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * It is assumed that gfs_bind is called from a context where gfs_lock is held | ||
342 | */ | ||
212 | static int gfs_bind(struct usb_composite_dev *cdev) | 343 | static int gfs_bind(struct usb_composite_dev *cdev) |
213 | { | 344 | { |
214 | int ret, i; | 345 | int ret, i; |
215 | 346 | ||
216 | ENTER(); | 347 | ENTER(); |
217 | 348 | ||
218 | if (WARN_ON(!gfs_ffs_data)) | 349 | if (missing_funcs) |
219 | return -ENODEV; | 350 | return -ENODEV; |
220 | 351 | ||
221 | ret = gether_setup(cdev->gadget, gfs_hostaddr); | 352 | ret = gether_setup(cdev->gadget, gfs_hostaddr); |
222 | if (unlikely(ret < 0)) | 353 | if (unlikely(ret < 0)) |
223 | goto error_quick; | 354 | goto error_quick; |
355 | gfs_ether_setup = true; | ||
224 | 356 | ||
225 | ret = usb_string_ids_tab(cdev, gfs_strings); | 357 | ret = usb_string_ids_tab(cdev, gfs_strings); |
226 | if (unlikely(ret < 0)) | 358 | if (unlikely(ret < 0)) |
227 | goto error; | 359 | goto error; |
228 | 360 | ||
229 | ret = functionfs_bind(gfs_ffs_data, cdev); | 361 | for (i = func_num; --i; ) { |
230 | if (unlikely(ret < 0)) | 362 | ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); |
231 | goto error; | 363 | if (unlikely(ret < 0)) { |
364 | while (++i < func_num) | ||
365 | functionfs_unbind(ffs_tab[i].ffs_data); | ||
366 | goto error; | ||
367 | } | ||
368 | } | ||
232 | 369 | ||
233 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { | 370 | for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { |
234 | struct gfs_configuration *c = gfs_configurations + i; | 371 | struct gfs_configuration *c = gfs_configurations + i; |
@@ -246,16 +383,22 @@ static int gfs_bind(struct usb_composite_dev *cdev) | |||
246 | return 0; | 383 | return 0; |
247 | 384 | ||
248 | error_unbind: | 385 | error_unbind: |
249 | functionfs_unbind(gfs_ffs_data); | 386 | for (i = 0; i < func_num; i++) |
387 | functionfs_unbind(ffs_tab[i].ffs_data); | ||
250 | error: | 388 | error: |
251 | gether_cleanup(); | 389 | gether_cleanup(); |
252 | error_quick: | 390 | error_quick: |
253 | gfs_ffs_data = NULL; | 391 | gfs_ether_setup = false; |
254 | return ret; | 392 | return ret; |
255 | } | 393 | } |
256 | 394 | ||
395 | /* | ||
396 | * It is assumed that gfs_unbind is called from a context where gfs_lock is held | ||
397 | */ | ||
257 | static int gfs_unbind(struct usb_composite_dev *cdev) | 398 | static int gfs_unbind(struct usb_composite_dev *cdev) |
258 | { | 399 | { |
400 | int i; | ||
401 | |||
259 | ENTER(); | 402 | ENTER(); |
260 | 403 | ||
261 | /* | 404 | /* |
@@ -266,22 +409,29 @@ static int gfs_unbind(struct usb_composite_dev *cdev) | |||
266 | * from composite on orror recovery, but what you're gonna | 409 | * from composite on orror recovery, but what you're gonna |
267 | * do...? | 410 | * do...? |
268 | */ | 411 | */ |
269 | if (gfs_ffs_data) { | 412 | if (gfs_ether_setup) |
270 | gether_cleanup(); | 413 | gether_cleanup(); |
271 | functionfs_unbind(gfs_ffs_data); | 414 | gfs_ether_setup = false; |
272 | gfs_ffs_data = NULL; | 415 | |
273 | } | 416 | for (i = func_num; --i; ) |
417 | if (ffs_tab[i].ffs_data) | ||
418 | functionfs_unbind(ffs_tab[i].ffs_data); | ||
274 | 419 | ||
275 | return 0; | 420 | return 0; |
276 | } | 421 | } |
277 | 422 | ||
423 | /* | ||
424 | * It is assumed that gfs_do_config is called from a context where | ||
425 | * gfs_lock is held | ||
426 | */ | ||
278 | static int gfs_do_config(struct usb_configuration *c) | 427 | static int gfs_do_config(struct usb_configuration *c) |
279 | { | 428 | { |
280 | struct gfs_configuration *gc = | 429 | struct gfs_configuration *gc = |
281 | container_of(c, struct gfs_configuration, c); | 430 | container_of(c, struct gfs_configuration, c); |
431 | int i; | ||
282 | int ret; | 432 | int ret; |
283 | 433 | ||
284 | if (WARN_ON(!gfs_ffs_data)) | 434 | if (missing_funcs) |
285 | return -ENODEV; | 435 | return -ENODEV; |
286 | 436 | ||
287 | if (gadget_is_otg(c->cdev->gadget)) { | 437 | if (gadget_is_otg(c->cdev->gadget)) { |
@@ -295,9 +445,11 @@ static int gfs_do_config(struct usb_configuration *c) | |||
295 | return ret; | 445 | return ret; |
296 | } | 446 | } |
297 | 447 | ||
298 | ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data); | 448 | for (i = 0; i < func_num; i++) { |
299 | if (unlikely(ret < 0)) | 449 | ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); |
300 | return ret; | 450 | if (unlikely(ret < 0)) |
451 | return ret; | ||
452 | } | ||
301 | 453 | ||
302 | /* | 454 | /* |
303 | * After previous do_configs there may be some invalid | 455 | * After previous do_configs there may be some invalid |