apm.c revision 3260
1/*
2 * LP (Laptop Package)
3 *
4 * Copyright (c) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
5 *
6 * This software may be used, modified, copied, and distributed, in
7 * both source and binary form provided that the above copyright and
8 * these terms are retained. Under no circumstances is the author
9 * responsible for the proper functioning of this software, nor does
10 * the author assume any responsibility for damages incurred with its
11 * use.
12 *
13 * Sep, 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
14 */
15
16#include "apm.h"
17
18#if NAPM > 0
19
20#include <sys/param.h>
21#include "conf.h"
22#include <sys/kernel.h>
23#include <sys/systm.h>
24#include <sys/malloc.h>
25#include <sys/ioctl.h>
26#include <sys/tty.h>
27#include <sys/file.h>
28#include <sys/proc.h>
29#include <sys/vnode.h>
30#include "i386/isa/isa.h"
31#include "i386/isa/isa_device.h"
32#include <machine/apm_bios.h>
33#include <machine/segments.h>
34#include <vm/vm.h>
35#include <sys/syslog.h>
36#include "apm_setup.h"
37
38/* static data */
39static int	apm_initialized = 0, active = 0, halt_cpu = 1;
40static u_int	minorversion, majorversion;
41static u_int	cs32_base, cs16_base, ds_base;
42static u_int	cs_limit, ds_limit;
43static u_int	cs_entry;
44static u_int	intversion;
45static int	idle_cpu, disabled, disengaged;
46
47/* Map version number to integer (keeps ordering of version numbers) */
48#define INTVERSION(major, minor)	((major)*100 + (minor))
49
50static timeout_t apm_timeout;
51
52/* setup APM GDT discriptors */
53static void
54setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code_limit, u_int data_limit)
55{
56	/* setup 32bit code segment */
57	gdt_segs[GAPMCODE32_SEL].ssd_base  = code32_base;
58	gdt_segs[GAPMCODE32_SEL].ssd_limit = code_limit;
59
60	/* setup 16bit code segment */
61	gdt_segs[GAPMCODE16_SEL].ssd_base  = code16_base;
62	gdt_segs[GAPMCODE16_SEL].ssd_limit = code_limit;
63
64	/* setup data segment */
65	gdt_segs[GAPMDATA_SEL  ].ssd_base  = data_base;
66	gdt_segs[GAPMDATA_SEL  ].ssd_limit = data_limit;
67
68	/* reflect these changes on physical GDT */
69	ssdtosd(gdt_segs + GAPMCODE32_SEL, gdt + GAPMCODE32_SEL);
70	ssdtosd(gdt_segs + GAPMCODE16_SEL, gdt + GAPMCODE16_SEL);
71	ssdtosd(gdt_segs + GAPMDATA_SEL  , gdt + GAPMDATA_SEL  );
72}
73
74/* 48bit far pointer */
75struct addr48 {
76	u_long		offset;
77	u_short		segment;
78} apm_addr;
79
80
81/* register structure for BIOS call */
82union real_regs {
83	struct xregs {
84		u_short	ax;
85		u_short	bx __attribute__ ((packed));
86		u_short	cx __attribute__ ((packed));
87		u_short	dx __attribute__ ((packed));
88		u_short	si __attribute__ ((packed));
89		u_short	di __attribute__ ((packed));
90		u_short	cf __attribute__ ((packed));	/* carry */
91	} x;
92	struct hlregs {
93		u_char	al;
94		u_char	ah __attribute__ ((packed));
95		u_char	bl __attribute__ ((packed));
96		u_char	bh __attribute__ ((packed));
97		u_char	cl __attribute__ ((packed));
98		u_char	ch __attribute__ ((packed));
99		u_char	dl __attribute__ ((packed));
100		u_char	dh __attribute__ ((packed));
101		u_short	si __attribute__ ((packed));
102		u_short	di __attribute__ ((packed));
103		u_short	cf __attribute__ ((packed));	/* carry */
104	} hl;
105};
106
107
108/* call APM BIOS */
109extern void call_apm(union real_regs* regs);
110
111extern u_char apm_errno;
112
113/* enable/disable power management */
114static int
115apm_enable_disable_pm(int enable)
116{
117	union real_regs regs;
118
119	regs.hl.ah = APM_BIOS;
120	regs.hl.al = APM_ENABLEDISABLEPM;
121	if (intversion >= INTVERSION(1, 1)) {
122		regs.x.bx  = PMDV_ALLDEV;
123	}
124	else {
125		regs.x.bx  = 0xffff;	/* APM version 1.0 only */
126	}
127	regs.x.cx  = enable;
128	call_apm(&regs);
129	return regs.x.cf;
130}
131
132/* engage/disengage power management (APM 1.1 or later) */
133static int
134apm_engage_disengage_pm(int engage)
135{
136	union real_regs regs;
137
138	regs.hl.ah = APM_BIOS;
139	regs.hl.al = APM_ENGAGEDISENGAGEPM;
140	regs.x.bx = PMDV_ALLDEV;
141	regs.x.cx = engage;
142	call_apm(&regs);
143	return regs.x.cf;
144}
145
146/* get PM event */
147static u_int
148apm_getevent(void)
149{
150	union real_regs regs;
151
152	regs.hl.ah = APM_BIOS;
153	regs.hl.al = APM_GETPMEVENT;
154	call_apm(&regs);
155	if (regs.x.cf) {
156#if 0
157		printf("No event: errcode = %d\n", apm_errno);
158#endif
159		return PMEV_NOEVENT;
160	}
161	return (u_int)regs.x.bx;
162}
163
164/*
165 * In many cases, the first event that occured after resume, needs
166 * special treatment. This binary flag make this process possible.
167 * Initial value of this variable is 1, because the bootstrap
168 * condition is equivalent to resumed condition for the power
169 * manager.
170 */
171static int	resumed_event = 1;
172
173/* suspend entire system */
174static int
175apm_suspend_system(void)
176{
177	union real_regs regs;
178
179	regs.hl.ah = APM_BIOS;
180	regs.hl.al = APM_SETPWSTATE;
181	regs.x.bx = PMDV_ALLDEV;
182	regs.x.cx = PMST_SUSPEND;
183	call_apm(&regs);
184	if (regs.x.cf) {
185		printf("Entire system suspend failure: errcode = %d\n", apm_errno);
186		return 1;
187	}
188	resumed_event = 1;
189
190	return 0;
191}
192
193/* APM Battery low handler */
194static void
195apm_battery_low(void)
196{
197	/* Currently, this routine has not been implemented. Sorry... */
198}
199
200
201/* APM driver calls some functions automatically when the system wakes up */
202static void
203apm_execute_hook(apm_hook_func_t list)
204{
205	apm_hook_func_t p;
206
207	for (p = list; p != NULL; p = p->next) {
208		if ((*(p->func))()) {
209			printf("Warning: APM hook of %s failed", p->name);
210		}
211	}
212}
213
214
215/* APM hook manager */
216static apm_hook_func_t
217apm_hook_init(apm_hook_func_t *list, int (*func)(void), char *name, int order)
218{
219	int pl;
220	apm_hook_func_t p, prev, new_node;
221
222	pl = splhigh();
223	new_node = malloc(sizeof(*new_node), M_DEVBUF, M_NOWAIT);
224	if (new_node == NULL) {
225		panic("Can't allocate device buffer for apm_resume_hook.");
226	}
227	new_node->func = func;
228	new_node->name = name;
229#if 0
230	new_node->next = *list;
231	*list = new_node;
232#else
233	prev = NULL;
234	for (p = *list; p != NULL; prev = p, p = p->next) {
235		if (p->order > order) {
236			break;
237		}
238	}
239
240	if (prev == NULL) {
241		new_node->next = *list;
242		*list = new_node;
243	}
244	else {
245		new_node->next = prev->next;
246		prev->next = new_node;
247	}
248#endif
249	splx(pl);
250	return new_node;
251}
252
253void
254apm_hook_delete(apm_hook_func_t *list, apm_hook_func_t delete_node)
255{
256	int pl;
257	apm_hook_func_t p, prev;
258
259	pl = splhigh();
260	prev = NULL;
261	for (p = *list; p != NULL; prev = p, p = p->next) {
262		if (p == delete_node) {
263			goto deleteit;
264		}
265	}
266	panic("Tried to delete unregistered apm_resume_hook.");
267	goto nosuchnode;
268deleteit:
269	if (prev != NULL) {
270		prev->next = p->next;
271	}
272	else {
273		*list = p->next;
274	}
275	free(delete_node, M_DEVBUF);
276nosuchnode:
277	splx(pl);
278}
279
280static struct timeval suspend_time;
281
282/* default APM hook functions */
283static int
284apm_default_resume(void)
285{
286	u_int second, minute, hour;
287	struct timeval resume_time;
288
289	inittodr(0);	/* adjust time to RTC */
290	microtime(&resume_time);
291	second = resume_time.tv_sec - suspend_time.tv_sec;
292	hour = second / 3600;
293	second %= 3600;
294	minute = second / 60;
295	second %= 60;
296	log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", hour, minute, second);
297	return 0;
298}
299
300static int
301apm_default_suspend(void)
302{
303	int	pl;
304#if 0
305	pl = splhigh();
306	sync(curproc, NULL, NULL);
307	splx(pl);
308#endif
309	microtime(&suspend_time);
310	return 0;
311}
312
313/* list structure for hook */
314static apm_hook_func_t	apm_resume_hook  = NULL;
315static apm_hook_func_t	apm_suspend_hook = NULL;
316
317/* execute resume hook */
318static void
319apm_execute_resume_hook(void)
320{
321	apm_execute_hook(apm_resume_hook);
322}
323
324/* add a node on resume hook */
325apm_hook_func_t
326apm_resume_hook_init(int (*func)(void), char *name, int order)
327{
328	return apm_hook_init(&apm_resume_hook, func, name, order);
329}
330
331/* delete a node from resume hook */
332void
333apm_resume_hook_delete(apm_hook_func_t delete_node)
334{
335	apm_hook_delete(&apm_resume_hook, delete_node);
336}
337
338/* execute suspend hook */
339static void
340apm_execute_suspend_hook(void)
341{
342	apm_execute_hook(apm_suspend_hook);
343}
344
345/* add a node on resume hook */
346apm_hook_func_t
347apm_suspend_hook_init(int (*func)(void), char *name, int order)
348{
349	return apm_hook_init(&apm_suspend_hook, func, name, order);
350}
351
352/* delete a node from resume hook */
353void
354apm_suspend_hook_delete(apm_hook_func_t delete_node)
355{
356	apm_hook_delete(&apm_suspend_hook, delete_node);
357}
358
359/* get APM information */
360static int
361apm_get_info(apm_info_t aip)
362{
363	union real_regs regs;
364
365	regs.hl.ah = APM_BIOS;
366	regs.hl.al = APM_GETPWSTATUS;
367	regs.x.bx = PMDV_ALLDEV;
368	call_apm(&regs);
369	if (regs.x.cf) {
370		printf("Get APM info failure: errcode = %d\n", apm_errno);
371		return 1;
372	}
373	aip->ai_major = (u_int)majorversion;
374	aip->ai_minor = (u_int)minorversion;
375	aip->ai_acline = (u_int)regs.hl.bh;
376	aip->ai_batt_stat = (u_int)regs.hl.bl;
377	aip->ai_batt_life = (u_int)regs.hl.cl;
378	return 0;
379}
380
381
382/* Define equivalent event sets */
383
384static int equiv_event_num = 0;
385static struct apm_eqv_event equiv_events[APM_MAX_EQUIV_EVENTS];
386
387static int
388apm_def_eqv(apm_eqv_event_t aee)
389{
390	if (equiv_event_num == APM_MAX_EQUIV_EVENTS) {
391		return 1;
392	}
393	memcpy(&equiv_events[equiv_event_num], aee, sizeof(struct apm_eqv_event));
394	equiv_event_num++;
395	return 0;
396}
397
398static void
399apm_flush_eqv(void)
400{
401	equiv_event_num = 0;
402}
403
404static void apm_processevent(void);
405
406/*
407 * Public interface to the suspend/resume:
408 *
409 * Execute suspend and resume hook before and after sleep, respectively.
410 */
411
412void
413apm_suspend_resume(void)
414{
415	int	pl;
416#if 0
417	printf("Called apm_suspend_resume();\n");
418#endif
419	if (apm_initialized) {
420		apm_execute_suspend_hook();
421		apm_suspend_system();
422		apm_execute_resume_hook();
423		apm_processevent();
424	}
425}
426
427/* inform APM BIOS that CPU is idle */
428void
429apm_cpu_idle(void)
430{
431	if (idle_cpu) {
432		if (active) {
433			asm("movw $0x5305, %ax; lcall _apm_addr");
434		}
435	}
436	/*
437	 * Some APM implementation halts CPU in BIOS, whenever
438	 * "CPU-idle" function are invoked, but swtch() of
439	 * FreeBSD halts CPU, therefore, CPU is halted twice
440	 * in the sched loop. It makes the interrupt latency
441	 * terribly long and be able to cause a serious problem
442	 * in interrupt processing. We prevent it by removing
443	 * "hlt" operation from swtch() and managed it under
444	 * APM driver.
445	 */
446	if (!active || halt_cpu) {
447		asm("sti ; hlt");	/* wait for interrupt */
448	}
449}
450
451/* inform APM BIOS that CPU is busy */
452void
453apm_cpu_busy(void)
454{
455	if (idle_cpu && active) {
456		asm("movw $0x5306, %ax; lcall _apm_addr");
457	}
458}
459
460
461/*
462 * APM timeout routine:
463 *
464 * This routine is automatically called by timer two times within one
465 * seconed.
466 */
467
468static void
469apm_timeout(void *arg1)
470{
471#if 0
472	printf("Called apm_timeout\n");
473#endif
474	apm_processevent();
475	timeout(apm_timeout, NULL, hz / 2); /* 2 Hz */
476	/* APM driver must polls APM event a time per second */
477}
478
479/* enable APM BIOS */
480static void
481apm_event_enable(void)
482{
483#if 0
484	printf("called apm_event_enable()\n");
485#endif
486	if (apm_initialized) {
487		active = 1;
488		timeout(apm_timeout, NULL, 2 * hz);
489	}
490}
491
492/* disable APM BIOS */
493static void
494apm_event_disable(void)
495{
496#if 0
497	printf("called apm_event_disable()\n");
498#endif
499	if (apm_initialized) {
500		untimeout(apm_timeout, NULL);
501		active = 0;
502	}
503}
504
505/* halt CPU in scheduling loop */
506static void apm_halt_cpu(void)
507{
508	if (apm_initialized) {
509		halt_cpu = 1;
510	}
511}
512
513/* don't halt CPU in scheduling loop */
514static void apm_not_halt_cpu(void)
515{
516	if (apm_initialized) {
517		halt_cpu = 0;
518	}
519}
520
521/* device driver definitions */
522int apmprobe (struct isa_device *);
523int apmattach(struct isa_device *);
524
525struct isa_driver apmdriver = { apmprobe, apmattach, "apm" };
526
527
528/*
529 * probe APM (dummy):
530 *
531 * APM probing routine is placed on locore.s and apm_init.S because
532 * this process forces the CPU to turn to real mode or V86 mode.
533 * Current version uses real mode, but on future version, we want
534 * to use V86 mode in APM initialization.
535 */
536int
537apmprobe(struct isa_device *dvp)
538{
539	switch (apm_version) {
540	case APMINI_CANTFIND:
541		/* silent */
542		return 0;
543	case APMINI_NOT32BIT:
544		printf("apm%d: 32bit connection is not supported.\n", dvp->id_unit);
545		return 0;
546	case APMINI_CONNECTERR:
547		printf("apm%d: 32-bit connection error.\n", dvp->id_unit);
548		return 0;
549	}
550	if ((apm_version & 0xff00) != 0x0100) return 0;
551	if ((apm_version & 0x00f0) >= 0x00a0) return 0;
552	if ((apm_version & 0x000f) >= 0x000a) return 0;
553	return -1;
554}
555
556static const char *
557is_enabled(int enabled)
558{
559	if (enabled) {
560		return "enabled";
561	}
562	return "disabled";
563}
564
565static const char *
566apm_error(void)
567{
568	static char buffer[64];
569
570	switch (apm_errno) {
571	case 0:
572		return "APM OK.";
573	default:
574		sprintf(buffer, "Unknown Error 0x%x", (u_int)apm_errno);
575		return buffer;
576	}
577}
578
579
580
581/* Process APM event */
582static void
583apm_processevent(void)
584{
585	int i, apm_event;
586
587getevent:
588	while (1) {
589		if ((apm_event = apm_getevent()) == PMEV_NOEVENT) {
590			break;
591		}
592#if 0
593#if 1
594#define OPMEV_DEBUGMESSAGE(symbol) case symbol: break;
595#else
596#define OPMEV_DEBUGMESSAGE(symbol) case symbol: printf("Original APM Event: " #symbol "\n"); break
597#endif
598		switch (apm_event) {
599			OPMEV_DEBUGMESSAGE(PMEV_NOEVENT);
600			OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
601			OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
602			OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
603			OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
604			OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
605			OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
606			OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
607			OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
608			OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
609			OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
610			default:
611				printf("Unknown Original APM Event 0x%x\n", apm_event);
612				break;
613		}
614#endif
615		for (i = 0; i < equiv_event_num; i++) {
616			if (equiv_events[i].aee_event == apm_event) {
617				u_int tmp = PMEV_DEFAULT;
618				if (resumed_event) {
619					tmp = equiv_events[i].aee_resume;
620				}
621				else {
622					tmp = equiv_events[i].aee_equiv;
623				}
624				if (tmp != PMEV_DEFAULT) {
625					apm_event = tmp;
626					break;
627				}
628			}
629		}
630#if 1
631#if 1
632#define PMEV_DEBUGMESSAGE(symbol) case symbol: break;
633#else
634#define PMEV_DEBUGMESSAGE(symbol) case symbol: printf("APM Event: " #symbol "\n"); break
635#endif
636		switch (apm_event) {
637			PMEV_DEBUGMESSAGE(PMEV_NOEVENT);
638			PMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
639			PMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
640			PMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
641			PMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
642			PMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
643			PMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
644			PMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
645			PMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
646			PMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
647			PMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
648			default:
649				printf("Unknown APM Event 0x%x\n", apm_event);
650				break;
651		}
652#endif
653		switch (apm_event) {
654		case PMEV_NOEVENT:
655		case PMEV_STANDBYREQ:
656		case PMEV_POWERSTATECHANGE:
657		case PMEV_CRITSUSPEND:
658		case PMEV_USERSTANDBYREQ:
659		case PMEV_USERSUSPENDREQ:
660			break;
661		case PMEV_BATTERYLOW:
662			apm_battery_low();
663			break;
664		case PMEV_SUSPENDREQ:
665			apm_suspend_resume();
666			break;
667		case PMEV_NORMRESUME:
668		case PMEV_CRITRESUME:
669		case PMEV_UPDATETIME:
670		case PMEV_STANDBYRESUME:
671			inittodr(0);	/* adjust time to RTC */
672			break;
673		}
674	}
675	resumed_event = 0;
676}
677
678
679/*
680 * Attach APM:
681 *
682 * Initialize APM driver (APM BIOS itself has been initialized in locore.s)
683 */
684
685int
686apmattach(struct isa_device *dvp)
687{
688
689	/* setup APM parameters */
690	minorversion = ((apm_version & 0x00f0) >>  4) * 10 + ((apm_version & 0x000f) >> 0);
691	majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8);
692	intversion = INTVERSION(majorversion, minorversion);
693	cs32_base = (apm_cs32_base << 4) + KERNBASE;
694	cs16_base = (apm_cs16_base << 4) + KERNBASE;
695	ds_base = (apm_ds_base << 4) + KERNBASE;
696	cs_limit = apm_cs_limit;
697	ds_limit = apm_ds_limit;
698	cs_entry = apm_cs_entry;
699	idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0);
700	disabled = ((apm_flags & APM_DISABLED) != 0);
701	disengaged = ((apm_flags & APM_DISENGAGED) != 0);
702
703	/* print bootstrap messages */
704#ifdef DEBUG
705	printf(" found APM BIOS version %d.%d\n", dvp->id_unit, majorversion, minorversion);
706	printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", dvp->id_unit, cs32_base, cs16_base, ds_base);
707	printf("apm%d: Code entry 0x%08x, Idling CPU %s, Management %s\n", dvp->id_unit, cs_entry, is_enabled(idle_cpu), is_enabled(!disabled));
708#else
709	printf(" found APM BIOS version %d.%d\n", majorversion, minorversion);
710	printf("apm%d: Idling CPU %s\n", dvp->id_unit, is_enabled(idle_cpu));
711#endif
712
713	/*
714	 * APM 1.0 does not have:
715	 *
716	 * 	1. segment limit parameters
717	 *
718	 *	2. engage/disengage operations
719	 */
720	if (intversion >= INTVERSION(1, 1)) {
721		printf("apm%d: Engaged control %s\n", dvp->id_unit, is_enabled(!disengaged));
722	}
723	else {
724		cs_limit = 0xffff;
725		ds_limit = 0xffff;
726	}
727
728	/* setup GDT */
729	setup_apm_gdt(cs32_base, cs16_base, ds_base, cs_limit, ds_limit);
730
731	/* setup entry point 48bit pointer */
732	apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
733	apm_addr.offset  = cs_entry;
734
735	/* enable power management */
736	if (disabled) {
737		if (apm_enable_disable_pm(1)) {
738			printf("Warning: APM enable function failed! [%s]\n", apm_error());
739		}
740	}
741
742	/* engage power managment (APM 1.1 or later) */
743	if (intversion >= INTVERSION(1, 1) && disengaged) {
744		if (apm_engage_disengage_pm(1)) {
745			printf("Warning: APM engage function failed [%s]\n", apm_error());
746		}
747	}
748
749	apm_suspend_hook_init(apm_default_suspend, "default suspend", APM_MAX_ORDER);
750	apm_resume_hook_init (apm_default_resume , "default resume" , APM_MIN_ORDER);
751	apm_initialized = 1;
752
753	return 0;
754}
755
756int
757apmopen(dev_t dev, int flag, int fmt, struct proc *p)
758{
759	if (!apm_initialized) {
760		return ENXIO;
761	}
762	switch (minor(dev)) {
763	case 0:	/* apm0 */
764		break;
765	defaults:
766		return (ENXIO);
767	}
768	return 0;
769}
770
771int
772apmclose(dev_t dev, int flag, int fmt, struct proc *p)
773{
774	return 0;
775}
776
777int
778apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
779{
780	int error = 0;
781	int pl;
782
783#if 0
784	printf("APM ioctl: minor = %d, cmd = 0x%x\n", minor(dev), cmd);
785#endif
786
787	pl = splhigh();
788	if (minor(dev) != 0) {
789		return ENXIO;
790	}
791	if (!apm_initialized) {
792		return ENXIO;
793	}
794	switch (cmd) {
795	case APMIO_SUSPEND:
796		apm_suspend_resume();
797		break;
798	case APMIO_GETINFO:
799		if (apm_get_info((apm_info_t)addr)) {
800			error = ENXIO;
801		}
802		break;
803	case APMIO_DEFEQV:
804		if (apm_def_eqv((apm_eqv_event_t)addr)) {
805			error = ENOSPC;
806		}
807		break;
808	case APMIO_FLUSHEQV:
809		apm_flush_eqv();
810		break;
811	case APMIO_ENABLE:
812		apm_event_enable();
813		break;
814	case APMIO_DISABLE:
815		apm_event_disable();
816		break;
817	case APMIO_HALTCPU:
818		apm_halt_cpu();
819		break;
820	case APMIO_NOTHALTCPU:
821		apm_not_halt_cpu();
822		break;
823	default:
824		error = EINVAL;
825		break;
826	}
827	splx(pl);
828	return error;
829}
830
831#endif /* NAPM > 0 */
832