UVC 驅動調用過程與驅動框架的簡單分析


內核:Linux-3.4.2
驅動:drivers\media\video\uvc\uvc_driver.c

UVC 驅動整體調用流程:

/* 打開設備描述符 */
1. open:
        uvc_v4l2_open

/* 查詢設備屬性 */
2. VIDIOC_QUERYCAP
		if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
			cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
					  | V4L2_CAP_STREAMING;
		else
			cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
					  | V4L2_CAP_STREAMING;

/* 枚舉設備支持的格式 */
3. VIDIOC_ENUM_FMT
        format = &video->streaming->format[fmt->index];

/* 得到設備當前所使用的 format 與 frame */
4. VIDIOC_G_FMT
        uvc_v4l2_get_format
        	struct uvc_format *format = video->streaming->cur_format;
        	struct uvc_frame *frame = video->streaming->cur_frame;

/* Check if the hardware supports the requested format. */
5. VIDIOC_TRY_FMT
        uvc_v4l2_try_format
        	 
/* 設置數據,此時並沒有真正的設置,而是在啟動視頻流時將數據發送給設備 */
6. VIDIOC_S_FMT
        uvc_v4l2_set_format
            uvc_v4l2_try_format
        	video->streaming->cur_format = format;
        	video->streaming->cur_frame = frame;

/* 分配視頻緩沖區 */
7. VIDIOC_REQBUFS
        uvc_alloc_buffers
           	for (; nbuffers > 0; --nbuffers) {
        		mem = vmalloc_32(nbuffers * bufsize);
        		if (mem != NULL)
        			break;
        	}

/* 查詢並獲取到分配的緩沖區信息 */
8. VIDIOC_QUERYBUF
        uvc_query_buffer
            __uvc_query_buffer

/* 映射緩沖區地址到用戶空間 */
9. mmap
        uvc_v4l2_mmap

/* 將 V4L2 信息塊放入隊列 */
10. VIDIOC_QBUF
        uvc_queue_buffer
            list_add_tail(&buf->queue, &queue->irqqueue);
        	list_add_tail(&buf->stream, &queue->mainqueue);

/* 設置設備並啟動視頻流 */
11. VIDIOC_STREAMON
        uvc_video_enable(video, 1)
            /* Commit the streaming parameters. */
            uvc_commit_video
                /* 設置 format, frame */
                uvc_set_video_ctrl  
                    
            /* 啟動:Initialize isochronous/bulk URBs and allocate transfer buffers. */
            uvc_init_video(video, GFP_KERNEL);
                    uvc_init_video_isoc / uvc_init_video_bulk
                        
                    usb_submit_urb    

/* 休眠等待數據 */
12. poll
        uvc_v4l2_poll            
            uvc_queue_poll
                poll_wait(file, &buf->wait, wait);

/* 取出信息塊 */
13. VIDIOC_DQBUF
        uvc_dequeue_buffer
        	list_del(&buf->stream);

/* 關閉視頻流 */
14. VIDIOC_STREAMOFF            
        uvc_video_enable(video, 0);
    		usb_kill_urb(urb);
    		usb_free_urb(urb);

驅動分析,首先找到 UVC 驅動的入口點:

struct uvc_driver uvc_driver = {
	.driver = {
		.name		= "uvcvideo",
		.probe		= uvc_probe,
		.disconnect	= uvc_disconnect,
		.suspend	= uvc_suspend,
		.resume		= uvc_resume,
		.reset_resume	= uvc_reset_resume,
		.id_table	= uvc_ids,
		.supports_autosuspend = 1,
	},
};

usb_register(&uvc_driver.driver);

注冊了 usb 驅動,如果系統中出現了與其 id_table 匹配的設備,則驅動會與它建立關系並調用 probe 函數:

我們選擇比較重要的函數進行分析,在 probe 中的函數調用:

uvc_register_chains -> uvc_register_terms -> uvc_register_video(對類型為 UVC_TT_STREAMING 的video調用本函數)

uvc_register_video 函數如下:

static int uvc_register_video(struct uvc_device *dev,
		struct uvc_streaming *stream)
{
	struct video_device *vdev;
	int ret;

    /* 對 video 做一些初始化 */
	ret = uvc_video_init(stream);
	if (ret < 0) {
		uvc_printk(KERN_ERR, "Failed to initialize the device "
			"(%d).\n", ret);
		return ret;
	}

	uvc_debugfs_init_stream(stream);

	/* 分配一個 video device */
	vdev = video_device_alloc();
	if (vdev == NULL) {
		uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
			   ret);
		return -ENOMEM;
	}

    /* 配置 video device */
	vdev->v4l2_dev = &dev->vdev;
	vdev->fops = &uvc_fops;
	vdev->release = uvc_release;
	strlcpy(vdev->name, dev->name, sizeof vdev->name);

	stream->vdev = vdev;
	video_set_drvdata(vdev, stream);

    /* 注冊 video device */
	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
	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;
}

在這里面有個重要的結構,即:

const struct v4l2_file_operations uvc_fops = {
	.owner		= THIS_MODULE,
	.open		= uvc_v4l2_open,
	.release	= uvc_v4l2_release,
	.unlocked_ioctl	= uvc_v4l2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32	= uvc_v4l2_compat_ioctl32,
#endif
	.read		= uvc_v4l2_read,
	.mmap		= uvc_v4l2_mmap,
	.poll		= uvc_v4l2_poll,
#ifndef CONFIG_MMU
	.get_unmapped_area = uvc_v4l2_get_unmapped_area,
#endif
};

uvc_v4l2_ioctl 就是實現 V4L2 操作的函數集。

這個 unlocked_ioctl 屬性的賦值需要注意一下,當它為 uvc_v4l2_ioctl(當前內核所使用)時,調用的是 uvc_v4l2.c 中現有的函數; 但如果將它賦值為 video_ioctl2 時,內核將調用我們在驅動中 vdev 的 ioctl_ops 屬性所賦給的函數集進行操作。

__video_do_ioctl 中:
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; 之后就調用 ops 中的一系列函數來操作 video

所以這一系列的函數可以由我們自己來編寫。


免責聲明!

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



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