• uvc摄像头代码解析6


    10.扫描视频设备链和注册视频设备

    10.1 uvc视频链

    struct uvc_video_chain {	//uvc视频链
    	struct uvc_device *dev;			//uvc设备
    	struct list_head list;			//uvc视频链链表头
    	struct list_head entities;		//uvc实体链表头
    	struct uvc_entity *processing;	//处理Unit实体
    	struct uvc_entity *selector;	//选择器Unit实体
    	struct mutex ctrl_mutex;		/* Protects ctrl.info */
    };

    10.2 uvc扫描设备

    static int uvc_scan_device(struct uvc_device *dev)
    {
    	struct uvc_video_chain *chain;	//uvc视频链
    	struct uvc_entity *term;	//uvc实体
    
    	list_for_each_entry(term, &dev->entities, list) {	//遍历全局实体链表
    		if (!UVC_ENTITY_IS_OTERM(term))	//获取实体链表中的输出Terminal实体
    			continue;
    		if (term->chain.next || term->chain.prev)	//已经添加到uvc视频链中了
    			continue;
    		chain = kzalloc(sizeof(*chain), GFP_KERNEL);	//分配uvc视频链内存(有多少个输入Terminal就有多少个uvc_video_chain)
    		if (chain == NULL)
    			return -ENOMEM;
    		INIT_LIST_HEAD(&chain->entities);	//初始化视频链entities(实体)链表
    		mutex_init(&chain->ctrl_mutex);
    		chain->dev = dev;	//捆绑uvc视频链和uvc设备
    		if (uvc_scan_chain(chain, term) < 0) {	//扫描uvc视频链(处理所有相关的输入pin)
    			kfree(chain);
    			continue;
    		}
    		uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).
    ",uvc_print_chain(chain));
    		list_add_tail(&chain->list, &dev->chains);	//添加到uvc设备的uvc视频链链表
    	}
    	if (list_empty(&dev->chains)) {
    		uvc_printk(KERN_INFO, "No valid video chain found.
    ");
    		return -1;
    	}
    	return 0;
    }

    10.3 uvc扫描视频链

    static int uvc_scan_chain(struct uvc_video_chain *chain,struct uvc_entity *term)
    {
    	struct uvc_entity *entity, *prev;
    	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
    	entity = term;	//获取实体
    	prev = NULL;	//前一个实体
    	while (entity != NULL) {
    		/* Entity must not be part of an existing chain */
    		if (entity->chain.next || entity->chain.prev) {	//已经添加到uvc视频链中了
    			uvc_trace(UVC_TRACE_DESCR, "Found reference to entity %d already in chain.
    ", entity->id);
    			return -EINVAL;
    		}
    		/* Process entity */
    		if (uvc_scan_chain_entity(chain, entity) < 0)	//扫描当前实体
    			return -EINVAL;
    		/* Forward scan */
    		if (uvc_scan_chain_forward(chain, entity, prev) < 0)	//向前扫描实体
    			return -EINVAL;
    		/* Backward scan */
    		prev = entity;		//当前实体作为下一次while循环的前一个实体
    		if (uvc_scan_chain_backward(chain, &entity) < 0)	//向后扫描实体
    			return -EINVAL;
    	}
    	return 0;
    }

    将uvc视频链的输入实体添加到uvc视频链的entities链表中
    将uvc视频链添加到uvc设备的chains链表中

    10.3.1 扫描当前实体

    static int uvc_scan_chain_entity(struct uvc_video_chain *chain,struct uvc_entity *entity)
    {
    	switch (UVC_ENTITY_TYPE(entity)) {
    	case UVC_VC_EXTENSION_UNIT:	//扩展Unit
    		if (uvc_trace_param & UVC_TRACE_PROBE)
    			printk(" <- XU %d", entity->id);
    		if (entity->bNrInPins != 1) {
    			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.
    ", entity->id);
    			return -1;
    		}
    		break;
    	case UVC_VC_PROCESSING_UNIT:	//处理Unit
    		if (uvc_trace_param & UVC_TRACE_PROBE)
    			printk(" <- PU %d", entity->id);
    		if (chain->processing != NULL) {
    			uvc_trace(UVC_TRACE_DESCR, "Found multiple Processing Units in chain.
    ");
    			return -1;
    		}
    		chain->processing = entity;	//如果是处理Unit则设置其为uvc视频链的processing对象
    		break;
    	case UVC_VC_SELECTOR_UNIT:	//选择器Unit
    		if (uvc_trace_param & UVC_TRACE_PROBE)
    			printk(" <- SU %d", entity->id);
    		/* Single-input selector units are ignored. */
    		if (entity->bNrInPins == 1)
    			break;
    		if (chain->selector != NULL) {
    			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector Units in chain.
    ");
    			return -1;
    		}
    		chain->selector = entity;	//如果是选择器Unit则设置其为uvc视频链的selector对象
    		break;
    	case UVC_ITT_VENDOR_SPECIFIC:	//厂商特殊
    	case UVC_ITT_CAMERA:	//输入Terminal camera
    	case UVC_ITT_MEDIA_TRANSPORT_INPUT:	//输入Terminal Media transport
    		if (uvc_trace_param & UVC_TRACE_PROBE)
    			printk(" <- IT %d
    ", entity->id);
    		break;
    	case UVC_TT_STREAMING:	//输入Terminal stream
    		if (UVC_ENTITY_IS_ITERM(entity)) {
    			if (uvc_trace_param & UVC_TRACE_PROBE)
    				printk(" <- IT %d
    ", entity->id);
    		} 
    		else {
    			if (uvc_trace_param & UVC_TRACE_PROBE)
    				printk(" OT %d", entity->id);
    		}
    		break;
    	default:
    		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type 0x%04x found in chain.
    ", UVC_ENTITY_TYPE(entity));
    		return -1;
    	}
    	list_add_tail(&entity->chain, &chain->entities);	//添加到uvc视频链的实体链表
    	return 0;
    }

    10.3.2 向前扫描实体

    static int uvc_scan_chain_forward(struct uvc_video_chain *chain,struct uvc_entity *entity, struct uvc_entity *prev)
    {
    	struct uvc_entity *forward;
    	int found;
    	/* Forward scan */
    	forward = NULL;
    	found = 0;
    	while (1) {	//获取实体前面的所以实体处理直到前面的实体forward=NULL为止跳出死循环
    		forward = uvc_entity_by_reference(chain->dev, entity->id,forward);	//获取前一个实体
    		if (forward == NULL)
    			break;
    		if (forward == prev)
    			continue;
    		switch (UVC_ENTITY_TYPE(forward)) {
    		case UVC_VC_EXTENSION_UNIT:	//扩展Unit
    			if (forward->bNrInPins != 1) {
    				uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.
    ",entity->id);
    				return -EINVAL;
    			}
    			list_add_tail(&forward->chain, &chain->entities);	//添加uvc实体到uvc视频链的entities中
    			if (uvc_trace_param & UVC_TRACE_PROBE) {
    				if (!found)
    					printk(" (->");
    				printk(" XU %d", forward->id);
    				found = 1;
    			}
    			break;
    		case UVC_OTT_VENDOR_SPECIFIC:	//厂商特殊
    		case UVC_OTT_DISPLAY:	//输出Termianl display
    		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:	//输出Terminal media transport
    		case UVC_TT_STREAMING: //输出Terminal stream
    			if (UVC_ENTITY_IS_ITERM(forward)) {
    				uvc_trace(UVC_TRACE_DESCR, "Unsupported input terminal %u.
    ", forward->id);
    				return -EINVAL;
    			}
    			list_add_tail(&forward->chain, &chain->entities);	//添加uvc实体到uvc视频链的entities中
    			if (uvc_trace_param & UVC_TRACE_PROBE) {
    				if (!found)
    					printk(" (->");
    				printk(" OT %d", forward->id);
    				found = 1;
    			}
    			break;
    		}
    	}
    	if (found)
    		printk(")");
    	return 0;
    }

    10.3.3 向后扫描实体

    static int uvc_scan_chain_backward(struct uvc_video_chain *chain,struct uvc_entity **_entity)
    {
    	struct uvc_entity *entity = *_entity;
    	struct uvc_entity *term;
    	int id = -EINVAL, i;
    	switch (UVC_ENTITY_TYPE(entity)) {
    	case UVC_VC_EXTENSION_UNIT:	//扩展Unit
    	case UVC_VC_PROCESSING_UNIT:	//处理Unit处理Unit的输入Terminal个数只能为1
    		id = entity->baSourceID[0];	//获取输入pin(Unit/Terminal)的ID
    		break;
    	case UVC_VC_SELECTOR_UNIT:	//选择器实体
    		/* Single-input selector units are ignored. */
    		if (entity->bNrInPins == 1) {	//若输入pin个数为1
    			id = entity->baSourceID[0];	//获取输入in(Unit/Terminal)的ID
    			break;
    		}
    		if (uvc_trace_param & UVC_TRACE_PROBE)
    			printk(" <- IT");
    		chain->selector = entity;	//uvc视频链的selector对象指向uvc实体
    		for (i = 0; i < entity->bNrInPins; ++i) {	//总共有多少个输入pin
    			id = entity->baSourceID[i];	//获取输入in(Unit/Terminal)的ID
    			term = uvc_entity_by_id(chain->dev, id);	//获取对应的输入pin实体
    			if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
    				uvc_trace(UVC_TRACE_DESCR, "Selector unit %d input %d isn't connected to an input terminal
    ", entity->id, i);
    				return -1;
    			}
    			if (uvc_trace_param & UVC_TRACE_PROBE)
    				printk(" %d", term->id);
    			list_add_tail(&term->chain, &chain->entities);	//添加uvc实体到uvc视频链的entities链表
    			uvc_scan_chain_forward(chain, term, entity);	//向前扫描实体
    		}
    		if (uvc_trace_param & UVC_TRACE_PROBE)
    			printk("
    ");
    		id = 0;
    		break;
    	case UVC_ITT_VENDOR_SPECIFIC:
    	case UVC_ITT_CAMERA:
    	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
    	case UVC_OTT_VENDOR_SPECIFIC:
    	case UVC_OTT_DISPLAY:
    	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
    	case UVC_TT_STREAMING:
    		id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
    		break;
    	}
    	if (id <= 0) {
    		*_entity = NULL;
    		return id;
    	}
    	entity = uvc_entity_by_id(chain->dev, id);
    	if (entity == NULL) {
    		uvc_trace(UVC_TRACE_DESCR, "Found reference to unknown entity %d.
    ", id);
    		return -EINVAL;
    	}
    	*_entity = entity;
    	return 0;
    }

    注意到trace打印的语句会发现有一条

    uvcvideo: Scanning UVC chain: OT 2 <- XU 5 <- XU 4 <- PU 3 <- IT 1

    可以看到这些Unit和Terminal是如何组建起来的

    这里补充一下:

    1.打开trace:echo 0xffff > /sys/module/uvcvideo/par 消息用dmesg查看,清除dmesg信息带上-c参数就行

    2.留意之前lsusb打印出来的描述符表,对应的bTerminalID就是trace打印信息中对应的Unit或Terminal的数字,而baSourceID则是它的前一级Unit或Terminal的ID号

    10.3.4 添加链表

    list_add_tail(&entity->chain, &chain->entities);



    11.注册uvc视频链

    11.1 uvc注册视频链

    static int uvc_register_chains(struct uvc_device *dev)
    {
    	struct uvc_video_chain *chain;
    	int ret;
    	list_for_each_entry(chain, &dev->chains, list) {	//遍历uvc设备的uvc视频链链表
    		ret = uvc_register_terms(dev, chain);	//注册uvc视频链
    		if (ret < 0)
    			return ret;
    	}
    	return 0;
    }

    11.2 uvc注册实体

    static int uvc_register_terms(struct uvc_device *dev,struct uvc_video_chain *chain)
    {
    	struct uvc_streaming *stream;
    	struct uvc_entity *term;
    	int ret;
    	list_for_each_entry(term, &chain->entities, chain) {	//遍历uvc视频链的uvc实体链表
    		if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)	//不是输入Terminal streaming类型
    			continue;
    		stream = uvc_stream_by_id(dev, term->id);	//获取uvc视频流
    		if (stream == NULL) {
    			uvc_printk(KERN_INFO, "No streaming interface found for terminal %u.", term->id);
    			continue;
    		}
    		stream->chain = chain;	//捆绑uvc视频流和uvc视频链
    		ret = uvc_register_video(dev, stream);	//注册uvc视频流
    		if (ret < 0)
    			return ret;
    	}
    	return 0;
    }

    11.3 uvc注册视频

    static int uvc_register_video(struct uvc_device *dev,struct uvc_streaming *stream)
    {
    	struct video_device *vdev;
    	int ret;
    	/* Initialize the streaming interface with default streaming parameters.*/
    	ret = uvc_video_init(stream);	//13.uvc视频初始化
    	if (ret < 0) {
    		uvc_printk(KERN_ERR, "Failed to initialize the device (%d).
    ", ret);
    		return ret;
    	}
    	/* Register the device with V4L. */
    	vdev = video_device_alloc();	//分配v4l2设备内存
    	if (vdev == NULL) {
    		uvc_printk(KERN_ERR, "Failed to allocate video device (%d).
    ",ret);
    		return -ENOMEM;
    	}
    	vdev->parent = &dev->intf->dev;	//v4l2设备的父设备为usb接口设备
    	vdev->fops = &uvc_fops;	//v4l2操作函数集
    	vdev->release = uvc_release;	//释放方法
    	strlcpy(vdev->name, dev->name, sizeof vdev->name);	//设置名字
    	stream->vdev = vdev;	//捆绑uvc视频流和v4l2设备
    	video_set_drvdata(vdev, stream);	//将uvc视频流作为v4l2设备的驱动数据
    	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);	//注册v4l2设备
    	if (ret < 0) {
    		uvc_printk(KERN_ERR, "Failed to register video device (%d).
    ",ret);
    		stream->vdev = NULL;
    		video_device_release(vdev);
    		return ret;
    	}
    	atomic_inc(&dev->nstreams);
    	return 0;
    }

    12.uvc设备状态初始化

    uvc状态的处理由中断端点来控制处理

    int uvc_status_init(struct uvc_device *dev)
    {
    	struct usb_host_endpoint *ep = dev->int_ep;	//获取usb_host_endpoint
    	unsigned int pipe;
    	int interval;
    	if (ep == NULL)
    		return 0;
    	uvc_input_init(dev);	//初始化uvc输入设备
    	dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);	//分配uvc设备状态内存
    	if (dev->status == NULL)
    		return -ENOMEM;
    	dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);	//分配urb
    	if (dev->int_urb == NULL) {
    		kfree(dev->status);
    		return -ENOMEM;
    	}
    	pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);	//中断输入端点
    	interval = ep->desc.bInterval;	//获取间隔
    	if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&(dev->quirks & UVC_QUIRK_STATUS_INTERVAL))	//高速设备
    		interval = fls(interval) - 1;
    	usb_fill_int_urb(dev->int_urb, dev->udev, pipe,dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,dev, interval);	//填充中断urb
    	return 0;
    }

    这里只填充了urb信息,urb的提交请参看14.2.2.1 uvc_status_start启动状态,在打开uvc的V4L2设备方法时调用

    12.1 初始化uvc输入事件

    static int uvc_input_init(struct uvc_device *dev)
    {
    	struct input_dev *input;
    	int ret;
    	input = input_allocate_device();	//分配input设备内存
    	if (input == NULL)
    		return -ENOMEM;
    	usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys));	//设备节点路径
    	strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
    	input->name = dev->name;	//输入设备名
    	input->phys = dev->input_phys;	//输入设备节点路径
    	usb_to_input_id(dev->udev, &input->id);
    	input->dev.parent = &dev->intf->dev;	//输入设备的父设备为usb接口设备
    	__set_bit(EV_KEY, input->evbit);	//设置输入事件类型
    	__set_bit(KEY_CAMERA, input->keybit);	//设置按钮
    	if ((ret = input_register_device(input)) < 0)	//注册input设备
    		goto error;
    	dev->input = input;	//uvc设备捆绑输入设备
    	return 0;
    error:
    	input_free_device(input);
    	return ret;
    }

    12.2 urb回调函数

    static void uvc_status_complete(struct urb *urb)
    {
    	struct uvc_device *dev = urb->context;
    	int len, ret;
    	switch (urb->status) {
    	case 0:
    		break;
    	case -ENOENT:		/* usb_kill_urb() called. */
    	case -ECONNRESET:	/* usb_unlink_urb() called. */
    	case -ESHUTDOWN:	/* The endpoint is being disabled. */
    	case -EPROTO:		/* Device is disconnected (reported by some host controller). */
    		return;
    	default:
    		uvc_printk(KERN_WARNING, "Non-zero status (%d) in status completion handler.
    ", urb->status);
    		return;
    	}
    	len = urb->actual_length;
    	if (len > 0) {
    		switch (dev->status[0] & 0x0f) {
    		case UVC_STATUS_TYPE_CONTROL:	//VC事件
    			uvc_event_control(dev, dev->status, len);	//Table 2-2 Status Packet Format (VideoControl Interface as the Originator)
    			break;
    		case UVC_STATUS_TYPE_STREAMING:	//VS事件
    			uvc_event_streaming(dev, dev->status, len);	//Table 2-3 Status Packet Format (VideoStreaming Interface as the Originator)
    			break;
    		default
    			uvc_trace(UVC_TRACE_STATUS, "Unknown status event type %u.
    ", dev->status[0]);
    			break;
    		}
    	}
    	/* Resubmit the URB. */
    	urb->interval = dev->int_ep->desc.bInterval;
    	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {	//提交urb
    		uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).
    ",ret);
    	}
    }

    12.2.1 VC状态变化事件处理

    static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
    {
    	char *attrs[3] = { "value", "info", "failure" };
    	if (len < 6 || data[2] != 0 || data[4] > 2) {//长度应该为6,且data[2](bEvent)为0表示(Control Change),data[4]大于2部分为保留值
    		uvc_trace(UVC_TRACE_STATUS, "Invalid control status event received.
    ");
    		return;
    	}
    	uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.
    ",data[1], data[3], attrs[data[4]], len);
    }

    12.2.2 VS状态变化事件处理

    static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
    {
    	if (len < 3) {	//长度等于4
    		uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event received.
    ");
    		return;
    	}
    	if (data[2] == 0) {	//data[2](bevent)--0x00(Button Press)摄像头上的按钮按下
    		if (len < 4)
    			return;
    		uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d
    ",data[1], data[3] ? "pressed" : "released", len);
    		uvc_input_report_key(dev, KEY_CAMERA, data[3]);	//上报按键事件
    	} 
    	else {
    		uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x ""len %d.
    ", data[1], data[2], data[3], len);
    	}
    }
  • 相关阅读:
    Java 8 的内存结构
    数据库锁和隔离级别
    MySQL中的where和having
    单例对象线程安全问题
    计算机速成课 第6集:寄存器 & 内存
    多叉树的深度优先和广度优先遍历
    javascript中异步任务的执行顺序
    错误:C2062:意外的类型"unsigned int"问题的排查
    键值类型QMap、QHash等区别
    qt中文不相等问题
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3315524.html
Copyright © 2020-2023  润新知