There also seems to be a bug in linux APM support where page descriptors
are corrupted by the APM bios during suspend/hibernate. I'm still
tracking this down.
--Scott
@ @
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-oOO-(_)-OOo-=-=-=-=-=
C. Scott Ananian: cananian@lcs.mit.edu / Declare the Truth boldly and
Laboratory for Computer Science/Crypto / without hindrance.
Massachusetts Institute of Technology /META-PARRESIAS AKOLUTOS:Acts 28:31
-.-. .-.. .. ..-. ..-. --- .-. -.. ... -.-. --- - - .- -. .- -. .. .- -.
PGP key available via finger and from http://www.pdos.lcs.mit.edu/~cananian
diff -ruHp -X badboys linux-2.1.86-orig/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- linux-2.1.86-orig/arch/i386/boot/setup.S Thu Feb 12 15:13:10 1998
+++ linux/arch/i386/boot/setup.S Thu Feb 12 15:16:01 1998
@@ -410,8 +410,8 @@ no_psmouse:
mov [68],ebx ! BIOS entry point offset
mov [72],cx ! BIOS 16 bit code segment
mov [74],dx ! BIOS data segment
- mov [78],si ! BIOS code segment length
- mov [80],di ! BIOS data segment length
+ mov [78],esi ! BIOS code segment length
+ mov [82],di ! BIOS data segment length
jmp done_apm_bios
no_32_apm_bios:
diff -ruHp -X badboys linux-2.1.86-orig/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- linux-2.1.86-orig/drivers/char/apm_bios.c Thu Feb 12 15:11:56 1998
+++ linux/drivers/char/apm_bios.c Thu Feb 12 17:40:00 1998
@@ -141,6 +141,8 @@ extern unsigned long get_cmos_time(void)
* U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
* [Confirmed by BIOS disassembly]
* P: Toshiba 1950S: battery life information only gets updated after resume
+ * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
+ * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
*
* Legend: U = unusable with APM patches
* P = partially usable with APM patches
@@ -279,9 +281,15 @@ extern unsigned long get_cmos_time(void)
: "a" (0x530a), "b" (1) \
APM_BIOS_CALL_END
-#define APM_GET_EVENT(event, error) \
+#define APM_GET_BATTERY_STATUS(which, bx, cx, dx, si, error) \
APM_BIOS_CALL(al) \
- : "=a" (error), "=b" (event) \
+ : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx), "=S" (si) \
+ : "a" (0x530a), "b" (0x8000 | (which)) \
+ APM_BIOS_CALL_END
+
+#define APM_GET_EVENT(event, info, error) \
+ APM_BIOS_CALL(al) \
+ : "=a" (error), "=b" (event), "=c" (info) \
: "a" (0x530b) \
APM_BIOS_CALL_END
@@ -356,7 +364,8 @@ static char * apm_event_name[] = {
"critical suspend",
"user standby",
"user suspend",
- "system standby resume"
+ "system standby resume",
+ "capabilities change"
};
#define NR_APM_EVENT_NAME \
(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
@@ -406,6 +415,8 @@ static const lookup_t error_table[] = {
{ APM_BAD_DEVICE, "Unrecognized device ID" },
{ APM_BAD_PARAM, "Parameter out of range" },
{ APM_NOT_ENGAGED, "Interface not engaged" },
+ { APM_BAD_FUNCTION, "Function not supported" },
+ { APM_RESUME_DISABLED, "Resume timer disabled" },
{ APM_BAD_STATE, "Unable to enter requested state" },
/* N/A { APM_NO_EVENTS, "No events pending" }, */
{ APM_NOT_PRESENT, "No APM present" }
@@ -423,13 +434,15 @@ static int apm_driver_version(u_short *v
return APM_SUCCESS;
}
-static int apm_get_event(apm_event_t *event)
+static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
{
u_short error;
- APM_GET_EVENT(*event, error);
+ APM_GET_EVENT(*event, *info, error);
if (error & 0xff)
return (error >> 8);
+ if (apm_bios_info.version < 0x0102)
+ *info = ~0; /* indicate info not valid */
return APM_SUCCESS;
}
@@ -481,6 +494,24 @@ static int apm_get_power_status(u_short
return APM_SUCCESS;
}
+static int apm_get_battery_status(u_short which,
+ u_short *bat, u_short *life, u_short *nbat)
+{
+ u_short status, error;
+
+ if (apm_bios_info.version < 0x0102) {
+ /* pretend we only have one battery. */
+ if (which!=1) return APM_BAD_DEVICE;
+ *nbat = 1;
+ return apm_get_power_status(&status, bat, life);
+ }
+
+ APM_GET_BATTERY_STATUS(which, status, *bat, *life, *nbat, error);
+ if (error & 0xff)
+ return (error >> 8);
+ return APM_SUCCESS;
+}
+
static inline int apm_engage_power_management(u_short device)
{
u_short error;
@@ -652,10 +683,12 @@ static apm_event_t get_event(void)
{
int error;
apm_event_t event;
+ apm_eventinfo_t info;
static int notified = 0;
- error = apm_get_event(&event);
+ /* we don't use the eventinfo */
+ error = apm_get_event(&event, &info);
if (error == APM_SUCCESS)
return event;
@@ -718,6 +751,7 @@ static void check_events(void)
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
+ case APM_CAPABILITY_CHANGE:
send_event(event, 0, NULL);
break;
@@ -1106,12 +1140,17 @@ __initfunc(void apm_bios_init(void))
if (apm_bios_info.version == 0x001)
apm_bios_info.version = 0x100;
+ /* BIOS < 1.2 doesn't set cseg_16_len */
+ if (apm_bios_info.version < 0x102)
+ apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */
+
printk(KERN_INFO " 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, dseg len %x",
- apm_bios_info.cseg_len, apm_bios_info.dseg_len);
+ printk(" cseg len %x, cseg16 len %x, dseg len %x",
+ apm_bios_info.cseg_len, apm_bios_info.cseg_16_len,
+ apm_bios_info.dseg_len);
printk("\n");
/*
@@ -1146,19 +1185,25 @@ __initfunc(void apm_bios_init(void))
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], 64 * 1024);
+ set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len);
set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
#endif
- apm_bios_info.version = 0x0101;
+ /* The APM 1.2 docs state that the apm_driver_version
+ * call can fail if we try to connect as 1.2 to a 1.1 bios.
+ */
+ apm_bios_info.version = 0x0102;
error = apm_driver_version(&apm_bios_info.version);
- if (error != 0)
+ if (error != 0) { /* Fall back to an APM 1.1 connection. */
+ apm_bios_info.version = 0x0101;
+ error = apm_driver_version(&apm_bios_info.version);
+ }
+ if (error != 0) /* Fall back to an APM 1.0 connection. */
apm_bios_info.version = 0x100;
else {
apm_engage_power_management(0x0001);
printk( " Connection version %d.%d\n",
(apm_bios_info.version >> 8) & 0xff,
apm_bios_info.version & 0xff );
- apm_bios_info.version = 0x0101;
}
}
diff -ruHp -X badboys linux-2.1.86-orig/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- linux-2.1.86-orig/include/linux/apm_bios.h Thu Feb 12 15:09:48 1998
+++ linux/include/linux/apm_bios.h Thu Feb 12 16:32:16 1998
@@ -19,6 +19,7 @@
*/
typedef unsigned short apm_event_t;
+typedef unsigned short apm_eventinfo_t;
#ifdef __KERNEL__
@@ -35,6 +36,7 @@ struct apm_bios_info {
unsigned short dseg;
unsigned short flags;
unsigned short cseg_len;
+ unsigned short cseg_16_len;
unsigned short dseg_len;
};
@@ -111,6 +113,7 @@ extern int apm_display_unblank(void);
#define APM_USER_STANDBY 0x0009
#define APM_USER_SUSPEND 0x000a
#define APM_STANDBY_RESUME 0x000b
+#define APM_CAPABILITY_CHANGE 0x000c
/*
* Error codes
@@ -126,6 +129,8 @@ extern int apm_display_unblank(void);
#define APM_BAD_DEVICE 0x09
#define APM_BAD_PARAM 0x0a
#define APM_NOT_ENGAGED 0x0b
+#define APM_BAD_FUNCTION 0x0c
+#define APM_RESUME_DISABLED 0x0d
#define APM_BAD_STATE 0x60
#define APM_NO_EVENTS 0x80
#define APM_NOT_PRESENT 0x86
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu