2 * APM BIOS driver for Linux
3 * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.com)
5 * Initial development of this driver was funded by NEC Australia P/L
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
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
31 * Aug 1998, Version 1.5
32 * Sep 1998, Version 1.6
33 * Nov 1998, Version 1.7
34 * Jan 1999, Version 1.8
35 * Jan 1999, Version 1.9
36 * Oct 1999, Version 1.10
37 * Nov 1999, Version 1.11
38 * Jan 2000, Version 1.12
39 * Feb 2000, Version 1.13
40 * Nov 2000, Version 1.14
43 * 0.6b: first version in official kernel, Linux 1.3.46
44 * 0.7: changed /proc/apm format, Linux 1.3.58
45 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
46 * 0.9: only call bios if bios is present, Linux 1.3.72
47 * 1.0: use fixed device number, consolidate /proc/apm into this file,
49 * 1.1: support user-space standby and suspend, power off after system
50 * halted, Linux 1.3.98
51 * 1.2: When resetting RTC after resume, take care so that the time
52 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
53 * <jtoth@princeton.edu>); improve interaction between
54 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
55 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
56 * levels to the printk calls. APM is not defined for SMP machines.
57 * The new replacment for it is, but Linux doesn't yet support this.
58 * Alan Cox Linux 2.1.55
59 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
60 * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
61 * Dean Gaudet <dgaudet@arctic.org>.
62 * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
63 * 1.5: Fix segment register reloading (in case of bad segments saved
66 * 1.6: Cope with complier/assembler differences.
67 * Only try to turn off the first display device.
68 * Fix OOPS at power off with no APM BIOS by Jan Echternach
69 * <echter@informatik.uni-rostock.de>
71 * 1.7: Modify driver's cached copy of the disabled/disengaged flags
72 * to reflect current state of APM BIOS.
73 * Chris Rankin <rankinc@bellsouth.net>
74 * Reset interrupt 0 timer to 100Hz after suspend
75 * Chad Miller <cmiller@surfsouth.com>
76 * Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
77 * Richard Gooch <rgooch@atnf.csiro.au>
78 * Allow boot time disabling of APM
79 * Make boot messages far less verbose by default
82 * 1.8: Add CONFIG_APM_RTC_IS_GMT
83 * Richard Gooch <rgooch@atnf.csiro.au>
84 * change APM_NOINTS to CONFIG_APM_ALLOW_INTS
85 * remove dependency on CONFIG_PROC_FS
87 * 1.9: Fix small typo. <laslo@ilo.opole.pl>
88 * Try to cope with BIOS's that need to have all display
89 * devices blanked and not just the first one.
90 * Ross Paterson <ross@soi.city.ac.uk>
91 * Fix segment limit setting it has always been wrong as
92 * the segments needed to have byte granularity.
93 * Mark a few things __init.
94 * Add hack to allow power off of SMP systems by popular request.
95 * Use CONFIG_SMP instead of __SMP__
96 * Ignore BOUNCES for three seconds.
98 * 1.10: Fix for Thinkpad return code.
99 * Merge 2.2 and 2.3 drivers.
100 * Remove APM dependencies in arch/i386/kernel/process.c
101 * Remove APM dependencies in drivers/char/sysrq.c
102 * Reset time across standby.
103 * Allow more inititialisation on SMP.
104 * Remove CONFIG_APM_POWER_OFF and make it boot time
105 * configurable (default on).
106 * Make debug only a boot time parameter (remove APM_DEBUG).
107 * Try to blank all devices on any error.
108 * 1.11: Remove APM dependencies in drivers/char/console.c
109 * Check nr_running to detect if we are idle (from
110 * Borislav Deianov <borislav@lix.polytechnique.fr>)
111 * Fix for bioses that don't zero the top part of the
112 * entrypoint offset (Mario Sitta <sitta@al.unipmn.it>)
113 * (reported by Panos Katsaloulis <teras@writeme.com>).
114 * Real mode power off patch (Walter Hofmann
115 * <Walter.Hofmann@physik.stud.uni-erlangen.de>).
116 * 1.12: Remove CONFIG_SMP as the compiler will optimize
117 * the code away anyway (smp_num_cpus == 1 in UP)
118 * noted by Artur Skawina <skawina@geocities.com>.
119 * Make power off under SMP work again.
120 * Fix thinko with initial engaging of BIOS.
121 * Make sure power off only happens on CPU 0
122 * (Paul "Rusty" Russell <rusty@linuxcare.com>).
123 * Do error notification to user mode if BIOS calls fail.
124 * Move entrypoint offset fix to ...boot/setup.S
125 * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
126 * Remove smp-power-off. SMP users must now specify
127 * "apm=power-off" on the kernel command line. Suggested
128 * by Jim Avera <jima@hal.com>, modified by Alan Cox
129 * <alan@lxorguk.ukuu.org.uk>.
130 * Register the /proc/apm entry even on SMP so that
131 * scripts that check for it before doing power off
132 * work (Jim Avera <jima@hal.com>).
133 * 1.13: Changes for new pm_ interfaces (Andy Henroid
134 * <andy_henroid@yahoo.com>).
135 * Modularize the code.
136 * Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS
137 * is now the way life works).
138 * Fix thinko in suspend() (wrong return).
139 * Notify drivers on critical suspend.
140 * Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz>
142 * Disable interrupts while we are suspended (Andy Henroid
143 * <andy_henroid@yahoo.com> fixed by sfr).
144 * Make power off work on SMP again (Tony Hoyle
145 * <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr.
146 * Remove CONFIG_APM_SUSPEND_BOUNCE. The bounce ignore
147 * interval is now configurable.
148 * 1.14: Make connection version persist across module unload/load.
149 * Enable and engage power management earlier.
150 * Disengage power management on module unload.
154 * Intel Corporation, Microsoft Corporation. Advanced Power Management
155 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
156 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
158 * [This document is available free from Intel by calling 800.628.8686 (fax
159 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
160 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
161 * available from Microsoft by calling 206.882.8080.]
164 * Intel Corporation, Microsoft Corporation. Advanced Power Management
165 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
167 * [This document is available from Microsoft at:
168 * http://www.microsoft.com/hwdev/busbios/amp_12.htm]
171 #include <linux/config.h>
172 #include <linux/module.h>
174 #include <linux/poll.h>
175 #include <linux/types.h>
176 #include <linux/stddef.h>
177 #include <linux/timer.h>
178 #include <linux/fcntl.h>
179 #include <linux/malloc.h>
180 #include <linux/stat.h>
181 #include <linux/proc_fs.h>
182 #include <linux/miscdevice.h>
183 #include <linux/apm_bios.h>
184 #include <linux/init.h>
185 #include <linux/sched.h>
186 #include <linux/pm.h>
187 #include <linux/kernel.h>
188 #include <linux/smp_lock.h>
190 #include <asm/system.h>
191 #include <asm/uaccess.h>
192 #include <asm/desc.h>
194 extern unsigned long get_cmos_time(void);
195 extern void machine_real_restart(unsigned char *, int);
197 #ifdef CONFIG_MAGIC_SYSRQ
198 extern void (*sysrq_power_off
)(void);
200 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
201 extern int (*console_blank_hook
)(int);
205 * The apm_bios device is one of the misc char devices.
206 * This is its minor number.
208 #define APM_MINOR_DEV 134
211 * See Documentation/Config.help for the configuration options.
213 * Various options can be changed at boot time as follows:
214 * (We allow underscores for compatibility with the modules code)
215 * apm=on/off enable/disable APM
216 * [no-]debug log some debugging messages
217 * [no-]power[-_]off power off on shutdown
220 /* KNOWN PROBLEM MACHINES:
222 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
223 * [Confirmed by TI representative]
224 * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
225 * [Confirmed by BIOS disassembly]
226 * [This may work now ...]
227 * P: Toshiba 1950S: battery life information only gets updated after resume
228 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
229 * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
231 * Legend: U = unusable with APM patches
232 * P = partially usable with APM patches
236 * Define to always call the APM BIOS busy routine even if the clock was
237 * not slowed by the idle routine.
239 #define ALWAYS_CALL_BUSY
242 * Define to make the APM BIOS calls zero all data segment registers (so
243 * that an incorrect BIOS implementation will cause a kernel panic if it
244 * tries to write to arbitrary memory).
246 #define APM_ZERO_SEGS
249 * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is
250 * supposed to provide limit information that it recognizes. Many machines
251 * do this correctly, but many others do not restrict themselves to their
252 * claimed limit. When this happens, they will cause a segmentation
253 * violation in the kernel at boot time. Most BIOS's, however, will
254 * respect a 64k limit, so we use that. If you want to be pedantic and
255 * hold your BIOS to its claims, then undefine this.
257 #define APM_RELAX_SEGMENTS
260 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
261 * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
262 * David Chen <chen@ctpa04.mit.edu>
264 #undef INIT_TIMER_AFTER_SUSPEND
266 #ifdef INIT_TIMER_AFTER_SUSPEND
267 #include <linux/timex.h>
269 #include <linux/delay.h>
273 * Need to poll the APM BIOS every second
275 #define APM_CHECK_TIMEOUT (HZ)
278 * Ignore suspend events for this amount of time after a resume
280 #define DEFAULT_BOUNCE_INTERVAL (3 * HZ)
283 * Save a segment register away
285 #define savesegment(seg, where) \
286 __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
289 * Maximum number of events stored
291 #define APM_MAX_EVENTS 20
294 * The per-file APM data
298 struct apm_user
* next
;
302 int suspends_pending
;
303 int standbys_pending
;
308 apm_event_t events
[APM_MAX_EVENTS
];
312 * The magic number in apm_user
314 #define APM_BIOS_MAGIC 0x4101
320 unsigned long offset
;
321 unsigned short segment
;
323 #ifdef CONFIG_APM_CPU_IDLE
324 static int clock_slowed
;
326 static int suspends_pending
;
327 static int standbys_pending
;
328 static int waiting_for_resume
;
329 static int ignore_normal_resume
;
330 static int bounce_interval
= DEFAULT_BOUNCE_INTERVAL
;
332 #ifdef CONFIG_APM_RTC_IS_GMT
333 # define clock_cmos_diff 0
334 # define got_clock_diff 1
336 static long clock_cmos_diff
;
337 static int got_clock_diff
;
340 static int apm_disabled
;
342 static int power_off
;
344 static int power_off
= 1;
346 static int exit_kapmd
;
347 static int kapmd_running
;
349 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue
);
350 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue
);
351 static struct apm_user
* user_list
;
353 static char driver_version
[] = "1.14"; /* no spaces */
355 static char * apm_event_name
[] = {
361 "power status change",
366 "system standby resume",
367 "capabilities change"
369 #define NR_APM_EVENT_NAME \
370 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
372 typedef struct lookup_t
{
377 static const lookup_t error_table
[] = {
378 /* N/A { APM_SUCCESS, "Operation succeeded" }, */
379 { APM_DISABLED
, "Power management disabled" },
380 { APM_CONNECTED
, "Real mode interface already connected" },
381 { APM_NOT_CONNECTED
, "Interface not connected" },
382 { APM_16_CONNECTED
, "16 bit interface already connected" },
383 /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
384 { APM_32_CONNECTED
, "32 bit interface already connected" },
385 { APM_32_UNSUPPORTED
, "32 bit interface not supported" },
386 { APM_BAD_DEVICE
, "Unrecognized device ID" },
387 { APM_BAD_PARAM
, "Parameter out of range" },
388 { APM_NOT_ENGAGED
, "Interface not engaged" },
389 { APM_BAD_FUNCTION
, "Function not supported" },
390 { APM_RESUME_DISABLED
, "Resume timer disabled" },
391 { APM_BAD_STATE
, "Unable to enter requested state" },
392 /* N/A { APM_NO_EVENTS, "No events pending" }, */
393 { APM_NO_ERROR
, "BIOS did not set a return code" },
394 { APM_NOT_PRESENT
, "No APM present" }
396 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
399 * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and
400 * CONFIG_APM_ALLOW_INTS, we are being really paranoid here! Not only
401 * are interrupts disabled, but all the segment registers (except SS)
402 * are saved and zeroed this means that if the BIOS tries to reference
403 * any data without explicitly loading the segment registers, the kernel
404 * will fault immediately rather than have some unforeseen circumstances
405 * for the rest of the kernel. And it will be very obvious! :-) Doing
406 * this depends on CS referring to the same physical memory as DS so that
407 * DS can be zeroed before the call. Unfortunately, we can't do anything
408 * about the stack segment/pointer. Also, we tell the compiler that
409 * everything could change.
411 * Also, we KNOW that for the non error case of apm_bios_call, there
412 * is no useful data returned in the low order 8 bits of eax.
414 #ifndef CONFIG_APM_ALLOW_INTS
415 # define APM_DO_CLI __cli()
417 # define APM_DO_CLI __sti()
420 # define APM_DECL_SEGS \
421 unsigned int saved_fs; unsigned int saved_gs;
422 # define APM_DO_SAVE_SEGS \
423 savesegment(fs, saved_fs); savesegment(gs, saved_gs)
424 # define APM_DO_ZERO_SEGS \
427 "xorl %%edx, %%edx\n\t" \
428 "mov %%dx, %%ds\n\t" \
429 "mov %%dx, %%es\n\t" \
430 "mov %%dx, %%fs\n\t" \
432 # define APM_DO_POP_SEGS \
435 # define APM_DO_RESTORE_SEGS \
436 loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)
438 # define APM_DECL_SEGS
439 # define APM_DO_SAVE_SEGS
440 # define APM_DO_ZERO_SEGS
441 # define APM_DO_POP_SEGS
442 # define APM_DO_RESTORE_SEGS
445 static u8
apm_bios_call(u32 func
, u32 ebx_in
, u32 ecx_in
,
446 u32
*eax
, u32
*ebx
, u32
*ecx
, u32
*edx
, u32
*esi
)
455 * N.B. We do NOT need a cld after the BIOS call
456 * because we always save and restore the flags.
458 __asm__
__volatile__(APM_DO_ZERO_SEGS
461 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry
) "\n\t"
466 : "=a" (*eax
), "=b" (*ebx
), "=c" (*ecx
), "=d" (*edx
),
468 : "a" (func
), "b" (ebx_in
), "c" (ecx_in
)
471 __restore_flags(flags
);
476 * This version only returns one value (usually an error code)
479 static u8
apm_bios_call_simple(u32 func
, u32 ebx_in
, u32 ecx_in
, u32
*eax
)
492 * N.B. We do NOT need a cld after the BIOS call
493 * because we always save and restore the flags.
495 __asm__
__volatile__(APM_DO_ZERO_SEGS
498 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry
) "\n\t"
503 : "=a" (*eax
), "=b" (error
), "=c" (cx
), "=d" (dx
),
505 : "a" (func
), "b" (ebx_in
), "c" (ecx_in
)
509 __restore_flags(flags
);
513 static int __init
apm_driver_version(u_short
*val
)
517 if (apm_bios_call_simple(APM_FUNC_VERSION
, 0, *val
, &eax
))
518 return (eax
>> 8) & 0xff;
523 static int apm_get_event(apm_event_t
*event
, apm_eventinfo_t
*info
)
530 if (apm_bios_call(APM_FUNC_GET_EVENT
, 0, 0, &eax
, &ebx
, &ecx
,
532 return (eax
>> 8) & 0xff;
534 if (apm_info
.connection_version
< 0x0102)
535 *info
= ~0; /* indicate info not valid */
541 static int set_power_state(u_short what
, u_short state
)
545 if (apm_bios_call_simple(APM_FUNC_SET_STATE
, what
, state
, &eax
))
546 return (eax
>> 8) & 0xff;
550 static int apm_set_power_state(u_short state
)
552 return set_power_state(APM_DEVICE_ALL
, state
);
555 #ifdef CONFIG_APM_CPU_IDLE
556 static int apm_do_idle(void)
560 if (apm_bios_call_simple(APM_FUNC_IDLE
, 0, 0, &dummy
))
563 #ifdef ALWAYS_CALL_BUSY
566 clock_slowed
= (apm_info
.bios
.flags
& APM_IDLE_SLOWS_CLOCK
) != 0;
571 static void apm_do_busy(void)
576 (void) apm_bios_call_simple(APM_FUNC_BUSY
, 0, 0, &dummy
);
582 extern int hlt_counter
;
585 * If no process has been interested in this
586 * CPU for some time, we want to wake up the
587 * power management thread - we probably want
590 #define HARD_IDLE_TIMEOUT (HZ/3)
592 /* This should wake up kapmd and ask it to slow the CPU */
593 #define powermanagement_idle() do { } while (0)
596 * This is the idle thing.
598 static void apm_cpu_idle(void)
600 unsigned int start_idle
;
602 start_idle
= jiffies
;
604 if (!current
->need_resched
) {
605 if (jiffies
- start_idle
< HARD_IDLE_TIMEOUT
) {
606 if (!current_cpu_data
.hlt_works_ok
)
611 if (!current
->need_resched
)
619 * Ok, do some power management - we've been idle for too long
621 powermanagement_idle();
626 start_idle
= jiffies
;
633 static int apm_magic(void * unused
)
640 static void apm_power_off(void)
642 #ifdef CONFIG_APM_REAL_MODE_POWER_OFF
643 unsigned char po_bios_call
[] = {
644 0xb8, 0x00, 0x10, /* movw $0x1000,ax */
645 0x8e, 0xd0, /* movw ax,ss */
646 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
647 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
648 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
649 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
650 0xcd, 0x15 /* int $0x15 */
655 * This may be called on an SMP machine.
658 /* Some bioses don't like being called from CPU != 0 */
659 while (cpu_number_map(smp_processor_id()) != 0) {
660 kernel_thread(apm_magic
, NULL
,
661 CLONE_FS
| CLONE_FILES
| CLONE_SIGHAND
| SIGCHLD
);
665 #ifdef CONFIG_APM_REAL_MODE_POWER_OFF
666 machine_real_restart(po_bios_call
, sizeof(po_bios_call
));
668 (void) apm_set_power_state(APM_STATE_OFF
);
672 #ifdef CONFIG_APM_DO_ENABLE
673 static int apm_enable_power_management(int enable
)
677 if ((enable
== 0) && (apm_info
.bios
.flags
& APM_BIOS_DISENGAGED
))
678 return APM_NOT_ENGAGED
;
679 if (apm_bios_call_simple(APM_FUNC_ENABLE_PM
, APM_DEVICE_BALL
,
681 return (eax
>> 8) & 0xff;
683 apm_info
.bios
.flags
&= ~APM_BIOS_DISABLED
;
685 apm_info
.bios
.flags
|= APM_BIOS_DISABLED
;
690 static int apm_get_power_status(u_short
*status
, u_short
*bat
, u_short
*life
)
698 if (apm_info
.get_power_status_broken
)
699 return APM_32_UNSUPPORTED
;
700 if (apm_bios_call(APM_FUNC_GET_STATUS
, APM_DEVICE_ALL
, 0,
701 &eax
, &ebx
, &ecx
, &edx
, &dummy
))
702 return (eax
>> 8) & 0xff;
710 static int apm_get_battery_status(u_short which
, u_short
*status
,
711 u_short
*bat
, u_short
*life
, u_short
*nbat
)
719 if (apm_info
.connection_version
< 0x0102) {
720 /* pretend we only have one battery. */
722 return APM_BAD_DEVICE
;
724 return apm_get_power_status(status
, bat
, life
);
727 if (apm_bios_call(APM_FUNC_GET_STATUS
, (0x8000 | (which
)), 0, &eax
,
728 &ebx
, &ecx
, &edx
, &esi
))
729 return (eax
>> 8) & 0xff;
738 static int apm_engage_power_management(u_short device
, int enable
)
742 if ((enable
== 0) && (device
== APM_DEVICE_ALL
)
743 && (apm_info
.bios
.flags
& APM_BIOS_DISABLED
))
745 if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM
, device
, enable
, &eax
))
746 return (eax
>> 8) & 0xff;
747 if (device
== APM_DEVICE_ALL
) {
749 apm_info
.bios
.flags
&= ~APM_BIOS_DISENGAGED
;
751 apm_info
.bios
.flags
|= APM_BIOS_DISENGAGED
;
756 static void apm_error(char *str
, int err
)
760 for (i
= 0; i
< ERROR_COUNT
; i
++)
761 if (error_table
[i
].key
== err
) break;
763 printk(KERN_NOTICE
"apm: %s: %s\n", str
, error_table
[i
].msg
);
765 printk(KERN_NOTICE
"apm: %s: unknown error code %#2.2x\n",
769 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
770 static int apm_console_blank(int blank
)
775 state
= blank
? APM_STATE_STANDBY
: APM_STATE_READY
;
776 /* Blank the first display device */
777 error
= set_power_state(0x100, state
);
778 if ((error
!= APM_SUCCESS
) && (error
!= APM_NO_ERROR
)) {
779 /* try to blank them all instead */
780 error
= set_power_state(0x1ff, state
);
781 if ((error
!= APM_SUCCESS
) && (error
!= APM_NO_ERROR
))
782 /* try to blank device one instead */
783 error
= set_power_state(0x101, state
);
785 if ((error
== APM_SUCCESS
) || (error
== APM_NO_ERROR
))
787 apm_error("set display", error
);
792 static int queue_empty(struct apm_user
*as
)
794 return as
->event_head
== as
->event_tail
;
797 static apm_event_t
get_queued_event(struct apm_user
*as
)
799 as
->event_tail
= (as
->event_tail
+ 1) % APM_MAX_EVENTS
;
800 return as
->events
[as
->event_tail
];
803 static void queue_event(apm_event_t event
, struct apm_user
*sender
)
805 struct apm_user
* as
;
807 if (user_list
== NULL
)
809 for (as
= user_list
; as
!= NULL
; as
= as
->next
) {
812 as
->event_head
= (as
->event_head
+ 1) % APM_MAX_EVENTS
;
813 if (as
->event_head
== as
->event_tail
) {
817 printk(KERN_ERR
"apm: an event queue overflowed\n");
818 as
->event_tail
= (as
->event_tail
+ 1) % APM_MAX_EVENTS
;
820 as
->events
[as
->event_head
] = event
;
824 case APM_SYS_SUSPEND
:
825 case APM_USER_SUSPEND
:
826 as
->suspends_pending
++;
830 case APM_SYS_STANDBY
:
831 case APM_USER_STANDBY
:
832 as
->standbys_pending
++;
837 wake_up_interruptible(&apm_waitqueue
);
840 static void set_time(void)
844 if (got_clock_diff
) { /* Must know time zone in order to set clock */
847 CURRENT_TIME
= get_cmos_time() + clock_cmos_diff
;
848 restore_flags(flags
);
852 static void get_time_diff(void)
854 #ifndef CONFIG_APM_RTC_IS_GMT
858 * Estimate time zone so that set_time can update the clock
861 clock_cmos_diff
= -get_cmos_time();
863 clock_cmos_diff
+= CURRENT_TIME
;
865 restore_flags(flags
);
869 static void reinit_timer(void)
871 #ifdef INIT_TIMER_AFTER_SUSPEND
876 /* set the clock to 100 Hz */
877 outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
879 outb_p(LATCH
& 0xff , 0x40); /* LSB */
881 outb(LATCH
>> 8 , 0x40); /* MSB */
883 restore_flags(flags
);
887 static int send_event(apm_event_t event
)
890 case APM_SYS_SUSPEND
:
891 case APM_CRITICAL_SUSPEND
:
892 case APM_USER_SUSPEND
:
893 /* map all suspends to ACPI D3 */
894 if (pm_send_all(PM_SUSPEND
, (void *)3)) {
895 if (event
== APM_CRITICAL_SUSPEND
) {
896 printk(KERN_CRIT
"apm: Critical suspend was vetoed, expect armageddon\n" );
899 if (apm_info
.connection_version
> 0x100)
900 apm_set_power_state(APM_STATE_REJECT
);
904 case APM_NORMAL_RESUME
:
905 case APM_CRITICAL_RESUME
:
906 /* map all resumes to ACPI D0 */
907 (void) pm_send_all(PM_RESUME
, (void *)0);
914 static int suspend(void)
921 err
= apm_set_power_state(APM_STATE_SUSPEND
);
924 if (err
== APM_NO_ERROR
)
926 if (err
!= APM_SUCCESS
)
927 apm_error("suspend", err
);
928 send_event(APM_NORMAL_RESUME
);
930 queue_event(APM_NORMAL_RESUME
, NULL
);
931 for (as
= user_list
; as
!= NULL
; as
= as
->next
) {
932 as
->suspend_wait
= 0;
933 as
->suspend_result
= ((err
== APM_SUCCESS
) ? 0 : -EIO
);
935 ignore_normal_resume
= 1;
936 wake_up_interruptible(&apm_suspend_waitqueue
);
940 static void standby(void)
945 err
= apm_set_power_state(APM_STATE_STANDBY
);
946 if ((err
!= APM_SUCCESS
) && (err
!= APM_NO_ERROR
))
947 apm_error("standby", err
);
950 static apm_event_t
get_event(void)
954 apm_eventinfo_t info
;
958 /* we don't use the eventinfo */
959 error
= apm_get_event(&event
, &info
);
960 if (error
== APM_SUCCESS
)
963 if ((error
!= APM_NO_EVENTS
) && (notified
++ == 0))
964 apm_error("get_event", error
);
969 static void check_events(void)
972 static unsigned long last_resume
;
973 static int ignore_bounce
;
975 while ((event
= get_event()) != 0) {
977 if (event
<= NR_APM_EVENT_NAME
)
978 printk(KERN_DEBUG
"apm: received %s notify\n",
979 apm_event_name
[event
- 1]);
981 printk(KERN_DEBUG
"apm: received unknown "
982 "event 0x%02x\n", event
);
985 && ((jiffies
- last_resume
) > bounce_interval
))
987 if (ignore_normal_resume
&& (event
!= APM_NORMAL_RESUME
))
988 ignore_normal_resume
= 0;
991 case APM_SYS_STANDBY
:
992 case APM_USER_STANDBY
:
993 if (send_event(event
)) {
994 queue_event(event
, NULL
);
995 if (standbys_pending
<= 0)
1000 case APM_USER_SUSPEND
:
1001 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
1002 if (apm_info
.connection_version
> 0x100)
1003 apm_set_power_state(APM_STATE_REJECT
);
1006 case APM_SYS_SUSPEND
:
1007 if (ignore_bounce
) {
1008 if (apm_info
.connection_version
> 0x100)
1009 apm_set_power_state(APM_STATE_REJECT
);
1013 * If we are already processing a SUSPEND,
1014 * then further SUSPEND events from the BIOS
1015 * will be ignored. We also return here to
1016 * cope with the fact that the Thinkpads keep
1017 * sending a SUSPEND event until something else
1020 if (waiting_for_resume
)
1022 if (send_event(event
)) {
1023 queue_event(event
, NULL
);
1024 waiting_for_resume
= 1;
1025 if (suspends_pending
<= 0)
1030 case APM_NORMAL_RESUME
:
1031 case APM_CRITICAL_RESUME
:
1032 case APM_STANDBY_RESUME
:
1033 waiting_for_resume
= 0;
1034 last_resume
= jiffies
;
1036 if ((event
!= APM_NORMAL_RESUME
)
1037 || (ignore_normal_resume
== 0)) {
1040 queue_event(event
, NULL
);
1044 case APM_CAPABILITY_CHANGE
:
1045 case APM_LOW_BATTERY
:
1046 case APM_POWER_STATUS_CHANGE
:
1048 queue_event(event
, NULL
);
1051 case APM_UPDATE_TIME
:
1055 case APM_CRITICAL_SUSPEND
:
1058 * We can only hope it worked - we are not allowed
1059 * to reject a critical suspend.
1067 static void apm_event_handler(void)
1069 static int pending_count
= 4;
1072 if ((standbys_pending
> 0) || (suspends_pending
> 0)) {
1073 if ((apm_info
.connection_version
> 0x100) && (pending_count
-- <= 0)) {
1076 printk(KERN_DEBUG
"apm: setting state busy\n");
1077 err
= apm_set_power_state(APM_STATE_BUSY
);
1079 apm_error("busy", err
);
1087 * This is the APM thread main loop.
1089 * Check whether we're the only running process to
1090 * decide if we should just power down.
1093 #define system_idle() (nr_running == 1)
1095 static void apm_mainloop(void)
1098 DECLARE_WAITQUEUE(wait
, current
);
1100 add_wait_queue(&apm_waitqueue
, &wait
);
1101 set_current_state(TASK_INTERRUPTIBLE
);
1103 /* Nothing to do, just sleep for the timeout */
1104 timeout
= 2*timeout
;
1105 if (timeout
> APM_CHECK_TIMEOUT
)
1106 timeout
= APM_CHECK_TIMEOUT
;
1107 schedule_timeout(timeout
);
1112 * Ok, check all events, check for idle (and mark us sleeping
1113 * so as not to count towards the load average)..
1115 set_current_state(TASK_INTERRUPTIBLE
);
1116 apm_event_handler();
1117 #ifdef CONFIG_APM_CPU_IDLE
1120 if (apm_do_idle()) {
1121 unsigned long start
= jiffies
;
1122 while ((!exit_kapmd
) && system_idle()) {
1124 if ((jiffies
- start
) > APM_CHECK_TIMEOUT
) {
1125 apm_event_handler();
1130 apm_event_handler();
1135 remove_wait_queue(&apm_waitqueue
, &wait
);
1138 static int check_apm_user(struct apm_user
*as
, const char *func
)
1140 if ((as
== NULL
) || (as
->magic
!= APM_BIOS_MAGIC
)) {
1141 printk(KERN_ERR
"apm: %s passed bad filp\n", func
);
1147 static ssize_t
do_read(struct file
*fp
, char *buf
, size_t count
, loff_t
*ppos
)
1149 struct apm_user
* as
;
1152 DECLARE_WAITQUEUE(wait
, current
);
1154 as
= fp
->private_data
;
1155 if (check_apm_user(as
, "read"))
1157 if (count
< sizeof(apm_event_t
))
1159 if (queue_empty(as
)) {
1160 if (fp
->f_flags
& O_NONBLOCK
)
1162 add_wait_queue(&apm_waitqueue
, &wait
);
1164 set_current_state(TASK_INTERRUPTIBLE
);
1165 if (queue_empty(as
) && !signal_pending(current
)) {
1169 set_current_state(TASK_RUNNING
);
1170 remove_wait_queue(&apm_waitqueue
, &wait
);
1173 while ((i
>= sizeof(event
)) && !queue_empty(as
)) {
1174 event
= get_queued_event(as
);
1175 if (copy_to_user(buf
, &event
, sizeof(event
))) {
1181 case APM_SYS_SUSPEND
:
1182 case APM_USER_SUSPEND
:
1183 as
->suspends_read
++;
1186 case APM_SYS_STANDBY
:
1187 case APM_USER_STANDBY
:
1188 as
->standbys_read
++;
1191 buf
+= sizeof(event
);
1196 if (signal_pending(current
))
1197 return -ERESTARTSYS
;
1201 static unsigned int do_poll(struct file
*fp
, poll_table
* wait
)
1203 struct apm_user
* as
;
1205 as
= fp
->private_data
;
1206 if (check_apm_user(as
, "poll"))
1208 poll_wait(fp
, &apm_waitqueue
, wait
);
1209 if (!queue_empty(as
))
1210 return POLLIN
| POLLRDNORM
;
1214 static int do_ioctl(struct inode
* inode
, struct file
*filp
,
1215 u_int cmd
, u_long arg
)
1217 struct apm_user
* as
;
1218 DECLARE_WAITQUEUE(wait
, current
);
1220 as
= filp
->private_data
;
1221 if (check_apm_user(as
, "ioctl"))
1226 case APM_IOC_STANDBY
:
1227 if (as
->standbys_read
> 0) {
1228 as
->standbys_read
--;
1229 as
->standbys_pending
--;
1231 } else if (!send_event(APM_USER_STANDBY
))
1234 queue_event(APM_USER_STANDBY
, as
);
1235 if (standbys_pending
<= 0)
1238 case APM_IOC_SUSPEND
:
1239 if (as
->suspends_read
> 0) {
1240 as
->suspends_read
--;
1241 as
->suspends_pending
--;
1243 } else if (!send_event(APM_USER_SUSPEND
))
1246 queue_event(APM_USER_SUSPEND
, as
);
1247 if (suspends_pending
<= 0) {
1248 if (suspend() != APM_SUCCESS
)
1251 as
->suspend_wait
= 1;
1252 add_wait_queue(&apm_suspend_waitqueue
, &wait
);
1254 set_current_state(TASK_INTERRUPTIBLE
);
1255 if ((as
->suspend_wait
== 0)
1256 || signal_pending(current
))
1260 set_current_state(TASK_RUNNING
);
1261 remove_wait_queue(&apm_suspend_waitqueue
, &wait
);
1262 return as
->suspend_result
;
1271 static int do_release(struct inode
* inode
, struct file
* filp
)
1273 struct apm_user
* as
;
1275 as
= filp
->private_data
;
1276 if (check_apm_user(as
, "release"))
1278 filp
->private_data
= NULL
;
1280 if (as
->standbys_pending
> 0) {
1281 standbys_pending
-= as
->standbys_pending
;
1282 if (standbys_pending
<= 0)
1285 if (as
->suspends_pending
> 0) {
1286 suspends_pending
-= as
->suspends_pending
;
1287 if (suspends_pending
<= 0)
1290 if (user_list
== as
)
1291 user_list
= as
->next
;
1293 struct apm_user
* as1
;
1295 for (as1
= user_list
;
1296 (as1
!= NULL
) && (as1
->next
!= as
);
1300 printk(KERN_ERR
"apm: filp not in user list\n");
1302 as1
->next
= as
->next
;
1309 static int do_open(struct inode
* inode
, struct file
* filp
)
1311 struct apm_user
* as
;
1313 as
= (struct apm_user
*)kmalloc(sizeof(*as
), GFP_KERNEL
);
1315 printk(KERN_ERR
"apm: cannot allocate struct of size %d bytes\n",
1319 as
->magic
= APM_BIOS_MAGIC
;
1320 as
->event_tail
= as
->event_head
= 0;
1321 as
->suspends_pending
= as
->standbys_pending
= 0;
1322 as
->suspends_read
= as
->standbys_read
= 0;
1324 * XXX - this is a tiny bit broken, when we consider BSD
1325 * process accounting. If the device is opened by root, we
1326 * instantly flag that we used superuser privs. Who knows,
1327 * we might close the device immediately without doing a
1328 * privileged operation -- cevans
1330 as
->suser
= capable(CAP_SYS_ADMIN
);
1331 as
->next
= user_list
;
1333 filp
->private_data
= as
;
1337 static int apm_get_info(char *buf
, char **start
, off_t fpos
, int length
)
1344 unsigned short ac_line_status
= 0xff;
1345 unsigned short battery_status
= 0xff;
1346 unsigned short battery_flag
= 0xff;
1347 int percentage
= -1;
1348 int time_units
= -1;
1353 if ((smp_num_cpus
== 1) &&
1354 !(error
= apm_get_power_status(&bx
, &cx
, &dx
))) {
1355 ac_line_status
= (bx
>> 8) & 0xff;
1356 battery_status
= bx
& 0xff;
1357 if ((cx
& 0xff) != 0xff)
1358 percentage
= cx
& 0xff;
1360 if (apm_info
.connection_version
> 0x100) {
1361 battery_flag
= (cx
>> 8) & 0xff;
1363 units
= (dx
& 0x8000) ? "min" : "sec";
1364 time_units
= dx
& 0x7fff;
1368 /* Arguments, with symbols from linux/apm_bios.h. Information is
1369 from the Get Power Status (0x0a) call unless otherwise noted.
1371 0) Linux driver version (this will change if format changes)
1372 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
1373 2) APM flags from APM Installation Check (0x00):
1374 bit 0: APM_16_BIT_SUPPORT
1375 bit 1: APM_32_BIT_SUPPORT
1376 bit 2: APM_IDLE_SLOWS_CLOCK
1377 bit 3: APM_BIOS_DISABLED
1378 bit 4: APM_BIOS_DISENGAGED
1382 0x02: On backup power (BIOS >= 1.1 only)
1389 0x04: Selected battery not present (BIOS >= 1.2 only)
1396 bit 7: No system battery
1398 6) Remaining battery life (percentage of charge):
1401 7) Remaining battery life (time units):
1402 Number of remaining minutes or seconds
1404 8) min = minutes; sec = seconds */
1406 p
+= sprintf(p
, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1408 (apm_info
.bios
.version
>> 8) & 0xff,
1409 apm_info
.bios
.version
& 0xff,
1410 apm_info
.bios
.flags
,
1421 static int apm(void *unused
)
1434 strcpy(current
->comm
, "kapm-idled");
1435 sigfillset(¤t
->blocked
);
1436 current
->tty
= NULL
; /* get rid of controlling tty */
1438 if (apm_info
.connection_version
== 0) {
1439 apm_info
.connection_version
= apm_info
.bios
.version
;
1440 if (apm_info
.connection_version
> 0x100) {
1442 * We only support BIOSs up to version 1.2
1444 if (apm_info
.connection_version
> 0x0102)
1445 apm_info
.connection_version
= 0x0102;
1446 error
= apm_driver_version(&apm_info
.connection_version
);
1447 if (error
!= APM_SUCCESS
) {
1448 apm_error("driver version", error
);
1449 /* Fall back to an APM 1.0 connection. */
1450 apm_info
.connection_version
= 0x100;
1456 printk(KERN_INFO
"apm: Connection version %d.%d\n",
1457 (apm_info
.connection_version
>> 8) & 0xff,
1458 apm_info
.connection_version
& 0xff);
1460 #ifdef CONFIG_APM_DO_ENABLE
1461 if (apm_info
.bios
.flags
& APM_BIOS_DISABLED
) {
1463 * This call causes my NEC UltraLite Versa 33/C to hang if it
1464 * is booted with PM disabled but not in the docking station.
1467 error
= apm_enable_power_management(1);
1469 apm_error("enable power management", error
);
1475 if ((apm_info
.bios
.flags
& APM_BIOS_DISENGAGED
)
1476 && (apm_info
.connection_version
> 0x0100)) {
1477 error
= apm_engage_power_management(APM_DEVICE_ALL
, 1);
1479 apm_error("engage power management", error
);
1484 if (debug
&& (smp_num_cpus
== 1)) {
1485 error
= apm_get_power_status(&bx
, &cx
, &dx
);
1487 printk(KERN_INFO
"apm: power status not available\n");
1489 switch ((bx
>> 8) & 0xff) {
1490 case 0: power_stat
= "off line"; break;
1491 case 1: power_stat
= "on line"; break;
1492 case 2: power_stat
= "on backup power"; break;
1493 default: power_stat
= "unknown"; break;
1495 switch (bx
& 0xff) {
1496 case 0: bat_stat
= "high"; break;
1497 case 1: bat_stat
= "low"; break;
1498 case 2: bat_stat
= "critical"; break;
1499 case 3: bat_stat
= "charging"; break;
1500 default: bat_stat
= "unknown"; break;
1503 "apm: AC %s, battery status %s, battery life ",
1504 power_stat
, bat_stat
);
1505 if ((cx
& 0xff) == 0xff)
1506 printk("unknown\n");
1508 printk("%d%%\n", cx
& 0xff);
1509 if (apm_info
.connection_version
> 0x100) {
1511 "apm: battery flag 0x%02x, battery life ",
1514 printk("unknown\n");
1516 printk("%d %s\n", dx
& 0x7fff,
1518 "minutes" : "seconds");
1523 /* Install our power off handler.. */
1525 pm_power_off
= apm_power_off
;
1526 #ifdef CONFIG_MAGIC_SYSRQ
1527 sysrq_power_off
= apm_power_off
;
1529 if (smp_num_cpus
== 1) {
1530 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1531 console_blank_hook
= apm_console_blank
;
1534 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1535 console_blank_hook
= NULL
;
1544 static int __init
apm_setup(char *str
)
1548 while ((str
!= NULL
) && (*str
!= '\0')) {
1549 if (strncmp(str
, "off", 3) == 0)
1551 if (strncmp(str
, "on", 2) == 0)
1553 invert
= (strncmp(str
, "no-", 3) == 0);
1556 if (strncmp(str
, "debug", 5) == 0)
1558 if ((strncmp(str
, "power-off", 9) == 0) ||
1559 (strncmp(str
, "power_off", 9) == 0))
1560 power_off
= !invert
;
1561 if ((strncmp(str
, "bounce-interval=", 16) == 0) ||
1562 (strncmp(str
, "bounce_interval=", 16) == 0))
1563 bounce_interval
= simple_strtol(str
+ 16, NULL
, 0);
1564 str
= strchr(str
, ',');
1566 str
+= strspn(str
, ", \t");
1571 __setup("apm=", apm_setup
);
1574 static struct file_operations apm_bios_fops
= {
1580 release
: do_release
,
1583 static struct miscdevice apm_device
= {
1590 * Just start the APM thread. We do NOT want to do APM BIOS
1591 * calls from anything but the APM thread, if for no other reason
1592 * than the fact that we don't trust the APM BIOS. This way,
1593 * most common APM BIOS problems that lead to protection errors
1594 * etc will have at least some level of being contained...
1596 * In short, if something bad happens, at least we have a choice
1597 * of just killing the apm thread..
1599 static int __init
apm_init(void)
1601 struct proc_dir_entry
*apm_proc
;
1603 if (apm_info
.bios
.version
== 0) {
1604 printk(KERN_INFO
"apm: BIOS not found.\n");
1608 "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
1609 ((apm_info
.bios
.version
>> 8) & 0xff),
1610 (apm_info
.bios
.version
& 0xff),
1611 apm_info
.bios
.flags
,
1613 if ((apm_info
.bios
.flags
& APM_32_BIT_SUPPORT
) == 0) {
1614 printk(KERN_INFO
"apm: no 32 bit BIOS support\n");
1619 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1620 * but is reportedly a 1.0 BIOS.
1622 if (apm_info
.bios
.version
== 0x001)
1623 apm_info
.bios
.version
= 0x100;
1625 /* BIOS < 1.2 doesn't set cseg_16_len */
1626 if (apm_info
.bios
.version
< 0x102)
1627 apm_info
.bios
.cseg_16_len
= 0; /* 64k */
1630 printk(KERN_INFO
"apm: entry %x:%lx cseg16 %x dseg %x",
1631 apm_info
.bios
.cseg
, apm_info
.bios
.offset
,
1632 apm_info
.bios
.cseg_16
, apm_info
.bios
.dseg
);
1633 if (apm_info
.bios
.version
> 0x100)
1634 printk(" cseg len %x, dseg len %x",
1635 apm_info
.bios
.cseg_len
,
1636 apm_info
.bios
.dseg_len
);
1637 if (apm_info
.bios
.version
> 0x101)
1638 printk(" cseg16 len %x", apm_info
.bios
.cseg_16_len
);
1643 printk(KERN_NOTICE
"apm: disabled on user request.\n");
1646 if ((smp_num_cpus
> 1) && !power_off
) {
1647 printk(KERN_NOTICE
"apm: disabled - APM is not SMP safe.\n");
1650 if (PM_IS_ACTIVE()) {
1651 printk(KERN_NOTICE
"apm: overridden by ACPI.\n");
1657 * Set up a segment that references the real mode segment 0x40
1658 * that extends up to the end of page zero (that we have reserved).
1659 * This is for buggy BIOS's that refer to (real mode) segment 0x40
1660 * even though they are called in protected mode.
1662 set_base(gdt
[APM_40
>> 3],
1663 __va((unsigned long)0x40 << 4));
1664 _set_limit((char *)&gdt
[APM_40
>> 3], 4095 - (0x40 << 4));
1666 apm_bios_entry
.offset
= apm_info
.bios
.offset
;
1667 apm_bios_entry
.segment
= APM_CS
;
1668 set_base(gdt
[APM_CS
>> 3],
1669 __va((unsigned long)apm_info
.bios
.cseg
<< 4));
1670 set_base(gdt
[APM_CS_16
>> 3],
1671 __va((unsigned long)apm_info
.bios
.cseg_16
<< 4));
1672 set_base(gdt
[APM_DS
>> 3],
1673 __va((unsigned long)apm_info
.bios
.dseg
<< 4));
1674 #ifndef APM_RELAX_SEGMENTS
1675 if (apm_info
.bios
.version
== 0x100) {
1677 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1678 _set_limit((char *)&gdt
[APM_CS
>> 3], 64 * 1024 - 1);
1679 /* For some unknown machine. */
1680 _set_limit((char *)&gdt
[APM_CS_16
>> 3], 64 * 1024 - 1);
1681 /* For the DEC Hinote Ultra CT475 (and others?) */
1682 _set_limit((char *)&gdt
[APM_DS
>> 3], 64 * 1024 - 1);
1683 #ifndef APM_RELAX_SEGMENTS
1685 _set_limit((char *)&gdt
[APM_CS
>> 3],
1686 (apm_info
.bios
.cseg_len
- 1) & 0xffff);
1687 _set_limit((char *)&gdt
[APM_CS_16
>> 3],
1688 (apm_info
.bios
.cseg_16_len
- 1) & 0xffff);
1689 _set_limit((char *)&gdt
[APM_DS
>> 3],
1690 (apm_info
.bios
.dseg_len
- 1) & 0xffff);
1694 apm_proc
= create_proc_info_entry("apm", 0, NULL
, apm_get_info
);
1696 SET_MODULE_OWNER(apm_proc
);
1698 kernel_thread(apm
, NULL
, CLONE_FS
| CLONE_FILES
| CLONE_SIGHAND
| SIGCHLD
);
1700 if (smp_num_cpus
> 1) {
1702 "apm: disabled - APM is not SMP safe (power off active).\n");
1706 misc_register(&apm_device
);
1711 static void __exit
apm_exit(void)
1715 if (((apm_info
.bios
.flags
& APM_BIOS_DISENGAGED
) == 0)
1716 && (apm_info
.connection_version
> 0x0100)) {
1717 error
= apm_engage_power_management(APM_DEVICE_ALL
, 0);
1719 apm_error("disengage power management", error
);
1721 misc_deregister(&apm_device
);
1722 remove_proc_entry("apm", NULL
);
1723 #ifdef CONFIG_MAGIC_SYSRQ
1724 sysrq_power_off
= NULL
;
1727 pm_power_off
= NULL
;
1729 while (kapmd_running
)
1734 module_init(apm_init
);
1735 module_exit(apm_exit
);
1737 MODULE_AUTHOR("Stephen Rothwell");
1738 MODULE_DESCRIPTION("Advanced Power Management");
1739 MODULE_PARM(debug
, "i");
1740 MODULE_PARM_DESC(debug
, "Enable debug mode");
1741 MODULE_PARM(power_off
, "i");
1742 MODULE_PARM_DESC(power_off
, "Enable power off");
1743 MODULE_PARM(bounce_interval
, "i");
1744 MODULE_PARM_DESC(bounce_interval
, "Set the number of ticks to ignore suspend bounces");