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;
        // 返回0
        return 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;
    // 返回0
    return 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;
        // 返回0
        return 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; // 定义一个无符号整型变量i

    memset(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成员变量赋值为0

    if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_CUR按位与的结果为0
        v4l2_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按位与的结果为0
        v4l2_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按位与的结果不为0
        s32 val; // 定义一个有符号32位整型变量val
        int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); // 调用__uvc_ctrl_get函数,获取控制器的值
        if (ret < 0) // 如果ret小于0
            return ret; // 返回ret

        if (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按位与的结果不为0
    if (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成员变量赋值为i
                v4l2_ctrl->default_value = i;
                break;
            }
        }

        return 0;


    case V4L2_CTRL_TYPE_BOOLEAN: // 如果mapping的v4l2_type成员变量为V4L2_CTRL_TYPE_BOOLEAN
        v4l2_ctrl->minimum = 0; // 将v4l2_ctrl的minimum成员变量赋值为0
        v4l2_ctrl->maximum = 1; // 将v4l2_ctrl的maximum成员变量赋值为1
        v4l2_ctrl->step = 1; // 将v4l2_ctrl的step成员变量赋值为1
        return 0; // 返回0

    case V4L2_CTRL_TYPE_BUTTON: // 如果mapping的v4l2_type成员变量为V4L2_CTRL_TYPE_BUTTON
        v4l2_ctrl->minimum = 0; // 将v4l2_ctrl的minimum成员变量赋值为0
        v4l2_ctrl->maximum = 0; // 将v4l2_ctrl的maximum成员变量赋值为0
        v4l2_ctrl->step = 0; // 将v4l2_ctrl的step成员变量赋值为0
        return 0; // 返回0

    default: // 如果mapping的v4l2_type成员变量不为V4L2_CTRL_TYPE_BOOLEAN或V4L2_CTRL_TYPE_BUTTON
        break;
    }

    if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) // 如果ctrl的info的flags成员变量与UVC_CTRL_FLAG_GET_MIN按位与的结果不为0
        v4l2_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按位与的结果不为0
        v4l2_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按位与的结果不为0
        v4l2_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);
    else
        return 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; // 分母为10000000
        uvc_simplify_fraction(&fival->discrete.numerator, &fival->discrete.denominator, 8, 333); // 简化分数
    } else { // 如果帧间隔类型不存在
        if (fival->index) // 如果索引不为0
            return -EINVAL; // 返回无效参数错误

        fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; // 帧间隔类型为步进型
        fival->stepwise.min.numerator = frame->dwFrameInterval[0]; // 最小值为第一个帧间隔
        fival->stepwise.min.denominator = 10000000; // 分母为10000000
        fival->stepwise.max.numerator = frame->dwFrameInterval[1]; // 最大值为第二个帧间隔
        fival->stepwise.max.denominator = 10000000; // 分母为10000000
        fival->stepwise.step.numerator = frame->dwFrameInterval[2]; // 步进值为第三个帧间隔
        fival->stepwise.step.denominator = 10000000; // 分母为10000000
        uvc_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; // 获取查询菜单的id
    int ret;

    memset(query_menu, 0, sizeof(*query_menu)); // 将查询菜单的内存清零
    query_menu->id = id; // 将查询菜单的id赋值为id
    query_menu->index = index; // 将查询菜单的索引赋值为index

    ret = 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_MENU
        ret = -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成员变量为0
            ret = uvc_ctrl_populate_cache(chain, ctrl); // 调用uvc_ctrl_populate_cache函数
            if (ret < 0) // 如果返回值小于0
                goto 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按位与的结果为0
            ret = -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; // 返回查询结果
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/22850.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

爬虫基本原理

爬虫基本原理 1.1获取网页1.1.1提取信息1.1.2保存数据 1.2请求1.2.1 请求方法1.2.2 请求网址1.2.3 请求头1.2.4请求体1.3响应 1.1获取网页 爬虫首先要做的工作就是获取网页&#xff0c;这里就是获取网页的源代码。源代码里包含了网页的部分有用信息&#xff0c;所以只要把源代…

【航空和卫星图像中检测建筑物】使用gabor特征和概率的城市区域和建筑物检测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

如何把数据从 TDengine 2.x 迁移到 3.x ?

一.迁移背景&#xff1a; 随着时序数据库&#xff08;Time Series Database&#xff09; TDengine 3.0 的发布至今&#xff0c;我们除了在持续地优化产品质量的本身&#xff0c;也一直在努力地提升用户体验。但由于 3.0 底层有大量的重构优化&#xff0c;导致开源版的 2.0 用户…

免费部署你的私人 ChatGPT 网页应用

免费部署你的私人 ChatGPT 网页应用 1、注册Github账号&#xff0c;拷贝仓库 第一步、打开GitHub官网&#xff0c;点击右上角Sign up注册即可 第二步、打开开源项目【Chatgpt-next-web】,点击fork&#xff0c;点击Create fork完成操作 2、选择免费的容器【vercel】或者【r…

2023新版Spring6全新讲解-核心内容之IoC

Spring核心之IoC 一、IoC概念介绍 1.IoC 介绍 IoC 是 Inversion of Control 的简写&#xff0c;译为“控制反转”&#xff0c;它不是一门技术&#xff0c;而是一种设计思想&#xff0c;是一个重要的面向对象编程法则&#xff0c;能够指导我们如何设计出松耦合、更优良的程序。…

USB摄像头描述符参数获取和来源分析

USB摄像头描述符参数获取和来源分析 文章目录 USB摄像头描述符参数获取和来源分析描述符USB设备描述符描述符 USB摄像头参数获取myuvc.c结果device descriptor设备描述符configuration descriptor配置描述符interface association接口关联inteface desciptor atsettingvideocon…

Linux :: 【基础指令篇 :: 用户管理:(2)】::设置用户密码(及本地Xshell 登录云服务器操作演示) :: passwd

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 目录索引&#xff1a; 1. 基本语法 2. 基本用法 3. 注意点 4. 补充&#xff1a;指定用户设置密码操作实例测试及登录本地 Xshell 登录演…

前端微服务无界实践 | 京东云技术团队

一、前言 随着项目的发展&#xff0c;前端SPA应用的规模不断加大、业务代码耦合、编译慢&#xff0c;导致日常的维护难度日益增加。同时前端技术的发展迅猛&#xff0c;导致功能扩展吃力&#xff0c;重构成本高&#xff0c;稳定性低。因此前端微服务应运而生。 前端微服务优势…

什么是智慧校园?

什么是智慧校园&#xff1f; 智慧校园平台是目前教育信息化领域的热点之一。 随着数字化转型的加速&#xff0c;越来越多的学校开始寻求解决方案&#xff0c;以提高教育管理的效率和质量。 在使用智慧校园平台的过程中&#xff0c;一些痛点问题也浮现出来。为解决这些问题&a…

10 工具Bootchart的使用(windows)

Bootchart的使用方法&#xff08;windows&#xff09; 下载bootchart.jar并拷贝到windows, 然后保证windows也安装了open jdk 1.8; 下载地址&#xff1a;https://download.csdn.net/download/Johnny2004/87807973 打开设备开机启动bootchart的开关: adb shell touch /data/boo…

DID-M3D 论文学习

1. 解决了什么问题&#xff1f; 单目 3D 检测成本低、配置简单&#xff0c;对一张 RGB 图像预测 3D 空间的 3D 边框。最难的任务就是预测实例深度&#xff0c;因为相机投影后会丢失深度信息。以前的方法大多直接预测深度&#xff0c;本文则指出 RGB 图像上的实例深度不是一目了…

【学习日记2023.5.22】 之 套餐模块完善

4. 功能模块完善之套餐模块 4.1 新增套餐 4.1.1 需求分析与设计 产品原型 后台系统中可以管理套餐信息&#xff0c;通过 新增功能来添加一个新的套餐&#xff0c;在添加套餐时需要添加套餐对应菜品的信息&#xff0c;并且需要上传套餐图片。 新增套餐原型&#xff1a; 当填…

自动化如何做?爆肝整理企业自动化测试工具/框架选择实施,你要的都有...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

web基础与HTTP协议

web基础与HTTP协议 一、域名概述二、网页的概念三、HTML四、web概述静态网页&#xff1a;动态页面动态页面与静态页面的区别 五、HTTP 一、域名概述 域名的概念&#xff1a;IP地址不易记忆 早期使用Hosts文件解析域名 – 主机名称重复 – 主机维护困难 DNS&#xff08;域名系…

大学4年做出来这个算不算丢人

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

开发者关系工程师如何帮助开发者在Sui上构建

近期&#xff0c;我们与Sui开发者关系负责人Brian Hennessey-Hsien进行了对话&#xff0c;就Sui上的开源、去中心化和开发者成就等话题展开讨论。 日前&#xff0c;我们采访了Sui基金会的开发者关系负责人Brian Hennessey-Hsieh&#xff0c;共同探讨了其对于Web3中开发者发展历…

2009.03-2022.06华证ESG季度评级(季度)

2009.03-2022.06华证ESG评级&#xff08;季度&#xff09; 1、时间&#xff1a;2009.03-2022.06.15 2、来源&#xff1a;整理自Wind 3、指标&#xff1a;华证ESG&#xff08;只有综合评级&#xff0c;无细分评级数据&#xff09; 4、样本数量&#xff1a;A股4800多家公司 …

【数据安全-02】AI打假利器数字水印,及java+opencv实现

AIGC 的火爆引燃了数字水印&#xff0c;说实话数字水印并不是一项新的技术&#xff0c;但是这时候某些公司拿出来宣传一下特别应景&#xff0c;相应股票蹭蹭地涨。数字水印是什么呢&#xff0c;顾名思义&#xff0c;和我们在pdf中打的水印作用差不多&#xff0c;起到明确版权、…

拉货搬家货运APP开发分析和功能列表

作为国家经济发展的重要基础设施&#xff0c;物流行业正在面对转型升级的风口。巨大的市场体量&#xff0c;也迎来了激烈的市场竞争。为了从同质化的服务中脱颖而出&#xff0c;开拓更大的市场&#xff0c;并且解决线下司机的载货痛点&#xff0c;货运APP的开发必不可少。 开发…

firewalld防火墙

firewalld防火墙 1&#xff1a;firewalld概述 firewalld防火墙是Centos7系统默认的防火墙管理工具&#xff0c;取代了之前的iptables防火墙&#xff0c;也是工作在网络层&#xff0c;属于包过滤防火墙。firewalld和iptables都是用来管理防火墙的工具&#xff08;属于用户态&a…