blob: 02f30d68425b0ee8140a9feade4f7489ffd5f43b [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'Connor2d2fa312013-09-14 21:55:26 -040010#include "output.h" // debug_enter
Kevin O'Connor3df600b2013-09-14 19:28:55 -040011#include "stacks.h" // yield
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "util.h" // serial_setup
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050013
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050014
15/****************************************************************
16 * COM ports
17 ****************************************************************/
18
19static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040020detect_serial(u16 port, u8 timeout, u8 count)
21{
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040022 outb(0x02, port+SEROFF_IER);
23 u8 ier = inb(port+SEROFF_IER);
24 if (ier != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040025 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040026 u8 iir = inb(port+SEROFF_IIR);
27 if ((iir & 0x3f) != 0x02)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040028 return 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040029
30 outb(0x00, port+SEROFF_IER);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040031 SET_BDA(port_com[count], port);
32 SET_BDA(com_timeout[count], timeout);
33 return 1;
34}
35
36void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050037serial_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040038{
Kevin O'Connor40967022008-07-21 22:23:05 -040039 if (! CONFIG_SERIAL)
40 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -040041 dprintf(3, "init serial\n");
Kevin O'Connor40967022008-07-21 22:23:05 -040042
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040043 u16 count = 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040044 count += detect_serial(PORT_SERIAL1, 0x0a, count);
45 count += detect_serial(PORT_SERIAL2, 0x0a, count);
46 count += detect_serial(PORT_SERIAL3, 0x0a, count);
47 count += detect_serial(PORT_SERIAL4, 0x0a, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -040048 dprintf(1, "Found %d serial ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040049
50 // Equipment word bits 9..11 determing # serial ports
Kevin O'Connore51316d2012-06-10 09:09:22 -040051 set_equipment_flags(0xe00, count << 9);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -040052}
53
54static u16
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050055getComAddr(struct bregs *regs)
56{
57 if (regs->dx >= 4) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050058 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050059 return 0;
60 }
61 u16 addr = GET_BDA(port_com[regs->dx]);
62 if (! addr)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050063 set_invalid(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050064 return addr;
65}
66
Kevin O'Connor1812e202008-05-07 21:29:50 -040067// SERIAL - INITIALIZE PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050068static void
69handle_1400(struct bregs *regs)
70{
71 u16 addr = getComAddr(regs);
72 if (!addr)
73 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040074 outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050075 if ((regs->al & 0xE0) == 0) {
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040076 outb(0x17, addr+SEROFF_DLL);
77 outb(0x04, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050078 } else {
79 u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5);
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040080 outb(val16 & 0xFF, addr+SEROFF_DLL);
81 outb(val16 >> 8, addr+SEROFF_DLH);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050082 }
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040083 outb(regs->al & 0x1F, addr+SEROFF_LCR);
84 regs->ah = inb(addr+SEROFF_LSR);
85 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -040086 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050087}
88
Kevin O'Connor1812e202008-05-07 21:29:50 -040089// SERIAL - WRITE CHARACTER TO PORT
90static void
91handle_1401(struct bregs *regs)
92{
93 u16 addr = getComAddr(regs);
94 if (!addr)
95 return;
Kevin O'Connor95ee3822013-07-20 18:07:50 -040096 u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -040097 for (;;) {
98 u8 lsr = inb(addr+SEROFF_LSR);
99 if ((lsr & 0x60) == 0x60) {
100 // Success - can write data
101 outb(regs->al, addr+SEROFF_DATA);
102 // XXX - reread lsr?
103 regs->ah = lsr;
104 break;
105 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400106 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400107 // Timed out - can't write data.
108 regs->ah = lsr | 0x80;
109 break;
Kevin O'Connor5c732402008-06-07 10:43:07 -0400110 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400111 yield();
Kevin O'Connor5c732402008-06-07 10:43:07 -0400112 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400113 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500114}
115
Kevin O'Connor1812e202008-05-07 21:29:50 -0400116// SERIAL - READ CHARACTER FROM PORT
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500117static void
118handle_1402(struct bregs *regs)
119{
120 u16 addr = getComAddr(regs);
121 if (!addr)
122 return;
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400123 u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx]));
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400124 for (;;) {
125 u8 lsr = inb(addr+SEROFF_LSR);
126 if (lsr & 0x01) {
127 // Success - can read data
128 regs->al = inb(addr+SEROFF_DATA);
129 regs->ah = lsr;
130 break;
131 }
Kevin O'Connor95ee3822013-07-20 18:07:50 -0400132 if (irqtimer_check(end)) {
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400133 // Timed out - can't read data.
134 regs->ah = lsr | 0x80;
135 break;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500136 }
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400137 yield();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500138 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400139 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500140}
141
Kevin O'Connor1812e202008-05-07 21:29:50 -0400142// SERIAL - GET PORT STATUS
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500143static void
144handle_1403(struct bregs *regs)
145{
146 u16 addr = getComAddr(regs);
147 if (!addr)
148 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -0400149 regs->ah = inb(addr+SEROFF_LSR);
150 regs->al = inb(addr+SEROFF_MSR);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400151 set_success(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500152}
153
154static void
155handle_14XX(struct bregs *regs)
156{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500157 set_unimplemented(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500158}
159
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500160// INT 14h Serial Communications Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500161void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500162handle_14(struct bregs *regs)
163{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400164 debug_enter(regs, DEBUG_HDL_14);
Kevin O'Connor40967022008-07-21 22:23:05 -0400165 if (! CONFIG_SERIAL) {
166 handle_14XX(regs);
167 return;
168 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500169
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500170 switch (regs->ah) {
171 case 0x00: handle_1400(regs); break;
172 case 0x01: handle_1401(regs); break;
173 case 0x02: handle_1402(regs); break;
174 case 0x03: handle_1403(regs); break;
175 default: handle_14XX(regs); break;
176 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500177}
178
Kevin O'Connor30853762009-01-17 18:49:20 -0500179// XXX - Baud Rate Generator Table
180u8 BaudTable[16] VAR16FIXED(0xe729);
181
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500182
183/****************************************************************
184 * LPT ports
185 ****************************************************************/
186
187static u16
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400188detect_parport(u16 port, u8 timeout, u8 count)
189{
190 // clear input mode
191 outb(inb(port+2) & 0xdf, port+2);
192
193 outb(0xaa, port);
194 if (inb(port) != 0xaa)
195 // Not present
196 return 0;
197 SET_BDA(port_lpt[count], port);
198 SET_BDA(lpt_timeout[count], timeout);
199 return 1;
200}
201
202void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500203lpt_setup(void)
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400204{
Kevin O'Connor40967022008-07-21 22:23:05 -0400205 if (! CONFIG_LPT)
206 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400207 dprintf(3, "init lpt\n");
Kevin O'Connor40967022008-07-21 22:23:05 -0400208
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400209 u16 count = 0;
Kevin O'Connor0c5893d2009-07-13 20:29:07 -0400210 count += detect_parport(PORT_LPT1, 0x14, count);
211 count += detect_parport(PORT_LPT2, 0x14, count);
Kevin O'Connor4edb2752009-05-06 23:25:40 -0400212 dprintf(1, "Found %d lpt ports\n", count);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400213
214 // Equipment word bits 14..15 determing # parallel ports
Kevin O'Connore51316d2012-06-10 09:09:22 -0400215 set_equipment_flags(0xc000, count << 14);
Kevin O'Connor913cc2e2008-04-13 17:31:45 -0400216}
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'Connor95ee3822013-07-20 18:07:50 -0400239 u32 end = irqtimer_calc_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'Connor95ee3822013-07-20 18:07:50 -0400254 if (irqtimer_check(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}