V4L2 soc

本文介绍了soc-camera子系统的架构和目的,旨在简化camera驱动的移植工作。通过将camera驱动分为camerahost端和cameradevice端,同一sensor驱动可以在不同host上复用。文章还详细介绍了camerahost API,并探讨了soc-camera子系统在open、mmap、ioctl等操作中的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自【http://blog.youkuaiyun.com/kickxxx/article/details/8330401

soc-camera的作者之所以引入这个子系统,我想一个主要目的就是把camera驱动划分为camera host端,camera device端,这样同一个camera sensor驱动就可以方便的移植到多个camera host下,而无须做较多的改动。


本译文取自kernel文档:Documentation/video4linux/soc-camera.txt


术语

在阅读本文档前先明确几个术语:

  • camera / camera device / camera sensor - 是一个video-camera sensor chip,可以连接到各种系统和接口,典型的使用i2c来控制和配置设备,使用并行或者串行总线来传输数据。
  • camera host - 一个接口,camera连接到它上面。存在于SoCs之上的特定的接口,比如PXA27X PXA3xx, SuperH, AVR32, i.MX27, i.MX31等等。我觉得i.MX51以及s5pv210物理上和这些host没有什么区别,只是二者的开发人员出于某种考虑而没有使用这个子系统实现。
  • camera host bus - camera host和camera之间的连接,可以是串行的或者并行的,保存着数据线和控制线,比如clock,水平和垂直同步信号。

soc-camera subsystem的目的

soc-camera 子系统在camera host驱动和camera sensor驱动间提供了统一的接口。soc camera子系统向上提供了V4L2接口,当前仅支持open, mmap, ioctl, mmap,不支持read

这个子系统连接SoC video capture接口和CMOS camera sensor chips,使得sensor驱动可以在其他的hosts驱动上复用。子系统在设计时也考虑到了多个camera host接口的存在,以及每个接口存在多个camera sensor,尽管在大部分情况下仅有一个camera sensor。

现存的驱动

在2.6.27-rc4中,主线内核有两个host 驱动:PXA27x的pxa_camera.c和SuperH SoCs的sh_mobile_ceu_camera.c,有四个sensor驱动:mt9m001.c,mt9v002.c以及一个通用的soc_camera_platform.c驱动。这个列表可能不是最新的,所以在你的源码树中查看,可能有更多的例子存在。

实际开发中,新的sensor驱动可以参照mt9m001.c和mt9v002.c这两个sensor驱动框架,根据项目所用的sensor稍加修改即可。

Camera host API

一个host camera driver使用soc_camera_host_register(struct soc_camera_host *)函数来注册。host object可以如下初始化

[cpp]  view plain copy
  1. static struct soc_camera_host pxa_soc_camera_host = {  
  2.     .drv_name   = PXA_CAM_DRV_NAME,  
  3.     .ops        = &pxa_soc_camera_host_ops,  
  4. };   

所有的方法都是通过struct soc_camera_host_ops

[cpp]  view plain copy
  1. static struct soc_camera_host_ops pxa_soc_camera_host_ops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .add        = pxa_camera_add_device,  
  4.     .remove     = pxa_camera_remove_device,  
  5.     .suspend    = pxa_camera_suspend,  
  6.     .resume     = pxa_camera_resume,  
  7.     .set_fmt_cap    = pxa_camera_set_fmt_cap,  
  8.     .try_fmt_cap    = pxa_camera_try_fmt_cap,  
  9.     .init_videobuf  = pxa_camera_init_videobuf,  
  10.     .reqbufs    = pxa_camera_reqbufs,  
  11.     .poll       = pxa_camera_poll,  
  12.     .querycap   = pxa_camera_querycap,  
  13.     .try_bus_param  = pxa_camera_try_bus_param,  
  14.     .set_bus_param  = pxa_camera_set_bus_param,  
  15. };  

.add是把camera sensor接入host;.remove方法从host卸载camera sensor时调用,除了执行host内部的初始化工作外,还应该调用camera sensor的.init和.release方法。(.add方法仅仅在soc_camera_open中被调用,也就是说当应用层第一个打开一个/dev/videoX设备节点时,此时进行camera host以及camera sensor的初始化是合理的)

.suspend和.resume方法实现host的电源管理功能, 他们要负责调用相应的sensor 方法。

.try_bus_param和.set_bus_param用来协商host和sensor间的物理连接参数。

.init_videobuf是当一个video-device被打开时调用,video-buffer管理的实现是完全依赖于特定的host

其余部分被V4L2的相应部分调用。

Camera API

Sensor drivers可以使用struct soc_camera_link,典型的由platform提供,用来代表sensor连接到的host总线,这个结构还提供了.power和.reset接口。本节所描述的代码和当前代码已经发生了很大的变化,所以暂时略过本节。

VIDIOC_S_CROP和VIDIOC_S_FMT调用的行为

VIDIOC_S_CROP:设置sensor窗口的位置和尺寸,单位是sensor的像素。改变sensor窗口尺寸时维持缩放因子没变,因此用户窗口尺寸也相应的变化了。

VIDIOC_S_FMT:设置用户窗口。通过修改缩放因子,尽可能的保持之前设置的窗口尺寸。如果sensor窗口无法保持不变,那么也可以修改。

soc-camera中有两个地方,可以进行缩放和剪切:camera sensor驱动,和camera host驱动。User ioctl首先发送到host dirver,然后再向下发送给camera driver。在camera sensor驱动执行缩放和剪切可能更有效,因为会节约camera总线带宽以及最大帧率(如果放大还成立吗,对于AD video芯片来说,缩放只能在host driver来做)。然而,如果camera sensor驱动无法实现要求的参数,那么host 驱动可以决定用它自己的缩放和剪切功能来满足用户需求。

Camera sensor驱动soc-camera core以及host驱动交互,完全是功能上的,不涉及任何数据传输。因而所有的camera sensor驱动应该用当前输出格式响应.g_fmt请求。这对于正确配置camera bus是必要的。s_fmt() 和try_fmt也不得不实现。sensor窗口和缩放因子也应该在camera sensor驱动内维护。按照V4L2 API,所有的capture驱动应该支持VIDIOC_CROPCAP ioctl,因此我们依赖于camera drivers实现.cropcap()。如果camera sensor驱动不支持cropping,那么可以选择不支持.s_crop,但是必须在camera host驱动实现cropping支持,起码g_crop方法必须支持。

用户设置的几何尺寸保存在struct soc_camera_device的.user_width和.user_height成员中,以供soc_camera core和host drivers使用。core在成功调用s_fmt后会修改这些成员,但是如果是在其他地方修改了他们,比如s_crop,那么host driver要负责更型他们。

[cpp]  view plain copy
  1. static LIST_HEAD(hosts)  
所有的host都在hosts指向的链表上

[cpp]  view plain copy
  1. static LIST_HEAD(devices);  
所有soc camera device都挂在devices指向的链表上。

系统内可能有多个soc_camera_host,每个soc_camera_host可以对应1 ~ n个soc_camera_device。每个soc_camera_device通过soc_camera_video_start函数创建设备节点/dev/videoX

soc_camera_host对应着系统camera处理模块,尽管理论上可以有多个camera host,但是大部分系统仅有一个camera host

在soc_camera_host_register中调用v4l2_device_register为这个soc_camera_host注册一个v4l2_device设备。

在soc_camera_probe中调用soc_camera_init_i2c,为soc_camera_device注册一个v4l2_subdev,我们从soc_camera_probe代码可以看出,只有i2C的设备可以使用这种方式,对于非I2C设备来说,需要soc_camera_link提供add_device来增加子设备。


soc_camera_host, soc_camera_device,v4l2_device,v4l2_subdev关系如下:

  • 理论上系统内可以有多个soc_camera_host,物理上soc_camera_host就是系统的camera处理模块驱动
  • 一个soc_camera_host可以对应多个soc_camera_device,物理上soc_camera_device是一个camera接口,每个soc_camera_host对应一个v4l2_dev
  • 每个soc_camera_device,系统会为他们创建设备节点/dev/videoX。
  • 每个soc_camera_device有多个v4l2_subdev,物理上v4l2_subdev可以是sensor,video AD芯片
  • v4l2_subdev可以通过i2c挂接到v4l2_device,也可以通过soc_camera_link提供的add_device来增加,这依赖于sensor和video AD芯片挂接到MCU camera接口的方式。



[cpp]  view plain copy
  1. struct soc_camera_device {  
  2.     struct list_head list;  
  3.     struct device dev;  
  4.     struct device *pdev;        /* Platform device */  
  5.     s32 user_width;  
  6.     s32 user_height;  
  7.     enum v4l2_colorspace colorspace;  
  8.     unsigned char iface;        /* Host number */  
  9.     unsigned char devnum;       /* Device number per host */  
  10.     struct soc_camera_sense *sense; /* See comment in struct definition */  
  11.     struct soc_camera_ops *ops;  
  12.     struct video_device *vdev;  
  13.     const struct soc_camera_format_xlate *current_fmt;  
  14.     struct soc_camera_format_xlate *user_formats;  
  15.     int num_user_formats;  
  16.     enum v4l2_field field;      /* Preserve field over close() */  
  17.     void *host_priv;        /* Per-device host private data */  
  18.     /* soc_camera.c private count. Only accessed with .video_lock held */  
  19.     int use_count;  
  20.     struct mutex video_lock;    /* Protects device data */  
  21.     struct file *streamer;      /* stream owner */  
  22.     struct videobuf_queue vb_vidq;  
  23. };  

每一个soc_camera_device都会对应一个/dev/videoX设备节点,除非在soc_camera_probe时失败。

@list:soc_came_device通过这个成员连接到全局devices链表上

@pdev:每一个soc_camera_device,都会有一个platform device,pdev就是这个platform device结构的dev成员

@user_width, @user_height:这个camera的缺省width和height

@iface:camera bus id,也是host id,一个host可以对应多个soc_camera_device

@devnum:device number,每个soc_camera_device都会自动分配一个device number

@ops:操作集合,sensor,video AD(也许还有host)驱动要实现这个接口

@use_count:soc camera使用计数,每次打开这个设备加1,关闭则减1


soc_camera_host

[cpp]  view plain copy
  1. struct soc_camera_host {  
  2.     struct v4l2_device v4l2_dev;  
  3.     struct list_head list;  
  4.     unsigned char nr;               /* Host number */  
  5.     void *priv;  
  6.     const char *drv_name;  
  7.     struct soc_camera_host_ops *ops;  
  8. }  

@v4l2_dev:每个host都是一个v4l2_device

@list:soc_camera_host通过这个成员链接到全局hosts链表上

@nr:host number,每个host接口对应一个host

@priv:一般存放平台特定的camera参数,比如irq,DMA


[cpp]  view plain copy
  1. struct soc_camera_ops {  
  2.     int (*suspend)(struct soc_camera_device *, pm_message_t state);  
  3.     int (*resume)(struct soc_camera_device *);  
  4.     unsigned long (*query_bus_param)(struct soc_camera_device *);  
  5.     int (*set_bus_param)(struct soc_camera_device *, unsigned long);  
  6.     int (*enum_input)(struct soc_camera_device *, struct v4l2_input *);  
  7.     const struct v4l2_queryctrl *controls;  
  8.     int num_controls;  
  9. };    
这个是由sensor,video AD或者host实现的回调函数集。

@suspend,@resume:系统休眠唤醒时的回调函数,如果不考虑电源管理,可以不实现这两个函数

@query_bus_param:获取sensor和host之间的总线信息,比如HSYNC VSYNC极性,数据总线宽度,数据线极性等

@set_bus_param:设置sensor和host之间的总线参数。

@enum_input:枚举给定的input number,上层通过@v4l2_input->index指定要查看哪个input,一般可以不实现。

@controls:controls

@num_controls:@controls数目

soc_camera.c

[cpp]  view plain copy
  1. 1455 static struct platform_driver __refdata soc_camera_pdrv = {  
  2. 1456     .remove  = __devexit_p(soc_camera_pdrv_remove),  
  3. 1457     .driver  = {  
  4. 1458         .name   = "soc-camera-pdrv",  
  5. 1459         .owner  = THIS_MODULE,  
  6. 1460     },  
  7. 1461 };  
  8. 1462   
  9. 1463 static int __init soc_camera_init(void)  
  10. 1464 {  
  11. 1465     int ret = bus_register(&soc_camera_bus_type);  
  12. 1466     if (ret)  
  13. 1467         return ret;  
  14. 1468     ret = driver_register(&ic_drv);  
  15. 1469     if (ret)  
  16. 1470         goto edrvr;  
  17. 1471   
  18. 1472     ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);  
  19. 1473     if (ret)  
  20. 1474         goto epdr;  
  21. 1475   
  22. 1476     return 0;  
  23. 1477   
  24. 1478 epdr:  
  25. 1479     driver_unregister(&ic_drv);  
  26. 1480 edrvr:  
  27. 1481     bus_unregister(&soc_camera_bus_type);  
  28. 1482     return ret;  
  29. 1483 }  
1472 platform_driver_probe和platform_driver_register的区别:前者功能上和platform_driver_register是一样的,但是在内核启动完成后,这个函数就不能再执行了,这样可以释放函数soc_camera_pdrv_probe所占的空间。

soc_camera_pdrv_probe会probe系统内名称为"soc-camera-pdrv"的平台设备,系统内有几个这样的平台设备,那么就会创建几个soc_camera_device。这些平台设备可如下定义:

[cpp]  view plain copy
  1. struct platform_device your_mach_cameras[] = {  
  2.     {  
  3.         .name = "soc-camera-pdrv",  
  4.         .id = 0,  
  5.         .dev = {  
  6.             .platform_data = adv7180_link,  
  7.         },  
  8.     }, {  
  9.         .name = "soc-camera-pdrv",  
  10.         .id = 1,  
  11.         .dev = {  
  12.             .platform_data = tw9912_link,  
  13.         },  
  14.     }  
  15. };  

注意,这里假定系统的camera处理模块,接了两个camera sensor, adv7180_link和tw9912_link

[cpp]  view plain copy
  1. static struct i2c_board_info decoder_i2c_adv7180 = {  
  2.     I2C_BOARD_INFO("adv7180", (0x42 >> 1)),   
  3. };    
  4. struct soc_camera_link adv7180_link = {  
  5.     .bus_id     = 0,  
  6.     .board_info = &decoder_i2c_adv7180,  
  7.     .i2c_adapter_id = 0,  
  8. };    
soc_camera_link主要用来定义i2c地址,如果sensor不是通过i2c连接到host上,那么要定义add_device和del_device函数

1465 注册一条新的总线soc-camera,这样在scan_add_host中调用device_register时,就会把这个设备挂到这个总线上。


[cpp]  view plain copy
  1. 1135 struct bus_type soc_camera_bus_type = {  
  2. 1136     .name       = "soc-camera",  
  3. 1137     .probe      = soc_camera_probe,  
  4. 1138     .remove     = soc_camera_remove,  
  5. 1139     .suspend    = soc_camera_suspend,  
  6. 1140     .resume     = soc_camera_resume,  
  7. 1141 };  
  8. 1142 EXPORT_SYMBOL_GPL(soc_camera_bus_type);  

当一个soc-camera-device设备通过device_register注册设备时,就会调用soc_camera_probe函数

[cpp]  view plain copy
  1. 1402 static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)  
  2. 1403 {  
  3. 1404     struct soc_camera_link *icl = pdev->dev.platform_data;  
  4. 1405     struct soc_camera_device *icd;  
  5. 1406     int ret;  
  6. 1407   
  7. 1408     if (!icl)  
  8. 1409         return -EINVAL;  
  9. 1410   
  10. 1411     icd = kzalloc(sizeof(*icd), GFP_KERNEL);  
  11. 1412     if (!icd)  
  12. 1413         return -ENOMEM;  
  13. 1414           
  14. 1415     icd->iface = icl->bus_id;  
  15. 1416     icd->pdev = &pdev->dev;  
  16. 1417     platform_set_drvdata(pdev, icd);  
  17. 1418       
  18. 1419     ret = soc_camera_device_register(icd);  
  19. 1420     if (ret < 0)  
  20. 1421         goto escdevreg;  
  21. 1422           
  22. 1423     soc_camera_device_init(&icd->dev, icl);  
  23. 1424       
  24. 1425     icd->user_width     = DEFAULT_WIDTH;  
  25. 1426     icd->user_height    = DEFAULT_HEIGHT;  
  26. 1427       
  27. 1428     return 0;  
  28. 1429       
  29. 1430 escdevreg:  
  30. 1431     kfree(icd);  
  31. 1432       
  32. 1433     return ret;  
  33. 1434 }    

查找匹配名为soc-camera-pdrv的platform device时,调用该函数。

1419 调用soc_camera_device_register,把这个soc_camera_device加到全局camera device链表@devices上,并且为它分配设备号,做一些必要的初始化

1423 设置soc_came_device对应device的bus为soc_camera_bus_type,这样当我们注册设备时,就会调用soc_camera_probe


[cpp]  view plain copy
  1. 1374 /* 
  2. 1375  * Called from soc_camera_probe() above (with .video_lock held???) 
  3. 1376  */  
  4. 1377 static int soc_camera_video_start(struct soc_camera_device *icd)  
  5. 1378 {  
  6. 1379     struct device_type *type = icd->vdev->dev.type;  
  7. 1380     int ret;  
  8. 1381   
  9. 1382     if (!icd->dev.parent)  
  10. 1383         return -ENODEV;  
  11. 1384   
  12. 1385     if (!icd->ops ||  
  13. 1386         !icd->ops->query_bus_param ||  
  14. 1387         !icd->ops->set_bus_param)  
  15. 1388         return -EINVAL;  
  16. 1389   
  17. 1390     ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);  
  18. 1391     if (ret < 0) {  
  19. 1392         dev_err(&icd->dev, "video_register_device failed: %d\n", ret);  
  20. 1393         return ret;  
  21. 1394     }  
  22. 1395   
  23. 1396     /* Restore device type, possibly set by the subdevice driver */  
  24. 1397     icd->vdev->dev.type = type;  
  25. 1398   
  26. 1399     return 0;  
  27. 1400 }  

在当前的上下文,soc_camera_video_start的调用路径如下

soc_camera_host_register ==> scan_add_host ==> device_register ==> bus_probe_device ==> soc_camera_bus_type.probe ==> soc_camera_video_start

1390 我们可以看出,系统为每一个soc-camera-device创建了一个video device设备节点


[cpp]  view plain copy
  1. 1352 static int video_dev_create(struct soc_camera_device *icd)  
  2. 1353 {    
  3. 1354     struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);  
  4. 1355     struct video_device *vdev = video_device_alloc();  
  5. 1356   
  6. 1357     if (!vdev)  
  7. 1358         return -ENOMEM;  
  8. 1359      
  9. 1360     strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));  
  10. 1361   
  11. 1362     vdev->parent        = &icd->dev;  
  12. 1363     vdev->current_norm  = V4L2_STD_UNKNOWN;  
  13. 1364     vdev->fops      = &soc_camera_fops;  
  14. 1365     vdev->ioctl_ops     = &soc_camera_ioctl_ops;  
  15. 1366     vdev->release       = video_device_release;  
  16. 1367     vdev->tvnorms       = V4L2_STD_UNKNOWN;  
  17. 1368   
  18. 1369     icd->vdev = vdev;  
  19. 1370   
  20. 1371     return 0;  
  21. 1372 }    

当前的上下文,video_dev_create的调用路径如下

soc_camera_host_register ==> scan_add_host ==> device_register ==> bus_probe_device ==> soc_camera_bus_type.probe ==> soc_camera_video_start

这里面设置了video_device的两个非常重要的参数:soc_camera_ioctl_ops和soc_camera_fops,当user space打开video device后,所有可执行的操作,都是通过这两个入口进行的,下面是他们的定义。

[cpp]  view plain copy
  1. 549 static struct v4l2_file_operations soc_camera_fops = {  
  2. 550     .owner      = THIS_MODULE,  
  3. 551     .open       = soc_camera_open,  
  4. 552     .release    = soc_camera_close,  
  5. 553     .unlocked_ioctl = video_ioctl2,  
  6. 554     .read       = soc_camera_read,  
  7. 555     .mmap       = soc_camera_mmap,  
  8. 556     .poll       = soc_camera_poll,  
  9. 557 };  

[cpp]  view plain copy
  1. 1321 static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {  
  2. 1322     .vidioc_querycap     = soc_camera_querycap,  
  3. 1323     .vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,  
  4. 1324     .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,  
  5. 1325     .vidioc_s_fmt_vid_cap    = soc_camera_s_fmt_vid_cap,  
  6. 1326     .vidioc_enum_input   = soc_camera_enum_input,  
  7. 1327     .vidioc_g_input      = soc_camera_g_input,  
  8. 1328     .vidioc_s_input      = soc_camera_s_input,  
  9. 1329     .vidioc_s_std        = soc_camera_s_std,  
  10. 1330     .vidioc_reqbufs      = soc_camera_reqbufs,  
  11. 1331     .vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,  
  12. 1332     .vidioc_querybuf     = soc_camera_querybuf,  
  13. 1333     .vidioc_qbuf         = soc_camera_qbuf,  
  14. 1334     .vidioc_dqbuf        = soc_camera_dqbuf,  
  15. 1335     .vidioc_streamon     = soc_camera_streamon,  
  16. 1336     .vidioc_streamoff    = soc_camera_streamoff,  
  17. 1337     .vidioc_queryctrl    = soc_camera_queryctrl,  
  18. 1338     .vidioc_g_ctrl       = soc_camera_g_ctrl,  
  19. 1339     .vidioc_s_ctrl       = soc_camera_s_ctrl,  
  20. 1340     .vidioc_cropcap      = soc_camera_cropcap,  
  21. 1341     .vidioc_g_crop       = soc_camera_g_crop,  
  22. 1342     .vidioc_s_crop       = soc_camera_s_crop,  
  23. 1343     .vidioc_g_parm       = soc_camera_g_parm,  
  24. 1344     .vidioc_s_parm       = soc_camera_s_parm,  
  25. 1345     .vidioc_g_chip_ident     = soc_camera_g_chip_ident,  
  26. 1346 #ifdef CONFIG_VIDEO_ADV_DEBUG  
  27. 1347     .vidioc_g_register   = soc_camera_g_register,  
  28. 1348     .vidioc_s_register   = soc_camera_s_register,  
  29. 1349 #endif  
  30. 1350 };  

soc_camera_ops 不支持read操作,因此如果使用了soc camera子系统,那么应用层就无法再使用read操作获取camera 数据,而只能选择使用mmap方式。不支持read操作没什么关系,大部分camera操作都是使用mmap方式进行的。samsung的s5pv210不支持read操作,而freescale mxc系列则支持read操作获取camera数据。

soc_camera_ioctl_ops也仅仅支持了v4l2_ioctl_ops的一个子集,这就意味着应用程序的作者需要考虑ioctl可能没有被支持。


[cpp]  view plain copy
  1. 1281 /* Image capture device */  
  2. 1282 static int soc_camera_device_register(struct soc_camera_device *icd)  
  3. 1283 {  
  4. 1284     struct soc_camera_device *ix;  
  5. 1285     int num = -1, i;  
  6. 1286   
  7. 1287     for (i = 0; i < 256 && num < 0; i++) {  
  8. 1288         num = i;  
  9. 1289         /* Check if this index is available on this interface */  
  10. 1290         list_for_each_entry(ix, &devices, list) {  
  11. 1291             if (ix->iface == icd->iface && ix->devnum == i) {  
  12. 1292                 num = -1;  
  13. 1293                 break;  
  14. 1294             }  
  15. 1295         }  
  16. 1296     }  
  17. 1297   
  18. 1298     if (num < 0)  
  19. 1299         /* 
  20. 1300          * ok, we have 256 cameras on this host... 
  21. 1301          * man, stay reasonable... 
  22. 1302          */  
  23. 1303         return -ENOMEM;  
  24. 1304   
  25. 1305     icd->devnum     = num;  
  26. 1306     icd->use_count      = 0;  
  27. 1307     icd->host_priv      = NULL;  
  28. 1308     mutex_init(&icd->video_lock);  
  29. 1309   
  30. 1310     list_add_tail(&icd->list, &devices);  
  31. 1311   
  32. 1312     return 0;  
  33. 1313 }   

把给定的@icd加到全局soc camera device列表中

1290~1294 @devices是一个全局soc camera device列表,这段代码相当拗口,注意1293行是break 1290这个循环

[cpp]  view plain copy
  1. 1194 int soc_camera_host_register(struct soc_camera_host *ici)  
  2. 1195 {  
  3. 1196     struct soc_camera_host *ix;  
  4. 1197     int ret;  
  5. 1198   
  6. 1199     if (!ici || !ici->ops ||  
  7. 1200         !ici->ops->try_fmt ||  
  8. 1201         !ici->ops->set_fmt ||  
  9. 1202         !ici->ops->set_bus_param ||  
  10. 1203         !ici->ops->querycap ||  
  11. 1204         !ici->ops->init_videobuf ||  
  12. 1205         !ici->ops->reqbufs ||  
  13. 1206         !ici->ops->add ||  
  14. 1207         !ici->ops->remove ||  
  15. 1208         !ici->ops->poll ||  
  16. 1209         !ici->v4l2_dev.dev)  
  17. 1210         return -EINVAL;  
  18. 1211   
  19. 1212     if (!ici->ops->set_crop)  
  20. 1213         ici->ops->set_crop = default_s_crop;  
  21. 1214     if (!ici->ops->get_crop)  
  22. 1215         ici->ops->get_crop = default_g_crop;  
  23. 1216     if (!ici->ops->cropcap)  
  24. 1217         ici->ops->cropcap = default_cropcap;  
  25. 1218     if (!ici->ops->set_parm)  
  26. 1219         ici->ops->set_parm = default_s_parm;  
  27. 1220     if (!ici->ops->get_parm)  
  28. 1221         ici->ops->get_parm = default_g_parm;  
  29. 1222   
  30. 1223     mutex_lock(&list_lock);  
  31. 1224     list_for_each_entry(ix, &hosts, list) {  
  32. 1225         if (ix->nr == ici->nr) {  
  33. 1226             ret = -EBUSY;  
  34. 1227             goto edevreg;  
  35. 1228         }  
  36. 1229     }  
  37. 1230   
  38. 1231     ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);  
  39. 1232     if (ret < 0)  
  40. 1233         goto edevreg;  
  41. 1234   
  42. 1235     list_add_tail(&ici->list, &hosts);  
  43. 1236     mutex_unlock(&list_lock);  
  44. 1237   
  45. 1238     scan_add_host(ici);  
  46. 1239   
  47. 1240     return 0;  
  48. 1241   
  49. 1242 edevreg:  
  50. 1243     mutex_unlock(&list_lock);  
  51. 1244     return ret;  
  52. 1245 }  
  53. 1246 EXPORT_SYMBOL(soc_camera_host_register);  

1231 每个camera host对应一个v4l2 device(注意不是video device),host上device对应的才是video device

1235 @host是一个全局camera host 链表

1238 scan_add_host 关联系统内属于这个host的video device


[cpp]  view plain copy
  1. 1135 struct bus_type soc_camera_bus_type = {  
  2. 1136     .name       = "soc-camera",  
  3. 1137     .probe      = soc_camera_probe,  
  4. 1138     .remove     = soc_camera_remove,  
  5. 1139     .suspend    = soc_camera_suspend,  
  6. 1140     .resume     = soc_camera_resume,  
  7. 1141 };  
  8. 1142 EXPORT_SYMBOL_GPL(soc_camera_bus_type);  

soc camera总线代码,当调用device_register注册一个新设备时,会调用probe函数

[cpp]  view plain copy
  1.  947 static int soc_camera_probe(struct device *dev)  
  2.  948 {  
  3.  949     struct soc_camera_device *icd = to_soc_camera_dev(dev);  
  4.  950     struct soc_camera_host *ici = to_soc_camera_host(dev->parent);  
  5.  951     struct soc_camera_link *icl = to_soc_camera_link(icd);  
  6.  952     struct device *control = NULL;  
  7.  953     struct v4l2_subdev *sd;  
  8.  954     struct v4l2_mbus_framefmt mf;  
  9.  955     int ret;  
  10.  956   
  11.  957     dev_info(dev, "Probing %s\n", dev_name(dev));  
  12.  958   
  13.  959     ret = regulator_bulk_get(icd->pdev, icl->num_regulators,  
  14.  960                  icl->regulators);  
  15.  961     if (ret < 0)  
  16.  962         goto ereg;  
  17.  963   
  18.  964     ret = soc_camera_power_set(icd, icl, 1);  
  19.  965     if (ret < 0)  
  20.  966         goto epower;  
  21.  967   
  22.  968     /* The camera could have been already on, try to reset */  
  23.  969     if (icl->reset)  
  24.  970         icl->reset(icd->pdev);  
  25.  971   
  26.  972     ret = ici->ops->add(icd);  
  27.  973     if (ret < 0)  
  28.  974         goto eadd;  
  29.  975   
  30.  976     /* Must have icd->vdev before registering the device */  
  31.  977     ret = video_dev_create(icd);  
  32.  978     if (ret < 0)  
  33.  979         goto evdc;  
  34.  980   
  35.  981     /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */  
  36.  982     if (icl->board_info) {  
  37.  983         ret = soc_camera_init_i2c(icd, icl);  
  38.  984         if (ret < 0) {  
  39.  985             goto eadddev;  
  40.  986         }  
  41.  987     } else if (!icl->add_device || !icl->del_device) {  
  42.  988         ret = -EINVAL;  
  43.  989         goto eadddev;  
  44.  990     } else {  
  45.  991         if (icl->module_name)  
  46.  992             ret = request_module(icl->module_name);  
  47.  993   
  48.  994         ret = icl->add_device(icl, &icd->dev);  
  49.  995         if (ret < 0)  
  50.  996             goto eadddev;  
  51.  997   
  52.  998         /* 
  53.  999          * FIXME: this is racy, have to use driver-binding notification, 
  54. 1000          * when it is available 
  55. 1001          */  
  56. 1002         control = to_soc_camera_control(icd);  
  57. 1003         if (!control || !control->driver || !dev_get_drvdata(control) ||  
  58. 1004             !try_module_get(control->driver->owner)) {  
  59. 1005             icl->del_device(icl);  
  60. 1006             goto enodrv;  
  61. 1007         }  
  62. 1008     }  
  63. 1009   
  64. 1010     /* At this point client .probe() should have run already */  
  65. 1011     ret = soc_camera_init_user_formats(icd);  
  66. 1012     if (ret < 0)  
  67. 1013         goto eiufmt;  
  68. 1014   
  69. 1015     icd->field = V4L2_FIELD_ANY;  
  70. 1016   
  71. 1017     icd->vdev->lock = &icd->video_lock;  
  72. 1018   
  73. 1019     /* 
  74. 1020      * ..._video_start() will create a device node, video_register_device() 
  75. 1021      * itself is protected against concurrent open() calls, but we also have 
  76. 1022      * to protect our data. 
  77. 1023      */  
  78. 1024     mutex_lock(&icd->video_lock);  
  79. 1025   
  80. 1026     ret = soc_camera_video_start(icd);  
  81. 1027     if (ret < 0)  
  82. 1028         goto evidstart;  
  83. 1029   
  84. 1030     /* Try to improve our guess of a reasonable window format */  
  85. 1031     sd = soc_camera_to_subdev(icd);  
  86. 1032     if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {  
  87. 1033         icd->user_width     = mf.width;  
  88. 1034         icd->user_height    = mf.height;  
  89. 1035         icd->colorspace     = mf.colorspace;  
  90. 1036         icd->field      = mf.field;  
  91. 1037     }  
  92. 1038   
  93. 1039     /* Do we have to sysfs_remove_link() before device_unregister()? */  
  94. 1040     if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,  
  95. 1041                   "control"))  
  96. 1042         dev_warn(&icd->dev, "Failed creating the control symlink\n");  
  97. 1043   
  98. 1044     ici->ops->remove(icd);  
  99. 1045   
  100. 1046     soc_camera_power_set(icd, icl, 0);  
  101. 1047   
  102. 1048     mutex_unlock(&icd->video_lock);  
  103. 1049   
  104. 1050     return 0;  
  105. 1051   
  106. 1052 evidstart:  
  107. 1053     mutex_unlock(&icd->video_lock);  
  108. 1054     soc_camera_free_user_formats(icd);  
  109. 1055 eiufmt:  
  110. 1056     if (icl->board_info) {  
  111. 1057         soc_camera_free_i2c(icd);  
  112. 1058     } else {  
  113. 1059         icl->del_device(icl);  
  114. 1060         module_put(control->driver->owner);  
  115. 1061     }  
  116. 1062 enodrv:  
  117. 1063 eadddev:  
  118. 1064     video_device_release(icd->vdev);  
  119. 1065 evdc:  
  120. 1066     ici->ops->remove(icd);  
  121. 1067 eadd:  
  122. 1068     soc_camera_power_set(icd, icl, 0);  
  123. 1069 epower:  
  124. 1070     regulator_bulk_free(icl->num_regulators, icl->regulators);  
  125. 1071 ereg:  
  126. 1072     return ret;  
  127. 1073 }  

在host-driver probe函数中调用,soc_camera_host_register ==> scan_add_host ==> device_register ==> bus_probe_device ==> soc_camera_probe

972 调用camera host驱动的add函数,比如pxa平台的pxa_camera_add_device

977 在调用video_device_register之前,要先创建video_device

982~1008 如果是i2c camera,那么调用soc_camera_init_i2c来初始华i2c,否则就调用add_device增加设备。soc_camera_init_i2c会调用到芯片驱动i2c_driver.probe,对于我们的项目,则是adv7180_probe

1010~1013  初始化client format,调用soc_camera_init_user_format之前,已经执行了芯片的probe函数,已经可以对芯片进一步的操作。

1026 调用soc_camera_video_start注册一个video device

1031 每一个soc camera device都一一对应一个v4l2 subdev

1044 ~ 1046 已经获取了soc camera device必要的信息后,调用remove_device关闭soc camera device,然后调用soc_camera_power_set关闭soc camera device的电源。


[cpp]  view plain copy
  1. 869 /* So far this function cannot fail */  
  2. 870 static void scan_add_host(struct soc_camera_host *ici)  
  3. 871 {  
  4. 872     struct soc_camera_device *icd;  
  5. 873   
  6. 874     mutex_lock(&list_lock);  
  7. 875   
  8. 876     list_for_each_entry(icd, &devices, list) {  
  9. 877         if (icd->iface == ici->nr) {  
  10. 878             int ret;  
  11. 879             icd->dev.parent = ici->v4l2_dev.dev;  
  12. 880             dev_set_name(&icd->dev, "%u-%u", icd->iface,  
  13. 881                      icd->devnum);  
  14. 882             ret = device_register(&icd->dev);  
  15. 883             if (ret < 0) {  
  16. 884                 icd->dev.parent = NULL;  
  17. 885                 dev_err(&icd->dev,  
  18. 886                     "Cannot register device: %d\n", ret);  
  19. 887             }  
  20. 888         }  
  21. 889     }  
  22. 890   
  23. 891     mutex_unlock(&list_lock);  
  24. 892 }  

这个函数只被soc_camera_host_register调用。扫描系统所有的camera device,把属于这个camera host(参数@ici指定)的所有camera device注册到系统中。

876 系统所有的camera device,在没有被camera host注册前,这些camera device仅保存在@devices链表中

877 比较camera device的host number是否等于这个camera host

882 device_register 注册一个设备到系统中,这个函数会调用bus_probe_device,而bus_probe_device则会调用soc_camera_bus_type.probe,也就是soc_camera_probe。这样soc_camera_host_register就会注册所有属于这个host的camera device到系统中,并且创建了相应的设备节点/dev/videoX,整个设备的注册过程全部结束了,从现在开始,可以在设备节点/dev/videoX上调用open read write ioctl以及poll。

[cpp]  view plain copy
  1. 178 static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)  
  2. 179 {  
  3. 180     int ret;  
  4. 181     struct soc_camera_device *icd = file->private_data;  
  5. 182     struct v4l2_subdev *sd = soc_camera_to_subdev(icd);  
  6. 183   
  7. 184     /* call s_routing to select the input of camera sensor */  
  8. 185     ret = v4l2_subdev_call(sd, video, s_routing, i, 0, 0);  
  9. 186   
  10. 187     return ret;  
  11. 188 }  
185 soc_camera驱动并没有实现这句话,而是直接  return 0,这就导致当前的soc camera子系统不支持S_INPUT接口。

[cpp]  view plain copy
  1. 384 static int soc_camera_open(struct file *file)  
  2.  385 {  
  3.  386     struct video_device *vdev = video_devdata(file);  
  4.  387     struct soc_camera_device *icd = container_of(vdev->parent,  
  5.  388                              struct soc_camera_device,  
  6.  389                              dev);  
  7.  390     struct soc_camera_link *icl = to_soc_camera_link(icd);  
  8.  391     struct soc_camera_host *ici;  
  9.  392     int ret;  
  10.  393   
  11.  394     if (!icd->ops)  
  12.  395         /* No device driver attached */  
  13.  396         return -ENODEV;  
  14.  397   
  15.  398     ici = to_soc_camera_host(icd->dev.parent);  
  16.  399   
  17.  400     if (!try_module_get(ici->ops->owner)) {  
  18.  401         dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");  
  19.  402         return -EINVAL;  
  20.  403     }  
  21.  404   
  22.  405     icd->use_count++;  
  23.  406   
  24.  407     /* Now we really have to activate the camera */  
  25.  408     if (icd->use_count == 1) {  
  26.  409         /* Restore parameters before the last close() per V4L2 API */  
  27.  410         struct v4l2_format f = {  
  28.  411             .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,  
  29.  412             .fmt.pix = {  
  30.  413                 .width      = icd->user_width,  
  31.  414                 .height     = icd->user_height,  
  32.  415                 .field      = icd->field,  
  33.  416                 .colorspace = icd->colorspace,  
  34.  417                 .pixelformat    =  
  35.  418                     icd->current_fmt->host_fmt->fourcc,  
  36.  419             },  
  37.  420         };  
  38.  421   
  39.  422         ret = soc_camera_power_set(icd, icl, 1);  
  40.  423         if (ret < 0)  
  41.  424             goto epower;  
  42.  425   
  43.  426         /* The camera could have been already on, try to reset */  
  44.  427         if (icl->reset)  
  45.  428             icl->reset(icd->pdev);  
  46.  429   
  47.  430         ret = ici->ops->add(icd);  
  48.  431         if (ret < 0) {  
  49.  432             dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);  
  50.  433             goto eiciadd;  
  51.  434         }  
  52.  435   
  53.  436         pm_runtime_enable(&icd->vdev->dev);  
  54.  437         ret = pm_runtime_resume(&icd->vdev->dev);  
  55.  438         if (ret < 0 && ret != -ENOSYS)  
  56.  439             goto eresume;  
  57.  440   
  58.  441         /* 
  59.  442          * Try to configure with default parameters. Notice: this is the 
  60.  443          * very first open, so, we cannot race against other calls, 
  61.  444          * apart from someone else calling open() simultaneously, but 
  62.  445          * .video_lock is protecting us against it. 
  63.  446          */  
  64.  447         ret = soc_camera_set_fmt(icd, &f);  
  65.  448         if (ret < 0)  
  66.  449             goto esfmt;  
  67.  450   
  68.  451         ici->ops->init_videobuf(&icd->vb_vidq, icd);  
  69.  452     }  
  70.  453   
  71.  454     file->private_data = icd;  
  72.  455     dev_dbg(&icd->dev, "camera device open\n");  
  73.  456   
  74.  457     return 0;  
  75.  458   
  76.  459     /* 
  77.  460      * First four errors are entered with the .video_lock held 
  78.  461      * and use_count == 1 
  79.  462      */  
  80.  463 esfmt:  
  81.  464     pm_runtime_disable(&icd->vdev->dev);  
  82.  465 eresume:  
  83.  466     ici->ops->remove(icd);  
  84.  467 eiciadd:  
  85.  468     soc_camera_power_set(icd, icl, 0);  
  86.  469 epower:  
  87.  470     icd->use_count--;  
  88.  471     module_put(ici->ops->owner);  
  89.  472   
  90.  473     return ret;  
  91.  474 }   

当应用通过open系统调用,打开设备节点/dev/videoX时,会调用soc_camera_open

430 ici->ops->add 不仅要执行host内部的初始化,还会调用camera sensor(参数icd指定)的init

447 配置camera sensor缺省的格式参数,从我个人理解,一切合理的fmt都应该在调用S_INPUT之后进行设置。当然,这需要应用程序编程时先调用S_INPUT再进行S_FMT。

405~408 仅在第一次打开时,才对camera host和camera sensor做初始化操作,否则,仅仅增加引用计数。

应用层通过设备节点/dev/videoX打开video4linux devices。/dev/videoX是一个字符设备,主设备号81,次设备号: (0~63)分配给capture设备,64~127分配给radio设备,223~255分配给VBI设备,128~191分配给其他类型的。

如果驱动要注册一个video4linux设备,那么需要调用video_register_device函数。


利用dump_stack函数,可以方便的获取内核函数调用关系。


open调用关系

从调用关系角度来看,open是最复杂的,因为它不仅要执行真正的打开操作,还需要为mmap ioctl设置操作函数。

1. Application 通过open打开设备节点/dev/videoX

2. 进入系统调用sys_open,sys_open调用do_sys_open

3. do_sys_open 调用 do_flip_open

4. do_filp_open 这个函数比较长,大部分都是路径查找相关的代码,这里只需要关注do_last

5. do_last 大部分是路径相关的代码,只需关注finish_open

6. finish_open 调用这个函数时,已经填充好了路径名对应的nameidata结构,调用nameidata_to_flip

7. nameidata_to_flip 调用__dentry_open

8.  __dentry_open会调用chrdev_open,在__dentry_open中有如下代码片段

[cpp]  view plain copy
  1.     f->f_op = fops_get(inode->i_fop);  
  2.     .....  
  3.     if (!open && f->f_op)  
  4.         open = f->f_op->open;  
  5.     if (open) {  
  6.         error = open(inode, f);  
  7.         if (error)  
  8.             goto cleanup_all;  
  9.     }  

当系统进行路径lookup过程中,会把这个设备文件对应的inode读入到内存中,在读取文件inode的过程中,判断这个inode是下列类型中的哪一个:regualr,char,block,pipe。此时,会根据inode类型的不同,赋给inode->i_fop不同的操作函数

在当前的case,shmem_get_inode会调用init_special_inode初始化这个inode,由于/dev/videoX是字符设备,所以inode->i_fop = &def_chr_fops。def_chr_fops.open = chrdev_open

9. chrdev_open 根据设备节点的主次设备好在系统内查找对应的cdev对象,把cdev->ops赋给filp->f_op(这个赋值操作是很重要的步骤,它把filp和具体设备的操作函数联系到一起),当前的case,cdev->ops是v4l2_ops。调用filp->f_op->open就完成了打开操作,flip->f_op->open则是v4l2_open。

10. v4l2_open 中调用vdev->fops->open,对于soc camera来说,我们在调用video_register_device之前,已经把vdev->fops设置为soc_camera_fops。vdev->fops->open也就是调用soc_camera_open

11. 绕了很大一圈,终于调用了soc_camera_open。


mmap调用关系

1. 系统调用mmap_pgoff,mmap_pgoff调用do_mmap_pgoff

2. do_mmap_pgoff,调用mmap_region

3. mmap_region 调用filp->f_op->mmap,在第一次打开文件时,open操作设置了file->f_op为v4l2_ops,file->f_op->mmap则是v4l2_mmap

4. v4l2_mmap,v4l2_mmap调用vdev->fops->mmap,对于soc_camera来说,vdev->fops是soc_camera_ops,所以vdev->fops->mmap是soc_camera_mmap

5. soc_camera_mmap


ioctl调用关系

1. 系统调用ioctl,调用do_vfs_ioctl

2. do_vfs_ioctl ,调用vfs_ioctl

3. vfs_ioctl 代码片段

[cpp]  view plain copy
  1. if (filp->f_op->unlocked_ioctl) {  
  2.     error = filp->f_op->unlocked_ioctl(filp, cmd, arg);  
  3.     if (error == -ENOIOCTLCMD)  
  4.         error = -EINVAL;  
  5.     goto out;  
  6. else if (filp->f_op->ioctl) {  
  7.     lock_kernel();  
  8.     error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,  
  9.                   filp, cmd, arg);  
  10.     unlock_kernel();  
  11. }  

在第一次打开文件时,已经设置了filp->f_op为v4l2_ops。注意不同内核版本中v4l2_ops实现是不同的,可能定义了ioctl,也可能定义了unlocked_ioctl。我的kernel v4l2_ops定义如下

[cpp]  view plain copy
  1. static const struct file_operations v4l2_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .read = v4l2_read,  
  4.     .write = v4l2_write,  
  5.     .open = v4l2_open,  
  6.     .mmap = v4l2_mmap,  
  7.     .unlocked_ioctl = v4l2_ioctl,  
  8. #ifdef CONFIG_COMPAT  
  9.     .compat_ioctl = v4l2_compat_ioctl32,  
  10. #endif  
  11.     .release = v4l2_release,  
  12.     .poll = v4l2_poll,  
  13.     .llseek = no_llseek,  
  14. };  

因此filep->f_op_unlocked_ioctl是v4l2_ioctl

5. v4l2_ioctl 代码片段如下

[cpp]  view plain copy
  1. if (vdev->fops->unlocked_ioctl) {  
  2.     if (vdev->lock && mutex_lock_interruptible(vdev->lock))  
  3.         return -ERESTARTSYS;  
  4.     if (video_is_registered(vdev))  
  5.         ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);  
  6.     if (vdev->lock)  
  7.         mutex_unlock(vdev->lock);  

soc_camera_fops.unlocked_ioctl = video_ioctl2

6. video_ioctl2 调用__video_do_ioctl

7. __video_do_ioctl,这个函数对参数做一些基本的判断,然后调用video_device的ioctl_ops,对于soc_camera系统来说,是soc_camera_ioctl_ops

8 soc_camera_ioctl_ops实现如下

[cpp]  view plain copy
  1. static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {  
  2.     .vidioc_querycap     = soc_camera_querycap,  
  3.     .vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,  
  4.     .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,  
  5.     .vidioc_s_fmt_vid_cap    = soc_camera_s_fmt_vid_cap,  
  6.     .vidioc_enum_input   = soc_camera_enum_input,  
  7.     .vidioc_g_input      = soc_camera_g_input,  
  8.     .vidioc_s_input      = soc_camera_s_input,  
  9.     .vidioc_s_std        = soc_camera_s_std,  
  10.     .vidioc_reqbufs      = soc_camera_reqbufs,  
  11.     .vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,  
  12.     .vidioc_querybuf     = soc_camera_querybuf,  
  13.     .vidioc_qbuf         = soc_camera_qbuf,  
  14.     .vidioc_dqbuf        = soc_camera_dqbuf,  
  15.     .vidioc_streamon     = soc_camera_streamon,  
  16.     .vidioc_streamoff    = soc_camera_streamoff,  
  17.     .vidioc_queryctrl    = soc_camera_queryctrl,  
  18.     .vidioc_g_ctrl       = soc_camera_g_ctrl,  
  19.     .vidioc_s_ctrl       = soc_camera_s_ctrl,  
  20.     .vidioc_cropcap      = soc_camera_cropcap,  
  21.     .vidioc_g_crop       = soc_camera_g_crop,  
  22.     .vidioc_s_crop       = soc_camera_s_crop,  
  23.     .vidioc_g_parm       = soc_camera_g_parm,  
  24.     .vidioc_s_parm       = soc_camera_s_parm,  
  25.     .vidioc_g_chip_ident     = soc_camera_g_chip_ident,  
  26. #ifdef CONFIG_VIDEO_ADV_DEBUG  
  27.     .vidioc_g_register   = soc_camera_g_register,  
  28.     .vidioc_s_register   = soc_camera_s_register,  
  29. #endif  
  30. };  


图1 soc camera 子系统 系统架构图


Soc camera sub-system对应着drivers/media/video/下的soc_camera.c soc_camera_platform.c

Soc camera host 是host端实现,是由平台厂商实现的,向上实现soc_camera_host_ops接口,向下操作Camera host硬件以及通过平台特定的接口操作Soc camera device

Soc camera device 是平台的camera device(同时也是subdev),由驱动开发者来实现v4l2_subdev_call调用的subdev 接口,同时还要为soc camera host实现平台特定的操作接口;向下操作camera sensor或者video AD芯片。

Camera host hardware是平台硬件相关的,不同的平台有不同的host硬件,比如imx51的ipu,三星s5pv210的fimc控制器等。


图1 soc camera 子系统 系统架构图


Soc camera sub-system对应着drivers/media/video/下的soc_camera.c soc_camera_platform.c

Soc camera host 是host端实现,是由平台厂商实现的,向上实现soc_camera_host_ops接口,向下操作Camera host硬件以及通过平台特定的接口操作Soc camera device

Soc camera device 是平台的camera device(同时也是subdev),由驱动开发者来实现v4l2_subdev_call调用的subdev 接口,同时还要为soc camera host实现平台特定的操作接口;向下操作camera sensor或者video AD芯片。

Camera host hardware是平台硬件相关的,不同的平台有不同的host硬件,比如imx51的ipu,三星s5pv210的fimc控制器等。

### V4L2源代码在Linux内核中的位置 V4L2(Video for Linux 2)是Linux内核中用于视频设备驱动的子系统,其源代码主要分布在内核的`drivers/media`目录下。以下是V4L2源代码的主要组织结构和关键文件[^1]: - **`drivers/media/v4l2-core/`**:这是V4L2核心功能的实现目录,包含了V4L2的基础框架和通用接口。例如: - `v4l2-dev.c`:实现了标准的字符设备操作函数,如`open()`、`close()`和`ioctl()`等[^2]。 - `v4l2-subdev.c`:提供了对子设备的支持,包括子设备的初始化、枚举和控制等功能。 - `videobuf2-core.c`:实现了缓冲区管理框架的核心逻辑,支持多种缓冲区管理模式。 - **`include/media/`**:定义了V4L2相关的头文件,包含数据结构和API声明。例如: - `v4l2-device.h`:定义了`v4l2_device`结构体及其相关操作。 - `v4l2-ctrls.h`:描述了控制机制的数据结构和函数原型。 - `v4l2-mediabus.h`:定义了媒体总线的规范和数据传输格式。 - **`drivers/media/common/`**:包含一些通用的功能模块,适用于不同的硬件平台。例如: - `v4l2-fwnode.c`:处理固件节点解析,用于从设备树或ACPI中获取配置信息。 - `v4l2-async.c`:支持异步子设备注册和注销。 - **`drivers/media/platform/`**:针对特定硬件平台的V4L2驱动程序实现。每个子目录通常对应一个具体的SoC或芯片组。 - **`drivers/media/i2c/`**:包含与I2C总线相关的视频子设备驱动程序,例如摄像头传感器和图像信号处理器。 #### 示例代码片段 以下是一个典型的V4L2驱动程序初始化过程的代码示例: ```c #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> static int my_v4l2_init(struct v4l2_device *v4l2_dev) { int ret; // 初始化v4l2_device ret = v4l2_device_register(NULL, v4l2_dev); if (ret < 0) return ret; // 创建控制句柄 v4l2_ctrl_handler_init(&v4l2_dev->ctrl_handler, 10); v4l2_dev->ctrl_handler.lock = &my_mutex; // 注册视频设备节点 video_device *vdev = video_device_alloc(); if (!vdev) { v4l2_device_unregister(v4l2_dev); return -ENOMEM; } strscpy(vdev->name, "my_v4l2_device", sizeof(vdev->name)); vdev->v4l2_dev = v4l2_dev; vdev->fops = &my_v4l2_fops; vdev->release = video_device_release_empty; ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { video_device_release(vdev); v4l2_device_unregister(v4l2_dev); return ret; } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值