linux-bk/arch/i386/kernel/apm.c
<<
>>
Prefs
   1/* -*- linux-c -*-
   2 * APM BIOS driver for Linux
   3 * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au)
   4 *
   5 * Initial development of this driver was funded by NEC Australia P/L
   6 *      and NEC Corporation
   7 *
   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.
  12 *
  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.
  17 *
  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 (sfr@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
  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
  41 * Oct 2001, Version 1.15
  42 * Jan 2002, Version 1.16
  43 * Oct 2002, Version 1.16ac
  44 *
  45 * History:
  46 *    0.6b: first version in official kernel, Linux 1.3.46
  47 *    0.7: changed /proc/apm format, Linux 1.3.58
  48 *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
  49 *    0.9: only call bios if bios is present, Linux 1.3.72
  50 *    1.0: use fixed device number, consolidate /proc/apm into this file,
  51 *         Linux 1.3.85
  52 *    1.1: support user-space standby and suspend, power off after system
  53 *         halted, Linux 1.3.98
  54 *    1.2: When resetting RTC after resume, take care so that the time
  55 *         is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
  56 *         <jtoth@princeton.edu>); improve interaction between
  57 *         screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
  58 *    1.2a:Simple change to stop mysterious bug reports with SMP also added
  59 *         levels to the printk calls. APM is not defined for SMP machines.
  60 *         The new replacment for it is, but Linux doesn't yet support this.
  61 *         Alan Cox Linux 2.1.55
  62 *    1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
  63 *    1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
  64 *         Dean Gaudet <dgaudet@arctic.org>.
  65 *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
  66 *    1.5: Fix segment register reloading (in case of bad segments saved
  67 *         across BIOS call).
  68 *         Stephen Rothwell
  69 *    1.6: Cope with complier/assembler differences.
  70 *         Only try to turn off the first display device.
  71 *         Fix OOPS at power off with no APM BIOS by Jan Echternach
  72 *                   <echter@informatik.uni-rostock.de>
  73 *         Stephen Rothwell
  74 *    1.7: Modify driver's cached copy of the disabled/disengaged flags
  75 *         to reflect current state of APM BIOS.
  76 *         Chris Rankin <rankinc@bellsouth.net>
  77 *         Reset interrupt 0 timer to 100Hz after suspend
  78 *         Chad Miller <cmiller@surfsouth.com>
  79 *         Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
  80 *         Richard Gooch <rgooch@atnf.csiro.au>
  81 *         Allow boot time disabling of APM
  82 *         Make boot messages far less verbose by default
  83 *         Make asm safer
  84 *         Stephen Rothwell
  85 *    1.8: Add CONFIG_APM_RTC_IS_GMT
  86 *         Richard Gooch <rgooch@atnf.csiro.au>
  87 *         change APM_NOINTS to CONFIG_APM_ALLOW_INTS
  88 *         remove dependency on CONFIG_PROC_FS
  89 *         Stephen Rothwell
  90 *    1.9: Fix small typo.  <laslo@wodip.opole.pl>
  91 *         Try to cope with BIOS's that need to have all display
  92 *         devices blanked and not just the first one.
  93 *         Ross Paterson <ross@soi.city.ac.uk>
  94 *         Fix segment limit setting it has always been wrong as
  95 *         the segments needed to have byte granularity.
  96 *         Mark a few things __init.
  97 *         Add hack to allow power off of SMP systems by popular request.
  98 *         Use CONFIG_SMP instead of __SMP__
  99 *         Ignore BOUNCES for three seconds.
 100 *         Stephen Rothwell
 101 *   1.10: Fix for Thinkpad return code.
 102 *         Merge 2.2 and 2.3 drivers.
 103 *         Remove APM dependencies in arch/i386/kernel/process.c
 104 *         Remove APM dependencies in drivers/char/sysrq.c
 105 *         Reset time across standby.
 106 *         Allow more inititialisation on SMP.
 107 *         Remove CONFIG_APM_POWER_OFF and make it boot time
 108 *         configurable (default on).
 109 *         Make debug only a boot time parameter (remove APM_DEBUG).
 110 *         Try to blank all devices on any error.
 111 *   1.11: Remove APM dependencies in drivers/char/console.c
 112 *         Check nr_running to detect if we are idle (from
 113 *         Borislav Deianov <borislav@lix.polytechnique.fr>)
 114 *         Fix for bioses that don't zero the top part of the
 115 *         entrypoint offset (Mario Sitta <sitta@al.unipmn.it>)
 116 *         (reported by Panos Katsaloulis <teras@writeme.com>).
 117 *         Real mode power off patch (Walter Hofmann
 118 *         <Walter.Hofmann@physik.stud.uni-erlangen.de>).
 119 *   1.12: Remove CONFIG_SMP as the compiler will optimize
 120 *         the code away anyway (smp_num_cpus == 1 in UP)
 121 *         noted by Artur Skawina <skawina@geocities.com>.
 122 *         Make power off under SMP work again.
 123 *         Fix thinko with initial engaging of BIOS.
 124 *         Make sure power off only happens on CPU 0
 125 *         (Paul "Rusty" Russell <rusty@rustcorp.com.au>).
 126 *         Do error notification to user mode if BIOS calls fail.
 127 *         Move entrypoint offset fix to ...boot/setup.S
 128 *         where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
 129 *         Remove smp-power-off. SMP users must now specify
 130 *         "apm=power-off" on the kernel command line. Suggested
 131 *         by Jim Avera <jima@hal.com>, modified by Alan Cox
 132 *         <alan@lxorguk.ukuu.org.uk>.
 133 *         Register the /proc/apm entry even on SMP so that
 134 *         scripts that check for it before doing power off
 135 *         work (Jim Avera <jima@hal.com>).
 136 *   1.13: Changes for new pm_ interfaces (Andy Henroid
 137 *         <andy_henroid@yahoo.com>).
 138 *         Modularize the code.
 139 *         Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS
 140 *         is now the way life works).
 141 *         Fix thinko in suspend() (wrong return).
 142 *         Notify drivers on critical suspend.
 143 *         Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz>
 144 *         modified by sfr).
 145 *         Disable interrupts while we are suspended (Andy Henroid
 146 *         <andy_henroid@yahoo.com> fixed by sfr).
 147 *         Make power off work on SMP again (Tony Hoyle
 148 *         <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr.
 149 *         Remove CONFIG_APM_SUSPEND_BOUNCE.  The bounce ignore
 150 *         interval is now configurable.
 151 *   1.14: Make connection version persist across module unload/load.
 152 *         Enable and engage power management earlier.
 153 *         Disengage power management on module unload.
 154 *         Changed to use the sysrq-register hack for registering the
 155 *         power off function called by magic sysrq based upon discussions
 156 *         in irc://irc.openprojects.net/#kernelnewbies
 157 *         (Crutcher Dunnavant <crutcher+kernel@datastacks.com>).
 158 *         Make CONFIG_APM_REAL_MODE_POWER_OFF run time configurable.
 159 *         (Arjan van de Ven <arjanv@redhat.com>) modified by sfr.
 160 *         Work around byte swap bug in one of the Vaio's BIOS's
 161 *         (Marc Boucher <marc@mbsi.ca>).
 162 *         Exposed the disable flag to dmi so that we can handle known
 163 *         broken APM (Alan Cox <alan@redhat.com>).
 164 *   1.14ac: If the BIOS says "I slowed the CPU down" then don't spin
 165 *         calling it - instead idle. (Alan Cox <alan@redhat.com>)
 166 *         If an APM idle fails log it and idle sensibly
 167 *   1.15: Don't queue events to clients who open the device O_WRONLY.
 168 *         Don't expect replies from clients who open the device O_RDONLY.
 169 *         (Idea from Thomas Hood <jdthood@mail.com>)
 170 *         Minor waitqueue cleanups. (John Fremlin <chief@bandits.org>)
 171 *   1.16: Fix idle calling. (Andreas Steinmetz <ast@domdv.de> et al.)
 172 *         Notify listeners of standby or suspend events before notifying
 173 *         drivers. Return EBUSY to ioctl() if suspend is rejected.
 174 *         (Russell King <rmk@arm.linux.org.uk> and Thomas Hood)
 175 *         Ignore first resume after we generate our own resume event
 176 *         after a suspend (Thomas Hood <jdthood@mail.com>)
 177 *         Daemonize now gets rid of our controlling terminal (sfr).
 178 *         CONFIG_APM_CPU_IDLE now just affects the default value of
 179 *         idle_threshold (sfr).
 180 *         Change name of kernel apm daemon (as it no longer idles) (sfr).
 181 *   1.16ac: Fix up SMP support somewhat. You can now force SMP on and we
 182 *         make _all_ APM calls on the CPU#0. Fix unsafe sign bug.
 183 *         TODO: determine if its "boot CPU" or "CPU0" we want to lock to.
 184 *
 185 * APM 1.1 Reference:
 186 *
 187 *   Intel Corporation, Microsoft Corporation. Advanced Power Management
 188 *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
 189 *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
 190 *
 191 * [This document is available free from Intel by calling 800.628.8686 (fax
 192 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
 193 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc.  It is also
 194 * available from Microsoft by calling 206.882.8080.]
 195 *
 196 * APM 1.2 Reference:
 197 *   Intel Corporation, Microsoft Corporation. Advanced Power Management
 198 *   (APM) BIOS Interface Specification, Revision 1.2, February 1996.
 199 *
 200 * [This document is available from Microsoft at:
 201 *    http://www.microsoft.com/hwdev/busbios/amp_12.htm]
 202 */
 203
 204#include <linux/config.h>
 205#include <linux/module.h>
 206
 207#include <linux/poll.h>
 208#include <linux/types.h>
 209#include <linux/stddef.h>
 210#include <linux/timer.h>
 211#include <linux/fcntl.h>
 212#include <linux/slab.h>
 213#include <linux/stat.h>
 214#include <linux/proc_fs.h>
 215#include <linux/miscdevice.h>
 216#include <linux/apm_bios.h>
 217#include <linux/init.h>
 218#include <linux/time.h>
 219#include <linux/sched.h>
 220#include <linux/pm.h>
 221#include <linux/device.h>
 222#include <linux/kernel.h>
 223#include <linux/smp.h>
 224#include <linux/smp_lock.h>
 225#include <linux/dmi.h>
 226#include <linux/suspend.h>
 227
 228#include <asm/system.h>
 229#include <asm/uaccess.h>
 230#include <asm/desc.h>
 231
 232#include "io_ports.h"
 233
 234extern spinlock_t i8253_lock;
 235extern unsigned long get_cmos_time(void);
 236extern void machine_real_restart(unsigned char *, int);
 237
 238#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 239extern int (*console_blank_hook)(int);
 240#endif
 241
 242/*
 243 * The apm_bios device is one of the misc char devices.
 244 * This is its minor number.
 245 */
 246#define APM_MINOR_DEV   134
 247
 248/*
 249 * See Documentation/Config.help for the configuration options.
 250 *
 251 * Various options can be changed at boot time as follows:
 252 * (We allow underscores for compatibility with the modules code)
 253 *      apm=on/off                      enable/disable APM
 254 *          [no-]allow[-_]ints          allow interrupts during BIOS calls
 255 *          [no-]broken[-_]psr          BIOS has a broken GetPowerStatus call
 256 *          [no-]realmode[-_]power[-_]off       switch to real mode before
 257 *                                              powering off
 258 *          [no-]debug                  log some debugging messages
 259 *          [no-]power[-_]off           power off on shutdown
 260 *          [no-]smp                    Use apm even on an SMP box
 261 *          bounce[-_]interval=<n>      number of ticks to ignore suspend
 262 *                                      bounces
 263 *          idle[-_]threshold=<n>       System idle percentage above which to
 264 *                                      make APM BIOS idle calls. Set it to
 265 *                                      100 to disable.
 266 *          idle[-_]period=<n>          Period (in 1/100s of a second) over
 267 *                                      which the idle percentage is
 268 *                                      calculated.
 269 */
 270
 271/* KNOWN PROBLEM MACHINES:
 272 *
 273 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
 274 *                         [Confirmed by TI representative]
 275 * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
 276 *                    [Confirmed by BIOS disassembly]
 277 *                    [This may work now ...]
 278 * P: Toshiba 1950S: battery life information only gets updated after resume
 279 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
 280 *      broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
 281 * ?: AcerNote-950: oops on reading /proc/apm - workaround is a WIP
 282 *      Neale Banks <neale@lowendale.com.au> December 2000
 283 *
 284 * Legend: U = unusable with APM patches
 285 *         P = partially usable with APM patches
 286 */
 287
 288/*
 289 * Define as 1 to make the driver always call the APM BIOS busy
 290 * routine even if the clock was not reported as slowed by the
 291 * idle routine.  Otherwise, define as 0.
 292 */
 293#define ALWAYS_CALL_BUSY   1
 294
 295/*
 296 * Define to make the APM BIOS calls zero all data segment registers (so
 297 * that an incorrect BIOS implementation will cause a kernel panic if it
 298 * tries to write to arbitrary memory).
 299 */
 300#define APM_ZERO_SEGS
 301
 302#include "apm.h"
 303
 304/*
 305 * Define to make all _set_limit calls use 64k limits.  The APM 1.1 BIOS is
 306 * supposed to provide limit information that it recognizes.  Many machines
 307 * do this correctly, but many others do not restrict themselves to their
 308 * claimed limit.  When this happens, they will cause a segmentation
 309 * violation in the kernel at boot time.  Most BIOS's, however, will
 310 * respect a 64k limit, so we use that.  If you want to be pedantic and
 311 * hold your BIOS to its claims, then undefine this.
 312 */
 313#define APM_RELAX_SEGMENTS
 314
 315/*
 316 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
 317 * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
 318 * David Chen <chen@ctpa04.mit.edu>
 319 */
 320#undef INIT_TIMER_AFTER_SUSPEND
 321
 322#ifdef INIT_TIMER_AFTER_SUSPEND
 323#include <linux/timex.h>
 324#include <asm/io.h>
 325#include <linux/delay.h>
 326#endif
 327
 328/*
 329 * Need to poll the APM BIOS every second
 330 */
 331#define APM_CHECK_TIMEOUT       (HZ)
 332
 333/*
 334 * Ignore suspend events for this amount of time after a resume
 335 */
 336#define DEFAULT_BOUNCE_INTERVAL         (3 * HZ)
 337
 338/*
 339 * Maximum number of events stored
 340 */
 341#define APM_MAX_EVENTS          20
 342
 343/*
 344 * The per-file APM data
 345 */
 346struct apm_user {
 347        int             magic;
 348        struct apm_user *       next;
 349        int             suser: 1;
 350        int             writer: 1;
 351        int             reader: 1;
 352        int             suspend_wait: 1;
 353        int             suspend_result;
 354        int             suspends_pending;
 355        int             standbys_pending;
 356        int             suspends_read;
 357        int             standbys_read;
 358        int             event_head;
 359        int             event_tail;
 360        apm_event_t     events[APM_MAX_EVENTS];
 361};
 362
 363/*
 364 * The magic number in apm_user
 365 */
 366#define APM_BIOS_MAGIC          0x4101
 367
 368/*
 369 * idle percentage above which bios idle calls are done
 370 */
 371#ifdef CONFIG_APM_CPU_IDLE
 372#define DEFAULT_IDLE_THRESHOLD  95
 373#else
 374#define DEFAULT_IDLE_THRESHOLD  100
 375#endif
 376#define DEFAULT_IDLE_PERIOD     (100 / 3)
 377
 378/*
 379 * Local variables
 380 */
 381static struct {
 382        unsigned long   offset;
 383        unsigned short  segment;
 384}                               apm_bios_entry;
 385static int                      clock_slowed;
 386static int                      idle_threshold = DEFAULT_IDLE_THRESHOLD;
 387static int                      idle_period = DEFAULT_IDLE_PERIOD;
 388static int                      set_pm_idle;
 389static int                      suspends_pending;
 390static int                      standbys_pending;
 391static int                      ignore_sys_suspend;
 392static int                      ignore_normal_resume;
 393static int                      bounce_interval = DEFAULT_BOUNCE_INTERVAL;
 394
 395#ifdef CONFIG_APM_RTC_IS_GMT
 396#       define  clock_cmos_diff 0
 397#       define  got_clock_diff  1
 398#else
 399static long                     clock_cmos_diff;
 400static int                      got_clock_diff;
 401#endif
 402static int                      debug;
 403static int                      smp;
 404static int                      apm_disabled = -1;
 405#ifdef CONFIG_SMP
 406static int                      power_off;
 407#else
 408static int                      power_off = 1;
 409#endif
 410#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
 411static int                      realmode_power_off = 1;
 412#else
 413static int                      realmode_power_off;
 414#endif
 415static int                      exit_kapmd;
 416static int                      kapmd_running;
 417#ifdef CONFIG_APM_ALLOW_INTS
 418static int                      allow_ints = 1;
 419#else
 420static int                      allow_ints;
 421#endif
 422static int                      broken_psr;
 423
 424static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
 425static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 426static struct apm_user *        user_list;
 427static DEFINE_SPINLOCK(user_list_lock);
 428static struct desc_struct       bad_bios_desc = { 0, 0x00409200 };
 429
 430static char                     driver_version[] = "1.16ac";    /* no spaces */
 431
 432/*
 433 *      APM event names taken from the APM 1.2 specification. These are
 434 *      the message codes that the BIOS uses to tell us about events
 435 */
 436static char *   apm_event_name[] = {
 437        "system standby",
 438        "system suspend",
 439        "normal resume",
 440        "critical resume",
 441        "low battery",
 442        "power status change",
 443        "update time",
 444        "critical suspend",
 445        "user standby",
 446        "user suspend",
 447        "system standby resume",
 448        "capabilities change"
 449};
 450#define NR_APM_EVENT_NAME       \
 451                (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
 452
 453typedef struct lookup_t {
 454        int     key;
 455        char *  msg;
 456} lookup_t;
 457
 458/*
 459 *      The BIOS returns a set of standard error codes in AX when the
 460 *      carry flag is set.
 461 */
 462 
 463static const lookup_t error_table[] = {
 464/* N/A  { APM_SUCCESS,          "Operation succeeded" }, */
 465        { APM_DISABLED,         "Power management disabled" },
 466        { APM_CONNECTED,        "Real mode interface already connected" },
 467        { APM_NOT_CONNECTED,    "Interface not connected" },
 468        { APM_16_CONNECTED,     "16 bit interface already connected" },
 469/* N/A  { APM_16_UNSUPPORTED,   "16 bit interface not supported" }, */
 470        { APM_32_CONNECTED,     "32 bit interface already connected" },
 471        { APM_32_UNSUPPORTED,   "32 bit interface not supported" },
 472        { APM_BAD_DEVICE,       "Unrecognized device ID" },
 473        { APM_BAD_PARAM,        "Parameter out of range" },
 474        { APM_NOT_ENGAGED,      "Interface not engaged" },
 475        { APM_BAD_FUNCTION,     "Function not supported" },
 476        { APM_RESUME_DISABLED,  "Resume timer disabled" },
 477        { APM_BAD_STATE,        "Unable to enter requested state" },
 478/* N/A  { APM_NO_EVENTS,        "No events pending" }, */
 479        { APM_NO_ERROR,         "BIOS did not set a return code" },
 480        { APM_NOT_PRESENT,      "No APM present" }
 481};
 482#define ERROR_COUNT     (sizeof(error_table)/sizeof(lookup_t))
 483
 484/**
 485 *      apm_error       -       display an APM error
 486 *      @str: information string
 487 *      @err: APM BIOS return code
 488 *
 489 *      Write a meaningful log entry to the kernel log in the event of
 490 *      an APM error.
 491 */
 492 
 493static void apm_error(char *str, int err)
 494{
 495        int     i;
 496
 497        for (i = 0; i < ERROR_COUNT; i++)
 498                if (error_table[i].key == err) break;
 499        if (i < ERROR_COUNT)
 500                printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
 501        else
 502                printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
 503                        str, err);
 504}
 505
 506/*
 507 * Lock APM functionality to physical CPU 0
 508 */
 509 
 510#ifdef CONFIG_SMP
 511
 512static cpumask_t apm_save_cpus(void)
 513{
 514        cpumask_t x = current->cpus_allowed;
 515        /* Some bioses don't like being called from CPU != 0 */
 516        set_cpus_allowed(current, cpumask_of_cpu(0));
 517        BUG_ON(smp_processor_id() != 0);
 518        return x;
 519}
 520
 521static inline void apm_restore_cpus(cpumask_t mask)
 522{
 523        set_cpus_allowed(current, mask);
 524}
 525
 526#else
 527
 528/*
 529 *      No CPU lockdown needed on a uniprocessor
 530 */
 531 
 532#define apm_save_cpus()         (current->cpus_allowed)
 533#define apm_restore_cpus(x)     (void)(x)
 534
 535#endif
 536
 537/*
 538 * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS and
 539 * apm_info.allow_ints, we are being really paranoid here!  Not only
 540 * are interrupts disabled, but all the segment registers (except SS)
 541 * are saved and zeroed this means that if the BIOS tries to reference
 542 * any data without explicitly loading the segment registers, the kernel
 543 * will fault immediately rather than have some unforeseen circumstances
 544 * for the rest of the kernel.  And it will be very obvious!  :-) Doing
 545 * this depends on CS referring to the same physical memory as DS so that
 546 * DS can be zeroed before the call. Unfortunately, we can't do anything
 547 * about the stack segment/pointer.  Also, we tell the compiler that
 548 * everything could change.
 549 *
 550 * Also, we KNOW that for the non error case of apm_bios_call, there
 551 * is no useful data returned in the low order 8 bits of eax.
 552 */
 553#define APM_DO_CLI      \
 554        if (apm_info.allow_ints) \
 555                local_irq_enable(); \
 556        else \
 557                local_irq_disable();
 558
 559#ifdef APM_ZERO_SEGS
 560#       define APM_DECL_SEGS \
 561                unsigned int saved_fs; unsigned int saved_gs;
 562#       define APM_DO_SAVE_SEGS \
 563                savesegment(fs, saved_fs); savesegment(gs, saved_gs)
 564#       define APM_DO_RESTORE_SEGS \
 565                loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)
 566#else
 567#       define APM_DECL_SEGS
 568#       define APM_DO_SAVE_SEGS
 569#       define APM_DO_RESTORE_SEGS
 570#endif
 571
 572/**
 573 *      apm_bios_call   -       Make an APM BIOS 32bit call
 574 *      @func: APM function to execute
 575 *      @ebx_in: EBX register for call entry
 576 *      @ecx_in: ECX register for call entry
 577 *      @eax: EAX register return
 578 *      @ebx: EBX register return
 579 *      @ecx: ECX register return
 580 *      @edx: EDX register return
 581 *      @esi: ESI register return
 582 *
 583 *      Make an APM call using the 32bit protected mode interface. The
 584 *      caller is responsible for knowing if APM BIOS is configured and
 585 *      enabled. This call can disable interrupts for a long period of
 586 *      time on some laptops.  The return value is in AH and the carry
 587 *      flag is loaded into AL.  If there is an error, then the error
 588 *      code is returned in AH (bits 8-15 of eax) and this function
 589 *      returns non-zero.
 590 */
 591 
 592static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in,
 593        u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
 594{
 595        APM_DECL_SEGS
 596        unsigned long           flags;
 597        cpumask_t               cpus;
 598        int                     cpu;
 599        struct desc_struct      save_desc_40;
 600
 601        cpus = apm_save_cpus();
 602        
 603        cpu = get_cpu();
 604        save_desc_40 = per_cpu(cpu_gdt_table, cpu)[0x40 / 8];
 605        per_cpu(cpu_gdt_table, cpu)[0x40 / 8] = bad_bios_desc;
 606
 607        local_save_flags(flags);
 608        APM_DO_CLI;
 609        APM_DO_SAVE_SEGS;
 610        apm_bios_call_asm(func, ebx_in, ecx_in, eax, ebx, ecx, edx, esi);
 611        APM_DO_RESTORE_SEGS;
 612        local_irq_restore(flags);
 613        per_cpu(cpu_gdt_table, cpu)[0x40 / 8] = save_desc_40;
 614        put_cpu();
 615        apm_restore_cpus(cpus);
 616        
 617        return *eax & 0xff;
 618}
 619
 620/**
 621 *      apm_bios_call_simple    -       make a simple APM BIOS 32bit call
 622 *      @func: APM function to invoke
 623 *      @ebx_in: EBX register value for BIOS call
 624 *      @ecx_in: ECX register value for BIOS call
 625 *      @eax: EAX register on return from the BIOS call
 626 *
 627 *      Make a BIOS call that does only returns one value, or just status.
 628 *      If there is an error, then the error code is returned in AH
 629 *      (bits 8-15 of eax) and this function returns non-zero. This is
 630 *      used for simpler BIOS operations. This call may hold interrupts
 631 *      off for a long time on some laptops.
 632 */
 633
 634static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax)
 635{
 636        u8                      error;
 637        APM_DECL_SEGS
 638        unsigned long           flags;
 639        cpumask_t               cpus;
 640        int                     cpu;
 641        struct desc_struct      save_desc_40;
 642
 643
 644        cpus = apm_save_cpus();
 645        
 646        cpu = get_cpu();
 647        save_desc_40 = per_cpu(cpu_gdt_table, cpu)[0x40 / 8];
 648        per_cpu(cpu_gdt_table, cpu)[0x40 / 8] = bad_bios_desc;
 649
 650        local_save_flags(flags);
 651        APM_DO_CLI;
 652        APM_DO_SAVE_SEGS;
 653        error = apm_bios_call_simple_asm(func, ebx_in, ecx_in, eax);
 654        APM_DO_RESTORE_SEGS;
 655        local_irq_restore(flags);
 656        __get_cpu_var(cpu_gdt_table)[0x40 / 8] = save_desc_40;
 657        put_cpu();
 658        apm_restore_cpus(cpus);
 659        return error;
 660}
 661
 662/**
 663 *      apm_driver_version      -       APM driver version
 664 *      @val:   loaded with the APM version on return
 665 *
 666 *      Retrieve the APM version supported by the BIOS. This is only
 667 *      supported for APM 1.1 or higher. An error indicates APM 1.0 is
 668 *      probably present.
 669 *
 670 *      On entry val should point to a value indicating the APM driver
 671 *      version with the high byte being the major and the low byte the
 672 *      minor number both in BCD
 673 *
 674 *      On return it will hold the BIOS revision supported in the
 675 *      same format.
 676 */
 677
 678static int apm_driver_version(u_short *val)
 679{
 680        u32     eax;
 681
 682        if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))
 683                return (eax >> 8) & 0xff;
 684        *val = eax;
 685        return APM_SUCCESS;
 686}
 687
 688/**
 689 *      apm_get_event   -       get an APM event from the BIOS
 690 *      @event: pointer to the event
 691 *      @info: point to the event information
 692 *
 693 *      The APM BIOS provides a polled information for event
 694 *      reporting. The BIOS expects to be polled at least every second
 695 *      when events are pending. When a message is found the caller should
 696 *      poll until no more messages are present.  However, this causes
 697 *      problems on some laptops where a suspend event notification is
 698 *      not cleared until it is acknowledged.
 699 *
 700 *      Additional information is returned in the info pointer, providing
 701 *      that APM 1.2 is in use. If no messges are pending the value 0x80
 702 *      is returned (No power management events pending).
 703 */
 704 
 705static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
 706{
 707        u32     eax;
 708        u32     ebx;
 709        u32     ecx;
 710        u32     dummy;
 711
 712        if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx,
 713                        &dummy, &dummy))
 714                return (eax >> 8) & 0xff;
 715        *event = ebx;
 716        if (apm_info.connection_version < 0x0102)
 717                *info = ~0; /* indicate info not valid */
 718        else
 719                *info = ecx;
 720        return APM_SUCCESS;
 721}
 722
 723/**
 724 *      set_power_state -       set the power management state
 725 *      @what: which items to transition
 726 *      @state: state to transition to
 727 *
 728 *      Request an APM change of state for one or more system devices. The
 729 *      processor state must be transitioned last of all. what holds the
 730 *      class of device in the upper byte and the device number (0xFF for
 731 *      all) for the object to be transitioned.
 732 *
 733 *      The state holds the state to transition to, which may in fact
 734 *      be an acceptance of a BIOS requested state change.
 735 */
 736 
 737static int set_power_state(u_short what, u_short state)
 738{
 739        u32     eax;
 740
 741        if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax))
 742                return (eax >> 8) & 0xff;
 743        return APM_SUCCESS;
 744}
 745
 746/**
 747 *      set_system_power_state - set system wide power state
 748 *      @state: which state to enter
 749 *
 750 *      Transition the entire system into a new APM power state.
 751 */
 752 
 753static int set_system_power_state(u_short state)
 754{
 755        return set_power_state(APM_DEVICE_ALL, state);
 756}
 757
 758/**
 759 *      apm_do_idle     -       perform power saving
 760 *
 761 *      This function notifies the BIOS that the processor is (in the view
 762 *      of the OS) idle. It returns -1 in the event that the BIOS refuses
 763 *      to handle the idle request. On a success the function returns 1
 764 *      if the BIOS did clock slowing or 0 otherwise.
 765 */
 766 
 767static int apm_do_idle(void)
 768{
 769        u32     eax;
 770
 771        if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax)) {
 772                static unsigned long t;
 773
 774                /* This always fails on some SMP boards running UP kernels.
 775                 * Only report the failure the first 5 times.
 776                 */
 777                if (++t < 5)
 778                {
 779                        printk(KERN_DEBUG "apm_do_idle failed (%d)\n",
 780                                        (eax >> 8) & 0xff);
 781                        t = jiffies;
 782                }
 783                return -1;
 784        }
 785        clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0;
 786        return clock_slowed;
 787}
 788
 789/**
 790 *      apm_do_busy     -       inform the BIOS the CPU is busy
 791 *
 792 *      Request that the BIOS brings the CPU back to full performance. 
 793 */
 794 
 795static void apm_do_busy(void)
 796{
 797        u32     dummy;
 798
 799        if (clock_slowed || ALWAYS_CALL_BUSY) {
 800                (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy);
 801                clock_slowed = 0;
 802        }
 803}
 804
 805/*
 806 * If no process has really been interested in
 807 * the CPU for some time, we want to call BIOS
 808 * power management - we probably want
 809 * to conserve power.
 810 */
 811#define IDLE_CALC_LIMIT   (HZ * 100)
 812#define IDLE_LEAKY_MAX    16
 813
 814static void (*original_pm_idle)(void);
 815
 816extern void default_idle(void);
 817
 818/**
 819 * apm_cpu_idle         -       cpu idling for APM capable Linux
 820 *
 821 * This is the idling function the kernel executes when APM is available. It 
 822 * tries to do BIOS powermanagement based on the average system idle time.
 823 * Furthermore it calls the system default idle routine.
 824 */
 825
 826static void apm_cpu_idle(void)
 827{
 828        static int use_apm_idle; /* = 0 */
 829        static unsigned int last_jiffies; /* = 0 */
 830        static unsigned int last_stime; /* = 0 */
 831
 832        int apm_idle_done = 0;
 833        unsigned int jiffies_since_last_check = jiffies - last_jiffies;
 834        unsigned int bucket;
 835
 836recalc:
 837        if (jiffies_since_last_check > IDLE_CALC_LIMIT) {
 838                use_apm_idle = 0;
 839                last_jiffies = jiffies;
 840                last_stime = current->stime;
 841        } else if (jiffies_since_last_check > idle_period) {
 842                unsigned int idle_percentage;
 843
 844                idle_percentage = current->stime - last_stime;
 845                idle_percentage *= 100;
 846                idle_percentage /= jiffies_since_last_check;
 847                use_apm_idle = (idle_percentage > idle_threshold);
 848                if (apm_info.forbid_idle)
 849                        use_apm_idle = 0;
 850                last_jiffies = jiffies;
 851                last_stime = current->stime;
 852        }
 853
 854        bucket = IDLE_LEAKY_MAX;
 855
 856        while (!need_resched()) {
 857                if (use_apm_idle) {
 858                        unsigned int t;
 859
 860                        t = jiffies;
 861                        switch (apm_do_idle()) {
 862                        case 0: apm_idle_done = 1;
 863                                if (t != jiffies) {
 864                                        if (bucket) {
 865                                                bucket = IDLE_LEAKY_MAX;
 866                                                continue;
 867                                        }
 868                                } else if (bucket) {
 869                                        bucket--;
 870                                        continue;
 871                                }
 872                                break;
 873                        case 1: apm_idle_done = 1;
 874                                break;
 875                        default: /* BIOS refused */
 876                                break;
 877                        }
 878                }
 879                if (original_pm_idle)
 880                        original_pm_idle();
 881                else
 882                        default_idle();
 883                jiffies_since_last_check = jiffies - last_jiffies;
 884                if (jiffies_since_last_check > idle_period)
 885                        goto recalc;
 886        }
 887
 888        if (apm_idle_done)
 889                apm_do_busy();
 890}
 891
 892/**
 893 *      apm_power_off   -       ask the BIOS to power off
 894 *
 895 *      Handle the power off sequence. This is the one piece of code we
 896 *      will execute even on SMP machines. In order to deal with BIOS
 897 *      bugs we support real mode APM BIOS power off calls. We also make
 898 *      the SMP call on CPU0 as some systems will only honour this call
 899 *      on their first cpu.
 900 */
 901 
 902static void apm_power_off(void)
 903{
 904        unsigned char   po_bios_call[] = {
 905                0xb8, 0x00, 0x10,       /* movw  $0x1000,ax  */
 906                0x8e, 0xd0,             /* movw  ax,ss       */
 907                0xbc, 0x00, 0xf0,       /* movw  $0xf000,sp  */
 908                0xb8, 0x07, 0x53,       /* movw  $0x5307,ax  */
 909                0xbb, 0x01, 0x00,       /* movw  $0x0001,bx  */
 910                0xb9, 0x03, 0x00,       /* movw  $0x0003,cx  */
 911                0xcd, 0x15              /* int   $0x15       */
 912        };
 913
 914        /*
 915         * This may be called on an SMP machine.
 916         */
 917#ifdef CONFIG_SMP
 918        /* Some bioses don't like being called from CPU != 0 */
 919        set_cpus_allowed(current, cpumask_of_cpu(0));
 920        BUG_ON(smp_processor_id() != 0);
 921#endif
 922        if (apm_info.realmode_power_off)
 923        {
 924                (void)apm_save_cpus();
 925                machine_real_restart(po_bios_call, sizeof(po_bios_call));
 926        }
 927        else
 928                (void) set_system_power_state(APM_STATE_OFF);
 929}
 930
 931#ifdef CONFIG_APM_DO_ENABLE
 932
 933/**
 934 *      apm_enable_power_management - enable BIOS APM power management
 935 *      @enable: enable yes/no
 936 *
 937 *      Enable or disable the APM BIOS power services. 
 938 */
 939 
 940static int apm_enable_power_management(int enable)
 941{
 942        u32     eax;
 943
 944        if ((enable == 0) && (apm_info.bios.flags & APM_BIOS_DISENGAGED))
 945                return APM_NOT_ENGAGED;
 946        if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,
 947                        enable, &eax))
 948                return (eax >> 8) & 0xff;
 949        if (enable)
 950                apm_info.bios.flags &= ~APM_BIOS_DISABLED;
 951        else
 952                apm_info.bios.flags |= APM_BIOS_DISABLED;
 953        return APM_SUCCESS;
 954}
 955#endif
 956
 957/**
 958 *      apm_get_power_status    -       get current power state
 959 *      @status: returned status
 960 *      @bat: battery info
 961 *      @life: estimated life
 962 *
 963 *      Obtain the current power status from the APM BIOS. We return a
 964 *      status which gives the rough battery status, and current power
 965 *      source. The bat value returned give an estimate as a percentage
 966 *      of life and a status value for the battery. The estimated life
 967 *      if reported is a lifetime in secodnds/minutes at current powwer
 968 *      consumption.
 969 */
 970 
 971static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
 972{
 973        u32     eax;
 974        u32     ebx;
 975        u32     ecx;
 976        u32     edx;
 977        u32     dummy;
 978
 979        if (apm_info.get_power_status_broken)
 980                return APM_32_UNSUPPORTED;
 981        if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0,
 982                        &eax, &ebx, &ecx, &edx, &dummy))
 983                return (eax >> 8) & 0xff;
 984        *status = ebx;
 985        *bat = ecx;
 986        if (apm_info.get_power_status_swabinminutes) {
 987                *life = swab16((u16)edx);
 988                *life |= 0x8000;
 989        } else
 990                *life = edx;
 991        return APM_SUCCESS;
 992}
 993
 994#if 0
 995static int apm_get_battery_status(u_short which, u_short *status,
 996                                  u_short *bat, u_short *life, u_short *nbat)
 997{
 998        u32     eax;
 999        u32     ebx;
1000        u32     ecx;
1001        u32     edx;
1002        u32     esi;
1003
1004        if (apm_info.connection_version < 0x0102) {
1005                /* pretend we only have one battery. */
1006                if (which != 1)
1007                        return APM_BAD_DEVICE;
1008                *nbat = 1;
1009                return apm_get_power_status(status, bat, life);
1010        }
1011
1012        if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax,
1013                        &ebx, &ecx, &edx, &esi))
1014                return (eax >> 8) & 0xff;
1015        *status = ebx;
1016        *bat = ecx;
1017        *life = edx;
1018        *nbat = esi;
1019        return APM_SUCCESS;
1020}
1021#endif
1022
1023/**
1024 *      apm_engage_power_management     -       enable PM on a device
1025 *      @device: identity of device
1026 *      @enable: on/off
1027 *
1028 *      Activate or deactive power management on either a specific device
1029 *      or the entire system (%APM_DEVICE_ALL).
1030 */
1031 
1032static int apm_engage_power_management(u_short device, int enable)
1033{
1034        u32     eax;
1035
1036        if ((enable == 0) && (device == APM_DEVICE_ALL)
1037            && (apm_info.bios.flags & APM_BIOS_DISABLED))
1038                return APM_DISABLED;
1039        if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax))
1040                return (eax >> 8) & 0xff;
1041        if (device == APM_DEVICE_ALL) {
1042                if (enable)
1043                        apm_info.bios.flags &= ~APM_BIOS_DISENGAGED;
1044                else
1045                        apm_info.bios.flags |= APM_BIOS_DISENGAGED;
1046        }
1047        return APM_SUCCESS;
1048}
1049
1050#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1051
1052/**
1053 *      apm_console_blank       -       blank the display
1054 *      @blank: on/off
1055 *
1056 *      Attempt to blank the console, firstly by blanking just video device
1057 *      zero, and if that fails (some BIOSes don't support it) then it blanks
1058 *      all video devices. Typically the BIOS will do laptop backlight and
1059 *      monitor powerdown for us.
1060 */
1061 
1062static int apm_console_blank(int blank)
1063{
1064        int     error;
1065        u_short state;
1066
1067        state = blank ? APM_STATE_STANDBY : APM_STATE_READY;
1068        /* Blank the first display device */
1069        error = set_power_state(0x100, state);
1070        if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) {
1071                /* try to blank them all instead */
1072                error = set_power_state(0x1ff, state);
1073                if ((error != APM_SUCCESS) && (error != APM_NO_ERROR))
1074                        /* try to blank device one instead */
1075                        error = set_power_state(0x101, state);
1076        }
1077        if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
1078                return 1;
1079        if (error == APM_NOT_ENGAGED) {
1080                static int tried;
1081                int eng_error;
1082                if (tried++ == 0) {
1083                        eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1);
1084                        if (eng_error) {
1085                                apm_error("set display", error);
1086                                apm_error("engage interface", eng_error);
1087                                return 0;
1088                        } else
1089                                return apm_console_blank(blank);
1090                }
1091        }
1092        apm_error("set display", error);
1093        return 0;
1094}
1095#endif
1096
1097static int queue_empty(struct apm_user *as)
1098{
1099        return as->event_head == as->event_tail;
1100}
1101
1102static apm_event_t get_queued_event(struct apm_user *as)
1103{
1104        as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
1105        return as->events[as->event_tail];
1106}
1107
1108static void queue_event(apm_event_t event, struct apm_user *sender)
1109{
1110        struct apm_user *       as;
1111
1112        spin_lock(&user_list_lock);
1113        if (user_list == NULL)
1114                goto out;
1115        for (as = user_list; as != NULL; as = as->next) {
1116                if ((as == sender) || (!as->reader))
1117                        continue;
1118                as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
1119                if (as->event_head == as->event_tail) {
1120                        static int notified;
1121
1122                        if (notified++ == 0)
1123                            printk(KERN_ERR "apm: an event queue overflowed\n");
1124                        as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
1125                }
1126                as->events[as->event_head] = event;
1127                if ((!as->suser) || (!as->writer))
1128                        continue;
1129                switch (event) {
1130                case APM_SYS_SUSPEND:
1131                case APM_USER_SUSPEND:
1132                        as->suspends_pending++;
1133                        suspends_pending++;
1134                        break;
1135
1136                case APM_SYS_STANDBY:
1137                case APM_USER_STANDBY:
1138                        as->standbys_pending++;
1139                        standbys_pending++;
1140                        break;
1141                }
1142        }
1143        wake_up_interruptible(&apm_waitqueue);
1144out:
1145        spin_unlock(&user_list_lock);
1146}
1147
1148static void set_time(void)
1149{
1150        if (got_clock_diff) {   /* Must know time zone in order to set clock */
1151                xtime.tv_sec = get_cmos_time() + clock_cmos_diff;
1152                xtime.tv_nsec = 0; 
1153        } 
1154}
1155
1156static void get_time_diff(void)
1157{
1158#ifndef CONFIG_APM_RTC_IS_GMT
1159        /*
1160         * Estimate time zone so that set_time can update the clock
1161         */
1162        clock_cmos_diff = -get_cmos_time();
1163        clock_cmos_diff += get_seconds();
1164        got_clock_diff = 1;
1165#endif
1166}
1167
1168static void reinit_timer(void)
1169{
1170#ifdef INIT_TIMER_AFTER_SUSPEND
1171        unsigned long   flags;
1172        extern spinlock_t i8253_lock;
1173
1174        spin_lock_irqsave(&i8253_lock, flags);
1175        /* set the clock to 100 Hz */
1176        outb_p(0x34, PIT_MODE);         /* binary, mode 2, LSB/MSB, ch 0 */
1177        udelay(10);
1178        outb_p(LATCH & 0xff, PIT_CH0);  /* LSB */
1179        udelay(10);
1180        outb(LATCH >> 8, PIT_CH0);      /* MSB */
1181        udelay(10);
1182        spin_unlock_irqrestore(&i8253_lock, flags);
1183#endif
1184}
1185
1186static int suspend(int vetoable)
1187{
1188        int             err;
1189        struct apm_user *as;
1190
1191        if (pm_send_all(PM_SUSPEND, (void *)3)) {
1192                /* Vetoed */
1193                if (vetoable) {
1194                        if (apm_info.connection_version > 0x100)
1195                                set_system_power_state(APM_STATE_REJECT);
1196                        err = -EBUSY;
1197                        ignore_sys_suspend = 0;
1198                        printk(KERN_WARNING "apm: suspend was vetoed.\n");
1199                        goto out;
1200                }
1201                printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
1202        }
1203
1204        device_suspend(PMSG_SUSPEND);
1205        device_power_down(PMSG_SUSPEND);
1206
1207        /* serialize with the timer interrupt */
1208        write_seqlock_irq(&xtime_lock);
1209
1210        /* protect against access to timer chip registers */
1211        spin_lock(&i8253_lock);
1212
1213        get_time_diff();
1214        /*
1215         * Irq spinlock must be dropped around set_system_power_state.
1216         * We'll undo any timer changes due to interrupts below.
1217         */
1218        spin_unlock(&i8253_lock);
1219        write_sequnlock_irq(&xtime_lock);
1220
1221        save_processor_state();
1222        err = set_system_power_state(APM_STATE_SUSPEND);
1223        restore_processor_state();
1224
1225        write_seqlock_irq(&xtime_lock);
1226        spin_lock(&i8253_lock);
1227        reinit_timer();
1228        set_time();
1229        ignore_normal_resume = 1;
1230
1231        spin_unlock(&i8253_lock);
1232        write_sequnlock_irq(&xtime_lock);
1233
1234        if (err == APM_NO_ERROR)
1235                err = APM_SUCCESS;
1236        if (err != APM_SUCCESS)
1237                apm_error("suspend", err);
1238        err = (err == APM_SUCCESS) ? 0 : -EIO;
1239        device_power_up();
1240        device_resume();
1241        pm_send_all(PM_RESUME, (void *)0);
1242        queue_event(APM_NORMAL_RESUME, NULL);
1243 out:
1244        spin_lock(&user_list_lock);
1245        for (as = user_list; as != NULL; as = as->next) {
1246                as->suspend_wait = 0;
1247                as->suspend_result = err;
1248        }
1249        spin_unlock(&user_list_lock);
1250        wake_up_interruptible(&apm_suspend_waitqueue);
1251        return err;
1252}
1253
1254static void standby(void)
1255{
1256        int     err;
1257
1258        device_power_down(PMSG_SUSPEND);
1259        /* serialize with the timer interrupt */
1260        write_seqlock_irq(&xtime_lock);
1261        /* If needed, notify drivers here */
1262        get_time_diff();
1263        write_sequnlock_irq(&xtime_lock);
1264
1265        err = set_system_power_state(APM_STATE_STANDBY);
1266        if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
1267                apm_error("standby", err);
1268        device_power_up();
1269}
1270
1271static apm_event_t get_event(void)
1272{
1273        int             error;
1274        apm_event_t     event;
1275        apm_eventinfo_t info;
1276
1277        static int notified;
1278
1279        /* we don't use the eventinfo */
1280        error = apm_get_event(&event, &info);
1281        if (error == APM_SUCCESS)
1282                return event;
1283
1284        if ((error != APM_NO_EVENTS) && (notified++ == 0))
1285                apm_error("get_event", error);
1286
1287        return 0;
1288}
1289
1290static void check_events(void)
1291{
1292        apm_event_t             event;
1293        static unsigned long    last_resume;
1294        static int              ignore_bounce;
1295
1296        while ((event = get_event()) != 0) {
1297                if (debug) {
1298                        if (event <= NR_APM_EVENT_NAME)
1299                                printk(KERN_DEBUG "apm: received %s notify\n",
1300                                       apm_event_name[event - 1]);
1301                        else
1302                                printk(KERN_DEBUG "apm: received unknown "
1303                                       "event 0x%02x\n", event);
1304                }
1305                if (ignore_bounce
1306                    && ((jiffies - last_resume) > bounce_interval))
1307                        ignore_bounce = 0;
1308
1309                switch (event) {
1310                case APM_SYS_STANDBY:
1311                case APM_USER_STANDBY:
1312                        queue_event(event, NULL);
1313                        if (standbys_pending <= 0)
1314                                standby();
1315                        break;
1316
1317                case APM_USER_SUSPEND:
1318#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
1319                        if (apm_info.connection_version > 0x100)
1320                                set_system_power_state(APM_STATE_REJECT);
1321                        break;
1322#endif
1323                case APM_SYS_SUSPEND:
1324                        if (ignore_bounce) {
1325                                if (apm_info.connection_version > 0x100)
1326                                        set_system_power_state(APM_STATE_REJECT);
1327                                break;
1328                        }
1329                        /*
1330                         * If we are already processing a SUSPEND,
1331                         * then further SUSPEND events from the BIOS
1332                         * will be ignored.  We also return here to
1333                         * cope with the fact that the Thinkpads keep
1334                         * sending a SUSPEND event until something else
1335                         * happens!
1336                         */
1337                        if (ignore_sys_suspend)
1338                                return;
1339                        ignore_sys_suspend = 1;
1340                        queue_event(event, NULL);
1341                        if (suspends_pending <= 0)
1342                                (void) suspend(1);
1343                        break;
1344
1345                case APM_NORMAL_RESUME:
1346                case APM_CRITICAL_RESUME:
1347                case APM_STANDBY_RESUME:
1348                        ignore_sys_suspend = 0;
1349                        last_resume = jiffies;
1350                        ignore_bounce = 1;
1351                        if ((event != APM_NORMAL_RESUME)
1352                            || (ignore_normal_resume == 0)) {
1353                                write_seqlock_irq(&xtime_lock);
1354                                set_time();
1355                                write_sequnlock_irq(&xtime_lock);
1356                                device_resume();
1357                                pm_send_all(PM_RESUME, (void *)0);
1358                                queue_event(event, NULL);
1359                        }
1360                        ignore_normal_resume = 0;
1361                        break;
1362
1363                case APM_CAPABILITY_CHANGE:
1364                case APM_LOW_BATTERY:
1365                case APM_POWER_STATUS_CHANGE:
1366                        queue_event(event, NULL);
1367                        /* If needed, notify drivers here */
1368                        break;
1369
1370                case APM_UPDATE_TIME:
1371                        write_seqlock_irq(&xtime_lock);
1372                        set_time();
1373                        write_sequnlock_irq(&xtime_lock);
1374                        break;
1375
1376                case APM_CRITICAL_SUSPEND:
1377                        /*
1378                         * We are not allowed to reject a critical suspend.
1379                         */
1380                        (void) suspend(0);
1381                        break;
1382                }
1383        }
1384}
1385
1386static void apm_event_handler(void)
1387{
1388        static int      pending_count = 4;
1389        int             err;
1390
1391        if ((standbys_pending > 0) || (suspends_pending > 0)) {
1392                if ((apm_info.connection_version > 0x100) &&
1393                                (pending_count-- <= 0)) {
1394                        pending_count = 4;
1395                        if (debug)
1396                                printk(KERN_DEBUG "apm: setting state busy\n");
1397                        err = set_system_power_state(APM_STATE_BUSY);
1398                        if (err)
1399                                apm_error("busy", err);
1400                }
1401        } else
1402                pending_count = 4;
1403        check_events();
1404}
1405
1406/*
1407 * This is the APM thread main loop.
1408 */
1409
1410static void apm_mainloop(void)
1411{
1412        DECLARE_WAITQUEUE(wait, current);
1413
1414        add_wait_queue(&apm_waitqueue, &wait);
1415        set_current_state(TASK_INTERRUPTIBLE);
1416        for (;;) {
1417                schedule_timeout(APM_CHECK_TIMEOUT);
1418                if (exit_kapmd)
1419                        break;
1420                /*
1421                 * Ok, check all events, check for idle (and mark us sleeping
1422                 * so as not to count towards the load average)..
1423                 */
1424                set_current_state(TASK_INTERRUPTIBLE);
1425                apm_event_handler();
1426        }
1427        remove_wait_queue(&apm_waitqueue, &wait);
1428}
1429
1430static int check_apm_user(struct apm_user *as, const char *func)
1431{
1432        if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
1433                printk(KERN_ERR "apm: %s passed bad filp\n", func);
1434                return 1;
1435        }
1436        return 0;
1437}
1438
1439static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
1440{
1441        struct apm_user *       as;
1442        int                     i;
1443        apm_event_t             event;
1444
1445        as = fp->private_data;
1446        if (check_apm_user(as, "read"))
1447                return -EIO;
1448        if ((int)count < sizeof(apm_event_t))
1449                return -EINVAL;
1450        if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))
1451                return -EAGAIN;
1452        wait_event_interruptible(apm_waitqueue, !queue_empty(as));
1453        i = count;
1454        while ((i >= sizeof(event)) && !queue_empty(as)) {
1455                event = get_queued_event(as);
1456                if (copy_to_user(buf, &event, sizeof(event))) {
1457                        if (i < count)
1458                                break;
1459                        return -EFAULT;
1460                }
1461                switch (event) {
1462                case APM_SYS_SUSPEND:
1463                case APM_USER_SUSPEND:
1464                        as->suspends_read++;
1465                        break;
1466
1467                case APM_SYS_STANDBY:
1468                case APM_USER_STANDBY:
1469                        as->standbys_read++;
1470                        break;
1471                }
1472                buf += sizeof(event);
1473                i -= sizeof(event);
1474        }
1475        if (i < count)
1476                return count - i;
1477        if (signal_pending(current))
1478                return -ERESTARTSYS;
1479        return 0;
1480}
1481
1482static unsigned int do_poll(struct file *fp, poll_table * wait)
1483{
1484        struct apm_user * as;
1485
1486        as = fp->private_data;
1487        if (check_apm_user(as, "poll"))
1488                return 0;
1489        poll_wait(fp, &apm_waitqueue, wait);
1490        if (!queue_empty(as))
1491                return POLLIN | POLLRDNORM;
1492        return 0;
1493}
1494
1495static int do_ioctl(struct inode * inode, struct file *filp,
1496                    u_int cmd, u_long arg)
1497{
1498        struct apm_user *       as;
1499
1500        as = filp->private_data;
1501        if (check_apm_user(as, "ioctl"))
1502                return -EIO;
1503        if ((!as->suser) || (!as->writer))
1504                return -EPERM;
1505        switch (cmd) {
1506        case APM_IOC_STANDBY:
1507                if (as->standbys_read > 0) {
1508                        as->standbys_read--;
1509                        as->standbys_pending--;
1510                        standbys_pending--;
1511                } else
1512                        queue_event(APM_USER_STANDBY, as);
1513                if (standbys_pending <= 0)
1514                        standby();
1515                break;
1516        case APM_IOC_SUSPEND:
1517                if (as->suspends_read > 0) {
1518                        as->suspends_read--;
1519                        as->suspends_pending--;
1520                        suspends_pending--;
1521                } else
1522                        queue_event(APM_USER_SUSPEND, as);
1523                if (suspends_pending <= 0) {
1524                        return suspend(1);
1525                } else {
1526                        as->suspend_wait = 1;
1527                        wait_event_interruptible(apm_suspend_waitqueue,
1528                                        as->suspend_wait == 0);
1529                        return as->suspend_result;
1530                }
1531                break;
1532        default:
1533                return -EINVAL;
1534        }
1535        return 0;
1536}
1537
1538static int do_release(struct inode * inode, struct file * filp)
1539{
1540        struct apm_user *       as;
1541
1542        as = filp->private_data;
1543        if (check_apm_user(as, "release"))
1544                return 0;
1545        filp->private_data = NULL;
1546        if (as->standbys_pending > 0) {
1547                standbys_pending -= as->standbys_pending;
1548                if (standbys_pending <= 0)
1549                        standby();
1550        }
1551        if (as->suspends_pending > 0) {
1552                suspends_pending -= as->suspends_pending;
1553                if (suspends_pending <= 0)
1554                        (void) suspend(1);
1555        }
1556        spin_lock(&user_list_lock);
1557        if (user_list == as)
1558                user_list = as->next;
1559        else {
1560                struct apm_user *       as1;
1561
1562                for (as1 = user_list;
1563                     (as1 != NULL) && (as1->next != as);
1564                     as1 = as1->next)
1565                        ;
1566                if (as1 == NULL)
1567                        printk(KERN_ERR "apm: filp not in user list\n");
1568                else
1569                        as1->next = as->next;
1570        }
1571        spin_unlock(&user_list_lock);
1572        kfree(as);
1573        return 0;
1574}
1575
1576static int do_open(struct inode * inode, struct file * filp)
1577{
1578        struct apm_user *       as;
1579
1580        as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
1581        if (as == NULL) {
1582                printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
1583                       sizeof(*as));
1584                return -ENOMEM;
1585        }
1586        as->magic = APM_BIOS_MAGIC;
1587        as->event_tail = as->event_head = 0;
1588        as->suspends_pending = as->standbys_pending = 0;
1589        as->suspends_read = as->standbys_read = 0;
1590        /*
1591         * XXX - this is a tiny bit broken, when we consider BSD
1592         * process accounting. If the device is opened by root, we
1593         * instantly flag that we used superuser privs. Who knows,
1594         * we might close the device immediately without doing a
1595         * privileged operation -- cevans
1596         */
1597        as->suser = capable(CAP_SYS_ADMIN);
1598        as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
1599        as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
1600        spin_lock(&user_list_lock);
1601        as->next = user_list;
1602        user_list = as;
1603        spin_unlock(&user_list_lock);
1604        filp->private_data = as;
1605        return 0;
1606}
1607
1608static int apm_get_info(char *buf, char **start, off_t fpos, int length)
1609{
1610        char *          p;
1611        unsigned short  bx;
1612        unsigned short  cx;
1613        unsigned short  dx;
1614        int             error;
1615        unsigned short  ac_line_status = 0xff;
1616        unsigned short  battery_status = 0xff;
1617        unsigned short  battery_flag   = 0xff;
1618        int             percentage     = -1;
1619        int             time_units     = -1;
1620        char            *units         = "?";
1621
1622        p = buf;
1623
1624        if ((num_online_cpus() == 1) &&
1625            !(error = apm_get_power_status(&bx, &cx, &dx))) {
1626                ac_line_status = (bx >> 8) & 0xff;
1627                battery_status = bx & 0xff;
1628                if ((cx & 0xff) != 0xff)
1629                        percentage = cx & 0xff;
1630
1631                if (apm_info.connection_version > 0x100) {
1632                        battery_flag = (cx >> 8) & 0xff;
1633                        if (dx != 0xffff) {
1634                                units = (dx & 0x8000) ? "min" : "sec";
1635                                time_units = dx & 0x7fff;
1636                        }
1637                }
1638        }
1639        /* Arguments, with symbols from linux/apm_bios.h.  Information is
1640           from the Get Power Status (0x0a) call unless otherwise noted.
1641
1642           0) Linux driver version (this will change if format changes)
1643           1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
1644           2) APM flags from APM Installation Check (0x00):
1645              bit 0: APM_16_BIT_SUPPORT
1646              bit 1: APM_32_BIT_SUPPORT
1647              bit 2: APM_IDLE_SLOWS_CLOCK
1648              bit 3: APM_BIOS_DISABLED
1649              bit 4: APM_BIOS_DISENGAGED
1650           3) AC line status
1651              0x00: Off-line
1652              0x01: On-line
1653              0x02: On backup power (BIOS >= 1.1 only)
1654              0xff: Unknown
1655           4) Battery status
1656              0x00: High
1657              0x01: Low
1658              0x02: Critical
1659              0x03: Charging
1660              0x04: Selected battery not present (BIOS >= 1.2 only)
1661              0xff: Unknown
1662           5) Battery flag
1663              bit 0: High
1664              bit 1: Low
1665              bit 2: Critical
1666              bit 3: Charging
1667              bit 7: No system battery
1668              0xff: Unknown
1669           6) Remaining battery life (percentage of charge):
1670              0-100: valid
1671              -1: Unknown
1672           7) Remaining battery life (time units):
1673              Number of remaining minutes or seconds
1674              -1: Unknown
1675           8) min = minutes; sec = seconds */
1676
1677        p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1678                     driver_version,
1679                     (apm_info.bios.version >> 8) & 0xff,
1680                     apm_info.bios.version & 0xff,
1681                     apm_info.bios.flags,
1682                     ac_line_status,
1683                     battery_status,
1684                     battery_flag,
1685                     percentage,
1686                     time_units,
1687                     units);
1688
1689        return p - buf;
1690}
1691
1692static int apm(void *unused)
1693{
1694        unsigned short  bx;
1695        unsigned short  cx;
1696        unsigned short  dx;
1697        int             error;
1698        char *          power_stat;
1699        char *          bat_stat;
1700
1701        kapmd_running = 1;
1702
1703        daemonize("kapmd");
1704
1705        current->flags |= PF_NOFREEZE;
1706
1707#ifdef CONFIG_SMP
1708        /* 2002/08/01 - WT
1709         * This is to avoid random crashes at boot time during initialization
1710         * on SMP systems in case of "apm=power-off" mode. Seen on ASUS A7M266D.
1711         * Some bioses don't like being called from CPU != 0.
1712         * Method suggested by Ingo Molnar.
1713         */
1714        set_cpus_allowed(current, cpumask_of_cpu(0));
1715        BUG_ON(smp_processor_id() != 0);
1716#endif
1717
1718        if (apm_info.connection_version == 0) {
1719                apm_info.connection_version = apm_info.bios.version;
1720                if (apm_info.connection_version > 0x100) {
1721                        /*
1722                         * We only support BIOSs up to version 1.2
1723                         */
1724                        if (apm_info.connection_version > 0x0102)
1725                                apm_info.connection_version = 0x0102;
1726                        error = apm_driver_version(&apm_info.connection_version);
1727                        if (error != APM_SUCCESS) {
1728                                apm_error("driver version", error);
1729                                /* Fall back to an APM 1.0 connection. */
1730                                apm_info.connection_version = 0x100;
1731                        }
1732                }
1733        }
1734
1735        if (debug)
1736                printk(KERN_INFO "apm: Connection version %d.%d\n",
1737                        (apm_info.connection_version >> 8) & 0xff,
1738                        apm_info.connection_version & 0xff);
1739
1740#ifdef CONFIG_APM_DO_ENABLE
1741        if (apm_info.bios.flags & APM_BIOS_DISABLED) {
1742                /*
1743                 * This call causes my NEC UltraLite Versa 33/C to hang if it
1744                 * is booted with PM disabled but not in the docking station.
1745                 * Unfortunate ...
1746                 */
1747                error = apm_enable_power_management(1);
1748                if (error) {
1749                        apm_error("enable power management", error);
1750                        return -1;
1751                }
1752        }
1753#endif
1754
1755        if ((apm_info.bios.flags & APM_BIOS_DISENGAGED)
1756            && (apm_info.connection_version > 0x0100)) {
1757                error = apm_engage_power_management(APM_DEVICE_ALL, 1);
1758                if (error) {
1759                        apm_error("engage power management", error);
1760                        return -1;
1761                }
1762        }
1763
1764        if (debug && (num_online_cpus() == 1 || smp )) {
1765                error = apm_get_power_status(&bx, &cx, &dx);
1766                if (error)
1767                        printk(KERN_INFO "apm: power status not available\n");
1768                else {
1769                        switch ((bx >> 8) & 0xff) {
1770                        case 0: power_stat = "off line"; break;
1771                        case 1: power_stat = "on line"; break;
1772                        case 2: power_stat = "on backup power"; break;
1773                        default: power_stat = "unknown"; break;
1774                        }
1775                        switch (bx & 0xff) {
1776                        case 0: bat_stat = "high"; break;
1777                        case 1: bat_stat = "low"; break;
1778                        case 2: bat_stat = "critical"; break;
1779                        case 3: bat_stat = "charging"; break;
1780                        default: bat_stat = "unknown"; break;
1781                        }
1782                        printk(KERN_INFO
1783                               "apm: AC %s, battery status %s, battery life ",
1784                               power_stat, bat_stat);
1785                        if ((cx & 0xff) == 0xff)
1786                                printk("unknown\n");
1787                        else
1788                                printk("%d%%\n", cx & 0xff);
1789                        if (apm_info.connection_version > 0x100) {
1790                                printk(KERN_INFO
1791                                       "apm: battery flag 0x%02x, battery life ",
1792                                       (cx >> 8) & 0xff);
1793                                if (dx == 0xffff)
1794                                        printk("unknown\n");
1795                                else
1796                                        printk("%d %s\n", dx & 0x7fff,
1797                                                (dx & 0x8000) ?
1798                                                "minutes" : "seconds");
1799                        }
1800                }
1801        }
1802
1803        /* Install our power off handler.. */
1804        if (power_off)
1805                pm_power_off = apm_power_off;
1806
1807        if (num_online_cpus() == 1 || smp) {
1808#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1809                console_blank_hook = apm_console_blank;
1810#endif
1811                apm_mainloop();
1812#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1813                console_blank_hook = NULL;
1814#endif
1815        }
1816        kapmd_running = 0;
1817
1818        return 0;
1819}
1820
1821#ifndef MODULE
1822static int __init apm_setup(char *str)
1823{
1824        int     invert;
1825
1826        while ((str != NULL) && (*str != '\0')) {
1827                if (strncmp(str, "off", 3) == 0)
1828                        apm_disabled = 1;
1829                if (strncmp(str, "on", 2) == 0)
1830                        apm_disabled = 0;
1831                if ((strncmp(str, "bounce-interval=", 16) == 0) ||
1832                    (strncmp(str, "bounce_interval=", 16) == 0))
1833                        bounce_interval = simple_strtol(str + 16, NULL, 0);
1834                if ((strncmp(str, "idle-threshold=", 15) == 0) ||
1835                    (strncmp(str, "idle_threshold=", 15) == 0))
1836                        idle_threshold = simple_strtol(str + 15, NULL, 0);
1837                if ((strncmp(str, "idle-period=", 12) == 0) ||
1838                    (strncmp(str, "idle_period=", 12) == 0))
1839                        idle_period = simple_strtol(str + 12, NULL, 0);
1840                invert = (strncmp(str, "no-", 3) == 0) ||
1841                        (strncmp(str, "no_", 3) == 0);
1842                if (invert)
1843                        str += 3;
1844                if (strncmp(str, "debug", 5) == 0)
1845                        debug = !invert;
1846                if ((strncmp(str, "power-off", 9) == 0) ||
1847                    (strncmp(str, "power_off", 9) == 0))
1848                        power_off = !invert;
1849                if (strncmp(str, "smp", 3) == 0)
1850                {
1851                        smp = !invert;
1852                        idle_threshold = 100;
1853                }
1854                if ((strncmp(str, "allow-ints", 10) == 0) ||
1855                    (strncmp(str, "allow_ints", 10) == 0))
1856                        apm_info.allow_ints = !invert;
1857                if ((strncmp(str, "broken-psr", 10) == 0) ||
1858                    (strncmp(str, "broken_psr", 10) == 0))
1859                        apm_info.get_power_status_broken = !invert;
1860                if ((strncmp(str, "realmode-power-off", 18) == 0) ||
1861                    (strncmp(str, "realmode_power_off", 18) == 0))
1862                        apm_info.realmode_power_off = !invert;
1863                str = strchr(str, ',');
1864                if (str != NULL)
1865                        str += strspn(str, ", \t");
1866        }
1867        return 1;
1868}
1869
1870__setup("apm=", apm_setup);
1871#endif
1872
1873static struct file_operations apm_bios_fops = {
1874        .owner          = THIS_MODULE,
1875        .read           = do_read,
1876        .poll           = do_poll,
1877        .ioctl          = do_ioctl,
1878        .open           = do_open,
1879        .release        = do_release,
1880};
1881
1882static struct miscdevice apm_device = {
1883        APM_MINOR_DEV,
1884        "apm_bios",
1885        &apm_bios_fops
1886};
1887
1888
1889/* Simple "print if true" callback */
1890static int __init print_if_true(struct dmi_system_id *d)
1891{
1892        printk("%s\n", d->ident);
1893        return 0;
1894}
1895
1896/*
1897 * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it was
1898 * disabled before the suspend. Linux used to get terribly confused by that.
1899 */
1900static int __init broken_ps2_resume(struct dmi_system_id *d)
1901{
1902        printk(KERN_INFO "%s machine detected. Mousepad Resume Bug workaround hopefully not needed.\n", d->ident);
1903        return 0;
1904}
1905
1906/* Some bioses have a broken protected mode poweroff and need to use realmode */
1907static int __init set_realmode_power_off(struct dmi_system_id *d)
1908{
1909        if (apm_info.realmode_power_off == 0) {
1910                apm_info.realmode_power_off = 1;
1911                printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
1912        }
1913        return 0;
1914}
1915
1916/* Some laptops require interrupts to be enabled during APM calls */
1917static int __init set_apm_ints(struct dmi_system_id *d)
1918{
1919        if (apm_info.allow_ints == 0) {
1920                apm_info.allow_ints = 1;
1921                printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
1922        }
1923        return 0;
1924}
1925
1926/* Some APM bioses corrupt memory or just plain do not work */
1927static int __init apm_is_horked(struct dmi_system_id *d)
1928{
1929        if (apm_info.disabled == 0) {
1930                apm_info.disabled = 1;
1931                printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
1932        }
1933        return 0;
1934}
1935
1936static int __init apm_is_horked_d850md(struct dmi_system_id *d)
1937{
1938        if (apm_info.disabled == 0) {
1939                apm_info.disabled = 1;
1940                printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
1941                printk(KERN_INFO "This bug is fixed in bios P15 which is available for \n");
1942                printk(KERN_INFO "download from support.intel.com \n");
1943        }
1944        return 0;
1945}
1946
1947/* Some APM bioses hang on APM idle calls */
1948static int __init apm_likes_to_melt(struct dmi_system_id *d)
1949{
1950        if (apm_info.forbid_idle == 0) {
1951                apm_info.forbid_idle = 1;
1952                printk(KERN_INFO "%s machine detected. Disabling APM idle calls.\n", d->ident);
1953        }
1954        return 0;
1955}
1956
1957/*
1958 *  Check for clue free BIOS implementations who use
1959 *  the following QA technique
1960 *
1961 *      [ Write BIOS Code ]<------
1962 *               |                ^
1963 *      < Does it Compile >----N--
1964 *               |Y               ^
1965 *      < Does it Boot Win98 >-N--
1966 *               |Y
1967 *           [Ship It]
1968 *
1969 *      Phoenix A04  08/24/2000 is known bad (Dell Inspiron 5000e)
1970 *      Phoenix A07  09/29/2000 is known good (Dell Inspiron 5000)
1971 */
1972static int __init broken_apm_power(struct dmi_system_id *d)
1973{
1974        apm_info.get_power_status_broken = 1;
1975        printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
1976        return 0;
1977}
1978
1979/*
1980 * This bios swaps the APM minute reporting bytes over (Many sony laptops
1981 * have this problem).
1982 */
1983static int __init swab_apm_power_in_minutes(struct dmi_system_id *d)
1984{
1985        apm_info.get_power_status_swabinminutes = 1;
1986        printk(KERN_WARNING "BIOS strings suggest APM reports battery life in minutes and wrong byte order.\n");
1987        return 0;
1988}
1989
1990static struct dmi_system_id __initdata apm_dmi_table[] = {
1991        {
1992                print_if_true,
1993                KERN_WARNING "IBM T23 - BIOS 1.03b+ and controller firmware 1.02+ may be needed for Linux APM.",
1994                {       DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
1995                        DMI_MATCH(DMI_BIOS_VERSION, "1AET38WW (1.01b)"), },
1996        },
1997        {       /* Handle problems with APM on the C600 */
1998                broken_ps2_resume, "Dell Latitude C600",
1999                {       DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
2000                        DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C600"), },
2001        },
2002        {       /* Allow interrupts during suspend on Dell Latitude laptops*/
2003                set_apm_ints, "Dell Latitude",
2004                {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2005                        DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C510"), }
2006        },
2007        {       /* APM crashes */
2008                apm_is_horked, "Dell Inspiron 2500",
2009                {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2010                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
2011                        DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
2012                        DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2013        },
2014        {       /* Allow interrupts during suspend on Dell Inspiron laptops*/
2015                set_apm_ints, "Dell Inspiron", {
2016                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2017                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"), },
2018        },
2019        {       /* Handle problems with APM on Inspiron 5000e */
2020                broken_apm_power, "Dell Inspiron 5000e",
2021                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2022                        DMI_MATCH(DMI_BIOS_VERSION, "A04"),
2023                        DMI_MATCH(DMI_BIOS_DATE, "08/24/2000"), },
2024        },
2025        {       /* Handle problems with APM on Inspiron 2500 */
2026                broken_apm_power, "Dell Inspiron 2500",
2027                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2028                        DMI_MATCH(DMI_BIOS_VERSION, "A12"),
2029                        DMI_MATCH(DMI_BIOS_DATE, "02/04/2002"), },
2030        },
2031        {       /* APM crashes */
2032                apm_is_horked, "Dell Dimension 4100",
2033                {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2034                        DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"),
2035                        DMI_MATCH(DMI_BIOS_VENDOR,"Intel Corp."),
2036                        DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2037        },
2038        {       /* Allow interrupts during suspend on Compaq Laptops*/
2039                set_apm_ints, "Compaq 12XL125",
2040                {       DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
2041                        DMI_MATCH(DMI_PRODUCT_NAME, "Compaq PC"),
2042                        DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2043                        DMI_MATCH(DMI_BIOS_VERSION,"4.06"), },
2044        },
2045        {       /* Allow interrupts during APM or the clock goes slow */
2046                set_apm_ints, "ASUSTeK",
2047                {       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
2048                        DMI_MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"), },
2049        },
2050        {       /* APM blows on shutdown */
2051                apm_is_horked, "ABIT KX7-333[R]",
2052                {       DMI_MATCH(DMI_BOARD_VENDOR, "ABIT"),
2053                        DMI_MATCH(DMI_BOARD_NAME, "VT8367-8233A (KX7-333[R])"), },
2054        },
2055        {       /* APM crashes */
2056                apm_is_horked, "Trigem Delhi3",
2057                {       DMI_MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
2058                        DMI_MATCH(DMI_PRODUCT_NAME, "Delhi3"), },
2059        },
2060        {       /* APM crashes */
2061                apm_is_horked, "Fujitsu-Siemens",
2062                {       DMI_MATCH(DMI_BIOS_VENDOR, "hoenix/FUJITSU SIEMENS"),
2063                        DMI_MATCH(DMI_BIOS_VERSION, "Version1.01"), },
2064        },
2065        {       /* APM crashes */
2066                apm_is_horked_d850md, "Intel D850MD",
2067                {       DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2068                        DMI_MATCH(DMI_BIOS_VERSION, "MV85010A.86A.0016.P07.0201251536"), },
2069        },
2070        {       /* APM crashes */
2071                apm_is_horked, "Intel D810EMO",
2072                {       DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2073                        DMI_MATCH(DMI_BIOS_VERSION, "MO81010A.86A.0008.P04.0004170800"), },
2074        },
2075        {       /* APM crashes */
2076                apm_is_horked, "Dell XPS-Z",
2077                {       DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2078                        DMI_MATCH(DMI_BIOS_VERSION, "A11"),
2079                        DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"), },
2080        },
2081        {       /* APM crashes */
2082                apm_is_horked, "Sharp PC-PJ/AX",
2083                {       DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
2084                        DMI_MATCH(DMI_PRODUCT_NAME, "PC-PJ/AX"),
2085                        DMI_MATCH(DMI_BIOS_VENDOR,"SystemSoft"),
2086                        DMI_MATCH(DMI_BIOS_VERSION,"Version R2.08"), },
2087        },
2088        {       /* APM crashes */
2089                apm_is_horked, "Dell Inspiron 2500",
2090                {       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2091                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
2092                        DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
2093                        DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2094        },
2095        {       /* APM idle hangs */
2096                apm_likes_to_melt, "Jabil AMD",
2097                {       DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
2098                        DMI_MATCH(DMI_BIOS_VERSION, "0AASNP06"), },
2099        },
2100        {       /* APM idle hangs */
2101                apm_likes_to_melt, "AMI Bios",
2102                {       DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
2103                        DMI_MATCH(DMI_BIOS_VERSION, "0AASNP05"), },
2104        },
2105        {       /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */
2106                swab_apm_power_in_minutes, "Sony VAIO",
2107                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2108                        DMI_MATCH(DMI_BIOS_VERSION, "R0206H"),
2109                        DMI_MATCH(DMI_BIOS_DATE, "08/23/99"), },
2110        },
2111        {       /* Handle problems with APM on Sony Vaio PCG-N505VX */
2112                swab_apm_power_in_minutes, "Sony VAIO",
2113                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2114                        DMI_MATCH(DMI_BIOS_VERSION, "W2K06H0"),
2115                        DMI_MATCH(DMI_BIOS_DATE, "02/03/00"), },
2116        },
2117        {       /* Handle problems with APM on Sony Vaio PCG-XG29 */
2118                swab_apm_power_in_minutes, "Sony VAIO",
2119                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2120                        DMI_MATCH(DMI_BIOS_VERSION, "R0117A0"),
2121                        DMI_MATCH(DMI_BIOS_DATE, "04/25/00"), },
2122        },
2123        {       /* Handle problems with APM on Sony Vaio PCG-Z600NE */
2124                swab_apm_power_in_minutes, "Sony VAIO",
2125                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2126                        DMI_MATCH(DMI_BIOS_VERSION, "R0121Z1"),
2127                        DMI_MATCH(DMI_BIOS_DATE, "05/11/00"), },
2128        },
2129        {       /* Handle problems with APM on Sony Vaio PCG-Z600NE */
2130                swab_apm_power_in_minutes, "Sony VAIO",
2131                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2132                        DMI_MATCH(DMI_BIOS_VERSION, "WME01Z1"),
2133                        DMI_MATCH(DMI_BIOS_DATE, "08/11/00"), },
2134        },
2135        {       /* Handle problems with APM on Sony Vaio PCG-Z600LEK(DE) */
2136                swab_apm_power_in_minutes, "Sony VAIO",
2137                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2138                        DMI_MATCH(DMI_BIOS_VERSION, "R0206Z3"),
2139                        DMI_MATCH(DMI_BIOS_DATE, "12/25/00"), },
2140        },
2141        {       /* Handle problems with APM on Sony Vaio PCG-Z505LS */
2142                swab_apm_power_in_minutes, "Sony VAIO",
2143                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2144                        DMI_MATCH(DMI_BIOS_VERSION, "R0203D0"),
2145                        DMI_MATCH(DMI_BIOS_DATE, "05/12/00"), },
2146        },
2147        {       /* Handle problems with APM on Sony Vaio PCG-Z505LS */
2148                swab_apm_power_in_minutes, "Sony VAIO",
2149                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2150                        DMI_MATCH(DMI_BIOS_VERSION, "R0203Z3"),
2151                        DMI_MATCH(DMI_BIOS_DATE, "08/25/00"), },
2152        },
2153        {       /* Handle problems with APM on Sony Vaio PCG-Z505LS (with updated BIOS) */
2154                swab_apm_power_in_minutes, "Sony VAIO",
2155                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2156                        DMI_MATCH(DMI_BIOS_VERSION, "R0209Z3"),
2157                        DMI_MATCH(DMI_BIOS_DATE, "05/12/01"), },
2158        },
2159        {       /* Handle problems with APM on Sony Vaio PCG-F104K */
2160                swab_apm_power_in_minutes, "Sony VAIO",
2161                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2162                        DMI_MATCH(DMI_BIOS_VERSION, "R0204K2"),
2163                        DMI_MATCH(DMI_BIOS_DATE, "08/28/00"), },
2164        },
2165
2166        {       /* Handle problems with APM on Sony Vaio PCG-C1VN/C1VE */
2167                swab_apm_power_in_minutes, "Sony VAIO",
2168                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2169                        DMI_MATCH(DMI_BIOS_VERSION, "R0208P1"),
2170                        DMI_MATCH(DMI_BIOS_DATE, "11/09/00"), },
2171        },
2172        {       /* Handle problems with APM on Sony Vaio PCG-C1VE */
2173                swab_apm_power_in_minutes, "Sony VAIO",
2174                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2175                        DMI_MATCH(DMI_BIOS_VERSION, "R0204P1"),
2176                        DMI_MATCH(DMI_BIOS_DATE, "09/12/00"), },
2177        },
2178        {       /* Handle problems with APM on Sony Vaio PCG-C1VE */
2179                swab_apm_power_in_minutes, "Sony VAIO",
2180                {       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2181                        DMI_MATCH(DMI_BIOS_VERSION, "WXPO1Z3"),
2182                        DMI_MATCH(DMI_BIOS_DATE, "10/26/01"), },
2183        },
2184        {       /* broken PM poweroff bios */
2185                set_realmode_power_off, "Award Software v4.60 PGMA",
2186                {       DMI_MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
2187                        DMI_MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
2188                        DMI_MATCH(DMI_BIOS_DATE, "134526184"), },
2189        },
2190
2191        /* Generic per vendor APM settings  */
2192
2193        {       /* Allow interrupts during suspend on IBM laptops */
2194                set_apm_ints, "IBM",
2195                {       DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
2196        },
2197
2198        { }
2199};
2200
2201/*
2202 * Just start the APM thread. We do NOT want to do APM BIOS
2203 * calls from anything but the APM thread, if for no other reason
2204 * than the fact that we don't trust the APM BIOS. This way,
2205 * most common APM BIOS problems that lead to protection errors
2206 * etc will have at least some level of being contained...
2207 *
2208 * In short, if something bad happens, at least we have a choice
2209 * of just killing the apm thread..
2210 */
2211static int __init apm_init(void)
2212{
2213        struct proc_dir_entry *apm_proc;
2214        int ret;
2215        int i;
2216
2217        dmi_check_system(apm_dmi_table);
2218
2219        if (apm_info.bios.version == 0) {
2220                printk(KERN_INFO "apm: BIOS not found.\n");
2221                return -ENODEV;
2222        }
2223        printk(KERN_INFO
2224                "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
2225                ((apm_info.bios.version >> 8) & 0xff),
2226                (apm_info.bios.version & 0xff),
2227                apm_info.bios.flags,
2228                driver_version);
2229        if ((apm_info.bios.flags & APM_32_BIT_SUPPORT) == 0) {
2230                printk(KERN_INFO "apm: no 32 bit BIOS support\n");
2231                return -ENODEV;
2232        }
2233
2234        if (allow_ints)
2235                apm_info.allow_ints = 1;
2236        if (broken_psr)
2237                apm_info.get_power_status_broken = 1;
2238        if (realmode_power_off)
2239                apm_info.realmode_power_off = 1;
2240        /* User can override, but default is to trust DMI */
2241        if (apm_disabled != -1)
2242                apm_info.disabled = apm_disabled;
2243
2244        /*
2245         * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
2246         * but is reportedly a 1.0 BIOS.
2247         */
2248        if (apm_info.bios.version == 0x001)
2249                apm_info.bios.version = 0x100;
2250
2251        /* BIOS < 1.2 doesn't set cseg_16_len */
2252        if (apm_info.bios.version < 0x102)
2253                apm_info.bios.cseg_16_len = 0; /* 64k */
2254
2255        if (debug) {
2256                printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x",
2257                        apm_info.bios.cseg, apm_info.bios.offset,
2258                        apm_info.bios.cseg_16, apm_info.bios.dseg);
2259                if (apm_info.bios.version > 0x100)
2260                        printk(" cseg len %x, dseg len %x",
2261                                apm_info.bios.cseg_len,
2262                                apm_info.bios.dseg_len);
2263                if (apm_info.bios.version > 0x101)
2264                        printk(" cseg16 len %x", apm_info.bios.cseg_16_len);
2265                printk("\n");
2266        }
2267
2268        if (apm_info.disabled) {
2269                printk(KERN_NOTICE "apm: disabled on user request.\n");
2270                return -ENODEV;
2271        }
2272        if ((num_online_cpus() > 1) && !power_off && !smp) {
2273                printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
2274                apm_info.disabled = 1;
2275                return -ENODEV;
2276        }
2277        if (PM_IS_ACTIVE()) {
2278                printk(KERN_NOTICE "apm: overridden by ACPI.\n");
2279                apm_info.disabled = 1;
2280                return -ENODEV;
2281        }
2282        pm_active = 1;
2283
2284        /*
2285         * Set up a segment that references the real mode segment 0x40
2286         * that extends up to the end of page zero (that we have reserved).
2287         * This is for buggy BIOS's that refer to (real mode) segment 0x40
2288         * even though they are called in protected mode.
2289         */
2290        set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
2291        _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
2292
2293        apm_bios_entry.offset = apm_info.bios.offset;
2294        apm_bios_entry.segment = APM_CS;
2295
2296        for (i = 0; i < NR_CPUS; i++) {
2297                set_base(per_cpu(cpu_gdt_table, i)[APM_CS >> 3],
2298                         __va((unsigned long)apm_info.bios.cseg << 4));
2299                set_base(per_cpu(cpu_gdt_table, i)[APM_CS_16 >> 3],
2300                         __va((unsigned long)apm_info.bios.cseg_16 << 4));
2301                set_base(per_cpu(cpu_gdt_table, i)[APM_DS >> 3],
2302                         __va((unsigned long)apm_info.bios.dseg << 4));
2303#ifndef APM_RELAX_SEGMENTS
2304                if (apm_info.bios.version == 0x100) {
2305#endif
2306                        /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
2307                        _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_CS >> 3], 64 * 1024 - 1);
2308                        /* For some unknown machine. */
2309                        _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_CS_16 >> 3], 64 * 1024 - 1);
2310                        /* For the DEC Hinote Ultra CT475 (and others?) */
2311                        _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_DS >> 3], 64 * 1024 - 1);
2312#ifndef APM_RELAX_SEGMENTS
2313                } else {
2314                        _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_CS >> 3],
2315                                (apm_info.bios.cseg_len - 1) & 0xffff);
2316                        _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_CS_16 >> 3],
2317                                (apm_info.bios.cseg_16_len - 1) & 0xffff);
2318                        _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_DS >> 3],
2319                                (apm_info.bios.dseg_len - 1) & 0xffff);
2320                      /* workaround for broken BIOSes */
2321                        if (apm_info.bios.cseg_len <= apm_info.bios.offset)
2322                                _set_limit((char *)&per_cpu(cpu_gdt_table, i)[APM_CS >> 3], 64 * 1024 -1);
2323                       if (apm_info.bios.dseg_len <= 0x40) { /* 0x40 * 4kB == 64kB */
2324                                /* for the BIOS that assumes granularity = 1 */
2325                                per_cpu(cpu_gdt_table, i)[APM_DS >> 3].b |= 0x800000;
2326                                printk(KERN_NOTICE "apm: we set the granularity of dseg.\n");
2327                        }
2328                }
2329#endif
2330        }
2331
2332        apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info);
2333        if (apm_proc)
2334                apm_proc->owner = THIS_MODULE;
2335
2336        ret = kernel_thread(apm, NULL, CLONE_KERNEL | SIGCHLD);
2337        if (ret < 0) {
2338                printk(KERN_ERR "apm: disabled - Unable to start kernel thread.\n");
2339                return -ENOMEM;
2340        }
2341
2342        if (num_online_cpus() > 1 && !smp ) {
2343                printk(KERN_NOTICE
2344                   "apm: disabled - APM is not SMP safe (power off active).\n");
2345                return 0;
2346        }
2347
2348        misc_register(&apm_device);
2349
2350        if (HZ != 100)
2351                idle_period = (idle_period * HZ) / 100;
2352        if (idle_threshold < 100) {
2353                original_pm_idle = pm_idle;
2354                pm_idle  = apm_cpu_idle;
2355                set_pm_idle = 1;
2356        }
2357
2358        return 0;
2359}
2360
2361static void __exit apm_exit(void)
2362{
2363        int     error;
2364
2365        if (set_pm_idle) {
2366                pm_idle = original_pm_idle;
2367                /*
2368                 * We are about to unload the current idle thread pm callback
2369                 * (pm_idle), Wait for all processors to update cached/local
2370                 * copies of pm_idle before proceeding.
2371                 */
2372                cpu_idle_wait();
2373        }
2374        if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)
2375            && (apm_info.connection_version > 0x0100)) {
2376                error = apm_engage_power_management(APM_DEVICE_ALL, 0);
2377                if (error)
2378                        apm_error("disengage power management", error);
2379        }
2380        misc_deregister(&apm_device);
2381        remove_proc_entry("apm", NULL);
2382        if (power_off)
2383                pm_power_off = NULL;
2384        exit_kapmd = 1;
2385        while (kapmd_running)
2386                schedule();
2387        pm_active = 0;
2388}
2389
2390module_init(apm_init);
2391module_exit(apm_exit);
2392
2393MODULE_AUTHOR("Stephen Rothwell");
2394MODULE_DESCRIPTION("Advanced Power Management");
2395MODULE_LICENSE("GPL");
2396module_param(debug, bool, 0644);
2397MODULE_PARM_DESC(debug, "Enable debug mode");
2398module_param(power_off, bool, 0444);
2399MODULE_PARM_DESC(power_off, "Enable power off");
2400module_param(bounce_interval, int, 0444);
2401MODULE_PARM_DESC(bounce_interval,
2402                "Set the number of ticks to ignore suspend bounces");
2403module_param(allow_ints, bool, 0444);
2404MODULE_PARM_DESC(allow_ints, "Allow interrupts during BIOS calls");
2405module_param(broken_psr, bool, 0444);
2406MODULE_PARM_DESC(broken_psr, "BIOS has a broken GetPowerStatus call");
2407module_param(realmode_power_off, bool, 0444);
2408MODULE_PARM_DESC(realmode_power_off,
2409                "Switch to real mode before powering off");
2410module_param(idle_threshold, int, 0444);
2411MODULE_PARM_DESC(idle_threshold,
2412        "System idle percentage above which to make APM BIOS idle calls");
2413module_param(idle_period, int, 0444);
2414MODULE_PARM_DESC(idle_period,
2415        "Period (in sec/100) over which to caculate the idle percentage");
2416module_param(smp, bool, 0444);
2417MODULE_PARM_DESC(smp,
2418        "Set this to enable APM use on an SMP platform. Use with caution on older systems");
2419MODULE_ALIAS_MISCDEV(APM_MINOR_DEV);
2420
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.