summaryrefslogtreecommitdiff
path: root/driver/touchpad_elan.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/touchpad_elan.c')
-rw-r--r--driver/touchpad_elan.c847
1 files changed, 0 insertions, 847 deletions
diff --git a/driver/touchpad_elan.c b/driver/touchpad_elan.c
deleted file mode 100644
index 6df4f0f7de..0000000000
--- a/driver/touchpad_elan.c
+++ /dev/null
@@ -1,847 +0,0 @@
-/* Copyright 2016 The Chromium OS 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 "byteorder.h"
-#include "common.h"
-#include "console.h"
-#include "gpio.h"
-#include "hwtimer.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "math_util.h"
-#include "sha256.h"
-#include "shared_mem.h"
-#include "task.h"
-#include "tablet_mode.h"
-#include "timer.h"
-#include "touchpad.h"
-#include "update_fw.h"
-#include "util.h"
-#include "usb_api.h"
-#include "usb_hid_touchpad.h"
-#include "watchdog.h"
-
-/* Console output macros */
-#define CPUTS(outstr) cputs(CC_TOUCHPAD, outstr)
-#define CPRINTF(format, args...) cprintf(CC_TOUCHPAD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_TOUCHPAD, format, ## args)
-
-#define TASK_EVENT_POWER TASK_EVENT_CUSTOM_BIT(0)
-
-/******************************************************************************/
-/* How to talk to the controller */
-/******************************************************************************/
-
-#define ELAN_VENDOR_ID 0x04f3
-
-#define ETP_I2C_RESET 0x0100
-#define ETP_I2C_WAKE_UP 0x0800
-#define ETP_I2C_SLEEP 0x0801
-
-#define ETP_I2C_STAND_CMD 0x0005
-#define ETP_I2C_UNIQUEID_CMD 0x0101
-#define ETP_I2C_FW_VERSION_CMD 0x0102
-#define ETP_I2C_OSM_VERSION_CMD 0x0103
-#define ETP_I2C_XY_TRACENUM_CMD 0x0105
-#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
-#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
-#define ETP_I2C_RESOLUTION_CMD 0x0108
-#define ETP_I2C_IAP_VERSION_CMD 0x0110
-#define ETP_I2C_PRESSURE_CMD 0x010A
-#define ETP_I2C_SET_CMD 0x0300
-#define ETP_I2C_IAP_TYPE_CMD 0x0304
-#define ETP_I2C_POWER_CMD 0x0307
-#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
-
-#define ETP_ENABLE_ABS 0x0001
-
-#define ETP_DISABLE_POWER 0x0001
-
-#define ETP_I2C_REPORT_LEN 34
-
-#define ETP_MAX_FINGERS 5
-#define ETP_FINGER_DATA_LEN 5
-
-#define ETP_PRESSURE_OFFSET 25
-#define ETP_FWIDTH_REDUCE 90
-
-#define ETP_REPORT_ID 0x5D
-#define ETP_REPORT_ID_OFFSET 2
-#define ETP_TOUCH_INFO_OFFSET 3
-#define ETP_FINGER_DATA_OFFSET 4
-#define ETP_HOVER_INFO_OFFSET 30
-#define ETP_MAX_REPORT_LEN 34
-
-#define ETP_IAP_START_ADDR 0x0083
-
-#define ETP_I2C_IAP_RESET_CMD 0x0314
-#define ETP_I2C_IAP_RESET 0xF0F0
-#define ETP_I2C_IAP_CTRL_CMD 0x0310
-#define ETP_I2C_MAIN_MODE_ON BIT(9)
-#define ETP_I2C_IAP_CMD 0x0311
-#define ETP_I2C_IAP_PASSWORD 0x1EA5
-
-#define ETP_I2C_IAP_REG_L 0x01
-#define ETP_I2C_IAP_REG_H 0x06
-
-#define ETP_FW_IAP_PAGE_ERR BIT(5)
-#define ETP_FW_IAP_INTF_ERR BIT(4)
-
-#ifdef CONFIG_USB_UPDATE
-/* The actual FW_SIZE depends on IC. */
-#define FW_SIZE CONFIG_TOUCHPAD_VIRTUAL_SIZE
-#endif
-
-struct {
- /* Max X/Y position */
- uint16_t max_x;
- uint16_t max_y;
- /* Scaling factor for finger width/height */
- uint16_t width_x;
- uint16_t width_y;
- /* Pressure adjustment */
- uint8_t pressure_adj;
- uint16_t ic_type;
- uint16_t page_count;
- uint16_t page_size;
- uint16_t iap_version;
-} elan_tp_params;
-
-/*
- * Report a more reasonable pressure value, so that no adjustment is necessary
- * on Chrome OS side. 3216/1024 ~= 3.1416.
- */
-const int pressure_mult = 3216;
-const int pressure_div = 1024;
-
-static int elan_tp_read_cmd(uint16_t reg, uint16_t *val)
-{
- uint8_t buf[2];
-
- buf[0] = reg;
- buf[1] = reg >> 8;
-
- return i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- buf, sizeof(buf), (uint8_t *)val, sizeof(*val));
-}
-
-static int elan_tp_write_cmd(uint16_t reg, uint16_t val)
-{
- uint8_t buf[4];
-
- buf[0] = reg;
- buf[1] = reg >> 8;
- buf[2] = val;
- buf[3] = val >> 8;
-
- return i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- buf, sizeof(buf), NULL, 0);
-}
-
-/* Power is on by default. */
-static int elan_tp_power = 1;
-
-static int elan_tp_set_power(int enable)
-{
- int rv;
- uint16_t val;
-
- if ((enable && elan_tp_power) || (!enable && !elan_tp_power))
- return EC_SUCCESS;
-
- CPRINTS("elan TP power %s", enable ? "on" : "off");
-
- rv = elan_tp_read_cmd(ETP_I2C_POWER_CMD, &val);
- if (rv)
- goto out;
-
- if (enable)
- val &= ~ETP_DISABLE_POWER;
- else
- val |= ETP_DISABLE_POWER;
-
- rv = elan_tp_write_cmd(ETP_I2C_POWER_CMD, val);
-
- elan_tp_power = enable;
-out:
- return rv;
-}
-
-static int finger_status[ETP_MAX_FINGERS] = {0};
-
-/*
- * Timestamp of last interrupt (32 bits are enough as we divide the value by 100
- * and then put it in a 16-bit field).
- */
-static uint32_t irq_ts;
-
-/*
- * Read touchpad report.
- * Returns 0 on success, positive (EC_RES_*) value on I2C error, and a negative
- * value if the I2C transaction is successful but the data is invalid (fairly
- * common).
- */
-static int elan_tp_read_report(void)
-{
- int rv;
- uint8_t tp_buf[ETP_I2C_REPORT_LEN];
- int i, ri;
- uint8_t touch_info;
- uint8_t hover_info;
- uint8_t *finger = tp_buf+ETP_FINGER_DATA_OFFSET;
- struct usb_hid_touchpad_report report;
- uint16_t timestamp;
-
- /* Compute and save timestamp early in case another interrupt comes. */
- timestamp = irq_ts / USB_HID_TOUCHPAD_TIMESTAMP_UNIT;
-
- rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- NULL, 0, tp_buf, ETP_I2C_REPORT_LEN);
-
- if (rv) {
- CPRINTS("read report error (%d)", rv);
- return rv;
- }
-
- if (tp_buf[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID) {
- CPRINTS("Invalid report id (%x)", tp_buf[ETP_REPORT_ID_OFFSET]);
- return -1;
- }
-
- memset(&report, 0, sizeof(report));
- report.id = 0x01;
- ri = 0; /* Next finger index in HID report */
-
- touch_info = tp_buf[ETP_TOUCH_INFO_OFFSET];
- hover_info = tp_buf[ETP_HOVER_INFO_OFFSET];
-
- for (i = 0; i < ETP_MAX_FINGERS; i++) {
- int valid = touch_info & (1 << (3+i));
-
- if (valid) {
- int width = finger[3] & 0x0f;
- int height = (finger[3] & 0xf0) >> 4;
- int pressure = finger[4] + elan_tp_params.pressure_adj;
- pressure = DIV_ROUND_NEAREST(pressure * pressure_mult,
- pressure_div);
-
- width = MIN(4095, width * elan_tp_params.width_x);
- height = MIN(4095, height * elan_tp_params.width_y);
- pressure = MIN(1023, pressure);
-
- report.finger[ri].confidence = 1;
- report.finger[ri].tip = 1;
- report.finger[ri].inrange = 1;
- report.finger[ri].id = i;
- report.finger[ri].width = width;
- report.finger[ri].height = height;
- report.finger[ri].x =
- ((finger[0] & 0xf0) << 4) | finger[1];
- report.finger[ri].y =
- elan_tp_params.max_y -
- (((finger[0] & 0x0f) << 8) | finger[2]);
- report.finger[ri].pressure = pressure;
- finger += ETP_FINGER_DATA_LEN;
- ri++;
- finger_status[i] = 1;
- } else if (finger_status[i]) {
- report.finger[ri].id = i;
- /* When a finger is leaving, it's not a plam */
- report.finger[ri].confidence = 1;
- ri++;
- finger_status[i] = 0;
- }
- }
-
- report.count = ri;
- report.timestamp = timestamp;
-
- if (touch_info & 0x01) {
- /* Do not report zero-finger click events */
- if (report.count > 0)
- report.button = 1;
- }
-
- if (hover_info & 0x40) {
- /* TODO(b/35582031): Report hover event */
- CPRINTF("[TP] hover!\n");
- }
-
- set_touchpad_report(&report);
-
- return 0;
-}
-
-static void elan_get_fwinfo(void)
-{
- uint16_t ic_type = elan_tp_params.ic_type;
- uint16_t iap_version = elan_tp_params.iap_version;
-
- switch (ic_type) {
- case 0x09:
- elan_tp_params.page_count = 768;
- break;
- case 0x0D:
- elan_tp_params.page_count = 896;
- break;
- case 0x00:
- case 0x10:
- case 0x14:
- case 0x15:
- elan_tp_params.page_count = 1024;
- break;
- default:
- elan_tp_params.page_count = -1;
- CPRINTS("unknown ic_type: %d", ic_type);
- }
-
- if ((ic_type == 0x14 || ic_type == 0x15) && iap_version >= 2) {
- elan_tp_params.page_count /= 8;
- elan_tp_params.page_size = 512;
- } else if (ic_type >= 0x0D && iap_version >= 1) {
- elan_tp_params.page_count /= 2;
- elan_tp_params.page_size = 128;
- } else {
- elan_tp_params.page_size = 64;
- }
-}
-
-/*
- * - dpi == logical dimension / physical dimension (inches)
- * (254 tenths of mm per inch)
- */
-__maybe_unused static int calc_physical_dimension(int dpi, int logical_dim)
-{
- return round_divide(254 * logical_dim, dpi);
-}
-
-/* Initialize the controller ICs after reset */
-static void elan_tp_init(void)
-{
- int rv;
- uint8_t val[2];
- int dpi_x, dpi_y;
-
- CPRINTS("%s", __func__);
-
- elan_tp_write_cmd(ETP_I2C_STAND_CMD, ETP_I2C_RESET);
- msleep(100);
- rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- NULL, 0, val, sizeof(val));
-
- CPRINTS("reset rv %d buf=%04x", rv, *((uint16_t *)val));
- if (rv)
- goto out;
-
- /* Read IC type, IAP version */
- rv = elan_tp_read_cmd(ETP_I2C_OSM_VERSION_CMD, &elan_tp_params.ic_type);
- CPRINTS("%s: ic_type:%04X.", __func__, elan_tp_params.ic_type);
- elan_tp_params.ic_type >>= 8;
- if (rv)
- goto out;
-
- rv = elan_tp_read_cmd(ETP_I2C_IAP_VERSION_CMD,
- &elan_tp_params.iap_version);
- CPRINTS("%s: iap_version:%04X.", __func__, elan_tp_params.iap_version);
- elan_tp_params.iap_version >>= 8;
- if (rv)
- goto out;
-
- elan_get_fwinfo();
-
- /* Read min/max */
- rv = elan_tp_read_cmd(ETP_I2C_MAX_X_AXIS_CMD, &elan_tp_params.max_x);
- if (rv)
- goto out;
- rv = elan_tp_read_cmd(ETP_I2C_MAX_Y_AXIS_CMD, &elan_tp_params.max_y);
- if (rv)
- goto out;
-
- /* Read min/max */
- rv = elan_tp_read_cmd(ETP_I2C_XY_TRACENUM_CMD, (uint16_t *)val);
- if (rv)
- goto out;
- if (val[0] == 0 || val[1] == 0) {
- CPRINTS("Invalid XY_TRACENUM");
- goto out;
- }
-
- /* ETP_FWIDTH_REDUCE reduces the apparent width to avoid treating large
- * finger as palm. Multiply value by 2 as HID multitouch divides it.
- */
- elan_tp_params.width_x =
- 2 * ((elan_tp_params.max_x / val[0]) - ETP_FWIDTH_REDUCE);
- elan_tp_params.width_y =
- 2 * ((elan_tp_params.max_y / val[1]) - ETP_FWIDTH_REDUCE);
-
- rv = elan_tp_read_cmd(ETP_I2C_PRESSURE_CMD, (uint16_t *)val);
- if (rv)
- goto out;
- elan_tp_params.pressure_adj = (val[0] & 0x10) ? 0 : ETP_PRESSURE_OFFSET;
-
- rv = elan_tp_read_cmd(ETP_I2C_RESOLUTION_CMD, (uint16_t *)val);
- if (rv)
- goto out;
-
- dpi_x = 10*val[0] + 790;
- dpi_y = 10*val[1] + 790;
-
- CPRINTS("max=%d/%d width=%d/%d adj=%d dpi=%d/%d",
- elan_tp_params.max_x, elan_tp_params.max_y,
- elan_tp_params.width_x, elan_tp_params.width_y,
- elan_tp_params.pressure_adj, dpi_x, dpi_y);
-
-#ifdef CONFIG_USB_HID_TOUCHPAD
- /* Validity check dimensions provided at build time. */
- if (elan_tp_params.max_x != CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_X ||
- elan_tp_params.max_y != CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_Y ||
- calc_physical_dimension(dpi_x, elan_tp_params.max_x) !=
- CONFIG_USB_HID_TOUCHPAD_PHYSICAL_MAX_X ||
- calc_physical_dimension(dpi_y, elan_tp_params.max_y) !=
- CONFIG_USB_HID_TOUCHPAD_PHYSICAL_MAX_Y) {
- CPRINTS("*** TP mismatch!");
- }
-#endif
-
- /* Switch to absolute mode */
- rv = elan_tp_write_cmd(ETP_I2C_SET_CMD, ETP_ENABLE_ABS);
- if (rv)
- goto out;
-
- /* Sleep control off */
- rv = elan_tp_write_cmd(ETP_I2C_STAND_CMD, ETP_I2C_WAKE_UP);
-
- /* Enable interrupt to fetch reports */
- gpio_enable_interrupt(GPIO_TOUCHPAD_INT);
-
-out:
- CPRINTS("%s:%d", __func__, rv);
-
- return;
-}
-DECLARE_DEFERRED(elan_tp_init);
-
-#ifdef CONFIG_USB_UPDATE
-int touchpad_get_info(struct touchpad_info *tp)
-{
- int rv;
- uint16_t val;
-
- tp->status = EC_RES_SUCCESS;
- tp->vendor = ELAN_VENDOR_ID;
-
- /* Get unique ID, FW, SM version. */
- rv = elan_tp_read_cmd(ETP_I2C_UNIQUEID_CMD, &val);
- if (rv)
- return -1;
- tp->elan.id = val;
-
- rv = elan_tp_read_cmd(ETP_I2C_FW_VERSION_CMD, &val);
- if (rv)
- return -1;
- tp->elan.fw_version = val & 0xff;
-
- rv = elan_tp_read_cmd(ETP_I2C_FW_CHECKSUM_CMD, &val);
- if (rv)
- return -1;
- tp->elan.fw_checksum = val;
-
- return sizeof(*tp);
-}
-
-static int elan_in_main_mode(void)
-{
- uint16_t val;
-
- elan_tp_read_cmd(ETP_I2C_IAP_CTRL_CMD, &val);
- return val & ETP_I2C_MAIN_MODE_ON;
-}
-
-static int elan_read_write_iap_type(void)
-{
- for (int retry = 0; retry < 3; ++retry) {
- uint16_t val;
-
- if (elan_tp_write_cmd(ETP_I2C_IAP_TYPE_CMD,
- elan_tp_params.page_size / 2))
- return EC_ERROR_UNKNOWN;
-
- if (elan_tp_read_cmd(ETP_I2C_IAP_TYPE_CMD, &val))
- return EC_ERROR_UNKNOWN;
-
- if (val == elan_tp_params.page_size / 2)
- return EC_SUCCESS;
-
- }
- return EC_ERROR_UNKNOWN;
-}
-
-static int elan_prepare_for_update(void)
-{
- uint16_t rx_buf;
- int initial_mode;
-
- initial_mode = elan_in_main_mode();
- if (!initial_mode) {
- CPRINTS("%s: In IAP mode, reset IC.", __func__);
- elan_tp_write_cmd(ETP_I2C_IAP_RESET_CMD, ETP_I2C_IAP_RESET);
- msleep(30);
- }
- /* Send the passphrase */
- elan_tp_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD);
- msleep(initial_mode ? 100 : 30);
-
- /* We should be in the IAP mode now */
- if (elan_in_main_mode()) {
- CPRINTS("%s: Failure to enter IAP mode.", __func__);
- return EC_ERROR_UNKNOWN;
- }
-
- if (elan_tp_params.ic_type >= 0x0D && elan_tp_params.iap_version >= 1) {
- if (elan_read_write_iap_type())
- return EC_ERROR_UNKNOWN;
- }
-
- /* Send the passphrase again */
- elan_tp_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD);
- msleep(30);
-
- /* Verify the password */
- if (elan_tp_read_cmd(ETP_I2C_IAP_CMD, &rx_buf)) {
- CPRINTS("%s: Cannot read IAP password.", __func__);
- return EC_ERROR_UNKNOWN;
- }
- if (rx_buf != ETP_I2C_IAP_PASSWORD) {
- CPRINTS("%s: Got an unexpected IAP password %0x4x.", __func__,
- rx_buf);
- return EC_ERROR_UNKNOWN;
- }
- return EC_SUCCESS;
-}
-
-static int touchpad_update_page(const uint8_t *data)
-{
- const uint8_t cmd[2] = {ETP_I2C_IAP_REG_L, ETP_I2C_IAP_REG_H};
- uint16_t checksum = 0;
- uint16_t rx_buf;
- int i, rv;
-
- for (i = 0; i < elan_tp_params.page_size; i += 2)
- checksum += ((uint16_t)(data[i + 1]) << 8) | (data[i]);
- checksum = htole16(checksum);
-
- i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1);
-
- rv = i2c_xfer_unlocked(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- cmd, sizeof(cmd), NULL, 0, I2C_XFER_START);
- if (rv)
- goto fail;
- rv = i2c_xfer_unlocked(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- data, elan_tp_params.page_size, NULL, 0, 0);
- if (rv)
- goto fail;
- rv = i2c_xfer_unlocked(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- (uint8_t *)&checksum, sizeof(checksum), NULL, 0,
- I2C_XFER_STOP);
- if (rv)
- goto fail;
-
-fail:
- i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0);
- if (rv)
- return rv;
- msleep(elan_tp_params.page_size >= 512 ? 50 : 35);
-
- rv = elan_tp_read_cmd(ETP_I2C_IAP_CTRL_CMD, &rx_buf);
-
- if (rv || (rx_buf & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR))) {
- CPRINTS("%s: IAP reports failed write : %x.",
- __func__, rx_buf);
- return EC_ERROR_UNKNOWN;
- }
- return 0;
-}
-
-int touchpad_update_write(int offset, int size, const uint8_t *data)
-{
- static int iap_addr = -1;
- int addr, rv;
-
- CPRINTS("%s %08x %d", __func__, offset, size);
-
- if (offset == 0) {
- /* Verify the IC type is aligned with defined firmware size */
- if (elan_tp_params.page_size * elan_tp_params.page_count
- != FW_SIZE) {
- CPRINTS("%s: IC(%d*%d) size and FW_SIZE(%d) mismatch",
- __func__, elan_tp_params.page_count,
- elan_tp_params.page_size, FW_SIZE);
- return EC_ERROR_UNKNOWN;
- }
-
- gpio_disable_interrupt(GPIO_TOUCHPAD_INT);
- CPRINTS("%s: prepare fw update.", __func__);
- rv = elan_prepare_for_update();
- if (rv)
- return rv;
- iap_addr = 0;
- }
-
- if (offset <= (ETP_IAP_START_ADDR * 2) &&
- (ETP_IAP_START_ADDR * 2) < (offset + size)) {
- iap_addr = ((data[ETP_IAP_START_ADDR * 2 - offset + 1] << 8) |
- data[ETP_IAP_START_ADDR * 2 - offset]) << 1;
- CPRINTS("%s: payload starts from 0x%x.", __func__, iap_addr);
- }
-
- /* Data that comes in must align with page_size */
- if (offset % elan_tp_params.page_size)
- return EC_ERROR_INVAL;
-
- for (addr = offset; addr < (offset + size);
- addr += elan_tp_params.page_size) {
- if (iap_addr > addr) /* Skip chunk */
- continue;
- rv = touchpad_update_page(data + addr - offset);
- if (rv)
- return rv;
- CPRINTF("/p%d", addr / elan_tp_params.page_size);
- watchdog_reload();
- }
- CPRINTF("\n");
-
- if (offset + size == FW_SIZE) {
- CPRINTS("%s: End update, wait for reset.", __func__);
- hook_call_deferred(&elan_tp_init_data, 600 * MSEC);
- }
- return EC_SUCCESS;
-}
-
-/* Debugging mode. */
-
-/* Allowed debug commands. We only store a hash of the allowed commands. */
-#define TOUCHPAD_ELAN_DEBUG_CMD_LENGTH 50
-#define TOUCHPAD_ELAN_DEBUG_NUM_CMD 2
-
-static const uint8_t
-allowed_command_hashes[TOUCHPAD_ELAN_DEBUG_NUM_CMD][SHA256_DIGEST_SIZE] = {
- {
- 0x0a, 0xf6, 0x37, 0x03, 0x93, 0xb2, 0xde, 0x8c,
- 0x56, 0x7b, 0x86, 0xba, 0xa6, 0x79, 0xe3, 0xa3,
- 0x8b, 0xc7, 0x15, 0xf2, 0x53, 0xcf, 0x71, 0x8b,
- 0x3d, 0xe4, 0x81, 0xf9, 0xd9, 0xa8, 0x78, 0x48
- },
- {
- 0xac, 0xe5, 0xbf, 0x17, 0x1f, 0xde, 0xce, 0x76,
- 0x0c, 0x0e, 0xf8, 0xa2, 0xe9, 0x67, 0x2d, 0xc9,
- 0x1b, 0xd4, 0xba, 0x34, 0x51, 0xca, 0xf6, 0x6d,
- 0x7b, 0xb2, 0x1f, 0x14, 0x82, 0x1c, 0x0b, 0x74
- },
-};
-
-/* Debugging commands need to allocate a <=1k buffer. */
-SHARED_MEM_CHECK_SIZE(1024);
-
-int touchpad_debug(const uint8_t *param, unsigned int param_size,
- uint8_t **data, unsigned int *data_size)
-{
- static uint8_t *buffer;
- static unsigned int buffer_size;
- unsigned int offset;
-
- /* Offset parameter is 1 byte. */
- if (param_size < 1)
- return EC_RES_INVALID_PARAM;
-
- /*
- * Debug command, compute SHA-256, check that it matches allowed hashes,
- * and execute I2C command.
- *
- * param[0] must be 0xff
- * param[1] is the offset of the command in the data
- * param[2] is the command length
- * param[3-4] is the read-back length (MSB first), can be 0
- * param[5-49] is verified using SHA-256 hash.
- */
- if (param[0] == 0xff && param_size == TOUCHPAD_ELAN_DEBUG_CMD_LENGTH) {
- struct sha256_ctx ctx;
- uint8_t *command_hash;
- unsigned int offset = param[1];
- unsigned int write_length = param[2];
- unsigned int read_length =
- ((unsigned int)param[3] << 8) | param[4];
- int i;
- int match;
- int rv;
-
- if (offset < 5 || write_length == 0 ||
- (offset + write_length) >= TOUCHPAD_ELAN_DEBUG_CMD_LENGTH)
- return EC_RES_INVALID_PARAM;
-
- SHA256_init(&ctx);
- SHA256_update(&ctx, param+5, TOUCHPAD_ELAN_DEBUG_CMD_LENGTH-5);
- command_hash = SHA256_final(&ctx);
-
- match = 0;
- for (i = 0; i < TOUCHPAD_ELAN_DEBUG_NUM_CMD; i++) {
- if (!memcmp(command_hash, allowed_command_hashes[i],
- sizeof(allowed_command_hashes[i]))) {
- match = 1;
- break;
- }
- }
-
- if (!match)
- return EC_RES_INVALID_PARAM;
-
- if (buffer) {
- shared_mem_release(buffer);
- buffer = NULL;
- }
-
- buffer_size = read_length;
-
- if (read_length > 0) {
- if (shared_mem_acquire(buffer_size,
- (char **)&buffer) != EC_SUCCESS) {
- buffer = NULL;
- buffer_size = 0;
- return EC_RES_BUSY;
- }
-
- memset(buffer, 0, buffer_size);
- }
-
- rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT,
- CONFIG_TOUCHPAD_I2C_ADDR_FLAGS,
- &param[offset], write_length,
- buffer, read_length);
-
- if (rv)
- return EC_RES_BUS_ERROR;
-
- return EC_RES_SUCCESS;
- }
-
- /*
- * Data request: Retrieve previously read data from buffer, in blocks of
- * 64 bytes.
- */
- offset = param[0] * 64;
-
- if (!buffer)
- return EC_RES_UNAVAILABLE;
-
- if (offset >= buffer_size) {
- shared_mem_release(buffer);
- buffer = NULL;
- *data = NULL;
- *data_size = 0;
- return EC_RES_OVERFLOW;
- }
-
- *data = buffer + offset;
- *data_size = MIN(64, buffer_size - offset);
-
- return EC_RES_SUCCESS;
-}
-#endif
-
-/*
- * Try to read touchpad report up to 3 times, reset the touchpad if we still
- * fail.
- */
-void elan_tp_read_report_retry(void)
-{
- int ret;
- int retry = 3;
-
- while (retry--) {
- ret = elan_tp_read_report();
-
- if (ret <= 0)
- return;
-
- /* Try again */
- msleep(1);
- }
-
- /* Failed to read data, reset the touchpad. */
- CPRINTF("Resetting TP.\n");
- board_touchpad_reset();
- elan_tp_init();
-}
-
-void touchpad_interrupt(enum gpio_signal signal)
-{
- irq_ts = __hw_clock_source_read();
-
- task_wake(TASK_ID_TOUCHPAD);
-}
-
-/* Make a decision on touchpad power, based on USB and tablet mode status. */
-static void touchpad_power_control(void)
-{
- static int enabled = 1;
- int enable = 1;
-
-#ifdef CONFIG_USB_SUSPEND
- enable = enable &&
- (!usb_is_suspended() || usb_is_remote_wakeup_enabled());
-#endif
-
-#ifdef CONFIG_TABLET_MODE
- enable = enable && !tablet_get_mode();
-#endif
-
- if (enabled == enable)
- return;
-
- elan_tp_set_power(enable);
-
- enabled = enable;
-}
-
-void touchpad_task(void *u)
-{
- uint32_t event;
-
- elan_tp_init();
- touchpad_power_control();
-
- while (1) {
- event = task_wait_event(-1);
-
- if (event & TASK_EVENT_WAKE)
- elan_tp_read_report_retry();
-
- if (event & TASK_EVENT_POWER)
- touchpad_power_control();
- }
-}
-
-/*
- * When USB PM status changes, or tablet mode changes, call in the main task to
- * decide whether to turn touchpad on or off.
- */
-#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_TABLET_MODE)
-static void touchpad_power_change(void)
-{
- task_set_event(TASK_ID_TOUCHPAD, TASK_EVENT_POWER);
-}
-#endif
-#ifdef CONFIG_USB_SUSPEND
-DECLARE_HOOK(HOOK_USB_PM_CHANGE, touchpad_power_change, HOOK_PRIO_DEFAULT);
-#endif
-#ifdef CONFIG_TABLET_MODE
-DECLARE_HOOK(HOOK_TABLET_MODE_CHANGE, touchpad_power_change, HOOK_PRIO_DEFAULT);
-#endif