diff options
-rw-r--r-- | drivers/usb/gadget/Kconfig | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/ether.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/f_subset.c | 136 | ||||
-rw-r--r-- | drivers/usb/gadget/g_ffs.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/u_gether.h | 27 |
6 files changed, 155 insertions, 15 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d5b0ffe26118..803aa93f3aa5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig | |||
@@ -523,6 +523,9 @@ config USB_F_PHONET | |||
523 | config USB_F_EEM | 523 | config USB_F_EEM |
524 | tristate | 524 | tristate |
525 | 525 | ||
526 | config USB_F_SUBSET | ||
527 | tristate | ||
528 | |||
526 | choice | 529 | choice |
527 | tristate "USB Gadget Drivers" | 530 | tristate "USB Gadget Drivers" |
528 | default USB_ETH | 531 | default USB_ETH |
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7069f53e140d..1bfad55b9678 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile | |||
@@ -56,6 +56,8 @@ usb_f_phonet-y := f_phonet.o | |||
56 | obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o | 56 | obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o |
57 | usb_f_eem-y := f_eem.o | 57 | usb_f_eem-y := f_eem.o |
58 | obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o | 58 | obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o |
59 | usb_f_ecm_subset-y := f_subset.o | ||
60 | obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o | ||
59 | 61 | ||
60 | # | 62 | # |
61 | # USB gadget drivers | 63 | # USB gadget drivers |
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 2078e6c227a1..31739662ad00 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c | |||
@@ -103,6 +103,7 @@ static inline bool has_rndis(void) | |||
103 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | 103 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. |
104 | */ | 104 | */ |
105 | #include "u_ecm.h" | 105 | #include "u_ecm.h" |
106 | #define USB_FSUBSET_INCLUDED | ||
106 | #include "f_subset.c" | 107 | #include "f_subset.c" |
107 | #ifdef USB_ETH_RNDIS | 108 | #ifdef USB_ETH_RNDIS |
108 | #include "f_rndis.c" | 109 | #include "f_rndis.c" |
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 7be04b342494..5ae0bf612428 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c | |||
@@ -12,11 +12,12 @@ | |||
12 | 12 | ||
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> | ||
15 | #include <linux/device.h> | 16 | #include <linux/device.h> |
16 | #include <linux/etherdevice.h> | 17 | #include <linux/etherdevice.h> |
17 | 18 | ||
18 | #include "u_ether.h" | 19 | #include "u_ether.h" |
19 | 20 | #include "u_gether.h" | |
20 | 21 | ||
21 | /* | 22 | /* |
22 | * This function packages a simple "CDC Subset" Ethernet port with no real | 23 | * This function packages a simple "CDC Subset" Ethernet port with no real |
@@ -298,6 +299,35 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) | |||
298 | int status; | 299 | int status; |
299 | struct usb_ep *ep; | 300 | struct usb_ep *ep; |
300 | 301 | ||
302 | #ifndef USB_FSUBSET_INCLUDED | ||
303 | struct f_gether_opts *gether_opts; | ||
304 | |||
305 | gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); | ||
306 | |||
307 | /* | ||
308 | * in drivers/usb/gadget/configfs.c:configfs_composite_bind() | ||
309 | * configurations are bound in sequence with list_for_each_entry, | ||
310 | * in each configuration its functions are bound in sequence | ||
311 | * with list_for_each_entry, so we assume no race condition | ||
312 | * with regard to gether_opts->bound access | ||
313 | */ | ||
314 | if (!gether_opts->bound) { | ||
315 | gether_set_gadget(gether_opts->net, cdev->gadget); | ||
316 | status = gether_register_netdev(gether_opts->net); | ||
317 | if (status) | ||
318 | return status; | ||
319 | gether_opts->bound = true; | ||
320 | } | ||
321 | #endif | ||
322 | /* maybe allocate device-global string IDs */ | ||
323 | if (geth_string_defs[0].id == 0) { | ||
324 | status = usb_string_ids_tab(c->cdev, geth_string_defs); | ||
325 | if (status < 0) | ||
326 | return status; | ||
327 | subset_data_intf.iInterface = geth_string_defs[0].id; | ||
328 | ether_desc.iMACAddress = geth_string_defs[1].id; | ||
329 | } | ||
330 | |||
301 | /* allocate instance-specific interface IDs */ | 331 | /* allocate instance-specific interface IDs */ |
302 | status = usb_interface_id(c, f); | 332 | status = usb_interface_id(c, f); |
303 | if (status < 0) | 333 | if (status < 0) |
@@ -360,8 +390,10 @@ fail: | |||
360 | return status; | 390 | return status; |
361 | } | 391 | } |
362 | 392 | ||
393 | #ifdef USB_FSUBSET_INCLUDED | ||
394 | |||
363 | static void | 395 | static void |
364 | geth_unbind(struct usb_configuration *c, struct usb_function *f) | 396 | geth_old_unbind(struct usb_configuration *c, struct usb_function *f) |
365 | { | 397 | { |
366 | geth_string_defs[0].id = 0; | 398 | geth_string_defs[0].id = 0; |
367 | usb_free_all_descriptors(f); | 399 | usb_free_all_descriptors(f); |
@@ -387,18 +419,6 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
387 | struct f_gether *geth; | 419 | struct f_gether *geth; |
388 | int status; | 420 | int status; |
389 | 421 | ||
390 | if (!ethaddr) | ||
391 | return -EINVAL; | ||
392 | |||
393 | /* maybe allocate device-global string IDs */ | ||
394 | if (geth_string_defs[0].id == 0) { | ||
395 | status = usb_string_ids_tab(c->cdev, geth_string_defs); | ||
396 | if (status < 0) | ||
397 | return status; | ||
398 | subset_data_intf.iInterface = geth_string_defs[0].id; | ||
399 | ether_desc.iMACAddress = geth_string_defs[1].id; | ||
400 | } | ||
401 | |||
402 | /* allocate and initialize one new instance */ | 422 | /* allocate and initialize one new instance */ |
403 | geth = kzalloc(sizeof *geth, GFP_KERNEL); | 423 | geth = kzalloc(sizeof *geth, GFP_KERNEL); |
404 | if (!geth) | 424 | if (!geth) |
@@ -414,7 +434,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
414 | geth->port.func.name = "cdc_subset"; | 434 | geth->port.func.name = "cdc_subset"; |
415 | geth->port.func.strings = geth_strings; | 435 | geth->port.func.strings = geth_strings; |
416 | geth->port.func.bind = geth_bind; | 436 | geth->port.func.bind = geth_bind; |
417 | geth->port.func.unbind = geth_unbind; | 437 | geth->port.func.unbind = geth_old_unbind; |
418 | geth->port.func.set_alt = geth_set_alt; | 438 | geth->port.func.set_alt = geth_set_alt; |
419 | geth->port.func.disable = geth_disable; | 439 | geth->port.func.disable = geth_disable; |
420 | 440 | ||
@@ -423,3 +443,89 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
423 | kfree(geth); | 443 | kfree(geth); |
424 | return status; | 444 | return status; |
425 | } | 445 | } |
446 | |||
447 | #else | ||
448 | |||
449 | static void geth_free_inst(struct usb_function_instance *f) | ||
450 | { | ||
451 | struct f_gether_opts *opts; | ||
452 | |||
453 | opts = container_of(f, struct f_gether_opts, func_inst); | ||
454 | if (opts->bound) | ||
455 | gether_cleanup(netdev_priv(opts->net)); | ||
456 | else | ||
457 | free_netdev(opts->net); | ||
458 | kfree(opts); | ||
459 | } | ||
460 | |||
461 | static struct usb_function_instance *geth_alloc_inst(void) | ||
462 | { | ||
463 | struct f_gether_opts *opts; | ||
464 | |||
465 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | ||
466 | if (!opts) | ||
467 | return ERR_PTR(-ENOMEM); | ||
468 | |||
469 | opts->func_inst.free_func_inst = geth_free_inst; | ||
470 | opts->net = gether_setup_default(); | ||
471 | if (IS_ERR(opts->net)) | ||
472 | return ERR_CAST(opts->net); | ||
473 | |||
474 | return &opts->func_inst; | ||
475 | } | ||
476 | |||
477 | static void geth_free(struct usb_function *f) | ||
478 | { | ||
479 | struct f_gether *eth; | ||
480 | |||
481 | eth = func_to_geth(f); | ||
482 | kfree(eth); | ||
483 | } | ||
484 | |||
485 | static void geth_unbind(struct usb_configuration *c, struct usb_function *f) | ||
486 | { | ||
487 | geth_string_defs[0].id = 0; | ||
488 | usb_free_all_descriptors(f); | ||
489 | } | ||
490 | |||
491 | static struct usb_function *geth_alloc(struct usb_function_instance *fi) | ||
492 | { | ||
493 | struct f_gether *geth; | ||
494 | struct f_gether_opts *opts; | ||
495 | int status; | ||
496 | |||
497 | /* allocate and initialize one new instance */ | ||
498 | geth = kzalloc(sizeof(*geth), GFP_KERNEL); | ||
499 | if (!geth) | ||
500 | return ERR_PTR(-ENOMEM); | ||
501 | |||
502 | opts = container_of(fi, struct f_gether_opts, func_inst); | ||
503 | |||
504 | /* export host's Ethernet address in CDC format */ | ||
505 | status = gether_get_host_addr_cdc(opts->net, geth->ethaddr, | ||
506 | sizeof(geth->ethaddr)); | ||
507 | if (status < 12) { | ||
508 | kfree(geth); | ||
509 | return ERR_PTR(-EINVAL); | ||
510 | } | ||
511 | geth_string_defs[1].s = geth->ethaddr; | ||
512 | |||
513 | geth->port.ioport = netdev_priv(opts->net); | ||
514 | geth->port.cdc_filter = DEFAULT_FILTER; | ||
515 | |||
516 | geth->port.func.name = "cdc_subset"; | ||
517 | geth->port.func.strings = geth_strings; | ||
518 | geth->port.func.bind = geth_bind; | ||
519 | geth->port.func.unbind = geth_unbind; | ||
520 | geth->port.func.set_alt = geth_set_alt; | ||
521 | geth->port.func.disable = geth_disable; | ||
522 | geth->port.func.free_func = geth_free; | ||
523 | |||
524 | return &geth->port.func; | ||
525 | } | ||
526 | |||
527 | DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc); | ||
528 | MODULE_LICENSE("GPL"); | ||
529 | MODULE_AUTHOR("David Brownell"); | ||
530 | |||
531 | #endif | ||
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index d38a073efd5d..3d290e5106af 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #define USBF_ECM_INCLUDED | 31 | #define USBF_ECM_INCLUDED |
32 | # include "f_ecm.c" | 32 | # include "f_ecm.c" |
33 | #define USB_FSUBSET_INCLUDED | ||
33 | # include "f_subset.c" | 34 | # include "f_subset.c" |
34 | # ifdef USB_ETH_RNDIS | 35 | # ifdef USB_ETH_RNDIS |
35 | # include "f_rndis.c" | 36 | # include "f_rndis.c" |
diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/u_gether.h new file mode 100644 index 000000000000..3a4a2bf61cdc --- /dev/null +++ b/drivers/usb/gadget/u_gether.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * u_gether.h | ||
3 | * | ||
4 | * Utility definitions for the subset function | ||
5 | * | ||
6 | * Copyright (c) 2013 Samsung Electronics Co., Ltd. | ||
7 | * http://www.samsung.com | ||
8 | * | ||
9 | * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #ifndef U_GETHER_H | ||
17 | #define U_GETHER_H | ||
18 | |||
19 | #include <linux/usb/composite.h> | ||
20 | |||
21 | struct f_gether_opts { | ||
22 | struct usb_function_instance func_inst; | ||
23 | struct net_device *net; | ||
24 | bool bound; | ||
25 | }; | ||
26 | |||
27 | #endif /* U_GETHER_H */ | ||