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