patch-2.2.0-pre8 linux/arch/i386/kernel/apm.c
Next file: linux/arch/i386/kernel/entry.S
Previous file: linux/arch/i386/defconfig
Back to the patch index
Back to the overall index
- Lines: 320
- Date:
Thu Jan 14 22:57:25 1999
- Orig file:
v2.2.0-pre7/linux/arch/i386/kernel/apm.c
- Orig date:
Fri Jan 8 22:36:00 1999
diff -u --recursive --new-file v2.2.0-pre7/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
@@ -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");
}
}
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov