Merge branch 'vendor/BZIP'
[dragonfly.git] / sys / platform / pc64 / apm / apm.c
1 /*
2  * APM (Advanced Power Management) BIOS Device Driver
3  *
4  * Copyright (c) 1994 UKAI, Fumitoshi.
5  * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>
6  * Copyright (c) 1996 Nate Williams <nate@FreeBSD.org>
7  * Copyright (c) 1997 Poul-Henning Kamp <phk@FreeBSD.org>
8  *
9  * This software may be used, modified, copied, and distributed, in
10  * both source and binary form provided that the above copyright and
11  * these terms are retained. Under no circumstances is the author
12  * responsible for the proper functioning of this software, nor does
13  * the author assume any responsibility for damages incurred with its
14  * use.
15  *
16  * Sep, 1994    Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
17  *
18  * $FreeBSD: src/sys/i386/apm/apm.c,v 1.114.2.5 2002/11/02 04:41:50 iwasaki Exp $
19  * $DragonFly: src/sys/platform/pc32/apm/apm.c,v 1.21 2006/12/23 00:27:03 swildner Exp $
20  */
21
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/eventhandler.h>
25 #include <sys/conf.h>
26 #include <sys/device.h>
27 #include <sys/kernel.h>
28 #include <sys/time.h>
29 #include <sys/reboot.h>
30 #include <sys/bus.h>
31 #include <sys/event.h>
32 #include <sys/fcntl.h>
33 #include <sys/uio.h>
34 #include <sys/signalvar.h>
35 #include <sys/sysctl.h>
36 #include <machine/apm_bios.h>
37 #include <machine/segments.h>
38 #include <machine/clock.h>
39 #include <vm/vm.h>
40 #include <vm/vm_param.h>
41 #include <vm/pmap.h>
42 #include <sys/syslog.h>
43 #include <sys/thread2.h>
44
45 #include <machine/pc/bios.h>
46 #include <machine/vm86.h>
47
48 #include <machine_base/apm/apm.h>
49
50 /* Used by the apm_saver screen saver module */
51 int apm_display (int newstate);
52 struct apm_softc apm_softc;
53
54 static void apm_resume (void);
55 static int apm_bioscall(void);
56 static int apm_check_function_supported (u_int version, u_int func);
57
58 static u_long   apm_version;
59
60 int     apm_evindex;
61
62 #define SCFLAG_ONORMAL  0x0000001
63 #define SCFLAG_OCTL     0x0000002
64 #define SCFLAG_OPEN     (SCFLAG_ONORMAL|SCFLAG_OCTL)
65
66 #define APMDEV(dev)     (minor(dev)&0x0f)
67 #define APMDEV_NORMAL   0
68 #define APMDEV_CTL      8
69
70 static struct apmhook   *hook[NAPM_HOOK];               /* XXX */
71
72 #define is_enabled(foo) ((foo) ? "enabled" : "disabled")
73
74 /* Map version number to integer (keeps ordering of version numbers) */
75 #define INTVERSION(major, minor)        ((major)*100 + (minor))
76
77 static struct callout apm_timeout_ch;
78
79 static timeout_t apm_timeout;
80 static d_open_t apmopen;
81 static d_close_t apmclose;
82 static d_write_t apmwrite;
83 static d_ioctl_t apmioctl;
84 static d_kqfilter_t apmkqfilter;
85
86 static void apmfilter_detach(struct knote *);
87 static int apmfilter_read(struct knote *, long);
88 static int apmfilter_write(struct knote *, long);
89
90 #define CDEV_MAJOR 39
91 static struct dev_ops apm_ops = {
92         { "apm", CDEV_MAJOR, 0 },
93         .d_open =       apmopen,
94         .d_close =      apmclose,
95         .d_write =      apmwrite,
96         .d_ioctl =      apmioctl,
97         .d_kqfilter =   apmkqfilter
98 };
99
100 static int apm_suspend_delay = 1;
101 static int apm_standby_delay = 1;
102 static int apm_debug = 0;
103
104 #define APM_DPRINT(args...) do  {                                       \
105         if (apm_debug) {                                                \
106                 kprintf(args);                                          \
107         }                                                               \
108 } while (0)
109
110 SYSCTL_INT(_machdep, OID_AUTO, apm_suspend_delay, CTLFLAG_RW, &apm_suspend_delay, 1, "");
111 SYSCTL_INT(_machdep, OID_AUTO, apm_standby_delay, CTLFLAG_RW, &apm_standby_delay, 1, "");
112 SYSCTL_INT(_debug, OID_AUTO, apm_debug, CTLFLAG_RW, &apm_debug, 0, "");
113
114 /*
115  * return  0 if the function successfull,
116  * return  1 if the function unsuccessfull,
117  * return -1 if the function unsupported.
118  */
119 static int
120 apm_bioscall(void)
121 {
122         struct apm_softc *sc = &apm_softc;
123         int error = 0;
124         u_int apm_func = sc->bios.r.eax & 0xff;
125
126         if (!apm_check_function_supported(sc->intversion, apm_func)) {
127                 APM_DPRINT("apm_bioscall: function 0x%x is not supported in v%d.%d\n",
128                     apm_func, sc->majorversion, sc->minorversion);
129                 return (-1);
130         }
131
132         sc->bios_busy = 1;
133         if (sc->connectmode == APM_PROT32CONNECT) {
134                 set_bios_selectors(&sc->bios.seg,
135                                    BIOSCODE_FLAG | BIOSDATA_FLAG);
136                 error = bios32(&sc->bios.r,
137                                sc->bios.entry, GSEL(GBIOSCODE32_SEL, SEL_KPL));
138         } else {
139                 error = bios16(&sc->bios, NULL);
140         }
141         sc->bios_busy = 0;
142         return (error);
143 }
144
145 /* check whether APM function is supported (1)  or not (0). */
146 static int
147 apm_check_function_supported(u_int version, u_int func)
148 {
149         /* except driver version */
150         if (func == APM_DRVVERSION) {
151                 return (1);
152         }
153
154         switch (version) {
155         case INTVERSION(1, 0):
156                 if (func > APM_GETPMEVENT) {
157                         return (0); /* not supported */
158                 }
159                 break;
160         case INTVERSION(1, 1):
161                 if (func > APM_ENGAGEDISENGAGEPM &&
162                     func < APM_OEMFUNC) {
163                         return (0); /* not supported */
164                 }
165                 break;
166         case INTVERSION(1, 2):
167                 break;
168         }
169
170         return (1); /* supported */
171 }
172
173 /* enable/disable power management */
174 static int
175 apm_enable_disable_pm(int enable)
176 {
177         struct apm_softc *sc = &apm_softc;
178
179         sc->bios.r.eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM;
180
181         if (sc->intversion >= INTVERSION(1, 1))
182                 sc->bios.r.ebx  = PMDV_ALLDEV;
183         else
184                 sc->bios.r.ebx  = 0xffff;       /* APM version 1.0 only */
185         sc->bios.r.ecx  = enable;
186         sc->bios.r.edx = 0;
187         return (apm_bioscall());
188 }
189
190 /* register driver version (APM 1.1 or later) */
191 static int
192 apm_driver_version(int version)
193 {
194         struct apm_softc *sc = &apm_softc;
195
196         sc->bios.r.eax = (APM_BIOS << 8) | APM_DRVVERSION;
197         sc->bios.r.ebx  = 0x0;
198         sc->bios.r.ecx  = version;
199         sc->bios.r.edx = 0;
200
201         if (apm_bioscall() == 0 && sc->bios.r.eax == version)
202                 return (0);
203
204         /* Some old BIOSes don't return the connection version in %ax. */
205         if (sc->bios.r.eax == ((APM_BIOS << 8) | APM_DRVVERSION))
206                 return (0);
207
208         return (1);
209 }
210
211 /* engage/disengage power management (APM 1.1 or later) */
212 static int
213 apm_engage_disengage_pm(int engage)
214 {
215         struct apm_softc *sc = &apm_softc;
216
217         sc->bios.r.eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM;
218         sc->bios.r.ebx = PMDV_ALLDEV;
219         sc->bios.r.ecx = engage;
220         sc->bios.r.edx = 0;
221         return (apm_bioscall());
222 }
223
224 /* get PM event */
225 static u_int
226 apm_getevent(void)
227 {
228         struct apm_softc *sc = &apm_softc;
229
230         sc->bios.r.eax = (APM_BIOS << 8) | APM_GETPMEVENT;
231
232         sc->bios.r.ebx = 0;
233         sc->bios.r.ecx = 0;
234         sc->bios.r.edx = 0;
235         if (apm_bioscall())
236                 return (PMEV_NOEVENT);
237         return (sc->bios.r.ebx & 0xffff);
238 }
239
240 /* suspend entire system */
241 static int
242 apm_suspend_system(int state)
243 {
244         struct apm_softc *sc = &apm_softc;
245
246         sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE;
247         sc->bios.r.ebx = PMDV_ALLDEV;
248         sc->bios.r.ecx = state;
249         sc->bios.r.edx = 0;
250
251         if (apm_bioscall()) {
252                 kprintf("Entire system suspend failure: errcode = %d\n",
253                         0xff & (sc->bios.r.eax >> 8));
254                 return 1;
255         }
256         return 0;
257 }
258
259 /* Display control */
260 /*
261  * Experimental implementation: My laptop machine can't handle this function
262  * If your laptop can control the display via APM, please inform me.
263  *                            HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>
264  */
265 int
266 apm_display(int newstate)
267 {
268         struct apm_softc *sc = &apm_softc;
269
270         sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE;
271         sc->bios.r.ebx = PMDV_DISP0;
272         sc->bios.r.ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND;
273         sc->bios.r.edx = 0;
274         if (apm_bioscall() == 0) {
275                 return 0;
276         }
277
278         /* If failed, then try to blank all display devices instead. */
279         sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE;
280         sc->bios.r.ebx = PMDV_DISPALL;  /* all display devices */
281         sc->bios.r.ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND;
282         sc->bios.r.edx = 0;
283         if (apm_bioscall() == 0) {
284                 return 0;
285         }
286         kprintf("Display off failure: errcode = %d\n",
287                0xff & (sc->bios.r.eax >> 8));
288         return 1;
289 }
290
291 /*
292  * Turn off the entire system.
293  */
294 static void
295 apm_power_off(void *junk, int howto)
296 {
297         struct apm_softc *sc = &apm_softc;
298
299         /* Not halting powering off, or not active */
300         if (!(howto & RB_POWEROFF) || !apm_softc.active)
301                 return;
302         sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE;
303         sc->bios.r.ebx = PMDV_ALLDEV;
304         sc->bios.r.ecx = PMST_OFF;
305         sc->bios.r.edx = 0;
306         apm_bioscall();
307 }
308
309 /* APM Battery low handler */
310 static void
311 apm_battery_low(void)
312 {
313         kprintf("\007\007 * * * BATTERY IS LOW * * * \007\007");
314 }
315
316 /* APM hook manager */
317 static struct apmhook *
318 apm_add_hook(struct apmhook **list, struct apmhook *ah)
319 {
320         struct apmhook *p, *prev;
321
322         APM_DPRINT("Add hook \"%s\"\n", ah->ah_name);
323
324         crit_enter();
325         if (ah == NULL)
326                 panic("illegal apm_hook!");
327         prev = NULL;
328         for (p = *list; p != NULL; prev = p, p = p->ah_next)
329                 if (p->ah_order > ah->ah_order)
330                         break;
331
332         if (prev == NULL) {
333                 ah->ah_next = *list;
334                 *list = ah;
335         } else {
336                 ah->ah_next = prev->ah_next;
337                 prev->ah_next = ah;
338         }
339         crit_exit();
340         return ah;
341 }
342
343 static void
344 apm_del_hook(struct apmhook **list, struct apmhook *ah)
345 {
346         struct apmhook *p, *prev;
347
348         crit_enter();
349         prev = NULL;
350         for (p = *list; p != NULL; prev = p, p = p->ah_next)
351                 if (p == ah)
352                         goto deleteit;
353         panic("Tried to delete unregistered apm_hook.");
354         goto nosuchnode;
355 deleteit:
356         if (prev != NULL)
357                 prev->ah_next = p->ah_next;
358         else
359                 *list = p->ah_next;
360 nosuchnode:
361         crit_exit();
362 }
363
364
365 /* APM driver calls some functions automatically */
366 static void
367 apm_execute_hook(struct apmhook *list)
368 {
369         struct apmhook *p;
370
371         for (p = list; p != NULL; p = p->ah_next) {
372                 APM_DPRINT("Execute APM hook \"%s.\"\n", p->ah_name);
373                 if ((*(p->ah_fun))(p->ah_arg))
374                         kprintf("Warning: APM hook \"%s\" failed", p->ah_name);
375         }
376 }
377
378
379 /* establish an apm hook */
380 struct apmhook *
381 apm_hook_establish(int apmh, struct apmhook *ah)
382 {
383         if (apmh < 0 || apmh >= NAPM_HOOK)
384                 return NULL;
385
386         return apm_add_hook(&hook[apmh], ah);
387 }
388
389 /* disestablish an apm hook */
390 void
391 apm_hook_disestablish(int apmh, struct apmhook *ah)
392 {
393         if (apmh < 0 || apmh >= NAPM_HOOK)
394                 return;
395
396         apm_del_hook(&hook[apmh], ah);
397 }
398
399
400 static struct timeval suspend_time;
401 static struct timeval diff_time;
402
403 static int
404 apm_default_resume(void *arg)
405 {
406         u_int second, minute, hour;
407         struct timeval resume_time, tmp_time;
408
409         /* modified for adjkerntz */
410         crit_enter();
411         timer_restore();                /* restore the all timers */
412         inittodr(0);                    /* adjust time to RTC */
413         microtime(&resume_time);
414         getmicrotime(&tmp_time);
415         timevaladd(&tmp_time, &diff_time);
416
417 #ifdef FIXME
418         /* XXX THIS DOESN'T WORK!!! */
419         time = tmp_time;
420 #endif
421
422 #ifdef APM_FIXUP_CALLTODO
423         /* Calculate the delta time suspended */
424         timevalsub(&resume_time, &suspend_time);
425         /* Fixup the calltodo list with the delta time. */
426         adjust_timeout_calltodo(&resume_time);
427 #endif /* APM_FIXUP_CALLTODOK */
428         crit_exit();
429 #ifndef APM_FIXUP_CALLTODO
430         second = resume_time.tv_sec - suspend_time.tv_sec;
431 #else /* APM_FIXUP_CALLTODO */
432         /*
433          * We've already calculated resume_time to be the delta between
434          * the suspend and the resume.
435          */
436         second = resume_time.tv_sec;
437 #endif /* APM_FIXUP_CALLTODO */
438         hour = second / 3600;
439         second %= 3600;
440         minute = second / 60;
441         second %= 60;
442         log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
443                 hour, minute, second);
444         return 0;
445 }
446
447 static int
448 apm_default_suspend(void *arg)
449 {
450         crit_enter();
451         microtime(&diff_time);
452         inittodr(0);
453         microtime(&suspend_time);
454         timevalsub(&diff_time, &suspend_time);
455         crit_exit();
456         return 0;
457 }
458
459 static int apm_record_event (struct apm_softc *, u_int);
460 static void apm_processevent(void);
461
462 static u_int apm_op_inprog = 0;
463
464 static void
465 apm_do_suspend(void)
466 {
467         struct apm_softc *sc = &apm_softc;
468         int error;
469
470         if (!sc)
471                 return;
472
473         apm_op_inprog = 0;
474         sc->suspends = sc->suspend_countdown = 0;
475
476         if (sc->initialized) {
477                 error = DEVICE_SUSPEND(root_bus);
478                 if (error) {
479                         DEVICE_RESUME(root_bus);
480                 } else {
481                         apm_execute_hook(hook[APM_HOOK_SUSPEND]);
482                         if (apm_suspend_system(PMST_SUSPEND) == 0) {
483                                 apm_processevent();
484                         } else {
485                                 /* Failure, 'resume' the system again */
486                                 apm_execute_hook(hook[APM_HOOK_RESUME]);
487                                 DEVICE_RESUME(root_bus);
488                         }
489                 }
490         }
491 }
492
493 static void
494 apm_do_standby(void)
495 {
496         struct apm_softc *sc = &apm_softc;
497
498         if (!sc)
499                 return;
500
501         apm_op_inprog = 0;
502         sc->standbys = sc->standby_countdown = 0;
503
504         if (sc->initialized) {
505                 /*
506                  * As far as standby, we don't need to execute
507                  * all of suspend hooks.
508                  */
509                 apm_default_suspend(&apm_softc);
510                 if (apm_suspend_system(PMST_STANDBY) == 0)
511                         apm_processevent();
512         }
513 }
514
515 static void
516 apm_lastreq_notify(void)
517 {
518         struct apm_softc *sc = &apm_softc;
519
520         sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE;
521         sc->bios.r.ebx = PMDV_ALLDEV;
522         sc->bios.r.ecx = PMST_LASTREQNOTIFY;
523         sc->bios.r.edx = 0;
524         apm_bioscall();
525 }
526
527 static int
528 apm_lastreq_rejected(void)
529 {
530         struct apm_softc *sc = &apm_softc;
531
532         if (apm_op_inprog == 0) {
533                 return 1;       /* no operation in progress */
534         }
535
536         sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE;
537         sc->bios.r.ebx = PMDV_ALLDEV;
538         sc->bios.r.ecx = PMST_LASTREQREJECT;
539         sc->bios.r.edx = 0;
540
541         if (apm_bioscall()) {
542                 APM_DPRINT("apm_lastreq_rejected: failed\n");
543                 return 1;
544         }
545         apm_op_inprog = 0;
546         return 0;
547 }
548
549 /*
550  * Public interface to the suspend/resume:
551  *
552  * Execute suspend and resume hook before and after sleep, respectively.
553  *
554  */
555
556 void
557 apm_suspend(int state)
558 {
559         struct apm_softc *sc = &apm_softc;
560
561         if (!sc->initialized)
562                 return;
563
564         switch (state) {
565         case PMST_SUSPEND:
566                 if (sc->suspends)
567                         return;
568                 sc->suspends++;
569                 sc->suspend_countdown = apm_suspend_delay;
570                 break;
571         case PMST_STANDBY:
572                 if (sc->standbys)
573                         return;
574                 sc->standbys++;
575                 sc->standby_countdown = apm_standby_delay;
576                 break;
577         default:
578                 kprintf("apm_suspend: Unknown Suspend state 0x%x\n", state);
579                 return;
580         }
581
582         apm_op_inprog++;
583         apm_lastreq_notify();
584 }
585
586 void
587 apm_resume(void)
588 {
589         struct apm_softc *sc = &apm_softc;
590
591         if (!sc)
592                 return;
593
594         if (sc->initialized) {
595                 apm_execute_hook(hook[APM_HOOK_RESUME]);
596                 DEVICE_RESUME(root_bus);
597         }
598 }
599
600
601 /* get power status per battery */
602 static int
603 apm_get_pwstatus(apm_pwstatus_t app)
604 {
605         struct apm_softc *sc = &apm_softc;
606
607         if (app->ap_device != PMDV_ALLDEV &&
608             (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL))
609                 return 1;
610
611         sc->bios.r.eax = (APM_BIOS << 8) | APM_GETPWSTATUS;
612         sc->bios.r.ebx = app->ap_device;
613         sc->bios.r.ecx = 0;
614         sc->bios.r.edx = 0xffff;        /* default to unknown battery time */
615
616         if (apm_bioscall())
617                 return 1;
618
619         app->ap_acline    = (sc->bios.r.ebx >> 8) & 0xff;
620         app->ap_batt_stat = sc->bios.r.ebx & 0xff;
621         app->ap_batt_flag = (sc->bios.r.ecx >> 8) & 0xff;
622         app->ap_batt_life = sc->bios.r.ecx & 0xff;
623         sc->bios.r.edx &= 0xffff;
624         if (sc->bios.r.edx == 0xffff)   /* Time is unknown */
625                 app->ap_batt_time = -1;
626         else if (sc->bios.r.edx & 0x8000)       /* Time is in minutes */
627                 app->ap_batt_time = (sc->bios.r.edx & 0x7fff) * 60;
628         else                            /* Time is in seconds */
629                 app->ap_batt_time = sc->bios.r.edx;
630
631         return 0;
632 }
633
634
635 /* get APM information */
636 static int
637 apm_get_info(apm_info_t aip)
638 {
639         struct apm_softc *sc = &apm_softc;
640         struct apm_pwstatus aps;
641
642         bzero(&aps, sizeof(aps));
643         aps.ap_device = PMDV_ALLDEV;
644         if (apm_get_pwstatus(&aps))
645                 return 1;
646
647         aip->ai_infoversion = 1;
648         aip->ai_acline      = aps.ap_acline;
649         aip->ai_batt_stat   = aps.ap_batt_stat;
650         aip->ai_batt_life   = aps.ap_batt_life;
651         aip->ai_batt_time   = aps.ap_batt_time;
652         aip->ai_major       = (u_int)sc->majorversion;
653         aip->ai_minor       = (u_int)sc->minorversion;
654         aip->ai_status      = (u_int)sc->active;
655
656         sc->bios.r.eax = (APM_BIOS << 8) | APM_GETCAPABILITIES;
657         sc->bios.r.ebx = 0;
658         sc->bios.r.ecx = 0;
659         sc->bios.r.edx = 0;
660         if (apm_bioscall()) {
661                 aip->ai_batteries = -1; /* Unknown */
662                 aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */
663         } else {
664                 aip->ai_batteries = sc->bios.r.ebx & 0xff;
665                 aip->ai_capabilities = sc->bios.r.ecx & 0xf;
666         }
667
668         bzero(aip->ai_spare, sizeof aip->ai_spare);
669
670         return 0;
671 }
672
673
674 /* inform APM BIOS that CPU is idle */
675 void
676 apm_cpu_idle(void)
677 {
678         struct apm_softc *sc = &apm_softc;
679
680         if (sc->active) {
681
682                 sc->bios.r.eax = (APM_BIOS <<8) | APM_CPUIDLE;
683                 sc->bios.r.edx = sc->bios.r.ecx = sc->bios.r.ebx = 0;
684                 apm_bioscall();
685         }
686         /*
687          * Some APM implementation halts CPU in BIOS, whenever
688          * "CPU-idle" function are invoked, but swtch() of
689          * FreeBSD halts CPU, therefore, CPU is halted twice
690          * in the sched loop. It makes the interrupt latency
691          * terribly long and be able to cause a serious problem
692          * in interrupt processing. We prevent it by removing
693          * "hlt" operation from swtch() and managed it under
694          * APM driver.
695          */
696         if (!sc->active || sc->always_halt_cpu)
697                 __asm("hlt");   /* wait for interrupt */
698 }
699
700 /* inform APM BIOS that CPU is busy */
701 void
702 apm_cpu_busy(void)
703 {
704         struct apm_softc *sc = &apm_softc;
705
706         /*
707          * The APM specification says this is only necessary if your BIOS
708          * slows down the processor in the idle task, otherwise it's not
709          * necessary.
710          */
711         if (sc->slow_idle_cpu && sc->active) {
712
713                 sc->bios.r.eax = (APM_BIOS <<8) | APM_CPUBUSY;
714                 sc->bios.r.edx = sc->bios.r.ecx = sc->bios.r.ebx = 0;
715                 apm_bioscall();
716         }
717 }
718
719
720 /*
721  * APM timeout routine:
722  *
723  * This routine is automatically called by timer once per second.
724  */
725
726 static void
727 apm_timeout(void *dummy)
728 {
729         struct apm_softc *sc = &apm_softc;
730
731         if (apm_op_inprog)
732                 apm_lastreq_notify();
733
734         if (sc->standbys && sc->standby_countdown-- <= 0)
735                 apm_do_standby();
736
737         if (sc->suspends && sc->suspend_countdown-- <= 0)
738                 apm_do_suspend();
739
740         if (!sc->bios_busy)
741                 apm_processevent();
742
743         if (sc->active == 1) {
744                 /* Run slightly more oftan than 1 Hz */
745                 callout_reset(&apm_timeout_ch, hz - 1, apm_timeout, NULL);
746         }
747 }
748
749 /* enable APM BIOS */
750 static void
751 apm_event_enable(void)
752 {
753         struct apm_softc *sc = &apm_softc;
754
755         APM_DPRINT("called apm_event_enable()\n");
756         if (sc->initialized) {
757                 sc->active = 1;
758                 callout_init(&apm_timeout_ch);
759                 apm_timeout(sc);
760         }
761 }
762
763 /* disable APM BIOS */
764 static void
765 apm_event_disable(void)
766 {
767         struct apm_softc *sc = &apm_softc;
768
769         APM_DPRINT("called apm_event_disable()\n");
770         if (sc->initialized) {
771                 callout_stop(&apm_timeout_ch);
772                 sc->active = 0;
773         }
774 }
775
776 /* halt CPU in scheduling loop */
777 static void
778 apm_halt_cpu(void)
779 {
780         struct apm_softc *sc = &apm_softc;
781
782         if (sc->initialized)
783                 sc->always_halt_cpu = 1;
784 }
785
786 /* don't halt CPU in scheduling loop */
787 static void
788 apm_not_halt_cpu(void)
789 {
790         struct apm_softc *sc = &apm_softc;
791
792         if (sc->initialized)
793                 sc->always_halt_cpu = 0;
794 }
795
796 /* device driver definitions */
797
798 /*
799  * probe for APM BIOS
800  */
801 static int
802 apm_probe(device_t dev)
803 {
804 #define APM_KERNBASE    KERNBASE
805         struct vm86frame        vmf;
806         struct apm_softc        *sc = &apm_softc;
807         int                     disabled, flags;
808
809         if (resource_int_value("apm", 0, "disabled", &disabled) == 0
810             && disabled != 0)
811                 return ENXIO;
812
813         device_set_desc(dev, "APM BIOS");
814
815         if ( device_get_unit(dev) > 0 ) {
816                 kprintf("apm: Only one APM driver supported.\n");
817                 return ENXIO;
818         }
819
820         if (resource_int_value("apm", 0, "flags", &flags) != 0)
821                 flags = 0;
822
823         bzero(&vmf, sizeof(struct vm86frame));          /* safety */
824         bzero(&apm_softc, sizeof(apm_softc));
825         vmf.vmf_ah = APM_BIOS;
826         vmf.vmf_al = APM_INSTCHECK;
827         vmf.vmf_bx = 0;
828         if (vm86_intcall(APM_INT, &vmf))
829                 return ENXIO;                   /* APM not found */
830         if (vmf.vmf_bx != 0x504d) {
831                 kprintf("apm: incorrect signature (0x%x)\n", vmf.vmf_bx);
832                 return ENXIO;
833         }
834         if ((vmf.vmf_cx & (APM_32BIT_SUPPORT | APM_16BIT_SUPPORT)) == 0) {
835                 kprintf("apm: protected mode connections are not supported\n");
836                 return ENXIO;
837         }
838
839         apm_version = vmf.vmf_ax;
840         sc->slow_idle_cpu = ((vmf.vmf_cx & APM_CPUIDLE_SLOW) != 0);
841         sc->disabled = ((vmf.vmf_cx & APM_DISABLED) != 0);
842         sc->disengaged = ((vmf.vmf_cx & APM_DISENGAGED) != 0);
843
844         vmf.vmf_ah = APM_BIOS;
845         vmf.vmf_al = APM_DISCONNECT;
846         vmf.vmf_bx = 0;
847         vm86_intcall(APM_INT, &vmf);            /* disconnect, just in case */
848
849         if ((vmf.vmf_cx & APM_32BIT_SUPPORT) != 0) {
850                 vmf.vmf_ah = APM_BIOS;
851                 vmf.vmf_al = APM_PROT32CONNECT;
852                 vmf.vmf_bx = 0;
853                 if (vm86_intcall(APM_INT, &vmf)) {
854                         kprintf("apm: 32-bit connection error.\n");
855                         return (ENXIO);
856                 }
857                 sc->bios.seg.code32.base = (vmf.vmf_ax << 4) + APM_KERNBASE;
858                 sc->bios.seg.code32.limit = 0xffff;
859                 sc->bios.seg.code16.base = (vmf.vmf_cx << 4) + APM_KERNBASE;
860                 sc->bios.seg.code16.limit = 0xffff;
861                 sc->bios.seg.data.base = (vmf.vmf_dx << 4) + APM_KERNBASE;
862                 sc->bios.seg.data.limit = 0xffff;
863                 sc->bios.entry = vmf.vmf_ebx;
864                 sc->connectmode = APM_PROT32CONNECT;
865         } else {
866                 /* use 16-bit connection */
867                 vmf.vmf_ah = APM_BIOS;
868                 vmf.vmf_al = APM_PROT16CONNECT;
869                 vmf.vmf_bx = 0;
870                 if (vm86_intcall(APM_INT, &vmf)) {
871                         kprintf("apm: 16-bit connection error.\n");
872                         return (ENXIO);
873                 }
874                 sc->bios.seg.code16.base = (vmf.vmf_ax << 4) + APM_KERNBASE;
875                 sc->bios.seg.code16.limit = 0xffff;
876                 sc->bios.seg.data.base = (vmf.vmf_cx << 4) + APM_KERNBASE;
877                 sc->bios.seg.data.limit = 0xffff;
878                 sc->bios.entry = vmf.vmf_bx;
879                 sc->connectmode = APM_PROT16CONNECT;
880         }
881         return(0);
882 }
883
884
885 /*
886  * return 0 if the user will notice and handle the event,
887  * return 1 if the kernel driver should do so.
888  */
889 static int
890 apm_record_event(struct apm_softc *sc, u_int event_type)
891 {
892         struct apm_event_info *evp;
893
894         if ((sc->sc_flags & SCFLAG_OPEN) == 0)
895                 return 1;               /* no user waiting */
896         if (sc->event_count == APM_NEVENTS)
897                 return 1;                       /* overflow */
898         if (sc->event_filter[event_type] == 0)
899                 return 1;               /* not registered */
900         evp = &sc->event_list[sc->event_ptr];
901         sc->event_count++;
902         sc->event_ptr++;
903         sc->event_ptr %= APM_NEVENTS;
904         evp->type = event_type;
905         evp->index = ++apm_evindex;
906         KNOTE(&sc->sc_rkq.ki_note, 0);
907         return (sc->sc_flags & SCFLAG_OCTL) ? 0 : 1; /* user may handle */
908 }
909
910 /* Process APM event */
911 static void
912 apm_processevent(void)
913 {
914         int apm_event;
915         struct apm_softc *sc = &apm_softc;
916
917 #define OPMEV_DEBUGMESSAGE(symbol) case symbol:                         \
918         APM_DPRINT("Received APM Event: " #symbol "\n");
919
920         do {
921                 apm_event = apm_getevent();
922                 switch (apm_event) {
923                     OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
924                         if (apm_op_inprog == 0) {
925                             apm_op_inprog++;
926                             if (apm_record_event(sc, apm_event)) {
927                                 apm_suspend(PMST_STANDBY);
928                             }
929                         }
930                         break;
931                     OPMEV_DEBUGMESSAGE(PMEV_USERSTANDBYREQ);
932                         if (apm_op_inprog == 0) {
933                             apm_op_inprog++;
934                             if (apm_record_event(sc, apm_event)) {
935                                 apm_suspend(PMST_STANDBY);
936                             }
937                         }
938                         break;
939                     OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
940                         apm_lastreq_notify();
941                         if (apm_op_inprog == 0) {
942                             apm_op_inprog++;
943                             if (apm_record_event(sc, apm_event)) {
944                                 apm_do_suspend();
945                             }
946                         }
947                         return; /* XXX skip the rest */
948                     OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
949                         apm_lastreq_notify();
950                         if (apm_op_inprog == 0) {
951                             apm_op_inprog++;
952                             if (apm_record_event(sc, apm_event)) {
953                                 apm_do_suspend();
954                             }
955                         }
956                         return; /* XXX skip the rest */
957                     OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
958                         apm_do_suspend();
959                         break;
960                     OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
961                         apm_record_event(sc, apm_event);
962                         apm_resume();
963                         break;
964                     OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
965                         apm_record_event(sc, apm_event);
966                         apm_resume();
967                         break;
968                     OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
969                         apm_record_event(sc, apm_event);
970                         apm_resume();
971                         break;
972                     OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
973                         if (apm_record_event(sc, apm_event)) {
974                             apm_battery_low();
975                             apm_suspend(PMST_SUSPEND);
976                         }
977                         break;
978                     OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
979                         apm_record_event(sc, apm_event);
980                         break;
981                     OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
982                         apm_record_event(sc, apm_event);
983                         inittodr(0);    /* adjust time to RTC */
984                         break;
985                     OPMEV_DEBUGMESSAGE(PMEV_CAPABILITIESCHANGE);
986                         apm_record_event(sc, apm_event);
987                         break;
988                     case PMEV_NOEVENT:
989                         break;
990                     default:
991                         kprintf("Unknown Original APM Event 0x%x\n", apm_event);
992                             break;
993                 }
994         } while (apm_event != PMEV_NOEVENT);
995 }
996
997 /*
998  * Attach APM:
999  *
1000  * Initialize APM driver
1001  */
1002
1003 static int
1004 apm_attach(device_t dev)
1005 {
1006         struct apm_softc        *sc = &apm_softc;
1007         int                     flags;
1008         int                     drv_version;
1009
1010         if (resource_int_value("apm", 0, "flags", &flags) != 0)
1011                 flags = 0;
1012
1013         sc->initialized = 0;
1014
1015         /* Must be externally enabled */
1016         sc->active = 0;
1017
1018         /* Always call HLT in idle loop */
1019         sc->always_halt_cpu = 1;
1020
1021         kgetenv_int("debug.apm_debug", &apm_debug);
1022
1023         /* print bootstrap messages */
1024         APM_DPRINT("apm: APM BIOS version %04lx\n",  apm_version);
1025         APM_DPRINT("apm: Code16 0x%08x, Data 0x%08x\n",
1026             sc->bios.seg.code16.base, sc->bios.seg.data.base);
1027         APM_DPRINT("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n",
1028             sc->bios.entry, is_enabled(sc->slow_idle_cpu),
1029             is_enabled(!sc->disabled));
1030         APM_DPRINT("apm: CS_limit=0x%x, DS_limit=0x%x\n",
1031             sc->bios.seg.code16.limit, sc->bios.seg.data.limit);
1032
1033         /*
1034          * In one test, apm bios version was 1.02; an attempt to register
1035          * a 1.04 driver resulted in a 1.00 connection!  Registering a
1036          * 1.02 driver resulted in a 1.02 connection.
1037          */
1038         drv_version = apm_version > 0x102 ? 0x102 : apm_version;
1039         for (; drv_version > 0x100; drv_version--)
1040                 if (apm_driver_version(drv_version) == 0)
1041                         break;
1042         sc->minorversion = ((drv_version & 0x00f0) >>  4) * 10 +
1043                 ((drv_version & 0x000f) >> 0);
1044         sc->majorversion = ((drv_version & 0xf000) >> 12) * 10 +
1045                 ((apm_version & 0x0f00) >> 8);
1046
1047         sc->intversion = INTVERSION(sc->majorversion, sc->minorversion);
1048
1049         if (sc->intversion >= INTVERSION(1, 1))
1050                 APM_DPRINT("apm: Engaged control %s\n", is_enabled(!sc->disengaged));
1051         device_printf(dev, "found APM BIOS v%ld.%ld, connected at v%d.%d\n",
1052                ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8),
1053                ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0),
1054                sc->majorversion, sc->minorversion);
1055
1056
1057         APM_DPRINT("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu));
1058         /* enable power management */
1059         if (sc->disabled) {
1060                 if (apm_enable_disable_pm(1)) {
1061                         APM_DPRINT("apm: *Warning* enable function failed! [%x]\n",
1062                             (sc->bios.r.eax >> 8) & 0xff);
1063                 }
1064         }
1065
1066         /* engage power managment (APM 1.1 or later) */
1067         if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) {
1068                 if (apm_engage_disengage_pm(1)) {
1069                         APM_DPRINT("apm: *Warning* engage function failed err=[%x]",
1070                             (sc->bios.r.eax >> 8) & 0xff);
1071                         APM_DPRINT(" (Docked or using external power?).\n");
1072                 }
1073         }
1074
1075         /* default suspend hook */
1076         sc->sc_suspend.ah_fun = apm_default_suspend;
1077         sc->sc_suspend.ah_arg = sc;
1078         sc->sc_suspend.ah_name = "default suspend";
1079         sc->sc_suspend.ah_order = APM_MAX_ORDER;
1080
1081         /* default resume hook */
1082         sc->sc_resume.ah_fun = apm_default_resume;
1083         sc->sc_resume.ah_arg = sc;
1084         sc->sc_resume.ah_name = "default resume";
1085         sc->sc_resume.ah_order = APM_MIN_ORDER;
1086
1087         apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend);
1088         apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume);
1089
1090         /* Power the system off using APM */
1091         EVENTHANDLER_REGISTER(shutdown_final, apm_power_off, NULL,
1092                               SHUTDOWN_PRI_LAST);
1093
1094         sc->initialized = 1;
1095
1096         make_dev(&apm_ops, 0, UID_ROOT, GID_OPERATOR, 0660, "apm");
1097         make_dev(&apm_ops, 8, UID_ROOT, GID_OPERATOR, 0660, "apmctl");
1098         return 0;
1099 }
1100
1101 static int
1102 apmopen(struct dev_open_args *ap)
1103 {
1104         cdev_t dev = ap->a_head.a_dev;
1105         struct apm_softc *sc = &apm_softc;
1106         int ctl = APMDEV(dev);
1107
1108         if (!sc->initialized)
1109                 return (ENXIO);
1110
1111         switch (ctl) {
1112         case APMDEV_CTL:
1113                 if (!(ap->a_oflags & FWRITE))
1114                         return EINVAL;
1115                 if (sc->sc_flags & SCFLAG_OCTL)
1116                         return EBUSY;
1117                 sc->sc_flags |= SCFLAG_OCTL;
1118                 bzero(sc->event_filter, sizeof sc->event_filter);
1119                 break;
1120         case APMDEV_NORMAL:
1121                 sc->sc_flags |= SCFLAG_ONORMAL;
1122                 break;
1123         default:
1124                 return ENXIO;
1125                 break;
1126         }
1127         return 0;
1128 }
1129
1130 static int
1131 apmclose(struct dev_close_args *ap)
1132 {
1133         cdev_t dev = ap->a_head.a_dev;
1134         struct apm_softc *sc = &apm_softc;
1135         int ctl = APMDEV(dev);
1136
1137         switch (ctl) {
1138         case APMDEV_CTL:
1139                 apm_lastreq_rejected();
1140                 sc->sc_flags &= ~SCFLAG_OCTL;
1141                 bzero(sc->event_filter, sizeof sc->event_filter);
1142                 break;
1143         case APMDEV_NORMAL:
1144                 sc->sc_flags &= ~SCFLAG_ONORMAL;
1145                 break;
1146         }
1147         if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
1148                 sc->event_count = 0;
1149                 sc->event_ptr = 0;
1150         }
1151         return 0;
1152 }
1153
1154 static int
1155 apmioctl(struct dev_ioctl_args *ap)
1156 {
1157         cdev_t dev = ap->a_head.a_dev;
1158         struct apm_softc *sc = &apm_softc;
1159         struct apm_bios_arg *args;
1160         int error = 0;
1161         int ret;
1162         int newstate;
1163
1164         if (!sc->initialized)
1165                 return (ENXIO);
1166         APM_DPRINT("APM ioctl: cmd = 0x%lx\n", ap->a_cmd);
1167
1168         switch (ap->a_cmd) {
1169         case APMIO_SUSPEND:
1170                 if (!(ap->a_fflag & FWRITE))
1171                         return (EPERM);
1172                 if (sc->active)
1173                         apm_suspend(PMST_SUSPEND);
1174                 else
1175                         error = EINVAL;
1176                 break;
1177
1178         case APMIO_STANDBY:
1179                 if (!(ap->a_fflag & FWRITE))
1180                         return (EPERM);
1181                 if (sc->active)
1182                         apm_suspend(PMST_STANDBY);
1183                 else
1184                         error = EINVAL;
1185                 break;
1186
1187         case APMIO_GETINFO_OLD:
1188                 {
1189                         struct apm_info info;
1190                         apm_info_old_t aiop;
1191
1192                         if (apm_get_info(&info))
1193                                 error = ENXIO;
1194                         aiop = (apm_info_old_t)ap->a_data;
1195                         aiop->ai_major = info.ai_major;
1196                         aiop->ai_minor = info.ai_minor;
1197                         aiop->ai_acline = info.ai_acline;
1198                         aiop->ai_batt_stat = info.ai_batt_stat;
1199                         aiop->ai_batt_life = info.ai_batt_life;
1200                         aiop->ai_status = info.ai_status;
1201                 }
1202                 break;
1203         case APMIO_GETINFO:
1204                 if (apm_get_info((apm_info_t)ap->a_data))
1205                         error = ENXIO;
1206                 break;
1207         case APMIO_GETPWSTATUS:
1208                 if (apm_get_pwstatus((apm_pwstatus_t)ap->a_data))
1209                         error = ENXIO;
1210                 break;
1211         case APMIO_ENABLE:
1212                 if (!(ap->a_fflag & FWRITE))
1213                         return (EPERM);
1214                 apm_event_enable();
1215                 break;
1216         case APMIO_DISABLE:
1217                 if (!(ap->a_fflag & FWRITE))
1218                         return (EPERM);
1219                 apm_event_disable();
1220                 break;
1221         case APMIO_HALTCPU:
1222                 if (!(ap->a_fflag & FWRITE))
1223                         return (EPERM);
1224                 apm_halt_cpu();
1225                 break;
1226         case APMIO_NOTHALTCPU:
1227                 if (!(ap->a_fflag & FWRITE))
1228                         return (EPERM);
1229                 apm_not_halt_cpu();
1230                 break;
1231         case APMIO_DISPLAY:
1232                 if (!(ap->a_fflag & FWRITE))
1233                         return (EPERM);
1234                 newstate = *(int *)ap->a_data;
1235                 if (apm_display(newstate))
1236                         error = ENXIO;
1237                 break;
1238         case APMIO_BIOS:
1239                 if (!(ap->a_fflag & FWRITE))
1240                         return (EPERM);
1241                 /* XXX compatibility with the old interface */
1242                 args = (struct apm_bios_arg *)ap->a_data;
1243                 sc->bios.r.eax = args->eax;
1244                 sc->bios.r.ebx = args->ebx;
1245                 sc->bios.r.ecx = args->ecx;
1246                 sc->bios.r.edx = args->edx;
1247                 sc->bios.r.esi = args->esi;
1248                 sc->bios.r.edi = args->edi;
1249                 if ((ret = apm_bioscall())) {
1250                         /*
1251                          * Return code 1 means bios call was unsuccessful.
1252                          * Error code is stored in %ah.
1253                          * Return code -1 means bios call was unsupported
1254                          * in the APM BIOS version.
1255                          */
1256                         if (ret == -1) {
1257                                 error = EINVAL;
1258                         }
1259                 } else {
1260                         /*
1261                          * Return code 0 means bios call was successful.
1262                          * We need only %al and can discard %ah.
1263                          */
1264                         sc->bios.r.eax &= 0xff;
1265                 }
1266                 args->eax = sc->bios.r.eax;
1267                 args->ebx = sc->bios.r.ebx;
1268                 args->ecx = sc->bios.r.ecx;
1269                 args->edx = sc->bios.r.edx;
1270                 args->esi = sc->bios.r.esi;
1271                 args->edi = sc->bios.r.edi;
1272                 break;
1273         default:
1274                 error = EINVAL;
1275                 break;
1276         }
1277
1278         /* for /dev/apmctl */
1279         if (APMDEV(dev) == APMDEV_CTL) {
1280                 struct apm_event_info *evp;
1281                 int i;
1282
1283                 error = 0;
1284                 switch (ap->a_cmd) {
1285                 case APMIO_NEXTEVENT:
1286                         if (!sc->event_count) {
1287                                 error = EAGAIN;
1288                         } else {
1289                                 evp = (struct apm_event_info *)ap->a_data;
1290                                 i = sc->event_ptr + APM_NEVENTS - sc->event_count;
1291                                 i %= APM_NEVENTS;
1292                                 *evp = sc->event_list[i];
1293                                 sc->event_count--;
1294                         }
1295                         break;
1296                 case APMIO_REJECTLASTREQ:
1297                         if (apm_lastreq_rejected()) {
1298                                 error = EINVAL;
1299                         }
1300                         break;
1301                 default:
1302                         error = EINVAL;
1303                         break;
1304                 }
1305         }
1306
1307         return error;
1308 }
1309
1310 static int
1311 apmwrite(struct dev_write_args *ap)
1312 {
1313         cdev_t dev = ap->a_head.a_dev;
1314         struct uio *uio = ap->a_uio;
1315         struct apm_softc *sc = &apm_softc;
1316         u_int event_type;
1317         int error;
1318         u_char enabled;
1319
1320         if (APMDEV(dev) != APMDEV_CTL)
1321                 return(ENODEV);
1322         if (uio->uio_resid != sizeof(u_int))
1323                 return(E2BIG);
1324
1325         if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio)))
1326                 return(error);
1327
1328         if (event_type < 0 || event_type >= APM_NPMEV)
1329                 return(EINVAL);
1330
1331         if (sc->event_filter[event_type] == 0) {
1332                 enabled = 1;
1333         } else {
1334                 enabled = 0;
1335         }
1336         sc->event_filter[event_type] = enabled;
1337         APM_DPRINT("apmwrite: event 0x%x %s\n", event_type, is_enabled(enabled));
1338
1339         return uio->uio_resid;
1340 }
1341
1342 static struct filterops apmfiltops_read =
1343         { FILTEROP_ISFD, NULL, apmfilter_detach, apmfilter_read };
1344 static struct filterops apmfiltops_write =
1345         { FILTEROP_ISFD, NULL, apmfilter_detach, apmfilter_write };
1346
1347 static int
1348 apmkqfilter(struct dev_kqfilter_args *ap)
1349 {
1350         struct apm_softc *sc = &apm_softc;
1351         struct knote *kn = ap->a_kn;
1352         struct klist *klist;
1353
1354         ap->a_result = 0;
1355
1356         switch (kn->kn_filter) {
1357         case EVFILT_READ:
1358                 kn->kn_fop = &apmfiltops_read;
1359                 kn->kn_hook = (caddr_t)sc;
1360                 break;
1361         case EVFILT_WRITE:
1362                 kn->kn_fop = &apmfiltops_write;
1363                 kn->kn_hook = (caddr_t)sc;
1364                 break;
1365         default:
1366                 ap->a_result = EOPNOTSUPP;
1367                 return (0);
1368         }
1369
1370         klist = &sc->sc_rkq.ki_note;
1371         knote_insert(klist, kn);
1372
1373         return (0);
1374 }
1375
1376 static void
1377 apmfilter_detach(struct knote *kn)
1378 {
1379         struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
1380         struct klist *klist;
1381
1382         klist = &sc->sc_rkq.ki_note;
1383         knote_remove(klist, kn);
1384 }
1385
1386 static int
1387 apmfilter_read(struct knote *kn, long hint)
1388 {
1389         struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
1390         int ready = 0;
1391
1392         if (sc->event_count)
1393                 ready = 1;
1394
1395         return (ready);
1396 }
1397
1398 static int
1399 apmfilter_write(struct knote *kn, long hint)
1400 {
1401         /* write()'s are always OK */
1402         return (1);
1403 }
1404
1405 /*
1406  * Because apm is a static device that always exists under any attached
1407  * isa device, and not scanned by the isa device, we need an identify
1408  * function to install the device so we can probe for it.
1409  */
1410 static device_method_t apm_methods[] = {
1411         /* Device interface */
1412         DEVMETHOD(device_identify,      bus_generic_identify),
1413         DEVMETHOD(device_probe,         apm_probe),
1414         DEVMETHOD(device_attach,        apm_attach),
1415
1416         { 0, 0 }
1417 };
1418
1419 static driver_t apm_driver = {
1420         "apm",
1421         apm_methods,
1422         1,                      /* no softc (XXX) */
1423 };
1424
1425 static devclass_t apm_devclass;
1426
1427 DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, 0, 0);