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

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