xref: /inferno-os/os/ipaq1110/devipaq.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 /*
2  *  iPAQ H3650 touch screen and other devices
3  *
4  * Inferno driver derived from sketchy documentation and
5  * information gleaned from linux/char/h3650_ts.c
6  * by Charles Flynn.
7  *
8  * Copyright © 2000,2001 Vita Nuova Holdings Limited.  All rights reserved.
9  */
10 #include	"u.h"
11 #include	"../port/lib.h"
12 #include	"mem.h"
13 #include	"dat.h"
14 #include	"fns.h"
15 #include	"io.h"
16 #include	"../port/error.h"
17 
18 #include "keyboard.h"
19 #include <kernel.h>
20 
21 #include <draw.h>
22 #include <memdraw.h>
23 #include "screen.h"
24 
25 #define	DEBUG	0
26 
27 /*
28  * packet format
29  *
30  * SOF (0x02)
31  * (id<<4) | len	byte length
32  * data[len] bytes
33  * chk	checksum mod 256 excluding SOF
34  */
35 
36 enum {
37 	Csof = 0x02,
38 	Ceof = 0x03,
39 	Hdrlen = 3,
40 
41 	/* opcodes */
42 
43 	Oversion = 0,
44 	Okeys = 2,
45 	Otouch = 3,
46 	Ordeeprom = 4,
47 	Owreeprom = 5,
48 	Othermal = 6,
49 	Oled = 8,
50 	Obattery = 9,
51 	Ospiread = 11,
52 	Ospiwrite = 12,
53 	Obacklight = 13,
54 	Oextstatus = 0xA1,
55  };
56 
57 enum {
58 	Powerbit = 0,	/* GPIO bit for power on/off key */
59 };
60 
61 enum{
62 	Qdir,
63 	Qctl,
64 	Qtouchctl,
65 	Qbattery,
66 	Qversion,
67 };
68 
69 static
70 Dirtab ipaqtab[]={
71 	".",	{Qdir, 0, QTDIR},	0,	0555,
72 	"ipaqctl",		{Qctl},		0,	0600,
73 	"battery",		{Qbattery},	0,	0444,
74 	"version",		{Qversion},	0,	0444,
75 	"touchctl",	{Qtouchctl},	0,	0644,
76 };
77 
78 static struct {
79 	QLock;
80 	Chan*	c;
81 
82 	Lock	rl;	/* protect cmd, reply */
83 	int	cmd;
84 	Block*	reply;
85 	Rendez	r;
86 } atmel;
87 
88 /* to and from fixed point */
89 #define	FX(a,b)	(((a)<<16)/(b))
90 #define	XF(v)		((v)>>16)
91 
92 static struct {
93 	Lock;
94 	int	rate;
95 	int	m[2][3];	/* transformation matrix */
96 	Point	avg;
97 	Point	diff;
98 	Point	pts[4];
99 	int	n;	/* number of points in pts */
100 	int	p;	/* current index in pts */
101 	int	down;
102 	int	nout;
103 } touch = {
104 	{0},
105 	.m {{-FX(1,3), 0, FX(346,1)},{0, -FX(1,4), FX(256, 1)}},
106 };
107 
108 /*
109  * map rocker positions to same codes as plan 9
110  */
111 static	Rune	rockermap[2][4] ={
112 	{Right, Down, Up, Left},	/* landscape */
113 	{Up, Right, Left, Down},	/* portrait */
114 };
115 
116 static	Rendez	powerevent;
117 
118 static	void	cmdack(int, void*, int);
119 static	int	cmdio(int, void*, int, void*, int);
120 static	void	ipaqreadproc(void*);
121 static	void	powerwaitproc(void*);
122 static	Block*	rdevent(Block**);
123 static	long	touchctl(char*, long);
124 static	void	touched(Block*, int);
125 static	int	wrcmd(int, void*, int, void*, int);
126 static	char*	acstatus(int);
127 static	char*	batstatus(int);
128 static	void	powerintr(Ureg*, void*);
129 
130 static void
ipaqreset(void)131 ipaqreset(void)
132 {
133 	intrenable(Powerbit, powerintr, nil, BusGPIOfalling, "power off");
134 }
135 
136 static void
ipaqinit(void)137 ipaqinit(void)
138 {
139 	kproc("powerwait", powerwaitproc, nil, 0);
140 }
141 
142 static Chan*
ipaqattach(char * spec)143 ipaqattach(char* spec)
144 {
145 	int fd;
146 
147 	qlock(&atmel);
148 	if(waserror()){
149 		qunlock(&atmel);
150 		nexterror();
151 	}
152 	if(atmel.c == nil){
153 		fd = kopen("#t/eia1ctl", ORDWR);
154 		if(fd < 0)
155 			error(up->env->errstr);
156 		kwrite(fd, "b115200", 7);	/* it's already pn, l8 */
157 		kclose(fd);
158 		fd = kopen("#t/eia1", ORDWR);
159 		if(fd < 0)
160 			error(up->env->errstr);
161 		atmel.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
162 		kclose(fd);
163 		atmel.cmd = -1;
164 		kproc("ipaqread", ipaqreadproc, nil, 0);
165 	}
166 	poperror();
167 	qunlock(&atmel);
168 	return devattach('T', spec);
169 }
170 
171 static Walkqid*
ipaqwalk(Chan * c,Chan * nc,char ** name,int nname)172 ipaqwalk(Chan *c, Chan *nc, char **name, int nname)
173 {
174 	return devwalk(c, nc, name, nname, ipaqtab, nelem(ipaqtab), devgen);
175 }
176 
177 static int
ipaqstat(Chan * c,uchar * db,int n)178 ipaqstat(Chan* c, uchar *db, int n)
179 {
180 	return devstat(c, db, n, ipaqtab, nelem(ipaqtab), devgen);
181 }
182 
183 static Chan*
ipaqopen(Chan * c,int omode)184 ipaqopen(Chan* c, int omode)
185 {
186 	return devopen(c, omode, ipaqtab, nelem(ipaqtab), devgen);
187 }
188 
189 static void
ipaqclose(Chan *)190 ipaqclose(Chan*)
191 {
192 }
193 
194 static long
ipaqread(Chan * c,void * a,long n,vlong offset)195 ipaqread(Chan* c, void* a, long n, vlong offset)
196 {
197 	char *tmp, buf[64];
198 	uchar reply[12];
199 	int v, p, l;
200 
201 	switch((ulong)c->qid.path){
202 	case Qdir:
203 		return devdirread(c, a, n, ipaqtab, nelem(ipaqtab), devgen);
204 	case Qtouchctl:
205 		tmp = malloc(READSTR);
206 		if(waserror()){
207 			free(tmp);
208 			nexterror();
209 		}
210 		snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",
211 			1000, 0, 1,
212 			touch.m[0][0], touch.m[0][1], touch.m[0][2],
213 			touch.m[1][0], touch.m[1][1], touch.m[1][2]);
214 		n = readstr(offset, a, n, tmp);
215 		poperror();
216 		free(tmp);
217 		break;
218 	case Qbattery:
219 		cmdio(Obattery, reply, 0, reply, sizeof(reply));
220 		tmp = malloc(READSTR);
221 		if(waserror()){
222 			free(tmp);
223 			nexterror();
224 		}
225 		v = (reply[4]<<8)|reply[3];
226 		p = 425*v/1000 - 298;
227 		snprint(tmp, READSTR, "voltage: %d %dmV %d%% %d\nac: %s\nstatus: %d %s\nchem: %d\n",
228 			v, 1000*v/228, p, 300*p/100, acstatus(reply[1]), reply[5], batstatus(reply[5]), reply[2]);
229 		n = readstr(offset, a, n, tmp);
230 		poperror();
231 		free(tmp);
232 		break;
233 	case Qversion:
234 		l = cmdio(Oversion, reply, 0, reply, sizeof(reply));
235 		if(l > 4){
236 			l--;
237 			memmove(buf, reply+1, 4);
238 			if(l > 8){
239 				buf[4] = ' ';
240 				memmove(buf+5, reply+5, 4);	/* pack version */
241 				sprint(buf+9, " %.2x\n", reply[9]);	/* ``boot type'' */
242 			}else{
243 				buf[4] = '\n';
244 				buf[5] = 0;
245 			}
246 			return readstr(offset, a, n, buf);
247 		}
248 		n=0;
249 		break;
250 	default:
251 		n=0;
252 		break;
253 	}
254 	return n;
255 }
256 
257 static long
ipaqwrite(Chan * c,void * a,long n,vlong)258 ipaqwrite(Chan* c, void* a, long n, vlong)
259 {
260 	char cmd[64], op[32], *fields[6];
261 	int nf;
262 
263 	switch((ulong)c->qid.path){
264 	case Qctl:
265 		if(n >= sizeof(cmd)-1)
266 			n = sizeof(cmd)-1;
267 		memmove(cmd, a, n);
268 		cmd[n] = 0;
269 		nf = getfields(cmd, fields, nelem(fields), 1, " \t\n");
270 		if(nf <= 0)
271 			error(Ebadarg);
272 		if(nf >= 4 && strcmp(fields[0], "light") == 0){
273 			op[0] = atoi(fields[1]);	/* mode */
274 			op[1] = atoi(fields[2]);	/* power */
275 			op[2] = atoi(fields[3]);	/* brightness */
276 			cmdack(Obacklight, op, 3);
277 		}else if(nf >= 5 && strcmp(fields[0], "led") == 0){
278 			op[0] = atoi(fields[1]);
279 			op[1] = atoi(fields[2]);
280 			op[2] = atoi(fields[3]);
281 			op[3] = atoi(fields[4]);
282 			cmdack(Oled, op, 4);
283 		}else if(strcmp(fields[0], "suspend") == 0){
284 			/* let the kproc do it */
285 			wakeup(&powerevent);
286 		}else
287 			error(Ebadarg);
288 		break;
289 	case Qtouchctl:
290 		return touchctl(a, n);
291 	default:
292 		error(Ebadusefd);
293 	}
294 	return n;
295 }
296 
297 static void
powerintr(Ureg *,void *)298 powerintr(Ureg*, void*)
299 {
300 	wakeup(&powerevent);
301 }
302 
303 static void
cmdack(int id,void * a,int n)304 cmdack(int id, void *a, int n)
305 {
306 	uchar reply[16];
307 
308 	cmdio(id, a, n, reply, sizeof(reply));
309 }
310 
311 static int
cmdio(int id,void * a,int n,void * reply,int lim)312 cmdio(int id, void *a, int n, void *reply, int lim)
313 {
314 	qlock(&atmel);
315 	if(waserror()){
316 		qunlock(&atmel);
317 		nexterror();
318 	}
319 	n = wrcmd(id, a, n, reply, lim);
320 	poperror();
321 	qunlock(&atmel);
322 	return n;
323 }
324 
325 static int
havereply(void *)326 havereply(void*)
327 {
328 	return atmel.reply != nil;
329 }
330 
331 static int
wrcmd(int id,void * a,int n,void * b,int lim)332 wrcmd(int id, void *a, int n, void *b, int lim)
333 {
334 	uchar buf[32];
335 	int i, sum;
336 	Block *e;
337 
338 	if(n >= 16)
339 		error(Eio);
340 	lock(&atmel.rl);
341 	atmel.cmd = id;
342 	unlock(&atmel.rl);
343 	buf[0] = Csof;
344 	buf[1] = (id<<4) | (n&0xF);
345 	if(n)
346 		memmove(buf+2, a, n);
347 	sum = 0;
348 	for(i=1; i<n+2; i++)
349 		sum += buf[i];
350 	buf[i++] = sum;
351 	if(0){
352 		iprint("msg=");
353 		for(sum=0; sum<i; sum++)
354 			iprint(" %2.2ux", buf[sum]);
355 		iprint("\n");
356 	}
357 	if(kchanio(atmel.c, buf, i, OWRITE) != i)
358 		error(Eio);
359 	tsleep(&atmel.r, havereply, nil, 500);
360 	lock(&atmel.rl);
361 	e = atmel.reply;
362 	atmel.reply = nil;
363 	atmel.cmd = -1;
364 	unlock(&atmel.rl);
365 	if(e == nil){
366 		print("ipaq: no reply\n");
367 		error(Eio);
368 	}
369 	if(waserror()){
370 		freeb(e);
371 		nexterror();
372 	}
373 	if(e->rp[0] != id){
374 		print("ipaq: rdreply: mismatched reply %d :: %d\n", id, e->rp[0]);
375 		error(Eio);
376 	}
377 	n = BLEN(e);
378 	if(n < lim)
379 		lim = n;
380 	memmove(b, e->rp, lim);
381 	poperror();
382 	freeb(e);
383 	return lim;
384 }
385 
386 static void
ipaqreadproc(void *)387 ipaqreadproc(void*)
388 {
389 	Block *e, *b, *partial;
390 	int c, mousemod;
391 
392 	while(waserror())
393 		print("ipaqread: %r\n");
394 	partial = nil;
395 	mousemod = 0;
396 	for(;;){
397 		e = rdevent(&partial);
398 		if(e == nil){
399 			print("ipaqread: rdevent: %r\n");
400 			continue;
401 		}
402 		switch(e->rp[0]){
403 		case Otouch:
404 			touched(e, mousemod);
405 			freeb(e);
406 			break;
407 		case Okeys:
408 			//print("key %2.2ux\n", e->rp[1]);
409 			c = e->rp[1] & 0xF;
410 			if(c >= 6 && c < 10){	/* rocker */
411 				if((e->rp[1] & 0x80) == 0){
412 					kbdrepeat(0);
413 					kbdputc(kbdq, rockermap[conf.portrait&1][c-6]);
414 				}else
415 					kbdrepeat(0);
416 			}else{
417 				/* TO DO: change tkmouse and mousetrack to allow extra buttons */
418 				if(--c == 0)
419 					c = 5;
420 				if(e->rp[1] & 0x80)
421 					mousemod &= ~(1<<c);
422 				else
423 					mousemod |= 1<<c;
424 			}
425 			freeb(e);
426 			break;
427 		default:
428 			lock(&atmel.rl);
429 			if(atmel.cmd == e->rp[0]){
430 				b = atmel.reply;
431 				atmel.reply = e;
432 				unlock(&atmel.rl);
433 				wakeup(&atmel.r);
434 				if(b != nil)
435 					freeb(b);
436 			}else{
437 				unlock(&atmel.rl);
438 				print("ipaqread: discard op %d\n", e->rp[0]);
439 				freeb(e);
440 			}
441 		}
442 	}
443 }
444 
445 static Block *
rdevent(Block ** bp)446 rdevent(Block **bp)
447 {
448 	Block *b, *e;
449 	int s, c, len, csum;
450 	enum {Ssof=16, Sid, Ssum};
451 
452 	s = Ssof;
453 	csum = 0;
454 	len = 0;
455 	e = nil;
456 	if(waserror()){
457 		if(e != nil)
458 			freeb(e);
459 		nexterror();
460 	}
461 	for(;;){
462 		b = *bp;
463 		*bp = nil;
464 		if(b == nil){
465 			b = devtab[atmel.c->type]->bread(atmel.c, 128, 0);
466 			if(b == nil)
467 				error(Eio);
468 			if(DEBUG)
469 				iprint("r: %ld\n", BLEN(b));
470 		}
471 		while(b->rp < b->wp){
472 			c = *b->rp++;
473 			switch(s){
474 			case Ssof:
475 				if(c == Csof)
476 					s = Sid;
477 				else if(1)
478 					iprint("!sof: %2.2ux %d\n", c, s);
479 				break;
480 			case Sid:
481 				csum = c;
482 				len = c & 0xF;
483 				e = allocb(len+1);
484 				if(e == nil)
485 					error(Eio);
486 				*e->wp++ = c>>4;	/* id */
487 				if(len)
488 					s = 0;
489 				else
490 					s = Ssum;
491 				break;
492 			case Ssum:
493 				csum &= 0xFF;
494 				if(c != csum){
495 					iprint("cksum: %2.2ux != %2.2ux\n", c, csum);
496 					s = Ssof;	/* try to resynchronise */
497 					if(e != nil){
498 						freeb(e);
499 						e = nil;
500 					}
501 					break;
502 				}
503 				if(b->rp < b->wp)
504 					*bp = b;
505 				else
506 					freeb(b);
507 				if(DEBUG){
508 					int i;
509 					iprint("event: [%ld]", BLEN(e));
510 					for(i=0; i<BLEN(e);i++)
511 						iprint(" %2.2ux", e->rp[i]);
512 					iprint("\n");
513 				}
514 				poperror();
515 				return e;
516 			default:
517 				csum += c;
518 				*e->wp++ = c;
519 				if(++s >= len)
520 					s = Ssum;
521 				break;
522 			}
523 		}
524 		freeb(b);
525 	}
526 	return 0;	/* not reached */
527 }
528 
529 static char *
acstatus(int x)530 acstatus(int x)
531 {
532 	switch(x){
533 	case 0:	return "offline";
534 	case 1:	return "online";
535 	case 2:	return "backup";
536 	}
537 	return "unknown";
538 }
539 
540 static char *
batstatus(int x)541 batstatus(int x)
542 {
543 	if(x & 0x40)
544 		return "charging";	/* not in linux but seems to be on mine */
545 	switch(x){
546 	case 0:		return "ok";
547 	case 1:		return "high";
548 	case 2:		return "low";
549 	case 4:		return "critical";
550 	case 8:		return "charging";
551 	case 0x80:	return "none";
552 	}
553 	return "unknown";
554 }
555 
556 static int
ptmap(int * m,int x,int y)557 ptmap(int *m, int x, int y)
558 {
559 	return XF(m[0]*x + m[1]*y + m[2]);
560 }
561 
562 static void
touched(Block * b,int buttons)563 touched(Block *b, int buttons)
564 {
565 	int rx, ry, x, y, dx, dy, n;
566 	Point op, *lp, cur;
567 
568 	if(BLEN(b) == 5){
569 		/* id Xhi Xlo Yhi Ylo */
570 		if(touch.down < 0){
571 			touch.down = 0;
572 			return;
573 		}
574 		rx = (b->rp[1]<<8)|b->rp[2];
575 		ry = (b->rp[3]<<8)|b->rp[4];
576 		if(conf.portrait){
577 			dx = rx; rx = ry; ry = dx;
578 		}
579 		if(touch.down == 0){
580 			touch.nout = 0;
581 			touch.p = 1;
582 			touch.n = 1;
583 			touch.avg = Pt(rx, ry);
584 			touch.pts[0] = touch.avg;
585 			touch.down = 1;
586 			return;
587 		}
588 		n = touch.p-1;
589 		if(n < 0)
590 			n = nelem(touch.pts)-1;
591 		lp = &touch.pts[n];	/* last point */
592 		if(touch.n > 0 && (rx-lp->x)*(ry-lp->y) > 50*50){	/* far out */
593 			if(++touch.nout > 3){
594 				touch.down = 0;
595 				touch.n = 0;
596 			}
597 			return;
598 		}
599 		op = touch.pts[touch.p];
600 		touch.pts[touch.p] = Pt(rx, ry);
601 		touch.p = (touch.p+1) % nelem(touch.pts);
602 		touch.avg.x += rx;
603 		touch.avg.y += ry;
604 		if(touch.n < nelem(touch.pts)){
605 			touch.n++;
606 			return;
607 		}
608 		touch.avg.x -= op.x;
609 		touch.avg.y -= op.y;
610 		cur = mousexy();
611 		rx = touch.avg.x/touch.n;
612 		ry = touch.avg.y/touch.n;
613 		x = ptmap(touch.m[0], rx, ry);
614 		dx = x-cur.x;
615 		y = ptmap(touch.m[1], rx, ry);
616 		dy = y-cur.y;
617 		if(dx*dx + dy*dy <= 2){
618 			dx = 0;
619 			dy = 0;
620 		}
621 		if(buttons == 0)
622 			buttons = 1<<0;	/* by default, stylus down implies button 1 */
623 		mousetrack(buttons&0x1f, dx, dy, 1);	/* TO DO: allow more than 3 buttons */
624 		/* TO DO: swcursupdate(oldx, oldy, x, y); */
625 		touch.down = 1;
626 	}else{
627 		if(touch.down){
628 			mousetrack(0, 0, 0, 1);	/* stylus up */
629 			touch.down = 0;
630 		}else
631 			touch.down = -1;
632 		touch.n = 0;
633 		touch.p = 0;
634 		touch.avg.x = 0;
635 		touch.avg.y = 0;
636 	}
637 }
638 
639 /*
640  * touchctl commands:
641  *	X a b c	- set X transformation
642  *	Y d e f	- set Y transformation
643  *	s<delay>		- set sample delay in millisec per sample
644  *	r<delay>		- set read delay in microsec
645  *	R<l2nr>			- set log2 of number of readings to average
646  */
647 static long
touchctl(char * a,long n)648 touchctl(char* a, long n)
649 {
650 	char buf[64];
651 	char *cp;
652 	int n0 = n;
653 	int bn;
654 	char *field[8];
655 	int nf, cmd, pn, m[2][3];
656 
657 	while(n) {
658 		bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;
659 		n -= bn;
660 		cp = a;
661 		a += bn;
662 		bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;
663 		memmove(buf, cp, bn);
664 		buf[bn] = '\0';
665 		nf = getfields(buf, field, nelem(field), 1, " \t\n");
666 		if(nf <= 0)
667 			continue;
668 		if(strcmp(field[0], "calibrate") == 0){
669 			if(nf == 1){
670 				lock(&touch);
671 				memset(touch.m, 0, sizeof(touch.m));
672 				touch.m[0][0] = FX(1,1);
673 				touch.m[1][1] = FX(1,1);
674 				unlock(&touch);
675 			}else if(nf >= 5){
676 				memset(m, 0, sizeof(m));
677 				m[0][0] = strtol(field[1], 0, 0);
678 				m[1][1] = strtol(field[2], 0, 0);
679 				m[0][2] = strtol(field[3], 0, 0);
680 				m[1][2] = strtol(field[4], 0, 0);
681 				if(nf > 5)
682 					m[0][1] = strtol(field[5], 0, 0);
683 				if(nf > 6)
684 					m[1][0] = strtol(field[6], 0, 0);
685 				lock(&touch);
686 				memmove(touch.m, m, sizeof(touch.m[0]));
687 				unlock(&touch);
688 			}else
689 				error(Ebadarg);
690 			continue;
691 		}
692 		cmd = *field[0]++;
693 		pn = *field[0] == 0;
694 		switch(cmd) {
695 		case 's':
696 			pn = strtol(field[pn], 0, 0);
697 			if(pn <= 0)
698 				error(Ebadarg);
699 			touch.rate = pn;
700 			break;
701 		case 'r':
702 			/* touch read delay */
703 			break;
704 		case 'X':
705 		case 'Y':
706 			if(nf < pn+2)
707 				error(Ebadarg);
708 			m[0][0] = strtol(field[pn], 0, 0);
709 			m[0][1] = strtol(field[pn+1], 0, 0);
710 			m[0][2] = strtol(field[pn+2], 0, 0);
711 			lock(&touch);
712 			memmove(touch.m[cmd=='Y'], m[0], sizeof(touch.m[0]));
713 			unlock(&touch);
714 			break;
715 		default:
716 			error(Ebadarg);
717 		}
718 	}
719 	return n0-n;
720 }
721 
722 /*
723  * this might belong elsewhere
724  */
725 static int
powerwait(void *)726 powerwait(void*)
727 {
728 	return (GPIOREG->gplr & GPIO_PWR_ON_i) == 0;
729 }
730 
731 static void
powerwaitproc(void *)732 powerwaitproc(void*)
733 {
734 	for(;;){
735 		sleep(&powerevent, powerwait, nil);
736 		do{
737 			tsleep(&up->sleep, return0, nil, 50);
738 		}while((GPIOREG->gplr & GPIO_PWR_ON_i) == 0);
739 		powersuspend();
740 	}
741 }
742 
743 Dev ipaqdevtab = {
744 	'T',
745 	"ipaq",
746 
747 	ipaqreset,
748 	ipaqinit,
749 	devshutdown,
750 	ipaqattach,
751 	ipaqwalk,
752 	ipaqstat,
753 	ipaqopen,
754 	devcreate,
755 	ipaqclose,
756 	ipaqread,
757 	devbread,
758 	ipaqwrite,
759 	devbwrite,
760 	devremove,
761 	devwstat,
762 };
763