Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4
AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.
* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
PRNG: Device tree entry for qrng device.
vidc:1080p: Set video core timeout value for Thumbnail mode
msm: sps: improve the debugging support in SPS driver
board-8064 msm: Overlap secure and non secure video firmware heaps.
msm: clock: Add handoff ops for 7x30 and copper XO clocks
msm_fb: display: Wait for external vsync before DTV IOMMU unmap
msm: Fix ciruclar dependency in debug UART settings
msm: gdsc: Add GDSC regulator driver for msm-copper
defconfig: Enable Mobicore Driver.
mobicore: Add mobicore driver.
mobicore: rename variable to lower case.
mobicore: rename folder.
mobicore: add makefiles
mobicore: initial import of kernel driver
ASoC: msm: Add SLIMBUS_2_RX CPU DAI
board-8064-gpio: Update FUNC for EPM SPI CS
msm_fb: display: Remove chicken bit config during video playback
mmc: msm_sdcc: enable the sanitize capability
msm-fb: display: lm2 writeback support on mpq platfroms
msm_fb: display: Disable LVDS phy & pll during panel off
...
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 7a2af58..eefe95f 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -23,10 +23,12 @@
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/android.h>
#include "gadget_chips.h"
@@ -42,16 +44,33 @@
#include "epautoconf.c"
#include "composite.c"
+#include "f_diag.c"
+#include "f_rmnet_smd.c"
+#include "f_rmnet_sdio.c"
+#include "f_rmnet_smd_sdio.c"
+#include "f_rmnet.c"
#include "f_mass_storage.c"
#include "u_serial.c"
+#include "u_sdio.c"
+#include "u_smd.c"
+#include "u_bam.c"
+#include "u_rmnet_ctrl_smd.c"
+#include "u_ctrl_hsic.c"
+#include "u_data_hsic.c"
+#include "u_ctrl_hsuart.c"
+#include "u_data_hsuart.c"
+#include "f_serial.c"
#include "f_acm.c"
#include "f_adb.c"
+#include "f_ccid.c"
#include "f_mtp.c"
#include "f_accessory.c"
#define USB_ETH_RNDIS y
#include "f_rndis.c"
#include "rndis.c"
#include "u_ether.c"
+#include "u_bam_data.c"
+#include "f_mbim.c"
MODULE_AUTHOR("Mike Lockwood");
MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -106,8 +125,12 @@
bool enabled;
int disable_depth;
struct mutex mutex;
+ struct android_usb_platform_data *pdata;
+
bool connected;
bool sw_connected;
+ char pm_qos[5];
+ struct pm_qos_request pm_qos_req_dma;
struct work_struct work;
};
@@ -154,14 +177,50 @@
.bNumConfigurations = 1,
};
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ .bcdOTG = __constant_cpu_to_le16(0x0200),
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
static struct usb_configuration android_config_driver = {
.label = "android",
.unbind = android_unbind_config,
.bConfigurationValue = 1,
- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 0xFA, /* 500ma */
};
+enum android_device_state {
+ USB_DISCONNECTED,
+ USB_CONNECTED,
+ USB_CONFIGURED,
+};
+
+static void android_pm_qos_update_latency(struct android_dev *dev, int vote)
+{
+ struct android_usb_platform_data *pdata = dev->pdata;
+ u32 swfi_latency = 0;
+ static int last_vote = -1;
+
+ if (!pdata || vote == last_vote
+ || !pdata->swfi_latency)
+ return;
+
+ swfi_latency = pdata->swfi_latency + 1;
+ if (vote)
+ pm_qos_update_request(&dev->pm_qos_req_dma,
+ swfi_latency);
+ else
+ pm_qos_update_request(&dev->pm_qos_req_dma,
+ PM_QOS_DEFAULT_VALUE);
+ last_vote = vote;
+}
+
static void android_work(struct work_struct *data)
{
struct android_dev *dev = container_of(data, struct android_dev, work);
@@ -170,18 +229,53 @@
char *connected[2] = { "USB_STATE=CONNECTED", NULL };
char *configured[2] = { "USB_STATE=CONFIGURED", NULL };
char **uevent_envp = NULL;
+ static enum android_device_state last_uevent, next_state;
unsigned long flags;
+ int pm_qos_vote = -1;
spin_lock_irqsave(&cdev->lock, flags);
- if (cdev->config)
+ if (cdev->config) {
uevent_envp = configured;
- else if (dev->connected != dev->sw_connected)
+ next_state = USB_CONFIGURED;
+ } else if (dev->connected != dev->sw_connected) {
uevent_envp = dev->connected ? connected : disconnected;
+ next_state = dev->connected ? USB_CONNECTED : USB_DISCONNECTED;
+ if (dev->connected && strncmp(dev->pm_qos, "low", 3))
+ pm_qos_vote = 1;
+ else if (!dev->connected || !strncmp(dev->pm_qos, "low", 3))
+ pm_qos_vote = 0;
+ }
dev->sw_connected = dev->connected;
spin_unlock_irqrestore(&cdev->lock, flags);
+ if (pm_qos_vote != -1)
+ android_pm_qos_update_latency(dev, pm_qos_vote);
+
if (uevent_envp) {
+ /*
+ * Some userspace modules, e.g. MTP, work correctly only if
+ * CONFIGURED uevent is preceded by DISCONNECT uevent.
+ * Check if we missed sending out a DISCONNECT uevent. This can
+ * happen if host PC resets and configures device really quick.
+ */
+ if (((uevent_envp == connected) &&
+ (last_uevent != USB_DISCONNECTED)) ||
+ ((uevent_envp == configured) &&
+ (last_uevent == USB_CONFIGURED))) {
+ pr_info("%s: sent missed DISCONNECT event\n", __func__);
+ kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE,
+ disconnected);
+ msleep(20);
+ }
+ /*
+ * Before sending out CONFIGURED uevent give function drivers
+ * a chance to wakeup userspace threads and notify disconnect
+ */
+ if (uevent_envp == configured)
+ msleep(50);
+
kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp);
+ last_uevent = next_state;
pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]);
} else {
pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
@@ -311,88 +405,410 @@
}
-#define MAX_ACM_INSTANCES 4
-struct acm_function_config {
- int instances;
+/*-------------------------------------------------------------------------*/
+/* Supported functions initialization */
+
+/* RMNET_SMD */
+static int rmnet_smd_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return rmnet_smd_bind_config(c);
+}
+
+static struct android_usb_function rmnet_smd_function = {
+ .name = "rmnet_smd",
+ .bind_config = rmnet_smd_function_bind_config,
};
-static int
-acm_function_init(struct android_usb_function *f,
- struct usb_composite_dev *cdev)
+/* RMNET_SDIO */
+static int rmnet_sdio_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
{
- f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL);
- if (!f->config)
- return -ENOMEM;
-
- return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES);
+ return rmnet_sdio_function_add(c);
}
+static struct android_usb_function rmnet_sdio_function = {
+ .name = "rmnet_sdio",
+ .bind_config = rmnet_sdio_function_bind_config,
+};
+
+/* RMNET_SMD_SDIO */
+static int rmnet_smd_sdio_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ return rmnet_smd_sdio_init();
+}
+
+static void rmnet_smd_sdio_function_cleanup(struct android_usb_function *f)
+{
+ rmnet_smd_sdio_cleanup();
+}
+
+static int rmnet_smd_sdio_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return rmnet_smd_sdio_function_add(c);
+}
+
+static struct device_attribute *rmnet_smd_sdio_attributes[] = {
+ &dev_attr_transport, NULL };
+
+static struct android_usb_function rmnet_smd_sdio_function = {
+ .name = "rmnet_smd_sdio",
+ .init = rmnet_smd_sdio_function_init,
+ .cleanup = rmnet_smd_sdio_function_cleanup,
+ .bind_config = rmnet_smd_sdio_bind_config,
+ .attributes = rmnet_smd_sdio_attributes,
+};
+
+/*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */
+#define MAX_XPORT_STR_LEN 50
+static char rmnet_transports[MAX_XPORT_STR_LEN];
+
+static void rmnet_function_cleanup(struct android_usb_function *f)
+{
+ frmnet_cleanup();
+}
+
+static int rmnet_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ int i;
+ int err = 0;
+ char *ctrl_name;
+ char *data_name;
+ char buf[MAX_XPORT_STR_LEN], *b;
+ static int rmnet_initialized, ports;
+
+ if (!rmnet_initialized) {
+ rmnet_initialized = 1;
+ strlcpy(buf, rmnet_transports, sizeof(buf));
+ b = strim(buf);
+ while (b) {
+ ctrl_name = strsep(&b, ",");
+ data_name = strsep(&b, ",");
+ if (ctrl_name && data_name) {
+ err = frmnet_init_port(ctrl_name, data_name);
+ if (err) {
+ pr_err("rmnet: Cannot open ctrl port:"
+ "'%s' data port:'%s'\n",
+ ctrl_name, data_name);
+ goto out;
+ }
+ ports++;
+ }
+ }
+
+ err = rmnet_gport_setup();
+ if (err) {
+ pr_err("rmnet: Cannot setup transports");
+ goto out;
+ }
+ }
+
+ for (i = 0; i < ports; i++) {
+ err = frmnet_bind_config(c, i);
+ if (err) {
+ pr_err("Could not bind rmnet%u config\n", i);
+ break;
+ }
+ }
+out:
+ return err;
+}
+
+static ssize_t rmnet_transports_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports);
+}
+
+static ssize_t rmnet_transports_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(rmnet_transports, buff, sizeof(rmnet_transports));
+
+ return size;
+}
+
+static struct device_attribute dev_attr_rmnet_transports =
+ __ATTR(transports, S_IRUGO | S_IWUSR,
+ rmnet_transports_show,
+ rmnet_transports_store);
+static struct device_attribute *rmnet_function_attributes[] = {
+ &dev_attr_rmnet_transports,
+ NULL };
+
+static struct android_usb_function rmnet_function = {
+ .name = "rmnet",
+ .cleanup = rmnet_function_cleanup,
+ .bind_config = rmnet_function_bind_config,
+ .attributes = rmnet_function_attributes,
+};
+
+
+/* MBIM - used with BAM */
+#define MAX_MBIM_INSTANCES 1
+
+static int mbim_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ return mbim_init(MAX_MBIM_INSTANCES);
+}
+
+static void mbim_function_cleanup(struct android_usb_function *f)
+{
+ fmbim_cleanup();
+}
+
+static int mbim_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return mbim_bind_config(c, 0);
+}
+
+static struct android_usb_function mbim_function = {
+ .name = "usb_mbim",
+ .cleanup = mbim_function_cleanup,
+ .bind_config = mbim_function_bind_config,
+ .init = mbim_function_init,
+};
+
+
+/* DIAG */
+static char diag_clients[32]; /*enabled DIAG clients- "diag[,diag_mdm]" */
+static ssize_t clients_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(diag_clients, buff, sizeof(diag_clients));
+
+ return size;
+}
+
+static DEVICE_ATTR(clients, S_IWUSR, NULL, clients_store);
+static struct device_attribute *diag_function_attributes[] =
+ { &dev_attr_clients, NULL };
+
+static int diag_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ return diag_setup();
+}
+
+static void diag_function_cleanup(struct android_usb_function *f)
+{
+ diag_cleanup();
+}
+
+static int diag_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ char *name;
+ char buf[32], *b;
+ int once = 0, err = -1;
+ int (*notify)(uint32_t, const char *);
+
+ strlcpy(buf, diag_clients, sizeof(buf));
+ b = strim(buf);
+
+ while (b) {
+ notify = NULL;
+ name = strsep(&b, ",");
+ /* Allow only first diag channel to update pid and serial no */
+ if (_android_dev->pdata && !once++)
+ notify = _android_dev->pdata->update_pid_and_serial_num;
+
+ if (name) {
+ err = diag_function_add(c, name, notify);
+ if (err)
+ pr_err("diag: Cannot open channel '%s'", name);
+ }
+ }
+
+ return err;
+}
+
+static struct android_usb_function diag_function = {
+ .name = "diag",
+ .init = diag_function_init,
+ .cleanup = diag_function_cleanup,
+ .bind_config = diag_function_bind_config,
+ .attributes = diag_function_attributes,
+};
+
+/* SERIAL */
+static char serial_transports[32]; /*enabled FSERIAL ports - "tty[,sdio]"*/
+static ssize_t serial_transports_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(serial_transports, buff, sizeof(serial_transports));
+
+ return size;
+}
+
+static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
+static struct device_attribute *serial_function_attributes[] =
+ { &dev_attr_transports, NULL };
+
+static void serial_function_cleanup(struct android_usb_function *f)
+{
+ gserial_cleanup();
+}
+
+static int serial_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ char *name;
+ char buf[32], *b;
+ int err = -1, i;
+ static int serial_initialized = 0, ports = 0;
+
+ if (serial_initialized)
+ goto bind_config;
+
+ serial_initialized = 1;
+ strlcpy(buf, serial_transports, sizeof(buf));
+ b = strim(buf);
+
+ while (b) {
+ name = strsep(&b, ",");
+
+ if (name) {
+ err = gserial_init_port(ports, name);
+ if (err) {
+ pr_err("serial: Cannot open port '%s'", name);
+ goto out;
+ }
+ ports++;
+ }
+ }
+ err = gport_setup(c);
+ if (err) {
+ pr_err("serial: Cannot setup transports");
+ goto out;
+ }
+
+bind_config:
+ for (i = 0; i < ports; i++) {
+ err = gser_bind_config(c, i);
+ if (err) {
+ pr_err("serial: bind_config failed for port %d", i);
+ goto out;
+ }
+ }
+
+out:
+ return err;
+}
+
+static struct android_usb_function serial_function = {
+ .name = "serial",
+ .cleanup = serial_function_cleanup,
+ .bind_config = serial_function_bind_config,
+ .attributes = serial_function_attributes,
+};
+
+/* ACM */
+static char acm_transports[32]; /*enabled ACM ports - "tty[,sdio]"*/
+static ssize_t acm_transports_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(acm_transports, buff, sizeof(acm_transports));
+
+ return size;
+}
+
+static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store);
+static struct device_attribute *acm_function_attributes[] = {
+ &dev_attr_acm_transports, NULL };
+
static void acm_function_cleanup(struct android_usb_function *f)
{
gserial_cleanup();
- kfree(f->config);
- f->config = NULL;
}
-static int
-acm_function_bind_config(struct android_usb_function *f,
- struct usb_configuration *c)
+static int acm_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
{
- int i;
- int ret = 0;
- struct acm_function_config *config = f->config;
+ char *name;
+ char buf[32], *b;
+ int err = -1, i;
+ static int acm_initialized, ports;
- for (i = 0; i < config->instances; i++) {
- ret = acm_bind_config(c, i);
- if (ret) {
- pr_err("Could not bind acm%u config\n", i);
- break;
+ if (acm_initialized)
+ goto bind_config;
+
+ acm_initialized = 1;
+ strlcpy(buf, acm_transports, sizeof(buf));
+ b = strim(buf);
+
+ while (b) {
+ name = strsep(&b, ",");
+
+ if (name) {
+ err = acm_init_port(ports, name);
+ if (err) {
+ pr_err("acm: Cannot open port '%s'", name);
+ goto out;
+ }
+ ports++;
+ }
+ }
+ err = acm_port_setup(c);
+ if (err) {
+ pr_err("acm: Cannot setup transports");
+ goto out;
+ }
+
+bind_config:
+ for (i = 0; i < ports; i++) {
+ err = acm_bind_config(c, i);
+ if (err) {
+ pr_err("acm: bind_config failed for port %d", i);
+ goto out;
}
}
- return ret;
+out:
+ return err;
}
-
-static ssize_t acm_instances_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct android_usb_function *f = dev_get_drvdata(dev);
- struct acm_function_config *config = f->config;
- return sprintf(buf, "%d\n", config->instances);
-}
-
-static ssize_t acm_instances_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct android_usb_function *f = dev_get_drvdata(dev);
- struct acm_function_config *config = f->config;
- int value;
-
- sscanf(buf, "%d", &value);
- if (value > MAX_ACM_INSTANCES)
- value = MAX_ACM_INSTANCES;
- config->instances = value;
- return size;
-}
-
-static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show,
- acm_instances_store);
-static struct device_attribute *acm_function_attributes[] = {
- &dev_attr_instances,
- NULL
-};
-
static struct android_usb_function acm_function = {
.name = "acm",
- .init = acm_function_init,
.cleanup = acm_function_cleanup,
.bind_config = acm_function_bind_config,
.attributes = acm_function_attributes,
};
+/* CCID */
+static int ccid_function_init(struct android_usb_function *f,
+ struct usb_composite_dev *cdev)
+{
+ return ccid_setup();
+}
-static int
-mtp_function_init(struct android_usb_function *f,
+static void ccid_function_cleanup(struct android_usb_function *f)
+{
+ ccid_cleanup();
+}
+
+static int ccid_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return ccid_bind_config(c);
+}
+
+static struct android_usb_function ccid_function = {
+ .name = "ccid",
+ .init = ccid_function_init,
+ .cleanup = ccid_function_cleanup,
+ .bind_config = ccid_function_bind_config,
+};
+
+static int mtp_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
return mtp_setup();
@@ -403,16 +819,13 @@
mtp_cleanup();
}
-static int
-mtp_function_bind_config(struct android_usb_function *f,
+static int mtp_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
return mtp_bind_config(c, false);
}
-static int
-ptp_function_init(struct android_usb_function *f,
- struct usb_composite_dev *cdev)
+static int ptp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
{
/* nothing to do - initialization is handled by mtp_function_init */
return 0;
@@ -423,9 +836,7 @@
/* nothing to do - cleanup is handled by mtp_function_cleanup */
}
-static int
-ptp_function_bind_config(struct android_usb_function *f,
- struct usb_configuration *c)
+static int ptp_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
{
return mtp_bind_config(c, true);
}
@@ -527,7 +938,8 @@
{
struct android_usb_function *f = dev_get_drvdata(dev);
struct rndis_function_config *config = f->config;
- return sprintf(buf, "%s\n", config->manufacturer);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", config->manufacturer);
}
static ssize_t rndis_manufacturer_store(struct device *dev,
@@ -538,7 +950,8 @@
if (size >= sizeof(config->manufacturer))
return -EINVAL;
- if (sscanf(buf, "%s", config->manufacturer) == 1)
+
+ if (sscanf(buf, "%255s", config->manufacturer) == 1)
return size;
return -1;
}
@@ -551,7 +964,8 @@
{
struct android_usb_function *f = dev_get_drvdata(dev);
struct rndis_function_config *config = f->config;
- return sprintf(buf, "%d\n", config->wceis);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", config->wceis);
}
static ssize_t rndis_wceis_store(struct device *dev,
@@ -576,7 +990,8 @@
{
struct android_usb_function *f = dev_get_drvdata(dev);
struct rndis_function_config *rndis = f->config;
- return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+
+ return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2],
rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]);
}
@@ -603,7 +1018,8 @@
{
struct android_usb_function *f = dev_get_drvdata(dev);
struct rndis_function_config *config = f->config;
- return sprintf(buf, "%04x\n", config->vendorID);
+
+ return snprintf(buf, PAGE_SIZE, "%04x\n", config->vendorID);
}
static ssize_t rndis_vendorID_store(struct device *dev,
@@ -671,6 +1087,7 @@
&common->luns[0].dev.kobj,
"lun");
if (err) {
+ fsg_common_release(&common->ref);
kfree(config);
return err;
}
@@ -698,7 +1115,7 @@
{
struct android_usb_function *f = dev_get_drvdata(dev);
struct mass_storage_function_config *config = f->config;
- return sprintf(buf, "%s\n", config->common->inquiry_string);
+ return snprintf(buf, PAGE_SIZE, "%s\n", config->common->inquiry_string);
}
static ssize_t mass_storage_inquiry_store(struct device *dev,
@@ -708,7 +1125,7 @@
struct mass_storage_function_config *config = f->config;
if (size >= sizeof(config->common->inquiry_string))
return -EINVAL;
- if (sscanf(buf, "%s", config->common->inquiry_string) != 1)
+ if (sscanf(buf, "%28s", config->common->inquiry_string) != 1)
return -EINVAL;
return size;
}
@@ -765,7 +1182,15 @@
static struct android_usb_function *supported_functions[] = {
+ &mbim_function,
+ &rmnet_smd_function,
+ &rmnet_sdio_function,
+ &rmnet_smd_sdio_function,
+ &rmnet_function,
+ &diag_function,
+ &serial_function,
&adb_function,
+ &ccid_function,
&acm_function,
&mtp_function,
&ptp_function,
@@ -775,6 +1200,31 @@
NULL
};
+static void android_cleanup_functions(struct android_usb_function **functions)
+{
+ struct android_usb_function *f;
+ struct device_attribute **attrs;
+ struct device_attribute *attr;
+
+ while (*functions) {
+ f = *functions++;
+
+ if (f->dev) {
+ device_destroy(android_class, f->dev->devt);
+ kfree(f->dev_name);
+ } else
+ continue;
+
+ if (f->cleanup)
+ f->cleanup(f);
+
+ attrs = f->attributes;
+ if (attrs) {
+ while ((attr = *attrs++))
+ device_remove_file(f->dev, attr);
+ }
+ }
+}
static int android_init_functions(struct android_usb_function **functions,
struct usb_composite_dev *cdev)
@@ -783,17 +1233,22 @@
struct android_usb_function *f;
struct device_attribute **attrs;
struct device_attribute *attr;
- int err;
- int index = 0;
+ int err = 0;
+ int index = 1; /* index 0 is for android0 device */
for (; (f = *functions++); index++) {
f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);
+ if (!f->dev_name) {
+ err = -ENOMEM;
+ goto err_out;
+ }
f->dev = device_create(android_class, dev->dev,
MKDEV(0, index), f, f->dev_name);
if (IS_ERR(f->dev)) {
pr_err("%s: Failed to create dev %s", __func__,
f->dev_name);
err = PTR_ERR(f->dev);
+ f->dev = NULL;
goto err_create;
}
@@ -802,7 +1257,7 @@
if (err) {
pr_err("%s: Failed to init %s", __func__,
f->name);
- goto err_out;
+ goto err_init;
}
}
@@ -814,35 +1269,26 @@
if (err) {
pr_err("%s: Failed to create function %s attributes",
__func__, f->name);
- goto err_out;
+ goto err_attrs;
}
}
return 0;
-err_out:
+err_attrs:
+ for (attr = *(attrs -= 2); attrs != f->attributes; attr = *(attrs--))
+ device_remove_file(f->dev, attr);
+ if (f->cleanup)
+ f->cleanup(f);
+err_init:
device_destroy(android_class, f->dev->devt);
err_create:
+ f->dev = NULL;
kfree(f->dev_name);
+err_out:
+ android_cleanup_functions(dev->functions);
return err;
}
-static void android_cleanup_functions(struct android_usb_function **functions)
-{
- struct android_usb_function *f;
-
- while (*functions) {
- f = *functions++;
-
- if (f->dev) {
- device_destroy(android_class, f->dev->devt);
- kfree(f->dev_name);
- }
-
- if (f->cleanup)
- f->cleanup(f);
- }
-}
-
static int
android_bind_enabled_functions(struct android_dev *dev,
struct usb_configuration *c)
@@ -889,6 +1335,32 @@
/*-------------------------------------------------------------------------*/
/* /sys/class/android_usb/android%d/ interface */
+static ssize_t remote_wakeup_show(struct device *pdev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ !!(android_config_driver.bmAttributes &
+ USB_CONFIG_ATT_WAKEUP));
+}
+
+static ssize_t remote_wakeup_store(struct device *pdev,
+ struct device_attribute *attr, const char *buff, size_t size)
+{
+ int enable = 0;
+
+ sscanf(buff, "%d", &enable);
+
+ pr_debug("android_usb: %s remote wakeup\n",
+ enable ? "enabling" : "disabling");
+
+ if (enable)
+ android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ else
+ android_config_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
+
+ return size;
+}
+
static ssize_t
functions_show(struct device *pdev, struct device_attribute *attr, char *buf)
{
@@ -899,7 +1371,7 @@
mutex_lock(&dev->mutex);
list_for_each_entry(f, &dev->enabled_functions, enabled_list)
- buff += sprintf(buff, "%s,", f->name);
+ buff += snprintf(buff, PAGE_SIZE, "%s,", f->name);
mutex_unlock(&dev->mutex);
@@ -926,7 +1398,7 @@
INIT_LIST_HEAD(&dev->enabled_functions);
- strncpy(buf, buff, sizeof(buf));
+ strlcpy(buf, buff, sizeof(buf));
b = strim(buf);
while (b) {
@@ -947,7 +1419,8 @@
char *buf)
{
struct android_dev *dev = dev_get_drvdata(pdev);
- return sprintf(buf, "%d\n", dev->enabled);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", dev->enabled);
}
static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
@@ -958,7 +1431,6 @@
struct android_usb_function *f;
int enabled = 0;
-
if (!cdev)
return -ENODEV;
@@ -996,6 +1468,26 @@
}
mutex_unlock(&dev->mutex);
+
+ return size;
+}
+
+static ssize_t pm_qos_show(struct device *pdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", dev->pm_qos);
+}
+
+static ssize_t pm_qos_store(struct device *pdev,
+ struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ struct android_dev *dev = dev_get_drvdata(pdev);
+
+ strlcpy(dev->pm_qos, buff, sizeof(dev->pm_qos));
+
return size;
}
@@ -1017,7 +1509,7 @@
state = "CONNECTED";
spin_unlock_irqrestore(&cdev->lock, flags);
out:
- return sprintf(buf, "%s\n", state);
+ return snprintf(buf, PAGE_SIZE, "%s\n", state);
}
#define DESCRIPTOR_ATTR(field, format_string) \
@@ -1025,7 +1517,8 @@
field ## _show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
- return sprintf(buf, format_string, device_desc.field); \
+ return snprintf(buf, PAGE_SIZE, \
+ format_string, device_desc.field); \
} \
static ssize_t \
field ## _store(struct device *dev, struct device_attribute *attr, \
@@ -1045,7 +1538,7 @@
field ## _show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
- return sprintf(buf, "%s", buffer); \
+ return snprintf(buf, PAGE_SIZE, "%s", buffer); \
} \
static ssize_t \
field ## _store(struct device *dev, struct device_attribute *attr, \
@@ -1071,7 +1564,11 @@
static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show,
functions_store);
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
+static DEVICE_ATTR(pm_qos, S_IRUGO | S_IWUSR,
+ pm_qos_show, pm_qos_store);
static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+static DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR,
+ remote_wakeup_show, remote_wakeup_store);
static struct device_attribute *android_usb_attributes[] = {
&dev_attr_idVendor,
@@ -1085,7 +1582,9 @@
&dev_attr_iSerial,
&dev_attr_functions,
&dev_attr_enable,
+ &dev_attr_pm_qos,
&dev_attr_state,
+ &dev_attr_remote_wakeup,
NULL
};
@@ -1143,9 +1642,10 @@
device_desc.iProduct = id;
/* Default strings - should be updated by userspace */
- strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1);
- strncpy(product_string, "Android", sizeof(product_string) - 1);
- strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
+ strlcpy(manufacturer_string, "Android",
+ sizeof(manufacturer_string) - 1);
+ strlcpy(product_string, "Android", sizeof(product_string) - 1);
+ strlcpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1);
id = usb_string_id(cdev);
if (id < 0)
@@ -1153,6 +1653,9 @@
strings_dev[STRING_SERIAL_IDX].id = id;
device_desc.iSerialNumber = id;
+ if (gadget_is_otg(cdev->gadget))
+ android_config_driver.descriptors = otg_desc;
+
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
@@ -1162,7 +1665,6 @@
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
}
- usb_gadget_set_selfpowered(gadget);
dev->cdev = cdev;
return 0;
@@ -1172,6 +1674,9 @@
{
struct android_dev *dev = _android_dev;
+ manufacturer_string[0] = '\0';
+ product_string[0] = '\0';
+ serial_string[0] = '0';
cancel_work_sync(&dev->work);
android_cleanup_functions(dev->functions);
return 0;
@@ -1182,7 +1687,7 @@
.dev = &device_desc,
.strings = dev_strings,
.unbind = android_usb_unbind,
- .max_speed = USB_SPEED_HIGH,
+ .max_speed = USB_SPEED_SUPER
};
static int
@@ -1267,19 +1772,86 @@
return 0;
}
-
-static int __init init(void)
+static void android_destroy_device(struct android_dev *dev)
{
- struct android_dev *dev;
- int err;
+ struct device_attribute **attrs = android_usb_attributes;
+ struct device_attribute *attr;
+
+ while ((attr = *attrs++))
+ device_remove_file(dev->dev, attr);
+ device_destroy(android_class, dev->dev->devt);
+}
+
+static int __devinit android_probe(struct platform_device *pdev)
+{
+ struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+ struct android_dev *dev = _android_dev;
+ int ret = 0;
+
+ dev->pdata = pdata;
android_class = class_create(THIS_MODULE, "android_usb");
if (IS_ERR(android_class))
return PTR_ERR(android_class);
+ ret = android_create_device(dev);
+ if (ret) {
+ pr_err("%s(): android_create_device failed\n", __func__);
+ goto err_dev;
+ }
+
+ ret = usb_composite_probe(&android_usb_driver, android_bind);
+ if (ret) {
+ pr_err("%s(): Failed to register android "
+ "composite driver\n", __func__);
+ goto err_probe;
+ }
+
+ /* pm qos request to prevent apps idle power collapse */
+ if (pdata && pdata->swfi_latency)
+ pm_qos_add_request(&dev->pm_qos_req_dma,
+ PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+ strlcpy(dev->pm_qos, "high", sizeof(dev->pm_qos));
+
+ return ret;
+err_probe:
+ android_destroy_device(dev);
+err_dev:
+ class_destroy(android_class);
+ return ret;
+}
+
+static int android_remove(struct platform_device *pdev)
+{
+ struct android_dev *dev = _android_dev;
+ struct android_usb_platform_data *pdata = pdev->dev.platform_data;
+
+ android_destroy_device(dev);
+ class_destroy(android_class);
+ usb_composite_unregister(&android_usb_driver);
+ if (pdata && pdata->swfi_latency)
+ pm_qos_remove_request(&dev->pm_qos_req_dma);
+
+ return 0;
+}
+
+static struct platform_driver android_platform_driver = {
+ .driver = { .name = "android_usb"},
+ .probe = android_probe,
+ .remove = android_remove,
+};
+
+static int __init init(void)
+{
+ struct android_dev *dev;
+ int ret;
+
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
+ if (!dev) {
+ pr_err("%s(): Failed to alloc memory for android_dev\n",
+ __func__);
return -ENOMEM;
+ }
dev->disable_depth = 1;
dev->functions = supported_functions;
@@ -1287,27 +1859,26 @@
INIT_WORK(&dev->work, android_work);
mutex_init(&dev->mutex);
- err = android_create_device(dev);
- if (err) {
- class_destroy(android_class);
- kfree(dev);
- return err;
- }
-
_android_dev = dev;
/* Override composite driver functions */
composite_driver.setup = android_setup;
composite_driver.disconnect = android_disconnect;
- return usb_composite_probe(&android_usb_driver, android_bind);
+ ret = platform_driver_register(&android_platform_driver);
+ if (ret) {
+ pr_err("%s(): Failed to register android"
+ "platform driver\n", __func__);
+ kfree(dev);
+ }
+
+ return ret;
}
module_init(init);
static void __exit cleanup(void)
{
- usb_composite_unregister(&android_usb_driver);
- class_destroy(android_class);
+ platform_driver_unregister(&android_platform_driver);
kfree(_android_dev);
_android_dev = NULL;
}