blob: e54ad7ef593c7bc5c6adbac74f719f2e241f5a8f [file] [log] [blame]
Angel Pons210a0082020-04-02 23:48:24 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Hung-Te Lin23fb9972013-06-21 20:11:47 +08002
Hung-Te Lin23fb9972013-06-21 20:11:47 +08003#include <console/console.h>
Hung-Te Lin23fb9972013-06-21 20:11:47 +08004#include <stdint.h>
5#include <string.h>
Elyes HAOUASbf0970e2019-03-21 11:10:03 +01006
Hung-Te Lin23fb9972013-06-21 20:11:47 +08007#include "ec.h"
8#include "ec_commands.h"
9#include "ec_message.h"
10
Hung-Te Line946f982013-06-22 11:18:39 +080011/* Common utilities */
Elyes HAOUASc92f5f22018-07-08 12:36:18 +020012void *__weak crosec_get_buffer(size_t size, int req)
Aaron Durbin82827272014-08-06 14:34:57 -050013{
14 printk(BIOS_DEBUG, "crosec_get_buffer() implementation required.\n");
15 return NULL;
16}
Hung-Te Line946f982013-06-22 11:18:39 +080017
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 */
25static 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 */
44static 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
55struct ec_command_v3 {
56 struct ec_host_request header;
57 uint8_t data[MSG_BYTES];
58};
59
60struct 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 */
72static 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 */
113static 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 */
139static 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
193static 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;
Aaron Durbin82827272014-08-06 14:34:57 -0500198 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;
Hung-Te Line946f982013-06-22 11:18:39 +0800205
206 /* Create request packet */
Aaron Durbin82827272014-08-06 14:34:57 -0500207 out_bytes = create_proto3_request(cec_command, cmd);
Hung-Te Line946f982013-06-22 11:18:39 +0800208 if (out_bytes < 0) {
209 return out_bytes;
210 }
211
212 /* Prepare response buffer */
Aaron Durbin82827272014-08-06 14:34:57 -0500213 in_bytes = prepare_proto3_response_buffer(cec_command, resp);
Hung-Te Line946f982013-06-22 11:18:39 +0800214 if (in_bytes < 0) {
215 return in_bytes;
216 }
217
Aaron Durbin82827272014-08-06 14:34:57 -0500218 rv = crosec_io(out_bytes, in_bytes, context);
Hung-Te Line946f982013-06-22 11:18:39 +0800219 if (rv != 0) {
Julius Werner02e847b2015-02-20 13:36:11 -0800220 printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.\n",
Hung-Te Line946f982013-06-22 11:18:39 +0800221 __func__, rv >= 0 ? rv : -rv);
222 return -EC_RES_ERROR;
223 }
224
225 /* Process the response */
Aaron Durbin82827272014-08-06 14:34:57 -0500226 return handle_proto3_response(resp, cec_command);
Hung-Te Line946f982013-06-22 11:18:39 +0800227}
228
229static 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
Hung-Te Lin23fb9972013-06-21 20:11:47 +0800240int crosec_command_proto(struct chromeec_command *cec_command,
241 crosec_io_t crosec_io, void *context)
242{
Hung-Te Line946f982013-06-22 11:18:39 +0800243 // TODO(hungte) Detect and fallback to v2 if we need.
244 return crosec_command_proto_v3(cec_command, crosec_io, context);
Hung-Te Lin23fb9972013-06-21 20:11:47 +0800245}