1/*	$NetBSD: pcio.c,v 1.29 2011/02/14 23:47:11 jmcneill 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
41extern struct x86_boot_params boot_params;
42
43struct btinfo_console btinfo_console;
44
45#ifdef SUPPORT_SERIAL
46static int iodev;
47
48#ifdef DIRECT_SERIAL
49#include "comio_direct.h"
50
51#define cominit_x()	btinfo_console.speed = \
52			    cominit_d(btinfo_console.addr, btinfo_console.speed)
53#define computc_x(ch)	computc_d(ch, btinfo_console.addr)
54#define comgetc_x()	comgetc_d(btinfo_console.addr)
55#define comstatus_x()	comstatus_d(btinfo_console.addr)
56
57#else
58#define cominit_x()	cominit(iodev - CONSDEV_COM0)
59#define computc_x(ch)	computc(ch, iodev - CONSDEV_COM0)
60#define comgetc_x()	comgetc(iodev - CONSDEV_COM0)
61#define comstatus_x()	comstatus(iodev - CONSDEV_COM0)
62
63#endif /* DIRECT_SERIAL */
64
65static int getcomaddr(int);
66#endif /* SUPPORT_SERIAL */
67
68#define POLL_FREQ 10
69
70static void
71wait(int us)
72{
73	int prev = biosgetsystime();
74	int tgt = prev + (20 * us) / 1000000;
75	int new;
76
77	while ((new = biosgetsystime()) < tgt) {
78		if (new < prev) /* XXX timer wrapped */
79			break;
80		prev = new;
81	}
82}
83
84#ifdef SUPPORT_SERIAL
85static int
86getcomaddr(int idx)
87{
88	short addr;
89#ifdef CONSADDR
90	if (CONSADDR != 0)
91		return CONSADDR;
92#endif
93	/* read in BIOS data area */
94	pvbcopy((void *)(0x400 + 2 * idx), &addr, 2);
95	return addr;
96}
97#endif
98
99void
100clear_pc_screen(void)
101{
102#ifdef SUPPORT_SERIAL
103	/* Clear the screen if we are on a glass tty. */
104	if (iodev == CONSDEV_PC)
105		conclr();
106#endif
107}
108
109void
110initio(int dev)
111{
112#ifdef SUPPORT_SERIAL
113	int i;
114
115#if defined(DIRECT_SERIAL) && defined(CONSPEED)
116	btinfo_console.speed = CONSPEED;
117#else
118	btinfo_console.speed = 9600;
119#endif
120
121	switch (dev) {
122	case CONSDEV_AUTO:
123		for (i = 0; i < 3; i++) {
124			iodev = CONSDEV_COM0 + i;
125			btinfo_console.addr = getcomaddr(i);
126			if (!btinfo_console.addr)
127				break;
128			conputc('0' + i); /* to tell user what happens */
129			cominit_x();
130#ifdef DIRECT_SERIAL
131			/* check for:
132			 *  1. successful output
133			 *  2. optionally, keypress within 7s
134			 */
135			if (	computc_x(':') &&
136				computc_x('-') &&
137				computc_x('(')
138#ifdef COMCONS_KEYPRESS
139			   && awaitkey(7, 0)
140#endif
141			   )
142				goto ok;
143#else /* ! DIRECT_SERIAL */
144			/*
145			 * serial console must have hardware handshake!
146			 * check:
147			 *  1. character output without error
148			 *  2. status bits for modem ready set
149			 *     (status seems only useful after character output)
150			 *  3. optionally, keypress within 7s
151			 */
152			if (!(computc_x('@') & 0x80)
153			    && (comstatus_x() & 0x00b0)
154#ifdef COMCONS_KEYPRESS
155			    && awaitkey(7, 0)
156#endif
157			    )
158				goto ok;
159#endif /* DIRECT_SERIAL */
160		}
161		iodev = CONSDEV_PC;
162ok:
163		break;
164	case CONSDEV_COM0:
165	case CONSDEV_COM1:
166	case CONSDEV_COM2:
167	case CONSDEV_COM3:
168		iodev = dev;
169		btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
170		if (!btinfo_console.addr)
171			goto nocom;
172		cominit_x();
173		break;
174	case CONSDEV_COM0KBD:
175	case CONSDEV_COM1KBD:
176	case CONSDEV_COM2KBD:
177	case CONSDEV_COM3KBD:
178		iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
179		i = iodev - CONSDEV_COM0;
180		btinfo_console.addr = getcomaddr(i);
181		if (!btinfo_console.addr)
182			goto nocom;
183		conputc('0' + i); /* to tell user what happens */
184		cominit_x();
185#ifdef DIRECT_SERIAL
186			/* check for:
187			 *  1. successful output
188			 *  2. optionally, keypress within 7s
189			 */
190			if (	computc_x(':') &&
191				computc_x('-') &&
192				computc_x('(')
193#ifdef COMCONS_KEYPRESS
194			   && awaitkey(7, 0)
195#endif
196			   )
197				break;
198#else /* ! DIRECT_SERIAL */
199			/*
200			 * serial console must have hardware handshake!
201			 * check:
202			 *  1. character output without error
203			 *  2. status bits for modem ready set
204			 *     (status seems only useful after character output)
205			 *  3. optionally, keypress within 7s
206			 */
207			if (!(computc_x('@') & 0x80)
208			    && (comstatus_x() & 0x00b0)
209#ifdef COMCONS_KEYPRESS
210			    && awaitkey(7, 0)
211#endif
212			    )
213				break;
214#endif /* DIRECT_SERIAL */
215	default:
216nocom:
217		iodev = CONSDEV_PC;
218		break;
219	}
220	conputc('\015');
221	conputc('\n');
222	strncpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16);
223
224#else /* !SUPPORT_SERIAL */
225	btinfo_console.devname[0] = 'p';
226	btinfo_console.devname[1] = 'c';
227	btinfo_console.devname[2] = 0;
228#endif /* SUPPORT_SERIAL */
229}
230
231static inline void internal_putchar(int);
232
233static inline void
234internal_putchar(int c)
235{
236#ifdef SUPPORT_SERIAL
237	switch (iodev) {
238	case CONSDEV_PC:
239#endif
240		conputc(c);
241#ifdef SUPPORT_SERIAL
242		break;
243	case CONSDEV_COM0:
244	case CONSDEV_COM1:
245	case CONSDEV_COM2:
246	case CONSDEV_COM3:
247		computc_x(c);
248		break;
249	}
250#endif
251}
252
253void
254putchar(int c)
255{
256	if (c == '\n')
257		internal_putchar('\r');
258	internal_putchar(c);
259}
260
261int
262getchar(void)
263{
264	int c;
265#ifdef SUPPORT_SERIAL
266	switch (iodev) {
267	default: /* to make gcc -Wall happy... */
268	case CONSDEV_PC:
269#endif
270		while (!coniskey())
271			;
272		c = congetc();
273#ifdef CONSOLE_KEYMAP
274		{
275			char *cp = strchr(CONSOLE_KEYMAP, c);
276			if (cp != 0 && cp[1] != 0)
277				c = cp[1];
278		}
279#endif
280		return c;
281#ifdef SUPPORT_SERIAL
282	case CONSDEV_COM0:
283	case CONSDEV_COM1:
284	case CONSDEV_COM2:
285	case CONSDEV_COM3:
286#ifdef DIRECT_SERIAL
287		c = comgetc_x();
288#else
289		do {
290			c = comgetc_x();
291		} while ((c >> 8) == 0xe0); /* catch timeout */
292#ifdef COMDEBUG
293		if (c & 0x8000) {
294			printf("com input %x, status %x\n",
295			       c, comstatus_x());
296		}
297#endif
298		c &= 0xff;
299#endif /* DIRECT_SERIAL */
300		return c;
301	}
302#endif /* SUPPORT_SERIAL */
303}
304
305int
306iskey(int intr)
307{
308#ifdef SUPPORT_SERIAL
309	switch (iodev) {
310	default: /* to make gcc -Wall happy... */
311	case CONSDEV_PC:
312#endif
313		return (intr && conisshift()) || coniskey();
314#ifdef SUPPORT_SERIAL
315	case CONSDEV_COM0:
316	case CONSDEV_COM1:
317	case CONSDEV_COM2:
318	case CONSDEV_COM3:
319#ifdef DIRECT_SERIAL
320		return !!comstatus_x();
321#else
322		return !!(comstatus_x() & 0x0100);
323#endif
324	}
325#endif /* SUPPORT_SERIAL */
326}
327
328char
329awaitkey(int timeout, int tell)
330{
331	int i;
332	char c = 0;
333
334	i = timeout * POLL_FREQ;
335
336	for (;;) {
337		if (tell && (i % POLL_FREQ) == 0) {
338			char numbuf[32];
339			int len;
340
341			len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ",
342			    i/POLL_FREQ);
343			if (len > 0 && len < sizeof(numbuf)) {
344				char *p = numbuf;
345
346				printf("%s", numbuf);
347				while (*p)
348					*p++ = '\b';
349				printf("%s", numbuf);
350			}
351		}
352		if (iskey(1)) {
353			/* flush input buffer */
354			while (iskey(0))
355				c = getchar();
356			if (c == 0)
357				c = -1;
358			goto out;
359		}
360		if (i--)
361			wait(1000000 / POLL_FREQ);
362		else
363			break;
364	}
365
366out:
367	if (tell)
368		printf("0 seconds.     \n");
369
370	return c;
371}
372
373void
374wait_sec(int sec)
375{
376
377	wait(sec * 1000000);
378}
379