2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
30 #include <sys/errno.h>
31 #include <bootstrap.h>
37 #include "loader_efi.h"
39 static EFI_GUID serial = SERIAL_IO_PROTOCOL;
41 #define COMC_TXWAIT 0x40000 /* transmit timeout */
47 #define PNP0501 0x501 /* 16550A-compatible COM port */
52 EFI_PARITY_TYPE parity;
53 EFI_STOP_BITS_TYPE stopbits;
54 uint8_t ignore_cd; /* boolean */
55 uint8_t rtsdtr_off; /* boolean */
56 int ioaddr; /* index in handles array */
57 EFI_HANDLE currdev; /* current serial device */
58 EFI_HANDLE condev; /* EFI Console device */
59 SERIAL_IO_INTERFACE *sio;
62 static void comc_probe(struct console *);
63 static int comc_init(int);
64 static void comc_putchar(int);
65 static int comc_getchar(void);
66 static int comc_ischar(void);
67 static bool comc_setup(void);
68 static int comc_parse_intval(const char *, unsigned *);
69 static int comc_port_set(struct env_var *, int, const void *);
70 static int comc_speed_set(struct env_var *, int, const void *);
72 static struct serial *comc_port;
73 extern struct console efi_console;
75 struct console comconsole = {
76 .c_name = "comconsole",
77 .c_desc = "serial port",
79 .c_probe = comc_probe,
81 .c_out = comc_putchar,
83 .c_ready = comc_ischar,
87 efi_serial_init(EFI_HANDLE **handlep, int *nhandles)
98 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
99 if (status != EFI_BUFFER_TOO_SMALL)
102 if ((handles = malloc(bufsz)) == NULL)
105 *nhandles = (int)(bufsz / sizeof (EFI_HANDLE));
109 status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles);
110 if (EFI_ERROR(status)) {
119 * Find serial device number from device path.
120 * Return -1 if not found.
123 efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx)
125 ACPI_HID_DEVICE_PATH *acpi;
128 while (!IsDevicePathEnd(devpath)) {
129 if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH &&
130 DevicePathSubType(devpath) == MSG_UART_DP)
133 if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
134 (DevicePathSubType(devpath) == ACPI_DP ||
135 DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) {
137 acpi = (ACPI_HID_DEVICE_PATH *)devpath;
138 if (acpi->HID == EISA_PNP_ID(PNP0501)) {
143 devpath = NextDevicePathNode(devpath);
149 * The order of handles from LocateHandle() is not known, we need to
150 * iterate handles, pick device path for handle, and check the device
154 efi_serial_get_handle(int port, EFI_HANDLE condev)
157 EFI_HANDLE *handles, handle;
158 EFI_DEVICE_PATH *devpath;
166 status = efi_serial_init(&handles, &nhandles);
167 if (EFI_ERROR(status))
171 * We have console handle, set ioaddr for it.
173 if (condev != NULL) {
174 for (index = 0; index < nhandles; index++) {
175 if (condev == handles[index]) {
176 devpath = efi_lookup_devpath(condev);
178 efi_serial_get_index(devpath, index);
179 efi_close_devpath(condev);
187 for (index = 0; handle == NULL && index < nhandles; index++) {
188 devpath = efi_lookup_devpath(handles[index]);
189 if (port == efi_serial_get_index(devpath, index))
190 handle = (handles[index]);
191 efi_close_devpath(handles[index]);
195 * In case we did fail to identify the device by path, use port as
196 * array index. Note, we did check port == -1 above.
198 if (port < nhandles && handle == NULL)
199 handle = handles[port];
206 comc_get_con_serial_handle(const char *name)
209 EFI_DEVICE_PATH *node;
216 status = efi_global_getenv(name, buf, &sz);
217 if (status == EFI_BUFFER_TOO_SMALL) {
221 status = efi_global_getenv(name, buf, &sz);
223 if (status != EFI_SUCCESS) {
229 node = (EFI_DEVICE_PATH *)buf;
230 while ((char *)node < ep) {
231 status = BS->LocateDevicePath(&serial, &node, &handle);
232 if (status == EFI_SUCCESS) {
237 /* Sanity check the node before moving to the next node. */
238 if (DevicePathNodeLength(node) < sizeof(*node))
241 /* Start of next device path in list. */
242 node = NextDevicePathNode(node);
249 comc_probe(struct console *sc)
256 char *env, *buf, *ep;
259 if (comc_port == NULL) {
260 comc_port = malloc(sizeof (struct serial));
261 if (comc_port == NULL)
264 comc_port->baudrate = COMSPEED;
265 comc_port->ioaddr = 0; /* default port */
266 comc_port->databits = 8; /* 8,n,1 */
267 comc_port->parity = NoParity; /* 8,n,1 */
268 comc_port->stopbits = OneStopBit; /* 8,n,1 */
269 comc_port->ignore_cd = 1; /* ignore cd */
270 comc_port->rtsdtr_off = 0; /* rts-dtr is on */
271 comc_port->sio = NULL;
274 env = getenv("efi_com_port");
275 if (comc_parse_intval(env, &val) == CMD_OK) {
276 comc_port->ioaddr = val;
279 * efi_com_port is not set, we need to select default.
280 * First, we consult ConOut variable to see if
281 * we have serial port redirection. If not, we just
284 handle = comc_get_con_serial_handle("ConOut");
285 comc_port->condev = handle;
288 handle = efi_serial_get_handle(comc_port->ioaddr, handle);
289 if (handle != NULL) {
290 comc_port->currdev = handle;
291 status = BS->OpenProtocol(handle, &serial,
292 (void**)&comc_port->sio, IH, NULL,
293 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
295 if (EFI_ERROR(status))
296 comc_port->sio = NULL;
300 unsetenv("efi_com_port");
301 snprintf(value, sizeof (value), "%u", comc_port->ioaddr);
302 env_setenv("efi_com_port", EV_VOLATILE, value,
303 comc_port_set, env_nounset);
305 env = getenv("efi_com_speed");
306 if (comc_parse_intval(env, &val) == CMD_OK)
307 comc_port->baudrate = val;
310 unsetenv("efi_com_speed");
311 snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate);
312 env_setenv("efi_com_speed", EV_VOLATILE, value,
313 comc_speed_set, env_nounset);
315 comconsole.c_flags = 0;
317 sc->c_flags = C_PRESENTIN | C_PRESENTOUT;
321 comc_init(int arg __unused)
327 comconsole.c_flags = 0;
339 if (comc_port->sio == NULL)
342 for (wait = COMC_TXWAIT; wait > 0; wait--) {
343 status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb);
344 if (status != EFI_TIMEOUT)
358 * if this device is also used as ConIn, some firmwares
359 * fail to return all input via SIO protocol.
361 if (comc_port->currdev == comc_port->condev) {
362 if ((efi_console.c_flags & C_ACTIVEIN) == 0)
363 return (efi_console.c_in());
367 if (comc_port->sio == NULL)
370 status = comc_port->sio->Read(comc_port->sio, &bufsz, &c);
371 if (EFI_ERROR(status) || bufsz == 0)
384 * if this device is also used as ConIn, some firmwares
385 * fail to return all input via SIO protocol.
387 if (comc_port->currdev == comc_port->condev) {
388 if ((efi_console.c_flags & C_ACTIVEIN) == 0)
389 return (efi_console.c_ready());
393 if (comc_port->sio == NULL)
396 status = comc_port->sio->GetControl(comc_port->sio, &control);
397 if (EFI_ERROR(status))
400 return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY));
404 comc_parse_intval(const char *value, unsigned *valp)
409 if (value == NULL || *value == '\0')
413 n = strtoul(value, &ep, 10);
414 if (errno != 0 || *ep != '\0')
422 comc_port_set(struct env_var *ev, int flags, const void *value)
425 SERIAL_IO_INTERFACE *sio;
432 if (comc_parse_intval(value, &port) != CMD_OK)
435 handle = efi_serial_get_handle(port, NULL);
436 if (handle == NULL) {
437 printf("no handle\n");
441 status = BS->OpenProtocol(handle, &serial,
442 (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
444 if (EFI_ERROR(status)) {
445 printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status));
449 comc_port->currdev = handle;
450 comc_port->ioaddr = port;
451 comc_port->sio = sio;
455 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
460 comc_speed_set(struct env_var *ev, int flags, const void *value)
467 if (comc_parse_intval(value, &speed) != CMD_OK)
470 comc_port->baudrate = speed;
473 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
479 * In case of error, we also reset ACTIVE flags, so the console
480 * framefork will try alternate consoles.
488 /* port is not usable */
489 if (comc_port->sio == NULL)
492 status = comc_port->sio->Reset(comc_port->sio);
493 if (EFI_ERROR(status))
496 status = comc_port->sio->SetAttributes(comc_port->sio,
497 comc_port->baudrate, 0, 0, comc_port->parity,
498 comc_port->databits, comc_port->stopbits);
499 if (EFI_ERROR(status))
502 status = comc_port->sio->GetControl(comc_port->sio, &control);
503 if (EFI_ERROR(status))
505 if (comc_port->rtsdtr_off) {
506 control &= ~(EFI_SERIAL_REQUEST_TO_SEND |
507 EFI_SERIAL_DATA_TERMINAL_READY);
509 control |= EFI_SERIAL_REQUEST_TO_SEND;
511 (void) comc_port->sio->SetControl(comc_port->sio, control);
512 /* Mark this port usable. */
513 comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);