#include #include #include "generic.h" #include #ifndef USB_H #define USB_H // NOTE: fields here have to be kept in same order, // and care must be taken the structs are not padded. // They are from the usb standard. #define USB_SUBLANG_ENGLISH_US 0x01 #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; /**< 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; /**< 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; /**< 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, /**< 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, /**< 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, /**< 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; /**< 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, /**< 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, /**< 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 { DESCRIPTOR_DEVICE = 1, DESCRIPTOR_CONFIGURATION = 2, DESCRIPTOR_STRING = 3, DESCRIPTOR_INTERFACE = 4, DESCRIPTOR_ENDPOINT = 5, DESCRIPTOR_DEVICE_QUALIFIER = 6, DESCRIPTOR_OTHER_SPEED_CONFIGURATION = 7, 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; /**< 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; /**< Full length of this descriptor. */ uint8_t bDescriptorType; /**< Type of the descriptor - usb_descriptor_type_t */ } usb_descriptor_t; typedef struct { usb_descriptor_t header; uint16_t bcdUSB; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bMaxPacketSize0; uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t iManufacturer; uint8_t iProduct; uint8_t iSerialNumber; uint8_t bNumConfigurations; } usb_device_descriptor_t; _Static_assert(sizeof(usb_device_descriptor_t) == 18, "Size check"); typedef struct { usb_descriptor_t header; uint16_t bcdUSB; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bMaxPacketSize0; uint8_t bNumConfigurations; uint8_t bReserved; } usb_device_qualifier_t; _Static_assert(sizeof(usb_device_qualifier_t) == 10, "Size check"); typedef struct { uint8_t reserved1 : 1; uint8_t self_powered : 1; uint8_t remote_wakeup : 1; uint8_t reserved_zeros: 5; } usb_configuration_descriptor_attributes; typedef struct __attribute__((packed)) { usb_descriptor_t header; uint16_t wTotalLength; uint8_t bNumInterfaces; uint8_t bConfigurationValue; uint8_t iConfiguration; usb_configuration_descriptor_attributes bmAttributes; uint8_t bMaxPower; } usb_configuration_descriptor_t; _Static_assert(sizeof(usb_configuration_descriptor_t) == 9, "Size check"); typedef usb_configuration_descriptor_t usb_other_speed_configuration_t; typedef struct __attribute__((packed)) { usb_descriptor_t header; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bNumEndpoints; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t iInterface; } usb_interface_descriptor_t; _Static_assert(sizeof(usb_interface_descriptor_t) == 9, "Size check"); typedef enum { USB_ENDPOINT_OUT = 0, USB_ENDPOINT_IN = 1, } usb_endpoint_direction_t; typedef struct __attribute__((packed)) { uint8_t endpoint_number : 4; uint8_t reserved : 3; usb_endpoint_direction_t direction : 1; } usb_endpoint_address_t; typedef enum { USB_ENDPOINT_TYPE_CONTROL = 0, USB_ENDPOINT_TYPE_ISOCHRONOUS = 1, USB_ENDPOINT_TYPE_BULK = 2, USB_ENDPOINT_TYPE_INTERRUPT = 3, } usb_endpoint_transfer_type_t; typedef enum { USB_ENDPOINT_SYNC_NO_SYNC = 0, USB_ENDPOINT_SYNC_ASYNCHRONOUS = 1, USB_ENDPOINT_SYNC_ADAPTIVE = 2, USB_ENDPOINT_SYNC_SYNCHRONOUS = 3, } usb_endpoint_synchronization_type_t; typedef enum { USB_ENDPOINT_USAGE_DATA = 0, USB_ENDPOINT_USAGE_FEEDBACK = 1, USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK = 2, USB_ENDPOINT_USAGE_RESERVED = 3, } usb_endpoint_usage_type_t; typedef struct __attribute__((packed)) { usb_endpoint_transfer_type_t transfer_type : 2; usb_endpoint_synchronization_type_t synchronization_type : 2; usb_endpoint_usage_type_t usage_type : 2; uint8_t reserved_zeros : 2; } usb_endpoint_attributes_t; typedef struct __attribute__((packed)) { usb_descriptor_t header; usb_endpoint_address_t bEndpointAddress; usb_endpoint_attributes_t bmAttributes; uint16_t wMaxPacketSize; uint8_t bInterval; } usb_endpoint_descriptor_t; _Static_assert(sizeof(usb_endpoint_descriptor_t) == 7, "Size check"); #pragma GCC diagnostic ignored "-Wpadded" typedef struct { // uint8_t bLength; // lengto of wLANGID + 2 usb_descriptor_t header; uint16_t *wLANGID; // should have length equal to bLength - 2 } usb_string_descriptor_zero_t; typedef struct { usb_descriptor_t header; uint16_t *bString; } usb_unicode_string_descriptor_t; // 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); void usb_generic_fill_fifo(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, volatile uint32_t *fifo_tx_target); task_result_t usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size, uint16_t max_packet_size); /** * @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. */ task_result_t usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint, uint8_t *data, uint16_t size, volatile uint32_t *fifo_tx_target); bool usb_is_inendpoint_ready(USB_OTG_INEndpointTypeDef *endpoint); bool usb_check_fifo_space(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size); /** * @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. */ task_result_t 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. */ task_result_t usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_descriptor_t *descriptor, uint16_t max_size, 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. */ task_result_t usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_device_descriptor_t *descriptor, uint16_t max_size, 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. */ task_result_t usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor, uint16_t max_size, 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. */ task_result_t usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_interface_descriptor_t *descriptor, uint16_t max_size, 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. */ task_result_t usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint, usb_endpoint_descriptor_t *descriptor, uint16_t max_size, 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. */ task_result_t usb_send_string_descriptor_zero( USB_OTG_INEndpointTypeDef *endpoint, usb_string_descriptor_zero_t *string_descriptor, uint16_t max_size, 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. */ task_result_t usb_send_unicode_string_descriptor( USB_OTG_INEndpointTypeDef *endpoint, usb_unicode_string_descriptor_t *string_descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target); task_result_t usb_send_unicode_string_descriptor( USB_OTG_INEndpointTypeDef *endpoint, usb_unicode_string_descriptor_t *string_descriptor, uint16_t max_size, volatile uint32_t *fifo_tx_target); void usb_flush_tx_fifo(USB_OTG_GlobalTypeDef* core, uint16_t fifo_num); void usb_flush_rx_fifo(USB_OTG_GlobalTypeDef* core); #endif // USB_H