This source file includes following definitions.
- apm_driver_version
- apm_get_event
- apm_set_power_state
- apm_set_display_power_state
- apm_enable_power_management
- apm_get_power_status
- apm_engage_power_management
- apm_error
- apm_display_blank
- apm_display_unblank
- apm_register_callback
- apm_unregister_callback
- queue_empty
- get_queued_event
- queue_event
- set_time
- suspend
- standby
- get_event
- send_event
- check_events
- do_apm_timer
- apm_do_idle
- apm_do_busy
- check_apm_bios_struct
- do_read
- do_select
- do_ioctl
- do_release
- do_open
- apm_get_info
- apm_bios_init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 #include <linux/config.h>
51 #include <linux/module.h>
52
53 #include <asm/system.h>
54 #include <asm/segment.h>
55
56 #include <linux/types.h>
57 #include <linux/stddef.h>
58 #include <linux/timer.h>
59 #include <linux/fcntl.h>
60 #include <linux/malloc.h>
61 #include <linux/linkage.h>
62 #ifdef CONFIG_PROC_FS
63 #include <linux/stat.h>
64 #include <linux/proc_fs.h>
65 #endif
66 #include <linux/miscdevice.h>
67 #include <linux/apm_bios.h>
68
69 static struct symbol_table apm_syms = {
70 #include <linux/symtab_begin.h>
71 X(apm_register_callback),
72 X(apm_unregister_callback),
73 #include <linux/symtab_end.h>
74 };
75
76 extern unsigned long get_cmos_time(void);
77
78
79
80
81
82 #define APM_MINOR_DEV 134
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139 #undef APM_DEBUG
140
141
142
143
144
145 #define ALWAYS_CALL_BUSY
146
147
148
149
150
151 #define APM_NOINTS
152
153
154
155
156
157
158 #define APM_ZERO_SEGS
159
160
161
162
163
164
165
166
167
168
169 #define APM_RELAX_SEGMENTS
170
171
172
173
174 #define APM_CHECK_TIMEOUT (HZ)
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 #ifdef APM_NOINTS
190 # define APM_DO_CLI "cli\n\t"
191 #else
192 # define APM_DO_CLI
193 #endif
194 #ifdef APM_ZERO_SEGS
195 # define APM_DO_ZERO_SEGS \
196 "pushl %%ds\n\t" \
197 "pushl %%es\n\t" \
198 "pushl %%fs\n\t" \
199 "pushl %%gs\n\t" \
200 "xorl %%edx, %%edx\n\t" \
201 "mov %%dx, %%ds\n\t" \
202 "mov %%dx, %%es\n\t" \
203 "mov %%dx, %%fs\n\t" \
204 "mov %%dx, %%gs\n\t"
205 # define APM_DO_RESTORE_SEGS \
206 "popl %%gs\n\t" \
207 "popl %%fs\n\t" \
208 "popl %%es\n\t" \
209 "popl %%ds\n\t"
210 #else
211 # define APM_DO_ZERO_SEGS
212 # define APM_DO_RESTORE_SEGS
213 #endif
214
215 #define APM_BIOS_CALL(error_reg) \
216 __asm__ __volatile__( \
217 APM_DO_ZERO_SEGS \
218 "pushfl\n\t" \
219 APM_DO_CLI \
220 "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
221 "setc %%" # error_reg "\n\t" \
222 "popfl\n\t" \
223 APM_DO_RESTORE_SEGS
224 #define APM_BIOS_CALL_END \
225 : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
226
227 #ifdef CONFIG_APM_CPU_IDLE
228 #define APM_SET_CPU_IDLE(error) \
229 APM_BIOS_CALL(al) \
230 : "=a" (error) \
231 : "a" (0x5305) \
232 APM_BIOS_CALL_END
233 #endif
234
235 #define APM_SET_CPU_BUSY(error) \
236 APM_BIOS_CALL(al) \
237 : "=a" (error) \
238 : "a" (0x5306) \
239 APM_BIOS_CALL_END
240
241 #define APM_SET_POWER_STATE(state, error) \
242 APM_BIOS_CALL(al) \
243 : "=a" (error) \
244 : "a" (0x5307), "b" (0x0001), "c" (state) \
245 APM_BIOS_CALL_END
246
247 #ifdef CONFIG_APM_DISPLAY_BLANK
248 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
249 APM_BIOS_CALL(al) \
250 : "=a" (error) \
251 : "a" (0x5307), "b" (0x01ff), "c" (state) \
252 APM_BIOS_CALL_END
253 #endif
254
255 #ifdef CONFIG_APM_DO_ENABLE
256 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
257 APM_BIOS_CALL(al) \
258 : "=a" (error) \
259 : "a" (0x5308), "b" (device), "c" (1) \
260 APM_BIOS_CALL_END
261 #endif
262
263 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
264 APM_BIOS_CALL(al) \
265 : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
266 : "a" (0x530a), "b" (1) \
267 APM_BIOS_CALL_END
268
269 #define APM_GET_EVENT(event, error) \
270 APM_BIOS_CALL(al) \
271 : "=a" (error), "=b" (event) \
272 : "a" (0x530b) \
273 APM_BIOS_CALL_END
274
275 #define APM_DRIVER_VERSION(ver, ax, error) \
276 APM_BIOS_CALL(bl) \
277 : "=a" (ax), "=b" (error) \
278 : "a" (0x530e), "b" (0), "c" (ver) \
279 APM_BIOS_CALL_END
280
281 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
282 APM_BIOS_CALL(al) \
283 : "=a" (error) \
284 : "a" (0x530f), "b" (device), "c" (1) \
285 APM_BIOS_CALL_END
286
287
288
289
290 static void suspend(void);
291 static void standby(void);
292 static void set_time(void);
293
294 static void check_events(void);
295 static void do_apm_timer(unsigned long);
296
297 static int do_open(struct inode *, struct file *);
298 static void do_release(struct inode *, struct file *);
299 static int do_read(struct inode *, struct file *, char *, int);
300 static int do_select(struct inode *, struct file *, int,
301 select_table *);
302 static int do_ioctl(struct inode *, struct file *, u_int, u_long);
303
304 #ifdef CONFIG_PROC_FS
305 static int apm_get_info(char *, char **, off_t, int, int);
306 #endif
307
308 extern int apm_register_callback(int (*)(apm_event_t));
309 extern void apm_unregister_callback(int (*)(apm_event_t));
310
311
312
313
314 static asmlinkage struct {
315 unsigned long offset;
316 unsigned short segment;
317 } apm_bios_entry;
318 static int apm_enabled = 0;
319 #ifdef CONFIG_APM_CPU_IDLE
320 static int clock_slowed = 0;
321 #endif
322 static int apm_major;
323 static int suspends_pending = 0;
324 static int standbys_pending = 0;
325
326 static long clock_cmos_diff;
327 static int got_clock_diff = 0;
328
329 static struct wait_queue * process_list = NULL;
330 static struct apm_bios_struct * user_list = NULL;
331
332 static struct timer_list apm_timer;
333
334 static char driver_version[] = "1.0";
335
336 #ifdef APM_DEBUG
337 static char * apm_event_name[] = {
338 "system standby",
339 "system suspend",
340 "normal resume",
341 "critical resume",
342 "low battery",
343 "power status change",
344 "update time",
345 "critical suspend",
346 "user standby",
347 "user suspend",
348 "system standby resume"
349 };
350 #define NR_APM_EVENT_NAME \
351 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
352 #endif
353
354 static struct file_operations apm_bios_fops = {
355 NULL,
356 do_read,
357 NULL,
358 NULL,
359 do_select,
360 do_ioctl,
361 NULL,
362 do_open,
363 do_release,
364 NULL,
365 NULL
366 };
367
368 static struct miscdevice apm_device = {
369 APM_MINOR_DEV,
370 "apm",
371 &apm_bios_fops
372 };
373
374 #ifdef CONFIG_PROC_FS
375 static struct proc_dir_entry apm_proc_entry = {
376 0, 3, "apm", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, apm_get_info
377 };
378 #endif
379
380 typedef struct callback_list_t {
381 int (* callback)(apm_event_t);
382 struct callback_list_t * next;
383 } callback_list_t;
384
385 static callback_list_t * callback_list = NULL;
386
387 typedef struct lookup_t {
388 int key;
389 char * msg;
390 } lookup_t;
391
392 static const lookup_t error_table[] = {
393
394 { APM_DISABLED, "Power management disabled" },
395 { APM_CONNECTED, "Real mode interface already connected" },
396 { APM_NOT_CONNECTED, "Interface not connected" },
397 { APM_16_CONNECTED, "16 bit interface already connected" },
398
399 { APM_32_CONNECTED, "32 bit interface already connected" },
400 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
401 { APM_BAD_DEVICE, "Unrecognized device ID" },
402 { APM_BAD_PARAM, "Parameter out of range" },
403 { APM_NOT_ENGAGED, "Interface not engaged" },
404 { APM_BAD_STATE, "Unable to enter requested state" },
405
406 { APM_NOT_PRESENT, "No APM present" }
407 };
408 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
409
410 static int apm_driver_version(u_short *val)
411 {
412 u_short error;
413
414 APM_DRIVER_VERSION(*val, *val, error);
415
416 if (error & 0xff)
417 return (*val >> 8);
418 return APM_SUCCESS;
419 }
420
421 static int apm_get_event(apm_event_t *event)
422 {
423 u_short error;
424
425 APM_GET_EVENT(*event, error);
426 if (error & 0xff)
427 return (error >> 8);
428 return APM_SUCCESS;
429 }
430
431 static int apm_set_power_state(u_short state)
432 {
433 u_short error;
434
435 APM_SET_POWER_STATE(state, error);
436 if (error & 0xff)
437 return (error >> 8);
438 return APM_SUCCESS;
439 }
440
441 #ifdef CONFIG_APM_DISPLAY_BLANK
442
443 static int apm_set_display_power_state(u_short state)
444 {
445 u_short error;
446
447 APM_SET_DISPLAY_POWER_STATE(state, error);
448 if (error & 0xff)
449 return (error >> 8);
450 return APM_SUCCESS;
451 }
452 #endif
453
454 #ifdef CONFIG_APM_DO_ENABLE
455
456 static int apm_enable_power_management(void)
457 {
458 u_short error;
459
460 APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
461 ? 0x0001 : 0xffff,
462 error);
463 if (error & 0xff)
464 return (error >> 8);
465 return APM_SUCCESS;
466 }
467 #endif
468
469 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
470 {
471 u_short error;
472
473 APM_GET_POWER_STATUS(*status, *bat, *life, error);
474 if (error & 0xff)
475 return (error >> 8);
476 return APM_SUCCESS;
477 }
478
479 static int apm_engage_power_management(u_short device)
480 {
481 u_short error;
482
483 APM_ENGAGE_POWER_MANAGEMENT(device, error);
484 if (error & 0xff)
485 return (error >> 8);
486 return APM_SUCCESS;
487 }
488
489 static void apm_error(char *str, int err)
490 {
491 int i;
492
493 for (i = 0; i < ERROR_COUNT; i++)
494 if (error_table[i].key == err) break;
495 if (i < ERROR_COUNT)
496 printk("apm_bios: %s: %s\n", str, error_table[i].msg);
497 else
498 printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
499 }
500
501
502 int apm_display_blank(void)
503 {
504 #ifdef CONFIG_APM_DISPLAY_BLANK
505 int error;
506
507 if (!apm_enabled)
508 return 0;
509 error = apm_set_display_power_state(APM_STATE_STANDBY);
510 if (error == APM_SUCCESS)
511 return 1;
512 apm_error("set display standby", error);
513 #endif
514 return 0;
515 }
516
517
518 int apm_display_unblank(void)
519 {
520 #ifdef CONFIG_APM_DISPLAY_BLANK
521 int error;
522
523 if (!apm_enabled)
524 return 0;
525 error = apm_set_display_power_state(APM_STATE_READY);
526 if (error == APM_SUCCESS)
527 return 1;
528 apm_error("set display ready", error);
529 #endif
530 return 0;
531 }
532
533 int apm_register_callback(int (*callback)(apm_event_t))
534 {
535 callback_list_t * new;
536
537 new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
538 if (new == NULL)
539 return -ENOMEM;
540 new->callback = callback;
541 new->next = callback_list;
542 callback_list = new;
543 return 0;
544 }
545
546 void apm_unregister_callback(int (*callback)(apm_event_t))
547 {
548 callback_list_t ** ptr;
549 callback_list_t * old;
550
551 ptr = &callback_list;
552 for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
553 if ((*ptr)->callback == callback)
554 break;
555 old = *ptr;
556 *ptr = old->next;
557 kfree_s(old, sizeof(callback_list_t));
558 }
559
560 static int queue_empty(struct apm_bios_struct * as)
561 {
562 return as->event_head == as->event_tail;
563 }
564
565 static apm_event_t get_queued_event(struct apm_bios_struct * as)
566 {
567 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
568 return as->events[as->event_tail];
569 }
570
571 static int queue_event(apm_event_t event)
572 {
573 struct apm_bios_struct * as;
574
575 if (user_list == NULL)
576 return 0;
577 for (as = user_list; as != NULL; as = as->next) {
578 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
579 if (as->event_head == as->event_tail)
580 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
581 as->events[as->event_head] = event;
582 if (!as->suser)
583 continue;
584 switch (event) {
585 case APM_SYS_SUSPEND:
586 case APM_USER_SUSPEND:
587 as->suspends_pending++;
588 suspends_pending++;
589 break;
590
591 case APM_SYS_STANDBY:
592 case APM_USER_STANDBY:
593 as->standbys_pending++;
594 standbys_pending++;
595 break;
596 }
597 }
598 wake_up_interruptible(&process_list);
599 return 1;
600 }
601
602 static void set_time(void)
603 {
604 unsigned long flags;
605
606 if (!got_clock_diff)
607 return;
608
609 save_flags(flags);
610 cli();
611 CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
612 restore_flags(flags);
613 }
614
615 static void suspend(void)
616 {
617 unsigned long flags;
618 int err;
619
620
621
622 save_flags(flags);
623 cli();
624 clock_cmos_diff = CURRENT_TIME - get_cmos_time();
625 got_clock_diff = 1;
626 restore_flags(flags);
627
628 err = apm_set_power_state(APM_STATE_SUSPEND);
629 if (err)
630 apm_error("suspend", err);
631 set_time();
632 }
633
634 static void standby(void)
635 {
636 int err;
637
638 err = apm_set_power_state(APM_STATE_STANDBY);
639 if (err)
640 apm_error("standby", err);
641 }
642
643 static apm_event_t get_event(void)
644 {
645 int error;
646 apm_event_t event;
647
648 static int notified = 0;
649
650 error = apm_get_event(&event);
651 if (error == APM_SUCCESS)
652 return event;
653
654 if ((error != APM_NO_EVENTS) && (notified++ == 0))
655 apm_error("get_event", error);
656
657 return 0;
658 }
659
660 static void send_event(apm_event_t event, apm_event_t undo)
661 {
662 callback_list_t * call;
663 callback_list_t * fix;
664
665 for (call = callback_list; call != NULL; call = call->next) {
666 if (call->callback(event) && undo) {
667 for (fix = callback_list; fix != call; fix = fix->next)
668 fix->callback(undo);
669 if (apm_bios_info.version > 0x100)
670 apm_set_power_state(APM_STATE_REJECT);
671 return;
672 }
673 }
674
675 queue_event(event);
676 }
677
678 static void check_events(void)
679 {
680 apm_event_t event;
681
682 while ((event = get_event()) != 0) {
683 switch (event) {
684 case APM_SYS_STANDBY:
685 case APM_USER_STANDBY:
686 send_event(event, APM_STANDBY_RESUME);
687 if (standbys_pending <= 0)
688 standby();
689 break;
690
691 case APM_USER_SUSPEND:
692 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
693 apm_set_power_state(APM_STATE_REJECT);
694 break;
695 #endif
696 case APM_SYS_SUSPEND:
697 send_event(event, APM_NORMAL_RESUME);
698 if (suspends_pending <= 0)
699 suspend();
700 break;
701
702 case APM_NORMAL_RESUME:
703 case APM_CRITICAL_RESUME:
704 case APM_STANDBY_RESUME:
705 set_time();
706 send_event(event, 0);
707 break;
708
709 case APM_LOW_BATTERY:
710 case APM_POWER_STATUS_CHANGE:
711 send_event(event, 0);
712 break;
713
714 case APM_UPDATE_TIME:
715 set_time();
716 break;
717
718 case APM_CRITICAL_SUSPEND:
719 suspend();
720 break;
721 }
722 #ifdef APM_DEBUG
723 if (event <= NR_APM_EVENT_NAME)
724 printk("APM BIOS received %s notify\n",
725 apm_event_name[event - 1]);
726 else
727 printk("APM BIOS received unknown event 0x%02x\n",
728 event);
729 #endif
730 }
731 }
732
733 static void do_apm_timer(unsigned long unused)
734 {
735 int err;
736
737 static int pending_count = 0;
738
739 if (((standbys_pending > 0) || (suspends_pending > 0))
740 && (apm_bios_info.version > 0x100)
741 && (pending_count-- <= 0)) {
742 pending_count = 4;
743
744 err = apm_set_power_state(APM_STATE_BUSY);
745 if (err)
746 apm_error("busy", err);
747 }
748
749 if (!(((standbys_pending > 0) || (suspends_pending > 0))
750 && (apm_bios_info.version == 0x100)))
751 check_events();
752
753 init_timer(&apm_timer);
754 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
755 add_timer(&apm_timer);
756 }
757
758
759 int apm_do_idle(void)
760 {
761 #ifdef CONFIG_APM_CPU_IDLE
762 unsigned short error;
763
764 if (!apm_enabled)
765 return 0;
766
767 APM_SET_CPU_IDLE(error);
768 if (error & 0xff)
769 return 0;
770
771 clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
772 return 1;
773 #else
774 return 0;
775 #endif
776 }
777
778
779 void apm_do_busy(void)
780 {
781 #ifdef CONFIG_APM_CPU_IDLE
782 unsigned short error;
783
784 if (!apm_enabled)
785 return;
786
787 #ifndef ALWAYS_CALL_BUSY
788 if (!clock_slowed)
789 return;
790 #endif
791
792 APM_SET_CPU_BUSY(error);
793
794 clock_slowed = 0;
795 #endif
796 }
797
798 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
799 {
800 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
801 printk("apm_bios: %s passed bad filp", func);
802 return 1;
803 }
804 return 0;
805 }
806
807 static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
808 {
809 struct apm_bios_struct * as;
810 int i;
811 apm_event_t event;
812 struct wait_queue wait = { current, NULL };
813
814 as = fp->private_data;
815 if (check_apm_bios_struct(as, "read"))
816 return -EIO;
817 if (count < sizeof(apm_event_t))
818 return -EINVAL;
819 if (queue_empty(as)) {
820 if (fp->f_flags & O_NONBLOCK)
821 return -EAGAIN;
822 add_wait_queue(&process_list, &wait);
823 repeat:
824 current->state = TASK_INTERRUPTIBLE;
825 if (queue_empty(as)
826 && !(current->signal & ~current->blocked)) {
827 schedule();
828 goto repeat;
829 }
830 current->state = TASK_RUNNING;
831 remove_wait_queue(&process_list, &wait);
832 }
833 i = count;
834 while ((i >= sizeof(event)) && !queue_empty(as)) {
835 event = get_queued_event(as);
836 memcpy_tofs(buf, &event, sizeof(event));
837 buf += sizeof(event);
838 i -= sizeof(event);
839 }
840 if (i < count)
841 return count - i;
842 if (current->signal & ~current->blocked)
843 return -ERESTARTSYS;
844 return 0;
845 }
846
847 static int do_select(struct inode *inode, struct file *fp, int sel_type,
848 select_table * wait)
849 {
850 struct apm_bios_struct * as;
851
852 as = fp->private_data;
853 if (check_apm_bios_struct(as, "select"))
854 return 0;
855 if (sel_type != SEL_IN)
856 return 0;
857 if (!queue_empty(as))
858 return 1;
859 select_wait(&process_list, wait);
860 return 0;
861 }
862
863 static int do_ioctl(struct inode * inode, struct file *filp,
864 u_int cmd, u_long arg)
865 {
866 struct apm_bios_struct * as;
867
868 as = filp->private_data;
869 if (check_apm_bios_struct(as, "ioctl"))
870 return -EIO;
871 switch (cmd) {
872 case APM_IOC_STANDBY:
873 if (as->standbys_pending > 0) {
874 as->standbys_pending--;
875 standbys_pending--;
876 if (standbys_pending <= 0)
877 standby();
878 }
879 break;
880 case APM_IOC_SUSPEND:
881 if (as->suspends_pending > 0) {
882 as->suspends_pending--;
883 suspends_pending--;
884 if (suspends_pending <= 0)
885 suspend();
886 }
887 break;
888 default:
889 return -EINVAL;
890 }
891 return 0;
892 }
893
894 static void do_release(struct inode * inode, struct file * filp)
895 {
896 struct apm_bios_struct * as;
897
898 as = filp->private_data;
899 filp->private_data = NULL;
900 if (check_apm_bios_struct(as, "release"))
901 return;
902 if (as->standbys_pending > 0) {
903 standbys_pending -= as->standbys_pending;
904 if (standbys_pending <= 0)
905 standby();
906 }
907 if (as->suspends_pending > 0) {
908 suspends_pending -= as->suspends_pending;
909 if (suspends_pending <= 0)
910 suspend();
911 }
912 if (user_list == as)
913 user_list = as->next;
914 else {
915 struct apm_bios_struct * as1;
916
917 for (as1 = user_list;
918 (as1 != NULL) && (as1->next != as);
919 as1 = as1->next)
920 ;
921 if (as1 == NULL)
922 printk("apm_bios: filp not in user list");
923 else
924 as1->next = as->next;
925 }
926 kfree_s(as, sizeof(*as));
927 }
928
929 static int do_open(struct inode * inode, struct file * filp)
930 {
931 struct apm_bios_struct * as;
932
933 as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
934 if (as == NULL) {
935 printk("apm_bios: cannot allocate struct of size %d bytes",
936 sizeof(*as));
937 return -ENOMEM;
938 }
939 as->magic = APM_BIOS_MAGIC;
940 as->event_tail = as->event_head = 0;
941 as->suspends_pending = as->standbys_pending = 0;
942 as->suser = suser();
943 as->next = user_list;
944 user_list = as;
945 filp->private_data = as;
946 return 0;
947 }
948
949 #ifdef CONFIG_PROC_FS
950 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
951 {
952 char * p;
953 unsigned short bx;
954 unsigned short cx;
955 unsigned short dx;
956 unsigned short error;
957 unsigned short ac_line_status = 0xff;
958 unsigned short battery_status = 0xff;
959 unsigned short battery_flag = 0xff;
960 int percentage = -1;
961 int time_units = -1;
962 char *units = "?";
963
964 if (!apm_enabled)
965 return 0;
966 p = buf;
967
968 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
969 ac_line_status = (bx >> 8) & 0xff;
970 battery_status = bx & 0xff;
971 if ((cx & 0xff) != 0xff)
972 percentage = cx & 0xff;
973
974 if (apm_bios_info.version > 0x100) {
975 battery_flag = (cx >> 8) & 0xff;
976 if (dx != 0xffff) {
977 if ((dx & 0x8000) == 0x8000) {
978 units = "min";
979 time_units = dx & 0x7ffe;
980 } else {
981 units = "sec";
982 time_units = dx & 0x7fff;
983 }
984 }
985 }
986 }
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024 p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1025 driver_version,
1026 (apm_bios_info.version >> 8) & 0xff,
1027 apm_bios_info.version & 0xff,
1028 apm_bios_info.flags,
1029 ac_line_status,
1030 battery_status,
1031 battery_flag,
1032 percentage,
1033 time_units,
1034 units);
1035
1036 return p - buf;
1037 }
1038 #endif
1039
1040 void apm_bios_init(void)
1041 {
1042 unsigned short bx;
1043 unsigned short cx;
1044 unsigned short dx;
1045 unsigned short error;
1046 char * power_stat;
1047 char * bat_stat;
1048
1049 if (apm_bios_info.version == 0) {
1050 printk("APM BIOS not found.\n");
1051 return;
1052 }
1053 printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
1054 ((apm_bios_info.version >> 8) & 0xff) + '0',
1055 (apm_bios_info.version & 0xff) + '0',
1056 apm_bios_info.flags,
1057 driver_version);
1058 if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1059 printk(" No 32 bit BIOS support\n");
1060 return;
1061 }
1062
1063
1064
1065
1066
1067 if (apm_bios_info.version == 0x001)
1068 apm_bios_info.version = 0x100;
1069
1070 printk(" Entry %x:%lx cseg16 %x dseg %x",
1071 apm_bios_info.cseg, apm_bios_info.offset,
1072 apm_bios_info.cseg_16, apm_bios_info.dseg);
1073 if (apm_bios_info.version > 0x100)
1074 printk(" cseg len %x, dseg len %x",
1075 apm_bios_info.cseg_len, apm_bios_info.dseg_len);
1076 printk("\n");
1077
1078 apm_bios_entry.offset = apm_bios_info.offset;
1079 apm_bios_entry.segment = APM_CS;
1080 set_base(gdt[APM_CS >> 3],
1081 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
1082 set_base(gdt[APM_CS_16 >> 3],
1083 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
1084 set_base(gdt[APM_DS >> 3],
1085 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
1086 if (apm_bios_info.version == 0x100) {
1087 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1088 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1089 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1090 } else {
1091 #ifdef APM_RELAX_SEGMENTS
1092
1093 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1094
1095 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1096
1097 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1098 #else
1099 set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
1100 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1101 set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
1102 #endif
1103 apm_bios_info.version = 0x0101;
1104 error = apm_driver_version(&apm_bios_info.version);
1105 if (error != 0)
1106 apm_bios_info.version = 0x100;
1107 else {
1108 apm_engage_power_management(0x0001);
1109 printk( " Connection version %d.%d\n",
1110 (apm_bios_info.version >> 8) & 0xff,
1111 apm_bios_info.version & 0xff );
1112 apm_bios_info.version = 0x0101;
1113 }
1114 }
1115
1116 error = apm_get_power_status(&bx, &cx, &dx);
1117 if (error)
1118 printk(" Power status not available\n");
1119 else {
1120 switch ((bx >> 8) & 0xff) {
1121 case 0: power_stat = "off line"; break;
1122 case 1: power_stat = "on line"; break;
1123 case 2: power_stat = "on backup power"; break;
1124 default: power_stat = "unknown"; break;
1125 }
1126 switch (bx & 0xff) {
1127 case 0: bat_stat = "high"; break;
1128 case 1: bat_stat = "low"; break;
1129 case 2: bat_stat = "critical"; break;
1130 case 3: bat_stat = "charging"; break;
1131 default: bat_stat = "unknown"; break;
1132 }
1133 printk(" AC %s, battery status %s, battery life ",
1134 power_stat, bat_stat);
1135 if ((cx & 0xff) == 0xff)
1136 printk("unknown\n");
1137 else
1138 printk("%d%%\n", cx & 0xff);
1139 if (apm_bios_info.version > 0x100) {
1140 printk(" battery flag 0x%02x, battery life ",
1141 (cx >> 8) & 0xff);
1142 if (dx == 0xffff)
1143 printk("unknown\n");
1144 else {
1145 if ((dx & 0x8000))
1146 printk("%d minutes\n", dx & 0x7ffe );
1147 else
1148 printk("%d seconds\n", dx & 0x7fff );
1149 }
1150 }
1151 }
1152
1153 #ifdef CONFIG_APM_DO_ENABLE
1154
1155
1156
1157
1158
1159 error = apm_enable_power_management();
1160 if (error)
1161 apm_error("enable power management", error);
1162 if (error == APM_DISABLED)
1163 return;
1164 #endif
1165
1166 init_timer(&apm_timer);
1167 apm_timer.function = do_apm_timer;
1168 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
1169 add_timer(&apm_timer);
1170
1171 register_symtab(&apm_syms);
1172
1173 #ifdef CONFIG_PROC_FS
1174 proc_register_dynamic(&proc_root, &apm_proc_entry);
1175 #endif
1176
1177 misc_register(&apm_device);
1178
1179 apm_enabled = 1;
1180 }