summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/f_ecm.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman2013-06-12 23:44:13 +0200
committerGreg Kroah-Hartman2013-06-12 23:44:13 +0200
commit976f8bef9cfb5246bc0e8dc781562daa79cb7aaf (patch)
treed995465d23f10080c3df6f5ca0c4375d59138b39 /drivers/usb/gadget/f_ecm.c
parentUSB: serial: ports: add minor and port number (diff)
parentusb: dwc3: omap: Adds dwc3_omap_readl/writel wrappers (diff)
downloadkernel-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.c196
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