From 9db48e9622188345ed867afec065b1f68690e20d Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 17 Nov 2024 15:30:52 +0100 Subject: [PATCH] feat: use task_result to indicate blocking There should be no spin loops inside of the interrupts, so this change removes them, and instead uses a result system that has WOULD_BLOCK result. When this result is returned, it is expected there was no side-effect and the function can safely be reran later. --- include/generic.h | 10 ++ include/queue.h | 25 +++++ include/usb.h | 42 +++++--- include/usb_device.h | 24 +++-- include/usb_device_cdc.h | 4 +- src/queue.c | 49 +++++++++ src/usb.c | 144 +++++++++++-------------- src/usb_device.c | 228 +++++++++++++++++++++++---------------- src/usb_device_cdc.c | 26 ++--- 9 files changed, 338 insertions(+), 214 deletions(-) create mode 100644 include/generic.h create mode 100644 include/queue.h create mode 100644 src/queue.c diff --git a/include/generic.h b/include/generic.h new file mode 100644 index 0000000..5f02803 --- /dev/null +++ b/include/generic.h @@ -0,0 +1,10 @@ +#ifndef GENERIC_H +#define GENERIC_H + +typedef enum { + RES_OK, + RES_ERROR, + RES_WOULD_BLOCK +} task_result_t; + +#endif // GENERIC_H diff --git a/include/queue.h b/include/queue.h new file mode 100644 index 0000000..62f1b0c --- /dev/null +++ b/include/queue.h @@ -0,0 +1,25 @@ +#include +#include + +#ifndef QUEUE_H +#define QUEUE_H + +typedef struct { + uint16_t curr_read_ptr; + uint16_t curr_write_ptr; + + uint16_t element_size; + + uint16_t length; + uint16_t space; + + uint8_t elements[0]; +} queue_t; + +queue_t* queue_malloc(uint16_t element_size, uint16_t length); +void queue_init(queue_t* queue, uint16_t element_size, uint16_t length); +bool queue_enqueue(queue_t* queue, void* element); +void* queue_dequeue(queue_t* queue); +void* queue_peek(queue_t* queue); + +#endif // QUEUE_H diff --git a/include/usb.h b/include/usb.h index 74e06b7..290cc5d 100644 --- a/include/usb.h +++ b/include/usb.h @@ -1,5 +1,7 @@ #include #include +#include "generic.h" +#include #ifndef USB_H #define USB_H @@ -297,6 +299,15 @@ void usb_generic_fill_fifo_words(USB_OTG_INEndpointTypeDef *endpoint, uint32_t *sub_word_data, uint8_t *sub_word_bytes); +void usb_generic_fill_fifo(USB_OTG_INEndpointTypeDef *endpoint, + uint8_t *data, + uint16_t size, + volatile uint32_t *fifo_tx_target); + +task_result_t usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, + uint16_t size, + uint16_t max_packet_size); + /** * @brief Send data through usb * @details Configures the endpoint with given data count, fills fifos with those, and enabled the endpoint. @@ -305,11 +316,14 @@ void usb_generic_fill_fifo_words(USB_OTG_INEndpointTypeDef *endpoint, * @param[in] size Number of bytes to send. * @param[out] fifo_tx_target Address of the fifo. All data is written to this exact address, no offsets are made. */ -void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, volatile uint32_t *fifo_tx_target); +bool usb_is_inendpoint_ready(USB_OTG_INEndpointTypeDef *endpoint); +bool usb_check_fifo_space(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size); + /** * @brief Read data from the usb fifo. * @details The data are read from the fifo pointer, without any offsets, and put into data array @@ -317,9 +331,9 @@ void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, * @param[in] size Number of bytes to store in the array. * @param[out] fifo_rx_source Address of the fifo. All data are read from this exact address, no offsets are made. */ -void usb_generic_read(uint8_t *data, - uint16_t size, - volatile uint32_t *fifo_rx_source); +task_result_t usb_generic_read(uint8_t *data, + uint16_t size, + volatile uint32_t *fifo_rx_source); /** * @brief Sends descriptor @@ -328,7 +342,7 @@ void usb_generic_read(uint8_t *data, * @param[out] descriptor The descriptor to send. The wlength has to be set. * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target); @@ -340,7 +354,7 @@ void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced. * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_device_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target); @@ -352,7 +366,7 @@ void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced. * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor, +task_result_t usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target); @@ -363,7 +377,7 @@ void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,us * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced. * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_interface_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target); @@ -375,10 +389,10 @@ void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced. * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint, - usb_endpoint_descriptor_t *descriptor, - uint16_t max_size, - volatile uint32_t *fifo_tx_target); +task_result_t usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint, + usb_endpoint_descriptor_t *descriptor, + uint16_t max_size, + volatile uint32_t *fifo_tx_target); /** * @brief Sends string descriptor zero. @@ -387,7 +401,7 @@ void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint, * @param[out] string_descriptor The descriptor to send. The wLength NEEDS to be correct! * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_string_descriptor_zero( +task_result_t usb_send_string_descriptor_zero( USB_OTG_INEndpointTypeDef *endpoint, usb_string_descriptor_zero_t *string_descriptor, uint16_t max_size, @@ -400,7 +414,7 @@ void usb_send_string_descriptor_zero( * @param[out] string_descriptor The descriptor to send. The wLength NEEDS to be correct! * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made. */ -void usb_send_unicode_string_descriptor( +task_result_t usb_send_unicode_string_descriptor( USB_OTG_INEndpointTypeDef *endpoint, usb_unicode_string_descriptor_t *string_descriptor, uint16_t max_size, diff --git a/include/usb_device.h b/include/usb_device.h index 6a3f2d3..9abf351 100644 --- a/include/usb_device.h +++ b/include/usb_device.h @@ -1,5 +1,7 @@ #include #include "usb.h" +#include "generic.h" +#include "queue.h" #ifndef USB_DEVICE_H #define USB_DEVICE_H @@ -70,7 +72,7 @@ typedef struct { * @param[in,out] device The usb device. * @param[out] cmd The command that has been received, GET_DESCRIPTOR of type CONFIGURATION */ - void (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd); + task_result_t (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd); /** * @brief Set all endpoints except for endpoint 0 based on the configuration. @@ -98,9 +100,9 @@ typedef struct { * @details Got a setup packet that cannot be handled by the device handler. That likely means a class specific setup command. * @param[in,out] device The usb device. * @param[out] cmd The command that was received. - * @return Zero if packet was handled, other if there is an issue. + * @return A result of the callback, successfull or error. */ - uint8_t (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd); + task_result_t (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd); /** * @brief Received SET_CONFIGURATION, Endpoints set up. @@ -186,6 +188,15 @@ typedef enum { SETUP_STAGE_AWAITING_ACK, /**< Handled a setup packet, sent data, waiting for acknowledge */ } usb_setup_command_stage_t; +#define MAX_SETUP_PACKETS 3 +typedef struct { + queue_t* received_setup_commands; + + usb_setup_command_stage_t stage; /**< Current stage status. */ + + uint8_t detected_setup_errors; /**< Number of errors when handling setup commands. This should be zero. */ +} usb_device_setup_t; + /** * @struct usb_device_t * Struct representing usb device. @@ -204,12 +215,7 @@ struct usb_device_t { usb_device_state_t state; /**< The current status of the device. */ - // TODO: is this count field required? /**< Description */ - uint8_t received_setup_commands_count; /**< Number of setup commands received, max 3, wrapped. */ - uint8_t received_setup_commands_index; /**< Currently handled setup command index. */ - usb_setup_command_stage_t setup_stage; /**< Current stage status. */ - uint8_t detected_setup_errors; /**< Number of errors when handling setup commands. This should be zero. */ - usb_setup_command_t received_setup_commands[3]; /**< The setup commands reseived so far. */ + usb_device_setup_t setup; }; // has configuration etc diff --git a/include/usb_device_cdc.h b/include/usb_device_cdc.h index ba7dbbd..b6b8d5c 100644 --- a/include/usb_device_cdc.h +++ b/include/usb_device_cdc.h @@ -106,9 +106,9 @@ usb_class_header_t* usb_device_cdc_init(usb_device_t *device, 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); +task_result_t usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd); -uint8_t usb_device_cdc_setup_packet_callback(usb_device_t* device, usb_setup_command_t* cmd); +task_result_t 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); diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..53fd89c --- /dev/null +++ b/src/queue.c @@ -0,0 +1,49 @@ +#include +#include "queue.h" + +queue_t *queue_malloc(uint16_t element_size, uint16_t length) { + return (queue_t*)malloc(sizeof(queue_t) + element_size * length); +} + +void queue_init(queue_t* queue, uint16_t element_size, uint16_t length) { + queue->curr_write_ptr = 0; + queue->curr_read_ptr = 0; + queue->element_size = element_size; + queue->length = length; + queue->space = length; +} + +bool queue_enqueue(queue_t *queue, void *element) { + if (queue->space == 0) { + return false; + } + + for (uint16_t i = 0; i < queue->element_size; i++) { + queue->elements[queue->curr_write_ptr + i] = *(uint8_t*)(element + i); + } + + queue->space--; + queue->curr_write_ptr += queue->element_size; + queue->curr_write_ptr %= queue->length * queue->element_size; + return true; +} + +void *queue_dequeue(queue_t *queue) { + void* element = queue_peek(queue); + + if (element != NULL) { + queue->curr_read_ptr += queue->element_size; + queue->curr_read_ptr %= queue->length * queue->element_size; + queue->space++; + } + + return element; +} + +void *queue_peek(queue_t *queue) { + if (queue->space == queue->length) { + return NULL; + } + + return &queue->elements[queue->curr_read_ptr]; +} diff --git a/src/usb.c b/src/usb.c index f83f845..28957ec 100644 --- a/src/usb.c +++ b/src/usb.c @@ -77,21 +77,32 @@ void usb_generic_fill_fifo(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, volatile uint32_t *fifo_tx_target) { - size = ((size + 3) / 4) * 4; + usb_data_t txData; + uint8_t subWordBytes = size % 4; + uint8_t wordCount = size / 4; + + for (uint8_t i = 0; i < wordCount; i++) { + txData.word = *((uint32_t*)data); + *fifo_tx_target = txData.word; + data += 4; + } - uint32_t subWordData; - uint8_t subWordCount; + if (subWordBytes > 0) { + txData.word = 0; + for (int i = 0; i < subWordBytes; i++) { + txData.bytes[i] = *(data++); + size--; + } - usb_generic_fill_fifo_words(endpoint, data, size, fifo_tx_target, &subWordData, &subWordCount); + *fifo_tx_target = txData.word; + } } -void usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size, uint16_t max_packet_size) { +task_result_t usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size, uint16_t max_packet_size) { uint16_t packet_count = (size + max_packet_size - 1) / max_packet_size; - if (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA) { - // this is bad! Can't send the packet, this shouldn't get here, ever. - while (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA); - /* __nop(); */ + if (usb_is_inendpoint_ready(endpoint)) { + return RES_WOULD_BLOCK; } if (size > 0) { @@ -100,50 +111,27 @@ void usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t endpoint->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos); } endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA; + + return RES_OK; } -void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, volatile uint32_t *fifo_tx_target) { - if (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA) { - // this is bad! Can't send the packet, this shouldn't get here, ever. - while (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA); - /* __nop(); */ - } - - uint32_t fifo_size; - while ((fifo_size = endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) < (size + 3) / 4) { - /* __nop(); */ + task_result_t result = usb_generic_setup_in_endpoint(endpoint, size, 64); + if (result != RES_OK) { + return result; } - // TODO: generic max packet size - usb_generic_setup_in_endpoint(endpoint, size, 64); - - usb_data_t tx_data; - 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; - } - - // TODO: check where to put it... beginning, end... - if (subWordBytes != 0) { - tx_data.word = 0; - for (uint8_t i = 0; i < subWordBytes; i++) { - tx_data.bytes[i] = *(data + i); - } - *fifo_tx_target = tx_data.word; - } + usb_generic_fill_fifo(endpoint, data, size, fifo_tx_target); // After the fifo is filled... endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK; + return RES_OK; } -void usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_source) { +task_result_t usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_source) { usb_data_t rx_data; uint8_t subWordBytes = size % 4; uint8_t wordCount = size / 4; @@ -161,9 +149,11 @@ void usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_s *(data + i) = rx_data.bytes[i]; } } + + return RES_OK; } -void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { @@ -172,81 +162,60 @@ void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, size = max_size; } - usb_generic_send(endpoint, (uint8_t*)descriptor, size, fifo_tx_target); + return usb_generic_send(endpoint, (uint8_t*)descriptor, size, 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); */ -/* } */ - -/* // 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, +task_result_t usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_device_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { descriptor->header.bDescriptorType = DESCRIPTOR_DEVICE; descriptor->header.bLength = sizeof(usb_device_descriptor_t); - usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); + return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); } -void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_device_qualifier_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { descriptor->header.bDescriptorType = DESCRIPTOR_DEVICE_QUALIFIER; descriptor->header.bLength = sizeof(usb_device_qualifier_t); - usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); + return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); } -void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_interface_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { descriptor->header.bDescriptorType = DESCRIPTOR_INTERFACE; descriptor->header.bLength = sizeof(usb_interface_descriptor_t); - usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); + return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); } -void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_endpoint_descriptor_t *descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { descriptor->header.bDescriptorType = DESCRIPTOR_ENDPOINT; descriptor->header.bLength = sizeof(usb_endpoint_descriptor_t); - usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); + return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target); } -void usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint, +task_result_t usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint, usb_string_descriptor_zero_t *string_descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { usb_data_t data; data.word = 0; - usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64); + task_result_t result = usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64); + if (result != RES_OK) { + return result; + } data.bytes[0] = string_descriptor->header.bLength; data.bytes[1] = string_descriptor->header.bDescriptorType; if (string_descriptor->header.bLength == 2) { usb_generic_fill_fifo(endpoint, &data.bytes[0], 2, fifo_tx_target); - return; + return RES_OK; } data.halfwords[1] = string_descriptor->wLANGID[0]; @@ -258,14 +227,20 @@ void usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint, } endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK; + + return RES_OK; } -void usb_send_unicode_string_descriptor( + +task_result_t usb_send_unicode_string_descriptor( USB_OTG_INEndpointTypeDef *endpoint, usb_unicode_string_descriptor_t *string_descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target) { - usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64); + task_result_t result = usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64); + if (result != RES_OK) { + return result; + } usb_data_t data = { .word = 0 }; @@ -285,4 +260,13 @@ void usb_send_unicode_string_descriptor( } endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK; + return RES_OK; +} + +bool usb_is_inendpoint_ready(USB_OTG_INEndpointTypeDef *endpoint) { + return (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA) != 0; +} + +bool usb_check_fifo_space(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size) { + return ((endpoint->DTXFSTS & (USB_OTG_DTXFSTS_INEPTFSAV_Msk)) >> USB_OTG_DTXFSTS_INEPTFSAV_Pos) >= (size + 3) / 4; } diff --git a/src/usb_device.c b/src/usb_device.c index 9226504..551fbf7 100644 --- a/src/usb_device.c +++ b/src/usb_device.c @@ -20,14 +20,15 @@ void* usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class, void* peripheral_address = usb_periph_addresses[slot]; device->state = INIT; - device->detected_setup_errors = 0; + device->setup.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->received_setup_commands_count = 0; - device->received_setup_commands_index = 0; + + device->setup.received_setup_commands = queue_malloc(sizeof(usb_setup_command_t), MAX_SETUP_PACKETS); + queue_init(device->setup.received_setup_commands, sizeof(usb_setup_command_t), MAX_SETUP_PACKETS); device->vt = *class; device->class = device-> @@ -152,10 +153,8 @@ typedef struct { uint8_t status_phase_start; } 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++; - } +task_result_t usb_handle_setup_command(usb_device_t *device, usb_setup_command_t* cmd) { + task_result_t result = RES_ERROR; switch (cmd->bRequest) { case USB_SETUP_GET_STATUS: { @@ -189,9 +188,11 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { break; } - usb_generic_send(device->in, packet, size, - &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = usb_generic_send(device->in, packet, size, + &device->fifos[0].data[0]); + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; + } } break; case USB_SETUP_GET_DESCRIPTOR: { @@ -200,44 +201,40 @@ 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, cmd->wLength, &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = usb_send_device_descriptor(device->in, &device->class->device_descriptor, cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_CONFIGURATION: - device->vt.send_configuration(device, cmd); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = 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, cmd->wLength, &device->fifos[0].data[0]); + result = usb_send_string_descriptor_zero(device->in, &device->class->string_descriptor_zero, cmd->wLength, &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], cmd->wLength, &device->fifos[0].data[0]); + result = usb_send_unicode_string_descriptor(device->in, &device->class->string_descriptors[index], cmd->wLength, &device->fifos[0].data[0]); } } break; case DESCRIPTOR_INTERFACE: - usb_send_interface_descriptor(device->in, &device->class->interfaces[descriptor_index].interface_descriptor, cmd->wLength, &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = usb_send_interface_descriptor(device->in, &device->class->interfaces[descriptor_index].interface_descriptor, cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_ENDPOINT: // TODO: how to match the interface to the descriptor index? - usb_send_endpoint_descriptor(device->in, &device->class->interfaces[0].endpoint_descriptors[descriptor_index], cmd->wLength, &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = usb_send_endpoint_descriptor(device->in, &device->class->interfaces[0].endpoint_descriptors[descriptor_index], cmd->wLength, &device->fifos[0].data[0]); break; case DESCRIPTOR_DEVICE_QUALIFIER: - usb_send_device_qualifier_descriptor(device->in, &device->class->device_qualifier, cmd->wLength, &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = usb_send_device_qualifier_descriptor(device->in, &device->class->device_qualifier, cmd->wLength, &device->fifos[0].data[0]); 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 + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; + } } break; case USB_SETUP_GET_CONFIGURATION: { @@ -245,16 +242,20 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { 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; + result = usb_generic_send(device->in, &value, sizeof(value), &device->fifos[0].data[0]); + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; + } } break; case USB_SETUP_GET_INTERFACE: { - usb_generic_send(device->in, - &device->class->interfaces[cmd->wIndex].interface_descriptor.bAlternateSetting, - sizeof(uint8_t), - &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_RESPONSE; + result = usb_generic_send(device->in, + &device->class->interfaces[cmd->wIndex].interface_descriptor.bAlternateSetting, + sizeof(uint8_t), + &device->fifos[0].data[0]); + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_RESPONSE; + } } break; case USB_SETUP_SET_ADDRESS: @@ -262,9 +263,11 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { cmd->wValue, 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; + result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_ACK; + device->state = SET_ADDRESS_RCVD; + } break; case USB_SETUP_SET_CONFIGURATION: { if (cmd->wValue == 0) { @@ -275,7 +278,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { } 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; + device->setup.stage = SETUP_STAGE_SENDING_ACK; // TODO setup endpoints } else { @@ -297,8 +300,10 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { break; } - usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_ACK; + result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_ACK; + } break; case USB_SETUP_SET_FEATURE: switch (cmd->wValue) { @@ -314,8 +319,10 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { break; } - usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); - device->setup_stage = SETUP_STAGE_SENDING_ACK; + result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); + if (result == RES_OK) { + device->setup.stage = SETUP_STAGE_SENDING_ACK; + } break; case USB_SETUP_SET_DESCRIPTOR: case USB_SETUP_SET_INTERFACE: @@ -326,13 +333,77 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) { case RESERVED1: case RESERVED2: default: - device->vt.setup_packet_callback(device, cmd); + return device->vt.setup_packet_callback(device, cmd); break; } - device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK; + return result; } +typedef enum { + USB_UNKNOWN, + USB_GOT_SETUP, + USB_PROCESS_SETUP, + USB_GOT_ACK, + USB_GOT_RESPONSE, + USB_SENT_RESPONSE, +} usb_event_t; + +task_result_t usb_handle_setup(usb_device_t *device, usb_event_t event) { + switch (device->setup.stage) { + case SETUP_STAGE_RCVD_SETUP_PACKET: { + if (event == USB_GOT_SETUP) { + return RES_OK; + } + + usb_setup_command_t* command = queue_peek(device->setup.received_setup_commands); + task_result_t result = usb_handle_setup_command(device, command); + if (result == RES_OK) { + queue_dequeue(device->setup.received_setup_commands); + uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); + if (setup_packets_count == 0) { + reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); + } + } + return result; + } + break; + case SETUP_STAGE_NONE: + if (queue_peek(device->setup.received_setup_commands) != NULL) { + device->setup.stage = SETUP_STAGE_RCVD_SETUP_PACKET; + + if (event == USB_PROCESS_SETUP) { + usb_handle_setup(device, event); + } + } + break; + case SETUP_STAGE_AWAITING_RESPONSE: + // NOTE: Currently not really supported. + return RES_ERROR; + break; + case SETUP_STAGE_SENDING_RESPONSE: + if (event == USB_SENT_RESPONSE) { + device->setup.stage = SETUP_STAGE_AWAITING_ACK; + } + break; + case SETUP_STAGE_SENDING_ACK: + if (event == USB_SENT_RESPONSE) { + device->setup.stage = SETUP_STAGE_NONE; + return usb_handle_setup(device, event); + } + break; + case SETUP_STAGE_AWAITING_ACK: + if (event == USB_GOT_ACK) { + device->setup.stage = SETUP_STAGE_NONE; + return usb_handle_setup(device, event); + } + break; + } + + return RES_OK; +} + + void usb_handle_rxflvl_control_int(usb_device_t *device, packet_info_t *packet_info) { uint32_t dummy; @@ -342,44 +413,27 @@ void usb_handle_rxflvl_control_int(usb_device_t *device, if (packet_info->packet_status == PACKET_SETUP_TRANSACTION_COMPLETED) { // Nothing do to. dummy = *fifo; - - if (device->setup_stage != SETUP_STAGE_RCVD_SETUP_PACKET) { - // something went wrong. Let's continue, but this isn't looking good. - device->detected_setup_errors++; - } - - device->received_setup_commands_index %= 3; - uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); - usb_setup_command_t* command = &device->received_setup_commands[device->received_setup_commands_index++]; - usb_handle_setup(device, command); - - if (setup_packets_count == 0) { - reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); - } + usb_handle_setup(device, USB_PROCESS_SETUP); } else if (packet_info->packet_status == PACKET_SETUP_DATA_PACKET_RECEIVED) { // SAVE data - uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); - usb_setup_command_t* command = &device - ->received_setup_commands[device->received_setup_commands_count++]; - device->received_setup_commands_count %= 3; - - usb_generic_read(command, 8, fifo); + usb_setup_command_t command; + usb_generic_read((uint8_t*)&command, 8, fifo); + usb_handle_setup(device, USB_GOT_SETUP); - 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); */ - /* } */ + if (!queue_enqueue(device->setup.received_setup_commands, &command)) { + // Got a problem, setup command lost! + device->setup.detected_setup_errors++; + } 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) { + 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; + device->setup.detected_setup_errors++; + usb_handle_setup(device, USB_GOT_RESPONSE); } } } @@ -494,11 +548,8 @@ void usb_handle_endpoint_in_int(usb_device_t* device) { } if (ep_id == 0) { - if (device->setup_stage == SETUP_STAGE_SENDING_RESPONSE) { - device->setup_stage = SETUP_STAGE_AWAITING_ACK; - } else if (device->setup_stage == SETUP_STAGE_SENDING_ACK) { - device->setup_stage = SETUP_STAGE_NONE; - } + usb_handle_setup(device, USB_GOT_RESPONSE); + usb_handle_setup(device, USB_PROCESS_SETUP); } reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_XFRC); } @@ -566,20 +617,8 @@ void usb_handle_endpoint_out_int(usb_device_t* device) { reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_OTEPDIS); } if (interrupt_reg & USB_OTG_DOEPINT_STUP) { + usb_handle_setup(device, USB_PROCESS_SETUP); reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_STUP); - /* if (device->setup_stage != SETUP_STAGE_RCVD_SETUP_PACKET) { */ - /* // something went wrong. Let's continue, but this isn't looking good. */ - /* device->detected_setup_errors++; */ - /* } */ - - /* device->received_setup_commands_index %= 3; */ - /* uint8_t setup_packets_count = reg_read_bits_pos(&device->out[ep_id].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); */ - /* usb_setup_command_t* command = &device->received_setup_commands[device->received_setup_commands_index++]; */ - /* usb_handle_setup(device, command); */ - - /* if (setup_packets_count == 0) { */ - /* reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); */ - /* } */ } if (interrupt_reg & USB_OTG_DOEPINT_AHBERR) { // NOTE This shoudln't be reached since DMA is not used @@ -591,15 +630,15 @@ void usb_handle_endpoint_out_int(usb_device_t* device) { reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_EPDISD); } if (interrupt_reg & USB_OTG_DOEPINT_XFRC) { - // TODO: handle data? - callback to device data handle? + device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA; reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_XFRC); - if (ep_id == 0 && device->setup_stage == SETUP_STAGE_AWAITING_ACK) { - device->setup_stage = SETUP_STAGE_NONE; - } + // TODO: handle data? - callback to device data handle? - device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA; - // Transfer has been completed + if (ep_id == 0) { + usb_handle_setup(device, USB_GOT_ACK); + usb_handle_setup(device, USB_PROCESS_SETUP); + } } } @@ -696,9 +735,10 @@ void otg_hs_handler(void) { // Start of frame if (device->core->GINTSTS & USB_OTG_GINTSTS_SOF) { - // Nothing to do? device->core->GINTSTS = USB_OTG_GINTSTS_SOF; handled = 1; + + usb_handle_setup(device, USB_PROCESS_SETUP); return; } diff --git a/src/usb_device_cdc.c b/src/usb_device_cdc.c index 34a5ca4..2e8b798 100644 --- a/src/usb_device_cdc.c +++ b/src/usb_device_cdc.c @@ -248,19 +248,14 @@ uint16_t get_size(uint16_t size_to_send, uint16_t* remaining_size) { return size; } -void usb_device_cdc_send_configuration(usb_device_t *device, - usb_setup_command_t *cmd) { +task_result_t 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 uint16_t size = sizeof(usb_configuration_descriptor_t) + @@ -285,11 +280,11 @@ void usb_device_cdc_send_configuration(usb_device_t *device, // 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; - // configure size - enp0->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_MULCNT_Pos) | (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos); - // enable endpoint - enp0->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA; + task_result_t result = usb_generic_setup_in_endpoint(enp0, size, 64); + + if (result != RES_OK) { + return result; + } // fill fifo with all configuration usb_generic_fill_fifo_words(enp0, @@ -334,12 +329,13 @@ void usb_device_cdc_send_configuration(usb_device_t *device, // After the fifo is filled... enp0->DIEPCTL = USB_OTG_DIEPCTL_CNAK; + return result; } -uint8_t usb_device_cdc_setup_packet_callback(usb_device_t *device, - usb_setup_command_t *cmd) { +task_result_t 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? - return 1; + return RES_OK; } void usb_device_cdc_enumeration_done_callback(usb_device_t *device) { // TODO start the application somehow -- 2.48.1