diff --git a/stand/efi/libefi/eficom.c b/stand/efi/libefi/eficom.c index d5f3f07e083f..f1ce14eb50f8 100644 --- a/stand/efi/libefi/eficom.c +++ b/stand/efi/libefi/eficom.c @@ -1,601 +1,601 @@ /*- * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include static EFI_GUID serial = SERIAL_IO_PROTOCOL; #define COMC_TXWAIT 0x40000 /* transmit timeout */ #define PNP0501 0x501 /* 16550A-compatible COM port */ struct serial { uint64_t newbaudrate; uint64_t baudrate; uint32_t timeout; uint32_t receivefifodepth; uint32_t databits; EFI_PARITY_TYPE parity; EFI_STOP_BITS_TYPE stopbits; int ioaddr; /* index in handles array */ EFI_HANDLE currdev; /* current serial device */ EFI_HANDLE condev; /* EFI Console device */ SERIAL_IO_INTERFACE *sio; }; static void comc_probe(struct console *); static int comc_init(int); static void comc_putchar(int); static int comc_getchar(void); static int comc_ischar(void); static bool comc_setup(void); static int comc_parse_intval(const char *, unsigned *); static int comc_port_set(struct env_var *, int, const void *); static int comc_speed_set(struct env_var *, int, const void *); static struct serial *comc_port; extern struct console efi_console; struct console eficom = { .c_name = "eficom", .c_desc = "serial port", .c_flags = 0, .c_probe = comc_probe, .c_init = comc_init, .c_out = comc_putchar, .c_in = comc_getchar, .c_ready = comc_ischar, }; #if defined(__aarch64__) && __FreeBSD_version < 1500000 static void comc_probe_compat(struct console *); struct console comconsole = { .c_name = "comconsole", .c_desc = "serial port", .c_flags = 0, .c_probe = comc_probe_compat, .c_init = comc_init, .c_out = comc_putchar, .c_in = comc_getchar, .c_ready = comc_ischar, }; #endif static EFI_STATUS efi_serial_init(EFI_HANDLE **handlep, int *nhandles) { UINTN bufsz = 0; EFI_STATUS status; EFI_HANDLE *handles; /* * get buffer size */ *nhandles = 0; handles = NULL; status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); if (status != EFI_BUFFER_TOO_SMALL) return (status); if ((handles = malloc(bufsz)) == NULL) return (ENOMEM); *nhandles = (int)(bufsz / sizeof (EFI_HANDLE)); /* * get handle array */ status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); if (EFI_ERROR(status)) { free(handles); *nhandles = 0; } else *handlep = handles; return (status); } /* * Find serial device number from device path. * Return -1 if not found. */ static int efi_serial_get_index(EFI_DEVICE_PATH *devpath, int idx) { ACPI_HID_DEVICE_PATH *acpi; CHAR16 *text; while (!IsDevicePathEnd(devpath)) { if (DevicePathType(devpath) == MESSAGING_DEVICE_PATH && DevicePathSubType(devpath) == MSG_UART_DP) return (idx); if (DevicePathType(devpath) == ACPI_DEVICE_PATH && (DevicePathSubType(devpath) == ACPI_DP || DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) { acpi = (ACPI_HID_DEVICE_PATH *)devpath; if (acpi->HID == EISA_PNP_ID(PNP0501)) { return (acpi->UID); } } devpath = NextDevicePathNode(devpath); } return (-1); } /* * The order of handles from LocateHandle() is not known, we need to * iterate handles, pick device path for handle, and check the device * number. */ static EFI_HANDLE efi_serial_get_handle(int port, EFI_HANDLE condev) { EFI_STATUS status; EFI_HANDLE *handles, handle; EFI_DEVICE_PATH *devpath; int index, nhandles; if (port == -1) return (NULL); handles = NULL; nhandles = 0; status = efi_serial_init(&handles, &nhandles); if (EFI_ERROR(status)) return (NULL); /* * We have console handle, set ioaddr for it. */ if (condev != NULL) { for (index = 0; index < nhandles; index++) { if (condev == handles[index]) { devpath = efi_lookup_devpath(condev); comc_port->ioaddr = efi_serial_get_index(devpath, index); efi_close_devpath(condev); free(handles); return (condev); } } } handle = NULL; for (index = 0; handle == NULL && index < nhandles; index++) { devpath = efi_lookup_devpath(handles[index]); if (port == efi_serial_get_index(devpath, index)) handle = (handles[index]); efi_close_devpath(handles[index]); } /* * In case we did fail to identify the device by path, use port as * array index. Note, we did check port == -1 above. */ if (port < nhandles && handle == NULL) handle = handles[port]; free(handles); return (handle); } static EFI_HANDLE comc_get_con_serial_handle(const char *name) { EFI_HANDLE handle; EFI_DEVICE_PATH *node; EFI_STATUS status; char *buf, *ep; size_t sz; buf = NULL; sz = 0; status = efi_global_getenv(name, buf, &sz); if (status == EFI_BUFFER_TOO_SMALL) { buf = malloc(sz); if (buf == NULL) return (NULL); status = efi_global_getenv(name, buf, &sz); } if (status != EFI_SUCCESS) { free(buf); return (NULL); } ep = buf + sz; node = (EFI_DEVICE_PATH *)buf; while ((char *)node < ep) { status = BS->LocateDevicePath(&serial, &node, &handle); if (status == EFI_SUCCESS) { free(buf); return (handle); } /* Sanity check the node before moving to the next node. */ if (DevicePathNodeLength(node) < sizeof(*node)) break; /* Start of next device path in list. */ node = NextDevicePathNode(node); } free(buf); return (NULL); } /* * Called from cons_probe() to see if this device is available. * Return immediately on x86, except for hyperv, since it interferes with * common configurations otherwise (yes, this is just firewalling the bug). */ static void comc_probe(struct console *sc) { EFI_STATUS status; EFI_HANDLE handle; char name[20]; char value[20]; unsigned val; char *env, *buf, *ep; size_t sz; #ifdef __amd64__ /* * This driver tickles issues on a number of different firmware loads. * It is only required for HyperV, and is only known to work on HyperV, * so only allow it on HyperV. */ env = getenv("smbios.bios.version"); if (env == NULL || strncmp(env, "Hyper-V", 7) != 0) { return; } #endif if (comc_port == NULL) { comc_port = calloc(1, sizeof (struct serial)); if (comc_port == NULL) return; } /* Use defaults from firmware */ comc_port->databits = 8; comc_port->parity = DefaultParity; comc_port->stopbits = DefaultStopBits; handle = NULL; env = getenv("efi_com_port"); if (comc_parse_intval(env, &val) == CMD_OK) { comc_port->ioaddr = val; } else { /* * efi_com_port is not set, we need to select default. * First, we consult ConOut variable to see if * we have serial port redirection. If not, we just * pick first device. */ handle = comc_get_con_serial_handle("ConOut"); comc_port->condev = handle; } handle = efi_serial_get_handle(comc_port->ioaddr, handle); if (handle != NULL) { comc_port->currdev = handle; status = BS->OpenProtocol(handle, &serial, (void**)&comc_port->sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) { comc_port->sio = NULL; } else { comc_port->newbaudrate = comc_port->baudrate = comc_port->sio->Mode->BaudRate; comc_port->timeout = comc_port->sio->Mode->Timeout; comc_port->receivefifodepth = comc_port->sio->Mode->ReceiveFifoDepth; comc_port->databits = comc_port->sio->Mode->DataBits; comc_port->parity = comc_port->sio->Mode->Parity; comc_port->stopbits = comc_port->sio->Mode->StopBits; } } /* * If there's no sio, then the device isn't there, so just return since * the present flags aren't yet set. */ if (comc_port->sio == NULL) { free(comc_port); comc_port = NULL; return; } if (env != NULL) unsetenv("efi_com_port"); snprintf(value, sizeof (value), "%u", comc_port->ioaddr); env_setenv("efi_com_port", EV_VOLATILE, value, comc_port_set, env_nounset); env = getenv("efi_com_speed"); if (env == NULL) /* fallback to comconsole setting */ env = getenv("comconsole_speed"); if (comc_parse_intval(env, &val) == CMD_OK) comc_port->newbaudrate = val; if (env != NULL) unsetenv("efi_com_speed"); snprintf(value, sizeof (value), "%ju", (uintmax_t)comc_port->baudrate); env_setenv("efi_com_speed", EV_VOLATILE, value, comc_speed_set, env_nounset); if (comc_setup()) { sc->c_flags = C_PRESENTIN | C_PRESENTOUT; } else { sc->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); free(comc_port); comc_port = NULL; } } #if defined(__aarch64__) && __FreeBSD_version < 1500000 static void comc_probe_compat(struct console *sc) { comc_probe(&eficom); if (eficom.c_flags & (C_PRESENTIN | C_PRESENTOUT)) { printf("comconsole: comconsole device name is deprecated, switch to eficom\n"); } /* * Note: We leave the present bits unset in sc to avoid ghosting. */ } #endif /* * Called when the console is selected in cons_change. If we didn't detect the * device, comc_port will be NULL, and comc_setup will fail. It may be called * even when the device isn't present as a 'fallback' console or when listed * specifically in console env, so we have to reset the c_flags in those case to * say it's not present. */ static int comc_init(int arg __unused) { if (comc_setup()) - return (CMD_OK); + return (0); eficom.c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); - return (CMD_ERROR); + return (1); } static void comc_putchar(int c) { int wait; EFI_STATUS status; UINTN bufsz = 1; char cb = c; if (comc_port->sio == NULL) return; for (wait = COMC_TXWAIT; wait > 0; wait--) { status = comc_port->sio->Write(comc_port->sio, &bufsz, &cb); if (status != EFI_TIMEOUT) break; } } static int comc_getchar(void) { EFI_STATUS status; UINTN bufsz = 1; char c; /* * if this device is also used as ConIn, some firmwares * fail to return all input via SIO protocol. */ if (comc_port->currdev == comc_port->condev) { if ((efi_console.c_flags & C_ACTIVEIN) == 0) return (efi_console.c_in()); return (-1); } if (comc_port->sio == NULL) return (-1); status = comc_port->sio->Read(comc_port->sio, &bufsz, &c); if (EFI_ERROR(status) || bufsz == 0) return (-1); return (c); } static int comc_ischar(void) { EFI_STATUS status; uint32_t control; /* * if this device is also used as ConIn, some firmwares * fail to return all input via SIO protocol. */ if (comc_port->currdev == comc_port->condev) { if ((efi_console.c_flags & C_ACTIVEIN) == 0) return (efi_console.c_ready()); return (0); } if (comc_port->sio == NULL) return (0); status = comc_port->sio->GetControl(comc_port->sio, &control); if (EFI_ERROR(status)) return (0); return (!(control & EFI_SERIAL_INPUT_BUFFER_EMPTY)); } static int comc_parse_intval(const char *value, unsigned *valp) { unsigned n; char *ep; if (value == NULL || *value == '\0') return (CMD_ERROR); errno = 0; n = strtoul(value, &ep, 10); if (errno != 0 || *ep != '\0') return (CMD_ERROR); *valp = n; return (CMD_OK); } static int comc_port_set(struct env_var *ev, int flags, const void *value) { unsigned port; SERIAL_IO_INTERFACE *sio; EFI_HANDLE handle; EFI_STATUS status; if (value == NULL || comc_port == NULL) return (CMD_ERROR); if (comc_parse_intval(value, &port) != CMD_OK) return (CMD_ERROR); handle = efi_serial_get_handle(port, NULL); if (handle == NULL) { printf("no handle\n"); return (CMD_ERROR); } status = BS->OpenProtocol(handle, &serial, (void**)&sio, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) { printf("OpenProtocol: %lu\n", EFI_ERROR_CODE(status)); return (CMD_ERROR); } comc_port->currdev = handle; comc_port->ioaddr = port; comc_port->sio = sio; (void) comc_setup(); env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } static int comc_speed_set(struct env_var *ev, int flags, const void *value) { unsigned speed; if (value == NULL || comc_port == NULL) return (CMD_ERROR); if (comc_parse_intval(value, &speed) != CMD_OK) return (CMD_ERROR); comc_port->newbaudrate = speed; if (comc_setup()) env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } /* * In case of error, we also reset ACTIVE flags, so the console * framefork will try alternate consoles. */ static bool comc_setup(void) { EFI_STATUS status; char *ev; /* * If the device isn't active, or there's no port present. */ if ((eficom.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0 || comc_port == NULL) return (false); if (comc_port->sio->Reset != NULL) { status = comc_port->sio->Reset(comc_port->sio); if (EFI_ERROR(status)) return (false); } /* * Avoid setting the baud rate on Hyper-V. Also, only set the baud rate * if the baud rate has changed from the default. And pass in '0' or * DefaultFoo when we're not changing those values. Some EFI * implementations get cranky when you set things to the values reported * back even when they are unchanged. */ if (comc_port->sio->SetAttributes != NULL && comc_port->newbaudrate != comc_port->baudrate) { ev = getenv("smbios.bios.version"); if (ev != NULL && strncmp(ev, "Hyper-V", 7) != 0) { status = comc_port->sio->SetAttributes(comc_port->sio, comc_port->newbaudrate, 0, 0, DefaultParity, 0, DefaultStopBits); if (EFI_ERROR(status)) return (false); comc_port->baudrate = comc_port->newbaudrate; } } #ifdef EFI_FORCE_RTS if (comc_port->sio->GetControl != NULL && comc_port->sio->SetControl != NULL) { UINT32 control; status = comc_port->sio->GetControl(comc_port->sio, &control); if (EFI_ERROR(status)) return (false); control |= EFI_SERIAL_REQUEST_TO_SEND; (void) comc_port->sio->SetControl(comc_port->sio, control); } #endif /* Mark this port usable. */ eficom.c_flags |= (C_PRESENTIN | C_PRESENTOUT); return (true); } diff --git a/stand/i386/libi386/comconsole.c b/stand/i386/libi386/comconsole.c index b2d48b13a9f4..dc6bbcfe9806 100644 --- a/stand/i386/libi386/comconsole.c +++ b/stand/i386/libi386/comconsole.c @@ -1,395 +1,395 @@ /*- * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "libi386.h" #define COMC_FMT 0x3 /* 8N1 */ #define COMC_TXWAIT 0x40000 /* transmit timeout */ #define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ #define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ #ifndef COMPORT #define COMPORT 0x3f8 #endif #ifndef COMSPEED #define COMSPEED 115200 #endif static void comc_probe(struct console *cp); static int comc_init(int arg); static void comc_putchar(int c); static int comc_getchar(void); static int comc_getspeed(void); static int comc_ischar(void); static int comc_parseint(const char *string); static uint32_t comc_parse_pcidev(const char *string); static int comc_pcidev_set(struct env_var *ev, int flags, const void *value); static int comc_pcidev_handle(uint32_t locator); static int comc_port_set(struct env_var *ev, int flags, const void *value); static void comc_setup(int speed, int port); static int comc_speed_set(struct env_var *ev, int flags, const void *value); static int comc_curspeed; static int comc_port = COMPORT; static uint32_t comc_locator; struct console comconsole = { .c_name = "comconsole", .c_desc = "serial port", .c_flags = 0, .c_probe = comc_probe, .c_init = comc_init, .c_out = comc_putchar, .c_in = comc_getchar, .c_ready = comc_ischar }; static void comc_probe(struct console *cp) { char intbuf[16]; char *cons, *env; int speed, port; uint32_t locator; if (comc_curspeed == 0) { comc_curspeed = COMSPEED; /* * Assume that the speed was set by an earlier boot loader if * comconsole is already the preferred console. */ cons = getenv("console"); if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || getenv("boot_multicons") != NULL) { comc_curspeed = comc_getspeed(); } env = getenv("comconsole_speed"); if (env != NULL) { speed = comc_parseint(env); if (speed > 0) comc_curspeed = speed; } sprintf(intbuf, "%d", comc_curspeed); unsetenv("comconsole_speed"); env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, env_nounset); env = getenv("comconsole_port"); if (env != NULL) { port = comc_parseint(env); if (port > 0) comc_port = port; } sprintf(intbuf, "%d", comc_port); unsetenv("comconsole_port"); env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, env_nounset); env = getenv("comconsole_pcidev"); if (env != NULL) { locator = comc_parse_pcidev(env); if (locator != 0) comc_pcidev_handle(locator); } unsetenv("comconsole_pcidev"); env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, env_nounset); } comc_setup(comc_curspeed, comc_port); } static int comc_init(int arg) { comc_setup(comc_curspeed, comc_port); if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == (C_PRESENTIN | C_PRESENTOUT)) - return (CMD_OK); - return (CMD_ERROR); + return (0); + return (1); } static void comc_putchar(int c) { int wait; for (wait = COMC_TXWAIT; wait > 0; wait--) if (inb(comc_port + com_lsr) & LSR_TXRDY) { outb(comc_port + com_data, (u_char)c); break; } } static int comc_getchar(void) { return (comc_ischar() ? inb(comc_port + com_data) : -1); } static int comc_ischar(void) { return (inb(comc_port + com_lsr) & LSR_RXRDY); } static int comc_speed_set(struct env_var *ev, int flags, const void *value) { int speed; if (value == NULL || (speed = comc_parseint(value)) <= 0) { printf("Invalid speed\n"); return (CMD_ERROR); } if (comc_curspeed != speed) comc_setup(speed, comc_port); env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } static int comc_port_set(struct env_var *ev, int flags, const void *value) { int port; if (value == NULL || (port = comc_parseint(value)) <= 0) { printf("Invalid port\n"); return (CMD_ERROR); } if (comc_port != port) comc_setup(comc_curspeed, port); env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } /* * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] */ static uint32_t comc_parse_pcidev(const char *string) { #ifdef EFI /* We don't support PCI in EFI yet */ return (0); #else char *p, *p1; uint8_t bus, dev, func, bar; uint32_t locator; int pres; pres = strtol(string, &p, 0); if (p == string || *p != ':' || pres < 0 ) return (0); bus = pres; p1 = ++p; pres = strtol(p1, &p, 0); if (p == string || *p != ':' || pres < 0 ) return (0); dev = pres; p1 = ++p; pres = strtol(p1, &p, 0); if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) return (0); func = pres; if (*p == ':') { p1 = ++p; pres = strtol(p1, &p, 0); if (p == string || *p != '\0' || pres <= 0 ) return (0); bar = pres; } else bar = 0x10; locator = (bar << 16) | biospci_locator(bus, dev, func); return (locator); #endif } static int comc_pcidev_handle(uint32_t locator) { #ifdef EFI /* We don't support PCI in EFI yet */ return (CMD_ERROR); #else char intbuf[64]; uint32_t port; if (biospci_read_config(locator & 0xffff, (locator & 0xff0000) >> 16, BIOSPCI_32BITS, &port) == -1) { printf("Cannot read bar at 0x%x\n", locator); return (CMD_ERROR); } /* * biospci_read_config() sets port == 0xffffffff if the pcidev * isn't found on the bus. Check for 0xffffffff and return to not * panic in BTX. */ if (port == 0xffffffff) { printf("Cannot find specified pcidev\n"); return (CMD_ERROR); } if (!PCI_BAR_IO(port)) { printf("Memory bar at 0x%x\n", locator); return (CMD_ERROR); } port &= PCIM_BAR_IO_BASE; sprintf(intbuf, "%d", port); unsetenv("comconsole_port"); env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, env_nounset); comc_setup(comc_curspeed, port); comc_locator = locator; return (CMD_OK); #endif } static int comc_pcidev_set(struct env_var *ev, int flags, const void *value) { uint32_t locator; int error; if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { printf("Invalid pcidev\n"); return (CMD_ERROR); } if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && comc_locator != locator) { error = comc_pcidev_handle(locator); if (error != CMD_OK) return (error); } env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } static void comc_setup(int speed, int port) { static int TRY_COUNT = 1000000; char intbuf[64]; int tries; comc_curspeed = speed; comc_port = port; if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) return; unsetenv("hw.uart.console"); #define COMC_TEST 0xbb /* * Write byte to scratch register and read it out. */ outb(comc_port + com_scr, COMC_TEST); if (inb(comc_port + com_scr) != COMC_TEST) { comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); return; } outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); outb(comc_port + com_cfcr, COMC_FMT); outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); tries = 0; do inb(comc_port + com_data); while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); if (tries < TRY_COUNT) { comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL); } else comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); } static int comc_parseint(const char *speedstr) { char *p; int speed; speed = strtol(speedstr, &p, 0); if (p == speedstr || *p != '\0' || speed <= 0) return (-1); return (speed); } static int comc_getspeed(void) { u_int divisor; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(comc_port + com_cfcr); outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(comc_port + com_dlbl); dlbh = inb(comc_port + com_dlbh); outb(comc_port + com_cfcr, cfcr); divisor = dlbh << 8 | dlbl; /* XXX there should be more sanity checking. */ if (divisor == 0) return (COMSPEED); return (COMC_DIV2BPS(divisor)); }