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         check_events();
 709 
 710         init_timer(&apm_timer);
 711         apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
 712         add_timer(&apm_timer);
 713 }
 714 
 715 int apm_do_idle(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 716 {
 717 #ifdef CONFIG_APM_CPU_IDLE
 718         unsigned short  error;
 719 
 720         if (!apm_enabled)
 721                 return 0;
 722 
 723         APM_SET_CPU_IDLE(error);
 724         if (error & 0xff)
 725                 return 0;
 726 
 727         clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
 728         return 1;
 729 #else
 730         return 0;
 731 #endif
 732 }
 733 
 734 void apm_do_busy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 735 {
 736 #ifdef CONFIG_APM_CPU_IDLE
 737         unsigned short  error;
 738 
 739 #ifndef ALWAYS_CALL_BUSY
 740         if (!clock_slowed)
 741                 return;
 742 #endif
 743 
 744         APM_SET_CPU_BUSY(error);
 745 
 746         clock_slowed = 0;
 747 #endif
 748 }
 749 
 750 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
     /* [previous][next][first][last][top][bottom][index][help] */
 751 {
 752         if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
 753                 printk("apm_bios: %s passed bad filp", func);
 754                 return 1;
 755         }
 756         return 0;
 757 }
 758 
 759 static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 760 {
 761         struct apm_bios_struct *        as;
 762         int                     i;
 763         apm_event_t             event;
 764         struct wait_queue       wait = { current,       NULL };
 765 
 766         as = fp->private_data;
 767         if (check_apm_bios_struct(as, "read"))
 768                 return -EIO;
 769         if (count < sizeof(apm_event_t))
 770                 return -EINVAL;
 771         if (queue_empty(as)) {
 772                 if (fp->f_flags & O_NONBLOCK)
 773                         return -EAGAIN;
 774                 add_wait_queue(&process_list, &wait);
 775 repeat:
 776                 current->state = TASK_INTERRUPTIBLE;
 777                 if (queue_empty(as)
 778                     && !(current->signal & ~current->blocked)) {
 779                         schedule();
 780                         goto repeat;
 781                 }
 782                 current->state = TASK_RUNNING;
 783                 remove_wait_queue(&process_list, &wait);
 784         }
 785         i = count;
 786         while ((i >= sizeof(event)) && !queue_empty(as)) {
 787                 event = get_queued_event(as);
 788                 memcpy_tofs(buf, &event, sizeof(event));
 789                 buf += sizeof(event);
 790                 i -= sizeof(event);
 791         }
 792         if (i < count)
 793                 return count - i;
 794         if (current->signal & ~current->blocked)
 795                 return -ERESTARTSYS;
 796         return 0;
 797 }
 798 
 799 static int do_select(struct inode *inode, struct file *fp, int sel_type,
     /* [previous][next][first][last][top][bottom][index][help] */
 800                      select_table * wait)
 801 {
 802         struct apm_bios_struct *        as;
 803 
 804         as = fp->private_data;
 805         if (check_apm_bios_struct(as, "select"))
 806                 return 0;
 807         if (sel_type != SEL_IN)
 808                 return 0;
 809         if (!queue_empty(as))
 810                 return 1;
 811         select_wait(&process_list, wait);
 812         return 0;
 813 }
 814 
 815 static int do_ioctl(struct inode * inode, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 816                     u_int cmd, u_long arg)
 817 {
 818         struct apm_bios_struct *        as;
 819 
 820         as = filp->private_data;
 821         if (check_apm_bios_struct(as, "ioctl"))
 822                 return -EIO;
 823         switch (cmd) {
 824         case APM_IOC_STANDBY:
 825                 if (as->standbys_pending > 0) {
 826                         as->standbys_pending--;
 827                         standbys_pending--;
 828                         if (standbys_pending <= 0)
 829                                 standby();
 830                 }
 831                 break;
 832         case APM_IOC_SUSPEND:
 833                 if (as->suspends_pending > 0) {
 834                         as->suspends_pending--;
 835                         suspends_pending--;
 836                         if (suspends_pending <= 0)
 837                                 suspend();
 838                 }
 839                 break;
 840         default:
 841                 return -EINVAL;
 842         }
 843         return 0;
 844 }
 845 
 846 static void do_release(struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 847 {
 848         struct apm_bios_struct *        as;
 849 
 850         as = filp->private_data;
 851         filp->private_data = NULL;
 852         if (check_apm_bios_struct(as, "release"))
 853                 return;
 854         if (as->standbys_pending > 0) {
 855                 standbys_pending -= as->standbys_pending;
 856                 if (standbys_pending <= 0)
 857                         standby();
 858         }
 859         if (as->suspends_pending > 0) {
 860                 suspends_pending -= as->suspends_pending;
 861                 if (suspends_pending <= 0)
 862                         suspend();
 863         }
 864         if (user_list == as)
 865                 user_list = as->next;
 866         else {
 867                 struct apm_bios_struct *        as1;
 868 
 869                 for (as1 = user_list;
 870                      (as1 != NULL) && (as1->next != as);
 871                      as1 = as1->next)
 872                         ;
 873                 if (as1 == NULL)
 874                         printk("apm_bios: filp not in user list");
 875                 else
 876                         as1->next = as->next;
 877         }
 878         kfree_s(as, sizeof(*as));
 879 }
 880 
 881 static int do_open(struct inode * inode, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 882 {
 883         struct apm_bios_struct *        as;
 884 
 885         as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
 886         if (as == NULL) {
 887                 printk("apm_bios: cannot allocate struct of size %d bytes",
 888                        sizeof(*as));
 889                 return -ENOMEM;
 890         }
 891         as->magic = APM_BIOS_MAGIC;
 892         as->event_tail = as->event_head = 0;
 893         as->suspends_pending = as->standbys_pending = 0;
 894         as->suser = suser();
 895         as->next = user_list;
 896         user_list = as;
 897         filp->private_data = as;
 898         return 0;
 899 }
 900 
 901 int apm_proc(char *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 902 {
 903         char *          p;
 904         unsigned short  bx;
 905         unsigned short  cx;
 906         unsigned short  dx;
 907         unsigned short  error;
 908         unsigned short  ac_line_status = 0xff;
 909         unsigned short  battery_status = 0xff;
 910         unsigned short  battery_flag   = 0xff;
 911         unsigned short  percentage     = -1;
 912         int             time_units     = -1;
 913         char            *units         = "?";
 914 
 915         if (apm_bios_info.version == 0)
 916                 return 0;
 917         p = buf;
 918 
 919         if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) != 0) {
 920                 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
 921                         ac_line_status = (bx >> 8) & 0xff;
 922                         battery_status = bx & 0xff;
 923                         if ((cx & 0xff) != 0xff)
 924                                 percentage = cx & 0xff;
 925         
 926                         if (apm_bios_info.version > 0x100) {
 927                                 battery_flag = (cx >> 8) & 0xff;
 928                                 if (dx != 0xffff) {
 929                                         if ((dx & 0x8000) == 0x8000) {
 930                                                 units = "min";
 931                                                 time_units = dx & 0x7ffe;
 932                                         } else {
 933                                                 units = "sec";
 934                                                 time_units = dx & 0x7fff;
 935                                         }
 936                                 }
 937                         }
 938                 }
 939         }
 940         /* Arguments, with symbols from linux/apm_bios.h.  Information is
 941            from the Get Power Status (0x0a) call unless otherwise noted.
 942 
 943            0) Linux driver version (this will change if format changes)
 944            1) APM BIOS Version.  Usually 1.0 or 1.1.
 945            2) APM flags from APM Installation Check (0x00):
 946               bit 0: APM_16_BIT_SUPPORT
 947               bit 1: APM_32_BIT_SUPPORT
 948               bit 2: APM_IDLE_SLOWS_CLOCK
 949               bit 3: APM_BIOS_DISABLED
 950               bit 4: APM_BIOS_DISENGAGED
 951            3) AC line status
 952               0x00: Off-line
 953               0x01: On-line
 954               0x02: On backup power (APM BIOS 1.1 only)
 955               0xff: Unknown
 956            4) Battery status
 957               0x00: High
 958               0x01: Low
 959               0x02: Critical
 960               0x03: Charging
 961               0xff: Unknown
 962            5) Battery flag
 963               bit 0: High
 964               bit 1: Low
 965               bit 2: Critical
 966               bit 3: Charging
 967               bit 7: No system battery
 968               0xff: Unknown
 969            6) Remaining battery life (percentage of charge):
 970               0-100: valid
 971               -1: Unknown
 972            7) Remaining battery life (time units):
 973               Number of remaining minutes or seconds
 974               -1: Unknown
 975            8) min = minutes; sec = seconds */
 976             
 977         p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
 978                      driver_version,
 979                      (apm_bios_info.version >> 8) & 0xff,
 980                      apm_bios_info.version & 0xff,
 981                      apm_bios_info.flags,
 982                      ac_line_status,
 983                      battery_status,
 984                      battery_flag,
 985                      percentage,
 986                      time_units,
 987                      units );
 988 
 989         return p - buf;
 990 }
 991 
 992 static int apm_setup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 993 {
 994         unsigned short  bx;
 995         unsigned short  cx;
 996         unsigned short  dx;
 997         unsigned short  error;
 998         char *          power_stat;
 999         char *          bat_stat;
1000 
1001         if (apm_bios_info.version == 0) {
1002                 printk("APM BIOS not found.\n");
1003                 return -1;
1004         }
1005         printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
1006                ((apm_bios_info.version >> 8) & 0xff) + '0',
1007                (apm_bios_info.version & 0xff) + '0',
1008                apm_bios_info.flags,
1009                driver_version);
1010         if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1011                 printk("    No 32 bit BIOS support\n");
1012                 return -1;
1013         }
1014 
1015         /*
1016          * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
1017          * but is reportedly a 1.0 BIOS.
1018          */
1019         if (apm_bios_info.version == 0x001)
1020                 apm_bios_info.version = 0x100;
1021 
1022         printk("    Entry %x:%lx cseg16 %x dseg %x",
1023                apm_bios_info.cseg, apm_bios_info.offset,
1024                apm_bios_info.cseg_16, apm_bios_info.dseg);
1025         if (apm_bios_info.version > 0x100)
1026                 printk(" cseg len %x, dseg len %x",
1027                        apm_bios_info.cseg_len, apm_bios_info.dseg_len);
1028         printk("\n");
1029 
1030         apm_bios_entry.offset = apm_bios_info.offset;
1031         apm_bios_entry.segment = APM_CS;
1032         set_base(gdt[APM_CS >> 3],
1033                  0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
1034         set_base(gdt[APM_CS_16 >> 3],
1035                  0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
1036         set_base(gdt[APM_DS >> 3],
1037                  0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
1038         if (apm_bios_info.version == 0x100) {
1039                 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1040                 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1041                 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1042         } else {
1043 #ifdef APM_RELAX_SEGMENTS
1044                 /* For ASUS motherboard, Award BIOS rev 110 (and others?) */
1045                 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1046                 /* For some unknown machine. */
1047                 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1048                 /* For the DEC Hinote Ultra CT475 (and others?) */
1049                 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1050 #else
1051                 set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
1052                 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1053                 set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
1054 #endif
1055                 apm_bios_info.version = 0x0101;
1056                 error = apm_driver_version(&apm_bios_info.version);
1057                 if (error != 0)
1058                         apm_bios_info.version = 0x100;
1059                 else {
1060                         apm_engage_power_management(0x0001);
1061                         printk( "    Connection version %d.%d\n",
1062                                 (apm_bios_info.version >> 8) & 0xff,
1063                                 apm_bios_info.version & 0xff );
1064                         apm_bios_info.version = 0x0101;
1065                 }
1066         }
1067 
1068         error = apm_get_power_status(&bx, &cx, &dx);
1069         if (error)
1070                 printk("    Power status not available\n");
1071         else {
1072                 switch ((bx >> 8) & 0xff) {
1073                 case 0: power_stat = "off line"; break;
1074                 case 1: power_stat = "on line"; break;
1075                 case 2: power_stat = "on backup power"; break;
1076                 default: power_stat = "unknown"; break;
1077                 }
1078                 switch (bx & 0xff) {
1079                 case 0: bat_stat = "high"; break;
1080                 case 1: bat_stat = "low"; break;
1081                 case 2: bat_stat = "critical"; break;
1082                 case 3: bat_stat = "charging"; break;
1083                 default: bat_stat = "unknown"; break;
1084                 }
1085                 printk("    AC %s, battery status %s, battery life ",
1086                        power_stat, bat_stat);
1087                 if ((cx & 0xff) == 0xff)
1088                         printk("unknown\n");
1089                 else
1090                         printk("%d%%\n", cx & 0xff);
1091                 if (apm_bios_info.version > 0x100) {
1092                         printk("    battery flag 0x%02x, battery life ",
1093                                (cx >> 8) & 0xff);
1094                         if (dx == 0xffff)
1095                                 printk("unknown\n");
1096                         else {
1097                                 if ((dx & 0x8000)) 
1098                                         printk("%d minutes\n", dx & 0x7ffe );
1099                                 else
1100                                         printk("%d seconds\n", dx & 0x7fff );
1101                         }
1102                 }
1103         }
1104 
1105 #ifdef CONFIG_APM_DO_ENABLE
1106         /*
1107          * This call causes my NEC UltraLite Versa 33/C to hang if it is
1108          * booted with PM disabled but not in the docking station.
1109          * Unfortunate ...
1110          */
1111         error = apm_enable_power_management();
1112         if (error)
1113                 apm_error("enable power management", error);
1114 #endif
1115 
1116         init_timer(&apm_timer);
1117         apm_timer.function = do_apm_timer;
1118         apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
1119         add_timer(&apm_timer);
1120 
1121         register_symtab(&apm_syms);
1122 
1123         apm_enabled = 1;
1124 
1125         if ((apm_major = register_chrdev(0, "apm_bios", &apm_bios_fops)) < 0)
1126                 printk("APM BIOS: Cannot allocate major device number\n");
1127 
1128         return 0;
1129 }
1130 
1131 void apm_bios_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1132 {
1133         apm_setup();
1134 }

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