📄 apm.c
字号:
/* -*- linux-c -*- * APM BIOS driver for Linux * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au) * * Initial development of this driver was funded by NEC Australia P/L * and NEC Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * October 1995, Rik Faith (faith@cs.unc.edu): * Minor enhancements and updates (to the patch set) for 1.3.x * Documentation * January 1996, Rik Faith (faith@cs.unc.edu): * Make /proc/apm easy to format (bump driver version) * March 1996, Rik Faith (faith@cs.unc.edu): * Prohibit APM BIOS calls unless apm_enabled. * (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>) * April 1996, Stephen Rothwell (sfr@canb.auug.org.au) * Version 1.0 and 1.1 * May 1996, Version 1.2 * Feb 1998, Version 1.3 * Feb 1998, Version 1.4 * Aug 1998, Version 1.5 * Sep 1998, Version 1.6 * Nov 1998, Version 1.7 * Jan 1999, Version 1.8 * Jan 1999, Version 1.9 * Oct 1999, Version 1.10 * Nov 1999, Version 1.11 * Jan 2000, Version 1.12 * Feb 2000, Version 1.13 * Nov 2000, Version 1.14 * Oct 2001, Version 1.15 * Jan 2002, Version 1.16 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 * 0.7: changed /proc/apm format, Linux 1.3.58 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59 * 0.9: only call bios if bios is present, Linux 1.3.72 * 1.0: use fixed device number, consolidate /proc/apm into this file, * Linux 1.3.85 * 1.1: support user-space standby and suspend, power off after system * halted, Linux 1.3.98 * 1.2: When resetting RTC after resume, take care so that the time * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth * <jtoth@princeton.edu>); improve interaction between * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4 * 1.2a:Simple change to stop mysterious bug reports with SMP also added * levels to the printk calls. APM is not defined for SMP machines. * The new replacment for it is, but Linux doesn't yet support this. * Alan Cox Linux 2.1.55 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by * Dean Gaudet <dgaudet@arctic.org>. * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87 * 1.5: Fix segment register reloading (in case of bad segments saved * across BIOS call). * Stephen Rothwell * 1.6: Cope with complier/assembler differences. * Only try to turn off the first display device. * Fix OOPS at power off with no APM BIOS by Jan Echternach * <echter@informatik.uni-rostock.de> * Stephen Rothwell * 1.7: Modify driver's cached copy of the disabled/disengaged flags * to reflect current state of APM BIOS. * Chris Rankin <rankinc@bellsouth.net> * Reset interrupt 0 timer to 100Hz after suspend * Chad Miller <cmiller@surfsouth.com> * Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE * Richard Gooch <rgooch@atnf.csiro.au> * Allow boot time disabling of APM * Make boot messages far less verbose by default * Make asm safer * Stephen Rothwell * 1.8: Add CONFIG_APM_RTC_IS_GMT * Richard Gooch <rgooch@atnf.csiro.au> * change APM_NOINTS to CONFIG_APM_ALLOW_INTS * remove dependency on CONFIG_PROC_FS * Stephen Rothwell * 1.9: Fix small typo. <laslo@wodip.opole.pl> * Try to cope with BIOS's that need to have all display * devices blanked and not just the first one. * Ross Paterson <ross@soi.city.ac.uk> * Fix segment limit setting it has always been wrong as * the segments needed to have byte granularity. * Mark a few things __init. * Add hack to allow power off of SMP systems by popular request. * Use CONFIG_SMP instead of __SMP__ * Ignore BOUNCES for three seconds. * Stephen Rothwell * 1.10: Fix for Thinkpad return code. * Merge 2.2 and 2.3 drivers. * Remove APM dependencies in arch/i386/kernel/process.c * Remove APM dependencies in drivers/char/sysrq.c * Reset time across standby. * Allow more inititialisation on SMP. * Remove CONFIG_APM_POWER_OFF and make it boot time * configurable (default on). * Make debug only a boot time parameter (remove APM_DEBUG). * Try to blank all devices on any error. * 1.11: Remove APM dependencies in drivers/char/console.c * Check nr_running to detect if we are idle (from * Borislav Deianov <borislav@lix.polytechnique.fr>) * Fix for bioses that don't zero the top part of the * entrypoint offset (Mario Sitta <sitta@al.unipmn.it>) * (reported by Panos Katsaloulis <teras@writeme.com>). * Real mode power off patch (Walter Hofmann * <Walter.Hofmann@physik.stud.uni-erlangen.de>). * 1.12: Remove CONFIG_SMP as the compiler will optimize * the code away anyway (smp_num_cpus == 1 in UP) * noted by Artur Skawina <skawina@geocities.com>. * Make power off under SMP work again. * Fix thinko with initial engaging of BIOS. * Make sure power off only happens on CPU 0 * (Paul "Rusty" Russell <rusty@rustcorp.com.au>). * Do error notification to user mode if BIOS calls fail. * Move entrypoint offset fix to ...boot/setup.S * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>). * Remove smp-power-off. SMP users must now specify * "apm=power-off" on the kernel command line. Suggested * by Jim Avera <jima@hal.com>, modified by Alan Cox * <alan@lxorguk.ukuu.org.uk>. * Register the /proc/apm entry even on SMP so that * scripts that check for it before doing power off * work (Jim Avera <jima@hal.com>). * 1.13: Changes for new pm_ interfaces (Andy Henroid * <andy_henroid@yahoo.com>). * Modularize the code. * Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS * is now the way life works). * Fix thinko in suspend() (wrong return). * Notify drivers on critical suspend. * Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz> * modified by sfr). * Disable interrupts while we are suspended (Andy Henroid * <andy_henroid@yahoo.com> fixed by sfr). * Make power off work on SMP again (Tony Hoyle * <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr. * Remove CONFIG_APM_SUSPEND_BOUNCE. The bounce ignore * interval is now configurable. * 1.14: Make connection version persist across module unload/load. * Enable and engage power management earlier. * Disengage power management on module unload. * Changed to use the sysrq-register hack for registering the * power off function called by magic sysrq based upon discussions * in irc://irc.openprojects.net/#kernelnewbies * (Crutcher Dunnavant <crutcher+kernel@datastacks.com>). * Make CONFIG_APM_REAL_MODE_POWER_OFF run time configurable. * (Arjan van de Ven <arjanv@redhat.com>) modified by sfr. * Work around byte swap bug in one of the Vaio's BIOS's * (Marc Boucher <marc@mbsi.ca>). * Exposed the disable flag to dmi so that we can handle known * broken APM (Alan Cox <alan@redhat.com>). * 1.14ac: If the BIOS says "I slowed the CPU down" then don't spin * calling it - instead idle. (Alan Cox <alan@redhat.com>) * If an APM idle fails log it and idle sensibly * 1.15: Don't queue events to clients who open the device O_WRONLY. * Don't expect replies from clients who open the device O_RDONLY. * (Idea from Thomas Hood <jdthood@mail.com>) * Minor waitqueue cleanups. (John Fremlin <chief@bandits.org>) * 1.16: Fix idle calling. (Andreas Steinmetz <ast@domdv.de> et al.) * Notify listeners of standby or suspend events before notifying * drivers. Return EBUSY to ioctl() if suspend is rejected. * (Russell King <rmk@arm.linux.org.uk> and Thomas Hood) * Ignore first resume after we generate our own resume event * after a suspend (Thomas Hood <jdthood@mail.com>) * Daemonize now gets rid of our controlling terminal (sfr). * CONFIG_APM_CPU_IDLE now just affects the default value of * idle_threshold (sfr). * Change name of kernel apm daemon (as it no longer idles) (sfr). * * APM 1.1 Reference: * * Intel Corporation, Microsoft Corporation. Advanced Power Management * (APM) BIOS Interface Specification, Revision 1.1, September 1993. * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01. * * [This document is available free from Intel by calling 800.628.8686 (fax * 916.356.6100) or 800.548.4725; or via anonymous ftp from * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also * available from Microsoft by calling 206.882.8080.] * * APM 1.2 Reference: * Intel Corporation, Microsoft Corporation. Advanced Power Management * (APM) BIOS Interface Specification, Revision 1.2, February 1996. * * [This document is available from Microsoft at: * http://www.microsoft.com/hwdev/busbios/amp_12.htm] */#include <linux/config.h>#include <linux/module.h>#include <linux/poll.h>#include <linux/types.h>#include <linux/stddef.h>#include <linux/timer.h>#include <linux/fcntl.h>#include <linux/slab.h>#include <linux/stat.h>#include <linux/proc_fs.h>#include <linux/miscdevice.h>#include <linux/apm_bios.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/kernel.h>#include <linux/smp_lock.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/desc.h>#include <linux/sysrq.h>extern unsigned long get_cmos_time(void);extern void machine_real_restart(unsigned char *, int);#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)extern int (*console_blank_hook)(int);#endif/* * The apm_bios device is one of the misc char devices. * This is its minor number. */#define APM_MINOR_DEV 134/* * See Documentation/Config.help for the configuration options. * * Various options can be changed at boot time as follows: * (We allow underscores for compatibility with the modules code) * apm=on/off enable/disable APM * [no-]allow[-_]ints allow interrupts during BIOS calls * [no-]broken[-_]psr BIOS has a broken GetPowerStatus call * [no-]realmode[-_]power[-_]off switch to real mode before * powering off * [no-]debug log some debugging messages * [no-]power[-_]off power off on shutdown * bounce[-_]interval=<n> number of ticks to ignore suspend * bounces * idle[-_]threshold=<n> System idle percentage above which to * make APM BIOS idle calls. Set it to * 100 to disable. * idle[-_]period=<n> Period (in 1/100s of a second) over * which the idle percentage is * calculated. *//* KNOWN PROBLEM MACHINES: * * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant * [Confirmed by TI representative] * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification * [Confirmed by BIOS disassembly] * [This may work now ...] * P: Toshiba 1950S: battery life information only gets updated after resume * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>] * ?: AcerNote-950: oops on reading /proc/apm - workaround is a WIP * Neale Banks <neale@lowendale.com.au> December 2000 * * Legend: U = unusable with APM patches * P = partially usable with APM patches *//* * Define as 1 to make the driver always call the APM BIOS busy * routine even if the clock was not reported as slowed by the * idle routine. Otherwise, define as 0. */#define ALWAYS_CALL_BUSY 1/* * Define to make the APM BIOS calls zero all data segment registers (so * that an incorrect BIOS implementation will cause a kernel panic if it * tries to write to arbitrary memory). */#define APM_ZERO_SEGS/* * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is * supposed to provide limit information that it recognizes. Many machines * do this correctly, but many others do not restrict themselves to their * claimed limit. When this happens, they will cause a segmentation * violation in the kernel at boot time. Most BIOS's, however, will * respect a 64k limit, so we use that. If you want to be pedantic and * hold your BIOS to its claims, then undefine this. */#define APM_RELAX_SEGMENTS/* * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend. * This patched by Chad Miller <cmiller@surfsouth.com>, original code by * David Chen <chen@ctpa04.mit.edu> */#undef INIT_TIMER_AFTER_SUSPEND#ifdef INIT_TIMER_AFTER_SUSPEND#include <linux/timex.h>#include <asm/io.h>#include <linux/delay.h>#endif/* * Need to poll the APM BIOS every second */#define APM_CHECK_TIMEOUT (HZ)/* * Ignore suspend events for this amount of time after a resume */#define DEFAULT_BOUNCE_INTERVAL (3 * HZ)/* * Save a segment register away */#define savesegment(seg, where) \ __asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))/* * Maximum number of events stored */#define APM_MAX_EVENTS 20/* * The per-file APM data */struct apm_user { int magic; struct apm_user * next; int suser: 1; int writer: 1; int reader: 1; int suspend_wait: 1; int suspend_result; int suspends_pending; int standbys_pending; int suspends_read; int standbys_read; int event_head; int event_tail; apm_event_t events[APM_MAX_EVENTS];};/* * The magic number in apm_user */#define APM_BIOS_MAGIC 0x4101/* * idle percentage above which bios idle calls are done */#ifdef CONFIG_APM_CPU_IDLE#define DEFAULT_IDLE_THRESHOLD 95#else#define DEFAULT_IDLE_THRESHOLD 100#endif#define DEFAULT_IDLE_PERIOD (100 / 3)/* * Local variables */static struct { unsigned long offset; unsigned short segment;} apm_bios_entry;static int clock_slowed;static int idle_threshold = DEFAULT_IDLE_THRESHOLD;static int idle_period = DEFAULT_IDLE_PERIOD;static int set_pm_idle;static int suspends_pending;static int standbys_pending;static int ignore_sys_suspend;static int ignore_normal_resume;static int bounce_interval = DEFAULT_BOUNCE_INTERVAL;#ifdef CONFIG_APM_RTC_IS_GMT# define clock_cmos_diff 0# define got_clock_diff 1#elsestatic long clock_cmos_diff;static int got_clock_diff;#endifstatic int debug;static int apm_disabled = -1;#ifdef CONFIG_SMPstatic int power_off;#elsestatic int power_off = 1;#endif#ifdef CONFIG_APM_REAL_MODE_POWER_OFFstatic int realmode_power_off = 1;#elsestatic int realmode_power_off;#endifstatic int exit_kapmd;static int kapmd_running;#ifdef CONFIG_APM_ALLOW_INTSstatic int allow_ints = 1;#elsestatic int allow_ints;#endifstatic int broken_psr;static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);static struct apm_user * user_list;static char driver_version[] = "1.16"; /* no spaces *//* * APM event names taken from the APM 1.2 specification. These are * the message codes that the BIOS uses to tell us about events */static char * apm_event_name[] = { "system standby", "system suspend", "normal resume", "critical resume", "low battery", "power status change", "update time", "critical suspend", "user standby", "user suspend", "system standby resume", "capabilities change"};#define NR_APM_EVENT_NAME \ (sizeof(apm_event_name) / sizeof(apm_event_name[0]))typedef struct lookup_t { int key; char * msg;} lookup_t;/* * The BIOS returns a set of standard error codes in AX when the * carry flag is set. */ static const lookup_t error_table[] = {/* N/A { APM_SUCCESS, "Operation succeeded" }, */ { APM_DISABLED, "Power management disabled" }, { APM_CONNECTED, "Real mode interface already connected" }, { APM_NOT_CONNECTED, "Interface not connected" }, { APM_16_CONNECTED, "16 bit interface already connected" },/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ { APM_32_CONNECTED, "32 bit interface already connected" }, { APM_32_UNSUPPORTED, "32 bit interface not supported" }, { APM_BAD_DEVICE, "Unrecognized device ID" }, { APM_BAD_PARAM, "Parameter out of range" }, { APM_NOT_ENGAGED, "Interface not engaged" }, { APM_BAD_FUNCTION, "Function not supported" }, { APM_RESUME_DISABLED, "Resume timer disabled" }, { APM_BAD_STATE, "Unable to enter requested state" },/* N/A { APM_NO_EVENTS, "No events pending" }, */ { APM_NO_ERROR, "BIOS did not set a return code" }, { APM_NOT_PRESENT, "No APM present" }};#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))/** * apm_error - display an APM error * @str: information string * @err: APM BIOS return code * * Write a meaningful log entry to the kernel log in the event of * an APM error. */ static void apm_error(char *str, int err){ int i; for (i = 0; i < ERROR_COUNT; i++) if (error_table[i].key == err) break; if (i < ERROR_COUNT) printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg); else printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n", str, err);}/* * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and * apm_info.allow_ints, we are being really paranoid here! Not only * are interrupts disabled, but all the segment registers (except SS) * are saved and zeroed this means that if the BIOS tries to reference * any data without explicitly loading the segment registers, the kernel * will fault immediately rather than have some unforeseen circumstances * for the rest of the kernel. And it will be very obvious! :-) Doing * this depends on CS referring to the same physical memory as DS so that * DS can be zeroed before the call. Unfortunately, we can't do anything * about the stack segment/pointer. Also, we tell the compiler that * everything could change. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -