v4l2框架
文章目录
- v4l2框架
- 框架
- 1.硬件相关层
- uvc_probe
- uvc_register_chains
- uvc_register_terms
- uvc_register_video
- 2.核心层
- __video_register_device
- 3.虚拟视频驱动vivid分析
- 入口vivid_init注册vivid平台驱动
- vivid_probe
- vivid_create_instance
框架
1.硬件相关层
driver/media/usb/uvc/uvc_driver.c
/*
* UVC 驱动结构体
*/
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo", // 驱动名
.probe = uvc_probe, // 探测函数
.disconnect = uvc_disconnect, // 断开连接函数
.suspend = uvc_suspend, // 挂起函数
.resume = uvc_resume, // 恢复函数
.reset_resume = uvc_reset_resume, // 重置恢复函数
.id_table = uvc_ids, // 设备 ID 表
.supports_autosuspend = 1, // 支持自动挂起
},
};
接入摄像头
driver/media/usb/uvc/uvc_driver.c
uvc_probe
int uvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
/* ------------------------------------------------------------------------
* USB probe, disconnect, suspend and resume
*/
static int uvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
// 获取 USB 设备
struct usb_device *udev = interface_to_usbdev(intf);
// 定义 UVC 设备
struct uvc_device *dev;
int ret;
// 如果 idVendor 和 idProduct 都存在,则打印已知 UVC 设备的信息
if (id->idVendor && id->idProduct)
uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
"(%04x:%04x)\n", udev->devpath, id->idVendor,
id->idProduct);
// 否则打印通用 UVC 设备的信息
else
uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
udev->devpath);
// 为设备分配内存并初始化
if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
return -ENOMEM;
// 初始化设备的各个链表
INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
atomic_set(&dev->nstreams, 0);
atomic_set(&dev->nmappings, 0);
mutex_init(&dev->lock);
// 获取 USB 设备
dev->udev = usb_get_dev(udev);
dev->intf = usb_get_intf(intf);
dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
dev->quirks = (uvc_quirks_param == -1)
? id->driver_info : uvc_quirks_param;
// 如果 USB 设备有产品名称,则使用该名称,否则使用默认名称
if (udev->product != NULL)
strlcpy(dev->name, udev->product, sizeof dev->name);
else
snprintf(dev->name, sizeof dev->name,
"UVC Camera (%04x:%04x)",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
// 解析 Video Class 控制描述符
if (uvc_parse_control(dev) < 0) {
uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
"descriptors.\n");
goto error;
}
// 打印 UVC 设备信息
uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
dev->uvc_version >> 8, dev->uvc_version & 0xff,
udev->product ? udev->product : "<unnamed>",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
if (dev->quirks != id->driver_info) {
// 如果设备 quirks 不等于驱动程序信息,则打印信息
uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
"parameter for testing purpose.\n", dev->quirks);
// 打印信息
uvc_printk(KERN_INFO, "Please report required quirks to the "
"linux-uvc-devel mailing list.\n");
}
/* Register the media and V4L2 devices. */
#ifdef CONFIG_MEDIA_CONTROLLER
// 设置 media 设备的信息
dev->mdev.dev = &intf->dev;
strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
if (udev->serial)
strlcpy(dev->mdev.serial, udev->serial,
sizeof(dev->mdev.serial));
strcpy(dev->mdev.bus_info, udev->devpath);
dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
dev->mdev.driver_version = LINUX_VERSION_CODE;
// 注册 media 设备
if (media_device_register(&dev->mdev) < 0)
goto error;
// 设置 v4l2 设备的信息
dev->vdev.mdev = &dev->mdev;
#endif
// 注册 v4l2 设备
if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
goto error;
/* Initialize controls. */
// 初始化控制器
if (uvc_ctrl_init_device(dev) < 0)
goto error;
/* Scan the device for video chains. */
// 扫描设备的视频链
if (uvc_scan_device(dev) < 0)
goto error;
/* Register video device nodes. */
// 注册视频设备节点
if (uvc_register_chains(dev) < 0)
goto error;
/* Save our data pointer in the interface data. */
// 在接口数据中保存数据指针
usb_set_intfdata(intf, dev);
/* Initialize the interrupt URB. */
// 初始化中断 URB
if ((ret = uvc_status_init(dev)) < 0) {
uvc_printk(KERN_INFO, "Unable to initialize the status "
"endpoint (%d), status interrupt will not be "
"supported.\n", ret);
}
// 打印 UVC 设备初始化信息
uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
// 启用 USB 自动挂起
usb_enable_autosuspend(udev);
return 0;
error:
// 注销视频设备
uvc_unregister_video(dev);
return -ENODEV;
}
uvc_register_chains
uvc_probe
uvc_register_chains
/*
* 注册所有的视频链
*/
static int uvc_register_chains(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
int ret;
// 遍历所有的视频链
list_for_each_entry(chain, &dev->chains, list) {
// 注册视频链中的所有终端
ret = uvc_register_terms(dev, chain);
if (ret < 0)
return ret;
#ifdef CONFIG_MEDIA_CONTROLLER
// 如果支持 media controller,则注册实体
ret = uvc_mc_register_entities(chain);
if (ret < 0) {
uvc_printk(KERN_INFO, "Failed to register entites "
"(%d).\n", ret);
}
#endif
}
return 0;
}
uvc_register_terms
uvc_probe
uvc_register_chains
uvc_register_terms
/*
* Register all video devices in all chains.
*/
/*
* 注册视频链中的所有终端
*/
static int uvc_register_terms(struct uvc_device *dev,
struct uvc_video_chain *chain)
{
struct uvc_streaming *stream;
struct uvc_entity *term;
int ret;
// 遍历视频链中的所有实体
list_for_each_entry(term, &chain->entities, chain) {
// 如果实体不是流式传输终端,则跳过
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
continue;
// 根据终端 ID 获取流式传输结构体
stream = uvc_stream_by_id(dev, term->id);
if (stream == NULL) {
uvc_printk(KERN_INFO, "No streaming interface found "
"for terminal %u.", term->id);
continue;
}
// 将流式传输结构体与视频链关联
stream->chain = chain;
// 注册视频设备
ret = uvc_register_video(dev, stream);
if (ret < 0)
return ret;
// 将视频设备与终端关联
term->vdev = &stream->vdev;
}
return 0;
}
uvc_register_video
uvc_probe
uvc_register_chains
uvc_register_terms
uvc_register_video
static int uvc_register_video(struct uvc_device *dev,
struct uvc_streaming *stream)
{
// 初始化视频缓冲区队列
int ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
if (ret)
return ret;
// 使用默认的流式传输参数初始化流式传输接口
ret = uvc_video_init(stream);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device "
"(%d).\n", ret);
return ret;
}
// 初始化调试文件系统
uvc_debugfs_init_stream(stream);
// 在 V4L 中注册设备
// 我们已经持有对 dev->udev 的引用。在引用被释放之前,视频设备将被注销,因此我们不需要获取另一个引用。
struct video_device *vdev = &stream->vdev;
vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops;
vdev->ioctl_ops = &uvc_ioctl_ops;
vdev->release = uvc_release;
vdev->prio = &stream->chain->prio;
if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vdev->vfl_dir = VFL_DIR_TX;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
// 在调用 video_register_device 之前设置驱动程序数据,否则 uvc_v4l2_open 可能会与我们竞争。
video_set_drvdata(vdev, stream);
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
ret);
return ret;
}
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE;
else
stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
atomic_inc(&dev->nstreams);
return 0;
}
2.核心层
driver/media/v4l2-core.c
__video_register_device
uvc_probe
uvc_register_chains
uvc_register_terms
uvc_register_video
__video_register_device
/*
__video_register_device函数用于在内核中注册video4linux设备。它根据请求的类型分配
次要号和设备节点号,并将新的设备节点注册到内核中。
*/
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
// 初始化变量
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
// A minor value of -1 marks this video device as never having been registered
// 初始化 minor 值为 -1
vdev->minor = -1;
// the release callback MUST be present
// 检查 release 回调是否存在
if (WARN_ON(!vdev->release))
return -EINVAL;
// the v4l2_dev pointer MUST be present
// 检查 v4l2_dev 指针是否存在
if (WARN_ON(!vdev->v4l2_dev))
return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock); // 初始化锁
INIT_LIST_HEAD(&vdev->fh_list); // 初始化链表
/* Part 1: check device type */
switch (type) { // 根据设备类型选择设备名
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
case VFL_TYPE_SDR:
/* Use device name 'swradio' because 'sdr' was already taken. */
name_base = "swradio";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type); // 打印错误信息
return -EINVAL;
}
// 设置设备类型
vdev->vfl_type = type;
// 初始化字符设备指针
vdev->cdev = NULL;
// 如果设备的父设备指针为空,则将其指向 v4l2_dev 的设备指针
if (vdev->dev_parent == NULL)
vdev->dev_parent = vdev->v4l2_dev->dev;
// 如果控制处理程序指针为空,则将其指向 v4l2_dev 的控制处理程序指针
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* 如果优先级状态指针为空,则使用 v4l2_device 的优先级状态。*/
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 为前四种类型保留范围,出于历史原因。
* 新设备(尚未就位)应使用范围
* 128-191,只需在那里选择第一个空闲的次要设备
* (新样式)。 */
switch (type) {
case VFL_TYPE_GRABBER: // 如果设备类型为 VFL_TYPE_GRABBER
minor_offset = 0; // 次设备号偏移量为 0
minor_cnt = 64; // 次设备号数量为 64
break;
case VFL_TYPE_RADIO: // 如果设备类型为 VFL_TYPE_RADIO
minor_offset = 64; // 次设备号偏移量为 64
minor_cnt = 64; // 次设备号数量为 64
break;
case VFL_TYPE_VBI: // 如果设备类型为 VFL_TYPE_VBI
minor_offset = 224; // 次设备号偏移量为 224
minor_cnt = 32; // 次设备号数量为 32
break;
default: // 如果设备类型为其他类型
minor_offset = 128; // 次设备号偏移量为 128
minor_cnt = 64; // 次设备号数量为 64
break;
}
#endif
/* 选择设备节点号 */
mutex_lock(&videodev_lock); // 加锁
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); // 查找设备节点号
if (nr == minor_cnt) // 如果找不到空闲的设备节点号
nr = devnode_find(vdev, 0, minor_cnt); // 从头开始查找
if (nr == minor_cnt) { // 如果还是找不到空闲的设备节点号
printk(KERN_ERR "could not get a free device node number\n"); // 打印错误信息
mutex_unlock(&videodev_lock); // 解锁
return -ENFILE; // 返回错误码
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr; // 将设备节点号赋值给 i
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++) // 遍历所有设备
if (video_device[i] == NULL) // 如果设备未被占用
break; // 跳出循环
if (i == VIDEO_NUM_DEVICES) { // 如果所有设备都被占用
mutex_unlock(&videodev_lock); // 解锁
printk(KERN_ERR "could not get a free minor\n"); // 打印错误信息
return -ENFILE; // 返回错误码
}
#endif
vdev->minor = i + minor_offset; // 设置设备节点号
vdev->num = nr; // 设置设备编号
devnode_set(vdev); // 设置设备节点
/* Should not happen since we thought this minor was free */
WARN_ON(video_device[vdev->minor] != NULL); // 如果设备节点已被占用,打印警告信息
vdev->index = get_index(vdev); // 获取设备索引
video_device[vdev->minor] = vdev; // 将设备添加到 video_device 数组中
mutex_unlock(&videodev_lock); // 解锁
if (vdev->ioctl_ops)
determine_valid_ioctls(vdev); // 确定设备支持的 ioctl 操作
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc(); // 分配 cdev 结构体
if (vdev->cdev == NULL) { // 如果分配失败
ret = -ENOMEM; // 返回错误码
goto cleanup; // 跳转到 cleanup 标签处
}
vdev->cdev->ops = &v4l2_fops; // 设置 cdev 的操作函数
vdev->cdev->owner = owner; // 设置 cdev 的 owner
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); // 添加 cdev
if (ret < 0) { // 如果添加失败
printk(KERN_ERR "%s: cdev_add failed\n", __func__); // 打印错误信息
kfree(vdev->cdev); // 释放 cdev
vdev->cdev = NULL; // 将 cdev 置为空
goto cleanup; // 跳转到 cleanup 标签处
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class; // 设置设备的 class
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); // 设置设备的 devt
vdev->dev.parent = vdev->dev_parent; // 设置设备的 parent
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); // 设置设备的名字
ret = device_register(&vdev->dev); // 注册设备
if (ret < 0) { // 如果注册失败
printk(KERN_ERR "%s: device_register failed\n", __func__); // 打印错误信息
goto cleanup; // 跳转到 cleanup 标签处
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release; // 设置设备的 release 回调函数
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev)); // 打印警告信息
/* Increase v4l2_device refcount */
v4l2_device_get(vdev->v4l2_dev); // 增加 v4l2_device 的引用计数
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev->mdev && // 如果 v4l2_dev 的 mdev 不为空
vdev->vfl_type != VFL_TYPE_SUBDEV) { // 如果 vfl_type 不是 VFL_TYPE_SUBDEV
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; // 设置 entity 的 type
vdev->entity.name = vdev->name; // 设置 entity 的 name
vdev->entity.info.dev.major = VIDEO_MAJOR; // 设置 entity 的 major
vdev->entity.info.dev.minor = vdev->minor; // 设置 entity 的 minor
ret = media_device_register_entity(vdev->v4l2_dev->mdev, // 注册 entity
&vdev->entity);
if (ret < 0) // 如果注册失败
printk(KERN_WARNING
"%s: media_device_register_entity failed\n",
__func__); // 打印警告信息
}
#endif
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags); // 设置设备已注册标志位
return 0; // 返回 0
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
video_device[vdev->minor] = NULL;
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc(); // 分配 cdev 结构体
if (vdev->cdev == NULL) { // 如果分配失败
ret = -ENOMEM; // 返回错误码
goto cleanup; // 跳转到 cleanup 标签处
}
vdev->cdev->ops = &v4l2_fops; // 设置 cdev 的操作函数
vdev->cdev->owner = owner; // 设置 cdev 的 owner
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); // 添加 cdev
if (ret < 0) { // 如果添加失败
printk(KERN_ERR "%s: cdev_add failed\n", __func__); // 打印错误信息
kfree(vdev->cdev); // 释放 cdev
vdev->cdev = NULL; // 将 cdev 置为空
goto cleanup; // 跳转到 cleanup 标签处
}
移除摄像头
3.虚拟视频驱动vivid分析
drivers/media/platform/vivd/vivid-core.c
平台设备驱动
static struct platform_driver vivid_pdrv = {
.probe = vivid_probe,
.remove = vivid_remove,
.driver = {
.name = "vivid",
},
};
入口vivid_init注册vivid平台驱动
static int __init vivid_init(void)
{
int ret;
// 注册vivid平台设备
ret = platform_device_register(&vivid_pdev);
if (ret)
return ret;
// 注册vivid平台驱动
ret = platform_driver_register(&vivid_pdrv);
if (ret)
platform_device_unregister(&vivid_pdev);
return ret;
}
vivid_probe
vivid_init
platform_driver_register
匹配后调用vivid_probe
/* This routine allocates from 1 to n_devs virtual drivers.
The real maximum number of virtual drivers will depend on how many drivers
will succeed. This is limited to the maximum number of devices that
videodev supports, which is equal to VIDEO_NUM_DEVICES.
*/
static int vivid_probe(struct platform_device *pdev)
{
// 查找字体
const struct font_desc *font = find_font("VGA8x16");
// 如果找不到字体,返回错误
if (font == NULL) {
pr_err("vivid: could not find font\n");
return -ENODEV;
}
// 设置字体
tpg_set_font(font->data);
// 限制设备数量在1到VIVID_MAX_DEVS之间
n_devs = clamp_t(unsigned, n_devs, 1, VIVID_MAX_DEVS);
// 创建n_devs个设备实例
for (unsigned i = 0; i < n_devs; i++) {
// 创建设备实例
int ret = vivid_create_instance(pdev, i);
// 如果创建失败,如果已经创建了一些实例,保留这些实例,否则返回错误
if (ret) {
if (i)
ret = 0;
break;
}
}
// 如果创建失败,返回错误
if (ret < 0) {
pr_err("vivid: error %d while loading driver\n", ret);
return ret;
}
// n_devs将反映实际分配的设备数量
n_devs = i;
return ret;
}
vivid_create_instance
vivid_probe
vivid_create_instance创建设备实例
static int vivid_create_instance(struct platform_device *pdev, int inst)
{
// 定义默认的视频格式
static const struct v4l2_dv_timings def_dv_timings =
V4L2_DV_BT_CEA_1280X720P60;
// 计数器
unsigned in_type_counter[4] = { 0, 0, 0, 0 };
unsigned out_type_counter[4] = { 0, 0, 0, 0 };
// 是否支持ccs_cap_mode和ccs_out_mode
int ccs_cap = ccs_cap_mode[inst];
int ccs_out = ccs_out_mode[inst];
// 是否有调谐器和调制器
bool has_tuner;
bool has_modulator;
// 定义vivid_dev和video_device结构体
struct vivid_dev *dev;
struct video_device *vfd;
// 定义vb2_queue结构体
struct vb2_queue *q;
// 节点类型
unsigned node_type = node_types[inst];
// 视频标准
v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0;
int ret;
int i;
// 分配vivid_dev结构体
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->inst = inst;
// 注册v4l2_device
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", VIVID_MODULE_NAME, inst);
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
kfree(dev);
return ret;
}
dev->v4l2_dev.release = vivid_dev_release;
/* start detecting feature set */
/* do we use single- or multi-planar? */
// 判断是否使用多平面
dev->multiplanar = multiplanar[inst] > 1;
// 输出使用的格式
v4l2_info(&dev->v4l2_dev, "using %splanar format API\n",
dev->multiplanar ? "multi" : "single ");
/* how many inputs do we have and of what type? */
// 获取输入的数量和类型
dev->num_inputs = num_inputs[inst];
if (dev->num_inputs < 1)
dev->num_inputs = 1;
if (dev->num_inputs >= MAX_INPUTS)
dev->num_inputs = MAX_INPUTS;
for (i = 0; i < dev->num_inputs; i++) {
// 获取输入的类型
dev->input_type[i] = (input_types[inst] >> (i * 2)) & 0x3;
// 获取输入的名称计数器
dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++;
}
// 判断是否有音频输入
dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID];
/* how many outputs do we have and of what type? */
// 获取输出的数量和类型
dev->num_outputs = num_outputs[inst];
if (dev->num_outputs < 1)
dev->num_outputs = 1;
if (dev->num_outputs >= MAX_OUTPUTS)
dev->num_outputs = MAX_OUTPUTS;
for (i = 0; i < dev->num_outputs; i++) {
// 获取输出的类型
dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID;
// 获取输出的名称计数器
dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
}
// 判断是否有音频输出
dev->has_audio_outputs = out_type_counter[SVID];
/* do we create a video capture device? */
// 判断是否创建视频捕获设备
dev->has_vid_cap = node_type & 0x0001;
/* do we create a vbi capture device? */
// 判断是否创建VBI捕获设备
if (in_type_counter[TV] || in_type_counter[SVID]) {
dev->has_raw_vbi_cap = node_type & 0x0004;
dev->has_sliced_vbi_cap = node_type & 0x0008;
dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap;
}
/* do we create a video output device? */
// 判断是否创建视频输出设备
dev->has_vid_out = node_type & 0x0100;
/* do we create a vbi output device? */
// 判断是否创建VBI输出设备
if (out_type_counter[SVID]) {
dev->has_raw_vbi_out = node_type & 0x0400;
dev->has_sliced_vbi_out = node_type & 0x0800;
dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out;
}
/* do we create a radio receiver device? */
// 判断是否创建无线电接收器设备
dev->has_radio_rx = node_type & 0x0010;
/* do we create a radio transmitter device? */
// 判断是否创建无线电发射器设备
dev->has_radio_tx = node_type & 0x1000;
/* do we create a software defined radio capture device? */
// 判断是否创建软件定义无线电捕获设备
dev->has_sdr_cap = node_type & 0x0020;
/* do we have a tuner? */
// 判断是否有调谐器
has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) ||
dev->has_radio_rx || dev->has_sdr_cap;
/* do we have a modulator? */
// 判断是否有调制器
has_modulator = dev->has_radio_tx;
if (dev->has_vid_cap)
/* do we have a framebuffer for overlay testing? */
// 判断是否有用于叠加测试的帧缓冲区
dev->has_fb = node_type & 0x10000;
/* do we create a video capture device? */
// 判断是否创建视频捕获设备
dev->has_vid_cap = node_type & 0x0001;
/* do we create a vbi capture device? */
// 判断是否创建VBI捕获设备
if (in_type_counter[TV] || in_type_counter[SVID]) {
dev->has_raw_vbi_cap = node_type & 0x0004;
dev->has_sliced_vbi_cap = node_type & 0x0008;
dev->has_vbi_cap = dev->has_raw_vbi_cap | dev->has_sliced_vbi_cap;
}
/* do we create a video output device? */
// 判断是否创建视频输出设备
dev->has_vid_out = node_type & 0x0100;
/* do we create a vbi output device? */
// 判断是否创建VBI输出设备
if (out_type_counter[SVID]) {
dev->has_raw_vbi_out = node_type & 0x0400;
dev->has_sliced_vbi_out = node_type & 0x0800;
dev->has_vbi_out = dev->has_raw_vbi_out | dev->has_sliced_vbi_out;
}
/* do we create a radio receiver device? */
// 判断是否创建无线电接收器设备
dev->has_radio_rx = node_type & 0x0010;
/* do we create a radio transmitter device? */
// 判断是否创建无线电发射器设备
dev->has_radio_tx = node_type & 0x1000;
/* do we create a software defined radio capture device? */
// 判断是否创建软件定义无线电捕获设备
dev->has_sdr_cap = node_type & 0x0020;
/* do we have a tuner? */
// 判断是否有调谐器
has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) ||
dev->has_radio_rx || dev->has_sdr_cap;
/* do we have a modulator? */
// 判断是否有调制器
has_modulator = dev->has_radio_tx;
if (dev->has_vid_cap)
/* do we have a framebuffer for overlay testing? */
// 判断是否有用于叠加测试的帧缓冲区
dev->has_fb = node_type & 0x10000;
/* can we do crop/compose/scaling while capturing? */
// 判断是否可以在捕获时进行裁剪/组合/缩放
if (no_error_inj && ccs_cap == -1)
ccs_cap = 7;
/* if ccs_cap == -1, then the use can select it using controls */
// 如果ccs_cap == -1,则用户可以使用控件进行选择
if (ccs_cap != -1) {
dev->has_crop_cap = ccs_cap & 1;
dev->has_compose_cap = ccs_cap & 2;
dev->has_scaler_cap = ccs_cap & 4;
v4l2_info(&dev->v4l2_dev, "Capture Crop: %c Compose: %c Scaler: %c\n",
dev->has_crop_cap ? 'Y' : 'N',
dev->has_compose_cap ? 'Y' : 'N',
dev->has_scaler_cap ? 'Y' : 'N');
}
/* can we do crop/compose/scaling with video output? */
// 判断是否可以在视频输出时进行裁剪/组合/缩放
if (no_error_inj && ccs_out == -1)
ccs_out = 7;
/* if ccs_out == -1, then the use can select it using controls */
// 如果ccs_out == -1,则用户可以使用控件进行选择
if (ccs_out != -1) {
dev->has_crop_out = ccs_out & 1;
dev->has_compose_out = ccs_out & 2;
dev->has_scaler_out = ccs_out & 4;
v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n",
dev->has_crop_out ? 'Y' : 'N',
dev->has_compose_out ? 'Y' : 'N',
dev->has_scaler_out ? 'Y' : 'N');
}
/* end detecting feature set */
// 如果有视频捕获设备
if (dev->has_vid_cap) {
/* set up the capabilities of the video capture device */
// 设置视频捕获设备的功能
dev->vid_cap_caps = dev->multiplanar ?
V4L2_CAP_VIDEO_CAPTURE_MPLANE :
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY;
dev->vid_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
// 如果有音频输入
if (dev->has_audio_inputs)
dev->vid_cap_caps |= V4L2_CAP_AUDIO;
// 如果有电视输入
if (in_type_counter[TV])
dev->vid_cap_caps |= V4L2_CAP_TUNER;
}
// 如果有视频输出设备
if (dev->has_vid_out) {
/* set up the capabilities of the video output device */
// 设置视频输出设备的功能
dev->vid_out_caps = dev->multiplanar ?
V4L2_CAP_VIDEO_OUTPUT_MPLANE :
V4L2_CAP_VIDEO_OUTPUT;
// 如果有帧缓存
if (dev->has_fb)
dev->vid_out_caps |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
dev->vid_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
// 如果有音频输出
if (dev->has_audio_outputs)
dev->vid_out_caps |= V4L2_CAP_AUDIO;
}
// 如果有VBI捕获设备
if (dev->has_vbi_cap) {
/* set up the capabilities of the vbi capture device */
// 设置VBI捕获设备的功能
dev->vbi_cap_caps = (dev->has_raw_vbi_cap ? V4L2_CAP_VBI_CAPTURE : 0) |
(dev->has_sliced_vbi_cap ? V4L2_CAP_SLICED_VBI_CAPTURE : 0);
dev->vbi_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
// 如果有音频输入
if (dev->has_audio_inputs)
dev->vbi_cap_caps |= V4L2_CAP_AUDIO;
// 如果有电视输入
if (in_type_counter[TV])
dev->vbi_cap_caps |= V4L2_CAP_TUNER;
}
// 如果有VBI输出设备
if (dev->has_vbi_out) {
/* set up the capabilities of the vbi output device */
// 设置VBI输出设备的功能
dev->vbi_out_caps = (dev->has_raw_vbi_out ? V4L2_CAP_VBI_OUTPUT : 0) |
(dev->has_sliced_vbi_out ? V4L2_CAP_SLICED_VBI_OUTPUT : 0);
dev->vbi_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
// 如果有音频输出
if (dev->has_audio_outputs)
dev->vbi_out_caps |= V4L2_CAP_AUDIO;
}
// 如果有SDR捕获设备
if (dev->has_sdr_cap) {
/* set up the capabilities of the sdr capture device */
// 设置SDR捕获设备的功能
dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER;
dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
}
/* set up the capabilities of the radio receiver device */
// 设置无线电接收设备的功能
if (dev->has_radio_rx)
dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE |
V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
V4L2_CAP_READWRITE;
/* set up the capabilities of the radio transmitter device */
// 设置无线电发射设备的功能
if (dev->has_radio_tx)
dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR |
V4L2_CAP_READWRITE;
/* initialize the test pattern generator */
// 初始化测试图案生成器
tpg_init(&dev->tpg, 640, 360);
if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH))
goto free_dev;
dev->scaled_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
if (!dev->scaled_line)
goto free_dev;
dev->blended_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
if (!dev->blended_line)
goto free_dev;
/* load the edid */
// 加载EDID
dev->edid = vmalloc(256 * 128);
if (!dev->edid)
goto free_dev;
/* create a string array containing the names of all the preset timings */
// 创建一个包含所有预设时间名称的字符串数组
while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width)
dev->query_dv_timings_size++;
dev->query_dv_timings_qmenu = kmalloc(dev->query_dv_timings_size *
(sizeof(void *) + 32), GFP_KERNEL);
if (dev->query_dv_timings_qmenu == NULL)
goto free_dev;
for (i = 0; i < dev->query_dv_timings_size; i++) {
const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
char *p = (char *)&dev->query_dv_timings_qmenu[dev->query_dv_timings_size];
u32 htot, vtot;
p += i * 32;
dev->query_dv_timings_qmenu[i] = p;
htot = V4L2_DV_BT_FRAME_WIDTH(bt);
vtot = V4L2_DV_BT_FRAME_HEIGHT(bt);
snprintf(p, 32, "%ux%u%s%u",
bt->width, bt->height, bt->interlaced ? "i" : "p",
(u32)bt->pixelclock / (htot * vtot));
}
/* disable invalid ioctls based on the feature set */
if (!dev->has_audio_inputs) {
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_AUDIO);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMAUDIO);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO);
}
if (!dev->has_audio_outputs) {
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_AUDOUT);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMAUDOUT);
v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_AUDOUT);
v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_AUDOUT);
v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_ENUMAUDOUT);
}
if (!in_type_counter[TV] && !in_type_counter[SVID]) {
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_STD);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_STD);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUMSTD);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERYSTD);
}
if (!out_type_counter[SVID]) {
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_STD);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_STD);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUMSTD);
}
if (!has_tuner && !has_modulator) {
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_FREQUENCY);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY);
}
if (!has_tuner) {
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER);
}
if (in_type_counter[HDMI] == 0) {
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_EDID);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_DV_TIMINGS_CAP);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_DV_TIMINGS);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_DV_TIMINGS);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_ENUM_DV_TIMINGS);
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_QUERY_DV_TIMINGS);
}
if (out_type_counter[HDMI] == 0) {
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_EDID);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_DV_TIMINGS_CAP);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_DV_TIMINGS);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_DV_TIMINGS);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_DV_TIMINGS);
}
if (!dev->has_fb) {
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FBUF);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FBUF);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_OVERLAY);
}
v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES);
v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMEINTERVALS);
v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_S_FREQUENCY);
v4l2_disable_ioctl(&dev->vbi_out_dev, VIDIOC_G_FREQUENCY);
/* configure internal data */
// 设置内部数据
dev->fmt_cap = &vivid_formats[0];
// 设置输入格式
dev->fmt_out = &vivid_formats[0];
// 设置输出格式
if (!dev->multiplanar)
// 如果不是多平面,则数据偏移为0
vivid_formats[0].data_offset[0] = 0;
dev->webcam_size_idx = 1;
// 设置webcam的大小
dev->webcam_ival_idx = 3;
// 设置webcam的间隔
tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
// 设置tpg的fourcc
dev->std_cap = V4L2_STD_PAL;
// 设置输入标准为PAL
dev->std_out = V4L2_STD_PAL;
// 设置输出标准为PAL
if (dev->input_type[0] == TV || dev->input_type[0] == SVID)
// 如果输入类型为TV或SVID,则tvnorms_cap为V4L2_STD_ALL
tvnorms_cap = V4L2_STD_ALL;
if (dev->output_type[0] == SVID)
// 如果输出类型为SVID,则tvnorms_out为V4L2_STD_ALL
tvnorms_out = V4L2_STD_ALL;
dev->dv_timings_cap = def_dv_timings;
// 设置输入dv_timings为默认值
dev->dv_timings_out = def_dv_timings;
// 设置输出dv_timings为默认值
dev->tv_freq = 2804 /* 175.25 * 16 */;
// 设置tv的频率
dev->tv_audmode = V4L2_TUNER_MODE_STEREO;
// 设置tv的音频模式为立体声
dev->tv_field_cap = V4L2_FIELD_INTERLACED;
// 设置tv的场为交错场
dev->tv_field_out = V4L2_FIELD_INTERLACED;
// 设置输出tv的场为交错场
dev->radio_rx_freq = 95000 * 16;
// 设置收音机的接收频率
dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO;
// 设置收音机的音频模式为立体声
if (dev->has_radio_tx) {
dev->radio_tx_freq = 95500 * 16;
// 如果有收音机发射,则设置发射频率
dev->radio_rds_loop = false;
}
dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS;
// 设置收音机的子通道
dev->sdr_adc_freq = 300000;
// 设置sdr的adc频率
dev->sdr_fm_freq = 50000000;
// 设置sdr的fm频率
dev->edid_max_blocks = dev->edid_blocks = 2;
// 设置edid的最大块数和块数
memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid));
// 复制vivid_hdmi_edid到edid
ktime_get_ts(&dev->radio_rds_init_ts);
/* create all controls */
// 创建所有控件
ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj,
in_type_counter[TV] || in_type_counter[SVID] ||
out_type_counter[SVID],
in_type_counter[HDMI] || out_type_counter[HDMI]);
if (ret)
goto unreg_dev;
/*
* update the capture and output formats to do a proper initial
* configuration.
*/
// 更新捕获和输出格式以进行正确的初始配置。
vivid_update_format_cap(dev, false);
vivid_update_format_out(dev);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx);
v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap);
/* initialize overlay */
// 初始化叠加层
dev->fb_cap.fmt.width = dev->src_rect.width;
dev->fb_cap.fmt.height = dev->src_rect.height;
dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc;
dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2;
dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline;
/* initialize locks */
// 初始化锁
spin_lock_init(&dev->slock);
mutex_init(&dev->mutex);
/* init dma queues */
// 初始化dma队列
INIT_LIST_HEAD(&dev->vid_cap_active);
INIT_LIST_HEAD(&dev->vid_out_active);
INIT_LIST_HEAD(&dev->vbi_cap_active);
INIT_LIST_HEAD(&dev->vbi_out_active);
INIT_LIST_HEAD(&dev->sdr_cap_active);
/* start creating the vb2 queues */
// 开始创建vb2队列
if (dev->has_vid_cap) {
/* initialize vid_cap queue */
// 初始化vid_cap队列
q = &dev->vb_vid_cap_q;
q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vid_cap_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
goto unreg_dev;
}
if (dev->has_vid_out) {
/* 初始化vid_out队列 */
q = &dev->vb_vid_out_q;
q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vid_out_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
goto unreg_dev;
}
if (dev->has_vbi_cap) {
/* 初始化vbi_cap队列 */
q = &dev->vb_vbi_cap_q;
q->type = dev->has_raw_vbi_cap ? V4L2_BUF_TYPE_VBI_CAPTURE :
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vbi_cap_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
goto unreg_dev;
}
if (dev->has_vbi_out) {
/* 初始化vbi_out队列 */
q = &dev->vb_vbi_out_q;
q->type = dev->has_raw_vbi_out ? V4L2_BUF_TYPE_VBI_OUTPUT :
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_vbi_out_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
goto unreg_dev;
}
if (dev->has_sdr_cap) {
/* 初始化sdr_cap队列 */
q = &dev->vb_sdr_cap_q;
q->type = V4L2_BUF_TYPE_SDR_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivid_buffer);
q->ops = &vivid_sdr_cap_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 8;
q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
goto unreg_dev;
}
if (dev->has_fb) {
/* 为测试捕获/输出叠加创建帧缓冲区 */
ret = vivid_fb_init(dev);
if (ret)
goto unreg_dev;
v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n",
dev->fb_info.node);
}
/* 最后开始创建设备节点 */
if (dev->has_vid_cap) {
vfd = &dev->vid_cap_dev;
strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vid_cap_q;
vfd->tvnorms = tvnorms_cap;
/*
* 提供一个互斥锁给v4l2核心。它将用于保护所有fops和v4l2 ioctls。
*/
vfd->lock = &dev->mutex;
video_set_drvdata(vfd, dev);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
if (ret < 0)
goto unreg_dev;
v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
video_device_node_name(vfd));
}
// 如果设备支持视频采集
if (dev->has_vid_out) {
vfd = &dev->vid_out_dev;
strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name));
vfd->vfl_dir = VFL_DIR_TX;
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vid_out_q;
vfd->tvnorms = tvnorms_out;
/*
* 提供一个互斥锁给v4l2核心。它将用于保护所有fops和v4l2 ioctls。
*/
vfd->lock = &dev->mutex;
video_set_drvdata(vfd, dev);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
if (ret < 0)
goto unreg_dev;
v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n",
video_device_node_name(vfd));
}
// 如果设备支持VBI采集
if (dev->has_vbi_cap) {
vfd = &dev->vbi_cap_dev;
strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
vfd->fops = &vivid_fops;
vfd->ioctl_ops = &vivid_ioctl_ops;
vfd->release = video_device_release_empty;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->queue = &dev->vb_vbi_cap_q;
vfd->lock = &dev->mutex;
vfd->tvnorms = tvnorms_cap;
video_set_drvdata(vfd, dev);
ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]);
if (ret < 0)
goto unreg_dev;
v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s, supports %s VBI\n",
video_device_node_name(vfd),
(dev->has_raw_vbi_cap && dev->has_sliced_vbi_cap) ?
"raw and sliced" :
(dev->has_raw_vbi_cap ? "raw" : "sliced"));
}
if (dev->has_vbi_out) {
// 如果设备支持VBI输出
vfd = &dev->vbi_out_dev;
// 设置设备名称
strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name));
// 设置设备方向为发送
vfd->vfl_dir = VFL_DIR_TX;
// 设置文件操作
vfd->fops = &vivid_fops;
// 设置ioctl操作
vfd->ioctl_ops = &vivid_ioctl_ops;
// 设置设备释放函数
vfd->release = video_device_release_empty;
// 设置v4l2设备
vfd->v4l2_dev = &dev->v4l2_dev;
// 设置队列
vfd->queue = &dev->vb_vbi_out_q;
// 设置锁
vfd->lock = &dev->mutex;
// 设置视频标准
vfd->tvnorms = tvnorms_out;
// 设置设备私有数据
video_set_drvdata(vfd, dev);
// 注册设备
ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]);
if (ret < 0)
goto unreg_dev;
// 打印设备信息
v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s, supports %s VBI\n",
video_device_node_name(vfd),
(dev->has_raw_vbi_out && dev->has_sliced_vbi_out) ?
"raw and sliced" :
(dev->has_raw_vbi_out ? "raw" : "sliced"));
}
if (dev->has_sdr_cap) {
// 如果设备支持SDR采集
vfd = &dev->sdr_cap_dev;
// 设置设备名称
strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
// 设置文件操作
vfd->fops = &vivid_fops;
// 设置ioctl操作
vfd->ioctl_ops = &vivid_ioctl_ops;
// 设置设备释放函数
vfd->release = video_device_release_empty;
// 设置v4l2设备
vfd->v4l2_dev = &dev->v4l2_dev;
// 设置队列
vfd->queue = &dev->vb_sdr_cap_q;
// 设置锁
vfd->lock = &dev->mutex;
// 设置设备私有数据
video_set_drvdata(vfd, dev);
// 注册设备
ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]);
if (ret < 0)
goto unreg_dev;
// 打印设备信息
v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n",
video_device_node_name(vfd));
}
if (dev->has_radio_rx) {
// 如果设备支持无线电接收
vfd = &dev->radio_rx_dev;
// 设置设备名称
strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
// 设置文件操作
vfd->fops = &vivid_radio_fops;
// 设置ioctl操作
vfd->ioctl_ops = &vivid_ioctl_ops;
// 设置设备释放函数
vfd->release = video_device_release_empty;
// 设置v4l2设备
vfd->v4l2_dev = &dev->v4l2_dev;
// 设置锁
vfd->lock = &dev->mutex;
// 设置设备私有数据
video_set_drvdata(vfd, dev);
// 注册设备
ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_rx_nr[inst]);
if (ret < 0)
goto unreg_dev;
// 打印设备信息
v4l2_info(&dev->v4l2_dev, "V4L2 receiver device registered as %s\n",
video_device_node_name(vfd));
}
if (dev->has_radio_tx) {
// 如果设备支持无线电发送
vfd = &dev->radio_tx_dev;
// 设置设备名称
strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name));
// 设置vfl_dir
vfd->vfl_dir = VFL_DIR_TX;
// 设置文件操作
vfd->fops = &vivid_radio_fops;
// 设置ioctl操作
vfd->ioctl_ops = &vivid_ioctl_ops;
// 设置设备释放函数
vfd->release = video_device_release_empty;
// 设置v4l2设备
vfd->v4l2_dev = &dev->v4l2_dev;
// 设置锁
vfd->lock = &dev->mutex;
// 设置设备私有数据
video_set_drvdata(vfd, dev);
// 注册设备
ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_tx_nr[inst]);
if (ret < 0)
goto unreg_dev;
// 打印设备信息
v4l2_info(&dev->v4l2_dev, "V4L2 transmitter device registered as %s\n",
video_device_node_name(vfd));
}
/* 现在一切都很好,让我们将其添加到设备列表中 */
vivid_devs[inst] = dev;
return 0;
unreg_dev:
// 取消注册设备
video_unregister_device(&dev->radio_tx_dev);
video_unregister_device(&dev->radio_rx_dev);
video_unregister_device(&dev->sdr_cap_dev);
video_unregister_device(&dev->vbi_out_dev);
video_unregister_device(&dev->vbi_cap_dev);
video_unregister_device(&dev->vid_out_dev);
video_unregister_device(&dev->vid_cap_dev);
free_dev:
// 释放设备
v4l2_device_put(&dev->v4l2_dev);
return ret;
}