[syslinux] [PATCH 001/001] core/serial: Add support for serial output functions.

Raphael S.Carvalho
Fri, 12 Jul 2013 05:16:24 -0000

This patch adds support for serial output functions.
It may be useful for debugging purposes since you can separate ordinary outputs
from debugging dumps.
Just add "serial.h", and use either serial_print or serial_puts as you
would use printf and puts respectivelly.

For QEMU, you only need to specify the option -serial stdio.
If you use another VM, find how you can redirect the serial port to stdio.

Signed-off-by: Raphael S.Carvalho <raphael.scarv at gmail.com>
---
 core/include/serial.h |   66 ++++++++++++++++++++++++
 core/serial.c         |  135 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+), 0 deletions(-)
 create mode 100644 core/include/serial.h
 create mode 100644 core/serial.c

diff --git a/core/include/serial.h b/core/include/serial.h
new file mode 100644
index 0000000..4276d97
--- /dev/null
+++ b/core/include/serial.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _SERIAL_H_
+#define _SERIAL_H_
+
+/* COM Port / IO Port */
+#define COM1_PORT 0x3F8
+#define COM2_PORT 0x02F8
+#define COM3_PORT 0x03E8
+#define COM4_PORT 0x02E8
+
+/* Data Registers */
+#define COM_INTR_ENABLE_REG	1 /* Interrupt Enable Register */
+#define COM_INTR_ID_REG		2 /* Interrupt Ident Register (IN) */
+#define COM_FIFO_CTRL_REG	2 /* FIFO Control Register (OUT) */
+#define COM_LINE_CTRL_REG	3 /* Line Control Register */
+#define COM_MODEM_CTRL_REG	4 /* Modem Control Register */
+#define COM_LINE_STATUS_REG	5 /* Line Status Register */
+
+/* Line status register fields */
+#define COM_LINE_STATUS_DATA	0x01 /* Data available */
+#define COM_LINE_STATUS_RDY	0x20 /* Transmitter ready */
+#define COM_LINE_STATUS_OFF	0x40 /* Transmitter off */
+
+/* Modem ctrl register fields */
+#define	COM_MODEM_CTRL_RTS	0x02
+#define	COM_MODEM_CTRL_DTR	0x01
+
+/* DLAB bit pos (Divisor Latch Access Bit) */
+#define COM_DLAB_BIT 		(1 << 7)
+
+/* If DLAB is set 0 in COM_LINE_CTRL_REG */
+#define COM_RX			0 /* Receiver */
+#define COM_TX			0 /* Transmitter */
+
+/* If DLAB is set 1 in COM_LINE_CTRL_REG */
+#define COM_DIVREG_LOW		0
+#define COM_DIVREG_HIGH		1
+
+typedef enum {
+    SERIAL_OFF,
+    SERIAL_ON,
+    SERIAL_DONT_EXIST,
+} serial_state;
+
+extern int serial_puts(const char *);
+extern int serial_print(const char *, ...);
+
+#endif
\ No newline at end of file
diff --git a/core/serial.c b/core/serial.c
new file mode 100644
index 0000000..e2e5366
--- /dev/null
+++ b/core/serial.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 Raphael S. Carvalho <raphael.scarv at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <sys/io.h>
+#include <serial.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+extern void writestr(const char *);
+
+/*
+ * Taken from linux/arch/x86/lib/delay.c
+ * simple loop based delay:
+ */
+static void delay_loop(unsigned long loops)
+{
+    asm volatile(
+	"       test %0,%0      \n"
+	"       jz 3f           \n"
+	"       jmp 1f          \n"
+
+	".align 16              \n"
+	"1:     jmp 2f          \n"
+
+	".align 16              \n"
+	"2:     dec %0          \n"
+	"       jnz 2b          \n"
+	"3:     dec %0          \n"
+
+	: /* we don't need output */
+	:"a" (loops)
+    );
+}
+
+/*
+ * Try to initialize serial port.
+ * Default settings: 9600 baud, 8 data bits, 1 stop bit and parity off.
+ */
+static int serial_init(uint16_t com_port)
+{
+    /* Turn off the FIFO */
+    outb(0x00, com_port + COM_FIFO_CTRL_REG);
+    /* Disable all interrupts */
+    outb(0x00, com_port + COM_INTR_ENABLE_REG);
+
+    /* Enable DLAB (Divisor Latch Access Bit) */
+    outb(COM_DLAB_BIT, com_port + COM_LINE_CTRL_REG);
+    /*
+     * When COM_DLAB_BIT is set in COM_LINE_CTRL_REG, offsets 0 and 1
+     * are mapped to the low and high bytes of divisor register respectivelly
+     * for setting baud rate of the port.
+     */
+    outb((uint8_t) (115200 / 9600), com_port + COM_DIVREG_LOW);
+    outb(0x00, com_port + COM_DIVREG_HIGH);
+
+    /* 8 bits, no parity, one stop bit, and disable DLAB. */
+    outb(0x03, com_port + COM_LINE_CTRL_REG);
+
+    /* Enable FIFO, clear them, with 14-byte threshold */
+    outb(0xC7, com_port + COM_FIFO_CTRL_REG);
+
+    /* IRQs enabled, RTS/DSR set */
+    outb(0x0B, com_port + COM_MODEM_CTRL_REG);
+
+    /* COM_LINE_STATUS_REG returns 0xFF if serial port doesn't exist. */
+    return (inb(COM_LINE_STATUS_REG) == 0xFF);
+}
+
+/* Write one byte to the serial line */
+static void serial_putc(uint8_t byte)
+{
+    int i;
+
+    /* Wait for transmitter to be ready */
+    for (i = 0; i < 128; i++) {
+	if (inb(COM1_PORT + COM_LINE_STATUS_REG) & COM_LINE_STATUS_RDY)
+	    break;
+	delay_loop(100);
+    }
+
+    outb(byte, COM1_PORT + COM_TX);
+}
+
+int serial_puts(const char *str)
+{
+    int rv = 0;
+
+    while (*str) {
+	serial_putc(*str++);
+	rv++;
+    }
+
+    return rv;
+}
+
+int serial_print(const char *format, ...)
+{
+    char buf[1024];
+    va_list ap;
+    int rv = 0;
+    static serial_state serial = SERIAL_OFF;
+
+    if (serial == SERIAL_OFF) {
+	if (serial_init(COM1_PORT)) {
+	    writestr("Serial port (COM1) does not exist!\n");
+	    serial = SERIAL_DONT_EXIST;
+	} else {
+	    serial = SERIAL_ON;
+	}
+    } else if (serial == SERIAL_ON) {
+	va_start(ap, format);
+	vsnprintf(buf, sizeof buf, format, ap);
+	va_end(ap);
+	rv = serial_puts(buf);
+    }
+
+    return rv;
+}
--
1.7.2.5