Import 2.1.55pre1
[davej-history.git] / drivers / char / apm_bios.c
blob26bd7799a8d2398810bf2e7ca72ed8f1a1057d3a
1 /* -*- linux-c -*-
2 * APM BIOS driver for Linux
3 * Copyright 1994, 1995, 1996 Stephen Rothwell
4 * (Stephen.Rothwell@canb.auug.org.au)
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * $Id: apm_bios.c,v 0.22 1995/03/09 14:12:02 sfr Exp $
18 * October 1995, Rik Faith (faith@cs.unc.edu):
19 * Minor enhancements and updates (to the patch set) for 1.3.x
20 * Documentation
21 * January 1996, Rik Faith (faith@cs.unc.edu):
22 * Make /proc/apm easy to format (bump driver version)
23 * March 1996, Rik Faith (faith@cs.unc.edu):
24 * Prohibit APM BIOS calls unless apm_enabled.
25 * (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
26 * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
27 * Version 1.0 and 1.1
28 * May 1996, Version 1.2
30 * History:
31 * 0.6b: first version in official kernel, Linux 1.3.46
32 * 0.7: changed /proc/apm format, Linux 1.3.58
33 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
34 * 0.9: only call bios if bios is present, Linux 1.3.72
35 * 1.0: use fixed device number, consolidate /proc/apm into this file,
36 * Linux 1.3.85
37 * 1.1: support user-space standby and suspend, power off after system
38 * halted, Linux 1.3.98
39 * 1.2: When resetting RTC after resume, take care so that the the time
40 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
41 * <jtoth@princeton.edu>); improve interaction between
42 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
43 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
44 * levels to the printk calls. APM is not defined for SMP machines.
45 * The new replacment for it is, but Linux doesn't yet support this.
46 * Alan Cox Linux 2.1.55
48 * Reference:
50 * Intel Corporation, Microsoft Corporation. Advanced Power Management
51 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
52 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
54 * [This document is available free from Intel by calling 800.628.8686 (fax
55 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
56 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
57 * available from Microsoft by calling 206.882.8080.]
61 #include <linux/config.h>
62 #include <linux/module.h>
64 #include <asm/system.h>
65 #include <asm/uaccess.h>
66 #include <asm/poll.h>
68 #include <linux/types.h>
69 #include <linux/stddef.h>
70 #include <linux/timer.h>
71 #include <linux/fcntl.h>
72 #include <linux/malloc.h>
73 #include <linux/linkage.h>
74 #ifdef CONFIG_PROC_FS
75 #include <linux/stat.h>
76 #include <linux/proc_fs.h>
77 #endif
78 #include <linux/miscdevice.h>
79 #include <linux/apm_bios.h>
80 #include <linux/init.h>
82 EXPORT_SYMBOL(apm_register_callback);
83 EXPORT_SYMBOL(apm_unregister_callback);
85 extern unsigned long get_cmos_time(void);
88 * The apm_bios device is one of the misc char devices.
89 * This is its minor number.
91 #define APM_MINOR_DEV 134
93 /* Configurable options:
95 * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
96 * This is necessary on the NEC Versa M series, which generates these when
97 * resuming from SYSTEM SUSPEND. However, enabling this on other laptops
98 * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
99 * USER SUSPEND is ignored -- this may prevent the APM driver from updating
100 * the system time on a RESUME.
102 * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of
103 * the specification: "When disabled, the APM BIOS does not automatically
104 * power manage devices, enter the Standby State, enter the Suspend State,
105 * or take power saving steps in response to CPU Idle calls." This driver
106 * will make CPU Idle calls when Linux is idle (unless this feature is
107 * turned off -- see below). This should always save battery power, but
108 * more complicated APM features will be dependent on your BIOS
109 * implementation. You may need to turn this option off if your computer
110 * hangs at boot time when using APM support, or if it beeps continuously
111 * instead of suspending. Turn this off if you have a NEC UltraLite Versa
112 * 33/C or a Toshiba T400CDT. This is off by default since most machines
113 * do fine without this feature.
115 * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
116 * idle loop. On some machines, this can activate improved power savings,
117 * such as a slowed CPU clock rate, when the machine is idle. These idle
118 * call is made after the idle loop has run for some length of time (e.g.,
119 * 333 mS). On some machines, this will cause a hang at boot time or
120 * whenever the CPU becomes idle.
122 * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some
123 * laptops can use this to turn of the LCD backlight when the VC screen
124 * blanker blanks the screen. Note that this is only used by the VC screen
125 * blanker, and probably won't turn off the backlight when using X11. Some
126 * problems have been reported when using this option with gpm (if you'd
127 * like to debug this, please do so).
129 * If you are debugging the APM support for your laptop, note that code for
130 * all of these options is contained in this file, so you can #define or
131 * #undef these on the next line to avoid recompiling the whole kernel.
135 /* KNOWN PROBLEM MACHINES:
137 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
138 * [Confirmed by TI representative]
139 * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
140 * [Confirmed by BIOS disassembly]
141 * P: Toshiba 1950S: battery life information only gets updated after resume
143 * Legend: U = unusable with APM patches
144 * P = partially usable with APM patches
148 * Define to have debug messages.
150 #undef APM_DEBUG
153 * Define to always call the APM BIOS busy routine even if the clock was
154 * not slowed by the idle routine.
156 #define ALWAYS_CALL_BUSY
159 * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
160 * should turn interrupts on before it does a 'hlt').
162 #define APM_NOINTS
165 * Define to make the APM BIOS calls zero all data segment registers (do
166 * that if an incorrect BIOS implementation will cause a kernel panic if it
167 * tries to write to arbitrary memory).
169 #define APM_ZERO_SEGS
172 * Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is
173 * supposed to provide limit information that it recognizes. Many machines
174 * do this correctly, but many others do not restrict themselves to their
175 * claimed limit. When this happens, they will cause a segmentation
176 * violation in the kernel at boot time. Most BIOS's, however, will
177 * respect a 64k limit, so we use that. If you want to be pedantic and
178 * hold your BIOS to its claims, then undefine this.
180 #define APM_RELAX_SEGMENTS
183 * Need to poll the APM BIOS every second
185 #define APM_CHECK_TIMEOUT (HZ)
188 * These are the actual BIOS calls in assembler. Depending on
189 * APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here! Not
190 * only are interrupts disabled, but all the segment registers (except SS)
191 * are saved and zeroed this means that if the BIOS tries to reference any
192 * data without explicitly loading the segment registers, the kernel will
193 * fault immediately rather than have some unforeseen circumstances for the
194 * rest of the kernel. And it will be very obvious! :-) Doing this
195 * depends on CS referring to the same physical memory as DS so that DS can
196 * be zeroed before the call. Unfortunately, we can't do anything about the
197 * stack segment/pointer. Also, we tell the compiler that everything could
198 * change.
200 #ifdef APM_NOINTS
201 # define APM_DO_CLI "cli\n\t"
202 #else
203 # define APM_DO_CLI
204 #endif
205 #ifdef APM_ZERO_SEGS
206 # define APM_DO_ZERO_SEGS \
207 "pushl %%ds\n\t" \
208 "pushl %%es\n\t" \
209 "pushl %%fs\n\t" \
210 "pushl %%gs\n\t" \
211 "xorl %%edx, %%edx\n\t" \
212 "mov %%dx, %%ds\n\t" \
213 "mov %%dx, %%es\n\t" \
214 "mov %%dx, %%fs\n\t" \
215 "mov %%dx, %%gs\n\t"
216 # define APM_DO_RESTORE_SEGS \
217 "popl %%gs\n\t" \
218 "popl %%fs\n\t" \
219 "popl %%es\n\t" \
220 "popl %%ds\n\t"
221 #else
222 # define APM_DO_ZERO_SEGS
223 # define APM_DO_RESTORE_SEGS
224 #endif
226 #define APM_BIOS_CALL(error_reg) \
227 __asm__ __volatile__( \
228 APM_DO_ZERO_SEGS \
229 "pushfl\n\t" \
230 APM_DO_CLI \
231 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
232 "setc %%" # error_reg "\n\t" \
233 "popfl\n\t" \
234 APM_DO_RESTORE_SEGS
235 #define APM_BIOS_CALL_END \
236 : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
238 #ifdef CONFIG_APM_CPU_IDLE
239 #define APM_SET_CPU_IDLE(error) \
240 APM_BIOS_CALL(al) \
241 : "=a" (error) \
242 : "a" (0x5305) \
243 APM_BIOS_CALL_END
244 #endif
246 #define APM_SET_CPU_BUSY(error) \
247 APM_BIOS_CALL(al) \
248 : "=a" (error) \
249 : "a" (0x5306) \
250 APM_BIOS_CALL_END
252 #define APM_SET_POWER_STATE(state, error) \
253 APM_BIOS_CALL(al) \
254 : "=a" (error) \
255 : "a" (0x5307), "b" (0x0001), "c" (state) \
256 APM_BIOS_CALL_END
258 #ifdef CONFIG_APM_DISPLAY_BLANK
259 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
260 APM_BIOS_CALL(al) \
261 : "=a" (error) \
262 : "a" (0x5307), "b" (0x01ff), "c" (state) \
263 APM_BIOS_CALL_END
264 #endif
266 #ifdef CONFIG_APM_DO_ENABLE
267 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
268 APM_BIOS_CALL(al) \
269 : "=a" (error) \
270 : "a" (0x5308), "b" (device), "c" (1) \
271 APM_BIOS_CALL_END
272 #endif
274 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
275 APM_BIOS_CALL(al) \
276 : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
277 : "a" (0x530a), "b" (1) \
278 APM_BIOS_CALL_END
280 #define APM_GET_EVENT(event, error) \
281 APM_BIOS_CALL(al) \
282 : "=a" (error), "=b" (event) \
283 : "a" (0x530b) \
284 APM_BIOS_CALL_END
286 #define APM_DRIVER_VERSION(ver, ax, error) \
287 APM_BIOS_CALL(bl) \
288 : "=a" (ax), "=b" (error) \
289 : "a" (0x530e), "b" (0), "c" (ver) \
290 APM_BIOS_CALL_END
292 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
293 APM_BIOS_CALL(al) \
294 : "=a" (error) \
295 : "a" (0x530f), "b" (device), "c" (1) \
296 APM_BIOS_CALL_END
299 * Forward declarations
301 static void suspend(void);
302 static void standby(void);
303 static void set_time(void);
305 static void check_events(void);
306 static void do_apm_timer(unsigned long);
308 static int do_open(struct inode *, struct file *);
309 static int do_release(struct inode *, struct file *);
310 static long do_read(struct inode *, struct file *, char *, unsigned long);
311 static unsigned int do_poll(struct file *, poll_table *);
312 static int do_ioctl(struct inode *, struct file *, u_int, u_long);
314 #ifdef CONFIG_PROC_FS
315 static int apm_get_info(char *, char **, off_t, int, int);
316 #endif
318 extern int apm_register_callback(int (*)(apm_event_t));
319 extern void apm_unregister_callback(int (*)(apm_event_t));
322 * Local variables
324 static asmlinkage struct {
325 unsigned long offset;
326 unsigned short segment;
327 } apm_bios_entry;
328 static int apm_enabled = 0;
329 #ifdef CONFIG_APM_CPU_IDLE
330 static int clock_slowed = 0;
331 #endif
332 static int suspends_pending = 0;
333 static int standbys_pending = 0;
335 static long clock_cmos_diff;
336 static int got_clock_diff = 0;
338 static struct wait_queue * process_list = NULL;
339 static struct apm_bios_struct * user_list = NULL;
341 static struct timer_list apm_timer;
343 static char driver_version[] = "1.2";/* no spaces */
345 #ifdef APM_DEBUG
346 static char * apm_event_name[] = {
347 "system standby",
348 "system suspend",
349 "normal resume",
350 "critical resume",
351 "low battery",
352 "power status change",
353 "update time",
354 "critical suspend",
355 "user standby",
356 "user suspend",
357 "system standby resume"
359 #define NR_APM_EVENT_NAME \
360 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
361 #endif
363 static struct file_operations apm_bios_fops = {
364 NULL, /* lseek */
365 do_read,
366 NULL, /* write */
367 NULL, /* readdir */
368 do_poll,
369 do_ioctl,
370 NULL, /* mmap */
371 do_open,
372 do_release,
373 NULL, /* fsync */
374 NULL /* fasync */
377 static struct miscdevice apm_device = {
378 APM_MINOR_DEV,
379 "apm",
380 &apm_bios_fops
383 typedef struct callback_list_t {
384 int (* callback)(apm_event_t);
385 struct callback_list_t * next;
386 } callback_list_t;
388 static callback_list_t * callback_list = NULL;
390 typedef struct lookup_t {
391 int key;
392 char * msg;
393 } lookup_t;
395 static const lookup_t error_table[] = {
396 /* N/A { APM_SUCCESS, "Operation succeeded" }, */
397 { APM_DISABLED, "Power management disabled" },
398 { APM_CONNECTED, "Real mode interface already connected" },
399 { APM_NOT_CONNECTED, "Interface not connected" },
400 { APM_16_CONNECTED, "16 bit interface already connected" },
401 /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
402 { APM_32_CONNECTED, "32 bit interface already connected" },
403 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
404 { APM_BAD_DEVICE, "Unrecognized device ID" },
405 { APM_BAD_PARAM, "Parameter out of range" },
406 { APM_NOT_ENGAGED, "Interface not engaged" },
407 { APM_BAD_STATE, "Unable to enter requested state" },
408 /* N/A { APM_NO_EVENTS, "No events pending" }, */
409 { APM_NOT_PRESENT, "No APM present" }
411 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
413 static int apm_driver_version(u_short *val)
415 u_short error;
417 APM_DRIVER_VERSION(*val, *val, error);
419 if (error & 0xff)
420 return (*val >> 8);
421 return APM_SUCCESS;
424 static int apm_get_event(apm_event_t *event)
426 u_short error;
428 APM_GET_EVENT(*event, error);
429 if (error & 0xff)
430 return (error >> 8);
431 return APM_SUCCESS;
434 int apm_set_power_state(u_short state)
436 u_short error;
438 APM_SET_POWER_STATE(state, error);
439 if (error & 0xff)
440 return (error >> 8);
441 return APM_SUCCESS;
444 #ifdef CONFIG_APM_DISPLAY_BLANK
445 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
446 static int apm_set_display_power_state(u_short state)
448 u_short error;
450 APM_SET_DISPLAY_POWER_STATE(state, error);
451 if (error & 0xff)
452 return (error >> 8);
453 return APM_SUCCESS;
455 #endif
457 #ifdef CONFIG_APM_DO_ENABLE
458 /* Called by apm_setup if apm_enabled will be true. */
459 static inline int apm_enable_power_management(void)
461 u_short error;
463 APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
464 ? 0x0001 : 0xffff,
465 error);
466 if (error & 0xff)
467 return (error >> 8);
468 return APM_SUCCESS;
470 #endif
472 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
474 u_short error;
476 APM_GET_POWER_STATUS(*status, *bat, *life, error);
477 if (error & 0xff)
478 return (error >> 8);
479 return APM_SUCCESS;
482 static inline int apm_engage_power_management(u_short device)
484 u_short error;
486 APM_ENGAGE_POWER_MANAGEMENT(device, error);
487 if (error & 0xff)
488 return (error >> 8);
489 return APM_SUCCESS;
492 static void apm_error(char *str, int err)
494 int i;
496 for (i = 0; i < ERROR_COUNT; i++)
497 if (error_table[i].key == err) break;
498 if (i < ERROR_COUNT)
499 printk(KERN_NOTICE "apm_bios: %s: %s\n", str, error_table[i].msg);
500 else
501 printk(KERN_NOTICE "apm_bios: %s: unknown error code %#2.2x\n", str, err);
504 /* Called from console driver -- must make sure apm_enabled. */
505 int apm_display_blank(void)
507 #ifdef CONFIG_APM_DISPLAY_BLANK
508 int error;
510 if (!apm_enabled)
511 return 0;
512 error = apm_set_display_power_state(APM_STATE_STANDBY);
513 if (error == APM_SUCCESS)
514 return 1;
515 apm_error("set display standby", error);
516 #endif
517 return 0;
520 /* Called from console driver -- must make sure apm_enabled. */
521 int apm_display_unblank(void)
523 #ifdef CONFIG_APM_DISPLAY_BLANK
524 int error;
526 if (!apm_enabled)
527 return 0;
528 error = apm_set_display_power_state(APM_STATE_READY);
529 if (error == APM_SUCCESS)
530 return 1;
531 apm_error("set display ready", error);
532 #endif
533 return 0;
536 int apm_register_callback(int (*callback)(apm_event_t))
538 callback_list_t * new;
540 new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
541 if (new == NULL)
542 return -ENOMEM;
543 new->callback = callback;
544 new->next = callback_list;
545 callback_list = new;
546 return 0;
549 void apm_unregister_callback(int (*callback)(apm_event_t))
551 callback_list_t ** ptr;
552 callback_list_t * old;
554 ptr = &callback_list;
555 for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
556 if ((*ptr)->callback == callback)
557 break;
558 old = *ptr;
559 *ptr = old->next;
560 kfree_s(old, sizeof(callback_list_t));
563 static int queue_empty(struct apm_bios_struct * as)
565 return as->event_head == as->event_tail;
568 static apm_event_t get_queued_event(struct apm_bios_struct * as)
570 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
571 return as->events[as->event_tail];
574 static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
576 struct apm_bios_struct * as;
578 if (user_list == NULL)
579 return 0;
580 for (as = user_list; as != NULL; as = as->next) {
581 if (as == sender)
582 continue;
583 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
584 if (as->event_head == as->event_tail)
585 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
586 as->events[as->event_head] = event;
587 if (!as->suser)
588 continue;
589 switch (event) {
590 case APM_SYS_SUSPEND:
591 case APM_USER_SUSPEND:
592 as->suspends_pending++;
593 suspends_pending++;
594 break;
596 case APM_SYS_STANDBY:
597 case APM_USER_STANDBY:
598 as->standbys_pending++;
599 standbys_pending++;
600 break;
603 wake_up_interruptible(&process_list);
604 return 1;
607 static void set_time(void)
609 unsigned long flags;
611 if (!got_clock_diff) /* Don't know time zone, can't set clock */
612 return;
614 save_flags(flags);
615 cli();
616 CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
617 restore_flags(flags);
620 static void suspend(void)
622 unsigned long flags;
623 int err;
625 /* Estimate time zone so that set_time can
626 update the clock */
627 save_flags(flags);
628 clock_cmos_diff = -get_cmos_time();
629 cli();
630 clock_cmos_diff += CURRENT_TIME;
631 got_clock_diff = 1;
632 restore_flags(flags);
634 err = apm_set_power_state(APM_STATE_SUSPEND);
635 if (err)
636 apm_error("suspend", err);
637 set_time();
640 static void standby(void)
642 int err;
644 err = apm_set_power_state(APM_STATE_STANDBY);
645 if (err)
646 apm_error("standby", err);
649 static apm_event_t get_event(void)
651 int error;
652 apm_event_t event;
654 static int notified = 0;
656 error = apm_get_event(&event);
657 if (error == APM_SUCCESS)
658 return event;
660 if ((error != APM_NO_EVENTS) && (notified++ == 0))
661 apm_error("get_event", error);
663 return 0;
666 static void send_event(apm_event_t event, apm_event_t undo,
667 struct apm_bios_struct *sender)
669 callback_list_t * call;
670 callback_list_t * fix;
672 for (call = callback_list; call != NULL; call = call->next) {
673 if (call->callback(event) && undo) {
674 for (fix = callback_list; fix != call; fix = fix->next)
675 fix->callback(undo);
676 if (apm_bios_info.version > 0x100)
677 apm_set_power_state(APM_STATE_REJECT);
678 return;
682 queue_event(event, sender);
685 static void check_events(void)
687 apm_event_t event;
689 while ((event = get_event()) != 0) {
690 switch (event) {
691 case APM_SYS_STANDBY:
692 case APM_USER_STANDBY:
693 send_event(event, APM_STANDBY_RESUME, NULL);
694 if (standbys_pending <= 0)
695 standby();
696 break;
698 case APM_USER_SUSPEND:
699 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
700 if (apm_bios_info.version > 0x100)
701 apm_set_power_state(APM_STATE_REJECT);
702 break;
703 #endif
704 case APM_SYS_SUSPEND:
705 send_event(event, APM_NORMAL_RESUME, NULL);
706 if (suspends_pending <= 0)
707 suspend();
708 break;
710 case APM_NORMAL_RESUME:
711 case APM_CRITICAL_RESUME:
712 case APM_STANDBY_RESUME:
713 set_time();
714 send_event(event, 0, NULL);
715 break;
717 case APM_LOW_BATTERY:
718 case APM_POWER_STATUS_CHANGE:
719 send_event(event, 0, NULL);
720 break;
722 case APM_UPDATE_TIME:
723 set_time();
724 break;
726 case APM_CRITICAL_SUSPEND:
727 suspend();
728 break;
730 #ifdef APM_DEBUG
731 if (event <= NR_APM_EVENT_NAME)
732 printk(KERN_DEBUG "APM BIOS received %s notify\n",
733 apm_event_name[event - 1]);
734 else
735 printk(KERN_DEBUG "APM BIOS received unknown event 0x%02x\n",
736 event);
737 #endif
741 static void do_apm_timer(unsigned long unused)
743 int err;
745 static int pending_count = 0;
747 if (((standbys_pending > 0) || (suspends_pending > 0))
748 && (apm_bios_info.version > 0x100)
749 && (pending_count-- <= 0)) {
750 pending_count = 4;
752 err = apm_set_power_state(APM_STATE_BUSY);
753 if (err)
754 apm_error("busy", err);
757 if (!(((standbys_pending > 0) || (suspends_pending > 0))
758 && (apm_bios_info.version == 0x100)))
759 check_events();
761 init_timer(&apm_timer);
762 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
763 add_timer(&apm_timer);
766 /* Called from sys_idle, must make sure apm_enabled. */
767 int apm_do_idle(void)
769 #ifdef CONFIG_APM_CPU_IDLE
770 unsigned short error;
772 if (!apm_enabled)
773 return 0;
775 APM_SET_CPU_IDLE(error);
776 if (error & 0xff)
777 return 0;
779 clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
780 return 1;
781 #else
782 return 0;
783 #endif
786 /* Called from sys_idle, must make sure apm_enabled. */
787 void apm_do_busy(void)
789 #ifdef CONFIG_APM_CPU_IDLE
790 unsigned short error;
792 if (!apm_enabled)
793 return;
795 #ifndef ALWAYS_CALL_BUSY
796 if (!clock_slowed)
797 return;
798 #endif
800 APM_SET_CPU_BUSY(error);
802 clock_slowed = 0;
803 #endif
806 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
808 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
809 printk(KERN_ERR "apm_bios: %s passed bad filp", func);
810 return 1;
812 return 0;
815 static long do_read(struct inode *inode, struct file *fp,
816 char *buf, unsigned long count)
818 struct apm_bios_struct * as;
819 int i;
820 apm_event_t event;
821 struct wait_queue wait = { current, NULL };
823 as = fp->private_data;
824 if (check_apm_bios_struct(as, "read"))
825 return -EIO;
826 if (count < sizeof(apm_event_t))
827 return -EINVAL;
828 if (queue_empty(as)) {
829 if (fp->f_flags & O_NONBLOCK)
830 return -EAGAIN;
831 add_wait_queue(&process_list, &wait);
832 repeat:
833 current->state = TASK_INTERRUPTIBLE;
834 if (queue_empty(as)
835 && !(current->signal & ~current->blocked)) {
836 schedule();
837 goto repeat;
839 current->state = TASK_RUNNING;
840 remove_wait_queue(&process_list, &wait);
842 i = count;
843 while ((i >= sizeof(event)) && !queue_empty(as)) {
844 event = get_queued_event(as);
845 copy_to_user(buf, &event, sizeof(event));
846 switch (event) {
847 case APM_SYS_SUSPEND:
848 case APM_USER_SUSPEND:
849 as->suspends_read++;
850 break;
852 case APM_SYS_STANDBY:
853 case APM_USER_STANDBY:
854 as->standbys_read++;
855 break;
857 buf += sizeof(event);
858 i -= sizeof(event);
860 if (i < count)
861 return count - i;
862 if (current->signal & ~current->blocked)
863 return -ERESTARTSYS;
864 return 0;
867 static unsigned int do_poll(struct file *fp, poll_table * wait)
869 struct apm_bios_struct * as;
871 as = fp->private_data;
872 if (check_apm_bios_struct(as, "select"))
873 return 0;
874 poll_wait(&process_list, wait);
875 if (!queue_empty(as))
876 return POLLIN | POLLRDNORM;
877 return 0;
880 static int do_ioctl(struct inode * inode, struct file *filp,
881 u_int cmd, u_long arg)
883 struct apm_bios_struct * as;
885 as = filp->private_data;
886 if (check_apm_bios_struct(as, "ioctl"))
887 return -EIO;
888 if (!as->suser)
889 return -EPERM;
890 switch (cmd) {
891 case APM_IOC_STANDBY:
892 if (as->standbys_read > 0) {
893 as->standbys_read--;
894 as->standbys_pending--;
895 standbys_pending--;
897 else
898 send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
899 if (standbys_pending <= 0)
900 standby();
901 break;
902 case APM_IOC_SUSPEND:
903 if (as->suspends_read > 0) {
904 as->suspends_read--;
905 as->suspends_pending--;
906 suspends_pending--;
908 else
909 send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
910 if (suspends_pending <= 0)
911 suspend();
912 break;
913 default:
914 return -EINVAL;
916 return 0;
919 static int do_release(struct inode * inode, struct file * filp)
921 struct apm_bios_struct * as;
923 as = filp->private_data;
924 filp->private_data = NULL;
925 if (check_apm_bios_struct(as, "release"))
926 return 0;
927 if (as->standbys_pending > 0) {
928 standbys_pending -= as->standbys_pending;
929 if (standbys_pending <= 0)
930 standby();
932 if (as->suspends_pending > 0) {
933 suspends_pending -= as->suspends_pending;
934 if (suspends_pending <= 0)
935 suspend();
937 if (user_list == as)
938 user_list = as->next;
939 else {
940 struct apm_bios_struct * as1;
942 for (as1 = user_list;
943 (as1 != NULL) && (as1->next != as);
944 as1 = as1->next)
946 if (as1 == NULL)
947 printk(KERN_ERR "apm_bios: filp not in user list");
948 else
949 as1->next = as->next;
951 kfree_s(as, sizeof(*as));
952 return 0;
955 static int do_open(struct inode * inode, struct file * filp)
957 struct apm_bios_struct * as;
959 as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
960 if (as == NULL) {
961 printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes",
962 sizeof(*as));
963 return -ENOMEM;
965 as->magic = APM_BIOS_MAGIC;
966 as->event_tail = as->event_head = 0;
967 as->suspends_pending = as->standbys_pending = 0;
968 as->suspends_read = as->standbys_read = 0;
969 as->suser = suser();
970 as->next = user_list;
971 user_list = as;
972 filp->private_data = as;
973 return 0;
976 #ifdef CONFIG_PROC_FS
977 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
979 char * p;
980 unsigned short bx;
981 unsigned short cx;
982 unsigned short dx;
983 unsigned short error;
984 unsigned short ac_line_status = 0xff;
985 unsigned short battery_status = 0xff;
986 unsigned short battery_flag = 0xff;
987 int percentage = -1;
988 int time_units = -1;
989 char *units = "?";
991 if (!apm_enabled)
992 return 0;
993 p = buf;
995 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
996 ac_line_status = (bx >> 8) & 0xff;
997 battery_status = bx & 0xff;
998 if ((cx & 0xff) != 0xff)
999 percentage = cx & 0xff;
1001 if (apm_bios_info.version > 0x100) {
1002 battery_flag = (cx >> 8) & 0xff;
1003 if (dx != 0xffff) {
1004 if ((dx & 0x8000) == 0x8000) {
1005 units = "min";
1006 time_units = dx & 0x7ffe;
1007 } else {
1008 units = "sec";
1009 time_units = dx & 0x7fff;
1014 /* Arguments, with symbols from linux/apm_bios.h. Information is
1015 from the Get Power Status (0x0a) call unless otherwise noted.
1017 0) Linux driver version (this will change if format changes)
1018 1) APM BIOS Version. Usually 1.0 or 1.1.
1019 2) APM flags from APM Installation Check (0x00):
1020 bit 0: APM_16_BIT_SUPPORT
1021 bit 1: APM_32_BIT_SUPPORT
1022 bit 2: APM_IDLE_SLOWS_CLOCK
1023 bit 3: APM_BIOS_DISABLED
1024 bit 4: APM_BIOS_DISENGAGED
1025 3) AC line status
1026 0x00: Off-line
1027 0x01: On-line
1028 0x02: On backup power (APM BIOS 1.1 only)
1029 0xff: Unknown
1030 4) Battery status
1031 0x00: High
1032 0x01: Low
1033 0x02: Critical
1034 0x03: Charging
1035 0xff: Unknown
1036 5) Battery flag
1037 bit 0: High
1038 bit 1: Low
1039 bit 2: Critical
1040 bit 3: Charging
1041 bit 7: No system battery
1042 0xff: Unknown
1043 6) Remaining battery life (percentage of charge):
1044 0-100: valid
1045 -1: Unknown
1046 7) Remaining battery life (time units):
1047 Number of remaining minutes or seconds
1048 -1: Unknown
1049 8) min = minutes; sec = seconds */
1051 p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1052 driver_version,
1053 (apm_bios_info.version >> 8) & 0xff,
1054 apm_bios_info.version & 0xff,
1055 apm_bios_info.flags,
1056 ac_line_status,
1057 battery_status,
1058 battery_flag,
1059 percentage,
1060 time_units,
1061 units);
1063 return p - buf;
1065 #endif
1067 __initfunc(void apm_bios_init(void))
1069 unsigned short bx;
1070 unsigned short cx;
1071 unsigned short dx;
1072 unsigned short error;
1073 char * power_stat;
1074 char * bat_stat;
1075 static struct proc_dir_entry *ent;
1077 #ifdef __SMP__
1078 printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n");
1079 return;
1080 #endif
1081 if (apm_bios_info.version == 0) {
1082 printk(KERN_INFO "APM BIOS not found.\n");
1083 return;
1085 printk(KERN_INFO "APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
1086 ((apm_bios_info.version >> 8) & 0xff) + '0',
1087 (apm_bios_info.version & 0xff) + '0',
1088 apm_bios_info.flags,
1089 driver_version);
1090 if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1091 printk(KERN_INFO " No 32 bit BIOS support\n");
1092 return;
1096 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1097 * but is reportedly a 1.0 BIOS.
1099 if (apm_bios_info.version == 0x001)
1100 apm_bios_info.version = 0x100;
1102 printk(KERN_INFO " Entry %x:%lx cseg16 %x dseg %x",
1103 apm_bios_info.cseg, apm_bios_info.offset,
1104 apm_bios_info.cseg_16, apm_bios_info.dseg);
1105 if (apm_bios_info.version > 0x100)
1106 printk(" cseg len %x, dseg len %x",
1107 apm_bios_info.cseg_len, apm_bios_info.dseg_len);
1108 printk("\n");
1110 apm_bios_entry.offset = apm_bios_info.offset;
1111 apm_bios_entry.segment = APM_CS;
1112 set_base(gdt[APM_CS >> 3],
1113 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
1114 set_base(gdt[APM_CS_16 >> 3],
1115 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
1116 set_base(gdt[APM_DS >> 3],
1117 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
1118 if (apm_bios_info.version == 0x100) {
1119 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1120 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1121 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1122 } else {
1123 #ifdef APM_RELAX_SEGMENTS
1124 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1125 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1126 /* For some unknown machine. */
1127 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1128 /* For the DEC Hinote Ultra CT475 (and others?) */
1129 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1130 #else
1131 set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
1132 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1133 set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
1134 #endif
1135 apm_bios_info.version = 0x0101;
1136 error = apm_driver_version(&apm_bios_info.version);
1137 if (error != 0)
1138 apm_bios_info.version = 0x100;
1139 else {
1140 apm_engage_power_management(0x0001);
1141 printk( " Connection version %d.%d\n",
1142 (apm_bios_info.version >> 8) & 0xff,
1143 apm_bios_info.version & 0xff );
1144 apm_bios_info.version = 0x0101;
1148 error = apm_get_power_status(&bx, &cx, &dx);
1149 if (error)
1150 printk(KERN_INFO " Power status not available\n");
1151 else {
1152 switch ((bx >> 8) & 0xff) {
1153 case 0: power_stat = "off line"; break;
1154 case 1: power_stat = "on line"; break;
1155 case 2: power_stat = "on backup power"; break;
1156 default: power_stat = "unknown"; break;
1158 switch (bx & 0xff) {
1159 case 0: bat_stat = "high"; break;
1160 case 1: bat_stat = "low"; break;
1161 case 2: bat_stat = "critical"; break;
1162 case 3: bat_stat = "charging"; break;
1163 default: bat_stat = "unknown"; break;
1165 printk(KERN_INFO " AC %s, battery status %s, battery life ",
1166 power_stat, bat_stat);
1167 if ((cx & 0xff) == 0xff)
1168 printk("unknown\n");
1169 else
1170 printk("%d%%\n", cx & 0xff);
1171 if (apm_bios_info.version > 0x100) {
1172 printk(" battery flag 0x%02x, battery life ",
1173 (cx >> 8) & 0xff);
1174 if (dx == 0xffff)
1175 printk("unknown\n");
1176 else {
1177 if ((dx & 0x8000))
1178 printk("%d minutes\n", dx & 0x7ffe );
1179 else
1180 printk("%d seconds\n", dx & 0x7fff );
1185 #ifdef CONFIG_APM_DO_ENABLE
1187 * This call causes my NEC UltraLite Versa 33/C to hang if it is
1188 * booted with PM disabled but not in the docking station.
1189 * Unfortunate ...
1191 error = apm_enable_power_management();
1192 if (error)
1193 apm_error("enable power management", error);
1194 if (error == APM_DISABLED)
1195 return;
1196 #endif
1198 init_timer(&apm_timer);
1199 apm_timer.function = do_apm_timer;
1200 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
1201 add_timer(&apm_timer);
1203 #ifdef CONFIG_PROC_FS
1204 ent = create_proc_entry("apm", 0, 0);
1205 ent->get_info = apm_get_info;
1206 #endif
1208 misc_register(&apm_device);
1210 apm_enabled = 1;