patch-2.3.13 linux/arch/i386/kernel/apm.c
Next file: linux/arch/i386/kernel/bios32.c
Previous file: linux/arch/i386/kernel/Makefile
Back to the patch index
Back to the overall index
- Lines: 424
- Date:
Fri Aug 6 11:17:13 1999
- Orig file:
v2.3.12/linux/arch/i386/kernel/apm.c
- Orig date:
Thu May 13 23:22:05 1999
diff -u --recursive --new-file v2.3.12/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
@@ -273,7 +273,6 @@
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 *);
@@ -314,11 +313,9 @@
static int debug = 0;
static int apm_disabled = 0;
-static DECLARE_WAIT_QUEUE_HEAD(process_list);
+static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
static struct apm_bios_struct * user_list = NULL;
-static struct timer_list apm_timer;
-
static char driver_version[] = "1.9"; /* no spaces */
#ifdef APM_DEBUG
@@ -543,6 +540,50 @@
return set_power_state(0x0001, state);
}
+/*
+ * If no process has been interested in this
+ * CPU for some time, we want to wake up the
+ * power management thread - we probably want
+ * to conserve power.
+ */
+#define HARD_IDLE_TIMEOUT (HZ/3)
+
+/* This should wake up kapmd and ask it to slow the CPU */
+#define powermanagement_idle() do { } while (0)
+
+extern int hlt_counter;
+
+/*
+ * This is the idle thing.
+ */
+void apm_cpu_idle(void)
+{
+ unsigned int start_idle;
+
+ start_idle = jiffies;
+ while (1) {
+ if (!current->need_resched) {
+ if (jiffies - start_idle < HARD_IDLE_TIMEOUT) {
+ if (!current_cpu_data.hlt_works_ok)
+ continue;
+ if (hlt_counter)
+ continue;
+ asm volatile("sti ; hlt" : : : "memory");
+ continue;
+ }
+
+ /*
+ * Ok, do some power management - we've been idle for too long
+ */
+ powermanagement_idle();
+ }
+
+ schedule();
+ check_pgt_cache();
+ start_idle = jiffies;
+ }
+}
+
void apm_power_off(void)
{
/*
@@ -756,7 +797,7 @@
break;
}
}
- wake_up_interruptible(&process_list);
+ wake_up_interruptible(&apm_waitqueue);
return 1;
}
@@ -942,32 +983,39 @@
}
}
-static void do_apm_timer(unsigned long unused)
+/*
+ * This is the APM thread main loop.
+ */
+static void apm_mainloop(void)
{
- int err;
-
- static int pending_count = 0;
+ DECLARE_WAITQUEUE(wait, current);
+ apm_enabled = 1;
- if (((standbys_pending > 0) || (suspends_pending > 0))
- && (apm_bios_info.version > 0x100)
- && (pending_count-- <= 0)) {
- pending_count = 4;
+ add_wait_queue(&apm_waitqueue, &wait);
+ for (;;) {
+ static int pending_count = 0;
+ int err;
- err = apm_set_power_state(APM_STATE_BUSY);
- if (err)
- apm_error("busy", err);
- }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(APM_CHECK_TIMEOUT);
- if (!(((standbys_pending > 0) || (suspends_pending > 0))
- && (apm_bios_info.version == 0x100)))
- check_events();
+ if (((standbys_pending > 0) || (suspends_pending > 0))
+ && (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);
+ }
- init_timer(&apm_timer);
- apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
- add_timer(&apm_timer);
+ if (!(((standbys_pending > 0) || (suspends_pending > 0))
+ && (apm_bios_info.version == 0x100)))
+ check_events();
+ }
}
-/* Called from sys_idle, must make sure apm_enabled. */
+/* Called from cpu_idle, must make sure apm_enabled. */
int apm_do_idle(void)
{
#ifdef CONFIG_APM_CPU_IDLE
@@ -986,7 +1034,7 @@
#endif
}
-/* Called from sys_idle, must make sure apm_enabled. */
+/* Called from cpu_idle, must make sure apm_enabled. */
void apm_do_busy(void)
{
#ifdef CONFIG_APM_CPU_IDLE
@@ -1027,7 +1075,7 @@
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;
if (queue_empty(as) && !signal_pending(current)) {
@@ -1035,7 +1083,7 @@
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)) {
@@ -1069,7 +1117,7 @@
as = fp->private_data;
if (check_apm_bios_struct(as, "select"))
return 0;
- poll_wait(fp, &process_list, wait);
+ poll_wait(fp, &apm_waitqueue, wait);
if (!queue_empty(as))
return POLLIN | POLLRDNORM;
return 0;
@@ -1263,7 +1311,97 @@
return p - buf;
}
-void __init apm_setup(char *str, int *dummy)
+static int apm(void *unused)
+{
+ unsigned short bx;
+ unsigned short cx;
+ unsigned short dx;
+ unsigned short error;
+ char * power_stat;
+ char * bat_stat;
+
+ strcpy(current->comm, "kapmd");
+ sigfillset(¤t->blocked);
+
+ 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");
+ }
+ }
+ }
+
+#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 -1;
+ }
+ }
+#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;
+ }
+
+ apm_mainloop();
+ return 0;
+}
+
+static int __init apm_setup(char *str)
{
int invert;
@@ -1283,16 +1421,23 @@
if (str != NULL)
str += strspn(str, ", \t");
}
+ return 1;
}
-void __init apm_bios_init(void)
+__setup("apm=", apm_setup);
+
+/*
+ * Just start the APM thread. We do NOT want to do APM BIOS
+ * calls from anything but the APM thread, if for no other reason
+ * than the fact that we don't trust the APM BIOS. This way,
+ * most common APM BIOS problems that lead to protection errors
+ * etc will have at least some level of being contained...
+ *
+ * In short, if something bad happens, at least we have a choice
+ * of just killing the apm thread..
+ */
+static int __init apm_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;
if (apm_bios_info.version == 0) {
@@ -1339,6 +1484,15 @@
return;
}
+#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 -1;
+ }
+#endif
+
/*
* Set up a segment that references the real mode segment 0x40
* that extends up to the end of page zero (that we have reserved).
@@ -1378,92 +1532,6 @@
(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");
- }
- }
- }
-
-#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;
- }
-
- init_timer(&apm_timer);
- apm_timer.function = do_apm_timer;
- apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
- add_timer(&apm_timer);
ent = create_proc_entry("apm", 0, 0);
if (ent != NULL)
@@ -1471,5 +1539,7 @@
misc_register(&apm_device);
- apm_enabled = 1;
+ kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
}
+
+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)