diff --git a/cpu/arm/common/usb/Makefile.usb b/cpu/arm/common/usb/Makefile.usb new file mode 100644 index 000000000..b58b2a9a2 --- /dev/null +++ b/cpu/arm/common/usb/Makefile.usb @@ -0,0 +1,48 @@ +### Put generated sources in a separate directory +BUILTSRCDIR = src_$(TARGET) +ifeq (${wildcard $(BUILTSRCDIR)},) + DUMMY := ${shell mkdir $(BUILTSRCDIR)} +endif + +PROJECTDIRS += $(BUILTSRCDIR) + +USB_STRING_DESCRIPTORS ?= $(CONTIKI_CPU_ARM)/common/usb/cdc-acm/string-descriptors.xml + +XMLDIRS= + +USB = usb-arch.c usb-core.c + +ifdef USB_CDC_ACM_CLASS +CONTIKI_CPU_DIRS += ../common/usb/cdc-acm +USB += cdc-acm.c cdc-acm-descriptors.c cdc-acm-string-descriptors.c +XMLDIRS += $(CONTIKI_CPU_ARM)/common/usb/cdc-acm/ +endif + +ifdef USB_CDC_ETH_CLASS +CONTIKI_CPU_DIRS += ../common/usb/cdc-eth +USB += cdc-eth.c cdc-eth-descriptors.c cdc-eth-string-descriptors.c dhcps.c +XMLDIRS += $(CONTIKI_CPU_ARM)/common/usb/cdc-eth/ +endif + +ifdef USB_MASS_STORAGE_CLASS +CONTIKI_CPU_DIRS += ../common/usb/msc +USB += usb-msc-bulk.c usb-rbc.c msc-descriptors.c msc-string-descriptors.c +XMLDIRS += $(CONTIKI_CPU_ARM)/common/usb/msc +endif + +ifdef USB_MSC_QIC157 +CONTIKI_CPU_DIRS += ../common/usb/msc +USB += usb-msc-bulk.c usb-qic157.c msc-qic157-descriptors.c msc-qic157-string-descriptors.c +XMLDIRS += $(CONTIKI_CPU_ARM)/common/usb/msc +endif + +ifdef USB_MSC_STREAMING +CONTIKI_CPU_DIRS += ../common/usb/msc +USB += usb-msc-bulk.c usb-streaming.c msc-scsi-transparent-descriptors.c msc-streaming-string-descriptors.c +XMLDIRS += $(CONTIKI_CPU_ARM)/common/usb/msc +endif + +vpath %.xml $(XMLDIRS) + +%.c: %.xml + $(XSLTPROC) $(CONTIKI_CPU_ARM)/common/usb/string-descriptors.xslt $^ >$(BUILTSRCDIR)/$@ diff --git a/cpu/arm/common/usb/descriptors.h b/cpu/arm/common/usb/descriptors.h new file mode 100644 index 000000000..c8bc72745 --- /dev/null +++ b/cpu/arm/common/usb/descriptors.h @@ -0,0 +1,8 @@ +#ifndef __DESCRIPTORS_H__RPFUB8O7OV__ +#define __DESCRIPTORS_H__RPFUB8O7OV__ + +#include "usb.h" + +extern const struct usb_st_device_descriptor device_descriptor; +extern const struct usb_st_configuration_descriptor const *configuration_head; +#endif /* __DESCRIPTORS_H__RPFUB8O7OV__ */ diff --git a/cpu/arm/common/usb/string-descriptors.dtd b/cpu/arm/common/usb/string-descriptors.dtd new file mode 100644 index 000000000..66f7469f3 --- /dev/null +++ b/cpu/arm/common/usb/string-descriptors.dtd @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/cpu/arm/common/usb/string-descriptors.h b/cpu/arm/common/usb/string-descriptors.h new file mode 100644 index 000000000..6d0ffea5f --- /dev/null +++ b/cpu/arm/common/usb/string-descriptors.h @@ -0,0 +1,16 @@ +#include "usb.h" +struct usb_st_string_language_map +{ + Uint16 lang_id; + const struct usb_st_string_descriptor * const *descriptors; +}; + +struct usb_st_string_languages +{ + Uchar num_lang; + Uchar max_index; + const struct usb_st_language_descriptor *lang_descr; + const struct usb_st_string_language_map map[1]; +}; + +extern const struct usb_st_string_languages * const string_languages; diff --git a/cpu/arm/common/usb/string-descriptors.xslt b/cpu/arm/common/usb/string-descriptors.xslt new file mode 100644 index 000000000..eab453326 --- /dev/null +++ b/cpu/arm/common/usb/string-descriptors.xslt @@ -0,0 +1,129 @@ + + + + + + #include "string-descriptors.h" + + + + static const struct { + struct usb_st_string_descriptor base; + Uint16 chars[ + + ]; + } string_descriptor_ + + _ + + + + + + all + + + = {{ + + , 3, {' + + '}}, { + + + + }}; + + + + + + static const struct usb_st_string_descriptor * string_table_ + + [] = { + + &string_descriptor_ + + _ + + + + + + all + + + + No string found for index + + and language + + + + + .base, + + }; + + + + static const struct { + struct usb_st_language_descriptor base; + Uint16 langs[ + + ]; + } language_descriptor = { + { + + , 3, { + + }}, { + + + , + + }}; + + + static const struct { + struct usb_st_string_languages base; + struct usb_st_string_language_map map[ + + ]; } + string_languages_full={{ + + , + + , &language_descriptor.base + , {{ + + , string_table_ + + }}}, { + + { + + , string_table_ + + }, + + } }; + const struct usb_st_string_languages * const string_languages = &string_languages_full.base; + + + + + + ' + + ' + + , + + + + + + + + + diff --git a/cpu/arm/common/usb/usb-api.h b/cpu/arm/common/usb/usb-api.h new file mode 100644 index 000000000..45a67e29f --- /dev/null +++ b/cpu/arm/common/usb/usb-api.h @@ -0,0 +1,142 @@ +#ifndef __USB_API_H__SYN81IFYBN__ +#define __USB_API_H__SYN81IFYBN__ + +#include + +typedef struct _USBBuffer USBBuffer; + +struct _USBBuffer +{ + USBBuffer *next; /* Pointer to next buffer in chain */ + uint8_t *data; /* Where to read/write data next */ + uint16_t left; /* Remaining length of buffer. */ + uint16_t flags; + uint32_t id; /* User data */ +}; + +/* Buffer owned by the USB code, cleared when done */ +#define USB_BUFFER_SUBMITTED 0x01 + +/* Write a short packet at end of buffer or release buffer when a + short packet is received. */ +#define USB_BUFFER_SHORT_END 0x02 + +/* Release buffer as soon as any received data has been written in it. */ +#define USB_BUFFER_PACKET_END 0x04 + +/* Notify the user when the buffer is released */ +#define USB_BUFFER_NOTIFY 0x08 + +/* Packet should be sent to host. */ +#define USB_BUFFER_IN 0x40 + +/* Used for receiving SETUP packets. If a SETUP packet is received and + the next buffers doesn't have this flag set, they will be skipped + until one is found. The associated buffer must be at least 8 bytes */ +#define USB_BUFFER_SETUP 0x20 + +/* HALT the endpoint at this point. Only valid for bulk and interrupt + endpoints */ +#define USB_BUFFER_HALT 0x20 + +/* Flags set by system */ + +/* The last packet written to this buffer was short. */ +#define USB_BUFFER_SHORT_PACKET 0x10 + +/* The operation associated with this buffer failed. I.e. it was discarded since it didn't match the received SETUP packet. */ +#define USB_BUFFER_FAILED 0x80 + +/* Architecture specific flags */ +#define USB_BUFFER_ARCH_FLAG_1 0x1000 +#define USB_BUFFER_ARCH_FLAG_2 0x2000 +#define USB_BUFFER_ARCH_FLAG_3 0x4000 +#define USB_BUFFER_ARCH_FLAG_4 0x8000 + +void +usb_setup(void); + + +/* Read only */ +struct USBRequestHandler +{ + uint8_t request_type; + uint8_t request_type_mask; + uint8_t request; + uint8_t request_mask; + /* Returns true if it handled the request, if false let another handler try*/ + unsigned int (*handler_func)(); +}; + +/* Must be writeable */ +struct USBRequestHandlerHook +{ + struct USBRequestHandlerHook *next; + const struct USBRequestHandler * const handler; +}; + +void +usb_register_request_handler(struct USBRequestHandlerHook *hook); + +void +usb_setup_bulk_endpoint(uint8_t addr); +void +usb_setup_interrupt_endpoint(uint8_t addr); + +/* Submit a chain of buffers to be filled with received data. Last + buffer must have next set to NULL. */ +void +usb_submit_recv_buffer(uint8_t ep_addr, USBBuffer *buffer); + +/* Submit a chain of buffers to be sent. Last buffer must have next + set to NULL. When submitting packets to receive or send data in on + a control enpoint, all packets in the data stage must be submitted + at the same time. */ +void +usb_submit_xmit_buffer(uint8_t ep_addr, USBBuffer *buffer); + +/* Return true if not all data has been sent to the host */ +int +usb_send_pending(uint8_t ep_addr); + +/* Release all buffers submitted to the endpoint and discard any + buffered data. */ +void +usb_discard_all_buffers(uint8_t ep_addr); + +void +usb_disable_endpoint(uint8_t addr); + +/* Set or remove a HALT condition on an endpoint */ +void +usb_halt_endpoint(uint8_t addr, int halt); + +/* Select what process should be polled when buffers with the + USB_BUFFER_NOTIFY flag set is released from the endpoint */ +void +usb_set_ep_event_process(uint8_t addr, struct process *p); + +/* Select what process should be polled when a global event occurs */ +void +usb_set_global_event_process(struct process *p); + +/* Global events */ +#define USB_EVENT_CONFIG 0x01 +#define USB_EVENT_SUSPEND 0x02 +#define USB_EVENT_RESUME 0x04 +#define USB_EVENT_RESET 0x08 + +/* Returns global events that has occured since last time this + function was called */ +unsigned int +usb_get_global_events(void); + + +#define USB_EP_EVENT_NOTIFICATION 0x01 +unsigned int +usb_get_ep_events(uint8_t addr); + +unsigned int +usb_get_current_configuration(void); + +#endif /* __USB_API_H__SYN81IFYBN__ */ diff --git a/cpu/arm/common/usb/usb-arch.h b/cpu/arm/common/usb/usb-arch.h new file mode 100644 index 000000000..d0969c004 --- /dev/null +++ b/cpu/arm/common/usb/usb-arch.h @@ -0,0 +1,92 @@ +#ifndef __USB_ARCH_H__0Z52ZDP0H6__ +#define __USB_ARCH_H__0Z52ZDP0H6__ + +#include +#include + + +/* Includes control endpoint 0 */ +#ifndef USB_MAX_ENDPOINTS +#define USB_MAX_ENDPOINTS 4 +#endif + +#ifndef CTRL_EP_SIZE +#define CTRL_EP_SIZE 8 +#endif + +#ifndef USB_EP1_SIZE +#define USB_EP1_SIZE 8 +#endif +#ifndef USB_EP2_SIZE +#define USB_EP2_SIZE 8 +#endif +#ifndef USB_EP3_SIZE +#define USB_EP3_SIZE 8 +#endif +#ifndef USB_EP4_SIZE +#define USB_EP4_SIZE 0 +#endif +#ifndef USB_EP5_SIZE +#define USB_EP5_SIZE 0 +#endif +#ifndef USB_EP6_SIZE +#define USB_EP6_SIZE 0 +#endif +#ifndef USB_EP7_SIZE +#define USB_EP7_SIZE 0 +#endif + + +#ifndef MAX_CTRL_DATA +#define MAX_CTRL_DATA 128 +#endif + +void +usb_arch_setup(void); + +void +usb_arch_setup_control_endpoint(uint8_t addr); + +void +usb_arch_setup_bulk_endpoint(uint8_t addr); + +void +usb_arch_setup_interrupt_endpoint(uint8_t addr); + +void +usb_arch_disable_endpoint(uint8_t addr); + +void +usb_arch_discard_all_buffers(uint8_t addr); + +/* Stall a control endpoint. The stall will be cleared when the next + SETUP token arrives. */ +void +usb_arch_control_stall(uint8_t addr); + +/* Set or remove a HALT condition on an endpoint */ +void +usb_arch_halt_endpoint(uint8_t addr, int halt); + +void +usb_arch_set_configuration(uint8_t usb_configuration_value); + +uint16_t +usb_arch_get_ep_status(uint8_t addr); + +void +usb_arch_set_address(uint8_t addr); + + +/* Select what process should be polled when a global event occurs. Intended for the protocol handler. Applications should use usb_set_global_event_process */ +void +usb_arch_set_global_event_process(struct process *p); + +unsigned int +usb_arch_get_global_events(void); + +/* Return true if not all data has been sent to the host */ +int +usb_arch_send_pending(uint8_t ep_addr); + +#endif /* __USB_ARCH_H__0Z52ZDP0H6__ */ diff --git a/cpu/arm/common/usb/usb-core.c b/cpu/arm/common/usb/usb-core.c new file mode 100644 index 000000000..4fae45aaa --- /dev/null +++ b/cpu/arm/common/usb/usb-core.c @@ -0,0 +1,588 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define DEBUG */ +#ifdef DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + + +struct USB_request_st usb_setup_buffer; +static USBBuffer ctrl_buffer; + +#define SETUP_ID 1 +#define OUT_ID 2 +#define IN_ID 3 +#define STATUS_OUT_ID 4 +#define STATUS_IN_ID 5 + +static unsigned short usb_device_status; +static unsigned char usb_configuration_value; + +static struct USBRequestHandlerHook *usb_request_handler_hooks = NULL; + +static const unsigned char zero_byte = 0; +static const unsigned short zero_word = 0; + +static unsigned char usb_flags = 0; +#define USB_FLAG_ADDRESS_PENDING 0x01 +#define USB_FLAG_RECEIVING_CTRL 0x04 +#define USB_FLAG_SEND_ZLP 0x08 /* If the last packet has max length, + then it needs to be followed by a + zero length packet to mark the + end. */ + +static struct process *global_user_event_pocess = NULL; +static unsigned int global_user_events = 0; + +void +usb_set_global_event_process(struct process *p) +{ + global_user_event_pocess = p; +} +unsigned int +usb_get_global_events(void) +{ + unsigned int e = global_user_events; + global_user_events = 0; + return e; +} + +static void +notify_user(unsigned int e) +{ + global_user_events |= e; + if (global_user_event_pocess) { + process_poll(global_user_event_pocess); + } +} + +void +usb_send_ctrl_response(const uint8_t *data, unsigned int len) +{ + if (ctrl_buffer.flags & USB_BUFFER_SUBMITTED) return; + if (len >= usb_setup_buffer.wLength) { + len = usb_setup_buffer.wLength; /* Truncate if too long */ + } + ctrl_buffer.flags = USB_BUFFER_NOTIFY | USB_BUFFER_IN; + ctrl_buffer.next = NULL; + ctrl_buffer.data = (uint8_t*)data; + ctrl_buffer.left = len; + ctrl_buffer.id = IN_ID; + usb_submit_xmit_buffer(0,&ctrl_buffer); +} + +void +usb_error_stall() +{ + usb_arch_control_stall(0); +} + +void +usb_send_ctrl_status() +{ + if (ctrl_buffer.flags & USB_BUFFER_SUBMITTED) return; + ctrl_buffer.flags = USB_BUFFER_NOTIFY | USB_BUFFER_IN; + ctrl_buffer.next = NULL; + ctrl_buffer.data = NULL; + ctrl_buffer.left = 0; + ctrl_buffer.id = STATUS_IN_ID; + usb_submit_xmit_buffer(0,&ctrl_buffer); +} + +static usb_ctrl_data_callback data_callback = NULL; +static uint8_t *ctrl_data = NULL; +static unsigned int ctrl_data_len = 0; +void +usb_get_ctrl_data(uint8_t *data, unsigned int length, + usb_ctrl_data_callback cb) +{ + if (ctrl_buffer.flags & USB_BUFFER_SUBMITTED) return; + PRINTF("usb_get_ctrl_data: %d\n",length); + data_callback = cb; + ctrl_data = data; + ctrl_data_len = length; + ctrl_buffer.flags = USB_BUFFER_NOTIFY; + ctrl_buffer.next = NULL; + ctrl_buffer.data = data; + ctrl_buffer.left = length; + ctrl_buffer.id = OUT_ID; + usb_submit_recv_buffer(0,&ctrl_buffer); +} + +#if 0 + +void +usb_set_user_process(struct process *p) +{ + user_process = p; +} +#endif + +static void +get_device_descriptor() +{ + usb_send_ctrl_response((unsigned char*)&device_descriptor, sizeof(device_descriptor)); +} + +static void +get_string_descriptor() +{ + if (LOW_BYTE(usb_setup_buffer.wValue) == 0) { + usb_send_ctrl_response((const unsigned char*)string_languages->lang_descr, + string_languages->lang_descr->bLength); + } else { + unsigned char l; + const struct usb_st_string_descriptor *descriptor; + const struct usb_st_string_descriptor * const *table; + const struct usb_st_string_language_map *map; + if (LOW_BYTE(usb_setup_buffer.wValue) > string_languages->max_index) { + usb_error_stall(); + return; + } + l = string_languages->num_lang; + map = string_languages->map; + table = map->descriptors; /* Use first table if language not found */ + while (l > 0) { + if (map->lang_id == usb_setup_buffer.wIndex) { + table = map->descriptors; + break; + } + map++; + l--; + } + PRINTF("Lang id %04x = table %p\n", usb_setup_buffer.wIndex, (void*)table); + descriptor = table[LOW_BYTE(usb_setup_buffer.wValue) - 1]; + usb_send_ctrl_response((const unsigned char*)descriptor, + descriptor->bLength); + } +} + +static void +get_configuration_descriptor() +{ + usb_send_ctrl_response((unsigned char*)configuration_head, + configuration_head->wTotalLength); +} + +static void +get_configuration() +{ + usb_send_ctrl_response((unsigned char*)&usb_configuration_value, + sizeof(usb_configuration_value)); +} + +/* Returns true if the configuration value changed */ +static int +set_configuration() +{ + notify_user(USB_EVENT_CONFIG); + if (usb_configuration_value != LOW_BYTE(usb_setup_buffer.wValue)) { + usb_configuration_value = LOW_BYTE(usb_setup_buffer.wValue); + usb_arch_set_configuration(usb_configuration_value); + usb_send_ctrl_status(); + return 1; + } else { + usb_send_ctrl_status(); + return 0; + } +} + +static void +get_device_status() +{ + PRINTF("get_device_status\n"); + usb_send_ctrl_response((const unsigned char*)&usb_device_status, + sizeof(usb_device_status)); +} + +static void +get_endpoint_status() +{ + static uint16_t status; + PRINTF("get_endpoint_status\n"); + if ((usb_setup_buffer.wIndex & 0x7f) == 0) { + usb_send_ctrl_response((const unsigned char*)&zero_word, + sizeof(zero_word)); + } else { + status = usb_arch_get_ep_status(usb_setup_buffer.wIndex); + usb_send_ctrl_response((uint8_t*)&status, sizeof(status)); + } +} + +static void +get_interface_status() +{ + PRINTF("get_interface_status\n"); + usb_send_ctrl_response((const unsigned char*)&zero_word, + sizeof(zero_word)); +} + +static void +get_interface() +{ + PRINTF("get_interface\n"); + if (usb_configuration_value == 0) usb_error_stall(); + else { + usb_send_ctrl_response(&zero_byte, + sizeof(zero_byte)); + } +} + + +static unsigned int +handle_standard_requests() +{ + switch(usb_setup_buffer.bmRequestType) { + case 0x80: /* standard device IN requests */ + switch(usb_setup_buffer.bRequest) { + case GET_DESCRIPTOR: + switch (HIGH_BYTE(usb_setup_buffer.wValue)) { + case DEVICE: + get_device_descriptor(); + break; + case CONFIGURATION: + get_configuration_descriptor(); + break; + case STRING: + get_string_descriptor(); + break; + default: + /* Unknown descriptor */ + return 0; + } + break; + case GET_CONFIGURATION: + get_configuration(); + break; + case GET_STATUS: + get_device_status(); + break; + case GET_INTERFACE: + get_interface(); + break; + default: + return 0; + } + break; + case 0x81: /* standard interface IN requests */ + switch(usb_setup_buffer.bRequest) { + case GET_STATUS: + get_interface_status(); + break; +#ifdef HID_ENABLED + case GET_DESCRIPTOR: + switch (USB_setup_buffer.wValue.byte.high) { + case REPORT: + get_report_descriptor(); + break; + } + break; +#endif + default: + return 0; + } + break; + case 0x82: /* standard endpoint IN requests */ + switch(usb_setup_buffer.bRequest) { + case GET_STATUS: + get_endpoint_status(); + break; + default: + return 0; + } + break; + case 0x00: /* standard device OUT requests */ + switch(usb_setup_buffer.bRequest) { + case SET_ADDRESS: + PRINTF("Address: %d\n", LOW_BYTE(usb_setup_buffer.wValue)); + usb_flags |= USB_FLAG_ADDRESS_PENDING; + /* The actual setting of the address is done when the status packet + is sent. */ + usb_send_ctrl_status(); + break; +#if SETABLE_STRING_DESCRIPTORS > 0 + case SET_DESCRIPTOR: + if (usb_setup_buffer.wValue.byte.high == STRING) { + set_string_descriptor(); + } else { + return 0; + } + break; +#endif + case SET_CONFIGURATION: + if (set_configuration()) { +#if 0 + config_msg.data.config = LOW_BYTE(usb_setup_buffer.wValue); + notify_user(&config_msg); +#endif + } + break; + default: + return 0; + } + break; + case 0x01: /* standard interface OUT requests */ + switch(usb_setup_buffer.bRequest) { + case SET_INTERFACE: + /* Change interface here if we support more than one */ + usb_send_ctrl_status(); + break; + default: + return 0; + } + break; + case 0x02: /* standard endpoint OUT requests */ + switch(usb_setup_buffer.bRequest) { + case SET_FEATURE: + case CLEAR_FEATURE: + if (usb_setup_buffer.wValue == ENDPOINT_HALT_FEATURE) { + usb_arch_halt_endpoint(usb_setup_buffer.wIndex, usb_setup_buffer.bRequest== SET_FEATURE); + usb_send_ctrl_status(); + } else { + usb_error_stall(); + } + break; + default: + return 0; + } + break; +#ifdef HID_ENABLED + case 0xa1: /* class specific interface IN request*/ + switch(USB_setup_buffer.bRequest) { + case GET_HID_REPORT: + PRINTF("Get report\n"); + send_ctrl_response((code u_int8_t*)&zero_byte, + sizeof(zero_byte)); + break; + case GET_HID_IDLE: + PRINTF("Get idle\n"); + send_ctrl_response((code u_int8_t*)&zero_byte, + sizeof(zero_byte)); + break; + default: + return 0; + } + break; + case 0x21: /* class specific interface OUT request*/ + switch(USB_setup_buffer.bRequest) { + case SET_HID_IDLE: + PRINTF("Set idle\n"); + send_ctrl_status(); + break; + default: + return 0; + } + break; +#endif + default: + return 0; + } + return 1; +} + +static const struct USBRequestHandler standard_request_handler = + { + 0x00, 0x60, + 0x00, 0x00, + handle_standard_requests + }; + +static struct USBRequestHandlerHook standard_request_hook = + { + NULL, + &standard_request_handler + }; + +static void +submit_setup(void) +{ + ctrl_buffer.next = NULL; + ctrl_buffer.data = (uint8_t*)&usb_setup_buffer; + ctrl_buffer.left = sizeof(usb_setup_buffer); + ctrl_buffer.flags = (USB_BUFFER_PACKET_END | USB_BUFFER_SETUP + | USB_BUFFER_NOTIFY); + ctrl_buffer.id = SETUP_ID; + usb_submit_recv_buffer(0, &ctrl_buffer); +} + +PROCESS(usb_process, "USB"); + +PROCESS_THREAD(usb_process, ev , data) +{ + PROCESS_BEGIN(); + PRINTF("USB process started\n"); + while(1) { + PROCESS_WAIT_EVENT(); + if (ev == PROCESS_EVENT_EXIT) break; + if (ev == PROCESS_EVENT_POLL) { + unsigned int events = usb_arch_get_global_events(); + if (events) { + if (events & USB_EVENT_RESET) { + submit_setup(); + usb_configuration_value = 0; + notify_user(USB_EVENT_RESET); + } + if (events & USB_EVENT_SUSPEND) { + notify_user(USB_EVENT_SUSPEND); + } + if (events & USB_EVENT_RESUME) { + notify_user(USB_EVENT_RESUME); + } + + } + events = usb_get_ep_events(0); + if (events) { + if ((events & USB_EP_EVENT_NOTIFICATION) + && !(ctrl_buffer.flags & USB_BUFFER_SUBMITTED)) { + /* PRINTF("Endpoint 0\n"); */ + if (ctrl_buffer.flags & USB_BUFFER_FAILED) { + /* Something went wrong with the buffer, just wait for a + new SETUP packet */ + PRINTF("Discarded\n"); + submit_setup(); + } else if (ctrl_buffer.flags & USB_BUFFER_SETUP) { + struct USBRequestHandlerHook *hook = usb_request_handler_hooks; + + PRINTF("Setup\n"); + { + unsigned int i; + for (i = 0; i< 8; i++) PRINTF(" %02x", ((unsigned char*)&usb_setup_buffer)[i]); + PRINTF("\n"); + } + + while(hook) { + const struct USBRequestHandler *handler = hook->handler; + /* Check if the handler matches the request */ + if (((handler->request_type ^ usb_setup_buffer.bmRequestType) + & handler->request_type_mask) == 0 + && ((handler->request ^ usb_setup_buffer.bRequest) + & handler->request_mask) == 0) { + if (handler->handler_func()) break; + } + hook = hook->next; + } + if (!hook) { + /* No handler found */ + usb_error_stall(); + PRINTF("Unhandled setup: %02x %02x %04x %04x %04x\n", + usb_setup_buffer.bmRequestType, usb_setup_buffer.bRequest, + usb_setup_buffer.wValue, usb_setup_buffer.wIndex, + usb_setup_buffer.wLength); + submit_setup(); + } + } else { + if (ctrl_buffer.id == IN_ID) { + /* Receive status stage */ + PRINTF("Status OUT\n"); + ctrl_buffer.flags = USB_BUFFER_NOTIFY; + ctrl_buffer.next = NULL; + ctrl_buffer.data = NULL; + ctrl_buffer.left = 0; + ctrl_buffer.id = STATUS_OUT_ID; + usb_submit_recv_buffer(0,&ctrl_buffer); + } else if (ctrl_buffer.id == STATUS_OUT_ID) { + PRINTF("Status OUT done\n"); + submit_setup(); + } else if (ctrl_buffer.id == STATUS_IN_ID) { + PRINTF("Status IN done\n"); + if (usb_flags & USB_FLAG_ADDRESS_PENDING) { + while(usb_send_pending(0)); + usb_arch_set_address(LOW_BYTE(usb_setup_buffer.wValue)); + usb_flags &= ~USB_FLAG_ADDRESS_PENDING; + } + submit_setup(); + } else if (ctrl_buffer.id == OUT_ID) { + PRINTF("OUT\n"); + if (data_callback) { + data_callback(ctrl_data, ctrl_data_len- ctrl_buffer.left); + } else { + usb_send_ctrl_status(); + } + } + } + } + } + } + } + PROCESS_END(); +} + + +void +usb_setup(void) +{ + usb_arch_setup(); + process_start(&usb_process, NULL); + usb_arch_set_global_event_process(&usb_process); + usb_set_ep_event_process(0, &usb_process); + + usb_register_request_handler(&standard_request_hook); +} + +void +usb_register_request_handler(struct USBRequestHandlerHook *hook) +{ + struct USBRequestHandlerHook **prevp = &usb_request_handler_hooks; + /* Find last hook */ + while(*prevp) { + prevp = &(*prevp)->next; + } + /* Add last */ + *prevp = hook; + hook->next = NULL; +} + + +unsigned int +usb_get_current_configuration(void) +{ + return usb_configuration_value; +} + +void +usb_setup_bulk_endpoint(unsigned char addr) +{ + usb_arch_setup_bulk_endpoint(addr); +} + +void +usb_setup_interrupt_endpoint(unsigned char addr) +{ + usb_arch_setup_interrupt_endpoint(addr); +} + +void +usb_disable_endpoint(uint8_t addr) +{ + usb_arch_discard_all_buffers(addr); + usb_arch_disable_endpoint(addr); +} + +void +usb_discard_all_buffers(uint8_t addr) +{ + usb_arch_discard_all_buffers(addr); +} + +void +usb_halt_endpoint(uint8_t addr, int halt) +{ + usb_arch_halt_endpoint(addr, halt); +} + +int +usb_send_pending(uint8_t addr) +{ + return usb_arch_send_pending(addr); +} + diff --git a/cpu/arm/common/usb/usb-core.h b/cpu/arm/common/usb/usb-core.h new file mode 100644 index 000000000..9baf5ab0e --- /dev/null +++ b/cpu/arm/common/usb/usb-core.h @@ -0,0 +1,23 @@ +#ifndef __USB_CORE_H__YIKJDA7S1X__ +#define __USB_CORE_H__YIKJDA7S1X__ + +#include + +struct USB_request_st usb_setup_buffer; + +void +usb_send_ctrl_response(const uint8_t *data, unsigned int len); + +void +usb_error_stall(); + +void +usb_send_ctrl_status(); + +typedef void (*usb_ctrl_data_callback)(uint8_t *data, unsigned int length); + +void +usb_get_ctrl_data(uint8_t *data, unsigned int length, + usb_ctrl_data_callback cb); + +#endif /* __USB_CORE_H__YIKJDA7S1X__ */ diff --git a/cpu/arm/common/usb/usb.h b/cpu/arm/common/usb/usb.h new file mode 100644 index 000000000..86dc3419a --- /dev/null +++ b/cpu/arm/common/usb/usb.h @@ -0,0 +1,185 @@ +#ifndef __USB_H__6PFTDPIMZM__ +#define __USB_H__6PFTDPIMZM__ +#include + +/* Adapted from usb_kbd_enum.h in c5131-usb-kbd-light-1_0_2 package from + Atmel */ + +/* These definitions assume a little endian architecture */ + +#ifdef __GNUC__ +#define BYTE_ALIGNED __attribute__ ((__packed__)) +#else +#define BYTE_ALIGNED +#endif + +#define LOW_BYTE(x) ((unsigned char)x) +#define HIGH_BYTE(x) ((unsigned char)(x>>8)) + +typedef uint8_t Uchar; +typedef uint16_t Uint16; +typedef uint32_t Uint32; + +/*_____ S T A N D A R D R E Q U E S T S __________________________________*/ + +#define GET_STATUS 0x00 +#define GET_DEVICE 0x01 +#define CLEAR_FEATURE 0x01 /* see FEATURES below */ +#define GET_STRING 0x03 +#define SET_FEATURE 0x03 /* see FEATURES below */ +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_DESCRIPTOR 0x07 +#define GET_CONFIGURATION 0x08 +#define SET_CONFIGURATION 0x09 +#define GET_INTERFACE 0x0A +#define SET_INTERFACE 0x0B +#define SYNCH_FRAME 0x0C + +#define GET_DEVICE_DESCRIPTOR 1 +#define GET_CONFIGURATION_DESCRIPTOR 4 + +#define REQUEST_DEVICE_STATUS 0x80 +#define REQUEST_INTERFACE_STATUS 0x81 +#define REQUEST_ENDPOINT_STATUS 0x82 +#define ZERO_TYPE 0x00 +#define INTERFACE_TYPE 0x01 +#define ENDPOINT_TYPE 0x02 + +/*_____ D E S C R I P T O R T Y P E S ____________________________________*/ + +#define DEVICE 0x01 +#define CONFIGURATION 0x02 +#define STRING 0x03 +#define INTERFACE 0x04 +#define ENDPOINT 0x05 + +/* HID specific */ +#define HID 0x21 +#define REPORT 0x22 +/* *** */ + +/*_____ S T A N D A R D F E A T U R E S __________________________________*/ + +#define DEVICE_REMOTE_WAKEUP_FEATURE 0x01 +#define ENDPOINT_HALT_FEATURE 0x00 + +/*_____ D E V I C E S T A T U S ___________________________________________*/ + +#define SELF_POWERED 1 + +/*_____ D E V I C E S T A T E _____________________________________________*/ + +#define ATTACHED 0 +#define POWERED 1 +#define DEFAULT 2 +#define ADDRESSED 3 +#define CONFIGURED 4 +#define SUSPENDED 5 + +#define USB_CONFIG_BUSPOWERED 0x80 +#define USB_CONFIG_SELFPOWERED 0x40 +#define USB_CONFIG_REMOTEWAKEUP 0x20 + +/* Class specific */ +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/*_________________________________________________________ S T R U C T _____*/ +/*_____ U S B D E V I C E R E Q U E S T _________________________________*/ + +struct USB_request_st +{ + Uchar bmRequestType; /* Characteristics of the request */ + Uchar bRequest; /* Specific request */ + Uint16 wValue; + Uint16 wIndex; /* field that varies according to request */ + Uint16 wLength; /* Number of bytes to transfer if Data */ +}; + + +/*_____ U S B D E V I C E D E S C R I P T O R ___________________________*/ + +struct usb_st_device_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* DEVICE descriptor type */ + Uint16 bscUSB; /* Binay Coded Decimal Spec. release */ + Uchar bDeviceClass; /* Class code assigned by the USB */ + Uchar bDeviceSubClass; /* Sub-class code assigned by the USB */ + Uchar bDeviceProtocol; /* Protocol code assigned by the USB */ + Uchar bMaxPacketSize0; /* Max packet size for EP0 */ + Uint16 idVendor; /* Vendor ID. ATMEL = 0x03EB */ + Uint16 idProduct; /* Product ID assigned by the manufacturer */ + Uint16 bcdDevice; /* Device release number */ + Uchar iManufacturer; /* Index of manu. string descriptor */ + Uchar iProduct; /* Index of prod. string descriptor */ + Uchar iSerialNumber; /* Index of S.N. string descriptor */ + Uchar bNumConfigurations; /* Number of possible configurations */ +} BYTE_ALIGNED; + + +/*_____ U S B C O N F I G U R A T I O N D E S C R I P T O R _____________*/ + +struct usb_st_configuration_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* CONFIGURATION descriptor type */ + Uint16 wTotalLength; /* total length of data returned */ + Uchar bNumInterfaces; /* number of interfaces for this conf. */ + Uchar bConfigurationValue; /* value for SetConfiguration resquest */ + Uchar iConfiguration; /* index of string descriptor */ + Uchar bmAttibutes; /* Configuration characteristics */ + Uchar MaxPower; /* maximum power consumption */ +} BYTE_ALIGNED; + + +/*_____ U S B I N T E R F A C E D E S C R I P T O R _____________________*/ + +struct usb_st_interface_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* INTERFACE descriptor type */ + Uchar bInterfaceNumber; /* Number of interface */ + Uchar bAlternateSetting; /* value to select alternate setting */ + Uchar bNumEndpoints; /* Number of EP except EP 0 */ + Uchar bInterfaceClass; /* Class code assigned by the USB */ + Uchar bInterfaceSubClass; /* Sub-class code assigned by the USB */ + Uchar bInterfaceProtocol; /* Protocol code assigned by the USB */ + Uchar iInterface; /* Index of string descriptor */ +} BYTE_ALIGNED; + + +/*_____ U S B E N D P O I N T D E S C R I P T O R _______________________*/ + +struct usb_st_endpoint_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* ENDPOINT descriptor type */ + Uchar bEndpointAddress; /* Address of the endpoint */ + Uchar bmAttributes; /* Endpoint's attributes */ + Uint16 wMaxPacketSize; /* Maximum packet size for this EP */ + Uchar bInterval; /* Interval for polling EP in ms */ +/* Uchar bRefresh; */ +/* Uchar bSynchAddress; */ +} BYTE_ALIGNED; + + +/*_____ U S B S T R I N G D E S C R I P T O R _______________*/ + +struct usb_st_string_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* STRING descriptor type */ + Uint16 wstring[1];/* unicode characters */ +} BYTE_ALIGNED; + + +struct usb_st_language_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* STRING descriptor type */ + Uint16 wlangid[1]; /* language id */ +} BYTE_ALIGNED; + +#endif /* __USB_H__6PFTDPIMZM__ */