OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [MW_0_8_9PRE7/] [mw/] [src/] [drivers/] [mou_tp.c] - Blame information for rev 674

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 673 markom
/*
2
 * drivers/mou_tp.c
3
 *
4
 * Touch-panel driver
5
 *
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)
10
 *
11
 * Requires /dev/tpanel kernel driver (char special 10,11)
12
 *
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
17
 *
18
 * Permission is granted to use, distribute, or modify this source,
19
 * provided that this copyright notice remains intact.
20
 *
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.
25
 *
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.
30
 *
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
34
 */
35
 
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <unistd.h>
39
#include <errno.h>
40
#include <fcntl.h>
41
#include <math.h>
42
#include <sys/ioctl.h>
43
#if !defined(TPHELIO) && !defined(YOPY)
44
#include <linux/tpanel.h>
45
#endif
46
#include "device.h"
47
#include "mou_tp.h"
48
 
49
#define EMBEDDEDPLANET  0        /* =1 for embeddedplanet ppc framebuffer*/
50
#if EMBEDDEDPLANET
51
#define DATACHANGELIMIT 50
52
#else
53
#define DATACHANGELIMIT 100
54
#endif
55
 
56
/*
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.
62
 */
63
int enable_pointing_coordinate_transform = 1;
64
 
65
static TRANSFORMATION_COEFFICIENTS tc;
66
 
67
/* file descriptor for touch panel */
68
static int pd_fd;
69
 
70
int GetPointerCalibrationData(void)
71
{
72
        /*
73
         * Read the calibration data from the calibration file.
74
         * Calibration file format is seven coefficients separated by spaces.
75
         */
76
 
77
        /* Get pointer calibration data from this file */
78
        const char cal_filename[] = "/etc/pointercal";
79
 
80
        int items;
81
 
82
        FILE* f = fopen(cal_filename, "r");
83
        if ( f == NULL )
84
        {
85
                EPRINTF("Error %d opening pointer calibration file %s.\n",
86
                        errno, cal_filename);
87
                return -1;
88
        }
89
 
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);
92
        if ( items != 7 )
93
        {
94
                EPRINTF("Improperly formatted pointer calibration file %s.\n",
95
                        cal_filename);
96
                return -1;
97
        }
98
 
99
#if TEST
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);
102
#endif
103
 
104
        return 0;
105
}
106
 
107
inline MWPOINT DeviceToScreen(MWPOINT p)
108
{
109
        /*
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.
116
         */
117
 
118
        static MWPOINT prev;
119
        /* set slop at 3/4 pixel */
120
        const short slop = TRANSFORMATION_UNITS_PER_PIXEL * 3 / 4;
121
        MWPOINT new, out;
122
 
123
        /* transform */
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;
126
 
127
        /* hysteresis (thanks to John Siau) */
128
        if ( abs(new.x - prev.x) >= slop )
129
                out.x = (new.x | 0x3) ^ 0x3;
130
        else
131
                out.x = prev.x;
132
 
133
        if ( abs(new.y - prev.y) >= slop )
134
                out.y = (new.y | 0x3) ^ 0x3;
135
        else
136
                out.y = prev.y;
137
 
138
        prev = out;
139
 
140
        return out;
141
}
142
 
143
#ifndef YOPY
144
static int PD_Open(MOUSEDEVICE *pmd)
145
{
146
        /*
147
         * open up the touch-panel device.
148
         * Return the fd if successful, or negative if unsuccessful.
149
         */
150
#ifndef TPHELIO
151
        struct scanparam s;
152
        int settle_upper_limit;
153
        int result;
154
#endif
155
 
156
        pd_fd = open("/dev/tpanel", O_NONBLOCK);
157
        if (pd_fd < 0) {
158
                EPRINTF("Error %d opening touch panel\n", errno);
159
                return -1;
160
        }
161
 
162
#ifndef TPHELIO
163
        /* set interval to 5000us (200Hz) */
164
        s.interval = 5000;
165
        /*
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
169
         * distortion.
170
         * 50% of upper limit works well on my Clio.  25% gets into distortion.
171
         */
172
        settle_upper_limit = (s.interval / 5) - 60;
173
        s.settletime = settle_upper_limit * 50 / 100;
174
        result = ioctl(pd_fd, TPSETSCANPARM, &s);
175
        if ( result < 0 )
176
                EPRINTF("Error %d, result %d setting scan parameters.\n",
177
                        result, errno);
178
#endif
179
 
180
        if (enable_pointing_coordinate_transform)
181
        {
182
                if (GetPointerCalibrationData() < 0)
183
                {
184
                        close(pd_fd);
185
                        return -1;
186
                }
187
        }
188
 
189
        return pd_fd;
190
}
191
#else   /* ifndef YOPY */
192
 
193
static TRANSFORMATION_COEFFICIENTS default_tc = {
194
        68339, 328, -3042464, -508, 91638, -4339545, 65536
195
};
196
 
197
static int PD_Open(MOUSEDEVICE *pmd)
198
 
199
{
200
        /*
201
         * open up the touch-panel device.
202
         * Return the fd if successful, or negative if unsuccessful.
203
         */
204
 
205
        pd_fd = open("/dev/yopy-ts", O_NONBLOCK);
206
        if (pd_fd < 0) {
207
                EPRINTF("Error %d opening touch panel\n", errno);
208
                return -1;
209
        }
210
 
211
        if (enable_pointing_coordinate_transform)
212
        {
213
                if (GetPointerCalibrationData() < 0)
214
                {
215
                        //close(pd_fd);
216
                        //return -1;
217
                        memcpy( &tc, &default_tc, sizeof(TRANSFORMATION_COEFFICIENTS) );
218
                }
219
        }
220
 
221
#if 0
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);
224
#endif
225
 
226
        return pd_fd;
227
}
228
#endif
229
 
230
static void PD_Close(void)
231
{
232
        /* Close the touch panel device. */
233
        if (pd_fd > 0)
234
                close(pd_fd);
235
        pd_fd = 0;
236
}
237
 
238
static int PD_GetButtonInfo(void)
239
{
240
        /* get "mouse" buttons supported */
241
        return MWBUTTON_L;
242
}
243
 
244
static void PD_GetDefaultAccel(int *pscale,int *pthresh)
245
{
246
        /*
247
         * Get default mouse acceleration settings
248
         * This doesn't make sense for a touch panel.
249
         * Just return something inconspicuous for now.
250
         */
251
        *pscale = 3;
252
        *pthresh = 5;
253
}
254
 
255
#ifndef YOPY
256
static int PD_Read(MWCOORD *px, MWCOORD *py, MWCOORD *pz, int *pb)
257
{
258
        /*
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.
267
         *
268
         * Unlike a mouse, this driver returns absolute postions, not deltas.
269
         */
270
 
271
        /* If z is below this value, ignore the data. */
272
        /* const int low_z_limit = 900; EVEREX*/
273
#ifndef TPHELIO
274
        const int low_z_limit = 815;
275
#endif
276
        /*
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.
282
         */
283
#ifndef TPHELIO
284
        const int data_change_limit = DATACHANGELIMIT;
285
#endif
286
        static int have_last_data = 0;
287
        static int last_data_x = 0;
288
        static int last_data_y = 0;
289
 
290
        /*
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
298
         * about 10db.
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).
302
         *
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
306
         * steadier pointer.
307
         */
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;
315
 
316
        /* read a data point */
317
#if TPHELIO
318
        short data[3];
319
#else
320
        short data[6];
321
#endif
322
        int bytes_read;
323
        bytes_read = read(pd_fd, data, sizeof(data));
324
        if (bytes_read != sizeof(data)) {
325
                if (errno == EINTR || errno == EAGAIN) {
326
                        return 0;
327
                }
328
                return -1;
329
        }
330
#ifndef TPHELIO
331
        /* did we lose any data? */
332
        if ( (data[0] & 0x2000) )
333
                EPRINTF("Lost touch panel data\n");
334
 
335
        /* do we only have contact state data (no position data)? */
336
        if ( (data[0] & 0x8000) == 0 )
337
        {
338
                /* is it a pen-release? */
339
                if ( (data[0] & 0x4000) == 0 )
340
                {
341
                        /* reset the limiter */
342
                        have_last_data = 0;
343
 
344
                        /* reset the filter */
345
                        iir_count = 0;
346
                        iir_accum_x = 0;
347
                        iir_accum_y = 0;
348
                        iir_accum_z = 0;
349
 
350
                        /* return the pen (button) state only, */
351
                        /* indicating that the pen is up (no buttons are down)*/
352
                        *pb = 0;
353
                        return 3;
354
                }
355
 
356
                /* ignore pen-down since we don't know where it is */
357
                return 0;
358
        }
359
#endif
360
        /* we have position data */
361
#if TPHELIO
362
        data_x = data[1];
363
        data_y = data[2];
364
        data_z = data[0] ? 2000 : 0;
365
#else
366
        /*
367
         * Combine the complementary panel readings into one value (except z)
368
         * This effectively doubles the sampling freqency, reducing noise
369
         * by approx 3db.
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.
373
         */
374
        data_x = data[2] - data[1];
375
        data_y = data[4] - data[3];
376
        data_z = data[5];
377
 
378
        /* isn't z big enough for valid position data? */
379
        if ( data_z <= low_z_limit ) {
380
                return 0;
381
        }
382
 
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 ) ) {
387
                        return 0;
388
                }
389
#endif
390
 
391
        /* save last position */
392
        last_data_x = data_x;
393
        last_data_y = data_y;
394
        have_last_data = 1;
395
 
396
        /* is filter ready? */
397
        if ( iir_count == iir_sample_depth )
398
        {
399
#if TPHELIO
400
                if (enable_pointing_coordinate_transform) {
401
                        MWPOINT transformed = {data_x, data_y};
402
                        transformed = DeviceToScreen(transformed);
403
 
404
                        *px = transformed.x >> 2;
405
                        *py = transformed.y >> 2;
406
                } else {
407
                        *px = data_x;
408
                        *py = data_y;
409
                }
410
                *pb = data[0] ? MWBUTTON_L : 0;
411
#else
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;
416
 
417
                /* feed new sample to filter */
418
                iir_accum_x += data_x;
419
                iir_accum_y += data_y;
420
                iir_accum_z += data_z;
421
 
422
                /* transformation enabled? */
423
                if (enable_pointing_coordinate_transform)
424
                {
425
                        /* transform x,y to screen coords */
426
                        MWPOINT transformed = {iir_accum_x, iir_accum_y};
427
                        transformed = DeviceToScreen(transformed);
428
                        /*
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.
433
                         */
434
                        *px = transformed.x >> 2;
435
                        *py = transformed.y >> 2;
436
                }
437
                else
438
                {
439
                        /* return untransformed coords (for calibration) */
440
                        *px = iir_accum_x;
441
                        *py = iir_accum_y;
442
                }
443
                *pb = MWBUTTON_L;
444
#endif
445
                /* return filtered pressure */
446
                *pz = iir_accum_z;
447
 
448
#ifdef TEST
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);
452
#endif
453
                return 2;
454
        }
455
 
456
        /* prime the filter */
457
        iir_accum_x += data_x;
458
        iir_accum_y += data_y;
459
        iir_accum_z += data_z;
460
        iir_count += 1;
461
 
462
        return 0;
463
}
464
#else   /* ifdef YOPY */
465
static int PD_Read(MWCOORD *px, MWCOORD *py, MWCOORD *pz, int *pb)
466
{
467
        /*
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.
476
         *
477
         * Unlike a mouse, this driver returns absolute postions, not deltas.
478
         */
479
 
480
        /* If z is below this value, ignore the data. */
481
        /* const int low_z_limit = 900; EVEREX*/
482
 
483
        /*
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.
489
         */
490
 
491
        int data_x, data_y;
492
 
493
        /* read a data point */
494
        int bytes_read;
495
        int     mou_data;
496
 
497
        bytes_read = read(pd_fd, &mou_data, sizeof(mou_data));
498
        if (bytes_read != sizeof(mou_data)) {
499
                if (errno == EINTR || errno == EAGAIN) {
500
                        return 0;
501
                }
502
                return -1;
503
        }
504
 
505
        data_x = ((MWCOORD)(mou_data & 0x3ff));
506
        data_y = (MWCOORD)(( mou_data>>10 ) & 0x3ff );
507
 
508
        /* transformation enabled? */
509
        if (enable_pointing_coordinate_transform)
510
        {
511
                /* transform x,y to screen coords */
512
                MWPOINT transformed = {data_x, data_y};
513
                transformed = DeviceToScreen(transformed);
514
                /*
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.
519
                 */
520
                *px = transformed.x >> 2;
521
                *py = transformed.y >> 2;
522
        }
523
        else
524
        {
525
                /* return untransformed coords (for calibration) */
526
                *px = data_x;
527
                *py = data_y;
528
        }
529
 
530
        *pz = 0;
531
        *pb = ( mou_data & (1<<31) )? MWBUTTON_L: 0;
532
 
533
        if ( ! *pb )
534
                return 3;
535
        else
536
                return 2;
537
}
538
#endif
539
 
540
MOUSEDEVICE mousedev = {
541
        PD_Open,
542
        PD_Close,
543
        PD_GetButtonInfo,
544
        PD_GetDefaultAccel,
545
        PD_Read,
546
        NULL
547
};
548
 
549
#ifdef TEST
550
int main()
551
{
552
        MWCOORD x, y, z;
553
        int     b;
554
        int result;
555
 
556
        enable_pointing_coordinate_transform = 1;
557
 
558
        DPRINTF("Opening touch panel...\n");
559
 
560
        if((result=PD_Open(0)) < 0)
561
                DPRINTF("Error %d, result %d opening touch-panel\n", errno, result);
562
 
563
        DPRINTF("Reading touch panel...\n");
564
 
565
        while(1) {
566
                result = PD_Read(&x, &y, &z, &b);
567
                if( result > 0) {
568
                        /* DPRINTF("%d,%d,%d,%d,%d\n", result, x, y, z, b); */
569
                }
570
        }
571
}
572
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.