122028508SToomas Soome /*
222028508SToomas Soome  * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
322028508SToomas Soome  *
422028508SToomas Soome  * Redistribution and use in source and binary forms, with or without
522028508SToomas Soome  * modification, are permitted provided that the following conditions
622028508SToomas Soome  * are met:
722028508SToomas Soome  * 1. Redistributions of source code must retain the above copyright
822028508SToomas Soome  *    notice, this list of conditions and the following disclaimer.
922028508SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
1022028508SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
1122028508SToomas Soome  *    documentation and/or other materials provided with the distribution.
1222028508SToomas Soome  *
1322028508SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1422028508SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1522028508SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1622028508SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1722028508SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1822028508SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1922028508SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2022028508SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2122028508SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2222028508SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2322028508SToomas Soome  * SUCH DAMAGE.
2422028508SToomas Soome  */
2522028508SToomas Soome 
2622028508SToomas Soome /*
2722028508SToomas Soome  * We do not use this implementation with x86 till we can fix two issues:
2822028508SToomas Soome  * 1. Reliably identify the serial ports in correct order.
2922028508SToomas Soome  * 2. Ensure we get properly working reads from serial io.
3022028508SToomas Soome  */
3122028508SToomas Soome 
3222028508SToomas Soome #include <sys/cdefs.h>
3322028508SToomas Soome 
3422028508SToomas Soome #include <stand.h>
3522028508SToomas Soome #include <sys/errno.h>
3622028508SToomas Soome #include <bootstrap.h>
3722028508SToomas Soome #include <stdbool.h>
3822028508SToomas Soome 
3922028508SToomas Soome #include <efi.h>
4022028508SToomas Soome #include <efilib.h>
41*f334afcfSToomas Soome #include <Protocol/SerialIo.h>
4222028508SToomas Soome 
4322028508SToomas Soome #include "loader_efi.h"
4422028508SToomas Soome 
45*f334afcfSToomas Soome EFI_GUID gEfiSerialIoProtocolGuid = EFI_SERIAL_IO_PROTOCOL_GUID;
46*f334afcfSToomas Soome EFI_GUID gEfiSerialTerminalDeviceTypeGuid =
47*f334afcfSToomas Soome     EFI_SERIAL_TERMINAL_DEVICE_TYPE_GUID;
4822028508SToomas Soome 
4922028508SToomas Soome #define	COMC_TXWAIT	0x40000		/* transmit timeout */
5022028508SToomas Soome 
5122028508SToomas Soome #ifndef	COMSPEED
5222028508SToomas Soome #define	COMSPEED	9600
5322028508SToomas Soome #endif
5422028508SToomas Soome 
5522028508SToomas Soome #define	PNP0501		0x501		/* 16550A-compatible COM port */
5622028508SToomas Soome 
5722028508SToomas Soome struct serial {
5822028508SToomas Soome 	uint64_t	baudrate;
5922028508SToomas Soome 	uint8_t		databits;
6022028508SToomas Soome 	EFI_PARITY_TYPE	parity;
6122028508SToomas Soome 	EFI_STOP_BITS_TYPE stopbits;
6222028508SToomas Soome 	uint8_t		ignore_cd;	/* boolean */
6322028508SToomas Soome 	uint8_t		rtsdtr_off;	/* boolean */
6422028508SToomas Soome 	int		ioaddr;		/* index in handles array */
6522028508SToomas Soome 	SERIAL_IO_INTERFACE *sio;
6622028508SToomas Soome };
6722028508SToomas Soome 
6822028508SToomas Soome static void	comc_probe(struct console *);
6922028508SToomas Soome static int	comc_init(struct console *, int);
7022028508SToomas Soome static void	comc_putchar(struct console *, int);
7122028508SToomas Soome static int	comc_getchar(struct console *);
7222028508SToomas Soome static int	comc_ischar(struct console *);
7322028508SToomas Soome static int	comc_ioctl(struct console *, int, void *);
7422028508SToomas Soome static void	comc_devinfo(struct console *);
7522028508SToomas Soome static bool	comc_setup(struct console *);
7622028508SToomas Soome static char	*comc_asprint_mode(struct serial *);
7722028508SToomas Soome static int	comc_parse_mode(struct serial *, const char *);
7822028508SToomas Soome static int	comc_mode_set(struct env_var *, int, const void *);
7922028508SToomas Soome static int	comc_cd_set(struct env_var *, int, const void *);
8022028508SToomas Soome static int	comc_rtsdtr_set(struct env_var *, int, const void *);
8122028508SToomas Soome 
8222028508SToomas Soome struct console ttya = {
8322028508SToomas Soome 	.c_name = "ttya",
8422028508SToomas Soome 	.c_desc = "serial port a",
8522028508SToomas Soome 	.c_flags = 0,
8622028508SToomas Soome 	.c_probe = comc_probe,
8722028508SToomas Soome 	.c_init = comc_init,
8822028508SToomas Soome 	.c_out = comc_putchar,
8922028508SToomas Soome 	.c_in = comc_getchar,
9022028508SToomas Soome 	.c_ready = comc_ischar,
9122028508SToomas Soome 	.c_ioctl = comc_ioctl,
9222028508SToomas Soome 	.c_devinfo = comc_devinfo,
9322028508SToomas Soome 	.c_private = NULL
9422028508SToomas Soome };
9522028508SToomas Soome 
9622028508SToomas Soome struct console ttyb = {
9722028508SToomas Soome 	.c_name = "ttyb",
9822028508SToomas Soome 	.c_desc = "serial port b",
9922028508SToomas Soome 	.c_flags = 0,
10022028508SToomas Soome 	.c_probe = comc_probe,
10122028508SToomas Soome 	.c_init = comc_init,
10222028508SToomas Soome 	.c_out = comc_putchar,
10322028508SToomas Soome 	.c_in = comc_getchar,
10422028508SToomas Soome 	.c_ready = comc_ischar,
10522028508SToomas Soome 	.c_ioctl = comc_ioctl,
10622028508SToomas Soome 	.c_devinfo = comc_devinfo,
10722028508SToomas Soome 	.c_private = NULL
10822028508SToomas Soome };
10922028508SToomas Soome 
11022028508SToomas Soome struct console ttyc = {
11122028508SToomas Soome 	.c_name = "ttyc",
11222028508SToomas Soome 	.c_desc = "serial port c",
11322028508SToomas Soome 	.c_flags = 0,
11422028508SToomas Soome 	.c_probe = comc_probe,
11522028508SToomas Soome 	.c_init = comc_init,
11622028508SToomas Soome 	.c_out = comc_putchar,
11722028508SToomas Soome 	.c_in = comc_getchar,
11822028508SToomas Soome 	.c_ready = comc_ischar,
11922028508SToomas Soome 	.c_ioctl = comc_ioctl,
12022028508SToomas Soome 	.c_devinfo = comc_devinfo,
12122028508SToomas Soome 	.c_private = NULL
12222028508SToomas Soome };
12322028508SToomas Soome 
12422028508SToomas Soome struct console ttyd = {
12522028508SToomas Soome 	.c_name = "ttyd",
12622028508SToomas Soome 	.c_desc = "serial port d",
12722028508SToomas Soome 	.c_flags = 0,
12822028508SToomas Soome 	.c_probe = comc_probe,
12922028508SToomas Soome 	.c_init = comc_init,
13022028508SToomas Soome 	.c_out = comc_putchar,
13122028508SToomas Soome 	.c_in = comc_getchar,
13222028508SToomas Soome 	.c_ready = comc_ischar,
13322028508SToomas Soome 	.c_ioctl = comc_ioctl,
13422028508SToomas Soome 	.c_devinfo = comc_devinfo,
13522028508SToomas Soome 	.c_private = NULL
13622028508SToomas Soome };
13722028508SToomas Soome 
13822028508SToomas Soome /*
13922028508SToomas Soome  * Find serial device number from device path.
14022028508SToomas Soome  * Return -1 if not found.
14122028508SToomas Soome  */
14222028508SToomas Soome static int
efi_serial_get_index(EFI_DEVICE_PATH * devpath)14322028508SToomas Soome efi_serial_get_index(EFI_DEVICE_PATH *devpath)
14422028508SToomas Soome {
14522028508SToomas Soome 	ACPI_HID_DEVICE_PATH  *acpi;
14622028508SToomas Soome 
14722028508SToomas Soome 	while (!IsDevicePathEnd(devpath)) {
14822028508SToomas Soome 		if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
14922028508SToomas Soome 		    DevicePathSubType(devpath) == ACPI_DP) {
15022028508SToomas Soome 
15122028508SToomas Soome 			acpi = (ACPI_HID_DEVICE_PATH *)devpath;
15222028508SToomas Soome 			if (acpi->HID == EISA_PNP_ID(PNP0501)) {
15322028508SToomas Soome 				return (acpi->UID);
15422028508SToomas Soome 			}
15522028508SToomas Soome 		}
15622028508SToomas Soome 
15722028508SToomas Soome 		devpath = NextDevicePathNode(devpath);
15822028508SToomas Soome 	}
15922028508SToomas Soome 	return (-1);
16022028508SToomas Soome }
16122028508SToomas Soome 
16222028508SToomas Soome /*
16322028508SToomas Soome  * The order of handles from LocateHandle() is not known, we need to
16422028508SToomas Soome  * iterate handles, pick device path for handle, and check the device
16522028508SToomas Soome  * number.
16622028508SToomas Soome  */
16722028508SToomas Soome static EFI_HANDLE
efi_serial_get_handle(int port)16822028508SToomas Soome efi_serial_get_handle(int port)
16922028508SToomas Soome {
17022028508SToomas Soome 	EFI_STATUS status;
17122028508SToomas Soome 	EFI_HANDLE *handles, handle;
17222028508SToomas Soome 	EFI_DEVICE_PATH *devpath;
17369764de3SToomas Soome 	uint_t index, nhandles;
17422028508SToomas Soome 
17522028508SToomas Soome 	if (port == -1)
17622028508SToomas Soome 		return (NULL);
17722028508SToomas Soome 
178*f334afcfSToomas Soome 	status = efi_get_protocol_handles(&gEfiSerialIoProtocolGuid,
179*f334afcfSToomas Soome 	    &nhandles, &handles);
18022028508SToomas Soome 	if (EFI_ERROR(status))
18122028508SToomas Soome 		return (NULL);
18222028508SToomas Soome 
18322028508SToomas Soome 	handle = NULL;
18422028508SToomas Soome 	for (index = 0; index < nhandles; index++) {
18522028508SToomas Soome 		devpath = efi_lookup_devpath(handles[index]);
18622028508SToomas Soome 		if (port == efi_serial_get_index(devpath)) {
18722028508SToomas Soome 			handle = (handles[index]);
18822028508SToomas Soome 			break;
18922028508SToomas Soome 		}
19022028508SToomas Soome 	}
19122028508SToomas Soome 
19222028508SToomas Soome 	/*
19322028508SToomas Soome 	 * In case we did fail to identify the device by path, use port as
19422028508SToomas Soome 	 * array index. Note, we did check port == -1 above.
19522028508SToomas Soome 	 */
19622028508SToomas Soome 	if (port < nhandles && handle == NULL)
19722028508SToomas Soome 		handle = handles[port];
19822028508SToomas Soome 
19922028508SToomas Soome 	free(handles);
20022028508SToomas Soome 	return (handle);
20122028508SToomas Soome }
20222028508SToomas Soome 
20322028508SToomas Soome static void
comc_probe(struct console * cp)20422028508SToomas Soome comc_probe(struct console *cp)
20522028508SToomas Soome {
20622028508SToomas Soome 	EFI_STATUS status;
20722028508SToomas Soome 	EFI_HANDLE handle;
20822028508SToomas Soome 	struct serial *port;
20922028508SToomas Soome 	char name[20];
21022028508SToomas Soome 	char value[20];
21122028508SToomas Soome 	char *env;
21222028508SToomas Soome 
21322028508SToomas Soome 	/* are we already set up? */
21422028508SToomas Soome 	if (cp->c_private != NULL)
21522028508SToomas Soome 		return;
21622028508SToomas Soome 
21722028508SToomas Soome 	cp->c_private = malloc(sizeof (struct serial));
21822028508SToomas Soome 	port = cp->c_private;
21922028508SToomas Soome 	port->baudrate = COMSPEED;
22022028508SToomas Soome 
22122028508SToomas Soome 	port->ioaddr = -1;	/* invalid port */
22222028508SToomas Soome 	if (strcmp(cp->c_name, "ttya") == 0)
22322028508SToomas Soome 		port->ioaddr = 0;
22422028508SToomas Soome 	else if (strcmp(cp->c_name, "ttyb") == 0)
22522028508SToomas Soome 		port->ioaddr = 1;
22622028508SToomas Soome 	else if (strcmp(cp->c_name, "ttyc") == 0)
22722028508SToomas Soome 		port->ioaddr = 2;
22822028508SToomas Soome 	else if (strcmp(cp->c_name, "ttyd") == 0)
22922028508SToomas Soome 		port->ioaddr = 3;
23022028508SToomas Soome 
23122028508SToomas Soome 	port->databits = 8;		/* 8,n,1 */
23222028508SToomas Soome 	port->parity = NoParity;	/* 8,n,1 */
23322028508SToomas Soome 	port->stopbits = OneStopBit;	/* 8,n,1 */
23422028508SToomas Soome 	port->ignore_cd = 1;		/* ignore cd */
23522028508SToomas Soome 	port->rtsdtr_off = 0;		/* rts-dtr is on */
23622028508SToomas Soome 	port->sio = NULL;
23722028508SToomas Soome 
23822028508SToomas Soome 	handle = efi_serial_get_handle(port->ioaddr);
23922028508SToomas Soome 
24022028508SToomas Soome 	if (handle != NULL) {
241*f334afcfSToomas Soome 		status = BS->OpenProtocol(handle, &gEfiSerialIoProtocolGuid,
24222028508SToomas Soome 		    (void**)&port->sio, IH, NULL,
24322028508SToomas Soome 		    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
24422028508SToomas Soome 
24522028508SToomas Soome 		if (EFI_ERROR(status))
24622028508SToomas Soome 			port->sio = NULL;
24722028508SToomas Soome 	}
24822028508SToomas Soome 
24922028508SToomas Soome 	snprintf(name, sizeof (name), "%s-mode", cp->c_name);
25022028508SToomas Soome 	env = getenv(name);
25122028508SToomas Soome 
25222028508SToomas Soome 	if (env != NULL)
25322028508SToomas Soome 		(void) comc_parse_mode(port, env);
25422028508SToomas Soome 
25522028508SToomas Soome 	env = comc_asprint_mode(port);
25622028508SToomas Soome 
25722028508SToomas Soome 	if (env != NULL) {
25822028508SToomas Soome 		unsetenv(name);
25922028508SToomas Soome 		env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
26022028508SToomas Soome 		free(env);
26122028508SToomas Soome 	}
26222028508SToomas Soome 
26322028508SToomas Soome 	snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
26422028508SToomas Soome 	env = getenv(name);
26522028508SToomas Soome 	if (env != NULL) {
26622028508SToomas Soome 		if (strcmp(env, "true") == 0)
26722028508SToomas Soome 			port->ignore_cd = 1;
26822028508SToomas Soome 		else if (strcmp(env, "false") == 0)
26922028508SToomas Soome 			port->ignore_cd = 0;
27022028508SToomas Soome 	}
27122028508SToomas Soome 
27222028508SToomas Soome 	snprintf(value, sizeof (value), "%s",
27322028508SToomas Soome 	    port->ignore_cd? "true" : "false");
27422028508SToomas Soome 	unsetenv(name);
27522028508SToomas Soome 	env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
27622028508SToomas Soome 
27722028508SToomas Soome 	snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
27822028508SToomas Soome 	env = getenv(name);
27922028508SToomas Soome 	if (env != NULL) {
28022028508SToomas Soome 		if (strcmp(env, "true") == 0)
28122028508SToomas Soome 			port->rtsdtr_off = 1;
28222028508SToomas Soome 		else if (strcmp(env, "false") == 0)
28322028508SToomas Soome 			port->rtsdtr_off = 0;
28422028508SToomas Soome 	}
28522028508SToomas Soome 
28622028508SToomas Soome 	snprintf(value, sizeof (value), "%s",
28722028508SToomas Soome 	    port->rtsdtr_off? "true" : "false");
28822028508SToomas Soome 	unsetenv(name);
28922028508SToomas Soome 	env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
29022028508SToomas Soome 
29122028508SToomas Soome 	cp->c_flags = 0;
29222028508SToomas Soome 	if (comc_setup(cp))
29322028508SToomas Soome 		cp->c_flags = C_PRESENTIN | C_PRESENTOUT;
29422028508SToomas Soome }
29522028508SToomas Soome 
29622028508SToomas Soome static int
comc_init(struct console * cp,int arg __attribute ((unused)))29722028508SToomas Soome comc_init(struct console *cp, int arg __attribute((unused)))
29822028508SToomas Soome {
29922028508SToomas Soome 
30022028508SToomas Soome 	if (comc_setup(cp))
30122028508SToomas Soome 		return (CMD_OK);
30222028508SToomas Soome 
30322028508SToomas Soome 	cp->c_flags = 0;
30422028508SToomas Soome 	return (CMD_ERROR);
30522028508SToomas Soome }
30622028508SToomas Soome 
30722028508SToomas Soome static void
comc_putchar(struct console * cp,int c)30822028508SToomas Soome comc_putchar(struct console *cp, int c)
30922028508SToomas Soome {
31022028508SToomas Soome 	int wait;
31122028508SToomas Soome 	EFI_STATUS status;
31222028508SToomas Soome 	UINTN bufsz = 1;
31322028508SToomas Soome 	char cb = c;
31422028508SToomas Soome 	struct serial *sp = cp->c_private;
31522028508SToomas Soome 
31622028508SToomas Soome 	if (sp->sio == NULL)
31722028508SToomas Soome 		return;
31822028508SToomas Soome 
31922028508SToomas Soome 	for (wait = COMC_TXWAIT; wait > 0; wait--) {
32022028508SToomas Soome 		status = sp->sio->Write(sp->sio, &bufsz, &cb);
32122028508SToomas Soome 		if (status != EFI_TIMEOUT)
32222028508SToomas Soome 			break;
32322028508SToomas Soome 	}
32422028508SToomas Soome }
32522028508SToomas Soome 
32622028508SToomas Soome static int
comc_getchar(struct console * cp)32722028508SToomas Soome comc_getchar(struct console *cp)
32822028508SToomas Soome {
32922028508SToomas Soome 	EFI_STATUS status;
33022028508SToomas Soome 	UINTN bufsz = 1;
33122028508SToomas Soome 	char c;
33222028508SToomas Soome 	struct serial *sp = cp->c_private;
33322028508SToomas Soome 
33422028508SToomas Soome 	if (sp->sio == NULL || !comc_ischar(cp))
33522028508SToomas Soome 		return (-1);
33622028508SToomas Soome 
33722028508SToomas Soome 	status = sp->sio->Read(sp->sio, &bufsz, &c);
33822028508SToomas Soome 	if (EFI_ERROR(status) || bufsz == 0)
33922028508SToomas Soome 		return (-1);
34022028508SToomas Soome 
34122028508SToomas Soome 	return (c);
34222028508SToomas Soome }
34322028508SToomas Soome 
34422028508SToomas Soome static int
comc_ischar(struct console * cp)34522028508SToomas Soome comc_ischar(struct console *cp)
34622028508SToomas Soome {
34722028508SToomas Soome 	EFI_STATUS status;
34822028508SToomas Soome 	uint32_t control;
34922028508SToomas Soome 	struct serial *sp = cp->c_private;
35022028508SToomas Soome 
35122028508SToomas Soome 	if (sp->sio == NULL)
35222028508SToomas Soome 		return (0);
35322028508SToomas Soome 
35422028508SToomas Soome 	status = sp->sio->GetControl(sp->sio, &control);
35522028508SToomas Soome 	if (EFI_ERROR(status))
35622028508SToomas Soome 		return (0);
35722028508SToomas Soome 
35822028508SToomas Soome 	return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
35922028508SToomas Soome }
36022028508SToomas Soome 
36122028508SToomas Soome static int
comc_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)36222028508SToomas Soome comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused)
36322028508SToomas Soome {
36422028508SToomas Soome 	return (ENOTTY);
36522028508SToomas Soome }
36622028508SToomas Soome 
36722028508SToomas Soome static void
comc_devinfo(struct console * cp)36822028508SToomas Soome comc_devinfo(struct console *cp)
36922028508SToomas Soome {
37022028508SToomas Soome 	struct serial *port = cp->c_private;
37122028508SToomas Soome 	EFI_HANDLE handle;
37222028508SToomas Soome 	EFI_DEVICE_PATH *dp;
37322028508SToomas Soome 	CHAR16 *text;
37422028508SToomas Soome 
37522028508SToomas Soome 	handle = efi_serial_get_handle(port->ioaddr);
37622028508SToomas Soome 	if (handle == NULL) {
37722028508SToomas Soome 		printf("\tdevice is not present");
37822028508SToomas Soome 		return;
37922028508SToomas Soome 	}
38022028508SToomas Soome 
38122028508SToomas Soome 	dp = efi_lookup_devpath(handle);
38222028508SToomas Soome 	if (dp == NULL)
38322028508SToomas Soome 		return;
38422028508SToomas Soome 
38522028508SToomas Soome 	text = efi_devpath_name(dp);
38622028508SToomas Soome 	if (text == NULL)
38722028508SToomas Soome 		return;
38822028508SToomas Soome 
38922028508SToomas Soome 	printf("\t%S", text);
39022028508SToomas Soome 	efi_free_devpath_name(text);
39122028508SToomas Soome }
39222028508SToomas Soome 
39322028508SToomas Soome static char *
comc_asprint_mode(struct serial * sp)39422028508SToomas Soome comc_asprint_mode(struct serial *sp)
39522028508SToomas Soome {
39622028508SToomas Soome 	char par, *buf;
39722028508SToomas Soome 	char *stop;
39822028508SToomas Soome 
39922028508SToomas Soome 	if (sp == NULL)
40022028508SToomas Soome 		return (NULL);
40122028508SToomas Soome 
40222028508SToomas Soome 	switch (sp->parity) {
40322028508SToomas Soome 	case NoParity:
40422028508SToomas Soome 		par = 'n';
40522028508SToomas Soome 		break;
40622028508SToomas Soome 	case EvenParity:
40722028508SToomas Soome 		par = 'e';
40822028508SToomas Soome 		break;
40922028508SToomas Soome 	case OddParity:
41022028508SToomas Soome 		par = 'o';
41122028508SToomas Soome 		break;
41222028508SToomas Soome 	case MarkParity:
41322028508SToomas Soome 		par = 'm';
41422028508SToomas Soome 		break;
41522028508SToomas Soome 	case SpaceParity:
41622028508SToomas Soome 		par = 's';
41722028508SToomas Soome 		break;
41822028508SToomas Soome 	default:
41922028508SToomas Soome 		par = 'n';
42022028508SToomas Soome 		break;
42122028508SToomas Soome 	}
42222028508SToomas Soome 
42322028508SToomas Soome 	switch (sp->stopbits) {
42422028508SToomas Soome 	case OneStopBit:
42522028508SToomas Soome 		stop = "1";
42622028508SToomas Soome 		break;
42722028508SToomas Soome 	case TwoStopBits:
42822028508SToomas Soome 		stop = "2";
42922028508SToomas Soome 		break;
43022028508SToomas Soome 	case OneFiveStopBits:
43122028508SToomas Soome 		stop = "1.5";
43222028508SToomas Soome 		break;
43322028508SToomas Soome 	default:
43422028508SToomas Soome 		stop = "1";
43522028508SToomas Soome 		break;
43622028508SToomas Soome 	}
43722028508SToomas Soome 
43822028508SToomas Soome 	asprintf(&buf, "%ju,%d,%c,%s,-", sp->baudrate, sp->databits, par, stop);
43922028508SToomas Soome 	return (buf);
44022028508SToomas Soome }
44122028508SToomas Soome 
44222028508SToomas Soome static int
comc_parse_mode(struct serial * sp,const char * value)44322028508SToomas Soome comc_parse_mode(struct serial *sp, const char *value)
44422028508SToomas Soome {
44522028508SToomas Soome 	unsigned long n;
44622028508SToomas Soome 	uint64_t baudrate;
44722028508SToomas Soome 	uint8_t databits = 8;
44822028508SToomas Soome 	int parity = NoParity;
44922028508SToomas Soome 	int stopbits = OneStopBit;
45022028508SToomas Soome 	char *ep;
45122028508SToomas Soome 
45222028508SToomas Soome 	if (value == NULL || *value == '\0')
45322028508SToomas Soome 		return (CMD_ERROR);
45422028508SToomas Soome 
45522028508SToomas Soome 	errno = 0;
45622028508SToomas Soome 	n = strtoul(value, &ep, 10);
45722028508SToomas Soome 	if (errno != 0 || *ep != ',')
45822028508SToomas Soome 		return (CMD_ERROR);
45922028508SToomas Soome 	baudrate = n;
46022028508SToomas Soome 
46122028508SToomas Soome 	ep++;
46222028508SToomas Soome 	n = strtoul(ep, &ep, 10);
46322028508SToomas Soome 	if (errno != 0 || *ep != ',')
46422028508SToomas Soome 		return (CMD_ERROR);
46522028508SToomas Soome 
46622028508SToomas Soome 	switch (n) {
46722028508SToomas Soome 	case 5: databits = 5;
46822028508SToomas Soome 		break;
46922028508SToomas Soome 	case 6: databits = 6;
47022028508SToomas Soome 		break;
47122028508SToomas Soome 	case 7: databits = 7;
47222028508SToomas Soome 		break;
47322028508SToomas Soome 	case 8: databits = 8;
47422028508SToomas Soome 		break;
47522028508SToomas Soome 	default:
47622028508SToomas Soome 		return (CMD_ERROR);
47722028508SToomas Soome 	}
47822028508SToomas Soome 
47922028508SToomas Soome 	ep++;
48022028508SToomas Soome 	switch (*ep++) {
48122028508SToomas Soome 	case 'n': parity = NoParity;
48222028508SToomas Soome 		break;
48322028508SToomas Soome 	case 'e': parity = EvenParity;
48422028508SToomas Soome 		break;
48522028508SToomas Soome 	case 'o': parity = OddParity;
48622028508SToomas Soome 		break;
48722028508SToomas Soome 	case 'm': parity = MarkParity;
48822028508SToomas Soome 		break;
48922028508SToomas Soome 	case 's': parity = SpaceParity;
49022028508SToomas Soome 		break;
49122028508SToomas Soome 	default:
49222028508SToomas Soome 		return (CMD_ERROR);
49322028508SToomas Soome 	}
49422028508SToomas Soome 
49522028508SToomas Soome 	if (*ep == ',')
49622028508SToomas Soome 		ep++;
49722028508SToomas Soome 	else
49822028508SToomas Soome 		return (CMD_ERROR);
49922028508SToomas Soome 
50022028508SToomas Soome 	switch (*ep++) {
50122028508SToomas Soome 	case '1': stopbits = OneStopBit;
50222028508SToomas Soome 		if (ep[0] == '.' && ep[1] == '5') {
50322028508SToomas Soome 			ep += 2;
50422028508SToomas Soome 			stopbits = OneFiveStopBits;
50522028508SToomas Soome 		}
50622028508SToomas Soome 		break;
50722028508SToomas Soome 	case '2': stopbits = TwoStopBits;
50822028508SToomas Soome 		break;
50922028508SToomas Soome 	default:
51022028508SToomas Soome 		return (CMD_ERROR);
51122028508SToomas Soome 	}
51222028508SToomas Soome 
51322028508SToomas Soome 	/* handshake is ignored, but we check syntax anyhow */
51422028508SToomas Soome 	if (*ep == ',')
51522028508SToomas Soome 		ep++;
51622028508SToomas Soome 	else
51722028508SToomas Soome 		return (CMD_ERROR);
51822028508SToomas Soome 
51922028508SToomas Soome 	switch (*ep++) {
52022028508SToomas Soome 	case '-':
52122028508SToomas Soome 	case 'h':
52222028508SToomas Soome 	case 's':
52322028508SToomas Soome 		break;
52422028508SToomas Soome 	default:
52522028508SToomas Soome 		return (CMD_ERROR);
52622028508SToomas Soome 	}
52722028508SToomas Soome 
52822028508SToomas Soome 	if (*ep != '\0')
52922028508SToomas Soome 		return (CMD_ERROR);
53022028508SToomas Soome 
53122028508SToomas Soome 	sp->baudrate = baudrate;
53222028508SToomas Soome 	sp->databits = databits;
53322028508SToomas Soome 	sp->parity = parity;
53422028508SToomas Soome 	sp->stopbits = stopbits;
53522028508SToomas Soome 	return (CMD_OK);
53622028508SToomas Soome }
53722028508SToomas Soome 
53822028508SToomas Soome static struct console *
get_console(char * name)53922028508SToomas Soome get_console(char *name)
54022028508SToomas Soome {
54122028508SToomas Soome 	struct console *cp = NULL;
54222028508SToomas Soome 
54322028508SToomas Soome 	switch (name[3]) {
54422028508SToomas Soome 	case 'a': cp = &ttya;
54522028508SToomas Soome 		break;
54622028508SToomas Soome 	case 'b': cp = &ttyb;
54722028508SToomas Soome 		break;
54822028508SToomas Soome 	case 'c': cp = &ttyc;
54922028508SToomas Soome 		break;
55022028508SToomas Soome 	case 'd': cp = &ttyd;
55122028508SToomas Soome 		break;
55222028508SToomas Soome 	}
55322028508SToomas Soome 	return (cp);
55422028508SToomas Soome }
55522028508SToomas Soome 
55622028508SToomas Soome static int
comc_mode_set(struct env_var * ev,int flags,const void * value)55722028508SToomas Soome comc_mode_set(struct env_var *ev, int flags, const void *value)
55822028508SToomas Soome {
55922028508SToomas Soome 	struct console *cp;
56022028508SToomas Soome 
56122028508SToomas Soome 	if (value == NULL)
56222028508SToomas Soome 		return (CMD_ERROR);
56322028508SToomas Soome 
56422028508SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
56522028508SToomas Soome 		return (CMD_ERROR);
56622028508SToomas Soome 
56722028508SToomas Soome 	if (comc_parse_mode(cp->c_private, value) == CMD_ERROR)
56822028508SToomas Soome 		return (CMD_ERROR);
56922028508SToomas Soome 
57022028508SToomas Soome 	(void) comc_setup(cp);
57122028508SToomas Soome 
57222028508SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
57322028508SToomas Soome 
57422028508SToomas Soome 	return (CMD_OK);
57522028508SToomas Soome }
57622028508SToomas Soome 
57722028508SToomas Soome static int
comc_cd_set(struct env_var * ev,int flags,const void * value)57822028508SToomas Soome comc_cd_set(struct env_var *ev, int flags, const void *value)
57922028508SToomas Soome {
58022028508SToomas Soome 	struct console *cp;
58122028508SToomas Soome 	struct serial *sp;
58222028508SToomas Soome 
58322028508SToomas Soome 	if (value == NULL)
58422028508SToomas Soome 		return (CMD_ERROR);
58522028508SToomas Soome 
58622028508SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
58722028508SToomas Soome 		return (CMD_ERROR);
58822028508SToomas Soome 
58922028508SToomas Soome 	sp = cp->c_private;
59022028508SToomas Soome 	if (strcmp(value, "true") == 0)
59122028508SToomas Soome 		sp->ignore_cd = 1;
59222028508SToomas Soome 	else if (strcmp(value, "false") == 0)
59322028508SToomas Soome 		sp->ignore_cd = 0;
59422028508SToomas Soome 	else
59522028508SToomas Soome 		return (CMD_ERROR);
59622028508SToomas Soome 
59722028508SToomas Soome 	(void) comc_setup(cp);
59822028508SToomas Soome 
59922028508SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
60022028508SToomas Soome 
60122028508SToomas Soome 	return (CMD_OK);
60222028508SToomas Soome }
60322028508SToomas Soome 
60422028508SToomas Soome static int
comc_rtsdtr_set(struct env_var * ev,int flags,const void * value)60522028508SToomas Soome comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
60622028508SToomas Soome {
60722028508SToomas Soome 	struct console *cp;
60822028508SToomas Soome 	struct serial *sp;
60922028508SToomas Soome 
61022028508SToomas Soome 	if (value == NULL)
61122028508SToomas Soome 		return (CMD_ERROR);
61222028508SToomas Soome 
61322028508SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
61422028508SToomas Soome 		return (CMD_ERROR);
61522028508SToomas Soome 
61622028508SToomas Soome 	sp = cp->c_private;
61722028508SToomas Soome 	if (strcmp(value, "true") == 0)
61822028508SToomas Soome 		sp->rtsdtr_off = 1;
61922028508SToomas Soome 	else if (strcmp(value, "false") == 0)
62022028508SToomas Soome 		sp->rtsdtr_off = 0;
62122028508SToomas Soome 	else
62222028508SToomas Soome 		return (CMD_ERROR);
62322028508SToomas Soome 
62422028508SToomas Soome 	(void) comc_setup(cp);
62522028508SToomas Soome 
62622028508SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
62722028508SToomas Soome 
62822028508SToomas Soome 	return (CMD_OK);
62922028508SToomas Soome }
63022028508SToomas Soome 
63122028508SToomas Soome /*
63222028508SToomas Soome  * In case of error, we also reset ACTIVE flags, so the console
63322028508SToomas Soome  * framefork will try alternate consoles.
63422028508SToomas Soome  */
63522028508SToomas Soome static bool
comc_setup(struct console * cp)63622028508SToomas Soome comc_setup(struct console *cp)
63722028508SToomas Soome {
63822028508SToomas Soome 	EFI_STATUS status;
63922028508SToomas Soome 	UINT32 control;
64022028508SToomas Soome 	struct serial *sp = cp->c_private;
64122028508SToomas Soome 
64222028508SToomas Soome 	/* port is not usable */
64322028508SToomas Soome 	if (sp->sio == NULL)
64422028508SToomas Soome 		return (false);
64522028508SToomas Soome 
64622028508SToomas Soome 	status = sp->sio->Reset(sp->sio);
64722028508SToomas Soome 	if (EFI_ERROR(status))
64822028508SToomas Soome 		return (false);
64922028508SToomas Soome 
65022028508SToomas Soome 	status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity,
65122028508SToomas Soome 	    sp->databits, sp->stopbits);
65222028508SToomas Soome 	if (EFI_ERROR(status))
65322028508SToomas Soome 		return (false);
65422028508SToomas Soome 
65522028508SToomas Soome 	status = sp->sio->GetControl(sp->sio, &control);
65622028508SToomas Soome 	if (EFI_ERROR(status))
65722028508SToomas Soome 		return (false);
65822028508SToomas Soome 	if (sp->rtsdtr_off) {
65922028508SToomas Soome 		control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
66022028508SToomas Soome 		    EFI_SERIAL_DATA_TERMINAL_READY);
66122028508SToomas Soome 	} else {
66222028508SToomas Soome 		control |= EFI_SERIAL_REQUEST_TO_SEND;
66322028508SToomas Soome 	}
66422028508SToomas Soome 
66522028508SToomas Soome 	(void) sp->sio->SetControl(sp->sio, control);
66622028508SToomas Soome 
66722028508SToomas Soome 	/* Mark this port usable. */
66822028508SToomas Soome 	cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
66922028508SToomas Soome 	return (true);
67022028508SToomas Soome }
671