uvc驱动ioctl分析下
函数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_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).
", uvc_query_name(query), cs,
unit, ret, size);
return -EIO;
}
return 0;
}
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。如果最高位为则表示接收控制消息,使用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用于获取当前视频输入源的索引。函数的主要步骤如下:从文件句柄fh中获取UVC文件句柄handle和UVC视频链chain。如果视频链中没有选择器或者忽略选择器单元,执行以下操作:将输入索引input设置为0。返回成功状态码0。否则,执行以下操作:使用uvc_query_ctrl函数查询选择器控件的当前值,其中参数包括设备chain->dev、选择器IDchain->selector->id、接口号chain->dev->intfnum、控件类型UVC_SU_INPUT_SELECT_CONTROL和输出缓冲区&i。如果查询失败,返回相应的错误码。将查询到的输入索引值减去并将结果存储到输入索引指针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是用于设置视频设备的输入选择的函数。函数的概括如下:获取文件句柄和视频链。尝试获取权限。如果视频链没有选择器或者选择器单元被忽略:如果输入参数不为0,则返回错误。如果输入参数为0,则返回成功。如果视频链有选择器:如果输入参数大于等于选择器的输入引脚数,则返回错误。设置输入索引为输入参数加调用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用于查询控制器属性并填充到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类型进行不同的操作:如果是菜单类型,设置最小值、最大值和步长,并找到默认值所在的索引。如果是布尔类型,设置最小值为0,最大值为步长为如果是按钮类型,设置最小值为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参数决定是提交控制还是回滚控制。返回操作结果。该函数的主要目的是循环尝试设置扩展控制,并根据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);
}
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取消订阅事件
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; // 返回无效参数错误
}
}
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
}
// 枚举帧大小
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;
}
// 获取流参数
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获取参数
// 获取流参数
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);
}
// 获取选择区域
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;
}
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);
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; // 返回查询结果
}
文章为作者独立观点,不代表股票配资公司观点