blob: e92862a4376814451bb277b19acf30fb43ea46b0 [file] [log] [blame]
// Copyright 2021 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 "i2c.h"
#include <fidl/fuchsia.hardware.i2c.businfo/cpp/wire.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/fdf/env.h>
#include <ddktl/device.h>
#include <zxtest/zxtest.h>
#include "fake-incoming-namespace.h"
#include "lib/ddk/metadata.h"
#include "src/devices/i2c/drivers/i2c/i2c-child.h"
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace {
namespace fi2c = fuchsia_hardware_i2c_businfo::wire;
fi2c::I2CChannel MakeChannel(fidl::AnyArena& arena, uint16_t addr) {
return fi2c::I2CChannel::Builder(arena).address(addr).Build();
}
} // namespace
class I2cMetadataTest : public zxtest::Test {
public:
void TearDown() override {
auto* parent = fake_root_.get();
for (auto& device : parent->children()) {
device_async_remove(device.get());
}
mock_ddk::ReleaseFlaggedDevices(parent);
}
protected:
void CreateDevice(fi2c::I2CBusMetadata& metadata) {
// Setup i2c impl parent
zx::result endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ASSERT_OK(endpoints);
incoming_.SyncCall(&i2c::FakeIncomingNamespace::AddI2cImplService,
std::move(endpoints->server));
fake_root_->AddFidlService(fuchsia_hardware_i2cimpl::Service::Name,
std::move(endpoints->client));
// Setup metadata
fit::result metadata_bytes = fidl::Persist(metadata);
ASSERT_TRUE(metadata_bytes.is_ok());
fake_root_->SetMetadata(DEVICE_METADATA_I2C_CHANNELS, metadata_bytes->data(),
metadata_bytes->size());
ASSERT_OK(i2c::I2cDevice::Create(nullptr, fake_root_.get()));
ASSERT_EQ(fake_root_->child_count(), 1);
i2c_ = fake_root_->GetLatestChild()->GetDeviceContext<i2c::I2cDevice>();
ASSERT_NOT_NULL(i2c_);
}
i2c::I2cDevice* i2c() { return i2c_; }
private:
std::shared_ptr<zx_device> fake_root_ = MockDevice::FakeRootParent();
i2c::I2cDevice* i2c_;
fdf::UnownedSynchronizedDispatcher incoming_dispatcher_{
fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher()};
async_patterns::TestDispatcherBound<i2c::FakeIncomingNamespace> incoming_{
incoming_dispatcher_->async_dispatcher(), std::in_place, 64};
};
TEST_F(I2cMetadataTest, ProvidesMetadataToChildren) {
std::vector<fi2c::I2CChannel> channels;
fidl::Arena<> arena;
channels.emplace_back(MakeChannel(arena, 0xa));
channels.emplace_back(MakeChannel(arena, 0xb));
auto channels_view = fidl::VectorView<fi2c::I2CChannel>::FromExternal(channels);
auto metadata = fi2c::I2CBusMetadata::Builder(arena).channels(channels_view).Build();
CreateDevice(metadata);
zx_device_t* i2c_device = i2c()->zxdev();
// There should be two devices per channel.
ASSERT_EQ(i2c_device->child_count(), 2);
for (auto& child : i2c_device->children()) {
uint16_t expected_addr = 0xff;
for (const auto& prop : child->GetProperties()) {
if (prop.id == BIND_I2C_ADDRESS) {
expected_addr = static_cast<uint16_t>(prop.value);
}
}
auto decoded =
ddk::GetEncodedMetadata<fi2c::I2CChannel>(child.get(), DEVICE_METADATA_I2C_DEVICE);
ASSERT_TRUE(decoded.is_ok());
ASSERT_EQ(decoded->address(), expected_addr);
}
}