From 466285a21c320c5a1e1c606789425d029b2356f7 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Fri, 24 Mar 2017 14:19:24 +0100 Subject: [PATCH] trezorhal: WIP usb rewrite --- micropython/trezorhal/usb.c | 507 ++++++++++++++++++++++++++++++++---- micropython/trezorhal/usb.h | 167 +++++++++++- 2 files changed, 616 insertions(+), 58 deletions(-) diff --git a/micropython/trezorhal/usb.c b/micropython/trezorhal/usb.c index 211ca852a9..2920860139 100644 --- a/micropython/trezorhal/usb.c +++ b/micropython/trezorhal/usb.c @@ -2,41 +2,464 @@ #include "usbd_core.h" #include "usbd_desc.h" -#include "usbd_cdc_msc_hid.h" -#include "usbd_cdc_interface.h" -#include "usbd_hid_interface.h" -USBD_HandleTypeDef hUSBDDevice; +#define USBD_MAX_NUM_INTERFACES (3) + +#define USB_MAX_CONFIG_DESC_SIZE (128) +#define USB_MAX_STR_DESC_SIZE (256) + +#define USB_DESC_TYPE_HID (0x21) +#define USB_DESC_TYPE_REPORT (0x22) + extern PCD_HandleTypeDef pcd_fs_handle; -extern PCD_HandleTypeDef pcd_hs_handle; -int usb_init(void) { - const uint16_t vid = 0x1209; - const uint16_t pid = 0x53C1; +static USBD_HandleTypeDef usb_dev_handle; - USBD_HID_ModeInfoTypeDef hid_info = { - .subclass = 0, - .protocol = 0, - .max_packet_len = 64, - .polling_interval = 1, - .report_desc = (const uint8_t*)"\x06\x00\xff\x09\x01\xa1\x01\x09\x20\x15\x00\x26\xff\x00\x75\x08\x95\x40\x81\x02\x09\x21\x15\x00\x26\xff\x00\x75\x08\x95\x40\x91\x02\xc0", - .report_desc_len = 34, - }; +static usb_device_descriptor_t usb_dev_desc; +static uint8_t usb_config_buf[USB_MAX_CONFIG_DESC_SIZE]; +static uint8_t usb_str_buf[USB_MAX_STR_DESC_SIZE]; +static const usb_string_descriptor_t usb_langid_str_desc = { + .bLength = USB_LEN_LANGID_STR_DESC, + .bDescriptorType = USB_DESC_TYPE_STRING, + .wData = USB_LANGID_ENGLISH_US, +}; +static usb_config_descriptor_t *usb_config_desc = (usb_config_descriptor_t *)(usb_config_buf); +static usb_interface_descriptor_t *usb_next_iface_desc; +static usb_string_table_t usb_str_table; +static usb_iface_t usb_ifaces[USBD_MAX_NUM_INTERFACES]; - USBD_SetVIDPIDRelease(vid, pid, 0x0200, 0); - if (USBD_SelectMode(USBD_MODE_CDC_HID, &hid_info) != 0) { - return 1; - } - USBD_Init(&hUSBDDevice, (USBD_DescriptorsTypeDef*)&USBD_Descriptors, 0); // 0 == full speed - USBD_RegisterClass(&hUSBDDevice, &USBD_CDC_MSC_HID); - USBD_CDC_RegisterInterface(&hUSBDDevice, (USBD_CDC_ItfTypeDef*)&USBD_CDC_fops); - USBD_HID_RegisterInterface(&hUSBDDevice, (USBD_HID_ItfTypeDef*)&USBD_HID_fops); - USBD_Start(&hUSBDDevice); +int usb_init(const usb_dev_info_t *dev_info) { + + // Device descriptor + usb_dev_desc.bLength = USB_LEN_DEV_DESC; + usb_dev_desc.bDescriptorType = USB_DESC_TYPE_DEVICE; + usb_dev_desc.bcdUSB = 0x00ef; + usb_dev_desc.bDeviceClass = 0xef; // Composite Device Class + usb_dev_desc.bDeviceSubClass = 0x02; // Common Class + usb_dev_desc.bDeviceProtocol = 0x01; // Interface Association Descriptor + usb_dev_desc.bMaxPacketSize = USB_MAX_EP0_SIZE; + usb_dev_desc.idVendor = dev_info->vendor_id; + usb_dev_desc.idProduct = dev_info->product_id; + usb_dev_desc.bcdDevice = dev_info->release_num; + usb_dev_desc.iManufacturer = USBD_IDX_MFC_STR; // Index of manufacturer string + usb_dev_desc.iProduct = USBD_IDX_PRODUCT_STR; // Index of product string + usb_dev_desc.iSerialNumber = USBD_IDX_SERIAL_STR; // Index of serial number string + usb_dev_desc.bNumConfigurations = 0x01; + + // Configuration descriptor + usb_config_desc->bLength = USB_LEN_CFG_DESC; + usb_config_desc->bDescriptorType = USB_DESC_TYPE_CONFIGURATION; + usb_config_desc->wTotalLength = USB_LEN_CFG_DESC; + usb_config_desc->bNumInterfaces = 0x00; + usb_config_desc->bConfigurationValue = 0x01; // Configuration value + usb_config_desc->iConfiguration = 0x00; // Index of string descriptor describing the configuration + usb_config_desc->bmAttributes = 0x80; // 0x80 = bus powered; 0xc0 = self powered + usb_config_desc->bMaxPower = 0xfa; // In units of 2mA + + // Pointer to interface descriptor data, see: usb_desc_alloc_iface, usb_desc_add_iface + usb_next_iface_desc = (usb_interface_descriptor_t *)(usb_config_buf + usb_config_desc->wTotalLength); + + // Reset the iface state map + usb_iface_list = {}; + + // String table + usb_str_table->manufacturer_str = dev_info->manufacturer_str; + usb_str_table->product_str = dev_info->product_str; + usb_str_table->serial_str = dev_info->serial_number_str; + usb_str_table->config_str = dev_info->configuration_str; + usb_str_table->interface_str = dev_info->interface_str; + + USBD_Init(&usb_dev_handle, (USBD_DescriptorsTypeDef*)&usb_descriptors, 0); // 0 == full speed + USBD_RegisterClass(&usb_dev_handle, &usb_class); return 0; } -#if defined(USE_USB_FS) +int usb_start(void) { + return USBD_Start(&usb_dev_handle); +} + +int usb_stop(void) { + return USBD_Stop(&usb_dev_handle); +} + +static uint8_t *usb_get_dev_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + *length = sizeof(usb_dev_desc); + return (uint8_t *)usb_dev_desc; +} + +static uint8_t *usb_get_langid_str_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + *length = sizeof(usb_langid_str_desc); + return (uint8_t *)usb_langid_str_desc; +} + +static uint8_t *usb_get_manufacturer_str_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + USBD_GetString(usb_str_table.manufacturer_str, usb_str_buf, length); + return usb_str_buf; +} + +static uint8_t *usb_get_product_str_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + USBD_GetString(usb_str_table.product_str, usb_str_buf, length); + return usb_str_buf; +} + +static uint8_t *usb_get_serial_str_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + USBD_GetString(usb_str_table.serial_str, usb_str_buf, length); + return usb_str_buf; +} + +static uint8_t *usb_get_config_str_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + USBD_GetString(usb_str_table.config_str, usb_str_buf, length); + return usb_str_buf; +} + +static uint8_t *usb_get_interface_str_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + USBD_GetString(usb_str_table.interface_str, usb_str_buf, length); + return usb_str_buf; +} + +static const USBD_DescriptorsTypeDef usb_descriptors = { + .GetDeviceDescriptor = usb_get_dev_descriptor, + .GetLangIDStrDescriptor = usb_get_langid_str_descriptor, + .GetManufacturerStrDescriptor = usb_get_manufacturer_str_descriptor, + .GetProductStrDescriptor = usb_get_product_str_descriptor, + .GetSerialStrDescriptor = usb_get_serial_str_descriptor, + .GetConfigurationStrDescriptor = usb_get_config_str_descriptor, + .GetInterfaceStrDescriptor = usb_get_interface_str_descriptor, +}; + +static void *usb_desc_alloc_iface(size_t desc_len) { + if (usb_config_desc->wTotalLength + desc_len > USB_MAX_CONFIG_DESC_SIZE) { + return NULL; // Not enough space in the descriptor + } + if (usb_config_desc->bNumInterfaces + 1 >= USBD_MAX_NUM_INTERFACES) + return NULL; // Already using all the interfaces + } + return usb_next_iface_desc; +} + +static void usb_desc_add_iface(size_t desc_len) { + usb_config_desc->bNumInterfaces++; + usb_config_desc->wTotalLength += desc_len; + usb_next_iface_desc = (usb_interface_descriptor_t *)(usb_config_buf + usb_config_desc->wTotalLength); +} + +/* usb_hid_add adds and configures new USB HID interface according to + * configuration options passed in `info`. */ +int usb_hid_add(const usb_hid_info_t *info) { + usb_hid_descriptor_block_t *d = usb_desc_alloc_iface(sizeof(*d)); + + if (!d) { + return 1; // Not enough space in the configuration descriptor + } + + if ((info->iface_num < usb_config_desc->bNumInterfaces) + (info->iface_num >= USBD_MAX_NUM_INTERFACES) || + ((info->ep_in & 0x80) == 0) || + ((info->ep_out & 0x80) != 0)) { + + return 1; // Invalid configuration values + } + + // Interface descriptor + d->iface.bLength = USB_LEN_IF_DESC; + d->iface.bDescriptorType = USB_DESC_TYPE_INTERFACE; + d->iface.bInterfaceNumber = info->iface_num; + d->iface.bAlternateSetting = 0x00; + d->iface.bNumEndpoints = 0x02; + d->iface.bInterfaceClass = 0x03; // HID Class + d->iface.bInterfaceSubClass = info->subclass; + d->iface.nInterfaceProtocol = info->protocol; + d->iface.iInterface = 0x00; // Index of string descriptor describing the interface + + // HID descriptor + d->hid.bLength = sizeof(usb_hid_descriptor_t); + d->hid.bDescriptorType = USB_DESC_TYPE_HID; + d->hid.bcdHID = 0x1101; // HID Class Spec release number + d->hid.bCountryCode = 0x00; // Hardware target country + d->hid.bNumDescriptors = 0x01; // Number of HID class descriptors to follow + d->hid.bReportDescriptorType = USB_DESC_TYPE_REPORT; + d->hid.wReportDescriptorLength = info->report_desc_len; + + // IN endpoint (sending) + d->ep_in.bLength = USB_LEN_EP_DESC; + d->ep_in.bDescriptorType = USB_DESC_TYPE_ENDPOINT; + d->ep_in.bEndpointAddress = info->ep_in; + d->ep_in.bmAttributes = USBD_EP_TYPE_INTR; + d->ep_in.wMaxPacketSize = info->max_packet_len; + d->ep_in.bInterval = info->polling_interval; + + // OUT endpoint (receiving) + d->ep_out.bLength = USB_LEN_EP_DESC; + d->ep_out.bDescriptorType = USB_DESC_TYPE_ENDPOINT; + d->ep_out.bEndpointAddress = info->ep_out; + d->ep_out.bmAttributes = USBD_EP_TYPE_INTR; + d->ep_out.wMaxPacketSize = info->max_packet_len; + d->ep_out.bInterval = info->polling_interval; + + // Config descriptor + usb_desc_add_iface(sizeof(*d)); + + // Interface state + usb_iface_t *i = &usb_ifaces[info->iface_num]; + i->hid.ep_in = info->ep_in; + i->hid.ep_out = info->ep_out; + i->hid.rx_buffer = info->hid_buffer; + i->hid.max_packet_len = info->max_packet_len; + i->hid.report_desc_len = info->report_desc_len; + i->hid.report_desc = info->report_desc; + i->hid.desc_block = d; +} + +int usb_hid_can_read(uint8_t iface_num) { + return ((iface_num < USBD_MAX_NUM_INTERFACES) && + (usb_ifaces[iface_num].type == USB_IFACE_TYPE_HID) && + (usb_ifaces[iface_num].hid.rx_buffer_len > 0) && + usb_dev_handle.dev_state == USBD_STATE_CONFIGURED); +} + +int usb_hid_can_write(uint8_t iface_num) { + return ((iface_num < USBD_MAX_NUM_INTERFACES) && + (usb_ifaces[iface_num].type == USB_IFACE_TYPE_HID) && + (usb_ifaces[iface_num].hid.is_idle) && + usb_dev_handle.dev_state == USBD_STATE_CONFIGURED); +} + +int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len) { + if (iface_num >= USBD_MAX_NUM_INTERFACES) { + return -1; // Invalid interface number + } + if (usb_ifaces[iface_num].type != USB_IFACE_TYPE_HID) { + return -1; // Invalid interface number + } + usb_hid_state_t *state = &usb_ifaces[iface_num].hid; + if (len < state->rx_buffer_len) { + return 0; // Not enough data in the read buffer + } + + memcpy(buf, state->rx_buffer, state->rx_buffer_len); + + // Clear NAK to indicate we are ready to read more data + usb_ep_clear_nak(&usb_dev_handle, state->ep_out); + + return state->rx_buffer_len; +} + +int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) { + if (iface_num >= USBD_MAX_NUM_INTERFACES) { + return -1; // Invalid interface number + } + if (usb_ifaces[iface_num].type != USB_IFACE_TYPE_HID) { + return -1; // Invalid interface number + } + usb_hid_state_t *state = &usb_ifaces[iface_num].hid; + + state->in_idle = 0; + USBD_LL_Transmit(&usb_dev_handle, state->ep_in, (uint8_t *)buf, (uint16_t)len); +} + +static int usb_hid_class_init(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t cfg_idx) { + // Open endpoints + USBD_LL_OpenEP(dev, state->ep_in, USBD_EP_TYPE_INTR, state->max_packet_len); + USBD_LL_OpenEP(dev, state->ep_out, USBD_EP_TYPE_INTR, state->max_packet_len); + + // Reset the state + state->in_idle = 1; + state->protocol = 0; + state->idle_state = 0; + state->alt_setting = 0; + + // Prepare Out endpoint to receive next packet + USBD_LL_PrepareReceive(dev, hid_out_ep, state->rx_buffer, state->max_packet_len); +} + +static int usb_hid_class_deinit(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t cfg_idx) { + // Close endpoints + USBD_LL_CloseEP(dev, state->ep_in); + USBD_LL_CloseEP(dev, state->ep_out); +} + +static int usb_hid_class_setup(USBD_HandleTypeDef *dev, usb_hid_state_t *state, USBD_SetupReqTypedef *req) { + switch (req->bmRequest & USB_REQ_TYPE_MASK) { + + // Class request + case USB_REQ_TYPE_CLASS: + switch (req->bRequest) { + + case HID_REQ_SET_PROTOCOL: + state->protocol = req->wValue; + break; + + case HID_REQ_GET_PROTOCOL: + USBD_CtlSendData(dev, &state->protocol, sizeof(state->protocol)); + break; + + case HID_REQ_SET_IDLE: + state->idle_rate = req->wValue >> 8; + break; + + case HID_REQ_GET_IDLE: + USBD_CtlSendData(dev, &state->idle_rate, sizeof(state->idle_rate)); + break; + + default: + USBD_CtlError(dev, req); + return USBD_FAIL; + } + break; + + // Interface & Endpoint request + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) { + + case USB_REQ_SET_INTERFACE: + state->alt_setting = req->wValue; + break; + + case USB_REQ_GET_INTERFACE: + USBD_CtlSendData(dev, &state->alt_setting, sizeof(state->alt_setting)); + break; + + case USB_REQ_GET_DESCRIPTOR: + switch (req->wValue >> 8) { + + case USB_DESC_TYPE_HID: + USBD_CtlSendData(dev, (uint8_t*)&state->desc_block->hid, MIN(req->wLength, sizeof(state->desc_block->hid))); + break; + + case USB_DESC_TYPE_REPORT: + USBD_CtlSendData(dev, state->report_desc, MIN(req->wLength, state->report_desc_len)); + break; + } + break; + } + break; + } + return USBD_OK; +} + +static uint8_t usb_hid_class_data_in(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t ep_num) { + if (ep_num == state->ep_in) { + // Ensure that the FIFO is empty before a new transfer, + // this condition could be caused by a new transfer + // before the end of the previous transfer. + state->in_idle = 1; + } +} + +static uint8_t usb_hid_class_data_out(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t ep_num) { + if (ep_num == state->ep_out) { + // User should provide state->rx_buffer_len that is big + // enough for state->max_packet_len bytes. + state->rx_buffer_len = USBD_LL_GetRxDataSize(dev, ep_num); + + if (state->rx_buffer_len > 0) { + // Block the OUT EP until we process received data + usb_ep_set_nak(dev, ep_num); + } + } +} + +static uint8_t usb_class_init(USBD_HandleTypeDef *dev, uint8_t cfg_idx) { + for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { + switch (usb_ifaces[i].type) { + case USB_IFACE_TYPE_HID: + usb_hid_class_init(dev, &usb_ifaces[i].hid, cfg_idx); + break; + } + } + return USBD_OK; +} + +static uint8_t usb_class_deinit(USBD_HandleTypeDef *dev, uint8_t cfg_idx) { + for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { + switch (usb_ifaces[i].type) { + case USB_IFACE_TYPE_HID: + usb_hid_class_deinit(dev, &usb_ifaces[i].hid, cfg_idx); + break; + } + } + return USBD_OK; +} + +static uint8_t usb_class_setup(USBD_HandleTypeDef *dev, USBD_SetupReqTypedef *req) { + if (((req->bmRequest & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_CLASS) && + ((req->bmRequest & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)) { + return USBD_OK; + } + if (req->wIndex >= USBD_MAX_NUM_INTERFACES) { + return USBD_FAIL; + } + switch (usb_ifaces[req->wIndex].type) { + case USB_IFACE_TYPE_HID: + return usb_hid_class_setup(dev, &usb_ifaces[req->wIndex].hid, req); + default: + return USBD_FAIL; + } +} + +static uint8_t usb_class_ep0_rx_ready(USBD_HandleTypeDef *dev) { + return USBD_OK; +} + +static uint8_t usb_class_data_in(USBD_HandleTypeDef *pdev, uint8_t ep_num) { + for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { + switch (usb_ifaces[i].type) { + case USB_IFACE_TYPE_HID: + usb_hid_class_data_in(dev, &usb_ifaces[i].hid, ep_num); + break; + } + } + return USBD_OK; +} + +static uint8_t usb_class_data_out(USBD_HandleTypeDef *dev, uint8_t ep_num) { + for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { + switch (usb_ifaces[i].type) { + case USB_IFACE_TYPE_HID: + usb_hid_class_data_out(dev, &usb_ifaces[i].hid, ep_num); + break; + } + } + return USBD_OK; +} + +static uint8_t *usb_class_get_cfg_desc(uint16_t *length) { + *length = usb_config_desc->wTotalLength; + return usb_config_buf; +} + +static const USBD_ClassTypeDef usb_class = { + .Init = usb_class_init, + .DeInit = usb_class_deinit, + .Setup = usb_class_setup, + .EP0_TxSent = NULL, + .EP0_RxReady = usb_class_ep0_rx_ready, + .DataIn = usb_class_data_in, + .DataOut = usb_class_data_out, + .SOF = NULL, + .IsoINIncomplete = NULL, + .IsoOUTIncomplete = NULL, + .GetHSConfigDescriptor = usb_class_get_cfg_desc, + .GetFSConfigDescriptor = usb_class_get_cfg_desc, + .GetOtherSpeedConfigDescriptor = usb_class_get_cfg_desc, + .GetDeviceQualifierDescriptor = NULL, +}; + +static uint8_t usb_ep_set_nak(USBD_HandleTypeDef *dev, uint8_t ep_num) { + PCD_HandleTypeDef *hpcd = dev->pData; + USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; + USBx_OUTEP(ep_num)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK; + return USBD_OK; +} + +static uint8_t usb_ep_clear_nak(USBD_HandleTypeDef *dev, uint8_t ep_num) { + PCD_HandleTypeDef *hpcd = dev->pData; + USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; + USBx_OUTEP(ep_num)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + return USBD_OK; +} + /** * @brief This function handles USB-On-The-Go FS global interrupt request. * @param None @@ -45,26 +468,13 @@ int usb_init(void) { void OTG_FS_IRQHandler(void) { HAL_PCD_IRQHandler(&pcd_fs_handle); } -#endif -#if defined(USE_USB_HS) -/** - * @brief This function handles USB-On-The-Go HS global interrupt request. - * @param None - * @retval None - */ -void OTG_HS_IRQHandler(void) { - HAL_PCD_IRQHandler(&pcd_hs_handle); -} -#endif - -#if defined(USE_USB_FS) || defined(USE_USB_HS) /** * @brief This function handles USB OTG Common FS/HS Wakeup functions. * @param *pcd_handle for FS or HS * @retval None */ -STATIC void OTG_CMD_WKUP_Handler(PCD_HandleTypeDef *pcd_handle) { +static void OTG_CMD_WKUP_Handler(PCD_HandleTypeDef *pcd_handle) { if (!(pcd_handle->Init.low_power_enable)) { return; } @@ -94,9 +504,7 @@ STATIC void OTG_CMD_WKUP_Handler(PCD_HandleTypeDef *pcd_handle) { /* ungate PHY clock */ __HAL_PCD_UNGATE_PHYCLOCK(pcd_handle); } -#endif -#if defined(USE_USB_FS) /** * @brief This function handles USB OTG FS Wakeup IRQ Handler. * @param None @@ -108,18 +516,3 @@ void OTG_FS_WKUP_IRQHandler(void) { /* Clear EXTI pending Bit*/ __HAL_USB_FS_EXTI_CLEAR_FLAG(); } -#endif - -#if defined(USE_USB_HS) -/** - * @brief This function handles USB OTG HS Wakeup IRQ Handler. - * @param None - * @retval None - */ -void OTG_HS_WKUP_IRQHandler(void) { - OTG_CMD_WKUP_Handler(&pcd_hs_handle); - - /* Clear EXTI pending Bit*/ - __HAL_USB_HS_EXTI_CLEAR_FLAG(); -} -#endif diff --git a/micropython/trezorhal/usb.h b/micropython/trezorhal/usb.h index 2eda07eb7d..e30b64690a 100644 --- a/micropython/trezorhal/usb.h +++ b/micropython/trezorhal/usb.h @@ -1,6 +1,171 @@ #ifndef __TREZORHAL_USB_H__ #define __TREZORHAL_USB_H__ -int usb_init(void); +typedef struct __attribute__((packed)) { + uint8_t bLength; + uint8_t bDescriptorType; + 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; + +typedef struct __attribute__((packed)) { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData; +} usb_string_descriptor_t; + +typedef struct __attribute__((packed)) { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} usb_config_descriptor_t; + +typedef struct __attribute__((packed)) { + uint8_t bLength; + uint8_t bDescriptorType; + 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; + +typedef struct __attribute__((packed)) { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} usb_endpoint_descriptor_t; + +typedef struct __attribute__((packed)) { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bReportDescriptorType; + uint16_t wReportDescriptorLength; +} usb_hid_descriptor_t; + +struct __attribute__((packed)) { + usb_interface_descriptor_t iface; + usb_hid_descriptor_t hid; + usb_endpoint_descriptor_t ep_in; + usb_endpoint_descriptor_t ep_out; +} usb_hid_descriptor_block_t; + +typedef enum { + USB_LANGID_ENGLISH_US = 0x409, +} usb_language_id_t; + +typedef struct { + const uint8_t *manufacturer_str; + const uint8_t *product_str; + const uint8_t *serial_str; + const uint8_t *config_str; + const uint8_t *interface_str; +} usb_string_table_t; + +typedef struct { + uint16_t vendor_id; + uint16_t product_id; + uint16_t release_num; + const uint8_t *product_str; + const uint8_t *manufacturer_str; + const uint8_t *serial_number_str; + const uint8_t *configuration_str; + const uint8_t *interface_str; +} usb_dev_info_t; + +typedef enum { + USB_HID_SUBCLASS_NONE = 0, + USB_HID_SUBCLASS_BOOT = 1, +} usb_hid_subclass_t; + +typedef enum { + USB_HID_PROTOCOL_NONE = 0, + USB_HID_PROTOCOL_KEYBOARD = 1, + USB_HID_PROTOCOL_MOUSE = 2, +} usb_hid_protocol_t; + +typedef struct { + // Interface configuration + uint8_t iface_num; // Address of this HID interface + uint8_t ep_in; // Address of IN endpoint (with the highest bit set) + uint8_t ep_out; // Address of OUT endpoint + + // HID configuration + uint8_t subclass; // usb_iface_subclass_t + uint8_t protocol; // usb_iface_protocol_t + uint8_t max_packet_len; // rx_buffer should be big enough + uint8_t polling_interval; // In units of 1ms + uint8_t report_desc_len; + const uint8_t *report_desc; + + // HID read buffer + uint8_t *rx_buffer; // Big enough for max_packet_len +} usb_hid_info_t; + +typedef struct { + + // HID state + uint8_t in_idle; // Set to 1 after IN endpoint gets idle + uint8_t protocol; // For SET_PROTOCOL/GET_PROTOCOL setup reqs + uint8_t idle_rate; // For SET_IDLE/GET_IDLE setup reqs + uint8_t alt_setting; // For SET_INTERFACE/GET_INTERFACE setup reqs + uint8_t rx_buffer_len; // Length of data read into rx_buffer + + // HID configuration (copied from usb_hid_info_t on init) + uint8_t ep_in; + uint8_t ep_out; + uint8_t max_packet_len; + uint8_t report_desc_len; + uint8_t *rx_buffer; + const uint8_t *report_desc; + const usb_hid_descriptor_block_t *desc_block; +} usb_hid_state_t; + +typedef enum { + USB_IFACE_TYPE_DISABLED = 0, + USB_IFACE_TYPE_CDC = 1, + USB_IFACE_TYPE_MSC = 2, + USD_IFACE_TYPE_HID = 3, +} usb_iface_type_t; + +typedef struct { + union { + usb_hid_state_t hid; + }; + usb_iface_type_t type; +} usb_iface_t; + +int usb_init(const usb_dev_info_t *dev_info); +int usb_start(void); +int usb_stop(void); + +int usb_hid_add(const usb_hid_info_t *hid_info); +int usb_hid_can_read(uint8_t iface_num); +int usb_hid_can_write(uint8_t iface_num); +int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t *len); +int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len); #endif