USB摄像头驱动之设置属性

之前的程序只实现了数据的传输,在pc linux环境下智能看到摄像 头捕抓的数据,不能调节亮度等属性。
1.设置属性
(1)应用程序xawtv部分分析

  1. 先看APP以确定需要实现哪些接口
    xawtv.c:
    grabber_scan
    ng_vid_open//对ng_vid_drivers链表的每一个成员都取出来,调用其open函数
    v4l2_driver.open //v4l2_driver结构体的 v4l2_open函数
    get_device_capabilities(h);
    // 调用VIDIOC_QUERYCTRL ioctl确定是否支持某个属性
    /* controls */对于0~MAX_CTRL,先设置ctrl的ID,调用ioctl看看驱动程序是否支持对应ID的属性,如果支持就把此属性记录下来,如果不支持吧ID改为-1,
    for (i = 0; i < MAX_CTRL; i++) {
    h->ctl[i].id = V4L2_CID_BASE+i;
    if (-1 == xioctl(h->fd, VIDIOC_QUERYCTRL, &h->ctl[i], EINVAL) ||
    (h->ctl[i].flags & V4L2_CTRL_FLAG_DISABLED))
    h->ctl[i].id = -1;
    }
    怎么去获得/设置属性?
    看drv0-v4l2.c
    可见这2个函数:
    v4l2_read_attr : VIDIOC_G_CTRL
    v4l2_write_attr : VIDIOC_S_CTRL

所以: 视频驱动里要实现3个ioctl:
VIDIOC_QUERYCTRL//查询是否支持此属性
VIDIOC_G_CTRL //获得属性
VIDIOC_S_CTRL//设置属性

(2)底层驱动分析

USB摄像头驱动之设置属性_第1张图片

USB摄像头的内部结构回顾
videostreaming interface用于接收视频数据,设置属性应该操作videocontrl interface,里面有很多个单元,这些单元在代码里称为实体。
USB摄像头驱动之设置属性_第2张图片
在uvc规范中(uvc1.5 class specification.pdf),找到processing Uint Descriptor,里面的bmcontrols的每一位对应一种属性
USB摄像头驱动之设置属性_第3张图片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;

}


你可能感兴趣的:(jz2440)