aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
authorwzedlare <vedatak01@gmail.com>2017-06-18 16:38:26 +0000
committerwzedlare <vedatak01@gmail.com>2017-06-19 16:57:11 +0000
commitc7d4e3fd588e3ba3d3fa4d5cfa224aa54bc288bf (patch)
treeb8b64cb9deb6832c1e41f58f0f143514beafc709 /drivers/usb/gadget
parent28c99c87b881bb664c44bb26e80a681f87d54e60 (diff)
p2a42: Import fully working kernel sourceHEADn7.1
Change-Id: Ia4c94f09e29843b1af34d466243378a357e97b70
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/android.c156
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c41
-rw-r--r--drivers/usb/gadget/composite.c4
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_accessory.c7
-rw-r--r--drivers/usb/gadget/function/f_diag.c62
-rw-r--r--drivers/usb/gadget/function/f_fs.c4
-rw-r--r--drivers/usb/gadget/function/f_gsi.c2
-rw-r--r--drivers/usb/gadget/function/f_midi.c2
-rw-r--r--drivers/usb/gadget/function/f_mtp.c2
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c2
-rw-r--r--drivers/usb/gadget/function/f_qdss.c4
-rw-r--r--drivers/usb/gadget/function/f_rmnet.c9
-rw-r--r--drivers/usb/gadget/function/f_rndis.c2
-rw-r--r--drivers/usb/gadget/function/f_usbnet.c881
-rw-r--r--drivers/usb/gadget/function/u_bam.c7
-rw-r--r--drivers/usb/gadget/function/u_bam_data.c1
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c8
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.h3
-rw-r--r--drivers/usb/gadget/function/u_qdss.c1
-rw-r--r--drivers/usb/gadget/function/u_rmnet.h3
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*/