This source file includes following definitions.
- apm_get_event
- apm_err_translate
- apm_perror
- apm_power_print
- apm_suspend
- apm_standby
- apm_resume
- apm_record_event
- apm_handle_event
- apm_periodic_check
- apm_powmgt_enable
- apm_powmgt_engage
- apm_devpowmgt_enable
- apm_set_powstate
- apm_cpu_busy
- apm_cpu_idle
- apm_set_ver
- apm_disconnect
- apmprobe
- apmattach
- apm_thread_create
- apm_thread
- apmopen
- apmclose
- apmioctl
- filt_apmrdetach
- filt_apmread
- apmkqfilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 #include "apm.h"
38
39 #if NAPM > 1
40 #error only one APM device may be configured
41 #endif
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/signalvar.h>
46 #include <sys/kernel.h>
47 #include <sys/kthread.h>
48 #include <sys/rwlock.h>
49 #include <sys/proc.h>
50 #include <sys/user.h>
51 #include <sys/malloc.h>
52 #include <sys/device.h>
53 #include <sys/fcntl.h>
54 #include <sys/ioctl.h>
55 #include <sys/event.h>
56 #include <sys/mount.h>
57
58 #include <machine/conf.h>
59 #include <machine/cpu.h>
60 #include <machine/cpufunc.h>
61 #include <machine/gdt.h>
62 #include <machine/psl.h>
63
64 #include <dev/isa/isareg.h>
65 #include <i386/isa/isa_machdep.h>
66 #include <i386/isa/nvram.h>
67 #include <dev/isa/isavar.h>
68
69 #include <machine/biosvar.h>
70 #include <machine/apmvar.h>
71
72 #if defined(APMDEBUG)
73 #define DPRINTF(x) printf x
74 #else
75 #define DPRINTF(x)
76 #endif
77
78 struct cfdriver apm_cd = {
79 NULL, "apm", DV_DULL
80 };
81
82 struct apm_softc {
83 struct device sc_dev;
84 struct klist sc_note;
85 int sc_flags;
86 int batt_life;
87 struct proc *sc_thread;
88 struct rwlock sc_lock;
89 };
90 #define SCFLAG_OREAD 0x0000001
91 #define SCFLAG_OWRITE 0x0000002
92 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
93
94 int apmprobe(struct device *, void *, void *);
95 void apmattach(struct device *, struct device *, void *);
96
97 struct cfattach apm_ca = {
98 sizeof(struct apm_softc), apmprobe, apmattach
99 };
100
101 void filt_apmrdetach(struct knote *kn);
102 int filt_apmread(struct knote *kn, long hint);
103
104 struct filterops apmread_filtops = {
105 1, NULL, filt_apmrdetach, filt_apmread
106 };
107
108
109
110
111
112 int cpu_apmwarn = 10;
113
114 #define APM_RESUME_HOLDOFF 3
115
116
117
118
119
120
121
122
123
124
125 #define SCFLAG_NOPRINT 0x0008000
126 #define SCFLAG_PCTPRINT 0x0004000
127 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
128
129 #define APMUNIT(dev) (minor(dev)&0xf0)
130 #define APMDEV(dev) (minor(dev)&0x0f)
131 #define APMDEV_NORMAL 0
132 #define APMDEV_CTL 8
133
134 int apm_standbys;
135 int apm_userstandbys;
136 int apm_suspends;
137 int apm_resumes;
138 int apm_battlow;
139 int apm_evindex;
140 int apm_error;
141 int apm_op_inprog;
142
143 u_int apm_flags;
144 u_char apm_majver;
145 u_char apm_minver;
146 int apm_dobusy = 0;
147 int apm_doidle = 0;
148 int apm_bebatt = 0;
149 int apm_idle_called = 0;
150 int apm_attached = 0;
151
152 struct {
153 u_int32_t entry;
154 u_int16_t seg;
155 u_int16_t pad;
156 } apm_ep;
157
158 struct apmregs {
159 u_int32_t ax;
160 u_int32_t bx;
161 u_int32_t cx;
162 u_int32_t dx;
163 };
164
165 int apmcall(u_int, u_int, struct apmregs *);
166 void apm_power_print(struct apm_softc *, struct apmregs *);
167 int apm_handle_event(struct apm_softc *, struct apmregs *);
168 void apm_set_ver(struct apm_softc *);
169 int apm_periodic_check(struct apm_softc *);
170 void apm_thread_create(void *v);
171 void apm_thread(void *);
172 void apm_disconnect(struct apm_softc *);
173 void apm_perror(const char *, struct apmregs *);
174 void apm_powmgt_enable(int onoff);
175 void apm_powmgt_engage(int onoff, u_int devid);
176
177 int apm_record_event(struct apm_softc *sc, u_int type);
178 const char *apm_err_translate(int code);
179
180 #define apm_get_powstat(r) apmcall(APM_POWER_STATUS, APM_DEV_ALLDEVS, r)
181 void apm_standby(void);
182 void apm_suspend(void);
183 void apm_resume(struct apm_softc *, struct apmregs *);
184
185 static int __inline
186 apm_get_event(struct apmregs *r)
187 {
188 int rv;
189
190 bzero(r, sizeof(*r));
191 rv = apmcall(APM_GET_PM_EVENT, 0, r);
192 return rv;
193 }
194
195 const char *
196 apm_err_translate(int code)
197 {
198 switch (code) {
199 case APM_ERR_PM_DISABLED:
200 return "power management disabled";
201 case APM_ERR_REALALREADY:
202 return "real mode interface already connected";
203 case APM_ERR_NOTCONN:
204 return "interface not connected";
205 case APM_ERR_16ALREADY:
206 return "16-bit interface already connected";
207 case APM_ERR_16NOTSUPP:
208 return "16-bit interface not supported";
209 case APM_ERR_32ALREADY:
210 return "32-bit interface already connected";
211 case APM_ERR_32NOTSUPP:
212 return "32-bit interface not supported";
213 case APM_ERR_UNRECOG_DEV:
214 return "unrecognized device ID";
215 case APM_ERR_ERANGE:
216 return "parameter out of range";
217 case APM_ERR_NOTENGAGED:
218 return "interface not engaged";
219 case APM_ERR_UNABLE:
220 return "unable to enter requested state";
221 case APM_ERR_NOEVENTS:
222 return "No pending events";
223 case APM_ERR_NOT_PRESENT:
224 return "No APM present";
225 default:
226 return "unknown error code?";
227 }
228 }
229
230 int apmerrors = 0;
231
232 void
233 apm_perror(const char *str, struct apmregs *regs)
234 {
235 printf("apm0: APM %s: %s (%d)\n", str,
236 apm_err_translate(APM_ERR_CODE(regs)),
237 APM_ERR_CODE(regs));
238 delay(1000000);
239
240 apmerrors++;
241 }
242
243 void
244 apm_power_print (struct apm_softc *sc, struct apmregs *regs)
245 {
246 #if !defined(APM_NOPRINT)
247 sc->batt_life = BATT_LIFE(regs);
248 if (BATT_LIFE(regs) != APM_BATT_LIFE_UNKNOWN) {
249 printf("%s: battery life expectancy %d%%\n",
250 sc->sc_dev.dv_xname,
251 BATT_LIFE(regs));
252 }
253 printf("%s: AC ", sc->sc_dev.dv_xname);
254 switch (AC_STATE(regs)) {
255 case APM_AC_OFF:
256 printf("off,");
257 break;
258 case APM_AC_ON:
259 printf("on,");
260 break;
261 case APM_AC_BACKUP:
262 printf("backup power,");
263 break;
264 default:
265 case APM_AC_UNKNOWN:
266 printf("unknown,");
267 break;
268 }
269 if (apm_minver == 0) {
270 printf(" battery is ");
271 switch (BATT_STATE(regs)) {
272 case APM_BATT_HIGH:
273 printf("high");
274 break;
275 case APM_BATT_LOW:
276 printf("low");
277 break;
278 case APM_BATT_CRITICAL:
279 printf("CRITICAL");
280 break;
281 case APM_BATT_CHARGING:
282 printf("charging");
283 break;
284 case APM_BATT_UNKNOWN:
285 printf("unknown");
286 break;
287 default:
288 printf("undecoded (%x)", BATT_STATE(regs));
289 break;
290 }
291 } else if (apm_minver >= 1) {
292 if (BATT_FLAGS(regs) & APM_BATT_FLAG_NOBATTERY)
293 printf(" no battery");
294 else {
295 printf(" battery charge ");
296 if (BATT_FLAGS(regs) & APM_BATT_FLAG_HIGH)
297 printf("high");
298 else if (BATT_FLAGS(regs) & APM_BATT_FLAG_LOW)
299 printf("low");
300 else if (BATT_FLAGS(regs) & APM_BATT_FLAG_CRITICAL)
301 printf("critical");
302 else
303 printf("unknown");
304 if (BATT_FLAGS(regs) & APM_BATT_FLAG_CHARGING)
305 printf(", charging");
306 if (BATT_REM_VALID(regs)) {
307 int life = BATT_REMAINING(regs);
308 if (apm_bebatt)
309 life = swap16(life);
310 printf(", estimated %d:%02d hours",
311 life / 60, life % 60);
312 }
313 }
314 }
315
316 printf("\n");
317 #endif
318 }
319
320 void
321 apm_suspend()
322 {
323 dopowerhooks(PWR_SUSPEND);
324
325 if (cold)
326 vfs_syncwait(0);
327
328 (void)apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_SUSPEND);
329 }
330
331 void
332 apm_standby()
333 {
334 dopowerhooks(PWR_STANDBY);
335
336 if (cold)
337 vfs_syncwait(0);
338
339 (void)apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_STANDBY);
340 }
341
342 void
343 apm_resume(struct apm_softc *sc, struct apmregs *regs)
344 {
345 extern int perflevel;
346
347 apm_resumes = APM_RESUME_HOLDOFF;
348
349
350 initrtclock();
351
352 inittodr(time_second);
353
354 dopowerhooks(PWR_RESUME);
355 apm_record_event(sc, regs->bx);
356
357
358 rtcdrain(NULL);
359
360
361 if (cpu_setperf != NULL)
362 cpu_setperf(perflevel);
363 }
364
365 int
366 apm_record_event(struct apm_softc *sc, u_int type)
367 {
368 if (!apm_error && (sc->sc_flags & SCFLAG_OPEN) == 0) {
369 DPRINTF(("apm_record_event: no user waiting\n"));
370 apm_error++;
371 return 1;
372 }
373
374 apm_evindex++;
375 KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, apm_evindex));
376 return (0);
377 }
378
379 int
380 apm_handle_event(struct apm_softc *sc, struct apmregs *regs)
381 {
382 struct apmregs nregs;
383 int ret = 0;
384
385 switch (regs->bx) {
386 case APM_NOEVENT:
387 ret++;
388 break;
389
390 case APM_USER_STANDBY_REQ:
391 if (apm_resumes || apm_op_inprog)
392 break;
393 DPRINTF(("user wants STANDBY--fat chance\n"));
394 apm_op_inprog++;
395 if (apm_record_event(sc, regs->bx)) {
396 DPRINTF(("standby ourselves\n"));
397 apm_userstandbys++;
398 }
399 break;
400 case APM_STANDBY_REQ:
401 if (apm_resumes || apm_op_inprog)
402 break;
403 DPRINTF(("standby requested\n"));
404 if (apm_standbys || apm_suspends) {
405 DPRINTF(("premature standby\n"));
406 apm_error++;
407 ret++;
408 }
409 apm_op_inprog++;
410 if (apm_record_event(sc, regs->bx)) {
411 DPRINTF(("standby ourselves\n"));
412 apm_standbys++;
413 }
414 break;
415 case APM_USER_SUSPEND_REQ:
416 if (apm_resumes || apm_op_inprog)
417 break;
418 DPRINTF(("user wants suspend--fat chance!\n"));
419 apm_op_inprog++;
420 if (apm_record_event(sc, regs->bx)) {
421 DPRINTF(("suspend ourselves\n"));
422 apm_suspends++;
423 }
424 break;
425 case APM_SUSPEND_REQ:
426 if (apm_resumes || apm_op_inprog)
427 break;
428 DPRINTF(("suspend requested\n"));
429 if (apm_standbys || apm_suspends) {
430 DPRINTF(("premature suspend\n"));
431 apm_error++;
432 ret++;
433 }
434 apm_op_inprog++;
435 if (apm_record_event(sc, regs->bx)) {
436 DPRINTF(("suspend ourselves\n"));
437 apm_suspends++;
438 }
439 break;
440 case APM_POWER_CHANGE:
441 DPRINTF(("power status change\n"));
442 if (apm_get_powstat(&nregs) == 0 &&
443 BATT_LIFE(&nregs) != APM_BATT_LIFE_UNKNOWN &&
444 BATT_LIFE(&nregs) < cpu_apmwarn &&
445 (sc->sc_flags & SCFLAG_PRINT) != SCFLAG_NOPRINT &&
446 ((sc->sc_flags & SCFLAG_PRINT) != SCFLAG_PCTPRINT ||
447 sc->batt_life != BATT_LIFE(&nregs)))
448 apm_power_print(sc, &nregs);
449 apm_record_event(sc, regs->bx);
450 break;
451 case APM_NORMAL_RESUME:
452 DPRINTF(("system resumed\n"));
453 apm_resume(sc, regs);
454 break;
455 case APM_CRIT_RESUME:
456 DPRINTF(("system resumed without us!\n"));
457 apm_resume(sc, regs);
458 break;
459 case APM_SYS_STANDBY_RESUME:
460 DPRINTF(("system standby resume\n"));
461 apm_resume(sc, regs);
462 break;
463 case APM_UPDATE_TIME:
464 DPRINTF(("update time, please\n"));
465 inittodr(time_second);
466 apm_record_event(sc, regs->bx);
467 break;
468 case APM_CRIT_SUSPEND_REQ:
469 DPRINTF(("suspend required immediately\n"));
470 apm_record_event(sc, regs->bx);
471 apm_suspend();
472 break;
473 case APM_BATTERY_LOW:
474 DPRINTF(("Battery low!\n"));
475 apm_battlow++;
476 apm_record_event(sc, regs->bx);
477 break;
478 case APM_CAPABILITY_CHANGE:
479 DPRINTF(("capability change\n"));
480 if (apm_minver < 2) {
481 DPRINTF(("adult event\n"));
482 } else {
483 if (apmcall(APM_GET_CAPABILITIES, APM_DEV_APM_BIOS,
484 &nregs) != 0) {
485 apm_perror("get capabilities", &nregs);
486 } else {
487 apm_get_powstat(&nregs);
488 }
489 }
490 break;
491 default: {
492 #ifdef APMDEBUG
493 char *p;
494 switch (regs->bx >> 8) {
495 case 0: p = "reserved system"; break;
496 case 1: p = "reserved device"; break;
497 case 2: p = "OEM defined"; break;
498 default:p = "reserved"; break;
499 }
500 #endif
501 DPRINTF(("apm_handle_event: %s event, code %d\n", p, regs->bx));
502 }
503 }
504 return ret;
505 }
506
507 int
508 apm_periodic_check(struct apm_softc *sc)
509 {
510 struct apmregs regs;
511 int ret = 0;
512
513 if (apm_op_inprog)
514 apm_set_powstate(APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
515
516 while (1) {
517 if (apm_get_event(®s) != 0) {
518
519 if (!(APM_ERR_CODE(®s) & APM_ERR_NOEVENTS))
520 apm_perror("get event", ®s);
521 break;
522 }
523
524 if (apm_handle_event(sc, ®s))
525 break;
526 }
527
528 if (apm_error || APM_ERR_CODE(®s) == APM_ERR_NOTCONN)
529 ret = -1;
530
531 if (apm_suspends ) {
532 apm_op_inprog = 0;
533 apm_suspend();
534 } else if (apm_standbys || apm_userstandbys) {
535 apm_op_inprog = 0;
536 apm_standby();
537 }
538 apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
539 apm_error = 0;
540
541 if (apm_resumes)
542 apm_resumes--;
543 return (ret);
544 }
545
546 void
547 apm_powmgt_enable(int onoff)
548 {
549 struct apmregs regs;
550
551 bzero(®s, sizeof(regs));
552 regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
553 if (apmcall(APM_PWR_MGT_ENABLE,
554 (apm_minver? APM_DEV_APM_BIOS : APM_MGT_ALL), ®s) != 0)
555 apm_perror("power management enable", ®s);
556 }
557
558 void
559 apm_powmgt_engage(int onoff, u_int dev)
560 {
561 struct apmregs regs;
562
563 if (apm_minver == 0)
564 return;
565 bzero(®s, sizeof(regs));
566 regs.cx = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE;
567 if (apmcall(APM_PWR_MGT_ENGAGE, dev, ®s) != 0)
568 printf("apm0: APM engage (device %x): %s (%d)\n",
569 dev, apm_err_translate(APM_ERR_CODE(®s)),
570 APM_ERR_CODE(®s));
571 }
572
573 #ifdef notused
574 void
575 apm_devpowmgt_enable(int onoff, u_int dev)
576 {
577 struct apmregs regs;
578
579 if (apm_minver == 0)
580 return;
581
582
583
584 bzero(®s, sizeof(regs));
585 regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
586 if (apmcall(APM_DEVICE_MGMT_ENABLE, dev, ®s) != 0)
587 printf("APM device engage (device %x): %s (%d)\n",
588 dev, apm_err_translate(APM_ERR_CODE(®s)),
589 APM_ERR_CODE(®s));
590 }
591 #endif
592
593 int
594 apm_set_powstate(u_int dev, u_int state)
595 {
596 struct apmregs regs;
597
598 if (!apm_cd.cd_ndevs || (apm_minver == 0 && state > APM_SYS_OFF))
599 return EINVAL;
600 bzero(®s, sizeof(regs));
601 regs.cx = state;
602 if (apmcall(APM_SET_PWR_STATE, dev, ®s) != 0) {
603 apm_perror("set power state", ®s);
604 if (APM_ERR_CODE(®s) == APM_ERR_UNRECOG_DEV)
605 return ENXIO;
606 else
607 return EIO;
608 }
609 return 0;
610 }
611
612 void
613 apm_cpu_busy(void)
614 {
615 struct apmregs regs;
616
617 if (!apm_cd.cd_ndevs)
618 return;
619 if (!apm_dobusy)
620 return;
621 if (!apm_idle_called)
622 return;
623
624 if (apm_flags & APM_IDLE_SLOWS) {
625 bzero(®s, sizeof(regs));
626 if (apmcall(APM_CPU_BUSY, 0, ®s) != 0) {
627 #ifdef DIAGNOSTIC
628 apm_perror("set CPU busy", ®s);
629 #endif
630 }
631 apm_idle_called = 0;
632 }
633 }
634
635 void
636 apm_cpu_idle(void)
637 {
638 struct apmregs regs;
639 static u_int64_t call_apm = 0;
640
641 if (!apm_cd.cd_ndevs) {
642 __asm __volatile("sti;hlt");
643 return;
644 }
645
646 if (!apm_doidle) {
647 __asm __volatile("sti;hlt");
648 return;
649 }
650
651
652
653
654
655
656 if (call_apm != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
657
658 bzero(®s, sizeof(regs));
659 if (apmcall(APM_CPU_IDLE, 0, ®s) != 0) {
660 #ifdef APMDEBUG
661 apm_perror("set CPU idle", ®s);
662 #endif
663 }
664 apm_idle_called = 1;
665
666 if (apm_flags & APM_IDLE_SLOWS) {
667 __asm __volatile("sti;hlt");
668 }
669 call_apm = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
670 } else {
671 __asm __volatile("sti;hlt");
672 }
673 }
674
675 void
676 apm_set_ver(struct apm_softc *self)
677 {
678 struct apmregs regs;
679 int rv = 0;
680
681 bzero(®s, sizeof(regs));
682 regs.cx = APM_VERSION;
683
684 if (APM_MAJOR(apm_flags) == 1 && APM_MINOR(apm_flags) == 2 &&
685 (rv = apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, ®s)) == 0) {
686 apm_majver = APM_CONN_MAJOR(®s);
687 apm_minver = APM_CONN_MINOR(®s);
688 } else {
689 #ifdef APMDEBUG
690 if (rv)
691 apm_perror("set version 1.2", ®s);
692 #endif
693
694 bzero(®s, sizeof(regs));
695 regs.cx = 0x0101;
696
697 if (apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, ®s) == 0) {
698 apm_majver = 1;
699 apm_minver = 1;
700 } else {
701 #ifdef APMDEBUG
702 apm_perror("set version 1.1", ®s);
703 #endif
704
705 apm_majver = APM_MAJOR(apm_flags);
706 apm_minver = APM_MINOR(apm_flags);
707
708
709 if (!apm_majver) {
710 apm_majver = 1;
711 apm_minver = 0;
712 }
713 }
714 }
715 printf(": Power Management spec V%d.%d", apm_majver, apm_minver);
716 if (apm_flags & APM_IDLE_SLOWS) {
717 DPRINTF((" (slowidle)"));
718 apm_dobusy = 1;
719 apm_doidle = 1;
720 } else {
721 apm_dobusy = 0;
722 apm_doidle = 1;
723 }
724 #ifdef DIAGNOSTIC
725 if (apm_flags & APM_BIOS_PM_DISABLED)
726 printf(" (BIOS mgmt disabled)");
727 if (apm_flags & APM_BIOS_PM_DISENGAGED)
728 printf(" (BIOS managing devices)");
729 #endif
730 printf("\n");
731 }
732
733 void
734 apm_disconnect(struct apm_softc *sc)
735 {
736 struct apmregs regs;
737
738 bzero(®s, sizeof(regs));
739 if (apmcall(APM_SYSTEM_DEFAULTS,
740 (apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL), ®s))
741 apm_perror("system defaults failed", ®s);
742
743 if (apmcall(APM_DISCONNECT, APM_DEV_APM_BIOS, ®s))
744 apm_perror("disconnect failed", ®s);
745 else
746 printf("%s: disconnected\n", sc->sc_dev.dv_xname);
747 apm_flags |= APM_BIOS_PM_DISABLED;
748 }
749
750 int
751 apmprobe(struct device *parent, void *match, void *aux)
752 {
753 struct bios_attach_args *ba = aux;
754 bios_apminfo_t *ap = ba->bios_apmp;
755 bus_space_handle_t ch, dh;
756
757 if (apm_cd.cd_ndevs || strcmp(ba->bios_dev, "apm") ||
758 !(ba->bios_apmp->apm_detail & APM_32BIT_SUPPORTED)) {
759 DPRINTF(("%s: %x\n", ba->bios_dev, ba->bios_apmp->apm_detail));
760 return 0;
761 }
762
763
764
765
766
767 if (ap->apm_code_len == 0 ||
768 (ap->apm_code32_base < IOM_BEGIN &&
769 ap->apm_code32_base + ap->apm_code_len > IOM_BEGIN) ||
770 (ap->apm_code16_base < IOM_BEGIN &&
771 ap->apm_code16_base + ap->apm_code16_len > IOM_BEGIN) ||
772 (ap->apm_data_base < IOM_BEGIN &&
773 ap->apm_data_base + ap->apm_data_len > IOM_BEGIN))
774 return 0;
775
776 if (bus_space_map(ba->bios_memt, ap->apm_code32_base,
777 ap->apm_code_len, 1, &ch) != 0) {
778 DPRINTF(("apm0: can't map code\n"));
779 return 0;
780 }
781 bus_space_unmap(ba->bios_memt, ch, ap->apm_code_len);
782
783 if (bus_space_map(ba->bios_memt, ap->apm_data_base,
784 ap->apm_data_len, 1, &dh) != 0) {
785 DPRINTF(("apm0: can't map data\n"));
786 return 0;
787 }
788 bus_space_unmap(ba->bios_memt, dh, ap->apm_data_len);
789 return 1;
790 }
791
792 void
793 apmattach(struct device *parent, struct device *self, void *aux)
794 {
795 struct bios_attach_args *ba = aux;
796 bios_apminfo_t *ap = ba->bios_apmp;
797 struct apm_softc *sc = (void *)self;
798 struct apmregs regs;
799 u_int cbase, clen, l;
800 bus_space_handle_t ch16, ch32, dh;
801
802 apm_flags = ap->apm_detail;
803
804
805
806 if (apm_flags & APM_32BIT_SUPPORTED) {
807
808
809 ap->apm_code_len -= (ap->apm_code32_base +
810 ap->apm_code_len + 1) & 0xfff;
811 ap->apm_code16_len -= (ap->apm_code16_base +
812 ap->apm_code16_len + 1) & 0xfff;
813 ap->apm_data_len -= (ap->apm_data_base +
814 ap->apm_data_len + 1) & 0xfff;
815
816
817 if ((sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK) &&
818 (apm_flags & APM_VERMASK) !=
819 (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK))
820 apm_flags = (apm_flags & ~APM_VERMASK) |
821 (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK);
822 if (sc->sc_dev.dv_cfdata->cf_flags & APM_NOCLI) {
823 extern int apm_cli;
824 apm_cli = 0;
825 }
826 if (sc->sc_dev.dv_cfdata->cf_flags & APM_BEBATT)
827 apm_bebatt = 1;
828 apm_ep.seg = GSEL(GAPM32CODE_SEL,SEL_KPL);
829 apm_ep.entry = ap->apm_entry;
830 cbase = min(ap->apm_code32_base, ap->apm_code16_base);
831 clen = max(ap->apm_code32_base + ap->apm_code_len,
832 ap->apm_code16_base + ap->apm_code16_len) - cbase;
833 if ((cbase <= ap->apm_data_base &&
834 cbase + clen >= ap->apm_data_base) ||
835 (ap->apm_data_base <= cbase &&
836 ap->apm_data_base + ap->apm_data_len >= cbase)) {
837 l = max(ap->apm_data_base + ap->apm_data_len + 1,
838 cbase + clen + 1) -
839 min(ap->apm_data_base, cbase);
840 bus_space_map(ba->bios_memt,
841 min(ap->apm_data_base, cbase),
842 l, 1, &dh);
843 ch16 = dh;
844 if (ap->apm_data_base < cbase)
845 ch16 += cbase - ap->apm_data_base;
846 else
847 dh += ap->apm_data_base - cbase;
848 } else {
849
850 bus_space_map(ba->bios_memt, cbase, clen + 1, 1, &ch16);
851 bus_space_map(ba->bios_memt, ap->apm_data_base,
852 ap->apm_data_len + 1, 1, &dh);
853 }
854 ch32 = ch16;
855 if (ap->apm_code16_base == cbase)
856 ch32 += ap->apm_code32_base - cbase;
857 else
858 ch16 += ap->apm_code16_base - cbase;
859
860 setgdt(GAPM32CODE_SEL, (void *)ch32, ap->apm_code_len,
861 SDT_MEMERA, SEL_KPL, 1, 0);
862 setgdt(GAPM16CODE_SEL, (void *)ch16, ap->apm_code16_len,
863 SDT_MEMERA, SEL_KPL, 0, 0);
864 setgdt(GAPMDATA_SEL, (void *)dh, ap->apm_data_len, SDT_MEMRWA,
865 SEL_KPL, 1, 0);
866 DPRINTF((": flags %x code 32:%x/%x[%x] 16:%x/%x[%x] "
867 "data %x/%x/%x ep %x (%x:%x)\n%s", apm_flags,
868 ap->apm_code32_base, ch32, ap->apm_code_len,
869 ap->apm_code16_base, ch16, ap->apm_code16_len,
870 ap->apm_data_base, dh, ap->apm_data_len,
871 ap->apm_entry, apm_ep.seg, ap->apm_entry+ch32,
872 sc->sc_dev.dv_xname));
873
874 apm_set_ver(sc);
875
876 if (apm_flags & APM_BIOS_PM_DISABLED)
877 apm_powmgt_enable(1);
878
879
880
881
882 apm_powmgt_engage(1, APM_DEV_ALLDEVS);
883
884 bzero(®s, sizeof(regs));
885 if (apm_get_powstat(®s) == 0)
886 apm_power_print(sc, ®s);
887 else
888 apm_perror("get power status", ®s);
889 apm_cpu_busy();
890
891 rw_init(&sc->sc_lock, "apmlk");
892
893
894
895
896
897
898
899 apm_periodic_check(sc);
900
901 if (apm_periodic_check(sc) == -1) {
902 apm_disconnect(sc);
903 apm_dobusy = apm_doidle = 0;
904 } else {
905 kthread_create_deferred(apm_thread_create, sc);
906 apm_attached = 1;
907 }
908 } else {
909 setgdt(GAPM32CODE_SEL, NULL, 0, 0, 0, 0, 0);
910 setgdt(GAPM16CODE_SEL, NULL, 0, 0, 0, 0, 0);
911 setgdt(GAPMDATA_SEL, NULL, 0, 0, 0, 0, 0);
912 }
913
914 printf("apm0: flags %x dobusy %d doidle %d\n",
915 apm_flags, apm_dobusy, apm_doidle);
916 }
917
918 void
919 apm_thread_create(void *v)
920 {
921 struct apm_softc *sc = v;
922
923 #ifdef MULTIPROCESSOR
924 if (ncpus > 1) {
925 apm_disconnect(sc);
926 apm_dobusy = apm_doidle = 0;
927 return;
928 }
929 #endif
930
931 if (kthread_create(apm_thread, sc, &sc->sc_thread, "%s",
932 sc->sc_dev.dv_xname)) {
933 apm_disconnect(sc);
934 printf("%s: failed to create kernel thread, disabled",
935 sc->sc_dev.dv_xname);
936 apm_dobusy = apm_doidle = 0;
937 }
938 }
939
940 void
941 apm_thread(void *v)
942 {
943 struct apm_softc *sc = v;
944
945 for (;;) {
946 rw_enter_write(&sc->sc_lock);
947 (void) apm_periodic_check(sc);
948 rw_exit_write(&sc->sc_lock);
949 tsleep(&lbolt, PWAIT, "apmev", 0);
950 }
951 }
952
953 int
954 apmopen(dev_t dev, int flag, int mode, struct proc *p)
955 {
956 struct apm_softc *sc;
957 int error = 0;
958
959
960 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
961 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
962 return ENXIO;
963
964 if (apm_flags & APM_BIOS_PM_DISABLED)
965 return ENXIO;
966
967 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
968 APMDEV(dev), p->p_pid, flag, mode));
969
970 rw_enter_write(&sc->sc_lock);
971 switch (APMDEV(dev)) {
972 case APMDEV_CTL:
973 if (!(flag & FWRITE)) {
974 error = EINVAL;
975 break;
976 }
977 if (sc->sc_flags & SCFLAG_OWRITE) {
978 error = EBUSY;
979 break;
980 }
981 sc->sc_flags |= SCFLAG_OWRITE;
982 break;
983 case APMDEV_NORMAL:
984 if (!(flag & FREAD) || (flag & FWRITE)) {
985 error = EINVAL;
986 break;
987 }
988 sc->sc_flags |= SCFLAG_OREAD;
989 break;
990 default:
991 error = ENXIO;
992 break;
993 }
994 rw_exit_write(&sc->sc_lock);
995 return error;
996 }
997
998 int
999 apmclose(dev_t dev, int flag, int mode, struct proc *p)
1000 {
1001 struct apm_softc *sc;
1002
1003
1004 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
1005 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
1006 return ENXIO;
1007
1008 DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
1009
1010 rw_enter_write(&sc->sc_lock);
1011 switch (APMDEV(dev)) {
1012 case APMDEV_CTL:
1013 sc->sc_flags &= ~SCFLAG_OWRITE;
1014 break;
1015 case APMDEV_NORMAL:
1016 sc->sc_flags &= ~SCFLAG_OREAD;
1017 break;
1018 }
1019 rw_exit_write(&sc->sc_lock);
1020 return 0;
1021 }
1022
1023 int
1024 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
1025 {
1026 struct apm_softc *sc;
1027 struct apmregs regs;
1028 int error = 0;
1029
1030
1031 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
1032 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
1033 return ENXIO;
1034
1035 rw_enter_write(&sc->sc_lock);
1036 switch (cmd) {
1037
1038 case APM_IOC_STANDBY:
1039 if ((flag & FWRITE) == 0)
1040 error = EBADF;
1041 else
1042 apm_userstandbys++;
1043 break;
1044 case APM_IOC_SUSPEND:
1045 if ((flag & FWRITE) == 0)
1046 error = EBADF;
1047 else
1048 apm_suspends++;
1049 break;
1050 case APM_IOC_PRN_CTL:
1051 if ((flag & FWRITE) == 0)
1052 error = EBADF;
1053 else {
1054 int flag = *(int *)data;
1055 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
1056 switch (flag) {
1057 case APM_PRINT_ON:
1058 sc->sc_flags &= ~SCFLAG_PRINT;
1059 break;
1060 case APM_PRINT_OFF:
1061 sc->sc_flags &= ~SCFLAG_PRINT;
1062 sc->sc_flags |= SCFLAG_NOPRINT;
1063 break;
1064 case APM_PRINT_PCT:
1065 sc->sc_flags &= ~SCFLAG_PRINT;
1066 sc->sc_flags |= SCFLAG_PCTPRINT;
1067 break;
1068 default:
1069 error = EINVAL;
1070 break;
1071 }
1072 }
1073 break;
1074 case APM_IOC_DEV_CTL:
1075 if ((flag & FWRITE) == 0)
1076 error = EBADF;
1077 else {
1078 struct apm_ctl *actl = (struct apm_ctl *)data;
1079
1080 bzero(®s, sizeof(regs));
1081 if (!apmcall(APM_GET_POWER_STATE, actl->dev, ®s))
1082 printf("%s: dev %04x state %04x\n",
1083 sc->sc_dev.dv_xname, dev, regs.cx);
1084
1085 error = apm_set_powstate(actl->dev, actl->mode);
1086 }
1087 break;
1088 case APM_IOC_GETPOWER:
1089 if (apm_get_powstat(®s) == 0) {
1090 struct apm_power_info *powerp =
1091 (struct apm_power_info *)data;
1092
1093 bzero(powerp, sizeof(*powerp));
1094 if (BATT_LIFE(®s) != APM_BATT_LIFE_UNKNOWN)
1095 powerp->battery_life = BATT_LIFE(®s);
1096 powerp->ac_state = AC_STATE(®s);
1097 switch (apm_minver) {
1098 case 0:
1099 if (!(BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY))
1100 powerp->battery_state = BATT_STATE(®s);
1101 break;
1102 case 1:
1103 default:
1104 if (BATT_FLAGS(®s) & APM_BATT_FLAG_HIGH)
1105 powerp->battery_state = APM_BATT_HIGH;
1106 else if (BATT_FLAGS(®s) & APM_BATT_FLAG_LOW)
1107 powerp->battery_state = APM_BATT_LOW;
1108 else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CRITICAL)
1109 powerp->battery_state = APM_BATT_CRITICAL;
1110 else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CHARGING)
1111 powerp->battery_state = APM_BATT_CHARGING;
1112 else if (BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY)
1113 powerp->battery_state = APM_BATTERY_ABSENT;
1114 else
1115 powerp->battery_state = APM_BATT_UNKNOWN;
1116 if (BATT_REM_VALID(®s)) {
1117 powerp->minutes_left = BATT_REMAINING(®s);
1118 if (apm_bebatt)
1119 powerp->minutes_left =
1120 swap16(powerp->minutes_left);
1121 }
1122 }
1123 } else {
1124 apm_perror("ioctl get power status", ®s);
1125 error = EIO;
1126 }
1127 break;
1128
1129 default:
1130 error = ENOTTY;
1131 }
1132
1133 rw_exit_write(&sc->sc_lock);
1134 return error;
1135 }
1136
1137 void
1138 filt_apmrdetach(struct knote *kn)
1139 {
1140 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
1141
1142 rw_enter_write(&sc->sc_lock);
1143 SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
1144 rw_exit_write(&sc->sc_lock);
1145 }
1146
1147 int
1148 filt_apmread(struct knote *kn, long hint)
1149 {
1150
1151 if (hint && !kn->kn_data)
1152 kn->kn_data = (int)hint;
1153 return (1);
1154 }
1155
1156 int
1157 apmkqfilter(dev_t dev, struct knote *kn)
1158 {
1159 struct apm_softc *sc;
1160
1161
1162 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
1163 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
1164 return ENXIO;
1165
1166 switch (kn->kn_filter) {
1167 case EVFILT_READ:
1168 kn->kn_fop = &apmread_filtops;
1169 break;
1170 default:
1171 return (1);
1172 }
1173
1174 kn->kn_hook = (caddr_t)sc;
1175
1176 rw_enter_write(&sc->sc_lock);
1177 SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
1178 rw_exit_write(&sc->sc_lock);
1179 return (0);
1180 }