/* * VGA controller */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #define Image IMAGE #include #include #include #include "screen.h" typedef struct Vgaseg Vgaseg; struct Vgaseg { QLock; ulong pa; ulong len; void* va; }; enum { Nvgaseg = 4, Qdir = 0, Qvgactl, Qvgaovl, Qvgaovlctl, Qsegs, Qmax = Qsegs+Nvgaseg }; static Dirtab vgadir[Qmax] = { ".", { Qdir, 0, QTDIR }, 0, 0550, "vgactl", { Qvgactl, 0 }, 0, 0660, "vgaovl", { Qvgaovl, 0 }, 0, 0660, "vgaovlctl", { Qvgaovlctl, 0 }, 0, 0660, /* dynamically-created memory segments are added here */ }; static Vgaseg vgasegs[Nvgaseg]; static Lock vgadirlock; static int nvgadir = Qsegs; enum { CMactualsize, CMblank, CMblanktime, CMdrawinit, CMhwaccel, CMhwblank, CMhwgc, CMlinear, CMpalettedepth, CMpanning, CMsize, CMtype, CMunblank, }; static Cmdtab vgactlmsg[] = { CMactualsize, "actualsize", 2, CMblank, "blank", 1, CMblanktime, "blanktime", 2, CMdrawinit, "drawinit", 1, CMhwaccel, "hwaccel", 2, CMhwblank, "hwblank", 2, CMhwgc, "hwgc", 2, CMlinear, "linear", 0, CMpalettedepth, "palettedepth", 2, CMpanning, "panning", 2, CMsize, "size", 3, CMtype, "type", 2, CMunblank, "unblank", 1, }; static void vgareset(void) { /* reserve the 'standard' vga registers */ if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0) panic("vga ports already allocated"); if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0) panic("vga ports already allocated"); conf.monitor = 1; } void addvgaseg(char *name, ulong pa, ulong size) { int i; Dirtab d; Vgaseg *s; ulong va; va = mmukmap(pa, 0, size); if(va == 0) return; memset(&d, 0, sizeof(d)); strecpy(d.name, d.name+sizeof(name), name); lock(&vgadirlock); for(i=0; i= nelem(vgadir)){ unlock(&vgadirlock); print("devvga: segment %s: too many segments\n", name); return; } d.qid.path = nvgadir; d.perm = 0660; d.length = size; s = &vgasegs[nvgadir-Qsegs]; s->pa = pa; s->len = size; s->va = (void*)va; vgadir[nvgadir] = d; nvgadir++; unlock(&vgadirlock); } static long vgasegrd(Vgaseg *s, uchar *buf, long n, ulong offset) { int i; uchar *a, *d; ulong v; if(offset >= s->len) return 0; if(offset+n > s->len) n = s->len - offset; d = (uchar*)s->va + offset; qlock(s); if(waserror()){ qunlock(s); nexterror(); } a = buf; while(n > 0){ i = 4 - ((ulong)d & 3); if(i > n) i = n; if(i == 3) i = 2; switch(i){ case 4: v = (a[3]<<24) | (a[2]<<16) | (a[1]<<8) | a[0]; *(ulong*)d = v; break; case 2: v = (a[1]<<8) | a[0]; *(ushort*)d = v; break; case 1: *d = *a; break; } d += i; a += i; n -= i; } poperror(); qunlock(s); return a-buf; } static long vgasegwr(Vgaseg *s, uchar *buf, long n, ulong offset) { int i; uchar *a, *r; ulong v; if(offset >= s->len) return 0; if(offset+n > s->len) n = s->len - offset; r = (uchar*)s->va + offset; qlock(s); if(waserror()){ qunlock(s); nexterror(); } a = buf; while(n > 0){ i = 4 - ((ulong)r & 3); if(i > n) i = n; if(i == 3) i = 2; switch(i){ case 4: v = *(ulong*)r; a[0] = v; a[1] = v>>8; a[2] = v>>16; a[3] = v>>24; break; case 2: v = *(ushort*)r; a[0] = v; a[1] = v>>8; break; case 1: *a = *r; break; } r += i; a += i; n -= i; } poperror(); qunlock(s); return a-buf; } static Chan* vgaattach(char* spec) { if(*spec && strcmp(spec, "0")) error(Eio); return devattach('v', spec); } Walkqid* vgawalk(Chan* c, Chan *nc, char** name, int nname) { return devwalk(c, nc, name, nname, vgadir, nvgadir, devgen); } static int vgastat(Chan* c, uchar* dp, int n) { return devstat(c, dp, n, vgadir, nvgadir, devgen); } static Chan* vgaopen(Chan* c, int omode) { VGAscr *scr; static char *openctl = "openctl\n"; scr = &vgascreen[0]; if ((ulong)c->qid.path == Qvgaovlctl) { if (scr->dev && scr->dev->ovlctl) scr->dev->ovlctl(scr, c, openctl, strlen(openctl)); else error(Enonexist); } return devopen(c, omode, vgadir, nvgadir, devgen); } static void vgaclose(Chan* c) { VGAscr *scr; static char *closectl = "closectl\n"; scr = &vgascreen[0]; if((ulong)c->qid.path == Qvgaovlctl) if(scr->dev && scr->dev->ovlctl){ if(waserror()){ print("ovlctl error: %s\n", up->env->errstr); return; } scr->dev->ovlctl(scr, c, closectl, strlen(closectl)); poperror(); } } static void checkport(int start, int end) { /* standard vga regs are OK */ if(start >= 0x2b0 && end <= 0x2df+1) return; if(start >= 0x3c0 && end <= 0x3da+1) return; if(iounused(start, end)) return; error(Eperm); } static long vgaread(Chan* c, void* a, long n, vlong off) { int len; char *p, *s; VGAscr *scr; ulong offset = off; char chbuf[30]; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, a, n, vgadir, nvgadir, devgen); case Qvgactl: scr = &vgascreen[0]; p = malloc(READSTR); if(waserror()){ free(p); nexterror(); } len = 0; if(scr->dev) s = scr->dev->name; else s = "cga"; len += snprint(p+len, READSTR-len, "type %s\n", s); if(scr->gscreen) { len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n", scr->gscreen->r.max.x, scr->gscreen->r.max.y, scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan)); if(Dx(scr->gscreen->r) != Dx(physgscreenr) || Dy(scr->gscreen->r) != Dy(physgscreenr)) len += snprint(p+len, READSTR-len, "actualsize %dx%d\n", physgscreenr.max.x, physgscreenr.max.y); } len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n", blanktime, drawidletime(), scr->isblank ? "off" : "on"); len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off"); len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off"); len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off"); snprint(p+len, READSTR-len, "addr 0x%lux\n", scr->aperture); n = readstr(offset, a, n, p); poperror(); free(p); return n; case Qvgaovl: case Qvgaovlctl: error(Ebadusefd); break; default: if(c->qid.path < nvgadir) return vgasegrd(&vgasegs[c->qid.path], a, n, offset); error(Egreg); break; } return 0; } static char Ebusy[] = "vga already configured"; static void vgactl(Cmdbuf *cb) { int align, i, size, x, y, z; char *chanstr, *p; ulong chan; Cmdtab *ct; VGAscr *scr; extern VGAdev *vgadev[]; extern VGAcur *vgacur[]; scr = &vgascreen[0]; ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg)); switch(ct->index){ case CMhwgc: if(strcmp(cb->f[1], "off") == 0){ lock(&cursor); if(scr->cur){ if(scr->cur->disable) scr->cur->disable(scr); scr->cur = nil; } unlock(&cursor); return; } for(i = 0; vgacur[i]; i++){ if(strcmp(cb->f[1], vgacur[i]->name)) continue; lock(&cursor); if(scr->cur && scr->cur->disable) scr->cur->disable(scr); scr->cur = vgacur[i]; if(scr->cur->enable) scr->cur->enable(scr); unlock(&cursor); return; } break; case CMtype: for(i = 0; vgadev[i]; i++){ if(strcmp(cb->f[1], vgadev[i]->name)) continue; if(scr->dev && scr->dev->disable) scr->dev->disable(scr); scr->dev = vgadev[i]; if(scr->dev->enable) scr->dev->enable(scr); return; } break; case CMsize: if(drawhasclients()) error(Ebusy); x = strtoul(cb->f[1], &p, 0); if(x == 0 || x > 2048) error(Ebadarg); if(*p) p++; y = strtoul(p, &p, 0); if(y == 0 || y > 2048) error(Ebadarg); if(*p) p++; z = strtoul(p, &p, 0); chanstr = cb->f[2]; if((chan = strtochan(chanstr)) == 0) error("bad channel"); if(chantodepth(chan) != z) error("depth, channel do not match"); cursoroff(1); deletescreenimage(); if(screensize(x, y, z, chan)) error(Egreg); vgascreenwin(scr); cursoron(1); return; case CMactualsize: if(scr->gscreen == nil) error("set the screen size first"); x = strtoul(cb->f[1], &p, 0); if(x == 0 || x > 2048) error(Ebadarg); if(*p) p++; y = strtoul(p, nil, 0); if(y == 0 || y > 2048) error(Ebadarg); if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y) error("physical screen bigger than virtual"); physgscreenr = Rect(0,0,x,y); scr->gscreen->clipr = physgscreenr; return; case CMpalettedepth: x = strtoul(cb->f[1], &p, 0); if(x != 8 && x != 6) error(Ebadarg); scr->palettedepth = x; return; case CMdrawinit: memimagedraw(scr->gscreen, scr->gscreen->r, memblack, ZP, nil, ZP, S); if(scr && scr->dev && scr->dev->drawinit) scr->dev->drawinit(scr); return; case CMlinear: if(cb->nf!=2 && cb->nf!=3) error(Ebadarg); size = strtoul(cb->f[1], 0, 0); if(cb->nf == 2) align = 0; else align = strtoul(cb->f[2], 0, 0); if(screenaperture(size, align)) error("not enough free address space"); return; case CMblank: drawblankscreen(1); return; case CMunblank: drawblankscreen(0); return; case CMblanktime: blanktime = strtoul(cb->f[1], 0, 0); return; case CMpanning: if(strcmp(cb->f[1], "on") == 0){ if(scr == nil || scr->cur == nil) error("set screen first"); if(!scr->cur->doespanning) error("panning not supported"); scr->gscreen->clipr = scr->gscreen->r; panning = 1; } else if(strcmp(cb->f[1], "off") == 0){ scr->gscreen->clipr = physgscreenr; panning = 0; }else break; return; case CMhwaccel: if(strcmp(cb->f[1], "on") == 0) hwaccel = 1; else if(strcmp(cb->f[1], "off") == 0) hwaccel = 0; else break; return; case CMhwblank: if(strcmp(cb->f[1], "on") == 0) hwblank = 1; else if(strcmp(cb->f[1], "off") == 0) hwblank = 0; else break; return; } cmderror(cb, "bad VGA control message"); } char Enooverlay[] = "No overlay support"; static long vgawrite(Chan* c, void* a, long n, vlong off) { ulong offset = off; Cmdbuf *cb; VGAscr *scr; switch((ulong)c->qid.path){ case Qdir: error(Eperm); case Qvgactl: if(offset || n >= READSTR) error(Ebadarg); cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } vgactl(cb); poperror(); free(cb); return n; case Qvgaovl: scr = &vgascreen[0]; if (scr->dev == nil || scr->dev->ovlwrite == nil) { error(Enooverlay); break; } return scr->dev->ovlwrite(scr, a, n, off); case Qvgaovlctl: scr = &vgascreen[0]; if (scr->dev == nil || scr->dev->ovlctl == nil) { error(Enooverlay); break; } scr->dev->ovlctl(scr, c, a, n); return n; default: if(c->qid.path < nvgadir) return vgasegwr(&vgasegs[c->qid.path], a, n, offset); error(Egreg); break; } return 0; } Dev vgadevtab = { 'v', "vga", vgareset, devinit, devshutdown, vgaattach, vgawalk, vgastat, vgaopen, devcreate, vgaclose, vgaread, devbread, vgawrite, devbwrite, devremove, devwstat, };