Import 2.3.13pre1
[davej-history.git] / arch / i386 / kernel / apm.c
blobd23127d6ebf2a7ec008745289644b63f0bcedeb9
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 asmlinkage 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);
543 void apm_power_off(void)
546 * smp_hack == 2 means that we would have enabled APM support
547 * except there is more than one processor and so most of
548 * the APM stuff is unsafe. We will still try power down
549 * because is is useful to some people and they know what
550 * they are doing because they booted with the smp-power-off
551 * kernel option.
553 if (apm_enabled || (smp_hack == 2))
554 (void) apm_set_power_state(APM_STATE_OFF);
557 #ifdef CONFIG_APM_DISPLAY_BLANK
558 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
559 static int apm_set_display_power_state(u_short state)
561 int error;
563 /* Blank the first display device */
564 error = set_power_state(0x0100, state);
565 if (error == APM_BAD_DEVICE)
566 /* try to blank them all instead */
567 error = set_power_state(0x01ff, state);
568 return error;
570 #endif
572 #ifdef CONFIG_APM_DO_ENABLE
573 static int __init apm_enable_power_management(void)
575 u32 eax;
577 if (apm_bios_call_simple(0x5308,
578 (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff,
579 1, &eax))
580 return (eax >> 8) & 0xff;
581 apm_bios_info.flags &= ~APM_BIOS_DISABLED;
582 return APM_SUCCESS;
584 #endif
586 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
588 u32 eax;
589 u32 ebx;
590 u32 ecx;
591 u32 edx;
592 u32 dummy;
594 if (apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy))
595 return (eax >> 8) & 0xff;
596 *status = ebx;
597 *bat = ecx;
598 *life = edx;
599 return APM_SUCCESS;
602 static int apm_get_battery_status(u_short which, u_short *status,
603 u_short *bat, u_short *life, u_short *nbat)
605 u32 eax;
606 u32 ebx;
607 u32 ecx;
608 u32 edx;
609 u32 esi;
611 if (apm_bios_info.version < 0x0102) {
612 /* pretend we only have one battery. */
613 if (which != 1)
614 return APM_BAD_DEVICE;
615 *nbat = 1;
616 return apm_get_power_status(status, bat, life);
619 if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax,
620 &ebx, &ecx, &edx, &esi))
621 return (eax >> 8) & 0xff;
622 *status = ebx;
623 *bat = ecx;
624 *life = edx;
625 *nbat = esi;
626 return APM_SUCCESS;
629 static int __init apm_engage_power_management(u_short device)
631 u32 eax;
633 if (apm_bios_call_simple(0x530f, device, 1, &eax))
634 return (eax >> 8) & 0xff;
635 return APM_SUCCESS;
638 static void apm_error(char *str, int err)
640 int i;
642 for (i = 0; i < ERROR_COUNT; i++)
643 if (error_table[i].key == err) break;
644 if (i < ERROR_COUNT)
645 printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
646 else
647 printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
648 str, err);
651 /* Called from console driver -- must make sure apm_enabled. */
652 int apm_display_blank(void)
654 #ifdef CONFIG_APM_DISPLAY_BLANK
655 int error;
657 if (!apm_enabled)
658 return 0;
659 error = apm_set_display_power_state(APM_STATE_STANDBY);
660 if (error == APM_SUCCESS)
661 return 1;
662 apm_error("set display standby", error);
663 #endif
664 return 0;
667 /* Called from console driver -- must make sure apm_enabled. */
668 int apm_display_unblank(void)
670 #ifdef CONFIG_APM_DISPLAY_BLANK
671 int error;
673 if (!apm_enabled)
674 return 0;
675 error = apm_set_display_power_state(APM_STATE_READY);
676 if (error == APM_SUCCESS)
677 return 1;
678 apm_error("set display ready", error);
679 #endif
680 return 0;
683 int apm_register_callback(int (*callback)(apm_event_t))
685 callback_list_t * new;
687 new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
688 if (new == NULL)
689 return -ENOMEM;
690 new->callback = callback;
691 new->next = callback_list;
692 callback_list = new;
693 return 0;
696 void apm_unregister_callback(int (*callback)(apm_event_t))
698 callback_list_t ** ptr;
699 callback_list_t * old;
701 for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
702 if ((*ptr)->callback == callback)
703 break;
704 old = *ptr;
705 *ptr = old->next;
706 kfree_s(old, sizeof(callback_list_t));
709 static int queue_empty(struct apm_bios_struct * as)
711 return as->event_head == as->event_tail;
714 static apm_event_t get_queued_event(struct apm_bios_struct * as)
716 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
717 return as->events[as->event_tail];
720 static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
722 struct apm_bios_struct * as;
724 if (user_list == NULL)
725 return 0;
726 for (as = user_list; as != NULL; as = as->next) {
727 if (as == sender)
728 continue;
729 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
730 if (as->event_head == as->event_tail) {
731 static int notified;
733 if (notified == 0) {
734 printk(KERN_ERR "apm: an event queue overflowed\n");
735 notified = 1;
737 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
739 as->events[as->event_head] = event;
740 if (!as->suser)
741 continue;
742 switch (event) {
743 case APM_SYS_SUSPEND:
744 case APM_USER_SUSPEND:
745 as->suspends_pending++;
746 suspends_pending++;
747 break;
749 case APM_SYS_STANDBY:
750 case APM_USER_STANDBY:
751 as->standbys_pending++;
752 standbys_pending++;
753 break;
756 wake_up_interruptible(&apm_waitqueue);
757 return 1;
760 static void set_time(void)
762 unsigned long flags;
764 if (!got_clock_diff) /* Don't know time zone, can't set clock */
765 return;
767 save_flags(flags);
768 cli();
769 CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
770 restore_flags(flags);
773 static void suspend(void)
775 unsigned long flags;
776 int err;
778 #ifndef CONFIG_APM_RTC_IS_GMT
780 * Estimate time zone so that set_time can update the clock
782 save_flags(flags);
783 clock_cmos_diff = -get_cmos_time();
784 cli();
785 clock_cmos_diff += CURRENT_TIME;
786 got_clock_diff = 1;
787 restore_flags(flags);
788 #endif
790 err = apm_set_power_state(APM_STATE_SUSPEND);
791 if (err)
792 apm_error("suspend", err);
793 #ifdef INIT_TIMER_AFTER_SUSPEND
794 save_flags(flags);
795 cli();
796 /* set the clock to 100 Hz */
797 outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
798 udelay(10);
799 outb_p(LATCH & 0xff , 0x40); /* LSB */
800 udelay(10);
801 outb(LATCH >> 8 , 0x40); /* MSB */
802 udelay(10);
803 restore_flags(flags);
804 #endif
805 set_time();
808 static void standby(void)
810 int err;
812 err = apm_set_power_state(APM_STATE_STANDBY);
813 if (err)
814 apm_error("standby", err);
817 static apm_event_t get_event(void)
819 int error;
820 apm_event_t event;
821 apm_eventinfo_t info;
823 static int notified = 0;
825 /* we don't use the eventinfo */
826 error = apm_get_event(&event, &info);
827 if (error == APM_SUCCESS)
828 return event;
830 if ((error != APM_NO_EVENTS) && (notified++ == 0))
831 apm_error("get_event", error);
833 return 0;
836 static void send_event(apm_event_t event, apm_event_t undo,
837 struct apm_bios_struct *sender)
839 callback_list_t * call;
840 callback_list_t * fix;
842 for (call = callback_list; call != NULL; call = call->next) {
843 if (call->callback(event) && undo) {
844 for (fix = callback_list; fix != call; fix = fix->next)
845 fix->callback(undo);
846 if (apm_bios_info.version > 0x100)
847 apm_set_power_state(APM_STATE_REJECT);
848 return;
852 queue_event(event, sender);
855 static void check_events(void)
857 apm_event_t event;
858 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
859 static unsigned long last_resume = 0;
860 static int ignore_bounce = 0;
861 #endif
863 while ((event = get_event()) != 0) {
864 #ifdef APM_DEBUG
865 if (event <= NR_APM_EVENT_NAME)
866 printk(KERN_DEBUG "apm: received %s notify\n",
867 apm_event_name[event - 1]);
868 else
869 printk(KERN_DEBUG "apm: received unknown "
870 "event 0x%02x\n", event);
871 #endif
872 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
873 if (ignore_bounce
874 && ((jiffies - last_resume) > BOUNCE_INTERVAL))
875 ignore_bounce = 0;
876 #endif
877 switch (event) {
878 case APM_SYS_STANDBY:
879 case APM_USER_STANDBY:
880 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
881 if (waiting_for_resume)
882 return;
883 waiting_for_resume = 1;
884 #endif
885 send_event(event, APM_STANDBY_RESUME, NULL);
886 if (standbys_pending <= 0)
887 standby();
888 break;
890 case APM_USER_SUSPEND:
891 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
892 if (apm_bios_info.version > 0x100)
893 apm_set_power_state(APM_STATE_REJECT);
894 break;
895 #endif
896 case APM_SYS_SUSPEND:
897 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
898 if (ignore_bounce)
899 break;
900 #endif
901 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
902 if (waiting_for_resume)
903 return;
904 waiting_for_resume = 1;
905 #endif
906 send_event(event, APM_NORMAL_RESUME, NULL);
907 if (suspends_pending <= 0)
908 suspend();
909 break;
911 case APM_NORMAL_RESUME:
912 case APM_CRITICAL_RESUME:
913 case APM_STANDBY_RESUME:
914 #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
915 waiting_for_resume = 0;
916 #endif
917 #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE
918 last_resume = jiffies;
919 ignore_bounce = 1;
920 #endif
921 set_time();
922 send_event(event, 0, NULL);
923 break;
925 case APM_LOW_BATTERY:
926 case APM_POWER_STATUS_CHANGE:
927 case APM_CAPABILITY_CHANGE:
928 send_event(event, 0, NULL);
929 break;
931 case APM_UPDATE_TIME:
932 set_time();
933 break;
935 case APM_CRITICAL_SUSPEND:
936 suspend();
937 break;
943 * This is the APM thread main loop.
945 static void apm_mainloop(void)
947 DECLARE_WAITQUEUE(wait, current);
948 apm_enabled = 1;
950 add_wait_queue(&apm_waitqueue, &wait);
951 for (;;) {
952 static int pending_count = 0;
953 int err;
955 current->state = TASK_INTERRUPTIBLE;
956 schedule_timeout(APM_CHECK_TIMEOUT);
958 if (((standbys_pending > 0) || (suspends_pending > 0))
959 && (apm_bios_info.version > 0x100)
960 && (pending_count-- <= 0)) {
961 pending_count = 4;
963 err = apm_set_power_state(APM_STATE_BUSY);
964 if (err)
965 apm_error("busy", err);
968 if (!(((standbys_pending > 0) || (suspends_pending > 0))
969 && (apm_bios_info.version == 0x100)))
970 check_events();
974 /* Called from cpu_idle, must make sure apm_enabled. */
975 int apm_do_idle(void)
977 #ifdef CONFIG_APM_CPU_IDLE
978 u32 dummy;
980 if (!apm_enabled)
981 return 0;
983 if (apm_bios_call_simple(0x5305, 0, 0, &dummy))
984 return 0;
986 clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
987 return 1;
988 #else
989 return 0;
990 #endif
993 /* Called from cpu_idle, must make sure apm_enabled. */
994 void apm_do_busy(void)
996 #ifdef CONFIG_APM_CPU_IDLE
997 u32 dummy;
999 if (apm_enabled
1000 #ifndef ALWAYS_CALL_BUSY
1001 && clock_slowed
1002 #endif
1004 (void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
1005 clock_slowed = 0;
1007 #endif
1010 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
1012 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
1013 printk(KERN_ERR "apm: %s passed bad filp", func);
1014 return 1;
1016 return 0;
1019 static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
1021 struct apm_bios_struct * as;
1022 int i;
1023 apm_event_t event;
1024 DECLARE_WAITQUEUE(wait, current);
1026 as = fp->private_data;
1027 if (check_apm_bios_struct(as, "read"))
1028 return -EIO;
1029 if (count < sizeof(apm_event_t))
1030 return -EINVAL;
1031 if (queue_empty(as)) {
1032 if (fp->f_flags & O_NONBLOCK)
1033 return -EAGAIN;
1034 add_wait_queue(&apm_waitqueue, &wait);
1035 repeat:
1036 current->state = TASK_INTERRUPTIBLE;
1037 if (queue_empty(as) && !signal_pending(current)) {
1038 schedule();
1039 goto repeat;
1041 current->state = TASK_RUNNING;
1042 remove_wait_queue(&apm_waitqueue, &wait);
1044 i = count;
1045 while ((i >= sizeof(event)) && !queue_empty(as)) {
1046 event = get_queued_event(as);
1047 copy_to_user(buf, &event, sizeof(event));
1048 switch (event) {
1049 case APM_SYS_SUSPEND:
1050 case APM_USER_SUSPEND:
1051 as->suspends_read++;
1052 break;
1054 case APM_SYS_STANDBY:
1055 case APM_USER_STANDBY:
1056 as->standbys_read++;
1057 break;
1059 buf += sizeof(event);
1060 i -= sizeof(event);
1062 if (i < count)
1063 return count - i;
1064 if (signal_pending(current))
1065 return -ERESTARTSYS;
1066 return 0;
1069 static unsigned int do_poll(struct file *fp, poll_table * wait)
1071 struct apm_bios_struct * as;
1073 as = fp->private_data;
1074 if (check_apm_bios_struct(as, "select"))
1075 return 0;
1076 poll_wait(fp, &apm_waitqueue, wait);
1077 if (!queue_empty(as))
1078 return POLLIN | POLLRDNORM;
1079 return 0;
1082 static int do_ioctl(struct inode * inode, struct file *filp,
1083 u_int cmd, u_long arg)
1085 struct apm_bios_struct * as;
1087 as = filp->private_data;
1088 if (check_apm_bios_struct(as, "ioctl"))
1089 return -EIO;
1090 if (!as->suser)
1091 return -EPERM;
1092 switch (cmd) {
1093 case APM_IOC_STANDBY:
1094 if (as->standbys_read > 0) {
1095 as->standbys_read--;
1096 as->standbys_pending--;
1097 standbys_pending--;
1099 else
1100 send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
1101 if (standbys_pending <= 0)
1102 standby();
1103 break;
1104 case APM_IOC_SUSPEND:
1105 if (as->suspends_read > 0) {
1106 as->suspends_read--;
1107 as->suspends_pending--;
1108 suspends_pending--;
1110 else
1111 send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
1112 if (suspends_pending <= 0)
1113 suspend();
1114 break;
1115 default:
1116 return -EINVAL;
1118 return 0;
1121 static int do_release(struct inode * inode, struct file * filp)
1123 struct apm_bios_struct * as;
1125 as = filp->private_data;
1126 filp->private_data = NULL;
1127 if (check_apm_bios_struct(as, "release"))
1128 return 0;
1129 if (as->standbys_pending > 0) {
1130 standbys_pending -= as->standbys_pending;
1131 if (standbys_pending <= 0)
1132 standby();
1134 if (as->suspends_pending > 0) {
1135 suspends_pending -= as->suspends_pending;
1136 if (suspends_pending <= 0)
1137 suspend();
1139 if (user_list == as)
1140 user_list = as->next;
1141 else {
1142 struct apm_bios_struct * as1;
1144 for (as1 = user_list;
1145 (as1 != NULL) && (as1->next != as);
1146 as1 = as1->next)
1148 if (as1 == NULL)
1149 printk(KERN_ERR "apm: filp not in user list");
1150 else
1151 as1->next = as->next;
1153 kfree_s(as, sizeof(*as));
1154 return 0;
1157 static int do_open(struct inode * inode, struct file * filp)
1159 struct apm_bios_struct * as;
1161 as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
1162 if (as == NULL) {
1163 printk(KERN_ERR "apm: cannot allocate struct of size %d bytes",
1164 sizeof(*as));
1165 return -ENOMEM;
1167 as->magic = APM_BIOS_MAGIC;
1168 as->event_tail = as->event_head = 0;
1169 as->suspends_pending = as->standbys_pending = 0;
1170 as->suspends_read = as->standbys_read = 0;
1172 * XXX - this is a tiny bit broken, when we consider BSD
1173 * process accounting. If the device is opened by root, we
1174 * instantly flag that we used superuser privs. Who knows,
1175 * we might close the device immediately without doing a
1176 * privileged operation -- cevans
1178 as->suser = capable(CAP_SYS_ADMIN);
1179 as->next = user_list;
1180 user_list = as;
1181 filp->private_data = as;
1182 return 0;
1185 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
1187 char * p;
1188 unsigned short bx;
1189 unsigned short cx;
1190 unsigned short dx;
1191 unsigned short nbat;
1192 unsigned short error;
1193 unsigned short ac_line_status = 0xff;
1194 unsigned short battery_status = 0xff;
1195 unsigned short battery_flag = 0xff;
1196 int percentage = -1;
1197 int time_units = -1;
1198 char *units = "?";
1200 if (!apm_enabled)
1201 return 0;
1202 p = buf;
1204 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
1205 ac_line_status = (bx >> 8) & 0xff;
1206 battery_status = bx & 0xff;
1207 if ((cx & 0xff) != 0xff)
1208 percentage = cx & 0xff;
1210 if (apm_bios_info.version > 0x100) {
1211 battery_flag = (cx >> 8) & 0xff;
1212 if (dx != 0xffff) {
1213 units = (dx & 0x8000) ? "min" : "sec";
1214 time_units = dx & 0x7fff;
1218 /* Arguments, with symbols from linux/apm_bios.h. Information is
1219 from the Get Power Status (0x0a) call unless otherwise noted.
1221 0) Linux driver version (this will change if format changes)
1222 1) APM BIOS Version. Usually 1.0 or 1.1.
1223 2) APM flags from APM Installation Check (0x00):
1224 bit 0: APM_16_BIT_SUPPORT
1225 bit 1: APM_32_BIT_SUPPORT
1226 bit 2: APM_IDLE_SLOWS_CLOCK
1227 bit 3: APM_BIOS_DISABLED
1228 bit 4: APM_BIOS_DISENGAGED
1229 3) AC line status
1230 0x00: Off-line
1231 0x01: On-line
1232 0x02: On backup power (APM BIOS 1.1 only)
1233 0xff: Unknown
1234 4) Battery status
1235 0x00: High
1236 0x01: Low
1237 0x02: Critical
1238 0x03: Charging
1239 0xff: Unknown
1240 5) Battery flag
1241 bit 0: High
1242 bit 1: Low
1243 bit 2: Critical
1244 bit 3: Charging
1245 bit 7: No system battery
1246 0xff: Unknown
1247 6) Remaining battery life (percentage of charge):
1248 0-100: valid
1249 -1: Unknown
1250 7) Remaining battery life (time units):
1251 Number of remaining minutes or seconds
1252 -1: Unknown
1253 8) min = minutes; sec = seconds */
1255 p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1256 driver_version,
1257 (apm_bios_info.version >> 8) & 0xff,
1258 apm_bios_info.version & 0xff,
1259 apm_bios_info.flags,
1260 ac_line_status,
1261 battery_status,
1262 battery_flag,
1263 percentage,
1264 time_units,
1265 units);
1267 return p - buf;
1270 void __init apm_setup(char *str, int *dummy)
1272 int invert;
1274 while ((str != NULL) && (*str != '\0')) {
1275 if (strncmp(str, "off", 3) == 0)
1276 apm_disabled = 1;
1277 if (strncmp(str, "on", 2) == 0)
1278 apm_disabled = 0;
1279 invert = (strncmp(str, "no-", 3) == 0);
1280 if (invert)
1281 str += 3;
1282 if (strncmp(str, "debug", 5) == 0)
1283 debug = !invert;
1284 if (strncmp(str, "smp-power-off", 13) == 0)
1285 smp_hack = !invert;
1286 str = strchr(str, ',');
1287 if (str != NULL)
1288 str += strspn(str, ", \t");
1292 static int apm(void *unused)
1294 unsigned short bx;
1295 unsigned short cx;
1296 unsigned short dx;
1297 unsigned short error;
1298 char * power_stat;
1299 char * bat_stat;
1301 strcpy(current->comm, "kapmd");
1302 sigfillset(&current->blocked);
1304 if (apm_bios_info.version > 0x100) {
1306 * We only support BIOSs up to version 1.2
1308 if (apm_bios_info.version > 0x0102)
1309 apm_bios_info.version = 0x0102;
1310 if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) {
1311 /* Fall back to an APM 1.0 connection. */
1312 apm_bios_info.version = 0x100;
1315 if (debug) {
1316 printk(KERN_INFO "apm: Connection version %d.%d\n",
1317 (apm_bios_info.version >> 8) & 0xff,
1318 apm_bios_info.version & 0xff );
1320 error = apm_get_power_status(&bx, &cx, &dx);
1321 if (error)
1322 printk(KERN_INFO "apm: power status not available\n");
1323 else {
1324 switch ((bx >> 8) & 0xff) {
1325 case 0: power_stat = "off line"; break;
1326 case 1: power_stat = "on line"; break;
1327 case 2: power_stat = "on backup power"; break;
1328 default: power_stat = "unknown"; break;
1330 switch (bx & 0xff) {
1331 case 0: bat_stat = "high"; break;
1332 case 1: bat_stat = "low"; break;
1333 case 2: bat_stat = "critical"; break;
1334 case 3: bat_stat = "charging"; break;
1335 default: bat_stat = "unknown"; break;
1337 printk(KERN_INFO
1338 "apm: AC %s, battery status %s, battery life ",
1339 power_stat, bat_stat);
1340 if ((cx & 0xff) == 0xff)
1341 printk("unknown\n");
1342 else
1343 printk("%d%%\n", cx & 0xff);
1344 if (apm_bios_info.version > 0x100) {
1345 printk(KERN_INFO
1346 "apm: battery flag 0x%02x, battery life ",
1347 (cx >> 8) & 0xff);
1348 if (dx == 0xffff)
1349 printk("unknown\n");
1350 else
1351 printk("%d %s\n", dx & 0x7fff,
1352 (dx & 0x8000) ?
1353 "minutes" : "seconds");
1358 #ifdef CONFIG_APM_DO_ENABLE
1359 if (apm_bios_info.flags & APM_BIOS_DISABLED) {
1361 * This call causes my NEC UltraLite Versa 33/C to hang if it
1362 * is booted with PM disabled but not in the docking station.
1363 * Unfortunate ...
1365 error = apm_enable_power_management();
1366 if (error) {
1367 apm_error("enable power management", error);
1368 return -1;
1371 #endif
1372 if (((apm_bios_info.flags & APM_BIOS_DISABLED) == 0)
1373 && (apm_bios_info.version > 0x0100)) {
1374 if (apm_engage_power_management(0x0001) == APM_SUCCESS)
1375 apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
1378 apm_mainloop();
1379 return 0;
1383 * Just start the APM thread. We do NOT want to do APM BIOS
1384 * calls from anything but the APM thread, if for no other reason
1385 * than the fact that we don't trust the APM BIOS. This way,
1386 * most common APM BIOS problems that lead to protection errors
1387 * etc will have at least some level of being contained...
1389 * In short, if something bad happens, at least we have a choice
1390 * of just killing the apm thread..
1392 void __init apm_init(void)
1394 static struct proc_dir_entry *ent;
1396 if (apm_bios_info.version == 0) {
1397 printk(KERN_INFO "apm: BIOS not found.\n");
1398 return;
1400 printk(KERN_INFO
1401 "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
1402 ((apm_bios_info.version >> 8) & 0xff),
1403 (apm_bios_info.version & 0xff),
1404 apm_bios_info.flags,
1405 driver_version);
1406 if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1407 printk(KERN_INFO "apm: no 32 bit BIOS support\n");
1408 return;
1412 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1413 * but is reportedly a 1.0 BIOS.
1415 if (apm_bios_info.version == 0x001)
1416 apm_bios_info.version = 0x100;
1418 /* BIOS < 1.2 doesn't set cseg_16_len */
1419 if (apm_bios_info.version < 0x102)
1420 apm_bios_info.cseg_16_len = 0; /* 64k */
1422 if (debug) {
1423 printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x",
1424 apm_bios_info.cseg, apm_bios_info.offset,
1425 apm_bios_info.cseg_16, apm_bios_info.dseg);
1426 if (apm_bios_info.version > 0x100)
1427 printk(" cseg len %x, dseg len %x",
1428 apm_bios_info.cseg_len,
1429 apm_bios_info.dseg_len);
1430 if (apm_bios_info.version > 0x101)
1431 printk(" cseg16 len %x", apm_bios_info.cseg_16_len);
1432 printk("\n");
1435 if (apm_disabled) {
1436 printk(KERN_NOTICE "apm: disabled on user request.\n");
1437 return;
1440 #ifdef CONFIG_SMP
1441 if (smp_num_cpus > 1) {
1442 printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
1443 if (smp_hack)
1444 smp_hack = 2;
1445 return -1;
1447 #endif
1450 * Set up a segment that references the real mode segment 0x40
1451 * that extends up to the end of page zero (that we have reserved).
1452 * This is for buggy BIOS's that refer to (real mode) segment 0x40
1453 * even though they are called in protected mode.
1455 set_base(gdt[APM_40 >> 3],
1456 __va((unsigned long)0x40 << 4));
1457 _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
1459 apm_bios_entry.offset = apm_bios_info.offset;
1460 apm_bios_entry.segment = APM_CS;
1461 set_base(gdt[APM_CS >> 3],
1462 __va((unsigned long)apm_bios_info.cseg << 4));
1463 set_base(gdt[APM_CS_16 >> 3],
1464 __va((unsigned long)apm_bios_info.cseg_16 << 4));
1465 set_base(gdt[APM_DS >> 3],
1466 __va((unsigned long)apm_bios_info.dseg << 4));
1467 #ifndef APM_RELAX_SEGMENTS
1468 if (apm_bios_info.version == 0x100)
1469 #endif
1471 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1472 _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
1473 /* For some unknown machine. */
1474 _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
1475 /* For the DEC Hinote Ultra CT475 (and others?) */
1476 _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
1478 #ifndef APM_RELAX_SEGMENTS
1479 else {
1480 _set_limit((char *)&gdt[APM_CS >> 3],
1481 (apm_bios_info.cseg_len - 1) & 0xffff);
1482 _set_limit((char *)&gdt[APM_CS_16 >> 3],
1483 (apm_bios_info.cseg_16_len - 1) & 0xffff);
1484 _set_limit((char *)&gdt[APM_DS >> 3],
1485 (apm_bios_info.dseg_len - 1) & 0xffff);
1487 #endif
1489 ent = create_proc_entry("apm", 0, 0);
1490 if (ent != NULL)
1491 ent->get_info = apm_get_info;
1493 misc_register(&apm_device);
1495 kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);