IPC 服务

IPC 服务 API 提供了一个用于在两个域或 CPU 之间交换数据的接口。

概述

一个 IPC 服务通信通道由一个实例以及与该实例关联的一个或多个端点组成。

实例是两个域或 CPU 之间物理通信通道的外部表示。实例的实际实现和内部表示因每个后端而异。

不能使用单个实例在域/CPU 之间发送数据。要发送和接收数据,用户必须在实例中创建(注册)一个端点。这样便可将两个相关的域连接起来。

一个实例可以有零个或多个端点,这些端点的优先级可以不同,并可使用每个端点交换数据。端点的优先级排序和多实例能力很大程度上取决于所使用的后端。

端点是用户在两个域(由实例连接)之间发送和接收数据时必须使用的实体。端点始终与某个实例关联。

实例的创建由后端负责,通常在初始化时进行。端点的注册由用户负责,通常在运行时进行。

该 API 不强制后端以何种方式创建实例,但强烈建议使用 devicetree 来获取实例的配置参数。目前,每个后端都定义了自己的与 DT 兼容的配置,用于在启动时配置接口。

支持以下使用场景:

  • 简单数据交换。

  • 使用零拷贝 API 的数据交换。

简单数据交换

要在域或 CPU 之间发送数据,必须将端点注册到实例上。

参见以下示例:

备注

在注册端点之前,必须使用 ipc_service_open_instance() 函数打开实例。

#include <zephyr/ipc/ipc_service.h>

static void bound_cb(void *priv)
{
   /* Endpoint 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 endpoint bound (bound_cb called) */

   unsigned char message[] = "hello world";
   ret = ipc_service_send(&ept0, &message, sizeof(message));
}

使用零拷贝 API 的数据交换

如果后端支持零拷贝 API,则可以使用它直接读写共享内存区域。

参见以下示例:

#include <zephyr/ipc/ipc_service.h>
#include <stdint.h>
#include <string.h>

static struct ipc_ept ept0;

static void bound_cb(void *priv)
{
   /* Endpoint 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 endpoint 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));
}

后端

实现后端所需的要求为 IPC 服务提供了灵活性。这些要求允许为特定用例添加仅具有部分功能的专用后端。

后端必须至少支持以下功能:

  • 在初始化时创建实例。

  • 在运行时向实例注册端点。

此外,后端还可以支持以下功能:

  • 在运行时从实例注销端点。

  • 在运行时关闭实例。

  • 零拷贝 API。

每个后端都可以有自己的限制和特性,这使每个后端都独一无二,并专用于特定的用例。IPC 服务 API 可以同时与多个后端一起使用,从而综合每个后端的优点和缺点。

API 参考

IPC 服务 API

IPC service APIs

IPC 服务后端 API

IPC service backend