Skip to content

Instantly share code, notes, and snippets.

@Herrie82
Created December 27, 2025 15:49
Show Gist options
  • Select an option

  • Save Herrie82/1e4300e63e2852e4c43a3a68510cfca3 to your computer and use it in GitHub Desktop.

Select an option

Save Herrie82/1e4300e63e2852e4c43a3a68510cfca3 to your computer and use it in GitHub Desktop.
Touchscreen for Tenderloin on Mainline
# Cleaned-Up ts-srv Driver Analysis
## Overview
This analysis covers the cleaned-up HP TouchPad touchscreen driver (`ts-srv`) found in `/home/herrie/webos/touchpad-kernel/touchscreen/`. This version has been adapted from the original Android driver to work on modern Linux systems.
**Source Origin:** Evervolv Android ROM for HP TouchPad
**Modifications:** Android NDK dependencies removed, paths updated for Linux
**Status:** Ready for testing on mainline Linux
## File Structure
### Core Files
| File | Size | Purpose |
|------|------|---------|
| `ts_srv.c` | 45KB | Main touchscreen driver daemon |
| `digitizer.c` | 5.9KB | Power management and I²C initialization |
| `config.h` | 3.9KB | Tunable parameters and thresholds |
| `hsuart.h` | 5.7KB | High-speed UART API definitions (Palm) |
| `digitizer.h` | 1.1KB | Power management interface |
| `log.h` | 163B | Android logging stub (no-ops) |
| `ts_srv_set.c` | 4.3KB | Settings management utility |
| `Android.mk` | 1.4KB | Android build file (reference only) |
## Key Modifications from Android Version
### 1. Logging System
**Before (Android):**
```c
#include <cutils/log.h>
ALOGD("Debug message");
ALOGI("Info message");
ALOGE("Error message");
```
**After (Linux):**
```c
#include "log.h" // Stub header
// All logging is no-ops
#define ALOGD(...)
#define ALOGI(...)
#define ALOGE(...)
```
**Impact:** Silent operation by default. Can easily replace with `syslog()` for production.
### 2. File Paths
**Socket Path:**
- Android: `/dev/socket/tsdriver`
- Linux: `/run/tsdriver.sock`
**Settings File:**
- Android: `/data/tssettings`
- Linux: `/etc/tssettings`
**UART Device:**
- Remains: `/dev/ctp_uart` (needs device tree to create this device node)
**uinput Device:**
- Standard: `/dev/uinput` (same on both)
### 3. I²C Device
**Critical Discovery:**
```c
i2c_fd = open("/dev/i2c-5", O_RDWR);
```
**NOT `/dev/i2c-10`!** The cleaned code uses **I²C bus 5**.
**Analysis:**
- Original webOS kernel may have numbered buses differently
- GSBI10 in device tree is I²C bus 10 in mainline kernel
- **Need to verify actual I²C bus numbering on hardware**
- May need to change to `/dev/i2c-10` for mainline kernel
### 4. Wake GPIO Path
**Wake Pin Control:**
```c
wake_fd = open("/sys/user_hw/pins/ctp/wake/level", O_WRONLY);
```
**Issue:** This is a **Palm/webOS specific sysfs path** that doesn't exist in mainline.
**Mainline Alternative:**
```c
// Use cy8ctma395 driver sysfs or control wake via I²C
// The wake pin is GPIO 123, may need to export it manually
wake_fd = open("/sys/class/gpio/gpio123/value", O_WRONLY);
```
## UART Protocol Details
### Packet Format
**Scan Line Data Packet (0xFF 0x43):**
```c
if (cline[0] == 0xff && cline[1] == 0x43 && cidx == 44)
```
**Structure:**
```
Byte 0: 0xFF // Magic header
Byte 1: 0x43 // Packet type: Scan data
Byte 2: Row index & flags // bits 0-4: row (0-29)
// bit 7: clear matrix flag
Bytes 3-42: Matrix data (40 bytes, one per column)
```
**Total:** 44 bytes (including 1 byte consumed before reaching this check)
**Scan Complete Packet (0xFF 0x47):**
```c
if (cline[0] == 0xff && cline[1] == 0x47 && cidx > 4 &&
cidx == (cline[2]+4))
```
**Structure:**
```
Byte 0: 0xFF // Magic header
Byte 1: 0x47 // Packet type: Scan complete
Byte 2: Data length (N) // Additional data bytes
Bytes 3-N: Additional data // Meaning unknown
```
**Total:** Variable (4 + N bytes)
### Matrix Organization
**30×40 Sensor Grid:**
```c
#define X_AXIS_POINTS 30 // Columns (i-axis, 0-29)
#define Y_AXIS_POINTS 40 // Rows (j-axis, 0-39)
unsigned char matrix[X_AXIS_POINTS][Y_AXIS_POINTS];
```
**Data Storage:**
- Each 0xFF 0x43 packet contains **one row** (40 values)
- Row index encoded in `cline[2] & 0x1F` (bits 0-4)
- If bit 7 set (`cline[2] & 0x80`), clear entire matrix first
- Takes **30 packets** to fill complete matrix
- Final 0xFF 0x47 packet signals scan complete
**Example Packet Processing:**
```c
if (cline[1] == 0x43) {
// Clear matrix if this is first row
if (cline[2] & 0x80) {
memset(matrix, 0, sizeof(matrix));
}
// Write row data
int row = cline[2] & 0x1F; // Extract row index (0-29)
for (i = 0; i < Y_AXIS_POINTS; i++)
matrix[row][i] = cline[i+3]; // Copy 40 bytes
}
```
### UART Configuration
**Device Initialization:**
```c
*uart_fd = open("/dev/ctp_uart", O_RDONLY|O_NONBLOCK);
struct hsuart_mode uart_mode;
ioctl(*uart_fd, HSUART_IOCTL_GET_UARTMODE, &uart_mode);
uart_mode.speed = 0x3D0900; // 4 Mbps
ioctl(*uart_fd, HSUART_IOCTL_SET_UARTMODE, &uart_mode);
ioctl(*uart_fd, HSUART_IOCTL_FLUSH, 0x9); // Flush RX+TX
```
**Read Loop:**
```c
#define RECV_BUF_SIZE 1540
unsigned char recv_buf[RECV_BUF_SIZE];
nbytes = read(uart_fd, recv_buf, RECV_BUF_SIZE);
snarf2(recv_buf, nbytes); // Parse packets
```
## I²C Initialization Sequence
### Power-On Sequence
```c
void digitizer_power(int enable) {
if (enable) {
// 1. Assert reset
write(xres_fd, "1", 1);
// 2. Power on VDD
write(vdd_fd, "1", 1);
usleep(50000); // 50ms voltage stabilization
// 3. Assert wake
write(wake_fd, "1", 1);
// 4. Release reset
write(xres_fd, "0", 1);
usleep(50000);
// 5. Release wake
write(wake_fd, "0", 1);
usleep(50000);
// 6. I²C configuration sequence (7 commands)
i2c_msg.addr = 0x67;
// Command 1: Enter config mode
i2c_buf[0] = 0x08; i2c_buf[1] = 0x00;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
// Command 2: Scan configuration (6 bytes)
i2c_buf[0] = 0x31; i2c_buf[1] = 0x01; i2c_buf[2] = 0x08;
i2c_buf[3] = 0x0C; i2c_buf[4] = 0x0D; i2c_buf[5] = 0x0A;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
// Commands 3-6: Configuration parameters
i2c_buf[0] = 0x30; i2c_buf[1] = 0x0F;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
i2c_buf[0] = 0x40; i2c_buf[1] = 0x02;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
i2c_buf[0] = 0x41; i2c_buf[1] = 0x10;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
i2c_buf[0] = 0x0A; i2c_buf[1] = 0x04;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
// Command 7: Start UART streaming
i2c_buf[0] = 0x08; i2c_buf[1] = 0x03;
ioctl(i2c_fd, I2C_RDWR, &i2c_ioctl_data);
// 7. Re-assert wake (keep active during operation)
write(wake_fd, "1", 1);
}
}
```
**Retry Logic:**
- If first I²C command fails, retry up to `MAX_DIGITIZER_RETRY` (3) times
- Power cycle between retries: VDD off, 10ms delay, try again
### Power-Off Sequence
```c
// 1. Power off VDD
write(vdd_fd, "0", 1);
// 2. Reset to clear data stream (4G TouchPad quirk)
write(xres_fd, "1", 1);
usleep(10000);
write(xres_fd, "0", 1);
usleep(80000); // Wait for liftoff timeout
```
## Touch Detection Algorithm
### Coordinate Transformation
**Matrix to Screen Conversion:**
```c
#define X_RESOLUTION 1024
#define Y_RESOLUTION 768
#define X_LOCATION_VALUE ((float)X_RESOLUTION / (float)(Y_AXIS_MINUS1)) // 1024/39
#define Y_LOCATION_VALUE ((float)Y_RESOLUTION / (float)(X_AXIS_MINUS1)) // 768/29
// Conversion (axes swapped and inverted)
tp[tpoint][tpc].x = X_RESOLUTION_MINUS1 - tp[tpoint][tpc].j * X_LOCATION_VALUE;
tp[tpoint][tpc].y = Y_RESOLUTION_MINUS1 - tp[tpoint][tpc].i * Y_LOCATION_VALUE;
```
**Coordinate Mapping:**
- Matrix J-axis (0-29) → Screen X-axis (1023-0) **INVERTED**
- Matrix I-axis (0-39) → Screen Y-axis (767-0) **INVERTED**
### Threshold Configuration
**Finger Mode (Default):**
```c
#define TOUCH_INITIAL_THRESHOLD 32 // Immediate report
#define TOUCH_CONTINUE_THRESHOLD 26 // Continue tracking
#define TOUCH_DELAY_THRESHOLD 28 // Delayed report
#define TOUCH_DELAY 5 // Frames to wait
#define LARGE_AREA_UNPRESS 22 // Large touch end
#define LARGE_AREA_FRINGE 5 // Touch edge
```
**Stylus Mode:**
```c
#define TOUCH_INITIAL_THRESHOLD_S 32
#define TOUCH_CONTINUE_THRESHOLD_S 16 // Lower threshold
#define TOUCH_DELAY_THRESHOLD_S 24
#define TOUCH_DELAY_S 2
```
**Settable via socket or `/etc/tssettings` file.**
### Touch Blob Detection
**Weighted Centroid Calculation:**
```c
// 1. Scan matrix for values above threshold
for (i = 0; i < X_AXIS_POINTS; i++) {
for (j = 0; j < Y_AXIS_POINTS; j++) {
if (matrix[i][j] > touch_continue_thresh && !invalid_matrix[i][j]) {
// 2. Recursively find connected area
determine_area_loc(&isum, &jsum, &tweight, i, j,
&mini, &maxi, &minj, &maxj,
touch_id, &highest_val);
// 3. Calculate weighted centroid
powered = pow(matrix[i][j], 1.5); // 1.5-power scaling
tweight += powered;
isum += powered * i;
jsum += powered * j;
// 4. Centroid coordinates
avgi = isum / (float)tweight;
avgj = jsum / (float)tweight;
}
}
}
```
**Touch Area Size:**
```c
#define PIXELS_PER_POINT 25 // Roughly 1024/40 or 768/30
int maxi = maxi - mini; // Height in matrix units
int maxj = maxj - minj; // Width in matrix units
touch_major = MAX(maxi, maxj) * PIXELS_PER_POINT;
```
### Touch Tracking
**Nearest Neighbor Matching:**
```c
// Match current touch to previous touch by distance
for (k = 0; k < previoustpc; k++) {
int dx = tp[tpoint][i].unfiltered_x - tp[prevtpoint][k].x;
int dy = tp[tpoint][i].unfiltered_y - tp[prevtpoint][k].y;
int distance_sq = dx*dx + dy*dy;
if (distance_sq < min_distance_sq) {
min_distance_sq = distance_sq;
closest_prev_touch = k;
}
}
// Inherit tracking ID from closest match
if (closest_prev_touch >= 0)
tp[tpoint][i].tracking_id = tp[prevtpoint][closest_prev_touch].tracking_id;
```
**Jump Detection:**
```c
#define MAX_DELTA 130 // pixels
#define MIN_PREV_DELTA 40 // pixels
#define MAX_DELTA_ANGLE 0.25 // radians
// If jump exceeds threshold, check for consistent motion
if (distance_sq > MAX_DELTA_SQ) {
// Check previous touch movement
float prev_direction = tp[prevtpoint][k].direction;
float prev_distance = tp[prevtpoint][k].distance;
// Allow jump if previous motion was similar
if (prev_distance >= MIN_PREV_DELTA &&
abs(direction - prev_direction) < MAX_DELTA_ANGLE) {
// Accept as swipe continuation
} else {
// Reject as separate touch (lift + new touch)
tp[tpoint][i].tracking_id = new_tracking_id++;
}
}
```
### Debouncing Filters
**Initial Touch Debounce:**
```c
#define DEBOUNCE_RADIUS 10 // pixels (actually a square)
// First touch in new location
if (new_touch) {
initialx = tp[tpoint][i].x;
initialy = tp[tpoint][i].y;
}
// Keep initial position if within radius
if (abs(tp[tpoint][i].x - initialx) < DEBOUNCE_RADIUS &&
abs(tp[tpoint][i].y - initialy) < DEBOUNCE_RADIUS) {
tp[tpoint][i].x = initialx;
tp[tpoint][i].y = initialy;
}
```
**Hover Debounce (After Swipe):**
```c
#define HOVER_DEBOUNCE_RADIUS 2 // pixels
#define HOVER_DEBOUNCE_DELAY 30 // frames
// Track consistency over multiple frames
if (abs(tp[tpoint][i].x - tp[tpoint][i].hover_x) < HOVER_DEBOUNCE_RADIUS &&
abs(tp[tpoint][i].y - tp[tpoint][i].hover_y) < HOVER_DEBOUNCE_RADIUS) {
tp[tpoint][i].hover_delay++;
} else {
// Position changed, reset tracking
tp[tpoint][i].hover_x = tp[tpoint][i].x;
tp[tpoint][i].hover_y = tp[tpoint][i].y;
tp[tpoint][i].hover_delay = 0;
}
// Start debouncing after stable period
if (tp[tpoint][i].hover_delay >= HOVER_DEBOUNCE_DELAY) {
// Apply debounce filter
}
```
## Linux Input Integration
### uinput Device Setup
```c
void open_uinput(void) {
struct uinput_user_dev device;
memset(&device, 0, sizeof device);
uinput_fd = open("/dev/uinput", O_WRONLY);
strcpy(device.name, "HPTouchpad");
// Enable event types
ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN);
ioctl(uinput_fd, UI_SET_EVBIT, EV_ABS);
ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY);
// Multi-touch protocol A (not B!)
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ioctl(uinput_fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
// Set axis ranges
device.absmin[ABS_MT_POSITION_X] = 0;
device.absmax[ABS_MT_POSITION_X] = X_RESOLUTION - 1;
device.absmin[ABS_MT_POSITION_Y] = 0;
device.absmax[ABS_MT_POSITION_Y] = Y_RESOLUTION - 1;
device.absmin[ABS_MT_TRACKING_ID] = 0;
device.absmax[ABS_MT_TRACKING_ID] = TRACKING_ID_MAX;
device.absmin[ABS_MT_PRESSURE] = 0;
device.absmax[ABS_MT_PRESSURE] = 255;
write(uinput_fd, &device, sizeof(device));
ioctl(uinput_fd, UI_DEV_CREATE);
}
```
**Note:** Protocol B support exists (`USE_B_PROTOCOL`) but is **disabled** by default due to kernel issues on TouchPad.
### Event Reporting (Protocol A)
```c
// For each active touch
send_uevent(uinput_fd, EV_ABS, ABS_MT_TRACKING_ID, tracking_id);
send_uevent(uinput_fd, EV_ABS, ABS_MT_TOUCH_MAJOR, touch_major);
send_uevent(uinput_fd, EV_ABS, ABS_MT_POSITION_X, x);
send_uevent(uinput_fd, EV_ABS, ABS_MT_POSITION_Y, y);
send_uevent(uinput_fd, EV_ABS, ABS_MT_PRESSURE, pressure);
send_uevent(uinput_fd, EV_SYN, SYN_MT_REPORT, 0); // End of touch
// After all touches
send_uevent(uinput_fd, EV_SYN, SYN_REPORT, 0); // Frame sync
```
### Lift-Off Handling
```c
#define LIFTOFF_TIMEOUT 25000 // microseconds
// Timeout mechanism
select(uart_fd + 1, &fdset, NULL, NULL, &timeout);
if (select_ret == 0) {
// No data for 25ms = liftoff
if (need_liftoff) {
liftoff(); // Send all tracking IDs as -1
clear_arrays();
}
}
```
## Runtime Control
### Socket Interface
**Location:** `/run/tsdriver.sock`
**Type:** UNIX domain socket, SOCK_STREAM
**Permissions:** 0666 (world readable/writable)
**Commands:**
| Byte | Command | Description |
|------|---------|-------------|
| 'P' | Power off | Turn off touchscreen |
| 'Q' | Power on | Turn on touchscreen |
| 'F' | Finger mode | Set to finger thresholds |
| 'S' | Stylus mode | Set to stylus thresholds |
| 'M' | Get mode | Return current mode (0=finger, 1=stylus) |
**Example Usage:**
```bash
# Turn off touchscreen
echo -n "P" | nc -U /run/tsdriver.sock
# Turn on touchscreen
echo -n "Q" | nc -U /run/tsdriver.sock
# Switch to stylus mode
echo -n "S" | nc -U /run/tsdriver.sock
# Get current mode
echo -n "M" | nc -U /run/tsdriver.sock
```
### Settings File
**Location:** `/etc/tssettings`
**Format:** Single byte (0 or 1)
```bash
# Finger mode
echo -n "0" > /etc/tssettings
# Stylus mode
echo -n "1" > /etc/tssettings
```
**Auto-loaded on startup.**
## Performance Optimizations
### Real-Time Scheduling
```c
struct sched_param sparam = { .sched_priority = 99 };
if (sched_setscheduler(0, SCHED_FIFO, &sparam))
perror("Cannot set RT priority, ignoring: ");
```
**Priority 99:** Linux maximum RT priority
**SCHED_FIFO:** Never preempted until voluntarily yields CPU
**Goal:** Zero latency in touch processing
### Efficient Event Loop
```c
while (1) {
FD_ZERO(&fdset);
FD_SET(uart_fd, &fdset);
FD_SET(socket_fd, &fdset);
// Wait for data with timeout
select(max_fd + 1, &fdset, NULL, NULL, &timeout);
if (FD_ISSET(uart_fd, &fdset)) {
// Process touch data
}
if (FD_ISSET(socket_fd, &fdset)) {
// Process commands
}
}
```
**Advantages:**
- Sleep until data arrives (low CPU)
- Automatic liftoff detection via timeout
- Multiplexes UART and socket
## Issues and Required Fixes for Mainline Linux
### 1. I²C Bus Number (CRITICAL)
**Current Code:**
```c
i2c_fd = open("/dev/i2c-5", O_RDWR);
```
**Likely Needed:**
```c
i2c_fd = open("/dev/i2c-10", O_RDWR); // GSBI10 = bus 10
```
**Verification:** Check `/sys/bus/i2c/devices/` after booting mainline kernel.
### 2. Wake GPIO Path (CRITICAL)
**Current Code:**
```c
wake_fd = open("/sys/user_hw/pins/ctp/wake/level", O_WRONLY);
```
**Fix Option A - Export GPIO:**
```bash
echo 123 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio123/direction
```
```c
wake_fd = open("/sys/class/gpio/gpio123/value", O_WRONLY);
```
**Fix Option B - Remove Wake Control:**
The wake pin (GPIO 123) is also used as the IRQ pin. The cy8ctma395 driver may handle it automatically. Try commenting out wake control entirely.
### 3. UART Device Node
**Current:** `/dev/ctp_uart`
**Expected:** `/dev/ttyMSM2` (after setting `USE_I2C_TOUCHSCREEN_DRIVER=0`)
**Fix:** Create udev rule or symlink:
```bash
# Udev rule
KERNEL=="ttyMSM2", SYMLINK+="ctp_uart"
```
Or modify code:
```c
*uart_fd = open("/dev/ttyMSM2", O_RDONLY|O_NONBLOCK);
```
### 4. HSUART ioctls
**Current:** Uses Palm HSUART custom ioctls (`HSUART_IOCTL_*`)
**Mainline Alternative:** Standard termios
```c
#include <termios.h>
int fd = open("/dev/ttyMSM2", O_RDONLY | O_NOCTTY);
struct termios tty;
tcgetattr(fd, &tty);
cfsetispeed(&tty, B4000000); // May need custom baud rate ioctl
cfmakeraw(&tty);
tcsetattr(fd, TCSANOW, &tty);
```
**Issue:** Standard termios may not support 4 Mbps. May need MSM UART driver custom ioctl.
### 5. Logging
**Current:** All logging disabled (no-ops)
**Recommendation:** Add syslog support
```c
#include <syslog.h>
#define ALOGD(...) syslog(LOG_DEBUG, __VA_ARGS__)
#define ALOGI(...) syslog(LOG_INFO, __VA_ARGS__)
#define ALOGE(...) syslog(LOG_ERR, __VA_ARGS__)
// In main()
openlog("ts-srv", LOG_PID | LOG_CONS, LOG_DAEMON);
```
### 6. Systemd Integration
Create `/etc/systemd/system/ts-srv.service`:
```ini
[Unit]
Description=HP TouchPad Touchscreen Driver
After=sysinit.target
[Service]
Type=simple
ExecStart=/usr/sbin/ts-srv
Restart=always
RestartSec=1
[Install]
WantedBy=multi-user.target
```
## Build Instructions for Modern Linux
### Dependencies
```bash
# Debian/Ubuntu
sudo apt-get install build-essential libc6-dev linux-headers-$(uname -r)
# Fedora
sudo dnf install gcc glibc-devel kernel-devel
```
### Compilation
```bash
cd /home/herrie/webos/touchpad-kernel/touchscreen
gcc -o ts-srv ts_srv.c digitizer.c -lm -O2 -Wall
gcc -o ts-srv-set ts_srv_set.c -O2 -Wall
```
**Note:** Requires math library (`-lm`) for `pow()` and `sqrt()`.
### Installation
```bash
sudo install -m 755 ts-srv /usr/sbin/ts-srv
sudo install -m 755 ts-srv-set /usr/sbin/ts-srv-set
# Create socket directory
sudo mkdir -p /run
# Set default mode (finger)
echo -n "0" | sudo tee /etc/tssettings
# Install systemd service
sudo cp ts-srv.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable ts-srv
```
## Testing Procedure
### 1. Verify Device Tree Configuration
```bash
# Check UART mode is enabled
grep "USE_I2C_TOUCHSCREEN_DRIVER" /path/to/device-tree-source.dts
# Should be 0, not 1
# Verify GSBI10 mode
grep -A5 "gsbi10:" /path/to/device-tree-source.dts
# Should have qcom,mode = <GSBI_PROT_I2C_UART>
```
### 2. Check UART Device
```bash
ls -l /dev/ttyMSM2
# Should exist if device tree is correct
# Or create symlink
sudo ln -s /dev/ttyMSM2 /dev/ctp_uart
```
### 3. Verify I²C Bus
```bash
ls /dev/i2c-*
# Should see /dev/i2c-10 (or /dev/i2c-5)
# Test I²C communication
sudo i2cdetect -y 10
# Should see device at 0x67
```
### 4. Export Wake GPIO (if needed)
```bash
echo 123 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio123/direction
echo 1 > /sys/class/gpio/gpio123/value
```
### 5. Run Driver
```bash
# Run in foreground for debugging
sudo /usr/sbin/ts-srv
# Check for errors
dmesg | tail -50
# In another terminal, test touch events
sudo evtest
# Select HPTouchpad device
# Touch screen and verify events appear
```
### 6. Control via Socket
```bash
# Test socket commands
echo -n "P" | nc -U /run/tsdriver.sock # Power off
sleep 1
echo -n "Q" | nc -U /run/tsdriver.sock # Power on
```
## Summary of Cleaning Changes
| Aspect | Android Version | Cleaned Linux Version |
|--------|----------------|----------------------|
| Logging | `<cutils/log.h>` | Stub no-ops in `log.h` |
| Socket path | `/dev/socket/tsdriver` | `/run/tsdriver.sock` |
| Settings file | `/data/tssettings` | `/etc/tssettings` |
| I²C bus | Unknown/varies | `/dev/i2c-5` (may need →10) |
| Wake GPIO | webOS sysfs | `/sys/user_hw/pins/...` (needs fix) |
| UART device | `/dev/ctp_uart` | Same (needs symlink) |
| Build system | Android NDK | Standard GCC |
## Conclusion
The cleaned-up `ts-srv` driver is **90% ready** for mainline Linux use. The remaining issues are:
**Required Fixes:**
1. ✓ Change I²C bus from 5 to 10 (or verify actual number)
2. ✓ Fix wake GPIO path (export GPIO 123 or remove)
3. ✓ Create `/dev/ctp_uart` symlink to `/dev/ttyMSM2`
4. ? Replace HSUART ioctls with termios (if MSM UART doesn't support them)
**Optional Improvements:**
- Add syslog logging
- Create systemd service
- Add command-line options
- Better error handling
**Estimated effort:** 1-2 days for fixes, testing, and integration.
The code quality is excellent, the algorithm is well-documented, and the touch processing logic is production-ready. This is a solid foundation for getting the HP TouchPad touchscreen working on mainline Linux.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment