summaryrefslogtreecommitdiff
path: root/common/host_command_controller.c
diff options
context:
space:
mode:
authorDiana Z <dzigterman@chromium.org>2020-11-18 16:44:58 -0700
committerCommit Bot <commit-bot@chromium.org>2020-11-30 16:52:08 +0000
commit529312de85aa8ec53dc3a4fcf1f6249b8195a1cb (patch)
tree30ae0b7b4108f8ecbf4de50f3d413628dc702798 /common/host_command_controller.c
parenta0850cc42ee1b56186440fb03f563fa5f7000710 (diff)
downloadchrome-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.c230
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");
+