当前位置: 首页 > news >正文

怎么制作h5棋牌软件/郑州seo全网营销

怎么制作h5棋牌软件,郑州seo全网营销,玉溪市建设局网站,观澜做网站v4l2框架 文章目录 v4l2框架框架1.硬件相关层uvc_probeuvc_register_chainsuvc_register_termsuvc_register_video 2.核心层__video_register_device 3.虚拟视频驱动vivid分析入口vivid_init注册vivid平台驱动vivid_probevivid_create_instance 框架 1.硬件相关层 driver/medi…

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 设备的信息elseuvc_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);elsesnprintf(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. */// 初始化中断 URBif ((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;elsestream->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 值为 -1vdev->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_GRABBERminor_offset = 0; // 次设备号偏移量为 0minor_cnt = 64; // 次设备号数量为 64break;case VFL_TYPE_RADIO: // 如果设备类型为 VFL_TYPE_RADIOminor_offset = 64; // 次设备号偏移量为 64minor_cnt = 64; // 次设备号数量为 64break;case VFL_TYPE_VBI: // 如果设备类型为 VFL_TYPE_VBIminor_offset = 224; // 次设备号偏移量为 224minor_cnt = 32; // 次设备号数量为 32break;default: // 如果设备类型为其他类型minor_offset = 128; // 次设备号偏移量为 128minor_cnt = 64; // 次设备号数量为 64break;}
#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, sowe 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; // 返回错误码}#endifvdev->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 的 ownerret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); // 添加 cdevif (ret < 0) { // 如果添加失败printk(KERN_ERR "%s: cdev_add failed\n", __func__); // 打印错误信息kfree(vdev->cdev); // 释放 cdevvdev->cdev = NULL; // 将 cdev 置为空goto cleanup; // 跳转到 cleanup 标签处}/* Part 4: register the device with sysfs */vdev->dev.class = &video_class; // 设置设备的 classvdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); // 设置设备的 devtvdev->dev.parent = vdev->dev_parent; // 设置设备的 parentdev_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 lastreference 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_SUBDEVvdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; // 设置 entity 的 typevdev->entity.name = vdev->name; // 设置 entity 的 namevdev->entity.info.dev.major = VIDEO_MAJOR; // 设置 entity 的 majorvdev->entity.info.dev.minor = vdev->minor; // 设置 entity 的 minorret = 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; // 返回 0cleanup: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 的 ownerret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); // 添加 cdevif (ret < 0) { // 如果添加失败printk(KERN_ERR "%s: cdev_add failed\n", __func__); // 打印错误信息kfree(vdev->cdev); // 释放 cdevvdev->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 driverswill succeed. This is limited to the maximum number of devices thatvideodev 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_modeint 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_devicesnprintf(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 */// 加载EDIDdev->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)// 如果不是多平面,则数据偏移为0vivid_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的fourccdev->std_cap = V4L2_STD_PAL;// 设置输入标准为PALdev->std_out = V4L2_STD_PAL;// 设置输出标准为PALif (dev->input_type[0] == TV || dev->input_type[0] == SVID)// 如果输入类型为TV或SVID,则tvnorms_cap为V4L2_STD_ALLtvnorms_cap = V4L2_STD_ALL;if (dev->output_type[0] == SVID)// 如果输出类型为SVID,则tvnorms_out为V4L2_STD_ALLtvnorms_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到edidktime_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_dirvfd->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;
}

相关文章:

v4l2框架

v4l2框架 文章目录 v4l2框架框架1.硬件相关层uvc_probeuvc_register_chainsuvc_register_termsuvc_register_video 2.核心层__video_register_device 3.虚拟视频驱动vivid分析入口vivid_init注册vivid平台驱动vivid_probevivid_create_instance 框架 1.硬件相关层 driver/medi…...

vue项目中生成LICENSE文件

vue项目中生成LICENSE文件 简介 LICENSE 文件是一个文本文件&#xff0c;它包含了你的项目所使用的开源软件的许可证信息。 在开发过程中&#xff0c;我们经常会使用到各种各样的第三方开源软件&#xff0c;这些软件是有版权和许可证的&#xff0c;我们在使用时需要遵循它们的…...

NewBing最新更新使用体验(无需等待人人可用)

NewBing最新更新使用体验 微软Bing爆炸级更新&#xff01;无需等待人人可用&#xff01; 今天&#xff0c;微软突然官宣全面开放BingChat&#xff1a; 无需任何等待。只需注册一个账户&#xff0c;首页即可体验。 NewBing最新更新新特性官方文档 https://www.microsoft.com/en-…...

欧拉奔赴品牌2.0时代,女性汽车真实用户需求被定义?

每年的上海国际汽车工业展览会&#xff0c;不仅是各大汽车品牌的技术“秀场”&#xff0c;也是品牌的营销“修罗场”。今年上海车展出圈的营销事件特别多&#xff0c;热度甚至一再蔓延到汽车行业外&#xff0c;其中欧拉也贡献了不少流量。 据了解&#xff0c;在2023上海车展欧…...

机器视觉工程师,听我一句劝,别去外包,干了三年,废了....对女人没了兴趣

​外包三年,干了就废,最后只会安装软件。 对于年轻人来说,需要工作,更需要生活。 对于年轻人来说,需要努力,更需要“面包”。 对于年轻人来说,需要规划,更需要发展。 对于外包,虽说废的不是很彻底,但那三年几乎是出差了三年、玩了三年、荒废了三年,那三年,技术…...

PBDB Data Service:Special parameters(特殊参数)

Special parameters&#xff08;特殊参数&#xff09; 描述参数1&#xff1a;下列参数在大部分请求中可用2&#xff1a;以下参数只与文本格式(.csv&#xff0c; .tsv&#xff0c; .txt)相关 描述 本文将介绍一组特殊参数&#xff0c;它们几乎可以在此数据服务的任何请求中使用…...

腾讯云轻量应用服务器使用限制说明(十大限制)

腾讯云轻量应用服务器和云服务器CVM相比具有一些限制&#xff0c;比如轻量服务器不支持更换内网IP地址&#xff0c;轻量服务器只能套餐整体升级且不支持降配&#xff0c;轻量不支持用户自定义配置私有网络VPC&#xff0c;还有如实例配额、云硬盘配额、备案限制和内网连通性等限…...

Python每日一练(20230507) 丑数I\II\III、超级丑数

目录 1. 丑数 Ugly Number I 2. 丑数 Ugly Number II 3. 丑数 Ugly Number III 4. 超级丑数 Super Ugly Number &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 丑数 Ugly Number I …...

K8S常见异常事件与解决方案

集群相关 Coredns容器或local-dns容器重启 集群中的coredns组件发生重启(重新创建)&#xff0c;一般是由于coredns组件压力较大导致oom&#xff0c;请检查业务是否异常&#xff0c;是否存在应用容器无法解析域名的异常。 如果是local-dns重启&#xff0c;说明local-dns的性能…...

测试5年从中兴 15K 跳槽去腾讯 32K+16,啃完这份笔记你也可以

粉丝小王转行做测试已经是第5个年头&#xff0c;一直是一个不温不火的小职员&#xff0c;本本分分做着自己的事情&#xff0c;觉得自己的工作已经遇到了瓶颈&#xff0c;一个偶然的机会&#xff0c;获得了一份软件测试全栈知识点学习笔记&#xff0c;通过几个月的学习&#xff…...

CentOS 临时IP与永久IP配置

CentOS 临时IP与永久IP配置 CentOS是一种广泛使用的Linux发行版&#xff0c;通常用于服务器和企业网络中。在安装和配置CentOS服务器时&#xff0c;必须为其配置IP地址以便访问。在本文中&#xff0c;我们将介绍如何在CentOS中配置临时IP地址和永久IP地址。 临时IP地址配置 临…...

集线器、网桥、交换机

一.集线器 集线器&#xff08;HUB&#xff09;&#xff0c;它是工作在物理层的设备&#xff0c; 由于它只是工作在物理层的设备&#xff0c;所以它并不关心也不可能关心OSI上面几层所涉及的&#xff0c;它的工作机制流程是&#xff1a;从一个端口接收到数据包时&#xff0c;会在…...

api接口怎么用?

API接口是一种应用程序编程接口&#xff0c;它允许不同的软件应用程序之间进行通信和交互。通过使用API接口&#xff0c;开发人员可以轻松地将自己的应用程序集成到其他应用程序中&#xff0c;从而实现更丰富的功能和更好的用户体验。 API接口的使用方法一般包括以下几个步骤&a…...

Bad minute in crontab?

ERROR 详细 修改crontab出现如下错误&#xff1a; crontab: installing new crontab “/tmp/crontab.MswKCq”:0: bad minute errors in crontab file, can’t install. Do you want to retry the same edit? n crontab: edits left in /tmp/crontab.MswKCq 根因定位 通过…...

【二维矩阵如何存储在一维数组中(行优先和列优先)】

列优先和行优先的性能取决于具体的硬件架构和代码访问模式。在现代计算机中,内存访问的局部性(locality of reference)对性能至关重要。局部性分为两类:时间局部性(temporal locality)和空间局部性(spatial locality)。时间局部性表示最近访问过的数据项很可能在不久的…...

使用Gradle7.6+SpringBoot 3.0+java17创建微服务项目

系列文章目录 学习新版本&#xff0c;菜鸟一枚 会持续更新的 文章目录 系列文章目录前言一、搭建项目1.1、创建git仓库1.1.1、登录gitee&#xff0c;新建仓库1.1.2、得到如下命令&#xff08;新建仓库使用创建git仓库 即可&#xff09; 1.2、使用IDEA创建项目1.2.1、开发工具1.…...

pandas使用教程:apply函数、聚合函数agg和transform

文章目录 apply函数调用apply函数描述性统计apply函数lambda自定义 聚合函数aggregate/agg用字典实现聚合 transform函数多函数 Transform 重置索引与更换标签行重置索引行和列同时重置索引 apply函数调用 apply函数描述性统计 import numpy as np df.loc[:,Q1:Q4].apply(np.…...

使用rasterio裁剪遥感影像

文章目录 0. 数据准备1. polygon的坐标系转换1.1 polygon生成1.1.1 输入数据是shapefile1.1.2 输入数据是polygon 1.2 搞清楚遥感的坐标系和polygon的坐标系(重点)1.3 开始转换 2. 基于polygon的遥感影像裁剪2.1 基础裁剪方法2.1.1 使用rasterio保存2.1.2 使用numpy保存2.2 多线…...

BetaFlight统一硬件配置文件研读之set命令

BetaFlight统一硬件配置文件研读之set命令 1. 源由2. 代码分析3. 实例分析4. 配置情况4.1 set4.2 set parameter_name4.3 set parameter_name value 5. 参考资料 统一硬件配置文件的设计是一种非常好的设计模式&#xff0c;可以将硬件和软件的工作进行解耦。 1. 源由 cli命令…...

QT+OpenGL高级数据和高级GLSL

QTOpenGL高级数据和高级GLSL 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 高级数据 OpenGL中的缓冲区 对象管理特定的GPU内存 在将缓冲区绑定到特定的缓冲区目标时候赋予它意义 OpenGL在内部会保…...

接口测试之Jmeter+Ant+Jenkins接口自动化测试平台

目录 平台简介 环境准备 Jenkins简介 下载与安装 平台搭建 依赖文件配置 build.xml配置 Ant构建 阿里大佬倾情演绎&#xff0c;3天让你学会Jmeter接口测试&#xff0c;学不会算我输_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Q84y1K7bK/?spm_id_from333.99…...

FPGA设计中锁存器产生、避免与消除

FPGA设计中锁存器产生、避免与消除 一、锁存器的产生1.1 组合逻辑中使用保持状态1.2 组合逻辑中的if-else语句或case语句未列出所有可能性1.3 小结 二、锁存器的避免三、锁存器的消除3.1 情况一 一、锁存器的产生 锁存器的产生主要有以下两种情况&#xff1a;&#xff08;1&…...

一份标准的软件测试方案模板

第一章 概述 ​ 软件的错误是不可避免的&#xff0c;所以必须经过严格的测试。通过对本软件的测试&#xff0c;尽可能的发现软件中的错误&#xff0c;借以减少系统内部各模块的逻辑&#xff0c;功能上的缺陷和错误&#xff0c;保证每个单元能正确地实现其预期的功能。检测和排…...

【C++】-对于自定义类型的输入输出运算符重载

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树 ❤️‍&#x1fa79;作者宣言&#xff1a;认真写好每一篇博客 &#x1f4a8;作者gitee:gitee &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 文章目录 前言一、案例引入二、<<的重载三、>>的…...

(详解)js中什么是宏任务、微任务?宏任务、微任务有哪些?又是怎么执行的?

目录 参考资料 必看强烈建议十分钟看完视频 &#xff0c;即可学会 必看参考详解宏任务微任务 笔记 宏任务与微任务 定时器的任务编排 promise的微任务处理逻辑 DOM渲染任务 任务队列共享内存 进度条的实现 任务拆分成多个任务 promise复杂任务分割 img算同步还是异步…...

Okta 即代码:云原生时代的身份管理

我们为什么应该将 Okta 配置作为代码进行管理&#xff1f; 对于需要跨多个应用程序和环境管理对其数字资源的访问的组织来说&#xff0c;Okta 可能是最受欢迎的选择&#xff0c;因为它提供了一系列使其在身份验证和授权方面很受欢迎的功能&#xff0c;例如&#xff1a; 单点登…...

数据结构(六)—— 二叉树(7)构建二叉树

文章目录 如何使用递归构建二叉树1、创建一颗全新树&#xff08;题1-5&#xff09;2、在原有的树上新增东西&#xff08;题6&#xff09; 1 106 从 后序 与 中序 遍历序列构造二叉树2 105 从 前序 与 中序 遍历序列构造二叉树3 108 将有序数组转换为二叉搜索树&#xff08;输入…...

安装适用于Linux的Windows11子系统(WSL2)

1. 主板BIOS开启虚拟化 开启虚拟化需要在BIOS中进行设置&#xff0c;进入主板BIOS→找到虚拟化设置→开启。 2. 检验是否开启虚拟化 打开Windows命令行&#xff0c;并运行 systeminfo固件中已启用虚拟化为是&#xff0c;代表主板BIOS已经开启虚拟化。 3. 启用Windows功能…...

使用Spring的五大类注解读取和存储Bean

目录 1.存储Bean对象的注解 1.1 五大类注解 1.2 方法注解 1.3添加注解的依赖 2.注解的使用 2.1 controller注解 2. 2Service注解 2.3.Resopsitory注解 2.4Component注解 2.5Configuration注解 2.6 注解之间的关系 3.方法注解 3.1 方法注解要配合类注解来使用。 3.2…...

Vue3通透教程【十一】初探TypeScript

文章目录 🌟 写在前面🌟 TypeScript是什么?🌟TypeScript 增加了什么?🌟TypeScript 初体验🌟 写在最后🌟 写在前面 专栏介绍: 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章,应粉丝要求开始更新 Vue3 的相关技术文章,Vue 框架目前的地位大家应该都晓得,所谓…...