diff options
Diffstat (limited to 'drivers/usb/gadget/f_ecm.c')
-rw-r--r-- | drivers/usb/gadget/f_ecm.c | 197 |
1 files changed, 182 insertions, 15 deletions
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index abf8a31ae146..5d3561ea1c15 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c | |||
@@ -14,10 +14,13 @@ | |||
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_ether_configfs.h" | ||
23 | #include "u_ecm.h" | ||
21 | 24 | ||
22 | 25 | ||
23 | /* | 26 | /* |
@@ -684,9 +687,44 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) | |||
684 | { | 687 | { |
685 | struct usb_composite_dev *cdev = c->cdev; | 688 | struct usb_composite_dev *cdev = c->cdev; |
686 | struct f_ecm *ecm = func_to_ecm(f); | 689 | struct f_ecm *ecm = func_to_ecm(f); |
690 | struct usb_string *us; | ||
687 | int status; | 691 | int status; |
688 | struct usb_ep *ep; | 692 | struct usb_ep *ep; |
689 | 693 | ||
694 | #ifndef USBF_ECM_INCLUDED | ||
695 | struct f_ecm_opts *ecm_opts; | ||
696 | |||
697 | if (!can_support_ecm(cdev->gadget)) | ||
698 | return -EINVAL; | ||
699 | |||
700 | ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); | ||
701 | |||
702 | /* | ||
703 | * in drivers/usb/gadget/configfs.c:configfs_composite_bind() | ||
704 | * configurations are bound in sequence with list_for_each_entry, | ||
705 | * in each configuration its functions are bound in sequence | ||
706 | * with list_for_each_entry, so we assume no race condition | ||
707 | * with regard to ecm_opts->bound access | ||
708 | */ | ||
709 | if (!ecm_opts->bound) { | ||
710 | mutex_lock(&ecm_opts->lock); | ||
711 | gether_set_gadget(ecm_opts->net, cdev->gadget); | ||
712 | status = gether_register_netdev(ecm_opts->net); | ||
713 | mutex_unlock(&ecm_opts->lock); | ||
714 | if (status) | ||
715 | return status; | ||
716 | ecm_opts->bound = true; | ||
717 | } | ||
718 | #endif | ||
719 | us = usb_gstrings_attach(cdev, ecm_strings, | ||
720 | ARRAY_SIZE(ecm_string_defs)); | ||
721 | if (IS_ERR(us)) | ||
722 | return PTR_ERR(us); | ||
723 | ecm_control_intf.iInterface = us[0].id; | ||
724 | ecm_data_intf.iInterface = us[2].id; | ||
725 | ecm_desc.iMACAddress = us[1].id; | ||
726 | ecm_iad_descriptor.iFunction = us[3].id; | ||
727 | |||
690 | /* allocate instance-specific interface IDs */ | 728 | /* allocate instance-specific interface IDs */ |
691 | status = usb_interface_id(c, f); | 729 | status = usb_interface_id(c, f); |
692 | if (status < 0) | 730 | if (status < 0) |
@@ -796,14 +834,15 @@ fail: | |||
796 | return status; | 834 | return status; |
797 | } | 835 | } |
798 | 836 | ||
837 | #ifdef USBF_ECM_INCLUDED | ||
838 | |||
799 | static void | 839 | static void |
800 | ecm_unbind(struct usb_configuration *c, struct usb_function *f) | 840 | ecm_old_unbind(struct usb_configuration *c, struct usb_function *f) |
801 | { | 841 | { |
802 | struct f_ecm *ecm = func_to_ecm(f); | 842 | struct f_ecm *ecm = func_to_ecm(f); |
803 | 843 | ||
804 | DBG(c->cdev, "ecm unbind\n"); | 844 | DBG(c->cdev, "ecm unbind\n"); |
805 | 845 | ||
806 | ecm_string_defs[0].id = 0; | ||
807 | usb_free_all_descriptors(f); | 846 | usb_free_all_descriptors(f); |
808 | 847 | ||
809 | kfree(ecm->notify_req->buf); | 848 | kfree(ecm->notify_req->buf); |
@@ -834,17 +873,6 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
834 | if (!can_support_ecm(c->cdev->gadget) || !ethaddr) | 873 | if (!can_support_ecm(c->cdev->gadget) || !ethaddr) |
835 | return -EINVAL; | 874 | return -EINVAL; |
836 | 875 | ||
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 */ | 876 | /* allocate and initialize one new instance */ |
849 | ecm = kzalloc(sizeof *ecm, GFP_KERNEL); | 877 | ecm = kzalloc(sizeof *ecm, GFP_KERNEL); |
850 | if (!ecm) | 878 | if (!ecm) |
@@ -858,10 +886,9 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], | |||
858 | ecm->port.cdc_filter = DEFAULT_FILTER; | 886 | ecm->port.cdc_filter = DEFAULT_FILTER; |
859 | 887 | ||
860 | ecm->port.func.name = "cdc_ethernet"; | 888 | ecm->port.func.name = "cdc_ethernet"; |
861 | 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,143 @@ 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 inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) | ||
906 | { | ||
907 | return container_of(to_config_group(item), struct f_ecm_opts, | ||
908 | func_inst.group); | ||
909 | } | ||
910 | |||
911 | /* f_ecm_item_ops */ | ||
912 | USB_ETHERNET_CONFIGFS_ITEM(ecm); | ||
913 | |||
914 | /* f_ecm_opts_dev_addr */ | ||
915 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm); | ||
916 | |||
917 | /* f_ecm_opts_host_addr */ | ||
918 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm); | ||
919 | |||
920 | /* f_ecm_opts_qmult */ | ||
921 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm); | ||
922 | |||
923 | /* f_ecm_opts_ifname */ | ||
924 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm); | ||
925 | |||
926 | static struct configfs_attribute *ecm_attrs[] = { | ||
927 | &f_ecm_opts_dev_addr.attr, | ||
928 | &f_ecm_opts_host_addr.attr, | ||
929 | &f_ecm_opts_qmult.attr, | ||
930 | &f_ecm_opts_ifname.attr, | ||
931 | NULL, | ||
932 | }; | ||
933 | |||
934 | static struct config_item_type ecm_func_type = { | ||
935 | .ct_item_ops = &ecm_item_ops, | ||
936 | .ct_attrs = ecm_attrs, | ||
937 | .ct_owner = THIS_MODULE, | ||
938 | }; | ||
939 | |||
940 | static void ecm_free_inst(struct usb_function_instance *f) | ||
941 | { | ||
942 | struct f_ecm_opts *opts; | ||
943 | |||
944 | opts = container_of(f, struct f_ecm_opts, func_inst); | ||
945 | if (opts->bound) | ||
946 | gether_cleanup(netdev_priv(opts->net)); | ||
947 | else | ||
948 | free_netdev(opts->net); | ||
949 | kfree(opts); | ||
950 | } | ||
951 | |||
952 | static struct usb_function_instance *ecm_alloc_inst(void) | ||
953 | { | ||
954 | struct f_ecm_opts *opts; | ||
955 | |||
956 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | ||
957 | if (!opts) | ||
958 | return ERR_PTR(-ENOMEM); | ||
959 | mutex_init(&opts->lock); | ||
960 | opts->func_inst.free_func_inst = ecm_free_inst; | ||
961 | opts->net = gether_setup_default(); | ||
962 | if (IS_ERR(opts->net)) | ||
963 | return ERR_PTR(PTR_ERR(opts->net)); | ||
964 | |||
965 | config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type); | ||
966 | |||
967 | return &opts->func_inst; | ||
968 | } | ||
969 | |||
970 | static void ecm_free(struct usb_function *f) | ||
971 | { | ||
972 | struct f_ecm *ecm; | ||
973 | struct f_ecm_opts *opts; | ||
974 | |||
975 | ecm = func_to_ecm(f); | ||
976 | opts = container_of(f->fi, struct f_ecm_opts, func_inst); | ||
977 | kfree(ecm); | ||
978 | mutex_lock(&opts->lock); | ||
979 | opts->refcnt--; | ||
980 | mutex_unlock(&opts->lock); | ||
981 | } | ||
982 | |||
983 | static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) | ||
984 | { | ||
985 | struct f_ecm *ecm = func_to_ecm(f); | ||
986 | |||
987 | DBG(c->cdev, "ecm unbind\n"); | ||
988 | |||
989 | usb_free_all_descriptors(f); | ||
990 | |||
991 | kfree(ecm->notify_req->buf); | ||
992 | usb_ep_free_request(ecm->notify, ecm->notify_req); | ||
993 | } | ||
994 | |||
995 | struct usb_function *ecm_alloc(struct usb_function_instance *fi) | ||
996 | { | ||
997 | struct f_ecm *ecm; | ||
998 | struct f_ecm_opts *opts; | ||
999 | int status; | ||
1000 | |||
1001 | /* allocate and initialize one new instance */ | ||
1002 | ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); | ||
1003 | if (!ecm) | ||
1004 | return ERR_PTR(-ENOMEM); | ||
1005 | |||
1006 | opts = container_of(fi, struct f_ecm_opts, func_inst); | ||
1007 | mutex_lock(&opts->lock); | ||
1008 | opts->refcnt++; | ||
1009 | |||
1010 | /* export host's Ethernet address in CDC format */ | ||
1011 | status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr, | ||
1012 | sizeof(ecm->ethaddr)); | ||
1013 | if (status < 12) { | ||
1014 | kfree(ecm); | ||
1015 | mutex_unlock(&opts->lock); | ||
1016 | return ERR_PTR(-EINVAL); | ||
1017 | } | ||
1018 | ecm_string_defs[1].s = ecm->ethaddr; | ||
1019 | |||
1020 | ecm->port.ioport = netdev_priv(opts->net); | ||
1021 | mutex_unlock(&opts->lock); | ||
1022 | ecm->port.cdc_filter = DEFAULT_FILTER; | ||
1023 | |||
1024 | ecm->port.func.name = "cdc_ethernet"; | ||
1025 | /* descriptors are per-instance copies */ | ||
1026 | ecm->port.func.bind = ecm_bind; | ||
1027 | ecm->port.func.unbind = ecm_unbind; | ||
1028 | ecm->port.func.set_alt = ecm_set_alt; | ||
1029 | ecm->port.func.get_alt = ecm_get_alt; | ||
1030 | ecm->port.func.setup = ecm_setup; | ||
1031 | ecm->port.func.disable = ecm_disable; | ||
1032 | ecm->port.func.free_func = ecm_free; | ||
1033 | |||
1034 | return &ecm->port.func; | ||
1035 | } | ||
1036 | |||
1037 | DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); | ||
1038 | MODULE_LICENSE("GPL"); | ||
1039 | MODULE_AUTHOR("David Brownell"); | ||
1040 | |||
1041 | #endif | ||