[PATCH] APM patch to 2.2.0-pre6 +

Stephen Rothwell (sfr@canb.auug.org.au)
Wed, 13 Jan 1999 01:51:13 +1100


Hi Linus,

Below is another APM patch that fixes a few things:

Fix small typo.
Try to cope with BIOS's that need to have all display
devices blanked and not just the first one.
Fix segment limit setting it has always been wrong as
the segments needed to have byte granularity.
Mark a few things __init.
Add hack to allow power off of SMP systems by popular request.
For this to work, you need to boot with apm=smp-power-off
on the kernel command line, so it is pretty fool proof.
Use CONFIG_SMP instead of __SMP__
Ignore SUSPEND BOUNCES for three seconds instead of one.

This patch is relative to the last patch I sent you (for Configure.help)
as I noticed you have applied that to pre7.

Cheers,
Stephen

--
Stephen Rothwell                    Stephen.Rothwell@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

diff -ruN linux-2.2.0-pre6+/Documentation/Configure.help linux-2.2.0-pre6-APM/Documentation/Configure.help --- linux-2.2.0-pre6+/Documentation/Configure.help Wed Jan 13 01:13:02 1999 +++ linux-2.2.0-pre6-APM/Documentation/Configure.help Sun Jan 10 02:54:17 1999 @@ -8463,7 +8463,7 @@ CONFIG_APM_IGNORE_SUSPEND_BOUNCE This option is necessary on the Dell Inspiron 3200 and others, but should be safe for all other laptops. When enabled, a system suspend - event that occurs within one second of a resume is ignored. Without + event that occurs within three seconds of a resume is ignored. Without this the Inspiron will shut itself off a few seconds after you open the lid, requiring you to press the power button to resume it a second time. diff -ruN linux-2.2.0-pre6+/arch/i386/kernel/apm.c linux-2.2.0-pre6-APM/arch/i386/kernel/apm.c --- linux-2.2.0-pre6+/arch/i386/kernel/apm.c Sun Jan 10 01:23:31 1999 +++ linux-2.2.0-pre6-APM/arch/i386/kernel/apm.c Wed Jan 13 01:10:36 1999 @@ -32,6 +32,7 @@ * Sep 1998, Version 1.6 * Nov 1998, Version 1.7 * Jan 1999, Version 1.8 + * Jan 1999, Version 1.9 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -78,6 +79,17 @@ * change APM_NOINTS to CONFIG_APM_ALLOW_INTS * remove dependency on CONFIG_PROC_FS * Stephen Rothwell + * 1.9: Fix small typo. <laslo@ilo.opole.pl> + * Try to cope with BIOS's that need to have all display + * devices blanked and not just the first one. + * Ross Paterson <ross@soi.city.ac.uk> + * Fix segment limit setting it has always been wrong as + * the segments needed to have byte granularity. + * Mark a few things __init. + * Add hack to allow power off of SMP systems by popular request. + * Use CONFIG_SMP instead of __SMP__ + * Ignore BOUNCES for three seconds. + * Stephen Rothwell * * APM 1.1 Reference: * @@ -213,7 +225,7 @@ #define APM_ZERO_SEGS /* - * Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is + * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is * supposed to provide limit information that it recognizes. Many machines * do this correctly, but many others do not restrict themselves to their * claimed limit. When this happens, they will cause a segmentation @@ -242,6 +254,12 @@ #define APM_CHECK_TIMEOUT (HZ) /* + * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then + * ignore suspend events for this amount of time + */ +#define BOUNCE_INTERVAL (3 * HZ) + +/* * Save a segment register away */ #define savesegment(seg, where) \ @@ -276,6 +294,7 @@ unsigned short segment; } apm_bios_entry; static int apm_enabled = 0; +static int smp_hack = 0; #ifdef CONFIG_APM_CPU_IDLE static int clock_slowed = 0; #endif @@ -300,7 +319,7 @@ static struct timer_list apm_timer; -static char driver_version[] = "1.8"; /* no spaces */ +static char driver_version[] = "1.9"; /* no spaces */ #ifdef APM_DEBUG static char * apm_event_name[] = { @@ -526,7 +545,15 @@ void apm_power_off(void) { - if (apm_enabled) + /* + * smp_hack == 2 means that we would have enabled APM support + * except there is more than one processor and so most of + * the APM stuff is unsafe. We will still try power down + * because is is useful to some people and they know what + * they are doing because they booted with the smp-power-off + * kernel option. + */ + if (apm_enabled || (smp_hack == 2)) (void) apm_set_power_state(APM_STATE_OFF); } @@ -534,12 +561,19 @@ /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ static int apm_set_display_power_state(u_short state) { - return set_power_state(0x0100, state); + int error; + + /* Blank the first display device */ + error = set_power_state(0x0100, state); + if (error == APM_BAD_DEVICE) + /* try to blank them all instead */ + error = set_power_state(0x01ff, state); + return error; } #endif #ifdef CONFIG_APM_DO_ENABLE -static int apm_enable_power_management(void) +static int __init apm_enable_power_management(void) { u32 eax; @@ -568,12 +602,9 @@ return APM_SUCCESS; } -#if 0 -/* not used anywhere */ -static int apm_get_battery_status(u_short which, +static int apm_get_battery_status(u_short which, u_short *status, u_short *bat, u_short *life, u_short *nbat) { - u_short status; u32 eax; u32 ebx; u32 ecx; @@ -585,20 +616,20 @@ if (which != 1) return APM_BAD_DEVICE; *nbat = 1; - return apm_get_power_status(&status, bat, life); + return apm_get_power_status(status, bat, life); } if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi)) return (eax >> 8) & 0xff; + *status = ebx; *bat = ecx; *life = edx; *nbat = esi; return APM_SUCCESS; } -#endif -static int apm_engage_power_management(u_short device) +static int __init apm_engage_power_management(u_short device) { u32 eax; @@ -842,7 +873,8 @@ "event 0x%02x\n", event); #endif #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE - if (ignore_bounce && ((jiffies - last_resume) > HZ)) + if (ignore_bounce + && ((jiffies - last_resume) > BOUNCE_INTERVAL)) ignore_bounce = 0; #endif switch (event) { @@ -1152,6 +1184,7 @@ unsigned short bx; unsigned short cx; unsigned short dx; + unsigned short nbat; unsigned short error; unsigned short ac_line_status = 0xff; unsigned short battery_status = 0xff; @@ -1173,13 +1206,8 @@ if (apm_bios_info.version > 0x100) { battery_flag = (cx >> 8) & 0xff; if (dx != 0xffff) { - if ((dx & 0x8000) == 0x8000) { - units = "min"; - time_units = dx & 0x7ffe; - } else { - units = "sec"; - time_units = dx & 0x7fff; - } + units = (dx & 0x8000) ? "min" : "sec"; + time_units = dx & 0x7fff; } } } @@ -1249,6 +1277,8 @@ str += 3; if (strncmp(str, "debug", 5) == 0) debug = !invert; + if (strncmp(str, "smp-power-off", 13) == 0) + smp_hack = !invert; str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); @@ -1289,17 +1319,18 @@ /* BIOS < 1.2 doesn't set cseg_16_len */ if (apm_bios_info.version < 0x102) - apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */ + apm_bios_info.cseg_16_len = 0; /* 64k */ if (debug) { printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x", apm_bios_info.cseg, apm_bios_info.offset, apm_bios_info.cseg_16, apm_bios_info.dseg); if (apm_bios_info.version > 0x100) - printk(" cseg len %x, cseg16 len %x, dseg len %x", + printk(" cseg len %x, dseg len %x", apm_bios_info.cseg_len, - apm_bios_info.cseg_16_len, apm_bios_info.dseg_len); + if (apm_bios_info.version > 0x101) + printk(" cseg16 len %x", apm_bios_info.cseg_16_len); printk("\n"); } @@ -1307,12 +1338,6 @@ printk(KERN_NOTICE "apm: disabled on user request.\n"); return; } -#ifdef __SMP__ - if (smp_num_cpus > 1) { - printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); - return; - } -#endif /* * Set up a segment that references the real mode segment 0x40 @@ -1322,7 +1347,7 @@ */ set_base(gdt[APM_40 >> 3], __va((unsigned long)0x40 << 4)); - set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4)); + _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); apm_bios_entry.offset = apm_bios_info.offset; apm_bios_entry.segment = APM_CS; @@ -1332,23 +1357,36 @@ __va((unsigned long)apm_bios_info.cseg_16 << 4)); set_base(gdt[APM_DS >> 3], __va((unsigned long)apm_bios_info.dseg << 4)); - if (apm_bios_info.version == 0x100) { - set_limit(gdt[APM_CS >> 3], 64 * 1024); - set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); - set_limit(gdt[APM_DS >> 3], 64 * 1024); - } else { -#ifdef APM_RELAX_SEGMENTS +#ifndef APM_RELAX_SEGMENTS + if (apm_bios_info.version == 0x100) +#endif + { /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ - set_limit(gdt[APM_CS >> 3], 64 * 1024); + _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); /* For some unknown machine. */ - set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); + _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1); /* For the DEC Hinote Ultra CT475 (and others?) */ - set_limit(gdt[APM_DS >> 3], 64 * 1024); -#else - set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len); - set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len); - set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len); + _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1); + } +#ifndef APM_RELAX_SEGMENTS + else { + _set_limit((char *)&gdt[APM_CS >> 3], + (apm_bios_info.cseg_len - 1) & 0xffff); + _set_limit((char *)&gdt[APM_CS_16 >> 3], + (apm_bios_info.cseg_16_len - 1) & 0xffff); + _set_limit((char *)&gdt[APM_DS >> 3], + (apm_bios_info.dseg_len - 1) & 0xffff); + } +#endif +#ifdef CONFIG_SMP + if (smp_num_cpus > 1) { + printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); + if (smp_hack) + smp_hack = 2; + return; + } #endif + if (apm_bios_info.version > 0x100) { /* * We only support BIOSs up to version 1.2 */ @@ -1360,7 +1398,7 @@ } } if (debug) { - printk(KERN_INFO "apm: onnection version %d.%d\n", + printk(KERN_INFO "apm: Connection version %d.%d\n", (apm_bios_info.version >> 8) & 0xff, apm_bios_info.version & 0xff ); @@ -1381,23 +1419,23 @@ case 3: bat_stat = "charging"; break; default: bat_stat = "unknown"; break; } - printk(KERN_INFO "apm: AC %s, battery status %s, battery life ", + printk(KERN_INFO + "apm: AC %s, battery status %s, battery life ", power_stat, bat_stat); if ((cx & 0xff) == 0xff) printk("unknown\n"); else printk("%d%%\n", cx & 0xff); if (apm_bios_info.version > 0x100) { - printk("apm: battery flag 0x%02x, battery life ", + printk(KERN_INFO + "apm: battery flag 0x%02x, battery life ", (cx >> 8) & 0xff); if (dx == 0xffff) printk("unknown\n"); - else { - if ((dx & 0x8000)) - printk("%d minutes\n", dx & 0x7ffe ); - else - printk("%d seconds\n", dx & 0x7fff ); - } + else + printk("%d %s\n", dx & 0x7fff, + (dx & 0x8000) ? + "minutes" : "seconds"); } } } diff -ruN linux-2.2.0-pre6+/arch/i386/kernel/head.S linux-2.2.0-pre6-APM/arch/i386/kernel/head.S --- linux-2.2.0-pre6+/arch/i386/kernel/head.S Sat Sep 12 01:10:35 1998 +++ linux-2.2.0-pre6-APM/arch/i386/kernel/head.S Sun Jan 10 01:37:51 1999 @@ -534,10 +534,14 @@ .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ - .quad 0x00c0920000000000 /* 0x40 APM set up for bad BIOS's */ - .quad 0x00c09a0000000000 /* 0x48 APM CS code */ - .quad 0x00809a0000000000 /* 0x50 APM CS 16 code (16 bit) */ - .quad 0x00c0920000000000 /* 0x58 APM DS data */ + /* + * The APM segments have byte granularity and their bases + * and limits are set at run time. + */ + .quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */ + .quad 0x00409a0000000000 /* 0x48 APM CS code */ + .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ + .quad 0x0040920000000000 /* 0x58 APM DS data */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ /*

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/