![cinap_lenrek](/assets/img/avatar_default.png)
the software cursor starts flickering and reacts bumby if a process spends most of its time with drawlock acquired because the timer interrupt thats supposed to redraw the cursor fails to acquire the lock at the time the timer fires. instead of trying to draw the cursor on the screen from a timer interrupt 30 times per second, devmouse now creates a process calling cursoron() and cursoroff() when the cursor needs to be redrawn. this allows the swcursor to schedule a redraw while holding the drawlock in swcursoravoid() and cursoron()/cursoroff() are now able to wait for a qlock (drawlock) because they get called from process context. the overall responsiveness is also improved with this change as the cursor redraw rate isnt limited to 30 times a second anymore.
551 lines
10 KiB
C
551 lines
10 KiB
C
/*
|
|
* VGA controller
|
|
*/
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
|
|
#define Image IMAGE
|
|
#include <draw.h>
|
|
#include <memdraw.h>
|
|
#include <cursor.h>
|
|
#include "screen.h"
|
|
|
|
enum {
|
|
Qdir,
|
|
Qvgabios,
|
|
Qvgactl,
|
|
Qvgaovl,
|
|
Qvgaovlctl,
|
|
};
|
|
|
|
static Dirtab vgadir[] = {
|
|
".", { Qdir, 0, QTDIR }, 0, 0550,
|
|
"vgabios", { Qvgabios, 0 }, 0x100000, 0440,
|
|
"vgactl", { Qvgactl, 0 }, 0, 0660,
|
|
"vgaovl", { Qvgaovl, 0 }, 0, 0660,
|
|
"vgaovlctl", { Qvgaovlctl, 0 }, 0, 0660,
|
|
};
|
|
|
|
enum {
|
|
CMactualsize,
|
|
CMblank,
|
|
CMblanktime,
|
|
CMdrawinit,
|
|
CMhwaccel,
|
|
CMhwblank,
|
|
CMhwgc,
|
|
CMlinear,
|
|
CMpalettedepth,
|
|
CMpanning,
|
|
CMsize,
|
|
CMtextmode,
|
|
CMtype,
|
|
CMunblank,
|
|
CMsoftscreen,
|
|
CMpcidev,
|
|
};
|
|
|
|
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,
|
|
CMtextmode, "textmode", 1,
|
|
CMtype, "type", 2,
|
|
CMunblank, "unblank", 1,
|
|
CMsoftscreen, "softscreen", 2,
|
|
CMpcidev, "pcidev", 2,
|
|
};
|
|
|
|
static void
|
|
vgareset(void)
|
|
{
|
|
Pcidev *pci;
|
|
VGAscr *scr;
|
|
|
|
/* 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");
|
|
|
|
/* find graphics card pci device */
|
|
scr = &vgascreen[0];
|
|
scr->pci = pci = nil;
|
|
while((pci = pcimatch(pci, 0, 0)) != nil){
|
|
if(pci->ccrb == Pcibcdisp){
|
|
scr->pci = pci;
|
|
break;
|
|
}
|
|
}
|
|
|
|
conf.monitor = 1;
|
|
}
|
|
|
|
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, nelem(vgadir), devgen);
|
|
}
|
|
|
|
static int
|
|
vgastat(Chan* c, uchar* dp, int n)
|
|
{
|
|
return devstat(c, dp, n, vgadir, nelem(vgadir), 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, nelem(vgadir), 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->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, nelem(vgadir), devgen);
|
|
|
|
case Qvgabios:
|
|
if(offset >= 0x100000)
|
|
return 0;
|
|
if(offset+n >= 0x100000)
|
|
n = 0x100000 - offset;
|
|
memmove(a, (uchar*)kaddr(0)+offset, n);
|
|
return n;
|
|
|
|
case Qvgactl:
|
|
scr = &vgascreen[0];
|
|
|
|
p = smalloc(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");
|
|
len += snprint(p+len, READSTR-len, "addr p 0x%lux v 0x%p size 0x%ux\n", scr->paddr, scr->vaddr, scr->apsize);
|
|
len += snprint(p+len, READSTR-len, "softscreen %s\n", scr->softscreen ? "on" : "off");
|
|
USED(len);
|
|
|
|
n = readstr(offset, a, n, p);
|
|
poperror();
|
|
free(p);
|
|
|
|
return n;
|
|
|
|
case Qvgaovl:
|
|
case Qvgaovlctl:
|
|
error(Ebadusefd);
|
|
break;
|
|
|
|
default:
|
|
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(scr->gscreen == nil)
|
|
error("hwgc: no gscreen");
|
|
|
|
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;
|
|
}
|
|
if(strcmp(cb->f[1], "soft") == 0){
|
|
lock(&cursor);
|
|
swcursorinit();
|
|
if(scr->cur && scr->cur->disable)
|
|
scr->cur->disable(scr);
|
|
scr->cur = &swcursor;
|
|
if(scr->cur->enable)
|
|
scr->cur->enable(scr);
|
|
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 CMpcidev:
|
|
if(cb->nf == 2){
|
|
Pcidev *p;
|
|
|
|
if((p = pcimatchtbdf(strtoul(cb->f[1], 0, 16))) != nil)
|
|
scr->pci = p;
|
|
} else
|
|
error(Ebadarg);
|
|
return;
|
|
|
|
case CMtype:
|
|
for(i = 0; vgadev[i]; i++){
|
|
if(strcmp(cb->f[1], vgadev[i]->name))
|
|
continue;
|
|
if(scr->dev){
|
|
if(scr->dev->disable)
|
|
scr->dev->disable(scr);
|
|
scr->fill = nil;
|
|
scr->scroll = nil;
|
|
scr->blank = nil;
|
|
}
|
|
scr->dev = vgadev[i];
|
|
if(scr->dev->enable)
|
|
scr->dev->enable(scr);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case CMtextmode:
|
|
screeninit();
|
|
return;
|
|
|
|
case CMsize:
|
|
x = strtoul(cb->f[1], &p, 0);
|
|
if(x == 0 || x > 10240)
|
|
error(Ebadarg);
|
|
if(*p)
|
|
p++;
|
|
|
|
y = strtoul(p, &p, 0);
|
|
if(y == 0 || y > 10240)
|
|
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();
|
|
deletescreenimage();
|
|
if(screensize(x, y, z, chan))
|
|
error(Egreg);
|
|
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 CMsoftscreen:
|
|
if(strcmp(cb->f[1], "on") == 0)
|
|
scr->softscreen = 1;
|
|
else if(strcmp(cb->f[1], "off") == 0)
|
|
scr->softscreen = 0;
|
|
else
|
|
break;
|
|
if(scr->gscreen == nil)
|
|
return;
|
|
x = scr->gscreen->r.max.x;
|
|
y = scr->gscreen->r.max.y;
|
|
z = scr->gscreen->depth;
|
|
chan = scr->gscreen->chan;
|
|
cursoroff();
|
|
deletescreenimage();
|
|
if(screensize(x, y, z, chan))
|
|
error(Egreg);
|
|
/* no break */
|
|
case CMdrawinit:
|
|
if(scr->gscreen == nil)
|
|
error("drawinit: no gscreen");
|
|
if(scr->dev && scr->dev->drawinit)
|
|
scr->dev->drawinit(scr);
|
|
hwblank = scr->blank != nil;
|
|
hwaccel = !scr->softscreen && (scr->scroll || scr->fill);
|
|
vgascreenwin(scr);
|
|
resetscreenimage();
|
|
cursoron();
|
|
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) < 0)
|
|
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:
|
|
error(Egreg);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Dev vgadevtab = {
|
|
'v',
|
|
"vga",
|
|
|
|
vgareset,
|
|
devinit,
|
|
devshutdown,
|
|
vgaattach,
|
|
vgawalk,
|
|
vgastat,
|
|
vgaopen,
|
|
devcreate,
|
|
vgaclose,
|
|
vgaread,
|
|
devbread,
|
|
vgawrite,
|
|
devbwrite,
|
|
devremove,
|
|
devwstat,
|
|
};
|