summaryrefslogtreecommitdiff
path: root/common/host_command_master.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/host_command_master.c')
-rw-r--r--common/host_command_master.c185
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);
+