diff options
Diffstat (limited to 'common/host_command_master.c')
-rw-r--r-- | common/host_command_master.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/common/host_command_master.c b/common/host_command_master.c new file mode 100644 index 0000000000..55e3befbee --- /dev/null +++ b/common/host_command_master.c @@ -0,0 +1,185 @@ +/* Copyright (c) 2014 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. + */ + +/* Host command master module for Chrome EC */ + +#include "common.h" +#include "console.h" +#include "host_command.h" +#include "i2c.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_HOSTCMD, outstr) +#define CPRINTF(format, args...) cprintf(CC_HOSTCMD, format, ## args) + +/* + * Sends a command to the PD (protocol v3). + * + * Returns >= 0 for success, or negative if error. + */ +int pd_host_command(int command, int version, + const void *outdata, int outsize, + void *indata, int insize) +{ + int ret, i; + int resp_len; + struct ec_host_request rq; + struct ec_host_response rs; + static uint8_t req_buf[EC_LPC_HOST_PACKET_SIZE]; + static uint8_t resp_buf[EC_LPC_HOST_PACKET_SIZE]; + uint8_t sum = 0; + const uint8_t *c; + uint8_t *d; + + /* Fail if output size is too big */ + if (outsize + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE) + return -EC_RES_REQUEST_TRUNCATED; + + /* Fill in request packet */ + rq.struct_version = EC_HOST_REQUEST_VERSION; + rq.checksum = 0; + rq.command = command; + rq.command_version = version; + rq.reserved = 0; + rq.data_len = outsize; + + /* Copy data and start checksum */ + for (i = 0, c = (const uint8_t *)outdata; i < outsize; i++, c++) { + req_buf[sizeof(rq) + 1 + i] = *c; + sum += *c; + } + + /* Finish checksum */ + for (i = 0, c = (const uint8_t *)&rq; i < sizeof(rq); i++, c++) + sum += *c; + + /* Write checksum field so the entire packet sums to 0 */ + rq.checksum = (uint8_t)(-sum); + + /* Copy header */ + for (i = 0, c = (const uint8_t *)&rq; i < sizeof(rq); i++, c++) + req_buf[1 + i] = *c; + + /* Set command to use protocol v3 */ + req_buf[0] = EC_COMMAND_PROTOCOL_3; + + /* + * Transmit all data and receive 2 bytes for return value and response + * length. + */ + i2c_lock(I2C_PORT_PD_MCU, 1); + ret = i2c_xfer(I2C_PORT_PD_MCU, CONFIG_USB_PD_I2C_SLAVE_ADDR, + &req_buf[0], outsize + sizeof(rq) + 1, &resp_buf[0], + 2, I2C_XFER_START); + i2c_lock(I2C_PORT_PD_MCU, 0); + if (ret) { + CPRINTF("[%T i2c transaction 1 failed: %d]\n", ret); + return -ret; + } + + ret = resp_buf[0]; + resp_len = resp_buf[1]; + + if (ret) + CPRINTF("[%T command 0x%02x returned error %d]\n", command, + ret); + + if (resp_len > (insize + sizeof(rs))) { + CPRINTF("[%T response size is too large %d > %d]\n", + resp_len, insize + sizeof(rs)); + return -EC_RES_RESPONSE_TOO_BIG; + } + + /* Receive remaining data */ + i2c_lock(I2C_PORT_PD_MCU, 1); + ret = i2c_xfer(I2C_PORT_PD_MCU, CONFIG_USB_PD_I2C_SLAVE_ADDR, 0, 0, + &resp_buf[2], resp_len, I2C_XFER_STOP); + i2c_lock(I2C_PORT_PD_MCU, 0); + if (ret) { + CPRINTF("[%T i2c transaction 2 failed: %d]\n", ret); + return -ret; + } + + /* Read back response header and start checksum */ + sum = 0; + for (i = 0, d = (uint8_t *)&rs; i < sizeof(rs); i++, d++) { + *d = resp_buf[i + 2]; + sum += *d; + } + + if (rs.struct_version != EC_HOST_RESPONSE_VERSION) { + CPRINTF("[%T PD response version mismatch]\n"); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs.reserved) { + CPRINTF("[%T PD response reserved != 0]\n"); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs.data_len > insize) { + CPRINTF("[%T PD returned too much data]\n"); + return -EC_RES_RESPONSE_TOO_BIG; + } + + /* Read back data and update checksum */ + resp_len -= sizeof(rs); + for (i = 0, d = (uint8_t *)indata; i < resp_len; i++, d++) { + *d = resp_buf[sizeof(rs) + i + 2]; + sum += *d; + } + + + if ((uint8_t)sum) { + CPRINTF("[%T command 0x%02x bad checksum returned: " + "%d]\n", command, sum); + return -EC_RES_ERROR; + } + + /* Return output buffer size */ + return resp_len; +} + +static int command_pd_mcu(int argc, char **argv) +{ + char *e; + static char outbuf[128]; + static char inbuf[128]; + int command, version; + int i, ret, tmp; + + if (argc < 3) + return EC_ERROR_PARAM_COUNT; + + command = strtoi(argv[1], &e, 0); + if (*e) + return EC_ERROR_PARAM1; + + version = strtoi(argv[2], &e, 0); + if (*e) + return EC_ERROR_PARAM2; + + for (i = 3; i < argc; i++) { + tmp = strtoi(argv[i], &e, 0); + if (*e) + return EC_ERROR_PARAM3; + outbuf[i-3] = tmp; + } + + ret = pd_host_command(command, version, &outbuf, argc - 3, &inbuf, + sizeof(inbuf)); + + ccprintf("Host command 0x%02x, returned %d\n", command, ret); + for (i = 0; i < ret; i++) + ccprintf("0x%02x\n", inbuf[i]); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(pdcmd, command_pd_mcu, + "cmd ver [params]", + "Send PD host command", + NULL); + |