diff options
Diffstat (limited to 'os/pc/devvga.c')
| -rw-r--r-- | os/pc/devvga.c | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/os/pc/devvga.c b/os/pc/devvga.c new file mode 100644 index 00000000..0d7a9f0d --- /dev/null +++ b/os/pc/devvga.c @@ -0,0 +1,620 @@ +/* + * 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 <draw.h> +#include <memdraw.h> +#include <cursor.h> +#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<nvgadir; i++) + if(strcmp(vgadir[i].name, name) == 0){ + unlock(&vgadirlock); + print("devvga: duplicate segment %s\n", name); + return; + } + if(nvgadir >= 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, +}; |
