root/drivers/char/apm_bios.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. apm_driver_version
  2. apm_get_event
  3. apm_set_power_state
  4. apm_set_display_power_state
  5. apm_enable_power_management
  6. apm_get_power_status
  7. apm_engage_power_management
  8. apm_error
  9. apm_display_blank
  10. apm_display_unblank
  11. apm_register_callback
  12. apm_unregister_callback
  13. queue_empty
  14. get_queued_event
  15. queue_event
  16. set_time
  17. suspend
  18. standby
  19. get_event
  20. send_event
  21. check_events
  22. do_apm_timer
  23. apm_do_idle
  24. apm_do_busy
  25. check_apm_bios_struct
  26. do_read
  27. do_select
  28. do_ioctl
  29. do_release
  30. do_open
  31. apm_get_info
  32. apm_bios_init

   1 /* -*- linux-c -*-
   2  * APM BIOS driver for Linux
   3  * Copyright 1994, 1995, 1996 Stephen Rothwell
   4  *                           (Stephen.Rothwell@canb.auug.org.au)
   5  *
   6  * This program is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License as published by the
   8  * Free Software Foundation; either version 2, or (at your option) any
   9  * later version.
  10  *
  11  * This program is distributed in the hope that it will be useful, but
  12  * WITHOUT ANY WARRANTY; without even the implied warranty of
  13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14  * General Public License for more details.
  15  *
  16  * $Id: apm_bios.c,v 0.22 1995/03/09 14:12:02 sfr Exp $
  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 (Stephen.Rothwell@canb.auug.org.au)
  27  *    Version 1.0
  28  *
  29  * History:
  30  *    0.6b: first version in official kernel, Linux 1.3.46
  31  *    0.7: changed /proc/apm format, Linux 1.3.58
  32  *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
  33  *    0.9: only call bios if bios is present, Linux 1.3.72
  34  *    1.0: use fixed device number, consolidate /proc/apm into
  35  *         this file, Linux 1.3.85
  36  *
  37  * Reference:
  38  *
  39  *   Intel Corporation, Microsoft Corporation. Advanced Power Management
  40  *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
  41  *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
  42  *
  43  * [This document is available free from Intel by calling 800.628.8686 (fax
  44  * 916.356.6100) or 800.548.4725; or via anonymous ftp from
  45  * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc.  It is also
  46  * available from Microsoft by calling 206.882.8080.]
  47  *
  48  */
  49 
  50 #include <linux/config.h>
  51 #include <linux/module.h>
  52 
  53 #include <asm/system.h>
  54 #include <asm/segment.h>
  55 
  56 #include <linux/types.h>
  57 #include <linux/stddef.h>
  58 #include <linux/timer.h>
  59 #include <linux/fcntl.h>
  60 #include <linux/malloc.h>
  61 #include <linux/linkage.h>
  62 #ifdef CONFIG_PROC_FS
  63 #include <linux/stat.h>
  64 #include <linux/proc_fs.h>
  65 #endif
  66 #include <linux/miscdevice.h>
  67 #include <linux/apm_bios.h>
  68 
  69 static struct symbol_table      apm_syms = {
  70 #include <linux/symtab_begin.h>
  71         X(apm_register_callback),
  72         X(apm_unregister_callback),
  73 #include <linux/symtab_end.h>
  74 };
  75 
  76 extern unsigned long get_cmos_time(void);
  77 
  78 /*
  79  * The apm_bios device is one of the misc char devices.
  80  * This is its minor number.
  81  */
  82 #define APM_MINOR_DEV   134
  83 
  84 /* Configurable options:
  85  *  
  86  * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
  87  * This is necessary on the NEC Versa M series, which generates these when
  88  * resuming from SYSTEM SUSPEND.  However, enabling this on other laptops
  89  * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
  90  * USER SUSPEND is ignored -- this may prevent the APM driver from updating
  91  * the system time on a RESUME.
  92  *
  93  * CONFIG_APM_DO_ENABLE: enable APM features at boot time.  From page 36 of
  94  * the specification: "When disabled, the APM BIOS does not automatically
  95  * power manage devices, enter the Standby State, enter the Suspend State,
  96  * or take power saving steps in response to CPU Idle calls."  This driver
  97  * will make CPU Idle calls when Linux is idle (unless this feature is
  98  * turned off -- see below).  This should always save battery power, but
  99  * more complicated APM features will be dependent on your BIOS
 100  * implementation.  You may need to turn this option off if your computer
 101  * hangs at boot time when using APM support, or if it beeps continuously
 102  * instead of suspending.  Turn this off if you have a NEC UltraLite Versa
 103  * 33/C or a Toshiba T400CDT.  This is off by default since most machines
 104  * do fine without this feature.
 105  *
 106  * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
 107  * idle loop.  On some machines, this can activate improved power savings,
 108  * such as a slowed CPU clock rate, when the machine is idle.  These idle
 109  * call is made after the idle loop has run for some length of time (e.g.,
 110  * 333 mS).  On some machines, this will cause a hang at boot time or
 111  * whenever the CPU becomes idle.
 112  *
 113  * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM.  Some
 114  * laptops can use this to turn of the LCD backlight when the VC screen
 115  * blanker blanks the screen.  Note that this is only used by the VC screen
 116  * blanker, and probably won't turn off the backlight when using X11.
 117  *
 118  * If you are debugging the APM support for your laptop, note that code for
 119  * all of these options is contained in this file, so you can #define or
 120  * #undef these on the next line to avoid recompiling the whole kernel.
 121  *
 122  */
 123 
 124 /* KNOWN PROBLEM MACHINES:
 125  *
 126  * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
 127  *                         [Confirmed by TI representative]
 128  * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
 129  *                    [Confirmed by BIOS disassembly]
 130  * P: Toshiba 1950S: battery life information only gets updated after resume
 131  *
 132  * Legend: U = unusable with APM patches
 133  *         P = partially usable with APM patches
 134  */
 135 
 136 /*
 137  * Define to have debug messages.
 138  */
 139 #undef APM_DEBUG
 140 
 141 /*
 142  * Define to always call the APM BIOS busy routine even if the clock was
 143  * not slowed by the idle routine.
 144  */
 145 #define ALWAYS_CALL_BUSY
 146 
 147 /*
 148  * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
 149  * should turn interrupts on before it does a 'hlt').
 150  */
 151 #define APM_NOINTS
 152 
 153 /*
 154  * Define to make the APM BIOS calls zero all data segment registers (do
 155  * that if an incorrect BIOS implementation will cause a kernel panic if it
 156  * tries to write to arbitrary memory).
 157  */
 158 #define APM_ZERO_SEGS
 159 
 160 /*
 161  * Define to make all set_limit calls use 64k limits.  The APM 1.1 BIOS is
 162  * supposed to provide limit information that it recognizes.  Many machines
 163  * do this correctly, but many others do not restrict themselves to their
 164  * claimed limit.  When this happens, they will cause a segmentation
 165  * violation in the kernel at boot time.  Most BIOS's, however, will
 166  * respect a 64k limit, so we use that.  If you want to be pedantic and
 167  * hold your BIOS to its claims, then undefine this.
 168  */
 169 #define APM_RELAX_SEGMENTS
 170 
 171 /*
 172  * Need to poll the APM BIOS every second
 173  */
 174 #define APM_CHECK_TIMEOUT       (HZ)
 175 
 176 /*
 177  * These are the actual BIOS calls in assembler.  Depending on
 178  * APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here!  Not
 179  * only are interrupts disabled, but all the segment registers (except SS)
 180  * are saved and zeroed this means that if the BIOS tries to reference any
 181  * data without explicitly loading the segment registers, the kernel will
 182  * fault immediately rather than have some unforeseen circumstances for the
 183  * rest of the kernel.  And it will be very obvious!  :-) Doing this
 184  * depends on CS referring to the same physical memory as DS so that DS can
 185  * be zeroed before the call. Unfortunately, we can't do anything about the
 186  * stack segment/pointer.  Also, we tell the compiler that everything could
 187  * change.
 188  */
 189 #ifdef APM_NOINTS
 190 #       define APM_DO_CLI       "cli\n\t"
 191 #else
 192 #       define APM_DO_CLI
 193 #endif
 194 #ifdef APM_ZERO_SEGS
 195 #       define APM_DO_ZERO_SEGS \
 196                 "pushl %%ds\n\t" \
 197                 "pushl %%es\n\t" \
 198                 "pushl %%fs\n\t" \
 199                 "pushl %%gs\n\t" \
 200                 "xorl %%edx, %%edx\n\t" \
 201                 "mov %%dx, %%ds\n\t" \
 202                 "mov %%dx, %%es\n\t" \
 203                 "mov %%dx, %%fs\n\t" \
 204                 "mov %%dx, %%gs\n\t"
 205 #       define APM_DO_RESTORE_SEGS      \
 206                 "popl %%gs\n\t" \
 207                 "popl %%fs\n\t" \
 208                 "popl %%es\n\t" \
 209                 "popl %%ds\n\t"
 210 #else
 211 #       define APM_DO_ZERO_SEGS
 212 #       define APM_DO_RESTORE_SEGS
 213 #endif
 214 
 215 #define APM_BIOS_CALL(error_reg) \
 216         __asm__ __volatile__( \
 217                 APM_DO_ZERO_SEGS \
 218                 "pushfl\n\t" \
 219                 APM_DO_CLI \
 220                 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
 221                 "setc %%" # error_reg "\n\t" \
 222                 "popfl\n\t" \
 223                 APM_DO_RESTORE_SEGS
 224 #define APM_BIOS_CALL_END \
 225                 : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
 226 
 227 #ifdef CONFIG_APM_CPU_IDLE
 228 #define APM_SET_CPU_IDLE(error) \
 229         APM_BIOS_CALL(al) \
 230         : "=a" (error) \
 231         : "a" (0x5305) \
 232         APM_BIOS_CALL_END
 233 #endif
 234 
 235 #define APM_SET_CPU_BUSY(error) \
 236         APM_BIOS_CALL(al) \
 237         : "=a" (error) \
 238         : "a" (0x5306) \
 239         APM_BIOS_CALL_END
 240 
 241 #define APM_SET_POWER_STATE(state, error) \
 242         APM_BIOS_CALL(al) \
 243         : "=a" (error) \
 244         : "a" (0x5307), "b" (0x0001), "c" (state) \
 245         APM_BIOS_CALL_END
 246 
 247 #ifdef CONFIG_APM_DISPLAY_BLANK
 248 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
 249         APM_BIOS_CALL(al) \
 250         : "=a" (error) \
 251         : "a" (0x5307), "b" (0x01ff), "c" (state) \
 252         APM_BIOS_CALL_END
 253 #endif
 254 
 255 #ifdef CONFIG_APM_DO_ENABLE
 256 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
 257         APM_BIOS_CALL(al) \
 258         : "=a" (error) \
 259         : "a" (0x5308), "b" (device), "c" (1) \
 260         APM_BIOS_CALL_END
 261 #endif
 262 
 263 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
 264         APM_BIOS_CALL(al) \
 265         : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
 266         : "a" (0x530a), "b" (1) \
 267         APM_BIOS_CALL_END
 268 
 269 #define APM_GET_EVENT(event, error)     \
 270         APM_BIOS_CALL(al) \
 271         : "=a" (error), "=b" (event) \
 272         : "a" (0x530b) \
 273         APM_BIOS_CALL_END
 274 
 275 #define APM_DRIVER_VERSION(ver, ax, error) \
 276         APM_BIOS_CALL(bl) \
 277         : "=a" (ax), "=b" (error) \
 278         : "a" (0x530e), "b" (0), "c" (ver) \
 279         APM_BIOS_CALL_END
 280 
 281 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
 282         APM_BIOS_CALL(al) \
 283         : "=a" (error) \
 284         : "a" (0x530f), "b" (device), "c" (1) \
 285         APM_BIOS_CALL_END
 286 
 287 /*
 288  * Forward declarations
 289  */
 290 static void     suspend(void);
 291 static void     standby(void);
 292 static void     set_time(void);
 293 
 294 static void     check_events(void);
 295 static void     do_apm_timer(unsigned long);
 296 
 297 static int      do_open(struct inode *, struct file *);
 298 static void     do_release(struct inode *, struct file *);
 299 static int      do_read(struct inode *, struct file *, char *, int);
 300 static int      do_select(struct inode *, struct file *, int,
 301                           select_table *);
 302 static int      do_ioctl(struct inode *, struct file *, u_int, u_long);
 303 
 304 #ifdef CONFIG_PROC_FS
 305 static int      apm_get_info(char *, char **, off_t, int, int);
 306 #endif
 307 
 308 extern int      apm_register_callback(int (*)(apm_event_t));
 309 extern void     apm_unregister_callback(int (*)(apm_event_t));
 310 
 311 /*
 312  * Local variables
 313  */
 314 static asmlinkage struct {
 315         unsigned long   offset;
 316         unsigned short  segment;
 317 }                               apm_bios_entry;
 318 static int                      apm_enabled = 0;
 319 #ifdef CONFIG_APM_CPU_IDLE
 320 static int                      clock_slowed = 0;
 321 #endif
 322 static int                      suspends_pending = 0;
 323 static int                      standbys_pending = 0;
 324 
 325 static long                     clock_cmos_diff;
 326 static int                      got_clock_diff = 0;
 327 
 328 static struct wait_queue *      process_list = NULL;
 329 static struct apm_bios_struct * user_list = NULL;
 330 
 331 static struct timer_list        apm_timer;
 332 
 333 static char                     driver_version[] = "1.0";/* no spaces */
 334 
 335 #ifdef APM_DEBUG
 336 static char *   apm_event_name[] = {
 337         "system standby",
 338         "system suspend",
 339         "normal resume",
 340         "critical resume",
 341         "low battery",
 342         "power status change",
 343         "update time",
 344         "critical suspend",
 345         "user standby",
 346         "user suspend",
 347         "system standby resume"
 348 };
 349 #define NR_APM_EVENT_NAME       \
 350                 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
 351 #endif
 352 
 353 static struct file_operations apm_bios_fops = {
 354         NULL,           /* lseek */
 355         do_read,
 356         NULL,           /* write */
 357         NULL,           /* readdir */
 358         do_select,
 359         do_ioctl,
 360         NULL,           /* mmap */
 361         do_open,
 362         do_release,
 363         NULL,           /* fsync */
 364         NULL            /* fasync */
 365 };
 366 
 367 static struct miscdevice apm_device = {
 368         APM_MINOR_DEV,
 369         "apm",
 370         &apm_bios_fops
 371 };
 372 
 373 #ifdef CONFIG_PROC_FS
 374 static struct proc_dir_entry    apm_proc_entry = {
 375         0, 3, "apm", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, apm_get_info
 376 };
 377 #endif
 378 
 379 typedef struct callback_list_t {
 380         int (*                          callback)(apm_event_t);
 381         struct callback_list_t *        next;
 382 } callback_list_t;
 383 
 384 static callback_list_t *        callback_list = NULL;
 385 
 386 typedef struct lookup_t {
 387         int     key;
 388         char *  msg;
 389 } lookup_t;
 390 
 391 static const lookup_t error_table[] = {
 392 /* N/A  { APM_SUCCESS,          "Operation succeeded" }, */
 393         { APM_DISABLED,         "Power management disabled" },
 394         { APM_CONNECTED,        "Real mode interface already connected" },
 395         { APM_NOT_CONNECTED,    "Interface not connected" },
 396         { APM_16_CONNECTED,     "16 bit interface already connected" },
 397 /* N/A  { APM_16_UNSUPPORTED,   "16 bit interface not supported" }, */
 398         { APM_32_CONNECTED,     "32 bit interface already connected" },
 399         { APM_32_UNSUPPORTED,   "32 bit interface not supported" },
 400         { APM_BAD_DEVICE,       "Unrecognized device ID" },
 401         { APM_BAD_PARAM,        "Parameter out of range" },
 402         { APM_NOT_ENGAGED,      "Interface not engaged" },
 403         { APM_BAD_STATE,        "Unable to enter requested state" },
 404 /* N/A  { APM_NO_EVENTS,        "No events pending" }, */
 405         { APM_NOT_PRESENT,      "No APM present" }
 406 };
 407 #define ERROR_COUNT     (sizeof(error_table)/sizeof(lookup_t))
 408 
 409 static int apm_driver_version(u_short *val)
     /* [previous][next][first][last][top][bottom][index][help] */
 410 {
 411         u_short error;
 412 
 413         APM_DRIVER_VERSION(*val, *val, error);
 414 
 415         if (error & 0xff)
 416                 return (*val >> 8);
 417         return APM_SUCCESS;
 418 }
 419 
 420 static int apm_get_event(apm_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422         u_short error;
 423 
 424         APM_GET_EVENT(*event, error);
 425         if (error & 0xff)
 426                 return (error >> 8);
 427         return APM_SUCCESS;
 428 }
 429 
 430 static int apm_set_power_state(u_short state)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432         u_short error;
 433 
 434         APM_SET_POWER_STATE(state, error);
 435         if (error & 0xff)
 436                 return (error >> 8);
 437         return APM_SUCCESS;
 438 }
 439 
 440 #ifdef CONFIG_APM_DISPLAY_BLANK
 441 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
 442 static int apm_set_display_power_state(u_short state)
     /* [previous][next][first][last][top][bottom][index][help] */
 443 {
 444         u_short error;
 445 
 446         APM_SET_DISPLAY_POWER_STATE(state, error);
 447         if (error & 0xff)
 448                 return (error >> 8);
 449         return APM_SUCCESS;
 450 }
 451 #endif
 452 
 453 #ifdef CONFIG_APM_DO_ENABLE
 454 /* Called by apm_setup if apm_enabled will be true. */
 455 static int apm_enable_power_management(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 456 {
 457         u_short error;
 458 
 459         APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
 460                                     ? 0x0001 : 0xffff,
 461                                     error);
 462         if (error & 0xff)
 463                 return (error >> 8);
 464         return APM_SUCCESS;
 465 }
 466 #endif
 467 
 468 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
     /* [previous][next][first][last][top][bottom][index][help] */
 469 {
 470         u_short error;
 471 
 472         APM_GET_POWER_STATUS(*status, *bat, *life, error);
 473         if (error & 0xff)
 474                 return (error >> 8);
 475         return APM_SUCCESS;
 476 }
 477 
 478 static int apm_engage_power_management(u_short device)
     /* [previous][next][first][last][top][bottom][index][help] */
 479 {
 480         u_short error;
 481 
 482         APM_ENGAGE_POWER_MANAGEMENT(device, error);
 483         if (error & 0xff)
 484                 return (error >> 8);
 485         return APM_SUCCESS;
 486 }
 487 
 488 static void apm_error(char *str, int err)
     /* [previous][next][first][last][top][bottom][index][help] */
 489 {
 490         int     i;
 491 
 492         for (i = 0; i < ERROR_COUNT; i++)
 493                 if (error_table[i].key == err) break;
 494         if (i < ERROR_COUNT)
 495                 printk("apm_bios: %s: %s\n", str, error_table[i].msg);
 496         else
 497                 printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
 498 }
 499 
 500 /* Called from console driver -- must make sure apm_enabled. */
 501 int apm_display_blank(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 502 {
 503 #ifdef CONFIG_APM_DISPLAY_BLANK
 504         int     error;
 505 
 506         if (!apm_enabled)
 507                 return 0;
 508         error = apm_set_display_power_state(APM_STATE_STANDBY);
 509         if (error == APM_SUCCESS)
 510                 return 1;
 511         apm_error("set display standby", error);
 512 #endif
 513         return 0;
 514 }
 515 
 516 /* Called from console driver -- must make sure apm_enabled. */
 517 int apm_display_unblank(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 518 {
 519 #ifdef CONFIG_APM_DISPLAY_BLANK
 520         int error;
 521 
 522         if (!apm_enabled)
 523                 return 0;
 524         error = apm_set_display_power_state(APM_STATE_READY);
 525         if (error == APM_SUCCESS)
 526                 return 1;
 527         apm_error("set display ready", error);
 528 #endif
 529         return 0;
 530 }
 531 
 532 int apm_register_callback(int (*callback)(apm_event_t))
     /* [previous][next][first][last][top][bottom][index][help] */
 533 {
 534         callback_list_t *       new;
 535 
 536         new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
 537         if (new == NULL)
 538                 return -ENOMEM;
 539         new->callback = callback;
 540         new->next = callback_list;
 541         callback_list = new;
 542         return 0;
 543 }
 544 
 545 void apm_unregister_callback(int (*callback)(apm_event_t))
     /* [previous][next][first][last][top][bottom][index][help] */
 546 {
 547         callback_list_t **      ptr;
 548         callback_list_t *       old;
 549 
 550         ptr = &callback_list;
 551         for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
 552                 if ((*ptr)->callback == callback)
 553                         break;
 554         old = *ptr;
 555         *ptr = old->next;
 556         kfree_s(old, sizeof(callback_list_t));
 557 }
 558         
 559 static int queue_empty(struct apm_bios_struct * as)
     /* [previous][next][first][last][top][bottom][index][help] */
 560 {
 561         return as->event_head == as->event_tail;
 562 }
 563 
 564 static apm_event_t get_queued_event(struct apm_bios_struct * as)
     /* [previous][next][first][last][top][bottom][index][help] */
 565 {
 566         as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
 567         return as->events[as->event_tail];
 568 }
 569 
 570 static int queue_event(apm_event_t event)
     /* [previous][next][first][last][top][bottom][index][help] */
 571 {
 572         struct apm_bios_struct *        as;
 573         
 574         if (user_list == NULL)
 575                 return 0;
 576         for (as = user_list; as != NULL; as = as->next) {
 577                 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
 578                 if (as->event_head == as->event_tail)
 579                         as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
 580                 as->events[as->event_head] = event;
 581                 if (!as->suser)
 582                         continue;
 583                 switch (event) {
 584                 case APM_SYS_SUSPEND:
 585                 case APM_USER_SUSPEND:
 586                         as->suspends_pending++;
 587                         suspends_pending++;
 588                         break;
 589 
 590                 case APM_SYS_STANDBY:
 591                 case APM_USER_STANDBY:
 592                         as->standbys_pending++;
 593                         standbys_pending++;
 594                         break;
 595                 }
 596         }
 597         wake_up_interruptible(&process_list);
 598         return 1;
 599 }
 600 
 601 static void set_time(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 602 {
 603         unsigned long   flags;
 604 
 605         if (!got_clock_diff)    /* Don't know time zone, can't set clock */
 606                 return;
 607 
 608         save_flags(flags);
 609         cli();
 610         CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
 611         restore_flags(flags);
 612 }
 613 
 614 static void suspend(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 615 {
 616         unsigned long   flags;
 617         int             err;
 618 
 619                                 /* Estimate time zone so that set_time can
 620                                    update the clock */
 621         save_flags(flags);
 622         cli();
 623         clock_cmos_diff = CURRENT_TIME - get_cmos_time();
 624         got_clock_diff = 1;
 625         restore_flags(flags);
 626         
 627         err = apm_set_power_state(APM_STATE_SUSPEND);
 628         if (err)
 629                 apm_error("suspend", err);
 630         set_time();
 631 }
 632 
 633 static void standby(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 634 {
 635         int     err;
 636 
 637         err = apm_set_power_state(APM_STATE_STANDBY);
 638         if (err)
 639                 apm_error("standby", err);
 640 }
 641 
 642 static apm_event_t get_event(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 643 {
 644         int             error;
 645         apm_event_t     event;
 646 
 647         static int notified = 0;
 648 
 649         error = apm_get_event(&event);
 650         if (error == APM_SUCCESS)
 651                 return event;
 652 
 653         if ((error != APM_NO_EVENTS) && (notified++ == 0))
 654                 apm_error("get_event", error);
 655 
 656         return 0;
 657 }
 658 
 659 static void send_event(apm_event_t event, apm_event_t undo)
     /* [previous][next][first][last][top][bottom][index][help] */
 660 {
 661         callback_list_t *       call;
 662         callback_list_t *       fix;
 663     
 664         for (call = callback_list; call != NULL; call = call->next) {
 665                 if (call->callback(event) && undo) {
 666                         for (fix = callback_list; fix != call; fix = fix->next)
 667                                 fix->callback(undo);
 668                         if (apm_bios_info.version > 0x100)
 669                                 apm_set_power_state(APM_STATE_REJECT);
 670                         return;
 671                 }
 672         }
 673 
 674         queue_event(event);
 675 }
 676 
 677 static void check_events(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 678 {
 679         apm_event_t     event;
 680 
 681         while ((event = get_event()) != 0) {
 682                 switch (event) {
 683                 case APM_SYS_STANDBY:
 684                 case APM_USER_STANDBY:
 685                         send_event(event, APM_STANDBY_RESUME);
 686                         if (standbys_pending <= 0)
 687                                 standby();
 688                         break;
 689 
 690                 case APM_USER_SUSPEND:
 691 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
 692                         apm_set_power_state(APM_STATE_REJECT);
 693                         break;
 694 #endif
 695                 case APM_SYS_SUSPEND:
 696                         send_event(event, APM_NORMAL_RESUME);
 697                         if (suspends_pending <= 0)
 698                                 suspend();
 699                         break;
 700 
 701                 case APM_NORMAL_RESUME:
 702                 case APM_CRITICAL_RESUME:
 703                 case APM_STANDBY_RESUME:
 704                         set_time();
 705                         send_event(event, 0);
 706                         break;
 707 
 708                 case APM_LOW_BATTERY:
 709                 case APM_POWER_STATUS_CHANGE:
 710                         send_event(event, 0);
 711                         break;
 712 
 713                 case APM_UPDATE_TIME:
 714                         set_time();
 715                         break;
 716 
 717                 case APM_CRITICAL_SUSPEND:
 718                         suspend();
 719                         break;
 720                 }
 721 #ifdef APM_DEBUG
 722                 if (event <= NR_APM_EVENT_NAME)
 723                         printk("APM BIOS received %s notify\n",
 724                                apm_event_name[event - 1]);
 725                 else
 726                         printk("APM BIOS received unknown event 0x%02x\n",
 727                                event);
 728 #endif
 729         }
 730 }
 731 
 732 static void do_apm_timer(unsigned long unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 733 {
 734         int     err;
 735 
 736         static int      pending_count = 0;
 737 
 738         if (((standbys_pending > 0) || (suspends_pending > 0))
 739             && (apm_bios_info.version > 0x100)
 740             && (pending_count-- <= 0)) {
 741                 pending_count = 4;
 742 
 743                 err = apm_set_power_state(APM_STATE_BUSY);
 744                 if (err)
 745                         apm_error("busy", err);
 746         }
 747 
 748         if (!(((standbys_pending > 0) || (suspends_pending > 0))
 749               && (apm_bios_info.version == 0x100)))
 750                 check_events();
 751 
 752         init_timer(&apm_timer);
 753         apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
 754         add_timer(&apm_timer);
 755 }
 756 
 757 /* Called from sys_idle, must make sure apm_enabled. */
 758 int apm_do_idle(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 759 {
 760 #ifdef CONFIG_APM_CPU_IDLE
 761         unsigned short  error;
 762 
 763         if (!apm_enabled)
 764                 return 0;
 765 
 766         APM_SET_CPU_IDLE(error);
 767         if (error & 0xff)
 768                 return 0;
 769 
 770         clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
 771         return 1;
 772 #else
 773         return 0;
 774 #endif
 775 }
 776 
 777 /* Called from sys_idle, must make sure apm_enabled. */
 778 void apm_do_busy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 779 {
 780 #ifdef CONFIG_APM_CPU_IDLE
 781         unsigned short  error;
 782 
 783         if (!apm_enabled)
 784                 return;
 785         
 786 #ifndef ALWAYS_CALL_BUSY
 787         if (!clock_slowed)
 788                 return;
 789 #endif
 790 
 791         APM_SET_CPU_BUSY(error);
 792 
 793         clock_slowed = 0;
 794 #endif
 795 }
 796 
 797 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
     /* [previous][next][first][last][top][bottom][index][help] */
 798 {
 799         if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
 800                 printk("apm_bios: %s passed bad filp", func);
 801                 return 1;
 802         }
 803         return 0;
 804 }
 805 
 806 static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 807 {
 808         struct apm_bios_struct *        as;
 809         int                     i;
 810         apm_event_t             event;
 811         struct wait_queue       wait = { current,       NULL };
 812 
 813         as = fp->private_data;
 814         if (check_apm_bios_struct(as, "read"))
 815                 return -EIO;
 816         if (count < sizeof(apm_event_t))
 817                 return -EINVAL;
 818         if (queue_empty(as)) {
 819                 if (fp->f_flags & O_NONBLOCK)
 820                         return -EAGAIN;
 821                 add_wait_queue(&process_list, &wait);
 822 repeat:
 823                 current->state = TASK_INTERRUPTIBLE;
 824                 if (queue_empty(as)
 825                     && !(current->signal & ~current->blocked)) {
 826                         schedule();
 827                         goto repeat;
 828                 }
 829                 current->state = TASK_RUNNING;
 830                 remove_wait_queue(&process_list, &wait);
 831         }
 832         i = count;
 833         while ((i >= sizeof(event)) && !queue_empty(as)) {
 834                 event = get_queued_event(as);
 835                 memcpy_tofs(buf, &event, sizeof(event));
 836                 buf += sizeof(event);
 837                 i -= sizeof(event);
 838         }
 839         if (i < count)
 840                 return count - i;
 841         if (current->signal & ~current->blocked)
 842                 return -ERESTARTSYS;
 843         return 0;
 844 }
 845 
 846 static int do_select(struct inode *inode, struct file *fp, int sel_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 847                      select_table * wait)
 848 {
 849         struct apm_bios_struct *        as;
 850 
 851         as = fp->private_data;
 852         if (check_apm_bios_struct(as, "select"))
 853                 return 0;
 854         if (sel_type != SEL_IN)
 855                 return 0;
 856         if (!queue_empty(as))
 857                 return 1;
 858         select_wait(&process_list, wait);
 859         return 0;
 860 }
 861 
 862 static int do_ioctl(struct inode * inode, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 863                     u_int cmd, u_long arg)
 864 {
 865         struct apm_bios_struct *        as;
 866 
 867         as = filp->private_data;
 868         if (check_apm_bios_struct(as, "ioctl"))
 869                 return -EIO;
 870         switch (cmd) {
 871         case APM_IOC_STANDBY:
 872                 if (as->standbys_pending > 0) {
 873                         as->standbys_pending--;
 874                         standbys_pending--;
 875                         if (standbys_pending <= 0)
 876                                 standby();
 877                 }
 878                 break;
 879         case APM_IOC_SUSPEND:
 880                 if (as->suspends_pending > 0) {
 881                         as->suspends_pending--;
 882                         suspends_pending--;
 883                         if (suspends_pending <= 0)
 884                                 suspend();
 885                 }
 886                 break;
 887         default:
 888                 return -EINVAL;
 889         }
 890         return 0;
 891 }
 892 
 893 static void do_release(struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 894 {
 895         struct apm_bios_struct *        as;
 896 
 897         as = filp->private_data;
 898         filp->private_data = NULL;
 899         if (check_apm_bios_struct(as, "release"))
 900                 return;
 901         if (as->standbys_pending > 0) {
 902                 standbys_pending -= as->standbys_pending;
 903                 if (standbys_pending <= 0)
 904                         standby();
 905         }
 906         if (as->suspends_pending > 0) {
 907                 suspends_pending -= as->suspends_pending;
 908                 if (suspends_pending <= 0)
 909                         suspend();
 910         }
 911         if (user_list == as)
 912                 user_list = as->next;
 913         else {
 914                 struct apm_bios_struct *        as1;
 915 
 916                 for (as1 = user_list;
 917                      (as1 != NULL) && (as1->next != as);
 918                      as1 = as1->next)
 919                         ;
 920                 if (as1 == NULL)
 921                         printk("apm_bios: filp not in user list");
 922                 else
 923                         as1->next = as->next;
 924         }
 925         kfree_s(as, sizeof(*as));
 926 }
 927 
 928 static int do_open(struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 929 {
 930         struct apm_bios_struct *        as;
 931 
 932         as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
 933         if (as == NULL) {
 934                 printk("apm_bios: cannot allocate struct of size %d bytes",
 935                        sizeof(*as));
 936                 return -ENOMEM;
 937         }
 938         as->magic = APM_BIOS_MAGIC;
 939         as->event_tail = as->event_head = 0;
 940         as->suspends_pending = as->standbys_pending = 0;
 941         as->suser = suser();
 942         as->next = user_list;
 943         user_list = as;
 944         filp->private_data = as;
 945         return 0;
 946 }
 947 
 948 #ifdef CONFIG_PROC_FS
 949 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
     /* [previous][next][first][last][top][bottom][index][help] */
 950 {
 951         char *          p;
 952         unsigned short  bx;
 953         unsigned short  cx;
 954         unsigned short  dx;
 955         unsigned short  error;
 956         unsigned short  ac_line_status = 0xff;
 957         unsigned short  battery_status = 0xff;
 958         unsigned short  battery_flag   = 0xff;
 959         int             percentage     = -1;
 960         int             time_units     = -1;
 961         char            *units         = "?";
 962 
 963         if (!apm_enabled)
 964                 return 0;
 965         p = buf;
 966 
 967         if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
 968                 ac_line_status = (bx >> 8) & 0xff;
 969                 battery_status = bx & 0xff;
 970                 if ((cx & 0xff) != 0xff)
 971                         percentage = cx & 0xff;
 972 
 973                 if (apm_bios_info.version > 0x100) {
 974                         battery_flag = (cx >> 8) & 0xff;
 975                         if (dx != 0xffff) {
 976                                 if ((dx & 0x8000) == 0x8000) {
 977                                         units = "min";
 978                                         time_units = dx & 0x7ffe;
 979                                 } else {
 980                                         units = "sec";
 981                                         time_units = dx & 0x7fff;
 982                                 }
 983                         }
 984                 }
 985         }
 986         /* Arguments, with symbols from linux/apm_bios.h.  Information is
 987            from the Get Power Status (0x0a) call unless otherwise noted.
 988 
 989            0) Linux driver version (this will change if format changes)
 990            1) APM BIOS Version.  Usually 1.0 or 1.1.
 991            2) APM flags from APM Installation Check (0x00):
 992               bit 0: APM_16_BIT_SUPPORT
 993               bit 1: APM_32_BIT_SUPPORT
 994               bit 2: APM_IDLE_SLOWS_CLOCK
 995               bit 3: APM_BIOS_DISABLED
 996               bit 4: APM_BIOS_DISENGAGED
 997            3) AC line status
 998               0x00: Off-line
 999               0x01: On-line
1000               0x02: On backup power (APM BIOS 1.1 only)
1001               0xff: Unknown
1002            4) Battery status
1003               0x00: High
1004               0x01: Low
1005               0x02: Critical
1006               0x03: Charging
1007               0xff: Unknown
1008            5) Battery flag
1009               bit 0: High
1010               bit 1: Low
1011               bit 2: Critical
1012               bit 3: Charging
1013               bit 7: No system battery
1014               0xff: Unknown
1015            6) Remaining battery life (percentage of charge):
1016               0-100: valid
1017               -1: Unknown
1018            7) Remaining battery life (time units):
1019               Number of remaining minutes or seconds
1020               -1: Unknown
1021            8) min = minutes; sec = seconds */
1022             
1023         p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1024                      driver_version,
1025                      (apm_bios_info.version >> 8) & 0xff,
1026                      apm_bios_info.version & 0xff,
1027                      apm_bios_info.flags,
1028                      ac_line_status,
1029                      battery_status,
1030                      battery_flag,
1031                      percentage,
1032                      time_units,
1033                      units);
1034 
1035         return p - buf;
1036 }
1037 #endif
1038 
1039 void apm_bios_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1040 {
1041         unsigned short  bx;
1042         unsigned short  cx;
1043         unsigned short  dx;
1044         unsigned short  error;
1045         char *          power_stat;
1046         char *          bat_stat;
1047 
1048         if (apm_bios_info.version == 0) {
1049                 printk("APM BIOS not found.\n");
1050                 return;
1051         }
1052         printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
1053                ((apm_bios_info.version >> 8) & 0xff) + '0',
1054                (apm_bios_info.version & 0xff) + '0',
1055                apm_bios_info.flags,
1056                driver_version);
1057         if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1058                 printk("    No 32 bit BIOS support\n");
1059                 return;
1060         }
1061 
1062         /*
1063          * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1064          * but is reportedly a 1.0 BIOS.
1065          */
1066         if (apm_bios_info.version == 0x001)
1067                 apm_bios_info.version = 0x100;
1068 
1069         printk("    Entry %x:%lx cseg16 %x dseg %x",
1070                apm_bios_info.cseg, apm_bios_info.offset,
1071                apm_bios_info.cseg_16, apm_bios_info.dseg);
1072         if (apm_bios_info.version > 0x100)
1073                 printk(" cseg len %x, dseg len %x",
1074                        apm_bios_info.cseg_len, apm_bios_info.dseg_len);
1075         printk("\n");
1076 
1077         apm_bios_entry.offset = apm_bios_info.offset;
1078         apm_bios_entry.segment = APM_CS;
1079         set_base(gdt[APM_CS >> 3],
1080                  0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
1081         set_base(gdt[APM_CS_16 >> 3],
1082                  0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
1083         set_base(gdt[APM_DS >> 3],
1084                  0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
1085         if (apm_bios_info.version == 0x100) {
1086                 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1087                 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1088                 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1089         } else {
1090 #ifdef APM_RELAX_SEGMENTS
1091                 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1092                 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1093                 /* For some unknown machine. */
1094                 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1095                 /* For the DEC Hinote Ultra CT475 (and others?) */
1096                 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1097 #else
1098                 set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
1099                 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1100                 set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
1101 #endif
1102                 apm_bios_info.version = 0x0101;
1103                 error = apm_driver_version(&apm_bios_info.version);
1104                 if (error != 0)
1105                         apm_bios_info.version = 0x100;
1106                 else {
1107                         apm_engage_power_management(0x0001);
1108                         printk( "    Connection version %d.%d\n",
1109                                 (apm_bios_info.version >> 8) & 0xff,
1110                                 apm_bios_info.version & 0xff );
1111                         apm_bios_info.version = 0x0101;
1112                 }
1113         }
1114 
1115         error = apm_get_power_status(&bx, &cx, &dx);
1116         if (error)
1117                 printk("    Power status not available\n");
1118         else {
1119                 switch ((bx >> 8) & 0xff) {
1120                 case 0: power_stat = "off line"; break;
1121                 case 1: power_stat = "on line"; break;
1122                 case 2: power_stat = "on backup power"; break;
1123                 default: power_stat = "unknown"; break;
1124                 }
1125                 switch (bx & 0xff) {
1126                 case 0: bat_stat = "high"; break;
1127                 case 1: bat_stat = "low"; break;
1128                 case 2: bat_stat = "critical"; break;
1129                 case 3: bat_stat = "charging"; break;
1130                 default: bat_stat = "unknown"; break;
1131                 }
1132                 printk("    AC %s, battery status %s, battery life ",
1133                        power_stat, bat_stat);
1134                 if ((cx & 0xff) == 0xff)
1135                         printk("unknown\n");
1136                 else
1137                         printk("%d%%\n", cx & 0xff);
1138                 if (apm_bios_info.version > 0x100) {
1139                         printk("    battery flag 0x%02x, battery life ",
1140                                (cx >> 8) & 0xff);
1141                         if (dx == 0xffff)
1142                                 printk("unknown\n");
1143                         else {
1144                                 if ((dx & 0x8000)) 
1145                                         printk("%d minutes\n", dx & 0x7ffe );
1146                                 else
1147                                         printk("%d seconds\n", dx & 0x7fff );
1148                         }
1149                 }
1150         }
1151 
1152 #ifdef CONFIG_APM_DO_ENABLE
1153         /*
1154          * This call causes my NEC UltraLite Versa 33/C to hang if it is
1155          * booted with PM disabled but not in the docking station.
1156          * Unfortunate ...
1157          */
1158         error = apm_enable_power_management();
1159         if (error)
1160                 apm_error("enable power management", error);
1161         if (error == APM_DISABLED)
1162                 return;
1163 #endif
1164 
1165         init_timer(&apm_timer);
1166         apm_timer.function = do_apm_timer;
1167         apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
1168         add_timer(&apm_timer);
1169 
1170         register_symtab(&apm_syms);
1171 
1172 #ifdef CONFIG_PROC_FS
1173         proc_register_dynamic(&proc_root, &apm_proc_entry);
1174 #endif
1175 
1176         misc_register(&apm_device);
1177 
1178         apm_enabled = 1;
1179 }

/* [previous][next][first][last][top][bottom][index][help] */