1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8
9 #define DPRINT if(0)print
10 #define THREEBUT 0 /* !=0, enable 3-button emulation (see below) */
11
12 /*
13 * DynaPro touch panel and Maxim MAX192 A/D converter on
14 * York Electronics Centre's BRD/98/024 (Version A) interface,
15 * accessed via mpc8xx SPI interface (see spi.c).
16 *
17 * The highest level of the driver is derived from the ARM/UCB touch panel driver,
18 * simplified because of the differences between the panels.
19 */
20
21 /*
22 * Values determined by interface board
23 */
24 enum {
25 /* MAX192 control words */
26 MeasureX = (1<<7)|(0<<6)|(1<<3)|(1<<2)|3, /* start, channel 0, unipolar, single-ended, external clock */
27 MeasureY = (1<<7)|(1<<6)|(1<<3)|(1<<2)|3, /* start, channel 1, unipolar, single-ended, external clock */
28
29 /* port B bits */
30 ADselect = IBIT(16), /* chip select to MAX192, active low */
31
32 /* port C bits */
33 Xenable = 1<<2, /* PC13: TOUCH_XEN, active low */
34 Yenable = 1<<3, /* PC12: TOUCH_YEN, active low */
35 Touched = 1<<10, /* PC5: contact detect, active low */
36
37 /* interrupt control via port C */
38 TouchIRQ= 2, /* parallel i/o - PC5 */
39 TouchEnable= 1<<TouchIRQ, /* mask for cimr */
40
41 /* other parameters */
42 Nconverge = 10, /* maximum iterations for convergence */
43 MaxDelta = 2, /* acceptable change in X/Y between iterations */
44 };
45
46 /*
47 * ADC interface via SPI (see MAX192 data sheet)
48 * select the ADC
49 * send 8-bit control word and two zero bytes to clock the conversion
50 * receive three data bytes
51 * deselect the ADC and return the result
52 */
53 static int
getcoord(int cw)54 getcoord(int cw)
55 {
56 uchar tbuf[3], rbuf[3];
57 IMM *io;
58 int nr;
59
60 tbuf[0] = cw;
61 tbuf[1] = 0;
62 tbuf[2] = 0;
63 io = ioplock();
64 io->pbdat &= ~ADselect;
65 iopunlock();
66 nr = spioutin(tbuf, sizeof(tbuf), rbuf);
67 io = ioplock();
68 io->pbdat |= ADselect;
69 iopunlock();
70 if(nr != 3)
71 return -1;
72 return ((rbuf[1]<<8)|rbuf[2])>>5;
73 }
74
75 /*
76 * keep reading the a/d until the value stabilises
77 */
78 static int
dejitter(int enable,int cw)79 dejitter(int enable, int cw)
80 {
81 int i, diff, prev, v;
82 IMM *io;
83
84 io = ioplock();
85 io->pcdat &= ~enable; /* active low */
86 iopunlock();
87
88 i = 0;
89 v = getcoord(cw);
90 do{
91 prev = v;
92 v = getcoord(cw);
93 diff = v - prev;
94 if(diff < 0)
95 diff = -diff;
96 }while(diff >= MaxDelta && ++i <= Nconverge);
97
98 io = ioplock();
99 io->pcdat |= enable;
100 iopunlock();
101 return v;
102 }
103
104 static void
adcreset(void)105 adcreset(void)
106 {
107 IMM *io;
108
109 /* select port pins */
110 io = ioplock();
111 io->pcdir &= ~(Xenable|Yenable); /* ensure set to input before changing state */
112 io->pcpar &= ~(Xenable|Yenable);
113 io->pcdat |= Xenable|Yenable;
114 io->pcdir |= Xenable; /* change enable bits to output one at a time to avoid both being low at once (could damage panel) */
115 io->pcdat |= Xenable; /* ensure it's high after making it an output */
116 io->pcdir |= Yenable;
117 io->pcdat |= Yenable; /* ensure it's high after making it an output */
118 io->pcso &= ~(Xenable|Yenable);
119 io->pbdat |= ADselect;
120 io->pbpar &= ~ADselect;
121 io->pbdir |= ADselect;
122 iopunlock();
123 }
124
125 /*
126 * high-level touch panel interface
127 */
128
129 /* to and from fixed point */
130 #define FX(n) ((n)<<16)
131 #define XF(v) ((v)>>16)
132
133 typedef struct Touch Touch;
134
135 struct Touch {
136 Lock;
137 Rendez r;
138 int m[2][3]; /* transformation matrix */
139 int rate;
140 int down;
141 int raw_count;
142 int valid_count;
143 int wake_time;
144 int sleep_time;
145 };
146
147 static Touch touch = {
148 {0},
149 .r {0},
150 .m {{FX(1), 0, 0},{0, FX(1), 0}}, /* default is 1:1 */
151 .rate 20, /* milliseconds */
152 };
153
154 /*
155 * panel-touched state and interrupt
156 */
157
158 static int
touching(void)159 touching(void)
160 {
161 eieio();
162 return (m->iomem->pcdat & Touched) == 0;
163 }
164
165 static int
ispendown(void *)166 ispendown(void*)
167 {
168 return touch.down || touching();
169 }
170
171 static void
touchintr(Ureg *,void *)172 touchintr(Ureg*, void*)
173 {
174 if((m->iomem->pcdat & Touched) == 0){
175 m->iomem->cimr &= ~TouchEnable; /* mask interrupts when reading pen */
176 touch.down = 1;
177 wakeup(&touch.r);
178 }
179 }
180
181 static void
touchenable(void)182 touchenable(void)
183 {
184 IMM *io;
185
186 io = ioplock();
187 io->cimr |= TouchEnable;
188 iopunlock();
189 }
190
191 /*
192 * touchctl commands:
193 * X a b c - set X transformation
194 * Y d e f - set Y transformation
195 * s<delay> - set sample delay in millisec per sample
196 * r<delay> - set read delay in microsec
197 * R<l2nr> - set log2 of number of readings to average
198 */
199
200 enum{
201 Qdir,
202 Qtouchctl,
203 Qtouchstat,
204 Qtouch,
205 };
206
207 Dirtab touchdir[]={
208 ".", {Qdir, 0, QTDIR}, 0, 0555,
209 "touchctl", {Qtouchctl, 0}, 0, 0666,
210 "touchstat", {Qtouchstat, 0}, 0, 0444,
211 "touch", {Qtouch, 0}, 0, 0444,
212 };
213
214 static int
ptmap(int * m,int x,int y)215 ptmap(int *m, int x, int y)
216 {
217 return XF(m[0]*x + m[1]*y + m[2]);
218 }
219
220 /*
221 * read a point from the touch panel;
222 * returns true iff the point is valid, otherwise x, y aren't changed
223 */
224 static int
touchreadxy(int * fx,int * fy)225 touchreadxy(int *fx, int *fy)
226 {
227 int rx, ry;
228
229 if(touching()){
230 rx = dejitter(Xenable, MeasureX);
231 ry = dejitter(Yenable, MeasureY);
232 microdelay(40);
233 if(rx >=0 && ry >= 0){
234 if(0)
235 print("touch %d %d\n", rx, ry);
236 *fx = ptmap(touch.m[0], rx, ry);
237 *fy = ptmap(touch.m[1], rx, ry);
238 touch.raw_count++;
239 return 1;
240 }
241 }
242 return 0;
243 }
244
245 #define timer_start() 0 /* could use TBL if necessary */
246 #define tmr2us(n) 0
247
248 static void
touchproc(void *)249 touchproc(void*)
250 {
251 int b, i, x, y;
252 ulong t1, t2;
253
254 t1 = timer_start();
255 b = 1;
256 for(;;) {
257 //setpri(PriHi);
258 do{
259 touch.down = 0;
260 touch.wake_time += (t2 = timer_start())-t1;
261 touchenable();
262 sleep(&touch.r, ispendown, nil);
263 touch.sleep_time += (t1 = timer_start())-t2;
264 }while(!touchreadxy(&x, &y));
265
266 /* 640x480-specific 3-button emulation hack: */
267 if(THREEBUT){
268 if(y > 481) {
269 b = ((639-x) >> 7);
270 continue;
271 } else if(y < -2) {
272 b = (x >> 7)+3;
273 continue;
274 }
275 }
276
277 DPRINT("#%d %d", x, y);
278 mousetrack(b, x, y, 0);
279 setpri(PriNormal);
280 while(touching()) {
281 for(i=0; i<3; i++)
282 if(touchreadxy(&x, &y)) {
283 DPRINT("*%d %d", x, y);
284 mousetrack(b, x, y, 0);
285 break;
286 }
287 touch.wake_time += (t2 = timer_start())-t1;
288 tsleep(&touch.r, return0, nil, touch.rate);
289 touch.sleep_time += (t1 = timer_start())-t2;
290 }
291 mousetrack(0, x, y, 0);
292 b = 1; /* go back to just button one for next press */
293 }
294 }
295
296 static void
touchreset(void)297 touchreset(void)
298 {
299 IMM *io;
300
301 spireset();
302 adcreset();
303 intrenable(VectorCPIC+TouchIRQ, touchintr, &touch, BUSUNKNOWN, "touch");
304
305 /* set i/o pin to interrupt when panel touched */
306 io = ioplock();
307 io->pcdat &= ~Touched;
308 io->pcpar &= ~Touched;
309 io->pcdir &= ~Touched;
310 io->pcso &= ~Touched;
311 io->pcint |= Touched; /* high-to-low trigger */
312 io->cimr &= ~TouchEnable; /* touchproc will enable when ready */
313 iopunlock();
314 }
315
316 static void
touchinit(void)317 touchinit(void)
318 {
319 static int done;
320
321 if(!done){
322 done = 1;
323 kproc( "touchscreen", touchproc, nil, 0);
324 }
325 }
326
327 static Chan*
touchattach(char * spec)328 touchattach(char* spec)
329 {
330 return devattach('T', spec);
331 }
332
333 static Walkqid*
touchwalk(Chan * c,Chan * nc,char ** name,int nname)334 touchwalk(Chan* c, Chan *nc, char **name, int nname)
335 {
336 return devwalk(c, nc, name, nname, touchdir, nelem(touchdir), devgen);
337 }
338
339 static int
touchstat(Chan * c,uchar * dp,int n)340 touchstat(Chan* c, uchar* dp, int n)
341 {
342 return devstat(c, dp, n, touchdir, nelem(touchdir), devgen);
343 }
344
345 static Chan*
touchopen(Chan * c,int omode)346 touchopen(Chan* c, int omode)
347 {
348 omode = openmode(omode);
349 switch((ulong)c->qid.path){
350 case Qtouchctl:
351 case Qtouchstat:
352 if(!iseve())
353 error(Eperm);
354 break;
355 }
356 return devopen(c, omode, touchdir, nelem(touchdir), devgen);
357 }
358
359 static void
touchclose(Chan *)360 touchclose(Chan*)
361 {
362 }
363
364 static long
touchread(Chan * c,void * buf,long n,vlong offset)365 touchread(Chan* c, void* buf, long n, vlong offset)
366 {
367 char *tmp;
368 int x, y;
369
370 if(c->qid.type & QTDIR)
371 return devdirread(c, buf, n, touchdir, nelem(touchdir), devgen);
372
373 tmp = malloc(READSTR);
374 if(waserror()){
375 free(tmp);
376 nexterror();
377 }
378 switch((ulong)c->qid.path){
379 case Qtouch:
380 if(!touchreadxy(&x, &y))
381 x = y = -1;
382 snprint(tmp, READSTR, "%d %d", x, y);
383 break;
384 case Qtouchctl:
385 snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",
386 touch.rate, 0, 1,
387 touch.m[0][0], touch.m[0][1], touch.m[0][2],
388 touch.m[1][0], touch.m[1][1], touch.m[1][2]);
389 break;
390 case Qtouchstat:
391 snprint(tmp, READSTR, "%d %d\n%d %d\n",
392 touch.raw_count, touch.valid_count, tmr2us(touch.sleep_time), tmr2us(touch.wake_time));
393 touch.raw_count = 0;
394 touch.valid_count = 0;
395 touch.sleep_time = 0;
396 touch.wake_time = 0;
397 break;
398 default:
399 error(Ebadarg);
400 return 0;
401 }
402 n = readstr(offset, buf, n, tmp);
403 poperror();
404 free(tmp);
405 return n;
406 }
407
408 static void
dotouchwrite(Chan * c,char * buf)409 dotouchwrite(Chan *c, char *buf)
410 {
411 char *field[8];
412 int nf, cmd, pn, m[3], n;
413
414 nf = getfields(buf, field, nelem(field), 1, " \t\n");
415 if(nf <= 0)
416 return;
417 switch((ulong)c->qid.path){
418 case Qtouchctl:
419 cmd = *(field[0])++;
420 pn = *field[0] == 0;
421 switch(cmd) {
422 case 's':
423 n = strtol(field[pn], 0, 0);
424 if(n <= 0)
425 error(Ebadarg);
426 touch.rate = n;
427 break;
428 case 'r':
429 /* touch read delay */
430 break;
431 case 'X':
432 case 'Y':
433 if(nf < pn+2)
434 error(Ebadarg);
435 m[0] = strtol(field[pn], 0, 0);
436 m[1] = strtol(field[pn+1], 0, 0);
437 m[2] = strtol(field[pn+2], 0, 0);
438 memmove(touch.m[cmd=='Y'], m, sizeof(touch.m[0]));
439 break;
440 case 'c':
441 case 'C':
442 case 'v':
443 case 't':
444 case 'e':
445 /* not used */
446 /* break; */
447 default:
448 error(Ebadarg);
449 }
450 break;
451 default:
452 error(Ebadarg);
453 return;
454 }
455 }
456
457 static long
touchwrite(Chan * c,void * vp,long n,vlong)458 touchwrite(Chan* c, void* vp, long n, vlong)
459 {
460 char buf[64];
461 char *cp, *a;
462 int n0 = n;
463 int bn;
464
465 a = vp;
466 while(n) {
467 bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;
468 n -= bn;
469 bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;
470 memmove(buf, a, bn);
471 buf[bn] = '\0';
472 a = cp;
473 dotouchwrite(c, buf);
474 }
475 return n0-n;
476 }
477
478 Dev touchdevtab = {
479 'T',
480 "touch",
481
482 touchreset,
483 touchinit,
484 devshutdown,
485 touchattach,
486 touchwalk,
487 touchstat,
488 touchopen,
489 devcreate,
490 touchclose,
491 touchread,
492 devbread,
493 touchwrite,
494 devbwrite,
495 devremove,
496 devwstat,
497 };
498