Rpmsg与Virtio介绍

目录

  • Rpmsg与Virtio介绍
  • 一、Rpmsg的介绍
    • 1、rpmsg_core.c的详细介绍
      • 1.1 rpmsg_bus结构体
      • 1.2 rpmsg_dev_match()函数
      • 1.3 rpmsg_dev_probe()函数
      • 1.4 rpmsg_register_device () 函数的介绍
      • 1.5 __register_rpmsg_driver() 函数的介绍
      • 1.6 rpmsg_create_ept、rpmsg_send、rpmsg_trysend、rpmsg_poll
    • 2、rpmsg_char.c的详细介绍
      • 2.1 rpmsg_chrdev_driver结构体
      • 2.2 rpmsg_chrdev_probe()函数
      • 2.3 rpmsg_chrdev_remove()函数
  • 二、virtio_rpmsg_bus.c 的详细介绍
    • 1、virtio_ipc_driver 结构体的介绍
    • 2、rpmsg_probe()函数的介绍
    • 3、__rpmsg_create_ept()介绍
    • 4、 virtio_endpoint_ops结构体介绍
    • 5、rpmsg_ns_cb() 函数的介绍
    • 6、rpmsg_create_channel() 函数的介绍
    • 7、virtio_rpmsg_ops() 结构体的介绍
  • 三、Virtio的介绍
    • 1、virtio.c 的介绍
      • 1.1 virtio_bus结构体的介绍
      • 1.2 register_virtio_driver() 函数的介绍
      • 1.3 register_virtio_device() 函数的介绍
      • 1.4 virtio_dev_probe 函数的介绍
      • 1.5 virtio_dev_match 函数的介绍
    • 2、virtio_input.c的介绍
      • 2.1 virtio_input_driver结构体介绍
  • 四、virtio_device、virtio_driver、virtio_bus、rpmsg_device、rpmsg_drivver、rpmsg_bus如何如何联系且运作起来
    • 1、联系的建立
    • 2、运作

一、Rpmsg的介绍

  • 源码所在路径:drivers\rpmsg\

    KconfigMakefileqcom_glink_native.cqcom_glink_native.hqcom_glink_rpm.cqcom_glink_smem.cqcom_smd.crpmsg_char.crpmsg_core.crpmsg_internal.hvirtio_rpmsg_bus.c
    

结合官方所提供的rpmsg framwork框架图,我们可知:

  1. Rpmsg的整体框架是Rpmsg BusRpmsg Device与Rpmsg Driver所构成,即Linux中的Bus模型;

  2. Rpmsg Bus:由rpmsg_core.c文件所构建,负责

    1. bus的构建;
    2. Driver-device的match;
    3. device的probe与remove;
    4. uevent机制;
    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,
    };
    
  3. Rpmsg Driver:由rpmsg_char.c所register(上图中还有两个文件,不在该章节进行介绍),负责:

    1. register char driver;
    2. Remove char driver;
    3. 该driver name 为 rpmsg_chrdev;
    static struct rpmsg_driver rpmsg_chrdev_driver = {.probe = rpmsg_chrdev_probe,.remove = rpmsg_chrdev_remove,.drv = {.name = "rpmsg_chrdev",},
    };
    
  4. Rpmsg Device:该层一开始是只有Virtio框架所构成的,后面添加了Glink与SMD架构(主要为高通所用),故主要介绍Virtio框架,通过上图可知其主要由virtio_rpmsg_bus.c 文件所维护

    1. 文件比较特殊,其是Virtio BUS与Rpmsg Bus的连接层,该文件中定义了virtio driver;
    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,
    };
    
  5. virtio driver会向Rpmsg Bus register rpmsg device,这样一来Rpmsg Bus与Virtio Bus就通过该rpmsg device联系起来了。

1、rpmsg_core.c的详细介绍

1.1 rpmsg_bus结构体

结构体定义如下,可以看到该bus的名为rpmsg

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_init()中调用bus_register函数

  1. 完成bus_type_private的初始化、创建并注册的这条总线需要的目录,该目录为rpmsg;
  2. rpmsg目录下创建/device /driver 目录
  3. 初始化这条总线上的设备链表:struct klist klist_devices;
  4. 初始化这条总线上的驱动链表:struct klist klist_drivers;
static int __init rpmsg_init(void)
{int ret;ret = bus_register(&rpmsg_bus);if (ret)pr_err("failed to register rpmsg bus: %d\n", ret);return ret;
}

而该rpmsg_init函数会在注册进postcore_initcall,这样当kernel起来时就会调用根据initcall的顺序调用到rpmsg_init,进而注册Rpmsg Bus,为其准备所需要的资源

postcore_initcall(rpmsg_init);

1.2 rpmsg_dev_match()函数

问:在Bus中,如何调入到Rpmsg Bus的match函数呢

答:当有driver/device register是会触发Bus中的bus_add_driver/bus_add_device继而触发Bus–>match/probe函数

在该match函数中,匹配顺序如下:

static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
{struct rpmsg_device *rpdev = to_rpmsg_device(dev);struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);const struct rpmsg_device_id *ids = rpdrv->id_table;unsigned int i;/* 针对特殊情况,dev中的driver_override被设置,则只匹配和driver_override名字相同的驱动程序 */if (rpdev->driver_override)return !strcmp(rpdev->driver_override, drv->name);/* 后根据dev->id.mane与driver id_table中每个name进行匹配 */if (ids)for (i = 0; ids[i].name[0]; i++)if (rpmsg_id_match(rpdev, &ids[i]))return 1;/* 最后以设备树进行匹配 */return of_driver_match_device(dev, drv);
}

1.3 rpmsg_dev_probe()函数

Rpmsg Bus->match()成功后,则Bus会调用Rpmsg Bus->probe对driver与device进行bind

/** when an rpmsg driver is probed with a channel, we seamlessly create* it an endpoint, binding its rx callback to a unique local rpmsg* address.** if we need to, we also announce about this channel to the remote* processor (needed in case the driver is exposing an rpmsg service).*/
static int rpmsg_dev_probe(struct device *dev)
{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;int err;/* 电源管理相关 */err = dev_pm_domain_attach(dev, true);if (err)goto out;/* driver->callback存在会进行调用,但是rpmsg_char.c中的driver->callback == NULL* 此处可以客制化自己的driver*/if (rpdrv->callback) {/* 以device->id.name作为rpsmg_channel->name* channel的src为device->src*/strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);chinfo.src = rpdev->src;chinfo.dst = RPMSG_ADDR_ANY;/* 会以回调的形式调用device->ops->create_ept,该ept->cb(rx_cb) =rpdrv->callback */ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);if (!ept) {dev_err(dev, "failed to create endpoint\n");err = -ENOMEM;goto out;}/* 把创建的ept和addr存储到device中 */rpdev->ept = ept;rpdev->src = ept->addr;}/* 调用driver->probe ==   rpmsg_chrdev_probe */err = rpdrv->probe(rpdev);if (err) {dev_err(dev, "%s: failed: %d\n", __func__, err);if (ept)rpmsg_destroy_ept(ept);goto out;}/* 调用device->ops->announce_create == virtio_rpmsg_announce_create */if (ept && rpdev->ops->announce_create)err = rpdev->ops->announce_create(rpdev);
out:return err;
}

1.4 rpmsg_register_device () 函数的介绍

int rpmsg_register_device(struct rpmsg_device *rpdev)
{struct device *dev = &rpdev->dev;int ret;dev_set_name(&rpdev->dev, "%s.%s.%d.%d", dev_name(dev->parent),rpdev->id.name, rpdev->src, rpdev->dst);rpdev->dev.bus = &rpmsg_bus;ret = device_register(&rpdev->dev);if (ret) {dev_err(dev, "device_register failed: %d\n", ret);put_device(&rpdev->dev);}return ret;
}
EXPORT_SYMBOL(rpmsg_register_device);

1.5 __register_rpmsg_driver() 函数的介绍

int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
{rpdrv->drv.bus = &rpmsg_bus;rpdrv->drv.owner = owner;return driver_register(&rpdrv->drv);
}
EXPORT_SYMBOL(__register_rpmsg_driver);C

1.6 rpmsg_create_ept、rpmsg_send、rpmsg_trysend、rpmsg_poll

从函数实体可以看到,这些operation都是以回调的方式调用,所以进行客制化的实现;

struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,rpmsg_rx_cb_t cb, void *priv,struct rpmsg_channel_info chinfo)
{if (WARN_ON(!rpdev))return NULL;return rpdev->ops->create_ept(rpdev, cb, priv, chinfo);
}
EXPORT_SYMBOL(rpmsg_create_ept);/*** rpmsg_destroy_ept() - destroy an existing rpmsg endpoint* @ept: endpoing to destroy** Should be used by drivers to destroy an rpmsg endpoint previously* created with rpmsg_create_ept(). As with other types of "free" NULL* is a valid parameter.*/
void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
{if (ept)ept->ops->destroy_ept(ept);
}
EXPORT_SYMBOL(rpmsg_destroy_ept);/*** rpmsg_send() - send a message across to the remote processor* @ept: the rpmsg endpoint* @data: payload of message* @len: length of payload** This function sends @data of length @len on the @ept endpoint.* The message will be sent to the remote processor which the @ept* endpoint belongs to, using @ept's address and its associated rpmsg* device destination addresses.* In case there are no TX buffers available, the function will block until* one becomes available, or a timeout of 15 seconds elapses. When the latter* happens, -ERESTARTSYS is returned.** Can only be called from process context (for now).** Returns 0 on success and an appropriate error value on failure.*/
int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
{if (WARN_ON(!ept))return -EINVAL;if (!ept->ops->send)return -ENXIO;return ept->ops->send(ept, data, len);
}
EXPORT_SYMBOL(rpmsg_send);/*** rpmsg_sendto() - send a message across to the remote processor, specify dst* @ept: the rpmsg endpoint* @data: payload of message* @len: length of payload* @dst: destination address** This function sends @data of length @len to the remote @dst address.* The message will be sent to the remote processor which the @ept* endpoint belongs to, using @ept's address as source.* In case there are no TX buffers available, the function will block until* one becomes available, or a timeout of 15 seconds elapses. When the latter* happens, -ERESTARTSYS is returned.** Can only be called from process context (for now).** Returns 0 on success and an appropriate error value on failure.*/
int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
{if (WARN_ON(!ept))return -EINVAL;if (!ept->ops->sendto)return -ENXIO;return ept->ops->sendto(ept, data, len, dst);
}
EXPORT_SYMBOL(rpmsg_sendto);/*** rpmsg_send_offchannel() - send a message using explicit src/dst addresses* @ept: the rpmsg endpoint* @src: source address* @dst: destination address* @data: payload of message* @len: length of payload** This function sends @data of length @len to the remote @dst address,* and uses @src as the source address.* The message will be sent to the remote processor which the @ept* endpoint belongs to.* In case there are no TX buffers available, the function will block until* one becomes available, or a timeout of 15 seconds elapses. When the latter* happens, -ERESTARTSYS is returned.** Can only be called from process context (for now).** Returns 0 on success and an appropriate error value on failure.*/
int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,void *data, int len)
{if (WARN_ON(!ept))return -EINVAL;if (!ept->ops->send_offchannel)return -ENXIO;return ept->ops->send_offchannel(ept, src, dst, data, len);
}
EXPORT_SYMBOL(rpmsg_send_offchannel);/*** rpmsg_send() - send a message across to the remote processor* @ept: the rpmsg endpoint* @data: payload of message* @len: length of payload** This function sends @data of length @len on the @ept endpoint.* The message will be sent to the remote processor which the @ept* endpoint belongs to, using @ept's address as source and its associated* rpdev's address as destination.* In case there are no TX buffers available, the function will immediately* return -ENOMEM without waiting until one becomes available.** Can only be called from process context (for now).** Returns 0 on success and an appropriate error value on failure.*/
int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)
{if (WARN_ON(!ept))return -EINVAL;if (!ept->ops->trysend)return -ENXIO;return ept->ops->trysend(ept, data, len);
}
EXPORT_SYMBOL(rpmsg_trysend);/*** rpmsg_sendto() - send a message across to the remote processor, specify dst* @ept: the rpmsg endpoint* @data: payload of message* @len: length of payload* @dst: destination address** This function sends @data of length @len to the remote @dst address.* The message will be sent to the remote processor which the @ept* endpoint belongs to, using @ept's address as source.* In case there are no TX buffers available, the function will immediately* return -ENOMEM without waiting until one becomes available.** Can only be called from process context (for now).** Returns 0 on success and an appropriate error value on failure.*/
int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
{if (WARN_ON(!ept))return -EINVAL;if (!ept->ops->trysendto)return -ENXIO;return ept->ops->trysendto(ept, data, len, dst);
}
EXPORT_SYMBOL(rpmsg_trysendto);/*** rpmsg_poll() - poll the endpoint's send buffers* @ept:    the rpmsg endpoint* @filp: file for poll_wait()* @wait:   poll_table for poll_wait()** Returns mask representing the current state of the endpoint's send buffers*/
__poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,poll_table *wait)
{if (WARN_ON(!ept))return 0;if (!ept->ops->poll)return 0;return ept->ops->poll(ept, filp, wait);
}
EXPORT_SYMBOL(rpmsg_poll);/*** rpmsg_send_offchannel() - send a message using explicit src/dst addresses* @ept: the rpmsg endpoint* @src: source address* @dst: destination address* @data: payload of message* @len: length of payload** This function sends @data of length @len to the remote @dst address,* and uses @src as the source address.* The message will be sent to the remote processor which the @ept* endpoint belongs to.* In case there are no TX buffers available, the function will immediately* return -ENOMEM without waiting until one becomes available.** Can only be called from process context (for now).** Returns 0 on success and an appropriate error value on failure.*/
int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,void *data, int len)
{if (WARN_ON(!ept))return -EINVAL;if (!ept->ops->trysend_offchannel)return -ENXIO;return ept->ops->trysend_offchannel(ept, src, dst, data, len);
}
EXPORT_SYMBOL(rpmsg_trysend_offchannel);

2、rpmsg_char.c的详细介绍

2.1 rpmsg_chrdev_driver结构体

定义如下:

static struct rpmsg_driver rpmsg_chrdev_driver = {.probe = rpmsg_chrdev_probe,.remove = rpmsg_chrdev_remove,.drv = {.name = "rpmsg_chrdev",},
};

从该结构体定义可以看出其是一个字符设备,在rpmsg_char_init()会register dirver

static int rpmsg_char_init(void)
{int ret;/* 系统自动分配设备号 */ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg");if (ret < 0) {pr_err("rpmsg: failed to allocate char dev region\n");return ret;}/* 创建/sys/class/rpmsg设备类 */rpmsg_class = class_create(THIS_MODULE, "rpmsg");if (IS_ERR(rpmsg_class)) {pr_err("failed to create rpmsg class\n");unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);return PTR_ERR(rpmsg_class);}/* 注册rpmsg driver,实质上是赋值操作 */ret = register_rpmsg_driver(&rpmsg_chrdev_driver);if (ret < 0) {pr_err("rpmsgchr: failed to register rpmsg driver\n");class_destroy(rpmsg_class);unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX);}return ret;
}#define register_rpmsg_driver(drv) \__register_rpmsg_driver(drv, THIS_MODULE)int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
{rpdrv->drv.bus = &rpmsg_bus;rpdrv->drv.owner = owner;return driver_register(&rpdrv->drv);
}int driver_register(struct device_driver *drv)
{int ret;struct device_driver *other;if (!drv->bus->p) {pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",drv->name, drv->bus->name);return -EINVAL;}/* 检查driver与bus的函数是否有冲突 */if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown))printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods\n", drv->name);/* driver找到bus->p->drivers_kset中的空位(先会判断这个list中是否已经存在同名的driver) */other = driver_find(drv->name, drv->bus);if (other) {printk(KERN_ERR "Error: Driver '%s' is already registered, ""aborting...\n", drv->name);return -EBUSY;}/* bus中添加driver */ret = bus_add_driver(drv);if (ret)return ret;/* 如果grop不为空的话,将在驱动文件夹下创建以group名字的子文件夹,然后在子文件夹下添加group的属性文件 */ret = driver_add_groups(drv, drv->groups);if (ret) {bus_remove_driver(drv);return ret;}kobject_uevent(&drv->p->kobj, KOBJ_ADD);return ret;
}

而rpmsg_char_init会在驱动加载时被调用

  postcore_initcall(rpmsg_char_init);

2.2 rpmsg_chrdev_probe()函数

问:该函数在何时被调用呢?

答:

  • 在bus中,若driver,会触发bus中的bus_add_driver->driver_attach->__driver_attach>driver_probe_device->really_probe->

    if (dev->bus->probe) {ret = dev->bus->probe(dev);if (ret)goto probe_failed;
    } else if (drv->probe) {ret = drv->probe(dev);if (ret)goto probe_failed;
    }
    
  • 在bus中,若device add,会触发bus中device_add->bus_add_device,if(dev->bus)bus_probe_device->device_initial_probe->__device_attach->这里有两条路if(dev->driver) device_bind_driver, (走else)**else** **bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver)**;->driver_match_device->driver_probe_device->>really_probe->

    if (dev->bus->probe) {ret = dev->bus->probe(dev);if (ret)goto probe_failed;
    } else if (drv->probe) {ret = drv->probe(dev);if (ret)goto probe_failed;
    }
    

从上面分析可以看到bus->probe优先级比driver->probe高。

问:那是不是表示rpmsg driver->probe永远也不会被调用到?

答:不会,应为rpmsg bus->probe()中会调用rpmsg driver->probe;

接下来分析rpmsg_chrdev_probe()函数中主要做的事情。

  • 前提:刚刚分析了,只有在driver match device后才会调用到rpmsg bus probe进而调用到rpmsg driver probe,故此时对应的device已经找到了。
static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
{struct rpmsg_ctrldev *ctrldev;struct device *dev;int ret;/* 初始化rpmsg  控制设备,该控制设备保存着已经被实例化的ept device*/ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL);if (!ctrldev)return -ENOMEM;ctrldev->rpdev = rpdev;dev = &ctrldev->dev;device_initialize(dev);dev->parent = &rpdev->dev;dev->class = rpmsg_class;/* 为该控制设备添加字符设备,以便后续支持ioctl */cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops);ctrldev->cdev.owner = THIS_MODULE;/* 获取ida数组中一个有效的index,并获取此设备号 */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);/* 获取ida数组中一个有效的index,并设置名字 */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);/* 填充设备号 */ret = cdev_add(&ctrldev->cdev, dev->devt, 1);if (ret)goto free_ctrl_ida;/* We can now rely on the release function for cleanup */dev->release = rpmsg_ctrldev_release_device;/* 添加字符设备,后续可以通过dev找到所对应的rpmsg_ctrldev,进而可以会找到rpmsg device */ret = device_add(dev);if (ret) {dev_err(&rpdev->dev, "device_add failed: %d\n", ret);put_device(dev);}dev_set_drvdata(&rpdev->dev, ctrldev);return ret;free_ctrl_ida:ida_simple_remove(&rpmsg_ctrl_ida, dev->id);
free_minor_ida:ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt));
free_ctrldev:put_device(dev);kfree(ctrldev);return ret;
}

2.3 rpmsg_chrdev_remove()函数

从下面的函数可以知道,当该driver remove,则改在在该driver下的所有rpmsg device会一个个被调用rpmsg_eptdev_destroy-> ept->ops->destroy_ept,可以看到每个rpmsg device维护自己的device函数。

那么这里留一个问题,什么时候rpmsg_create_ept?

static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev)
{struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev);int ret;/* Destroy all endpoints */ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy);if (ret)dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret);device_del(&ctrldev->dev);put_device(&ctrldev->dev);
}

二、virtio_rpmsg_bus.c 的详细介绍

1、virtio_ipc_driver 结构体的介绍

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,
};

从上面结构体中的变量可以看到,该virtio_dirver有属于自己的probo与remove函数,故可以猜测该dirver是属于virtio_bus的(该猜测在后续中会印证)。

该结构体会在 subsys_initcall(rpmsg_init); --> register_virtio_driver(&virtio_ipc_driver);中被注册,通过查看register_virtio_dirver函数:

  1. 该virtio_driver被设为的属于virtio_bus —>这就印证了刚刚的猜测;
  2. 通过driver_register将virtio_driver register 到 virtio_bus中;
int register_virtio_driver(struct virtio_driver *driver)
{/* Catch this early. */BUG_ON(driver->feature_table_size && !driver->feature_table);driver->driver.bus = &virtio_bus;return driver_register(&driver->driver);
}

2、rpmsg_probe()函数的介绍

注意该函数是virtio_driver中的probe函数。

源码如下图:

主要做了如下的事情:

  1. 根据vdev->config->find_vqs(),根据names作为匹配原则期望找到对应的rx与tx virtqueue;

  2. 根据找到的rx/tx,将其赋值到virtproc_info->virqueue[0/1] ;

  3. 统计rx/tx 所需要的buf size/buf num/total buf space,并为需要的buf申请DMA memory

  4. DMA memory对半分给若rx/tx;

  5. 每一个rx buf进行vring的初始化

  6. 上述virtproc_info初始化赋值给该virtio_device

  7. 支持remote process,创建一个以RPMSG_NS_ADDR的virtproc_info->ns_ept,支持后续的name service announcement;

  8. 备kick_off并告知remote process notify

在该函数中涉及到很多数据结构,统一的来说:

  1. 先根据名字,找到rx/tx virtqueue(vqs);
  2. 将rx/tx virtqueue存储到virtproc_info(vrq)->rvq/tvq;
  3. 初始化vrq(rvq vring、tvq info、ns_ept);
  4. 将vrq存储到virtio_device中;
static int rpmsg_probe(struct virtio_device *vdev)
{vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };static const char * const names[] = { "input", "output" };struct virtqueue *vqs[2];struct virtproc_info *vrp;void *bufs_va;int err = 0, i;size_t total_buf_space;bool notify;vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);if (!vrp)return -ENOMEM;vrp->vdev = vdev;idr_init(&vrp->endpoints);mutex_init(&vrp->endpoints_lock);mutex_init(&vrp->tx_lock);init_waitqueue_head(&vrp->sendq);/* 根据vdev->config->find_vqs(),根据names作为匹配原则期望找到对应的rx与tx * 其中rx与tx对应各自的virtqueue*/err = virtio_find_vqs(vdev, 2, vqs, vq_cbs, names, NULL);if (err)goto free_vrp;/* 根据找到的rx/tx,将其赋值到virtproc_info->virqueue[0/1] */vrp->rvq = vqs[0];vrp->svq = vqs[1];/* 期望rx/tx   virtqueue对应的vring size是对称的 */WARN_ON(virtqueue_get_vring_size(vrp->rvq) !=virtqueue_get_vring_size(vrp->svq));/* 如果rx vring size小于MAX_RPMSG_NUM_BUFS / 2,则后续可利用的buf num为最小size * 2* 否则以最大的MAX_RPMS_NUM_BUFS为准*/if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2)vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2;elsevrp->num_bufs = MAX_RPMSG_NUM_BUFS;vrp->buf_size = MAX_RPMSG_BUF_SIZE;total_buf_space = vrp->num_bufs * vrp->buf_size;/* 根据统计所得的buf space 申请一段连续的DMA memory */bufs_va = dma_alloc_coherent(vdev->dev.parent->parent,total_buf_space, &vrp->bufs_dma,GFP_KERNEL);if (!bufs_va) {err = -ENOMEM;goto vqs_del;}dev_dbg(&vdev->dev, "buffers: va %p, dma %pad\n",bufs_va, &vrp->bufs_dma);/* 将上述的DMA内存对半给rx与tx的buf, DMA memory的起始地址在rx  */vrp->rbufs = bufs_va;vrp->sbufs = bufs_va + total_buf_space / 2;/* 根据rx buf num对每一个buf进行初始化 */for (i = 0; i < vrp->num_bufs / 2; i++) {struct scatterlist sg;void *cpu_addr = vrp->rbufs + i * vrp->buf_size;    //获取每一个rx buf cpu addrrpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);    //根据cpu addr初始化一个散列表err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,   //根据散列表与cpu addr,初始化rx vring inbufGFP_KERNEL);WARN_ON(err); /* sanity check; this can't really happen */}/* 设置相关的tx flag,来抑制发送行为 */virtqueue_disable_cb(vrp->svq);/* 将virtproc_info赋值给该virtio_device */vdev->priv = vrp;/*  创建一个RPMSG_NS_ADDR的ept,以供支持remote process */if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) {/* a dedicated endpoint handles the name service msgs */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;}}/* 准备事件相关的kick off(中断、标志) */notify = virtqueue_kick_prepare(vrp->rvq);/* 将该virtio device设置成ready status. */virtio_device_ready(vdev);/* 告知remote device可以发消息了,notify一般为戳中断的形式 */if (notify)virtqueue_notify(vrp->rvq);dev_info(&vdev->dev, "rpmsg host is online\n");return 0;free_coherent:dma_free_coherent(vdev->dev.parent->parent, total_buf_space,bufs_va, vrp->bufs_dma);
vqs_del:vdev->config->del_vqs(vrp->vdev);
free_vrp:kfree(vrp);return err;
}

3、__rpmsg_create_ept()介绍

其主要将该ns_ept的ops = virtio_endpoint_ops,从上面的函数调用可以知道,传进去的rpdev == NULL,所以这个函数还未与rpmsg_bus建立联系。

  • 问:何时才与rpmsg _bus建立联系呢?
  • 答:(vrp->ns_ept->cb == rpmsg_ns_cb)–>rpmsg_create_channel–>rpmsg_register_device();
  • 问:何时才调用rpmsg_ns_cb?
  • 答:从之前的分析,都是对virtio_driver结构体的分析,应该在后续有virtio_device match时候,调用到virtio_bus->probe --> virtio_driver->probe -->为这个virtio_device 进行初始化操作,此时virtio_device->priv->ns_ept = ept,其中ept->rpdev == NULL,后续可使用这个virtio_device 进行name service rpmsg_create_channel->rpmsg_register_device,进行rpmsg_device的真正建立。进而virtio_device对应的virtio_driver有真正的rpdev,使得virtio_driver、virtio_device通过rpmsg_device与rpmsg_bus打交道。
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;struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev;ept = kzalloc(sizeof(*ept), GFP_KERNEL);if (!ept)return NULL;kref_init(&ept->refcount);mutex_init(&ept->cb_lock);/* 初始化ept */ept->rpdev = rpdev;ept->cb = cb;ept->priv = priv;ept->ops = &virtio_endpoint_ops;    /* 该virproc_info->ns_ept->ops = virtio_endpoint_ops  *//* do we need to allocate a local address ? */if (addr == RPMSG_ADDR_ANY) {id_min = RPMSG_RESERVED_ADDRESSES;id_max = 0;} else {id_min = addr;id_max = addr + 1;}mutex_lock(&vrp->endpoints_lock);/* bind the endpoint to an rpmsg address (and allocate one if needed) */id = idr_alloc(&vrp->endpoints, ept, id_min, id_max, GFP_KERNEL);if (id < 0) {dev_err(dev, "idr_alloc failed: %d\n", id);goto free_ept;}ept->addr = id;mutex_unlock(&vrp->endpoints_lock);return ept;free_ept:mutex_unlock(&vrp->endpoints_lock);kref_put(&ept->refcount, __ept_release);return NULL;
}

4、 virtio_endpoint_ops结构体介绍

可以看到这个结构体中包含了很多后续rpmsg_device 的ops,所以对于利用了virtio 来实现rpmsg ipc的具体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,
};

5、rpmsg_ns_cb() 函数的介绍

rpsmg_probe()函数中,为了支持remote_device,会进行virtio_device->priv(virtproc)->ns_ept = cretea ns_ept ,其中就会为注册rpmsg_ns_cb(),该函数主要进行如下事情:

  1. 初始化rpmsg_channel_info;
  2. 根据channel_info进行rpomsg_create_channel
static int rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,void *priv, u32 src)
{struct rpmsg_ns_msg *msg = data;struct rpmsg_device *newch;struct rpmsg_channel_info chinfo;struct virtproc_info *vrp = priv;struct device *dev = &vrp->vdev->dev;int ret;#if defined(CONFIG_DYNAMIC_DEBUG)dynamic_hex_dump("NS announcement: ", DUMP_PREFIX_NONE, 16, 1,data, len, true);
#endifif (len != sizeof(*msg)) {dev_err(dev, "malformed ns msg (%d)\n", len);return -EINVAL;}/** the name service ept does _not_ belong to a real rpmsg channel,* and is handled by the rpmsg bus itself.* for sanity reasons, make sure a valid rpdev has _not_ sneaked* in somehow.*/if (rpdev) {dev_err(dev, "anomaly: ns ept has an rpdev handle\n");return -EINVAL;}/* don't trust the remote processor for null terminating the name */msg->name[RPMSG_NAME_SIZE - 1] = '\0';dev_info(dev, "%sing channel %s addr 0x%x\n",msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat",msg->name, msg->addr);strncpy(chinfo.name, msg->name, sizeof(chinfo.name));chinfo.src = RPMSG_ADDR_ANY;chinfo.dst = msg->addr;if (msg->flags & RPMSG_NS_DESTROY) {ret = rpmsg_unregister_device(&vrp->vdev->dev, &chinfo);if (ret)dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);} else {newch = rpmsg_create_channel(vrp, &chinfo);if (!newch)dev_err(dev, "rpmsg_create_channel failed\n");}return 0;
}

6、rpmsg_create_channel() 函数的介绍

函数原型如下:

  1. 调用rpmsg_device_match,根据chinfo来确认该channel没有被创建 ;
  2. 分配virtio_rpmsg_channel memory;
  3. 初始化rpmsg-device:virtio_rpmsg_channel->rpmsg_device src、dst、ops、announce、id.name;
  4. 根据初始化的rpmsg_device,进行rpmsg_register_device,往rpmsg_bus中register device;
  5. 返回rpmsg_device,该rpmsg_device addr == virtio_rpmsg_channel->rpmsg_device;

这样就可以理解为:

  1. 一个virtio_device对应一个virtio_rpmsg_channel;
  2. 一个virtio_rpmsg_channel对应一个rpmsg_device;
  3. 一个rpmsg_device对应一个rpmsg_driver;
/** create an rpmsg channel using its name and address info.* this function will be used to create both static and dynamic* channels.*/
static struct rpmsg_device *rpmsg_create_channel(struct virtproc_info *vrp,struct rpmsg_channel_info *chinfo)
{struct virtio_rpmsg_channel *vch;struct rpmsg_device *rpdev;struct device *tmp, *dev = &vrp->vdev->dev;int ret;/* make sure a similar channel doesn't already exist *//* 调用rpmsg_device_match,根据chinfo来确认该channel没有被创建 */tmp = rpmsg_find_device(dev, chinfo);if (tmp) {/* decrement the matched device's refcount back */put_device(tmp);dev_err(dev, "channel %s:%x:%x already exist\n",chinfo->name, chinfo->src, chinfo->dst);return NULL;}/* 分配virtio_rpmsg_channel memory */vch = kzalloc(sizeof(*vch), GFP_KERNEL);if (!vch)return NULL;/* Link the channel to our vrp */vch->vrp = vrp;/* Assign public information to the rpmsg_device *//* 初始化rpmsg-device* virtio_rpmsg_channel->rpmsg_device src、dst、ops、announce、id.name*/rpdev = &vch->rpdev;rpdev->src = chinfo->src;rpdev->dst = chinfo->dst;rpdev->ops = &virtio_rpmsg_ops;/** rpmsg server channels has predefined local address (for now),* and their existence needs to be announced remotely*/rpdev->announce = rpdev->src != RPMSG_ADDR_ANY;strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE);rpdev->dev.parent = &vrp->vdev->dev;rpdev->dev.release = virtio_rpmsg_release_device;/* 根据初始化的rpmsg_device,进行该device的register */ret = rpmsg_register_device(rpdev);if (ret)return NULL;return rpdev;
}

7、virtio_rpmsg_ops() 结构体的介绍

上面提到过,会将rpmsg_device->ops = &virtio_rpmsg_ops,这个结构体里面有如下三个操作函数,结合上面的理解,可以进一步规划为:

  1. 一个virtio_device对应一个virtio_rpmsg_channel;
  2. 一个virtio_rpmsg_channel对应一个rpmsg_device,一个virtio_rpmsg_channel对应多个rpmsg_endpoint
  3. 一个rpmsg_device对应一个rpmsg_driver;
static const struct rpmsg_device_ops virtio_rpmsg_ops = {.create_ept = virtio_rpmsg_create_ept,.announce_create = virtio_rpmsg_announce_create,.announce_destroy = virtio_rpmsg_announce_destroy,
};

其中virtio_rpmsg_create_ept()最终是调用__rpmsg_create_ept()

三、Virtio的介绍

1、virtio.c 的介绍

1.1 virtio_bus结构体的介绍

源码如下:

  • 该文件会建立一个name为“virtio”的bus

  • 有几个比较关键的函数:

    • virtio_dev_match;
    • virtio_dev_groups;
    • virtio_dev_probe;
    • virtio_dev_remove;
    static struct bus_type virtio_bus = {.name  = "virtio",.match = virtio_dev_match,.dev_groups = virtio_dev_groups,.uevent = virtio_uevent,.probe = virtio_dev_probe,.remove = virtio_dev_remove,
    };
    
  • 其通过core_initcall和module_exit进行bus的初始化

    static int virtio_init(void)
    {if (bus_register(&virtio_bus) != 0)panic("virtio bus registration failed");return 0;
    }static void __exit virtio_exit(void)
    {bus_unregister(&virtio_bus);ida_destroy(&virtio_index_ida);
    }
    core_initcall(virtio_init);
    module_exit(virtio_exit);
    

1.2 register_virtio_driver() 函数的介绍

当我们需要向virtio_bus register一个virtio_driver就需要调用该函数。

  • 源码如下:可以看到其最终是调用driver_register

    int register_virtio_driver(struct virtio_driver *driver)
    {/* Catch this early. */BUG_ON(driver->feature_table_size && !driver->feature_table);driver->driver.bus = &virtio_bus;return driver_register(&driver->driver);
    }
    EXPORT_SYMBOL_GPL(register_virtio_driver);
    

1.3 register_virtio_device() 函数的介绍

当我们需要向virtio_bus register一个virtio_device就需要调用该函数。

  • 源码如下:可以看到其最终是调用device_add(),主要做了如下事情:

    1. bus name 赋值;
    2. 设置相关标志;
    3. 调用device_add()向bus总线添加device;
    /*** register_virtio_device - register virtio device* @dev        : virtio device to be registered** On error, the caller must call put_device on &@dev->dev (and not kfree),* as another code path may have obtained a reference to @dev.** Returns: 0 on suceess, -error on failure*/
    int register_virtio_device(struct virtio_device *dev)
    {int err;dev->dev.bus = &virtio_bus;device_initialize(&dev->dev);/* Assign a unique device index and hence name. */err = ida_simple_get(&virtio_index_ida, 0, 0, GFP_KERNEL);if (err < 0)goto out;dev->index = err;dev_set_name(&dev->dev, "virtio%u", dev->index);spin_lock_init(&dev->config_lock);dev->config_enabled = false;dev->config_change_pending = false;/* We always start by resetting the device, in case a previous* driver messed it up.  This also tests that code path a little. */dev->config->reset(dev);/* Acknowledge that we've seen the device. */virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);INIT_LIST_HEAD(&dev->vqs);/** device_add() causes the bus infrastructure to look for a matching* driver.*/err = device_add(&dev->dev);if (err)ida_simple_remove(&virtio_index_ida, dev->index);
    out:if (err)virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);return err;
    }
    EXPORT_SYMBOL_GPL(register_virtio_device);
    

1.4 virtio_dev_probe 函数的介绍

该函数主要做如下事情:

  1. 根据据传入的device,找该device说属于的virtio_device和virtio_driver;
  2. 设定相关的标志位;
  3. **调用virtio_driver->probe()**这个函数后面会进行具体分析;
  4. 完成配置;
  • 函数源码:

    static int virtio_dev_probe(struct device *_d)
    {int err, i;/* find virtio_device according to device  */struct virtio_device *dev = dev_to_virtio(_d);/* find virtio_driver according to virtio_device  */struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);u64 device_features;u64 driver_features;u64 driver_features_legacy;/* We have a driver! */virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);/* Figure out what features the device supports. */device_features = dev->config->get_features(dev);/* Figure out what features the driver supports. */driver_features = 0;for (i = 0; i < drv->feature_table_size; i++) {unsigned int f = drv->feature_table[i];BUG_ON(f >= 64);driver_features |= (1ULL << f);}/* Some drivers have a separate feature table for virtio v1.0 */if (drv->feature_table_legacy) {driver_features_legacy = 0;for (i = 0; i < drv->feature_table_size_legacy; i++) {unsigned int f = drv->feature_table_legacy[i];BUG_ON(f >= 64);driver_features_legacy |= (1ULL << f);}} else {driver_features_legacy = driver_features;}if (device_features & (1ULL << VIRTIO_F_VERSION_1))dev->features = driver_features & device_features;elsedev->features = driver_features_legacy & device_features;/* Transport features always preserved to pass to finalize_features. */for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)if (device_features & (1ULL << i))__virtio_set_bit(dev, i);if (drv->validate) {err = drv->validate(dev);if (err)goto err;}err = virtio_finalize_features(dev);if (err)goto err;/* callback virtio_driver->probe() */err = drv->probe(dev);if (err)goto err;/* If probe didn't do it, mark device DRIVER_OK ourselves. */if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))virtio_device_ready(dev);if (drv->scan)drv->scan(dev);virtio_config_enable(dev);return 0;
    err:virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);return err;}
    

1.5 virtio_dev_match 函数的介绍

该函数是virtio_bus进行device 和driver match

  • 源码如下:

    可以看到,其有两个匹配原则:

    1. driver匹配任意device:virtio_driver->id_table[i] (virtio_device_id) ->vendor == VIRTIO_DEV_ANY_ID;
    2. driver匹配指定一类device:virtio_driver->id_table[i] (virtio_device_id) ->vendor == virtio_device->id.vendor
    static inline int virtio_id_match(const struct virtio_device *dev,const struct virtio_device_id *id)
    {if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID)return 0;return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor;
    }/* This looks through all the IDs a driver claims to support.  If any of them* match, we return 1 and the kernel will call virtio_dev_probe(). */
    static int virtio_dev_match(struct device *_dv, struct device_driver *_dr)
    {unsigned int i;struct virtio_device *dev = dev_to_virtio(_dv);const struct virtio_device_id *ids;ids = drv_to_virtio(_dr)->id_table;for (i = 0; ids[i].device; i++)if (virtio_id_match(dev, &ids[i]))return 1;return 0;
    }
    

2、virtio_input.c的介绍

上面virtio.c是介绍virtio bus,该文件是介绍virtio_driver。

在整个目前所使用的kernel版本中,有多种virtio_driver(virtio_pci_driver、virtio-mmio、virtio_balloon_driver、virtio_input_driver),除了virtio_input_driver支持VIRTIO_DEV_ANY_ID,其他的virtio_driver都是支持指定的virtio_device。

2.1 virtio_input_driver结构体介绍

  • 原型如下:

    • 可以看到其支持power manager的相关freeze和restore操作,当然其最重要的函数就是virtinput_probe
    • 这个结构体是通过module_virtio_driver宏进而调用register_virtio_driver()
    static unsigned int features[] = {/* none */
    };
    static struct virtio_device_id id_table[] = {{ VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },{ 0 },
    };static struct virtio_driver virtio_input_driver = {.driver.name         = KBUILD_MODNAME,.driver.owner        = THIS_MODULE,.feature_table       = features,.feature_table_size  = ARRAY_SIZE(features),.id_table            = id_table,.probe               = virtinput_probe,.remove              = virtinput_remove,
    #ifdef CONFIG_PM_SLEEP.freeze                = virtinput_freeze,.restore             = virtinput_restore,
    #endif
    };module_virtio_driver(virtio_input_driver);
    

2.2 virtinput_probe() 函数的介绍

  • 函数原型如下:可以看到所做的事情和virtio_ipc_driver是类似的:

    1. 初始化virtioquqe,设置callback;
    2. 分配buf;
    3. 设置相关属性:name、physaddr、serial name;
    4. 设置device为ready状态 ;
    5. kick off;
    static int virtinput_probe(struct virtio_device *vdev)
    {struct virtio_input *vi;unsigned long flags;size_t size;int abs, err;if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))return -ENODEV;vi = kzalloc(sizeof(*vi), GFP_KERNEL);if (!vi)return -ENOMEM;vdev->priv = vi; vi->vdev = vdev; spin_lock_init(&vi->lock);/* 初始化virtqueue* callback virtio_device->config->find_vqs* init virtinput_recv_events and virtinput_recv_status callback*/err = virtinput_init_vqs(vi);if (err)goto err_init_vq;/* alloc buf for device */vi->idev = input_allocate_device();if (!vi->idev) {err = -ENOMEM;goto err_input_alloc;}input_set_drvdata(vi->idev, vi);/* 设置相关属性:name、physaddr、serial name*/size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config,u.string),vi->name, min(size, sizeof(vi->name)));size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config,u.string),vi->serial, min(size, sizeof(vi->serial)));snprintf(vi->phys, sizeof(vi->phys),"virtio%d/input0", vdev->index);vi->idev->name = vi->name;vi->idev->phys = vi->phys;vi->idev->uniq = vi->serial;size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);if (size >= sizeof(struct virtio_input_devids)) {virtio_cread(vi->vdev, struct virtio_input_config,u.ids.bustype, &vi->idev->id.bustype);virtio_cread(vi->vdev, struct virtio_input_config,u.ids.vendor, &vi->idev->id.vendor);virtio_cread(vi->vdev, struct virtio_input_config,u.ids.product, &vi->idev->id.product);virtio_cread(vi->vdev, struct virtio_input_config,u.ids.version, &vi->idev->id.version);} else {vi->idev->id.bustype = BUS_VIRTUAL;}virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,vi->idev->propbit, INPUT_PROP_CNT);size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);if (size)__set_bit(EV_REP, vi->idev->evbit);vi->idev->dev.parent = &vdev->dev;vi->idev->event = virtinput_status;/* device -> kernel */virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,vi->idev->keybit, KEY_CNT);virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,vi->idev->relbit, REL_CNT);virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,vi->idev->absbit, ABS_CNT);virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,vi->idev->mscbit, MSC_CNT);virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,vi->idev->swbit,  SW_CNT);/* kernel -> device */virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,vi->idev->ledbit, LED_CNT);virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,vi->idev->sndbit, SND_CNT);if (test_bit(EV_ABS, vi->idev->evbit)) {for (abs = 0; abs < ABS_CNT; abs++) {if (!test_bit(abs, vi->idev->absbit))continue;virtinput_cfg_abs(vi, abs);}}/* 设置device为ready状态 */virtio_device_ready(vdev);vi->ready = true;err = input_register_device(vi->idev);if (err)goto err_input_register;/* 设定完成将会kickoff   */virtinput_fill_evt(vi);return 0;err_input_register:spin_lock_irqsave(&vi->lock, flags);vi->ready = false;spin_unlock_irqrestore(&vi->lock, flags);input_free_device(vi->idev);
    err_input_alloc:vdev->config->del_vqs(vdev);
    err_init_vq:kfree(vi);return err;
    }

四、virtio_device、virtio_driver、virtio_bus、rpmsg_device、rpmsg_drivver、rpmsg_bus如何如何联系且运作起来

1、联系的建立

当系统初始化时(前提ce是打开的kernel的rpmsg与virtio的相关config),其实已经存在了virtio_bus、virtio_ipc_driver、rpmsg_bus、rpmsg_bus,如下图:

  1. 此时需要register virtio_device,进而触发virtio_bus->probe(),该probe会进行如下事情:

    1. 根据据传入的device,找该device所属于的virtio_device和virtio_driver;
    2. 设定相关的标志位;
    3. 调用virtio_driver->probe()
    4. 完成配置;
  2. virtio_bus->probe() 会进而调用virtio_ipc_driver->probe(),该probe会做如下事情:
    1. 根据vdev->config->find_vqs(),根据names作为匹配原则期望找到对应的rx与tx virtqueue;

    2. 根据找到的rx/tx,将其赋值到virtproc_info->virqueue[0/1] ;

    3. 统计rx/tx 所需要的buf size/buf num/total buf space,并为需要的buf申请DMA memory

    4. DMA memory对半分给若rx/tx;

    5. 每一个rx buf进行vring的初始化

    6. 上述virtproc_info初始化赋值给该virtio_device

    7. 支持remote process,创建一个以RPMSG_NS_ADDR的virtproc_info->ns_ept,支持后续的name service announcement;

    8. 备kick_off并告知remote process notify

完成上述两个步骤后,此时virtio_ipc_driver-----virtio_bus-----virtio_device已经建立成功,且该virtio_device中有一个ns_ept(virtio_device->priv(virtproc_inf)->ns_ept)。

  1. 此时我们可以进行客制化,调用该virtio_device->priv(virtproc_inf)->ns_ept->cb()即可rpmsg_ns_cb(),该函数会进行rpmsg_create_channel():

    1. 初始化rpmsg_channel_info;
    2. 根据channel_info进行rpomsg_create_channel
  2. rpomsg_create_channel()进而会调用rpmsg_register_device():
    1. 调用rpmsg_device_match,根据chinfo来确认该channel没有被创建 ;
    2. 分配virtio_rpmsg_channel memory;
    3. 初始化rpmsg-device:virtio_rpmsg_channel->rpmsg_device src、dst、ops、announce、id.name;
    4. 根据初始化的rpmsg_device,进行rpmsg_register_device,往rpmsg_bus中register device;
    5. 返回rpmsg_device,该rpmsg_device addr == virtio_rpmsg_channel->rpmsg_device;
  3. 由于调用了rpmsg_register_device(),则进而会出发rpmsg_dev_probe(),将该rpmsg_device匹配上rpmsg_driver

此时就已经完成了virtio_device-----virtio_bus----virtio_ipc_driver <-----> rpmsg_device----rpmsg_bus-----rpmsg_driver,之间的联系,如下图:

2、运作

由于此时已经建立的联系,可以使用rpmsg_create_ept、rpmsg_send、rpmsg_trysend、rpmsg_poll等一系列的rpmsg operation,这个会进而会以callback的形式,最终调用virtio_rpmsg_bus.c中的virtio_rpmsg_ops。

举个例子,调用rpsmg_create_ept()

/----------------- rpmsg_core.c ---------------------/
struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,rpmsg_rx_cb_t cb, void *priv,struct rpmsg_channel_info chinfo)
{if (WARN_ON(!rpdev))return NULL;return rpdev->ops->create_ept(rpdev, cb, priv, chinfo);
}/* 该rpmsg device->ops,即是之前* virtio_device进行virtio_ipc_driver->probe()时:* virtio_device->priv(virtproc_info)->ns_ept->ops = & virtio_endpoint_ops;* 当根据该ns_ept->cb()进行rpmsg_channel-->rpmsg_device的建立时:* rpmsg->device->ops = & virtio_rpmsg_ops* virtio_rpmsg_ops中就有virtio_rpmsg_create_ept*/

Rpmsg与Virtio介绍相关推荐

  1. KVM之Virtio介绍 (十五)

    概而言之,virtio 是半虚拟化 hypervisor 中位于设备之上的抽象层.virtio 由 Rusty Russell 开发,他当时的目的是支持自己的虚拟化解决方案 lguest.本文在开篇时 ...

  2. IO虚拟化 - virtio介绍及代码分析【转】

    1. 概述 Virtio是linux平台下一种IO半虚拟化框架,virtio 由 Rusty Russell 开发,他当时的目的是支持自己的虚拟化解决方案 lguest.而在KVM中也广泛使用了vir ...

  3. 虚拟化设备-Virtio介绍

    虚拟化环境必须使用客户操作系统自身的驱动感觉不到自己运行在虚拟机上,不然让客户操作系统开发人员编写大量代码同样不是一个很好的设计方案,因此虚拟化系统需要提供抽象设备,抽象设备实现了针对特定设备类的高级 ...

  4. MPU进化,多核异构处理器有多强?A核与M核通信过程解析

    内容来源:www.forlinx.com 随着市场对嵌入式设备功能需求的提高,市面上出现了集成嵌入式处理器和单片机的主控方案,以兼顾性能和效率. 在实际应用中,嵌入式处理器和单片机之间需要进行大量且频 ...

  5. 【正点原子MP157连载】第二十八章 A7和M4联合调试-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  6. 兼具A核和M核的多核异构处理器,A核与M核如何通讯?

    随着市场对嵌入式设备功能需求的提高,市面上出现了集成嵌入式处理器和单片机的主控方案,以兼顾性能和效率. 在实际应用中,嵌入式处理器和单片机之间需要进行大量且频繁的数据交换,如果采用低速串行接口,则数据 ...

  7. 多核异构核间通信-mailbox/RPMsg 介绍及实验

    1. 多核异构核间通信 由于MP157是一款多核异构的芯片,其中既包含的高性能的A7核及实时性强的M4内核,那么这两种处理器在工作时,怎么互相协调配合呢? 这就涉及到了核间通信的概念了. IPCC ( ...

  8. l298n电机驱动模块使用方法_SPDK virtio 驱动模块介绍及使用

    作者简介闫亮 Intel高级软件工程师专注于开源存储SPDK的测试和优化 简介 CONTENTS SPDK  virtio模块介绍 SPDK  virtio 用户模式使用示例 SPDK  virtio ...

  9. 高通Q+A Virtio hypervisor touch框架介绍(share-device)

    背景 大家都知道现在高通芯片在汽车行业的座舱域运用比较热门,但是这种现象不是突然冒出来的,高通最早在2015年左右就开始推广他们的第一代座舱芯片820A.而在2018年左右开始推广他们的第二代产品(6 ...

最新文章

  1. Python在ubuntu中更改Python和pip指向
  2. tf.keras.layers.Resizing 示例 改变维度的层
  3. java查看文件夹下文件夹大小,java 获取文件夹大小,文件大小,文件个数
  4. Android规范文档
  5. jenkins 入门教程(下)
  6. 文计笔记1: 计算机基本原理
  7. SAP事业部内部互供
  8. php函数嵌套 作用域,javascript 嵌套的函数(作用域链)_javascript技巧
  9. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制
  10. boot定时任务开启和关闭 spring_spring-boot 多线程并发定时任务的解决方案
  11. JS对数据进行判空操作
  12. 嵌入式linux入门六步曲
  13. three.js 加载fbx文件并解析
  14. 微软Exchange Server 0Day漏洞,尽快修复
  15. Windows API (DAY07) ShowWindow 函数
  16. 我怎样学会英语的--钟道隆逆向英语学习法1
  17. 从原理上解释什么是DDR的ZQ校准?
  18. 数码博主自用五款高性价比蓝牙耳机分享,双11不想踩雷选什么蓝牙耳机好?
  19. Problem F: Matrix Problem (III) : Array Practice Time Limit: 1 Sec Memory Limit: 4 MB Submit: 8787
  20. php强类型 vscode,VSCode 扩展入门,后缀代码补全的实现

热门文章

  1. 2020 年新基建七大领域 数据“新基建”
  2. java之阻塞队列和非阻塞队列
  3. 咖啡制作培训:黑芝麻生椰拿铁详细教程
  4. 一加论坛签到3天积分换来的完整版壁纸合集
  5. win32通用控件TreeView滚动条自绘
  6. 计算机的诊断策略服务怎么打开6,win7诊断策略服务已被禁用的最佳解决方法
  7. 批处理:PUSHD和CD的区别
  8. 007_理解App Upgrade
  9. HTC One M8 拥有 IPX3 防水认证
  10. 常见测试用例的设计方法