xref: /plan9/sys/src/cmd/aux/apm.c (revision 5e91980f0bca263e952809e3dc0cfc5dde74b999)
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