xref: /haiku/src/system/boot/platform/bios_ia32/serial.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
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 
20 enum 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 
32 static const uint32 kSerialBaudRate = 115200;
33 
34 static int32 sSerialEnabled = 0;
35 static uint16 sSerialBasePort = 0x3f8;
36 
37 static char sBuffer[4096];
38 static uint32 sBufferPosition;
39 
40 
41 static void
42 serial_putc(char c)
43 {
44 	// wait until the transmitter empty bit is set
45 	while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
46 		asm volatile ("pause;");
47 
48 	out8(c, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
49 }
50 
51 
52 extern "C" void
53 serial_puts(const char *string, size_t size)
54 {
55 	if (sSerialEnabled <= 0)
56 		return;
57 
58 	if (sBufferPosition + size < sizeof(sBuffer)) {
59 		memcpy(sBuffer + sBufferPosition, string, size);
60 		sBufferPosition += size;
61 	}
62 
63 	while (size-- != 0) {
64 		char c = string[0];
65 
66 		if (c == '\n') {
67 			serial_putc('\r');
68 			serial_putc('\n');
69 		} else if (c != '\r')
70 			serial_putc(c);
71 
72 		string++;
73 	}
74 }
75 
76 
77 extern "C" void
78 serial_disable(void)
79 {
80 #ifdef ENABLE_SERIAL
81 	sSerialEnabled = 0;
82 #else
83 	sSerialEnabled--;
84 #endif
85 }
86 
87 
88 extern "C" void
89 serial_enable(void)
90 {
91 	sSerialEnabled++;
92 }
93 
94 
95 extern "C" void
96 serial_cleanup(void)
97 {
98 	if (sSerialEnabled <= 0)
99 		return;
100 
101 	gKernelArgs.debug_output = kernel_args_malloc(sBufferPosition);
102 	if (gKernelArgs.debug_output != NULL) {
103 		memcpy(gKernelArgs.debug_output, sBuffer, sBufferPosition);
104 		gKernelArgs.debug_size = sBufferPosition;
105 	}
106 }
107 
108 
109 extern "C" void
110 serial_init(void)
111 {
112 	// copy the base ports of the optional 4 serial ports to the kernel args
113 	// 0x0000:0x0400 is the location of that information in the BIOS data segment
114 	uint16 *ports = (uint16 *)0x400;
115 	memcpy(gKernelArgs.platform_args.serial_base_ports, ports, sizeof(uint16) * MAX_SERIAL_PORTS);
116 
117 	// only use the port if we could find one, else use the standard port
118 	if (gKernelArgs.platform_args.serial_base_ports[0] != 0)
119 		sSerialBasePort = gKernelArgs.platform_args.serial_base_ports[0];
120 
121 	uint16 divisor = uint16(115200 / kSerialBaudRate);
122 
123 	out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL);	/* set divisor latch access bit */
124 	out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW);
125 	out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH);
126 	out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);		/* 8N1 */
127 
128 #ifdef ENABLE_SERIAL
129 	serial_enable();
130 #endif
131 }
132 
133