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

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