ipc_service: Extend API with nocopy functions
Add several new functions to the IPC service API: - ipc_service_get_tx_buffer() - ipc_service_drop_tx_buffer() - ipc_service_send_nocopy() - ipc_service_release_rx_buffer() - ipc_service_hold_rx_buffer() This set of function is used to support backends with nocopy capability. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
parent
065eda4675
commit
7cb91be0c0
3 changed files with 460 additions and 3 deletions
|
@ -77,6 +77,41 @@ extern "C" {
|
|||
* 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()
|
||||
*
|
||||
*/
|
||||
|
||||
/** @brief Event callback structure.
|
||||
|
@ -97,8 +132,9 @@ struct ipc_service_cb {
|
|||
*
|
||||
* This callback is called when new data is received.
|
||||
*
|
||||
* @note The data buffer is to be considered released and available
|
||||
* again only when this callback returs.
|
||||
* @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.
|
||||
|
@ -203,6 +239,174 @@ int ipc_service_register_endpoint(const struct device *instance,
|
|||
*/
|
||||
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 -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 -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 -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 -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 release.
|
||||
*
|
||||
* @retval -EIO when no backend is registered or release hook is missing from
|
||||
* backend.
|
||||
* @retval -EINVAL when instance or endpoint is invalid.
|
||||
* @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 -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -48,7 +48,7 @@ struct ipc_service_backend {
|
|||
* @retval -EBADMSG when the message is invalid.
|
||||
* @retval -EBUSY when the instance is busy or not ready.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval bytes number of bytes sent.
|
||||
* @retval other errno codes depending on the implementation of the
|
||||
* backend.
|
||||
*/
|
||||
|
@ -71,6 +71,114 @@ struct ipc_service_backend {
|
|||
int (*register_endpoint)(const struct device *instance,
|
||||
void **token,
|
||||
const struct ipc_ept_cfg *cfg);
|
||||
|
||||
/** @brief Pointer to the function that will return the TX buffer size
|
||||
*
|
||||
* @param[in] instance Instance pointer.
|
||||
* @param[in] token Backend-specific token.
|
||||
*
|
||||
* @retval -EINVAL when instance is invalid.
|
||||
* @retval -ENOTSUP when the operation is not supported.
|
||||
*
|
||||
* @retval size TX buffer size on success.
|
||||
* @retval other errno codes depending on the implementation of the
|
||||
* backend.
|
||||
*/
|
||||
int (*get_tx_buffer_size)(const struct device *instance, void *token);
|
||||
|
||||
/** @brief Pointer to the function that will return an empty TX buffer.
|
||||
*
|
||||
* @param[in] instance Instance pointer.
|
||||
* @param[in] token Backend-specific token.
|
||||
* @param[out] data Pointer to the empty TX buffer.
|
||||
* @param[in,out] len Pointer to store the TX buffer size.
|
||||
* @param[in] wait Timeout waiting for an available TX buffer.
|
||||
*
|
||||
* @retval -EINVAL when instance is invalid.
|
||||
* @retval -ENOTSUP when the operation or the timeout is not supported.
|
||||
* @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 (*get_tx_buffer)(const struct device *instance, void *token,
|
||||
void **data, uint32_t *len, k_timeout_t wait);
|
||||
|
||||
/** @brief Pointer to the function that will drop a TX buffer.
|
||||
*
|
||||
* @param[in] instance Instance pointer.
|
||||
* @param[in] token Backend-specific token.
|
||||
* @param[in] data Pointer to the TX buffer.
|
||||
*
|
||||
* @retval -EINVAL when instance is invalid.
|
||||
* @retval -ENOTSUP when this function is not supported.
|
||||
* @retval -EALREADY when the buffer was already dropped.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval other errno codes depending on the implementation of the
|
||||
* backend.
|
||||
*/
|
||||
int (*drop_tx_buffer)(const struct device *instance, void *token,
|
||||
const void *data);
|
||||
|
||||
/** @brief Pointer to the function that will be used to send data to
|
||||
* the endpoint when the TX buffer has been obtained using @ref
|
||||
* ipc_service_get_tx_buffer
|
||||
*
|
||||
* @param[in] instance Instance pointer.
|
||||
* @param[in] token Backend-specific token.
|
||||
* @param[in] data Pointer to the buffer to send.
|
||||
* @param[in] len Number of bytes to send.
|
||||
*
|
||||
* @retval -EINVAL when instance is invalid.
|
||||
* @retval -EBADMSG when the data is invalid (i.e. invalid data format,
|
||||
* invalid length, ...)
|
||||
* @retval -EBUSY when the instance is busy or not ready.
|
||||
*
|
||||
* @retval bytes number of bytes sent.
|
||||
* @retval other errno codes depending on the implementation of the
|
||||
* backend.
|
||||
*/
|
||||
int (*send_nocopy)(const struct device *instance, void *token,
|
||||
const void *data, size_t len);
|
||||
|
||||
/** @brief Pointer to the function that will hold the RX buffer
|
||||
*
|
||||
* @param[in] instance Instance pointer.
|
||||
* @param[in] token Backend-specific token.
|
||||
* @param[in] data Pointer to the RX buffer to hold.
|
||||
*
|
||||
* @retval -EINVAL when instance is invalid.
|
||||
* @retval -EALREADY when the buffer data has been already hold.
|
||||
* @retval -ENOTSUP when this function is not supported.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval other errno codes depending on the implementation of the
|
||||
* backend.
|
||||
*/
|
||||
int (*hold_rx_buffer)(const struct device *instance, void *token,
|
||||
void *data);
|
||||
|
||||
/** @brief Pointer to the function that will release the RX buffer.
|
||||
*
|
||||
* @param[in] instance Instance pointer.
|
||||
* @param[in] token Backend-specific token.
|
||||
* @param[in] data Pointer to the RX buffer to release.
|
||||
*
|
||||
* @retval -EINVAL when instance is invalid.
|
||||
* @retval -EALREADY when the buffer data has been already released.
|
||||
* @retval -ENOTSUP when this function is not supported.
|
||||
*
|
||||
* @retval 0 on success
|
||||
* @retval other errno codes depending on the implementation of the
|
||||
* backend.
|
||||
*/
|
||||
int (*release_rx_buffer)(const struct device *instance, void *token,
|
||||
void *data);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,3 +81,148 @@ int ipc_service_send(struct ipc_ept *ept, const void *data, size_t len)
|
|||
|
||||
return backend->send(ept->instance, ept->token, data, len);
|
||||
}
|
||||
|
||||
int ipc_service_get_tx_buffer_size(struct ipc_ept *ept)
|
||||
{
|
||||
const struct ipc_service_backend *backend;
|
||||
|
||||
if (!ept) {
|
||||
LOG_ERR("Invalid endpoint");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
backend = ept->instance->api;
|
||||
|
||||
if (!backend) {
|
||||
LOG_ERR("Invalid backend configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!backend->get_tx_buffer_size) {
|
||||
LOG_ERR("No-copy feature not available");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return backend->get_tx_buffer_size(ept->instance, ept->token);
|
||||
}
|
||||
|
||||
int ipc_service_get_tx_buffer(struct ipc_ept *ept, void **data, uint32_t *len, k_timeout_t wait)
|
||||
{
|
||||
const struct ipc_service_backend *backend;
|
||||
|
||||
if (!ept || !data || !len) {
|
||||
LOG_ERR("Invalid endpoint, data or len pointer");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
backend = ept->instance->api;
|
||||
|
||||
if (!backend) {
|
||||
LOG_ERR("Invalid backend configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!backend->send_nocopy || !backend->get_tx_buffer) {
|
||||
LOG_ERR("No-copy feature not available");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return backend->get_tx_buffer(ept->instance, ept->token, data, len, wait);
|
||||
}
|
||||
|
||||
int ipc_service_drop_tx_buffer(struct ipc_ept *ept, const void *data)
|
||||
{
|
||||
const struct ipc_service_backend *backend;
|
||||
|
||||
if (!ept || !data) {
|
||||
LOG_ERR("Invalid endpoint or data pointer");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
backend = ept->instance->api;
|
||||
|
||||
if (!backend) {
|
||||
LOG_ERR("Invalid backend configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!backend->drop_tx_buffer) {
|
||||
LOG_ERR("No-copy feature not available");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return backend->drop_tx_buffer(ept->instance, ept->token, data);
|
||||
}
|
||||
|
||||
int ipc_service_send_nocopy(struct ipc_ept *ept, const void *data, size_t len)
|
||||
{
|
||||
const struct ipc_service_backend *backend;
|
||||
|
||||
if (!ept) {
|
||||
LOG_ERR("Invalid endpoint");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
backend = ept->instance->api;
|
||||
|
||||
if (!backend) {
|
||||
LOG_ERR("Invalid backend configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!backend->get_tx_buffer || !backend->send_nocopy) {
|
||||
LOG_ERR("No-copy feature not available");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return backend->send_nocopy(ept->instance, ept->token, data, len);
|
||||
}
|
||||
|
||||
int ipc_service_hold_rx_buffer(struct ipc_ept *ept, void *data)
|
||||
{
|
||||
const struct ipc_service_backend *backend;
|
||||
|
||||
if (!ept) {
|
||||
LOG_ERR("Invalid endpoint");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
backend = ept->instance->api;
|
||||
|
||||
if (!backend) {
|
||||
LOG_ERR("Invalid backend configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* We also need the release function */
|
||||
if (!backend->release_rx_buffer || !backend->hold_rx_buffer) {
|
||||
LOG_ERR("No-copy feature not available");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return backend->hold_rx_buffer(ept->instance, ept->token, data);
|
||||
}
|
||||
int ipc_service_release_rx_buffer(struct ipc_ept *ept, void *data)
|
||||
{
|
||||
const struct ipc_service_backend *backend;
|
||||
|
||||
if (!ept) {
|
||||
LOG_ERR("Invalid endpoint");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
backend = ept->instance->api;
|
||||
|
||||
if (!backend) {
|
||||
LOG_ERR("Invalid backend configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* We also need the hold function */
|
||||
if (!backend->hold_rx_buffer || !backend->release_rx_buffer) {
|
||||
LOG_ERR("No-copy feature not available");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return backend->release_rx_buffer(ept->instance, ept->token, data);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue