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; // 返回查询结果
}