From: Harry J M. <hj...@ec...> - 2008-08-05 15:53:11
|
This is an experimental replacement driver for the tsc2003 touchscreen controller, written in user space using the uinput kernel module. To use it, modprobe uinput, i2c-dev, and i2c-pxa, run the program, and configure the tslib touchscreen device to be /dev/input/event0 in /etc/profile.d/tslib.sh . It needs to be run before the X server starts. I've found that the regular driver is unreliable and can cause i2c timeouts, as well as apparently using 100% of the CPU. Has anyone else had these problems with the in-kernel driver? Harry /* * tsc2003-uinput.c * User space driver for the TSC2003 I2C touchscreen controller * Copyright (C) 2008 Harry Mason * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include <linux/input.h> #include <linux/uinput.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <signal.h> #include <sys/time.h> /* I2C bus device. This requires the i2c-dev kernel module. */ static char i2c_device[] = "/dev/i2c-0"; /* I2C address of the TSC2003 chip. */ static int i2c_address = 0x48; /* This requires the uinput kernel module. */ static char uinput_device[] = "/dev/input/uinput"; static int i2c_fd; static int uinput_fd; struct tsc2003_state { int x; int y; int z1; int z2; }; static int quit_pending = 0; void signal_handler(int signum) { quit_pending = 1; } int init_i2c(void) { if ((i2c_fd = open(i2c_device, O_RDWR)) < 0) { perror("opening i2c device"); return -1; } if (ioctl(i2c_fd, I2C_SLAVE, i2c_address) < 0) { perror("setting i2c slave address"); return -1; } return 0; } void fini_i2c(void) { close(i2c_fd); } int read_tsc2003(struct tsc2003_state *state) { unsigned char buf[3]; int result; /* Commands are taken from the TSC2003 datasheet. */ buf[0] = 0xee; /* measure Z1; leave drivers powered up; 8 bit */ if (write(i2c_fd, buf, 1) != 1) { perror("read_tsc2003: i2c write error"); return -1; } if (read(i2c_fd, buf, 1) != 1) { perror("read_tsc2003: i2c read error"); return -1; } state->z1 = buf[0]; buf[0] = 0xfe; /* measure Z2; leave drivers powered up; 8 bit */ if (write(i2c_fd, buf, 1) != 1) { perror("read_tsc2003: i2c write error"); return -1; } if (read(i2c_fd, buf, 1) != 1) { perror("read_tsc2003: i2c read error"); return -1; } state->z2 = buf[0]; buf[0] = 0xcc; /* measure X; leave drivers powered up; 12 bit */ if (write(i2c_fd, buf, 1) != 1) { perror("read_tsc2003: i2c write error"); return -1; } if (read(i2c_fd, buf, 2) != 2) { perror("read_tsc2003: i2c read error"); return -1; } state->x = (buf[0] << 4) | (buf[1] >> 4); buf[0] = 0xd0; /* measure Y; power down drivers after; 12 bit */ if (write(i2c_fd, buf, 1) != 1) { perror("read_tsc2003: i2c write error"); return -1; } if (read(i2c_fd, buf, 2) != 2) { perror("read_tsc2003: i2c read error"); return -1; } state->y = (buf[0] << 4) | (buf[1] >> 4); return 0; } int init_uinput(void) { struct uinput_user_dev uinput_conf; if ((uinput_fd = open(uinput_device, O_WRONLY | O_NDELAY)) < 0) { perror("opening uinput device"); return -1; } if (ioctl(uinput_fd, UI_SET_EVBIT, EV_ABS) < 0) { perror("configuring uinput device"); return -1; } if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_X) < 0) { perror("configuring uinput device"); return -1; } if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_Y) < 0) { perror("configuring uinput device"); return -1; } if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_PRESSURE) < 0) { perror("configuring uinput device"); return -1; } memset(&uinput_conf, 0, sizeof(uinput_conf)); strncpy(uinput_conf.name, "TSC2003 Touchscreen (userspace driver)", UINPUT_MAX_NAME_SIZE); uinput_conf.id.version = 1; uinput_conf.id.bustype = BUS_I2C; /* set range of axes */ uinput_conf.absmin [ABS_X] = 0; uinput_conf.absmax [ABS_X] = 0x0fff; uinput_conf.absfuzz[ABS_X] = 0; uinput_conf.absflat[ABS_X] = 0; uinput_conf.absmin [ABS_Y] = 0; uinput_conf.absmax [ABS_Y] = 0x0fff; uinput_conf.absfuzz[ABS_Y] = 0; uinput_conf.absflat[ABS_Y] = 0; uinput_conf.absmin [ABS_PRESSURE] = 0; uinput_conf.absmax [ABS_PRESSURE] = 0xff; uinput_conf.absfuzz[ABS_PRESSURE] = 0; uinput_conf.absflat[ABS_PRESSURE] = 0; write(uinput_fd, &uinput_conf, sizeof(uinput_conf)); if (ioctl(uinput_fd, UI_DEV_CREATE)) { perror("creating uinput device"); return -1; } return 0; } void fini_uinput(void) { ioctl(uinput_fd, UI_DEV_DESTROY); close(uinput_fd); } void uinput_send_event(struct tsc2003_state *state) { struct input_event event; memset(&event, 0, sizeof(event)); gettimeofday(&event.time, NULL); event.type = EV_ABS; event.code = ABS_X; event.value = state->x; write(uinput_fd, &event, sizeof(event)); event.type = EV_ABS; event.code = ABS_Y; event.value = state->y; write(uinput_fd, &event, sizeof(event)); event.type = EV_ABS; event.code = ABS_PRESSURE; event.value = state->z1; write(uinput_fd, &event, sizeof(event)); event.type = EV_SYN; event.code = SYN_REPORT; event.value = 0; write(uinput_fd, &event, sizeof(event)); } int main(int argc, char *argv[]) { struct tsc2003_state state; int result; if (init_i2c() != 0) { return 1; } if (init_uinput() != 0) { return 1; } signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); while (!quit_pending) { if (read_tsc2003(&state) != 0) { return 1; } uinput_send_event(&state); usleep(30000); } fini_uinput(); fini_i2c(); return 0; } |
From: Edward S. <esc...@gm...> - 2008-08-05 16:09:36
|
2008/8/5 Harry J Mason <hj...@ec...>: > This is an experimental replacement driver for the tsc2003 touchscreen > controller, written in user space using the uinput kernel module. > To use it, modprobe uinput, i2c-dev, and i2c-pxa, run the program, and > configure the tslib touchscreen device to be /dev/input/event0 in > /etc/profile.d/tslib.sh . It needs to be run before the X server starts. > > I've found that the regular driver is unreliable and can cause i2c > timeouts, as well as apparently using 100% of the CPU. > > Has anyone else had these problems with the in-kernel driver? > Yes. I found the touch screen totally unusable. The best I could get was one touch accepted on the calibration program. SDL based applications would hang on any use of the touch screen. Thanks for publishing the user space driver. Edward Scott |