| #ifndef ZIRCON_SYSTEM_DEV_SERIAL_NUC_SERIAL_NUC_SERIAL_H_ |
| #define ZIRCON_SYSTEM_DEV_SERIAL_NUC_SERIAL_NUC_SERIAL_H_ |
| |
| #include <ddktl/device.h> |
| #include <ddktl/protocol/acpi.h> |
| #include <ddktl/protocol/serialimpl.h> |
| #include <fbl/function.h> |
| #include <hwreg/bitfields.h> |
| #include <lib/zx/fifo.h> |
| #include <zircon/compiler.h> |
| |
| #include <mutex> |
| #include <thread> |
| #include <vector> |
| |
| namespace uart16550 { |
| |
| class Uart16550; |
| using DeviceType = ddk::Device<Uart16550, ddk::Unbindable>; |
| |
| class Uart16550 : public DeviceType, public ddk::SerialImplProtocol<Uart16550, ddk::base_protocol> { |
| public: |
| Uart16550(); |
| |
| explicit Uart16550(zx_device_t* parent); |
| |
| size_t FifoDepth() const; |
| |
| bool Enabled(); |
| |
| bool NotifyCallbackSet(); |
| |
| static zx_status_t Create(void* ctx, zx_device_t* parent); |
| |
| zx_status_t Init(); |
| |
| // test-use only |
| zx_status_t Init(zx::interrupt interrupt, fbl::Function<uint8_t(uint16_t)> port_read, |
| fbl::Function<void(uint8_t, uint16_t)> port_write); |
| |
| // 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(void* buf, size_t size, size_t* actual); |
| |
| // ddk::SerialImplProtocol |
| zx_status_t SerialImplWrite(const void* buf, size_t size, size_t* actual); |
| |
| // ddk::SerialImplProtocol |
| zx_status_t SerialImplSetNotifyCallback(const serial_notify_t* cb); |
| |
| // ddk::Releasable |
| void DdkRelease(); |
| |
| // ddk::Unbindable |
| void DdkUnbind(); |
| |
| private: |
| class PortIo { |
| public: |
| using PortReadFn = fbl::Function<uint8_t(uint16_t)>; |
| using PortWriteFn = fbl::Function<void(uint8_t, uint16_t)>; |
| |
| PortIo() = default; |
| |
| PortIo(PortReadFn read, PortWriteFn write, uint16_t base) |
| : port_read_(std::move(read)), port_write_(std::move(write)), port_base_(base) {} |
| |
| template <typename IntType> |
| void Write(IntType val, uint32_t offset) { |
| static_assert(std::is_same_v<IntType, uint8_t>, "unsupported register access width"); |
| port_write_(val, static_cast<uint16_t>(port_base_ + offset)); |
| } |
| |
| template <typename IntType> |
| IntType Read(uint32_t offset) { |
| static_assert(std::is_same_v<IntType, uint8_t>, "unsupported register access width"); |
| return port_read_(static_cast<uint16_t>(port_base_ + offset)); |
| } |
| |
| uintptr_t base() const { return port_base_; } |
| |
| private: |
| PortReadFn port_read_ = [](uint16_t /*unused*/) -> uint8_t { return 0x00; }; |
| PortWriteFn port_write_ = [](uint8_t /*unused*/, uint16_t /*unused*/) {}; |
| uint16_t port_base_ = 0; |
| }; |
| |
| 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_; |
| std::mutex device_mutex_; |
| |
| std::thread interrupt_thread_; |
| zx::interrupt interrupt_; |
| |
| serial_notify_t notify_cb_ __TA_GUARDED(device_mutex_) = {}; |
| PortIo port_io_ __TA_GUARDED(device_mutex_); |
| |
| 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 // ZIRCON_SYSTEM_DEV_SERIAL_NUC_SERIAL_NUC_SERIAL_H_ |