Index: firmware/export/linkedlist.h =================================================================== --- firmware/export/linkedlist.h (revision 0) +++ firmware/export/linkedlist.h (revision 0) @@ -0,0 +1,354 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: adc.h 13174 2007-04-15 23:35:56Z amiconn $ + * + * Copyright (C) 2007 by Christian Gmeiner + * + * Original source can be found in linux kernel: /include/list.h + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _LINKED_LIST_H +#define _LINKED_LIST_H + +static inline void prefetch(const void *x) {(void)x;} + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +/* TODO move this macro? */ +/* also in usb_gadget.h */ +/* more about this macro: http://www.kroah.com/log/linux/container_of.html */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + } +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; prefetch(pos->next), pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +#endif /*_LINKED_LIST_H*/ Index: firmware/export/usb_gadget.h =================================================================== --- firmware/export/usb_gadget.h (revision 0) +++ firmware/export/usb_gadget.h (revision 0) @@ -0,0 +1,878 @@ +/* + * + * + * 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. + * + * + * (C) Copyright 2002-2004 by David Brownell + * All Rights Reserved. + * + * This software is licensed under the GNU GPL version 2. + */ + +#ifndef __LINUX_USB_GADGET_H +#define __LINUX_USB_GADGET_H + +#include +#include + +#include "linkedlist.h" +#include "usb_ch9.h" + +#ifndef min +#define min(a, b) (a) < (b) ? a : b +#endif + +struct usb_ep; + +/** + * struct usb_request - describes one i/o request + * @buf: Buffer used for data. Always provide this; some controllers + * only use PIO, or don't use DMA for some endpoints. + * @length: Length of that data + * @no_interrupt: If true, hints that no completion irq is needed. + * Helpful sometimes with deep request queues that are handled + * directly by DMA controllers. + * @zero: If true, when writing data, makes the last packet be "short" + * by adding a zero length packet as needed; + * @short_not_ok: When reading data, makes short packets be + * treated as errors (queue stops advancing till cleanup). + * @complete: Function called when request completes, so this request and + * its buffer may be re-used. + * Reads terminate with a short packet, or when the buffer fills, + * whichever comes first. When writes terminate, some data bytes + * will usually still be in flight (often in a hardware fifo). + * Errors (for reads or writes) stop the queue from advancing + * until the completion function returns, so that any transfers + * invalidated by the error may first be dequeued. + * @context: For use by the completion callback + * @list: For use by the gadget driver. + * @status: Reports completion code, zero or a negative errno. + * Normally, faults block the transfer queue from advancing until + * the completion callback returns. + * Code "-ESHUTDOWN" indicates completion caused by device disconnect, + * or when the driver disabled the endpoint. + * @actual: Reports bytes transferred to/from the buffer. For reads (OUT + * transfers) this may be less than the requested length. If the + * short_not_ok flag is set, short reads are treated as errors + * even when status otherwise indicates successful completion. + * Note that for writes (IN transfers) some data bytes may still + * reside in a device-side FIFO when the request is reported as + * complete. + * + * These are allocated/freed through the endpoint they're used with. The + * hardware's driver can add extra per-request data to the memory it returns, + * which often avoids separate memory allocations (potential failures), + * later when the request is queued. + * + * Request flags affect request handling, such as whether a zero length + * packet is written (the "zero" flag), whether a short read should be + * treated as an error (blocking request queue advance, the "short_not_ok" + * flag), or hinting that an interrupt is not required (the "no_interrupt" + * flag, for use with deep request queues). + * + * Bulk endpoints can use any size buffers, and can also be used for interrupt + * transfers. interrupt-only endpoints can be much less functional. + */ + // NOTE this is analagous to 'struct urb' on the host side, + // except that it's thinner and promotes more pre-allocation. + +struct usb_request { + void *buf; + unsigned length; + + unsigned no_interrupt:1; + unsigned zero:1; + unsigned short_not_ok:1; + + void (*complete)(struct usb_ep *ep, + struct usb_request *req); + void *context; + struct list_head list; + + int status; + unsigned actual; +}; + +/*-------------------------------------------------------------------------*/ + +/* endpoint-specific parts of the api to the usb controller hardware. + * unlike the urb model, (de)multiplexing layers are not required. + * (so this api could slash overhead if used on the host side...) + * + * 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/*, + gfp_t gfp_flags*/); + void (*free_request) (struct usb_ep *ep, struct usb_request *req); + + void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes); + void (*free_buffer) (struct usb_ep *ep, void *buf); + + int (*queue) (struct usb_ep *ep, struct usb_request *req/*, + gfp_t gfp_flags*/); + int (*dequeue) (struct usb_ep *ep, struct usb_request *req); + + int (*set_halt) (struct usb_ep *ep, int value); + int (*fifo_status) (struct usb_ep *ep); + void (*fifo_flush) (struct usb_ep *ep); +}; + +/** + * struct usb_ep - device side representation of USB endpoint + * @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk" + * @ops: Function pointers used to access hardware-specific operations. + * @ep_list:the gadget's ep_list holds all of its endpoints + * @maxpacket:The maximum packet size used on this endpoint. The initial + * value can sometimes be reduced (hardware allowing), according to + * the endpoint descriptor used to configure the endpoint. + * @driver_data:for use by the gadget driver. all other fields are + * read-only to gadget drivers. + * + * 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 { + void *driver_data; + + const char *name; + const struct usb_ep_ops *ops; + struct list_head ep_list; + unsigned maxpacket:16; +}; + +/*-------------------------------------------------------------------------*/ + +/** + * 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; the data byte order + * is little-endian (usb-standard). + * + * 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 (which calls this routine) 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 USB master".) + * + * 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. + * any pending and uncompleted requests will complete with status + * indicating disconnect (-ESHUTDOWN) before this call returns. + * gadget drivers must call usb_ep_enable() again before queueing + * requests to the endpoint. + * + * 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 + * @gfp_flags:GFP_* flags to use + * + * Request objects must be allocated with this call, since they normally + * need controller-specific setup and may even need endpoint-specific + * resources such as allocation of DMA descriptors. + * 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/*, gfp_t gfp_flags*/) +{ + return ep->ops->alloc_request (ep/*, gfp_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 + * @gfp_flags:GFP_* flags to use + * + * Returns a new buffer, or null if one could not be allocated. + * The buffer is suitably aligned for dma, if that endpoint uses DMA, + * and the caller won't have to care about dma-inconsistency + * or any hidden "bounce buffer" mechanism. No additional per-request + * DMA mapping will be required for such buffers. + * Free it later with usb_ep_free_buffer(). + * + * You don't need to use this call to allocate I/O buffers unless you + * want to make sure drivers don't incur costs for such "bounce buffer" + * copies or per-request DMA mappings. + */ +static inline void * +usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len) +{ + return ep->ops->alloc_buffer (ep, len); +} + +/** + * 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) +{ + ep->ops->free_buffer (ep, buf); +} + +/** + * usb_ep_queue - queues (submits) an I/O request to an endpoint. + * @ep:the endpoint associated with the request + * @req:the request being submitted + * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't + * pre-allocate all necessary memory with the request. + * + * 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. Any endpoint + * (except control endpoints like ep0) may have more than one transfer + * request queued; they complete in FIFO order. Once a gadget driver + * submits a request, that request may not be examined or modified until it + * is given back to that driver through the completion callback. + * + * Each request is turned into one or more packets. The controller driver + * never merges adjacent requests into the same packet. OUT transfers + * will sometimes use data that's already buffered in the hardware. + * Drivers can rely on the fact that the first byte of the request's buffer + * always corresponds to the first byte of some USB packet, for both + * IN and OUT transfers. + * + * Bulk endpoints can queue any amount of data; the transfer is packetized + * automatically. The last packet will be short if the request doesn't fill it + * out completely. Zero length packets (ZLPs) should be avoided in portable + * protocols since not all usb hardware can successfully handle zero length + * packets. (ZLPs may be explicitly written, and may be implicitly written if + * the request 'zero' flag is set.) Bulk endpoints may also be used + * for interrupt transfers; but the reverse is not true, and some endpoints + * won't support every interrupt transfer. (Such as 768 byte packets.) + * + * 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. They may also treat data + * toggle differently. + * + * Control endpoints ... after getting a setup() callback, the driver queues + * one response (even if it would be zero length). That enables the + * status ack, after transfering data as specified in the response. Setup + * functions may return negative error codes to generate protocol stalls. + * (Note that some USB device controllers disallow protocol stall responses + * in some cases.) When control responses are deferred (the response is + * written after the setup callback returns), then usb_ep_set_halt() may be + * used on ep0 to trigger protocol stalls. + * + * 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 some data to transfer at that time. + * + * Returns zero, or a negative error code. Endpoints that are not enabled + * report errors; errors will also be + * reported when the usb peripheral is disconnected. + */ +static inline int +usb_ep_queue (struct usb_ep *ep, struct usb_request *req) +{ + return ep->ops->queue (ep, req); +} + +/** + * 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. + * + * note that some hardware can't clear out write fifos (to unlink the request + * at the head of the queue) except as part of disconnecting from usb. such + * restrictions prevent drivers from supporting configuration changes, + * even to configuration zero (a "chapter 9" requirement). + */ +static inline int usb_ep_dequeue (struct usb_ep *ep, struct usb_request *req) +{ + return ep->ops->dequeue (ep, req); +} + +/** + * usb_ep_set_halt - sets the endpoint halt feature. + * @ep: the non-isochronous endpoint being stalled + * + * Use this to stall an endpoint, perhaps as an error report. + * Except for control endpoints, + * the endpoint stays halted (will not stream any data) until the host + * clears this feature; drivers may need to empty the endpoint's request + * queue first, to make sure no inappropriate transfers happen. + * + * Note that while an endpoint CLEAR_FEATURE will be invisible to the + * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the + * current altsetting, see usb_ep_clear_halt(). When switching altsettings, + * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. + * + * Returns zero, or a negative error code. On success, this call sets + * underlying hardware state that blocks data transfers. + * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any + * transfer requests are still queued, or if the controller hardware + * (usually a FIFO) still holds bytes that the host hasn't collected. + */ +static inline int +usb_ep_set_halt (struct usb_ep *ep) +{ + return ep->ops->set_halt (ep, 1); +} + +/** + * usb_ep_clear_halt - clears endpoint halt, and resets toggle + * @ep:the bulk or interrupt endpoint being reset + * + * Use this when responding to the standard usb "set interface" request, + * for endpoints that aren't reconfigured, after clearing any other state + * in the endpoint's i/o queue. + * + * Returns zero, or a negative error code. On success, this call clears + * the underlying hardware state reflecting endpoint halt and data toggle. + * Note that some hardware can't support this request (like pxa2xx_udc), + * and accordingly can't correctly implement interface altsettings. + */ +static inline int +usb_ep_clear_halt (struct usb_ep *ep) +{ + return ep->ops->set_halt (ep, 0); +} + +/** + * usb_ep_fifo_status - returns number of bytes in fifo, or error + * @ep: the endpoint whose fifo status is being checked. + * + * FIFO endpoints may have "unclaimed data" in them in certain cases, + * such as after aborted transfers. Hosts may not have collected all + * the IN data written by the gadget driver (and reported by a request + * completion). The gadget driver may not have collected all the data + * written OUT to it by the host. Drivers that need precise handling for + * fault reporting or recovery may need to use this call. + * + * This returns the number of such bytes in the fifo, or a negative + * errno if the endpoint doesn't use a FIFO or doesn't support such + * precise handling. + */ +static inline int +usb_ep_fifo_status (struct usb_ep *ep) +{ + if (ep->ops->fifo_status) + return ep->ops->fifo_status (ep); + else + return -EOPNOTSUPP; +} + +/** + * usb_ep_fifo_flush - flushes contents of a fifo + * @ep: the endpoint whose fifo is being flushed. + * + * This call may be used to flush the "unclaimed data" that may exist in + * an endpoint fifo after abnormal transaction terminations. The call + * must never be used except when endpoint is not being used for any + * protocol translation. + */ +static inline void +usb_ep_fifo_flush (struct usb_ep *ep) +{ + if (ep->ops->fifo_flush) + ep->ops->fifo_flush (ep); +} + + +/*-------------------------------------------------------------------------*/ + +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 { + int (*get_frame)(struct usb_gadget *); + int (*wakeup)(struct usb_gadget *); + int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered); + int (*vbus_session) (struct usb_gadget *, int is_active); + int (*vbus_draw) (struct usb_gadget *, unsigned mA); + int (*pullup) (struct usb_gadget *, int is_on); + int (*ioctl)(struct usb_gadget *, + unsigned code, unsigned long param); +}; + +/** + * struct usb_gadget - represents a usb slave device + * @ops: Function pointers used to access hardware-specific operations. + * @ep0: Endpoint zero, used when reading or writing responses to + * driver setup() requests + * @ep_list: List of other endpoints supported by the device. + * @speed: Speed of current connection to USB host. + * @is_dualspeed: True if the controller supports both high and full speed + * operation. If it does, the gadget driver must also support both. + * @is_otg: True if the USB device port uses a Mini-AB jack, so that the + * gadget driver must provide a USB OTG descriptor. + * @is_a_peripheral: False unless is_otg, the "A" end of a USB cable + * is in the Mini-AB jack, and HNP has been used to switch roles + * so that the "A" device currently acts as A-Peripheral, not A-Host. + * @a_hnp_support: OTG device feature flag, indicating that the A-Host + * supports HNP at this port. + * @a_alt_hnp_support: OTG device feature flag, indicating that the A-Host + * only supports HNP on a different root port. + * @b_hnp_enable: OTG device feature flag, indicating that the A-Host + * enabled HNP support. + * @name: Identifies the controller hardware type. Used in diagnostics + * and sometimes configuration. + * @dev: Driver model state for this abstract device. + * + * Gadgets have a mostly-portable "gadget driver" implementing device + * functions, handling all usb configurations and interfaces. Gadget + * drivers talk to hardware-specific code indirectly, through ops vectors. + * That insulates the gadget driver from hardware details, and packages + * the hardware endpoints through generic i/o queues. The "usb_gadget" + * and "usb_ep" interfaces provide that insulation from the hardware. + * + * Except for the driver data, all fields in this structure are + * read-only to the gadget driver. That driver data is part of the + * "driver model" infrastructure in 2.6 (and later) kernels, and for + * earlier systems is grouped in a similar structure that's not known + * to the rest of the kernel. + * + * Values of the three OTG device feature flags are updated before the + * setup() call corresponding to USB_REQ_SET_CONFIGURATION, and before + * driver suspend() calls. They are valid only when is_otg, and when the + * device is acting as a B-Peripheral (so is_a_peripheral is false). + */ +struct usb_gadget { + /* 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; + unsigned is_dualspeed:1; + 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; + const char *name; + void *data; +}; + +static inline void set_gadget_data (struct usb_gadget *gadget, void *data) + { gadget->data = data; } +static inline void *get_gadget_data (struct usb_gadget *gadget) + { return gadget->data; } + +/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */ +#define gadget_for_each_ep(tmp,gadget) \ + list_for_each_entry(tmp, &(gadget)->ep_list, ep_list) + + +/** + * usb_gadget_frame_number - returns the current frame number + * @gadget: controller that reports the frame number + * + * Returns the usb frame number, normally eleven bits from a SOF packet, + * or negative errno if this device doesn't support this capability. + */ +static inline int usb_gadget_frame_number (struct usb_gadget *gadget) +{ + return gadget->ops->get_frame (gadget); +} + +/** + * usb_gadget_wakeup - tries to wake up the host connected to this gadget + * @gadget: controller used to wake up the host + * + * Returns zero on success, else negative error code if the hardware + * doesn't support such attempts, or its support has not been enabled + * by the usb host. Drivers must return device descriptors that report + * their ability to support this, or hosts won't enable it. + * + * This may also try to use SRP to wake the host and start enumeration, + * even if OTG isn't otherwise in use. OTG devices may also start + * remote wakeup even when hosts don't explicitly enable it. + */ +static inline int usb_gadget_wakeup (struct usb_gadget *gadget) +{ + if (!gadget->ops->wakeup) + return -EOPNOTSUPP; + return gadget->ops->wakeup (gadget); +} + +/** + * usb_gadget_set_selfpowered - sets the device selfpowered feature. + * @gadget:the device being declared as self-powered + * + * this affects the device status reported by the hardware driver + * to reflect that it now has a local power supply. + * + * returns zero on success, else negative errno. + */ +static inline int +usb_gadget_set_selfpowered (struct usb_gadget *gadget) +{ + if (!gadget->ops->set_selfpowered) + return -EOPNOTSUPP; + return gadget->ops->set_selfpowered (gadget, 1); +} + +/** + * usb_gadget_clear_selfpowered - clear the device selfpowered feature. + * @gadget:the device being declared as bus-powered + * + * this affects the device status reported by the hardware driver. + * some hardware may not support bus-powered operation, in which + * case this feature's value can never change. + * + * returns zero on success, else negative errno. + */ +static inline int +usb_gadget_clear_selfpowered (struct usb_gadget *gadget) +{ + if (!gadget->ops->set_selfpowered) + return -EOPNOTSUPP; + return gadget->ops->set_selfpowered (gadget, 0); +} + +/** + * usb_gadget_vbus_connect - Notify controller that VBUS is powered + * @gadget:The device which now has VBUS power. + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session starting. Common responses include + * resuming the controller, activating the D+ (or D-) pullup to let the + * host detect that a USB device is attached, and starting to draw power + * (8mA or possibly more, especially after SET_CONFIGURATION). + * + * Returns zero on success, else negative errno. + */ +static inline int +usb_gadget_vbus_connect(struct usb_gadget *gadget) +{ + if (!gadget->ops->vbus_session) + return -EOPNOTSUPP; + return gadget->ops->vbus_session (gadget, 1); +} + +/** + * usb_gadget_vbus_draw - constrain controller's VBUS power usage + * @gadget:The device whose VBUS usage is being described + * @mA:How much current to draw, in milliAmperes. This should be twice + * the value listed in the configuration descriptor bMaxPower field. + * + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static inline int +usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + if (!gadget->ops->vbus_draw) + return -EOPNOTSUPP; + return gadget->ops->vbus_draw (gadget, mA); +} + +/** + * usb_gadget_vbus_disconnect - notify controller about VBUS session end + * @gadget:the device whose VBUS supply is being described + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session ending. Common responses include + * reversing everything done in usb_gadget_vbus_connect(). + * + * Returns zero on success, else negative errno. + */ +static inline int +usb_gadget_vbus_disconnect(struct usb_gadget *gadget) +{ + if (!gadget->ops->vbus_session) + return -EOPNOTSUPP; + return gadget->ops->vbus_session (gadget, 0); +} + +/** + * usb_gadget_connect - software-controlled connect to USB host + * @gadget:the peripheral being connected + * + * Enables the D+ (or potentially D-) pullup. The host will start + * enumerating this gadget when the pullup is active and a VBUS session + * is active (the link is powered). This pullup is always enabled unless + * usb_gadget_disconnect() has been used to disable it. + * + * Returns zero on success, else negative errno. + */ +static inline int +usb_gadget_connect (struct usb_gadget *gadget) +{ + if (!gadget->ops->pullup) + return -EOPNOTSUPP; + return gadget->ops->pullup (gadget, 1); +} + +/** + * usb_gadget_disconnect - software-controlled disconnect from USB host + * @gadget:the peripheral being disconnected + * + * Disables the D+ (or potentially D-) pullup, which the host may see + * as a disconnect (when a VBUS session is active). Not all systems + * support software pullup controls. + * + * This routine may be used during the gadget driver bind() call to prevent + * the peripheral from ever being visible to the USB host, unless later + * usb_gadget_connect() is called. For example, user mode components may + * need to be activated before the system can talk to hosts. + * + * Returns zero on success, else negative errno. + */ +static inline int +usb_gadget_disconnect (struct usb_gadget *gadget) +{ + if (!gadget->ops->pullup) + return -EOPNOTSUPP; + return gadget->ops->pullup (gadget, 0); +} + + + +/*-------------------------------------------------------------------------*/ + +/** + * struct usb_gadget_driver - driver for usb 'slave' devices + * @function: String describing the gadget's function + * @speed: Highest speed the driver handles. + * @bind: Invoked when the driver is bound to a gadget, usually + * after registering the driver. + * At that point, ep0 is fully initialized, and ep_list holds + * the currently-available endpoints. + * Called in a context that permits sleeping. + * @setup: Invoked for ep0 control requests that aren't handled by + * the hardware level driver. Most calls must be handled by + * the gadget driver, including descriptor and configuration + * management. The 16 bit members of the setup data are in + * USB byte order. Called in_interrupt; this may not sleep. Driver + * queues a response to ep0, or returns negative to stall. + * @disconnect: Invoked after all transfers have been stopped, + * when the host is disconnected. May be called in_interrupt; this + * may not sleep. Some devices can't detect disconnect, so this might + * not be called except as part of controller shutdown. + * @unbind: Invoked when the driver is unbound from a gadget, + * usually from rmmod (after a disconnect is reported). + * Called in a context that permits sleeping. + * @suspend: Invoked on USB suspend. May be called in_interrupt. + * @resume: Invoked on USB resume. May be called in_interrupt. + * @driver: Driver model state for this driver. + * + * 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. + * + * If gadget->is_otg is true, the gadget driver must provide an OTG + * descriptor during enumeration, or else fail the bind() call. In such + * cases, no USB traffic may flow until both bind() returns without + * having called usb_gadget_disconnect(), and the USB host stack has + * initialized. + * + * 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. + * + * The usb controller driver handles a few standard usb requests. Those + * include set_address, and feature flags for devices, interfaces, and + * endpoints (the get_status, set_feature, and clear_feature requests). + * + * Accordingly, the driver's setup() callback must always implement all + * get_descriptor requests, returning at least a device descriptor and + * a configuration descriptor. Drivers must make sure the endpoint + * descriptors match any hardware constraints. Some hardware also constrains + * other descriptors. (The pxa250 allows only configurations 1, 2, or 3). + * + * The driver's setup() callback must also implement set_configuration, + * and should also implement set_interface, get_configuration, and + * get_interface. Setting a configuration (or interface) is where + * endpoints should be activated or (config 0) shut down. + * + * (Note that only the default control endpoint is supported. Neither + * hosts nor devices generally support control traffic except to ep0.) + * + * Most devices will ignore USB suspend/resume operations, and so will + * not provide those callbacks. However, some may need to change modes + * when the host is not longer directing those activities. For example, + * local controls (buttons, dials, etc) may need to be re-enabled since + * the (remote) host can't do that any longer; or an error state might + * be cleared, to make the device behave identically whether or not + * power is maintained. + */ +struct usb_gadget_driver { + char *function; + enum usb_device_speed speed; + int (*bind)(struct usb_gadget *); + void (*unbind)(struct usb_gadget *); + int (*setup)(struct usb_gadget *, + const struct usb_ctrlrequest *); + void (*disconnect)(struct usb_gadget *); + void (*suspend)(struct usb_gadget *); + void (*resume)(struct usb_gadget *); + + // FIXME support safe rmmod +/* struct device_driver driver;*/ +}; + + + +/*-------------------------------------------------------------------------*/ + +/* 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. + */ + +/** + * usb_gadget_register_driver - register a gadget driver + * @driver:the driver being registered + * + * Call this in your gadget driver's module initialization function, + * to tell the underlying usb controller driver about your driver. + * The driver's bind() function will be called to bind it to a + * gadget before this registration call returns. It's expected that + * the bind() functions will be in init sections. + * This function must be called in a context that can sleep. + */ +int usb_gadget_register_driver (struct usb_gadget_driver *driver); + +/** + * usb_gadget_unregister_driver - unregister a gadget driver + * @driver:the driver being unregistered + * + * Call this in your gadget driver's module cleanup function, + * to tell the underlying usb controller that your driver is + * going away. If the controller is connected to a USB host, + * it will first disconnect(). The driver is also requested + * to unbind() and clean up any device state, before this procedure + * finally returns. It's expected that the unbind() functions + * will in in exit sections, so may not be linked in some kernels. + * This function must be called in a context that can sleep. + */ +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver); + +/*-------------------------------------------------------------------------*/ + +/* utility to simplify dealing with string descriptors */ + +/** + * struct usb_string - wraps a C string and its USB id + * @id:the (nonzero) ID for this string + * @s:the string, in UTF-8 encoding + * + * If you're using usb_gadget_get_string(), use this to wrap a string + * together with its ID. + */ +struct usb_string { + u8 id; + const char *s; +}; + +/** + * struct usb_gadget_strings - a set of USB strings in a given language + * @language:identifies the strings' language (0x0409 for en-us) + * @strings:array of strings with their ids + * + * If you're using usb_gadget_get_string(), use this to wrap all the + * strings for a given language. + */ +struct usb_gadget_strings { + u16 language; /* 0x0409 for en-us */ + struct usb_string *strings; +}; + +/* put descriptor for string with that id into buf (buflen >= 256) */ +int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf); + +/*-------------------------------------------------------------------------*/ + +/* utility to simplify managing config descriptors */ + +/* write vector of descriptors into buffer */ +int usb_descriptor_fillbuf(void *, unsigned, + const struct usb_descriptor_header **); + +/* build config descriptor from single descriptor vector */ +int usb_gadget_config_buf(const struct usb_config_descriptor *config, + void *buf, unsigned buflen, const struct usb_descriptor_header **desc); + +/*-------------------------------------------------------------------------*/ + +/* utility wrapping a simple endpoint selection policy */ + +extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *, + struct usb_endpoint_descriptor *); + +extern void usb_ep_autoconfig_reset (struct usb_gadget *); + +#endif /* __LINUX_USB_GADGET_H */ Index: firmware/export/arcotg_udc.h =================================================================== --- firmware/export/arcotg_udc.h (revision 13869) +++ firmware/export/arcotg_udc.h (working copy) @@ -36,9 +36,9 @@ #define __ARCOTG_UDC_H #include "cpu.h" +#include "system.h" +#include "usb_gadget.h" -#define ETIMEDOUT 1 - #define USB_MAX_ENDPOINTS 8 #define USB_MAX_PIPES (USB_MAX_ENDPOINTS*2) #define USB_MAX_CTRL_PAYLOAD 64 @@ -312,9 +312,151 @@ #define USB_CTRL_IOENB (0x00000004) #define USB_CTRL_ULPI_INT0EN (0x00000001) +/* Endpoint Queue Head Bit Masks */ +#define EP_QUEUE_HEAD_MULT_POS (30) +#define EP_QUEUE_HEAD_ZLT_SEL (0x20000000) +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS (16) +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS (0x00008000) +#define EP_QUEUE_HEAD_NEXT_TERMINATE (0x00000001) +#define EP_QUEUE_HEAD_IOC (0x00008000) +#define EP_QUEUE_HEAD_MULTO (0x00000C00) +#define EP_QUEUE_HEAD_STATUS_HALT (0x00000040) +#define EP_QUEUE_HEAD_STATUS_ACTIVE (0x00000080) +#define EP_QUEUE_CURRENT_OFFSET_MASK (0x00000FFF) +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK (0xFFFFFFE0) +#define EP_QUEUE_FRINDEX_MASK (0x000007FF) +#define EP_MAX_LENGTH_TRANSFER (0x4000) + +/* pipe direction macro from device view */ +#define USB_RECV (0) /* OUT EP */ +#define USB_SEND (1) /* IN EP */ + /* Externally used functions */ -int dr_controller_setup(void); +void arcotg_init(void); void dr_controller_run(void); void dr_controller_stop(void); + +/* ep name is important in gadget, it should obey the convention of ep_match() */ +/* even numbered EPs are OUT or setup, odd are IN/INTERRUPT */ +static const char *const ep_name[] = { + "ep0-control", NULL, /* everyone has ep0 */ + /* 7 configurable endpoints */ + "ep1out", + "ep1in", + "ep2out", + "ep2in", + "ep3out", + "ep3in", + "ep4out", + "ep4in", + "ep5out", + "ep5in", + "ep6out", + "ep6in", + "ep7out", + "ep7in" +}; + +/* controller datastructues */ + +/* Endpoint Transfer Descriptor data struct */ +struct ep_dtd { + u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set indicate invalid */ + u32 caps; /* Total bytes (30-16), IOC (15), MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 res; /* make it an even 8 words */ +} __attribute((packed)); + +struct ep_queue_head { + u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len + * and IOS(15) */ + u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ + struct ep_dtd dtd; /* Transfer descriptor */ + u8 setup_buffer[8]; /* Setup data 8 bytes */ + u32 res2[4]; /* pad out to 64 bytes */ +} __attribute((packed)); + + +/* Endpoint Transfer Descriptor bit Masks */ +#define DTD_NEXT_TERMINATE (1 << 0) +#define DTD_IOC (1 << 15) +#define DTD_STATUS_ACTIVE (1 << 7) +#define DTD_STATUS_HALTED (1 << 6) +#define DTD_STATUS_DATA_BUFF_ERR (1 << 5) +#define DTD_STATUS_TRANSACTION_ERR (1 << 4) +#define DTD_RESERVED_FIELDS ( (1 << 0) | (1 << 2) | (1 << 4) | \ + (1 << 8) | (1 << 9) | (1 << 12)| \ + (1 << 13)| (1 << 14)| (1 << 31) ) + +#define DTD_ADDR_MASK (0xFFFFFFE0) +#define DTD_PACKET_SIZE (0x7FFF0000) +#define DTD_LENGTH_BIT_POS (16) +#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) + + +#define REQUEST_BUFFER_SIZE 64 +#define DTD_PRO_REQUERS 4 +struct arcotg_req { + struct usb_request req; + struct list_head queue; + /* ep_queue() func will add + a request->queue into a udc_ep->queue 'd tail */ + struct arcotg_ep *ep; + bool used; + + struct ep_td_struct *head, *tail; /* For dTD List */ + unsigned int dtd_count; +}; + +struct arcotg_ep { + struct usb_ep ep; + struct list_head queue; + struct arcotg_udc *udc; + const struct usb_endpoint_descriptor *desc; + struct usb_gadget *gadget; + + char name[14]; + bool stopped; + + /* static buffers */ + struct arcotg_req req[REQUEST_BUFFER_SIZE]; +}; + +struct arcotg_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct arcotg_ep eps[USB_MAX_ENDPOINTS * 2]; + + struct usb_ctrlrequest local_setup_buff; + struct mutex lock; + bool stopped; + bool bus_reset; /* Device is bus reseting */ + enum usb_device_state resume_state; /* USB state to resume */ + enum usb_device_state usb_state; /* USB current state */ + u32 ep0_dir; /* Enpoint zero direction: can be + * USB_DIR_IN or USB_DIR_OUT */ + + u8 device_address; /* Device USB address */ +}; + +/* some helper macros */ + +#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) + +#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ + USB_DIR_IN ):((EP)->desc->bEndpointAddress \ + & USB_DIR_IN)==USB_DIR_IN) + +#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc.eps[0]: \ + &udc.eps[pipe]) + #endif /* __ARCOTG_UDC_H */ Index: firmware/export/usb_ser.h =================================================================== --- firmware/export/usb_ser.h (revision 0) +++ firmware/export/usb_ser.h (revision 0) @@ -0,0 +1,26 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: usb.h 12169 2007-02-01 10:47:22Z pondlife $ + * + * Copyright (C) 2007 by Christian Gmeiner + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _USB_SER_H_ +#define _USB_SER_H_ + +#include "usb_gadget.h" + +const struct usb_gadget_driver gs_gadget_driver; + +#endif /*_USB_SER_H_*/ Index: firmware/export/usb_ch9.h =================================================================== --- firmware/export/usb_ch9.h (revision 0) +++ firmware/export/usb_ch9.h (revision 0) @@ -0,0 +1,577 @@ +/* + * This file holds USB constants and structures that are needed for + * USB device APIs. These are used by the USB device model, which is + * defined in chapter 9 of the USB 2.0 specification and in the + * Wireless USB 1.0 (spread around). Linux has several APIs in C that + * need these: + * + * - the master/host side Linux-USB kernel driver API; + * - the "usbfs" user space API; and + * - the Linux "gadget" slave/device/peripheral side driver API. + * + * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems + * act either as a USB master/host or as a USB slave/device. That means + * the master and slave side APIs benefit from working well together. + * + * There's also "Wireless USB", using low power short range radios for + * peripheral interconnection but otherwise building on the USB framework. + * + * Note all descriptors are declared '__attribute__((packed))' so that: + * + * [a] they never get padded, either internally (USB spec writers + * probably handled that) or externally; + * + * [b] so that accessing bigger-than-a-bytes fields will never + * generate bus errors on any platform, even when the location of + * its descriptor inside a bundle isn't "naturally aligned", and + * + * [c] for consistency, removing all doubt even when it appears to + * someone that the two other points are non-issues for that + * particular descriptor type. + */ + +#ifndef __LINUX_USB_CH9_H +#define __LINUX_USB_CH9_H + +#include "linux2rb.h" + +/*-------------------------------------------------------------------------*/ + +/* CONTROL REQUEST SUPPORT */ + +/* + * USB directions + * + * This bit flag is used in endpoint descriptors' bEndpointAddress field. + * It's also one of three fields in control requests bRequestType. + */ +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB types, the second of three bRequestType fields + */ +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients, the third of three bRequestType fields + */ +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 +/* From Wireless USB 1.0 */ +#define USB_RECIP_PORT 0x04 +#define USB_RECIP_RPIPE 0x05 + +/* + * Standard requests, for the bRequest field of a SETUP packet. + * + * These are qualified by the bRequestType field, so that for example + * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved + * by a GET_STATUS request. + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */ +#define USB_REQ_GET_ENCRYPTION 0x0E +#define USB_REQ_RPIPE_ABORT 0x0E +#define USB_REQ_SET_HANDSHAKE 0x0F +#define USB_REQ_RPIPE_RESET 0x0F +#define USB_REQ_GET_HANDSHAKE 0x10 +#define USB_REQ_SET_CONNECTION 0x11 +#define USB_REQ_SET_SECURITY_DATA 0x12 +#define USB_REQ_GET_SECURITY_DATA 0x13 +#define USB_REQ_SET_WUSB_DATA 0x14 +#define USB_REQ_LOOPBACK_DATA_WRITE 0x15 +#define USB_REQ_LOOPBACK_DATA_READ 0x16 +#define USB_REQ_SET_INTERFACE_DS 0x17 + +/* + * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and + * are read as a bit array returned by USB_REQ_GET_STATUS. (So there + * are at most sixteen features of each type.) + */ +#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ +#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ +#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ +#define USB_DEVICE_BATTERY 2 /* (wireless) */ +#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ +#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless)*/ +#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ +#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ +#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ + +#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ + + +/** + * struct usb_ctrlrequest - SETUP data for a USB device control request + * @bRequestType: matches the USB bmRequestType field + * @bRequest: matches the USB bRequest field + * @wValue: matches the USB wValue field (le16 byte order) + * @wIndex: matches the USB wIndex field (le16 byte order) + * @wLength: matches the USB wLength field (le16 byte order) + * + * This structure is used to send control requests to a USB device. It matches + * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the + * USB spec for a fuller description of the different fields, and what they are + * used for. + * + * Note that the driver for any interface can issue control requests. + * For most devices, interfaces don't coordinate with each other, so + * such requests may be made at any time. + */ +struct usb_ctrlrequest { + __u8 bRequestType; + __u8 bRequest; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* + * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or + * (rarely) accepted by SET_DESCRIPTOR. + * + * Note that all multi-byte values here are encoded in little endian + * byte order "on the wire". But when exposed through Linux-USB APIs, + * they've been converted to cpu byte order. + */ + +/* + * Descriptor types ... USB 2.0 spec table 9.5 + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_INTERFACE_POWER 0x08 +/* these are from a minor usb 2.0 revision (ECN) */ +#define USB_DT_OTG 0x09 +#define USB_DT_DEBUG 0x0a +#define USB_DT_INTERFACE_ASSOCIATION 0x0b +/* these are from the Wireless USB spec */ +#define USB_DT_SECURITY 0x0c +#define USB_DT_KEY 0x0d +#define USB_DT_ENCRYPTION_TYPE 0x0e +#define USB_DT_BOS 0x0f +#define USB_DT_DEVICE_CAPABILITY 0x10 +#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11 +#define USB_DT_WIRE_ADAPTER 0x21 +#define USB_DT_RPIPE 0x22 + +/* conventional codes for class-specific descriptors */ +#define USB_DT_CS_DEVICE 0x21 +#define USB_DT_CS_CONFIG 0x22 +#define USB_DT_CS_STRING 0x23 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 + +/* All standard descriptors have these 2 fields at the beginning */ +struct usb_descriptor_header { + __u8 bLength; + __u8 bDescriptorType; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEVICE: Device descriptor */ +struct usb_device_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdUSB; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + __u8 bMaxPacketSize0; + __le16 idVendor; + __le16 idProduct; + __le16 bcdDevice; + __u8 iManufacturer; + __u8 iProduct; + __u8 iSerialNumber; + __u8 bNumConfigurations; +} __attribute__ ((packed)); + +#define USB_DT_DEVICE_SIZE 18 + + +/* + * Device and/or Interface Class codes + * as found in bDeviceClass or bInterfaceClass + * and defined by www.usb.org documents + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b /* chip+ smart card */ +#define USB_CLASS_CONTENT_SEC 0x0d /* content security */ +#define USB_CLASS_VIDEO 0x0e +#define USB_CLASS_WIRELESS_CONTROLLER 0xe0 +#define USB_CLASS_MISC 0xef +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_CONFIG: Configuration descriptor information. + * + * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the + * descriptor type is different. Highspeed-capable devices can look + * different depending on what speed they're currently running. Only + * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG + * descriptors. + */ +struct usb_config_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wTotalLength; + __u8 bNumInterfaces; + __u8 bConfigurationValue; + __u8 iConfiguration; + __u8 bmAttributes; + __u8 bMaxPower; +} __attribute__ ((packed)); + +#define USB_DT_CONFIG_SIZE 9 + +/* from config descriptor bmAttributes */ +#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */ +#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */ +#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */ +#define USB_CONFIG_ATT_BATTERY (1 << 4) /* battery powered */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_STRING: String descriptor */ +struct usb_string_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wData[1]; /* UTF-16LE encoded */ +} __attribute__ ((packed)); + +/* note that "string" zero is special, it holds language codes that + * the device supports, not Unicode characters. + */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_INTERFACE: Interface descriptor */ +struct usb_interface_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + __u8 bNumEndpoints; + __u8 bInterfaceClass; + __u8 bInterfaceSubClass; + __u8 bInterfaceProtocol; + __u8 iInterface; +} __attribute__ ((packed)); + +#define USB_DT_INTERFACE_SIZE 9 + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_ENDPOINT: Endpoint descriptor */ +struct usb_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bEndpointAddress; + __u8 bmAttributes; + __le16 wMaxPacketSize; + __u8 bInterval; + + /* NOTE: these two are _only_ in audio endpoints. */ + /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ + __u8 bRefresh; + __u8 bSynchAddress; +} __attribute__ ((packed)); + +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ + + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */ +struct usb_qualifier_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdUSB; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + __u8 bMaxPacketSize0; + __u8 bNumConfigurations; + __u8 bRESERVED; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_OTG (from OTG 1.0a supplement) */ +struct usb_otg_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bmAttributes; /* support for HNP, SRP, etc */ +} __attribute__ ((packed)); + +/* from usb_otg_descriptor.bmAttributes */ +#define USB_OTG_SRP (1 << 0) +#define USB_OTG_HNP (1 << 1) /* swap host/device roles */ + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEBUG: for special highspeed devices, replacing serial console */ +struct usb_debug_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + /* bulk endpoints with 8 byte maxpacket */ + __u8 bDebugInEndpoint; + __u8 bDebugOutEndpoint; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */ +struct usb_interface_assoc_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bFirstInterface; + __u8 bInterfaceCount; + __u8 bFunctionClass; + __u8 bFunctionSubClass; + __u8 bFunctionProtocol; + __u8 iFunction; +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_SECURITY: group of wireless security descriptors, including + * encryption types available for setting up a CC/association. + */ +struct usb_security_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wTotalLength; + __u8 bNumEncryptionTypes; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_KEY: used with {GET,SET}_SECURITY_DATA; only public keys + * may be retrieved. + */ +struct usb_key_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 tTKID[3]; + __u8 bReserved; + __u8 bKeyData[0]; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_ENCRYPTION_TYPE: bundled in DT_SECURITY groups */ +struct usb_encryption_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bEncryptionType; +#define USB_ENC_TYPE_UNSECURE 0 +#define USB_ENC_TYPE_WIRED 1 /* non-wireless mode */ +#define USB_ENC_TYPE_CCM_1 2 /* aes128/cbc session */ +#define USB_ENC_TYPE_RSA_1 3 /* rsa3072/sha1 auth */ + __u8 bEncryptionValue; /* use in SET_ENCRYPTION */ + __u8 bAuthKeyIndex; +} __attribute__((packed)); + + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_BOS: group of wireless capabilities */ +struct usb_bos_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 wTotalLength; + __u8 bNumDeviceCaps; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_DEVICE_CAPABILITY: grouped with BOS */ +struct usb_dev_cap_header { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; +} __attribute__((packed)); + +#define USB_CAP_TYPE_WIRELESS_USB 1 + +struct usb_wireless_cap_descriptor { /* Ultra Wide Band */ + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; + + __u8 bmAttributes; +#define USB_WIRELESS_P2P_DRD (1 << 1) +#define USB_WIRELESS_BEACON_MASK (3 << 2) +#define USB_WIRELESS_BEACON_SELF (1 << 2) +#define USB_WIRELESS_BEACON_DIRECTED (2 << 2) +#define USB_WIRELESS_BEACON_NONE (3 << 2) + __le16 wPHYRates; /* bit rates, Mbps */ +#define USB_WIRELESS_PHY_53 (1 << 0) /* always set */ +#define USB_WIRELESS_PHY_80 (1 << 1) +#define USB_WIRELESS_PHY_107 (1 << 2) /* always set */ +#define USB_WIRELESS_PHY_160 (1 << 3) +#define USB_WIRELESS_PHY_200 (1 << 4) /* always set */ +#define USB_WIRELESS_PHY_320 (1 << 5) +#define USB_WIRELESS_PHY_400 (1 << 6) +#define USB_WIRELESS_PHY_480 (1 << 7) + __u8 bmTFITXPowerInfo; /* TFI power levels */ + __u8 bmFFITXPowerInfo; /* FFI power levels */ + __le16 bmBandGroup; + __u8 bReserved; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with + * each endpoint descriptor for a wireless device + */ +struct usb_wireless_ep_comp_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bMaxBurst; + __u8 bMaxSequence; + __le16 wMaxStreamDelay; + __le16 wOverTheAirPacketSize; + __u8 bOverTheAirInterval; + __u8 bmCompAttributes; +#define USB_ENDPOINT_SWITCH_MASK 0x03 /* in bmCompAttributes */ +#define USB_ENDPOINT_SWITCH_NO 0 +#define USB_ENDPOINT_SWITCH_SWITCH 1 +#define USB_ENDPOINT_SWITCH_SCALE 2 +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_REQ_SET_HANDSHAKE is a four-way handshake used between a wireless + * host and a device for connection set up, mutual authentication, and + * exchanging short lived session keys. The handshake depends on a CC. + */ +struct usb_handshake { + __u8 bMessageNumber; + __u8 bStatus; + __u8 tTKID[3]; + __u8 bReserved; + __u8 CDID[16]; + __u8 nonce[16]; + __u8 MIC[8]; +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB_REQ_SET_CONNECTION modifies or revokes a connection context (CC). + * A CC may also be set up using non-wireless secure channels (including + * wired USB!), and some devices may support CCs with multiple hosts. + */ +struct usb_connection_context { + __u8 CHID[16]; /* persistent host id */ + __u8 CDID[16]; /* device id (unique w/in host context) */ + __u8 CK[16]; /* connection key */ +} __attribute__((packed)); + +/*-------------------------------------------------------------------------*/ + +/* USB 2.0 defines three speeds, here's how Linux identifies them */ + +enum usb_device_speed { + USB_SPEED_UNKNOWN = 0, /* enumerating */ + USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ + USB_SPEED_HIGH, /* usb 2.0 */ + USB_SPEED_VARIABLE, /* wireless (usb 2.5) */ +}; + +enum usb_device_state { + /* NOTATTACHED isn't in the USB spec, and this state acts + * the same as ATTACHED ... but it's clearer this way. + */ + USB_STATE_NOTATTACHED = 0, + + /* chapter 9 and authentication (wireless) device states */ + USB_STATE_ATTACHED, + USB_STATE_POWERED, /* wired */ + USB_STATE_UNAUTHENTICATED, /* auth */ + USB_STATE_RECONNECTING, /* auth */ + USB_STATE_DEFAULT, /* limited function */ + USB_STATE_ADDRESS, + USB_STATE_CONFIGURED, /* most functions */ + + USB_STATE_SUSPENDED + + /* NOTE: there are actually four different SUSPENDED + * states, returning to POWERED, DEFAULT, ADDRESS, or + * CONFIGURED respectively when SOF tokens flow again. + */ +}; + +#endif /* __LINUX_USB_CH9_H */ Index: firmware/export/config-e200.h =================================================================== --- firmware/export/config-e200.h (revision 13869) +++ firmware/export/config-e200.h (working copy) @@ -152,6 +152,7 @@ /* USB On-the-go */ #define CONFIG_USBOTG USBOTG_ARC +#define USE_USBSTACK /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER Index: firmware/export/usb_cdc.h =================================================================== --- firmware/export/usb_cdc.h (revision 0) +++ firmware/export/usb_cdc.h (revision 0) @@ -0,0 +1,212 @@ +/* + * USB Communications Device Class (CDC) definitions + * + * CDC says how to talk to lots of different types of network adapters, + * notably ethernet adapters and various modems. It's used mostly with + * firmware based USB peripherals. + */ + +#define USB_CDC_SUBCLASS_ACM 0x02 +#define USB_CDC_SUBCLASS_ETHERNET 0x06 +#define USB_CDC_SUBCLASS_WHCM 0x08 +#define USB_CDC_SUBCLASS_DMM 0x09 +#define USB_CDC_SUBCLASS_MDLM 0x0a +#define USB_CDC_SUBCLASS_OBEX 0x0b + +#define USB_CDC_PROTO_NONE 0 + +#define USB_CDC_ACM_PROTO_AT_V25TER 1 +#define USB_CDC_ACM_PROTO_AT_PCCA101 2 +#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3 +#define USB_CDC_ACM_PROTO_AT_GSM 4 +#define USB_CDC_ACM_PROTO_AT_3G 5 +#define USB_CDC_ACM_PROTO_AT_CDMA 6 +#define USB_CDC_ACM_PROTO_VENDOR 0xff + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific descriptors ... there are a couple dozen of them + */ + +#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ +#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ +#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ +#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ +#define USB_CDC_COUNTRY_TYPE 0x07 +#define USB_CDC_NETWORK_TERMINAL_TYPE 0x0a /* network_terminal_desc */ +#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ +#define USB_CDC_WHCM_TYPE 0x11 +#define USB_CDC_MDLM_TYPE 0x12 /* mdlm_desc */ +#define USB_CDC_MDLM_DETAIL_TYPE 0x13 /* mdlm_detail_desc */ +#define USB_CDC_DMM_TYPE 0x14 +#define USB_CDC_OBEX_TYPE 0x15 + +/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ +struct usb_cdc_header_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdCDC; +} __attribute__ ((packed)); + +/* "Call Management Descriptor" from CDC spec 5.2.3.2 */ +struct usb_cdc_call_mgmt_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 +#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 + + __u8 bDataInterface; +} __attribute__ ((packed)); + +/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */ +struct usb_cdc_acm_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +} __attribute__ ((packed)); + +/* capabilities from 5.2.3.3 */ + +#define USB_CDC_COMM_FEATURE 0x01 +#define USB_CDC_CAP_LINE 0x02 +#define USB_CDC_CAP_BRK 0x04 +#define USB_CDC_CAP_NOTIFY 0x08 + +/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ +struct usb_cdc_union_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bMasterInterface0; + __u8 bSlaveInterface0; + /* ... and there could be other slave interfaces */ +} __attribute__ ((packed)); + +/* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ +struct usb_cdc_network_terminal_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bEntityId; + __u8 iName; + __u8 bChannelIndex; + __u8 bPhysicalInterface; +} __attribute__ ((packed)); + +/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ +struct usb_cdc_ether_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 iMACAddress; + __le32 bmEthernetStatistics; + __le16 wMaxSegmentSize; + __le16 wNumberMCFilters; + __u8 bNumberPowerFilters; +} __attribute__ ((packed)); + +/* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */ +struct usb_cdc_mdlm_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdVersion; + __u8 bGUID[16]; +} __attribute__ ((packed)); + +/* "MDLM Detail Functional Descriptor" from CDC WMC spec 6.7.2.4 */ +struct usb_cdc_mdlm_detail_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + /* type is associated with mdlm_desc.bGUID */ + __u8 bGuidDescriptorType; + __u8 bDetailData[0]; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Control Requests (6.2) + * + * section 3.6.2.1 table 4 has the ACM profile, for modems. + * section 3.8.2 table 10 has the ethernet profile. + * + * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant, + * heavily dependent on the encapsulated (proprietary) command mechanism. + */ + +#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 +#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 + +/* Line Coding Structure from CDC spec 6.2.13 */ +struct usb_cdc_line_coding { + __le32 dwDTERate; + __u8 bCharFormat; +#define USB_CDC_1_STOP_BITS 0 +#define USB_CDC_1_5_STOP_BITS 1 +#define USB_CDC_2_STOP_BITS 2 + + __u8 bParityType; +#define USB_CDC_NO_PARITY 0 +#define USB_CDC_ODD_PARITY 1 +#define USB_CDC_EVEN_PARITY 2 +#define USB_CDC_MARK_PARITY 3 +#define USB_CDC_SPACE_PARITY 4 + + __u8 bDataBits; +} __attribute__ ((packed)); + +/* table 62; bits in multicast filter */ +#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) +#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ +#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2) +#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3) +#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ + + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Notifications (6.3) sent by interrupt transfers + * + * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications + * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS + * RNDIS also defines its own bit-incompatible notifications + */ + +#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00 +#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 +#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 +#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a + +struct usb_cdc_notification { + __u8 bmRequestType; + __u8 bNotificationType; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +} __attribute__ ((packed)); + Index: firmware/usbstack/usb-ser.c =================================================================== --- firmware/usbstack/usb-ser.c (revision 0) +++ firmware/usbstack/usb-ser.c (revision 0) @@ -0,0 +1,371 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2007 by Christian Gmeiner + * + * Based on: + * g_serial.c -- USB gadget serial driver + * + * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com) + * + * This code is based in part on the Gadget Zero driver, which + * is Copyright (C) 2003 by David Brownell, all rights reserved. + * + * This code also borrows from usbserial.c, which is + * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2000 Peter Berger (pberger@brimson.com) + * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * either version 2 of that License or (at your option) any later version. + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include "logf.h" +#include "usb_ser.h" +#include "usb_cdc.h" + +//static struct gs_dev gs_device; +static const char *EP_IN_NAME; +static const char *EP_OUT_NAME; +static const char *EP_NOTIFY_NAME; + +static unsigned int use_acm = 1; + +#define GS_VERSION_STR "v2.2" +#define GS_VERSION_NUM 0x0202 +#define GS_LONG_NAME "Gadget Serial" +#define GS_NUM_CONFIGS 1 + +/* Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ +#define GS_VENDOR_ID 0x0525 /* NetChip */ +#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ +#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ + +#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ +#define GS_NOTIFY_MAXPACKET 8 + + +/* the device structure holds info for the USB device */ +struct gs_dev { + struct usb_request *dev_ctrl_req; /* control request */ +}; + + +static struct gs_dev device; + +/*-----------------*/ +/* usb descriptors */ +/*-----------------*/ + +#define GS_MANUFACTURER_STR_ID 1 +#define GS_PRODUCT_STR_ID 2 +#define GS_SERIAL_STR_ID 3 +#define GS_BULK_CONFIG_STR_ID 4 +#define GS_ACM_CONFIG_STR_ID 5 +#define GS_CONTROL_STR_ID 6 +#define GS_DATA_STR_ID 7 + +/* static strings, in UTF-8 */ +static char manufacturer[50]; +static struct usb_string gs_strings[] = { + { GS_MANUFACTURER_STR_ID, manufacturer }, + { GS_PRODUCT_STR_ID, GS_LONG_NAME }, + { GS_SERIAL_STR_ID, "0" }, + { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, + { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, + { GS_CONTROL_STR_ID, "Gadget Serial Control" }, + { GS_DATA_STR_ID, "Gadget Serial Data" }, +}; + +static struct usb_gadget_strings gs_string_table = { + .language = 0x0409, /* en-us */ + .strings = gs_strings, +}; + +static struct usb_device_descriptor gs_device_desc = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .idVendor = GS_VENDOR_ID, + .idProduct = GS_PRODUCT_ID, + .iManufacturer = GS_MANUFACTURER_STR_ID, + .iProduct = GS_PRODUCT_STR_ID, + .iSerialNumber = GS_SERIAL_STR_ID, + .bNumConfigurations = GS_NUM_CONFIGS, +}; + +static struct usb_qualifier_descriptor gs_qualifier_desc = { + .bLength = sizeof(struct usb_qualifier_descriptor), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = 0x0200, + /* assumes ep0 uses the same value for both speeds ... */ + .bNumConfigurations = GS_NUM_CONFIGS, +}; + +static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = GS_NOTIFY_MAXPACKET, + .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* gadget driver */ +static int gs_bind(struct usb_gadget *gadget); +static void gs_unbind(struct usb_gadget *gadget); +static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl); +static void gs_disconnect(struct usb_gadget *gadget); + + +/* gadget driver struct */ +const struct usb_gadget_driver gs_gadget_driver = { + .speed = USB_SPEED_HIGH, + .function = GS_LONG_NAME, + .bind = gs_bind, + .unbind = gs_unbind, + .setup = gs_setup, + .disconnect = gs_disconnect, +}; + +static int gs_bind(struct usb_gadget *gadget) { + + struct usb_ep *ep = NULL; + + gs_device_desc.bcdDevice = GS_VERSION_NUM|0x0099; + usb_ep_autoconfig_reset(gadget); + + ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); + if (!ep) { + goto autoconf_fail; + } + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + + + ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); + if (!ep) { + goto autoconf_fail; + } + EP_OUT_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + + + if (use_acm) { + ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); + if (!ep) { + logf("gs_bind: cannot run ACM on %s", gadget->name); + goto autoconf_fail; + } + gs_device_desc.idProduct = GS_CDC_PRODUCT_ID, EP_NOTIFY_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + } + + gs_device_desc.bDeviceClass = use_acm ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; + gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + +#if 0 +#ifdef CONFIG_USB_GADGET_DUALSPEED + gs_qualifier_desc.bDeviceClass = use_acm ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; + /* assume ep0 uses the same packet size for both speeds */ + gs_qualifier_desc.bMaxPacketSize0 = gs_device_desc.bMaxPacketSize0; + /* assume endpoints are dual-speed */ + gs_highspeed_notify_desc.bEndpointAddress = gs_fullspeed_notify_desc.bEndpointAddress; + gs_highspeed_in_desc.bEndpointAddress = gs_fullspeed_in_desc.bEndpointAddress; + gs_highspeed_out_desc.bEndpointAddress = gs_fullspeed_out_desc.bEndpointAddress; +#endif /* CONFIG_USB_GADGET_DUALSPEED */ + + + usb_gadget_set_selfpowered(gadget); + + if (gadget->is_otg) { + gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, + gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } +#endif + + /* preallocate control response */ + device.dev_ctrl_req = usb_ep_alloc_request(ep); + device.dev_ctrl_req->buf = usb_ep_alloc_buffer(ep, GS_NOTIFY_MAXPACKET); + if (device.dev_ctrl_req == NULL) { + gs_unbind(gadget); + return -ENOMEM; + } + + logf("gs_bind: %s %s bound", GS_LONG_NAME, GS_VERSION_STR); + + return 0; + +autoconf_fail: + logf("gs_bind: cannot autoconfigure on %s", gadget->name); + return -ENODEV; +} + +/*----------------------------------*/ +/* Setup packet handling */ +/*----------------------------------*/ + +static int gs_setup_standard(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl); + +/** + * Implements all the control endpoint functionality that's not + * handled in hardware or the hardware driver. + * + * Returns the size of the data sent to the host, or a negative + * error number. + */ +static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { + + int ret = -EOPNOTSUPP; + struct usb_request *req = device.dev_ctrl_req; + + logf("gs_setup"); + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + ret = gs_setup_standard(gadget, ctrl); + break; + + case USB_TYPE_CLASS: + //ret = gs_setup_class(gadget, ctrl); + break; + + default: + logf("gs_setup: unknown request"); + logf("type=%02x", ctrl->bRequestType); + logf("request=%02x", ctrl->bRequest); + logf("value=%04x", ctrl->wValue); + logf("index=%04x", ctrl->wIndex); + logf("length=%d", ctrl->wLength); + } + + /* respond with data transfer before status phase */ + + logf("trying to send"); + if (ret >= 0) { + req->length = ret; + req->zero = ret < ctrl->wLength && (ret % gadget->ep0->maxpacket) == 0; + ret = usb_ep_queue(gadget->ep0, req); + if (ret < 0) { + logf("gs_setup: cannot queue response, ret=%d", ret); + req->status = 0; + //gs_setup_complete(gadget->ep0, req); + } + } + + /* device either stalls (ret < 0) or reports success */ + return ret; +} + +static int gs_setup_standard(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { + + int ret = -EOPNOTSUPP; + struct usb_request *req = device.dev_ctrl_req; + + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) { + break; + } + + switch (ctrl->wValue >> 8) { + case USB_DT_DEVICE: + ret = min(ctrl->wLength, (u16)sizeof(struct usb_device_descriptor)); + memcpy(req->buf, &gs_device_desc, ret); + logf("USB_DT_DEVICE"); + logf("ret %d", ret); + break; + + case USB_DT_DEVICE_QUALIFIER: + if (!gadget->is_dualspeed) { + break; + } + ret = min(ctrl->wLength,(u16)sizeof(struct usb_qualifier_descriptor)); + memcpy(req->buf, &gs_qualifier_desc, ret); + logf("USB_DT_DEVICE_QUALIFIER"); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + logf("USB_DT_OTHER_SPEED_CONFIG"); + break; + + + case USB_DT_STRING: + logf("USB_DT_STRING"); + break; +#if 0 + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget->is_dualspeed) { + break; + } + /* fall through */ + case USB_DT_CONFIG: + ret = gs_build_config_buf(req->buf, gadget->speed, ctrl->wValue >> 8, ctrl->wValue & 0xff, gadget->is_otg); + if (ret >= 0) { + ret = min(ctrl->wLength, (u16)ret); + } + break; + + case USB_DT_STRING: + /* wIndex == language code. */ + ret = usb_gadget_get_string(&gs_string_table,ctrl->wValue & 0xff, req->buf); + if (ret >= 0) + ret = min(ctrl->wLength, (u16)ret); + break; + } + break; +#endif + } + } + + return ret; +} + +static void gs_unbind(struct usb_gadget *gadget) { + + if (device.dev_ctrl_req != NULL) { + usb_ep_free_request(gadget->ep0, device.dev_ctrl_req); + device.dev_ctrl_req = NULL; + } + + logf("gs_unbind: %s %s unbound\n", GS_LONG_NAME, GS_VERSION_STR); +} + +static void gs_disconnect(struct usb_gadget *gadget) { +} Index: firmware/usbstack/usbstring.c =================================================================== --- firmware/usbstack/usbstring.c (revision 0) +++ firmware/usbstack/usbstring.c (revision 0) @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2003 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include "usb_gadget.h" + +static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) +{ + int count = 0; + u8 c; + u16 uchar; + + /* this insists on correct encodings, though not minimal ones. + * BUT it currently rejects legit 4-byte UTF-8 code points, + * which need surrogate pairs. (Unicode 3.1 can use them.) + */ + while (len != 0 && (c = (u8) *s++) != 0) { + if (unlikely(c & 0x80)) { + // 2-byte sequence: + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + if ((c & 0xe0) == 0xc0) { + uchar = (c & 0x1f) << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c; + + // 3-byte sequence (most CJKV characters): + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + } else if ((c & 0xf0) == 0xe0) { + uchar = (c & 0x0f) << 12; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c; + + /* no bogus surrogates */ + if (0xd800 <= uchar && uchar <= 0xdfff) + goto fail; + + // 4-byte sequence (surrogate pairs, currently rare): + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + // (uuuuu = wwww + 1) + // FIXME accept the surrogate code points (only) + + } else + goto fail; + } else + uchar = c; + put_unaligned (cpu_to_le16 (uchar), cp++); + count++; + len--; + } + return count; +fail: + return -1; +} + + +/** + * usb_gadget_get_string - fill out a string descriptor + * @table: of c strings encoded using UTF-8 + * @id: string id, from low byte of wValue in get string descriptor + * @buf: at least 256 bytes + * + * Finds the UTF-8 string matching the ID, and converts it into a + * string descriptor in utf16-le. + * Returns length of descriptor (always even) or negative errno + * + * If your driver needs stings in multiple languages, you'll probably + * "switch (wIndex) { ... }" in your ep0 string descriptor logic, + * using this routine after choosing which set of UTF-8 strings to use. + * Note that US-ASCII is a strict subset of UTF-8; any string bytes with + * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 + * characters (which are also widely used in C strings). + */ +int +usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) +{ + struct usb_string *s; + int len; + + /* descriptor 0 has the language id */ + if (id == 0) { + buf [0] = 4; + buf [1] = USB_DT_STRING; + buf [2] = (u8) table->language; + buf [3] = (u8) (table->language >> 8); + return 4; + } + for (s = table->strings; s && s->s; s++) + if (s->id == id) + break; + + /* unrecognized: stall. */ + if (!s || !s->s) + return -EINVAL; + + /* string descriptors have length, tag, then UTF16-LE text */ + len = min ((size_t) 126, strlen (s->s)); + memset (buf + 2, 0, 2 * len); /* zero all the bytes */ + len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); + if (len < 0) + return -EINVAL; + buf [0] = (len + 1) * 2; + buf [1] = USB_DT_STRING; + return buf [0]; +} + Index: firmware/usbstack/epautoconf.c =================================================================== --- firmware/usbstack/epautoconf.c (revision 0) +++ firmware/usbstack/epautoconf.c (revision 0) @@ -0,0 +1,250 @@ +/* + * 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 +#include +#include +#include "usb_gadget.h" + +/* we must assign addresses for configurable endpoints */ +static unsigned epnum; + +/* + * This should work with endpoints from controller drivers sharing the + * same endpoint naming convention. By example: + * + * - 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 + * - ep-* ... no functionality restrictions + * + * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal. + * Less common restrictions are implied by gadget_is_*(). + * + * NOTE: each endpoint is unidirectional, as specified by its USB + * descriptor; and isn't specific to a configuration or altsetting. + */ +static int ep_matches ( + struct usb_gadget *gadget, + struct usb_ep *ep, + struct usb_endpoint_descriptor *desc) { + + u8 type; + const char *tmp; + u16 max; + + /* 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 (USB_ENDPOINT_XFER_CONTROL == type) { + return 0; + } + + /* some other naming convention */ + if (ep->name[0] != 'e') { + 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-quirky iso-synch kind + */ + if (tmp[2] == 's') { // == "-iso" + return 0; + } + break; + case USB_ENDPOINT_XFER_BULK: + if (tmp[1] != 'b') { // != "-bulk" + return 0; + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (tmp[2] != 's') {// != "-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 ('n' != *tmp) { + return 0; + } + } else { + if ('t' != *tmp) { + return 0; + } + } + } + } + + /* endpoint maxpacket size is an input parameter, except for bulk + * where it's an output parameter representing the full speed limit. + * the usb spec fixes high speed bulk maxpacket at 512 bytes. + */ + max = desc->wMaxPacketSize & 0x7ff; + switch (type) { + case USB_ENDPOINT_XFER_INT: + /* INT: limit 64 bytes full speed, 1024 high speed */ + if (!gadget->is_dualspeed && max > 64) { + return 0; + } + /* FALLTHROUGH */ + + case USB_ENDPOINT_XFER_ISOC: + /* ISO: limit 1023 bytes full speed, 1024 high speed */ + if (ep->maxpacket < max) { + return 0; + } + if (!gadget->is_dualspeed && max > 1023) { + return 0; + } + + /* BOTH: "high bandwidth" works only at high speed */ + if ((desc->wMaxPacketSize & (3<<11))) { + if (!gadget->is_dualspeed) { + return 0; + /* configure your hardware with enough buffering!! */ + } + } + break; + } + + /* MATCH!! */ + + /* report address */ + if (isdigit (ep->name[2])) { + u8 num = atoi(&ep->name[2]); // was simple_strtol + desc->bEndpointAddress |= num; + } else { + if (++epnum > 15) { + return 0; + } + desc->bEndpointAddress |= epnum; + } + + /* report (variable) full speed bulk maxpacket */ + if (type == USB_ENDPOINT_XFER_BULK) { + int size = ep->maxpacket; + + /* min() doesn't work on bitfields with gcc-3.5 */ + if (size > 64) { + size = 64; + } + desc->wMaxPacketSize = size; + } + return 1; +} + +static struct usb_ep *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 NULL; +} + +/** + * usb_ep_autoconfig - choose an endpoint matching the descriptor + * @gadget: The device to which the endpoint must belong. + * @desc: Endpoint descriptor, with endpoint direction and transfer mode + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on success. + * + * By choosing an endpoint to use with the specified descriptor, this + * routine simplifies writing gadget drivers that work with multiple + * USB device controllers. The endpoint would be passed later to + * usb_ep_enable(), along with some descriptor. + * + * That second descriptor won't always be the same as the first one. + * For example, isochronous endpoints can be autoconfigured for high + * bandwidth, and then used in several lower bandwidth altsettings. + * Also, high and full speed descriptors will be different. + * + * Be sure to examine and test the results of autoconfiguration on your + * hardware. This code may not make the best choices about how to use the + * USB controller, and it can't know all the restrictions that may apply. + * Some combinations of driver and hardware won't be able to autoconfigure. + * + * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value + * is 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. + */ +struct usb_ep *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; + + /* 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 NULL; +} + +/** + * usb_ep_autoconfig_reset - reset endpoint autoconfig state + * @gadget: device for which autoconfig state will be reset + * + * 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 + * used by usb_ep_autoconfig(). + */ +void 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 = NULL; + } + epnum = 0; +} + Index: firmware/SOURCES =================================================================== --- firmware/SOURCES (revision 13869) +++ firmware/SOURCES (working copy) @@ -223,6 +223,10 @@ /* USBOTG */ #if !defined(SIMULATOR) +#if defined(USE_USBSTACK) +usbstack/epautoconf.c +usbstack/usb-ser.c +#endif #if CONFIG_USBOTG == USBOTG_ISP1362 drivers/isp1362.c #endif Index: firmware/target/arm/usb-pp.c =================================================================== --- firmware/target/arm/usb-pp.c (revision 13869) +++ firmware/target/arm/usb-pp.c (working copy) @@ -44,7 +44,10 @@ void usb_init_device(void) { - int r0; +#ifdef SANSA_E200 +// GPIOB_INT_EN &= ~(1 << 4); +#endif + outl(inl(0x70000084) | 0x200, 0x70000084); outl(inl(0x7000002C) | 0x3000000, 0x7000002C); @@ -56,38 +59,26 @@ DEV_INIT |= INIT_USB; while ((inl(0x70000028) & 0x80) == 0); - UDC_PORTSC1 |= PORTSCX_PORT_RESET; - while ((UDC_PORTSC1 & PORTSCX_PORT_RESET) != 0); + /* enable interrupts */ +#if defined(SANSA_E200) +#if 0 + GPIOB_OUTPUT_EN &= ~(1 << 4); + GPIOB_ENABLE |= (1 << 4); + GPIOB_INT_LEV = (GPIOB_INT_LEV & ~(1 << 4)) | + (~GPIOB_INPUT_VAL & (1 << 4)); - UDC_OTGSC |= 0x5F000000; - if( (UDC_OTGSC & 0x100) == 0) { - UDC_USBMODE &=~ USB_MODE_CTRL_MODE_HOST; - UDC_USBMODE |= USB_MODE_CTRL_MODE_DEVICE; - outl(inl(0x70000028) | 0x4000, 0x70000028); - outl(inl(0x70000028) | 0x2, 0x70000028); - } else { - UDC_USBMODE |= USB_MODE_CTRL_MODE_DEVICE; - outl(inl(0x70000028) &~0x4000, 0x70000028); - outl(inl(0x70000028) | 0x2, 0x70000028); - } - - - UDC_USBCMD |= USB_CMD_CTRL_RESET; - while((UDC_USBCMD & USB_CMD_CTRL_RESET) != 0); - - r0 = UDC_PORTSC1; + /* enable gpio interrupt in cpu */ + CPU_INT_EN = HI_MASK; + CPU_HI_INT_EN = GPIO0_MASK; - /* Note from IPL source (referring to next 5 lines of code: - THIS NEEDS TO BE CHANGED ONCE THERE IS KERNEL USB */ - DEV_INIT |= INIT_USB; - DEV_EN |= DEV_USB; - while ((inl(0x70000028) & 0x80) == 0); - outl(inl(0x70000028) | 0x2, 0x70000028); + /* ack interrupt */ + GPIOB_INT_CLR = (1 << 4); + /* enable gpiob interupt */ + GPIOB_INT_EN |= (1 << 4); +#endif +#endif - udelay(0x186A0); - - - dr_controller_setup(); + arcotg_init(); } void usb_enable(bool on) Index: firmware/target/arm/system-pp502x.c =================================================================== --- firmware/target/arm/system-pp502x.c (revision 13869) +++ firmware/target/arm/system-pp502x.c (working copy) @@ -60,6 +60,7 @@ extern void button_int(void); extern void clickwheel_int(void); extern void microsd_int(void); +extern void usb_int(void); #endif void irq(void) @@ -67,6 +68,7 @@ if(CURRENT_CORE == CPU) { if (CPU_INT_STAT & TIMER1_MASK) { TIMER1(); + usb_int(); } else if (CPU_INT_STAT & TIMER2_MASK) { TIMER2(); Index: firmware/drivers/arcotg_udc.c =================================================================== --- firmware/drivers/arcotg_udc.c (revision 13869) +++ firmware/drivers/arcotg_udc.c (working copy) @@ -31,105 +31,1132 @@ * KIND, either express or implied. * ****************************************************************************/ +#include #include "arcotg_udc.h" #include "logf.h" +/* NOTE: only for testing */ +#include "usb_ser.h" + static int timeout; +static struct arcotg_udc udc_controller; -/* @qh_addr is the aligned virt addr of ep QH addr - * it is used to set endpointlistaddr Reg */ -/* was static int dr_controller_setup(void *qh_addr) */ -int dr_controller_setup(void) -{ +/* ep0's endpoint descriptor */ +static struct usb_endpoint_descriptor arcotg_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, +}; + +/* NOTE: qh _MUST_ be in IRAM, else the controller + * is not able to modify data in it. + * TODO: is an ugly hack at the moment. + */ +static struct ep_queue_head qh[USB_MAX_PIPES] __attribute((aligned (1 << 11))) IBSS_ATTR; +static struct ep_dtd dtd_pool[DTD_PRO_REQUERS] __attribute((aligned (32))) IBSS_ATTR; +static bool dtd_used[DTD_PRO_REQUERS]; +static unsigned char buffer[256] __attribute((aligned (256))) IBSS_ATTR; + +/*----------------------------------*/ +/* BLA */ +/*----------------------------------*/ + +static void dump_ep_queue(struct arcotg_ep *ep) { + + int ep_index = ep_index(ep) * 2 + ep_is_in(ep); + struct arcotg_req *req; + struct ep_dtd *dtd; + + logf("DUMP"); + logf("ep=0x%x", ep); + logf("index=%d", ep_index); + + if (list_empty(&ep->queue)) { + logf("empty"); + return; + } + + list_for_each_entry(req, &ep->queue, queue) { + logf(" req=0x%x", req); + logf(" dTD count=%d", req->dtd_count); + logf(" dTD head=0x%x", req->head); + logf(" tail=0x%x", req->tail); + + dtd = req->head; + while (dtd) { + logf(" dTD 0x%x:", dtd); + logf(" active=%d ", (dtd->caps & DTD_STATUS_ACTIVE) == DTD_STATUS_ACTIVE); + + if (dtd->next_td_ptr & DTD_NEXT_TERMINATE) { + break; /* end of dTD list */ + } + + dtd = (struct ep_td_struct *) (dtd->next_td_ptr & DTD_ADDR_MASK); + } + } +} + +static void done(struct arcotg_ep *ep, struct arcotg_req *req, int status) { + + list_del_init(&req->queue); + + /* todo */ +} + +static void nuke(struct arcotg_ep *ep, int status) { + + struct arcotg_req *req = NULL; + ep->stopped = 1; + + /* Whether this eq has request linked */ + while (!list_empty(&ep->queue)) { + struct arcotg_req *req = NULL; + + req = list_entry(ep->queue.next, struct arcotg_req, queue); + done(ep, req, status); + } +} + +static int init_dtd(struct arcotg_req *req, unsigned max, struct ep_dtd **address) { + + int i; + int index = -1; + int length; + u32 tmp; + struct ep_dtd *dtd = NULL; + + for (i = 0; i < DTD_PRO_REQUERS; i++) { + if (dtd_used[i] == false) { + index = i; + dtd_used[i] = true; + i = DTD_PRO_REQUERS; + } + } + + if (index == -1) { + logf("no free dtd"); + return 0; + } + + logf("using dtd %d", index); + dtd = &dtd_pool[index]; + + /* see 32.14.5.2 - Building a Transfer Descriptor */ + + /* set all to 0 */ + memset(dtd, 0, sizeof(struct ep_dtd)); + + /* set terminate bit to 1 */ + dtd->next_td_ptr |= DTD_NEXT_TERMINATE; + + /* fill in bytes with total transfer size */ + length = min(req->req.length - req->req.actual, max); + dtd->caps |= (length << DTD_LENGTH_BIT_POS); + + /* set interupt on complete */ + dtd->caps |= DTD_IOC; + + /* init the status field with active bit set to 1 and all + * remaining bits to 0 */ + dtd->caps |= DTD_STATUS_ACTIVE; + dtd->caps &= ~DTD_RESERVED_FIELDS; + + /* fill in buffer pointer page 0 and the current offset to + * point to the start of data buffer */ + dtd->buff_ptr0 = req->req.buf; + + logf("caps: 0x%x", dtd->caps); +/* + 0x0x128080 -> 100101000000010000000 + active + IOC + LEN: 10010 = 18 +*/ + + logf("mem addr 0x%x", req->req.buf); + logf("mem size %d", req->req.length); + logf("calc size %d", length); + logf("td ptr 0x%x", dtd); + + /* init buffer pointer page 1 throught page 4 to be one + * greater than each of the previous buffer pointer */ + dtd->buff_ptr1 = 0; + dtd->buff_ptr2 = 0; + dtd->buff_ptr3 = 0; + dtd->buff_ptr4 = 0; + + *address = dtd; + + return length; +} + +static void arcotg_req_to_dtd(struct arcotg_req *req) { + + unsigned max; + unsigned count; + int is_last; + int is_first = 1; + struct ep_dtd *last_addr = NULL, *addr; + + max = EP_MAX_LENGTH_TRANSFER; + do { + count = init_dtd(req, max, &addr); + + if (is_first) { + is_first = 0; + req->head = (u32)addr; + } else { + if (!last_addr) { + /* FIXME last_addr not set. iso only + * case, which we don't do yet + */ + logf("*** wiping out something at 0!!"); + } + + last_addr->next_td_ptr = addr; + last_addr = addr; + } + + /* last packet is usually short (or a zlp) */ + if (count != max) { + is_last = 1; + } else if (req->req.length != req->req.actual || req->req.zero) { + is_last = 0; + } else { + is_last = 1; + } + + req->dtd_count++; + } while (!is_last); + + addr->next_td_ptr = DTD_NEXT_TERMINATE; + req->tail = addr; +} + +/* + * link req's dTD queue to the end of ep's QH's dTD queue. + */ +static int arcotg_queue_td(struct arcotg_ep *ep, struct arcotg_req *req) { + + int i = ep_index(ep) * 2 + ep_is_in(ep); + u32 temp, bitmask, tmp_stat; + struct ep_queue_head *dQH = &qh[i]; + struct ep_dtd *dtd = req->head; + + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : (1 << (ep_index(ep))); + + /* see 32.14.5.3 - Executing a Transfer Descriptor */ + + /* as we are using at the moment only one dtd, we dont have a full quque */ +#if 0 + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + logf("not empty"); + + /* Add td to the end */ + struct arcotg_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct arcotg_req, queue); + + u32 tmp = (u32)req->head; + //lastreq->tail->next_td_ptr = tmp & DTD_ADDR_MASK; + + /* Read prime bit, if 1 goto done */ + if (UDC_ENDPTPRIME & bitmask) { + logf("ep's already primed"); + goto out; + } + + timeout = 10000000; + do { + /* Set ATDTW bit in USBCMD */ + UDC_USBCMD |= USB_CMD_ATDTW; + + /* Read correct status bit */ + tmp_stat = UDC_ENDPTSTAT & bitmask; + + } while ((!(UDC_USBCMD & USB_CMD_ATDTW)) && --timeout); + + if (timeout == 0) { + logf("TIMEOUT"); + } + + /* Write ATDTW bit to 0 */ + UDC_USBCMD &= ~USB_CMD_ATDTW; + + if (tmp_stat) { + goto out; + } + + /* fall through to Case 1: List is empty */ + } + +#endif + + logf("list empty"); + + /* Write dQH next pointer and terminate bit to 0 as a single DWord operation */ + //dQH->next_dtd_ptr = (((u32)req->head) /*& EP_QUEUE_HEAD_NEXT_POINTER_MASK*/) | ~(1 << 0); + dQH->dtd.next_td_ptr = (((u32)dtd) & EP_QUEUE_HEAD_NEXT_POINTER_MASK) | ~(1 << 0); + + /* Clear active and halt bit in case of previous error */ + dQH->dtd.caps &= ~(EP_QUEUE_HEAD_STATUS_ACTIVE | EP_QUEUE_HEAD_STATUS_HALT); + + /* Prime endpoint by writing 1 to ENDPTPRIME */ + logf(":)"); + logf("setting endpointprime"); + logf("bitmask=0x%x", bitmask); + logf("prime=0x%x", UDC_ENDPTPRIME); + UDC_ENDPTPRIME |= bitmask; +/* logf("bitmask=0x%x", 1 << 15); + UDC_ENDPTPRIME |= (1 << 15); + logf("3 prime=0x%x", UDC_ENDPTPRIME); + logf("bitmask=0x%x", 1 << 16); + UDC_ENDPTPRIME |= (1 << 16); + logf("4 prime=0x%x", UDC_ENDPTPRIME); + logf("bitmask=0x%x", 1 << 17); + UDC_ENDPTPRIME |= (1 << 17); + logf("5 prime=0x%x", UDC_ENDPTPRIME); +*/ +out: + return 0; +} + +/* + * ch9 Set address + */ +static void ch9SetAddress(u16 value) { + + logf("new address=%d", value); + + /* Save the new address to device struct */ + udc_controller.device_address = (u8) value; + + /* Update usb state */ + udc_controller.usb_state = USB_STATE_ADDRESS; + + /* Status phase */ #if 0 - struct arc_usb_config *config; + if (ep0_prime_status(udc, EP_DIR_IN)) + Ep0Stall(udc); +#endif +} - config = udc_controller->config; +/*----------------------------------*/ +/* Endpoint Management */ +/*----------------------------------*/ - /* before here, make sure usb_slave_regs has been initialized */ - if (!qh_addr) + +static int arcotg_ep_disable(struct usb_ep *_ep) { + + struct arcotg_ep *ep = NULL; + + ep = container_of(_ep, struct arcotg_ep, ep); + if (!_ep || !ep->desc) { + logf("%s not enabled", _ep ? ep->ep.name : NULL); return -EINVAL; + } + + /* Nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + + logf("disabled %s OK", _ep->name); + return 0; +} + +static struct usb_request *arcotg_alloc_request(struct usb_ep *_ep) { + + int i; + struct arcotg_req *req = NULL; + struct arcotg_ep *ep = NULL; + + ep = container_of(_ep, struct arcotg_ep, ep); + + /* find a free request in endpoint */ + for (i = 0; i < REQUEST_BUFFER_SIZE; i++) { + if (ep->req[i].used == false) { + ep->req[i].used = true; + req = &ep->req[i]; + /* stop for */ + i = REQUEST_BUFFER_SIZE; + } + } + + if (!req) { + logf("req buffer empty"); + return NULL; + } + + memset(req, 0, sizeof *req); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void arcotg_free_request(struct usb_ep *_ep, struct usb_request *_req) { + + struct arcotg_req *req; + req = container_of(_req, struct arcotg_req, req); + + /* we dont need the _ep pointer */ + (void)_ep; + + /* mark as unused */ + if (req) { + req->used = false; + } +} + +static void *arcotg_alloc_buffer(struct usb_ep *_ep, unsigned bytes) { + void *retval = NULL; + + logf("alloc %d mem", bytes); + + if (!bytes) { + return 0; + } + + retval = &buffer; + return retval; +} + +static int arcotg_ep_queue(struct usb_ep *_ep, struct usb_request *_req) { + + struct arcotg_ep *ep = container_of(_ep, struct arcotg_ep, ep); + struct arcotg_req *req = container_of(_req, struct arcotg_req, req); + struct arcotg_udc *udc; + + int is_iso = 0; + /* catch various bogus parameters */ + if (!_req || /*!req->req.complete ||*/ !req->req.buf || !list_empty(&req->queue)) { + logf("%s, bad params", __FUNCTION__); + return -EINVAL; + } + if (!_ep || (!ep->desc && ep_index(ep))) { + logf("%s, bad ep", __FUNCTION__); + return -EINVAL; + } + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) { + logf("isoc"); + return -EMSGSIZE; + } + is_iso = 1; + } + + udc = ep->udc; +#if 0 + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + logf("hmmm"); + return -ESHUTDOWN; + } #endif + req->ep = ep; - /* Stop and reset the usb controller */ + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + /* push the dtds to device queue */ + arcotg_req_to_dtd(req); + arcotg_queue_td(ep, req); + + + /* put this req at the end of the ep's queue */ + /* irq handler advances the queue */ + if (req != NULL) { + list_add_tail(&req->queue, &ep->queue); + } + + dump_ep_queue(ep); + + return 0; +} + +static int arcotg_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { + + struct arcotg_ep *ep = container_of(_ep, struct arcotg_ep, ep); + struct arcotg_req *req; + + if (!_ep || !_req) { + return -EINVAL; + } + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) { + break; + } + } + + if (&req->req != _req) { + return -EINVAL; + } + //done(ep, req, -ECONNRESET); + + return 0; +} + +static struct usb_ep_ops arcotg_ep_ops = { + //.enable = arcotg_ep_enable, + .disable = arcotg_ep_disable, + + .alloc_request = arcotg_alloc_request, + .free_request = arcotg_free_request, + + .alloc_buffer = arcotg_alloc_buffer, + //.free_buffer = arcotg_free_buffer, + + .queue = arcotg_ep_queue, + .dequeue = arcotg_ep_dequeue, + + //.set_halt = arcotg_ep_set_halt, +}; + +/*----------------------------------*/ +/* Interrupt handling */ +/*----------------------------------*/ + +static void port_change_int(void); +static void reset_int(void); +static void suspend_int(void); +static void resume_int(void); +static void setup_received_int(struct usb_ctrlrequest* request); + +/** + * Our main dispatcher for all interrupts comming + * from usb controller. + */ +void usb_int(void) { + + unsigned int irq_src = 0; + int i; + + spinlock_lock(&udc_controller.lock); + + /* read source of irq */ + irq_src = UDC_USBSTS & UDC_USBINTR; + + /* TODO */ + if (irq_src == 0x0) { + spinlock_unlock(&udc_controller.lock); + return; + } + + /* clear notification bits */ + UDC_USBSTS &= irq_src; + logf("irq_src [0x%08x]", irq_src); + logf("1"); + /* check if we need to wake up from suspend */ + if (!(irq_src & USB_STS_SUSPEND) && udc_controller.resume_state) { + resume_int(); + } + + logf("endptsetupstat=0x%x", UDC_ENDPTSETUPSTAT); + logf("endptcomplete=0x%x", UDC_ENDPTCOMPLETE); + + /* USB Interrupt */ + switch (irq_src) { + case USB_STS_INT: + + /* setup packet, we only support ep0 as control ep */ + if (UDC_ENDPTSETUPSTAT & EP_SETUP_STATUS_EP0) { + /* copy data from queue head to local buffer */ + memcpy(&udc_controller.local_setup_buff, (u8 *) &qh[0].setup_buffer, 8); + setup_received_int(&udc_controller.local_setup_buff); + } + + /* completion of dtd */ + if (UDC_ENDPTCOMPLETE) { + logf("complete dtd"); + //dtd_complete_int(); + } + + break; + + case USB_STS_PORT_CHANGE: + port_change_int(); + break; + + case USB_STS_RESET: + reset_int(); + break; + + case USB_STS_SUSPEND: + suspend_int(); + break; + + case USB_INTR_ERR_INT_EN: + logf("ERRRROOORRR"); + break; + + default: + logf("unkown"); + } + + /* watch qhs for important status bits */ + for (i = 0; i < USB_MAX_PIPES; i++) { + if (qh[i].dtd.caps & DTD_STATUS_DATA_BUFF_ERR) { + logf("!!!buffer error %d", i); + } + + if (qh[i].dtd.caps & DTD_STATUS_TRANSACTION_ERR) { + logf("!!!transaction error %d", i); + } + } + + + spinlock_unlock(&udc_controller.lock); +} + +static void port_change_int(void) { + + u32 speed; + + if (udc_controller.bus_reset) { + udc_controller.bus_reset = false; + } + + /* Bus resetting is finished */ + if (!(UDC_PORTSC1 & PORTSCX_PORT_RESET)) { + /* Get the speed */ + speed = (UDC_PORTSC1 & PORTSCX_PORT_SPEED_MASK); + switch (speed) { + case PORTSCX_PORT_SPEED_HIGH: + udc_controller.gadget.speed = USB_SPEED_HIGH; + break; + case PORTSCX_PORT_SPEED_FULL: + udc_controller.gadget.speed = USB_SPEED_FULL; + break; + case PORTSCX_PORT_SPEED_LOW: + udc_controller.gadget.speed = USB_SPEED_LOW; + break; + default: + udc_controller.gadget.speed = USB_SPEED_UNKNOWN; + break; + } + } + + logf("arcotg: new speed %d", udc_controller.gadget.speed); + + /* Update USB state */ + if (!udc_controller.resume_state) { + udc_controller.usb_state = USB_STATE_DEFAULT; + } +} + +static void reset_int(void) { + + /* Clear usb state */ + udc_controller.usb_state = USB_STATE_DEFAULT; + + /* Clear all the setup token semaphores */ + UDC_ENDPTSETUPSTAT = UDC_ENDPTSETUPSTAT; + + /* Clear all the endpoint complete status bits */ + UDC_ENDPTCOMPLETE = UDC_ENDPTCOMPLETE; + + timeout = 10000000; + /* Wait until all endptprime bits cleared */ + while (UDC_ENDPTPRIME && --timeout) { + continue; + } + if (timeout == 0) { + logf("%s: TIMEOUT\n", __FUNCTION__); + } + + /* Write 1s to the Flush register */ + UDC_ENDPTFLUSH = ~0; + + + if (UDC_PORTSC1 & PORTSCX_PORT_RESET) { + logf("arcotg: Bus RESET"); + /* Bus is reseting */ + udc_controller.bus_reset = true; +// udc_controller.ep0_state = WAIT_FOR_SETUP; + logf("ep0_state now WAIT_FOR_SETUP"); + udc_controller.ep0_dir = 0; + /* Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + //reset_queues(udc); + } else { + logf("arcotg: Controller reset"); + /* initialize usb hw reg except for regs for EP, not + * touch usbintr reg */ + //arcotg_init(); + dr_controller_run(); + udc_controller.usb_state = USB_STATE_ATTACHED; +// udc_controller.ep0_state = WAIT_FOR_SETUP; + logf("ep0_state now WAIT_FOR_SETUP"); + udc_controller.ep0_dir = 0; + } +} + +static void suspend_int(void) { + udc_controller.resume_state = udc_controller.usb_state; + udc_controller.usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver */ + if (udc_controller.driver->suspend) { + udc_controller.driver->suspend(&udc_controller.gadget); + } +} + +static void resume_int(void) { + udc_controller.usb_state = udc_controller.resume_state; + udc_controller.resume_state = 0; + + /* report resume to the driver */ + if (udc_controller.driver->resume) { + udc_controller.driver->resume(&udc_controller.gadget); + } +} + +static void setup_received_int(struct usb_ctrlrequest* request) { + + int handled = 1; /* set to zero if we do not handle the message, */ + /* and should pass it to the gadget driver */ + + logf("setup packet"); + + /* We asume setup only occurs on EP0 */ + if (request->bRequestType & USB_DIR_IN) { + udc_controller.ep0_dir = USB_DIR_IN; + } else { + udc_controller.ep0_dir = USB_DIR_OUT; + } + + /* look what kind of request we get */ + switch (request->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + logf("standard"); + + switch (request->bRequest) { + case USB_REQ_SET_ADDRESS: + if (request->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { + break; + } + ch9SetAddress(request->wValue); + break; + + default: + handled = 0; + break; + } + + switch (request->bRequest) { + case 0x00 : /* get status */ + logf("get status"); + break; + case 0x05 : /* set address */ + logf("set address"); + break; + case 0x06 : /* get descriptor */ + logf("get descriptor"); + break; + case 0x08 : /* get configuration */ + logf("get config"); + break; + case 0x09 : /* set configuration */ + logf("set config"); + break; + default : + logf("unkown"); + } + + break; + + case USB_TYPE_CLASS: + handled = 0; + break; + + case USB_TYPE_VENDOR: + handled = 0; + break; + } + + if (!handled) { + if (udc_controller.driver->setup(&udc_controller.gadget, &udc_controller.local_setup_buff) != 0) { + logf("failed"); + } else { + logf("wow"); + } + } +} + +static void dtd_complete_int(void) { + + u32 bit_pos; + int i, ep_num, direction, bit_mask, status; + struct arcotg_ep *curr_ep; + struct arcotg_req *curr_req, *temp_req; + + /* Clear the bits in the register */ + bit_pos = UDC_ENDPTCOMPLETE; + UDC_ENDPTCOMPLETE = bit_pos; + + if (!bit_pos) { + return; + } + + for (i = 0; i < USB_MAX_ENDPOINTS * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_mask = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & bit_mask)) { + continue; + } + + curr_ep = get_ep_by_pipe(udc_controller, i); + + /* If the ep is configured */ + if (curr_ep->name == NULL) { + logf("Invalid EP?"); + continue; + } + + /* search all arcotg_reqs of ep */ + list_for_each_entry(curr_req, &curr_ep->queue, queue) { + //status = process_ep_req(udc, i, curr_req); +#if 0 + if (status == REQ_UNCOMPLETE) { + logf("Not all tds are completed in the req"); + break; + } +#endif +#if 0 + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else { +#endif + done(curr_ep, curr_req, status); + + //} + + } + } +} + +/*----------------------------------*/ +/* Lifecyle functions */ +/*----------------------------------*/ + +/** + * Used to initialise queue head for one endpoint. + * @param ep_num + * @param dir + * @param ep_type + * @param max_pkt_len + * @param zlt + * @param mult + */ +static void qh_init(unsigned char ep_num, unsigned char dir, unsigned char ep_type, + unsigned int max_pkt_len, unsigned int zlt, unsigned char mult) { + + struct ep_queue_head *p_QH = &qh[2 * ep_num + dir]; + u32 tmp = 0; + + /* set the Endpoint Capabilites Reg of QH */ + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + /* Interrupt On Setup (IOS). for control ep */ + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + if (zlt) { + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + } +