1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/console.h>
8 #include "ec_commands.h"
9 #include "ec_message.h"
11 /* Common utilities */
12 void *__weak
crosec_get_buffer(size_t size
, int req
)
14 printk(BIOS_DEBUG
, "crosec_get_buffer() implementation required.\n");
18 /* Dumps EC command / response data into debug output.
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.
25 static void cros_ec_dump_data(const char *name
, int cmd
, const uint8_t *data
,
30 printk(BIOS_DEBUG
, "%s: ", name
);
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");
38 /* Calculate a simple 8-bit checksum of a data block
40 * @param data Data block to checksum
41 * @param size Size of data block in bytes
42 * @return checksum value (0 to 255)
44 static int cros_ec_calc_checksum(const uint8_t *data
, int size
)
48 for (i
= csum
= 0; i
< size
; i
++)
53 /* Standard Chrome EC protocol, version 3 */
55 struct ec_command_v3
{
56 struct ec_host_request header
;
57 uint8_t data
[MSG_BYTES
];
60 struct ec_response_v3
{
61 struct ec_host_response header
;
62 uint8_t data
[MSG_BYTES
];
66 * Create a request packet for protocol version 3.
68 * @param cec_command Command description.
69 * @param cmd Packed command bit stream.
70 * @return packet size in bytes, or <0 if error.
72 static int create_proto3_request(const struct chromeec_command
*cec_command
,
73 struct ec_command_v3
*cmd
)
75 struct ec_host_request
*rq
= &cmd
->header
;
76 int out_bytes
= cec_command
->cmd_size_in
+ sizeof(*rq
);
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
;
85 /* Fill in request packet */
86 rq
->struct_version
= EC_HOST_REQUEST_VERSION
;
88 rq
->command
= cec_command
->cmd_code
;
89 rq
->command_version
= cec_command
->cmd_version
;
91 rq
->data_len
= cec_command
->cmd_size_in
;
93 /* Copy data after header */
94 memcpy(cmd
->data
, cec_command
->cmd_data_in
, cec_command
->cmd_size_in
);
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
));
100 cros_ec_dump_data("out", rq
->command
, (const uint8_t *)cmd
, out_bytes
);
102 /* Return size of request packet */
107 * Prepare the device to receive a protocol version 3 response.
109 * @param cec_command Command description.
110 * @param resp Response buffer.
111 * @return maximum expected number of bytes in response, or <0 if error.
113 static int prepare_proto3_response_buffer(
114 const struct chromeec_command
*cec_command
,
115 struct ec_response_v3
*resp
)
117 int in_bytes
= cec_command
->cmd_size_out
+ sizeof(resp
->header
);
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
;
126 /* Return expected size of response packet */
131 * Handle a protocol version 3 response packet.
133 * The packet must already be stored in the response buffer.
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
139 static int handle_proto3_response(struct ec_response_v3
*resp
,
140 struct chromeec_command
*cec_command
)
142 struct ec_host_response
*rs
= &resp
->header
;
146 cros_ec_dump_data("in-header", -1, (const uint8_t*)rs
, sizeof(*rs
));
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
;
155 printk(BIOS_ERR
, "%s: EC response reserved != 0\n", __func__
);
156 return -EC_RES_INVALID_RESPONSE
;
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
;
165 cros_ec_dump_data("in-data", -1, resp
->data
, rs
->data_len
);
167 /* Update in_bytes to actual data size */
168 in_bytes
= sizeof(*rs
) + rs
->data_len
;
170 /* Verify checksum */
171 csum
= cros_ec_calc_checksum((const uint8_t *)resp
, in_bytes
);
173 printk(BIOS_ERR
, "%s: EC response checksum invalid: 0x%02x\n",
175 return -EC_RES_INVALID_CHECKSUM
;
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
);
183 /* Return error result, if any */
185 printk(BIOS_ERR
, "%s: EC response with error code: %d\n",
186 __func__
, rs
->result
);
187 return -(int)rs
->result
;
193 static int send_command_proto3(struct chromeec_command
*cec_command
,
194 crosec_io_t crosec_io
, void *context
)
196 int out_bytes
, in_bytes
;
198 struct ec_command_v3
*cmd
;
199 struct ec_response_v3
*resp
;
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
;
206 /* Create request packet */
207 out_bytes
= create_proto3_request(cec_command
, cmd
);
212 /* Prepare response buffer */
213 in_bytes
= prepare_proto3_response_buffer(cec_command
, resp
);
218 rv
= crosec_io(out_bytes
, in_bytes
, context
);
220 printk(BIOS_ERR
, "%s: failed to complete I/O: Err = %#x.\n",
221 __func__
, rv
>= 0 ? rv
: -rv
);
222 return -EC_RES_ERROR
;
225 /* Process the response */
226 return handle_proto3_response(resp
, cec_command
);
229 static int crosec_command_proto_v3(struct chromeec_command
*cec_command
,
230 crosec_io_t crosec_io
, void *context
)
232 int rv
= send_command_proto3(cec_command
, crosec_io
, context
);
234 cec_command
->cmd_code
= rv
;
240 int crosec_command_proto(struct chromeec_command
*cec_command
,
241 crosec_io_t crosec_io
, void *context
)
243 // TODO(hungte) Detect and fallback to v2 if we need.
244 return crosec_command_proto_v3(cec_command
, crosec_io
, context
);