I have been trying to connect my ESP-32 microcontroller to an XBox One S Wireless controller via classic bluetooth using this BTStack implemented by Bluekitchen. I have managed to connect the two devices (with the ESP-32 as the host) and can read all the values from the controller on the ESP-32. I am now trying to work out how to send valid reports to the controller. The descriptor reported by the controller upon connection is this:
05 01 09 05 A1 01 85 01 09 01 A1 00 09 30 09 31 15 00 27 FF FF 00 00 95 02 75 10 81 02 C0 09 01 A1 00 09 33 09 34 15 00 27 FF FF 00 00 95 02 75 10 81 02 C0 05 01 09 32 15 00 26 FF 03 95 01 75 0A 81 02 15 00 25 00 75 06 95 01 81 03 05 01 09 35 15 00 26 FF 03 95 01 75 0A 81 02 15 00 25 00 75 06 95 01 81 03 05 01 09 39 15 01 25 08 35 00 46 3B 01 66 14 00 75 04 95 01 81 42 75 04 95 01 15 00 25 00 35 00 45 00 65 00 81 03 05 09 19 01 29 0A 15 00 25 01 75 01 95 0A 81 02 15 00 25 00 75 06 95 01 81 03 05 01 09 80 85 02 A1 00 09 85 15 00 25 01 95 01 75 01 81 02 15 00 25 00 75 07 95 01 81 03 C0 05 0F 09 21 85 03 A1 02 09 97 15 00 25 01 75 04 95 01 91 02 15 00 25 00 75 04 95 01 91 03 09 70 15 00 25 64 75 08 95 04 91 02 09 50 66 01 10 55 0E 15 00 26 FF 00 75 08 95 01 91 02 09 A7 15 00 26 FF 00 75 08 95 01 91 02 65 00 55 00 09 7C 15 00 26 FF 00 75 08 95 01 91 02 C0 85 04 05 06 09 20 15 00 26 FF 00 75 08 95 01 81 02 C0
When I run this dump through this translator, I get a readable output that is included at the end of the question since it is quite long. Only the output report format of the HID descriptor is listed here:
0x05, 0x0F, // Usage Page (PID Page)
0x09, 0x21, // Usage (0x21)
0x85, 0x03, // Report ID (3)
0xA1, 0x02, // Collection (Logical)
0x09, 0x97, // Usage (0x97)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0x70, // Usage (0x70)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0x50, // Usage (0x50)
0x66, 0x01, 0x10, // Unit (System: SI Linear, Time: Seconds)
0x55, 0x0E, // Unit Exponent (-2)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0xA7, // Usage (0xA7)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x65, 0x00, // Unit (None)
0x55, 0x00, // Unit Exponent (0)
0x09, 0x7C, // Usage (0x7C)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
My understanding of the HID standard would indicate that this part of the descriptor is invalid, since Usage (0x70) is followed by Report Count (4), the btstack parser that I am using to debug why this isn't working then has to try to match 4 bytes to a single usage and isn't able to do this as far as I am aware. Currently the report I am trying to send to the controller (and to the parser, while I am debugging) is 0x03, 0xF0, 0x64, 0x64, 0x64, 0x64, 0xFF, 0x10, 0x05, where the 0x03 indicates the report ID, and the following values indicate parameters I am sending to control rumble motors. The 4 0x64 values are the 4 bytes that should be mapped to 4 usages, however only correspond to 1 usage 0x70 according to the HID Descriptor. At the moment the parser stops after finding the first 0x64 and claims to be finished.
Am I wrong in thinking this HID Descriptor is invalid? If I am, does anyone know what report I should be sending to match this HID Descriptor template. Sorry for the messiness/length of this question.
Thanks in advance,
Richard
Full Readable HID Descriptor Output:
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
0x95, 0x02, // Report Count (2)
0x75, 0x10, // Report Size (16)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x15, 0x00, // Logical Minimum (0)
0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
0x95, 0x02, // Report Count (2)
0x75, 0x10, // Report Size (16)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x32, // Usage (Z)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x95, 0x01, // Report Count (1)
0x75, 0x0A, // Report Size (10)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x75, 0x06, // Report Size (6)
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x35, // Usage (Rz)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x95, 0x01, // Report Count (1)
0x75, 0x0A, // Report Size (10)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x75, 0x06, // Report Size (6)
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x08, // Logical Maximum (8)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x66, 0x14, 0x00, // 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)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x00, // Physical Maximum (0)
0x65, 0x00, // Unit (None)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0A, // Usage Maximum (0x0A)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0A, // Report Count (10)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x75, 0x06, // Report Size (6)
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x80, // Usage (Sys Control)
0x85, 0x02, // Report ID (2)
0xA1, 0x00, // Collection (Physical)
0x09, 0x85, // Usage (Sys Main Menu)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x01, // Report Count (1)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x75, 0x07, // Report Size (7)
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x0F, // Usage Page (PID Page)
0x09, 0x21, // Usage (0x21)
0x85, 0x03, // Report ID (3)
0xA1, 0x02, // Collection (Logical)
0x09, 0x97, // Usage (0x97)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x00, // Logical Maximum (0)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0x70, // Usage (0x70)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x64, // Logical Maximum (100)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0x50, // Usage (0x50)
0x66, 0x01, 0x10, // Unit (System: SI Linear, Time: Seconds)
0x55, 0x0E, // Unit Exponent (-2)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0xA7, // Usage (0xA7)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x65, 0x00, // Unit (None)
0x55, 0x00, // Unit Exponent (0)
0x09, 0x7C, // Usage (0x7C)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0x85, 0x04, // Report ID (4)
0x05, 0x06, // Usage Page (Generic Dev Ctrls)
0x09, 0x20, // Usage (Battery Strength)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
I know you've found the issue, but this may be of interest to others. I decoded the HID descriptor with hidrdd (which points out errors and suggests improvements). The result is as follows:
//--------------------------------------------------------------------------------
// Report descriptor data in hex (length 306 bytes)
//--------------------------------------------------------------------------------
// 05010905 A1018501 0901A100 09300931 150027FF FF000095 02751081 02C00901
// A1000933 09341500 27FFFF00 00950275 108102C0 05010932 150026FF 03950175
// 0A810215 00250075 06950181 03050109 35150026 FF039501 750A8102 15002500
// 75069501 81030501 09391501 25083500 463B0166 14007504 95018142 75049501
// 15002500 35004500 65008103 05091901 290A1500 25017501 950A8102 15002500
// 75069501 81030501 09808502 A1000985 15002501 95017501 81021500 25007507
// 95018103 C0050F09 218503A1 02099715 00250175 04950191 02150025 00750495
// 01910309 70150025 64750895 04910209 50660110 550E1500 26FF0075 08950191
// 0209A715 0026FF00 75089501 91026500 5500097C 150026FF 00750895 019102C0
// 85040506 09201500 26FF0075 08950181 02C0
//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------
/*
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
09 05 (LOCAL) USAGE 0x00010005 Game Pad (Application Collection)
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x00010005: Page=Generic Desktop Page, Usage=Game Pad, Type=Application Collection)
85 01 (GLOBAL) REPORT_ID 0x01 (1)
09 01 (LOCAL) USAGE 0x00010001 Pointer (Physical Collection)
A1 00 (MAIN) COLLECTION 0x00 Physical (Usage=0x00010001: Page=Generic Desktop Page, Usage=Pointer, Type=Physical Collection)
09 30 (LOCAL) USAGE 0x00010030 X (Dynamic Value)
09 31 (LOCAL) USAGE 0x00010031 Y (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
27 FFFF0000 (GLOBAL) LOGICAL_MAXIMUM 0x0000FFFF (65535)
95 02 (GLOBAL) REPORT_COUNT 0x02 (2) Number of fields
75 10 (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
81 02 (MAIN) INPUT 0x00000002 (2 fields x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Physical
09 01 (LOCAL) USAGE 0x00010001 Pointer (Physical Collection)
A1 00 (MAIN) COLLECTION 0x00 Physical (Usage=0x00010001: Page=Generic Desktop Page, Usage=Pointer, Type=Physical Collection)
09 33 (LOCAL) USAGE 0x00010033 Rx (Dynamic Value)
09 34 (LOCAL) USAGE 0x00010034 Ry (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
27 FFFF0000 (GLOBAL) LOGICAL_MAXIMUM 0x0000FFFF (65535) <-- Redundant: LOGICAL_MAXIMUM is already 65535
95 02 (GLOBAL) REPORT_COUNT 0x02 (2) Number of fields <-- Redundant: REPORT_COUNT is already 2
75 10 (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field <-- Redundant: REPORT_SIZE is already 16
81 02 (MAIN) INPUT 0x00000002 (2 fields x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Physical
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page <-- Redundant: USAGE_PAGE is already 0x0001
09 32 (LOCAL) USAGE 0x00010032 Z (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
26 FF03 (GLOBAL) LOGICAL_MAXIMUM 0x03FF (1023)
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
75 0A (GLOBAL) REPORT_SIZE 0x0A (10) Number of bits per field
81 02 (MAIN) INPUT 0x00000002 (1 field x 10 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 00 (GLOBAL) LOGICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 25 00 with 24
75 06 (GLOBAL) REPORT_SIZE 0x06 (6) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
81 03 (MAIN) INPUT 0x00000003 (1 field x 6 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page <-- Redundant: USAGE_PAGE is already 0x0001
09 35 (LOCAL) USAGE 0x00010035 Rz (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
26 FF03 (GLOBAL) LOGICAL_MAXIMUM 0x03FF (1023)
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
75 0A (GLOBAL) REPORT_SIZE 0x0A (10) Number of bits per field
81 02 (MAIN) INPUT 0x00000002 (1 field x 10 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 00 (GLOBAL) LOGICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 25 00 with 24
75 06 (GLOBAL) REPORT_SIZE 0x06 (6) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
81 03 (MAIN) INPUT 0x00000003 (1 field x 6 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page <-- Redundant: USAGE_PAGE is already 0x0001
09 39 (LOCAL) USAGE 0x00010039 Hat switch (Dynamic Value)
15 01 (GLOBAL) LOGICAL_MINIMUM 0x01 (1)
25 08 (GLOBAL) LOGICAL_MAXIMUM 0x08 (8)
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
46 3B01 (GLOBAL) PHYSICAL_MAXIMUM 0x013B (315)
66 1400 (GLOBAL) UNIT 0x0014 Rotation in degrees [1° units] (4=System=English Rotation, 1=Rotation=Degrees) <-- Info: Consider replacing 66 1400 with 65 14
75 04 (GLOBAL) REPORT_SIZE 0x04 (4) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
81 42 (MAIN) INPUT 0x00000042 (1 field x 4 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
75 04 (GLOBAL) REPORT_SIZE 0x04 (4) Number of bits per field <-- Redundant: REPORT_SIZE is already 4
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
25 00 (GLOBAL) LOGICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 25 00 with 24
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Redundant: PHYSICAL_MINIMUM is already 0 <-- Info: Consider replacing 35 00 with 34
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
65 00 (GLOBAL) UNIT 0x00 No unit (0=None) <-- Info: Consider replacing 65 00 with 64
81 03 (MAIN) INPUT 0x00000003 (1 field x 4 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
05 09 (GLOBAL) USAGE_PAGE 0x0009 Button Page
19 01 (LOCAL) USAGE_MINIMUM 0x00090001 Button 1 Primary/trigger (Selector, On/Off Control, Momentary Control, or One Shot Control)
29 0A (LOCAL) USAGE_MAXIMUM 0x0009000A Button 10 (Selector, On/Off Control, Momentary Control, or One Shot Control)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
75 01 (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
95 0A (GLOBAL) REPORT_COUNT 0x0A (10) Number of fields
81 02 (MAIN) INPUT 0x00000002 (10 fields x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 00 (GLOBAL) LOGICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 25 00 with 24
75 06 (GLOBAL) REPORT_SIZE 0x06 (6) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
81 03 (MAIN) INPUT 0x00000003 (1 field x 6 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
09 80 (LOCAL) USAGE 0x00010080 System Control (Application Collection)
85 02 (GLOBAL) REPORT_ID 0x02 (2)
A1 00 (MAIN) COLLECTION 0x00 Physical (Usage=0x00010080: Page=Generic Desktop Page, Usage=System Control, Type=Application Collection) <-- Warning: USAGE type should be CP (Physical Collection)
09 85 (LOCAL) USAGE 0x00010085 System Main Menu (One Shot Control)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
75 01 (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
81 02 (MAIN) INPUT 0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 00 (GLOBAL) LOGICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 25 00 with 24
75 07 (GLOBAL) REPORT_SIZE 0x07 (7) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
81 03 (MAIN) INPUT 0x00000003 (1 field x 7 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Physical
05 0F (GLOBAL) USAGE_PAGE 0x000F Physical Interface Device Page
09 21 (LOCAL) USAGE 0x000F0021 Set Effect Report (Logical Collection)
85 03 (GLOBAL) REPORT_ID 0x03 (3)
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000F0021: Page=Physical Interface Device Page, Usage=Set Effect Report, Type=Logical Collection)
09 97 (LOCAL) USAGE 0x000F0097 DC Enable Actuators (Selector)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
75 04 (GLOBAL) REPORT_SIZE 0x04 (4) Number of bits per field
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
91 02 (MAIN) OUTPUT 0x00000002 (1 field x 4 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 00 (GLOBAL) LOGICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 25 00 with 24
75 04 (GLOBAL) REPORT_SIZE 0x04 (4) Number of bits per field <-- Redundant: REPORT_SIZE is already 4
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
91 03 (MAIN) OUTPUT 0x00000003 (1 field x 4 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
09 70 (LOCAL) USAGE 0x000F0070 Magnitude (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 64 (GLOBAL) LOGICAL_MAXIMUM 0x64 (100)
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
09 50 (LOCAL) USAGE 0x000F0050 Duration (Dynamic Value)
66 0110 (GLOBAL) UNIT 0x1001 Time in seconds [1 s units] (1=System=SI Linear, 1=Time=Seconds)
55 0E (GLOBAL) UNIT_EXPONENT 0x0E (Unit Value x 10⁻²)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
26 FF00 (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255)
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
91 02 (MAIN) OUTPUT 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
09 A7 (LOCAL) USAGE 0x000F00A7 Start Delay (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
26 FF00 (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255) <-- Redundant: LOGICAL_MAXIMUM is already 255
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
91 02 (MAIN) OUTPUT 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
65 00 (GLOBAL) UNIT 0x00 No unit (0=None) <-- Info: Consider replacing 65 00 with 64
55 00 (GLOBAL) UNIT_EXPONENT 0x00 (Unit Value x 10⁰) <-- Info: Consider replacing 55 00 with 54
09 7C (LOCAL) USAGE 0x000F007C Loop Count (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
26 FF00 (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255) <-- Redundant: LOGICAL_MAXIMUM is already 255
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
91 02 (MAIN) OUTPUT 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Logical
85 04 (GLOBAL) REPORT_ID 0x04 (4)
05 06 (GLOBAL) USAGE_PAGE 0x0006 Generic Device Controls Page
09 20 (LOCAL) USAGE 0x00060020 Battery Strength (Dynamic Value)
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
26 FF00 (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255) <-- Redundant: LOGICAL_MAXIMUM is already 255
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
81 02 (MAIN) INPUT 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
C0 (MAIN) END_COLLECTION Application
*/
//--------------------------------------------------------------------------------
// Generic Desktop Page inputReport 01 (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x01 (1)
// Collection: CA:GamePad CP:Pointer
uint16_t GD_GamePadPointerX; // Usage 0x00010030: X, Value = 0 to 65535
uint16_t GD_GamePadPointerY; // Usage 0x00010031: Y, Value = 0 to 65535
uint16_t GD_GamePadPointerRx; // Usage 0x00010033: Rx, Value = 0 to 65535
uint16_t GD_GamePadPointerRy; // Usage 0x00010034: Ry, Value = 0 to 65535
// Collection: CA:GamePad
uint16_t GD_GamePadZ : 10; // Usage 0x00010032: Z, Value = 0 to 1023
uint8_t : 6; // Pad
uint16_t GD_GamePadRz : 10; // Usage 0x00010035: Rz, Value = 0 to 1023
uint8_t : 6; // Pad
uint8_t GD_GamePadHatSwitch : 4; // Usage 0x00010039: Hat switch, Value = 1 to 8, Physical = (Value - 1) x 45 in degrees
uint8_t : 4; // Pad
uint8_t BTN_GamePadButton1 : 1; // Usage 0x00090001: Button 1 Primary/trigger, Value = 0 to 1
uint8_t BTN_GamePadButton2 : 1; // Usage 0x00090002: Button 2 Secondary, Value = 0 to 1
uint8_t BTN_GamePadButton3 : 1; // Usage 0x00090003: Button 3 Tertiary, Value = 0 to 1
uint8_t BTN_GamePadButton4 : 1; // Usage 0x00090004: Button 4, Value = 0 to 1
uint8_t BTN_GamePadButton5 : 1; // Usage 0x00090005: Button 5, Value = 0 to 1
uint8_t BTN_GamePadButton6 : 1; // Usage 0x00090006: Button 6, Value = 0 to 1
uint8_t BTN_GamePadButton7 : 1; // Usage 0x00090007: Button 7, Value = 0 to 1
uint8_t BTN_GamePadButton8 : 1; // Usage 0x00090008: Button 8, Value = 0 to 1
uint8_t BTN_GamePadButton9 : 1; // Usage 0x00090009: Button 9, Value = 0 to 1
uint8_t BTN_GamePadButton10 : 1; // Usage 0x0009000A: Button 10, Value = 0 to 1
uint8_t : 6; // Pad
} inputReport01_t;
//--------------------------------------------------------------------------------
// Generic Desktop Page inputReport 02 (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x02 (2)
// Collection: CA:GamePad CP:SystemControl
uint8_t GD_GamePadSystemControlSystemMainMenu : 1; // Usage 0x00010085: System Main Menu, Value = 0 to 1
uint8_t : 7; // Pad
} inputReport02_t;
//--------------------------------------------------------------------------------
// Generic Device Controls Page inputReport 04 (Device --> Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x04 (4)
// Collection: CA:GamePad
uint8_t GEN_GamePadBatteryStrength; // Usage 0x00060020: Battery Strength, Value = 0 to 255
} inputReport04_t;
//--------------------------------------------------------------------------------
// Physical Interface Device Page outputReport 03 (Device <-- Host)
//--------------------------------------------------------------------------------
typedef struct
{
uint8_t reportId; // Report ID = 0x03 (3)
// Collection: CA:GamePad CL:SetEffectReport
uint8_t PID_GamePadSetEffectReportDcEnableActuators : 4; // Usage 0x000F0097: DC Enable Actuators, Value = 0 to 1
uint8_t : 4; // Pad
uint8_t PID_GamePadSetEffectReportMagnitude[4]; // Usage 0x000F0070: Magnitude, Value = 0 to 100
uint8_t PID_GamePadSetEffectReportDuration; // Usage 0x000F0050: Duration, Value = 0 to 255, Physical = Value in 10⁻² s units
uint8_t PID_GamePadSetEffectReportStartDelay; // Usage 0x000F00A7: Start Delay, Value = 0 to 255, Physical = Value in 10⁻² s units
uint8_t PID_GamePadSetEffectReportLoopCount; // Usage 0x000F007C: Loop Count, Value = 0 to 255
} outputReport03_t;
The report descriptor seems to be generally OK. I could optimise it down from 306 bytes to just 191 bytes - representing exactly the same data flows (see below).
As for the 0x70 usage, it looks to me like there are 4 actuators which can be enabled using the 4-bit DcEnableActuators usage (0x97). So I would guess that you should be sending:
0x03, 0x0F, 0x64, 0x64, 0x64, 0x64, 0xFF, 0x10, 0x05
in order to enable all 4 of them instead of:
0x03, 0xF0, 0x64, 0x64, 0x64, 0x64, 0xFF, 0x10, 0x05
Just for fun, here is an optimised version of the report descriptor that they could have used instead:
//--------------------------------------------------------------------------------
// Report descriptor data in hex (length 191 bytes)
//--------------------------------------------------------------------------------
// 05010905 A1018501 0901A100 09300931 09330934 1427FFFF 00009504 75108102
// C0093226 FF039501 750A8102 24750681 03093526 FF03750A 81022475 06810309
// 39150125 0834463B 01651475 04814214 24446481 03050919 01290A25 01750195
// 0A810224 75069501 81030501 09808502 A1000985 25017501 81022475 078103C0
// 050F0921 8503A102 09972501 75049102 24910309 70256475 08950491 02095066
// 0110550E 26FF0095 01910209 A7910264 54097C91 02C08504 05060920 8102C0
It turns out the controller had disabled the vibration motors since the battery was "low", and sending the report described in the question (with a 0xA2 at the start) worked as I intended it to when I had replaced the batteries. I'm still not sure if the HID Descriptor is actually syntactically valid, but the controller does what I want it to now at least!
I need to generate M1, M2, and M3 in order to reprogram a security key within a SHE module. I have verified that both my AES_128_ECB function, as well as my CMAC function are working properly against NIST test vectors. I am also able to generate M1 and M2 correctly without issue, however, when I input M1||M2 into my CMAC function, the result I am getting is not the result for M3 that I should be getting according to the SHE test vector for memory reprogramming in the SHE documentation.
Here are all of the inputs and results for each of the steps of the M1,M2,M3 generating process:
New Key: 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
Auth Key: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
M1: 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 1
KDF result 1 K1: 7a cb d da b8 d3 ea 7b 97 9e 4c 6d 1a eb ac 8d
KDF result 1 K2: 7a cb d da b8 d3 ea 7b 97 9e 4c 6d 1a eb ac 8d
KDF result 2 K1: 6a 40 18 d6 87 a4 67 fc 15 14 25 af 38 9 7d 43
KDF result 2 K2: 55 72 74 af 5b fe d7 1f 26 15 ea 24 24 74 12 1f
K1: 11 8a 46 44 7a 77 d 87 82 8a 69 c2 22 e2 d1 7e
K2: 2e bb 2a 3d a6 2d bd 64 b1 8b a6 49 3e 9f be 22
input 1 M2: 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0
output 1 M2: 2b 11 1e 2d 93 f4 86 56 6b cb ba 1d 7f 7a 97 97
output 1 xor input 2 M2: 24 1f 13 21 98 fe 8f 5e 6c cd bf 19 7c 78 96 97
output 2 M2: c9 46 43 b0 50 fc 5d 4d 7d e1 4c ff 68 22 3 c3
M2: 2b 11 1e 2d 93 f4 86 56 6b cb ba 1d 7f 7a 97 97 c9 46 43 b0 50 fc 5d 4d 7d e1 4c ff 68 22 3 c3
input M3: 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 1 2b 11 1e 2d 93 f4 86 56 6b cb ba 1d 7f 7a 97 97 c9 46 43 b0 50 fc 5d 4d 7d e1 4c ff 68 22 3 c3
M3: c 12 41 48 ff d6 fa f7 e4 25 6a 84 53 b2 81 8d
This is driving me crazy. The input data to the generation algorithm were taken directly from the SHE test vector. According to the algorithm all I should have to do to get M3 is to run a CMAC over M1||M2, but my result does not match what it should be.
I can only assume I am missing some extra step that is needed for M3, beyond just running the CMAC over M1||M2. Does anybody here have any experience with SHE modules and generating the data to reprogram a key?
Tried rearranging the order of M1||M2 with no luck
byte KEY_UPDATE_ENC_C[16] = { 0x01, 0x01, 0x53, 0x48, 0x45, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0 };
byte KEY_UPDATE_MAC_C[16] = { 0x01, 0x02, 0x53, 0x48, 0x45, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0 };
byte keyAuth[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
byte keyNew[16] = { 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 } ;
byte iniVec[16];
byte k1_o1[16];
byte k1[16];
byte k2_o1[16];
byte k2[16];
byte m1[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01 }
byte m2_i1[16] = { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte m2_o1[16];
byte m2_o2[16];
byte m2[32];
byte m3_i[48];
byte m3[16];
qword i;
...
// Generate K1, K2
encrypt( KEY_LEN_128, iniVec, BLOCK_LEN_128, keyAuth, k2_o1 );
for( i = 0; i < BLOCK_LEN_128; i++ )
k1_o1[i] = k2_o1[i] ^= keyAuth[i];
encrypt( KEY_LEN_128, k1_o1, BLOCK_LEN_128, KEY_UPDATE_ENC_C, k1 );
encrypt( KEY_LEN_128, k2_o1, BLOCK_LEN_128, KEY_UPDATE_MAC_C, k2 );
for( i = 0; i < BLOCK_LEN_128; i++ ) {
k1[i] ^= k1_o1[i] ^ KEY_UPDATE_ENC_C[i];
k2[i] ^= k2_o1[i] ^ KEY_UPDATE_MAC_C[i]; }
// Generate M2
encrypt( KEY_LEN_128, k1, BLOCK_LEN_128, m2_i1, m2_o1 );
for( i = 0; i < BLOCK_LEN_128; i++ )
keyNew[i] ^= m2_o1[i];
encrypt( KEY_LEN_128, k1, BLOCK_LEN_128, keyNew, m2_o2 );
for( i = 0; i < 32; i++ ) {
if( i < 16 )
m2[i] = m2_o1[i];
else
m2[i] = m2_o2[i-16]; }
// Generate M3
for( i = 0; i < 48; i++ ) {
if( i < 16 )
m3_i[i] = m1[i];
else
m3_i[i] = m2[i-16]; }
LocalSecurityGenerateCMAC( k2, KEY_LEN_128, m3_i, 48, m3, BLOCK_LEN_128 );
M3 / CMAC result should be: b9 d7 45 e5 ac e7 d4 18 60 bc 63 c2 b9 f5 bb 46