1. 程式人生 > >uvc攝像頭程式碼解析6

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).\n",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.\n");
		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.\n", 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.\n", 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.\n");
			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.\n");
			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\n", 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\n", 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.\n", 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.\n",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.\n", 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\n", 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("\n");
		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.\n", 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).\n", 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).\n",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).\n",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.\n", 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.\n", 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).\n",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.\n");
		return;
	}
	uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",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.\n");
		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\n",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.\n", data[1], data[2], data[3], len);
	}
}