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