123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- /*
- * iPAQ H3650 touch screen and other devices
- *
- * Inferno driver derived from sketchy documentation and
- * information gleaned from linux/char/h3650_ts.c
- * by Charles Flynn.
- *
- * Copyright © 2000,2001 Vita Nuova Holdings Limited. All rights reserved.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- #include "keyboard.h"
- #include <kernel.h>
- #include <draw.h>
- #include <memdraw.h>
- #include "screen.h"
- #define DEBUG 0
- /*
- * packet format
- *
- * SOF (0x02)
- * (id<<4) | len byte length
- * data[len] bytes
- * chk checksum mod 256 excluding SOF
- */
- enum {
- Csof = 0x02,
- Ceof = 0x03,
- Hdrlen = 3,
- /* opcodes */
- Oversion = 0,
- Okeys = 2,
- Otouch = 3,
- Ordeeprom = 4,
- Owreeprom = 5,
- Othermal = 6,
- Oled = 8,
- Obattery = 9,
- Ospiread = 11,
- Ospiwrite = 12,
- Obacklight = 13,
- Oextstatus = 0xA1,
- };
- enum {
- Powerbit = 0, /* GPIO bit for power on/off key */
- };
- enum{
- Qdir,
- Qctl,
- Qtouchctl,
- Qbattery,
- Qversion,
- };
- static
- Dirtab ipaqtab[]={
- ".", {Qdir, 0, QTDIR}, 0, 0555,
- "ipaqctl", {Qctl}, 0, 0600,
- "battery", {Qbattery}, 0, 0444,
- "version", {Qversion}, 0, 0444,
- "touchctl", {Qtouchctl}, 0, 0644,
- };
- static struct {
- QLock;
- Chan* c;
- Lock rl; /* protect cmd, reply */
- int cmd;
- Block* reply;
- Rendez r;
- } atmel;
- /* to and from fixed point */
- #define FX(a,b) (((a)<<16)/(b))
- #define XF(v) ((v)>>16)
- static struct {
- Lock;
- int rate;
- int m[2][3]; /* transformation matrix */
- Point avg;
- Point diff;
- Point pts[4];
- int n; /* number of points in pts */
- int p; /* current index in pts */
- int down;
- int nout;
- } touch = {
- {0},
- .m {{-FX(1,3), 0, FX(346,1)},{0, -FX(1,4), FX(256, 1)}},
- };
- /*
- * map rocker positions to same codes as plan 9
- */
- static Rune rockermap[2][4] ={
- {Right, Down, Up, Left}, /* landscape */
- {Up, Right, Left, Down}, /* portrait */
- };
- static Rendez powerevent;
- static void cmdack(int, void*, int);
- static int cmdio(int, void*, int, void*, int);
- static void ipaqreadproc(void*);
- static void powerwaitproc(void*);
- static Block* rdevent(Block**);
- static long touchctl(char*, long);
- static void touched(Block*, int);
- static int wrcmd(int, void*, int, void*, int);
- static char* acstatus(int);
- static char* batstatus(int);
- static void powerintr(Ureg*, void*);
- static void
- ipaqreset(void)
- {
- intrenable(Powerbit, powerintr, nil, BusGPIOfalling, "power off");
- }
- static void
- ipaqinit(void)
- {
- kproc("powerwait", powerwaitproc, nil, 0);
- }
- static Chan*
- ipaqattach(char* spec)
- {
- int fd;
- qlock(&atmel);
- if(waserror()){
- qunlock(&atmel);
- nexterror();
- }
- if(atmel.c == nil){
- fd = kopen("#t/eia1ctl", ORDWR);
- if(fd < 0)
- error(up->env->errstr);
- kwrite(fd, "b115200", 7); /* it's already pn, l8 */
- kclose(fd);
- fd = kopen("#t/eia1", ORDWR);
- if(fd < 0)
- error(up->env->errstr);
- atmel.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
- kclose(fd);
- atmel.cmd = -1;
- kproc("ipaqread", ipaqreadproc, nil, 0);
- }
- poperror();
- qunlock(&atmel);
- return devattach('T', spec);
- }
- static Walkqid*
- ipaqwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, ipaqtab, nelem(ipaqtab), devgen);
- }
- static int
- ipaqstat(Chan* c, uchar *db, int n)
- {
- return devstat(c, db, n, ipaqtab, nelem(ipaqtab), devgen);
- }
- static Chan*
- ipaqopen(Chan* c, int omode)
- {
- return devopen(c, omode, ipaqtab, nelem(ipaqtab), devgen);
- }
- static void
- ipaqclose(Chan*)
- {
- }
- static long
- ipaqread(Chan* c, void* a, long n, vlong offset)
- {
- char *tmp, buf[64];
- uchar reply[12];
- int v, p, l;
- switch((ulong)c->qid.path){
- case Qdir:
- return devdirread(c, a, n, ipaqtab, nelem(ipaqtab), devgen);
- case Qtouchctl:
- tmp = malloc(READSTR);
- if(waserror()){
- free(tmp);
- nexterror();
- }
- snprint(tmp, READSTR, "s%d\nr%d\nR%d\nX %d %d %d\nY %d %d %d\n",
- 1000, 0, 1,
- touch.m[0][0], touch.m[0][1], touch.m[0][2],
- touch.m[1][0], touch.m[1][1], touch.m[1][2]);
- n = readstr(offset, a, n, tmp);
- poperror();
- free(tmp);
- break;
- case Qbattery:
- cmdio(Obattery, reply, 0, reply, sizeof(reply));
- tmp = malloc(READSTR);
- if(waserror()){
- free(tmp);
- nexterror();
- }
- v = (reply[4]<<8)|reply[3];
- p = 425*v/1000 - 298;
- snprint(tmp, READSTR, "voltage: %d %dmV %d%% %d\nac: %s\nstatus: %d %s\nchem: %d\n",
- v, 1000*v/228, p, 300*p/100, acstatus(reply[1]), reply[5], batstatus(reply[5]), reply[2]);
- n = readstr(offset, a, n, tmp);
- poperror();
- free(tmp);
- break;
- case Qversion:
- l = cmdio(Oversion, reply, 0, reply, sizeof(reply));
- if(l > 4){
- l--;
- memmove(buf, reply+1, 4);
- if(l > 8){
- buf[4] = ' ';
- memmove(buf+5, reply+5, 4); /* pack version */
- sprint(buf+9, " %.2x\n", reply[9]); /* ``boot type'' */
- }else{
- buf[4] = '\n';
- buf[5] = 0;
- }
- return readstr(offset, a, n, buf);
- }
- n=0;
- break;
- default:
- n=0;
- break;
- }
- return n;
- }
- static long
- ipaqwrite(Chan* c, void* a, long n, vlong)
- {
- char cmd[64], op[32], *fields[6];
- int nf;
- switch((ulong)c->qid.path){
- case Qctl:
- if(n >= sizeof(cmd)-1)
- n = sizeof(cmd)-1;
- memmove(cmd, a, n);
- cmd[n] = 0;
- nf = getfields(cmd, fields, nelem(fields), 1, " \t\n");
- if(nf <= 0)
- error(Ebadarg);
- if(nf >= 4 && strcmp(fields[0], "light") == 0){
- op[0] = atoi(fields[1]); /* mode */
- op[1] = atoi(fields[2]); /* power */
- op[2] = atoi(fields[3]); /* brightness */
- cmdack(Obacklight, op, 3);
- }else if(nf >= 5 && strcmp(fields[0], "led") == 0){
- op[0] = atoi(fields[1]);
- op[1] = atoi(fields[2]);
- op[2] = atoi(fields[3]);
- op[3] = atoi(fields[4]);
- cmdack(Oled, op, 4);
- }else if(strcmp(fields[0], "suspend") == 0){
- /* let the kproc do it */
- wakeup(&powerevent);
- }else
- error(Ebadarg);
- break;
- case Qtouchctl:
- return touchctl(a, n);
- default:
- error(Ebadusefd);
- }
- return n;
- }
- static void
- powerintr(Ureg*, void*)
- {
- wakeup(&powerevent);
- }
- static void
- cmdack(int id, void *a, int n)
- {
- uchar reply[16];
- cmdio(id, a, n, reply, sizeof(reply));
- }
- static int
- cmdio(int id, void *a, int n, void *reply, int lim)
- {
- qlock(&atmel);
- if(waserror()){
- qunlock(&atmel);
- nexterror();
- }
- n = wrcmd(id, a, n, reply, lim);
- poperror();
- qunlock(&atmel);
- return n;
- }
- static int
- havereply(void*)
- {
- return atmel.reply != nil;
- }
- static int
- wrcmd(int id, void *a, int n, void *b, int lim)
- {
- uchar buf[32];
- int i, sum;
- Block *e;
- if(n >= 16)
- error(Eio);
- lock(&atmel.rl);
- atmel.cmd = id;
- unlock(&atmel.rl);
- buf[0] = Csof;
- buf[1] = (id<<4) | (n&0xF);
- if(n)
- memmove(buf+2, a, n);
- sum = 0;
- for(i=1; i<n+2; i++)
- sum += buf[i];
- buf[i++] = sum;
- if(0){
- iprint("msg=");
- for(sum=0; sum<i; sum++)
- iprint(" %2.2ux", buf[sum]);
- iprint("\n");
- }
- if(kchanio(atmel.c, buf, i, OWRITE) != i)
- error(Eio);
- tsleep(&atmel.r, havereply, nil, 500);
- lock(&atmel.rl);
- e = atmel.reply;
- atmel.reply = nil;
- atmel.cmd = -1;
- unlock(&atmel.rl);
- if(e == nil){
- print("ipaq: no reply\n");
- error(Eio);
- }
- if(waserror()){
- freeb(e);
- nexterror();
- }
- if(e->rp[0] != id){
- print("ipaq: rdreply: mismatched reply %d :: %d\n", id, e->rp[0]);
- error(Eio);
- }
- n = BLEN(e);
- if(n < lim)
- lim = n;
- memmove(b, e->rp, lim);
- poperror();
- freeb(e);
- return lim;
- }
- static void
- ipaqreadproc(void*)
- {
- Block *e, *b, *partial;
- int c, mousemod;
- while(waserror())
- print("ipaqread: %r\n");
- partial = nil;
- mousemod = 0;
- for(;;){
- e = rdevent(&partial);
- if(e == nil){
- print("ipaqread: rdevent: %r\n");
- continue;
- }
- switch(e->rp[0]){
- case Otouch:
- touched(e, mousemod);
- freeb(e);
- break;
- case Okeys:
- //print("key %2.2ux\n", e->rp[1]);
- c = e->rp[1] & 0xF;
- if(c >= 6 && c < 10){ /* rocker */
- if((e->rp[1] & 0x80) == 0){
- kbdrepeat(0);
- kbdputc(kbdq, rockermap[conf.portrait&1][c-6]);
- }else
- kbdrepeat(0);
- }else{
- /* TO DO: change tkmouse and mousetrack to allow extra buttons */
- if(--c == 0)
- c = 5;
- if(e->rp[1] & 0x80)
- mousemod &= ~(1<<c);
- else
- mousemod |= 1<<c;
- }
- freeb(e);
- break;
- default:
- lock(&atmel.rl);
- if(atmel.cmd == e->rp[0]){
- b = atmel.reply;
- atmel.reply = e;
- unlock(&atmel.rl);
- wakeup(&atmel.r);
- if(b != nil)
- freeb(b);
- }else{
- unlock(&atmel.rl);
- print("ipaqread: discard op %d\n", e->rp[0]);
- freeb(e);
- }
- }
- }
- }
- static Block *
- rdevent(Block **bp)
- {
- Block *b, *e;
- int s, c, len, csum;
- enum {Ssof=16, Sid, Ssum};
- s = Ssof;
- csum = 0;
- len = 0;
- e = nil;
- if(waserror()){
- if(e != nil)
- freeb(e);
- nexterror();
- }
- for(;;){
- b = *bp;
- *bp = nil;
- if(b == nil){
- b = devtab[atmel.c->type]->bread(atmel.c, 128, 0);
- if(b == nil)
- error(Eio);
- if(DEBUG)
- iprint("r: %ld\n", BLEN(b));
- }
- while(b->rp < b->wp){
- c = *b->rp++;
- switch(s){
- case Ssof:
- if(c == Csof)
- s = Sid;
- else if(1)
- iprint("!sof: %2.2ux %d\n", c, s);
- break;
- case Sid:
- csum = c;
- len = c & 0xF;
- e = allocb(len+1);
- if(e == nil)
- error(Eio);
- *e->wp++ = c>>4; /* id */
- if(len)
- s = 0;
- else
- s = Ssum;
- break;
- case Ssum:
- csum &= 0xFF;
- if(c != csum){
- iprint("cksum: %2.2ux != %2.2ux\n", c, csum);
- s = Ssof; /* try to resynchronise */
- if(e != nil){
- freeb(e);
- e = nil;
- }
- break;
- }
- if(b->rp < b->wp)
- *bp = b;
- else
- freeb(b);
- if(DEBUG){
- int i;
- iprint("event: [%ld]", BLEN(e));
- for(i=0; i<BLEN(e);i++)
- iprint(" %2.2ux", e->rp[i]);
- iprint("\n");
- }
- poperror();
- return e;
- default:
- csum += c;
- *e->wp++ = c;
- if(++s >= len)
- s = Ssum;
- break;
- }
- }
- freeb(b);
- }
- return 0; /* not reached */
- }
- static char *
- acstatus(int x)
- {
- switch(x){
- case 0: return "offline";
- case 1: return "online";
- case 2: return "backup";
- }
- return "unknown";
- }
- static char *
- batstatus(int x)
- {
- if(x & 0x40)
- return "charging"; /* not in linux but seems to be on mine */
- switch(x){
- case 0: return "ok";
- case 1: return "high";
- case 2: return "low";
- case 4: return "critical";
- case 8: return "charging";
- case 0x80: return "none";
- }
- return "unknown";
- }
- static int
- ptmap(int *m, int x, int y)
- {
- return XF(m[0]*x + m[1]*y + m[2]);
- }
- static void
- touched(Block *b, int buttons)
- {
- int rx, ry, x, y, dx, dy, n;
- Point op, *lp, cur;
- if(BLEN(b) == 5){
- /* id Xhi Xlo Yhi Ylo */
- if(touch.down < 0){
- touch.down = 0;
- return;
- }
- rx = (b->rp[1]<<8)|b->rp[2];
- ry = (b->rp[3]<<8)|b->rp[4];
- if(conf.portrait){
- dx = rx; rx = ry; ry = dx;
- }
- if(touch.down == 0){
- touch.nout = 0;
- touch.p = 1;
- touch.n = 1;
- touch.avg = Pt(rx, ry);
- touch.pts[0] = touch.avg;
- touch.down = 1;
- return;
- }
- n = touch.p-1;
- if(n < 0)
- n = nelem(touch.pts)-1;
- lp = &touch.pts[n]; /* last point */
- if(touch.n > 0 && (rx-lp->x)*(ry-lp->y) > 50*50){ /* far out */
- if(++touch.nout > 3){
- touch.down = 0;
- touch.n = 0;
- }
- return;
- }
- op = touch.pts[touch.p];
- touch.pts[touch.p] = Pt(rx, ry);
- touch.p = (touch.p+1) % nelem(touch.pts);
- touch.avg.x += rx;
- touch.avg.y += ry;
- if(touch.n < nelem(touch.pts)){
- touch.n++;
- return;
- }
- touch.avg.x -= op.x;
- touch.avg.y -= op.y;
- cur = mousexy();
- rx = touch.avg.x/touch.n;
- ry = touch.avg.y/touch.n;
- x = ptmap(touch.m[0], rx, ry);
- dx = x-cur.x;
- y = ptmap(touch.m[1], rx, ry);
- dy = y-cur.y;
- if(dx*dx + dy*dy <= 2){
- dx = 0;
- dy = 0;
- }
- if(buttons == 0)
- buttons = 1<<0; /* by default, stylus down implies button 1 */
- mousetrack(buttons&0x1f, dx, dy, 1); /* TO DO: allow more than 3 buttons */
- /* TO DO: swcursupdate(oldx, oldy, x, y); */
- touch.down = 1;
- }else{
- if(touch.down){
- mousetrack(0, 0, 0, 1); /* stylus up */
- touch.down = 0;
- }else
- touch.down = -1;
- touch.n = 0;
- touch.p = 0;
- touch.avg.x = 0;
- touch.avg.y = 0;
- }
- }
- /*
- * touchctl commands:
- * X a b c - set X transformation
- * Y d e f - set Y transformation
- * s<delay> - set sample delay in millisec per sample
- * r<delay> - set read delay in microsec
- * R<l2nr> - set log2 of number of readings to average
- */
- static long
- touchctl(char* a, long n)
- {
- char buf[64];
- char *cp;
- int n0 = n;
- int bn;
- char *field[8];
- int nf, cmd, pn, m[2][3];
- while(n) {
- bn = (cp = memchr(a, '\n', n))!=nil ? cp-a+1 : n;
- n -= bn;
- cp = a;
- a += bn;
- bn = bn > sizeof(buf)-1 ? sizeof(buf)-1 : bn;
- memmove(buf, cp, bn);
- buf[bn] = '\0';
- nf = getfields(buf, field, nelem(field), 1, " \t\n");
- if(nf <= 0)
- continue;
- if(strcmp(field[0], "calibrate") == 0){
- if(nf == 1){
- lock(&touch);
- memset(touch.m, 0, sizeof(touch.m));
- touch.m[0][0] = FX(1,1);
- touch.m[1][1] = FX(1,1);
- unlock(&touch);
- }else if(nf >= 5){
- memset(m, 0, sizeof(m));
- m[0][0] = strtol(field[1], 0, 0);
- m[1][1] = strtol(field[2], 0, 0);
- m[0][2] = strtol(field[3], 0, 0);
- m[1][2] = strtol(field[4], 0, 0);
- if(nf > 5)
- m[0][1] = strtol(field[5], 0, 0);
- if(nf > 6)
- m[1][0] = strtol(field[6], 0, 0);
- lock(&touch);
- memmove(touch.m, m, sizeof(touch.m[0]));
- unlock(&touch);
- }else
- error(Ebadarg);
- continue;
- }
- cmd = *field[0]++;
- pn = *field[0] == 0;
- switch(cmd) {
- case 's':
- pn = strtol(field[pn], 0, 0);
- if(pn <= 0)
- error(Ebadarg);
- touch.rate = pn;
- break;
- case 'r':
- /* touch read delay */
- break;
- case 'X':
- case 'Y':
- if(nf < pn+2)
- error(Ebadarg);
- m[0][0] = strtol(field[pn], 0, 0);
- m[0][1] = strtol(field[pn+1], 0, 0);
- m[0][2] = strtol(field[pn+2], 0, 0);
- lock(&touch);
- memmove(touch.m[cmd=='Y'], m[0], sizeof(touch.m[0]));
- unlock(&touch);
- break;
- default:
- error(Ebadarg);
- }
- }
- return n0-n;
- }
- /*
- * this might belong elsewhere
- */
- static int
- powerwait(void*)
- {
- return (GPIOREG->gplr & GPIO_PWR_ON_i) == 0;
- }
- static void
- powerwaitproc(void*)
- {
- for(;;){
- sleep(&powerevent, powerwait, nil);
- do{
- tsleep(&up->sleep, return0, nil, 50);
- }while((GPIOREG->gplr & GPIO_PWR_ON_i) == 0);
- powersuspend();
- }
- }
- Dev ipaqdevtab = {
- 'T',
- "ipaq",
- ipaqreset,
- ipaqinit,
- devshutdown,
- ipaqattach,
- ipaqwalk,
- ipaqstat,
- ipaqopen,
- devcreate,
- ipaqclose,
- ipaqread,
- devbread,
- ipaqwrite,
- devbwrite,
- devremove,
- devwstat,
- };
|