JZ2440 摄像头驱动之设置属性_学习笔记

1、引言

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

卸载自己写的驱动程序,安装系统自带的驱动,执行xawtv,可以查看可以调节的属性



2、设置属性

(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)底层驱动分析


   /* 查询/获得/设置属性 */
        .vidioc_queryctrl     = myuvc_vidioc_queryctrl,
        .vidioc_g_ctrl        = myuvc_vidioc_g_ctrl,
        .vidioc_s_ctrl        = myuvc_vidioc_s_ctrl,

        }


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 */    
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;//设置iD值
ctrl->type = V4L2_CTRL_TYPE_INTEGER;//类型
strcpy(ctrl->name, "MyUVC_BRIGHTNESS");//名字
ctrl->flags = 0;

v4l2_queryctrl 结构体参考http://blog.csdn.net/u011425939/article/details/53671869

pipe = usb_rcvctrlpipe(myuvc_udev, 0);//把指定USB设备指定端点设置为一个控制IN端点。

   unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
         把指定USB设备指定端点设置为一个控制IN端点。

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 */step是阶梯值,也就是每调节1步变化值是多少

  /* 发起USB传输
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位数据)

/* 参考: uvc_ctrl_set/uvc_ctrl_commit */
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);//把传进来的value值转换成data值


    pipe = usb_sndctrlpipe(myuvc_udev, 0);//把指定USB设备指定端点设置为一个控制OUT端点
    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;
}




猜你喜欢

转载自blog.csdn.net/weixin_38807927/article/details/87919541