blob: dd9c6d7e706f8542d21dbb35d85c63f6a3e24196 [file]
// Copyright 2020 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_UART16550_UART16550_H_
#define SRC_DEVICES_SERIAL_DRIVERS_UART16550_UART16550_H_
#include <fidl/fuchsia.hardware.serialimpl/cpp/driver/wire.h>
#include <lib/stdcompat/span.h>
#include <zircon/compiler.h>
#include <mutex>
#include <optional>
#include <thread>
#include <variant>
#include <vector>
#include <ddktl/device.h>
#include <hwreg/bitfields.h>
#include <hwreg/pio.h>
#include "sdk/lib/driver/outgoing/cpp/outgoing_directory.h"
#include "src/devices/lib/acpi/client.h"
#if UART16550_TESTING
#include <hwreg/mock.h>
#endif
namespace uart16550 {
class Uart16550;
using DeviceType = ddk::Device<Uart16550>;
class Uart16550 : public DeviceType, public fdf::WireServer<fuchsia_hardware_serialimpl::Device> {
public:
Uart16550();
explicit Uart16550(zx_device_t* parent, acpi::Client acpi);
size_t FifoDepth() const;
bool Enabled();
static zx_status_t Create(void* ctx, zx_device_t* parent);
zx_status_t Init();
#if UART16550_TESTING
// test-use only
zx_status_t Init(zx::interrupt interrupt, hwreg::Mock::RegisterIo port_mock);
#endif
// test-use only
zx::unowned_interrupt InterruptHandle();
// ddk::Releasable
void DdkRelease();
fidl::ProtocolHandler<fuchsia_hardware_serialimpl::Device> GetHandler();
private:
struct WriteContext {
WriteContext(WriteCompleter::Async completer, cpp20::span<const uint8_t> data)
: completer(std::move(completer)), data(data) {}
WriteCompleter::Async completer;
cpp20::span<const uint8_t> data;
};
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void GetInfo(fdf::Arena& arena, GetInfoCompleter::Sync& completer) override;
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void Config(fuchsia_hardware_serialimpl::wire::DeviceConfigRequest* request, fdf::Arena& arena,
ConfigCompleter::Sync& completer) override;
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void Enable(fuchsia_hardware_serialimpl::wire::DeviceEnableRequest* request, fdf::Arena& arena,
EnableCompleter::Sync& completer) override;
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void Read(fdf::Arena& arena, ReadCompleter::Sync& completer) override;
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void Write(fuchsia_hardware_serialimpl::wire::DeviceWriteRequest* request, fdf::Arena& arena,
WriteCompleter::Sync& completer) override;
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void CancelAll(fdf::Arena& arena, CancelAllCompleter::Sync& completer) override;
// fdf::WireServer<fuchsia_hardware_serialimpl::Device>
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_serialimpl::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
bool SupportsAutomaticFlowControl() const;
zx_status_t Config(uint32_t baud_rate, uint32_t flags);
zx_status_t Enable(bool enable);
void CancelAll();
void ResetFifosLocked() __TA_REQUIRES(device_mutex_);
void InitFifosLocked() __TA_REQUIRES(device_mutex_);
void HandleInterrupts();
// Returns the number of bytes read from the RX FIFO.
size_t DrainRxFifo(cpp20::span<uint8_t> buffer) __TA_REQUIRES(device_mutex_);
// Fills the TX FIFO with as many bytes as possible, and returns a subspan pointing to the
// remaining data that did not fit.
cpp20::span<const uint8_t> FillTxFifo(cpp20::span<const uint8_t> data)
__TA_REQUIRES(device_mutex_);
acpi::Client acpi_fidl_;
std::mutex device_mutex_;
std::thread interrupt_thread_;
zx::interrupt interrupt_;
#if UART16550_TESTING
// This should never be used before Init, but must be default-constructible.
// The Mock is the default (first) variant so it's default-constructible.
std::variant<hwreg::Mock::RegisterIo, hwreg::RegisterPio> port_io_ __TA_GUARDED(device_mutex_){
std::in_place_index<0>};
#else
std::variant<hwreg::RegisterPio> port_io_ __TA_GUARDED(device_mutex_){nullptr};
#endif
size_t uart_fifo_len_ = 1;
bool enabled_ __TA_GUARDED(device_mutex_) = false;
std::optional<ReadCompleter::Async> read_completer_ __TA_GUARDED(device_mutex_);
std::vector<uint8_t> write_buffer_ __TA_GUARDED(device_mutex_);
std::optional<WriteContext> write_context_ __TA_GUARDED(device_mutex_);
fdf::ServerBindingGroup<fuchsia_hardware_serialimpl::Device> bindings_;
fdf::OutgoingDirectory outgoing_;
}; // namespace uart16550
} // namespace uart16550
#endif // SRC_DEVICES_SERIAL_DRIVERS_UART16550_UART16550_H_