#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);
}
|