6 * Designed for for use with the Linux-VR project touch-panel kernel driver.
7 * This includes the VTech Helio.
8 * Also runs with Embedded Planet's PowerPC LinuxPlanet.
9 * Also runs with Yopy (untested, can use mou_yopy.c also)
11 * Requires /dev/tpanel kernel driver (char special 10,11)
13 * Copyright (C) 1999 Bradley D. LaRonde <brad@ltc.com>
14 * Portions Copyright (c) 2001 Kevin Oh <webmaster@prg-lib.net>
15 * Portions Copyright (c) 1999, 2000 Greg Haerr <greg@censoft.com>
16 * Portions Copyright (c) 1991 David I. Bell
18 * Permission is granted to use, distribute, or modify this source,
19 * provided that this copyright notice remains intact.
21 * This program is free software; you may redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42 #include <sys/ioctl.h>
43 #if !defined(TPHELIO) && !defined(YOPY)
44 #include <linux/tpanel.h>
49 #define EMBEDDEDPLANET 0 /* =1 for embeddedplanet ppc framebuffer*/
51 #define DATACHANGELIMIT 50
53 #define DATACHANGELIMIT 100
57 * Enable absolute coordinate transformation.
58 * Normally this should be left at 1.
59 * To disable transformation, set it to 0 before calling MwMain().
60 * This is done by the pointer calibration utility since it's used
61 * to produce the pointer calibration data file.
63 int enable_pointing_coordinate_transform = 1;
65 static TRANSFORMATION_COEFFICIENTS tc;
67 /* file descriptor for touch panel */
70 int GetPointerCalibrationData(void)
73 * Read the calibration data from the calibration file.
74 * Calibration file format is seven coefficients separated by spaces.
77 /* Get pointer calibration data from this file */
78 const char cal_filename[] = "/etc/pointercal";
82 FILE* f = fopen(cal_filename, "r");
85 EPRINTF("Error %d opening pointer calibration file %s.\n",
90 items = fscanf(f, "%d %d %d %d %d %d %d",
91 &tc.a, &tc.b, &tc.c, &tc.d, &tc.e, &tc.f, &tc.s);
94 EPRINTF("Improperly formatted pointer calibration file %s.\n",
100 EPRINTF("a=%d b=%d c=%d d=%d e=%d f=%d s=%d\n",
101 tc.a, tc.b, tc.c, tc.d, tc.e, tc.f, tc.s);
107 inline MWPOINT DeviceToScreen(MWPOINT p)
110 * Transform device coordinates to screen coordinates.
111 * Take a point p in device coordinates and return the corresponding
112 * point in screen coodinates.
113 * This can scale, translate, rotate and/or skew, based on the
114 * coefficients calculated above based on the list of screen
115 * vs. device coordinates.
119 /* set slop at 3/4 pixel */
120 const short slop = TRANSFORMATION_UNITS_PER_PIXEL * 3 / 4;
124 new.x = (tc.a * p.x + tc.b * p.y + tc.c) / tc.s;
125 new.y = (tc.d * p.x + tc.e * p.y + tc.f) / tc.s;
127 /* hysteresis (thanks to John Siau) */
128 if ( abs(new.x - prev.x) >= slop )
129 out.x = (new.x | 0x3) ^ 0x3;
133 if ( abs(new.y - prev.y) >= slop )
134 out.y = (new.y | 0x3) ^ 0x3;
144 static int PD_Open(MOUSEDEVICE *pmd)
147 * open up the touch-panel device.
148 * Return the fd if successful, or negative if unsuccessful.
152 int settle_upper_limit;
156 pd_fd = open("/dev/tpanel", O_NONBLOCK);
158 EPRINTF("Error %d opening touch panel\n", errno);
163 /* set interval to 5000us (200Hz) */
166 * Upper limit on settle time is approximately (scan_interval / 5) - 60
167 * (5 conversions and some fixed overhead)
168 * The opmtimal value is the lowest that doesn't cause significant
170 * 50% of upper limit works well on my Clio. 25% gets into distortion.
172 settle_upper_limit = (s.interval / 5) - 60;
173 s.settletime = settle_upper_limit * 50 / 100;
174 result = ioctl(pd_fd, TPSETSCANPARM, &s);
176 EPRINTF("Error %d, result %d setting scan parameters.\n",
180 if (enable_pointing_coordinate_transform)
182 if (GetPointerCalibrationData() < 0)
191 #else /* ifndef YOPY */
193 static TRANSFORMATION_COEFFICIENTS default_tc = {
194 68339, 328, -3042464, -508, 91638, -4339545, 65536
197 static int PD_Open(MOUSEDEVICE *pmd)
201 * open up the touch-panel device.
202 * Return the fd if successful, or negative if unsuccessful.
205 pd_fd = open("/dev/yopy-ts", O_NONBLOCK);
207 EPRINTF("Error %d opening touch panel\n", errno);
211 if (enable_pointing_coordinate_transform)
213 if (GetPointerCalibrationData() < 0)
217 memcpy( &tc, &default_tc, sizeof(TRANSFORMATION_COEFFICIENTS) );
222 EPRINTF("a=%d b=%d c=%d d=%d e=%d f=%d s=%d\n",
223 tc.a, tc.b, tc.c, tc.d, tc.e, tc.f, tc.s);
230 static void PD_Close(void)
232 /* Close the touch panel device. */
238 static int PD_GetButtonInfo(void)
240 /* get "mouse" buttons supported */
244 static void PD_GetDefaultAccel(int *pscale,int *pthresh)
247 * Get default mouse acceleration settings
248 * This doesn't make sense for a touch panel.
249 * Just return something inconspicuous for now.
256 static int PD_Read(MWCOORD *px, MWCOORD *py, MWCOORD *pz, int *pb)
259 * Read the tpanel state and position.
260 * Returns the position data in x, y, and button data in b.
261 * Returns -1 on error.
262 * Returns 0 if no new data is available.
263 * Returns 1 if position data is relative (i.e. mice).
264 * Returns 2 if position data is absolute (i.e. touch panels).
265 * Returns 3 if position data is not available, but button data is.
266 * This routine does not block.
268 * Unlike a mouse, this driver returns absolute postions, not deltas.
271 /* If z is below this value, ignore the data. */
272 /* const int low_z_limit = 900; EVEREX*/
274 const int low_z_limit = 815;
277 * I do some error masking by tossing out really wild data points.
278 * Lower data_change_limit value means pointer get's "left behind"
279 * more easily. Higher value means less errors caught.
280 * The right setting of this value is just slightly higher than
281 * the number of units traversed per sample during a "quick" stroke.
284 const int data_change_limit = DATACHANGELIMIT;
286 static int have_last_data = 0;
287 static int last_data_x = 0;
288 static int last_data_y = 0;
291 * Thanks to John Siau <jsiau@benchmarkmedia.com> for help with the
292 * noise filter. I use an infinite impulse response low-pass filter
293 * on the data to filter out high-frequency noise. Results look
294 * better than a finite impulse response filter.
295 * If I understand it right, the nice thing is that the noise now
296 * acts as a dither signal that effectively increases the resolution
297 * of the a/d converter by a few bits and drops the noise level by
299 * Please don't quote me on those db numbers however. :-)
300 * The end result is that the pointer goes from very fuzzy to much
301 * more steady. Hysteresis really calms it down in the end (elsewhere).
303 * iir_shift_bits effectively sets the number of samples used by
304 * the filter * (number of samples is 2^iir_shift_bits).
305 * Lower iir_width means less pointer lag, higher iir_width means
308 const int iir_shift_bits = 3;
309 const int iir_sample_depth = (1 << iir_shift_bits);
310 static int iir_accum_x = 0;
311 static int iir_accum_y = 0;
312 static int iir_accum_z = 0;
313 static int iir_count = 0;
314 int data_x, data_y, data_z;
316 /* read a data point */
323 bytes_read = read(pd_fd, data, sizeof(data));
324 if (bytes_read != sizeof(data)) {
325 if (errno == EINTR || errno == EAGAIN) {
331 /* did we lose any data? */
332 if ( (data[0] & 0x2000) )
333 EPRINTF("Lost touch panel data\n");
335 /* do we only have contact state data (no position data)? */
336 if ( (data[0] & 0x8000) == 0 )
338 /* is it a pen-release? */
339 if ( (data[0] & 0x4000) == 0 )
341 /* reset the limiter */
344 /* reset the filter */
350 /* return the pen (button) state only, */
351 /* indicating that the pen is up (no buttons are down)*/
356 /* ignore pen-down since we don't know where it is */
360 /* we have position data */
364 data_z = data[0] ? 2000 : 0;
367 * Combine the complementary panel readings into one value (except z)
368 * This effectively doubles the sampling freqency, reducing noise
370 * Again, please don't quote the 3db figure. I think it also
371 * cancels out changes in the overall resistance of the panel
372 * such as may be caused by changes in panel temperature.
374 data_x = data[2] - data[1];
375 data_y = data[4] - data[3];
378 /* isn't z big enough for valid position data? */
379 if ( data_z <= low_z_limit ) {
383 /* has the position changed more than we will allow? */
384 if ( have_last_data )
385 if ( (abs(data_x - last_data_x) > data_change_limit)
386 || ( abs(data_y - last_data_y) > data_change_limit ) ) {
391 /* save last position */
392 last_data_x = data_x;
393 last_data_y = data_y;
396 /* is filter ready? */
397 if ( iir_count == iir_sample_depth )
400 if (enable_pointing_coordinate_transform) {
401 MWPOINT transformed = {data_x, data_y};
402 transformed = DeviceToScreen(transformed);
404 *px = transformed.x >> 2;
405 *py = transformed.y >> 2;
410 *pb = data[0] ? MWBUTTON_L : 0;
412 /* make room for new sample */
413 iir_accum_x -= iir_accum_x >> iir_shift_bits;
414 iir_accum_y -= iir_accum_y >> iir_shift_bits;
415 iir_accum_z -= iir_accum_z >> iir_shift_bits;
417 /* feed new sample to filter */
418 iir_accum_x += data_x;
419 iir_accum_y += data_y;
420 iir_accum_z += data_z;
422 /* transformation enabled? */
423 if (enable_pointing_coordinate_transform)
425 /* transform x,y to screen coords */
426 MWPOINT transformed = {iir_accum_x, iir_accum_y};
427 transformed = DeviceToScreen(transformed);
429 * HACK: move this from quarter pixels to whole
430 * pixels for now at least until I decide on the
431 * right interface to get the quarter-pixel data
432 * up to the next layer.
434 *px = transformed.x >> 2;
435 *py = transformed.y >> 2;
439 /* return untransformed coords (for calibration) */
445 /* return filtered pressure */
449 EPRINTF("In: %hd, %hd, %hd Filtered: %d %d %d Out: %d, %d, %d\n",
450 data_x, data_y, data_z, iir_accum_x, iir_accum_y,
451 iir_accum_z, *px, *py, *pz);
456 /* prime the filter */
457 iir_accum_x += data_x;
458 iir_accum_y += data_y;
459 iir_accum_z += data_z;
464 #else /* ifdef YOPY */
465 static int PD_Read(MWCOORD *px, MWCOORD *py, MWCOORD *pz, int *pb)
468 * Read the tpanel state and position.
469 * Returns the position data in x, y, and button data in b.
470 * Returns -1 on error.
471 * Returns 0 if no new data is available.
472 * Returns 1 if position data is relative (i.e. mice).
473 * Returns 2 if position data is absolute (i.e. touch panels).
474 * Returns 3 if position data is not available, but button data is.
475 * This routine does not block.
477 * Unlike a mouse, this driver returns absolute postions, not deltas.
480 /* If z is below this value, ignore the data. */
481 /* const int low_z_limit = 900; EVEREX*/
484 * I do some error masking by tossing out really wild data points.
485 * Lower data_change_limit value means pointer get's "left behind"
486 * more easily. Higher value means less errors caught.
487 * The right setting of this value is just slightly higher than
488 * the number of units traversed per sample during a "quick" stroke.
493 /* read a data point */
497 bytes_read = read(pd_fd, &mou_data, sizeof(mou_data));
498 if (bytes_read != sizeof(mou_data)) {
499 if (errno == EINTR || errno == EAGAIN) {
505 data_x = ((MWCOORD)(mou_data & 0x3ff));
506 data_y = (MWCOORD)(( mou_data>>10 ) & 0x3ff );
508 /* transformation enabled? */
509 if (enable_pointing_coordinate_transform)
511 /* transform x,y to screen coords */
512 MWPOINT transformed = {data_x, data_y};
513 transformed = DeviceToScreen(transformed);
515 * HACK: move this from quarter pixels to whole
516 * pixels for now at least until I decide on the
517 * right interface to get the quarter-pixel data
518 * up to the next layer.
520 *px = transformed.x >> 2;
521 *py = transformed.y >> 2;
525 /* return untransformed coords (for calibration) */
531 *pb = ( mou_data & (1<<31) )? MWBUTTON_L: 0;
540 MOUSEDEVICE mousedev = {
556 enable_pointing_coordinate_transform = 1;
558 DPRINTF("Opening touch panel...\n");
560 if((result=PD_Open(0)) < 0)
561 DPRINTF("Error %d, result %d opening touch-panel\n", errno, result);
563 DPRINTF("Reading touch panel...\n");
566 result = PD_Read(&x, &y, &z, &b);
568 /* DPRINTF("%d,%d,%d,%d,%d\n", result, x, y, z, b); */