Hi Bhupesh, Thanks for the patch. It looks quite good, please see below for various small comments. On Friday 01 June 2012 15:08:57 Bhupesh Sharma wrote: > This patch reworks the videobuffer management logic present in the UVC > webcam gadget and ports it to use the "more apt" videobuf2 framework for > video buffer management. > > To support routing video data captured from a real V4L2 video capture > device with a "zero copy" operation on videobuffers (as they pass from the > V4L2 domain to UVC domain via a user-space application), we need to support > USER_PTR IO method at the UVC gadget side. > > So the V4L2 capture device driver can still continue to use MMAO IO method s/MMAO/MMAP/ > and now the user-space application can just pass a pointer to the video > buffers being DeQueued from the V4L2 device side while Queueing them at the I don't think dequeued and queueing need capitals :-) > UVC gadget end. This ensures that we have a "zero-copy" design as the > videobuffers pass from the V4L2 capture device to the UVC gadget. > > Note that there will still be a need to apply UVC specific payload headers > on top of each UVC payload data, which will still require a copy operation > to be performed in the 'encode' routines of the UVC gadget. > > Signed-off-by: Bhupesh Sharma <bhupesh.sharma@xxxxxx> > --- > drivers/usb/gadget/Kconfig | 1 + > drivers/usb/gadget/uvc_queue.c | 524 ++++++++++--------------------------- > drivers/usb/gadget/uvc_queue.h | 25 +-- > drivers/usb/gadget/uvc_v4l2.c | 35 ++-- > drivers/usb/gadget/uvc_video.c | 17 +- > 5 files changed, 184 insertions(+), 418 deletions(-) [snip] > diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c > index 0cdf89d..907ece8 100644 > --- a/drivers/usb/gadget/uvc_queue.c > +++ b/drivers/usb/gadget/uvc_queue.c [snip] This part is a bit difficult to review, as git tried too hard to create a universal diff where your patch really replaces the code. I'll remove the - lines to make the comments as readable as possible. > +/* > --------------------------------------------------------------------------- > -- > + * videobuf2 queue operations > */ > + > +static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format > *fmt, > + unsigned int *nbuffers, unsigned int *nplanes, > + unsigned int sizes[], void *alloc_ctxs[]) > { > + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); > + struct uvc_video *video = > + container_of(queue, struct uvc_video, queue); No need for a line split. > > + if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) > + *nbuffers = UVC_MAX_VIDEO_BUFFERS; > > + *nplanes = 1; > + > + sizes[0] = video->imagesize; > > return 0; > } > > +static int uvc_buffer_prepare(struct vb2_buffer *vb) > { > + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); > + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); > > + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && > + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { Please align vb2 with the vb-> on the previous line. Have you by any chance found some inspiration in drivers/media/video/uvc/uvc_queue.c ? :-) It would probably make sense to move this check to vb2 core, but that's outside of the scope of this patch. > + uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n"); > + return -EINVAL; > + } > > + if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) > + return -ENODEV; > > + buf->state = UVC_BUF_STATE_QUEUED; > + buf->mem = vb2_plane_vaddr(vb, 0); > + buf->length = vb2_plane_size(vb, 0); > + if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) > + buf->bytesused = 0; > + else > + buf->bytesused = vb2_get_plane_payload(vb, 0); The driver doesn't support the capture type at the moment so this might be a bit overkill, but I think it's a good idea to support capture in the queue imeplementation. I plan to try and merge the uvcvideo and uvcgadget queue implementations at some point. > > + return 0; > +} > > +static void uvc_buffer_queue(struct vb2_buffer *vb) > +{ > + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); > + struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf); > + unsigned long flags; > > + spin_lock_irqsave(&queue->irqlock, flags); > > + if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { > + list_add_tail(&buf->queue, &queue->irqqueue); > + } else { > + /* If the device is disconnected return the buffer to userspace > + * directly. The next QBUF call will fail with -ENODEV. > + */ > + buf->state = UVC_BUF_STATE_ERROR; > + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); > } > > + spin_unlock_irqrestore(&queue->irqlock, flags); > } > > +static struct vb2_ops uvc_queue_qops = { > + .queue_setup = uvc_queue_setup, > + .buf_prepare = uvc_buffer_prepare, > + .buf_queue = uvc_buffer_queue, > +}; > + > +static > +void uvc_queue_init(struct uvc_video_queue *queue, > + enum v4l2_buf_type type) This can fit on two lines. Please align enum with struct. > { > + mutex_init(&queue->mutex); > + spin_lock_init(&queue->irqlock); > + INIT_LIST_HEAD(&queue->irqqueue); Please add a blank line here. > + queue->queue.type = type; > + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; > + queue->queue.drv_priv = queue; > + queue->queue.buf_struct_size = sizeof(struct uvc_buffer); > + queue->queue.ops = &uvc_queue_qops; > + queue->queue.mem_ops = &vb2_vmalloc_memops; > + vb2_queue_init(&queue->queue); > } [snip] > /* > + * Allocate the video buffers. > */ > +static int uvc_alloc_buffers(struct uvc_video_queue *queue, > + struct v4l2_requestbuffers *rb) Please align struct with struct (same for the rest of the file). > { > + int ret; > > + /* > + * we can support a max of UVC_MAX_VIDEO_BUFFERS video buffers > + */ > + if (rb->count > UVC_MAX_VIDEO_BUFFERS) > + rb->count = UVC_MAX_VIDEO_BUFFERS; > The check is already present in uvc_queue_setup(), you can remove it here. > mutex_lock(&queue->mutex); > + ret = vb2_reqbufs(&queue->queue, rb); > + mutex_unlock(&queue->mutex); > > + return ret ? ret : rb->count; > +} [snip] > @@ -481,10 +250,10 @@ static void uvc_queue_cancel(struct uvc_video_queue > *queue, int disconnect) spin_lock_irqsave(&queue->irqlock, flags); > while (!list_empty(&queue->irqqueue)) { > buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, > - queue); > + queue); No need to change indentation here. > list_del(&buf->queue); > buf->state = UVC_BUF_STATE_ERROR; > - wake_up(&buf->wait); > + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); > } > /* This must be protected by the irqlock spinlock to avoid race > * conditions between uvc_queue_buffer and the disconnection event that > @@ -516,26 +285,28 @@ static void uvc_queue_cancel(struct uvc_video_queue > *queue, int disconnect) */ > static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) > { > + unsigned long flags; > int ret = 0; > > mutex_lock(&queue->mutex); > if (enable) { > + ret = vb2_streamon(&queue->queue, queue->queue.type); > + if (ret < 0) > goto done; > + > queue->buf_used = 0; > + queue->flags |= UVC_QUEUE_STREAMING; I think UVC_QUEUE_STREAMING isn't used anymore. > } else { > + if (uvc_queue_streaming(queue)) { The uvcvideo driver doesn't have this check. It thus returns -EINVAL if VIDIOC_STREAMOFF is called on a stream that is already stopped. I'm not sure what the right behaviour is, so let's keep the check here until we figure it out. > + ret = vb2_streamoff(&queue->queue, queue->queue.type); > + if (ret < 0) > + goto done; > + > + spin_lock_irqsave(&queue->irqlock, flags); > + INIT_LIST_HEAD(&queue->irqqueue); > + queue->flags &= ~UVC_QUEUE_STREAMING; > + spin_unlock_irqrestore(&queue->irqlock, flags); > + } > } > > done: > @@ -543,30 +314,29 @@ done: > return ret; > } > > -/* called with queue->irqlock held.. */ > +/* called with &queue_irqlock held.. */ > static struct uvc_buffer * > uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer > *buf) { > struct uvc_buffer *nextbuf; > > if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && > - buf->buf.length != buf->buf.bytesused) { > + buf->length != buf->bytesused) { Please keep the indentation. > buf->state = UVC_BUF_STATE_QUEUED; > - buf->buf.bytesused = 0; > + vb2_set_plane_payload(&buf->buf, 0, 0); > return buf; > } > > list_del(&buf->queue); > if (!list_empty(&queue->irqqueue)) > nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, > - queue); > + queue); Same here. > else > nextbuf = NULL; > > - buf->buf.sequence = queue->sequence++; > - do_gettimeofday(&buf->buf.timestamp); videobuf2 doesn't fill the sequence number or timestamp fields, so you either need to keep this here or move it to the caller. > + vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); > + vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); > > - wake_up(&buf->wait); > return nextbuf; > } > > @@ -576,7 +346,7 @@ static struct uvc_buffer *uvc_queue_head(struct > uvc_video_queue *queue) > > if (!list_empty(&queue->irqqueue)) > buf = list_first_entry(&queue->irqqueue, struct uvc_buffer, > - queue); > + queue); Please keep the indentation. > else > queue->flags |= UVC_QUEUE_PAUSED; > [snip] > diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c > index f6e083b..9c2b45b 100644 > --- a/drivers/usb/gadget/uvc_v4l2.c > +++ b/drivers/usb/gadget/uvc_v4l2.c > @@ -144,20 +144,23 @@ uvc_v4l2_release(struct file *file) > struct uvc_device *uvc = video_get_drvdata(vdev); > struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); > struct uvc_video *video = handle->device; > + int ret; > > uvc_function_disconnect(uvc); > > - uvc_video_enable(video, 0); > - mutex_lock(&video->queue.mutex); > - if (uvc_free_buffers(&video->queue) < 0) > - printk(KERN_ERR "uvc_v4l2_release: Unable to free " > - "buffers.\n"); > - mutex_unlock(&video->queue.mutex); > + ret = uvc_video_enable(video, 0); > + if (ret < 0) { > + printk(KERN_ERR "uvc_v4l2_release: uvc video disable failed\n"); > + return ret; > + } This shouldn't prevent uvc_v4l2_release() from succeeding. In practive uvc_video_enable(0) will never fail, so you can remove the error check. > + > + uvc_free_buffers(&video->queue); > > file->private_data = NULL; > v4l2_fh_del(&handle->vfh); > v4l2_fh_exit(&handle->vfh); > kfree(handle); > + > return 0; > } [snip] > diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c > index b0e53a8..195bbb6 100644 > --- a/drivers/usb/gadget/uvc_video.c > +++ b/drivers/usb/gadget/uvc_video.c [snip] > @@ -161,6 +161,7 @@ static void > uvc_video_complete(struct usb_ep *ep, struct usb_request *req) > { > struct uvc_video *video = req->context; > + struct uvc_video_queue *queue = &video->queue; > struct uvc_buffer *buf; > unsigned long flags; > int ret; > @@ -169,13 +170,15 @@ uvc_video_complete(struct usb_ep *ep, struct > usb_request *req) case 0: > break; > > - case -ESHUTDOWN: > + case -ESHUTDOWN: /* disconnect from host. */ > printk(KERN_INFO "VS request cancelled.\n"); > + uvc_queue_cancel(queue, 1); > goto requeue; > > default: > printk(KERN_INFO "VS request completed with status %d.\n", > req->status); > + uvc_queue_cancel(queue, 0); I wonder why there was no uvc_queue_cancel() here already, it makes me a bit suspicious :-) Have you double-checked this ? > goto requeue; > } -- Regards, Laurent Pinchart -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html