diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/pc/devlpt.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/devlpt.c')
-rwxr-xr-x | sys/src/9/pc/devlpt.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/sys/src/9/pc/devlpt.c b/sys/src/9/pc/devlpt.c new file mode 100755 index 000000000..dc06622b9 --- /dev/null +++ b/sys/src/9/pc/devlpt.c @@ -0,0 +1,241 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* Centronix parallel (printer) port */ + +/* base addresses */ +static int lptbase[] = { + 0x378, /* lpt1 */ + 0x3bc, /* lpt2 */ + 0x278 /* lpt3 (sic) */ +}; +#define NDEV nelem(lptbase) +static int lptallocd[NDEV]; + +/* offsets, and bits in the registers */ +enum +{ + Qdir= 0x8000, + /* data latch register */ + Qdlr= 0x0, + /* printer status register */ + Qpsr= 0x1, + Fnotbusy= 0x80, + Fack= 0x40, + Fpe= 0x20, + Fselect= 0x10, + Fnoerror= 0x08, + /* printer control register */ + Qpcr= 0x2, + Fie= 0x10, + Fselectin= 0x08, + Finitbar= 0x04, + Faf= 0x02, + Fstrobe= 0x01, + /* fake `data register' */ + Qdata= 0x3, +}; + +static int lptready(void*); +static void outch(int, int); +static void lptintr(Ureg*, void*); + +static Rendez lptrendez; + +Dirtab lptdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dlr", {Qdlr}, 1, 0666, + "psr", {Qpsr}, 5, 0444, + "pcr", {Qpcr}, 0, 0222, + "data", {Qdata}, 0, 0222, +}; + +static int +lptgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Qid qid; + + if(i == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, ".", 0, eve, 0555, dp); + return 1; + } + i++; /* skip first element for . itself */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + qid = tab->qid; + qid.path &= ~Qdir; + if(qid.path < Qdata) + qid.path += lptbase[c->dev]; + qid.vers = c->dev; + snprint(up->genbuf, sizeof up->genbuf, "lpt%lud%s", c->dev+1, tab->name); + devdir(c, qid, up->genbuf, tab->length, eve, tab->perm, dp); + return 1; +} + +static Chan* +lptattach(char *spec) +{ + Chan *c; + int i = (spec && *spec) ? strtol(spec, 0, 0) : 1; + char name[8]; + static int set; + + if(!set){ + outb(lptbase[i-1]+Qpcr, 0); /* turn off interrupts */ + set = 1; + intrenable(IrqLPT, lptintr, 0, BUSUNKNOWN, "lpt"); + } + if(i < 1 || i > NDEV) + error(Ebadarg); + if(lptallocd[i-1] == 0){ + int ecr; + snprint(name, sizeof name, "lpt%d", i-1); + if(ioalloc(lptbase[i-1], 3, 0, name) < 0) + error("lpt port space in use"); + lptallocd[i-1] = 1; + /* Detect ECP - if found, put into PS/2 mode to suit style of driver */ + ecr = lptbase[i-1] + 0x402; + if ((inb(ecr) & 3) == 1) { + outb(ecr, 0x34); + if (inb(ecr) == 0x35) { + outb(ecr, (inb(ecr) & 0x1f) | (1 << 5)); + if(ioalloc(ecr, 1, 0, name) < 0) + error("lpt ecr port space in use"); + } + } + } + c = devattach('L', spec); + c->qid.path = Qdir; + c->dev = i-1; + return c; +} + +static Walkqid* +lptwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, lptdir, nelem(lptdir), lptgen); +} + +static int +lptstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen); +} + +static Chan* +lptopen(Chan *c, int omode) +{ + return devopen(c, omode, lptdir, nelem(lptdir), lptgen); +} + +static void +lptclose(Chan *) +{ +} + +static long +lptread(Chan *c, void *a, long n, vlong) +{ + char str[16]; + int size; + ulong o; + + if(c->qid.path == Qdir) + return devdirread(c, a, n, lptdir, nelem(lptdir), lptgen); + size = snprint(str, sizeof str, "0x%2.2ux\n", inb(c->qid.path)); + o = c->offset; + if(o >= size) + return 0; + if(o+n > size) + n = size-c->offset; + memmove(a, str+o, n); + return n; +} + +static long +lptwrite(Chan *c, void *a, long n, vlong) +{ + char str[16], *p; + long base, k; + + if(n <= 0) + return 0; + if(c->qid.path != Qdata){ + if(n > sizeof str-1) + n = sizeof str-1; + memmove(str, a, n); + str[n] = 0; + outb(c->qid.path, strtoul(str, 0, 0)); + return n; + } + p = a; + k = n; + base = lptbase[c->dev]; + if(waserror()){ + outb(base+Qpcr, Finitbar); + nexterror(); + } + while(--k >= 0) + outch(base, *p++); + poperror(); + return n; +} + +static void +outch(int base, int c) +{ + int status, tries; + + for(tries=0;; tries++) { + status = inb(base+Qpsr); + if(status&Fnotbusy) + break; + if((status&Fpe)==0 && (status&(Fselect|Fnoerror)) != (Fselect|Fnoerror)) + error(Eio); + outb(base+Qpcr, Finitbar|Fie); + tsleep(&lptrendez, lptready, (void *)base, 100); + } + outb(base+Qdlr, c); + outb(base+Qpcr, Finitbar|Fstrobe); + outb(base+Qpcr, Finitbar); +} + +static int +lptready(void *base) +{ + return inb((int)base+Qpsr)&Fnotbusy; +} + +static void +lptintr(Ureg *, void *) +{ + wakeup(&lptrendez); +} + +Dev lptdevtab = { + 'L', + "lpt", + + devreset, + devinit, + devshutdown, + lptattach, + lptwalk, + lptstat, + lptopen, + devcreate, + lptclose, + lptread, + devbread, + lptwrite, + devbwrite, + devremove, + devwstat, +}; |