上一节中,我们已经知道,某个soc camera device 已经被添加到device链表中,那么,什么情况下,它会被注册呢。下面我们就结合camera host 进一步分析。
我们来看看在soc_camera.c中有一个函数int soc_camera_host_register(struct soc_camera_host *ici),该函数有一个参数struct soc_camera_host的指针,那么这个函数是由谁来调用的呢,这就要牵涉到soc camera host驱动的编写了,暂时不表,在这里也不须太过关心。我们首先来贴出这个函数来分析一下
int soc_camera_host_register(struct soc_camera_host *ici)
{
struct soc_camera_host *ix;
int ret;
......
⑴
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
if (ix->nr == ici->nr) {
ret = -EBUSY;
goto edevreg;
}
}
⑵
ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
if (ret < 0)
goto edevreg;
⑶
list_add_tail(&ici->list, &hosts);
mutex_unlock(&list_lock);
⑷
scan_add_host(ici);
return 0;
edevreg:
mutex_unlock(&list_lock);
return ret;
}
(1)处的代码,用于判断,当前host链表中是否已经存在index 为ici->nr的soc camera host,如果存在,则说明该index已经被占用,返回出错码-EBUSY。
(2)处的代码调用了函数v4l2_device_register,那么该函数到底做了什么呢?有兴趣的同学可以到v4l2-device.c中查看源码,也可以简单的理解为向内核注册了soc camera host设备。当然,v4l2_device_register也会做一些自旋锁,互斥锁的初始化,以及引用计数和链表subdevs的初始化,其中特别注意,这个链表subdevs,这个会涉及到image capture设备,其实每个image capture设备也会对应一个subdev。
(3)处的代码,就很简单了,将ici添加到本地链表hosts中。
(4)处的代码调用了函数scan_add_host,那么这个函数又做了什么呢?好了,高潮来了,我们展开这个函数,来分析一下。
/* So far this function cannot fail */
static void scan_add_host(struct soc_camera_host *ici)
{
struct soc_camera_device *icd;
mutex_lock(&list_lock);
list_for_each_entry(icd, &devices, list) {
if (icd->iface == ici->nr) {
int ret;
icd->dev.parent = ici->v4l2_dev.dev;
dev_set_name(&icd->dev, "%u-%u", icd->iface,
icd->devnum);
ret = device_register(&icd->dev);
if (ret < 0) {
icd->dev.parent = NULL;
dev_err(&icd->dev,
"Cannot register device: %d\n", ret);
}
}
}
mutex_unlock(&list_lock);
}
看到了吗,这个函数的核心还是一个for循环,遍历链表devices,确定devices链表上是否有camera device需要连接到host上,如果有,则将icd->dev.parent赋值为ici->v4l2_dev.dev,其中核心工作是dev_register(&icd->dev),如果对Linux内核中的“总线,设备,驱动”模型熟悉的话,相信就会明白接下来会发生什么事情。大家还记得吗,icd->dev所挂载的总线是本地的总线soc_camera_bus_type,其中soc_camera_type代码如下:
struct bus_type soc_camera_bus_type = {
.name = "soc-camera",
.probe = soc_camera_probe,
.remove = soc_camera_remove,
.suspend = soc_camera_suspend,
.resume = soc_camera_resume,
};
大家很容易就能发现,该bus_type并没有实现match函数,而实现了probe函数,那么这么做的目的是什么呢?我们来先展开device_register函数的调用流程
看了流程图之后,大家是不是明白了,接下来会调用soc_camera_bus_type的成员函数probe了,好了,我们来着重分析soc_camera_probe函数,首先将代码展开如下:
/* Called during host-driver probe */
static int soc_camera_probe(struct device *dev)
{
⑴
struct soc_camera_device *icd = to_soc_camera_dev(dev);
struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
struct soc_camera_link *icl = to_soc_camera_link(icd);
struct device *control = NULL;
int ret;
⑵
ret = ici->ops->add(icd);
if (ret < 0)
goto eadd;
/* The camera could have been already on, try to reset */
if (icl->reset)
icl->reset(icd->pdev);
/* Must have icd->vdev before registering the device */
⑶
ret = video_dev_create(icd);
if (ret < 0)
goto evdc;
⑷
/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
if (icl->board_info) {
ret = soc_camera_init_i2c(icd, icl);
if (ret < 0)
goto eadddev;
} else if (!icl->add_device || !icl->del_device) {
ret = -EINVAL;
goto eadddev;
} else {
⑸
if (icl->module_name)
ret = request_module(icl->module_name);
ret = icl->add_device(icl, &icd->dev);
if (ret < 0)
goto eadddev;
/*
* FIXME: this is racy, have to use driver-binding notification,
* when it is available
*/
control = to_soc_camera_control(icd);
if (!control || !control->driver || !dev_get_drvdata(control) ||
!try_module_get(control->driver->owner)) {
icl->del_device(icl);
goto enodrv;
}
}
⑹
/* At this point client .probe() should have run already */
ret = soc_camera_init_user_formats(icd);
if (ret < 0)
goto eiufmt;
icd->field = V4L2_FIELD_ANY;
icd->vdev->lock = &icd->video_lock;
/*
* ..._video_start() will create a device node, video_register_device()
* itself is protected against concurrent open() calls, but we also have
* to protect our data.
*/
mutex_lock(&icd->video_lock);
⑺
ret = soc_camera_video_start(icd);
if (ret < 0)
goto evidstart;
......
}
首先来看(1)处的几个重要的内联函数,展开代码。
static inline struct soc_camera_device *to_soc_camera_dev(
const struct device *dev)
{
return container_of(dev, struct soc_camera_device, dev);
}
相信,对于内核链表相当熟悉的人,那么对container_of这个宏也应该很清楚了,这是通过指向结构体内部成员变量的指针,来获取该结构体的地址。
接着看下一个内联函数,展开代码如下:
static inline struct soc_camera_host *to_soc_camera_host(
const struct device *dev)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
return container_of(v4l2_dev, struct soc_camera_host, v4l2_dev);
}
这个内联函数也很好理解,首先,通过icd->dev.parent获取v4l2_dev,可能有人会问,既然get了,哪里set的呢,首先,大家要搞清楚,icd->dev.parent= ici->v4l2_dev.dev(这个赋值是在scan_add_host里面做的),然后,我们要知道,哪里set了呢,前面分析的函数soc_camera_host_register还记得吧,就是在该函数体内通过调用函数v4l2_device_register函数来设置的。继续分析,获取了v4l2_dev之后,就简单了,还是通过container_of来获取外部结构体soc_camera_host。
接着展开最后一个内联函数.
static inline struct soc_camera_link *to_soc_camera_link(
const struct soc_camera_device *icd)
{
return icd->dev.platform_data;
}
这个函数也是相当的简单,直接从icd->dev.platform_data中获取soc_camera_link,大家可能会问,这个哪里赋值的呢?我们在上一篇博客分析soc_camera_devic 初始化的时候,分析了platform driver probe函数,该函数体内调用了soc_camera_device_init函数,就是在这个函数体内,将soccamera link赋值给了icd->dev.platform_data。
好了,这几个内联函数解析完了,不论是在soc_camera.c中或者自己写soc camera host驱动的时候,就会经常用到这几个宏。接下来继续分析soc_camera_probe函数,再来看(2)处的代码,该代码会调用soc camera host 驱动实现的一个add函数,该add函数主要是做一些硬件方面的初始化,比如时钟的使能。
继续分析(3)处的代码,闲话少说,直接展开video_dev_create的代码。
static int video_dev_create(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct video_device *vdev = video_device_alloc();
if (!vdev)
return -ENOMEM;
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
vdev->parent = &icd->dev;
vdev->current_norm = V4L2_STD_UNKNOWN;
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
vdev->tvnorms = V4L2_STD_UNKNOWN;
icd->vdev = vdev;
return 0;
}
在分析video_dev_create这个函数之前,我们要明白V4L2 系统的体现结构,soccamera 子系统是基于V4L2 系统的,所以来说,不论是soc camera host还是soc camera device都是存在于soc camera 子系统中的,而对于v4l2 系统来说,它不管你将host或者image capture抽象成什么结构体,最终,你要注册给V4L2一个抽象出来的实体,那就是Video_device,V4L2系统就是通过这个实体来起到承上启下的作用的。所以在video_dev_create函数体内,要申请一段内存,并且初始化video_device,其中重要的三个初始化如下:vdev->parent = &icd->dev;
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_iotcl_ops;
至于这三个成员的初始化为什么重要,先不表,大家先留个印象,待用时我们在着重分析。
接着分析(4)处的代码,先看if体内,对I2C熟悉的同学,应该明白,看到board info,大家应该就明白了,这是要初始化i2c啊。在前面的博客soc camera 子系统简介中,我曾经提到过,soc camera 子系统主要是针对当前的移动平台,而多数移动平台的摄像头都是固定在移动设备上的,一般是通过i2c总线集成到soc上面的,因此这里需要初始化i2c,先展开代码。
static int soc_camera_init_i2c(struct soc_camera_device *icd,
struct soc_camera_link *icl)
{
struct i2c_client *client;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
struct v4l2_subdev *subdev;
if (!adap) {
dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n",
icl->i2c_adapter_id);
goto ei2cga;
}
icl->board_info->platform_data = icd;
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
icl->board_info, NULL);
if (!subdev)
goto ei2cnd;
client = v4l2_get_subdevdata(subdev);
/* Use to_i2c_client(dev) to recover the i2c client */
dev_set_drvdata(&icd->dev, &client->dev);
return 0;
ei2cnd:
i2c_put_adapter(adap);
ei2cga:
return -ENODEV;
}
这个函数分析起来稍显繁琐,因为要牵涉到image capture 驱动(主要是i2c driver的probe函数),我只讲一下大致做了哪些事情。
首先,大家要注意这个函数内部出现了一个新的结构体,v4l2_subdev,这里是针对image capture的,即每个image capture都会对应一个v4l2_subdev,这个结构体内有一个重要的成员变量v4l2_subdev_ops,是有image capture驱动来实现的,而v4l2_subdev就是soc_camera.c以及host驱动来调用imagecapture相关实现的中介。我们来画一个subdev和soc camera device得关系图,方便大家理解。
通过这个关系图,很好理解下面的内联函数。
static inline struct device *to_soc_camera_control(
const struct soc_camera_device *icd)
{
return dev_get_drvdata(&icd->dev);
}
static inline struct v4l2_subdev *soc_camera_to_subdev(
const struct soc_camera_device *icd)
{
struct device *control = to_soc_camera_control(icd);
return dev_get_drvdata(control);
}
大家可以根据上面的关系图,来理解内联函数soc_camera_to_subdev,这个内联函数就是通过soccamera device来获取subdev的。其实subdev和soc camera device都是对应一个image capture,只是所起的作用不一样罢了。
(6)处的代码用于获取当前摄像头所支持的所有格式,并且存储在icd->user_formats数组中,是通过subdev来调用image capture 驱动实现的回调函数来实现的,
(7)处的代码先展开如下:
/*
* Called from soc_camera_probe() above (with .video_lock held???)
*/
static int soc_camera_video_start(struct soc_camera_device *icd)
{
const struct device_type *type = icd->vdev->dev.type;
int ret;
if (!icd->dev.parent)
return -ENODEV;
if (!icd->ops ||
!icd->ops->query_bus_param ||
!icd->ops->set_bus_param)
return -EINVAL;
ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
return ret;
}
/* Restore device type, possibly set by the subdevice driver */
icd->vdev->dev.type = type;
return 0;
}
主要做的工作就是向V4L2系统注册设备video device。下一篇会继续分析!
本文详细解析了SOC Camera子系统中设备注册的过程,包括如何将Camera设备与Host进行关联,以及Camera设备如何通过V4L2系统进行注册。重点介绍了关键函数soc_camera_host_register和soc_camera_probe的工作原理。
982

被折叠的 条评论
为什么被折叠?



