usb: gadget: Support CDC ECM for android usb
Add f_ecm into android_usb. you can use cdc ecm instead of rndis.
Change-Id: I0a1235fdeba8b601ddcc3bed737b2c8505fee631
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 14b07e5..08fa1a6 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -1095,4 +1095,11 @@
transfers. This option enables only control interface.
Data interface used is BAM.
+config USB_ANDROID_CDC_ECM
+ boolean "Android Gadget support CDC ECM"
+ depends on USB_G_ANDROID
+ default n
+ help
+ Android Gadget USB support CDC ECM instead of RNDIS
+
endif # USB_GADGET
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index eefe95f..3f240c0 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -65,9 +65,13 @@
#include "f_ccid.c"
#include "f_mtp.c"
#include "f_accessory.c"
+#ifdef CONFIG_USB_ANDROID_CDC_ECM
+#include "f_ecm.c"
+#else
#define USB_ETH_RNDIS y
#include "f_rndis.c"
#include "rndis.c"
+#endif
#include "u_ether.c"
#include "u_bam_data.c"
#include "f_mbim.c"
@@ -864,6 +868,154 @@
.bind_config = ptp_function_bind_config,
};
+#ifdef CONFIG_USB_ANDROID_CDC_ECM
+struct ecm_function_config {
+ u8 ethaddr[ETH_ALEN];
+ u32 vendorID;
+ char manufacturer[256];
+};
+
+static int ecm_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ f->config = kzalloc(sizeof(struct ecm_function_config), GFP_KERNEL);
+ if (!f->config)
+ return -ENOMEM;
+ return 0;
+}
+
+static void ecm_function_cleanup(struct android_usb_function *f)
+{
+ kfree(f->config);
+ f->config = NULL;
+}
+
+static int ecm_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int ret;
+ struct ecm_function_config *ecm = f->config;
+
+ if (!ecm) {
+ pr_err("%s: ecm_function_config\n", __func__);
+ return -1;
+ }
+
+ pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__,
+ ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2],
+ ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]);
+
+ ret = gether_setup_name(c->cdev->gadget, ecm->ethaddr, "usb");
+ if (ret) {
+ pr_err("%s: gether_setup failed\n", __func__);
+ return ret;
+ }
+
+ return ecm_bind_config(c, ecm->ethaddr);
+}
+
+static void ecm_function_unbind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ gether_cleanup();
+}
+
+static ssize_t ecm_manufacturer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ecm_function_config *config = f->config;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", config->manufacturer);
+}
+
+static ssize_t ecm_manufacturer_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ecm_function_config *config = f->config;
+
+ if (size >= sizeof(config->manufacturer))
+ return -EINVAL;
+
+ if (sscanf(buf, "%255s", config->manufacturer) == 1)
+ return size;
+ return -1;
+}
+
+static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, ecm_manufacturer_show,
+ ecm_manufacturer_store);
+
+static ssize_t ecm_ethaddr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ecm_function_config *ecm = f->config;
+ return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2],
+ ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]);
+}
+
+static ssize_t ecm_ethaddr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ecm_function_config *ecm = f->config;
+
+ if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ (int *)&ecm->ethaddr[0], (int *)&ecm->ethaddr[1],
+ (int *)&ecm->ethaddr[2], (int *)&ecm->ethaddr[3],
+ (int *)&ecm->ethaddr[4], (int *)&ecm->ethaddr[5]) == 6)
+ return size;
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, ecm_ethaddr_show,
+ ecm_ethaddr_store);
+
+static ssize_t ecm_vendorID_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ecm_function_config *config = f->config;
+
+ return snprintf(buf, PAGE_SIZE, "%04x\n", config->vendorID);
+}
+
+static ssize_t ecm_vendorID_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct ecm_function_config *config = f->config;
+ int value;
+
+ if (sscanf(buf, "%04x", &value) == 1) {
+ config->vendorID = value;
+ return size;
+ }
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, ecm_vendorID_show,
+ ecm_vendorID_store);
+
+static struct device_attribute *ecm_function_attributes[] = {
+ &dev_attr_manufacturer,
+ &dev_attr_ethaddr,
+ &dev_attr_vendorID,
+ NULL
+};
+
+static struct android_usb_function ecm_function = {
+ .name = "ecm",
+ .init = ecm_function_init,
+ .cleanup = ecm_function_cleanup,
+ .bind_config = ecm_function_bind_config,
+ .unbind_config = ecm_function_unbind_config,
+ .attributes = ecm_function_attributes,
+};
+
+#else
struct rndis_function_config {
u8 ethaddr[ETH_ALEN];
@@ -1055,7 +1207,7 @@
.unbind_config = rndis_function_unbind_config,
.attributes = rndis_function_attributes,
};
-
+#endif /* CONFIG_USB_ANDROID_CDC_ECM */
struct mass_storage_function_config {
struct fsg_config fsg;
@@ -1194,7 +1346,11 @@
&acm_function,
&mtp_function,
&ptp_function,
+#ifdef CONFIG_USB_ANDROID_CDC_ECM
+ &ecm_function,
+#else
&rndis_function,
+#endif
&mass_storage_function,
&accessory_function,
NULL
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 30b908f..1003b1d 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -92,7 +92,11 @@
*/
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#ifdef CONFIG_USB_ANDROID_CDC_ECM
+#define ECM_STATUS_BYTECOUNT 64
+#else
#define ECM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
+#endif
/* interface descriptor: */
@@ -192,7 +196,11 @@
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
+#ifdef CONFIG_USB_CDC_ECM
+ .bInterval = 4,
+#else
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+#endif
};
static struct usb_endpoint_descriptor fs_ecm_in_desc = {
@@ -201,6 +209,9 @@
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
+#ifdef CONFIG_USB_CDC_ECM
+ .wMaxPacketSize = cpu_to_le16(64),
+#endif
};
static struct usb_endpoint_descriptor fs_ecm_out_desc = {
@@ -209,6 +220,9 @@
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
+#ifdef CONFIG_USB_CDC_ECM
+ .wMaxPacketSize = cpu_to_le16(64),
+#endif
};
static struct usb_descriptor_header *ecm_fs_function[] = {
@@ -239,7 +253,11 @@
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
+#ifdef CONFIG_USB_CDC_ECM
+ .bInterval = 4,
+#else
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+#endif
};
static struct usb_endpoint_descriptor hs_ecm_in_desc = {
@@ -578,11 +596,20 @@
if (alt == 1) {
struct net_device *net;
+#ifdef CONFIG_USB_CDC_ECM
+ /* Disable zlps in case of LG android USB and qct's udc.
+ * By this, host driver can handle null packet properly.
+ */
+ ecm->port.is_zlp_ok = !(
+ gadget_is_musbhdrc(cdev->gadget) ||
+ gadget_is_ci13xxx_msm(cdev->gadget));
+#else
/* Enable zlps by default for ECM conformance;
* override for musb_hdrc (avoids txdma ovhead).
*/
ecm->port.is_zlp_ok = !(gadget_is_musbhdrc(cdev->gadget)
);
+#endif
ecm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ecm\n");
net = gether_connect(&ecm->port);
@@ -869,6 +896,16 @@
ecm_string_defs[0].id = status;
ecm_control_intf.iInterface = status;
+#ifdef CONFIG_USB_CDC_ECM
+ /* MAC address */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ecm_string_defs[1].id = status;
+ pr_info("%s: iMACAddress = %d\n", __func__, status);
+ ecm_desc.iMACAddress = status;
+#endif
+
/* data interface label */
status = usb_string_id(c->cdev);
if (status < 0)
@@ -876,12 +913,14 @@
ecm_string_defs[2].id = status;
ecm_data_intf.iInterface = status;
+#ifndef CONFIG_USB_CDC_ECM
/* MAC address */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
ecm_string_defs[1].id = status;
ecm_desc.iMACAddress = status;
+#endif
/* IAD label */
status = usb_string_id(c->cdev);
@@ -905,7 +944,11 @@
ecm->port.cdc_filter = DEFAULT_FILTER;
+#ifdef CONFIG_USB_CDC_ECM
+ ecm->port.func.name = "ecm";
+#else
ecm->port.func.name = "cdc_ethernet";
+#endif
ecm->port.func.strings = ecm_strings;
/* descriptors are per-instance copies */
ecm->port.func.bind = ecm_bind;