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