#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 uint16_t usb_device_status; static uint8_t 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 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; if (len < usb_setup_buffer.wLength) { ctrl_buffer.flags |= USB_BUFFER_SHORT_END; } 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); } static uint8_t error_stall = 0; void usb_error_stall() { error_stall = 1; 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 OLD_STRING_DESCR 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 { const struct usb_st_string_descriptor *descriptor; unsigned char l; 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); } #else const struct usb_st_string_descriptor *descriptor; descriptor = (struct usb_st_string_descriptor*) usb_class_get_string_descriptor(usb_setup_buffer.wIndex, LOW_BYTE(usb_setup_buffer.wValue)); if (!descriptor) { usb_error_stall(); return; } usb_send_ctrl_response((const unsigned char*)descriptor, descriptor->bLength); #endif } 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); } /* Check if any handler stalled the pipe, if so prepare for next setup */ if (error_stall) { error_stall = 0; 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; } void usb_prepend_request_handler(struct USBRequestHandlerHook *hook) { hook->next = usb_request_handler_hooks; usb_request_handler_hooks = hook; } 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); }