blob: 8813831bf09b8b36f47872a7e1e671de5cba6e1a [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
Helge Deller711bd2f2021-02-10 20:35:45 +010022detect_serial(portaddr_t port, u8 timeout, u8 count)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040023{
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;
Helge Deller711bd2f2021-02-10 20:35:45 +010027 if (!port)
28 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040029 outb(0x02, port+SEROFF_IER);
30 u8 ier = inb(port+SEROFF_IER);
31 if (ier != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040032 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040033 u8 iir = inb(port+SEROFF_IIR);
34 if ((iir & 0x3f) != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040035 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040036
37 outb(0x00, port+SEROFF_IER);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040038 SET_BDA(port_com[count], port);
39 SET_BDA(com_timeout[count], timeout);
40 return 1;
41}
42
43void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050044serial_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040045{
Kevin O'Connor40967022008-07-21 22:23:05 -040046 if (! CONFIG_SERIAL)
47 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -040048 dprintf(3, "init serial\n");
Kevin O'Connor40967022008-07-21 22:23:05 -040049
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040050 u16 count = 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040051 count += detect_serial(PORT_SERIAL1, 0x0a, count);
52 count += detect_serial(PORT_SERIAL2, 0x0a, count);
53 count += detect_serial(PORT_SERIAL3, 0x0a, count);
54 count += detect_serial(PORT_SERIAL4, 0x0a, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -040055 dprintf(1, "Found %d serial ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040056
57 // Equipment word bits 9..11 determing # serial ports
Kevin O'Connore51316d2012-06-10 09:09:22 -040058 set_equipment_flags(0xe00, count << 9);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040059}
60
61static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050062getComAddr(struct bregs *regs)
63{
64 if (regs->dx >= 4) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050065 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050066 return 0;
67 }
68 u16 addr = GET_BDA(port_com[regs->dx]);
69 if (! addr)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050070 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050071 return addr;
72}
73
Kevin O'Connor1812e202008-05-07 21:29:50 -040074// SERIAL - INITIALIZE PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050075static void
76handle_1400(struct bregs *regs)
77{
78 u16 addr = getComAddr(regs);
79 if (!addr)
80 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040081 outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050082 if ((regs->al & 0xE0) == 0) {
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040083 outb(0x17, addr+SEROFF_DLL);
84 outb(0x04, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050085 } else {
86 u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5);
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040087 outb(val16 & 0xFF, addr+SEROFF_DLL);
88 outb(val16 >> 8, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050089 }
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040090 outb(regs->al & 0x1F, addr+SEROFF_LCR);
91 regs->ah = inb(addr+SEROFF_LSR);
92 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -040093 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050094}
95
Kevin O'Connor1812e202008-05-07 21:29:50 -040096// SERIAL - WRITE CHARACTER TO PORT
97static void
98handle_1401(struct bregs *regs)
99{
100 u16 addr = getComAddr(regs);
101 if (!addr)
102 return;
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400103 u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400104 for (;;) {
105 u8 lsr = inb(addr+SEROFF_LSR);
106 if ((lsr & 0x60) == 0x60) {
107 // Success - can write data
108 outb(regs->al, addr+SEROFF_DATA);
109 // XXX - reread lsr?
110 regs->ah = lsr;
111 break;
112 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400113 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400114 // Timed out - can't write data.
115 regs->ah = lsr | 0x80;
116 break;
Kevin O'Connor5c732402008-06-07 10:43:07 -0400117 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400118 yield();
Kevin O'Connor5c732402008-06-07 10:43:07 -0400119 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400120 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500121}
122
Kevin O'Connor1812e202008-05-07 21:29:50 -0400123// SERIAL - READ CHARACTER FROM PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500124static void
125handle_1402(struct bregs *regs)
126{
127 u16 addr = getComAddr(regs);
128 if (!addr)
129 return;
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400130 u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400131 for (;;) {
132 u8 lsr = inb(addr+SEROFF_LSR);
133 if (lsr & 0x01) {
134 // Success - can read data
135 regs->al = inb(addr+SEROFF_DATA);
136 regs->ah = lsr;
137 break;
138 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400139 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400140 // Timed out - can't read data.
141 regs->ah = lsr | 0x80;
142 break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500143 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400144 yield();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500145 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400146 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500147}
148
Kevin O'Connor1812e202008-05-07 21:29:50 -0400149// SERIAL - GET PORT STATUS
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500150static void
151handle_1403(struct bregs *regs)
152{
153 u16 addr = getComAddr(regs);
154 if (!addr)
155 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400156 regs->ah = inb(addr+SEROFF_LSR);
157 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400158 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500159}
160
161static void
162handle_14XX(struct bregs *regs)
163{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500164 set_unimplemented(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500165}
166
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500167// INT 14h Serial Communications Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500168void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500169handle_14(struct bregs *regs)
170{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400171 debug_enter(regs, DEBUG_HDL_14);
Kevin O'Connor40967022008-07-21 22:23:05 -0400172 if (! CONFIG_SERIAL) {
173 handle_14XX(regs);
174 return;
175 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500176
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500177 switch (regs->ah) {
178 case 0x00: handle_1400(regs); break;
179 case 0x01: handle_1401(regs); break;
180 case 0x02: handle_1402(regs); break;
181 case 0x03: handle_1403(regs); break;
182 default: handle_14XX(regs); break;
183 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500184}
185
186
187/****************************************************************
188 * LPT ports
189 ****************************************************************/
190
191static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400192detect_parport(u16 port, u8 timeout, u8 count)
193{
194 // clear input mode
195 outb(inb(port+2) & 0xdf, port+2);
196
197 outb(0xaa, port);
198 if (inb(port) != 0xaa)
199 // Not present
200 return 0;
201 SET_BDA(port_lpt[count], port);
202 SET_BDA(lpt_timeout[count], timeout);
203 return 1;
204}
205
206void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500207lpt_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400208{
Kevin O'Connor40967022008-07-21 22:23:05 -0400209 if (! CONFIG_LPT)
210 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400211 dprintf(3, "init lpt\n");
Kevin O'Connor40967022008-07-21 22:23:05 -0400212
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400213 u16 count = 0;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400214 count += detect_parport(PORT_LPT1, 0x14, count);
215 count += detect_parport(PORT_LPT2, 0x14, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -0400216 dprintf(1, "Found %d lpt ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400217
218 // Equipment word bits 14..15 determing # parallel ports
Kevin O'Connore51316d2012-06-10 09:09:22 -0400219 set_equipment_flags(0xc000, count << 14);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400220}
221
222static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500223getLptAddr(struct bregs *regs)
224{
225 if (regs->dx >= 3) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500226 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500227 return 0;
228 }
229 u16 addr = GET_BDA(port_lpt[regs->dx]);
230 if (! addr)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500231 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500232 return addr;
233}
234
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500235// INT 17 - PRINTER - WRITE CHARACTER
236static void
237handle_1700(struct bregs *regs)
238{
239 u16 addr = getLptAddr(regs);
240 if (!addr)
241 return;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400242
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400243 u32 end = irqtimer_calc_ticks(GET_BDA(lpt_timeout[regs->dx]));
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500244
245 outb(regs->al, addr);
246 u8 val8 = inb(addr+2);
247 outb(val8 | 0x01, addr+2); // send strobe
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400248 udelay(5);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500249 outb(val8 & ~0x01, addr+2);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500250
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400251 for (;;) {
252 u8 v = inb(addr+1);
253 if (!(v & 0x40)) {
254 // Success
255 regs->ah = v ^ 0x48;
256 break;
257 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400258 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400259 // Timeout
260 regs->ah = (v ^ 0x48) | 0x01;
261 break;
262 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400263 yield();
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400264 }
265
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400266 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500267}
268
269// INT 17 - PRINTER - INITIALIZE PORT
270static void
271handle_1701(struct bregs *regs)
272{
273 u16 addr = getLptAddr(regs);
274 if (!addr)
275 return;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500276
277 u8 val8 = inb(addr+2);
278 outb(val8 & ~0x04, addr+2); // send init
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400279 udelay(5);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500280 outb(val8 | 0x04, addr+2);
281
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400282 regs->ah = inb(addr+1) ^ 0x48;
283 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500284}
285
286// INT 17 - PRINTER - GET STATUS
287static void
288handle_1702(struct bregs *regs)
289{
290 u16 addr = getLptAddr(regs);
291 if (!addr)
292 return;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400293 regs->ah = inb(addr+1) ^ 0x48;
294 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500295}
296
297static void
298handle_17XX(struct bregs *regs)
299{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500300 set_unimplemented(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500301}
302
303// INT17h : Printer Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500304void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500305handle_17(struct bregs *regs)
306{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400307 debug_enter(regs, DEBUG_HDL_17);
Kevin O'Connor40967022008-07-21 22:23:05 -0400308 if (! CONFIG_LPT) {
309 handle_17XX(regs);
310 return;
311 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500312
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500313 switch (regs->ah) {
314 case 0x00: handle_1700(regs); break;
315 case 0x01: handle_1701(regs); break;
316 case 0x02: handle_1702(regs); break;
317 default: handle_17XX(regs); break;
318 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500319}