feat(trackpad): working ptp implementation

This commit is contained in:
Florian Didron
2025-12-11 10:49:18 +07:00
parent 378fc09e35
commit a0e09fd122
7 changed files with 406 additions and 379 deletions

View File

@@ -12,11 +12,8 @@
#include "timer.h"
// Shared globals
deferred_token callback_token = 0;
uint16_t current_cpi = DEFAULT_CPI_TICK;
uint8_t has_motion = 0;
bool trackpad_init = false;
cgen6_report_t ptp_report;
uint16_t current_cpi = DEFAULT_CPI_TICK;
bool trackpad_init = false;
// I2C communication functions
i2c_status_t cirque_gen6_read_report(uint8_t *data, uint16_t cnt) {
@@ -211,56 +208,60 @@ uint8_t cirque_gen6_enable_logical_scaling(bool set) {
return cirque_gen6_write_reg(CGEN6_XY_CONFIG, xy_config);
}
// Motion detection
uint8_t cirque_gen6_has_motion(void) {
return cirque_gen6_read_reg(CGEN6_I2C_DR, true);
// Motion detection - returns true if data ready, false on no motion or I2C failure
bool cirque_gen6_has_motion(void) {
uint8_t data;
uint8_t res = cirque_gen6_read_memory(CGEN6_I2C_DR, &data, 1, true);
if (res != CGEN6_SUCCESS) {
trackpad_init = false;
return false;
}
return data != 0;
}
// Report reading - fills ptp_report global
void cirque_gen_6_read_report(void) {
// Report reading - fills provided report struct. Returns true on valid data, false on I2C failure or no data.
bool cirque_gen_6_read_report(cgen6_report_t *report) {
uint8_t packet[CGEN6_MAX_PACKET_SIZE];
if (cirque_gen6_read_report(packet, CGEN6_MAX_PACKET_SIZE) != I2C_STATUS_SUCCESS) {
return;
trackpad_init = false;
return false;
}
uint8_t report_id = packet[2];
// PTP mode report
if (report_id == CGEN6_PTP_REPORT_ID) {
ptp_report.fingers[0].tip = (packet[3] & 0x02) >> 1;
ptp_report.fingers[0].x = packet[5] << 8 | packet[4];
ptp_report.fingers[0].y = packet[7] << 8 | packet[6];
ptp_report.fingers[1].tip = (packet[8] & 0x02) >> 1;
ptp_report.fingers[1].x = packet[10] << 8 | packet[9];
ptp_report.fingers[1].y = packet[12] << 8 | packet[11];
ptp_report.scan_time = packet[14] << 8 | packet[13];
ptp_report.buttons = packet[16];
report->fingers[0].confidence = packet[3] & 0x01;
report->fingers[0].tip = (packet[3] & 0x02) >> 1;
report->fingers[0].id = (packet[3] & 0xFC) >> 2;
report->fingers[0].x = packet[5] << 8 | packet[4];
report->fingers[0].y = packet[7] << 8 | packet[6];
report->fingers[1].confidence = packet[8] & 0x01;
report->fingers[1].tip = (packet[8] & 0x02) >> 1;
report->fingers[1].id = (packet[8] & 0xFC) >> 2;
report->fingers[1].x = packet[10] << 8 | packet[9];
report->fingers[1].y = packet[12] << 8 | packet[11];
report->scan_time = packet[14] << 8 | packet[13];
report->contact_count = packet[15];
report->buttons = packet[16];
return true;
}
// Mouse/relative mode report
else if (report_id == CGEN6_MOUSE_REPORT_ID) {
ptp_report.buttons = packet[3];
ptp_report.xDelta = packet[4];
ptp_report.yDelta = packet[5];
ptp_report.scrollDelta = packet[6];
ptp_report.panDelta = packet[7];
has_motion = 1;
report->buttons = packet[3];
report->xDelta = packet[4];
report->yDelta = packet[5];
report->scrollDelta = packet[6];
report->panDelta = packet[7];
return true;
}
// Unknown or empty report - no valid data
return false;
}
// Deferred callback for polling
uint32_t cirque_gen6_read_callback(uint32_t trigger_time, void *cb_arg) {
if (!trackpad_init) {
navigator_trackpad_device_init();
return NAVIGATOR_TRACKPAD_PROBE;
}
if (cirque_gen6_has_motion()) {
has_motion = 1;
cirque_gen_6_read_report();
}
return NAVIGATOR_TRACKPAD_READ;
}
// Device initialization
// Device initialization - returns true on success, false on failure
void navigator_trackpad_device_init(void) {
i2c_init();
i2c_status_t status = i2c_ping_address(NAVIGATOR_TRACKPAD_ADDRESS, NAVIGATOR_TRACKPAD_TIMEOUT);
@@ -279,23 +280,16 @@ void navigator_trackpad_device_init(void) {
#endif
if (res != CGEN6_SUCCESS) {
trackpad_init = false;
return;
}
// Reset to the default alignment
cirque_gen6_swap_xy(false);
cirque_gen6_invert_x(false);
cirque_gen6_invert_y(false);
cirque_gen6_swap_xy(true);
cirque_gen6_invert_x(true);
cirque_gen6_invert_y(true);
cirque_gen6_enable_logical_scaling(true);
cirque_gen6_enable_logical_scaling(false); // Disable scaling for raw coordinates
trackpad_init = true;
// Only register the callback for the first time
if (!callback_token) {
callback_token = defer_exec(NAVIGATOR_TRACKPAD_READ, cirque_gen6_read_callback, NULL);
}
}
// CPI management

View File

@@ -5,11 +5,10 @@
#include <stdint.h>
#include <stdbool.h>
#include "i2c_master.h"
#include "deferred_exec.h"
// I2C configuration
#define NAVIGATOR_TRACKPAD_READ 7
#define NAVIGATOR_TRACKPAD_PROBE 1000
// Polling intervals (in ms)
#define NAVIGATOR_TRACKPAD_POLL_INTERVAL_MS 3 // Minimum interval between sensor queries
#define NAVIGATOR_TRACKPAD_PROBE_INTERVAL_MS 1000 // Interval for probing disconnected device
#ifndef NAVIGATOR_TRACKPAD_ADDRESS
# define NAVIGATOR_TRACKPAD_ADDRESS 0x58
@@ -69,13 +68,15 @@
# define TRACKPAD_PHYSICAL_HEIGHT 157 // 1.57 inches (40mm actual size)
#endif
// Logical coordinate range from Cirque Gen6 sensor in PTP mode
// Actual usable range is approximately 1-897, rounded to 900
#define TRACKPAD_LOGICAL_MAX 900
// Logical coordinate range from Cirque Gen6 sensor in PTP mode (raw, no scaling)
// Actual usable range is approximately 280-2018, rounded to 2048
#define TRACKPAD_LOGICAL_MAX 2048
// Common finger structure (used by both mouse and PTP modes)
typedef struct {
uint8_t tip;
uint8_t confidence;
uint8_t id;
uint16_t x;
uint16_t y;
} cgen6_finger_t;
@@ -84,11 +85,12 @@ typedef struct {
typedef struct {
cgen6_finger_t fingers[2];
uint8_t buttons;
uint16_t scan_time; // Sensor timestamp (100μs units) - used by PTP mode
int8_t xDelta; // Used by mouse mode
int8_t yDelta; // Used by mouse mode
int8_t scrollDelta; // Used by mouse mode
int8_t panDelta; // Used by mouse mode
uint8_t contact_count; // Number of active contacts - used by PTP mode
uint16_t scan_time; // Sensor timestamp (100μs units) - used by PTP mode
int8_t xDelta; // Used by mouse mode
int8_t yDelta; // Used by mouse mode
int8_t scrollDelta; // Used by mouse mode
int8_t panDelta; // Used by mouse mode
} cgen6_report_t;
// Low-level I2C functions
@@ -114,11 +116,10 @@ uint8_t cirque_gen6_invert_x(bool set);
uint8_t cirque_gen6_enable_logical_scaling(bool set);
// Motion detection and report reading
uint8_t cirque_gen6_has_motion(void);
void cirque_gen_6_read_report(void);
// Deferred callback
uint32_t cirque_gen6_read_callback(uint32_t trigger_time, void *cb_arg);
// Returns true if motion data is ready, false otherwise (including I2C failure)
bool cirque_gen6_has_motion(void);
// Reads report data into provided report struct. Returns true on success, false on I2C failure.
bool cirque_gen_6_read_report(cgen6_report_t *report);
// Device initialization
void navigator_trackpad_device_init(void);
@@ -129,11 +130,8 @@ void navigator_trackpad_set_cpi(uint16_t cpi);
void restore_cpi(uint8_t cpi);
// Shared globals
extern deferred_token callback_token;
extern uint16_t current_cpi;
extern uint8_t has_motion;
extern bool trackpad_init;
extern cgen6_report_t ptp_report; // Shared between modes
// Helper functions
uint8_t cirque_gen6_finger_count(cgen6_report_t *report);

View File

@@ -23,6 +23,11 @@ const pointing_device_driver_t navigator_trackpad_pointing_device_driver = {
trackpad_gesture_t gesture = {0};
extern bool set_scrolling; // Declared in navigator.c
#ifndef PRECISION_TRACKPAD_ENABLE
// Local sensor report for mouse mode (not used when PTP is enabled)
static cgen6_report_t ptp_report;
#endif
#ifdef NAVIGATOR_TRACKPAD_SCROLL_INERTIA_ENABLE
scroll_inertia_t scroll_inertia = {0};
#endif
@@ -122,7 +127,7 @@ report_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report) {
#endif // End scroll inertia block
#if defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE)
if (!has_motion || !trackpad_init) {
if (!trackpad_init) {
return mouse_report;
}
@@ -132,7 +137,7 @@ report_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report) {
mouse_report.h = ptp_report.panDelta;
mouse_report.buttons = ptp_report.buttons;
#elif defined(NAVIGATOR_TRACKPAD_PTP_MODE)
if (!has_motion || !trackpad_init) {
if (!trackpad_init) {
return mouse_report;
}
// Create local snapshot to avoid race condition with callback updating ptp_report
@@ -368,7 +373,6 @@ report_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report) {
}
#endif
has_motion = 0;
return mouse_report;
#endif // PRECISION_TRACKPAD_ENABLE
}

View File

@@ -9,139 +9,116 @@
#include "precision_trackpad_drivers.h"
#include "quantum.h"
#include "report.h"
#include "timer.h"
#ifdef PRECISION_TRACKPAD_ENABLE
// External declaration for PTP report sending
extern void send_trackpad(report_trackpad_t *report);
// External declaration for report sending
extern void send_trackpad(report_digitizer_t *report);
#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)
// PTP task function - converts trackpad touches to PTP reports
// Build a finger's 6 bytes into the report buffer
// Format: [conf:1 + tip:1 + pad:6] [contact_id:3 + pad:5] [X_lo] [X_hi] [Y_lo] [Y_hi]
// For PTP, we always send contact_id and coordinates. Only tip changes.
static void build_finger_bytes(uint8_t *buf, uint8_t contact_id, uint16_t x, uint16_t y, bool tip) {
buf[0] = tip ? 0x03 : 0x01; // confidence=1 always, tip varies
buf[1] = contact_id & 0x07; // contact_id in bits 0-2
buf[2] = x & 0xFF; // X low byte
buf[3] = (x >> 8) & 0xFF; // X high byte
buf[4] = y & 0xFF; // Y low byte
buf[5] = (y >> 8) & 0xFF; // Y high byte
}
// PTP task function - synchronous polling with timer-based throttling
static bool navigator_trackpad_ptp_task(void) {
if (!has_motion || !trackpad_init) {
static uint32_t last_poll_time = 0;
static uint32_t last_probe_time = 0;
static uint8_t prev_buttons = 0;
// Track previous finger state to detect lift-offs
static bool prev_finger0_tip = false;
static bool prev_finger1_tip = false;
uint32_t now = timer_read32();
// Throttle polling to NAVIGATOR_TRACKPAD_POLL_INTERVAL_MS
if (timer_elapsed32(last_poll_time) < NAVIGATOR_TRACKPAD_POLL_INTERVAL_MS) {
return false;
}
last_poll_time = now;
// Handle disconnected/uninitialized state with slower probe interval
if (!trackpad_init) {
if (timer_elapsed32(last_probe_time) < NAVIGATOR_TRACKPAD_PROBE_INTERVAL_MS) {
return false;
}
last_probe_time = now;
navigator_trackpad_device_init();
return false;
}
// Create local snapshot to avoid race condition with callback
cgen6_report_t local_report = ptp_report;
uint8_t raw_fingers = cirque_gen6_finger_count(&local_report);
// Initialize PTP report
report_trackpad_t ptp = {0};
ptp.report_id = REPORT_ID_TRACKPAD;
// Sensitivity scaling: Track previous RAW sensor position and accumulated scaled position
// Apply sensitivity only to the delta from raw sensor, then accumulate
static uint16_t prev_raw_x[2] = {0, 0};
static uint16_t prev_raw_y[2] = {0, 0};
static uint16_t accum_x[2] = {0, 0};
static uint16_t accum_y[2] = {0, 0};
static bool was_touching[2] = {false, false};
if (local_report.fingers[0].tip) {
uint16_t raw_x = local_report.fingers[0].x;
uint16_t raw_y = local_report.fingers[0].y;
if (was_touching[0]) {
// Calculate delta from RAW sensor data
int16_t delta_x = (int16_t)(raw_x - prev_raw_x[0]);
int16_t delta_y = (int16_t)(raw_y - prev_raw_y[0]);
// Apply sensitivity to the delta
delta_x = (int16_t)((float)delta_x * NAVIGATOR_TRACKPAD_SENSITIVITY);
delta_y = (int16_t)((float)delta_y * NAVIGATOR_TRACKPAD_SENSITIVITY);
// Add scaled delta to accumulated position
int32_t new_x = (int32_t)accum_x[0] + delta_x;
int32_t new_y = (int32_t)accum_y[0] + delta_y;
// Clamp to valid range
if (new_x < 0) new_x = 0;
if (new_x > 4095) new_x = 4095;
if (new_y < 0) new_y = 0;
if (new_y > 4095) new_y = 4095;
accum_x[0] = (uint16_t)new_x;
accum_y[0] = (uint16_t)new_y;
} else {
// First touch - initialize accumulated position to raw position
accum_x[0] = raw_x;
accum_y[0] = raw_y;
}
ptp.contacts[0].confidence = 1;
ptp.contacts[0].tip = 1;
ptp.contacts[0].contact_id = 0;
ptp.contacts[0].x = accum_x[0];
ptp.contacts[0].y = accum_y[0];
prev_raw_x[0] = raw_x;
prev_raw_y[0] = raw_y;
was_touching[0] = true;
} else {
was_touching[0] = false;
// Read the report data into local struct
cgen6_report_t sensor_report = {0};
if (!cirque_gen_6_read_report(&sensor_report)) {
return false;
}
if (local_report.fingers[1].tip) {
uint16_t raw_x = local_report.fingers[1].x;
uint16_t raw_y = local_report.fingers[1].y;
// Current finger states
bool finger0_tip = sensor_report.fingers[0].tip;
bool finger1_tip = sensor_report.fingers[1].tip;
if (was_touching[1]) {
// Calculate delta from RAW sensor data
int16_t delta_x = (int16_t)(raw_x - prev_raw_x[1]);
int16_t delta_y = (int16_t)(raw_y - prev_raw_y[1]);
// Determine if each finger should be included in contact_count
// Include finger if: currently touching OR was touching last frame (lift-off)
bool finger0_contact = finger0_tip || prev_finger0_tip;
bool finger1_contact = finger1_tip || prev_finger1_tip;
// Apply sensitivity to the delta
delta_x = (int16_t)((float)delta_x * NAVIGATOR_TRACKPAD_SENSITIVITY);
delta_y = (int16_t)((float)delta_y * NAVIGATOR_TRACKPAD_SENSITIVITY);
uint8_t buttons = sensor_report.buttons & 0x01;
bool button_changed = (buttons != prev_buttons);
// Add scaled delta to accumulated position
int32_t new_x = (int32_t)accum_x[1] + delta_x;
int32_t new_y = (int32_t)accum_y[1] + delta_y;
// Contact count includes fingers that are touching OR lifting off this frame
uint8_t contact_count = (finger0_contact ? 1 : 0) + (finger1_contact ? 1 : 0);
// Clamp to valid range
if (new_x < 0) new_x = 0;
if (new_x > 4095) new_x = 4095;
if (new_y < 0) new_y = 0;
if (new_y > 4095) new_y = 4095;
// Build report from sensor data using explicit byte manipulation
// Report format (16 bytes): [report_id] [finger0: 6 bytes] [finger1: 6 bytes] [scan_time: 2 bytes] [count+buttons: 1 byte]
uint8_t report[16] = {0};
accum_x[1] = (uint16_t)new_x;
accum_y[1] = (uint16_t)new_y;
} else {
// First touch - initialize accumulated position to raw position
accum_x[1] = raw_x;
accum_y[1] = raw_y;
}
// Byte 0: Report ID
report[0] = 0x01;
ptp.contacts[1].confidence = 1;
ptp.contacts[1].tip = 1;
ptp.contacts[1].contact_id = 1;
ptp.contacts[1].x = accum_x[1];
ptp.contacts[1].y = accum_y[1];
prev_raw_x[1] = raw_x;
prev_raw_y[1] = raw_y;
was_touching[1] = true;
} else {
was_touching[1] = false;
// Bytes 1-6: Finger 0 (include if touching or lifting off)
if (finger0_contact) {
build_finger_bytes(&report[1], 0,
sensor_report.fingers[0].x,
sensor_report.fingers[0].y,
finger0_tip);
}
// Set scan time, contact count, and buttons (after contacts per Microsoft spec)
// Use the sensor's scan_time for accurate gesture velocity calculation
ptp.scan_time = local_report.scan_time;
ptp.contact_count = raw_fingers;
ptp.button1 = (local_report.buttons & 0x01) ? 1 : 0;
ptp.button2 = (local_report.buttons & 0x02) ? 1 : 0;
ptp.button3 = (local_report.buttons & 0x04) ? 1 : 0;
if (raw_fingers > 0) {
send_trackpad(&ptp);
// Bytes 7-12: Finger 1 (include if touching or lifting off)
if (finger1_contact) {
build_finger_bytes(&report[7], 1,
sensor_report.fingers[1].x,
sensor_report.fingers[1].y,
finger1_tip);
}
has_motion = 0;
return true;
// Bytes 13-14: Scan time (little-endian)
report[13] = sensor_report.scan_time & 0xFF;
report[14] = (sensor_report.scan_time >> 8) & 0xFF;
// Byte 15: Contact count (bits 0-3) + buttons (bits 4-6)
report[15] = (contact_count & 0x0F) | ((buttons & 0x01) << 4);
// Send report if any contacts (including lift-offs) or button changed
if (contact_count > 0 || button_changed) {
send_trackpad((report_digitizer_t *)report);
}
// Update previous state
prev_finger0_tip = finger0_tip;
prev_finger1_tip = finger1_tip;
prev_buttons = buttons;
return contact_count > 0 || button_changed;
}
#else
// Stub for when PTP mode is not enabled
@@ -152,7 +129,7 @@ static bool navigator_trackpad_ptp_task(void) {
// Internal init function
static void navigator_trackpad_ptp_init(void) {
navigator_trackpad_device_init(); // Common init
navigator_trackpad_device_init();
}
// Driver registration

View File

@@ -85,37 +85,74 @@ bool usb_get_report_cb(USBDriver *driver) {
static usb_fs_report_t report;
#ifdef PRECISION_TRACKPAD_ENABLE
// Handle PTP feature report requests
// Handle digitizer/touchpad feature report requests
// Touchpad interface uses its own report ID space (1-5)
if (interface == TRACKPAD_INTERFACE) {
static uint8_t feature_report[2];
switch (report_id) {
case REPORT_ID_TRACKPAD_MAX_COUNT:
// Report max contact count and pad type
feature_report[0] = REPORT_ID_TRACKPAD_MAX_COUNT;
feature_report[1] = (2 << 0) | (0 << 4); // 2 contacts, clickpad (depressible)
case 0x02: // Contact Count Maximum + Pad Type
// Contact Count Maximum (4 bits) + Pad Type (3 bits) + Surface Switch (1 bit)
// Pad Type: 0=Clickpad (depressible), 1=Pressure-pad
// Navigator: 2 contacts max, clickpad (type 0)
feature_report[0] = 0x02;
feature_report[1] = (DIGITIZER_CONTACT_COUNT << 0) | (0 << 4) | (0 << 7);
usbSetupTransfer(driver, feature_report, 2, NULL);
return true;
case REPORT_ID_TRACKPAD_CONFIG:
case 0x04: // Configuration - Input Mode
// Report input mode (3 = multi-touch)
feature_report[0] = REPORT_ID_TRACKPAD_CONFIG;
feature_report[0] = 0x04;
feature_report[1] = 3; // Multi-touch mode
usbSetupTransfer(driver, feature_report, 2, NULL);
return true;
case REPORT_ID_TRACKPAD_FEATURE:
case 0x05: // Function Switch
// Surface and button switches (both enabled)
feature_report[0] = REPORT_ID_TRACKPAD_FEATURE;
feature_report[0] = 0x05;
feature_report[1] = 0; // Both switches enabled (0 = enabled)
usbSetupTransfer(driver, feature_report, 2, NULL);
return true;
case REPORT_ID_TRACKPAD_PTPHQA:
// Certification blob - send empty 257-byte report
case 0x03: // Certification blob
// Certification blob - Microsoft's default blob for non-certified devices
{
static uint8_t cert_blob[257] = {REPORT_ID_TRACKPAD_PTPHQA};
usbSetupTransfer(driver, cert_blob, sizeof(cert_blob), NULL);
static const uint8_t cert_blob[257] = {
0x03,
0xfc, 0x28, 0xfe, 0x84, 0x40, 0xcb, 0x9a, 0x87,
0x0d, 0xbe, 0x57, 0x3c, 0xb6, 0x70, 0x09, 0x88,
0x07, 0x97, 0x2d, 0x2b, 0xe3, 0x38, 0x34, 0xb6,
0x6c, 0xed, 0xb0, 0xf7, 0xe5, 0x9c, 0xf6, 0xc2,
0x2e, 0x84, 0x1b, 0xe8, 0xb4, 0x51, 0x78, 0x43,
0x1f, 0x28, 0x4b, 0x7c, 0x2d, 0x53, 0xaf, 0xfc,
0x47, 0x70, 0x1b, 0x59, 0x6f, 0x74, 0x43, 0xc4,
0xf3, 0x47, 0x18, 0x53, 0x1a, 0xa2, 0xa1, 0x71,
0xc7, 0x95, 0x0e, 0x31, 0x55, 0x21, 0xd3, 0xb5,
0x1e, 0xe9, 0x0c, 0xba, 0xec, 0xb8, 0x89, 0x19,
0x3e, 0xb3, 0xaf, 0x75, 0x81, 0x9d, 0x53, 0xb9,
0x41, 0x57, 0xf4, 0x6d, 0x39, 0x25, 0x29, 0x7c,
0x87, 0xd9, 0xb4, 0x98, 0x45, 0x7d, 0xa7, 0x26,
0x9c, 0x65, 0x3b, 0x85, 0x68, 0x89, 0xd7, 0x3b,
0xbd, 0xff, 0x14, 0x67, 0xf2, 0x2b, 0xf0, 0x2a,
0x41, 0x54, 0xf0, 0xfd, 0x2c, 0x66, 0x7c, 0xf8,
0xc0, 0x8f, 0x33, 0x13, 0x03, 0xf1, 0xd3, 0xc1,
0x0b, 0x89, 0xd9, 0x1b, 0x62, 0xcd, 0x51, 0xb7,
0x80, 0xb8, 0xaf, 0x3a, 0x10, 0xc1, 0x8a, 0x5b,
0xe8, 0x8a, 0x56, 0xf0, 0x8c, 0xaa, 0xfa, 0x35,
0xe9, 0x42, 0xc4, 0xd8, 0x55, 0xc3, 0x38, 0xcc,
0x2b, 0x53, 0x5c, 0x69, 0x52, 0xd5, 0xc8, 0x73,
0x02, 0x38, 0x7c, 0x73, 0xb6, 0x41, 0xe7, 0xff,
0x05, 0xd8, 0x2b, 0x79, 0x9a, 0xe2, 0x34, 0x60,
0x8f, 0xa3, 0x32, 0x1f, 0x09, 0x78, 0x62, 0xbc,
0x80, 0xe3, 0x0f, 0xbd, 0x65, 0x20, 0x08, 0x13,
0xc1, 0xe2, 0xee, 0x53, 0x2d, 0x86, 0x7e, 0xa7,
0x5a, 0xc5, 0xd3, 0x7d, 0x98, 0xbe, 0x31, 0x48,
0x1f, 0xfb, 0xda, 0xaf, 0xa2, 0xa8, 0x6a, 0x89,
0xd6, 0xbf, 0xf2, 0xd3, 0x32, 0x2a, 0x9a, 0xe4,
0xcf, 0x17, 0xb7, 0xb8, 0xf4, 0xe1, 0x33, 0x08,
0x24, 0x8b, 0xc4, 0x43, 0xa5, 0xe5, 0x24, 0xc2
};
usbSetupTransfer(driver, (uint8_t *)cert_blob, sizeof(cert_blob), NULL);
}
return true;
}

View File

@@ -39,17 +39,12 @@ enum hid_report_ids {
REPORT_ID_NKRO,
REPORT_ID_JOYSTICK,
REPORT_ID_DIGITIZER,
// PTP trackpad uses separate report IDs (per Microsoft PTP specification)
// These don't auto-increment from above, they're on a separate interface
REPORT_ID_TRACKPAD = 0x01, // Input report (multi-touch data)
REPORT_ID_TRACKPAD_MAX_COUNT = 0x02, // Feature: Contact Count Maximum
REPORT_ID_TRACKPAD_CONFIG = 0x03, // Feature: Input Mode configuration
REPORT_ID_TRACKPAD_FEATURE = 0x04, // Feature: Surface/Button switches
REPORT_ID_TRACKPAD_PTPHQA = 0x05, // Feature: Certification blob
// REPORT_ID_COUNT must be the highest ID from the auto-incrementing sequence
REPORT_ID_COUNT = REPORT_ID_DIGITIZER // Highest report ID value
REPORT_ID_DIGITIZER_STYLUS,
REPORT_ID_DIGITIZER_CONFIGURATION,
REPORT_ID_DIGITIZER_GET_FEATURE,
REPORT_ID_DIGITIZER_FUNCTION_SWITCH,
REPORT_ID_DIGITIZER_CERTIFICATE,
REPORT_ID_COUNT = REPORT_ID_DIGITIZER_CERTIFICATE
};
#define IS_VALID_REPORT_ID(id) ((id) >= REPORT_ID_ALL && (id) <= REPORT_ID_COUNT)
@@ -239,71 +234,53 @@ typedef struct {
} PACKED report_mouse_t;
typedef struct {
#ifdef DIGITIZER_SHARED_EP
uint8_t report_id;
#endif
bool in_range : 1;
bool tip : 1;
bool barrel : 1;
uint8_t report_id;
uint8_t in_range : 1;
uint8_t tip : 1;
uint8_t barrel : 1;
uint8_t reserved : 5;
uint16_t x;
uint16_t y;
} PACKED report_digitizer_stylus_t;
// Digitizer/Touchpad report structures
// Per-contact data structure - 6 bytes per contact
// Byte layout: [conf:1 + tip:1 + pad:6] [id:3 + pad:5] [X low] [X high] [Y low] [Y high]
typedef struct {
uint8_t confidence : 1;
uint8_t tip : 1;
uint8_t reserved : 6;
uint8_t contact_id : 3;
uint8_t reserved2 : 5;
uint16_t x;
uint16_t y;
} PACKED digitizer_finger_report_t;
// Main digitizer/touchpad input report
// Field order: [ReportID][Contacts...][ScanTime][Count:4+Buttons:3+Pad:1]
#ifndef DIGITIZER_CONTACT_COUNT
#define DIGITIZER_CONTACT_COUNT 2
#endif
typedef struct {
uint8_t report_id;
#if DIGITIZER_CONTACT_COUNT > 0
digitizer_finger_report_t fingers[DIGITIZER_CONTACT_COUNT];
#endif
uint16_t scan_time;
uint8_t contact_count : 4;
uint8_t button1 : 1;
uint8_t button2 : 1;
uint8_t button3 : 1;
uint8_t reserved2 : 1;
} PACKED report_digitizer_t;
#ifdef PRECISION_TRACKPAD_ENABLE
// Per-contact data structure for PTP
typedef struct {
bool confidence : 1; // Confidence bit (1 = intentional touch, 0 = palm/accidental)
bool tip : 1; // Tip switch (contact state)
uint8_t contact_id : 2; // Contact identifier (0-based, supports up to 4 contacts)
uint8_t reserved : 4; // Reserved/padding bits
uint16_t x; // Absolute X position
uint16_t y; // Absolute Y position
} PACKED report_trackpad_contact_t;
// Verify structure sizes
_Static_assert(sizeof(digitizer_finger_report_t) == 6, "digitizer_finger_report_t must be 6 bytes");
_Static_assert(sizeof(report_digitizer_t) == 16, "report_digitizer_t must be 16 bytes");
// Main trackpad input report
// Field order per Microsoft PTP spec: [ReportID][Contacts][ScanTime][Count][Buttons]
typedef struct {
uint8_t report_id; // Report ID for trackpad
report_trackpad_contact_t contacts[2]; // Up to 2 simultaneous contacts
uint16_t scan_time; // Scan time in 100μs units
uint8_t contact_count; // Number of active contacts
uint8_t button1 : 1; // Physical button 1
uint8_t button2 : 1; // Physical button 2
uint8_t button3 : 1; // Physical button 3
uint8_t button_pad : 5; // Padding for buttons
} PACKED report_trackpad_t;
// Compile-time assertion to verify structure size
_Static_assert(sizeof(report_trackpad_t) == 15, "report_trackpad_t must be exactly 15 bytes");
// Feature report for device capabilities
typedef struct {
uint8_t report_id; // REPORT_ID_TRACKPAD_MAX_COUNT
uint8_t max_contact_count : 4; // Maximum number of contacts (2)
uint8_t pad_type : 4; // Pad type (0 = depressible click-pad, 1 = pressure pad)
} PACKED report_trackpad_max_count_t;
// Feature report for configuration
typedef struct {
uint8_t report_id; // REPORT_ID_TRACKPAD_CONFIG
uint8_t input_mode; // Input mode (0 = mouse, 3 = multi-touch)
} PACKED report_trackpad_config_t;
// Feature report for function switches
typedef struct {
uint8_t report_id; // REPORT_ID_TRACKPAD_FEATURE
uint8_t surface_switch : 1; // Surface switch (0 = enabled, 1 = disabled)
uint8_t button_switch : 1; // Button switch (0 = enabled, 1 = disabled)
uint8_t reserved : 6;
} PACKED report_trackpad_feature_t;
// Vendor-specific certification blob (256 bytes)
typedef struct {
uint8_t report_id; // REPORT_ID_TRACKPAD_PTPHQA
uint8_t blob[256]; // Certification status blob
} PACKED report_trackpad_ptphqa_t;
#endif
// Legacy alias for compatibility
typedef report_digitizer_t report_trackpad_t;
#if JOYSTICK_AXIS_RESOLUTION > 8
typedef int16_t joystick_axis_t;

View File

@@ -388,166 +388,206 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
// Include trackpad dimensions
#include "drivers/sensors/navigator_trackpad_common.h"
// Windows Precision Touchpad (PTP) HID Descriptor
// Follows Microsoft PTP specification exactly
// Digitizer/Touchpad HID Descriptor
const USB_Descriptor_HIDReport_Datatype_t PROGMEM PrecisionTrackpadReport[] = {
// Touch Pad Input TLC
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x05), // Touch Pad
HID_RI_COLLECTION(8, 0x01), // Application
HID_RI_REPORT_ID(8, REPORT_ID_TRACKPAD),
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x05), // Touchpad
HID_RI_COLLECTION(8, 0x01), // Application
// Use report ID 1 for touchpad input (separate interface has own ID space)
HID_RI_REPORT_ID(8, 0x01),
// First finger (contact 0)
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x02), // Logical
// Contact 1
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x00), // Physical
HID_RI_PUSH(0),
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_USAGE(8, 0x47), // Confidence
HID_RI_USAGE(8, 0x42), // Tip Switch
// Tip Switch, Confidence (2 bits)
HID_RI_USAGE(8, 0x47), // Confidence
HID_RI_USAGE(8, 0x42), // Tip Switch
HID_RI_REPORT_COUNT(8, 0x02),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_SIZE(8, 0x02),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_LOGICAL_MAXIMUM(8, 0x02),
HID_RI_USAGE(8, 0x51), // Contact Identifier
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x04),
HID_RI_INPUT(8, HID_IOF_CONSTANT), // Padding
// X/Y position
HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(16, TRACKPAD_LOGICAL_MAX), // 4095 (12-bit resolution)
HID_RI_REPORT_SIZE(8, 0x10),
HID_RI_UNIT_EXPONENT(8, 0x0E), // -2 (hundredths)
HID_RI_UNIT(8, 0x13), // Inch, English Linear
HID_RI_PHYSICAL_MINIMUM(8, 0x00),
HID_RI_PHYSICAL_MAXIMUM(16, TRACKPAD_PHYSICAL_WIDTH), // Physical width in 0.01 inch
// Padding (6 bits)
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x06),
HID_RI_INPUT(8, HID_IOF_CONSTANT),
// Contact identifier (3 bits)
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_USAGE(8, 0x30), // X
HID_RI_REPORT_SIZE(8, 0x03),
HID_RI_LOGICAL_MAXIMUM(8, 0x05),
HID_RI_USAGE(8, 0x51), // Contact identifier
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_PHYSICAL_MAXIMUM(16, TRACKPAD_PHYSICAL_HEIGHT), // Physical height in 0.01 inch
HID_RI_USAGE(8, 0x31), // Y
// Padding (5 bits)
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x05),
HID_RI_INPUT(8, HID_IOF_CONSTANT),
// X/Y Position (4 bytes)
HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop
HID_RI_LOGICAL_MINIMUM(8, 0x0),
HID_RI_LOGICAL_MAXIMUM(16, TRACKPAD_LOGICAL_MAX),
HID_RI_REPORT_SIZE(8, 16),
HID_RI_UNIT_EXPONENT(8, 0x0E), // -2
HID_RI_UNIT(8, 0x11), // CM, English Linear
HID_RI_USAGE(8, 0x30), // X
HID_RI_PHYSICAL_MINIMUM(8, 0x0),
HID_RI_PHYSICAL_MAXIMUM(16, (TRACKPAD_PHYSICAL_WIDTH)),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_LOGICAL_MAXIMUM(16, TRACKPAD_LOGICAL_MAX),
HID_RI_PHYSICAL_MAXIMUM(16, (TRACKPAD_PHYSICAL_HEIGHT)),
HID_RI_USAGE(8, 0x31), // Y
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_POP(0),
HID_RI_END_COLLECTION(0),
// Second finger (identical structure)
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers (switch back!)
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x02), // Logical
// Contact 2
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x00), // Physical
HID_RI_PUSH(0),
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_USAGE(8, 0x47), // Confidence
HID_RI_USAGE(8, 0x42), // Tip Switch
// Tip Switch, Confidence (2 bits)
HID_RI_USAGE(8, 0x47), // Confidence
HID_RI_USAGE(8, 0x42), // Tip Switch
HID_RI_REPORT_COUNT(8, 0x02),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_SIZE(8, 0x02),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_LOGICAL_MAXIMUM(8, 0x02),
HID_RI_USAGE(8, 0x51), // Contact Identifier
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x04),
HID_RI_INPUT(8, HID_IOF_CONSTANT), // Padding
HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(16, TRACKPAD_LOGICAL_MAX), // 4095 (12-bit resolution)
HID_RI_REPORT_SIZE(8, 0x10),
HID_RI_UNIT_EXPONENT(8, 0x0E),
HID_RI_UNIT(8, 0x13),
HID_RI_PHYSICAL_MINIMUM(8, 0x00),
HID_RI_PHYSICAL_MAXIMUM(16, TRACKPAD_PHYSICAL_WIDTH), // Physical width in 0.01 inch
// Padding (6 bits)
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x06),
HID_RI_INPUT(8, HID_IOF_CONSTANT),
// Contact identifier (3 bits)
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_USAGE(8, 0x30), // X
HID_RI_REPORT_SIZE(8, 0x03),
HID_RI_LOGICAL_MAXIMUM(8, 0x05),
HID_RI_USAGE(8, 0x51), // Contact identifier
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_PHYSICAL_MAXIMUM(16, TRACKPAD_PHYSICAL_HEIGHT), // Physical height in 0.01 inch
HID_RI_USAGE(8, 0x31), // Y
// Padding (5 bits)
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x05),
HID_RI_INPUT(8, HID_IOF_CONSTANT),
// X/Y Position (4 bytes)
HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop
HID_RI_LOGICAL_MINIMUM(8, 0x0),
HID_RI_LOGICAL_MAXIMUM(16, TRACKPAD_LOGICAL_MAX),
HID_RI_REPORT_SIZE(8, 16),
HID_RI_UNIT_EXPONENT(8, 0x0E), // -2
HID_RI_UNIT(8, 0x11), // CM, English Linear
HID_RI_USAGE(8, 0x30), // X
HID_RI_PHYSICAL_MINIMUM(8, 0x0),
HID_RI_PHYSICAL_MAXIMUM(16, (TRACKPAD_PHYSICAL_WIDTH)),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_LOGICAL_MAXIMUM(16, TRACKPAD_LOGICAL_MAX),
HID_RI_PHYSICAL_MAXIMUM(16, (TRACKPAD_PHYSICAL_HEIGHT)),
HID_RI_USAGE(8, 0x31), // Y
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_POP(0),
HID_RI_END_COLLECTION(0),
// Scan Time (comes AFTER contacts per Microsoft spec)
HID_RI_UNIT_EXPONENT(8, 0x0C), // -4
HID_RI_UNIT(16, 0x1001), // Seconds
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(32, 0x0000FFFF),
HID_RI_PHYSICAL_MINIMUM(8, 0x00),
HID_RI_PHYSICAL_MAXIMUM(32, 0x0000FFFF),
HID_RI_REPORT_SIZE(8, 0x10),
// Scan Time and Contact Count
HID_RI_PUSH(0),
HID_RI_UNIT_EXPONENT(8, 0x0C), // -4
HID_RI_UNIT(16, 0x1001), // Seconds, SI Linear
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x56), // Scan Time
HID_RI_PHYSICAL_MINIMUM(0),
HID_RI_LOGICAL_MINIMUM(0),
HID_RI_PHYSICAL_MAXIMUM(32, 65535),
HID_RI_LOGICAL_MAXIMUM(32, 65535),
HID_RI_REPORT_SIZE(8, 16),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x56), // Scan Time
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
// Contact Count (8 bits, not 4!)
HID_RI_USAGE(8, 0x54), // Contact Count
HID_RI_LOGICAL_MAXIMUM(8, 0x7F),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_USAGE(8, 0x54), // Contact count
HID_RI_LOGICAL_MAXIMUM(8, 5),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x04),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
// Buttons
HID_RI_USAGE_PAGE(8, 0x09), // Button
HID_RI_USAGE(8, 0x01), // Button 1
HID_RI_USAGE(8, 0x02), // Button 2
HID_RI_USAGE(8, 0x03), // Button 3
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x03),
HID_RI_USAGE_PAGE(8, 0x09), // Buttons
HID_RI_USAGE(8, 0x01), // Button 1
HID_RI_USAGE(8, 0x02), // Button 2
HID_RI_USAGE(8, 0x03), // Button 3
HID_RI_LOGICAL_MAXIMUM(8, 1),
HID_RI_REPORT_SIZE(8, 1),
HID_RI_REPORT_COUNT(8, 3),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_COUNT(8, 0x05), // Padding
// Padding (1 bit)
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_INPUT(8, HID_IOF_CONSTANT),
// Feature reports
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_REPORT_ID(8, REPORT_ID_TRACKPAD_MAX_COUNT),
HID_RI_USAGE(8, 0x55), // Contact Count Maximum
HID_RI_USAGE(8, 0x59), // Pad Type
// Feature: Contact Count Maximum + Pad Type
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_REPORT_ID(8, 0x02), // Feature report ID 2
HID_RI_USAGE(8, 0x55), // Contact Count Maximum
HID_RI_REPORT_SIZE(8, 0x04),
HID_RI_REPORT_COUNT(8, 0x02),
HID_RI_LOGICAL_MAXIMUM(8, 0x0F),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_LOGICAL_MAXIMUM(8, 15),
HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_USAGE(8, 0x59), // Pad type
HID_RI_REPORT_SIZE(8, 0x03),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_LOGICAL_MAXIMUM(8, 7),
HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_USAGE(8, 0x57), // Surface switch
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_LOGICAL_MAXIMUM(8, 1),
HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
// Certification blob
HID_RI_USAGE_PAGE(16, 0xFF00), // Vendor Defined
HID_RI_REPORT_ID(8, REPORT_ID_TRACKPAD_PTPHQA),
HID_RI_USAGE(8, 0xC5), // Vendor Usage 0xC5
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),
// Feature: Certification blob (256 bytes)
HID_RI_USAGE_PAGE(16, 0xFF), // Vendor
HID_RI_REPORT_ID(8, 0x03), // Feature report ID 3
HID_RI_USAGE(8, 0xC5), // Vendor usage
HID_RI_LOGICAL_MAXIMUM(16, 255),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_REPORT_COUNT(16, 0x0100), // 256 bytes
HID_RI_REPORT_COUNT(16, 256),
HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_POP(0),
HID_RI_END_COLLECTION(0),
// Configuration TLC
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x0E), // Configuration
HID_RI_COLLECTION(8, 0x01), // Application
HID_RI_REPORT_ID(8, REPORT_ID_TRACKPAD_CONFIG),
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x02), // Logical
HID_RI_USAGE(8, 0x52), // Input Mode
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x0A),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers
HID_RI_USAGE(8, 0x0E), // Configuration
HID_RI_COLLECTION(8, 0x01), // Application
HID_RI_PUSH(0),
HID_RI_REPORT_ID(8, 0x04), // Feature report ID 4
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x02), // Logical
HID_RI_USAGE(8, 0x52), // Input mode
HID_RI_LOGICAL_MINIMUM(8, 0),
HID_RI_LOGICAL_MAXIMUM(8, 10),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_END_COLLECTION(0),
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x00), // Physical
HID_RI_REPORT_ID(8, REPORT_ID_TRACKPAD_FEATURE),
HID_RI_USAGE(8, 0x57), // Surface Switch
HID_RI_USAGE(8, 0x58), // Button Switch
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_USAGE(8, 0x22), // Finger
HID_RI_COLLECTION(8, 0x00), // Physical
HID_RI_REPORT_ID(8, 0x05), // Feature report ID 5
HID_RI_USAGE(8, 0x57), // Surface switch
HID_RI_USAGE(8, 0x58), // Button switch
HID_RI_LOGICAL_MAXIMUM(8, 1),
HID_RI_REPORT_COUNT(8, 0x02),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_COUNT(8, 0x06), // Padding
HID_RI_FEATURE(8, HID_IOF_CONSTANT),
HID_RI_REPORT_COUNT(8, 0x06),
HID_RI_FEATURE(8, HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_POP(0),
HID_RI_END_COLLECTION(0),
HID_RI_END_COLLECTION(0),
};