1. 在视频驱动程序中设置属性主要涉及3个ioctl :
VIDIOC_QUERYCTRL
VIDIOC_G_CTRL
VIDIOC_S_CTRL
2. 硬件上怎么设置属性呢?
2.1 UVC规划定义了那些属性:定义在uvc_ctrl.c里面的数组
static struct uvc_control_info uvc_ctrls[] = {
{
.entity = UVC_GUID_UVC_PROCESSING, //属于哪一个entity
.selector = UVC_PU_BRIGHTNESS_CONTROL,//用于亮度
.index = 0,// 对应Processing Unit Descriptor 的bmControls[0]
.size = 2,//数据长度2个字节
.flags = UVC_CTRL_FLAG_SET_CUR| UVC_CTRL_FLAG_GET_RANGE| UVC_CTRL_FLAG_RESTORE,
},
}
2.2 设备支持哪些属性呢
这需要具体去看设备描述符,比如 Processing Unit Descriptor 的bmControls值为7f 14
可知BIT0 为1 表示支持BRIGHTNESS
在uvc_probe 函数中对其属性进行了初始化代码中
uvc_ctrl_init_device(dev)
对于每一个entity(IT,PU,SU,OT等)
list_for_each_entry(entity, &dev->entities, list)
分别提取对应的控制集(bmControls) 以及 控制集的大小(bControlSize—》 指定了bmControls字段的大小)
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
bmControls = entity->extension.bmControls;
bControlSize = entity->extension.bControlSize;
} else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
bmControls = entity->processing.bmControls;
bControlSize = entity->processing.bControlSize;
计算bmControls 里面1 的个数 也就是支持的属性个数
ncontrols = memweight(bmControls, bControlSize);
为每一个属性分配一个struct uvc_control
entity->controls = kcalloc(ncontrols, sizeof(*ctrl),
GFP_KERNEL);
设置这些struct uvc_control
ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i)
{
if (uvc_test_bit(bmControls, i) == 0)
continue;
ctrl->entity = entity;
ctrl->index = i;
把uvc_control 和uvc_control_info 挂钩
uvc_ctrl_init_ctrl(dev, ctrl);
ctrl->info = 某个uvc_control_info数组项(同属于一个entity, index相同)
2.3 怎么去操控这些属性呢
参考
uvc_ioctl_queryctrl
---->uvc_query_v4l2_ctrl(chain, qc);
------>uvc_find_control
------> __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl);
--------> __uvc_ctrl_get
---------->uvc_query_ctrl
------------>__uvc_query_ctrl
__uvc_query_ctrl 包装了USB核心的usb_control_msg 函数,可以用来获得或设置特定的属性
/* 用来查询或者设置 uvc的属性
struct uvc_device *dev:指向表示UVC设备的结构体的指针。
__u8 query :指定控制请求的类型,比如GET_CUR(获取当前值)、SET_CUR(设置当前值)等。
__u8 unit :指定请求目标的单元ID,例如摄像头控制单元(CCU)或处理单元(PU)。
__u8 intfnum :指定USB接口编号。
__u8 cs :指定控制选择符(Control Selector),定义了要控制的具体功能。
void *data :指向数据的指针,用于发送或接收控制消息的数据部分。
__u16 size :数据部分的大小,以字节为单位。
int timeout :控制请求的超时时间,以毫秒为单位。
*/
static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size,
int timeout)
{
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
/*1.设置类型 USB_TYPE_CLASS 特定请求 USB_RECIP_INTERFACE 请求的接收者是一个接口*/
unsigned int pipe;
pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
: usb_sndctrlpipe(dev->udev, 0);
/*2.根据query的最高位进行判断 是发送数据还是读取数据*/
type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
/*3.发送控制信息 data 缓冲区*/
return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
unit << 8 | intfnum, data, size, timeout);
}
关于结构体 uvc_control_mapping ,用来更加细致的描述 属性
例如 struct uvc_control_mapping uvc_ctrl_mapping[]
3. 比如设置亮度 如何操作呢
3.1 根据PU的描述符的bmControl 找到它的bot 0 = 1,也就找到了是否支持亮度调节
3.2 在uvc_ctrls数组中根据id和index 找到这一项:
{
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_BRIGHTNESS_CONTROL,
.index = 0,
.size = 2,
.flags = UVC_CTRL_FLAG_SET_CUR
| UVC_CTRL_FLAG_GET_RANGE
| UVC_CTRL_FLAG_RESTORE,
},
可以知道,这个设备支持 UVC_CTRL_FLAG_SET_CUR, UVC_CTRL_FLAG_GET_RANGE,UVC_CTRL_FLAG_RESTORE,要进行设置时可以向PU的selector 发送2字节数据
3.3 在uvc_control_mapping 数组中根据ID 找到对应的数组项,从而知道 更加细致的描述信息,然后使用usb_control_msg 读写数据
具体步骤
3.3.1 首先是设置控制值 参考: uvc_ctrl_set(chain, &xctrl);
- 需要根据 id 找到对应的uvc_ctrl_mapping
/*参考 static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
struct uvc_control_mapping **mapping, struct uvc_control **control,
int next)函数*/
for (i = 0; i < entity->ncontrols; ++i) { /*遍历所有实体的控制项*/
ctrl = &entity->controls[i];
if (!ctrl->initialized)/*是否初始化*/
continue;
list_for_each_entry(map, &ctrl->info.mappings, list) { /*遍历其映射表*/
if ((map->id == v4l2_id) && !next) {
*control = ctrl;
*mapping = map;
return;
}
- 根据 属性类型 v4l2_type 设置不同的data_type 比如 UVC_CTRL_DATA_MIN
switch (mapping->v4l2_type)
- 将调整后的值写入到控制数据中
mapping->set(mapping, value,uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
3.3.2 然后就是提交控制值 参考: uvc_ctrl_commit(handle, &xctrl, 1);
一系列复杂调用之后 通过usb_control_msg 发送给硬件
参考这句代码
__uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,UVC_CTRL_CONTROL_TIMEOUT);