3 #include </386/include/ureg.h>
4 typedef struct Ureg Ureg;
11 /* power mgmt event codes */
12 NotifyStandbyRequest = 0x0001,
13 NotifySuspendRequest = 0x0002,
14 NotifyNormalResume = 0x0003,
15 NotifyCriticalResume = 0x0004,
16 NotifyBatteryLow = 0x0005,
17 NotifyPowerStatusChange = 0x0006,
18 NotifyUpdateTime = 0x0007,
19 NotifyCriticalSuspend = 0x0008,
20 NotifyUserStandbyRequest = 0x0009,
21 NotifyUserSuspendRequest = 0x000A,
22 NotifyStandbyResume = 0x000B,
23 NotifyCapabilitiesChange = 0x000C,
25 /* power device ids: add device number or All */
39 PowerEnabled = 0x0000,
40 PowerStandby = 0x0001,
41 PowerSuspend = 0x0002,
45 CmdInstallationCheck = 0x5300,
46 CmdRealModeConnect = 0x5301,
47 CmdProtMode16Connect = 0x5302,
48 CmdProtMode32Connect = 0x5303,
49 CmdDisconnect = 0x5304,
52 CmdSetPowerState = 0x5307,
53 CmdSetPowerMgmt = 0x5308,
54 DisablePowerMgmt = 0x0000, /* CX */
55 EnablePowerMgmt = 0x0001,
56 CmdRestoreDefaults = 0x5309,
57 CmdGetPowerStatus = 0x530A,
58 CmdGetPMEvent = 0x530B,
59 CmdGetPowerState = 0x530C,
60 CmdGetPowerMgmt = 0x530D,
61 CmdDriverVersion = 0x530E,
63 /* like CmdDisconnect but doesn't lose the interface */
64 CmdGagePowerMgmt = 0x530F,
65 DisengagePowerMgmt = 0x0000, /* CX */
66 EngagePowerManagemenet = 0x0001,
68 CmdGetCapabilities = 0x5310,
71 CapTimerResumeStandby = 0x0004,
72 CapTimerResumeSuspend = 0x0008,
73 CapRingResumeStandby = 0x0010,
74 CapRingResumeSuspend = 0x0020,
75 CapPcmciaResumeStandby = 0x0040,
76 CapPcmciaResumeSuspend = 0x0080,
78 CmdResumeTimer = 0x5311,
79 DisableResumeTimer = 0x00, /* CL */
80 GetResumeTimer = 0x01,
81 SetResumeTimer = 0x02,
82 CmdResumeOnRing = 0x5312,
83 DisableResumeOnRing = 0x0000, /* CX */
84 EnableResumeOnRing = 0x0001,
85 GetResumeOnRing = 0x0002,
86 CmdTimerRequests = 0x5313,
87 DisableTimerRequests = 0x0000, /* CX */
88 EnableTimerRequests = 0x0001,
89 GetTimerRequests = 0x0002,
92 static char* eventstr[] = {
93 [NotifyStandbyRequest] "system standby request",
94 [NotifySuspendRequest] "system suspend request",
95 [NotifyNormalResume] "normal resume",
96 [NotifyCriticalResume] "critical resume",
97 [NotifyBatteryLow] "battery low",
98 [NotifyPowerStatusChange] "power status change",
99 [NotifyUpdateTime] "update time",
100 [NotifyCriticalSuspend] "critical suspend",
101 [NotifyUserStandbyRequest] "user standby request",
102 [NotifyUserSuspendRequest] "user suspend request",
103 [NotifyCapabilitiesChange] "capabilities change",
111 if(0 <= e && e < nelem(eventstr) && eventstr[e])
114 sprint(buf, "event 0x%ux", (uint)e);
118 static char *error[256] = {
119 [0x01] "power mgmt disabled",
120 [0x02] "real mode connection already established",
121 [0x03] "interface not connected",
122 [0x05] "16-bit protected mode connection already established",
123 [0x06] "16-bit protected mode interface not supported",
124 [0x07] "32-bit protected mode interface already established",
125 [0x08] "32-bit protected mode interface not supported",
126 [0x09] "unrecognized device id",
127 [0x0A] "parameter value out of range",
128 [0x0B] "interface not engaged",
129 [0x0C] "function not supported",
130 [0x0D] "resume timer disabled",
131 [0x60] "unable to enter requested state",
132 [0x80] "no power mgmt events pending",
133 [0x86] "apm not present",
142 if(e = error[id&0xFF])
145 sprint(buf, "unknown error %x", id);
153 _apmcall(int fd, Ureg *u)
155 if(apmdebug) fprint(2, "call ax 0x%lux bx 0x%lux cx 0x%lux\n",
156 u->ax&0xFFFF, u->bx&0xFFFF, u->cx&0xFFFF);
159 if(write(fd, u, sizeof *u) != sizeof *u)
163 if(read(fd, u, sizeof *u) != sizeof *u)
166 if(apmdebug) fprint(2, "flags 0x%lux ax 0x%lux bx 0x%lux cx 0x%lux\n",
167 u->flags&0xFFFF, u->ax&0xFFFF, u->bx&0xFFFF, u->cx&0xFFFF);
169 if(u->flags & 1) { /* carry flag */
170 werrstr("%s", apmerror(u->ax>>8));
177 apmcall(int fd, Ureg *u)
187 typedef struct Apm Apm;
188 typedef struct Battery Battery;
210 Battery battery[Mbattery];
213 AcUnknown = 0, /* Apm.acstatus */
218 BatteryUnknown = 0, /* Battery.status */
227 [AcUnknown] "unknown",
228 [AcOffline] "offline",
234 batterystatusstr[] = {
235 [BatteryUnknown] "unknown",
236 [BatteryHigh] "high",
238 [BatteryCritical] "critical",
239 [BatteryCharging] "charging",
245 [PowerSuspend] "suspend",
246 [PowerStandby] "standby",
251 xstatus(char **str, int nstr, int x)
253 if(0 <= x && x < nstr && str[x])
261 return xstatus(batterystatusstr, nelem(batterystatusstr), b);
267 return xstatus(powerstatestr, nelem(powerstatestr), s);
273 return xstatus(acstatusstr, nelem(acstatusstr), a);
281 u.ax = CmdDriverVersion;
284 if(apmcall(apm->fd, &u) < 0)
287 apm->verhi = u.cx>>8;
288 apm->verlo = u.cx & 0xFF;
299 return apmcall(apm->fd, &u);
308 return apmcall(apm->fd, &u);
312 apmsetpowerstate(Apm *apm, int dev, int state)
316 u.ax = CmdSetPowerState;
319 return apmcall(apm->fd, &u);
323 apmsetpowermgmt(Apm *apm, int dev, int state)
327 u.ax = CmdSetPowerMgmt;
330 return apmcall(apm->fd, &u);
334 apmrestoredefaults(Apm *apm, int dev)
338 u.ax = CmdRestoreDefaults;
340 return apmcall(apm->fd, &u);
344 apmgetpowerstatus(Apm *apm, int dev)
350 b = &apm->battery[0];
351 else if((dev & DevMask) == DevBattery) {
352 if(dev - DevBattery < nelem(apm->battery))
353 b = &apm->battery[dev - DevBattery];
357 werrstr("bad device number");
361 u.ax = CmdGetPowerStatus;
364 if(apmcall(apm->fd, &u) < 0)
367 if((dev & DevMask) == DevBattery)
368 apm->nbattery = u.si;
372 apm->acstatus = AcOffline;
375 apm->acstatus = AcOnline;
378 apm->acstatus = AcBackup;
381 apm->acstatus = AcUnknown;
388 b->status = BatteryHigh;
391 b->status = BatteryLow;
394 b->status = BatteryCritical;
397 b->status = BatteryCharging;
400 b->status = BatteryUnknown;
404 if((u.cx & 0xFF) == 0xFF)
407 b->percent = u.cx & 0xFF;
409 if((u.dx&0xFFFF) == 0xFFFF)
411 else if(u.dx & 0x8000)
412 b->time = 60*(u.dx & 0x7FFF);
414 b->time = u.dx & 0x7FFF;
421 apmgetevent(Apm *apm)
425 u.ax = CmdGetPMEvent;
429 //when u.bx == NotifyNormalResume or NotifyCriticalResume,
430 //u.cx & 1 indicates PCMCIA socket was on while suspended,
431 //u.cx & 1 == 0 indicates was off.
433 if(apmcall(apm->fd, &u) < 0)
440 apmgetpowerstate(Apm *apm, int dev)
444 u.ax = CmdGetPowerState;
448 if(apmcall(apm->fd, &u) < 0)
455 apmgetpowermgmt(Apm *apm, int dev)
459 u.ax = CmdGetPowerMgmt;
462 if(apmcall(apm->fd, &u) < 0)
469 apmgetcapabilities(Apm *apm)
473 u.ax = CmdGetCapabilities;
476 if(apmcall(apm->fd, &u) < 0)
479 apm->nbattery = u.bx & 0xFF;
480 apm->capabilities &= ~0xFFFF;
481 apm->capabilities |= u.cx;
486 apminstallationcheck(Apm *apm)
490 u.ax = CmdInstallationCheck;
492 if(apmcall(apm->fd, &u) < 0)
496 apm->capabilities |= CapSlowCpu;
498 apm->capabilities &= ~CapSlowCpu;
503 apmsetdisplaystate(Apm *apm, int s)
505 apmsetpowerstate(apm, DevDisplay, s);
511 apmsetdisplaystate(apm, PowerStandby);
517 apmsetdisplaystate(apm, PowerEnabled);
523 apmsetpowerstate(apm, DevAll, PowerSuspend);
531 print("%s", ctime(time(0)));
532 if(apmgetpowerstatus(&apm, DevAll) == 0) {
533 print("%d batteries\n", apm.nbattery);
534 print("battery 0: status %s percent %d time %d:%.2d\n",
535 batterystatus(apm.battery[0].status), apm.battery[0].percent,
536 apm.battery[0].time/60, apm.battery[0].time%60);
541 erealloc(void *v, ulong n)
545 sysfatal("out of memory reallocating %lud", n);
546 setmalloctag(v, getcallerpc(&v));
557 sysfatal("out of memory allocating %lud", n);
559 setmalloctag(v, getcallerpc(&n));
574 setmalloctag(t, getcallerpc(&s));
579 estrdupn(char *s, int n)
590 setmalloctag(t, getcallerpc(&s));
601 static void rootread(Req*);
602 static void eventread(Req*);
603 static void ctlread(Req*);
604 static void ctlwrite(Req*);
605 static void batteryread(Req*);
607 typedef struct Dfile Dfile;
617 { {Qroot,0,QTDIR}, "/", DMDIR|0555, rootread, nil, },
618 { {Qevent}, "event", 0444, eventread, nil, },
619 { {Qbattery}, "battery", 0444, batteryread, nil, },
620 { {Qctl}, "ctl", 0666, ctlread, ctlwrite, },
624 fillstat(uvlong path, Dir *d, int doalloc)
628 for(i=0; i<nelem(dfile); i++)
629 if(path==dfile[i].qid.path)
634 memset(d, 0, sizeof *d);
635 d->uid = doalloc ? estrdup("apm") : "apm";
636 d->gid = doalloc ? estrdup("apm") : "apm";
638 d->name = doalloc ? estrdup(dfile[i].name) : dfile[i].name;
639 d->mode = dfile[i].mode;
640 d->atime = d->mtime = time(0);
641 d->qid = dfile[i].qid;
646 fswalk1(Fid *fid, char *name, Qid *qid)
650 if(strcmp(name, "..")==0){
656 for(i=1; i<nelem(dfile); i++){ /* i=1: 0 is root dir */
657 if(strcmp(dfile[i].name, name)==0){
663 return "file does not exist";
669 switch((ulong)r->fid->qid.path){
671 r->fid->aux = (void*)0;
677 if(r->ifcall.mode == OREAD){
684 if((r->ifcall.mode&~(OTRUNC|OREAD|OWRITE|ORDWR)) == 0){
690 respond(r, "permission denied");
697 fillstat(r->fid->qid.path, &r->d, 1);
704 dfile[r->fid->qid.path].read(r);
710 dfile[r->fid->qid.path].write(r);
721 if(r->ifcall.offset == 0)
724 offset = (uvlong)r->fid->aux;
727 ep = r->ofcall.data+r->ifcall.count;
729 if(offset == 0) /* skip root */
731 for(; p+2 < ep; p+=n){
732 if(fillstat(offset, &d, 0) < 0)
734 n = convD2M(&d, (uchar*)p, ep-p);
739 r->fid->aux = (void*)offset;
740 r->ofcall.count = p - r->ofcall.data;
747 char buf[Mbattery*80], *ep, *p;
750 apmgetpowerstatus(&apm, DevAll);
754 *p = '\0'; /* could be no batteries */
755 for(i=0; i<apm.nbattery && i<Mbattery; i++)
756 p += snprint(p, ep-p, "%s %d %d\n",
757 batterystatus(apm.battery[i].status),
758 apm.battery[i].percent, apm.battery[i].time);
765 iscmd(char *p, char *cmd)
770 return strncmp(p, cmd, l)==0 && p[l]=='\0' || p[l]==' ' || p[l]=='\t';
774 skip(char *p, char *cmd)
777 while(*p==' ' || *p=='\t')
783 respondx(Req *r, int c)
790 rerrstr(err, sizeof err);
796 * we don't do suspend because it messes up the
797 * cycle counter as well as the pcmcia ethernet cards.
802 char buf[80], *p, *q;
806 count = r->ifcall.count;
807 if(count > sizeof(buf)-1)
808 count = sizeof(buf)-1;
809 memmove(buf, r->ifcall.data, count);
812 if(count && buf[count-1] == '\n'){
824 if(strcmp(q, "")==0 || strcmp(q, "system")==0)
826 else if(strcmp(q, "display")==0)
828 else if(strcmp(q, "storage")==0)
830 else if(strcmp(q, "lpt")==0)
832 else if(strcmp(q, "eia")==0)
834 else if(strcmp(q, "network")==0)
836 else if(strcmp(q, "pcmcia")==0)
839 respond(r, "unknown device");
843 if(strcmp(p, "enable")==0)
844 respondx(r, apmsetpowermgmt(&apm, dev, EnablePowerMgmt));
845 else if(strcmp(p, "disable")==0)
846 respondx(r, apmsetpowermgmt(&apm, dev, DisablePowerMgmt));
847 else if(strcmp(p, "standby")==0)
848 respondx(r, apmsetpowerstate(&apm, dev, PowerStandby));
849 else if(strcmp(p, "on")==0)
850 respondx(r, apmsetpowerstate(&apm, dev, PowerEnabled));
852 else if(strcmp(p, "off")==0)
853 respondx(r, apmsetpowerstate(&apm, dev, PowerOff));
855 else if(strcmp(p, "suspend")==0)
856 respondx(r, apmsetpowerstate(&apm, dev, PowerSuspend));
858 respond(r, "unknown verb");
862 statusline(char *buf, int nbuf, char *name, int dev)
868 if((s = apmgetpowerstate(&apm, dev)) >= 0)
869 state = powerstate(s);
870 return snprint(buf, nbuf, "%s %s\n", name, state);
876 char buf[256+7*50], *ep, *p;
881 p += snprint(p, ep-p, "ac %s\n", acstatus(apm.acstatus));
882 p += snprint(p, ep-p, "capabilities");
883 if(apm.capabilities & CapStandby)
884 p += snprint(p, ep-p, " standby");
885 if(apm.capabilities & CapSuspend)
886 p += snprint(p, ep-p, " suspend");
887 if(apm.capabilities & CapSlowCpu)
888 p += snprint(p, ep-p, " slowcpu");
889 p += snprint(p, ep-p, "\n");
891 p += statusline(p, ep-p, "system", DevAll);
892 p += statusline(p, ep-p, "display", DevDisplay);
893 p += statusline(p, ep-p, "storage", DevStorage);
894 p += statusline(p, ep-p, "lpt", DevLpt);
895 p += statusline(p, ep-p, "eia", DevEia|All);
896 p += statusline(p, ep-p, "network", DevNetwork|All);
897 p += statusline(p, ep-p, "pcmcia", DevPCMCIA|All);
921 threadsetname("flushthread");
922 while(r = recvp(cflush)){
924 for(rq=&rlist; *rq; rq=&(*rq)->aux){
929 respond(or, "interrupted");
944 if(rlist==nil || rp==wp)
947 while(rlist && rp != wp){
954 buf = r->ofcall.data;
957 if(l+strlen(eventq[rp]) <= m){
958 strcpy(buf+l, eventq[rp]);
961 strncpy(buf, eventq[rp], m-1);
967 if(rp == nelem(eventq))
980 threadsetname("eventwatch");
983 while((e = apmgetevent(&apm)) >= 0){
999 threadsetname("eventthread");
1001 while((e = recvul(cevent)) >= 0){
1002 snprint(eventq[wp], sizeof(eventq[wp])-1, "%s", apmevent(e));
1003 strcat(eventq[wp], "\n");
1005 if(wp==nelem(eventq))
1007 if(wp+1==rp || (wp+1==nelem(eventq) && rp==0))
1019 threadsetname("eventproc");
1021 creq = chancreate(sizeof(Req*), 0);
1022 cevent = chancreate(sizeof(ulong), 0);
1023 cflush = chancreate(sizeof(Req*), 0);
1027 proccreate(eventwatch, nil, STACK);
1028 threadcreate(eventthread, nil, STACK);
1029 threadcreate(flushthread, nil, STACK);
1031 while(r = recvp(creq)){
1055 static int first = 1;
1057 spec = r->ifcall.aname;
1061 proccreate(eventproc, nil, STACK);
1064 if(spec && spec[0]){
1065 respond(r, "invalid attach specifier");
1068 r->fid->qid = dfile[0].qid;
1069 r->ofcall.qid = dfile[0].qid;
1086 fprint(2, "usage: aux/apm [-ADPi] [-d /dev/apm] [-m /mnt/apm] [-s service]\n");
1091 threadmain(int argc, char **argv)
1093 char *dev, *mtpt, *srv;
1109 dev = EARGF(usage());
1118 mtpt = EARGF(usage());
1121 srv = EARGF(usage());
1126 if((apm.fd = open("/dev/apm", ORDWR)) < 0
1127 && (apm.fd = open("#P/apm", ORDWR)) < 0){
1128 fprint(2, "open %s: %r\n", dev);
1129 threadexitsall("open");
1131 } else if((apm.fd = open(dev, ORDWR)) < 0){
1132 fprint(2, "open %s: %r\n", dev);
1133 threadexitsall("open");
1136 if(apmversion(&apm) < 0){
1137 fprint(2, "cannot get apm version: %r\n");
1138 threadexitsall("apmversion");
1141 if(apm.verhi < 1 || (apm.verhi==1 && apm.verlo < 2)){
1142 fprint(2, "apm version %d.%d not supported\n", apm.verhi, apm.verlo);
1143 threadexitsall("apmversion");
1146 if(apmgetcapabilities(&apm) < 0)
1147 fprint(2, "warning: cannot read apm capabilities: %r\n");
1149 apminstallationcheck(&apm);
1153 threadpostmountsrv(&fs, srv, mtpt, MREPL);