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 函數:html
咱們選擇比較重要的函數進行分析,在 probe 中的函數調用:markdown
uvc_register_chains -> uvc_register_terms -> uvc_register_video(對類型爲 UVC_TT_STREAMING 的video調用本函數)
uvc_register_video 函數以下:app
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; }
在這裏面有個重要的結構,即:ide
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 屬性所賦給的函數集進行操做。post
__video_do_ioctl 中: const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; 以後就調用 ops 中的一系列函數來操做 video
因此這一系列的函數能夠由咱們本身來編寫。atom