text: // 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 <fuchsia/hardware/acpi/cpp/banjo.h>
  #include <fuchsia/hardware/serialimpl/cpp/banjo.h>
  #include <lib/fit/function.h>
  #include <lib/zx/fifo.h>
  #include <zircon/compiler.h>
  
  #include <mutex>
  #include <thread>
  #include <variant>
  #include <vector>
  
  #include <ddktl/device.h>
  #include <hwreg/bitfields.h>
  #include <hwreg/pio.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 ddk::SerialImplProtocol<Uart16550, ddk::base_protocol> {
   public:
    Uart16550();
  
    explicit Uart16550(zx_device_t* parent, acpi::Client acpi);
  
    size_t FifoDepth() const;
  
    bool Enabled();
  
    bool NotifyCallbackSet();
  
    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::SerialImplProtocol
    zx_status_t SerialImplGetInfo(serial_port_info_t* info);
  
    // ddk::SerialImplProtocol
    zx_status_t SerialImplConfig(uint32_t baud_rate, uint32_t flags);
  
    // ddk::SerialImplProtocol
    zx_status_t SerialImplEnable(bool enable);
  
    // ddk::SerialImplProtocol
    zx_status_t SerialImplRead(uint8_t* buf, size_t size, size_t* actual);
  
    // ddk::SerialImplProtocol
    zx_status_t SerialImplWrite(const uint8_t* buf, size_t size, size_t* actual);
  
    // ddk::SerialImplProtocol
    zx_status_t SerialImplSetNotifyCallback(const serial_notify_t* cb);
  
    // ddk::Releasable
    void DdkRelease();
  
   private:
    bool SupportsAutomaticFlowControl() const;
  
    void ResetFifosLocked() __TA_REQUIRES(device_mutex_);
  
    void InitFifosLocked() __TA_REQUIRES(device_mutex_);
  
    void NotifyLocked() __TA_REQUIRES(device_mutex_);
  
    void HandleInterrupts();
  
    ddk::AcpiProtocolClient acpi_;
    acpi::Client acpi_fidl_;
    std::mutex device_mutex_;
  
    std::thread interrupt_thread_;
    zx::interrupt interrupt_;
  
    serial_notify_t notify_cb_ __TA_GUARDED(device_mutex_) = {};
  
  #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;
    serial_state_t state_ __TA_GUARDED(device_mutex_) = 0;
  };  // namespace uart16550
  
  }  // namespace uart16550
  
  #endif  // SRC_DEVICES_SERIAL_DRIVERS_UART16550_UART16550_H_
  