/* * The MIT License (MIT) * * Copyright (c) 2023 HiFiPhile * * 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 "bsp/board_api.h" #include "tusb.h" #include "config.h" #ifndef ENABLE_SERIAL #define ENABLE_SERIAL 0 #endif bool ds_mode() { if (get_config().controller_mode == 2) { return !is_dse; } return get_config().controller_mode == 0; } enum { ITF_NUM_AUDIO_CONTROL = 0, ITF_NUM_AUDIO_STREAMING_OUT, ITF_NUM_AUDIO_STREAMING_IN, ITF_NUM_HID, #if ENABLE_SERIAL ITF_NUM_CDC, ITF_NUM_CDC_DATA, #endif ITF_NUM_TOTAL, CONFIG_DESC_LEN_AUDIO_IAD = #if ENABLE_SERIAL 8, #else 0, #endif CONFIG_DESC_LEN_BASE = 0x00E3 + CONFIG_DESC_LEN_AUDIO_IAD, CONFIG_DESC_LEN_TOTAL = CONFIG_DESC_LEN_BASE #if ENABLE_SERIAL + TUD_CDC_DESC_LEN #endif }; // String Descriptor Index enum { STRID_LANGID = 0, STRID_MANUFACTURER, STRID_PRODUCT, STRID_SERIAL, #if ENABLE_SERIAL STRID_CDC, #endif }; //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ tusb_desc_device_t desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, // Use Interface Association Descriptor (IAD) for Audio // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) #if ENABLE_SERIAL .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, #else .bDeviceClass = 0x00, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, #endif .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = 0x054C, // .idProduct = 0x0CE6, // DS // .idProduct = 0x0DF2, // DSE .bcdDevice = 0x0100, .iManufacturer = 0x01, .iProduct = 0x02, // OLED Edition restores the per-board serial number that upstream stripped // in commit e79c762 ("remove usb serialnumber #32"). Without it, Windows // treats the dongle as a NEW device for each USB port (issue awalol#100): // users lose their per-device volume / app preferences when they move // ports. The trade-off (re-introducing SpecialK incompatibility from #32) // affects only Windows users with that specific tool; the broader Windows // population gets stable device identity back. The serial string itself // is produced by board_usb_get_serial() from the flash chip's unique ID. .iSerialNumber = STRID_SERIAL, .bNumConfigurations = 0x01 }; // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor uint8_t const *tud_descriptor_device_cb(void) { desc_device.idProduct = ds_mode() ? 0x0CE6 : 0x0DF2; return reinterpret_cast(&desc_device); } //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ uint8_t descriptor_configuration[] = { // --- CONFIGURATION DESCRIPTOR --- 0x09, // bLength 0x02, // bDescriptorType (CONFIGURATION) U16_TO_U8S_LE(CONFIG_DESC_LEN_TOTAL), // wTotalLength ITF_NUM_TOTAL, // bNumInterfaces 0x01, // bConfigurationValue: 1 0x00, // iConfiguration: 0 0xC0, // bmAttributes: SELF-POWERED, NO REMOTE-WAKEUP 0xFA, // bMaxPower: 500mA (250 * 2mA) #if ENABLE_SERIAL // --- INTERFACE ASSOCIATION DESCRIPTOR: Audio function (interfaces 0-2) --- 0x08, // bLength TUSB_DESC_INTERFACE_ASSOCIATION, // bDescriptorType ITF_NUM_AUDIO_CONTROL, // bFirstInterface 0x03, // bInterfaceCount 0x01, // bFunctionClass: Audio 0x01, // bFunctionSubClass: Audio Control 0x00, // bFunctionProtocol 0x00, // iFunction #endif // --- INTERFACE DESCRIPTOR (0.0): Audio Control --- 0x09, // bLength 0x04, // bDescriptorType (INTERFACE) 0x00, // bInterfaceNumber: 0 0x00, // bAlternateSetting: 0 0x00, // bNumEndpoints: 0 0x01, // bInterfaceClass: Audio (0x01) 0x01, // bInterfaceSubClass: Audio Control (0x01) 0x00, // bInterfaceProtocol: 0x00 0x00, // iInterface: 0 // Class-specific AC Interface Header Descriptor 0x0A, // bLength: 10 0x24, // bDescriptorType: CS_INTERFACE (0x24) 0x01, // bDescriptorSubtype: Header (0x01) 0x00, 0x01, // bcdADC: 1.00 0x49, 0x00, // wTotalLength: 73 (0x0049) 0x02, // bInCollection: 2 streaming interfaces 0x01, // baInterfaceNr(1): Interface 1 0x02, // baInterfaceNr(2): Interface 2 // Input Terminal Descriptor (Terminal ID 1: USB Streaming → Output to Speaker) 0x0C, // bLength: 12 0x24, // bDescriptorType: CS_INTERFACE 0x02, // bDescriptorSubtype: Input Terminal 0x01, // bTerminalID: 1 0x01, 0x01, // wTerminalType: USB Streaming (0x0101) 0x06, // bAssocTerminal: 6 (paired with USB OUT terminal) 0x04, // bNrChannels: 4 0x33, 0x00, // wChannelConfig: L/R Front + L/R Surround (0x0033) 0x00, // iChannelNames: 0 0x00, // iTerminal: 0 // Feature Unit Descriptor (Unit ID 2 ← from Terminal 1) 0x0C, // bLength: 12 0x24, // bDescriptorType: CS_INTERFACE 0x06, // bDescriptorSubtype: Feature Unit 0x02, // bUnitID: 2 0x01, // bSourceID: 1 0x01, // bControlSize: 1 byte per control 0x03, // bmaControls[0]: Master – Mute, Volume 0x00, 0x00, 0x00, 0x00, 0x00, // bmaControls[1..4]: No per-channel controls // Output Terminal Descriptor (Terminal ID 3: Speaker ← from Unit 2) 0x09, // bLength: 9 0x24, // bDescriptorType: CS_INTERFACE 0x03, // bDescriptorSubtype: Output Terminal 0x03, // bTerminalID: 3 0x01, 0x03, // wTerminalType: Speaker (0x0301) 0x04, // bAssocTerminal: 4 (paired with mic input) 0x02, // bSourceID: 2 (Feature Unit) 0x00, // iTerminal: 0 // Input Terminal Descriptor (Terminal ID 4: Headset Mic) 0x0C, // bLength: 12 0x24, // bDescriptorType: CS_INTERFACE 0x02, // bDescriptorSubtype: Input Terminal 0x04, // bTerminalID: 4 0x02, 0x04, // wTerminalType: Headset (0x0402) 0x03, // bAssocTerminal: 3 (paired with speaker) 0x02, // bNrChannels: 2 0x03, 0x00, // wChannelConfig: L/R Front (0x0003) 0x00, // iChannelNames: 0 0x00, // iTerminal: 0 // Feature Unit Descriptor (Unit ID 5 ← from Terminal 4) 0x09, // bLength: 9 0x24, // bDescriptorType: CS_INTERFACE 0x06, // bDescriptorSubtype: Feature Unit 0x05, // bUnitID: 5 0x04, // bSourceID: 4 0x01, // bControlSize: 1 0x03, // bmaControls[0]: Master – Mute, Volume 0x00, // bmaControls[1]: Ch1 – no controls 0x00, // iFeature: 0 // Output Terminal Descriptor (Terminal ID 6: USB Streaming ← from Unit 5) 0x09, // bLength: 9 0x24, // bDescriptorType: CS_INTERFACE 0x03, // bDescriptorSubtype: Output Terminal 0x06, // bTerminalID: 6 0x01, 0x01, // wTerminalType: USB Streaming (0x0101) 0x01, // bAssocTerminal: 1 0x05, // bSourceID: 5 0x00, // iTerminal: 0 // --- INTERFACE DESCRIPTOR (1.0): Audio Streaming (OUT - Alternate 0) --- 0x09, // bLength 0x04, // bDescriptorType (INTERFACE) 0x01, // bInterfaceNumber: 1 0x00, // bAlternateSetting: 0 0x00, // bNumEndpoints: 0 0x01, // bInterfaceClass: Audio 0x02, // bInterfaceSubClass: Audio Streaming 0x00, // bInterfaceProtocol 0x00, // iInterface // --- INTERFACE DESCRIPTOR (1.1): Audio Streaming (OUT - Alternate 1) --- 0x09, // bLength 0x04, // bDescriptorType (INTERFACE) 0x01, // bInterfaceNumber: 1 0x01, // bAlternateSetting: 1 0x01, // bNumEndpoints: 1 0x01, // bInterfaceClass: Audio 0x02, // bInterfaceSubClass: Audio Streaming 0x00, // bInterfaceProtocol 0x00, // iInterface // AS General Descriptor (for Interface 1.1) 0x07, // bLength: 7 0x24, // bDescriptorType: CS_INTERFACE 0x01, // bDescriptorSubtype: AS_GENERAL 0x01, // bTerminalLink: connected to Terminal ID 1 0x01, // bDelay: 1 frame 0x01, 0x00, // wFormatTag: PCM (0x0001) // Format Type Descriptor (4-channel, 16-bit, 48kHz) 0x0B, // bLength: 11 0x24, // bDescriptorType: CS_INTERFACE 0x02, // bDescriptorSubtype: FORMAT_TYPE 0x01, // bFormatType: TYPE_I 0x04, // bNrChannels: 4 0x02, // bSubframeSize: 2 bytes/sample 0x10, // bBitResolution: 16 bits 0x01, // bSamFreqType: 1 discrete frequency 0x80, 0xBB, 0x00, // tSamFreq: 48000 Hz (0x00BB80) // Endpoint Descriptor (Audio OUT: EP1) 0x09, // bLength 0x05, // bDescriptorType (ENDPOINT) 0x01, // bEndpointAddress: OUT EP1 0x09, // bmAttributes: Isochronous, Adaptive 0x88, 0x01, // wMaxPacketSize: 392 bytes 0x01, // bInterval: 1 0x00, // bRefresh 0x00, // bSynchAddress // Class-specific Audio Streaming Endpoint Descriptor (EP1) 0x07, // bLength 0x25, // bDescriptorType: CS_ENDPOINT 0x01, // bDescriptorSubtype: GENERAL 0x00, // Attributes: No pitch/sampling freq control 0x00, // Lock Delay Units: Undefined 0x00, 0x00, // Lock Delay: 0 // --- INTERFACE DESCRIPTOR (2.0): Audio Streaming IN (Alternate 0) --- 0x09, // bLength 0x04, // bDescriptorType (INTERFACE) 0x02, // bInterfaceNumber: 2 0x00, // bAlternateSetting: 0 0x00, // bNumEndpoints: 0 0x01, // bInterfaceClass: Audio 0x02, // bInterfaceSubClass: Audio Streaming 0x00, // bInterfaceProtocol 0x00, // iInterface // --- INTERFACE DESCRIPTOR (2.1): Audio Streaming IN (Alternate 1) --- 0x09, // bLength 0x04, // bDescriptorType (INTERFACE) 0x02, // bInterfaceNumber: 2 0x01, // bAlternateSetting: 1 0x01, // bNumEndpoints: 1 0x01, // bInterfaceClass: Audio 0x02, // bInterfaceSubClass: Audio Streaming 0x00, // bInterfaceProtocol 0x00, // iInterface // AS General Descriptor (for Interface 2.1) 0x07, // bLength: 7 0x24, // bDescriptorType: CS_INTERFACE 0x01, // bDescriptorSubtype: AS_GENERAL 0x06, // bTerminalLink: connected to Terminal ID 6 0x01, // bDelay: 1 frame 0x01, 0x00, // wFormatTag: PCM (0x0001) // Format Type Descriptor (2-channel, 16-bit, 48kHz) 0x0B, // bLength: 11 0x24, // bDescriptorType: CS_INTERFACE 0x02, // bDescriptorSubtype: FORMAT_TYPE 0x01, // bFormatType: TYPE_I 0x02, // bNrChannels: 2 0x02, // bSubframeSize: 2 0x10, // bBitResolution: 16 0x01, // bSamFreqType: 1 0x80, 0xBB, 0x00, // tSamFreq: 48000 Hz // Endpoint Descriptor (Audio IN: EP2) 0x09, // bLength 0x05, // bDescriptorType (ENDPOINT) 0x82, // bEndpointAddress: IN EP2 0x05, // bmAttributes: Isochronous, Asynchronous 0xC4, 0x00, // wMaxPacketSize: 196 bytes 0x01, // bInterval: 1 0x00, // bRefresh 0x00, // bSynchAddress // Class-specific Audio Streaming Endpoint Descriptor (EP2) 0x07, // bLength 0x25, // bDescriptorType: CS_ENDPOINT 0x01, // bDescriptorSubtype: GENERAL 0x00, // Attributes: No controls 0x00, // Lock Delay Units 0x00, 0x00, // Lock Delay // --- INTERFACE DESCRIPTOR (3.0): HID (DualSense 5 Gamepad + Touchpad) --- 0x09, // bLength 0x04, // bDescriptorType (INTERFACE) 0x03, // bInterfaceNumber: 3 0x00, // bAlternateSetting: 0 0x02, // bNumEndpoints: 2 (IN + OUT) 0x03, // bInterfaceClass: HID 0x00, // bInterfaceSubClass: None 0x00, // bInterfaceProtocol: None 0x00, // iInterface // HID Descriptor 0x09, // bLength: 9 0x21, // bDescriptorType (HID) 0x11, 0x01, // bcdHID: 1.11 0x00, // bCountryCode: Not localized 0x01, // bNumDescriptors: 1 report descriptor 0x22, // bDescriptorType: Report 0x41, 0x01, // wDescriptorLength: 321 (0x0141) DS // 0xB5, 0x01, // wDescriptorLength: 437 (0x01B5) DSE // Endpoint Descriptor (HID IN: EP4) 0x07, // bLength 0x05, // bDescriptorType (ENDPOINT) 0x84, // bEndpointAddress: IN EP4 0x03, // bmAttributes: Interrupt 0x40, 0x00, // wMaxPacketSize: 64 0x01, // bInterval: 1 (polling every 4ms -> 1ms) // Endpoint Descriptor (HID OUT: EP3) 0x07, // bLength 0x05, // bDescriptorType (ENDPOINT) 0x03, // bEndpointAddress: OUT EP3 0x03, // bmAttributes: Interrupt 0x40, 0x00, // wMaxPacketSize: 64 0x01, // bInterval: 1 (polling every 4ms -> 1ms) #if ENABLE_SERIAL // --- CDC ACM (USB Serial) --- TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC, 0x85, 0x08, 0x06, 0x86, 0x40), #endif }; // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations auto bInterval = 0x01; switch (get_config().polling_rate_mode) { case 0: bInterval = 0x04; break; case 1: bInterval = 0x02; break; case 2: bInterval = 0x01; break; } constexpr auto offset = CONFIG_DESC_LEN_BASE; descriptor_configuration[offset - 1] = bInterval; descriptor_configuration[offset - 8] = bInterval; if (ds_mode()) { descriptor_configuration[offset - 16] = 0x41; }else { descriptor_configuration[offset - 16] = 0xB5; } return descriptor_configuration; } //--------------------------------------------------------------------+ // HID Report Descriptor //--------------------------------------------------------------------+ uint8_t const desc_hid_report_ds[] = { 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x05, // Usage (Game Pad) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x35, // Usage (Rz) 0x09, 0x33, // Usage (Rx) 0x09, 0x34, // Usage (Ry) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x06, // Report Count (6) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x20, // Usage (0x20) 0x95, 0x01, // Report Count (1) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x39, // Usage (Hat switch) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x07, // Logical Maximum (7) 0x35, 0x00, // Physical Minimum (0) 0x46, 0x3B, 0x01, // Physical Maximum (315) 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) 0x65, 0x00, // Unit (None) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (0x01) 0x29, 0x0F, // Usage Maximum (0x0F) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x0F, // Report Count (15) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x21, // Usage (0x21) 0x95, 0x0D, // Report Count (13) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x22, // Usage (0x22) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x34, // Report Count (52) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x85, 0x02, // Report ID (2) 0x09, 0x23, // Usage (0x23) 0x95, 0x2F, // Report Count (47) 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x05, // Report ID (5) 0x09, 0x33, // Usage (0x33) 0x95, 0x28, // Report Count (40) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x08, // Report ID (8) 0x09, 0x34, // Usage (0x34) 0x95, 0x2F, // Report Count (47) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x09, // Report ID (9) 0x09, 0x24, // Usage (0x24) 0x95, 0x13, // Report Count (19) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x0A, // Report ID (10) 0x09, 0x25, // Usage (0x25) 0x95, 0x1A, // Report Count (26) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x0B, // Report ID (11) 0x09, 0x41, // Usage (0x41) 0x95, 0x29, // Report Count (41) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x0C, // Report ID (12) 0x09, 0x42, // Usage (0x42) 0x95, 0x29, // Report Count (41) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x20, // Report ID (32) 0x09, 0x26, // Usage (0x26) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x21, // Report ID (33) 0x09, 0x27, // Usage (0x27) 0x95, 0x04, // Report Count (4) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x22, // Report ID (34) 0x09, 0x40, // Usage (0x40) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x80, // Report ID (-128) 0x09, 0x28, // Usage (0x28) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x81, // Report ID (-127) 0x09, 0x29, // Usage (0x29) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x82, // Report ID (-126) 0x09, 0x2A, // Usage (0x2A) 0x95, 0x09, // Report Count (9) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x83, // Report ID (-125) 0x09, 0x2B, // Usage (0x2B) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x84, // Report ID (-124) 0x09, 0x2C, // Usage (0x2C) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x85, // Report ID (-123) 0x09, 0x2D, // Usage (0x2D) 0x95, 0x02, // Report Count (2) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xA0, // Report ID (-96) 0x09, 0x2E, // Usage (0x2E) 0x95, 0x01, // Report Count (1) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xE0, // Report ID (-32) 0x09, 0x2F, // Usage (0x2F) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF0, // Report ID (-16) 0x09, 0x30, // Usage (0x30) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF1, // Report ID (-15) 0x09, 0x31, // Usage (0x31) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF2, // Report ID (-14) 0x09, 0x32, // Usage (0x32) 0x95, 0x0F, // Report Count (15) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF4, // Report ID (-12) 0x09, 0x35, // Usage (0x35) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF5, // Report ID (-11) 0x09, 0x36, // Usage (0x36) 0x95, 0x03, // Report Count (3) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF6, // Report ID (-10) 0x09, 0x37, // Usage (Vendor 0x37) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF7, // Report ID (-9) 0x09, 0x38, // Usage (Vendor 0x38) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF8, // Report ID (-8) 0x09, 0x39, // Usage (Vendor 0x39) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF9, // Report ID (-7) 0x09, 0x3A, // Usage (Vendor 0x3A) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection // 321 bytes }; static_assert(sizeof(desc_hid_report_ds) == 0x0141); uint8_t const desc_hid_report_dse[] = { 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x05, // Usage (Game Pad) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x35, // Usage (Rz) 0x09, 0x33, // Usage (Rx) 0x09, 0x34, // Usage (Ry) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x06, // Report Count (6) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x20, // Usage (0x20) 0x95, 0x01, // Report Count (1) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x39, // Usage (Hat switch) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x07, // Logical Maximum (7) 0x35, 0x00, // Physical Minimum (0) 0x46, 0x3B, 0x01, // Physical Maximum (315) 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) 0x65, 0x00, // Unit (None) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (0x01) 0x29, 0x0F, // Usage Maximum (0x0F) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x0F, // Report Count (15) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x21, // Usage (0x21) 0x95, 0x0D, // Report Count (13) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x22, // Usage (0x22) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x34, // Report Count (52) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x85, 0x02, // Report ID (2) 0x09, 0x23, // Usage (0x23) 0x95, 0x3F, // Report Count (63) 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x05, // Report ID (5) 0x09, 0x33, // Usage (0x33) 0x95, 0x28, // Report Count (40) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x08, // Report ID (8) 0x09, 0x34, // Usage (0x34) 0x95, 0x2F, // Report Count (47) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x09, // Report ID (9) 0x09, 0x24, // Usage (0x24) 0x95, 0x13, // Report Count (19) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x0A, // Report ID (10) 0x09, 0x25, // Usage (0x25) 0x95, 0x1A, // Report Count (26) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x0B, // Report ID (11) 0x09, 0x41, // Usage (0x41) 0x95, 0x29, // Report Count (41) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x0C, // Report ID (12) 0x09, 0x42, // Usage (0x42) 0x95, 0x29, // Report Count (41) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x20, // Report ID (32) 0x09, 0x26, // Usage (0x26) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x21, // Report ID (33) 0x09, 0x27, // Usage (0x27) 0x95, 0x04, // Report Count (4) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x22, // Report ID (34) 0x09, 0x40, // Usage (0x40) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x80, // Report ID (-128) 0x09, 0x28, // Usage (0x28) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x81, // Report ID (-127) 0x09, 0x29, // Usage (0x29) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x82, // Report ID (-126) 0x09, 0x2A, // Usage (0x2A) 0x95, 0x09, // Report Count (9) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x83, // Report ID (-125) 0x09, 0x2B, // Usage (0x2B) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x84, // Report ID (-124) 0x09, 0x2C, // Usage (0x2C) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x85, // Report ID (-123) 0x09, 0x2D, // Usage (0x2D) 0x95, 0x02, // Report Count (2) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xA0, // Report ID (-96) 0x09, 0x2E, // Usage (0x2E) 0x95, 0x01, // Report Count (1) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xE0, // Report ID (-32) 0x09, 0x2F, // Usage (0x2F) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF0, // Report ID (-16) 0x09, 0x30, // Usage (0x30) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF1, // Report ID (-15) 0x09, 0x31, // Usage (0x31) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF2, // Report ID (-14) 0x09, 0x32, // Usage (0x32) 0x95, 0x34, // Report Count (52) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF4, // Report ID (-12) 0x09, 0x35, // Usage (0x35) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF5, // Report ID (-11) 0x09, 0x36, // Usage (0x36) 0x95, 0x03, // Report Count (3) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x60, // Report ID (96) 0x09, 0x41, // Usage (0x41) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x61, // Report ID (97) 0x09, 0x42, // Usage (0x42) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x62, // Report ID (98) 0x09, 0x43, // Usage (0x43) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x63, // Report ID (99) 0x09, 0x44, // Usage (0x44) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x64, // Report ID (100) 0x09, 0x45, // Usage (0x45) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x65, // Report ID (101) 0x09, 0x46, // Usage (0x46) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x68, // Report ID (104) 0x09, 0x47, // Usage (0x47) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x70, // Report ID (112) 0x09, 0x48, // Usage (0x48) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x71, // Report ID (113) 0x09, 0x49, // Usage (0x49) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x72, // Report ID (114) 0x09, 0x4A, // Usage (0x4A) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x73, // Report ID (115) 0x09, 0x4B, // Usage (0x4B) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x74, // Report ID (116) 0x09, 0x4C, // Usage (0x4C) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x75, // Report ID (117) 0x09, 0x4D, // Usage (0x4D) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x76, // Report ID (118) 0x09, 0x4E, // Usage (0x4E) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x77, // Report ID (119) 0x09, 0x4F, // Usage (0x4F) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x78, // Report ID (120) 0x09, 0x50, // Usage (0x50) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x79, // Report ID (121) 0x09, 0x51, // Usage (0x51) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x7A, // Report ID (122) 0x09, 0x52, // Usage (0x52) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x7B, // Report ID (123) 0x09, 0x53, // Usage (0x53) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF6, // Report ID (-10) 0x09, 0x37, // Usage (Vendor 0x37) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF7, // Report ID (-9) 0x09, 0x38, // Usage (Vendor 0x38) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF8, // Report ID (-8) 0x09, 0x39, // Usage (Vendor 0x39) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF9, // Report ID (-7) 0x09, 0x3A, // Usage (Vendor 0x3A) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection // 437 bytes }; static_assert(sizeof(desc_hid_report_dse) == 0x01B5); // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf) { (void) itf; if (ds_mode()) { return desc_hid_report_ds; } return desc_hid_report_dse; } //--------------------------------------------------------------------+ // String Descriptors //--------------------------------------------------------------------+ // array of pointer to string descriptors static char const *string_desc_arr[] = { (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) "Sony Interactive Entertainment", // 1: Manufacturer NULL, // 2: Product NULL, // 3: Serials will use unique ID if possible #if ENABLE_SERIAL "USB Serial", // 4: CDC interface #endif }; static uint16_t _desc_str[60 + 1]; // 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; size_t chr_count; if (ds_mode()) { string_desc_arr[2] = "DualSense Wireless Controller"; }else { string_desc_arr[2] = "DualSense Edge Wireless Controller"; } switch (index) { case STRID_LANGID: memcpy(&_desc_str[1], string_desc_arr[0], 2); chr_count = 1; break; case STRID_SERIAL: chr_count = board_usb_get_serial(_desc_str + 1, 32); break; default: // 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); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type if (chr_count > max_count) chr_count = max_count; // Convert ASCII string into UTF-16 for (size_t i = 0; i < chr_count; i++) { _desc_str[1 + i] = str[i]; } break; } // first byte is length (including header), second byte is string type _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); return _desc_str; }