A include/clocks.h => include/clocks.h +39 -0
@@ 0,0 1,39 @@
+#include <stdint.h>
+
+#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
A include/delay.h => include/delay.h +28 -0
@@ 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
M include/usb.h => include/usb.h +6 -15
@@ 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);
M include/usb_device.h => include/usb_device.h +64 -9
@@ 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);
A include/usb_device_cdc.h => include/usb_device_cdc.h +116 -0
@@ 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
A src/clocks.c => src/clocks.c +38 -0
@@ 0,0 1,38 @@
+#include "clocks.h"
+#include "registers.h"
+#include <stm32h747xx.h>
+
+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);
+}
A src/delay.c => src/delay.c +7 -0
@@ 0,0 1,7 @@
+#include "delay.h"
+#include <stm32h747xx.h>
+
+void systick_configure() {
+ SysTick->LOAD = SYSTICK_LOAD;
+ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
+}
M src/main.c => src/main.c +34 -8
@@ 2,7 2,10 @@
#include <stm32h747xx.h>
#include <core_cm7.h>
#include <stdlib.h>
+#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);
M src/usb.c => src/usb.c +88 -23
@@ 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) {
M src/usb_device.c => src/usb_device.c +91 -39
@@ 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);
A src/usb_device_cdc.c => src/usb_device_cdc.c +338 -0
@@ 0,0 1,338 @@
+#include "usb.h"
+#include "usb_device.h"
+#include "usb_device_cdc.h"
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+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
+}