]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/services/gfx/mw/v2_0/src/drivers/mou_harrier.c
Initial revision
[karo-tx-redboot.git] / packages / services / gfx / mw / v2_0 / src / drivers / mou_harrier.c
1 /*
2  * Microwindows touch screen driver for NEC Harrier Demo Board.
3  *
4  * Copyright (c) 2000 Century Software Embedded Technologies
5  *
6  * Requires /dev/tpanel kernel device driver for the VRC4173 chip 
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <math.h>
14 #include <sys/ioctl.h>
15 #include <linux/tpanel.h>
16 #include "device.h"
17 #include "mou_tp.h"
18
19 /* Very basic handling for the touch panel */
20 /* Mostly borrowed from mou_ipaq.c which I believe was mostly */
21 /* borrowed from mou_tp.c */
22
23 /* Define this if you want to use the filter instead of the average method */
24 /* #define USE_FILTER */
25
26 /* Defines used throughout */
27 #define TP_STATUS_HARDDATALOST 0x1000
28 #define TP_STATUS_SOFTDATALOST 0x2000
29 #define TP_STATUS_PENCONTACT   0x4000
30 #define TP_STATUS_DATAVALID    0x8000
31
32 /* Fix these when we know the right size */
33
34 #define TP_MIN_X_SIZE          291
35 #define TP_MIN_Y_SIZE          355
36
37 #define TP_MAX_X_SIZE          3839
38 #define TP_MAX_Y_SIZE          3711
39
40 #define DATA_STATUS  0
41 #define DATA_YPLUS   1
42 #define DATA_YMINUS  2
43 #define DATA_XPLUS   3
44 #define DATA_XMINUS  4
45 #define DATA_Z       5
46
47
48 #ifdef USE_FILTER
49 #define MOU_SAMPLE_RATE   1
50 #else
51 #define MOU_SAMPLE_RATE   10
52 #endif
53
54 #define MOU_READ_INTERVAL 5000
55
56   /* Data format (from kernel driver): */
57   /* unsigned short status */
58   /* unsigned short x+ (raw) */
59   /* unsigned short x- (raw) */
60   /* unsigned short y+ (raw) */
61   /* unsigned short y- (raw) */
62   /* unsigned short z (presssure, raw) */
63
64 static int pd_fd = -1;
65 int enable_pointing_coordinate_transform = 1;
66
67 static TRANSFORMATION_COEFFICIENTS tc;
68
69 #ifndef TEST
70 extern SCREENDEVICE scrdev;
71 #endif
72
73 #ifdef TEST
74 #undef EPRINTF
75 #undef DPRINTF
76
77 #define EPRINTF printf
78 #define DPRINTF printf
79 #endif
80  
81 int GetPointerCalibrationData(void)
82 {
83         /*
84          * Read the calibration data from the calibration file.
85          * Calibration file format is seven coefficients separated by spaces.
86          */
87
88         /* Get pointer calibration data from this file */
89         const char cal_filename[] = "/etc/pointercal";
90
91         int items;
92
93         FILE* f = fopen(cal_filename, "r");
94         if ( f == NULL )
95         {
96                 EPRINTF("Error %d opening pointer calibration file %s.\n",
97                         errno, cal_filename);
98                 EPRINTF("Please type \"/usr/bin/tpcal > %s\" to calibrate\n",
99                         cal_filename);
100                 return -1;
101         }
102
103         items = fscanf(f, "%d %d %d %d %d %d %d",
104                 &tc.a, &tc.b, &tc.c, &tc.d, &tc.e, &tc.f, &tc.s);
105         if ( items != 7 )
106         {
107                 EPRINTF("Improperly formatted pointer calibration file %s.\n",
108                         cal_filename);
109                 return -1;
110         }
111
112 #if TEST
113                 EPRINTF("a=%d b=%d c=%d d=%d e=%d f=%d s=%d\n",
114                         tc.a, tc.b, tc.c, tc.d, tc.e, tc.f, tc.s);
115 #endif
116
117         return 0;
118 }
119
120 inline MWPOINT DeviceToScreen(MWPOINT p)
121 {
122         /*
123          * Transform device coordinates to screen coordinates.
124          * Take a point p in device coordinates and return the corresponding
125          * point in screen coodinates.
126          * This can scale, translate, rotate and/or skew, based on the
127          * coefficients calculated above based on the list of screen
128          * vs. device coordinates.
129          */
130
131         static MWPOINT prev;
132         /* set slop at 3/4 pixel */
133         const short slop = TRANSFORMATION_UNITS_PER_PIXEL * 3 / 4;
134         MWPOINT new, out;
135
136         /* transform */
137         new.x = (tc.a * p.x + tc.b * p.y + tc.c) / tc.s;
138         new.y = (tc.d * p.x + tc.e * p.y + tc.f) / tc.s;
139
140         /* hysteresis (thanks to John Siau) */
141         if ( abs(new.x - prev.x) >= slop )
142                 out.x = (new.x | 0x3) ^ 0x3;
143         else
144                 out.x = prev.x;
145
146         if ( abs(new.y - prev.y) >= slop )
147                 out.y = (new.y | 0x3) ^ 0x3;
148         else
149                 out.y = prev.y;
150
151         prev = out;
152
153         return out;
154 }
155
156 static int PD_Open(MOUSEDEVICE *pmd)
157 {
158   struct scanparam s;
159   int settle_upper_limit;
160   int result;
161   
162   /* Open the device */
163   pd_fd = open("/dev/tpanel", O_NONBLOCK);
164   
165   if (pd_fd < 0)
166     {
167       EPRINTF("Error %d opening touch panel\n", errno);
168       return -1;
169     }
170
171   s.interval = MOU_READ_INTERVAL;
172
173   /*
174    * Upper limit on settle time is approximately (scan_interval / 5) - 60
175    * (5 conversions and some fixed overhead)
176    * The opmtimal value is the lowest that doesn't cause significant
177    * distortion.
178    */
179
180   settle_upper_limit = (s.interval / 5) - 60;
181   s.settletime = settle_upper_limit * 50 / 100;
182   result = ioctl(pd_fd, TPSETSCANPARM, &s);
183
184   if ( result < 0 )
185     EPRINTF("Error %d, result %d setting scan parameters.\n",
186             result, errno);
187   
188   if (enable_pointing_coordinate_transform)
189     { 
190       if (GetPointerCalibrationData() < 0)
191         {
192           close(pd_fd);
193           return -1;
194         }
195     }
196   
197   /* We choose not to hide the cursor for now, others may want to */
198   
199 #ifdef NOTUSED
200 #ifndef TEST
201     /* Hide the cursor */
202   GdHideCursor(&scrdev);
203 #endif
204 #endif
205   
206   return(pd_fd);
207 }
208
209 static void PD_Close(void)
210 {
211         /* Close the touch panel device. */
212         if (pd_fd >= 0)
213                 close(pd_fd);
214         pd_fd = -1;
215 }
216
217 static int PD_GetButtonInfo(void)
218 {
219         /* get "mouse" buttons supported */
220         return MWBUTTON_L;
221 }
222
223 static void PD_GetDefaultAccel(int *pscale,int *pthresh)
224 {
225         /*
226          * Get default mouse acceleration settings
227          * This doesn't make sense for a touch panel.
228          * Just return something inconspicuous for now.
229          */
230         *pscale = 3;
231         *pthresh = 5;
232 }
233
234 #define MAX_DEVICE_READS 10
235 static int read_tp(unsigned short *x, unsigned short *y, 
236                    unsigned short *z, int *b, unsigned short *status, unsigned char block)
237 {
238   unsigned char read_count = 0;
239   unsigned short tempx, tempy;
240   int bytes_read;
241   unsigned short data[6];
242   
243   /* Uh, oh -- The driver is slow and fat, so we get lots of EAGAINS between   */
244   /* reads.  Thats never good.  So we loop here for some count before we bail  */
245
246   while(read_count < MAX_DEVICE_READS)
247     {
248       bytes_read = read(pd_fd, data, sizeof(data));
249   
250       if (bytes_read != sizeof(data))
251         {
252           if (errno != EAGAIN)
253             {
254               EPRINTF("Error reading touch panel.  errno = %d\n", errno);
255               return(errno);
256             }
257
258           if (block)
259             {
260               if (read_count++ == MAX_DEVICE_READS)
261                 return(EAGAIN);
262               else
263                 usleep(MOU_READ_INTERVAL / MAX_DEVICE_READS);
264             }
265           else
266             return(EAGAIN);
267         }
268       else
269         break;
270     }
271
272   tempx = data[DATA_XPLUS];
273   tempy = data[DATA_YPLUS];
274
275   /* Sanity check */
276   /* This is based on measured values.  Occassionally, we get a bad read from the board */
277   /* This is to ensure that really wacked out reads don't get through.                  */
278
279   if ((data[DATA_STATUS] & TP_STATUS_DATAVALID) == TP_STATUS_DATAVALID)
280     {
281        if (enable_pointing_coordinate_transform)
282          {
283            if (tempx < TP_MIN_X_SIZE || tempx > TP_MAX_X_SIZE)
284              {
285 #ifdef TEST
286                EPRINTF("Got an out of range X value.  X=%d,Y=%d,B=%d\n",
287                        tempx, tempy, 
288                        ((data[DATA_STATUS] & TP_STATUS_PENCONTACT) ? MWBUTTON_L : 0));
289 #endif
290                return(EAGAIN);
291              }
292            
293            if (tempy < TP_MIN_Y_SIZE || tempy > TP_MAX_Y_SIZE)
294              {
295 #ifdef TEST
296                EPRINTF("Got an out of range Y value.  X=%d,Y=%d,B=%d\n",
297                        tempx, tempy, 
298                        ((data[DATA_STATUS] & TP_STATUS_PENCONTACT) ? MWBUTTON_L : 0));
299 #endif
300                return(EAGAIN);
301              }
302          }
303
304        *x = tempx;
305        *y = tempy;
306        *z = data[DATA_Z];
307     }
308   else
309     { 
310       *x = 0;
311       *y = 0;
312       *z = 0;
313     }
314
315   *b = ((data[DATA_STATUS] & TP_STATUS_PENCONTACT) ? MWBUTTON_L : 0);  
316   *status = data[DATA_STATUS];
317
318   return(0);
319 }
320
321
322 static int PD_Read(MWCOORD *px, MWCOORD *py, MWCOORD *pz, int *pb)
323 {
324 #ifdef USE_FILTER
325   /* Filter stuff borrowed from mou_tp.c */
326   const int iir_shift_bits = 3;
327   const int iir_sample_depth = (1 << iir_shift_bits);
328   
329   static int iir_accum_x = 0;
330   static int iir_accum_y = 0;
331   static int iir_accum_z = 0;
332   static int iir_count = 0;
333 #else
334   double cx, cy, cz;
335 #endif
336
337   /* Other local variables */
338   MWPOINT transformed;
339   int err = 0;
340   unsigned short samples = 0;
341   unsigned short xpos = 0;
342   unsigned short ypos = 0;
343   unsigned short zpos = 0;
344   unsigned short status = 0;
345
346   *pb = 0;
347   *px = 0;
348   *py = 0;
349   *pz = 0;
350
351 #ifndef USE_FILTER
352   cx = 0;
353   cy = 0;
354   cz = 0;
355 #endif
356
357   if ((err = read_tp(&xpos, &ypos, &zpos, pb, &status, 0)))
358     {
359       if (err == EAGAIN)
360         return(0);
361       else
362         return(1);
363     }
364
365   /* Check the status of the button */
366
367   if ( (status & TP_STATUS_DATAVALID) != TP_STATUS_DATAVALID)
368     {
369       if (*pb)
370         return(0);
371       else
372         goto button_up;
373     }
374
375   while((status & TP_STATUS_DATAVALID) == TP_STATUS_DATAVALID)
376     {
377       int tempb = 0;
378
379       err = read_tp(&xpos, &ypos, &zpos, &tempb, &status, 1);
380  
381       if (err == EAGAIN)
382         {
383           if (!samples)
384             continue; /* We need at least one reading! */
385           else
386             break; /* The device continues to not respond.  Bail */
387         }
388       else if (err)
389         return(-1);
390
391       /* If the data is invalid and the button is down, then bail */
392       /* Otherwise, record the button data */
393      
394       if ( (status & TP_STATUS_DATAVALID) != TP_STATUS_DATAVALID)
395         {
396           if (tempb)
397             return(0); /* Button is down, but data is invalid */      
398           else 
399             {
400               *pb = tempb; /* Record button up */
401               goto button_up;
402             }
403         }
404
405 #ifdef USE_FILTER
406
407       /* Run the newly aquired data through a filter */
408       /* is filter ready? */
409       if ( iir_count == iir_sample_depth )
410         {
411           /* make room for new sample */
412           iir_accum_x -= iir_accum_x >> iir_shift_bits;
413           iir_accum_y -= iir_accum_y >> iir_shift_bits;
414           iir_accum_z -= iir_accum_z >> iir_shift_bits;
415           
416           /* feed new sample to filter */
417           iir_accum_x += xpos;
418           iir_accum_y += ypos;
419           iir_accum_z += zpos;
420         }
421       else
422         {
423           iir_accum_x += xpos;
424           iir_accum_y += ypos;
425           iir_accum_z += zpos;
426           iir_count += 1;
427         }
428
429 #else
430       cx += xpos;
431       cy += ypos;
432       cz += zpos;
433 #endif
434
435       samples++;
436       
437       /* Enough samples?? */
438       if (samples >= MOU_SAMPLE_RATE)
439         break;
440     }
441
442   if (!samples)
443     return(0);
444
445 #ifdef USE_FILTER  
446   /* We're not done gathering samples yet */
447   if (iir_count < iir_sample_depth)
448     return(0);
449
450   if (enable_pointing_coordinate_transform)
451     {     
452       /* transform x,y to screen coords */
453       transformed.x = iir_accum_x;
454       transformed.y = iir_accum_y;
455       transformed = DeviceToScreen(transformed);
456       
457       *px = transformed.x >> 2;
458       *py = transformed.y >> 2;
459     }
460   else
461     {
462       *px = (MWCOORD) abs(iir_accum_x);
463       *py = (MWCOORD) abs(iir_accum_y);
464     }
465 #else
466   
467   if (enable_pointing_coordinate_transform)
468     {
469       transformed.x = (cx / samples);
470       transformed.y = (cy / samples);
471       
472       transformed = DeviceToScreen(transformed);
473       
474       *px = (MWCOORD) transformed.x >> 2;
475       *py = (MWCOORD) transformed.y >> 2;
476     }
477   else
478     {
479       *px = (MWCOORD) abs(cx / samples);
480       *py = (MWCOORD) abs(cy / samples);
481     }
482 #endif
483   
484  button_up:
485   if (! *pb)
486     {
487 #ifdef USE_FILTER 
488      /* reset the filter */
489       iir_count = 0;
490       iir_accum_x = 0;
491       iir_accum_y = 0;
492       iir_accum_z = 0;
493 #endif
494       return(3);
495     }
496   else
497     return(2); /* XYZ and button data */
498
499 }
500
501 #ifndef TEST
502 MOUSEDEVICE mousedev = {
503         PD_Open,
504         PD_Close,
505         PD_GetButtonInfo,
506         PD_GetDefaultAccel,
507         PD_Read,
508         NULL
509 };
510 #endif
511
512 #ifdef TEST
513 int main(int argc, char ** v)
514 {
515         int x, y, z;
516
517         int     b;
518         int result;
519         
520         DPRINTF("Opening touch panel...\n");
521
522         if((result=PD_Open(0)) < 0)
523           {
524             
525             DPRINTF("Error %d, result %d opening touch-panel\n", errno, result);
526             exit(0);
527           }
528
529         DPRINTF("Reading touch panel...\n");
530
531         while(1) 
532           {
533             result = PD_Read(&x, &y, &z, &b);
534             
535             if( result > 0) 
536               {
537                 DPRINTF("(%d,%d,%d) b = %d\n",x, y, z, b);              
538               }
539           }
540 }
541 #endif
542