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 "io.h"
10 #include "../port/error.h"
11
12 #define Image IMAGE
13 #include <draw.h>
14 #include <memdraw.h>
15 #include <cursor.h>
16 #include "screen.h"
17
18 enum {
19 Qdir,
20 Qvgabios,
21 Qvgactl,
22 Qvgaovl,
23 Qvgaovlctl,
24 };
25
26 static Dirtab vgadir[] = {
27 ".", { Qdir, 0, QTDIR }, 0, 0550,
28 "vgabios", { Qvgabios, 0 }, 0x100000, 0440,
29 "vgactl", { Qvgactl, 0 }, 0, 0660,
30 "vgaovl", { Qvgaovl, 0 }, 0, 0660,
31 "vgaovlctl", { Qvgaovlctl, 0 }, 0, 0660,
32 };
33
34 enum {
35 CMactualsize,
36 CMblank,
37 CMblanktime,
38 CMdrawinit,
39 CMhwaccel,
40 CMhwblank,
41 CMhwgc,
42 CMlinear,
43 CMpalettedepth,
44 CMpanning,
45 CMsize,
46 CMtextmode,
47 CMtype,
48 CMunblank,
49 };
50
51 static Cmdtab vgactlmsg[] = {
52 CMactualsize, "actualsize", 2,
53 CMblank, "blank", 1,
54 CMblanktime, "blanktime", 2,
55 CMdrawinit, "drawinit", 1,
56 CMhwaccel, "hwaccel", 2,
57 CMhwblank, "hwblank", 2,
58 CMhwgc, "hwgc", 2,
59 CMlinear, "linear", 0,
60 CMpalettedepth, "palettedepth", 2,
61 CMpanning, "panning", 2,
62 CMsize, "size", 3,
63 CMtextmode, "textmode", 1,
64 CMtype, "type", 2,
65 CMunblank, "unblank", 1,
66 };
67
68 static void
vgareset(void)69 vgareset(void)
70 {
71 /* reserve the 'standard' vga registers */
72 if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0)
73 panic("vga ports already allocated");
74 if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0)
75 panic("vga ports already allocated");
76 conf.monitor = 1;
77 }
78
79 static Chan*
vgaattach(char * spec)80 vgaattach(char* spec)
81 {
82 if(*spec && strcmp(spec, "0"))
83 error(Eio);
84 return devattach('v', spec);
85 }
86
87 Walkqid*
vgawalk(Chan * c,Chan * nc,char ** name,int nname)88 vgawalk(Chan* c, Chan *nc, char** name, int nname)
89 {
90 return devwalk(c, nc, name, nname, vgadir, nelem(vgadir), devgen);
91 }
92
93 static int
vgastat(Chan * c,uchar * dp,int n)94 vgastat(Chan* c, uchar* dp, int n)
95 {
96 return devstat(c, dp, n, vgadir, nelem(vgadir), devgen);
97 }
98
99 static Chan*
vgaopen(Chan * c,int omode)100 vgaopen(Chan* c, int omode)
101 {
102 VGAscr *scr;
103 static char *openctl = "openctl\n";
104
105 scr = &vgascreen[0];
106 if ((ulong)c->qid.path == Qvgaovlctl) {
107 if (scr->dev && scr->dev->ovlctl)
108 scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
109 else
110 error(Enonexist);
111 }
112 return devopen(c, omode, vgadir, nelem(vgadir), devgen);
113 }
114
115 static void
vgaclose(Chan * c)116 vgaclose(Chan* c)
117 {
118 VGAscr *scr;
119 static char *closectl = "closectl\n";
120
121 scr = &vgascreen[0];
122 if((ulong)c->qid.path == Qvgaovlctl)
123 if(scr->dev && scr->dev->ovlctl){
124 if(waserror()){
125 print("ovlctl error: %s\n", up->errstr);
126 return;
127 }
128 scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
129 poperror();
130 }
131 }
132
133 static void
checkport(int start,int end)134 checkport(int start, int end)
135 {
136 /* standard vga regs are OK */
137 if(start >= 0x2b0 && end <= 0x2df+1)
138 return;
139 if(start >= 0x3c0 && end <= 0x3da+1)
140 return;
141
142 if(iounused(start, end))
143 return;
144 error(Eperm);
145 }
146
147 static long
vgaread(Chan * c,void * a,long n,vlong off)148 vgaread(Chan* c, void* a, long n, vlong off)
149 {
150 int len;
151 char *p, *s;
152 VGAscr *scr;
153 ulong offset = off;
154 char chbuf[30];
155
156 switch((ulong)c->qid.path){
157
158 case Qdir:
159 return devdirread(c, a, n, vgadir, nelem(vgadir), devgen);
160
161 case Qvgabios:
162 if(offset >= 0x100000)
163 return 0;
164 if(offset+n >= 0x100000)
165 n = 0x100000 - offset;
166 memmove(a, (uchar*)kaddr(0)+offset, n);
167 return n;
168
169 case Qvgactl:
170 scr = &vgascreen[0];
171
172 p = malloc(READSTR);
173 if(p == nil)
174 error(Enomem);
175 if(waserror()){
176 free(p);
177 nexterror();
178 }
179
180 len = 0;
181
182 if(scr->dev)
183 s = scr->dev->name;
184 else
185 s = "cga";
186 len += snprint(p+len, READSTR-len, "type %s\n", s);
187
188 if(scr->gscreen) {
189 len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
190 scr->gscreen->r.max.x, scr->gscreen->r.max.y,
191 scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
192
193 if(Dx(scr->gscreen->r) != Dx(physgscreenr)
194 || Dy(scr->gscreen->r) != Dy(physgscreenr))
195 len += snprint(p+len, READSTR-len, "actualsize %dx%d\n",
196 physgscreenr.max.x, physgscreenr.max.y);
197 }
198
199 len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
200 blanktime, drawidletime(), scr->isblank ? "off" : "on");
201 len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
202 len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
203 len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
204 len += snprint(p+len, READSTR-len, "addr p 0x%lux v 0x%p size 0x%ux\n", scr->paddr, scr->vaddr, scr->apsize);
205 USED(len);
206
207 n = readstr(offset, a, n, p);
208 poperror();
209 free(p);
210
211 return n;
212
213 case Qvgaovl:
214 case Qvgaovlctl:
215 error(Ebadusefd);
216 break;
217
218 default:
219 error(Egreg);
220 break;
221 }
222
223 return 0;
224 }
225
226 static char Ebusy[] = "vga already configured";
227
228 static void
vgactl(Cmdbuf * cb)229 vgactl(Cmdbuf *cb)
230 {
231 int align, i, size, x, y, z;
232 char *chanstr, *p;
233 ulong chan;
234 Cmdtab *ct;
235 VGAscr *scr;
236 extern VGAdev *vgadev[];
237 extern VGAcur *vgacur[];
238
239 scr = &vgascreen[0];
240 ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
241 switch(ct->index){
242 case CMhwgc:
243 if(strcmp(cb->f[1], "off") == 0){
244 lock(&cursor);
245 if(scr->cur){
246 if(scr->cur->disable)
247 scr->cur->disable(scr);
248 scr->cur = nil;
249 }
250 unlock(&cursor);
251 return;
252 }
253 if(strcmp(cb->f[1], "soft") == 0){
254 lock(&cursor);
255 swcursorinit();
256 if(scr->cur && scr->cur->disable)
257 scr->cur->disable(scr);
258 scr->cur = &swcursor;
259 if(scr->cur->enable)
260 scr->cur->enable(scr);
261 unlock(&cursor);
262 return;
263 }
264 for(i = 0; vgacur[i]; i++){
265 if(strcmp(cb->f[1], vgacur[i]->name))
266 continue;
267 lock(&cursor);
268 if(scr->cur && scr->cur->disable)
269 scr->cur->disable(scr);
270 scr->cur = vgacur[i];
271 if(scr->cur->enable)
272 scr->cur->enable(scr);
273 unlock(&cursor);
274 return;
275 }
276 break;
277
278 case CMtype:
279 for(i = 0; vgadev[i]; i++){
280 if(strcmp(cb->f[1], vgadev[i]->name))
281 continue;
282 if(scr->dev && scr->dev->disable)
283 scr->dev->disable(scr);
284 scr->dev = vgadev[i];
285 if(scr->dev->enable)
286 scr->dev->enable(scr);
287 return;
288 }
289 break;
290
291 case CMtextmode:
292 screeninit();
293 return;
294
295 case CMsize:
296 x = strtoul(cb->f[1], &p, 0);
297 if(x == 0 || x > 10240)
298 error(Ebadarg);
299 if(*p)
300 p++;
301
302 y = strtoul(p, &p, 0);
303 if(y == 0 || y > 10240)
304 error(Ebadarg);
305 if(*p)
306 p++;
307
308 z = strtoul(p, &p, 0);
309
310 chanstr = cb->f[2];
311 if((chan = strtochan(chanstr)) == 0)
312 error("bad channel");
313
314 if(chantodepth(chan) != z)
315 error("depth, channel do not match");
316
317 cursoroff(1);
318 deletescreenimage();
319 if(screensize(x, y, z, chan))
320 error(Egreg);
321 vgascreenwin(scr);
322 resetscreenimage();
323 cursoron(1);
324 return;
325
326 case CMactualsize:
327 if(scr->gscreen == nil)
328 error("set the screen size first");
329
330 x = strtoul(cb->f[1], &p, 0);
331 if(x == 0 || x > 2048)
332 error(Ebadarg);
333 if(*p)
334 p++;
335
336 y = strtoul(p, nil, 0);
337 if(y == 0 || y > 2048)
338 error(Ebadarg);
339
340 if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
341 error("physical screen bigger than virtual");
342
343 physgscreenr = Rect(0,0,x,y);
344 scr->gscreen->clipr = physgscreenr;
345 return;
346
347 case CMpalettedepth:
348 x = strtoul(cb->f[1], &p, 0);
349 if(x != 8 && x != 6)
350 error(Ebadarg);
351
352 scr->palettedepth = x;
353 return;
354
355 case CMdrawinit:
356 if(scr->gscreen == nil)
357 error("drawinit: no gscreen");
358 if(scr->dev && scr->dev->drawinit)
359 scr->dev->drawinit(scr);
360 return;
361
362 case CMlinear:
363 if(cb->nf!=2 && cb->nf!=3)
364 error(Ebadarg);
365 size = strtoul(cb->f[1], 0, 0);
366 if(cb->nf == 2)
367 align = 0;
368 else
369 align = strtoul(cb->f[2], 0, 0);
370 if(screenaperture(size, align) < 0)
371 error("not enough free address space");
372 return;
373 /*
374 case CMmemset:
375 memset((void*)strtoul(cb->f[1], 0, 0), atoi(cb->f[2]), atoi(cb->f[3]));
376 return;
377 */
378
379 case CMblank:
380 drawblankscreen(1);
381 return;
382
383 case CMunblank:
384 drawblankscreen(0);
385 return;
386
387 case CMblanktime:
388 blanktime = strtoul(cb->f[1], 0, 0);
389 return;
390
391 case CMpanning:
392 if(strcmp(cb->f[1], "on") == 0){
393 if(scr == nil || scr->cur == nil)
394 error("set screen first");
395 if(!scr->cur->doespanning)
396 error("panning not supported");
397 scr->gscreen->clipr = scr->gscreen->r;
398 panning = 1;
399 }
400 else if(strcmp(cb->f[1], "off") == 0){
401 scr->gscreen->clipr = physgscreenr;
402 panning = 0;
403 }else
404 break;
405 return;
406
407 case CMhwaccel:
408 if(strcmp(cb->f[1], "on") == 0)
409 hwaccel = 1;
410 else if(strcmp(cb->f[1], "off") == 0)
411 hwaccel = 0;
412 else
413 break;
414 return;
415
416 case CMhwblank:
417 if(strcmp(cb->f[1], "on") == 0)
418 hwblank = 1;
419 else if(strcmp(cb->f[1], "off") == 0)
420 hwblank = 0;
421 else
422 break;
423 return;
424 }
425
426 cmderror(cb, "bad VGA control message");
427 }
428
429 char Enooverlay[] = "No overlay support";
430
431 static long
vgawrite(Chan * c,void * a,long n,vlong off)432 vgawrite(Chan* c, void* a, long n, vlong off)
433 {
434 ulong offset = off;
435 Cmdbuf *cb;
436 VGAscr *scr;
437
438 switch((ulong)c->qid.path){
439
440 case Qdir:
441 error(Eperm);
442
443 case Qvgactl:
444 if(offset || n >= READSTR)
445 error(Ebadarg);
446 cb = parsecmd(a, n);
447 if(waserror()){
448 free(cb);
449 nexterror();
450 }
451 vgactl(cb);
452 poperror();
453 free(cb);
454 return n;
455
456 case Qvgaovl:
457 scr = &vgascreen[0];
458 if (scr->dev == nil || scr->dev->ovlwrite == nil) {
459 error(Enooverlay);
460 break;
461 }
462 return scr->dev->ovlwrite(scr, a, n, off);
463
464 case Qvgaovlctl:
465 scr = &vgascreen[0];
466 if (scr->dev == nil || scr->dev->ovlctl == nil) {
467 error(Enooverlay);
468 break;
469 }
470 scr->dev->ovlctl(scr, c, a, n);
471 return n;
472
473 default:
474 error(Egreg);
475 break;
476 }
477
478 return 0;
479 }
480
481 Dev vgadevtab = {
482 'v',
483 "vga",
484
485 vgareset,
486 devinit,
487 devshutdown,
488 vgaattach,
489 vgawalk,
490 vgastat,
491 vgaopen,
492 devcreate,
493 vgaclose,
494 vgaread,
495 devbread,
496 vgawrite,
497 devbwrite,
498 devremove,
499 devwstat,
500 };
501