From a0e09fd122946dbedd92ef10957aea30e476ff88 Mon Sep 17 00:00:00 2001 From: Florian Didron Date: Thu, 11 Dec 2025 10:49:18 +0700 Subject: [PATCH] feat(trackpad): working ptp implementation --- drivers/sensors/navigator_trackpad_common.c | 90 +++--- drivers/sensors/navigator_trackpad_common.h | 38 ++- drivers/sensors/navigator_trackpad_mouse.c | 10 +- drivers/sensors/navigator_trackpad_ptp.c | 201 ++++++------- .../protocol/chibios/usb_report_handling.c | 63 ++++- tmk_core/protocol/report.h | 117 ++++---- tmk_core/protocol/usb_descriptor.c | 266 ++++++++++-------- 7 files changed, 406 insertions(+), 379 deletions(-) diff --git a/drivers/sensors/navigator_trackpad_common.c b/drivers/sensors/navigator_trackpad_common.c index 3a063828c6..2c3fafbe88 100644 --- a/drivers/sensors/navigator_trackpad_common.c +++ b/drivers/sensors/navigator_trackpad_common.c @@ -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 diff --git a/drivers/sensors/navigator_trackpad_common.h b/drivers/sensors/navigator_trackpad_common.h index bc63f1f7ae..ba6a803ef9 100644 --- a/drivers/sensors/navigator_trackpad_common.h +++ b/drivers/sensors/navigator_trackpad_common.h @@ -5,11 +5,10 @@ #include #include #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); diff --git a/drivers/sensors/navigator_trackpad_mouse.c b/drivers/sensors/navigator_trackpad_mouse.c index 0b59ffebe0..f6f1bf6b84 100644 --- a/drivers/sensors/navigator_trackpad_mouse.c +++ b/drivers/sensors/navigator_trackpad_mouse.c @@ -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 } diff --git a/drivers/sensors/navigator_trackpad_ptp.c b/drivers/sensors/navigator_trackpad_ptp.c index c15ec7c52b..fe8cb332d8 100644 --- a/drivers/sensors/navigator_trackpad_ptp.c +++ b/drivers/sensors/navigator_trackpad_ptp.c @@ -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 diff --git a/tmk_core/protocol/chibios/usb_report_handling.c b/tmk_core/protocol/chibios/usb_report_handling.c index 61f6775551..f33feec4d2 100644 --- a/tmk_core/protocol/chibios/usb_report_handling.c +++ b/tmk_core/protocol/chibios/usb_report_handling.c @@ -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; } diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index 8df6ad9137..d5e4079d1e 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -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; diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index a78cf0afbb..a0f3d9f72a 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -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), };