~ruther/stm32h747i-disco-usb-image-viewer

02e7e4dfbf77f0e6aac3abd7ab75632233c347e0 — Rutherther 5 months ago c8fe27c
docs: add documentation comments to most functions
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

Do not follow this link