Hi,
I mentioned recently that USB-OTG support for Linux is "in the works". Now it's time to share some info about it ... see below.
What I'd like to do is have most of these merge into 2.6.8 since the programming interface changes seem small and safe. That means right now is a great time for folk working with OTG (or just exploring it) to share comments, preferably on-list (linux-usb-devel) but private email is OK too (within reason).
So ... read on! And please respond with any comments you may have, especially if you've got OTG insights to share from work with any other platforms.
- Dave
WHAT IS USB OTG?
It's an addendum to the USB 2.0 specification, not yet widely implemented, which some folk say will be merged into the main spec sometime ("USB 2.1" is a phrase I've heard).
The biggest single feature is that it defines "dual-role" devices, which can act as either host (like a PC) or peripheral (like a printer, cell phone, camera, portable game console, PDA, etc). So it's of interest to anyone developing smart peripherals. Many such developers have gotten very interested in embedding Linux.
This is not for stock PC hardware; it's probably going to use some low-power processor that runs well with batteries. Most current Texas Instruments OMAP processors (ARM v5tej and newer), and the new Intel PXA-270 (ARM v5te/XScale), are examples where the OTG support is partially integrated into the chip. An external OTG transciever is often used, such as the Philips ISP1301. At least one vendor is planning to support high speed OTG on Linux.
Key to the dual-role support is a new kind of "Mini-AB" connector, and "Mini-A to Mini-B" cable. The connector can be used with either end of the cable, and it can tell which end is which: the ID pin is grounded on the "A" end. Each peripheral thus knows which role to use when it starts. There's also a "Host Negotiation Protocol" (HNP), to switch roles later on (re-cabling would be user-antagonistic).
Lots more information is available on the www.usb.org website; notice the special "USB OTG" brand logos too. The big state diagram in the OTG specification is suitably impressive, and most of it relates to signaling before the reset in USB enumeration. (And most of it is internal to the usb controller implementations.) The presentations on that site are more immediately comprehensible.
WHAT DOES LINUX NEED BEFORE IT SUPPORTS OTG?
Since Linux-USB already supports both the host side of USB (since 2.2.8, and much improved in 2.6!) and the peripheral side (the newish "Gadget" framework, in both 2.4 and 2.6) ... it needs minor tweaks to those APIs, and some implementations to match. No major new APIs; driver writers get to re-use their existing knowledge and skills.
There are actually two sets of API changes on each side, and I'm only describing the upper level here -- visible to device drivers, though many won't need to care. There's a lower level API too, of interest only to folk working with USB Controller drivers. I'll send a separate note about those interfaces, currently focussing on an "otg_transciever" driver that manages role selection and other stuff ... that needs to mask hardware implementation details just like the other USB controller APIs, but I expect it to change much more than what I show here.
Of course, we need to see products supporting all this too. Software development boards don't count as much as a Linux cell phone or PDA!
HOST SIDE PROGRAMMING INTERFACE CHANGES
HNP is driven by USB suspend, so the CONFIG_USB_SUSPEND [1] calls to support selective suspend (and resume) are involved:
int usb_suspend_device(struct usb_device *dev); int usb_resume_device(struct usb_device *dev);
When HNP is enabled, suspending a device may trigger HNP (so the other device gets to be host for a while). Suspending idle devices is a good thing to do in any case though, to conserve power; these APIs are useful even without OTG support.
USBcore also needs some help from HCDs to support HNP. (And SRP too, but until Linux is willing to power down root hub ports, that's not part of the picture.) This seems sufficient:
struct usb_bus { ... u8 otg_port; /* 0, or index of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ ... };
So "otg_port" will say which root hub port has a Mini-AB connector (if any). If an OTG device connects to that port, the host may enable HNP with it by setting the B_HNP_ENABLE device feature flag. USB OTG has two kinds of host; A-Host is basically what Linux supports today, and B-Host means that HNP switched roles an odd number of times.
So far I'm believing that only the suspend/resume hooks will really matter to any host side device driver, and the rest for usbcore and the HCD to use when talking with each other. (An OTG host will need some driver to handle anything not on the "Targeted Peripheral List", but that doesn't seem to need any more than the current APIs.)
PERIPHERAL SIDE PROGRAMMING INTERFACE CHANGES
The "USB Gadget" API needs similar changes, new method calls and protocol flags. Also, the gadget drivers need to return an OTG descriptor, used by the host ... see the attached patch for "Gadget Zero".
Not unlike the USB_SUSPEND changes, the new method calls needed have also been sent around before [2] ... some of them are useful for other cases than OTG.
For example, a gadget driver may want to connect or disconnect from the host for reasons other than breaking an OTG session. One example is a driver that shouldn't enumerate until it's properly hooked up to its userspace logic; opening a file might permint connection:
/* these logically control the USB D+ pullup */ int usb_gadget_connect (struct usb_gadget *gadget); int usb_gadget_disconnect (struct usb_gadget *gadget);
The D+ (or D-) pullup normally comes after something detected VBUS power, like a GPIO or external transciever:
/* interface to external USB transceiver */ int usb_gadget_vbus_connect(struct usb_gadget *gadget); int usb_gadget_vbus_disconnect(struct usb_gadget *gadget);
For battery powered hosts, it's nice to know when you can use VBUS to charge the battery (up to 500 mA) ... that's more important for OTG since it targets mobile devices, which use batteries?
/* call this during SET_CONFIGURATION */ int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA);
The flags are however very OTG-specific.
struct usb_gadget { ... unsigned is_otg:1; unsigned is_a_peripheral:1; unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; ... };
As with the host side, these names closely match the OTG spec, as with the B_HNP_ENABLE, A_HNP_SUPPORT, and A_ALT_HNP_SUPPORT device feature flags. (Visible in SET_CONFIGURATION, where the user interface needs to be able to report their values, and in the suspend callback.) The "A-Peripheral" state is true when HNP switched roles an odd number of times.
The "is_otg" flag is set for devices that have a Mini-AB port, and it means the driver must provide an OTG descriptor; see the attached patch for an example about how it's used. (That structure is already defined in <linux/usb_ch9.h>, along with the numbers of those OTG features.)
SRP acts as a special kind of remote wakeup, so it's triggered by the existing usb_gadget_wakeup() call when the device isn't actually suspended.
EXAMPLE GADGET DRIVER CHANGES
The attached patch shows the changes needed in any OTG-aware gadget driver. It's complete enough to pass the USBCV OTG tests (OK, OMAP hardware handles some of that!), and should be simple enough to understand: pass OTG descriptor if needed, report OTG state in a few "interesting" moments that a user may need to care about.
REFERENCES
[1] A recent version of the CONFIG_USB_SUSPEND patch
http://www.mail-archive.com/[EMAIL PROTECTED]/msg23811.html
[2] Patch with some new OTG-related Gadget API changes
http://www.mail-archive.com/[EMAIL PROTECTED]/msg24369.html
--- 1.30/drivers/usb/gadget/zero.c Wed Jun 23 10:49:53 2004 +++ edited/drivers/usb/gadget/zero.c Mon Jun 28 17:13:42 2004 @@ -259,6 +264,14 @@ .bMaxPower = 1, /* self-powered */ }; +static struct usb_otg_descriptor +otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; + /* one interface in each configuration */ static const struct usb_interface_descriptor @@ -302,6 +315,7 @@ }; static const struct usb_descriptor_header *fs_source_sink_function [] = { + (struct usb_descriptor_header *) &otg_descriptor, (struct usb_descriptor_header *) &source_sink_intf, (struct usb_descriptor_header *) &fs_sink_desc, (struct usb_descriptor_header *) &fs_source_desc, @@ -309,6 +323,7 @@ }; static const struct usb_descriptor_header *fs_loopback_function [] = { + (struct usb_descriptor_header *) &otg_descriptor, (struct usb_descriptor_header *) &loopback_intf, (struct usb_descriptor_header *) &fs_sink_desc, (struct usb_descriptor_header *) &fs_source_desc, @@ -356,6 +371,7 @@ }; static const struct usb_descriptor_header *hs_source_sink_function [] = { + (struct usb_descriptor_header *) &otg_descriptor, (struct usb_descriptor_header *) &source_sink_intf, (struct usb_descriptor_header *) &hs_source_desc, (struct usb_descriptor_header *) &hs_sink_desc, @@ -363,6 +379,7 @@ }; static const struct usb_descriptor_header *hs_loopback_function [] = { + (struct usb_descriptor_header *) &otg_descriptor, (struct usb_descriptor_header *) &loopback_intf, (struct usb_descriptor_header *) &hs_source_desc, (struct usb_descriptor_header *) &hs_sink_desc, @@ -444,6 +461,10 @@ ? fs_source_sink_function : fs_loopback_function; + /* for now, don't advertise srp-only devices */ + if (!gadget->is_otg) + function++; + len = usb_gadget_config_buf (is_source_sink ? &source_sink_config : &loopback_config, @@ -951,6 +974,12 @@ case USB_REQ_SET_CONFIGURATION: if (ctrl->bRequestType != 0) goto unknown; + if (gadget->a_hnp_support) + DBG (dev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG (dev, "HNP needs a different root port\n"); + else + VDBG (dev, "HNP inactive\n"); spin_lock (&dev->lock); value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC); spin_unlock (&dev->lock); @@ -1199,6 +1228,12 @@ hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; #endif + if (gadget->is_otg) { + otg_descriptor.bmAttributes |= USB_OTG_HNP, + source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + usb_gadget_set_selfpowered (gadget); init_timer (&dev->resume); @@ -1232,15 +1267,18 @@ zero_suspend (struct usb_gadget *gadget) { struct zero_dev *dev = get_gadget_data (gadget); + char *hnp = ""; if (gadget->speed == USB_SPEED_UNKNOWN) return; + if (gadget->b_hnp_enable) + hnp = " (HNP enabled)"; if (autoresume) { mod_timer (&dev->resume, jiffies + (HZ * autoresume)); - DBG (dev, "suspend, wakeup in %d seconds\n", autoresume); + DBG (dev, "suspend%s, %d second wakeup\n", hnp, autoresume); } else - DBG (dev, "suspend\n"); + DBG (dev, "suspend%s\n", hnp); } static void