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
lptgen(Chan * c,char *,Dirtab * tab,int ntab,int i,Dir * dp)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*
lptattach(char * spec)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*
lptwalk(Chan * c,Chan * nc,char ** name,int nname)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
lptstat(Chan * c,uchar * dp,int n)127 lptstat(Chan *c, uchar *dp, int n)
128 {
129 return devstat(c, dp, n, lptdir, nelem(lptdir), lptgen);
130 }
131
132 static Chan*
lptopen(Chan * c,int omode)133 lptopen(Chan *c, int omode)
134 {
135 return devopen(c, omode, lptdir, nelem(lptdir), lptgen);
136 }
137
138 static void
lptclose(Chan *)139 lptclose(Chan *)
140 {
141 }
142
143 static long
lptread(Chan * c,void * a,long n,vlong)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
lptwrite(Chan * c,void * a,long n,vlong)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
outch(int base,int c)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
lptready(void * base)215 lptready(void *base)
216 {
217 return inb((int)base+Qpsr)&Fnotbusy;
218 }
219
220 static void
lptintr(Ureg *,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