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

uvc驱动ioctl分析下

uvc驱动ioctl分析下


文章目录

  • uvc驱动ioctl分析下
  • uvc_ioctl_enum_input枚举输入
    • uvc_query_ctrl
    • __uvc_query_ctrl
    • uvc_ioctl_g_input 获取输入
    • uvc_ioctl_s_input 设置输入
    • uvc_query_v4l2_ctrl
    • uvc_ioctl_queryctrl查询控制器
    • uvc_ioctl_query_ext_ctrl查询扩展控制器
  • uvc_ioctl_g_ctrl 获取控制器
  • uvc_ioctl_s_ctrl 设置控制器
  • uvc_ioctl_g_ext_ctrls 获取扩展控制器
  • uvc_ioctl_s_try_ext_ctrls尝试设置扩展控制
    • uvc_ioctl_try_ext_ctrls尝试设置扩展控制器
    • uvc_ioctl_s_ext_ctrls 设置扩展控制器
  • uvc_ioctl_default默认操作
  • v4l2_event_unsubscribe取消订阅事件
  • uvc_ioctl_enum_frameintervals 枚举帧间隔
  • uvc_ioctl_enum_framesizes 枚举帧大小
  • uvc_v4l2_get_streamparm
    • uvc_ioctl_s_parm设置参数
    • uvc_ioctl_g_parm 获取参数
  • uvc_ioctl_g_selection 获取选择
  • uvc_ioctl_querymenu 查询菜单


在这里插入图片描述

uvc_ioctl_enum_input枚举输入

函数 uvc_ioctl_enum_input 用于枚举视频输入源。
函数的主要步骤如下:
首先,从文件句柄 fh 中获取 UVC 文件句柄 handle 和 UVC 视频链 chain。
从视频链中获取选择器实体 selector。
初始化迭代器 iterm 和索引 index。
如果没有选择器或者忽略选择器单元,执行以下操作:
如果索引 index 不为 0,返回错误码 -EINVAL。
遍历实体链表,找到输入终端实体 iterm。
获取输入终端的引脚 pin。
否则,执行以下操作:
获取选择器的输入引脚数量和对应的引脚 ID。
如果索引 index 小于输入引脚数量,获取对应引脚的 ID 作为 pin。
遍历实体链表,找到与 pin 对应的输入终端实体 iterm。
如果没有找到有效的输入终端实体或者找到的输入终端实体的 ID 不等于 pin,返回错误码 -EINVAL。
初始化输入结构体 input。
设置输入结构体的索引 input->index。
将输入终端实体的名称 iterm->name 复制到输入结构体的名称 input->name 中。
如果输入终端的类型是相机类型,将输入结构体的类型 input->type 设置为相机类型 V4L2_INPUT_TYPE_CAMERA。
返回成功状态码 0。

总的来说,uvc_ioctl_enum_input 函数用于枚举视频输入源。根据选择器实体的存在与否,以及索引的值,函数在 UVC 视频链中查找对应的输入终端实体,并填充输入结构体。函数最后返回相应的状态码,指示操作的结果。

// 获取文件句柄
static int uvc_ioctl_enum_input(struct file *file, void *fh,struct v4l2_input *input)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 获取选择器const struct uvc_entity *selector = chain->selector;// 初始化迭代器struct uvc_entity *iterm = NULL;// 获取索引u32 index = input->index;// 初始化引脚int pin = 0;// 如果没有选择器或者忽略选择器单元if (selector == NULL ||(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {// 如果索引不为0,返回错误if (index != 0)return -EINVAL;// 遍历实体链表list_for_each_entry(iterm, &chain->entities, chain) {// 如果是输入终端if (UVC_ENTITY_IS_ITERM(iterm))break;}// 获取引脚pin = iterm->id;} else if (index < selector->bNrInPins) {// 获取引脚pin = selector->baSourceID[index];// 遍历实体链表list_for_each_entry(iterm, &chain->entities, chain) {// 如果不是输入终端if (!UVC_ENTITY_IS_ITERM(iterm))continue;// 如果是该引脚对应的输入终端if (iterm->id == pin)break;}}if (iterm == NULL || iterm->id != pin)return -EINVAL;// 初始化输入结构体memset(input, 0, sizeof(*input));// 设置输入索引input->index = index;// 设置输入名称strlcpy(input->name, iterm->name, sizeof(input->name));// 如果是相机类型,设置输入类型为相机if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)input->type = V4L2_INPUT_TYPE_CAMERA;return 0;
}

uvc_query_ctrl

uvc_ioctl_g_input->uvc_query_ctrl
函数 uvc_query_ctrl 用于向 UVC 设备发送控制命令并获取控制值。
函数的参数包括:
dev:UVC 设备结构体指针。
query:查询类型,表示对控制进行何种操作,例如获取当前值、获取默认值等。
unit:控制单元的 ID。
intfnum:接口号。
cs:控制选择器的 ID。
data:用于存储查询结果的缓冲区指针。
size:缓冲区大小。
函数的主要步骤如下:
调用 __uvc_query_ctrl 函数发送查询控制命令,并指定超时时间为 UVC_CTRL_CONTROL_TIMEOUT。
检查返回值 ret 是否等于 size,即查询到的控制值的大小。如果不相等,表示查询控制值失败。
如果查询失败,打印错误信息,并返回错误码 -EIO。
如果查询成功,返回成功状态码 0。

总的来说,uvc_query_ctrl 函数用于向 UVC 设备发送控制命令,并从设备获取控制值。函数会检查查询操作的结果,如果查询失败,则返回相应的错误码。否则,将查询到的控制值存储在指定的缓冲区中,并返回成功状态码。

/** uvc_query_ctrl - 查询UVC控制器* @dev: UVC设备* @query: 查询类型* @unit: 单元* @intfnum: 接口编号* @cs: 控制选择* @data: 数据* @size: 数据大小** 返回值: 成功返回0,失败返回-EIO*/
int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,__u8 intfnum, __u8 cs, void *data, __u16 size)
{int ret;ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,UVC_CTRL_CONTROL_TIMEOUT);if (ret != size) {uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on ""unit %u: %d (exp. %u).\n", uvc_query_name(query), cs,unit, ret, size);return -EIO;}return 0;
}

__uvc_query_ctrl

uvc_ioctl_g_input->uvc_query_ctrl->__uvc_query_ctrl
函数 __uvc_query_ctrl 用于向 UVC 设备发送控制命令,并通过 USB 控制传输进行通信。
函数的参数包括:
dev:UVC 设备结构体指针。
query:查询类型,表示对控制进行何种操作,例如获取当前值、获取默认值等。
unit:控制单元的 ID。
intfnum:接口号。
cs:控制选择器的 ID。
data:用于存储查询结果的缓冲区指针。
size:缓冲区大小。
timeout:超时时间。
函数的主要步骤如下:
根据查询类型 query 的最高位判断是发送还是接收控制消息,设置对应的 USB 类型 type 和管道 pipe。如果最高位为1,则表示接收控制消息,使用 usb_rcvctrlpipe 获取接收管道;否则,表示发送控制消息,使用 usb_sndctrlpipe 获取发送管道。
根据查询类型 query 的最高位设置 USB 方向,将 USB 类型 type 的最低位设置为 USB_DIR_IN(接收)或 USB_DIR_OUT(发送)。
调用 usb_control_msg 函数向 UVC 设备发送 USB 控制消息。该函数会发送控制请求并等待设备的响应。
返回函数 usb_control_msg 的返回值,表示控制消息的传输结果。
总的来说,__uvc_query_ctrl 函数通过 USB 控制传输与 UVC 设备进行通信。根据查询类型的不同,该函数会发送或接收控制消息,并等待设备的响应。函数使用 USB 控制传输的管道和方向进行通信,将查询结果存储在指定的缓冲区中,并返回控制传输的结果。

// 查询UVC控制器
// dev: UVC设备
// query: 查询类型
// unit: 单元
// intfnum: 接口编号
// cs: 控制选择
// data: 数据
// size: 数据大小
// timeout: 超时时间
static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,__u8 intfnum, __u8 cs, void *data, __u16 size,int timeout)
{// USB类型为类和接口__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;unsigned int pipe;// 根据查询类型设置管道pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0): usb_sndctrlpipe(dev->udev, 0);// 根据查询类型设置USB方向type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;// 发送控制消息return usb_control_msg(dev->udev, pipe, query, type, cs << 8,unit << 8 | intfnum, data, size, timeout);}

uvc_ioctl_g_input 获取输入

函数 uvc_ioctl_g_input 用于获取当前视频输入源的索引。
函数的主要步骤如下:
首先,从文件句柄 fh 中获取 UVC 文件句柄 handle 和 UVC 视频链 chain。
如果视频链中没有选择器或者忽略选择器单元,执行以下操作:
将输入索引 input 设置为 0。
返回成功状态码 0。
否则,执行以下操作:
使用 uvc_query_ctrl 函数查询选择器控件的当前值,其中参数包括设备 chain->dev、选择器 ID chain->selector->id、接口号 chain->dev->intfnum、控件类型 UVC_SU_INPUT_SELECT_CONTROL 和输出缓冲区 &i。
如果查询失败,返回相应的错误码。
将查询到的输入索引值减去 1,并将结果存储到输入索引指针 input 所指向的位置。
返回成功状态码 0。

总的来说,uvc_ioctl_g_input 函数用于获取当前视频输入源的索引。根据选择器实体的存在与否,在 UVC 视频链中查询选择器控件的当前值,并将其作为当前输入源的索引返回。函数最后返回相应的状态码,指示操作的结果

// 获取输入
static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 初始化返回值int ret;// 初始化输入索引u8 i;// 如果没有选择器或者忽略选择器单元if (chain->selector == NULL ||(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {// 设置输入索引为0*input = 0;// 返回0return 0;}// 获取当前输入ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,&i, 1);// 如果获取失败,返回错误if (ret < 0)return ret;// 设置输入索引*input = i - 1;// 返回0return 0;
}

uvc_ioctl_s_input 设置输入

函数 uvc_ioctl_s_input 是用于设置视频设备的输入选择的函数。函数的概括如下:
获取文件句柄和视频链。
尝试获取权限。
如果视频链没有选择器或者选择器单元被忽略:
如果输入参数不为0,则返回错误。
如果输入参数为0,则返回成功。
如果视频链有选择器:
如果输入参数大于等于选择器的输入引脚数,则返回错误。
设置输入索引为输入参数加1。
调用
uvc_query_ctrl 函数发送控制命令,设置输入选择控制,返回结果。
函数的主要目的是根据输入参数设置视频设备的输入选择,并返回相应的状态。

static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 定义返回值int ret;// 定义输入索引u32 i;// 获取权限ret = uvc_acquire_privileges(handle);if (ret < 0)return ret;// 如果没有选择器或者忽略选择器单元if (chain->selector == NULL ||(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {// 如果输入不为0,返回错误if (input)return -EINVAL;// 返回0return 0;}// 如果输入索引大于等于选择器的输入引脚数,返回错误if (input >= chain->selector->bNrInPins)return -EINVAL;// 设置输入索引i = input + 1;// 设置控制return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,&i, 1);
}

uvc_query_v4l2_ctrl

函数 uvc_query_v4l2_ctrl 用于查询控制器属性并填充到 v4l2_queryctrl 结构体中。函数的概括如下:
定义控制器和控制器映射的指针。
对控制器链表进行互斥锁加锁操作。
使用
uvc_find_control 函数在控制器链表中查找指定 ID 的控制器,并获取控制器映射。
如果找不到控制器,返回错误码。
调用
__uvc_query_v4l2_ctrl 函数,传递控制器、控制器映射和
v4l2_queryctrl 结构体,查询控制器的属性并填充到
v4l2_queryctrl 结构体中。
解锁控制器链表的互斥锁。
返回查询的结果。
该函数的主要目的是在控制器链表中查找指定 ID 的控制器,并将查询到的控制器属性填充到 v4l2_queryctrl 结构体中,提供给调用者使用。

int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,struct v4l2_queryctrl *v4l2_ctrl)
{struct uvc_control *ctrl; // 定义一个uvc_control结构体指针struct uvc_control_mapping *mapping; // 定义一个uvc_control_mapping结构体指针int ret;ret = mutex_lock_interruptible(&chain->ctrl_mutex); // 加锁if (ret < 0)return -ERESTARTSYS; // 加锁失败,返回错误码ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); // 查找控制器if (ctrl == NULL) { // 如果控制器不存在ret = -EINVAL; // 返回错误码goto done; // 跳转到done标签}ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl); // 查询控制器
done:mutex_unlock(&chain->ctrl_mutex); // 解锁return ret; // 返回查询结果
}

uvc_ioctl_queryctrl->uvc_query_v4l2_ctrl->__uvc_query_v4l2_ctrl
函数 __uvc_query_v4l2_ctrl 是用于查询 UVC 控制器的信息并填充 v4l2_queryctrl 结构体的函数。函数的概括如下:
初始化
v4l2_queryctrl 结构体,并根据
mapping 的信息填充相应的字段。
根据控制器的信息设置
v4l2_queryctrl 的
flags 字段,包括是否可读、是否可写等。
如果
mapping 的
master_id 不为0,则查找对应的主控制器。
如果主控制器存在且可读取当前值,则判断是否需要禁用从属控制器。
如果控制器的缓存未被填充,则调用
uvc_ctrl_populate_cache 函数填充缓存。
如果控制器允许获取默认值,则将
v4l2_queryctrl 的
default_value 字段设置为默认值。
根据
mapping 的
v4l2_type 类型进行不同的操作:
如果是菜单类型(
V4L2_CTRL_TYPE_MENU),设置最小值、最大值和步长,并找到默认值所在的索引。
如果是布尔类型(
V4L2_CTRL_TYPE_BOOLEAN),设置最小值为0,最大值为1,步长为1。
如果是按钮类型(
V4L2_CTRL_TYPE_BUTTON),设置最小值为0,最大值为0,步长为0。
对于其他类型,根据控制器的标志获取最小值、最大值和步长。
返回结果。
函数的主要目的是根据控制器的信息填充 v4l2_queryctrl 结构体,提供控制器的属性和范围信息。

static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, // 定义一个uvc_video_chain结构体指针struct uvc_control *ctrl, // 定义一个uvc_control结构体指针struct uvc_control_mapping *mapping, // 定义一个uvc_control_mapping结构体指针struct v4l2_queryctrl *v4l2_ctrl) // 定义一个v4l2_queryctrl结构体指针
{struct uvc_control_mapping *master_map = NULL; // 定义一个uvc_control_mapping结构体指针struct uvc_control *master_ctrl = NULL; // 定义一个uvc_control结构体指针struct uvc_menu_info *menu; // 定义一个uvc_menu_info结构体指针unsigned int i; // 定义一个无符号整型变量imemset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); // 将v4l2_ctrl指向的内存空间清零v4l2_ctrl->id = mapping->id; // 将v4l2_ctrl的id成员变量赋值为mapping的id成员变量v4l2_ctrl->type = mapping->v4l2_type; // 将v4l2_ctrl的type成员变量赋值为mapping的v4l2_type成员变量strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); // 将mapping的name成员变量拷贝到v4l2_ctrl的name成员变量中v4l2_ctrl->flags = 0; // 将v4l2_ctrl的flags成员变量赋值为0if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_CUR按位与的结果为0v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; // 将v4l2_ctrl的flags成员变量与V4L2_CTRL_FLAG_WRITE_ONLY按位或if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_SET_CUR按位与的结果为0v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; // 将v4l2_ctrl的flags成员变量与V4L2_CTRL_FLAG_READ_ONLY按位或if (mapping->master_id) // 如果mapping的master_id成员变量不为0__uvc_find_control(ctrl->entity, mapping->master_id,&master_map, &master_ctrl, 0); // 在ctrl的entity中查找master_id对应的控制器if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) { // 如果master_ctrl不为空且master_ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_CUR按位与的结果不为0s32 val; // 定义一个有符号32位整型变量valint ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); // 调用__uvc_ctrl_get函数,获取控制器的值if (ret < 0) // 如果ret小于0return ret; // 返回retif (val != mapping->master_manual) // 如果val不等于mapping的master_manual成员变量v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; // 将v4l2_ctrl的flags成员变量与V4L2_CTRL_FLAG_INACTIVE按位或}// 如果控制器没有缓存if (!ctrl->cached) {// 调用uvc_ctrl_populate_cache函数,填充控制器的缓存int ret = uvc_ctrl_populate_cache(chain, ctrl);if (ret < 0)return ret;}// 如果控制器的info的flags成员变量与UVC_CTRL_FLAG_GET_DEF按位与的结果不为0if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {// 将v4l2_ctrl的default_value成员变量赋值为mapping的get函数返回值v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));}// 根据mapping的v4l2_type成员变量进行不同的操作switch (mapping->v4l2_type) {case V4L2_CTRL_TYPE_MENU:// 设置v4l2_ctrl的minimum、maximum、step成员变量v4l2_ctrl->minimum = 0;v4l2_ctrl->maximum = mapping->menu_count - 1;v4l2_ctrl->step = 1;// 遍历mapping的menu_info成员变量menu = mapping->menu_info;for (i = 0; i < mapping->menu_count; ++i, ++menu) {// 如果menu的value成员变量等于v4l2_ctrl的default_value成员变量if (menu->value == v4l2_ctrl->default_value) {// 将v4l2_ctrl的default_value成员变量赋值为iv4l2_ctrl->default_value = i;break;}}return 0;case V4L2_CTRL_TYPE_BOOLEAN: // 如果mapping的v4l2_type成员变量为V4L2_CTRL_TYPE_BOOLEANv4l2_ctrl->minimum = 0; // 将v4l2_ctrl的minimum成员变量赋值为0v4l2_ctrl->maximum = 1; // 将v4l2_ctrl的maximum成员变量赋值为1v4l2_ctrl->step = 1; // 将v4l2_ctrl的step成员变量赋值为1return 0; // 返回0case V4L2_CTRL_TYPE_BUTTON: // 如果mapping的v4l2_type成员变量为V4L2_CTRL_TYPE_BUTTONv4l2_ctrl->minimum = 0; // 将v4l2_ctrl的minimum成员变量赋值为0v4l2_ctrl->maximum = 0; // 将v4l2_ctrl的maximum成员变量赋值为0v4l2_ctrl->step = 0; // 将v4l2_ctrl的step成员变量赋值为0return 0; // 返回0default: // 如果mapping的v4l2_type成员变量不为V4L2_CTRL_TYPE_BOOLEAN或V4L2_CTRL_TYPE_BUTTONbreak;}if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_MIN按位与的结果不为0v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); // 将v4l2_ctrl的minimum成员变量赋值为mapping的get函数返回值if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_MAX按位与的结果不为0v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); // 将v4l2_ctrl的maximum成员变量赋值为mapping的get函数返回值if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_RES按位与的结果不为0v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); // 将v4l2_ctrl的step成员变量赋值为mapping的get函数返回值return 0; // 返回0
}

uvc_ioctl_queryctrl查询控制器

函数 uvc_ioctl_queryctrl 是用于处理查询控制器属性的 ioctl 操作。函数的概括如下:
获取文件句柄和视频链。
调用
uvc_query_v4l2_ctrl 函数,将视频链和
v4l2_queryctrl 结构体作为参数,查询控制器的属性并填充到
v4l2_queryctrl 结构体中。
返回查询的结果。
该函数的主要目的是将查询控制器属性的操作转发给 uvc_query_v4l2_ctrl 函数,并返回查询的结果。

static int uvc_ioctl_queryctrl(struct file *file, void *fh,struct v4l2_queryctrl *qc)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 查询v4l2控制return uvc_query_v4l2_ctrl(chain, qc);
}

uvc_ioctl_query_ext_ctrl查询扩展控制器

函数 uvc_ioctl_query_ext_ctrl 用于查询扩展控制的属性并填充到 v4l2_query_ext_ctrl 结构体中。函数的概括如下:
获取文件句柄和视频链。
创建一个
v4l2_queryctrl 结构体
qc,并设置其 ID 为
qec->id。
调用
uvc_query_v4l2_ctrl 函数查询控制器属性,并将查询结果存储在
qc 中。
如果查询失败,返回错误码。
将查询结果赋值给
qec 结构体的相应成员变量。
返回查询成功。
该函数的主要目的是查询扩展控制的属性,并将查询结果填充到 v4l2_query_ext_ctrl 结构体中,以供调用者使用。

// 查询扩展控制
static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,struct v4l2_query_ext_ctrl *qec)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 定义v4l2_queryctrl结构体struct v4l2_queryctrl qc = { qec->id };// 定义返回值int ret;// 查询v4l2控制ret = uvc_query_v4l2_ctrl(chain, &qc);if (ret)return ret;// 将查询结果赋值给qec结构体qec->id = qc.id;qec->type = qc.type;strlcpy(qec->name, qc.name, sizeof(qec->name));qec->minimum = qc.minimum;qec->maximum = qc.maximum;qec->step = qc.step;qec->default_value = qc.default_value;qec->flags = qc.flags;qec->elem_size = 4;qec->elems = 1;qec->nr_of_dims = 0;memset(qec->dims, 0, sizeof(qec->dims));memset(qec->reserved, 0, sizeof(qec->reserved));return 0;
}

uvc_ioctl_g_ctrl 获取控制器

函数 uvc_ioctl_g_ctrl 用于获取单个控制的值。函数的概括如下:
获取文件句柄和视频链。
定义一个临时的 v4l2_ext_control 结构体 xctrl。
初始化 xctrl 结构体并将 ctrl 结构体中的 id 赋值给 xctrl 结构体。
开始控制操作,调用 uvc_ctrl_begin 函数。
获取控制的当前值,调用 uvc_ctrl_get 函数,将结果保存在 xctrl 结构体中。
回滚控制操作,调用 uvc_ctrl_rollback 函数。
如果获取失败,返回错误码。
将 xctrl 结构体中的 value 赋值给 ctrl 结构体。
返回操作结果。
该函数的目的是获取单个控制的当前值。它将 ctrl 结构体中的 id 转换为 v4l2_ext_control 结构体,并通过 uvc_ctrl_get 函数从控制器中获取当前值。获取完成后,将值更新到 ctrl 结构体中,并返回操作结果。

static int uvc_ioctl_g_ctrl(struct file *file, void *fh,struct v4l2_control *ctrl)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 定义一个v4l2_ext_control结构体struct v4l2_ext_control xctrl;// 定义返回值int ret;// 初始化xctrl结构体memset(&xctrl, 0, sizeof(xctrl));// 将ctrl结构体中的id赋值给xctrl结构体xctrl.id = ctrl->id;// 开始控制ret = uvc_ctrl_begin(chain);if (ret < 0)return ret;// 获取控制ret = uvc_ctrl_get(chain, &xctrl);// 回滚控制uvc_ctrl_rollback(handle);if (ret < 0)return ret;// 将xctrl结构体中的value赋值给ctrl结构体ctrl->value = xctrl.value;return 0;
} // uvc_ioctl_g_ctrl函数结束

uvc_ioctl_s_ctrl 设置控制器

函数 uvc_ioctl_s_ctrl 用于设置单个控制的值。函数的概括如下:
获取文件句柄和视频链。
定义一个临时的
v4l2_ext_control 结构体
xctrl。
初始化
xctrl 结构体并将
ctrl 结构体中的
id 和
value 赋值给
xctrl 结构体。
开始控制操作,调用
uvc_ctrl_begin 函数。
设置控制,调用
uvc_ctrl_set 函数将
xctrl 结构体的值设置到控制器中。
如果设置失败,回滚控制操作,调用
uvc_ctrl_rollback 函数,返回错误码。
提交控制操作,调用
uvc_ctrl_commit 函数将设置后的值提交到控制器中。
如果提交失败,返回错误码。

xctrl 结构体中的
value 赋值给
ctrl 结构体。
返回操作结果。
该函数的目的是设置单个控制的值,并在必要时回滚控制操作。它将 ctrl 结构体中的 id 和 value 转换为 v4l2_ext_control 结构体,并通过 uvc_ctrl_set 函数设置到控制器中。如果设置成功,将提交控制,并将最终的值更新到 ctrl 结构体中。

static int uvc_ioctl_s_ctrl(struct file *file, void *fh,struct v4l2_control *ctrl)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 定义一个v4l2_ext_control结构体struct v4l2_ext_control xctrl;// 定义返回值int ret;// 初始化xctrl结构体memset(&xctrl, 0, sizeof(xctrl));// 将ctrl结构体中的id和value赋值给xctrl结构体xctrl.id = ctrl->id;xctrl.value = ctrl->value;// 开始控制ret = uvc_ctrl_begin(chain);if (ret < 0)return ret;// 设置控制ret = uvc_ctrl_set(chain, &xctrl);if (ret < 0) {// 回滚控制uvc_ctrl_rollback(handle);return ret;}// 提交控制ret = uvc_ctrl_commit(handle, &xctrl, 1);if (ret < 0)return ret;// 将xctrl结构体中的value赋值给ctrl结构体ctrl->value = xctrl.value;return 0;
}

uvc_ioctl_g_ext_ctrls 获取扩展控制器

函数 uvc_ioctl_g_ext_ctrls 用于获取扩展控制的当前值。函数的概括如下:
获取文件句柄和视频链。
定义控制指针和循环计数器。
调用
uvc_ctrl_begin 函数开始控制。
循环获取控制,对于每个控制,调用
uvc_ctrl_get 函数获取当前值。
如果获取失败,调用
uvc_ctrl_rollback 函数回滚控制,设置
ctrls->error_idx 错误索引,并返回错误码。
如果所有控制都成功获取,将
ctrls->error_idx 设置为0。
调用
uvc_ctrl_rollback 函数回滚控制。
返回操作结果。
该函数的主要目的是循环获取扩展控制的当前值,并在必要时回滚控制操作。如果获取失败,则会回滚之前成功获取的控制,并返回错误码。

// 获取扩展控制
static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,struct v4l2_ext_controls *ctrls)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 定义一个v4l2_ext_control结构体struct v4l2_ext_control *ctrl = ctrls->controls;// 定义循环计数器unsigned int i;// 定义返回值int ret;// 开始控制ret = uvc_ctrl_begin(chain);if (ret < 0)return ret;// 循环获取控制for (i = 0; i < ctrls->count; ++ctrl, ++i) {ret = uvc_ctrl_get(chain, ctrl);if (ret < 0) {// 回滚控制uvc_ctrl_rollback(handle);ctrls->error_idx = i;return ret;}}ctrls->error_idx = 0;// 回滚控制return uvc_ctrl_rollback(handle);
}

uvc_ioctl_s_try_ext_ctrls尝试设置扩展控制

函数 uvc_ioctl_s_try_ext_ctrls 用于尝试设置扩展控制,并根据需要进行提交或回滚操作。函数的概括如下:
获取控制指针和视频链。
调用 uvc_ctrl_begin 函数开始控制。
循环设置控制,对于每个控制,调用 uvc_ctrl_set 函数进行设置。
如果设置失败,调用 uvc_ctrl_rollback 函数回滚控制,设置 ctrls->error_idx 错误索引,并返回错误码。
如果所有控制都成功设置,将 ctrls->error_idx 设置为0。
根据 commit 参数决定是提交控制 (uvc_ctrl_commit) 还是回滚控制 (uvc_ctrl_rollback)。
返回操作结果。
该函数的主要目的是循环尝试设置扩展控制,并根据 commit 参数决定是否提交或回滚控制操作。如果设置失败,则会回滚之前成功设置的控制。

// 尝试设置扩展控制
static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,struct v4l2_ext_controls *ctrls,bool commit)
{// 获取控制struct v4l2_ext_control *ctrl = ctrls->controls;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 定义循环计数器unsigned int i;// 定义返回值int ret;// 开始控制ret = uvc_ctrl_begin(chain);if (ret < 0)return ret;// 循环设置控制for (i = 0; i < ctrls->count; ++ctrl, ++i) {ret = uvc_ctrl_set(chain, ctrl);if (ret < 0) {// 回滚控制uvc_ctrl_rollback(handle);ctrls->error_idx = commit ? ctrls->count : i;return ret;}}ctrls->error_idx = 0;// 提交或回滚控制if (commit)return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count);elsereturn uvc_ctrl_rollback(handle);
}

uvc_ioctl_try_ext_ctrls尝试设置扩展控制器

uvc_ioctl_s_ext_ctrls 设置扩展控制器

函数 uvc_ioctl_s_ext_ctrls 用于设置一组扩展控制的值,并提交这些控制。函数的概括如下:
获取文件句柄。
调用 uvc_ioctl_s_try_ext_ctrls 函数,将控制的设置操作交给 uvc_ioctl_s_try_ext_ctrls 函数处理,并指定 commit 参数为 true,表示提交控制。
返回操作结果。
函数 uvc_ioctl_try_ext_ctrls 与 uvc_ioctl_s_ext_ctrls 类似,都是用于设置一组扩展控制的值,但不提交这些控制。它们的作用是调用 uvc_ioctl_s_try_ext_ctrls 函数,将控制的设置操作交给 uvc_ioctl_s_try_ext_ctrls 函数处理,并指定 commit 参数为 false,表示不提交控制。
这两个函数的目的是设置一组扩展控制的值,并根据需要选择是否提交这些控制。它们通过调用 uvc_ioctl_s_try_ext_ctrls 函数来处理控制的设置操作,并将 commit 参数传递给 uvc_ioctl_s_try_ext_ctrls 函数来决定是否提交控制。

// 设置扩展控制
static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,struct v4l2_ext_controls *ctrls)
{// 获取文件句柄struct uvc_fh *handle = fh;// 调用uvc_ioctl_s_try_ext_ctrls函数,提交控制return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
}// 尝试设置扩展控制
static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,struct v4l2_ext_controls *ctrls)
{// 获取文件句柄struct uvc_fh *handle = fh;// 调用uvc_ioctl_s_try_ext_ctrls函数,不提交控制return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
}

uvc_ioctl_default默认操作

static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,unsigned int cmd, void *arg)
{struct uvc_fh *handle = fh; // 获取文件句柄struct uvc_video_chain *chain = handle->chain; // 获取视频链switch (cmd) {/* Dynamic controls. */case UVCIOC_CTRL_MAP: // 如果命令为控制映射return uvc_ioctl_ctrl_map(chain, arg); // 执行控制映射case UVCIOC_CTRL_QUERY: // 如果命令为控制查询return uvc_xu_ctrl_query(chain, arg); // 执行控制查询default:return -ENOTTY; // 返回不支持的命令错误}
}

v4l2_event_unsubscribe取消订阅事件

uvc_ioctl_subscribe_event 订阅事件
这段代码实现了 uvc_ioctl_subscribe_event 函数,用于订阅 V4L2 事件。
函数的功能是根据提供的事件订阅信息 sub,订阅相应类型的事件。它接收一个 v4l2_fh 结构体指针 fh 和一个 v4l2_event_subscription 结构体指针 sub。
函数的概述如下:
根据 sub->type 的值进行不同的操作:
如果事件类型是 V4L2_EVENT_CTRL,则执行以下步骤:
调用 v4l2_event_subscribe 函数,订阅控制事件。
传递参数 fh、sub、0 和 &uvc_ctrl_sub_ev_ops,其中 0 是标志位参数,&uvc_ctrl_sub_ev_ops 是事件订阅操作的函数指针。
如果事件类型不是已知类型,则返回无效参数错误 -EINVAL。
返回相应的状态码。
该函数用于根据提供的事件订阅信息订阅相应类型的 V4L2 事件,并将结果返回给调用方。在代码中,目前仅支持订阅控制事件 (V4L2_EVENT_CTRL),对于其他类型的事件会返回无效参数错误。

static int uvc_ioctl_subscribe_event(struct v4l2_fh *fh,const struct v4l2_event_subscription *sub)
{switch (sub->type) {case V4L2_EVENT_CTRL: // 如果事件类型为控制事件return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops); // 订阅控制事件default:return -EINVAL; // 返回无效参数错误}
}

uvc_ioctl_enum_frameintervals 枚举帧间隔

这段代码实现了 uvc_ioctl_enum_frameintervals 函数,用于枚举帧间隔。
函数的功能是在给定的像素格式和帧大小下,枚举可用的帧间隔。它接收一个 v4l2_frmivalenum 结构体指针 fival,并将符合条件的帧间隔信息填充到该结构体中。
函数的概述如下:
获取文件句柄和视频流。
使用循环遍历视频流的不同格式,查找与 fival->pixel_format 相匹配的像素格式。
如果找到匹配的格式,将其赋值给变量 format。
使用循环遍历匹配格式的不同帧,查找与 fival->width 和 fival->height 相匹配的帧。
如果找到匹配的帧,将其赋值给变量 frame。
检查帧的 bFrameIntervalType 是否存在,如果存在:
检查 fival->index 是否超过帧间隔类型的数量,如果是,则返回错误码。
设置 fival 的类型为 V4L2_FRMIVAL_TYPE_DISCRETE。
将帧的第 fival->index 个帧间隔赋值给 fival->discrete.numerator。
将分母设置为 10000000。
使用 uvc_simplify_fraction 函数简化分数。
如果帧的 bFrameIntervalType 不存在:
检查 fival->index 是否为 0,如果不是,则返回错误码。
设置 fival 的类型为 V4L2_FRMIVAL_TYPE_STEPWISE。
将帧的第一个帧间隔赋值给 fival->stepwise.min.numerator。
将帧的第二个帧间隔赋值给 fival->stepwise.max.numerator。
将帧的第三个帧间隔赋值给 fival->stepwise.step.numerator。
将分母设置为 10000000。
使用 uvc_simplify_fraction 函数简化分数。
返回成功的状态码。
该函数用于在给定的像素格式和帧大小下枚举可用的帧间隔,并将结果填充到 v4l2_frmivalenum 结构体中,以供调用方使用。

static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,struct v4l2_frmivalenum *fival)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频流struct uvc_streaming *stream = handle->stream;// 格式指针struct uvc_format *format = NULL;// 帧指针struct uvc_frame *frame = NULL;// 循环计数器int i;/* 查找给定像素格式和帧大小 */for (i = 0; i < stream->nformats; i++) {if (stream->format[i].fcc == fival->pixel_format) {format = &stream->format[i];break;}}if (format == NULL)return -EINVAL;for (i = 0; i < format->nframes; i++) {if (format->frame[i].wWidth == fival->width &&format->frame[i].wHeight == fival->height) {frame = &format->frame[i];break;}}if (frame == NULL)return -EINVAL;if (frame->bFrameIntervalType) { // 如果帧间隔类型存在if (fival->index >= frame->bFrameIntervalType) // 如果索引大于帧间隔类型return -EINVAL; // 返回无效参数错误fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; // 帧间隔类型为离散型fival->discrete.numerator = frame->dwFrameInterval[fival->index]; // 分子为帧间隔fival->discrete.denominator = 10000000; // 分母为10000000uvc_simplify_fraction(&fival->discrete.numerator, &fival->discrete.denominator, 8, 333); // 简化分数} else { // 如果帧间隔类型不存在if (fival->index) // 如果索引不为0return -EINVAL; // 返回无效参数错误fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; // 帧间隔类型为步进型fival->stepwise.min.numerator = frame->dwFrameInterval[0]; // 最小值为第一个帧间隔fival->stepwise.min.denominator = 10000000; // 分母为10000000fival->stepwise.max.numerator = frame->dwFrameInterval[1]; // 最大值为第二个帧间隔fival->stepwise.max.denominator = 10000000; // 分母为10000000fival->stepwise.step.numerator = frame->dwFrameInterval[2]; // 步进值为第三个帧间隔fival->stepwise.step.denominator = 10000000; // 分母为10000000uvc_simplify_fraction(&fival->stepwise.min.numerator, &fival->stepwise.min.denominator, 8, 333); // 简化最小值分数uvc_simplify_fraction(&fival->stepwise.max.numerator, &fival->stepwise.max.denominator, 8, 333); // 简化最大值分数uvc_simplify_fraction(&fival->stepwise.step.numerator, &fival->stepwise.step.denominator, 8, 333); // 简化步进值分数}return 0; // 返回0
}

uvc_ioctl_enum_framesizes 枚举帧大小

这段代码实现了 uvc_ioctl_enum_framesizes 函数,用于枚举帧大小。
函数的功能是在给定的像素格式下,枚举可用的帧大小。它接收一个 v4l2_frmsizeenum 结构体指针 fsize,并将符合条件的帧大小信息填充到该结构体中。
函数的概述如下:
获取文件句柄和视频流。
使用循环遍历视频流的不同格式,查找与 fsize->pixel_format 相匹配的像素格式。
如果找到匹配的格式,将其赋值给变量 format。
检查 fsize->index 是否超过了格式的帧数量,如果是,则返回错误码。
获取帧指针 frame,指向匹配格式的第 fsize->index 帧。
设置 fsize 的类型为 V4L2_FRMSIZE_TYPE_DISCRETE。
将帧的宽度和高度赋值给 fsize->discrete.width 和 fsize->discrete.height。
返回成功的状态码。
该函数用于在给定的像素格式下枚举可用的帧大小,并将结果填充到 v4l2_frmsizeenum 结构体中,以供调用方使用。

// 枚举帧大小
static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,struct v4l2_frmsizeenum *fsize)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频流struct uvc_streaming *stream = handle->stream;// 格式指针struct uvc_format *format = NULL;// 帧指针struct uvc_frame *frame;// 循环计数器int i;// 查找给定像素格式for (i = 0; i < stream->nformats; i++) {if (stream->format[i].fcc == fsize->pixel_format) {format = &stream->format[i];break;}}if (format == NULL)return -EINVAL;if (fsize->index >= format->nframes)return -EINVAL;frame = &format->frame[fsize->index];fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;fsize->discrete.width = frame->wWidth;fsize->discrete.height = frame->wHeight;return 0;
}

uvc_v4l2_get_streamparm

该函数 uvc_v4l2_get_streamparm 用于获取流参数,并将其填充到 v4l2_streamparm 结构体中。函数的概述如下:
检查请求的流参数类型 parm->type 是否与流的类型 stream->type 匹配,如果不匹配,则返回错误码 -EINVAL。
使用互斥锁锁定流的互斥体。
从流的控制器中获取帧间隔 stream->ctrl.dwFrameInterval。
使用互斥锁解锁流的互斥体。
计算帧间隔的分数形式,通过调用 uvc_simplify_fraction 函数将帧间隔转化为最简分数形式。
初始化 v4l2_streamparm 结构体 parm,将其内存清零,并设置类型为流的类型 stream->type。
如果流的类型是 V4L2_BUF_TYPE_VIDEO_CAPTURE(视频捕获类型),则设置捕获模式参数:
parm->parm.capture.capability 设置为 V4L2_CAP_TIMEPERFRAME。
parm->parm.capture.capturemode 设置为 0。
parm->parm.capture.timeperframe.numerator 设置为帧间隔的分子部分 numerator。
parm->parm.capture.timeperframe.denominator 设置为帧间隔的分母部分 denominator。
parm->parm.capture.extendedmode 设置为 0。
parm->parm.capture.readbuffers 设置为 0。
如果流的类型是输出类型,则设置输出模式参数:
parm->parm.output.capability 设置为 V4L2_CAP_TIMEPERFRAME。
parm->parm.output.outputmode 设置为 0。
parm->parm.output.timeperframe.numerator 设置为帧间隔的分子部分 numerator。
parm->parm.output.timeperframe.denominator 设置为帧间隔的分母部分 denominator。
返回成功标志 0。
该函数的作用是根据流的类型获取帧间隔参数,并将参数填充到 v4l2_streamparm 结构体中。具体的参数设置取决于流的类型。如果流是视频捕获类型,则设置捕获模式参数;如果流是输出类型,则设置输出模式参数。帧间隔的分数形式会被转化为最简分数形式,并填充到对应的参数字段中。

// 获取流参数
static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,struct v4l2_streamparm *parm)
{uint32_t numerator, denominator;// 如果请求的格式不是当前流的格式,返回错误if (parm->type != stream->type)return -EINVAL;// 上锁mutex_lock(&stream->mutex);numerator = stream->ctrl.dwFrameInterval;mutex_unlock(&stream->mutex);// 计算分数denominator = 10000000;uvc_simplify_fraction(&numerator, &denominator, 8, 333);// 初始化参数memset(parm, 0, sizeof *parm);parm->type = stream->type;// 如果是视频捕获类型if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;parm->parm.capture.capturemode = 0;parm->parm.capture.timeperframe.numerator = numerator;parm->parm.capture.timeperframe.denominator = denominator;parm->parm.capture.extendedmode = 0;parm->parm.capture.readbuffers = 0;} else { // 如果是输出类型parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;parm->parm.output.outputmode = 0;parm->parm.output.timeperframe.numerator = numerator;parm->parm.output.timeperframe.denominator = denominator;}return 0;
}

uvc_ioctl_s_parm设置参数

uvc_ioctl_g_parm 获取参数

这段代码包含两个函数:uvc_ioctl_g_parm 和 uvc_ioctl_s_parm,用于获取和设置流参数。
uvc_ioctl_g_parm函数用于获取流参数。它的功能是调用 uvc_v4l2_get_streamparm 函数,将流的参数填充到 v4l2_streamparm 结构体中。函数的概述如下:
获取文件句柄和视频流。
调用 uvc_v4l2_get_streamparm 函数,传入视频流和参数结构体 parm。
返回 uvc_v4l2_get_streamparm 函数的结果。
uvc_ioctl_s_parm 函数用于设置流参数。它的功能是调用 uvc_v4l2_set_streamparm 函数,根据传入的参数结构体 parm 设置流的参数。函数的概述如下:
获取文件句柄和视频流。
调用 uvc_acquire_privileges 函数,获取权限。
调用 uvc_v4l2_set_streamparm 函数,传入视频流和参数结构体 parm,以设置流的参数。
返回 uvc_v4l2_set_streamparm 函数的结果。
这两个函数分别用于获取和设置流参数。uvc_ioctl_g_parm 获取当前的流参数,而 uvc_ioctl_s_parm 设置新的流参数。

// 获取流参数
static int uvc_ioctl_g_parm(struct file *file, void *fh,struct v4l2_streamparm *parm)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频流struct uvc_streaming *stream = handle->stream;// 调用uvc_v4l2_get_streamparm函数,获取流参数return uvc_v4l2_get_streamparm(stream, parm);
}
// 设置流参数
static int uvc_ioctl_s_parm(struct file *file, void *fh,struct v4l2_streamparm *parm)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频流struct uvc_streaming *stream = handle->stream;int ret;// 获取权限ret = uvc_acquire_privileges(handle);if (ret < 0)return ret;// 调用uvc_v4l2_set_streamparm函数,设置流参数return uvc_v4l2_set_streamparm(stream, parm);
}

uvc_ioctl_g_selection 获取选择

该函数 uvc_ioctl_g_selection 是一个 V4L2 控制的 IOCTL 函数,用于获取选择区域的信息。函数的概述如下:
从文件句柄中获取 uvc_fh 结构体指针 handle。
从句柄中获取视频流 uvc_streaming 的指针 stream。
检查选择类型是否与流的类型匹配,如果不匹配,则返回错误码 -EINVAL。
根据选择目标类型 sel->target 进行判断:
如果目标类型是 V4L2_SEL_TGT_CROP_DEFAULT 或 V4L2_SEL_TGT_CROP_BOUNDS,并且流的类型是 V4L2_BUF_TYPE_VIDEO_CAPTURE,则继续执行。
如果目标类型是 V4L2_SEL_TGT_COMPOSE_DEFAULT 或 V4L2_SEL_TGT_COMPOSE_BOUNDS,并且流的类型是 V4L2_BUF_TYPE_VIDEO_OUTPUT,则继续执行。
否则,返回错误码 -EINVAL。
设置选择区域的坐标和尺寸:
sel->r.left 和 sel->r.top 设置为 0。
使用互斥锁锁定流的互斥体。
sel->r.width 设置为当前帧的宽度 stream->cur_frame->wWidth。
sel->r.height 设置为当前帧的高度 stream->cur_frame->wHeight。
使用互斥锁解锁流的互斥体。
返回成功标志 0。
该函数的作用是根据选择目标类型和流的类型设置选择区域的坐标和尺寸,并返回成功标志。选择区域的坐标和尺寸通过 sel->r 结构体表示。函数会根据流的类型进行判断,确保选择目标类型与流的类型匹配,并设置选择区域的信息。

// 获取选择区域
static int uvc_ioctl_g_selection(struct file *file, void *fh,struct v4l2_selection *sel)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频流struct uvc_streaming *stream = handle->stream;// 判断选择类型是否匹配if (sel->type != stream->type)return -EINVAL;// 根据选择目标类型进行判断switch (sel->target) {case V4L2_SEL_TGT_CROP_DEFAULT:case V4L2_SEL_TGT_CROP_BOUNDS:if (stream->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)return -EINVAL;break;case V4L2_SEL_TGT_COMPOSE_DEFAULT:case V4L2_SEL_TGT_COMPOSE_BOUNDS:if (stream->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)return -EINVAL;break;default:return -EINVAL;}// 设置选择区域sel->r.left = 0;sel->r.top = 0;mutex_lock(&stream->mutex);sel->r.width = stream->cur_frame->wWidth;sel->r.height = stream->cur_frame->wHeight;mutex_unlock(&stream->mutex);return 0;
}

uvc_ioctl_querymenu 查询菜单

该函数 uvc_ioctl_querymenu 是一个 V4L2 控制的 IOCTL 函数,用于查询菜单项的信息。函数的概述如下:
从文件句柄中获取
uvc_fh 结构体指针
handle。
从句柄中获取视频链
uvc_video_chain 的指针
chain。
调用
uvc_query_v4l2_menu 函数,将视频链和查询菜单结构体
qm 作为参数,查询菜单项的信息。
返回查询结果。
该函数的作用是通过调用 uvc_query_v4l2_menu 函数来实现对菜单项的信息查询。它将视频链和查询菜单结构体传递给 uvc_query_v4l2_menu 函数,并将其返回结果作为自己的返回值。
// 查询菜单

static int uvc_ioctl_querymenu(struct file *file, void *fh,struct v4l2_querymenu *qm)
{// 获取文件句柄struct uvc_fh *handle = fh;// 获取视频链struct uvc_video_chain *chain = handle->chain;// 调用uvc_query_v4l2_menu函数,查询菜单return uvc_query_v4l2_menu(chain, qm);

}
uvc_query_v4l2_menu
该函数 uvc_query_v4l2_menu 用于查询V4L2菜单控制的信息。函数的概述如下:
声明并初始化变量,包括
uvc_menu_info 结构体指针
menu_info,
uvc_control_mapping 结构体指针
mapping,
uvc_control 结构体指针
ctrl,以及索引变量
index 和
id。
清零
query_menu 结构体并设置其
id 和
index。
加锁,以确保在查询期间不会有其他线程修改相关数据。
查找指定的控制器
ctrl,并检查其类型是否为
V4L2_CTRL_TYPE_MENU。
检查索引是否超出菜单项的范围。
获取指定菜单项的信息
menu_info。
如果控制器的数据类型为位掩码且具有
UVC_CTRL_FLAG_GET_RES 标志,则从缓存中获取位掩码,并检查菜单项的值是否有效。
将菜单项的名称拷贝到
query_menu 结构体的
name 字段中。
解锁。
返回查询结果。
该函数的主要功能是根据给定的控制器 ID 和菜单项索引查询菜单项的信息,并将结果填充到 query_menu 结构体中。

int uvc_query_v4l2_menu(struct uvc_video_chain *chain,struct v4l2_querymenu *query_menu)
{struct uvc_menu_info *menu_info; // 定义一个uvc_menu_info结构体指针struct uvc_control_mapping *mapping; // 定义一个uvc_control_mapping结构体指针struct uvc_control *ctrl; // 定义一个uvc_control结构体指针u32 index = query_menu->index; // 获取查询菜单的索引u32 id = query_menu->id; // 获取查询菜单的idint ret;memset(query_menu, 0, sizeof(*query_menu)); // 将查询菜单的内存清零query_menu->id = id; // 将查询菜单的id赋值为idquery_menu->index = index; // 将查询菜单的索引赋值为indexret = mutex_lock_interruptible(&chain->ctrl_mutex); // 加锁if (ret < 0)return -ERESTARTSYS; // 加锁失败,返回错误码ctrl = uvc_find_control(chain, query_menu->id, &mapping); // 查找控制器if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) { // 如果控制器不存在或mapping的v4l2_type成员变量不为V4L2_CTRL_TYPE_MENUret = -EINVAL; // 返回错误码goto done; // 跳转到done标签}if (query_menu->index >= mapping->menu_count) { // 如果查询菜单的索引大于等于mapping的menu_count成员变量ret = -EINVAL; // 返回错误码goto done; // 跳转到done标签}menu_info = &mapping->menu_info[query_menu->index]; // 定义menu_info指针指向mapping的menu_info数组的第query_menu->index个元素if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK && // 如果mapping的data_type成员变量为UVC_CTRL_DATA_TYPE_BITMASK并且ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_RES按位与的结果不为0(ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {s32 bitmap; // 定义一个s32类型的bitmap变量if (!ctrl->cached) { // 如果ctrl的cached成员变量为0ret = uvc_ctrl_populate_cache(chain, ctrl); // 调用uvc_ctrl_populate_cache函数if (ret < 0) // 如果返回值小于0goto done; // 跳转到done标签}bitmap = mapping->get(mapping, UVC_GET_RES, // 将bitmap赋值为mapping的get函数返回值uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));if (!(bitmap & menu_info->value)) { // 如果bitmap与menu_info的value按位与的结果为0ret = -EINVAL; // 返回错误码goto done; // 跳转到done标签}}strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); // 将menu_info的name成员变量拷贝到query_menu的name成员变量中done:mutex_unlock(&chain->ctrl_mutex); // 解锁return ret; // 返回查询结果
}

相关文章:

uvc驱动ioctl分析下

uvc驱动ioctl分析下 文章目录 uvc驱动ioctl分析下uvc_ioctl_enum_input枚举输入uvc_query_ctrl__uvc_query_ctrluvc_ioctl_g_input 获取输入uvc_ioctl_s_input 设置输入uvc_query_v4l2_ctrluvc_ioctl_queryctrl查询控制器uvc_ioctl_query_ext_ctrl查询扩展控制器 uvc_ioctl_g_c…...

数据库可视化神器,你在用哪一款呢

唠嗑部分 在我们日常开发中&#xff0c;作为开发者&#xff0c;与数据库是肯定要打交道的&#xff0c;比如MySQL&#xff0c;Oracle、sqlserver… 那么数据库可视化工具&#xff0c;你用什么呢&#xff1f;小白今天将常用地几款工具列一下&#xff0c;各位小伙伴如有喜欢的自…...

CMD与DOS脚本编程【第三章】

预计更新 第一章. 简介和基础命令 1.1 介绍cmd/dos脚本语言的概念和基本语法 1.2 讲解常用的基础命令和参数&#xff0c;如echo、dir、cd等 第二章. 变量和运算符 2.1 讲解变量和常量的定义和使用方法 2.2 介绍不同类型的运算符和运算规则 第三章. 控制流程和条件语句 3.1 介…...

多激光雷达手眼标定

手眼标定方法已经有很多博客进行解析&#xff0c;但是都是针对机器人的手&#xff08;夹爪&#xff09;眼睛&#xff08;相机&#xff09;进行标定。例如&#xff1a; 标定学习笔记&#xff08;四&#xff09;-- 手眼标定详解 手眼标定_全面细致的推导过程 本文主要描述多激光…...

SQL执行过程

1. select 语句执行过程 一条 select 语句的执行过程如上图所示 1、建立连接 连接器会校验你输入的用户名和密码是否正确&#xff0c;如果错误会返回提示&#xff0c;如果正确&#xff0c;连接器会查询当前用户对于的权限。连接器的作用就是校验用户权限 2、查询缓存 MySQL…...

K8S 部署 seata

文章目录 创建 Deployment 文件创建 ConfigMap 文件创建 Service 文件运行访问高可用部署踩坑 官方文档 k8s中volumeMounts.subPath的巧妙用法 创建 Deployment 文件 deploymemt.yaml namespace&#xff1a;指定命名空间image&#xff1a;使用 1.5.2 版本的镜像ports&#xf…...

ClickHouse:(二)数据类型

1.整型 固定长度的整型分为&#xff1a;有符号和无符合整型 有符号整型无符号整型类型范围类型范围Int8 -128 : 127 UInt8 0 : 255 Int16 -32768 : 32767 UInt16 0 : 65535 Int32 -2147483648 : 2147483647 UInt32 0 : 4294967295 Int64 -9223372036854775808 : 9223372036854…...

项目文档(request页面代码逻辑)

项目文档 目录 项目文档 1. 封装请求基地址 代码 2. 添加请求拦截器并设置请求头 作用 代码部分 3. 添加响应拦截器 作用 代码 4. token过期问题处理 5. 无感刷新 作用 代码 6. refresh_token过期处理 解决方式 1. 封装请求基地址 在src目录下 放上一个专门写…...

后端传到前端的JSON数据大写变小写--2023

问题复现&#xff1a;1. 首先我先说一下&#xff0c;我用了lombok&#xff0c;事实证明和这个也有关系 前端这里写的也是按照驼峰命名来写的 控制台打印出来的数据 后台打印出来的数据 解决方法&#xff1a; 1. 重写get/set方法 因为我在实体类上标注了Data注解 重写get/se…...

学习【菜鸟教程】【C++ 类 对象】【C++ 类的静态成员】

链接 1. 教程 可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时&#xff0c;这意味着无论创建多少个类的对象&#xff0c;静态成员都只有一个副本。 静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句&#xff0c;在创建第一个对象时…...

计算机四大件笔记

啊~数据库、操作系统、计算机网络、Linux start 操作系统 并发和并行 并发是同一时间段内发生了多个事情&#xff0c;多任务之间互相抢占资源。 并行是在同一时间点内发生了多个事情&#xff0c;多任务之间不互相抢占资源&#xff0c;只有多CPU的情况下才能并行。 例如&a…...

【vue上传文件——hash】

vue上传文件 要求:只能上传视频,先计算文件的hash值,hash值一样则不需要上传,不一样在执行上传 分析:因为el-upload没有找到合适的属性,本次用的是原生的input的type属性为file上传 代码: html: 通过点击选取文件按钮调用input上传 js 第一步:点击上传文件先效验是否…...

【OpenCV DNN】Flask 视频监控目标检测教程 01

欢迎关注『OpenCV DNN Youcans』系列&#xff0c;持续更新中 【OpenCV DNN】Flask 视频监控目标检测教程 01 【OpenCV DNN】Flask 视频监控目标检测教程 01 1. 面向Python程序的Web框架2. Flask 框架的安装与使用2.1 Flask 安装2.2 Flask 框架例程2.3 绑定IP和端口2.4 Flask路…...

(转载)从0开始学matlab(第10天)—自顶向下的编程思想

在前面的内容中&#xff0c;我们开发了几个完全运转的 MATLAB 程序。但是这些程序都十分简单&#xff0c;包括一系列的 MATLAB 语句&#xff0c;这些语句按照固定的顺序一个接一个的执行。像这样的程序我们称之顺序结构程序。它首先读取输入&#xff0c;然后运算得到所需结果&a…...

mapreduce技术

要实现操作hbase数据表首先要了解它的原理&#xff1a; 1,Hbase原理篇 HBASE就是基于Hadoop的一个开源项目&#xff0c;也是对Google的BigTable的一种实现。 BigTable最浅显来看就是一张很大的表&#xff0c;表的属性可以根据需求去动态增加&#xff0c;但是又没有表与表之间…...

AI智慧安监视频平台EasyCVR视频出现不能播放的情况排查与解决

EasyCVR基于云边端协同&#xff0c;可支持海量视频的轻量化接入与汇聚管理。平台兼容性强、拓展度高&#xff0c;可提供视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码、平台级联等功能。 有用户反…...

嵌入式学习之Linux驱动(第九期_设备模型_教程更新了)_基于RK3568

驱动视频全新升级&#xff0c;并持续更新~更全&#xff0c;思路更科学&#xff0c;入门更简单。 迅为基于iTOP-RK3568开发板进行讲解&#xff0c;本次更新内容为第九期&#xff0c;主要讲解设备模型&#xff0c;共计29讲。视频选集 0.课程规划 06:35 1.抛砖引玉-设备模型…...

LeetCode662.设计循环队列||4种方法实现

目录 题目 思路1(链表) 代码 思路2(数组) 代码 题目 题目要求的队列需要实现的功能有 ①Creat---设置队列长度 ②Front---获取队列头 ③Rear---获取队列尾 ④en----插入元素 ⑤de---删除元素 ⑥empty---判空 ⑦full---判满 思路1(链表) &#x1f50d;普通队列长度没有限制&…...

人工智能专栏第十二讲——依存解析

依存句法分析是一种自然语言处理技术&#xff0c;其目的是识别句子中单词之间的依赖关系。在自然语言处理中&#xff0c;依存句法分析是一项非常重要的任务&#xff0c;因为它可以帮助我们理解句子的语义结构&#xff0c;从而更好地进行文本分析、信息抽取、语音识别等任务。 …...

nest日志包pino、winston配置-懒人的折腾

nest日志 三种node服务端日志选型 winstonpinolog4js 2023年5月23日 看star数&#xff1a;winston > pino > log4js 使用体验&#xff1a; pino 格式简洁&#xff0c;速度快&#xff0c;支持输入日志到任意数据库&#xff0c;日志暂无自动清理&#xff08;可能是我…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...