From c8fe27c955b05f63fefa02f9c19dbbfb47de11ee Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 2 Nov 2024 17:33:22 +0100 Subject: [PATCH] feat: implement usb devicd cdc descriptors, init, setup --- include/clocks.h | 39 +++++ include/delay.h | 28 ++++ include/usb.h | 21 +-- include/usb_device.h | 73 +++++++-- include/usb_device_cdc.h | 116 ++++++++++++++ src/clocks.c | 38 +++++ src/delay.c | 7 + src/main.c | 42 ++++- src/usb.c | 111 ++++++++++--- src/usb_device.c | 130 ++++++++++----- src/usb_device_cdc.c | 338 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 849 insertions(+), 94 deletions(-) create mode 100644 include/clocks.h create mode 100644 include/delay.h create mode 100644 include/usb_device_cdc.h create mode 100644 src/clocks.c create mode 100644 src/delay.c create mode 100644 src/usb_device_cdc.c diff --git a/include/clocks.h b/include/clocks.h new file mode 100644 index 0000000..4e56c5c --- /dev/null +++ b/include/clocks.h @@ -0,0 +1,39 @@ +#include + +#ifndef CLOCKS_H +#define CLOCKS_H + +typedef enum { + CLOCK_PLL1 = 0, + CLOCK_PLL2 = 1, + CLOCK_PLL3 = 2, +} clock_pll_t; + +typedef enum { + CLOCK_SOURCE_HSI = 0, + CLOCK_SOURCE_HSE = 1, + CLOCK_SOURCE_CSI = 2, + CLOCK_SOURCE_PLL_1_P_CK = 3, +} sysclock_source_t; + +typedef enum { + PLL_SOURCE_HSI = 0, + PLL_SOURCE_CSI = 1, + PLL_SOURCE_HSE = 2, + PLL_SOURCE_NONE = 3, +} pll_source_t; + + +void clocks_pll_configure(clock_pll_t pll, uint8_t divm, pll_source_t source, + uint16_t divn, uint8_t divp, uint8_t divq, + uint8_t divr); +void clocks_pll_enable(clock_pll_t pll); +void clocks_pll_disable(clock_pll_t pll); +void clocks_pll_wait_ready(clock_pll_t pll, uint16_t timeout_us); + +void clocks_system_clock_source(sysclock_source_t source, uint8_t d1cpre, + uint8_t d1ppre, uint8_t hpre, + uint16_t timeout_us); +uint8_t clocks_system_clock_wait_ready(uint16_t timeout_us); + +#endif // CLOCKS_H diff --git a/include/delay.h b/include/delay.h new file mode 100644 index 0000000..e117504 --- /dev/null +++ b/include/delay.h @@ -0,0 +1,28 @@ +#ifndef DELAY_H +#define DELAY_H + +// TODO: define system clock well +/* #define SYSTEM_CLOCK ((uint32_t)360000000UL) */ +#define SYSTEM_CLOCK ((uint32_t)64000000UL) + +#define SYSTICK_CALIB 0x3E8 +#define SYSTICK_LOAD (SYSTEM_CLOCK/1000000UL) +#define SYSTICK_DELAY_CALIB (SYSTICK_LOAD >> 1) + +#define DELAY_US(us) \ + do { \ + uint32_t start = SysTick->VAL; \ + uint32_t ticks = (us * SYSTICK_LOAD)-SYSTICK_DELAY_CALIB; \ + while((start - SysTick->VAL) < ticks); \ + } while (0) + +#define DELAY_MS(ms) \ + do { \ + for (uint32_t i = 0; i < ms; ++i) { \ + DELAY_US(1000); \ + } \ + } while (0) + +void systick_configure(); + +#endif // DELAY_H diff --git a/include/usb.h b/include/usb.h index 308173b..7619276 100644 --- a/include/usb.h +++ b/include/usb.h @@ -225,18 +225,13 @@ typedef struct { // NOTE: End of structs from usb specification here -typedef struct __attribute__((packed)) { - usb_device_descriptor_t device_descriptor; - usb_device_qualifier_t device_qualifier; - // NOTE: keep these three fields in this order! - usb_configuration_descriptor_t configuration_descriptor; - usb_interface_descriptor_t interface_descriptor; - usb_endpoint_descriptor_t endpoint_descriptors[8]; - // - usb_string_descriptor_zero_t string_descriptor_zero; - usb_unicode_string_descriptor_t *string_descriptors; -} usb_class_t; +void usb_generic_fill_fifo_words(USB_OTG_INEndpointTypeDef *endpoint, + uint8_t *data, + uint16_t size, + volatile uint32_t *fifo_tx_target, + uint32_t *sub_word_data, + uint8_t *sub_word_bytes); void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, @@ -252,10 +247,6 @@ void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, volatile uint32_t *fifo_tx_target); void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor, volatile uint32_t *fifo_tx_target); -/* void usb_send_ack(uint32_t* fifo_tx_target); */ -void usb_send_configuration_descriptor(USB_OTG_INEndpointTypeDef* endpoint, - usb_class_t *class, - volatile uint32_t *fifo_tx_target); void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_interface_descriptor_t *descriptor, volatile uint32_t *fifo_tx_target); diff --git a/include/usb_device.h b/include/usb_device.h index ba8faae..a8cd3d8 100644 --- a/include/usb_device.h +++ b/include/usb_device.h @@ -23,7 +23,56 @@ typedef struct { volatile uint32_t data[128]; } usb_fifo_t; +struct usb_class_header_t; +struct usb_device_t; +typedef struct usb_device_t usb_device_t; + +typedef struct { + struct usb_class_header_t *(*init)(usb_device_t *device, + uint16_t id_vendor, uint16_t id_product, + char *vendor_name, char *product_name, + uint16_t serial_number, char* serial_name); + void (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd); + void (*setup_endpoints)(usb_device_t* device, uint8_t configuration); + void (*reset_endpoints)(usb_device_t* device); + + void (*reset_callback)(usb_device_t* device); + void (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd); + void (*enumeration_done_callback)(usb_device_t* device); + void (*txfifo_empty_callback)(usb_device_t* device, uint8_t endpoint); + void (*rxfifo_empty_callback)(usb_device_t* device, uint8_t endpoint); + void (*transmit_done_callback)(usb_device_t* device, uint8_t endpoint); + void (*nak_callback)(usb_device_t* device, uint8_t endpoint); + void (*nyet_callback)(usb_device_t* device, uint8_t endpoint); +} usb_class_vtable_t; + typedef struct { + usb_interface_descriptor_t interface_descriptor; + uint8_t endpoint_descriptors_count; + usb_endpoint_descriptor_t *endpoint_descriptors; +} usb_interface_t; + +struct usb_class_header_t { + usb_device_descriptor_t device_descriptor; + usb_device_qualifier_t device_qualifier; + usb_configuration_descriptor_t configuration_descriptor; + uint8_t interfaces_count; + usb_interface_t *interfaces; + usb_string_descriptor_zero_t string_descriptor_zero; + usb_unicode_string_descriptor_t *string_descriptors; +}; +typedef struct usb_class_header_t usb_class_header_t; + +typedef enum { + SETUP_STAGE_NONE, + SETUP_STAGE_RCVD_SETUP_PACKET, + SETUP_STAGE_AWAITING_RESPONSE, + SETUP_STAGE_SENDING_RESPONSE, + SETUP_STAGE_SENDING_ACK, + SETUP_STAGE_AWAITING_ACK, +} usb_setup_command_stage_t; + +struct usb_device_t { USB_OTG_GlobalTypeDef *core; USB_OTG_DeviceTypeDef *device; @@ -32,25 +81,31 @@ typedef struct { usb_fifo_t *fifos; - uint8_t endpoint_count; - - usb_class_t class; + usb_class_vtable_t vt; + usb_class_header_t* class; usb_device_state_t state; // TODO: is this count field required? uint8_t received_setup_commands_count; uint8_t received_setup_commands_index; + usb_setup_command_stage_t setup_stage; + uint8_t detected_setup_errors; usb_setup_command_t received_setup_commands[3]; -} usb_device_t; +}; // has configuration etc -#define USB_DEVICE_SIZE sizeof(usb_device_t) - -extern usb_device_t* usb1_device; - -void* usb_device_init(void* peripheral_address, usb_class_t* class, void* buffer); +typedef enum { + USB_OTG_HS1 = 0, + USB_OTG_FS2 = 1, +} usb_device_slot_t; +extern usb_device_t usb_devices[2]; + +void *usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class, + uint16_t id_vendor, uint16_t id_product, + char *vendor_name, char *product_name, + uint16_t serial_number, char* serial_name); void usb_device_setup(void* device); void usb_device_wait_for_handshake(void* device); diff --git a/include/usb_device_cdc.h b/include/usb_device_cdc.h new file mode 100644 index 0000000..81c4a94 --- /dev/null +++ b/include/usb_device_cdc.h @@ -0,0 +1,116 @@ +#include "usb_device.h" + +#ifndef USB_DEVICE_CDC_H +#define USB_DEVICE_CDC_H + +#define USB_CLASS_CDC_CODE 0x02 +#define USB_CLASS_DATA_CODE 0x0A +#define USB_SUBCLASS_CDC_ACM_CODE 0x02 + +extern usb_class_vtable_t USB_CLASS_CDC_ACM; + +typedef enum { + SEND_ENCAPSULATED_COMMAND = 0, + GET_ENCAPSULATED_RESPONSE = 1, + /* SET_COMM_FEATURE = 2, */ + /* GET_COMM_FEATURE = 3, */ + /* CLEAR_COMM_FEATURE = 4, */ + /* SET_AUX_LINE_STATE = 0x10, */ + /* SET_HOOK_STATE = 0x11, */ + /* PULSE_SETUP = 0x12, */ + /* SEND_PULSE = 0x13, */ + /* SET_PULSE_TIME = 0x14, */ + /* RING_AUX_JACK = 0x15, */ + /* SET_LINE_CODING = 0x20, */ + /* GET_LINE_CODING = 0x21, */ +} usb_cdc_request_code_type_t; + +typedef enum { + CS_INTERFACE = 0x24, + CS_ENDPOINT = 0x25 +} usb_cdc_functional_descriptor_type_t; + +typedef enum { + HEADER_FUNCTIONAL_DESCRIPTOR_FUNCTIONAL_DESCRIPTOR = 0x00, + CALL_MANAGEMENT_FUNCTIONAL_FUNCTIONAL_DESCRIPTOR = 0x01, + ABSTRACT_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x02, + DIRECT_LINE_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x03, + TELEPHONE_RINGER_FUNCTIONAL_DESCRIPTOR = 0x04, + TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPABILITIES_FUNCTIONAL_DESCRIPTOR = 0x05, + UNION_FUNCTIONAL_DESCRIPTOR = 0x06, + CONTRY_SELECTION_FUNCTIONAL_DESCRIPTOR = 0x07, + TELEPHONE_OPERATIONAL_MODES_FUNCTIONAL_DESCRIPTOR = 0x08, + USB_TERMINAL_FUNCTIONAL_DESCRIPTOR = 0x09, + NETWORK_CHANNEL_FUNCTIONAL_DESCRIPTOR = 0x0A, + PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0x0B, + EXTENSION_UNIT_FUNCTIONAL_DESCRIPTOR = 0x0C, + MULTI_CHANNEL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x0D, + CAPI_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR = 0x0E, + ETHERNET_NETWORKING_FUNCTIONAL_DESCRIPTOR = 0x0F, + ATM_NETWORKING_FUNCTIONAL_DESCRIPTOR = 0x10, + WIRELESS_HANDSET_CONTROL_MODEL_FUNCTIONAL_DESCRIPTOR = 0x11, +} usb_cdc_functional_descriptor_subtype_t; + +#pragma GCC diagnostic error "-Wpadded" +typedef struct __attribute__((packed)) { + uint8_t bFunctionLength; + usb_cdc_functional_descriptor_type_t bDescriptorType; + usb_cdc_functional_descriptor_subtype_t bDescriptorSubType; +} usb_cdc_functional_descriptor_header_t; +_Static_assert(sizeof(usb_cdc_functional_descriptor_header_t) == 3, "Size check"); + +typedef struct __attribute__((packed)) { + usb_cdc_functional_descriptor_header_t header; + uint16_t bcdCDC; +} usb_cdc_header_functional_decriptor_t; +_Static_assert(sizeof(usb_cdc_header_functional_decriptor_t) == 5, "Size check"); + +typedef struct { + usb_cdc_functional_descriptor_header_t header; + uint8_t bmCapabilities; +} usb_cdc_acm_functional_decriptor_t; +_Static_assert(sizeof(usb_cdc_acm_functional_decriptor_t) == 4, "Size check"); + +typedef struct { + usb_cdc_functional_descriptor_header_t header; + uint8_t bControlInterface; + uint8_t bSubordinateInterface0; +} usb_cdc_union_functional_decriptor_t; +_Static_assert(sizeof(usb_cdc_union_functional_decriptor_t) == 5, "Size check"); + +typedef struct { + usb_cdc_functional_descriptor_header_t header; + uint8_t bmCapabilities; + uint8_t bDataInterface; +} usb_cdc_call_management_functional_decriptor_t; +_Static_assert(sizeof(usb_cdc_call_management_functional_decriptor_t) == 5, "Size check"); + +#pragma GCC diagnostic ignored "-Wpadded" +typedef struct { + usb_class_header_t header; + uint8_t functional_descriptors_count; + usb_cdc_functional_descriptor_header_t** functional_descriptors; +} usb_device_cdc_t; + +usb_class_header_t* usb_device_cdc_init(usb_device_t *device, + uint16_t id_vendor, uint16_t id_product, + char *vendor_name, char *product_name, + uint16_t serial_number, char* serial_name); + +void usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd); + +void usb_device_cdc_setup_packet_callback(usb_device_t* device, usb_setup_command_t* cmd); +void usb_device_cdc_enumeration_done_callback(usb_device_t* device); +void usb_device_cdc_txfifo_empty_callback(usb_device_t* device, uint8_t endpoint); +void usb_device_cdc_rxfifo_empty_callback(usb_device_t* device, uint8_t endpoint); +void usb_device_cdc_transmit_done_callback(usb_device_t* device, uint8_t endpoint); +void usb_device_cdc_nak_callback(usb_device_t* device, uint8_t endpoint); +void usb_device_cdc_nyet_callback(usb_device_t* device, uint8_t endpoint); + +void usb_device_cdc_acm_configure(usb_device_t *device // , TODO + ); + + + + +#endif // USB_DEVICE_CDC_H diff --git a/src/clocks.c b/src/clocks.c new file mode 100644 index 0000000..44fe526 --- /dev/null +++ b/src/clocks.c @@ -0,0 +1,38 @@ +#include "clocks.h" +#include "registers.h" +#include + +void clocks_pll_configure(clock_pll_t pll, uint8_t divm, pll_source_t source, + uint16_t divn, uint8_t divp, uint8_t divq, + uint8_t divr) { + clocks_pll_disable(pll); + + reg_write_bits_pos(&RCC->PLLCKSELR, divm, RCC_PLLCKSELR_DIVM1_Pos + ((pll << 1) << 3), 0x3F); + reg_write_bits_pos(&RCC->PLLCKSELR, source, RCC_PLLCKSELR_PLLSRC_Pos, 0x3); + + reg_write_bits(&RCC->PLL1DIVR + (pll << 1), + (((divr - 1) & 0x7F) << RCC_PLL1DIVR_R1_Pos) | + (((divq - 1) & 0x7F) << RCC_PLL1DIVR_Q1_Pos) | + (((divp - 1) & 0x7F) << RCC_PLL1DIVR_P1_Pos) | + (((divn - 1) & 0x1FF) << RCC_PLL1DIVR_N1_Pos), + 0x7F7FFFFF); +} +void clocks_pll_enable(clock_pll_t pll) { + reg_set_bits(&RCC->CR, RCC_CR_PLL1ON << (uint8_t)(pll << 1)); +} +void clocks_pll_disable(clock_pll_t pll) { + reg_clear_bits(&RCC->CR, RCC_CR_PLL1ON << ((uint8_t)(pll << 1))); +} +void clocks_pll_wait_ready(clock_pll_t pll, uint16_t timeout_us) { + while (reg_read_bits_pos(&RCC->CR, RCC_CR_PLL1ON_Pos + (uint8_t)(pll << 1) + 1, 1) == 0); +} + +void clocks_system_clock_source(sysclock_source_t source, uint8_t d1cpre, uint8_t d1ppre, uint8_t hpre, uint16_t timeout_us) { + reg_write_bits(&RCC->D1CFGR, + ((d1cpre & 0xF) << RCC_D1CFGR_D1CPRE_Pos) | + ((d1ppre & 0x7) << RCC_D1CFGR_D1PPRE_Pos) | + ((hpre & 0xF) << RCC_D1CFGR_HPRE_Pos), + RCC_D1CFGR_D1CPRE | RCC_D1CFGR_D1PPRE | RCC_D1CFGR_HPRE); + reg_write_bits(&RCC->CFGR, source, RCC_CFGR_SW); + while (reg_read_bits_pos(&RCC->CFGR, RCC_CFGR_SWS_Pos, 0x7) != source); +} diff --git a/src/delay.c b/src/delay.c new file mode 100644 index 0000000..794223d --- /dev/null +++ b/src/delay.c @@ -0,0 +1,7 @@ +#include "delay.h" +#include + +void systick_configure() { + SysTick->LOAD = SYSTICK_LOAD; + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; +} diff --git a/src/main.c b/src/main.c index b07bf5e..ffa9e91 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,10 @@ #include #include #include +#include "delay.h" #include "usb_device.h" +#include "usb_device_cdc.h" +#include "clocks.h" #include "exti.h" #include "registers.h" #include "pin.h" @@ -55,24 +58,45 @@ void exti15_10_handler(void) void main() { - // The hsi is by default on 64 MHz. - // TODO: plls? configure dividers (note the need of refclk being between 1 and - // 16 MHz, the hsi can stay at 64 MHz, and prescaler DIVM can be given to account for that) - // enable the pll1, wait for it to be ready, and - // switch the sys clock to pll1. - // Is this necessary for something though? + systick_configure(); // Clocks section // Enable hsi48 for usb RCC->CR |= RCC_CR_HSI48ON; while ((RCC->CR & RCC_CR_HSI48RDY) == 0); + // TODO: pll, system clock switch... too complicated it seems. + // Getting hard faults, apparently because of too low voltage + // Has to be enabled before vco can be changed + /* PWR->CR3 |= PWR_CR3_LDOEN; */ + /* PWR->CR3 &= ~PWR_CR3_LDOEN; */ + /* reg_write_bits_pos(&PWR->D3CR, 2, PWR_D3CR_VOS_Pos, 3); */ + + /* /\* SYSCFG->PWRCR |= SYSCFG_PWRCR_ODEN; *\/ */ + + /* while((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0); */ + /* /\* while((PWR->D3CR & PWR_D3CR_VOSRDY) == 0); *\/ */ + + /* reg_write_bits(&FLASH->ACR, FLASH_ACR_LATENCY_3WS, FLASH_ACR_LATENCY_Msk); */ + + /* // HSI is 64 MHz, not divided */ + /* // Diving by 32 to put to PLL -> 2 MHz */ + /* // DIVN = 360 -> F_VCO = 720 MHz */ + /* // DIVP = 2 -> pll1_p is 360 MHz */ + /* // DIVQ = 8 -> pll1_q is 90 MHz */ + /* // DIVR = 8 -> pll1_r is 90 MHz */ + /* clocks_pll_configure(CLOCK_PLL1, 32, PLL_SOURCE_HSI, */ + /* 360, 2, 8, 8); */ + /* clocks_pll_enable(CLOCK_PLL1); */ + /* clocks_pll_wait_ready(CLOCK_PLL1, 300); */ + /* clocks_system_clock_source(CLOCK_SOURCE_PLL_1_P_CK, */ + /* 1, 1, 2, 300); */ + // Clock gating RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; volatile uint32_t dummy; dummy = RCC->APB4ENR; dummy = RCC->APB4ENR; - RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIOHEN | RCC_AHB4ENR_GPIOIEN | RCC_AHB4ENR_GPIOJEN; dummy = RCC->AHB4ENR; dummy = RCC->AHB4ENR; @@ -131,7 +155,9 @@ void main() // TODO: ? pin_into_input_highspeed(otg_hs_overcurrent); - void* usb_otg = usb_device_init(USB1_OTG_HS, NULL, NULL); + void *usb_otg = usb_device_init(USB_OTG_HS1, &USB_CLASS_CDC_ACM, + 0x1234, 0x1111, "Frantisek Bohacek", + "Display", 1, NULL); usb_device_setup(usb_otg); usb_device_wait_for_handshake(usb_otg); diff --git a/src/usb.c b/src/usb.c index 6b566d9..be8492d 100644 --- a/src/usb.c +++ b/src/usb.c @@ -8,6 +8,70 @@ typedef union { uint8_t bytes[4]; } usb_data_t; +uint32_t get_mask(uint8_t byte_count) { + switch (byte_count) { + case 0: + return 0; + case 1: + return 0xFF; + case 2: + return 0xFFFF; + case 3: + return 0xFFFFFF; + default: + return ~0; + } +} + +void usb_generic_fill_fifo_words(USB_OTG_INEndpointTypeDef *endpoint, + uint8_t *data, + uint16_t size, + volatile uint32_t *fifo_tx_target, + uint32_t *sub_word_data, + uint8_t *sub_word_bytes) { + usb_data_t tx_data; + uint8_t total_bytes = size + *sub_word_bytes; + if (total_bytes < 4) { + uint8_t bytes = *sub_word_bytes; + *sub_word_bytes = total_bytes; + + uint32_t mask = get_mask(bytes); + tx_data.word = *sub_word_data & mask; + + for (int i = 0; i < size; i++) { + tx_data.bytes[i + bytes] = *(data++); + } + + *sub_word_bytes = tx_data.word; + return; + } + + if (*sub_word_bytes > 0) { + // first send these bytes, + uint8_t skip_bytes = *sub_word_bytes; + tx_data.word = *sub_word_data; + for (int i = 0; i < 4 - *sub_word_bytes; i++) { + tx_data.bytes[skip_bytes + i] = *(data++); + size--; + } + + *fifo_tx_target = tx_data.word; + } + + uint8_t subWordBytes = size % 4; + uint8_t wordCount = size / 4; + + for (uint8_t i = 0; i < wordCount; i++) { + tx_data.word = *((uint32_t*)data); + *fifo_tx_target = tx_data.word; + data += 4; + } + + // NOTE: hm. This is not generally safe, but it's tempting... + *sub_word_data = *((uint32_t*)data); + *sub_word_bytes = subWordBytes; +} + void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, @@ -18,7 +82,7 @@ void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, } // TODO: generic max packet size - uint16_t packet_count = size + 63 / 64; + uint16_t packet_count = (size + 63) / 64; endpoint->DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos); @@ -70,29 +134,30 @@ void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_generic_send(endpoint, (uint8_t*)descriptor, descriptor->bLength, fifo_tx_target); } -void usb_send_configuration_descriptor(USB_OTG_INEndpointTypeDef* endpoint, - usb_class_t *class, - volatile uint32_t *fifo_tx_target) { - class->configuration_descriptor.header.bDescriptorType = DESCRIPTOR_CONFIGURATION; - class->configuration_descriptor.header.bLength = sizeof(usb_configuration_descriptor_t); - class->interface_descriptor.header.bDescriptorType = DESCRIPTOR_INTERFACE; - class->interface_descriptor.header.bLength = sizeof(usb_interface_descriptor_t); - uint16_t total_length = - sizeof(usb_configuration_descriptor_t) + - sizeof(usb_interface_descriptor_t) + - class->interface_descriptor.bNumEndpoints * sizeof(usb_endpoint_descriptor_t); - - for (int i = 0; i < class->interface_descriptor.bNumEndpoints; i++) { - class->endpoint_descriptors[i].header.bDescriptorType = DESCRIPTOR_ENDPOINT; - class->endpoint_descriptors[i].header.bLength = sizeof(usb_endpoint_descriptor_t); - } +/* void usb_send_configuration_descriptor(USB_OTG_INEndpointTypeDef* endpoint, */ +/* usb_class_t *class, */ +/* volatile uint32_t *fifo_tx_target) { */ +/* class->configuration_descriptor.header.bDescriptorType = DESCRIPTOR_CONFIGURATION; */ +/* class->configuration_descriptor.header.bLength = sizeof(usb_configuration_descriptor_t); */ +/* class->interface_descriptor.header.bDescriptorType = DESCRIPTOR_INTERFACE; */ +/* class->interface_descriptor.header.bLength = sizeof(usb_interface_descriptor_t); */ +/* uint16_t total_length = */ +/* sizeof(usb_configuration_descriptor_t) + */ +/* sizeof(usb_interface_descriptor_t) + */ +/* class->interface_descriptor.bNumEndpoints * sizeof(usb_endpoint_descriptor_t); */ + +/* for (int i = 0; i < class->interface_descriptor.bNumEndpoints; i++) { */ +/* class->endpoint_descriptors[i].header.bDescriptorType = DESCRIPTOR_ENDPOINT; */ +/* class->endpoint_descriptors[i].header.bLength = sizeof(usb_endpoint_descriptor_t); */ +/* } */ + +/* // NOTE: since the memory layout is: configuration, interface, endpoints, */ +/* // we can send directly like this. */ +/* usb_generic_send(endpoint, */ +/* (uint8_t*)&class->configuration_descriptor, */ +/* total_length, fifo_tx_target); */ +/* } */ - // NOTE: since the memory layout is: configuration, interface, endpoints, - // we can send directly like this. - usb_generic_send(endpoint, - (uint8_t*)&class->configuration_descriptor, - total_length, fifo_tx_target); -} void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_device_descriptor_t *descriptor, volatile uint32_t *fifo_tx_target) { diff --git a/src/usb_device.c b/src/usb_device.c index 09b18c5..6324677 100644 --- a/src/usb_device.c +++ b/src/usb_device.c @@ -8,28 +8,31 @@ /* USB_OTG_GlobalTypeDef */ -usb_device_t* usb1_device; +usb_device_t usb_devices[2]; +void* usb_periph_addresses[] = +{ (void*)USB1_OTG_HS, (void*)USB2_OTG_FS }; -void* usb_device_init(void* peripheral_address, usb_class_t* class, void* buffer) { - if (buffer == NULL) { - buffer = (void*)malloc(sizeof(usb_device_t)); - } - - usb_device_t* device = (usb_device_t*)buffer; +void* usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class, + uint16_t id_vendor, uint16_t id_product, + char *vendor_name, char *product_name, + uint16_t serial_number, char* serial_name) { + usb_device_t* device = &usb_devices[slot]; + void* peripheral_address = usb_periph_addresses[slot]; device->state = INIT; + device->detected_setup_errors = 0; device->core = peripheral_address + USB_OTG_GLOBAL_BASE; device->device = peripheral_address + USB_OTG_DEVICE_BASE; device->out = peripheral_address + USB_OTG_OUT_ENDPOINT_BASE; device->in = peripheral_address + USB_OTG_IN_ENDPOINT_BASE; device->fifos = (usb_fifo_t*)(((uint8_t*)device->core) + USB_OTG_FIFO_BASE); - device->endpoint_count = 8; device->received_setup_commands_count = 0; device->received_setup_commands_index = 0; - device->class = *class; + device->vt = *class; - // TODO: clarify how this should work... - usb1_device = device; + device->class = device-> + vt.init(device, id_vendor, id_product, + vendor_name, product_name, serial_number, serial_name); return device; } @@ -143,6 +146,10 @@ typedef struct { } packet_info_t; void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { + if (device->setup_stage != SETUP_STAGE_RCVD_SETUP_PACKET) { + device->detected_setup_errors++; + } + switch (cmd->bRequest) { case USB_SETUP_GET_STATUS: { uint8_t size; @@ -177,6 +184,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { usb_generic_send(device->in, packet, size, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; } break; case USB_SETUP_GET_DESCRIPTOR: { @@ -185,52 +193,59 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { switch (descriptor_type) { case DESCRIPTOR_DEVICE: - usb_send_device_descriptor(device->in, &device->class.device_descriptor, &device->fifos[0].data[0]); + usb_send_device_descriptor(device->in, &device->class->device_descriptor, &device->fifos[0].data[0]); break; case DESCRIPTOR_CONFIGURATION: - usb_send_configuration_descriptor(device->in, &device->class.configuration_descriptor, &device->fifos[0].data[0]); + device->vt.send_configuration(device, cmd); break; case DESCRIPTOR_STRING: { if (descriptor_index == 0) { - usb_send_string_descriptor_zero(device->in, &device->class.string_descriptor_zero, &device->fifos[0].data[0]); + usb_send_string_descriptor_zero(device->in, &device->class->string_descriptor_zero, &device->fifos[0].data[0]); } else { uint8_t index = descriptor_index - 1; // NOTE: the user could potentially read different memory part!! // This has to be fixed, the length has to be stored somewhere. - usb_send_unicode_string_descriptor(device->in, &device->class.string_descriptors[index], &device->fifos[0].data[0]); + usb_send_unicode_string_descriptor(device->in, &device->class->string_descriptors[index], &device->fifos[0].data[0]); } } break; case DESCRIPTOR_INTERFACE: - usb_send_interface_descriptor(device->in, &device->class.interface_descriptor, &device->fifos[0].data[0]); + usb_send_interface_descriptor(device->in, &device->class->interfaces[descriptor_index].interface_descriptor, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; break; case DESCRIPTOR_ENDPOINT: - usb_send_endpoint_descriptor(device->in, &device->class.endpoint_descriptors[descriptor_index], &device->fifos[0].data[0]); + // TODO: how to match the interface to the descriptor index? + usb_send_endpoint_descriptor(device->in, &device->class->interfaces[0].endpoint_descriptors[descriptor_index], &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; break; case DESCRIPTOR_DEVICE_QUALIFIER: - usb_send_device_qualifier_descriptor(device->in, &device->class.device_qualifier, &device->fifos[0].data[0]); + usb_send_device_qualifier_descriptor(device->in, &device->class->device_qualifier, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; break; case DESCRIPTOR_OTHER_SPEED_CONFIGURATION: case DESCRIPTOR_INTERFACE_POWER: reg_set_bits(&device->out[0].DOEPCTL, USB_OTG_DOEPCTL_STALL); + device->setup_stage = SETUP_STAGE_NONE; break; } // TODO } break; case USB_SETUP_GET_CONFIGURATION: { - uint8_t value = device->class.configuration_descriptor.bConfigurationValue; + uint8_t value = device->class->configuration_descriptor.bConfigurationValue; if (device->state != ENUMERATED) { value = 0; } usb_generic_send(device->in, &value, sizeof(value), &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; } break; case USB_SETUP_GET_INTERFACE: { usb_generic_send(device->in, - &device->class.interface_descriptor.bAlternateSetting, + &device->class->interfaces[cmd->wIndex].interface_descriptor.bAlternateSetting, sizeof(uint8_t), &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; } break; case USB_SETUP_SET_ADDRESS: @@ -239,6 +254,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { USB_OTG_DCFG_DAD_Pos, 0x7F); usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_ACK; device->state = SET_ADDRESS_RCVD; break; case USB_SETUP_SET_CONFIGURATION: { @@ -247,9 +263,10 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); // TODO disable endpoints - } else if (cmd->wValue == device->class.configuration_descriptor.bConfigurationValue) { + } else if (cmd->wValue == device->class->configuration_descriptor.bConfigurationValue) { device->state = ENUMERATED; usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_ACK; // TODO setup endpoints } else { @@ -272,6 +289,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { } usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_ACK; break; case USB_SETUP_SET_FEATURE: switch (cmd->wValue) { @@ -288,6 +306,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { } usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + device->setup_stage = SETUP_STAGE_SENDING_ACK; break; case USB_SETUP_SET_DESCRIPTOR: case USB_SETUP_SET_INTERFACE: @@ -297,7 +316,8 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { break; case RESERVED1: case RESERVED2: - device->state = UNKNOWN_CONTROL_COMMAND; + default: + device->vt.setup_packet_callback(device, cmd); break; } } @@ -315,10 +335,11 @@ void usb_handle_rxflvl_control_int(usb_device_t *device, uint8_t setup_packets_count = 3 - reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); usb_generic_read( (uint8_t *)&device - ->received_setup_commands[device->received_setup_commands_index++], + ->received_setup_commands[device->received_setup_commands_count++], 8, fifo); - device->received_setup_commands_index %= 3; + device->received_setup_commands_count %= 3; + device->setup_stage = SETUP_STAGE_RCVD_SETUP_PACKET; if (setup_packets_count == 0) { reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); @@ -327,6 +348,13 @@ void usb_handle_rxflvl_control_int(usb_device_t *device, dummy = *fifo; // the last that will trigger another interrupt } else if (packet_info->byte_count != 0) { usb_generic_read(data, packet_info->byte_count, fifo); + + if (device->setup_stage == SETUP_STAGE_AWAITING_ACK) { + // This is an error, since there is data in status phase... + // TODO: How to handle? + device->detected_setup_errors++; + device->setup_stage = SETUP_STAGE_NONE; + } } } @@ -396,7 +424,9 @@ void usb_handle_endpoint_in_int(usb_device_t* device) { // NOTE this should not be reached as thresholding is not used reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_TXFIFOUDRN); } else if (interrupt_reg & USB_OTG_DIEPINT_TXFE) { - // Now we can send more data. TODO notify application about this + if (device->state == ENUMERATED) { + device->vt.txfifo_empty_callback(device, ep_id); + } reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_TXFE); } else if (interrupt_reg & USB_OTG_DIEPINT_INEPNE) { // NAK effective. Okay, ack, go on. @@ -418,7 +448,18 @@ void usb_handle_endpoint_in_int(usb_device_t* device) { // Endpoint is disabled, per application's request. Okay. reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_EPDISD); } else if (interrupt_reg & USB_OTG_DIEPINT_XFRC) { - // Transfer is completed. TODO notify application? + // Transfer is completed. + if (device->state == ENUMERATED) { + device->vt.transmit_done_callback(device, ep_id); + } + + if (ep_id == 0) { + if (device->setup_stage == SETUP_STAGE_SENDING_RESPONSE) { + device->setup_stage = SETUP_STAGE_NONE; + } else if (device->setup_stage == SETUP_STAGE_SENDING_ACK) { + device->setup_stage = SETUP_STAGE_AWAITING_ACK; + } + } reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_XFRC); } } @@ -445,11 +486,11 @@ void usb_handle_endpoint_out_int(usb_device_t* device) { reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_STPKTRX); } else if (interrupt_reg & USB_OTG_DOEPINT_NYET) { // We don't really care about this one for now - // TODO: for future - trigger a callback to application + device->vt.nyet_callback(device, ep_id); reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_NYET); } else if (interrupt_reg & USB_OTG_DOEPINT_NAK) { // We don't really care about this one for now - // TODO: for future - trigger a callback to application + device->vt.nak_callback(device, ep_id); reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_NAK); } else if (interrupt_reg & USB_OTG_DOEPINT_BERR) { // Uh? Babble much? @@ -466,22 +507,30 @@ void usb_handle_endpoint_out_int(usb_device_t* device) { device->state = CONTROL_ERROR; reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_B2BSTUP); } else if (interrupt_reg & USB_OTG_DOEPINT_OTEPSPR) { - // TODO: ack or stall the status phase? How? + // This is valid only for DATA phases where the host sends us data. + // Those are not supported yet! After they are, here is the place to + // set STALL or send zero length packet. + // TODO: ack or stall the status phase? reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_OTEPSPR); } else if (interrupt_reg & USB_OTG_DOEPINT_OTEPDIS) { // NOTE: Can we handle this? a callback to application? reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_OTEPDIS); } else if (interrupt_reg & USB_OTG_DOEPINT_STUP) { - // handle all setup commands - uint8_t setup_packets_count = 3 - reg_read_bits_pos(&device->out[ep_id].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); - usb_handle_setup(device, &device->received_setup_commands[0]); - for (int i = 0; i < device->received_setup_commands_count; i++) { + if (device->setup_stage != SETUP_STAGE_NONE) { + // something went wrong. Let's continue, but this isn't looking good. + device->detected_setup_errors++; } - device->received_setup_commands_count = 0; - // TODO: null the data? shouldn't be needed... + uint8_t setup_packets_count = 3 - reg_read_bits_pos(&device->out[ep_id].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); + usb_handle_setup(device, &device->received_setup_commands[device->received_setup_commands_index]); + + device->received_setup_commands_index++; + device->received_setup_commands_index %= 3; + + if (setup_packets_count == 0) { + reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); + } - // 3 packets to receive as setup reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_STUP); } else if (interrupt_reg & USB_OTG_DOEPINT_AHBERR) { // NOTE This shoudln't be reached since DMA is not used @@ -491,6 +540,9 @@ void usb_handle_endpoint_out_int(usb_device_t* device) { // need handling? reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_EPDISD); } else if (interrupt_reg & USB_OTG_DOEPINT_XFRC) { + if (ep_id == 0 && device->setup_stage == SETUP_STAGE_AWAITING_ACK) { + device->setup_stage = SETUP_STAGE_NONE; + } // Transfer has been completed // TODO: handle data? - callback to device data handle? reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_XFRC); @@ -500,7 +552,7 @@ void usb_handle_endpoint_out_int(usb_device_t* device) { // NOTE: this is irq handler void otg_hs_handler(void) { - usb_device_t* device = usb1_device; + usb_device_t* device = &usb_devices[USB_OTG_HS1]; // Reset detected if (device->core->GINTSTS & USB_OTG_GINTSTS_USBRST) { @@ -508,7 +560,7 @@ void otg_hs_handler(void) { reg_set_bits(&device->device->DOEPMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT); // TODO: should this be done for ep 0 as well? - for (int ep = 0; ep < device->endpoint_count; ep++) { + for (int ep = 0; ep < 8; ep++) { USB_OTG_OUTEndpointTypeDef *out = device->out + ep; out->DOEPCTL |= USB_OTG_DOEPCTL_SNAK; } @@ -521,7 +573,7 @@ void otg_hs_handler(void) { // 512 bytes device->core->GRXFSIZ = 256 / 4; // 64 bytes, beginning of ram - device->core->DIEPTXF[0] = (0) << USB_OTG_DIEPTXF_INEPTXSA_Pos | (64 / 4) << USB_OTG_DIEPTXF_INEPTXFD_Pos; + device->core->DIEPTXF[0] = (0) << USB_OTG_TX0FSA_Pos | (64 / 4) << USB_OTG_TX0FD_Pos; // 3 packets to receive as setup reg_write_bits_pos(&device->out->DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); diff --git a/src/usb_device_cdc.c b/src/usb_device_cdc.c new file mode 100644 index 0000000..11e0611 --- /dev/null +++ b/src/usb_device_cdc.c @@ -0,0 +1,338 @@ +#include "usb.h" +#include "usb_device.h" +#include "usb_device_cdc.h" +#include +#include +#include + +uint16_t usb_cdc_lang_descriptors[] = { + USB_LANG_ENGLISH | (USB_SUBLANG_ENGLISH_US << 10) +}; + +usb_class_vtable_t USB_CLASS_CDC_ACM = { + .init = usb_device_cdc_init, + .send_configuration = usb_device_cdc_send_configuration, + .setup_packet_callback = usb_device_cdc_setup_packet_callback, + .enumeration_done_callback = usb_device_cdc_enumeration_done_callback, + .txfifo_empty_callback = usb_device_cdc_txfifo_empty_callback, + .rxfifo_empty_callback = usb_device_cdc_rxfifo_empty_callback, + .transmit_done_callback = usb_device_cdc_transmit_done_callback, + .nak_callback = usb_device_cdc_nak_callback, + .nyet_callback = usb_device_cdc_nyet_callback, +}; + +usb_class_header_t *usb_device_cdc_init(usb_device_t *device, uint16_t id_vendor, + uint16_t id_product, char *vendor_name, + char *product_name, uint16_t serial_number, + char *serial_name) { + usb_class_header_t* class = (usb_class_header_t*)calloc(1, sizeof(usb_device_cdc_t)); + + class->string_descriptor_zero.wLANGID = usb_cdc_lang_descriptors; + + uint8_t string_count = 0; + + if (vendor_name != NULL) { + string_count++; + class->device_descriptor.iManufacturer = string_count; + } + if (product_name != NULL) { + string_count++; + class->device_descriptor.iProduct = string_count; + } + if (serial_name != NULL) { + string_count++; + class->device_descriptor.iSerialNumber = string_count; + } + + usb_unicode_string_descriptor_t *descriptor = + (usb_unicode_string_descriptor_t*)malloc(sizeof(usb_unicode_string_descriptor_t) * string_count); + class->string_descriptors = descriptor; + + if (vendor_name != NULL) { + descriptor->header.bDescriptorType = DESCRIPTOR_STRING; + descriptor->header.bLength = 4 + strlen(vendor_name); + descriptor->bString = (uint8_t*)vendor_name; + + descriptor++; + } + if (product_name != NULL) { + descriptor->header.bDescriptorType = DESCRIPTOR_STRING; + descriptor->header.bLength = 4 + strlen(product_name); + descriptor->bString = (uint8_t*)product_name; + descriptor++; + } + if (serial_name != NULL) { + descriptor->header.bDescriptorType = DESCRIPTOR_STRING; + descriptor->header.bLength = 4 + strlen(serial_name); + descriptor->bString = (uint8_t*)serial_name; + descriptor++; + } + + return class; +} + +void usb_device_cdc_acm_configure(usb_device_t* device) { + usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class; + usb_class_header_t* header = &cdc->header; + + usb_device_descriptor_t device_descriptor = + { + .header = { .bDescriptorType = DESCRIPTOR_DEVICE, .bLength = sizeof(usb_device_descriptor_t) }, + .bcdUSB = 0x20, + .bDeviceClass = USB_CLASS_CDC_CODE, + .bDeviceSubClass = USB_SUBCLASS_CDC_ACM_CODE, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 64, + .idVendor = header->device_descriptor.idVendor, + .idProduct = header->device_descriptor.idProduct, + .bcdDevice = 0x0000, + .iManufacturer = header->device_descriptor.iManufacturer, + .iProduct = header->device_descriptor.iManufacturer, + .iSerialNumber = header->device_descriptor.iSerialNumber, + .bNumConfigurations = 1, + }; + header->device_descriptor = device_descriptor; + + usb_device_qualifier_t qualifier = + { + .header = { .bDescriptorType = DESCRIPTOR_DEVICE_QUALIFIER, .bLength = sizeof(usb_device_qualifier_t) }, + .bcdUSB = 0x20, + .bDeviceClass = USB_CLASS_CDC_CODE, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 0, + .bReserved = 0 + }; + header->device_qualifier = qualifier; + + usb_configuration_descriptor_t configuration_descriptor = + { + .header = { .bDescriptorType = DESCRIPTOR_CONFIGURATION, .bLength = sizeof(usb_configuration_descriptor_t) }, + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = + { + .self_powered = 0, + .remote_wakeup = 0, + .reserved1 = 1, + .reserved_zeros = 0, + }, + .bMaxPower = 50, + }; + header->configuration_descriptor = configuration_descriptor; + + header->interfaces_count = 2; + header->interfaces = malloc(2 * sizeof(usb_interface_t)); + + static usb_endpoint_descriptor_t notification_endpoint = + {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT, + .bLength = sizeof(usb_endpoint_descriptor_t)}, + .bEndpointAddress = { .endpoint_number = 1, .direction = USB_ENDPOINT_IN, .reserved = 0 }, + .bInterval = 16, + .bmAttributes = { + .reserved_zeros = 0, + .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC, + .usage_type = USB_ENDPOINT_USAGE_DATA, + .transfer_type = USB_ENDPOINT_TYPE_INTERRUPT, + }, + .wMaxPacketSize = 64, + }; + usb_interface_t communications_interface = + {.interface_descriptor = { + .header = {.bDescriptorType = DESCRIPTOR_INTERFACE, + .bLength = sizeof(usb_interface_descriptor_t)}, + .bInterfaceNumber = 0, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC_CODE, + .bInterfaceSubClass = USB_SUBCLASS_CDC_ACM_CODE, + .bInterfaceProtocol = 0, + .iInterface = 0 + }, + .endpoint_descriptors_count = 1, + // NOTE: mind here, the endpoint is same for all devices, + // so it's defined here as static variable, meaning it will + // not be deallocated when this function exits. + .endpoint_descriptors = ¬ification_endpoint, + }; + static usb_endpoint_descriptor_t data_endpoints[2] = { + {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT, + .bLength = sizeof(usb_endpoint_descriptor_t)}, + .bEndpointAddress = { .endpoint_number = 2, .direction = USB_ENDPOINT_IN, .reserved = 0 }, + .bInterval = 1, + .bmAttributes = { + .reserved_zeros = 0, + .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC, + .usage_type = USB_ENDPOINT_USAGE_DATA, + .transfer_type = USB_ENDPOINT_TYPE_BULK, + }, + .wMaxPacketSize = 64, + }, + {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT, + .bLength = sizeof(usb_endpoint_descriptor_t)}, + .bEndpointAddress = { .endpoint_number = 1, .direction = USB_ENDPOINT_OUT, .reserved = 0 }, + .bInterval = 16, + .bmAttributes = { + .reserved_zeros = 0, + .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC, + .usage_type = USB_ENDPOINT_USAGE_DATA, + .transfer_type = USB_ENDPOINT_TYPE_BULK, + }, + .wMaxPacketSize = 64, + }, + }; + usb_interface_t data_interface = { + .interface_descriptor = { + .header = {.bDescriptorType = DESCRIPTOR_INTERFACE, + .bLength = sizeof(usb_interface_descriptor_t)}, + .bInterfaceNumber = 1, + .bAlternateSetting = 2, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_DATA_CODE, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0, + .iInterface = 0 + }, + .endpoint_descriptors_count = 2, + // NOTE: mind here, the endpoint is same for all devices, + // so it's defined here as static variable, meaning it will + // not be deallocated when this function exits. + .endpoint_descriptors = data_endpoints, + }; + + cdc->functional_descriptors_count = 4; + + static usb_cdc_header_functional_decriptor_t header_function = { + .header = { .bFunctionLength = sizeof(usb_cdc_header_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = HEADER_FUNCTIONAL_DESCRIPTOR_FUNCTIONAL_DESCRIPTOR }, + .bcdCDC = 0x0110, + }; + static usb_cdc_acm_functional_decriptor_t acm_function = { + .header = { .bFunctionLength = sizeof(usb_cdc_acm_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = ABSTRACT_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR }, + .bmCapabilities = 0x00, + }; + static usb_cdc_union_functional_decriptor_t union_function = { + .header = { .bFunctionLength = sizeof(usb_cdc_union_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = UNION_FUNCTIONAL_DESCRIPTOR }, + .bControlInterface = 0, + .bSubordinateInterface0 = 1, + }; + static usb_cdc_call_management_functional_decriptor_t call_function = { + .header = { .bFunctionLength = sizeof(usb_cdc_call_management_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = CALL_MANAGEMENT_FUNCTIONAL_FUNCTIONAL_DESCRIPTOR }, + .bmCapabilities = 0x00 + }; + static usb_cdc_functional_descriptor_header_t *headers[4] = + { &header_function.header, &acm_function.header, &union_function.header, &call_function.header }; + cdc->functional_descriptors = headers; + + header->interfaces[0] = communications_interface; + header->interfaces[1] = data_interface; +} + +void usb_device_cdc_send_configuration(usb_device_t *device, + usb_setup_command_t *cmd) { + usb_device_cdc_t* dev = (usb_device_cdc_t*)device->class; + USB_OTG_INEndpointTypeDef* enp0 = &device->in[0]; + volatile uint32_t* enp0fifo = &device->fifos[0].data[0]; + uint32_t sub_word_data; + uint8_t sub_word_count = 0; + + if (enp0->DIEPCTL & USB_OTG_DIEPCTL_EPENA) { + // this is bad! Can't send the packet, this shouldn't get here, ever. + while (enp0->DIEPCTL & USB_OTG_DIEPCTL_EPENA); + } + + // first configure the size + uint32_t size = + sizeof(usb_configuration_descriptor_t) + + dev->header.interfaces_count * sizeof(usb_interface_descriptor_t); + + for (uint8_t i = 0; i < dev->header.interfaces_count; i++) { + usb_interface_t* interface = &dev->header.interfaces[i]; + size += interface->endpoint_descriptors_count * sizeof(usb_endpoint_descriptor_t); + } + + for (uint8_t i = 0; i < dev->functional_descriptors_count; i++) { + usb_cdc_functional_descriptor_header_t* descriptor = dev->functional_descriptors[i]; + size += descriptor->bFunctionLength; + } + + dev->header.configuration_descriptor.wTotalLength = size; + + // TODO: what if there is not enough space for this? + // I mean there should be... but that case should probably be handled to, + // it depends a lot on how many functions and interfaces we do have... + uint16_t packet_count = (size + 63) / 64; + enp0->DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos); + + // fill fifo with all configuration + usb_generic_fill_fifo_words(enp0, + (uint8_t*)&dev->header.configuration_descriptor, + sizeof(usb_configuration_descriptor_t), + enp0fifo, &sub_word_data, &sub_word_count); + + // NOTE: there is always one control interface and one data one. + for (uint8_t i = 0; i < dev->header.interfaces_count; i++) { + usb_interface_t* interface = &dev->header.interfaces[i]; + usb_generic_fill_fifo_words(enp0, + (uint8_t*)&interface->interface_descriptor, + sizeof(usb_interface_descriptor_t), + enp0fifo, &sub_word_data, &sub_word_count); + + // Control interface has functional descriptors + if (i == 0) { + for (uint8_t j = 0; j < dev->functional_descriptors_count; j++) { + usb_cdc_functional_descriptor_header_t* descriptor = dev->functional_descriptors[j]; + usb_generic_fill_fifo_words(enp0, (uint8_t *)&descriptor, + descriptor->bFunctionLength, + enp0fifo, &sub_word_data, &sub_word_count); + } + } + + for (uint8_t j = 0; j < interface->endpoint_descriptors_count; j++) { + usb_generic_fill_fifo_words(enp0, + (uint8_t*)&interface->endpoint_descriptors[j], + sizeof(usb_endpoint_descriptor_t), + enp0fifo, &sub_word_data, &sub_word_count); + } + + // The fifo takes always 4 elements. We do not care what's written on the + // last bytes, since the peripheral will know only about the first bytes + // as per the size written to DIEPTSIZ + if (sub_word_count > 0) { + sub_word_count = 0; + usb_generic_fill_fifo_words(enp0, (uint8_t *)&sub_word_data, + 4, enp0fifo, &sub_word_data, &sub_word_count); + } + + } + + // enable endpoint + enp0->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA; +} + +void usb_device_cdc_setup_packet_callback(usb_device_t *device, + usb_setup_command_t *cmd) { + // TODO - is there something to do? maybe just the multiplexed commands? +} +void usb_device_cdc_enumeration_done_callback(usb_device_t *device) { + // TODO start the application somehow +} +void usb_device_cdc_txfifo_empty_callback(usb_device_t *device, + uint8_t endpoint) { +// TODO the application +} +void usb_device_cdc_rxfifo_empty_callback(usb_device_t *device, + uint8_t endpoint) { +// TODO the application +} +void usb_device_cdc_transmit_done_callback(usb_device_t *device, + uint8_t endpoint) { +// TODO the application +} +void usb_device_cdc_nak_callback(usb_device_t *device, uint8_t endpoint) { + // Nothing to do for now +} +void usb_device_cdc_nyet_callback(usb_device_t *device, uint8_t endpoint) { + // Nothing to do for now +} -- 2.48.1