V4L2_mipi-csi

(gdb) p q->ops->start_streaming

$26 = (int (*)(struct vb2_queue *, unsigned int)) 0xffffff80089eda10 <rkisp_start_streaming>

(gdb) p ((struct rkisp_stream*)q->drv_priv)->ispdev

$27 = (struct rkisp_device *) 0xffffffc0e6880080

(gdb) p q

$28 = (struct vb2_queue *) 0xffffffc0e6880d78

(gdb) p (p->subdevs[0])->name

$9 = "rkisp-csi-subdev", '\000' <repeats 15 times>

(gdb) p (p->subdevs[1])->name

$10 = "rockchip-csi2-dphy0", '\000' <repeats 12 times>

(gdb) p (p->subdevs[2])->name

$11 = "m00_b_ov13850 4-0010", '\000' <repeats 11 times>

phy -> sensor:启动视频流时,先启动 phy(接收端),后启动 sensor(发送端),避免数据丢失;

sensor -> phy:停止视频流时,先停止 sensor(发送端),后停止 phy(接收端),避免硬件卡死;

核心原则:数据流启停需遵循「接收端先就绪、发送端先关闭」的顺序,这是 MIPI CSI 硬件的通用规范;

底层逻辑:p->subdevs数组按phy在前、sensor在后排列,因此正向遍历是 phy→sensor,反向是 sensor→phy。

这个顺序是嵌入式 MIPI 摄像头开发的 "通用坑点",如果顺序反了,会出现摄像头启动后无图像、停止后设备卡死等问题,

(gdb) p ((struct v4l2_ctrl*)hdl->ctrls->next->next->next->next->next->next->next->next->next->next)->name

$30 = 0xffffff8009203da3 "Test Pattern"

ubuntu20@NYX:~/opt/test$ ./a.out

VIDIOC_QUERYCAP: 2154321408 (0x80685600)

VIDIOC_ENUM_FMT: 3225441794 (0xC0405602)

VIDIOC_G_FMT: 3234878980 (0xC0D05604)

VIDIOC_S_FMT: 3234878981 (0xC0D05605)

VIDIOC_REQBUFS: 3222558216 (0xC0145608)

VIDIOC_QUERYBUF: 3227014665 (0xC0585609)

VIDIOC_G_FBUF: 2150651402 (0x8030560A)

VIDIOC_S_FBUF: 1076909579 (0x4030560B)

VIDIOC_OVERLAY: 1074025998 (0x4004560E)

VIDIOC_QBUF: 3227014671 (0xC058560F)

VIDIOC_EXPBUF: 3225441808 (0xC0405610)

VIDIOC_DQBUF: 3227014673 (0xC0585611)

VIDIOC_STREAMON: 1074026002 (0x40045612)

VIDIOC_STREAMOFF: 1074026003 (0x40045613)

ubuntu20@NYX:~/opt/test$ cat a.c

#include <stdio.h>

#include <linux/videodev2.h> // 包含V4L2宏定义

#include <linux/ioctl.h> // 包含_IO/_IOR/_IOW/_IOWR宏

int main() {

// 打印所有命令的十进制和十六进制值

printf("VIDIOC_QUERYCAP: %u (0x%X)\n", VIDIOC_QUERYCAP, VIDIOC_QUERYCAP);

printf("VIDIOC_ENUM_FMT: %u (0x%X)\n", VIDIOC_ENUM_FMT, VIDIOC_ENUM_FMT);

printf("VIDIOC_G_FMT: %u (0x%X)\n", VIDIOC_G_FMT, VIDIOC_G_FMT);

printf("VIDIOC_S_FMT: %u (0x%X)\n", VIDIOC_S_FMT, VIDIOC_S_FMT);

printf("VIDIOC_REQBUFS: %u (0x%X)\n", VIDIOC_REQBUFS, VIDIOC_REQBUFS);

printf("VIDIOC_QUERYBUF: %u (0x%X)\n", VIDIOC_QUERYBUF, VIDIOC_QUERYBUF);

printf("VIDIOC_G_FBUF: %u (0x%X)\n", VIDIOC_G_FBUF, VIDIOC_G_FBUF);

printf("VIDIOC_S_FBUF: %u (0x%X)\n", VIDIOC_S_FBUF, VIDIOC_S_FBUF);

printf("VIDIOC_OVERLAY: %u (0x%X)\n", VIDIOC_OVERLAY, VIDIOC_OVERLAY);

printf("VIDIOC_QBUF: %u (0x%X)\n", VIDIOC_QBUF, VIDIOC_QBUF);

printf("VIDIOC_EXPBUF: %u (0x%X)\n", VIDIOC_EXPBUF, VIDIOC_EXPBUF);

printf("VIDIOC_DQBUF: %u (0x%X)\n", VIDIOC_DQBUF, VIDIOC_DQBUF);

printf("VIDIOC_STREAMON: %u (0x%X)\n", VIDIOC_STREAMON, VIDIOC_STREAMON);

printf("VIDIOC_STREAMOFF: %u (0x%X)\n", VIDIOC_STREAMOFF, VIDIOC_STREAMOFF);

return 0;

}

ubuntu20@NYX:~/opt/test$

//test_pattern

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <string.h>

#include <sys/ioctl.h>

#include <linux/videodev2.h>

// 自定义ARRAY_SIZE宏(用户态通用,替代内核宏)

#ifndef ARRAY_SIZE

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

#endif

// 定义测试模式menu的映射(和驱动侧保持一致)

static const char *ov13850_test_pattern_str[] = {

"Disabled",

"Vertical Color Bar Type 1",

"Vertical Color Bar Type 2",

"Vertical Color Bar Type 3",

"Vertical Color Bar Type 4"

};

// 函数:查看V4L2_CID_TEST_PATTERN的menu选项

static int query_test_pattern_menu(int fd) {

struct v4l2_queryctrl qctrl = {0};

qctrl.id = V4L2_CID_TEST_PATTERN;

// 查询控件基本信息

if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {

perror("VIDIOC_QUERYCTRL failed (test pattern ctrl not exist?)");

return -1;

}

printf("==== Test Pattern Control Info ====\n");

printf("Name: %s\n", qctrl.name);

printf("Min: %d, Max: %d, Default: %d\n", qctrl.minimum, qctrl.maximum, qctrl.default_value);

printf("Menu Options:\n");

// 遍历menu选项并打印

struct v4l2_querymenu qmenu = {0};

qmenu.id = V4L2_CID_TEST_PATTERN;

for (qmenu.index = qctrl.minimum; qmenu.index <= qctrl.maximum; qmenu.index++) {

if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu) < 0) {

perror("VIDIOC_QUERYMENU failed");

continue;

}

printf(" %d: %s\n", qmenu.index, qmenu.name);

}

printf("====================================\n\n");

return 0;

}

// 函数:设置测试模式

static int set_test_pattern(int fd, int value) {

struct v4l2_control ctrl = {0};

ctrl.id = V4L2_CID_TEST_PATTERN;

ctrl.value = value;

if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0) {

perror("VIDIOC_S_CTRL failed");

return -1;

}

printf("Set test pattern to: %d (%s)\n", value,

(value >=0 && value < ARRAY_SIZE(ov13850_test_pattern_str)) ?

ov13850_test_pattern_str[value] : "Unknown");

return 0;

}

// 函数:读取当前测试模式

static int get_test_pattern(int fd) {

struct v4l2_control ctrl = {0};

ctrl.id = V4L2_CID_TEST_PATTERN;

if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) < 0) {

perror("VIDIOC_G_CTRL failed");

return -1;

}

printf("Current test pattern: %d (%s)\n", ctrl.value,

(ctrl.value >=0 && ctrl.value < ARRAY_SIZE(ov13850_test_pattern_str)) ?

ov13850_test_pattern_str[ctrl.value] : "Unknown");

return ctrl.value;

}

int main(int argc, char *argv[]) {

if (argc != 3) {

fprintf(stderr, "Usage: %s <video_dev> <test_pattern_value>\n", argv[0]);

fprintf(stderr, "Example: %s /dev/video0 1 (set to Vertical Color Bar Type 1)\n", argv[0]);

return -1;

}

// 1. 打开视频设备

int fd = open(argv[1], O_RDWR);

if (fd < 0) {

perror("open video dev failed");

return -1;

}

// 2. 查询测试模式的menu选项

if (query_test_pattern_menu(fd) < 0) {

close(fd);

return -1;

}

// 3. 设置测试模式(参数2为要设置的值)

int target_value = atoi(argv[2]);

if (set_test_pattern(fd, target_value) < 0) {

close(fd);

return -1;

}

// 4. 读取并验证设置结果

get_test_pattern(fd);

// 5. 关闭设备

close(fd);

return 0;

}

///

root@ATK-DLRK3568:/data# ./v4l2TestPt /dev/video0 3

==== Test Pattern Control Info ====

Name: Test Pattern

Min: 0, Max: 4, Default: 0

Menu Options:

0: Disabled

1: Vertical Color Bar Type 1

2: Vertical Color Bar Type 2

3: Vertical Color Bar Type 3

4: Vertical Color Bar Type 4

====================================

Set test pattern to: 3 (Vertical Color Bar Type 3)

Current test pattern: 3 (Vertical Color Bar Type 3)

open camera

//////////////////////////////////////////////////////////////////////////

这块注意到v4l2_ioctls里面其实是带有debug函数的.

(gdb) p v4l2_ioctls

$8 = {{ioctl = 2154321408, flags = 0, name = 0xffffff8009201eed "VIDIOC_QUERYCAP",

func = 0xffffff800898da1c <v4l_querycap>, debug = 0xffffff800898cd3c <v4l_print_querycap>}, {ioctl = 0, flags = 0,

name = 0x0, func = 0x0, debug = 0x0}, {ioctl = 3225441794, flags = 524288,

name = 0xffffff8009201efd "VIDIOC_ENUM_FMT", func = 0xffffff800898f950 <v4l_enum_fmt>,

debug = 0xffffff800898ccb4 <v4l_print_fmtdesc>}, {ioctl = 0, flags = 0, name = 0x0, func = 0x0, debug = 0x0}, {

//开启的条件如下

drivers/media/v4l2-core/v4l2-ioctl.c

static long __video_do_ioctl(struct file *file,

unsigned int cmd, void *arg)

{

...

ret = info->func(ops, file, fh, arg);

...

info->debug(arg, write_only); // enable debug when dev_debug !=0

}

// sys 子系统下需要kernel开启debug模式

root@ATK-DLRK3568:/# cat /sys/class/video4linux/video0/dev_debug

0

drivers/media/v4l2-core/v4l2-dev.c

static ssize_t dev_debug_store(struct device *cd, struct device_attribute *attr,

const char *buf, size_t len)

{

struct video_device *vdev = to_video_device(cd);

int res = 0;

u16 value;

res = kstrtou16(buf, 0, &value);

if (res)

return res;

vdev->dev_debug = value;

return len;

}

相关推荐
2301_765703142 小时前
C++代码复杂度控制
开发语言·c++·算法
m0_708830962 小时前
C++中的享元模式实战
开发语言·c++·算法
naruto_lnq2 小时前
分布式计算C++库
开发语言·c++·算法
m0_706653232 小时前
模板编译期排序算法
开发语言·c++·算法
历程里程碑2 小时前
Linxu14 进程一
linux·c语言·开发语言·数据结构·c++·笔记·算法
木井巳2 小时前
【递归算法】验证二叉搜索树
java·算法·leetcode·深度优先·剪枝
m0_561359672 小时前
嵌入式C++加密库
开发语言·c++·算法
近津薪荼2 小时前
优选算法——双指针专题7(单调性)
c++·学习·算法
j445566113 小时前
C++中的职责链模式实战
开发语言·c++·算法