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 suspends_pending = 0;
323 static int standbys_pending = 0;
324
325 static long clock_cmos_diff;
326 static int got_clock_diff = 0;
327
328 static struct wait_queue * process_list = NULL;
329 static struct apm_bios_struct * user_list = NULL;
330
331 static struct timer_list apm_timer;
332
333 static char driver_version[] = "1.0";
334
335 #ifdef APM_DEBUG
336 static char * apm_event_name[] = {
337 "system standby",
338 "system suspend",
339 "normal resume",
340 "critical resume",
341 "low battery",
342 "power status change",
343 "update time",
344 "critical suspend",
345 "user standby",
346 "user suspend",
347 "system standby resume"
348 };
349 #define NR_APM_EVENT_NAME \
350 (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
351 #endif
352
353 static struct file_operations apm_bios_fops = {
354 NULL,
355 do_read,
356 NULL,
357 NULL,
358 do_select,
359 do_ioctl,
360 NULL,
361 do_open,
362 do_release,
363 NULL,
364 NULL
365 };
366
367 static struct miscdevice apm_device = {
368 APM_MINOR_DEV,
369 "apm",
370 &apm_bios_fops
371 };
372
373 #ifdef CONFIG_PROC_FS
374 static struct proc_dir_entry apm_proc_entry = {
375 0, 3, "apm", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, apm_get_info
376 };
377 #endif
378
379 typedef struct callback_list_t {
380 int (* callback)(apm_event_t);
381 struct callback_list_t * next;
382 } callback_list_t;
383
384 static callback_list_t * callback_list = NULL;
385
386 typedef struct lookup_t {
387 int key;
388 char * msg;
389 } lookup_t;
390
391 static const lookup_t error_table[] = {
392
393 { APM_DISABLED, "Power management disabled" },
394 { APM_CONNECTED, "Real mode interface already connected" },
395 { APM_NOT_CONNECTED, "Interface not connected" },
396 { APM_16_CONNECTED, "16 bit interface already connected" },
397
398 { APM_32_CONNECTED, "32 bit interface already connected" },
399 { APM_32_UNSUPPORTED, "32 bit interface not supported" },
400 { APM_BAD_DEVICE, "Unrecognized device ID" },
401 { APM_BAD_PARAM, "Parameter out of range" },
402 { APM_NOT_ENGAGED, "Interface not engaged" },
403 { APM_BAD_STATE, "Unable to enter requested state" },
404
405 { APM_NOT_PRESENT, "No APM present" }
406 };
407 #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
408
409 static int apm_driver_version(u_short *val)
410 {
411 u_short error;
412
413 APM_DRIVER_VERSION(*val, *val, error);
414
415 if (error & 0xff)
416 return (*val >> 8);
417 return APM_SUCCESS;
418 }
419
420 static int apm_get_event(apm_event_t *event)
421 {
422 u_short error;
423
424 APM_GET_EVENT(*event, error);
425 if (error & 0xff)
426 return (error >> 8);
427 return APM_SUCCESS;
428 }
429
430 static int apm_set_power_state(u_short state)
431 {
432 u_short error;
433
434 APM_SET_POWER_STATE(state, error);
435 if (error & 0xff)
436 return (error >> 8);
437 return APM_SUCCESS;
438 }
439
440 #ifdef CONFIG_APM_DISPLAY_BLANK
441
442 static int apm_set_display_power_state(u_short state)
443 {
444 u_short error;
445
446 APM_SET_DISPLAY_POWER_STATE(state, error);
447 if (error & 0xff)
448 return (error >> 8);
449 return APM_SUCCESS;
450 }
451 #endif
452
453 #ifdef CONFIG_APM_DO_ENABLE
454
455 static int apm_enable_power_management(void)
456 {
457 u_short error;
458
459 APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
460 ? 0x0001 : 0xffff,
461 error);
462 if (error & 0xff)
463 return (error >> 8);
464 return APM_SUCCESS;
465 }
466 #endif
467
468 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
469 {
470 u_short error;
471
472 APM_GET_POWER_STATUS(*status, *bat, *life, error);
473 if (error & 0xff)
474 return (error >> 8);
475 return APM_SUCCESS;
476 }
477
478 static int apm_engage_power_management(u_short device)
479 {
480 u_short error;
481
482 APM_ENGAGE_POWER_MANAGEMENT(device, error);
483 if (error & 0xff)
484 return (error >> 8);
485 return APM_SUCCESS;
486 }
487
488 static void apm_error(char *str, int err)
489 {
490 int i;
491
492 for (i = 0; i < ERROR_COUNT; i++)
493 if (error_table[i].key == err) break;
494 if (i < ERROR_COUNT)
495 printk("apm_bios: %s: %s\n", str, error_table[i].msg);
496 else
497 printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
498 }
499
500
501 int apm_display_blank(void)
502 {
503 #ifdef CONFIG_APM_DISPLAY_BLANK
504 int error;
505
506 if (!apm_enabled)
507 return 0;
508 error = apm_set_display_power_state(APM_STATE_STANDBY);
509 if (error == APM_SUCCESS)
510 return 1;
511 apm_error("set display standby", error);
512 #endif
513 return 0;
514 }
515
516
517 int apm_display_unblank(void)
518 {
519 #ifdef CONFIG_APM_DISPLAY_BLANK
520 int error;
521
522 if (!apm_enabled)
523 return 0;
524 error = apm_set_display_power_state(APM_STATE_READY);
525 if (error == APM_SUCCESS)
526 return 1;
527 apm_error("set display ready", error);
528 #endif
529 return 0;
530 }
531
532 int apm_register_callback(int (*callback)(apm_event_t))
533 {
534 callback_list_t * new;
535
536 new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
537 if (new == NULL)
538 return -ENOMEM;
539 new->callback = callback;
540 new->next = callback_list;
541 callback_list = new;
542 return 0;
543 }
544
545 void apm_unregister_callback(int (*callback)(apm_event_t))
546 {
547 callback_list_t ** ptr;
548 callback_list_t * old;
549
550 ptr = &callback_list;
551 for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
552 if ((*ptr)->callback == callback)
553 break;
554 old = *ptr;
555 *ptr = old->next;
556 kfree_s(old, sizeof(callback_list_t));
557 }
558
559 static int queue_empty(struct apm_bios_struct * as)
560 {
561 return as->event_head == as->event_tail;
562 }
563
564 static apm_event_t get_queued_event(struct apm_bios_struct * as)
565 {
566 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
567 return as->events[as->event_tail];
568 }
569
570 static int queue_event(apm_event_t event)
571 {
572 struct apm_bios_struct * as;
573
574 if (user_list == NULL)
575 return 0;
576 for (as = user_list; as != NULL; as = as->next) {
577 as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
578 if (as->event_head == as->event_tail)
579 as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
580 as->events[as->event_head] = event;
581 if (!as->suser)
582 continue;
583 switch (event) {
584 case APM_SYS_SUSPEND:
585 case APM_USER_SUSPEND:
586 as->suspends_pending++;
587 suspends_pending++;
588 break;
589
590 case APM_SYS_STANDBY:
591 case APM_USER_STANDBY:
592 as->standbys_pending++;
593 standbys_pending++;
594 break;
595 }
596 }
597 wake_up_interruptible(&process_list);
598 return 1;
599 }
600
601 static void set_time(void)
602 {
603 unsigned long flags;
604
605 if (!got_clock_diff)
606 return;
607
608 save_flags(flags);
609 cli();
610 CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
611 restore_flags(flags);
612 }
613
614 static void suspend(void)
615 {
616 unsigned long flags;
617 int err;
618
619
620
621 save_flags(flags);
622 cli();
623 clock_cmos_diff = CURRENT_TIME - get_cmos_time();
624 got_clock_diff = 1;
625 restore_flags(flags);
626
627 err = apm_set_power_state(APM_STATE_SUSPEND);
628 if (err)
629 apm_error("suspend", err);
630 set_time();
631 }
632
633 static void standby(void)
634 {
635 int err;
636
637 err = apm_set_power_state(APM_STATE_STANDBY);
638 if (err)
639 apm_error("standby", err);
640 }
641
642 static apm_event_t get_event(void)
643 {
644 int error;
645 apm_event_t event;
646
647 static int notified = 0;
648
649 error = apm_get_event(&event);
650 if (error == APM_SUCCESS)
651 return event;
652
653 if ((error != APM_NO_EVENTS) && (notified++ == 0))
654 apm_error("get_event", error);
655
656 return 0;
657 }
658
659 static void send_event(apm_event_t event, apm_event_t undo)
660 {
661 callback_list_t * call;
662 callback_list_t * fix;
663
664 for (call = callback_list; call != NULL; call = call->next) {
665 if (call->callback(event) && undo) {
666 for (fix = callback_list; fix != call; fix = fix->next)
667 fix->callback(undo);
668 if (apm_bios_info.version > 0x100)
669 apm_set_power_state(APM_STATE_REJECT);
670 return;
671 }
672 }
673
674 queue_event(event);
675 }
676
677 static void check_events(void)
678 {
679 apm_event_t event;
680
681 while ((event = get_event()) != 0) {
682 switch (event) {
683 case APM_SYS_STANDBY:
684 case APM_USER_STANDBY:
685 send_event(event, APM_STANDBY_RESUME);
686 if (standbys_pending <= 0)
687 standby();
688 break;
689
690 case APM_USER_SUSPEND:
691 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
692 apm_set_power_state(APM_STATE_REJECT);
693 break;
694 #endif
695 case APM_SYS_SUSPEND:
696 send_event(event, APM_NORMAL_RESUME);
697 if (suspends_pending <= 0)
698 suspend();
699 break;
700
701 case APM_NORMAL_RESUME:
702 case APM_CRITICAL_RESUME:
703 case APM_STANDBY_RESUME:
704 set_time();
705 send_event(event, 0);
706 break;
707
708 case APM_LOW_BATTERY:
709 case APM_POWER_STATUS_CHANGE:
710 send_event(event, 0);
711 break;
712
713 case APM_UPDATE_TIME:
714 set_time();
715 break;
716
717 case APM_CRITICAL_SUSPEND:
718 suspend();
719 break;
720 }
721 #ifdef APM_DEBUG
722 if (event <= NR_APM_EVENT_NAME)
723 printk("APM BIOS received %s notify\n",
724 apm_event_name[event - 1]);
725 else
726 printk("APM BIOS received unknown event 0x%02x\n",
727 event);
728 #endif
729 }
730 }
731
732 static void do_apm_timer(unsigned long unused)
733 {
734 int err;
735
736 static int pending_count = 0;
737
738 if (((standbys_pending > 0) || (suspends_pending > 0))
739 && (apm_bios_info.version > 0x100)
740 && (pending_count-- <= 0)) {
741 pending_count = 4;
742
743 err = apm_set_power_state(APM_STATE_BUSY);
744 if (err)
745 apm_error("busy", err);
746 }
747
748 if (!(((standbys_pending > 0) || (suspends_pending > 0))
749 && (apm_bios_info.version == 0x100)))
750 check_events();
751
752 init_timer(&apm_timer);
753 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
754 add_timer(&apm_timer);
755 }
756
757
758 int apm_do_idle(void)
759 {
760 #ifdef CONFIG_APM_CPU_IDLE
761 unsigned short error;
762
763 if (!apm_enabled)
764 return 0;
765
766 APM_SET_CPU_IDLE(error);
767 if (error & 0xff)
768 return 0;
769
770 clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
771 return 1;
772 #else
773 return 0;
774 #endif
775 }
776
777
778 void apm_do_busy(void)
779 {
780 #ifdef CONFIG_APM_CPU_IDLE
781 unsigned short error;
782
783 if (!apm_enabled)
784 return;
785
786 #ifndef ALWAYS_CALL_BUSY
787 if (!clock_slowed)
788 return;
789 #endif
790
791 APM_SET_CPU_BUSY(error);
792
793 clock_slowed = 0;
794 #endif
795 }
796
797 static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
798 {
799 if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
800 printk("apm_bios: %s passed bad filp", func);
801 return 1;
802 }
803 return 0;
804 }
805
806 static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
807 {
808 struct apm_bios_struct * as;
809 int i;
810 apm_event_t event;
811 struct wait_queue wait = { current, NULL };
812
813 as = fp->private_data;
814 if (check_apm_bios_struct(as, "read"))
815 return -EIO;
816 if (count < sizeof(apm_event_t))
817 return -EINVAL;
818 if (queue_empty(as)) {
819 if (fp->f_flags & O_NONBLOCK)
820 return -EAGAIN;
821 add_wait_queue(&process_list, &wait);
822 repeat:
823 current->state = TASK_INTERRUPTIBLE;
824 if (queue_empty(as)
825 && !(current->signal & ~current->blocked)) {
826 schedule();
827 goto repeat;
828 }
829 current->state = TASK_RUNNING;
830 remove_wait_queue(&process_list, &wait);
831 }
832 i = count;
833 while ((i >= sizeof(event)) && !queue_empty(as)) {
834 event = get_queued_event(as);
835 memcpy_tofs(buf, &event, sizeof(event));
836 buf += sizeof(event);
837 i -= sizeof(event);
838 }
839 if (i < count)
840 return count - i;
841 if (current->signal & ~current->blocked)
842 return -ERESTARTSYS;
843 return 0;
844 }
845
846 static int do_select(struct inode *inode, struct file *fp, int sel_type,
847 select_table * wait)
848 {
849 struct apm_bios_struct * as;
850
851 as = fp->private_data;
852 if (check_apm_bios_struct(as, "select"))
853 return 0;
854 if (sel_type != SEL_IN)
855 return 0;
856 if (!queue_empty(as))
857 return 1;
858 select_wait(&process_list, wait);
859 return 0;
860 }
861
862 static int do_ioctl(struct inode * inode, struct file *filp,
863 u_int cmd, u_long arg)
864 {
865 struct apm_bios_struct * as;
866
867 as = filp->private_data;
868 if (check_apm_bios_struct(as, "ioctl"))
869 return -EIO;
870 switch (cmd) {
871 case APM_IOC_STANDBY:
872 if (as->standbys_pending > 0) {
873 as->standbys_pending--;
874 standbys_pending--;
875 if (standbys_pending <= 0)
876 standby();
877 }
878 break;
879 case APM_IOC_SUSPEND:
880 if (as->suspends_pending > 0) {
881 as->suspends_pending--;
882 suspends_pending--;
883 if (suspends_pending <= 0)
884 suspend();
885 }
886 break;
887 default:
888 return -EINVAL;
889 }
890 return 0;
891 }
892
893 static void do_release(struct inode * inode, struct file * filp)
894 {
895 struct apm_bios_struct * as;
896
897 as = filp->private_data;
898 filp->private_data = NULL;
899 if (check_apm_bios_struct(as, "release"))
900 return;
901 if (as->standbys_pending > 0) {
902 standbys_pending -= as->standbys_pending;
903 if (standbys_pending <= 0)
904 standby();
905 }
906 if (as->suspends_pending > 0) {
907 suspends_pending -= as->suspends_pending;
908 if (suspends_pending <= 0)
909 suspend();
910 }
911 if (user_list == as)
912 user_list = as->next;
913 else {
914 struct apm_bios_struct * as1;
915
916 for (as1 = user_list;
917 (as1 != NULL) && (as1->next != as);
918 as1 = as1->next)
919 ;
920 if (as1 == NULL)
921 printk("apm_bios: filp not in user list");
922 else
923 as1->next = as->next;
924 }
925 kfree_s(as, sizeof(*as));
926 }
927
928 static int do_open(struct inode * inode, struct file * filp)
929 {
930 struct apm_bios_struct * as;
931
932 as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
933 if (as == NULL) {
934 printk("apm_bios: cannot allocate struct of size %d bytes",
935 sizeof(*as));
936 return -ENOMEM;
937 }
938 as->magic = APM_BIOS_MAGIC;
939 as->event_tail = as->event_head = 0;
940 as->suspends_pending = as->standbys_pending = 0;
941 as->suser = suser();
942 as->next = user_list;
943 user_list = as;
944 filp->private_data = as;
945 return 0;
946 }
947
948 #ifdef CONFIG_PROC_FS
949 int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
950 {
951 char * p;
952 unsigned short bx;
953 unsigned short cx;
954 unsigned short dx;
955 unsigned short error;
956 unsigned short ac_line_status = 0xff;
957 unsigned short battery_status = 0xff;
958 unsigned short battery_flag = 0xff;
959 int percentage = -1;
960 int time_units = -1;
961 char *units = "?";
962
963 if (!apm_enabled)
964 return 0;
965 p = buf;
966
967 if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
968 ac_line_status = (bx >> 8) & 0xff;
969 battery_status = bx & 0xff;
970 if ((cx & 0xff) != 0xff)
971 percentage = cx & 0xff;
972
973 if (apm_bios_info.version > 0x100) {
974 battery_flag = (cx >> 8) & 0xff;
975 if (dx != 0xffff) {
976 if ((dx & 0x8000) == 0x8000) {
977 units = "min";
978 time_units = dx & 0x7ffe;
979 } else {
980 units = "sec";
981 time_units = dx & 0x7fff;
982 }
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 p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
1024 driver_version,
1025 (apm_bios_info.version >> 8) & 0xff,
1026 apm_bios_info.version & 0xff,
1027 apm_bios_info.flags,
1028 ac_line_status,
1029 battery_status,
1030 battery_flag,
1031 percentage,
1032 time_units,
1033 units);
1034
1035 return p - buf;
1036 }
1037 #endif
1038
1039 void apm_bios_init(void)
1040 {
1041 unsigned short bx;
1042 unsigned short cx;
1043 unsigned short dx;
1044 unsigned short error;
1045 char * power_stat;
1046 char * bat_stat;
1047
1048 if (apm_bios_info.version == 0) {
1049 printk("APM BIOS not found.\n");
1050 return;
1051 }
1052 printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
1053 ((apm_bios_info.version >> 8) & 0xff) + '0',
1054 (apm_bios_info.version & 0xff) + '0',
1055 apm_bios_info.flags,
1056 driver_version);
1057 if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
1058 printk(" No 32 bit BIOS support\n");
1059 return;
1060 }
1061
1062
1063
1064
1065
1066 if (apm_bios_info.version == 0x001)
1067 apm_bios_info.version = 0x100;
1068
1069 printk(" Entry %x:%lx cseg16 %x dseg %x",
1070 apm_bios_info.cseg, apm_bios_info.offset,
1071 apm_bios_info.cseg_16, apm_bios_info.dseg);
1072 if (apm_bios_info.version > 0x100)
1073 printk(" cseg len %x, dseg len %x",
1074 apm_bios_info.cseg_len, apm_bios_info.dseg_len);
1075 printk("\n");
1076
1077 apm_bios_entry.offset = apm_bios_info.offset;
1078 apm_bios_entry.segment = APM_CS;
1079 set_base(gdt[APM_CS >> 3],
1080 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
1081 set_base(gdt[APM_CS_16 >> 3],
1082 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
1083 set_base(gdt[APM_DS >> 3],
1084 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
1085 if (apm_bios_info.version == 0x100) {
1086 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1087 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1088 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1089 } else {
1090 #ifdef APM_RELAX_SEGMENTS
1091
1092 set_limit(gdt[APM_CS >> 3], 64 * 1024);
1093
1094 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1095
1096 set_limit(gdt[APM_DS >> 3], 64 * 1024);
1097 #else
1098 set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
1099 set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
1100 set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
1101 #endif
1102 apm_bios_info.version = 0x0101;
1103 error = apm_driver_version(&apm_bios_info.version);
1104 if (error != 0)
1105 apm_bios_info.version = 0x100;
1106 else {
1107 apm_engage_power_management(0x0001);
1108 printk( " Connection version %d.%d\n",
1109 (apm_bios_info.version >> 8) & 0xff,
1110 apm_bios_info.version & 0xff );
1111 apm_bios_info.version = 0x0101;
1112 }
1113 }
1114
1115 error = apm_get_power_status(&bx, &cx, &dx);
1116 if (error)
1117 printk(" Power status not available\n");
1118 else {
1119 switch ((bx >> 8) & 0xff) {
1120 case 0: power_stat = "off line"; break;
1121 case 1: power_stat = "on line"; break;
1122 case 2: power_stat = "on backup power"; break;
1123 default: power_stat = "unknown"; break;
1124 }
1125 switch (bx & 0xff) {
1126 case 0: bat_stat = "high"; break;
1127 case 1: bat_stat = "low"; break;
1128 case 2: bat_stat = "critical"; break;
1129 case 3: bat_stat = "charging"; break;
1130 default: bat_stat = "unknown"; break;
1131 }
1132 printk(" AC %s, battery status %s, battery life ",
1133 power_stat, bat_stat);
1134 if ((cx & 0xff) == 0xff)
1135 printk("unknown\n");
1136 else
1137 printk("%d%%\n", cx & 0xff);
1138 if (apm_bios_info.version > 0x100) {
1139 printk(" battery flag 0x%02x, battery life ",
1140 (cx >> 8) & 0xff);
1141 if (dx == 0xffff)
1142 printk("unknown\n");
1143 else {
1144 if ((dx & 0x8000))
1145 printk("%d minutes\n", dx & 0x7ffe );
1146 else
1147 printk("%d seconds\n", dx & 0x7fff );
1148 }
1149 }
1150 }
1151
1152 #ifdef CONFIG_APM_DO_ENABLE
1153
1154
1155
1156
1157
1158 error = apm_enable_power_management();
1159 if (error)
1160 apm_error("enable power management", error);
1161 if (error == APM_DISABLED)
1162 return;
1163 #endif
1164
1165 init_timer(&apm_timer);
1166 apm_timer.function = do_apm_timer;
1167 apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
1168 add_timer(&apm_timer);
1169
1170 register_symtab(&apm_syms);
1171
1172 #ifdef CONFIG_PROC_FS
1173 proc_register_dynamic(&proc_root, &apm_proc_entry);
1174 #endif
1175
1176 misc_register(&apm_device);
1177
1178 apm_enabled = 1;
1179 }