From 89fcb14409b1c3ff532cf4bbd0b582037ef47616 Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Fri, 10 Oct 2008 03:18:10 -0700 Subject: [PATCH] [usb] first stab at a usb device stack --- dev/usb/usb.c | 262 ++++++++++++++++++++++++++++++++++++++++++++- include/dev/usb.h | 32 +++++- include/dev/usbc.h | 70 +++++++++++- 3 files changed, 360 insertions(+), 4 deletions(-) diff --git a/dev/usb/usb.c b/dev/usb/usb.c index 6042391f..b3ddabcd 100644 --- a/dev/usb/usb.c +++ b/dev/usb/usb.c @@ -21,9 +21,267 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include -#include +#include +#include +#include #include -#include +#include + +//#include +//#include +//#include +//#include + +#define LOCAL_TRACE 1 + +static usb_config *config; + +static uint8_t active_config; +static bool usb_active = false; + +static void append_desc_data(usb_descriptor *desc, const void *dat, size_t len) +{ + uint8_t *ptr = malloc(desc->len + len); + + memcpy(ptr, desc->desc, desc->len); + memcpy(ptr + desc->len, dat, len); + free(desc->desc); + desc->desc = ptr; + desc->len += len; +} + +/* returns the interface number assigned */ +static int usb_append_interface(usb_descriptor *desc, const uint8_t *int_descr, size_t len) +{ + uint8_t *ptr = malloc(len); + int interface_num; + + // create a temporary copy of the interface + memcpy(ptr, int_descr, len); + + // find the last interface used + interface_num = ((uint8_t *)desc->desc)[4]; // current interface + + // patch our interface descriptor with the new id + ptr[2] = interface_num; + + // append it to our config desriptor + append_desc_data(desc, ptr, len); + free(ptr); + + // patch the total length of the config descriptor and set the number of interfaces + ((uint16_t *)desc->desc)[1] += len; + interface_num++; + ((uint8_t *)desc->desc)[4] = interface_num; + + return interface_num - 1; +} + +int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len) +{ + return usb_append_interface(&config->highspeed.config, int_descr, len); +} + +int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len) +{ + return usb_append_interface(&config->lowspeed.config, int_descr, len); +} + +void usb_set_string_descriptor(usb_descriptor *desc, const char *string) +{ + int len = strlen(string); + ushort *data; + int datalen = len * 2 + 2; + + data = malloc(datalen); + + /* write length field */ + data[0] = 0x0300 + datalen; + + /* copy the string into the uint16_t based usb string */ + int i; + for (i = 0; i < len; i++) { + data[i + 1] = string[i]; + } + + desc->desc = (void *)data; + desc->len = datalen; +} + +static void set_usb_id(uint16_t vendor, uint16_t product) +{ + // patch the current configuration to with the vendor/product id + ((uint16_t *)config->lowspeed.device.desc)[4] = vendor; + ((uint16_t *)config->lowspeed.device.desc)[5] = product; + + ((uint16_t *)config->highspeed.device.desc)[4] = vendor; + ((uint16_t *)config->highspeed.device.desc)[5] = product; +} + +static int default_usb_callback(usbc_callback_op_t op, const union usb_callback_args *args) +{ + LTRACEF("op %d, args %p\n", op, args); + + /* start looking for specific things to handle */ + if (op == CB_SETUP_MSG) { + const struct usb_setup *setup = args->setup; + DEBUG_ASSERT(setup); + LTRACEF("SETUP: req_type=%#x req=%#x value=%#x index=%#x len=%#x\n", setup->request_type, setup->request, setup->value, setup->index, setup->length); + + if ((setup->request_type & TYPE_MASK) == TYPE_STANDARD) { + switch (setup->request) { + case SET_ADDRESS: + LTRACEF("SET_ADDRESS 0x%x\n", setup->value); + usbc_ep0_ack(); + break; + case SET_FEATURE: + case CLEAR_FEATURE: + // OTAY + LTRACEF("SET/CLEAR_FEATURE, feature 0x%x\n", setup->value); + usbc_ep0_ack(); + break; + case SET_DESCRIPTOR: + LTRACEF("SET_DESCRIPTOR\n"); + usbc_ep0_stall(); + break; + case GET_DESCRIPTOR: { + /* Get the right descriptors based on current speed */ + const struct usb_descriptor_speed *speed; + if (usbc_is_highspeed()) { + speed = &config->highspeed; + } else { + speed = &config->lowspeed; + } + + if ((setup->request_type & RECIP_MASK) == RECIP_DEVICE) { + switch (setup->value) { + case 0x100: /* device */ + LTRACEF("got GET_DESCRIPTOR, device descriptor\n"); + usbc_ep0_send(speed->device.desc, speed->device.len, + setup->length); + break; + case 0x200: /* CONFIGURATION */ + LTRACEF("got GET_DESCRIPTOR, config descriptor\n"); + usbc_ep0_send(speed->config.desc, speed->config.len, + setup->length); + break; + case 0x300: /* Language ID */ + LTRACEF("got GET_DESCRIPTOR, language id\n"); + usbc_ep0_send(config->langid.desc, + config->langid.len, setup->length); + break; + case 0x301: /* Manufacturer string */ + LTRACEF("got GET_DESCRIPTOR, mfg string\n"); + usbc_ep0_send(config->mfg_string.desc, + config->mfg_string.len, + setup->length); + break; + case 0x302: /* Device string */ + LTRACEF("got GET_DESCRIPTOR, device string\n"); + usbc_ep0_send(config->device_string.desc, + config->device_string.len, + setup->length); + break; + case 0x303: /* Serial number string */ + LTRACEF("got GET_DESCRIPTOR, serial number string\n"); + if (config->serial_string.desc) { + usbc_ep0_send(config->serial_string.desc, + config->serial_string.len, + setup->length); + } else { + /* stall */ + usbc_ep0_stall(); + } + break; + case 0x600: /* DEVICE QUALIFIER */ + LTRACEF("got GET_DESCRIPTOR, device qualifier\n"); + usbc_ep0_send(speed->device_qual.desc, + speed->device_qual.len, setup->length); + break; + case 0xa00: + /* we aint got one of these */ + LTRACEF("got GET_DESCRIPTOR, debug descriptor\n"); + usbc_ep0_stall(); + break; + default: + LTRACEF("unhandled descriptor %#x\n", setup->value); + // stall + break; + } + } else { + // interface/endpoint descriptors? let someone else handle it + // STALL + } + break; + } + + case SET_CONFIGURATION: + LTRACEF("SET_CONFIGURATION %d\n", setup->value); + active_config = setup->value; + usbc_ep0_ack(); + break; + + case GET_CONFIGURATION: + LTRACEF("GET_CONFIGURATION\n"); + usbc_ep0_send(&active_config, 1, setup->length); + break; + + case SET_INTERFACE: + LTRACEF("SET_INTERFACE %d\n", setup->value); + usbc_ep0_ack(); + break; + + case GET_INTERFACE: { + static uint8_t i = 1; + LTRACEF("GET_INTERFACE\n"); + usbc_ep0_send(&i, 1, setup->length); + break; + } + + case GET_STATUS: { + static uint16_t i = 1; // self powered + LTRACEF("GET_STATUS\n"); + usbc_ep0_send(&i, 2, setup->length); + break; + } + default: + LTRACEF("unhandled standard request 0x%x\n", setup->request); + } + } + } + + return 0; +} + +void usb_setup(usb_config *_config) +{ + ASSERT(_config); + + config = _config; + + ASSERT(usb_active == false); + + // set the default usb control callback handler + usbc_set_callback(&default_usb_callback); +} + +void usb_start(void) +{ + ASSERT(config); + ASSERT(usb_active == false); + + // go online + usbc_set_active(true); + usb_active = true; +} + +void usb_stop(void) +{ + ASSERT(usb_active == true); + + usb_active = false; + usbc_set_active(false); +} void usb_init(void) { diff --git a/include/dev/usb.h b/include/dev/usb.h index 556a4edc..a35a8195 100644 --- a/include/dev/usb.h +++ b/include/dev/usb.h @@ -23,9 +23,39 @@ #ifndef __DEV_USB_H #define __DEV_USB_H -/* device side usb stack api */ +#include +#include + +/* top level initialization for usb client, abstracts away the interfaces */ +typedef struct { + void *desc; + size_t len; +} usb_descriptor __ALIGNED(2); + +/* complete usb config struct, passed in to usb_setup() */ +typedef struct { + struct usb_descriptor_speed { + usb_descriptor device; + usb_descriptor device_qual; + usb_descriptor config; + } lowspeed, highspeed; + usb_descriptor device_string; + usb_descriptor mfg_string; + usb_descriptor serial_string; + usb_descriptor langid; +} usb_config; void usb_init(void); +/* external code needs to set up the usb stack via the following calls */ +void usb_setup(usb_config *config); + +/* apped new interface descriptors to the existing config if desired */ +int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len); +int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len); + +void usb_start(void); +void usb_stop(void); + #endif diff --git a/include/dev/usbc.h b/include/dev/usbc.h index 26c19455..f566190c 100644 --- a/include/dev/usbc.h +++ b/include/dev/usbc.h @@ -23,8 +23,76 @@ #ifndef __DEV_USBC_H #define __DEV_USBC_H -/* device side usb controller api (used by the usb stack) */ +#include +#include +#include + void usbc_init(void); +typedef uint ep_t; + +typedef enum { + IN = 0, + OUT +} ep_dir_t; + +typedef enum { + CB_RESET, + CB_SUSPEND, + CB_RESUME, + CB_DISCONNECT, + CB_ONLINE, + CB_OFFLINE, + CB_SETUP_MSG, + + /* endpoint transfer stuff */ + CB_EP_RXCOMPLETE, + CB_EP_TXCOMPLETE, + CB_EP_TRANSFER_CANCELLED, +} usbc_callback_op_t; + +typedef struct { + void *buf; + size_t buflen; + uint bufpos; + int result; + void *extra; // extra pointer to store whatever you want +} usbc_transfer; + +enum { + USB_TRANSFER_RESULT_OK = 0, + USB_TRANSFER_RESULT_ERR = -1, + USB_TRANSFER_RESULT_CANCELLED = -2, +}; + +typedef int (*ep_callback)(ep_t endpoint, usbc_callback_op_t op, usbc_transfer *transfer); + +void usbc_setup_endpoint(ep_t ep, ep_dir_t dir, bool active, ep_callback callback, uint width, uint blocksize); +int usbc_queue_rx(ep_t ep, usbc_transfer *transfer); +int usbc_queue_tx(ep_t ep, usbc_transfer *transfer); + +/* setup arg is valid during CB_SETUP_MSG */ +union usb_callback_args { + const struct usb_setup *setup; +}; + +typedef int (*usb_callback)(usbc_callback_op_t op, const union usb_callback_args *args); + +int usbc_set_callback(usb_callback); +int usbc_set_active(bool active); + +/* called back from within a callback to handle setup responses */ +void usbc_ep0_ack(void); +void usbc_ep0_stall(void); +void usbc_ep0_send(const void *buf, size_t len, size_t maxlen); +void usbc_ep0_recv(void *buf, size_t len, ep_callback); + +bool usbc_is_highspeed(void); + +static inline void usbc_dump_transfer(const usbc_transfer *t) +{ + printf("usb transfer %p: buf %p, buflen %zd, bufpos %u, result %d\n", t, t->buf, t->buflen, t->bufpos, t->result); +} + #endif