apm.c revision 12504
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@mt.cs.keio.ac.jp> 6 * 7 * This software may be used, modified, copied, and distributed, in 8 * both source and binary form provided that the above copyright and 9 * these terms are retained. Under no circumstances is the author 10 * responsible for the proper functioning of this software, nor does 11 * the author assume any responsibility for damages incurred with its 12 * use. 13 * 14 * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) 15 * 16 * $Id: apm.c,v 1.15 1995/11/18 04:37:23 bde Exp $ 17 */ 18 19#include "apm.h" 20 21#if NAPM > 0 22 23#ifdef __FreeBSD__ 24#include <sys/param.h> 25#include "conf.h" 26#ifdef JREMOD 27#include <sys/conf.h> 28#endif /*JREMOD*/ 29#include <sys/kernel.h> 30#include <sys/systm.h> 31#include <sys/malloc.h> 32#include <sys/ioctl.h> 33#include <sys/file.h> 34#include <sys/proc.h> 35#include <sys/vnode.h> 36#include "i386/isa/isa.h" 37#include "i386/isa/isa_device.h" 38#include <machine/apm_bios.h> 39#include <machine/segments.h> 40#include <machine/clock.h> 41#include <vm/vm.h> 42#include <sys/syslog.h> 43#include "apm_setup.h" 44#endif /* __FreeBSD__ */ 45 46#ifdef MACH_KERNEL 47#include <mach/std_types.h> 48#include <sys/types.h> 49#include <sys/time.h> 50#include <device/conf.h> 51#include <device/errno.h> 52#include <device/tty.h> 53#include <device/io_req.h> 54#include <kern/time_out.h> 55 56#include <i386/ipl.h> 57#include <i386/pio.h> 58#include <i386/seg.h> 59#include <i386/machspl.h> 60#include <chips/busses.h> 61#include <i386at/apm_bios.h> 62#include <i386at/apm_setup.h> 63#include <i386at/apm_segments.h> 64#endif /* MACH_KERNEL */ 65 66extern int apm_display_off __P((void)); 67extern int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx)); 68extern void apm_resume __P((void)); 69 70/* static data */ 71struct apm_softc { 72 int initialized, active, halt_cpu; 73 u_int minorversion, majorversion; 74 u_int cs32_base, cs16_base, ds_base; 75 u_int cs_limit, ds_limit; 76 u_int cs_entry; 77 u_int intversion; 78 int idle_cpu, disabled, disengaged; 79 struct apmhook sc_suspend; 80 struct apmhook sc_resume; 81}; 82 83static struct apm_softc apm_softc[NAPM]; 84static struct apm_softc *master_softc = NULL; /* XXX */ 85struct apmhook *hook[NAPM_HOOK]; /* XXX */ 86#ifdef APM_SLOWSTART 87int apm_slowstart = 0; 88int apm_ss_cnt = 0; 89static int apm_slowstart_p = 0; 90int apm_slowstart_stat = 0; 91#endif /* APM_SLOWSTART */ 92 93#ifdef MACH_KERNEL 94extern struct fake_descriptor gdt[GDTSZ]; 95extern void fix_desc(struct fake_descriptor *, int); 96#endif /* MACH_KERNEL */ 97 98#define is_enabled(foo) ((foo) ? "enabled" : "disabled") 99 100/* Map version number to integer (keeps ordering of version numbers) */ 101#define INTVERSION(major, minor) ((major)*100 + (minor)) 102 103#ifdef __FreeBSD__ 104static timeout_t apm_timeout; 105#ifdef JREMOD 106#define CDEV_MAJOR 39 107static void apm_devsw_install(); 108#endif /* JREMOD */ 109#endif /* __FreeBSD__ */ 110#ifdef MACH_KERNEL 111static void apm_timeout(void *); 112#endif /* MACH_KERNEL */ 113 114/* setup APM GDT discriptors */ 115static void 116setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code_limit, u_int data_limit) 117{ 118#ifdef __FreeBSD__ 119 /* setup 32bit code segment */ 120 gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; 121 gdt_segs[GAPMCODE32_SEL].ssd_limit = code_limit; 122 123 /* setup 16bit code segment */ 124 gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; 125 gdt_segs[GAPMCODE16_SEL].ssd_limit = code_limit; 126 127 /* setup data segment */ 128 gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; 129 gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; 130 131 /* reflect these changes on physical GDT */ 132 ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd); 133 ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd); 134 ssdtosd(gdt_segs + GAPMDATA_SEL , &gdt[GAPMDATA_SEL ].sd); 135#endif /* __FreeBSD__ */ 136#ifdef MACH_KERNEL 137 /* setup 32bit code segment */ 138 gdt[sel_idx(GAPMCODE32_SEL)].offset = code32_base; 139 gdt[sel_idx(GAPMCODE32_SEL)].lim_or_seg = code_limit; 140 gdt[sel_idx(GAPMCODE32_SEL)].size_or_wdct = SZ_32; 141 gdt[sel_idx(GAPMCODE32_SEL)].access = ACC_P | ACC_PL_K | ACC_CODE_R; 142 143 /* setup 16bit code segment */ 144 gdt[sel_idx(GAPMCODE16_SEL)].offset = code16_base; 145 gdt[sel_idx(GAPMCODE16_SEL)].lim_or_seg = code_limit; 146 gdt[sel_idx(GAPMCODE16_SEL)].size_or_wdct = 0; 147 gdt[sel_idx(GAPMCODE16_SEL)].access = ACC_P | ACC_PL_K | ACC_CODE_R; 148 149 /* setup data segment */ 150 gdt[sel_idx(GAPMDATA_SEL )].offset = data_base; 151 gdt[sel_idx(GAPMDATA_SEL )].lim_or_seg = data_limit; 152 gdt[sel_idx(GAPMDATA_SEL )].size_or_wdct = 0; 153 gdt[sel_idx(GAPMDATA_SEL )].access = ACC_P | ACC_PL_K | ACC_DATA_W; 154 155 /* reflect these changes on physical GDT */ 156 fix_desc(gdt + sel_idx(GAPMCODE32_SEL), 1); 157 fix_desc(gdt + sel_idx(GAPMCODE16_SEL), 1); 158 fix_desc(gdt + sel_idx(GAPMDATA_SEL) , 1); 159#endif /* MACH_KERNEL */ 160} 161 162/* 48bit far pointer */ 163struct addr48 { 164 u_long offset; 165 u_short segment; 166} apm_addr; 167 168int apm_errno; 169 170inline 171int 172apm_int(u_long *eax, u_long *ebx, u_long *ecx) 173{ 174 u_long cf; 175 __asm ("pushl %%ebp 176 pushl %%edx 177 pushl %%esi 178 xorl %3,%3 179 movl %3,%%esi 180 lcall _apm_addr 181 jnc 1f 182 incl %3 183 1: 184 popl %%esi 185 popl %%edx 186 popl %%ebp" 187 : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=D" (cf) 188 : "0" (*eax), "1" (*ebx), "2" (*ecx) 189 ); 190 apm_errno = ((*eax) >> 8) & 0xff; 191 return cf; 192} 193 194 195/* enable/disable power management */ 196static int 197apm_enable_disable_pm(struct apm_softc *sc, int enable) 198{ 199 u_long eax, ebx, ecx; 200 201 eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM; 202 203 if (sc->intversion >= INTVERSION(1, 1)) { 204 ebx = PMDV_ALLDEV; 205 } else { 206 ebx = 0xffff; /* APM version 1.0 only */ 207 } 208 ecx = enable; 209 return apm_int(&eax, &ebx, &ecx); 210} 211 212/* Tell APM-BIOS that WE will do 1.1 and see what they say... */ 213static void 214apm_driver_version(void) 215{ 216 u_long eax, ebx, ecx; 217 218#ifdef APM_DEBUG 219 eax = (APM_BIOS<<8) | APM_INSTCHECK; 220 ebx = 0x0; 221 ecx = 0x0101; 222 i = apm_int(&eax, &ebx, &ecx); 223 printf("[%04lx %04lx %04lx %ld %02x]\n", 224 eax, ebx, ecx, i, apm_errno); 225#endif 226 227 eax = (APM_BIOS << 8) | APM_DRVVERSION; 228 ebx = 0x0; 229 ecx = 0x0101; 230 if(!apm_int(&eax, &ebx, &ecx)) 231 apm_version = eax & 0xffff; 232 233#ifdef APM_DEBUG 234 eax = (APM_BIOS << 8) | APM_INSTCHECK; 235 ebx = 0x0; 236 ecx = 0x0101; 237 i = apm_int(&eax, &ebx, &ecx); 238 printf("[%04lx %04lx %04lx %ld %02x]\n", 239 eax, ebx, ecx, i, apm_errno); 240#endif 241} 242 243/* engage/disengage power management (APM 1.1 or later) */ 244static int 245apm_engage_disengage_pm(struct apm_softc *sc, int engage) 246{ 247 u_long eax, ebx, ecx, i; 248 249 eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; 250 ebx = PMDV_ALLDEV; 251 ecx = engage; 252 i = apm_int(&eax, &ebx, &ecx); 253 return i; 254} 255 256/* get PM event */ 257static u_int 258apm_getevent(struct apm_softc *sc) 259{ 260 u_long eax, ebx, ecx; 261 262 eax = (APM_BIOS << 8) | APM_GETPMEVENT; 263 264 ebx = 0; 265 ecx = 0; 266 if (apm_int(&eax, &ebx, &ecx)) 267 return PMEV_NOEVENT; 268 269 return ebx & 0xffff; 270} 271 272/* suspend entire system */ 273static int 274apm_suspend_system(struct apm_softc *sc) 275{ 276 u_long eax, ebx, ecx; 277 278 eax = (APM_BIOS << 8) | APM_SETPWSTATE; 279 ebx = PMDV_ALLDEV; 280 ecx = PMST_SUSPEND; 281 282 __asm("cli"); 283 if (apm_int(&eax, &ebx, &ecx)) { 284 __asm("sti"); 285 printf("Entire system suspend failure: errcode = %ld\n", 286 0xff & (eax >> 8)); 287 return 1; 288 } 289 __asm("sti"); 290 return 0; 291} 292 293/* Display control */ 294/* 295 * Experimental implementation: My laptop machine can't handle this function 296 * If your laptop can control the display via APM, please inform me. 297 * HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> 298 */ 299int 300apm_display_off(void) 301{ 302 u_long eax, ebx, ecx; 303 304 eax = (APM_BIOS << 8) | APM_SETPWSTATE; 305 ebx = PMDV_2NDSTORAGE0; 306 ecx = PMST_STANDBY; 307 if (apm_int(&eax, &ebx, &ecx)) { 308 printf("Display off failure: errcode = %ld\n", 309 0xff & (eax >> 8)); 310 return 1; 311 } 312 313 return 0; 314} 315 316/* APM Battery low handler */ 317static void 318apm_battery_low(struct apm_softc *sc) 319{ 320 printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); 321} 322 323/* APM hook manager */ 324static struct apmhook * 325apm_add_hook(struct apmhook **list, struct apmhook *ah) 326{ 327 int s; 328 struct apmhook *p, *prev; 329 330#if 0 331 printf("Add hook \"%s\"\n", ah->ah_name); 332#endif 333 334 s = splhigh(); 335 if (ah == NULL) { 336 panic("illegal apm_hook!"); 337 } 338 prev = NULL; 339 for (p = *list; p != NULL; prev = p, p = p->ah_next) { 340 if (p->ah_order > ah->ah_order) { 341 break; 342 } 343 } 344 345 if (prev == NULL) { 346 ah->ah_next = *list; 347 *list = ah; 348 } else { 349 ah->ah_next = prev->ah_next; 350 prev->ah_next = ah; 351 } 352 splx(s); 353 return ah; 354} 355 356static void 357apm_del_hook(struct apmhook **list, struct apmhook *ah) 358{ 359 int s; 360 struct apmhook *p, *prev; 361 362 s = splhigh(); 363 prev = NULL; 364 for (p = *list; p != NULL; prev = p, p = p->ah_next) { 365 if (p == ah) { 366 goto deleteit; 367 } 368 } 369 panic("Tried to delete unregistered apm_hook."); 370 goto nosuchnode; 371deleteit: 372 if (prev != NULL) { 373 prev->ah_next = p->ah_next; 374 } else { 375 *list = p->ah_next; 376 } 377nosuchnode: 378 splx(s); 379} 380 381 382/* APM driver calls some functions automatically */ 383static void 384apm_execute_hook(struct apmhook *list) 385{ 386 struct apmhook *p; 387 388 for (p = list; p != NULL; p = p->ah_next) { 389#if 0 390 printf("Execute APM hook \"%s.\"\n", p->ah_name); 391#endif 392 if ((*(p->ah_fun))(p->ah_arg)) { 393 printf("Warning: APM hook \"%s\" failed", p->ah_name); 394 } 395 } 396} 397 398 399/* establish an apm hook */ 400struct apmhook * 401apm_hook_establish(int apmh, struct apmhook *ah) 402{ 403 if (apmh < 0 || apmh >= NAPM_HOOK) 404 return NULL; 405 406 return apm_add_hook(&hook[apmh], ah); 407} 408 409/* disestablish an apm hook */ 410void 411apm_hook_disestablish(int apmh, struct apmhook *ah) 412{ 413 if (apmh < 0 || apmh >= NAPM_HOOK) 414 return; 415 416 apm_del_hook(&hook[apmh], ah); 417} 418 419 420static struct timeval suspend_time; 421static struct timeval diff_time; 422 423static int 424apm_default_resume(void *arg) 425{ 426#ifdef __FreeBSD__ 427 int pl; 428 u_int second, minute, hour; 429 struct timeval resume_time, tmp_time; 430 431 /* modified for adjkerntz */ 432 pl = splsoftclock(); 433 inittodr(0); /* adjust time to RTC */ 434 microtime(&resume_time); 435 tmp_time = time; /* because 'time' is volatile */ 436 timevaladd(&tmp_time, &diff_time); 437 time = tmp_time; 438 splx(pl); 439 second = resume_time.tv_sec - suspend_time.tv_sec; 440 hour = second / 3600; 441 second %= 3600; 442 minute = second / 60; 443 second %= 60; 444 log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", 445 hour, minute, second); 446#endif /* __FreeBSD__ */ 447 return 0; 448} 449 450static int 451apm_default_suspend(void *arg) 452{ 453#ifdef __FreeBSD__ 454 int pl; 455 456 pl = splsoftclock(); 457 microtime(&diff_time); 458 inittodr(0); 459 microtime(&suspend_time); 460 timevalsub(&diff_time, &suspend_time); 461 splx(pl); 462#if 0 463 printf("diff_time = %d:%d\n", diff_time.tv_sec, diff_time.tv_usec); 464#endif 465#endif /* __FreeBSD__ */ 466 return 0; 467} 468 469static void apm_processevent(struct apm_softc *); 470 471/* 472 * Public interface to the suspend/resume: 473 * 474 * Execute suspend and resume hook before and after sleep, respectively. 475 * 476 */ 477 478void 479apm_suspend(void) 480{ 481 struct apm_softc *sc; 482 483 sc = master_softc; /* XXX */ 484 if (!sc) 485 return; 486 487 if (sc->initialized) { 488 apm_execute_hook(hook[APM_HOOK_SUSPEND]); 489 apm_suspend_system(sc); 490 apm_processevent(sc); 491 } 492} 493 494void 495apm_resume(void) 496{ 497 struct apm_softc *sc; 498 499 sc = master_softc; /* XXX */ 500 if (!sc) 501 return; 502 503 if (sc->initialized) { 504 apm_execute_hook(hook[APM_HOOK_RESUME]); 505 } 506} 507 508 509/* get APM information */ 510static int 511apm_get_info(struct apm_softc *sc, apm_info_t aip) 512{ 513 u_long eax, ebx, ecx; 514 515 eax = (APM_BIOS << 8) | APM_GETPWSTATUS; 516 ebx = PMDV_ALLDEV; 517 ecx = 0; 518 519 if (apm_int(&eax, &ebx, &ecx)) 520 return 1; 521 522 aip->ai_acline = (ebx >> 8) & 0xff; 523 aip->ai_batt_stat = ebx & 0xff; 524 aip->ai_batt_life = ecx & 0xff; 525 aip->ai_major = (u_int)sc->majorversion; 526 aip->ai_minor = (u_int)sc->minorversion; 527 return 0; 528} 529 530 531/* inform APM BIOS that CPU is idle */ 532void 533apm_cpu_idle(void) 534{ 535 struct apm_softc *sc = master_softc; /* XXX */ 536 537 if (sc->idle_cpu) { 538 if (sc->active) { 539 __asm ("movw $0x5305, %ax; lcall _apm_addr"); 540 } 541 } 542 /* 543 * Some APM implementation halts CPU in BIOS, whenever 544 * "CPU-idle" function are invoked, but swtch() of 545 * FreeBSD halts CPU, therefore, CPU is halted twice 546 * in the sched loop. It makes the interrupt latency 547 * terribly long and be able to cause a serious problem 548 * in interrupt processing. We prevent it by removing 549 * "hlt" operation from swtch() and managed it under 550 * APM driver. 551 */ 552 /* 553 * UKAI Note: on NetBSD, idle() called from cpu_switch() 554 * doesn't halt CPU, so halt_cpu may not need on NetBSD/i386 555 * or only "sti" operation would be needed. 556 */ 557 558 if (!sc->active || sc->halt_cpu) { 559 __asm("sti ; hlt"); /* wait for interrupt */ 560 } 561} 562 563/* inform APM BIOS that CPU is busy */ 564void 565apm_cpu_busy(void) 566{ 567 struct apm_softc *sc = master_softc; /* XXX */ 568 569 if (sc->idle_cpu && sc->active) { 570 __asm("movw $0x5306, %ax; lcall _apm_addr"); 571 } 572} 573 574 575/* 576 * APM timeout routine: 577 * 578 * This routine is automatically called by timer once per second. 579 */ 580 581static void 582apm_timeout(void *arg) 583{ 584 struct apm_softc *sc = arg; 585 586 apm_processevent(sc); 587 timeout(apm_timeout, (void *)sc, hz - 1 ); /* More than 1 Hz */ 588} 589 590/* enable APM BIOS */ 591static void 592apm_event_enable(struct apm_softc *sc) 593{ 594#ifdef APM_DEBUG 595 printf("called apm_event_enable()\n"); 596#endif 597 if (sc->initialized) { 598 sc->active = 1; 599 apm_timeout(sc); 600 } 601} 602 603/* disable APM BIOS */ 604static void 605apm_event_disable(struct apm_softc *sc) 606{ 607#ifdef APM_DEBUG 608 printf("called apm_event_disable()\n"); 609#endif 610 if (sc->initialized) { 611 untimeout(apm_timeout, NULL); 612 sc->active = 0; 613 } 614} 615 616/* halt CPU in scheduling loop */ 617static void 618apm_halt_cpu(struct apm_softc *sc) 619{ 620 if (sc->initialized) { 621 sc->halt_cpu = 1; 622 } 623#ifdef APM_SLOWSTART 624 apm_slowstart = 0; 625#endif /* APM_SLOWSTART */ 626} 627 628/* don't halt CPU in scheduling loop */ 629static void 630apm_not_halt_cpu(struct apm_softc *sc) 631{ 632 if (sc->initialized) { 633 sc->halt_cpu = 0; 634 } 635#ifdef APM_SLOWSTART 636 apm_slowstart = apm_slowstart_p; 637#endif /* APM_SLOWSTART */ 638} 639 640/* device driver definitions */ 641#ifdef __FreeBSD__ 642int apmprobe (struct isa_device *); 643int apmattach(struct isa_device *); 644struct isa_driver apmdriver = { 645 apmprobe, apmattach, "apm" }; 646#endif /* __FreeBSD__ */ 647 648#ifdef MACH_KERNEL 649int apmprobe(vm_offset_t, struct bus_ctlr *); 650void apmattach(struct bus_device *); 651static struct bus_device *apminfo[NAPM]; 652static vm_offset_t apmstd[NAPM] = { 0 }; 653struct bus_driver apmdriver = { 654 apmprobe, 0, apmattach, 0, apmstd, "apm", apminfo, 0, 0, 0}; 655#endif /* MACH_KERNEL */ 656 657 658/* 659 * probe APM (dummy): 660 * 661 * APM probing routine is placed on locore.s and apm_init.S because 662 * this process forces the CPU to turn to real mode or V86 mode. 663 * Current version uses real mode, but on future version, we want 664 * to use V86 mode in APM initialization. 665 */ 666 667int 668#ifdef __FreeBSD__ 669 apmprobe(struct isa_device *dvp) 670#endif /* __FreeBSD__ */ 671#ifdef MACH_KERNEL 672 apmprobe(vm_offset_t port, struct bus_ctlr *devc) 673#endif /* MACH_KERNEL */ 674{ 675#ifdef __FreeBSD__ 676 int unit = dvp->id_unit; 677#endif /* __FreeBSD__ */ 678#ifdef MACH_KERNEL 679 int unit = devc->unit; 680#endif /* MACH_KERNEL */ 681 682 switch (apm_version) { 683 case APMINI_CANTFIND: 684 /* silent */ 685 return 0; 686 case APMINI_NOT32BIT: 687 printf("apm%d: 32bit connection is not supported.\n", unit); 688 return 0; 689 case APMINI_CONNECTERR: 690 printf("apm%d: 32-bit connection error.\n", unit); 691 return 0; 692 } 693#ifdef __FreeBSD__ 694#ifdef JREMOD 695 apm_devsw_install(); 696#endif /*JREMOD*/ 697#endif /* __FreeBSD__ */ 698 699 return -1; 700} 701 702 703/* Process APM event */ 704static void 705apm_processevent(struct apm_softc *sc) 706{ 707 int apm_event; 708 709#ifdef APM_DEBUG 710# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \ 711 printf("Original APM Event: " #symbol "\n"); 712#else 713# define OPMEV_DEBUGMESSAGE(symbol) case symbol: 714#endif 715 while (1) { 716 apm_event = apm_getevent(sc); 717 if (apm_event == PMEV_NOEVENT) 718 break; 719 switch (apm_event) { 720 OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 721 apm_suspend(); 722 break; 723 OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 724 apm_suspend(); 725 break; 726 OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 727 apm_suspend(); 728 break; 729 OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 730 apm_suspend(); 731 break; 732 OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 733 apm_resume(); 734 break; 735 OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 736 apm_resume(); 737 break; 738 OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 739 apm_resume(); 740 break; 741 OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 742 apm_battery_low(sc); 743 apm_suspend(); 744 break; 745 746 OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 747 break; 748 749 OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 750 inittodr(0); /* adjust time to RTC */ 751 break; 752 753 default: 754 printf("Unknown Original APM Event 0x%x\n", apm_event); 755 break; 756 } 757 } 758} 759 760/* 761 * Attach APM: 762 * 763 * Initialize APM driver (APM BIOS itself has been initialized in locore.s) 764 * 765 * Now, unless I'm mad, (not quite ruled out yet), the APM-1.1 spec is bogus: 766 * 767 * Appendix C says under the header "APM 1.0/APM 1.1 Modal BIOS Behavior" 768 * that "When an APM Driver connects with an APM 1.1 BIOS, the APM 1.1 BIOS 769 * will default to an APM 1.0 connection. After an APM Driver calls the APM 770 * Driver Version function, specifying that it supports APM 1.1, and [sic!] 771 * APM BIOS will change its behavior to an APM 1.1 connection. If the APM 772 * BIOS is an APM 1.0 BIOS, the APM Driver Version function call will fail, 773 * and the connection will remain an APM 1.0 connection." 774 * 775 * OK so I can establish a 1.0 connection, and then tell that I'm a 1.1 776 * and maybe then the BIOS will tell that it too is a 1.1. 777 * Fine. 778 * Now how will I ever get the segment-limits for instance ? There is no 779 * way I can see that I can get a 1.1 response back from an "APM Protected 780 * Mode 32-bit Interface Connect" function ??? 781 * 782 * Who made this, Intel and Microsoft ? -- How did you guess ! 783 * 784 * /phk 785 */ 786 787#ifdef __FreeBSD__ 788int 789apmattach(struct isa_device *dvp) 790#endif /* __FreeBSD__ */ 791#ifdef MACH_KERNEL 792void 793apmattach(struct bus_device *dvp) 794#endif /* MACH_KERNEL */ 795{ 796#ifdef __FreeBSD__ 797 int unit = dvp->id_unit; 798#define APM_KERNBASE KERNBASE 799#endif /* __FreeBSD__ */ 800#ifdef MACH_KERNEL 801 int unit = dvp->unit; 802#define APM_KERNBASE VM_MIN_KERNEL_ADDRESS 803#endif /* MACH_KERNEL */ 804 struct apm_softc *sc = &apm_softc[unit]; 805 806 master_softc = sc; /* XXX */ 807 sc->initialized = 0; 808 sc->active = 0; 809 sc->halt_cpu = 1; 810 811 /* setup APM parameters */ 812 sc->cs16_base = (apm_cs32_base << 4) + APM_KERNBASE; 813 sc->cs32_base = (apm_cs16_base << 4) + APM_KERNBASE; 814 sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE; 815 sc->cs_limit = apm_cs_limit; 816 sc->ds_limit = apm_ds_limit; 817 sc->cs_entry = apm_cs_entry; 818 819 sc->idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); 820 sc->disabled = ((apm_flags & APM_DISABLED) != 0); 821 sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0); 822 823#ifdef APM_SLOWSTART 824 if (sc->idle_cpu) { 825 apm_slowstart = apm_slowstart_p = 1; 826 } 827#endif 828 829 /* print bootstrap messages */ 830#ifdef APM_DEBUG 831 printf(" found APM BIOS version %04x\n", apm_version); 832 printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", 833 unit, sc->cs32_base, sc->cs16_base, sc->ds_base); 834 printf("apm%d: Code entry 0x%08x, Idling CPU %s, Management %s\n", 835 unit, sc->cs_entry, is_enabled(sc->idle_cpu), 836 is_enabled(!sc->disabled)); 837 printf("apm%d: CS_limit=%x, DS_limit=%x\n", 838 unit, sc->cs_limit, sc->ds_limit); 839#endif /* APM_DEBUG */ 840 841 sc->cs_limit = 0xffff; 842 sc->ds_limit = 0xffff; 843 844 /* setup GDT */ 845 setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base, 846 sc->cs_limit, sc->ds_limit); 847 848 /* setup entry point 48bit pointer */ 849#ifdef __FreeBSD__ 850 apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); 851#endif /* __FreeBSD__ */ 852#ifdef MACH_KERNEL 853 apm_addr.segment = GAPMCODE32_SEL; 854#endif /* MACH_KERNEL */ 855 apm_addr.offset = sc->cs_entry; 856 857 /* Try to kick bios into 1.1 mode */ 858 apm_driver_version(); 859 sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + 860 ((apm_version & 0x000f) >> 0); 861 sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + 862 ((apm_version & 0x0f00) >> 8); 863 864 sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); 865 866 if (sc->intversion >= INTVERSION(1, 1)) { 867 printf("apm%d: Engaged control %s\n", 868 unit, is_enabled(!sc->disengaged)); 869 } 870 871 printf(" found APM BIOS version %d.%d\n", 872 sc->majorversion, sc->minorversion); 873 printf("apm%d: Idling CPU %s\n", unit, is_enabled(sc->idle_cpu)); 874 875 /* enable power management */ 876 if (sc->disabled) { 877 if (apm_enable_disable_pm(sc, 1)) { 878 printf("Warning: APM enable function failed! [%x]\n", 879 apm_errno); 880 } 881 } 882 883 /* engage power managment (APM 1.1 or later) */ 884 if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { 885 if (apm_engage_disengage_pm(sc, 1)) { 886 printf("Warning: APM engage function failed [%x]\n", 887 apm_errno); 888 } 889 } 890 891 /* default suspend hook */ 892 sc->sc_suspend.ah_fun = apm_default_suspend; 893 sc->sc_suspend.ah_arg = sc; 894 sc->sc_suspend.ah_name = "default suspend"; 895 sc->sc_suspend.ah_order = APM_MAX_ORDER; 896 897 /* default resume hook */ 898 sc->sc_resume.ah_fun = apm_default_resume; 899 sc->sc_resume.ah_arg = sc; 900 sc->sc_resume.ah_name = "default resume"; 901 sc->sc_resume.ah_order = APM_MIN_ORDER; 902 903 apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); 904 apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); 905 906 apm_event_enable(sc); 907 908 sc->initialized = 1; 909 910#ifdef __FreeBSD__ 911 return 0; 912#endif /* __FreeBSD__ */ 913} 914 915#ifdef __FreeBSD__ 916int 917apmopen(dev_t dev, int flag, int fmt, struct proc *p) 918{ 919 struct apm_softc *sc = &apm_softc[minor(dev)]; 920 921 if (minor(dev) >= NAPM) { 922 return (ENXIO); 923 } 924 if (!sc->initialized) { 925 return ENXIO; 926 } 927 return 0; 928} 929 930int 931apmclose(dev_t dev, int flag, int fmt, struct proc *p) 932{ 933 return 0; 934} 935 936int 937apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) 938{ 939 struct apm_softc *sc = &apm_softc[minor(dev)]; 940 int error = 0; 941 942#ifdef APM_DEBUG 943 printf("APM ioctl: minor = %d, cmd = 0x%x\n", minor(dev), cmd); 944#endif 945 946 if (minor(dev) >= NAPM) { 947 return ENXIO; 948 } 949 if (!sc->initialized) { 950 return ENXIO; 951 } 952 switch (cmd) { 953 case APMIO_SUSPEND: 954 apm_suspend(); 955 break; 956 case APMIO_GETINFO: 957 if (apm_get_info(sc, (apm_info_t)addr)) { 958 error = ENXIO; 959 } 960 break; 961 case APMIO_ENABLE: 962 apm_event_enable(sc); 963 break; 964 case APMIO_DISABLE: 965 apm_event_disable(sc); 966 break; 967 case APMIO_HALTCPU: 968 apm_halt_cpu(sc); 969 break; 970 case APMIO_NOTHALTCPU: 971 apm_not_halt_cpu(sc); 972 break; 973 case APMIO_DISPLAYOFF: 974 if (apm_display_off()) { 975 error = ENXIO; 976 } 977 break; 978 default: 979 error = EINVAL; 980 break; 981 } 982 return error; 983} 984#ifdef JREMOD 985struct cdevsw apm_cdevsw = 986 { apmopen, apmclose, noread, nowrite, /*39*/ 987 apmioctl, nostop, nullreset, nodevtotty,/* APM */ 988 seltrue, nommap, NULL }; 989 990static apm_devsw_installed = 0; 991 992static void apm_devsw_install() 993{ 994 dev_t descript; 995 if( ! apm_devsw_installed ) { 996 descript = makedev(CDEV_MAJOR,0) 997 cdevsw_add(&descript,&apm_cdevsw,NULL); 998#if defined(BDEV_MAJOR) 999 descript = makedev(BDEV_MAJOR,0) 1000 bdevsw_add(&descript,&apm_bdevsw,NULL); 1001#endif /*BDEV_MAJOR*/ 1002 apm_devsw_installed = 1; 1003 } 1004} 1005#endif /* JREMOD */ 1006#endif /* __FreeBSD__ */ 1007 1008#ifdef MACH_KERNEL 1009io_return_t apmopen(dev_t dev, int flag, io_req_t ior) 1010{ 1011 int result; 1012 1013 result = ENXIO; 1014 return result; 1015} 1016 1017io_return_t apmclose(dev_t dev, int flag) 1018{ 1019 return 0; 1020} 1021 1022io_return_t apmgetstat(dev_t dev, int flavor, int *data, u_int *count) 1023{ 1024} 1025 1026io_return_t apmsetstat(dev_t dev, int flavor, int *data, u_int count) 1027{ 1028} 1029#endif /* MACH_KERNEL */ 1030 1031#endif /* NAPM > 0 */ 1032