M include/exti.h => include/exti.h +22 -0
@@ 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
M include/pin.h => include/pin.h +6 -0
@@ 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);
M include/registers.h => include/registers.h +65 -1
@@ 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
M include/usb.h => include/usb.h +179 -44
@@ 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
M include/usb_device.h => include/usb_device.h +197 -40
@@ 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
M include/usb_device_cdc.h => include/usb_device_cdc.h +18 -3
@@ 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);
M src/main.c => src/main.c +4 -3
@@ 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) {}
}
M src/usb_device_cdc.c => src/usb_device_cdc.c +2 -1
@@ 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