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