The Pedigree Project 0.1
mach_pc/Serial.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Serial.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/processor/Processor.h"
23
24static constexpr size_t SerialStatusPollLimit = 100000;
25
26X86Serial::X86Serial() : m_Port("COM"), m_Present(false) {}
27
28X86Serial::~X86Serial() {}
29
30void X86Serial::setBase(uintptr_t nBaseAddr) {
31 m_Present = false;
32 if (!m_Port.allocate(nBaseAddr, 8))
33 return;
34
35 // Unimplemented ports can read as 0xff, including a false data-ready bit.
36 // Probe the UART itself without requiring an attached modem or serial cable.
37 const uint8_t scratch = m_Port.read8(serial::scratch);
38 m_Port.write8(0x5a, serial::scratch);
39 const bool first = m_Port.read8(serial::scratch) == 0x5a;
40 m_Port.write8(0xa5, serial::scratch);
41 m_Present = first && m_Port.read8(serial::scratch) == 0xa5;
42 m_Port.write8(scratch, serial::scratch);
43 if (!m_Present)
44 return;
45
46 m_Port.write8(0x00, serial::inten); // Disable all interrupts
47 m_Port.write8(0x80, serial::lctrl); // Enable DLAB (set baud rate divisor)
48 m_Port.write8(0x01, serial::rxtx); // Set divisor to 3 (lo byte) 115200 baud
49 m_Port.write8(0x00, serial::inten); // (hi byte)
50 m_Port.write8(0x03, serial::lctrl); // 8 bits, no parity, one stop bit
51 m_Port.write8(0xC7,
52 serial::iififo); // Enable FIFO, clear them, with 14-byte threshold
53 // This driver is polling-only: keep UART interrupt sources and OUT2 off.
54 m_Port.write8(0x03, serial::mctrl); // DTR and RTS asserted
55 m_Port.write8(0x00, serial::inten);
56
57 NOTICE("Modem status: " << Hex << m_Port.read8(serial::mstat));
58 NOTICE("Line status: " << Hex << m_Port.read8(serial::lstat));
59}
60
61bool X86Serial::hasData() {
62 return isConnected() && (m_Port.read8(serial::lstat) & 0x1);
63}
64
65char X86Serial::read() {
66 if (!isConnected())
67 return 0;
68 if (!waitForStatus(0x1))
69 return 0;
70
71 return m_Port.read8(serial::rxtx);
72}
73
74char X86Serial::readNonBlock() {
75 if (!isConnected())
76 return 0;
77 if (hasData())
78 return m_Port.read8(serial::rxtx);
79 else
80 return '\0';
81}
82
83void X86Serial::write(char c) {
84 if (!isConnected())
85 return;
86 if (!waitForStatus(0x20))
87 return;
88
89 m_Port.write8(static_cast<unsigned char>(c), serial::rxtx);
90}
91
92bool X86Serial::waitForStatus(uint8_t mask) {
93 for (size_t poll = 0; poll < SerialStatusPollLimit; ++poll) {
94 if (m_Port.read8(serial::lstat) & mask)
95 return true;
97 }
98 return (m_Port.read8(serial::lstat) & mask) != 0;
99}
100
101bool X86Serial::isConnected() {
102 return m_Present;
103}
bool allocate(io_port_t ioPort, size_t size)
Definition IoPort.cc:29
virtual void write8(uint8_t value, size_t offset=0)
virtual uint8_t read8(size_t offset=0)
static void pause()
virtual void setBase(uintptr_t nBaseAddr)
@ Hex
Definition Log.h:124