grml...
[seabios.git] / src / apm.c
1 // Basic support for apmbios callbacks.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2005 Struan Bartlett
5 // Copyright (C) 2004 Fabrice Bellard
6 //
7 // This file may be distributed under the terms of the GNU LGPLv3 license.
8
9 #include "farptr.h" // GET_VAR
10 #include "bregs.h" // struct bregs
11 #include "ioport.h" // outb
12 #include "util.h" // wait_irq
13 #include "config.h" // CONFIG_*
14 #include "biosvar.h" // GET_GLOBAL
15
16 static void
17 out_str(const char *str_cs)
18 {
19     if (CONFIG_COREBOOT) {
20         dprintf(1, "APM request '%s'\n", str_cs);
21         return;
22     }
23
24     u8 *s = (u8*)str_cs;
25     for (;;) {
26         u8 c = GET_GLOBAL(*s);
27         if (!c)
28             break;
29         outb(c, PORT_BIOS_APM);
30         s++;
31     }
32 }
33
34 // APM installation check
35 static void
36 handle_155300(struct bregs *regs)
37 {
38     regs->ah = 1; // APM major version
39     regs->al = 2; // APM minor version
40     regs->bh = 'P';
41     regs->bl = 'M';
42     // bit 0 : 16 bit interface supported
43     // bit 1 : 32 bit interface supported
44     regs->cx = 0x03;
45     set_success(regs);
46 }
47
48 // APM real mode interface connect
49 static void
50 handle_155301(struct bregs *regs)
51 {
52     set_success(regs);
53 }
54
55 // Assembler entry points defined in romlayout.S
56 extern void entry_apm16(void);
57 extern void entry_apm32(void);
58
59 // APM 16 bit protected mode interface connect
60 static void
61 handle_155302(struct bregs *regs)
62 {
63     regs->bx = (u32)entry_apm16;
64     regs->ax = SEG_BIOS; // 16 bit code segment base
65     regs->si = 0xfff0;   // 16 bit code segment size
66     regs->cx = SEG_BIOS; // data segment address
67     regs->di = 0xfff0;   // data segment length
68     set_success(regs);
69 }
70
71 // APM 32 bit protected mode interface connect
72 static void
73 handle_155303(struct bregs *regs)
74 {
75     regs->ax = SEG_BIOS; // 32 bit code segment base
76     regs->ebx = (u32)entry_apm32;
77     regs->cx = SEG_BIOS; // 16 bit code segment base
78     // 32 bit code segment size (low 16 bits)
79     // 16 bit code segment size (high 16 bits)
80     regs->esi = 0xfff0fff0;
81     regs->dx = SEG_BIOS; // data segment address
82     regs->di = 0xfff0; // data segment length
83     set_success(regs);
84 }
85
86 // APM interface disconnect
87 static void
88 handle_155304(struct bregs *regs)
89 {
90     set_success(regs);
91 }
92
93 // APM cpu idle
94 static void
95 handle_155305(struct bregs *regs)
96 {
97     wait_irq();
98     set_success(regs);
99 }
100
101 // APM cpu busy
102 static void
103 handle_155306(struct bregs *regs)
104 {
105     set_success(regs);
106 }
107
108 void
109 apm_shutdown(void)
110 {
111     irq_disable();
112     out_str("Shutdown");
113     for (;;)
114         hlt();
115 }
116
117 // APM Set Power State
118 static void
119 handle_155307(struct bregs *regs)
120 {
121     if (regs->bx != 1) {
122         set_success(regs);
123         return;
124     }
125     switch (regs->cx) {
126     case 1:
127         out_str("Standby");
128         break;
129     case 2:
130         out_str("Suspend");
131         break;
132     case 3:
133         apm_shutdown();
134         break;
135     }
136     set_success(regs);
137 }
138
139 static void
140 handle_155308(struct bregs *regs)
141 {
142     set_success(regs);
143 }
144
145 // Get Power Status
146 static void
147 handle_15530a(struct bregs *regs)
148 {
149     regs->bh = 0x01; // on line
150     regs->bl = 0xff; // unknown battery status
151     regs->ch = 0x80; // no system battery
152     regs->cl = 0xff; // unknown remaining time
153     regs->dx = 0xffff; // unknown remaining time
154     regs->si = 0x00; // zero battery
155     set_success(regs);
156 }
157
158 #define RET_ENOEVENT 0x80
159
160 // Get PM Event
161 static void
162 handle_15530b(struct bregs *regs)
163 {
164     set_code_invalid_silent(regs, RET_ENOEVENT);
165 }
166
167 // APM Driver Version
168 static void
169 handle_15530e(struct bregs *regs)
170 {
171     regs->ah = 1;
172     regs->al = 2;
173     set_success(regs);
174 }
175
176 // APM Engage / Disengage
177 static void
178 handle_15530f(struct bregs *regs)
179 {
180     set_success(regs);
181 }
182
183 // APM Get Capabilities
184 static void
185 handle_155310(struct bregs *regs)
186 {
187     regs->bl = 0;
188     regs->cx = 0;
189     set_success(regs);
190 }
191
192 static void
193 handle_1553XX(struct bregs *regs)
194 {
195     set_unimplemented(regs);
196 }
197
198 void
199 handle_1553(struct bregs *regs)
200 {
201     if (! CONFIG_APMBIOS) {
202         set_code_invalid(regs, RET_EUNSUPPORTED);
203         return;
204     }
205
206     //debug_stub(regs);
207     switch (regs->al) {
208     case 0x00: handle_155300(regs); break;
209     case 0x01: handle_155301(regs); break;
210     case 0x02: handle_155302(regs); break;
211     case 0x03: handle_155303(regs); break;
212     case 0x04: handle_155304(regs); break;
213     case 0x05: handle_155305(regs); break;
214     case 0x06: handle_155306(regs); break;
215     case 0x07: handle_155307(regs); break;
216     case 0x08: handle_155308(regs); break;
217     case 0x0a: handle_15530a(regs); break;
218     case 0x0b: handle_15530b(regs); break;
219     case 0x0e: handle_15530e(regs); break;
220     case 0x0f: handle_15530f(regs); break;
221     case 0x10: handle_155310(regs); break;
222     default:   handle_1553XX(regs); break;
223     }
224 }
225
226 void VISIBLE16
227 handle_apm16(struct bregs *regs)
228 {
229     debug_enter(regs, DEBUG_HDL_apm);
230     handle_1553(regs);
231 }
232
233 void VISIBLE32SEG
234 handle_apm32(struct bregs *regs)
235 {
236     debug_enter(regs, DEBUG_HDL_apm);
237     handle_1553(regs);
238 }