diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/aux/apm.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/aux/apm.c')
-rwxr-xr-x | sys/src/cmd/aux/apm.c | 1154 |
1 files changed, 1154 insertions, 0 deletions
diff --git a/sys/src/cmd/aux/apm.c b/sys/src/cmd/aux/apm.c new file mode 100755 index 000000000..5df37d2b6 --- /dev/null +++ b/sys/src/cmd/aux/apm.c @@ -0,0 +1,1154 @@ +#include <u.h> +#include <libc.h> +#include </386/include/ureg.h> +typedef struct Ureg Ureg; +#include <auth.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> + +enum { + /* power mgmt event codes */ + NotifyStandbyRequest = 0x0001, + NotifySuspendRequest = 0x0002, + NotifyNormalResume = 0x0003, + NotifyCriticalResume = 0x0004, + NotifyBatteryLow = 0x0005, + NotifyPowerStatusChange = 0x0006, + NotifyUpdateTime = 0x0007, + NotifyCriticalSuspend = 0x0008, + NotifyUserStandbyRequest = 0x0009, + NotifyUserSuspendRequest = 0x000A, + NotifyStandbyResume = 0x000B, + NotifyCapabilitiesChange = 0x000C, + + /* power device ids: add device number or All */ + DevBios = 0x0000, + DevAll = 0x0001, + DevDisplay = 0x0100, + DevStorage = 0x0200, + DevLpt = 0x0300, + DevEia = 0x0400, + DevNetwork = 0x0500, + DevPCMCIA = 0x0600, + DevBattery = 0x8000, + All = 0x00FF, + DevMask = 0xFF00, + + /* power states */ + PowerEnabled = 0x0000, + PowerStandby = 0x0001, + PowerSuspend = 0x0002, + PowerOff = 0x0003, + + /* apm commands */ + CmdInstallationCheck = 0x5300, + CmdRealModeConnect = 0x5301, + CmdProtMode16Connect = 0x5302, + CmdProtMode32Connect = 0x5303, + CmdDisconnect = 0x5304, + CmdCpuIdle = 0x5305, + CmdCpuBusy = 0x5306, + CmdSetPowerState = 0x5307, + CmdSetPowerMgmt = 0x5308, + DisablePowerMgmt = 0x0000, /* CX */ + EnablePowerMgmt = 0x0001, + CmdRestoreDefaults = 0x5309, + CmdGetPowerStatus = 0x530A, + CmdGetPMEvent = 0x530B, + CmdGetPowerState = 0x530C, + CmdGetPowerMgmt = 0x530D, + CmdDriverVersion = 0x530E, + + /* like CmdDisconnect but doesn't lose the interface */ + CmdGagePowerMgmt = 0x530F, + DisengagePowerMgmt = 0x0000, /* CX */ + EngagePowerManagemenet = 0x0001, + + CmdGetCapabilities = 0x5310, + CapStandby = 0x0001, + CapSuspend = 0x0002, + CapTimerResumeStandby = 0x0004, + CapTimerResumeSuspend = 0x0008, + CapRingResumeStandby = 0x0010, + CapRingResumeSuspend = 0x0020, + CapPcmciaResumeStandby = 0x0040, + CapPcmciaResumeSuspend = 0x0080, + CapSlowCpu = 0x0100, + CmdResumeTimer = 0x5311, + DisableResumeTimer = 0x00, /* CL */ + GetResumeTimer = 0x01, + SetResumeTimer = 0x02, + CmdResumeOnRing = 0x5312, + DisableResumeOnRing = 0x0000, /* CX */ + EnableResumeOnRing = 0x0001, + GetResumeOnRing = 0x0002, + CmdTimerRequests = 0x5313, + DisableTimerRequests = 0x0000, /* CX */ + EnableTimerRequests = 0x0001, + GetTimerRequests = 0x0002, +}; + +static char* eventstr[] = { +[NotifyStandbyRequest] "system standby request", +[NotifySuspendRequest] "system suspend request", +[NotifyNormalResume] "normal resume", +[NotifyCriticalResume] "critical resume", +[NotifyBatteryLow] "battery low", +[NotifyPowerStatusChange] "power status change", +[NotifyUpdateTime] "update time", +[NotifyCriticalSuspend] "critical suspend", +[NotifyUserStandbyRequest] "user standby request", +[NotifyUserSuspendRequest] "user suspend request", +[NotifyCapabilitiesChange] "capabilities change", +}; + +static char* +apmevent(int e) +{ + static char buf[32]; + + if(0 <= e && e < nelem(eventstr) && eventstr[e]) + return eventstr[e]; + + sprint(buf, "event 0x%ux", (uint)e); + return buf; +} + +static char *error[256] = { +[0x01] "power mgmt disabled", +[0x02] "real mode connection already established", +[0x03] "interface not connected", +[0x05] "16-bit protected mode connection already established", +[0x06] "16-bit protected mode interface not supported", +[0x07] "32-bit protected mode interface already established", +[0x08] "32-bit protected mode interface not supported", +[0x09] "unrecognized device id", +[0x0A] "parameter value out of range", +[0x0B] "interface not engaged", +[0x0C] "function not supported", +[0x0D] "resume timer disabled", +[0x60] "unable to enter requested state", +[0x80] "no power mgmt events pending", +[0x86] "apm not present", +}; + +static char* +apmerror(int id) +{ + char *e; + static char buf[64]; + + if(e = error[id&0xFF]) + return e; + + sprint(buf, "unknown error %x", id); + return buf; +} + +QLock apmlock; +int apmdebug; + +static int +_apmcall(int fd, Ureg *u) +{ +if(apmdebug) fprint(2, "call ax 0x%lux bx 0x%lux cx 0x%lux\n", + u->ax&0xFFFF, u->bx&0xFFFF, u->cx&0xFFFF); + + seek(fd, 0, 0); + if(write(fd, u, sizeof *u) != sizeof *u) + return -1; + + seek(fd, 0, 0); + if(read(fd, u, sizeof *u) != sizeof *u) + return -1; + +if(apmdebug) fprint(2, "flags 0x%lux ax 0x%lux bx 0x%lux cx 0x%lux\n", + u->flags&0xFFFF, u->ax&0xFFFF, u->bx&0xFFFF, u->cx&0xFFFF); + + if(u->flags & 1) { /* carry flag */ + werrstr("%s", apmerror(u->ax>>8)); + return -1; + } + return 0; +} + +static int +apmcall(int fd, Ureg *u) +{ + int r; + + qlock(&apmlock); + r = _apmcall(fd, u); + qunlock(&apmlock); + return r; +} + +typedef struct Apm Apm; +typedef struct Battery Battery; + +struct Battery { + int status; + int percent; + int time; +}; + +enum { + Mbattery = 4, +}; +struct Apm { + int fd; + + int verhi; + int verlo; + + int acstatus; + int nbattery; + + int capabilities; + + Battery battery[Mbattery]; +}; +enum { + AcUnknown = 0, /* Apm.acstatus */ + AcOffline, + AcOnline, + AcBackup, + + BatteryUnknown = 0, /* Battery.status */ + BatteryHigh, + BatteryLow, + BatteryCritical, + BatteryCharging, +}; + +static char* +acstatusstr[] = { +[AcUnknown] "unknown", +[AcOffline] "offline", +[AcOnline] "online", +[AcBackup] "backup", +}; + +static char* +batterystatusstr[] = { +[BatteryUnknown] "unknown", +[BatteryHigh] "high", +[BatteryLow] "low", +[BatteryCritical] "critical", +[BatteryCharging] "charging", +}; + +static char* +powerstatestr[] = { +[PowerOff] "off", +[PowerSuspend] "suspend", +[PowerStandby] "standby", +[PowerEnabled] "on", +}; + +static char* +xstatus(char **str, int nstr, int x) +{ + if(0 <= x && x < nstr && str[x]) + return str[x]; + return "unknown"; +} + +static char* +batterystatus(int b) +{ + return xstatus(batterystatusstr, nelem(batterystatusstr), b); +} + +static char* +powerstate(int s) +{ + return xstatus(powerstatestr, nelem(powerstatestr), s); +} + +static char* +acstatus(int a) +{ + return xstatus(acstatusstr, nelem(acstatusstr), a); +} + +static int +apmversion(Apm *apm) +{ + Ureg u; + + u.ax = CmdDriverVersion; + u.bx = 0x0000; + u.cx = 0x0102; + if(apmcall(apm->fd, &u) < 0) + return -1; + + apm->verhi = u.cx>>8; + apm->verlo = u.cx & 0xFF; + + return u.cx; +} + +static int +apmcpuidle(Apm *apm) +{ + Ureg u; + + u.ax = CmdCpuIdle; + return apmcall(apm->fd, &u); +} + +static int +apmcpubusy(Apm *apm) +{ + Ureg u; + + u.ax = CmdCpuBusy; + return apmcall(apm->fd, &u); +} + +static int +apmsetpowerstate(Apm *apm, int dev, int state) +{ + Ureg u; + + u.ax = CmdSetPowerState; + u.bx = dev; + u.cx = state; + return apmcall(apm->fd, &u); +} + +static int +apmsetpowermgmt(Apm *apm, int dev, int state) +{ + Ureg u; + + u.ax = CmdSetPowerMgmt; + u.bx = dev; + u.cx = state; + return apmcall(apm->fd, &u); +} + +static int +apmrestoredefaults(Apm *apm, int dev) +{ + Ureg u; + + u.ax = CmdRestoreDefaults; + u.bx = dev; + return apmcall(apm->fd, &u); +} + +static int +apmgetpowerstatus(Apm *apm, int dev) +{ + Battery *b; + Ureg u; + + if(dev == DevAll) + b = &apm->battery[0]; + else if((dev & DevMask) == DevBattery) { + if(dev - DevBattery < nelem(apm->battery)) + b = &apm->battery[dev - DevBattery]; + else + b = nil; + } else { + werrstr("bad device number"); + return -1; + } + + u.ax = CmdGetPowerStatus; + u.bx = dev; + + if(apmcall(apm->fd, &u) < 0) + return -1; + + if((dev & DevMask) == DevBattery) + apm->nbattery = u.si; + + switch(u.bx>>8) { + case 0x00: + apm->acstatus = AcOffline; + break; + case 0x01: + apm->acstatus = AcOnline; + break; + case 0x02: + apm->acstatus = AcBackup; + break; + default: + apm->acstatus = AcUnknown; + break; + } + + if(b != nil) { + switch(u.bx&0xFF) { + case 0x00: + b->status = BatteryHigh; + break; + case 0x01: + b->status = BatteryLow; + break; + case 0x02: + b->status = BatteryCritical; + break; + case 0x03: + b->status = BatteryCharging; + break; + default: + b->status = BatteryUnknown; + break; + } + + if((u.cx & 0xFF) == 0xFF) + b->percent = -1; + else + b->percent = u.cx & 0xFF; + + if((u.dx&0xFFFF) == 0xFFFF) + b->time = -1; + else if(u.dx & 0x8000) + b->time = 60*(u.dx & 0x7FFF); + else + b->time = u.dx & 0x7FFF; + } + + return 0; +} + +static int +apmgetevent(Apm *apm) +{ + Ureg u; + + u.ax = CmdGetPMEvent; + u.bx = 0; + u.cx = 0; + + //when u.bx == NotifyNormalResume or NotifyCriticalResume, + //u.cx & 1 indicates PCMCIA socket was on while suspended, + //u.cx & 1 == 0 indicates was off. + + if(apmcall(apm->fd, &u) < 0) + return -1; + + return u.bx; +} + +static int +apmgetpowerstate(Apm *apm, int dev) +{ + Ureg u; + + u.ax = CmdGetPowerState; + u.bx = dev; + u.cx = 0; + + if(apmcall(apm->fd, &u) < 0) + return -1; + + return u.cx; +} + +static int +apmgetpowermgmt(Apm *apm, int dev) +{ + Ureg u; + + u.ax = CmdGetPowerMgmt; + u.bx = dev; + + if(apmcall(apm->fd, &u) < 0) + return -1; + + return u.cx; +} + +static int +apmgetcapabilities(Apm *apm) +{ + Ureg u; + + u.ax = CmdGetCapabilities; + u.bx = DevBios; + + if(apmcall(apm->fd, &u) < 0) + return -1; + + apm->nbattery = u.bx & 0xFF; + apm->capabilities &= ~0xFFFF; + apm->capabilities |= u.cx; + return 0; +} + +static int +apminstallationcheck(Apm *apm) +{ + Ureg u; + + u.ax = CmdInstallationCheck; + u.bx = DevBios; + if(apmcall(apm->fd, &u) < 0) + return -1; + + if(u.cx & 0x0004) + apm->capabilities |= CapSlowCpu; + else + apm->capabilities &= ~CapSlowCpu; + return 0; +} + +void +apmsetdisplaystate(Apm *apm, int s) +{ + apmsetpowerstate(apm, DevDisplay, s); +} + +void +apmblank(Apm *apm) +{ + apmsetdisplaystate(apm, PowerStandby); +} + +void +apmunblank(Apm *apm) +{ + apmsetdisplaystate(apm, PowerEnabled); +} + +void +apmsuspend(Apm *apm) +{ + apmsetpowerstate(apm, DevAll, PowerSuspend); +} + +Apm apm; + +void +powerprint(void) +{ + print("%s", ctime(time(0))); + if(apmgetpowerstatus(&apm, DevAll) == 0) { + print("%d batteries\n", apm.nbattery); + print("battery 0: status %s percent %d time %d:%.2d\n", + batterystatus(apm.battery[0].status), apm.battery[0].percent, + apm.battery[0].time/60, apm.battery[0].time%60); + } +} + +void* +erealloc(void *v, ulong n) +{ + v = realloc(v, n); + if(v == nil) + sysfatal("out of memory reallocating %lud", n); + setmalloctag(v, getcallerpc(&v)); + return v; +} + +void* +emalloc(ulong n) +{ + void *v; + + v = malloc(n); + if(v == nil) + sysfatal("out of memory allocating %lud", n); + memset(v, 0, n); + setmalloctag(v, getcallerpc(&n)); + return v; +} + +char* +estrdup(char *s) +{ + int l; + char *t; + + if (s == nil) + return nil; + l = strlen(s)+1; + t = emalloc(l); + memcpy(t, s, l); + setmalloctag(t, getcallerpc(&s)); + return t; +} + +char* +estrdupn(char *s, int n) +{ + int l; + char *t; + + l = strlen(s); + if(l > n) + l = n; + t = emalloc(l+1); + memmove(t, s, l); + t[l] = '\0'; + setmalloctag(t, getcallerpc(&s)); + return t; +} + +enum { + Qroot = 0, + Qevent, + Qbattery, + Qctl, +}; + +static void rootread(Req*); +static void eventread(Req*); +static void ctlread(Req*); +static void ctlwrite(Req*); +static void batteryread(Req*); + +typedef struct Dfile Dfile; +struct Dfile { + Qid qid; + char *name; + ulong mode; + void (*read)(Req*); + void (*write)(Req*); +}; + +Dfile dfile[] = { + { {Qroot,0,QTDIR}, "/", DMDIR|0555, rootread, nil, }, + { {Qevent}, "event", 0444, eventread, nil, }, + { {Qbattery}, "battery", 0444, batteryread, nil, }, + { {Qctl}, "ctl", 0666, ctlread, ctlwrite, }, +}; + +static int +fillstat(uvlong path, Dir *d, int doalloc) +{ + int i; + + for(i=0; i<nelem(dfile); i++) + if(path==dfile[i].qid.path) + break; + if(i==nelem(dfile)) + return -1; + + memset(d, 0, sizeof *d); + d->uid = doalloc ? estrdup("apm") : "apm"; + d->gid = doalloc ? estrdup("apm") : "apm"; + d->length = 0; + d->name = doalloc ? estrdup(dfile[i].name) : dfile[i].name; + d->mode = dfile[i].mode; + d->atime = d->mtime = time(0); + d->qid = dfile[i].qid; + return 0; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + int i; + + if(strcmp(name, "..")==0){ + *qid = dfile[0].qid; + fid->qid = *qid; + return nil; + } + + for(i=1; i<nelem(dfile); i++){ /* i=1: 0 is root dir */ + if(strcmp(dfile[i].name, name)==0){ + *qid = dfile[i].qid; + fid->qid = *qid; + return nil; + } + } + return "file does not exist"; +} + +static void +fsopen(Req *r) +{ + switch((ulong)r->fid->qid.path){ + case Qroot: + r->fid->aux = (void*)0; + respond(r, nil); + return; + + case Qevent: + case Qbattery: + if(r->ifcall.mode == OREAD){ + respond(r, nil); + return; + } + break; + + case Qctl: + if((r->ifcall.mode&~(OTRUNC|OREAD|OWRITE|ORDWR)) == 0){ + respond(r, nil); + return; + } + break; + } + respond(r, "permission denied"); + return; +} + +static void +fsstat(Req *r) +{ + fillstat(r->fid->qid.path, &r->d, 1); + respond(r, nil); +} + +static void +fsread(Req *r) +{ + dfile[r->fid->qid.path].read(r); +} + +static void +fswrite(Req *r) +{ + dfile[r->fid->qid.path].write(r); +} + +static void +rootread(Req *r) +{ + int n; + uvlong offset; + char *p, *ep; + Dir d; + + if(r->ifcall.offset == 0) + offset = 0; + else + offset = (uvlong)r->fid->aux; + + p = r->ofcall.data; + ep = r->ofcall.data+r->ifcall.count; + + if(offset == 0) /* skip root */ + offset = 1; + for(; p+2 < ep; p+=n){ + if(fillstat(offset, &d, 0) < 0) + break; + n = convD2M(&d, (uchar*)p, ep-p); + if(n <= BIT16SZ) + break; + offset++; + } + r->fid->aux = (void*)offset; + r->ofcall.count = p - r->ofcall.data; + respond(r, nil); +} + +static void +batteryread(Req *r) +{ + char buf[Mbattery*80], *ep, *p; + int i; + + apmgetpowerstatus(&apm, DevAll); + + p = buf; + ep = buf+sizeof buf; + *p = '\0'; /* could be no batteries */ + for(i=0; i<apm.nbattery && i<Mbattery; i++) + p += snprint(p, ep-p, "%s %d %d\n", + batterystatus(apm.battery[i].status), + apm.battery[i].percent, apm.battery[i].time); + + readstr(r, buf); + respond(r, nil); +} + +int +iscmd(char *p, char *cmd) +{ + int l; + + l = strlen(cmd); + return strncmp(p, cmd, l)==0 && p[l]=='\0' || p[l]==' ' || p[l]=='\t'; +} + +char* +skip(char *p, char *cmd) +{ + p += strlen(cmd); + while(*p==' ' || *p=='\t') + p++; + return p; +} + +static void +respondx(Req *r, int c) +{ + char err[ERRMAX]; + + if(c == 0) + respond(r, nil); + else{ + rerrstr(err, sizeof err); + respond(r, err); + } +} + +/* + * we don't do suspend because it messes up the + * cycle counter as well as the pcmcia ethernet cards. + */ +static void +ctlwrite(Req *r) +{ + char buf[80], *p, *q; + int dev; + long count; + + count = r->ifcall.count; + if(count > sizeof(buf)-1) + count = sizeof(buf)-1; + memmove(buf, r->ifcall.data, count); + buf[count] = '\0'; + + if(count && buf[count-1] == '\n'){ + --count; + buf[count] = '\0'; + } + + q = buf; + p = strchr(q, ' '); + if(p==nil) + p = q+strlen(q); + else + *p++ = '\0'; + + if(strcmp(q, "")==0 || strcmp(q, "system")==0) + dev = DevAll; + else if(strcmp(q, "display")==0) + dev = DevDisplay; + else if(strcmp(q, "storage")==0) + dev = DevStorage; + else if(strcmp(q, "lpt")==0) + dev = DevLpt; + else if(strcmp(q, "eia")==0) + dev = DevEia; + else if(strcmp(q, "network")==0) + dev = DevNetwork; + else if(strcmp(q, "pcmcia")==0) + dev = DevPCMCIA; + else{ + respond(r, "unknown device"); + return; + } + + if(strcmp(p, "enable")==0) + respondx(r, apmsetpowermgmt(&apm, dev, EnablePowerMgmt)); + else if(strcmp(p, "disable")==0) + respondx(r, apmsetpowermgmt(&apm, dev, DisablePowerMgmt)); + else if(strcmp(p, "standby")==0) + respondx(r, apmsetpowerstate(&apm, dev, PowerStandby)); + else if(strcmp(p, "on")==0) + respondx(r, apmsetpowerstate(&apm, dev, PowerEnabled)); +/* + else if(strcmp(p, "off")==0) + respondx(r, apmsetpowerstate(&apm, dev, PowerOff)); +*/ + else if(strcmp(p, "suspend")==0) + respondx(r, apmsetpowerstate(&apm, dev, PowerSuspend)); + else + respond(r, "unknown verb"); +} + +static int +statusline(char *buf, int nbuf, char *name, int dev) +{ + int s; + char *state; + + state = "unknown"; + if((s = apmgetpowerstate(&apm, dev)) >= 0) + state = powerstate(s); + return snprint(buf, nbuf, "%s %s\n", name, state); +} + +static void +ctlread(Req *r) +{ + char buf[256+7*50], *ep, *p; + + p = buf; + ep = buf+sizeof buf; + + p += snprint(p, ep-p, "ac %s\n", acstatus(apm.acstatus)); + p += snprint(p, ep-p, "capabilities"); + if(apm.capabilities & CapStandby) + p += snprint(p, ep-p, " standby"); + if(apm.capabilities & CapSuspend) + p += snprint(p, ep-p, " suspend"); + if(apm.capabilities & CapSlowCpu) + p += snprint(p, ep-p, " slowcpu"); + p += snprint(p, ep-p, "\n"); + + p += statusline(p, ep-p, "system", DevAll); + p += statusline(p, ep-p, "display", DevDisplay); + p += statusline(p, ep-p, "storage", DevStorage); + p += statusline(p, ep-p, "lpt", DevLpt); + p += statusline(p, ep-p, "eia", DevEia|All); + p += statusline(p, ep-p, "network", DevNetwork|All); + p += statusline(p, ep-p, "pcmcia", DevPCMCIA|All); + USED(p); + + readstr(r, buf); + respond(r, nil); +} + +enum { + STACK = 16384, +}; + +Channel *creq; +Channel *cflush; +Channel *cevent; +Req *rlist, **tailp; +int rp, wp; +int nopoll; +char eventq[32][80]; + +static void +flushthread(void*) +{ + Req *r, *or, **rq; + + threadsetname("flushthread"); + while(r = recvp(cflush)){ + or = r->oldreq; + for(rq=&rlist; *rq; rq=&(*rq)->aux){ + if(*rq == or){ + *rq = or->aux; + if(tailp==&or->aux) + tailp = rq; + respond(or, "interrupted"); + break; + } + } + respond(r, nil); + } +} + +static void +answerany(void) +{ + char *buf; + int l, m; + Req *r; + + if(rlist==nil || rp==wp) + return; + + while(rlist && rp != wp){ + r = rlist; + rlist = r->aux; + if(rlist==nil) + tailp = &rlist; + + l = 0; + buf = r->ofcall.data; + m = r->ifcall.count; + while(rp != wp){ + if(l+strlen(eventq[rp]) <= m){ + strcpy(buf+l, eventq[rp]); + l += strlen(buf+l); + }else if(l==0){ + strncpy(buf, eventq[rp], m-1); + buf[m-1] = '\0'; + l += m; + }else + break; + rp++; + if(rp == nelem(eventq)) + rp = 0; + } + r->ofcall.count = l; + respond(r, nil); + } +} + +static void +eventwatch(void*) +{ + int e, s; + + threadsetname("eventwatch"); + for(;;){ + s = 0; + while((e = apmgetevent(&apm)) >= 0){ + sendul(cevent, e); + s = 1; + } + if(s) + sendul(cevent, -1); + if(sleep(750) < 0) + break; + } +} + +static void +eventthread(void*) +{ + int e; + + threadsetname("eventthread"); + for(;;){ + while((e = recvul(cevent)) >= 0){ + snprint(eventq[wp], sizeof(eventq[wp])-1, "%s", apmevent(e)); + strcat(eventq[wp], "\n"); + wp++; + if(wp==nelem(eventq)) + wp = 0; + if(wp+1==rp || (wp+1==nelem(eventq) && rp==0)) + break; + } + answerany(); + } +} + +static void +eventproc(void*) +{ + Req *r; + + threadsetname("eventproc"); + + creq = chancreate(sizeof(Req*), 0); + cevent = chancreate(sizeof(ulong), 0); + cflush = chancreate(sizeof(Req*), 0); + + tailp = &rlist; + if(!nopoll) + proccreate(eventwatch, nil, STACK); + threadcreate(eventthread, nil, STACK); + threadcreate(flushthread, nil, STACK); + + while(r = recvp(creq)){ + *tailp = r; + r->aux = nil; + tailp = &r->aux; + answerany(); + } +} + +static void +fsflush(Req *r) +{ + sendp(cflush, r); +} + +static void +eventread(Req *r) +{ + sendp(creq, r); +} + +static void +fsattach(Req *r) +{ + char *spec; + static int first = 1; + + spec = r->ifcall.aname; + + if(first){ + first = 0; + proccreate(eventproc, nil, STACK); + } + + if(spec && spec[0]){ + respond(r, "invalid attach specifier"); + return; + } + r->fid->qid = dfile[0].qid; + r->ofcall.qid = dfile[0].qid; + respond(r, nil); +} + +Srv fs = { +.attach= fsattach, +.walk1= fswalk1, +.open= fsopen, +.read= fsread, +.write= fswrite, +.stat= fsstat, +.flush= fsflush, +}; + +void +usage(void) +{ + fprint(2, "usage: aux/apm [-ADPi] [-d /dev/apm] [-m /mnt/apm] [-s service]\n"); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + char *dev, *mtpt, *srv; + + dev = nil; + mtpt = "/mnt/apm"; + srv = nil; + ARGBEGIN{ + case 'A': + apmdebug = 1; + break; + case 'D': + chatty9p = 1; + break; + case 'P': + nopoll = 1; + break; + case 'd': + dev = EARGF(usage()); + break; + case 'i': + fs.nopipe++; + fs.infd = 0; + fs.outfd = 1; + mtpt = nil; + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + srv = EARGF(usage()); + break; + }ARGEND + + if(dev == nil){ + if((apm.fd = open("/dev/apm", ORDWR)) < 0 + && (apm.fd = open("#P/apm", ORDWR)) < 0){ + fprint(2, "open %s: %r\n", dev); + threadexitsall("open"); + } + } else if((apm.fd = open(dev, ORDWR)) < 0){ + fprint(2, "open %s: %r\n", dev); + threadexitsall("open"); + } + + if(apmversion(&apm) < 0){ + fprint(2, "cannot get apm version: %r\n"); + threadexitsall("apmversion"); + } + + if(apm.verhi < 1 || (apm.verhi==1 && apm.verlo < 2)){ + fprint(2, "apm version %d.%d not supported\n", apm.verhi, apm.verlo); + threadexitsall("apmversion"); + } + + if(apmgetcapabilities(&apm) < 0) + fprint(2, "warning: cannot read apm capabilities: %r\n"); + + apminstallationcheck(&apm); + apmcpuidle(&apm); + + rfork(RFNOTEG); + threadpostmountsrv(&fs, srv, mtpt, MREPL); +} |