| .. _ipc_service: |
| |
| IPC service |
| ########### |
| |
| .. contents:: |
| :local: |
| :depth: 2 |
| |
| The IPC service API provides an interface to exchange data between two domains |
| or CPUs. |
| |
| Overview |
| ======== |
| |
| An IPC service communication channel consists of one instance and one or |
| several endpoints associated with the instance. |
| |
| An instance is the external representation of a physical communication channel |
| between two domains or CPUs. The actual implementation and internal |
| representation of the instance is peculiar to each backend. |
| |
| An individual instance is not used to send data between domains/CPUs. To send |
| and receive the data, the user must create (register) an endpoint in the |
| instance. This allows for the connection of the two domains of interest. |
| |
| It is possible to have zero or multiple endpoints for one single instance, |
| possibly with different priorities, and to use each to exchange data. |
| Endpoint prioritization and multi-instance ability highly depend on the backend |
| used. |
| |
| The endpoint is an entity the user must use to send and receive data between |
| two domains (connected by the instance). An endpoint is always associated to an |
| instance. |
| |
| The creation of the instances is left to the backend, usually at init time. |
| The registration of the endpoints is left to the user, usually at run time. |
| |
| The API does not mandate a way for the backend to create instances but it is |
| strongly recommended to use the devicetree to retrieve the configuration |
| parameters for an instance. Currently, each backend defines its own |
| DT-compatible configuration that is used to configure the interface at boot |
| time. |
| |
| The following usage scenarios are supported: |
| |
| * Simple data exchange. |
| * Data exchange using the no-copy API. |
| |
| Simple data exchange |
| ==================== |
| |
| To send data between domains or CPUs, an endpoint must be registered onto |
| an instance. |
| |
| See the following example: |
| |
| .. note:: |
| |
| Before registering an endpoint, the instance must be opened using the |
| :c:func:`ipc_service_open_instance` function. |
| |
| |
| .. code-block:: c |
| |
| #include <zephyr/include/ipc_service.h> |
| |
| static void bound_cb(void *priv) |
| { |
| /* Endpint bounded */ |
| } |
| |
| static void recv_cb(const void *data, size_t len, void *priv) |
| { |
| /* Data received */ |
| } |
| |
| static struct ipc_ept_cfg ept0_cfg = { |
| .name = "ept0", |
| .cb = { |
| .bound = bound_cb, |
| .received = recv_cb, |
| }, |
| }; |
| |
| int main(void) |
| { |
| const struct device *inst0; |
| struct ipc_ept ept0; |
| int ret; |
| |
| inst0 = DEVICE_DT_GET(DT_NODELABEL(ipc0)); |
| ret = ipc_service_open_instance(inst0); |
| ret = ipc_service_register_endpoint(inst0, &ept0, &ept0_cfg); |
| |
| /* Wait for endpint bound (bound_cb called) */ |
| |
| unsigned char message[] = "hello world"; |
| ret = ipc_service_send(&ept0, &message, sizeof(message)); |
| } |
| |
| Data exchange using the no-copy API |
| =================================== |
| |
| If the backend supports the no-copy API you can use it to directly write and |
| read to and from shared memory regions. |
| |
| See the following example: |
| |
| .. code-block:: c |
| |
| #include <zephyr/include/ipc_service.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| static struct ipc_ept ept0; |
| |
| static void bound_cb(void *priv) |
| { |
| /* Endpint bounded */ |
| } |
| |
| static void recv_cb_nocopy(const void *data, size_t len, void *priv) |
| { |
| int ret; |
| |
| ret = ipc_service_hold_rx_buffer(&ept0, (void *)data); |
| /* Process directly or put the buffer somewhere else and release. */ |
| ret = ipc_service_release_rx_buffer(&ept0, (void *)data); |
| } |
| |
| static struct ipc_ept_cfg ept0_cfg = { |
| .name = "ept0", |
| .cb = { |
| .bound = bound_cb, |
| .received = recv_cb, |
| }, |
| }; |
| |
| int main(void) |
| { |
| const struct device *inst0; |
| int ret; |
| |
| inst0 = DEVICE_DT_GET(DT_NODELABEL(ipc0)); |
| ret = ipc_service_open_instance(inst0); |
| ret = ipc_service_register_endpoint(inst0, &ept0, &ept0_cfg); |
| |
| /* Wait for endpint bound (bound_cb called) */ |
| void *data; |
| unsigned char message[] = "hello world"; |
| uint32_t len = sizeof(message); |
| |
| ret = ipc_service_get_tx_buffer(&ept0, &data, &len, K_FOREVER); |
| |
| memcpy(data, message, len); |
| |
| ret = ipc_service_send_nocopy(&ept0, data, sizeof(message)); |
| } |
| |
| Backends |
| ******** |
| |
| The requirements needed for implementating backends give flexibility to the IPC |
| service. These allow for the addition of dedicated backends having only a |
| subsets of features for specific use cases. |
| |
| The backend must support at least the following: |
| |
| * The init-time creation of instances. |
| * The run-time registration of an endpoint in an instance. |
| |
| Additionally, the backend can also support the following: |
| |
| * The run-time deregistration of an endpoint from the instance. |
| * The run-time closing of an instance. |
| * The no-copy API. |
| |
| Each backend can have its own limitations and features that make the backend |
| unique and dedicated to a specific use case. The IPC service API can be used |
| with multiple backends simultaneously, combining the pros and cons of each |
| backend. |
| |
| .. toctree:: |
| :maxdepth: 1 |
| |
| backends/ipc_service_icmsg.rst |
| |
| API Reference |
| ************* |
| |
| IPC service API |
| =============== |
| |
| .. doxygengroup:: ipc_service_api |
| |
| IPC service backend API |
| ======================= |
| |
| .. doxygengroup:: ipc_service_backend |