Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 14368) +++ apps/lang/english.lang (working copy) @@ -11037,3 +11037,73 @@ *: "Only when viewing all types" + + id: LANG_USBSTACK + desc: in settings_menu + user: + + *: "USB Stack" + + + *: "USB Stack" + + + *: "USB Stack" + + + + id: LANG_USBSTACK_MODE + desc: in usbstack settings + user: + + *: "USB Stack Mode" + + + *: "USB Stack Mode" + + + *: "USB Stack Mode" + + + + id: LANG_USBSTACK_AUTOMATIC + desc: in usbstack settings + user: + + *: "Automatic" + + + *: "Automatic" + + + *: "Automatic" + + + + id: LANG_USBSTACK_DEVICE + desc: in usbstack settings + user: + + *: "Device" + + + *: "Device" + + + *: "Device" + + + + id: LANG_USBSTACK_HOST + desc: in usbstack settings + user: + + *: "Host" + + + *: "Host" + + + *: "Host" + + Index: apps/settings.c =================================================================== --- apps/settings.c (revision 14368) +++ apps/settings.c (working copy) @@ -98,6 +98,10 @@ #include "lcd-remote.h" #endif +#ifdef USE_USBSTACK +#include "usbstack.h" +#endif + long lasttime = 0; /** NVRAM stuff, if the target doesnt have NVRAM it is saved in ROCKBOX_DIR /nvram.bin **/ @@ -874,7 +878,15 @@ if (global_settings.colors_file) read_color_theme_file(); #endif - + +#ifdef USE_USBSTACK + usb_controller_select(global_settings.usb_stack_mode); +#if 0 + if (global_settings.usb_device_driver != NULL) { + usb_device_driver_bind(global_settings.usb_device_driver); + } +#endif +#endif } Index: apps/settings.h =================================================================== --- apps/settings.h (revision 14368) +++ apps/settings.h (working copy) @@ -745,6 +745,10 @@ int list_accel_start_delay; /* ms before we start increaseing step size */ int list_accel_wait; /* ms between increases */ #endif +#ifdef USE_USBSTACK + int usb_stack_mode; /* automatic, device or host */ + unsigned char usb_device_driver[100]; /* name of usb device driver to use */ +#endif }; /** global variables **/ Index: apps/menus/settings_menu.c =================================================================== --- apps/menus/settings_menu.c (revision 14368) +++ apps/menus/settings_menu.c (working copy) @@ -442,7 +442,18 @@ /* VOICE MENU */ /***********************************/ +#ifdef USE_USBSTACK /***********************************/ +/* USB STACK MENU */ +MENUITEM_SETTING(usbstack_mode, &global_settings.usb_stack_mode, NULL); + +MAKE_MENU(usbstack_menu, ID2P(LANG_USBSTACK), 0, Icon_file_view_menu, + &usbstack_mode); +/* USB STACK MENU */ +/***********************************/ +#endif + +/***********************************/ /* SETTINGS MENU */ static int language_browse(void) { @@ -458,6 +469,11 @@ &tagcache_menu, #endif &display_menu, &system_menu, - &bookmark_settings_menu, &browse_langs, &voice_settings_menu ); + &bookmark_settings_menu, &browse_langs, &voice_settings_menu + +#ifdef USE_USBSTACK + , &usbstack_menu +#endif + ); /* SETTINGS MENU */ /***********************************/ Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 14368) +++ apps/settings_list.c (working copy) @@ -47,6 +47,9 @@ #include "radio.h" #endif +#ifdef USE_USBSTACK +#include "usbstack.h" +#endif #define NVRAM(bytes) (bytes< ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: adc.h 13174 2007-04-15 23:35:56Z amiconn $ + * + * Copyright (C) by Linux Kernel Developers + * + * 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_ + +#include /* used for offsetof */ + +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? */ +/* 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/config.h =================================================================== --- firmware/export/config.h (revision 14368) +++ firmware/export/config.h (working copy) @@ -334,7 +334,7 @@ #define ICODE_ATTR __attribute__ ((section(".icode"))) #define ICONST_ATTR __attribute__ ((section(".irodata"))) #define IDATA_ATTR __attribute__ ((section(".idata"))) -#define IBSS_ATTR __attribute__ ((section(".ibss"))) +#define IBSS_ATTR #define USE_IRAM #if CONFIG_CPU != SH7034 #define IRAM_STEAL Index: firmware/export/arcotg_udc.h =================================================================== --- firmware/export/arcotg_udc.h (revision 14368) +++ firmware/export/arcotg_udc.h (working copy) @@ -37,8 +37,6 @@ #include "cpu.h" -#define ETIMEDOUT 1 - #define USB_MAX_ENDPOINTS 8 #define USB_MAX_PIPES (USB_MAX_ENDPOINTS*2) #define USB_MAX_CTRL_PAYLOAD 64 @@ -66,6 +64,7 @@ #define UDC_FRINDEX (*(volatile unsigned int *)(USB_BASE+0x14c)) /* Frame Index Register */ #define UDC_DEVICEADDR (*(volatile unsigned int *)(USB_BASE+0x154)) /* Device Address */ #define UDC_ENDPOINTLISTADDR (*(volatile unsigned int *)(USB_BASE+0x158)) /* Endpoint List Address Register */ +#define UDC_ASYNCTTSTS (*(volatile unsigned int *)(USB_BASE+0x15c)) #define UDC_BURSTSIZE (*(volatile unsigned int *)(USB_BASE+0x160)) /* Master Interface Data Burst Size Register */ #define UDC_TXFILLTUNING (*(volatile unsigned int *)(USB_BASE+0x164)) /* Transmit FIFO Tuning Controls Register */ #define UDC_ULPIVIEW (*(volatile unsigned int *)(USB_BASE+0x170)) Index: firmware/export/usbstack.h =================================================================== --- firmware/export/usbstack.h (revision 0) +++ firmware/export/usbstack.h (revision 0) @@ -0,0 +1,171 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * 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 _USBSTACK_H_ +#define _USBSTACK_H_ + +#include +#include "linkedlist.h" +#include "usb_ch9.h" +#include "logf.h" + +/* + * error codes + */ +#define ENOFREESLOT 1 +#define ENOCONTROLLERACTIVE 2 +#define EWRONGCONTROLLERTYPE 3 +#define ENODRIVERFOUND 4 +#define EHWCRITICAL 5 + +/* + * stack routines + */ +void usb_stack_init(void); +void usb_stack_shutdown(void); +void usb_stack_start(void); +void usb_stack_stop(void); +void usb_stack_irq(void); +void usb_stack_work(void); + +/* + * stack datatypes + */ +struct usb_ep { + const char* name; + uint8_t type; + uint32_t size; + struct list_head list; +}; + +struct usb_request { + void *buf; + unsigned length; +}; + + +/* + * usb controller + */ + +enum usb_controller_type { + AUTOMATIC = 0, /* only used for autodetection, do not use in controllers */ + DEVICE, + HOST, +}; + +struct usb_device_driver; +struct usb_host_driver; + +struct usb_controller { + const char* name; + enum usb_controller_type type; + int (*init)(void); + void (*shutdown)(void); + void (*irq)(void); + void (*start)(void); + void (*stop)(void); + void* controller_ops; + struct usb_device_driver* device_driver; + struct usb_host_driver* host_driver; +}; + +int usb_controller_register(struct usb_controller* ctrl); +int usb_controller_unregister(struct usb_controller* ctrl); +void usb_controller_select(int type); + +/* + * usb device driver + */ +struct usb_device_driver { + const char* name; + void (*bind)(void* controller_ops); + void (*unbind)(void); + int (*request)(struct usb_ctrlrequest* req); + void (*suspend)(void); + void (*resume)(void); + void* data; /* used to store controller specific ops struct */ + struct list_head list; +}; + +int usb_device_driver_register(struct usb_device_driver* driver); +int usb_device_driver_unregister(struct usb_device_driver* driver); +int usb_device_driver_bind(const char* name); +void ubs_device_driver_unbind(void); + +/* + * usb host driver + */ +struct usb_host_driver { + void* data; /* used to store controller specific ops struct */ + struct list_head list; +}; + +/* + * dcd - device controller driver + */ +void usb_dcd_init(void); +void usb_dcd_shutdown(void); + +struct usb_dcd_controller_ops { + /* endpoint management */ + int (*enable)(struct usb_ep* ep, const struct usb_endpoint_descriptor *desc); + int (*disable)(struct usb_ep* ep); + int (*set_halt)(struct usb_ep* ep, bool hald); + + /* request management*/ + struct usb_request* (*alloc_request)(struct usb_ep* ep); + void (*free_request)(struct usb_ep* ep, struct usb_request* req); + + /* transmitting */ + int (*queue)(struct usb_ep* ep, struct usb_request* req); + int (*dequeue)(struct usb_ep* ep, struct usb_request* req); +}; + +/* + * hcd - host controller driver + */ +void usb_hcd_init(void); +void usb_hcd_shutdown(void); + +struct usb_hcd_controller_ops { + /* TODO */ +}; + +/* + * usb core + */ + +struct usb_core { + /* we can have maximum two controllers (one device, one host) */ + struct usb_controller* controller[2]; + struct usb_controller* active_controller; + struct usb_host_driver host_drivers; + struct usb_device_driver device_drivers; + enum usb_controller_type mode; + bool running; +}; + +/* use a global variable to make usb_core accessable in other places */ +struct usb_core usbcore; + +/* only used for debug */ +void into_usb_ctrlrequest(struct usb_ctrlrequest* request); + +#endif /*_USBSTACK_H_*/ Index: firmware/export/usb_ch9.h =================================================================== --- firmware/export/usb_ch9.h (revision 0) +++ firmware/export/usb_ch9.h (revision 0) @@ -0,0 +1,196 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2007 by Christian Gmeiner + * + * Based on code from the Linux Kernel + * available at http://www.kernel.org + * Original file: /include/linux/usb/ch9.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 _CH9_H_ +#define _CH9_H_ + +#include + +/* + * 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) + +/** + * 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) + */ +struct usb_ctrlrequest { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__ ((packed)); + +/* + * 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 + +/* + * 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. The convention is + * defined in the USB "Common Class" Spec (3.11). Individual class specs + * are authoritative for their usage, not the "common class" writeup. + */ +#define USB_DT_CS_DEVICE (USB_TYPE_CLASS | USB_DT_DEVICE) +#define USB_DT_CS_CONFIG (USB_TYPE_CLASS | USB_DT_CONFIG) +#define USB_DT_CS_STRING (USB_TYPE_CLASS | USB_DT_STRING) +#define USB_DT_CS_INTERFACE (USB_TYPE_CLASS | USB_DT_INTERFACE) +#define USB_DT_CS_ENDPOINT (USB_TYPE_CLASS | USB_DT_ENDPOINT) + +/* USB_DT_DEVICE: Device descriptor */ +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__ ((packed)); + +#define USB_DT_DEVICE_SIZE 18 + + +/* USB_DT_ENDPOINT: Endpoint descriptor */ +struct usb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + + /* NOTE: these two are _only_ in audio endpoints. */ + /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */ + uint8_t bRefresh; + uint8_t bSynchAddress; +} __attribute__ ((packed)); + +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ + +/* + * Endpoints + */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +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 /*_CH9_H_*/ Index: firmware/export/config-e200.h =================================================================== --- firmware/export/config-e200.h (revision 14368) +++ firmware/export/config-e200.h (working copy) @@ -154,6 +154,7 @@ /* USB On-the-go */ #define CONFIG_USBOTG USBOTG_ARC +#define USE_USBSTACK /* Virtual LED (icon) */ #define CONFIG_LED LED_VIRTUAL Index: firmware/usbstack/core/utils.c =================================================================== --- firmware/usbstack/core/utils.c (revision 0) +++ firmware/usbstack/core/utils.c (revision 0) @@ -0,0 +1,121 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * 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. + * + ****************************************************************************/ + +#include "usbstack.h" + +void into_usb_ctrlrequest(struct usb_ctrlrequest* request) { + + char* type = ""; + char* req = ""; + char* extra = 0; + + logf("-usb request-"); + /* check if packet is okay */ + if (request->bRequestType == 0 && + request->bRequest == 0 && + request->wValue == 0 && + request->wIndex == 0 && + request->wLength == 0) { + logf(" -> INVALID <-"); + return; + } + + switch (request->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + type = "standard"; + + switch (request->bRequest) { + case USB_REQ_GET_STATUS: + req = "get status"; + break; + case USB_REQ_CLEAR_FEATURE: + req = "clear feature"; + break; + case USB_REQ_SET_FEATURE: + req = "set feature"; + break; + case USB_REQ_SET_ADDRESS: + req = "set address"; + break; + case USB_REQ_GET_DESCRIPTOR: + req = "get descriptor"; + + switch (request->wValue >> 8) { + case USB_DT_DEVICE: + extra = "get device descriptor"; + break; + case USB_DT_DEVICE_QUALIFIER: + extra = "get device qualifier"; + break; + case USB_DT_OTHER_SPEED_CONFIG: + extra = "get other-speed config descriptor"; + case USB_DT_CONFIG: + extra = "get configuration descriptor"; + break; + case USB_DT_STRING: + extra = "get string descriptor"; + break; + } + break; + + break; + case USB_REQ_SET_DESCRIPTOR: + req = "set descriptor"; + break; + case USB_REQ_GET_CONFIGURATION: + req = "get configuration"; + break; + case USB_REQ_SET_CONFIGURATION: + req = "set configuration"; + break; + case USB_REQ_GET_INTERFACE: + req = "get interface"; + break; + case USB_REQ_SET_INTERFACE: + req = "set interface"; + break; + case USB_REQ_SYNCH_FRAME: + req = "sync frame"; + break; + default: + req = "unkown"; + break; + } + + break; + case USB_TYPE_CLASS: + type = "class"; + break; + + case USB_TYPE_VENDOR: + type = "vendor"; + break; + } + + logf(" -b 0x%x", request->bRequestType); + logf(" -b 0x%x", request->bRequest); + logf(" -b 0x%x", request->wValue); + logf(" -b 0x%x", request->wIndex); + logf(" -b 0x%x", request->wLength); + logf(" -> t: %s", type); + logf(" -> r: %s", req); + if (extra != 0) { + logf(" -> e: %s", extra); + } +} Index: firmware/usbstack/core/core.c =================================================================== --- firmware/usbstack/core/core.c (revision 0) +++ firmware/usbstack/core/core.c (revision 0) @@ -0,0 +1,343 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * 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. + * + ****************************************************************************/ + +#include +#include +#include "usbstack.h" + +#include "usbstack/drivers/usb_serial.h" + +/** + * Initialize usb stack. + */ +void usb_stack_init(void) { + + logf("usb_stack_init"); + + /* init datastructures */ + usbcore.controller[0] = NULL; + usbcore.controller[1] = NULL; + usbcore.active_controller = NULL; + usbcore.running = false; + + /* init linked lists */ + INIT_LIST_HEAD(&usbcore.device_drivers.list); + INIT_LIST_HEAD(&usbcore.host_drivers.list); + + /* idea: all drivers should be something like rockbox plugins... rockbox usb drivers. + * the app code scans for usb drivers and register them automaticly in the stack. + * We only need one *.c file for a driver and we dont need any *.h files with one + * method in it: usb__driver_init(void); + */ + + /* init drivers */ + usb_serial_driver_init(); +} + +/** + * Shutdown usb stack. + */ +void usb_stack_shutdown(void) { + + /* free current used device driver */ + if (usbcore.active_controller->device_driver != NULL) { + + /* call unbind if supported */ + if (usbcore.active_controller->device_driver->unbind != NULL) { + usbcore.active_controller->device_driver->unbind(); + } + usbcore.active_controller->device_driver = NULL; + } + + /* stop stack */ + usb_stack_stop(); + + /* TODO delete linked lists */ +} + +/** + * Start processing of usb stack. This function init + * active usb controller. + */ +void usb_stack_start(void) { + + /* are we allready running? */ + if (usbcore.running) { + return; + } + + /* forward to controller */ + if (usbcore.active_controller != NULL && usbcore.active_controller->start != NULL) { + usbcore.active_controller->start(); + usbcore.running = true; + } +} + +/** + * Stop processing of usb stack. This function shutsdown + * active usb controller. + */ +void usb_stack_stop(void) { + + /* are we allready stopped? */ + if (usbcore.running == false) { + return; + } + + /* forward to controller */ + if (usbcore.active_controller != NULL && usbcore.active_controller->stop != NULL) { + usbcore.active_controller->stop(); + usbcore.running = false; + } +} + +/** + * Gets called by upper layers to indicate that there is + * an interrupt waiting for the controller. + */ +void usb_stack_irq(void) { + + /* simply notify usb controller */ + if (usbcore.active_controller != NULL && usbcore.active_controller->irq != NULL) { + usbcore.active_controller->irq(); + } +} + +/** + * If a host device controller is loaded, we need to have a function + * to call for maintanence. We need to check if a new device has connected, + * find suitable drivers for new devices. + */ +void usb_stack_work(void) { + /* TODO will be used with host device controllers + * and needs to be called in a loop (thread) */ +} + +/** + * Register an usb controller in the stack. The stack can + * only have two controllers registered at one time. + * One device host controller and one host device controller. + * + * @param ctrl pointer to controller to register. + * @return 0 on success else a defined error code. + */ +int usb_controller_register(struct usb_controller* ctrl) { + + if (ctrl == NULL) { + return EINVAL; + } + + logf("usb_stack: register usb ctrl"); + logf(" -> name: %s", ctrl->name); + logf(" -> type: %d", ctrl->type); + + switch (ctrl->type) { + case DEVICE: + if (usbcore.controller[0] == NULL) { + usbcore.controller[0] = ctrl; + return 0; + } + break; + case HOST: + if (usbcore.controller[1] == NULL) { + usbcore.controller[1] = ctrl; + return 0; + } + break; + default: + return EINVAL; + } + + return ENOFREESLOT; +} + +/** + * Unregister an usb controller from the stack. + * + * @param ctrl pointer to controller to unregister. + * @return 0 on success else a defined error code. + */ +int usb_controller_unregister(struct usb_controller* ctrl) { + + if (ctrl == NULL) { + return EINVAL; + } + + switch (ctrl->type) { + case DEVICE: + if (usbcore.controller[0] == ctrl) { + usbcore.controller[0] = NULL; + return 0; + } + break; + case HOST: + if (usbcore.controller[1] == ctrl) { + usbcore.controller[1] = NULL; + return 0; + } + break; + default: + return EINVAL; + } + + return 0; /* never reached */ +} + +/** + * Select an usb controller and active it. + * + * @param type of controller to activate. + */ +void usb_controller_select(int type) { + + struct usb_controller* new = NULL; + + /* check if a controller of the wanted type is already loaded */ + if (usbcore.active_controller != NULL && (int)usbcore.active_controller->type == type) { + logf("controller already set"); + return; + } + + logf("usb_controller_select"); + logf(" -> type: %d", type); + + usbcore.mode = type; + + switch (type) { + case AUTOMATIC: + /* we want autodetection, so shutdown current controller */ + if (usbcore.active_controller != NULL) { + usbcore.active_controller->shutdown(); + usbcore.active_controller = NULL; + } + return; + break; + case DEVICE: + new = usbcore.controller[0]; + break; + case HOST: + new = usbcore.controller[1]; + break; + } + + /* if there is only one controller, stop here */ + if (new == NULL) { + logf("no suitable cntrl found"); + return; + } + + /* shutdown current used controller */ + if (usbcore.active_controller != NULL) { + usbcore.active_controller->shutdown(); + } + + /* set and init new controller */ + usbcore.active_controller = new; + usbcore.active_controller->init(); +} + +/** + * Register an usb device driver. + * + * @param driver pointer to an usb_device_driver struct. + * @return 0 on success, else a defined error code. + */ +int usb_device_driver_register(struct usb_device_driver* driver) { + + if (driver == NULL) { + return EINVAL; + } + + logf("usb_stack: register usb driver"); + /* add to linked list */ + list_add(&driver->list, &usbcore.device_drivers.list); + + return 0; +} + +/** + * Unregister an usb device driver. + * + * @param driver to unregister + * @return 0 or defined error code. + */ +int usb_device_driver_unregister(struct usb_device_driver* driver) { + + if (driver == NULL) { + return EINVAL; + } + + /* delete from linked list */ + list_del(&driver->list); + return 0; +} + +int usb_device_driver_bind(const char* name) { + + struct list_head *pos = NULL; + struct usb_device_driver *tmp = NULL; + struct usb_device_driver *driver = NULL; + + if (name == NULL) { + return EINVAL; + } + + /* look if there is an usb controller loaded */ + if (usbcore.active_controller == NULL) { + logf("no active controller"); + return ENOCONTROLLERACTIVE; + } + + /* we need to have an active dcd controller */ + if (usbcore.active_controller->type != DEVICE) { + logf("wrong type"); + return EWRONGCONTROLLERTYPE; + } + + /* look for driver */ + logf("looking for driver %s", name); + list_for_each(pos, &usbcore.device_drivers.list) { + tmp = list_entry(pos, struct usb_device_driver, list); + if (strcmp(name, tmp->name) == 0) { + driver = tmp; + } + } + + if (driver == NULL) { + logf("no driver found"); + return ENODRIVERFOUND; + } + + /* bind driver to controller */ + usbcore.active_controller->device_driver = driver; + + /* init dirver */ + driver->bind(usbcore.active_controller->controller_ops); + + return 0; +} + +void usb_device_driver_unbind(void) { + + logf("usb_device_driver_unbind"); + if (usbcore.active_controller->device_driver != NULL) { + usbcore.active_controller->device_driver->unbind(); + usbcore.active_controller->device_driver = NULL; + } +} Index: firmware/usbstack/drivers/usb_serial.c =================================================================== --- firmware/usbstack/drivers/usb_serial.c (revision 0) +++ firmware/usbstack/drivers/usb_serial.c (revision 0) @@ -0,0 +1,120 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * 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. + * + ****************************************************************************/ + +#include "usb_serial.h" +#include + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define GS_NUM_CONFIGS 1 +#define GS_NO_CONFIG_ID 0 +#define GS_BULK_CONFIG_ID 1 +#define GS_ACM_CONFIG_ID 2 + +static struct usb_dcd_controller_ops* ops; +static struct usb_request req; + +struct usb_device_driver usb_serial_driver = { + "serial", + usb_serial_driver_bind, + 0, + usb_serial_driver_request, + 0, + 0, + 0, +}; + +/* 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 */ + +/* 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 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, +}; + +void usb_serial_driver_init(void) { + + logf("usb serial: register"); + usb_device_driver_register(&usb_serial_driver); +} + +void usb_serial_driver_bind(void* controler_ops) { + + logf("usb serial: bind"); + ops = controler_ops; + + /* alloc some space to store responses */ + //req.buf = ops- +} + +int usb_serial_driver_request(struct usb_ctrlrequest* request) { + + int ret = -EOPNOTSUPP; + logf("usb serial: request"); + + if ((request->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (request->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + + switch (request->wValue >> 8) { + case USB_DT_DEVICE: + ret = MIN(request->wLength, (uint16_t)sizeof(struct usb_device_descriptor)); + req.buf = &gs_device_desc; + break; + } + break; + } + } + + if (ret > 0) { + req.length = ret; + logf("queuing response"); + ret = ops->queue(NULL, &req); + } + + return ret; +} + +struct usb_device_driver* usb_serial_get_driver(void) { + return &usb_serial_driver; +} Index: firmware/usbstack/drivers/usb_serial.h =================================================================== --- firmware/usbstack/drivers/usb_serial.h (revision 0) +++ firmware/usbstack/drivers/usb_serial.h (revision 0) @@ -0,0 +1,30 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * 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 _SERIAL_H_ +#define _SERIAL_H_ + +#include "usbstack.h" + +void usb_serial_driver_init(void); +void usb_serial_driver_bind(void* controller_ops); +int usb_serial_driver_request(struct usb_ctrlrequest* req); +struct usb_device_driver* usb_serial_get_driver(void); + +#endif /*_SERIAL_H_*/ Index: firmware/SOURCES =================================================================== --- firmware/SOURCES (revision 14368) +++ firmware/SOURCES (working copy) @@ -223,6 +223,11 @@ /* USBOTG */ #if !defined(SIMULATOR) +#if defined(USE_USBSTACK) +usbstack/core/core.c +usbstack/core/utils.c +usbstack/drivers/usb_serial.c +#endif #if CONFIG_USBOTG == USBOTG_ISP1362 drivers/isp1362.c #endif @@ -230,7 +235,7 @@ #if CONFIG_USBOTG == USBOTG_M5636 drivers/m5636.c #elif CONFIG_USBOTG == USBOTG_ARC -drivers/arcotg_udc.c +drivers/usb/arcotg_dcd.c #endif /* CONFIG_USBOTG */ #endif /* !defined(BOOTLOADER) */ #endif /* !defined(SIMULATOR) */ Index: firmware/target/arm/usb-fw-pp502x.c =================================================================== --- firmware/target/arm/usb-fw-pp502x.c (revision 14368) +++ firmware/target/arm/usb-fw-pp502x.c (working copy) @@ -8,6 +8,7 @@ * $Id$ * * Copyright (C) 2002 by Linus Nielsen Feltzing + * Copyright (C) 2007 by Christian Gmeiner * * iPod driver based on code from the ipodlinux project - http://ipodlinux.org * Adapted for Rockbox in January 2006 @@ -21,32 +22,18 @@ * KIND, either express or implied. * ****************************************************************************/ -#include "config.h" -#include "cpu.h" -#include "kernel.h" -#include "thread.h" + #include "system.h" -#include "debug.h" -#include "ata.h" -#include "fat.h" -#include "disk.h" -#include "panic.h" -#include "lcd.h" -#include "adc.h" #include "usb.h" -#include "button.h" -#include "sprintf.h" -#include "string.h" -#include "hwcompat.h" - #include "usb-target.h" +#include "usbstack.h" #include "arcotg_udc.h" -void usb_init_device(void) -{ - int r0; - outl(inl(0x70000084) | 0x200, 0x70000084); +void usb_init_device(void) { + logf("usb_init_device"); + + outl(inl(0x70000084) | 0x200, 0x70000084); outl(inl(0x7000002C) | 0x3000000, 0x7000002C); DEV_EN |= DEV_USB; @@ -55,90 +42,42 @@ DEV_INIT |= INIT_USB; while ((inl(0x70000028) & 0x80) == 0); - - UDC_PORTSC1 |= PORTSCX_PORT_RESET; - while ((UDC_PORTSC1 & PORTSCX_PORT_RESET) != 0); - - 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); - } + /* enable interrupts */ +#if defined(SANSA_E200) + GPIOB_INT_EN &= ~(1 << 4); - UDC_USBCMD |= USB_CMD_CTRL_RESET; - while((UDC_USBCMD & USB_CMD_CTRL_RESET) != 0); - - r0 = UDC_PORTSC1; + GPIOB_OUTPUT_EN &= ~(1 << 4); + GPIOB_ENABLE |= (1 << 4); + GPIOB_INT_LEV = (GPIOB_INT_LEV & ~(1 << 4)) | + (~GPIOB_INPUT_VAL & (1 << 4)); - /* 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); + /* enable gpio interrupt in cpu */ + CPU_INT_EN = HI_MASK; + CPU_HI_INT_EN = GPIO0_MASK; - udelay(0x186A0); - - dr_controller_setup(); - -#if defined(IPOD_COLOR) || defined(IPOD_4G) \ - || defined(IPOD_MINI) || defined(IPOD_MINI2G) - /* GPIO C bit 1 is firewire detect */ - GPIOC_ENABLE |= 0x02; - GPIOC_OUTPUT_EN &= ~0x02; + /* ack interrupt */ + GPIOB_INT_CLR = (1 << 4); + /* enable gpiob interupt */ + GPIOB_INT_EN |= (1 << 4); #endif } -void usb_enable(bool on) -{ - /* This device specific code will eventually give way to proper USB - handling, which should be the same for all PP502x targets. */ - if (on) - { -#if defined(IPOD_ARCH) || defined(IRIVER_H10) || defined (IRIVER_H10_5GB) - /* For the H10 and iPod, we can only do one thing with USB mode - reboot - into the flash-based disk-mode. This does not return. */ +void usb_enable(bool on) { -#if defined(IRIVER_H10) || defined (IRIVER_H10_5GB) - if(button_status()==BUTTON_RIGHT) -#endif - { - ata_sleepnow(); /* Immediately spindown the disk. */ - sleep(HZ*2); - -#ifdef IPOD_ARCH /* The following code is based on ipodlinux */ -#if CONFIG_CPU == PP5020 - memcpy((void *)0x40017f00, "diskmode\0\0hotstuff\0\0\1", 21); -#elif CONFIG_CPU == PP5022 - memcpy((void *)0x4001ff00, "diskmode\0\0hotstuff\0\0\1", 21); -#endif -#endif - - system_reboot(); /* Reboot */ - } -#endif + if (on) { + logf("enabling usb"); + usb_stack_start(); + } else { + logf("disabling usb"); + usb_stack_stop(); } } -bool usb_detect(void) -{ - static bool prev_usbstatus1 = false; - bool usbstatus1,usbstatus2; +bool usb_detect(void) { -#if defined(IPOD_COLOR) || defined(IPOD_4G) \ - || defined(IPOD_MINI) || defined(IPOD_MINI2G) - /* GPIO C bit 1 is firewire detect */ - if (!(GPIOC_INPUT_VAL & 0x02)) - return true; -#endif - + bool connected = false; + /* UDC_ID should have the bit format: [31:24] = 0x0 [23:16] = 0x22 (Revision number) @@ -150,19 +89,29 @@ return false; } - usbstatus1 = (UDC_OTGSC & 0x800) ? true : false; - if ((usbstatus1 == true) && (prev_usbstatus1 == false)) { - dr_controller_run(); - } else if ((usbstatus1 == false) && (prev_usbstatus1 == true)) { - dr_controller_stop(); + connected = (UDC_OTGSC & 0x800) ? true : false; + + if (connected == false) { + //logf("con2: %d", (UDC_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS) ? true : false); + usb_stack_stop(); + return false; } + + /* do we want autodetection? */ + if (usbcore.mode == AUTOMATIC) { - prev_usbstatus1 = usbstatus1; - usbstatus2 = (UDC_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS) ? true : false; - - if (usbstatus1 && usbstatus2) { - return true; - } else { - return false; + logf("automatic mode"); + UDC_OTGSC |= 0x5F000000; + if ((UDC_OTGSC & 0x100) == 0) { + usb_controller_select(HOST); + } else { + usb_controller_select(DEVICE); + } } + + /* TODO correct place? */ + usb_stack_start(); + + return true; } + Index: firmware/target/arm/system-pp502x.c =================================================================== --- firmware/target/arm/system-pp502x.c (revision 14368) +++ firmware/target/arm/system-pp502x.c (working copy) @@ -20,6 +20,10 @@ #include "thread.h" #include "i2s.h" +#if defined(USE_USBSTACK) +#include "usbstack.h" +#endif + #if NUM_CORES > 1 struct mutex boostctrl_mtx NOCACHEBSS_ATTR; #endif @@ -53,13 +57,15 @@ else if (CPU_HI_INT_STAT & GPIO0_MASK) { if (GPIOA_INT_STAT & 0x80) microsd_int(); + if (GPIOB_INT_STAT & 0x10) + usb_stack_irq(); } else if (CPU_HI_INT_STAT & GPIO1_MASK) { if (GPIOF_INT_STAT & 0xff) button_int(); if (GPIOH_INT_STAT & 0xc0) clickwheel_int(); - } + } #endif } else { if (COP_INT_STAT & TIMER1_MASK) Index: firmware/drivers/usb/arcotg_dcd.h =================================================================== --- firmware/drivers/usb/arcotg_dcd.h (revision 0) +++ firmware/drivers/usb/arcotg_dcd.h (revision 0) @@ -0,0 +1,128 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: arcotg_udc.h 12340 2007-02-16 22:13:21Z barrywardell $ + * + * Copyright (C) 2007 by Christian Gmeiner + * + * Based on code from the Linux Target Image Builder from Freescale + * available at http://www.bitshrine.org/ and + * http://www.bitshrine.org/gpp/linux-2.6.16-mx31-usb-2.patch + * Adapted for Rockbox in January 2007 + * Original file: drivers/usb/gadget/arcotg_udc.c + * + * USB Device Controller Driver + * Driver for ARC OTG USB module in the i.MX31 platform, etc. + * + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Based on mpc-udc.h + * Author: Li Yang (leoli@freescale.com) + * Jiang Bo (Tanya.jiang@freescale.com) + * + * 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 _ARCOTG_DCD_H_ +#define _ARCOTG_DCD_H_ + +#include "system.h" +#include "arcotg_udc.h" + +#define USB_MAX_ENDPOINTS 8 +#define USB_MAX_PIPES (USB_MAX_ENDPOINTS*2) + +/* pipe direction macro from device view */ +#define USB_RECV (0) /* OUT EP */ +#define USB_SEND (1) /* IN EP */ + +/* Shared Bit Masks for Endpoint Queue Head and Endpoint Transfer Descriptor */ +#define TERMINATE (1 << 0) +#define STATUS_ACTIVE (1 << 7) +#define STATUS_HALTED (1 << 6) +#define STATUS_DATA_BUFF_ERR (1 << 5) +#define STATUS_TRANSACTION_ERR (1 << 4) +#define INTERRUPT_ON_COMPLETE (1 << 15) +#define LENGTH_BIT_POS (16) +#define ADDRESS_MASK (0xFFFFFFE0) +#define ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) + +#define RESERVED_FIELDS ((1 << 0) | (1 << 2) | (1 << 4) | \ + (1 << 8) | (1 << 9) | (1 << 12)| \ + (1 << 13)| (1 << 14)| (1 << 31)) + +/* 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(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_MULTO (0x00000C00) +#define EP_QUEUE_CURRENT_OFFSET_MASK (0x00000FFF) +#define EP_QUEUE_FRINDEX_MASK (0x000007FF) +#define EP_MAX_LENGTH_TRANSFER (0x4000) + +/* Endpoint Transfer Descriptor data struct */ +struct etd { + uint32_t next_dtd; /* Next TD pointer(31-5), T(0) set indicate invalid */ + uint32_t dtd_token; /* Total bytes (30-16), IOC (15), MultO(11-10), STS (7-0) */ + uint32_t buff_ptr0; /* Buffer pointer Page 0 */ + uint32_t buff_ptr1; /* Buffer pointer Page 1 */ + uint32_t buff_ptr2; /* Buffer pointer Page 2 */ + uint32_t buff_ptr3; /* Buffer pointer Page 3 */ + uint32_t buff_ptr4; /* Buffer pointer Page 4 */ + uint32_t res; /* make it an even 8 words */ +} __attribute((packed)); + +/* Endpoint Queue Head*/ +struct eqh { + uint32_t endpt_cap; /* Mult(31-30) , Zlt(29) , Max Pkt len + * and IOS(15) */ + uint32_t cur_dtd; /* Current dTD Pointer(31-5) */ + struct etd dtd_ovrl; /* Transfer descriptor */ + uint32_t setup_buffer[2]; /* Setup data 8 bytes */ + uint32_t res2[4]; /* pad out to 64 bytes */ +} __attribute((packed)); + + +struct arcotg_udc { + struct mutex lock; + struct usb_ctrlrequest local_setup_buff; + enum usb_device_state usb_state; + enum usb_device_state resume_state; + bool stopped; +}; + +/* usb controller ops */ +int usb_arcotg_queue(struct usb_ep* ep, struct usb_request* req); + +/* usb_controller functions */ +int usb_arcotg_dcd_init(void); +void usb_arcotg_dcd_shutdown(void); +void usb_arcotg_dcd_irq(void); +void usb_arcotg_dcd_start(void); +void usb_arcotg_dcd_stop(void); + +/* interrupt handlers */ +static void setup_received_int(struct usb_ctrlrequest* request); +static void port_change_int(void); +static void reset_int(void); +static void suspend_int(void); +static void resume_int(void); + +/* dcd fcuntions */ +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); +static void td_init(struct etd* td, void* buffer, uint32_t todo); +static void ep0Stall(void); + +#endif /*_ARCOTG_DCD_H_*/ Index: firmware/drivers/usb/arcotg_dcd.c =================================================================== --- firmware/drivers/usb/arcotg_dcd.c (revision 0) +++ firmware/drivers/usb/arcotg_dcd.c (revision 0) @@ -0,0 +1,590 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: arcotg_udc.c 12340 2007-02-16 22:13:21Z barrywardell $ + * + * Copyright (C) 2007 by Christian Gmeiner + * + * Based on code from the Linux Target Image Builder from Freescale + * available at http://www.bitshrine.org/ and + * http://www.bitshrine.org/gpp/linux-2.6.16-mx31-usb-2.patch + * Adapted for Rockbox in January 2007 + * Original file: drivers/usb/gadget/arcotg_udc.c + * + * USB Device Controller Driver + * Driver for ARC OTG USB module in the i.MX31 platform, etc. + * + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Based on mpc-udc.h + * Author: Li Yang (leoli@freescale.com) + * Jiang Bo (Tanya.jiang@freescale.com) + * + * 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 "usbstack.h" +#include "arcotg_dcd.h" + +static void info_usb_register(void) { + + logf("-reg dump-"); + + logf("USBCMD 0x%08x", UDC_USBCMD); + logf("USBSTS 0x%08x", UDC_USBSTS); + logf("USBINTR 0x%08x", UDC_USBINTR); + logf("FRINDEX 0x%08x", UDC_FRINDEX); + logf("DEVICEADDR 0x%08x", UDC_DEVICEADDR); + logf("ENDPOINTLISTADDR 0x%08x", UDC_ENDPOINTLISTADDR); + logf("ASYNCTTSTS 0x%08x", UDC_ASYNCTTSTS); + logf("PORTSC1 0x%08x", UDC_PORTSC1); + logf("OTGSC 0x%08x", UDC_OTGSC); + logf("USBMODE 0x%08x", UDC_USBMODE); + logf("ENDPTSETUPSTAT 0x%08x", UDC_ENDPTSETUPSTAT); + logf("ENDPTPRIME 0x%08x", UDC_ENDPTPRIME); + logf("ENDPTCOMPLETE 0x%08x", UDC_ENDPTCOMPLETE); + logf("ENDPTCTRL0 0x%08x", UDC_ENDPTCTRL0); + + logf("-end dump-"); +} + +/* description of our device driver operations */ +struct usb_dcd_controller_ops arotg_dcd_opt = { + .enable = NULL, + .disable = NULL, + .set_halt = NULL, + .alloc_request = NULL, + .free_request = NULL, + .queue = usb_arcotg_queue, + .dequeue = NULL, +}; + +/* description of our usb controller driver */ +struct usb_controller arcotg_dcd = { + .name = "arcotg_dcd", + .type = DEVICE, + .init = usb_arcotg_dcd_init, + .shutdown = usb_arcotg_dcd_shutdown, + .irq = usb_arcotg_dcd_irq, + .start = usb_arcotg_dcd_start, + .stop = usb_arcotg_dcd_stop, + .controller_ops = (void*)&arotg_dcd_opt, +}; + +/* USEC timer */ +#define PRIME_TIMER 100000 +#define TRANSFER_TIMER 1000000 +#define RESET_TIMER 5000000 + +struct timer { + unsigned long s; + unsigned long e; +}; + +/* Maybe put this in a more global place */ +void timer_set(struct timer * timer, unsigned long val) { + timer->s = USEC_TIMER; + timer->e = timer->s + val + 1; +} + +int timer_expired(struct timer * timer) { + unsigned long val = USEC_TIMER; + + if (timer->e > timer->s) { + return !(val >= timer->s && val <= timer->e); + } else { + return (val > timer->e && val < timer->s); + } +} + +void usleep(unsigned long t) { + struct timer timer; + + timer_set(&timer, t); + while (!timer_expired(&timer)); +} + +static struct arcotg_udc udc_controller; + +/* device structes used to `communicate` with the device controler */ +struct eqh qh[USB_MAX_PIPES] __attribute((aligned (1 << 11))) __attribute__ ((section(".ibss"))); +struct etd td[1] __attribute((aligned (32))) __attribute__ ((section(".ibss"))); /* only for testing */ + +/* shared memory used by rockbox and dcd to exchange data */ +#define BUFFER_SIZE 4096 +unsigned char buffer[BUFFER_SIZE] __attribute((aligned (1 << 12))) __attribute__ ((section(".ibss"))); + + +void usb_dcd_init() { + + logf("arcotg_dcd: registering us"); + /* register us in the usb stack */ + usb_controller_register(&arcotg_dcd); +} + + +/* + * arotg controller functions + */ + +void usb_arcotg_dcd_start(void) { + + logf("arcotg_dcd: start"); + + /* enable interrupt sources */ + UDC_USBINTR = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN | + USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN | + USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + /* clear stopped bit */ + udc_controller.stopped = false; + + /* set controller to Run */ + UDC_USBCMD |= USB_CMD_RUN_STOP; +} + +void usb_arcotg_dcd_stop(void) { + + logf("arcotg_dcd: stop"); + + /* disable all interrupts */ + UDC_USBINTR = 0; + + /* set stopped bit */ + udc_controller.stopped = true; + + /* set controller to Stop */ + UDC_USBCMD &= ~USB_CMD_RUN_STOP; +} + +int usb_arcotg_dcd_init(void) { + + struct timer t; + logf("arcotg_dcd: init"); + + /* setup data structues */ + memset(&udc_controller, 0, sizeof(struct arcotg_udc)); + spinlock_init(&udc_controller.lock); + + /* stop usb controller */ + UDC_USBCMD &= ~USB_CMD_RUN_STOP; + + usleep(500000); + + timer_set(&t, RESET_TIMER); + + /* reset */ + UDC_USBCMD |= USB_CMD_CTRL_RESET; + while (UDC_USBCMD & USB_CMD_CTRL_RESET) { + if (timer_expired(&t)) { + logf("%s: TIMEOUT", __FUNCTION__); + return -ETIMEDOUT; + } + } + + /* set the controller as device mode */ + UDC_USBMODE |= USB_MODE_CTRL_MODE_DEVICE; + + /* init queue heads for ep0 */ + qh_init(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, 0, 0); + qh_init(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, 0, 0); + + /* set address of queue heads */ + UDC_ENDPOINTLISTADDR = (uint32_t)qh; + logf("qhaddr: %x", (uint32_t)qh); + + /* TODO: hardcoded... fix/extend menu code */ + usb_device_driver_bind("serial"); + + return 0; +} + +void usb_arcotg_dcd_shutdown(void) { + + logf("arcotg_dcd: shutdown"); +} + +/** + * Main interrupt dispatcher. + */ +void usb_arcotg_dcd_irq(void) { + + unsigned int irq_src = 0; + int oldlevel = 0; + + if (udc_controller.stopped == true) { + return; + } + + oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + spinlock_lock(&udc_controller.lock); + + /* read source of irq */ + irq_src = UDC_USBSTS & UDC_USBINTR; + + /* TODO */ + if (irq_src == 0x0) { + /* ack interrupt */ + GPIOB_INT_CLR = (1 << 4); + logf("BAD IRQ"); + spinlock_unlock(&udc_controller.lock); + return; + } + + /* hmmm...is this right? seems to work though */ + GPIOB_INT_LEV = (GPIOB_INT_LEV & ~(1 << 4)) | + (~GPIOB_INPUT_VAL & (1 << 4)); + + /* clear notification bits */ + UDC_USBSTS &= irq_src; + logf("irq_src [0x%08x]", irq_src); + + /* check if we need to wake up from suspend */ + if (!(irq_src & USB_STS_SUSPEND) && udc_controller.resume_state) { + resume_int(); + } + + /* USB Interrupt */ + if (irq_src & 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, (uint8_t *) &qh[0].setup_buffer, 8); + setup_received_int(&udc_controller.local_setup_buff); + } + + if (UDC_ENDPTCOMPLETE != 0) { + logf("!!!!!!!!!!!!"); + logf("yes"); + } + } + + if (irq_src & USB_STS_PORT_CHANGE) { + port_change_int(); + } + + if (irq_src & USB_STS_SUSPEND) { + suspend_int(); + } + + if (irq_src & USB_STS_RESET) { + reset_int(); + } + + if (irq_src & USB_STS_ERR) { + logf("!!! error !!!"); + } + + if (irq_src & USB_STS_SYS_ERR) { + logf("!!! sys error !!!"); + } + + /* ack interrupt */ + GPIOB_INT_CLR = (1 << 4); + + spinlock_unlock(&udc_controller.lock); + + set_irq_level(oldlevel); +} + +/* + * arcotg controller ops + */ +int usb_arcotg_queue(struct usb_ep* ep, struct usb_request* req) { + + /* hardcoded to ep0 */ + struct etd* p_Td = &td[0]; + struct eqh* p_Qh = &qh[0]; + const unsigned int mask = 1 << 16; + struct timer t; + + int todo, done = 0; + int size = req->length; + const char * ptr = req->buf; + + do { + todo = MIN(req->length, BUFFER_SIZE); + logf("try tp send %d byte", todo); + + memcpy(buffer, ptr, todo); + td_init(p_Td, buffer, todo); + + /* execute td */ + p_Qh->dtd_ovrl.next_dtd = (uint32_t)p_Td; + p_Qh->dtd_ovrl.dtd_token &= ~0xc0; + + logf("priming..."); + timer_set(&t, PRIME_TIMER); + UDC_ENDPTPRIME |= mask; + + while (UDC_ENDPTPRIME & mask) { + if (timer_expired(&t)) { + logf("%s: TIMEOUT", __FUNCTION__); + return -ETIMEDOUT; + } + } + + if ((UDC_ENDPTSTAT & mask) == 0) { + return -EHWCRITICAL; + } + + info_usb_register(); + + size -= todo; + ptr += todo; + done += todo; + + } while (size > 0); + + return 0; +} + +/* + * Interrupt handlers + */ + +static void setup_received_int(struct usb_ctrlrequest* request) { + + int handled = 0; /* set to zero if we do not handle the message, */ + /* and should pass it to the driver */ + + logf("setup_int"); + into_usb_ctrlrequest(request); + + /* we only handle standard requests */ + if ((request->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + + /* what shall we do? */ + switch (request->bRequest) { + case USB_REQ_GET_STATUS: + break; + + case USB_REQ_SET_ADDRESS: + /* set recived address */ + break; + } + } + + if (handled == 0) { + logf("pass it to driver..."); + /* pass it to device driver */ + if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->request != NULL) { + handled = arcotg_dcd.device_driver->request(request); + logf("result from driver: %d", handled); + } + } + + /* was the request handeled? */ + if (handled < 0) { + /* stall */ + ep0Stall(); + } +} + +static void port_change_int(void) { + + logf("port_change_int"); +#if 0 + uint32_t 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; + } +#endif +} + +static void reset_int(void) { + + struct timer t; + logf("reset_int"); + + /* clear usb state */ + udc_controller.usb_state = USB_STATE_DEFAULT; + + timer_set(&t, RESET_TIMER); + + /* clear all the setup token semaphores */ + UDC_ENDPTSETUPSTAT = UDC_ENDPTSETUPSTAT; + + /* clear all the endpoint complete status bits */ + UDC_ENDPTCOMPLETE = UDC_ENDPTCOMPLETE; + + /* prime and flush pending transfers */ + while (UDC_ENDPTPRIME) { + if (timer_expired(&t)) { + logf("%s->%s: TIMEOUT", __FUNCTION__, "prime"); + return; + } + } + + /* write 1s to the Flush register */ + UDC_ENDPTFLUSH = ~0; + + if ((UDC_PORTSC1 & (1 << 8)) == 0) { + logf("%s->%s: TIMEOUT", __FUNCTION__, "flush"); + return; + } + + UDC_USBSTS = (1 << 6); + + while ((UDC_USBSTS & (1 << 2)) == 0) { /* wait for port change */ + if (timer_expired(&t)) { + logf("%s->%s: TIMEOUT", __FUNCTION__, "port"); + return; + } + } + + UDC_USBSTS = (1 << 2); + + if (UDC_PORTSC1 & PORTSCX_PORT_RESET) { + logf("arcotg: Bus RESET"); + /* Bus is reseting */ + } else { + logf("arcotg: Controller reset"); + udc_controller.usb_state = USB_STATE_ATTACHED; + } +} + +static void suspend_int(void) { + + logf("suspend_int"); + udc_controller.resume_state = udc_controller.usb_state; + udc_controller.usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver */ + if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->suspend != NULL) { + arcotg_dcd.device_driver->suspend(); + } +} + +static void resume_int(void) { + + logf("resume_int"); + udc_controller.usb_state = udc_controller.resume_state; + udc_controller.resume_state = USB_STATE_NOTATTACHED; + + /* report resume to the driver */ + if (arcotg_dcd.device_driver != NULL && arcotg_dcd.device_driver->resume != NULL) { + arcotg_dcd.device_driver->resume(); + } +} + +/* + * Lifecycle + */ + +/** + * Used to initialise queue head for one endpoint. + */ +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 eqh *p_QH = &qh[2 * ep_num + dir]; + uint32_t tmp = 0; + memset(p_QH, 0, sizeof(*p_QH)); + + /* 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 << LENGTH_BIT_POS) | INTERRUPT_ON_COMPLETE; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << LENGTH_BIT_POS) | (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << LENGTH_BIT_POS; + if (zlt) { + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + } + break; + default: + logf("error ep type is %d", ep_type); + return; + } + + /* see 32.14.4.1 Queue Head Initialization */ + + /* write the wMaxPacketSize field as required by the USB Chapter9 or application specific portocol */ + p_QH->endpt_cap = tmp; + + /* write the next dTD Terminate bit fild to 1 */ + p_QH->dtd_ovrl.next_dtd = 1; + + /* write the Active bit in the status field to 0 */ + p_QH->dtd_ovrl.dtd_token &= ~STATUS_ACTIVE; + + /* write the Hald bit in the status field to 0 */ + p_QH->dtd_ovrl.dtd_token &= ~STATUS_HALTED; +} + +static void td_init(struct etd* td, void* buffer, uint32_t todo) { + + /* see 32.14.5.2 Building a Transfer Descriptor */ + + /* init first 7 dwords with 0 */ + memset(td, 0, sizeof(td)); /* set set all to 0 */ + + /* set terminate bit to 1*/ + td->next_dtd = 1; + + /* fill in total bytes with transfer size */ + td->dtd_token = (todo << 16); + + /* set interrupt on compilte if desierd */ + td->dtd_token |= INTERRUPT_ON_COMPLETE; + + /* initialize the status field with the active bit set to 1 and all remaining status bits to 0 */ + td->dtd_token |= STATUS_ACTIVE; + + td->buff_ptr0 = (uint32_t)buffer; + td->buff_ptr1 = 0; + td->buff_ptr2 = 0; + td->buff_ptr3 = 0; + td->buff_ptr4 = 0; +} + +static void ep0Stall(void) { + + logf("stalling ep0"); + UDC_ENDPTCTRL0 |= (EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL); +}