diff options
Diffstat (limited to 'drivers/usb/gadget/f_ecm.c')
-rw-r--r-- | drivers/usb/gadget/f_ecm.c | 149 |
1 files changed, 136 insertions, 13 deletions
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index abf8a31ae146..1b5aeb280d4b 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c | |||
@@ -14,10 +14,12 @@ | |||
14 | 14 | ||
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | ||
17 | #include <linux/device.h> | 18 | #include <linux/device.h> |
18 | #include <linux/etherdevice.h> | 19 | #include <linux/etherdevice.h> |
19 | 20 | ||
20 | #include "u_ether.h" | 21 | #include "u_ether.h" |
22 | #include "u_ecm.h" | ||
21 | 23 | ||
22 | 24 | ||
23 | /* | 25 | /* |
@@ -687,6 +689,40 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) | |||
687 | int status; | 689 | int status; |
688 | struct usb_ep *ep; | 690 | struct usb_ep *ep; |
689 | 691 | ||
692 | #ifndef USBF_ECM_INCLUDED | ||
693 | struct f_ecm_opts *ecm_opts; | ||
694 | |||
695 | if (!can_support_ecm(cdev->gadget)) | ||
696 | return -EINVAL; | ||
697 | |||
698 | ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); | ||
699 | |||
700 | /* | ||
701 | * in drivers/usb/gadget/configfs.c:configfs_composite_bind() | ||
702 | * configurations are bound in sequence with list_for_each_entry, | ||
703 | * in each configuration its functions are bound in sequence | ||
704 | * with list_for_each_entry, so we assume no race condition | ||
705 | * with regard to ecm_opts->bound access | ||
706 | */ | ||
707 | if (!ecm_opts->bound) { | ||
708 | gether_set_gadget(ecm_opts->net, cdev->gadget); | ||
709 | status = gether_register_netdev(ecm_opts->net); | ||
710 | if (status) | ||
711 | return status; | ||
712 | ecm_opts->bound = true; | ||
713 | } | ||
714 | #endif | ||
715 | if (ecm_string_defs[0].id == 0) { | ||
716 | status = usb_string_ids_tab(c->cdev, ecm_string_defs); | ||
717 | if (status) | ||
718 | return status; | ||
719 | |||
720 | ecm_control_intf.iInterface = ecm_string_defs[0].id; | ||
721 | ecm_data_intf.iInterface = ecm_string_defs[2].id; | ||
722 | ecm_desc.iMACAddress = ecm_string_defs[1].id; | ||
723 | ecm_iad_descriptor.iFunction = ecm_string_defs[3].id; | ||
724 | } | ||
725 | |||
690 | /* allocate instance-specific interface IDs */ | 726 | /* allocate instance-specific interface IDs */ |
691 | status = usb_interface_id(c, f); | 727 | status = usb_interface_id(c, f); |
692 | if (status < 0) | 728 | if (status < 0) |
@@ -796,8 +832,10 @@ fail: | |||
796 | return status; | 832 | return status; |
797 | } | 833 | } |
798 | 834 | ||
835 | #ifdef USBF_ECM_INCLUDED | ||
836 | |||
799 | static void | 837 | static void |
800 | ecm_unbind(struct usb_configuration *c, struct usb_function *f) | 838 | ecm_old_unbind(struct usb_configuration *c, struct usb_function *f) |
801 | { | 839 | { |
802 | struct f_ecm *ecm = func_to_ecm(f); | 840 | struct f_ecm *ecm = func_to_ecm(f); |
803 | 841 | ||
@@ -834,17 +872,6 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
834 | if (!can_support_ecm(c->cdev->gadget) || !ethaddr) | 872 | if (!can_support_ecm(c->cdev->gadget) || !ethaddr) |
835 | return -EINVAL; | 873 | return -EINVAL; |
836 | 874 | ||
837 | if (ecm_string_defs[0].id == 0) { | ||
838 | status = usb_string_ids_tab(c->cdev, ecm_string_defs); | ||
839 | if (status) | ||
840 | return status; | ||
841 | |||
842 | ecm_control_intf.iInterface = ecm_string_defs[0].id; | ||
843 | ecm_data_intf.iInterface = ecm_string_defs[2].id; | ||
844 | ecm_desc.iMACAddress = ecm_string_defs[1].id; | ||
845 | ecm_iad_descriptor.iFunction = ecm_string_defs[3].id; | ||
846 | } | ||
847 | |||
848 | /* allocate and initialize one new instance */ | 875 | /* allocate and initialize one new instance */ |
849 | ecm = kzalloc(sizeof *ecm, GFP_KERNEL); | 876 | ecm = kzalloc(sizeof *ecm, GFP_KERNEL); |
850 | if (!ecm) | 877 | if (!ecm) |
@@ -861,7 +888,7 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
861 | ecm->port.func.strings = ecm_strings; | 888 | ecm->port.func.strings = ecm_strings; |
862 | /* descriptors are per-instance copies */ | 889 | /* descriptors are per-instance copies */ |
863 | ecm->port.func.bind = ecm_bind; | 890 | ecm->port.func.bind = ecm_bind; |
864 | ecm->port.func.unbind = ecm_unbind; | 891 | ecm->port.func.unbind = ecm_old_unbind; |
865 | ecm->port.func.set_alt = ecm_set_alt; | 892 | ecm->port.func.set_alt = ecm_set_alt; |
866 | ecm->port.func.get_alt = ecm_get_alt; | 893 | ecm->port.func.get_alt = ecm_get_alt; |
867 | ecm->port.func.setup = ecm_setup; | 894 | ecm->port.func.setup = ecm_setup; |
@@ -872,3 +899,99 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
872 | kfree(ecm); | 899 | kfree(ecm); |
873 | return status; | 900 | return status; |
874 | } | 901 | } |
902 | |||
903 | #else | ||
904 | |||
905 | static void ecm_free_inst(struct usb_function_instance *f) | ||
906 | { | ||
907 | struct f_ecm_opts *opts; | ||
908 | |||
909 | opts = container_of(f, struct f_ecm_opts, func_inst); | ||
910 | if (opts->bound) | ||
911 | gether_cleanup(netdev_priv(opts->net)); | ||
912 | else | ||
913 | free_netdev(opts->net); | ||
914 | kfree(opts); | ||
915 | } | ||
916 | |||
917 | static struct usb_function_instance *ecm_alloc_inst(void) | ||
918 | { | ||
919 | struct f_ecm_opts *opts; | ||
920 | |||
921 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | ||
922 | if (!opts) | ||
923 | return ERR_PTR(-ENOMEM); | ||
924 | |||
925 | opts->func_inst.free_func_inst = ecm_free_inst; | ||
926 | opts->net = gether_setup_default(); | ||
927 | if (IS_ERR(opts->net)) | ||
928 | return ERR_PTR(PTR_ERR(opts->net)); | ||
929 | |||
930 | return &opts->func_inst; | ||
931 | } | ||
932 | |||
933 | static void ecm_free(struct usb_function *f) | ||
934 | { | ||
935 | struct f_ecm *ecm; | ||
936 | |||
937 | ecm = func_to_ecm(f); | ||
938 | kfree(ecm); | ||
939 | } | ||
940 | |||
941 | static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) | ||
942 | { | ||
943 | struct f_ecm *ecm = func_to_ecm(f); | ||
944 | |||
945 | DBG(c->cdev, "ecm unbind\n"); | ||
946 | |||
947 | ecm_string_defs[0].id = 0; | ||
948 | usb_free_all_descriptors(f); | ||
949 | |||
950 | kfree(ecm->notify_req->buf); | ||
951 | usb_ep_free_request(ecm->notify, ecm->notify_req); | ||
952 | } | ||
953 | |||
954 | struct usb_function *ecm_alloc(struct usb_function_instance *fi) | ||
955 | { | ||
956 | struct f_ecm *ecm; | ||
957 | struct f_ecm_opts *opts; | ||
958 | int status; | ||
959 | |||
960 | /* allocate and initialize one new instance */ | ||
961 | ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); | ||
962 | if (!ecm) | ||
963 | return ERR_PTR(-ENOMEM); | ||
964 | |||
965 | opts = container_of(fi, struct f_ecm_opts, func_inst); | ||
966 | |||
967 | /* export host's Ethernet address in CDC format */ | ||
968 | status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr, | ||
969 | sizeof(ecm->ethaddr)); | ||
970 | if (status < 12) { | ||
971 | kfree(ecm); | ||
972 | return ERR_PTR(-EINVAL); | ||
973 | } | ||
974 | ecm_string_defs[1].s = ecm->ethaddr; | ||
975 | |||
976 | ecm->port.ioport = netdev_priv(opts->net); | ||
977 | ecm->port.cdc_filter = DEFAULT_FILTER; | ||
978 | |||
979 | ecm->port.func.name = "cdc_ethernet"; | ||
980 | ecm->port.func.strings = ecm_strings; | ||
981 | /* descriptors are per-instance copies */ | ||
982 | ecm->port.func.bind = ecm_bind; | ||
983 | ecm->port.func.unbind = ecm_unbind; | ||
984 | ecm->port.func.set_alt = ecm_set_alt; | ||
985 | ecm->port.func.get_alt = ecm_get_alt; | ||
986 | ecm->port.func.setup = ecm_setup; | ||
987 | ecm->port.func.disable = ecm_disable; | ||
988 | ecm->port.func.free_func = ecm_free; | ||
989 | |||
990 | return &ecm->port.func; | ||
991 | } | ||
992 | |||
993 | DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); | ||
994 | MODULE_LICENSE("GPL"); | ||
995 | MODULE_AUTHOR("David Brownell"); | ||
996 | |||
997 | #endif | ||