Serenity Operating System
at master 139 lines 3.9 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <Kernel/Devices/DeviceManagement.h> 9#include <Kernel/Devices/SerialDevice.h> 10#include <Kernel/IOWindow.h> 11#include <Kernel/Sections.h> 12 13namespace Kernel { 14 15UNMAP_AFTER_INIT SerialDevice::SerialDevice(NonnullOwnPtr<IOWindow> registers_io_window, unsigned minor) 16 : CharacterDevice(4, minor) 17 , m_registers_io_window(move(registers_io_window)) 18{ 19 initialize(); 20} 21 22UNMAP_AFTER_INIT SerialDevice::~SerialDevice() = default; 23 24bool SerialDevice::can_read(OpenFileDescription const&, u64) const 25{ 26 return (get_line_status() & DataReady) != 0; 27} 28 29ErrorOr<size_t> SerialDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) 30{ 31 if (!size) 32 return 0; 33 34 SpinlockLocker lock(m_serial_lock); 35 if (!(get_line_status() & DataReady)) 36 return 0; 37 38 return buffer.write_buffered<128>(size, [&](Bytes bytes) { 39 for (auto& byte : bytes) 40 byte = m_registers_io_window->read8(0); 41 return bytes.size(); 42 }); 43} 44 45bool SerialDevice::can_write(OpenFileDescription const&, u64) const 46{ 47 return (get_line_status() & EmptyTransmitterHoldingRegister) != 0; 48} 49 50ErrorOr<size_t> SerialDevice::write(OpenFileDescription& description, u64, UserOrKernelBuffer const& buffer, size_t size) 51{ 52 if (!size) 53 return 0; 54 55 SpinlockLocker lock(m_serial_lock); 56 if (!can_write(description, size)) 57 return EAGAIN; 58 59 return buffer.read_buffered<128>(size, [&](ReadonlyBytes bytes) { 60 for (const auto& byte : bytes) 61 put_char(byte); 62 return bytes.size(); 63 }); 64} 65 66void SerialDevice::put_char(char ch) 67{ 68 while ((get_line_status() & EmptyTransmitterHoldingRegister) == 0) 69 ; 70 71 if (ch == '\n' && !m_last_put_char_was_carriage_return) 72 m_registers_io_window->write8(0, '\r'); 73 74 m_registers_io_window->write8(0, ch); 75 76 m_last_put_char_was_carriage_return = (ch == '\r'); 77} 78 79UNMAP_AFTER_INIT void SerialDevice::initialize() 80{ 81 set_interrupts(false); 82 set_baud(Baud38400); 83 set_line_control(None, One, EightBits); 84 set_fifo_control(EnableFIFO | ClearReceiveFIFO | ClearTransmitFIFO | TriggerLevel4); 85 set_modem_control(RequestToSend | DataTerminalReady); 86} 87 88UNMAP_AFTER_INIT void SerialDevice::set_interrupts(bool interrupt_enable) 89{ 90 m_interrupt_enable = interrupt_enable; 91 92 m_registers_io_window->write8(1, interrupt_enable); 93} 94 95void SerialDevice::set_baud(Baud baud) 96{ 97 m_baud = baud; 98 99 m_registers_io_window->write8(3, m_registers_io_window->read8(3) | 0x80); // turn on DLAB 100 m_registers_io_window->write8(0, ((u8)(baud)) & 0xff); // lower half of divisor 101 m_registers_io_window->write8(1, ((u8)(baud)) >> 2); // lower half of divisor 102 m_registers_io_window->write8(3, m_registers_io_window->read8(3) & 0x7f); // turn off DLAB 103} 104 105void SerialDevice::set_fifo_control(u8 fifo_control) 106{ 107 m_fifo_control = fifo_control; 108 m_registers_io_window->write8(2, fifo_control); 109} 110 111void SerialDevice::set_line_control(ParitySelect parity_select, StopBits stop_bits, WordLength word_length) 112{ 113 m_parity_select = parity_select; 114 m_stop_bits = stop_bits; 115 m_word_length = word_length; 116 117 m_registers_io_window->write8(3, (m_registers_io_window->read8(3) & ~0x3f) | parity_select | stop_bits | word_length); 118} 119 120void SerialDevice::set_break_enable(bool break_enable) 121{ 122 m_break_enable = break_enable; 123 124 m_registers_io_window->write8(3, m_registers_io_window->read8(3) & (break_enable ? 0xff : 0xbf)); 125} 126 127void SerialDevice::set_modem_control(u8 modem_control) 128{ 129 m_modem_control = modem_control; 130 131 m_registers_io_window->write8(4, modem_control); 132} 133 134u8 SerialDevice::get_line_status() const 135{ 136 return m_registers_io_window->read8(5); 137} 138 139}