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*
apmevent(int e)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*
apmerror(int id)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
_apmcall(int fd,Ureg * u)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
apmcall(int fd,Ureg * u)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*
xstatus(char ** str,int nstr,int x)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*
batterystatus(int b)259 batterystatus(int b)
260 {
261 return xstatus(batterystatusstr, nelem(batterystatusstr), b);
262 }
263
264 static char*
powerstate(int s)265 powerstate(int s)
266 {
267 return xstatus(powerstatestr, nelem(powerstatestr), s);
268 }
269
270 static char*
acstatus(int a)271 acstatus(int a)
272 {
273 return xstatus(acstatusstr, nelem(acstatusstr), a);
274 }
275
276 static int
apmversion(Apm * apm)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
apmcpuidle(Apm * apm)294 apmcpuidle(Apm *apm)
295 {
296 Ureg u;
297
298 u.ax = CmdCpuIdle;
299 return apmcall(apm->fd, &u);
300 }
301
302 static int
apmcpubusy(Apm * apm)303 apmcpubusy(Apm *apm)
304 {
305 Ureg u;
306
307 u.ax = CmdCpuBusy;
308 return apmcall(apm->fd, &u);
309 }
310
311 static int
apmsetpowerstate(Apm * apm,int dev,int state)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
apmsetpowermgmt(Apm * apm,int dev,int state)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
apmrestoredefaults(Apm * apm,int dev)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
apmgetpowerstatus(Apm * apm,int dev)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
apmgetevent(Apm * apm)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
apmgetpowerstate(Apm * apm,int dev)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
apmgetpowermgmt(Apm * apm,int dev)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
apmgetcapabilities(Apm * apm)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
apminstallationcheck(Apm * apm)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
apmsetdisplaystate(Apm * apm,int s)503 apmsetdisplaystate(Apm *apm, int s)
504 {
505 apmsetpowerstate(apm, DevDisplay, s);
506 }
507
508 void
apmblank(Apm * apm)509 apmblank(Apm *apm)
510 {
511 apmsetdisplaystate(apm, PowerStandby);
512 }
513
514 void
apmunblank(Apm * apm)515 apmunblank(Apm *apm)
516 {
517 apmsetdisplaystate(apm, PowerEnabled);
518 }
519
520 void
apmsuspend(Apm * apm)521 apmsuspend(Apm *apm)
522 {
523 apmsetpowerstate(apm, DevAll, PowerSuspend);
524 }
525
526 Apm apm;
527
528 void
powerprint(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*
erealloc(void * v,ulong n)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*
emalloc(ulong n)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*
estrdup(char * s)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*
estrdupn(char * s,int n)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
fillstat(uvlong path,Dir * d,int doalloc)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*
fswalk1(Fid * fid,char * name,Qid * qid)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
fsopen(Req * r)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
fsstat(Req * r)695 fsstat(Req *r)
696 {
697 fillstat(r->fid->qid.path, &r->d, 1);
698 respond(r, nil);
699 }
700
701 static void
fsread(Req * r)702 fsread(Req *r)
703 {
704 dfile[r->fid->qid.path].read(r);
705 }
706
707 static void
fswrite(Req * r)708 fswrite(Req *r)
709 {
710 dfile[r->fid->qid.path].write(r);
711 }
712
713 static void
rootread(Req * r)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
batteryread(Req * r)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
iscmd(char * p,char * cmd)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*
skip(char * p,char * cmd)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
respondx(Req * r,int c)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
ctlwrite(Req * r)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 /*
852 else if(strcmp(p, "off")==0)
853 respondx(r, apmsetpowerstate(&apm, dev, PowerOff));
854 */
855 else if(strcmp(p, "suspend")==0)
856 respondx(r, apmsetpowerstate(&apm, dev, PowerSuspend));
857 else
858 respond(r, "unknown verb");
859 }
860
861 static int
statusline(char * buf,int nbuf,char * name,int dev)862 statusline(char *buf, int nbuf, char *name, int dev)
863 {
864 int s;
865 char *state;
866
867 state = "unknown";
868 if((s = apmgetpowerstate(&apm, dev)) >= 0)
869 state = powerstate(s);
870 return snprint(buf, nbuf, "%s %s\n", name, state);
871 }
872
873 static void
ctlread(Req * r)874 ctlread(Req *r)
875 {
876 char buf[256+7*50], *ep, *p;
877
878 p = buf;
879 ep = buf+sizeof buf;
880
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");
890
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);
898 USED(p);
899
900 readstr(r, buf);
901 respond(r, nil);
902 }
903
904 enum {
905 STACK = 16384,
906 };
907
908 Channel *creq;
909 Channel *cflush;
910 Channel *cevent;
911 Req *rlist, **tailp;
912 int rp, wp;
913 int nopoll;
914 char eventq[32][80];
915
916 static void
flushthread(void *)917 flushthread(void*)
918 {
919 Req *r, *or, **rq;
920
921 threadsetname("flushthread");
922 while(r = recvp(cflush)){
923 or = r->oldreq;
924 for(rq=&rlist; *rq; rq=&(*rq)->aux){
925 if(*rq == or){
926 *rq = or->aux;
927 if(tailp==&or->aux)
928 tailp = rq;
929 respond(or, "interrupted");
930 break;
931 }
932 }
933 respond(r, nil);
934 }
935 }
936
937 static void
answerany(void)938 answerany(void)
939 {
940 char *buf;
941 int l, m;
942 Req *r;
943
944 if(rlist==nil || rp==wp)
945 return;
946
947 while(rlist && rp != wp){
948 r = rlist;
949 rlist = r->aux;
950 if(rlist==nil)
951 tailp = &rlist;
952
953 l = 0;
954 buf = r->ofcall.data;
955 m = r->ifcall.count;
956 while(rp != wp){
957 if(l+strlen(eventq[rp]) <= m){
958 strcpy(buf+l, eventq[rp]);
959 l += strlen(buf+l);
960 }else if(l==0){
961 strncpy(buf, eventq[rp], m-1);
962 buf[m-1] = '\0';
963 l += m;
964 }else
965 break;
966 rp++;
967 if(rp == nelem(eventq))
968 rp = 0;
969 }
970 r->ofcall.count = l;
971 respond(r, nil);
972 }
973 }
974
975 static void
eventwatch(void *)976 eventwatch(void*)
977 {
978 int e, s;
979
980 threadsetname("eventwatch");
981 for(;;){
982 s = 0;
983 while((e = apmgetevent(&apm)) >= 0){
984 sendul(cevent, e);
985 s = 1;
986 }
987 if(s)
988 sendul(cevent, -1);
989 if(sleep(750) < 0)
990 break;
991 }
992 }
993
994 static void
eventthread(void *)995 eventthread(void*)
996 {
997 int e;
998
999 threadsetname("eventthread");
1000 for(;;){
1001 while((e = recvul(cevent)) >= 0){
1002 snprint(eventq[wp], sizeof(eventq[wp])-1, "%s", apmevent(e));
1003 strcat(eventq[wp], "\n");
1004 wp++;
1005 if(wp==nelem(eventq))
1006 wp = 0;
1007 if(wp+1==rp || (wp+1==nelem(eventq) && rp==0))
1008 break;
1009 }
1010 answerany();
1011 }
1012 }
1013
1014 static void
eventproc(void *)1015 eventproc(void*)
1016 {
1017 Req *r;
1018
1019 threadsetname("eventproc");
1020
1021 creq = chancreate(sizeof(Req*), 0);
1022 cevent = chancreate(sizeof(ulong), 0);
1023 cflush = chancreate(sizeof(Req*), 0);
1024
1025 tailp = &rlist;
1026 if(!nopoll)
1027 proccreate(eventwatch, nil, STACK);
1028 threadcreate(eventthread, nil, STACK);
1029 threadcreate(flushthread, nil, STACK);
1030
1031 while(r = recvp(creq)){
1032 *tailp = r;
1033 r->aux = nil;
1034 tailp = &r->aux;
1035 answerany();
1036 }
1037 }
1038
1039 static void
fsflush(Req * r)1040 fsflush(Req *r)
1041 {
1042 sendp(cflush, r);
1043 }
1044
1045 static void
eventread(Req * r)1046 eventread(Req *r)
1047 {
1048 sendp(creq, r);
1049 }
1050
1051 static void
fsattach(Req * r)1052 fsattach(Req *r)
1053 {
1054 char *spec;
1055 static int first = 1;
1056
1057 spec = r->ifcall.aname;
1058
1059 if(first){
1060 first = 0;
1061 proccreate(eventproc, nil, STACK);
1062 }
1063
1064 if(spec && spec[0]){
1065 respond(r, "invalid attach specifier");
1066 return;
1067 }
1068 r->fid->qid = dfile[0].qid;
1069 r->ofcall.qid = dfile[0].qid;
1070 respond(r, nil);
1071 }
1072
1073 Srv fs = {
1074 .attach= fsattach,
1075 .walk1= fswalk1,
1076 .open= fsopen,
1077 .read= fsread,
1078 .write= fswrite,
1079 .stat= fsstat,
1080 .flush= fsflush,
1081 };
1082
1083 void
usage(void)1084 usage(void)
1085 {
1086 fprint(2, "usage: aux/apm [-ADPi] [-d /dev/apm] [-m /mnt/apm] [-s service]\n");
1087 exits("usage");
1088 }
1089
1090 void
threadmain(int argc,char ** argv)1091 threadmain(int argc, char **argv)
1092 {
1093 char *dev, *mtpt, *srv;
1094
1095 dev = nil;
1096 mtpt = "/mnt/apm";
1097 srv = nil;
1098 ARGBEGIN{
1099 case 'A':
1100 apmdebug = 1;
1101 break;
1102 case 'D':
1103 chatty9p = 1;
1104 break;
1105 case 'P':
1106 nopoll = 1;
1107 break;
1108 case 'd':
1109 dev = EARGF(usage());
1110 break;
1111 case 'i':
1112 fs.nopipe++;
1113 fs.infd = 0;
1114 fs.outfd = 1;
1115 mtpt = nil;
1116 break;
1117 case 'm':
1118 mtpt = EARGF(usage());
1119 break;
1120 case 's':
1121 srv = EARGF(usage());
1122 break;
1123 }ARGEND
1124
1125 if(dev == nil){
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");
1130 }
1131 } else if((apm.fd = open(dev, ORDWR)) < 0){
1132 fprint(2, "open %s: %r\n", dev);
1133 threadexitsall("open");
1134 }
1135
1136 if(apmversion(&apm) < 0){
1137 fprint(2, "cannot get apm version: %r\n");
1138 threadexitsall("apmversion");
1139 }
1140
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");
1144 }
1145
1146 if(apmgetcapabilities(&apm) < 0)
1147 fprint(2, "warning: cannot read apm capabilities: %r\n");
1148
1149 apminstallationcheck(&apm);
1150 apmcpuidle(&apm);
1151
1152 rfork(RFNOTEG);
1153 threadpostmountsrv(&fs, srv, mtpt, MREPL);
1154 }
1155