之前的程序只实现了数据的传输,在pc linux环境下智能看到摄像 头捕抓的数据,不能调节亮度等属性。
1.设置属性
(1)应用程序xawtv部分分析
所以: 视频驱动里要实现3个ioctl:
VIDIOC_QUERYCTRL//查询是否支持此属性
VIDIOC_G_CTRL //获得属性
VIDIOC_S_CTRL//设置属性
(2)底层驱动分析
USB摄像头的内部结构回顾
videostreaming interface用于接收视频数据,设置属性应该操作videocontrl interface,里面有很多个单元,这些单元在代码里称为实体。
在uvc规范中(uvc1.5 class specification.pdf),找到processing Uint Descriptor,里面的bmcontrols的每一位对应一种属性
2. 硬件上怎么设置属性?
2.1 UVC规范里定义了哪些属性(参考上面内容) : uvc_ctrl.c里数组: static struct uvc_control_info uvc_ctrls[]
//每一项对应一个属性
{
.entity= UVC_GUID_UVC_PROCESSING,// 属于哪了个entity(比如PU),我们要去设置亮度,需要把数据发给硬件,发给硬件上的videoctrol interface的哪一个entity.把数据发给PU ,怎么知道数据用于设置PU的哪一个属性
.selector= PU_BRIGHTNESS_CONTROL, // 用于亮度(用于判别选择PU的某个属性)
.index= 0, // 对应Processing Unit Descriptor的bmControls[0]
.size= 2, // 数据长度为2字节
.flags= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE,//SET_CUR表示设置当前值(亮度),
},
2.2 我们的设备支持哪些属性
这需要去看描述符, 比如 Processing Unit Descriptor的bmControls的值为7f 14
可知BIT0为1,表示支持BRIGHTNESS
在代码里:
uvc_drvier.c
uvc_ctrl_init_device
// 对于每一个entity(IT,PU,SU,OT等)
list_for_each_entry(entity, &dev->entities, list) {
// 取出bmControls
bmControls = …
// 计算bmControls里位值为1的个数,就是支持的属性个数
ncontrols += hweight8(bmControls[i]);
// 为每一个属性分配一个struct uvc_control
entity->controls = kzalloc…
// 设置这些struct uvc_control
ctrl = entity->controls;
for (…)
{
ctrl->entity = entity;
ctrl->index = i;
}
// 把uvc_control和uvc_control_info(数组)挂构
uvc_ctrl_add_ctrl(dev, info);
ctrl->info = 某个uvc_control_info数组项(同属于一个entity, index相同)
2.3 怎么去操作这些属性
参考 uvc_query_v4l2_ctrl
uvc_find_control
找到一个uvc_control_mapping结构体: uvc_ctrl.c里有static struct uvc_control_mapping uvc_ctrl_mappings[]
{
.id= V4L2_CID_BRIGHTNESS, // APP根据ID来找到对应的属性
.name= “Brightness”,
.entity= UVC_GUID_UVC_PROCESSING, // 属于哪了个entity(比如PU)
.selector= PU_BRIGHTNESS_CONTROL, // 用于亮度
.size= 16, // (为2字节)数据占多少位(对于某些属性只用到里面的若干位)
.offset= 0, // 从哪位开始(用到的若干位从哪里开始 )
.v4l2_type= V4L2_CTRL_TYPE_INTEGER, // 属性类别
.data_type= UVC_CTRL_DATA_TYPE_SIGNED,// 数据类型(有符号或无符号)
},
uvc_control_mapping结构体 用来更加细致地描述属性,和uvc_control_info基本一一对应。
uvc_query_ctrl
usb_control_msg(发起usb控制传输)
举例说明: 要设置亮度,怎么操作?
a. 根据PU的描述符的bmControls, 从它的bit0等于1知道它支持调节亮度
b. 在uvc_ctrls数组中根据entity和index找到这一项:
{
.entity= UVC_GUID_UVC_PROCESSING,
.selector= PU_BRIGHTNESS_CONTROL,
.index= 0,
.size= 2,
.flags= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE,
},
知道了:这个设备支持SET_CUR, GET_CUR, GET_MIN等
要设置时,可以向PU的selector发数据, 发的数据是2字节
c. 根据应用程序给的ID值,在uvc_ctrl_mappings数组中根据ID找到对应的数组项
从而知道了更加细致的信息,
然后使用usb_control_msg读写数据
3. 怎么写代码?
实现3个ioctl: vidioc_queryctrl/vidioc_g_ctrl/vidioc_s_ctrl
vidioc_queryctrl : 发起USB控制传输获得亮度的最小值、最大值、默认值、步进值
vidioc_s_ctrl : 把APP传入的亮度值通过USB传输发给硬件
vidioc_g_ctrl : 发起USB传输获得当前亮度值
要点:数据发给谁?发给usb_device的
VideoControl Interface
里面的Processing Unit
里面的PU_BRIGHTNESS_CONTROL
3、函数实现
(1)查询支持属性
/* 参考:uvc_query_v4l2_ctrl */
static int myuvc_vidioc_queryctrl(struct file *file, void *fh,
struct v4l2_queryctrl *ctrl)
{
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
int ret;
u8 data[2];
//只支持调节亮度(判断ID值)
if( ctrl->id != V4L2_CID_BRIGHTNESS )
return -EINVAL;
//对结构体进行清零并进行设置
memset(ctrl, 0, sizeof *ctrl);
ctrl->id = V4L2_CID_BRIGHTNESS;
ctrl->type =V4L2_CTRL_TYPE_INTEGER;
strcpy(ctrl->name, "MyUVC_BRIGHTNESS");
ctrl->flags = 0;
pipe = usb_rcvctrlpipe(myuvc_udev, 0);
type |= USB_DIR_IN;
/* 发起USB传输确定这些值
//我们要操作的结构体是myuvc_udev,要操作的接口是控制接口myuvc_control_intf,
访问哪一个Unit(entity)是ProcessingUnitID,
访问PU里的哪一个属性根据PU_BRIGHTNESS_CONTROL,
读取数据的大小为2,data用于存储读取到的数据,
5000是5秒的超时时间,根据GET_MIN知道要读取的是min值
*/
ret = usb_control_msg(myuvc_udev, pipe, GET_MIN, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
if (ret != 2)
return -EIO;
ctrl->minimum = myuvc_get_le_value(data); /* Note signedness读取到数据后设置min值,根据id值找到uvc_ctrl_mappings数组里面对应的项,
根据数据所占位的大小和偏移值来从data中取出min值 */
ret = usb_control_msg(myuvc_udev, pipe, GET_MAX, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
if (ret != 2)
return -EIO;
ctrl->maximum = myuvc_get_le_value(data); /* Note signedness */
ret = usb_control_msg(myuvc_udev, pipe, GET_RES, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
if (ret != 2)
return -EIO;
ctrl->step = myuvc_get_le_value(data); /* Note signedness */
ret = usb_control_msg(myuvc_udev, pipe, GET_DEF, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
if (ret != 2)
return -EIO;
ctrl->default_value = myuvc_get_le_value(data); /* Note signedness */
//打印最小值、最大值、阶梯值、默认值
printk("Brightness: min =%d, max = %d, step = %d, default = %d\n", ctrl->minimum, ctrl->maximum, ctrl->step, ctrl->default_value);
return 0;
}
(2)获得属性
/* 参考 : uvc_ctrl_get */
int myuvc_vidioc_g_ctrl (struct file *file, void *fh,
struct v4l2_control *ctrl)
{
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
int ret;
u8 data[2];
if (ctrl->id != V4L2_CID_BRIGHTNESS)
return -EINVAL;
pipe = usb_rcvctrlpipe(myuvc_udev, 0);
type |= USB_DIR_IN;
/* 发起USB传输
ret = usb_control_msg(myuvc_udev, pipe, GET_CUR, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);//获得当前亮度值
if (ret != 2)
return -EIO;
ctrl->value = myuvc_get_le_value(data);/* Note signedness */进行转换
return 0;
}
(3)设置属性(把应用程序传进来的值转换成16位数据)
static int myuvc_vidioc_s_ctrl(struct file *file, void *fh,
struct v4l2_control *ctrl)
{
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
int ret;
u8 data[2];
if (ctrl->id != V4L2_CID_BRIGHTNESS)
return -EINVAL;
myuvc_set_le_value(ctrl->value, data);
pipe = usb_sndctrlpipe(myuvc_udev, 0);
type |= USB_DIR_OUT;
ret = usb_control_msg(myuvc_udev, pipe, SET_CUR, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
if (ret != 2)
return -EIO;
return 0;
}