One of the "we'd like to have it" features for 2.5 is what's been
called a "gadget driver" API -- useful inside small portable
things that talk USB as a pure slave (like most ARM-based PDAs),
or other periperals that are smart enough to run Linux.  Lineo's
code didn't get in to Linus' tree, so ...

I thought I'd send around a version of something I've had on the
cooker for a bit  In practical terms, not only is it thin, but
this is it for the API!  No library level code (think "usbcore")
is needed, just some drivers.  It's full (overfull) of functional
doc; it's gotta go somewhere!  The structure is simple:

 - Hardware drivers do things like talk to the UDC on many ARM
   based CPUs (Linux PDAs etc), PCI drivers like NetChip2280, the
   Philips chips some folk use, and so on, and expose a simple API.

 - Gadget drivers do I/O using the endpoints exposed by that hardware
   API, return usb descriptors, and enable/disable endpoints as needed
   to implement the interfaces they expose (audio device, hid control,
   networking, crypto, video, and so on).  They own essentially all the
   ep0 implementation code in the device.

The API is URB-like, but it turns out that most of <linux/usb.h>
is host-centric.  So for now only <linux/usb_ch9.h> is shared, but
it'd be good to share more.  That way when we add USB role changes,
as required by OTG (slaves can become masters), the same I/O APIs
would work on bot sides.  (Except of course for control requests.)

I'd be interested in any feedback y'all may have.  Does it seem
to be a reasonble fit for hardware you're familiar with?  Does
it seem like it'd be easy to work with this (given examples)?
What's broken?  (Naming is of course easily changed.)  Is this
even vaguely the right direction?  :)

- Dave


--- ./include/linux-dist/usb_gadget.h   Wed Dec 31 16:00:00 1969
+++ ./include/linux/usb_gadget.h        Wed Nov 20 09:03:22 2002
@@ -0,0 +1,469 @@
+/*
+ * <linux/usb_gadget.h>
+ *
+ * We call the USB code inside a Linux-based peripheral device a "gadget"
+ * driver, except for the hardware-specific bus glue.  One USB host can
+ * master many USB gadgets, but the gadgets are only slaved to one host.
+ * In USB 2.0 "On The Go", peripherals can swap roles.
+ *
+ * (c) Copyright 2002 by David Brownell
+ *
+ * This software is licenced under the GNU GPL version 2.
+ */
+
+#ifndef __LINUX_USB_GADGET_H
+#define __LINUX_USB_GADGET_H
+
+#ifdef __KERNEL__
+
+/*
+ * Gadget drivers make endpoint I/O requests to hardware without needing
+ * to know many details of the hardware, but driver setup/configuration
+ * code needs to handle some differences.  Compile-time misconfigurations
+ * should be detected at run-time.  Use the API like this:
+ *
+ * (1) Initialize a driver for the particular device side bus interface
+ * hardware, such as the sa1100 or pxa250 as found in Linux PDAs, net2280
+ * on PCI (USB 2.0), and so on.  At this point the device is logically in the
+ * USB ch9 initial state ("attached"), drawing no power and not usable.
+ *
+ * (2) Register a gadget driver that implements some higher level device
+ * function.  That will then bind() to a usb_gadget, letting the gadget
+ * driver initialize using the gadget's list of named endpoints.
+ *
+ * (3) The hardware driver can now start enumerating.  The steps it handles
+ * are to accept USB power and USB device address.  Other steps are handled
+ * by the gadget driver.
+ *
+ * (4) The gadget driver's setup() call returns usb descriptors, based both
+ * on what the bus interface hardware provides and on the functionality being
+ * implemented.  That can involve alternate settings or configurations.
+ *
+ * (5) The gadget driver handles the last step of enumeration, when the USB
+ * host issues a the set_configuration call.  It enables the endpoints used
+ * in that configuration, and gives all interfaces their default settings.
+ *
+ * (6) Do real work and perform data transfers, possibly involving changes
+ * to interface settings or switching to new configurations, until ...
+ *
+ * (7) The device shuts down (including by unregistering the gadget driver)
+ * or disconnects.  After disconnect, goto step 3 above.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_ep;
+
+// FIXME it'd be good to evolve so that master and slave sides of usb
+// had the same way to package and submit requests, but that implies
+// something new or slimming 'urb' to be less host-centric.  good for
+// many reasons including support for usb-otg (peer-to-peer usb).
+
+
+/**
+ * struct usb_request - describes one i/o request
+ *
+ * these are allocated/freed through the endpoint they're used with.  the
+ * endpoint's driver can add extra per-request data to the memory it returns.
+ *
+ * bulk endpoints can use any size buffers, and can also be used for interrupt.
+ * interrupt-only endpoints can be much less functional.
+ */
+       // NOTE this is analagous to 'struct urb' on the host side,
+       // except that it's thinner.  the analogue of 'hcpriv' data
+       // can be pre-allocated, maybe a few TDs, and so on.
+struct usb_request {
+       // inputs, read by the hardware driver
+       dma_addr_t              dma;
+       void                    *buf;
+       unsigned                length;
+       // flag bits?
+
+       void                    (*complete) (struct usb_ep *ep,
+                                       struct usb_request *req);
+       void                    *context;
+
+       // outputs, valid from complete() until resubmit or free
+       int                     status;
+       unsigned                actual;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* endpoint-specific parts of the api to the usb controller hardware.
+ * unlike the host side, (de)multiplexing layers are not required.
+ *
+ * note that device side usb controllers commonly differ in how many
+ * endpoints they support, as well as their capabilities.
+ */
+struct usb_ep_ops {
+       int (*enable) (struct usb_ep *ep,
+               const struct usb_endpoint_descriptor *desc);
+       int (*disable) (struct usb_ep *ep);
+
+       struct usb_request *(*alloc_request) (struct usb_ep *ep,
+               int mem_flags);
+       void (*free_request) (struct usb_ep *ep, struct usb_request *req);
+
+       void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes,
+               dma_addr_t *dma, int mem_flags);
+       void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma,
+               unsigned bytes);
+       // FIXME map/unmap too, so more drivers can avoid copies
+
+       int (*queue) (struct usb_ep *ep, struct usb_request *req,
+               int mem_flags);
+       int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
+
+       int (*set_halt) (struct usb_ep *ep, int is_halted);
+};
+
+/**
+ * struct usb_ep - device side representation of USB endpoint
+ *
+ * the bus controller driver lists all the general purpose endpoints in
+ * gadget->ep_list.  the control endpoint (gadget->ep0) is not in that list,
+ * and is accessed only in response to a driver setup() callback.
+ */
+struct usb_ep {
+       // managed by gadget driver
+       void                    *driver_data;
+
+       // readonly to gadget driver
+       const char              *name;
+       const struct usb_ep_ops *ops;
+       struct list_head        ep_list;
+       unsigned                maxpacket : 16,
+                               is_halted : 1;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_ep_enable - configure endpoint, making it usable
+ * @ep: the endpoint being configured.  may not be the endpoint named "ep0".
+ *     drivers discover endpoints through the ep_list of a usb_gadget.
+ * @desc: descriptor for desired behavior.  caller guarantees this pointer
+ *     remains valid until the endpoint is disabled.
+ *
+ * when configurations are set, or when interface settings change, the driver
+ * will enable or disable the relevant endpoints.  while it is enabled, an
+ * endpoint may be used for i/o until the driver receives a disconnect() from
+ * the host or until the endpoint is disabled.
+ *
+ * the ep0 implementation must ensure that the hardware capabilities of each
+ * endpoint match the descriptor provided for it.  for example, an endpoint
+ * named "ep2in-bulk" would be usable for interrupt transfers as well as bulk,
+ * but it likely couldn't be used for iso transfers or for endpoint 14.  some
+ * endpoints are fully configurable, with more generic names like "ep-a".
+ * (remember that for USB, "in" means "towards the host".)
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_enable (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
+{
+       return ep->ops->enable (ep, desc);
+}
+
+/**
+ * usb_ep_disable - endpoint is no longer usable
+ * @ep: the endpoint being unconfigured.  may not be the endpoint named "ep0".
+ *
+ * no other task may be using this endpoint when this is called,
+ * all submitted requests must have been completed (or canceled), and
+ * all allocated requests should have been freed.  after this call
+ * returns, the only legal request to this endpoint is usb_ep_enable().
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_disable (struct usb_ep *ep)
+{
+       return ep->ops->disable (ep);
+}
+
+/**
+ * usb_ep_alloc_request - allocate a request object to use with this endpoint
+ * @ep: the endpoint to be used with with the request
+ * @mem_flags: SLAB_* flags to use
+ *
+ * request objects must not be allocated except with this call, since they
+ * normally require controller-specific (and perhaps endpoint-specific) setup.
+ * requests may be submitted with usb_ep_queue(), and receive a single
+ * completion callback.  free requests with usb_ep_free_request(), when
+ * they are no longer needed.
+ *
+ * returns the request, or null if one could not be allocated.
+ */
+static inline struct usb_request *
+usb_ep_alloc_request (struct usb_ep *ep, int mem_flags)
+{
+       return ep->ops->alloc_request (ep, mem_flags);
+}
+
+/**
+ * usb_ep_free_request - frees a request object
+ * @ep: the endpoint associated with the request
+ * @req: the request being freed
+ *
+ * reverses the effect of usb_ep_alloc_request().
+ * caller guarantees the request is not queued, and that it will
+ * no longer be requeued (or otherwise used).
+ */
+static inline void
+usb_ep_free_request (struct usb_ep *ep, struct usb_request *req)
+{
+       ep->ops->free_request (ep, req);
+}
+
+/**
+ * usb_ep_alloc_buffer - allocate an I/O buffer
+ * @ep: the endpoint associated with the buffer
+ * @len: length of the desired buffer
+ * @dma: pointer to the buffer's DMA address; must be valid
+ * @mem_flags: SLAB_* flags to use
+ *
+ * returns a new buffer, or null if one could not be allocated.
+ * the buffer is suitably aligned for dma; caller won't have
+ * to worry about dma-inconsistency.
+ * free it later with usb_ep_free_buffer().
+ */
+static inline void *
+usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len, dma_addr_t *dma,
+       int mem_flags)
+{
+       return ep->ops->alloc_buffer (ep, len, dma, mem_flags);
+}
+
+/**
+ * usb_ep_free_buffer - frees an i/o buffer
+ * @ep: the endpoint associated with the buffer
+ * @buf: CPU view address of the buffer
+ * @dma: the buffer's DMA address
+ * @len: length of the buffer
+ *
+ * reverses the effect of usb_ep_alloc_buffer().
+ * caller guarantees the buffer will no longer be accessed
+ */
+static inline void
+usb_ep_free_buffer (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned len)
+{
+       ep->ops->free_buffer (ep, buf, dma, len);
+}
+
+/**
+ * usb_ep_queue - queues (submits) an I/O request to an endpoint.
+ * @ep: the endpoint associated with the request
+ * @req: the request being submitted
+ * @mem_flags: SLAB_* flags to use in case of memory allocation
+ *
+ * this tells the device controller to perform the specified request through
+ * that endpoint (reading or writing a buffer).  when the request completes,
+ * including being canceled by usb_ep_dequeue(), the request's completion
+ * routine is called to return the request to the driver.
+ *
+ * bulk endpoints can queue any amount of data; the transfer is packetized
+ * automatically.  the last packet may be short if the request doesn't fill it
+ * out completely.  zero length packets are only sent by zero length requests,
+ * but should be avoided in portable drivers since not all usb hardware can
+ * successfully handle zero length packets.  bulk endpoints may also be used
+ * for interrupt transfers (but the reverse is not true).
+ *
+ * interrupt-only endpoints are less functional than bulk endpoints, for
+ * example by not supporting queueing or not handling buffers that are
+ * larger than the endpoint's maxpacket size.
+ *
+ * control endpoints ... after getting a setup() callback, the driver queues
+ * one response (perhaps zero length).  that implicitly sends the status ack,
+ * as well as transfering data as specified in the response.  (failing the
+ * setup callback will generate an immediate protocol stall.  this area will
+ * need more attention.)
+ *
+ * for periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+ * have queued data to transfer at that time.
+ *
+ * returns zero, or a negative error code.  endpoints that are not enabled,
+ * or which are enabled but halted, report errors.
+ */
+static inline int
+usb_ep_queue (struct usb_ep *ep, struct usb_request *req, int mem_flags)
+{
+       return ep->ops->queue (ep, req, mem_flags);
+}
+
+/**
+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
+ * @ep: the endpoint associated with the request
+ * @req: the request being canceled
+ *
+ * if the request is still active on the endpoint, it is dequeued and its
+ * completion routine is called (with status -ECONNRESET); else a negative
+ * error code is returned.
+ */
+static inline int usb_ep_dequeue (struct usb_ep *ep, struct usb_request *req)
+{
+       return ep->ops->dequeue (ep, req);
+}
+
+/**
+ * usb_set_halt - set or clear the endpoint halt feature.
+ * @ep: the bulk or interrupt endpoint being stalled or cleared
+ * @is_halted: nonzero value sets the feature, zero clears it.
+ *
+ * when a driver (or host) stalls an endpoint, it stays stalled until the
+ * host issues request to clear the endpoint halt feature, or the endpoint
+ * is disabled.
+ *
+ * returns zero, or a negative error code.
+ */
+static inline int
+usb_ep_set_halt (struct usb_ep *ep, int is_halted)
+{
+       return ep->ops->set_halt (ep, is_halted);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_gadget;
+
+/* the rest of the api to the controller hardware: device operations,
+ * which don't involve endpoints (or i/o).
+ */
+struct usb_gadget_ops {
+       u16     (*get_frame)(struct usb_gadget *);
+       int     (*set_enable_sof)(struct usb_gadget *, int);
+       int     (*remote_wakeup)(struct usb_gadget *);
+};
+
+/**
+ * struct usb_gadget - represents a usb slave device
+ *
+ * gadgets have a mostly-portable driver implementing device functions,
+ * handling all interfaces.  they also have a hardware-specific driver
+ * (accessed through ops vectors), which packages the hardware endpoints.
+ */
+struct usb_gadget {
+       // maintained by the gadget's driver
+       void                            *driver_data;
+
+       // readonly to gadget driver
+       const struct usb_gadget_ops     *ops;
+       struct usb_ep                   *ep0;
+       struct list_head                ep_list;        // of usb_ep
+       enum usb_device_speed           speed;
+       struct device                   *dev;
+};
+
+/* iterates the non-control endpoints; 'tmp' is a struct usb_ep */
+#define gadget_for_each_ep(tmp,gadget) \
+       list_for_each_entry(tmp, struct usb_ep, &(gadget)->ep_list)
+
+/**
+ * usb_get_frame - returns the current frame number
+ */
+static inline u16 usb_get_frame (struct usb_gadget *gadget)
+{
+       return gadget->ops->get_frame (gadget);
+}
+
+/**
+ * usb_set_enable_sof - enables or disables start-of-frame callbacks
+ *
+ * this may be of the most interest to isochronous drivers; it arranges
+ * to call the driver's sof callback at the start of every frame except
+ * when the device has suspended.
+ *
+ * returns zero on success, else negative error code
+ */
+static inline int usb_set_enable_sof (struct usb_gadget *gadget, int do_call)
+{
+       return gadget->ops->set_enable_sof (gadget, do_call);
+}
+
+/**
+ * usb_remote_wakeup - tries to wake up the host connected to this gadget
+ *
+ * returns zero on success, else negative error code.  not all hardware
+ * supports such attempts.
+ */
+static inline int usb_remote_wakeup (struct usb_gadget *gadget)
+{
+       return gadget->ops->remote_wakeup (gadget);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * struct usb_gadget_driver - driver for usb 'slave' devices
+ *
+ * devices are disabled till a gadget driver successfully bind()s, which
+ * means the driver will handle setup() requests needed to enumerate (and
+ * meet "chapter 9" requirements) then do some useful work.
+ *
+ * drivers use hardware-specific knowledge to configure the usb hardware.
+ * endpoint addressing is only one of several hardware characteristics that
+ * are in descriptors the ep0 implementation returns from setup() calls.
+ *
+ * except for ep0 implementation, most driver code shouldn't need change to
+ * run on top of different usb controllers.  it'll use endpoints set up by
+ * that ep0 implementation.
+ */
+struct usb_gadget_driver {
+       char            *name;
+       char            *function;              // description 
+
+       /* lets a driver bind to the usb slave hardware, or unbinds it
+        * on driver rmmod etc.  these calls are made in a context
+        * that can sleep.
+        *
+        * endpoint zero is active when a driver is bound. then setup()
+        * returns descriptors and otherwise cooperates with enumeration.
+        */
+       int             (*bind) (struct usb_gadget *);
+       void            (*unbind) (struct usb_gadget *);
+       
+       /* set_address is always hidden from device drivers.
+        *
+        * get_descriptor must always be implemented, returning at
+        * least device and configuration descriptors.
+        *
+        * set_configuration must always be implemented, and is where
+        * endpoints should be activated or (config 0) shut down.
+        * likewise for set_interface, if altsettings are used
+        *
+        * the driver queues a response to ep0, or returns negative to stall.
+        * note: the 16-bit memembers of the request are in cpu order.
+        */
+       int             (*setup) (struct usb_gadget *,
+                               const struct usb_ctrlrequest *);
+
+       /* called on bus or host disconnect */
+       void            (*disconnect) (struct usb_gadget *);
+
+       /* sof interrupt (1/msec while not suspended) */
+       void            (*sof) (struct usb_gadget *);
+
+       /* usb can suspend due to lack of activity, then resume */
+       void            (*suspend) (struct usb_gadget *);
+       void            (*resume) (struct usb_gadget *);
+
+       /* managed by usb_gadget_*register_driver() calls */
+       struct device_driver    driver;
+
+       // FIXME support safe rmmod
+};
+
+/* driver modules register and unregister, as usual.
+ * these calls must be made in a context that can sleep.
+ *
+ * these will usually be implemented directly by the hardware-dependent
+ * usb bus interface driver, which will only support a single driver.
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *);
+int usb_gadget_unregister_driver (struct usb_gadget_driver *);
+
+#endif  /* __KERNEL__ */
+
+#endif /* __LINUX_USB_GADGET_H */

Reply via email to