Commit 1ae4eefe authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB OTG: usbcore enumeration (3/5)

Teach usbcore how to enumerate OTG devices and perform HNP:

 - CONFIG_USB_OTG is a boolean; it selects CONFIG_USB_SUSPEND.
   Boards with a Mini-AB connector should offer it as a config
   option; no other hardware could support OTG.

 - Before resetting a port, make sure it's not still suspended.
   (For example, after an HNP role switch.)

 - When an OTG A-Host enumerates a dual-role device, set the
   appropriate device feature:  B_HNP_ENABLE if it's connected
   to the OTG port, otherwise A_ALT_HNP_SUPPORT.

 - When an OTG B-Host enumerates a dual-role device, don't
   bother debouncing ... the power session is stable already,
   the A-Host already debounced.

 - The OTG "Targeted Peripheral List" lives in a product-customized
   "otg_whitelist.h" header.

 - CONFIG_USB_OTG_WHITELIST lets developers choose to ignore whitelist
   failures, so unsupported devices can be configured anyway.

 - If the whitelist check fails, immediately suspend the device.

    * For dual-role devices, that triggers HNP so the other device
      can try to act as host.

    * For peripheral-only devices, that conserves power ... but not
      quite as much as turning off power on that port, which should
      eventually be done with OTG ports (and all other ports that
      support SRP).

The whitelist logic tries to make use of the existing usb_device_id
logic, but since the interface info isn't available that early it's
a bit awkward use information anywhere outside the device descriptor.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 6c932443
...@@ -71,3 +71,29 @@ config USB_SUSPEND ...@@ -71,3 +71,29 @@ config USB_SUSPEND
may not yet work as expected. may not yet work as expected.
If you are unsure about this, say N here. If you are unsure about this, say N here.
config USB_OTG
bool
depends on USB && EXPERIMENTAL
select USB_SUSPEND
default n
config USB_OTG_WHITELIST
bool "Rely on OTG Targeted Peripherals List"
depends on USB_OTG
default y
help
If you say Y here, the "otg_whitelist.h" file will be used as a
product whitelist, so USB peripherals not listed there will be
rejected during enumeration. This behavior is required by the
USB OTG specification for all devices not on your product's
"Targeted Peripherals List".
Otherwise, peripherals not listed there will only generate a
warning and enumeration will continue. That's more like what
normal Linux-USB hosts do (other than the warning), and is
convenient for many stages of product development.
...@@ -1095,6 +1095,10 @@ static inline void show_string(struct usb_device *udev, char *id, int index) ...@@ -1095,6 +1095,10 @@ static inline void show_string(struct usb_device *udev, char *id, int index)
{} {}
#endif #endif
#ifdef CONFIG_USB_OTG
#include "otg_whitelist.h"
#endif
/** /**
* usb_new_device - perform initial device setup (usbcore-internal) * usb_new_device - perform initial device setup (usbcore-internal)
* @udev: newly addressed device (in ADDRESS state) * @udev: newly addressed device (in ADDRESS state)
...@@ -1144,6 +1148,79 @@ int usb_new_device(struct usb_device *udev) ...@@ -1144,6 +1148,79 @@ int usb_new_device(struct usb_device *udev)
show_string(udev, "SerialNumber", show_string(udev, "SerialNumber",
udev->descriptor.iSerialNumber); udev->descriptor.iSerialNumber);
#ifdef CONFIG_USB_OTG
/*
* OTG-aware devices on OTG-capable root hubs may be able to use SRP,
* to wake us after we've powered off VBUS; and HNP, switching roles
* "host" to "peripheral". The OTG descriptor helps figure this out.
*/
if (!udev->bus->is_b_host
&& udev->config
&& udev->parent == udev->bus->root_hub) {
struct usb_otg_descriptor *desc = 0;
struct usb_bus *bus = udev->bus;
/* descriptor may appear anywhere in config */
if (__usb_get_extra_descriptor (udev->rawdescriptors[0],
udev->config[0].desc.wTotalLength,
USB_DT_OTG, (void **) &desc) == 0) {
if (desc->bmAttributes & USB_OTG_HNP) {
unsigned port;
struct usb_device *root = udev->parent;
for (port = 0; port < root->maxchild; port++) {
if (root->children[port] == udev)
break;
}
port++;
dev_info(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port == bus->otg_port)
? "" : "non-");
/* enable HNP before suspend, it's simpler */
if (port == bus->otg_port)
bus->b_hnp_enable = 1;
err = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
bus->b_hnp_enable
? USB_DEVICE_B_HNP_ENABLE
: USB_DEVICE_A_ALT_HNP_SUPPORT,
0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
if (err < 0) {
/* OTG MESSAGE: report errors here,
* customize to match your product.
*/
dev_info(&udev->dev,
"can't set HNP mode; %d\n",
err);
bus->b_hnp_enable = 0;
}
}
}
}
if (!is_targeted(udev)) {
/* Maybe it can talk to us, though we can't talk to it.
* (Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
static int __usb_suspend_device (struct usb_device *,
int port, u32 state);
err = __usb_suspend_device(udev,
udev->bus->otg_port - 1,
PM_SUSPEND_MEM);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
err = -ENODEV;
goto fail;
}
#endif
/* put device-specific files into sysfs */ /* put device-specific files into sysfs */
err = device_add (&udev->dev); err = device_add (&udev->dev);
if (err) { if (err) {
...@@ -1934,6 +2011,10 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port) ...@@ -1934,6 +2011,10 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port)
hdev->bus->b_hnp_enable = 0; hdev->bus->b_hnp_enable = 0;
} }
retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
if (retval < 0 && retval != -EPIPE)
dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval);
/* Some low speed devices have problems with the quick delay, so */ /* Some low speed devices have problems with the quick delay, so */
/* be a bit pessimistic with those devices. RHbug #23670 */ /* be a bit pessimistic with those devices. RHbug #23670 */
if (oldspeed == USB_SPEED_LOW) if (oldspeed == USB_SPEED_LOW)
...@@ -2160,6 +2241,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -2160,6 +2241,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
usb_disconnect(&hdev->children[port]); usb_disconnect(&hdev->children[port]);
clear_bit(port, hub->change_bits); clear_bit(port, hub->change_bits);
#ifdef CONFIG_USB_OTG
/* during HNP, don't repeat the debounce */
if (hdev->bus->is_b_host)
portchange &= ~USB_PORT_STAT_C_CONNECTION;
#endif
if (portchange & USB_PORT_STAT_C_CONNECTION) { if (portchange & USB_PORT_STAT_C_CONNECTION) {
status = hub_port_debounce(hdev, port); status = hub_port_debounce(hdev, port);
if (status < 0) { if (status < 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.
*/
/*
* This OTG Whitelist is the OTG "Targeted Peripheral List". It should
* mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
*
* YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
*/
static struct usb_device_id whitelist_table [] = {
/* hubs are optional in OTG, but very handy ... */
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */
/* FIXME actually, printers are NOT supposed to use device classes;
* they're supposed to use interface classes...
*/
{ USB_DEVICE_INFO(7, 1, 1) },
{ USB_DEVICE_INFO(7, 1, 2) },
{ USB_DEVICE_INFO(7, 1, 3) },
#endif
#ifdef CONFIG_USB_CDCETHER
/* Linux-USB CDC Ethernet gadget */
{ USB_DEVICE(0x0525, 0xa4a1), },
/* Linux-USB CDC Ethernet + RNDIS gadget */
{ USB_DEVICE(0x0525, 0xa4a2), },
#endif
#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE)
/* gadget zero, for testing */
{ USB_DEVICE(0x0525, 0xa4a0), },
#endif
{ } /* Terminating entry */
};
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
/* possible in developer configs only! */
if (!dev->bus->otg_port)
return 1;
/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
if (dev->descriptor.idVendor == 0x1a0a
&& dev->descriptor.idProduct == 0xbadd)
return 0;
/* 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++) {
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != dev->descriptor.idVendor)
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
id->idProduct != dev->descriptor.idProduct)
continue;
/* No need to test id->bcdDevice_lo != 0, since 0 is never
greater than any unsigned number. */
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
(id->bcdDevice_lo > dev->descriptor.bcdDevice))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
(id->bcdDevice_hi < dev->descriptor.bcdDevice))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
(id->bDeviceClass != dev->descriptor.bDeviceClass))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
(id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
continue;
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
continue;
return 1;
}
/* 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",
dev->descriptor.idVendor,
dev->descriptor.idProduct);
#ifdef CONFIG_USB_OTG_WHITELIST
return 0;
#else
return 1;
#endif
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment