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
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,
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
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>
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>
75 #include <linux/stat.h>
76 #include <linux/proc_fs.h>
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.
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').
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
201 # define APM_DO_CLI "cli\n\t"
206 # define APM_DO_ZERO_SEGS \
211 "xorl %%edx, %%edx\n\t" \
212 "mov %%dx, %%ds\n\t" \
213 "mov %%dx, %%es\n\t" \
214 "mov %%dx, %%fs\n\t" \
216 # define APM_DO_RESTORE_SEGS \
222 # define APM_DO_ZERO_SEGS
223 # define APM_DO_RESTORE_SEGS
226 #define APM_BIOS_CALL(error_reg) \
227 __asm__ __volatile__( \
231 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
232 "setc %%" # error_reg "\n\t" \
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) \
246 #define APM_SET_CPU_BUSY(error) \
252 #define APM_SET_POWER_STATE(state, error) \
255 : "a" (0x5307), "b" (0x0001), "c" (state) \
258 #ifdef CONFIG_APM_DISPLAY_BLANK
259 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
262 : "a" (0x5307), "b" (0x01ff), "c" (state) \
266 #ifdef CONFIG_APM_DO_ENABLE
267 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
270 : "a" (0x5308), "b" (device), "c" (1) \
274 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
276 : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
277 : "a" (0x530a), "b" (1) \
280 #define APM_GET_EVENT(event, error) \
282 : "=a" (error), "=b" (event) \
286 #define APM_DRIVER_VERSION(ver, ax, error) \
288 : "=a" (ax), "=b" (error) \
289 : "a" (0x530e), "b" (0), "c" (ver) \
292 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
295 : "a" (0x530f), "b" (device), "c" (1) \
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);
318 extern int apm_register_callback(int (*)(apm_event_t
));
319 extern void apm_unregister_callback(int (*)(apm_event_t
));
324 static asmlinkage
struct {
325 unsigned long offset
;
326 unsigned short segment
;
328 static int apm_enabled
= 0;
329 #ifdef CONFIG_APM_CPU_IDLE
330 static int clock_slowed
= 0;
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 */
346 static char * apm_event_name
[] = {
352 "power status change",
357 "system standby resume"
359 #define NR_APM_EVENT_NAME \
360 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
363 static struct file_operations apm_bios_fops
= {
377 static struct miscdevice apm_device
= {
383 typedef struct callback_list_t
{
384 int (* callback
)(apm_event_t
);
385 struct callback_list_t
* next
;
388 static callback_list_t
* callback_list
= NULL
;
390 typedef struct 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
)
417 APM_DRIVER_VERSION(*val
, *val
, error
);
424 static int apm_get_event(apm_event_t
*event
)
428 APM_GET_EVENT(*event
, error
);
434 int apm_set_power_state(u_short state
)
438 APM_SET_POWER_STATE(state
, error
);
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
)
450 APM_SET_DISPLAY_POWER_STATE(state
, error
);
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)
463 APM_ENABLE_POWER_MANAGEMENT((apm_bios_info
.version
> 0x100)
472 static int apm_get_power_status(u_short
*status
, u_short
*bat
, u_short
*life
)
476 APM_GET_POWER_STATUS(*status
, *bat
, *life
, error
);
482 static inline int apm_engage_power_management(u_short device
)
486 APM_ENGAGE_POWER_MANAGEMENT(device
, error
);
492 static void apm_error(char *str
, int err
)
496 for (i
= 0; i
< ERROR_COUNT
; i
++)
497 if (error_table
[i
].key
== err
) break;
499 printk(KERN_NOTICE
"apm_bios: %s: %s\n", str
, error_table
[i
].msg
);
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
512 error
= apm_set_display_power_state(APM_STATE_STANDBY
);
513 if (error
== APM_SUCCESS
)
515 apm_error("set display standby", error
);
520 /* Called from console driver -- must make sure apm_enabled. */
521 int apm_display_unblank(void)
523 #ifdef CONFIG_APM_DISPLAY_BLANK
528 error
= apm_set_display_power_state(APM_STATE_READY
);
529 if (error
== APM_SUCCESS
)
531 apm_error("set display ready", error
);
536 int apm_register_callback(int (*callback
)(apm_event_t
))
538 callback_list_t
* new;
540 new = kmalloc(sizeof(callback_list_t
), GFP_KERNEL
);
543 new->callback
= callback
;
544 new->next
= callback_list
;
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
)
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
)
580 for (as
= user_list
; as
!= NULL
; as
= as
->next
) {
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
;
590 case APM_SYS_SUSPEND
:
591 case APM_USER_SUSPEND
:
592 as
->suspends_pending
++;
596 case APM_SYS_STANDBY
:
597 case APM_USER_STANDBY
:
598 as
->standbys_pending
++;
603 wake_up_interruptible(&process_list
);
607 static void set_time(void)
611 if (!got_clock_diff
) /* Don't know time zone, can't set clock */
616 CURRENT_TIME
= get_cmos_time() + clock_cmos_diff
;
617 restore_flags(flags
);
620 static void suspend(void)
625 /* Estimate time zone so that set_time can
628 clock_cmos_diff
= -get_cmos_time();
630 clock_cmos_diff
+= CURRENT_TIME
;
632 restore_flags(flags
);
634 err
= apm_set_power_state(APM_STATE_SUSPEND
);
636 apm_error("suspend", err
);
640 static void standby(void)
644 err
= apm_set_power_state(APM_STATE_STANDBY
);
646 apm_error("standby", err
);
649 static apm_event_t
get_event(void)
654 static int notified
= 0;
656 error
= apm_get_event(&event
);
657 if (error
== APM_SUCCESS
)
660 if ((error
!= APM_NO_EVENTS
) && (notified
++ == 0))
661 apm_error("get_event", error
);
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
)
676 if (apm_bios_info
.version
> 0x100)
677 apm_set_power_state(APM_STATE_REJECT
);
682 queue_event(event
, sender
);
685 static void check_events(void)
689 while ((event
= get_event()) != 0) {
691 case APM_SYS_STANDBY
:
692 case APM_USER_STANDBY
:
693 send_event(event
, APM_STANDBY_RESUME
, NULL
);
694 if (standbys_pending
<= 0)
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
);
704 case APM_SYS_SUSPEND
:
705 send_event(event
, APM_NORMAL_RESUME
, NULL
);
706 if (suspends_pending
<= 0)
710 case APM_NORMAL_RESUME
:
711 case APM_CRITICAL_RESUME
:
712 case APM_STANDBY_RESUME
:
714 send_event(event
, 0, NULL
);
717 case APM_LOW_BATTERY
:
718 case APM_POWER_STATUS_CHANGE
:
719 send_event(event
, 0, NULL
);
722 case APM_UPDATE_TIME
:
726 case APM_CRITICAL_SUSPEND
:
731 if (event
<= NR_APM_EVENT_NAME
)
732 printk(KERN_DEBUG
"APM BIOS received %s notify\n",
733 apm_event_name
[event
- 1]);
735 printk(KERN_DEBUG
"APM BIOS received unknown event 0x%02x\n",
741 static void do_apm_timer(unsigned long unused
)
745 static int pending_count
= 0;
747 if (((standbys_pending
> 0) || (suspends_pending
> 0))
748 && (apm_bios_info
.version
> 0x100)
749 && (pending_count
-- <= 0)) {
752 err
= apm_set_power_state(APM_STATE_BUSY
);
754 apm_error("busy", err
);
757 if (!(((standbys_pending
> 0) || (suspends_pending
> 0))
758 && (apm_bios_info
.version
== 0x100)))
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
;
775 APM_SET_CPU_IDLE(error
);
779 clock_slowed
= (apm_bios_info
.flags
& APM_IDLE_SLOWS_CLOCK
) != 0;
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
;
795 #ifndef ALWAYS_CALL_BUSY
800 APM_SET_CPU_BUSY(error
);
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
);
815 static long do_read(struct inode
*inode
, struct file
*fp
,
816 char *buf
, unsigned long count
)
818 struct apm_bios_struct
* as
;
821 struct wait_queue wait
= { current
, NULL
};
823 as
= fp
->private_data
;
824 if (check_apm_bios_struct(as
, "read"))
826 if (count
< sizeof(apm_event_t
))
828 if (queue_empty(as
)) {
829 if (fp
->f_flags
& O_NONBLOCK
)
831 add_wait_queue(&process_list
, &wait
);
833 current
->state
= TASK_INTERRUPTIBLE
;
835 && !(current
->signal
& ~current
->blocked
)) {
839 current
->state
= TASK_RUNNING
;
840 remove_wait_queue(&process_list
, &wait
);
843 while ((i
>= sizeof(event
)) && !queue_empty(as
)) {
844 event
= get_queued_event(as
);
845 copy_to_user(buf
, &event
, sizeof(event
));
847 case APM_SYS_SUSPEND
:
848 case APM_USER_SUSPEND
:
852 case APM_SYS_STANDBY
:
853 case APM_USER_STANDBY
:
857 buf
+= sizeof(event
);
862 if (current
->signal
& ~current
->blocked
)
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"))
874 poll_wait(&process_list
, wait
);
875 if (!queue_empty(as
))
876 return POLLIN
| POLLRDNORM
;
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"))
891 case APM_IOC_STANDBY
:
892 if (as
->standbys_read
> 0) {
894 as
->standbys_pending
--;
898 send_event(APM_USER_STANDBY
, APM_STANDBY_RESUME
, as
);
899 if (standbys_pending
<= 0)
902 case APM_IOC_SUSPEND
:
903 if (as
->suspends_read
> 0) {
905 as
->suspends_pending
--;
909 send_event(APM_USER_SUSPEND
, APM_NORMAL_RESUME
, as
);
910 if (suspends_pending
<= 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"))
927 if (as
->standbys_pending
> 0) {
928 standbys_pending
-= as
->standbys_pending
;
929 if (standbys_pending
<= 0)
932 if (as
->suspends_pending
> 0) {
933 suspends_pending
-= as
->suspends_pending
;
934 if (suspends_pending
<= 0)
938 user_list
= as
->next
;
940 struct apm_bios_struct
* as1
;
942 for (as1
= user_list
;
943 (as1
!= NULL
) && (as1
->next
!= as
);
947 printk(KERN_ERR
"apm_bios: filp not in user list");
949 as1
->next
= as
->next
;
951 kfree_s(as
, sizeof(*as
));
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
);
961 printk(KERN_ERR
"apm_bios: cannot allocate struct of size %d bytes",
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;
970 as
->next
= user_list
;
972 filp
->private_data
= as
;
976 #ifdef CONFIG_PROC_FS
977 int apm_get_info(char *buf
, char **start
, off_t fpos
, int length
, int dummy
)
983 unsigned short error
;
984 unsigned short ac_line_status
= 0xff;
985 unsigned short battery_status
= 0xff;
986 unsigned short battery_flag
= 0xff;
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;
1004 if ((dx
& 0x8000) == 0x8000) {
1006 time_units
= dx
& 0x7ffe;
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
1028 0x02: On backup power (APM BIOS 1.1 only)
1041 bit 7: No system battery
1043 6) Remaining battery life (percentage of charge):
1046 7) Remaining battery life (time units):
1047 Number of remaining minutes or seconds
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",
1053 (apm_bios_info
.version
>> 8) & 0xff,
1054 apm_bios_info
.version
& 0xff,
1055 apm_bios_info
.flags
,
1067 __initfunc(void apm_bios_init(void))
1072 unsigned short error
;
1075 static struct proc_dir_entry
*ent
;
1078 printk(KERN_NOTICE
"APM disabled: APM is not SMP safe.\n");
1081 if (apm_bios_info
.version
== 0) {
1082 printk(KERN_INFO
"APM BIOS not found.\n");
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
,
1090 if ((apm_bios_info
.flags
& APM_32_BIT_SUPPORT
) == 0) {
1091 printk(KERN_INFO
" No 32 bit BIOS support\n");
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
);
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);
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);
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
);
1135 apm_bios_info
.version
= 0x0101;
1136 error
= apm_driver_version(&apm_bios_info
.version
);
1138 apm_bios_info
.version
= 0x100;
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
);
1150 printk(KERN_INFO
" Power status not available\n");
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");
1170 printk("%d%%\n", cx
& 0xff);
1171 if (apm_bios_info
.version
> 0x100) {
1172 printk(" battery flag 0x%02x, battery life ",
1175 printk("unknown\n");
1178 printk("%d minutes\n", dx
& 0x7ffe );
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.
1191 error
= apm_enable_power_management();
1193 apm_error("enable power management", error
);
1194 if (error
== APM_DISABLED
)
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
;
1208 misc_register(&apm_device
);