/** * Handle a protocol version 3 response packet. * * The packet must already be stored in the response buffer. * * @param resp Response buffer. * @param cec_command Command structure to receive valid response. * @return number of bytes of response data, or <0 if error */ static int handle_proto3_response(struct ec_response_v3 *resp, struct chromeec_command *cec_command) { struct ec_host_response *rs = &resp->header; int in_bytes; int csum; cros_ec_dump_data("in-header", -1, (const uint8_t*)rs, sizeof(*rs)); /* Check input data */ if (rs->struct_version != EC_HOST_RESPONSE_VERSION) { printk(BIOS_ERR, "%s: EC response version mismatch\n", __func__); return -EC_RES_INVALID_RESPONSE; } if (rs->reserved) { printk(BIOS_ERR, "%s: EC response reserved != 0\n", __func__); return -EC_RES_INVALID_RESPONSE; } if (rs->data_len > sizeof(resp->data) || rs->data_len > cec_command->cmd_size_out) { printk(BIOS_ERR, "%s: EC returned too much data\n", __func__); return -EC_RES_RESPONSE_TOO_BIG; } cros_ec_dump_data("in-data", -1, resp->data, rs->data_len); /* Update in_bytes to actual data size */ in_bytes = sizeof(*rs) + rs->data_len; /* Verify checksum */ csum = cros_ec_calc_checksum((const uint8_t *)resp, in_bytes); if (csum) { printk(BIOS_ERR, "%s: EC response checksum invalid: 0x%02x\n", __func__, csum); return -EC_RES_INVALID_CHECKSUM; } /* Return raw response. */ cec_command->cmd_code = rs->result; cec_command->cmd_size_out = rs->data_len; memcpy(cec_command->cmd_data_out, resp->data, rs->data_len); /* Return error result, if any */ if (rs->result) { printk(BIOS_ERR, "%s: EC response with error code: %d\n", __func__, rs->result); return -(int)rs->result; } return rs->data_len; }
/** * Create a request packet for protocol version 3. * * @param cec_command Command description. * @param cmd Packed command bit stream. * @return packet size in bytes, or <0 if error. */ static int create_proto3_request(const struct chromeec_command *cec_command, struct ec_command_v3 *cmd) { struct ec_host_request *rq = &cmd->header; int out_bytes = cec_command->cmd_size_in + sizeof(*rq); /* Fail if output size is too big */ if (out_bytes > sizeof(*cmd)) { printk(BIOS_ERR, "%s: Cannot send %d bytes\n", __func__, cec_command->cmd_size_in); return -EC_RES_REQUEST_TRUNCATED; } /* Fill in request packet */ rq->struct_version = EC_HOST_REQUEST_VERSION; rq->checksum = 0; rq->command = cec_command->cmd_code; rq->command_version = cec_command->cmd_version; rq->reserved = 0; rq->data_len = cec_command->cmd_size_in; /* Copy data after header */ memcpy(cmd->data, cec_command->cmd_data_in, cec_command->cmd_size_in); /* Write checksum field so the entire packet sums to 0 */ rq->checksum = (uint8_t)(-cros_ec_calc_checksum( (const uint8_t*)cmd, out_bytes)); cros_ec_dump_data("out", rq->command, (const uint8_t *)cmd, out_bytes); /* Return size of request packet */ return out_bytes; }
/** * Create a request packet for protocol version 3. * * The packet is stored in the device's internal output buffer. * * @param dev CROS-EC device * @param cmd Command to send (EC_CMD_...) * @param cmd_version Version of command to send (EC_VER_...) * @param dout Output data (may be NULL If dout_len=0) * @param dout_len Size of output data in bytes * @return packet size in bytes, or <0 if error. */ static int create_proto3_request(struct cros_ec_dev *dev, int cmd, int cmd_version, const void *dout, int dout_len) { struct ec_host_request *rq = (struct ec_host_request *)dev->dout; int out_bytes = dout_len + sizeof(*rq); /* Fail if output size is too big */ if (out_bytes > (int)sizeof(dev->dout)) { debug("%s: Cannot send %d bytes\n", __func__, dout_len); return -EC_RES_REQUEST_TRUNCATED; } /* Fill in request packet */ rq->struct_version = EC_HOST_REQUEST_VERSION; rq->checksum = 0; rq->command = cmd; rq->command_version = cmd_version; rq->reserved = 0; rq->data_len = dout_len; /* Copy data after header */ memcpy(rq + 1, dout, dout_len); /* Write checksum field so the entire packet sums to 0 */ rq->checksum = (uint8_t)(-cros_ec_calc_checksum(dev->dout, out_bytes)); cros_ec_dump_data("out", cmd, dev->dout, out_bytes); /* Return size of request packet */ return out_bytes; }
/** * Handle a protocol version 3 response packet. * * The packet must already be stored in the device's internal input buffer. * * @param dev CROS-EC device * @param dinp Returns pointer to response data * @param din_len Maximum size of response in bytes * @return number of bytes of response data, or <0 if error. Note that error * codes can be from errno.h or -ve EC_RES_INVALID_CHECKSUM values (and they * overlap!) */ static int handle_proto3_response(struct cros_ec_dev *dev, uint8_t **dinp, int din_len) { struct ec_host_response *rs = (struct ec_host_response *)dev->din; int in_bytes; int csum; cros_ec_dump_data("in-header", -1, dev->din, sizeof(*rs)); /* Check input data */ if (rs->struct_version != EC_HOST_RESPONSE_VERSION) { debug("%s: EC response version mismatch\n", __func__); return -EC_RES_INVALID_RESPONSE; } if (rs->reserved) { debug("%s: EC response reserved != 0\n", __func__); return -EC_RES_INVALID_RESPONSE; } if (rs->data_len > din_len) { debug("%s: EC returned too much data\n", __func__); return -EC_RES_RESPONSE_TOO_BIG; } cros_ec_dump_data("in-data", -1, dev->din + sizeof(*rs), rs->data_len); /* Update in_bytes to actual data size */ in_bytes = sizeof(*rs) + rs->data_len; /* Verify checksum */ csum = cros_ec_calc_checksum(dev->din, in_bytes); if (csum) { debug("%s: EC response checksum invalid: 0x%02x\n", __func__, csum); return -EC_RES_INVALID_CHECKSUM; } /* Return error result, if any */ if (rs->result) return -(int)rs->result; /* If we're still here, set response data pointer and return length */ *dinp = (uint8_t *)(rs + 1); return rs->data_len; }
/** * Send a command to a LPC CROS_EC device and return the reply. * * The device's internal input/output buffers are used. * * @param dev CROS_EC device * @param cmd Command to send (EC_CMD_...) * @param cmd_version Version of command to send (EC_VER_...) * @param dout Output data (may be NULL If dout_len=0) * @param dout_len Size of output data in bytes * @param dinp Returns pointer to response data. This will be * untouched unless we return a value > 0. * @param din_len Maximum size of response in bytes * @return number of bytes in response, or -1 on error */ int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const uint8_t *dout, int dout_len, uint8_t **dinp, int din_len) { int in_bytes = din_len + 4; /* status, length, checksum, trailer */ uint8_t *out; uint8_t *p; int csum, len; int rv; if (dev->protocol_version != 2) { debug("%s: Unsupported EC protcol version %d\n", __func__, dev->protocol_version); return -1; } /* * Sanity-check input size to make sure it plus transaction overhead * fits in the internal device buffer. */ if (in_bytes > sizeof(dev->din)) { debug("%s: Cannot receive %d bytes\n", __func__, din_len); return -1; } /* We represent message length as a byte */ if (dout_len > 0xff) { debug("%s: Cannot send %d bytes\n", __func__, dout_len); return -1; } /* * Clear input buffer so we don't get false hits for MSG_HEADER */ memset(dev->din, '\0', in_bytes); if (spi_claim_bus(dev->spi)) { debug("%s: Cannot claim SPI bus\n", __func__); return -1; } out = dev->dout; out[0] = EC_CMD_VERSION0 + cmd_version; out[1] = cmd; out[2] = (uint8_t)dout_len; memcpy(out + 3, dout, dout_len); csum = cros_ec_calc_checksum(out, 3) + cros_ec_calc_checksum(dout, dout_len); out[3 + dout_len] = (uint8_t)csum; /* * Send output data and receive input data starting such that the * message body will be dword aligned. */ p = dev->din + sizeof(int64_t) - 2; len = dout_len + 4; cros_ec_dump_data("out", cmd, out, len); rv = spi_xfer(dev->spi, max(len, in_bytes) * 8, out, p, SPI_XFER_BEGIN | SPI_XFER_END); spi_release_bus(dev->spi); if (rv) { debug("%s: Cannot complete SPI transfer\n", __func__); return -1; } len = min(p[1], din_len); cros_ec_dump_data("in", -1, p, len + 3); /* Response code is first byte of message */ if (p[0] != EC_RES_SUCCESS) { printf("%s: Returned status %d\n", __func__, p[0]); return -(int)(p[0]); } /* Check checksum */ csum = cros_ec_calc_checksum(p, len + 2); if (csum != p[len + 2]) { debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__, p[2 + len], csum); return -1; } /* Anything else is the response data */ *dinp = p + 2; return len; }
int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, const uint8_t *dout, int dout_len, uint8_t **dinp, int din_len) { int old_bus = 0; /* version8, cmd8, arglen8, out8[dout_len], csum8 */ int out_bytes = dout_len + 4; /* response8, arglen8, in8[din_len], checksum8 */ int in_bytes = din_len + 3; uint8_t *ptr; /* Receive input data, so that args will be dword aligned */ uint8_t *in_ptr; int ret; old_bus = i2c_get_bus_num(); /* * Sanity-check I/O sizes given transaction overhead in internal * buffers. */ if (out_bytes > sizeof(dev->dout)) { debug("%s: Cannot send %d bytes\n", __func__, dout_len); return -1; } if (in_bytes > sizeof(dev->din)) { debug("%s: Cannot receive %d bytes\n", __func__, din_len); return -1; } assert(dout_len >= 0); assert(dinp); /* * Copy command and data into output buffer so we can do a single I2C * burst transaction. */ ptr = dev->dout; /* * in_ptr starts of pointing to a dword-aligned input data buffer. * We decrement it back by the number of header bytes we expect to * receive, so that the first parameter of the resulting input data * will be dword aligned. */ in_ptr = dev->din + sizeof(int64_t); if (!dev->cmd_version_is_supported) { /* Send an old-style command */ *ptr++ = cmd; out_bytes = dout_len + 1; in_bytes = din_len + 2; in_ptr--; /* Expect just a status byte */ } else { *ptr++ = EC_CMD_VERSION0 + cmd_version; *ptr++ = cmd; *ptr++ = dout_len; in_ptr -= 2; /* Expect status, length bytes */ } memcpy(ptr, dout, dout_len); ptr += dout_len; if (dev->cmd_version_is_supported) *ptr++ = (uint8_t) cros_ec_calc_checksum(dev->dout, dout_len + 3); /* Set to the proper i2c bus */ if (i2c_set_bus_num(dev->bus_num)) { debug("%s: Cannot change to I2C bus %d\n", __func__, dev->bus_num); return -1; } /* Send output data */ cros_ec_dump_data("out", -1, dev->dout, out_bytes); ret = i2c_write(dev->addr, 0, 0, dev->dout, out_bytes); if (ret) { debug("%s: Cannot complete I2C write to 0x%x\n", __func__, dev->addr); ret = -1; } if (!ret) { ret = i2c_read(dev->addr, 0, 0, in_ptr, in_bytes); if (ret) { debug("%s: Cannot complete I2C read from 0x%x\n", __func__, dev->addr); ret = -1; } } /* Return to original bus number */ i2c_set_bus_num(old_bus); if (ret) return ret; if (*in_ptr != EC_RES_SUCCESS) { debug("%s: Received bad result code %d\n", __func__, *in_ptr); return -(int)*in_ptr; } if (dev->cmd_version_is_supported) { int len, csum; len = in_ptr[1]; if (len + 3 > sizeof(dev->din)) { debug("%s: Received length %#02x too large\n", __func__, len); return -1; } csum = cros_ec_calc_checksum(in_ptr, 2 + len); if (csum != in_ptr[2 + len]) { debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__, in_ptr[2 + din_len], csum); return -1; } din_len = min(din_len, len); cros_ec_dump_data("in", -1, in_ptr, din_len + 3); } else { cros_ec_dump_data("in (old)", -1, in_ptr, in_bytes); } /* Return pointer to dword-aligned input data, if any */ *dinp = dev->din + sizeof(int64_t); return din_len; }
static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd, int cmd_version, const uint8_t *dout, int dout_len, uint8_t **dinp, int din_len) { struct cros_ec_dev *dev = dev_get_uclass_priv(udev); /* version8, cmd8, arglen8, out8[dout_len], csum8 */ int out_bytes = dout_len + 4; /* response8, arglen8, in8[din_len], checksum8 */ int in_bytes = din_len + 3; uint8_t *ptr; /* Receive input data, so that args will be dword aligned */ uint8_t *in_ptr; int len, csum, ret; /* * Sanity-check I/O sizes given transaction overhead in internal * buffers. */ if (out_bytes > sizeof(dev->dout)) { debug("%s: Cannot send %d bytes\n", __func__, dout_len); return -1; } if (in_bytes > sizeof(dev->din)) { debug("%s: Cannot receive %d bytes\n", __func__, din_len); return -1; } assert(dout_len >= 0); assert(dinp); /* * Copy command and data into output buffer so we can do a single I2C * burst transaction. */ ptr = dev->dout; /* * in_ptr starts of pointing to a dword-aligned input data buffer. * We decrement it back by the number of header bytes we expect to * receive, so that the first parameter of the resulting input data * will be dword aligned. */ in_ptr = dev->din + sizeof(int64_t); if (dev->protocol_version != 2) { /* Something we don't support */ debug("%s: Protocol version %d unsupported\n", __func__, dev->protocol_version); return -1; } *ptr++ = EC_CMD_VERSION0 + cmd_version; *ptr++ = cmd; *ptr++ = dout_len; in_ptr -= 2; /* Expect status, length bytes */ memcpy(ptr, dout, dout_len); ptr += dout_len; *ptr++ = (uint8_t) cros_ec_calc_checksum(dev->dout, dout_len + 3); /* Send output data */ cros_ec_dump_data("out", -1, dev->dout, out_bytes); ret = dm_i2c_write(udev, 0, dev->dout, out_bytes); if (ret) { debug("%s: Cannot complete I2C write to %s\n", __func__, udev->name); ret = -1; } if (!ret) { ret = dm_i2c_read(udev, 0, in_ptr, in_bytes); if (ret) { debug("%s: Cannot complete I2C read from %s\n", __func__, udev->name); ret = -1; } } if (*in_ptr != EC_RES_SUCCESS) { debug("%s: Received bad result code %d\n", __func__, *in_ptr); return -(int)*in_ptr; } len = in_ptr[1]; if (len + 3 > sizeof(dev->din)) { debug("%s: Received length %#02x too large\n", __func__, len); return -1; } csum = cros_ec_calc_checksum(in_ptr, 2 + len); if (csum != in_ptr[2 + len]) { debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__, in_ptr[2 + din_len], csum); return -1; } din_len = min(din_len, len); cros_ec_dump_data("in", -1, in_ptr, din_len + 3); /* Return pointer to dword-aligned input data, if any */ *dinp = dev->din + sizeof(int64_t); return din_len; }