Replace irq_enable() regions with explicit calls to check for irqs.
[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     for (;;) {
122         u8 lsr = inb(addr+SEROFF_LSR);
123         if ((lsr & 0x60) == 0x60) {
124             // Success - can write data
125             outb(regs->al, addr+SEROFF_DATA);
126             // XXX - reread lsr?
127             regs->ah = lsr;
128             break;
129         }
130         if (checkTickTimer(&tt)) {
131             // Timed out - can't write data.
132             regs->ah = lsr | 0x80;
133             break;
134         }
135         yield();
136     }
137     set_success(regs);
138 }
139
140 // SERIAL - READ CHARACTER FROM PORT
141 static void
142 handle_1402(struct bregs *regs)
143 {
144     u16 addr = getComAddr(regs);
145     if (!addr)
146         return;
147     struct tick_timer_s tt = initTickTimer(GET_BDA(com_timeout[regs->dx]));
148     for (;;) {
149         u8 lsr = inb(addr+SEROFF_LSR);
150         if (lsr & 0x01) {
151             // Success - can read data
152             regs->al = inb(addr+SEROFF_DATA);
153             regs->ah = lsr;
154             break;
155         }
156         if (checkTickTimer(&tt)) {
157             // Timed out - can't read data.
158             regs->ah = lsr | 0x80;
159             break;
160         }
161         yield();
162     }
163     set_success(regs);
164 }
165
166 // SERIAL - GET PORT STATUS
167 static void
168 handle_1403(struct bregs *regs)
169 {
170     u16 addr = getComAddr(regs);
171     if (!addr)
172         return;
173     regs->ah = inb(addr+SEROFF_LSR);
174     regs->al = inb(addr+SEROFF_MSR);
175     set_success(regs);
176 }
177
178 static void
179 handle_14XX(struct bregs *regs)
180 {
181     // Unsupported
182     set_fail(regs);
183 }
184
185 // INT 14h Serial Communications Service Entry Point
186 void VISIBLE16
187 handle_14(struct bregs *regs)
188 {
189     debug_enter(regs, DEBUG_HDL_14);
190     if (! CONFIG_SERIAL) {
191         handle_14XX(regs);
192         return;
193     }
194
195     switch (regs->ah) {
196     case 0x00: handle_1400(regs); break;
197     case 0x01: handle_1401(regs); break;
198     case 0x02: handle_1402(regs); break;
199     case 0x03: handle_1403(regs); break;
200     default:   handle_14XX(regs); break;
201     }
202 }
203
204 // XXX - Baud Rate Generator Table
205 u8 BaudTable[16] VAR16FIXED(0xe729);
206
207
208 /****************************************************************
209  * LPT ports
210  ****************************************************************/
211
212 static u16
213 detect_parport(u16 port, u8 timeout, u8 count)
214 {
215     // clear input mode
216     outb(inb(port+2) & 0xdf, port+2);
217
218     outb(0xaa, port);
219     if (inb(port) != 0xaa)
220         // Not present
221         return 0;
222     SET_BDA(port_lpt[count], port);
223     SET_BDA(lpt_timeout[count], timeout);
224     return 1;
225 }
226
227 void
228 lpt_setup()
229 {
230     if (! CONFIG_LPT)
231         return;
232     dprintf(3, "init lpt\n");
233
234     u16 count = 0;
235     count += detect_parport(PORT_LPT1, 0x14, count);
236     count += detect_parport(PORT_LPT2, 0x14, count);
237     dprintf(1, "Found %d lpt ports\n", count);
238
239     // Equipment word bits 14..15 determing # parallel ports
240     u16 eqb = GET_BDA(equipment_list_flags);
241     SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
242 }
243
244 static u16
245 getLptAddr(struct bregs *regs)
246 {
247     if (regs->dx >= 3) {
248         set_fail(regs);
249         return 0;
250     }
251     u16 addr = GET_BDA(port_lpt[regs->dx]);
252     if (! addr)
253         set_fail(regs);
254     return addr;
255 }
256
257 // INT 17 - PRINTER - WRITE CHARACTER
258 static void
259 handle_1700(struct bregs *regs)
260 {
261     u16 addr = getLptAddr(regs);
262     if (!addr)
263         return;
264
265     struct tick_timer_s tt = initTickTimer(GET_BDA(lpt_timeout[regs->dx]));
266
267     outb(regs->al, addr);
268     u8 val8 = inb(addr+2);
269     outb(val8 | 0x01, addr+2); // send strobe
270     udelay(5);
271     outb(val8 & ~0x01, addr+2);
272
273     for (;;) {
274         u8 v = inb(addr+1);
275         if (!(v & 0x40)) {
276             // Success
277             regs->ah = v ^ 0x48;
278             break;
279         }
280         if (checkTickTimer(&tt)) {
281             // Timeout
282             regs->ah = (v ^ 0x48) | 0x01;
283             break;
284         }
285         yield();
286     }
287
288     set_success(regs);
289 }
290
291 // INT 17 - PRINTER - INITIALIZE PORT
292 static void
293 handle_1701(struct bregs *regs)
294 {
295     u16 addr = getLptAddr(regs);
296     if (!addr)
297         return;
298
299     u8 val8 = inb(addr+2);
300     outb(val8 & ~0x04, addr+2); // send init
301     udelay(5);
302     outb(val8 | 0x04, addr+2);
303
304     regs->ah = inb(addr+1) ^ 0x48;
305     set_success(regs);
306 }
307
308 // INT 17 - PRINTER - GET STATUS
309 static void
310 handle_1702(struct bregs *regs)
311 {
312     u16 addr = getLptAddr(regs);
313     if (!addr)
314         return;
315     regs->ah = inb(addr+1) ^ 0x48;
316     set_success(regs);
317 }
318
319 static void
320 handle_17XX(struct bregs *regs)
321 {
322     // Unsupported
323     set_fail(regs);
324 }
325
326 // INT17h : Printer Service Entry Point
327 void VISIBLE16
328 handle_17(struct bregs *regs)
329 {
330     debug_enter(regs, DEBUG_HDL_17);
331     if (! CONFIG_LPT) {
332         handle_17XX(regs);
333         return;
334     }
335
336     switch (regs->ah) {
337     case 0x00: handle_1700(regs); break;
338     case 0x01: handle_1701(regs); break;
339     case 0x02: handle_1702(regs); break;
340     default:   handle_17XX(regs); break;
341     }
342 }