diff options
Diffstat (limited to 'driver/touchpad_elan.c')
-rw-r--r-- | driver/touchpad_elan.c | 847 |
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, - ¶m[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 |