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_proc
  32. apm_setup
  33. apm_bios_init

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

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