apm.c revision 3258
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	return -1;
551}
552
553static const char *
554is_enabled(int enabled)
555{
556	if (enabled) {
557		return "enabled";
558	}
559	return "disabled";
560}
561
562static const char *
563apm_error(void)
564{
565	static char buffer[64];
566
567	switch (apm_errno) {
568	case 0:
569		return "APM OK.";
570	default:
571		sprintf(buffer, "Unknown Error 0x%x", (u_int)apm_errno);
572		return buffer;
573	}
574}
575
576
577
578/* Process APM event */
579static void
580apm_processevent(void)
581{
582	int i, apm_event;
583
584getevent:
585	while (1) {
586		if ((apm_event = apm_getevent()) == PMEV_NOEVENT) {
587			break;
588		}
589#if 0
590#if 1
591#define OPMEV_DEBUGMESSAGE(symbol) case symbol: break;
592#else
593#define OPMEV_DEBUGMESSAGE(symbol) case symbol: printf("Original APM Event: " #symbol "\n"); break
594#endif
595		switch (apm_event) {
596			OPMEV_DEBUGMESSAGE(PMEV_NOEVENT);
597			OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
598			OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
599			OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
600			OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
601			OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
602			OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
603			OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
604			OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
605			OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
606			OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
607			default:
608				printf("Unknown Original APM Event 0x%x\n", apm_event);
609				break;
610		}
611#endif
612		for (i = 0; i < equiv_event_num; i++) {
613			if (equiv_events[i].aee_event == apm_event) {
614				u_int tmp = PMEV_DEFAULT;
615				if (resumed_event) {
616					tmp = equiv_events[i].aee_resume;
617				}
618				else {
619					tmp = equiv_events[i].aee_equiv;
620				}
621				if (tmp != PMEV_DEFAULT) {
622					apm_event = tmp;
623					break;
624				}
625			}
626		}
627#if 1
628#if 1
629#define PMEV_DEBUGMESSAGE(symbol) case symbol: break;
630#else
631#define PMEV_DEBUGMESSAGE(symbol) case symbol: printf("APM Event: " #symbol "\n"); break
632#endif
633		switch (apm_event) {
634			PMEV_DEBUGMESSAGE(PMEV_NOEVENT);
635			PMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
636			PMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
637			PMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
638			PMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
639			PMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
640			PMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
641			PMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
642			PMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
643			PMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
644			PMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
645			default:
646				printf("Unknown APM Event 0x%x\n", apm_event);
647				break;
648		}
649#endif
650		switch (apm_event) {
651		case PMEV_NOEVENT:
652		case PMEV_STANDBYREQ:
653		case PMEV_POWERSTATECHANGE:
654		case PMEV_CRITSUSPEND:
655		case PMEV_USERSTANDBYREQ:
656		case PMEV_USERSUSPENDREQ:
657			break;
658		case PMEV_BATTERYLOW:
659			apm_battery_low();
660			break;
661		case PMEV_SUSPENDREQ:
662			apm_suspend_resume();
663			break;
664		case PMEV_NORMRESUME:
665		case PMEV_CRITRESUME:
666		case PMEV_UPDATETIME:
667		case PMEV_STANDBYRESUME:
668			inittodr(0);	/* adjust time to RTC */
669			break;
670		}
671	}
672	resumed_event = 0;
673}
674
675
676/*
677 * Attach APM:
678 *
679 * Initialize APM driver (APM BIOS itself has been initialized in locore.s)
680 */
681
682int
683apmattach(struct isa_device *dvp)
684{
685
686	/* setup APM parameters */
687	minorversion = ((apm_version & 0x00f0) >>  4) * 10 + ((apm_version & 0x000f) >> 0);
688	majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8);
689	intversion = INTVERSION(majorversion, minorversion);
690	cs32_base = (apm_cs32_base << 4) + KERNBASE;
691	cs16_base = (apm_cs16_base << 4) + KERNBASE;
692	ds_base = (apm_ds_base << 4) + KERNBASE;
693	cs_limit = apm_cs_limit;
694	ds_limit = apm_ds_limit;
695	cs_entry = apm_cs_entry;
696	idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0);
697	disabled = ((apm_flags & APM_DISABLED) != 0);
698	disengaged = ((apm_flags & APM_DISENGAGED) != 0);
699
700	/* print bootstrap messages */
701	printf(" found APM BIOS version %d.%d\n", dvp->id_unit, majorversion, minorversion);
702	printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", dvp->id_unit, cs32_base, cs16_base, ds_base);
703	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));
704
705	/*
706	 * APM 1.0 does not have:
707	 *
708	 * 	1. segment limit parameters
709	 *
710	 *	2. engage/disengage operations
711	 */
712	if (intversion >= INTVERSION(1, 1)) {
713		printf("apm%d: Engaged control %s\n", dvp->id_unit, is_enabled(!disengaged));
714	}
715	else {
716		cs_limit = 0xffff;
717		ds_limit = 0xffff;
718	}
719
720	/* setup GDT */
721	setup_apm_gdt(cs32_base, cs16_base, ds_base, cs_limit, ds_limit);
722
723	/* setup entry point 48bit pointer */
724	apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
725	apm_addr.offset  = cs_entry;
726
727	/* enable power management */
728	if (disabled) {
729		if (apm_enable_disable_pm(1)) {
730			printf("Warning: APM enable function failed! [%s]\n", apm_error());
731		}
732	}
733
734	/* engage power managment (APM 1.1 or later) */
735	if (intversion >= INTVERSION(1, 1) && disengaged) {
736		if (apm_engage_disengage_pm(1)) {
737			printf("Warning: APM engage function failed [%s]\n", apm_error());
738		}
739	}
740
741	apm_suspend_hook_init(apm_default_suspend, "default suspend", APM_MAX_ORDER);
742	apm_resume_hook_init (apm_default_resume , "default resume" , APM_MIN_ORDER);
743	apm_initialized = 1;
744
745	return 0;
746}
747
748int
749apmopen(dev_t dev, int flag, int fmt, struct proc *p)
750{
751	if (!apm_initialized) {
752		return ENXIO;
753	}
754	switch (minor(dev)) {
755	case 0:	/* apm0 */
756		break;
757	defaults:
758		return (ENXIO);
759	}
760	return 0;
761}
762
763int
764apmclose(dev_t dev, int flag, int fmt, struct proc *p)
765{
766	return 0;
767}
768
769int
770apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
771{
772	int error = 0;
773	int pl;
774
775#if 0
776	printf("APM ioctl: minor = %d, cmd = 0x%x\n", minor(dev), cmd);
777#endif
778
779	pl = splhigh();
780	if (minor(dev) != 0) {
781		return ENXIO;
782	}
783	if (!apm_initialized) {
784		return ENXIO;
785	}
786	switch (cmd) {
787	case APMIO_SUSPEND:
788		apm_suspend_resume();
789		break;
790	case APMIO_GETINFO:
791		if (apm_get_info((apm_info_t)addr)) {
792			error = ENXIO;
793		}
794		break;
795	case APMIO_DEFEQV:
796		if (apm_def_eqv((apm_eqv_event_t)addr)) {
797			error = ENOSPC;
798		}
799		break;
800	case APMIO_FLUSHEQV:
801		apm_flush_eqv();
802		break;
803	case APMIO_ENABLE:
804		apm_event_enable();
805		break;
806	case APMIO_DISABLE:
807		apm_event_disable();
808		break;
809	case APMIO_HALTCPU:
810		apm_halt_cpu();
811		break;
812	case APMIO_NOTHALTCPU:
813		apm_not_halt_cpu();
814		break;
815	default:
816		error = EINVAL;
817		break;
818	}
819	splx(pl);
820	return error;
821}
822
823#endif /* NAPM > 0 */
824