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