16e4a3393SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
3199767f8SToomas Soome  *
4199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
5199767f8SToomas Soome  * modification, are permitted provided that the following conditions
6199767f8SToomas Soome  * are met:
7199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
8199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
9199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
10199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
11199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
12199767f8SToomas Soome  *
13199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14199767f8SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15199767f8SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17199767f8SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19199767f8SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21199767f8SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22199767f8SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23199767f8SToomas Soome  * SUCH DAMAGE.
24199767f8SToomas Soome  */
25199767f8SToomas Soome 
265ac07b12SToomas Soome /*
275ac07b12SToomas Soome  * This code is shared on BIOS and UEFI systems on x86 because
285ac07b12SToomas Soome  * we can access io ports on both platforms and the UEFI Serial IO protocol
295ac07b12SToomas Soome  * is not giving us reliable port order and we see issues with input.
305ac07b12SToomas Soome  */
31199767f8SToomas Soome #include <sys/cdefs.h>
32199767f8SToomas Soome 
33199767f8SToomas Soome #include <stand.h>
34199767f8SToomas Soome #include <bootstrap.h>
35fec53dd4SToomas Soome #include <stdbool.h>
36199767f8SToomas Soome #include <machine/cpufunc.h>
37199767f8SToomas Soome #include <dev/ic/ns16550.h>
38199767f8SToomas Soome #include <dev/pci/pcireg.h>
39199767f8SToomas Soome #include "libi386.h"
40199767f8SToomas Soome 
416e4a3393SToomas Soome #define	COMC_TXWAIT	0x40000		/* transmit timeout */
426e4a3393SToomas Soome #define	COMC_BPS(x)	(115200 / (x))	/* speed to DLAB divisor */
436e4a3393SToomas Soome #define	COMC_DIV2BPS(x)	(115200 / (x))	/* DLAB divisor to speed */
44199767f8SToomas Soome 
45199767f8SToomas Soome #ifndef	COMSPEED
466e4a3393SToomas Soome #define	COMSPEED	9600
47199767f8SToomas Soome #endif
48199767f8SToomas Soome 
49d3bd5503SToomas Soome #define	COM_NPORTS	4
50199767f8SToomas Soome #define	COM1_IOADDR	0x3f8
51199767f8SToomas Soome #define	COM2_IOADDR	0x2f8
52199767f8SToomas Soome #define	COM3_IOADDR	0x3e8
53199767f8SToomas Soome #define	COM4_IOADDR	0x2e8
54199767f8SToomas Soome 
55199767f8SToomas Soome #define	STOP1		0x00
56199767f8SToomas Soome #define	STOP2		0x04
57199767f8SToomas Soome 
58199767f8SToomas Soome #define	PARODD		0x00
59199767f8SToomas Soome #define	PAREN		0x08
60199767f8SToomas Soome #define	PAREVN		0x10
61199767f8SToomas Soome #define	PARMARK		0x20
62199767f8SToomas Soome 
63199767f8SToomas Soome #define	BITS5		0x00	/* 5 bits per char */
64199767f8SToomas Soome #define	BITS6		0x01	/* 6 bits per char */
65199767f8SToomas Soome #define	BITS7		0x02	/* 7 bits per char */
66199767f8SToomas Soome #define	BITS8		0x03	/* 8 bits per char */
67199767f8SToomas Soome 
68199767f8SToomas Soome struct serial {
69199767f8SToomas Soome     int		speed;		/* baud rate */
70199767f8SToomas Soome     uint8_t	lcr;		/* line control */
71199767f8SToomas Soome     uint8_t	ignore_cd;	/* boolean */
72199767f8SToomas Soome     uint8_t	rtsdtr_off;	/* boolean */
73199767f8SToomas Soome     int		ioaddr;
74199767f8SToomas Soome     uint32_t	locator;
75199767f8SToomas Soome };
76199767f8SToomas Soome 
7730c75cb0SToomas Soome static void	comc_probe(struct console *);
7830c75cb0SToomas Soome static int	comc_init(struct console *, int);
7930c75cb0SToomas Soome static void	comc_putchar(struct console *, int);
8030c75cb0SToomas Soome static int	comc_getchar(struct console *);
81ceb3ef19SToomas Soome int		comc_getspeed(int);
8230c75cb0SToomas Soome static int	comc_ischar(struct console *);
839890ff83SToomas Soome static int	comc_ioctl(struct console *, int, void *);
8430c75cb0SToomas Soome static uint32_t comc_parse_pcidev(const char *);
8530c75cb0SToomas Soome static int	comc_pcidev_set(struct env_var *, int, const void *);
8630c75cb0SToomas Soome static int	comc_pcidev_handle(struct console *, uint32_t);
87fec53dd4SToomas Soome static bool	comc_setup(struct console *);
8830c75cb0SToomas Soome static char	*comc_asprint_mode(struct serial *);
8930c75cb0SToomas Soome static int	comc_parse_mode(struct serial *, const char *);
90199767f8SToomas Soome static int	comc_mode_set(struct env_var *, int, const void *);
91199767f8SToomas Soome static int	comc_cd_set(struct env_var *, int, const void *);
92199767f8SToomas Soome static int	comc_rtsdtr_set(struct env_var *, int, const void *);
9380e47917SToomas Soome static void	comc_devinfo(struct console *);
94199767f8SToomas Soome 
9580e47917SToomas Soome static void
comc_devinfo(struct console * cp)9680e47917SToomas Soome comc_devinfo(struct console *cp)
9780e47917SToomas Soome {
9880e47917SToomas Soome 	struct serial *port = cp->c_private;
9980e47917SToomas Soome 
100d3bd5503SToomas Soome 	printf("\tport %#x", port->ioaddr);
101d3bd5503SToomas Soome }
102d3bd5503SToomas Soome 
103d3bd5503SToomas Soome static bool
comc_port_is_present(int ioaddr)104d3bd5503SToomas Soome comc_port_is_present(int ioaddr)
105d3bd5503SToomas Soome {
106d3bd5503SToomas Soome 	/*
107d3bd5503SToomas Soome 	 * Write byte to scratch register and read it out.
108d3bd5503SToomas Soome 	 */
109d3bd5503SToomas Soome #define	COMC_TEST	0xbb
110d3bd5503SToomas Soome 	outb(ioaddr + com_scr, COMC_TEST);
111d3bd5503SToomas Soome 	return (inb(ioaddr + com_scr) == COMC_TEST);
112d3bd5503SToomas Soome }
113d3bd5503SToomas Soome 
114d3bd5503SToomas Soome /*
115d3bd5503SToomas Soome  * Set up list of possible serial consoles.
116d3bd5503SToomas Soome  * This function is run very early, so we do not expect to
117d3bd5503SToomas Soome  * run out of memory, and on error, we can not print output.
118d3bd5503SToomas Soome  */
119d3bd5503SToomas Soome void
comc_ini(void)120d3bd5503SToomas Soome comc_ini(void)
121d3bd5503SToomas Soome {
122d3bd5503SToomas Soome 	uint_t n = 0, c;
123d3bd5503SToomas Soome 	bool ports[COM_NPORTS];
124d3bd5503SToomas Soome 	struct console **tmp;
125d3bd5503SToomas Soome 	struct console *tty;
126d3bd5503SToomas Soome 	struct serial *port;
127d3bd5503SToomas Soome 
128d3bd5503SToomas Soome 	/*
129d3bd5503SToomas Soome 	 * Test the presence of 4 serial devices com1-com4
130d3bd5503SToomas Soome 	 */
131d3bd5503SToomas Soome 	ports[0] = comc_port_is_present(COM1_IOADDR);
132d3bd5503SToomas Soome 	ports[1] = comc_port_is_present(COM2_IOADDR);
133d3bd5503SToomas Soome 	ports[2] = comc_port_is_present(COM3_IOADDR);
134d3bd5503SToomas Soome 	ports[3] = comc_port_is_present(COM4_IOADDR);
135d3bd5503SToomas Soome 
136d3bd5503SToomas Soome 	for (uint_t i = 0; i < COM_NPORTS; i++)
137d3bd5503SToomas Soome 		if (ports[i])
138d3bd5503SToomas Soome 			n++;
139d3bd5503SToomas Soome 
140d3bd5503SToomas Soome 	if (n == 0)	/* there are no serial ports */
141d3bd5503SToomas Soome 		return;
142d3bd5503SToomas Soome 
143d3bd5503SToomas Soome 	c = cons_array_size();
144d3bd5503SToomas Soome 	if (c == 0)
145d3bd5503SToomas Soome 		n++;	/* For NULL pointer */
146d3bd5503SToomas Soome 
147d3bd5503SToomas Soome 	tmp = realloc(consoles, (c + n) * sizeof (*consoles));
148d3bd5503SToomas Soome 	if (tmp == NULL)
149d3bd5503SToomas Soome 		return;
150d3bd5503SToomas Soome 	consoles = tmp;
151d3bd5503SToomas Soome 	if (c > 0)
152d3bd5503SToomas Soome 		c--;
153d3bd5503SToomas Soome 
154d3bd5503SToomas Soome 	for (uint_t i = 0; i < COM_NPORTS; i++) {
155d3bd5503SToomas Soome 		if (!ports[i])
156d3bd5503SToomas Soome 			continue;
157d3bd5503SToomas Soome 		tty = malloc(sizeof (*tty));
158d3bd5503SToomas Soome 		if (tty == NULL) {
159d3bd5503SToomas Soome 			/* Out of memory?! can not continue */
160d3bd5503SToomas Soome 			consoles[c] = tty;
161d3bd5503SToomas Soome 			return;
162d3bd5503SToomas Soome 		}
163d3bd5503SToomas Soome 		if (asprintf(&tty->c_name, "tty%c", 'a' + i) < 0) {
164d3bd5503SToomas Soome 			free(tty);
165d3bd5503SToomas Soome 			consoles[c] = NULL;
166d3bd5503SToomas Soome 			return;
167d3bd5503SToomas Soome 		}
168d3bd5503SToomas Soome 		if (asprintf(&tty->c_desc, "serial port %c", 'a' + i) < 0) {
169d3bd5503SToomas Soome 			free(tty->c_name);
170d3bd5503SToomas Soome 			free(tty);
171d3bd5503SToomas Soome 			consoles[c] = NULL;
172d3bd5503SToomas Soome 			return;
173d3bd5503SToomas Soome 		}
174d3bd5503SToomas Soome 		tty->c_flags = 0;
175d3bd5503SToomas Soome 		tty->c_probe = comc_probe;
176d3bd5503SToomas Soome 		tty->c_init = comc_init;
177d3bd5503SToomas Soome 		tty->c_out = comc_putchar;
178d3bd5503SToomas Soome 		tty->c_in = comc_getchar;
179d3bd5503SToomas Soome 		tty->c_ready = comc_ischar;
180d3bd5503SToomas Soome 		tty->c_ioctl = comc_ioctl;
181d3bd5503SToomas Soome 		tty->c_devinfo = comc_devinfo;
182d3bd5503SToomas Soome 		port = malloc(sizeof (*port));
183d3bd5503SToomas Soome 		if (port == NULL) {
184d3bd5503SToomas Soome 			free(tty->c_name);
185d3bd5503SToomas Soome 			free(tty->c_desc);
186d3bd5503SToomas Soome 			free(tty);
187d3bd5503SToomas Soome 			consoles[c] = NULL;
188d3bd5503SToomas Soome 			return;
189d3bd5503SToomas Soome 		}
190d3bd5503SToomas Soome 		port->speed = 0;	/* Leave this for comc_probe */
191d3bd5503SToomas Soome 		switch (i) {
192d3bd5503SToomas Soome 		case 0:
193d3bd5503SToomas Soome 			port->ioaddr = COM1_IOADDR;
194d3bd5503SToomas Soome 			break;
195d3bd5503SToomas Soome 		case 1:
196d3bd5503SToomas Soome 			port->ioaddr = COM2_IOADDR;
197d3bd5503SToomas Soome 			break;
198d3bd5503SToomas Soome 		case 2:
199d3bd5503SToomas Soome 			port->ioaddr = COM3_IOADDR;
200d3bd5503SToomas Soome 			break;
201d3bd5503SToomas Soome 		case 3:
202d3bd5503SToomas Soome 			port->ioaddr = COM4_IOADDR;
203d3bd5503SToomas Soome 			break;
204d3bd5503SToomas Soome 		}
205*ecee58d9SToomas Soome 		port->speed = comc_getspeed(port->ioaddr);
206d3bd5503SToomas Soome 		port->lcr = BITS8;	/* 8,n,1 */
207d3bd5503SToomas Soome 		port->ignore_cd = 1;	/* ignore cd */
208d3bd5503SToomas Soome 		port->rtsdtr_off = 0;	/* rts-dtr is on */
209d3bd5503SToomas Soome 
210d3bd5503SToomas Soome 		tty->c_private = port;
211d3bd5503SToomas Soome 		consoles[c++] = tty;
212*ecee58d9SToomas Soome 
213*ecee58d9SToomas Soome 		/* Reset terminal to initial normal settings with ESC [ 0 m */
214*ecee58d9SToomas Soome 		comc_putchar(tty, 0x1b);
215*ecee58d9SToomas Soome 		comc_putchar(tty, '[');
216*ecee58d9SToomas Soome 		comc_putchar(tty, '0');
217*ecee58d9SToomas Soome 		comc_putchar(tty, 'm');
218*ecee58d9SToomas Soome 		/* drain input from random data */
219*ecee58d9SToomas Soome 		while (comc_getchar(tty) != -1)
220*ecee58d9SToomas Soome 			;
221d3bd5503SToomas Soome 	}
222d3bd5503SToomas Soome 	consoles[c] = NULL;
22380e47917SToomas Soome }
22480e47917SToomas Soome 
225199767f8SToomas Soome static void
comc_probe(struct console * cp)226199767f8SToomas Soome comc_probe(struct console *cp)
227199767f8SToomas Soome {
228dbef1f18SToomas Soome 	struct serial *port;
229dbef1f18SToomas Soome 	char name[20];
230dbef1f18SToomas Soome 	char value[20];
231ceb3ef19SToomas Soome 	char *env;
232dbef1f18SToomas Soome 
233d3bd5503SToomas Soome 	port = cp->c_private;
234d3bd5503SToomas Soome 	if (port->speed != 0)
235dbef1f18SToomas Soome 		return;
236199767f8SToomas Soome 
237199767f8SToomas Soome 	port->speed = COMSPEED;
238199767f8SToomas Soome 
239199767f8SToomas Soome 	/*
240199767f8SToomas Soome 	 * Assume that the speed was set by an earlier boot loader if
241199767f8SToomas Soome 	 * comconsole is already the preferred console.
242199767f8SToomas Soome 	 */
24330c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-mode", cp->c_name);
244199767f8SToomas Soome 	env = getenv(name);
245199767f8SToomas Soome 	if (env != NULL) {
246ceb3ef19SToomas Soome 		port->speed = comc_getspeed(port->ioaddr);
247199767f8SToomas Soome 	}
24830c75cb0SToomas Soome 	env = comc_asprint_mode(port);
249199767f8SToomas Soome 
25030c75cb0SToomas Soome 	if (env != NULL) {
25130c75cb0SToomas Soome 		unsetenv(name);
25230c75cb0SToomas Soome 		env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
25330c75cb0SToomas Soome 		free(env);
25430c75cb0SToomas Soome 	}
255199767f8SToomas Soome 
25630c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
257199767f8SToomas Soome 	env = getenv(name);
258199767f8SToomas Soome 	if (env != NULL) {
259199767f8SToomas Soome 		if (strcmp(env, "true") == 0)
260199767f8SToomas Soome 			port->ignore_cd = 1;
261199767f8SToomas Soome 		else if (strcmp(env, "false") == 0)
262199767f8SToomas Soome 			port->ignore_cd = 0;
263199767f8SToomas Soome 	}
264199767f8SToomas Soome 
26530c75cb0SToomas Soome 	snprintf(value, sizeof (value), "%s",
26630c75cb0SToomas Soome 	    port->ignore_cd? "true" : "false");
267199767f8SToomas Soome 	unsetenv(name);
268199767f8SToomas Soome 	env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
269199767f8SToomas Soome 
27030c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
271199767f8SToomas Soome 	env = getenv(name);
272199767f8SToomas Soome 	if (env != NULL) {
273199767f8SToomas Soome 		if (strcmp(env, "true") == 0)
274199767f8SToomas Soome 			port->rtsdtr_off = 1;
275199767f8SToomas Soome 		else if (strcmp(env, "false") == 0)
276199767f8SToomas Soome 			port->rtsdtr_off = 0;
277199767f8SToomas Soome 	}
278199767f8SToomas Soome 
27930c75cb0SToomas Soome 	snprintf(value, sizeof (value), "%s",
28030c75cb0SToomas Soome 	    port->rtsdtr_off? "true" : "false");
281199767f8SToomas Soome 	unsetenv(name);
282199767f8SToomas Soome 	env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
283199767f8SToomas Soome 
28430c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-pcidev", cp->c_name);
285199767f8SToomas Soome 	env = getenv(name);
286199767f8SToomas Soome 	if (env != NULL) {
2876e4a3393SToomas Soome 		port->locator = comc_parse_pcidev(env);
2886e4a3393SToomas Soome 		if (port->locator != 0)
2896e4a3393SToomas Soome 			comc_pcidev_handle(cp, port->locator);
290199767f8SToomas Soome 	}
291199767f8SToomas Soome 
292199767f8SToomas Soome 	unsetenv(name);
293199767f8SToomas Soome 	env_setenv(name, EV_VOLATILE, env, comc_pcidev_set, env_nounset);
294fec53dd4SToomas Soome 
295fec53dd4SToomas Soome 	cp->c_flags = 0;
296fec53dd4SToomas Soome 	if (comc_setup(cp))
297fec53dd4SToomas Soome 		cp->c_flags = C_PRESENTIN | C_PRESENTOUT;
298199767f8SToomas Soome }
299199767f8SToomas Soome 
300199767f8SToomas Soome static int
comc_init(struct console * cp,int arg __attribute ((unused)))301199767f8SToomas Soome comc_init(struct console *cp, int arg __attribute((unused)))
302199767f8SToomas Soome {
303199767f8SToomas Soome 
304fec53dd4SToomas Soome 	if (comc_setup(cp))
3056e4a3393SToomas Soome 		return (CMD_OK);
306fec53dd4SToomas Soome 
307fec53dd4SToomas Soome 	cp->c_flags = 0;
3086e4a3393SToomas Soome 	return (CMD_ERROR);
309199767f8SToomas Soome }
310199767f8SToomas Soome 
311199767f8SToomas Soome static void
comc_putchar(struct console * cp,int c)312199767f8SToomas Soome comc_putchar(struct console *cp, int c)
313199767f8SToomas Soome {
3146e4a3393SToomas Soome 	int wait;
3156e4a3393SToomas Soome 	struct serial *sp = cp->c_private;
316199767f8SToomas Soome 
3176e4a3393SToomas Soome 	for (wait = COMC_TXWAIT; wait > 0; wait--)
3186e4a3393SToomas Soome 		if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) {
3196e4a3393SToomas Soome 			outb(sp->ioaddr + com_data, (uchar_t)c);
3206e4a3393SToomas Soome 			break;
3216e4a3393SToomas Soome 		}
322199767f8SToomas Soome }
323199767f8SToomas Soome 
324199767f8SToomas Soome static int
comc_getchar(struct console * cp)325199767f8SToomas Soome comc_getchar(struct console *cp)
326199767f8SToomas Soome {
3276e4a3393SToomas Soome 	struct serial *sp = cp->c_private;
3286e4a3393SToomas Soome 	return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1);
329199767f8SToomas Soome }
330199767f8SToomas Soome 
331199767f8SToomas Soome static int
comc_ischar(struct console * cp)332199767f8SToomas Soome comc_ischar(struct console *cp)
333199767f8SToomas Soome {
3346e4a3393SToomas Soome 	struct serial *sp = cp->c_private;
3356e4a3393SToomas Soome 	return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY);
336199767f8SToomas Soome }
337199767f8SToomas Soome 
3389890ff83SToomas Soome static int
comc_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)3399890ff83SToomas Soome comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused)
3409890ff83SToomas Soome {
3419890ff83SToomas Soome 	return (ENOTTY);
3429890ff83SToomas Soome }
3439890ff83SToomas Soome 
344199767f8SToomas Soome static char *
comc_asprint_mode(struct serial * sp)34530c75cb0SToomas Soome comc_asprint_mode(struct serial *sp)
346199767f8SToomas Soome {
34730c75cb0SToomas Soome 	char par, *buf;
34830c75cb0SToomas Soome 
34930c75cb0SToomas Soome 	if (sp == NULL)
35030c75cb0SToomas Soome 		return (NULL);
351199767f8SToomas Soome 
352199767f8SToomas Soome 	if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN))
353199767f8SToomas Soome 		par = 'e';
354199767f8SToomas Soome 	else if ((sp->lcr & PAREN) == PAREN)
355199767f8SToomas Soome 		par = 'o';
356199767f8SToomas Soome 	else
357199767f8SToomas Soome 		par = 'n';
358199767f8SToomas Soome 
35930c75cb0SToomas Soome 	asprintf(&buf, "%d,%d,%c,%d,-", sp->speed,
360199767f8SToomas Soome 	    (sp->lcr & BITS8) == BITS8? 8:7,
36130c75cb0SToomas Soome 	    par, (sp->lcr & STOP2) == STOP2? 2:1);
362199767f8SToomas Soome 	return (buf);
363199767f8SToomas Soome }
364199767f8SToomas Soome 
365199767f8SToomas Soome static int
comc_parse_mode(struct serial * sp,const char * value)366199767f8SToomas Soome comc_parse_mode(struct serial *sp, const char *value)
367199767f8SToomas Soome {
36830c75cb0SToomas Soome 	unsigned long n;
369199767f8SToomas Soome 	int speed;
370199767f8SToomas Soome 	int lcr;
371199767f8SToomas Soome 	char *ep;
372199767f8SToomas Soome 
37330c75cb0SToomas Soome 	if (value == NULL || *value == '\0')
374199767f8SToomas Soome 		return (CMD_ERROR);
375199767f8SToomas Soome 
37630c75cb0SToomas Soome 	errno = 0;
37730c75cb0SToomas Soome 	n = strtoul(value, &ep, 10);
37830c75cb0SToomas Soome 	if (errno != 0 || *ep != ',')
37930c75cb0SToomas Soome 		return (CMD_ERROR);
38030c75cb0SToomas Soome 	speed = n;
38130c75cb0SToomas Soome 
38230c75cb0SToomas Soome 	ep++;
38330c75cb0SToomas Soome 	errno = 0;
38430c75cb0SToomas Soome 	n = strtoul(ep, &ep, 10);
38530c75cb0SToomas Soome 	if (errno != 0 || *ep != ',')
386199767f8SToomas Soome 		return (CMD_ERROR);
387199767f8SToomas Soome 
388199767f8SToomas Soome 	switch (n) {
389199767f8SToomas Soome 	case 7: lcr = BITS7;
390199767f8SToomas Soome 		break;
391199767f8SToomas Soome 	case 8: lcr = BITS8;
392199767f8SToomas Soome 		break;
393199767f8SToomas Soome 	default:
394199767f8SToomas Soome 		return (CMD_ERROR);
395199767f8SToomas Soome 	}
396199767f8SToomas Soome 
39730c75cb0SToomas Soome 	ep++;
398199767f8SToomas Soome 	switch (*ep++) {
399199767f8SToomas Soome 	case 'n':
400199767f8SToomas Soome 		break;
401199767f8SToomas Soome 	case 'e': lcr |= PAREN|PAREVN;
402199767f8SToomas Soome 		break;
403199767f8SToomas Soome 	case 'o': lcr |= PAREN|PARODD;
404199767f8SToomas Soome 		break;
405199767f8SToomas Soome 	default:
406199767f8SToomas Soome 		return (CMD_ERROR);
407199767f8SToomas Soome 	}
408199767f8SToomas Soome 
409199767f8SToomas Soome 	if (*ep == ',')
410199767f8SToomas Soome 		ep++;
411199767f8SToomas Soome 	else
412199767f8SToomas Soome 		return (CMD_ERROR);
413199767f8SToomas Soome 
414199767f8SToomas Soome 	switch (*ep++) {
415199767f8SToomas Soome 	case '1':
416199767f8SToomas Soome 		break;
417199767f8SToomas Soome 	case '2': lcr |= STOP2;
418199767f8SToomas Soome 		break;
419199767f8SToomas Soome 	default:
420199767f8SToomas Soome 		return (CMD_ERROR);
421199767f8SToomas Soome 	}
422199767f8SToomas Soome 
423199767f8SToomas Soome 	/* handshake is ignored, but we check syntax anyhow */
424199767f8SToomas Soome 	if (*ep == ',')
425199767f8SToomas Soome 		ep++;
426199767f8SToomas Soome 	else
427199767f8SToomas Soome 		return (CMD_ERROR);
428199767f8SToomas Soome 
429199767f8SToomas Soome 	switch (*ep++) {
430199767f8SToomas Soome 	case '-':
431199767f8SToomas Soome 	case 'h':
432199767f8SToomas Soome 	case 's':
433199767f8SToomas Soome 		break;
434199767f8SToomas Soome 	default:
435199767f8SToomas Soome 		return (CMD_ERROR);
436199767f8SToomas Soome 	}
437199767f8SToomas Soome 
438199767f8SToomas Soome 	if (*ep != '\0')
439199767f8SToomas Soome 		return (CMD_ERROR);
440199767f8SToomas Soome 
441199767f8SToomas Soome 	sp->speed = speed;
442199767f8SToomas Soome 	sp->lcr = lcr;
443199767f8SToomas Soome 	return (CMD_OK);
444199767f8SToomas Soome }
445199767f8SToomas Soome 
446199767f8SToomas Soome static struct console *
get_console(const char * name)447d3bd5503SToomas Soome get_console(const char *name)
448199767f8SToomas Soome {
449d3bd5503SToomas Soome 	char port[5];
450199767f8SToomas Soome 
451d3bd5503SToomas Soome 	(void) strlcpy(port, name, sizeof (port));
452d3bd5503SToomas Soome 	for (uint_t i = 0; consoles[i] != NULL; i++) {
453d3bd5503SToomas Soome 		if (strcmp(port, consoles[i]->c_name) == 0)
454d3bd5503SToomas Soome 			return (consoles[i]);
455199767f8SToomas Soome 	}
456d3bd5503SToomas Soome 
457d3bd5503SToomas Soome 	printf("No such port: %s\n", port);
458d3bd5503SToomas Soome 	return (NULL);
459199767f8SToomas Soome }
460199767f8SToomas Soome 
461d3bd5503SToomas Soome /*
462d3bd5503SToomas Soome  * CMD_ERROR will cause set/setenv/setprop command to fail,
463d3bd5503SToomas Soome  * when used in loader scripts (forth), this will cause processing
464d3bd5503SToomas Soome  * of boot scripts to fail, rendering bootloading impossible.
465d3bd5503SToomas Soome  * To prevent such unfortunate situation, we return CMD_OK when
466d3bd5503SToomas Soome  * there is no such port, or there is invalid value in mode line.
467d3bd5503SToomas Soome  */
468199767f8SToomas Soome static int
comc_mode_set(struct env_var * ev,int flags,const void * value)469199767f8SToomas Soome comc_mode_set(struct env_var *ev, int flags, const void *value)
470199767f8SToomas Soome {
4716e4a3393SToomas Soome 	struct console *cp;
472ceb3ef19SToomas Soome 	char name[15];
473199767f8SToomas Soome 
4746e4a3393SToomas Soome 	if (value == NULL)
4756e4a3393SToomas Soome 		return (CMD_ERROR);
476199767f8SToomas Soome 
4776e4a3393SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
478d3bd5503SToomas Soome 		return (CMD_OK);
479199767f8SToomas Soome 
480ceb3ef19SToomas Soome 	/* Do not override serial setup from SPCR */
481ceb3ef19SToomas Soome 	snprintf(name, sizeof (name), "%s-spcr-mode", cp->c_name);
482ceb3ef19SToomas Soome 	if (getenv(name) == NULL) {
483d3bd5503SToomas Soome 		if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) {
484d3bd5503SToomas Soome 			printf("%s: invalid mode: %s\n", ev->ev_name,
485d3bd5503SToomas Soome 			    (char *)value);
486d3bd5503SToomas Soome 			return (CMD_OK);
487d3bd5503SToomas Soome 		}
488ceb3ef19SToomas Soome 		(void) comc_setup(cp);
489ceb3ef19SToomas Soome 		env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
490ceb3ef19SToomas Soome 	}
491199767f8SToomas Soome 
4926e4a3393SToomas Soome 	return (CMD_OK);
493199767f8SToomas Soome }
494199767f8SToomas Soome 
495d3bd5503SToomas Soome /*
496d3bd5503SToomas Soome  * CMD_ERROR will cause set/setenv/setprop command to fail,
497d3bd5503SToomas Soome  * when used in loader scripts (forth), this will cause processing
498d3bd5503SToomas Soome  * of boot scripts to fail, rendering bootloading impossible.
499d3bd5503SToomas Soome  * To prevent such unfortunate situation, we return CMD_OK when
500d3bd5503SToomas Soome  * there is no such port or invalid value was used.
501d3bd5503SToomas Soome  */
502199767f8SToomas Soome static int
comc_cd_set(struct env_var * ev,int flags,const void * value)503199767f8SToomas Soome comc_cd_set(struct env_var *ev, int flags, const void *value)
504199767f8SToomas Soome {
5056e4a3393SToomas Soome 	struct console *cp;
5066e4a3393SToomas Soome 	struct serial *sp;
507199767f8SToomas Soome 
5086e4a3393SToomas Soome 	if (value == NULL)
5096e4a3393SToomas Soome 		return (CMD_ERROR);
510199767f8SToomas Soome 
5116e4a3393SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
512d3bd5503SToomas Soome 		return (CMD_OK);
513199767f8SToomas Soome 
5146e4a3393SToomas Soome 	sp = cp->c_private;
515d3bd5503SToomas Soome 	if (strcmp(value, "true") == 0) {
5166e4a3393SToomas Soome 		sp->ignore_cd = 1;
517d3bd5503SToomas Soome 	} else if (strcmp(value, "false") == 0) {
5186e4a3393SToomas Soome 		sp->ignore_cd = 0;
519d3bd5503SToomas Soome 	} else {
520d3bd5503SToomas Soome 		printf("%s: invalid value: %s\n", ev->ev_name,
521d3bd5503SToomas Soome 		    (char *)value);
5226e4a3393SToomas Soome 		return (CMD_ERROR);
523d3bd5503SToomas Soome 	}
524199767f8SToomas Soome 
525fec53dd4SToomas Soome 	(void) comc_setup(cp);
526199767f8SToomas Soome 
5276e4a3393SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
528199767f8SToomas Soome 
5296e4a3393SToomas Soome 	return (CMD_OK);
530199767f8SToomas Soome }
531199767f8SToomas Soome 
532d3bd5503SToomas Soome /*
533d3bd5503SToomas Soome  * CMD_ERROR will cause set/setenv/setprop command to fail,
534d3bd5503SToomas Soome  * when used in loader scripts (forth), this will cause processing
535d3bd5503SToomas Soome  * of boot scripts to fail, rendering bootloading impossible.
536d3bd5503SToomas Soome  * To prevent such unfortunate situation, we return CMD_OK when
537d3bd5503SToomas Soome  * there is no such port, or invalid value was used.
538d3bd5503SToomas Soome  */
539199767f8SToomas Soome static int
comc_rtsdtr_set(struct env_var * ev,int flags,const void * value)540199767f8SToomas Soome comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
541199767f8SToomas Soome {
5426e4a3393SToomas Soome 	struct console *cp;
5436e4a3393SToomas Soome 	struct serial *sp;
544199767f8SToomas Soome 
5456e4a3393SToomas Soome 	if (value == NULL)
5466e4a3393SToomas Soome 		return (CMD_ERROR);
547199767f8SToomas Soome 
5486e4a3393SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
549d3bd5503SToomas Soome 		return (CMD_OK);
550199767f8SToomas Soome 
5516e4a3393SToomas Soome 	sp = cp->c_private;
552d3bd5503SToomas Soome 	if (strcmp(value, "true") == 0) {
5536e4a3393SToomas Soome 		sp->rtsdtr_off = 1;
554d3bd5503SToomas Soome 	} else if (strcmp(value, "false") == 0) {
5556e4a3393SToomas Soome 		sp->rtsdtr_off = 0;
556d3bd5503SToomas Soome 	} else {
557d3bd5503SToomas Soome 		printf("%s: invalid value: %s\n", ev->ev_name,
558d3bd5503SToomas Soome 		    (char *)value);
5596e4a3393SToomas Soome 		return (CMD_ERROR);
560d3bd5503SToomas Soome 	}
561199767f8SToomas Soome 
562fec53dd4SToomas Soome 	(void) comc_setup(cp);
563199767f8SToomas Soome 
5646e4a3393SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
565199767f8SToomas Soome 
5666e4a3393SToomas Soome 	return (CMD_OK);
567199767f8SToomas Soome }
568199767f8SToomas Soome 
569199767f8SToomas Soome /*
570199767f8SToomas Soome  * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
571199767f8SToomas Soome  * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
572199767f8SToomas Soome  */
573199767f8SToomas Soome static uint32_t
comc_parse_pcidev(const char * string)574199767f8SToomas Soome comc_parse_pcidev(const char *string)
575199767f8SToomas Soome {
5765ac07b12SToomas Soome #ifdef EFI
5776e4a3393SToomas Soome 	(void) string;
578199767f8SToomas Soome 	return (0);
579199767f8SToomas Soome #else
580199767f8SToomas Soome 	char *p, *p1;
581199767f8SToomas Soome 	uint8_t bus, dev, func, bar;
582199767f8SToomas Soome 	uint32_t locator;
583199767f8SToomas Soome 	int pres;
584199767f8SToomas Soome 
58530c75cb0SToomas Soome 	errno = 0;
58630c75cb0SToomas Soome 	pres = strtoul(string, &p, 10);
5876e4a3393SToomas Soome 	if (errno != 0 || p == string || *p != ':' || pres < 0)
588199767f8SToomas Soome 		return (0);
589199767f8SToomas Soome 	bus = pres;
590199767f8SToomas Soome 	p1 = ++p;
591199767f8SToomas Soome 
59230c75cb0SToomas Soome 	pres = strtoul(p1, &p, 10);
5936e4a3393SToomas Soome 	if (errno != 0 || p == string || *p != ':' || pres < 0)
594199767f8SToomas Soome 		return (0);
595199767f8SToomas Soome 	dev = pres;
596199767f8SToomas Soome 	p1 = ++p;
597199767f8SToomas Soome 
59830c75cb0SToomas Soome 	pres = strtoul(p1, &p, 10);
5996e4a3393SToomas Soome 	if (errno != 0 || p == string || (*p != ':' && *p != '\0') || pres < 0)
600199767f8SToomas Soome 		return (0);
601199767f8SToomas Soome 	func = pres;
602199767f8SToomas Soome 
603199767f8SToomas Soome 	if (*p == ':') {
604199767f8SToomas Soome 		p1 = ++p;
60530c75cb0SToomas Soome 		pres = strtoul(p1, &p, 10);
6066e4a3393SToomas Soome 		if (errno != 0 || p == string || *p != '\0' || pres <= 0)
607199767f8SToomas Soome 			return (0);
608199767f8SToomas Soome 		bar = pres;
609199767f8SToomas Soome 	} else
610199767f8SToomas Soome 		bar = 0x10;
611199767f8SToomas Soome 
612199767f8SToomas Soome 	locator = (bar << 16) | biospci_locator(bus, dev, func);
613199767f8SToomas Soome 	return (locator);
614199767f8SToomas Soome #endif
615199767f8SToomas Soome }
616199767f8SToomas Soome 
617199767f8SToomas Soome static int
comc_pcidev_handle(struct console * cp,uint32_t locator)618199767f8SToomas Soome comc_pcidev_handle(struct console *cp, uint32_t locator)
619199767f8SToomas Soome {
6205ac07b12SToomas Soome #ifdef EFI
6216e4a3393SToomas Soome 	(void) cp;
6226e4a3393SToomas Soome 	(void) locator;
623199767f8SToomas Soome 	return (CMD_ERROR);
624199767f8SToomas Soome #else
62530c75cb0SToomas Soome 	struct serial *sp = cp->c_private;
626199767f8SToomas Soome 	uint32_t port;
627199767f8SToomas Soome 
628199767f8SToomas Soome 	if (biospci_read_config(locator & 0xffff,
6296e4a3393SToomas Soome 	    (locator & 0xff0000) >> 16, 2, &port) == -1) {
630199767f8SToomas Soome 		printf("Cannot read bar at 0x%x\n", locator);
631199767f8SToomas Soome 		return (CMD_ERROR);
632199767f8SToomas Soome 	}
633199767f8SToomas Soome 	if (!PCI_BAR_IO(port)) {
634199767f8SToomas Soome 		printf("Memory bar at 0x%x\n", locator);
635199767f8SToomas Soome 		return (CMD_ERROR);
636199767f8SToomas Soome 	}
6376e4a3393SToomas Soome 	port &= PCIM_BAR_IO_BASE;
638199767f8SToomas Soome 
639fec53dd4SToomas Soome 	(void) comc_setup(cp);
640fec53dd4SToomas Soome 
641199767f8SToomas Soome 	sp->locator = locator;
642199767f8SToomas Soome 
643199767f8SToomas Soome 	return (CMD_OK);
644199767f8SToomas Soome #endif
645199767f8SToomas Soome }
646199767f8SToomas Soome 
647199767f8SToomas Soome static int
comc_pcidev_set(struct env_var * ev,int flags,const void * value)648199767f8SToomas Soome comc_pcidev_set(struct env_var *ev, int flags, const void *value)
649199767f8SToomas Soome {
650199767f8SToomas Soome 	struct console *cp;
651199767f8SToomas Soome 	struct serial *sp;
652199767f8SToomas Soome 	uint32_t locator;
653199767f8SToomas Soome 	int error;
654199767f8SToomas Soome 
655199767f8SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
656199767f8SToomas Soome 		return (CMD_ERROR);
65730c75cb0SToomas Soome 	sp = cp->c_private;
658199767f8SToomas Soome 
659199767f8SToomas Soome 	if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
660199767f8SToomas Soome 		printf("Invalid pcidev\n");
661199767f8SToomas Soome 		return (CMD_ERROR);
662199767f8SToomas Soome 	}
663199767f8SToomas Soome 	if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
664199767f8SToomas Soome 	    sp->locator != locator) {
665199767f8SToomas Soome 		error = comc_pcidev_handle(cp, locator);
666199767f8SToomas Soome 		if (error != CMD_OK)
667199767f8SToomas Soome 			return (error);
668199767f8SToomas Soome 	}
669199767f8SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
670199767f8SToomas Soome 	return (CMD_OK);
671199767f8SToomas Soome }
672199767f8SToomas Soome 
673fec53dd4SToomas Soome /*
674fec53dd4SToomas Soome  * In case of error, we also reset ACTIVE flags, so the console
675fec53dd4SToomas Soome  * framefork will try alternate consoles.
676fec53dd4SToomas Soome  */
677fec53dd4SToomas Soome static bool
comc_setup(struct console * cp)678199767f8SToomas Soome comc_setup(struct console *cp)
679199767f8SToomas Soome {
6806e4a3393SToomas Soome 	struct serial *sp = cp->c_private;
6816e4a3393SToomas Soome 	static int TRY_COUNT = 1000000;
6826e4a3393SToomas Soome 	int tries;
6836e4a3393SToomas Soome 
6846e4a3393SToomas Soome 	outb(sp->ioaddr + com_cfcr, CFCR_DLAB | sp->lcr);
6856e4a3393SToomas Soome 	outb(sp->ioaddr + com_dlbl, COMC_BPS(sp->speed) & 0xff);
6866e4a3393SToomas Soome 	outb(sp->ioaddr + com_dlbh, COMC_BPS(sp->speed) >> 8);
6876e4a3393SToomas Soome 	outb(sp->ioaddr + com_cfcr, sp->lcr);
6886e4a3393SToomas Soome 	outb(sp->ioaddr + com_mcr,
6896e4a3393SToomas Soome 	    sp->rtsdtr_off? ~(MCR_RTS | MCR_DTR) : MCR_RTS | MCR_DTR);
6906e4a3393SToomas Soome 
6916e4a3393SToomas Soome 	tries = 0;
6926e4a3393SToomas Soome 	do {
6936e4a3393SToomas Soome 		inb(sp->ioaddr + com_data);
6946e4a3393SToomas Soome 	} while (inb(sp->ioaddr + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
6956e4a3393SToomas Soome 
696fec53dd4SToomas Soome 	if (tries == TRY_COUNT)
697fec53dd4SToomas Soome 		return (false);
698fec53dd4SToomas Soome 	/* Mark this port usable. */
699fec53dd4SToomas Soome 	cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
700fec53dd4SToomas Soome 	return (true);
701199767f8SToomas Soome }
702199767f8SToomas Soome 
703ceb3ef19SToomas Soome int
comc_getspeed(int ioaddr)704ceb3ef19SToomas Soome comc_getspeed(int ioaddr)
705199767f8SToomas Soome {
7066e4a3393SToomas Soome 	uint_t	divisor;
7076e4a3393SToomas Soome 	uchar_t	dlbh;
7086e4a3393SToomas Soome 	uchar_t	dlbl;
7096e4a3393SToomas Soome 	uchar_t	cfcr;
710199767f8SToomas Soome 
711ceb3ef19SToomas Soome 	cfcr = inb(ioaddr + com_cfcr);
712ceb3ef19SToomas Soome 	outb(ioaddr + com_cfcr, CFCR_DLAB | cfcr);
713199767f8SToomas Soome 
714ceb3ef19SToomas Soome 	dlbl = inb(ioaddr + com_dlbl);
715ceb3ef19SToomas Soome 	dlbh = inb(ioaddr + com_dlbh);
716199767f8SToomas Soome 
717ceb3ef19SToomas Soome 	outb(ioaddr + com_cfcr, cfcr);
718199767f8SToomas Soome 
719199767f8SToomas Soome 	divisor = dlbh << 8 | dlbl;
720199767f8SToomas Soome 
721199767f8SToomas Soome 	/* XXX there should be more sanity checking. */
722199767f8SToomas Soome 	if (divisor == 0)
723199767f8SToomas Soome 		return (COMSPEED);
724199767f8SToomas Soome 	return (COMC_DIV2BPS(divisor));
725199767f8SToomas Soome }
726