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

Reply via email to