(十一) UVC調節亮度



title: UVC調節亮度
date: 2019/4/23 20:30:00
toc: true

UVC調節亮度

引入

攝像頭的參數比如亮度等是通過VC接口控制的,具體可以參考APP的調用流程,這里暫時不分析了

xawtv.c:
    grabber_scan
        ng_vid_open
            v4l2_driver.open // v4l2_open
                get_device_capabilities(h);
                    // 調用VIDIOC_QUERYCTRL ioctl確定是否支持某個屬性
                    /* controls */
                    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

直接說結論

ioctl中的VIDIOC_QUERYCTRL來查詢是支持的屬性,VIDIOC_G_CTRL,VIDIOC_S_CTRL來讀取設置具體的屬性

硬件協議速覽

亮度設置等屬性是歸屬於PU的,我們找到uvc規范中的Processing Unit Descriptor

mark

這里有一個3字節的bmControls指示了所有的屬性,1表示支持這個屬性,0表示不支持.我們之前讀取聯合接口描述符可以分析出PU是支持亮度調節的

mark

代碼框架

對於硬件的描述,程序在uvc_ctrl.c > uvc_ctrls也描述了這個

  • SU,PU,CT,IT等都抽象為entity,這使用16個字節的GUID標識
  • selector標識了具體的屬性,index則指出了在實際的數據位,在亮度中為bit0,這里index=0
  • size標識了數據的大小,單位為字節,亮度為兩個字節,有些屬性可能為11位,那么他也占據兩個字節,具體有11位在下面另外一個結構體uvc_ctrl_mappings指出
  • flags表示可讀可寫等
static struct uvc_control_info uvc_ctrls[] = {
	{
		.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,
	},
	.....
    {
		.entity		= UVC_GUID_UVC_PROCESSING,
		.selector	= UVC_PU_ANALOG_LOCK_STATUS_CONTROL,
		.index		= 17,
		.size		= 1,
		.flags		= UVC_CTRL_FLAG_GET_CUR,
	},
    //===================================================================//
	{
		.entity		= UVC_GUID_UVC_CAMERA,
		.selector	= UVC_CT_SCANNING_MODE_CONTROL,
		.index		= 0,
		.size		= 1,
		.flags		= UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
				| UVC_CTRL_FLAG_RESTORE,
	},

這里是另外更詳細的一些描述

  • size標識了使用了上述字節數中的多少位
  • offset 則是偏移量
  • v4l2_type提供給APP這是什么數據類型,比如菜單,滑動條等
  • data_type則是數據的類型
static struct uvc_control_mapping uvc_ctrl_mappings[] = {
	{
		.id		= V4L2_CID_BRIGHTNESS,
		.name		= "Brightness",
		.entity		= UVC_GUID_UVC_PROCESSING,
		.selector	= UVC_PU_BRIGHTNESS_CONTROL,
		.size		= 16,
		.offset		= 0,
		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
	},
....
    
enum v4l2_ctrl_type {
	V4L2_CTRL_TYPE_INTEGER	     = 1,
	V4L2_CTRL_TYPE_BOOLEAN	     = 2,
	V4L2_CTRL_TYPE_MENU	     = 3,
	V4L2_CTRL_TYPE_BUTTON	     = 4,
	V4L2_CTRL_TYPE_INTEGER64     = 5,
	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
	V4L2_CTRL_TYPE_STRING        = 7,
	V4L2_CTRL_TYPE_BITMASK       = 8,
};
   
/* Data types for UVC control data */
#define UVC_CTRL_DATA_TYPE_RAW		0
#define UVC_CTRL_DATA_TYPE_SIGNED	1
#define UVC_CTRL_DATA_TYPE_UNSIGNED	2
#define UVC_CTRL_DATA_TYPE_BOOLEAN	3
#define UVC_CTRL_DATA_TYPE_ENUM		4
#define UVC_CTRL_DATA_TYPE_BITMASK	5
   

屬性初始化

uvc_drvier.c > uvc_ctrl_init_device 中會通過USB讀取到具體支持哪些屬性,並構造一個結構去管理.

最終目的就是實現

  1. 讀取具體的屬性支持,構造uvc_control

  2. 將之前統一設置的uvc_ctrls(包含了具體的屬性是否可讀的信息等),掛載到上述構造的結構中

    memcpy(&ctrl->info, info, sizeof(*info))
    //>ctrl->info = 某個uvc_control_info數組項
    

詳細的流程如下

uvc_ctrl_init_device
	// 遍歷所有的 pu/eu/ct 
	list_for_each_entry(entity, &dev->entities, list) {
		struct uvc_control *ctrl;
		// 取出具體的數據,數據size
		bmControls = entity->processing.bmControls;
		bControlSize = entity->processing.bControlSize;
		
		// 統計支持的屬性個數
		for (i = 0; i < bControlSize; ++i)
			ncontrols += hweight8(bmControls[i]);
			
		// 一次性分配n個屬性控制結構,到 uvc_control
        entity->controls = kcalloc(ncontrols, sizeof(*ctrl),GFP_KERNEL);
        ctrl = entity->controls;
        
        // 關聯這個 uvc_control(這個剛構造的屬性描述) 和 uvc_control_info類型的 uvc_ctrls (統一的描述)
        uvc_ctrl_init_ctrl
        	// 查詢guid 和 index 一致 則關聯
        	if (uvc_entity_match_guid(ctrl->entity, info->entity) &&ctrl->index == info->index) {
				uvc_ctrl_add_info(dev, ctrl, info); 
           //這里有個關鍵函數 uvc_control_mapping.get/set 為這個  control 結構具體的數據轉換函數   
            __uvc_ctrl_add_mapping(dev, ctrl, mapping);  
                if (map->get == NULL)
                    map->get = uvc_get_le_value;
                if (map->set == NULL)
                    map->set = uvc_set_le_value;

            	

屬性支持查詢

ioctl入手

uvc_v4l2_do_ioctl
	case VIDIOC_QUERYCTRL:
		uvc_query_v4l2_ctrl(chain, arg)
            uvc_find_control(chain, v4l2_ctrl->id, &mapping)
            	//遍歷 entity
            	list_for_each_entry(entity, &chain->entities, chain)
            		//尋找到一個詳細的描述
            		// 這里有指示這個 unit 的詳細信息,這個mapping就是
            		// static struct uvc_control_mapping uvc_ctrl_mappings[]
            		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next)

也就是說存在這么個流程

  1. 設備插入后初始化屬性管理結構,這個管理結構會掛載uvc_control_info uvc_ctrls,指示了可讀可寫等信息
  2. 查詢屬性的時候,可以根據這個找到更詳細的uvc_control_mapping uvc_ctrl_mappings[],指示了具體的數據位寬和默認值等

具體屬性值獲取

VIDIOC_G_CTRL
	uvc_ctrl_get(chain, &xctrl);
		// 屬性查詢,了解具體的操作格式
		uvc_find_control
		// 發起usb傳輸 獲取最大最小值,存到 ctrl->uvc_data  這個是在初始化后分配的內存
        	uvc_ctrl_populate_cache (UVC_GET_DEF,UVC_GET_MIN,UVC_GET_MAX)
        		uvc_query_ctrl
        			usb_control_msg
		// 傳遞給usb
		uvc_query_ctrl
			__uvc_query_ctrl
				usb_control_msg
		// ...
		uvc_ctrl_rollback(chain);

具體屬性值設置

VIDIOC_S_CTRL
	uvc_ctrl_set
		// 屬性查詢
		uvc_find_control
			
			// 發起usb傳輸 獲取最大最小值,存到 ctrl->uvc_data  這個是在初始化后分配的內存
			if (!ctrl->cached) {	//這里如果在讀屬性的時候獲取過就不再發起usb傳輸獲取了
				ret = uvc_ctrl_populate_cache(chain, ctrl);
			
			//數據轉換
			min,max,step=get(uvc_ctrl_data..)
			// 傳輸usb
			uvc_query_ctrl
				__uvc_query_ctrl
					usb_control_msg
	uvc_ctrl_commit

代碼實現

這里代碼的實現難點還是在usb_control_msg的參數確定了,摘錄自這里

  • usb_control_msg()
    功能:發送一個簡單的控制消息到指定的端點,並等待消息完成或超時;
    參數:
      dev:指向控制消息所發送的目標USB設備(usb_device)的指針; <這里是在probe()里獲取的my_uvc_udev>
      pipe:控制消息所發送的目標USB設備的特定端點,調用usb_sndctrlpipe(把指定USB設備的指定端點設置為一個控制OUT端點)或usb_rcvctrlpipe(把指定USB設備的指定端點設置為一個控制IN端點)來創建的; <這里把my_uvc_udev設置為接收端點>
      request:控制消息的USB請求值; <這里分別是需要的GET_MIN、GET_MAX、GET_RES、GET_DEF>
      requesttype:控制消息的USB請求類型值; <這里為USB_TYPE_CLASS(1<<5)、usb_recip_interface(1<<0)、usb_dir_in(1<<7)>
        D7:數據的傳輸方向:0表示從主機到設備;1表示從設備到主機;
        D6~5:命令的類型:0表示標准命令;1表示類命令;2表示廠商提供的命令;3保留;
        D4~0:接收對象:0表示設備; 1表示接口;2表示端點;3表示其他;
      value:控制消息的USB消息值; <這里是PU亮度控制>
      index:控制消息的USB消息索引值;<這里是PU對應的ID和控制接口>
      data:指向要發送/接收的數據的指針; <這里是接收數據>
      size:data參數所指緩沖區的大小; <這里是兩字節,bControlSize=2>
      timeout:以msecs為單位,期望等待的超時時間,如果為0,該函數將一直等待消息結束以的時間;<這里是5s>
    返回值:
      成功返回接收/發送的字節數,否則返回負的錯誤值;

實例代碼如下

/* 查詢/獲得/設置屬性 
.vidioc_queryctrl     = myuvc_vidioc_queryctrl,
.vidioc_g_ctrl        = myuvc_vidioc_g_ctrl,
.vidioc_s_ctrl        = myuvc_vidioc_s_ctrl,
*/

// uvc_get_le_value  uvc_set_le_value
static void myuvc_set_le_value(__s32 value, __u8 *data);
static __s32 myuvc_get_le_value(const __u8 *data);


/* 參考: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);
	type |= USB_DIR_IN;

    /* 發起USB傳輸確定這些值 */
	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 */


	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)
        return -EINVAL;

	pipe = usb_rcvctrlpipe(myuvc_udev, 0);
	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);
    if (ret != 2)
        return -EIO;
	ctrl->value = myuvc_get_le_value(data);	/* Note signedness */
    
    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);

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM