Import 2.3.18pre1
[davej-history.git] / arch / i386 / kernel / apm.c
blob3bafdfcfc50bba57a252f0e9387a1f7be230d8d7
1 /* -*- linux-c -*-
2 * APM BIOS driver for Linux
3 * Copyright 1994-1998 Stephen Rothwell
4 * (Stephen.Rothwell@canb.auug.org.au)
5 * Development of this driver was funded by NEC Australia P/L
6 * and NEC Corporation
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
11 * later version.
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
20 * Documentation
21 * January 1996, Rik Faith (faith@cs.unc.edu):
22 * Make /proc/apm easy to format (bump driver version)
23 * March 1996, Rik Faith (faith@cs.unc.edu):
24 * Prohibit APM BIOS calls unless apm_enabled.
25 * (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
26 * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
27 * Version 1.0 and 1.1
28 * May 1996, Version 1.2
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
37 * History:
38 * 0.6b: first version in official kernel, Linux 1.3.46
39 * 0.7: changed /proc/apm format, Linux 1.3.58
40 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
41 * 0.9: only call bios if bios is present, Linux 1.3.72
42 * 1.0: use fixed device number, consolidate /proc/apm into this file,
43 * Linux 1.3.85
44 * 1.1: support user-space standby and suspend, power off after system
45 * halted, Linux 1.3.98
46 * 1.2: When resetting RTC after resume, take care so that the time
47 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
48 * <jtoth@princeton.edu>); improve interaction between
49 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
50 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
51 * levels to the printk calls. APM is not defined for SMP machines.
52 * The new replacment for it is, but Linux doesn't yet support this.
53 * Alan Cox Linux 2.1.55
54 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
55 * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
56 * Dean Gaudet <dgaudet@arctic.org>.
57 * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
58 * 1.5: Fix segment register reloading (in case of bad segments saved
59 * across BIOS call).
60 * Stephen Rothwell
61 * 1.6: Cope with complier/assembler differences.
62 * Only try to turn off the first display device.
63 * Fix OOPS at power off with no APM BIOS by Jan Echternach
64 * <echter@informatik.uni-rostock.de>
65 * Stephen Rothwell
66 * 1.7: Modify driver's cached copy of the disabled/disengaged flags
67 * to reflect current state of APM BIOS.
68 * Chris Rankin <rankinc@bellsouth.net>
69 * Reset interrupt 0 timer to 100Hz after suspend
70 * Chad Miller <cmiller@surfsouth.com>
71 * Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
72 * Richard Gooch <rgooch@atnf.csiro.au>
73 * Allow boot time disabling of APM
74 * Make boot messages far less verbose by default
75 * Make asm safer
76 * Stephen Rothwell
77 * 1.8: Add CONFIG_APM_RTC_IS_GMT
78 * Richard Gooch <rgooch@atnf.csiro.au>
79 * change APM_NOINTS to CONFIG_APM_ALLOW_INTS
80 * remove dependency on CONFIG_PROC_FS
81 * Stephen Rothwell
82 * 1.9: Fix small typo. <laslo@ilo.opole.pl>
83 * Try to cope with BIOS's that need to have all display
84 * devices blanked and not just the first one.
85 * Ross Paterson <ross@soi.city.ac.uk>
86 * Fix segment limit setting it has always been wrong as
87 * the segments needed to have byte granularity.
88 * Mark a few things __init.
89 * Add hack to allow power off of SMP systems by popular request.
90 * Use CONFIG_SMP instead of __SMP__
91 * Ignore BOUNCES for three seconds.
92 * Stephen Rothwell
94 * APM 1.1 Reference:
96 * Intel Corporation, Microsoft Corporation. Advanced Power Management
97 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
98 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
100 * [This document is available free from Intel by calling 800.628.8686 (fax
101 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
102 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
103 * available from Microsoft by calling 206.882.8080.]
105 * APM 1.2 Reference:
106 * Intel Corporation, Microsoft Corporation. Advanced Power Management
107 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
109 * [This document is available from Intel at:
110 * http://www.intel.com/IAL/powermgm
111 * or Microsoft at
112 * http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm
116 #include <linux/config.h>
117 #include <linux/module.h>
119 #include <linux/poll.h>
120 #include <linux/types.h>
121 #include <linux/stddef.h>
122 #include <linux/timer.h>
123 #include <linux/fcntl.h>
124 #include <linux/malloc.h>
125 #include <linux/linkage.h>
126 #include <linux/stat.h>
127 #include <linux/proc_fs.h>
128 #include <linux/miscdevice.h>
129 #include <linux/apm_bios.h>
130 #include <linux/init.h>
132 #include <asm/system.h>
133 #include <asm/uaccess.h>
134 #include <asm/desc.h>
136 EXPORT_SYMBOL(apm_register_callback);
137 EXPORT_SYMBOL(apm_unregister_callback);
139 extern unsigned long get_cmos_time(void);
142 * The apm_bios device is one of the misc char devices.
143 * This is its minor number.
145 #define APM_MINOR_DEV 134
147 /* Configurable options:
149 * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
150 * This is necessary on the NEC Versa M series, which generates these when
151 * resuming from SYSTEM SUSPEND. However, enabling this on other laptops
152 * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
153 * USER SUSPEND is ignored -- this may prevent the APM driver from updating
154 * the system time on a RESUME.
156 * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of
157 * the specification: "When disabled, the APM BIOS does not automatically
158 * power manage devices, enter the Standby State, enter the Suspend State,
159 * or take power saving steps in response to CPU Idle calls." This driver
160 * will make CPU Idle calls when Linux is idle (unless this feature is
161 * turned off -- see below). This should always save battery power, but
162 * more complicated APM features will be dependent on your BIOS
163 * implementation. You may need to turn this option off if your computer
164 * hangs at boot time when using APM support, or if it beeps continuously
165 * instead of suspending. Turn this off if you have a NEC UltraLite Versa
166 * 33/C or a Toshiba T400CDT. This is off by default since most machines
167 * do fine without this feature.
169 * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
170 * idle loop. On some machines, this can activate improved power savings,
171 * such as a slowed CPU clock rate, when the machine is idle. These idle
172 * call is made after the idle loop has run for some length of time (e.g.,
173 * 333 mS). On some machines, this will cause a hang at boot time or
174 * whenever the CPU becomes idle.
176 * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some
177 * laptops can use this to turn of the LCD backlight when the VC screen
178 * blanker blanks the screen. Note that this is only used by the VC screen
179 * blanker, and probably won't turn off the backlight when using X11. Some
180 * problems have been reported when using this option with gpm (if you'd
181 * like to debug this, please do so).
183 * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
184 * on returning multiple suspend/standby events whenever one occurs. We
185 * really only need one at a time, so just ignore any beyond the first.
186 * This is probably safe on most laptops.
188 * If you are debugging the APM support for your laptop, note that code for
189 * all of these options is contained in this file, so you can #define or
190 * #undef these on the next line to avoid recompiling the whole kernel.
194 /* KNOWN PROBLEM MACHINES:
196 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
197 * [Confirmed by TI representative]
198 * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
199 * [Confirmed by BIOS disassembly]
200 * [This may work now ...]
201 * P: Toshiba 1950S: battery life information only gets updated after resume
202 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
203 * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
205 * Legend: U = unusable with APM patches
206 * P = partially usable with APM patches
210 * Define to have debug messages.
212 #undef APM_DEBUG
215 * Define to always call the APM BIOS busy routine even if the clock was
216 * not slowed by the idle routine.
218 #define ALWAYS_CALL_BUSY
221 * Define to make the APM BIOS calls zero all data segment registers (so
222 * that an incorrect BIOS implementation will cause a kernel panic if it
223 * tries to write to arbitrary memory).
225 #define APM_ZERO_SEGS
228 * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is
229 * supposed to provide limit information that it recognizes. Many machines
230 * do this correctly, but many others do not restrict themselves to their
231 * claimed limit. When this happens, they will cause a segmentation
232 * violation in the kernel at boot time. Most BIOS's, however, will
233 * respect a 64k limit, so we use that. If you want to be pedantic and
234 * hold your BIOS to its claims, then undefine this.
236 #define APM_RELAX_SEGMENTS
239 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
240 * This patched by Chad Miller <cmiller@surfsouth.com>, orig code by David
241 * Chen <chen@ctpa04.mit.edu>
243 #undef INIT_TIMER_AFTER_SUSPEND
245 #ifdef INIT_TIMER_AFTER_SUSPEND
246 #include <linux/timex.h>
247 #include <asm/io.h>
248 #include <linux/delay.h>
249 #endif
252 * Need to poll the APM BIOS every second
254 #define APM_CHECK_TIMEOUT (HZ)
257 * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then
258 * ignore suspend events for this amount of time
260 #define BOUNCE_INTERVAL (3 * HZ)
263 * Save a segment register away
265 #define savesegment(seg, where) \
266 __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
269 * Forward declarations
271 static void suspend(void);
272 static void standby(void);
273 static void set_time(void);
275 static void check_events(void);
277 static int do_open(struct inode *, struct file *);
278 static int do_release(struct inode *, struct file *);
279 static ssize_t do_read(struct file *, char *, size_t , loff_t *);
280 static unsigned int do_poll(struct file *, poll_table *);
281 static int do_ioctl(struct inode *, struct file *, u_int, u_long);
283 static int apm_get_info(char *, char **, off_t, int, int);
285 extern int apm_register_callback(int (*)(apm_event_t));
286 extern void apm_unregister_callback(int (*)(apm_event_t));
289 * Local variables
291 static struct {
292 unsigned long offset;
293 unsigned short segment;
294 } apm_bios_entry;
295 static int apm_enabled = 0;
296 static int smp_hack = 0;
297 #ifdef CONFIG_APM_CPU_IDLE
298 static int clock_slowed = 0;
299 #endif
300 static int suspends_pending = 0;
301 static int standbys_pending = 0;
302 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
303 static int waiting_for_resume = 0;
304 #endif
306 #ifdef CONFIG_APM_RTC_IS_GMT
307 # define clock_cmos_diff 0
308 # define got_clock_diff 1
309 #else
310 static long clock_cmos_diff;
311 static int got_clock_diff = 0;
312 #endif
313 static int debug = 0;
314 static int apm_disabled = 0;
316 static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
317 static struct apm_bios_struct * user_list = NULL;
319 static char driver_version[] = "1.9"; /* no spaces */
321 #ifdef APM_DEBUG
322 static char * apm_event_name[] = {
323 "system standby",
324 "system suspend",
325 "normal resume",
326 "critical resume",
327 "low battery",
328 "power status change",
329 "update time",
330 "critical suspend",
331 "user standby",
332 "user suspend",
333 "system standby resume",
334 "capabilities change"
336 #define NR_APM_EVENT_NAME \
337 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
338 #endif
340 static struct file_operations apm_bios_fops = {
341 NULL, /* lseek */
342 do_read,
343 NULL, /* write */
344 NULL, /* readdir */
345 do_poll,
346 do_ioctl,
347 NULL, /* mmap */
348 do_open,
349 NULL, /* flush */
350 do_release,
351 NULL, /* fsync */
352 NULL /* fasync */
355 static struct miscdevice apm_device = {
356 APM_MINOR_DEV,
357 "apm",
358 &apm_bios_fops
361 typedef struct callback_list_t {
362 int (* callback)(apm_event_t);
363 struct callback_list_t * next;
364 } callback_list_t;
366 static callback_list_t * callback_list = NULL;
368 typedef struct lookup_t {
369 int key;
370 char * msg;
371 } lookup_t;
373 static const lookup_t error_table[] = {
374 /* N/A { APM_SUCCESS, "Operation succeeded" }, */
375 { APM_DISABLED, "Power management disabled" },
376 { APM_CONNECTED, "Real mode interface already connected" },
377 { APM_NOT_CONNECTED, "Interface not connected" },
378 { APM_16_CONNECTED, "16 bit interface already connected" },
379 /* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
380 { APM_32_CONNECTED, "32 bit interface already connected" },
381 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
382 { APM_BAD_DEVICE, "Unrecognized device ID" },
383 { APM_BAD_PARAM, "Parameter out of range" },
384 { APM_NOT_ENGAGED, "Interface not engaged" },
385 { APM_BAD_FUNCTION, "Function not supported" },
386 { APM_RESUME_DISABLED, "Resume timer disabled" },
387 { APM_BAD_STATE, "Unable to enter requested state" },
388 /* N/A { APM_NO_EVENTS, "No events pending" }, */
389 { APM_NOT_PRESENT, "No APM present" }
391 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
394 * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and
395 * CONFIG_APM_ALLOW_INTS, we are being really paranoid here! Not only
396 * are interrupts disabled, but all the segment registers (except SS)
397 * are saved and zeroed this means that if the BIOS tries to reference
398 * any data without explicitly loading the segment registers, the kernel
399 * will fault immediately rather than have some unforeseen circumstances
400 * for the rest of the kernel. And it will be very obvious! :-) Doing
401 * this depends on CS referring to the same physical memory as DS so that
402 * DS can be zeroed before the call. Unfortunately, we can't do anything
403 * about the stack segment/pointer. Also, we tell the compiler that
404 * everything could change.
406 * Also, we KNOW that for the non error case of apm_bios_call, there
407 * is no useful data returned in the low order 8 bits of eax.
409 #ifndef CONFIG_APM_ALLOW_INTS
410 # define APM_DO_CLI __cli()
411 #else
412 # define APM_DO_CLI
413 #endif
414 #ifdef APM_ZERO_SEGS
415 # define APM_DO_SAVE_SEGS \
416 savesegment(fs, saved_fs); \
417 savesegment(gs, saved_gs)
418 # define APM_DO_ZERO_SEGS \
419 "pushl %%ds\n\t" \
420 "pushl %%es\n\t" \
421 "xorl %%edx, %%edx\n\t" \
422 "mov %%dx, %%ds\n\t" \
423 "mov %%dx, %%es\n\t" \
424 "mov %%dx, %%fs\n\t" \
425 "mov %%dx, %%gs\n\t"
426 # define APM_DO_POP_SEGS \
427 "popl %%es\n\t" \
428 "popl %%ds\n\t"
429 # define APM_DO_RESTORE_SEGS \
430 loadsegment(fs, saved_fs); \
431 loadsegment(gs, saved_gs)
432 #else
433 # define APM_DO_SAVE_SEGS
434 # define APM_DO_ZERO_SEGS
435 # define APM_DO_POP_SEGS
436 # define APM_DO_RESTORE_SEGS
437 #endif
439 static u8 apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
440 u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
442 unsigned int saved_fs;
443 unsigned int saved_gs;
444 unsigned long flags;
446 __save_flags(flags);
447 APM_DO_CLI;
448 APM_DO_SAVE_SEGS;
449 __asm__ __volatile__(APM_DO_ZERO_SEGS
450 "pushl %%edi\n\t"
451 "pushl %%ebp\n\t"
452 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
453 "setc %%al\n\t"
454 "popl %%ebp\n\t"
455 "popl %%edi\n\t"
456 APM_DO_POP_SEGS
457 : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
458 "=S" (*esi)
459 : "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
460 : "memory", "cc");
461 APM_DO_RESTORE_SEGS;
462 __restore_flags(flags);
463 return *eax & 0xff;
467 * This version only returns one value (usually an error code)
470 static u8 apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in,
471 u32 *eax)
473 u8 error;
474 unsigned int saved_fs;
475 unsigned int saved_gs;
476 unsigned long flags;
478 __save_flags(flags);
479 APM_DO_CLI;
480 APM_DO_SAVE_SEGS;
482 int cx, dx, si;
484 __asm__ __volatile__(APM_DO_ZERO_SEGS
485 "pushl %%edi\n\t"
486 "pushl %%ebp\n\t"
487 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
488 "setc %%bl\n\t"
489 "popl %%ebp\n\t"
490 "popl %%edi\n\t"
491 APM_DO_POP_SEGS
492 : "=a" (*eax), "=b" (error), "=c" (cx), "=d" (dx),
493 "=S" (si)
494 : "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
495 : "memory", "cc");
497 APM_DO_RESTORE_SEGS;
498 __restore_flags(flags);
499 return error;
502 static int apm_driver_version(u_short *val)
504 u32 eax;
506 if (apm_bios_call_simple(0x530e, 0, *val, &eax))
507 return (eax >> 8) & 0xff;
508 *val = eax;
509 return APM_SUCCESS;
512 static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
514 u32 eax;
515 u32 ebx;
516 u32 ecx;
517 u32 dummy;
519 if (apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy))
520 return (eax >> 8) & 0xff;
521 *event = ebx;
522 if (apm_bios_info.version < 0x0102)
523 *info = ~0; /* indicate info not valid */
524 else
525 *info = ecx;
526 return APM_SUCCESS;
529 static int set_power_state(u_short what, u_short state)
531 u32 eax;
533 if (apm_bios_call_simple(0x5307, what, state, &eax))
534 return (eax >> 8) & 0xff;
535 return APM_SUCCESS;
538 static int apm_set_power_state(u_short state)
540 return set_power_state(0x0001, state);
544 * If no process has been interested in this
545 * CPU for some time, we want to wake up the
546 * power management thread - we probably want
547 * to conserve power.
549 #define HARD_IDLE_TIMEOUT (HZ/3)
551 /* This should wake up kapmd and ask it to slow the CPU */
552 #define powermanagement_idle() do { } while (0)
554 extern int hlt_counter;
557 * This is the idle thing.
559 void apm_cpu_idle(void)
561 unsigned int start_idle;
563 start_idle = jiffies;
564 while (1) {
565 if (!current->need_resched) {
566 if (jiffies - start_idle < HARD_IDLE_TIMEOUT) {
567 if (!current_cpu_data.hlt_works_ok)
568 continue;
569 if (hlt_counter)
570 continue;
571 asm volatile("sti ; hlt" : : : "memory");
572 continue;
576 * Ok, do some power management - we've been idle for too long
578 powermanagement_idle();
581 schedule();
582 check_pgt_cache();
583 start_idle = jiffies;
587 void apm_power_off(void)
590 * smp_hack == 2 means that we would have enabled APM support
591 * except there is more than one processor and so most of
592 * the APM stuff is unsafe. We will still try power down
593 * because is is useful to some people and they know what
594 * they are doing because they booted with the smp-power-off
595 * kernel option.
597 if (apm_enabled || (smp_hack == 2))
598 (void) apm_set_power_state(APM_STATE_OFF);
601 #ifdef CONFIG_APM_DISPLAY_BLANK
602 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
603 static int apm_set_display_power_state(u_short state)
605 int error;
607 /* Blank the first display device */
608 error = set_power_state(0x0100, state);
609 if (error == APM_BAD_DEVICE)
610 /* try to blank them all instead */
611 error = set_power_state(0x01ff, state);
612 return error;
614 #endif
616 #ifdef CONFIG_APM_DO_ENABLE
617 static int __init apm_enable_power_management(void)
619 u32 eax;
621 if (apm_bios_call_simple(0x5308,
622 (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff,
623 1, &eax))
624 return (eax >> 8) & 0xff;
625 apm_bios_info.flags &= ~APM_BIOS_DISABLED;
626 return APM_SUCCESS;
628 #endif
630 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
632 u32 eax;
633 u32 ebx;
634 u32 ecx;
635 u32 edx;
636 u32 dummy;
638 if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy))
639 return (eax >> 8) & 0xff;
640 *status = ebx;
641 *bat = ecx;
642 *life = edx;
643 return APM_SUCCESS;
646 static int apm_get_battery_status(u_short which, u_short *status,
647 u_short *bat, u_short *life, u_short *nbat)
649 u32 eax;
650 u32 ebx;
651 u32 ecx;
652 u32 edx;
653 u32 esi;
655 if (apm_bios_info.version < 0x0102) {
656 /* pretend we only have one battery. */
657 if (which != 1)
658 return APM_BAD_DEVICE;
659 *nbat = 1;
660 return apm_get_power_status(status, bat, life);
663 if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax,
664 &ebx, &ecx, &edx, &esi))
665 return (eax >> 8) & 0xff;
666 *status = ebx;
667 *bat = ecx;
668 *life = edx;
669 *nbat = esi;
670 return APM_SUCCESS;
673 static int __init apm_engage_power_management(u_short device)
675 u32 eax;
677 if (apm_bios_call_simple(0x530f, device, 1, &eax))
678 return (eax >> 8) & 0xff;
679 return APM_SUCCESS;
682 static void apm_error(char *str, int err)
684 int i;
686 for (i = 0; i < ERROR_COUNT; i++)
687 if (error_table[i].key == err) break;
688 if (i < ERROR_COUNT)
689 printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
690 else
691 printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
692 str, err);
695 /* Called from console driver -- must make sure apm_enabled. */
696 int apm_display_blank(void)
698 #ifdef CONFIG_APM_DISPLAY_BLANK
699 int error;
701 if (!apm_enabled)
702 return 0;
703 error = apm_set_display_power_state(APM_STATE_STANDBY);
704 if (error == APM_SUCCESS)
705 return 1;
706 apm_error("set display standby", error);
707 #endif
708 return 0;
711 /* Called from console driver -- must make sure apm_enabled. */
712 int apm_display_unblank(void)
714 #ifdef CONFIG_APM_DISPLAY_BLANK
715 int error;
717 if (!apm_enabled)
718 return 0;
719 error = apm_set_display_power_state(APM_STATE_READY);
720 if (error == APM_SUCCESS)
721 return 1;
722 apm_error("set display ready", error);
723 #endif
724 return 0;
727 int apm_register_callback(int (*callback)(apm_event_t))
729 callback_list_t * new;
731 new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
732 if (new == NULL)
733 return -ENOMEM;
734 new->callback = callback;
735 new->next = callback_list;
736 callback_list = new;
737 return 0;
740 void apm_unregister_callback(int (*callback)(apm_event_t))
742 callback_list_t ** ptr;
743 callback_list_t * old;
745 for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
746 if ((*ptr)->callback == callback)
747 break;
748 old = *ptr;
749 *ptr = old->next;
750 kfree_s(old, sizeof(callback_list_t));
753 static int queue_empty(struct apm_bios_struct * as)
755 return as->event_head == as->event_tail;
758 static apm_event_t get_queued_event(struct apm_bios_struct * as)
760 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
761 return as->events[as->event_tail];
764 static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
766 struct apm_bios_struct * as;
768 if (user_list == NULL)
769 return 0;
770 for (as = user_list; as != NULL; as = as->next) {
771 if (as == sender)
772 continue;
773 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
774 if (as->event_head == as->event_tail) {
775 static int notified;
777 if (notified == 0) {
778 printk(KERN_ERR "apm: an event queue overflowed\n");
779 notified = 1;
781 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
783 as->events[as->event_head] = event;
784 if (!as->suser)
785 continue;
786 switch (event) {
787 case APM_SYS_SUSPEND:
788 case APM_USER_SUSPEND:
789 as->suspends_pending++;
790 suspends_pending++;
791 break;
793 case APM_SYS_STANDBY:
794 case APM_USER_STANDBY:
795 as->standbys_pending++;
796 standbys_pending++;
797 break;
800 wake_up_interruptible(&apm_waitqueue);
801 return 1;
804 static void set_time(void)
806 unsigned long flags;
808 if (!got_clock_diff) /* Don't know time zone, can't set clock */
809 return;
811 save_flags(flags);
812 cli();
813 CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
814 restore_flags(flags);
817 static void suspend(void)
819 unsigned long flags;
820 int err;
822 #ifndef CONFIG_APM_RTC_IS_GMT
824 * Estimate time zone so that set_time can update the clock
826 save_flags(flags);
827 clock_cmos_diff = -get_cmos_time();
828 cli();
829 clock_cmos_diff += CURRENT_TIME;
830 got_clock_diff = 1;
831 restore_flags(flags);
832 #endif
834 err = apm_set_power_state(APM_STATE_SUSPEND);
835 if (err)
836 apm_error("suspend", err);
837 #ifdef INIT_TIMER_AFTER_SUSPEND
838 save_flags(flags);
839 cli();
840 /* set the clock to 100 Hz */
841 outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
842 udelay(10);
843 outb_p(LATCH & 0xff , 0x40); /* LSB */
844 udelay(10);
845 outb(LATCH >> 8 , 0x40); /* MSB */
846 udelay(10);
847 restore_flags(flags);
848 #endif
849 set_time();
852 static void standby(void)
854 int err;
856 err = apm_set_power_state(APM_STATE_STANDBY);
857 if (err)
858 apm_error("standby", err);
861 static apm_event_t get_event(void)
863 int error;
864 apm_event_t event;
865 apm_eventinfo_t info;
867 static int notified = 0;
869 /* we don't use the eventinfo */
870 error = apm_get_event(&event, &info);
871 if (error == APM_SUCCESS)
872 return event;
874 if ((error != APM_NO_EVENTS) && (notified++ == 0))
875 apm_error("get_event", error);
877 return 0;
880 static void send_event(apm_event_t event, apm_event_t undo,
881 struct apm_bios_struct *sender)
883 callback_list_t * call;
884 callback_list_t * fix;
886 for (call = callback_list; call != NULL; call = call->next) {
887 if (call->callback(event) && undo) {
888 for (fix = callback_list; fix != call; fix = fix->next)
889 fix->callback(undo);
890 if (apm_bios_info.version > 0x100)
891 apm_set_power_state(APM_STATE_REJECT);
892 return;
896 queue_event(event, sender);
899 static void check_events(void)
901 apm_event_t event;
902 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
903 static unsigned long last_resume = 0;
904 static int ignore_bounce = 0;
905 #endif
907 while ((event = get_event()) != 0) {
908 #ifdef APM_DEBUG
909 if (event <= NR_APM_EVENT_NAME)
910 printk(KERN_DEBUG "apm: received %s notify\n",
911 apm_event_name[event - 1]);
912 else
913 printk(KERN_DEBUG "apm: received unknown "
914 "event 0x%02x\n", event);
915 #endif
916 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
917 if (ignore_bounce
918 && ((jiffies - last_resume) > BOUNCE_INTERVAL))
919 ignore_bounce = 0;
920 #endif
921 switch (event) {
922 case APM_SYS_STANDBY:
923 case APM_USER_STANDBY:
924 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
925 if (waiting_for_resume)
926 return;
927 waiting_for_resume = 1;
928 #endif
929 send_event(event, APM_STANDBY_RESUME, NULL);
930 if (standbys_pending <= 0)
931 standby();
932 break;
934 case APM_USER_SUSPEND:
935 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
936 if (apm_bios_info.version > 0x100)
937 apm_set_power_state(APM_STATE_REJECT);
938 break;
939 #endif
940 case APM_SYS_SUSPEND:
941 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
942 if (ignore_bounce)
943 break;
944 #endif
945 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
946 if (waiting_for_resume)
947 return;
948 waiting_for_resume = 1;
949 #endif
950 send_event(event, APM_NORMAL_RESUME, NULL);
951 if (suspends_pending <= 0)
952 suspend();
953 break;
955 case APM_NORMAL_RESUME:
956 case APM_CRITICAL_RESUME:
957 case APM_STANDBY_RESUME:
958 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
959 waiting_for_resume = 0;
960 #endif
961 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
962 last_resume = jiffies;
963 ignore_bounce = 1;
964 #endif
965 set_time();
966 send_event(event, 0, NULL);
967 break;
969 case APM_LOW_BATTERY:
970 case APM_POWER_STATUS_CHANGE:
971 case APM_CAPABILITY_CHANGE:
972 send_event(event, 0, NULL);
973 break;
975 case APM_UPDATE_TIME:
976 set_time();
977 break;
979 case APM_CRITICAL_SUSPEND:
980 suspend();
981 break;
986 static void apm_event_handler(void)
988 static int pending_count = 0;
990 if (((standbys_pending > 0) || (suspends_pending > 0))
991 && (apm_bios_info.version > 0x100)
992 && (pending_count-- <= 0)) {
993 int err;
994 pending_count = 4;
996 err = apm_set_power_state(APM_STATE_BUSY);
997 if (err)
998 apm_error("busy", err);
1001 if (!(((standbys_pending > 0) || (suspends_pending > 0))
1002 && (apm_bios_info.version == 0x100)))
1003 check_events();
1006 static int apm_do_idle(void)
1008 #ifdef CONFIG_APM_CPU_IDLE
1009 u32 dummy;
1011 if (!apm_enabled)
1012 return 0;
1014 if (apm_bios_call_simple(0x5305, 0, 0, &dummy))
1015 return 0;
1017 #ifdef ALWAYS_CALL_BUSY
1018 clock_slowed = 1;
1019 #else
1020 clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
1021 #endif
1022 return 1;
1023 #else
1024 return 0;
1025 #endif
1028 static void apm_do_busy(void)
1030 #ifdef CONFIG_APM_CPU_IDLE
1031 u32 dummy;
1033 if (clock_slowed) {
1034 (void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
1035 clock_slowed = 0;
1037 #endif
1041 * This is the APM thread main loop.
1043 * Check whether we're the only running process to
1044 * decide if we should just power down.
1046 * Do this by checking the runqueue: if we're the
1047 * only one, then the current process run_list will
1048 * have both prev and next pointing to the same
1049 * entry (the true idle process)
1051 #define system_idle() (current->run_list.next == current->run_list.prev)
1053 static void apm_mainloop(void)
1055 DECLARE_WAITQUEUE(wait, current);
1056 apm_enabled = 1;
1058 add_wait_queue(&apm_waitqueue, &wait);
1059 current->state = TASK_INTERRUPTIBLE;
1060 for (;;) {
1061 /* Nothing to do, just sleep for the timeout */
1062 schedule_timeout(APM_CHECK_TIMEOUT);
1065 * Ok, check all events, check for idle (and mark us sleeping
1066 * so as not to count towards the load average)..
1068 current->state = TASK_INTERRUPTIBLE;
1069 apm_event_handler();
1070 if (!system_idle())
1071 continue;
1072 if (apm_do_idle()) {
1073 unsigned long start = jiffies;
1074 do {
1075 apm_do_idle();
1076 if (jiffies - start > APM_CHECK_TIMEOUT)
1077 break;
1078 } while (system_idle());
1079 apm_do_busy();
1080 apm_event_handler();
1085 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
1087 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
1088 printk(KERN_ERR "apm: %s passed bad filp", func);
1089 return 1;
1091 return 0;
1094 static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
1096 struct apm_bios_struct * as;
1097 int i;
1098 apm_event_t event;
1099 DECLARE_WAITQUEUE(wait, current);
1101 as = fp->private_data;
1102 if (check_apm_bios_struct(as, "read"))
1103 return -EIO;
1104 if (count < sizeof(apm_event_t))
1105 return -EINVAL;
1106 if (queue_empty(as)) {
1107 if (fp->f_flags & O_NONBLOCK)
1108 return -EAGAIN;
1109 add_wait_queue(&apm_waitqueue, &wait);
1110 repeat:
1111 set_current_state(TASK_INTERRUPTIBLE);
1112 if (queue_empty(as) && !signal_pending(current)) {
1113 schedule();
1114 goto repeat;
1116 current->state = TASK_RUNNING;
1117 remove_wait_queue(&apm_waitqueue, &wait);
1119 i = count;
1120 while ((i >= sizeof(event)) && !queue_empty(as)) {
1121 event = get_queued_event(as);
1122 copy_to_user(buf, &event, sizeof(event));
1123 switch (event) {
1124 case APM_SYS_SUSPEND:
1125 case APM_USER_SUSPEND:
1126 as->suspends_read++;
1127 break;
1129 case APM_SYS_STANDBY:
1130 case APM_USER_STANDBY:
1131 as->standbys_read++;
1132 break;
1134 buf += sizeof(event);
1135 i -= sizeof(event);
1137 if (i < count)
1138 return count - i;
1139 if (signal_pending(current))
1140 return -ERESTARTSYS;
1141 return 0;
1144 static unsigned int do_poll(struct file *fp, poll_table * wait)
1146 struct apm_bios_struct * as;
1148 as = fp->private_data;
1149 if (check_apm_bios_struct(as, "select"))
1150 return 0;
1151 poll_wait(fp, &apm_waitqueue, wait);
1152 if (!queue_empty(as))
1153 return POLLIN | POLLRDNORM;
1154 return 0;
1157 static int do_ioctl(struct inode * inode, struct file *filp,
1158 u_int cmd, u_long arg)
1160 struct apm_bios_struct * as;
1162 as = filp->private_data;
1163 if (check_apm_bios_struct(as, "ioctl"))
1164 return -EIO;
1165 if (!as->suser)
1166 return -EPERM;
1167 switch (cmd) {
1168 case APM_IOC_STANDBY:
1169 if (as->standbys_read > 0) {
1170 as->standbys_read--;
1171 as->standbys_pending--;
1172 standbys_pending--;
1174 else
1175 send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
1176 if (standbys_pending <= 0)
1177 standby();
1178 break;
1179 case APM_IOC_SUSPEND:
1180 if (as->suspends_read > 0) {
1181 as->suspends_read--;
1182 as->suspends_pending--;
1183 suspends_pending--;
1185 else
1186 send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
1187 if (suspends_pending <= 0)
1188 suspend();
1189 break;
1190 default:
1191 return -EINVAL;
1193 return 0;
1196 static int do_release(struct inode * inode, struct file * filp)
1198 struct apm_bios_struct * as;
1200 as = filp->private_data;
1201 filp->private_data = NULL;
1202 if (check_apm_bios_struct(as, "release"))
1203 return 0;
1204 if (as->standbys_pending > 0) {
1205 standbys_pending -= as->standbys_pending;
1206 if (standbys_pending <= 0)
1207 standby();
1209 if (as->suspends_pending > 0) {
1210 suspends_pending -= as->suspends_pending;
1211 if (suspends_pending <= 0)
1212 suspend();
1214 if (user_list == as)
1215 user_list = as->next;
1216 else {
1217 struct apm_bios_struct * as1;
1219 for (as1 = user_list;
1220 (as1 != NULL) && (as1->next != as);
1221 as1 = as1->next)
1223 if (as1 == NULL)
1224 printk(KERN_ERR "apm: filp not in user list");
1225 else
1226 as1->next = as->next;
1228 kfree_s(as, sizeof(*as));
1229 return 0;
1232 static int do_open(struct inode * inode, struct file * filp)
1234 struct apm_bios_struct * as;
1236 as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
1237 if (as == NULL) {
1238 printk(KERN_ERR "apm: cannot allocate struct of size %d bytes",
1239 sizeof(*as));
1240 return -ENOMEM;
1242 as->magic = APM_BIOS_MAGIC;
1243 as->event_tail = as->event_head = 0;
1244 as->suspends_pending = as->standbys_pending = 0;
1245 as->suspends_read = as->standbys_read = 0;
1247 * XXX - this is a tiny bit broken, when we consider BSD
1248 * process accounting. If the device is opened by root, we
1249 * instantly flag that we used superuser privs. Who knows,
1250 * we might close the device immediately without doing a
1251 * privileged operation -- cevans
1253 as->suser = capable(CAP_SYS_ADMIN);
1254 as->next = user_list;
1255 user_list = as;
1256 filp->private_data = as;
1257 return 0;
1260 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
1262 char * p;
1263 unsigned short bx;
1264 unsigned short cx;
1265 unsigned short dx;
1266 unsigned short nbat;
1267 unsigned short error;
1268 unsigned short ac_line_status = 0xff;
1269 unsigned short battery_status = 0xff;
1270 unsigned short battery_flag = 0xff;
1271 int percentage = -1;
1272 int time_units = -1;
1273 char *units = "?";
1275 if (!apm_enabled)
1276 return 0;
1277 p = buf;
1279 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
1280 ac_line_status = (bx >> 8) & 0xff;
1281 battery_status = bx & 0xff;
1282 if ((cx & 0xff) != 0xff)
1283 percentage = cx & 0xff;
1285 if (apm_bios_info.version > 0x100) {
1286 battery_flag = (cx >> 8) & 0xff;
1287 if (dx != 0xffff) {
1288 units = (dx & 0x8000) ? "min" : "sec";
1289 time_units = dx & 0x7fff;
1293 /* Arguments, with symbols from linux/apm_bios.h. Information is
1294 from the Get Power Status (0x0a) call unless otherwise noted.
1296 0) Linux driver version (this will change if format changes)
1297 1) APM BIOS Version. Usually 1.0 or 1.1.
1298 2) APM flags from APM Installation Check (0x00):
1299 bit 0: APM_16_BIT_SUPPORT
1300 bit 1: APM_32_BIT_SUPPORT
1301 bit 2: APM_IDLE_SLOWS_CLOCK
1302 bit 3: APM_BIOS_DISABLED
1303 bit 4: APM_BIOS_DISENGAGED
1304 3) AC line status
1305 0x00: Off-line
1306 0x01: On-line
1307 0x02: On backup power (APM BIOS 1.1 only)
1308 0xff: Unknown
1309 4) Battery status
1310 0x00: High
1311 0x01: Low
1312 0x02: Critical
1313 0x03: Charging
1314 0xff: Unknown
1315 5) Battery flag
1316 bit 0: High
1317 bit 1: Low
1318 bit 2: Critical
1319 bit 3: Charging
1320 bit 7: No system battery
1321 0xff: Unknown
1322 6) Remaining battery life (percentage of charge):
1323 0-100: valid
1324 -1: Unknown
1325 7) Remaining battery life (time units):
1326 Number of remaining minutes or seconds
1327 -1: Unknown
1328 8) min = minutes; sec = seconds */
1330 p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1331 driver_version,
1332 (apm_bios_info.version >> 8) & 0xff,
1333 apm_bios_info.version & 0xff,
1334 apm_bios_info.flags,
1335 ac_line_status,
1336 battery_status,
1337 battery_flag,
1338 percentage,
1339 time_units,
1340 units);
1342 return p - buf;
1345 static int apm(void *unused)
1347 unsigned short bx;
1348 unsigned short cx;
1349 unsigned short dx;
1350 unsigned short error;
1351 char * power_stat;
1352 char * bat_stat;
1354 strcpy(current->comm, "kapmd");
1355 sigfillset(&current->blocked);
1357 if (apm_bios_info.version > 0x100) {
1359 * We only support BIOSs up to version 1.2
1361 if (apm_bios_info.version > 0x0102)
1362 apm_bios_info.version = 0x0102;
1363 if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) {
1364 /* Fall back to an APM 1.0 connection. */
1365 apm_bios_info.version = 0x100;
1368 if (debug) {
1369 printk(KERN_INFO "apm: Connection version %d.%d\n",
1370 (apm_bios_info.version >> 8) & 0xff,
1371 apm_bios_info.version & 0xff );
1373 error = apm_get_power_status(&bx, &cx, &dx);
1374 if (error)
1375 printk(KERN_INFO "apm: power status not available\n");
1376 else {
1377 switch ((bx >> 8) & 0xff) {
1378 case 0: power_stat = "off line"; break;
1379 case 1: power_stat = "on line"; break;
1380 case 2: power_stat = "on backup power"; break;
1381 default: power_stat = "unknown"; break;
1383 switch (bx & 0xff) {
1384 case 0: bat_stat = "high"; break;
1385 case 1: bat_stat = "low"; break;
1386 case 2: bat_stat = "critical"; break;
1387 case 3: bat_stat = "charging"; break;
1388 default: bat_stat = "unknown"; break;
1390 printk(KERN_INFO
1391 "apm: AC %s, battery status %s, battery life ",
1392 power_stat, bat_stat);
1393 if ((cx & 0xff) == 0xff)
1394 printk("unknown\n");
1395 else
1396 printk("%d%%\n", cx & 0xff);
1397 if (apm_bios_info.version > 0x100) {
1398 printk(KERN_INFO
1399 "apm: battery flag 0x%02x, battery life ",
1400 (cx >> 8) & 0xff);
1401 if (dx == 0xffff)
1402 printk("unknown\n");
1403 else
1404 printk("%d %s\n", dx & 0x7fff,
1405 (dx & 0x8000) ?
1406 "minutes" : "seconds");
1411 #ifdef CONFIG_APM_DO_ENABLE
1412 if (apm_bios_info.flags & APM_BIOS_DISABLED) {
1414 * This call causes my NEC UltraLite Versa 33/C to hang if it
1415 * is booted with PM disabled but not in the docking station.
1416 * Unfortunate ...
1418 error = apm_enable_power_management();
1419 if (error) {
1420 apm_error("enable power management", error);
1421 return -1;
1424 #endif
1425 if (((apm_bios_info.flags & APM_BIOS_DISENGAGED) == 0)
1426 && (apm_bios_info.version > 0x0100)) {
1427 if (apm_engage_power_management(0x0001) == APM_SUCCESS)
1428 apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
1431 apm_mainloop();
1432 return 0;
1435 static int __init apm_setup(char *str)
1437 int invert;
1439 while ((str != NULL) && (*str != '\0')) {
1440 if (strncmp(str, "off", 3) == 0)
1441 apm_disabled = 1;
1442 if (strncmp(str, "on", 2) == 0)
1443 apm_disabled = 0;
1444 invert = (strncmp(str, "no-", 3) == 0);
1445 if (invert)
1446 str += 3;
1447 if (strncmp(str, "debug", 5) == 0)
1448 debug = !invert;
1449 if (strncmp(str, "smp-power-off", 13) == 0)
1450 smp_hack = !invert;
1451 str = strchr(str, ',');
1452 if (str != NULL)
1453 str += strspn(str, ", \t");
1455 return 1;
1458 __setup("apm=", apm_setup);
1461 * Just start the APM thread. We do NOT want to do APM BIOS
1462 * calls from anything but the APM thread, if for no other reason
1463 * than the fact that we don't trust the APM BIOS. This way,
1464 * most common APM BIOS problems that lead to protection errors
1465 * etc will have at least some level of being contained...
1467 * In short, if something bad happens, at least we have a choice
1468 * of just killing the apm thread..
1470 static int __init apm_init(void)
1472 static struct proc_dir_entry *ent;
1474 if (apm_bios_info.version == 0) {
1475 printk(KERN_INFO "apm: BIOS not found.\n");
1476 return;
1478 printk(KERN_INFO
1479 "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
1480 ((apm_bios_info.version >> 8) & 0xff),
1481 (apm_bios_info.version & 0xff),
1482 apm_bios_info.flags,
1483 driver_version);
1484 if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1485 printk(KERN_INFO "apm: no 32 bit BIOS support\n");
1486 return;
1490 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1491 * but is reportedly a 1.0 BIOS.
1493 if (apm_bios_info.version == 0x001)
1494 apm_bios_info.version = 0x100;
1496 /* BIOS < 1.2 doesn't set cseg_16_len */
1497 if (apm_bios_info.version < 0x102)
1498 apm_bios_info.cseg_16_len = 0; /* 64k */
1500 if (debug) {
1501 printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x",
1502 apm_bios_info.cseg, apm_bios_info.offset,
1503 apm_bios_info.cseg_16, apm_bios_info.dseg);
1504 if (apm_bios_info.version > 0x100)
1505 printk(" cseg len %x, dseg len %x",
1506 apm_bios_info.cseg_len,
1507 apm_bios_info.dseg_len);
1508 if (apm_bios_info.version > 0x101)
1509 printk(" cseg16 len %x", apm_bios_info.cseg_16_len);
1510 printk("\n");
1513 if (apm_disabled) {
1514 printk(KERN_NOTICE "apm: disabled on user request.\n");
1515 return;
1518 #ifdef CONFIG_SMP
1519 if (smp_num_cpus > 1) {
1520 printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
1521 if (smp_hack)
1522 smp_hack = 2;
1523 return -1;
1525 #endif
1528 * Set up a segment that references the real mode segment 0x40
1529 * that extends up to the end of page zero (that we have reserved).
1530 * This is for buggy BIOS's that refer to (real mode) segment 0x40
1531 * even though they are called in protected mode.
1533 set_base(gdt[APM_40 >> 3],
1534 __va((unsigned long)0x40 << 4));
1535 _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
1537 apm_bios_entry.offset = apm_bios_info.offset;
1538 apm_bios_entry.segment = APM_CS;
1539 set_base(gdt[APM_CS >> 3],
1540 __va((unsigned long)apm_bios_info.cseg << 4));
1541 set_base(gdt[APM_CS_16 >> 3],
1542 __va((unsigned long)apm_bios_info.cseg_16 << 4));
1543 set_base(gdt[APM_DS >> 3],
1544 __va((unsigned long)apm_bios_info.dseg << 4));
1545 #ifndef APM_RELAX_SEGMENTS
1546 if (apm_bios_info.version == 0x100)
1547 #endif
1549 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1550 _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
1551 /* For some unknown machine. */
1552 _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
1553 /* For the DEC Hinote Ultra CT475 (and others?) */
1554 _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
1556 #ifndef APM_RELAX_SEGMENTS
1557 else {
1558 _set_limit((char *)&gdt[APM_CS >> 3],
1559 (apm_bios_info.cseg_len - 1) & 0xffff);
1560 _set_limit((char *)&gdt[APM_CS_16 >> 3],
1561 (apm_bios_info.cseg_16_len - 1) & 0xffff);
1562 _set_limit((char *)&gdt[APM_DS >> 3],
1563 (apm_bios_info.dseg_len - 1) & 0xffff);
1565 #endif
1567 ent = create_proc_entry("apm", 0, 0);
1568 if (ent != NULL)
1569 ent->get_info = apm_get_info;
1571 misc_register(&apm_device);
1573 kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
1576 module_init(apm_init)