第3篇 UVC驱动分析

UVC驱动编写流程:

写一个USB摄像头驱动程序
	1.构造一个 usb_driver    //usb_interface 由USB host 枚举生成
	2.设置
	   probe:
			2.1. 分配 video_device:video_device_alloc
			2.2. 设置
			   .fops
			   .ioctl_ops (里面需要设置11项)
			   如果要用内核提供的缓冲区操作函数,还需要构造一个 videobuf_queue_ops
			2.3. 注册: video_register_device      
	  id_table: 表示支持哪些USB设备      
	3.注册: usb_register
 
UVC: USB Video Class
	UVC驱动:drivers\media\video\uvc\
	uvc_driver.c分析:
		1. usb_register(&uvc_driver.driver);
		2. uvc_probe
				uvc_register_video
					vdev = video_device_alloc();		//分配 video_device
					vdev->fops = &uvc_fops;				//设置
					video_register_device				//注册

分析UVC驱动调用过程:

分析UVC驱动调用过程:
	1. open:
			uvc_v4l2_open
	2. VIDIOC_QUERYCAP   			// video->streaming->type 应该是在设备被枚举时分析描述符时设置的
			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 数组应是在设备被枚举时设置的
			format = &video->streaming->format[fmt->index];
	4. VIDIOC_G_FMT
			uvc_v4l2_get_format  	// USB摄像头支持多种格式 fromat, 每种格式下有多种 frame(比如分辨率)
					struct uvc_format *format = video->streaming->cur_format;
					struct uvc_frame *frame = video->streaming->cur_frame;
	5. VIDIOC_TRY_FMT
			uvc_v4l2_try_format
				/* Check if the hardware supports the requested format. */

				/* Find the closest image size. The distance between image sizes is
				 * the size in pixels of the non-overlapping regions between the
				 * requested size and the frame-specified size.
				 */
	6. VIDIOC_S_FMT  // 只是把参数保存起来,还没有发给USB摄像头
			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
					memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);  // 复制参数
	9. mmap
			uvc_v4l2_mmap
				
	10. VIDIOC_QBUF		//把buffer 放入队列
			uvc_queue_buffer
				list_add_tail(&buf->stream, &queue->mainqueue);
				list_add_tail(&buf->queue, &queue->irqqueue);

	11. VIDIOC_STREAMON
			uvc_video_enable(video, 1)  			// 把所设置的参数发给硬件,然后启动摄像头
				/* Commit the streaming parameters. */
				uvc_commit_video
					uvc_set_video_ctrl  /* 设置格式 fromat, frame */
							ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0,
								video->streaming->intfnum  /* 哪一个接口: VS */,
								probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
								uvc_timeout_param);
						
				/* 启动:Initialize isochronous/bulk URBs and allocate transfer buffers. */
				uvc_init_video(video, GFP_KERNEL);
						uvc_init_video_isoc / uvc_init_video_bulk
							urb->complete = uvc_video_complete; 		(收到数据后此函数被调用,它又调用 video->decode(urb, video, buf); ==> uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait); 唤醒 poll_wait)
							
						usb_submit_urb                  
						
	12. poll
			uvc_v4l2_poll            
				uvc_queue_poll
					poll_wait(file, &buf->wait, wait);  // 休眠等待有数据

	13. VIDIOC_DQBUF									//从队列中获取buffer, 获取数据
			uvc_dequeue_buffer
				list_del(&buf->stream);

	14. VIDIOC_STREAMOFF            
			uvc_video_enable(video, 0);
				usb_kill_urb(urb);
				usb_free_urb(urb);
			
分析设置亮度过程:
	ioctl: VIDIOC_S_CTRL
				uvc_ctrl_set
				uvc_ctrl_commit
					__uvc_ctrl_commit(video, 0);
						uvc_ctrl_commit_entity(video->dev, entity, rollback);
								ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个 unit/terminal */,
									dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
									uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
									ctrl->info->size);

UVC描述符:(由USB host枚举生成)

1. UVC设备有2个 interface: VideoControl Interface, VideoStreaming Interface
2. VideoControl Interface用于控制,比如设置亮度。它内部有多个 Unit/Terminal(在程序里 Unit/Terminal 都称为 entity)
   可以通过类似的函数来访问:
							ret = uvc_query_ctrl(dev  /* 哪一个USB设备 */, SET_CUR, ctrl->entity->id  /* 哪一个 unit/terminal */,
								dev->intfnum /* 哪一个接口: VC interface */, ctrl->info->selector,
								uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
								ctrl->info->size);
3. VideoStreaming Interface 用于获得视频数据,也可以用来选择 format/frame(VS可能有多种 format, 一个 format 支持多种 frame , frame 用来表示分辨率等信息)
   可以通过类似的函数来访问:
						ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0,
							video->streaming->intfnum  /* 哪一个接口: VS */,
							probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
							uvc_timeout_param);
4. 我们在设置 FORMAT 时只是简单的使用 video->streaming->format[fmt->index]等数据,
   这些数据哪来的?
   应是设备被枚举时设置的,也就是分析它的描述符时设置的。

5. UVC驱动的重点在于:
   描述符的分析
   属性的控制: 通过 VideoControl Interface 来设置
   格式的选择:通过 VideoStreaming Interface 来设置
   数据的获得:通过 VideoStreaming Interface 的URB来获得

参考:韦东山Linux驱动教程

posted @   charlie12345  阅读(250)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示