]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/apm.c
kbdfs: allow to escape ctlr-alt-del with shift for vmx and vnc.
[plan9front.git] / sys / src / cmd / aux / apm.c
1 #include <u.h>
2 #include <libc.h>
3 #include </386/include/ureg.h>
4 typedef struct Ureg Ureg;
5 #include <auth.h>
6 #include <fcall.h>
7 #include <thread.h>
8 #include <9p.h>
9
10 enum {
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,
24
25         /* power device ids: add device number or All */
26         DevBios                                 = 0x0000,
27         DevAll                                  = 0x0001,
28         DevDisplay                              = 0x0100,
29         DevStorage                              = 0x0200,
30         DevLpt                                  = 0x0300,
31         DevEia                                  = 0x0400,
32         DevNetwork                              = 0x0500,
33         DevPCMCIA                               = 0x0600,
34         DevBattery                              = 0x8000,
35                 All                                     = 0x00FF,
36         DevMask                                 = 0xFF00,
37
38         /* power states */
39         PowerEnabled                            = 0x0000,
40         PowerStandby                            = 0x0001,
41         PowerSuspend                            = 0x0002,
42         PowerOff                                        = 0x0003,
43
44         /* apm commands */
45         CmdInstallationCheck            = 0x5300,
46         CmdRealModeConnect              = 0x5301,
47         CmdProtMode16Connect            = 0x5302,
48         CmdProtMode32Connect            = 0x5303,
49         CmdDisconnect                   = 0x5304,
50         CmdCpuIdle                              = 0x5305,
51         CmdCpuBusy                              = 0x5306,
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,
62
63         /* like CmdDisconnect but doesn't lose the interface */
64         CmdGagePowerMgmt        = 0x530F,
65           DisengagePowerMgmt    = 0x0000,       /* CX */
66           EngagePowerManagemenet        = 0x0001,
67
68         CmdGetCapabilities                      = 0x5310,
69           CapStandby                            = 0x0001,
70           CapSuspend                            = 0x0002,
71           CapTimerResumeStandby = 0x0004,
72           CapTimerResumeSuspend = 0x0008,
73           CapRingResumeStandby          = 0x0010,
74           CapRingResumeSuspend  = 0x0020,
75           CapPcmciaResumeStandby        = 0x0040,
76           CapPcmciaResumeSuspend        = 0x0080,
77           CapSlowCpu                            = 0x0100,
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,
90 };
91
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",
104 };
105
106 static char*
107 apmevent(int e)
108 {
109         static char buf[32];
110
111         if(0 <= e && e < nelem(eventstr) && eventstr[e])
112                 return eventstr[e];
113         
114         sprint(buf, "event 0x%ux", (uint)e);
115         return buf;
116 }
117
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",
134 };
135
136 static char*
137 apmerror(int id)
138 {
139         char *e;
140         static char buf[64];
141
142         if(e = error[id&0xFF])
143                 return e;
144
145         sprint(buf, "unknown error %x", id);
146         return buf;
147 }
148
149 QLock apmlock;
150 int apmdebug;
151
152 static int
153 _apmcall(int fd, Ureg *u)
154 {
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);
157
158         seek(fd, 0, 0);
159         if(write(fd, u, sizeof *u) != sizeof *u)
160                 return -1;
161
162         seek(fd, 0, 0);
163         if(read(fd, u, sizeof *u) != sizeof *u)
164                 return -1;
165
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);
168
169         if(u->flags & 1) {      /* carry flag */
170                 werrstr("%s", apmerror(u->ax>>8));
171                 return -1;
172         }
173         return 0;
174 }
175
176 static int
177 apmcall(int fd, Ureg *u)
178 {
179         int r;
180
181         qlock(&apmlock);
182         r = _apmcall(fd, u);
183         qunlock(&apmlock);
184         return r;
185 }
186
187 typedef struct Apm Apm;
188 typedef struct Battery Battery;
189
190 struct Battery {
191         int status;
192         int percent;
193         int time;
194 };
195
196 enum {
197         Mbattery = 4,
198 };
199 struct Apm {
200         int fd;
201
202         int verhi;
203         int verlo;
204
205         int acstatus;
206         int nbattery;
207
208         int capabilities;
209
210         Battery battery[Mbattery];
211 };
212 enum {
213         AcUnknown = 0,          /* Apm.acstatus */
214         AcOffline,
215         AcOnline,
216         AcBackup,
217
218         BatteryUnknown = 0,     /* Battery.status */
219         BatteryHigh,
220         BatteryLow,
221         BatteryCritical,
222         BatteryCharging,
223 };
224
225 static char*
226 acstatusstr[] = {
227 [AcUnknown]     "unknown",
228 [AcOffline]     "offline",
229 [AcOnline]      "online",
230 [AcBackup]      "backup",
231 };
232
233 static char*
234 batterystatusstr[] = {
235 [BatteryUnknown] "unknown",
236 [BatteryHigh]   "high",
237 [BatteryLow]    "low",
238 [BatteryCritical]       "critical",
239 [BatteryCharging]       "charging",
240 };
241
242 static char*
243 powerstatestr[] = {
244 [PowerOff]      "off",
245 [PowerSuspend]  "suspend",
246 [PowerStandby]  "standby",
247 [PowerEnabled]  "on",
248 };
249
250 static char*
251 xstatus(char **str, int nstr, int x)
252 {
253         if(0 <= x && x < nstr && str[x])
254                 return str[x];
255         return "unknown";
256 }
257
258 static char*
259 batterystatus(int b)
260 {
261         return xstatus(batterystatusstr, nelem(batterystatusstr), b);
262 }
263
264 static char*
265 powerstate(int s)
266 {
267         return xstatus(powerstatestr, nelem(powerstatestr), s);
268 }
269
270 static char*
271 acstatus(int a)
272 {
273         return xstatus(acstatusstr, nelem(acstatusstr), a);
274 }
275           
276 static int
277 apmversion(Apm *apm)
278 {
279         Ureg u;
280
281         u.ax = CmdDriverVersion;
282         u.bx = 0x0000;
283         u.cx = 0x0102;
284         if(apmcall(apm->fd, &u) < 0)
285                 return -1;
286
287         apm->verhi = u.cx>>8;
288         apm->verlo = u.cx & 0xFF;
289
290         return u.cx;
291 }
292
293 static int
294 apmcpuidle(Apm *apm)
295 {
296         Ureg u;
297
298         u.ax = CmdCpuIdle;
299         return apmcall(apm->fd, &u);
300 }
301
302 static int
303 apmcpubusy(Apm *apm)
304 {
305         Ureg u;
306
307         u.ax = CmdCpuBusy;
308         return apmcall(apm->fd, &u);
309 }
310
311 static int
312 apmsetpowerstate(Apm *apm, int dev, int state)
313 {
314         Ureg u;
315
316         u.ax = CmdSetPowerState;
317         u.bx = dev;
318         u.cx = state;
319         return apmcall(apm->fd, &u);
320 }
321
322 static int
323 apmsetpowermgmt(Apm *apm, int dev, int state)
324 {
325         Ureg u;
326
327         u.ax = CmdSetPowerMgmt;
328         u.bx = dev;
329         u.cx = state;
330         return apmcall(apm->fd, &u);
331 }
332
333 static int
334 apmrestoredefaults(Apm *apm, int dev)
335 {
336         Ureg u;
337
338         u.ax = CmdRestoreDefaults;
339         u.bx = dev;
340         return apmcall(apm->fd, &u);
341 }
342
343 static int
344 apmgetpowerstatus(Apm *apm, int dev)
345 {
346         Battery *b;
347         Ureg u;
348
349         if(dev == DevAll)
350                 b = &apm->battery[0];
351         else if((dev & DevMask) == DevBattery) {
352                 if(dev - DevBattery < nelem(apm->battery))
353                         b = &apm->battery[dev - DevBattery];
354                 else
355                         b = nil;
356         } else {
357                 werrstr("bad device number");
358                 return -1;
359         }
360
361         u.ax = CmdGetPowerStatus;
362         u.bx = dev;
363
364         if(apmcall(apm->fd, &u) < 0)
365                 return -1;
366
367         if((dev & DevMask) == DevBattery)
368                 apm->nbattery = u.si;
369
370         switch(u.bx>>8) {
371         case 0x00:
372                 apm->acstatus = AcOffline;
373                 break;
374         case 0x01:
375                 apm->acstatus = AcOnline;
376                 break;
377         case 0x02:
378                 apm->acstatus = AcBackup;
379                 break;
380         default:
381                 apm->acstatus = AcUnknown;
382                 break;
383         }
384
385         if(b != nil) {
386                 switch(u.bx&0xFF) {
387                 case 0x00:
388                         b->status = BatteryHigh;
389                         break;
390                 case 0x01:
391                         b->status = BatteryLow;
392                         break;
393                 case 0x02:
394                         b->status = BatteryCritical;
395                         break;
396                 case 0x03:
397                         b->status = BatteryCharging;
398                         break;
399                 default:
400                         b->status = BatteryUnknown;
401                         break;
402                 }
403
404                 if((u.cx & 0xFF) == 0xFF)
405                         b->percent = -1;
406                 else
407                         b->percent = u.cx & 0xFF;
408
409                 if((u.dx&0xFFFF) == 0xFFFF)
410                         b->time = -1;
411                 else if(u.dx & 0x8000)
412                         b->time = 60*(u.dx & 0x7FFF);
413                 else
414                         b->time = u.dx & 0x7FFF;
415         }
416
417         return 0;
418 }
419
420 static int
421 apmgetevent(Apm *apm)
422 {
423         Ureg u;
424
425         u.ax = CmdGetPMEvent;
426         u.bx = 0;
427         u.cx = 0;
428
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.
432
433         if(apmcall(apm->fd, &u) < 0)
434                 return -1;
435
436         return u.bx;
437 }
438
439 static int
440 apmgetpowerstate(Apm *apm, int dev)
441 {
442         Ureg u;
443
444         u.ax = CmdGetPowerState;
445         u.bx = dev;
446         u.cx = 0;
447
448         if(apmcall(apm->fd, &u) < 0)
449                 return -1;
450
451         return u.cx;
452 }
453
454 static int
455 apmgetpowermgmt(Apm *apm, int dev)
456 {
457         Ureg u;
458
459         u.ax = CmdGetPowerMgmt;
460         u.bx = dev;
461
462         if(apmcall(apm->fd, &u) < 0)
463                 return -1;
464
465         return u.cx;
466 }
467
468 static int
469 apmgetcapabilities(Apm *apm)
470 {
471         Ureg u;
472
473         u.ax = CmdGetCapabilities;
474         u.bx = DevBios;
475
476         if(apmcall(apm->fd, &u) < 0)
477                 return -1;
478
479         apm->nbattery = u.bx & 0xFF;
480         apm->capabilities &= ~0xFFFF;
481         apm->capabilities |= u.cx;
482         return 0;
483 }
484
485 static int
486 apminstallationcheck(Apm *apm)
487 {
488         Ureg u;
489
490         u.ax = CmdInstallationCheck;
491         u.bx = DevBios;
492         if(apmcall(apm->fd, &u) < 0)
493                 return -1;
494
495         if(u.cx & 0x0004)
496                 apm->capabilities |= CapSlowCpu;
497         else
498                 apm->capabilities &= ~CapSlowCpu;
499         return 0;
500 }
501
502 void
503 apmsetdisplaystate(Apm *apm, int s)
504 {
505         apmsetpowerstate(apm, DevDisplay, s);
506 }
507
508 void
509 apmblank(Apm *apm)
510 {
511         apmsetdisplaystate(apm, PowerStandby);
512 }
513
514 void
515 apmunblank(Apm *apm)
516 {
517         apmsetdisplaystate(apm, PowerEnabled);
518 }
519
520 void
521 apmsuspend(Apm *apm)
522 {
523         apmsetpowerstate(apm, DevAll, PowerSuspend);
524 }
525
526 Apm apm;
527
528 void
529 powerprint(void)
530 {
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);
537         }
538 }
539
540 void*
541 erealloc(void *v, ulong n)
542 {
543         v = realloc(v, n);
544         if(v == nil)
545                 sysfatal("out of memory reallocating %lud", n);
546         setmalloctag(v, getcallerpc(&v));
547         return v;
548 }
549
550 void*
551 emalloc(ulong n)
552 {
553         void *v;
554
555         v = malloc(n);
556         if(v == nil)
557                 sysfatal("out of memory allocating %lud", n);
558         memset(v, 0, n);
559         setmalloctag(v, getcallerpc(&n));
560         return v;
561 }
562
563 char*
564 estrdup(char *s)
565 {
566         int l;
567         char *t;
568
569         if (s == nil)
570                 return nil;
571         l = strlen(s)+1;
572         t = emalloc(l);
573         memcpy(t, s, l);
574         setmalloctag(t, getcallerpc(&s));
575         return t;
576 }
577
578 char*
579 estrdupn(char *s, int n)
580 {
581         int l;
582         char *t;
583
584         l = strlen(s);
585         if(l > n)
586                 l = n;
587         t = emalloc(l+1);
588         memmove(t, s, l);
589         t[l] = '\0';
590         setmalloctag(t, getcallerpc(&s));
591         return t;
592 }
593
594 enum {
595         Qroot = 0,
596         Qevent,
597         Qbattery,
598         Qctl,
599 };
600
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*);
606
607 typedef struct Dfile Dfile;
608 struct Dfile {
609         Qid qid;
610         char *name;
611         ulong mode;
612         void (*read)(Req*);
613         void (*write)(Req*);
614 };
615
616 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, },
621 };
622
623 static int
624 fillstat(uvlong path, Dir *d, int doalloc)
625 {
626         int i;
627
628         for(i=0; i<nelem(dfile); i++)
629                 if(path==dfile[i].qid.path)
630                         break;
631         if(i==nelem(dfile))
632                 return -1;
633
634         memset(d, 0, sizeof *d);
635         d->uid = doalloc ? estrdup("apm") : "apm";
636         d->gid = doalloc ? estrdup("apm") : "apm";
637         d->length = 0;
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;
642         return 0;
643 }
644
645 static char*
646 fswalk1(Fid *fid, char *name, Qid *qid)
647 {
648         int i;
649
650         if(strcmp(name, "..")==0){
651                 *qid = dfile[0].qid;
652                 fid->qid = *qid;
653                 return nil;
654         }
655
656         for(i=1; i<nelem(dfile); i++){  /* i=1: 0 is root dir */
657                 if(strcmp(dfile[i].name, name)==0){
658                         *qid = dfile[i].qid;
659                         fid->qid = *qid;
660                         return nil;
661                 }
662         }
663         return "file does not exist";
664 }
665
666 static void
667 fsopen(Req *r)
668 {
669         switch((ulong)r->fid->qid.path){
670         case Qroot:
671                 r->fid->aux = (void*)0;
672                 respond(r, nil);
673                 return;
674
675         case Qevent:
676         case Qbattery:
677                 if(r->ifcall.mode == OREAD){
678                         respond(r, nil);
679                         return;
680                 }
681                 break;
682
683         case Qctl:
684                 if((r->ifcall.mode&~(OTRUNC|OREAD|OWRITE|ORDWR)) == 0){
685                         respond(r, nil);
686                         return;
687                 }
688                 break;
689         }
690         respond(r, "permission denied");
691         return;
692 }
693
694 static void
695 fsstat(Req *r)
696 {
697         fillstat(r->fid->qid.path, &r->d, 1);
698         respond(r, nil);
699 }
700
701 static void
702 fsread(Req *r)
703 {
704         dfile[r->fid->qid.path].read(r);
705 }
706
707 static void
708 fswrite(Req *r)
709 {
710         dfile[r->fid->qid.path].write(r);
711 }
712
713 static void
714 rootread(Req *r)
715 {
716         int n;
717         uvlong offset;
718         char *p, *ep;
719         Dir d;
720
721         if(r->ifcall.offset == 0)
722                 offset = 0;
723         else
724                 offset = (uvlong)r->fid->aux;
725
726         p = r->ofcall.data;
727         ep = r->ofcall.data+r->ifcall.count;
728
729         if(offset == 0)         /* skip root */
730                 offset = 1;
731         for(; p+2 < ep; p+=n){
732                 if(fillstat(offset, &d, 0) < 0)
733                         break;
734                 n = convD2M(&d, (uchar*)p, ep-p);
735                 if(n <= BIT16SZ)
736                         break;
737                 offset++;
738         }
739         r->fid->aux = (void*)offset;
740         r->ofcall.count = p - r->ofcall.data;
741         respond(r, nil);
742 }
743
744 static void
745 batteryread(Req *r)
746 {
747         char buf[Mbattery*80], *ep, *p;
748         int i;
749
750         apmgetpowerstatus(&apm, DevAll);
751
752         p = buf;
753         ep = buf+sizeof buf;
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);
759         
760         readstr(r, buf);
761         respond(r, nil);
762 }
763
764 int
765 iscmd(char *p, char *cmd)
766 {
767         int l;
768
769         l = strlen(cmd);
770         return strncmp(p, cmd, l)==0 && p[l]=='\0' || p[l]==' ' || p[l]=='\t';
771 }
772
773 char*
774 skip(char *p, char *cmd)
775 {
776         p += strlen(cmd);
777         while(*p==' ' || *p=='\t')
778                 p++;
779         return p;
780 }
781
782 static void
783 respondx(Req *r, int c)
784 {
785         char err[ERRMAX];
786
787         if(c == 0)
788                 respond(r, nil);
789         else{
790                 rerrstr(err, sizeof err);
791                 respond(r, err);
792         }
793 }
794
795 /*
796  * we don't do suspend because it messes up the
797  * cycle counter as well as the pcmcia ethernet cards.
798  */
799 static void
800 ctlwrite(Req *r)
801 {
802         char buf[80], *p, *q;
803         int dev;
804         long count;
805
806         count = r->ifcall.count;
807         if(count > sizeof(buf)-1)
808                 count = sizeof(buf)-1;
809         memmove(buf, r->ifcall.data, count);
810         buf[count] = '\0';
811
812         if(count && buf[count-1] == '\n'){
813                 --count;
814                 buf[count] = '\0';
815         }
816
817         q = buf;
818         p = strchr(q, ' ');
819         if(p==nil)
820                 p = q+strlen(q);
821         else
822                 *p++ = '\0';
823
824         if(strcmp(q, "")==0 || strcmp(q, "system")==0)
825                 dev = DevAll;
826         else if(strcmp(q, "display")==0)
827                 dev = DevDisplay;
828         else if(strcmp(q, "storage")==0)
829                 dev = DevStorage;
830         else if(strcmp(q, "lpt")==0)
831                 dev = DevLpt;
832         else if(strcmp(q, "eia")==0)
833                 dev = DevEia;
834         else if(strcmp(q, "network")==0)
835                 dev = DevNetwork;
836         else if(strcmp(q, "pcmcia")==0)
837                 dev = DevPCMCIA;
838         else{
839                 respond(r, "unknown device");
840                 return;
841         }
842
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));
851         else if(strcmp(p, "off")==0)
852                 respondx(r, apmsetpowerstate(&apm, dev, PowerOff));
853         else if(strcmp(p, "suspend")==0)
854                 respondx(r, apmsetpowerstate(&apm, dev, PowerSuspend));
855         else
856                 respond(r, "unknown verb");
857 }
858
859 static int
860 statusline(char *buf, int nbuf, char *name, int dev)
861 {
862         int s;
863         char *state;
864
865         state = "unknown";
866         if((s = apmgetpowerstate(&apm, dev)) >= 0)
867                 state = powerstate(s);
868         return snprint(buf, nbuf, "%s %s\n", name, state);
869 }
870
871 static void
872 ctlread(Req *r)
873 {
874         char buf[256+7*50], *ep, *p;
875
876         p = buf;
877         ep = buf+sizeof buf;
878
879         p += snprint(p, ep-p, "ac %s\n", acstatus(apm.acstatus));
880         p += snprint(p, ep-p, "capabilities");
881         if(apm.capabilities & CapStandby) 
882                 p += snprint(p, ep-p, " standby");
883         if(apm.capabilities & CapSuspend) 
884                 p += snprint(p, ep-p, " suspend");
885         if(apm.capabilities & CapSlowCpu)
886                 p += snprint(p, ep-p, " slowcpu");
887         p += snprint(p, ep-p, "\n");
888
889         p += statusline(p, ep-p, "system", DevAll);
890         p += statusline(p, ep-p, "display", DevDisplay);
891         p += statusline(p, ep-p, "storage", DevStorage);
892         p += statusline(p, ep-p, "lpt", DevLpt);
893         p += statusline(p, ep-p, "eia", DevEia|All);
894         p += statusline(p, ep-p, "network", DevNetwork|All);
895         p += statusline(p, ep-p, "pcmcia", DevPCMCIA|All);
896         USED(p);
897
898         readstr(r, buf);
899         respond(r, nil);
900 }
901
902 enum {
903         STACK = 16384,
904 };
905
906 Channel *creq;
907 Channel *cflush;
908 Channel *cevent;
909 Req *rlist, **tailp;
910 int rp, wp;
911 int nopoll;
912 char eventq[32][80];
913
914 static void
915 flushthread(void*)
916 {
917         Req *r, *or, **rq;
918
919         threadsetname("flushthread");
920         while(r = recvp(cflush)){
921                 or = r->oldreq;
922                 for(rq=&rlist; *rq; rq=&(*rq)->aux){
923                         if(*rq == or){
924                                 *rq = or->aux;
925                                 if(tailp==&or->aux)
926                                         tailp = rq;
927                                 respond(or, "interrupted");
928                                 break;
929                         }
930                 }
931                 respond(r, nil);
932         }
933 }
934
935 static void
936 answerany(void)
937 {
938         char *buf;
939         int l, m;
940         Req *r;
941
942         if(rlist==nil || rp==wp)
943                 return;
944
945         while(rlist && rp != wp){
946                 r = rlist;
947                 rlist = r->aux;
948                 if(rlist==nil)
949                         tailp = &rlist;
950
951                 l = 0;
952                 buf = r->ofcall.data;
953                 m = r->ifcall.count;
954                 while(rp != wp){
955                         if(l+strlen(eventq[rp]) <= m){
956                                 strcpy(buf+l, eventq[rp]);
957                                 l += strlen(buf+l);
958                         }else if(l==0){
959                                 strncpy(buf, eventq[rp], m-1);
960                                 buf[m-1] = '\0';
961                                 l += m;
962                         }else
963                                 break;
964                         rp++;
965                         if(rp == nelem(eventq))
966                                 rp = 0;
967                 }
968                 r->ofcall.count = l;
969                 respond(r, nil);
970         }
971 }
972
973 static void
974 eventwatch(void*)
975 {
976         int e, s;
977
978         threadsetname("eventwatch");
979         for(;;){
980                 s = 0;
981                 while((e = apmgetevent(&apm)) >= 0){
982                         sendul(cevent, e);
983                         s = 1;
984                 }
985                 if(s)
986                         sendul(cevent, -1);
987                 if(sleep(750) < 0)
988                         break;
989         }
990 }
991
992 static void
993 eventthread(void*)
994 {
995         int e;
996
997         threadsetname("eventthread");
998         for(;;){
999                 while((e = recvul(cevent)) >= 0){
1000                         snprint(eventq[wp], sizeof(eventq[wp])-1, "%s", apmevent(e));
1001                         strcat(eventq[wp], "\n");
1002                         wp++;
1003                         if(wp==nelem(eventq))
1004                                 wp = 0;
1005                         if(wp+1==rp || (wp+1==nelem(eventq) && rp==0))
1006                                 break;
1007                 }
1008                 answerany();
1009         }
1010 }
1011
1012 static void
1013 eventproc(void*)
1014 {
1015         Req *r;
1016
1017         threadsetname("eventproc");
1018
1019         creq = chancreate(sizeof(Req*), 0);
1020         cevent = chancreate(sizeof(ulong), 0);
1021         cflush = chancreate(sizeof(Req*), 0);
1022
1023         tailp = &rlist;
1024         if(!nopoll)
1025                 proccreate(eventwatch, nil, STACK);
1026         threadcreate(eventthread, nil, STACK);
1027         threadcreate(flushthread, nil, STACK);
1028
1029         while(r = recvp(creq)){
1030                 *tailp = r;
1031                 r->aux = nil;
1032                 tailp = &r->aux;
1033                 answerany();
1034         }
1035 }
1036
1037 static void
1038 fsflush(Req *r)
1039 {
1040         sendp(cflush, r);
1041 }
1042
1043 static void
1044 eventread(Req *r)
1045 {
1046         sendp(creq, r);
1047 }
1048
1049 static void
1050 fsattach(Req *r)
1051 {
1052         char *spec;
1053         static int first = 1;
1054
1055         spec = r->ifcall.aname;
1056
1057         if(first){
1058                 first = 0;
1059                 proccreate(eventproc, nil, STACK);
1060         }
1061
1062         if(spec && spec[0]){
1063                 respond(r, "invalid attach specifier");
1064                 return;
1065         }
1066         r->fid->qid = dfile[0].qid;
1067         r->ofcall.qid = dfile[0].qid;
1068         respond(r, nil);
1069 }
1070
1071 Srv fs = {
1072 .attach=        fsattach,
1073 .walk1= fswalk1,
1074 .open=  fsopen,
1075 .read=  fsread,
1076 .write= fswrite,
1077 .stat=  fsstat,
1078 .flush= fsflush,
1079 };
1080
1081 void
1082 usage(void)
1083 {
1084         fprint(2, "usage: aux/apm [-ADP] [-d /dev/apm] [-m /mnt/apm] [-s service]\n");
1085         exits("usage");
1086 }
1087
1088 void
1089 threadmain(int argc, char **argv)
1090 {
1091         char *dev, *mtpt, *srv;
1092
1093         dev = nil;
1094         mtpt = "/mnt/apm";
1095         srv = nil;
1096         ARGBEGIN{
1097         case 'A':
1098                 apmdebug = 1;
1099                 break;
1100         case 'D':
1101                 chatty9p = 1;
1102                 break;
1103         case 'P':
1104                 nopoll = 1;
1105                 break;
1106         case 'd':
1107                 dev = EARGF(usage());
1108                 break;
1109         case 'm':
1110                 mtpt = EARGF(usage());
1111                 break;
1112         case 's':
1113                 srv = EARGF(usage());
1114                 break;
1115         }ARGEND
1116
1117         if(dev == nil){
1118                 if((apm.fd = open("/dev/apm", ORDWR)) < 0
1119                 && (apm.fd = open("#P/apm", ORDWR)) < 0){
1120                         fprint(2, "open %s: %r\n", dev);
1121                         threadexitsall("open");
1122                 }
1123         } else if((apm.fd = open(dev, ORDWR)) < 0){
1124                 fprint(2, "open %s: %r\n", dev);
1125                 threadexitsall("open");
1126         }
1127
1128         if(apmversion(&apm) < 0){
1129                 fprint(2, "cannot get apm version: %r\n");
1130                 threadexitsall("apmversion");
1131         }
1132
1133         if(apm.verhi < 1 || (apm.verhi==1 && apm.verlo < 2)){
1134                 fprint(2, "apm version %d.%d not supported\n", apm.verhi, apm.verlo);
1135                 threadexitsall("apmversion");
1136         }
1137
1138         if(apmgetcapabilities(&apm) < 0)
1139                 fprint(2, "warning: cannot read apm capabilities: %r\n");
1140
1141         apminstallationcheck(&apm);
1142         apmcpuidle(&apm);
1143
1144         threadpostmountsrv(&fs, srv, mtpt, MREPL);
1145 }