]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devvga.c
fix kernel: pio()/mfreeseg() race
[plan9front.git] / sys / src / 9 / pc / devvga.c
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
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*
80 vgaattach(char* spec)
81 {
82         if(*spec && strcmp(spec, "0"))
83                 error(Eio);
84         return devattach('v', spec);
85 }
86
87 Walkqid*
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
94 vgastat(Chan* c, uchar* dp, int n)
95 {
96         return devstat(c, dp, n, vgadir, nelem(vgadir), devgen);
97 }
98
99 static Chan*
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
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
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
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(waserror()){
174                         free(p);
175                         nexterror();
176                 }
177
178                 len = 0;
179
180                 if(scr->dev)
181                         s = scr->dev->name;
182                 else
183                         s = "cga";
184                 len += snprint(p+len, READSTR-len, "type %s\n", s);
185
186                 if(scr->gscreen) {
187                         len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
188                                 scr->gscreen->r.max.x, scr->gscreen->r.max.y,
189                                 scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
190
191                         if(Dx(scr->gscreen->r) != Dx(physgscreenr) 
192                         || Dy(scr->gscreen->r) != Dy(physgscreenr))
193                                 len += snprint(p+len, READSTR-len, "actualsize %dx%d\n",
194                                         physgscreenr.max.x, physgscreenr.max.y);
195                 }
196
197                 len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
198                         blanktime, drawidletime(), scr->isblank ? "off" : "on");
199                 len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
200                 len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
201                 len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
202                 len += snprint(p+len, READSTR-len, "addr p 0x%lux v 0x%p size 0x%ux\n", scr->paddr, scr->vaddr, scr->apsize);
203                 USED(len);
204
205                 n = readstr(offset, a, n, p);
206                 poperror();
207                 free(p);
208
209                 return n;
210
211         case Qvgaovl:
212         case Qvgaovlctl:
213                 error(Ebadusefd);
214                 break;
215
216         default:
217                 error(Egreg);
218                 break;
219         }
220
221         return 0;
222 }
223
224 static char Ebusy[] = "vga already configured";
225
226 static void
227 vgactl(Cmdbuf *cb)
228 {
229         int align, i, size, x, y, z;
230         char *chanstr, *p;
231         ulong chan;
232         Cmdtab *ct;
233         VGAscr *scr;
234         extern VGAdev *vgadev[];
235         extern VGAcur *vgacur[];
236
237         scr = &vgascreen[0];
238         ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
239         switch(ct->index){
240         case CMhwgc:
241                 if(strcmp(cb->f[1], "off") == 0){
242                         lock(&cursor);
243                         if(scr->cur){
244                                 if(scr->cur->disable)
245                                         scr->cur->disable(scr);
246                                 scr->cur = nil;
247                         }
248                         unlock(&cursor);
249                         return;
250                 }
251                 if(strcmp(cb->f[1], "soft") == 0){
252                         lock(&cursor);
253                         swcursorinit();
254                         if(scr->cur && scr->cur->disable)
255                                 scr->cur->disable(scr);
256                         scr->cur = &swcursor;
257                         if(scr->cur->enable)
258                                 scr->cur->enable(scr);
259                         unlock(&cursor);
260                         return;
261                 }
262                 for(i = 0; vgacur[i]; i++){
263                         if(strcmp(cb->f[1], vgacur[i]->name))
264                                 continue;
265                         lock(&cursor);
266                         if(scr->cur && scr->cur->disable)
267                                 scr->cur->disable(scr);
268                         scr->cur = vgacur[i];
269                         if(scr->cur->enable)
270                                 scr->cur->enable(scr);
271                         unlock(&cursor);
272                         return;
273                 }
274                 break;
275
276         case CMtype:
277                 for(i = 0; vgadev[i]; i++){
278                         if(strcmp(cb->f[1], vgadev[i]->name))
279                                 continue;
280                         if(scr->dev){
281                                 if(scr->dev->disable)
282                                         scr->dev->disable(scr);
283                                 scr->fill = nil;
284                                 scr->scroll = nil;
285                                 scr->blank = nil;
286                         }
287                         scr->dev = vgadev[i];
288                         if(scr->dev->enable)
289                                 scr->dev->enable(scr);
290                         return;
291                 }
292                 break;
293
294         case CMtextmode:
295                 screeninit();
296                 return;
297
298         case CMsize:
299                 x = strtoul(cb->f[1], &p, 0);
300                 if(x == 0 || x > 10240)
301                         error(Ebadarg);
302                 if(*p)
303                         p++;
304
305                 y = strtoul(p, &p, 0);
306                 if(y == 0 || y > 10240)
307                         error(Ebadarg);
308                 if(*p)
309                         p++;
310
311                 z = strtoul(p, &p, 0);
312
313                 chanstr = cb->f[2];
314                 if((chan = strtochan(chanstr)) == 0)
315                         error("bad channel");
316
317                 if(chantodepth(chan) != z)
318                         error("depth, channel do not match");
319
320                 cursoroff(1);
321                 deletescreenimage();
322                 if(screensize(x, y, z, chan))
323                         error(Egreg);
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                 hwblank = scr->blank != nil;
361                 hwaccel = scr->scroll || scr->fill;
362                 vgascreenwin(scr);
363                 resetscreenimage();
364                 cursoron(1);
365                 return;
366         
367         case CMlinear:
368                 if(cb->nf!=2 && cb->nf!=3)
369                         error(Ebadarg);
370                 size = strtoul(cb->f[1], 0, 0);
371                 if(cb->nf == 2)
372                         align = 0;
373                 else
374                         align = strtoul(cb->f[2], 0, 0);
375                 if(screenaperture(size, align) < 0)
376                         error("not enough free address space");
377                 return;
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
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 };