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
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
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)
28 * May 1996, Version 1.2
29 * Feb 1998, Version 1.3
30 * Feb 1998, Version 1.4
33 * 0.6b: first version in official kernel, Linux 1.3.46
34 * 0.7: changed /proc/apm format, Linux 1.3.58
35 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
36 * 0.9: only call bios if bios is present, Linux 1.3.72
37 * 1.0: use fixed device number, consolidate /proc/apm into this file,
39 * 1.1: support user-space standby and suspend, power off after system
40 * halted, Linux 1.3.98
41 * 1.2: When resetting RTC after resume, take care so that the time
42 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
43 * <jtoth@princeton.edu>); improve interaction between
44 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
45 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
46 * levels to the printk calls. APM is not defined for SMP machines.
47 * The new replacment for it is, but Linux doesn't yet support this.
48 * Alan Cox Linux 2.1.55
49 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
50 * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
51 * Dean Gaudet <dgaudet@arctic.org>.
52 * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
56 * Intel Corporation, Microsoft Corporation. Advanced Power Management
57 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
58 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
60 * [This document is available free from Intel by calling 800.628.8686 (fax
61 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
62 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
63 * available from Microsoft by calling 206.882.8080.]
66 * Intel Corporation, Microsoft Corporation. Advanced Power Management
67 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
69 * [This document is available from Intel at:
70 * http://www.intel.com/IAL/powermgm
72 * http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
76 #include <linux/config.h>
77 #include <linux/module.h>
79 #include <asm/system.h>
80 #include <asm/uaccess.h>
82 #include <linux/poll.h>
83 #include <linux/types.h>
84 #include <linux/stddef.h>
85 #include <linux/timer.h>
86 #include <linux/fcntl.h>
87 #include <linux/malloc.h>
88 #include <linux/linkage.h>
90 #include <linux/stat.h>
91 #include <linux/proc_fs.h>
93 #include <linux/miscdevice.h>
94 #include <linux/apm_bios.h>
95 #include <linux/init.h>
97 EXPORT_SYMBOL(apm_register_callback
);
98 EXPORT_SYMBOL(apm_unregister_callback
);
100 extern unsigned long get_cmos_time(void);
103 * The apm_bios device is one of the misc char devices.
104 * This is its minor number.
106 #define APM_MINOR_DEV 134
108 /* Configurable options:
110 * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
111 * This is necessary on the NEC Versa M series, which generates these when
112 * resuming from SYSTEM SUSPEND. However, enabling this on other laptops
113 * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
114 * USER SUSPEND is ignored -- this may prevent the APM driver from updating
115 * the system time on a RESUME.
117 * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of
118 * the specification: "When disabled, the APM BIOS does not automatically
119 * power manage devices, enter the Standby State, enter the Suspend State,
120 * or take power saving steps in response to CPU Idle calls." This driver
121 * will make CPU Idle calls when Linux is idle (unless this feature is
122 * turned off -- see below). This should always save battery power, but
123 * more complicated APM features will be dependent on your BIOS
124 * implementation. You may need to turn this option off if your computer
125 * hangs at boot time when using APM support, or if it beeps continuously
126 * instead of suspending. Turn this off if you have a NEC UltraLite Versa
127 * 33/C or a Toshiba T400CDT. This is off by default since most machines
128 * do fine without this feature.
130 * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
131 * idle loop. On some machines, this can activate improved power savings,
132 * such as a slowed CPU clock rate, when the machine is idle. These idle
133 * call is made after the idle loop has run for some length of time (e.g.,
134 * 333 mS). On some machines, this will cause a hang at boot time or
135 * whenever the CPU becomes idle.
137 * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some
138 * laptops can use this to turn of the LCD backlight when the VC screen
139 * blanker blanks the screen. Note that this is only used by the VC screen
140 * blanker, and probably won't turn off the backlight when using X11. Some
141 * problems have been reported when using this option with gpm (if you'd
142 * like to debug this, please do so).
144 * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
145 * on returning multiple suspend/standby events whenever one occurs. We
146 * really only need one at a time, so just ignore any beyond the first.
147 * This is probably safe on most laptops.
149 * If you are debugging the APM support for your laptop, note that code for
150 * all of these options is contained in this file, so you can #define or
151 * #undef these on the next line to avoid recompiling the whole kernel.
155 /* KNOWN PROBLEM MACHINES:
157 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
158 * [Confirmed by TI representative]
159 * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
160 * [Confirmed by BIOS disassembly]
161 * P: Toshiba 1950S: battery life information only gets updated after resume
162 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
163 * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
165 * Legend: U = unusable with APM patches
166 * P = partially usable with APM patches
170 * Define to have debug messages.
175 * Define to always call the APM BIOS busy routine even if the clock was
176 * not slowed by the idle routine.
178 #define ALWAYS_CALL_BUSY
181 * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
182 * should turn interrupts on before it does a 'hlt').
187 * Define to make the APM BIOS calls zero all data segment registers (so
188 * that an incorrect BIOS implementation will cause a kernel panic if it
189 * tries to write to arbitrary memory).
191 #define APM_ZERO_SEGS
194 * Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is
195 * supposed to provide limit information that it recognizes. Many machines
196 * do this correctly, but many others do not restrict themselves to their
197 * claimed limit. When this happens, they will cause a segmentation
198 * violation in the kernel at boot time. Most BIOS's, however, will
199 * respect a 64k limit, so we use that. If you want to be pedantic and
200 * hold your BIOS to its claims, then undefine this.
202 #define APM_RELAX_SEGMENTS
205 * Need to poll the APM BIOS every second
207 #define APM_CHECK_TIMEOUT (HZ)
210 * These are the actual BIOS calls in assembler. Depending on
211 * APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here! Not
212 * only are interrupts disabled, but all the segment registers (except SS)
213 * are saved and zeroed this means that if the BIOS tries to reference any
214 * data without explicitly loading the segment registers, the kernel will
215 * fault immediately rather than have some unforeseen circumstances for the
216 * rest of the kernel. And it will be very obvious! :-) Doing this
217 * depends on CS referring to the same physical memory as DS so that DS can
218 * be zeroed before the call. Unfortunately, we can't do anything about the
219 * stack segment/pointer. Also, we tell the compiler that everything could
223 # define APM_DO_CLI "cli\n\t"
228 # define APM_DO_ZERO_SEGS \
233 "xorl %%edx, %%edx\n\t" \
234 "movl %%dx, %%ds\n\t" \
235 "movl %%dx, %%es\n\t" \
236 "movl %%dx, %%fs\n\t" \
237 "movl %%dx, %%gs\n\t"
238 # define APM_DO_RESTORE_SEGS \
244 # define APM_DO_ZERO_SEGS
245 # define APM_DO_RESTORE_SEGS
248 #define APM_BIOS_CALL(error_reg) \
249 __asm__ __volatile__( \
253 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
254 "setc %%" # error_reg "\n\t" \
257 #define APM_BIOS_CALL_END \
258 : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
260 #ifdef CONFIG_APM_CPU_IDLE
261 #define APM_SET_CPU_IDLE(error) \
268 #define APM_SET_CPU_BUSY(error) \
274 #define APM_SET_POWER_STATE(state, error) \
277 : "a" (0x5307), "b" (0x0001), "c" (state) \
280 #ifdef CONFIG_APM_DISPLAY_BLANK
281 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
284 : "a" (0x5307), "b" (0x01ff), "c" (state) \
288 #ifdef CONFIG_APM_DO_ENABLE
289 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
292 : "a" (0x5308), "b" (device), "c" (1) \
296 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
298 : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
299 : "a" (0x530a), "b" (1) \
302 #define APM_GET_BATTERY_STATUS(which, bx, cx, dx, si, error) \
304 : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx), "=S" (si) \
305 : "a" (0x530a), "b" (0x8000 | (which)) \
308 #define APM_GET_EVENT(event, info, error) \
310 : "=a" (error), "=b" (event), "=c" (info) \
314 #define APM_DRIVER_VERSION(ver, ax, error) \
316 : "=a" (ax), "=b" (error) \
317 : "a" (0x530e), "b" (0), "c" (ver) \
320 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
323 : "a" (0x530f), "b" (device), "c" (1) \
327 * Forward declarations
329 static void suspend(void);
330 static void standby(void);
331 static void set_time(void);
333 static void check_events(void);
334 static void do_apm_timer(unsigned long);
336 static int do_open(struct inode
*, struct file
*);
337 static int do_release(struct inode
*, struct file
*);
338 static ssize_t
do_read(struct file
*, char *, size_t , loff_t
*);
339 static unsigned int do_poll(struct file
*, poll_table
*);
340 static int do_ioctl(struct inode
*, struct file
*, u_int
, u_long
);
342 #ifdef CONFIG_PROC_FS
343 static int apm_get_info(char *, char **, off_t
, int, int);
346 extern int apm_register_callback(int (*)(apm_event_t
));
347 extern void apm_unregister_callback(int (*)(apm_event_t
));
352 static asmlinkage
struct {
353 unsigned long offset
;
354 unsigned short segment
;
356 static int apm_enabled
= 0;
357 #ifdef CONFIG_APM_CPU_IDLE
358 static int clock_slowed
= 0;
360 static int suspends_pending
= 0;
361 static int standbys_pending
= 0;
362 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
363 static int waiting_for_resume
= 0;
366 static long clock_cmos_diff
;
367 static int got_clock_diff
= 0;
369 static struct wait_queue
* process_list
= NULL
;
370 static struct apm_bios_struct
* user_list
= NULL
;
372 static struct timer_list apm_timer
;
374 static char driver_version
[] = "1.4"; /* no spaces */
377 static char * apm_event_name
[] = {
383 "power status change",
388 "system standby resume",
389 "capabilities change"
391 #define NR_APM_EVENT_NAME \
392 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
395 static struct file_operations apm_bios_fops
= {
410 static struct miscdevice apm_device
= {
416 typedef struct callback_list_t
{
417 int (* callback
)(apm_event_t
);
418 struct callback_list_t
* next
;
421 static callback_list_t
* callback_list
= NULL
;
423 typedef struct lookup_t
{
428 static const lookup_t error_table
[] = {
429 /* N/A { APM_SUCCESS, "Operation succeeded" }, */
430 { APM_DISABLED
, "Power management disabled" },
431 { APM_CONNECTED
, "Real mode interface already connected" },
432 { APM_NOT_CONNECTED
, "Interface not connected" },
433 { APM_16_CONNECTED
, "16 bit interface already connected" },
434 /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
435 { APM_32_CONNECTED
, "32 bit interface already connected" },
436 { APM_32_UNSUPPORTED
, "32 bit interface not supported" },
437 { APM_BAD_DEVICE
, "Unrecognized device ID" },
438 { APM_BAD_PARAM
, "Parameter out of range" },
439 { APM_NOT_ENGAGED
, "Interface not engaged" },
440 { APM_BAD_FUNCTION
, "Function not supported" },
441 { APM_RESUME_DISABLED
, "Resume timer disabled" },
442 { APM_BAD_STATE
, "Unable to enter requested state" },
443 /* N/A { APM_NO_EVENTS, "No events pending" }, */
444 { APM_NOT_PRESENT
, "No APM present" }
446 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
448 static int apm_driver_version(u_short
*val
)
452 APM_DRIVER_VERSION(*val
, *val
, error
);
459 static int apm_get_event(apm_event_t
*event
, apm_eventinfo_t
*info
)
463 APM_GET_EVENT(*event
, *info
, error
);
466 if (apm_bios_info
.version
< 0x0102)
467 *info
= ~0; /* indicate info not valid */
471 int apm_set_power_state(u_short state
)
475 APM_SET_POWER_STATE(state
, error
);
481 #ifdef CONFIG_APM_DISPLAY_BLANK
482 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
483 static int apm_set_display_power_state(u_short state
)
487 APM_SET_DISPLAY_POWER_STATE(state
, error
);
494 #ifdef CONFIG_APM_DO_ENABLE
495 /* Called by apm_setup if apm_enabled will be true. */
496 static inline int apm_enable_power_management(void)
500 APM_ENABLE_POWER_MANAGEMENT((apm_bios_info
.version
> 0x100)
509 static int apm_get_power_status(u_short
*status
, u_short
*bat
, u_short
*life
)
513 APM_GET_POWER_STATUS(*status
, *bat
, *life
, error
);
520 /* not used anywhere */
521 static int apm_get_battery_status(u_short which
,
522 u_short
*bat
, u_short
*life
, u_short
*nbat
)
524 u_short status
, error
;
526 if (apm_bios_info
.version
< 0x0102) {
527 /* pretend we only have one battery. */
528 if (which
!=1) return APM_BAD_DEVICE
;
530 return apm_get_power_status(&status
, bat
, life
);
533 APM_GET_BATTERY_STATUS(which
, status
, *bat
, *life
, *nbat
, error
);
540 static inline int apm_engage_power_management(u_short device
)
544 APM_ENGAGE_POWER_MANAGEMENT(device
, error
);
550 static void apm_error(char *str
, int err
)
554 for (i
= 0; i
< ERROR_COUNT
; i
++)
555 if (error_table
[i
].key
== err
) break;
557 printk(KERN_NOTICE
"apm_bios: %s: %s\n", str
, error_table
[i
].msg
);
559 printk(KERN_NOTICE
"apm_bios: %s: unknown error code %#2.2x\n", str
, err
);
562 /* Called from console driver -- must make sure apm_enabled. */
563 int apm_display_blank(void)
565 #ifdef CONFIG_APM_DISPLAY_BLANK
570 error
= apm_set_display_power_state(APM_STATE_STANDBY
);
571 if (error
== APM_SUCCESS
)
573 apm_error("set display standby", error
);
578 /* Called from console driver -- must make sure apm_enabled. */
579 int apm_display_unblank(void)
581 #ifdef CONFIG_APM_DISPLAY_BLANK
586 error
= apm_set_display_power_state(APM_STATE_READY
);
587 if (error
== APM_SUCCESS
)
589 apm_error("set display ready", error
);
594 int apm_register_callback(int (*callback
)(apm_event_t
))
596 callback_list_t
* new;
598 new = kmalloc(sizeof(callback_list_t
), GFP_KERNEL
);
601 new->callback
= callback
;
602 new->next
= callback_list
;
607 void apm_unregister_callback(int (*callback
)(apm_event_t
))
609 callback_list_t
** ptr
;
610 callback_list_t
* old
;
612 ptr
= &callback_list
;
613 for (ptr
= &callback_list
; *ptr
!= NULL
; ptr
= &(*ptr
)->next
)
614 if ((*ptr
)->callback
== callback
)
618 kfree_s(old
, sizeof(callback_list_t
));
621 static int queue_empty(struct apm_bios_struct
* as
)
623 return as
->event_head
== as
->event_tail
;
626 static apm_event_t
get_queued_event(struct apm_bios_struct
* as
)
628 as
->event_tail
= (as
->event_tail
+ 1) % APM_MAX_EVENTS
;
629 return as
->events
[as
->event_tail
];
632 static int queue_event(apm_event_t event
, struct apm_bios_struct
*sender
)
634 struct apm_bios_struct
* as
;
636 if (user_list
== NULL
)
638 for (as
= user_list
; as
!= NULL
; as
= as
->next
) {
641 as
->event_head
= (as
->event_head
+ 1) % APM_MAX_EVENTS
;
642 if (as
->event_head
== as
->event_tail
) {
646 printk( "apm_bios: an event queue overflowed\n" );
649 as
->event_tail
= (as
->event_tail
+ 1) % APM_MAX_EVENTS
;
651 as
->events
[as
->event_head
] = event
;
655 case APM_SYS_SUSPEND
:
656 case APM_USER_SUSPEND
:
657 as
->suspends_pending
++;
661 case APM_SYS_STANDBY
:
662 case APM_USER_STANDBY
:
663 as
->standbys_pending
++;
668 wake_up_interruptible(&process_list
);
672 static void set_time(void)
676 if (!got_clock_diff
) /* Don't know time zone, can't set clock */
681 CURRENT_TIME
= get_cmos_time() + clock_cmos_diff
;
682 restore_flags(flags
);
685 static void suspend(void)
690 /* Estimate time zone so that set_time can
693 clock_cmos_diff
= -get_cmos_time();
695 clock_cmos_diff
+= CURRENT_TIME
;
697 restore_flags(flags
);
699 err
= apm_set_power_state(APM_STATE_SUSPEND
);
701 apm_error("suspend", err
);
705 static void standby(void)
709 err
= apm_set_power_state(APM_STATE_STANDBY
);
711 apm_error("standby", err
);
714 static apm_event_t
get_event(void)
718 apm_eventinfo_t info
;
720 static int notified
= 0;
722 /* we don't use the eventinfo */
723 error
= apm_get_event(&event
, &info
);
724 if (error
== APM_SUCCESS
)
727 if ((error
!= APM_NO_EVENTS
) && (notified
++ == 0))
728 apm_error("get_event", error
);
733 static void send_event(apm_event_t event
, apm_event_t undo
,
734 struct apm_bios_struct
*sender
)
736 callback_list_t
* call
;
737 callback_list_t
* fix
;
739 for (call
= callback_list
; call
!= NULL
; call
= call
->next
) {
740 if (call
->callback(event
) && undo
) {
741 for (fix
= callback_list
; fix
!= call
; fix
= fix
->next
)
743 if (apm_bios_info
.version
> 0x100)
744 apm_set_power_state(APM_STATE_REJECT
);
749 queue_event(event
, sender
);
752 static void check_events(void)
756 while ((event
= get_event()) != 0) {
758 if (event
<= NR_APM_EVENT_NAME
)
759 printk(KERN_DEBUG
"APM BIOS received %s notify\n",
760 apm_event_name
[event
- 1]);
762 printk(KERN_DEBUG
"APM BIOS received unknown "
763 "event 0x%02x\n", event
);
766 case APM_SYS_STANDBY
:
767 case APM_USER_STANDBY
:
768 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
769 if (waiting_for_resume
) {
772 waiting_for_resume
= 1;
774 send_event(event
, APM_STANDBY_RESUME
, NULL
);
775 if (standbys_pending
<= 0)
779 case APM_USER_SUSPEND
:
780 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
781 if (apm_bios_info
.version
> 0x100)
782 apm_set_power_state(APM_STATE_REJECT
);
785 case APM_SYS_SUSPEND
:
786 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
787 if (waiting_for_resume
) {
790 waiting_for_resume
= 1;
792 send_event(event
, APM_NORMAL_RESUME
, NULL
);
793 if (suspends_pending
<= 0)
797 case APM_NORMAL_RESUME
:
798 case APM_CRITICAL_RESUME
:
799 case APM_STANDBY_RESUME
:
800 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
801 waiting_for_resume
= 0;
804 send_event(event
, 0, NULL
);
807 case APM_LOW_BATTERY
:
808 case APM_POWER_STATUS_CHANGE
:
809 case APM_CAPABILITY_CHANGE
:
810 send_event(event
, 0, NULL
);
813 case APM_UPDATE_TIME
:
817 case APM_CRITICAL_SUSPEND
:
824 static void do_apm_timer(unsigned long unused
)
828 static int pending_count
= 0;
830 if (((standbys_pending
> 0) || (suspends_pending
> 0))
831 && (apm_bios_info
.version
> 0x100)
832 && (pending_count
-- <= 0)) {
835 err
= apm_set_power_state(APM_STATE_BUSY
);
837 apm_error("busy", err
);
840 if (!(((standbys_pending
> 0) || (suspends_pending
> 0))
841 && (apm_bios_info
.version
== 0x100)))
844 init_timer(&apm_timer
);
845 apm_timer
.expires
= APM_CHECK_TIMEOUT
+ jiffies
;
846 add_timer(&apm_timer
);
849 /* Called from sys_idle, must make sure apm_enabled. */
850 int apm_do_idle(void)
852 #ifdef CONFIG_APM_CPU_IDLE
853 unsigned short error
;
858 APM_SET_CPU_IDLE(error
);
862 clock_slowed
= (apm_bios_info
.flags
& APM_IDLE_SLOWS_CLOCK
) != 0;
869 /* Called from sys_idle, must make sure apm_enabled. */
870 void apm_do_busy(void)
872 #ifdef CONFIG_APM_CPU_IDLE
873 unsigned short error
;
878 #ifndef ALWAYS_CALL_BUSY
883 APM_SET_CPU_BUSY(error
);
889 static int check_apm_bios_struct(struct apm_bios_struct
*as
, const char *func
)
891 if ((as
== NULL
) || (as
->magic
!= APM_BIOS_MAGIC
)) {
892 printk(KERN_ERR
"apm_bios: %s passed bad filp", func
);
898 static ssize_t
do_read(struct file
*fp
, char *buf
, size_t count
, loff_t
*ppos
)
900 struct apm_bios_struct
* as
;
903 struct wait_queue wait
= { current
, NULL
};
905 as
= fp
->private_data
;
906 if (check_apm_bios_struct(as
, "read"))
908 if (count
< sizeof(apm_event_t
))
910 if (queue_empty(as
)) {
911 if (fp
->f_flags
& O_NONBLOCK
)
913 add_wait_queue(&process_list
, &wait
);
915 current
->state
= TASK_INTERRUPTIBLE
;
916 if (queue_empty(as
) && !signal_pending(current
)) {
920 current
->state
= TASK_RUNNING
;
921 remove_wait_queue(&process_list
, &wait
);
924 while ((i
>= sizeof(event
)) && !queue_empty(as
)) {
925 event
= get_queued_event(as
);
926 copy_to_user(buf
, &event
, sizeof(event
));
928 case APM_SYS_SUSPEND
:
929 case APM_USER_SUSPEND
:
933 case APM_SYS_STANDBY
:
934 case APM_USER_STANDBY
:
938 buf
+= sizeof(event
);
943 if (signal_pending(current
))
948 static unsigned int do_poll(struct file
*fp
, poll_table
* wait
)
950 struct apm_bios_struct
* as
;
952 as
= fp
->private_data
;
953 if (check_apm_bios_struct(as
, "select"))
955 poll_wait(fp
, &process_list
, wait
);
956 if (!queue_empty(as
))
957 return POLLIN
| POLLRDNORM
;
961 static int do_ioctl(struct inode
* inode
, struct file
*filp
,
962 u_int cmd
, u_long arg
)
964 struct apm_bios_struct
* as
;
966 as
= filp
->private_data
;
967 if (check_apm_bios_struct(as
, "ioctl"))
972 case APM_IOC_STANDBY
:
973 if (as
->standbys_read
> 0) {
975 as
->standbys_pending
--;
979 send_event(APM_USER_STANDBY
, APM_STANDBY_RESUME
, as
);
980 if (standbys_pending
<= 0)
983 case APM_IOC_SUSPEND
:
984 if (as
->suspends_read
> 0) {
986 as
->suspends_pending
--;
990 send_event(APM_USER_SUSPEND
, APM_NORMAL_RESUME
, as
);
991 if (suspends_pending
<= 0)
1000 static int do_release(struct inode
* inode
, struct file
* filp
)
1002 struct apm_bios_struct
* as
;
1004 as
= filp
->private_data
;
1005 filp
->private_data
= NULL
;
1006 if (check_apm_bios_struct(as
, "release"))
1008 if (as
->standbys_pending
> 0) {
1009 standbys_pending
-= as
->standbys_pending
;
1010 if (standbys_pending
<= 0)
1013 if (as
->suspends_pending
> 0) {
1014 suspends_pending
-= as
->suspends_pending
;
1015 if (suspends_pending
<= 0)
1018 if (user_list
== as
)
1019 user_list
= as
->next
;
1021 struct apm_bios_struct
* as1
;
1023 for (as1
= user_list
;
1024 (as1
!= NULL
) && (as1
->next
!= as
);
1028 printk(KERN_ERR
"apm_bios: filp not in user list");
1030 as1
->next
= as
->next
;
1032 kfree_s(as
, sizeof(*as
));
1036 static int do_open(struct inode
* inode
, struct file
* filp
)
1038 struct apm_bios_struct
* as
;
1040 as
= (struct apm_bios_struct
*)kmalloc(sizeof(*as
), GFP_KERNEL
);
1042 printk(KERN_ERR
"apm_bios: cannot allocate struct of size %d bytes",
1046 as
->magic
= APM_BIOS_MAGIC
;
1047 as
->event_tail
= as
->event_head
= 0;
1048 as
->suspends_pending
= as
->standbys_pending
= 0;
1049 as
->suspends_read
= as
->standbys_read
= 0;
1051 * XXX - this is a tiny bit broken, when we consider BSD
1052 * process accounting. If the device is opened by root, we
1053 * instantly flag that we used superuser privs. Who knows,
1054 * we might close the device immediately without doing a
1055 * privileged operation -- cevans
1057 as
->suser
= capable(CAP_SYS_ADMIN
);
1058 as
->next
= user_list
;
1060 filp
->private_data
= as
;
1064 #ifdef CONFIG_PROC_FS
1065 int apm_get_info(char *buf
, char **start
, off_t fpos
, int length
, int dummy
)
1071 unsigned short error
;
1072 unsigned short ac_line_status
= 0xff;
1073 unsigned short battery_status
= 0xff;
1074 unsigned short battery_flag
= 0xff;
1075 int percentage
= -1;
1076 int time_units
= -1;
1083 if (!(error
= apm_get_power_status(&bx
, &cx
, &dx
))) {
1084 ac_line_status
= (bx
>> 8) & 0xff;
1085 battery_status
= bx
& 0xff;
1086 if ((cx
& 0xff) != 0xff)
1087 percentage
= cx
& 0xff;
1089 if (apm_bios_info
.version
> 0x100) {
1090 battery_flag
= (cx
>> 8) & 0xff;
1092 if ((dx
& 0x8000) == 0x8000) {
1094 time_units
= dx
& 0x7ffe;
1097 time_units
= dx
& 0x7fff;
1102 /* Arguments, with symbols from linux/apm_bios.h. Information is
1103 from the Get Power Status (0x0a) call unless otherwise noted.
1105 0) Linux driver version (this will change if format changes)
1106 1) APM BIOS Version. Usually 1.0 or 1.1.
1107 2) APM flags from APM Installation Check (0x00):
1108 bit 0: APM_16_BIT_SUPPORT
1109 bit 1: APM_32_BIT_SUPPORT
1110 bit 2: APM_IDLE_SLOWS_CLOCK
1111 bit 3: APM_BIOS_DISABLED
1112 bit 4: APM_BIOS_DISENGAGED
1116 0x02: On backup power (APM BIOS 1.1 only)
1129 bit 7: No system battery
1131 6) Remaining battery life (percentage of charge):
1134 7) Remaining battery life (time units):
1135 Number of remaining minutes or seconds
1137 8) min = minutes; sec = seconds */
1139 p
+= sprintf(p
, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1141 (apm_bios_info
.version
>> 8) & 0xff,
1142 apm_bios_info
.version
& 0xff,
1143 apm_bios_info
.flags
,
1155 __initfunc(void apm_bios_init(void))
1160 unsigned short error
;
1163 static struct proc_dir_entry
*ent
;
1166 if (smp_num_cpus
> 1) {
1167 printk(KERN_NOTICE
"APM disabled: APM is not SMP safe.\n");
1171 if (apm_bios_info
.version
== 0) {
1172 printk(KERN_INFO
"APM BIOS not found.\n");
1175 printk(KERN_INFO
"APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
1176 ((apm_bios_info
.version
>> 8) & 0xff) + '0',
1177 (apm_bios_info
.version
& 0xff) + '0',
1178 apm_bios_info
.flags
,
1180 if ((apm_bios_info
.flags
& APM_32_BIT_SUPPORT
) == 0) {
1181 printk(KERN_INFO
" No 32 bit BIOS support\n");
1186 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1187 * but is reportedly a 1.0 BIOS.
1189 if (apm_bios_info
.version
== 0x001)
1190 apm_bios_info
.version
= 0x100;
1192 /* BIOS < 1.2 doesn't set cseg_16_len */
1193 if (apm_bios_info
.version
< 0x102)
1194 apm_bios_info
.cseg_16_len
= 0xFFFF; /* 64k */
1196 printk(KERN_INFO
" Entry %x:%lx cseg16 %x dseg %x",
1197 apm_bios_info
.cseg
, apm_bios_info
.offset
,
1198 apm_bios_info
.cseg_16
, apm_bios_info
.dseg
);
1199 if (apm_bios_info
.version
> 0x100)
1200 printk(" cseg len %x, cseg16 len %x, dseg len %x",
1201 apm_bios_info
.cseg_len
, apm_bios_info
.cseg_16_len
,
1202 apm_bios_info
.dseg_len
);
1206 * Set up a segment that references the real mode segment 0x40
1207 * that extends up to the end of page zero (that we have reserved).
1208 * This is for buggy BIOS's that refer to (real mode) segment 0x40
1209 * even though they are called in protected mode.
1211 set_base(gdt
[APM_40
>> 3],
1212 __va((unsigned long)0x40 << 4));
1213 set_limit(gdt
[APM_40
>> 3], 4096 - (0x40 << 4));
1215 apm_bios_entry
.offset
= apm_bios_info
.offset
;
1216 apm_bios_entry
.segment
= APM_CS
;
1217 set_base(gdt
[APM_CS
>> 3],
1218 __va((unsigned long)apm_bios_info
.cseg
<< 4));
1219 set_base(gdt
[APM_CS_16
>> 3],
1220 __va((unsigned long)apm_bios_info
.cseg_16
<< 4));
1221 set_base(gdt
[APM_DS
>> 3],
1222 __va((unsigned long)apm_bios_info
.dseg
<< 4));
1223 if (apm_bios_info
.version
== 0x100) {
1224 set_limit(gdt
[APM_CS
>> 3], 64 * 1024);
1225 set_limit(gdt
[APM_CS_16
>> 3], 64 * 1024);
1226 set_limit(gdt
[APM_DS
>> 3], 64 * 1024);
1228 #ifdef APM_RELAX_SEGMENTS
1229 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1230 set_limit(gdt
[APM_CS
>> 3], 64 * 1024);
1231 /* For some unknown machine. */
1232 set_limit(gdt
[APM_CS_16
>> 3], 64 * 1024);
1233 /* For the DEC Hinote Ultra CT475 (and others?) */
1234 set_limit(gdt
[APM_DS
>> 3], 64 * 1024);
1236 set_limit(gdt
[APM_CS
>> 3], apm_bios_info
.cseg_len
);
1237 set_limit(gdt
[APM_CS_16
>> 3], apm_bios_info
.cseg_16_len
);
1238 set_limit(gdt
[APM_DS
>> 3], apm_bios_info
.dseg_len
);
1240 /* The APM 1.2 docs state that the apm_driver_version
1241 * call can fail if we try to connect as 1.2 to a 1.1 bios.
1243 apm_bios_info
.version
= 0x0102;
1244 error
= apm_driver_version(&apm_bios_info
.version
);
1245 if (error
!= 0) { /* Fall back to an APM 1.1 connection. */
1246 apm_bios_info
.version
= 0x0101;
1247 error
= apm_driver_version(&apm_bios_info
.version
);
1249 if (error
!= 0) /* Fall back to an APM 1.0 connection. */
1250 apm_bios_info
.version
= 0x100;
1252 apm_engage_power_management(0x0001);
1253 printk( " Connection version %d.%d\n",
1254 (apm_bios_info
.version
>> 8) & 0xff,
1255 apm_bios_info
.version
& 0xff );
1259 error
= apm_get_power_status(&bx
, &cx
, &dx
);
1261 printk(KERN_INFO
" Power status not available\n");
1263 switch ((bx
>> 8) & 0xff) {
1264 case 0: power_stat
= "off line"; break;
1265 case 1: power_stat
= "on line"; break;
1266 case 2: power_stat
= "on backup power"; break;
1267 default: power_stat
= "unknown"; break;
1269 switch (bx
& 0xff) {
1270 case 0: bat_stat
= "high"; break;
1271 case 1: bat_stat
= "low"; break;
1272 case 2: bat_stat
= "critical"; break;
1273 case 3: bat_stat
= "charging"; break;
1274 default: bat_stat
= "unknown"; break;
1276 printk(KERN_INFO
" AC %s, battery status %s, battery life ",
1277 power_stat
, bat_stat
);
1278 if ((cx
& 0xff) == 0xff)
1279 printk("unknown\n");
1281 printk("%d%%\n", cx
& 0xff);
1282 if (apm_bios_info
.version
> 0x100) {
1283 printk(" battery flag 0x%02x, battery life ",
1286 printk("unknown\n");
1289 printk("%d minutes\n", dx
& 0x7ffe );
1291 printk("%d seconds\n", dx
& 0x7fff );
1296 #ifdef CONFIG_APM_DO_ENABLE
1298 * This call causes my NEC UltraLite Versa 33/C to hang if it is
1299 * booted with PM disabled but not in the docking station.
1302 error
= apm_enable_power_management();
1304 apm_error("enable power management", error
);
1305 if (error
== APM_DISABLED
)
1309 init_timer(&apm_timer
);
1310 apm_timer
.function
= do_apm_timer
;
1311 apm_timer
.expires
= APM_CHECK_TIMEOUT
+ jiffies
;
1312 add_timer(&apm_timer
);
1314 #ifdef CONFIG_PROC_FS
1315 ent
= create_proc_entry("apm", 0, 0);
1316 ent
->get_info
= apm_get_info
;
1319 misc_register(&apm_device
);