diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index b9565aa7ff..dd4ea7cb0b 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -249,6 +249,7 @@ set(KERNEL_SOURCES UserOrKernelBuffer.cpp VirtIO/VirtIO.cpp VirtIO/VirtIOConsole.cpp + VirtIO/VirtIOConsolePort.cpp VirtIO/VirtIOQueue.cpp VirtIO/VirtIORNG.cpp VM/AnonymousVMObject.cpp diff --git a/Kernel/VM/RingBuffer.h b/Kernel/VM/RingBuffer.h index 8363a43bec..8a748e4b65 100644 --- a/Kernel/VM/RingBuffer.h +++ b/Kernel/VM/RingBuffer.h @@ -8,6 +8,7 @@ #include #include +#include namespace Kernel { @@ -25,6 +26,8 @@ public: SpinLock& lock() { return m_lock; } size_t used_bytes() const { return m_num_used_bytes; } PhysicalAddress start_of_region() const { return m_region->physical_page(0)->paddr(); } + VirtualAddress vaddr() const { return m_region->vaddr(); } + size_t bytes_till_end() const { return (m_capacity_in_bytes - ((m_start_of_used + m_num_used_bytes) % m_capacity_in_bytes)) % m_capacity_in_bytes; }; private: OwnPtr m_region; diff --git a/Kernel/VirtIO/VirtIOConsole.cpp b/Kernel/VirtIO/VirtIOConsole.cpp index b443d171dd..b1df8def1b 100644 --- a/Kernel/VirtIO/VirtIOConsole.cpp +++ b/Kernel/VirtIO/VirtIOConsole.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2021, Kyle Pereira * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,8 +13,8 @@ namespace Kernel { unsigned VirtIOConsole::next_device_id = 0; UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) - : CharacterDevice(229, next_device_id++) - , VirtIODevice(address, "VirtIOConsole") + : VirtIODevice(address, "VirtIOConsole") + , m_device_id(next_device_id++) { if (auto cfg = get_config(ConfigurationType::Device)) { bool success = negotiate_features([&](u64 supported_features) { @@ -21,7 +22,7 @@ UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE)) dbgln("VirtIOConsole: Console size is not yet supported!"); if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT)) - dbgln("VirtIOConsole: Multi port is not yet supported!"); + negotiated |= VIRTIO_CONSOLE_F_MULTIPORT; return negotiated; }); if (success) { @@ -34,37 +35,24 @@ UNMAP_AFTER_INIT VirtIOConsole::VirtIOConsole(PCI::Address address) } if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { max_nr_ports = config_read32(*cfg, 0x4); + m_ports.resize(max_nr_ports); } }); dbgln("VirtIOConsole: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports); - success = setup_queues(2 + max_nr_ports * 2); // base receiveq/transmitq for port0 + 2 per every additional port + // Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port + success = setup_queues(2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0); } if (success) { finish_init(); - m_receive_buffer = make("VirtIOConsole Receive", RINGBUFFER_SIZE); - m_transmit_buffer = make("VirtIOConsole Transmit", RINGBUFFER_SIZE); - init_receive_buffer(); + if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) + setup_multiport(); + else + m_ports.append(new VirtIOConsolePort(0u, *this)); } } } -VirtIOConsole::~VirtIOConsole() -{ -} - -void VirtIOConsole::init_receive_buffer() -{ - auto& queue = get_queue(RECEIVEQ); - ScopedSpinLock queue_lock(queue.lock()); - VirtIOQueueChain chain(queue); - - auto buffer_start = m_receive_buffer->start_of_region(); - auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, RINGBUFFER_SIZE, BufferType::DeviceWritable); - VERIFY(did_add_buffer); - supply_chain_and_notify(RECEIVEQ, chain); -} - bool VirtIOConsole::handle_device_config_change() { dbgln("VirtIOConsole: Handle device config change"); @@ -73,112 +61,158 @@ bool VirtIOConsole::handle_device_config_change() void VirtIOConsole::handle_queue_update(u16 queue_index) { - dbgln_if(VIRTIO_DEBUG, "VirtIOConsole: Handle queue update"); - VERIFY(queue_index <= TRANSMITQ); - switch (queue_index) { - case RECEIVEQ: { - auto& queue = get_queue(RECEIVEQ); + dbgln_if(VIRTIO_DEBUG, "VirtIOConsole: Handle queue update {}", queue_index); + + if (queue_index == CONTROL_RECEIVEQ) { + ScopedSpinLock ringbuffer_lock(m_control_receive_buffer->lock()); + auto& queue = get_queue(CONTROL_RECEIVEQ); ScopedSpinLock queue_lock(queue.lock()); size_t used; VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used); - ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock()); + while (!popped_chain.is_empty()) { + popped_chain.for_each([&](auto addr, auto) { + auto offset = addr.as_ptr() - m_control_receive_buffer->start_of_region().as_ptr(); + auto* message = reinterpret_cast(m_control_receive_buffer->vaddr().offset(offset).as_ptr()); + process_control_message(*message); + }); - auto used_space = m_receive_buffer->reserve_space(used).value(); - auto remaining_space = RINGBUFFER_SIZE - used; - - // Our algorithm always has only one buffer in the queue. - VERIFY(!queue.new_data_available()); - popped_chain.release_buffer_slots_to_queue(); - - VirtIOQueueChain new_chain(queue); - if (remaining_space != 0) { - new_chain.add_buffer_to_chain(used_space.offset(used), remaining_space, BufferType::DeviceWritable); - supply_chain_and_notify(RECEIVEQ, new_chain); + supply_chain_and_notify(CONTROL_RECEIVEQ, popped_chain); + popped_chain = queue.pop_used_buffer_chain(used); } - - evaluate_block_conditions(); - break; - } - case TRANSMITQ: { - ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); - auto& queue = get_queue(TRANSMITQ); + } else if (queue_index == CONTROL_TRANSMITQ) { + ScopedSpinLock ringbuffer_lock(m_control_transmit_buffer->lock()); + auto& queue = get_queue(CONTROL_TRANSMITQ); ScopedSpinLock queue_lock(queue.lock()); size_t used; VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used); + auto number_of_messages = 0; do { popped_chain.for_each([this](PhysicalAddress address, size_t length) { - m_transmit_buffer->reclaim_space(address, length); + m_control_transmit_buffer->reclaim_space(address, length); }); popped_chain.release_buffer_slots_to_queue(); popped_chain = queue.pop_used_buffer_chain(used); + number_of_messages++; } while (!popped_chain.is_empty()); - // Unblock any IO tasks that were blocked because can_write() returned false - evaluate_block_conditions(); + m_control_wait_queue.wake_n(number_of_messages); + } else { + u32 port_index = queue_index < 2 ? 0 : (queue_index - 2) / 2; + if (port_index >= m_ports.size() || !m_ports.at(port_index)) { + dbgln("Invalid queue_index {}", queue_index); + return; + } + m_ports.at(port_index)->handle_queue_update({}, queue_index); + } +} + +void VirtIOConsole::setup_multiport() +{ + m_control_receive_buffer = make("VirtIOConsole control receive queue", CONTROL_BUFFER_SIZE); + m_control_transmit_buffer = make("VirtIOConsole control transmit queue", CONTROL_BUFFER_SIZE); + + auto& queue = get_queue(CONTROL_RECEIVEQ); + ScopedSpinLock queue_lock(queue.lock()); + VirtIOQueueChain chain(queue); + auto offset = 0ul; + + while (offset < CONTROL_BUFFER_SIZE) { + auto buffer_start = m_control_receive_buffer->start_of_region().offset(offset); + auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, CONTROL_MESSAGE_SIZE, BufferType::DeviceWritable); + VERIFY(did_add_buffer); + offset += CONTROL_MESSAGE_SIZE; + supply_chain_and_notify(CONTROL_RECEIVEQ, chain); + } + + ControlMessage ready_event { + .id = 0, // Unused + .event = (u16)ControlEvent::DeviceReady, + .value = (u16)ControlMessage::Status::Success + }; + write_control_message(ready_event); +} + +void VirtIOConsole::process_control_message(ControlMessage message) +{ + switch (message.event) { + case (u16)ControlEvent::DeviceAdd: { + u32 id = message.id; + if (id >= m_ports.size()) { + dbgln("Device provided an invalid port number {}. max_nr_ports: {}", id, m_ports.size()); + return; + } else if (!m_ports.at(id).is_null()) { + dbgln("Device tried to add port {} which was already added!", id); + return; + } + + m_ports.at(id) = new VirtIOConsolePort(id, *this); + ControlMessage ready_event { + .id = static_cast(id), + .event = (u16)ControlEvent::PortReady, + .value = (u16)ControlMessage::Status::Success + }; + + write_control_message(ready_event); + break; + } + case (u16)ControlEvent::ConsolePort: + case (u16)ControlEvent::PortOpen: { + if (message.id >= m_ports.size()) { + dbgln("Device provided an invalid port number {}. max_nr_ports: {}", message.id, m_ports.size()); + return; + } else if (m_ports.at(message.id).is_null()) { + dbgln("Device tried to open port {} which was not added!", message.id); + return; + } + + if (message.value == (u16)ControlMessage::PortStatus::Open) { + auto is_open = m_ports.at(message.id)->is_open(); + if (!is_open) { + m_ports.at(message.id)->set_open({}, true); + send_open_control_message(message.id, true); + } + } else if (message.value == (u16)ControlMessage::PortStatus::Close) { + m_ports.at(message.id)->set_open({}, false); + } else { + dbgln("Device specified invalid value {}. Must be 0 or 1.", message.value); + } break; } default: - VERIFY_NOT_REACHED(); + dbgln("Unhandled message event {}!", message.event); } } - -bool VirtIOConsole::can_read(const FileDescription&, size_t) const +void VirtIOConsole::write_control_message(ControlMessage message) { - return m_receive_buffer->used_bytes() > 0; -} + ScopedSpinLock ringbuffer_lock(m_control_transmit_buffer->lock()); -KResultOr VirtIOConsole::read(FileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size) -{ - if (!size) - return 0; + PhysicalAddress start_of_chunk; + size_t length_of_chunk; - if (!can_read(desc, size)) - return EAGAIN; + auto data = UserOrKernelBuffer::for_kernel_buffer((u8*)&message); + while (!m_control_transmit_buffer->copy_data_in(data, 0, sizeof(message), start_of_chunk, length_of_chunk)) { + ringbuffer_lock.unlock(); + m_control_wait_queue.wait_forever(); + ringbuffer_lock.lock(); + } - ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock()); - - auto bytes_copied = m_receive_buffer->copy_data_out(size, buffer); - m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied.value()); - - return bytes_copied; -} - -bool VirtIOConsole::can_write(const FileDescription&, size_t) const -{ - return get_queue(TRANSMITQ).has_free_slots() && m_transmit_buffer->has_space(); -} - -KResultOr VirtIOConsole::write(FileDescription& desc, u64, const UserOrKernelBuffer& data, size_t size) -{ - if (!size) - return 0; - - if (!can_write(desc, size)) - return EAGAIN; - - ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); - auto& queue = get_queue(TRANSMITQ); + auto& queue = get_queue(CONTROL_TRANSMITQ); ScopedSpinLock queue_lock(queue.lock()); VirtIOQueueChain chain(queue); - size_t total_bytes_copied = 0; - do { - PhysicalAddress start_of_chunk; - size_t length_of_chunk; + bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); + VERIFY(did_add_buffer); - if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) { - chain.release_buffer_slots_to_queue(); - return EINVAL; - } - - bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); - VERIFY(did_add_buffer); - total_bytes_copied += length_of_chunk; - } while (total_bytes_copied < size && can_write(desc, size - total_bytes_copied)); - - supply_chain_and_notify(TRANSMITQ, chain); - - return total_bytes_copied; + supply_chain_and_notify(CONTROL_TRANSMITQ, chain); } +void VirtIOConsole::send_open_control_message(unsigned port_number, bool open) +{ + ControlMessage port_open { + .id = static_cast(port_number), + .event = (u16)ControlEvent::PortOpen, + .value = open + }; + write_control_message(port_open); +} } diff --git a/Kernel/VirtIO/VirtIOConsole.h b/Kernel/VirtIO/VirtIOConsole.h index 2514352826..267c448b2b 100644 --- a/Kernel/VirtIO/VirtIOConsole.h +++ b/Kernel/VirtIO/VirtIOConsole.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 2021, Kyle Pereira * Copyright (c) 2021, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -6,48 +7,72 @@ #pragma once -#include #include #include +#include namespace Kernel { +class VirtIOConsole + : public VirtIODevice + , public RefCounted { + friend VirtIOConsolePort; -#define VIRTIO_CONSOLE_F_SIZE (1 << 0) -#define VIRTIO_CONSOLE_F_MULTIPORT (1 << 1) -#define VIRTIO_CONSOLE_F_EMERG_WRITE (1 << 2) - -#define RECEIVEQ 0 -#define TRANSMITQ 1 - -class VirtIOConsole final : public CharacterDevice - , public VirtIODevice { public: VirtIOConsole(PCI::Address); - virtual ~VirtIOConsole() override; + virtual ~VirtIOConsole() override = default; - virtual const char* purpose() const override { return class_name(); } + virtual const char* purpose() const override { return "VirtIOConsole"; } + + unsigned device_id() const + { + return m_device_id; + } private: - constexpr static size_t RINGBUFFER_SIZE = 2 * PAGE_SIZE; - virtual const char* class_name() const override { return m_class_name.characters(); } + enum class ControlEvent : u16 { + DeviceReady = 0, + DeviceAdd = 1, + PortReady = 3, + ConsolePort = 4, + PortOpen = 6, + }; + struct [[gnu::packed]] ControlMessage { + u32 id; + u16 event; + u16 value; - virtual bool can_read(const FileDescription&, size_t) const override; - virtual KResultOr read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override; - virtual bool can_write(const FileDescription&, size_t) const override; - virtual KResultOr write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override; + enum class Status : u16 { + Success = 1, + Failure = 0 + }; - virtual mode_t required_mode() const override { return 0666; } + enum class PortStatus : u16 { + Open = 1, + Close = 0 + }; + }; + + constexpr static u16 CONTROL_RECEIVEQ = 2; + constexpr static u16 CONTROL_TRANSMITQ = 3; + constexpr static size_t CONTROL_MESSAGE_SIZE = sizeof(ControlMessage); + constexpr static size_t CONTROL_BUFFER_SIZE = CONTROL_MESSAGE_SIZE * 32; virtual bool handle_device_config_change() override; - virtual String device_name() const override { return String::formatted("hvc{}", minor()); } virtual void handle_queue_update(u16 queue_index) override; - void init_receive_buffer(); - - OwnPtr m_receive_buffer; - OwnPtr m_transmit_buffer; + Vector> m_ports; + void setup_multiport(); + void process_control_message(ControlMessage message); + void write_control_message(ControlMessage message); + void send_open_control_message(unsigned port_number, bool open); + + unsigned m_device_id; + + OwnPtr m_control_transmit_buffer; + OwnPtr m_control_receive_buffer; + + WaitQueue m_control_wait_queue; static unsigned next_device_id; }; - } diff --git a/Kernel/VirtIO/VirtIOConsolePort.cpp b/Kernel/VirtIO/VirtIOConsolePort.cpp new file mode 100644 index 0000000000..e51fbdb260 --- /dev/null +++ b/Kernel/VirtIO/VirtIOConsolePort.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2021, Kyle Pereira + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +unsigned VirtIOConsolePort::next_device_id = 0; + +VirtIOConsolePort::VirtIOConsolePort(unsigned port, VirtIOConsole& console) + : CharacterDevice(229, next_device_id++) + , m_console(console) + , m_port(port) +{ + m_receive_buffer = make("VirtIOConsolePort Receive", RINGBUFFER_SIZE); + m_transmit_buffer = make("VirtIOConsolePort Transmit", RINGBUFFER_SIZE); + m_receive_queue = m_port == 0 ? 0 : m_port * 2 + 2; + m_transmit_queue = m_port == 0 ? 1 : m_port * 2 + 3; + init_receive_buffer(); +} + +void VirtIOConsolePort::init_receive_buffer() +{ + auto& queue = m_console.get_queue(m_receive_queue); + ScopedSpinLock queue_lock(queue.lock()); + VirtIOQueueChain chain(queue); + + auto buffer_start = m_receive_buffer->start_of_region(); + auto did_add_buffer = chain.add_buffer_to_chain(buffer_start, RINGBUFFER_SIZE, BufferType::DeviceWritable); + VERIFY(did_add_buffer); + m_console.supply_chain_and_notify(m_receive_queue, chain); +} + +void VirtIOConsolePort::handle_queue_update(Badge, u16 queue_index) +{ + dbgln_if(VIRTIO_DEBUG, "VirtIOConsolePort: Handle queue update for port {}", m_port); + VERIFY(queue_index == m_transmit_queue || queue_index == m_receive_queue); + if (queue_index == m_receive_queue) { + auto& queue = m_console.get_queue(m_receive_queue); + ScopedSpinLock queue_lock(queue.lock()); + size_t used; + VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used); + + ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock()); + auto used_space = m_receive_buffer->reserve_space(used).value(); + auto remaining_space = m_receive_buffer->bytes_till_end(); + + // Our algorithm always has only one buffer in the queue. + VERIFY(popped_chain.length() == 1); + VERIFY(!queue.new_data_available()); + popped_chain.release_buffer_slots_to_queue(); + + VirtIOQueueChain new_chain(queue); + if (remaining_space != 0) { + new_chain.add_buffer_to_chain(used_space.offset(used), remaining_space, BufferType::DeviceWritable); + m_console.supply_chain_and_notify(m_receive_queue, new_chain); + } else { + m_receive_buffer_exhausted = true; + } + + evaluate_block_conditions(); + } else { + ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); + auto& queue = m_console.get_queue(m_transmit_queue); + ScopedSpinLock queue_lock(queue.lock()); + size_t used; + VirtIOQueueChain popped_chain = queue.pop_used_buffer_chain(used); + do { + popped_chain.for_each([this](PhysicalAddress address, size_t length) { + m_transmit_buffer->reclaim_space(address, length); + }); + popped_chain.release_buffer_slots_to_queue(); + popped_chain = queue.pop_used_buffer_chain(used); + } while (!popped_chain.is_empty()); + // Unblock any IO tasks that were blocked because can_write() returned false + evaluate_block_conditions(); + } +} + +bool VirtIOConsolePort::can_read(const FileDescription&, size_t) const +{ + return m_receive_buffer->used_bytes() > 0; +} + +KResultOr VirtIOConsolePort::read(FileDescription& desc, u64, UserOrKernelBuffer& buffer, size_t size) +{ + if (!size) + return 0; + + ScopedSpinLock ringbuffer_lock(m_receive_buffer->lock()); + + if (!can_read(desc, size)) + return EAGAIN; + + auto bytes_copied = m_receive_buffer->copy_data_out(size, buffer).value(); + m_receive_buffer->reclaim_space(m_receive_buffer->start_of_used(), bytes_copied); + + if (m_receive_buffer_exhausted && m_receive_buffer->used_bytes() == 0) { + auto& queue = m_console.get_queue(m_receive_queue); + ScopedSpinLock queue_lock(queue.lock()); + VirtIOQueueChain new_chain(queue); + new_chain.add_buffer_to_chain(m_receive_buffer->start_of_region(), RINGBUFFER_SIZE, BufferType::DeviceWritable); + m_console.supply_chain_and_notify(m_receive_queue, new_chain); + m_receive_buffer_exhausted = false; + } + + return bytes_copied; +} + +bool VirtIOConsolePort::can_write(const FileDescription&, size_t) const +{ + return m_console.get_queue(m_transmit_queue).has_free_slots() && m_transmit_buffer->has_space(); +} + +KResultOr VirtIOConsolePort::write(FileDescription& desc, u64, const UserOrKernelBuffer& data, size_t size) +{ + if (!size) + return 0; + + ScopedSpinLock ringbuffer_lock(m_transmit_buffer->lock()); + auto& queue = m_console.get_queue(m_transmit_queue); + ScopedSpinLock queue_lock(queue.lock()); + + if (!can_write(desc, size)) + return EAGAIN; + + VirtIOQueueChain chain(queue); + + size_t total_bytes_copied = 0; + do { + PhysicalAddress start_of_chunk; + size_t length_of_chunk; + + if (!m_transmit_buffer->copy_data_in(data, total_bytes_copied, size - total_bytes_copied, start_of_chunk, length_of_chunk)) { + chain.release_buffer_slots_to_queue(); + return EINVAL; + } + + bool did_add_buffer = chain.add_buffer_to_chain(start_of_chunk, length_of_chunk, BufferType::DeviceReadable); + VERIFY(did_add_buffer); + total_bytes_copied += length_of_chunk; + } while (total_bytes_copied < size && can_write(desc, size)); + + m_console.supply_chain_and_notify(m_transmit_queue, chain); + + return total_bytes_copied; +} + +String VirtIOConsolePort::device_name() const +{ + return String::formatted("hvc{}p{}", m_console.device_id(), m_port); +} + +KResultOr> VirtIOConsolePort::open(int options) +{ + if (m_open) + m_console.send_open_control_message(m_port, true); + + return File::open(options); +} + +} diff --git a/Kernel/VirtIO/VirtIOConsolePort.h b/Kernel/VirtIO/VirtIOConsolePort.h new file mode 100644 index 0000000000..6390a4ae77 --- /dev/null +++ b/Kernel/VirtIO/VirtIOConsolePort.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * Copyright (c) 2021, Kyle Pereira + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class VirtIOConsole; + +#define VIRTIO_CONSOLE_F_SIZE (1 << 0) +#define VIRTIO_CONSOLE_F_MULTIPORT (1 << 1) +#define VIRTIO_CONSOLE_F_EMERG_WRITE (1 << 2) + +class VirtIOConsolePort + : public CharacterDevice { +public: + explicit VirtIOConsolePort(unsigned port, VirtIOConsole&); + void handle_queue_update(Badge, u16 queue_index); + + void set_open(Badge, bool state) { m_open = state; } + bool is_open() const { return m_open; } + +private: + constexpr static size_t RINGBUFFER_SIZE = 2 * PAGE_SIZE; + + virtual const char* class_name() const override { return "VirtIOConsolePort"; } + + virtual bool can_read(const FileDescription&, size_t) const override; + virtual KResultOr read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override; + virtual bool can_write(const FileDescription&, size_t) const override; + virtual KResultOr write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override; + virtual KResultOr> open(int options) override; + + mode_t required_mode() const override { return 0666; } + + String device_name() const override; + + void init_receive_buffer(); + + static unsigned next_device_id; + u16 m_receive_queue {}; + u16 m_transmit_queue {}; + + OwnPtr m_receive_buffer; + OwnPtr m_transmit_buffer; + + VirtIOConsole& m_console; + unsigned m_port; + + bool m_open { false }; + Atomic m_receive_buffer_exhausted; +}; + +}