blob: cfdbf6debdded7f8077ed4deeee09c1bc4610760 [file] [log] [blame]
// Copyright 2018 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_AML_UART_AML_UART_H_
#define SRC_DEVICES_SERIAL_DRIVERS_AML_UART_AML_UART_H_
#include <fidl/fuchsia.hardware.serialimpl/cpp/driver/fidl.h>
#include <fidl/fuchsia.power.broker/cpp/fidl.h>
#include <lib/async/cpp/irq.h>
#include <lib/async/cpp/wait.h>
#include <lib/device-protocol/pdev-fidl.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/mmio/mmio.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/timer.h>
#include <fbl/mutex.h>
namespace serial {
namespace internal {
class DriverTransportReadOperation {
using ReadCompleter = fidl::internal::WireCompleter<fuchsia_hardware_serialimpl::Device::Read>;
public:
DriverTransportReadOperation(fdf::Arena arena, ReadCompleter::Async completer)
: arena_(std::move(arena)), completer_(std::move(completer)) {}
fit::closure MakeCallback(zx_status_t status, void* buf, size_t len);
private:
fdf::Arena arena_;
ReadCompleter::Async completer_;
};
class DriverTransportWriteOperation {
using WriteCompleter = fidl::internal::WireCompleter<fuchsia_hardware_serialimpl::Device::Write>;
public:
DriverTransportWriteOperation(fdf::Arena arena, WriteCompleter::Async completer)
: arena_(std::move(arena)), completer_(std::move(completer)) {}
fit::closure MakeCallback(zx_status_t status);
private:
fdf::Arena arena_;
WriteCompleter::Async completer_;
};
} // namespace internal
class AmlUart : public fdf::WireServer<fuchsia_hardware_serialimpl::Device> {
public:
explicit AmlUart(
ddk::PDevFidl pdev, const fuchsia_hardware_serial::wire::SerialPortInfo& serial_port_info,
fdf::MmioBuffer mmio, fdf::UnownedSynchronizedDispatcher irq_dispatcher,
std::optional<fdf::UnownedSynchronizedDispatcher> timer_dispatcher = std::nullopt,
std::optional<fidl::ClientEnd<fuchsia_power_broker::Lessor>> lessor_client_end = std::nullopt,
std::optional<fidl::ClientEnd<fuchsia_power_broker::CurrentLevel>> current_level_client_end =
std::nullopt,
std::optional<fidl::ClientEnd<fuchsia_power_broker::RequiredLevel>>
required_level_client_end = std::nullopt,
std::optional<fidl::ClientEnd<fuchsia_power_broker::ElementControl>>
element_control_client_end = std::nullopt);
zx_status_t Config(uint32_t baud_rate, uint32_t flags);
zx_status_t Enable(bool enable);
// fuchsia_hardware_serialimpl::Device FIDL implementation.
void GetInfo(fdf::Arena& arena, GetInfoCompleter::Sync& completer) override;
void Config(ConfigRequestView request, fdf::Arena& arena,
ConfigCompleter::Sync& completer) override;
void Enable(EnableRequestView request, fdf::Arena& arena,
EnableCompleter::Sync& completer) override;
void Read(fdf::Arena& arena, ReadCompleter::Sync& completer) override;
void Write(WriteRequestView request, fdf::Arena& arena, WriteCompleter::Sync& completer) override;
void CancelAll(fdf::Arena& arena, CancelAllCompleter::Sync& completer) override;
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_serialimpl::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
fidl::ClientEnd<fuchsia_power_broker::ElementControl>& element_control_for_testing();
// Test functions: simulate a data race where the HandleTX / HandleRX functions get called twice.
void HandleTXRaceForTest();
void HandleRXRaceForTest();
// Allow fake timer injected by unittests.
void InjectTimerForTest(zx_handle_t handle);
const fuchsia_hardware_serial::wire::SerialPortInfo& serial_port_info() const {
return serial_port_info_;
}
static const fuchsia_power_broker::PowerLevel kPowerLevelHandling =
static_cast<fuchsia_power_broker::PowerLevel>(fuchsia_power_broker::BinaryPowerLevel::kOn);
static const fuchsia_power_broker::PowerLevel kPowerLevelOff =
static_cast<fuchsia_power_broker::PowerLevel>(fuchsia_power_broker::BinaryPowerLevel::kOff);
private:
bool Readable();
bool Writable();
void EnableLocked(bool enable) TA_REQ(enable_lock_);
void HandleRX();
void HandleTX();
fit::closure MakeReadCallbackLocked(zx_status_t status, void* buf, size_t len) TA_REQ(read_lock_);
fit::closure MakeWriteCallbackLocked(zx_status_t status) TA_REQ(write_lock_);
void WatchRequiredLevel();
void HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_status_t status,
const zx_packet_interrupt_t* interrupt);
void HandleLeaseTimer(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal);
zx::result<fidl::ClientEnd<fuchsia_power_broker::LeaseControl>> AcquireLease();
ddk::PDevFidl pdev_;
const fuchsia_hardware_serial::wire::SerialPortInfo serial_port_info_;
fdf::MmioBuffer mmio_;
bool enabled_ TA_GUARDED(enable_lock_) = false;
// Protects enabling/disabling lifecycle.
fbl::Mutex enable_lock_;
// Protects status register and notify_cb.
fbl::Mutex status_lock_;
// Reads
fbl::Mutex read_lock_;
std::optional<internal::DriverTransportReadOperation> read_operation_ TA_GUARDED(read_lock_);
// Writes
fbl::Mutex write_lock_;
std::optional<internal::DriverTransportWriteOperation> write_operation_ TA_GUARDED(write_lock_);
const uint8_t* write_buffer_ TA_GUARDED(write_lock_) = nullptr;
size_t write_size_ TA_GUARDED(write_lock_) = 0;
fdf::UnownedSynchronizedDispatcher irq_dispatcher_;
zx::interrupt irq_;
async::IrqMethod<AmlUart, &AmlUart::HandleIrq> irq_handler_{this};
fidl::SyncClient<fuchsia_power_broker::CurrentLevel> current_level_client_;
fidl::Client<fuchsia_power_broker::RequiredLevel> required_level_client_;
fidl::SyncClient<fuchsia_power_broker::Lessor> lessor_client_;
std::optional<fidl::ClientEnd<fuchsia_power_broker::ElementControl>> element_control_client_end_;
fidl::ClientEnd<fuchsia_power_broker::LeaseControl> lease_control_client_end_
TA_GUARDED(timer_lock_);
static const uint32_t kPowerLeaseTimeoutMs = 300;
// Record the current deadline of the lease timer, so that the timer handler can tell whether the
// timer has been reset when it's executing.
zx::time timeout_;
// The timer to keep track of the time that this driver hold the wake lease client end, the lease
// will be dropped when the timer times out, the timer will be reset when there's another
// interrupt comes before it times out.
zx::timer lease_timer_;
async::WaitMethod<AmlUart, &AmlUart::HandleLeaseTimer> timer_waiter_{this};
fdf::UnownedSynchronizedDispatcher timer_dispatcher_;
fbl::Mutex timer_lock_;
};
} // namespace serial
#endif // SRC_DEVICES_SERIAL_DRIVERS_AML_UART_AML_UART_H_