diff options
author | Hyungwoo Yang <hyungwoo.yang@intel.com> | 2018-10-12 19:28:25 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-12-28 16:14:00 -0800 |
commit | a2c87e75cc5b42b900832f0e80244c077b497f2c (patch) | |
tree | 4c75487b398691bdc1264a78aad2b48a98aa88fb | |
parent | 8123080f146d712ca0942873584c8a0f1f489237 (diff) | |
download | chrome-ec-a2c87e75cc5b42b900832f0e80244c077b497f2c.tar.gz |
ISH: HECI: implement HECI layer
Introduce Host Embedeed Controller Interface for ISH.
HECI is bi-directional fully asynchronous communication interface
between host and ISH. It enables a host software to communicate with
a ISH software(a HECI client).
BUG=b:79676054
BRANCH=none
TEST=Tested on Atlas board.
CQ-DEPEND=CL:1279363
Change-Id: I5fdc3018e9575c5fd0c804a883293f6c9f8aa2e7
Reviewed-on: https://chromium-review.googlesource.com/1279432
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Hyungwoo Yang <hyungwoo.yang@intel.com>
Reviewed-by: Hyungwoo Yang <hyungwoo.yang@intel.com>
Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r-- | chip/ish/build.mk | 1 | ||||
-rw-r--r-- | chip/ish/hbm.h | 195 | ||||
-rw-r--r-- | chip/ish/heci.c | 940 | ||||
-rw-r--r-- | chip/ish/heci_client.h | 102 | ||||
-rw-r--r-- | chip/ish/system_state.h | 31 | ||||
-rw-r--r-- | chip/ish/system_state_subsys.c | 149 |
6 files changed, 1418 insertions, 0 deletions
diff --git a/chip/ish/build.mk b/chip/ish/build.mk index c8461b3638..13b6ae2474 100644 --- a/chip/ish/build.mk +++ b/chip/ish/build.mk @@ -21,6 +21,7 @@ chip-y+=clock.o gpio.o system.o hwtimer.o uart.o flash.o chip-$(CONFIG_I2C)+=i2c.o chip-$(CONFIG_HOSTCMD_LPC)+=ipc.o chip-$(CONFIG_ISH_IPC)+=ipc_heci.o +chip-$(CONFIG_HECI)+=heci.o system_state_subsys.o chip-$(CONFIG_WATCHDOG)+=watchdog.o # location of the scripts and keys used to pack the SPI flash image diff --git a/chip/ish/hbm.h b/chip/ish/hbm.h new file mode 100644 index 0000000000..edfb587d21 --- /dev/null +++ b/chip/ish/hbm.h @@ -0,0 +1,195 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __HBM_H +#define __HBM_H + +#include <stdint.h> +#include <stddef.h> + +#include "heci_client.h" + +#define HBM_MAJOR_VERSION 1 +#ifdef HECI_ENABLE_DMA +#define HBM_MINOR_VERSION 2 +#else +#define HBM_MINOR_VERSION 0 +#endif + +#define __packed __attribute__((packed)) + +#define HECI_MSG_REPONSE_FLAG 0x80 + +enum HECI_BUS_MSG { + /* requests */ + HECI_BUS_MSG_VERSION_REQ = 1, + HECI_BUS_MSG_HOST_STOP_REQ = 2, + HECI_BUS_MSG_ME_STOP_REQ = 3, + HECI_BUS_MSG_HOST_ENUM_REQ = 4, + HECI_BUS_MSG_HOST_CLIENT_PROP_REQ = 5, + HECI_BUS_MSG_CLIENT_CONNECT_REQ = 6, + HECI_BUS_MSG_CLIENT_DISCONNECT_REQ = 7, + HECI_BUS_MSG_FLOW_CONTROL = 8, + HECI_BUS_MSG_RESET_REQ = 9, + HECI_BUS_MSG_ADD_CLIENT_REQ = 0x0A, + HECI_BUS_MSG_DMA_REQ = 0x10, + HECI_BUS_MSG_DMA_ALLOC_NOTIFY = 0x11, + HECI_BUS_MSG_DMA_XFER_REQ = 0x12, + + /* responses */ + HECI_BUS_MSG_VERSION_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_VERSION_REQ), + HECI_BUS_MSG_HOST_STOP_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_HOST_STOP_REQ), + HECI_BUS_MSG_HOST_ENUM_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_HOST_ENUM_REQ), + HECI_BUS_MSG_HOST_CLIENT_PROP_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_HOST_CLIENT_PROP_REQ), + HECI_BUS_MSG_CLIENT_CONNECT_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_CLIENT_CONNECT_REQ), + HECI_BUS_MSG_CLIENT_DISCONNECT_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_CLIENT_DISCONNECT_REQ), + HECI_BUS_MSG_RESET_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_RESET_REQ), + HECI_BUS_MSG_ADD_CLIENT_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_ADD_CLIENT_REQ), + HECI_BUS_MSG_DMA_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_DMA_REQ), + HECI_BUS_MSG_DMA_ALLOC_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_DMA_ALLOC_NOTIFY), + HECI_BUS_MSG_DMA_XFER_RESP = + (HECI_MSG_REPONSE_FLAG | HECI_BUS_MSG_DMA_XFER_REQ) +}; + +enum { + HECI_CONNECT_STATUS_SUCCESS = 0, + HECI_CONNECT_STATUS_CLIENT_NOT_FOUND = 1, + HECI_CONNECT_STATUS_ALREADY_EXISTS = 2, + HECI_CONNECT_STATUS_REJECTED = 3, + HECI_CONNECT_STATUS_INVALID_PARAMETER = 4, + HECI_CONNECT_STATUS_INACTIVE_CLIENT = 5, +}; + +struct hbm_version { + uint8_t minor; + uint8_t major; +} __packed; + +struct hbm_version_req { + uint8_t reserved; + struct hbm_version version; +} __packed; + +struct hbm_version_res { + uint8_t supported; + struct hbm_version version; +} __packed; + +struct hbm_enum_req { + uint8_t reserved[3]; +} __packed; + +struct hbm_enum_res { + uint8_t reserved[3]; + uint8_t valid_addresses[32]; +} __packed; + +struct hbm_client_prop_req { + uint8_t address; + uint8_t reserved[2]; +} __packed; + +#define CLIENT_DMA_ENABLE 0x80 + +struct hbm_client_properties { + struct heci_guid protocol_name; /* heci client protocol ID */ + uint8_t protocol_version; /* protocol version */ + /* max connection from host to client. currently only 1 is allowed */ + uint8_t max_number_of_connections; + uint8_t fixed_address; /* not yet supported */ + uint8_t single_recv_buf; /* not yet supported */ + uint32_t max_msg_length; /* max payload size */ + /* not yet supported. [7] enable/disable, [6:0] dma length */ + uint8_t dma_hdr_len; + uint8_t reserved4; + uint8_t reserved5; + uint8_t reserved6; +} __packed; + +struct hbm_client_prop_res { + uint8_t address; + uint8_t status; + uint8_t reserved[1]; + struct hbm_client_properties client_prop; +} __packed; + +struct hbm_client_connect_req { + uint8_t fw_addr; + uint8_t host_addr; + uint8_t reserved; +} __packed; + +struct hbm_client_connect_res { + uint8_t fw_addr; + uint8_t host_addr; + uint8_t status; +} __packed; + +struct hbm_flow_control { + uint8_t fw_addr; + uint8_t host_addr; + uint8_t reserved[5]; +} __packed; + +struct hbm_client_disconnect_req { + uint8_t fw_addr; + uint8_t host_addr; + uint8_t reserved; +} __packed; + +struct hbm_client_disconnect_res { + uint8_t fw_addr; + uint8_t host_addr; + uint8_t status; +} __packed; + +struct hbm_host_stop_req { + uint8_t reason; + uint8_t reserved[2]; +}; + +struct hbm_host_stop_res { + uint8_t reserved[3]; +}; + +/* host bus message : host -> ish */ +struct hbm_h2i { + uint8_t cmd; + union { + struct hbm_version_req ver_req; + struct hbm_enum_req enum_req; + struct hbm_client_prop_req client_prop_req; + struct hbm_client_connect_req client_connect_req; + struct hbm_flow_control flow_ctrl; + struct hbm_client_disconnect_req client_disconnect_req; + struct hbm_host_stop_req host_stop_req; + } data; +} __packed; + +/* host bus message : i2h -> host */ +struct hbm_i2h { + uint8_t cmd; + union { + struct hbm_version_res ver_res; + struct hbm_enum_res enum_res; + struct hbm_client_prop_res client_prop_res; + struct hbm_client_connect_res client_connect_res; + struct hbm_flow_control flow_ctrl; + struct hbm_client_disconnect_res client_disconnect_res; + struct hbm_host_stop_res host_stop_res; + } data; +} __packed; + +#endif /* __HBM_H */ diff --git a/chip/ish/heci.c b/chip/ish/heci.c new file mode 100644 index 0000000000..512a9393a4 --- /dev/null +++ b/chip/ish/heci.c @@ -0,0 +1,940 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "atomic.h" +#include "compile_time_macros.h" +#include "console.h" +#include "hbm.h" +#include "heci_client.h" +#include "ipc_heci.h" +#include "system_state.h" +#include "task.h" +#include "util.h" + +#ifdef DEBUG_IPC_HECI +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args) +#else +#define CPUTS(outstr) +#define CPRINTS(format, args...) +#define CPRINTF(format, args...) +#endif + +struct heci_header { + uint8_t fw_addr; + uint8_t host_addr; + uint16_t length; /* [8:0] length, [14:9] reserved, [15] msg_complete */ +} __packed; +#define HECI_MSG_CMPL_SHIFT 15 +#define HECI_MSG_LENGTH_MASK 0x01FF +#define HECI_MSG_LENGTH(length) ((length) & HECI_MSG_LENGTH_MASK) +#define HECI_MSG_IS_COMPLETED(length) \ + (!!((length) & (0x01 << HECI_MSG_CMPL_SHIFT))) + +#define HECI_IPC_PAYLOAD_SIZE \ + (IPC_MAX_PAYLOAD_SIZE - sizeof(struct heci_header)) + +struct heci_msg { + struct heci_header hdr; + uint8_t payload[HECI_IPC_PAYLOAD_SIZE]; +} __packed; + +/* address for Host Bus */ +#define HECI_HBM_ADDRESS 0 + +/* should be less than HECI_INVALID_HANDLE - 1 */ +BUILD_ASSERT(HECI_MAX_NUM_OF_CLIENTS < 0x0FE); + +struct heci_client_connect { + uint8_t is_connected; /* client is connected to host */ + uint8_t host_addr; /* connected host address */ + + /* receiving message */ + uint8_t ignore_rx_msg; + uint8_t rx_msg[HECI_MAX_MSG_SIZE]; + size_t rx_msg_length; + + uint32_t flow_ctrl_creds; /* flow control */ + struct mutex lock; /* mutext for operation related with connection */ +}; + +struct heci_client_context { + const struct heci_client *client; + void *data; /* client specific data */ + + struct heci_client_connect connect; /* connection context */ + struct ss_subsys_device ss_device; /* system state receiver device */ +}; + +struct heci_bus_context { + ipc_handle_t ipc_handle; /* ipc handle for heci protocol */ + + int num_of_clients; + struct heci_client_context client_ctxs[HECI_MAX_NUM_OF_CLIENTS]; +}; + +/* declare heci bus */ +struct heci_bus_context heci_bus_ctx = { + .ipc_handle = IPC_INVALID_HANDLE, +}; + +/* host generates client fw address starting from 1 */ +static inline struct heci_client_context * +heci_get_client_context(const uint8_t fw_addr) +{ + return &heci_bus_ctx.client_ctxs[fw_addr - 1]; +} + +static inline struct heci_client_connect * +heci_get_client_connect(const uint8_t fw_addr) +{ + struct heci_client_context *cli_ctx = heci_get_client_context(fw_addr); + return &cli_ctx->connect; +} + +static inline int heci_is_client_connected(const uint8_t fw_addr) +{ + struct heci_client_context *cli_ctx = heci_get_client_context(fw_addr); + return cli_ctx->connect.is_connected; +} + +static inline int heci_is_valid_client_addr(const uint8_t fw_addr) +{ + return fw_addr > 0 && fw_addr <= heci_bus_ctx.num_of_clients; +} + +static inline int heci_is_valid_handle(const heci_handle_t handle) +{ + return heci_is_valid_client_addr((uintptr_t)(handle)); +} + +#define TO_FW_ADDR(handle) ((uintptr_t)(handle)) +#define TO_HECI_HANDLE(fw_addr) ((heci_handle_t)(uintptr_t)(fw_addr)) + +/* find heci device that contains this system state device in it */ +#define ss_device_to_heci_client_context(ss_dev) \ + ((struct heci_client_context *)((void *)(ss_dev) - \ + (void *)(&(((struct heci_client_context *)0)->ss_device)))) +#define client_context_to_handle(cli_ctx) \ + ((heci_handle_t)((uint32_t)((cli_ctx) - &heci_bus_ctx.client_ctxs[0]) \ + / sizeof(heci_bus_ctx.client_ctxs[0]) + 1)) + +/* + * each heci device registered as system state device which gets + * system state(e.g. suspend/resume, portrait/landscape) events + * through system state subsystem from host + */ +static int heci_client_suspend(struct ss_subsys_device *ss_device) +{ + struct heci_client_context *cli_ctx = + ss_device_to_heci_client_context(ss_device); + heci_handle_t handle = client_context_to_handle(cli_ctx); + + if (cli_ctx->client->cbs->suspend) + cli_ctx->client->cbs->suspend(handle); + + return EC_SUCCESS; +} + +static int heci_client_resume(struct ss_subsys_device *ss_device) +{ + struct heci_client_context *cli_ctx = + ss_device_to_heci_client_context(ss_device); + heci_handle_t handle = client_context_to_handle(cli_ctx); + + if (cli_ctx->client->cbs->resume) + cli_ctx->client->cbs->resume(handle); + + return EC_SUCCESS; +} + +struct system_state_callbacks heci_ss_cbs = { + .suspend = heci_client_suspend, + .resume = heci_client_resume, +}; + +/* + * This function should be called only by HECI_CLIENT_ENTRY() + */ +heci_handle_t heci_register_client(const struct heci_client *client) +{ + int ret; + struct heci_client_context *cli_ctx; + + if (client == NULL || client->cbs == NULL) + return HECI_INVALID_HANDLE; + + /* + * we don't need mutex here since this function is called by + * entry function which is serialized among heci clients. + */ + if (heci_bus_ctx.num_of_clients >= HECI_MAX_NUM_OF_CLIENTS) + return HECI_INVALID_HANDLE; + + /* we only support 1 connection */ + if (client->max_n_of_connections > 1) + return HECI_INVALID_HANDLE; + + if (client->max_msg_size > HECI_MAX_MSG_SIZE) + return HECI_INVALID_HANDLE; + + cli_ctx = &heci_bus_ctx.client_ctxs[heci_bus_ctx.num_of_clients++]; + + cli_ctx->client = client; + + if (client->cbs->initialize) { + ret = client->cbs->initialize( + (heci_handle_t)heci_bus_ctx.num_of_clients); + + if (ret) { + heci_bus_ctx.num_of_clients--; + return HECI_INVALID_HANDLE; + } + } + + if (client->cbs->suspend || client->cbs->resume) { + cli_ctx->ss_device.cbs = &heci_ss_cbs; + ss_subsys_register_client(&cli_ctx->ss_device); + } + + return (heci_handle_t)heci_bus_ctx.num_of_clients; +} + +static void heci_build_hbm_header(struct heci_header *hdr, uint32_t length) +{ + hdr->fw_addr = HECI_HBM_ADDRESS; + hdr->host_addr = HECI_HBM_ADDRESS; + hdr->length = length; + /* payload of hbm is less than IPC payload */ + hdr->length |= (uint16_t)1 << HECI_MSG_CMPL_SHIFT; +} + +static void heci_build_fixed_client_header(struct heci_header *hdr, + const uint8_t fw_addr, + const uint32_t length) +{ + hdr->fw_addr = fw_addr; + hdr->host_addr = 0; + hdr->length = length; + /* Fixed client payload < IPC payload */ + hdr->length |= (uint16_t)1 << HECI_MSG_CMPL_SHIFT; +} + +static int heci_send_heci_msg(struct heci_msg *msg) +{ + int length, written; + + if (heci_bus_ctx.ipc_handle == IPC_INVALID_HANDLE) + return -1; + + length = sizeof(msg->hdr) + HECI_MSG_LENGTH(msg->hdr.length); + written = ipc_write(heci_bus_ctx.ipc_handle, msg, length); + + if (written != length) { + CPRINTF("%s error : len = %d err = %d\n", __func__, + (int)length, written); + return -EC_ERROR_UNKNOWN; + } + + return EC_SUCCESS; +} + +int heci_set_client_data(const heci_handle_t handle, void *data) +{ + struct heci_client_context *cli_ctx; + const uint8_t fw_addr = TO_FW_ADDR(handle); + + if (!heci_is_valid_handle(handle)) + return -EC_ERROR_INVAL; + + cli_ctx = heci_get_client_context(fw_addr); + cli_ctx->data = data; + + return EC_SUCCESS; +} + +void *heci_get_client_data(const heci_handle_t handle) +{ + struct heci_client_context *cli_ctx; + const uint8_t fw_addr = TO_FW_ADDR(handle); + + if (!heci_is_valid_handle(handle)) + return NULL; + + cli_ctx = heci_get_client_context(fw_addr); + return cli_ctx->data; +} + +int heci_send_msg(const heci_handle_t handle, uint8_t *buf, + const size_t buf_size) +{ + int buf_offset = 0, ret = 0, remain, payload_size; + struct heci_client_connect *connect; + struct heci_msg msg; + const uint8_t fw_addr = TO_FW_ADDR(handle); + + if (!heci_is_valid_handle(handle)) + return -EC_ERROR_INVAL; + + if (buf_size > HECI_MAX_MSG_SIZE) + return -EC_ERROR_OVERFLOW; + + connect = heci_get_client_connect(fw_addr); + mutex_lock(&connect->lock); + + if (!heci_is_client_connected(fw_addr)) { + ret = -HECI_ERR_CLIENT_IS_NOT_CONNECTED; + goto err_locked; + } + + if (!connect->flow_ctrl_creds) { + CPRINTF("no cred\n"); + ret = -HECI_ERR_NO_CRED_FROM_CLIENT_IN_HOST; + goto err_locked; + } + + msg.hdr.fw_addr = fw_addr; + msg.hdr.host_addr = connect->host_addr; + + remain = buf_size; + while (remain) { + if (remain > HECI_IPC_PAYLOAD_SIZE) { + msg.hdr.length = HECI_IPC_PAYLOAD_SIZE; + payload_size = HECI_IPC_PAYLOAD_SIZE; + } else { + msg.hdr.length = remain; + /* set as last heci msg */ + msg.hdr.length |= (uint16_t)1 << HECI_MSG_CMPL_SHIFT; + payload_size = remain; + } + + memcpy(msg.payload, buf + buf_offset, payload_size); + + heci_send_heci_msg(&msg); + + remain -= payload_size; + buf_offset += payload_size; + } + + atomic_sub(&connect->flow_ctrl_creds, 1); + mutex_unlock(&connect->lock); + + return buf_size; + +err_locked: + mutex_unlock(&connect->lock); + + return ret; +} + +int heci_send_msgs(const heci_handle_t handle, + const struct heci_msg_list *msg_list) +{ + struct heci_msg_item *cur_item; + int total_size = 0; + int i, msg_cur_pos, buf_size, copy_size, msg_sent; + struct heci_client_connect *connect; + struct heci_msg msg; + const uint8_t fw_addr = TO_FW_ADDR(handle); + + if (!heci_is_valid_handle(handle)) + return -EC_ERROR_INVAL; + + for (i = 0; i < msg_list->num_of_items; i++) { + if (!msg_list->items[i]->size || !msg_list->items[i]->buf) + return -EC_ERROR_INVAL; + + total_size += msg_list->items[i]->size; + } + + if (total_size > HECI_MAX_MSG_SIZE) + return -EC_ERROR_OVERFLOW; + + if (msg_list->num_of_items > HECI_MAX_MSGS) + return -HECI_ERR_TOO_MANY_MSG_ITEMS; + + connect = heci_get_client_connect(fw_addr); + mutex_lock(&connect->lock); + + if (!heci_is_client_connected(fw_addr)) { + total_size = -HECI_ERR_CLIENT_IS_NOT_CONNECTED; + goto err_locked; + } + + if (!connect->flow_ctrl_creds) { + CPRINTF("no cred\n"); + total_size = -HECI_ERR_NO_CRED_FROM_CLIENT_IN_HOST; + goto err_locked; + } + + msg.hdr.fw_addr = fw_addr; + msg.hdr.host_addr = connect->host_addr; + + i = 1; + msg_cur_pos = 0; + buf_size = 0; + cur_item = msg_list->items[0]; + msg_sent = 0; + while (1) { + /* get next item if current item is consumed */ + if (msg_cur_pos == cur_item->size) { + /* + * break if no more item. + * if "msg" contains data to be sent + * it will be sent after break. + */ + if (i == msg_list->num_of_items) + break; + + /* get next item and reset msg_cur_pos */ + cur_item = msg_list->items[i++]; + msg_cur_pos = 0; + } + + /* send data in ipc buf if it's completely filled */ + if (buf_size == HECI_IPC_PAYLOAD_SIZE) { + msg.hdr.length = buf_size; + msg_sent += buf_size; + + /* no leftovers, send the last msg here */ + if (msg_sent == total_size) { + msg.hdr.length |= + (uint16_t)1 << HECI_MSG_CMPL_SHIFT; + } + + heci_send_heci_msg(&msg); + buf_size = 0; + } + + /* fill ipc msg buffer */ + if (cur_item->size - msg_cur_pos > + HECI_IPC_PAYLOAD_SIZE - buf_size) { + copy_size = HECI_IPC_PAYLOAD_SIZE - buf_size; + } else { + copy_size = cur_item->size - msg_cur_pos; + } + + memcpy(msg.payload + buf_size, cur_item->buf + msg_cur_pos, + copy_size); + + msg_cur_pos += copy_size; + buf_size += copy_size; + } + + /* leftovers ? send last msg */ + if (buf_size != 0) { + msg.hdr.length = buf_size; + msg.hdr.length |= (uint16_t)1 << HECI_MSG_CMPL_SHIFT; + + heci_send_heci_msg(&msg); + } + + atomic_sub(&connect->flow_ctrl_creds, 1); + +err_locked: + mutex_unlock(&connect->lock); + + return total_size; + +} + +/* For now, we only support fixed client payload size < IPC payload size */ +int heci_send_fixed_client_msg(const uint8_t fw_addr, uint8_t *buf, + const size_t buf_size) +{ + struct heci_msg msg; + + heci_build_fixed_client_header(&msg.hdr, fw_addr, buf_size); + + memcpy(msg.payload, buf, buf_size); + + heci_send_heci_msg(&msg); + + return EC_SUCCESS; +} + +static int handle_version_req(struct hbm_version_req *ver_req) +{ + struct hbm_version_res *ver_res; + struct heci_msg heci_msg; + struct hbm_i2h *i2h; + + heci_build_hbm_header(&heci_msg.hdr, + sizeof(i2h->cmd) + sizeof(*ver_res)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_VERSION_RESP; + ver_res = (struct hbm_version_res *)&i2h->data; + + memset(ver_res, 0, sizeof(*ver_res)); + + ver_res->version.major = HBM_MAJOR_VERSION; + ver_res->version.minor = HBM_MINOR_VERSION; + if (ver_req->version.major == HBM_MAJOR_VERSION && + ver_req->version.minor == HBM_MINOR_VERSION) { + ver_res->supported = 1; + } else { + ver_res->supported = 0; + } + + heci_send_heci_msg(&heci_msg); + + return EC_SUCCESS; +} + +#define BITS_PER_BYTE 8 +/* get number of bits for one element of "valid_addresses" array */ +#define BITS_PER_ELEMENT \ + (sizeof(((struct hbm_enum_res *)0)->valid_addresses[0]) * BITS_PER_BYTE) + +static int handle_enum_req(struct hbm_enum_req *enum_req) +{ + struct hbm_enum_res *enum_res; + struct heci_msg heci_msg; + struct hbm_i2h *i2h; + int i; + + heci_build_hbm_header(&heci_msg.hdr, + sizeof(i2h->cmd) + sizeof(*enum_res)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_HOST_ENUM_RESP; + enum_res = (struct hbm_enum_res *)&i2h->data; + + memset(enum_res, 0, sizeof(*enum_res)); + + /* + * current host driver implementation doesn't allow to use bit0 + * due to its fw address generation scheme. + * bit-0 set -> fw address "0", bit-1 set -> fw address "1" + * but fw address "0" is used for HECI bus management only. + * so the bitmap should not use bit-0 + */ + for (i = 0; i < heci_bus_ctx.num_of_clients; i++) { + enum_res->valid_addresses[(i + 1) / BITS_PER_ELEMENT] |= + 1 << ((i + 1) & (BITS_PER_ELEMENT - 1)); + } + + heci_send_heci_msg(&heci_msg); + + return EC_SUCCESS; +} + +static int handle_client_prop_req(struct hbm_client_prop_req *client_prop_req) +{ + struct hbm_client_prop_res *client_prop_res; + struct heci_msg heci_msg; + struct hbm_i2h *i2h; + struct heci_client_context *client_ctx; + const struct heci_client *client; + + heci_build_hbm_header(&heci_msg.hdr, + sizeof(i2h->cmd) + sizeof(*client_prop_res)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_HOST_CLIENT_PROP_RESP; + client_prop_res = (struct hbm_client_prop_res *)&i2h->data; + + memset(client_prop_res, 0, sizeof(*client_prop_res)); + + client_prop_res->address = client_prop_req->address; + if (!heci_is_valid_client_addr(client_prop_req->address)) { + client_prop_res->status = HECI_CONNECT_STATUS_CLIENT_NOT_FOUND; + } else { + struct hbm_client_properties *client_prop; + + client_ctx = heci_get_client_context(client_prop_req->address); + client = client_ctx->client; + client_prop = &client_prop_res->client_prop; + + client_prop->protocol_name = client->protocol_id; + client_prop->protocol_version = client->protocol_ver; + client_prop->max_number_of_connections = + client->max_n_of_connections; + client_prop->max_msg_length = client->max_msg_size; + client_prop->dma_hdr_len = client->dma_header_length; + client_prop->dma_hdr_len |= client->dma_enabled ? + CLIENT_DMA_ENABLE : 0; + } + + heci_send_heci_msg(&heci_msg); + + return EC_SUCCESS; +} + +static int heci_send_flow_control(uint8_t fw_addr) +{ + struct heci_client_connect *connect; + struct hbm_i2h *i2h; + struct hbm_flow_control *flow_ctrl; + struct heci_msg heci_msg; + + connect = heci_get_client_connect(fw_addr); + + heci_build_hbm_header(&heci_msg.hdr, + sizeof(i2h->cmd) + sizeof(*flow_ctrl)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_FLOW_CONTROL; + flow_ctrl = (struct hbm_flow_control *)&i2h->data; + + memset(flow_ctrl, 0, sizeof(*flow_ctrl)); + + flow_ctrl->fw_addr = fw_addr; + flow_ctrl->host_addr = connect->host_addr; + + heci_send_heci_msg(&heci_msg); + + return EC_SUCCESS; +} + +static int handle_client_connect_req( + struct hbm_client_connect_req *client_connect_req) +{ + struct hbm_client_connect_res *client_connect_res; + struct heci_msg heci_msg; + struct hbm_i2h *i2h; + struct heci_client_connect *connect; + + heci_build_hbm_header(&heci_msg.hdr, + sizeof(i2h->cmd) + sizeof(*client_connect_res)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_CLIENT_CONNECT_RESP; + client_connect_res = (struct hbm_client_connect_res *)&i2h->data; + + memset(client_connect_res, 0, sizeof(*client_connect_res)); + + client_connect_res->fw_addr = client_connect_req->fw_addr; + client_connect_res->host_addr = client_connect_req->host_addr; + if (!heci_is_valid_client_addr(client_connect_req->fw_addr)) { + client_connect_res->status = + HECI_CONNECT_STATUS_CLIENT_NOT_FOUND; + } else { + connect = heci_get_client_connect(client_connect_req->fw_addr); + if (connect->is_connected) { + client_connect_res->status = + HECI_CONNECT_STATUS_ALREADY_EXISTS; + } else { + connect->is_connected = 1; + connect->host_addr = client_connect_req->host_addr; + } + } + + heci_send_heci_msg(&heci_msg); + heci_send_flow_control(client_connect_req->fw_addr); + + return EC_SUCCESS; +} + +static int handle_flow_control_cmd(struct hbm_flow_control *flow_ctrl) +{ + struct heci_client_connect *connect; + + if (!heci_is_valid_client_addr(flow_ctrl->fw_addr)) + return -1; + + if (!heci_is_client_connected(flow_ctrl->fw_addr)) + return -1; + + connect = heci_get_client_connect(flow_ctrl->fw_addr); + atomic_add(&connect->flow_ctrl_creds, 1); + + return EC_SUCCESS; +} + +static void heci_handle_client_msg(struct heci_msg *msg, size_t length) +{ + struct heci_client_context *cli_ctx; + struct heci_client_connect *connect; + const struct heci_client_callbacks *cbs; + int payload_size; + + if (!heci_is_valid_client_addr(msg->hdr.fw_addr)) + return; + + if (!heci_is_client_connected(msg->hdr.fw_addr)) + return; + + cli_ctx = heci_get_client_context(msg->hdr.fw_addr); + cbs = cli_ctx->client->cbs; + connect = &cli_ctx->connect; + + payload_size = HECI_MSG_LENGTH(msg->hdr.length); + if (connect->is_connected && + msg->hdr.host_addr == connect->host_addr) { + if (!connect->ignore_rx_msg && + connect->rx_msg_length + payload_size > HECI_MAX_MSG_SIZE) { + connect->ignore_rx_msg = 1; /* too big. discard */ + } + + if (!connect->ignore_rx_msg) { + memcpy(connect->rx_msg + connect->rx_msg_length, + msg->payload, payload_size); + + connect->rx_msg_length += payload_size; + } + + if (HECI_MSG_IS_COMPLETED(msg->hdr.length)) { + if (!connect->ignore_rx_msg) { + cbs->new_msg_received( + TO_HECI_HANDLE(msg->hdr.fw_addr), + connect->rx_msg, + connect->rx_msg_length); + } + + connect->rx_msg_length = 0; + connect->ignore_rx_msg = 0; + + heci_send_flow_control(msg->hdr.fw_addr); + } + } +} + +static int handle_client_disconnect_req( + struct hbm_client_disconnect_req *client_disconnect_req) +{ + struct hbm_client_disconnect_res *client_disconnect_res; + struct heci_msg heci_msg; + struct hbm_i2h *i2h; + struct heci_client_context *cli_ctx; + struct heci_client_connect *connect; + const struct heci_client_callbacks *cbs; + uint8_t fw_addr; + + heci_build_hbm_header(&heci_msg.hdr, sizeof(i2h->cmd) + + sizeof(*client_disconnect_res)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_CLIENT_DISCONNECT_RESP; + client_disconnect_res = (struct hbm_client_disconnect_res *)&i2h->data; + + memset(client_disconnect_res, 0, sizeof(*client_disconnect_res)); + + fw_addr = client_disconnect_req->fw_addr; + + client_disconnect_res->fw_addr = fw_addr; + client_disconnect_res->host_addr = client_disconnect_req->host_addr; + if (!heci_is_valid_client_addr(fw_addr) || + !heci_is_client_connected(fw_addr)) { + client_disconnect_res->status = + HECI_CONNECT_STATUS_CLIENT_NOT_FOUND; + } else { + connect = heci_get_client_connect(fw_addr); + cli_ctx = heci_get_client_context(fw_addr); + cbs = cli_ctx->client->cbs; + mutex_lock(&connect->lock); + if (connect->is_connected) { + cbs->disconnected(TO_HECI_HANDLE(fw_addr)); + connect->is_connected = 0; + } + mutex_unlock(&connect->lock); + } + + heci_send_heci_msg(&heci_msg); + + return EC_SUCCESS; +} + +/* host stops due to version mismatch */ +static int handle_host_stop_req(struct hbm_host_stop_req *host_stop_req) +{ + struct hbm_host_stop_res *host_stop_res; + struct heci_msg heci_msg; + struct hbm_i2h *i2h; + + heci_build_hbm_header(&heci_msg.hdr, + sizeof(i2h->cmd) + sizeof(*host_stop_res)); + + i2h = (struct hbm_i2h *)heci_msg.payload; + i2h->cmd = HECI_BUS_MSG_HOST_STOP_RESP; + host_stop_res = (struct hbm_host_stop_res *)&i2h->data; + + memset(host_stop_res, 0, sizeof(*host_stop_res)); + + heci_send_heci_msg(&heci_msg); + + return EC_SUCCESS; +} + +static int is_hbm_validity(struct hbm_h2i *h2i, size_t length) +{ + int valid_msg_len; + + valid_msg_len = sizeof(h2i->cmd); + + switch (h2i->cmd) { + case HECI_BUS_MSG_VERSION_REQ: + valid_msg_len += sizeof(struct hbm_version_req); + break; + + case HECI_BUS_MSG_HOST_ENUM_REQ: + valid_msg_len += sizeof(struct hbm_enum_req); + break; + + case HECI_BUS_MSG_HOST_CLIENT_PROP_REQ: + valid_msg_len += sizeof(struct hbm_client_prop_req); + break; + + case HECI_BUS_MSG_CLIENT_CONNECT_REQ: + valid_msg_len += sizeof(struct hbm_client_connect_req); + break; + + case HECI_BUS_MSG_FLOW_CONTROL: + valid_msg_len += sizeof(struct hbm_flow_control); + break; + + case HECI_BUS_MSG_CLIENT_DISCONNECT_REQ: + valid_msg_len += sizeof(struct hbm_client_disconnect_req); + break; + + case HECI_BUS_MSG_HOST_STOP_REQ: + valid_msg_len += sizeof(struct hbm_host_stop_req); + break; + +/* TODO: DMA support for large data */ +#if 0 + case HECI_BUS_MSG_DMA_REQ: + valid_msg_len += sizeof(struct hbm_dma_req); + break; + + case HECI_BUS_MSG_DMA_ALLOC_NOTIFY: + valid_msg_len += sizeof(struct hbm_dma_alloc_notify); + break; + + case HECI_BUS_MSG_DMA_XFER_REQ: /* DMA transfer to FW */ + valid_msg_len += sizeof(struct hbm_dma_xfer_req); + break; + + case HECI_BUS_MSG_DMA_XFER_RESP: /* Ack for DMA transfer from FW */ + valid_msg_len += sizeof(struct hbm_dma_xfer_resp); + break; +#endif + default: + break; + } + + if (valid_msg_len != length) { + CPRINTF("invalid cmd(%d) valid : %d, cur : %d\n", + valid_msg_len, length); + /* TODO: invalid cmd. not sure to reply with error ? */ + return 0; + } + + return 1; +} + +static void heci_handle_hbm(struct hbm_h2i *h2i, size_t length) +{ + void *data = (void *)&h2i->data; + + if (!is_hbm_validity(h2i, length)) + return; + + switch (h2i->cmd) { + case HECI_BUS_MSG_VERSION_REQ: + handle_version_req((struct hbm_version_req *)data); + break; + + case HECI_BUS_MSG_HOST_ENUM_REQ: + handle_enum_req((struct hbm_enum_req *)data); + break; + + case HECI_BUS_MSG_HOST_CLIENT_PROP_REQ: + handle_client_prop_req((struct hbm_client_prop_req *)data); + break; + + case HECI_BUS_MSG_CLIENT_CONNECT_REQ: + handle_client_connect_req( + (struct hbm_client_connect_req *)data); + break; + + case HECI_BUS_MSG_FLOW_CONTROL: + handle_flow_control_cmd((struct hbm_flow_control *)data); + break; + + case HECI_BUS_MSG_CLIENT_DISCONNECT_REQ: + handle_client_disconnect_req( + (struct hbm_client_disconnect_req *)data); + break; + + case HECI_BUS_MSG_HOST_STOP_REQ: + handle_host_stop_req((struct hbm_host_stop_req *)data); + break; + +/* TODO: DMA transfer if data is too big >= ? KB */ +#if 0 + case HECI_BUS_MSG_DMA_REQ: + handle_dma_req((struct hbm_dma_req *)data); + break; + + case HECI_BUS_MSG_DMA_ALLOC_NOTIFY: + handle_dma_alloc_notify((struct hbm_dma_alloc_notify *)); + break; + + case HECI_BUS_MSG_DMA_XFER_REQ: /* DMA transfer to FW */ + handle_dma_xfer_req((struct hbm_dma_xfer_req *)data); + break; + + case HECI_BUS_MSG_DMA_XFER_RESP: /* Ack for DMA transfer from FW */ + handle_dma_xfer_resp((struct hbm_dma_xfer_resp *)data); + break; +#endif + default: + break; + } +} + +static void heci_handle_heci_msg(struct heci_msg *heci_msg, size_t msg_length) +{ + if (heci_msg->hdr.fw_addr) { + if (heci_msg->hdr.fw_addr == HECI_FIXED_SYSTEM_STATE_ADDR) + heci_handle_system_state_msg( + heci_msg->payload, + HECI_MSG_LENGTH(heci_msg->hdr.length) + ); + else + heci_handle_client_msg(heci_msg, msg_length); + } else { + heci_handle_hbm((struct hbm_h2i *)heci_msg->payload, + HECI_MSG_LENGTH(heci_msg->hdr.length)); + } +} + +/* event flag for HECI msg */ +#define EVENT_FLAG_BIT_HECI_MSG TASK_EVENT_CUSTOM(1) + +void heci_rx_task(void) +{ + int msg_len; + struct heci_msg heci_msg; + ipc_handle_t ipc_handle; + + /* open IPC for HECI protocol */ + heci_bus_ctx.ipc_handle = ipc_open(IPC_PEER_ID_HOST, IPC_PROTOCOL_HECI, + EVENT_FLAG_BIT_HECI_MSG); + + ASSERT(heci_bus_ctx.ipc_handle != IPC_INVALID_HANDLE); + + /* get ipc handle */ + ipc_handle = heci_bus_ctx.ipc_handle; + + while (1) { + /* task will be blocked here, waiting for event */ + msg_len = ipc_read(ipc_handle, &heci_msg, sizeof(heci_msg), -1); + + if (msg_len <= 0) { + CPRINTS("discard heci packet"); + continue; + } + + if (HECI_MSG_LENGTH(heci_msg.hdr.length) + sizeof(heci_msg.hdr) + == msg_len) + heci_handle_heci_msg(&heci_msg, msg_len); + else + CPRINTS("msg len mismatch.. discard.."); + } +} diff --git a/chip/ish/heci_client.h b/chip/ish/heci_client.h new file mode 100644 index 0000000000..00f4753951 --- /dev/null +++ b/chip/ish/heci_client.h @@ -0,0 +1,102 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __HECI_CLIENT_H +#define __HECI_CLIENT_H + +#include <stdint.h> +#include <stddef.h> + +#include "hooks.h" + +#define HECI_MAX_NUM_OF_CLIENTS 2 + +#define HECI_MAX_MSG_SIZE 4960 +#define HECI_MAX_MSGS 3 + +enum HECI_ERR { + HECI_ERR_TOO_MANY_MSG_ITEMS = EC_ERROR_INTERNAL_FIRST + 0, + HECI_ERR_NO_CRED_FROM_CLIENT_IN_HOST = EC_ERROR_INTERNAL_FIRST + 1, + HECI_ERR_CLIENT_IS_NOT_CONNECTED = EC_ERROR_INTERNAL_FIRST + 2, +}; + +typedef void * heci_handle_t; + +#define HECI_INVALID_HANDLE NULL + +struct heci_guid { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; +}; + +struct heci_client_callbacks { + /* + * called while registering heci client. + * if returns non-zero, the registration will fail. + */ + int (*initialize)(const heci_handle_t handle); + /* called when new heci msg for the client is arrived */ + void (*new_msg_received)(const heci_handle_t handle, uint8_t *msg, + const size_t msg_size); + /* called when the heci client is disconnected */ + void (*disconnected)(const heci_handle_t handle); + + /* called when ISH goes to suspend */ + int (*suspend)(const heci_handle_t); + /* called when ISH resumes */ + int (*resume)(const heci_handle_t); +}; + +struct heci_client { + struct heci_guid protocol_id; + uint32_t max_msg_size; + uint8_t protocol_ver; + uint8_t max_n_of_connections; + uint8_t dma_header_length :7; + uint8_t dma_enabled :1; + + const struct heci_client_callbacks *cbs; +}; + +struct heci_msg_item { + size_t size; + uint8_t *buf; +}; + +struct heci_msg_list { + int num_of_items; + struct heci_msg_item *items[HECI_MAX_MSGS]; +}; + +/* + * Do not call this function directly. + * The function should be called only by HECI_CLIENT_ENTRY() + */ +heci_handle_t heci_register_client(const struct heci_client *client); +int heci_set_client_data(const heci_handle_t handle, void *data); +void *heci_get_client_data(const heci_handle_t handle); +/* send client msg */ +int heci_send_msg(const heci_handle_t handle, uint8_t *buf, + const size_t buf_size); +/* + * send client msgs(using list of buffer&size). + * heci_msg_item with size == 0 is not acceptable. + */ +int heci_send_msgs(const heci_handle_t handle, + const struct heci_msg_list *msg_list); +/* send msg to fixed client(system level client) */ +int heci_send_fixed_client_msg(const uint8_t fw_addr, uint8_t *buf, + const size_t buf_size); + +#define HECI_CLIENT_ENTRY(heci_client) \ + void _heci_entry_##heci_client(void) \ + { \ + heci_register_client(&(heci_client)); \ + } \ + DECLARE_HOOK(HOOK_INIT, _heci_entry_##heci_client, HOOK_PRIO_LAST - 1) + +#endif /* __HECI_CLIENT_H */ diff --git a/chip/ish/system_state.h b/chip/ish/system_state.h new file mode 100644 index 0000000000..20de1aaf4b --- /dev/null +++ b/chip/ish/system_state.h @@ -0,0 +1,31 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __SYSTEM_STATE_H +#define __SYSTEM_STATE_H + +#define HECI_FIXED_SYSTEM_STATE_ADDR 13 + +struct ss_subsys_device; + +struct system_state_callbacks { + int (*resume)(struct ss_subsys_device *ss_device); + int (*suspend)(struct ss_subsys_device *ss_device); +}; + +struct ss_subsys_device { + struct system_state_callbacks *cbs; +}; + +/* register system state client */ +int ss_subsys_register_client(struct ss_subsys_device *ss_device); + +/* + * this function is called by HECI layer when there's a message for + * system state subsystem + */ +void heci_handle_system_state_msg(uint8_t *msg, const size_t length); + +#endif /* __SYSTEM_STATE_H */ diff --git a/chip/ish/system_state_subsys.c b/chip/ish/system_state_subsys.c new file mode 100644 index 0000000000..bb5b62dce3 --- /dev/null +++ b/chip/ish/system_state_subsys.c @@ -0,0 +1,149 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "heci_client.h" +#include "system_state.h" +#include "console.h" + +#ifdef SS_SUBSYSTEM_DEBUG +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args) +#else +#define CPUTS(outstr) +#define CPRINTS(format, args...) +#define CPRINTF(format, args...) +#endif + + +/* the following "define"s and structures are from host driver + * and they are slightly modified for look&feel purpose. + */ +#define SYSTEM_STATE_SUBSCRIBE 0x1 +#define SYSTEM_STATE_STATUS 0x2 +#define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3 +#define SYSTEM_STATE_STATE_CHANGE_REQ 0x4 + +#define SUSPEND_STATE_BIT (1<<1) /* suspend/resume */ + +struct ss_header { + uint32_t cmd; + uint32_t cmd_status; +} __packed; + +struct ss_query_subscribers { + struct ss_header hdr; +} __packed; + +struct ss_subscribe { + struct ss_header hdr; + uint32_t states; +} __packed; + +struct ss_status { + struct ss_header hdr; + uint32_t supported_states; + uint32_t states_status; +} __packed; + +/* change request from device but host doesn't support it */ +struct ss_state_change_req { + struct ss_header hdr; + uint32_t requested_states; + uint32_t states_status; +} __packed; + +/* + * TODO: For now, every HECI client with valid .suspend or .resume callback is + * automatically registered as client of system state subsystem. + * so MAX_SS_CLIENTS should be HECI_MAX_NUM_OF_CLIENTS. + * if an object wants to get system state event then it can embeds + * "struct ss_subsys_device" in it and calls ss_subsys_register_client() like + * HECI client. + */ +#define MAX_SS_CLIENTS HECI_MAX_NUM_OF_CLIENTS + +struct ss_subsystem_context { + uint32_t registered_state; + + int num_of_ss_client; + struct ss_subsys_device *clients[MAX_SS_CLIENTS]; +}; + +static struct ss_subsystem_context ss_subsys_ctx; + +int ss_subsys_register_client(struct ss_subsys_device *ss_device) +{ + int handle; + + if (ss_subsys_ctx.num_of_ss_client == MAX_SS_CLIENTS) + return -1; + + if (ss_device->cbs->resume || ss_device->cbs->suspend) { + handle = ss_subsys_ctx.num_of_ss_client++; + ss_subsys_ctx.registered_state |= SUSPEND_STATE_BIT; + ss_subsys_ctx.clients[handle] = ss_device; + } else { + return -1; + } + + return handle; +} + +static int ss_subsys_suspend(void) +{ + int i; + + for (i = ss_subsys_ctx.num_of_ss_client - 1; i >= 0; i--) { + if (ss_subsys_ctx.clients[i]->cbs->suspend) + ss_subsys_ctx.clients[i]->cbs->suspend( + ss_subsys_ctx.clients[i]); + } + + return EC_SUCCESS; +} + +static int ss_subsys_resume(void) +{ + int i; + + for (i = 0; i < ss_subsys_ctx.num_of_ss_client; i++) { + if (ss_subsys_ctx.clients[i]->cbs->resume) + ss_subsys_ctx.clients[i]->cbs->resume( + ss_subsys_ctx.clients[i]); + } + + return EC_SUCCESS; +} + +void heci_handle_system_state_msg(uint8_t *msg, const size_t length) +{ + struct ss_header *hdr = (struct ss_header *)msg; + struct ss_subscribe subscribe; + struct ss_status *status; + + switch (hdr->cmd) { + case SYSTEM_STATE_QUERY_SUBSCRIBERS: + subscribe.hdr.cmd = SYSTEM_STATE_SUBSCRIBE; + subscribe.hdr.cmd_status = 0; + subscribe.states = ss_subsys_ctx.registered_state; + + heci_send_fixed_client_msg(HECI_FIXED_SYSTEM_STATE_ADDR, + (uint8_t *)&subscribe, + sizeof(subscribe)); + + break; + case SYSTEM_STATE_STATUS: + status = (struct ss_status *)msg; + if (status->supported_states & SUSPEND_STATE_BIT) { + if (status->states_status & SUSPEND_STATE_BIT) + ss_subsys_suspend(); + else + ss_subsys_resume(); + } + + break; + } +} |