zephyr/include/zephyr/ipc/ipc_service.h

486 lines
18 KiB
C
Raw Permalink Normal View History

/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_IPC_IPC_SERVICE_H_
#define ZEPHYR_INCLUDE_IPC_IPC_SERVICE_H_
#include <stdio.h>
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IPC
* @defgroup ipc IPC
* @ingroup os_services
* @{
* @}
*/
/**
* @brief IPC Service API
* @defgroup ipc_service_api IPC service APIs
* @ingroup ipc
* @{
*/
/**
* @cond INTERNAL_HIDDEN
*
* These are for internal use only, so skip these in
* public documentation.
*/
/**
* Some terminology:
*
* - INSTANCE: an instance is the external representation of a physical
* communication channel between two domains / CPUs.
*
* The actual implementation and internal representation of the
* instance is peculiar to each backend. For example for
* OpenAMP-based backends, an instance is usually represented by a
* shared memory region and a couple of IPM devices for RX/TX
* signalling.
*
* It's important to note that an instance per se is not used to
* send data between domains / CPUs. To send and receive data the
* user have to create (register) an endpoint in the instance
* connecting the two domains of interest.
*
* It's possible to have zero or multiple endpoints in one single
* instance, each one used to exchange data, possibly with different
* priorities.
*
* The creation of the instances is left to the backend (usually at
* init time), while the registration of the endpoints is left to
* the user (usually at run time).
*
* - ENDPOINT: an endpoint is the entity the user must use to send / receive
* data between two domains (connected by the instance). An
* endpoint is always associated to an instance.
*
* - BACKEND: the backend must take care of at least two different things:
*
* 1) creating the instances at init time
* 2) creating / registering the endpoints onto an instance at run
* time when requested by the user
*
* The API doesn't mandate a way for the backend to create the
* instances but itis strongly recommended to use the DT to retrieve
* the configuration parameters for the instance.
*
* Common API usage from the application prospective:
*
* HOST REMOTE
* -----------------------------------------------------------------------------
* # Open the (same) instance on host and remote
* ipc_service_open() ipc_service_open()
*
* # Register the endpoints
* ipc_service_register_endpoint() ipc_service_register_endpoint()
* .bound() .bound()
*
* # After the .bound() callbacks are received the communication channel
* # is ready to be used
*
* # Start sending and receiving data
* ipc_service_send()
* .receive()
* ipc_service_send()
* .receive()
*
*
* Common API usage from the application prospective when using NOCOPY feature:
*
* HOST REMOTE
* -----------------------------------------------------------------------------
* ipc_service_open() ipc_service_open()
*
* ipc_service_register_endpoint() ipc_service_register_endpoint()
* .bound() .bound()
*
* # Get a pointer to an available TX buffer
* ipc_service_get_tx_buffer()
*
* # Fill the buffer with data
*
* # Send out the buffer
* ipc_service_send_nocopy()
* .receive()
*
* # Get hold of the received RX buffer
* # in the .receive callback
* ipc_service_hold_rx_buffer()
*
* # Copy the data out of the buffer at
* # user convenience
*
* # Release the buffer when done
* ipc_service_release_rx_buffer()
*
* # Get another TX buffer
* ipc_service_get_tx_buffer()
*
* # We can also drop it if needed
* ipc_service_drop_tx_buffer()
*
*/
/**
* @endcond
*/
/** @brief Event callback structure.
*
* It is registered during endpoint registration.
* This structure is part of the endpoint configuration.
*/
struct ipc_service_cb {
/** @brief Bind was successful.
*
* This callback is called when the endpoint binding is successful.
*
* @param[in] priv Private user data.
*/
void (*bound)(void *priv);
/** @brief New packet arrived.
*
* This callback is called when new data is received.
*
* @note When @ref ipc_service_hold_rx_buffer is not used, the data
* buffer is to be considered released and available again only
* when this callback returns.
*
* @param[in] data Pointer to data buffer.
* @param[in] len Length of @a data.
* @param[in] priv Private user data.
*/
void (*received)(const void *data, size_t len, void *priv);
/** @brief An error occurred.
*
* @param[in] message Error message.
* @param[in] priv Private user data.
*/
void (*error)(const char *message, void *priv);
};
/** @brief Endpoint instance.
*
* Token is not important for user of the API. It is implemented in a
* specific backend.
*/
struct ipc_ept {
/** Instance this endpoint belongs to. */
const struct device *instance;
/** Backend-specific token used to identify an endpoint in an instance. */
void *token;
};
/** @brief Endpoint configuration structure. */
struct ipc_ept_cfg {
/** Name of the endpoint. */
const char *name;
/** Endpoint priority. If the backend supports priorities. */
int prio;
/** Event callback structure. */
struct ipc_service_cb cb;
/** Private user data. */
void *priv;
};
ipc_service: Add open_instance function As know, an instance is the representation of a physical communication channel between two domains / CPUs. This communication channel must be usually known and initialized (that is "opened") by both parties in the communication before a proper communication can be instaurated using endpoints. Depending on the backend and on the library / protocol used by the backend, this "opening" can go through some handshaking or synchronization procedure run by the parties that sometimes can be blocking or time-consuming. For example in the simplest case of a backend using OpenAMP, the remote side of the communication is waiting for the local part to be up and running by loop-waiting on some flag set in the shared memory by the local party. This is a blocking process so a particular attention must be paid to where this is going to be placed in the backend code. Currently it is only possible to have this synchronization procedure in two points: (1) the init function of the instance, (2) during ipc_service_register_endpoint(). It should be highly discouraged to put any blocking routine in the init code, so (1) must be excluded. It is also frowned upon using the endpoint registration function (2) because the synchronization is something concerning the instance, not the single endpoints. This patch is adding a new optional ipc_service_open_instance() function that can be used to host the handshaking or synchronization code between the two parties of the instance. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
2021-11-11 16:34:51 +01:00
/** @brief Open an instance
*
* Function to be used to open an instance before being able to register a new
* endpoint on it.
*
* @param[in] instance Instance to open.
*
ipc_service: Add open_instance function As know, an instance is the representation of a physical communication channel between two domains / CPUs. This communication channel must be usually known and initialized (that is "opened") by both parties in the communication before a proper communication can be instaurated using endpoints. Depending on the backend and on the library / protocol used by the backend, this "opening" can go through some handshaking or synchronization procedure run by the parties that sometimes can be blocking or time-consuming. For example in the simplest case of a backend using OpenAMP, the remote side of the communication is waiting for the local part to be up and running by loop-waiting on some flag set in the shared memory by the local party. This is a blocking process so a particular attention must be paid to where this is going to be placed in the backend code. Currently it is only possible to have this synchronization procedure in two points: (1) the init function of the instance, (2) during ipc_service_register_endpoint(). It should be highly discouraged to put any blocking routine in the init code, so (1) must be excluded. It is also frowned upon using the endpoint registration function (2) because the synchronization is something concerning the instance, not the single endpoints. This patch is adding a new optional ipc_service_open_instance() function that can be used to host the handshaking or synchronization code between the two parties of the instance. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
2021-11-11 16:34:51 +01:00
* @retval -EINVAL when instance configuration is invalid.
* @retval -EIO when no backend is registered.
* @retval -EALREADY when the instance is already opened (or being opened).
*
* @retval 0 on success or when not implemented on the backend (not needed).
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_open_instance(const struct device *instance);
/** @brief Close an instance
*
* Function to be used to close an instance. All bounded endpoints must be
* deregistered using ipc_service_deregister_endpoint before this
* is called.
*
* @param[in] instance Instance to close.
*
* @retval -EINVAL when instance configuration is invalid.
* @retval -EIO when no backend is registered.
* @retval -EALREADY when the instance is not already opened.
* @retval -EBUSY when an endpoint exists that hasn't been
* deregistered
*
* @retval 0 on success or when not implemented on the backend (not needed).
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_close_instance(const struct device *instance);
ipc_service: Add open_instance function As know, an instance is the representation of a physical communication channel between two domains / CPUs. This communication channel must be usually known and initialized (that is "opened") by both parties in the communication before a proper communication can be instaurated using endpoints. Depending on the backend and on the library / protocol used by the backend, this "opening" can go through some handshaking or synchronization procedure run by the parties that sometimes can be blocking or time-consuming. For example in the simplest case of a backend using OpenAMP, the remote side of the communication is waiting for the local part to be up and running by loop-waiting on some flag set in the shared memory by the local party. This is a blocking process so a particular attention must be paid to where this is going to be placed in the backend code. Currently it is only possible to have this synchronization procedure in two points: (1) the init function of the instance, (2) during ipc_service_register_endpoint(). It should be highly discouraged to put any blocking routine in the init code, so (1) must be excluded. It is also frowned upon using the endpoint registration function (2) because the synchronization is something concerning the instance, not the single endpoints. This patch is adding a new optional ipc_service_open_instance() function that can be used to host the handshaking or synchronization code between the two parties of the instance. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
2021-11-11 16:34:51 +01:00
/** @brief Register IPC endpoint onto an instance.
*
* Registers IPC endpoint onto an instance to enable communication with a
* remote device.
*
* The same function registers endpoints for both host and remote devices.
*
* @param[in] instance Instance to register the endpoint onto.
* @param[in] ept Endpoint object.
* @param[in] cfg Endpoint configuration.
*
* @note Keep the variable pointed by @p cfg alive when endpoint is in use.
*
* @retval -EIO when no backend is registered.
* @retval -EINVAL when instance, endpoint or configuration is invalid.
* @retval -EBUSY when the instance is busy.
*
* @retval 0 on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_register_endpoint(const struct device *instance,
struct ipc_ept *ept,
const struct ipc_ept_cfg *cfg);
/** @brief Deregister an IPC endpoint from its instance.
*
* Deregisters an IPC endpoint from its instance.
*
* The same function deregisters endpoints for both host and remote devices.
*
* @param[in] ept Endpoint object.
*
* @retval -EIO when no backend is registered.
* @retval -EINVAL when instance, endpoint or configuration is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -EBUSY when the instance is busy.
*
* @retval 0 on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_deregister_endpoint(struct ipc_ept *ept);
/** @brief Send data using given IPC endpoint.
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
* @param[in] data Pointer to the buffer to send.
* @param[in] len Number of bytes to send.
*
* @retval -EIO when no backend is registered or send hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -EBADMSG when the data is invalid (i.e. invalid data format,
* invalid length, ...)
* @retval -EBUSY when the instance is busy.
* @retval -ENOMEM when no memory / buffers are available.
*
* @retval bytes number of bytes sent.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_send(struct ipc_ept *ept, const void *data, size_t len);
/** @brief Get the TX buffer size
*
* Get the maximal size of a buffer which can be obtained by @ref
* ipc_service_get_tx_buffer
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
*
* @retval -EIO when no backend is registered or send hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -ENOTSUP when the operation is not supported by backend.
*
* @retval size TX buffer size on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_get_tx_buffer_size(struct ipc_ept *ept);
/** @brief Get an empty TX buffer to be sent using @ref ipc_service_send_nocopy
*
* This function can be called to get an empty TX buffer so that the
* application can directly put its data into the sending buffer without copy
* from an application buffer.
*
* It is the application responsibility to correctly fill the allocated TX
* buffer with data and passing correct parameters to @ref
* ipc_service_send_nocopy function to perform data no-copy-send mechanism.
*
* The size parameter can be used to request a buffer with a certain size:
* - if the size can be accommodated the function returns no errors and the
* buffer is allocated
* - if the requested size is too big, the function returns -ENOMEM and the
* the buffer is not allocated.
* - if the requested size is '0' the buffer is allocated with the maximum
* allowed size.
*
* In all the cases on return the size parameter contains the maximum size for
* the returned buffer.
*
* When the function returns no errors, the buffer is intended as allocated
* and it is released under two conditions: (1) when sending the buffer using
* @ref ipc_service_send_nocopy (and in this case the buffer is automatically
* released by the backend), (2) when using @ref ipc_service_drop_tx_buffer on
* a buffer not sent.
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
* @param[out] data Pointer to the empty TX buffer.
* @param[in,out] size Pointer to store the requested TX buffer size. If the
* function returns -ENOMEM, this parameter returns the
* maximum allowed size.
* @param[in] wait Timeout waiting for an available TX buffer.
*
* @retval -EIO when no backend is registered or send hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -ENOTSUP when the operation or the timeout is not supported by backend.
* @retval -ENOBUFS when there are no TX buffers available.
* @retval -EALREADY when a buffer was already claimed and not yet released.
* @retval -ENOMEM when the requested size is too big (and the size parameter
* contains the maximum allowed size).
*
* @retval 0 on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_get_tx_buffer(struct ipc_ept *ept, void **data, uint32_t *size, k_timeout_t wait);
/** @brief Drop and release a TX buffer
*
* Drop and release a TX buffer. It is possible to drop only TX buffers
* obtained by using @ref ipc_service_get_tx_buffer.
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
* @param[in] data Pointer to the TX buffer.
*
* @retval -EIO when no backend is registered or send hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -ENOTSUP when this is not supported by backend.
* @retval -EALREADY when the buffer was already dropped.
* @retval -ENXIO when the buffer was not obtained using @ref
* ipc_service_get_tx_buffer
*
* @retval 0 on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_drop_tx_buffer(struct ipc_ept *ept, const void *data);
/** @brief Send data in a TX buffer reserved by @ref ipc_service_get_tx_buffer
* using the given IPC endpoint.
*
* This is equivalent to @ref ipc_service_send but in this case the TX buffer
* has been obtained by using @ref ipc_service_get_tx_buffer.
*
* The application has to take the responsibility for getting the TX buffer
* using @ref ipc_service_get_tx_buffer and filling the TX buffer with the data.
*
* After the @ref ipc_service_send_nocopy function is issued the TX buffer is
* no more owned by the sending task and must not be touched anymore unless
* the function fails and returns an error.
*
* If this function returns an error, @ref ipc_service_drop_tx_buffer can be
* used to drop the TX buffer.
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
* @param[in] data Pointer to the buffer to send obtained by @ref
* ipc_service_get_tx_buffer.
* @param[in] len Number of bytes to send.
*
* @retval -EIO when no backend is registered or send hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -EBADMSG when the data is invalid (i.e. invalid data format,
* invalid length, ...)
* @retval -EBUSY when the instance is busy.
*
* @retval bytes number of bytes sent.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_send_nocopy(struct ipc_ept *ept, const void *data, size_t len);
/** @brief Holds the RX buffer for usage outside the receive callback.
*
* Calling this function prevents the receive buffer from being released
* back to the pool of shmem buffers. This function can be called in the
* receive callback when the user does not want to copy the message out in
* the callback itself.
*
* After the message is processed, the application must release the buffer
* using the @ref ipc_service_release_rx_buffer function.
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
* @param[in] data Pointer to the RX buffer to hold.
*
* @retval -EIO when no backend is registered or release hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -EALREADY when the buffer data has been hold already.
* @retval -ENOTSUP when this is not supported by backend.
*
* @retval 0 on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_hold_rx_buffer(struct ipc_ept *ept, void *data);
/** @brief Release the RX buffer for future reuse.
*
* When supported by the backend, this function can be called after the
* received message has been processed and the buffer can be marked as
* reusable again.
*
* It is possible to release only RX buffers on which @ref
* ipc_service_hold_rx_buffer was previously used.
*
* @param[in] ept Registered endpoint by @ref ipc_service_register_endpoint.
* @param[in] data Pointer to the RX buffer to release.
*
* @retval -EIO when no backend is registered or release hook is missing from
* backend.
* @retval -EINVAL when instance or endpoint is invalid.
* @retval -ENOENT when the endpoint is not registered with the instance.
* @retval -EALREADY when the buffer data has been already released.
* @retval -ENOTSUP when this is not supported by backend.
* @retval -ENXIO when the buffer was not hold before using @ref
* ipc_service_hold_rx_buffer
*
* @retval 0 on success.
* @retval other errno codes depending on the implementation of the backend.
*/
int ipc_service_release_rx_buffer(struct ipc_ept *ept, void *data);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_IPC_IPC_SERVICE_H_ */