OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [i386/] [kernel/] [apm.c] - Blame information for rev 1782

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.