From b9939cf42b22f6e2a4fcaebd75e7520538afccd6 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 9 Nov 2017 16:39:37 -0800 Subject: touchpad_elan: Add support for debugging mode We add 2 hashes that each map to a list of allowed commands (command list is secret, and provided by Elan). UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG on the USB interface have the following format for the parameter: - param[0]: must be 0xff - param[1]: offset of the I2C command in data - param[2]: command length - param[3-4]: read-back length (MSB first) over I2C, can be 0 - param[5-49]: data, is verified using SHA-256 hash. The I2C command pointed at by param[1-2] is then sent over I2C, and the data is read back into a shared buffer of size param[3-4]. The data can be fetched over USB by a single byte USB update touchpad debug command (indicating an offset in 64-bytes unit), fetching data from the shared buffer in blocks of 64 bytes. BRANCH=none BUG=b:63993891 TEST=Elan can run debugging commands using their proprietary tool. Change-Id: Idb19bcb940b7f030c3b3aeaf39d6b725fcb9ef34 Signed-off-by: Nicolas Boichat Reviewed-on: https://chromium-review.googlesource.com/763576 Reviewed-by: Chun-ta Lin --- driver/touchpad_elan.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) (limited to 'driver/touchpad_elan.c') diff --git a/driver/touchpad_elan.c b/driver/touchpad_elan.c index d3f19275ff..ba0d543a4d 100644 --- a/driver/touchpad_elan.c +++ b/driver/touchpad_elan.c @@ -9,6 +9,8 @@ #include "hwtimer.h" #include "hooks.h" #include "i2c.h" +#include "sha256.h" +#include "shared_mem.h" #include "task.h" #include "timer.h" #include "touchpad.h" @@ -506,11 +508,132 @@ int touchpad_update_write(int offset, int size, const uint8_t *data) return EC_SUCCESS; } -/* TODO(b:63993891): Implement debugging mode for Elan touchpad. */ +/* 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 + }, +}; + int touchpad_debug(const uint8_t *param, unsigned int param_size, uint8_t **data, unsigned int *data_size) { - return EC_RES_INVALID_COMMAND; + 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); + } + + i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1); + rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, + CONFIG_TOUCHPAD_I2C_ADDR, + ¶m[offset], write_length, + buffer, read_length, I2C_XFER_SINGLE); + i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0); + + 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 -- cgit v1.2.1