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