1bab80c12SWarner Losh /*-
2bab80c12SWarner Losh * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
3bab80c12SWarner Losh *
4bab80c12SWarner Losh * Redistribution and use in source and binary forms, with or without
5bab80c12SWarner Losh * modification, are permitted provided that the following conditions
6bab80c12SWarner Losh * are met:
7bab80c12SWarner Losh * 1. Redistributions of source code must retain the above copyright
8bab80c12SWarner Losh * notice, this list of conditions and the following disclaimer.
9bab80c12SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright
10bab80c12SWarner Losh * notice, this list of conditions and the following disclaimer in the
11bab80c12SWarner Losh * documentation and/or other materials provided with the distribution.
12bab80c12SWarner Losh *
13bab80c12SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14bab80c12SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15bab80c12SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16bab80c12SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17bab80c12SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18bab80c12SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19bab80c12SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20bab80c12SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21bab80c12SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22bab80c12SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23bab80c12SWarner Losh * SUCH DAMAGE.
24bab80c12SWarner Losh */
25bab80c12SWarner Losh
26bab80c12SWarner Losh #include <stand.h>
27bab80c12SWarner Losh #include <sys/errno.h>
28bab80c12SWarner Losh #include <bootstrap.h>
29bab80c12SWarner Losh #include <stdbool.h>
30bab80c12SWarner Losh
31bab80c12SWarner Losh #include <efi.h>
32bab80c12SWarner Losh #include <efilib.h>
33bab80c12SWarner Losh
34bab80c12SWarner Losh static EFI_GUID serial = SERIAL_IO_PROTOCOL;
35bab80c12SWarner Losh
36bab80c12SWarner Losh #define COMC_TXWAIT 0x40000 /* transmit timeout */
37bab80c12SWarner Losh
38bab80c12SWarner Losh #define PNP0501 0x501 /* 16550A-compatible COM port */
39bab80c12SWarner Losh
40bab80c12SWarner Losh struct serial {
418c3d6917SWarner Losh uint64_t newbaudrate;
42bab80c12SWarner Losh uint64_t baudrate;
43bab80c12SWarner Losh uint32_t timeout;
44bab80c12SWarner Losh uint32_t receivefifodepth;
45bab80c12SWarner Losh uint32_t databits;
46bab80c12SWarner Losh EFI_PARITY_TYPE parity;
47bab80c12SWarner Losh EFI_STOP_BITS_TYPE stopbits;
48bab80c12SWarner Losh int ioaddr; /* index in handles array */
49bab80c12SWarner Losh EFI_HANDLE currdev; /* current serial device */
50bab80c12SWarner Losh EFI_HANDLE condev; /* EFI Console device */
51bab80c12SWarner Losh SERIAL_IO_INTERFACE *sio;
52bab80c12SWarner Losh };
53bab80c12SWarner Losh
54bab80c12SWarner Losh static void comc_probe(struct console *);
55bab80c12SWarner Losh static int comc_init(int);
56bab80c12SWarner Losh static void comc_putchar(int);
57bab80c12SWarner Losh static int comc_getchar(void);
58bab80c12SWarner Losh static int comc_ischar(void);
59bab80c12SWarner Losh static bool comc_setup(void);
60bab80c12SWarner Losh static int comc_parse_intval(const char *, unsigned *);
61bab80c12SWarner Losh static int comc_port_set(struct env_var *, int, const void *);
62bab80c12SWarner Losh static int comc_speed_set(struct env_var *, int, const void *);
63bab80c12SWarner Losh
64bab80c12SWarner Losh static struct serial *comc_port;
65bab80c12SWarner Losh extern struct console efi_console;
66bab80c12SWarner Losh
67bab80c12SWarner Losh struct console eficom = {
68bab80c12SWarner Losh .c_name = "eficom",
69bab80c12SWarner Losh .c_desc = "serial port",
70bab80c12SWarner Losh .c_flags = 0,
71bab80c12SWarner Losh .c_probe = comc_probe,
72bab80c12SWarner Losh .c_init = comc_init,
73bab80c12SWarner Losh .c_out = comc_putchar,
74bab80c12SWarner Losh .c_in = comc_getchar,
75bab80c12SWarner Losh .c_ready = comc_ischar,
76bab80c12SWarner Losh };
77bab80c12SWarner Losh
78bab80c12SWarner Losh #if defined(__aarch64__) && __FreeBSD_version < 1500000
79bab80c12SWarner Losh static void comc_probe_compat(struct console *);
80bab80c12SWarner Losh struct console comconsole = {
81bab80c12SWarner Losh .c_name = "comconsole",
82bab80c12SWarner Losh .c_desc = "serial port",
83bab80c12SWarner Losh .c_flags = 0,
84bab80c12SWarner Losh .c_probe = comc_probe_compat,
85bab80c12SWarner Losh .c_init = comc_init,
86bab80c12SWarner Losh .c_out = comc_putchar,
87bab80c12SWarner Losh .c_in = comc_getchar,
88bab80c12SWarner Losh .c_ready = comc_ischar,
89bab80c12SWarner Losh };
90bab80c12SWarner Losh #endif
91bab80c12SWarner Losh
92bab80c12SWarner Losh static EFI_STATUS
efi_serial_init(EFI_HANDLE ** handlep,int * nhandles)93bab80c12SWarner Losh efi_serial_init(EFI_HANDLE **handlep, int *nhandles)
94bab80c12SWarner Losh {
95bab80c12SWarner Losh UINTN bufsz = 0;
96bab80c12SWarner Losh EFI_STATUS status;
97bab80c12SWarner Losh EFI_HANDLE *handles;
98bab80c12SWarner Losh
99bab80c12SWarner Losh /*
100bab80c12SWarner Losh * get buffer size
101bab80c12SWarner Losh */
102bab80c12SWarner Losh *nhandles = 0;
103bab80c12SWarner Losh handles = NULL;
104bab80c12SWarner Losh status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
105bab80c12SWarner Losh if (status != EFI_BUFFER_TOO_SMALL)
106bab80c12SWarner Losh return (status);
107bab80c12SWarner Losh
108bab80c12SWarner Losh if ((handles = malloc(bufsz)) == NULL)
109bab80c12SWarner Losh return (ENOMEM);
110bab80c12SWarner Losh
111bab80c12SWarner Losh *nhandles = (int)(bufsz / sizeof (EFI_HANDLE));
112bab80c12SWarner Losh /*
113bab80c12SWarner Losh * get handle array
114bab80c12SWarner Losh */
115bab80c12SWarner Losh status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
116bab80c12SWarner Losh if (EFI_ERROR(status)) {
117bab80c12SWarner Losh free(handles);
118bab80c12SWarner Losh *nhandles = 0;
119bab80c12SWarner Losh } else
120bab80c12SWarner Losh *handlep = handles;
121bab80c12SWarner Losh return (status);
122bab80c12SWarner Losh }
123bab80c12SWarner Losh
124bab80c12SWarner Losh /*
125bab80c12SWarner Losh * Find serial device number from device path.
126bab80c12SWarner Losh * Return -1 if not found.
127bab80c12SWarner Losh */
128bab80c12SWarner Losh static int
efi_serial_get_index(EFI_DEVICE_PATH * devpath,int idx)129bab80c12SWarner Losh efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx)
130bab80c12SWarner Losh {
131bab80c12SWarner Losh ACPI_HID_DEVICE_PATH *acpi;
132bab80c12SWarner Losh CHAR16 *text;
133bab80c12SWarner Losh
134bab80c12SWarner Losh while (!IsDevicePathEnd(devpath)) {
135bab80c12SWarner Losh if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH &&
136bab80c12SWarner Losh DevicePathSubType(devpath) == MSG_UART_DP)
137bab80c12SWarner Losh return (idx);
138bab80c12SWarner Losh
139bab80c12SWarner Losh if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
140bab80c12SWarner Losh (DevicePathSubType(devpath) == ACPI_DP ||
141bab80c12SWarner Losh DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) {
142bab80c12SWarner Losh
143bab80c12SWarner Losh acpi = (ACPI_HID_DEVICE_PATH *)devpath;
144bab80c12SWarner Losh if (acpi->HID == EISA_PNP_ID(PNP0501)) {
145bab80c12SWarner Losh return (acpi->UID);
146bab80c12SWarner Losh }
147bab80c12SWarner Losh }
148bab80c12SWarner Losh
149bab80c12SWarner Losh devpath = NextDevicePathNode(devpath);
150bab80c12SWarner Losh }
151bab80c12SWarner Losh return (-1);
152bab80c12SWarner Losh }
153bab80c12SWarner Losh
154bab80c12SWarner Losh /*
155bab80c12SWarner Losh * The order of handles from LocateHandle() is not known, we need to
156bab80c12SWarner Losh * iterate handles, pick device path for handle, and check the device
157bab80c12SWarner Losh * number.
158bab80c12SWarner Losh */
159bab80c12SWarner Losh static EFI_HANDLE
efi_serial_get_handle(int port,EFI_HANDLE condev)160bab80c12SWarner Losh efi_serial_get_handle(int port, EFI_HANDLE condev)
161bab80c12SWarner Losh {
162bab80c12SWarner Losh EFI_STATUS status;
163bab80c12SWarner Losh EFI_HANDLE *handles, handle;
164bab80c12SWarner Losh EFI_DEVICE_PATH *devpath;
165bab80c12SWarner Losh int index, nhandles;
166bab80c12SWarner Losh
167bab80c12SWarner Losh if (port == -1)
168bab80c12SWarner Losh return (NULL);
169bab80c12SWarner Losh
170bab80c12SWarner Losh handles = NULL;
171bab80c12SWarner Losh nhandles = 0;
172bab80c12SWarner Losh status = efi_serial_init(&handles, &nhandles);
173bab80c12SWarner Losh if (EFI_ERROR(status))
174bab80c12SWarner Losh return (NULL);
175bab80c12SWarner Losh
176bab80c12SWarner Losh /*
177bab80c12SWarner Losh * We have console handle, set ioaddr for it.
178bab80c12SWarner Losh */
179bab80c12SWarner Losh if (condev != NULL) {
180bab80c12SWarner Losh for (index = 0; index < nhandles; index++) {
181bab80c12SWarner Losh if (condev == handles[index]) {
182bab80c12SWarner Losh devpath = efi_lookup_devpath(condev);
183bab80c12SWarner Losh comc_port->ioaddr =
184bab80c12SWarner Losh efi_serial_get_index(devpath, index);
185bab80c12SWarner Losh efi_close_devpath(condev);
186bab80c12SWarner Losh free(handles);
187bab80c12SWarner Losh return (condev);
188bab80c12SWarner Losh }
189bab80c12SWarner Losh }
190bab80c12SWarner Losh }
191bab80c12SWarner Losh
192bab80c12SWarner Losh handle = NULL;
193bab80c12SWarner Losh for (index = 0; handle == NULL && index < nhandles; index++) {
194bab80c12SWarner Losh devpath = efi_lookup_devpath(handles[index]);
195bab80c12SWarner Losh if (port == efi_serial_get_index(devpath, index))
196bab80c12SWarner Losh handle = (handles[index]);
197bab80c12SWarner Losh efi_close_devpath(handles[index]);
198bab80c12SWarner Losh }
199bab80c12SWarner Losh
200bab80c12SWarner Losh /*
201bab80c12SWarner Losh * In case we did fail to identify the device by path, use port as
202bab80c12SWarner Losh * array index. Note, we did check port == -1 above.
203bab80c12SWarner Losh */
204bab80c12SWarner Losh if (port < nhandles && handle == NULL)
205bab80c12SWarner Losh handle = handles[port];
206bab80c12SWarner Losh
207bab80c12SWarner Losh free(handles);
208bab80c12SWarner Losh return (handle);
209bab80c12SWarner Losh }
210bab80c12SWarner Losh
211bab80c12SWarner Losh static EFI_HANDLE
comc_get_con_serial_handle(const char * name)212bab80c12SWarner Losh comc_get_con_serial_handle(const char *name)
213bab80c12SWarner Losh {
214bab80c12SWarner Losh EFI_HANDLE handle;
215bab80c12SWarner Losh EFI_DEVICE_PATH *node;
216bab80c12SWarner Losh EFI_STATUS status;
217bab80c12SWarner Losh char *buf, *ep;
218bab80c12SWarner Losh size_t sz;
219bab80c12SWarner Losh
220bab80c12SWarner Losh buf = NULL;
221bab80c12SWarner Losh sz = 0;
222bab80c12SWarner Losh status = efi_global_getenv(name, buf, &sz);
223bab80c12SWarner Losh if (status == EFI_BUFFER_TOO_SMALL) {
224bab80c12SWarner Losh buf = malloc(sz);
225bab80c12SWarner Losh if (buf == NULL)
226bab80c12SWarner Losh return (NULL);
227bab80c12SWarner Losh status = efi_global_getenv(name, buf, &sz);
228bab80c12SWarner Losh }
229bab80c12SWarner Losh if (status != EFI_SUCCESS) {
230bab80c12SWarner Losh free(buf);
231bab80c12SWarner Losh return (NULL);
232bab80c12SWarner Losh }
233bab80c12SWarner Losh
234bab80c12SWarner Losh ep = buf + sz;
235bab80c12SWarner Losh node = (EFI_DEVICE_PATH *)buf;
236bab80c12SWarner Losh while ((char *)node < ep) {
237bab80c12SWarner Losh status = BS->LocateDevicePath(&serial, &node, &handle);
238bab80c12SWarner Losh if (status == EFI_SUCCESS) {
239bab80c12SWarner Losh free(buf);
240bab80c12SWarner Losh return (handle);
241bab80c12SWarner Losh }
242bab80c12SWarner Losh
243bab80c12SWarner Losh /* Sanity check the node before moving to the next node. */
244bab80c12SWarner Losh if (DevicePathNodeLength(node) < sizeof(*node))
245bab80c12SWarner Losh break;
246bab80c12SWarner Losh
247bab80c12SWarner Losh /* Start of next device path in list. */
248bab80c12SWarner Losh node = NextDevicePathNode(node);
249bab80c12SWarner Losh }
250bab80c12SWarner Losh free(buf);
251bab80c12SWarner Losh return (NULL);
252bab80c12SWarner Losh }
253bab80c12SWarner Losh
25442b0b7a9SWarner Losh /*
25542b0b7a9SWarner Losh * Called from cons_probe() to see if this device is available.
25642b0b7a9SWarner Losh * Return immediately on x86, except for hyperv, since it interferes with
25742b0b7a9SWarner Losh * common configurations otherwise (yes, this is just firewalling the bug).
25842b0b7a9SWarner Losh */
259bab80c12SWarner Losh static void
comc_probe(struct console * sc)260bab80c12SWarner Losh comc_probe(struct console *sc)
261bab80c12SWarner Losh {
262bab80c12SWarner Losh EFI_STATUS status;
263bab80c12SWarner Losh EFI_HANDLE handle;
264bab80c12SWarner Losh char name[20];
265bab80c12SWarner Losh char value[20];
266bab80c12SWarner Losh unsigned val;
267bab80c12SWarner Losh char *env, *buf, *ep;
268bab80c12SWarner Losh size_t sz;
269bab80c12SWarner Losh
27042b0b7a9SWarner Losh #ifdef __amd64__
27142b0b7a9SWarner Losh /*
27242b0b7a9SWarner Losh * This driver tickles issues on a number of different firmware loads.
27342b0b7a9SWarner Losh * It is only required for HyperV, and is only known to work on HyperV,
27442b0b7a9SWarner Losh * so only allow it on HyperV.
27542b0b7a9SWarner Losh */
27642b0b7a9SWarner Losh env = getenv("smbios.bios.version");
27742b0b7a9SWarner Losh if (env == NULL || strncmp(env, "Hyper-V", 7) != 0) {
27842b0b7a9SWarner Losh return;
27942b0b7a9SWarner Losh }
28042b0b7a9SWarner Losh #endif
28142b0b7a9SWarner Losh
282bab80c12SWarner Losh if (comc_port == NULL) {
283bab80c12SWarner Losh comc_port = calloc(1, sizeof (struct serial));
284bab80c12SWarner Losh if (comc_port == NULL)
285bab80c12SWarner Losh return;
286bab80c12SWarner Losh }
287bab80c12SWarner Losh
288bab80c12SWarner Losh /* Use defaults from firmware */
289bab80c12SWarner Losh comc_port->databits = 8;
290bab80c12SWarner Losh comc_port->parity = DefaultParity;
291bab80c12SWarner Losh comc_port->stopbits = DefaultStopBits;
292bab80c12SWarner Losh
293bab80c12SWarner Losh handle = NULL;
294bab80c12SWarner Losh env = getenv("efi_com_port");
295bab80c12SWarner Losh if (comc_parse_intval(env, &val) == CMD_OK) {
296bab80c12SWarner Losh comc_port->ioaddr = val;
297bab80c12SWarner Losh } else {
298bab80c12SWarner Losh /*
299bab80c12SWarner Losh * efi_com_port is not set, we need to select default.
300bab80c12SWarner Losh * First, we consult ConOut variable to see if
301bab80c12SWarner Losh * we have serial port redirection. If not, we just
302bab80c12SWarner Losh * pick first device.
303bab80c12SWarner Losh */
304bab80c12SWarner Losh handle = comc_get_con_serial_handle("ConOut");
305bab80c12SWarner Losh comc_port->condev = handle;
306bab80c12SWarner Losh }
307bab80c12SWarner Losh
308bab80c12SWarner Losh handle = efi_serial_get_handle(comc_port->ioaddr, handle);
309bab80c12SWarner Losh if (handle != NULL) {
310bab80c12SWarner Losh comc_port->currdev = handle;
311bab80c12SWarner Losh status = BS->OpenProtocol(handle, &serial,
312bab80c12SWarner Losh (void**)&comc_port->sio, IH, NULL,
313bab80c12SWarner Losh EFI_OPEN_PROTOCOL_GET_PROTOCOL);
314bab80c12SWarner Losh
315bab80c12SWarner Losh if (EFI_ERROR(status)) {
316bab80c12SWarner Losh comc_port->sio = NULL;
317bab80c12SWarner Losh } else {
3188c3d6917SWarner Losh comc_port->newbaudrate =
319bab80c12SWarner Losh comc_port->baudrate = comc_port->sio->Mode->BaudRate;
320bab80c12SWarner Losh comc_port->timeout = comc_port->sio->Mode->Timeout;
321bab80c12SWarner Losh comc_port->receivefifodepth =
322bab80c12SWarner Losh comc_port->sio->Mode->ReceiveFifoDepth;
323bab80c12SWarner Losh comc_port->databits = comc_port->sio->Mode->DataBits;
324bab80c12SWarner Losh comc_port->parity = comc_port->sio->Mode->Parity;
325bab80c12SWarner Losh comc_port->stopbits = comc_port->sio->Mode->StopBits;
326bab80c12SWarner Losh }
327bab80c12SWarner Losh }
328bab80c12SWarner Losh
32946927f67SWarner Losh /*
33046927f67SWarner Losh * If there's no sio, then the device isn't there, so just return since
33146927f67SWarner Losh * the present flags aren't yet set.
33246927f67SWarner Losh */
33346927f67SWarner Losh if (comc_port->sio == NULL) {
33446927f67SWarner Losh free(comc_port);
33546927f67SWarner Losh comc_port = NULL;
33646927f67SWarner Losh return;
33746927f67SWarner Losh }
33846927f67SWarner Losh
339bab80c12SWarner Losh if (env != NULL)
340bab80c12SWarner Losh unsetenv("efi_com_port");
341bab80c12SWarner Losh snprintf(value, sizeof (value), "%u", comc_port->ioaddr);
342bab80c12SWarner Losh env_setenv("efi_com_port", EV_VOLATILE, value,
343bab80c12SWarner Losh comc_port_set, env_nounset);
344bab80c12SWarner Losh
345bab80c12SWarner Losh env = getenv("efi_com_speed");
346bab80c12SWarner Losh if (env == NULL)
347bab80c12SWarner Losh /* fallback to comconsole setting */
348bab80c12SWarner Losh env = getenv("comconsole_speed");
349bab80c12SWarner Losh
350bab80c12SWarner Losh if (comc_parse_intval(env, &val) == CMD_OK)
3518c3d6917SWarner Losh comc_port->newbaudrate = val;
352bab80c12SWarner Losh
353bab80c12SWarner Losh if (env != NULL)
354bab80c12SWarner Losh unsetenv("efi_com_speed");
355bab80c12SWarner Losh snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate);
356bab80c12SWarner Losh env_setenv("efi_com_speed", EV_VOLATILE, value,
357bab80c12SWarner Losh comc_speed_set, env_nounset);
358bab80c12SWarner Losh
359bab80c12SWarner Losh if (comc_setup()) {
360bab80c12SWarner Losh sc->c_flags = C_PRESENTIN | C_PRESENTOUT;
361f28dff43SWarner Losh } else {
362f28dff43SWarner Losh sc->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
363f28dff43SWarner Losh free(comc_port);
364f28dff43SWarner Losh comc_port = NULL;
365bab80c12SWarner Losh }
366bab80c12SWarner Losh }
367bab80c12SWarner Losh
368bab80c12SWarner Losh #if defined(__aarch64__) && __FreeBSD_version < 1500000
369bab80c12SWarner Losh static void
comc_probe_compat(struct console * sc)370bab80c12SWarner Losh comc_probe_compat(struct console *sc)
371bab80c12SWarner Losh {
372e5d4e036SWarner Losh comc_probe(&eficom);
373e5d4e036SWarner Losh if (eficom.c_flags & (C_PRESENTIN | C_PRESENTOUT)) {
374bab80c12SWarner Losh printf("comconsole: comconsole device name is deprecated, switch to eficom\n");
375bab80c12SWarner Losh }
376e5d4e036SWarner Losh /*
377e5d4e036SWarner Losh * Note: We leave the present bits unset in sc to avoid ghosting.
378e5d4e036SWarner Losh */
379bab80c12SWarner Losh }
380bab80c12SWarner Losh #endif
381bab80c12SWarner Losh
382f28dff43SWarner Losh /*
383f28dff43SWarner Losh * Called when the console is selected in cons_change. If we didn't detect the
384f28dff43SWarner Losh * device, comc_port will be NULL, and comc_setup will fail. It may be called
385f28dff43SWarner Losh * even when the device isn't present as a 'fallback' console or when listed
386f28dff43SWarner Losh * specifically in console env, so we have to reset the c_flags in those case to
387f28dff43SWarner Losh * say it's not present.
388f28dff43SWarner Losh */
389bab80c12SWarner Losh static int
comc_init(int arg __unused)390bab80c12SWarner Losh comc_init(int arg __unused)
391bab80c12SWarner Losh {
392bab80c12SWarner Losh if (comc_setup())
393*2d425b63SWarner Losh return (0);
394bab80c12SWarner Losh
395f28dff43SWarner Losh eficom.c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT);
396*2d425b63SWarner Losh return (1);
397bab80c12SWarner Losh }
398bab80c12SWarner Losh
399bab80c12SWarner Losh static void
comc_putchar(int c)400bab80c12SWarner Losh comc_putchar(int c)
401bab80c12SWarner Losh {
402bab80c12SWarner Losh int wait;
403bab80c12SWarner Losh EFI_STATUS status;
404bab80c12SWarner Losh UINTN bufsz = 1;
405bab80c12SWarner Losh char cb = c;
406bab80c12SWarner Losh
407bab80c12SWarner Losh if (comc_port->sio == NULL)
408bab80c12SWarner Losh return;
409bab80c12SWarner Losh
410bab80c12SWarner Losh for (wait = COMC_TXWAIT; wait > 0; wait--) {
411bab80c12SWarner Losh status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb);
412bab80c12SWarner Losh if (status != EFI_TIMEOUT)
413bab80c12SWarner Losh break;
414bab80c12SWarner Losh }
415bab80c12SWarner Losh }
416bab80c12SWarner Losh
417bab80c12SWarner Losh static int
comc_getchar(void)418bab80c12SWarner Losh comc_getchar(void)
419bab80c12SWarner Losh {
420bab80c12SWarner Losh EFI_STATUS status;
421bab80c12SWarner Losh UINTN bufsz = 1;
422bab80c12SWarner Losh char c;
423bab80c12SWarner Losh
424bab80c12SWarner Losh
425bab80c12SWarner Losh /*
426bab80c12SWarner Losh * if this device is also used as ConIn, some firmwares
427bab80c12SWarner Losh * fail to return all input via SIO protocol.
428bab80c12SWarner Losh */
429bab80c12SWarner Losh if (comc_port->currdev == comc_port->condev) {
430bab80c12SWarner Losh if ((efi_console.c_flags & C_ACTIVEIN) == 0)
431bab80c12SWarner Losh return (efi_console.c_in());
432bab80c12SWarner Losh return (-1);
433bab80c12SWarner Losh }
434bab80c12SWarner Losh
435bab80c12SWarner Losh if (comc_port->sio == NULL)
436bab80c12SWarner Losh return (-1);
437bab80c12SWarner Losh
438bab80c12SWarner Losh status = comc_port->sio->Read(comc_port->sio, &bufsz, &c);
439bab80c12SWarner Losh if (EFI_ERROR(status) || bufsz == 0)
440bab80c12SWarner Losh return (-1);
441bab80c12SWarner Losh
442bab80c12SWarner Losh return (c);
443bab80c12SWarner Losh }
444bab80c12SWarner Losh
445bab80c12SWarner Losh static int
comc_ischar(void)446bab80c12SWarner Losh comc_ischar(void)
447bab80c12SWarner Losh {
448bab80c12SWarner Losh EFI_STATUS status;
449bab80c12SWarner Losh uint32_t control;
450bab80c12SWarner Losh
451bab80c12SWarner Losh /*
452bab80c12SWarner Losh * if this device is also used as ConIn, some firmwares
453bab80c12SWarner Losh * fail to return all input via SIO protocol.
454bab80c12SWarner Losh */
455bab80c12SWarner Losh if (comc_port->currdev == comc_port->condev) {
456bab80c12SWarner Losh if ((efi_console.c_flags & C_ACTIVEIN) == 0)
457bab80c12SWarner Losh return (efi_console.c_ready());
458bab80c12SWarner Losh return (0);
459bab80c12SWarner Losh }
460bab80c12SWarner Losh
461bab80c12SWarner Losh if (comc_port->sio == NULL)
462bab80c12SWarner Losh return (0);
463bab80c12SWarner Losh
464bab80c12SWarner Losh status = comc_port->sio->GetControl(comc_port->sio, &control);
465bab80c12SWarner Losh if (EFI_ERROR(status))
466bab80c12SWarner Losh return (0);
467bab80c12SWarner Losh
468bab80c12SWarner Losh return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
469bab80c12SWarner Losh }
470bab80c12SWarner Losh
471bab80c12SWarner Losh static int
comc_parse_intval(const char * value,unsigned * valp)472bab80c12SWarner Losh comc_parse_intval(const char *value, unsigned *valp)
473bab80c12SWarner Losh {
474bab80c12SWarner Losh unsigned n;
475bab80c12SWarner Losh char *ep;
476bab80c12SWarner Losh
477bab80c12SWarner Losh if (value == NULL || *value == '\0')
478bab80c12SWarner Losh return (CMD_ERROR);
479bab80c12SWarner Losh
480bab80c12SWarner Losh errno = 0;
481bab80c12SWarner Losh n = strtoul(value, &ep, 10);
482bab80c12SWarner Losh if (errno != 0 || *ep != '\0')
483bab80c12SWarner Losh return (CMD_ERROR);
484bab80c12SWarner Losh *valp = n;
485bab80c12SWarner Losh
486bab80c12SWarner Losh return (CMD_OK);
487bab80c12SWarner Losh }
488bab80c12SWarner Losh
489bab80c12SWarner Losh static int
comc_port_set(struct env_var * ev,int flags,const void * value)490bab80c12SWarner Losh comc_port_set(struct env_var *ev, int flags, const void *value)
491bab80c12SWarner Losh {
492bab80c12SWarner Losh unsigned port;
493bab80c12SWarner Losh SERIAL_IO_INTERFACE *sio;
494bab80c12SWarner Losh EFI_HANDLE handle;
495bab80c12SWarner Losh EFI_STATUS status;
496bab80c12SWarner Losh
4979ed4ec4aSKyle Evans if (value == NULL || comc_port == NULL)
498bab80c12SWarner Losh return (CMD_ERROR);
499bab80c12SWarner Losh
500bab80c12SWarner Losh if (comc_parse_intval(value, &port) != CMD_OK)
501bab80c12SWarner Losh return (CMD_ERROR);
502bab80c12SWarner Losh
503bab80c12SWarner Losh handle = efi_serial_get_handle(port, NULL);
504bab80c12SWarner Losh if (handle == NULL) {
505bab80c12SWarner Losh printf("no handle\n");
506bab80c12SWarner Losh return (CMD_ERROR);
507bab80c12SWarner Losh }
508bab80c12SWarner Losh
509bab80c12SWarner Losh status = BS->OpenProtocol(handle, &serial,
510bab80c12SWarner Losh (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
511bab80c12SWarner Losh
512bab80c12SWarner Losh if (EFI_ERROR(status)) {
513bab80c12SWarner Losh printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status));
514bab80c12SWarner Losh return (CMD_ERROR);
515bab80c12SWarner Losh }
516bab80c12SWarner Losh
517bab80c12SWarner Losh comc_port->currdev = handle;
518bab80c12SWarner Losh comc_port->ioaddr = port;
519bab80c12SWarner Losh comc_port->sio = sio;
520bab80c12SWarner Losh
521bab80c12SWarner Losh (void) comc_setup();
522bab80c12SWarner Losh
523bab80c12SWarner Losh env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
524bab80c12SWarner Losh return (CMD_OK);
525bab80c12SWarner Losh }
526bab80c12SWarner Losh
527bab80c12SWarner Losh static int
comc_speed_set(struct env_var * ev,int flags,const void * value)528bab80c12SWarner Losh comc_speed_set(struct env_var *ev, int flags, const void *value)
529bab80c12SWarner Losh {
530bab80c12SWarner Losh unsigned speed;
531bab80c12SWarner Losh
5329ed4ec4aSKyle Evans if (value == NULL || comc_port == NULL)
533bab80c12SWarner Losh return (CMD_ERROR);
534bab80c12SWarner Losh
535bab80c12SWarner Losh if (comc_parse_intval(value, &speed) != CMD_OK)
536bab80c12SWarner Losh return (CMD_ERROR);
537bab80c12SWarner Losh
5388c3d6917SWarner Losh comc_port->newbaudrate = speed;
5398c3d6917SWarner Losh if (comc_setup())
540bab80c12SWarner Losh env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
541bab80c12SWarner Losh
542bab80c12SWarner Losh return (CMD_OK);
543bab80c12SWarner Losh }
544bab80c12SWarner Losh
545bab80c12SWarner Losh /*
546bab80c12SWarner Losh * In case of error, we also reset ACTIVE flags, so the console
547bab80c12SWarner Losh * framefork will try alternate consoles.
548bab80c12SWarner Losh */
549bab80c12SWarner Losh static bool
comc_setup(void)550bab80c12SWarner Losh comc_setup(void)
551bab80c12SWarner Losh {
552bab80c12SWarner Losh EFI_STATUS status;
553bab80c12SWarner Losh char *ev;
554bab80c12SWarner Losh
555f28dff43SWarner Losh /*
556f28dff43SWarner Losh * If the device isn't active, or there's no port present.
557f28dff43SWarner Losh */
558f28dff43SWarner Losh if ((eficom.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0 || comc_port == NULL)
559bab80c12SWarner Losh return (false);
560bab80c12SWarner Losh
561bab80c12SWarner Losh if (comc_port->sio->Reset != NULL) {
562bab80c12SWarner Losh status = comc_port->sio->Reset(comc_port->sio);
563bab80c12SWarner Losh if (EFI_ERROR(status))
564bab80c12SWarner Losh return (false);
565bab80c12SWarner Losh }
566bab80c12SWarner Losh
5678c3d6917SWarner Losh /*
5688c3d6917SWarner Losh * Avoid setting the baud rate on Hyper-V. Also, only set the baud rate
5698c3d6917SWarner Losh * if the baud rate has changed from the default. And pass in '0' or
5708c3d6917SWarner Losh * DefaultFoo when we're not changing those values. Some EFI
5718c3d6917SWarner Losh * implementations get cranky when you set things to the values reported
5728c3d6917SWarner Losh * back even when they are unchanged.
5738c3d6917SWarner Losh */
5748c3d6917SWarner Losh if (comc_port->sio->SetAttributes != NULL &&
5758c3d6917SWarner Losh comc_port->newbaudrate != comc_port->baudrate) {
576bab80c12SWarner Losh ev = getenv("smbios.bios.version");
5778c3d6917SWarner Losh if (ev != NULL && strncmp(ev, "Hyper-V", 7) != 0) {
578bab80c12SWarner Losh status = comc_port->sio->SetAttributes(comc_port->sio,
5798c3d6917SWarner Losh comc_port->newbaudrate, 0, 0, DefaultParity, 0,
5808c3d6917SWarner Losh DefaultStopBits);
581bab80c12SWarner Losh if (EFI_ERROR(status))
582bab80c12SWarner Losh return (false);
5838c3d6917SWarner Losh comc_port->baudrate = comc_port->newbaudrate;
5848c3d6917SWarner Losh }
585bab80c12SWarner Losh }
586bab80c12SWarner Losh
587cb2da749SWarner Losh #ifdef EFI_FORCE_RTS
588bab80c12SWarner Losh if (comc_port->sio->GetControl != NULL && comc_port->sio->SetControl != NULL) {
589cb2da749SWarner Losh UINT32 control;
590cb2da749SWarner Losh
591bab80c12SWarner Losh status = comc_port->sio->GetControl(comc_port->sio, &control);
592bab80c12SWarner Losh if (EFI_ERROR(status))
593bab80c12SWarner Losh return (false);
594bab80c12SWarner Losh control |= EFI_SERIAL_REQUEST_TO_SEND;
595bab80c12SWarner Losh (void) comc_port->sio->SetControl(comc_port->sio, control);
596bab80c12SWarner Losh }
597cb2da749SWarner Losh #endif
598bab80c12SWarner Losh /* Mark this port usable. */
599bab80c12SWarner Losh eficom.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
600bab80c12SWarner Losh return (true);
601bab80c12SWarner Losh }
602