1 /*
2  * Copyright (c) 2020 Google LLC.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/devicetree.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/logging/log.h>
10 #include <zephyr/mgmt/ec_host_cmd/ec_host_cmd.h>
11 #include <zephyr/mgmt/ec_host_cmd/backend.h>
12 #include <zephyr/sys/iterable_sections.h>
13 #include <stdio.h>
14 #include <string.h>
15 
16 LOG_MODULE_REGISTER(host_cmd_handler, CONFIG_EC_HC_LOG_LEVEL);
17 
18 #ifdef CONFIG_EC_HOST_CMD_INITIALIZE_AT_BOOT
19 #define EC_HOST_CMD_CHOSEN_BACKEND_LIST                                                            \
20 	zephyr_host_cmd_espi_backend, zephyr_host_cmd_shi_backend, zephyr_host_cmd_uart_backend,   \
21 		zephyr_host_cmd_spi_backend
22 
23 #define EC_HOST_CMD_ADD_CHOSEN(chosen) COND_CODE_1(DT_NODE_EXISTS(DT_CHOSEN(chosen)), (1), (0))
24 
25 #define NUMBER_OF_CHOSEN_BACKENDS                                                                  \
26 	FOR_EACH(EC_HOST_CMD_ADD_CHOSEN, (+), EC_HOST_CMD_CHOSEN_BACKEND_LIST)                     \
27 	+0
28 
29 BUILD_ASSERT(NUMBER_OF_CHOSEN_BACKENDS < 2, "Number of chosen backends > 1");
30 #endif
31 
32 #define RX_HEADER_SIZE (sizeof(struct ec_host_cmd_request_header))
33 #define TX_HEADER_SIZE (sizeof(struct ec_host_cmd_response_header))
34 
35 COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_DEF,
36 	    (static uint8_t hc_rx_buffer[CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_SIZE] __aligned(4);),
37 	    ())
38 COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF,
39 	    (static uint8_t hc_tx_buffer[CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_SIZE] __aligned(4);),
40 	    ())
41 
42 #ifdef CONFIG_EC_HOST_CMD_DEDICATED_THREAD
43 static K_KERNEL_STACK_DEFINE(hc_stack, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE);
44 #endif /* CONFIG_EC_HOST_CMD_DEDICATED_THREAD */
45 
46 static struct ec_host_cmd ec_host_cmd = {
47 	.rx_ctx = {
48 			.buf = COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_DEF, (hc_rx_buffer),
49 					   (NULL)),
50 			.len_max = COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_DEF,
51 					       (CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_SIZE), (0)),
52 		},
53 	.tx = {
54 			.buf = COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF, (hc_tx_buffer),
55 					   (NULL)),
56 			.len_max = COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF,
57 					       (CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_SIZE), (0)),
58 		},
59 };
60 
61 #ifdef CONFIG_EC_HOST_CMD_IN_PROGRESS_STATUS
62 /* Indicates that a command has sent EC_HOST_CMD_IN_PROGRESS but hasn't sent a final status */
63 static bool cmd_in_progress;
64 
65 /* The final result of the last command that has sent EC_HOST_CMD_IN_PROGRESS */
66 static enum ec_host_cmd_status saved_status = EC_HOST_CMD_UNAVAILABLE;
67 static struct k_work work_in_progress;
68 ec_host_cmd_in_progress_cb_t cb_in_progress;
69 static void *user_data_in_progress;
70 #endif /* CONFIG_EC_HOST_CMD_IN_PROGRESS_STATUS */
71 
72 #ifdef CONFIG_EC_HOST_CMD_LOG_SUPPRESSED
73 static uint16_t suppressed_cmds[CONFIG_EC_HOST_CMD_LOG_SUPPRESSED_NUMBER];
74 static uint16_t suppressed_cmds_count[CONFIG_EC_HOST_CMD_LOG_SUPPRESSED_NUMBER];
75 static int64_t suppressed_cmds_deadline = CONFIG_EC_HOST_CMD_LOG_SUPPRESSED_INTERVAL_SECS * 1000U;
76 static size_t suppressed_cmds_number;
77 #endif /* CONFIG_EC_HOST_CMD_LOG_SUPPRESSED */
78 
cal_checksum(const uint8_t * const buffer,const uint16_t size)79 static uint8_t cal_checksum(const uint8_t *const buffer, const uint16_t size)
80 {
81 	uint8_t checksum = 0;
82 
83 	for (size_t i = 0; i < size; ++i) {
84 		checksum += buffer[i];
85 	}
86 	return (uint8_t)(-checksum);
87 }
88 
89 #ifdef CONFIG_EC_HOST_CMD_IN_PROGRESS_STATUS
ec_host_cmd_send_in_progress_ended(void)90 bool ec_host_cmd_send_in_progress_ended(void)
91 {
92 	return !cmd_in_progress;
93 }
94 
ec_host_cmd_send_in_progress_status(void)95 enum ec_host_cmd_status ec_host_cmd_send_in_progress_status(void)
96 {
97 	enum ec_host_cmd_status ret = saved_status;
98 
99 	saved_status = EC_HOST_CMD_UNAVAILABLE;
100 
101 	return ret;
102 }
103 
ec_host_cmd_send_in_progress_continue(ec_host_cmd_in_progress_cb_t cb,void * user_data)104 enum ec_host_cmd_status ec_host_cmd_send_in_progress_continue(ec_host_cmd_in_progress_cb_t cb,
105 							      void *user_data)
106 {
107 	if (cmd_in_progress) {
108 		return EC_HOST_CMD_BUSY;
109 	}
110 
111 	cmd_in_progress = true;
112 	cb_in_progress = cb;
113 	user_data_in_progress = user_data;
114 	saved_status = EC_HOST_CMD_UNAVAILABLE;
115 	LOG_INF("HC pending");
116 	k_work_submit(&work_in_progress);
117 
118 	return EC_HOST_CMD_SUCCESS;
119 }
120 
handler_in_progress(struct k_work * work)121 static void handler_in_progress(struct k_work *work)
122 {
123 	if (cb_in_progress != NULL) {
124 		saved_status = cb_in_progress(user_data_in_progress);
125 		LOG_INF("HC pending done, result=%d", saved_status);
126 	} else {
127 		saved_status = EC_HOST_CMD_UNAVAILABLE;
128 		LOG_ERR("HC incorrect IN_PROGRESS callback");
129 	}
130 	cb_in_progress = NULL;
131 	cmd_in_progress = false;
132 }
133 #endif /* CONFIG_EC_HOST_CMD_IN_PROGRESS_STATUS */
134 
135 #ifdef CONFIG_EC_HOST_CMD_LOG_SUPPRESSED
ec_host_cmd_add_suppressed(uint16_t cmd_id)136 int ec_host_cmd_add_suppressed(uint16_t cmd_id)
137 {
138 	if (suppressed_cmds_number >= CONFIG_EC_HOST_CMD_LOG_SUPPRESSED_NUMBER) {
139 		return -EIO;
140 	}
141 
142 	suppressed_cmds[suppressed_cmds_number] = cmd_id;
143 	++suppressed_cmds_number;
144 
145 	return 0;
146 }
147 
ec_host_cmd_is_suppressed(uint16_t cmd_id)148 static bool ec_host_cmd_is_suppressed(uint16_t cmd_id)
149 {
150 	int i;
151 
152 	for (i = 0; i < suppressed_cmds_number; i++) {
153 		if (suppressed_cmds[i] == cmd_id) {
154 			suppressed_cmds_count[i]++;
155 
156 			return true;
157 		}
158 	}
159 
160 	return false;
161 }
162 
ec_host_cmd_dump_suppressed(void)163 void ec_host_cmd_dump_suppressed(void)
164 {
165 	int i;
166 	int64_t uptime = k_uptime_get();
167 
168 	LOG_PRINTK("[%llds HC Suppressed:", uptime / 1000U);
169 	for (i = 0; i < suppressed_cmds_number; i++) {
170 		LOG_PRINTK(" 0x%x=%d", suppressed_cmds[i], suppressed_cmds_count[i]);
171 		suppressed_cmds_count[i] = 0;
172 	}
173 	LOG_PRINTK("]\n");
174 
175 	/* Reset the timer */
176 	suppressed_cmds_deadline = uptime + CONFIG_EC_HOST_CMD_LOG_SUPPRESSED_INTERVAL_SECS * 1000U;
177 }
178 
ec_host_cmd_check_suppressed(void)179 static void ec_host_cmd_check_suppressed(void)
180 {
181 	if (k_uptime_get() >= suppressed_cmds_deadline) {
182 		ec_host_cmd_dump_suppressed();
183 	}
184 }
185 #endif /* CONFIG_EC_HOST_CMD_LOG_SUPPRESSED */
186 
send_status_response(const struct ec_host_cmd_backend * backend,struct ec_host_cmd_tx_buf * tx,const enum ec_host_cmd_status status)187 static void send_status_response(const struct ec_host_cmd_backend *backend,
188 				 struct ec_host_cmd_tx_buf *tx,
189 				 const enum ec_host_cmd_status status)
190 {
191 	struct ec_host_cmd_response_header *const tx_header = (void *)tx->buf;
192 
193 	tx_header->prtcl_ver = 3;
194 	tx_header->result = status;
195 	tx_header->data_len = 0;
196 	tx_header->reserved = 0;
197 	tx_header->checksum = 0;
198 	tx_header->checksum = cal_checksum((uint8_t *)tx_header, TX_HEADER_SIZE);
199 
200 	tx->len = TX_HEADER_SIZE;
201 
202 	backend->api->send(backend);
203 }
204 
verify_rx(struct ec_host_cmd_rx_ctx * rx)205 static enum ec_host_cmd_status verify_rx(struct ec_host_cmd_rx_ctx *rx)
206 {
207 	/* rx buf and len now have valid incoming data */
208 	if (rx->len < RX_HEADER_SIZE) {
209 		return EC_HOST_CMD_REQUEST_TRUNCATED;
210 	}
211 
212 	const struct ec_host_cmd_request_header *rx_header =
213 		(struct ec_host_cmd_request_header *)rx->buf;
214 
215 	/* Only support version 3 */
216 	if (rx_header->prtcl_ver != 3) {
217 		return EC_HOST_CMD_INVALID_HEADER;
218 	}
219 
220 	const uint16_t rx_valid_data_size = rx_header->data_len + RX_HEADER_SIZE;
221 	/*
222 	 * Ensure we received at least as much data as is expected.
223 	 * It is okay to receive more since some hardware interfaces
224 	 * add on extra padding bytes at the end.
225 	 */
226 	if (rx->len < rx_valid_data_size) {
227 		return EC_HOST_CMD_REQUEST_TRUNCATED;
228 	}
229 
230 	/* Validate checksum */
231 	if (cal_checksum((uint8_t *)rx_header, rx_valid_data_size) != 0) {
232 		return EC_HOST_CMD_INVALID_CHECKSUM;
233 	}
234 
235 	return EC_HOST_CMD_SUCCESS;
236 }
237 
validate_handler(const struct ec_host_cmd_handler * handler,const struct ec_host_cmd_handler_args * args)238 static enum ec_host_cmd_status validate_handler(const struct ec_host_cmd_handler *handler,
239 						const struct ec_host_cmd_handler_args *args)
240 {
241 	if (handler->min_rqt_size > args->input_buf_size) {
242 		return EC_HOST_CMD_REQUEST_TRUNCATED;
243 	}
244 
245 	if (handler->min_rsp_size > args->output_buf_max) {
246 		return EC_HOST_CMD_INVALID_RESPONSE;
247 	}
248 
249 	if (args->version >= NUM_BITS(handler->version_mask) ||
250 	    !(handler->version_mask & BIT(args->version))) {
251 		return EC_HOST_CMD_INVALID_VERSION;
252 	}
253 
254 	return EC_HOST_CMD_SUCCESS;
255 }
256 
prepare_response(struct ec_host_cmd_tx_buf * tx,uint16_t len)257 static enum ec_host_cmd_status prepare_response(struct ec_host_cmd_tx_buf *tx, uint16_t len)
258 {
259 	struct ec_host_cmd_response_header *const tx_header = (void *)tx->buf;
260 
261 	tx_header->prtcl_ver = 3;
262 	tx_header->result = EC_HOST_CMD_SUCCESS;
263 	tx_header->data_len = len;
264 	tx_header->reserved = 0;
265 
266 	const uint16_t tx_valid_data_size = tx_header->data_len + TX_HEADER_SIZE;
267 
268 	if (tx_valid_data_size > tx->len_max) {
269 		return EC_HOST_CMD_INVALID_RESPONSE;
270 	}
271 
272 	/* Calculate checksum */
273 	tx_header->checksum = 0;
274 	tx_header->checksum = cal_checksum(tx->buf, tx_valid_data_size);
275 
276 	tx->len = tx_valid_data_size;
277 
278 	return EC_HOST_CMD_SUCCESS;
279 }
280 
ec_host_cmd_set_user_cb(ec_host_cmd_user_cb_t cb,void * user_data)281 void ec_host_cmd_set_user_cb(ec_host_cmd_user_cb_t cb, void *user_data)
282 {
283 	struct ec_host_cmd *hc = &ec_host_cmd;
284 
285 	hc->user_cb = cb;
286 	hc->user_data = user_data;
287 }
288 
ec_host_cmd_send_response(enum ec_host_cmd_status status,const struct ec_host_cmd_handler_args * args)289 int ec_host_cmd_send_response(enum ec_host_cmd_status status,
290 			      const struct ec_host_cmd_handler_args *args)
291 {
292 	struct ec_host_cmd *hc = &ec_host_cmd;
293 	struct ec_host_cmd_tx_buf *tx = &hc->tx;
294 
295 	if (hc->state != EC_HOST_CMD_STATE_PROCESSING) {
296 		LOG_ERR("Unexpected state while sending");
297 		return -ENOTSUP;
298 	}
299 	hc->state = EC_HOST_CMD_STATE_SENDING;
300 
301 	if (status != EC_HOST_CMD_SUCCESS) {
302 		const struct ec_host_cmd_request_header *const rx_header =
303 			(const struct ec_host_cmd_request_header *const)hc->rx_ctx.buf;
304 
305 		LOG_INF("HC 0x%04x err %d", rx_header->cmd_id, status);
306 		send_status_response(hc->backend, tx, status);
307 		return status;
308 	}
309 
310 #ifdef CONFIG_EC_HOST_CMD_LOG_DBG_BUFFERS
311 	if (args->output_buf_size) {
312 		LOG_HEXDUMP_DBG(args->output_buf, args->output_buf_size, "HC resp:");
313 	}
314 #endif
315 
316 	status = prepare_response(tx, args->output_buf_size);
317 	if (status != EC_HOST_CMD_SUCCESS) {
318 		send_status_response(hc->backend, tx, status);
319 		return status;
320 	}
321 
322 	return hc->backend->api->send(hc->backend);
323 }
324 
ec_host_cmd_rx_notify(void)325 void ec_host_cmd_rx_notify(void)
326 {
327 	struct ec_host_cmd *hc = &ec_host_cmd;
328 	struct ec_host_cmd_rx_ctx *rx = &hc->rx_ctx;
329 
330 	hc->rx_status = verify_rx(rx);
331 
332 	if (!hc->rx_status && hc->user_cb) {
333 		hc->user_cb(rx, hc->user_data);
334 	}
335 
336 	k_sem_give(&hc->rx_ready);
337 }
338 
ec_host_cmd_log_request(const uint8_t * rx_buf)339 static void ec_host_cmd_log_request(const uint8_t *rx_buf)
340 {
341 	static uint16_t prev_cmd;
342 	const struct ec_host_cmd_request_header *const rx_header =
343 		(const struct ec_host_cmd_request_header *const)rx_buf;
344 
345 #ifdef CONFIG_EC_HOST_CMD_LOG_SUPPRESSED
346 	if (ec_host_cmd_is_suppressed(rx_header->cmd_id)) {
347 		ec_host_cmd_check_suppressed();
348 
349 		return;
350 	}
351 #endif /* CONFIG_EC_HOST_CMD_LOG_SUPPRESSED */
352 
353 	if (IS_ENABLED(CONFIG_EC_HOST_CMD_LOG_DBG_BUFFERS)) {
354 		if (rx_header->data_len) {
355 			const uint8_t *rx_data = rx_buf + RX_HEADER_SIZE;
356 			static const char dbg_fmt[] = "HC 0x%04x.%d:";
357 			/* Use sizeof because "%04x" needs 4 bytes for command id, and
358 			 * %d needs 2 bytes for version, so no additional buffer is required.
359 			 */
360 			char dbg_raw[sizeof(dbg_fmt)];
361 
362 			snprintf(dbg_raw, sizeof(dbg_raw), dbg_fmt, rx_header->cmd_id,
363 				 rx_header->cmd_ver);
364 			LOG_HEXDUMP_DBG(rx_data, rx_header->data_len, dbg_raw);
365 
366 			return;
367 		}
368 	}
369 
370 	/* In normal output mode, skip printing repeats of the same command
371 	 * that occur in rapid succession - such as flash commands during
372 	 * software sync.
373 	 */
374 	if (rx_header->cmd_id != prev_cmd) {
375 		prev_cmd = rx_header->cmd_id;
376 		LOG_INF("HC 0x%04x", rx_header->cmd_id);
377 	} else {
378 		LOG_DBG("HC 0x%04x", rx_header->cmd_id);
379 	}
380 }
381 
ec_host_cmd_thread(void * hc_handle,void * arg2,void * arg3)382 FUNC_NORETURN static void ec_host_cmd_thread(void *hc_handle, void *arg2, void *arg3)
383 {
384 	ARG_UNUSED(arg2);
385 	ARG_UNUSED(arg3);
386 	enum ec_host_cmd_status status;
387 	struct ec_host_cmd *hc = (struct ec_host_cmd *)hc_handle;
388 	struct ec_host_cmd_rx_ctx *rx = &hc->rx_ctx;
389 	struct ec_host_cmd_tx_buf *tx = &hc->tx;
390 	const struct ec_host_cmd_handler *found_handler;
391 	const struct ec_host_cmd_request_header *const rx_header = (void *)rx->buf;
392 	/* The pointer to rx buffer is constant during communication */
393 	struct ec_host_cmd_handler_args args = {
394 		.output_buf = (uint8_t *)tx->buf + TX_HEADER_SIZE,
395 		.input_buf = rx->buf + RX_HEADER_SIZE,
396 		.reserved = NULL,
397 	};
398 
399 	__ASSERT(hc->state != EC_HOST_CMD_STATE_DISABLED, "HC backend not initialized");
400 
401 	while (1) {
402 		hc->state = EC_HOST_CMD_STATE_RECEIVING;
403 		/* Wait until RX messages is received on host interface */
404 		k_sem_take(&hc->rx_ready, K_FOREVER);
405 		hc->state = EC_HOST_CMD_STATE_PROCESSING;
406 
407 		ec_host_cmd_log_request(rx->buf);
408 
409 		/* Check status of the rx data, that has been verified in
410 		 * ec_host_cmd_send_received.
411 		 */
412 		if (hc->rx_status != EC_HOST_CMD_SUCCESS) {
413 			ec_host_cmd_send_response(hc->rx_status, &args);
414 			continue;
415 		}
416 
417 		found_handler = NULL;
418 		STRUCT_SECTION_FOREACH(ec_host_cmd_handler, handler) {
419 			if (handler->id == rx_header->cmd_id) {
420 				found_handler = handler;
421 				break;
422 			}
423 		}
424 
425 		/* No handler in this image for requested command */
426 		if (found_handler == NULL) {
427 			ec_host_cmd_send_response(EC_HOST_CMD_INVALID_COMMAND, &args);
428 			continue;
429 		}
430 
431 		args.command = rx_header->cmd_id;
432 		args.version = rx_header->cmd_ver;
433 		args.input_buf_size = rx_header->data_len;
434 		args.output_buf_max = tx->len_max - TX_HEADER_SIZE,
435 		args.output_buf_size = 0;
436 
437 		status = validate_handler(found_handler, &args);
438 		if (status != EC_HOST_CMD_SUCCESS) {
439 			ec_host_cmd_send_response(status, &args);
440 			continue;
441 		}
442 
443 		/*
444 		 * Pre-emptively clear the entire response buffer so we do not
445 		 * have any left over contents from previous host commands.
446 		 */
447 		memset(args.output_buf, 0, args.output_buf_max);
448 
449 		status = found_handler->handler(&args);
450 
451 		ec_host_cmd_send_response(status, &args);
452 	}
453 }
454 
455 #ifndef CONFIG_EC_HOST_CMD_DEDICATED_THREAD
ec_host_cmd_task(void)456 FUNC_NORETURN void ec_host_cmd_task(void)
457 {
458 	ec_host_cmd_thread(&ec_host_cmd, NULL, NULL);
459 }
460 #endif
461 
ec_host_cmd_init(struct ec_host_cmd_backend * backend)462 int ec_host_cmd_init(struct ec_host_cmd_backend *backend)
463 {
464 	struct ec_host_cmd *hc = &ec_host_cmd;
465 	int ret;
466 	uint8_t *handler_tx_buf, *handler_rx_buf;
467 	uint8_t *handler_tx_buf_end, *handler_rx_buf_end;
468 	uint8_t *backend_tx_buf, *backend_rx_buf;
469 
470 	hc->backend = backend;
471 
472 	/* Allow writing to rx buff at startup */
473 	k_sem_init(&hc->rx_ready, 0, 1);
474 
475 #ifdef CONFIG_EC_HOST_CMD_IN_PROGRESS_STATUS
476 	k_work_init(&work_in_progress, handler_in_progress);
477 #endif /* CONFIG_EC_HOST_CMD_IN_PROGRESS_STATUS */
478 
479 	handler_tx_buf = hc->tx.buf;
480 	handler_rx_buf = hc->rx_ctx.buf;
481 	handler_tx_buf_end = handler_tx_buf + CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_SIZE;
482 	handler_rx_buf_end = handler_rx_buf + CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_SIZE;
483 
484 	ret = backend->api->init(backend, &hc->rx_ctx, &hc->tx);
485 
486 	backend_tx_buf = hc->tx.buf;
487 	backend_rx_buf = hc->rx_ctx.buf;
488 
489 	if (ret != 0) {
490 		return ret;
491 	}
492 
493 	if (!backend_tx_buf || !backend_rx_buf) {
494 		LOG_ERR("No buffer for Host Command communication");
495 		return -EIO;
496 	}
497 
498 	hc->state = EC_HOST_CMD_STATE_RECEIVING;
499 
500 	/* Check if a backend uses provided buffers. The buffer pointers can be shifted within the
501 	 * buffer to make space for preamble. Make sure the rx/tx pointers are within the provided
502 	 * buffers ranges.
503 	 */
504 	if ((handler_tx_buf &&
505 	     !((handler_tx_buf <= backend_tx_buf) && (handler_tx_buf_end > backend_tx_buf))) ||
506 	    (handler_rx_buf &&
507 	     !((handler_rx_buf <= backend_rx_buf) && (handler_rx_buf_end > backend_rx_buf)))) {
508 		LOG_WRN("Host Command handler provided unused buffer");
509 	}
510 
511 #ifdef CONFIG_EC_HOST_CMD_DEDICATED_THREAD
512 	k_thread_create(&hc->thread, hc_stack, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE,
513 			ec_host_cmd_thread, (void *)hc, NULL, NULL, CONFIG_EC_HOST_CMD_HANDLER_PRIO,
514 			0, K_NO_WAIT);
515 	k_thread_name_set(&hc->thread, "ec_host_cmd");
516 #endif /* CONFIG_EC_HOST_CMD_DEDICATED_THREAD */
517 
518 	return 0;
519 }
520 
ec_host_cmd_get_hc(void)521 const struct ec_host_cmd *ec_host_cmd_get_hc(void)
522 {
523 	return &ec_host_cmd;
524 }
525