第2课第1.1.8节 摄像头驱动_从零写USB摄像头驱动之设置属性

  硬件上怎么设置属性?
1.  UVC规范里定义了哪些属性 : uvc_ctrl.c里数组: static struct uvc_control_info uvc_ctrls[],这个数组里每一项都对应一个属性,这个数组里就定义了UVC规范所支持的所有属性。
    以设置亮度为例讲述设置过程:我们要设置亮度,就需要发一个数据给硬件,就要把数据发给VC下的PU模块,发给PU里面的selector。
{
.entity = UVC_GUID_UVC_PROCESSING, // 属于哪个entity(比如PU),比如我们要设置亮度,需要把数据发给硬件,那发给哪个接口呢?VC。那发给VC接口的那哪一个entity呢,这个成员就是指定entity。
.selector = PU_BRIGHTNESS_CONTROL,   // 用于亮度。PU中有许多属性,我们把数据发给PU,那我们怎么知道这个数据是用来设置亮度的呢,这个成员就是用来分辨的。
.index = 0,                       // 对应Processing Unit Descriptor的bmControls[0],表示是PU中的第几个属性
.size = 2,                       // 数据长度为2字节
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE,                  //UVC_CONTROL_SET_CUR 表示我们可以设置当前的亮度等
},

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相同的数组项)


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,                       // 数据占多少位
        .offset = 0,                        // 从哪位开始
        .v4l2_type = V4L2_CTRL_TYPE_INTEGER,   // 属性类别
        .data_type = UVC_CTRL_DATA_TYPE_SIGNED,// 数据类型
        },


         uvc_control_mapping结构体 用来更加细致地描述属性,比如传输的数据是什么数据类型等。


    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. 在uvc_ctrl_mappings数组中根据ID(比如V4L2_CID_BRIGHTNESS)找到对应的数组项
   从而知道了更加细致的信息,

   然后使用usb_control_msg读写数据




 怎么写代码?
实现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




/* 参考: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];


    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);                                 //端点0,控制传输端点
type |= USB_DIR_IN;


    /* 发起USB传输确定ctrl内其他成员的值 */
ret = usb_control_msg(myuvc_udev, pipe, GET_MIN, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);             //发起控制传输,超时时间5000ms
    if (ret != 2)                                //为什么是2?
        return -EIO;
ctrl->minimum = myuvc_get_le_value(data); //通过USB传输,获得两个字节的数据,这一步就是把数据解析出来,得到minimum

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;
}


/* 参考 : 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)                              //uvc_control_mapping结构体里的id,这里的一个ctrl应该是对应一个属性(比如亮度)
        return -EINVAL;


pipe = usb_rcvctrlpipe(myuvc_udev, 0);                //rcv表示接收,及从设备传进来
type |= USB_DIR_IN;


ret = usb_control_msg(myuvc_udev, pipe, GET_CUR, type, PU_BRIGHTNESS_CONTROL << 8,
ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);   //跟myuvc_udev这个USB设备的myuvc_control_intf接口的ProcessingUnitID (PU)的PU_BRIGHTNESS_CONTROL (selector)建立数据传输,因为是从设备中读取,所以type是USB_DIR_IN。
    if (ret != 2)
        return -EIO;
ctrl->value = myuvc_get_le_value(data); //获得亮度值后,解析后放入ctrl->value 。
    
    return 0;

}


/* 参考: 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);                   //把一个数值转换成若干位,也就是把ctrl传进的亮度值转换成16位数据放到data里面去,转换之后就可以直接发给USB设备了


    pipe = usb_sndctrlpipe(myuvc_udev, 0);    //snd表示发出,及发给设备 ,发给设备的第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;
}

猜你喜欢

转载自blog.csdn.net/hahaha_2017/article/details/80037173