[PATCH] 2.1.120 APM problems

Stephen Rothwell (sfr@canb.auug.org.au)
Mon, 07 Sep 1998 02:32:17 +1000


Hi all,

I have another patch to 2.1.120 to (hopefully) put the APM
problems to rest ... I have compiled this and it looks OK
(by inspection of disassembled code), but I haven't booted
this on an APM enabled machine.

Please try this - especially those who use gcc 2.8.x as I
only use 2.7.2.3.

I was not sure why I bothered to use __asm__ or __volatile__,
so I removed them.

Cheers,
Stephen

--
Stephen Rothwell                    Stephen.Rothwell@canb.auug.org.au

diff -ru linux-2.1.120/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- linux-2.1.120/drivers/char/apm_bios.c Sun Sep 6 17:44:28 1998 +++ linux/drivers/char/apm_bios.c Mon Sep 7 02:15:05 1998 @@ -27,6 +27,7 @@ * Feb 1998, Version 1.3 * Feb 1998, Version 1.4 * Aug 1998, Version 1.5 + * Sep 1998, Version 1.6 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -52,6 +53,11 @@ * 1.5: Fix segment register reloading (in case of bad segments saved * across BIOS call). * Stephen ROthwell + * 1.6: Alternate solution to register reloading inspired by + * Gabriel Paubert <paubert@iram.es> + * Cope with complier/assembler differences. + * Only try to turn off the first display device. + * Stephen Rothwell * * APM 1.1 Reference: * @@ -211,11 +217,6 @@ #define APM_CHECK_TIMEOUT (HZ) /* - * Save a segment register away - */ -#define savesegment(seg, where) __asm__ __volatile__("movw %%" #seg ", %0\n" : "=m" (where)) - -/* * Forward declarations */ static void suspend(void); @@ -263,7 +264,7 @@ static struct timer_list apm_timer; -static char driver_version[] = "1.5"; /* no spaces */ +static char driver_version[] = "1.6"; /* no spaces */ #ifdef APM_DEBUG static char * apm_event_name[] = { @@ -349,107 +350,97 @@ * can be zeroed before the call. Unfortunately, we can't do anything * about the stack segment/pointer. Also, we tell the compiler that * everything could change. + * + * Also, we KNOW that for the non error case of apm_bios_call, there + * is no useful data returned in the low order 8 bits of eax. */ - -static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, - u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) -{ - u16 old_fs; - u16 old_gs; - int error; - -#ifdef APM_ZERO_SEGS - savesegment(fs, old_fs); - savesegment(gs, old_gs); -#endif - __asm__ __volatile__( - "pushfl\n\t" #ifdef APM_NOINTS - "cli\n\t" +# define APM_DO_CLI "cli\n\t" +#else +# define APM_DO_CLI #endif #ifdef APM_ZERO_SEGS - "pushl %%ds\n\t" - "pushl %%es\n\t" - "movw %9, %%ds\n\t" - "movw %9, %%es\n\t" - "movw %9, %%fs\n\t" - "movw %9, %%gs\n\t" +# define APM_DO_ZERO_SEGS \ + "pushl %%gs\n\t" \ + "pushl %%fs\n\t" \ + "pushl %%es\n\t" \ + "pushl %%ds\n\t" \ + "xorl %%edx, %%edx\n\t" \ + "movl %%edx, %%ds\n\t" \ + "movl %%edx, %%es\n\t" \ + "movl %%edx, %%fs\n\t" \ + "movl %%edx, %%gs\n\t" +# define APM_DO_RESTORE_SEGS \ + "popl %%ds\n\t" \ + "popl %%es\n" \ + "1:\t" \ + "popl %%fs\n" \ + "2:\t" \ + "popl %%gs\n\t" \ + ".section fixup, \"ax\"\n" \ + "3:\t" \ + "movl $0, (%%esp)\n\t" \ + "jmp 1b\n" \ + "4:\t" \ + "movl $0, (%%esp)\n\t" \ + "jmp 2b\n\t" \ + ".previous\n\t" \ + ".section __ex_table, \"a\"\n\t" \ + ".align 4\n\t" \ + ".long 1b, 3b\n\t" \ + ".long 2b, 4b\n\t" \ + ".previous\n\t" +#else +# define APM_DO_ZERO_SEGS +# define APM_DO_RESTORE_SEGS #endif + +static inline u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, + u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) +{ + asm( + "pushfl\n\t" + APM_DO_CLI + APM_DO_ZERO_SEGS "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" - "movl $0, %%edi\n\t" - "jnc 1f\n\t" - "movl $1, %%edi\n" - "1:\tpopl %%es\n\t" - "popl %%ds\n\t" - "popfl\n\t" + "setc %%al\n\t" + APM_DO_RESTORE_SEGS + "popfl" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx), - "=S" (*esi), "=D" (error) + "=S" (*esi) : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) -#ifdef APM_ZERO_SEGS - , "r" (0) -#endif : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory"); -#ifdef APM_ZERO_SEGS - loadsegment(fs, old_fs); - loadsegment(gs, old_gs); -#endif - return error; + return *eax & 0xff; } /* * This version only returns one value (usually an error code) */ -static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax) +static inline u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, + u32 *eax) { - u16 old_fs; - u16 old_gs; - int error; + u8 error; -#ifdef APM_ZERO_SEGS - savesegment(fs, old_fs); - savesegment(gs, old_gs); -#endif - __asm__ __volatile__( + asm( "pushfl\n\t" -#ifdef APM_NOINTS - "cli\n\t" -#endif -#ifdef APM_ZERO_SEGS - "pushl %%ds\n\t" - "pushl %%es\n\t" - "movw %5, %%ds\n\t" - "movw %5, %%es\n\t" - "movw %5, %%fs\n\t" - "movw %5, %%gs\n\t" -#endif + APM_DO_CLI + APM_DO_ZERO_SEGS "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" - "movl $0, %%edi\n\t" - "jnc 1f\n\t" - "movl $1, %%edi\n" - "1:\tpopl %%es\n\t" - "popl %%ds\n\t" - "popfl\n\t" - : "=a" (*eax), "=D" (error) + "setc %%bl\n\t" + APM_DO_RESTORE_SEGS + "popfl" + : "=a" (*eax), "=b" (error) : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) -#ifdef APM_ZERO_SEGS - , "r" (0) -#endif : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory"); -#ifdef APM_ZERO_SEGS - loadsegment(fs, old_fs); - loadsegment(gs, old_gs); -#endif return error; } static int apm_driver_version(u_short *val) { - int error; u32 eax; - error = apm_bios_call_simple(0x530e, 0, *val, &eax); - if (error) + if (apm_bios_call_simple(0x530e, 0, *val, &eax)) return (eax >> 8) & 0xff; *val = eax; return APM_SUCCESS; @@ -457,14 +448,12 @@ static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info) { - int error; u32 eax; u32 ebx; u32 ecx; u32 dummy; - error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy); - if (error) + if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy)) return (eax >> 8) & 0xff; *event = ebx; if (apm_bios_info.version < 0x0102) @@ -474,13 +463,11 @@ return APM_SUCCESS; } -static int set_power_state(u_short what, u_short state) +static inline int set_power_state(u_short what, u_short state) { - int error; u32 eax; - error = apm_bios_call_simple(0x5307, what, state, &eax); - if (error) + if (apm_bios_call_simple(0x5307, what, state, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } @@ -494,7 +481,7 @@ /* 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(0x01ff, state); + return set_power_state(0x0100, state); } #endif @@ -502,12 +489,11 @@ /* Called by apm_setup if apm_enabled will be true. */ static int apm_enable_power_management(void) { - int error; u32 eax; - error = apm_bios_call_simple(0x5308, - (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax); - if (error) + if (apm_bios_call_simple(0x5308, + (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, + 1, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } @@ -515,15 +501,13 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) { - int error; u32 eax; u32 ebx; u32 ecx; u32 edx; u32 dummy; - error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy); - if (error) + if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy)) return (eax >> 8) & 0xff; *status = ebx; *bat = ecx; @@ -537,7 +521,6 @@ u_short *bat, u_short *life, u_short *nbat) { u_short status; - int error; u32 eax; u32 ebx; u32 ecx; @@ -552,8 +535,8 @@ return apm_get_power_status(&status, bat, life); } - error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi); - if (error) + if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, + &ebx, &ecx, &edx, &esi)) return (eax >> 8) & 0xff; *bat = ecx; *life = edx; @@ -564,11 +547,9 @@ static int apm_engage_power_management(u_short device) { - int error; u32 eax; - error = apm_bios_call_simple(0x530f, device, 1, &eax); - if (error) + if (apm_bios_call_simple(0x530f, device, 1, &eax)) return (eax >> 8) & 0xff; return APM_SUCCESS; } @@ -876,14 +857,12 @@ int apm_do_idle(void) { #ifdef CONFIG_APM_CPU_IDLE - int error; u32 dummy; if (!apm_enabled) return 0; - error = apm_bios_call_simple(0x5305, 0, 0, &dummy); - if (error) + if (apm_bios_call_simple(0x5305, 0, 0, &dummy)) return 0; clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;

- 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/faq.html