diff options
author | Greg Kroah-Hartman | 2013-06-12 23:44:13 +0200 |
---|---|---|
committer | Greg Kroah-Hartman | 2013-06-12 23:44:13 +0200 |
commit | 976f8bef9cfb5246bc0e8dc781562daa79cb7aaf (patch) | |
tree | d995465d23f10080c3df6f5ca0c4375d59138b39 /drivers/usb/gadget/f_ecm.c | |
parent | USB: serial: ports: add minor and port number (diff) | |
parent | usb: dwc3: omap: Adds dwc3_omap_readl/writel wrappers (diff) | |
download | kernel-qcow2-linux-976f8bef9cfb5246bc0e8dc781562daa79cb7aaf.tar.gz kernel-qcow2-linux-976f8bef9cfb5246bc0e8dc781562daa79cb7aaf.tar.xz kernel-qcow2-linux-976f8bef9cfb5246bc0e8dc781562daa79cb7aaf.zip |
Merge tag 'usb-for-v3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes:
usb: patches for v3.11 merge window
All function drivers are now converted to our new configfs-based
binding. Eventually this will help us getting rid of in-kernel
gadget drivers and only keep function drivers in the kernel.
MUSB was taught that it needs to be built for host-only and
device-only modes too. We had this support long ago but it
involved a ridiculous amount of ifdefs. Now we have a much
cleaner approach.
Samsung Exynos4 platform now implements HSIC support.
We're introducing support for AB8540 and AB9540 PHYs.
MUSB module reinsertion now works as expected, before we were
getting -EBUSY being returned by the resource checks done on
driver core.
DWC3 now has minimum support for TI's AM437x series of SoCs.
OMAP5 USB3 PHY learned one extra DPLL configuration values because
that PHY is reused in TI's DRA7xx devices.
We're introducing support for Faraday fotg210 UDCs.
Last, but not least, the usual set of non-critical fixes and cleanups
ranging from usage of platform_{get,set}_drvdata to lock improvements.
Signed-of-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget/f_ecm.c')
-rw-r--r-- | drivers/usb/gadget/f_ecm.c | 196 |
1 files changed, 181 insertions, 15 deletions
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index abf8a31ae146..fcafe1af4585 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -14,10 +14,13 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> #include "u_ether.h" +#include "u_ether_configfs.h" +#include "u_ecm.h" /* @@ -684,9 +687,44 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_ecm *ecm = func_to_ecm(f); + struct usb_string *us; int status; struct usb_ep *ep; +#ifndef USBF_ECM_INCLUDED + struct f_ecm_opts *ecm_opts; + + if (!can_support_ecm(cdev->gadget)) + return -EINVAL; + + ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to ecm_opts->bound access + */ + if (!ecm_opts->bound) { + mutex_lock(&ecm_opts->lock); + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + mutex_unlock(&ecm_opts->lock); + if (status) + return status; + ecm_opts->bound = true; + } +#endif + us = usb_gstrings_attach(cdev, ecm_strings, + ARRAY_SIZE(ecm_string_defs)); + if (IS_ERR(us)) + return PTR_ERR(us); + ecm_control_intf.iInterface = us[0].id; + ecm_data_intf.iInterface = us[2].id; + ecm_desc.iMACAddress = us[1].id; + ecm_iad_descriptor.iFunction = us[3].id; + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -796,14 +834,15 @@ fail: return status; } +#ifdef USBF_ECM_INCLUDED + static void -ecm_unbind(struct usb_configuration *c, struct usb_function *f) +ecm_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ecm *ecm = func_to_ecm(f); DBG(c->cdev, "ecm unbind\n"); - ecm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ecm->notify_req->buf); @@ -834,17 +873,6 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], if (!can_support_ecm(c->cdev->gadget) || !ethaddr) return -EINVAL; - if (ecm_string_defs[0].id == 0) { - status = usb_string_ids_tab(c->cdev, ecm_string_defs); - if (status) - return status; - - ecm_control_intf.iInterface = ecm_string_defs[0].id; - ecm_data_intf.iInterface = ecm_string_defs[2].id; - ecm_desc.iMACAddress = ecm_string_defs[1].id; - ecm_iad_descriptor.iFunction = ecm_string_defs[3].id; - } - /* allocate and initialize one new instance */ ecm = kzalloc(sizeof *ecm, GFP_KERNEL); if (!ecm) @@ -858,10 +886,9 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ecm->port.cdc_filter = DEFAULT_FILTER; ecm->port.func.name = "cdc_ethernet"; - ecm->port.func.strings = ecm_strings; /* descriptors are per-instance copies */ ecm->port.func.bind = ecm_bind; - ecm->port.func.unbind = ecm_unbind; + ecm->port.func.unbind = ecm_old_unbind; ecm->port.func.set_alt = ecm_set_alt; ecm->port.func.get_alt = ecm_get_alt; ecm->port.func.setup = ecm_setup; @@ -872,3 +899,142 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], kfree(ecm); return status; } + +#else + +static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ecm_opts, + func_inst.group); +} + +/* f_ecm_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(ecm); + +/* f_ecm_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm); + +/* f_ecm_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm); + +/* f_ecm_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm); + +/* f_ecm_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm); + +static struct configfs_attribute *ecm_attrs[] = { + &f_ecm_opts_dev_addr.attr, + &f_ecm_opts_host_addr.attr, + &f_ecm_opts_qmult.attr, + &f_ecm_opts_ifname.attr, + NULL, +}; + +static struct config_item_type ecm_func_type = { + .ct_item_ops = &ecm_item_ops, + .ct_attrs = ecm_attrs, + .ct_owner = THIS_MODULE, +}; + +static void ecm_free_inst(struct usb_function_instance *f) +{ + struct f_ecm_opts *opts; + + opts = container_of(f, struct f_ecm_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + kfree(opts); +} + +static struct usb_function_instance *ecm_alloc_inst(void) +{ + struct f_ecm_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = ecm_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_PTR(PTR_ERR(opts->net)); + + config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type); + + return &opts->func_inst; +} + +static void ecm_free(struct usb_function *f) +{ + struct f_ecm *ecm; + struct f_ecm_opts *opts; + + ecm = func_to_ecm(f); + opts = container_of(f->fi, struct f_ecm_opts, func_inst); + kfree(ecm); + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); +} + +static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_ecm *ecm = func_to_ecm(f); + + DBG(c->cdev, "ecm unbind\n"); + + usb_free_all_descriptors(f); + + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); +} + +struct usb_function *ecm_alloc(struct usb_function_instance *fi) +{ + struct f_ecm *ecm; + struct f_ecm_opts *opts; + int status; + + /* allocate and initialize one new instance */ + ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); + if (!ecm) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_ecm_opts, func_inst); + mutex_lock(&opts->lock); + opts->refcnt++; + + /* export host's Ethernet address in CDC format */ + status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr, + sizeof(ecm->ethaddr)); + if (status < 12) { + kfree(ecm); + return ERR_PTR(-EINVAL); + } + ecm_string_defs[1].s = ecm->ethaddr; + + ecm->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); + ecm->port.cdc_filter = DEFAULT_FILTER; + + ecm->port.func.name = "cdc_ethernet"; + /* descriptors are per-instance copies */ + ecm->port.func.bind = ecm_bind; + ecm->port.func.unbind = ecm_unbind; + ecm->port.func.set_alt = ecm_set_alt; + ecm->port.func.get_alt = ecm_get_alt; + ecm->port.func.setup = ecm_setup; + ecm->port.func.disable = ecm_disable; + ecm->port.func.free_func = ecm_free; + + return &ecm->port.func; +} + +DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); + +#endif |