#include <stm32h747xx.h>
#include "usb.h"
#include "generic.h"
#include "queue.h"
typedef uint16_t char16_t;
#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, /**< 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. */
USB_DEV_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. */
USB_DEV_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[1024]; /**< 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;
typedef enum {
PACKET_GLOBAL_OUT_NAK = 1,
PACKET_OUT_DATA_PACKET_RCVD = 2,
PACKET_OUT_TRANSFER_COMPLETED = 3,
PACKET_SETUP_TRANSACTION_COMPLETED = 4,
PACKET_SETUP_DATA_PACKET_RECEIVED = 6,
} packet_status_t;
typedef enum {
DATA0 = 0,
DATA1 = 1,
DATA2 = 2,
MDATA = 3
} packet_dpid_t;
typedef struct {
packet_status_t packet_status;
packet_dpid_t dpid;
uint8_t byte_count;
uint8_t endpoint_num;
uint8_t status_phase_start;
} packet_info_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,
char16_t *vendor_name, char16_t *product_name,
uint16_t serial_number, char16_t* 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
*/
task_result_t (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd);
/**
* @brief Set all endpoints except for endpoint 0 based on the configuration.
* @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);
/**
* @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 A result of the callback, successfull or error.
*/
task_result_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 full.
* @param[in,out] device The usb device.
* @param[in] endpoint The number of the endpoint.
*/
void (*rxfifo_full_callback)(usb_device_t* device, uint8_t endpoint);
/**
* @brief Callback on tx 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 (*tx_done_callback)(usb_device_t* device, uint8_t endpoint);
/**
* @brief Callback on rx 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 (*rx_done_callback)(usb_device_t* device, uint8_t endpoint);
void (*received_data_callback)(usb_device_t* device, packet_info_t* packet);
/**
* @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; /**< 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; /**< 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; /**< 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, /**< No setup command being handled. */
SETUP_STAGE_RCVD_SETUP_PACKET, /**< Received a setup packet, saved to memory. */
SETUP_STAGE_AWAITING_DATA, /**< 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;
#define MAX_SETUP_PACKETS 3
typedef struct {
queue_t* received_setup_commands;
uint8_t* rcvd_data;
uint16_t rcvd_count;
uint16_t rcvd_awaiting;
usb_setup_command_stage_t stage; /**< Current stage status. */
uint8_t detected_setup_errors; /**< Number of errors when handling setup commands. This should be zero. */
} usb_device_setup_t;
/**
* @struct usb_device_t
* Struct representing usb device.
*/
struct usb_device_t {
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; /**< 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; /**< The fifos, one by one, so fifos[1] will point to fifo for endpoint1 */
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; /**< The current status of the device. */
usb_device_setup_t setup;
};
// 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, /**< 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,
char16_t *vendor_name, char16_t *product_name,
uint16_t serial_number, char16_t* 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