其申请流程如下图所示:
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]; /* [email protected] */};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;};
/* * 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;}
/* * 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;}
/* * 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;}
当URB请求(usb_submit_urb)完成之后,它将调用其回调函数(uvc_video_complete),下面分析此回调函数到底做了些什么。即如何把transfer_buffer或transfer_dma中数据转换为应用程序需要的v4l2_buffer中的数据。
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); }}
现在关键是stream->decode到底做了些什么?它也是一个回调函数,首先要搞明白它是一个什么函数,其注册过程如下图所示:
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); } }}
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow