AMP相关:3 Linux rpmsg子系统(STM32MP157方案)
rpmsg框架位于virtio上层,rpmsg总线是一种基于virtio的消息总线。
rpmsg子系统包含如下部分:
- rpmsg核心提供rpmsg_bus总线,以及rpmsg驱动和设备注册注销接口。
- rpmsg通道提供主处理器和远程处理器之间的通信通道。这些通道通过文本名称和本地(源)及远程(目的)地址来标识。
- rpmsg端点每个端点都有一个唯一的源地址和相关联的回调函数。当应用程序创建一个端点时,所有目标地址等于端点本地地址的传入消息都会被路由到该回调函数。
- remoteproc允许不同平台或架构控制远程处理器,同时抽象出硬件差异,为支持 RPMsg 协议的远程处理器添加virtio设备。
1 rpmsg配置
rpmsg子系统配置:
Device Drivers
Rpmsg drivers
Virtio RPMSG bus driver
RPMSG tty driver
2 rpmsg文件
rpmsg子系统相关文件:
drivers/rpmsg/
├── rpmsg_core.c--rpmsg子系统初始化、设备驱动注册注销函数、消息收发函数。 ├── rpmsg_tty.c--创建/dev/ttyRPMSGX设备。 └── virtio_rpmsg_bus.c--基于virtio总线的消息处理。
3 rpmsg API和数据结构
struct rpmsg_channel_info提供了有关 rpmsg 通道的信息,例如通道的源地址、目标地址、通道名称。它用于在 rpmsg 设备之间建立和识别通信通道。
struct rpmsg_channel_info { char name[RPMSG_NAME_SIZE]; u32 src; u32 dst; };
struct rpmsg_endpoint代表一个 rpmsg 端点,即通信的一端。它可能包含端点的标识符、端点名称、发送和接收缓冲区、以及与特定 rpmsg 设备相关联的指针等。
struct rpmsg_endpoint { struct rpmsg_device *rpdev; struct kref refcount; rpmsg_rx_cb_t cb; struct mutex cb_lock; u32 addr; void *priv; const struct rpmsg_endpoint_ops *ops; };
struct rpmsg_device代表一个 rpmsg 设备,它是一个物理或虚拟的设备,用于 rpmsg 通信。它可能包含设备名称、设备编号、端点列表、以及与 rpmsg 通道相关的信息。
struct rpmsg_device { struct device dev; struct rpmsg_device_id id; char *driver_override; u32 src; u32 dst; struct rpmsg_endpoint *ept; bool announce; const struct rpmsg_device_ops *ops; };
struct rpmsg_driver定义了 rpmsg 驱动程序的特定操作:
struct rpmsg_driver { struct device_driver drv; const struct rpmsg_device_id *id_table; int (*probe)(struct rpmsg_device *dev);--当一个channel匹配后,调用probe函数。 void (*remove)(struct rpmsg_device *dev); int (*callback)(struct rpmsg_device *, void *, int, void *, u32); };
驱动和设备的注册注销函数:
int register_rpmsg_device(struct rpmsg_device *dev); void unregister_rpmsg_device(struct rpmsg_device *dev); int __register_rpmsg_driver(struct rpmsg_driver *drv, struct module *owner); void unregister_rpmsg_driver(struct rpmsg_driver *drv); void rpmsg_destroy_ept(struct rpmsg_endpoint *);
rpmsg通道创建销毁和消息收发:
void rpmsg_destroy_ept(struct rpmsg_endpoint *);--创建和销毁用户传输rpmsg的endpoint。 struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo); int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len); int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len); int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait);
4 rpmsg子系统初始化
4.1 rpmsg子系统初始化
rpmsg_init()进行rpmsg总线注册:
rpmsg_init
bus_register--创建rpmsg_bus总线。
rpmsg_bus定义了总线名称,以及总线上的操作:
static struct bus_type rpmsg_bus = { .name = "rpmsg", .match = rpmsg_dev_match, .dev_groups = rpmsg_dev_groups, .uevent = rpmsg_uevent, .probe = rpmsg_dev_probe, .remove = rpmsg_dev_remove, };
rpmsg_dev_probe在总线上设备match后执行:
rpmsg_dev_probe
rpmsg_create_ept--当驱动支持callback,创建endpoint。
--调用remoteproc驱动的probe。
4.2 rpmsg virtio驱动注册
rpmsg_init()注册一个virtio_driver:
- 根据需求创建tx/rx两个virtqueue,以及赋值回调函数。
- 根据vring中buffer数量和buffer大小,申请DMA内存,并将申请到的内存分配给vring中的buffer。
- 创建endpoint。
rpmsg_init
register_virtio_driver--注册virtio_ipc_driver到virtio_bus总线。
virtio_ipc_driver
rpmsg_probe
virtio_find_vqs--调用virtio的rproc_virtio_config_ops->find_vqs创建virtqueue,并且匹配回调函数。
rpmsg_recv_done
virtqueue_get_buf--获取被使用的ring buffer。
rpmsg_recv_single--处理接收到的ring buffer。
idr_find--根据msg->dst找到对应的endpoint。
--调用endpoint的callback函数。
rpmsg_sg_init
virtqueue_add_inbuf--返还缓存给remoteproc virtqueue。
virtqueue_kick--告诉remoteproc返还空闲buffer。
rpmsg_xmit_done
virtqueue_get_vring_size--获取vring大小,包含rx/rx两个vring,已经vring中每个buf大小。据此可以计算出virtqueue所需要的内存大小。
dma_alloc_coherent--分配virtqueue缓存,共分成32个,每个buffer512字节。rx使用vring0,占用前16个buffer;tx占用后16个。
rpmsg_sg_init
virtqueue_add_inbuf--将生成的buffer加入到virtqueue中,交给virtqueue管理,并且对端也可以使用。
virtqueue_disable_cb
__rpmsg_create_ept--创建endpoint,回调函数为rpmsg_ns_cb。
rpmsg_ns_cb--rpmsg在收到字符串后,根据消息内容创建或者销毁channel。
rpmsg_create_channel--创建channel的情况。
rpmsg_find_device--查找channel是否已经存在。
rpmsg_register_device--在rpmsg_bus上创建rpmsg设备,如果收到字符串“rpmsg-tty-channel”,则和rpmsg tty驱动匹配进行probe创建/dev/ttyRPMSGx设备。
virtqueue_kick_prepare
virtio_device_ready--至此rpmsg功能处于可用状态。
注册创建virtio_device在remoteproc中here。
static struct virtio_driver virtio_ipc_driver = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table,--VIRTIO_DEV_ANY_ID表示任何vendor id都会创建。 .probe = rpmsg_probe, .remove = rpmsg_remove, };
4.3 rpmsg tty驱动注册
rpmsg tty驱动根据接收到的消息动态创建/dev/ttyRPMSGX设备:
- 分配tty驱动结构体,并初始化。
- 注册tty启动。
- 注册rpmsg驱动。
- 在收到rpmsg收到消息然后创建rpmsg设备时,调用rpmsg_tty_probe创建/dev/ttyRPMSGX设备。
- rpmsg总线还创建了endpoint用于通过tty和rpmsg通信。
rpmsg_tty_init
tty_alloc_driver--分配一个tty driver即rpmsg_tty_driver。然后进行初始化。
tty_set_operations--设置tty驱动的操作函数集为rpmsg_tty_ops。
tty_register_driver--注册rpmsg_tty_driver。
register_rpmsg_driver--注册rpmsg_tty_rmpsg_drv驱动到rpmsg子系统。
rpmsg_tty_probe--如果M4发送rpmsg-tty-channel字符,rpmsg在收到消息后创建设备时调用。
tty_port_init--初始化一个tty端口。
tty_port_register_device--注册tty设备/dev/ttyRPMSGX。
rpmsg_tty_ops是/dev/ttyRPMSGX的对应的tty操作函数集,它建立了tty设备和rpmsg之间的关联:
static const struct tty_operations rpmsg_tty_ops = { .install = rpmsg_tty_install, .open = rpmsg_tty_open, .close = rpmsg_tty_close, .write = rpmsg_tty_write, .write_room = rpmsg_tty_write_room, };
rpmsg_tty_rmpsg_drv是rpmsg tty驱动:
static struct rpmsg_driver rpmsg_tty_rmpsg_drv = { .drv.name = KBUILD_MODNAME, .drv.owner = THIS_MODULE, .id_table = rpmsg_driver_tty_id_table, .probe = rpmsg_tty_probe, .callback = rpmsg_tty_cb, .remove = rpmsg_tty_remove, };
其中rpmsg_tty_cb是rpmsg接收endpint消息后的回调函数:
rpmsg_tty_cb
tty_prepare_flip_string
tty_flip_buffer_push--将接收到的字符串通过tty发送出去。