usb: gadget: RNDIS function driver for QCOM chipsets
New function driver to support RNDIS with adaptions to
QCOM chipsets (Data path is over SPS BAM-to-BAM).
CRs-Fixed: 361085
Change-Id: I90dcb52e4a4fe939e2150e89d794aaddac4392f5
Signed-off-by: Ofir Cohen <ofirc@codeaurora.org>
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 54c486e..ac88636 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -72,6 +72,7 @@
#include "u_bam_data.c"
#include "f_mbim.c"
#include "f_qc_ecm.c"
+#include "f_qc_rndis.c"
#include "u_qc_ether.c"
#ifdef CONFIG_TARGET_CORE
#include "f_tcm.c"
@@ -965,6 +966,7 @@
struct rndis_function_config {
u8 ethaddr[ETH_ALEN];
u32 vendorID;
+ u8 max_pkt_per_xfer;
char manufacturer[256];
/* "Wireless" RNDIS; auto-detected by Windows */
bool wceis;
@@ -986,6 +988,22 @@
f->config = NULL;
}
+static int rndis_qc_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL);
+ if (!f->config)
+ return -ENOMEM;
+
+ return rndis_qc_init();
+}
+
+static void rndis_qc_function_cleanup(struct android_usb_function *f)
+{
+ rndis_qc_cleanup();
+ kfree(f->config);
+}
+
static int
rndis_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
@@ -1024,12 +1042,56 @@
rndis->manufacturer);
}
+static int rndis_qc_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int ret;
+ struct rndis_function_config *rndis = f->config;
+
+ if (!rndis) {
+ pr_err("%s: rndis_pdata\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__,
+ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
+ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
+
+ ret = gether_qc_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis");
+ if (ret) {
+ pr_err("%s: gether_setup failed\n", __func__);
+ return ret;
+ }
+
+ if (rndis->wceis) {
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ rndis_qc_iad_descriptor.bFunctionClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ rndis_qc_iad_descriptor.bFunctionSubClass = 0x01;
+ rndis_qc_iad_descriptor.bFunctionProtocol = 0x03;
+ rndis_qc_control_intf.bInterfaceClass =
+ USB_CLASS_WIRELESS_CONTROLLER;
+ rndis_qc_control_intf.bInterfaceSubClass = 0x01;
+ rndis_qc_control_intf.bInterfaceProtocol = 0x03;
+ }
+
+ return rndis_qc_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID,
+ rndis->manufacturer,
+ rndis->max_pkt_per_xfer);
+}
+
static void rndis_function_unbind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
gether_cleanup();
}
+static void rndis_qc_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ gether_qc_cleanup();
+}
+
static ssize_t rndis_manufacturer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1136,11 +1198,38 @@
static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show,
rndis_vendorID_store);
+static ssize_t rndis_max_pkt_per_xfer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ return snprintf(buf, PAGE_SIZE, "%d\n", config->max_pkt_per_xfer);
+}
+
+static ssize_t rndis_max_pkt_per_xfer_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct rndis_function_config *config = f->config;
+ int value;
+
+ if (sscanf(buf, "%d", &value) == 1) {
+ config->max_pkt_per_xfer = value;
+ return size;
+ }
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(max_pkt_per_xfer, S_IRUGO | S_IWUSR,
+ rndis_max_pkt_per_xfer_show,
+ rndis_max_pkt_per_xfer_store);
+
static struct device_attribute *rndis_function_attributes[] = {
&dev_attr_manufacturer,
&dev_attr_wceis,
&dev_attr_ethaddr,
&dev_attr_vendorID,
+ &dev_attr_max_pkt_per_xfer,
NULL
};
@@ -1153,6 +1242,14 @@
.attributes = rndis_function_attributes,
};
+static struct android_usb_function rndis_qc_function = {
+ .name = "rndis_qc",
+ .init = rndis_qc_function_init,
+ .cleanup = rndis_qc_function_cleanup,
+ .bind_config = rndis_qc_function_bind_config,
+ .unbind_config = rndis_qc_function_unbind_config,
+ .attributes = rndis_function_attributes,
+};
struct mass_storage_function_config {
struct fsg_config fsg;
@@ -1356,6 +1453,7 @@
&mtp_function,
&ptp_function,
&rndis_function,
+ &rndis_qc_function,
&mass_storage_function,
&accessory_function,
&uasp_function,