xref: /inferno-os/os/pc/devvga.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 /*
2  * VGA controller
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "../port/error.h"
10 
11 #define	Image	IMAGE
12 #include <draw.h>
13 #include <memdraw.h>
14 #include <cursor.h>
15 #include "screen.h"
16 
17 typedef struct Vgaseg Vgaseg;
18 struct Vgaseg {
19 	QLock;
20 	ulong	pa;
21 	ulong	len;
22 	void*	va;
23 };
24 
25 enum {
26 	Nvgaseg = 4,
27 
28 	Qdir = 0,
29 	Qvgactl,
30 	Qvgaovl,
31 	Qvgaovlctl,
32 
33 	Qsegs,
34 	Qmax = Qsegs+Nvgaseg
35 };
36 
37 static Dirtab vgadir[Qmax] = {
38 	".",	{ Qdir, 0, QTDIR },		0,	0550,
39 	"vgactl",		{ Qvgactl, 0 },		0,	0660,
40 	"vgaovl",		{ Qvgaovl, 0 },		0,	0660,
41 	"vgaovlctl",	{ Qvgaovlctl, 0 },	0, 	0660,
42 	/* dynamically-created memory segments are added here */
43 };
44 
45 static Vgaseg vgasegs[Nvgaseg];
46 static Lock vgadirlock;
47 static int nvgadir = Qsegs;
48 
49 enum {
50 	CMactualsize,
51 	CMblank,
52 	CMblanktime,
53 	CMdrawinit,
54 	CMhwaccel,
55 	CMhwblank,
56 	CMhwgc,
57 	CMlinear,
58 	CMpalettedepth,
59 	CMpanning,
60 	CMsize,
61 	CMtype,
62 	CMunblank,
63 };
64 
65 static Cmdtab vgactlmsg[] = {
66 	CMactualsize,	"actualsize",	2,
67 	CMblank,	"blank",	1,
68 	CMblanktime,	"blanktime",	2,
69 	CMdrawinit,	"drawinit",	1,
70 	CMhwaccel,	"hwaccel",	2,
71 	CMhwblank,	"hwblank",	2,
72 	CMhwgc,		"hwgc",		2,
73 	CMlinear,	"linear",	0,
74 	CMpalettedepth,	"palettedepth",	2,
75 	CMpanning,	"panning",	2,
76 	CMsize,		"size",		3,
77 	CMtype,		"type",		2,
78 	CMunblank,	"unblank",	1,
79 };
80 
81 static void
vgareset(void)82 vgareset(void)
83 {
84 	/* reserve the 'standard' vga registers */
85 	if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0)
86 		panic("vga ports already allocated");
87 	if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0)
88 		panic("vga ports already allocated");
89 	conf.monitor = 1;
90 }
91 
92 void
addvgaseg(char * name,ulong pa,ulong size)93 addvgaseg(char *name, ulong pa, ulong size)
94 {
95 	int i;
96 	Dirtab d;
97 	Vgaseg *s;
98 	ulong va;
99 
100 	va = mmukmap(pa, 0, size);
101 	if(va == 0)
102 		return;
103 	memset(&d, 0, sizeof(d));
104 	strecpy(d.name, d.name+sizeof(name), name);
105 	lock(&vgadirlock);
106 	for(i=0; i<nvgadir; i++)
107 		if(strcmp(vgadir[i].name, name) == 0){
108 			unlock(&vgadirlock);
109 			print("devvga: duplicate segment %s\n", name);
110 			return;
111 		}
112 	if(nvgadir >= nelem(vgadir)){
113 		unlock(&vgadirlock);
114 		print("devvga: segment %s: too many segments\n", name);
115 		return;
116 	}
117 	d.qid.path = nvgadir;
118 	d.perm = 0660;
119 	d.length = size;
120 	s = &vgasegs[nvgadir-Qsegs];
121 	s->pa = pa;
122 	s->len = size;
123 	s->va = (void*)va;
124 	vgadir[nvgadir] = d;
125 	nvgadir++;
126 	unlock(&vgadirlock);
127 }
128 
129 static long
vgasegrd(Vgaseg * s,uchar * buf,long n,ulong offset)130 vgasegrd(Vgaseg *s, uchar *buf, long n, ulong offset)
131 {
132 	int i;
133 	uchar *a, *d;
134 	ulong v;
135 
136 	if(offset >= s->len)
137 		return 0;
138 	if(offset+n > s->len)
139 		n = s->len - offset;
140 	d = (uchar*)s->va + offset;
141 	qlock(s);
142 	if(waserror()){
143 		qunlock(s);
144 		nexterror();
145 	}
146 	a = buf;
147 	while(n > 0){
148 		i = 4 - ((ulong)d & 3);
149 		if(i > n)
150 			i = n;
151 		if(i == 3)
152 			i = 2;
153 		switch(i){
154 		case 4:
155 			v = (a[3]<<24) | (a[2]<<16) | (a[1]<<8) | a[0];
156 			*(ulong*)d = v;
157 			break;
158 		case 2:
159 			v = (a[1]<<8) | a[0];
160 			*(ushort*)d = v;
161 			break;
162 		case 1:
163 			*d = *a;
164 			break;
165 		}
166 		d += i;
167 		a += i;
168 		n -= i;
169 	}
170 	poperror();
171 	qunlock(s);
172 	return a-buf;
173 }
174 
175 static long
vgasegwr(Vgaseg * s,uchar * buf,long n,ulong offset)176 vgasegwr(Vgaseg *s, uchar *buf, long n, ulong offset)
177 {
178 	int i;
179 	uchar *a, *r;
180 	ulong v;
181 
182 	if(offset >= s->len)
183 		return 0;
184 	if(offset+n > s->len)
185 		n = s->len - offset;
186 	r = (uchar*)s->va + offset;
187 	qlock(s);
188 	if(waserror()){
189 		qunlock(s);
190 		nexterror();
191 	}
192 	a = buf;
193 	while(n > 0){
194 		i = 4 - ((ulong)r & 3);
195 		if(i > n)
196 			i = n;
197 		if(i == 3)
198 			i = 2;
199 		switch(i){
200 		case 4:
201 			v = *(ulong*)r;
202 			a[0] = v;
203 			a[1] = v>>8;
204 			a[2] = v>>16;
205 			a[3] = v>>24;
206 			break;
207 		case 2:
208 			v = *(ushort*)r;
209 			a[0] = v;
210 			a[1] = v>>8;
211 			break;
212 		case 1:
213 			*a = *r;
214 			break;
215 		}
216 		r += i;
217 		a += i;
218 		n -= i;
219 	}
220 	poperror();
221 	qunlock(s);
222 	return a-buf;
223 }
224 
225 static Chan*
vgaattach(char * spec)226 vgaattach(char* spec)
227 {
228 	if(*spec && strcmp(spec, "0"))
229 		error(Eio);
230 	return devattach('v', spec);
231 }
232 
233 Walkqid*
vgawalk(Chan * c,Chan * nc,char ** name,int nname)234 vgawalk(Chan* c, Chan *nc, char** name, int nname)
235 {
236 	return devwalk(c, nc, name, nname, vgadir, nvgadir, devgen);
237 }
238 
239 static int
vgastat(Chan * c,uchar * dp,int n)240 vgastat(Chan* c, uchar* dp, int n)
241 {
242 	return devstat(c, dp, n, vgadir, nvgadir, devgen);
243 }
244 
245 static Chan*
vgaopen(Chan * c,int omode)246 vgaopen(Chan* c, int omode)
247 {
248 	VGAscr *scr;
249 	static char *openctl = "openctl\n";
250 
251 	scr = &vgascreen[0];
252 	if ((ulong)c->qid.path == Qvgaovlctl) {
253 		if (scr->dev && scr->dev->ovlctl)
254 			scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
255 		else
256 			error(Enonexist);
257 	}
258 	return devopen(c, omode, vgadir, nvgadir, devgen);
259 }
260 
261 static void
vgaclose(Chan * c)262 vgaclose(Chan* c)
263 {
264 	VGAscr *scr;
265 	static char *closectl = "closectl\n";
266 
267 	scr = &vgascreen[0];
268 	if((ulong)c->qid.path == Qvgaovlctl)
269 		if(scr->dev && scr->dev->ovlctl){
270 			if(waserror()){
271 				print("ovlctl error: %s\n", up->env->errstr);
272 				return;
273 			}
274 			scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
275 			poperror();
276 		}
277 }
278 
279 static void
checkport(int start,int end)280 checkport(int start, int end)
281 {
282 	/* standard vga regs are OK */
283 	if(start >= 0x2b0 && end <= 0x2df+1)
284 		return;
285 	if(start >= 0x3c0 && end <= 0x3da+1)
286 		return;
287 
288 	if(iounused(start, end))
289 		return;
290 	error(Eperm);
291 }
292 
293 static long
vgaread(Chan * c,void * a,long n,vlong off)294 vgaread(Chan* c, void* a, long n, vlong off)
295 {
296 	int len;
297 	char *p, *s;
298 	VGAscr *scr;
299 	ulong offset = off;
300 	char chbuf[30];
301 
302 	switch((ulong)c->qid.path){
303 
304 	case Qdir:
305 		return devdirread(c, a, n, vgadir, nvgadir, devgen);
306 
307 	case Qvgactl:
308 		scr = &vgascreen[0];
309 
310 		p = malloc(READSTR);
311 		if(waserror()){
312 			free(p);
313 			nexterror();
314 		}
315 
316 		len = 0;
317 
318 		if(scr->dev)
319 			s = scr->dev->name;
320 		else
321 			s = "cga";
322 		len += snprint(p+len, READSTR-len, "type %s\n", s);
323 
324 		if(scr->gscreen) {
325 			len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
326 				scr->gscreen->r.max.x, scr->gscreen->r.max.y,
327 				scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
328 
329 			if(Dx(scr->gscreen->r) != Dx(physgscreenr)
330 			|| Dy(scr->gscreen->r) != Dy(physgscreenr))
331 				len += snprint(p+len, READSTR-len, "actualsize %dx%d\n",
332 					physgscreenr.max.x, physgscreenr.max.y);
333 		}
334 
335 		len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
336 			blanktime, drawidletime(), scr->isblank ? "off" : "on");
337 		len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
338 		len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
339 		len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
340 		snprint(p+len, READSTR-len, "addr 0x%lux\n", scr->aperture);
341 		n = readstr(offset, a, n, p);
342 		poperror();
343 		free(p);
344 
345 		return n;
346 
347 	case Qvgaovl:
348 	case Qvgaovlctl:
349 		error(Ebadusefd);
350 		break;
351 
352 	default:
353 		if(c->qid.path < nvgadir)
354 			return vgasegrd(&vgasegs[c->qid.path], a, n, offset);
355 		error(Egreg);
356 		break;
357 	}
358 
359 	return 0;
360 }
361 
362 static char Ebusy[] = "vga already configured";
363 
364 static void
vgactl(Cmdbuf * cb)365 vgactl(Cmdbuf *cb)
366 {
367 	int align, i, size, x, y, z;
368 	char *chanstr, *p;
369 	ulong chan;
370 	Cmdtab *ct;
371 	VGAscr *scr;
372 	extern VGAdev *vgadev[];
373 	extern VGAcur *vgacur[];
374 
375 	scr = &vgascreen[0];
376 	ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
377 	switch(ct->index){
378 	case CMhwgc:
379 		if(strcmp(cb->f[1], "off") == 0){
380 			lock(&cursor);
381 			if(scr->cur){
382 				if(scr->cur->disable)
383 					scr->cur->disable(scr);
384 				scr->cur = nil;
385 			}
386 			unlock(&cursor);
387 			return;
388 		}
389 
390 		for(i = 0; vgacur[i]; i++){
391 			if(strcmp(cb->f[1], vgacur[i]->name))
392 				continue;
393 			lock(&cursor);
394 			if(scr->cur && scr->cur->disable)
395 				scr->cur->disable(scr);
396 			scr->cur = vgacur[i];
397 			if(scr->cur->enable)
398 				scr->cur->enable(scr);
399 			unlock(&cursor);
400 			return;
401 		}
402 		break;
403 
404 	case CMtype:
405 		for(i = 0; vgadev[i]; i++){
406 			if(strcmp(cb->f[1], vgadev[i]->name))
407 				continue;
408 			if(scr->dev && scr->dev->disable)
409 				scr->dev->disable(scr);
410 			scr->dev = vgadev[i];
411 			if(scr->dev->enable)
412 				scr->dev->enable(scr);
413 			return;
414 		}
415 		break;
416 
417 	case CMsize:
418 		if(drawhasclients())
419 			error(Ebusy);
420 
421 		x = strtoul(cb->f[1], &p, 0);
422 		if(x == 0 || x > 2048)
423 			error(Ebadarg);
424 		if(*p)
425 			p++;
426 
427 		y = strtoul(p, &p, 0);
428 		if(y == 0 || y > 2048)
429 			error(Ebadarg);
430 		if(*p)
431 			p++;
432 
433 		z = strtoul(p, &p, 0);
434 
435 		chanstr = cb->f[2];
436 		if((chan = strtochan(chanstr)) == 0)
437 			error("bad channel");
438 
439 		if(chantodepth(chan) != z)
440 			error("depth, channel do not match");
441 
442 		cursoroff(1);
443 		deletescreenimage();
444 		if(screensize(x, y, z, chan))
445 			error(Egreg);
446 		vgascreenwin(scr);
447 		cursoron(1);
448 		return;
449 
450 	case CMactualsize:
451 		if(scr->gscreen == nil)
452 			error("set the screen size first");
453 
454 		x = strtoul(cb->f[1], &p, 0);
455 		if(x == 0 || x > 2048)
456 			error(Ebadarg);
457 		if(*p)
458 			p++;
459 
460 		y = strtoul(p, nil, 0);
461 		if(y == 0 || y > 2048)
462 			error(Ebadarg);
463 
464 		if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
465 			error("physical screen bigger than virtual");
466 
467 		physgscreenr = Rect(0,0,x,y);
468 		scr->gscreen->clipr = physgscreenr;
469 		return;
470 
471 	case CMpalettedepth:
472 		x = strtoul(cb->f[1], &p, 0);
473 		if(x != 8 && x != 6)
474 			error(Ebadarg);
475 
476 		scr->palettedepth = x;
477 		return;
478 
479 	case CMdrawinit:
480 		memimagedraw(scr->gscreen, scr->gscreen->r, memblack, ZP, nil, ZP, S);
481 		if(scr && scr->dev && scr->dev->drawinit)
482 			scr->dev->drawinit(scr);
483 		return;
484 
485 	case CMlinear:
486 		if(cb->nf!=2 && cb->nf!=3)
487 			error(Ebadarg);
488 		size = strtoul(cb->f[1], 0, 0);
489 		if(cb->nf == 2)
490 			align = 0;
491 		else
492 			align = strtoul(cb->f[2], 0, 0);
493 		if(screenaperture(size, align))
494 			error("not enough free address space");
495 		return;
496 
497 	case CMblank:
498 		drawblankscreen(1);
499 		return;
500 
501 	case CMunblank:
502 		drawblankscreen(0);
503 		return;
504 
505 	case CMblanktime:
506 		blanktime = strtoul(cb->f[1], 0, 0);
507 		return;
508 
509 	case CMpanning:
510 		if(strcmp(cb->f[1], "on") == 0){
511 			if(scr == nil || scr->cur == nil)
512 				error("set screen first");
513 			if(!scr->cur->doespanning)
514 				error("panning not supported");
515 			scr->gscreen->clipr = scr->gscreen->r;
516 			panning = 1;
517 		}
518 		else if(strcmp(cb->f[1], "off") == 0){
519 			scr->gscreen->clipr = physgscreenr;
520 			panning = 0;
521 		}else
522 			break;
523 		return;
524 
525 	case CMhwaccel:
526 		if(strcmp(cb->f[1], "on") == 0)
527 			hwaccel = 1;
528 		else if(strcmp(cb->f[1], "off") == 0)
529 			hwaccel = 0;
530 		else
531 			break;
532 		return;
533 
534 	case CMhwblank:
535 		if(strcmp(cb->f[1], "on") == 0)
536 			hwblank = 1;
537 		else if(strcmp(cb->f[1], "off") == 0)
538 			hwblank = 0;
539 		else
540 			break;
541 		return;
542 	}
543 
544 	cmderror(cb, "bad VGA control message");
545 }
546 
547 char Enooverlay[] = "No overlay support";
548 
549 static long
vgawrite(Chan * c,void * a,long n,vlong off)550 vgawrite(Chan* c, void* a, long n, vlong off)
551 {
552 	ulong offset = off;
553 	Cmdbuf *cb;
554 	VGAscr *scr;
555 
556 	switch((ulong)c->qid.path){
557 
558 	case Qdir:
559 		error(Eperm);
560 
561 	case Qvgactl:
562 		if(offset || n >= READSTR)
563 			error(Ebadarg);
564 		cb = parsecmd(a, n);
565 		if(waserror()){
566 			free(cb);
567 			nexterror();
568 		}
569 		vgactl(cb);
570 		poperror();
571 		free(cb);
572 		return n;
573 
574 	case Qvgaovl:
575 		scr = &vgascreen[0];
576 		if (scr->dev == nil || scr->dev->ovlwrite == nil) {
577 			error(Enooverlay);
578 			break;
579 		}
580 		return scr->dev->ovlwrite(scr, a, n, off);
581 
582 	case Qvgaovlctl:
583 		scr = &vgascreen[0];
584 		if (scr->dev == nil || scr->dev->ovlctl == nil) {
585 			error(Enooverlay);
586 			break;
587 		}
588 		scr->dev->ovlctl(scr, c, a, n);
589 		return n;
590 
591 	default:
592 		if(c->qid.path < nvgadir)
593 			return vgasegwr(&vgasegs[c->qid.path], a, n, offset);
594 		error(Egreg);
595 		break;
596 	}
597 
598 	return 0;
599 }
600 
601 Dev vgadevtab = {
602 	'v',
603 	"vga",
604 
605 	vgareset,
606 	devinit,
607 	devshutdown,
608 	vgaattach,
609 	vgawalk,
610 	vgastat,
611 	vgaopen,
612 	devcreate,
613 	vgaclose,
614 	vgaread,
615 	devbread,
616 	vgawrite,
617 	devbwrite,
618 	devremove,
619 	devwstat,
620 };
621