mirror of
https://github.com/zsa/qmk_firmware.git
synced 2026-02-26 05:58:31 +00:00
feat(trackpad): working ptp implementation
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user