blob: 21b4bd010586166bc4cfe8453a268147870403aa [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
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050035serial_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040036{
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'Connordfefeb52009-12-13 13:04:17 -050057 set_invalid(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'Connordfefeb52009-12-13 13:04:17 -050062 set_invalid(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'Connorb5cc2ca2010-05-23 11:38:53 -040095 u32 end = calc_future_timer_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -040096 for (;;) {
97 u8 lsr = inb(addr+SEROFF_LSR);
98 if ((lsr & 0x60) == 0x60) {
99 // Success - can write data
100 outb(regs->al, addr+SEROFF_DATA);
101 // XXX - reread lsr?
102 regs->ah = lsr;
103 break;
104 }
Kevin O'Connorb5cc2ca2010-05-23 11:38:53 -0400105 if (check_timer(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400106 // Timed out - can't write data.
107 regs->ah = lsr | 0x80;
108 break;
Kevin O'Connor5c732402008-06-07 10:43:07 -0400109 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400110 yield();
Kevin O'Connor5c732402008-06-07 10:43:07 -0400111 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400112 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500113}
114
Kevin O'Connor1812e202008-05-07 21:29:50 -0400115// SERIAL - READ CHARACTER FROM PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500116static void
117handle_1402(struct bregs *regs)
118{
119 u16 addr = getComAddr(regs);
120 if (!addr)
121 return;
Kevin O'Connorb5cc2ca2010-05-23 11:38:53 -0400122 u32 end = calc_future_timer_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400123 for (;;) {
124 u8 lsr = inb(addr+SEROFF_LSR);
125 if (lsr & 0x01) {
126 // Success - can read data
127 regs->al = inb(addr+SEROFF_DATA);
128 regs->ah = lsr;
129 break;
130 }
Kevin O'Connorb5cc2ca2010-05-23 11:38:53 -0400131 if (check_timer(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400132 // Timed out - can't read data.
133 regs->ah = lsr | 0x80;
134 break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500135 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400136 yield();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500137 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400138 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500139}
140
Kevin O'Connor1812e202008-05-07 21:29:50 -0400141// SERIAL - GET PORT STATUS
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500142static void
143handle_1403(struct bregs *regs)
144{
145 u16 addr = getComAddr(regs);
146 if (!addr)
147 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400148 regs->ah = inb(addr+SEROFF_LSR);
149 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400150 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500151}
152
153static void
154handle_14XX(struct bregs *regs)
155{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500156 set_unimplemented(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500157}
158
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500159// INT 14h Serial Communications Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500160void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500161handle_14(struct bregs *regs)
162{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400163 debug_enter(regs, DEBUG_HDL_14);
Kevin O'Connor40967022008-07-21 22:23:05 -0400164 if (! CONFIG_SERIAL) {
165 handle_14XX(regs);
166 return;
167 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500168
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500169 switch (regs->ah) {
170 case 0x00: handle_1400(regs); break;
171 case 0x01: handle_1401(regs); break;
172 case 0x02: handle_1402(regs); break;
173 case 0x03: handle_1403(regs); break;
174 default: handle_14XX(regs); break;
175 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500176}
177
Kevin O'Connor30853762009-01-17 18:49:20 -0500178// XXX - Baud Rate Generator Table
179u8 BaudTable[16] VAR16FIXED(0xe729);
180
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500181
182/****************************************************************
183 * LPT ports
184 ****************************************************************/
185
186static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400187detect_parport(u16 port, u8 timeout, u8 count)
188{
189 // clear input mode
190 outb(inb(port+2) & 0xdf, port+2);
191
192 outb(0xaa, port);
193 if (inb(port) != 0xaa)
194 // Not present
195 return 0;
196 SET_BDA(port_lpt[count], port);
197 SET_BDA(lpt_timeout[count], timeout);
198 return 1;
199}
200
201void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500202lpt_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400203{
Kevin O'Connor40967022008-07-21 22:23:05 -0400204 if (! CONFIG_LPT)
205 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400206 dprintf(3, "init lpt\n");
Kevin O'Connor40967022008-07-21 22:23:05 -0400207
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400208 u16 count = 0;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400209 count += detect_parport(PORT_LPT1, 0x14, count);
210 count += detect_parport(PORT_LPT2, 0x14, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -0400211 dprintf(1, "Found %d lpt ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400212
213 // Equipment word bits 14..15 determing # parallel ports
214 u16 eqb = GET_BDA(equipment_list_flags);
215 SET_BDA(equipment_list_flags, (eqb & 0x3fff) | (count << 14));
216}
217
218static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500219getLptAddr(struct bregs *regs)
220{
221 if (regs->dx >= 3) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500222 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500223 return 0;
224 }
225 u16 addr = GET_BDA(port_lpt[regs->dx]);
226 if (! addr)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500227 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500228 return addr;
229}
230
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500231// INT 17 - PRINTER - WRITE CHARACTER
232static void
233handle_1700(struct bregs *regs)
234{
235 u16 addr = getLptAddr(regs);
236 if (!addr)
237 return;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400238
Kevin O'Connorb5cc2ca2010-05-23 11:38:53 -0400239 u32 end = calc_future_timer_ticks(GET_BDA(lpt_timeout[regs->dx]));
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500240
241 outb(regs->al, addr);
242 u8 val8 = inb(addr+2);
243 outb(val8 | 0x01, addr+2); // send strobe
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400244 udelay(5);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500245 outb(val8 & ~0x01, addr+2);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500246
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400247 for (;;) {
248 u8 v = inb(addr+1);
249 if (!(v & 0x40)) {
250 // Success
251 regs->ah = v ^ 0x48;
252 break;
253 }
Kevin O'Connorb5cc2ca2010-05-23 11:38:53 -0400254 if (check_timer(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400255 // Timeout
256 regs->ah = (v ^ 0x48) | 0x01;
257 break;
258 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400259 yield();
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400260 }
261
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400262 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500263}
264
265// INT 17 - PRINTER - INITIALIZE PORT
266static void
267handle_1701(struct bregs *regs)
268{
269 u16 addr = getLptAddr(regs);
270 if (!addr)
271 return;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500272
273 u8 val8 = inb(addr+2);
274 outb(val8 & ~0x04, addr+2); // send init
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400275 udelay(5);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500276 outb(val8 | 0x04, addr+2);
277
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400278 regs->ah = inb(addr+1) ^ 0x48;
279 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500280}
281
282// INT 17 - PRINTER - GET STATUS
283static void
284handle_1702(struct bregs *regs)
285{
286 u16 addr = getLptAddr(regs);
287 if (!addr)
288 return;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400289 regs->ah = inb(addr+1) ^ 0x48;
290 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500291}
292
293static void
294handle_17XX(struct bregs *regs)
295{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500296 set_unimplemented(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500297}
298
299// INT17h : Printer Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500300void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500301handle_17(struct bregs *regs)
302{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400303 debug_enter(regs, DEBUG_HDL_17);
Kevin O'Connor40967022008-07-21 22:23:05 -0400304 if (! CONFIG_LPT) {
305 handle_17XX(regs);
306 return;
307 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500308
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500309 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}