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