1/*
2 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "serial.h"
8
9#include <boot/platform.h>
10#include <arch/cpu.h>
11#include <boot/stage2.h>
12
13#include <string.h>
14
15
16//#define ENABLE_SERIAL
17	// define this to always enable serial output
18
19
20enum serial_register_offsets {
21	SERIAL_TRANSMIT_BUFFER		= 0,
22	SERIAL_RECEIVE_BUFFER		= 0,
23	SERIAL_DIVISOR_LATCH_LOW	= 0,
24	SERIAL_DIVISOR_LATCH_HIGH	= 1,
25	SERIAL_FIFO_CONTROL			= 2,
26	SERIAL_LINE_CONTROL			= 3,
27	SERIAL_MODEM_CONTROL		= 4,
28	SERIAL_LINE_STATUS			= 5,
29	SERIAL_MODEM_STATUS			= 6,
30};
31
32static const uint32 kSerialBaudRate = 115200;
33
34static int32 sSerialEnabled = 0;
35static uint16 sSerialBasePort = 0x3f8;
36
37static void
38serial_putc(char c)
39{
40	// wait until the transmitter empty bit is set
41	while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
42		asm volatile ("pause;");
43
44	out8(c, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
45}
46
47
48extern "C" void
49serial_puts(const char* string, size_t size)
50{
51	if (sSerialEnabled <= 0)
52		return;
53
54	while (size-- != 0) {
55		char c = string[0];
56
57		if (c == '\n') {
58			serial_putc('\r');
59			serial_putc('\n');
60		} else if (c != '\r')
61			serial_putc(c);
62
63		string++;
64	}
65}
66
67
68extern "C" void
69serial_disable(void)
70{
71#ifdef ENABLE_SERIAL
72	sSerialEnabled = 0;
73#else
74	sSerialEnabled--;
75#endif
76}
77
78
79extern "C" void
80serial_enable(void)
81{
82	sSerialEnabled++;
83}
84
85
86extern "C" void
87serial_init(void)
88{
89	// copy the base ports of the optional 4 serial ports to the kernel args
90	// 0x0000:0x0400 is the location of that information in the BIOS data
91	// segment
92	uint16* ports = (uint16*)0x400;
93	memcpy(gKernelArgs.platform_args.serial_base_ports, ports,
94		sizeof(uint16) * MAX_SERIAL_PORTS);
95
96	// only use the port if we could find one, else use the standard port
97	if (gKernelArgs.platform_args.serial_base_ports[0] != 0)
98		sSerialBasePort = gKernelArgs.platform_args.serial_base_ports[0];
99
100	uint16 divisor = uint16(115200 / kSerialBaudRate);
101
102	out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL);
103		// set divisor latch access bit
104	out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW);
105	out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH);
106	out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);
107		// 8N1
108
109#ifdef ENABLE_SERIAL
110	serial_enable();
111#endif
112}
113