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

From: Felipe Balbi
Date: Sat Jan 19 2008 - 09:36:36 EST


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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/