| // 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. |
| |
| #include <fuchsia/hardware/registers/llcpp/fidl.h> |
| |
| #include <map> |
| #include <queue> |
| |
| #include <ddktl/protocol/registers.h> |
| |
| namespace mock_registers { |
| |
| // Mock Registers. FIDL implementation. |
| class MockRegisters : public ::llcpp::fuchsia::hardware::registers::Device::Interface { |
| public: |
| explicit MockRegisters(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {} |
| ~MockRegisters() {} |
| |
| // Manage the Fake FIDL Message Loop |
| zx_status_t Init(zx::channel remote) { |
| auto result = fidl::BindServer(dispatcher_, std::move(remote), this); |
| return result.is_ok() ? ZX_OK : result.error(); |
| } |
| |
| template <typename T> |
| void ExpectRead(uint64_t offset, T mask, T value) { |
| static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || |
| std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>); |
| GetExpectRead<T>()[offset].push(std::pair<T, T>(mask, value)); |
| } |
| |
| template <typename T> |
| void ExpectWrite(uint64_t offset, T mask, T value) { |
| static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || |
| std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>); |
| GetExpectWrite<T>()[offset].push(std::pair<T, T>(mask, value)); |
| } |
| |
| inline zx_status_t VerifyAll(); |
| |
| private: |
| // Implement Registers FIDL Protocol. |
| void ReadRegister8(uint64_t offset, uint8_t mask, ReadRegister8Completer::Sync& completer) { |
| ReadRegister(offset, mask, completer); |
| } |
| void ReadRegister16(uint64_t offset, uint16_t mask, ReadRegister16Completer::Sync& completer) { |
| ReadRegister(offset, mask, completer); |
| } |
| void ReadRegister32(uint64_t offset, uint32_t mask, ReadRegister32Completer::Sync& completer) { |
| ReadRegister(offset, mask, completer); |
| } |
| void ReadRegister64(uint64_t offset, uint64_t mask, ReadRegister64Completer::Sync& completer) { |
| ReadRegister(offset, mask, completer); |
| } |
| void WriteRegister8(uint64_t offset, uint8_t mask, uint8_t value, |
| WriteRegister8Completer::Sync& completer) { |
| WriteRegister(offset, mask, value, completer); |
| } |
| void WriteRegister16(uint64_t offset, uint16_t mask, uint16_t value, |
| WriteRegister16Completer::Sync& completer) { |
| WriteRegister(offset, mask, value, completer); |
| } |
| void WriteRegister32(uint64_t offset, uint32_t mask, uint32_t value, |
| WriteRegister32Completer::Sync& completer) { |
| WriteRegister(offset, mask, value, completer); |
| } |
| void WriteRegister64(uint64_t offset, uint64_t mask, uint64_t value, |
| WriteRegister64Completer::Sync& completer) { |
| WriteRegister(offset, mask, value, completer); |
| } |
| |
| // Helper functions for FIDL |
| template <typename T, typename Completer> |
| void ReadRegister(uint64_t offset, T mask, Completer& completer) { |
| auto& expect_read = GetExpectRead<T>()[offset]; |
| if (expect_read.empty()) { |
| completer.ReplyError(ZX_ERR_NOT_SUPPORTED); |
| return; |
| } |
| auto value = expect_read.front(); |
| expect_read.pop(); |
| if (value.first == mask) { |
| completer.ReplySuccess(static_cast<T>(value.second)); |
| } else { |
| completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| template <typename T, typename Completer> |
| void WriteRegister(uint64_t offset, T mask, T value, Completer& completer) { |
| auto& expect_write = GetExpectWrite<T>()[offset]; |
| if (expect_write.empty()) { |
| completer.ReplyError(ZX_ERR_NOT_SUPPORTED); |
| return; |
| } |
| auto expected_value = expect_write.front(); |
| expect_write.pop(); |
| if ((expected_value.first == mask) && (expected_value.second == value)) { |
| completer.ReplySuccess(); |
| } else { |
| completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| // Helper functions to get queues. |
| template <typename T> |
| auto& GetExpectRead() { |
| if constexpr (!std::is_same_v<T, uint64_t>) { |
| return expect_read64; |
| } else if constexpr (!std::is_same_v<T, uint32_t>) { |
| return expect_read32; |
| } else if constexpr (!std::is_same_v<T, uint16_t>) { |
| return expect_read16; |
| } else if constexpr (!std::is_same_v<T, uint8_t>) { |
| return expect_read8; |
| } |
| } |
| |
| template <typename T> |
| auto& GetExpectWrite() { |
| if constexpr (!std::is_same_v<T, uint64_t>) { |
| return expect_write64; |
| } else if constexpr (!std::is_same_v<T, uint32_t>) { |
| return expect_write32; |
| } else if constexpr (!std::is_same_v<T, uint16_t>) { |
| return expect_write16; |
| } else if constexpr (!std::is_same_v<T, uint8_t>) { |
| return expect_write8; |
| } |
| } |
| |
| async_dispatcher_t* dispatcher_; |
| |
| std::map<uint64_t, std::queue<std::pair<uint8_t, uint8_t>>> expect_read8; |
| std::map<uint64_t, std::queue<std::pair<uint16_t, uint16_t>>> expect_read16; |
| std::map<uint64_t, std::queue<std::pair<uint32_t, uint32_t>>> expect_read32; |
| std::map<uint64_t, std::queue<std::pair<uint64_t, uint64_t>>> expect_read64; |
| std::map<uint64_t, std::queue<std::pair<uint8_t, uint8_t>>> expect_write8; |
| std::map<uint64_t, std::queue<std::pair<uint16_t, uint16_t>>> expect_write16; |
| std::map<uint64_t, std::queue<std::pair<uint32_t, uint32_t>>> expect_write32; |
| std::map<uint64_t, std::queue<std::pair<uint64_t, uint64_t>>> expect_write64; |
| }; |
| |
| // Mock Registers Device implementing Banjo protocol to connect to FIDL implementation. |
| class MockRegistersDevice : public ddk::RegistersProtocol<MockRegistersDevice> { |
| public: |
| MockRegistersDevice(async_dispatcher_t* dispatcher) |
| : proto_({®isters_protocol_ops_, this}), fidl_service_(dispatcher) {} |
| |
| void RegistersConnect(zx::channel chan) { fidl_service_.Init(std::move(chan)); } |
| |
| const registers_protocol_t* proto() const { return &proto_; } |
| MockRegisters* fidl_service() { return &fidl_service_; } |
| |
| private: |
| registers_protocol_t proto_; |
| MockRegisters fidl_service_; |
| }; |
| |
| zx_status_t MockRegisters::VerifyAll() { |
| for (const auto& expect_read : GetExpectRead<uint8_t>()) { |
| if (!expect_read.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| for (const auto& expect_read : GetExpectRead<uint16_t>()) { |
| if (!expect_read.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| for (const auto& expect_read : GetExpectRead<uint32_t>()) { |
| if (!expect_read.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| for (const auto& expect_read : GetExpectRead<uint64_t>()) { |
| if (!expect_read.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| |
| for (const auto& expect_write : GetExpectWrite<uint8_t>()) { |
| if (!expect_write.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| for (const auto& expect_write : GetExpectWrite<uint16_t>()) { |
| if (!expect_write.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| for (const auto& expect_write : GetExpectWrite<uint32_t>()) { |
| if (!expect_write.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| for (const auto& expect_write : GetExpectWrite<uint64_t>()) { |
| if (!expect_write.second.empty()) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| } // namespace mock_registers |