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