uvc驱动ioctl分析下

简介: uvc驱动ioctl分析下

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

如果文章对您有帮助,点赞👍支持,感谢🤝


目录
相关文章
|
6月前
|
存储 Linux 开发工具
Rockchip系列之浅度分析UART接口系列(1)
Rockchip系列之浅度分析UART接口系列(1)
334 1
|
存储 Linux API
嵌入式Linux 字符设备驱动标准ioctl接口
嵌入式Linux 字符设备驱动标准ioctl接口
|
存储 缓存 流计算
uvc驱动ioctl分析上
uvc驱动ioctl分析上
153 0
|
存储 Linux
uvc驱动中的v4l2
uvc驱动中的v4l2
129 0
|
监控 Linux
uvc摄像头驱动uvc设备的注册分析
uvc摄像头驱动uvc设备的注册分析
385 0
|
Linux Android开发 芯片
Linux驱动分析之Framebuffer驱动
前面我们了解了LCD的基本架构《Linux驱动分析之LCD驱动架构》,接下来我们拿个具体的实例来分析分析。这样可以了解其大概是如何使用和工作的。
UART子系统(十)UART驱动情景分析_read
UART子系统(十)UART驱动情景分析_read
193 2
UART子系统(十)UART驱动情景分析_read
|
缓存 Linux 芯片
Linux驱动分析之Uart驱动
之前对Uart驱动的整体架构做了介绍,现在来分析具体的驱动程序。我们以NXP 的 IMX6来进行分析。
|
索引
UVC 基础学习(3):基础描述符介绍
UVC 基础学习(3):基础描述符介绍
562 0
|
Ubuntu Linux Shell
UART子系统(三) UART属于TTY体系之一
UART子系统(三) UART属于TTY体系之一
236 1
UART子系统(三) UART属于TTY体系之一