1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 #include "../port/error.h" 8 9 /* Centronix parallel (printer) port */ 10 11 /* base addresses */ 12 static int lptbase[] = { 13 0x378, /* lpt1 */ 14 0x3bc, /* lpt2 */ 15 0x278 /* lpt3 (sic) */ 16 }; 17 #define NDEV nelem(lptbase) 18 static int lptallocd[NDEV]; 19 20 /* offsets, and bits in the registers */ 21 enum 22 { 23 Qdir= 0x8000, 24 /* data latch register */ 25 Qdlr= 0x0, 26 /* printer status register */ 27 Qpsr= 0x1, 28 Fnotbusy= 0x80, 29 Fack= 0x40, 30 Fpe= 0x20, 31 Fselect= 0x10, 32 Fnoerror= 0x08, 33 /* printer control register */ 34 Qpcr= 0x2, 35 Fie= 0x10, 36 Fselectin= 0x08, 37 Finitbar= 0x04, 38 Faf= 0x02, 39 Fstrobe= 0x01, 40 /* fake `data register' */ 41 Qdata= 0x3, 42 }; 43 44 static int lptready(void*); 45 static void outch(int, int); 46 static void lptintr(Ureg*, void*); 47 48 static Rendez lptrendez; 49 50 Dirtab lptdir[]={ 51 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 52 "dlr", {Qdlr}, 1, 0666, 53 "psr", {Qpsr}, 5, 0444, 54 "pcr", {Qpcr}, 0, 0222, 55 "data", {Qdata}, 0, 0222, 56 }; 57 58 static int 59 lptgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp) 60 { 61 Qid qid; 62 63 if(i == DEVDOTDOT){ 64 mkqid(&qid, Qdir, 0, QTDIR); 65 devdir(c, qid, ".", 0, eve, 0555, dp); 66 return 1; 67 } 68 i++; /* skip first element for . itself */ 69 if(tab==0 || i>=ntab) 70 return -1; 71 tab += i; 72 qid = tab->qid; 73 qid.path &= ~Qdir; 74 if(qid.path < Qdata) 75 qid.path += lptbase[c->dev]; 76 qid.vers = c->dev; 77 sprint(up->genbuf, "lpt%lud%s", c->dev+1, tab->name); 78 devdir(c, qid, up->genbuf, tab->length, eve, tab->perm, dp); 79 return 1; 80 } 81 82 static Chan* 83 lptattach(char *spec) 84 { 85 Chan *c; 86 int i = (spec && *spec) ? strtol(spec, 0, 0) : 1; 87 char name[5]; 88 static int set; 89 90 if(!set){ 91 outb(lptbase[i-1]+Qpcr, 0); /* turn off interrupts */ 92 set = 1; 93 intrenable(IrqLPT, lptintr, 0, BUSUNKNOWN, "lpt"); 94 } 95 if(i < 1 || i > NDEV) 96 error(Ebadarg); 97 if(lptallocd[i-1] == 0){ 98 int ecr; 99 sprint(name, "lpt%d", i-1); 100 if(ioalloc(lptbase[i-1], 3, 0, name) < 0) 101 error("lpt port space in use"); 102 lptallocd[i-1] = 1; 103 // Detect ECP - if found, put into PS/2 mode to suit style of driver 104 ecr = lptbase[i-1] + 0x402; 105 if ((inb(ecr) & 3) == 1) { 106 outb(ecr, 0x34); 107 if (inb(ecr) == 0x35) { 108 outb(ecr, (inb(ecr) & 0x1f) | (1 << 5)); 109 if(ioalloc(ecr, 1, 0, name) < 0) 110 error("lpt ecr port space in use"); 111 } 112 } 113 } 114 c = devattach('L', spec); 115 c->qid.path = Qdir; 116 c->dev = i-1; 117 return c; 118 } 119 120 static Walkqid* 121 lptwalk(Chan *c, Chan *nc, char **name, int nname) 122 { 123 return devwalk(c, nc, name, nname, lptdir, nelem(lptdir), lptgen); 124 } 125 126 static int 127 lptstat(Chan *c, uchar *dp, int n) 128 { 129 return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen); 130 } 131 132 static Chan* 133 lptopen(Chan *c, int omode) 134 { 135 return devopen(c, omode, lptdir, nelem(lptdir), lptgen); 136 } 137 138 static void 139 lptclose(Chan *) 140 { 141 } 142 143 static long 144 lptread(Chan *c, void *a, long n, vlong) 145 { 146 char str[16]; 147 int size; 148 ulong o; 149 150 if(c->qid.path == Qdir) 151 return devdirread(c, a, n, lptdir, nelem(lptdir), lptgen); 152 size = sprint(str, "0x%2.2ux\n", inb(c->qid.path)); 153 o = c->offset; 154 if(o >= size) 155 return 0; 156 if(o+n > size) 157 n = size-c->offset; 158 memmove(a, str+o, n); 159 return n; 160 } 161 162 static long 163 lptwrite(Chan *c, void *a, long n, vlong) 164 { 165 char str[16], *p; 166 long base, k; 167 168 if(n <= 0) 169 return 0; 170 if(c->qid.path != Qdata){ 171 if(n > sizeof str-1) 172 n = sizeof str-1; 173 memmove(str, a, n); 174 str[n] = 0; 175 outb(c->qid.path, strtoul(str, 0, 0)); 176 return n; 177 } 178 p = a; 179 k = n; 180 base = lptbase[c->dev]; 181 if(waserror()){ 182 outb(base+Qpcr, Finitbar); 183 nexterror(); 184 } 185 while(--k >= 0) 186 outch(base, *p++); 187 poperror(); 188 return n; 189 } 190 191 static void 192 outch(int base, int c) 193 { 194 int status, tries; 195 196 for(tries=0;; tries++) { 197 status = inb(base+Qpsr); 198 if(status&Fnotbusy) 199 break; 200 if((status&Fpe)==0 && (status&(Fselect|Fnoerror)) != (Fselect|Fnoerror)) 201 error(Eio); 202 if(tries < 10) 203 tsleep(&lptrendez, return0, nil, 1); 204 else { 205 outb(base+Qpcr, Finitbar|Fie); 206 tsleep(&lptrendez, lptready, (void *)base, 100); 207 } 208 } 209 outb(base+Qdlr, c); 210 outb(base+Qpcr, Finitbar|Fstrobe); 211 outb(base+Qpcr, Finitbar); 212 } 213 214 static int 215 lptready(void *base) 216 { 217 return inb((int)base+Qpsr)&Fnotbusy; 218 } 219 220 static void 221 lptintr(Ureg *, void *) 222 { 223 wakeup(&lptrendez); 224 } 225 226 Dev lptdevtab = { 227 'L', 228 "lpt", 229 230 devreset, 231 devinit, 232 devshutdown, 233 lptattach, 234 lptwalk, 235 lptstat, 236 lptopen, 237 devcreate, 238 lptclose, 239 lptread, 240 devbread, 241 lptwrite, 242 devbwrite, 243 devremove, 244 devwstat, 245 }; 246