xref: /netbsd/sys/arch/i386/stand/lib/pcio.c (revision 6550d01e)
1 /*	$NetBSD: pcio.c,v 1.28 2010/06/25 15:35:08 tsutsui Exp $	 */
2 
3 /*
4  * Copyright (c) 1996, 1997
5  *	Matthias Drochner.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 /*
30  * console I/O
31  * needs lowlevel routines from conio.S and comio.S
32  */
33 
34 #include <lib/libsa/stand.h>
35 #include <lib/libkern/libkern.h>
36 #include <sys/bootblock.h>
37 
38 #include "libi386.h"
39 #include "bootinfo.h"
40 
41 extern void conputc(int);
42 extern int congetc(void);
43 extern int conisshift(void);
44 extern int coniskey(void);
45 extern struct x86_boot_params boot_params;
46 
47 struct btinfo_console btinfo_console;
48 
49 #ifdef SUPPORT_SERIAL
50 static int iodev;
51 
52 #ifdef DIRECT_SERIAL
53 #include "comio_direct.h"
54 
55 #define cominit_x()	btinfo_console.speed = \
56 			    cominit_d(btinfo_console.addr, btinfo_console.speed)
57 #define computc_x(ch)	computc_d(ch, btinfo_console.addr)
58 #define comgetc_x()	comgetc_d(btinfo_console.addr)
59 #define comstatus_x()	comstatus_d(btinfo_console.addr)
60 
61 #else
62 #define cominit_x()	cominit(iodev - CONSDEV_COM0)
63 #define computc_x(ch)	computc(ch, iodev - CONSDEV_COM0)
64 #define comgetc_x()	comgetc(iodev - CONSDEV_COM0)
65 #define comstatus_x()	comstatus(iodev - CONSDEV_COM0)
66 
67 #endif /* DIRECT_SERIAL */
68 
69 static int getcomaddr(int);
70 #endif /* SUPPORT_SERIAL */
71 
72 #define POLL_FREQ 10
73 
74 static void
75 wait(int us)
76 {
77 	int prev = biosgetsystime();
78 	int tgt = prev + (20 * us) / 1000000;
79 	int new;
80 
81 	while ((new = biosgetsystime()) < tgt) {
82 		if (new < prev) /* XXX timer wrapped */
83 			break;
84 		prev = new;
85 	}
86 }
87 
88 #ifdef SUPPORT_SERIAL
89 static int
90 getcomaddr(int idx)
91 {
92 	short addr;
93 #ifdef CONSADDR
94 	if (CONSADDR != 0)
95 		return CONSADDR;
96 #endif
97 	/* read in BIOS data area */
98 	pvbcopy((void *)(0x400 + 2 * idx), &addr, 2);
99 	return addr;
100 }
101 #endif
102 
103 void
104 clear_pc_screen(void)
105 {
106 #ifdef SUPPORT_SERIAL
107 	/* Clear the screen if we are on a glass tty. */
108 	if (iodev == CONSDEV_PC)
109 		conclr();
110 #endif
111 }
112 
113 void
114 initio(int dev)
115 {
116 #ifdef SUPPORT_SERIAL
117 	int i;
118 
119 #if defined(DIRECT_SERIAL) && defined(CONSPEED)
120 	btinfo_console.speed = CONSPEED;
121 #else
122 	btinfo_console.speed = 9600;
123 #endif
124 
125 	switch (dev) {
126 	case CONSDEV_AUTO:
127 		for (i = 0; i < 3; i++) {
128 			iodev = CONSDEV_COM0 + i;
129 			btinfo_console.addr = getcomaddr(i);
130 			if (!btinfo_console.addr)
131 				break;
132 			conputc('0' + i); /* to tell user what happens */
133 			cominit_x();
134 #ifdef DIRECT_SERIAL
135 			/* check for:
136 			 *  1. successful output
137 			 *  2. optionally, keypress within 7s
138 			 */
139 			if (	computc_x(':') &&
140 				computc_x('-') &&
141 				computc_x('(')
142 #ifdef COMCONS_KEYPRESS
143 			   && awaitkey(7, 0)
144 #endif
145 			   )
146 				goto ok;
147 #else /* ! DIRECT_SERIAL */
148 			/*
149 			 * serial console must have hardware handshake!
150 			 * check:
151 			 *  1. character output without error
152 			 *  2. status bits for modem ready set
153 			 *     (status seems only useful after character output)
154 			 *  3. optionally, keypress within 7s
155 			 */
156 			if (!(computc_x('@') & 0x80)
157 			    && (comstatus_x() & 0x00b0)
158 #ifdef COMCONS_KEYPRESS
159 			    && awaitkey(7, 0)
160 #endif
161 			    )
162 				goto ok;
163 #endif /* DIRECT_SERIAL */
164 		}
165 		iodev = CONSDEV_PC;
166 ok:
167 		break;
168 	case CONSDEV_COM0:
169 	case CONSDEV_COM1:
170 	case CONSDEV_COM2:
171 	case CONSDEV_COM3:
172 		iodev = dev;
173 		btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
174 		if (!btinfo_console.addr)
175 			goto nocom;
176 		cominit_x();
177 		break;
178 	case CONSDEV_COM0KBD:
179 	case CONSDEV_COM1KBD:
180 	case CONSDEV_COM2KBD:
181 	case CONSDEV_COM3KBD:
182 		iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
183 		i = iodev - CONSDEV_COM0;
184 		btinfo_console.addr = getcomaddr(i);
185 		if (!btinfo_console.addr)
186 			goto nocom;
187 		conputc('0' + i); /* to tell user what happens */
188 		cominit_x();
189 #ifdef DIRECT_SERIAL
190 			/* check for:
191 			 *  1. successful output
192 			 *  2. optionally, keypress within 7s
193 			 */
194 			if (	computc_x(':') &&
195 				computc_x('-') &&
196 				computc_x('(')
197 #ifdef COMCONS_KEYPRESS
198 			   && awaitkey(7, 0)
199 #endif
200 			   )
201 				break;
202 #else /* ! DIRECT_SERIAL */
203 			/*
204 			 * serial console must have hardware handshake!
205 			 * check:
206 			 *  1. character output without error
207 			 *  2. status bits for modem ready set
208 			 *     (status seems only useful after character output)
209 			 *  3. optionally, keypress within 7s
210 			 */
211 			if (!(computc_x('@') & 0x80)
212 			    && (comstatus_x() & 0x00b0)
213 #ifdef COMCONS_KEYPRESS
214 			    && awaitkey(7, 0)
215 #endif
216 			    )
217 				break;
218 #endif /* DIRECT_SERIAL */
219 	default:
220 nocom:
221 		iodev = CONSDEV_PC;
222 		break;
223 	}
224 	conputc('\015');
225 	conputc('\n');
226 	strncpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16);
227 
228 #else /* !SUPPORT_SERIAL */
229 	btinfo_console.devname[0] = 'p';
230 	btinfo_console.devname[1] = 'c';
231 	btinfo_console.devname[2] = 0;
232 #endif /* SUPPORT_SERIAL */
233 }
234 
235 static inline void internal_putchar(int);
236 
237 static inline void
238 internal_putchar(int c)
239 {
240 #ifdef SUPPORT_SERIAL
241 	switch (iodev) {
242 	case CONSDEV_PC:
243 #endif
244 		conputc(c);
245 #ifdef SUPPORT_SERIAL
246 		break;
247 	case CONSDEV_COM0:
248 	case CONSDEV_COM1:
249 	case CONSDEV_COM2:
250 	case CONSDEV_COM3:
251 		computc_x(c);
252 		break;
253 	}
254 #endif
255 }
256 
257 void
258 putchar(int c)
259 {
260 	if (c == '\n')
261 		internal_putchar('\r');
262 	internal_putchar(c);
263 }
264 
265 int
266 getchar(void)
267 {
268 	int c;
269 #ifdef SUPPORT_SERIAL
270 	switch (iodev) {
271 	default: /* to make gcc -Wall happy... */
272 	case CONSDEV_PC:
273 #endif
274 		c = congetc();
275 #ifdef CONSOLE_KEYMAP
276 		{
277 			char *cp = strchr(CONSOLE_KEYMAP, c);
278 			if (cp != 0 && cp[1] != 0)
279 				c = cp[1];
280 		}
281 #endif
282 		return c;
283 #ifdef SUPPORT_SERIAL
284 	case CONSDEV_COM0:
285 	case CONSDEV_COM1:
286 	case CONSDEV_COM2:
287 	case CONSDEV_COM3:
288 #ifdef DIRECT_SERIAL
289 		c = comgetc_x();
290 #else
291 		do {
292 			c = comgetc_x();
293 		} while ((c >> 8) == 0xe0); /* catch timeout */
294 #ifdef COMDEBUG
295 		if (c & 0x8000) {
296 			printf("com input %x, status %x\n",
297 			       c, comstatus_x());
298 		}
299 #endif
300 		c &= 0xff;
301 #endif /* DIRECT_SERIAL */
302 		return c;
303 	}
304 #endif /* SUPPORT_SERIAL */
305 }
306 
307 int
308 iskey(int intr)
309 {
310 #ifdef SUPPORT_SERIAL
311 	switch (iodev) {
312 	default: /* to make gcc -Wall happy... */
313 	case CONSDEV_PC:
314 #endif
315 		return (intr && conisshift()) || coniskey();
316 #ifdef SUPPORT_SERIAL
317 	case CONSDEV_COM0:
318 	case CONSDEV_COM1:
319 	case CONSDEV_COM2:
320 	case CONSDEV_COM3:
321 #ifdef DIRECT_SERIAL
322 		return !!comstatus_x();
323 #else
324 		return !!(comstatus_x() & 0x0100);
325 #endif
326 	}
327 #endif /* SUPPORT_SERIAL */
328 }
329 
330 char
331 awaitkey(int timeout, int tell)
332 {
333 	int i;
334 	char c = 0;
335 
336 	i = timeout * POLL_FREQ;
337 
338 	for (;;) {
339 		if (tell && (i % POLL_FREQ) == 0) {
340 			char numbuf[32];
341 			int len;
342 
343 			len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ",
344 			    i/POLL_FREQ);
345 			if (len > 0 && len < sizeof(numbuf)) {
346 				char *p = numbuf;
347 
348 				printf("%s", numbuf);
349 				while (*p)
350 					*p++ = '\b';
351 				printf("%s", numbuf);
352 			}
353 		}
354 		if (iskey(1)) {
355 			/* flush input buffer */
356 			while (iskey(0))
357 				c = getchar();
358 			if (c == 0)
359 				c = -1;
360 			goto out;
361 		}
362 		if (i--)
363 			wait(1000000 / POLL_FREQ);
364 		else
365 			break;
366 	}
367 
368 out:
369 	if (tell)
370 		printf("0 seconds.     \n");
371 
372 	return c;
373 }
374 
375 void
376 wait_sec(int sec)
377 {
378 
379 	wait(sec * 1000000);
380 }
381