diff options
Diffstat (limited to 'contrib/syslinux-4.02/com32/sysdump/serial.c')
-rw-r--r-- | contrib/syslinux-4.02/com32/sysdump/serial.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/sysdump/serial.c b/contrib/syslinux-4.02/com32/sysdump/serial.c new file mode 100644 index 0000000..a398753 --- /dev/null +++ b/contrib/syslinux-4.02/com32/sysdump/serial.c @@ -0,0 +1,169 @@ +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <console.h> +#include <sys/io.h> +#include <sys/cpu.h> +#include <syslinux/config.h> + +#include "serial.h" + +enum { + THR = 0, + RBR = 0, + DLL = 0, + DLM = 1, + IER = 1, + IIR = 2, + FCR = 2, + LCR = 3, + MCR = 4, + LSR = 5, + MSR = 6, + SCR = 7, +}; + + +int serial_init(struct serial_if *sif, const char *argv[]) +{ + const struct syslinux_serial_console_info *sci + = syslinux_serial_console_info(); + uint16_t port; + unsigned int divisor; + uint8_t dll, dlm, lcr; + + if (!argv[0]) { + if (sci->iobase) { + port = sci->iobase; + } else { + printf("No port number specified and not using serial console!\n"); + return -1; + } + } else { + port = strtoul(argv[0], NULL, 0); + if (port <= 3) { + uint16_t addr = ((uint16_t *)0x400)[port]; + if (!addr) { + printf("No serial port address found!\n"); + return -1; + } + printf("Serial port %u is at 0x%04x\n", port, addr); + port = addr; + } + } + + sif->port = port; + sif->console = false; + + divisor = 1; /* Default speed = 115200 bps */ + + /* Check to see if this is the same as the serial console */ + if (port == sci->iobase) { + /* Overlaying the console... */ + sif->console = true; + + /* Default to already configured speed */ + divisor = sci->divisor; + + /* Shut down I/O to the console for the time being */ + openconsole(&dev_null_r, &dev_null_w); + } + + if (argv[0] && argv[1]) + divisor = 115200/strtoul(argv[1], NULL, 0); + + cli(); /* Just in case... */ + + /* Save old register settings */ + sif->old.lcr = inb(port + LCR); + sif->old.mcr = inb(port + MCR); + sif->old.iir = inb(port + IIR); + + /* Set speed */ + outb(0x83, port + LCR); /* Enable divisor access */ + sif->old.dll = inb(port + DLL); + sif->old.dlm = inb(port + DLM); + outb(divisor, port + DLL); + outb(divisor >> 8, port + DLM); + (void)inb(port + IER); /* Synchronize */ + + dll = inb(port + DLL); + dlm = inb(port + DLM); + lcr = inb(port + LCR); + outb(0x03, port + LCR); /* Enable data access, n81 */ + (void)inb(port + IER); /* Synchronize */ + sif->old.ier = inb(port + IER); + + /* Disable interrupts */ + outb(0, port + IER); + + sti(); + + if (dll != (uint8_t)divisor || + dlm != (uint8_t)(divisor >> 8) || + lcr != 0x83) { + serial_cleanup(sif); + printf("No serial port detected!\n"); + return -1; /* This doesn't look like a serial port */ + } + + /* Enable 16550A FIFOs if available */ + outb(0x01, port + FCR); /* Enable FIFO */ + (void)inb(port + IER); /* Synchronize */ + if (inb(port + IIR) < 0xc0) + outb(0x00, port + FCR); /* Disable FIFOs if non-functional */ + (void)inb(port + IER); /* Synchronize */ + + return 0; +} + +void serial_write(struct serial_if *sif, const void *data, size_t n) +{ + uint16_t port = sif->port; + const char *p = data; + uint8_t lsr; + + while (n--) { + do { + lsr = inb(port + LSR); + } while (!(lsr & 0x20)); + + outb(*p++, port + THR); + } +} + +void serial_read(struct serial_if *sif, void *data, size_t n) +{ + uint16_t port = sif->port; + char *p = data; + uint8_t lsr; + + while (n--) { + do { + lsr = inb(port + LSR); + } while (!(lsr & 0x01)); + + *p++ = inb(port + RBR); + } +} + +void serial_cleanup(struct serial_if *sif) +{ + uint16_t port = sif->port; + + outb(0x83, port + LCR); + (void)inb(port + IER); + outb(sif->old.dll, port + DLL); + outb(sif->old.dlm, port + DLM); + (void)inb(port + IER); + outb(sif->old.lcr & 0x7f, port + LCR); + (void)inb(port + IER); + outb(sif->old.mcr, port + MCR); + outb(sif->old.ier, port + IER); + if (sif->old.iir < 0xc0) + outb(0x00, port + FCR); /* Disable FIFOs */ + + /* Re-enable console messages, if we shut them down */ + if (sif->console) + openconsole(&dev_null_r, &dev_stdcon_w); +} |