soc camera 子系统之soc camera host 与soc camera device 注册

来源:互联网 发布:淘宝买三角梅哪家好 编辑:程序博客网 时间:2024/05/12 19:47

      上一节中,我们已经知道,某个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。下一篇会继续分析!

 











0 0
原创粉丝点击