首页 > 其他分享 >Rpmsg与Virtio介绍【转】

Rpmsg与Virtio介绍【转】

时间:2023-06-26 18:35:20浏览次数:53  
标签:virtio Rpmsg ept driver 介绍 rpmsg Virtio dev device

转自:https://blog.csdn.net/weixin_42813232/article/details/125577142

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\

Kconfig
Makefile
qcom_glink_native.c
qcom_glink_native.h
qcom_glink_rpm.c
qcom_glink_smem.c
qcom_smd.c
rpmsg_char.c
rpmsg_core.c
rpmsg_internal.h
virtio_rpmsg_bus.c

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

 

 

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

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

bus的构建;
Driver-device的match;
device的probe与remove;
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,
};

Rpmsg Driver:由rpmsg_char.c所register(上图中还有两个文件,不在该章节进行介绍),负责:

register char driver;
Remove char driver;
该driver name 为 rpmsg_chrdev;
static struct rpmsg_driver rpmsg_chrdev_driver = {
.probe = rpmsg_chrdev_probe,
.remove = rpmsg_chrdev_remove,
.drv = {
.name = "rpmsg_chrdev",
},
};

Rpmsg Device:该层一开始是只有Virtio框架所构成的,后面添加了Glink与SMD架构(主要为高通所用),故主要介绍Virtio框架,通过上图可知其主要由virtio_rpmsg_bus.c 文件所维护:

该文件比较特殊,其是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,
};

该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函数:

完成bus_type_private的初始化、创建并注册的这条总线需要的目录,该目录为rpmsg;
在rpmsg目录下创建/device /driver 目录;
初始化这条总线上的设备链表:struct klist klist_devices;
初始化这条总线上的驱动链表: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
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函数:

该virtio_driver被设为的属于virtio_bus —>这就印证了刚刚的猜测;
通过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函数。

源码如下图:

主要做了如下的事情:

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

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

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

DMA memory对半分给若rx/tx;

对每一个rx buf进行vring的初始化;

将上述virtproc_info初始化赋值给该virtio_device;

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

准备kick_off并告知remote process notify

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

先根据名字,找到rx/tx virtqueue(vqs);
将rx/tx virtqueue存储到virtproc_info(vrq)->rvq/tvq;
初始化vrq(rvq vring、tvq info、ns_ept);
将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;
else
vrp->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 addr

rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size); //根据cpu addr初始化一个散列表

err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr, //根据散列表与cpu addr,初始化rx vring inbuf
GFP_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(),该函数主要进行如下事情:

初始化rpmsg_channel_info;
根据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);
#endif

if (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() 函数的介绍
函数原型如下:

调用rpmsg_device_match,根据chinfo来确认该channel没有被创建 ;
分配virtio_rpmsg_channel memory;
初始化rpmsg-device:virtio_rpmsg_channel->rpmsg_device src、dst、ops、announce、id.name;
根据初始化的rpmsg_device,进行rpmsg_register_device,往rpmsg_bus中register device;
返回rpmsg_device,该rpmsg_device addr == virtio_rpmsg_channel->rpmsg_device;
这样就可以理解为:

一个virtio_device对应一个virtio_rpmsg_channel;
一个virtio_rpmsg_channel对应一个rpmsg_device;
一个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,这个结构体里面有如下三个操作函数,结合上面的理解,可以进一步规划为:

一个virtio_device对应一个virtio_rpmsg_channel;
一个virtio_rpmsg_channel对应一个rpmsg_device,一个virtio_rpmsg_channel对应多个rpmsg_endpoint;
一个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(),主要做了如下事情:

bus name 赋值;
设置相关标志;
调用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 函数的介绍
该函数主要做如下事情:

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

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;
else
dev->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

源码如下:

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

driver匹配任意device:virtio_driver->id_table[i] (virtio_device_id) ->vendor == VIRTIO_DEV_ANY_ID;
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是类似的:

初始化virtioquqe,设置callback;
分配buf;
设置相关属性:name、physaddr、serial name;
设置device为ready状态 ;
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,如下图:

 

 

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

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

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

DMA memory对半分给若rx/tx;

对每一个rx buf进行vring的初始化;

将上述virtproc_info初始化赋值给该virtio_device;

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

准备kick_off并告知remote process notify

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

此时我们可以进行客制化,调用该virtio_device->priv(virtproc_inf)->ns_ept->cb()即可rpmsg_ns_cb(),该函数会进行rpmsg_create_channel():
初始化rpmsg_channel_info;
根据channel_info进行rpomsg_create_channel
rpomsg_create_channel()进而会调用rpmsg_register_device():
调用rpmsg_device_match,根据chinfo来确认该channel没有被创建 ;
分配virtio_rpmsg_channel memory;
初始化rpmsg-device:virtio_rpmsg_channel->rpmsg_device src、dst、ops、announce、id.name;
根据初始化的rpmsg_device,进行rpmsg_register_device,往rpmsg_bus中register device;
返回rpmsg_device,该rpmsg_device addr == virtio_rpmsg_channel->rpmsg_device;
由于调用了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
*/
————————————————
版权声明:本文为CSDN博主「Going1」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42813232/article/details/125577142

标签:virtio,Rpmsg,ept,driver,介绍,rpmsg,Virtio,dev,device
From: https://www.cnblogs.com/sky-heaven/p/17506454.html

相关文章

  • linux 核间通讯rpmsg架构分析【转】
    转自:https://blog.csdn.net/wind0419/article/details/123277545以imx8为例在最底层硬件上,A核和M核通讯是靠硬件来进行的,称为MU,如图  LinuxRPMsg是在virtioframework上实现的一个消息传递机制VirtIO是一个用来实现“虚拟IO”的通用框架,典型虚拟的pci,网卡,磁盘等虚拟设......
  • fiddler的介绍和使用
    一、fiddler工作原理Fiddler是以代理WEB服务器的形式工作的,浏览器与服务器之间通过建立TCP连接以HTTP协议进行通信,浏览器默认通过自己发送HTTP请求到服务器,它使用代理地址:127.0.0.1,端口:8888.当Fiddler开启会自动设置代理,退出的时候它会自动注销代理,这样就不会影响别的......
  • 作为一个客户经理你一个如何给客户介绍API接口
    随着科技的发展,API(ApplicationProgrammingInterface,应用程序接口)的应用已经逐渐普及,而API接口作为现代企业实现智能化运营和管理的重要工具之一,也备受关注。作为一名客户经理,向客户介绍API接口,需要做好充分的准备工作和沟通,下面是一些我在实践中总结的建议:确定客户需求:在与客......
  • 存储系统文件共享协议(POSIX、NFS、SMB/CIFS、FTP、HTTP)介绍
    当您使用云存储系统,需要将文件共享给客户端时,通常需要通过共享协议来实现。目前比较流行的文件共享协议包括:POSIX、NFS、SMB/CIFS、FTP、HTTP,本文为您介绍这几种协议的基本概念、应用场景、访问方式和优缺点,帮助您选择合适的协议进行文件共享。协议概述典型应用场景访问方......
  • pgmetrics 介绍
    pgmetrics介绍pgmetrics是一个开源的、零依赖的、单二进制的工具,它可以轻松收集和报告PostgreSQL指标,用于脚本编写、自动化和故障排除。pgmetrics从正在运行的PostgreSQL服务器收集350多个指标,并以易于阅读的文本格式显示,或者将其导出为JSON和CSV用于脚本编写。pgmetrics是用......
  • 工业网络交换机的功率和管理功能介绍
    工业网络交换机的功率和管理功能可以根据具体的产品型号和厂商提供的规格和功能而有所不同。下面是一些常见的工业网络交换机功率和管理功能:功率:供电方式:工业网络交换机通常支持多种供电方式,包括AC电源、DC电源、POE(PoweroverEthernet)等。不同的供电方式可以根据实际情况选择,以......
  • spring里的@ImportResource注解介绍
    @ImportResource注解是Spring框架中的一个注解,它用于导入外部的XML配置文件。通过@ImportResource注解,可以将外部的XML配置文件加载到Spring的应用上下文中,从而使得这些配置文件中定义的Bean能够被Spring容器管理。使用@ImportResource注解的步骤如下:在需要使......
  • Kubernetes控制器介绍(二)
    一、DaemonSet1.1介绍与Deployment相似的是,DaemonSet也是基于标签选择器管控一组Pod副本。但是,DaemonSet用于确保所有或指定的工作节点上都运行有一个Pod副本。换句话说,其Pod数量由节点数量而定,因此无需定义replicas字段(Pod的副本数量)。从集群移除节点时,此类Pod对象也将被自动回......
  • MongoDB介绍
    MongoDB介绍MongoDB简介MongoDB是免费开源的跨平台NoSQL数据库,命名源于英文单词humongous,意思是「巨大无比」,可见开发组对MongoDB的定位。与关系型数据库不同,MongoDB的数据以类似于JSON格式的二进制文档存储:{name:"itBaiZhan",age:18,hobbies:["python",......
  • F5APM第一期产品功能介绍​
    F5APM第一期产品功能介绍APM的模式一:NetworkTunnelAccess:类似SSLVPNAPM的模式二:PortalAccess:全代理模式,后端的应用代理到APM上......