xref: /plan9/sys/src/9/pc/devlpt.c (revision 57d98441a62fcee6012d93fcd1f7724ec4b1afe9)
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 	snprint(up->genbuf, sizeof 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[8];
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 		snprint(name, sizeof 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 = snprint(str, sizeof 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 		outb(base+Qpcr, Finitbar|Fie);
203 		tsleep(&lptrendez, lptready, (void *)base, 100);
204 	}
205 	outb(base+Qdlr, c);
206 	outb(base+Qpcr, Finitbar|Fstrobe);
207 	outb(base+Qpcr, Finitbar);
208 }
209 
210 static int
lptready(void * base)211 lptready(void *base)
212 {
213 	return inb((int)base+Qpsr)&Fnotbusy;
214 }
215 
216 static void
lptintr(Ureg *,void *)217 lptintr(Ureg *, void *)
218 {
219 	wakeup(&lptrendez);
220 }
221 
222 Dev lptdevtab = {
223 	'L',
224 	"lpt",
225 
226 	devreset,
227 	devinit,
228 	devshutdown,
229 	lptattach,
230 	lptwalk,
231 	lptstat,
232 	lptopen,
233 	devcreate,
234 	lptclose,
235 	lptread,
236 	devbread,
237 	lptwrite,
238 	devbwrite,
239 	devremove,
240 	devwstat,
241 };
242