xref: /inferno-os/os/pc/devlpt.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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