From c8a291261674a821490b9faf0f31c2caf80bed00 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 30 Dec 2021 23:35:30 +0100 Subject: [PATCH] Moving to TinyUSB. Low level API does not provide methods for larger packets. TinyUSB provides a complete interface for managing CCID. Signed-off-by: Pol Henarejos --- CMakeLists.txt | 14 +- ccid.h | 46 ++++ hsm2040.c | 661 ++++++---------------------------------------- hsm2040.h | 133 +--------- tusb_config.h | 119 +++++++++ usb_common.h | 134 ---------- usb_descriptors.c | 210 +++++++++++++++ usb_descriptors.h | 36 +++ 8 files changed, 512 insertions(+), 841 deletions(-) create mode 100644 tusb_config.h delete mode 100644 usb_common.h create mode 100644 usb_descriptors.c create mode 100644 usb_descriptors.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 85bb511..f22f1fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,15 @@ set(CMAKE_CXX_STANDARD 17) pico_sdk_init() -add_executable(hsm2040 - hsm2040.c -) +add_executable(hsm2040) + +target_sources(hsm2040 PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/hsm2040.c + ${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c + ) + +target_include_directories(hsm2040 PUBLIC + ${CMAKE_CURRENT_LIST_DIR}) pico_add_extra_outputs(hsm2040) -target_link_libraries(hsm2040 PRIVATE pico_stdlib hardware_resets hardware_irq) \ No newline at end of file +target_link_libraries(hsm2040 PRIVATE pico_stdlib tinyusb_device tinyusb_board) \ No newline at end of file diff --git a/ccid.h b/ccid.h index e69de29..25fb99b 100644 --- a/ccid.h +++ b/ccid.h @@ -0,0 +1,46 @@ +#ifndef _CCID_H_ +#define _CCID_H_ + +#include "ccid-types.h" + + +static const class_desc_ccid_t desc_ccid = { + .bLength = sizeof (class_desc_ccid_t), + .bDescriptorType = 0x21, + .bcdCCID = 0x0110, + .bMaxSlotIndex = 0, + .bVoltageSupport = 0x01, // 5.0V + .dwProtocols = 0x01| // T=0 + 0x02, // T=1 + .dwDefaultClock = 0xdfc, + .dwMaximumClock = 0xdfc, + .bNumClockSupport = 1, + .dwDataRate = 0x2580, + .dwMaxDataRate = 0x2580, + .bNumDataRatesSupported = 1, + .dwMaxIFSD = 0xff, // IFSD is handled by the real reader driver + .dwSynchProtocols = 0, + .dwMechanical = 0, + .dwFeatures = 0x00000002| // Automatic parameter configuration based on ATR data + 0x00000004| // Automatic activation of ICC on inserting + 0x00000008| // Automatic ICC voltage selection + 0x00000010| // Automatic ICC clock frequency change + 0x00000020| // Automatic baud rate change + 0x00000040| // Automatic parameters negotiation + 0x00000080| // Automatic PPS + 0x00000400| // Automatic IFSD exchange as first exchange + 0x00040000| // Short and Extended APDU level exchange with CCID + 0x00100000, // USB Wake up signaling supported + .dwMaxCCIDMessageLength = CCID_EXT_APDU_MAX, + .bClassGetResponse = 0xff, + .bclassEnvelope = 0xff, + .wLcdLayout = 0xff00| // Number of lines for the LCD display + 0x00ff, // Number of characters per line + .bPINSupport = 0x1| // PIN Verification supported + 0x2| + 0x10| // PIN PACE Capabilities supported + 0x20, // PIN Modification supported + .bMaxCCIDBusySlots = 0x01, +}; + +#endif \ No newline at end of file diff --git a/hsm2040.c b/hsm2040.c index c3961cc..203dd2e 100644 --- a/hsm2040.c +++ b/hsm2040.c @@ -12,601 +12,116 @@ // For memcpy #include -// Include descriptor struct definitions -#include "usb_common.h" -// USB register definitions from pico-sdk -#include "hardware/regs/usb.h" -// USB hardware struct definitions from pico-sdk -#include "hardware/structs/usb.h" -// For interrupt enable and numbers -#include "hardware/irq.h" -// For resetting the USB controller -#include "hardware/resets.h" +#include "bsp/board.h" +#include "tusb.h" +#include "usb_descriptors.h" // Device descriptors #include "hsm2040.h" -#define usb_hw_set hw_set_alias(usb_hw) -#define usb_hw_clear hw_clear_alias(usb_hw) +enum { + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, -// Function prototypes for our device specific endpoint handlers defined -// later on -void ep0_in_handler(uint8_t *buf, uint16_t len); -void ep0_out_handler(uint8_t *buf, uint16_t len); -void ep1_out_handler(uint8_t *buf, uint16_t len); -void ep2_in_handler(uint8_t *buf, uint16_t len); + BLINK_RED = 18, + BLINK_GREEN = 19, + BLINK_BLUE = 20, -// Global device address -static bool should_set_address = false; -static uint8_t dev_addr = 0; -static volatile bool configured = false; - -// Global data buffer for EP0 -static uint8_t ep0_buf[64]; - -// Struct defining the device configuration -static struct usb_device_configuration dev_config = { - .device_descriptor = &device_descriptor, - .interface_descriptor = &interface_descriptor, - .config_descriptor = &config_descriptor, - .lang_descriptor = lang_descriptor, - .descriptor_strings = descriptor_strings, - .endpoints = { - { - .descriptor = &ep0_out, - .handler = &ep0_out_handler, - .endpoint_control = NULL, // NA for EP0 - .buffer_control = &usb_dpram->ep_buf_ctrl[0].out, - // EP0 in and out share a data buffer - .data_buffer = &usb_dpram->ep0_buf_a[0], - }, - { - .descriptor = &ep0_in, - .handler = &ep0_in_handler, - .endpoint_control = NULL, // NA for EP0, - .buffer_control = &usb_dpram->ep_buf_ctrl[0].in, - // EP0 in and out share a data buffer - .data_buffer = &usb_dpram->ep0_buf_a[0], - }, - { - .descriptor = &ep1_out, - .handler = &ep1_out_handler, - // EP1 starts at offset 0 for endpoint control - .endpoint_control = &usb_dpram->ep_ctrl[0].out, - .buffer_control = &usb_dpram->ep_buf_ctrl[1].out, - // First free EPX buffer - .data_buffer = &usb_dpram->epx_data[0 * 64], - }, - { - .descriptor = &ep2_in, - .handler = &ep2_in_handler, - .endpoint_control = &usb_dpram->ep_ctrl[1].in, - .buffer_control = &usb_dpram->ep_buf_ctrl[2].in, - // Second free EPX buffer - .data_buffer = &usb_dpram->epx_data[1 * 64], - } - } + BLINK_ALWAYS_ON = UINT32_MAX, + BLINK_ALWAYS_OFF = 0 }; -/** - * @brief Given an endpoint address, return the usb_endpoint_configuration of that endpoint. Returns NULL - * if an endpoint of that address is not found. - * - * @param addr - * @return struct usb_endpoint_configuration* - */ -struct usb_endpoint_configuration *usb_get_endpoint_configuration(uint8_t addr) { - struct usb_endpoint_configuration *endpoints = dev_config.endpoints; - for (int i = 0; i < USB_NUM_ENDPOINTS; i++) { - if (endpoints[i].descriptor && (endpoints[i].descriptor->bEndpointAddress == addr)) { - return &endpoints[i]; - } +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + + +//--------------------------------------------------------------------+ +// USB CDC +//--------------------------------------------------------------------+ +void vendor_task(void) +{ + if ( tud_vendor_mounted() ) + { + // connected and there are data available + if ( tud_vendor_available() ) + { + uint8_t buf[64]; + + uint32_t count = tud_vendor_read(buf, sizeof(buf)); + + // echo back to both web serial and cdc + //echo_all(buf, count); } - return NULL; + } } -/** - * @brief Given a C string, fill the EP0 data buf with a USB string descriptor for that string. - * - * @param C string you would like to send to the USB host - * @return the length of the string descriptor in EP0 buf - */ -uint8_t usb_prepare_string_descriptor(const unsigned char *str) { - // 2 for bLength + bDescriptorType + strlen * 2 because string is unicode. i.e. other byte will be 0 - uint8_t bLength = 2 + (strlen((const char *)str) * 2); - static const uint8_t bDescriptorType = 0x03; +// Invoked when cdc when line state changed e.g connected/disconnected +void tud_vendor_line_state_cb(uint8_t itf, bool dtr, bool rts) +{ + (void) itf; - volatile uint8_t *buf = &ep0_buf[0]; - *buf++ = bLength; - *buf++ = bDescriptorType; - - uint8_t c; - - do { - c = *str++; - *buf++ = c; - *buf++ = 0; - } while (c != '\0'); - - return bLength; + // connected + if ( dtr && rts ) + { + // print initial message when connected + tud_vendor_write_str("\r\nTinyUSB WebUSB device example\r\n"); + } } -/** - * @brief Take a buffer pointer located in the USB RAM and return as an offset of the RAM. - * - * @param buf - * @return uint32_t - */ -static inline uint32_t usb_buffer_offset(volatile uint8_t *buf) { - return (uint32_t) buf ^ (uint32_t) usb_dpram; +// Invoked when CDC interface received data from host +void tud_vendor_rx_cb(uint8_t itf) +{ + (void) itf; } -/** - * @brief Set up the endpoint control register for an endpoint (if applicable. Not valid for EP0). - * - * @param ep - */ -void usb_setup_endpoint(const struct usb_endpoint_configuration *ep) { - printf("Set up endpoint 0x%x with buffer address 0x%p\n", ep->descriptor->bEndpointAddress, ep->data_buffer); - - // EP0 doesn't have one so return if that is the case - if (!ep->endpoint_control) { - return; - } - - // Get the data buffer as an offset of the USB controller's DPRAM - uint32_t dpram_offset = usb_buffer_offset(ep->data_buffer); - uint32_t reg = EP_CTRL_ENABLE_BITS - | EP_CTRL_INTERRUPT_PER_BUFFER - | (ep->descriptor->bmAttributes << EP_CTRL_BUFFER_TYPE_LSB) - | dpram_offset; - *ep->endpoint_control = reg; +void tud_mount_cb() +{ + TU_LOG3("!!!!!!! MOUNTED\r\n"); } -/** - * @brief Set up the endpoint control register for each endpoint. - * - */ -void usb_setup_endpoints() { - const struct usb_endpoint_configuration *endpoints = dev_config.endpoints; - for (int i = 0; i < USB_NUM_ENDPOINTS; i++) { - if (endpoints[i].descriptor && endpoints[i].handler) { - usb_setup_endpoint(&endpoints[i]); - } - } +void led_blinking_task(void) +{ + static uint32_t start_ms = 0; + static uint8_t led_state = false; + static uint8_t led_color = BLINK_RED; + + // Blink every interval ms + if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + start_ms += blink_interval_ms; + + gpio_put(led_color, led_state); + led_state ^= 1; // toggle } -/** - * @brief Set up the USB controller in device mode, clearing any previous state. - * - */ -void usb_device_init() { - // Reset usb controller - reset_block(RESETS_RESET_USBCTRL_BITS); - unreset_block_wait(RESETS_RESET_USBCTRL_BITS); - - // Clear any previous state in dpram just in case - memset(usb_dpram, 0, sizeof(*usb_dpram)); // <1> - - // Enable USB interrupt at processor - irq_set_enabled(USBCTRL_IRQ, true); - - // Mux the controller to the onboard usb phy - usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; - - // Force VBUS detect so the device thinks it is plugged into a host - usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS; - - // Enable the USB controller in device mode. - usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS; - - // Enable an interrupt per EP0 transaction - usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; // <2> - - // Enable interrupts for when a buffer is done, when the bus is reset, - // and when a setup packet is received - usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | - USB_INTS_BUS_RESET_BITS | - USB_INTS_SETUP_REQ_BITS; - - // Set up endpoints (endpoint control registers) - // described by device configuration - usb_setup_endpoints(); - - // Present full speed device by enabling pull up on DP - usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS; +void led_off_all() +{ + gpio_put(18, 1); + gpio_put(19, 1); + gpio_put(20, 1); } -/** - * @brief Given an endpoint configuration, returns true if the endpoint - * is transmitting data to the host (i.e. is an IN endpoint) - * - * @param ep, the endpoint configuration - * @return true - * @return false - */ -static inline bool ep_is_tx(struct usb_endpoint_configuration *ep) { - return ep->descriptor->bEndpointAddress & USB_DIR_IN; -} +int main(void) +{ + printf("BOARD INIT\r\n"); + board_init(); + + gpio_init(18); + gpio_set_dir(18, GPIO_OUT); + gpio_init(19); + gpio_set_dir(19, GPIO_OUT); + gpio_init(20); + gpio_set_dir(20, GPIO_OUT); + + led_off_all(); -/** - * @brief Starts a transfer on a given endpoint. - * - * @param ep, the endpoint configuration. - * @param buf, the data buffer to send. Only applicable if the endpoint is TX - * @param len, the length of the data in buf (this example limits max len to one packet - 64 bytes) - */ -void usb_start_transfer(struct usb_endpoint_configuration *ep, uint8_t *buf, uint16_t len) { - // We are asserting that the length is <= 64 bytes for simplicity of the example. - // For multi packet transfers see the tinyusb port. - assert(len <= 64); + tusb_init(); - printf("Start transfer of len %d on ep addr 0x%x\n", len, ep->descriptor->bEndpointAddress); + while (1) + { + tud_task(); // tinyusb device task + vendor_task(); + led_blinking_task(); + } - // Prepare buffer control register value - uint32_t val = len | USB_BUF_CTRL_AVAIL; - printf("VAL %d (%x)\n",val,val); - - if (ep_is_tx(ep)) { - // Need to copy the data from the user buffer to the usb memory - memcpy((void *) ep->data_buffer, (void *) buf, len); - // Mark as full - val |= USB_BUF_CTRL_FULL; - } - - // Set pid and flip for next transfer - val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID; - ep->next_pid ^= 1u; - - *ep->buffer_control = val; -} - -/** - * @brief Send device descriptor to host - * - */ -void usb_handle_device_descriptor(volatile struct usb_setup_packet *pkt) { - const struct usb_device_descriptor *d = dev_config.device_descriptor; - // EP0 in - struct usb_endpoint_configuration *ep = usb_get_endpoint_configuration(EP0_IN_ADDR); - // Always respond with pid 1 - ep->next_pid = 1; - usb_start_transfer(ep, (uint8_t *) d, MIN(sizeof(struct usb_device_descriptor),pkt->wLength)); -} - -/** - * @brief Send the configuration descriptor (and potentially the configuration and endpoint descriptors) to the host. - * - * @param pkt, the setup packet received from the host. - */ -void usb_handle_config_descriptor(volatile struct usb_setup_packet *pkt) { - uint8_t *buf = &ep0_buf[0]; - - // First request will want just the config descriptor - const struct usb_configuration_descriptor *d = dev_config.config_descriptor; - memcpy((void *) buf, d, sizeof(struct usb_configuration_descriptor)); - buf += sizeof(struct usb_configuration_descriptor); - - // If we more than just the config descriptor copy it all - if (pkt->wLength >= d->wTotalLength) { - memcpy((void *) buf, dev_config.interface_descriptor, sizeof(struct usb_interface_descriptor)); - buf += sizeof(struct usb_interface_descriptor); - const struct usb_endpoint_configuration *ep = dev_config.endpoints; - - // Copy all the endpoint descriptors starting from EP1 - for (uint i = 2; i < USB_NUM_ENDPOINTS; i++) { - if (ep[i].descriptor) { - memcpy((void *) buf, ep[i].descriptor, sizeof(struct usb_endpoint_descriptor)); - buf += sizeof(struct usb_endpoint_descriptor); - } - } - - } - - // Send data - // Get len by working out end of buffer subtract start of buffer - uint32_t len = (uint32_t) buf - (uint32_t) &ep0_buf[0]; - usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), &ep0_buf[0], MIN(len,pkt->wLength)); -} - -/** - * @brief Handle a BUS RESET from the host by setting the device address back to 0. - * - */ -void usb_bus_reset(void) { - // Set address back to 0 - dev_addr = 0; - should_set_address = false; - usb_hw->dev_addr_ctrl = 0; - configured = false; -} - -/** - * @brief Send the requested string descriptor to the host. - * - * @param pkt, the setup packet from the host. - */ -void usb_handle_string_descriptor(volatile struct usb_setup_packet *pkt) { - uint8_t i = pkt->wValue & 0xff; - uint8_t len = 0; - - if (i == 0) { - len = 4; - memcpy(&ep0_buf[0], dev_config.lang_descriptor, len); - } else { - // Prepare fills in ep0_buf - len = usb_prepare_string_descriptor(dev_config.descriptor_strings[i - 1]); - } - - usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), &ep0_buf[0], MIN(len,pkt->wLength)); -} - -/** - * @brief Sends a zero length status packet back to the host. - */ -void usb_acknowledge_out_request(void) { - usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), NULL, 0); -} - -/** - * @brief Handles a SET_ADDR request from the host. The actual setting of the device address in - * hardware is done in ep0_in_handler. This is because we have to acknowledge the request first - * as a device with address zero. - * - * @param pkt, the setup packet from the host. - */ -void usb_set_device_address(volatile struct usb_setup_packet *pkt) { - // Set address is a bit of a strange case because we have to send a 0 length status packet first with - // address 0 - dev_addr = (pkt->wValue & 0xff); - printf("Set address %d\r\n", dev_addr); - // Will set address in the callback phase - should_set_address = true; - usb_acknowledge_out_request(); -} - -/** - * @brief Handles a SET_CONFIGRUATION request from the host. Assumes one configuration so simply - * sends a zero length status packet back to the host. - * - * @param pkt, the setup packet from the host. - */ -void usb_set_device_configuration(volatile struct usb_setup_packet *pkt) { - // Only one configuration so just acknowledge the request - printf("Device Enumerated\r\n"); - usb_acknowledge_out_request(); - configured = true; -} - -/** - * @brief Respond to a setup packet from the host. - * - */ -void usb_handle_setup_packet(void) { - volatile struct usb_setup_packet *pkt = (volatile struct usb_setup_packet *) &usb_dpram->setup_packet; - uint8_t req_direction = pkt->bmRequestType & USB_DIR_IN; - uint8_t req_type = pkt->bmRequestType & USB_REQ_TYPE_TYPE_MASK; - uint8_t req = pkt->bRequest; - - // Reset PID to 1 for EP0 IN - usb_get_endpoint_configuration(EP0_IN_ADDR)->next_pid = 1u; - - if (req_type != USB_REQ_TYPE_STANDARD) { - printf("NON STANDARD TYPE REQUEST\r\n"); - } - else { - if (req_direction == USB_DIR_OUT) { - if (req == USB_REQUEST_SET_ADDRESS) { - usb_set_device_address(pkt); - printf("SET ADDRESS\r\n"); - } else if (req == USB_REQUEST_SET_CONFIGURATION) { - usb_set_device_configuration(pkt); - printf("SET CONFIGURATION\r\n"); - } else { - usb_acknowledge_out_request(); - printf("Other OUT request (0x%x)\r\n", pkt->bRequest); - } - } else if (req_direction == USB_DIR_IN) { - if (req == USB_REQUEST_GET_DESCRIPTOR) { - uint16_t descriptor_type = pkt->wValue >> 8; - - switch (descriptor_type) { - case USB_DT_DEVICE: - usb_handle_device_descriptor(pkt); - printf("GET DEVICE DESCRIPTOR\r\n"); - break; - - case USB_DT_CONFIG: - usb_handle_config_descriptor(pkt); - printf("GET CONFIG DESCRIPTOR\r\n"); - break; - - case USB_DT_STRING: - usb_handle_string_descriptor(pkt); - printf("GET STRING DESCRIPTOR\r\n"); - break; - - default: - printf("Unhandled GET_DESCRIPTOR type 0x%x\r\n", descriptor_type); - } - } else { - printf("Other IN request (0x%x)\r\n", pkt->bRequest); - } - } - } -} - -/** - * @brief Notify an endpoint that a transfer has completed. - * - * @param ep, the endpoint to notify. - */ -static void usb_handle_ep_buff_done(struct usb_endpoint_configuration *ep) { - uint32_t buffer_control = *ep->buffer_control; - // Get the transfer length for this endpoint - uint16_t len = buffer_control & USB_BUF_CTRL_LEN_MASK; - - // Call that endpoints buffer done handler - ep->handler((uint8_t *) ep->data_buffer, len); -} - -/** - * @brief Find the endpoint configuration for a specified endpoint number and - * direction and notify it that a transfer has completed. - * - * @param ep_num - * @param in - */ -static void usb_handle_buff_done(uint ep_num, bool in) { - uint8_t ep_addr = ep_num | (in ? USB_DIR_IN : 0); - printf("EP %d (in = %d) done\n", ep_num, in); - for (uint i = 0; i < USB_NUM_ENDPOINTS; i++) { - struct usb_endpoint_configuration *ep = &dev_config.endpoints[i]; - if (ep->descriptor && ep->handler) { - if (ep->descriptor->bEndpointAddress == ep_addr) { - usb_handle_ep_buff_done(ep); - return; - } - } - } -} - -/** - * @brief Handle a "buffer status" irq. This means that one or more - * buffers have been sent / received. Notify each endpoint where this - * is the case. - */ -static void usb_handle_buff_status() { - uint32_t buffers = usb_hw->buf_status; - uint32_t remaining_buffers = buffers; - - uint bit = 1u; - for (uint i = 0; remaining_buffers && i < USB_NUM_ENDPOINTS * 2; i++) { - if (remaining_buffers & bit) { - // clear this in advance - usb_hw_clear->buf_status = bit; - // IN transfer for even i, OUT transfer for odd i - usb_handle_buff_done(i >> 1u, !(i & 1u)); - remaining_buffers &= ~bit; - } - bit <<= 1u; - } -} - -/** - * @brief USB interrupt handler - * - */ -/// \tag::isr_setup_packet[] -void isr_usbctrl(void) { - // USB interrupt handler - uint32_t status = usb_hw->ints; - uint32_t handled = 0; - - // Setup packet received - if (status & USB_INTS_SETUP_REQ_BITS) { - handled |= USB_INTS_SETUP_REQ_BITS; - usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS; - usb_handle_setup_packet(); - } -/// \end::isr_setup_packet[] - - // Buffer status, one or more buffers have completed - if (status & USB_INTS_BUFF_STATUS_BITS) { - handled |= USB_INTS_BUFF_STATUS_BITS; - usb_handle_buff_status(); - } - - // Bus is reset - if (status & USB_INTS_BUS_RESET_BITS) { - printf("BUS RESET\n"); - handled |= USB_INTS_BUS_RESET_BITS; - usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS; - usb_bus_reset(); - } - - if (status ^ handled) { - panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled)); - } -} - -/** - * @brief EP0 in transfer complete. Either finish the SET_ADDRESS process, or receive a zero - * length status packet from the host. - * - * @param buf the data that was sent - * @param len the length that was sent - */ -void ep0_in_handler(uint8_t *buf, uint16_t len) { - if (should_set_address) { - // Set actual device address in hardware - usb_hw->dev_addr_ctrl = dev_addr; - should_set_address = false; - } else { - // Receive a zero length status packet from the host on EP0 OUT - struct usb_endpoint_configuration *ep = usb_get_endpoint_configuration(EP0_OUT_ADDR); - usb_start_transfer(ep, NULL, 0); - } -} - -void ep0_out_handler(uint8_t *buf, uint16_t len) { - ; -} - -// Device specific functions -void ep1_out_handler(uint8_t *buf, uint16_t len) { - printf("RX %d bytes from host\n", len); - // Send data back to host - struct usb_endpoint_configuration *ep = usb_get_endpoint_configuration(EP2_IN_ADDR); - usb_start_transfer(ep, buf, len); -} - -void ep2_in_handler(uint8_t *buf, uint16_t len) { - printf("Sent %d bytes to host\n", len); - // Get ready to rx again from host - usb_start_transfer(usb_get_endpoint_configuration(EP1_OUT_ADDR), NULL, 64); -} - -int main(void) { - stdio_init_all(); - printf("USB Device Low-Level hardware example\n"); - usb_device_init(); - - gpio_init(18); - gpio_set_dir(18, GPIO_OUT); - gpio_init(19); - gpio_set_dir(19, GPIO_OUT); - gpio_init(20); - gpio_set_dir(20, GPIO_OUT); - gpio_put(18, 1); - gpio_put(19, 1); - gpio_put(20, 1); - - - // Wait until configured - while (!configured) { - tight_loop_contents(); - gpio_put(18, 1); - sleep_ms(250); - gpio_put(18, 0); - sleep_ms(250); - } - - // Get ready to rx from host - usb_start_transfer(usb_get_endpoint_configuration(EP1_OUT_ADDR), NULL, 64); - gpio_put(18, 1); - gpio_put(19, 1); - gpio_put(20, 1); - - // Everything is interrupt driven so just loop here - while (1) { - tight_loop_contents(); - - gpio_put(19, 1); - sleep_ms(250); - gpio_put(19, 0); - sleep_ms(250); - } - - return 0; -} + return 0; +} \ No newline at end of file diff --git a/hsm2040.h b/hsm2040.h index 1c76457..36e4156 100644 --- a/hsm2040.h +++ b/hsm2040.h @@ -4,137 +4,10 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef DEV_LOWLEVEL_H_ -#define DEV_LOWLEVEL_H_ +#ifndef _HSM2040_H_ +#define _HSM2040_H_ -#include "usb_common.h" - -// Struct in which we keep the endpoint configuration -typedef void (*usb_ep_handler)(uint8_t *buf, uint16_t len); -struct usb_endpoint_configuration { - const struct usb_endpoint_descriptor *descriptor; - usb_ep_handler handler; - - // Pointers to endpoint + buffer control registers - // in the USB controller DPSRAM - volatile uint32_t *endpoint_control; - volatile uint32_t *buffer_control; - volatile uint8_t *data_buffer; - - // Toggle after each packet (unless replying to a SETUP) - uint8_t next_pid; -}; - -// Struct in which we keep the device configuration -struct usb_device_configuration { - const struct usb_device_descriptor *device_descriptor; - const struct usb_interface_descriptor *interface_descriptor; - const struct usb_configuration_descriptor *config_descriptor; - const unsigned char *lang_descriptor; - const unsigned char **descriptor_strings; - // USB num endpoints is 16 - struct usb_endpoint_configuration endpoints[USB_NUM_ENDPOINTS]; -}; - -#define EP0_IN_ADDR (USB_DIR_IN | 0) -#define EP0_OUT_ADDR (USB_DIR_OUT | 0) -#define EP1_OUT_ADDR (USB_DIR_OUT | 1) -#define EP2_IN_ADDR (USB_DIR_IN | 2) - -// EP0 IN and OUT -static const struct usb_endpoint_descriptor ep0_out = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP0_OUT_ADDR, // EP number 0, OUT from host (rx to device) - .bmAttributes = USB_TRANSFER_TYPE_CONTROL, - .wMaxPacketSize = 64, - .bInterval = 0 -}; - -static const struct usb_endpoint_descriptor ep0_in = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP0_IN_ADDR, // EP number 0, OUT from host (rx to device) - .bmAttributes = USB_TRANSFER_TYPE_CONTROL, - .wMaxPacketSize = 64, - .bInterval = 0 -}; - -// Descriptors -static const struct usb_device_descriptor device_descriptor = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0200, // USB 1.1 device - .bDeviceClass = 0, // Specified in interface descriptor - .bDeviceSubClass = 0, // No subclass - .bDeviceProtocol = 0, // No protocol - .bMaxPacketSize0 = 64, // Max packet size for ep0 - .idVendor = 0x0D46, // Your vendor id - .idProduct = 0x3010, // Your product ID - .bcdDevice = 0, // No device revision number - .iManufacturer = 1, // Manufacturer string index - .iProduct = 2, // Product string index - .iSerialNumber = 3, // No serial number - .bNumConfigurations = 1 // One configuration -}; - -static const struct usb_interface_descriptor interface_descriptor = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 2, // Interface has 2 endpoints - .bInterfaceClass = 0x0b, // Vendor specific endpoint - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 4 -}; - -static const struct usb_endpoint_descriptor ep1_out = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP1_OUT_ADDR, // EP number 1, OUT from host (rx to device) - .bmAttributes = USB_TRANSFER_TYPE_BULK, - .wMaxPacketSize = 64, - .bInterval = 0 -}; - -static const struct usb_endpoint_descriptor ep2_in = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP2_IN_ADDR, // EP number 2, IN from host (tx from device) - .bmAttributes = USB_TRANSFER_TYPE_BULK, - .wMaxPacketSize = 64, - .bInterval = 0 -}; - -static const struct usb_configuration_descriptor config_descriptor = { - .bLength = sizeof(struct usb_configuration_descriptor), - .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = (sizeof(config_descriptor) + - sizeof(interface_descriptor) + - sizeof(ep1_out) + - sizeof(ep2_in)), - .bNumInterfaces = 1, - .bConfigurationValue = 1, // Configuration 1 - .iConfiguration = 4, // No string - .bmAttributes = 0xc0, // attributes: self powered, no remote wakeup - .bMaxPower = 0x32 // 100ma -}; - -static const unsigned char lang_descriptor[] = { - 4, // bLength - 0x03, // bDescriptorType == String Descriptor - 0x09, 0x04 // language id = us english -}; - -static const unsigned char *descriptor_strings[] = { - (unsigned char *) "Virtual SC", // Vendor - (unsigned char *) "CCID", // Product - (unsigned char *) "11223344", - (unsigned char *) "Configuration rocks", - (unsigned char *) "Interface rocks" -}; +#include "ccid.h" #define USB_REQ_CCID 0xA1 diff --git a/tusb_config.h b/tusb_config.h new file mode 100644 index 0000000..69b5534 --- /dev/null +++ b/tusb_config.h @@ -0,0 +1,119 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_PICO +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 0 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 1 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 16 + +#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +#include "pico/types.h" + +static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); } +static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); } + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/usb_common.h b/usb_common.h deleted file mode 100644 index 4f6c01e..0000000 --- a/usb_common.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef _USB_COMMON_H -#define _USB_COMMON_H - -#include "pico/types.h" -#include "hardware/structs/usb.h" - -// bmRequestType bit definitions -#define USB_REQ_TYPE_STANDARD 0x00u -#define USB_REQ_TYPE_TYPE_MASK 0x60u -#define USB_REQ_TYPE_TYPE_CLASS 0x20u -#define USB_REQ_TYPE_TYPE_VENDOR 0x40u - -#define USB_REQ_TYPE_RECIPIENT_MASK 0x1fu -#define USB_REQ_TYPE_RECIPIENT_DEVICE 0x00u -#define USB_REQ_TYPE_RECIPIENT_INTERFACE 0x01u -#define USB_REQ_TYPE_RECIPIENT_ENDPOINT 0x02u - -#define USB_DIR_OUT 0x00u -#define USB_DIR_IN 0x80u - -#define USB_TRANSFER_TYPE_CONTROL 0x0 -#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x1 -#define USB_TRANSFER_TYPE_BULK 0x2 -#define USB_TRANSFER_TYPE_INTERRUPT 0x3 -#define USB_TRANSFER_TYPE_BITS 0x3 - -// Descriptor types -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_REQUEST_GET_STATUS 0x0 -#define USB_REQUEST_CLEAR_FEATURE 0x01 -#define USB_REQUEST_SET_FEATURE 0x03 -#define USB_REQUEST_SET_ADDRESS 0x05 -#define USB_REQUEST_GET_DESCRIPTOR 0x06 -#define USB_REQUEST_SET_DESCRIPTOR 0x07 -#define USB_REQUEST_GET_CONFIGURATION 0x08 -#define USB_REQUEST_SET_CONFIGURATION 0x09 -#define USB_REQUEST_GET_INTERFACE 0x0a -#define USB_REQUEST_SET_INTERFACE 0x0b -#define USB_REQUEST_SYNC_FRAME 0x0c - -#define USB_REQUEST_MSC_GET_MAX_LUN 0xfe -#define USB_REQUEST_MSC_RESET 0xff - -#define USB_FEAT_ENDPOINT_HALT 0x00 -#define USB_FEAT_DEVICE_REMOTE_WAKEUP 0x01 -#define USB_FEAT_TEST_MODE 0x02 - -#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 - -struct usb_setup_packet { - uint8_t bmRequestType; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -} __packed; - -struct usb_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; -}; - -struct usb_device_descriptor { - 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; -} __packed; - -struct usb_configuration_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t wTotalLength; - uint8_t bNumInterfaces; - uint8_t bConfigurationValue; - uint8_t iConfiguration; - uint8_t bmAttributes; - uint8_t bMaxPower; -} __packed; - -struct usb_interface_descriptor { - 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; -} __packed; - -struct usb_endpoint_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bEndpointAddress; - uint8_t bmAttributes; - uint16_t wMaxPacketSize; - uint8_t bInterval; -} __packed; - -struct usb_endpoint_descriptor_long { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bEndpointAddress; - uint8_t bmAttributes; - uint16_t wMaxPacketSize; - uint8_t bInterval; - uint8_t bRefresh; - uint8_t bSyncAddr; -} __attribute__((packed)); - -#endif \ No newline at end of file diff --git a/usb_descriptors.c b/usb_descriptors.c new file mode 100644 index 0000000..3b0b877 --- /dev/null +++ b/usb_descriptors.c @@ -0,0 +1,210 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" +#include "usb_descriptors.h" +#include "ccid.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] MIDI | HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +#define USB_CONFIG_ATT_ONE TU_BIT(7) + +#define MAX_USB_POWER 1 + + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = (USB_BCD), + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = 0x00, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = (USB_VID), + .idProduct = (USB_PID), + .bcdDevice = (0x0100), + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +tusb_desc_interface_t const desc_interface = +{ + .bLength = sizeof(tusb_desc_interface_t), + .bDescriptorType = TUSB_DESC_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = TUSB_CLASS_SMART_CARD, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 5, +}; + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +tusb_desc_configuration_t const desc_config = +{ + .bLength = sizeof(tusb_desc_configuration_t), + .bDescriptorType = TUSB_DESC_CONFIGURATION, + .wTotalLength = U16_TO_U8S_LE(sizeof(tusb_desc_configuration_t) + sizeof(tusb_desc_interface_t) + sizeof(class_desc_ccid_t) + 2*sizeof(tusb_desc_endpoint_t)), + .bNumInterfaces = 1, + .bConfigurationValue = 3, + .iConfiguration = 4, + .bmAttributes = USB_CONFIG_ATT_ONE | TUSB_DESC_CONFIG_ATT_SELF_POWERED, + .bMaxPower = TUSB_DESC_CONFIG_POWER_MA(MAX_USB_POWER+1), +}; + +tusb_desc_endpoint_t const desc_ep1 = +{ + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = TUSB_DIR_IN_MASK | 1, + .bmAttributes.xfer = TUSB_XFER_BULK, + .wMaxPacketSize.size = U16_TO_U8S_LE(64), + .bInterval = 0 +}; + +tusb_desc_endpoint_t const desc_ep2 = +{ + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 2, + .bmAttributes.xfer = TUSB_XFER_BULK, + .wMaxPacketSize.size = U16_TO_U8S_LE(64), + .bInterval = 1 +}; + + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete + + +static uint8_t desc_config_extended[sizeof(tusb_desc_configuration_t) + sizeof(tusb_desc_interface_t) + sizeof(class_desc_ccid_t) + 2*sizeof(tusb_desc_endpoint_t)]; + +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + static uint8_t initd = 0; + if (initd == 0) + { + uint8_t *p = desc_config_extended; + memcpy(p, &desc_config, sizeof(tusb_desc_configuration_t)); p += sizeof(tusb_desc_configuration_t); + memcpy(p, &desc_interface, sizeof(tusb_desc_interface_t)); p += sizeof(tusb_desc_interface_t); + memcpy(p, &desc_ccid, sizeof(class_desc_ccid_t)); p += sizeof(class_desc_ccid_t); + memcpy(p, &desc_ep1, sizeof(tusb_desc_endpoint_t)); p += sizeof(tusb_desc_endpoint_t); + memcpy(p, &desc_ep2, sizeof(tusb_desc_endpoint_t)); p += sizeof(tusb_desc_endpoint_t); + initd = 1; + } + return (const uint8_t *)desc_config_extended; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "11223344", // 3: Serials, should use chip ID + "TinyUSB Config", // 4: Vendor Interface + "TinyUSB Interface" +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i