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

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