| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #ifndef SRC_DEVICES_SERIAL_DRIVERS_VIRTIO_CONSOLE_CONSOLE_H_ |
| #define SRC_DEVICES_SERIAL_DRIVERS_VIRTIO_CONSOLE_CONSOLE_H_ |
| |
| #include <fuchsia/hardware/virtioconsole/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/virtio/device.h> |
| #include <lib/virtio/ring.h> |
| #include <lib/zircon-internal/thread_annotations.h> |
| |
| #include <memory> |
| |
| #include <ddk/device.h> |
| #include <ddktl/device.h> |
| #include <ddktl/protocol/empty-protocol.h> |
| #include <fbl/array.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fs/managed_vfs.h> |
| |
| namespace virtio { |
| |
| // Describes a chunk of memory used for data transfers between the device and the driver, |
| // points to the memory inside TransferBuffer below |
| struct TransferDescriptor : fbl::DoublyLinkedListable<TransferDescriptor*> { |
| uint8_t* virt; |
| uintptr_t phys; |
| uint32_t total_len; |
| uint32_t used_len; |
| uint32_t processed_len; |
| }; |
| |
| // Manages memory we use for transfers, TransferDescriptor points to the memory inside the class |
| class TransferBuffer { |
| public: |
| TransferBuffer(); |
| ~TransferBuffer(); |
| |
| zx_status_t Init(const zx::bti& bti, size_t count, uint32_t chunk_size); |
| |
| TransferDescriptor* GetDescriptor(size_t index); |
| TransferDescriptor* PhysicalToDescriptor(uintptr_t phys); |
| |
| private: |
| size_t count_ = 0; |
| size_t size_ = 0; |
| uint32_t chunk_size_ = 0; |
| |
| io_buffer_t buf_; |
| fbl::Array<TransferDescriptor> descriptor_; |
| }; |
| |
| // Just a list of descriptors |
| class TransferQueue { |
| public: |
| void Add(TransferDescriptor* desc); |
| TransferDescriptor* Peek(); |
| TransferDescriptor* Dequeue(); |
| bool IsEmpty() const; |
| |
| private: |
| fbl::DoublyLinkedList<TransferDescriptor*> queue_; |
| }; |
| |
| // Actual virtio console implementation |
| class ConsoleDevice : public Device, |
| public ddk::Device<ConsoleDevice, ddk::Messageable>, |
| public ddk::EmptyProtocol<ZX_PROTOCOL_CONSOLE>, |
| public ::llcpp::fuchsia::hardware::virtioconsole::Device::Interface { |
| public: |
| explicit ConsoleDevice(zx_device_t* device, zx::bti bti, std::unique_ptr<Backend> backend); |
| ~ConsoleDevice() override; |
| zx_status_t DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn); |
| void DdkRelease() { virtio::Device::Release(); } |
| |
| zx_status_t Init() override; |
| void Unbind(ddk::UnbindTxn txn) override; |
| |
| void IrqRingUpdate() override; |
| void IrqConfigChange() override {} // No need to handle configuration changes |
| const char* tag() const override { return "virtio-console"; } |
| |
| void GetChannel(zx::channel req, GetChannelCompleter::Sync& completer) override; |
| zx_status_t GetEvent(zx::eventpair* event) { |
| return event_remote_.duplicate(ZX_RIGHTS_BASIC, event); |
| } |
| |
| zx_status_t Read(void* buf, size_t len, size_t* actual); |
| zx_status_t Write(const void* buf, size_t len, size_t* actual); |
| |
| private: |
| // For two queues it sums up to 32KiB, we probably don't need that much |
| constexpr static size_t kDescriptors = 32; |
| constexpr static uint32_t kChunkSize = 512; |
| |
| static zx_status_t virtio_console_read(void* ctx, void* buf, size_t len, zx_off_t off, |
| size_t* actual); |
| static zx_status_t virtio_console_write(void* ctx, const void* buf, size_t len, zx_off_t off, |
| size_t* actual); |
| |
| fbl::Mutex request_lock_; |
| |
| TransferBuffer port0_receive_buffer_ TA_GUARDED(request_lock_); |
| TransferQueue port0_receive_descriptors_ TA_GUARDED(request_lock_); |
| Ring port0_receive_queue_ TA_GUARDED(request_lock_) = {this}; |
| |
| TransferBuffer port0_transmit_buffer_ TA_GUARDED(request_lock_); |
| TransferQueue port0_transmit_descriptors_ TA_GUARDED(request_lock_); |
| Ring port0_transmit_queue_ TA_GUARDED(request_lock_) = {this}; |
| |
| async::Loop loop_{&kAsyncLoopConfigNoAttachToCurrentThread}; |
| fs::ManagedVfs vfs_{loop_.dispatcher()}; |
| fbl::RefPtr<fs::Vnode> console_vnode_; |
| zx::eventpair event_, event_remote_; |
| |
| std::optional<ddk::UnbindTxn> unbind_txn_; |
| }; |
| |
| } // namespace virtio |
| |
| #endif // SRC_DEVICES_SERIAL_DRIVERS_VIRTIO_CONSOLE_CONSOLE_H_ |