blob: 5b3310a9506698be6f3eeeed0db315796f602a11 [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 "ft_device.h"
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/ddk/metadata.h>
#include <lib/fake-i2c/fake-i2c.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/focaltech/focaltech.h>
#include <lib/zx/clock.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <cstddef>
#include <gtest/gtest.h>
#include "ft_firmware.h"
#include "lib/zx/result.h"
#include "src/devices/gpio/testing/fake-gpio/fake-gpio.h"
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wc99-designator"
// Firmware must be at least 0x120 bytes. Add some extra size to make them different.
constexpr uint8_t kFirmware0[0x120 + 0] = {0x00, 0xd2, 0xc8, 0x53, [0x10a] = 0xd5};
constexpr uint8_t kFirmware1[0x120 + 1] = {0x10, 0x58, 0xb2, 0x12, [0x10a] = 0xc8};
constexpr uint8_t kFirmware2[0x120 + 2] = {0xb7, 0xf9, 0xd1, 0x12, [0x10a] = 0xb0};
constexpr uint8_t kFirmware3[0x120 + 3] = {0x02, 0x69, 0x96, 0x71, [0x10a] = 0x61};
#pragma GCC diagnostic pop
} // namespace
namespace ft {
const FirmwareEntry kFirmwareEntries[] = {
{
.display_vendor = 0,
.ddic_version = 0,
.firmware_data = kFirmware0,
.firmware_size = sizeof(kFirmware0),
},
{
.display_vendor = 1,
.ddic_version = 0,
.firmware_data = kFirmware1,
.firmware_size = sizeof(kFirmware1),
},
{
.display_vendor = 0,
.ddic_version = 1,
.firmware_data = kFirmware2,
.firmware_size = sizeof(kFirmware2),
},
{
.display_vendor = 1,
.ddic_version = 1,
.firmware_data = kFirmware3,
.firmware_size = sizeof(kFirmware3),
},
};
const size_t kNumFirmwareEntries = std::size(kFirmwareEntries);
class FakeFtDevice : public fake_i2c::FakeI2c {
public:
~FakeFtDevice() { ZX_ASSERT(expected_report_.empty()); }
uint32_t firmware_write_size() const { return firmware_write_size_; }
void ExpectReport(uint8_t addr, std::vector<uint8_t> report) {
expected_report_.emplace(addr, std::move(report));
}
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 < 1) {
return ZX_ERR_IO;
}
*read_buffer_size = 0;
if (write_buffer[0] == 0xa3) { // Chip core register
read_buffer[0] = 0x58; // Firmware is valid
*read_buffer_size = 1;
} else if (write_buffer[0] == 0xa6) { // Chip firmware version
read_buffer[0] = kFirmware1[0x10a]; // Set to a known version to test the up-to-date case
*read_buffer_size = 1;
} else if (write_buffer[0] == 0xfc && write_buffer_size == 2) { // Chip work mode
if (write_buffer[1] != 0xaa && write_buffer[1] != 0x55) { // Soft reset
return ZX_ERR_IO;
}
} else if (write_buffer[0] == 0xeb && write_buffer_size == 3) { // HID to STD
if (write_buffer[1] != 0xaa || write_buffer[2] != 0x09) {
return ZX_ERR_IO;
}
} else if (write_buffer[0] == 0x55 && write_buffer_size == 1) { // Unlock boot
} else if (write_buffer[0] == 0x90 && write_buffer_size == 1) { // Boot ID
read_buffer[0] = 0x58;
read_buffer[1] = 0x2c;
*read_buffer_size = 2;
} else if (write_buffer[0] == 0x09 && write_buffer_size == 2) { // Flash erase
if (write_buffer[1] != 0x0b) { // Erase app area
return ZX_ERR_IO;
}
} else if (write_buffer[0] == 0xb0 && write_buffer_size == 4) { // Set erase size
} else if (write_buffer[0] == 0x61 && write_buffer_size == 1) { // Start erase
ecc_ = 0;
flash_status_ = 0xf0aa;
} else if (write_buffer[0] == 0x6a && write_buffer_size == 1) { // Read flash status
read_buffer[0] = flash_status_ >> 8;
read_buffer[1] = flash_status_ & 0xff;
*read_buffer_size = 2;
} else if (write_buffer[0] == 0xbf && write_buffer_size >= 6) { // Firmware packet
const uint32_t address = (write_buffer[1] << 16) | (write_buffer[2] << 8) | write_buffer[3];
const auto packet_size = static_cast<uint8_t>((write_buffer[4] << 8) | write_buffer[5]);
if ((packet_size + 6) != write_buffer_size) {
return ZX_ERR_IO;
}
for (uint32_t i = 6; i < write_buffer_size; i++) {
ecc_ ^= write_buffer[i];
}
flash_status_ = (0x1000 + (address / packet_size)) & 0xffff;
firmware_write_size_ += packet_size; // Ignore overlapping addresses.
} else if (write_buffer[0] == 0x64 && write_buffer_size == 1) { // ECC initialization
} else if (write_buffer[0] == 0x65 && write_buffer_size == 6) { // Start ECC calculation
flash_status_ = 0xf055; // ECC calculation done
} else if (write_buffer[0] == 0x66 && write_buffer_size == 1) { // Read calculated ECC
read_buffer[0] = ecc_;
*read_buffer_size = 1;
} else if (write_buffer[0] == 0x07 && write_buffer_size == 1) { // Reset
} else if ((write_buffer[0] == FTS_REG_TYPE || write_buffer[0] == FTS_REG_FIRMID ||
write_buffer[0] == FTS_REG_VENDOR_ID || write_buffer[0] == FTS_REG_PANEL_ID ||
write_buffer[0] == FTS_REG_RELEASE_ID_HIGH ||
write_buffer[0] == FTS_REG_RELEASE_ID_LOW ||
write_buffer[0] == FTS_REG_IC_VERSION) &&
write_buffer_size == 1) { // LogRegisterValue
read_buffer[0] = 0;
*read_buffer_size = 1;
} else if (write_buffer_size == 1) { // Read report
EXPECT_FALSE(expected_report_.empty());
EXPECT_EQ(write_buffer[0], expected_report_.front().first);
memcpy(read_buffer, expected_report_.front().second.data(),
expected_report_.front().second.size());
*read_buffer_size = expected_report_.front().second.size();
expected_report_.pop();
}
return ZX_OK;
}
private:
uint16_t flash_status_ = 0;
uint8_t ecc_ = 0;
uint32_t firmware_write_size_ = 0;
std::queue<std::pair<uint8_t, std::vector<uint8_t>>> expected_report_; // address, data pair
};
struct IncomingNamespace {
FakeFtDevice i2c_;
fake_gpio::FakeGpio interrupt_gpio_;
fake_gpio::FakeGpio reset_gpio_;
component::OutgoingDirectory i2c_fragment_outgoing_{async_get_default_dispatcher()};
component::OutgoingDirectory interrupt_gpio_fragment_outgoing_{async_get_default_dispatcher()};
component::OutgoingDirectory reset_gpio_fragment_outgoing_{async_get_default_dispatcher()};
};
class FocaltechTest : public testing::Test {
public:
void SetUp() override {
ASSERT_EQ(ZX_OK, incoming_loop_.StartThread("incoming-ns-thread"));
// I2c fragment
{
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ZX_ASSERT(endpoints.is_ok());
incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable {
zx::result service_result =
infra->i2c_fragment_outgoing_.AddService<fuchsia_hardware_i2c::Service>(
fuchsia_hardware_i2c::Service::InstanceHandler(
{.device = infra->i2c_.bind_handler(async_get_default_dispatcher())}));
ZX_ASSERT(service_result.is_ok());
ZX_ASSERT(infra->i2c_fragment_outgoing_.Serve(std::move(server)).is_ok());
});
fake_parent_->AddFidlService(fuchsia_hardware_i2c::Service::Name,
std::move(endpoints->client), "i2c");
}
// Reset gpio fragment
{
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ZX_ASSERT(endpoints.is_ok());
incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable {
auto service_result =
infra->reset_gpio_fragment_outgoing_.AddService<fuchsia_hardware_gpio::Service>(
infra->reset_gpio_.CreateInstanceHandler());
ZX_ASSERT(service_result.is_ok());
ZX_ASSERT(infra->reset_gpio_fragment_outgoing_.Serve(std::move(server)).is_ok());
});
fake_parent_->AddFidlService(fuchsia_hardware_gpio::Service::Name,
std::move(endpoints->client), "gpio-reset");
}
// Interrupt gpio fragment
{
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ZX_ASSERT(endpoints.is_ok());
incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable {
auto service_result =
infra->interrupt_gpio_fragment_outgoing_.AddService<fuchsia_hardware_gpio::Service>(
infra->interrupt_gpio_.CreateInstanceHandler());
ZX_ASSERT(service_result.is_ok());
ZX_ASSERT(infra->interrupt_gpio_fragment_outgoing_.Serve(std::move(server)).is_ok());
});
fake_parent_->AddFidlService(fuchsia_hardware_gpio::Service::Name,
std::move(endpoints->client), "gpio-int");
}
zx::interrupt interrupt;
ASSERT_EQ(ZX_OK, zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &interrupt));
ASSERT_EQ(ZX_OK, interrupt.duplicate(ZX_RIGHT_SAME_RIGHTS, &irq_));
incoming_.SyncCall([interrupt = std::move(interrupt)](IncomingNamespace* infra) mutable {
infra->interrupt_gpio_.SetInterrupt(zx::ok(std::move(interrupt)));
});
}
fidl::ClientEnd<fuchsia_input_report::InputDevice> CreateDut() {
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() {
EXPECT_EQ(ZX_OK, FtDevice::Create(nullptr, fake_parent_.get()));
});
EXPECT_EQ(ZX_OK, result.status_value());
EXPECT_EQ(size_t(1), fake_parent_->child_count());
child_ = fake_parent_->GetLatestChild();
dut_ = child_->GetDeviceContext<FtDevice>();
VerifyGpioInit();
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputDevice>();
EXPECT_EQ(ZX_OK, endpoints.status_value());
fidl::BindServer(dispatcher_->async_dispatcher(), std::move(endpoints->server), dut_);
return std::move(std::move(endpoints->client));
}
void TearDown() override {
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() {
device_async_remove(child_);
mock_ddk::ReleaseFlaggedDevices(fake_parent_.get());
;
});
EXPECT_EQ(ZX_OK, result.status_value());
}
private:
void VerifyGpioInit() {
incoming_.SyncCall([](IncomingNamespace* infra) mutable {
std::vector interrupt_states = infra->interrupt_gpio_.GetStateLog();
ASSERT_GE(interrupt_states.size(), size_t(1));
ASSERT_EQ(fake_gpio::ReadSubState{.flags = fuchsia_hardware_gpio::GpioFlags::kNoPull},
interrupt_states[0].sub_state);
std::vector reset_states = infra->reset_gpio_.GetStateLog();
ASSERT_GE(reset_states.size(), size_t(2));
ASSERT_EQ(fake_gpio::WriteSubState{.value = 0}, reset_states[0].sub_state);
ASSERT_EQ(fake_gpio::WriteSubState{.value = 1}, reset_states[1].sub_state);
});
}
async::Loop incoming_loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
protected:
std::shared_ptr<MockDevice> fake_parent_ = MockDevice::FakeRootParent();
fdf::UnownedSynchronizedDispatcher dispatcher_ =
fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher();
zx::interrupt irq_;
zx_device_t* child_;
FtDevice* dut_;
async_patterns::TestDispatcherBound<IncomingNamespace> incoming_{incoming_loop_.dispatcher(),
std::in_place};
};
void VerifyDescriptor(const fuchsia_input_report::wire::DeviceDescriptor& descriptor, int64_t x_max,
int64_t y_max) {
EXPECT_TRUE(descriptor.has_device_info());
EXPECT_EQ(descriptor.device_info().vendor_id,
static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle));
EXPECT_EQ(descriptor.device_info().product_id,
static_cast<uint32_t>(
fuchsia_input_report::wire::VendorGoogleProductId::kFocaltechTouchscreen));
EXPECT_TRUE(descriptor.has_touch());
EXPECT_FALSE(descriptor.has_consumer_control());
EXPECT_FALSE(descriptor.has_keyboard());
EXPECT_FALSE(descriptor.has_mouse());
EXPECT_FALSE(descriptor.has_sensor());
EXPECT_TRUE(descriptor.touch().has_input());
EXPECT_FALSE(descriptor.touch().has_feature());
EXPECT_TRUE(descriptor.touch().input().has_touch_type());
EXPECT_EQ(descriptor.touch().input().touch_type(),
fuchsia_input_report::wire::TouchType::kTouchscreen);
EXPECT_TRUE(descriptor.touch().input().has_max_contacts());
EXPECT_EQ(descriptor.touch().input().max_contacts(), uint32_t(10));
EXPECT_FALSE(descriptor.touch().input().has_buttons());
EXPECT_TRUE(descriptor.touch().input().has_contacts());
EXPECT_EQ(descriptor.touch().input().contacts().count(), size_t(10));
for (const auto& c : descriptor.touch().input().contacts()) {
EXPECT_TRUE(c.has_position_x());
EXPECT_TRUE(c.has_position_y());
EXPECT_FALSE(c.has_contact_height());
EXPECT_FALSE(c.has_contact_width());
EXPECT_FALSE(c.has_pressure());
EXPECT_EQ(c.position_x().range.min, 0);
EXPECT_EQ(c.position_x().range.max, x_max);
EXPECT_EQ(c.position_x().unit.type, fuchsia_input_report::wire::UnitType::kOther);
EXPECT_EQ(c.position_x().unit.exponent, 0);
EXPECT_EQ(c.position_y().range.min, 0);
EXPECT_EQ(c.position_y().range.max, y_max);
EXPECT_EQ(c.position_y().unit.type, fuchsia_input_report::wire::UnitType::kOther);
EXPECT_EQ(c.position_y().unit.exponent, 0);
}
}
TEST_F(FocaltechTest, Metadata3x27) {
constexpr FocaltechMetadata kFt3x27Metadata = {
.device_id = FOCALTECH_DEVICE_FT3X27,
.needs_firmware = false,
};
fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt3x27Metadata, sizeof(kFt3x27Metadata));
fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut());
auto result = client->GetDescriptor();
EXPECT_TRUE(result.ok());
VerifyDescriptor(result->descriptor, 600, 1024);
}
TEST_F(FocaltechTest, Metadata5726) {
constexpr FocaltechMetadata kFt5726Metadata = {
.device_id = FOCALTECH_DEVICE_FT5726,
.needs_firmware = false,
};
fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata));
fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut());
auto result = client->GetDescriptor();
EXPECT_TRUE(result.ok());
VerifyDescriptor(result->descriptor, 800, 1280);
}
TEST_F(FocaltechTest, Metadata6336) {
constexpr FocaltechMetadata kFt6336Metadata = {
.device_id = FOCALTECH_DEVICE_FT6336,
.needs_firmware = false,
};
fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt6336Metadata, sizeof(kFt6336Metadata));
fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut());
auto result = client->GetDescriptor();
EXPECT_TRUE(result.ok());
VerifyDescriptor(result->descriptor, 480, 800);
}
TEST_F(FocaltechTest, Firmware5726) {
constexpr FocaltechMetadata kFt5726Metadata = {
.device_id = FOCALTECH_DEVICE_FT5726,
.needs_firmware = true,
.display_vendor = 1,
.ddic_version = 1,
};
fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata));
CreateDut();
incoming_.SyncCall([](IncomingNamespace* infra) mutable {
EXPECT_EQ(infra->i2c_.firmware_write_size(), sizeof(kFirmware3));
});
}
TEST_F(FocaltechTest, Firmware5726UpToDate) {
constexpr FocaltechMetadata kFt5726Metadata = {
.device_id = FOCALTECH_DEVICE_FT5726,
.needs_firmware = true,
.display_vendor = 1,
.ddic_version = 0,
};
fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata));
CreateDut();
incoming_.SyncCall([](IncomingNamespace* infra) mutable {
EXPECT_EQ(infra->i2c_.firmware_write_size(), uint32_t(0));
});
}
TEST_F(FocaltechTest, Touch) {
constexpr FocaltechMetadata kFt6336Metadata = {
.device_id = FOCALTECH_DEVICE_FT6336,
.needs_firmware = false,
};
fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt6336Metadata, sizeof(kFt6336Metadata));
fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut());
auto reader_endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_EQ(ZX_OK, reader_endpoints.status_value());
auto result = client->GetInputReportsReader(std::move(reader_endpoints->server));
ASSERT_EQ(ZX_OK, result.status());
auto reader = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(
std::move(reader_endpoints->client));
ASSERT_EQ(ZX_OK, dut_->WaitForNextReader(zx::duration::infinite()));
// clang-format off
static const uint8_t expected_report[] = {
0x02, // contact_count
// Contact 0, finger_id = 0
0x80, 0x01, // x = 0x001
0x00, 0x13, // y = 0x013
0x00, 0x00,
// Contact 1, finger_id = 1
0x80, 0x31, // x = 0x031
0x10, 0x00, // y = 0x000
0x00, 0x00,
// Contact 2
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 3
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 4
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 5
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 6
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Contact 9
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// clang-format on
incoming_.SyncCall([](IncomingNamespace* infra) mutable {
for (size_t i = 0; i < sizeof(expected_report); i += 8) {
infra->i2c_.ExpectReport(
static_cast<uint8_t>(i + FTS_REG_CURPOINT),
std::vector<uint8_t>(expected_report + i,
expected_report + std::min(i + 8, sizeof(expected_report))));
}
});
irq_.trigger(0, zx::clock::get_monotonic());
{
auto result = reader->ReadInputReports();
ASSERT_EQ(ZX_OK, result.status());
ASSERT_FALSE(result.value().is_error());
auto& reports = result.value().value()->reports;
ASSERT_EQ(size_t(1), reports.count());
auto report = reports[0];
ASSERT_TRUE(report.has_event_time());
ASSERT_TRUE(report.has_touch());
auto& touch_report = report.touch();
ASSERT_TRUE(touch_report.has_contacts());
ASSERT_EQ(touch_report.contacts().count(), size_t(2));
EXPECT_EQ(touch_report.contacts()[0].contact_id(), uint32_t(0));
EXPECT_EQ(touch_report.contacts()[0].position_x(), 0x001);
EXPECT_EQ(touch_report.contacts()[0].position_y(), 0x013);
EXPECT_EQ(touch_report.contacts()[1].contact_id(), uint32_t(1));
EXPECT_EQ(touch_report.contacts()[1].position_x(), 0x031);
EXPECT_EQ(touch_report.contacts()[1].position_y(), 0x000);
}
}
} // namespace ft
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}