[astro][hid] Goodix touch driver
TEST: test on astro with hidtouch
Change-Id: I780befb0ffdf83680af7124f0bb6adb33f6f5cf1
diff --git a/system/dev/board/astro/astro-touch.c b/system/dev/board/astro/astro-touch.c
index 2cf66f4..d79c9ec 100644
--- a/system/dev/board/astro/astro-touch.c
+++ b/system/dev/board/astro/astro-touch.c
@@ -26,7 +26,7 @@
static const pbus_i2c_channel_t ft3x27_touch_i2c[] = {
{
- .bus_id = 1,
+ .bus_id = ASTRO_I2C_2,
.address = 0x38,
},
};
@@ -42,6 +42,25 @@
.gpio_count = countof(touch_gpios),
};
+static const pbus_i2c_channel_t gt92xx_touch_i2c[] = {
+ {
+ .bus_id = ASTRO_I2C_2,
+ .address = 0x5d,
+ },
+};
+
+static pbus_dev_t gt92xx_touch_dev = {
+ .name = "gt92xx-touch",
+ .vid = PDEV_VID_GOOGLE,
+ .pid = PDEV_PID_ASTRO,
+ .did = PDEV_DID_ASTRO_GOODIXTOUCH,
+ .i2c_channels = gt92xx_touch_i2c,
+ .i2c_channel_count = countof(gt92xx_touch_i2c),
+ .gpios = touch_gpios,
+ .gpio_count = countof(touch_gpios),
+};
+
+
zx_status_t astro_touch_init(aml_bus_t* bus) {
//Check the display ID pin to determine which driver device to add
@@ -56,8 +75,11 @@
*/
gpio_impl_read(&bus->gpio, S905D2_GPIOH(5), &gpio_state);
if (gpio_state) {
- zxlogf(INFO, "Innolux/Goodix screen not supported at this time\n");
- return ZX_OK;
+ zx_status_t status = pbus_device_add(&bus->pbus, >92xx_touch_dev);
+ if (status != ZX_OK) {
+ zxlogf(INFO, "astro_touch_init(gt92xx): pbus_device_add failed: %d\n", status);
+ return status;
+ }
} else {
zx_status_t status = pbus_device_add(&bus->pbus, &ft3x27_touch_dev);
if (status != ZX_OK) {
diff --git a/system/dev/input/goodix/gt92xx.cpp b/system/dev/input/goodix/gt92xx.cpp
new file mode 100644
index 0000000..07665ab
--- /dev/null
+++ b/system/dev/input/goodix/gt92xx.cpp
@@ -0,0 +1,324 @@
+// 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.
+
+#include <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/protocol/platform-defs.h>
+#include <ddk/protocol/platform-device.h>
+#include <fbl/auto_call.h>
+#include <fbl/auto_lock.h>
+
+#include "gt92xx.h"
+
+namespace goodix {
+// Configuration data
+// first two bytes contain starting register address (part of i2c transaction)
+static uint8_t conf_data[] = {
+ GT_REG_CONFIG_DATA >> 8, GT_REG_CONFIG_DATA & 0xff,
+ 0x5C, 0x00, 0x04, 0x58, 0x02, 0x05, 0xBD, 0xC0,
+ 0x00, 0x08, 0x1E, 0x05, 0x50, 0x32, 0x05, 0x0B,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x12, 0x00, 0x17,
+ 0x17, 0x19, 0x12, 0x8D, 0x2D, 0x0F, 0x3F, 0x41,
+ 0xB2, 0x04, 0x00, 0x00, 0x00, 0xBC, 0x03, 0x1D,
+ 0x1E, 0x80, 0x01, 0x00, 0x14, 0x46, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x37, 0x55, 0x8F, 0xC5, 0x02,
+ 0x07, 0x11, 0x00, 0x04, 0x8A, 0x39, 0x00, 0x81,
+ 0x3E, 0x00, 0x78, 0x44, 0x00, 0x71, 0x4A, 0x00,
+ 0x6A, 0x51, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E,
+ 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x00, 0x00,
+ 0xFF, 0xFF, 0x1F, 0xE7, 0xFF, 0xFF, 0xFF, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x29,
+ 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21,
+ 0x20, 0x1F, 0x1E, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6C, 0x01};
+
+int Gt92xxDevice::Thread() {
+ zx_status_t status;
+ zxlogf(INFO, "gt92xx: entering irq thread\n");
+ while (true) {
+ status = irq_.wait(nullptr);
+ if (!running_.load()) {
+ return ZX_OK;
+ }
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "gt92xx: Interrupt error %d\n", status);
+ }
+ uint8_t touch_stat = Read(GT_REG_TOUCH_STATUS);
+ if (touch_stat & 0x80) {
+ uint8_t num_reports = touch_stat & 0x0f;
+ FingerReport reports[kMaxPoints];
+ // Read touch reports
+ zx_status_t status =
+ Read(GT_REG_REPORTS, reinterpret_cast<uint8_t*>(&reports),
+ static_cast<uint8_t>(sizeof(FingerReport) * kMaxPoints));
+ if (status == ZX_OK) {
+ fbl::AutoLock lock(&proxy_lock_);
+ gt_rpt_.rpt_id = GT92XX_RPT_ID_TOUCH;
+ gt_rpt_.contact_count = num_reports;
+ // We are reusing same HID report as ft3x77 to simplify astro integration
+ // so we need to copy from device format to HID structure format
+ for (uint32_t i = 0; i < kMaxPoints; i++) {
+ gt_rpt_.fingers[i].finger_id = reports[i].id;
+ gt_rpt_.fingers[i].x = reports[i].x;
+ gt_rpt_.fingers[i].y = reports[i].y;
+ }
+ if (proxy_.is_valid()) {
+ proxy_.IoQueue(reinterpret_cast<uint8_t*>(>_rpt_), sizeof(gt92xx_touch_t));
+ }
+ }
+ // Clear the touch status
+ Write(GT_REG_TOUCH_STATUS, 0);
+ }
+ }
+ zxlogf(INFO, "gt92xx: exiting\n");
+ return 0;
+}
+
+zx_status_t Gt92xxDevice::Create(zx_device_t* device) {
+
+ zxlogf(INFO, "gt92xx: driver started...\n");
+
+ auto pdev = ddk::Pdev::Create(device);
+ if (!pdev) {
+ zxlogf(ERROR, "%s could not acquire platform device\n", __func__);
+ return ZX_ERR_NO_RESOURCES;
+ }
+
+ auto goodix_dev = fbl::make_unique<Gt92xxDevice>(device,
+ fbl::move(pdev->GetI2cChan(0)),
+ fbl::move(pdev->GetGpio(0)),
+ fbl::move(pdev->GetGpio(1)));
+
+ zx_status_t status = goodix_dev->Init();
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "Could not initialize gt92xx hardware %d\n", status);
+ return status;
+ }
+
+ auto thunk = [](void* arg) -> int {
+ return reinterpret_cast<Gt92xxDevice*>(arg)->Thread();
+ };
+
+ auto cleanup = fbl::MakeAutoCall([&]() { goodix_dev->ShutDown(); });
+
+ goodix_dev->running_.store(true);
+ int ret = thrd_create_with_name(&goodix_dev->thread_, thunk,
+ goodix_dev.get(),
+ "gt92xx-thread");
+ ZX_DEBUG_ASSERT(ret == thrd_success);
+
+ status = goodix_dev->DdkAdd("gt92xx HidDevice\n");
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "gt92xx: Could not create hid device: %d\n", status);
+ return status;
+ } else {
+ zxlogf(INFO, "gt92xx: Added hid device\n");
+ }
+
+ cleanup.cancel();
+
+ // device intentionally leaked as it is now held by DevMgr
+ __UNUSED auto ptr = goodix_dev.release();
+
+ return ZX_OK;
+}
+
+zx_status_t Gt92xxDevice::Init() {
+ // Hardware reset
+ HWReset();
+
+ uint8_t fw = Read(GT_REG_FIRMWARE);
+ if (fw != GT_FIRMWARE_MAGIC) {
+ zxlogf(ERROR, "Invalid gt92xx firmware configuration!\n");
+ return ZX_ERR_BAD_STATE;
+ }
+ // Device requires 50ms delay after this check (per datasheet)
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
+
+ // Configuration data should span specific set of registers
+ // last register has flag to latch in new configuration, second
+ // to last register holds checksum of register values.
+ // Note: first two bytes of conf_data hold the 16-bit register address where
+ // the write will start.
+ ZX_DEBUG_ASSERT((countof(conf_data) - sizeof(uint16_t)) ==
+ (GT_REG_CONFIG_REFRESH - GT_REG_CONFIG_DATA + 1));
+
+ // Write conf data to registers
+ zx_status_t status = i2c_.Transact(conf_data, sizeof(conf_data), NULL, 0);
+ if (status != ZX_OK) {
+ return status;
+ }
+ // Device requires 10ms delay to refresh configuration
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
+ // Clear touch state in case there were spurious touches registered
+ // during startup
+ Write(GT_REG_TOUCH_STATUS, 0);
+
+ status = int_gpio_.GetInterrupt(ZX_INTERRUPT_MODE_EDGE_HIGH, &irq_);
+
+ return status;
+}
+
+void Gt92xxDevice::HWReset() {
+ // Hardware reset will also set the address of the controller to either
+ // 0x14 0r 0x5d. See the datasheet for explanation of sequence.
+ reset_gpio_.ConfigOut(0); //Make reset pin an output and pull low
+ int_gpio_.ConfigOut(0); //Make interrupt pin an output and pull low
+
+ // Delay for 100us
+ zx_nanosleep(zx_deadline_after(ZX_USEC(100)));
+
+ reset_gpio_.Write(1); // Release the reset
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(5)));
+ int_gpio_.ConfigIn(0); // Make interrupt pin an input again;
+ zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); // Wait for reset to complete
+}
+
+zx_status_t Gt92xxDevice::HidBusQuery(uint32_t options, hid_info_t* info) {
+ if (!info) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+ info->dev_num = 0;
+ info->dev_class = HID_DEV_CLASS_OTHER;
+ info->boot_device = false;
+
+ return ZX_OK;
+}
+
+void Gt92xxDevice::DdkRelease() {
+ delete this;
+}
+
+void Gt92xxDevice::DdkUnbind() {
+ ShutDown();
+ DdkRemove();
+}
+
+zx_status_t Gt92xxDevice::ShutDown() {
+ running_.store(false);
+ irq_.destroy();
+ thrd_join(thread_, NULL);
+ {
+ fbl::AutoLock lock(&proxy_lock_);
+ proxy_.clear();
+ }
+ return ZX_OK;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetDescriptor(uint8_t desc_type, void** data, size_t* len) {
+
+ const uint8_t* desc_ptr;
+ uint8_t* buf;
+ *len = get_gt92xx_report_desc(&desc_ptr);
+ fbl::AllocChecker ac;
+ buf = new (&ac) uint8_t[*len];
+ if (!ac.check()) {
+ return ZX_ERR_NO_MEMORY;
+ }
+ memcpy(buf, desc_ptr, *len);
+ *data = buf;
+ return ZX_OK;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+ size_t len, size_t* out_len) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusSetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+ size_t len) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetIdle(uint8_t rpt_id, uint8_t* duration) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusSetIdle(uint8_t rpt_id, uint8_t duration) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusGetProtocol(uint8_t* protocol) {
+ return ZX_ERR_NOT_SUPPORTED;
+}
+
+zx_status_t Gt92xxDevice::HidBusSetProtocol(uint8_t protocol) {
+ return ZX_OK;
+}
+
+void Gt92xxDevice::HidBusStop() {
+ fbl::AutoLock lock(&proxy_lock_);
+ proxy_.clear();
+}
+
+zx_status_t Gt92xxDevice::HidBusStart(ddk::HidBusIfcProxy proxy) {
+ fbl::AutoLock lock(&proxy_lock_);
+ if (proxy_.is_valid()) {
+ zxlogf(ERROR, "gt92xx: Already bound!\n");
+ return ZX_ERR_ALREADY_BOUND;
+ } else {
+ proxy_ = proxy;
+ zxlogf(INFO, "gt92xx: started\n");
+ }
+ return ZX_OK;
+}
+
+uint8_t Gt92xxDevice::Read(uint16_t addr) {
+ uint8_t rbuf;
+ Read(addr, &rbuf, 1);
+ return rbuf;
+}
+
+zx_status_t Gt92xxDevice::Read(uint16_t addr, uint8_t* buf, uint8_t len) {
+ uint8_t tbuf[2];
+ tbuf[0] = static_cast<uint8_t>(addr >> 8);
+ tbuf[1] = static_cast<uint8_t>(addr & 0xff);
+ return i2c_.Transact(tbuf, 2, buf, len);
+}
+
+zx_status_t Gt92xxDevice::Write(uint16_t addr, uint8_t val) {
+ uint8_t tbuf[3];
+ tbuf[0] = static_cast<uint8_t>(addr >> 8);
+ tbuf[1] = static_cast<uint8_t>(addr & 0xff);
+ tbuf[2] = val;
+ return i2c_.Transact(tbuf, 3, NULL, 0);
+}
+
+} // namespace ft
+
+__BEGIN_CDECLS
+
+zx_status_t gt92xx_bind(void* ctx, zx_device_t* device) {
+ return goodix::Gt92xxDevice::Create(device);
+}
+
+static zx_driver_ops_t gt92xx_driver_ops = {
+ .version = DRIVER_OPS_VERSION,
+ .init = nullptr,
+ .bind = gt92xx_bind,
+ .create = nullptr,
+ .release = nullptr,
+};
+
+// clang-format off
+ZIRCON_DRIVER_BEGIN(gt92xx, gt92xx_driver_ops, "zircon", "0.1", 3)
+ BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_GOOGLE),
+ BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_ASTRO),
+ BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_ASTRO_GOODIXTOUCH),
+ZIRCON_DRIVER_END(gt92xx)
+// clang-format on
+__END_CDECLS
diff --git a/system/dev/input/goodix/gt92xx.h b/system/dev/input/goodix/gt92xx.h
new file mode 100644
index 0000000..88001f8
--- /dev/null
+++ b/system/dev/input/goodix/gt92xx.h
@@ -0,0 +1,103 @@
+// 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.
+
+#pragma once
+
+#include <ddk/device.h>
+#include <ddk/protocol/gpio.h>
+#include <ddk/protocol/i2c.h>
+#include <ddktl/device.h>
+#include <ddktl/pdev.h>
+#include <ddktl/protocol/hidbus.h>
+#include <fbl/atomic.h>
+#include <fbl/mutex.h>
+#include <hid/gt92xx.h>
+#include <lib/zx/interrupt.h>
+#include <threads.h>
+#include <zircon/types.h>
+
+// clang-format off
+#define GT_REG_SLEEP 0x8040
+#define GT_REG_CONFIG_DATA 0x8047
+#define GT_REG_MAX_X_LO 0x8048
+#define GT_REG_MAX_X_HI 0x8049
+#define GT_REG_MAX_Y_LO 0x804a
+#define GT_REG_MAX_Y_HI 0x804b
+#define GT_REG_NUM_FINGERS 0x804c
+
+#define GT_REG_CONFIG_REFRESH 0x812a
+#define GT_REG_VERSION 0x8140
+#define GT_REG_SENSOR_ID 0x814a
+#define GT_REG_TOUCH_STATUS 0x814e
+#define GT_REG_REPORTS 0x814f
+
+#define GT_REG_FIRMWARE 0x41e4
+#define GT_FIRMWARE_MAGIC 0xbe
+// clang-format on
+
+namespace goodix {
+
+class Gt92xxDevice : public ddk::Device<Gt92xxDevice, ddk::Unbindable>,
+ public ddk::HidBusProtocol<Gt92xxDevice> {
+public:
+ Gt92xxDevice(zx_device_t* device, ddk::I2cChannel i2c,
+ ddk::GpioPin intr, ddk::GpioPin reset)
+ : ddk::Device<Gt92xxDevice, ddk::Unbindable>(device),
+ i2c_(fbl::move(i2c)), int_gpio_(fbl::move(intr)),
+ reset_gpio_(fbl::move(reset)){};
+
+ static zx_status_t Create(zx_device_t* device);
+
+ void DdkRelease();
+ void DdkUnbind() __TA_EXCLUDES(proxy_lock_);
+
+ // HidBus required methods
+ void HidBusStop();
+ zx_status_t HidBusGetDescriptor(uint8_t desc_type, void** data, size_t* len);
+ zx_status_t HidBusGetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+ size_t len, size_t* out_len);
+ zx_status_t HidBusSetReport(uint8_t rpt_type, uint8_t rpt_id, void* data,
+ size_t len);
+ zx_status_t HidBusGetIdle(uint8_t rpt_id, uint8_t* duration);
+ zx_status_t HidBusSetIdle(uint8_t rpt_id, uint8_t duration);
+ zx_status_t HidBusGetProtocol(uint8_t* protocol);
+ zx_status_t HidBusSetProtocol(uint8_t protocol);
+ zx_status_t HidBusStart(ddk::HidBusIfcProxy proxy) __TA_EXCLUDES(proxy_lock_);
+ zx_status_t HidBusQuery(uint32_t options, hid_info_t* info) __TA_EXCLUDES(proxy_lock_);
+
+private:
+ // Format of data as it is read from the device
+ struct FingerReport {
+ uint8_t id;
+ uint16_t x;
+ uint16_t y;
+ uint16_t size;
+ uint8_t reserved;
+ } __PACKED;
+
+ static constexpr uint32_t kMaxPoints = 5;
+
+ zx_status_t ShutDown() __TA_EXCLUDES(proxy_lock_);
+ // performs hardware reset using gpio
+ void HWReset();
+ zx_status_t Init();
+
+ uint8_t Read(uint16_t addr);
+ zx_status_t Read(uint16_t addr, uint8_t* buf, uint8_t len);
+ zx_status_t Write(uint16_t addr, uint8_t val);
+
+ int Thread();
+
+ const ddk::I2cChannel i2c_;
+ const ddk::GpioPin int_gpio_;
+ const ddk::GpioPin reset_gpio_;
+
+ gt92xx_touch_t gt_rpt_ __TA_GUARDED(proxy_lock_);
+ zx::interrupt irq_;
+ thrd_t thread_;
+ fbl::atomic<bool> running_;
+ fbl::Mutex proxy_lock_;
+ ddk::HidBusIfcProxy proxy_ __TA_GUARDED(proxy_lock_);
+};
+}
\ No newline at end of file
diff --git a/system/dev/input/goodix/rules.mk b/system/dev/input/goodix/rules.mk
new file mode 100644
index 0000000..aa03610
--- /dev/null
+++ b/system/dev/input/goodix/rules.mk
@@ -0,0 +1,26 @@
+# 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := driver
+
+MODULE_SRCS := \
+ $(LOCAL_DIR)/gt92xx.cpp \
+
+MODULE_STATIC_LIBS := \
+ system/ulib/ddk \
+ system/ulib/ddktl \
+ system/ulib/hid \
+ system/ulib/zxcpp \
+ system/ulib/fbl \
+ system/ulib/zx \
+ system/ulib/sync \
+ system/ulib/hid
+
+MODULE_LIBS := system/ulib/driver system/ulib/zircon system/ulib/c
+
+include make/module.mk
diff --git a/system/ulib/hid/gt92xx.c b/system/ulib/hid/gt92xx.c
new file mode 100644
index 0000000..932d75f
--- /dev/null
+++ b/system/ulib/hid/gt92xx.c
@@ -0,0 +1,182 @@
+// 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.
+
+#include <hid/gt92xx.h>
+#include <zircon/errors.h>
+#include <string.h>
+
+static const uint8_t gt92xx_touch_report_desc[] = {
+ 0x05, 0x0D, // Usage Page (Digitizer)
+ 0x09, 0x04, // Usage (Touch Screen)
+ 0xA1, 0x01, // Collection (Application)
+ 0x85, 0x01, // Report ID (1)
+ 0x09, 0x22, // Usage (Finger)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x09, 0x42, // Usage (Tip Switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x06, // Report Size (6)
+ 0x09, 0x51, // Usage (0x51)
+ 0x25, 0x3F, // Logical Maximum (63)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0xA4, // Push
+ 0x26, 0x58, 0x02, // Logical Maximum (600)
+ 0x75, 0x10, // Report Size (16)
+ 0x09, 0x30, // Usage (X)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x26, 0x00, 0x04, // Logical Maximum (1024)
+ 0x09, 0x31, // Usage (Y)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xB4, // Pop
+ 0xC0, // End Collection
+ 0x05, 0x0D, // Usage Page (Digitizer)
+ 0x09, 0x22, // Usage (Finger)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x09, 0x42, // Usage (Tip Switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x06, // Report Size (6)
+ 0x09, 0x51, // Usage (0x51)
+ 0x25, 0x3F, // Logical Maximum (63)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0xA4, // Push
+ 0x26, 0x58, 0x02, // Logical Maximum (600)
+ 0x75, 0x10, // Report Size (16)
+ 0x09, 0x30, // Usage (X)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x26, 0x00, 0x04, // Logical Maximum (1024)
+ 0x09, 0x31, // Usage (Y)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xB4, // Pop
+ 0xC0, // End Collection
+ 0x05, 0x0D, // Usage Page (Digitizer)
+ 0x09, 0x22, // Usage (Finger)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x09, 0x42, // Usage (Tip Switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x06, // Report Size (6)
+ 0x09, 0x51, // Usage (0x51)
+ 0x25, 0x3F, // Logical Maximum (63)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0xA4, // Push
+ 0x26, 0x58, 0x02, // Logical Maximum (600)
+ 0x75, 0x10, // Report Size (16)
+ 0x09, 0x30, // Usage (X)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x26, 0x00, 0x04, // Logical Maximum (1024)
+ 0x09, 0x31, // Usage (Y)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xB4, // Pop
+ 0xC0, // End Collection
+ 0x05, 0x0D, // Usage Page (Digitizer)
+ 0x09, 0x22, // Usage (Finger)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x09, 0x42, // Usage (Tip Switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x06, // Report Size (6)
+ 0x09, 0x51, // Usage (0x51)
+ 0x25, 0x3F, // Logical Maximum (63)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0xA4, // Push
+ 0x26, 0x58, 0x02, // Logical Maximum (600)
+ 0x75, 0x10, // Report Size (16)
+ 0x09, 0x30, // Usage (X)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x26, 0x00, 0x04, // Logical Maximum (1024)
+ 0x09, 0x31, // Usage (Y)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xB4, // Pop
+ 0xC0, // End Collection
+ 0x05, 0x0D, // Usage Page (Digitizer)
+ 0x09, 0x22, // Usage (Finger)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x09, 0x42, // Usage (Tip Switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x06, // Report Size (6)
+ 0x09, 0x51, // Usage (0x51)
+ 0x25, 0x3F, // Logical Maximum (63)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0xA4, // Push
+ 0x26, 0x58, 0x02, // Logical Maximum (600)
+ 0x75, 0x10, // Report Size (16)
+ 0x09, 0x30, // Usage (X)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x26, 0x00, 0x04, // Logical Maximum (1024)
+ 0x09, 0x31, // Usage (Y)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xB4, // Pop
+ 0xC0, // End Collection
+ 0x05, 0x0D, // Usage Page (Digitizer)
+ 0x09, 0x54, // Usage (0x54)
+ 0x25, 0x05, // Logical Maximum (5)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x08, // Report Size (8)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+};
+
+bool is_gt92xx_touch_report_desc(const uint8_t* data, size_t len) {
+ if (!data)
+ return false;
+
+ if (len != sizeof(gt92xx_touch_report_desc))
+ return false;
+
+ return (memcmp(data, gt92xx_touch_report_desc, len) == 0);
+}
+
+zx_status_t setup_gt92xx_touch(int fd) {
+ if (fd < 0)
+ return ZX_ERR_INVALID_ARGS;
+
+ return ZX_OK;
+}
+
+size_t get_gt92xx_report_desc(const uint8_t** buf) {
+ *buf = gt92xx_touch_report_desc;
+ return sizeof(gt92xx_touch_report_desc);
+}
\ No newline at end of file
diff --git a/system/ulib/hid/include/hid/gt92xx.h b/system/ulib/hid/include/hid/gt92xx.h
new file mode 100644
index 0000000..c35eecc
--- /dev/null
+++ b/system/ulib/hid/include/hid/gt92xx.h
@@ -0,0 +1,33 @@
+// 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.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+__BEGIN_CDECLS
+
+#define GT92XX_RPT_ID_TOUCH 1
+
+typedef struct gt92xx_finger {
+ uint8_t finger_id;
+ uint16_t x;
+ uint16_t y;
+} __PACKED gt92xx_finger_t;
+
+typedef struct gt92xx_touch {
+ uint8_t rpt_id;
+ gt92xx_finger_t fingers[5];
+ uint8_t contact_count; // will be zero for reports for fingers 6-10
+} __PACKED gt92xx_touch_t;
+
+bool is_gt92xx_touch_report_desc(const uint8_t* data, size_t len);
+zx_status_t setup_gt92xx_touch(int fd);
+
+size_t get_gt92xx_report_desc(const uint8_t** buf);
+
+__END_CDECLS
diff --git a/system/ulib/hid/rules.mk b/system/ulib/hid/rules.mk
index 47a2782..85b761b 100644
--- a/system/ulib/hid/rules.mk
+++ b/system/ulib/hid/rules.mk
@@ -13,6 +13,7 @@
$(LOCAL_DIR)/egalax.c \
$(LOCAL_DIR)/eyoyo.c \
$(LOCAL_DIR)/ft3x27.c \
+ $(LOCAL_DIR)/gt92xx.c \
$(LOCAL_DIR)/hid.c \
$(LOCAL_DIR)/keymaps.c \
$(LOCAL_DIR)/paradise.c \