From 02e7e4dfbf77f0e6aac3abd7ab75632233c347e0 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 7 Nov 2024 21:33:48 +0100 Subject: [PATCH] docs: add documentation comments to most functions --- include/exti.h | 22 ++++ include/pin.h | 6 + include/registers.h | 66 ++++++++++- include/usb.h | 223 ++++++++++++++++++++++++++++-------- include/usb_device.h | 237 ++++++++++++++++++++++++++++++++------- include/usb_device_cdc.h | 21 +++- src/main.c | 7 +- src/usb_device_cdc.c | 3 +- 8 files changed, 493 insertions(+), 92 deletions(-) diff --git a/include/exti.h b/include/exti.h index 44322ed..ac5b830 100644 --- a/include/exti.h +++ b/include/exti.h @@ -13,10 +13,32 @@ exti_t* exti_init(EXTI_TypeDef* exti, SYSCFG_TypeDef* syscfg); void exti_external_interrupt(exti_t* exti, uint8_t line, uint8_t gpio); // Only 21 positions are now supported. This is too hard for other ones. + +/** + * @brief Set the exti line to send an interrupt on rising edge. + * @details Valid for external EXTI interrupts. The interrupt will be triggered on rising interrupt + * @param[in,out] exti The exti line descriptor + */ void exti_rising_interrupt(exti_t* exti, uint8_t line); + +/** + * @brief Set the exti line to send an interrupt on falling edge. + * @details Valid for external EXTI interrupts. The interrupt will be triggered on falling interrupt + * @param[in,out] exti The exti line descriptor + */ void exti_falling_interrupt(exti_t* exti, uint8_t line); + +/** + * @brief Mask the interrupt for given exti line. + * @details Description + * @param[in,out] exti The exti line descriptor + */ void exti_disable_interrupt(exti_t* exti, uint8_t line); +/** + * @brief Enable the nvic interrupt + * @param[in,out] exti The exti line descriptor + */ void exti_nvic_setup(exti_t* exti, uint8_t line); #endif // EXTI_H diff --git a/include/pin.h b/include/pin.h index 5d75ff3..8940a32 100644 --- a/include/pin.h +++ b/include/pin.h @@ -31,6 +31,12 @@ typedef enum { VERY_HIGH_SPEED } pin_speedmode_t; +/** + * @brief Allocate a pin for given gpio. + * @param[in,out] gpio The address for the gpio, like GPIOA + * @param[in] pin Location of the pin, ie. for GPIOA2 use 2 + * @return Description + */ pin_t* pin_init(GPIO_TypeDef* gpio, uint8_t pin); void pin_mode(pin_t* pin, pin_mode_t mode); diff --git a/include/registers.h b/include/registers.h index 64dbcb2..e255f10 100644 --- a/include/registers.h +++ b/include/registers.h @@ -3,14 +3,78 @@ #ifndef REGISTERS_H #define REGISTERS_H +/** + * @brief Writes data on the given position in register. + * @param[in,out] reg The address to change. + * @param[in] data The data to write on pos. + * @param[in] pos The position to start writing to. First @see data param is shifted by this. + * @param[in] mask The mask to and the data with, ie. if you want only two bits, set to 0x3. + */ extern inline void reg_write_bits_pos(volatile uint32_t *reg, uint32_t data, uint8_t pos, uint32_t mask); + +/** + * @brief Writes data into the register. + * @param[in,out] reg The address to change. + * @param[in] data The data to write to the whole register. + * @param[in] mask The mask to and the data with, ie. if you want only two bits, set to 0x3. + */ extern inline void reg_write_bits(volatile uint32_t *reg, uint32_t data, uint32_t mask); -extern inline void reg_set_bits_pos(volatile uint32_t *reg, uint32_t mask); + +/** + * @brief Write ones on the given position in register. + * @param[in,out] reg The address to change. + * @param[in] pos The position to start writing to. First @see mask param is shifted by this. + * @param[in] mask The mask of bits to write ones to, ie. if you want only two bits, set to 0x3. + */ +extern inline void reg_set_bits_pos(volatile uint32_t *reg, uint8_t pos, uint32_t mask); + +/** + * @brief Write ones into register. + * @param[in,out] reg The address to change. + * @param[in] mask The mask of bits to write ones to. + */ extern inline void reg_set_bits(volatile uint32_t *reg, uint32_t mask); + +/** + * @brief Toggle bits in the register, starting from @see pos. + * @details Toggles bits where one is written to the mask, but shifted by @see pos. + * @param[in,out] reg Address of the register to toggle bits in. + * @param[in] pos The position to shift the @see mask by. + * @param[in] mask The mask saying what bits to toggle (1), from the @see pos. + */ extern inline void reg_toggle_bits_pos(volatile uint32_t *reg, uint8_t pos, uint32_t mask); + +/** + * @brief Toggle bits in the register. + * @details Toggles bits where one is written to the mask. + * @param[in,out] reg Address of the register to toggle bits in. + * @param[in] mask The mask saying what bits to toggle (1). + */ extern inline void reg_toggle_bits(volatile uint32_t *reg, uint32_t mask); + +/** + * @brief Writes zeros on the given position in register. + * @param[in,out] reg The address to change. + * @param[in] pos The position to start writing to. First the mask is shifted by this. + * @param[in] mask The mask of bits to write ones to, ie. if you want only two bits, set to 0x3. + */ extern inline void reg_clear_bits_pos(volatile uint32_t *reg, uint8_t pos, uint32_t mask); + +/** + * @brief Write zeros on into register. + * @param[in,out] reg The address to change. + * @param[in] mask The mask of bits to write ones to. + */ extern inline void reg_clear_bits(volatile uint32_t *reg, uint32_t mask); + +/** + * @brief Get bits from the register on the given position. + * @details The obtained value starts from @see pos, and is anded with @see mask. + * @param[in,out] reg The register address to read + * @param[in] pos The position to shift data by. + * @param[in] mask The mask to apply on the resulting data + * @return The read data. + */ extern inline uint32_t reg_read_bits_pos(volatile uint32_t *reg, uint8_t pos, uint32_t mask); #endif // REGISTERS_H diff --git a/include/usb.h b/include/usb.h index 7619276..34962d9 100644 --- a/include/usb.h +++ b/include/usb.h @@ -12,69 +12,106 @@ #define USB_LANG_ENGLISH 0x09 #pragma GCC diagnostic error "-Wpadded" + +/** + * @struct usb_device_status_t + * USB Device status halfword + */ typedef struct { - uint8_t self_powered : 1; - uint8_t remote_wakeup : 1; - uint16_t reserved_zeros : 14; + uint8_t self_powered : 1; /**< Is the device self powered? */ + uint8_t remote_wakeup : 1; /**< Can the device be remotely waken up by usb? */ + uint16_t reserved_zeros : 14; /**< Should be zeros, reserved. */ } usb_device_status_t; _Static_assert(sizeof(usb_device_status_t) == 2, "Size check"); +/** + * @struct usb_endpoint_status_t + * USB endpoint status halfword + */ typedef struct { - uint8_t halt : 1; - uint16_t reserved_zeros : 15; + uint8_t halt : 1; /**< Is the endpoint halted? */ + uint16_t reserved_zeros : 15; /**< Should be zeros, reserved */ } usb_endpoint_status_t; _Static_assert(sizeof(usb_endpoint_status_t) == 2, "Size check"); +/** + * @struct usb_interface_status_t + * USB interface status halfword + */ typedef struct { - uint16_t reserved; + uint16_t reserved; /**< Should be zeros, reserved. */ } usb_interface_status_t; _Static_assert(sizeof(usb_interface_status_t) == 2, "Size check"); +/** + * @enum usb_setup_command_direction_t + * USB setup command request direction + */ typedef enum { - USB_SETUP_HOST_TO_DEVICE, - USB_SETUP_DEVICE_TO_HOST, + USB_SETUP_HOST_TO_DEVICE, /**< The host will send data to device. */ + USB_SETUP_DEVICE_TO_HOST, /**< The device will send dat to host. */ } usb_setup_command_direction_t; +/** + * @enum usb_setup_command_type_t + * USB setup command type + */ typedef enum { - USB_SETUP_STANDARD, - USB_SETUP_CLASS, - USB_SETUP_VENDOR, - USB_SETUP_RESERVED, + USB_SETUP_STANDARD, /**< A standard setup command */ + USB_SETUP_CLASS, /**< A class-specific setup commandj */ + USB_SETUP_VENDOR, /**< A vendor-specific setup command */ + USB_SETUP_RESERVED, /**< Reserved. */ } usb_setup_command_type_t; +/** + * @enum usb_setup_command_recipient_t + * USB setup command recipient + */ typedef enum { - USB_SETUP_DEVICE, - USB_SETUP_INTERFACE, - USB_SETUP_ENDPOINT, - USB_SETUP_OTHER, + USB_SETUP_DEVICE, /**< The command's recipient is the device */ + USB_SETUP_INTERFACE, /**< The command's recipient is an interface (ie. get descriptor of an interface) */ + USB_SETUP_ENDPOINT, /**< The command's recipient is an endpoint (ie. get descriptor of an endpoint) */ + USB_SETUP_OTHER, /**< Other. */ } usb_setup_command_recipient_t; +/** + * @struct usb_setup_command_request_type_t + * USB setup command request type + */ typedef struct __attribute__((packed)) { - uint8_t recipient : 5; - usb_setup_command_type_t type : 2; - usb_setup_command_direction_t direction : 1; + uint8_t recipient : 5; /**< The address of the recipient */ + usb_setup_command_type_t type : 2; /**< The type of the command */ + usb_setup_command_direction_t direction : 1; /**< The direction of the setup command */ } usb_setup_command_request_type_t; +/** + * @enum usb_setup_request_t + * Generic setup request commands + */ typedef enum __attribute__((packed)) { - USB_SETUP_GET_STATUS = 0, - USB_SETUP_CLEAR_FEATURE = 1, - RESERVED1 = 2, - USB_SETUP_SET_FEATURE = 3, - RESERVED2 = 4, - USB_SETUP_SET_ADDRESS = 5, - USB_SETUP_GET_DESCRIPTOR = 6, - USB_SETUP_SET_DESCRIPTOR = 7, - USB_SETUP_GET_CONFIGURATION = 8, - USB_SETUP_SET_CONFIGURATION = 9, - USB_SETUP_GET_INTERFACE = 10, - USB_SETUP_SET_INTERFACE = 11, - USB_SETUP_SYNCH_FRAME = 12, + USB_SETUP_GET_STATUS = 0, /**< Get status byte from the device */ + USB_SETUP_CLEAR_FEATURE = 1, /**< Clear a feature of an interface or an endpoint, like halt */ + RESERVED1 = 2, /**< Reserved. */ + USB_SETUP_SET_FEATURE = 3, /**< Set a feature of an interface or an endpoint, like halt. */ + RESERVED2 = 4, /**< Reserved. */ + USB_SETUP_SET_ADDRESS = 5, /**< Set address of the device to what is in data of the request. */ + USB_SETUP_GET_DESCRIPTOR = 6, /**< Get a specific descriptor of the device. It should be sent in data stage. */ + USB_SETUP_SET_DESCRIPTOR = 7, /**< Set an alternate setting for given descriptor. */ + USB_SETUP_GET_CONFIGURATION = 8, /**< Get currently used configuration alternate setting. */ + USB_SETUP_SET_CONFIGURATION = 9, /**< Set alternate configuration. */ + USB_SETUP_GET_INTERFACE = 10, /**< Get currently used interface alternate setting. */ + USB_SETUP_SET_INTERFACE = 11, /**< Set alternate interface. */ + USB_SETUP_SYNCH_FRAME = 12, /**< I don't know. */ } usb_setup_request_t; +/** + * @enum usb_setup_feature_selector_t + * USB setup features for SET_FEATURE and CLEAR_FEATURE + */ typedef enum { - USB_FEATURE_ENDPOINT_HALT = 0, - USB_FEATURE_DEVICE_REMOTE_WAKEUP = 1, - USB_FEATURE_TEST_MODE = 2, + USB_FEATURE_ENDPOINT_HALT = 0, /**< Endpoint halt feature */ + USB_FEATURE_DEVICE_REMOTE_WAKEUP = 1, /**< Device remote wakeup feature */ + USB_FEATURE_TEST_MODE = 2, /**< Device test mode */ } usb_setup_feature_selector_t; typedef enum { @@ -88,18 +125,26 @@ typedef enum { DESCRIPTOR_INTERFACE_POWER = 8, } usb_descriptor_type_t; +/** + * @struct usb_setup_command_t + * USB setup command structure + */ typedef struct __attribute__((packed)) { - usb_setup_command_request_type_t bmRequestType; - usb_setup_request_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; + usb_setup_command_request_type_t bmRequestType; /**< Type of the request */ + usb_setup_request_t bRequest; /**< The request data. */ + uint16_t wValue; /**< Value of the request, dependant on the request type. For example device address to set. */ + uint16_t wIndex; /**< Index, dependant on the request type. For example interface index. */ + uint16_t wLength; /**< For output request, amount of data. For input request, maximum number of bytes to send. */ } usb_setup_command_t; _Static_assert(sizeof(usb_setup_command_t) == 8, "Size check"); +/** + * @struct usb_descriptor_t + * Header of every usb descriptor + */ typedef struct __attribute__((packed)) { - uint8_t bLength; - uint8_t bDescriptorType; + uint8_t bLength; /**< Full length of this descriptor. */ + uint8_t bDescriptorType; /**< Type of the descriptor - usb_descriptor_type_t */ } usb_descriptor_t; typedef struct { @@ -226,42 +271,132 @@ typedef struct { // NOTE: End of structs from usb specification here +/** + * @brief Put data into fifo + * @details + * This function will fill fifo of the given endpoint, without configuring the endpoint. + * The motivation for this function is to be able to pass multiple structures into the fifo. + * For example if you want to send usb configuration, you first send configuration descriptor, + * then endpoint descriptor and so on. These structures are not usually aligned to 4 bytes. + * So we need to always pass in only part of the structure that is divisible by 4 bytes and + * put the rest to the next one. To do that, we accept pointers to sub word data and bytes. + * It's expected to first send all structures, and at the end, if the sub_word_bytes is >0, + * to send one more call with size == 4, and data == sub_word_data. + * These hold the data that overflow this 4 byte threshold. + * @param[in,out] endpoint NOT used. + * @param[out] data The data to send, like a usb descriptor. + * @param[in] size Size of the data to send. + * @param[out] fifo_tx_target The fifo to put the data to. It is written to the same address even for multiple words. + * @param[in, out] sub_word_data Input and output data. For input this means data to prefix the transfer with. For output this means what to prefix next transfer with. + * @param[in, out] sub_word_bytes Input and output byte count of sub_word_data. + */ 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); + +/** + * @brief Send data through usb + * @details Configures the endpoint with given data count, fills fifos with those, and enabled the endpoint. + * @param[in,out] endpoint The endpoint to send the data with. + * @param[out] data Description + * @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, uint8_t *data, uint16_t size, volatile uint32_t *fifo_tx_target); + +/** + * @brief Read data from the usb fifo. + * @details The data are read from the fifo pointer, without any offsets, and put into data array + * @param[in,out] data The array to put data to + * @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); + +/** + * @brief Sends descriptor + * @details Sends data matching the given descriptor. It is expected that the descriptor has length set properly. + * @param[in,out] endpoint The endpoint to send data with. + * @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, usb_descriptor_t *descriptor, volatile uint32_t *fifo_tx_target); + +/** + * @brief Sends device descriptor. + * @details Sets appropriate length of the descriptor, and then sends it using @see usb_send_descriptor + * @param[in,out] endpoint The endpoint to send data with. + * @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, usb_device_descriptor_t *descriptor, volatile uint32_t *fifo_tx_target); + +/** + * @brief Sends device qualifier descriptor. + * @details Sets appropriate length of the descriptor, and then sends it using @see usb_send_descriptor + * @param[in,out] endpoint The endpoint to send data with. + * @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, volatile uint32_t *fifo_tx_target); + +/** + * @brief Sends interface qualifier descriptor. + * @details Sets appropriate length of the descriptor, and then sends it using @see usb_send_descriptor + * @param[in,out] endpoint The endpoint to send data with. + * @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, usb_interface_descriptor_t *descriptor, volatile uint32_t *fifo_tx_target); + +/** + * @brief Sends endpoint qualifier descriptor. + * @details Sets appropriate length of the descriptor, and then sends it using @see usb_send_descriptor + * @param[in,out] endpoint The endpoint to send data with. + * @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, volatile uint32_t *fifo_tx_target); + +/** + * @brief Sends string descriptor zero. + * @details The length of the array has to be known, it is determined by wLength of the header. + * @param[in,out] endpoint The endpoint to send data with. + * @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( USB_OTG_INEndpointTypeDef *endpoint, usb_string_descriptor_zero_t *string_descriptor, volatile uint32_t *fifo_tx_target); + +/** + * @brief Sends unicode string descriptor. + * @details The length of the array has to be known, it is determined by wLength of the header. + * @param[in,out] endpoint The endpoint to send data with. + * @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( USB_OTG_INEndpointTypeDef *endpoint, usb_unicode_string_descriptor_t *string_descriptor, volatile uint32_t *fifo_tx_target); - - #endif // USB_H diff --git a/include/usb_device.h b/include/usb_device.h index a8cd3d8..686aa9f 100644 --- a/include/usb_device.h +++ b/include/usb_device.h @@ -4,110 +4,267 @@ #ifndef USB_DEVICE_H #define USB_DEVICE_H +/** + * @enum usb_device_state_t + * State of the usb_device. + */ typedef enum { // error states first so they are lowest - CONTROL_ERROR = -4, - UNKNOWN_CONTROL_COMMAND = -4, - ERROR = -3, - UNKNOWN_INTERRUPT = -2, - OTG_ERROR = -1, // shouldn't happen, since otg is not used - - INIT = 0, - RESET = 1, - RESET_DONE = 2, - SET_ADDRESS_RCVD = 3, - ENUMERATED = 4, + CONTROL_ERROR = -4, /**< There was an error on the control interface. The device doesn't know how to proceed. */ + UNKNOWN_CONTROL_COMMAND = -4, /**< An unknown control command has been received. The device doesn't know how to proceed. */ + ERROR = -3, /**< An unspecified error. */ + UNKNOWN_INTERRUPT = -2, /**< There was an unknown interrupt. This should not occur as all interrupts should be handled. */ + OTG_ERROR = -1, /**< There was an interrupt from otg / error from otg. Otg is not used, so this is not supposed to happen. */ + + INIT = 0, /**< The device has just been created. */ + RESET = 1, /**< Received a reset from the usb host. */ + RESET_DONE = 2, /**< The reset from the host is over. */ + SET_ADDRESS_RCVD = 3, /**< Set address has been received and set to the register. */ + ENUMERATED = 4, /**< The device has received SET_CONFIGURATION command, endpoints were configured. */ } usb_device_state_t; +/** + * @struct usb_fifo_t + * Represents fifo memory area. You can write/read any data here, it behaves the same. + * Every read value is popped, every written value is enqueued to the fifo. + */ typedef struct { - volatile uint32_t data[128]; + volatile uint32_t data[128]; /**< Data memory. Every address here is the same. */ } usb_fifo_t; struct usb_class_header_t; struct usb_device_t; typedef struct usb_device_t usb_device_t; +/** + * @struct usb_class_vtable_t + * A vtable struct for usb class, + * with various callback functions. + * Every usb device has to have a class specified. + * The class provides the configuration of interfaces, endpoints, + * the pointers, and way to handle interrupts AFTER the device is initialized. + */ typedef struct { + +/** + * @brief A function that initializes class object. + * @details Called inside of usb_device_init, to set the device->class field. Few information about the device are given. + * @param[in,out] device The usb device created so far. device->class is not valid. + * @param[in] id_vendor Id of the vendor. + * @param[in] id_product Id of the product. + * @param[out] vendor_name Name of the vendor. + * @param[out] product_name The name of product. + * @param[in] serial_number The serial number. + * @param[out] serial_name Written representation of the serial number. + * @return The class itself, having header as first field. This will be saved to device->class. + */ 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); + +/** + * @brief Configure the endpoint zero, and send configuration, including writing to the fifo. + * @details This is called when GET_DESCRIPTOR of CONFIGURATION type is received. This should + * configure the sizes, write to the fifo, and enable the endpoint. + * @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); + +/** + * @brief Set all endpoints except for endpoint 0 based on the configuration. + * @details This is called after SET_CONFIGURATION command is received. + * @param[in,out] device The usb device. + * @param[in] configuration Number of the configuration, parameter from SET_CONFIGURATION. + */ void (*setup_endpoints)(usb_device_t* device, uint8_t configuration); + +/** + * @brief Reset all used endpoints, except endpoint 0. + * @details This is called after usb RESET. + * @param[in,out] device The usb device. + */ void (*reset_endpoints)(usb_device_t* device); +/** + * @brief Callback called when reset is detected. + * @param[in,out] device The usb device. + */ void (*reset_callback)(usb_device_t* device); - void (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd); + +/** + * @brief Received unhandled setup packet. + * @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. + */ + uint8_t (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd); + +/** + * @brief Received SET_CONFIGURATION, Endpoints set up. + * @param[in,out] device The usb device. + */ void (*enumeration_done_callback)(usb_device_t* device); + +/** + * @brief Callback on tx fifo empty. + * @param[in,out] device The usb device. + * @param[in] endpoint The number of the endpoint. + */ void (*txfifo_empty_callback)(usb_device_t* device, uint8_t endpoint); + +/** + * @brief Callback on rx fifo empty. + * @param[in,out] device The usb device. + * @param[in] endpoint The number of the endpoint. + */ void (*rxfifo_empty_callback)(usb_device_t* device, uint8_t endpoint); + +/** + * @brief Callback on transmission done - all data were sent from configured size. + * @param[in,out] device The usb device. + * @param[in] endpoint The number of the endpoint. + * @return Description + */ void (*transmit_done_callback)(usb_device_t* device, uint8_t endpoint); + +/** + * @brief Callback on nak being effective. + * @param[in,out] device The usb device. + * @param[in] endpoint The number of the endpoint. + */ void (*nak_callback)(usb_device_t* device, uint8_t endpoint); + +/** + * @brief Callback on nyet being effective. + * @param[in,out] device The usb device. + * @param[in] endpoint The number of the endpoint. + */ void (*nyet_callback)(usb_device_t* device, uint8_t endpoint); } usb_class_vtable_t; +/** + * @struct usb_interface_t + * Descriptors of all interfaces the device has + */ typedef struct { usb_interface_descriptor_t interface_descriptor; - uint8_t endpoint_descriptors_count; - usb_endpoint_descriptor_t *endpoint_descriptors; + uint8_t endpoint_descriptors_count; /**< Number of elements in @see endpoint_descriptors */ + usb_endpoint_descriptor_t *endpoint_descriptors; /**< A list of endpoint descriptors. */ } usb_interface_t; +/** + * @struct usb_class_header_t + * A header of every class, should be the first field of the class. + * The header contains common information. It is assumed the device + * has only one configuration. This is a limitation from our side, usb + * can support multiple alternate configurations. + */ 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; + uint8_t interfaces_count; /**< Number of elements in @see interfaces */ + usb_interface_t *interfaces; /**< A list of interfaces */ usb_string_descriptor_zero_t string_descriptor_zero; - usb_unicode_string_descriptor_t *string_descriptors; + usb_unicode_string_descriptor_t *string_descriptors; /**< A list of string descriptors. There is no count of those, since not all are sent. The host will request a specific one. Currently it is not checked if the given descriptors exists, which can lead to wrong memory part being read! */ }; typedef struct usb_class_header_t usb_class_header_t; +/** + * @enum usb_setup_command_stage_t + * Stages of setup command receival + */ 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, + SETUP_STAGE_NONE, /**< No setup command being handled. */ + SETUP_STAGE_RCVD_SETUP_PACKET, /**< Received a setup packet, saved to memory. */ + SETUP_STAGE_AWAITING_RESPONSE, /**< Handled a setup packet, waiting for data from the host. */ + SETUP_STAGE_SENDING_RESPONSE, /**< Handling setup packet by sending response to host. */ + SETUP_STAGE_SENDING_ACK, /**< Handling setup packet by sending acknowledge. */ + SETUP_STAGE_AWAITING_ACK, /**< Handled a setup packet, sent data, waiting for acknowledge */ } usb_setup_command_stage_t; +/** + * @struct usb_device_t + * Struct representing usb device. + */ struct usb_device_t { - USB_OTG_GlobalTypeDef *core; - USB_OTG_DeviceTypeDef *device; + USB_OTG_GlobalTypeDef *core; /**< Address of the usb otg peripheral core. */ + USB_OTG_DeviceTypeDef *device; /**< Address of the usb otg peripheral device. */ - USB_OTG_OUTEndpointTypeDef *out; - USB_OTG_INEndpointTypeDef *in; + USB_OTG_OUTEndpointTypeDef *out; /**< Address of the usb otg peripheral out endpoints, one by one, so out[1] will point to endpoint 1. */ + USB_OTG_INEndpointTypeDef *in; /**< Address of the usb otg peripheral in endpoints, one by one, so in[2] will point to endpoint 2. */ - usb_fifo_t *fifos; + usb_fifo_t *fifos; /**< The fifos, one by one, so fifos[1] will point to fifo for endpoint1 */ - usb_class_vtable_t vt; - usb_class_header_t* class; + usb_class_vtable_t vt; /**< The vtable for the class. */ + usb_class_header_t* class; /**< The state of the class, being unknown type, with header as first field. */ - usb_device_state_t state; + usb_device_state_t state; /**< The current status of the device. */ - // 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]; + // 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. */ }; // has configuration etc +/** + * @enum usb_device_slot_t + * The microcontroller has two peripherals, + * this says which one to initialize. This is important, + * since there can be only two devices at one point, + * and to know what device structure to use on interrupt + * received, there is a global variable for given slot. + */ typedef enum { - USB_OTG_HS1 = 0, - USB_OTG_FS2 = 1, + USB_OTG_HS1 = 0, /**< High speed usb otg at slot 1 */ + USB_OTG_FS2 = 1, /**< Full speed usb otg at slot 2 */ } usb_device_slot_t; extern usb_device_t usb_devices[2]; +/** + * @brief Create and initialize usb device structure. + * @details The function will initialize a device, its peripheral addresses, class, and return it. + * It is expected that the classes will have further initialization functions + * that need to be called before further proceeding with the initialization sequence. + * So something like @see usb_cdc_acm_configure has to be called, + * and only after that @see usb_device_setup can be called. + * @param[in] slot The slot of the device, used to get the peripheral addresses, and to fill in the global variable. + * @param[out] class The class vtable. init will be called on this class as part of this function to initialize device->class. + * @param[in] id_vendor The id of the vendor. + * @param[in] id_product The id of the product. + * @param[out] vendor_name The name of vendor + * @param[out] product_name The name of product + * @param[in] serial_number The number of serial to ? + * @param[out] serial_name The name of serial + * @return The usb device. + */ 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); +/** + * @brief Setup device endpoint 0 + * @details This function handles the setup of the device, it will initialize endpoint zero, and disable the soft disconnect. + * That means the device will get resetted by the host afterwards, if it's connected. And the setup stage will be initiated by + * the host. + * @param[in,out] device The usb device to call setup on. + */ void usb_device_setup(void* device); + +/** + * @brief Wait for the device to get enumerated + * @details After initializing the device, this waits for all setup commands to advance + * to SET_CONFIGURATION command. + * @param[in,out] device The usb device to wait for. + */ void usb_device_wait_for_handshake(void* device); #endif // USB_DEVICE_H diff --git a/include/usb_device_cdc.h b/include/usb_device_cdc.h index 81c4a94..ba7dbbd 100644 --- a/include/usb_device_cdc.h +++ b/include/usb_device_cdc.h @@ -7,6 +7,15 @@ #define USB_CLASS_DATA_CODE 0x0A #define USB_SUBCLASS_CDC_ACM_CODE 0x02 +/** + * @var USB_CLASS_CDC_ACM + * @brief Vtable of usb cdc acm class. + * @see usb_cdc_acm_configure to call after usb_device_init call to configure all descriptors. + * The usb cdc class is a standard usb class for communications and data transfer. + * acm stands for Abstract Control Model. It is used mainly in older modems to convert uart + * to usb. Here cdc acm is used since it is widely supported on computers, but there are no + * supported cdc acm commands. AT commands are not handled. + */ extern usb_class_vtable_t USB_CLASS_CDC_ACM; typedef enum { @@ -99,7 +108,7 @@ usb_class_header_t* usb_device_cdc_init(usb_device_t *device, 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); +uint8_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); @@ -107,8 +116,14 @@ void usb_device_cdc_transmit_done_callback(usb_device_t* device, uint8_t endpoin 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 - ); +/** + * @brief Configures cdc acm descriptors + * @details All the descriptors for cdc acm device are initialized here. + * That means that after call to this function, usb_device_setup can be safely + * called, and all setup commands handled. + * @param[in,out] device The usb device. + */ +void usb_device_cdc_acm_configure(usb_device_t *device); diff --git a/src/main.c b/src/main.c index ffa9e91..308c525 100644 --- a/src/main.c +++ b/src/main.c @@ -155,12 +155,13 @@ void main() // TODO: ? pin_into_input_highspeed(otg_hs_overcurrent); - void *usb_otg = usb_device_init(USB_OTG_HS1, &USB_CLASS_CDC_ACM, + void *usb_dev = usb_device_init(USB_OTG_HS1, &USB_CLASS_CDC_ACM, 0x1234, 0x1111, "Frantisek Bohacek", "Display", 1, NULL); - usb_device_setup(usb_otg); + usb_device_cdc_acm_configure(usb_dev); + usb_device_setup(usb_dev); - usb_device_wait_for_handshake(usb_otg); + usb_device_wait_for_handshake(usb_dev); while(1) {} } diff --git a/src/usb_device_cdc.c b/src/usb_device_cdc.c index 11e0611..09d15cc 100644 --- a/src/usb_device_cdc.c +++ b/src/usb_device_cdc.c @@ -311,9 +311,10 @@ void usb_device_cdc_send_configuration(usb_device_t *device, enp0->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA; } -void usb_device_cdc_setup_packet_callback(usb_device_t *device, +uint8_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; } void usb_device_cdc_enumeration_done_callback(usb_device_t *device) { // TODO start the application somehow -- 2.48.1