Remove address from GPLv2 headers
[coreboot.git] / src / ec / google / chromeec / crosec_proto.c
blob9da80c754560e83217524cea7266b943b6a3cbda
1 /*
2 * This file is part of the coreboot project.
4 * Copyright (C) 2013 Google Inc. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc.
20 #include <arch/io.h>
21 #include <console/console.h>
22 #include <delay.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include "ec.h"
26 #include "ec_commands.h"
27 #include "ec_message.h"
29 /* Common utilities */
30 void * __attribute__((weak)) crosec_get_buffer(size_t size, int req)
32 printk(BIOS_DEBUG, "crosec_get_buffer() implementation required.\n");
33 return NULL;
36 /* Dumps EC command / response data into debug output.
38 * @param name Message prefix name.
39 * @param cmd Command code, or -1 to ignore cmd message.
40 * @param data Data buffer to print.
41 * @param len Length of data.
43 static void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data,
44 int len)
46 int i;
48 printk(BIOS_DEBUG, "%s: ", name);
49 if (cmd != -1)
50 printk(BIOS_DEBUG, "cmd=%#x: ", cmd);
51 for (i = 0; i < len; i++)
52 printk(BIOS_DEBUG, "%02x ", data[i]);
53 printk(BIOS_DEBUG, "\n");
56 /* Calculate a simple 8-bit checksum of a data block
58 * @param data Data block to checksum
59 * @param size Size of data block in bytes
60 * @return checksum value (0 to 255)
62 static int cros_ec_calc_checksum(const uint8_t *data, int size)
64 int csum, i;
66 for (i = csum = 0; i < size; i++)
67 csum += data[i];
68 return csum & 0xff;
71 /* Standard Chrome EC protocol, version 3 */
73 struct ec_command_v3 {
74 struct ec_host_request header;
75 uint8_t data[MSG_BYTES];
78 struct ec_response_v3 {
79 struct ec_host_response header;
80 uint8_t data[MSG_BYTES];
83 /**
84 * Create a request packet for protocol version 3.
86 * @param cec_command Command description.
87 * @param cmd Packed command bit stream.
88 * @return packet size in bytes, or <0 if error.
90 static int create_proto3_request(const struct chromeec_command *cec_command,
91 struct ec_command_v3 *cmd)
93 struct ec_host_request *rq = &cmd->header;
94 int out_bytes = cec_command->cmd_size_in + sizeof(*rq);
96 /* Fail if output size is too big */
97 if (out_bytes > sizeof(*cmd)) {
98 printk(BIOS_ERR, "%s: Cannot send %d bytes\n", __func__,
99 cec_command->cmd_size_in);
100 return -EC_RES_REQUEST_TRUNCATED;
103 /* Fill in request packet */
104 rq->struct_version = EC_HOST_REQUEST_VERSION;
105 rq->checksum = 0;
106 rq->command = cec_command->cmd_code;
107 rq->command_version = cec_command->cmd_version;
108 rq->reserved = 0;
109 rq->data_len = cec_command->cmd_size_in;
111 /* Copy data after header */
112 memcpy(cmd->data, cec_command->cmd_data_in, cec_command->cmd_size_in);
114 /* Write checksum field so the entire packet sums to 0 */
115 rq->checksum = (uint8_t)(-cros_ec_calc_checksum(
116 (const uint8_t*)cmd, out_bytes));
118 cros_ec_dump_data("out", rq->command, (const uint8_t *)cmd, out_bytes);
120 /* Return size of request packet */
121 return out_bytes;
125 * Prepare the device to receive a protocol version 3 response.
127 * @param cec_command Command description.
128 * @param resp Response buffer.
129 * @return maximum expected number of bytes in response, or <0 if error.
131 static int prepare_proto3_response_buffer(
132 const struct chromeec_command *cec_command,
133 struct ec_response_v3 *resp)
135 int in_bytes = cec_command->cmd_size_out + sizeof(resp->header);
137 /* Fail if input size is too big */
138 if (in_bytes > sizeof(*resp)) {
139 printk(BIOS_ERR, "%s: Cannot receive %d bytes\n", __func__,
140 cec_command->cmd_size_out);
141 return -EC_RES_RESPONSE_TOO_BIG;
144 /* Return expected size of response packet */
145 return in_bytes;
149 * Handle a protocol version 3 response packet.
151 * The packet must already be stored in the response buffer.
153 * @param resp Response buffer.
154 * @param cec_command Command structure to receive valid response.
155 * @return number of bytes of response data, or <0 if error
157 static int handle_proto3_response(struct ec_response_v3 *resp,
158 struct chromeec_command *cec_command)
160 struct ec_host_response *rs = &resp->header;
161 int in_bytes;
162 int csum;
164 cros_ec_dump_data("in-header", -1, (const uint8_t*)rs, sizeof(*rs));
166 /* Check input data */
167 if (rs->struct_version != EC_HOST_RESPONSE_VERSION) {
168 printk(BIOS_ERR, "%s: EC response version mismatch\n", __func__);
169 return -EC_RES_INVALID_RESPONSE;
172 if (rs->reserved) {
173 printk(BIOS_ERR, "%s: EC response reserved != 0\n", __func__);
174 return -EC_RES_INVALID_RESPONSE;
177 if (rs->data_len > sizeof(resp->data) ||
178 rs->data_len > cec_command->cmd_size_out) {
179 printk(BIOS_ERR, "%s: EC returned too much data\n", __func__);
180 return -EC_RES_RESPONSE_TOO_BIG;
183 cros_ec_dump_data("in-data", -1, resp->data, rs->data_len);
185 /* Update in_bytes to actual data size */
186 in_bytes = sizeof(*rs) + rs->data_len;
188 /* Verify checksum */
189 csum = cros_ec_calc_checksum((const uint8_t *)resp, in_bytes);
190 if (csum) {
191 printk(BIOS_ERR, "%s: EC response checksum invalid: 0x%02x\n",
192 __func__, csum);
193 return -EC_RES_INVALID_CHECKSUM;
196 /* Return raw response. */
197 cec_command->cmd_code = rs->result;
198 cec_command->cmd_size_out = rs->data_len;
199 memcpy(cec_command->cmd_data_out, resp->data, rs->data_len);
201 /* Return error result, if any */
202 if (rs->result) {
203 printk(BIOS_ERR, "%s: EC response with error code: %d\n",
204 __func__, rs->result);
205 return -(int)rs->result;
208 return rs->data_len;
211 static int send_command_proto3(struct chromeec_command *cec_command,
212 crosec_io_t crosec_io, void *context)
214 int out_bytes, in_bytes;
215 int rv;
216 struct ec_command_v3 *cmd;
217 struct ec_response_v3 *resp;
219 if ((cmd = crosec_get_buffer(sizeof(*cmd), 1)) == NULL)
220 return -EC_RES_ERROR;
221 if ((resp = crosec_get_buffer(sizeof(*resp), 0)) == NULL)
222 return -EC_RES_ERROR;
224 /* Create request packet */
225 out_bytes = create_proto3_request(cec_command, cmd);
226 if (out_bytes < 0) {
227 return out_bytes;
230 /* Prepare response buffer */
231 in_bytes = prepare_proto3_response_buffer(cec_command, resp);
232 if (in_bytes < 0) {
233 return in_bytes;
236 rv = crosec_io(out_bytes, in_bytes, context);
237 if (rv != 0) {
238 printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.\n",
239 __func__, rv >= 0 ? rv : -rv);
240 return -EC_RES_ERROR;
243 /* Process the response */
244 return handle_proto3_response(resp, cec_command);
247 static int crosec_command_proto_v3(struct chromeec_command *cec_command,
248 crosec_io_t crosec_io, void *context)
250 int rv = send_command_proto3(cec_command, crosec_io, context);
251 if (rv < 0) {
252 cec_command->cmd_code = rv;
253 return 1;
255 return 0;
258 int crosec_command_proto(struct chromeec_command *cec_command,
259 crosec_io_t crosec_io, void *context)
261 // TODO(hungte) Detect and fallback to v2 if we need.
262 return crosec_command_proto_v3(cec_command, crosec_io, context);