blob: 3cb313d15bea70c5bcd220c3bb30379de28ae33f [file] [log] [blame]
// 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_