Neither macOS 14.7 "Standard" 'AppleUserHIDEventDriver' Matching Driver Nor Custom HIDDriverKit Driver 'IOUserHIDEventService::dispatchDigitizerTouchEvent' API Work for a HID-standard Digitizer Touch Pad Device

I have been working on a multi-platform multi-touch HID-standard digitizer clickpad device.

The device uses Bluetooth Low Energy (BLE) as its connectivity transport and advertises HID over GATT. To date, I have the device working successfully on Windows 11 as a multi-touch, gesture-capable click pad with no custom driver or app on Windows.

However, I have been having difficulty getting macOS to recognize and react to it as a HID-standard multi-touch click pad digitizer with either the standard Apple HID driver (AppleUserHIDEventDriver) or with a custom-coded driver extension (DEXT) modeled, based on the DTS stylus example and looking at the IOHIDFamily open source driver(s).

The trackpad works with full-gesture support on Windows 11 and the descriptors seem to be compliant with the R23 Accessory Guidelines document, §15.

With the standard, matching Apple AppleUserHIDEventDriver HID driver, when enumerating using stock-standard HID mouse descriptors, the device works fine on macOS 14.7 "Sonoma" as a relative pointer device with scroll wheel capability (two finger swipe generates a HID scroll report) and a single button.

With the standard, matching Apple AppleUserHIDEventDriver HID driver, when enumerating using stock-standard HID digitizer click/touch pad descriptors (those same descriptors used successfully on Windows 11), the device does nothing. No button, no cursor, no gestures, nothing. Looking at ioreg -filtb, all of the key/value pairs for the driver match look correct.

Because, even with the Apple open source IOHIDFamily drivers noted above, we could get little visibility into what might be going wrong, I wrote a custom DriverKit/HIDDriverKit driver extension (DEXT) (as noted above, based on the DTS HID stylus example and the open source IOHIDEventDriver.

With that custom driver, I can get a single button click from the click pad to work by dispatching button events to dispatchRelativePointerEvent; however, when parsing, processing, and dispatching HID digitizer touch finger (that is, transducer) events via IOUserHIDEventService::dispatchDigitizerTouchEvent, nothing happens.

If I log with:

% sudo log stream --info --debug --predicate '(subsystem == "com.apple.iohid")'

either using the standard AppleUserHIDEventDriver driver or our custom driver, we can see that our input events are tickling the IOHIDNXEventTranslatorSessionFilter HID event filter, so we know HID events are getting from the device into the macOS HID stack. This was further confirmed with the DTS Bluetooth PacketLogger app. Based on these events flowing in and hitting IOHIDNXEventTranslatorSessionFilter, using the standard AppleUserHIDEventDriver driver or our custom driver, clicks or click pad activity will either wake the display or system from sleep and activity will keep the display or system from going to sleep.

In short, whether with the stock driver or our custom driver, HID input reports come in over Bluetooth and get processed successfully; however, nothing happens—no pointer movement or gesture recognition.

STEPS TO REPRODUCE For the standard AppleUserHIDEventDriver:

  1. Pair the device with macOS 14.7 "Sonoma" using the Bluetooth menu.
  2. Confirm that it is paired / bonded / connected in the Bluetooth menu.
  3. Attempt to click or move one or more fingers on the touchpad surface.

Nothing happens.

For the our custom driver:

  1. Pair the device with macOS 14.7 "Sonoma" using the Bluetooth menu.
  2. Confirm that it is paired / bonded / connected in the Bluetooth menu.
  3. Attempt to click or move one or more fingers on the touchpad surface.

Clicks are correctly registered. With transducer movement, regardless of the number of fingers, nothing happens.

We can take care of closer look if descriptor provided.

Thank you for the quick follow-up. The descriptor is as follows:

05
0d 09 05 a1 01 85 14 09 22 a1 02 95 02 75 01 15
00 25 01 09 47 09 42 81 02 95 01 75 03 25 07 09
51 81 02 95 01 75 03 81 01 95 01 75 08 26 ff 00
09 30 81 02 a4 05 01 55 0e 65 11 95 01 75 10 26
d0 07 35 00 46 2e 04 09 30 81 02 26 e8 03 46 9e
02 09 31 81 02 b4 c0 09 22 a1 02 95 02 75 01 15
00 25 01 09 47 09 42 81 02 95 01 75 03 25 07 09
51 81 02 95 01 75 03 81 01 95 01 75 08 26 ff 00
09 30 81 02 a4 05 01 55 0e 65 11 95 01 75 10 26
d0 07 35 00 46 2e 04 09 30 81 02 26 e8 03 46 9e
02 09 31 81 02 b4 c0 09 22 a1 02 95 02 75 01 15
00 25 01 09 47 09 42 81 02 95 01 75 03 25 07 09
51 81 02 95 01 75 03 81 01 95 01 75 08 26 ff 00
09 30 81 02 a4 05 01 55 0e 65 11 95 01 75 10 26
d0 07 35 00 46 2e 04 09 30 81 02 26 e8 03 46 9e
02 09 31 81 02 b4 c0 75 03 25 03 09 54 81 02 75
01 25 01 05 09 09 01 81 02 95 01 75 10 27 ff ff
00 00 47 ff ff 00 00 55 0c 66 01 10 05 0d 09 56
81 02 65 00 55 00 85 15 09 55 15 00 25 03 75 03
95 01 b1 02 09 59 25 01 75 01 b1 02 75 04 b1 01
c0 05 0d 09 0e a1 01 85 16 09 52 15 00 25 03 75
03 95 01 b1 82 75 05 b1 01 85 17 09 57 09 58 25
01 75 01 95 02 b1 82 95 06 b1 01 c0

Let me know if you need further clarification.

I have also attached a copy with comments.

0x05, 0x0D,        // Usage Page (Digitizer)
0x09, 0x05,        // Usage (Touch Pad)
0xA1, 0x01,        // Collection (Application)
0x85, 0x14,        //   Report ID (20)
0x09, 0x22,        //   Usage (Finger)
0xA1, 0x02,        //   Collection (Logical)
0x95, 0x02,        //     Report Count (2)
0x75, 0x01,        //     Report Size (1)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x09, 0x47,        //     Usage (Confidence)
0x09, 0x42,        //     Usage (Tip Switch)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x03,        //     Report Size (3)
0x25, 0x07,        //     Logical Maximum (7)
0x09, 0x51,        //     Usage (Contact ID)
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)
0x95, 0x01,        //     Report Count (1)
0x75, 0x08,        //     Report Size (8)
0x26, 0xFF, 0x00,  //     Logical Maximum (255)
0x09, 0x30,        //     Usage (Tip Pressure)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xA4,              //     Push
0x05, 0x01,        //       Usage Page (Generic Desktop Ctrls)
0x55, 0x0E,        //       Unit Exponent (-2)
0x65, 0x11,        //       Unit (System: SI Linear, Length: Centimeter)
0x95, 0x01,        //       Report Count (1)
0x75, 0x10,        //       Report Size (16)
0x26, 0xD0, 0x07,  //       Logical Maximum (2000)
0x35, 0x00,        //       Physical Minimum (0)
0x46, 0x2E, 0x04,  //       Physical Maximum (1070)
0x09, 0x30,        //       Usage (X)
0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xE8, 0x03,  //       Logical Maximum (1000)
0x46, 0x9E, 0x02,  //       Physical Maximum (670)
0x09, 0x31,        //       Usage (Y)
0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xB4,              //     Pop
0xC0,              //   End Collection
0x09, 0x22,        //   Usage (Finger)
0xA1, 0x02,        //   Collection (Logical)
0x95, 0x02,        //     Report Count (2)
0x75, 0x01,        //     Report Size (1)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x09, 0x47,        //     Usage (Confidence)
0x09, 0x42,        //     Usage (Tip Switch)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x03,        //     Report Size (3)
0x25, 0x07,        //     Logical Maximum (7)
0x09, 0x51,        //     Usage (Contact ID)
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)
0x95, 0x01,        //     Report Count (1)
0x75, 0x08,        //     Report Size (8)
0x26, 0xFF, 0x00,  //     Logical Maximum (255)
0x09, 0x30,        //     Usage (Tip Pressure)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xA4,              //     Push
0x05, 0x01,        //       Usage Page (Generic Desktop Ctrls)
0x55, 0x0E,        //       Unit Exponent (-2)
0x65, 0x11,        //       Unit (System: SI Linear, Length: Centimeter)
0x95, 0x01,        //       Report Count (1)
0x75, 0x10,        //       Report Size (16)
0x26, 0xD0, 0x07,  //       Logical Maximum (2000)
0x35, 0x00,        //       Physical Minimum (0)
0x46, 0x2E, 0x04,  //       Physical Maximum (1070)
0x09, 0x30,        //       Usage (X)
0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xE8, 0x03,  //       Logical Maximum (1000)
0x46, 0x9E, 0x02,  //       Physical Maximum (670)
0x09, 0x31,        //       Usage (Y)
0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xB4,              //     Pop
0xC0,              //   End Collection
0x09, 0x22,        //   Usage (Finger)
0xA1, 0x02,        //   Collection (Logical)
0x95, 0x02,        //     Report Count (2)
0x75, 0x01,        //     Report Size (1)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x09, 0x47,        //     Usage (Confidence)
0x09, 0x42,        //     Usage (Tip Switch)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x03,        //     Report Size (3)
0x25, 0x07,        //     Logical Maximum (7)
0x09, 0x51,        //     Usage (Contact ID)
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)
0x95, 0x01,        //     Report Count (1)
0x75, 0x08,        //     Report Size (8)
0x26, 0xFF, 0x00,  //     Logical Maximum (255)
0x09, 0x30,        //     Usage (Tip Pressure)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xA4,              //     Push
0x05, 0x01,        //       Usage Page (Generic Desktop Ctrls)
0x55, 0x0E,        //       Unit Exponent (-2)
0x65, 0x11,        //       Unit (System: SI Linear, Length: Centimeter)
0x95, 0x01,        //       Report Count (1)
0x75, 0x10,        //       Report Size (16)
0x26, 0xD0, 0x07,  //       Logical Maximum (2000)
0x35, 0x00,        //       Physical Minimum (0)
0x46, 0x2E, 0x04,  //       Physical Maximum (1070)
0x09, 0x30,        //       Usage (X)
0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xE8, 0x03,  //       Logical Maximum (1000)
0x46, 0x9E, 0x02,  //       Physical Maximum (670)
0x09, 0x31,        //       Usage (Y)
0x81, 0x02,        //       Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xB4,              //     Pop
0xC0,              //   End Collection
0x75, 0x03,        //   Report Size (3)
0x25, 0x03,        //   Logical Maximum (3)
0x09, 0x54,        //   Usage (Contact Count)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01,        //   Report Size (1)
0x25, 0x01,        //   Logical Maximum (1)
0x05, 0x09,        //   Usage Page (Button)
0x09, 0x01,        //   Usage (0x01)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x10,        //   Report Size (16)
0x27, 0xFF, 0xFF, 0x00, 0x00,  //   Logical Maximum (65534)
0x47, 0xFF, 0xFF, 0x00, 0x00,  //   Physical Maximum (65534)
0x55, 0x0C,        //   Unit Exponent (-4)
0x66, 0x01, 0x10,  //   Unit (System: SI Linear, Time: Seconds)
0x05, 0x0D,        //   Usage Page (Digitizer)
0x09, 0x56,        //   Usage (Scan Time)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x65, 0x00,        //   Unit (None)
0x55, 0x00,        //   Unit Exponent (0)
0x85, 0x15,        //   Report ID (Device Capabilities)
0x09, 0x55,        //   Usage (Contact Count Maximum)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x03,        //   Logical Maximum (3)
0x75, 0x03,        //   Report Size (3)
0x95, 0x01,        //   Report Count (1)
0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x09, 0x59,        //   Usage (Pad Type)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x75, 0x04,        //   Report Size (4)
0xB1, 0x01,        //   Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection
0x05, 0x0D,        // Usage Page (Digitizer)
0x09, 0x0E,        // Usage (Configuration)
0xA1, 0x01,        // Collection (Application)
0x85, 0x16,        //   Report ID (22)
0x09, 0x52,        //   Usage (Device Mode)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x03,        //   Logical Maximum (3)
0x75, 0x03,        //   Report Size (3)
0x95, 0x01,        //   Report Count (1)
0xB1, 0x82,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x75, 0x05,        //   Report Size (5)
0xB1, 0x01,        //   Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x17,        //   Report ID (23)
0x09, 0x57,        //   Usage (Surface Switch)
0x09, 0x58,        //   Usage (Button Switch)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x02,        //   Report Count (2)
0xB1, 0x82,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
0x95, 0x06,        //   Report Count (6)
0xB1, 0x01,        //   Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              // End Collection

// 444 bytes
Neither macOS 14.7 "Standard" 'AppleUserHIDEventDriver' Matching Driver Nor Custom HIDDriverKit Driver 'IOUserHIDEventService::dispatchDigitizerTouchEvent' API Work for a HID-standard Digitizer Touch Pad Device
 
 
Q