diff options
author | Diana Z <dzigterman@chromium.org> | 2020-11-18 16:44:58 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-11-30 16:52:08 +0000 |
commit | 529312de85aa8ec53dc3a4fcf1f6249b8195a1cb (patch) | |
tree | 30ae0b7b4108f8ecbf4de50f3d413628dc702798 /common/host_command_controller.c | |
parent | a0850cc42ee1b56186440fb03f563fa5f7000710 (diff) | |
download | chrome-ec-529312de85aa8ec53dc3a4fcf1f6249b8195a1cb.tar.gz |
COIL: Rename host_command_controller.c
Rename host_command_controller.c and update build.mk
BRANCH=None
BUG=None
TEST=make -j buildall
Signed-off-by: Diana Z <dzigterman@chromium.org>
Change-Id: Ib5285b2686ceabca9830bbae40941754d3e13a1d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2558903
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Diffstat (limited to 'common/host_command_controller.c')
-rw-r--r-- | common/host_command_controller.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/common/host_command_controller.c b/common/host_command_controller.c new file mode 100644 index 0000000000..0d221e44e3 --- /dev/null +++ b/common/host_command_controller.c @@ -0,0 +1,230 @@ +/* Copyright 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 controller module for Chrome EC */ + +#include "common.h" +#include "console.h" +#include "host_command.h" +#include "i2c.h" +#include "task.h" +#include "timer.h" +#include "usb_pd.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_HOSTCMD, outstr) +#define CPRINTS(format, args...) cprints(CC_HOSTCMD, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_HOSTCMD, format, ## args) + +/* Number of attempts for each PD host command */ +#define PD_HOST_COMMAND_ATTEMPTS 3 + +static struct mutex pd_mutex; + +/** + * Non-task-safe internal version of pd_host_command(). + * + * Do not call this version directly! Use pd_host_command(). + */ +static int pd_host_command_internal(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); + i2c_set_timeout(I2C_PORT_PD_MCU, PD_HOST_COMMAND_TIMEOUT_US); + ret = i2c_xfer_unlocked(I2C_PORT_PD_MCU, + CONFIG_USB_PD_I2C_ADDR_FLAGS, + &req_buf[0], outsize + sizeof(rq) + 1, + &resp_buf[0], 2, I2C_XFER_START); + i2c_set_timeout(I2C_PORT_PD_MCU, 0); + if (ret) { + i2c_lock(I2C_PORT_PD_MCU, 0); + CPRINTS("i2c transaction 1 failed: %d", ret); + return -EC_RES_BUS_ERROR; + } + + resp_len = resp_buf[1]; + + if (resp_len > (insize + sizeof(rs))) { + /* Do a read to generate stop condition */ + i2c_xfer_unlocked(I2C_PORT_PD_MCU, + CONFIG_USB_PD_I2C_ADDR_FLAGS, + 0, 0, &resp_buf[2], 1, I2C_XFER_STOP); + i2c_lock(I2C_PORT_PD_MCU, 0); + CPRINTS("response size is too large %d > %d", + resp_len, insize + sizeof(rs)); + return -EC_RES_RESPONSE_TOO_BIG; + } + + /* Receive remaining data */ + ret = i2c_xfer_unlocked(I2C_PORT_PD_MCU, + CONFIG_USB_PD_I2C_ADDR_FLAGS, + 0, 0, + &resp_buf[2], resp_len, I2C_XFER_STOP); + i2c_lock(I2C_PORT_PD_MCU, 0); + if (ret) { + CPRINTS("i2c transaction 2 failed: %d", ret); + return -EC_RES_BUS_ERROR; + } + + /* Check for host command error code */ + ret = resp_buf[0]; + if (ret) { + CPRINTS("command 0x%02x returned error %d", command, 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) { + CPRINTS("PD response version mismatch"); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs.reserved) { + CPRINTS("PD response reserved != 0"); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs.data_len > insize) { + CPRINTS("PD returned too much data"); + 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) { + CPRINTS("command 0x%02x bad checksum returned: %d", + command, sum); + return -EC_RES_INVALID_CHECKSUM; + } + + /* Return output buffer size */ + return resp_len; +} + +int pd_host_command(int command, int version, + const void *outdata, int outsize, + void *indata, int insize) +{ + int rv; + int tries = 0; + + /* Try multiple times to send host command. */ + for (tries = 0; tries < PD_HOST_COMMAND_ATTEMPTS; tries++) { + /* Acquire mutex */ + mutex_lock(&pd_mutex); + /* Call internal version of host command */ + rv = pd_host_command_internal(command, version, outdata, + outsize, indata, insize); + /* Release mutex */ + mutex_unlock(&pd_mutex); + + /* If host command error due to i2c bus error, try again. */ + if (rv != -EC_RES_BUS_ERROR) + break; + task_wait_event(50*MSEC); + } + + return rv; +} + +static int command_pd_mcu(int argc, char **argv) +{ + char *e; + static char __bss_slow outbuf[128]; + static char __bss_slow 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"); + |