[PATCH] i386: Convert /proc/apm to seqfile
[deliverable/linux.git] / arch / i386 / kernel / apm.c
CommitLineData
1da177e4
LT
1/* -*- linux-c -*-
2 * APM BIOS driver for Linux
3 * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au)
4 *
5 * Initial development of this driver was funded by NEC Australia P/L
6 * and NEC Corporation
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * October 1995, Rik Faith (faith@cs.unc.edu):
19 * Minor enhancements and updates (to the patch set) for 1.3.x
20 * Documentation
21 * January 1996, Rik Faith (faith@cs.unc.edu):
22 * Make /proc/apm easy to format (bump driver version)
23 * March 1996, Rik Faith (faith@cs.unc.edu):
24 * Prohibit APM BIOS calls unless apm_enabled.
25 * (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
26 * April 1996, Stephen Rothwell (sfr@canb.auug.org.au)
27 * Version 1.0 and 1.1
28 * May 1996, Version 1.2
29 * Feb 1998, Version 1.3
30 * Feb 1998, Version 1.4
31 * Aug 1998, Version 1.5
32 * Sep 1998, Version 1.6
33 * Nov 1998, Version 1.7
34 * Jan 1999, Version 1.8
35 * Jan 1999, Version 1.9
36 * Oct 1999, Version 1.10
37 * Nov 1999, Version 1.11
38 * Jan 2000, Version 1.12
39 * Feb 2000, Version 1.13
40 * Nov 2000, Version 1.14
41 * Oct 2001, Version 1.15
42 * Jan 2002, Version 1.16
43 * Oct 2002, Version 1.16ac
44 *
45 * History:
46 * 0.6b: first version in official kernel, Linux 1.3.46
47 * 0.7: changed /proc/apm format, Linux 1.3.58
48 * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
49 * 0.9: only call bios if bios is present, Linux 1.3.72
50 * 1.0: use fixed device number, consolidate /proc/apm into this file,
51 * Linux 1.3.85
52 * 1.1: support user-space standby and suspend, power off after system
53 * halted, Linux 1.3.98
54 * 1.2: When resetting RTC after resume, take care so that the time
55 * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
56 * <jtoth@princeton.edu>); improve interaction between
57 * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
58 * 1.2a:Simple change to stop mysterious bug reports with SMP also added
59 * levels to the printk calls. APM is not defined for SMP machines.
60 * The new replacment for it is, but Linux doesn't yet support this.
61 * Alan Cox Linux 2.1.55
62 * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
63 * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
64 * Dean Gaudet <dgaudet@arctic.org>.
65 * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
66 * 1.5: Fix segment register reloading (in case of bad segments saved
67 * across BIOS call).
68 * Stephen Rothwell
69 * 1.6: Cope with complier/assembler differences.
70 * Only try to turn off the first display device.
71 * Fix OOPS at power off with no APM BIOS by Jan Echternach
72 * <echter@informatik.uni-rostock.de>
73 * Stephen Rothwell
74 * 1.7: Modify driver's cached copy of the disabled/disengaged flags
75 * to reflect current state of APM BIOS.
76 * Chris Rankin <rankinc@bellsouth.net>
77 * Reset interrupt 0 timer to 100Hz after suspend
78 * Chad Miller <cmiller@surfsouth.com>
79 * Add CONFIG_APM_IGNORE_SUSPEND_BOUNCE
80 * Richard Gooch <rgooch@atnf.csiro.au>
81 * Allow boot time disabling of APM
82 * Make boot messages far less verbose by default
83 * Make asm safer
84 * Stephen Rothwell
85 * 1.8: Add CONFIG_APM_RTC_IS_GMT
86 * Richard Gooch <rgooch@atnf.csiro.au>
87 * change APM_NOINTS to CONFIG_APM_ALLOW_INTS
88 * remove dependency on CONFIG_PROC_FS
89 * Stephen Rothwell
90 * 1.9: Fix small typo. <laslo@wodip.opole.pl>
91 * Try to cope with BIOS's that need to have all display
92 * devices blanked and not just the first one.
93 * Ross Paterson <ross@soi.city.ac.uk>
94 * Fix segment limit setting it has always been wrong as
95 * the segments needed to have byte granularity.
96 * Mark a few things __init.
97 * Add hack to allow power off of SMP systems by popular request.
98 * Use CONFIG_SMP instead of __SMP__
99 * Ignore BOUNCES for three seconds.
100 * Stephen Rothwell
101 * 1.10: Fix for Thinkpad return code.
102 * Merge 2.2 and 2.3 drivers.
103 * Remove APM dependencies in arch/i386/kernel/process.c
104 * Remove APM dependencies in drivers/char/sysrq.c
105 * Reset time across standby.
106 * Allow more inititialisation on SMP.
107 * Remove CONFIG_APM_POWER_OFF and make it boot time
108 * configurable (default on).
109 * Make debug only a boot time parameter (remove APM_DEBUG).
110 * Try to blank all devices on any error.
111 * 1.11: Remove APM dependencies in drivers/char/console.c
112 * Check nr_running to detect if we are idle (from
113 * Borislav Deianov <borislav@lix.polytechnique.fr>)
114 * Fix for bioses that don't zero the top part of the
115 * entrypoint offset (Mario Sitta <sitta@al.unipmn.it>)
116 * (reported by Panos Katsaloulis <teras@writeme.com>).
117 * Real mode power off patch (Walter Hofmann
118 * <Walter.Hofmann@physik.stud.uni-erlangen.de>).
119 * 1.12: Remove CONFIG_SMP as the compiler will optimize
120 * the code away anyway (smp_num_cpus == 1 in UP)
121 * noted by Artur Skawina <skawina@geocities.com>.
122 * Make power off under SMP work again.
123 * Fix thinko with initial engaging of BIOS.
124 * Make sure power off only happens on CPU 0
125 * (Paul "Rusty" Russell <rusty@rustcorp.com.au>).
126 * Do error notification to user mode if BIOS calls fail.
127 * Move entrypoint offset fix to ...boot/setup.S
128 * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
129 * Remove smp-power-off. SMP users must now specify
130 * "apm=power-off" on the kernel command line. Suggested
131 * by Jim Avera <jima@hal.com>, modified by Alan Cox
132 * <alan@lxorguk.ukuu.org.uk>.
133 * Register the /proc/apm entry even on SMP so that
134 * scripts that check for it before doing power off
135 * work (Jim Avera <jima@hal.com>).
136 * 1.13: Changes for new pm_ interfaces (Andy Henroid
137 * <andy_henroid@yahoo.com>).
138 * Modularize the code.
139 * Fix the Thinkpad (again) :-( (CONFIG_APM_IGNORE_MULTIPLE_SUSPENDS
140 * is now the way life works).
141 * Fix thinko in suspend() (wrong return).
142 * Notify drivers on critical suspend.
143 * Make kapmd absorb more idle time (Pavel Machek <pavel@suse.cz>
144 * modified by sfr).
145 * Disable interrupts while we are suspended (Andy Henroid
146 * <andy_henroid@yahoo.com> fixed by sfr).
147 * Make power off work on SMP again (Tony Hoyle
148 * <tmh@magenta-logic.com> and <zlatko@iskon.hr>) modified by sfr.
149 * Remove CONFIG_APM_SUSPEND_BOUNCE. The bounce ignore
150 * interval is now configurable.
151 * 1.14: Make connection version persist across module unload/load.
152 * Enable and engage power management earlier.
153 * Disengage power management on module unload.
154 * Changed to use the sysrq-register hack for registering the
155 * power off function called by magic sysrq based upon discussions
156 * in irc://irc.openprojects.net/#kernelnewbies
157 * (Crutcher Dunnavant <crutcher+kernel@datastacks.com>).
158 * Make CONFIG_APM_REAL_MODE_POWER_OFF run time configurable.
159 * (Arjan van de Ven <arjanv@redhat.com>) modified by sfr.
160 * Work around byte swap bug in one of the Vaio's BIOS's
161 * (Marc Boucher <marc@mbsi.ca>).
162 * Exposed the disable flag to dmi so that we can handle known
163 * broken APM (Alan Cox <alan@redhat.com>).
164 * 1.14ac: If the BIOS says "I slowed the CPU down" then don't spin
165 * calling it - instead idle. (Alan Cox <alan@redhat.com>)
166 * If an APM idle fails log it and idle sensibly
167 * 1.15: Don't queue events to clients who open the device O_WRONLY.
168 * Don't expect replies from clients who open the device O_RDONLY.
169 * (Idea from Thomas Hood)
170 * Minor waitqueue cleanups. (John Fremlin <chief@bandits.org>)
171 * 1.16: Fix idle calling. (Andreas Steinmetz <ast@domdv.de> et al.)
172 * Notify listeners of standby or suspend events before notifying
173 * drivers. Return EBUSY to ioctl() if suspend is rejected.
174 * (Russell King <rmk@arm.linux.org.uk> and Thomas Hood)
175 * Ignore first resume after we generate our own resume event
176 * after a suspend (Thomas Hood)
177 * Daemonize now gets rid of our controlling terminal (sfr).
178 * CONFIG_APM_CPU_IDLE now just affects the default value of
179 * idle_threshold (sfr).
180 * Change name of kernel apm daemon (as it no longer idles) (sfr).
181 * 1.16ac: Fix up SMP support somewhat. You can now force SMP on and we
182 * make _all_ APM calls on the CPU#0. Fix unsafe sign bug.
183 * TODO: determine if its "boot CPU" or "CPU0" we want to lock to.
184 *
185 * APM 1.1 Reference:
186 *
187 * Intel Corporation, Microsoft Corporation. Advanced Power Management
188 * (APM) BIOS Interface Specification, Revision 1.1, September 1993.
189 * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
190 *
191 * [This document is available free from Intel by calling 800.628.8686 (fax
192 * 916.356.6100) or 800.548.4725; or via anonymous ftp from
193 * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
194 * available from Microsoft by calling 206.882.8080.]
195 *
196 * APM 1.2 Reference:
197 * Intel Corporation, Microsoft Corporation. Advanced Power Management
198 * (APM) BIOS Interface Specification, Revision 1.2, February 1996.
199 *
200 * [This document is available from Microsoft at:
3f4b23e9 201 * http://www.microsoft.com/whdc/archive/amp_12.mspx]
1da177e4
LT
202 */
203
1da177e4
LT
204#include <linux/module.h>
205
206#include <linux/poll.h>
207#include <linux/types.h>
208#include <linux/stddef.h>
209#include <linux/timer.h>
210#include <linux/fcntl.h>
211#include <linux/slab.h>
212#include <linux/stat.h>
213#include <linux/proc_fs.h>
016d6f35 214#include <linux/seq_file.h>
1da177e4
LT
215#include <linux/miscdevice.h>
216#include <linux/apm_bios.h>
217#include <linux/init.h>
218#include <linux/time.h>
219#include <linux/sched.h>
220#include <linux/pm.h>
bca73e4b 221#include <linux/pm_legacy.h>
a9415644 222#include <linux/capability.h>
1da177e4
LT
223#include <linux/device.h>
224#include <linux/kernel.h>
225#include <linux/smp.h>
226#include <linux/smp_lock.h>
227#include <linux/dmi.h>
228#include <linux/suspend.h>
fc09561d 229#include <linux/kthread.h>
1da177e4
LT
230
231#include <asm/system.h>
232#include <asm/uaccess.h>
233#include <asm/desc.h>
306e440d 234#include <asm/i8253.h>
6020c8f3 235#include <asm/paravirt.h>
1da177e4
LT
236
237#include "io_ports.h"
238
1da177e4
LT
239extern unsigned long get_cmos_time(void);
240extern void machine_real_restart(unsigned char *, int);
241
242#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
243extern int (*console_blank_hook)(int);
244#endif
245
246/*
247 * The apm_bios device is one of the misc char devices.
248 * This is its minor number.
249 */
250#define APM_MINOR_DEV 134
251
252/*
253 * See Documentation/Config.help for the configuration options.
254 *
255 * Various options can be changed at boot time as follows:
256 * (We allow underscores for compatibility with the modules code)
257 * apm=on/off enable/disable APM
258 * [no-]allow[-_]ints allow interrupts during BIOS calls
259 * [no-]broken[-_]psr BIOS has a broken GetPowerStatus call
260 * [no-]realmode[-_]power[-_]off switch to real mode before
261 * powering off
262 * [no-]debug log some debugging messages
263 * [no-]power[-_]off power off on shutdown
264 * [no-]smp Use apm even on an SMP box
265 * bounce[-_]interval=<n> number of ticks to ignore suspend
266 * bounces
267 * idle[-_]threshold=<n> System idle percentage above which to
268 * make APM BIOS idle calls. Set it to
269 * 100 to disable.
270 * idle[-_]period=<n> Period (in 1/100s of a second) over
271 * which the idle percentage is
272 * calculated.
273 */
274
275/* KNOWN PROBLEM MACHINES:
276 *
277 * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
278 * [Confirmed by TI representative]
279 * ?: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
280 * [Confirmed by BIOS disassembly]
281 * [This may work now ...]
282 * P: Toshiba 1950S: battery life information only gets updated after resume
283 * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
284 * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
285 * ?: AcerNote-950: oops on reading /proc/apm - workaround is a WIP
286 * Neale Banks <neale@lowendale.com.au> December 2000
287 *
288 * Legend: U = unusable with APM patches
289 * P = partially usable with APM patches
290 */
291
292/*
293 * Define as 1 to make the driver always call the APM BIOS busy
294 * routine even if the clock was not reported as slowed by the
295 * idle routine. Otherwise, define as 0.
296 */
297#define ALWAYS_CALL_BUSY 1
298
299/*
300 * Define to make the APM BIOS calls zero all data segment registers (so
301 * that an incorrect BIOS implementation will cause a kernel panic if it
302 * tries to write to arbitrary memory).
303 */
304#define APM_ZERO_SEGS
305
306#include "apm.h"
307
1da177e4
LT
308/*
309 * Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
310 * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
311 * David Chen <chen@ctpa04.mit.edu>
312 */
313#undef INIT_TIMER_AFTER_SUSPEND
314
315#ifdef INIT_TIMER_AFTER_SUSPEND
316#include <linux/timex.h>
317#include <asm/io.h>
318#include <linux/delay.h>
319#endif
320
321/*
322 * Need to poll the APM BIOS every second
323 */
324#define APM_CHECK_TIMEOUT (HZ)
325
326/*
327 * Ignore suspend events for this amount of time after a resume
328 */
329#define DEFAULT_BOUNCE_INTERVAL (3 * HZ)
330
331/*
332 * Maximum number of events stored
333 */
334#define APM_MAX_EVENTS 20
335
336/*
337 * The per-file APM data
338 */
339struct apm_user {
340 int magic;
341 struct apm_user * next;
77617bd8
DP
342 unsigned int suser: 1;
343 unsigned int writer: 1;
344 unsigned int reader: 1;
345 unsigned int suspend_wait: 1;
1da177e4
LT
346 int suspend_result;
347 int suspends_pending;
348 int standbys_pending;
349 int suspends_read;
350 int standbys_read;
351 int event_head;
352 int event_tail;
353 apm_event_t events[APM_MAX_EVENTS];
354};
355
356/*
357 * The magic number in apm_user
358 */
359#define APM_BIOS_MAGIC 0x4101
360
361/*
362 * idle percentage above which bios idle calls are done
363 */
364#ifdef CONFIG_APM_CPU_IDLE
365#define DEFAULT_IDLE_THRESHOLD 95
366#else
367#define DEFAULT_IDLE_THRESHOLD 100
368#endif
369#define DEFAULT_IDLE_PERIOD (100 / 3)
370
371/*
372 * Local variables
373 */
374static struct {
375 unsigned long offset;
376 unsigned short segment;
377} apm_bios_entry;
378static int clock_slowed;
87af2ffd
AM
379static int idle_threshold __read_mostly = DEFAULT_IDLE_THRESHOLD;
380static int idle_period __read_mostly = DEFAULT_IDLE_PERIOD;
1da177e4
LT
381static int set_pm_idle;
382static int suspends_pending;
383static int standbys_pending;
384static int ignore_sys_suspend;
385static int ignore_normal_resume;
87af2ffd 386static int bounce_interval __read_mostly = DEFAULT_BOUNCE_INTERVAL;
1da177e4
LT
387
388#ifdef CONFIG_APM_RTC_IS_GMT
389# define clock_cmos_diff 0
390# define got_clock_diff 1
391#else
392static long clock_cmos_diff;
393static int got_clock_diff;
394#endif
87af2ffd
AM
395static int debug __read_mostly;
396static int smp __read_mostly;
1da177e4
LT
397static int apm_disabled = -1;
398#ifdef CONFIG_SMP
399static int power_off;
400#else
401static int power_off = 1;
402#endif
403#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
404static int realmode_power_off = 1;
405#else
406static int realmode_power_off;
407#endif
1da177e4
LT
408#ifdef CONFIG_APM_ALLOW_INTS
409static int allow_ints = 1;
410#else
411static int allow_ints;
412#endif
413static int broken_psr;
414
415static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
416static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
417static struct apm_user * user_list;
418static DEFINE_SPINLOCK(user_list_lock);
87af2ffd 419static const struct desc_struct bad_bios_desc = { 0, 0x00409200 };
1da177e4 420
87af2ffd 421static const char driver_version[] = "1.16ac"; /* no spaces */
1da177e4 422
fc09561d
SH
423static struct task_struct *kapmd_task;
424
1da177e4
LT
425/*
426 * APM event names taken from the APM 1.2 specification. These are
427 * the message codes that the BIOS uses to tell us about events
428 */
87af2ffd 429static const char * const apm_event_name[] = {
1da177e4
LT
430 "system standby",
431 "system suspend",
432 "normal resume",
433 "critical resume",
434 "low battery",
435 "power status change",
436 "update time",
437 "critical suspend",
438 "user standby",
439 "user suspend",
440 "system standby resume",
441 "capabilities change"
442};
38e548ee 443#define NR_APM_EVENT_NAME ARRAY_SIZE(apm_event_name)
1da177e4
LT
444
445typedef struct lookup_t {
446 int key;
447 char * msg;
448} lookup_t;
449
450/*
451 * The BIOS returns a set of standard error codes in AX when the
452 * carry flag is set.
453 */
454
455static const lookup_t error_table[] = {
456/* N/A { APM_SUCCESS, "Operation succeeded" }, */
457 { APM_DISABLED, "Power management disabled" },
458 { APM_CONNECTED, "Real mode interface already connected" },
459 { APM_NOT_CONNECTED, "Interface not connected" },
460 { APM_16_CONNECTED, "16 bit interface already connected" },
461/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
462 { APM_32_CONNECTED, "32 bit interface already connected" },
463 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
464 { APM_BAD_DEVICE, "Unrecognized device ID" },
465 { APM_BAD_PARAM, "Parameter out of range" },
466 { APM_NOT_ENGAGED, "Interface not engaged" },
467 { APM_BAD_FUNCTION, "Function not supported" },
468 { APM_RESUME_DISABLED, "Resume timer disabled" },
469 { APM_BAD_STATE, "Unable to enter requested state" },
470/* N/A { APM_NO_EVENTS, "No events pending" }, */
471 { APM_NO_ERROR, "BIOS did not set a return code" },
472 { APM_NOT_PRESENT, "No APM present" }
473};
38e548ee 474#define ERROR_COUNT ARRAY_SIZE(error_table)
1da177e4
LT
475
476/**
477 * apm_error - display an APM error
478 * @str: information string
479 * @err: APM BIOS return code
480 *
481 * Write a meaningful log entry to the kernel log in the event of
482 * an APM error.
483 */
484
485static void apm_error(char *str, int err)
486{
487 int i;
488
489 for (i = 0; i < ERROR_COUNT; i++)
490 if (error_table[i].key == err) break;
491 if (i < ERROR_COUNT)
492 printk(KERN_NOTICE "apm: %s: %s\n", str, error_table[i].msg);
493 else
494 printk(KERN_NOTICE "apm: %s: unknown error code %#2.2x\n",
495 str, err);
496}
497
498/*
499 * Lock APM functionality to physical CPU 0
500 */
501
502#ifdef CONFIG_SMP
503
504static cpumask_t apm_save_cpus(void)
505{
506 cpumask_t x = current->cpus_allowed;
507 /* Some bioses don't like being called from CPU != 0 */
508 set_cpus_allowed(current, cpumask_of_cpu(0));
509 BUG_ON(smp_processor_id() != 0);
510 return x;
511}
512
513static inline void apm_restore_cpus(cpumask_t mask)
514{
515 set_cpus_allowed(current, mask);
516}
517
518#else
519
520/*
521 * No CPU lockdown needed on a uniprocessor
522 */
523
524#define apm_save_cpus() (current->cpus_allowed)
525#define apm_restore_cpus(x) (void)(x)
526
527#endif
528
529/*
530 * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and
531 * apm_info.allow_ints, we are being really paranoid here! Not only
532 * are interrupts disabled, but all the segment registers (except SS)
533 * are saved and zeroed this means that if the BIOS tries to reference
534 * any data without explicitly loading the segment registers, the kernel
535 * will fault immediately rather than have some unforeseen circumstances
536 * for the rest of the kernel. And it will be very obvious! :-) Doing
537 * this depends on CS referring to the same physical memory as DS so that
538 * DS can be zeroed before the call. Unfortunately, we can't do anything
539 * about the stack segment/pointer. Also, we tell the compiler that
540 * everything could change.
541 *
542 * Also, we KNOW that for the non error case of apm_bios_call, there
543 * is no useful data returned in the low order 8 bits of eax.
544 */
3864c489
PZ
545
546static inline unsigned long __apm_irq_save(void)
547{
548 unsigned long flags;
549 local_save_flags(flags);
550 if (apm_info.allow_ints) {
551 if (irqs_disabled_flags(flags))
552 local_irq_enable();
553 } else
554 local_irq_disable();
555
556 return flags;
557}
558
559#define apm_irq_save(flags) \
560 do { flags = __apm_irq_save(); } while (0)
561
562static inline void apm_irq_restore(unsigned long flags)
563{
564 if (irqs_disabled_flags(flags))
1da177e4 565 local_irq_disable();
3864c489
PZ
566 else if (irqs_disabled())
567 local_irq_enable();
568}
1da177e4
LT
569
570#ifdef APM_ZERO_SEGS
571# define APM_DECL_SEGS \
572 unsigned int saved_fs; unsigned int saved_gs;
573# define APM_DO_SAVE_SEGS \
574 savesegment(fs, saved_fs); savesegment(gs, saved_gs)
575# define APM_DO_RESTORE_SEGS \
576 loadsegment(fs, saved_fs); loadsegment(gs, saved_gs)
577#else
578# define APM_DECL_SEGS
579# define APM_DO_SAVE_SEGS
580# define APM_DO_RESTORE_SEGS
581#endif
582
583/**
584 * apm_bios_call - Make an APM BIOS 32bit call
585 * @func: APM function to execute
586 * @ebx_in: EBX register for call entry
587 * @ecx_in: ECX register for call entry
588 * @eax: EAX register return
589 * @ebx: EBX register return
590 * @ecx: ECX register return
591 * @edx: EDX register return
592 * @esi: ESI register return
593 *
594 * Make an APM call using the 32bit protected mode interface. The
595 * caller is responsible for knowing if APM BIOS is configured and
596 * enabled. This call can disable interrupts for a long period of
597 * time on some laptops. The return value is in AH and the carry
598 * flag is loaded into AL. If there is an error, then the error
599 * code is returned in AH (bits 8-15 of eax) and this function
600 * returns non-zero.
601 */
602
603static u8 apm_bios_call(u32 func, u32 ebx_in, u32 ecx_in,
604 u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
605{
606 APM_DECL_SEGS
607 unsigned long flags;
608 cpumask_t cpus;
609 int cpu;
610 struct desc_struct save_desc_40;
251e6912 611 struct desc_struct *gdt;
1da177e4
LT
612
613 cpus = apm_save_cpus();
614
615 cpu = get_cpu();
251e6912
ZA
616 gdt = get_cpu_gdt_table(cpu);
617 save_desc_40 = gdt[0x40 / 8];
618 gdt[0x40 / 8] = bad_bios_desc;
1da177e4 619
3864c489 620 apm_irq_save(flags);
1da177e4
LT
621 APM_DO_SAVE_SEGS;
622 apm_bios_call_asm(func, ebx_in, ecx_in, eax, ebx, ecx, edx, esi);
623 APM_DO_RESTORE_SEGS;
3864c489 624 apm_irq_restore(flags);
251e6912 625 gdt[0x40 / 8] = save_desc_40;
1da177e4
LT
626 put_cpu();
627 apm_restore_cpus(cpus);
628
629 return *eax & 0xff;
630}
631
632/**
633 * apm_bios_call_simple - make a simple APM BIOS 32bit call
634 * @func: APM function to invoke
635 * @ebx_in: EBX register value for BIOS call
636 * @ecx_in: ECX register value for BIOS call
637 * @eax: EAX register on return from the BIOS call
638 *
87af2ffd 639 * Make a BIOS call that returns one value only, or just status.
1da177e4
LT
640 * If there is an error, then the error code is returned in AH
641 * (bits 8-15 of eax) and this function returns non-zero. This is
642 * used for simpler BIOS operations. This call may hold interrupts
643 * off for a long time on some laptops.
644 */
645
646static u8 apm_bios_call_simple(u32 func, u32 ebx_in, u32 ecx_in, u32 *eax)
647{
648 u8 error;
649 APM_DECL_SEGS
650 unsigned long flags;
651 cpumask_t cpus;
652 int cpu;
653 struct desc_struct save_desc_40;
251e6912 654 struct desc_struct *gdt;
1da177e4
LT
655
656 cpus = apm_save_cpus();
657
658 cpu = get_cpu();
251e6912
ZA
659 gdt = get_cpu_gdt_table(cpu);
660 save_desc_40 = gdt[0x40 / 8];
661 gdt[0x40 / 8] = bad_bios_desc;
1da177e4 662
3864c489 663 apm_irq_save(flags);
1da177e4
LT
664 APM_DO_SAVE_SEGS;
665 error = apm_bios_call_simple_asm(func, ebx_in, ecx_in, eax);
666 APM_DO_RESTORE_SEGS;
3864c489 667 apm_irq_restore(flags);
251e6912 668 gdt[0x40 / 8] = save_desc_40;
1da177e4
LT
669 put_cpu();
670 apm_restore_cpus(cpus);
671 return error;
672}
673
674/**
675 * apm_driver_version - APM driver version
676 * @val: loaded with the APM version on return
677 *
678 * Retrieve the APM version supported by the BIOS. This is only
679 * supported for APM 1.1 or higher. An error indicates APM 1.0 is
680 * probably present.
681 *
682 * On entry val should point to a value indicating the APM driver
683 * version with the high byte being the major and the low byte the
684 * minor number both in BCD
685 *
686 * On return it will hold the BIOS revision supported in the
687 * same format.
688 */
689
690static int apm_driver_version(u_short *val)
691{
692 u32 eax;
693
694 if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))
695 return (eax >> 8) & 0xff;
696 *val = eax;
697 return APM_SUCCESS;
698}
699
700/**
701 * apm_get_event - get an APM event from the BIOS
702 * @event: pointer to the event
703 * @info: point to the event information
704 *
705 * The APM BIOS provides a polled information for event
706 * reporting. The BIOS expects to be polled at least every second
707 * when events are pending. When a message is found the caller should
708 * poll until no more messages are present. However, this causes
709 * problems on some laptops where a suspend event notification is
710 * not cleared until it is acknowledged.
711 *
712 * Additional information is returned in the info pointer, providing
713 * that APM 1.2 is in use. If no messges are pending the value 0x80
714 * is returned (No power management events pending).
715 */
716
717static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
718{
719 u32 eax;
720 u32 ebx;
721 u32 ecx;
722 u32 dummy;
723
724 if (apm_bios_call(APM_FUNC_GET_EVENT, 0, 0, &eax, &ebx, &ecx,
725 &dummy, &dummy))
726 return (eax >> 8) & 0xff;
727 *event = ebx;
728 if (apm_info.connection_version < 0x0102)
729 *info = ~0; /* indicate info not valid */
730 else
731 *info = ecx;
732 return APM_SUCCESS;
733}
734
735/**
736 * set_power_state - set the power management state
737 * @what: which items to transition
738 * @state: state to transition to
739 *
740 * Request an APM change of state for one or more system devices. The
741 * processor state must be transitioned last of all. what holds the
742 * class of device in the upper byte and the device number (0xFF for
743 * all) for the object to be transitioned.
744 *
745 * The state holds the state to transition to, which may in fact
746 * be an acceptance of a BIOS requested state change.
747 */
748
749static int set_power_state(u_short what, u_short state)
750{
751 u32 eax;
752
753 if (apm_bios_call_simple(APM_FUNC_SET_STATE, what, state, &eax))
754 return (eax >> 8) & 0xff;
755 return APM_SUCCESS;
756}
757
758/**
759 * set_system_power_state - set system wide power state
760 * @state: which state to enter
761 *
762 * Transition the entire system into a new APM power state.
763 */
764
765static int set_system_power_state(u_short state)
766{
767 return set_power_state(APM_DEVICE_ALL, state);
768}
769
770/**
771 * apm_do_idle - perform power saving
772 *
773 * This function notifies the BIOS that the processor is (in the view
774 * of the OS) idle. It returns -1 in the event that the BIOS refuses
775 * to handle the idle request. On a success the function returns 1
776 * if the BIOS did clock slowing or 0 otherwise.
777 */
778
779static int apm_do_idle(void)
780{
781 u32 eax;
64c7c8f8
NP
782 u8 ret = 0;
783 int idled = 0;
784 int polling;
785
495ab9c0 786 polling = !!(current_thread_info()->status & TS_POLLING);
64c7c8f8 787 if (polling) {
495ab9c0 788 current_thread_info()->status &= ~TS_POLLING;
0888f06a
IM
789 /*
790 * TS_POLLING-cleared state must be visible before we
791 * test NEED_RESCHED:
792 */
793 smp_mb();
64c7c8f8
NP
794 }
795 if (!need_resched()) {
796 idled = 1;
797 ret = apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax);
798 }
799 if (polling)
495ab9c0 800 current_thread_info()->status |= TS_POLLING;
64c7c8f8
NP
801
802 if (!idled)
803 return 0;
804
805 if (ret) {
1da177e4
LT
806 static unsigned long t;
807
808 /* This always fails on some SMP boards running UP kernels.
809 * Only report the failure the first 5 times.
810 */
811 if (++t < 5)
812 {
813 printk(KERN_DEBUG "apm_do_idle failed (%d)\n",
814 (eax >> 8) & 0xff);
815 t = jiffies;
816 }
817 return -1;
818 }
819 clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0;
820 return clock_slowed;
821}
822
823/**
824 * apm_do_busy - inform the BIOS the CPU is busy
825 *
826 * Request that the BIOS brings the CPU back to full performance.
827 */
828
829static void apm_do_busy(void)
830{
831 u32 dummy;
832
833 if (clock_slowed || ALWAYS_CALL_BUSY) {
834 (void) apm_bios_call_simple(APM_FUNC_BUSY, 0, 0, &dummy);
835 clock_slowed = 0;
836 }
837}
838
839/*
840 * If no process has really been interested in
841 * the CPU for some time, we want to call BIOS
842 * power management - we probably want
843 * to conserve power.
844 */
845#define IDLE_CALC_LIMIT (HZ * 100)
846#define IDLE_LEAKY_MAX 16
847
87af2ffd 848static void (*original_pm_idle)(void) __read_mostly;
1da177e4 849
1da177e4
LT
850/**
851 * apm_cpu_idle - cpu idling for APM capable Linux
852 *
853 * This is the idling function the kernel executes when APM is available. It
854 * tries to do BIOS powermanagement based on the average system idle time.
855 * Furthermore it calls the system default idle routine.
856 */
857
858static void apm_cpu_idle(void)
859{
860 static int use_apm_idle; /* = 0 */
861 static unsigned int last_jiffies; /* = 0 */
862 static unsigned int last_stime; /* = 0 */
863
864 int apm_idle_done = 0;
865 unsigned int jiffies_since_last_check = jiffies - last_jiffies;
866 unsigned int bucket;
867
868recalc:
869 if (jiffies_since_last_check > IDLE_CALC_LIMIT) {
870 use_apm_idle = 0;
871 last_jiffies = jiffies;
872 last_stime = current->stime;
873 } else if (jiffies_since_last_check > idle_period) {
874 unsigned int idle_percentage;
875
876 idle_percentage = current->stime - last_stime;
877 idle_percentage *= 100;
878 idle_percentage /= jiffies_since_last_check;
879 use_apm_idle = (idle_percentage > idle_threshold);
880 if (apm_info.forbid_idle)
881 use_apm_idle = 0;
882 last_jiffies = jiffies;
883 last_stime = current->stime;
884 }
885
886 bucket = IDLE_LEAKY_MAX;
887
888 while (!need_resched()) {
889 if (use_apm_idle) {
890 unsigned int t;
891
892 t = jiffies;
893 switch (apm_do_idle()) {
894 case 0: apm_idle_done = 1;
895 if (t != jiffies) {
896 if (bucket) {
897 bucket = IDLE_LEAKY_MAX;
898 continue;
899 }
900 } else if (bucket) {
901 bucket--;
902 continue;
903 }
904 break;
905 case 1: apm_idle_done = 1;
906 break;
907 default: /* BIOS refused */
908 break;
909 }
910 }
911 if (original_pm_idle)
912 original_pm_idle();
913 else
914 default_idle();
915 jiffies_since_last_check = jiffies - last_jiffies;
916 if (jiffies_since_last_check > idle_period)
917 goto recalc;
918 }
919
920 if (apm_idle_done)
921 apm_do_busy();
922}
923
924/**
925 * apm_power_off - ask the BIOS to power off
926 *
927 * Handle the power off sequence. This is the one piece of code we
928 * will execute even on SMP machines. In order to deal with BIOS
929 * bugs we support real mode APM BIOS power off calls. We also make
930 * the SMP call on CPU0 as some systems will only honour this call
931 * on their first cpu.
932 */
933
934static void apm_power_off(void)
935{
936 unsigned char po_bios_call[] = {
937 0xb8, 0x00, 0x10, /* movw $0x1000,ax */
938 0x8e, 0xd0, /* movw ax,ss */
939 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
940 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
941 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
942 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
943 0xcd, 0x15 /* int $0x15 */
944 };
945
1da177e4 946 /* Some bioses don't like being called from CPU != 0 */
1da177e4
LT
947 if (apm_info.realmode_power_off)
948 {
949 (void)apm_save_cpus();
950 machine_real_restart(po_bios_call, sizeof(po_bios_call));
951 }
952 else
953 (void) set_system_power_state(APM_STATE_OFF);
954}
955
956#ifdef CONFIG_APM_DO_ENABLE
957
958/**
959 * apm_enable_power_management - enable BIOS APM power management
960 * @enable: enable yes/no
961 *
962 * Enable or disable the APM BIOS power services.
963 */
964
965static int apm_enable_power_management(int enable)
966{
967 u32 eax;
968
969 if ((enable == 0) && (apm_info.bios.flags & APM_BIOS_DISENGAGED))
970 return APM_NOT_ENGAGED;
971 if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,
972 enable, &eax))
973 return (eax >> 8) & 0xff;
974 if (enable)
975 apm_info.bios.flags &= ~APM_BIOS_DISABLED;
976 else
977 apm_info.bios.flags |= APM_BIOS_DISABLED;
978 return APM_SUCCESS;
979}
980#endif
981
982/**
983 * apm_get_power_status - get current power state
984 * @status: returned status
985 * @bat: battery info
986 * @life: estimated life
987 *
988 * Obtain the current power status from the APM BIOS. We return a
989 * status which gives the rough battery status, and current power
990 * source. The bat value returned give an estimate as a percentage
991 * of life and a status value for the battery. The estimated life
992 * if reported is a lifetime in secodnds/minutes at current powwer
993 * consumption.
994 */
995
996static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
997{
998 u32 eax;
999 u32 ebx;
1000 u32 ecx;
1001 u32 edx;
1002 u32 dummy;
1003
1004 if (apm_info.get_power_status_broken)
1005 return APM_32_UNSUPPORTED;
1006 if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0,
1007 &eax, &ebx, &ecx, &edx, &dummy))
1008 return (eax >> 8) & 0xff;
1009 *status = ebx;
1010 *bat = ecx;
1011 if (apm_info.get_power_status_swabinminutes) {
1012 *life = swab16((u16)edx);
1013 *life |= 0x8000;
1014 } else
1015 *life = edx;
1016 return APM_SUCCESS;
1017}
1018
1019#if 0
1020static int apm_get_battery_status(u_short which, u_short *status,
1021 u_short *bat, u_short *life, u_short *nbat)
1022{
1023 u32 eax;
1024 u32 ebx;
1025 u32 ecx;
1026 u32 edx;
1027 u32 esi;
1028
1029 if (apm_info.connection_version < 0x0102) {
1030 /* pretend we only have one battery. */
1031 if (which != 1)
1032 return APM_BAD_DEVICE;
1033 *nbat = 1;
1034 return apm_get_power_status(status, bat, life);
1035 }
1036
1037 if (apm_bios_call(APM_FUNC_GET_STATUS, (0x8000 | (which)), 0, &eax,
1038 &ebx, &ecx, &edx, &esi))
1039 return (eax >> 8) & 0xff;
1040 *status = ebx;
1041 *bat = ecx;
1042 *life = edx;
1043 *nbat = esi;
1044 return APM_SUCCESS;
1045}
1046#endif
1047
1048/**
1049 * apm_engage_power_management - enable PM on a device
1050 * @device: identity of device
1051 * @enable: on/off
1052 *
1053 * Activate or deactive power management on either a specific device
1054 * or the entire system (%APM_DEVICE_ALL).
1055 */
1056
1057static int apm_engage_power_management(u_short device, int enable)
1058{
1059 u32 eax;
1060
1061 if ((enable == 0) && (device == APM_DEVICE_ALL)
1062 && (apm_info.bios.flags & APM_BIOS_DISABLED))
1063 return APM_DISABLED;
1064 if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax))
1065 return (eax >> 8) & 0xff;
1066 if (device == APM_DEVICE_ALL) {
1067 if (enable)
1068 apm_info.bios.flags &= ~APM_BIOS_DISENGAGED;
1069 else
1070 apm_info.bios.flags |= APM_BIOS_DISENGAGED;
1071 }
1072 return APM_SUCCESS;
1073}
1074
1075#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1076
1077/**
1078 * apm_console_blank - blank the display
1079 * @blank: on/off
1080 *
1081 * Attempt to blank the console, firstly by blanking just video device
1082 * zero, and if that fails (some BIOSes don't support it) then it blanks
1083 * all video devices. Typically the BIOS will do laptop backlight and
1084 * monitor powerdown for us.
1085 */
1086
1087static int apm_console_blank(int blank)
1088{
87af2ffd
AM
1089 int error = APM_NOT_ENGAGED; /* silence gcc */
1090 int i;
3841b0a1
JC
1091 u_short state;
1092 static const u_short dev[3] = { 0x100, 0x1FF, 0x101 };
1da177e4
LT
1093
1094 state = blank ? APM_STATE_STANDBY : APM_STATE_READY;
3841b0a1
JC
1095
1096 for (i = 0; i < ARRAY_SIZE(dev); i++) {
1097 error = set_power_state(dev[i], state);
1098
1099 if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
1100 return 1;
1101
1102 if (error == APM_NOT_ENGAGED)
1103 break;
1da177e4 1104 }
3841b0a1 1105
73374454 1106 if (error == APM_NOT_ENGAGED) {
1da177e4
LT
1107 static int tried;
1108 int eng_error;
1109 if (tried++ == 0) {
1110 eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1);
1111 if (eng_error) {
1112 apm_error("set display", error);
1113 apm_error("engage interface", eng_error);
1114 return 0;
1115 } else
1116 return apm_console_blank(blank);
1117 }
1118 }
1119 apm_error("set display", error);
1120 return 0;
1121}
1122#endif
1123
1124static int queue_empty(struct apm_user *as)
1125{
1126 return as->event_head == as->event_tail;
1127}
1128
1129static apm_event_t get_queued_event(struct apm_user *as)
1130{
87af2ffd
AM
1131 if (++as->event_tail >= APM_MAX_EVENTS)
1132 as->event_tail = 0;
1da177e4
LT
1133 return as->events[as->event_tail];
1134}
1135
1136static void queue_event(apm_event_t event, struct apm_user *sender)
1137{
1138 struct apm_user * as;
1139
1140 spin_lock(&user_list_lock);
1141 if (user_list == NULL)
1142 goto out;
1143 for (as = user_list; as != NULL; as = as->next) {
1144 if ((as == sender) || (!as->reader))
1145 continue;
87af2ffd
AM
1146 if (++as->event_head >= APM_MAX_EVENTS)
1147 as->event_head = 0;
1148
1da177e4
LT
1149 if (as->event_head == as->event_tail) {
1150 static int notified;
1151
1152 if (notified++ == 0)
1153 printk(KERN_ERR "apm: an event queue overflowed\n");
87af2ffd
AM
1154 if (++as->event_tail >= APM_MAX_EVENTS)
1155 as->event_tail = 0;
1da177e4
LT
1156 }
1157 as->events[as->event_head] = event;
1158 if ((!as->suser) || (!as->writer))
1159 continue;
1160 switch (event) {
1161 case APM_SYS_SUSPEND:
1162 case APM_USER_SUSPEND:
1163 as->suspends_pending++;
1164 suspends_pending++;
1165 break;
1166
1167 case APM_SYS_STANDBY:
1168 case APM_USER_STANDBY:
1169 as->standbys_pending++;
1170 standbys_pending++;
1171 break;
1172 }
1173 }
1174 wake_up_interruptible(&apm_waitqueue);
1175out:
1176 spin_unlock(&user_list_lock);
1177}
1178
1179static void set_time(void)
1180{
c7f40ff1 1181 struct timespec ts;
1da177e4 1182 if (got_clock_diff) { /* Must know time zone in order to set clock */
c7f40ff1 1183 ts.tv_sec = get_cmos_time() + clock_cmos_diff;
1184 ts.tv_nsec = 0;
1185 do_settimeofday(&ts);
1da177e4
LT
1186 }
1187}
1188
1189static void get_time_diff(void)
1190{
1191#ifndef CONFIG_APM_RTC_IS_GMT
1192 /*
1193 * Estimate time zone so that set_time can update the clock
1194 */
1195 clock_cmos_diff = -get_cmos_time();
1196 clock_cmos_diff += get_seconds();
1197 got_clock_diff = 1;
1198#endif
1199}
1200
1201static void reinit_timer(void)
1202{
1203#ifdef INIT_TIMER_AFTER_SUSPEND
306e440d 1204 unsigned long flags;
1da177e4
LT
1205
1206 spin_lock_irqsave(&i8253_lock, flags);
1207 /* set the clock to 100 Hz */
1208 outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
1209 udelay(10);
1210 outb_p(LATCH & 0xff, PIT_CH0); /* LSB */
1211 udelay(10);
1212 outb(LATCH >> 8, PIT_CH0); /* MSB */
1213 udelay(10);
1214 spin_unlock_irqrestore(&i8253_lock, flags);
1215#endif
1216}
1217
1218static int suspend(int vetoable)
1219{
1220 int err;
1221 struct apm_user *as;
1222
1223 if (pm_send_all(PM_SUSPEND, (void *)3)) {
1224 /* Vetoed */
1225 if (vetoable) {
1226 if (apm_info.connection_version > 0x100)
1227 set_system_power_state(APM_STATE_REJECT);
1228 err = -EBUSY;
1229 ignore_sys_suspend = 0;
1230 printk(KERN_WARNING "apm: suspend was vetoed.\n");
1231 goto out;
1232 }
1233 printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
1234 }
1235
1236 device_suspend(PMSG_SUSPEND);
1237 local_irq_disable();
1238 device_power_down(PMSG_SUSPEND);
1239
1240 /* serialize with the timer interrupt */
1241 write_seqlock(&xtime_lock);
1242
1243 /* protect against access to timer chip registers */
1244 spin_lock(&i8253_lock);
1245
1246 get_time_diff();
1247 /*
1248 * Irq spinlock must be dropped around set_system_power_state.
1249 * We'll undo any timer changes due to interrupts below.
1250 */
1251 spin_unlock(&i8253_lock);
1252 write_sequnlock(&xtime_lock);
1253 local_irq_enable();
1254
1255 save_processor_state();
1256 err = set_system_power_state(APM_STATE_SUSPEND);
92c6dc59 1257 ignore_normal_resume = 1;
1da177e4
LT
1258 restore_processor_state();
1259
1260 local_irq_disable();
1da177e4 1261 set_time();
c7f40ff1 1262 reinit_timer();
1da177e4
LT
1263
1264 if (err == APM_NO_ERROR)
1265 err = APM_SUCCESS;
1266 if (err != APM_SUCCESS)
1267 apm_error("suspend", err);
1268 err = (err == APM_SUCCESS) ? 0 : -EIO;
1269 device_power_up();
1270 local_irq_enable();
1271 device_resume();
1272 pm_send_all(PM_RESUME, (void *)0);
1273 queue_event(APM_NORMAL_RESUME, NULL);
1274 out:
1275 spin_lock(&user_list_lock);
1276 for (as = user_list; as != NULL; as = as->next) {
1277 as->suspend_wait = 0;
1278 as->suspend_result = err;
1279 }
1280 spin_unlock(&user_list_lock);
1281 wake_up_interruptible(&apm_suspend_waitqueue);
1282 return err;
1283}
1284
1285static void standby(void)
1286{
1287 int err;
1288
1289 local_irq_disable();
1290 device_power_down(PMSG_SUSPEND);
1291 /* serialize with the timer interrupt */
1292 write_seqlock(&xtime_lock);
1293 /* If needed, notify drivers here */
1294 get_time_diff();
1295 write_sequnlock(&xtime_lock);
1296 local_irq_enable();
1297
1298 err = set_system_power_state(APM_STATE_STANDBY);
1299 if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
1300 apm_error("standby", err);
1301
1302 local_irq_disable();
1303 device_power_up();
1304 local_irq_enable();
1305}
1306
1307static apm_event_t get_event(void)
1308{
1309 int error;
87af2ffd 1310 apm_event_t event = APM_NO_EVENTS; /* silence gcc */
1da177e4
LT
1311 apm_eventinfo_t info;
1312
1313 static int notified;
1314
1315 /* we don't use the eventinfo */
1316 error = apm_get_event(&event, &info);
1317 if (error == APM_SUCCESS)
1318 return event;
1319
1320 if ((error != APM_NO_EVENTS) && (notified++ == 0))
1321 apm_error("get_event", error);
1322
1323 return 0;
1324}
1325
1326static void check_events(void)
1327{
1328 apm_event_t event;
1329 static unsigned long last_resume;
1330 static int ignore_bounce;
1331
1332 while ((event = get_event()) != 0) {
1333 if (debug) {
1334 if (event <= NR_APM_EVENT_NAME)
1335 printk(KERN_DEBUG "apm: received %s notify\n",
1336 apm_event_name[event - 1]);
1337 else
1338 printk(KERN_DEBUG "apm: received unknown "
1339 "event 0x%02x\n", event);
1340 }
1341 if (ignore_bounce
1342 && ((jiffies - last_resume) > bounce_interval))
1343 ignore_bounce = 0;
1344
1345 switch (event) {
1346 case APM_SYS_STANDBY:
1347 case APM_USER_STANDBY:
1348 queue_event(event, NULL);
1349 if (standbys_pending <= 0)
1350 standby();
1351 break;
1352
1353 case APM_USER_SUSPEND:
1354#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
1355 if (apm_info.connection_version > 0x100)
1356 set_system_power_state(APM_STATE_REJECT);
1357 break;
1358#endif
1359 case APM_SYS_SUSPEND:
1360 if (ignore_bounce) {
1361 if (apm_info.connection_version > 0x100)
1362 set_system_power_state(APM_STATE_REJECT);
1363 break;
1364 }
1365 /*
1366 * If we are already processing a SUSPEND,
1367 * then further SUSPEND events from the BIOS
1368 * will be ignored. We also return here to
1369 * cope with the fact that the Thinkpads keep
1370 * sending a SUSPEND event until something else
1371 * happens!
1372 */
1373 if (ignore_sys_suspend)
1374 return;
1375 ignore_sys_suspend = 1;
1376 queue_event(event, NULL);
1377 if (suspends_pending <= 0)
1378 (void) suspend(1);
1379 break;
1380
1381 case APM_NORMAL_RESUME:
1382 case APM_CRITICAL_RESUME:
1383 case APM_STANDBY_RESUME:
1384 ignore_sys_suspend = 0;
1385 last_resume = jiffies;
1386 ignore_bounce = 1;
1387 if ((event != APM_NORMAL_RESUME)
1388 || (ignore_normal_resume == 0)) {
1da177e4 1389 set_time();
1da177e4
LT
1390 device_resume();
1391 pm_send_all(PM_RESUME, (void *)0);
1392 queue_event(event, NULL);
1393 }
1394 ignore_normal_resume = 0;
1395 break;
1396
1397 case APM_CAPABILITY_CHANGE:
1398 case APM_LOW_BATTERY:
1399 case APM_POWER_STATUS_CHANGE:
1400 queue_event(event, NULL);
1401 /* If needed, notify drivers here */
1402 break;
1403
1404 case APM_UPDATE_TIME:
1da177e4 1405 set_time();
1da177e4
LT
1406 break;
1407
1408 case APM_CRITICAL_SUSPEND:
1409 /*
1410 * We are not allowed to reject a critical suspend.
1411 */
1412 (void) suspend(0);
1413 break;
1414 }
1415 }
1416}
1417
1418static void apm_event_handler(void)
1419{
1420 static int pending_count = 4;
1421 int err;
1422
1423 if ((standbys_pending > 0) || (suspends_pending > 0)) {
1424 if ((apm_info.connection_version > 0x100) &&
1425 (pending_count-- <= 0)) {
1426 pending_count = 4;
1427 if (debug)
1428 printk(KERN_DEBUG "apm: setting state busy\n");
1429 err = set_system_power_state(APM_STATE_BUSY);
1430 if (err)
1431 apm_error("busy", err);
1432 }
1433 } else
1434 pending_count = 4;
1435 check_events();
1436}
1437
1438/*
1439 * This is the APM thread main loop.
1440 */
1441
1442static void apm_mainloop(void)
1443{
1444 DECLARE_WAITQUEUE(wait, current);
1445
1446 add_wait_queue(&apm_waitqueue, &wait);
1447 set_current_state(TASK_INTERRUPTIBLE);
1448 for (;;) {
1449 schedule_timeout(APM_CHECK_TIMEOUT);
fc09561d 1450 if (kthread_should_stop())
1da177e4
LT
1451 break;
1452 /*
1453 * Ok, check all events, check for idle (and mark us sleeping
1454 * so as not to count towards the load average)..
1455 */
1456 set_current_state(TASK_INTERRUPTIBLE);
1457 apm_event_handler();
1458 }
1459 remove_wait_queue(&apm_waitqueue, &wait);
1460}
1461
1462static int check_apm_user(struct apm_user *as, const char *func)
1463{
1464 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
1465 printk(KERN_ERR "apm: %s passed bad filp\n", func);
1466 return 1;
1467 }
1468 return 0;
1469}
1470
1471static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
1472{
1473 struct apm_user * as;
1474 int i;
1475 apm_event_t event;
1476
1477 as = fp->private_data;
1478 if (check_apm_user(as, "read"))
1479 return -EIO;
1480 if ((int)count < sizeof(apm_event_t))
1481 return -EINVAL;
1482 if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))
1483 return -EAGAIN;
1484 wait_event_interruptible(apm_waitqueue, !queue_empty(as));
1485 i = count;
1486 while ((i >= sizeof(event)) && !queue_empty(as)) {
1487 event = get_queued_event(as);
1488 if (copy_to_user(buf, &event, sizeof(event))) {
1489 if (i < count)
1490 break;
1491 return -EFAULT;
1492 }
1493 switch (event) {
1494 case APM_SYS_SUSPEND:
1495 case APM_USER_SUSPEND:
1496 as->suspends_read++;
1497 break;
1498
1499 case APM_SYS_STANDBY:
1500 case APM_USER_STANDBY:
1501 as->standbys_read++;
1502 break;
1503 }
1504 buf += sizeof(event);
1505 i -= sizeof(event);
1506 }
1507 if (i < count)
1508 return count - i;
1509 if (signal_pending(current))
1510 return -ERESTARTSYS;
1511 return 0;
1512}
1513
1514static unsigned int do_poll(struct file *fp, poll_table * wait)
1515{
1516 struct apm_user * as;
1517
1518 as = fp->private_data;
1519 if (check_apm_user(as, "poll"))
1520 return 0;
1521 poll_wait(fp, &apm_waitqueue, wait);
1522 if (!queue_empty(as))
1523 return POLLIN | POLLRDNORM;
1524 return 0;
1525}
1526
1527static int do_ioctl(struct inode * inode, struct file *filp,
1528 u_int cmd, u_long arg)
1529{
1530 struct apm_user * as;
1531
1532 as = filp->private_data;
1533 if (check_apm_user(as, "ioctl"))
1534 return -EIO;
1535 if ((!as->suser) || (!as->writer))
1536 return -EPERM;
1537 switch (cmd) {
1538 case APM_IOC_STANDBY:
1539 if (as->standbys_read > 0) {
1540 as->standbys_read--;
1541 as->standbys_pending--;
1542 standbys_pending--;
1543 } else
1544 queue_event(APM_USER_STANDBY, as);
1545 if (standbys_pending <= 0)
1546 standby();
1547 break;
1548 case APM_IOC_SUSPEND:
1549 if (as->suspends_read > 0) {
1550 as->suspends_read--;
1551 as->suspends_pending--;
1552 suspends_pending--;
1553 } else
1554 queue_event(APM_USER_SUSPEND, as);
1555 if (suspends_pending <= 0) {
1556 return suspend(1);
1557 } else {
1558 as->suspend_wait = 1;
1559 wait_event_interruptible(apm_suspend_waitqueue,
1560 as->suspend_wait == 0);
1561 return as->suspend_result;
1562 }
1563 break;
1564 default:
1565 return -EINVAL;
1566 }
1567 return 0;
1568}
1569
1570static int do_release(struct inode * inode, struct file * filp)
1571{
1572 struct apm_user * as;
1573
1574 as = filp->private_data;
1575 if (check_apm_user(as, "release"))
1576 return 0;
1577 filp->private_data = NULL;
1578 if (as->standbys_pending > 0) {
1579 standbys_pending -= as->standbys_pending;
1580 if (standbys_pending <= 0)
1581 standby();
1582 }
1583 if (as->suspends_pending > 0) {
1584 suspends_pending -= as->suspends_pending;
1585 if (suspends_pending <= 0)
1586 (void) suspend(1);
1587 }
1588 spin_lock(&user_list_lock);
1589 if (user_list == as)
1590 user_list = as->next;
1591 else {
1592 struct apm_user * as1;
1593
1594 for (as1 = user_list;
1595 (as1 != NULL) && (as1->next != as);
1596 as1 = as1->next)
1597 ;
1598 if (as1 == NULL)
1599 printk(KERN_ERR "apm: filp not in user list\n");
1600 else
1601 as1->next = as->next;
1602 }
1603 spin_unlock(&user_list_lock);
1604 kfree(as);
1605 return 0;
1606}
1607
1608static int do_open(struct inode * inode, struct file * filp)
1609{
1610 struct apm_user * as;
1611
5cbded58 1612 as = kmalloc(sizeof(*as), GFP_KERNEL);
1da177e4
LT
1613 if (as == NULL) {
1614 printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
1615 sizeof(*as));
1616 return -ENOMEM;
1617 }
1618 as->magic = APM_BIOS_MAGIC;
1619 as->event_tail = as->event_head = 0;
1620 as->suspends_pending = as->standbys_pending = 0;
1621 as->suspends_read = as->standbys_read = 0;
1622 /*
1623 * XXX - this is a tiny bit broken, when we consider BSD
1624 * process accounting. If the device is opened by root, we
1625 * instantly flag that we used superuser privs. Who knows,
1626 * we might close the device immediately without doing a
1627 * privileged operation -- cevans
1628 */
1629 as->suser = capable(CAP_SYS_ADMIN);
1630 as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
1631 as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
1632 spin_lock(&user_list_lock);
1633 as->next = user_list;
1634 user_list = as;
1635 spin_unlock(&user_list_lock);
1636 filp->private_data = as;
1637 return 0;
1638}
1639
016d6f35 1640static int proc_apm_show(struct seq_file *m, void *v)
1da177e4 1641{
1da177e4
LT
1642 unsigned short bx;
1643 unsigned short cx;
1644 unsigned short dx;
1645 int error;
1646 unsigned short ac_line_status = 0xff;
1647 unsigned short battery_status = 0xff;
1648 unsigned short battery_flag = 0xff;
1649 int percentage = -1;
1650 int time_units = -1;
1651 char *units = "?";
1652
1da177e4
LT
1653 if ((num_online_cpus() == 1) &&
1654 !(error = apm_get_power_status(&bx, &cx, &dx))) {
1655 ac_line_status = (bx >> 8) & 0xff;
1656 battery_status = bx & 0xff;
1657 if ((cx & 0xff) != 0xff)
1658 percentage = cx & 0xff;
1659
1660 if (apm_info.connection_version > 0x100) {
1661 battery_flag = (cx >> 8) & 0xff;
1662 if (dx != 0xffff) {
1663 units = (dx & 0x8000) ? "min" : "sec";
1664 time_units = dx & 0x7fff;
1665 }
1666 }
1667 }
1668 /* Arguments, with symbols from linux/apm_bios.h. Information is
1669 from the Get Power Status (0x0a) call unless otherwise noted.
1670
1671 0) Linux driver version (this will change if format changes)
1672 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
1673 2) APM flags from APM Installation Check (0x00):
1674 bit 0: APM_16_BIT_SUPPORT
1675 bit 1: APM_32_BIT_SUPPORT
1676 bit 2: APM_IDLE_SLOWS_CLOCK
1677 bit 3: APM_BIOS_DISABLED
1678 bit 4: APM_BIOS_DISENGAGED
1679 3) AC line status
1680 0x00: Off-line
1681 0x01: On-line
1682 0x02: On backup power (BIOS >= 1.1 only)
1683 0xff: Unknown
1684 4) Battery status
1685 0x00: High
1686 0x01: Low
1687 0x02: Critical
1688 0x03: Charging
1689 0x04: Selected battery not present (BIOS >= 1.2 only)
1690 0xff: Unknown
1691 5) Battery flag
1692 bit 0: High
1693 bit 1: Low
1694 bit 2: Critical
1695 bit 3: Charging
1696 bit 7: No system battery
1697 0xff: Unknown
1698 6) Remaining battery life (percentage of charge):
1699 0-100: valid
1700 -1: Unknown
1701 7) Remaining battery life (time units):
1702 Number of remaining minutes or seconds
1703 -1: Unknown
1704 8) min = minutes; sec = seconds */
1705
016d6f35 1706 seq_printf(m, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1da177e4
LT
1707 driver_version,
1708 (apm_info.bios.version >> 8) & 0xff,
1709 apm_info.bios.version & 0xff,
1710 apm_info.bios.flags,
1711 ac_line_status,
1712 battery_status,
1713 battery_flag,
1714 percentage,
1715 time_units,
1716 units);
016d6f35
AD
1717 return 0;
1718}
1da177e4 1719
016d6f35
AD
1720static int proc_apm_open(struct inode *inode, struct file *file)
1721{
1722 return single_open(file, proc_apm_show, NULL);
1da177e4
LT
1723}
1724
016d6f35
AD
1725static const struct file_operations apm_file_ops = {
1726 .owner = THIS_MODULE,
1727 .open = proc_apm_open,
1728 .read = seq_read,
1729 .llseek = seq_lseek,
1730 .release = single_release,
1731};
1732
1da177e4
LT
1733static int apm(void *unused)
1734{
1735 unsigned short bx;
1736 unsigned short cx;
1737 unsigned short dx;
1738 int error;
1739 char * power_stat;
1740 char * bat_stat;
1741
1da177e4
LT
1742#ifdef CONFIG_SMP
1743 /* 2002/08/01 - WT
1744 * This is to avoid random crashes at boot time during initialization
1745 * on SMP systems in case of "apm=power-off" mode. Seen on ASUS A7M266D.
1746 * Some bioses don't like being called from CPU != 0.
1747 * Method suggested by Ingo Molnar.
1748 */
1749 set_cpus_allowed(current, cpumask_of_cpu(0));
1750 BUG_ON(smp_processor_id() != 0);
1751#endif
1752
1753 if (apm_info.connection_version == 0) {
1754 apm_info.connection_version = apm_info.bios.version;
1755 if (apm_info.connection_version > 0x100) {
1756 /*
1757 * We only support BIOSs up to version 1.2
1758 */
1759 if (apm_info.connection_version > 0x0102)
1760 apm_info.connection_version = 0x0102;
1761 error = apm_driver_version(&apm_info.connection_version);
1762 if (error != APM_SUCCESS) {
1763 apm_error("driver version", error);
1764 /* Fall back to an APM 1.0 connection. */
1765 apm_info.connection_version = 0x100;
1766 }
1767 }
1768 }
1769
1770 if (debug)
1771 printk(KERN_INFO "apm: Connection version %d.%d\n",
1772 (apm_info.connection_version >> 8) & 0xff,
1773 apm_info.connection_version & 0xff);
1774
1775#ifdef CONFIG_APM_DO_ENABLE
1776 if (apm_info.bios.flags & APM_BIOS_DISABLED) {
1777 /*
1778 * This call causes my NEC UltraLite Versa 33/C to hang if it
1779 * is booted with PM disabled but not in the docking station.
1780 * Unfortunate ...
1781 */
1782 error = apm_enable_power_management(1);
1783 if (error) {
1784 apm_error("enable power management", error);
1785 return -1;
1786 }
1787 }
1788#endif
1789
1790 if ((apm_info.bios.flags & APM_BIOS_DISENGAGED)
1791 && (apm_info.connection_version > 0x0100)) {
1792 error = apm_engage_power_management(APM_DEVICE_ALL, 1);
1793 if (error) {
1794 apm_error("engage power management", error);
1795 return -1;
1796 }
1797 }
1798
1799 if (debug && (num_online_cpus() == 1 || smp )) {
1800 error = apm_get_power_status(&bx, &cx, &dx);
1801 if (error)
1802 printk(KERN_INFO "apm: power status not available\n");
1803 else {
1804 switch ((bx >> 8) & 0xff) {
1805 case 0: power_stat = "off line"; break;
1806 case 1: power_stat = "on line"; break;
1807 case 2: power_stat = "on backup power"; break;
1808 default: power_stat = "unknown"; break;
1809 }
1810 switch (bx & 0xff) {
1811 case 0: bat_stat = "high"; break;
1812 case 1: bat_stat = "low"; break;
1813 case 2: bat_stat = "critical"; break;
1814 case 3: bat_stat = "charging"; break;
1815 default: bat_stat = "unknown"; break;
1816 }
1817 printk(KERN_INFO
1818 "apm: AC %s, battery status %s, battery life ",
1819 power_stat, bat_stat);
1820 if ((cx & 0xff) == 0xff)
1821 printk("unknown\n");
1822 else
1823 printk("%d%%\n", cx & 0xff);
1824 if (apm_info.connection_version > 0x100) {
1825 printk(KERN_INFO
1826 "apm: battery flag 0x%02x, battery life ",
1827 (cx >> 8) & 0xff);
1828 if (dx == 0xffff)
1829 printk("unknown\n");
1830 else
1831 printk("%d %s\n", dx & 0x7fff,
1832 (dx & 0x8000) ?
1833 "minutes" : "seconds");
1834 }
1835 }
1836 }
1837
1838 /* Install our power off handler.. */
1839 if (power_off)
1840 pm_power_off = apm_power_off;
1841
1842 if (num_online_cpus() == 1 || smp) {
1843#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1844 console_blank_hook = apm_console_blank;
1845#endif
1846 apm_mainloop();
1847#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
1848 console_blank_hook = NULL;
1849#endif
1850 }
1da177e4
LT
1851
1852 return 0;
1853}
1854
1855#ifndef MODULE
1856static int __init apm_setup(char *str)
1857{
1858 int invert;
1859
1860 while ((str != NULL) && (*str != '\0')) {
1861 if (strncmp(str, "off", 3) == 0)
1862 apm_disabled = 1;
1863 if (strncmp(str, "on", 2) == 0)
1864 apm_disabled = 0;
1865 if ((strncmp(str, "bounce-interval=", 16) == 0) ||
1866 (strncmp(str, "bounce_interval=", 16) == 0))
1867 bounce_interval = simple_strtol(str + 16, NULL, 0);
1868 if ((strncmp(str, "idle-threshold=", 15) == 0) ||
1869 (strncmp(str, "idle_threshold=", 15) == 0))
1870 idle_threshold = simple_strtol(str + 15, NULL, 0);
1871 if ((strncmp(str, "idle-period=", 12) == 0) ||
1872 (strncmp(str, "idle_period=", 12) == 0))
1873 idle_period = simple_strtol(str + 12, NULL, 0);
1874 invert = (strncmp(str, "no-", 3) == 0) ||
1875 (strncmp(str, "no_", 3) == 0);
1876 if (invert)
1877 str += 3;
1878 if (strncmp(str, "debug", 5) == 0)
1879 debug = !invert;
1880 if ((strncmp(str, "power-off", 9) == 0) ||
1881 (strncmp(str, "power_off", 9) == 0))
1882 power_off = !invert;
1883 if (strncmp(str, "smp", 3) == 0)
1884 {
1885 smp = !invert;
1886 idle_threshold = 100;
1887 }
1888 if ((strncmp(str, "allow-ints", 10) == 0) ||
1889 (strncmp(str, "allow_ints", 10) == 0))
1890 apm_info.allow_ints = !invert;
1891 if ((strncmp(str, "broken-psr", 10) == 0) ||
1892 (strncmp(str, "broken_psr", 10) == 0))
1893 apm_info.get_power_status_broken = !invert;
1894 if ((strncmp(str, "realmode-power-off", 18) == 0) ||
1895 (strncmp(str, "realmode_power_off", 18) == 0))
1896 apm_info.realmode_power_off = !invert;
1897 str = strchr(str, ',');
1898 if (str != NULL)
1899 str += strspn(str, ", \t");
1900 }
1901 return 1;
1902}
1903
1904__setup("apm=", apm_setup);
1905#endif
1906
5dfe4c96 1907static const struct file_operations apm_bios_fops = {
1da177e4
LT
1908 .owner = THIS_MODULE,
1909 .read = do_read,
1910 .poll = do_poll,
1911 .ioctl = do_ioctl,
1912 .open = do_open,
1913 .release = do_release,
1914};
1915
1916static struct miscdevice apm_device = {
1917 APM_MINOR_DEV,
1918 "apm_bios",
1919 &apm_bios_fops
1920};
1921
1922
1923/* Simple "print if true" callback */
1924static int __init print_if_true(struct dmi_system_id *d)
1925{
1926 printk("%s\n", d->ident);
1927 return 0;
1928}
1929
1930/*
1931 * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it was
1932 * disabled before the suspend. Linux used to get terribly confused by that.
1933 */
1934static int __init broken_ps2_resume(struct dmi_system_id *d)
1935{
1936 printk(KERN_INFO "%s machine detected. Mousepad Resume Bug workaround hopefully not needed.\n", d->ident);
1937 return 0;
1938}
1939
1940/* Some bioses have a broken protected mode poweroff and need to use realmode */
1941static int __init set_realmode_power_off(struct dmi_system_id *d)
1942{
1943 if (apm_info.realmode_power_off == 0) {
1944 apm_info.realmode_power_off = 1;
1945 printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
1946 }
1947 return 0;
1948}
1949
1950/* Some laptops require interrupts to be enabled during APM calls */
1951static int __init set_apm_ints(struct dmi_system_id *d)
1952{
1953 if (apm_info.allow_ints == 0) {
1954 apm_info.allow_ints = 1;
1955 printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
1956 }
1957 return 0;
1958}
1959
1960/* Some APM bioses corrupt memory or just plain do not work */
1961static int __init apm_is_horked(struct dmi_system_id *d)
1962{
1963 if (apm_info.disabled == 0) {
1964 apm_info.disabled = 1;
1965 printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
1966 }
1967 return 0;
1968}
1969
1970static int __init apm_is_horked_d850md(struct dmi_system_id *d)
1971{
1972 if (apm_info.disabled == 0) {
1973 apm_info.disabled = 1;
1974 printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
1975 printk(KERN_INFO "This bug is fixed in bios P15 which is available for \n");
1976 printk(KERN_INFO "download from support.intel.com \n");
1977 }
1978 return 0;
1979}
1980
1981/* Some APM bioses hang on APM idle calls */
1982static int __init apm_likes_to_melt(struct dmi_system_id *d)
1983{
1984 if (apm_info.forbid_idle == 0) {
1985 apm_info.forbid_idle = 1;
1986 printk(KERN_INFO "%s machine detected. Disabling APM idle calls.\n", d->ident);
1987 }
1988 return 0;
1989}
1990
1991/*
1992 * Check for clue free BIOS implementations who use
1993 * the following QA technique
1994 *
1995 * [ Write BIOS Code ]<------
1996 * | ^
1997 * < Does it Compile >----N--
1998 * |Y ^
1999 * < Does it Boot Win98 >-N--
2000 * |Y
2001 * [Ship It]
2002 *
2003 * Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e)
2004 * Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000)
2005 */
2006static int __init broken_apm_power(struct dmi_system_id *d)
2007{
2008 apm_info.get_power_status_broken = 1;
2009 printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
2010 return 0;
2011}
2012
2013/*
2014 * This bios swaps the APM minute reporting bytes over (Many sony laptops
2015 * have this problem).
2016 */
2017static int __init swab_apm_power_in_minutes(struct dmi_system_id *d)
2018{
2019 apm_info.get_power_status_swabinminutes = 1;
2020 printk(KERN_WARNING "BIOS strings suggest APM reports battery life in minutes and wrong byte order.\n");
2021 return 0;
2022}
2023
2024static struct dmi_system_id __initdata apm_dmi_table[] = {
2025 {
2026 print_if_true,
2027 KERN_WARNING "IBM T23 - BIOS 1.03b+ and controller firmware 1.02+ may be needed for Linux APM.",
2028 { DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
2029 DMI_MATCH(DMI_BIOS_VERSION, "1AET38WW (1.01b)"), },
2030 },
2031 { /* Handle problems with APM on the C600 */
2032 broken_ps2_resume, "Dell Latitude C600",
2033 { DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
2034 DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C600"), },
2035 },
2036 { /* Allow interrupts during suspend on Dell Latitude laptops*/
2037 set_apm_ints, "Dell Latitude",
2038 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2039 DMI_MATCH(DMI_PRODUCT_NAME, "Latitude C510"), }
2040 },
2041 { /* APM crashes */
2042 apm_is_horked, "Dell Inspiron 2500",
2043 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2044 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
2045 DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
2046 DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2047 },
2048 { /* Allow interrupts during suspend on Dell Inspiron laptops*/
2049 set_apm_ints, "Dell Inspiron", {
2050 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2051 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"), },
2052 },
2053 { /* Handle problems with APM on Inspiron 5000e */
2054 broken_apm_power, "Dell Inspiron 5000e",
2055 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2056 DMI_MATCH(DMI_BIOS_VERSION, "A04"),
2057 DMI_MATCH(DMI_BIOS_DATE, "08/24/2000"), },
2058 },
2059 { /* Handle problems with APM on Inspiron 2500 */
2060 broken_apm_power, "Dell Inspiron 2500",
2061 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2062 DMI_MATCH(DMI_BIOS_VERSION, "A12"),
2063 DMI_MATCH(DMI_BIOS_DATE, "02/04/2002"), },
2064 },
2065 { /* APM crashes */
2066 apm_is_horked, "Dell Dimension 4100",
2067 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2068 DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"),
2069 DMI_MATCH(DMI_BIOS_VENDOR,"Intel Corp."),
2070 DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2071 },
2072 { /* Allow interrupts during suspend on Compaq Laptops*/
2073 set_apm_ints, "Compaq 12XL125",
2074 { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
2075 DMI_MATCH(DMI_PRODUCT_NAME, "Compaq PC"),
2076 DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2077 DMI_MATCH(DMI_BIOS_VERSION,"4.06"), },
2078 },
2079 { /* Allow interrupts during APM or the clock goes slow */
2080 set_apm_ints, "ASUSTeK",
2081 { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
2082 DMI_MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"), },
2083 },
2084 { /* APM blows on shutdown */
2085 apm_is_horked, "ABIT KX7-333[R]",
2086 { DMI_MATCH(DMI_BOARD_VENDOR, "ABIT"),
2087 DMI_MATCH(DMI_BOARD_NAME, "VT8367-8233A (KX7-333[R])"), },
2088 },
2089 { /* APM crashes */
2090 apm_is_horked, "Trigem Delhi3",
2091 { DMI_MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
2092 DMI_MATCH(DMI_PRODUCT_NAME, "Delhi3"), },
2093 },
2094 { /* APM crashes */
2095 apm_is_horked, "Fujitsu-Siemens",
2096 { DMI_MATCH(DMI_BIOS_VENDOR, "hoenix/FUJITSU SIEMENS"),
2097 DMI_MATCH(DMI_BIOS_VERSION, "Version1.01"), },
2098 },
2099 { /* APM crashes */
2100 apm_is_horked_d850md, "Intel D850MD",
2101 { DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2102 DMI_MATCH(DMI_BIOS_VERSION, "MV85010A.86A.0016.P07.0201251536"), },
2103 },
2104 { /* APM crashes */
2105 apm_is_horked, "Intel D810EMO",
2106 { DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2107 DMI_MATCH(DMI_BIOS_VERSION, "MO81010A.86A.0008.P04.0004170800"), },
2108 },
2109 { /* APM crashes */
2110 apm_is_horked, "Dell XPS-Z",
2111 { DMI_MATCH(DMI_BIOS_VENDOR, "Intel Corp."),
2112 DMI_MATCH(DMI_BIOS_VERSION, "A11"),
2113 DMI_MATCH(DMI_PRODUCT_NAME, "XPS-Z"), },
2114 },
2115 { /* APM crashes */
2116 apm_is_horked, "Sharp PC-PJ/AX",
2117 { DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
2118 DMI_MATCH(DMI_PRODUCT_NAME, "PC-PJ/AX"),
2119 DMI_MATCH(DMI_BIOS_VENDOR,"SystemSoft"),
2120 DMI_MATCH(DMI_BIOS_VERSION,"Version R2.08"), },
2121 },
2122 { /* APM crashes */
2123 apm_is_horked, "Dell Inspiron 2500",
2124 { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
2125 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 2500"),
2126 DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
2127 DMI_MATCH(DMI_BIOS_VERSION,"A11"), },
2128 },
2129 { /* APM idle hangs */
2130 apm_likes_to_melt, "Jabil AMD",
2131 { DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
2132 DMI_MATCH(DMI_BIOS_VERSION, "0AASNP06"), },
2133 },
2134 { /* APM idle hangs */
2135 apm_likes_to_melt, "AMI Bios",
2136 { DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
2137 DMI_MATCH(DMI_BIOS_VERSION, "0AASNP05"), },
2138 },
2139 { /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */
2140 swab_apm_power_in_minutes, "Sony VAIO",
2141 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2142 DMI_MATCH(DMI_BIOS_VERSION, "R0206H"),
2143 DMI_MATCH(DMI_BIOS_DATE, "08/23/99"), },
2144 },
2145 { /* Handle problems with APM on Sony Vaio PCG-N505VX */
2146 swab_apm_power_in_minutes, "Sony VAIO",
2147 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2148 DMI_MATCH(DMI_BIOS_VERSION, "W2K06H0"),
2149 DMI_MATCH(DMI_BIOS_DATE, "02/03/00"), },
2150 },
2151 { /* Handle problems with APM on Sony Vaio PCG-XG29 */
2152 swab_apm_power_in_minutes, "Sony VAIO",
2153 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2154 DMI_MATCH(DMI_BIOS_VERSION, "R0117A0"),
2155 DMI_MATCH(DMI_BIOS_DATE, "04/25/00"), },
2156 },
2157 { /* Handle problems with APM on Sony Vaio PCG-Z600NE */
2158 swab_apm_power_in_minutes, "Sony VAIO",
2159 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2160 DMI_MATCH(DMI_BIOS_VERSION, "R0121Z1"),
2161 DMI_MATCH(DMI_BIOS_DATE, "05/11/00"), },
2162 },
2163 { /* Handle problems with APM on Sony Vaio PCG-Z600NE */
2164 swab_apm_power_in_minutes, "Sony VAIO",
2165 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2166 DMI_MATCH(DMI_BIOS_VERSION, "WME01Z1"),
2167 DMI_MATCH(DMI_BIOS_DATE, "08/11/00"), },
2168 },
2169 { /* Handle problems with APM on Sony Vaio PCG-Z600LEK(DE) */
2170 swab_apm_power_in_minutes, "Sony VAIO",
2171 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2172 DMI_MATCH(DMI_BIOS_VERSION, "R0206Z3"),
2173 DMI_MATCH(DMI_BIOS_DATE, "12/25/00"), },
2174 },
2175 { /* Handle problems with APM on Sony Vaio PCG-Z505LS */
2176 swab_apm_power_in_minutes, "Sony VAIO",
2177 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2178 DMI_MATCH(DMI_BIOS_VERSION, "R0203D0"),
2179 DMI_MATCH(DMI_BIOS_DATE, "05/12/00"), },
2180 },
2181 { /* Handle problems with APM on Sony Vaio PCG-Z505LS */
2182 swab_apm_power_in_minutes, "Sony VAIO",
2183 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2184 DMI_MATCH(DMI_BIOS_VERSION, "R0203Z3"),
2185 DMI_MATCH(DMI_BIOS_DATE, "08/25/00"), },
2186 },
2187 { /* Handle problems with APM on Sony Vaio PCG-Z505LS (with updated BIOS) */
2188 swab_apm_power_in_minutes, "Sony VAIO",
2189 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2190 DMI_MATCH(DMI_BIOS_VERSION, "R0209Z3"),
2191 DMI_MATCH(DMI_BIOS_DATE, "05/12/01"), },
2192 },
2193 { /* Handle problems with APM on Sony Vaio PCG-F104K */
2194 swab_apm_power_in_minutes, "Sony VAIO",
2195 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2196 DMI_MATCH(DMI_BIOS_VERSION, "R0204K2"),
2197 DMI_MATCH(DMI_BIOS_DATE, "08/28/00"), },
2198 },
2199
2200 { /* Handle problems with APM on Sony Vaio PCG-C1VN/C1VE */
2201 swab_apm_power_in_minutes, "Sony VAIO",
2202 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2203 DMI_MATCH(DMI_BIOS_VERSION, "R0208P1"),
2204 DMI_MATCH(DMI_BIOS_DATE, "11/09/00"), },
2205 },
2206 { /* Handle problems with APM on Sony Vaio PCG-C1VE */
2207 swab_apm_power_in_minutes, "Sony VAIO",
2208 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2209 DMI_MATCH(DMI_BIOS_VERSION, "R0204P1"),
2210 DMI_MATCH(DMI_BIOS_DATE, "09/12/00"), },
2211 },
2212 { /* Handle problems with APM on Sony Vaio PCG-C1VE */
2213 swab_apm_power_in_minutes, "Sony VAIO",
2214 { DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
2215 DMI_MATCH(DMI_BIOS_VERSION, "WXPO1Z3"),
2216 DMI_MATCH(DMI_BIOS_DATE, "10/26/01"), },
2217 },
2218 { /* broken PM poweroff bios */
2219 set_realmode_power_off, "Award Software v4.60 PGMA",
2220 { DMI_MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
2221 DMI_MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
2222 DMI_MATCH(DMI_BIOS_DATE, "134526184"), },
2223 },
2224
2225 /* Generic per vendor APM settings */
2226
2227 { /* Allow interrupts during suspend on IBM laptops */
2228 set_apm_ints, "IBM",
2229 { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), },
2230 },
2231
2232 { }
2233};
2234
2235/*
2236 * Just start the APM thread. We do NOT want to do APM BIOS
2237 * calls from anything but the APM thread, if for no other reason
2238 * than the fact that we don't trust the APM BIOS. This way,
2239 * most common APM BIOS problems that lead to protection errors
2240 * etc will have at least some level of being contained...
2241 *
2242 * In short, if something bad happens, at least we have a choice
2243 * of just killing the apm thread..
2244 */
2245static int __init apm_init(void)
2246{
2247 struct proc_dir_entry *apm_proc;
92f17f01 2248 struct desc_struct *gdt;
fc09561d 2249 int err;
1da177e4
LT
2250
2251 dmi_check_system(apm_dmi_table);
2252
6020c8f3 2253 if (apm_info.bios.version == 0 || paravirt_enabled()) {
1da177e4
LT
2254 printk(KERN_INFO "apm: BIOS not found.\n");
2255 return -ENODEV;
2256 }
2257 printk(KERN_INFO
2258 "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
2259 ((apm_info.bios.version >> 8) & 0xff),
2260 (apm_info.bios.version & 0xff),
2261 apm_info.bios.flags,
2262 driver_version);
2263 if ((apm_info.bios.flags & APM_32_BIT_SUPPORT) == 0) {
2264 printk(KERN_INFO "apm: no 32 bit BIOS support\n");
2265 return -ENODEV;
2266 }
2267
2268 if (allow_ints)
2269 apm_info.allow_ints = 1;
2270 if (broken_psr)
2271 apm_info.get_power_status_broken = 1;
2272 if (realmode_power_off)
2273 apm_info.realmode_power_off = 1;
2274 /* User can override, but default is to trust DMI */
2275 if (apm_disabled != -1)
2276 apm_info.disabled = apm_disabled;
2277
2278 /*
2279 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
2280 * but is reportedly a 1.0 BIOS.
2281 */
2282 if (apm_info.bios.version == 0x001)
2283 apm_info.bios.version = 0x100;
2284
2285 /* BIOS < 1.2 doesn't set cseg_16_len */
2286 if (apm_info.bios.version < 0x102)
2287 apm_info.bios.cseg_16_len = 0; /* 64k */
2288
2289 if (debug) {
2290 printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x",
2291 apm_info.bios.cseg, apm_info.bios.offset,
2292 apm_info.bios.cseg_16, apm_info.bios.dseg);
2293 if (apm_info.bios.version > 0x100)
2294 printk(" cseg len %x, dseg len %x",
2295 apm_info.bios.cseg_len,
2296 apm_info.bios.dseg_len);
2297 if (apm_info.bios.version > 0x101)
2298 printk(" cseg16 len %x", apm_info.bios.cseg_16_len);
2299 printk("\n");
2300 }
2301
2302 if (apm_info.disabled) {
2303 printk(KERN_NOTICE "apm: disabled on user request.\n");
2304 return -ENODEV;
2305 }
2306 if ((num_online_cpus() > 1) && !power_off && !smp) {
2307 printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
2308 apm_info.disabled = 1;
2309 return -ENODEV;
2310 }
2311 if (PM_IS_ACTIVE()) {
2312 printk(KERN_NOTICE "apm: overridden by ACPI.\n");
2313 apm_info.disabled = 1;
2314 return -ENODEV;
2315 }
987d4613 2316#ifdef CONFIG_PM_LEGACY
1da177e4 2317 pm_active = 1;
987d4613 2318#endif
1da177e4
LT
2319
2320 /*
2321 * Set up a segment that references the real mode segment 0x40
2322 * that extends up to the end of page zero (that we have reserved).
2323 * This is for buggy BIOS's that refer to (real mode) segment 0x40
2324 * even though they are called in protected mode.
2325 */
2326 set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
2327 _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
2328
3012d2d2
ZA
2329 /*
2330 * Set up the long jump entry point to the APM BIOS, which is called
2331 * from inline assembly.
2332 */
1da177e4
LT
2333 apm_bios_entry.offset = apm_info.bios.offset;
2334 apm_bios_entry.segment = APM_CS;
2335
3012d2d2
ZA
2336 /*
2337 * The APM 1.1 BIOS is supposed to provide limit information that it
2338 * recognizes. Many machines do this correctly, but many others do
2339 * not restrict themselves to their claimed limit. When this happens,
2340 * they will cause a segmentation violation in the kernel at boot time.
2341 * Most BIOS's, however, will respect a 64k limit, so we use that.
92f17f01
ZA
2342 *
2343 * Note we only set APM segments on CPU zero, since we pin the APM
2344 * code to that CPU.
3012d2d2 2345 */
92f17f01
ZA
2346 gdt = get_cpu_gdt_table(0);
2347 set_base(gdt[APM_CS >> 3],
2348 __va((unsigned long)apm_info.bios.cseg << 4));
2349 set_base(gdt[APM_CS_16 >> 3],
2350 __va((unsigned long)apm_info.bios.cseg_16 << 4));
2351 set_base(gdt[APM_DS >> 3],
2352 __va((unsigned long)apm_info.bios.dseg << 4));
1da177e4 2353
016d6f35 2354 apm_proc = create_proc_entry("apm", 0, NULL);
1da177e4 2355 if (apm_proc)
016d6f35 2356 apm_proc->proc_fops = &apm_file_ops;
1da177e4 2357
fc09561d
SH
2358 kapmd_task = kthread_create(apm, NULL, "kapmd");
2359 if (IS_ERR(kapmd_task)) {
2360 printk(KERN_ERR "apm: disabled - Unable to start kernel "
2361 "thread.\n");
2362 err = PTR_ERR(kapmd_task);
2363 kapmd_task = NULL;
c94a62aa 2364 remove_proc_entry("apm", NULL);
fc09561d 2365 return err;
1da177e4 2366 }
fc09561d
SH
2367 kapmd_task->flags |= PF_NOFREEZE;
2368 wake_up_process(kapmd_task);
1da177e4
LT
2369
2370 if (num_online_cpus() > 1 && !smp ) {
2371 printk(KERN_NOTICE
2372 "apm: disabled - APM is not SMP safe (power off active).\n");
2373 return 0;
2374 }
2375
c94a62aa
NH
2376 /*
2377 * Note we don't actually care if the misc_device cannot be registered.
2378 * this driver can do its job without it, even if userspace can't
2379 * control it. just log the error
2380 */
2381 if (misc_register(&apm_device))
2382 printk(KERN_WARNING "apm: Could not register misc device.\n");
1da177e4
LT
2383
2384 if (HZ != 100)
2385 idle_period = (idle_period * HZ) / 100;
2386 if (idle_threshold < 100) {
2387 original_pm_idle = pm_idle;
2388 pm_idle = apm_cpu_idle;
2389 set_pm_idle = 1;
2390 }
2391
2392 return 0;
2393}
2394
2395static void __exit apm_exit(void)
2396{
2397 int error;
2398
2399 if (set_pm_idle) {
2400 pm_idle = original_pm_idle;
2401 /*
2402 * We are about to unload the current idle thread pm callback
2403 * (pm_idle), Wait for all processors to update cached/local
2404 * copies of pm_idle before proceeding.
2405 */
2406 cpu_idle_wait();
2407 }
2408 if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)
2409 && (apm_info.connection_version > 0x0100)) {
2410 error = apm_engage_power_management(APM_DEVICE_ALL, 0);
2411 if (error)
2412 apm_error("disengage power management", error);
2413 }
2414 misc_deregister(&apm_device);
2415 remove_proc_entry("apm", NULL);
2416 if (power_off)
2417 pm_power_off = NULL;
fc09561d
SH
2418 if (kapmd_task) {
2419 kthread_stop(kapmd_task);
2420 kapmd_task = NULL;
2421 }
987d4613 2422#ifdef CONFIG_PM_LEGACY
1da177e4 2423 pm_active = 0;
987d4613 2424#endif
1da177e4
LT
2425}
2426
2427module_init(apm_init);
2428module_exit(apm_exit);
2429
2430MODULE_AUTHOR("Stephen Rothwell");
2431MODULE_DESCRIPTION("Advanced Power Management");
2432MODULE_LICENSE("GPL");
2433module_param(debug, bool, 0644);
2434MODULE_PARM_DESC(debug, "Enable debug mode");
2435module_param(power_off, bool, 0444);
2436MODULE_PARM_DESC(power_off, "Enable power off");
2437module_param(bounce_interval, int, 0444);
2438MODULE_PARM_DESC(bounce_interval,
2439 "Set the number of ticks to ignore suspend bounces");
2440module_param(allow_ints, bool, 0444);
2441MODULE_PARM_DESC(allow_ints, "Allow interrupts during BIOS calls");
2442module_param(broken_psr, bool, 0444);
2443MODULE_PARM_DESC(broken_psr, "BIOS has a broken GetPowerStatus call");
2444module_param(realmode_power_off, bool, 0444);
2445MODULE_PARM_DESC(realmode_power_off,
2446 "Switch to real mode before powering off");
2447module_param(idle_threshold, int, 0444);
2448MODULE_PARM_DESC(idle_threshold,
2449 "System idle percentage above which to make APM BIOS idle calls");
2450module_param(idle_period, int, 0444);
2451MODULE_PARM_DESC(idle_period,
2452 "Period (in sec/100) over which to caculate the idle percentage");
2453module_param(smp, bool, 0444);
2454MODULE_PARM_DESC(smp,
2455 "Set this to enable APM use on an SMP platform. Use with caution on older systems");
2456MODULE_ALIAS_MISCDEV(APM_MINOR_DEV);
This page took 0.412142 seconds and 5 git commands to generate.