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