// USB descriptors and the HID plumbing for the FIDO interface. #include #include "pico/unique_id.h" #include "platform.h" #include "tusb.h" // pid.codes test VID/PID: fine for personal use, not for distribution. Hosts // recognize FIDO devices by the HID usage page, not the IDs. #define USB_VID 0x1209 #define USB_PID 0x0001 #define SEND_TIMEOUT_MS 1000 #define RX_QUEUE_LEN 16 // power of two enum { STR_LANGID, STR_MANUFACTURER, STR_PRODUCT, STR_SERIAL, STR_INTERFACE }; static const tusb_desc_device_t desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0x00, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = USB_VID, .idProduct = USB_PID, .bcdDevice = 0x0100, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT, .iSerialNumber = STR_SERIAL, .bNumConfigurations = 1, }; // FIDO HID report descriptor (CTAP 2.1, section 11.2.8.1): 64-byte reports both ways. static const uint8_t desc_hid_report[] = { 0x06, 0xd0, 0xf1, // Usage Page (FIDO Alliance) 0x09, 0x01, // Usage (CTAPHID) 0xa1, 0x01, // Collection (Application) 0x09, 0x20, // Usage (Input Report Data) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xff, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x40, // Report Count (64) 0x81, 0x02, // Input (Data, Variable, Absolute) 0x09, 0x21, // Usage (Output Report Data) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xff, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x40, // Report Count (64) 0x91, 0x02, // Output (Data, Variable, Absolute) 0xc0, // End Collection }; #define EP_HID_OUT 0x01 #define EP_HID_IN 0x81 #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN) static const uint8_t desc_configuration[] = { TUD_CONFIG_DESCRIPTOR(1, 1, 0, CONFIG_TOTAL_LEN, 0, 100), TUD_HID_INOUT_DESCRIPTOR(0, STR_INTERFACE, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EP_HID_OUT, EP_HID_IN, CFG_TUD_HID_EP_BUFSIZE, 5), }; static uint8_t rx_queue[RX_QUEUE_LEN][HID_PACKET_SIZE]; static unsigned rx_head, rx_tail; const uint8_t *tud_descriptor_device_cb(void) { return (const uint8_t *)&desc_device; } const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { (void)index; return desc_configuration; } const uint8_t *tud_hid_descriptor_report_cb(uint8_t instance) { (void)instance; return desc_hid_report; } const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { static uint16_t desc[32]; char serial[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1]; const char *str; size_t len; (void)langid; switch (index) { case STR_LANGID: desc[1] = 0x0409; // English (US) desc[0] = (uint16_t)(TUSB_DESC_STRING << 8 | 4); return desc; case STR_MANUFACTURER: str = "Skey"; break; case STR_PRODUCT: str = "Skey FIDO2 Key"; break; case STR_SERIAL: pico_get_unique_board_id_string(serial, sizeof(serial)); str = serial; break; case STR_INTERFACE: str = "FIDO"; break; default: return NULL; } len = strlen(str); if (len > 31) len = 31; for (size_t i = 0; i < len; i++) desc[1 + i] = (uint8_t)str[i]; desc[0] = (uint16_t)(TUSB_DESC_STRING << 8 | (2 * len + 2)); return desc; } uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void)instance; (void)report_id; (void)report_type; (void)buffer; (void)reqlen; return 0; } // Runs inside tud_task(). Packets are queued so they are handled from the main // loop, never re-entrantly while a response is being sent. void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) { (void)instance; (void)report_id; (void)report_type; if (bufsize != HID_PACKET_SIZE || rx_head - rx_tail == RX_QUEUE_LEN) return; memcpy(rx_queue[rx_head % RX_QUEUE_LEN], buffer, HID_PACKET_SIZE); rx_head++; } bool platform_hid_receive(uint8_t packet[HID_PACKET_SIZE]) { tud_task(); if (rx_head == rx_tail) return false; memcpy(packet, rx_queue[rx_tail % RX_QUEUE_LEN], HID_PACKET_SIZE); rx_tail++; return true; } void platform_hid_send(const uint8_t packet[HID_PACKET_SIZE]) { uint32_t start = platform_millis(); while (!tud_hid_ready()) { if (!tud_mounted() || platform_millis() - start > SEND_TIMEOUT_MS) return; // host went away tud_task(); } tud_hid_report(0, packet, HID_PACKET_SIZE); }