subsys/mgmt/ec_host_cmd: rework Host Command support
Rework the Host Command support. It includes: -change API to backend -change a way of defining rx and tx buffers -fix synchronization between the handler and backend layer -simplify the HC handler Signed-off-by: Dawid Niedzwiecki <dawidn@google.com>
This commit is contained in:
parent
b2674a4b34
commit
2d0a784c41
|
@ -1314,6 +1314,17 @@ Release Notes:
|
|||
labels:
|
||||
- "area: Virtualization"
|
||||
|
||||
EC Host Commands:
|
||||
status: maintained
|
||||
maintainers:
|
||||
- semihalf-niedzwiecki-dawid
|
||||
files:
|
||||
- subsys/mgmt/ec_host_cmd/
|
||||
- include/zephyr/mgmt/ec_host_cmd/
|
||||
- tests/subsys/mgmt/ec_host_cmd/
|
||||
labels:
|
||||
- "area: ec_host_cmd"
|
||||
|
||||
Xen Platform:
|
||||
status: maintained
|
||||
maintainers:
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
zephyr,flash = &flash0;
|
||||
zephyr,entropy = &rng;
|
||||
zephyr,flash-controller = &flashcontroller0;
|
||||
zephyr,ec-host-interface = &hcp;
|
||||
zephyr,display = &sdl_dc;
|
||||
zephyr,canbus = &can_loopback0;
|
||||
zephyr,keyboard-scan = &sdl_kscan;
|
||||
|
@ -156,11 +155,6 @@
|
|||
compatible = "zephyr,native-posix-counter";
|
||||
};
|
||||
|
||||
hcp: ec-host-cmd-backend {
|
||||
status = "okay";
|
||||
compatible = "zephyr,sim-ec-host-cmd-backend";
|
||||
};
|
||||
|
||||
gpio0: gpio@800 {
|
||||
status = "okay";
|
||||
compatible = "zephyr,gpio-emul";
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
.. _ec_host_cmd_backend_api:
|
||||
|
||||
EC Host Command
|
||||
###############
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
|
||||
API Reference
|
||||
*************
|
||||
|
||||
.. doxygengroup:: ec_host_cmd_backend_interface
|
|
@ -15,7 +15,6 @@ Peripherals
|
|||
dac.rst
|
||||
display/index.rst
|
||||
dma.rst
|
||||
ec_host_cmd.rst
|
||||
edac/index.rst
|
||||
eeprom.rst
|
||||
entropy.rst
|
||||
|
|
BIN
doc/services/device_mgmt/ec_host_cmd.png
Normal file
BIN
doc/services/device_mgmt/ec_host_cmd.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
67
doc/services/device_mgmt/ec_host_cmd.rst
Normal file
67
doc/services/device_mgmt/ec_host_cmd.rst
Normal file
|
@ -0,0 +1,67 @@
|
|||
.. _ec_host_cmd_backend_api:
|
||||
|
||||
EC Host Command
|
||||
###############
|
||||
|
||||
Overview
|
||||
********
|
||||
The host command protocol defines the interface for a host, or application processor, to
|
||||
communicate with a target embedded controller (EC). The EC Host command subsystem implements the
|
||||
target side of the protocol, generating responses to commands sent by the host. The host command
|
||||
protocol interface supports multiple versions, but this subsystem implementation only support
|
||||
protocol version 3.
|
||||
|
||||
Architecture
|
||||
************
|
||||
The Host Command subsystem contains a few components:
|
||||
* Backend
|
||||
* General handler
|
||||
* Command handler
|
||||
|
||||
The backend is a layer between a peripheral driver and the general handler. It is responsible for
|
||||
sending and receiving commands via chosen peripheral.
|
||||
|
||||
The general handler validates data from the backend e.g. check sizes, checksum, etc. If the command
|
||||
is valid and the user has provided a handler for a received command id, the command handler is
|
||||
called.
|
||||
|
||||
.. image:: ec_host_cmd.png
|
||||
:align: center
|
||||
|
||||
SHI (Serial Host Interface) is different to this because it is used olny for communication with a
|
||||
host. SHI does not have API itself, thus the backend and peripheral driver layers are combined into
|
||||
one backend layer.
|
||||
|
||||
.. image:: ec_host_cmd_shi.png
|
||||
:align: center
|
||||
|
||||
The supported backend and peripheral drivers:
|
||||
* Simulator
|
||||
* SHI - ITE and NPCX
|
||||
* eSPI - any eSPI slave driver that support :kconfig:option:`CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD` and
|
||||
:kconfig:option:`CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE`
|
||||
|
||||
Initialization
|
||||
**************
|
||||
|
||||
If the application configures the ``zephyr,host-cmd-backend`` chosen node, then the backend
|
||||
automatically initializes the host command subsystem by calling :c:func:`ec_host_cmd_init`.
|
||||
|
||||
If ``zephyr,host-cmd-backend`` is not chosen, the :c:func:`ec_host_cmd_init` function should be
|
||||
called by application code. This way of initialization is useful if a backend is chosen in runtime
|
||||
based on e.g. GPIO state.
|
||||
|
||||
Buffers
|
||||
*******
|
||||
|
||||
The host command communication requires buffers for rx and tx. The buffers are be provided by the
|
||||
general handler if :kconfig:option:`CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER` > 0 for rx buffer and
|
||||
:kconfig:option:`CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER` > 0 for the tx buffer. The shared buffers are
|
||||
useful for applications that use multiple backends. Defining separate buffers by every backend would
|
||||
increase the memory usage. However, some buffers can be defined by a peripheral driver e.g. eSPI.
|
||||
These ones should be reused as much as possible.
|
||||
|
||||
API Reference
|
||||
*************
|
||||
|
||||
.. doxygengroup:: ec_host_cmd_interface
|
BIN
doc/services/device_mgmt/ec_host_cmd_shi.png
Normal file
BIN
doc/services/device_mgmt/ec_host_cmd_shi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
|
@ -13,6 +13,7 @@ Device Management
|
|||
smp_transport.rst
|
||||
dfu.rst
|
||||
ota.rst
|
||||
ec_host_cmd.rst
|
||||
|
||||
SMP Groups
|
||||
==========
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Host Command Backend using the eSPI bus
|
||||
|
||||
compatible: "zephyr,ec-host-cmd-backend-espi"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
bus:
|
||||
required: true
|
||||
type: phandle
|
||||
description:
|
||||
Phandle to the eSPI bus which will be used for communication with AP
|
||||
by the host commands subsystem
|
|
@ -1,7 +0,0 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Simulated Host Command Peripheral
|
||||
|
||||
compatible: "zephyr,sim-ec-host-cmd-backend"
|
||||
|
||||
include: base.yaml
|
|
@ -21,47 +21,88 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ec_host_cmd_backend {
|
||||
/** API provided by the backed. */
|
||||
const struct ec_host_cmd_backend_api *api;
|
||||
/** Context for the backed. */
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Host Command Backend API
|
||||
* @defgroup ec_host_cmd_backend Host Command Backend API
|
||||
* @brief EC Host Command Interface
|
||||
* @defgroup ec_host_cmd_interface EC Host Command Interface
|
||||
* @ingroup io_interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Context for host command backend and framework to pass rx data
|
||||
* @brief Context for host command backend and handler to pass rx data.
|
||||
*/
|
||||
struct ec_host_cmd_rx_ctx {
|
||||
/** Buffer written to by device (when dev_owns) and read from by
|
||||
* command framework and handler (when handler_owns). Buffer is owned
|
||||
* by devices and lives as long as device is valid. Device will never
|
||||
* read from this buffer (for security reasons).
|
||||
/**
|
||||
* Buffer to hold received data. The buffer is provided by the handler if
|
||||
* CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER > 0. Otherwise, the backend should provide
|
||||
* the buffer on its own and overwrites @a buf pointer in the init function.
|
||||
*/
|
||||
uint8_t *buf;
|
||||
/** Number of bytes written to @a buf by device (when dev_owns). */
|
||||
size_t *len;
|
||||
/** Device will take when it needs to write to @a buf and @a size. */
|
||||
struct k_sem *dev_owns;
|
||||
/** Handler will take so it can read @a buf and @a size */
|
||||
struct k_sem *handler_owns;
|
||||
/** Number of bytes written to @a buf by backend. */
|
||||
size_t len;
|
||||
/**
|
||||
* The backend gives @a handler_owns, when data in @a buf are ready.
|
||||
* The handler takes @a handler_owns to read data in @a buf.
|
||||
*/
|
||||
struct k_sem handler_owns;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Context for host command backend and framework to pass tx data
|
||||
* @brief Context for host command backend and handler to pass tx data
|
||||
*/
|
||||
struct ec_host_cmd_tx_buf {
|
||||
/** Data to write to the host */
|
||||
/**
|
||||
* Data to write to the host The buffer is provided by the handler if
|
||||
* CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER > 0. Otherwise, the backend should provide
|
||||
* the buffer on its own and overwrites @a buf pointer and @a len_max
|
||||
* in the init function.
|
||||
*/
|
||||
void *buf;
|
||||
/** Number of bytes to write from @a buf */
|
||||
/** Number of bytes to write from @a buf. */
|
||||
size_t len;
|
||||
/** Size of @a buf. */
|
||||
size_t len_max;
|
||||
};
|
||||
|
||||
typedef int (*ec_host_cmd_backend_api_init)(
|
||||
const struct device *dev, struct ec_host_cmd_rx_ctx *rx_ctx);
|
||||
/**
|
||||
* @brief Initialize a host command backend
|
||||
*
|
||||
* This routine initializes a host command backend. It includes initialization
|
||||
* a device used to communication and setting up buffers.
|
||||
* This function is called by the ec_host_cmd_init function.
|
||||
*
|
||||
* @param[in] backend Pointer to the backend structure for the driver instance.
|
||||
* @param[in,out] rx_ctx Pointer to the receive context object. These objects are used to receive
|
||||
* data from the driver when the host sends data. The buf member can be
|
||||
* assigned by the backend.
|
||||
* @param[in,out] tx Pointer to the transmit buffer object. The buf and len_max members can be
|
||||
* assigned by the backend. These objects are used to send data by the
|
||||
* backend with the ec_host_cmd_backend_api_send function.
|
||||
*
|
||||
* @retval 0 if successful
|
||||
*/
|
||||
typedef int (*ec_host_cmd_backend_api_init)(const struct ec_host_cmd_backend *backend,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx,
|
||||
struct ec_host_cmd_tx_buf *tx);
|
||||
|
||||
typedef int (*ec_host_cmd_backend_api_send)(
|
||||
const struct device *dev,
|
||||
const struct ec_host_cmd_tx_buf *tx_buf);
|
||||
/**
|
||||
* @brief Sends data to the host
|
||||
*
|
||||
* Sends data from tx buf that was passed via ec_host_cmd_backend_api_init
|
||||
* function.
|
||||
*
|
||||
* @param backend Pointer to the backed to send data.
|
||||
*
|
||||
* @retval 0 if successful.
|
||||
*/
|
||||
typedef int (*ec_host_cmd_backend_api_send)(const struct ec_host_cmd_backend *backend);
|
||||
|
||||
__subsystem struct ec_host_cmd_backend_api {
|
||||
ec_host_cmd_backend_api_init init;
|
||||
|
@ -69,50 +110,30 @@ __subsystem struct ec_host_cmd_backend_api {
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize a host command device
|
||||
* @brief Get the eSPI Host Command backend pointer
|
||||
*
|
||||
* This routine initializes a host command device, prior to its first use. The
|
||||
* receive context object are an output of this function and are valid
|
||||
* for the lifetime of this device. The RX context is used by the client to
|
||||
* receive data from the host.
|
||||
* Get the eSPI pointer backend and pass a pointer to eSPI device instance that will be used for
|
||||
* the Host Command communication.
|
||||
*
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
* @param rx_ctx [out] The receiving context object that are valid for the
|
||||
* lifetime of the device. These objects are used to receive data
|
||||
* from the driver when the host send data.
|
||||
* @param dev Pointer to eSPI device instance.
|
||||
*
|
||||
* @retval 0 if successful
|
||||
* @retval The eSPI backend pointer.
|
||||
*/
|
||||
static inline int
|
||||
ec_host_cmd_backend_init(const struct device *dev,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx)
|
||||
{
|
||||
const struct ec_host_cmd_backend_api *api =
|
||||
(const struct ec_host_cmd_backend_api *)dev->api;
|
||||
|
||||
return api->init(dev, rx_ctx);
|
||||
}
|
||||
struct ec_host_cmd_backend *ec_host_cmd_backend_get_espi(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Sends the specified data to the host
|
||||
* @brief Get the SHI NPCX Host Command backend pointer
|
||||
*
|
||||
* Sends the data specified in @a tx_buf to the host over the host communication
|
||||
* bus.
|
||||
*
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
* @param tx_buf The data to transmit to the host.
|
||||
*
|
||||
* @retval 0 if successful
|
||||
* @retval the SHI NPCX backend pointer
|
||||
*/
|
||||
static inline int ec_host_cmd_backend_send(
|
||||
const struct device *dev,
|
||||
const struct ec_host_cmd_tx_buf *tx_buf)
|
||||
{
|
||||
const struct ec_host_cmd_backend_api *api =
|
||||
(const struct ec_host_cmd_backend_api *)dev->api;
|
||||
struct ec_host_cmd_backend *ec_host_cmd_backend_get_shi_npcx(void);
|
||||
|
||||
return api->send(dev, tx_buf);
|
||||
}
|
||||
/**
|
||||
* @brief Get the SHI ITE Host Command backend pointer
|
||||
*
|
||||
* @retval the SHI ITE backend pointer
|
||||
*/
|
||||
struct ec_host_cmd_backend *ec_host_cmd_backend_get_shi_ite(void);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
@ -122,4 +143,4 @@ static inline int ec_host_cmd_backend_send(
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_MGMT_EC_HOST_CMD_BACKEND_H_ */
|
||||
#endif /* ZEPHYR_INCLUDE_MGMT_EC_HOST_CMD_EC_HOST_CMD_BACKEND_H_ */
|
||||
|
|
|
@ -9,14 +9,23 @@
|
|||
|
||||
/**
|
||||
* @brief EC Host Command Interface
|
||||
* @defgroup ec_host_cmd_backend_interface EC Host Command Interface
|
||||
* @defgroup ec_host_cmd_interface EC Host Command Interface
|
||||
* @ingroup io_interfaces
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/backend.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
|
||||
struct ec_host_cmd {
|
||||
struct ec_host_cmd_rx_ctx rx_ctx;
|
||||
struct ec_host_cmd_tx_buf tx;
|
||||
struct ec_host_cmd_backend *backend;
|
||||
struct k_thread *thread;
|
||||
k_thread_stack_t *stack;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Arguments passed into every installed host command handler
|
||||
*/
|
||||
|
@ -29,21 +38,20 @@ struct ec_host_cmd_handler_args {
|
|||
* The version of the host command that is being requested. This will
|
||||
* be a value that has been static registered as valid for the handler.
|
||||
*/
|
||||
const uint8_t version;
|
||||
uint8_t version;
|
||||
/** The incoming data that can be cast to the handlers request type. */
|
||||
const void *const input_buf;
|
||||
const void *input_buf;
|
||||
/** The number of valid bytes that can be read from @a input_buf. */
|
||||
const uint16_t input_buf_size;
|
||||
uint16_t input_buf_size;
|
||||
/** The data written to this buffer will be send to the host. */
|
||||
void *const output_buf;
|
||||
void *output_buf;
|
||||
/** Maximum number of bytes that can be written to the @a output_buf. */
|
||||
uint16_t output_buf_max;
|
||||
/** Number of bytes of @a output_buf to send to the host. */
|
||||
uint16_t output_buf_size;
|
||||
};
|
||||
|
||||
typedef enum ec_host_cmd_status (*ec_host_cmd_handler_cb)(
|
||||
struct ec_host_cmd_handler_args *args);
|
||||
typedef enum ec_host_cmd_status (*ec_host_cmd_handler_cb)(struct ec_host_cmd_handler_args *args);
|
||||
/**
|
||||
* @brief Structure use for statically registering host command handlers
|
||||
*/
|
||||
|
@ -52,23 +60,25 @@ struct ec_host_cmd_handler {
|
|||
ec_host_cmd_handler_cb handler;
|
||||
/** The numerical command id used as the lookup for commands. */
|
||||
uint16_t id;
|
||||
/** The bitfield of all versions that the @a handler supports, where
|
||||
/**
|
||||
* The bitfield of all versions that the @a handler supports, where
|
||||
* each bit value represents that the @a handler supports that version.
|
||||
* E.g. BIT(0) corresponds to version 0.
|
||||
*/
|
||||
uint16_t version_mask;
|
||||
/** The minimum @a input_buf_size enforced by the framework before
|
||||
/**
|
||||
* The minimum @a input_buf_size enforced by the framework before
|
||||
* passing to the handler.
|
||||
*/
|
||||
uint16_t min_rqt_size;
|
||||
/** The minimum @a output_buf_size enforced by the framework before
|
||||
/**
|
||||
* The minimum @a output_buf_size enforced by the framework before
|
||||
* passing to the handler.
|
||||
*/
|
||||
uint16_t min_rsp_size;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Statically define and register a host command handler.
|
||||
*
|
||||
* Helper macro to statically define and register a host command handler that
|
||||
|
@ -82,8 +92,7 @@ struct ec_host_cmd_handler {
|
|||
* @param _response_type The datatype of the response parameters for
|
||||
* @a _function.
|
||||
*/
|
||||
#define EC_HOST_CMD_HANDLER(_id, _function, _version_mask, _request_type, \
|
||||
_response_type) \
|
||||
#define EC_HOST_CMD_HANDLER(_id, _function, _version_mask, _request_type, _response_type) \
|
||||
const STRUCT_SECTION_ITERABLE(ec_host_cmd_handler, __cmd##_id) = { \
|
||||
.id = _id, \
|
||||
.handler = _function, \
|
||||
|
@ -93,7 +102,6 @@ struct ec_host_cmd_handler {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Statically define and register a host command handler without sizes.
|
||||
*
|
||||
* Helper macro to statically define and register a host command handler whose
|
||||
|
@ -121,17 +129,20 @@ struct ec_host_cmd_handler {
|
|||
* sent from host to embedded controller.
|
||||
*/
|
||||
struct ec_host_cmd_request_header {
|
||||
/** Should be 3. The EC will return EC_HOST_CMD_INVALID_HEADER if it
|
||||
/**
|
||||
* Should be 3. The EC will return EC_HOST_CMD_INVALID_HEADER if it
|
||||
* receives a header with a version it doesn't know how to parse.
|
||||
*/
|
||||
uint8_t prtcl_ver;
|
||||
/** Checksum of response and data; sum of all bytes including checksum.
|
||||
/**
|
||||
* Checksum of response and data; sum of all bytes including checksum.
|
||||
* Should total to 0.
|
||||
*/
|
||||
uint8_t checksum;
|
||||
/** Id of command that is being sent. */
|
||||
uint16_t cmd_id;
|
||||
/** Version of the specific @a cmd_id being requested. Valid
|
||||
/**
|
||||
* Version of the specific @a cmd_id being requested. Valid
|
||||
* versions start at 0.
|
||||
*/
|
||||
uint8_t cmd_ver;
|
||||
|
@ -151,7 +162,8 @@ struct ec_host_cmd_request_header {
|
|||
struct ec_host_cmd_response_header {
|
||||
/** Should be 3. */
|
||||
uint8_t prtcl_ver;
|
||||
/** Checksum of response and data; sum of all bytes including checksum.
|
||||
/**
|
||||
* Checksum of response and data; sum of all bytes including checksum.
|
||||
* Should total to 0.
|
||||
*/
|
||||
uint8_t checksum;
|
||||
|
@ -213,6 +225,22 @@ enum ec_host_cmd_status {
|
|||
EC_HOST_CMD_MAX = UINT16_MAX /* Force enum to be 16 bits. */
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* @brief Initialize the host command subsystem
|
||||
*
|
||||
* This routine initializes the host command subsystem. It includes initialization
|
||||
* of a backend and the handler.
|
||||
* When the application configures the zephyr,host-cmd-backend chosen node, the chosen backend
|
||||
* automatically calls this routine at CONFIG_EC_HOST_CMD_INIT_PRIORITY.
|
||||
* Applications that require a run-time selection of the backend must leave
|
||||
* zephyr,host-cmd-backend undefined and must explicitly call this routine.
|
||||
*
|
||||
* @param[in] backend Pointer to the backend structure to initialize.
|
||||
*
|
||||
* @retval 0 if successful
|
||||
*/
|
||||
int ec_host_cmd_init(struct ec_host_cmd_backend *backend);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -25,8 +25,11 @@
|
|||
* will override the first callback installation.
|
||||
*
|
||||
* @param cb Callback that is called when device would send data to host.
|
||||
* @param tx_buf Pointer of a pointer to the tx buf structure where data will
|
||||
* be sent.
|
||||
*/
|
||||
void ec_host_cmd_backend_sim_install_send_cb(ec_host_cmd_backend_api_send cb);
|
||||
void ec_host_cmd_backend_sim_install_send_cb(ec_host_cmd_backend_api_send cb,
|
||||
struct ec_host_cmd_tx_buf **tx_buf);
|
||||
|
||||
/**
|
||||
* @brief Simulate receiving data from host as passed in to this function
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(ec_host_cmd_handler.c)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_EC_HOST_CMD_BACKEND backends)
|
||||
add_subdirectory(backends)
|
||||
|
|
|
@ -9,20 +9,39 @@ config EC_HOST_CMD
|
|||
bool "Support Embedded Controller host command handler subsystem"
|
||||
help
|
||||
Enable host command processing for embedded controllers on notebook
|
||||
computers. Enabling this option requires specifying a chosen
|
||||
zephyr,ec-host-interface device as the ec host command backend that
|
||||
receive incoming host command requests to process.
|
||||
computers.
|
||||
|
||||
if EC_HOST_CMD
|
||||
|
||||
module = EC_HC
|
||||
module-str = ec-host-commands
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config EC_HOST_CMD_HANDLER_STACK_SIZE
|
||||
int "Stack size for the EC host command handler thread"
|
||||
default 800
|
||||
|
||||
config EC_HOST_CMD_HANDLER_TX_BUFFER
|
||||
int "Buffer size in bytes for TX buffer shared by all EC host commands"
|
||||
default EC_HOST_CMD_BACKEND_SHI_MAX_RESPONSE if EC_HOST_CMD_BACKEND_SHI
|
||||
default 0 if EC_HOST_CMD_BACKEND_ESPI
|
||||
default 0 if EC_HOST_CMD_BACKEND_SHI
|
||||
default 256
|
||||
help
|
||||
Buffer size in bytes for TX buffer defined by the host command handler.
|
||||
Some backend layers can define their own buffer, so the size can be zero to
|
||||
avoid duplicating buffers. If multiple backends are used, the size has to be
|
||||
set by user to the largest one.
|
||||
|
||||
config EC_HOST_CMD_HANDLER_RX_BUFFER
|
||||
int "Buffer size in bytes for RX buffer shared by all EC host commands"
|
||||
default 256 if EC_HOST_CMD_BACKEND_ESPI
|
||||
default 0 if EC_HOST_CMD_BACKEND_SHI
|
||||
default 256
|
||||
help
|
||||
Buffer size in bytes for TX buffer defined by the host command handler.
|
||||
Some backend layers can define their own buffer, so the size can be zero to
|
||||
avoid duplicating buffers. If multiple backends are used, the size has to be
|
||||
set by user to the largest one.
|
||||
|
||||
config EC_HOST_CMD_HANDLER_PRIO
|
||||
int "Priority of host command task"
|
||||
|
@ -33,8 +52,28 @@ config EC_HOST_CMD_HANDLER_PRIO
|
|||
process the command on time and the AP will abort the waiting for response and be unable
|
||||
to boot the system properly.
|
||||
|
||||
source "subsys/mgmt/ec_host_cmd/backends/Kconfig"
|
||||
config EC_HOST_CMD_INIT_PRIORITY
|
||||
int "Initialization priority"
|
||||
default 60
|
||||
range 0 99
|
||||
help
|
||||
Initialization priority for Host Command. It must be higher than the initialization
|
||||
priority of the used backend device.
|
||||
|
||||
config EC_HOST_CMD_HANDLER_TX_BUFFER_DEF
|
||||
bool
|
||||
default y if EC_HOST_CMD_HANDLER_TX_BUFFER > 0
|
||||
help
|
||||
The handler defines common tx buffer
|
||||
|
||||
config EC_HOST_CMD_HANDLER_RX_BUFFER_DEF
|
||||
bool
|
||||
default y if EC_HOST_CMD_HANDLER_RX_BUFFER > 0
|
||||
help
|
||||
The handler defines common rx buffer
|
||||
|
||||
endif # EC_HOST_CMD
|
||||
|
||||
source "subsys/mgmt/ec_host_cmd/backends/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
# Host Command backend configs
|
||||
|
||||
# Copyright (c) 2020 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig EC_HOST_CMD_BACKEND
|
||||
bool "Embedded Controller Host Command backend support"
|
||||
depends on EC_HOST_CMD
|
||||
help
|
||||
Enable the embedded controller host command backend driver. This
|
||||
is needed by the EC host command framework to send and receive data
|
||||
on the appropriate EC host bus.
|
||||
|
||||
if EC_HOST_CMD_BACKEND
|
||||
|
||||
module = EC_HC
|
||||
module-str = ec-host-commands
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
choice EC_HOST_CMD_BACKEND_TYPE
|
||||
prompt "Host commands backend"
|
||||
|
||||
config EC_HOST_CMD_BACKEND_SIMULATOR
|
||||
bool "Embedded Controller Host Command Backend Simulator"
|
||||
depends on DT_HAS_ZEPHYR_SIM_EC_HOST_CMD_BACKEND_ENABLED
|
||||
depends on SOC_POSIX
|
||||
help
|
||||
Enable the EC host command simulator.
|
||||
|
||||
|
@ -28,7 +13,6 @@ config EC_HOST_CMD_BACKEND_ESPI
|
|||
bool "Host commands support using eSPI bus"
|
||||
depends on ESPI_PERIPHERAL_EC_HOST_CMD
|
||||
depends on ESPI_PERIPHERAL_CUSTOM_OPCODE
|
||||
depends on DT_HAS_ZEPHYR_EC_HOST_CMD_BACKEND_ESPI_ENABLED
|
||||
help
|
||||
Enable support for Embedded Controller host commands using
|
||||
the eSPI bus.
|
||||
|
@ -39,8 +23,6 @@ config EC_HOST_CMD_BACKEND_SHI
|
|||
Enable support for Embedded Controller host commands using
|
||||
the Serial Host Interface.
|
||||
|
||||
endchoice
|
||||
|
||||
if EC_HOST_CMD_BACKEND_SHI
|
||||
|
||||
choice EC_HOST_CMD_BACKEND_SHI_DRIVER
|
||||
|
@ -83,5 +65,3 @@ config EC_HOST_CMD_BACKEND_SHI_MAX_RESPONSE
|
|||
command, flash read offset/size, and 512 bytes of flash data.
|
||||
|
||||
endif # EC_HOST_CMD_BACKEND_SHI
|
||||
|
||||
endif # EC_HOST_CMD_BACKEND
|
||||
|
|
|
@ -4,103 +4,157 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zephyr_ec_host_cmd_backend_espi
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/espi.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/backend.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/ec_host_cmd.h>
|
||||
|
||||
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "Invalid number of eSPI backends");
|
||||
LOG_MODULE_REGISTER(host_cmd_espi, CONFIG_EC_HC_LOG_LEVEL);
|
||||
|
||||
#define ESPI_BUS DT_PHANDLE(DT_DRV_INST(0), bus)
|
||||
#define ESPI_DEVICE DEVICE_DT_GET(ESPI_BUS)
|
||||
#define RX_HEADER_SIZE (sizeof(struct ec_host_cmd_request_header))
|
||||
|
||||
struct ec_host_cmd_backend_espi_data {
|
||||
struct k_sem handler_owns;
|
||||
struct k_sem dev_owns;
|
||||
uint32_t rx_buffer_len;
|
||||
struct espi_callback espi_cb;
|
||||
uint8_t *espi_shm;
|
||||
/* eSPI Host Command state */
|
||||
enum ec_host_cmd_espi_state {
|
||||
/* Interface is disabled */
|
||||
ESPI_STATE_DISABLED,
|
||||
/* Ready to receive next request */
|
||||
ESPI_STATE_READY_TO_RECV,
|
||||
/* Processing request */
|
||||
ESPI_STATE_PROCESSING,
|
||||
/* Processing request */
|
||||
ESPI_STATE_SENDING,
|
||||
ESPI_STATE_COUNT,
|
||||
};
|
||||
|
||||
static void ec_host_cmd_backend_espi_handler(const struct device *dev, struct espi_callback *cb,
|
||||
struct ec_host_cmd_espi_ctx {
|
||||
/* eSPI device instance */
|
||||
const struct device *espi_dev;
|
||||
/* Context for read operation */
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx;
|
||||
/* Transmit buffer */
|
||||
struct ec_host_cmd_tx_buf *tx;
|
||||
/* eSPI callback */
|
||||
struct espi_callback espi_cb;
|
||||
/* eSPI Host Command state */
|
||||
enum ec_host_cmd_espi_state state;
|
||||
};
|
||||
|
||||
#define EC_HOST_CMD_ESPI_DEFINE(_name) \
|
||||
static struct ec_host_cmd_espi_ctx _name##_hc_espi; \
|
||||
struct ec_host_cmd_backend _name = { \
|
||||
.api = &ec_host_cmd_api, \
|
||||
.ctx = (struct ec_host_cmd_espi_ctx *)&_name##_hc_espi, \
|
||||
}
|
||||
|
||||
static void espi_handler(const struct device *dev, struct espi_callback *cb,
|
||||
struct espi_event espi_evt)
|
||||
{
|
||||
struct ec_host_cmd_backend_espi_data *data =
|
||||
CONTAINER_OF(cb, struct ec_host_cmd_backend_espi_data, espi_cb);
|
||||
struct ec_host_cmd_espi_ctx *hc_espi =
|
||||
CONTAINER_OF(cb, struct ec_host_cmd_espi_ctx, espi_cb);
|
||||
uint16_t event_type = (uint16_t)espi_evt.evt_details;
|
||||
/* tx stores the shared memory buf pointer and size, so use it */
|
||||
const struct ec_host_cmd_request_header *rx_header = hc_espi->tx->buf;
|
||||
const size_t shared_size = hc_espi->tx->len_max;
|
||||
const uint16_t rx_valid_data_size = rx_header->data_len + RX_HEADER_SIZE;
|
||||
|
||||
if (event_type != ESPI_PERIPHERAL_EC_HOST_CMD) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (k_sem_take(&data->dev_owns, K_NO_WAIT) != 0) {
|
||||
int res = EC_HOST_CMD_IN_PROGRESS;
|
||||
|
||||
espi_write_lpc_request(ESPI_DEVICE, ECUSTOM_HOST_CMD_SEND_RESULT, &res);
|
||||
/* Make sure we've received a Host Command in a good state not to override buffers for
|
||||
* a Host Command that is currently being processed. There is a moment between sending
|
||||
* a response and setting state to ESPI_STATE_READY_TO_RECV when we can receive a new
|
||||
* host command, so accept the sending state as well.
|
||||
*/
|
||||
if (hc_espi->state != ESPI_STATE_READY_TO_RECV && hc_espi->state != ESPI_STATE_SENDING) {
|
||||
LOG_ERR("Received HC in bad state");
|
||||
return;
|
||||
}
|
||||
|
||||
k_sem_give(&data->handler_owns);
|
||||
}
|
||||
|
||||
int ec_host_cmd_backend_espi_init(const struct device *dev, struct ec_host_cmd_rx_ctx *rx_ctx)
|
||||
{
|
||||
struct ec_host_cmd_backend_espi_data *data = dev->data;
|
||||
|
||||
if (rx_ctx == NULL) {
|
||||
return -EINVAL;
|
||||
/* Only support version 3 and make sure the number of bytes to copy is not
|
||||
* bigger than rx buf size or the shared memory size
|
||||
*/
|
||||
if (rx_header->prtcl_ver != 3 ||
|
||||
rx_valid_data_size > CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER ||
|
||||
rx_valid_data_size > shared_size) {
|
||||
memcpy(hc_espi->rx_ctx->buf, (void *)rx_header, RX_HEADER_SIZE);
|
||||
hc_espi->rx_ctx->len = RX_HEADER_SIZE;
|
||||
} else {
|
||||
memcpy(hc_espi->rx_ctx->buf, (void *)rx_header, rx_valid_data_size);
|
||||
hc_espi->rx_ctx->len = rx_valid_data_size;
|
||||
}
|
||||
|
||||
rx_ctx->buf = data->espi_shm;
|
||||
rx_ctx->len = &data->rx_buffer_len;
|
||||
rx_ctx->dev_owns = &data->dev_owns;
|
||||
rx_ctx->handler_owns = &data->handler_owns;
|
||||
/* Even in case of errors, let the general handler send response */
|
||||
hc_espi->state = ESPI_STATE_PROCESSING;
|
||||
k_sem_give(&hc_espi->rx_ctx->handler_owns);
|
||||
}
|
||||
|
||||
static int ec_host_cmd_espi_init(const struct ec_host_cmd_backend *backend,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx, struct ec_host_cmd_tx_buf *tx)
|
||||
{
|
||||
struct ec_host_cmd_espi_ctx *hc_espi = (struct ec_host_cmd_espi_ctx *)backend->ctx;
|
||||
|
||||
hc_espi->state = ESPI_STATE_DISABLED;
|
||||
|
||||
if (!device_is_ready(hc_espi->espi_dev)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hc_espi->rx_ctx = rx_ctx;
|
||||
hc_espi->tx = tx;
|
||||
|
||||
espi_init_callback(&hc_espi->espi_cb, espi_handler, ESPI_BUS_PERIPHERAL_NOTIFICATION);
|
||||
espi_add_callback(hc_espi->espi_dev, &hc_espi->espi_cb);
|
||||
/* Use shared memory as the tx buffer */
|
||||
espi_read_lpc_request(hc_espi->espi_dev, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY,
|
||||
(uint32_t *)&tx->buf);
|
||||
espi_read_lpc_request(hc_espi->espi_dev, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY_SIZE,
|
||||
&tx->len_max);
|
||||
|
||||
hc_espi->state = ESPI_STATE_READY_TO_RECV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ec_host_cmd_backend_espi_send(const struct device *dev,
|
||||
const struct ec_host_cmd_tx_buf *buf)
|
||||
static int ec_host_cmd_espi_send(const struct ec_host_cmd_backend *backend)
|
||||
{
|
||||
struct ec_host_cmd_backend_espi_data *data = dev->data;
|
||||
struct ec_host_cmd_response_header *resp_hdr = buf->buf;
|
||||
struct ec_host_cmd_espi_ctx *hc_espi = (struct ec_host_cmd_espi_ctx *)backend->ctx;
|
||||
struct ec_host_cmd_response_header *resp_hdr = hc_espi->tx->buf;
|
||||
uint32_t result = resp_hdr->result;
|
||||
int ret;
|
||||
|
||||
memcpy(data->espi_shm, buf->buf, buf->len);
|
||||
hc_espi->state = ESPI_STATE_SENDING;
|
||||
|
||||
return espi_write_lpc_request(ESPI_DEVICE, ECUSTOM_HOST_CMD_SEND_RESULT, &result);
|
||||
/* Data to transfer are already in the tx buffer (shared memory) */
|
||||
ret = espi_write_lpc_request(hc_espi->espi_dev, ECUSTOM_HOST_CMD_SEND_RESULT, &result);
|
||||
hc_espi->state = ESPI_STATE_READY_TO_RECV;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ec_host_cmd_backend_api ec_host_cmd_api = {
|
||||
.init = &ec_host_cmd_backend_espi_init,
|
||||
.send = &ec_host_cmd_backend_espi_send,
|
||||
.init = &ec_host_cmd_espi_init,
|
||||
.send = &ec_host_cmd_espi_send,
|
||||
};
|
||||
|
||||
static int ec_host_cmd_espi_init(const struct device *dev)
|
||||
EC_HOST_CMD_ESPI_DEFINE(ec_host_cmd_espi);
|
||||
struct ec_host_cmd_backend *ec_host_cmd_backend_get_espi(const struct device *dev)
|
||||
{
|
||||
struct ec_host_cmd_backend_espi_data *data = dev->data;
|
||||
|
||||
/* Allow writing to rx buff at startup and block on reading. */
|
||||
k_sem_init(&data->handler_owns, 0, 1);
|
||||
k_sem_init(&data->dev_owns, 1, 1);
|
||||
|
||||
espi_init_callback(&data->espi_cb, ec_host_cmd_backend_espi_handler,
|
||||
ESPI_BUS_PERIPHERAL_NOTIFICATION);
|
||||
espi_add_callback(ESPI_DEVICE, &data->espi_cb);
|
||||
|
||||
espi_read_lpc_request(ESPI_DEVICE, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY,
|
||||
(uint32_t *)&data->espi_shm);
|
||||
espi_read_lpc_request(ESPI_DEVICE, ECUSTOM_HOST_CMD_GET_PARAM_MEMORY_SIZE,
|
||||
&data->rx_buffer_len);
|
||||
|
||||
return 0;
|
||||
((struct ec_host_cmd_espi_ctx *)(ec_host_cmd_espi.ctx))->espi_dev = dev;
|
||||
return &ec_host_cmd_espi;
|
||||
}
|
||||
|
||||
/* Assume only one backend */
|
||||
static struct ec_host_cmd_backend_espi_data espi_data;
|
||||
DEVICE_DT_INST_DEFINE(0, ec_host_cmd_espi_init, NULL, &espi_data, NULL, POST_KERNEL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ec_host_cmd_api);
|
||||
#if DT_NODE_EXISTS(DT_CHOSEN(zephyr_host_cmd_backend))
|
||||
static int host_cmd_init(const struct device *arg)
|
||||
{
|
||||
ARG_UNUSED(arg);
|
||||
const struct device *const dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_host_cmd_backend));
|
||||
|
||||
ec_host_cmd_init(ec_host_cmd_backend_get_espi(dev));
|
||||
return 0;
|
||||
}
|
||||
SYS_INIT(host_cmd_init, POST_KERNEL, CONFIG_EC_HOST_CMD_INIT_PRIORITY);
|
||||
#endif
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_EC_HOST_CMD_BACKEND_SHI_H_
|
||||
#define ZEPHYR_DRIVERS_EC_HOST_CMD_BACKEND_SHI_H_
|
||||
#ifndef ZEPHYR_SUBSYS_MGMT_EC_HOST_CMD_BACKENDS_EC_HOST_CMD_BACKEND_SHI_H_
|
||||
#define ZEPHYR_SUBSYS_MGMT_EC_HOST_CMD_BACKENDS_EC_HOST_CMD_BACKEND_SHI_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
|
||||
|
@ -98,4 +98,4 @@
|
|||
/* Supported version of host commands protocol. */
|
||||
#define EC_HOST_REQUEST_VERSION 3
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_EC_HOST_CMD_BACKEND_SHI_H_ */
|
||||
#endif /* ZEPHYR_SUBSYS_MGMT_EC_HOST_CMD_BACKENDS_EC_HOST_CMD_BACKEND_SHI_H_ */
|
||||
|
|
|
@ -30,8 +30,6 @@ LOG_MODULE_REGISTER(host_cmd_shi_ite, CONFIG_EC_HC_LOG_LEVEL);
|
|||
#define SHI_MAX_RESPONSE_SIZE \
|
||||
(SPI_TX_MAX_FIFO_SIZE - EC_SHI_PREAMBLE_LENGTH - EC_SHI_PAST_END_LENGTH)
|
||||
|
||||
BUILD_ASSERT(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER == SHI_MAX_RESPONSE_SIZE,
|
||||
"EC HC TX has different size than SHI TX response size");
|
||||
BUILD_ASSERT(CONFIG_EC_HOST_CMD_BACKEND_SHI_MAX_REQUEST <= SPI_RX_MAX_FIFO_SIZE,
|
||||
"SHI max request size is too big");
|
||||
BUILD_ASSERT(CONFIG_EC_HOST_CMD_BACKEND_SHI_MAX_RESPONSE <= SHI_MAX_RESPONSE_SIZE,
|
||||
|
@ -65,18 +63,22 @@ struct shi_it8xxx2_cfg {
|
|||
};
|
||||
|
||||
struct shi_it8xxx2_data {
|
||||
/* Backend data */
|
||||
struct k_sem handler_owns;
|
||||
struct k_sem dev_owns;
|
||||
/* Peripheral data */
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx;
|
||||
struct ec_host_cmd_tx_buf *tx;
|
||||
struct gpio_callback cs_cb;
|
||||
/* Current state */
|
||||
enum shi_state_machine shi_state;
|
||||
/* Buffers */
|
||||
uint32_t in_msg_size;
|
||||
uint8_t in_msg[SPI_RX_MAX_FIFO_SIZE] __aligned(4);
|
||||
uint8_t out_msg[SPI_TX_MAX_FIFO_SIZE] __aligned(4);
|
||||
};
|
||||
|
||||
struct ec_host_cmd_shi_ite_ctx {
|
||||
/* SHI device instance */
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
static const uint8_t out_preamble[EC_SHI_PREAMBLE_LENGTH] = {
|
||||
EC_SHI_PROCESSING,
|
||||
EC_SHI_PROCESSING,
|
||||
|
@ -92,6 +94,13 @@ static const int shi_ite_response_state[] = {
|
|||
};
|
||||
BUILD_ASSERT(ARRAY_SIZE(shi_ite_response_state) == SHI_STATE_COUNT);
|
||||
|
||||
#define EC_HOST_CMD_SHI_ITE_DEFINE(_name) \
|
||||
static struct ec_host_cmd_shi_ite_ctx _name##_hc_shi_ite; \
|
||||
struct ec_host_cmd_backend _name = { \
|
||||
.api = &ec_host_cmd_api, \
|
||||
.ctx = (struct ec_host_cmd_shi_ite_ctx *)&_name##_hc_shi_ite, \
|
||||
}
|
||||
|
||||
static void shi_ite_set_state(struct shi_it8xxx2_data *data, int state)
|
||||
{
|
||||
/* SPI peripheral state machine */
|
||||
|
@ -170,10 +179,10 @@ static void shi_ite_response_host_data(const struct device *dev, uint8_t *out_ms
|
|||
* Some commands can continue for a while. This function is called by
|
||||
* host_command when it completes.
|
||||
*/
|
||||
static int shi_ite_backend_send(const struct device *dev,
|
||||
const struct ec_host_cmd_tx_buf *buf)
|
||||
static int shi_ite_backend_send(const struct ec_host_cmd_backend *backend)
|
||||
{
|
||||
struct shi_it8xxx2_data *data = dev->data;
|
||||
struct ec_host_cmd_shi_ite_ctx *hc_shi = (struct ec_host_cmd_shi_ite_ctx *)backend->ctx;
|
||||
struct shi_it8xxx2_data *data = hc_shi->dev->data;
|
||||
int tx_size;
|
||||
|
||||
if (data->shi_state != SHI_STATE_PROCESSING) {
|
||||
|
@ -184,18 +193,17 @@ static int shi_ite_backend_send(const struct device *dev,
|
|||
/* Copy preamble */
|
||||
memcpy(data->out_msg, out_preamble, sizeof(out_preamble));
|
||||
|
||||
/* Copy data */
|
||||
memcpy(data->out_msg + sizeof(out_preamble), buf->buf, buf->len);
|
||||
/* Data to sent are already at "out_msg + sizeof(out_preamble)" memory address(tx buf
|
||||
* assigned in the init function), prepared by the handler.
|
||||
* Append our past-end byte, which we reserved space for.
|
||||
*/
|
||||
memset(&data->out_msg[sizeof(out_preamble) + data->tx->len], EC_SHI_PAST_END,
|
||||
EC_SHI_PAST_END_LENGTH);
|
||||
|
||||
/* Append our past-end byte, which we reserved space for. */
|
||||
for (int i = 0; i < EC_SHI_PAST_END_LENGTH; i++) {
|
||||
data->out_msg[sizeof(out_preamble) + buf->len + i] = EC_SHI_PAST_END;
|
||||
}
|
||||
|
||||
tx_size = buf->len + EC_SHI_PREAMBLE_LENGTH + EC_SHI_PAST_END_LENGTH;
|
||||
tx_size = data->tx->len + EC_SHI_PREAMBLE_LENGTH + EC_SHI_PAST_END_LENGTH;
|
||||
|
||||
/* Transmit the reply */
|
||||
shi_ite_response_host_data(dev, data->out_msg, tx_size);
|
||||
shi_ite_response_host_data(hc_shi->dev, data->out_msg, tx_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -238,24 +246,24 @@ static void shi_ite_parse_header(const struct device *dev)
|
|||
struct shi_it8xxx2_data *data = dev->data;
|
||||
struct ec_host_cmd_request_header *r = (struct ec_host_cmd_request_header *)data->in_msg;
|
||||
|
||||
/* Store request data from Rx FIFO to in_msg buffer */
|
||||
/* Store request data from Rx FIFO to in_msg buffer (rx_ctx->buf) */
|
||||
shi_ite_host_request_data(data->in_msg, sizeof(*r));
|
||||
|
||||
/* Protocol version 3 */
|
||||
if (data->in_msg[0] == EC_HOST_REQUEST_VERSION) {
|
||||
if (r->prtcl_ver == EC_HOST_REQUEST_VERSION) {
|
||||
/* Check how big the packet should be */
|
||||
data->in_msg_size = shi_ite_host_request_expected_size(r);
|
||||
data->rx_ctx->len = shi_ite_host_request_expected_size(r);
|
||||
|
||||
if (data->in_msg_size == 0 || data->in_msg_size > sizeof(data->in_msg)) {
|
||||
shi_ite_bad_received_data(dev, data->in_msg_size);
|
||||
if (data->rx_ctx->len == 0 || data->rx_ctx->len > sizeof(data->in_msg)) {
|
||||
shi_ite_bad_received_data(dev, data->rx_ctx->len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store request data from Rx FIFO to in_msg buffer */
|
||||
shi_ite_host_request_data(data->in_msg + sizeof(*r),
|
||||
data->in_msg_size - sizeof(*r));
|
||||
shi_ite_host_request_data(data->rx_ctx->buf + sizeof(*r),
|
||||
data->rx_ctx->len - sizeof(*r));
|
||||
|
||||
k_sem_give(&data->handler_owns);
|
||||
k_sem_give(&data->rx_ctx->handler_owns);
|
||||
} else {
|
||||
/* Invalid version number */
|
||||
LOG_ERR("Invalid version number");
|
||||
|
@ -396,17 +404,6 @@ static int shi_ite_init_registers(const struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int shi_ite_init_backend(const struct device *dev)
|
||||
{
|
||||
struct shi_it8xxx2_data *data = dev->data;
|
||||
|
||||
/* Allow writing to rx buff at startup and block on reading. */
|
||||
k_sem_init(&data->handler_owns, 0, 1);
|
||||
k_sem_init(&data->dev_owns, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_ite_init(const struct device *dev)
|
||||
{
|
||||
const struct shi_it8xxx2_cfg *cfg = dev->config;
|
||||
|
@ -418,11 +415,6 @@ static int shi_ite_init(const struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = shi_ite_init_backend(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure the SPI chip select */
|
||||
ret = gpio_pin_configure(cfg->cs.port, cfg->cs.pin, GPIO_INPUT | cfg->cs.dt_flags);
|
||||
if (ret < 0) {
|
||||
|
@ -447,15 +439,24 @@ static int shi_ite_init(const struct device *dev)
|
|||
return pm_device_runtime_enable(dev);
|
||||
}
|
||||
|
||||
static int shi_ite_backend_init_context(const struct device *dev,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx)
|
||||
static int shi_ite_backend_init(const struct ec_host_cmd_backend *backend,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx, struct ec_host_cmd_tx_buf *tx)
|
||||
{
|
||||
struct shi_it8xxx2_data *data = dev->data;
|
||||
struct ec_host_cmd_shi_ite_ctx *hc_shi = (struct ec_host_cmd_shi_ite_ctx *)backend->ctx;
|
||||
struct shi_it8xxx2_data *data;
|
||||
|
||||
hc_shi->dev = DEVICE_DT_INST_GET(0);
|
||||
if (!device_is_ready(hc_shi->dev)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data = hc_shi->dev->data;
|
||||
data->rx_ctx = rx_ctx;
|
||||
data->tx = tx;
|
||||
|
||||
rx_ctx->dev_owns = &data->dev_owns;
|
||||
rx_ctx->handler_owns = &data->handler_owns;
|
||||
rx_ctx->buf = data->in_msg;
|
||||
rx_ctx->len = &data->in_msg_size;
|
||||
tx->buf = data->out_msg + sizeof(out_preamble);
|
||||
data->tx->len_max = sizeof(data->out_msg) - EC_SHI_PREAMBLE_LENGTH - EC_SHI_PAST_END_LENGTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -488,7 +489,7 @@ static int shi_ite_pm_cb(const struct device *dev, enum pm_device_action action)
|
|||
PM_DEVICE_DT_INST_DEFINE(0, shi_ite_pm_cb);
|
||||
|
||||
static const struct ec_host_cmd_backend_api ec_host_cmd_api = {
|
||||
.init = shi_ite_backend_init_context,
|
||||
.init = shi_ite_backend_init,
|
||||
.send = shi_ite_backend_send,
|
||||
};
|
||||
|
||||
|
@ -499,8 +500,25 @@ static const struct shi_it8xxx2_cfg shi_cfg = {
|
|||
|
||||
static struct shi_it8xxx2_data shi_data = {
|
||||
.shi_state = SHI_STATE_DISABLED,
|
||||
.in_msg_size = 0,
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, shi_ite_init, PM_DEVICE_DT_INST_GET(0), &shi_data, &shi_cfg, POST_KERNEL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &ec_host_cmd_api);
|
||||
|
||||
EC_HOST_CMD_SHI_ITE_DEFINE(ec_host_cmd_shi_ite);
|
||||
|
||||
struct ec_host_cmd_backend *ec_host_cmd_backend_get_shi_ite(void)
|
||||
{
|
||||
return &ec_host_cmd_shi_ite;
|
||||
}
|
||||
|
||||
#if DT_NODE_EXISTS(DT_CHOSEN(zephyr_host_cmd_backend))
|
||||
static int host_cmd_init(const struct device *arg)
|
||||
{
|
||||
ARG_UNUSED(arg);
|
||||
|
||||
ec_host_cmd_init(ec_host_cmd_backend_get_shi_ite());
|
||||
return 0;
|
||||
}
|
||||
SYS_INIT(host_cmd_init, POST_KERNEL, CONFIG_EC_HOST_CMD_INIT_PRIORITY);
|
||||
#endif
|
||||
|
|
|
@ -109,9 +109,8 @@ struct shi_npcx_config {
|
|||
};
|
||||
|
||||
struct shi_npcx_data {
|
||||
/* Handler mutexes */
|
||||
struct k_sem handler_owns;
|
||||
struct k_sem dev_owns;
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx;
|
||||
struct ec_host_cmd_tx_buf *tx;
|
||||
/* Communication status */
|
||||
enum shi_npcx_state state;
|
||||
enum shi_npcx_state last_error_state;
|
||||
|
@ -119,7 +118,6 @@ struct shi_npcx_data {
|
|||
uint8_t *tx_msg; /* Entry pointer of msg tx buffer */
|
||||
volatile uint8_t *rx_buf; /* Entry pointer of receive buffer */
|
||||
volatile uint8_t *tx_buf; /* Entry pointer of transmit buffer */
|
||||
uint32_t sz_received; /* Size of received data in bytes */
|
||||
uint16_t sz_sending; /* Size of sending data in bytes */
|
||||
uint16_t sz_request; /* Request bytes need to receive */
|
||||
uint16_t sz_response; /* Response bytes need to receive */
|
||||
|
@ -131,6 +129,18 @@ struct shi_npcx_data {
|
|||
uint8_t in_msg[CONFIG_EC_HOST_CMD_BACKEND_SHI_MAX_REQUEST] __aligned(4);
|
||||
};
|
||||
|
||||
struct ec_host_cmd_shi_npcx_ctx {
|
||||
/* SHI device instance */
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
#define EC_HOST_CMD_SHI_NPCX_DEFINE(_name) \
|
||||
static struct ec_host_cmd_shi_npcx_ctx _name##_hc_shi_npcx; \
|
||||
struct ec_host_cmd_backend _name = { \
|
||||
.api = &ec_host_cmd_api, \
|
||||
.ctx = (struct ec_host_cmd_shi_npcx_ctx *)&_name##_hc_shi_npcx, \
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static void shi_npcx_reset_prepare(const struct device *dev);
|
||||
|
||||
|
@ -188,7 +198,7 @@ static int shi_npcx_read_inbuf_wait(const struct device *dev, uint32_t szbytes)
|
|||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
|
||||
/* Copy data to msg buffer from input buffer */
|
||||
for (uint32_t i = 0; i < szbytes; i++, data->sz_received++) {
|
||||
for (uint32_t i = 0; i < szbytes; i++, data->rx_ctx->len++) {
|
||||
/*
|
||||
* If input buffer pointer equals pointer which wants to read,
|
||||
* it means data is not ready.
|
||||
|
@ -255,7 +265,7 @@ static void shi_npcx_bad_received_data(const struct device *dev)
|
|||
|
||||
LOG_ERR("SHI bad data recv");
|
||||
LOG_DBG("BAD-");
|
||||
LOG_HEXDUMP_DBG(data->in_msg, data->sz_received, "in_msg=");
|
||||
LOG_HEXDUMP_DBG(data->in_msg, data->rx_ctx->len, "in_msg=");
|
||||
|
||||
/* Reset shi's state machine for error recovery */
|
||||
shi_npcx_reset_prepare(dev);
|
||||
|
@ -316,14 +326,14 @@ static void shi_npcx_handle_host_package(const struct device *dev)
|
|||
struct shi_npcx_data *data = dev->data;
|
||||
struct shi_reg *const inst = HAL_INSTANCE(dev);
|
||||
uint32_t sz_inbuf_int = data->sz_request / SHI_IBUF_HALF_SIZE;
|
||||
uint32_t cnt_inbuf_int = data->sz_received / SHI_IBUF_HALF_SIZE;
|
||||
uint32_t cnt_inbuf_int = data->rx_ctx->len / SHI_IBUF_HALF_SIZE;
|
||||
|
||||
if (sz_inbuf_int - cnt_inbuf_int) {
|
||||
/* Need to receive data from buffer */
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t remain_bytes = data->sz_request - data->sz_received;
|
||||
uint32_t remain_bytes = data->sz_request - data->rx_ctx->len;
|
||||
|
||||
/* Read remaining bytes from input buffer */
|
||||
if (!shi_npcx_read_inbuf_wait(dev, remain_bytes)) {
|
||||
|
@ -339,7 +349,7 @@ static void shi_npcx_handle_host_package(const struct device *dev)
|
|||
data->out_msg[0] = EC_SHI_FRAME_START;
|
||||
|
||||
/* Wake-up the HC handler thread */
|
||||
k_sem_give(&data->handler_owns);
|
||||
k_sem_give(&data->rx_ctx->handler_owns);
|
||||
}
|
||||
|
||||
static int shi_npcx_host_request_expected_size(const struct ec_host_cmd_request_header *r)
|
||||
|
@ -437,8 +447,8 @@ static void shi_npcx_read_half_inbuf(const struct device *dev)
|
|||
do {
|
||||
/* Restore data to msg buffer */
|
||||
*data->rx_msg++ = *data->rx_buf++;
|
||||
data->sz_received++;
|
||||
} while (data->sz_received % SHI_IBUF_HALF_SIZE && data->sz_received != data->sz_request);
|
||||
data->rx_ctx->len++;
|
||||
} while (data->rx_ctx->len % SHI_IBUF_HALF_SIZE && data->rx_ctx->len != data->sz_request);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -677,7 +687,7 @@ static void shi_npcx_reset_prepare(const struct device *dev)
|
|||
data->tx_msg = data->out_msg;
|
||||
data->rx_buf = inst->IBUF;
|
||||
data->tx_buf = inst->OBUF;
|
||||
data->sz_received = 0;
|
||||
data->rx_ctx->len = 0;
|
||||
data->sz_sending = 0;
|
||||
data->sz_request = 0;
|
||||
data->sz_response = 0;
|
||||
|
@ -841,17 +851,6 @@ static int shi_npcx_init_registers(const struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int shi_npcx_init_backend(const struct device *dev)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
|
||||
/* Allow writing to rx buff at startup and block on reading. */
|
||||
k_sem_init(&data->handler_owns, 0, 1);
|
||||
k_sem_init(&data->dev_owns, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_npcx_init(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -860,33 +859,37 @@ static int shi_npcx_init(const struct device *dev)
|
|||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = shi_npcx_init_backend(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_device_init_suspended(dev);
|
||||
|
||||
return pm_device_runtime_enable(dev);
|
||||
}
|
||||
|
||||
static int shi_npcx_backend_init_context(const struct device *dev,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx)
|
||||
static int shi_npcx_backend_init(const struct ec_host_cmd_backend *backend,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx, struct ec_host_cmd_tx_buf *tx)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct ec_host_cmd_shi_npcx_ctx *hc_shi = (struct ec_host_cmd_shi_npcx_ctx *)backend->ctx;
|
||||
struct shi_npcx_data *data;
|
||||
|
||||
hc_shi->dev = DEVICE_DT_INST_GET(0);
|
||||
if (!device_is_ready(hc_shi->dev)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data = hc_shi->dev->data;
|
||||
data->rx_ctx = rx_ctx;
|
||||
data->tx = tx;
|
||||
|
||||
rx_ctx->dev_owns = &data->dev_owns;
|
||||
rx_ctx->handler_owns = &data->handler_owns;
|
||||
rx_ctx->buf = data->in_msg;
|
||||
rx_ctx->len = &data->sz_received;
|
||||
tx->buf = data->out_msg_padded + SHI_OUT_START_PAD;
|
||||
tx->len_max = CONFIG_EC_HOST_CMD_BACKEND_SHI_MAX_RESPONSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int shi_npcx_backend_send(const struct device *dev,
|
||||
const struct ec_host_cmd_tx_buf *in_buf)
|
||||
static int shi_npcx_backend_send(const struct ec_host_cmd_backend *backend)
|
||||
{
|
||||
struct shi_npcx_data *data = dev->data;
|
||||
struct ec_host_cmd_shi_npcx_ctx *hc_shi = (struct ec_host_cmd_shi_npcx_ctx *)backend->ctx;
|
||||
struct shi_npcx_data *data = hc_shi->dev->data;
|
||||
uint8_t *out_buf = data->out_msg + EC_SHI_FRAME_START_LENGTH;
|
||||
|
||||
/*
|
||||
|
@ -897,17 +900,15 @@ static int shi_npcx_backend_send(const struct device *dev,
|
|||
*/
|
||||
__disable_irq();
|
||||
|
||||
memcpy(out_buf, in_buf->buf, in_buf->len);
|
||||
|
||||
if (data->state == SHI_STATE_PROCESSING) {
|
||||
/* Append our past-end byte, which we reserved space for. */
|
||||
((uint8_t *)out_buf)[in_buf->len] = EC_SHI_PAST_END;
|
||||
((uint8_t *)out_buf)[data->tx->len] = EC_SHI_PAST_END;
|
||||
|
||||
/* Computing sending bytes of response */
|
||||
data->sz_response = in_buf->len + EC_SHI_PROTO3_OVERHEAD;
|
||||
data->sz_response = data->tx->len + EC_SHI_PROTO3_OVERHEAD;
|
||||
|
||||
/* Start to fill output buffer with msg buffer */
|
||||
shi_npcx_write_first_pkg_outbuf(dev, data->sz_response);
|
||||
shi_npcx_write_first_pkg_outbuf(hc_shi->dev, data->sz_response);
|
||||
|
||||
/* Transmit the reply */
|
||||
data->state = SHI_STATE_SENDING;
|
||||
|
@ -918,7 +919,7 @@ static int shi_npcx_backend_send(const struct device *dev,
|
|||
* the transaction, and won't be listening for a response.
|
||||
* Reset state machine for next transaction.
|
||||
*/
|
||||
shi_npcx_reset_prepare(dev);
|
||||
shi_npcx_reset_prepare(hc_shi->dev);
|
||||
LOG_DBG("END\n");
|
||||
} else {
|
||||
LOG_ERR("Unexpected state %d in response handler", data->state);
|
||||
|
@ -929,7 +930,7 @@ static int shi_npcx_backend_send(const struct device *dev,
|
|||
}
|
||||
|
||||
static const struct ec_host_cmd_backend_api ec_host_cmd_api = {
|
||||
.init = shi_npcx_backend_init_context,
|
||||
.init = shi_npcx_backend_init,
|
||||
.send = shi_npcx_backend_send,
|
||||
};
|
||||
|
||||
|
@ -974,3 +975,21 @@ static struct shi_npcx_data shi_data = {
|
|||
|
||||
DEVICE_DT_INST_DEFINE(0, shi_npcx_init, PM_DEVICE_DT_INST_GET(0), &shi_data, &shi_cfg, POST_KERNEL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &ec_host_cmd_api);
|
||||
|
||||
EC_HOST_CMD_SHI_NPCX_DEFINE(ec_host_cmd_shi_npcx);
|
||||
|
||||
struct ec_host_cmd_backend *ec_host_cmd_backend_get_shi_npcx(void)
|
||||
{
|
||||
return &ec_host_cmd_shi_npcx;
|
||||
}
|
||||
|
||||
#if DT_NODE_EXISTS(DT_CHOSEN(zephyr_host_cmd_backend))
|
||||
static int host_cmd_init(const struct device *arg)
|
||||
{
|
||||
ARG_UNUSED(arg);
|
||||
|
||||
ec_host_cmd_init(ec_host_cmd_backend_get_shi_npcx());
|
||||
return 0;
|
||||
}
|
||||
SYS_INIT(host_cmd_init, POST_KERNEL, CONFIG_EC_HOST_CMD_INIT_PRIORITY);
|
||||
#endif
|
||||
|
|
|
@ -4,84 +4,83 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zephyr_sim_ec_host_cmd_backend
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/backend.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/ec_host_cmd.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef CONFIG_ARCH_POSIX
|
||||
#error Simulator only valid on posix
|
||||
#endif
|
||||
|
||||
static uint8_t rx_buffer[256];
|
||||
static size_t rx_buffer_len;
|
||||
struct ec_host_cmd_sim_ctx {
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx;
|
||||
struct ec_host_cmd_tx_buf *tx;
|
||||
};
|
||||
|
||||
/* Allow writing to rx buff at startup and block on reading. */
|
||||
static K_SEM_DEFINE(handler_owns, 0, 1);
|
||||
static K_SEM_DEFINE(dev_owns, 1, 1);
|
||||
#define EC_HOST_CMD_SIM_DEFINE(_name) \
|
||||
static struct ec_host_cmd_sim_ctx _name##_hc_sim; \
|
||||
struct ec_host_cmd_backend _name = { \
|
||||
.api = &ec_host_cmd_api, \
|
||||
.ctx = (struct ec_host_cmd_sim_ctx *)&_name##_hc_sim, \
|
||||
}
|
||||
|
||||
static ec_host_cmd_backend_api_send tx;
|
||||
|
||||
int ec_host_cmd_backend_sim_init(const struct device *dev,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx)
|
||||
static int ec_host_cmd_sim_init(const struct ec_host_cmd_backend *backend,
|
||||
struct ec_host_cmd_rx_ctx *rx_ctx,
|
||||
struct ec_host_cmd_tx_buf *tx_buf)
|
||||
{
|
||||
if (rx_ctx == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
struct ec_host_cmd_sim_ctx *hc_sim = (struct ec_host_cmd_sim_ctx *)backend->ctx;
|
||||
|
||||
rx_ctx->buf = rx_buffer;
|
||||
rx_ctx->len = &rx_buffer_len;
|
||||
rx_ctx->dev_owns = &dev_owns;
|
||||
rx_ctx->handler_owns = &handler_owns;
|
||||
hc_sim->rx_ctx = rx_ctx;
|
||||
hc_sim->tx = tx_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ec_host_cmd_backend_sim_send(const struct device *dev,
|
||||
const struct ec_host_cmd_tx_buf *buf)
|
||||
static int ec_host_cmd_sim_send(const struct ec_host_cmd_backend *backend)
|
||||
{
|
||||
if (tx != NULL) {
|
||||
return tx(dev, buf);
|
||||
return tx(backend);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ec_host_cmd_backend_sim_install_send_cb(ec_host_cmd_backend_api_send cb)
|
||||
static const struct ec_host_cmd_backend_api ec_host_cmd_api = {
|
||||
.init = &ec_host_cmd_sim_init,
|
||||
.send = &ec_host_cmd_sim_send,
|
||||
};
|
||||
EC_HOST_CMD_SIM_DEFINE(ec_host_cmd_sim);
|
||||
|
||||
void ec_host_cmd_backend_sim_install_send_cb(ec_host_cmd_backend_api_send cb,
|
||||
struct ec_host_cmd_tx_buf **tx_buf)
|
||||
{
|
||||
struct ec_host_cmd_sim_ctx *hc_sim = (struct ec_host_cmd_sim_ctx *)ec_host_cmd_sim.ctx;
|
||||
*tx_buf = hc_sim->tx;
|
||||
tx = cb;
|
||||
}
|
||||
|
||||
int ec_host_cmd_backend_sim_data_received(const uint8_t *buffer, size_t len)
|
||||
{
|
||||
if (sizeof(rx_buffer) < len) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (k_sem_take(&dev_owns, K_NO_WAIT) != 0) {
|
||||
return -EBUSY;
|
||||
}
|
||||
struct ec_host_cmd_sim_ctx *hc_sim = (struct ec_host_cmd_sim_ctx *)ec_host_cmd_sim.ctx;
|
||||
|
||||
memcpy(rx_buffer, buffer, len);
|
||||
rx_buffer_len = len;
|
||||
memcpy(hc_sim->rx_ctx->buf, buffer, len);
|
||||
hc_sim->rx_ctx->len = len;
|
||||
|
||||
k_sem_give(&hc_sim->rx_ctx->handler_owns);
|
||||
|
||||
k_sem_give(&handler_owns);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ec_host_cmd_backend_api ec_host_cmd_api = {
|
||||
.init = &ec_host_cmd_backend_sim_init,
|
||||
.send = &ec_host_cmd_backend_sim_send,
|
||||
};
|
||||
|
||||
static int ec_host_cmd_sim_init(const struct device *dev)
|
||||
static int host_cmd_init(const struct device *arg)
|
||||
{
|
||||
ARG_UNUSED(arg);
|
||||
|
||||
ec_host_cmd_init(&ec_host_cmd_sim);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assume only one simulator */
|
||||
DEVICE_DT_INST_DEFINE(0, ec_host_cmd_sim_init, NULL,
|
||||
NULL, NULL, POST_KERNEL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ec_host_cmd_api);
|
||||
SYS_INIT(host_cmd_init, POST_KERNEL, CONFIG_EC_HOST_CMD_INIT_PRIORITY);
|
||||
|
|
|
@ -6,24 +6,42 @@
|
|||
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/backend.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/ec_host_cmd.h>
|
||||
#include <zephyr/mgmt/ec_host_cmd/backend.h>
|
||||
#include <string.h>
|
||||
|
||||
BUILD_ASSERT(DT_HAS_CHOSEN(zephyr_ec_host_interface),
|
||||
"Must choose zephyr,ec-host-interface in device tree");
|
||||
|
||||
#define DT_HOST_CMD_DEV DT_CHOSEN(zephyr_ec_host_interface)
|
||||
LOG_MODULE_REGISTER(host_cmd_handler, CONFIG_EC_HC_LOG_LEVEL);
|
||||
|
||||
#define RX_HEADER_SIZE (sizeof(struct ec_host_cmd_request_header))
|
||||
#define TX_HEADER_SIZE (sizeof(struct ec_host_cmd_response_header))
|
||||
|
||||
/**
|
||||
* Used by host command handlers for their response before going over wire.
|
||||
* Host commands handlers will cast this to respective response structures that may have fields of
|
||||
* uint32_t or uint64_t, so this buffer must be aligned to protect against the unaligned access.
|
||||
*/
|
||||
static uint8_t tx_buffer[CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER] __aligned(8);
|
||||
#define EC_HOST_CMD_DEFINE(_name) \
|
||||
COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_DEF, \
|
||||
(static uint8_t _name##_rx_buffer[CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER];), ()) \
|
||||
COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF, \
|
||||
(static uint8_t _name##_tx_buffer[CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER];), ()) \
|
||||
static K_KERNEL_STACK_DEFINE(_name##stack, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE); \
|
||||
static struct k_thread _name##thread; \
|
||||
static struct ec_host_cmd _name = { \
|
||||
.rx_ctx = \
|
||||
{ \
|
||||
.buf = COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_RX_BUFFER_DEF, \
|
||||
(_name##_rx_buffer), (NULL)), \
|
||||
}, \
|
||||
.tx = \
|
||||
{ \
|
||||
.buf = COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF, \
|
||||
(_name##_tx_buffer), (NULL)), \
|
||||
.len_max = \
|
||||
COND_CODE_1(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER_DEF, \
|
||||
(CONFIG_EC_HOST_CMD_HANDLER_TX_BUFFER), (0)), \
|
||||
}, \
|
||||
.thread = &_name##thread, \
|
||||
.stack = _name##stack, \
|
||||
}
|
||||
|
||||
EC_HOST_CMD_DEFINE(ec_host_cmd);
|
||||
|
||||
static uint8_t cal_checksum(const uint8_t *const buffer, const uint16_t size)
|
||||
{
|
||||
|
@ -35,94 +53,128 @@ static uint8_t cal_checksum(const uint8_t *const buffer, const uint16_t size)
|
|||
return (uint8_t)(-checksum);
|
||||
}
|
||||
|
||||
static void send_error_response(const struct device *const ec_host_cmd_dev,
|
||||
const enum ec_host_cmd_status error)
|
||||
static void send_error_response(const struct ec_host_cmd_backend *backend,
|
||||
struct ec_host_cmd_tx_buf *tx, const enum ec_host_cmd_status error)
|
||||
{
|
||||
struct ec_host_cmd_response_header *const tx_header = (void *)tx_buffer;
|
||||
struct ec_host_cmd_response_header *const tx_header = (void *)tx->buf;
|
||||
|
||||
tx_header->prtcl_ver = 3;
|
||||
tx_header->result = error;
|
||||
tx_header->data_len = 0;
|
||||
tx_header->reserved = 0;
|
||||
tx_header->checksum = 0;
|
||||
tx_header->checksum = cal_checksum(tx_buffer, TX_HEADER_SIZE);
|
||||
tx_header->checksum = cal_checksum((uint8_t *)tx_header, TX_HEADER_SIZE);
|
||||
|
||||
const struct ec_host_cmd_tx_buf tx = {
|
||||
.buf = tx_buffer,
|
||||
.len = TX_HEADER_SIZE,
|
||||
};
|
||||
ec_host_cmd_backend_send(ec_host_cmd_dev, &tx);
|
||||
tx->len = TX_HEADER_SIZE;
|
||||
|
||||
backend->api->send(backend);
|
||||
}
|
||||
|
||||
static void handle_host_cmds_entry(void *arg1, void *arg2, void *arg3)
|
||||
static enum ec_host_cmd_status verify_rx(struct ec_host_cmd_rx_ctx *rx)
|
||||
{
|
||||
ARG_UNUSED(arg1);
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
const struct device *const ec_host_cmd_dev = DEVICE_DT_GET(DT_HOST_CMD_DEV);
|
||||
struct ec_host_cmd_rx_ctx rx;
|
||||
|
||||
if (!device_is_ready(ec_host_cmd_dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ec_host_cmd_backend_init(ec_host_cmd_dev, &rx);
|
||||
|
||||
while (1) {
|
||||
/* We have finished reading from RX buffer, so allow another
|
||||
* incoming msg.
|
||||
*/
|
||||
k_sem_give(rx.dev_owns);
|
||||
|
||||
/* Wait until and RX messages is received on host interface */
|
||||
if (k_sem_take(rx.handler_owns, K_FOREVER) < 0) {
|
||||
/* This code path should never occur due to the nature of
|
||||
* k_sem_take with K_FOREVER
|
||||
*/
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_ERROR);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* rx buf and len now have valid incoming data */
|
||||
if (*rx.len < RX_HEADER_SIZE) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_REQUEST_TRUNCATED);
|
||||
continue;
|
||||
if (rx->len < RX_HEADER_SIZE) {
|
||||
return EC_HOST_CMD_REQUEST_TRUNCATED;
|
||||
}
|
||||
|
||||
const struct ec_host_cmd_request_header *const rx_header =
|
||||
(void *)rx.buf;
|
||||
const struct ec_host_cmd_request_header *rx_header =
|
||||
(struct ec_host_cmd_request_header *)rx->buf;
|
||||
|
||||
/* Only support version 3 */
|
||||
if (rx_header->prtcl_ver != 3) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_INVALID_HEADER);
|
||||
continue;
|
||||
return EC_HOST_CMD_INVALID_HEADER;
|
||||
}
|
||||
|
||||
const uint16_t rx_valid_data_size =
|
||||
rx_header->data_len + RX_HEADER_SIZE;
|
||||
const uint16_t rx_valid_data_size = rx_header->data_len + RX_HEADER_SIZE;
|
||||
/*
|
||||
* Ensure we received at least as much data as is expected.
|
||||
* It is okay to receive more since some hardware interfaces
|
||||
* add on extra padding bytes at the end.
|
||||
*/
|
||||
if (*rx.len < rx_valid_data_size) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_REQUEST_TRUNCATED);
|
||||
continue;
|
||||
if (rx->len < rx_valid_data_size) {
|
||||
return EC_HOST_CMD_REQUEST_TRUNCATED;
|
||||
}
|
||||
|
||||
/* Validate checksum */
|
||||
if (cal_checksum(rx.buf, rx_valid_data_size) != 0) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_INVALID_CHECKSUM);
|
||||
if (cal_checksum((uint8_t *)rx_header, rx_valid_data_size) != 0) {
|
||||
return EC_HOST_CMD_INVALID_CHECKSUM;
|
||||
}
|
||||
|
||||
return EC_HOST_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static enum ec_host_cmd_status validate_handler(const struct ec_host_cmd_handler *handler,
|
||||
const struct ec_host_cmd_handler_args *args)
|
||||
{
|
||||
if (handler->min_rqt_size > args->input_buf_size) {
|
||||
return EC_HOST_CMD_REQUEST_TRUNCATED;
|
||||
}
|
||||
|
||||
if (handler->min_rsp_size > args->output_buf_max) {
|
||||
return EC_HOST_CMD_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
if (args->version > sizeof(handler->version_mask) ||
|
||||
!(handler->version_mask & BIT(args->version))) {
|
||||
return EC_HOST_CMD_INVALID_VERSION;
|
||||
}
|
||||
|
||||
return EC_HOST_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static enum ec_host_cmd_status prepare_response(struct ec_host_cmd_tx_buf *tx, uint16_t len)
|
||||
{
|
||||
struct ec_host_cmd_response_header *const tx_header = (void *)tx->buf;
|
||||
|
||||
tx_header->prtcl_ver = 3;
|
||||
tx_header->result = EC_HOST_CMD_SUCCESS;
|
||||
tx_header->data_len = len;
|
||||
tx_header->reserved = 0;
|
||||
|
||||
const uint16_t tx_valid_data_size = tx_header->data_len + TX_HEADER_SIZE;
|
||||
|
||||
if (tx_valid_data_size > tx->len_max) {
|
||||
return EC_HOST_CMD_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
/* Calculate checksum */
|
||||
tx_header->checksum = 0;
|
||||
tx_header->checksum = cal_checksum(tx->buf, tx_valid_data_size);
|
||||
|
||||
tx->len = tx_valid_data_size;
|
||||
|
||||
return EC_HOST_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void ec_host_cmd_thread(void *hc_handle, void *arg2, void *arg3)
|
||||
{
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
enum ec_host_cmd_status status;
|
||||
struct ec_host_cmd *hc = (struct ec_host_cmd *)hc_handle;
|
||||
struct ec_host_cmd_rx_ctx *rx = &hc->rx_ctx;
|
||||
struct ec_host_cmd_tx_buf *tx = &hc->tx;
|
||||
const struct ec_host_cmd_handler *found_handler;
|
||||
const struct ec_host_cmd_request_header *const rx_header = (void *)rx->buf;
|
||||
/* The pointer to rx buffer is constant during communication */
|
||||
struct ec_host_cmd_handler_args args = {
|
||||
.output_buf = (uint8_t *)tx->buf + TX_HEADER_SIZE,
|
||||
.output_buf_max = tx->len_max - TX_HEADER_SIZE,
|
||||
.input_buf = rx->buf + RX_HEADER_SIZE,
|
||||
.reserved = NULL,
|
||||
};
|
||||
|
||||
while (1) {
|
||||
/* Wait until RX messages is received on host interface */
|
||||
k_sem_take(&rx->handler_owns, K_FOREVER);
|
||||
|
||||
status = verify_rx(rx);
|
||||
if (status != EC_HOST_CMD_SUCCESS) {
|
||||
send_error_response(hc->backend, tx, status);
|
||||
continue;
|
||||
}
|
||||
|
||||
const struct ec_host_cmd_handler *found_handler = NULL;
|
||||
|
||||
found_handler = NULL;
|
||||
STRUCT_SECTION_FOREACH(ec_host_cmd_handler, handler)
|
||||
{
|
||||
if (handler->id == rx_header->cmd_id) {
|
||||
|
@ -133,79 +185,71 @@ static void handle_host_cmds_entry(void *arg1, void *arg2, void *arg3)
|
|||
|
||||
/* No handler in this image for requested command */
|
||||
if (found_handler == NULL) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_INVALID_COMMAND);
|
||||
send_error_response(hc->backend, tx, EC_HOST_CMD_INVALID_COMMAND);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct ec_host_cmd_handler_args args = {
|
||||
.reserved = NULL,
|
||||
.command = rx_header->cmd_id,
|
||||
.version = rx_header->cmd_ver,
|
||||
.input_buf = rx.buf + RX_HEADER_SIZE,
|
||||
.input_buf_size = rx_header->data_len,
|
||||
.output_buf = tx_buffer + TX_HEADER_SIZE,
|
||||
.output_buf_max = sizeof(tx_buffer) - TX_HEADER_SIZE,
|
||||
.output_buf_size = 0,
|
||||
};
|
||||
args.command = rx_header->cmd_id;
|
||||
args.version = rx_header->cmd_ver;
|
||||
args.input_buf_size = rx_header->data_len;
|
||||
args.output_buf_size = 0;
|
||||
|
||||
if (found_handler->min_rqt_size > args.input_buf_size) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_REQUEST_TRUNCATED);
|
||||
status = validate_handler(found_handler, &args);
|
||||
if (status != EC_HOST_CMD_SUCCESS) {
|
||||
send_error_response(hc->backend, tx, status);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found_handler->min_rsp_size > args.output_buf_max) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_INVALID_RESPONSE);
|
||||
status = found_handler->handler(&args);
|
||||
if (status != EC_HOST_CMD_SUCCESS) {
|
||||
send_error_response(hc->backend, tx, status);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (args.version > sizeof(found_handler->version_mask) ||
|
||||
!(found_handler->version_mask & BIT(args.version))) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_INVALID_VERSION);
|
||||
status = prepare_response(tx, args.output_buf_size);
|
||||
if (status != EC_HOST_CMD_SUCCESS) {
|
||||
send_error_response(hc->backend, tx, status);
|
||||
continue;
|
||||
}
|
||||
|
||||
const enum ec_host_cmd_status handler_rv =
|
||||
found_handler->handler(&args);
|
||||
|
||||
if (handler_rv != EC_HOST_CMD_SUCCESS) {
|
||||
send_error_response(ec_host_cmd_dev, handler_rv);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct ec_host_cmd_response_header *const tx_header =
|
||||
(void *)tx_buffer;
|
||||
|
||||
tx_header->prtcl_ver = 3;
|
||||
tx_header->result = EC_HOST_CMD_SUCCESS;
|
||||
tx_header->data_len = args.output_buf_size;
|
||||
tx_header->reserved = 0;
|
||||
|
||||
const uint16_t tx_valid_data_size =
|
||||
tx_header->data_len + TX_HEADER_SIZE;
|
||||
if (tx_valid_data_size > sizeof(tx_buffer)) {
|
||||
send_error_response(ec_host_cmd_dev,
|
||||
EC_HOST_CMD_INVALID_RESPONSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Calculate checksum */
|
||||
tx_header->checksum = 0;
|
||||
tx_header->checksum =
|
||||
cal_checksum(tx_buffer, tx_valid_data_size);
|
||||
|
||||
const struct ec_host_cmd_tx_buf tx = {
|
||||
.buf = tx_buffer,
|
||||
.len = tx_valid_data_size,
|
||||
};
|
||||
|
||||
ec_host_cmd_backend_send(ec_host_cmd_dev, &tx);
|
||||
hc->backend->api->send(hc->backend);
|
||||
}
|
||||
}
|
||||
|
||||
K_THREAD_DEFINE(ec_host_cmd_handler_tid, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE,
|
||||
handle_host_cmds_entry, NULL, NULL, NULL,
|
||||
CONFIG_EC_HOST_CMD_HANDLER_PRIO, 0, 0);
|
||||
int ec_host_cmd_init(struct ec_host_cmd_backend *backend)
|
||||
{
|
||||
struct ec_host_cmd *hc = &ec_host_cmd;
|
||||
int ret;
|
||||
uint8_t *handler_tx_buf, *handler_rx_buf;
|
||||
|
||||
hc->backend = backend;
|
||||
|
||||
/* Allow writing to rx buff at startup */
|
||||
k_sem_init(&hc->rx_ctx.handler_owns, 0, 1);
|
||||
|
||||
handler_tx_buf = hc->tx.buf;
|
||||
handler_rx_buf = hc->rx_ctx.buf;
|
||||
|
||||
ret = backend->api->init(backend, &hc->rx_ctx, &hc->tx);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!hc->tx.buf | !hc->rx_ctx.buf) {
|
||||
LOG_ERR("No buffer for Host Command communication");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if ((handler_tx_buf && (handler_tx_buf != hc->tx.buf)) ||
|
||||
(handler_rx_buf && (handler_rx_buf != hc->rx_ctx.buf))) {
|
||||
LOG_WRN("Host Command handler provided unused buffer");
|
||||
}
|
||||
|
||||
k_thread_create(hc->thread, hc->stack, CONFIG_EC_HOST_CMD_HANDLER_STACK_SIZE,
|
||||
ec_host_cmd_thread, (void *)hc, NULL, NULL, CONFIG_EC_HOST_CMD_HANDLER_PRIO,
|
||||
0, K_NO_WAIT);
|
||||
k_thread_name_set(hc->thread, "ec_host_cmd");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
CONFIG_EC_HOST_CMD=y
|
||||
CONFIG_EC_HOST_CMD_BACKEND=y
|
||||
CONFIG_EC_HOST_CMD_BACKEND_SIMULATOR=y
|
||||
CONFIG_ZTEST=y
|
||||
CONFIG_ZTEST_NEW_API=y
|
||||
|
|
|
@ -10,13 +10,10 @@
|
|||
|
||||
/* Variables used to record what is "sent" to host for verification. */
|
||||
K_SEM_DEFINE(send_called, 0, 1);
|
||||
struct ec_host_cmd_tx_buf sent;
|
||||
struct ec_host_cmd_tx_buf *sent;
|
||||
|
||||
static int host_send(const struct device *const dev,
|
||||
const struct ec_host_cmd_tx_buf *const buf)
|
||||
static int host_send(const struct ec_host_cmd_backend *backend)
|
||||
{
|
||||
sent.len = buf->len;
|
||||
sent.buf = buf->buf;
|
||||
k_sem_give(&send_called);
|
||||
return 0;
|
||||
}
|
||||
|
@ -124,8 +121,8 @@ static void verify_tx_data(void)
|
|||
{
|
||||
update_dut_to_host_checksum();
|
||||
|
||||
zassert_equal(sent.len, expected_tx_size(), "Sent bytes did not match");
|
||||
zassert_mem_equal(sent.buf, expected_dut_to_host, expected_tx_size(),
|
||||
zassert_equal(sent->len, expected_tx_size(), "Sent bytes did not match");
|
||||
zassert_mem_equal(sent->buf, expected_dut_to_host, expected_tx_size(),
|
||||
"Sent buffer did not match");
|
||||
}
|
||||
|
||||
|
@ -137,8 +134,8 @@ static void verify_tx_error(enum ec_host_cmd_status error)
|
|||
expected_dut_to_host->header.reserved = 0;
|
||||
update_dut_to_host_checksum();
|
||||
|
||||
zassert_equal(sent.len, expected_tx_size(), "Sent bytes did not match");
|
||||
zassert_mem_equal(sent.buf, expected_dut_to_host, expected_tx_size(),
|
||||
zassert_equal(sent->len, expected_tx_size(), "Sent bytes did not match");
|
||||
zassert_mem_equal(sent->buf, expected_dut_to_host, expected_tx_size(),
|
||||
"Sent buffer did not match");
|
||||
}
|
||||
|
||||
|
@ -439,7 +436,7 @@ ZTEST(ec_host_cmd, test_response_always_too_big)
|
|||
|
||||
static void *ec_host_cmd_tests_setup(void)
|
||||
{
|
||||
ec_host_cmd_backend_sim_install_send_cb(host_send);
|
||||
ec_host_cmd_backend_sim_install_send_cb(host_send, &sent);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue