#include #include "usb.h" #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[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); /** * @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; /**< 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_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; /**< 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. */ // 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, /**< 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