msm: hsic_sysmon: Support multiple instances

Enhance the driver to support (currently up to 2) multiple sysmon
interface instances. This is done by classifying the supported PIDs
as either the first or second allowable instance. Add PID 9079 and
allow it to be instantiated as the second instance. Also make use
of the new USB_DEVICE_INTERFACE_NUMBER macro.

Change-Id: I6f9fbf37d151dcce1d0bcf5204460ce0732730f1
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/arch/arm/mach-msm/hsic_sysmon.c b/arch/arm/mach-msm/hsic_sysmon.c
index 52ab934..0a9e2e3 100644
--- a/arch/arm/mach-msm/hsic_sysmon.c
+++ b/arch/arm/mach-msm/hsic_sysmon.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -239,7 +239,7 @@
 		if (!hs)
 			continue;
 
-		ret += scnprintf(buf, DEBUG_BUF_SIZE,
+		ret += scnprintf(buf + ret, DEBUG_BUF_SIZE - ret,
 				"---HSIC Sysmon #%d---\n"
 				"epin:%d, epout:%d\n"
 				"bytes to host: %d\n"
@@ -324,15 +324,6 @@
 	struct usb_endpoint_descriptor	*ep_desc;
 	int				i;
 	int				ret = -ENOMEM;
-	__u8				ifc_num;
-
-	pr_debug("id:%lu", id->driver_info);
-
-	ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber;
-
-	/* is this the interface we're looking for? */
-	if (ifc_num != id->driver_info)
-		return -ENODEV;
 
 	hs = kzalloc(sizeof(*hs), GFP_KERNEL);
 	if (!hs) {
@@ -367,12 +358,17 @@
 		goto error;
 	}
 
-	hs->id = HSIC_SYSMON_DEV_EXT_MODEM;
-	hsic_sysmon_devices[HSIC_SYSMON_DEV_EXT_MODEM] = hs;
+	hs->id = HSIC_SYSMON_DEV_EXT_MODEM + id->driver_info;
+	if (hs->id >= NUM_HSIC_SYSMON_DEVS) {
+		pr_warn("invalid dev id(%d)", hs->id);
+		hs->id = 0;
+	}
+
+	hsic_sysmon_devices[hs->id] = hs;
 	usb_set_intfdata(ifc, hs);
 
 	hs->pdev.name = "sys_mon";
-	hs->pdev.id = SYSMON_SS_EXT_MODEM;
+	hs->pdev.id = SYSMON_SS_EXT_MODEM + hs->id;
 	hs->pdev.dev.release = hsic_sysmon_pdev_release;
 	platform_device_register(&hs->pdev);
 
@@ -406,10 +402,11 @@
 	return 0;
 }
 
-/* driver_info maps to the interface number corresponding to sysmon */
+/* driver_info is the instance number when multiple devices are present */
 static const struct usb_device_id hsic_sysmon_ids[] = {
-	{ USB_DEVICE(0x5c6, 0x9048), .driver_info = 1, },
-	{ USB_DEVICE(0x5c6, 0x904C), .driver_info = 1, },
+	{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 1), .driver_info = 0, },
+	{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904C, 1), .driver_info = 0, },
+	{ USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 1), .driver_info = 1, },
 	{} /* terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, hsic_sysmon_ids);
diff --git a/arch/arm/mach-msm/hsic_sysmon.h b/arch/arm/mach-msm/hsic_sysmon.h
index 983f464..9655dc0 100644
--- a/arch/arm/mach-msm/hsic_sysmon.h
+++ b/arch/arm/mach-msm/hsic_sysmon.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -18,6 +18,7 @@
  */
 enum hsic_sysmon_device_id {
 	HSIC_SYSMON_DEV_EXT_MODEM,
+	HSIC_SYSMON_DEV_EXT_MODEM_2,
 	NUM_HSIC_SYSMON_DEVS
 };
 
diff --git a/arch/arm/mach-msm/hsic_sysmon_test.c b/arch/arm/mach-msm/hsic_sysmon_test.c
index bc60c6e..fac6575 100644
--- a/arch/arm/mach-msm/hsic_sysmon_test.c
+++ b/arch/arm/mach-msm/hsic_sysmon_test.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -15,6 +15,7 @@
 
 #include <linux/slab.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/device.h>
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
@@ -36,13 +37,14 @@
 				 size_t count, loff_t *ppos)
 {
 	struct sysmon_test_dev *dev = sysmon_dev;
+	enum hsic_sysmon_device_id id =
+				(enum hsic_sysmon_device_id)file->private_data;
 	int ret;
 
 	if (!dev)
 		return -ENODEV;
 
-	ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, dev->buf, RD_BUF_SIZE,
-				&dev->buflen, 3000);
+	ret = hsic_sysmon_read(id, dev->buf, RD_BUF_SIZE, &dev->buflen, 3000);
 	if (!ret)
 		return simple_read_from_buffer(ubuf, count, ppos,
 					dev->buf, dev->buflen);
@@ -53,7 +55,9 @@
 static ssize_t sysmon_test_write(struct file *file, const char __user *ubuf,
 				 size_t count, loff_t *ppos)
 {
-	struct sysmon_test_dev	*dev = sysmon_dev;
+	struct sysmon_test_dev *dev = sysmon_dev;
+	enum hsic_sysmon_device_id id =
+				(enum hsic_sysmon_device_id)file->private_data;
 	int ret;
 
 	if (!dev)
@@ -64,8 +68,7 @@
 		return 0;
 	}
 
-	ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
-				dev->buf, count, 1000);
+	ret = hsic_sysmon_write(id, dev->buf, count, 1000);
 	if (ret < 0) {
 		pr_err("error writing to hsic_sysmon");
 		return ret;
@@ -76,38 +79,44 @@
 
 static int sysmon_test_open(struct inode *inode, struct file *file)
 {
-	return hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
+	file->private_data = inode->i_private;
+	return hsic_sysmon_open((enum hsic_sysmon_device_id)inode->i_private);
 }
 
 static int sysmon_test_release(struct inode *inode, struct file *file)
 {
-	hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
+	hsic_sysmon_close((enum hsic_sysmon_device_id)inode->i_private);
 	return 0;
 }
 
-static struct dentry *dfile;
-const struct file_operations sysmon_test_ops = {
+static const struct file_operations sysmon_test_ops = {
 	.read = sysmon_test_read,
 	.write = sysmon_test_write,
 	.open = sysmon_test_open,
 	.release = sysmon_test_release
 };
 
+static struct dentry *dfile0, *dfile1;
+
 static int __init sysmon_test_init(void)
 {
 	sysmon_dev = kzalloc(sizeof(*sysmon_dev), GFP_KERNEL);
 	if (!sysmon_dev)
 		return -ENOMEM;
 
-	dfile = debugfs_create_file("hsic_sysmon_test", 0666, NULL,
-			0, &sysmon_test_ops);
+	dfile0 = debugfs_create_file("hsic_sysmon_test.0", 0666, NULL,
+			(void *)HSIC_SYSMON_DEV_EXT_MODEM, &sysmon_test_ops);
+	dfile1 = debugfs_create_file("hsic_sysmon_test.1", 0666, NULL,
+			(void *)HSIC_SYSMON_DEV_EXT_MODEM_2, &sysmon_test_ops);
 	return 0;
 }
 
 static void __exit sysmon_test_exit(void)
 {
-	if (dfile)
-		debugfs_remove(dfile);
+	if (dfile0)
+		debugfs_remove(dfile0);
+	if (dfile1)
+		debugfs_remove(dfile1);
 	kfree(sysmon_dev);
 }