xref: /plan9/sys/src/9/mtx/devarch.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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 typedef struct IOMap IOMap;
10 struct IOMap
11 {
12 	IOMap	*next;
13 	char	tag[13];
14 	ulong	start;
15 	ulong	end;
16 };
17 
18 static struct
19 {
20 	Lock;
21 	IOMap	*m;
22 	IOMap	*free;
23 	IOMap	maps[32];		// some initial free maps
24 
25 	QLock	ql;			// lock for reading map
26 } iomap;
27 
28 enum {
29 	Qdir = 0,
30 	Qioalloc = 1,
31 	Qiob,
32 	Qiow,
33 	Qiol,
34 	Qbase,
35 
36 	Qmax = 16,
37 };
38 
39 typedef long Rdwrfn(Chan*, void*, long, vlong);
40 
41 static Rdwrfn *readfn[Qmax];
42 static Rdwrfn *writefn[Qmax];
43 
44 static Dirtab archdir[] = {
45 	".",	{ Qdir, 0, QTDIR },	0,	0555,
46 	"ioalloc",	{ Qioalloc, 0 },	0,	0444,
47 	"iob",		{ Qiob, 0 },		0,	0660,
48 	"iow",		{ Qiow, 0 },		0,	0660,
49 	"iol",		{ Qiol, 0 },		0,	0660,
50 };
51 Lock archwlock;	/* the lock is only for changing archdir */
52 int narchdir = Qbase;
53 int (*_pcmspecial)(char *, ISAConf *);
54 void (*_pcmspecialclose)(int);
55 
56 /*
57  * Add a file to the #P listing.  Once added, you can't delete it.
58  * You can't add a file with the same name as one already there,
59  * and you get a pointer to the Dirtab entry so you can do things
60  * like change the Qid version.  Changing the Qid path is disallowed.
61  */
62 Dirtab*
addarchfile(char * name,int perm,Rdwrfn * rdfn,Rdwrfn * wrfn)63 addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
64 {
65 	int i;
66 	Dirtab d;
67 	Dirtab *dp;
68 
69 	memset(&d, 0, sizeof d);
70 	strcpy(d.name, name);
71 	d.perm = perm;
72 
73 	lock(&archwlock);
74 	if(narchdir >= Qmax){
75 		unlock(&archwlock);
76 		return nil;
77 	}
78 
79 	for(i=0; i<narchdir; i++)
80 		if(strcmp(archdir[i].name, name) == 0){
81 			unlock(&archwlock);
82 			return nil;
83 		}
84 
85 	d.qid.path = narchdir;
86 	archdir[narchdir] = d;
87 	readfn[narchdir] = rdfn;
88 	writefn[narchdir] = wrfn;
89 	dp = &archdir[narchdir++];
90 	unlock(&archwlock);
91 
92 	return dp;
93 }
94 
95 void
ioinit(void)96 ioinit(void)
97 {
98 	int i;
99 
100 	for(i = 0; i < nelem(iomap.maps)-1; i++)
101 		iomap.maps[i].next = &iomap.maps[i+1];
102 	iomap.maps[i].next = nil;
103 	iomap.free = iomap.maps;
104 
105 	// a dummy entry at 2^17
106 	ioalloc(0x20000, 1, 0, "dummy");
107 }
108 
109 //
110 //	alloc some io port space and remember who it was
111 //	alloced to.  if port < 0, find a free region.
112 //
113 int
ioalloc(int port,int size,int align,char * tag)114 ioalloc(int port, int size, int align, char *tag)
115 {
116 	IOMap *m, **l;
117 	int i;
118 
119 	lock(&iomap);
120 	if(port < 0){
121 		// find a free port above 0x400 and below 0x1000
122 		port = 0x400;
123 		for(l = &iomap.m; *l; l = &(*l)->next){
124 			m = *l;
125 			i = m->start - port;
126 			if(i > size)
127 				break;
128 			if(align > 0)
129 				port = ((port+align-1)/align)*align;
130 			else
131 				port = m->end;
132 		}
133 		if(*l == nil){
134 			unlock(&iomap);
135 			return -1;
136 		}
137 	} else {
138 		// see if the space clashes with previously allocated ports
139 		for(l = &iomap.m; *l; l = &(*l)->next){
140 			m = *l;
141 			if(m->end <= port)
142 				continue;
143 			if(m->start >= port+size)
144 				break;
145 			unlock(&iomap);
146 			return -1;
147 		}
148 	}
149 	m = iomap.free;
150 	if(m == nil){
151 		print("ioalloc: out of maps");
152 		unlock(&iomap);
153 		return port;
154 	}
155 	iomap.free = m->next;
156 	m->next = *l;
157 	m->start = port;
158 	m->end = port + size;
159 	strncpy(m->tag, tag, sizeof(m->tag));
160 	m->tag[sizeof(m->tag)-1] = 0;
161 	*l = m;
162 
163 	archdir[0].qid.vers++;
164 
165 	unlock(&iomap);
166 	return m->start;
167 }
168 
169 void
iofree(int port)170 iofree(int port)
171 {
172 	IOMap *m, **l;
173 
174 	lock(&iomap);
175 	for(l = &iomap.m; *l; l = &(*l)->next){
176 		if((*l)->start == port){
177 			m = *l;
178 			*l = m->next;
179 			m->next = iomap.free;
180 			iomap.free = m;
181 			break;
182 		}
183 		if((*l)->start > port)
184 			break;
185 	}
186 	archdir[0].qid.vers++;
187 	unlock(&iomap);
188 }
189 
190 int
iounused(int start,int end)191 iounused(int start, int end)
192 {
193 	IOMap *m;
194 
195 	for(m = iomap.m; m; m = m->next){
196 		if(start >= m->start && start < m->end
197 		|| start <= m->start && end > m->start)
198 			return 0;
199 	}
200 	return 1;
201 }
202 
203 static void
checkport(int start,int end)204 checkport(int start, int end)
205 {
206 	/* standard vga regs are OK */
207 	if(start >= 0x2b0 && end <= 0x2df+1)
208 		return;
209 	if(start >= 0x3c0 && end <= 0x3da+1)
210 		return;
211 
212 	if(iounused(start, end))
213 		return;
214 	error(Eperm);
215 }
216 
217 static Chan*
archattach(char * spec)218 archattach(char* spec)
219 {
220 	return devattach('P', spec);
221 }
222 
223 Walkqid*
archwalk(Chan * c,Chan * nc,char ** name,int nname)224 archwalk(Chan* c, Chan *nc, char** name, int nname)
225 {
226 	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
227 }
228 
229 static int
archstat(Chan * c,uchar * dp,int n)230 archstat(Chan* c, uchar* dp, int n)
231 {
232 	return devstat(c, dp, n, archdir, narchdir, devgen);
233 }
234 
235 static Chan*
archopen(Chan * c,int omode)236 archopen(Chan* c, int omode)
237 {
238 	return devopen(c, omode, archdir, nelem(archdir), devgen);
239 }
240 
241 static void
archclose(Chan *)242 archclose(Chan*)
243 {
244 }
245 
246 enum
247 {
248 	Linelen= 31,
249 };
250 
251 static long
archread(Chan * c,void * a,long n,vlong offset)252 archread(Chan *c, void *a, long n, vlong offset)
253 {
254 	char buf[Linelen+1], *p;
255 	int port;
256 	ushort *sp;
257 	ulong *lp;
258 	IOMap *m;
259 	Rdwrfn *fn;
260 
261 	switch((ulong)c->qid.path){
262 
263 	case Qdir:
264 		return devdirread(c, a, n, archdir, nelem(archdir), devgen);
265 
266 	case Qiob:
267 		port = offset;
268 		checkport(offset, offset+n);
269 		for(p = a; port < offset+n; port++)
270 			*p++ = inb(port);
271 		return n;
272 
273 	case Qiow:
274 		if((n & 0x01) || (offset & 0x01))
275 			error(Ebadarg);
276 		checkport(offset, offset+n+1);
277 		n /= 2;
278 		sp = a;
279 		for(port = offset; port < offset+n; port += 2)
280 			*sp++ = ins(port);
281 		return n*2;
282 
283 	case Qiol:
284 		if((n & 0x03) || (offset & 0x03))
285 			error(Ebadarg);
286 		checkport(offset, offset+n+3);
287 		n /= 4;
288 		lp = a;
289 		for(port = offset; port < offset+n; port += 4)
290 			*lp++ = inl(port);
291 		return n*4;
292 
293 	case Qioalloc:
294 		break;
295 
296 	default:
297 		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
298 			return fn(c, a, n, offset);
299 		error(Eperm);
300 		break;
301 	}
302 
303 	offset = offset/Linelen;
304 	n = n/Linelen;
305 	p = a;
306 	lock(&iomap);
307 	for(m = iomap.m; n > 0 && m != nil; m = m->next){
308 		if(offset-- > 0)
309 			continue;
310 		if(strcmp(m->tag, "dummy") == 0)
311 			break;
312 		sprint(buf, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag);
313 		memmove(p, buf, Linelen);
314 		p += Linelen;
315 		n--;
316 	}
317 	unlock(&iomap);
318 
319 	return p - (char*)a;
320 }
321 
322 static long
archwrite(Chan * c,void * a,long n,vlong offset)323 archwrite(Chan *c, void *a, long n, vlong offset)
324 {
325 	char *p;
326 	int port;
327 	ushort *sp;
328 	ulong *lp;
329 	Rdwrfn *fn;
330 
331 	switch((ulong)c->qid.path){
332 
333 	case Qiob:
334 		p = a;
335 		checkport(offset, offset+n);
336 		for(port = offset; port < offset+n; port++)
337 			outb(port, *p++);
338 		return n;
339 
340 	case Qiow:
341 		if((n & 01) || (offset & 01))
342 			error(Ebadarg);
343 		checkport(offset, offset+n+1);
344 		n /= 2;
345 		sp = a;
346 		for(port = offset; port < offset+n; port += 2)
347 			outs(port, *sp++);
348 		return n*2;
349 
350 	case Qiol:
351 		if((n & 0x03) || (offset & 0x03))
352 			error(Ebadarg);
353 		checkport(offset, offset+n+3);
354 		n /= 4;
355 		lp = a;
356 		for(port = offset; port < offset+n; port += 4)
357 			outl(port, *lp++);
358 		return n*4;
359 
360 	default:
361 		if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
362 			return fn(c, a, n, offset);
363 		error(Eperm);
364 		break;
365 	}
366 	return 0;
367 }
368 
369 Dev archdevtab = {
370 	'P',
371 	"arch",
372 
373 	devreset,
374 	devinit,
375 	devshutdown,
376 	archattach,
377 	archwalk,
378 	archstat,
379 	archopen,
380 	devcreate,
381 	archclose,
382 	archread,
383 	devbread,
384 	archwrite,
385 	devbwrite,
386 	devremove,
387 	devwstat,
388 };
389 
390 int
pcmspecial(char * idstr,ISAConf * isa)391 pcmspecial(char *idstr, ISAConf *isa)
392 {
393 	return (_pcmspecial  != nil)? _pcmspecial(idstr, isa): -1;
394 }
395 
396 void
pcmspecialclose(int a)397 pcmspecialclose(int a)
398 {
399 	if (_pcmspecialclose != nil)
400 		_pcmspecialclose(a);
401 }
402