Video4Linux框架简介(4) - v4l2_ioctl_ops

本文详细介绍了V4L2中常用的ioctl回调函数,包括输入设备枚举、设置和获取输入源、SDTV标准的获取与设置、DV定时功能、格式设置等。针对不同硬件需求,提供了具体实现示例。

译注:本节主要会介绍一些v4l2常用的ioctl回调函数,每个驱动开发者可根据硬件需要来做具体实现或者不做实现。

1. Input ioctls

vidioc_enum_input,顾名思义就是枚举输入,对于本示例驱动来说,有标准的S-Video输入和HDMI两种。

static int skeleton_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
if (i->index > 1)
return -EINVAL;
i->type = V4L2_INPUT_TYPE_CAMERA; //指定输入类型,有V4L2_INPUT_TYPE_CAMERA和V4L2_INPUT_TYPE_TUNER两种
if (i->index == 0) {
i->std = V4L2_STD_ALL;
strlcpy(i->name, "S-Video", sizeof(i->name));
i->capabilities = V4L2_IN_CAP_STD;
} else {
i->std = 0;
strlcpy(i->name, "HDMI", sizeof(i->name));
i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
}
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_enum_input = skeleton_enum_input, //可以和之前的vidioc_query_cap放在一起,后面的也一样
}; 

vidioc_g_input 和 vidioc_s_input

应用会调用这两个ioctl来设置和获取当前的input。

static int skeleton_s_input(struct file *file, void *priv, unsigned int i)
{
struct skeleton *skel = video_drvdata(file);
if (i > 1)
return -EINVAL;
skel->input = i;
skel->vdev.tvnorms = i ? 0 : V4L2_STD_ALL;
skeleton_fill_pix_format(skel, &skel->format); //和硬件相关
return 0;
}
static int skeleton_g_input(struct file *file, void *priv, unsigned int *i)
{
struct skeleton *skel = video_drvdata(file);
*i = skel->input;
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_g_input = skeleton_g_input,
.vidioc_s_input = skeleton_s_input,
}; 

2. SDTV Standards ioctls

vidioc_g_std和vidioc_s_std

获取/设置视频标准,在streaming状态下是不能做设置的。

static int skeleton_s_std(struct file *file, void *priv, v4l2_std_id std)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input)
return -ENODATA;
if (std == skel->std)
return 0;
/* TODO: handle changing std */
skel->std = std;
skeleton_fill_pix_format(skel, &skel->format);
return 0;
}
static int skeleton_g_std(struct file *file, void *priv, v4l2_std_id *std)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input)
return -ENODATA;
*std = skel->std;
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_g_std = skeleton_g_std,
.vidioc_s_std = skeleton_s_std,
}; 

vidioc_querystd

static int skeleton_querystd(struct file *file, void *priv,
v4l2_std_id *std)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input)
return -ENODATA;
/* TODO: Query currently seen standard. */
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_querystd = skeleton_querystd,
}; 

3. DV Timings ioctls

vidioc_dv_timings_cap

static const struct v4l2_dv_timings_cap skel_timings_cap = {
.type = V4L2_DV_BT_656_1120,
/* keep this initialization for compatibility with GCC < 4.4.6 */
.reserved = { 0 },
V4L2_INIT_BT_TIMINGS(
720, 1920, /* min/max width */
480, 1080, /* min/max height */
27000000, 74250000, /* min/max pixelclock*/
V4L2_DV_BT_STD_CEA861, /* Supported standards */
/* capabilities */
V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE
)
};
static int skeleton_dv_timings_cap(struct file *file, void *fh,
struct v4l2_dv_timings_cap *cap)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input == 0)
return -ENODATA;
*cap = skel_timings_cap;
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_dv_timings_cap = skeleton_dv_timings_cap,
}; 


vidioc_s_dv_timings

static int skeleton_s_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input == 0)
return -ENODATA;
if (!v4l2_valid_dv_timings(timings, &skel_timings_cap, NULL,
NULL))
return -EINVAL;
if (!v4l2_find_dv_timings_cap(timings, &skel_timings_cap, 0, NULL,
NULL))
return -EINVAL;
if (v4l2_match_dv_timings(timings, &skel->timings, 0))
return 0;
/* TODO: Configure new timings */
skel->timings = *timings;
skeleton_fill_pix_format(skel, &skel->format);
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_s_dv_timings = skeleton_s_dv_timings,
}; 

vidioc_g_dv_timings & vidioc_enum_dv_timings

static int skeleton_g_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input == 0)
return -ENODATA;
*timings = skel->timings;
return 0;
}
static int skeleton_enum_dv_timings(struct file *file, void *_fh,
struct v4l2_enum_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input == 0)
return -ENODATA;
return v4l2_enum_dv_timings_cap(timings, &skel_timings_cap, NULL, NULL);
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_g_dv_timings = skeleton_g_dv_timings,
.vidioc_enum_dv_timings = skeleton_enum_dv_timings,
}; 

vidioc_query_dv_timings

static int skeleton_query_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
if (skel->input == 0)
return -ENODATA;
/* TODO: Query currently seen timings. */
detect_timings();
if (no_signal)
return -ENOLINK;
if (cannot_lock_to_signal)
return -ENOLCK;
if (signal_out_of_range_of_capabilities)
return -ERANGE;
/* Useful for debugging */
if (debug)
v4l2_print_dv_timings(skel->v4l2_dev.name,
"query_dv_timings:",
timings, true);
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_query_dv_timings = skeleton_query_dv_timings,
}; 

4. Format ioctls

static int skeleton_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct skeleton *skel = video_drvdata(file);
int ret;
ret = skeleton_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
/* TODO: change format */
skel->format = f->fmt.pix;
return 0;
}
static int skeleton_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct skeleton *skel = video_drvdata(file);
f->fmt.pix = skel->format;
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_s_fmt_vid_cap = skeleton_s_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = skeleton_g_fmt_vid_cap,
};

static int skeleton_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index != 0)
return -EINVAL;
strlcpy(f->description, "4:2:2, packed, UYVY", sizeof(f-
>description));
f->pixelformat = V4L2_PIX_FMT_UYVY;
f->flags = 0;
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_enum_fmt_vid_cap = skeleton_enum_fmt_vid_cap,
};

static void skeleton_fill_pix_format(struct skeleton *skel, struct v4l2_pix_format *pix)
{
pix->pixelformat = V4L2_PIX_FMT_UYVY;
if (skel->input == 0) {
pix->width = 720;
pix->height = (skel->std & V4L2_STD_525_60) ? 480 : 576;
pix->field = V4L2_FIELD_INTERLACED;
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
} else {
pix->width = skel->timings.bt.width;
pix->height = skel->timings.bt.height;
if (skel->timings.bt.interlaced)
pix->field = V4L2_FIELD_INTERLACED;
else
pix->field = V4L2_FIELD_NONE;
pix->colorspace = V4L2_COLORSPACE_REC709;
}
pix->bytesperline = pix->width * 2;
pix->sizeimage = pix->bytesperline * pix->height;
pix->priv = 0;
}
static int skeleton_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
struct skeleton *skel = video_drvdata(file);
struct v4l2_pix_format *pix = &f->fmt.pix;
if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
return -EINVAL;
skeleton_fill_pix_format(skel, pix);
return 0;
}
static const struct v4l2_ioctl_ops skel_ioctl_ops = {
.vidioc_try_fmt_vid_cap = skeleton_try_fmt_vid_cap,
} 



<think>我们正在讨论Linux视频驱动V4L2)中的ioctl操作。用户之前询问了如何实现VIDIOC_CROPCAP,现在他想知道v4l2_ioctl_ops结构体的注册位置或方法。 根据引用[1]和[2],我们知道V4L2ioctl调用通过file_operations和v4l2_file_operations进行路由。具体来说,v4l2_ioctl_ops结构体是V4L2框架中处理各种ioctl命令的核心操作集。 注册v4l2_ioctl_ops结构体的位置通常在video_device结构体的初始化过程中。在V4L2驱动中,我们通常会创建一个video_device结构体,并将v4l2_ioctl_ops结构体赋值给它的ioctl_ops成员。 具体步骤: 1. 定义v4l2_ioctl_ops结构体: 在驱动代码中,我们会定义一个v4l2_ioctl_ops结构体实例,并填充各种ioctl处理函数指针(如vidioc_querycap, vidioc_enum_fmt_vid_cap, vidioc_g_fmt_vid_cap等)。 2. 初始化video_device结构体: 在驱动探测(probe)函数中,我们会初始化一个video_device结构体。其中,关键的一步就是将上一步定义的v4l2_ioctl_ops结构体赋值给video_device的ioctl_ops成员。 3. 注册video_device: 使用video_register_device函数注册video_device,这样V4L2框架就知道这个设备以及它的操作集了。 代码示例: ```c // 定义v4l2_ioctl_ops static const struct v4l2_ioctl_ops mydev_ioctl_ops = { .vidioc_querycap = mydev_vidioc_querycap, .vidioc_enum_fmt_vid_cap = mydev_vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = mydev_vidioc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = mydev_vidioc_s_fmt_vid_cap, .vidioc_cropcap = mydev_vidioc_cropcap, // 用户之前问的这个 // ... 其他ioctl操作 }; // 在probe函数中初始化并注册video_device static int mydev_probe(struct platform_device *pdev) { struct video_device *vdev; // ... 分配和初始化其他结构 vdev = video_device_alloc(); if (!vdev) { return -ENOMEM; } // 设置video_device的基本信息 strscpy(vdev->name, "My V4L2 Device", sizeof(vdev->name)); vdev->v4l2_dev = &dev->v4l2_dev; // 关联到v4l2_device vdev->fops = &mydev_fops; // 文件操作集 vdev->ioctl_ops = &mydev_ioctl_ops; // 这里注册ioctl_ops vdev->release = video_device_release; vdev->lock = &dev->mutex; // 互斥锁 // 设置设备类型,例如视频捕获设备 vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; // 注册video_device ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { video_device_release(vdev); return ret; } // ... 其他初始化 } ``` 注意:在较新的内核中,推荐使用video_device_alloc()video_register_device()来分配和注册video_device。另外,也可以使用video_device结构体的嵌入方式,但核心步骤相同,即设置ioctl_ops并注册。 引用[3]提到v4l2_device和v4l2_subdev,它们是V4L2框架中的核心结构体。在子设备(subdev)驱动中,ioctl操作可能通过v4l2_subdev_ops(引用[5])来处理,但这是另一个层次的操作集。对于主设备(video_device)的ioctl,我们关注的是v4l2_ioctl_ops。 总结:v4l2_ioctl_ops结构体的注册是通过将其赋值给video_device结构体的ioctl_ops成员,然后在驱动探测函数中调用video_register_device()注册video_device来完成的。 §§相关问题§§ 1. video_device结构体在V4L2驱动中扮演什么角色? 2. 如何实现V4L2驱动中的文件操作(fops)? 3. V4L2框架v4l2_device和video_device的关系是什么?</think>### V4L2驱动中`v4l2_ioctl_ops`结构体的注册方法 在Linux V4L2驱动框架中,`v4l2_ioctl_ops`结构体通过**video_device结构体**进行注册。以下是完整的注册流程和关键位置: #### 1. **定义ioctl操作集** 在驱动代码中定义`v4l2_ioctl_ops`结构体实例,填充具体的ioctl处理函数: ```c static const struct v4l2_ioctl_ops mydev_ioctl_ops = { .vidioc_querycap = mydev_vidioc_querycap, // 设备能力查询 .vidioc_enum_fmt_vid_cap = mydev_vidioc_enum_fmt, // 枚举格式 .vidioc_g_fmt_vid_cap = mydev_vidioc_g_fmt, // 获取格式 .vidioc_s_fmt_vid_cap = mydev_vidioc_s_fmt, // 设置格式 .vidioc_cropcap = mydev_vidioc_cropcap, // 裁剪能力 .vidioc_streamon = vb2_ioctl_streamon, // 启动流 .vidioc_streamoff = vb2_ioctl_streamoff, // 停止流 // ... 其他ioctl处理函数 }; ``` #### 2. **关联到video_device** 在**设备初始化阶段**(通常是`probe`函数)将ioctl_ops赋值给`video_device`: ```c static int mydev_probe(struct platform_device *pdev) { struct video_device *vdev; // 创建video_device vdev = video_device_alloc(); // 关键注册位置 vdev->ioctl_ops = &mydev_ioctl_ops; // 注册ioctl操作集 // 设置其他必要属性 vdev->fops = &mydev_fops; // 文件操作集 strscpy(vdev->name, "my_v4l2_device", sizeof(vdev->name)); vdev->v4l2_dev = &my_v4l2_dev; // 关联v4l2_device // 注册视频设备 video_register_device(vdev, VFL_TYPE_VIDEO, -1); } ``` #### 3. **注册流程解析** 1. **初始化阶段**: - 驱动在`probe()`函数中创建`video_device` - 将预定义的`v4l2_ioctl_ops`赋值给`video_device->ioctl_ops` - 调用`video_register_device()`注册设备[^1] 2. **调用链**(用户空间ioctl触发时): ```mermaid graph LR A[用户空间ioctl] --> B[v4l2_ioctl] B --> C[video_ioctl2] C --> D[__video_do_ioctl] D --> E[vdev->ioctl_ops->对应操作] ``` #### 4. **关键数据结构关系 ```c struct video_device { const struct v4l2_ioctl_ops *ioctl_ops; // 核心注册点 const struct v4l2_file_operations *fops; struct v4l2_device *v4l2_dev; // ... }; struct v4l2_ioctl_ops { int (*vidioc_querycap)(...); int (*vidioc_g_fmt_vid_cap)(...); int (*vidioc_s_fmt_vid_cap)(...); // ... 约50个操作函数[^2] }; ``` #### 5. **子设备特殊处理** 对于`v4l2_subdev`驱动ioctl操作,需通过**v4l2_subdev_ops**注册: ```c static const struct v4l2_subdev_video_ops my_subdev_video_ops = { .s_stream = mydev_s_stream, // 启动/停止流 }; static const struct v4l2_subdev_ops my_subdev_ops = { .core = &my_subdev_core_ops, .video = &my_subdev_video_ops, // 子设备操作集[^5] }; ``` > ⚠️ 注意:`v4l2_ioctl_ops`必须关联到`video_device`,而`v4l2_subdev_ops`关联到`v4l2_subdev`,两者属于不同层级[^3]。 --- ### 总结 `v4l2_ioctl_ops`的注册核心是: 1. 定义包含具体操作函数的`v4l2_ioctl_ops`实例 2. 在设备初始化时赋值给`video_device->ioctl_ops` 3. 通过`video_register_device()`完成注册 4. 子设备需通过`v4l2_subdev_ops`单独注册 典型位置在驱动的`probe()`函数中,通常在创建`video_device`后立即设置ioctl_ops[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值