• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <stdint.h>
5 #include <string.h>
6 
7 #include "ec.h"
8 #include "ec_commands.h"
9 #include "ec_message.h"
10 
11 /* Common utilities */
crosec_get_buffer(size_t size,int req)12 void *__weak crosec_get_buffer(size_t size, int req)
13 {
14 	printk(BIOS_DEBUG, "crosec_get_buffer() implementation required.\n");
15 	return NULL;
16 }
17 
18 /* Dumps EC command / response data into debug output.
19  *
20  * @param name	Message prefix name.
21  * @param cmd	Command code, or -1 to ignore cmd message.
22  * @param data	Data buffer to print.
23  * @param len	Length of data.
24  */
cros_ec_dump_data(const char * name,int cmd,const uint8_t * data,int len)25 static void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data,
26 			      int len)
27 {
28 	int i;
29 
30 	printk(BIOS_DEBUG, "%s: ", name);
31 	if (cmd != -1)
32 		printk(BIOS_DEBUG, "cmd=%#x: ", cmd);
33 	for (i = 0; i < len; i++)
34 		printk(BIOS_DEBUG, "%02x ", data[i]);
35 	printk(BIOS_DEBUG, "\n");
36 }
37 
38 /* Calculate a simple 8-bit checksum of a data block
39  *
40  * @param data	Data block to checksum
41  * @param size	Size of data block in bytes
42  * @return checksum value (0 to 255)
43  */
cros_ec_calc_checksum(const uint8_t * data,int size)44 static int cros_ec_calc_checksum(const uint8_t *data, int size)
45 {
46 	int csum, i;
47 
48 	for (i = csum = 0; i < size; i++)
49 		csum += data[i];
50 	return csum & 0xff;
51 }
52 
53 /* Standard Chrome EC protocol, version 3 */
54 
55 struct ec_command_v3 {
56 	struct ec_host_request header;
57 	uint8_t data[MSG_BYTES];
58 };
59 
60 struct ec_response_v3 {
61 	struct ec_host_response header;
62 	uint8_t data[MSG_BYTES];
63 };
64 
65 /**
66  * Create a request packet for protocol version 3.
67  *
68  * @param cec_command	Command description.
69  * @param cmd		Packed command bit stream.
70  * @return packet size in bytes, or <0 if error.
71  */
create_proto3_request(const struct chromeec_command * cec_command,struct ec_command_v3 * cmd)72 static int create_proto3_request(const struct chromeec_command *cec_command,
73 				 struct ec_command_v3 *cmd)
74 {
75 	struct ec_host_request *rq = &cmd->header;
76 	int out_bytes = cec_command->cmd_size_in + sizeof(*rq);
77 
78 	/* Fail if output size is too big */
79 	if (out_bytes > sizeof(*cmd)) {
80 		printk(BIOS_ERR, "%s: Cannot send %d bytes\n", __func__,
81 		       cec_command->cmd_size_in);
82 		return -EC_RES_REQUEST_TRUNCATED;
83 	}
84 
85 	/* Fill in request packet */
86 	rq->struct_version = EC_HOST_REQUEST_VERSION;
87 	rq->checksum = 0;
88 	rq->command = cec_command->cmd_code;
89 	rq->command_version = cec_command->cmd_version;
90 	rq->reserved = 0;
91 	rq->data_len = cec_command->cmd_size_in;
92 
93 	/* Copy data after header */
94 	memcpy(cmd->data, cec_command->cmd_data_in, cec_command->cmd_size_in);
95 
96 	/* Write checksum field so the entire packet sums to 0 */
97 	rq->checksum = (uint8_t)(-cros_ec_calc_checksum(
98 			(const uint8_t*)cmd, out_bytes));
99 
100 	cros_ec_dump_data("out", rq->command, (const uint8_t *)cmd, out_bytes);
101 
102 	/* Return size of request packet */
103 	return out_bytes;
104 }
105 
106 /**
107  * Prepare the device to receive a protocol version 3 response.
108  *
109  * @param cec_command	Command description.
110  * @param resp		Response buffer.
111  * @return maximum expected number of bytes in response, or <0 if error.
112  */
prepare_proto3_response_buffer(const struct chromeec_command * cec_command,struct ec_response_v3 * resp)113 static int prepare_proto3_response_buffer(
114 		const struct chromeec_command *cec_command,
115 		struct ec_response_v3 *resp)
116 {
117 	int in_bytes = cec_command->cmd_size_out + sizeof(resp->header);
118 
119 	/* Fail if input size is too big */
120 	if (in_bytes > sizeof(*resp)) {
121 		printk(BIOS_ERR, "%s: Cannot receive %d bytes\n", __func__,
122 		       cec_command->cmd_size_out);
123 		return -EC_RES_RESPONSE_TOO_BIG;
124 	}
125 
126 	/* Return expected size of response packet */
127 	return in_bytes;
128 }
129 
130 /**
131  * Handle a protocol version 3 response packet.
132  *
133  * The packet must already be stored in the response buffer.
134  *
135  * @param resp		Response buffer.
136  * @param cec_command	Command structure to receive valid response.
137  * @return number of bytes of response data, or <0 if error
138  */
handle_proto3_response(struct ec_response_v3 * resp,struct chromeec_command * cec_command)139 static int handle_proto3_response(struct ec_response_v3 *resp,
140 				  struct chromeec_command *cec_command)
141 {
142 	struct ec_host_response *rs = &resp->header;
143 	int in_bytes;
144 	int csum;
145 
146 	cros_ec_dump_data("in-header", -1, (const uint8_t*)rs, sizeof(*rs));
147 
148 	/* Check input data */
149 	if (rs->struct_version != EC_HOST_RESPONSE_VERSION) {
150 		printk(BIOS_ERR, "%s: EC response version mismatch\n", __func__);
151 		return -EC_RES_INVALID_RESPONSE;
152 	}
153 
154 	if (rs->reserved) {
155 		printk(BIOS_ERR, "%s: EC response reserved != 0\n", __func__);
156 		return -EC_RES_INVALID_RESPONSE;
157 	}
158 
159 	if (rs->data_len > sizeof(resp->data) ||
160 	    rs->data_len > cec_command->cmd_size_out) {
161 		printk(BIOS_ERR, "%s: EC returned too much data\n", __func__);
162 		return -EC_RES_RESPONSE_TOO_BIG;
163 	}
164 
165 	cros_ec_dump_data("in-data", -1, resp->data, rs->data_len);
166 
167 	/* Update in_bytes to actual data size */
168 	in_bytes = sizeof(*rs) + rs->data_len;
169 
170 	/* Verify checksum */
171 	csum = cros_ec_calc_checksum((const uint8_t *)resp, in_bytes);
172 	if (csum) {
173 		printk(BIOS_ERR, "%s: EC response checksum invalid: 0x%02x\n",
174 		       __func__, csum);
175 		return -EC_RES_INVALID_CHECKSUM;
176 	}
177 
178 	/* Return raw response. */
179 	cec_command->cmd_code = rs->result;
180 	cec_command->cmd_size_out = rs->data_len;
181 	memcpy(cec_command->cmd_data_out, resp->data, rs->data_len);
182 
183 	/* Return error result, if any */
184 	if (rs->result) {
185 		printk(BIOS_ERR, "%s: EC response with error code: %d\n",
186 		       __func__, rs->result);
187 		return -(int)rs->result;
188 	}
189 
190 	return rs->data_len;
191 }
192 
send_command_proto3(struct chromeec_command * cec_command,crosec_io_t crosec_io,void * context)193 static int send_command_proto3(struct chromeec_command *cec_command,
194 			       crosec_io_t crosec_io, void *context)
195 {
196 	int out_bytes, in_bytes;
197 	int rv;
198 	struct ec_command_v3 *cmd;
199 	struct ec_response_v3 *resp;
200 
201 	if ((cmd = crosec_get_buffer(sizeof(*cmd), 1)) == NULL)
202 		return -EC_RES_ERROR;
203 	if ((resp = crosec_get_buffer(sizeof(*resp), 0)) == NULL)
204 		return -EC_RES_ERROR;
205 
206 	/* Create request packet */
207 	out_bytes = create_proto3_request(cec_command, cmd);
208 	if (out_bytes < 0) {
209 		return out_bytes;
210 	}
211 
212 	/* Prepare response buffer */
213 	in_bytes = prepare_proto3_response_buffer(cec_command, resp);
214 	if (in_bytes < 0) {
215 		return in_bytes;
216 	}
217 
218 	rv = crosec_io(out_bytes, in_bytes, context);
219 	if (rv != 0) {
220 		printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.\n",
221 		       __func__, rv >= 0 ? rv : -rv);
222 		return -EC_RES_ERROR;
223 	}
224 
225 	/* Process the response */
226 	return handle_proto3_response(resp, cec_command);
227 }
228 
crosec_command_proto_v3(struct chromeec_command * cec_command,crosec_io_t crosec_io,void * context)229 static int crosec_command_proto_v3(struct chromeec_command *cec_command,
230 				   crosec_io_t crosec_io, void *context)
231 {
232 	int rv = send_command_proto3(cec_command, crosec_io, context);
233 	if (rv < 0) {
234 		cec_command->cmd_code = rv;
235 		return 1;
236 	}
237 	return 0;
238 }
239 
crosec_command_proto(struct chromeec_command * cec_command,crosec_io_t crosec_io,void * context)240 int crosec_command_proto(struct chromeec_command *cec_command,
241 			 crosec_io_t crosec_io, void *context)
242 {
243 	// TODO(hungte) Detect and fallback to v2 if we need.
244 	return crosec_command_proto_v3(cec_command, crosec_io, context);
245 }
246