comconsole.c revision 149213
1119482Sobrien/*-
238465Smsmith * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
338465Smsmith *
438465Smsmith * Redistribution and use in source and binary forms, with or without
538465Smsmith * modification, are permitted provided that the following conditions
638465Smsmith * are met:
738465Smsmith * 1. Redistributions of source code must retain the above copyright
838465Smsmith *    notice, this list of conditions and the following disclaimer.
938465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1038465Smsmith *    notice, this list of conditions and the following disclaimer in the
1138465Smsmith *    documentation and/or other materials provided with the distribution.
1238465Smsmith *
1338465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1438465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1538465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1638465Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1738465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1838465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1938465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2038465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2138465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2238465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2338465Smsmith * SUCH DAMAGE.
2438465Smsmith */
2538465Smsmith
26119482Sobrien#include <sys/cdefs.h>
27119482Sobrien__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/comconsole.c 149213 2005-08-18 01:39:43Z iedowse $");
28119482Sobrien
2938465Smsmith#include <stand.h>
3039441Smsmith#include <bootstrap.h>
3141285Srnordier#include <machine/cpufunc.h>
32120118Sbde#include <dev/ic/ns16550.h>
3339441Smsmith#include "libi386.h"
3438465Smsmith
3541285Srnordier#define COMC_FMT	0x3		/* 8N1 */
3641285Srnordier#define COMC_TXWAIT	0x40000		/* transmit timeout */
3741285Srnordier#define COMC_BPS(x)	(115200 / (x))	/* speed to DLAB divisor */
38149213Siedowse#define COMC_DIV2BPS(x)	(115200 / (x))	/* DLAB divisor to speed */
3941285Srnordier
4042480Srnordier#ifndef	COMPORT
4141285Srnordier#define COMPORT		0x3f8
4242480Srnordier#endif
4342480Srnordier#ifndef	COMSPEED
4441285Srnordier#define COMSPEED	9600
4542480Srnordier#endif
4641285Srnordier
4738465Smsmithstatic void	comc_probe(struct console *cp);
4838465Smsmithstatic int	comc_init(int arg);
4939441Smsmithstatic void	comc_putchar(int c);
5039441Smsmithstatic int	comc_getchar(void);
51149213Siedowsestatic int	comc_getspeed(void);
5239441Smsmithstatic int	comc_ischar(void);
53149213Siedowsestatic int	comc_parsespeed(const char *string);
54149213Siedowsestatic void	comc_setup(int speed);
55149213Siedowsestatic int	comc_speed_set(struct env_var *ev, int flags,
56149213Siedowse		    const void *value);
5738465Smsmith
5840211Speterstatic int	comc_started;
59149213Siedowsestatic int	comc_curspeed;
6040211Speter
6138465Smsmithstruct console comconsole = {
6238465Smsmith    "comconsole",
6341285Srnordier    "serial port",
6438465Smsmith    0,
6538465Smsmith    comc_probe,
6638465Smsmith    comc_init,
6739441Smsmith    comc_putchar,
6839441Smsmith    comc_getchar,
6939441Smsmith    comc_ischar
7038465Smsmith};
7138465Smsmith
7238465Smsmithstatic void
7338465Smsmithcomc_probe(struct console *cp)
7438465Smsmith{
75149213Siedowse    char speedbuf[16];
76149213Siedowse    char *cons, *speedenv;
77149213Siedowse    int speed;
78149213Siedowse
7938465Smsmith    /* XXX check the BIOS equipment list? */
8038465Smsmith    cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
81149213Siedowse
82149213Siedowse    if (comc_curspeed == 0) {
83149213Siedowse	comc_curspeed = COMSPEED;
84149213Siedowse	/*
85149213Siedowse	 * Assume that the speed was set by an earlier boot loader if
86149213Siedowse	 * comconsole is already the preferred console.
87149213Siedowse	 */
88149213Siedowse	cons = getenv("console");
89149213Siedowse	if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
90149213Siedowse	    getenv("boot_multicons") != NULL) {
91149213Siedowse		comc_curspeed = comc_getspeed();
92149213Siedowse	}
93149213Siedowse	speedenv = getenv("comconsole_speed");
94149213Siedowse	if (speedenv != NULL) {
95149213Siedowse	    speed = comc_parsespeed(speedenv);
96149213Siedowse	    if (speed > 0)
97149213Siedowse		comc_curspeed = speed;
98149213Siedowse	}
99149213Siedowse
100149213Siedowse	sprintf(speedbuf, "%d", comc_curspeed);
101149213Siedowse	unsetenv("comconsole_speed");
102149213Siedowse	env_setenv("comconsole_speed", EV_VOLATILE, speedbuf, comc_speed_set,
103149213Siedowse	    env_nounset);
104149213Siedowse    }
10538465Smsmith}
10638465Smsmith
10738465Smsmithstatic int
10838465Smsmithcomc_init(int arg)
10938465Smsmith{
11040211Speter    if (comc_started && arg == 0)
11140211Speter	return 0;
11240211Speter    comc_started = 1;
11340211Speter
114149213Siedowse    comc_setup(comc_curspeed);
11540211Speter
11640211Speter    return(0);
11738465Smsmith}
11838465Smsmith
11939441Smsmithstatic void
12039441Smsmithcomc_putchar(int c)
12139441Smsmith{
12241285Srnordier    int wait;
12341285Srnordier
12441285Srnordier    for (wait = COMC_TXWAIT; wait > 0; wait--)
12541285Srnordier        if (inb(COMPORT + com_lsr) & LSR_TXRDY) {
12664187Sjhb	    outb(COMPORT + com_data, (u_char)c);
12741285Srnordier	    break;
12841285Srnordier	}
12939441Smsmith}
13039441Smsmith
13138465Smsmithstatic int
13239441Smsmithcomc_getchar(void)
13338465Smsmith{
13441285Srnordier    return(comc_ischar() ? inb(COMPORT + com_data) : -1);
13538465Smsmith}
13639441Smsmith
13739441Smsmithstatic int
13839441Smsmithcomc_ischar(void)
13939441Smsmith{
14041285Srnordier    return(inb(COMPORT + com_lsr) & LSR_RXRDY);
14139441Smsmith}
142149213Siedowse
143149213Siedowsestatic int
144149213Siedowsecomc_speed_set(struct env_var *ev, int flags, const void *value)
145149213Siedowse{
146149213Siedowse    int speed;
147149213Siedowse
148149213Siedowse    if (value == NULL || (speed = comc_parsespeed(value)) <= 0) {
149149213Siedowse	printf("Invalid speed\n");
150149213Siedowse	return (CMD_ERROR);
151149213Siedowse    }
152149213Siedowse
153149213Siedowse    if (comc_started && comc_curspeed != speed)
154149213Siedowse	comc_setup(speed);
155149213Siedowse
156149213Siedowse    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
157149213Siedowse
158149213Siedowse    return (CMD_OK);
159149213Siedowse}
160149213Siedowse
161149213Siedowsestatic void
162149213Siedowsecomc_setup(int speed)
163149213Siedowse{
164149213Siedowse
165149213Siedowse    comc_curspeed = speed;
166149213Siedowse
167149213Siedowse    outb(COMPORT + com_cfcr, CFCR_DLAB | COMC_FMT);
168149213Siedowse    outb(COMPORT + com_dlbl, COMC_BPS(speed) & 0xff);
169149213Siedowse    outb(COMPORT + com_dlbh, COMC_BPS(speed) >> 8);
170149213Siedowse    outb(COMPORT + com_cfcr, COMC_FMT);
171149213Siedowse    outb(COMPORT + com_mcr, MCR_RTS | MCR_DTR);
172149213Siedowse
173149213Siedowse    do
174149213Siedowse        inb(COMPORT + com_data);
175149213Siedowse    while (inb(COMPORT + com_lsr) & LSR_RXRDY);
176149213Siedowse}
177149213Siedowse
178149213Siedowsestatic int
179149213Siedowsecomc_parsespeed(const char *speedstr)
180149213Siedowse{
181149213Siedowse    char *p;
182149213Siedowse    int speed;
183149213Siedowse
184149213Siedowse    speed = strtol(speedstr, &p, 0);
185149213Siedowse    if (p == speedstr || *p != '\0' || speed <= 0)
186149213Siedowse	return (-1);
187149213Siedowse
188149213Siedowse    return (speed);
189149213Siedowse}
190149213Siedowse
191149213Siedowsestatic int
192149213Siedowsecomc_getspeed(void)
193149213Siedowse{
194149213Siedowse	u_int	divisor;
195149213Siedowse	u_char	dlbh;
196149213Siedowse	u_char	dlbl;
197149213Siedowse	u_char	cfcr;
198149213Siedowse
199149213Siedowse	cfcr = inb(COMPORT + com_cfcr);
200149213Siedowse	outb(COMPORT + com_cfcr, CFCR_DLAB | cfcr);
201149213Siedowse
202149213Siedowse	dlbl = inb(COMPORT + com_dlbl);
203149213Siedowse	dlbh = inb(COMPORT + com_dlbh);
204149213Siedowse
205149213Siedowse	outb(COMPORT + com_cfcr, cfcr);
206149213Siedowse
207149213Siedowse	divisor = dlbh << 8 | dlbl;
208149213Siedowse
209149213Siedowse	/* XXX there should be more sanity checking. */
210149213Siedowse	if (divisor == 0)
211149213Siedowse		return (COMSPEED);
212149213Siedowse	return (COMC_DIV2BPS(divisor));
213149213Siedowse}
214