V4L2深入理解

1. VIDIOC_REQBUFS: 请求Kernel分配Video Buffer

其申请流程如下图所示:

1.1  Kernel态相关数据结构

struct uvc_fh {
	struct uvc_video_chain *chain;
	struct uvc_streaming *stream;  //Stream--->
	enum uvc_handle_state state;
};

struct uvc_streaming {
	struct list_head list;
	struct uvc_device *dev;
	struct video_device *vdev;
	struct uvc_video_chain *chain;
	atomic_t active;

	struct usb_interface *intf;
	int intfnum;
	__u16 maxpsize;

	struct uvc_streaming_header header;
	enum v4l2_buf_type type;

	unsigned int nformats;
	struct uvc_format *format;

	struct uvc_streaming_control ctrl;
	struct uvc_format *cur_format;
	struct uvc_frame *cur_frame;
	/* Protect access to ctrl, cur_format, cur_frame and hardware video
	 * probe control.
	 */
	struct mutex mutex;

	unsigned int frozen : 1;
	struct uvc_video_queue queue; // UVC Video Queue--->
	void (*decode) (struct urb *urb, struct uvc_streaming *video,
			struct uvc_buffer *buf);

	/* Context data used by the bulk completion handler. */
	struct {
		__u8 header[256];
		unsigned int header_size;
		int skip_payload;
		__u32 payload_size;
		__u32 max_payload_size;
	} bulk;

	struct urb *urb[UVC_URBS];
	char *urb_buffer[UVC_URBS];
	dma_addr_t urb_dma[UVC_URBS];
	unsigned int urb_size;

	__u32 sequence;
	__u8 last_fid;

    struct tasklet_struct *tasklet[UVC_URBS];     /* ddl@rock-chips.com */
};

struct uvc_video_queue {
	enum v4l2_buf_type type;

	void *mem;             // 已经分配的连续虚拟内存的首地址
	unsigned int flags;

	unsigned int count;    // 已分配的buffer个数
	unsigned int buf_size; // 每个buffer的大小
	unsigned int buf_used;
	struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];  // UVC buffer--->
	struct mutex mutex;	/* protects buffers and mainqueue */
	spinlock_t irqlock;	/* protects irqqueue */

    wait_queue_head_t wait; /* wait if mainqueue is empty */

	struct list_head mainqueue;
	struct list_head irqqueue;
    
};

struct uvc_buffer {
	unsigned long vma_use_count;
	struct list_head stream;

	/* Touched by interrupt handler. */
	struct v4l2_buffer buf;     // v4l2_buffer --->
	struct list_head queue;
	wait_queue_head_t wait;     // 初始化等待队列
	enum uvc_buffer_state state;
	unsigned int error;
};

struct v4l2_buffer {
	__u32			index;    //buffer索引
	enum v4l2_buf_type      type;     //如V4L2_BUF_TYPE_VIDEO_CAPTURE
	__u32			bytesused;
	__u32			flags;
	enum v4l2_field		field;    // V4L2_FIELD_NONE
	struct timeval		timestamp; 
	struct v4l2_timecode	timecode;
	__u32			sequence;

	/* memory location */
	enum v4l2_memory        memory;   // V4L2_MEMORY_MMAP
	union {
		__u32           offset;   //在已经分配的大块内存中的偏移量,
                                          //其首地址保存在uvc_video_queue->mem中
		unsigned long   userptr;
		struct v4l2_plane *planes;
	} m;
	__u32			length;   //申请的内存大小
	__u32			input;
	__u32			reserved;
};


1.2 uvc_alloc_buffers实现代码

/*
 * Allocate the video buffers.
 *
 * Pages are reserved to make sure they will not be swapped, as they will be
 * filled in the URB completion handler.
 *
 * Buffers will be individually mapped, so they must all be page aligned.
 */
int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
		unsigned int buflength)
{
	unsigned int bufsize = PAGE_ALIGN(buflength);
	unsigned int i;
	void *mem = NULL;
	int ret;

	if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
		nbuffers = UVC_MAX_VIDEO_BUFFERS;

	mutex_lock(&queue->mutex);

	if ((ret = __uvc_free_buffers(queue)) < 0)
		goto done;

	/* Bail out if no buffers should be allocated. */
	if (nbuffers == 0)
		goto done;

	/* Decrement the number of buffers until allocation succeeds. */
	for (; nbuffers > 0; --nbuffers) {
		mem = vmalloc_32(nbuffers * bufsize);
		if (mem != NULL)
			break;
	}

	if (mem == NULL) {
		ret = -ENOMEM;
		goto done;
	}

	for (i = 0; i < nbuffers; ++i) {
		memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
		queue->buffer[i].buf.index = i;
		queue->buffer[i].buf.m.offset = i * bufsize;
		queue->buffer[i].buf.length = buflength;
		queue->buffer[i].buf.type = queue->type;
		queue->buffer[i].buf.field = V4L2_FIELD_NONE;
		queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
		queue->buffer[i].buf.flags = 0;
		init_waitqueue_head(&queue->buffer[i].wait);
	}

	queue->mem = mem;
	queue->count = nbuffers;
	queue->buf_size = bufsize;
	ret = nbuffers;

done:
	mutex_unlock(&queue->mutex);
	return ret;
}


 2. VIDIOC_QUERYBUF: 把Kernel分配的内存映射到用户空间

 

 

 3. VIDIOC_QBUF: 把uvc_buffer放入队列中

 

 

/*
 * Queue a video buffer. Attempting to queue a buffer that has already been
 * queued will return -EINVAL.
 */
int uvc_queue_buffer(struct uvc_video_queue *queue,
	struct v4l2_buffer *v4l2_buf)
{
	struct uvc_buffer *buf;
	unsigned long flags;
	int ret = 0;

	uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);

	if (v4l2_buf->type != queue->type ||
	    v4l2_buf->memory != V4L2_MEMORY_MMAP) {
		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
			"and/or memory (%u).\n", v4l2_buf->type,
			v4l2_buf->memory);
		return -EINVAL;
	}

	mutex_lock(&queue->mutex);
	if (v4l2_buf->index >= queue->count) {
		uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
		ret = -EINVAL;
		goto done;
	}

	buf = &queue->buffer[v4l2_buf->index];
	if (buf->state != UVC_BUF_STATE_IDLE) {
		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
			"(%u).\n", buf->state);
		ret = -EINVAL;
		goto done;
	}

	if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
	    v4l2_buf->bytesused > buf->buf.length) {
		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
		ret = -EINVAL;
		goto done;
	}

	spin_lock_irqsave(&queue->irqlock, flags);
	if (queue->flags & UVC_QUEUE_DISCONNECTED) {
		spin_unlock_irqrestore(&queue->irqlock, flags);
		ret = -ENODEV;
		goto done;
	}
	buf->state = UVC_BUF_STATE_QUEUED;
	if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
		buf->buf.bytesused = 0;
	else
		buf->buf.bytesused = v4l2_buf->bytesused;

	list_add_tail(&buf->stream, &queue->mainqueue);
	list_add_tail(&buf->queue, &queue->irqqueue);
	spin_unlock_irqrestore(&queue->irqlock, flags);

done:
	mutex_unlock(&queue->mutex);
	return ret;
}


 4. VIDIOC_STREAMON: 做好准备工作并提交URB请求

 

/*
 * Initialize bulk URBs and allocate transfer buffers. The packet size is
 * given by the endpoint.
 */
static int uvc_init_video_bulk(struct uvc_streaming *stream,
	struct usb_host_endpoint *ep, gfp_t gfp_flags)
{
	struct urb *urb;
	unsigned int npackets, pipe, i;
	u16 psize;
	u32 size;
        
    // 获取每个包的大小
	psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
	
	// 一次可传输的最大负荷传输大小
	size = stream->ctrl.dwMaxPayloadTransferSize;
	stream->bulk.max_payload_size = size;
	
    /*
    分配置urb传输buffer,返回包个数(size/psize),它指每个urb
    包含多少个包; 为每个stream->urb_buffer分配DMA buffer,如下:
    for (i = 0; i < UVC_URBS; ++i) {
        stream->urb_size = psize * npackets; // urb_buffer的大小

        //分配DMA内存,并把地址保存在urb_buffer[i]和urb_dma[i]中,
        //同一块内存,不同的表示方法
        //1) stream->urb_buffer[i] = offset + page->vaddr;
        //2) stream->urb_dma[i] = offset + page->dma;

        stream->urb_buffer[i] = usb_alloc_coherent(
                     stream->dev->udev, stream->urb_size,
                     gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);

        if (!stream->urb_buffer[i]) {
            uvc_free_urb_buffers(stream);
            break;
        }
    }
    */
	npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
	if (npackets == 0)
		return -ENOMEM;

	size = npackets * psize;

	if (usb_endpoint_dir_in(&ep->desc))
		pipe = usb_rcvbulkpipe(stream->dev->udev,
				       ep->desc.bEndpointAddress);
	else
		pipe = usb_sndbulkpipe(stream->dev->udev,
				       ep->desc.bEndpointAddress);

	if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
		size = 0;

	for (i = 0; i < UVC_URBS; ++i) {

        // 创建一个 urb
		urb = usb_alloc_urb(0, gfp_flags);
		if (urb == NULL) {
			uvc_uninit_video(stream, 1);
			return -ENOMEM;
		}

        /* 填充urb参数
		struct urb {
			void *transfer_buffer;	// (in) associated data buffer
			dma_addr_t transfer_dma;// (in) dma addr for transfer_buffer
			usb_complete_t complete;// (in) completion routine
			struct usb_iso_packet_descriptor iso_frame_desc[0];
							        // (in) ISO ONLY
		}		
		*/
		usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
                          stream->urb_buffer[i],  //传输buffer
                          size,        //传输buffer的大小
                          uvc_video_complete, //URB请求完成之后的callback
                          stream);

		urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
		urb->transfer_dma = stream->urb_dma[i]; //给urb->transfer_dma赋值
		// 把此urb保存到stream->urb[i]中
		stream->urb[i] = urb;
	}

	return 0;
}


5. urb数据解析 (uvc_video_complete)

       当URB请求(usb_submit_urb)完成之后,它将调用其回调函数(uvc_video_complete),下面分析此回调函数到底做了些什么。即如何把transfer_buffer或transfer_dma中数据转换为应用程序需要的v4l2_buffer中的数据。

 5.1 uvc_video_complete

static void uvc_video_complete(struct urb *urb)
{    
	struct uvc_streaming *stream = urb->context;
	struct uvc_video_queue *queue = &stream->queue;
	struct uvc_buffer *buf = NULL;
	unsigned long flags;
	int ret;

	switch (urb->status) {
	case 0:
		break;

	default:
		uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
			"completion handler.\n", urb->status);

	case -ENOENT:		/* usb_kill_urb() called. */
		if (stream->frozen)
			return;

	case -ECONNRESET:	/* usb_unlink_urb() called. */
	case -ESHUTDOWN:	/* The endpoint is being disabled. */
		uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
		return;
	}
 
	spin_lock_irqsave(&queue->irqlock, flags);
	if (!list_empty(&queue->irqqueue))
		buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
				       queue);//从queue->irqqueue中取出一个空的uvc_buffer
	spin_unlock_irqrestore(&queue->irqlock, flags);
        //把urb中的数据转换为uvc_buffer中的数据,并设置对应的状态
	stream->decode(urb, stream, buf);

	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
		uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
			ret);
	}
}

5.2  stream->decode是什么?

        现在关键是stream->decode到底做了些什么?它也是一个回调函数,首先要搞明白它是一个什么函数,其注册过程如下图所示:

5.3 uvc_video_decode_bulk

 

 

5.4 uvc_video_decode_isoc

static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
	struct uvc_buffer *buf)
{
	u8 *mem;
	int ret, i;

	for (i = 0; i < urb->number_of_packets; ++i) {
		if (urb->iso_frame_desc[i].status < 0) {
			uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
				"lost (%d).\n", urb->iso_frame_desc[i].status);
  			/* Mark the buffer as faulty. */
			if (buf != NULL)
				buf->error = 1;
			continue;
		}

		/* Decode the payload header. */
		mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
		do {
			ret = uvc_video_decode_start(stream, buf, mem,
				urb->iso_frame_desc[i].actual_length);
			if (ret == -EAGAIN)
				buf = uvc_queue_next_buffer(&stream->queue,
							    buf);
		} while (ret == -EAGAIN);

		if (ret < 0)
			continue;

		/* Decode the payload data. */
		uvc_video_decode_data(stream, buf, mem + ret,
			urb->iso_frame_desc[i].actual_length - ret);

		/* Process the header again. */
		uvc_video_decode_end(stream, buf, mem,
			urb->iso_frame_desc[i].actual_length);

		if (buf->state == UVC_BUF_STATE_READY) {
			if (buf->buf.length != buf->buf.bytesused &&
			    !(stream->cur_format->flags &
			      UVC_FMT_FLAG_COMPRESSED))
				buf->error = 1;

			buf = uvc_queue_next_buffer(&stream->queue, buf);
		}
	}
}


 

6. VIDIOC_DQBUF: 获取视频数据

 

  7. CameraHAL工作流程

  

 

 

 

 

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值