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