apm.c revision 13062
151974Smsmith/* 265245Smsmith * APM (Advanced Power Management) BIOS Device Driver 365245Smsmith * 4140687Sscottl * Copyright (c) 1994 UKAI, Fumitoshi. 551974Smsmith * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> 651974Smsmith * 751974Smsmith * This software may be used, modified, copied, and distributed, in 851974Smsmith * both source and binary form provided that the above copyright and 951974Smsmith * these terms are retained. Under no circumstances is the author 1051974Smsmith * responsible for the proper functioning of this software, nor does 1151974Smsmith * the author assume any responsibility for damages incurred with its 1251974Smsmith * use. 1351974Smsmith * 1451974Smsmith * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) 1551974Smsmith * 1651974Smsmith * $Id: apm.c,v 1.22 1995/12/25 07:38:26 bde Exp $ 1751974Smsmith */ 1851974Smsmith 1951974Smsmith#include "apm.h" 2051974Smsmith 2151974Smsmith#include <sys/param.h> 2251974Smsmith#include <conf.h> 2351974Smsmith#include <sys/conf.h> 2451974Smsmith#include <sys/kernel.h> 2551974Smsmith#ifdef DEVFS 2651974Smsmith#include <sys/devfsext.h> 27119418Sobrien#endif /*DEVFS*/ 28139749Simp#include <sys/kernel.h> 29106225Semoore#include <sys/systm.h> 30140688Sscottl#include <sys/malloc.h> 31106225Semoore#include <sys/ioctl.h> 32106225Semoore#include <sys/file.h> 33106225Semoore#include <sys/proc.h> 34106225Semoore#include <sys/vnode.h> 35106225Semoore#include "i386/isa/isa.h" 36106225Semoore#include "i386/isa/isa_device.h" 37106225Semoore#include <machine/apm_bios.h> 38106225Semoore#include <machine/segments.h> 39106225Semoore#include <machine/clock.h> 40106225Semoore#include <vm/vm.h> 41105419Semoore#include <vm/vm_param.h> 42106225Semoore#include <vm/pmap.h> 43105419Semoore#include <sys/syslog.h> 44105419Semoore#include "apm_setup.h" 45106225Semoore 46106225Semoorestatic int apm_display_off __P((void)); 47106225Semoorestatic int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx)); 48106225Semoorestatic void apm_resume __P((void)); 49106225Semoore 50106225Semoore/* static data */ 51106225Semoorestruct apm_softc { 52106225Semoore int initialized, active, halt_cpu; 53106225Semoore u_int minorversion, majorversion; 54106225Semoore u_int cs32_base, cs16_base, ds_base; 55106225Semoore u_int cs_limit, ds_limit; 5651974Smsmith u_int cs_entry; 5751974Smsmith u_int intversion; 58119418Sobrien int idle_cpu, disabled, disengaged; 59119418Sobrien struct apmhook sc_suspend; 60119418Sobrien struct apmhook sc_resume; 6151974Smsmith void *sc_devfs_token; 6265245Smsmith}; 6351974Smsmith 6451974Smsmithstatic struct apm_softc apm_softc[NAPM]; 6551974Smsmithstatic struct apm_softc *master_softc = NULL; /* XXX */ 6651974Smsmithstatic struct apmhook *hook[NAPM_HOOK]; /* XXX */ 6751974Smsmith#ifdef APM_SLOWSTART 6851974Smsmithint apm_slowstart = 0; 6951974Smsmithint apm_ss_cnt = 0; 70148850Sscottlstatic int apm_slowstart_p = 0; 7151974Smsmithint apm_slowstart_stat = 0; 7251974Smsmith#endif /* APM_SLOWSTART */ 7365245Smsmith 7451974Smsmith#define is_enabled(foo) ((foo) ? "enabled" : "disabled") 7565245Smsmith 7651974Smsmith/* Map version number to integer (keeps ordering of version numbers) */ 7751974Smsmith#define INTVERSION(major, minor) ((major)*100 + (minor)) 7851974Smsmith 79119277Simpstatic timeout_t apm_timeout; 80119277Simpstatic d_open_t apmopen; 8165245Smsmithstatic d_close_t apmclose; 8251974Smsmithstatic d_ioctl_t apmioctl; 8351974Smsmith 8451974Smsmith#define CDEV_MAJOR 39 8565245Smsmithstatic struct cdevsw apm_cdevsw = 8665245Smsmith { apmopen, apmclose, noread, nowrite, /*39*/ 8751974Smsmith apmioctl, nostop, nullreset, nodevtotty,/* APM */ 8865245Smsmith seltrue, nommap, NULL , "apm" ,NULL, -1}; 8965245Smsmith 9065245Smsmith/* setup APM GDT discriptors */ 9165245Smsmithstatic void 9251974Smsmithsetup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code_limit, u_int data_limit) 93126080Sphk{ 94126080Sphk /* setup 32bit code segment */ 95111815Sphk gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; 96111815Sphk gdt_segs[GAPMCODE32_SEL].ssd_limit = code_limit; 97111815Sphk 98111815Sphk /* setup 16bit code segment */ 9951974Smsmith gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; 10051974Smsmith gdt_segs[GAPMCODE16_SEL].ssd_limit = code_limit; 10165245Smsmith 10265245Smsmith /* setup data segment */ 10365245Smsmith gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; 10465245Smsmith gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; 10551974Smsmith 10651974Smsmith /* reflect these changes on physical GDT */ 10751974Smsmith ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd); 10851974Smsmith ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd); 10965245Smsmith ssdtosd(gdt_segs + GAPMDATA_SEL , &gdt[GAPMDATA_SEL ].sd); 11065245Smsmith} 11165245Smsmith 11265245Smsmith/* 48bit far pointer */ 113106225Semoorestatic struct addr48 { 11451974Smsmith u_long offset; 11551974Smsmith u_short segment; 11665245Smsmith} apm_addr; 11751974Smsmith 11865245Smsmithstatic int apm_errno; 11965245Smsmith 12051974Smsmithinline 12151974Smsmithint 12265245Smsmithapm_int(u_long *eax, u_long *ebx, u_long *ecx) 12351974Smsmith{ 12465245Smsmith u_long cf; 125138422Sscottl __asm ("pushl %%ebp 12665245Smsmith pushl %%edx 127138422Sscottl pushl %%esi 12865245Smsmith xorl %3,%3 12965245Smsmith movl %3,%%esi 130138422Sscottl lcall _apm_addr 13165245Smsmith jnc 1f 132138422Sscottl incl %3 133138422Sscottl 1: 13451974Smsmith popl %%esi 13551974Smsmith popl %%edx 13658883Smsmith popl %%ebp" 13758883Smsmith : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=D" (cf) 13865245Smsmith : "0" (*eax), "1" (*ebx), "2" (*ecx) 13958883Smsmith ); 14058883Smsmith apm_errno = ((*eax) >> 8) & 0xff; 14151974Smsmith return cf; 14251974Smsmith} 14365245Smsmith 14465245Smsmith 145107756Semoore/* enable/disable power management */ 146138422Sscottlstatic int 14751974Smsmithapm_enable_disable_pm(struct apm_softc *sc, int enable) 14865245Smsmith{ 14965245Smsmith u_long eax, ebx, ecx; 150107756Semoore 15165245Smsmith eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM; 15251974Smsmith 15365245Smsmith if (sc->intversion >= INTVERSION(1, 1)) { 15465245Smsmith ebx = PMDV_ALLDEV; 15565245Smsmith } else { 15665245Smsmith ebx = 0xffff; /* APM version 1.0 only */ 15765245Smsmith } 15851974Smsmith ecx = enable; 15951974Smsmith return apm_int(&eax, &ebx, &ecx); 16051974Smsmith} 16165245Smsmith 16265245Smsmith/* Tell APM-BIOS that WE will do 1.1 and see what they say... */ 163107756Semoorestatic void 16465245Smsmithapm_driver_version(void) 16565245Smsmith{ 166107756Semoore u_long eax, ebx, ecx; 16751974Smsmith 16851974Smsmith#ifdef APM_DEBUG 16951974Smsmith eax = (APM_BIOS<<8) | APM_INSTCHECK; 17065245Smsmith ebx = 0x0; 17151974Smsmith ecx = 0x0101; 17251974Smsmith i = apm_int(&eax, &ebx, &ecx); 17351974Smsmith printf("[%04lx %04lx %04lx %ld %02x]\n", 17451974Smsmith eax, ebx, ecx, i, apm_errno); 17565245Smsmith#endif 17665245Smsmith 17765245Smsmith eax = (APM_BIOS << 8) | APM_DRVVERSION; 17865245Smsmith ebx = 0x0; 17951974Smsmith ecx = 0x0101; 18051974Smsmith if(!apm_int(&eax, &ebx, &ecx)) 18151974Smsmith apm_version = eax & 0xffff; 18251974Smsmith 18351974Smsmith#ifdef APM_DEBUG 18451974Smsmith eax = (APM_BIOS << 8) | APM_INSTCHECK; 18551974Smsmith ebx = 0x0; 18651974Smsmith ecx = 0x0101; 18765245Smsmith i = apm_int(&eax, &ebx, &ecx); 18865245Smsmith printf("[%04lx %04lx %04lx %ld %02x]\n", 18951974Smsmith eax, ebx, ecx, i, apm_errno); 19051974Smsmith#endif 19151974Smsmith} 19265245Smsmith 19351974Smsmith/* engage/disengage power management (APM 1.1 or later) */ 19465245Smsmithstatic int 19565245Smsmithapm_engage_disengage_pm(struct apm_softc *sc, int engage) 19659249Sphk{ 19751974Smsmith u_long eax, ebx, ecx, i; 19865245Smsmith 19965245Smsmith eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; 20065245Smsmith ebx = PMDV_ALLDEV; 20151974Smsmith ecx = engage; 20251974Smsmith i = apm_int(&eax, &ebx, &ecx); 20365245Smsmith return i; 20451974Smsmith} 20551974Smsmith 206107756Semoore/* get PM event */ 207138422Sscottlstatic u_int 20851974Smsmithapm_getevent(struct apm_softc *sc) 20951974Smsmith{ 21051974Smsmith u_long eax, ebx, ecx; 211107756Semoore 21265245Smsmith eax = (APM_BIOS << 8) | APM_GETPMEVENT; 21351974Smsmith 21451974Smsmith ebx = 0; 21565245Smsmith ecx = 0; 21665245Smsmith if (apm_int(&eax, &ebx, &ecx)) 21765245Smsmith return PMEV_NOEVENT; 21865245Smsmith 21951974Smsmith return ebx & 0xffff; 22051974Smsmith} 22165245Smsmith 22251974Smsmith/* suspend entire system */ 22365245Smsmithstatic int 22465245Smsmithapm_suspend_system(struct apm_softc *sc) 22551974Smsmith{ 22665245Smsmith u_long eax, ebx, ecx; 22751974Smsmith 22851974Smsmith eax = (APM_BIOS << 8) | APM_SETPWSTATE; 22965245Smsmith ebx = PMDV_ALLDEV; 23051974Smsmith ecx = PMST_SUSPEND; 23165245Smsmith 23251974Smsmith __asm("cli"); 23365245Smsmith if (apm_int(&eax, &ebx, &ecx)) { 23451974Smsmith __asm("sti"); 23551974Smsmith printf("Entire system suspend failure: errcode = %ld\n", 23665245Smsmith 0xff & (eax >> 8)); 23751974Smsmith return 1; 23865245Smsmith } 23965245Smsmith __asm("sti"); 24065245Smsmith return 0; 24151974Smsmith} 24251974Smsmith 24365245Smsmith/* Display control */ 24465245Smsmith/* 24551974Smsmith * Experimental implementation: My laptop machine can't handle this function 24665245Smsmith * If your laptop can control the display via APM, please inform me. 24765245Smsmith * HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> 24865245Smsmith */ 24965245Smsmithstatic int 25065245Smsmithapm_display_off(void) 25165245Smsmith{ 25265245Smsmith u_long eax, ebx, ecx; 25351974Smsmith 25458883Smsmith eax = (APM_BIOS << 8) | APM_SETPWSTATE; 25565245Smsmith ebx = PMDV_2NDSTORAGE0; 25658883Smsmith ecx = PMST_STANDBY; 25765245Smsmith if (apm_int(&eax, &ebx, &ecx)) { 25858883Smsmith printf("Display off failure: errcode = %ld\n", 25965245Smsmith 0xff & (eax >> 8)); 26051974Smsmith return 1; 26151974Smsmith } 26251974Smsmith 26351974Smsmith return 0; 26451974Smsmith} 26551974Smsmith 26665245Smsmith/* APM Battery low handler */ 26765245Smsmithstatic void 26851974Smsmithapm_battery_low(struct apm_softc *sc) 26965245Smsmith{ 27051974Smsmith printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); 27151974Smsmith} 27251974Smsmith 27365245Smsmith/* APM hook manager */ 27451974Smsmithstatic struct apmhook * 27565245Smsmithapm_add_hook(struct apmhook **list, struct apmhook *ah) 27665245Smsmith{ 27765245Smsmith int s; 27851974Smsmith struct apmhook *p, *prev; 27951974Smsmith 28065245Smsmith#if 0 28151974Smsmith printf("Add hook \"%s\"\n", ah->ah_name); 28251974Smsmith#endif 28351974Smsmith 28451974Smsmith s = splhigh(); 28551974Smsmith if (ah == NULL) { 28651974Smsmith panic("illegal apm_hook!"); 28751974Smsmith } 28851974Smsmith prev = NULL; 28951974Smsmith for (p = *list; p != NULL; prev = p, p = p->ah_next) { 29051974Smsmith if (p->ah_order > ah->ah_order) { 29151974Smsmith break; 29251974Smsmith } 29351974Smsmith } 29451974Smsmith 29551974Smsmith if (prev == NULL) { 29651974Smsmith ah->ah_next = *list; 29751974Smsmith *list = ah; 29854073Smdodd } else { 29951974Smsmith ah->ah_next = prev->ah_next; 30051974Smsmith prev->ah_next = ah; 30154073Smdodd } 30251974Smsmith splx(s); 30351974Smsmith return ah; 30451974Smsmith} 30551974Smsmith 30651974Smsmithstatic void 30751974Smsmithapm_del_hook(struct apmhook **list, struct apmhook *ah) 30851974Smsmith{ 30951974Smsmith int s; 31051974Smsmith struct apmhook *p, *prev; 31151974Smsmith 31251974Smsmith s = splhigh(); 31351974Smsmith prev = NULL; 31451974Smsmith for (p = *list; p != NULL; prev = p, p = p->ah_next) { 31565245Smsmith if (p == ah) { 31651974Smsmith goto deleteit; 31765245Smsmith } 31851974Smsmith } 31965245Smsmith panic("Tried to delete unregistered apm_hook."); 32051974Smsmith goto nosuchnode; 32151974Smsmithdeleteit: 32265245Smsmith if (prev != NULL) { 32365245Smsmith prev->ah_next = p->ah_next; 32451974Smsmith } else { 32565245Smsmith *list = p->ah_next; 32665245Smsmith } 32751974Smsmithnosuchnode: 32865245Smsmith splx(s); 32951974Smsmith} 33065245Smsmith 331107756Semoore 33251974Smsmith/* APM driver calls some functions automatically */ 33365245Smsmithstatic void 33465245Smsmithapm_execute_hook(struct apmhook *list) 33551974Smsmith{ 33665245Smsmith struct apmhook *p; 33765245Smsmith 33865245Smsmith for (p = list; p != NULL; p = p->ah_next) { 33965245Smsmith#if 0 34051974Smsmith printf("Execute APM hook \"%s.\"\n", p->ah_name); 341107756Semoore#endif 342107756Semoore if ((*(p->ah_fun))(p->ah_arg)) { 343130585Sphk printf("Warning: APM hook \"%s\" failed", p->ah_name); 344107756Semoore } 345140340Sscottl } 346140340Sscottl} 347140340Sscottl 34851974Smsmith 34951974Smsmith/* establish an apm hook */ 35051974Smsmithstruct apmhook * 35165245Smsmithapm_hook_establish(int apmh, struct apmhook *ah) 35251974Smsmith{ 35351974Smsmith if (apmh < 0 || apmh >= NAPM_HOOK) 35451974Smsmith return NULL; 35565245Smsmith 35651974Smsmith return apm_add_hook(&hook[apmh], ah); 35765245Smsmith} 35852543Smsmith 359140340Sscottl#ifdef notused 36065245Smsmith/* disestablish an apm hook */ 36151974Smsmithvoid 362140340Sscottlapm_hook_disestablish(int apmh, struct apmhook *ah) 36351974Smsmith{ 36451974Smsmith if (apmh < 0 || apmh >= NAPM_HOOK) 36551974Smsmith return; 36651974Smsmith 36751974Smsmith apm_del_hook(&hook[apmh], ah); 36851974Smsmith} 369104094Sphk#endif /* notused */ 370130585Sphk 37151974Smsmith 37251974Smsmithstatic struct timeval suspend_time; 37389055Smsmithstatic struct timeval diff_time; 37451974Smsmith 37565245Smsmithstatic int 37665245Smsmithapm_default_resume(void *arg) 37751974Smsmith{ 37851974Smsmith int pl; 37951974Smsmith u_int second, minute, hour; 38051974Smsmith struct timeval resume_time, tmp_time; 38151974Smsmith 38251974Smsmith /* modified for adjkerntz */ 38351974Smsmith pl = splsoftclock(); 384104094Sphk inittodr(0); /* adjust time to RTC */ 385130585Sphk microtime(&resume_time); 38651974Smsmith tmp_time = time; /* because 'time' is volatile */ 38751974Smsmith timevaladd(&tmp_time, &diff_time); 38889055Smsmith time = tmp_time; 38951974Smsmith splx(pl); 39065245Smsmith second = resume_time.tv_sec - suspend_time.tv_sec; 39165245Smsmith hour = second / 3600; 39251974Smsmith second %= 3600; 39351974Smsmith minute = second / 60; 39451974Smsmith second %= 60; 39551974Smsmith log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", 39651974Smsmith hour, minute, second); 39751974Smsmith return 0; 39851974Smsmith} 399104094Sphk 400130585Sphkstatic int 40151974Smsmithapm_default_suspend(void *arg) 40265245Smsmith{ 403133870Sambrisko int pl; 404133870Sambrisko 405133870Sambrisko pl = splsoftclock(); 406133870Sambrisko microtime(&diff_time); 407133870Sambrisko inittodr(0); 408133870Sambrisko microtime(&suspend_time); 409133870Sambrisko timevalsub(&diff_time, &suspend_time); 410133870Sambrisko splx(pl); 41165245Smsmith return 0; 41265245Smsmith} 413133870Sambrisko 414133870Sambriskostatic void apm_processevent(struct apm_softc *); 415133870Sambrisko 416133870Sambrisko/* 41765245Smsmith * Public interface to the suspend/resume: 418143121Sscottl * 41965245Smsmith * Execute suspend and resume hook before and after sleep, respectively. 42065245Smsmith * 42165245Smsmith */ 422133870Sambrisko 423133870Sambriskovoid 42451974Smsmithapm_suspend(void) 42565245Smsmith{ 42665245Smsmith struct apm_softc *sc; 42765245Smsmith 428133870Sambrisko sc = master_softc; /* XXX */ 429133870Sambrisko if (!sc) 430133870Sambrisko return; 431133870Sambrisko 432133870Sambrisko if (sc->initialized) { 433133870Sambrisko apm_execute_hook(hook[APM_HOOK_SUSPEND]); 434133870Sambrisko apm_suspend_system(sc); 435133870Sambrisko apm_processevent(sc); 436133870Sambrisko } 437133870Sambrisko} 438133870Sambrisko 439133870Sambriskovoid 440133870Sambriskoapm_resume(void) 441133870Sambrisko{ 442133870Sambrisko struct apm_softc *sc; 443133870Sambrisko 44465245Smsmith sc = master_softc; /* XXX */ 445133870Sambrisko if (!sc) 44665245Smsmith return; 44765245Smsmith 448133870Sambrisko if (sc->initialized) { 449133870Sambrisko apm_execute_hook(hook[APM_HOOK_RESUME]); 450133870Sambrisko } 451133870Sambrisko} 452133870Sambrisko 453133870Sambrisko 454133870Sambrisko/* get APM information */ 45565245Smsmithstatic int 456133870Sambriskoapm_get_info(struct apm_softc *sc, apm_info_t aip) 457133870Sambrisko{ 458133870Sambrisko u_long eax, ebx, ecx; 459133870Sambrisko 46065245Smsmith eax = (APM_BIOS << 8) | APM_GETPWSTATUS; 461133870Sambrisko ebx = PMDV_ALLDEV; 462133870Sambrisko ecx = 0; 463133870Sambrisko 464143121Sscottl if (apm_int(&eax, &ebx, &ecx)) 46565245Smsmith return 1; 466140688Sscottl 467140688Sscottl aip->ai_acline = (ebx >> 8) & 0xff; 468140688Sscottl aip->ai_batt_stat = ebx & 0xff; 469140688Sscottl aip->ai_batt_life = ecx & 0xff; 470133870Sambrisko aip->ai_major = (u_int)sc->majorversion; 471140688Sscottl aip->ai_minor = (u_int)sc->minorversion; 472143488Sscottl return 0; 47365245Smsmith} 474140340Sscottl 475140340Sscottl 476140340Sscottl#ifdef APM_SLOWSTART 477140340Sscottl/* inform APM BIOS that CPU is idle */ 478133870Sambriskovoid 479133870Sambriskoapm_cpu_idle(void) 48065245Smsmith{ 481143488Sscottl struct apm_softc *sc = master_softc; /* XXX */ 482143488Sscottl 483143488Sscottl if (sc->idle_cpu) { 484143488Sscottl if (sc->active) { 485140340Sscottl __asm ("movw $0x5305, %ax; lcall _apm_addr"); 486133870Sambrisko } 487133870Sambrisko } 488133870Sambrisko /* 489133870Sambrisko * Some APM implementation halts CPU in BIOS, whenever 49065245Smsmith * "CPU-idle" function are invoked, but swtch() of 491133870Sambrisko * FreeBSD halts CPU, therefore, CPU is halted twice 492133870Sambrisko * in the sched loop. It makes the interrupt latency 493140340Sscottl * terribly long and be able to cause a serious problem 49465245Smsmith * in interrupt processing. We prevent it by removing 495133870Sambrisko * "hlt" operation from swtch() and managed it under 496140340Sscottl * APM driver. 497143121Sscottl */ 498143121Sscottl /* 49965245Smsmith * UKAI Note: on NetBSD, idle() called from cpu_switch() 500133870Sambrisko * doesn't halt CPU, so halt_cpu may not need on NetBSD/i386 501143121Sscottl * or only "sti" operation would be needed. 502143121Sscottl */ 503143121Sscottl 504143121Sscottl if (!sc->active || sc->halt_cpu) { 505143121Sscottl __asm("sti ; hlt"); /* wait for interrupt */ 506143121Sscottl } 507143121Sscottl} 508143121Sscottl#endif /* APM_SLOWSTART */ 509133870Sambrisko 51065245Smsmith#if APM_SLOWSTART > 0 511133870Sambrisko/* inform APM BIOS that CPU is busy */ 512143121Sscottlvoid 513140340Sscottlapm_cpu_busy(void) 514133870Sambrisko{ 515133870Sambrisko struct apm_softc *sc = master_softc; /* XXX */ 516133870Sambrisko 517133870Sambrisko if (sc->idle_cpu && sc->active) { 518133870Sambrisko __asm("movw $0x5306, %ax; lcall _apm_addr"); 519133870Sambrisko } 520133870Sambrisko} 52165245Smsmith#endif /* APM_SLOWSTART > 0 */ 522133870Sambrisko 52365245Smsmith 524133870Sambrisko/* 525133870Sambrisko * APM timeout routine: 526133870Sambrisko * 527105419Semoore * This routine is automatically called by timer once per second. 528133870Sambrisko */ 529133870Sambrisko 530133870Sambriskostatic void 531133870Sambriskoapm_timeout(void *arg) 532133870Sambrisko{ 533133870Sambrisko struct apm_softc *sc = arg; 534133870Sambrisko 535133870Sambrisko apm_processevent(sc); 536133870Sambrisko timeout(apm_timeout, (void *)sc, hz - 1 ); /* More than 1 Hz */ 537133870Sambrisko} 538133870Sambrisko 539133870Sambrisko/* enable APM BIOS */ 540133870Sambriskostatic void 541133870Sambriskoapm_event_enable(struct apm_softc *sc) 54251974Smsmith{ 54351974Smsmith#ifdef APM_DEBUG 544133870Sambrisko printf("called apm_event_enable()\n"); 545133870Sambrisko#endif 546133870Sambrisko if (sc->initialized) { 547133870Sambrisko sc->active = 1; 548133870Sambrisko apm_timeout(sc); 549140340Sscottl } 550140340Sscottl} 551133870Sambrisko 552140340Sscottl/* disable APM BIOS */ 553140340Sscottlstatic void 554133870Sambriskoapm_event_disable(struct apm_softc *sc) 55565245Smsmith{ 556133870Sambrisko#ifdef APM_DEBUG 557133870Sambrisko printf("called apm_event_disable()\n"); 558133870Sambrisko#endif 559133870Sambrisko if (sc->initialized) { 560140340Sscottl untimeout(apm_timeout, NULL); 561140340Sscottl sc->active = 0; 562140340Sscottl } 563140340Sscottl} 564143488Sscottl 565143488Sscottl/* halt CPU in scheduling loop */ 566140340Sscottlstatic void 567143488Sscottlapm_halt_cpu(struct apm_softc *sc) 568143488Sscottl{ 569143488Sscottl if (sc->initialized) { 570143488Sscottl sc->halt_cpu = 1; 57165245Smsmith } 57251974Smsmith#ifdef APM_SLOWSTART 57351974Smsmith apm_slowstart = 0; 57451974Smsmith#endif /* APM_SLOWSTART */ 57551974Smsmith} 57658883Smsmith 57758883Smsmith/* don't halt CPU in scheduling loop */ 57858883Smsmithstatic void 57958883Smsmithapm_not_halt_cpu(struct apm_softc *sc) 58058883Smsmith{ 58158883Smsmith if (sc->initialized) { 58258883Smsmith sc->halt_cpu = 0; 58358883Smsmith } 58458883Smsmith#ifdef APM_SLOWSTART 58558883Smsmith apm_slowstart = apm_slowstart_p; 58658883Smsmith#endif /* APM_SLOWSTART */ 58758883Smsmith} 58865245Smsmith 58958883Smsmith/* device driver definitions */ 59065245Smsmithstatic int apmprobe (struct isa_device *); 59158883Smsmithstatic int apmattach(struct isa_device *); 59265245Smsmithstruct isa_driver apmdriver = { 59365245Smsmith apmprobe, apmattach, "apm" }; 59465245Smsmith 59558883Smsmith/* 59658883Smsmith * probe APM (dummy): 59758883Smsmith * 59858883Smsmith * APM probing routine is placed on locore.s and apm_init.S because 59958883Smsmith * this process forces the CPU to turn to real mode or V86 mode. 60058883Smsmith * Current version uses real mode, but on future version, we want 60151974Smsmith * to use V86 mode in APM initialization. 60251974Smsmith */ 60351974Smsmith 60451974Smsmithstatic int 60551974Smsmithapmprobe(struct isa_device *dvp) 60651974Smsmith{ 60751974Smsmith int unit = dvp->id_unit; 60851974Smsmith 60951974Smsmith switch (apm_version) { 61051974Smsmith case APMINI_CANTFIND: 61165245Smsmith /* silent */ 61265245Smsmith return 0; 61365245Smsmith case APMINI_NOT32BIT: 61465245Smsmith printf("apm%d: 32bit connection is not supported.\n", unit); 61551974Smsmith return 0; 616140340Sscottl case APMINI_CONNECTERR: 617140340Sscottl printf("apm%d: 32-bit connection error.\n", unit); 61865245Smsmith return 0; 61965245Smsmith } 62065245Smsmith 62165245Smsmith return -1; 62265245Smsmith} 62365245Smsmith 62451974Smsmith 625106225Semoore/* Process APM event */ 626106225Semoorestatic void 627106225Semooreapm_processevent(struct apm_softc *sc) 628106225Semoore{ 629106225Semoore int apm_event; 630106225Semoore 631106225Semoore#ifdef APM_DEBUG 632106225Semoore# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \ 633106225Semoore printf("Original APM Event: " #symbol "\n"); 63465245Smsmith#else 63565245Smsmith# define OPMEV_DEBUGMESSAGE(symbol) case symbol: 63665245Smsmith#endif 63765245Smsmith while (1) { 63865245Smsmith apm_event = apm_getevent(sc); 63951974Smsmith if (apm_event == PMEV_NOEVENT) 64065245Smsmith break; 64165245Smsmith switch (apm_event) { 64265245Smsmith OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 64365245Smsmith apm_suspend(); 64465245Smsmith break; 64565245Smsmith OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 64665245Smsmith apm_suspend(); 64765245Smsmith break; 64865245Smsmith OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 64951974Smsmith apm_suspend(); 65065245Smsmith break; 65158883Smsmith OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 65265245Smsmith apm_suspend(); 65365245Smsmith break; 65458883Smsmith OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 65565245Smsmith apm_resume(); 65665245Smsmith break; 657140340Sscottl OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 65865245Smsmith apm_resume(); 65965245Smsmith break; 66065245Smsmith OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 66165245Smsmith apm_resume(); 66265245Smsmith break; 66365245Smsmith OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 66465245Smsmith apm_battery_low(sc); 66558883Smsmith apm_suspend(); 66665245Smsmith break; 66765245Smsmith 66865245Smsmith OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 66965245Smsmith break; 67065245Smsmith 67165245Smsmith OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 672140340Sscottl inittodr(0); /* adjust time to RTC */ 67365245Smsmith break; 67465245Smsmith 67565245Smsmith default: 67651974Smsmith printf("Unknown Original APM Event 0x%x\n", apm_event); 67765245Smsmith break; 67858883Smsmith } 67965245Smsmith } 68058883Smsmith} 68165245Smsmith 68265245Smsmith/* 68365245Smsmith * Attach APM: 68465245Smsmith * 68565245Smsmith * Initialize APM driver (APM BIOS itself has been initialized in locore.s) 68665245Smsmith * 68751974Smsmith * Now, unless I'm mad, (not quite ruled out yet), the APM-1.1 spec is bogus: 68865245Smsmith * 68965245Smsmith * Appendix C says under the header "APM 1.0/APM 1.1 Modal BIOS Behavior" 69065245Smsmith * that "When an APM Driver connects with an APM 1.1 BIOS, the APM 1.1 BIOS 69165245Smsmith * will default to an APM 1.0 connection. After an APM Driver calls the APM 69265245Smsmith * Driver Version function, specifying that it supports APM 1.1, and [sic!] 69351974Smsmith * APM BIOS will change its behavior to an APM 1.1 connection. If the APM 69465245Smsmith * BIOS is an APM 1.0 BIOS, the APM Driver Version function call will fail, 69565245Smsmith * and the connection will remain an APM 1.0 connection." 69665245Smsmith * 69765245Smsmith * OK so I can establish a 1.0 connection, and then tell that I'm a 1.1 69865245Smsmith * and maybe then the BIOS will tell that it too is a 1.1. 69965245Smsmith * Fine. 70065245Smsmith * Now how will I ever get the segment-limits for instance ? There is no 70165245Smsmith * way I can see that I can get a 1.1 response back from an "APM Protected 70265245Smsmith * Mode 32-bit Interface Connect" function ??? 70365245Smsmith * 70465245Smsmith * Who made this, Intel and Microsoft ? -- How did you guess ! 70565245Smsmith * 70665245Smsmith * /phk 707140340Sscottl */ 70851974Smsmith 70951974Smsmithstatic int 71051974Smsmithapmattach(struct isa_device *dvp) 71151974Smsmith{ 71251974Smsmith int unit = dvp->id_unit; 71351974Smsmith char name[32]; 71451974Smsmith#define APM_KERNBASE KERNBASE 71551974Smsmith struct apm_softc *sc = &apm_softc[unit]; 71651974Smsmith 71751974Smsmith master_softc = sc; /* XXX */ 71851974Smsmith sc->initialized = 0; 71951974Smsmith sc->active = 0; 72051974Smsmith sc->halt_cpu = 1; 72151974Smsmith 72265245Smsmith /* setup APM parameters */ 72351974Smsmith sc->cs16_base = (apm_cs32_base << 4) + APM_KERNBASE; 72451974Smsmith sc->cs32_base = (apm_cs16_base << 4) + APM_KERNBASE; 72551974Smsmith sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE; 72651974Smsmith sc->cs_limit = apm_cs_limit; 72751974Smsmith sc->ds_limit = apm_ds_limit; 72851974Smsmith sc->cs_entry = apm_cs_entry; 72951974Smsmith 73051974Smsmith sc->idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); 731138422Sscottl sc->disabled = ((apm_flags & APM_DISABLED) != 0); 73251974Smsmith sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0); 73365245Smsmith 734138422Sscottl#ifdef APM_SLOWSTART 735135236Sscottl if (sc->idle_cpu) { 73651974Smsmith apm_slowstart = apm_slowstart_p = 1; 73765245Smsmith } 73851974Smsmith#endif 73951974Smsmith 74051974Smsmith /* print bootstrap messages */ 74151974Smsmith#ifdef APM_DEBUG 74251974Smsmith printf(" found APM BIOS version %04x\n", apm_version); 74351974Smsmith printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", 74451974Smsmith unit, sc->cs32_base, sc->cs16_base, sc->ds_base); 74551974Smsmith printf("apm%d: Code entry 0x%08x, Idling CPU %s, Management %s\n", 74651974Smsmith unit, sc->cs_entry, is_enabled(sc->idle_cpu), 74758883Smsmith is_enabled(!sc->disabled)); 748107756Semoore printf("apm%d: CS_limit=%x, DS_limit=%x\n", 74951974Smsmith unit, sc->cs_limit, sc->ds_limit); 75051974Smsmith#endif /* APM_DEBUG */ 75151974Smsmith 75251974Smsmith sc->cs_limit = 0xffff; 75351974Smsmith sc->ds_limit = 0xffff; 75451974Smsmith 75551974Smsmith /* setup GDT */ 75651974Smsmith setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base, 75751974Smsmith sc->cs_limit, sc->ds_limit); 75851974Smsmith 75951974Smsmith /* setup entry point 48bit pointer */ 76051974Smsmith apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); 76151974Smsmith apm_addr.offset = sc->cs_entry; 76251974Smsmith 76351974Smsmith /* Try to kick bios into 1.1 mode */ 76451974Smsmith apm_driver_version(); 76565245Smsmith sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + 76651974Smsmith ((apm_version & 0x000f) >> 0); 76751974Smsmith sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + 76851974Smsmith ((apm_version & 0x0f00) >> 8); 76951974Smsmith 77051974Smsmith sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); 77151974Smsmith 77251974Smsmith if (sc->intversion >= INTVERSION(1, 1)) { 773140340Sscottl printf("apm%d: Engaged control %s\n", 77451974Smsmith unit, is_enabled(!sc->disengaged)); 77551974Smsmith } 77665245Smsmith 77751974Smsmith printf(" found APM BIOS version %d.%d\n", 77851974Smsmith sc->majorversion, sc->minorversion); 77951974Smsmith printf("apm%d: Idling CPU %s\n", unit, is_enabled(sc->idle_cpu)); 78051974Smsmith 78151974Smsmith /* enable power management */ 78258883Smsmith if (sc->disabled) { 783107756Semoore if (apm_enable_disable_pm(sc, 1)) { 78451974Smsmith printf("Warning: APM enable function failed! [%x]\n", 78551974Smsmith apm_errno); 78651974Smsmith } 78751974Smsmith } 78851974Smsmith 78951974Smsmith /* engage power managment (APM 1.1 or later) */ 790140340Sscottl if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { 79151974Smsmith if (apm_engage_disengage_pm(sc, 1)) { 79251974Smsmith printf("Warning: APM engage function failed [%x]\n", 79351974Smsmith apm_errno); 79451974Smsmith } 795106225Semoore } 796106225Semoore 797106225Semoore /* default suspend hook */ 798106225Semoore sc->sc_suspend.ah_fun = apm_default_suspend; 799106225Semoore sc->sc_suspend.ah_arg = sc; 800106225Semoore sc->sc_suspend.ah_name = "default suspend"; 801106225Semoore sc->sc_suspend.ah_order = APM_MAX_ORDER; 802106225Semoore 803106225Semoore /* default resume hook */ 804106225Semoore sc->sc_resume.ah_fun = apm_default_resume; 805106225Semoore sc->sc_resume.ah_arg = sc; 806106225Semoore sc->sc_resume.ah_name = "default resume"; 807106225Semoore sc->sc_resume.ah_order = APM_MIN_ORDER; 808106225Semoore 809106225Semoore apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); 810106225Semoore apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); 811106225Semoore 812106225Semoore apm_event_enable(sc); 813106225Semoore 814106225Semoore sc->initialized = 1; 815106225Semoore 816106225Semoore#ifdef DEVFS 817106225Semoore sprintf(name,"apm%d",unit); 818106225Semoore sc->sc_devfs_token = devfs_add_devsw( 819106225Semoore "/", name, &apm_cdevsw, unit, DV_CHR, 0, 0, 0600); 820107756Semoore#endif 821106225Semoore return 0; 822106225Semoore} 823106225Semoore 824106225Semoorestatic int 825106225Semooreapmopen(dev_t dev, int flag, int fmt, struct proc *p) 826106225Semoore{ 827106225Semoore struct apm_softc *sc = &apm_softc[minor(dev)]; 828106225Semoore 829106225Semoore if (minor(dev) >= NAPM) { 830106225Semoore return (ENXIO); 831106225Semoore } 832106225Semoore if (!sc->initialized) { 83365245Smsmith return ENXIO; 83451974Smsmith } 83565245Smsmith return 0; 83665245Smsmith} 83765245Smsmith 83851974Smsmithstatic int 83965245Smsmithapmclose(dev_t dev, int flag, int fmt, struct proc *p) 84051974Smsmith{ 84151974Smsmith return 0; 84251974Smsmith} 84351974Smsmith 84451974Smsmithstatic int 84551974Smsmithapmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) 84651974Smsmith{ 847138422Sscottl struct apm_softc *sc = &apm_softc[minor(dev)]; 848138422Sscottl int error = 0; 849138422Sscottl 850138422Sscottl#ifdef APM_DEBUG 85165245Smsmith printf("APM ioctl: minor = %d, cmd = 0x%x\n", minor(dev), cmd); 85265245Smsmith#endif 85351974Smsmith 85465245Smsmith if (minor(dev) >= NAPM) { 85565245Smsmith return ENXIO; 85665245Smsmith } 85751974Smsmith if (!sc->initialized) { 85865245Smsmith return ENXIO; 85965245Smsmith } 86065245Smsmith switch (cmd) { 861105419Semoore case APMIO_SUSPEND: 86265245Smsmith apm_suspend(); 86365245Smsmith break; 86465245Smsmith case APMIO_GETINFO: 86551974Smsmith if (apm_get_info(sc, (apm_info_t)addr)) { 86665245Smsmith error = ENXIO; 86765245Smsmith } 86865245Smsmith break; 86965245Smsmith case APMIO_ENABLE: 87065245Smsmith apm_event_enable(sc); 87151974Smsmith break; 87251974Smsmith case APMIO_DISABLE: 87351974Smsmith apm_event_disable(sc); 87451974Smsmith break; 87551974Smsmith case APMIO_HALTCPU: 87651974Smsmith apm_halt_cpu(sc); 87751974Smsmith break; 87851974Smsmith case APMIO_NOTHALTCPU: 87951974Smsmith apm_not_halt_cpu(sc); 88051974Smsmith break; 881148499Sps case APMIO_DISPLAYOFF: 882140340Sscottl if (apm_display_off()) { 88351974Smsmith error = ENXIO; 88465245Smsmith } 88565245Smsmith break; 88651974Smsmith default: 887148499Sps error = EINVAL; 88865245Smsmith break; 88951974Smsmith } 89065245Smsmith return error; 89165245Smsmith} 89251974Smsmith 89351974Smsmith 89451974Smsmithstatic apm_devsw_installed = 0; 89551974Smsmith 89651974Smsmithstatic void 89751974Smsmithapm_drvinit(void *unused) 89851974Smsmith{ 89951974Smsmith dev_t dev; 90051974Smsmith 90165245Smsmith if( ! apm_devsw_installed ) { 90265245Smsmith dev = makedev(CDEV_MAJOR,0); 90365245Smsmith cdevsw_add(&dev,&apm_cdevsw,NULL); 90465245Smsmith apm_devsw_installed = 1; 90565245Smsmith } 90665245Smsmith} 90765245Smsmith 90865245SmsmithSYSINIT(apmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,apm_drvinit,NULL) 90965245Smsmith