diff --git a/include/ipc/ipc_service.h b/include/ipc/ipc_service.h index daec910dbf9..3ccdf360e5a 100644 --- a/include/ipc/ipc_service.h +++ b/include/ipc/ipc_service.h @@ -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); + /** * @} */ diff --git a/include/ipc/ipc_service_backend.h b/include/ipc/ipc_service_backend.h index 2d4dccf63c7..156d3312a34 100644 --- a/include/ipc/ipc_service_backend.h +++ b/include/ipc/ipc_service_backend.h @@ -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); }; /** diff --git a/subsys/ipc/ipc_service/ipc_service.c b/subsys/ipc/ipc_service/ipc_service.c index 0334ebe347b..200e2d7b2d5 100644 --- a/subsys/ipc/ipc_service/ipc_service.c +++ b/subsys/ipc/ipc_service/ipc_service.c @@ -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); +}