diff options
| author | wzedlare <vedatak01@gmail.com> | 2017-06-18 16:38:26 +0000 |
|---|---|---|
| committer | wzedlare <vedatak01@gmail.com> | 2017-06-19 16:57:11 +0000 |
| commit | c7d4e3fd588e3ba3d3fa4d5cfa224aa54bc288bf (patch) | |
| tree | b8b64cb9deb6832c1e41f58f0f143514beafc709 /drivers/usb/gadget | |
| parent | 28c99c87b881bb664c44bb26e80a681f87d54e60 (diff) | |
Change-Id: Ia4c94f09e29843b1af34d466243378a357e97b70
Diffstat (limited to 'drivers/usb/gadget')
21 files changed, 1174 insertions, 29 deletions
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index e5d8899b..efbfcfdc 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -25,6 +25,7 @@ #include <linux/utsname.h> #include <linux/platform_device.h> #include <linux/pm_qos.h> +#include <linux/reboot.h> #include <linux/of.h> #include <linux/usb/ch9.h> @@ -69,6 +70,7 @@ #include "u_qc_ether.c" #include "f_gsi.c" #include "f_mass_storage.h" +#include "f_usbnet.c" USB_ETHERNET_MODULE_PARAMETERS(); #include "debug.h" @@ -213,6 +215,8 @@ struct android_dev { /* A list node inside the android_dev_list */ struct list_head list_item; + /* reboot notifier */ + struct notifier_block android_reboot; }; struct android_configuration { @@ -2609,6 +2613,11 @@ static int mass_storage_function_init(struct android_usb_function *f, } fsg_mod_data.removable[0] = true; + +//lenovo sw yexh1, add for usb cdrom feature + fsg_mod_data.cdrom[0] = true; +//lenovo sw yexh1, end + fsg_config_from_params(&m_config, &fsg_mod_data, fsg_num_buffers); fsg_opts = fsg_opts_from_func_inst(config->f_ms_inst); ret = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers); @@ -3042,6 +3051,89 @@ static struct android_usb_function dpl_gsi_function = { .bind_config = dpl_gsi_function_bind_config, }; +static int usbnet_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + struct usbnet_device *dev; + struct usbnet_context *context; + struct net_device *net_dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + net_dev = alloc_netdev(sizeof(struct usbnet_context), + "usb%d", NET_NAME_UNKNOWN, usb_ether_setup); + if (!net_dev) { + pr_err("%s: alloc_netdev error\n", __func__); + return -EINVAL; + } + + ret = register_netdev(net_dev); + if (ret) { + pr_err("%s: register_netdev error\n", __func__); + free_netdev(net_dev); + return -EINVAL; + } + + ret = device_create_file(&net_dev->dev, &dev_attr_description); + if (ret < 0) { + pr_err("%s: sys file creation error\n", __func__); + unregister_netdev(net_dev); + free_netdev(net_dev); + return -EINVAL; + } + + context = netdev_priv(net_dev); + INIT_WORK(&context->usbnet_config_wq, usbnet_if_config); + + context->config = 0; + dev->net_ctxt = context; + + f->config = dev; + +#ifdef CONFIG_SWITCH + switch_dev_register(&usbnet_enable_device); +#endif + return 0; +} + +static void usbnet_function_cleanup(struct android_usb_function *f) +{ + struct usbnet_device *dev = f->config; + + usbnet_cleanup(dev); +#ifdef CONFIG_SWITCH + switch_dev_unregister(&usbnet_enable_device); +#endif +} + +static int usbnet_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct usbnet_device *dev = f->config; + + return usbnet_bind_config(dev, c); +} + +static int usbnet_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + struct usbnet_device *dev = f->config; + + return usbnet_ctrlrequest(dev, cdev, c); +} + +static struct android_usb_function usbnet_function = { + .name = "usbnet", + .init = usbnet_function_init, + .cleanup = usbnet_function_cleanup, + .bind_config = usbnet_function_bind_config, + .ctrlrequest = usbnet_function_ctrlrequest, +}; + static struct android_usb_function *supported_functions[] = { [ANDROID_FFS] = &ffs_function, [ANDROID_MBIM_BAM] = &mbim_function, @@ -3104,6 +3196,7 @@ static struct android_usb_function *default_functions[] = { #ifdef CONFIG_SND_RAWMIDI &midi_function, #endif + &usbnet_function, NULL }; @@ -3419,6 +3512,10 @@ functions_store(struct device *pdev, struct device_attribute *attr, strlcpy(buf, buff, sizeof(buf)); b = strim(buf); +//lenovo sw yexh1 add for print usb composite setting + pr_info("usb: %s\n", buff); +//lenovo sw yexh1 add end + while (b) { conf_str = strsep(&b, ":"); if (!conf_str) @@ -4070,6 +4167,52 @@ static int usb_diag_update_pid_and_serial_num(u32 pid, const char *snum) return 0; } +/* chenyb1 add factory feature begin*/ +static bool is_mmi_factory(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool fact_cable = false; + + if (np) + fact_cable = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + return fact_cable; +} + +static void configure_mmi_factory(struct platform_device *pdev, + struct android_usb_platform_data *pdata) +{ + int prop_len = 0; + if (is_mmi_factory()) { + of_get_property(pdev->dev.of_node, + "mmi,pm-qos-latency-factory", + &prop_len); + if (prop_len == sizeof(pdata->pm_qos_latency)) { + pr_info("Overwrite pm_qos latency with factory mode\n"); + of_property_read_u32_array(pdev->dev.of_node, + "mmi,pm-qos-latency-factory", + pdata->pm_qos_latency, + prop_len/sizeof(*pdata->pm_qos_latency)); + } else { + pr_info("pm_qos latency for factory not specified\n"); + } + } +} + +static int android_reboot_notifier(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct android_dev *dev = + container_of(nb, struct android_dev, android_reboot); + pr_err("Android reboot - de-enumerate\n"); + if (event == SYS_POWER_OFF) + usb_gadget_disconnect(dev->cdev->gadget); + return NOTIFY_DONE; +} +/* chenyb1 add factory feature end*/ + static int android_probe(struct platform_device *pdev) { struct android_usb_platform_data *pdata; @@ -4097,6 +4240,10 @@ static int android_probe(struct platform_device *pdev) pr_info("pm_qos latency not specified %d\n", prop_len); } +/* chenyb1 add factory feature begin*/ + configure_mmi_factory(pdev, pdata); +/* chenyb1 add factory feature end*/ + ret = of_property_read_u32(pdev->dev.of_node, "qcom,usb-core-id", &usb_core_id); @@ -4220,6 +4367,15 @@ static int android_probe(struct platform_device *pdev) } strlcpy(android_dev->pm_qos, "high", sizeof(android_dev->pm_qos)); + if (is_mmi_factory()) { + android_dev->android_reboot.notifier_call = + android_reboot_notifier; + android_dev->android_reboot.next = NULL; + android_dev->android_reboot.priority = 2; + ret = register_reboot_notifier(&android_dev->android_reboot); + if (ret) + dev_err(&pdev->dev, "register for reboot failed\n"); + } return ret; err_probe: android_destroy_device(android_dev); diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 25b421f9..829f0977 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -3427,21 +3427,28 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) gadget_ready = 1; spin_unlock_irqrestore(udc->lock, flags); - if (gadget_ready) { - if (is_active) { - hw_device_reset(udc); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, - CI13XXX_CONTROLLER_CONNECT_EVENT); - if (udc->softconnect) - hw_device_state(udc->ep0out.qh.dma); - } else { - hw_device_state(0); - _gadget_stop_activity(&udc->gadget); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, - CI13XXX_CONTROLLER_DISCONNECT_EVENT); + if (!gadget_ready) + return 0; + + if (is_active) { + hw_device_reset(udc); + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_CONNECT_EVENT); + /* Enable BAM (if needed) before starting controller */ + if (udc->softconnect) { + dbg_event(0xFF, "BAM EN2", + _gadget->bam2bam_func_enabled); + msm_usb_bam_enable(CI_CTRL, + _gadget->bam2bam_func_enabled); + hw_device_state(udc->ep0out.qh.dma); } + } else { + hw_device_state(0); + _gadget_stop_activity(&udc->gadget); + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_DISCONNECT_EVENT); } return 0; @@ -3488,6 +3495,12 @@ static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_active) pm_runtime_get_sync(&_gadget->dev); + /* Enable BAM (if needed) before starting controller */ + if (is_active) { + dbg_event(0xFF, "BAM EN1", _gadget->bam2bam_func_enabled); + msm_usb_bam_enable(CI_CTRL, _gadget->bam2bam_func_enabled); + } + spin_lock_irqsave(udc->lock, flags); if (!udc->vbus_active) { spin_unlock_irqrestore(udc->lock, flags); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 777e634a..006f1ea4 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1673,7 +1673,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (!gadget_is_dualspeed(gadget) || gadget->speed >= USB_SPEED_SUPER) break; + spin_lock(&cdev->lock); device_qual(cdev); + spin_unlock(&cdev->lock); value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor)); break; @@ -1683,7 +1685,9 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; /* FALLTHROUGH */ case USB_DT_CONFIG: + spin_lock(&cdev->lock); value = config_desc(cdev, w_value); + spin_unlock(&cdev->lock); if (value >= 0) value = min(w_length, (u16) value); break; diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index cc428c4d..aa6400d3 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -46,5 +46,7 @@ usb_f_audio_source-y := f_audio_source.o obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o usb_f_accessory-y := f_accessory.o obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o +usb_f_usbnet-y := f_usbnet.o +obj-$(CONFIG_USB_ANDROID_USBNET) += usb_f_usbnet.o diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 19f945b5..00a73f23 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -448,12 +448,19 @@ static void acc_hid_close(struct hid_device *hid) { } +static int acc_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, int reqtype) +{ + return 0; +} + static struct hid_ll_driver acc_hid_ll_driver = { .parse = acc_hid_parse, .start = acc_hid_start, .stop = acc_hid_stop, .open = acc_hid_open, .close = acc_hid_close, + .raw_request = acc_hid_raw_request, }; static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c index 3b57ad9c..4aaeb1e5 100644 --- a/drivers/usb/gadget/function/f_diag.c +++ b/drivers/usb/gadget/function/f_diag.c @@ -27,6 +27,13 @@ #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/kmemleak.h> +#ifdef CONFIG_DIAG_OVER_TTY +#include <linux/usb/tty_diag.h> +#include <linux/of.h> + +static bool tty_mode; +module_param(tty_mode, bool, S_IRUGO); +#endif static DEFINE_SPINLOCK(ch_lock); static LIST_HEAD(usb_diag_ch_list); @@ -174,6 +181,23 @@ static inline struct diag_context *func_to_diag(struct usb_function *f) return container_of(f, struct diag_context, function); } +#ifdef CONFIG_DIAG_OVER_TTY +static bool factory_cable_present(void) +{ + struct device_node *np; + bool fact_cable; + + np = of_find_node_by_path("/chosen"); + fact_cable = of_property_read_bool(np, "mmi,factory-cable"); + of_node_put(np); + + if (!np || !fact_cable) + return false; + + return true; +} +#endif + /* Called with ctxt->lock held; i.e. only use with kref_put_spinlock_irqsave */ static void diag_context_release(struct kref *kref) { @@ -302,6 +326,17 @@ struct usb_diag_ch *usb_diag_open(const char *name, void *priv, unsigned long flags; int found = 0; +#ifdef CONFIG_DIAG_OVER_TTY + static bool init; + + if (!init) { + init = true; + tty_mode = factory_cable_present(); + } + + if (tty_mode) + return tty_diag_channel_open(name, priv, notify); +#endif spin_lock_irqsave(&ch_lock, flags); /* Check if we already have a channel with this name */ list_for_each_entry(ch, &usb_diag_ch_list, list) { @@ -342,6 +377,10 @@ void usb_diag_close(struct usb_diag_ch *ch) struct diag_context *dev = NULL; unsigned long flags; +#ifdef CONFIG_DIAG_OVER_TTY + if (tty_mode) + return tty_diag_channel_close(ch); +#endif spin_lock_irqsave(&ch_lock, flags); ch->priv = NULL; ch->notify = NULL; @@ -392,6 +431,10 @@ int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read) int i; unsigned long flags; +#ifdef CONFIG_DIAG_OVER_TTY + if (tty_mode) + return 0; +#endif if (!ctxt) return -ENODEV; @@ -437,7 +480,15 @@ EXPORT_SYMBOL(usb_diag_alloc_req); int usb_diag_request_size(struct usb_diag_ch *ch) { struct diag_context *ctxt = ch->priv_usb; - struct usb_composite_dev *cdev = ctxt->cdev; + struct usb_composite_dev *cdev = NULL; + + /* MOT: qcom code need to check if the pointer is null + anyway, we use the default size for tty mode + */ + if (ctxt != NULL && ctxt->cdev != NULL) + cdev = ctxt->cdev; + else + return CI_MAX_REQUEST_SIZE; if (gadget_is_dwc3(cdev->gadget)) return DWC3_MAX_REQUEST_SIZE; @@ -467,6 +518,10 @@ int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req) struct usb_ep *out; static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1); +#ifdef CONFIG_DIAG_OVER_TTY + if (tty_mode) + return tty_diag_channel_read(ch, d_req); +#endif if (!ctxt) return -ENODEV; @@ -544,6 +599,11 @@ int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req) struct usb_ep *in; static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1); +#ifdef CONFIG_DIAG_OVER_TTY + if (tty_mode) + return tty_diag_channel_write(ch, d_req); +#endif + if (!ctxt) return -ENODEV; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index e389c27d..38aca41e 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -27,6 +27,7 @@ #include <linux/usb/composite.h> #include <linux/usb/functionfs.h> +#include <linux/freezer.h> #include <linux/aio.h> #include <linux/mmu_context.h> @@ -689,7 +690,6 @@ static void ffs_user_copy_worker(struct work_struct *work) usb_ep_free_request(io_data->ep, io_data->req); - io_data->kiocb->private = NULL; if (io_data->read) kfree(io_data->iovec); kfree(io_data->buf); @@ -748,7 +748,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) * and wait for next epfile open to happen */ if (!atomic_read(&epfile->error)) { - ret = wait_event_interruptible(epfile->wait, + ret = wait_event_freezable(epfile->wait, (ep = epfile->ep)); if (ret < 0) goto error; diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 380f62f5..09014379 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -18,7 +18,7 @@ #include <linux/ipa_usb.h> #include "f_gsi.h" #include "rndis.h" -#include "debug.h" +#include "../debug.h" static unsigned int gsi_in_aggr_size; module_param(gsi_in_aggr_size, uint, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index a3d9831b..587114c1 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -32,7 +32,7 @@ #include <linux/usb/audio.h> #include <linux/usb/midi.h> -#include "u_f.h" +#include "../u_f.h" MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index a11fdcd8..7bc3378a 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -40,7 +40,7 @@ #include <linux/configfs.h> #include <linux/usb/composite.h> -#include "configfs.h" +#include "../configfs.h" #define MTP_RX_BUFFER_INIT_SIZE 1048576 #define MTP_BULK_BUFFER_SIZE 16384 diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index 69d48b3e..b7446275 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -1084,6 +1084,7 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->hs_descriptors); usb_free_descriptors(f->fs_descriptors); + c->cdev->gadget->bam2bam_func_enabled = false; kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); @@ -1304,6 +1305,7 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], rndis_ipa_cleanup(rndis_ipa_params.private); goto fail; } + c->cdev->gadget->bam2bam_func_enabled = true; return 0; diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 9b0f4e4a..47655332 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -487,7 +487,10 @@ static void qdss_unbind(struct usb_configuration *c, struct usb_function *f) pr_debug("qdss_unbind\n"); flush_workqueue(qdss->wq); + ipa_data_flush_workqueue(); + + c->cdev->gadget->bam2bam_func_enabled = false; clear_eps(f); clear_desc(gadget, f); @@ -1040,6 +1043,7 @@ static int qdss_bind_config(struct usb_configuration *c, unsigned char portno) kfree(name); kfree(qdss); } + c->cdev->gadget->bam2bam_func_enabled = true; return status; } diff --git a/drivers/usb/gadget/function/f_rmnet.c b/drivers/usb/gadget/function/f_rmnet.c index df153571..3a26cc2b 100644 --- a/drivers/usb/gadget/function/f_rmnet.c +++ b/drivers/usb/gadget/function/f_rmnet.c @@ -553,6 +553,7 @@ static int gport_rmnet_disconnect(struct f_rmnet *dev) static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rmnet *dev = func_to_rmnet(f); + enum transport_type dxport = rmnet_ports[dev->port_num].data_xport; pr_debug("%s: portno:%d\n", __func__, dev->port_num); if (gadget_is_superspeed(c->cdev->gadget)) @@ -562,7 +563,10 @@ static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->fs_descriptors); frmnet_free_req(dev->notify, dev->notify_req); - + if (dxport == USB_GADGET_XPORT_BAM2BAM_IPA) { + gbam_data_flush_workqueue(); + c->cdev->gadget->bam2bam_func_enabled = false; + } kfree(f->name); } @@ -1318,6 +1322,9 @@ static int frmnet_bind_config(struct usb_configuration *c, unsigned portno) kfree(f->name); return status; } + if (rmnet_ports[portno].data_xport == + USB_GADGET_XPORT_BAM2BAM_IPA) + c->cdev->gadget->bam2bam_func_enabled = true; pr_debug("%s: complete\n", __func__); diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 8798a468..1d89e304 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -27,7 +27,7 @@ #include "u_ether_configfs.h" #include "u_rndis.h" #include "rndis.h" -#include "configfs.h" +#include "../configfs.h" /* * This function is an RNDIS Ethernet port -- a Microsoft protocol that's diff --git a/drivers/usb/gadget/function/f_usbnet.c b/drivers/usb/gadget/function/f_usbnet.c new file mode 100644 index 00000000..2b521fd6 --- /dev/null +++ b/drivers/usb/gadget/function/f_usbnet.c @@ -0,0 +1,881 @@ +/* + * Gadget Driver for Motorola USBNet + * + * Copyright (C) 2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/device.h> +#include <linux/fcntl.h> +#include <linux/spinlock.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> +#include <linux/if.h> +#include <linux/inetdevice.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> + +#include <linux/usb/ch9.h> +#include <asm/cacheflush.h> +#include <linux/switch.h> + + +/* + * Macro Defines + */ + +#define EP0_BUFSIZE 256 + + +/* Vendor Request to config IP */ +#define USBNET_SET_IP_ADDRESS 0x05 +#define USBNET_SET_SUBNET_MASK 0x06 +#define USBNET_SET_HOST_IP 0x07 + +/* Linux Network Interface */ +#define USB_MTU 1536 +#define MAX_BULK_TX_REQ_NUM 8 +#define MAX_BULK_RX_REQ_NUM 8 +#define MAX_INTR_RX_REQ_NUM 8 +#define STRING_INTERFACE 0 + +struct usbnet_context { + spinlock_t lock; /* For RX/TX list */ + struct net_device *dev; + + struct usb_gadget *gadget; + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; + struct usb_ep *intr_out; + u16 config; /* current USB config w_value */ + + struct list_head rx_reqs; + struct list_head tx_reqs; + + struct net_device_stats stats; + struct work_struct usbnet_config_wq; + u32 ip_addr; + u32 subnet_mask; + u32 router_ip; + u32 iff_flag; +}; + + +struct usbnet_device { + struct usb_function function; + struct usb_composite_dev *cdev; + struct usbnet_context *net_ctxt; +}; + +/* static strings, in UTF-8 */ +static struct usb_string usbnet_string_defs[] = { + [STRING_INTERFACE].s = "Motorola Test Command", + { /* ZEROES END LIST */ }, +}; + +static struct usb_gadget_strings usbnet_string_table = { + .language = 0x0409, /* en-us */ + .strings = usbnet_string_defs, +}; + +static struct usb_gadget_strings *usbnet_strings[] = { + &usbnet_string_table, + NULL, +}; + + + +/* There is only one interface. */ + +static struct usb_interface_descriptor usbnet_intf_desc = { + .bLength = sizeof(usbnet_intf_desc), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 3, + .bInterfaceClass = 0x02, + .bInterfaceSubClass = 0x0a, + .bInterfaceProtocol = 0x01, +}; + + +static struct usb_endpoint_descriptor usbnet_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor usbnet_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor intr_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(64), + .bInterval = 1, +}; + +static struct usb_descriptor_header *fs_function[] = { + (struct usb_descriptor_header *) &usbnet_intf_desc, + (struct usb_descriptor_header *) &usbnet_fs_bulk_in_desc, + (struct usb_descriptor_header *) &usbnet_fs_bulk_out_desc, + (struct usb_descriptor_header *) &intr_out_desc, + NULL, +}; + +static struct usb_endpoint_descriptor usbnet_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), + .bInterval = 0, +}; + +static struct usb_endpoint_descriptor usbnet_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), + .bInterval = 0, +}; + +static struct usb_descriptor_header *hs_function[] = { + (struct usb_descriptor_header *) &usbnet_intf_desc, + (struct usb_descriptor_header *) &usbnet_hs_bulk_in_desc, + (struct usb_descriptor_header *) &usbnet_hs_bulk_out_desc, + (struct usb_descriptor_header *) &intr_out_desc, + NULL, +}; + +static struct usb_endpoint_descriptor usbnet_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_ss_ep_comp_descriptor usbnet_ss_bulk_in_comp_desc = { + .bLength = sizeof usbnet_ss_bulk_in_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_endpoint_descriptor usbnet_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_ss_ep_comp_descriptor usbnet_ss_bulk_out_comp_desc = { + .bLength = sizeof usbnet_ss_bulk_out_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_ss_ep_comp_descriptor usbnet_ss_intr_comp_desc = { + .bLength = sizeof usbnet_ss_intr_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(64), +}; + +static struct usb_descriptor_header *ss_function[] = { + (struct usb_descriptor_header *) &usbnet_intf_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_in_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_out_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_out_comp_desc, + (struct usb_descriptor_header *) &intr_out_desc, + (struct usb_descriptor_header *) &usbnet_ss_intr_comp_desc, + NULL, +}; + +#ifdef CONFIG_SWITCH +static struct switch_dev usbnet_enable_device = { + .name = "usbnet_enable", +}; +#endif + +#define DO_NOT_STOP_QUEUE 0 +#define STOP_QUEUE 1 + +#define USBNETDBG(context, fmt, args...) {\ + if (context && (context->gadget)) \ + dev_dbg(&(context->gadget->dev) , fmt , ## args); } + +static const char *usb_description = "Motorola BLAN Interface"; + +static ssize_t usbnet_desc_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + ssize_t status = 0; + status = snprintf(buff, 30, "%s\n", usb_description); + return status; +} + +static DEVICE_ATTR(description, S_IRUGO, usbnet_desc_show, NULL); + +static inline struct usbnet_device *usbnet_func_to_dev(struct usb_function *f) +{ + return container_of(f, struct usbnet_device, function); +} + + +static int ether_queue_out(struct usb_request *req , + struct usbnet_context *context) +{ + unsigned long flags; + struct sk_buff *skb; + int ret; + + skb = alloc_skb(USB_MTU + NET_IP_ALIGN, GFP_ATOMIC); + if (!skb) { + USBNETDBG(context, "%s: failed to alloc skb\n", __func__); + ret = -ENOMEM; + goto fail; + } + + skb_reserve(skb, NET_IP_ALIGN); + + req->buf = skb->data; + req->length = ALIGN(USB_MTU, context->bulk_out->maxpacket); + req->context = skb; + + ret = usb_ep_queue(context->bulk_out, req, GFP_KERNEL); + if (ret == 0) + return 0; + else + kfree_skb(skb); +fail: + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->rx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + + return ret; +} + +struct usb_request *usb_get_recv_request(struct usbnet_context *context) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->rx_reqs)) { + req = NULL; + } else { + req = list_first_entry(&context->rx_reqs, + struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&context->lock, flags); + + return req; +} + +struct usb_request *usb_get_xmit_request(int stop_flag, struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->tx_reqs)) { + req = NULL; + } else { + req = list_first_entry(&context->tx_reqs, + struct usb_request, list); + list_del(&req->list); + if (stop_flag == STOP_QUEUE && + list_empty(&context->tx_reqs)) + netif_stop_queue(dev); + } + spin_unlock_irqrestore(&context->lock, flags); + return req; +} + +static int usb_ether_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + struct usb_request *req; + unsigned long flags; + unsigned len; + int rc; + + req = usb_get_xmit_request(STOP_QUEUE, dev); + + if (!req) { + USBNETDBG(context, "%s: could not obtain tx request\n", + __func__); + return 1; + } + + /* Add 4 bytes CRC */ + skb->len += 4; + + /* ensure that we end with a short packet */ + len = skb->len; + if (!(len & 63) || !(len & 511)) + len++; + + req->context = skb; + req->buf = skb->data; + req->length = len; + + rc = usb_ep_queue(context->bulk_in, req, GFP_KERNEL); + if (rc != 0) { + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->tx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + + dev_kfree_skb_any(skb); + context->stats.tx_dropped++; + + USBNETDBG(context, + "%s: could not queue tx request\n", __func__); + } + + return 0; +} + +static int usb_ether_open(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + USBNETDBG(context, "%s\n", __func__); + return 0; +} + +static int usb_ether_stop(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + USBNETDBG(context, "%s\n", __func__); + return 0; +} + +static struct net_device_stats *usb_ether_get_stats(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + USBNETDBG(context, "%s\n", __func__); + return &context->stats; +} + +static void usbnet_if_config(struct work_struct *work) +{ + struct ifreq ifr; + mm_segment_t saved_fs; + unsigned err; + struct sockaddr_in *sin; + struct usbnet_context *context = container_of(work, + struct usbnet_context, usbnet_config_wq); + + pr_info("%s : Configuring with config = %d, ip_addr = 0x%08x", + __func__, context->config, context->ip_addr); + pr_info("subnet = 0x%08x, router_ip = 0x%08x, flags = 0x%08x\n", + context->subnet_mask, context->router_ip, context->iff_flag); + memset(&ifr, 0, sizeof(ifr)); + sin = (void *) &(ifr.ifr_ifru.ifru_addr); + strlcpy(ifr.ifr_ifrn.ifrn_name, context->dev->name, + sizeof(ifr.ifr_ifrn.ifrn_name)); + sin->sin_family = AF_INET; + + sin->sin_addr.s_addr = context->ip_addr; + saved_fs = get_fs(); + set_fs(get_ds()); + err = devinet_ioctl(dev_net(context->dev), SIOCSIFADDR, &ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFADDR\n", __func__); + + sin->sin_addr.s_addr = context->subnet_mask; + err = devinet_ioctl(dev_net(context->dev), SIOCSIFNETMASK, &ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFNETMASK\n", __func__); + + sin->sin_addr.s_addr = context->ip_addr | ~(context->subnet_mask); + err = devinet_ioctl(dev_net(context->dev), SIOCSIFBRDADDR, &ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFBRDADDR\n", __func__); + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_ifrn.ifrn_name, context->dev->name, + sizeof(ifr.ifr_ifrn.ifrn_name)); + ifr.ifr_flags = context->dev->flags & ~IFF_UP; + ifr.ifr_flags |= context->iff_flag; + err = devinet_ioctl(dev_net(context->dev), SIOCSIFFLAGS, &ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFFLAGS\n", __func__); + + set_fs(saved_fs); +#ifdef CONFIG_SWITCH + switch_set_state(&usbnet_enable_device, context->config); +#endif +} + +static const struct net_device_ops usbnet_eth_netdev_ops = { + .ndo_open = usb_ether_open, + .ndo_stop = usb_ether_stop, + .ndo_start_xmit = usb_ether_xmit, + .ndo_get_stats = usb_ether_get_stats, +}; + +static void usb_ether_setup(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + INIT_LIST_HEAD(&context->rx_reqs); + INIT_LIST_HEAD(&context->tx_reqs); + + spin_lock_init(&context->lock); + context->dev = dev; + + dev->netdev_ops = &usbnet_eth_netdev_ops; + dev->watchdog_timeo = 20; + + ether_setup(dev); + + random_ether_addr(dev->dev_addr); +} + +/*-------------------------------------------------------------------------*/ +static void usbnet_cleanup(struct usbnet_device *dev) +{ + struct usbnet_context *context = dev->net_ctxt; + if (context) { + device_remove_file(&(context->dev->dev), &dev_attr_description); + unregister_netdev(context->dev); + free_netdev(context->dev); + dev->net_ctxt = NULL; + } +} + +static void usbnet_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usb_composite_dev *cdev = c->cdev; + struct usbnet_context *context = dev->net_ctxt; + struct usb_request *req; + + dev->cdev = cdev; + + if (context->bulk_in) + usb_ep_disable(context->bulk_in); + + if (context->bulk_out) { + usb_ep_disable(context->bulk_out); + + /* Free BULK OUT Requests */ + while ((req = usb_get_recv_request(context))) + usb_ep_free_request(context->bulk_out, req); + } + + if (context->bulk_in) { + /* Free BULK IN Requests */ + while ((req = usb_get_xmit_request(DO_NOT_STOP_QUEUE, + context->dev))) { + usb_ep_free_request(context->bulk_in, req); + } + } + + context->bulk_in = NULL; + context->bulk_out = NULL; + context->config = 0; +} + +static void ether_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = req->context; + struct usbnet_context *context = ep->driver_data; + + if (req->status == 0) { + skb_put(skb, req->actual); + skb->protocol = eth_type_trans(skb, context->dev); + context->stats.rx_packets++; + context->stats.rx_bytes += req->actual; + netif_rx(skb); + } else { + dev_kfree_skb_any(skb); + context->stats.rx_errors++; + } + + /* don't bother requeuing if we just went offline */ + if ((req->status == -ENODEV) || (req->status == -ESHUTDOWN)) { + unsigned long flags; + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->rx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + } else { + if (ether_queue_out(req, context)) + USBNETDBG(context, "ether_out: cannot requeue\n"); + } +} + +static void ether_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + unsigned long flags; + struct sk_buff *skb = req->context; + struct usbnet_context *context = ep->driver_data; + + if (req->status == 0) { + context->stats.tx_packets++; + context->stats.tx_bytes += req->actual; + } else { + context->stats.tx_errors++; + } + + dev_kfree_skb_any(skb); + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->tx_reqs)) + netif_start_queue(context->dev); + + list_add_tail(&req->list, &context->tx_reqs); + spin_unlock_irqrestore(&context->lock, flags); +} + +static int usbnet_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + int n, rc, id; + struct usb_ep *ep; + struct usb_request *req; + unsigned long flags; + + dev->cdev = cdev; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + usbnet_intf_desc.bInterfaceNumber = id; + context->gadget = cdev->gadget; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_in_desc); + if (!ep) { + USBNETDBG(context, "%s usbnet_hs_bulk_in_desc error\n", + __func__); + goto autoconf_fail; + } + ep->driver_data = context; + context->bulk_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_out_desc); + if (!ep) { + USBNETDBG(context, "%s usbnet_hs_bulk_out_desc error\n", + __func__); + goto autoconf_fail; + } + ep->driver_data = context; + context->bulk_out = ep; + + + ep = usb_ep_autoconfig(cdev->gadget, &intr_out_desc); + if (!ep) { + USBNETDBG(context, "%s intr_out_desc error\n", + __func__); + goto autoconf_fail; + } + ep->driver_data = context; + context->intr_out = ep; + + if (gadget_is_dualspeed(cdev->gadget)) { + + /* Assume endpoint addresses are the same for both speeds */ + usbnet_hs_bulk_in_desc.bEndpointAddress = + usbnet_fs_bulk_in_desc.bEndpointAddress; + usbnet_hs_bulk_out_desc.bEndpointAddress = + usbnet_fs_bulk_out_desc.bEndpointAddress; + } + + /* support super speed hardware */ + if (gadget_is_superspeed(c->cdev->gadget)) { + usbnet_ss_bulk_in_desc.bEndpointAddress = + usbnet_fs_bulk_in_desc.bEndpointAddress; + usbnet_ss_bulk_out_desc.bEndpointAddress = + usbnet_fs_bulk_out_desc.bEndpointAddress; + } + + + rc = -ENOMEM; + + for (n = 0; n < MAX_BULK_RX_REQ_NUM; n++) { + req = usb_ep_alloc_request(context->bulk_out, + GFP_KERNEL); + if (!req) { + USBNETDBG(context, "%s: alloc request bulk_out fail\n", + __func__); + break; + } + req->complete = ether_out_complete; + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->rx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + } + for (n = 0; n < MAX_BULK_TX_REQ_NUM; n++) { + req = usb_ep_alloc_request(context->bulk_in, + GFP_KERNEL); + if (!req) { + USBNETDBG(context, "%s: alloc request bulk_in fail\n", + __func__); + break; + } + req->complete = ether_in_complete; + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->tx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + } + + return 0; + +autoconf_fail: + rc = -ENOTSUPP; + usbnet_unbind(c, f); + return rc; +} + + + + +static void do_set_config(struct usb_function *f, u16 new_config) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + struct usb_composite_dev *cdev = f->config->cdev; + int result = 0; + struct usb_request *req; + + if (context->config == new_config) /* Config did not change */ + return; + + context->config = new_config; + + if (new_config == 1) { /* Enable End points */ + result = config_ep_by_speed(cdev->gadget, f, context->bulk_in); + if (result) { + context->bulk_in->desc = NULL; + USBNETDBG(context, + "config_ep_by_speed failed: ep %s, result %d\n", + context->bulk_in->name, result); + return; + } + + result = usb_ep_enable(context->bulk_in); + + if (result != 0) { + USBNETDBG(context, + "%s: failed to enable BULK_IN EP ret=%d\n", + __func__, result); + } + + context->bulk_in->driver_data = context; + + result = config_ep_by_speed(cdev->gadget, f, context->bulk_out); + if (result) { + context->bulk_out->desc = NULL; + USBNETDBG(context, + "config_ep_by_speed failed: ep %s, result %d\n", + context->bulk_out->name, result); + usb_ep_disable(context->bulk_in); + return; + } + + result = usb_ep_enable(context->bulk_out); + + if (result != 0) { + USBNETDBG(context, + "%s: failed to enable BULK_OUT EP ret = %d\n", + __func__, result); + } + + context->bulk_out->driver_data = context; + + context->intr_out->desc = &intr_out_desc; + result = usb_ep_enable(context->intr_out); + + if (result != 0) { + USBNETDBG(context, + "%s: failed to enable INTR_OUT EP ret = %d\n", + __func__, result); + } + + context->intr_out->driver_data = context; + + /* we're online -- get all rx requests queued */ + while ((req = usb_get_recv_request(context))) { + if (ether_queue_out(req, context)) { + USBNETDBG(context, + "%s: ether_queue_out failed\n", + __func__); + break; + } + } + + } else {/* Disable Endpoints */ + if (context->bulk_in) + usb_ep_disable(context->bulk_in); + if (context->bulk_out) + usb_ep_disable(context->bulk_out); + if (context->intr_out) + usb_ep_disable(context->intr_out); + context->ip_addr = 0; + context->subnet_mask = 0; + context->router_ip = 0; + context->iff_flag = 0; + schedule_work(&context->usbnet_config_wq); + } +} + + +static int usbnet_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "usbnet_set_alt intf: %d alt: %d\n", intf, alt); + do_set_config(f, 1); + return 0; +} + +static int usbnet_ctrlrequest(struct usbnet_device *dev, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct usbnet_context *context = dev->net_ctxt; + int rc = -EOPNOTSUPP; + int wIndex = le16_to_cpu(ctrl->wIndex); + int wValue = le16_to_cpu(ctrl->wValue); + int wLength = le16_to_cpu(ctrl->wLength); + struct usb_request *req = cdev->req; + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + switch (ctrl->bRequest) { + case USBNET_SET_IP_ADDRESS: + context->ip_addr = (wValue << 16) | wIndex; + rc = 0; + break; + case USBNET_SET_SUBNET_MASK: + context->subnet_mask = (wValue << 16) | wIndex; + rc = 0; + break; + case USBNET_SET_HOST_IP: + context->router_ip = (wValue << 16) | wIndex; + rc = 0; + break; + default: + break; + } + + if (context->ip_addr && context->subnet_mask + && context->router_ip) { + context->iff_flag = IFF_UP; + /* schedule a work queue to do this because we + need to be able to sleep */ + schedule_work(&context->usbnet_config_wq); + } + } + + /* respond with data transfer or status phase? */ + if (rc >= 0) { + req->zero = rc < wLength; + req->length = rc; + rc = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (rc < 0) + USBNETDBG(context, "usbnet setup response error\n"); + } + + return rc; +} + +static void usbnet_disable(struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "%s\n", __func__); + do_set_config(f, 0); +} + +static void usbnet_suspend(struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "%s\n", __func__); +} + +static void usbnet_resume(struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "%s\n", __func__); +} + +int usbnet_bind_config(struct usbnet_device *dev, struct usb_configuration *c) +{ + int ret, status; + + pr_debug("usbnet_bind_config\n"); + + if (usbnet_string_defs[STRING_INTERFACE].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) { + pr_err("%s: failed to get string id, err:%d\n", + __func__, status); + return status; + } + usbnet_string_defs[STRING_INTERFACE].id = status; + usbnet_intf_desc.iInterface = status; + } + + dev->cdev = c->cdev; + dev->function.name = "usbnet"; + dev->function.fs_descriptors = fs_function; + dev->function.hs_descriptors = hs_function; + if (gadget_is_superspeed(c->cdev->gadget)) + dev->function.ss_descriptors = ss_function; + dev->function.bind = usbnet_bind; + dev->function.unbind = usbnet_unbind; + dev->function.set_alt = usbnet_set_alt; + dev->function.disable = usbnet_disable; + dev->function.suspend = usbnet_suspend; + dev->function.resume = usbnet_resume; + dev->function.strings = usbnet_strings; + + ret = usb_add_function(c, &dev->function); + + return ret; +} diff --git a/drivers/usb/gadget/function/u_bam.c b/drivers/usb/gadget/function/u_bam.c index b2bf5b56..d3c27c30 100644 --- a/drivers/usb/gadget/function/u_bam.c +++ b/drivers/usb/gadget/function/u_bam.c @@ -1385,7 +1385,6 @@ static void gbam2bam_connect_work(struct work_struct *w) spin_unlock_irqrestore(&port->port_lock, flags); usb_bam_alloc_fifos(d->usb_bam_type, d->src_connection_idx); usb_bam_alloc_fifos(d->usb_bam_type, d->dst_connection_idx); - gadget->bam2bam_func_enabled = true; spin_lock_irqsave(&port->port_lock, flags); /* check if USB cable is disconnected or not */ @@ -2355,6 +2354,12 @@ exit: return ret; } +void gbam_data_flush_workqueue(void) +{ + pr_debug("%s(): Flushing workqueue\n", __func__); + flush_workqueue(gbam_wq); +} + int gbam_setup(unsigned int no_bam_port) { int i; diff --git a/drivers/usb/gadget/function/u_bam_data.c b/drivers/usb/gadget/function/u_bam_data.c index c1d770f4..5d6549e9 100644 --- a/drivers/usb/gadget/function/u_bam_data.c +++ b/drivers/usb/gadget/function/u_bam_data.c @@ -1021,7 +1021,6 @@ static void bam2bam_data_connect_work(struct work_struct *w) __func__, ret); goto free_fifos; } - gadget->bam2bam_func_enabled = true; spin_lock_irqsave(&port->port_lock, flags); if (port->last_event == U_BAM_DATA_DISCONNECT_E) { diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 9f601239..13d457bc 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -422,7 +422,6 @@ static void ipa_data_connect_work(struct work_struct *w) pr_err("usb_bam_connect_ipa out failed err:%d\n", ret); goto unconfig_msm_ep_in; } - gadget->bam2bam_func_enabled = true; gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx; is_ipa_disconnected = false; @@ -438,7 +437,6 @@ static void ipa_data_connect_work(struct work_struct *w) pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret); goto disconnect_usb_bam_ipa_out; } - gadget->bam2bam_func_enabled = true; gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx; is_ipa_disconnected = false; @@ -821,6 +819,12 @@ void ipa_data_port_select(int portno, enum gadget_type gtype) port->gtype = gtype; }; +void ipa_data_flush_workqueue(void) +{ + pr_debug("%s(): Flushing workqueue\n", __func__); + flush_workqueue(ipa_data_wq); +} + /** * ipa_data_setup() - setup BAM2BAM IPA port * @no_ipa_port: total number of BAM2BAM IPA port to support diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h index b7d47ab1..f6bc961e 100644 --- a/drivers/usb/gadget/function/u_data_ipa.h +++ b/drivers/usb/gadget/function/u_data_ipa.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -31,5 +31,6 @@ int ipa_data_connect(struct gadget_ipa_port *gp, u8 port_num, int ipa_data_setup(unsigned int no_ipa_port); void ipa_data_resume(struct gadget_ipa_port *gp, u8 port_num); void ipa_data_suspend(struct gadget_ipa_port *gp, u8 port_num); +void ipa_data_flush_workqueue(void); #endif diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c index f44bf51e..d29b0754 100644 --- a/drivers/usb/gadget/function/u_qdss.c +++ b/drivers/usb/gadget/function/u_qdss.c @@ -100,7 +100,6 @@ static int set_qdss_data_connection(struct usb_gadget *gadget, res = usb_bam_connect(usb_bam_type, idx, &(bam_info.usb_bam_pipe_idx)); - gadget->bam2bam_func_enabled = true; } else { kfree(bam_info.data_fifo); res = usb_bam_disconnect_pipe(usb_bam_type, idx); diff --git a/drivers/usb/gadget/function/u_rmnet.h b/drivers/usb/gadget/function/u_rmnet.h index 4336dbf2..c83aaae9 100644 --- a/drivers/usb/gadget/function/u_rmnet.h +++ b/drivers/usb/gadget/function/u_rmnet.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -76,4 +76,5 @@ int gsmd_ctrl_connect(struct grmnet *gr, int port_num); void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num); int gsmd_ctrl_setup(enum ctrl_client client_num, unsigned int count, u8 *first_port_idx); +void gbam_data_flush_workqueue(void); #endif /* __U_RMNET_H*/ |
