This patch includes the first version of some support for autoconfiguring gadget drivers. Later versions may be able to offload some logic into controller drivers, but this API looks like it'll suffice to get rid of compile-time configuration for simple drivers.
Basically drivers just call
struct usb_ep *ep = usb_ep_autoconfig (gadget, &fs_desc);
and the function chooses one of the endpoints (assuming there's more than one) which matches the requirements in the endpoint descriptor ... or returns null.
They can also use
if (gadget_is_pxa(gadget)) { ... pxa-specific logic ... }
to trigger controller-specific logic.
Sanity tested with Gadget Zero (see the second patch).
The RFC part: does this API look like the way to go? If you've written a (kernel) "bulk-only" gadget driver (including serial, file_storage, ethernet) does this look like it wouldn't work there?
I know Andrew wants implementations that talk bitmasks, eventually, but that could be rolled in without changing the API actually used by gadget drivers.
- Dave
--- linux-2.5/drivers/usb/gadget/epautoconf.c 1969-12-31 16:00:00.000000000 -0800 +++ gadget-2.6/drivers/usb/gadget/epautoconf.c 2004-03-05 16:33:00.331717592 -0800 @@ -0,0 +1,287 @@ +/* + * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers + * + * Copyright (C) 2004 David Brownell + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> + +#include <linux/ctype.h> +#include <linux/string.h> + +#include <linux/usb_ch9.h> +#include <linux/usb_gadget.h> + +#include "epautoconf.h" + + +/* + * This should do right by endpoints from these controller drivers, plus + * most others that obey the standard endpoint naming convention: + * + * - net2280 + * - pxa2xx_udc + * - goku_udc + * - sh_udc + * - mq11xx_udc + * + * That naming convention represents the most common endpoint hardware + * restrictions other than ep->maxpacket. Less common restrictions are + * implied by gadget_is_*(). By example: + * + * - ep-* ... no functionality restrictions + * - ep1, ep2, ... address is fixed, not direction or type + * - ep1in, ep2out, ... address and direction are fixed, not type + * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction + * - ep1in-bulk, ep2out-iso, ... all three are fixed + * + * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal. + * + * LATER autoconfig support may rely less on conventions, and more on explicit + * endpoint annotations from controller drivers. Expect hardware-specific + * quirk handling to be always needed. + */ + + +static __initdata unsigned epnum; + +static int __init +ep_matches ( + struct usb_gadget *gadget, + struct usb_ep *ep, + struct usb_endpoint_descriptor *desc +) +{ + u8 type; + const char *tmp; + + /* endpoint already claimed? */ + if (ep->driver_data != 0) + return 0; + + /* only support ep0 for portable CONTROL traffic */ + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (type == USB_ENDPOINT_XFER_CONTROL) + return 0; + + /* type-restriction: "-iso", "-bulk", or "-int". + * direction-restriction: "in", "out". + */ + if (ep->name[2] != '-') { + tmp = strrchr (ep->name, '-'); + if (tmp) { + switch (type) { + case USB_ENDPOINT_XFER_INT: + /* bulk endpoints handle interrupt transfers, + * except the toggle-ignored iso-synch kind + */ + if (!strcmp (tmp, "-iso")) + return 0; + /* for now, avoid PXA "interrupt-in"; + * it's documented as never using DATA1. + */ + if (gadget_is_pxa (gadget)) + return 0; + break; + case USB_ENDPOINT_XFER_BULK: + if (strcmp (tmp, "-bulk")) + return 0; + break; + case USB_ENDPOINT_XFER_ISOC: + if (strcmp (tmp, "-iso")) + return 0; + } + } else { + tmp = ep->name + strlen (ep->name); + } + + /* direction-restriction: "..in-..", "out-.." */ + tmp--; + if (!isdigit (*tmp)) { + if (desc->bEndpointAddress & USB_DIR_IN) { + if (*tmp != 'n') + return 0; + } else { + if (*tmp != 't') + return 0; + } + } + } + + /* endpoint maxpacket size is an input parameter, except for bulk + * where it's an output parameter representing the full speed limit. + * (high speed bulk maxpacket is fixed at 512 bytes.) + * + * NOTE: don't rely on this for high bandwidth endpoints (yet?), + * this doesn't know about hardware multi-buffering. Or for all + * nuances of dual speed devices. + */ + switch (type) { + u16 max; + + case USB_ENDPOINT_XFER_INT: + case USB_ENDPOINT_XFER_ISOC: + max = 0x7ff & le16_to_cpup (&desc->wMaxPacketSize); + if (ep->maxpacket < max) + return 0; + break; + } + + /* MATCH!! */ + + /* Modify endpoint descriptor to match the hardware */ + if (type == USB_ENDPOINT_XFER_BULK) + desc->wMaxPacketSize = cpu_to_le16 ( + max ((unsigned)64, ep->maxpacket)); + + if (isdigit (ep->name [2])) { + u8 num = simple_strtol (&ep->name [2], NULL, 10); + desc->bEndpointAddress |= num; + } else { + /* NOTE: could track IN and OUT separately, + * for devices with more than 15 endpoints. + */ + if (++epnum > 15) + return 0; + desc->bEndpointAddress |= epnum; + } + + return 1; +} + +static struct usb_ep * __init +find_ep (struct usb_gadget *gadget, const char *name) +{ + struct usb_ep *ep; + + list_for_each_entry (ep, &gadget->ep_list, ep_list) { + if (0 == strcmp (ep->name, name)) + return ep; + } + return 0; +} + +/** + * usb_ep_autoconfig - choose an endpoint matching the descriptor + * @gadget: The device to which the endpoint must belong. + * @desc: Descriptor for desired endpoint. This is modified on success. + * Endpoint direction and transfer mode must be initialized; and + * for periodic transfers, the maximum packet size needed. + * + * Chooses and returns an endpoint that can be configured to match the + * specified descriptor, using usb_ep_enable(). + * + * On success, this returns an un-claimed endpoint, and modifies the endpoint + * descriptor to include an endpoint address. For bulk endpoints, the endpoint + * maxpacket value is also initialized, as if the endpoint were used at full + * speed. To prevent the endpoint from being returned by a later autoconfig + * call, claim it by assigning ep->driver_data to some non-null value. + * + * On failure, this returns a null endpoint descriptor. + * + * Most "bulk-only" drivers (using bulk transfers and/or low-bandwidth + * interrupt transfers) should be able to autoconfigure selection of all + * hardware endpoints, even for dual-speed hardware. + * + * However, developers of gadget drivers may need to explicitly code some + * endpoint configuration choices. This routine can not know all of the + * hardware restrictions that may be relevant, and may not make the best + * choices about how to use the USB controller hardware. + * + * This function is linked into the init section, and should be invoked + * from a gadget driver's bind() entry. + */ +struct usb_ep * __init +usb_ep_autoconfig ( + struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc +) +{ + struct usb_ep *ep; + u8 type; + + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* First, apply chip-specific "best usage" knowledge. + * This might make a good usb_gadget_ops hook ... + */ + if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep (gadget, "ep-e"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + ep = find_ep (gadget, "ep-f"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + + } else if (gadget_is_goku (gadget)) { + if (type == USB_ENDPOINT_XFER_INT) { + /* single buffering is enough */ + ep = find_ep (gadget, "ep3-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + } else if (type == USB_ENDPOINT_XFER_BULK + && (USB_DIR_IN & desc->bEndpointAddress)) { + /* DMA may be available */ + ep = find_ep (gadget, "ep2-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + } + + } else if (gadget_is_sh (gadget) && type == USB_ENDPOINT_XFER_INT) { + /* single buffering is enough; maybe 8 byte fifo is too */ + ep = find_ep (gadget, "ep3in-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + + } else if (gadget_is_mq11xx (gadget) && type == USB_ENDPOINT_XFER_INT) { + ep = find_ep (gadget, "ep1-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + } + + /* Second, look at endpoints until an unclaimed one looks usable */ + list_for_each_entry (ep, &gadget->ep_list, ep_list) { + if (ep_matches (gadget, ep, desc)) + return ep; + } + + /* Fail */ + return 0; +} + +/** + * usb_ep_autoconfig_reset - reset endpoint autoconfig state + * + * Use this for devices where one configuration may need to assign + * endpoint resources very differently from the next one. It clears + * state such as ep->driver_data and the record of assigned endpoints. + */ +void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget) +{ + struct usb_ep *ep; + + list_for_each_entry (ep, &gadget->ep_list, ep_list) { + ep->driver_data = 0; + } + epnum = 0; +} + --- linux-2.5/drivers/usb/gadget/epautoconf.h 1969-12-31 16:00:00.000000000 -0800 +++ gadget-2.6/drivers/usb/gadget/epautoconf.h 2004-03-05 15:59:59.559840688 -0800 @@ -0,0 +1,85 @@ +/* + * epautoconf.h -- endpoint autoconfiguration for usb gadget drivers + * + * Copyright (C) 2004 David Brownell + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *, + struct usb_endpoint_descriptor *) __init; + +extern void usb_ep_autoconfig_reset (struct usb_gadget *) __init; + +/* + * USB device controllers have lots of quirks. Use these macros in + * gadget drivers or other code that needs to deal with them, and which + * autoconfigures instead of using early binding to the hardware. + * + * This could eventually work like the ARM mach_is_*() stuff, driven by + * some config file that gets updated as new hardware is supported. + * + * NOTE: some of these controller drivers may not be available yet. + */ +#ifdef CONFIG_USB_GADGET_NET2280 +#define gadget_is_net2280(g) !strcmp("net2280", (g)->name) +#else +#define gadget_is_net2280(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_PXA +#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) +#else +#define gadget_is_pxa(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_GOKU +#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name) +#else +#define gadget_is_goku(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_SUPERH +#define gadget_is_sh(g) !strcmp("sh_udc", (g)->name) +#else +#define gadget_is_sh(g) 0 +#endif + +#if 0 +#ifdef CONFIG_USB_GADGET_SA1100 +#define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name) +#else +#define gadget_is_sa1100(g) 0 +#endif +#endif + +#ifdef CONFIG_USB_GADGET_MQ11XX +#define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) +#else +#define gadget_is_mq11xx(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_OMAP +#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name) +#else +#define gadget_is_omap(g) 0 +#endif + + +/* NOTE: change this when other dual-speed hardware is supported! */ +#define gadget_is_dualspeed(g) gadget_is_net2280(g) +