Linux rpmsg源码分析

核间通信

AMP 通信方案采用中断 + 共享内存的方式实现,发送端在更新共享内存中数据后,通过触发中断通知接收端进行处理

三种核间中断触发方式,分别是 Mailbox 中断触发、软件中断触发及SGI 触发。

rpmsg物理层

  1. kernel/drivers/rpmsg/rockchip_rpmsg_mbox.c 是注册在 Platform Bus 上的 driver,同时向 VirtIO Bus 注册
    device。它是基于 mailbox 核间中断加 Shared Memory 底层驱动接口实现的物理层(Physical Layer)
  2. kernel/drivers/rpmsg/rockchip_rpmsg_softirq.c 也是注册在 Platform Bus 上的 driver,同时向 VirtIO Bus 注册
    device。它是基于 softirq 核间中断加 Shared Memory 底层驱动接口实现的物理层(Physical Layer)。

mailbox 方式的物理层源码

结构体

  1. struct rk_virtio_dev {
        struct virtio_device vdev;       // Virtio设备结构
        unsigned int vring[2];           // 两个vring的物理地址
        struct virtqueue *vq[2];         // 两个virtqueue指针
        unsigned int base_queue_id;      // 基础队列ID
        int num_of_vqs;                  // Virtqueue数量
        struct rk_rpmsg_dev *rpdev;      // 指向父RPMSG设备
    };
    
  2. struct rk_rpmsg_dev {
        struct platform_device *pdev;     // 平台设备
        int vdev_nums;                   // virtio设备数量
        unsigned int link_id;            // 链路ID
        int first_notify;                // 首次通知标志
        u32 flags;                       // 状态标志
        struct mbox_client mbox_cl;      // 邮箱客户端
        struct mbox_chan *mbox_rx_chan;  // 接收邮箱通道
        struct mbox_chan *mbox_tx_chan;  // 发送邮箱通道
        struct rk_virtio_dev *rpvdev[RPMSG_MAX_INSTANCE_NUM]; // virtio设备数组
    };
    
  3. struct rk_rpmsg_vq_info {
        u32 queue_id;                    // 队列ID
        void *vring_addr;                // vring虚拟地址
        struct rk_rpmsg_dev *rpdev;      // 指向RPMSG设备
    };
    

probe函数

/**
 * rockchip_rpmsg_probe - Rockchip RPMSG 平台驱动探测函数
 * @pdev: 关联的平台设备
 * 
 * 这是Rockchip RPMSG驱动的主要初始化函数,负责:
 * 1. 初始化RPMSG设备结构
 * 2. 设置邮箱通信通道
 * 3. 从设备树读取配置参数
 * 4. 设置virtio环形缓冲区
 * 5. 注册virtio设备
 * 
 * 返回: 0 成功,负值表示错误
 rpmsg: rpmsg@7c00000 {
		compatible = "rockchip,rpmsg";
		mbox-names = "rpmsg-rx", "rpmsg-tx";
		mboxes = <&mailbox0 0 &mailbox0 3>;
		rockchip,vdev-nums = <1>;
		rockchip,link-id = <0x03>;
		reg = <0x0 0x7c00000 0x0 0x20000>;
		memory-region = <&rpmsg_dma_reserved>;

		status = "okay";
	};
 */
static int rockchip_rpmsg_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;          // 获取平台设备的设备结构
    struct rk_rpmsg_dev *rpdev = NULL;       // RPMSG主设备结构指针
    struct mbox_client *cl;                  // 邮箱客户端指针
    int i, ret = 0;                         // 循环变量和返回值

    /* 1. 分配并初始化RPMSG主设备结构 */
    rpdev = devm_kzalloc(dev, sizeof(*rpdev), GFP_KERNEL);
    if (!rpdev)
        return -ENOMEM;  // 内存分配失败

    dev_info(dev, "rockchip rpmsg platform probe.\n");
    rpdev->pdev = pdev;       // 保存平台设备指针
    rpdev->first_notify = 0;  // 初始化首次通知标志

    /* 2. 设置邮箱客户端 */
    cl = &rpdev->mbox_cl;     // 获取邮箱客户端结构
    cl->dev = dev;            // 设置关联设备
    cl->rx_callback = rk_rpmsg_rx_callback;  // 设置接收回调函数

    /* 3. 申请邮箱通道 */
    // 请求接收通道
    rpdev->mbox_rx_chan = mbox_request_channel_byname(cl, "rpmsg-rx");
    if (IS_ERR(rpdev->mbox_rx_chan)) {
        ret = PTR_ERR(rpdev->mbox_rx_chan);
        dev_err(dev, "failed to request mbox rx chan, ret %d\n", ret);
        return ret;  // 接收通道申请失败
    }
    
    // 请求发送通道
    rpdev->mbox_tx_chan = mbox_request_channel_byname(cl, "rpmsg-tx");
    if (IS_ERR(rpdev->mbox_tx_chan)) {
        ret = PTR_ERR(rpdev->mbox_tx_chan);
        dev_err(dev, "failed to request mbox tx chan, ret %d\n", ret);
        return ret;  // 发送通道申请失败
    }

    /* 4. 从设备树读取配置参数 */
    // 读取链路ID
    ret = device_property_read_u32(dev, "rockchip,link-id", &rpdev->link_id);
    if (ret) {
        dev_err(dev, "failed to get link_id, ret %d\n", ret);
        goto free_channel;  // 读取失败跳转到清理
    }
    
    // 读取virtio设备数量,默认为1
    ret = device_property_read_u32(dev, "rockchip,vdev-nums", &rpdev->vdev_nums);
    if (ret) {
        dev_info(dev, "vdev-nums default 1\n");
        rpdev->vdev_nums = 1;
    }
    
    // 检查设备数量是否超过最大值
    if (rpdev->vdev_nums > RPMSG_MAX_INSTANCE_NUM) {
        dev_err(dev, "vdev-nums exceed the max %d\n", RPMSG_MAX_INSTANCE_NUM);
        ret = -EINVAL;
        goto free_channel;
    }

    /* 5. 设置virtio环形缓冲区 */
    ret = rk_set_vring_phy_buf(pdev, rpdev, rpdev->vdev_nums);
    if (ret) {
        dev_err(dev, "No vring buffer.\n");
        ret = -ENOMEM;
        goto free_channel;
    }

    /* 6. 初始化保留内存(共享DMA池) */
    if (of_reserved_mem_device_init(dev)) {
        dev_info(dev, "No shared DMA pool.\n");
        rpdev->flags &= (~RPMSG_SHARED_DMA_POOL);  // 清除共享内存标志
    } else {
        rpdev->flags |= RPMSG_SHARED_DMA_POOL;     // 设置共享内存标志
    }

    /* 7. 注册virtio设备 */
    for (i = 0; i < rpdev->vdev_nums; i++) {
        // 打印vring信息
        dev_info(dev, "rpdev vdev%d: vring0 0x%x, vring1 0x%x\n",
                 i, rpdev->rpvdev[i]->vring[0], rpdev->rpvdev[i]->vring[1]);
        
        // 配置virtio设备
        rpdev->rpvdev[i]->vdev.id.device = VIRTIO_ID_RPMSG;  // 设备类型为RPMSG
        rpdev->rpvdev[i]->vdev.config = &rk_rpmsg_config_ops; // 配置操作集
        rpdev->rpvdev[i]->vdev.dev.parent = dev;              // 父设备
        rpdev->rpvdev[i]->vdev.dev.release = rk_rpmsg_vdev_release; // 释放函数
        rpdev->rpvdev[i]->base_queue_id = i * 2;             // 基础队列ID
        rpdev->rpvdev[i]->rpdev = rpdev;                     // 回指主设备
        
        // 注册virtio设备
        ret = register_virtio_device(&rpdev->rpvdev[i]->vdev);
        if (ret) {
            dev_err(dev, "fail to register rpvdev: %d\n", ret);
            goto free_reserved_mem;  // 注册失败跳转到清理
        }
    }

    // 保存驱动数据
    platform_set_drvdata(pdev, rpdev);

    return ret;  // 返回成功或错误码

/* 错误处理路径 */
free_reserved_mem:
    // 如果配置了共享内存,释放之
    if (rpdev->flags & RPMSG_SHARED_DMA_POOL)
        of_reserved_mem_device_release(dev);

free_channel:
    // 释放邮箱通道
    mbox_free_channel(rpdev->mbox_rx_chan);
    mbox_free_channel(rpdev->mbox_tx_chan);

    return ret;
}
  1. 使用了 struct mbox_client 接收,接收处理函数 rk_rpmsg_rx_callback

  2. ops 功能结构体

    1. static struct virtio_config_ops rk_rpmsg_config_ops = {
      	.get_status	= rk_rpmsg_get_status,
      	.set_status	= rk_rpmsg_set_status,
      	.reset		= rk_rpmsg_reset,
      	.find_vqs	= rk_rpmsg_find_vqs,
      	.del_vqs	= rk_rpmsg_del_vqs,
      	.get_features	= rk_rpmsg_get_features,
      	.finalize_features = rk_rpmsg_finalize_features,
      };
      
  3. rpdev->rpvdev[i]->vdev.id.device = VIRTIO_ID_RPMSG; // 设备类型为RPMSG

    1. VIRTIO_ID_RPMSG 设备注册,之后匹配了virtio_rpmsg_bus.c 的驱动MAC层
  4. mailbox为外层提供了controllerclient(remoteproc层)两者

MAC层

kernel/drivers/rpmsg/virtio_rpmsg_bus.c 是注册在 VirtIO Bus 上的 driver,同时向 RPMsg Bus 注册 device。
VirtIO 和Virtqueue 是通用 RPMsg 协议选择的MAC层(MAC Layer)

源码

结构体

  1. /**
     * struct virtproc_info - 虚拟远程处理器状态
     */
    struct virtproc_info {
        struct virtio_device *vdev;      // 关联的virtio设备
        struct virtqueue *rvq, *svq;     // 接收/发送virtqueue
        void *rbufs, *sbufs;             // 接收/发送缓冲区的内核虚拟地址
        unsigned int num_bufs;           // 收发缓冲区总数
        unsigned int buf_size;           // 单个缓冲区大小
        int last_sbuf;                   // 最后使用的发送缓冲区索引
        dma_addr_t bufs_dma;             // 缓冲区的DMA基地址
        
        struct mutex tx_lock;            // 保护svq/sbufs/sleepers的互斥锁
        struct idr endpoints;            // 本地端点ID映射表
        struct mutex endpoints_lock;     // 保护endpoints的锁
        
        wait_queue_head_t sendq;         // 发送等待队列
        atomic_t sleepers;               // 等待发送缓冲区的任务计数
        struct rpmsg_endpoint *ns_ept;   // 名称服务端点
    };
    

probe函数

/**
 * rpmsg_probe - virtio RPMSG 设备探测函数
 * @vdev: 关联的virtio设备
 *
 * 这是virtio RPMSG驱动的主要初始化函数,负责:
 * 1. 初始化virtio RPMSG设备结构
 * 2. 查找并设置virtqueue
 * 3. 分配和初始化通信缓冲区
 * 4. 设置接收缓冲区
 * 5. 初始化名称服务端点
 * 6. 启动设备
 *
 * 返回: 0 成功,负值表示错误
 */
static int rpmsg_probe(struct virtio_device *vdev)
{
    // 定义virtqueue回调函数数组
    vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };
    // 定义virtqueue名称数组
    static const char * const names[] = { "input", "output" };
    struct virtqueue *vqs[2];          // virtqueue指针数组
    struct virtproc_info *vrp;         // RPMSG处理信息结构
    void *bufs_va;                     // 缓冲区的虚拟地址
    int err = 0, i;                    // 错误码和循环变量
    size_t total_buf_space;            // 总缓冲区大小
    bool notify;                       // 是否需要通知的标志

    /* 1. 分配并初始化virtproc_info结构 */
    vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
    if (!vrp)
        return -ENOMEM;

    vrp->vdev = vdev;  // 保存virtio设备指针

    // 初始化端点ID管理
    idr_init(&vrp->endpoints);
    // 初始化端点锁
    mutex_init(&vrp->endpoints_lock);
    // 初始化传输锁
    mutex_init(&vrp->tx_lock);
    // 初始化发送等待队列
    init_waitqueue_head(&vrp->sendq);

    /* 2. 查找并设置virtqueue */
    // 查找两个virtqueue(rx和tx)
    err = virtio_find_vqs(vdev, 2, vqs, vq_cbs, names, NULL);
    if (err)
        goto free_vrp;

    vrp->rvq = vqs[0];  // 接收virtqueue
    vrp->svq = vqs[1];  // 发送virtqueue

    // 验证rx和tx的vring大小是否相同
    WARN_ON(virtqueue_get_vring_size(vrp->rvq) !=
            virtqueue_get_vring_size(vrp->svq));

    /* 3. 确定缓冲区数量 */
    // 如果vring较小,则减少缓冲区数量
    if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2)
        vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2;
    else
        vrp->num_bufs = MAX_RPMSG_NUM_BUFS;

    // 设置每个缓冲区的大小
    vrp->buf_size = MAX_RPMSG_BUF_SIZE;
    // 计算总缓冲区空间
    total_buf_space = vrp->num_bufs * vrp->buf_size;

    /* 4. 分配DMA一致性内存用于缓冲区 */
    bufs_va = dma_alloc_coherent(vdev->dev.parent,
                                 total_buf_space, &vrp->bufs_dma,
                                 GFP_KERNEL);
    if (!bufs_va) {
        err = -ENOMEM;
        goto vqs_del;
    }

    dev_dbg(&vdev->dev, "buffers: va %pK, dma %pad\n",
            bufs_va, &vrp->bufs_dma);

    /* 5. 划分接收和发送缓冲区 */
    // 一半用于接收
    vrp->rbufs = bufs_va;
    // 另一半用于发送
    vrp->sbufs = bufs_va + total_buf_space / 2;

    /* 6. 设置接收缓冲区 */
    for (i = 0; i < vrp->num_bufs / 2; i++) {
        struct scatterlist sg;
        void *cpu_addr = vrp->rbufs + i * vrp->buf_size;

        // 初始化scatterlist
        rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);

        // 将接收缓冲区添加到virtqueue
        err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
                                  GFP_KERNEL);
        WARN_ON(err); // 正常情况下不应发生错误
    }

    /* 7. 禁用发送完成中断 */
    virtqueue_disable_cb(vrp->svq);

    // 将vrp保存到virtio设备私有数据
    vdev->priv = vrp;

    /* 8. 如果支持名称服务,初始化名称服务端点 */
    if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) {
        vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb,
                                        vrp, RPMSG_NS_ADDR);
        if (!vrp->ns_ept) {
            dev_err(&vdev->dev, "failed to create the ns ept\n");
            err = -ENOMEM;
            goto free_coherent;
        }
    }

    /* 9. 准备启动设备 */
    // 检查是否需要通知
    notify = virtqueue_kick_prepare(vrp->rvq);

    // 标记设备就绪
    virtio_device_ready(vdev);

    /* 10. 通知远程处理器可以开始发送消息 */
    if (notify)
        virtqueue_notify(vrp->rvq);

    dev_info(&vdev->dev, "rpmsg host is online\n");

    return 0;

/* 错误处理路径 */
free_coherent:
    // 释放DMA一致性内存
    dma_free_coherent(vdev->dev.parent, total_buf_space,
                      bufs_va, vrp->bufs_dma);
vqs_del:
    // 删除virtqueue
    vdev->config->del_vqs(vrp->vdev);
free_vrp:
    // 释放virtproc_info结构
    kfree(vrp);
    return err;
}
static struct virtio_device_id id_table[] = {
	{ VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID },
	{ 0 },
};

static unsigned int features[] = {
	VIRTIO_RPMSG_F_NS,
};

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,
	.probe		= rpmsg_probe,
	.remove		= rpmsg_remove,
};

  1. 收发函数 rpmsg_recv_done, rpmsg_xmit_done
  2. 512个缓冲区 MAX_RPMSG_NUM_BUFS,每一个大小512字节MAX_RPMSG_BUF_SIZE
  3. 初始化scatterlist ,按照页操作的可以看出
  4. VIRTIO_RPMSG_F_NS 是执行的,注册了rpmsg_core.c提供的rpmsg_register_device
    1. 这里是一个回调函数实现的,当m0核发送了端点信息后,Linux会注册dev设备,供上层去dri匹配(m没有发送就不会注册设备,也就不会有rpmsg_ctrl0
  5. 注册 virtio 的dri
    1. 根据名称创建两个virtio_queue,调用device提供的find_vqs回调(find_vqs是在物理层注册的virtio dev设备)
    2. 根据当前buf数量,配置到vring中,这里配置的一般是mailbox使用的共享内存
  6. 该文件另外最大的功能,是提供device以及ept端点的回调给上层

传输层

kernel/drivers/rpmsg/rpmsg_core.c 则是创建 RPMsg Bus,并提供传输层(Transport Layer)接口

源码

probe函数

/*
 * 当 rpmsg 驱动与通道(channel)绑定(probe)时,自动为其创建端点(endpoint),
 * 并将其接收回调(rx callback)绑定到一个唯一的本地 rpmsg 地址。
 *
 * 如果需要,还会向远程处理器(remote processor)宣告该通道的存在
 *(适用于驱动提供 rpmsg 服务的情况)。
 */
static int rpmsg_dev_probe(struct device *dev)
{
    // 1. 获取 rpmsg 设备结构和驱动结构
    struct rpmsg_device *rpdev = to_rpmsg_device(dev);
    struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
    struct rpmsg_channel_info chinfo = {}; // 初始化通道信息
    struct rpmsg_endpoint *ept = NULL;     // 端点初始化为 NULL
    int err;                               // 错误码

    // 2. 绑定设备的电源管理域(Power Management Domain)
    err = dev_pm_domain_attach(dev, true);
    if (err)
        goto out; // 失败则跳转到 out 标签

    // 3. 检查驱动是否定义了回调函数(必须有回调才能接收消息)
    if (rpdrv->callback) {
        // 填充通道信息:名称、源地址(src)、目标地址(dst=ANY表示自动分配)
        strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
        chinfo.src = rpdev->src;
        chinfo.dst = RPMSG_ADDR_ANY; // 由内核动态分配端点地址

        // 4. 创建端点(endpoint),绑定驱动的回调函数
        ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
        if (!ept) {
            dev_err(dev, "failed to create endpoint\n");
            err = -ENOMEM;
            goto out; // 创建失败则跳转到 out
        }

        // 5. 将端点关联到 rpmsg 设备,并更新源地址为端点分配的地址
        rpdev->ept = ept;
        rpdev->src = ept->addr; // 动态分配的地址更新到设备

        // 6. 如果驱动支持信号(signals),注册信号回调
        if (rpdrv->signals)
            ept->sig_cb = rpdrv->signals;
    }

    // 7. 调用驱动的 probe 函数(驱动自定义的初始化逻辑)
    err = rpdrv->probe(rpdev);
    if (err) {
        dev_err(dev, "%s: failed: %d\n", __func__, err);
        goto destroy_ept; // probe 失败则销毁端点
    }

    // 8. 如果端点创建成功且设备支持 announce_create,向远程处理器宣告通道
    if (ept && rpdev->ops->announce_create) {
        err = rpdev->ops->announce_create(rpdev);
        if (err) {
            dev_err(dev, "failed to announce creation\n");
            goto remove_rpdev; // 宣告失败则回滚驱动 probe
        }
    }

    // 9. 成功返回
    return 0;

// 错误处理标签(按执行顺序反向排列)
remove_rpdev:
    // 10. 如果宣告失败,调用驱动的 remove 函数回滚
    if (rpdrv->remove)
        rpdrv->remove(rpdev);
destroy_ept:
    // 11. 如果 probe 失败,销毁已创建的端点
    if (ept)
        rpmsg_destroy_ept(ept);
out:
    // 12. 返回错误码
    return err;
}
EXPORT_SYMBOL(rpmsg_unregister_device);
EXPORT_SYMBOL(__register_rpmsg_driver);
  1. 创建了一个bus总线,驱动应用层(rpmsg_ctrl.c、rpmsg_ns.c、rpmsg_char.c)向下注册rpmsg_driver,同时注册字符设备暴露接口给应用层,virtio_rpmsg_bus.c文件向下注册了virtio driver,向上则注册rpmsg device,一旦和上层驱动应用的driver匹配,上层驱动应用即可通过device提供的接口访问到device层,即virtio_rpmsg_bus.c这一层,这是rpmsg bus主要功能,同时也支持多个device和driver的接入。(相当于是一个中间层)
  2. 提供注册函数
    1. rpmsg_register_device
    2. register_rpmsg_driver
  3. 当匹配成功后,probe执行,然后执行dri下的 rpdrv->probe(rpdev)

上层驱动应用

rpmsg_char.c

源码

probe函数

/**
 * rpmsg_chrdev_probe - RPMsg字符设备探测函数
 * @rpdev: 关联的RPMsg设备
 *
 * 当RPMsg总线发现匹配的设备时调用,负责:
 * 1. 创建设备节点
 * 2. 初始化字符设备
 * 3. 注册到系统
 * 返回:0成功,负值表示错误
 */
static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
{
    struct rpmsg_ctrldev *ctrldev;
    struct device *dev;
    int ret;

    /* 1. 分配控制设备结构体内存 */
    ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL);
    if (!ctrldev)
        return -ENOMEM;  // 内存不足错误

    /* 关联RPMsg设备 */
    ctrldev->rpdev = rpdev;

    /* 2. 初始化设备结构 */
    dev = &ctrldev->dev;
    device_initialize(dev);  // 初始化device结构
    dev->parent = &rpdev->dev;  // 设置父设备
    dev->class = rpmsg_class;   // 指定设备类

    /* 3. 初始化字符设备 */
    cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops);  // 关联文件操作集
    ctrldev->cdev.owner = THIS_MODULE;  // 设置模块所有者

    /* 4. 分配次设备号 */
    ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
    if (ret < 0)
        goto free_ctrldev;
    dev->devt = MKDEV(MAJOR(rpmsg_major), ret);  // 组合主次设备号

    /* 5. 分配控制ID */
    ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL);
    if (ret < 0)
        goto free_minor_ida;
    dev->id = ret;
    dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret);  // 设置设备名称

    /* 6. 注册字符设备到系统 */
    ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev);
    if (ret)
        goto free_ctrl_ida;

    /* 7. 设置释放回调 */
    dev->release = rpmsg_ctrldev_release_device;  // 设备注销时的清理函数

    /* 8. 保存私有数据 */
    dev_set_drvdata(&rpdev->dev, ctrldev);

    return ret;

/* 错误处理路径(逆向释放资源) */
free_ctrl_ida:
    ida_simple_remove(&rpmsg_ctrl_ida, dev->id);  // 释放控制ID
free_minor_ida:
    ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));  // 释放次设备号
free_ctrldev:
    put_device(dev);  // 减少设备引用计数
    kfree(ctrldev);   // 释放设备内存

    return ret;
}
  1. 创建rpmsg_ctrl 设备文件
  2. 向下注册了driver

rpmsg_ctrl 操作集

/**
 * rpmsg_ctrldev_open - 打开控制设备文件
 * @inode: 设备文件的inode结构
 * @filp: 文件结构指针
 * 
 * 功能:
 * 1. 获取关联的rpmsg控制设备
 * 2. 增加设备引用计数
 * 3. 存储设备指针到文件私有数据
 * 返回:始终返回0(成功)
 */
static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp)
{
    /* 通过inode中的cdev指针获取控制设备结构 */
    struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);

    /* 增加设备引用计数,防止设备在使用期间被注销 */
    get_device(&ctrldev->dev);
    
    /* 将设备指针存入文件私有数据,供后续操作使用 */
    filp->private_data = ctrldev;

    return 0;
}

/**
 * rpmsg_ctrldev_release - 关闭设备文件
 * @inode: 设备文件的inode结构
 * @filp: 文件结构指针
 *
 * 功能:
 * 1. 减少设备引用计数
 * 2. 当引用计数为0时触发设备的release回调
 */
static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp)
{
    /* 获取文件关联的控制设备 */
    struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);

    /* 减少设备引用计数,可能触发设备释放 */
    put_device(&ctrldev->dev);

    return 0;
}

/**
 * rpmsg_ctrldev_ioctl - 设备控制接口
 * @fp: 文件结构指针
 * @cmd: ioctl命令字
 * @arg: 用户空间参数指针
 *
 * 支持的命令:
 * - RPMSG_CREATE_EPT_IOCTL:创建RPMsg端点
 *
 * 返回:成功返回0,失败返回错误码
 */
static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd,
                unsigned long arg)
{
    /* 从文件私有数据获取控制设备 */
    struct rpmsg_ctrldev *ctrldev = fp->private_data;
    /* 用户空间参数指针(需要做安全拷贝) */
    void __user *argp = (void __user *)arg;
    /* 端点信息结构(用户空间)*/
    struct rpmsg_endpoint_info eptinfo;
    /* 通道信息结构(内核空间)*/
    struct rpmsg_channel_info chinfo;

    /* 唯一支持的ioctl命令 */
    if (cmd != RPMSG_CREATE_EPT_IOCTL)
        return -EINVAL;  // 非法命令

    /* 从用户空间安全拷贝端点信息 */
    if (copy_from_user(&eptinfo, argp, sizeof(eptinfo)))
        return -EFAULT;  // 拷贝失败

    /* 准备通道信息 */
    memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE);  // 拷贝端点名
    chinfo.name[RPMSG_NAME_SIZE-1] = '\0';  // 确保字符串终止
    chinfo.src = eptinfo.src;  // 源地址 需要大于1024,Linux端
    chinfo.dst = eptinfo.dst;  // 目标地址,与m核对应

    /* 创建实际的端点设备 */
    return rpmsg_eptdev_create(ctrldev, chinfo);
}

/**
 * rpmsg_eptdev_create - 创建并注册RPMSG端点设备
 * @ctrldev: 所属的控制设备
 * @chinfo: 通道信息(名称/src/dst)
 *
 * 功能流程:
 * 1. 分配和初始化端点设备结构
 * 2. 设置同步机制(互斥锁/自旋锁)
 * 3. 初始化skb接收队列
 * 4. 注册字符设备到内核
 * 返回:0成功,负错误码表示失败
 */
static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,
                   struct rpmsg_channel_info chinfo)
{
    struct rpmsg_device *rpdev = ctrldev->rpdev;  // 获取底层RPMSG设备
    struct rpmsg_eptdev *eptdev;  // 端点设备结构
    struct device *dev;           // 基础设备结构
    int ret;

    /* 1. 内存分配 */
    eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
    if (!eptdev)
        return -ENOMEM;  // 内核内存不足

    /* 2. 基础结构初始化 */
    dev = &eptdev->dev;
    eptdev->rpdev = rpdev;  // 关联RPMSG设备
    eptdev->chinfo = chinfo; // 保存通道信息

    /* 3. 同步机制初始化 */
    mutex_init(&eptdev->ept_lock);    // 初始化互斥锁(保护端点操作)
    spin_lock_init(&eptdev->queue_lock); // 初始化自旋锁(保护队列)
    skb_queue_head_init(&eptdev->queue); // 初始化skb接收队列
    init_waitqueue_head(&eptdev->readq); // 初始化读等待队列

    /* 4. 设备模型设置 */
    device_initialize(dev);  // 初始化设备结构
    dev->class = rpmsg_class;    // 指定设备类
    dev->parent = &ctrldev->dev; // 设置父设备
    dev->groups = rpmsg_eptdev_groups; // 属性文件组
    dev_set_drvdata(dev, eptdev); // 存储私有数据

    /* 5. 字符设备设置 */
    cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops); // 关联文件操作集
    eptdev->cdev.owner = THIS_MODULE;  // 设置模块所有者

    /* 6. 设备号分配 */
    ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
    if (ret < 0)
        goto free_eptdev;
    dev->devt = MKDEV(MAJOR(rpmsg_major), ret); // 组合主次设备号

    /* 7. 端点ID分配 */
    ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL);
    if (ret < 0)
        goto free_minor_ida;
    dev->id = ret;
    dev_set_name(dev, "rpmsg%d", ret);  // 设置设备名称格式

    /* 8. 设备注册 */
    ret = cdev_device_add(&eptdev->cdev, &eptdev->dev);
    if (ret)
        goto free_ept_ida;

    /* 9. 设置释放回调 */
    dev->release = rpmsg_eptdev_release_device;  // 注销时的清理函数

    return ret;  // 返回状态

/* 错误处理路径(按资源申请逆序释放)*/
free_ept_ida:
    ida_simple_remove(&rpmsg_ept_ida, dev->id);  // 释放端点ID
free_minor_ida:
    ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); // 释放次设备号
free_eptdev:
    put_device(dev);  // 减少设备引用
    kfree(eptdev);    // 释放内存

    return ret;
}
  1. 应用层传入2个地址以及名称,驱动去创建节点 rpmsg_eptdev_create

rpmsg 操作集

static const struct file_operations rpmsg_eptdev_fops = {
	.owner = THIS_MODULE,
	.open = rpmsg_eptdev_open,
	.release = rpmsg_eptdev_release,
	.read_iter = rpmsg_eptdev_read_iter,
	.write_iter = rpmsg_eptdev_write_iter,
	.poll = rpmsg_eptdev_poll,
	.unlocked_ioctl = rpmsg_eptdev_ioctl,
	.compat_ioctl = compat_ptr_ioctl,
};
写:
rpmsg_eptdev_write_iter
rpmsg_sendto:ept->ops->sendto(ept, data, len, dst);//调用端点绑定的回调
端点ept的来源
rpmsg_eptdev_open
{
    ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
    eptdev->ept = ept;
}
而在rpmsg_create_ept: rpdev->ops->create_ept(rpdev, cb, priv, chinfo)(drivers/rpmsg/rpmsg_core.c)调用的是device的ops,该device之前以分析在kernel/drivers/rpmsg/virtio_rpmsg_bus.c生成
static struct rpmsg_device *rpmsg_virtio_add_ctrl_dev(struct virtio_device *vdev)
{
struct rpmsg_device *rpdev_ctrl;
    rpdev_ctrl->ops = &virtio_rpmsg_ops;
}
static const struct rpmsg_device_ops virtio_rpmsg_ops = {
    .create_channel = virtio_rpmsg_create_channel,
    .release_channel = virtio_rpmsg_release_channel,
    .create_ept = virtio_rpmsg_create_ept,
    .announce_create = virtio_rpmsg_announce_create,
    .announce_destroy = virtio_rpmsg_announce_destroy,
};
virtio_rpmsg_create_ept
static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
                         struct rpmsg_device *rpdev,
                         rpmsg_rx_cb_t cb,
                         void *priv, u32 addr)
{
    int id_min, id_max, id;
    struct rpmsg_endpoint *ept;
ept->ops = &virtio_endpoint_ops;
...
return ept;
}
static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
    .destroy_ept = virtio_rpmsg_destroy_ept,
    .send = virtio_rpmsg_send,
    .sendto = virtio_rpmsg_sendto,
    .send_offchannel = virtio_rpmsg_send_offchannel,
    .trysend = virtio_rpmsg_trysend,
    .trysendto = virtio_rpmsg_trysendto,
    .trysend_offchannel = virtio_rpmsg_trysend_offchannel,
    .get_mtu = virtio_rpmsg_get_mtu,
};
回到最开始的写函数
rpmsg_sendto:ept->ops->sendto(ept, data, len, dst);
即此时写调用的是drivers/rpmsg/virtio_rpmsg_bus.c下的virtio_rpmsg_sendto
其他send的函数同理
 
读
rpmsg_eptdev_read_iter
{
	skb = skb_dequeue(&eptdev->queue);
}
static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len,
            void *priv, u32 addr)
{
skb_queue_tail(&eptdev->queue, skb);
}
ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
根据上述写的分析,这个rpmsg_create_ept最终调用drivers/rpmsg/virtio_rpmsg_bus.c下的
static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
                         struct rpmsg_device *rpdev,
                         rpmsg_rx_cb_t cb,
                         void *priv, u32 addr)
{
ept->cb = cb;
}
该回调被绑定到端点上,等待其他地方调用。
该cb回调由以下流程调用
rpmsg_recv_done(注册到下层virtio,由下层调用,后面章节分析)
rpmsg_recv_single
{
ept->cb(ept->rpdev, msg->data, msg_len, ept->priv, __rpmsg32_to_cpu(little_endian, msg->src));
}
  1. 上层调用ioctrl创建ept时,会新创建一个rpmsgx设备
写流程
  1. rpmsg_eptdev_write_iter->rpmsg_trysend | rpmsg_send(非阻塞和阻塞)

  2. rpmsg_send-> ept->ops->send(ept, data, len)

    1. 端点ept的来源
      rpmsg_eptdev_open
      {
      ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
      eptdev->ept = ept;
      }

    2. ops的来源

      1. 在bus也就是mac层,ns名称服务的时候创建的dev

      2. __rpmsg_create_ept -》ept->ops = &virtio_endpoint_ops

        static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
        	.destroy_ept = virtio_rpmsg_destroy_ept,
        	.send = virtio_rpmsg_send,
        	.sendto = virtio_rpmsg_sendto,
        	.send_offchannel = virtio_rpmsg_send_offchannel,
        	.trysend = virtio_rpmsg_trysend,
        	.trysendto = virtio_rpmsg_trysendto,
        	.trysend_offchannel = virtio_rpmsg_trysend_offchannel,
        	.get_mtu = virtio_rpmsg_get_mtu,
        };
        
  3. virtio_rpmsg_send-》rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true)

    1. 将buffer添加vring中
      virtqueue_add_outbuf(vrp->svq, &sg, 1, msg, GFP_KERNEL);

    2. 通知下层进行数据发送
      virtqueue_kick(vrp->svq);

    3. 通知到remoteproc层,而remoteproc层则调用下层mailbox硬件接口进行远端通知

    4. virtqueue_notify 调用vq->notify回调,该回调在初始化struct virtqueue时传入
      rproc_virtio_notify(rpmsg_probe->virtio_find_vqs->(vdev->config->find_vqs) rproc_virtio_find_vqs->rp_find_vq
      ->vring_new_virtqueue->传入rproc_virtio_notify),该函数最终调用struct rproc *rproc的ops下kick回调。
      hobot_vdsp_rproc_kick(hobot_remoteproc_probe->rproc_alloc传入hobot_vdsp_rproc_ops->rproc_alloc_ops->rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);)
      trigger_interrupt:vdsp_trigger_interrupt
      
  4. mbox_send_message 下层mailbox硬件

    1. msg_submit:chan->mbox->ops->send_data
读流程
  1. get_mbox_dev_resource 注册中断服务hb_mbox_softirq
  2. mbox_chan_received_data 回调client 的rx_callback(rk_rpmsg_rx_callback)
  3. rpmsg_recv_done-》rpmsg_recv_single -》ept->cb,
  4. ept->cb
    1. rpmsg_ns_cb(就是ns名称服务时候的,通道rpmesg0完成后就不执行了)该回调在端点初始化时传入
    2. rpmsg_ept_cb(rpmsg_eptdev_open->rpmsg_create_ept时传入了该回调),后续读进入的是这个函数
  5. 最终将buf放入队列skb_queue_tail(&eptdev->queue, skb);
  6. 上层调用read时,rpmsg_eptdev_read_iter被调用
    1. skb = skb_dequeue(&eptdev->queue);//冲skb队列取数据

总结

  1. RPMsg是利用通道进行数据通信,先用 /dev/rpmsg_ctrl0 设备通过ioctrl生成端点,然后使用生成的端点进行通讯

  2. 名称服务,VIRTIO_RPMSG_F_NS 宏,不开启就不使用名称服务,使用就需要m核心先发送信息才可以注册设备
    在这里插入图片描述

    1. m核心

      在这里插入图片描述

    2. Linux

      1. 在这里插入图片描述
  3. RPMsg内核驱动框架涉及到RPMsg框架、virtio框架、remoteproc子系统和mailbox子系统

  4. mailbox,驱动开发者可自我注册client生成设备提供APP层直接进行核间通信

  5. 分析

    1. 物理层,创建virtio dev设备(物理和mac层中间有一个 remoteproc 这2层的桥梁 ,dev设备注册由virtio与remoteproc的适配层实现drivers/remoteproc/remoteproc_virtio.c)
    2. mac层,创建virtio dri匹配物理层的dev,然后去创建rpmsg dev设备
    3. 传输层core,注册rpmsg bus总线,提供dev和dri函数,mac注册dev,应用层注册dri,匹配后执行probe
    4. 应用层,注册rpmsg dri

参考博文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dz小伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值