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