V4L2调试之(五)

11 篇文章 2 订阅
8 篇文章 0 订阅

1.关于/dev/mediaX设备的注册,有几个V4L2设备,注册几个/dev/meidaX节点。

比如,插入两个UVC USB Camera,就会出现如下图所示的两个节点,分别用于描述各自的V4L2拓扑。下面是linux上的callstack.

[   86.241290] v4l2_device_register line 21, kworker/2:3 register device.
[   86.241295] CPU: 2 PID: 1920 Comm: kworker/2:3 Tainted: G        W         5.4.128+ #1
[   86.241297] Hardware name: TIMI RedmiBook 14/TM1814, BIOS RMRWL400P0503 11/13/2019
[   86.241307] Workqueue: usb_hub_wq hub_event
[   86.241310] Call Trace:
[   86.241322]  dump_stack+0x6d/0x8b
[   86.241345]  v4l2_device_register+0x4a/0xe0 [videodev]
[   86.241354]  uvc_probe+0x4eb/0x2a10 [uvcvideo]
[   86.241363]  usb_probe_interface+0x149/0x300
[   86.241370]  ? uvc_register_video_device+0x150/0x150 [uvcvideo]
[   86.241374]  ? usb_probe_interface+0x149/0x300
[   86.241379]  really_probe+0xf5/0x440
[   86.241384]  driver_probe_device+0x11b/0x130
[   86.241388]  __device_attach_driver+0x7b/0xe0
[   86.241392]  ? driver_allows_async_probing+0x60/0x60
[   86.241399]  bus_for_each_drv+0x6e/0xb0
[   86.241403]  __device_attach+0xe4/0x160
[   86.241408]  device_initial_probe+0x13/0x20
[   86.241411]  bus_probe_device+0x92/0xa0
[   86.241416]  device_add+0x402/0x690
[   86.241422]  ? _cond_resched+0x19/0x40
[   86.241426]  usb_set_configuration+0x3fd/0x8f0
[   86.241432]  ? kernfs_activate+0x78/0x80
[   86.241440]  generic_probe+0x2e/0x80
[   86.241443]  usb_probe_device+0x31/0x70
[   86.241447]  really_probe+0xf5/0x440
[   86.241451]  driver_probe_device+0x11b/0x130
[   86.241455]  __device_attach_driver+0x7b/0xe0
[   86.241459]  ? driver_allows_async_probing+0x60/0x60
[   86.241465]  bus_for_each_drv+0x6e/0xb0
[   86.241468]  __device_attach+0xe4/0x160
[   86.241473]  device_initial_probe+0x13/0x20
[   86.241476]  bus_probe_device+0x92/0xa0
[   86.241480]  device_add+0x402/0x690
[   86.241487]  ? add_device_randomness+0x9d/0x1c0
[   86.241492]  usb_new_device+0x218/0x4a0
[   86.241498]  hub_event+0x11b1/0x1760
[   86.241508]  process_one_work+0x20f/0x400
[   86.241514]  worker_thread+0x34/0x410
[   86.241518]  kthread+0x121/0x140
[   86.241523]  ? process_one_work+0x400/0x400
[   86.241527]  ? kthread_park+0x90/0x90
[   86.241532]  ret_from_fork+0x35/0x40
[   86.263776] input: Integrated Camera: Integrated C as /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/input/input25
[   96.375646] usb 1-4: new high-speed USB device number 7 using xhci_hcd
[   96.524824] usb 1-4: config 1 interface 0 altsetting 0 endpoint 0x83 has an invalid bInterval 32, changing to 9
[   96.525345] usb 1-4: New USB device found, idVendor=1b3f, idProduct=2247, bcdDevice= 1.00
[   96.525350] usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[   96.525353] usb 1-4: Product: GENERAL WEBCAM
[   96.525356] usb 1-4: Manufacturer: GENERAL
[   96.526771] uvcvideo: Found UVC 1.00 device GENERAL WEBCAM (1b3f:2247)
[   96.526777] v4l2_device_register line 21, kworker/2:3 register device.
[   96.526784] CPU: 2 PID: 1920 Comm: kworker/2:3 Tainted: G        W         5.4.128+ #1
[   96.526788] Hardware name: TIMI RedmiBook 14/TM1814, BIOS RMRWL400P0503 11/13/2019
[   96.526799] Workqueue: usb_hub_wq hub_event
[   96.526804] Call Trace:
[   96.526819]  dump_stack+0x6d/0x8b
[   96.526853]  v4l2_device_register+0x4a/0xe0 [videodev]
[   96.526867]  uvc_probe+0x4eb/0x2a10 [uvcvideo]
[   96.526878]  usb_probe_interface+0x149/0x300
[   96.526891]  ? uvc_register_video_device+0x150/0x150 [uvcvideo]
[   96.526896]  ? usb_probe_interface+0x149/0x300
[   96.526905]  really_probe+0xf5/0x440
[   96.526911]  driver_probe_device+0x11b/0x130
[   96.526918]  __device_attach_driver+0x7b/0xe0
[   96.526925]  ? driver_allows_async_probing+0x60/0x60
[   96.526933]  bus_for_each_drv+0x6e/0xb0
[   96.526939]  __device_attach+0xe4/0x160
[   96.526945]  device_initial_probe+0x13/0x20
[   96.526951]  bus_probe_device+0x92/0xa0
[   96.526964]  device_add+0x402/0x690
[   96.526972]  ? _cond_resched+0x19/0x40
[   96.526977]  usb_set_configuration+0x3fd/0x8f0
[   96.526985]  ? kernfs_activate+0x78/0x80
[   96.526997]  generic_probe+0x2e/0x80
[   96.527008]  usb_probe_device+0x31/0x70
[   96.527014]  really_probe+0xf5/0x440
[   96.527020]  driver_probe_device+0x11b/0x130
[   96.527025]  __device_attach_driver+0x7b/0xe0
[   96.527030]  ? driver_allows_async_probing+0x60/0x60
[   96.527038]  bus_for_each_drv+0x6e/0xb0
[   96.527045]  __device_attach+0xe4/0x160
[   96.527052]  device_initial_probe+0x13/0x20
[   96.527056]  bus_probe_device+0x92/0xa0
[   96.527063]  device_add+0x402/0x690
[   96.527072]  ? add_device_randomness+0x9d/0x1c0
[   96.527079]  usb_new_device+0x218/0x4a0
[   96.527087]  hub_event+0x11b1/0x1760
[   96.527108]  process_one_work+0x20f/0x400
[   96.527123]  worker_thread+0x34/0x410
[   96.527130]  kthread+0x121/0x140
[   96.527137]  ? process_one_work+0x400/0x400
[   96.527142]  ? kthread_park+0x90/0x90
[   96.527149]  ret_from_fork+0x35/0x40

struct v4l2_device的注册也是这样,它可以看成v4l2 框架的 rootdevice,每个V4L2的采集设备,都对应一个v4l2_device,也对应如上的/dev/mediaX., 所有的subdev都链接在同一个v4l2_device下. 通过v4l2_device, 我们可以遍历所有的sbudev. v4l2_device此时相当于所以subdev的父设备. 从如下调用堆栈也可以看出通用USB设备的发现流程。由于USB驱动已经事先注册进系统,这里是通过HUB的设备发现去发现USBS设备的插入,之后进行USB设备级的device_add,和设备驱动匹配上后,进行USB设备级的probe函数usb_probe_device.然后进步步,读取USB配置后,进行接口级的USB设备注册,再次调用device_add在USB总线上MATCH USB INTERFACE驱动,注册真正的video device uvc驱动。在这个层面,实际上最后一级的probe已经进入到 struct usb_driver的定义了。本质上,usb_driver是对USB INTERFACE struct device_driver的封装。

USB设备的注册接口和INTERFACE的注册接口是不同的,通过for_device变量区分。设别的变量为1.usb_register_device_driver

 而接口的变量为0,usb_register_driver:

​
[   58.718847] media_devnode_register line 248.comm kworker/1:2.
[   58.718854] CPU: 1 PID: 327 Comm: kworker/1:2 Tainted: G        W         5.4.128+ #1
[   58.718856] Hardware name: TIMI RedmiBook 14/TM1814, BIOS RMRWL400P0503 11/13/2019
[   58.718866] Workqueue: usb_hub_wq hub_event
[   58.718869] Call Trace:
[   58.718881]  dump_stack+0x6d/0x8b
[   58.718893]  media_devnode_register+0x11b/0x1c0 [mc]
[   58.718902]  __media_device_register+0x77/0x110 [mc]
[   58.718910]  uvc_probe+0x2638/0x2a10 [uvcvideo]
[   58.718919]  usb_probe_interface+0x149/0x300
[   58.718926]  ? uvc_register_video_device+0x150/0x150 [uvcvideo]
[   58.718929]  ? usb_probe_interface+0x149/0x300
[   58.718935]  really_probe+0xf5/0x440
[   58.718939]  driver_probe_device+0x11b/0x130
[   58.718944]  __device_attach_driver+0x7b/0xe0
[   58.718948]  ? driver_allows_async_probing+0x60/0x60
[   58.718955]  bus_for_each_drv+0x6e/0xb0
[   58.718959]  __device_attach+0xe4/0x160
[   58.718963]  device_initial_probe+0x13/0x20
[   58.718966]  bus_probe_device+0x92/0xa0
[   58.718972]  device_add+0x402/0x690
[   58.718978]  ? _cond_resched+0x19/0x40
[   58.718981]  usb_set_configuration+0x3fd/0x8f0
[   58.718988]  ? kernfs_activate+0x78/0x80
[   58.718996]  generic_probe+0x2e/0x80
[   58.718999]  usb_probe_device+0x31/0x70
[   58.719003]  really_probe+0xf5/0x440
[   58.719007]  driver_probe_device+0x11b/0x130
[   58.719011]  __device_attach_driver+0x7b/0xe0
[   58.719015]  ? driver_allows_async_probing+0x60/0x60
[   58.719020]  bus_for_each_drv+0x6e/0xb0
[   58.719024]  __device_attach+0xe4/0x160
[   58.719028]  device_initial_probe+0x13/0x20
[   58.719031]  bus_probe_device+0x92/0xa0
[   58.719036]  device_add+0x402/0x690
[   58.719043]  ? add_device_randomness+0x9d/0x1c0
[   58.719047]  usb_new_device+0x218/0x4a0
[   58.719053]  hub_event+0x11b1/0x1760
[   58.719063]  process_one_work+0x20f/0x400
[   58.719068]  worker_thread+0x34/0x410
[   58.719073]  kthread+0x121/0x140
[   58.719078]  ? process_one_work+0x400/0x400
[   58.719082]  ? kthread_park+0x90/0x90
[   58.719089]  ret_from_fork+0x35/0x40
[   58.719382] input: Integrated Camera: Integrated C as /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/input/input24
[  152.269448] usb 1-4: new high-speed USB device number 6 using xhci_hcd
[  152.418887] usb 1-4: config 1 interface 0 altsetting 0 endpoint 0x83 has an invalid bInterval 32, changing to 9
[  152.419610] usb 1-4: New USB device found, idVendor=1b3f, idProduct=2247, bcdDevice= 1.00
[  152.419616] usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[  152.419619] usb 1-4: Product: GENERAL WEBCAM
[  152.419622] usb 1-4: Manufacturer: GENERAL
[  152.421232] uvcvideo: Found UVC 1.00 device GENERAL WEBCAM (1b3f:2247)
[  152.421461] uvcvideo: Failed to query (GET_INFO) UVC control 2 on unit 1: -32 (exp. 1).
[  152.424449] uvcvideo: UVC non compliance - GET_DEF(PROBE) not supported. Enabling workaround.
[  152.424987] media_devnode_register line 248.comm kworker/1:2.
[  152.424996] CPU: 1 PID: 327 Comm: kworker/1:2 Tainted: G        W         5.4.128+ #1
[  152.424998] Hardware name: TIMI RedmiBook 14/TM1814, BIOS RMRWL400P0503 11/13/2019
[  152.425010] Workqueue: usb_hub_wq hub_event
[  152.425014] Call Trace:
[  152.425026]  dump_stack+0x6d/0x8b
[  152.425039]  media_devnode_register+0x11b/0x1c0 [mc]
[  152.425048]  __media_device_register+0x77/0x110 [mc]
[  152.425055]  uvc_probe+0x2638/0x2a10 [uvcvideo]
[  152.425063]  usb_probe_interface+0x149/0x300
[  152.425070]  ? uvc_register_video_device+0x150/0x150 [uvcvideo]
[  152.425077]  ? usb_probe_interface+0x149/0x300
[  152.425082]  really_probe+0xf5/0x440
[  152.425086]  driver_probe_device+0x11b/0x130
[  152.425089]  __device_attach_driver+0x7b/0xe0
[  152.425093]  ? driver_allows_async_probing+0x60/0x60
[  152.425099]  bus_for_each_drv+0x6e/0xb0
[  152.425103]  __device_attach+0xe4/0x160
[  152.425107]  device_initial_probe+0x13/0x20
[  152.425109]  bus_probe_device+0x92/0xa0
[  152.425114]  device_add+0x402/0x690
[  152.425119]  ? _cond_resched+0x19/0x40
[  152.425122]  usb_set_configuration+0x3fd/0x8f0
[  152.425128]  ? kernfs_activate+0x78/0x80
[  152.425135]  generic_probe+0x2e/0x80
[  152.425138]  usb_probe_device+0x31/0x70
[  152.425141]  really_probe+0xf5/0x440
[  152.425145]  driver_probe_device+0x11b/0x130
[  152.425148]  __device_attach_driver+0x7b/0xe0
[  152.425151]  ? driver_allows_async_probing+0x60/0x60
[  152.425156]  bus_for_each_drv+0x6e/0xb0
[  152.425160]  __device_attach+0xe4/0x160
[  152.425163]  device_initial_probe+0x13/0x20
[  152.425165]  bus_probe_device+0x92/0xa0
[  152.425170]  device_add+0x402/0x690
[  152.425176]  ? add_device_randomness+0x9d/0x1c0
[  152.425180]  usb_new_device+0x218/0x4a0
[  152.425185]  hub_event+0x11b1/0x1760
[  152.425195]  process_one_work+0x20f/0x400
[  152.425203]  worker_thread+0x34/0x410
[  152.425208]  kthread+0x121/0x140
[  152.425212]  ? process_one_work+0x400/0x400
[  152.425216]  ? kthread_park+0x90/0x90
[  152.425220]  ret_from_fork+0x35/0x40
[  152.425547] input: GENERAL WEBCAM: GENERAL WEBCAM as /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/input/input25
[  152.452622] uvcvideo: Failed to query (GET_DEF) UVC control 2 on unit 1: -32 (exp. 1).
[  152.452780] uvcvideo: Failed to query (GET_DEF) UVC control 2 on unit 1: -32 (exp. 1).
[  152.483857] usb 1-4: Warning! Unlikely big volume range (=5120), cval->res is probably wrong.
[  152.483859] usb 1-4: [5] FU [Mic Capture Volume] ch = 1, val = 7680/12800/1
[  152.484248] usbcore: registered new interface driver snd-usb-audio
czl@czl-RedmiBook-14:~/Workspace$ 

​

2.用到struct v4l2_subdev_internal_ops结构注册的地方 有两个,一个是

 另一个是:

有意思的是,这两个一个在PIPEline的首端(sensor),另一个在pipeline的尾端(video), v4l2_subdev_internal_ops提供了4个接口: registered、unregistered、open、close. 这4个接口是给v4l2核心层代码回调用的. 当向核心层注册/注销subdev时(调用v4l2_device_register_subdev), 核心层会回调这里的registered/unregistered. 当用户空间打开/关闭设备节点时, 核心层会回调这里的open/close.

3. VIN框架中管理有一张表,表中记录了VIN支持的所有可能的格式,JPEG类的是YUV, SRGB类的是RGB.

V4L2_COLORSPACE_JPEG表示YUV像素格式.

使用的地方再这里:

再创建pipeline的时候会看是否支持上面配置下来的格式,不支持会返回失败.

4.V4L2设备初始化的时通过I2C和sensor的交互过程.

MIPI CSI的传输是这样的,如下图,它通过CCI总线对Sensor的寄存器进行配置。

一般情况下,CCI总线是通过I2C总线实现的,所以从驱动角度来讲,它实际上调用的是I2C驱动。下面调试一下Melis系统上Sensor初始化过程中,对I2C的调用时序.

首先看一下打印,在hal_twi_xfer里面加打印:

 __builtin_return_address(LEVEL)的作用是返回调用栈第level层的返回值。

可以看到,貌似GCC只能返回当前函数的返回地址。

所以看起来是从cci_read_a16_d8调用下来的,我们看一下这个调用是怎么发生的。

可以看到,这里就是上面说的internal_ops的handler, sensor_registered,调用链中,会调用sensor_detect尝试读取sensor_id,根据ID来判断sensor是否存在。

 返回的是IMX的 sensor id.

sensor bringup 的第二步是上电:

第三步的是init寄存器

 初始化的是register的列表

下一步,设置默认帧率,还是同样的调用栈:

wsize->regs是默认的帧率配置参数中的一个,每个帧率对应一种配置

接下来,设置增益:

打开阀门,让水流进来,开启sensor抓图。

到这里,画面实际上已经出来了,之后,就是ISP常规化的控制了,这个设置gain的调用会一直存在。

解决的一个问题记录

客户要求可以通过melis shell命令来读取sensor寄存器,由于sensor是板载资源,访问方式是通过cci总线(I2C)的方式进行的。需求很简单,但是在实现的时候遇到了一个技术性问题,感觉对理解v4l2 sensor注册的流程挺有帮助的,这里记录一下。

实现很简单,以为读取sensor  id为例,写完后是这样子的,根据上面的分析,0x16,0x17寄存器是ID寄存器,况且启动过程中是可以访问的,那么我把它封装成一个shell命令,应该也可以读取才对。

但是测试情况出乎意料:

执行read_sensor,I2C接口访问竟然是失败的。

而启动过程中的访问是成功的:

启动过程和手敲命令测试流程上是相距不远的逻辑,一定是中间发生了什么才导致的问题。处理这种问题最好使用二分法,不断在从OK到failure的路径中加上打印,最后发现了明显的差异点的位置:

 如上图,正常启动流程中的访问是在67行中进行的,然后直到73行中的调用还是OK的,但是到了81行,就无法成功读取到ID了,这说明问题就发生在73行到81行中间。

很明显,最值得怀疑的逻辑是77行,看起来它是将sensor电源关闭了。添加上打印调试一下:

 编译启动验证:

LOG证实了我们的猜测,PWR_ON和PWR_OFF中间的代码读取是成功的,恰好是PWR_OFF之后的,读取都失败了。

 所以,可以说明,流程中为了节省功耗,对电源进行了按需管理,启动过程中,如果需要对sensor寄存器进行访问,就上电,如果不需要访问sensor了,就关闭电源。

这个例子也可以从侧面说明,启动过程中访问sensor的地方只有一个,就是这里读取sensor id.

接下来把pwr off这行关闭,确认一下是否OK:

OK,看打印结果,每次的读取都是成功的了。

Video Device:

无论是subdevice还是video device,都需要创建struct video_device结构并注册。

static inline int __must_check video_register_device(struct video_device *vdev, …)

向核心层注册一个video_device, 此时核心层会创建字符设备驱动. 后面用户空间就可以通过字符设备节点与video_device交互了.

V4L2设备逻辑图

参考资料:

Linux V4L2子系统 | 思学网V4L2, Camera, subdevhttp://www.mysixue.com/?p=131

结束!

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,针对你的问题,我可以给你提供一个基于 V4L2 的摄像头调试程序示例,该程序可以实现摄像头的白平衡和曝光等参数的调节。 首先,你需要安装 V4L2 开发包和相关依赖库,可以使用以下命令进行安装: ```bash sudo apt-get update sudo apt-get install v4l-utils libv4l-dev ``` 接下来,你可以使用以下 C 语言代码实现摄像头的参数调节: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h> int main(int argc, char **argv) { int fd; struct v4l2_control ctrl; fd = open("/dev/video0", O_RDWR); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } /* 设置白平衡参数 */ ctrl.id = V4L2_CID_AUTO_WHITE_BALANCE; ctrl.value = 0; if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) { perror("VIDIOC_S_CTRL"); exit(EXIT_FAILURE); } /* 设置曝光参数 */ ctrl.id = V4L2_CID_EXPOSURE_AUTO; ctrl.value = V4L2_EXPOSURE_MANUAL; if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) { perror("VIDIOC_S_CTRL"); exit(EXIT_FAILURE); } /* 设置曝光时间 */ ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE; ctrl.value = 100; if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) { perror("VIDIOC_S_CTRL"); exit(EXIT_FAILURE); } close(fd); return 0; } ``` 以上代码中,我们通过 V4L2 提供的 VIDIOC_S_CTRL ioctl 接口来设置摄像头的白平衡和曝光等参数,具体使用方法可以参考 V4L2 开发包的文档。 注意,上述代码仅为示例代码,实际使用时需要根据摄像头型号和参数进行相应的调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值