Пример #1
0
/**
 * 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;
}
Пример #2
0
/**
 * 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;
}
Пример #3
0
/**
 * 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;
}
Пример #4
0
/**
 * 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;
}
Пример #5
0
/**
 * 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;
}
Пример #6
0
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;
}
Пример #7
0
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;
}