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);
	}
}
原文地址:https://www.cnblogs.com/keanuyaoo/p/3315524.html