xref: /haiku/src/system/boot/platform/bios_ia32/serial.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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 
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[16384];
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
114 	// segment
115 	uint16* ports = (uint16*)0x400;
116 	memcpy(gKernelArgs.platform_args.serial_base_ports, ports,
117 		sizeof(uint16) * MAX_SERIAL_PORTS);
118 
119 	// only use the port if we could find one, else use the standard port
120 	if (gKernelArgs.platform_args.serial_base_ports[0] != 0)
121 		sSerialBasePort = gKernelArgs.platform_args.serial_base_ports[0];
122 
123 	uint16 divisor = uint16(115200 / kSerialBaudRate);
124 
125 	out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL);
126 		// set divisor latch access bit
127 	out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW);
128 	out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH);
129 	out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);
130 		// 8N1
131 
132 #ifdef ENABLE_SERIAL
133 	serial_enable();
134 #endif
135 }
136 
137