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 <drinkcat@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/763576
Reviewed-by: Chun-ta Lin <itspeter@chromium.org>
This commit is contained in:
Nicolas Boichat
2017-11-09 16:39:37 -08:00
committed by chrome-bot
parent d99b8722c9
commit b9939cf42b

View File

@@ -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,
&param[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