| // 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 "gt6853.h" |
| |
| #include <endian.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/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/device-protocol/i2c-channel.h> |
| #include <lib/fake-i2c/fake-i2c.h> |
| #include <lib/inspect/testing/cpp/zxtest/inspect.h> |
| #include <lib/zx/clock.h> |
| |
| #include <array> |
| #include <vector> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "src/devices/gpio/testing/fake-gpio/fake-gpio.h" |
| #include "src/devices/testing/mock-ddk/mock-device.h" |
| |
| namespace { |
| |
| zx::vmo* config_vmo = nullptr; |
| size_t config_size = 0; |
| |
| zx::vmo* firmware_vmo = nullptr; |
| size_t firmware_size = 0; |
| |
| const char* config_path = nullptr; |
| |
| } // namespace |
| |
| zx_status_t load_firmware_from_driver(zx_driver_t* drv, zx_device_t* device, const char* path, |
| zx_handle_t* fw, size_t* size) { |
| const std::string_view path_str(path); |
| if ((path_str == GT6853_CONFIG_9364_PATH || path_str == GT6853_CONFIG_9365_PATH) && config_vmo && |
| config_vmo->is_valid()) { |
| config_path = path; |
| *fw = config_vmo->get(); |
| *size = config_size; |
| return ZX_OK; |
| } |
| if (path_str == GT6853_FIRMWARE_PATH && firmware_vmo && firmware_vmo->is_valid()) { |
| *fw = firmware_vmo->get(); |
| *size = firmware_size; |
| return ZX_OK; |
| } |
| |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| namespace touch { |
| |
| class FakeTouchDevice : public fake_i2c::FakeI2c { |
| public: |
| struct FirmwarePacket { |
| uint8_t type; |
| uint16_t size; |
| uint16_t flash_addr; |
| }; |
| |
| explicit FakeTouchDevice(std::shared_ptr<sync_completion_t> read_completion) |
| : read_completion_(std::move(read_completion)) {} |
| |
| bool ok() const { return event_reset_; } |
| |
| void set_sensor_id(const uint16_t sensor_id) { sensor_id_ = sensor_id; } |
| cpp20::span<const uint8_t> get_config_data() const { |
| return {config_data_, std::size(config_data_)}; |
| } |
| const std::vector<FirmwarePacket>& get_firmware_packets() const { return firmware_packets_; } |
| |
| 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 { |
| constexpr uint8_t kTouchData[] = { |
| // clang-format off |
| 0x80, 0x5a, 0x00, 0xb9, 0x03, 0xae, 0x00, 0x00, |
| 0xc2, 0xf2, 0x01, 0x44, 0x00, 0x6c, 0x00, 0x00, |
| 0x01, 0x72, 0x00, 0x14, 0x01, 0x13, 0x00, 0x00, |
| 0xc3, 0x38, 0x01, 0xbe, 0x00, 0xdf, 0x00, 0x00, |
| // clang-format on |
| }; |
| |
| if (write_buffer_size < 2) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| const auto address = static_cast<uint16_t>((write_buffer[0] << 8) | write_buffer[1]); |
| write_buffer += 2; |
| write_buffer_size -= 2; |
| |
| using Register = Gt6853Device::Register; |
| |
| if (address == static_cast<uint16_t>(Register::kEventStatusReg)) { |
| if (write_buffer_size >= 1 && write_buffer[0] == 0x00) { |
| event_reset_ = true; |
| } else { |
| read_buffer[0] = current_state_ == kIdle ? 0x80 : 0x00; |
| *read_buffer_size = 1; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kContactsReg)) { |
| read_buffer[0] = current_state_ == kIdle ? 0x34 : 0x00; |
| *read_buffer_size = 1; |
| } else if (address == static_cast<uint16_t>(Register::kContactsStartReg)) { |
| // The interrupt has been received and the driver is reading out the data registers. |
| if (current_state_ == kIdle) { |
| memcpy(read_buffer, kTouchData, sizeof(kTouchData)); |
| } else { |
| memset(read_buffer, 0x00, sizeof(kTouchData)); |
| } |
| *read_buffer_size = sizeof(kTouchData); |
| sync_completion_signal(read_completion_.get()); |
| } else if (address == static_cast<uint16_t>(Register::kSensorIdReg)) { |
| memcpy(read_buffer, &sensor_id_, sizeof(sensor_id_)); |
| *read_buffer_size = sizeof(sensor_id_); |
| } else if (address == static_cast<uint16_t>(Register::kCommandReg) && write_buffer_size == 0) { |
| // Reading the device command. |
| read_buffer[0] = current_state_ == kWaitingForConfig ? 0x82 : 0xff; |
| *read_buffer_size = 1; |
| } else if (address == static_cast<uint16_t>(Register::kCommandReg) && write_buffer_size == 3) { |
| // Writing the host command. Must write all three registers in one transfer. |
| |
| uint8_t checksum = write_buffer[0]; |
| checksum += write_buffer[1]; |
| checksum += write_buffer[2]; |
| if (checksum != 0) { |
| return ZX_ERR_IO_DATA_INTEGRITY; |
| } |
| |
| if (write_buffer[0] == 0x80 && current_state_ == kIdle) { |
| current_state_ = kWaitingForConfig; |
| } else if (write_buffer[0] == 0x83 && current_state_ == kWaitingForConfig) { |
| current_state_ = kIdle; |
| } else { |
| return ZX_ERR_IO; |
| } |
| } else if (current_state_ == kWaitingForConfig && |
| address >= static_cast<uint16_t>(Register::kConfigDataReg) && |
| address < static_cast<uint16_t>(Register::kConfigDataReg) + sizeof(config_data_)) { |
| const size_t offset = address - static_cast<uint16_t>(Register::kConfigDataReg); |
| if (write_buffer_size > 0) { |
| memcpy(config_data_ + offset, write_buffer, write_buffer_size); |
| } else { |
| memcpy(read_buffer, config_data_ + offset, sizeof(config_data_)); |
| *read_buffer_size = sizeof(config_data_); |
| } |
| } else if (address >= static_cast<uint16_t>(Register::kIspBuffer) && |
| address < (static_cast<uint16_t>(Register::kIspBuffer) + 4096)) { |
| const uint16_t offset = address - static_cast<uint16_t>(Register::kIspBuffer); |
| if (write_buffer_size > 0) { |
| if (offset + write_buffer_size > 4096) { |
| return ZX_ERR_IO; |
| } |
| memcpy(flash_packet_ + offset, write_buffer, write_buffer_size); |
| } else { |
| memcpy(read_buffer, flash_packet_ + offset, 4096 - offset); |
| *read_buffer_size = 4096 - offset; |
| } |
| } else if (address >= static_cast<uint16_t>(Register::kIspAddr) && |
| address < (static_cast<uint16_t>(Register::kIspAddr) + 4096)) { |
| const uint16_t offset = address - static_cast<uint16_t>(Register::kIspAddr); |
| if (write_buffer_size > 0) { |
| if (offset + write_buffer_size > 4096) { |
| return ZX_ERR_IO; |
| } |
| memcpy(flash_packet_ + offset, write_buffer, write_buffer_size); |
| } else { |
| memcpy(read_buffer, flash_packet_ + offset, 4096 - offset); |
| *read_buffer_size = 4096 - offset; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kSubsysType)) { |
| if (write_buffer_size == 0) { |
| read_buffer[0] = subsys_type_; |
| read_buffer[1] = subsys_type_; |
| *read_buffer_size = 2; |
| } else if (write_buffer_size == 2 && write_buffer[0] == write_buffer[1]) { |
| subsys_type_ = write_buffer[0]; |
| |
| FirmwarePacket packet = {}; |
| packet.type = write_buffer[0]; |
| |
| memcpy(&packet.size, flash_packet_, sizeof(packet.size)); |
| packet.size = be16toh(packet.size); |
| |
| memcpy(&packet.flash_addr, flash_packet_ + sizeof(packet.size), sizeof(packet.flash_addr)); |
| packet.flash_addr = be16toh(packet.flash_addr); |
| |
| firmware_packets_.push_back(packet); |
| } else { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kFlashFlag)) { |
| if (write_buffer_size == 0) { |
| // The flash state is read twice, report success in two states to handle this. |
| if (current_state_ == kFlashingFirmware || current_state_ == kFlashingFirmwareDone) { |
| read_buffer[0] = 0xbb; |
| read_buffer[1] = 0xbb; |
| current_state_ = current_state_ == kFlashingFirmware ? kFlashingFirmwareDone : kIdle; |
| } else { |
| read_buffer[0] = 0; |
| read_buffer[1] = 0; |
| current_state_ = kFlashingFirmware; |
| } |
| *read_buffer_size = 2; |
| } else if (write_buffer_size != 2) { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kIspRunFlag)) { |
| if (write_buffer_size == 0) { |
| read_buffer[0] = 0xaa; |
| read_buffer[1] = 0xbb; |
| *read_buffer_size = 2; |
| } else if (write_buffer_size != 2) { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kAccessPatch0)) { |
| if (write_buffer_size == 0) { |
| read_buffer[0] = access_patch0; |
| *read_buffer_size = 1; |
| } else if (write_buffer_size == 1) { |
| access_patch0 = write_buffer[0]; |
| } else { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kCpuCtrl)) { |
| if (write_buffer_size == 0) { |
| read_buffer[0] = 0x24; // kCpuCtrlHoldSs51 |
| *read_buffer_size = 1; |
| } else if (write_buffer_size != 1) { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kDspMcuPower)) { |
| if (write_buffer_size == 0) { |
| read_buffer[0] = 0; |
| *read_buffer_size = 1; |
| } else if (write_buffer_size != 1) { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kBankSelect) || |
| address == static_cast<uint16_t>(Register::kCache) || |
| address == static_cast<uint16_t>(Register::kEsdKey) || |
| address == static_cast<uint16_t>(Register::kWtdTimer) || |
| address == static_cast<uint16_t>(Register::kScramble)) { |
| if (write_buffer_size != 1) { |
| return ZX_ERR_IO; |
| } |
| } else if (address == static_cast<uint16_t>(Register::kCpuRunFrom)) { |
| if (write_buffer_size != 8) { |
| return ZX_ERR_IO; |
| } |
| } else { |
| return ZX_ERR_IO; |
| } |
| |
| return ZX_OK; |
| } |
| |
| private: |
| enum State { |
| kIdle, |
| kWaitingForConfig, |
| kFlashingFirmware, |
| kFlashingFirmwareDone, |
| }; |
| |
| std::shared_ptr<sync_completion_t> read_completion_; |
| bool event_reset_ = false; |
| uint16_t sensor_id_ = UINT16_MAX; |
| State current_state_ = kIdle; |
| uint8_t config_data_[651]; // All panels have configs of this size. |
| uint8_t flash_packet_[4096]; |
| uint8_t subsys_type_ = 0; |
| uint8_t access_patch0 = 0; |
| std::vector<FirmwarePacket> firmware_packets_; |
| }; |
| |
| class Gt6853Test : public zxtest::Test { |
| public: |
| void SetUp() override { |
| ASSERT_OK(fragments_loop_.StartThread("fragments")); |
| ASSERT_OK(zx::interrupt::create(zx::resource(ZX_HANDLE_INVALID), 0, ZX_INTERRUPT_VIRTUAL, |
| &gpio_interrupt_)); |
| |
| zx::interrupt gpio_interrupt; |
| ASSERT_OK(gpio_interrupt_.duplicate(ZX_RIGHT_SAME_RIGHTS, &gpio_interrupt)); |
| interrupt_gpio_.SyncCall(&fake_gpio::FakeGpio::SetInterrupt, zx::ok(std::move(gpio_interrupt))); |
| |
| EXPECT_OK(loop_.StartThread()); |
| } |
| |
| void TearDown() override { |
| if (device_) { |
| auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() { |
| device_async_remove(device_); |
| EXPECT_OK(mock_ddk::ReleaseFlaggedDevices(fake_parent_.get())); |
| }); |
| EXPECT_OK(result.status_value()); |
| } |
| device_ = nullptr; |
| loop_.Quit(); |
| loop_.JoinThreads(); |
| loop_.RunUntilIdle(); |
| } |
| |
| zx_status_t Init(uint32_t panel_type_id = 1) { |
| panel_type_id_ = panel_type_id; |
| |
| InitParent(); |
| |
| config_vmo = &config_vmo_; |
| firmware_vmo = &firmware_vmo_; |
| |
| zx_status_t status; |
| auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() { |
| status = Gt6853Device::Create(nullptr, fake_parent_.get()); |
| }); |
| EXPECT_OK(result.status_value()); |
| device_ = fake_parent_->GetLatestChild(); |
| |
| std::vector interrupt_gpio_states = |
| interrupt_gpio().SyncCall(&fake_gpio::FakeGpio::GetStateLog); |
| ZX_ASSERT(interrupt_gpio_states.size() == 1); |
| ZX_ASSERT(interrupt_gpio_states[0].sub_state == |
| fake_gpio::ReadSubState{.flags = fuchsia_hardware_gpio::GpioFlags::kNoPull}); |
| |
| return status; |
| } |
| |
| protected: |
| void WaitForTouchDataRead() { |
| sync_completion_wait(i2c_read_completion_.get(), ZX_TIME_INFINITE); |
| sync_completion_reset(i2c_read_completion_.get()); |
| } |
| |
| zx_status_t WriteConfigData(const std::vector<uint8_t>& data, uint64_t offset) { |
| return config_vmo_.write(data.data(), offset, data.size()); |
| } |
| |
| zx_status_t WriteConfigString(const char* data, uint64_t offset) { |
| return config_vmo_.write(data, offset, strlen(data) + 1); |
| } |
| |
| zx_status_t WriteFirmwareData(const std::vector<uint8_t>& data, uint64_t offset) { |
| return firmware_vmo_.write(data.data(), offset, data.size()); |
| } |
| |
| void AddDefaultConfig() { |
| config_size = 2338; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(config_size, ZX_PAGE_SIZE), 0, &config_vmo_)); |
| |
| const uint32_t config_size_le = htole32(config_size); |
| ASSERT_OK(config_vmo_.write(&config_size_le, 0, sizeof(config_size_le))); |
| ASSERT_OK(WriteConfigData({0x2b}, 4)); |
| ASSERT_OK(WriteConfigData({0x03}, 9)); |
| ASSERT_OK(WriteConfigData({0x16, 0x00, 0x1a, 0x03, 0x1e, 0x06}, 16)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x0016)); |
| ASSERT_OK(WriteConfigData({0x02}, 0x0016 + 20)); |
| ASSERT_OK(WriteConfigString("Config number two", 0x0016 + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x031a)); |
| ASSERT_OK(WriteConfigData({0x00}, 0x031a + 20)); |
| ASSERT_OK(WriteConfigString("Config number zero", 0x031a + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x061e)); |
| ASSERT_OK(WriteConfigData({0x01}, 0x061e + 20)); |
| ASSERT_OK(WriteConfigString("Config number one", 0x061e + 121)); |
| |
| i2c_.SyncCall(&FakeTouchDevice::set_sensor_id, 0); |
| } |
| |
| void WaitForNextReader() { device_->GetDeviceContext<Gt6853Device>()->WaitForNextReader(); } |
| |
| fidl::ClientEnd<fuchsia_input_report::InputDevice> GetInputDeviceClient() { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputDevice>(); |
| EXPECT_OK(endpoints); |
| |
| fidl::BindServer(dispatcher_->async_dispatcher(), std::move(endpoints->server), |
| device_->GetDeviceContext<Gt6853Device>()); |
| |
| return std::move(endpoints->client); |
| } |
| |
| async_patterns::TestDispatcherBound<FakeTouchDevice>& i2c() { return i2c_; } |
| async_patterns::TestDispatcherBound<fake_gpio::FakeGpio>& reset_gpio() { return reset_gpio_; } |
| async_patterns::TestDispatcherBound<fake_gpio::FakeGpio>& interrupt_gpio() { |
| return interrupt_gpio_; |
| } |
| |
| zx::interrupt gpio_interrupt_; |
| MockDevice* device_ = nullptr; |
| uint32_t panel_type_id_ = 0; |
| zx::vmo config_vmo_; |
| zx::vmo firmware_vmo_; |
| |
| private: |
| void InitParent() { |
| // Create i2c fragment. |
| auto i2c_handler = fuchsia_hardware_i2c::Service::InstanceHandler( |
| {.device = i2c_.SyncCall(&fidl::WireServer<fuchsia_hardware_i2c::Device>::bind_handler, |
| async_patterns::PassDispatcher)}); |
| auto service_result = i2c_outgoing_.SyncCall( |
| [handler = std::move(i2c_handler)](component::OutgoingDirectory* outgoing) mutable { |
| return outgoing->AddService<fuchsia_hardware_i2c::Service>(std::move(handler)); |
| }); |
| ASSERT_TRUE(service_result.is_ok()); |
| zx::result endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| ASSERT_TRUE(endpoints.is_ok()); |
| ASSERT_TRUE( |
| i2c_outgoing_.SyncCall(&component::OutgoingDirectory::Serve, std::move(endpoints->server)) |
| .is_ok()); |
| fake_parent_->AddFidlService(fuchsia_hardware_i2c::Service::Name, std::move(endpoints->client), |
| "i2c"); |
| |
| // Create reset gpio fragment. |
| auto reset_gpio_handler = reset_gpio_.SyncCall(&fake_gpio::FakeGpio::CreateInstanceHandler); |
| service_result = reset_gpio_outgoing_.SyncCall( |
| [handler = std::move(reset_gpio_handler)](component::OutgoingDirectory* outgoing) mutable { |
| return outgoing->AddService<fuchsia_hardware_gpio::Service>(std::move(handler)); |
| }); |
| ASSERT_TRUE(service_result.is_ok()); |
| endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| ASSERT_TRUE(endpoints.is_ok()); |
| ASSERT_TRUE(reset_gpio_outgoing_ |
| .SyncCall(&component::OutgoingDirectory::Serve, std::move(endpoints->server)) |
| .is_ok()); |
| fake_parent_->AddFidlService(fuchsia_hardware_gpio::Service::Name, std::move(endpoints->client), |
| "gpio-reset"); |
| |
| // Create interrupt gpio fragment. |
| auto interrupt_gpio_handler = |
| interrupt_gpio_.SyncCall(&fake_gpio::FakeGpio::CreateInstanceHandler); |
| service_result = |
| interrupt_gpio_outgoing_.SyncCall([handler = std::move(interrupt_gpio_handler)]( |
| component::OutgoingDirectory* outgoing) mutable { |
| return outgoing->AddService<fuchsia_hardware_gpio::Service>(std::move(handler)); |
| }); |
| ASSERT_TRUE(service_result.is_ok()); |
| endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| ASSERT_TRUE(endpoints.is_ok()); |
| ASSERT_TRUE(interrupt_gpio_outgoing_ |
| .SyncCall(&component::OutgoingDirectory::Serve, std::move(endpoints->server)) |
| .is_ok()); |
| fake_parent_->AddFidlService(fuchsia_hardware_gpio::Service::Name, std::move(endpoints->client), |
| "gpio-int"); |
| |
| fake_parent_->AddProtocol(ZX_PROTOCOL_PDEV, nullptr, nullptr, "pdev"); |
| fake_parent_->SetMetadata(DEVICE_METADATA_BOARD_PRIVATE, &panel_type_id_, |
| sizeof(panel_type_id_)); |
| } |
| |
| std::shared_ptr<sync_completion_t> i2c_read_completion_ = std::make_shared<sync_completion_t>(); |
| std::shared_ptr<MockDevice> fake_parent_ = MockDevice::FakeRootParent(); |
| fdf::UnownedSynchronizedDispatcher dispatcher_ = |
| fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher(); |
| async::Loop loop_{&kAsyncLoopConfigNeverAttachToThread}; |
| async::Loop fragments_loop_{&kAsyncLoopConfigNoAttachToCurrentThread}; |
| async_patterns::TestDispatcherBound<FakeTouchDevice> i2c_{fragments_loop_.dispatcher(), |
| std::in_place, i2c_read_completion_}; |
| async_patterns::TestDispatcherBound<component::OutgoingDirectory> i2c_outgoing_{ |
| fragments_loop_.dispatcher(), std::in_place, async_patterns::PassDispatcher}; |
| async_patterns::TestDispatcherBound<fake_gpio::FakeGpio> interrupt_gpio_{ |
| fragments_loop_.dispatcher(), std::in_place}; |
| async_patterns::TestDispatcherBound<component::OutgoingDirectory> interrupt_gpio_outgoing_{ |
| fragments_loop_.dispatcher(), std::in_place, async_patterns::PassDispatcher}; |
| async_patterns::TestDispatcherBound<fake_gpio::FakeGpio> reset_gpio_{fragments_loop_.dispatcher(), |
| std::in_place}; |
| async_patterns::TestDispatcherBound<component::OutgoingDirectory> reset_gpio_outgoing_{ |
| fragments_loop_.dispatcher(), std::in_place, async_patterns::PassDispatcher}; |
| }; |
| |
| TEST_F(Gt6853Test, GetDescriptor) { |
| AddDefaultConfig(); |
| ASSERT_OK(Init()); |
| |
| fidl::WireSyncClient client(GetInputDeviceClient()); |
| |
| auto response = client->GetDescriptor(); |
| |
| ASSERT_TRUE(response.ok()); |
| ASSERT_TRUE(response.value().descriptor.has_device_info()); |
| ASSERT_TRUE(response.value().descriptor.has_touch()); |
| ASSERT_TRUE(response.value().descriptor.touch().has_input()); |
| ASSERT_TRUE(response.value().descriptor.touch().input().has_contacts()); |
| ASSERT_TRUE(response.value().descriptor.touch().input().has_max_contacts()); |
| ASSERT_TRUE(response.value().descriptor.touch().input().has_touch_type()); |
| ASSERT_EQ(response.value().descriptor.touch().input().contacts().count(), 10); |
| |
| EXPECT_EQ(response.value().descriptor.device_info().vendor_id, |
| static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle)); |
| EXPECT_EQ( |
| response.value().descriptor.device_info().product_id, |
| static_cast<uint32_t>(fuchsia_input_report::wire::VendorGoogleProductId::kGoodixTouchscreen)); |
| |
| for (size_t i = 0; i < 10; i++) { |
| const auto& contact = response.value().descriptor.touch().input().contacts()[i]; |
| ASSERT_TRUE(contact.has_position_x()); |
| ASSERT_TRUE(contact.has_position_y()); |
| |
| EXPECT_EQ(contact.position_x().range.min, 0); |
| EXPECT_EQ(contact.position_x().range.max, 600); |
| EXPECT_EQ(contact.position_x().unit.type, fuchsia_input_report::wire::UnitType::kNone); |
| EXPECT_EQ(contact.position_x().unit.exponent, 0); |
| |
| EXPECT_EQ(contact.position_y().range.min, 0); |
| EXPECT_EQ(contact.position_y().range.max, 1024); |
| EXPECT_EQ(contact.position_y().unit.type, fuchsia_input_report::wire::UnitType::kNone); |
| EXPECT_EQ(contact.position_y().unit.exponent, 0); |
| } |
| |
| EXPECT_EQ(response.value().descriptor.touch().input().max_contacts(), 10); |
| EXPECT_EQ(response.value().descriptor.touch().input().touch_type(), |
| fuchsia_input_report::wire::TouchType::kTouchscreen); |
| } |
| |
| TEST_F(Gt6853Test, ReadReport) { |
| AddDefaultConfig(); |
| ASSERT_OK(Init()); |
| |
| fidl::WireSyncClient client(GetInputDeviceClient()); |
| |
| auto reader_endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>(); |
| ASSERT_TRUE(reader_endpoints.is_ok()); |
| auto [reader_client, reader_server] = std::move(reader_endpoints.value()); |
| // TODO(https://fxbug.dev/42180237) Consider handling the error instead of ignoring it. |
| (void)client->GetInputReportsReader(std::move(reader_server)); |
| fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader(std::move(reader_client)); |
| WaitForNextReader(); |
| |
| EXPECT_OK(gpio_interrupt_.trigger(0, zx::clock::get_monotonic())); |
| |
| WaitForTouchDataRead(); |
| |
| const auto response = reader->ReadInputReports(); |
| ASSERT_TRUE(response.ok()); |
| ASSERT_TRUE(response->is_ok()); |
| |
| const auto& reports = response->value()->reports; |
| |
| ASSERT_EQ(reports.count(), 1); |
| ASSERT_TRUE(reports[0].has_touch()); |
| ASSERT_TRUE(reports[0].touch().has_contacts()); |
| ASSERT_EQ(reports[0].touch().contacts().count(), 4); |
| |
| EXPECT_EQ(reports[0].touch().contacts()[0].contact_id(), 0); |
| EXPECT_EQ(reports[0].touch().contacts()[0].position_x(), 0x005a); |
| EXPECT_EQ(reports[0].touch().contacts()[0].position_y(), 0x03b9); |
| |
| EXPECT_EQ(reports[0].touch().contacts()[1].contact_id(), 2); |
| EXPECT_EQ(reports[0].touch().contacts()[1].position_x(), 0x01f2); |
| EXPECT_EQ(reports[0].touch().contacts()[1].position_y(), 0x0044); |
| |
| EXPECT_EQ(reports[0].touch().contacts()[2].contact_id(), 1); |
| EXPECT_EQ(reports[0].touch().contacts()[2].position_x(), 0x0072); |
| EXPECT_EQ(reports[0].touch().contacts()[2].position_y(), 0x0114); |
| |
| EXPECT_EQ(reports[0].touch().contacts()[3].contact_id(), 3); |
| EXPECT_EQ(reports[0].touch().contacts()[3].position_x(), 0x0138); |
| EXPECT_EQ(reports[0].touch().contacts()[3].position_y(), 0x00be); |
| |
| EXPECT_TRUE(i2c().SyncCall(&FakeTouchDevice::ok)); |
| } |
| |
| TEST_F(Gt6853Test, ConfigDownloadPanelType9364) { |
| config_size = 2338; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(config_size, ZX_PAGE_SIZE), 0, &config_vmo_)); |
| |
| const uint32_t config_size_le = htole32(config_size); |
| ASSERT_OK(config_vmo_.write(&config_size_le, 0, sizeof(config_size_le))); |
| ASSERT_OK(WriteConfigData({0x2b}, 4)); // Checksum |
| ASSERT_OK(WriteConfigData({0x03}, 9)); // Number of config entries in the table |
| ASSERT_OK(WriteConfigData({0x16, 0x00, 0x1a, 0x03, 0x1e, 0x06}, 16)); // Entry offsets |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x0016)); // Entry 0 size |
| ASSERT_OK(WriteConfigData({0x02}, 0x0016 + 20)); // Entry 0 sensor ID |
| ASSERT_OK(WriteConfigString("Config number two", 0x0016 + 121)); // Entry 0 config data |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x031a)); // Repeat for entries 1, 2 |
| ASSERT_OK(WriteConfigData({0x00}, 0x031a + 20)); |
| ASSERT_OK(WriteConfigString("Config number zero", 0x031a + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x061e)); |
| ASSERT_OK(WriteConfigData({0x01}, 0x061e + 20)); |
| ASSERT_OK(WriteConfigString("Config number one", 0x061e + 121)); |
| |
| i2c().SyncCall(&FakeTouchDevice::set_sensor_id, 1); |
| |
| ASSERT_OK(Init()); |
| |
| auto config_data = i2c().SyncCall(&FakeTouchDevice::get_config_data); |
| EXPECT_STREQ(reinterpret_cast<const char*>(config_data.data()), "Config number one"); |
| EXPECT_STREQ(config_path, GT6853_CONFIG_9364_PATH); |
| EXPECT_EQ(config_data.size(), 0x0304 - 121); |
| } |
| |
| TEST_F(Gt6853Test, ConfigDownloadPanelType9365) { |
| config_size = 2338; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(config_size, ZX_PAGE_SIZE), 0, &config_vmo_)); |
| |
| const uint32_t config_size_le = htole32(config_size); |
| ASSERT_OK(config_vmo_.write(&config_size_le, 0, sizeof(config_size_le))); |
| ASSERT_OK(WriteConfigData({0x2b}, 4)); |
| ASSERT_OK(WriteConfigData({0x03}, 9)); |
| ASSERT_OK(WriteConfigData({0x16, 0x00, 0x1a, 0x03, 0x1e, 0x06}, 16)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x0016)); |
| ASSERT_OK(WriteConfigData({0x02}, 0x0016 + 20)); |
| ASSERT_OK(WriteConfigString("Config number two", 0x0016 + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x031a)); |
| ASSERT_OK(WriteConfigData({0x00}, 0x031a + 20)); |
| ASSERT_OK(WriteConfigString("Config number zero", 0x031a + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x061e)); |
| ASSERT_OK(WriteConfigData({0x01}, 0x061e + 20)); |
| ASSERT_OK(WriteConfigString("Config number one", 0x061e + 121)); |
| |
| i2c().SyncCall(&FakeTouchDevice::set_sensor_id, 0); |
| |
| ASSERT_OK(Init(4)); // kPanelTypeKdFiti9365 |
| |
| auto config_data = i2c().SyncCall(&FakeTouchDevice::get_config_data); |
| EXPECT_STREQ(reinterpret_cast<const char*>(config_data.data()), "Config number zero"); |
| EXPECT_STREQ(config_path, GT6853_CONFIG_9365_PATH); |
| EXPECT_EQ(config_data.size(), 0x0304 - 121); |
| } |
| |
| TEST_F(Gt6853Test, ConfigDownloadUnableToLoadConfig) { EXPECT_NOT_OK(Init()); } |
| |
| TEST_F(Gt6853Test, NoConfigEntry) { |
| config_size = 2338; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(config_size, ZX_PAGE_SIZE), 0, &config_vmo_)); |
| |
| const uint32_t config_size_le = htole32(config_size); |
| ASSERT_OK(config_vmo_.write(&config_size_le, 0, sizeof(config_size_le))); |
| ASSERT_OK(WriteConfigData({0x2b}, 4)); |
| ASSERT_OK(WriteConfigData({0x03}, 9)); |
| ASSERT_OK(WriteConfigData({0x16, 0x00, 0x1a, 0x03, 0x1e, 0x06}, 16)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x0016)); |
| ASSERT_OK(WriteConfigData({0x02}, 0x0016 + 20)); |
| ASSERT_OK(WriteConfigString("Config number two", 0x0016 + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x031a)); |
| ASSERT_OK(WriteConfigData({0x00}, 0x031a + 20)); |
| ASSERT_OK(WriteConfigString("Config number zero", 0x031a + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x061e)); |
| ASSERT_OK(WriteConfigData({0x01}, 0x061e + 20)); |
| ASSERT_OK(WriteConfigString("Config number one", 0x061e + 121)); |
| |
| i2c().SyncCall(&FakeTouchDevice::set_sensor_id, 4); |
| |
| EXPECT_NOT_OK(Init()); |
| } |
| |
| TEST_F(Gt6853Test, InvalidConfigEntry) { |
| config_size = 2338; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(config_size, ZX_PAGE_SIZE), 0, &config_vmo_)); |
| |
| ASSERT_OK(WriteConfigData({0x1c, 0x03, 0x00, 0x00, 0x2b}, 0)); |
| ASSERT_OK(WriteConfigData({0x03}, 9)); |
| ASSERT_OK(WriteConfigData({0x16, 0x00, 0x1a, 0x03, 0x1e, 0x06}, 16)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x0016)); |
| ASSERT_OK(WriteConfigData({0x02}, 0x0016 + 20)); |
| ASSERT_OK(WriteConfigString("Config number two", 0x0016 + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x031a)); |
| ASSERT_OK(WriteConfigData({0x00}, 0x031a + 20)); |
| ASSERT_OK(WriteConfigString("Config number zero", 0x031a + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x061e)); |
| ASSERT_OK(WriteConfigData({0x01}, 0x061e + 20)); |
| ASSERT_OK(WriteConfigString("Config number one", 0x061e + 121)); |
| |
| i2c().SyncCall(&FakeTouchDevice::set_sensor_id, 1); |
| |
| config_size = 0x031a + 2; |
| EXPECT_NOT_OK(Init()); |
| } |
| |
| TEST_F(Gt6853Test, BadConfigChecksum) { |
| config_size = 2338; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(config_size, ZX_PAGE_SIZE), 0, &config_vmo_)); |
| |
| const uint32_t config_size_le = htole32(config_size); |
| ASSERT_OK(config_vmo_.write(&config_size_le, 0, sizeof(config_size_le))); |
| ASSERT_OK(WriteConfigData({0x2b + 1}, 4)); |
| ASSERT_OK(WriteConfigData({0x03}, 9)); |
| ASSERT_OK(WriteConfigData({0x16, 0x00, 0x1a, 0x03, 0x1e, 0x06}, 16)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x0016)); |
| ASSERT_OK(WriteConfigData({0x02}, 0x0016 + 20)); |
| ASSERT_OK(WriteConfigString("Config number two", 0x0016 + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x031a)); |
| ASSERT_OK(WriteConfigData({0x00}, 0x031a + 20)); |
| ASSERT_OK(WriteConfigString("Config number zero", 0x031a + 121)); |
| ASSERT_OK(WriteConfigData({0x04, 0x03, 0x00, 0x00}, 0x061e)); |
| ASSERT_OK(WriteConfigData({0x01}, 0x061e + 20)); |
| ASSERT_OK(WriteConfigString("Config number one", 0x061e + 121)); |
| |
| i2c().SyncCall(&FakeTouchDevice::set_sensor_id, 1); |
| |
| EXPECT_EQ(Init(), ZX_ERR_IO_DATA_INTEGRITY); |
| } |
| |
| TEST_F(Gt6853Test, FirmwareDownload) { |
| firmware_size = 2048; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(firmware_size, ZX_PAGE_SIZE), 0, &firmware_vmo_)); |
| ASSERT_OK(WriteFirmwareData({0x00, 0x00, 0x07, 0xfa, 0x02, 0x98}, 0)); |
| ASSERT_OK(WriteFirmwareData({0x03}, 27)); |
| ASSERT_OK(WriteFirmwareData({0x01, 0x00, 0x00, 0x01, 0x00, 0xab, 0xcd}, 32)); |
| ASSERT_OK(WriteFirmwareData({0x02, 0x00, 0x00, 0x01, 0x00, 0x12, 0x34}, 40)); |
| ASSERT_OK(WriteFirmwareData({0x03, 0x00, 0x00, 0x01, 0x00, 0x56, 0x78}, 48)); |
| |
| AddDefaultConfig(); |
| |
| EXPECT_OK(Init()); |
| |
| std::vector reset_gpio_states = reset_gpio().SyncCall(&fake_gpio::FakeGpio::GetStateLog); |
| ASSERT_EQ(4, reset_gpio_states.size()); |
| ASSERT_EQ(fake_gpio::WriteSubState{.value = 0}, reset_gpio_states[0].sub_state); |
| ASSERT_EQ(fake_gpio::WriteSubState{.value = 1}, reset_gpio_states[1].sub_state); |
| ASSERT_EQ(fake_gpio::WriteSubState{.value = 0}, reset_gpio_states[2].sub_state); |
| ASSERT_EQ(fake_gpio::WriteSubState{.value = 1}, reset_gpio_states[3].sub_state); |
| std::vector interrupt_gpio_states = interrupt_gpio().SyncCall(&fake_gpio::FakeGpio::GetStateLog); |
| |
| std::vector firmware_packets = i2c().SyncCall(&FakeTouchDevice::get_firmware_packets); |
| ASSERT_EQ(firmware_packets.size(), 2); |
| |
| EXPECT_EQ(firmware_packets[0].type, 2); |
| EXPECT_EQ(firmware_packets[0].size, 256); |
| EXPECT_EQ(firmware_packets[0].flash_addr, 0x1234); |
| |
| EXPECT_EQ(firmware_packets[1].type, 3); |
| EXPECT_EQ(firmware_packets[1].size, 256); |
| EXPECT_EQ(firmware_packets[1].flash_addr, 0x5678); |
| } |
| |
| TEST_F(Gt6853Test, FirmwareDownloadInvalidCrc) { |
| firmware_size = 2048; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(firmware_size, ZX_PAGE_SIZE), 0, &firmware_vmo_)); |
| ASSERT_OK(WriteFirmwareData({0x00, 0x00, 0x07, 0xfa, 0x02, 0x99}, 0)); |
| ASSERT_OK(WriteFirmwareData({0x03}, 27)); |
| ASSERT_OK(WriteFirmwareData({0x01, 0x00, 0x00, 0x01, 0x00, 0xab, 0xcd}, 32)); |
| ASSERT_OK(WriteFirmwareData({0x02, 0x00, 0x00, 0x01, 0x00, 0x12, 0x34}, 40)); |
| ASSERT_OK(WriteFirmwareData({0x03, 0x00, 0x00, 0x01, 0x00, 0x56, 0x78}, 48)); |
| |
| EXPECT_NOT_OK(Init()); |
| } |
| |
| TEST_F(Gt6853Test, FirmwareDownloadNoIspEntry) { |
| firmware_size = 2048; |
| ASSERT_OK(zx::vmo::create(fbl::round_up(firmware_size, ZX_PAGE_SIZE), 0, &firmware_vmo_)); |
| ASSERT_OK(WriteFirmwareData({0x00, 0x00, 0x07, 0xfa, 0x00, 0x00}, 0)); |
| ASSERT_OK(WriteFirmwareData({0x00}, 27)); |
| |
| EXPECT_NOT_OK(Init()); |
| } |
| |
| TEST_F(Gt6853Test, LatencyMeasurements) { |
| AddDefaultConfig(); |
| ASSERT_OK(Init()); |
| |
| fidl::WireSyncClient client(GetInputDeviceClient()); |
| |
| auto reader_endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>(); |
| ASSERT_TRUE(reader_endpoints.is_ok()); |
| auto [reader_client, reader_server] = std::move(reader_endpoints.value()); |
| // TODO(https://fxbug.dev/42180237) Consider handling the error instead of ignoring it. |
| (void)client->GetInputReportsReader(std::move(reader_server)); |
| fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader(std::move(reader_client)); |
| WaitForNextReader(); |
| |
| for (int i = 0; i < 5; i++) { |
| EXPECT_OK(gpio_interrupt_.trigger(0, zx::clock::get_monotonic())); |
| WaitForTouchDataRead(); |
| } |
| |
| for (size_t reports = 0; reports < 5;) { |
| const auto response = reader->ReadInputReports(); |
| if (response.ok() && response->is_ok()) { |
| reports += response->value()->reports.count(); |
| } |
| } |
| |
| const zx::vmo& inspect_vmo = device_->GetInspectVmo(); |
| ASSERT_TRUE(inspect_vmo.is_valid()); |
| |
| inspect::InspectTestHelper inspector; |
| inspector.ReadInspect(inspect_vmo); |
| |
| const inspect::Hierarchy* root = inspector.hierarchy().GetByPath({"hid-input-report-touch"}); |
| ASSERT_NOT_NULL(root); |
| |
| const auto* average_latency = |
| root->node().get_property<inspect::UintPropertyValue>("average_latency_usecs"); |
| ASSERT_NOT_NULL(average_latency); |
| |
| const auto* max_latency = |
| root->node().get_property<inspect::UintPropertyValue>("max_latency_usecs"); |
| ASSERT_NOT_NULL(max_latency); |
| |
| EXPECT_GE(max_latency->value(), average_latency->value()); |
| } |
| |
| } // namespace touch |