From owner-freebsd-mobile Sun Feb 14 08:46:40 1999 Return-Path: Received: (from majordom@localhost) by hub.freebsd.org (8.8.8/8.8.8) id IAA12492 for freebsd-mobile-outgoing; Sun, 14 Feb 1999 08:46:40 -0800 (PST) (envelope-from owner-freebsd-mobile@FreeBSD.ORG) Received: from tasogare.imasy.or.jp (tasogare.imasy.or.jp [202.227.24.5]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id IAA12487 for ; Sun, 14 Feb 1999 08:46:37 -0800 (PST) (envelope-from iwasaki@jp.FreeBSD.org) Received: from localhost (ppp7.imasy.or.jp [202.227.24.17]) by tasogare.imasy.or.jp (8.9.3+3.1W/3.7W-tasogare) with ESMTP id BAA21353; Mon, 15 Feb 1999 01:46:04 +0900 (JST) (envelope-from iwasaki@jp.FreeBSD.org) Message-Id: <199902141646.BAA21353@tasogare.imasy.or.jp> To: Valentin Shopov Cc: mobile@FreeBSD.ORG, Nate Williams , Warner Losh Subject: Re: apm & current In-reply-to: Your message of "Sat, 13 Feb 1999 07:39:58 PST." <19990213153958.2977.rocketmail@send1e.yahoomail.com> References: <19990213153958.2977.rocketmail@send1e.yahoomail.com> X-Mailer: Mew version 1.93 on Emacs 19.34 / Mule 2.3 (SUETSUMUHANA) Mime-Version: 1.0 Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Date: Mon, 15 Feb 1999 01:43:35 +0900 From: Mitsuru IWASAKI X-Dispatcher: imput version 980905(IM100) Lines: 790 Sender: owner-freebsd-mobile@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org Hello, I'm now working on APM code for PAO in Japan. > Also kernel panics every time when I run apm, but zzz - suspend is > working. I already noticed that this problem happens on some old laptops which has apm v1.1 or 1.0 (Sotec WinBookPro DX4/100, DEC HiNote Ultra II, etc.). -current/-stable apm(8) try to call APM v1.2 BIOS functions (e.g. APM_RESUMETIMER) without version checking. Some of the old APM BIOSes, however, can make kernel panic when recieved unkown APM BIOS functions. In /sys/i386/apm/apm.c, apm_get_info() has the same problem (APM_GETCAPABILITIES), therefore xbatt also will cause the same trouble. So, now I'm going to commit version checking code just before calling apm_bios_call() in /sys/i386/apm/apm.c into PAO3 CVS repository. Adding to this, I noticed many other problems in apm code, such as - Fixed segment description for APM. The limit granularity should be specified in bytes, not pages. - Try to limit the number of apm_bios_call() executing to only one at the same time, watching busy state made by previous call and waiting if necessary. - Made apm_suspend() and apm_standby() be invoked by apm_timeout() in order to obtain stablities. - Added adjustment for segment size limits informed by APM BIOS. Following patch for apm_init.S and make in /sys/i386/apm/apm_init/ to generate apm_init.inc are required if VM86 isn't enabled in your kernel. --- /usr/src/sys/i386/apm/apm_init/apm_init.S Sat Feb 22 18:29:52 1997 +++ apm_init/apm_init.S Sun Feb 14 20:47:04 1999 @@ -150,6 +150,8 @@ movb $(APM_PROT32CONNECT), %al data32 movl $(PMDV_APMBIOS), %ebx + xorl %esi, %esi /* XXX clear %esi for cs length */ + xorl %edi, %edi /* XXX clear %edi for ds length */ sti int $(SYSTEM_BIOS) cli Followings are diffs for /sys/i386/apm/apm.c between PAO3 (not commited yet) and 3.0-RELEASE, please take a look this code. --- /usr/src/sys/i386/apm/apm.c Mon Sep 28 12:41:12 1998 +++ apm.c Sun Feb 14 22:01:16 1999 @@ -29,6 +29,7 @@ #endif /*DEVFS*/ #include #include +#include #include #include #include @@ -48,11 +49,17 @@ static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx, u_long *edx)); static void apm_resume __P((void)); +#define APM_FORCE_APM10_FLAG 0x02 +#define APM_NO_CLOCK_ADJUST_FLAG 0x04 +#define APM_FORCE_64K_SEG_FLAG 0x08 + + /* static data */ struct apm_softc { int initialized, active; int always_halt_cpu, slow_idle_cpu; int disabled, disengaged; + int suspending; u_int minorversion, majorversion; u_int cs32_base, cs16_base, ds_base; u_int cs16_limit, cs32_limit, ds_limit; @@ -92,16 +99,54 @@ setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code32_limit, u_int code16_limit, u_int data_limit) { /* setup 32bit code segment */ + /* iwasaki (1999/1/24) + * type : S bit = on, code segment, readable, accessed + * dpl : kernel priority level + * def32: default 32 bit size + * gran : limit granularity = byte units + */ gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; gdt_segs[GAPMCODE32_SEL].ssd_limit = code32_limit; + gdt_segs[GAPMCODE32_SEL].ssd_type = SDT_MEMERA; + gdt_segs[GAPMCODE32_SEL].ssd_dpl = SEL_KPL; + gdt_segs[GAPMCODE32_SEL].ssd_def32 = 1; + gdt_segs[GAPMCODE32_SEL].ssd_gran = 0; /* setup 16bit code segment */ + /* iwasaki (1999/1/24) + * type : S bit = on, code segment, readable, accessed + * dpl : kernel priority level + * def32: default 16 bit size + * gran : limit granularity = byte units + */ gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; gdt_segs[GAPMCODE16_SEL].ssd_limit = code16_limit; + gdt_segs[GAPMCODE16_SEL].ssd_type = SDT_MEMERA; + gdt_segs[GAPMCODE16_SEL].ssd_dpl = SEL_KPL; + gdt_segs[GAPMCODE16_SEL].ssd_def32 = 0; + gdt_segs[GAPMCODE16_SEL].ssd_gran = 0; /* setup data segment */ + /* iwasaki (1999/1/24) + * type : S bit = on, data segment, writable, accessed + * dpl : kernel priority level + * def32: default 32 bit size + * gran : limit granularity = byte units + */ gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; + gdt_segs[GAPMDATA_SEL ].ssd_type = SDT_MEMRWA; + gdt_segs[GAPMDATA_SEL ].ssd_dpl = SEL_KPL; + gdt_segs[GAPMDATA_SEL ].ssd_def32 = 1; + gdt_segs[GAPMDATA_SEL ].ssd_gran = 0; + if (data_limit == 0) { + /* iwasaki (1999/1/24) + * type : S bit = on, data segment, accessed + * def32: default 16 bit size + */ + gdt_segs[GAPMDATA_SEL ].ssd_type = SDT_MEMROA; + gdt_segs[GAPMDATA_SEL ].ssd_def32 = 0; + } /* reflect these changes on physical GDT */ ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd); @@ -116,6 +161,49 @@ } apm_addr; static int apm_errno; +static int apm_bios_busy = 0; + +static int +apm_do_int(struct apm_bios_arg *apap) +{ + struct apm_softc *sc = &apm_softc; + int cf; + int n = 0; + u_long apm_func; + + apm_func = apap->eax & 0x00ff; + switch (sc->intversion) { + case INTVERSION(1, 0): + if (apm_func != APM_DRVVERSION && + apm_func > APM_GETPMEVENT) { +#ifdef APM_DEBUG +printf("apm_int: function 0x%lx is not supported by APM V1.0\n", apm_func); +#endif + return ENOSYS; + } + break; + case INTVERSION(1, 1): + if (apm_func != APM_DRVVERSION && + apm_func > APM_ENGAGEDISENGAGEPM && + apm_func < APM_OEMFUNC) { +#ifdef APM_DEBUG +printf("apm_int: function 0x%lx is not supported by APM V1.1\n", apm_func); +#endif + return ENOSYS; + } + break; + case INTVERSION(1, 2): + break; + } + + while (apm_bios_busy && n++ < 50) { + DELAY(100000L); + } + ++apm_bios_busy; + cf = apm_bios_call(apap); + apm_bios_busy = 0; + return cf; +} static int apm_int(u_long *eax, u_long *ebx, u_long *ecx, u_long *edx) @@ -127,7 +215,9 @@ apa.ebx = *ebx; apa.ecx = *ecx; apa.edx = *edx; - cf = apm_bios_call(&apa); + apa.esi = 0; /* clear register */ + apa.edi = 0; /* clear register */ + cf = apm_do_int(&apa); *eax = apa.eax; *ebx = apa.ebx; *ecx = apa.ecx; @@ -156,12 +246,12 @@ return apm_int(&eax, &ebx, &ecx, &edx); } +/* Tell APM-BIOS that WE will do 1.1 and see what they say... */ static void apm_driver_version(int version) { u_long eax, ebx, ecx, edx; - /* First try APM 1.2 */ eax = (APM_BIOS << 8) | APM_DRVVERSION; ebx = 0x0; ecx = version; @@ -246,17 +336,24 @@ * Turn off the entire system. */ void -apm_power_off(void) +apm_power_off(int dummy, void *dummy_arg) { u_long eax, ebx, ecx, edx; if (!apm_softc.active) return; + + /* wait 1sec before turning off the system power */ + DELAY(1000000); + eax = (APM_BIOS << 8) | APM_SETPWSTATE; ebx = PMDV_ALLDEV; ecx = PMST_OFF; edx = 0; - apm_int(&eax, &ebx, &ecx, &edx); + if (apm_int(&eax, &ebx, &ecx, &edx)) { + printf("Power off failure: errcode = %ld\n", + 0xff & (eax >> 8)); + } } /* APM Battery low handler */ @@ -358,6 +455,7 @@ static struct timeval suspend_time; static struct timeval diff_time; +static int apm_no_clock_adjust = 0; static int apm_default_resume(void *arg) @@ -366,40 +464,45 @@ u_int second, minute, hour; struct timeval resume_time, tmp_time; - /* modified for adjkerntz */ - pl = splsoftclock(); - inittodr(0); /* adjust time to RTC */ - microtime(&resume_time); - getmicrotime(&tmp_time); - timevaladd(&tmp_time, &diff_time); + if (apm_no_clock_adjust) { + log(LOG_NOTICE, "resumed from suspended mode\n"); + } + else { + /* modified for adjkerntz */ + pl = splsoftclock(); + inittodr(0); /* adjust time to RTC */ + microtime(&resume_time); + getmicrotime(&tmp_time); + timevaladd(&tmp_time, &diff_time); #ifdef FIXME - /* XXX THIS DOESN'T WORK!!! */ - time = tmp_time; + /* XXX THIS DOESN'T WORK!!! */ + time = tmp_time; #endif #ifdef APM_FIXUP_CALLTODO - /* Calculate the delta time suspended */ - timevalsub(&resume_time, &suspend_time); - /* Fixup the calltodo list with the delta time. */ - adjust_timeout_calltodo(&resume_time); + /* Calculate the delta time suspended */ + timevalsub(&resume_time, &suspend_time); + /* Fixup the calltodo list with the delta time. */ + adjust_timeout_calltodo(&resume_time); #endif /* APM_FIXUP_CALLTODOK */ - splx(pl); + splx(pl); #ifndef APM_FIXUP_CALLTODO - second = resume_time.tv_sec - suspend_time.tv_sec; + second = resume_time.tv_sec - suspend_time.tv_sec; #else /* APM_FIXUP_CALLTODO */ - /* - * We've already calculated resume_time to be the delta between - * the suspend and the resume. - */ - second = resume_time.tv_sec; + /* + * We've already calculated resume_time to be the delta between + * the suspend and the resume. + */ + second = resume_time.tv_sec; #endif /* APM_FIXUP_CALLTODO */ - hour = second / 3600; - second %= 3600; - minute = second / 60; - second %= 60; - log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", - hour, minute, second); + hour = second / 3600; + second %= 3600; + minute = second / 60; + second %= 60; + log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", + hour, minute, second); + } return 0; } @@ -408,35 +511,52 @@ { int pl; - pl = splsoftclock(); - microtime(&diff_time); - inittodr(0); - microtime(&suspend_time); - timevalsub(&diff_time, &suspend_time); - splx(pl); + if (!apm_no_clock_adjust) { + pl = splsoftclock(); + microtime(&diff_time); + inittodr(0); + microtime(&suspend_time); + timevalsub(&diff_time, &suspend_time); + splx(pl); + } return 0; } +/* + * Do not suspend immediately after the system is resumed from + * suspended mode + */ + static void apm_processevent(void); -/* - * Public interface to the suspend/resume: - * - * Execute suspend and resume hook before and after sleep, respectively. - * - */ +static u_int apm_standbys = 0; +static u_int apm_suspends = 0; +static u_int apm_op_inprog = 0; -void -apm_suspend(int state) +static void +apm_do_suspend(void) { struct apm_softc *sc = &apm_softc; if (!sc) return; + apm_op_inprog = 0; + apm_suspends = 0; + + if (sc->suspending != 0) { /* avoid reentry */ + /* PCG-505: suspend -> save-to-disk */ + if (sc->initialized) { + if (apm_suspend_system(PMST_SUSPEND) == 0) + apm_processevent(); + } + return; + } + sc->suspending = 1; + if (sc->initialized) { apm_execute_hook(hook[APM_HOOK_SUSPEND]); - if (apm_suspend_system(state) == 0) + if (apm_suspend_system(PMST_SUSPEND) == 0) apm_processevent(); else /* Failure, 'resume' the system again */ @@ -444,6 +564,61 @@ } } +static void +apm_do_standby(void) +{ + apm_op_inprog = 0; + apm_standbys = 0; + + apm_default_suspend(&apm_softc); + if (apm_suspend_system(PMST_STANDBY) == 0) + apm_processevent(); +} + +static void +apm_lastreq_notify(void) +{ + u_long eax, ebx, ecx, edx; + + eax = (APM_BIOS << 8) | APM_SETPWSTATE; + ebx = PMDV_ALLDEV; + ecx = PMST_LASTREQNOTIFY; + edx = 0; + + apm_int(&eax, &ebx, &ecx, &edx); +} + +/* + * Public interface to the suspend/resume: + * + * Execute suspend and resume hook before and after sleep, respectively. + * + */ + +void +apm_suspend(int state) +{ + switch (state) { + case PMST_SUSPEND: + if(apm_suspends) + return; + apm_suspends++; + apm_op_inprog++; + apm_lastreq_notify(); + break; + case PMST_STANDBY: + if(apm_standbys) + return; + apm_standbys++; + apm_op_inprog++; + apm_lastreq_notify(); + break; + default: + printf("Unknown Suspend state 0x%x\n", state); + break; + } +} + void apm_resume(void) { @@ -452,6 +627,8 @@ if (!sc) return; + sc->suspending = 0; + if (sc->initialized) apm_execute_hook(hook[APM_HOOK_RESUME]); } @@ -472,7 +649,7 @@ if (apm_int(&eax, &ebx, &ecx, &edx)) return 1; - aip->ai_infoversion = 1; + aip->ai_infoversion = (sc->intversion >= INTVERSION(1, 2)) ? 1 : 0; aip->ai_acline = (ebx >> 8) & 0xff; aip->ai_batt_stat = ebx & 0xff; aip->ai_batt_life = ecx & 0xff; @@ -487,16 +664,21 @@ else /* Time is in seconds */ aip->ai_batt_time = edx; - eax = (APM_BIOS << 8) | APM_GETCAPABILITIES; - ebx = 0; - ecx = 0; - edx = 0; - if (apm_int(&eax, &ebx, &ecx, &edx)) { - aip->ai_batteries = -1; /* Unknown */ - aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */ + if (sc->intversion >= INTVERSION(1, 2)) { + eax = (APM_BIOS << 8) | APM_GETCAPABILITIES; + ebx = 0; + ecx = 0; + edx = 0; + if (apm_int(&eax, &ebx, &ecx, &edx)) { + aip->ai_batteries = -1; /* Unknown */ + aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */ + } else { + aip->ai_batteries = ebx & 0xff; + aip->ai_capabilities = ecx & 0xf; + } } else { - aip->ai_batteries = ebx & 0xff; - aip->ai_capabilities = ecx & 0xf; + aip->ai_batteries = -1; /* Unknown */ + aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */ } bzero(aip->ai_spare, sizeof aip->ai_spare); @@ -564,10 +746,21 @@ { struct apm_softc *sc = &apm_softc; + if (apm_op_inprog) + apm_lastreq_notify(); + apm_processevent(); - if (sc->active == 1) + + if (apm_standbys) + apm_do_standby(); + + if (apm_suspends) + apm_do_suspend(); + + if (sc->active == 1) { /* Run slightly more oftan than 1 Hz */ apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1 ); + } } /* enable APM BIOS */ @@ -741,7 +934,7 @@ apm_suspend(PMST_SUSPEND); break; OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); - apm_suspend(PMST_SUSPEND); + apm_do_suspend(); break; OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); apm_resume(); @@ -750,11 +943,17 @@ apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); - apm_resume(); +#if 0 + apm_resume(); /* should we call this or not? */ +#else /* 0 */ + inittodr(0); /* adjust time to RTC */ +#endif /* 0 */ break; OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); apm_battery_low(); +#ifdef APM_BATT_LOW_SUSPEND apm_suspend(PMST_SUSPEND); +#endif /* APM_BATT_LOW_SUSPEND */ break; OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); break; @@ -781,6 +980,27 @@ { #define APM_KERNBASE KERNBASE struct apm_softc *sc = &apm_softc; + int apm_force_apm10 = 0; + int apm_force_64k_segments = 0; + int rversion, rmajorversion, rminorversion; + +#ifdef FORCE_APM10 + apm_force_apm10 = 1; +#else /* FORCE_APM10 */ + apm_force_apm10 = (dvp->id_flags & APM_FORCE_APM10_FLAG); +#endif /* FORCE_APM10 */ + +#ifdef APM_NO_CLOCK_ADJUST + apm_no_clock_adjust = 1; +#else /* APM_NO_CLOCK_ADJUST */ + apm_no_clock_adjust = (dvp->id_flags & APM_NO_CLOCK_ADJUST_FLAG); +#endif /* APM_NO_CLOCK_ADJUST */ + +#ifdef APM_FORCE_64K_SEG + apm_force_64k_segments = 1; +#else /* APM_FORCE_64K_SEG */ + apm_force_64k_segments = (dvp->id_flags & APM_FORCE_64K_SEG_FLAG); +#endif /* APM_FORCE_64K_SEG */ sc->initialized = 0; @@ -791,13 +1011,85 @@ sc->cs16_base = (apm_cs16_base << 4) + APM_KERNBASE; sc->cs32_base = (apm_cs32_base << 4) + APM_KERNBASE; sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE; - sc->cs32_limit = apm_cs32_limit - 1; - if (apm_cs16_limit == 0) - apm_cs16_limit == apm_cs32_limit; - sc->cs16_limit = apm_cs16_limit - 1; - sc->ds_limit = apm_ds_limit - 1; sc->cs_entry = apm_cs_entry; + /* + * Some bogus APM V1.1 BIOSes do not return any + * size limits in the registers they are supposed to. + * We forced them to zero before calling the BIOS + * (see apm_init.S), so if we see zero limits here + * we assume that means they should be 64k (and trimmed + * if needed for legitimate memory needs). + */ + + /* for V1.0 and bogus BIOSes */ + if (apm_version <= 0x100 || apm_version >= 0xa00 || apm_force_apm10) { + apm_force_64k_segments = 1; + } + + if (apm_force_64k_segments) { + sc->cs32_limit = 0xffff; + sc->cs16_limit = 0xffff; + sc->ds_limit = 0xffff; + } else { + /* code segment (32 bit) */ + if (apm_cs32_limit == 0) { + /* XXX + * some BIOSes are lame, even if v1.1. + * (Or maybe they want 64k even though they can + * only ask for 64k-1?) + */ +#ifdef APM_DEBUG + printf("apmattach: lame v%04lx bios gave zero len code32, pegged to 64k\n", + apm_version); +#endif + sc->cs32_limit = 0xffff; + } else { + sc->cs32_limit = apm_cs32_limit - 1; + } + + /* code segment (16 bit) */ + if (apm_cs16_limit == 0) { +#ifdef APM_DEBUG + printf("apmattach: v%04lx bios gave zero len code16, pegged to code32's one\n", + apm_version); +#endif + sc->cs16_limit = sc->cs32_limit; + } else { + sc->cs16_limit = apm_cs16_limit - 1; + } + + /* data segment */ + if (apm_ds_limit == 0) { + /* XXX + * some BIOSes are lame, even if v1.1. + * just assume that means they should be 64k :-) + * TODO: + * need to confirm that segment in an available + * location within ISA hole or at page zero or + * above biosbasemem and below ISA hole end. + * if there is no avaivable locations, set up + * the segment descriptor to just the first byte + * of the code segment, read only. + */ +#ifdef APM_DEBUG + printf("apmattach: lame v%04lx bios gave zero len data, tentative 64k\n", + apm_version); +#endif + sc->ds_limit = 0xffff; + } else { + sc->ds_limit = apm_ds_limit - 1; + } + } + + if (sc->cs32_limit < sc->cs_entry + 4) { +#ifdef APM_DEBUG + printf("apmattach: nonsensical BIOS code length %d ignored (entry point offset is %d)\n", + sc->cs32_limit, sc->cs_entry); +#endif + sc->cs32_limit = 0xffff; + } + /* Always call HLT in idle loop */ sc->always_halt_cpu = 1; @@ -807,14 +1099,14 @@ /* print bootstrap messages */ #ifdef APM_DEBUG - printf("apm: APM BIOS version %04x\n", apm_version); + printf("apm: APM BIOS version %04lx\n", apm_version); printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", sc->cs32_base, sc->cs16_base, sc->ds_base); printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n", sc->cs_entry, is_enabled(sc->slow_idle_cpu), is_enabled(!sc->disabled)); printf("apm: CS32_limit=0x%x, CS16_limit=0x%x, DS_limit=0x%x\n", - (u_short)sc->cs32_limit, (u_short)sc->cs16_limit, (u_short)sc->ds_limit); + sc->cs32_limit, sc->cs16_limit, sc->ds_limit); #endif /* APM_DEBUG */ #if 0 @@ -831,35 +1123,60 @@ apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); apm_addr.offset = sc->cs_entry; - if ((dvp->id_flags & 0x10)) { - if ((dvp->id_flags & 0xf) >= 0x2) { + rversion = apm_version; + rminorversion = ((rversion & 0x00f0) >> 4) * 10 + + ((rversion & 0x000f) >> 0); + rmajorversion = ((rversion & 0xf000) >> 12) * 10 + + ((rversion & 0x0f00) >> 8); + + if (apm_force_apm10) { + apm_version = 0x100; + sc->majorversion = 1; + sc->minorversion = 0; + sc->intversion = INTVERSION(1, 0); + printf("apm: running in APM 1.0 compatible mode\n"); + } + else{ + if (rmajorversion > 1 || + (rmajorversion == 1 && rminorversion >= 2)) { apm_driver_version(0x102); - } - if (!apm_version && (dvp->id_flags & 0xf) >= 0x1) { + sc->intversion = INTVERSION(1, 2); + } else if (rmajorversion == 1 && rminorversion >= 1) { apm_driver_version(0x101); + sc->intversion = INTVERSION(1, 1); } - } else { - apm_driver_version(0x102); - if (!apm_version) - apm_driver_version(0x101); - } - if (!apm_version) - apm_version = 0x100; - sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + - ((apm_version & 0x000f) >> 0); - sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + - ((apm_version & 0x0f00) >> 8); + if (!apm_version) + apm_version = 0x100; - sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); + sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + + ((apm_version & 0x000f) >> 0); + sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + + ((apm_version & 0x0f00) >> 8); + + if ((sc->majorversion == 1 && sc->minorversion == 0 + && rmajorversion >= 1 && rminorversion >= 1) + || sc->majorversion > 10 /* for broken APM 1.1 */ + ) { + apm_version = 0x100; + sc->majorversion = 1; + sc->minorversion = 0; + sc->intversion = INTVERSION(1, 0); + printf("apm: running in APM 1.0 compatible mode\n"); + } + else{ + sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); #ifdef APM_DEBUG - if (sc->intversion >= INTVERSION(1, 1)) - printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged)); + if (sc->intversion >= INTVERSION(1, 1)) + printf("apm: Engaged control %s\n", + is_enabled(!sc->disengaged)); #endif - printf("apm: found APM BIOS version %d.%d\n", - sc->majorversion, sc->minorversion); + printf("apm: found APM BIOS version %d.%d\n", + sc->majorversion, sc->minorversion); + } + } #ifdef APM_DEBUG printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu)); @@ -901,6 +1218,8 @@ apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); + at_shutdown(apm_power_off, NULL, SHUTDOWN_POWER_OFF); + apm_event_enable(); sc->initialized = 1; @@ -939,7 +1258,7 @@ if (minor(dev) != 0 || !sc->initialized) return (ENXIO); #ifdef APM_DEBUG - printf("APM ioctl: cmd = 0x%x\n", cmd); + printf("APM ioctl: cmd = 0x%lx\n", cmd); #endif switch (cmd) { case APMIO_SUSPEND: @@ -991,11 +1310,13 @@ case APMIO_DISPLAY: newstate = *(int *)addr; if (apm_display(newstate)) error = ENXIO; break; case APMIO_BIOS: - if (apm_bios_call((struct apm_bios_arg*)addr) == 0) + if ((error = apm_do_int((struct apm_bios_arg*)addr)) == 0) ((struct apm_bios_arg*)addr)->eax &= 0xff; + else if (error == 1) + error = 0; break; default: error = EINVAL; To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-mobile" in the body of the message