Be sure to add "void" to all function prototypes that take no args.
[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(void)
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_invalid(regs);
83         return 0;
84     }
85     u16 addr = GET_BDA(port_com[regs->dx]);
86     if (! addr)
87         set_invalid(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     set_unimplemented(regs);
182 }
183
184 // INT 14h Serial Communications Service Entry Point
185 void VISIBLE16
186 handle_14(struct bregs *regs)
187 {
188     debug_enter(regs, DEBUG_HDL_14);
189     if (! CONFIG_SERIAL) {
190         handle_14XX(regs);
191         return;
192     }
193
194     switch (regs->ah) {
195     case 0x00: handle_1400(regs); break;
196     case 0x01: handle_1401(regs); break;
197     case 0x02: handle_1402(regs); break;
198     case 0x03: handle_1403(regs); break;
199     default:   handle_14XX(regs); break;
200     }
201 }
202
203 // XXX - Baud Rate Generator Table
204 u8 BaudTable[16] VAR16FIXED(0xe729);
205
206
207 /****************************************************************
208  * LPT ports
209  ****************************************************************/
210
211 static u16
212 detect_parport(u16 port, u8 timeout, u8 count)
213 {
214     // clear input mode
215     outb(inb(port+2) & 0xdf, port+2);
216
217     outb(0xaa, port);
218     if (inb(port) != 0xaa)
219         // Not present
220         return 0;
221     SET_BDA(port_lpt[count], port);
222     SET_BDA(lpt_timeout[count], timeout);
223     return 1;
224 }
225
226 void
227 lpt_setup(void)
228 {
229     if (! CONFIG_LPT)
230         return;
231     dprintf(3, "init lpt\n");
232
233     u16 count = 0;
234     count += detect_parport(PORT_LPT1, 0x14, count);
235     count += detect_parport(PORT_LPT2, 0x14, count);
236     dprintf(1, "Found %d lpt ports\n", count);
237
238     // Equipment word bits 14..15 determing # parallel ports
239     u16 eqb = GET_BDA(equipment_list_flags);
240     SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
241 }
242
243 static u16
244 getLptAddr(struct bregs *regs)
245 {
246     if (regs->dx >= 3) {
247         set_invalid(regs);
248         return 0;
249     }
250     u16 addr = GET_BDA(port_lpt[regs->dx]);
251     if (! addr)
252         set_invalid(regs);
253     return addr;
254 }
255
256 // INT 17 - PRINTER - WRITE CHARACTER
257 static void
258 handle_1700(struct bregs *regs)
259 {
260     u16 addr = getLptAddr(regs);
261     if (!addr)
262         return;
263
264     struct tick_timer_s tt = initTickTimer(GET_BDA(lpt_timeout[regs->dx]));
265
266     outb(regs->al, addr);
267     u8 val8 = inb(addr+2);
268     outb(val8 | 0x01, addr+2); // send strobe
269     udelay(5);
270     outb(val8 & ~0x01, addr+2);
271
272     for (;;) {
273         u8 v = inb(addr+1);
274         if (!(v & 0x40)) {
275             // Success
276             regs->ah = v ^ 0x48;
277             break;
278         }
279         if (checkTickTimer(&tt)) {
280             // Timeout
281             regs->ah = (v ^ 0x48) | 0x01;
282             break;
283         }
284         yield();
285     }
286
287     set_success(regs);
288 }
289
290 // INT 17 - PRINTER - INITIALIZE PORT
291 static void
292 handle_1701(struct bregs *regs)
293 {
294     u16 addr = getLptAddr(regs);
295     if (!addr)
296         return;
297
298     u8 val8 = inb(addr+2);
299     outb(val8 & ~0x04, addr+2); // send init
300     udelay(5);
301     outb(val8 | 0x04, addr+2);
302
303     regs->ah = inb(addr+1) ^ 0x48;
304     set_success(regs);
305 }
306
307 // INT 17 - PRINTER - GET STATUS
308 static void
309 handle_1702(struct bregs *regs)
310 {
311     u16 addr = getLptAddr(regs);
312     if (!addr)
313         return;
314     regs->ah = inb(addr+1) ^ 0x48;
315     set_success(regs);
316 }
317
318 static void
319 handle_17XX(struct bregs *regs)
320 {
321     set_unimplemented(regs);
322 }
323
324 // INT17h : Printer Service Entry Point
325 void VISIBLE16
326 handle_17(struct bregs *regs)
327 {
328     debug_enter(regs, DEBUG_HDL_17);
329     if (! CONFIG_LPT) {
330         handle_17XX(regs);
331         return;
332     }
333
334     switch (regs->ah) {
335     case 0x00: handle_1700(regs); break;
336     case 0x01: handle_1701(regs); break;
337     case 0x02: handle_1702(regs); break;
338     default:   handle_17XX(regs); break;
339     }
340 }