blob: 550762e3f79c121fdf544b08fae3b99c1a6a701c [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to handle serial and printer services.
2//
Kevin O'Connor4edb2752009-05-06 23:25:40 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05004// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "biosvar.h" // SET_BDA
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05009#include "util.h" // debug_enter
Kevin O'Connor9521e262008-07-04 13:04:29 -040010#include "bregs.h" // struct bregs
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050011
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050012
13/****************************************************************
14 * COM ports
15 ****************************************************************/
16
17static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040018detect_serial(u16 port, u8 timeout, u8 count)
19{
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040020 outb(0x02, port+SEROFF_IER);
21 u8 ier = inb(port+SEROFF_IER);
22 if (ier != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040023 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040024 u8 iir = inb(port+SEROFF_IIR);
25 if ((iir & 0x3f) != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040026 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040027
28 outb(0x00, port+SEROFF_IER);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040029 SET_BDA(port_com[count], port);
30 SET_BDA(com_timeout[count], timeout);
31 return 1;
32}
33
34void
35serial_setup()
36{
Kevin O'Connor40967022008-07-21 22:23:05 -040037 if (! CONFIG_SERIAL)
38 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -040039 dprintf(3, "init serial\n");
Kevin O'Connor40967022008-07-21 22:23:05 -040040
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040041 u16 count = 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040042 count += detect_serial(PORT_SERIAL1, 0x0a, count);
43 count += detect_serial(PORT_SERIAL2, 0x0a, count);
44 count += detect_serial(PORT_SERIAL3, 0x0a, count);
45 count += detect_serial(PORT_SERIAL4, 0x0a, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -040046 dprintf(1, "Found %d serial ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040047
48 // Equipment word bits 9..11 determing # serial ports
49 u16 eqb = GET_BDA(equipment_list_flags);
50 SET_BDA(equipment_list_flags, (eqb & 0xf1ff) | (count << 9));
51}
52
53static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050054getComAddr(struct bregs *regs)
55{
56 if (regs->dx >= 4) {
Kevin O'Connor6c781222008-03-09 12:19:23 -040057 set_fail(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050058 return 0;
59 }
60 u16 addr = GET_BDA(port_com[regs->dx]);
61 if (! addr)
Kevin O'Connor6c781222008-03-09 12:19:23 -040062 set_fail(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050063 return addr;
64}
65
Kevin O'Connor1812e202008-05-07 21:29:50 -040066// SERIAL - INITIALIZE PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050067static void
68handle_1400(struct bregs *regs)
69{
70 u16 addr = getComAddr(regs);
71 if (!addr)
72 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040073 outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050074 if ((regs->al & 0xE0) == 0) {
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040075 outb(0x17, addr+SEROFF_DLL);
76 outb(0x04, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050077 } else {
78 u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5);
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040079 outb(val16 & 0xFF, addr+SEROFF_DLL);
80 outb(val16 >> 8, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050081 }
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040082 outb(regs->al & 0x1F, addr+SEROFF_LCR);
83 regs->ah = inb(addr+SEROFF_LSR);
84 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -040085 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050086}
87
Kevin O'Connor1812e202008-05-07 21:29:50 -040088// SERIAL - WRITE CHARACTER TO PORT
89static void
90handle_1401(struct bregs *regs)
91{
92 u16 addr = getComAddr(regs);
93 if (!addr)
94 return;
Kevin O'Connor5c732402008-06-07 10:43:07 -040095 u16 timer = GET_BDA(timer_counter);
Kevin O'Connor1812e202008-05-07 21:29:50 -040096 u16 timeout = GET_BDA(com_timeout[regs->dx]);
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040097 while (((inb(addr+SEROFF_LSR) & 0x60) != 0x60) && (timeout)) {
Kevin O'Connor5c732402008-06-07 10:43:07 -040098 u16 val16 = GET_BDA(timer_counter);
99 if (val16 != timer) {
100 timer = val16;
101 timeout--;
102 }
103 }
104 if (timeout)
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400105 outb(regs->al, addr+SEROFF_DATA);
106 regs->ah = inb(addr+SEROFF_LSR);
Kevin O'Connor5c732402008-06-07 10:43:07 -0400107 if (!timeout)
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500108 regs->ah |= 0x80;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400109 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500110}
111
Kevin O'Connor1812e202008-05-07 21:29:50 -0400112// SERIAL - READ CHARACTER FROM PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500113static void
114handle_1402(struct bregs *regs)
115{
116 u16 addr = getComAddr(regs);
117 if (!addr)
118 return;
119 u16 timer = GET_BDA(timer_counter);
120 u16 timeout = GET_BDA(com_timeout[regs->dx]);
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400121 while (((inb(addr+SEROFF_LSR) & 0x01) == 0) && (timeout)) {
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500122 u16 val16 = GET_BDA(timer_counter);
123 if (val16 != timer) {
124 timer = val16;
125 timeout--;
126 }
127 }
128 if (timeout) {
129 regs->ah = 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400130 regs->al = inb(addr+SEROFF_DATA);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500131 } else {
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400132 regs->ah = inb(addr+SEROFF_LSR);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500133 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400134 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500135}
136
Kevin O'Connor1812e202008-05-07 21:29:50 -0400137// SERIAL - GET PORT STATUS
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500138static void
139handle_1403(struct bregs *regs)
140{
141 u16 addr = getComAddr(regs);
142 if (!addr)
143 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400144 regs->ah = inb(addr+SEROFF_LSR);
145 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400146 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500147}
148
149static void
150handle_14XX(struct bregs *regs)
151{
152 // Unsupported
Kevin O'Connor6c781222008-03-09 12:19:23 -0400153 set_fail(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500154}
155
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500156// INT 14h Serial Communications Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500157void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500158handle_14(struct bregs *regs)
159{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400160 debug_enter(regs, DEBUG_HDL_14);
Kevin O'Connor40967022008-07-21 22:23:05 -0400161 if (! CONFIG_SERIAL) {
162 handle_14XX(regs);
163 return;
164 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500165
166 irq_enable();
167
168 switch (regs->ah) {
169 case 0x00: handle_1400(regs); break;
170 case 0x01: handle_1401(regs); break;
171 case 0x02: handle_1402(regs); break;
172 case 0x03: handle_1403(regs); break;
173 default: handle_14XX(regs); break;
174 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500175}
176
Kevin O'Connor30853762009-01-17 18:49:20 -0500177// XXX - Baud Rate Generator Table
178u8 BaudTable[16] VAR16FIXED(0xe729);
179
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500180
181/****************************************************************
182 * LPT ports
183 ****************************************************************/
184
185static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400186detect_parport(u16 port, u8 timeout, u8 count)
187{
188 // clear input mode
189 outb(inb(port+2) & 0xdf, port+2);
190
191 outb(0xaa, port);
192 if (inb(port) != 0xaa)
193 // Not present
194 return 0;
195 SET_BDA(port_lpt[count], port);
196 SET_BDA(lpt_timeout[count], timeout);
197 return 1;
198}
199
200void
201lpt_setup()
202{
Kevin O'Connor40967022008-07-21 22:23:05 -0400203 if (! CONFIG_LPT)
204 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400205 dprintf(3, "init lpt\n");
Kevin O'Connor40967022008-07-21 22:23:05 -0400206
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400207 u16 count = 0;
208 count += detect_parport(0x378, 0x14, count);
209 count += detect_parport(0x278, 0x14, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -0400210 dprintf(1, "Found %d lpt ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400211
212 // Equipment word bits 14..15 determing # parallel ports
213 u16 eqb = GET_BDA(equipment_list_flags);
214 SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
215}
216
217static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500218getLptAddr(struct bregs *regs)
219{
220 if (regs->dx >= 3) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400221 set_fail(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500222 return 0;
223 }
224 u16 addr = GET_BDA(port_lpt[regs->dx]);
225 if (! addr)
Kevin O'Connor6c781222008-03-09 12:19:23 -0400226 set_fail(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500227 return addr;
228}
229
230static void
231lpt_ret(struct bregs *regs, u16 addr, u16 timeout)
232{
233 u8 val8 = inb(addr+1);
234 regs->ah = (val8 ^ 0x48);
235 if (!timeout)
236 regs->ah |= 0x01;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400237 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500238}
239
240// INT 17 - PRINTER - WRITE CHARACTER
241static void
242handle_1700(struct bregs *regs)
243{
244 u16 addr = getLptAddr(regs);
245 if (!addr)
246 return;
247 u16 timeout = GET_BDA(lpt_timeout[regs->dx]) << 8;
248
249 outb(regs->al, addr);
250 u8 val8 = inb(addr+2);
251 outb(val8 | 0x01, addr+2); // send strobe
252 nop();
253 outb(val8 & ~0x01, addr+2);
Kevin O'Connor9e91c7b2009-01-17 02:30:21 -0500254 // XXX - implement better timeout code.
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500255 while (((inb(addr+1) & 0x40) == 0x40) && (timeout))
256 timeout--;
257
258 lpt_ret(regs, addr, timeout);
259}
260
261// INT 17 - PRINTER - INITIALIZE PORT
262static void
263handle_1701(struct bregs *regs)
264{
265 u16 addr = getLptAddr(regs);
266 if (!addr)
267 return;
268 u16 timeout = GET_BDA(lpt_timeout[regs->dx]) << 8;
269
270 u8 val8 = inb(addr+2);
271 outb(val8 & ~0x04, addr+2); // send init
272 nop();
273 outb(val8 | 0x04, addr+2);
274
275 lpt_ret(regs, addr, timeout);
276}
277
278// INT 17 - PRINTER - GET STATUS
279static void
280handle_1702(struct bregs *regs)
281{
282 u16 addr = getLptAddr(regs);
283 if (!addr)
284 return;
285 u16 timeout = GET_BDA(lpt_timeout[regs->dx]) << 8;
286
287 lpt_ret(regs, addr, timeout);
288}
289
290static void
291handle_17XX(struct bregs *regs)
292{
293 // Unsupported
Kevin O'Connor6c781222008-03-09 12:19:23 -0400294 set_fail(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500295}
296
297// INT17h : Printer Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500298void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500299handle_17(struct bregs *regs)
300{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400301 debug_enter(regs, DEBUG_HDL_17);
Kevin O'Connor40967022008-07-21 22:23:05 -0400302 if (! CONFIG_LPT) {
303 handle_17XX(regs);
304 return;
305 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500306
307 irq_enable();
308
309 switch (regs->ah) {
310 case 0x00: handle_1700(regs); break;
311 case 0x01: handle_1701(regs); break;
312 case 0x02: handle_1702(regs); break;
313 default: handle_17XX(regs); break;
314 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500315}