blob: 580e45b0697603a286accf169847e623cc0e7aff [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 <fuchsia/hardware/gpio/cpp/banjo-mock.h>
#include <lib/ddk/metadata.h>
#include <lib/fake-i2c/fake-i2c.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <lib/focaltech/focaltech.h>
#include <hid/ft3x27.h>
#include <hid/ft5726.h>
#include <hid/ft6336.h>
#include <zxtest/zxtest.h>
#include "ft_firmware.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:
uint32_t firmware_write_size() const { return firmware_write_size_; }
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 {
return ZX_ERR_IO;
}
return ZX_OK;
}
private:
uint16_t flash_status_ = 0;
uint8_t ecc_ = 0;
uint32_t firmware_write_size_ = 0;
};
class FocaltechTest : public zxtest::Test {
public:
void SetUp() override {
fbl::Array<fake_ddk::FragmentEntry> fragments(new fake_ddk::FragmentEntry[3], 3);
fragments[0].name = "i2c";
fragments[0].protocols.push_back(fake_ddk::ProtocolEntry{
.id = ZX_PROTOCOL_I2C,
.proto =
{
.ops = i2c_.GetProto()->ops,
.ctx = i2c_.GetProto()->ctx,
},
});
fragments[1].name = "gpio-int";
fragments[1].protocols.push_back(fake_ddk::ProtocolEntry{
.id = ZX_PROTOCOL_GPIO,
.proto =
{
.ops = interrupt_gpio_.GetProto()->ops,
.ctx = interrupt_gpio_.GetProto()->ctx,
},
});
fragments[2].name = "gpio-reset";
fragments[2].protocols.push_back(fake_ddk::ProtocolEntry{
.id = ZX_PROTOCOL_GPIO,
.proto =
{
.ops = reset_gpio_.GetProto()->ops,
.ctx = reset_gpio_.GetProto()->ctx,
},
});
ddk_.SetFragments(std::move(fragments));
zx::interrupt interrupt;
ASSERT_OK(zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &interrupt));
interrupt_gpio_.ExpectConfigIn(ZX_OK, GPIO_NO_PULL)
.ExpectGetInterrupt(ZX_OK, ZX_INTERRUPT_MODE_EDGE_LOW, std::move(interrupt));
reset_gpio_.ExpectConfigOut(ZX_OK, 0).ExpectWrite(ZX_OK, 1);
}
protected:
fake_ddk::Bind ddk_;
FakeFtDevice i2c_;
private:
ddk::MockGpio interrupt_gpio_;
ddk::MockGpio reset_gpio_;
};
TEST_F(FocaltechTest, Metadata3x27) {
constexpr FocaltechMetadata kFt3x27Metadata = {
.device_id = FOCALTECH_DEVICE_FT3X27,
.needs_firmware = false,
};
ddk_.SetMetadata(DEVICE_METADATA_PRIVATE, &kFt3x27Metadata, sizeof(kFt3x27Metadata));
FtDevice dut(nullptr);
EXPECT_OK(dut.Init());
uint8_t actual_descriptor[1024];
size_t actual_size = 0;
EXPECT_OK(dut.HidbusGetDescriptor(0, actual_descriptor, sizeof(actual_descriptor), &actual_size));
const uint8_t* expected_descriptor;
const size_t expected_size = get_ft3x27_report_desc(&expected_descriptor);
ASSERT_EQ(actual_size, expected_size);
EXPECT_BYTES_EQ(actual_descriptor, expected_descriptor, expected_size);
}
TEST_F(FocaltechTest, Metadata5726) {
constexpr FocaltechMetadata kFt5726Metadata = {
.device_id = FOCALTECH_DEVICE_FT5726,
.needs_firmware = false,
};
ddk_.SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata));
FtDevice dut(nullptr);
EXPECT_OK(dut.Init());
uint8_t actual_descriptor[1024];
size_t actual_size = 0;
EXPECT_OK(dut.HidbusGetDescriptor(0, actual_descriptor, sizeof(actual_descriptor), &actual_size));
const uint8_t* expected_descriptor;
const size_t expected_size = get_ft5726_report_desc(&expected_descriptor);
ASSERT_EQ(actual_size, expected_size);
EXPECT_BYTES_EQ(actual_descriptor, expected_descriptor, expected_size);
}
TEST_F(FocaltechTest, Metadata6336) {
constexpr FocaltechMetadata kFt6336Metadata = {
.device_id = FOCALTECH_DEVICE_FT6336,
.needs_firmware = false,
};
ddk_.SetMetadata(DEVICE_METADATA_PRIVATE, &kFt6336Metadata, sizeof(kFt6336Metadata));
FtDevice dut(nullptr);
EXPECT_OK(dut.Init());
uint8_t actual_descriptor[1024];
size_t actual_size = 0;
EXPECT_OK(dut.HidbusGetDescriptor(0, actual_descriptor, sizeof(actual_descriptor), &actual_size));
const uint8_t* expected_descriptor;
const size_t expected_size = get_ft6336_report_desc(&expected_descriptor);
ASSERT_EQ(actual_size, expected_size);
EXPECT_BYTES_EQ(actual_descriptor, expected_descriptor, expected_size);
}
TEST_F(FocaltechTest, Firmware5726) {
constexpr FocaltechMetadata kFt5726Metadata = {
.device_id = FOCALTECH_DEVICE_FT5726,
.needs_firmware = true,
.display_vendor = 1,
.ddic_version = 1,
};
ddk_.SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata));
FtDevice dut(nullptr);
EXPECT_OK(dut.Init());
EXPECT_EQ(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,
};
ddk_.SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata));
FtDevice dut(nullptr);
EXPECT_OK(dut.Init());
EXPECT_EQ(i2c_.firmware_write_size(), 0);
}
} // namespace ft