patch-2.2.15 linux/arch/i386/kernel/apm.c
Next file: linux/arch/i386/kernel/bios32.c
Previous file: linux/arch/i386/defconfig
Back to the patch index
Back to the overall index
- Lines: 1530
- Date:
Fri Apr 21 12:45:45 2000
- Orig file:
v2.2.14/arch/i386/kernel/apm.c
- Orig date:
Tue Jan 4 21:18:39 2000
diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
@@ -1,6 +1,6 @@
/* -*- linux-c -*-
* APM BIOS driver for Linux
- * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com)
+ * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.com)
*
* Initial development of this driver was funded by NEC Australia P/L
* and NEC Corporation
@@ -33,6 +33,9 @@
* Nov 1998, Version 1.7
* Jan 1999, Version 1.8
* Jan 1999, Version 1.9
+ * Oct 1999, Version 1.10
+ * Nov 1999, Version 1.11
+ * Jan 2000, Version 1.12
*
* History:
* 0.6b: first version in official kernel, Linux 1.3.46
@@ -90,6 +93,39 @@
* Use CONFIG_SMP instead of __SMP__
* Ignore BOUNCES for three seconds.
* Stephen Rothwell
+ * 1.10: Fix for Thinkpad return code.
+ * Merge 2.2 and 2.3 drivers.
+ * Remove APM dependencies in arch/i386/kernel/process.c
+ * Remove APM dependencies in drivers/char/sysrq.c
+ * Reset time across standby.
+ * Allow more inititialisation on SMP.
+ * Remove CONFIG_APM_POWER_OFF and make it boot time
+ * configurable (default on).
+ * Make debug only a boot time parameter (remove APM_DEBUG).
+ * Try to blank all devices on any error.
+ * 1.11: Remove APM dependencies in drivers/char/console.c
+ * Fix for bioses that don't zero the top part of the
+ * entrypoint offset (Mario Sitta <sitta@al.unipmn.it>)
+ * (reported by Panos Katsaloulis <teras@writeme.com>).
+ * Real mode power off patch (Walter Hofmann
+ * <Walter.Hofmann@physik.stud.uni-erlangen.de>).
+ * 1.12: Remove CONFIG_SMP as the compiler will optimize
+ * the code away anyway (smp_num_cpus == 1 in UP)
+ * noted by Artur Skawina <skawina@geocities.com>.
+ * Make power off under SMP work again.
+ * Fix thinko with initial engaging of BIOS.
+ * Make sure power off only happens on CPU 0
+ * (Paul "Rusty" Russell <rusty@linuxcare.com>).
+ * Do error notification to user mode if BIOS calls fail.
+ * Move entrypoint offset fix to ...boot/setup.S
+ * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
+ * Remove smp-power-off. SMP users must now specify
+ * "apm=power-off" on the kernel command line. Suggested
+ * by Jim Avera <jima@hal.com>, modified by Alan Cox
+ * <alan@lxorguk.ukuu.org.uk>.
+ * Register the /proc/apm entry even on SMP so that
+ * scripts that check for it before doing power off
+ * work (Jim Avera <jima@hal.com>).
*
* APM 1.1 Reference:
*
@@ -106,11 +142,8 @@
* Intel Corporation, Microsoft Corporation. Advanced Power Management
* (APM) BIOS Interface Specification, Revision 1.2, February 1996.
*
- * [This document is available from Intel at:
- * http://www.intel.com/IAL/powermgm
- * or Microsoft at
- * http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
- * ]
+ * [This document is available from Microsoft at:
+ * http://www.microsoft.com/hwdev/busbios/amp_12.htm]
*/
#include <linux/config.h>
@@ -122,21 +155,44 @@
#include <linux/timer.h>
#include <linux/fcntl.h>
#include <linux/malloc.h>
-#include <linux/linkage.h>
#include <linux/stat.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/apm_bios.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/desc.h>
+#include <asm/softirq.h>
+
+/* 2.2-2.3 Compatability defines */
+#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL}
+#define __setup(x, y)
+#define module_init(x)
+#define set_current_state(a) current->state = (a)
+#define create_proc_info_entry(n, m, b, g) \
+ { \
+ struct proc_dir_entry *r = create_proc_entry(n, m, b); \
+ if (r) r->get_info = g; \
+ }
EXPORT_SYMBOL(apm_register_callback);
EXPORT_SYMBOL(apm_unregister_callback);
extern unsigned long get_cmos_time(void);
+extern void machine_real_restart(unsigned char *, int);
+
+extern void (*acpi_idle)(void);
+extern void (*acpi_power_off)(void);
+#ifdef CONFIG_MAGIC_SYSRQ
+extern void (*sysrq_power_off)(void);
+#endif
+#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
+extern int (*console_blank_hook)(int);
+#endif
/*
* The apm_bios device is one of the misc char devices.
@@ -144,51 +200,13 @@
*/
#define APM_MINOR_DEV 134
-/* Configurable options:
- *
- * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
- * This is necessary on the NEC Versa M series, which generates these when
- * resuming from SYSTEM SUSPEND. However, enabling this on other laptops
- * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
- * USER SUSPEND is ignored -- this may prevent the APM driver from updating
- * the system time on a RESUME.
- *
- * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of
- * the specification: "When disabled, the APM BIOS does not automatically
- * power manage devices, enter the Standby State, enter the Suspend State,
- * or take power saving steps in response to CPU Idle calls." This driver
- * will make CPU Idle calls when Linux is idle (unless this feature is
- * turned off -- see below). This should always save battery power, but
- * more complicated APM features will be dependent on your BIOS
- * implementation. You may need to turn this option off if your computer
- * hangs at boot time when using APM support, or if it beeps continuously
- * instead of suspending. Turn this off if you have a NEC UltraLite Versa
- * 33/C or a Toshiba T400CDT. This is off by default since most machines
- * do fine without this feature.
- *
- * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
- * idle loop. On some machines, this can activate improved power savings,
- * such as a slowed CPU clock rate, when the machine is idle. These idle
- * call is made after the idle loop has run for some length of time (e.g.,
- * 333 mS). On some machines, this will cause a hang at boot time or
- * whenever the CPU becomes idle.
- *
- * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some
- * laptops can use this to turn of the LCD backlight when the VC screen
- * blanker blanks the screen. Note that this is only used by the VC screen
- * blanker, and probably won't turn off the backlight when using X11. Some
- * problems have been reported when using this option with gpm (if you'd
- * like to debug this, please do so).
- *
- * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
- * on returning multiple suspend/standby events whenever one occurs. We
- * really only need one at a time, so just ignore any beyond the first.
- * This is probably safe on most laptops.
- *
- * If you are debugging the APM support for your laptop, note that code for
- * all of these options is contained in this file, so you can #define or
- * #undef these on the next line to avoid recompiling the whole kernel.
+/*
+ * See Documentation/Config.help for the configuration options.
*
+ * Various options can be changed at boot time as follows:
+ * apm=on/off enable/disable APM
+ * [no-]debug log some debugging messages
+ * [no-]power-off power off on shutdown
*/
/* KNOWN PROBLEM MACHINES:
@@ -207,11 +225,6 @@
*/
/*
- * Define to have debug messages.
- */
-#undef APM_DEBUG
-
-/*
* Define to always call the APM BIOS busy routine even if the clock was
* not slowed by the idle routine.
*/
@@ -255,7 +268,7 @@
/*
* If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then
- * ignore suspend events for this amount of time
+ * ignore suspend events for this amount of time after a resume
*/
#define BOUNCE_INTERVAL (3 * HZ)
@@ -266,25 +279,32 @@
__asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
/*
- * Forward declarations
+ * Maximum number of events stored
*/
-static void suspend(void);
-static void standby(void);
-static void set_time(void);
-
-static void check_events(void);
-static void do_apm_timer(unsigned long);
-
-static int do_open(struct inode *, struct file *);
-static int do_release(struct inode *, struct file *);
-static ssize_t do_read(struct file *, char *, size_t , loff_t *);
-static unsigned int do_poll(struct file *, poll_table *);
-static int do_ioctl(struct inode *, struct file *, u_int, u_long);
+#define APM_MAX_EVENTS 20
-static int apm_get_info(char *, char **, off_t, int, int);
+/*
+ * The per-file APM data
+ */
+struct apm_user {
+ int magic;
+ struct apm_user * next;
+ int suser: 1;
+ int suspend_wait: 1;
+ int suspend_result;
+ int suspends_pending;
+ int standbys_pending;
+ int suspends_read;
+ int standbys_read;
+ int event_head;
+ int event_tail;
+ apm_event_t events[APM_MAX_EVENTS];
+};
-extern int apm_register_callback(int (*)(apm_event_t));
-extern void apm_unregister_callback(int (*)(apm_event_t));
+/*
+ * The magic number in apm_user
+ */
+#define APM_BIOS_MAGIC 0x4101
/*
* Local variables
@@ -293,8 +313,6 @@
unsigned long offset;
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
@@ -313,15 +331,20 @@
#endif
static int debug = 0;
static int apm_disabled = 0;
+#ifdef CONFIG_SMP
+static int power_off_enabled = 0;
+#else
+static int power_off_enabled = 1;
+#endif
-static struct wait_queue * process_list = NULL;
-static struct apm_bios_struct * user_list = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
+static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
+static struct apm_user * user_list = NULL;
static struct timer_list apm_timer;
-static char driver_version[] = "1.9"; /* no spaces */
+static char driver_version[] = "1.12"; /* no spaces */
-#ifdef APM_DEBUG
static char * apm_event_name[] = {
"system standby",
"system suspend",
@@ -338,28 +361,6 @@
};
#define NR_APM_EVENT_NAME \
(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
-#endif
-
-static struct file_operations apm_bios_fops = {
- NULL, /* lseek */
- do_read,
- NULL, /* write */
- NULL, /* readdir */
- do_poll,
- do_ioctl,
- NULL, /* mmap */
- do_open,
- NULL, /* flush */
- do_release,
- NULL, /* fsync */
- NULL /* fasync */
-};
-
-static struct miscdevice apm_device = {
- APM_MINOR_DEV,
- "apm",
- &apm_bios_fops
-};
typedef struct callback_list_t {
int (* callback)(apm_event_t);
@@ -416,9 +417,10 @@
# define APM_DO_CLI
#endif
#ifdef APM_ZERO_SEGS
+# define APM_DECL_SEGS \
+ unsigned int saved_fs; unsigned int saved_gs;
# define APM_DO_SAVE_SEGS \
- savesegment(fs, saved_fs); \
- savesegment(gs, saved_gs)
+ savesegment(fs, saved_fs); savesegment(gs, saved_gs)
# define APM_DO_ZERO_SEGS \
"pushl %%ds\n\t" \
"pushl %%es\n\t" \
@@ -431,25 +433,28 @@
"popl %%es\n\t" \
"popl %%ds\n\t"
# define APM_DO_RESTORE_SEGS \
- loadsegment(fs, saved_fs); \
- loadsegment(gs, saved_gs)
+ loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)
#else
+# define APM_DECL_SEGS
# define APM_DO_SAVE_SEGS
# define APM_DO_ZERO_SEGS
# define APM_DO_POP_SEGS
# define APM_DO_RESTORE_SEGS
#endif
-static u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
+static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in,
u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
{
- unsigned int saved_fs;
- unsigned int saved_gs;
+ APM_DECL_SEGS
unsigned long flags;
__save_flags(flags);
APM_DO_CLI;
APM_DO_SAVE_SEGS;
+ /*
+ * N.B. We do NOT need a cld after the BIOS call
+ * because we always save and restore the flags.
+ */
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
@@ -460,7 +465,7 @@
APM_DO_POP_SEGS
: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
"=S" (*esi)
- : "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
+ : "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
APM_DO_RESTORE_SEGS;
__restore_flags(flags);
@@ -471,12 +476,10 @@
* This version only returns one value (usually an error code)
*/
-static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in,
- u32 *eax)
+static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax)
{
u8 error;
- unsigned int saved_fs;
- unsigned int saved_gs;
+ APM_DECL_SEGS
unsigned long flags;
__save_flags(flags);
@@ -485,6 +488,10 @@
{
int cx, dx, si;
+ /*
+ * N.B. We do NOT need a cld after the BIOS call
+ * because we always save and restore the flags.
+ */
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
@@ -495,7 +502,7 @@
APM_DO_POP_SEGS
: "=a" (*eax), "=b" (error), "=c" (cx), "=d" (dx),
"=S" (si)
- : "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
+ : "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
}
APM_DO_RESTORE_SEGS;
@@ -503,11 +510,11 @@
return error;
}
-static int apm_driver_version(u_short *val)
+static int __init apm_driver_version(u_short *val)
{
u32 eax;
- if (apm_bios_call_simple(0x530e, 0, *val, &eax))
+ if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))
return (eax >> 8) & 0xff;
*val = eax;
return APM_SUCCESS;
@@ -520,7 +527,8 @@
u32 ecx;
u32 dummy;
- if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy))
+ if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx,
+ &dummy, &dummy))
return (eax >> 8) & 0xff;
*event = ebx;
if (apm_bios_info.version < 0x0102)
@@ -534,58 +542,122 @@
{
u32 eax;
- if (apm_bios_call_simple(0x5307, what, state, &eax))
+ if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax))
return (eax >> 8) & 0xff;
return APM_SUCCESS;
}
static int apm_set_power_state(u_short state)
{
- return set_power_state(0x0001, state);
+ return set_power_state(APM_DEVICE_ALL, state);
}
-void apm_power_off(void)
+#ifdef CONFIG_APM_CPU_IDLE
+static int apm_do_idle(void)
{
- /*
- * 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);
+ u32 dummy;
+
+ if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &dummy))
+ return 0;
+
+#ifdef ALWAYS_CALL_BUSY
+ clock_slowed = 1;
+#else
+ clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
+#endif
+ return 1;
}
-#ifdef CONFIG_APM_DISPLAY_BLANK
-/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
-static int apm_set_display_power_state(u_short state)
+static void apm_do_busy(void)
{
- int error;
+ u32 dummy;
- /* 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;
+ if (clock_slowed) {
+ (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy);
+ clock_slowed = 0;
+ }
+}
+
+extern int hlt_counter;
+
+static void apm_cpu_idle(void)
+{
+ while (!current->need_resched) {
+ if (!boot_cpu_data.hlt_works_ok)
+ continue;
+ if (hlt_counter)
+ continue;
+ /* If there is an error calling the idle routine,
+ we should hlt if possible. We need to check
+ need_resched again because an interrupt
+ may have occurred in apm_do_idle(). */
+ start_bh_atomic();
+ if (!apm_do_idle() && !current->need_resched)
+ asm volatile("sti ; hlt" : : : "memory");
+ end_bh_atomic();
+ if (current->need_resched)
+ break;
+ schedule();
+ }
+ apm_do_busy();
}
#endif
-#ifdef CONFIG_APM_DO_ENABLE
-static int __init apm_enable_power_management(void)
+#ifdef CONFIG_SMP
+static int apm_magic(void * unused)
+{
+ while (1)
+ schedule();
+}
+#endif
+
+static void apm_power_off(void)
+{
+#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
+ unsigned char po_bios_call[] = {
+ 0xb8, 0x00, 0x10, /* movw $0x1000,ax */
+ 0x8e, 0xd0, /* movw ax,ss */
+ 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
+ 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
+ 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
+ 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
+ 0xcd, 0x15 /* int $0x15 */
+ };
+#endif
+
+ /*
+ * This may be called on an SMP machine.
+ */
+#ifdef CONFIG_SMP
+ /* Many bioses don't like being called from CPU != 0 */
+ while (cpu_number_map[smp_processor_id()] != 0) {
+ kernel_thread(apm_magic, NULL,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
+ schedule();
+ }
+#endif
+#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
+ machine_real_restart(po_bios_call, sizeof(po_bios_call));
+#else
+ (void) apm_set_power_state(APM_STATE_OFF);
+#endif
+}
+
+static int apm_enable_power_management(int enable)
{
u32 eax;
- if (apm_bios_call_simple(0x5308,
- (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff,
- 1, &eax))
+ if ((enable == 0) && (apm_bios_info.flags & APM_BIOS_DISENGAGED))
+ return APM_NOT_ENGAGED;
+ if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,
+ enable, &eax))
return (eax >> 8) & 0xff;
- apm_bios_info.flags &= ~APM_BIOS_DISABLED;
+ if (enable)
+ apm_bios_info.flags &= ~APM_BIOS_DISABLED;
+ else
+ apm_bios_info.flags |= APM_BIOS_DISABLED;
return APM_SUCCESS;
}
-#endif
static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
{
@@ -595,7 +667,8 @@
u32 edx;
u32 dummy;
- if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy))
+ if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0,
+ &eax, &ebx, &ecx, &edx, &dummy))
return (eax >> 8) & 0xff;
*status = ebx;
*bat = ecx;
@@ -603,6 +676,7 @@
return APM_SUCCESS;
}
+#if 0
static int apm_get_battery_status(u_short which, u_short *status,
u_short *bat, u_short *life, u_short *nbat)
{
@@ -620,7 +694,7 @@
return apm_get_power_status(status, bat, life);
}
- if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax,
+ if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax,
&ebx, &ecx, &edx, &esi))
return (eax >> 8) & 0xff;
*status = ebx;
@@ -629,13 +703,23 @@
*nbat = esi;
return APM_SUCCESS;
}
+#endif
-static int __init apm_engage_power_management(u_short device)
+static int apm_engage_power_management(u_short device, int enable)
{
u32 eax;
- if (apm_bios_call_simple(0x530f, device, 1, &eax))
+ if ((enable == 0) && (device == APM_DEVICE_ALL)
+ && (apm_bios_info.flags & APM_BIOS_DISABLED))
+ return APM_DISABLED;
+ if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax))
return (eax >> 8) & 0xff;
+ if (device == APM_DEVICE_ALL) {
+ if (enable)
+ apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
+ else
+ apm_bios_info.flags |= APM_BIOS_DISENGAGED;
+ }
return APM_SUCCESS;
}
@@ -652,37 +736,28 @@
str, err);
}
-/* Called from console driver -- must make sure apm_enabled. */
-int apm_display_blank(void)
+#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
+static int apm_console_blank(int blank)
{
-#ifdef CONFIG_APM_DISPLAY_BLANK
int error;
+ u_short state;
- if (!apm_enabled)
- return 0;
- error = apm_set_display_power_state(APM_STATE_STANDBY);
+ state = blank ? APM_STATE_STANDBY : APM_STATE_READY;
+ /* Blank the first display device */
+ error = set_power_state(0x100, state);
+ if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) {
+ /* try to blank them all instead */
+ error = set_power_state(0x1ff, state);
+ if ((error != APM_SUCCESS) && (error != APM_NO_ERROR))
+ /* try to blank device one instead */
+ error = set_power_state(0x101, state);
+ }
if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
return 1;
- apm_error("set display standby", error);
-#endif
+ apm_error("set display", error);
return 0;
}
-
-/* Called from console driver -- must make sure apm_enabled. */
-int apm_display_unblank(void)
-{
-#ifdef CONFIG_APM_DISPLAY_BLANK
- int error;
-
- if (!apm_enabled)
- return 0;
- error = apm_set_display_power_state(APM_STATE_READY);
- if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
- return 1;
- apm_error("set display ready", error);
#endif
- return 0;
-}
int apm_register_callback(int (*callback)(apm_event_t))
{
@@ -710,23 +785,23 @@
kfree_s(old, sizeof(callback_list_t));
}
-static int queue_empty(struct apm_bios_struct * as)
+static int queue_empty(struct apm_user *as)
{
return as->event_head == as->event_tail;
}
-static apm_event_t get_queued_event(struct apm_bios_struct * as)
+static apm_event_t get_queued_event(struct apm_user *as)
{
as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
return as->events[as->event_tail];
}
-static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
+static void queue_event(apm_event_t event, struct apm_user *sender)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
if (user_list == NULL)
- return 0;
+ return;
for (as = user_list; as != NULL; as = as->next) {
if (as == sender)
continue;
@@ -734,10 +809,8 @@
if (as->event_head == as->event_tail) {
static int notified;
- if (notified == 0) {
+ if (notified++ == 0)
printk(KERN_ERR "apm: an event queue overflowed\n");
- notified = 1;
- }
as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
}
as->events[as->event_head] = event;
@@ -757,29 +830,26 @@
break;
}
}
- wake_up_interruptible(&process_list);
- return 1;
+ wake_up_interruptible(&apm_waitqueue);
}
static void set_time(void)
{
unsigned long flags;
- if (!got_clock_diff) /* Don't know time zone, can't set clock */
- return;
-
- save_flags(flags);
- cli();
- CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
- restore_flags(flags);
+ if (got_clock_diff) { /* Must know time zone in order to set clock */
+ save_flags(flags);
+ cli();
+ CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
+ restore_flags(flags);
+ }
}
-static void suspend(void)
+static void get_time_diff(void)
{
+#ifndef CONFIG_APM_RTC_IS_GMT
unsigned long flags;
- int err;
-#ifndef CONFIG_APM_RTC_IS_GMT
/*
* Estimate time zone so that set_time can update the clock
*/
@@ -790,11 +860,13 @@
got_clock_diff = 1;
restore_flags(flags);
#endif
+}
- err = apm_set_power_state(APM_STATE_SUSPEND);
- if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
- apm_error("suspend", err);
+static void reinit_timer(void)
+{
#ifdef INIT_TIMER_AFTER_SUSPEND
+ unsigned long flags;
+
save_flags(flags);
cli();
/* set the clock to 100 Hz */
@@ -806,13 +878,34 @@
udelay(10);
restore_flags(flags);
#endif
+}
+
+static int suspend(void)
+{
+ int err;
+ int ret;
+ struct apm_user *as;
+
+ get_time_diff();
+ err = apm_set_power_state(APM_STATE_SUSPEND);
+ reinit_timer();
set_time();
+ ret = (err == APM_SUCCESS) || (err == APM_NO_ERROR);
+ if (!ret)
+ apm_error("suspend", err);
+ for (as = user_list; as != NULL; as = as->next) {
+ as->suspend_wait = 0;
+ as->suspend_result = (ret ? 0 : -EIO);
+ }
+ wake_up_interruptible(&apm_suspend_waitqueue);
+ return err;
}
static void standby(void)
{
int err;
+ get_time_diff();
err = apm_set_power_state(APM_STATE_STANDBY);
if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
apm_error("standby", err);
@@ -837,8 +930,8 @@
return 0;
}
-static void send_event(apm_event_t event, apm_event_t undo,
- struct apm_bios_struct *sender)
+static int send_event(apm_event_t event, apm_event_t undo,
+ struct apm_user *sender)
{
callback_list_t * call;
callback_list_t * fix;
@@ -849,11 +942,12 @@
fix->callback(undo);
if (apm_bios_info.version > 0x100)
apm_set_power_state(APM_STATE_REJECT);
- return;
+ return 0;
}
}
queue_event(event, sender);
+ return 1;
}
static void check_events(void)
@@ -865,14 +959,14 @@
#endif
while ((event = get_event()) != 0) {
-#ifdef APM_DEBUG
- if (event <= NR_APM_EVENT_NAME)
- printk(KERN_DEBUG "apm: received %s notify\n",
- apm_event_name[event - 1]);
- else
- printk(KERN_DEBUG "apm: received unknown "
- "event 0x%02x\n", event);
-#endif
+ if (debug) {
+ if (event <= NR_APM_EVENT_NAME)
+ printk(KERN_DEBUG "apm: received %s notify\n",
+ apm_event_name[event - 1]);
+ else
+ printk(KERN_DEBUG "apm: received unknown "
+ "event 0x%02x\n", event);
+ }
#ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
if (ignore_bounce
&& ((jiffies - last_resume) > BOUNCE_INTERVAL))
@@ -883,12 +977,15 @@
case APM_USER_STANDBY:
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
if (waiting_for_resume)
- return;
- waiting_for_resume = 1;
+ break;
+#endif
+ if (send_event(event, APM_STANDBY_RESUME, NULL)) {
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+ waiting_for_resume = 1;
#endif
- send_event(event, APM_STANDBY_RESUME, NULL);
- if (standbys_pending <= 0)
- standby();
+ if (standbys_pending <= 0)
+ standby();
+ }
break;
case APM_USER_SUSPEND:
@@ -904,12 +1001,15 @@
#endif
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
if (waiting_for_resume)
- return;
- waiting_for_resume = 1;
+ break;
#endif
- send_event(event, APM_NORMAL_RESUME, NULL);
- if (suspends_pending <= 0)
- suspend();
+ if (send_event(event, APM_NORMAL_RESUME, NULL)) {
+#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
+ waiting_for_resume = 1;
+#endif
+ if (suspends_pending <= 0)
+ (void) suspend();
+ }
break;
case APM_NORMAL_RESUME:
@@ -926,9 +1026,9 @@
send_event(event, 0, NULL);
break;
+ case APM_CAPABILITY_CHANGE:
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
- case APM_CAPABILITY_CHANGE:
send_event(event, 0, NULL);
break;
@@ -937,74 +1037,33 @@
break;
case APM_CRITICAL_SUSPEND:
- suspend();
+ (void) suspend();
break;
}
}
}
-static void do_apm_timer(unsigned long unused)
+static void apm_event_handler(unsigned long unused)
{
- int err;
-
- static int pending_count = 0;
+ static int pending_count = 4;
+ int err;
- if (((standbys_pending > 0) || (suspends_pending > 0))
- && (apm_bios_info.version > 0x100)
- && (pending_count-- <= 0)) {
+ if ((standbys_pending > 0) || (suspends_pending > 0)) {
+ if ((apm_bios_info.version > 0x100) && (pending_count-- < 0)) {
+ pending_count = 4;
+ err = apm_set_power_state(APM_STATE_BUSY);
+ if (err)
+ apm_error("busy", err);
+ }
+ } else
pending_count = 4;
-
- err = apm_set_power_state(APM_STATE_BUSY);
- if (err)
- apm_error("busy", err);
- }
-
- if (!(((standbys_pending > 0) || (suspends_pending > 0))
- && (apm_bios_info.version == 0x100)))
- check_events();
-
+ check_events();
init_timer(&apm_timer);
apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
add_timer(&apm_timer);
}
-/* Called from sys_idle, must make sure apm_enabled. */
-int apm_do_idle(void)
-{
-#ifdef CONFIG_APM_CPU_IDLE
- u32 dummy;
-
- if (!apm_enabled)
- return 0;
-
- if (apm_bios_call_simple(0x5305, 0, 0, &dummy))
- return 0;
-
- clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
- return 1;
-#else
- return 0;
-#endif
-}
-
-/* Called from sys_idle, must make sure apm_enabled. */
-void apm_do_busy(void)
-{
-#ifdef CONFIG_APM_CPU_IDLE
- u32 dummy;
-
- if (apm_enabled
-#ifndef ALWAYS_CALL_BUSY
- && clock_slowed
-#endif
- ) {
- (void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
- clock_slowed = 0;
- }
-#endif
-}
-
-static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
+static int check_apm_user(struct apm_user *as, const char *func)
{
if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
printk(KERN_ERR "apm: %s passed bad filp", func);
@@ -1015,33 +1074,37 @@
static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
int i;
apm_event_t event;
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
as = fp->private_data;
- if (check_apm_bios_struct(as, "read"))
+ if (check_apm_user(as, "read"))
return -EIO;
if (count < sizeof(apm_event_t))
return -EINVAL;
if (queue_empty(as)) {
if (fp->f_flags & O_NONBLOCK)
return -EAGAIN;
- add_wait_queue(&process_list, &wait);
+ add_wait_queue(&apm_waitqueue, &wait);
repeat:
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
if (queue_empty(as) && !signal_pending(current)) {
schedule();
goto repeat;
}
current->state = TASK_RUNNING;
- remove_wait_queue(&process_list, &wait);
+ remove_wait_queue(&apm_waitqueue, &wait);
}
i = count;
while ((i >= sizeof(event)) && !queue_empty(as)) {
event = get_queued_event(as);
- copy_to_user(buf, &event, sizeof(event));
+ if (copy_to_user(buf, &event, sizeof(event))) {
+ if (i < count)
+ break;
+ return -EFAULT;
+ }
switch (event) {
case APM_SYS_SUSPEND:
case APM_USER_SUSPEND:
@@ -1065,12 +1128,12 @@
static unsigned int do_poll(struct file *fp, poll_table * wait)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
as = fp->private_data;
- if (check_apm_bios_struct(as, "select"))
+ if (check_apm_user(as, "poll"))
return 0;
- poll_wait(fp, &process_list, wait);
+ poll_wait(fp, &apm_waitqueue, wait);
if (!queue_empty(as))
return POLLIN | POLLRDNORM;
return 0;
@@ -1079,10 +1142,11 @@
static int do_ioctl(struct inode * inode, struct file *filp,
u_int cmd, u_long arg)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
+ DECLARE_WAITQUEUE(wait, current);
as = filp->private_data;
- if (check_apm_bios_struct(as, "ioctl"))
+ if (check_apm_user(as, "ioctl"))
return -EIO;
if (!as->suser)
return -EPERM;
@@ -1092,9 +1156,8 @@
as->standbys_read--;
as->standbys_pending--;
standbys_pending--;
- }
- else
- send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
+ } else if (!send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as))
+ return -EAGAIN;
if (standbys_pending <= 0)
standby();
break;
@@ -1103,11 +1166,25 @@
as->suspends_read--;
as->suspends_pending--;
suspends_pending--;
+ } else if (!send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as))
+ return -EAGAIN;
+ if (suspends_pending <= 0) {
+ if (!suspend())
+ return -EIO;
+ } else {
+ as->suspend_wait = 1;
+ add_wait_queue(&apm_suspend_waitqueue, &wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((as->suspend_wait == 0)
+ || signal_pending(current))
+ break;
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&apm_suspend_waitqueue, &wait);
+ return as->suspend_result;
}
- else
- send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
- if (suspends_pending <= 0)
- suspend();
break;
default:
return -EINVAL;
@@ -1117,12 +1194,12 @@
static int do_release(struct inode * inode, struct file * filp)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
as = filp->private_data;
- filp->private_data = NULL;
- if (check_apm_bios_struct(as, "release"))
+ if (check_apm_user(as, "release"))
return 0;
+ filp->private_data = NULL;
if (as->standbys_pending > 0) {
standbys_pending -= as->standbys_pending;
if (standbys_pending <= 0)
@@ -1131,12 +1208,12 @@
if (as->suspends_pending > 0) {
suspends_pending -= as->suspends_pending;
if (suspends_pending <= 0)
- suspend();
+ (void) suspend();
}
if (user_list == as)
user_list = as->next;
else {
- struct apm_bios_struct * as1;
+ struct apm_user * as1;
for (as1 = user_list;
(as1 != NULL) && (as1->next != as);
@@ -1153,9 +1230,9 @@
static int do_open(struct inode * inode, struct file * filp)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
- as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
+ as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
printk(KERN_ERR "apm: cannot allocate struct of size %d bytes",
sizeof(*as));
@@ -1179,13 +1256,12 @@
return 0;
}
-int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
+static int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
{
char * p;
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;
@@ -1194,11 +1270,10 @@
int time_units = -1;
char *units = "?";
- if (!apm_enabled)
- return 0;
p = buf;
- if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
+ if ((smp_num_cpus == 1) &&
+ !(error = apm_get_power_status(&bx, &cx, &dx))) {
ac_line_status = (bx >> 8) & 0xff;
battery_status = bx & 0xff;
if ((cx & 0xff) != 0xff)
@@ -1216,7 +1291,7 @@
from the Get Power Status (0x0a) call unless otherwise noted.
0) Linux driver version (this will change if format changes)
- 1) APM BIOS Version. Usually 1.0 or 1.1.
+ 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
2) APM flags from APM Installation Check (0x00):
bit 0: APM_16_BIT_SUPPORT
bit 1: APM_32_BIT_SUPPORT
@@ -1226,13 +1301,14 @@
3) AC line status
0x00: Off-line
0x01: On-line
- 0x02: On backup power (APM BIOS 1.1 only)
+ 0x02: On backup power (BIOS >= 1.1 only)
0xff: Unknown
4) Battery status
0x00: High
0x01: Low
0x02: Critical
0x03: Charging
+ 0x04: Selected battery not present (BIOS >= 1.2 only)
0xff: Unknown
5) Battery flag
bit 0: High
@@ -1264,6 +1340,106 @@
return p - buf;
}
+static int __init apm(void *unused)
+{
+ unsigned short bx;
+ unsigned short cx;
+ unsigned short dx;
+ unsigned short error;
+ char * power_stat;
+ char * bat_stat;
+
+ if (apm_bios_info.version > 0x100) {
+ /*
+ * We only support BIOSs up to version 1.2
+ */
+ if (apm_bios_info.version > 0x0102)
+ apm_bios_info.version = 0x0102;
+ if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) {
+ /* Fall back to an APM 1.0 connection. */
+ apm_bios_info.version = 0x100;
+ }
+ }
+ if (debug && (smp_num_cpus == 1)) {
+ printk(KERN_INFO "apm: Connection version %d.%d\n",
+ (apm_bios_info.version >> 8) & 0xff,
+ apm_bios_info.version & 0xff);
+
+ error = apm_get_power_status(&bx, &cx, &dx);
+ if (error)
+ printk(KERN_INFO "apm: power status not available\n");
+ else {
+ switch ((bx >> 8) & 0xff) {
+ case 0: power_stat = "off line"; break;
+ case 1: power_stat = "on line"; break;
+ case 2: power_stat = "on backup power"; break;
+ default: power_stat = "unknown"; break;
+ }
+ switch (bx & 0xff) {
+ case 0: bat_stat = "high"; break;
+ case 1: bat_stat = "low"; break;
+ case 2: bat_stat = "critical"; break;
+ case 3: bat_stat = "charging"; break;
+ default: bat_stat = "unknown"; break;
+ }
+ 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(KERN_INFO
+ "apm: battery flag 0x%02x, battery life ",
+ (cx >> 8) & 0xff);
+ if (dx == 0xffff)
+ printk("unknown\n");
+ else
+ printk("%d %s\n", dx & 0x7fff,
+ (dx & 0x8000) ?
+ "minutes" : "seconds");
+ }
+ }
+ }
+
+#ifdef CONFIG_APM_DO_ENABLE
+ if (apm_bios_info.flags & APM_BIOS_DISABLED) {
+ /*
+ * This call causes my NEC UltraLite Versa 33/C to hang if it
+ * is booted with PM disabled but not in the docking station.
+ * Unfortunate ...
+ */
+ error = apm_enable_power_management(1);
+ if (error) {
+ apm_error("enable power management", error);
+ return -1;
+ }
+ }
+#endif
+ if ((apm_bios_info.flags & APM_BIOS_DISENGAGED)
+ && (apm_bios_info.version > 0x0100)) {
+ error = apm_engage_power_management(APM_DEVICE_ALL, 1);
+ if (error) {
+ apm_error("engage power management", error);
+ return -1;
+ }
+ }
+
+ /* Install our power off handler.. */
+ if (power_off_enabled)
+ acpi_power_off = apm_power_off;
+#ifdef CONFIG_MAGIC_SYSRQ
+ sysrq_power_off = apm_power_off;
+#endif
+#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
+ if (smp_num_cpus == 1)
+ console_blank_hook = apm_console_blank;
+#endif
+
+ return 0;
+}
+
void __init apm_setup(char *str, int *dummy)
{
int invert;
@@ -1278,27 +1454,37 @@
str += 3;
if (strncmp(str, "debug", 5) == 0)
debug = !invert;
- if (strncmp(str, "smp-power-off", 13) == 0)
- smp_hack = !invert;
+ if (strncmp(str, "power-off", 9) == 0)
+ power_off_enabled = !invert;
str = strchr(str, ',');
if (str != NULL)
str += strspn(str, ", \t");
}
}
-void __init apm_bios_init(void)
-{
- unsigned short bx;
- unsigned short cx;
- unsigned short dx;
- unsigned short error;
- char * power_stat;
- char * bat_stat;
- static struct proc_dir_entry *ent;
+__setup("apm=", apm_setup);
+
+static struct file_operations apm_bios_fops = {
+ read: do_read,
+ poll: do_poll,
+ ioctl: do_ioctl,
+ open: do_open,
+ release: do_release,
+};
+static struct miscdevice apm_device = {
+ APM_MINOR_DEV,
+ "apm",
+ &apm_bios_fops
+};
+
+#define APM_INIT_ERROR_RETURN return
+
+void __init apm_init(void)
+{
if (apm_bios_info.version == 0) {
printk(KERN_INFO "apm: BIOS not found.\n");
- return;
+ APM_INIT_ERROR_RETURN;
}
printk(KERN_INFO
"apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
@@ -1308,7 +1494,7 @@
driver_version);
if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
printk(KERN_INFO "apm: no 32 bit BIOS support\n");
- return;
+ APM_INIT_ERROR_RETURN;
}
/*
@@ -1337,7 +1523,11 @@
if (apm_disabled) {
printk(KERN_NOTICE "apm: disabled on user request.\n");
- return;
+ APM_INIT_ERROR_RETURN;
+ }
+ if ((smp_num_cpus > 1) && !power_off_enabled) {
+ printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
+ APM_INIT_ERROR_RETURN;
}
/*
@@ -1359,18 +1549,16 @@
set_base(gdt[APM_DS >> 3],
__va((unsigned long)apm_bios_info.dseg << 4));
#ifndef APM_RELAX_SEGMENTS
- if (apm_bios_info.version == 0x100)
+ if (apm_bios_info.version == 0x100) {
#endif
- {
/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
_set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
/* For some unknown machine. */
_set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
/* For the DEC Hinote Ultra CT475 (and others?) */
_set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
- }
#ifndef APM_RELAX_SEGMENTS
- else {
+ } else {
_set_limit((char *)&gdt[APM_CS >> 3],
(apm_bios_info.cseg_len - 1) & 0xffff);
_set_limit((char *)&gdt[APM_CS_16 >> 3],
@@ -1379,98 +1567,27 @@
(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
- */
- if (apm_bios_info.version > 0x0102)
- apm_bios_info.version = 0x0102;
- if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) {
- /* Fall back to an APM 1.0 connection. */
- apm_bios_info.version = 0x100;
- }
- }
- if (debug) {
- printk(KERN_INFO "apm: Connection version %d.%d\n",
- (apm_bios_info.version >> 8) & 0xff,
- apm_bios_info.version & 0xff );
- error = apm_get_power_status(&bx, &cx, &dx);
- if (error)
- printk(KERN_INFO "apm: power status not available\n");
- else {
- switch ((bx >> 8) & 0xff) {
- case 0: power_stat = "off line"; break;
- case 1: power_stat = "on line"; break;
- case 2: power_stat = "on backup power"; break;
- default: power_stat = "unknown"; break;
- }
- switch (bx & 0xff) {
- case 0: bat_stat = "high"; break;
- case 1: bat_stat = "low"; break;
- case 2: bat_stat = "critical"; break;
- case 3: bat_stat = "charging"; break;
- default: bat_stat = "unknown"; break;
- }
- 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(KERN_INFO
- "apm: battery flag 0x%02x, battery life ",
- (cx >> 8) & 0xff);
- if (dx == 0xffff)
- printk("unknown\n");
- else
- printk("%d %s\n", dx & 0x7fff,
- (dx & 0x8000) ?
- "minutes" : "seconds");
- }
- }
- }
+ apm(0);
-#ifdef CONFIG_APM_DO_ENABLE
- if (apm_bios_info.flags & APM_BIOS_DISABLED) {
- /*
- * This call causes my NEC UltraLite Versa 33/C to hang if it
- * is booted with PM disabled but not in the docking station.
- * Unfortunate ...
- */
- error = apm_enable_power_management();
- if (error) {
- apm_error("enable power management", error);
- return;
- }
- }
-#endif
- if (((apm_bios_info.flags & APM_BIOS_DISABLED) == 0)
- && (apm_bios_info.version > 0x0100)) {
- if (apm_engage_power_management(0x0001) == APM_SUCCESS)
- apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
+ create_proc_info_entry("apm", 0, 0, apm_get_info);
+
+ if (smp_num_cpus > 1) {
+ printk(KERN_NOTICE
+ "apm: disabled - APM is not SMP safe (power off active).\n");
+ APM_INIT_ERROR_RETURN;
}
init_timer(&apm_timer);
- apm_timer.function = do_apm_timer;
+ apm_timer.function = apm_event_handler;
apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
add_timer(&apm_timer);
- ent = create_proc_entry("apm", 0, 0);
- if (ent != NULL)
- ent->get_info = apm_get_info;
-
misc_register(&apm_device);
- apm_enabled = 1;
+#ifdef CONFIG_APM_CPU_IDLE
+ acpi_idle = apm_cpu_idle;
+#endif
}
+
+module_init(apm_init)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)