diag: Add support for mutiple HSIC instances

On a future target, applications processor will talk to
two MDMs simultaneously. This change will enable multiple
HSIC connections towards this configuration.

Change-Id: I2138b1d458936eddb8276a12e92916a4893d922b
Signed-off-by: Ravi Aravamudhan <aravamud@codeaurora.org>
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 53d2c93..376812c 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, 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,6 +30,9 @@
 #define DRIVER_DESC	"USB host diag bridge driver"
 #define DRIVER_VERSION	"1.0"
 
+#define MAX_DIAG_BRIDGE_DEVS	2
+#define AUTOSUSP_DELAY_WITH_USB 1000
+
 struct diag_bridge {
 	struct usb_device	*udev;
 	struct usb_interface	*ifc;
@@ -49,12 +51,18 @@
 	unsigned		pending_reads;
 	unsigned		pending_writes;
 };
-struct diag_bridge *__dev;
+struct diag_bridge *__dev[MAX_DIAG_BRIDGE_DEVS];
 
-int diag_bridge_open(struct diag_bridge_ops *ops)
+int diag_bridge_open(int id, struct diag_bridge_ops *ops)
 {
-	struct diag_bridge	*dev = __dev;
+	struct diag_bridge	*dev;
 
+	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
+		pr_err("Invalid device ID");
+		return -ENODEV;
+	}
+
+	dev = __dev[id];
 	if (!dev) {
 		pr_err("dev is null");
 		return -ENODEV;
@@ -77,16 +85,23 @@
 static void diag_bridge_delete(struct kref *kref)
 {
 	struct diag_bridge *dev = container_of(kref, struct diag_bridge, kref);
+	int id = dev->pdev->id;
 
 	usb_put_dev(dev->udev);
-	__dev = 0;
+	__dev[id] = 0;
 	kfree(dev);
 }
 
-void diag_bridge_close(void)
+void diag_bridge_close(int id)
 {
-	struct diag_bridge	*dev = __dev;
+	struct diag_bridge	*dev;
 
+	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
+		pr_err("Invalid device ID");
+		return;
+	}
+
+	dev = __dev[id];
 	if (!dev) {
 		pr_err("dev is null");
 		return;
@@ -128,15 +143,21 @@
 	kref_put(&dev->kref, diag_bridge_delete);
 }
 
-int diag_bridge_read(char *data, int size)
+int diag_bridge_read(int id, char *data, int size)
 {
 	struct urb		*urb = NULL;
 	unsigned int		pipe;
-	struct diag_bridge	*dev = __dev;
+	struct diag_bridge	*dev;
 	int			ret;
 
+	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
+		pr_err("Invalid device ID");
+		return -ENODEV;
+	}
+
 	pr_debug("reading %d bytes", size);
 
+	dev = __dev[id];
 	if (!dev) {
 		pr_err("device is disconnected");
 		return -ENODEV;
@@ -230,15 +251,21 @@
 	kref_put(&dev->kref, diag_bridge_delete);
 }
 
-int diag_bridge_write(char *data, int size)
+int diag_bridge_write(int id, char *data, int size)
 {
 	struct urb		*urb = NULL;
 	unsigned int		pipe;
-	struct diag_bridge	*dev = __dev;
+	struct diag_bridge	*dev;
 	int			ret;
 
+	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
+		pr_err("Invalid device ID");
+		return -ENODEV;
+	}
+
 	pr_debug("writing %d bytes", size);
 
+	dev = __dev[id];
 	if (!dev) {
 		pr_err("device is disconnected");
 		return -ENODEV;
@@ -315,28 +342,30 @@
 static ssize_t diag_read_stats(struct file *file, char __user *ubuf,
 				size_t count, loff_t *ppos)
 {
-	struct diag_bridge	*dev = __dev;
 	char			*buf;
-	int			ret;
-
-	if (!dev)
-		return -ENODEV;
+	int			i, ret = 0;
 
 	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
 
-	ret = scnprintf(buf, DEBUG_BUF_SIZE,
-			"epin:%d, epout:%d\n"
-			"bytes to host: %lu\n"
-			"bytes to mdm: %lu\n"
-			"pending reads: %u\n"
-			"pending writes: %u\n"
-			"last error: %d\n",
-			dev->in_epAddr, dev->out_epAddr,
-			dev->bytes_to_host, dev->bytes_to_mdm,
-			dev->pending_reads, dev->pending_writes,
-			dev->err);
+	for (i = 0; i < MAX_DIAG_BRIDGE_DEVS; i++) {
+		struct diag_bridge *dev = __dev[i];
+		if (!dev)
+			continue;
+
+		ret += scnprintf(buf, DEBUG_BUF_SIZE,
+				"epin:%d, epout:%d\n"
+				"bytes to host: %lu\n"
+				"bytes to mdm: %lu\n"
+				"pending reads: %u\n"
+				"pending writes: %u\n"
+				"last error: %d\n",
+				dev->in_epAddr, dev->out_epAddr,
+				dev->bytes_to_host, dev->bytes_to_mdm,
+				dev->pending_reads, dev->pending_writes,
+				dev->err);
+	}
 
 	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
 	kfree(buf);
@@ -346,11 +375,14 @@
 static ssize_t diag_reset_stats(struct file *file, const char __user *buf,
 				 size_t count, loff_t *ppos)
 {
-	struct diag_bridge	*dev = __dev;
+	int i;
 
-	if (dev) {
-		dev->bytes_to_host = dev->bytes_to_mdm = 0;
-		dev->pending_reads = dev->pending_writes = 0;
+	for (i = 0; i < MAX_DIAG_BRIDGE_DEVS; i++) {
+		struct diag_bridge *dev = __dev[i];
+		if (dev) {
+			dev->bytes_to_host = dev->bytes_to_mdm = 0;
+			dev->pending_reads = dev->pending_writes = 0;
+		}
 	}
 
 	return count;
@@ -394,8 +426,7 @@
 	struct diag_bridge		*dev;
 	struct usb_host_interface	*ifc_desc;
 	struct usb_endpoint_descriptor	*ep_desc;
-	int				i;
-	int				ret = -ENOMEM;
+	int				i, devid, ret = -ENOMEM;
 	__u8				ifc_num;
 
 	pr_debug("id:%lu", id->driver_info);
@@ -406,18 +437,30 @@
 	if (ifc_num != id->driver_info)
 		return -ENODEV;
 
+	/* This needs to figure out ID based on PID and/or host bus type */
+	devid = 0;
+
+	if (devid < 0 || devid >= MAX_DIAG_BRIDGE_DEVS)
+		return -ENODEV;
+
+	/* already probed? */
+	if (__dev[devid]) {
+		pr_err("Diag device already probed");
+		return -ENODEV;
+	}
+
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (!dev) {
 		pr_err("unable to allocate dev");
 		return -ENOMEM;
 	}
-	dev->pdev = platform_device_alloc("diag_bridge", -1);
+	dev->pdev = platform_device_alloc("diag_bridge", devid);
 	if (!dev->pdev) {
 		pr_err("unable to allocate platform device");
 		kfree(dev);
 		return -ENOMEM;
 	}
-	__dev = dev;
+	__dev[devid] = dev;
 
 	dev->udev = usb_get_dev(interface_to_usbdev(ifc));
 	dev->ifc = ifc;