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

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