blob: 7069969845ebd5de0e82a0b3242b2e6c31fee7d4 [file]
// 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 "ti-tca6408a.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/testing/cpp/real_loop.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/ddk/metadata.h>
#include <lib/fake-i2c/fake-i2c.h>
#include <zxtest/zxtest.h>
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace gpio {
class FakeTiTca6408aDevice : public fake_i2c::FakeI2c {
public:
uint8_t input_port() const { return input_port_; }
void set_input_port(uint8_t input_port) { input_port_ = input_port; }
uint8_t output_port() const { return output_port_; }
uint8_t polarity_inversion() const { return polarity_inversion_; }
uint8_t configuration() const { return configuration_; }
protected:
zx_status_t Transact(const uint8_t* write_buffer, size_t write_buffer_size, uint8_t* read_buffer,
size_t* read_buffer_size) override {
if (write_buffer_size > 2) {
return ZX_ERR_IO;
}
const uint8_t address = write_buffer[0];
uint8_t* reg = nullptr;
switch (address) {
case 0:
reg = &input_port_;
break;
case 1:
reg = &output_port_;
break;
case 2:
reg = &polarity_inversion_;
break;
case 3:
reg = &configuration_;
break;
default:
return ZX_ERR_IO;
};
if (write_buffer_size == 1) {
*read_buffer = *reg;
*read_buffer_size = 1;
} else {
*reg = write_buffer[1];
}
return ZX_OK;
}
private:
uint8_t input_port_ = 0;
uint8_t output_port_ = 0b1111'1111;
uint8_t polarity_inversion_ = 0;
uint8_t configuration_ = 0b1111'1111;
};
class TiTca6408aTest : public zxtest::Test, public loop_fixture::RealLoop {
public:
TiTca6408aTest() : ddk_(MockDevice::FakeRootParent()), outgoing_(dispatcher()) {}
void SetUp() override {
constexpr uint32_t kPinIndexOffset = 100;
ddk_->SetMetadata(DEVICE_METADATA_PRIVATE, &kPinIndexOffset, sizeof(kPinIndexOffset));
auto service_result = outgoing_.AddService<fuchsia_hardware_i2c::Service>(
fuchsia_hardware_i2c::Service::InstanceHandler(
{.device = fake_i2c_.bind_handler(dispatcher())}));
ZX_ASSERT(service_result.is_ok());
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ZX_ASSERT(endpoints.is_ok());
ZX_ASSERT(outgoing_.Serve(std::move(endpoints->server)).is_ok());
ddk_->AddFidlService(fuchsia_hardware_i2c::Service::Name, std::move(endpoints->client), "i2c");
PerformBlockingWork([&] {
// This TiTca6408a gets released by the MockDevice destructor.
ASSERT_OK(TiTca6408a::Create(nullptr, ddk_.get()));
});
MockDevice* device = ddk_->GetLatestChild();
ASSERT_NOT_NULL(device);
TiTca6408a* dut = device->GetDeviceContext<TiTca6408a>();
ASSERT_NOT_NULL(dut);
const gpio_impl_protocol_t proto{.ops = &dut->gpio_impl_protocol_ops_, .ctx = dut};
gpio_ = ddk::GpioImplProtocolClient(&proto);
EXPECT_EQ(fake_i2c_.polarity_inversion(), 0);
}
protected:
FakeTiTca6408aDevice fake_i2c_;
ddk::GpioImplProtocolClient gpio_;
private:
std::shared_ptr<MockDevice> ddk_;
component::OutgoingDirectory outgoing_;
};
TEST_F(TiTca6408aTest, ConfigInOut) {
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1111);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.ConfigOut(100, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1110);
PerformBlockingWork([&] { EXPECT_OK(gpio_.ConfigIn(100, GPIO_NO_PULL)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.ConfigOut(105, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1101'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1101'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.ConfigIn(105, GPIO_NO_PULL)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1101'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.ConfigOut(105, 1)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1101'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.ConfigOut(107, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b0111'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b0101'1111);
}
TEST_F(TiTca6408aTest, Read) {
fake_i2c_.set_input_port(0x55);
PerformBlockingWork([&] {
uint8_t value;
EXPECT_OK(gpio_.Read(100, &value));
EXPECT_EQ(value, 1);
EXPECT_OK(gpio_.Read(103, &value));
EXPECT_EQ(value, 0);
EXPECT_OK(gpio_.Read(104, &value));
EXPECT_EQ(value, 1);
EXPECT_OK(gpio_.Read(107, &value));
EXPECT_EQ(value, 0);
EXPECT_OK(gpio_.Read(105, nullptr));
});
}
TEST_F(TiTca6408aTest, Write) {
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1111);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(100, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(101, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'1100);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(103, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1111'0100);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(104, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1110'0100);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(106, 0)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1010'0100);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(101, 1)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1010'0110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
PerformBlockingWork([&] { EXPECT_OK(gpio_.Write(104, 1)); });
EXPECT_EQ(fake_i2c_.output_port(), 0b1011'0110);
EXPECT_EQ(fake_i2c_.configuration(), 0b1111'1111);
}
TEST_F(TiTca6408aTest, InvalidArgs) {
PerformBlockingWork([&] {
EXPECT_OK(gpio_.ConfigIn(107, GPIO_NO_PULL));
EXPECT_NOT_OK(gpio_.ConfigIn(108, GPIO_NO_PULL));
EXPECT_NOT_OK(gpio_.ConfigIn(107, GPIO_PULL_UP));
EXPECT_NOT_OK(gpio_.ConfigIn(100, GPIO_PULL_DOWN));
EXPECT_NOT_OK(gpio_.ConfigOut(0, 0));
EXPECT_NOT_OK(gpio_.ConfigOut(1, 1));
});
}
} // namespace gpio