Add support for sending debug messages to a serial port.
[seabios.git] / src / serial.c
1 // 16bit code to handle serial and printer services.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "biosvar.h" // struct bregs
9 #include "util.h" // debug_enter
10
11
12 /****************************************************************
13  * COM ports
14  ****************************************************************/
15
16 static u16
17 detect_serial(u16 port, u8 timeout, u8 count)
18 {
19     outb(0x02, port+1);
20     if (inb(port+1) != 0x02)
21         return 0;
22     if (inb(port+2) != 0x02)
23         return 0;
24     outb(0x00, port+1);
25     SET_BDA(port_com[count], port);
26     SET_BDA(com_timeout[count], timeout);
27     return 1;
28 }
29
30 void
31 serial_setup()
32 {
33     u16 count = 0;
34     count += detect_serial(0x3f8, 0x0a, count);
35     count += detect_serial(0x2f8, 0x0a, count);
36     count += detect_serial(0x3e8, 0x0a, count);
37     count += detect_serial(0x2e8, 0x0a, count);
38
39     // Equipment word bits 9..11 determing # serial ports
40     u16 eqb = GET_BDA(equipment_list_flags);
41     SET_BDA(equipment_list_flags, (eqb & 0xf1ff) | (count << 9));
42 }
43
44 static u16
45 getComAddr(struct bregs *regs)
46 {
47     if (regs->dx >= 4) {
48         set_fail(regs);
49         return 0;
50     }
51     u16 addr = GET_BDA(port_com[regs->dx]);
52     if (! addr)
53         set_fail(regs);
54     return addr;
55 }
56
57 // SERIAL - INITIALIZE PORT
58 static void
59 handle_1400(struct bregs *regs)
60 {
61     u16 addr = getComAddr(regs);
62     if (!addr)
63         return;
64     outb(inb(addr+3) | 0x80, addr+3);
65     if ((regs->al & 0xE0) == 0) {
66         outb(0x17, addr);
67         outb(0x04, addr+1);
68     } else {
69         u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5);
70         outb(val16 & 0xFF, addr);
71         outb(val16 >> 8, addr+1);
72     }
73     outb(regs->al & 0x1F, addr+3);
74     regs->ah = inb(addr+5);
75     regs->al = inb(addr+6);
76     set_success(regs);
77 }
78
79 static int
80 write_serial(u16 addr, u16 timeout, char c)
81 {
82     u16 timer = GET_BDA(timer_counter);
83     while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
84         u16 val16 = GET_BDA(timer_counter);
85         if (val16 != timer) {
86             timer = val16;
87             timeout--;
88         }
89     }
90     if (!timeout)
91         // Ran out of time.
92         return -1;
93     outb(c, addr);
94     return 0;
95 }
96
97 // SERIAL - WRITE CHARACTER TO PORT
98 static void
99 handle_1401(struct bregs *regs)
100 {
101     u16 addr = getComAddr(regs);
102     if (!addr)
103         return;
104     u16 timeout = GET_BDA(com_timeout[regs->dx]);
105     int ret = write_serial(addr, timeout, regs->al);
106     regs->ah = inb(addr+5);
107     if (ret)
108         regs->ah |= 0x80;
109     set_success(regs);
110 }
111
112 // SERIAL - READ CHARACTER FROM PORT
113 static void
114 handle_1402(struct bregs *regs)
115 {
116     u16 addr = getComAddr(regs);
117     if (!addr)
118         return;
119     u16 timer = GET_BDA(timer_counter);
120     u16 timeout = GET_BDA(com_timeout[regs->dx]);
121     while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
122         u16 val16 = GET_BDA(timer_counter);
123         if (val16 != timer) {
124             timer = val16;
125             timeout--;
126         }
127     }
128     if (timeout) {
129         regs->ah = 0;
130         regs->al = inb(addr);
131     } else {
132         regs->ah = inb(addr+5);
133     }
134     set_success(regs);
135 }
136
137 // SERIAL - GET PORT STATUS
138 static void
139 handle_1403(struct bregs *regs)
140 {
141     u16 addr = getComAddr(regs);
142     if (!addr)
143         return;
144     regs->ah = inb(addr+5);
145     regs->al = inb(addr+6);
146     set_success(regs);
147 }
148
149 static void
150 handle_14XX(struct bregs *regs)
151 {
152     // Unsupported
153     set_fail(regs);
154 }
155
156 // INT 14h Serial Communications Service Entry Point
157 void VISIBLE16
158 handle_14(struct bregs *regs)
159 {
160     debug_enter(regs);
161
162     irq_enable();
163
164     switch (regs->ah) {
165     case 0x00: handle_1400(regs); break;
166     case 0x01: handle_1401(regs); break;
167     case 0x02: handle_1402(regs); break;
168     case 0x03: handle_1403(regs); break;
169     default:   handle_14XX(regs); break;
170     }
171 }
172
173
174 /****************************************************************
175  * Serial debugging
176  ****************************************************************/
177
178 #define BX_DEBUG_PORT 0x03f8
179
180 void
181 debug_serial_setup()
182 {
183     /* setup for serial logging: 8N1 */
184     outb(0x03, BX_DEBUG_PORT+3);
185 }
186
187 void
188 debug_serial(char c)
189 {
190     write_serial(BX_DEBUG_PORT, 0x0a, c);
191 }
192
193
194 /****************************************************************
195  * LPT ports
196  ****************************************************************/
197
198 static u16
199 detect_parport(u16 port, u8 timeout, u8 count)
200 {
201     // clear input mode
202     outb(inb(port+2) & 0xdf, port+2);
203
204     outb(0xaa, port);
205     if (inb(port) != 0xaa)
206         // Not present
207         return 0;
208     SET_BDA(port_lpt[count], port);
209     SET_BDA(lpt_timeout[count], timeout);
210     return 1;
211 }
212
213 void
214 lpt_setup()
215 {
216     u16 count = 0;
217     count += detect_parport(0x378, 0x14, count);
218     count += detect_parport(0x278, 0x14, count);
219
220     // Equipment word bits 14..15 determing # parallel ports
221     u16 eqb = GET_BDA(equipment_list_flags);
222     SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
223 }
224
225 static u16
226 getLptAddr(struct bregs *regs)
227 {
228     if (regs->dx >= 3) {
229         set_fail(regs);
230         return 0;
231     }
232     u16 addr = GET_BDA(port_lpt[regs->dx]);
233     if (! addr)
234         set_fail(regs);
235     return addr;
236 }
237
238 static void
239 lpt_ret(struct bregs *regs, u16 addr, u16 timeout)
240 {
241     u8 val8 = inb(addr+1);
242     regs->ah = (val8 ^ 0x48);
243     if (!timeout)
244         regs->ah |= 0x01;
245     set_success(regs);
246 }
247
248 // INT 17 - PRINTER - WRITE CHARACTER
249 static void
250 handle_1700(struct bregs *regs)
251 {
252     u16 addr = getLptAddr(regs);
253     if (!addr)
254         return;
255     u16 timeout = GET_BDA(lpt_timeout[regs->dx]) << 8;
256
257     outb(regs->al, addr);
258     u8 val8 = inb(addr+2);
259     outb(val8 | 0x01, addr+2); // send strobe
260     nop();
261     outb(val8 & ~0x01, addr+2);
262     while (((inb(addr+1) & 0x40) == 0x40) && (timeout))
263         timeout--;
264
265     lpt_ret(regs, addr, timeout);
266 }
267
268 // INT 17 - PRINTER - INITIALIZE PORT
269 static void
270 handle_1701(struct bregs *regs)
271 {
272     u16 addr = getLptAddr(regs);
273     if (!addr)
274         return;
275     u16 timeout = GET_BDA(lpt_timeout[regs->dx]) << 8;
276
277     u8 val8 = inb(addr+2);
278     outb(val8 & ~0x04, addr+2); // send init
279     nop();
280     outb(val8 | 0x04, addr+2);
281
282     lpt_ret(regs, addr, timeout);
283 }
284
285 // INT 17 - PRINTER - GET STATUS
286 static void
287 handle_1702(struct bregs *regs)
288 {
289     u16 addr = getLptAddr(regs);
290     if (!addr)
291         return;
292     u16 timeout = GET_BDA(lpt_timeout[regs->dx]) << 8;
293
294     lpt_ret(regs, addr, timeout);
295 }
296
297 static void
298 handle_17XX(struct bregs *regs)
299 {
300     // Unsupported
301     set_fail(regs);
302 }
303
304 // INT17h : Printer Service Entry Point
305 void VISIBLE16
306 handle_17(struct bregs *regs)
307 {
308     debug_enter(regs);
309
310     irq_enable();
311
312     switch (regs->ah) {
313     case 0x00: handle_1700(regs); break;
314     case 0x01: handle_1701(regs); break;
315     case 0x02: handle_1702(regs); break;
316     default:   handle_17XX(regs); break;
317     }
318 }