vgabios: Use standard VGA IO wrappers in geodevga.c.
[seabios.git] / src / serial.c
1 // 16bit code to handle serial and printer services.
2 //
3 // Copyright (C) 2008,2009  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 LGPLv3 license.
7
8 #include "biosvar.h" // SET_BDA
9 #include "util.h" // debug_enter
10 #include "bregs.h" // struct bregs
11
12
13 /****************************************************************
14  * COM ports
15  ****************************************************************/
16
17 static u16
18 detect_serial(u16 port, u8 timeout, u8 count)
19 {
20     outb(0x02, port+SEROFF_IER);
21     u8 ier = inb(port+SEROFF_IER);
22     if (ier != 0x02)
23         return 0;
24     u8 iir = inb(port+SEROFF_IIR);
25     if ((iir & 0x3f) != 0x02)
26         return 0;
27
28     outb(0x00, port+SEROFF_IER);
29     SET_BDA(port_com[count], port);
30     SET_BDA(com_timeout[count], timeout);
31     return 1;
32 }
33
34 void
35 serial_setup(void)
36 {
37     if (! CONFIG_SERIAL)
38         return;
39     dprintf(3, "init serial\n");
40
41     u16 count = 0;
42     count += detect_serial(PORT_SERIAL1, 0x0a, count);
43     count += detect_serial(PORT_SERIAL2, 0x0a, count);
44     count += detect_serial(PORT_SERIAL3, 0x0a, count);
45     count += detect_serial(PORT_SERIAL4, 0x0a, count);
46     dprintf(1, "Found %d serial ports\n", count);
47
48     // Equipment word bits 9..11 determing # serial ports
49     u16 eqb = GET_BDA(equipment_list_flags);
50     SET_BDA(equipment_list_flags, (eqb & 0xf1ff) | (count << 9));
51 }
52
53 static u16
54 getComAddr(struct bregs *regs)
55 {
56     if (regs->dx >= 4) {
57         set_invalid(regs);
58         return 0;
59     }
60     u16 addr = GET_BDA(port_com[regs->dx]);
61     if (! addr)
62         set_invalid(regs);
63     return addr;
64 }
65
66 // SERIAL - INITIALIZE PORT
67 static void
68 handle_1400(struct bregs *regs)
69 {
70     u16 addr = getComAddr(regs);
71     if (!addr)
72         return;
73     outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR);
74     if ((regs->al & 0xE0) == 0) {
75         outb(0x17, addr+SEROFF_DLL);
76         outb(0x04, addr+SEROFF_DLH);
77     } else {
78         u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5);
79         outb(val16 & 0xFF, addr+SEROFF_DLL);
80         outb(val16 >> 8, addr+SEROFF_DLH);
81     }
82     outb(regs->al & 0x1F, addr+SEROFF_LCR);
83     regs->ah = inb(addr+SEROFF_LSR);
84     regs->al = inb(addr+SEROFF_MSR);
85     set_success(regs);
86 }
87
88 // SERIAL - WRITE CHARACTER TO PORT
89 static void
90 handle_1401(struct bregs *regs)
91 {
92     u16 addr = getComAddr(regs);
93     if (!addr)
94         return;
95     u32 end = calc_future_timer_ticks(GET_BDA(com_timeout[regs->dx]));
96     for (;;) {
97         u8 lsr = inb(addr+SEROFF_LSR);
98         if ((lsr & 0x60) == 0x60) {
99             // Success - can write data
100             outb(regs->al, addr+SEROFF_DATA);
101             // XXX - reread lsr?
102             regs->ah = lsr;
103             break;
104         }
105         if (check_timer(end)) {
106             // Timed out - can't write data.
107             regs->ah = lsr | 0x80;
108             break;
109         }
110         yield();
111     }
112     set_success(regs);
113 }
114
115 // SERIAL - READ CHARACTER FROM PORT
116 static void
117 handle_1402(struct bregs *regs)
118 {
119     u16 addr = getComAddr(regs);
120     if (!addr)
121         return;
122     u32 end = calc_future_timer_ticks(GET_BDA(com_timeout[regs->dx]));
123     for (;;) {
124         u8 lsr = inb(addr+SEROFF_LSR);
125         if (lsr & 0x01) {
126             // Success - can read data
127             regs->al = inb(addr+SEROFF_DATA);
128             regs->ah = lsr;
129             break;
130         }
131         if (check_timer(end)) {
132             // Timed out - can't read data.
133             regs->ah = lsr | 0x80;
134             break;
135         }
136         yield();
137     }
138     set_success(regs);
139 }
140
141 // SERIAL - GET PORT STATUS
142 static void
143 handle_1403(struct bregs *regs)
144 {
145     u16 addr = getComAddr(regs);
146     if (!addr)
147         return;
148     regs->ah = inb(addr+SEROFF_LSR);
149     regs->al = inb(addr+SEROFF_MSR);
150     set_success(regs);
151 }
152
153 static void
154 handle_14XX(struct bregs *regs)
155 {
156     set_unimplemented(regs);
157 }
158
159 // INT 14h Serial Communications Service Entry Point
160 void VISIBLE16
161 handle_14(struct bregs *regs)
162 {
163     debug_enter(regs, DEBUG_HDL_14);
164     if (! CONFIG_SERIAL) {
165         handle_14XX(regs);
166         return;
167     }
168
169     switch (regs->ah) {
170     case 0x00: handle_1400(regs); break;
171     case 0x01: handle_1401(regs); break;
172     case 0x02: handle_1402(regs); break;
173     case 0x03: handle_1403(regs); break;
174     default:   handle_14XX(regs); break;
175     }
176 }
177
178 // XXX - Baud Rate Generator Table
179 u8 BaudTable[16] VAR16FIXED(0xe729);
180
181
182 /****************************************************************
183  * LPT ports
184  ****************************************************************/
185
186 static u16
187 detect_parport(u16 port, u8 timeout, u8 count)
188 {
189     // clear input mode
190     outb(inb(port+2) & 0xdf, port+2);
191
192     outb(0xaa, port);
193     if (inb(port) != 0xaa)
194         // Not present
195         return 0;
196     SET_BDA(port_lpt[count], port);
197     SET_BDA(lpt_timeout[count], timeout);
198     return 1;
199 }
200
201 void
202 lpt_setup(void)
203 {
204     if (! CONFIG_LPT)
205         return;
206     dprintf(3, "init lpt\n");
207
208     u16 count = 0;
209     count += detect_parport(PORT_LPT1, 0x14, count);
210     count += detect_parport(PORT_LPT2, 0x14, count);
211     dprintf(1, "Found %d lpt ports\n", count);
212
213     // Equipment word bits 14..15 determing # parallel ports
214     u16 eqb = GET_BDA(equipment_list_flags);
215     SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
216 }
217
218 static u16
219 getLptAddr(struct bregs *regs)
220 {
221     if (regs->dx >= 3) {
222         set_invalid(regs);
223         return 0;
224     }
225     u16 addr = GET_BDA(port_lpt[regs->dx]);
226     if (! addr)
227         set_invalid(regs);
228     return addr;
229 }
230
231 // INT 17 - PRINTER - WRITE CHARACTER
232 static void
233 handle_1700(struct bregs *regs)
234 {
235     u16 addr = getLptAddr(regs);
236     if (!addr)
237         return;
238
239     u32 end = calc_future_timer_ticks(GET_BDA(lpt_timeout[regs->dx]));
240
241     outb(regs->al, addr);
242     u8 val8 = inb(addr+2);
243     outb(val8 | 0x01, addr+2); // send strobe
244     udelay(5);
245     outb(val8 & ~0x01, addr+2);
246
247     for (;;) {
248         u8 v = inb(addr+1);
249         if (!(v & 0x40)) {
250             // Success
251             regs->ah = v ^ 0x48;
252             break;
253         }
254         if (check_timer(end)) {
255             // Timeout
256             regs->ah = (v ^ 0x48) | 0x01;
257             break;
258         }
259         yield();
260     }
261
262     set_success(regs);
263 }
264
265 // INT 17 - PRINTER - INITIALIZE PORT
266 static void
267 handle_1701(struct bregs *regs)
268 {
269     u16 addr = getLptAddr(regs);
270     if (!addr)
271         return;
272
273     u8 val8 = inb(addr+2);
274     outb(val8 & ~0x04, addr+2); // send init
275     udelay(5);
276     outb(val8 | 0x04, addr+2);
277
278     regs->ah = inb(addr+1) ^ 0x48;
279     set_success(regs);
280 }
281
282 // INT 17 - PRINTER - GET STATUS
283 static void
284 handle_1702(struct bregs *regs)
285 {
286     u16 addr = getLptAddr(regs);
287     if (!addr)
288         return;
289     regs->ah = inb(addr+1) ^ 0x48;
290     set_success(regs);
291 }
292
293 static void
294 handle_17XX(struct bregs *regs)
295 {
296     set_unimplemented(regs);
297 }
298
299 // INT17h : Printer Service Entry Point
300 void VISIBLE16
301 handle_17(struct bregs *regs)
302 {
303     debug_enter(regs, DEBUG_HDL_17);
304     if (! CONFIG_LPT) {
305         handle_17XX(regs);
306         return;
307     }
308
309     switch (regs->ah) {
310     case 0x00: handle_1700(regs); break;
311     case 0x01: handle_1701(regs); break;
312     case 0x02: handle_1702(regs); break;
313     default:   handle_17XX(regs); break;
314     }
315 }