blob: dd10db0feb64475049cfc25d2464e6cca9a1093e [file] [log] [blame]
// Copyright 2018 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.
#ifndef SRC_UI_INPUT_DRIVERS_FOCALTECH_FT_DEVICE_H_
#define SRC_UI_INPUT_DRIVERS_FOCALTECH_FT_DEVICE_H_
#include <fidl/fuchsia.hardware.gpio/cpp/wire.h>
#include <fidl/fuchsia.input.report/cpp/wire.h>
#include <lib/async/cpp/irq.h>
#include <lib/ddk/device.h>
#include <lib/device-protocol/i2c-channel.h>
#include <lib/focaltech/focaltech.h>
#include <lib/input_report_reader/reader.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/stdcompat/span.h>
#include <lib/zx/interrupt.h>
#include <lib/zx/result.h>
#include <threads.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <atomic>
#include <ddktl/device.h>
#include <ddktl/protocol/empty-protocol.h>
#include <fbl/mutex.h>
// clang-format off
#define FTS_REG_CURPOINT 0x02
#define FTS_REG_FINGER_START 0x03
#define FTS_REG_INT_CNT 0x8F
#define FTS_REG_FLOW_WORK_CNT 0x91
#define FTS_REG_WORKMODE 0x00
#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40
#define FTS_REG_WORKMODE_WORK_VALUE 0x00
#define FTS_REG_ESDCHECK_DISABLE 0x8D
#define FTS_REG_CHIP_ID 0xA3
#define FTS_REG_CHIP_ID2 0x9F
#define FTS_REG_POWER_MODE 0xA5
#define FTS_REG_POWER_MODE_SLEEP_VALUE 0x03
#define FTS_REG_FW_VER 0xA6
#define FTS_REG_VENDOR_ID 0xA8
#define FTS_REG_LCD_BUSY_NUM 0xAB
#define FTS_REG_FACE_DEC_MODE_EN 0xB0
#define FTS_REG_FACE_DEC_MODE_STATUS 0x01
#define FTS_REG_IDE_PARA_VER_ID 0xB5
#define FTS_REG_IDE_PARA_STATUS 0xB6
#define FTS_REG_GLOVE_MODE_EN 0xC0
#define FTS_REG_COVER_MODE_EN 0xC1
#define FTS_REG_CHARGER_MODE_EN 0x8B
#define FTS_REG_GESTURE_EN 0xD0
#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3
#define FTS_REG_MODULE_ID 0xE3
#define FTS_REG_LIC_VER 0xE4
#define FTS_REG_ESD_SATURATE 0xED
#define FTS_REG_TYPE 0xA0 // Chip model number (refer to datasheet)
#define FTS_REG_FIRMID 0xA6 // Firmware version
#define FTS_REG_VENDOR_ID 0xA8
#define FTS_REG_PANEL_ID 0xAC
#define FTS_REG_RELEASE_ID_HIGH 0xAE // Firmware release ID (two bytes)
#define FTS_REG_RELEASE_ID_LOW 0xAF
#define FTS_REG_IC_VERSION 0xB1
// clang-format on
namespace ft {
class FtDevice;
using DeviceType = ddk::Device<FtDevice, ddk::Messageable<fuchsia_input_report::InputDevice>::Mixin,
ddk::Unbindable>;
class FtDevice : public DeviceType, public ddk::EmptyProtocol<ZX_PROTOCOL_INPUTREPORT> {
public:
explicit FtDevice(zx_device_t* device, async_dispatcher_t* dispatcher)
: DeviceType(device), dispatcher_(dispatcher) {}
static zx_status_t Create(void* ctx, zx_device_t* device);
void DdkRelease();
void DdkUnbind(ddk::UnbindTxn txn);
// fuchsia_input_report::InputDevice required methods
void GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) override;
void GetDescriptor(GetDescriptorCompleter::Sync& completer) override;
void SendOutputReport(SendOutputReportRequestView request,
SendOutputReportCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void GetFeatureReport(GetFeatureReportCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void SetFeatureReport(SetFeatureReportRequestView request,
SetFeatureReportCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void GetInputReport(GetInputReportRequestView request,
GetInputReportCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
// Visible for testing.
zx_status_t Init();
zx_status_t ShutDown();
#ifdef FT_TEST
zx_status_t WaitForNextReader(zx::duration timeout) {
zx_status_t status = sync_completion_wait(&next_reader_wait_, timeout.get());
if (status == ZX_OK) {
sync_completion_reset(&next_reader_wait_);
}
return status;
}
#endif
private:
static constexpr size_t kFeatureAndDescriptorBufferSize = 512;
/* Note: the focaltouch device is connected via i2c and is NOT a HID
device. This driver reads a collection of data from the data and
parses it into a message which will be sent up the stack. This message
complies with a HID descriptor that manually scripted (i.e. - not
reported by the device iteself).
*/
// Number of touch points this device can report simultaneously
static constexpr uint32_t kMaxPoints = 10;
// Size of each individual touch record (note: there are kMaxPoints of
// them) on the i2c bus. This is not the HID report size.
static constexpr uint32_t kFingerRptSize = 6;
static constexpr size_t kMaxI2cTransferLength = 8;
struct FtInputReport {
zx::time event_time = zx::time(ZX_TIME_INFINITE_PAST);
uint8_t contact_count;
struct Contact {
uint8_t finger_id;
uint16_t x;
uint16_t y;
};
std::array<Contact, kMaxPoints> contacts;
void ToFidlInputReport(
fidl::WireTableBuilder<::fuchsia_input_report::wire::InputReport>& input_report,
fidl::AnyArena& allocator);
};
static uint8_t CalculateEcc(const uint8_t* buffer, size_t size, uint8_t initial = 0);
// Enters romboot and returns true if firmware download is needed, returns false otherwise.
zx::result<bool> CheckFirmwareAndStartRomboot(uint8_t firmware_version);
zx_status_t StartRomboot();
zx_status_t WaitForRomboot();
zx::result<uint16_t> GetBootId();
// Returns true if the expected value was read before the timeout, false if not.
zx::result<bool> WaitForFlashStatus(uint16_t expected_value, int tries, zx::duration retry_sleep);
zx_status_t EraseFlash(size_t firmware_size);
zx_status_t SendFirmware(cpp20::span<const uint8_t> firmware);
zx_status_t SendFirmwarePacket(uint32_t address, const uint8_t* buffer, size_t size);
zx_status_t CheckFirmwareEcc(size_t size, uint8_t expected_ecc);
zx::result<uint8_t> ReadReg8(uint8_t address);
zx::result<uint16_t> ReadReg16(uint8_t address);
zx_status_t Write8(uint8_t value);
zx_status_t WriteReg8(uint8_t address, uint8_t value);
zx_status_t WriteReg16(uint8_t address, uint16_t value);
uint8_t Read(uint8_t addr);
zx_status_t Read(uint8_t addr, uint8_t* buf, size_t len);
void HandleIrq(async_dispatcher_t* dispatcher, async::IrqBase* irq, zx_status_t status,
const zx_packet_interrupt_t* interrupt);
static FtInputReport ParseReport(const uint8_t* buf);
void LogRegisterValue(uint8_t addr, const char* name);
zx_status_t UpdateFirmwareIfNeeded(const FocaltechMetadata& metadata);
async_dispatcher_t* dispatcher_;
fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio> int_gpio_;
fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio> reset_gpio_;
zx::interrupt irq_;
async::IrqMethod<FtDevice, &FtDevice::HandleIrq> irq_handler_{this};
ddk::I2cChannel i2c_;
inspect::Inspector inspector_;
inspect::Node node_;
inspect::ValueList values_;
inspect::Node metrics_root_;
inspect::UintProperty average_latency_usecs_;
inspect::UintProperty max_latency_usecs_;
inspect::UintProperty total_report_count_;
inspect::UintProperty last_event_timestamp_;
uint64_t report_count_ = 0;
zx::duration total_latency_ = {};
zx::duration max_latency_ = {};
input_report_reader::InputReportReaderManager<FtInputReport> readers_;
uint32_t x_max_;
uint32_t y_max_;
#ifdef FT_TEST
sync_completion_t next_reader_wait_;
#endif
};
} // namespace ft
#endif // SRC_UI_INPUT_DRIVERS_FOCALTECH_FT_DEVICE_H_