[PATCH 3/5] USB: OTG: Start using new otg tpl.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Introduces otg_set_error and start using it when we
want to show otg_errors.

Based on previous patch from Tony Lindgren <tony@xxxxxxxxxxx>

Signed-off-by: Felipe Balbi <me@xxxxxxxxxxxxxxx>
---
 drivers/usb/core/hub.c           |   16 ++++-
 drivers/usb/core/otg.c           |  127 ++++++++++++++++++++++++++++++++-----
 drivers/usb/core/otg_whitelist.h |   20 ------
 drivers/usb/core/sysfs.c         |  131 ++++++++++++++++++++++++++++++++++++++
 include/linux/usb/otg.h          |   19 ++++++
 5 files changed, 273 insertions(+), 40 deletions(-)
 delete mode 100644 drivers/usb/core/otg_whitelist.h

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b04d232..0b1a7b9 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -23,6 +23,8 @@
 #include <linux/mutex.h>
 #include <linux/freezer.h>
 
+#include <linux/usb/otg.h>
+
 #include <asm/semaphore.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -1252,6 +1254,7 @@ static int usb_configure_device_otg(struct usb_device *udev)
 					le16_to_cpu(udev->config[0].desc.wTotalLength),
 					USB_DT_OTG, (void **) &desc) == 0) {
 			if (desc->bmAttributes & USB_OTG_HNP) {
+				struct otg_transceiver	*xceiv = otg_get_transceiver();
 				unsigned		port1 = udev->portnum;
 
 				dev_info(&udev->dev,
@@ -1270,19 +1273,23 @@ static int usb_configure_device_otg(struct usb_device *udev)
 						: USB_DEVICE_A_ALT_HNP_SUPPORT,
 					0, NULL, 0, USB_CTRL_SET_TIMEOUT);
 				if (err < 0) {
-					/* OTG MESSAGE: report errors here,
-					 * customize to match your product.
-					 */
+					otg_set_error(xceiv,
+						OTG_ERR_DEVICE_NOT_RESPONDING);
 					dev_info(&udev->dev,
 						"can't set HNP mode; %d\n",
 						err);
 					bus->b_hnp_enable = 0;
 				}
+				tpl_enabled = xceiv->tpl_enabled;
+				put_device(xceiv->dev);
 			}
 		}
 	}
 
-	if (!is_targeted(udev)) {
+	if (err == 0)
+		err = otg_targeted(udev);
+
+	if (err != OTG_ERR_DEVICE_SUPPORTED && tpl_enabled) {
 
 		/* Maybe it can talk to us, though we can't talk to it.
 		 * (Includes HNP test device.)
@@ -1316,6 +1323,7 @@ fail:
 static int usb_configure_device(struct usb_device *udev)
 {
 	int err;
+	unsigned tpl_enabled = 0;
 
 	if (udev->config == NULL) {
 		err = usb_get_configuration(udev);
diff --git a/drivers/usb/core/otg.c b/drivers/usb/core/otg.c
index 531afa6..e9ece31 100644
--- a/drivers/usb/core/otg.c
+++ b/drivers/usb/core/otg.c
@@ -2,6 +2,7 @@
  * drivers/usb/core/otg.c
  *
  * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2007-2008 Nokia Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -9,15 +10,10 @@
  * (at your option) any later version.
  */
 
-#include <linux/device.h>
-#include <linux/mod_devicetable.h>
-#include <linux/byteorder/generic.h>
-#include <linux/gfp.h>
+#include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/usb/otg.h>
 
-#include "otg_whitelist.h"
-
 static struct otg_transceiver *xceiv;
 
 int otg_set_transceiver(struct otg_transceiver *x)
@@ -37,6 +33,22 @@ struct otg_transceiver *otg_get_transceiver(void)
 }
 EXPORT_SYMBOL(otg_get_transceiver);
 
+/* OTG MESSAGE: report errors here, customize to match your product */
+void otg_set_error(struct otg_transceiver *x, enum usb_otg_error errno)
+{
+	if (!x)
+		return;
+	if (!x->tpl_enabled)
+		x->last_error = OTG_ERR_DEVICE_SUPPORTED;
+	else
+		x->last_error = errno;
+
+	sysfs_notify(&x->host->root_hub->dev.kobj, NULL, "otg_last_error");
+}
+EXPORT_SYMBOL(otg_set_error);
+
+/*-------------------------------------------------------------------------*/
+
 #ifdef	CONFIG_USB_OTG_WHITELIST
 
 /*
@@ -46,7 +58,7 @@ EXPORT_SYMBOL(otg_get_transceiver);
  * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
  */
 
-static struct usb_device_id whitelist_table [] = {
+static struct usb_device_id whitelist_table [] __initdata = {
 
 /* hubs are optional in OTG, but very handy ... */
 { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
@@ -76,23 +88,74 @@ static struct usb_device_id whitelist_table [] = {
 { }	/* Terminating entry */
 };
 
-int is_targeted(struct usb_device *dev)
+struct otg_device {
+	struct list_head	list;
+	struct usb_device_id	id;
+};
+
+static struct list_head	tpl_devices;
+
+static void tpl_add_device(struct usb_device_id *id)
+{
+	struct otg_device	*otg;
+
+	otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+	if (!otg)
+		return;
+	INIT_LIST_HEAD(&otg->list);
+	memcpy(&otg->id, id, sizeof(struct usb_device_id));
+	list_add_tail(&otg->list, &tpl_devices);
+}
+
+static void tpl_add_devices(struct usb_device_id *whitelist_table)
+{
+	struct usb_device_id	*id;
+
+	for (id = whitelist_table; id->match_flags; id++)
+		tpl_add_device(id);
+}
+
+ssize_t otg_print_whitelist(char *buf)
+{
+	struct otg_device	*otg;
+	ssize_t			len = 0;
+
+	list_for_each_entry(otg, &tpl_devices, list)
+		len += sprintf(buf + len, "%04x:%04x\t%02x %02x %02x\n",
+				le16_to_cpu(otg->id.idVendor),
+				le16_to_cpu(otg->id.idProduct),
+				otg->id.bDeviceClass,
+				otg->id.bDeviceSubClass,
+				otg->id.bDeviceProtocol);
+	return len;
+}
+
+int otg_targeted(struct usb_device *dev)
 {
-	struct usb_device_id	*id = whitelist_table;
+	struct otg_device	*otg;
+
+	if (!xceiv || !xceiv->tpl_enabled)
+		return OTG_ERR_DEVICE_SUPPORTED;
 
 	/* possible in developer configs only! */
 	if (!dev->bus->otg_port)
-		return 1;
+		goto targeted;
 
 	/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
 	if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
 	     le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
-		return 0;
+		goto err;
+
+	/* roothub don't need to be tested here */
+	if (!dev->parent)
+		goto targeted;
 
 	/* NOTE: can't use usb_match_id() since interface caches
 	 * aren't set up yet. this is cut/paste from that code.
 	 */
-	for (id = whitelist_table; id->match_flags; id++) {
+	list_for_each_entry(otg, &tpl_devices, list) {
+		struct usb_device_id	*id = &otg->id;
+
 		if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
 		    id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
 			continue;
@@ -123,18 +186,50 @@ int is_targeted(struct usb_device *dev)
 		    (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
 			continue;
 
-		return 1;
+		goto targeted;
 	}
 
 	/* add other match criteria here ... */
 
-
-	/* OTG MESSAGE: report errors here, customize to match your product */
-	dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
+err:
+	dev_err(&dev->dev, "%s v%04x p%04x is not supported\n",
+		dev->descriptor.bDeviceClass & USB_CLASS_HUB ? "hub" : "device",
 		le16_to_cpu(dev->descriptor.idVendor),
 		le16_to_cpu(dev->descriptor.idProduct));
+	if (dev->descriptor.bDeviceClass & USB_CLASS_HUB)
+		otg_set_error(xceiv, OTG_ERR_HUB_NOT_SUPPORTED);
+	else
+		otg_set_error(xceiv, OTG_ERR_DEVICE_NOT_SUPPORTED);
+
+	return xceiv->last_error;
+
+targeted:
+	xceiv->last_error = OTG_ERR_DEVICE_SUPPORTED;
+	return xceiv->last_error;
+}
+
+static int __init tpl_init(void)
+{
+	INIT_LIST_HEAD(&tpl_devices);
+	tpl_add_devices(whitelist_table);
+	if (xceiv)
+		xceiv->tpl_enabled = 1;
 
 	return 0;
 }
 
+static void __exit tpl_exit(void)
+{
+	struct otg_device	*otg;
+
+	if (xceiv)
+		xceiv->tpl_enabled = 0;
+
+	list_for_each_entry(otg, &tpl_devices, list)
+		kfree(otg);
+}
+
+module_init(tpl_init);
+module_exit(tpl_exit);
+
 #endif	/* CONFIG_USB_OTG_WHITELIST */
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
deleted file mode 100644
index d6b352e..0000000
--- a/drivers/usb/core/otg_whitelist.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * drivers/usb/core/otg_whitelist.h
- *
- * Copyright (C) 2004 Texas Instruments
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifdef	CONFIG_USB_OTG_WHITELIST
-extern int is_targeted(struct usb_device *);
-#else
-static inline int is_targeted(struct usb_device *d)
-{
-	return 0;
-}
-#endif
-
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 32bd130..9efc01a 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/usb.h>
+#include <linux/usb/otg.h>
 #include "usb.h"
 
 /* Active configuration fields */
@@ -91,6 +92,132 @@ usb_string_attr(product);
 usb_string_attr(manufacturer);
 usb_string_attr(serial);
 
+#ifdef CONFIG_USB_OTG
+
+static ssize_t
+otg_last_error_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct otg_transceiver	*xceiv = otg_get_transceiver();
+	char			*msg;
+	ssize_t			ret;
+
+	if (!xceiv)
+		return -ENODEV;
+
+	switch (xceiv->last_error) {
+	case OTG_ERR_DEVICE_SUPPORTED:
+		msg = "is supported";
+		break;
+	case OTG_ERR_DEVICE_NOT_SUPPORTED:
+		msg = "is not supported";
+		break;
+	case OTG_ERR_DEVICE_NOT_RESPONDING:
+		msg = "is not responding";
+		break;
+	default:
+		msg = "unknown error";
+	}
+
+	ret = sprintf(buf, "OTG%02d: Device %s\n", xceiv->last_error, msg);
+	put_device(xceiv->dev);
+
+	return ret;
+}
+static DEVICE_ATTR(otg_last_error, 0444, otg_last_error_show, NULL);
+
+#ifdef CONFIG_USB_OTG_WHITELIST
+
+static ssize_t
+otg_whitelist_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return otg_print_whitelist(buf);
+}
+static DEVICE_ATTR(otg_whitelist, 0644, otg_whitelist_show, NULL);
+
+static ssize_t
+otg_whitelist_status_store(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t n)
+{
+	struct otg_transceiver	*xceiv = otg_get_transceiver();
+	int			enable;
+
+	sscanf(buf, "%d", &enable);
+	if (!xceiv)
+		return 0;
+	if (enable)
+		xceiv->tpl_enabled = 1;
+	else
+		xceiv->tpl_enabled = 0;
+	put_device(xceiv->dev);
+
+	return n;
+}
+
+static ssize_t
+otg_whitelist_status_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct otg_transceiver	*xceiv = otg_get_transceiver();
+	int			ret;
+
+	if (!xceiv)
+		return -ENODEV;
+
+	if (xceiv->tpl_enabled)
+		ret = sprintf(buf, "enabled\n");
+	else
+		ret = sprintf(buf, "disabled\n");
+	put_device(xceiv->dev);
+
+	return ret;
+}
+static DEVICE_ATTR(otg_whitelist_status, 0644, otg_whitelist_status_show,
+			otg_whitelist_status_store);
+
+#endif	/* CONFIG_USB_OTG_WHITELIST */
+
+static struct attribute *otg_dev_attrs[] = {
+	&dev_attr_otg_last_error.attr,
+
+#ifdef	CONFIG_USB_OTG_WHITELIST
+	&dev_attr_otg_whitelist.attr,
+	&dev_attr_otg_whitelist_status.attr,
+#endif
+
+};
+
+static int add_otg_attributes(struct device *dev)
+{
+	struct usb_device	*udev = to_usb_device(dev);
+	struct usb_bus		*bus = udev->bus;
+	int			ret = 0, i;
+
+	if (is_usb_device(dev) && !udev->parent && bus->otg_port)
+		for (i = 0; i < ARRAY_SIZE(otg_dev_attrs); i++) {
+			ret = sysfs_create_file(&dev->kobj, otg_dev_attrs[i]);
+			if (ret != 0)
+				return ret;
+		}
+
+	return ret;
+}
+
+static void remove_otg_attributes(struct device *dev)
+{
+	struct usb_device	*udev = to_usb_device(dev);
+	struct usb_bus		*bus = udev->bus;
+	int			i;
+
+	if (is_usb_device(dev) && !udev->parent && bus->otg_port)
+		for (i = 0; i < ARRAY_SIZE(otg_dev_attrs); i++)
+			sysfs_remove_file(&dev->kobj, otg_dev_attrs[i]);
+}
+
+#else
+#define add_otg_attributes(x)		0
+#define remove_otg_attributes(x)	do {} while (0)
+#endif /* CONFIG_USB_OTG */
+
 static ssize_t
 show_speed(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -575,6 +702,9 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
 		if (retval)
 			goto error;
 	}
+	retval = add_otg_attributes(dev);
+	if (retval)
+		goto error;
 	retval = usb_create_ep_files(dev, &udev->ep0, udev);
 	if (retval)
 		goto error;
@@ -593,6 +723,7 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
 	device_remove_file(dev, &dev_attr_product);
 	device_remove_file(dev, &dev_attr_serial);
 	remove_power_attributes(dev);
+	remove_otg_attributes(dev);
 	remove_persist_attributes(dev);
 	device_remove_bin_file(dev, &dev_bin_attr_descriptors);
 	sysfs_remove_group(&dev->kobj, &dev_attr_grp);
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 9897f7a..e748030 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -31,6 +31,13 @@ enum usb_otg_state {
 	OTG_STATE_A_VBUS_ERR,
 };
 
+enum usb_otg_error {
+	OTG_ERR_DEVICE_SUPPORTED,
+	OTG_ERR_DEVICE_NOT_SUPPORTED,
+	OTG_ERR_DEVICE_NOT_RESPONDING,
+	OTG_ERR_HUB_NOT_SUPPORTED,
+};
+
 /*
  * the otg driver needs to interact with both device side and host side
  * usb controllers.  it decides which controller is active at a given
@@ -42,7 +49,10 @@ struct otg_transceiver {
 	const char		*label;
 
 	u8			default_a;
+	unsigned		tpl_enabled:1;
+
 	enum usb_otg_state	state;
+	enum usb_otg_error	last_error;
 
 	struct usb_bus		*host;
 	struct usb_gadget	*gadget;
@@ -89,6 +99,7 @@ otg_start_hnp(struct otg_transceiver *otg)
 	return otg->start_hnp(otg);
 }
 
+extern void otg_set_error(struct otg_transceiver *otg, enum usb_otg_error);
 
 /* for HCDs */
 static inline int
@@ -129,3 +140,11 @@ otg_start_srp(struct otg_transceiver *otg)
 
 /* for OTG controller drivers (and maybe other stuff) */
 extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
+
+#ifdef CONFIG_USB_OTG_WHITELIST
+struct usb_device;
+extern int otg_targeted(struct usb_device *dev);
+extern ssize_t otg_print_whitelist(char *buf);
+#else
+#define otg_targeted(x)		OTG_ERR_IS_TARGETED
+#endif
-- 
1.5.4.rc3.24.gb53139

-
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux