blob: 88349c8a9267686d2df2308210f0cc3b7d454415 [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'Connor9521e262008-07-04 13:04:29 -04009#include "bregs.h" // struct bregs
Kevin O'Connor4ade5232013-09-18 21:41:48 -040010#include "hw/serialio.h" // SEROFF_IER
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "output.h" // debug_enter
Kevin O'Connorcf854182014-01-15 13:34:19 -050012#include "romfile.h" // romfile_loadint
Kevin O'Connor3df600b2013-09-14 19:28:55 -040013#include "stacks.h" // yield
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "util.h" // serial_setup
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050015
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050016
17/****************************************************************
18 * COM ports
19 ****************************************************************/
20
21static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040022detect_serial(u16 port, u8 timeout, u8 count)
23{
Kevin O'Connorcf854182014-01-15 13:34:19 -050024 if (CONFIG_DEBUG_SERIAL && port == CONFIG_DEBUG_SERIAL_PORT
25 && !romfile_loadint("etc/advertise-serial-debug-port", 1))
26 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040027 outb(0x02, port+SEROFF_IER);
28 u8 ier = inb(port+SEROFF_IER);
29 if (ier != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040030 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040031 u8 iir = inb(port+SEROFF_IIR);
32 if ((iir & 0x3f) != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040033 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040034
35 outb(0x00, port+SEROFF_IER);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040036 SET_BDA(port_com[count], port);
37 SET_BDA(com_timeout[count], timeout);
38 return 1;
39}
40
41void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050042serial_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040043{
Kevin O'Connor40967022008-07-21 22:23:05 -040044 if (! CONFIG_SERIAL)
45 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -040046 dprintf(3, "init serial\n");
Kevin O'Connor40967022008-07-21 22:23:05 -040047
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040048 u16 count = 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040049 count += detect_serial(PORT_SERIAL1, 0x0a, count);
50 count += detect_serial(PORT_SERIAL2, 0x0a, count);
51 count += detect_serial(PORT_SERIAL3, 0x0a, count);
52 count += detect_serial(PORT_SERIAL4, 0x0a, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -040053 dprintf(1, "Found %d serial ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040054
55 // Equipment word bits 9..11 determing # serial ports
Kevin O'Connore51316d2012-06-10 09:09:22 -040056 set_equipment_flags(0xe00, count << 9);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040057}
58
59static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050060getComAddr(struct bregs *regs)
61{
62 if (regs->dx >= 4) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050063 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050064 return 0;
65 }
66 u16 addr = GET_BDA(port_com[regs->dx]);
67 if (! addr)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050068 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050069 return addr;
70}
71
Kevin O'Connor1812e202008-05-07 21:29:50 -040072// SERIAL - INITIALIZE PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050073static void
74handle_1400(struct bregs *regs)
75{
76 u16 addr = getComAddr(regs);
77 if (!addr)
78 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040079 outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050080 if ((regs->al & 0xE0) == 0) {
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040081 outb(0x17, addr+SEROFF_DLL);
82 outb(0x04, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050083 } else {
84 u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5);
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040085 outb(val16 & 0xFF, addr+SEROFF_DLL);
86 outb(val16 >> 8, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050087 }
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040088 outb(regs->al & 0x1F, addr+SEROFF_LCR);
89 regs->ah = inb(addr+SEROFF_LSR);
90 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -040091 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050092}
93
Kevin O'Connor1812e202008-05-07 21:29:50 -040094// SERIAL - WRITE CHARACTER TO PORT
95static void
96handle_1401(struct bregs *regs)
97{
98 u16 addr = getComAddr(regs);
99 if (!addr)
100 return;
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400101 u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400102 for (;;) {
103 u8 lsr = inb(addr+SEROFF_LSR);
104 if ((lsr & 0x60) == 0x60) {
105 // Success - can write data
106 outb(regs->al, addr+SEROFF_DATA);
107 // XXX - reread lsr?
108 regs->ah = lsr;
109 break;
110 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400111 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400112 // Timed out - can't write data.
113 regs->ah = lsr | 0x80;
114 break;
Kevin O'Connor5c732402008-06-07 10:43:07 -0400115 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400116 yield();
Kevin O'Connor5c732402008-06-07 10:43:07 -0400117 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400118 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500119}
120
Kevin O'Connor1812e202008-05-07 21:29:50 -0400121// SERIAL - READ CHARACTER FROM PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500122static void
123handle_1402(struct bregs *regs)
124{
125 u16 addr = getComAddr(regs);
126 if (!addr)
127 return;
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400128 u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400129 for (;;) {
130 u8 lsr = inb(addr+SEROFF_LSR);
131 if (lsr & 0x01) {
132 // Success - can read data
133 regs->al = inb(addr+SEROFF_DATA);
134 regs->ah = lsr;
135 break;
136 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400137 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400138 // Timed out - can't read data.
139 regs->ah = lsr | 0x80;
140 break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500141 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400142 yield();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500143 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400144 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500145}
146
Kevin O'Connor1812e202008-05-07 21:29:50 -0400147// SERIAL - GET PORT STATUS
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500148static void
149handle_1403(struct bregs *regs)
150{
151 u16 addr = getComAddr(regs);
152 if (!addr)
153 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400154 regs->ah = inb(addr+SEROFF_LSR);
155 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400156 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500157}
158
159static void
160handle_14XX(struct bregs *regs)
161{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500162 set_unimplemented(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500163}
164
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500165// INT 14h Serial Communications Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500166void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500167handle_14(struct bregs *regs)
168{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400169 debug_enter(regs, DEBUG_HDL_14);
Kevin O'Connor40967022008-07-21 22:23:05 -0400170 if (! CONFIG_SERIAL) {
171 handle_14XX(regs);
172 return;
173 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500174
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500175 switch (regs->ah) {
176 case 0x00: handle_1400(regs); break;
177 case 0x01: handle_1401(regs); break;
178 case 0x02: handle_1402(regs); break;
179 case 0x03: handle_1403(regs); break;
180 default: handle_14XX(regs); break;
181 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500182}
183
184
185/****************************************************************
186 * LPT ports
187 ****************************************************************/
188
189static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400190detect_parport(u16 port, u8 timeout, u8 count)
191{
192 // clear input mode
193 outb(inb(port+2) & 0xdf, port+2);
194
195 outb(0xaa, port);
196 if (inb(port) != 0xaa)
197 // Not present
198 return 0;
199 SET_BDA(port_lpt[count], port);
200 SET_BDA(lpt_timeout[count], timeout);
201 return 1;
202}
203
204void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500205lpt_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400206{
Kevin O'Connor40967022008-07-21 22:23:05 -0400207 if (! CONFIG_LPT)
208 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400209 dprintf(3, "init lpt\n");
Kevin O'Connor40967022008-07-21 22:23:05 -0400210
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400211 u16 count = 0;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400212 count += detect_parport(PORT_LPT1, 0x14, count);
213 count += detect_parport(PORT_LPT2, 0x14, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -0400214 dprintf(1, "Found %d lpt ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400215
216 // Equipment word bits 14..15 determing # parallel ports
Kevin O'Connore51316d2012-06-10 09:09:22 -0400217 set_equipment_flags(0xc000, count << 14);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400218}
219
220static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500221getLptAddr(struct bregs *regs)
222{
223 if (regs->dx >= 3) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500224 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500225 return 0;
226 }
227 u16 addr = GET_BDA(port_lpt[regs->dx]);
228 if (! addr)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500229 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500230 return addr;
231}
232
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500233// INT 17 - PRINTER - WRITE CHARACTER
234static void
235handle_1700(struct bregs *regs)
236{
237 u16 addr = getLptAddr(regs);
238 if (!addr)
239 return;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400240
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400241 u32 end = irqtimer_calc_ticks(GET_BDA(lpt_timeout[regs->dx]));
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500242
243 outb(regs->al, addr);
244 u8 val8 = inb(addr+2);
245 outb(val8 | 0x01, addr+2); // send strobe
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400246 udelay(5);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500247 outb(val8 & ~0x01, addr+2);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500248
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400249 for (;;) {
250 u8 v = inb(addr+1);
251 if (!(v & 0x40)) {
252 // Success
253 regs->ah = v ^ 0x48;
254 break;
255 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400256 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400257 // Timeout
258 regs->ah = (v ^ 0x48) | 0x01;
259 break;
260 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400261 yield();
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400262 }
263
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400264 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500265}
266
267// INT 17 - PRINTER - INITIALIZE PORT
268static void
269handle_1701(struct bregs *regs)
270{
271 u16 addr = getLptAddr(regs);
272 if (!addr)
273 return;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500274
275 u8 val8 = inb(addr+2);
276 outb(val8 & ~0x04, addr+2); // send init
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400277 udelay(5);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500278 outb(val8 | 0x04, addr+2);
279
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400280 regs->ah = inb(addr+1) ^ 0x48;
281 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500282}
283
284// INT 17 - PRINTER - GET STATUS
285static void
286handle_1702(struct bregs *regs)
287{
288 u16 addr = getLptAddr(regs);
289 if (!addr)
290 return;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400291 regs->ah = inb(addr+1) ^ 0x48;
292 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500293}
294
295static void
296handle_17XX(struct bregs *regs)
297{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500298 set_unimplemented(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500299}
300
301// INT17h : Printer Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500302void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500303handle_17(struct bregs *regs)
304{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400305 debug_enter(regs, DEBUG_HDL_17);
Kevin O'Connor40967022008-07-21 22:23:05 -0400306 if (! CONFIG_LPT) {
307 handle_17XX(regs);
308 return;
309 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500310
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500311 switch (regs->ah) {
312 case 0x00: handle_1700(regs); break;
313 case 0x01: handle_1701(regs); break;
314 case 0x02: handle_1702(regs); break;
315 default: handle_17XX(regs); break;
316 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500317}