130c75cb0SToomas Soome /*
230c75cb0SToomas Soome  * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
330c75cb0SToomas Soome  *
430c75cb0SToomas Soome  * Redistribution and use in source and binary forms, with or without
530c75cb0SToomas Soome  * modification, are permitted provided that the following conditions
630c75cb0SToomas Soome  * are met:
730c75cb0SToomas Soome  * 1. Redistributions of source code must retain the above copyright
830c75cb0SToomas Soome  *    notice, this list of conditions and the following disclaimer.
930c75cb0SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
1030c75cb0SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
1130c75cb0SToomas Soome  *    documentation and/or other materials provided with the distribution.
1230c75cb0SToomas Soome  *
1330c75cb0SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1430c75cb0SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1530c75cb0SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1630c75cb0SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1730c75cb0SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1830c75cb0SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1930c75cb0SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2030c75cb0SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2130c75cb0SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2230c75cb0SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2330c75cb0SToomas Soome  * SUCH DAMAGE.
2430c75cb0SToomas Soome  */
2530c75cb0SToomas Soome 
265ac07b12SToomas Soome /*
275ac07b12SToomas Soome  * We do not use this implementation with x86 till we can fix two issues:
285ac07b12SToomas Soome  * 1. Reliably identify the serial ports in correct order.
295ac07b12SToomas Soome  * 2. Ensure we get properly working reads from serial io.
305ac07b12SToomas Soome  */
315ac07b12SToomas Soome 
3230c75cb0SToomas Soome #include <sys/cdefs.h>
3330c75cb0SToomas Soome 
3430c75cb0SToomas Soome #include <stand.h>
3530c75cb0SToomas Soome #include <sys/errno.h>
3630c75cb0SToomas Soome #include <bootstrap.h>
37fec53dd4SToomas Soome #include <stdbool.h>
3830c75cb0SToomas Soome 
3930c75cb0SToomas Soome #include <efi.h>
4030c75cb0SToomas Soome #include <efilib.h>
41*f334afcfSToomas Soome #include <Protocol/SerialIo.h>
4230c75cb0SToomas Soome 
4330c75cb0SToomas Soome #include "loader_efi.h"
4430c75cb0SToomas 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;
4830c75cb0SToomas Soome 
4930c75cb0SToomas Soome #define	COMC_TXWAIT	0x40000		/* transmit timeout */
5030c75cb0SToomas Soome 
5130c75cb0SToomas Soome #ifndef	COMSPEED
5230c75cb0SToomas Soome #define	COMSPEED	9600
5330c75cb0SToomas Soome #endif
5430c75cb0SToomas Soome 
55a2f10c44SToomas Soome #define	PNP0501		0x501		/* 16550A-compatible COM port */
56a2f10c44SToomas Soome 
5730c75cb0SToomas Soome struct serial {
5830c75cb0SToomas Soome 	uint64_t	baudrate;
5930c75cb0SToomas Soome 	uint8_t		databits;
6030c75cb0SToomas Soome 	EFI_PARITY_TYPE	parity;
6130c75cb0SToomas Soome 	EFI_STOP_BITS_TYPE stopbits;
6230c75cb0SToomas Soome 	uint8_t		ignore_cd;	/* boolean */
6330c75cb0SToomas Soome 	uint8_t		rtsdtr_off;	/* boolean */
6430c75cb0SToomas Soome 	int		ioaddr;		/* index in handles array */
6530c75cb0SToomas Soome 	SERIAL_IO_INTERFACE *sio;
6630c75cb0SToomas Soome };
6730c75cb0SToomas Soome 
6830c75cb0SToomas Soome static void	comc_probe(struct console *);
6930c75cb0SToomas Soome static int	comc_init(struct console *, int);
7030c75cb0SToomas Soome static void	comc_putchar(struct console *, int);
7130c75cb0SToomas Soome static int	comc_getchar(struct console *);
7230c75cb0SToomas Soome static int	comc_ischar(struct console *);
739890ff83SToomas Soome static int	comc_ioctl(struct console *, int, void *);
7480e47917SToomas Soome static void	comc_devinfo(struct console *);
75fec53dd4SToomas Soome static bool	comc_setup(struct console *);
7630c75cb0SToomas Soome static char	*comc_asprint_mode(struct serial *);
7730c75cb0SToomas Soome static int	comc_parse_mode(struct serial *, const char *);
7830c75cb0SToomas Soome static int	comc_mode_set(struct env_var *, int, const void *);
7930c75cb0SToomas Soome static int	comc_cd_set(struct env_var *, int, const void *);
8030c75cb0SToomas Soome static int	comc_rtsdtr_set(struct env_var *, int, const void *);
8130c75cb0SToomas Soome 
8230c75cb0SToomas Soome struct console ttya = {
8330c75cb0SToomas Soome 	.c_name = "ttya",
8430c75cb0SToomas Soome 	.c_desc = "serial port a",
8530c75cb0SToomas Soome 	.c_flags = 0,
8630c75cb0SToomas Soome 	.c_probe = comc_probe,
8730c75cb0SToomas Soome 	.c_init = comc_init,
8830c75cb0SToomas Soome 	.c_out = comc_putchar,
8930c75cb0SToomas Soome 	.c_in = comc_getchar,
9030c75cb0SToomas Soome 	.c_ready = comc_ischar,
919890ff83SToomas Soome 	.c_ioctl = comc_ioctl,
9280e47917SToomas Soome 	.c_devinfo = comc_devinfo,
9330c75cb0SToomas Soome 	.c_private = NULL
9430c75cb0SToomas Soome };
9530c75cb0SToomas Soome 
9630c75cb0SToomas Soome struct console ttyb = {
9730c75cb0SToomas Soome 	.c_name = "ttyb",
9830c75cb0SToomas Soome 	.c_desc = "serial port b",
9930c75cb0SToomas Soome 	.c_flags = 0,
10030c75cb0SToomas Soome 	.c_probe = comc_probe,
10130c75cb0SToomas Soome 	.c_init = comc_init,
10230c75cb0SToomas Soome 	.c_out = comc_putchar,
10330c75cb0SToomas Soome 	.c_in = comc_getchar,
10430c75cb0SToomas Soome 	.c_ready = comc_ischar,
1059890ff83SToomas Soome 	.c_ioctl = comc_ioctl,
10680e47917SToomas Soome 	.c_devinfo = comc_devinfo,
10730c75cb0SToomas Soome 	.c_private = NULL
10830c75cb0SToomas Soome };
10930c75cb0SToomas Soome 
11030c75cb0SToomas Soome struct console ttyc = {
11130c75cb0SToomas Soome 	.c_name = "ttyc",
11230c75cb0SToomas Soome 	.c_desc = "serial port c",
11330c75cb0SToomas Soome 	.c_flags = 0,
11430c75cb0SToomas Soome 	.c_probe = comc_probe,
11530c75cb0SToomas Soome 	.c_init = comc_init,
11630c75cb0SToomas Soome 	.c_out = comc_putchar,
11730c75cb0SToomas Soome 	.c_in = comc_getchar,
11830c75cb0SToomas Soome 	.c_ready = comc_ischar,
1199890ff83SToomas Soome 	.c_ioctl = comc_ioctl,
12080e47917SToomas Soome 	.c_devinfo = comc_devinfo,
12130c75cb0SToomas Soome 	.c_private = NULL
12230c75cb0SToomas Soome };
12330c75cb0SToomas Soome 
12430c75cb0SToomas Soome struct console ttyd = {
12530c75cb0SToomas Soome 	.c_name = "ttyd",
12630c75cb0SToomas Soome 	.c_desc = "serial port d",
12730c75cb0SToomas Soome 	.c_flags = 0,
12830c75cb0SToomas Soome 	.c_probe = comc_probe,
12930c75cb0SToomas Soome 	.c_init = comc_init,
13030c75cb0SToomas Soome 	.c_out = comc_putchar,
13130c75cb0SToomas Soome 	.c_in = comc_getchar,
13230c75cb0SToomas Soome 	.c_ready = comc_ischar,
1339890ff83SToomas Soome 	.c_ioctl = comc_ioctl,
13480e47917SToomas Soome 	.c_devinfo = comc_devinfo,
13530c75cb0SToomas Soome 	.c_private = NULL
13630c75cb0SToomas Soome };
13730c75cb0SToomas Soome 
138a2f10c44SToomas Soome /*
139a2f10c44SToomas Soome  * Find serial device number from device path.
140a2f10c44SToomas Soome  * Return -1 if not found.
141a2f10c44SToomas Soome  */
142a2f10c44SToomas Soome static int
efi_serial_get_index(EFI_DEVICE_PATH * devpath)143a2f10c44SToomas Soome efi_serial_get_index(EFI_DEVICE_PATH *devpath)
144a2f10c44SToomas Soome {
145a2f10c44SToomas Soome 	ACPI_HID_DEVICE_PATH  *acpi;
146a2f10c44SToomas Soome 
147a2f10c44SToomas Soome 	while (!IsDevicePathEnd(devpath)) {
148a2f10c44SToomas Soome 		if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
149a2f10c44SToomas Soome 		    DevicePathSubType(devpath) == ACPI_DP) {
150a2f10c44SToomas Soome 
151a2f10c44SToomas Soome 			acpi = (ACPI_HID_DEVICE_PATH *)devpath;
152a2f10c44SToomas Soome 			if (acpi->HID == EISA_PNP_ID(PNP0501)) {
153a2f10c44SToomas Soome 				return (acpi->UID);
154a2f10c44SToomas Soome 			}
155a2f10c44SToomas Soome 		}
156a2f10c44SToomas Soome 
157a2f10c44SToomas Soome 		devpath = NextDevicePathNode(devpath);
158a2f10c44SToomas Soome 	}
159a2f10c44SToomas Soome 	return (-1);
160a2f10c44SToomas Soome }
161a2f10c44SToomas Soome 
162a2f10c44SToomas Soome /*
163a2f10c44SToomas Soome  * The order of handles from LocateHandle() is not known, we need to
164a2f10c44SToomas Soome  * iterate handles, pick device path for handle, and check the device
165a2f10c44SToomas Soome  * number.
166a2f10c44SToomas Soome  */
167a2f10c44SToomas Soome static EFI_HANDLE
efi_serial_get_handle(int port)168a2f10c44SToomas Soome efi_serial_get_handle(int port)
169a2f10c44SToomas Soome {
170a2f10c44SToomas Soome 	EFI_STATUS status;
171a2f10c44SToomas Soome 	EFI_HANDLE *handles, handle;
172a2f10c44SToomas Soome 	EFI_DEVICE_PATH *devpath;
17369764de3SToomas Soome 	uint_t index, nhandles;
174a2f10c44SToomas Soome 
175a2f10c44SToomas Soome 	if (port == -1)
176a2f10c44SToomas Soome 		return (NULL);
177a2f10c44SToomas Soome 
178*f334afcfSToomas Soome 	status = efi_get_protocol_handles(&gEfiSerialIoProtocolGuid,
179*f334afcfSToomas Soome 	    &nhandles, &handles);
180a2f10c44SToomas Soome 	if (EFI_ERROR(status))
181a2f10c44SToomas Soome 		return (NULL);
182a2f10c44SToomas Soome 
183a2f10c44SToomas Soome 	handle = NULL;
184a2f10c44SToomas Soome 	for (index = 0; index < nhandles; index++) {
185a2f10c44SToomas Soome 		devpath = efi_lookup_devpath(handles[index]);
186a2f10c44SToomas Soome 		if (port == efi_serial_get_index(devpath)) {
187a2f10c44SToomas Soome 			handle = (handles[index]);
188a2f10c44SToomas Soome 			break;
189a2f10c44SToomas Soome 		}
190a2f10c44SToomas Soome 	}
191a2f10c44SToomas Soome 
192a2f10c44SToomas Soome 	/*
193a2f10c44SToomas Soome 	 * In case we did fail to identify the device by path, use port as
194a2f10c44SToomas Soome 	 * array index. Note, we did check port == -1 above.
195a2f10c44SToomas Soome 	 */
196a2f10c44SToomas Soome 	if (port < nhandles && handle == NULL)
197a2f10c44SToomas Soome 		handle = handles[port];
198a2f10c44SToomas Soome 
199a2f10c44SToomas Soome 	free(handles);
200a2f10c44SToomas Soome 	return (handle);
201a2f10c44SToomas Soome }
202a2f10c44SToomas Soome 
20330c75cb0SToomas Soome static void
comc_probe(struct console * cp)20430c75cb0SToomas Soome comc_probe(struct console *cp)
20530c75cb0SToomas Soome {
20630c75cb0SToomas Soome 	EFI_STATUS status;
207a2f10c44SToomas Soome 	EFI_HANDLE handle;
20830c75cb0SToomas Soome 	struct serial *port;
20930c75cb0SToomas Soome 	char name[20];
21030c75cb0SToomas Soome 	char value[20];
211dd842a53SToomas Soome 	char *env;
21230c75cb0SToomas Soome 
21330c75cb0SToomas Soome 	/* are we already set up? */
21430c75cb0SToomas Soome 	if (cp->c_private != NULL)
21530c75cb0SToomas Soome 		return;
21630c75cb0SToomas Soome 
21730c75cb0SToomas Soome 	cp->c_private = malloc(sizeof (struct serial));
21830c75cb0SToomas Soome 	port = cp->c_private;
21930c75cb0SToomas Soome 	port->baudrate = COMSPEED;
22030c75cb0SToomas Soome 
221a2f10c44SToomas Soome 	port->ioaddr = -1;	/* invalid port */
22230c75cb0SToomas Soome 	if (strcmp(cp->c_name, "ttya") == 0)
22330c75cb0SToomas Soome 		port->ioaddr = 0;
22430c75cb0SToomas Soome 	else if (strcmp(cp->c_name, "ttyb") == 0)
22530c75cb0SToomas Soome 		port->ioaddr = 1;
22630c75cb0SToomas Soome 	else if (strcmp(cp->c_name, "ttyc") == 0)
22730c75cb0SToomas Soome 		port->ioaddr = 2;
22830c75cb0SToomas Soome 	else if (strcmp(cp->c_name, "ttyd") == 0)
22930c75cb0SToomas Soome 		port->ioaddr = 3;
23030c75cb0SToomas Soome 
23130c75cb0SToomas Soome 	port->databits = 8;		/* 8,n,1 */
23230c75cb0SToomas Soome 	port->parity = NoParity;	/* 8,n,1 */
23330c75cb0SToomas Soome 	port->stopbits = OneStopBit;	/* 8,n,1 */
23430c75cb0SToomas Soome 	port->ignore_cd = 1;		/* ignore cd */
23530c75cb0SToomas Soome 	port->rtsdtr_off = 0;		/* rts-dtr is on */
23630c75cb0SToomas Soome 	port->sio = NULL;
23730c75cb0SToomas Soome 
238a2f10c44SToomas Soome 	handle = efi_serial_get_handle(port->ioaddr);
239a2f10c44SToomas Soome 
240a2f10c44SToomas Soome 	if (handle != NULL) {
241*f334afcfSToomas Soome 		status = BS->OpenProtocol(handle, &gEfiSerialIoProtocolGuid,
242a2f10c44SToomas Soome 		    (void**)&port->sio, IH, NULL,
24330c75cb0SToomas Soome 		    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
24430c75cb0SToomas Soome 
24530c75cb0SToomas Soome 		if (EFI_ERROR(status))
246a2f10c44SToomas Soome 			port->sio = NULL;
24730c75cb0SToomas Soome 	}
24830c75cb0SToomas Soome 
24930c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-mode", cp->c_name);
25030c75cb0SToomas Soome 	env = getenv(name);
25130c75cb0SToomas Soome 
25230c75cb0SToomas Soome 	if (env != NULL)
25330c75cb0SToomas Soome 		(void) comc_parse_mode(port, env);
25430c75cb0SToomas Soome 
25530c75cb0SToomas Soome 	env = comc_asprint_mode(port);
25630c75cb0SToomas Soome 
25730c75cb0SToomas Soome 	if (env != NULL) {
25830c75cb0SToomas Soome 		unsetenv(name);
25930c75cb0SToomas Soome 		env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset);
26030c75cb0SToomas Soome 		free(env);
26130c75cb0SToomas Soome 	}
26230c75cb0SToomas Soome 
26330c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name);
26430c75cb0SToomas Soome 	env = getenv(name);
26530c75cb0SToomas Soome 	if (env != NULL) {
26630c75cb0SToomas Soome 		if (strcmp(env, "true") == 0)
26730c75cb0SToomas Soome 			port->ignore_cd = 1;
26830c75cb0SToomas Soome 		else if (strcmp(env, "false") == 0)
26930c75cb0SToomas Soome 			port->ignore_cd = 0;
27030c75cb0SToomas Soome 	}
27130c75cb0SToomas Soome 
27230c75cb0SToomas Soome 	snprintf(value, sizeof (value), "%s",
27330c75cb0SToomas Soome 	    port->ignore_cd? "true" : "false");
27430c75cb0SToomas Soome 	unsetenv(name);
27530c75cb0SToomas Soome 	env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset);
27630c75cb0SToomas Soome 
27730c75cb0SToomas Soome 	snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name);
27830c75cb0SToomas Soome 	env = getenv(name);
27930c75cb0SToomas Soome 	if (env != NULL) {
28030c75cb0SToomas Soome 		if (strcmp(env, "true") == 0)
28130c75cb0SToomas Soome 			port->rtsdtr_off = 1;
28230c75cb0SToomas Soome 		else if (strcmp(env, "false") == 0)
28330c75cb0SToomas Soome 			port->rtsdtr_off = 0;
28430c75cb0SToomas Soome 	}
28530c75cb0SToomas Soome 
28630c75cb0SToomas Soome 	snprintf(value, sizeof (value), "%s",
28730c75cb0SToomas Soome 	    port->rtsdtr_off? "true" : "false");
28830c75cb0SToomas Soome 	unsetenv(name);
28930c75cb0SToomas Soome 	env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset);
290fec53dd4SToomas Soome 
291fec53dd4SToomas Soome 	cp->c_flags = 0;
292fec53dd4SToomas Soome 	if (comc_setup(cp))
293fec53dd4SToomas Soome 		cp->c_flags = C_PRESENTIN | C_PRESENTOUT;
29430c75cb0SToomas Soome }
29530c75cb0SToomas Soome 
29630c75cb0SToomas Soome static int
comc_init(struct console * cp,int arg __attribute ((unused)))29730c75cb0SToomas Soome comc_init(struct console *cp, int arg __attribute((unused)))
29830c75cb0SToomas Soome {
29930c75cb0SToomas Soome 
300fec53dd4SToomas Soome 	if (comc_setup(cp))
30130c75cb0SToomas Soome 		return (CMD_OK);
302fec53dd4SToomas Soome 
303fec53dd4SToomas Soome 	cp->c_flags = 0;
30430c75cb0SToomas Soome 	return (CMD_ERROR);
30530c75cb0SToomas Soome }
30630c75cb0SToomas Soome 
30730c75cb0SToomas Soome static void
comc_putchar(struct console * cp,int c)30830c75cb0SToomas Soome comc_putchar(struct console *cp, int c)
30930c75cb0SToomas Soome {
31030c75cb0SToomas Soome 	int wait;
31130c75cb0SToomas Soome 	EFI_STATUS status;
31230c75cb0SToomas Soome 	UINTN bufsz = 1;
31330c75cb0SToomas Soome 	char cb = c;
31430c75cb0SToomas Soome 	struct serial *sp = cp->c_private;
31530c75cb0SToomas Soome 
31630c75cb0SToomas Soome 	if (sp->sio == NULL)
31730c75cb0SToomas Soome 		return;
31830c75cb0SToomas Soome 
31930c75cb0SToomas Soome 	for (wait = COMC_TXWAIT; wait > 0; wait--) {
32030c75cb0SToomas Soome 		status = sp->sio->Write(sp->sio, &bufsz, &cb);
32130c75cb0SToomas Soome 		if (status != EFI_TIMEOUT)
32230c75cb0SToomas Soome 			break;
32330c75cb0SToomas Soome 	}
32430c75cb0SToomas Soome }
32530c75cb0SToomas Soome 
32630c75cb0SToomas Soome static int
comc_getchar(struct console * cp)32730c75cb0SToomas Soome comc_getchar(struct console *cp)
32830c75cb0SToomas Soome {
32930c75cb0SToomas Soome 	EFI_STATUS status;
33030c75cb0SToomas Soome 	UINTN bufsz = 1;
33130c75cb0SToomas Soome 	char c;
33230c75cb0SToomas Soome 	struct serial *sp = cp->c_private;
33330c75cb0SToomas Soome 
33430c75cb0SToomas Soome 	if (sp->sio == NULL || !comc_ischar(cp))
33530c75cb0SToomas Soome 		return (-1);
33630c75cb0SToomas Soome 
33730c75cb0SToomas Soome 	status = sp->sio->Read(sp->sio, &bufsz, &c);
33830c75cb0SToomas Soome 	if (EFI_ERROR(status) || bufsz == 0)
33930c75cb0SToomas Soome 		return (-1);
34030c75cb0SToomas Soome 
34130c75cb0SToomas Soome 	return (c);
34230c75cb0SToomas Soome }
34330c75cb0SToomas Soome 
34430c75cb0SToomas Soome static int
comc_ischar(struct console * cp)34530c75cb0SToomas Soome comc_ischar(struct console *cp)
34630c75cb0SToomas Soome {
34730c75cb0SToomas Soome 	EFI_STATUS status;
34830c75cb0SToomas Soome 	uint32_t control;
34930c75cb0SToomas Soome 	struct serial *sp = cp->c_private;
35030c75cb0SToomas Soome 
35130c75cb0SToomas Soome 	if (sp->sio == NULL)
35230c75cb0SToomas Soome 		return (0);
35330c75cb0SToomas Soome 
35430c75cb0SToomas Soome 	status = sp->sio->GetControl(sp->sio, &control);
35530c75cb0SToomas Soome 	if (EFI_ERROR(status))
35630c75cb0SToomas Soome 		return (0);
35730c75cb0SToomas Soome 
358d656c9b5SToomas Soome 	return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
35930c75cb0SToomas Soome }
36030c75cb0SToomas Soome 
3619890ff83SToomas Soome static int
comc_ioctl(struct console * cp __unused,int cmd __unused,void * data __unused)3629890ff83SToomas Soome comc_ioctl(struct console *cp __unused, int cmd __unused, void *data __unused)
3639890ff83SToomas Soome {
3649890ff83SToomas Soome 	return (ENOTTY);
3659890ff83SToomas Soome }
3669890ff83SToomas Soome 
36780e47917SToomas Soome static void
comc_devinfo(struct console * cp)36880e47917SToomas Soome comc_devinfo(struct console *cp)
36980e47917SToomas Soome {
37080e47917SToomas Soome 	struct serial *port = cp->c_private;
37180e47917SToomas Soome 	EFI_HANDLE handle;
37280e47917SToomas Soome 	EFI_DEVICE_PATH *dp;
37380e47917SToomas Soome 	CHAR16 *text;
37480e47917SToomas Soome 
37580e47917SToomas Soome 	handle = efi_serial_get_handle(port->ioaddr);
37680e47917SToomas Soome 	if (handle == NULL) {
37780e47917SToomas Soome 		printf("\tdevice is not present");
37880e47917SToomas Soome 		return;
37980e47917SToomas Soome 	}
38080e47917SToomas Soome 
38180e47917SToomas Soome 	dp = efi_lookup_devpath(handle);
38280e47917SToomas Soome 	if (dp == NULL)
38380e47917SToomas Soome 		return;
38480e47917SToomas Soome 
38580e47917SToomas Soome 	text = efi_devpath_name(dp);
38680e47917SToomas Soome 	if (text == NULL)
38780e47917SToomas Soome 		return;
38880e47917SToomas Soome 
38980e47917SToomas Soome 	printf("\t%S", text);
39080e47917SToomas Soome 	efi_free_devpath_name(text);
39180e47917SToomas Soome }
39280e47917SToomas Soome 
39330c75cb0SToomas Soome static char *
comc_asprint_mode(struct serial * sp)39430c75cb0SToomas Soome comc_asprint_mode(struct serial *sp)
39530c75cb0SToomas Soome {
396cbae6195SToomas Soome 	char par, *buf;
397cbae6195SToomas Soome 	char *stop;
39830c75cb0SToomas Soome 
39930c75cb0SToomas Soome 	if (sp == NULL)
40030c75cb0SToomas Soome 		return (NULL);
40130c75cb0SToomas Soome 
40230c75cb0SToomas Soome 	switch (sp->parity) {
403cbae6195SToomas Soome 	case NoParity:
404cbae6195SToomas Soome 		par = 'n';
40530c75cb0SToomas Soome 		break;
406cbae6195SToomas Soome 	case EvenParity:
407cbae6195SToomas Soome 		par = 'e';
40830c75cb0SToomas Soome 		break;
409cbae6195SToomas Soome 	case OddParity:
410cbae6195SToomas Soome 		par = 'o';
411cbae6195SToomas Soome 		break;
412cbae6195SToomas Soome 	case MarkParity:
413cbae6195SToomas Soome 		par = 'm';
414cbae6195SToomas Soome 		break;
415cbae6195SToomas Soome 	case SpaceParity:
416cbae6195SToomas Soome 		par = 's';
417cbae6195SToomas Soome 		break;
418cbae6195SToomas Soome 	default:
419cbae6195SToomas Soome 		par = 'n';
42030c75cb0SToomas Soome 		break;
42130c75cb0SToomas Soome 	}
422cbae6195SToomas Soome 
42330c75cb0SToomas Soome 	switch (sp->stopbits) {
424cbae6195SToomas Soome 	case OneStopBit:
425cbae6195SToomas Soome 		stop = "1";
426cbae6195SToomas Soome 		break;
427cbae6195SToomas Soome 	case TwoStopBits:
428cbae6195SToomas Soome 		stop = "2";
42930c75cb0SToomas Soome 		break;
430cbae6195SToomas Soome 	case OneFiveStopBits:
431cbae6195SToomas Soome 		stop = "1.5";
432cbae6195SToomas Soome 		break;
433cbae6195SToomas Soome 	default:
434cbae6195SToomas Soome 		stop = "1";
43530c75cb0SToomas Soome 		break;
43630c75cb0SToomas Soome 	}
43730c75cb0SToomas Soome 
438cbae6195SToomas Soome 	asprintf(&buf, "%ju,%d,%c,%s,-", sp->baudrate, sp->databits, par, stop);
43930c75cb0SToomas Soome 	return (buf);
44030c75cb0SToomas Soome }
44130c75cb0SToomas Soome 
44230c75cb0SToomas Soome static int
comc_parse_mode(struct serial * sp,const char * value)44330c75cb0SToomas Soome comc_parse_mode(struct serial *sp, const char *value)
44430c75cb0SToomas Soome {
44530c75cb0SToomas Soome 	unsigned long n;
44630c75cb0SToomas Soome 	uint64_t baudrate;
44730c75cb0SToomas Soome 	uint8_t databits = 8;
44830c75cb0SToomas Soome 	int parity = NoParity;
44930c75cb0SToomas Soome 	int stopbits = OneStopBit;
45030c75cb0SToomas Soome 	char *ep;
45130c75cb0SToomas Soome 
45230c75cb0SToomas Soome 	if (value == NULL || *value == '\0')
45330c75cb0SToomas Soome 		return (CMD_ERROR);
45430c75cb0SToomas Soome 
45530c75cb0SToomas Soome 	errno = 0;
45630c75cb0SToomas Soome 	n = strtoul(value, &ep, 10);
45730c75cb0SToomas Soome 	if (errno != 0 || *ep != ',')
45830c75cb0SToomas Soome 		return (CMD_ERROR);
45930c75cb0SToomas Soome 	baudrate = n;
46030c75cb0SToomas Soome 
46130c75cb0SToomas Soome 	ep++;
46230c75cb0SToomas Soome 	n = strtoul(ep, &ep, 10);
46330c75cb0SToomas Soome 	if (errno != 0 || *ep != ',')
46430c75cb0SToomas Soome 		return (CMD_ERROR);
46530c75cb0SToomas Soome 
46630c75cb0SToomas Soome 	switch (n) {
467cbae6195SToomas Soome 	case 5: databits = 5;
468cbae6195SToomas Soome 		break;
469cbae6195SToomas Soome 	case 6: databits = 6;
470cbae6195SToomas Soome 		break;
47130c75cb0SToomas Soome 	case 7: databits = 7;
47230c75cb0SToomas Soome 		break;
47330c75cb0SToomas Soome 	case 8: databits = 8;
47430c75cb0SToomas Soome 		break;
47530c75cb0SToomas Soome 	default:
47630c75cb0SToomas Soome 		return (CMD_ERROR);
47730c75cb0SToomas Soome 	}
47830c75cb0SToomas Soome 
47930c75cb0SToomas Soome 	ep++;
48030c75cb0SToomas Soome 	switch (*ep++) {
48130c75cb0SToomas Soome 	case 'n': parity = NoParity;
48230c75cb0SToomas Soome 		break;
48330c75cb0SToomas Soome 	case 'e': parity = EvenParity;
48430c75cb0SToomas Soome 		break;
48530c75cb0SToomas Soome 	case 'o': parity = OddParity;
48630c75cb0SToomas Soome 		break;
487cbae6195SToomas Soome 	case 'm': parity = MarkParity;
488cbae6195SToomas Soome 		break;
489cbae6195SToomas Soome 	case 's': parity = SpaceParity;
490cbae6195SToomas Soome 		break;
49130c75cb0SToomas Soome 	default:
49230c75cb0SToomas Soome 		return (CMD_ERROR);
49330c75cb0SToomas Soome 	}
49430c75cb0SToomas Soome 
49530c75cb0SToomas Soome 	if (*ep == ',')
49630c75cb0SToomas Soome 		ep++;
49730c75cb0SToomas Soome 	else
49830c75cb0SToomas Soome 		return (CMD_ERROR);
49930c75cb0SToomas Soome 
50030c75cb0SToomas Soome 	switch (*ep++) {
50130c75cb0SToomas Soome 	case '1': stopbits = OneStopBit;
502cbae6195SToomas Soome 		if (ep[0] == '.' && ep[1] == '5') {
503cbae6195SToomas Soome 			ep += 2;
504cbae6195SToomas Soome 			stopbits = OneFiveStopBits;
505cbae6195SToomas Soome 		}
50630c75cb0SToomas Soome 		break;
50730c75cb0SToomas Soome 	case '2': stopbits = TwoStopBits;
50830c75cb0SToomas Soome 		break;
50930c75cb0SToomas Soome 	default:
51030c75cb0SToomas Soome 		return (CMD_ERROR);
51130c75cb0SToomas Soome 	}
51230c75cb0SToomas Soome 
51330c75cb0SToomas Soome 	/* handshake is ignored, but we check syntax anyhow */
51430c75cb0SToomas Soome 	if (*ep == ',')
51530c75cb0SToomas Soome 		ep++;
51630c75cb0SToomas Soome 	else
51730c75cb0SToomas Soome 		return (CMD_ERROR);
51830c75cb0SToomas Soome 
51930c75cb0SToomas Soome 	switch (*ep++) {
52030c75cb0SToomas Soome 	case '-':
52130c75cb0SToomas Soome 	case 'h':
52230c75cb0SToomas Soome 	case 's':
52330c75cb0SToomas Soome 		break;
52430c75cb0SToomas Soome 	default:
52530c75cb0SToomas Soome 		return (CMD_ERROR);
52630c75cb0SToomas Soome 	}
52730c75cb0SToomas Soome 
52830c75cb0SToomas Soome 	if (*ep != '\0')
52930c75cb0SToomas Soome 		return (CMD_ERROR);
53030c75cb0SToomas Soome 
53130c75cb0SToomas Soome 	sp->baudrate = baudrate;
53230c75cb0SToomas Soome 	sp->databits = databits;
53330c75cb0SToomas Soome 	sp->parity = parity;
53430c75cb0SToomas Soome 	sp->stopbits = stopbits;
53530c75cb0SToomas Soome 	return (CMD_OK);
53630c75cb0SToomas Soome }
53730c75cb0SToomas Soome 
53830c75cb0SToomas Soome static struct console *
get_console(char * name)53930c75cb0SToomas Soome get_console(char *name)
54030c75cb0SToomas Soome {
54130c75cb0SToomas Soome 	struct console *cp = NULL;
54230c75cb0SToomas Soome 
54330c75cb0SToomas Soome 	switch (name[3]) {
54430c75cb0SToomas Soome 	case 'a': cp = &ttya;
54530c75cb0SToomas Soome 		break;
54630c75cb0SToomas Soome 	case 'b': cp = &ttyb;
54730c75cb0SToomas Soome 		break;
54830c75cb0SToomas Soome 	case 'c': cp = &ttyc;
54930c75cb0SToomas Soome 		break;
55030c75cb0SToomas Soome 	case 'd': cp = &ttyd;
55130c75cb0SToomas Soome 		break;
55230c75cb0SToomas Soome 	}
55330c75cb0SToomas Soome 	return (cp);
55430c75cb0SToomas Soome }
55530c75cb0SToomas Soome 
55630c75cb0SToomas Soome static int
comc_mode_set(struct env_var * ev,int flags,const void * value)55730c75cb0SToomas Soome comc_mode_set(struct env_var *ev, int flags, const void *value)
55830c75cb0SToomas Soome {
55930c75cb0SToomas Soome 	struct console *cp;
56030c75cb0SToomas Soome 
56130c75cb0SToomas Soome 	if (value == NULL)
56230c75cb0SToomas Soome 		return (CMD_ERROR);
56330c75cb0SToomas Soome 
56430c75cb0SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
56530c75cb0SToomas Soome 		return (CMD_ERROR);
56630c75cb0SToomas Soome 
56730c75cb0SToomas Soome 	if (comc_parse_mode(cp->c_private, value) == CMD_ERROR)
56830c75cb0SToomas Soome 		return (CMD_ERROR);
56930c75cb0SToomas Soome 
570fec53dd4SToomas Soome 	(void) comc_setup(cp);
571fec53dd4SToomas Soome 
57230c75cb0SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
57330c75cb0SToomas Soome 
57430c75cb0SToomas Soome 	return (CMD_OK);
57530c75cb0SToomas Soome }
57630c75cb0SToomas Soome 
57730c75cb0SToomas Soome static int
comc_cd_set(struct env_var * ev,int flags,const void * value)57830c75cb0SToomas Soome comc_cd_set(struct env_var *ev, int flags, const void *value)
57930c75cb0SToomas Soome {
58030c75cb0SToomas Soome 	struct console *cp;
58130c75cb0SToomas Soome 	struct serial *sp;
58230c75cb0SToomas Soome 
58330c75cb0SToomas Soome 	if (value == NULL)
58430c75cb0SToomas Soome 		return (CMD_ERROR);
58530c75cb0SToomas Soome 
58630c75cb0SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
58730c75cb0SToomas Soome 		return (CMD_ERROR);
58830c75cb0SToomas Soome 
58930c75cb0SToomas Soome 	sp = cp->c_private;
59030c75cb0SToomas Soome 	if (strcmp(value, "true") == 0)
59130c75cb0SToomas Soome 		sp->ignore_cd = 1;
59230c75cb0SToomas Soome 	else if (strcmp(value, "false") == 0)
59330c75cb0SToomas Soome 		sp->ignore_cd = 0;
59430c75cb0SToomas Soome 	else
59530c75cb0SToomas Soome 		return (CMD_ERROR);
59630c75cb0SToomas Soome 
597fec53dd4SToomas Soome 	(void) comc_setup(cp);
598fec53dd4SToomas Soome 
59930c75cb0SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
60030c75cb0SToomas Soome 
60130c75cb0SToomas Soome 	return (CMD_OK);
60230c75cb0SToomas Soome }
60330c75cb0SToomas Soome 
60430c75cb0SToomas Soome static int
comc_rtsdtr_set(struct env_var * ev,int flags,const void * value)60530c75cb0SToomas Soome comc_rtsdtr_set(struct env_var *ev, int flags, const void *value)
60630c75cb0SToomas Soome {
60730c75cb0SToomas Soome 	struct console *cp;
60830c75cb0SToomas Soome 	struct serial *sp;
60930c75cb0SToomas Soome 
61030c75cb0SToomas Soome 	if (value == NULL)
61130c75cb0SToomas Soome 		return (CMD_ERROR);
61230c75cb0SToomas Soome 
61330c75cb0SToomas Soome 	if ((cp = get_console(ev->ev_name)) == NULL)
61430c75cb0SToomas Soome 		return (CMD_ERROR);
61530c75cb0SToomas Soome 
61630c75cb0SToomas Soome 	sp = cp->c_private;
61730c75cb0SToomas Soome 	if (strcmp(value, "true") == 0)
61830c75cb0SToomas Soome 		sp->rtsdtr_off = 1;
61930c75cb0SToomas Soome 	else if (strcmp(value, "false") == 0)
62030c75cb0SToomas Soome 		sp->rtsdtr_off = 0;
62130c75cb0SToomas Soome 	else
62230c75cb0SToomas Soome 		return (CMD_ERROR);
62330c75cb0SToomas Soome 
624fec53dd4SToomas Soome 	(void) comc_setup(cp);
625fec53dd4SToomas Soome 
62630c75cb0SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
62730c75cb0SToomas Soome 
62830c75cb0SToomas Soome 	return (CMD_OK);
62930c75cb0SToomas Soome }
63030c75cb0SToomas Soome 
631fec53dd4SToomas Soome /*
632fec53dd4SToomas Soome  * In case of error, we also reset ACTIVE flags, so the console
633fec53dd4SToomas Soome  * framefork will try alternate consoles.
634fec53dd4SToomas Soome  */
635fec53dd4SToomas Soome static bool
comc_setup(struct console * cp)63630c75cb0SToomas Soome comc_setup(struct console *cp)
63730c75cb0SToomas Soome {
63830c75cb0SToomas Soome 	EFI_STATUS status;
63930c75cb0SToomas Soome 	UINT32 control;
64030c75cb0SToomas Soome 	struct serial *sp = cp->c_private;
64130c75cb0SToomas Soome 
64230c75cb0SToomas Soome 	/* port is not usable */
643fec53dd4SToomas Soome 	if (sp->sio == NULL)
644fec53dd4SToomas Soome 		return (false);
64530c75cb0SToomas Soome 
64630c75cb0SToomas Soome 	status = sp->sio->Reset(sp->sio);
647fec53dd4SToomas Soome 	if (EFI_ERROR(status))
648fec53dd4SToomas Soome 		return (false);
64930c75cb0SToomas Soome 
65030c75cb0SToomas Soome 	status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity,
65130c75cb0SToomas Soome 	    sp->databits, sp->stopbits);
652fec53dd4SToomas Soome 	if (EFI_ERROR(status))
653fec53dd4SToomas Soome 		return (false);
65430c75cb0SToomas Soome 
655ada70d03SToomas Soome 	status = sp->sio->GetControl(sp->sio, &control);
656ada70d03SToomas Soome 	if (EFI_ERROR(status))
657ada70d03SToomas Soome 		return (false);
65830c75cb0SToomas Soome 	if (sp->rtsdtr_off) {
65930c75cb0SToomas Soome 		control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
66030c75cb0SToomas Soome 		    EFI_SERIAL_DATA_TERMINAL_READY);
661ada70d03SToomas Soome 	} else {
662ada70d03SToomas Soome 		control |= EFI_SERIAL_REQUEST_TO_SEND;
66330c75cb0SToomas Soome 	}
664ada70d03SToomas Soome 
665ada70d03SToomas Soome 	(void) sp->sio->SetControl(sp->sio, control);
666ada70d03SToomas Soome 
667fec53dd4SToomas Soome 	/* Mark this port usable. */
668fec53dd4SToomas Soome 	cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
669fec53dd4SToomas Soome 	return (true);
67030c75cb0SToomas Soome }
671