def xac_compatible_gamepad_hid_descriptor(report_id): data = HID_DEVICE_DATA["XAC_COMPATIBLE_GAMEPAD"] return hid.ReportDescriptor( description="XAC", report_descriptor=bytes( # This descriptor mimics the simple joystick from PDP that the XBox likes (0x05, data.usage_page, # Usage Page (Desktop), 0x09, data.usage, # Usage (Gamepad), 0xA1, 0x01, # Collection (Application), ) + ((0x85, report_id) if report_id != 0 else ()) + (0x15, 0x00, # Logical Minimum (0), 0x25, 0x01, # Logical Maximum (1), 0x35, 0x00, # Physical Minimum (0), 0x45, 0x01, # Physical Maximum (1), 0x75, 0x01, # Report Size (1), 0x95, 0x08, # Report Count (8), 0x05, 0x09, # Usage Page (Button), 0x19, 0x01, # Usage Minimum (01h), 0x29, 0x08, # Usage Maximum (08h), 0x81, 0x02, # Input (Variable), 0x05, 0x01, # Usage Page (Desktop), 0x26, 0xFF, 0x00, # Logical Maximum (255), 0x46, 0xFF, 0x00, # Physical Maximum (255), 0x09, 0x30, # Usage (X), 0x09, 0x31, # Usage (Y), 0x75, 0x08, # Report Size (8), 0x95, 0x02, # Report Count (2), 0x81, 0x02, # Input (Variable), 0xC0 # End Collection )))
def consumer_hid_descriptor(report_id): data = HID_DEVICE_DATA["CONSUMER"] return hid.ReportDescriptor( description="CONSUMER", report_descriptor=bytes( # Consumer ("multimedia") keys ( 0x05, data.usage_page, # Usage Page (Consumer) 0x09, data.usage, # Usage (Consumer Control) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + ( 0x75, 0x10, # Report Size (16) 0x95, 0x01, # Report Count (1) 0x15, 0x01, # Logical Minimum (1) 0x26, 0x8C, 0x02, # Logical Maximum (652) 0x19, 0x01, # Usage Minimum (Consumer Control) 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send) 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection )))
def gamepad_hid_descriptor(report_id): data = HID_DEVICE_DATA["GAMEPAD"] return hid.ReportDescriptor( description="GAMEPAD", report_descriptor=bytes( # Gamepad with 16 buttons and two joysticks (0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls) 0x09, data.usage, # Usage (Game Pad) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x05, 0x09, # Usage Page (Button) 0x19, 0x01, # Usage Minimum (Button 1) 0x29, 0x10, # Usage Maximum (Button 16) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x10, # Report Count (16) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x15, 0x81, # Logical Minimum (-127) 0x25, 0x7F, # Logical Maximum (127) 0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y) 0x09, 0x32, # Usage (Z) 0x09, 0x35, # Usage (Rz) 0x75, 0x08, # Report Size (8) 0x95, 0x04, # Report Count (4) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection )))
def throttlecontroller_hid_descriptor(report_id): data = HID_DEVICE_DATA["QUADRANT"] return hid.ReportDescriptor( description="QUADRANT", report_descriptor=bytes( # Gamepad with 16 buttons and two joysticks (0x05, data.usage_page, # Usage Page (Desktop Ctrls) 0x09, data.usage, # Usage (Joystick) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x09, 0x01, # Usage (Pointer) 0xA1, 0x00, # Collection (Physical) 0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y) 0x09, 0x32, # Usage (Z) 0x15, 0x00, # Logical Minimum (0) 0x26, 0xFF, 0x00, # Logical Maximum (255) 0x75, 0x08, # Report Size (8) 0x95, 0x03, # Report Count (3) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x09, # Usage Page (Button) 0x19, 0x01, # Usage Minimum (0x01) 0x29, 0x09, # Usage Maximum (0x09) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x09, # Report Count (9) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x75, 0x07, # Report Size (7) 0x95, 0x01, # Report Count (1) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection 0xC0, # End Collection )))
def joystick_hid_descriptor(report_id): data = HID_DEVICE_DATA["JOYSTICK"] return hid.ReportDescriptor( description="JOYSTICK", report_descriptor=bytes( # Joystick with 3 buttons and 3 joysticks (0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls) 0x09, data.usage, # Usage (Joystick) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x09, 0x01, # Usage (Pointer) 0xA1, 0x00, # Collection (Physical) 0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y) 0x15, 0x00, # Logical Minimum (0) 0x26, 0xFF, 0x00, # Logical Maximum (255) 0x35, 0x00, # Physical Minimum (0) 0x46, 0xFF, 0x00, # Physical Maximum (255) 0x75, 0x08, # Report Size (8) 0x95, 0x02, # Report Count (2) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x32, # Usage (Z) 0x15, 0x00, # Logical Minimum (0) 0x26, 0xFF, 0x00, # Logical Maximum (255) 0x35, 0x00, # Physical Minimum (0) 0x46, 0x00, 0xFF, # Physical Maximum (-256) 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 0x05, 0x09, # Usage Page (Button) 0x19, 0x01, # Usage Minimum (0x01) 0x29, 0x03, # Usage Maximum (0x03) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x03, # Report Count (3) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x75, 0x05, # Report Size (5) 0x95, 0x01, # Report Count (1) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection )))
def mouse_hid_descriptor(report_id): data = HID_DEVICE_DATA["MOUSE"] return hid.ReportDescriptor( description="MOUSE", report_descriptor=bytes( # Regular mouse (0x05, data.usage_page, # Usage Page (Generic Desktop) 0x09, data.usage, # Usage (Mouse) 0xA1, 0x01, # Collection (Application) 0x09, 0x01, # Usage (Pointer) 0xA1, 0x00, # Collection (Physical) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x05, 0x09, # Usage Page (Button) 0x19, 0x01, # Usage Minimum (0x01) 0x29, 0x05, # Usage Maximum (0x05) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x95, 0x05, # Report Count (5) 0x75, 0x01, # Report Size (1) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x01, # Report Count (1) 0x75, 0x03, # Report Size (3) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y) 0x15, 0x81, # Logical Minimum (-127) 0x25, 0x7F, # Logical Maximum (127) 0x75, 0x08, # Report Size (8) 0x95, 0x02, # Report Count (2) 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x38, # Usage (Wheel) 0x15, 0x81, # Logical Minimum (-127) 0x25, 0x7F, # Logical Maximum (127) 0x75, 0x08, # Report Size (8) 0x95, 0x01, # Report Count (1) 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection 0xC0, # End Collection )))
def raw_hid_descriptor(report_id): if report_id != 0: raise ValueError("raw hid must not have a report id") data = HID_DEVICE_DATA["RAW"] return hid.ReportDescriptor( description="RAW", report_descriptor=bytes( # Provide vendor-defined # This is a two-byte page value. ( 0x06, data.usage_page & 0xff, (data.usage_page >> 8) & 0xff, # Usage Page (Vendor 0xFFAF "Adafruit"), 0x09, data.usage, # Usage (AF), 0xA1, 0x01, # Collection (Application), 0x75, 0x08, # Report Size (8), 0x15, 0x00, # Logical Minimum (0), 0x26, 0xFF, 0x00, # Logical Maximum (255), 0x95, 0x08, # Report Count (8), 0x09, 0x01, # Usage(xxx) 0x81, 0x02, # Input (Variable) 0x95, 0x08, # Report Count (8), 0x09, 0x02, # Usage(xxx) 0x91, 0x02, # Input (Variable) 0xC0 # End Collection )))
def keyboard_hid_descriptor(report_id): data = HID_DEVICE_DATA["KEYBOARD"] return hid.ReportDescriptor( description="KEYBOARD", report_descriptor=bytes( # Regular keyboard (0x05, data.usage_page, # Usage Page (Generic Desktop) 0x09, data.usage, # Usage (Keyboard) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x05, 0x07, # Usage Page (Keyboard) 0x19, 224, # Usage Minimum (224) 0x29, 231, # Usage Maximum (231) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x08, # Report Count (8) 0x81, 0x02, # Input (Data, Variable, Absolute) 0x81, 0x01, # Input (Constant) 0x19, 0x00, # Usage Minimum (0) 0x29, 0xDD, # Usage Maximum (221) 0x15, 0x00, # Logical Minimum (0) 0x25, 0xDD, # Logical Maximum (221) 0x75, 0x08, # Report Size (8) 0x95, 0x06, # Report Count (6) 0x81, 0x00, # Input (Data, Array) 0x05, 0x08, # Usage Page (LED) 0x19, 0x01, # Usage Minimum (1) 0x29, 0x05, # Usage Maximum (5) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x05, # Report Count (5) 0x91, 0x02, # Output (Data, Variable, Absolute) 0x95, 0x03, # Report Count (3) 0x91, 0x01, # Output (Constant) 0xC0, # End Collection )))
def sys_control_hid_descriptor(report_id): data = HID_DEVICE_DATA["SYS_CONTROL"] return hid.ReportDescriptor( description="SYS_CONTROL", report_descriptor=bytes( # Power controls (0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls) 0x09, data.usage, # Usage (Sys Control) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x75, 0x02, # Report Size (2) 0x95, 0x01, # Report Count (1) 0x15, 0x01, # Logical Minimum (1) 0x25, 0x03, # Logical Maximum (3) 0x09, 0x82, # Usage (Sys Sleep) 0x09, 0x81, # Usage (Sys Power Down) 0x09, 0x83, # Usage (Sys Wake Up) 0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State) 0x75, 0x06, # Report Size (6) 0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, # End Collection )))
def digitizer_hid_descriptor(report_id): data = HID_DEVICE_DATA["DIGITIZER"] return hid.ReportDescriptor( description="DIGITIZER", report_descriptor=bytes( # Digitizer (used as an absolute pointer) (0x05, data.usage_page, # Usage Page (Digitizers) 0x09, data.usage, # Usage (Pen) 0xA1, 0x01, # Collection (Application) ) + ((0x85, report_id) if report_id != 0 else ()) + (0x09, 0x01, # Usage (Stylus) 0xA1, 0x00, # Collection (Physical) 0x09, 0x32, # Usage (In-Range) 0x09, 0x42, # Usage (Tip Switch) 0x09, 0x44, # Usage (Barrel Switch) 0x09, 0x45, # Usage (Eraser Switch) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x04, # Report Count (4) 0x81, 0x02, # Input (Data,Var,Abs) 0x75, 0x04, # Report Size (4) -- Filler 0x95, 0x01, # Report Count (1) -- Filler 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x15, 0x00, # Logical Minimum (0) 0x26, 0xff, 0x7f, # Logical Maximum (32767) 0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y) 0x75, 0x10, # Report Size (16) 0x95, 0x02, # Report Count (2) 0x81, 0x02, # Input (Data,Var,Abs) 0xC0, # End Collection 0xC0, # End Collection )))
bmAttributes=standard.EndpointDescriptor.TYPE_BULK, bInterval=0) ] ) ] # When there's only one hid_device, it shouldn't have a report id. # Otherwise, report ids are assigned sequentially: # args.hid_devices[0] has report_id 1 # args.hid_devices[1] has report_id 2 # etc. if len(args.hid_devices) == 1: name = args.hid_devices[0] combined_hid_report_descriptor = hid.ReportDescriptor( description=name, report_descriptor=bytes(hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](0))) else: report_id = 1 concatenated_descriptors = bytearray() for name in args.hid_devices: concatenated_descriptors.extend( bytes(hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](report_id))) report_id += 1 combined_hid_report_descriptor = hid.ReportDescriptor( description="MULTIDEVICE", report_descriptor=bytes(concatenated_descriptors)) # ASF4 expects keyboard and generic devices to have both in and out endpoints, # and will fail (possibly silently) if both are not supplied. hid_endpoint_in_descriptor = standard.EndpointDescriptor(
KEYBOARD_WITH_ID = hid.ReportDescriptor( description="KEYBOARD", report_descriptor=bytes([ # Regular keyboard 0x05, 0x01, # Usage Page (Generic Desktop) 0x09, 0x06, # Usage (Keyboard) 0xA1, 0x01, # Collection (Application) 0x85, REPORT_IDS["KEYBOARD"], # Report ID (1) 0x05, 0x07, # Usage Page (Keyboard) 0x19, 224, # Usage Minimum (224) 0x29, 231, # Usage Maximum (231) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x08, # Report Count (8) 0x81, 0x02, # Input (Data, Variable, Absolute) 0x81, 0x01, # Input (Constant) 0x19, 0x00, # Usage Minimum (0) 0x29, 101, # Usage Maximum (101) 0x15, 0x00, # Logical Minimum (0) 0x25, 101, # Logical Maximum (101) 0x75, 0x08, # Report Size (8) 0x95, 0x06, # Report Count (6) 0x81, 0x00, # Input (Data, Array) 0x05, 0x08, # Usage Page (LED) 0x19, 0x01, # Usage Minimum (1) 0x29, 0x05, # Usage Maximum (5) 0x15, 0x00, # Logical Minimum (0) 0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1) 0x95, 0x05, # Report Count (5) 0x91, 0x02, # Output (Data, Variable, Absolute) 0x95, 0x03, # Report Count (3) 0x91, 0x01, # Output (Constant) 0xC0, # End Collection ]))
bInterval=0), standard.EndpointDescriptor( description="MSC out", bEndpointAddress=0x1 | standard.EndpointDescriptor.DIRECTION_OUT, bmAttributes=standard.EndpointDescriptor.TYPE_BULK, bInterval=0) ] ) ] # Include only these HID devices. # DIGITIZER works on Linux but conflicts with MOUSE, so leave it out for now. hid_devices = ("KEYBOARD", "MOUSE", "CONSUMER", "GAMEPAD") combined_hid_report_descriptor = hid.ReportDescriptor( description="MULTIDEVICE", report_descriptor=b''.join( hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor for name in hid_devices )) hid_report_ids_dict = { name: hid_report_descriptors.REPORT_IDS[name] for name in hid_devices } hid_report_lengths_dict = { name: hid_report_descriptors.REPORT_LENGTHS[name] for name in hid_devices } hid_max_report_length = max(hid_report_lengths_dict.values()) # ASF4 expects keyboard and generic devices to have both in and out endpoints, # and will fail (possibly silently) if both are not supplied. hid_endpoint_in_descriptor = standard.EndpointDescriptor( description="HID in", bEndpointAddress=0x0 | standard.EndpointDescriptor.DIRECTION_IN, bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, bInterval=10) hid_interfaces = [