13e12c5d1SDavid du Colombier #include "u.h"
23e12c5d1SDavid du Colombier #include "../port/lib.h"
33e12c5d1SDavid du Colombier #include "mem.h"
43e12c5d1SDavid du Colombier #include "dat.h"
53e12c5d1SDavid du Colombier #include "fns.h"
63e12c5d1SDavid du Colombier #include "io.h"
73e12c5d1SDavid du Colombier #include "../port/error.h"
83e12c5d1SDavid du Colombier
93e12c5d1SDavid du Colombier /* Centronix parallel (printer) port */
103e12c5d1SDavid du Colombier
113e12c5d1SDavid du Colombier /* base addresses */
123e12c5d1SDavid du Colombier static int lptbase[] = {
137dd7cddfSDavid du Colombier 0x378, /* lpt1 */
147dd7cddfSDavid du Colombier 0x3bc, /* lpt2 */
153e12c5d1SDavid du Colombier 0x278 /* lpt3 (sic) */
163e12c5d1SDavid du Colombier };
177dd7cddfSDavid du Colombier #define NDEV nelem(lptbase)
187dd7cddfSDavid du Colombier static int lptallocd[NDEV];
193e12c5d1SDavid du Colombier
203e12c5d1SDavid du Colombier /* offsets, and bits in the registers */
213e12c5d1SDavid du Colombier enum
223e12c5d1SDavid du Colombier {
239a747e4fSDavid du Colombier Qdir= 0x8000,
243e12c5d1SDavid du Colombier /* data latch register */
253e12c5d1SDavid du Colombier Qdlr= 0x0,
263e12c5d1SDavid du Colombier /* printer status register */
273e12c5d1SDavid du Colombier Qpsr= 0x1,
283e12c5d1SDavid du Colombier Fnotbusy= 0x80,
293e12c5d1SDavid du Colombier Fack= 0x40,
303e12c5d1SDavid du Colombier Fpe= 0x20,
313e12c5d1SDavid du Colombier Fselect= 0x10,
323e12c5d1SDavid du Colombier Fnoerror= 0x08,
333e12c5d1SDavid du Colombier /* printer control register */
343e12c5d1SDavid du Colombier Qpcr= 0x2,
353e12c5d1SDavid du Colombier Fie= 0x10,
363e12c5d1SDavid du Colombier Fselectin= 0x08,
373e12c5d1SDavid du Colombier Finitbar= 0x04,
383e12c5d1SDavid du Colombier Faf= 0x02,
393e12c5d1SDavid du Colombier Fstrobe= 0x01,
403e12c5d1SDavid du Colombier /* fake `data register' */
413e12c5d1SDavid du Colombier Qdata= 0x3,
423e12c5d1SDavid du Colombier };
433e12c5d1SDavid du Colombier
443e12c5d1SDavid du Colombier static int lptready(void*);
453e12c5d1SDavid du Colombier static void outch(int, int);
46219b2ee8SDavid du Colombier static void lptintr(Ureg*, void*);
473e12c5d1SDavid du Colombier
483e12c5d1SDavid du Colombier static Rendez lptrendez;
493e12c5d1SDavid du Colombier
503e12c5d1SDavid du Colombier Dirtab lptdir[]={
519a747e4fSDavid du Colombier ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
523e12c5d1SDavid du Colombier "dlr", {Qdlr}, 1, 0666,
533e12c5d1SDavid du Colombier "psr", {Qpsr}, 5, 0444,
543e12c5d1SDavid du Colombier "pcr", {Qpcr}, 0, 0222,
553e12c5d1SDavid du Colombier "data", {Qdata}, 0, 0222,
563e12c5d1SDavid du Colombier };
573e12c5d1SDavid du Colombier
583e12c5d1SDavid du Colombier static int
lptgen(Chan * c,char *,Dirtab * tab,int ntab,int i,Dir * dp)599a747e4fSDavid du Colombier lptgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp)
603e12c5d1SDavid du Colombier {
613e12c5d1SDavid du Colombier Qid qid;
623e12c5d1SDavid du Colombier
637dd7cddfSDavid du Colombier if(i == DEVDOTDOT){
649a747e4fSDavid du Colombier mkqid(&qid, Qdir, 0, QTDIR);
659a747e4fSDavid du Colombier devdir(c, qid, ".", 0, eve, 0555, dp);
667dd7cddfSDavid du Colombier return 1;
677dd7cddfSDavid du Colombier }
689a747e4fSDavid du Colombier i++; /* skip first element for . itself */
693e12c5d1SDavid du Colombier if(tab==0 || i>=ntab)
703e12c5d1SDavid du Colombier return -1;
713e12c5d1SDavid du Colombier tab += i;
723e12c5d1SDavid du Colombier qid = tab->qid;
739a747e4fSDavid du Colombier qid.path &= ~Qdir;
743e12c5d1SDavid du Colombier if(qid.path < Qdata)
753e12c5d1SDavid du Colombier qid.path += lptbase[c->dev];
763e12c5d1SDavid du Colombier qid.vers = c->dev;
77*57d98441SDavid du Colombier snprint(up->genbuf, sizeof up->genbuf, "lpt%lud%s", c->dev+1, tab->name);
789a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, tab->length, eve, tab->perm, dp);
793e12c5d1SDavid du Colombier return 1;
803e12c5d1SDavid du Colombier }
813e12c5d1SDavid du Colombier
827dd7cddfSDavid du Colombier static Chan*
lptattach(char * spec)833e12c5d1SDavid du Colombier lptattach(char *spec)
843e12c5d1SDavid du Colombier {
853e12c5d1SDavid du Colombier Chan *c;
863e12c5d1SDavid du Colombier int i = (spec && *spec) ? strtol(spec, 0, 0) : 1;
87*57d98441SDavid du Colombier char name[8];
88219b2ee8SDavid du Colombier static int set;
893e12c5d1SDavid du Colombier
90219b2ee8SDavid du Colombier if(!set){
917dd7cddfSDavid du Colombier outb(lptbase[i-1]+Qpcr, 0); /* turn off interrupts */
92219b2ee8SDavid du Colombier set = 1;
937dd7cddfSDavid du Colombier intrenable(IrqLPT, lptintr, 0, BUSUNKNOWN, "lpt");
94219b2ee8SDavid du Colombier }
953e12c5d1SDavid du Colombier if(i < 1 || i > NDEV)
963e12c5d1SDavid du Colombier error(Ebadarg);
977dd7cddfSDavid du Colombier if(lptallocd[i-1] == 0){
989a747e4fSDavid du Colombier int ecr;
99*57d98441SDavid du Colombier snprint(name, sizeof name, "lpt%d", i-1);
1007dd7cddfSDavid du Colombier if(ioalloc(lptbase[i-1], 3, 0, name) < 0)
1017dd7cddfSDavid du Colombier error("lpt port space in use");
1027dd7cddfSDavid du Colombier lptallocd[i-1] = 1;
10341dd6b47SDavid du Colombier /* Detect ECP - if found, put into PS/2 mode to suit style of driver */
1049a747e4fSDavid du Colombier ecr = lptbase[i-1] + 0x402;
1059a747e4fSDavid du Colombier if ((inb(ecr) & 3) == 1) {
1069a747e4fSDavid du Colombier outb(ecr, 0x34);
1079a747e4fSDavid du Colombier if (inb(ecr) == 0x35) {
1089a747e4fSDavid du Colombier outb(ecr, (inb(ecr) & 0x1f) | (1 << 5));
1099a747e4fSDavid du Colombier if(ioalloc(ecr, 1, 0, name) < 0)
1109a747e4fSDavid du Colombier error("lpt ecr port space in use");
1119a747e4fSDavid du Colombier }
1129a747e4fSDavid du Colombier }
1137dd7cddfSDavid du Colombier }
1143e12c5d1SDavid du Colombier c = devattach('L', spec);
1159a747e4fSDavid du Colombier c->qid.path = Qdir;
1163e12c5d1SDavid du Colombier c->dev = i-1;
1173e12c5d1SDavid du Colombier return c;
1183e12c5d1SDavid du Colombier }
1193e12c5d1SDavid du Colombier
1209a747e4fSDavid du Colombier static Walkqid*
lptwalk(Chan * c,Chan * nc,char ** name,int nname)1219a747e4fSDavid du Colombier lptwalk(Chan *c, Chan *nc, char **name, int nname)
1223e12c5d1SDavid du Colombier {
1239a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, lptdir, nelem(lptdir), lptgen);
1243e12c5d1SDavid du Colombier }
1253e12c5d1SDavid du Colombier
1269a747e4fSDavid du Colombier static int
lptstat(Chan * c,uchar * dp,int n)1279a747e4fSDavid du Colombier lptstat(Chan *c, uchar *dp, int n)
1283e12c5d1SDavid du Colombier {
1299a747e4fSDavid du Colombier return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen);
1303e12c5d1SDavid du Colombier }
1313e12c5d1SDavid du Colombier
1327dd7cddfSDavid du Colombier static Chan*
lptopen(Chan * c,int omode)1333e12c5d1SDavid du Colombier lptopen(Chan *c, int omode)
1343e12c5d1SDavid du Colombier {
1357dd7cddfSDavid du Colombier return devopen(c, omode, lptdir, nelem(lptdir), lptgen);
1363e12c5d1SDavid du Colombier }
1373e12c5d1SDavid du Colombier
1387dd7cddfSDavid du Colombier static void
lptclose(Chan *)1397dd7cddfSDavid du Colombier lptclose(Chan *)
1403e12c5d1SDavid du Colombier {
1413e12c5d1SDavid du Colombier }
1423e12c5d1SDavid du Colombier
1437dd7cddfSDavid du Colombier static long
lptread(Chan * c,void * a,long n,vlong)1447dd7cddfSDavid du Colombier lptread(Chan *c, void *a, long n, vlong)
1453e12c5d1SDavid du Colombier {
1467dd7cddfSDavid du Colombier char str[16];
1477dd7cddfSDavid du Colombier int size;
1487dd7cddfSDavid du Colombier ulong o;
1493e12c5d1SDavid du Colombier
1509a747e4fSDavid du Colombier if(c->qid.path == Qdir)
1517dd7cddfSDavid du Colombier return devdirread(c, a, n, lptdir, nelem(lptdir), lptgen);
152*57d98441SDavid du Colombier size = snprint(str, sizeof str, "0x%2.2ux\n", inb(c->qid.path));
1537dd7cddfSDavid du Colombier o = c->offset;
1547dd7cddfSDavid du Colombier if(o >= size)
1553e12c5d1SDavid du Colombier return 0;
1567dd7cddfSDavid du Colombier if(o+n > size)
1573e12c5d1SDavid du Colombier n = size-c->offset;
1587dd7cddfSDavid du Colombier memmove(a, str+o, n);
1593e12c5d1SDavid du Colombier return n;
1603e12c5d1SDavid du Colombier }
1613e12c5d1SDavid du Colombier
1627dd7cddfSDavid du Colombier static long
lptwrite(Chan * c,void * a,long n,vlong)1637dd7cddfSDavid du Colombier lptwrite(Chan *c, void *a, long n, vlong)
1643e12c5d1SDavid du Colombier {
1653e12c5d1SDavid du Colombier char str[16], *p;
1663e12c5d1SDavid du Colombier long base, k;
1673e12c5d1SDavid du Colombier
1683e12c5d1SDavid du Colombier if(n <= 0)
1693e12c5d1SDavid du Colombier return 0;
1703e12c5d1SDavid du Colombier if(c->qid.path != Qdata){
1713e12c5d1SDavid du Colombier if(n > sizeof str-1)
1723e12c5d1SDavid du Colombier n = sizeof str-1;
1733e12c5d1SDavid du Colombier memmove(str, a, n);
1743e12c5d1SDavid du Colombier str[n] = 0;
1753e12c5d1SDavid du Colombier outb(c->qid.path, strtoul(str, 0, 0));
1763e12c5d1SDavid du Colombier return n;
1773e12c5d1SDavid du Colombier }
1783e12c5d1SDavid du Colombier p = a;
1793e12c5d1SDavid du Colombier k = n;
1803e12c5d1SDavid du Colombier base = lptbase[c->dev];
1813e12c5d1SDavid du Colombier if(waserror()){
1823e12c5d1SDavid du Colombier outb(base+Qpcr, Finitbar);
1833e12c5d1SDavid du Colombier nexterror();
1843e12c5d1SDavid du Colombier }
1853e12c5d1SDavid du Colombier while(--k >= 0)
1863e12c5d1SDavid du Colombier outch(base, *p++);
1873e12c5d1SDavid du Colombier poperror();
1883e12c5d1SDavid du Colombier return n;
1893e12c5d1SDavid du Colombier }
1903e12c5d1SDavid du Colombier
1913e12c5d1SDavid du Colombier static void
outch(int base,int c)1923e12c5d1SDavid du Colombier outch(int base, int c)
1933e12c5d1SDavid du Colombier {
194219b2ee8SDavid du Colombier int status, tries;
1953e12c5d1SDavid du Colombier
196219b2ee8SDavid du Colombier for(tries=0;; tries++) {
197219b2ee8SDavid du Colombier status = inb(base+Qpsr);
198219b2ee8SDavid du Colombier if(status&Fnotbusy)
199219b2ee8SDavid du Colombier break;
20059cc4ca5SDavid du Colombier if((status&Fpe)==0 && (status&(Fselect|Fnoerror)) != (Fselect|Fnoerror))
20159cc4ca5SDavid du Colombier error(Eio);
2023e12c5d1SDavid du Colombier outb(base+Qpcr, Finitbar|Fie);
20359cc4ca5SDavid du Colombier tsleep(&lptrendez, lptready, (void *)base, 100);
204219b2ee8SDavid du Colombier }
2053e12c5d1SDavid du Colombier outb(base+Qdlr, c);
2063e12c5d1SDavid du Colombier outb(base+Qpcr, Finitbar|Fstrobe);
2073e12c5d1SDavid du Colombier outb(base+Qpcr, Finitbar);
2083e12c5d1SDavid du Colombier }
2093e12c5d1SDavid du Colombier
2103e12c5d1SDavid du Colombier static int
lptready(void * base)2113e12c5d1SDavid du Colombier lptready(void *base)
2123e12c5d1SDavid du Colombier {
2133e12c5d1SDavid du Colombier return inb((int)base+Qpsr)&Fnotbusy;
2143e12c5d1SDavid du Colombier }
2153e12c5d1SDavid du Colombier
2163e12c5d1SDavid du Colombier static void
lptintr(Ureg *,void *)2177dd7cddfSDavid du Colombier lptintr(Ureg *, void *)
2183e12c5d1SDavid du Colombier {
2193e12c5d1SDavid du Colombier wakeup(&lptrendez);
2203e12c5d1SDavid du Colombier }
2217dd7cddfSDavid du Colombier
2227dd7cddfSDavid du Colombier Dev lptdevtab = {
2237dd7cddfSDavid du Colombier 'L',
2247dd7cddfSDavid du Colombier "lpt",
2257dd7cddfSDavid du Colombier
2267dd7cddfSDavid du Colombier devreset,
2277dd7cddfSDavid du Colombier devinit,
2289a747e4fSDavid du Colombier devshutdown,
2297dd7cddfSDavid du Colombier lptattach,
2307dd7cddfSDavid du Colombier lptwalk,
2317dd7cddfSDavid du Colombier lptstat,
2327dd7cddfSDavid du Colombier lptopen,
2337dd7cddfSDavid du Colombier devcreate,
2347dd7cddfSDavid du Colombier lptclose,
2357dd7cddfSDavid du Colombier lptread,
2367dd7cddfSDavid du Colombier devbread,
2377dd7cddfSDavid du Colombier lptwrite,
2387dd7cddfSDavid du Colombier devbwrite,
2397dd7cddfSDavid du Colombier devremove,
2407dd7cddfSDavid du Colombier devwstat,
2417dd7cddfSDavid du Colombier };
242