zoukankan      html  css  js  c++  java
  • camera驱动框架分析(中)

    camera host的驱动

    下面开始分析camera host吧,如果仅仅是想知道camera sensor驱动怎么写,而不想知道内部具体怎么个调用流程,怎么个架构设计,那可以跳过该部分,直接去看i2c camera sensor的驱动了。前面说了我们选择at91平台,那对应的camera host 文件就是drivers/media/platform/soc_camera/atmel-isi.c了。

    static struct platform_driver atmel_isi_driver = {
    	.remove		= atmel_isi_remove,
    	.driver		= {
    		.name = "atmel_isi",
    		.owner = THIS_MODULE,
    	},
    };
    
    module_platform_driver_probe(atmel_isi_driver, atmel_isi_probe);
    

    直接看atmel_isi_probe吧,它怎么和它对应的平台设备匹配的过程就不再描述了,和上面说的类似,具体实现在文件arch/arm/mach-at91/at91sam9g45_devices.c里。

    atmel_isi_probe里面有一部分会设计到该soc isi(camera host)的硬件初始化,我这里不跟踪进去,只是将和camera sensor驱动相关的代码摘取出来:

    static int atmel_isi_probe(struct platform_device *pdev)
    {
    	unsigned int irq;
    	struct atmel_isi *isi;
    	struct resource *regs;
    	int ret, i;
    	struct device *dev = &pdev->dev;
    	struct soc_camera_host *soc_host;
    	struct isi_platform_data *pdata;
    
    	.....
    	.....
    	.....
    
    	soc_host		= &isi->soc_host;
    	soc_host->drv_name	= "isi-camera";
    	soc_host->ops		= &isi_soc_camera_host_ops;
    	soc_host->priv		= isi;
    	soc_host->v4l2_dev.dev	= &pdev->dev;
    	soc_host->nr		= pdev->id;
    
    	ret = soc_camera_host_register(soc_host);
    	if (ret) {
    		dev_err(&pdev->dev, "Unable to register soc camera host
    ");
    		goto err_register_soc_camera_host;
    	}
    	return 0;
    
    	.....
    	.....
    	.....
    
    	return ret;
    }
    

    这里我们会开始接触第三个重要的数据结构soc_camera_host,他在内核里代表的就是一个camera host设备。另外,第四个重要的数据结构soc_camera_host_ops,它是soc_camera架构为camera host定义的,用来实现所有host相关的操作回调。soc_camera在适当的时候,会调用里面的接口。camera host驱动主要工作的一部分就是实现该数据结构了。里面有一句soc_host->v4l2_dev.dev = &pdev->dev;表示soc_camera_host内嵌的v4l2框架定义的父对象(子对象v4l2_subdev,后面会出现的)对应的设备就是该camera host平台设备。将soc_camera_host进行适当的初始化后,再调用soc_camera架构定义的api soc_camera_host_register来实现camera host的注册。也就是在这里,会处理上面soc_camera_device_register注册进系统的camera sensor了。它里面核心的动作主要有两个

    第一个,ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev),这是v4l2框架给出的api,camera host在v4l2架构里面应该算是一个v4l2_device,camera里面的sensor控制部分,即i2c设备对应就应该是v4l2_subdev了。

    第二个,scan_add_host(ici),就是这个函数完成了对soc_camera_device_register注册进系统的camera的扫描及处理。

    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) {
    			struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
    			struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
    
    			/* The camera could have been already on, try to reset */
    			if (ssdd->reset)
    				ssdd->reset(icd->pdev);
    
    			icd->parent = ici->v4l2_dev.dev;
    
    			/* Ignore errors */
    			soc_camera_probe(ici, icd);
    		}
    
    	mutex_unlock(&list_lock);
    }
    

    它主要就是扫描devices全局链表,找里面icd->iface和自己的nr相同的。这里的icd->iface其实就是在soc_camera_link里面的bus_id,而nr对应着camera host平台设备描述信息里面的id。这里找到匹配的设备后,就调用soc_camera_probe(ici, icd)来处理挂载在该总线上的设备了。传入的两个参数ici,是我们camera host驱动分配并初始化的数据结构,由camera host负责初始化它里面的v4l2_dev以及ops等等,而icd是通用驱动soc_camera分配并根据我们在扳级相关的文件里面用soc_camera_link描述的信息初始化的对象。另外,还一个需要注意的地方,icd->parent = ici->v4l2_dev.dev;这里指定icd,也就是soc camera的parent为camera host,ici->v4l2_dev.dev就代表着camera host platform device对应的device,不信请回到atmel_isi_probe,里面有一句soc_host->v4l2_dev.dev = &pdev->dev;可以证实啦。这其实很好理解啦,camera host 当然就应该是camera的parent,对吧!

    到这里,host driver就开始正式的访问那些之前就注册到全局链表devices里面camera了(icd)。分析之前,可以想象一下,它会做什么事情呢?我觉得怎么也得将扳级相关的文件里面用soc_camera_linkboard_info对应的i2c控制部分给处理了吧,也就是找到对应用i2c控制的sensor driver。还有就是将该icd加入到host管理数据结构里啦,然后创建一个导出给应用层的设备节点,让对该设备节点的操作能够最终转到host或者sensor driver提供的ops里。下面正式开始分析吧,验证下对不对。这里直接将说明插入到代码中:

    /* Called during host-driver probe */
    static int soc_camera_probe(struct soc_camera_host *ici,
    			    struct soc_camera_device *icd)
    {
    	struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);//拿到扳级相关的文件里面用soc_camera_link信息,和我们预想一样  果然是要开始处理它了
    	struct soc_camera_host_desc *shd = &sdesc->host_desc;//同上
    	struct device *control = NULL;
    	int ret;
    
    	dev_info(icd->pdev, "Probing %s
    ", dev_name(icd->pdev));
    
    	/*
    	 * Currently the subdev with the largest number of controls (13) is
    	 * ov6550. So let's pick 16 as a hint for the control handler. Note
    	 * that this is a hint only: too large and you waste some memory, too
    	 * small and there is a (very) small performance hit when looking up
    	 * controls in the internal hash.
    	 */
    	ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);//v4l2_ctrl是v4l2框架提供的一种ioctl处理机制,我们只需要知道它这里主要是服务于sensor驱动,让sensor驱动里的一些ioctl控制能够注入到导出给应用层ioctl的里面,这样,应用层调用相关的ioctl,最终会有一部分进入到sensor驱动里面
    	if (ret < 0)
    		return ret;
    
    	/* Must have icd->vdev before registering the device */
    	ret = video_dev_create(icd);//这也是v4l2框架提供的api,用于创建一个导出给应用层操作的设备文件(这里仅仅是初始化对应的数据结构),一个camera设备对应一个设备文件导出,这个很容易理解吧
    	if (ret < 0)
    		goto evdc;
    
    	/*
    	 * ..._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 also during client probing.
    	 */
    
    	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
    	if (shd->board_info) {//我只分析这种情况,后面会详细分析soc_camera_i2c_init
    		ret = soc_camera_i2c_init(icd, sdesc);
    		if (ret < 0 && ret != -EPROBE_DEFER)
    			goto eadd;
    	} else if (!shd->add_device || !shd->del_device) {
    		ret = -EINVAL;
    		goto eadd;
    	} else {
    		mutex_lock(&ici->clk_lock);
    		ret = ici->ops->clock_start(ici);
    		mutex_unlock(&ici->clk_lock);
    		if (ret < 0)
    			goto eadd;
    
    		if (shd->module_name)
    			ret = request_module(shd->module_name);
    
    		ret = shd->add_device(icd);
    		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)) {
    			shd->del_device(icd);
    			ret = -ENODEV;
    			goto enodrv;
    		}
    	}
    
    	mutex_lock(&ici->host_lock);
    	ret = soc_camera_probe_finish(icd);//一切都准备好后,就可以正式添加video_device到系统来导出给应用层使用了,后面也会对该函数进一步分析
    	mutex_unlock(&ici->host_lock);
    	if (ret < 0)
    		goto efinish;
    
    	return 0;
    
    efinish:
    	if (shd->board_info) {
    		soc_camera_i2c_free(icd);
    	} else {
    		shd->del_device(icd);
    		module_put(control->driver->owner);
    enodrv:
    eadddev:
    		mutex_lock(&ici->clk_lock);
    		ici->ops->clock_stop(ici);
    		mutex_unlock(&ici->clk_lock);
    	}
    eadd:
    	video_device_release(icd->vdev);
    	icd->vdev = NULL;
    	if (icd->vdev) {
    		video_device_release(icd->vdev);
    		icd->vdev = NULL;
    	}
    evdc:
    	v4l2_ctrl_handler_free(&icd->ctrl_handler);
    	return ret;
    }
    

    下面重点分析soc_camera_i2c_init,它才是处理board_info的函数。还是直接将说明插入到代码中:

    static int soc_camera_i2c_init(struct soc_camera_device *icd,
    			       struct soc_camera_desc *sdesc)
    {
    	struct soc_camera_subdev_desc *ssdd;
    	struct i2c_client *client;
    	struct soc_camera_host *ici;
    	struct soc_camera_host_desc *shd = &sdesc->host_desc;
    	struct i2c_adapter *adap;
    	struct v4l2_subdev *subdev;
    	char clk_name[V4L2_SUBDEV_NAME_SIZE];
    	int ret;
    
    	/* First find out how we link the main client */
    	if (icd->sasc) {
    		/* Async non-OF probing handled by the subdevice list */
    		return -EPROBE_DEFER;
    	}
    
    	ici = to_soc_camera_host(icd->parent);//前面说了,icd的parent当然就是host对应的device,而该device里面的driver data部分早就被设置为v4l2_device了(详情请看v4l2_device_register),前面也说了v4l2_device在v4l2框架里就代表着老大,i2c sensor对应的就是v4l2_subdev,代表着小弟。to_soc_camera_host里面通过container_of从v4l2_device找到ici
    	adap = i2c_get_adapter(shd->i2c_adapter_id);//通过扳级相关文件里指定的i2c_adapter_id寻找该i2c host对应的数据结构,i2c驱动框架就不多说了,原理也差不多
    	if (!adap) {
    		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?
    ",
    			shd->i2c_adapter_id);
    		return -ENODEV;
    	}
    
    	ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
    	if (!ssdd) {
    		ret = -ENOMEM;
    		goto ealloc;
    	}
    
    	memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
    	/*
    	 * In synchronous case we request regulators ourselves in
    	 * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
    	 * to allocate them again.
    	 */
    	ssdd->sd_pdata.num_regulators = 0;
    	ssdd->sd_pdata.regulators = NULL;
    	shd->board_info->platform_data = ssdd;
    
    	snprintf(clk_name, sizeof(clk_name), "%d-%04x",
    		 shd->i2c_adapter_id, shd->board_info->addr);
    
    	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);//注册一个v4l2_clk,i2c sensor驱动里会请求这个clk,操作集里的函数最终将定向到ici的操作集合里的函数中,也就是camera host实现的操作集中去
    	if (IS_ERR(icd->clk)) {
    		ret = PTR_ERR(icd->clk);
    		goto eclkreg;
    	}
    
    	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
    				shd->board_info, NULL);//这里面就是处理i2c sensor了,后面会详细分析
    	if (!subdev) {
    		ret = -ENODEV;
    		goto ei2cnd;
    	}
    
    	client = v4l2_get_subdevdata(subdev);
    
    	/* Use to_i2c_client(dev) to recover the i2c client */
    	icd->control = &client->dev;
    
    	return 0;
    ei2cnd:
    	v4l2_clk_unregister(icd->clk);
    	icd->clk = NULL;
    eclkreg:
    	kfree(ssdd);
    ealloc:
    	i2c_put_adapter(adap);
    	return ret;
    }
    

    未完,待续!
    2015年6月

  • 相关阅读:
    QMainWindow透明背景
    在Xcode中产看QString的数据
    使用QtConcurrent在线程池中执行网络操作
    QThread和QObject的调用方法总结
    策略模式 C++实现
    【转】单元测试要做多细?
    Mac系统添加QQ邮箱帐号提示无法验证
    Qt半透明对话框
    SVD的几何意义,以及在去噪,推荐系统中的应用
    BP神经网络原理及python实现
  • 原文地址:https://www.cnblogs.com/rongpmcu/p/7662741.html
Copyright © 2011-2022 走看看