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