From 1f5965c4dfd7665f2914a1f1095dcc6020656b04 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 May 2018 10:44:00 -0400 Subject: [PATCH] media: gspca: convert to vb2 The gspca core has its own buffere implementation. Use the core VB 2 instead. Signed-off-by: Hans Verkuil Reviewed-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/Kconfig | 1 + drivers/media/usb/gspca/gspca.c | 932 ++++----------------- drivers/media/usb/gspca/gspca.h | 38 +- drivers/media/usb/gspca/m5602/m5602_core.c | 4 +- drivers/media/usb/gspca/vc032x.c | 2 +- 5 files changed, 195 insertions(+), 782 deletions(-) diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index bc9a439745aa..d3b6665c342d 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -2,6 +2,7 @@ menuconfig USB_GSPCA tristate "GSPCA based webcams" depends on VIDEO_V4L2 depends on INPUT || INPUT=n + select VIDEOBUF2_VMALLOC default m ---help--- Say Y here if you want to enable selecting webcams based diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index d29773b8f696..ff229d3aae0f 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -82,32 +82,6 @@ static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt, #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 -#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) - -/* - * VMA operations. - */ -static void gspca_vm_open(struct vm_area_struct *vma) -{ - struct gspca_frame *frame = vma->vm_private_data; - - frame->vma_use_count++; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; -} - -static void gspca_vm_close(struct vm_area_struct *vma) -{ - struct gspca_frame *frame = vma->vm_private_data; - - if (--frame->vma_use_count <= 0) - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} - -static const struct vm_operations_struct gspca_vm_ops = { - .open = gspca_vm_open, - .close = gspca_vm_close, -}; - /* * Input and interrupt endpoint handling functions */ @@ -356,7 +330,7 @@ static void isoc_irq(struct urb *urb) struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; gspca_dbg(gspca_dev, D_PACK, "isoc irq\n"); - if (!gspca_dev->streaming) + if (!vb2_start_streaming_called(&gspca_dev->queue)) return; fill_frame(gspca_dev, urb); } @@ -370,7 +344,7 @@ static void bulk_irq(struct urb *urb) int st; gspca_dbg(gspca_dev, D_PACK, "bulk irq\n"); - if (!gspca_dev->streaming) + if (!vb2_start_streaming_called(&gspca_dev->queue)) return; switch (urb->status) { case 0: @@ -417,25 +391,24 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, const u8 *data, int len) { - struct gspca_frame *frame; - int i, j; + struct gspca_buffer *buf; + unsigned long flags; gspca_dbg(gspca_dev, D_PACK, "add t:%d l:%d\n", packet_type, len); - if (packet_type == FIRST_PACKET) { - i = atomic_read(&gspca_dev->fr_i); + spin_lock_irqsave(&gspca_dev->qlock, flags); + buf = list_first_entry_or_null(&gspca_dev->buf_list, + typeof(*buf), list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); - /* if there are no queued buffer, discard the whole frame */ - if (i == atomic_read(&gspca_dev->fr_q)) { + if (packet_type == FIRST_PACKET) { + /* if there is no queued buffer, discard the whole frame */ + if (!buf) { gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence++; return; } - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - v4l2_get_timestamp(&frame->v4l2_buf.timestamp); - frame->v4l2_buf.sequence = gspca_dev->sequence++; - gspca_dev->image = frame->data; + gspca_dev->image = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); gspca_dev->image_len = 0; } else { switch (gspca_dev->last_packet_type) { @@ -453,10 +426,10 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, /* append the packet to the frame buffer */ if (len > 0) { - if (gspca_dev->image_len + len > gspca_dev->frsz) { + if (gspca_dev->image_len + len > gspca_dev->pixfmt.sizeimage) { gspca_err(gspca_dev, "frame overflow %d > %d\n", gspca_dev->image_len + len, - gspca_dev->frsz); + gspca_dev->pixfmt.sizeimage); packet_type = DISCARD_PACKET; } else { /* !! image is NULL only when last pkt is LAST or DISCARD @@ -476,80 +449,23 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, * next first packet, wake up the application and advance * in the queue */ if (packet_type == LAST_PACKET) { - i = atomic_read(&gspca_dev->fr_i); - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - frame->v4l2_buf.bytesused = gspca_dev->image_len; - frame->v4l2_buf.flags = (frame->v4l2_buf.flags - | V4L2_BUF_FLAG_DONE) - & ~V4L2_BUF_FLAG_QUEUED; - i = (i + 1) % GSPCA_MAX_FRAMES; - atomic_set(&gspca_dev->fr_i, i); - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_del(&buf->list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, + gspca_dev->image_len); + buf->vb.sequence = gspca_dev->sequence++; + buf->vb.field = V4L2_FIELD_NONE; gspca_dbg(gspca_dev, D_FRAM, "frame complete len:%d\n", - frame->v4l2_buf.bytesused); + gspca_dev->image_len); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); gspca_dev->image = NULL; gspca_dev->image_len = 0; } } EXPORT_SYMBOL(gspca_frame_add); -static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory, unsigned int count) -{ - struct gspca_frame *frame; - unsigned int frsz; - int i; - - frsz = gspca_dev->pixfmt.sizeimage; - gspca_dbg(gspca_dev, D_STREAM, "frame alloc frsz: %d\n", frsz); - frsz = PAGE_ALIGN(frsz); - if (count >= GSPCA_MAX_FRAMES) - count = GSPCA_MAX_FRAMES - 1; - gspca_dev->frbuf = vmalloc_32(frsz * count); - if (!gspca_dev->frbuf) { - pr_err("frame alloc failed\n"); - return -ENOMEM; - } - gspca_dev->capt_file = file; - gspca_dev->memory = memory; - gspca_dev->frsz = frsz; - gspca_dev->nframes = count; - for (i = 0; i < count; i++) { - frame = &gspca_dev->frame[i]; - frame->v4l2_buf.index = i; - frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frame->v4l2_buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - frame->v4l2_buf.field = V4L2_FIELD_NONE; - frame->v4l2_buf.length = frsz; - frame->v4l2_buf.memory = memory; - frame->v4l2_buf.sequence = 0; - frame->data = gspca_dev->frbuf + i * frsz; - frame->v4l2_buf.m.offset = i * frsz; - } - atomic_set(&gspca_dev->fr_q, 0); - atomic_set(&gspca_dev->fr_i, 0); - gspca_dev->fr_o = 0; - return 0; -} - -static void frame_free(struct gspca_dev *gspca_dev) -{ - int i; - - gspca_dbg(gspca_dev, D_STREAM, "frame free\n"); - if (gspca_dev->frbuf != NULL) { - vfree(gspca_dev->frbuf); - gspca_dev->frbuf = NULL; - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].data = NULL; - } - gspca_dev->nframes = 0; - gspca_dev->frsz = 0; - gspca_dev->capt_file = NULL; - gspca_dev->memory = GSPCA_MEMORY_NO; -} - static void destroy_urbs(struct gspca_dev *gspca_dev) { struct urb *urb; @@ -583,22 +499,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) return ret; } -/* Note: both the queue and the usb locks should be held when calling this */ -static void gspca_stream_off(struct gspca_dev *gspca_dev) -{ - gspca_dev->streaming = 0; - gspca_dev->usb_err = 0; - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n"); -} - /* * look for an input transfer endpoint in an alternate setting. * @@ -829,6 +729,23 @@ static int create_urbs(struct gspca_dev *gspca_dev, return 0; } +/* Note: both the queue and the usb locks should be held when calling this */ +static void gspca_stream_off(struct gspca_dev *gspca_dev) +{ + gspca_dev->streaming = false; + gspca_dev->usb_err = 0; + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + if (gspca_dev->present) + gspca_input_create_urb(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n"); +} + /* * start the USB transfer */ @@ -844,7 +761,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->image = NULL; gspca_dev->image_len = 0; gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; gspca_dev->usb_err = 0; @@ -924,8 +840,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) destroy_urbs(gspca_dev); goto out; } - gspca_dev->streaming = 1; v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + gspca_dev->streaming = true; /* some bulk transfers are started by the subdriver */ if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) @@ -1165,11 +1081,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - ret = try_fmt_vid_cap(gspca_dev, fmt); - if (ret < 0) - return ret; + if (try_fmt_vid_cap(gspca_dev, fmt) < 0) + return -EINVAL; return 0; } @@ -1177,36 +1091,22 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; + int mode; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; + if (vb2_is_busy(&gspca_dev->queue)) + return -EBUSY; - ret = try_fmt_vid_cap(gspca_dev, fmt); - if (ret < 0) - goto out; + mode = try_fmt_vid_cap(gspca_dev, fmt); + if (mode < 0) + return -EINVAL; - if (gspca_dev->nframes != 0 - && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { - ret = -EINVAL; - goto out; - } - - if (gspca_dev->streaming) { - ret = -EBUSY; - goto out; - } - gspca_dev->curr_mode = ret; + gspca_dev->curr_mode = mode; if (gspca_dev->sd_desc->try_fmt) /* subdriver try_fmt can modify format parameters */ gspca_dev->pixfmt = fmt->fmt.pix; else - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret]; - - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; + gspca_dev->pixfmt = gspca_dev->cam.cam_mode[mode]; + return 0; } static int vidioc_enum_framesizes(struct file *file, void *priv, @@ -1281,53 +1181,6 @@ static void gspca_release(struct v4l2_device *v4l2_device) kfree(gspca_dev); } -static int dev_open(struct file *file) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - gspca_dbg(gspca_dev, D_STREAM, "[%s] open\n", current->comm); - - /* protect the subdriver against rmmod */ - if (!try_module_get(gspca_dev->module)) - return -ENODEV; - - ret = v4l2_fh_open(file); - if (ret) - module_put(gspca_dev->module); - return ret; -} - -static int dev_close(struct file *file) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dbg(gspca_dev, D_STREAM, "[%s] close\n", current->comm); - - /* Needed for gspca_stream_off, always lock before queue_lock! */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { - mutex_unlock(&gspca_dev->usb_lock); - return -ERESTARTSYS; - } - - /* if the file did the capture, free the streaming resources */ - if (gspca_dev->capt_file == file) { - if (gspca_dev->streaming) - gspca_stream_off(gspca_dev); - frame_free(gspca_dev); - } - module_put(gspca_dev->module); - mutex_unlock(&gspca_dev->queue_lock); - mutex_unlock(&gspca_dev->usb_lock); - - gspca_dbg(gspca_dev, D_STREAM, "close done\n"); - - return v4l2_fh_release(file); -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1377,167 +1230,9 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { if (i > 0) return -EINVAL; - return (0); -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, ret = 0, streaming; - - i = rb->memory; /* (avoid compilation warning) */ - switch (i) { - case GSPCA_MEMORY_READ: /* (internal call) */ - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_USERPTR: - break; - default: - return -EINVAL; - } - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - if (gspca_dev->memory != GSPCA_MEMORY_NO - && gspca_dev->memory != GSPCA_MEMORY_READ - && gspca_dev->memory != rb->memory) { - ret = -EBUSY; - goto out; - } - - /* only one file may do the capture */ - if (gspca_dev->capt_file != NULL - && gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - /* if allocated, the buffers must not be mapped */ - for (i = 0; i < gspca_dev->nframes; i++) { - if (gspca_dev->frame[i].vma_use_count) { - ret = -EBUSY; - goto out; - } - } - - /* stop streaming */ - streaming = gspca_dev->streaming; - if (streaming) { - gspca_stream_off(gspca_dev); - - /* Don't restart the stream when switching from read - * to mmap mode */ - if (gspca_dev->memory == GSPCA_MEMORY_READ) - streaming = 0; - } - - /* free the previous allocated buffers, if any */ - if (gspca_dev->nframes != 0) - frame_free(gspca_dev); - if (rb->count == 0) /* unrequest */ - goto out; - ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); - if (ret == 0) { - rb->count = gspca_dev->nframes; - if (streaming) - ret = gspca_init_transfer(gspca_dev); - } -out: - mutex_unlock(&gspca_dev->queue_lock); - gspca_dbg(gspca_dev, D_STREAM, "reqbufs st:%d c:%d\n", ret, rb->count); - return ret; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - - if (v4l2_buf->index >= gspca_dev->nframes) - return -EINVAL; - - frame = &gspca_dev->frame[v4l2_buf->index]; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - /* check the capture file */ - if (gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - if (gspca_dev->nframes == 0 - || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { - ret = -EINVAL; - goto out; - } - if (!gspca_dev->streaming) { - ret = gspca_init_transfer(gspca_dev); - if (ret < 0) - goto out; - } - PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK", - gspca_dev->pixfmt.pixelformat, - gspca_dev->pixfmt.width, gspca_dev->pixfmt.height); - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, ret; - - if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - if (!gspca_dev->streaming) { - ret = 0; - goto out; - } - - /* check the capture file */ - if (gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - /* stop streaming */ - gspca_stream_off(gspca_dev); - /* In case another thread is waiting in dqbuf */ - wake_up_interruptible(&gspca_dev->wq); - - /* empty the transfer queues */ - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; - atomic_set(&gspca_dev->fr_q, 0); - atomic_set(&gspca_dev->fr_i, 0); - gspca_dev->fr_o = 0; - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { @@ -1561,7 +1256,7 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = video_drvdata(filp); - parm->parm.capture.readbuffers = gspca_dev->nbufread; + parm->parm.capture.readbuffers = 2; if (gspca_dev->sd_desc->get_streamparm) { gspca_dev->usb_err = 0; @@ -1575,13 +1270,8 @@ static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { struct gspca_dev *gspca_dev = video_drvdata(filp); - unsigned int n; - n = parm->parm.capture.readbuffers; - if (n == 0 || n >= GSPCA_MAX_FRAMES) - parm->parm.capture.readbuffers = gspca_dev->nbufread; - else - gspca_dev->nbufread = n; + parm->parm.capture.readbuffers = 2; if (gspca_dev->sd_desc->set_streamparm) { gspca_dev->usb_err = 0; @@ -1592,418 +1282,138 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -static int dev_mmap(struct file *file, struct vm_area_struct *vma) +static int gspca_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) { - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - struct page *page; - unsigned long addr, start, size; - int i, ret; + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); - start = vma->vm_start; - size = vma->vm_end - vma->vm_start; - gspca_dbg(gspca_dev, D_STREAM, "mmap start:%08x size:%d\n", - (int) start, (int)size); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - if (gspca_dev->capt_file != file) { - ret = -EINVAL; - goto out; - } - - frame = NULL; - for (i = 0; i < gspca_dev->nframes; ++i) { - if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { - gspca_dbg(gspca_dev, D_STREAM, "mmap bad memory type\n"); - break; - } - if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) - == vma->vm_pgoff) { - frame = &gspca_dev->frame[i]; - break; - } - } - if (frame == NULL) { - gspca_dbg(gspca_dev, D_STREAM, "mmap no frame buffer found\n"); - ret = -EINVAL; - goto out; - } - if (size != frame->v4l2_buf.length) { - gspca_dbg(gspca_dev, D_STREAM, "mmap bad size\n"); - ret = -EINVAL; - goto out; - } - - /* - * - VM_IO marks the area as being a mmaped region for I/O to a - * device. It also prevents the region from being core dumped. - */ - vma->vm_flags |= VM_IO; - - addr = (unsigned long) frame->data; - while (size > 0) { - page = vmalloc_to_page((void *) addr); - ret = vm_insert_page(vma, start, page); - if (ret < 0) - goto out; - start += PAGE_SIZE; - addr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &gspca_vm_ops; - vma->vm_private_data = frame; - gspca_vm_open(vma); - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; + if (*nplanes) + return sizes[0] < gspca_dev->pixfmt.sizeimage ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = gspca_dev->pixfmt.sizeimage; + return 0; } -static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory) +static int gspca_buffer_prepare(struct vb2_buffer *vb) { - if (!gspca_dev->present) - return -ENODEV; - if (gspca_dev->capt_file != file || gspca_dev->memory != memory || - !gspca_dev->streaming) + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = gspca_dev->pixfmt.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + gspca_err(gspca_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); return -EINVAL; - - /* check if a frame is ready */ - return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); + } + return 0; } -static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory) +static void gspca_buffer_finish(struct vb2_buffer *vb) { + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + + if (!gspca_dev->sd_desc->dq_callback) + return; + + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); +} + +static void gspca_buffer_queue(struct vb2_buffer *vb) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + struct gspca_buffer *buf = to_gspca_buffer(vb); + unsigned long flags; + + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_add_tail(&buf->list, &gspca_dev->buf_list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); +} + +static void gspca_return_all_buffers(struct gspca_dev *gspca_dev, + enum vb2_buffer_state state) +{ + struct gspca_buffer *buf, *node; + unsigned long flags; + + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_for_each_entry_safe(buf, node, &gspca_dev->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } + spin_unlock_irqrestore(&gspca_dev->qlock, flags); +} + +static int gspca_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); int ret; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - ret = frame_ready_nolock(gspca_dev, file, memory); - mutex_unlock(&gspca_dev->queue_lock); + gspca_dev->sequence = 0; + + ret = gspca_init_transfer(gspca_dev); + if (ret) + gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_QUEUED); return ret; } -/* - * dequeue a video buffer - * - * If nonblock_ing is false, block until a buffer is available. - */ -static int vidioc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) +static void gspca_stop_streaming(struct vb2_queue *vq) { - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - int i, j, ret; + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); - gspca_dbg(gspca_dev, D_FRAM, "dqbuf\n"); + gspca_stream_off(gspca_dev); - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - for (;;) { - ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); - if (ret < 0) - goto out; - if (ret > 0) - break; - - mutex_unlock(&gspca_dev->queue_lock); - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - /* wait till a frame is ready */ - ret = wait_event_interruptible_timeout(gspca_dev->wq, - frame_ready(gspca_dev, file, v4l2_buf->memory), - msecs_to_jiffies(3000)); - if (ret < 0) - return ret; - if (ret == 0) - return -EIO; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - } - - i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - - gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; - - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); - gspca_dbg(gspca_dev, D_FRAM, "dqbuf %d\n", j); - ret = 0; - - if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { - if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, - frame->data, - frame->v4l2_buf.bytesused)) { - gspca_err(gspca_dev, "dqbuf cp to user failed\n"); - ret = -EFAULT; - } - } -out: - mutex_unlock(&gspca_dev->queue_lock); - - if (ret == 0 && gspca_dev->sd_desc->dq_callback) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; - if (gspca_dev->present) - gspca_dev->sd_desc->dq_callback(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } - - return ret; + /* Release all active buffers */ + gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_ERROR); } -/* - * queue a video buffer - * - * Attempting to queue a buffer that has already been - * queued will return -EINVAL. - */ -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - int i, index, ret; - - gspca_dbg(gspca_dev, D_FRAM, "qbuf %d\n", v4l2_buf->index); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - index = v4l2_buf->index; - if ((unsigned) index >= gspca_dev->nframes) { - gspca_dbg(gspca_dev, D_FRAM, - "qbuf idx %d >= %d\n", index, gspca_dev->nframes); - ret = -EINVAL; - goto out; - } - if (v4l2_buf->memory != gspca_dev->memory) { - gspca_dbg(gspca_dev, D_FRAM, "qbuf bad memory type\n"); - ret = -EINVAL; - goto out; - } - - frame = &gspca_dev->frame[index]; - if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { - gspca_dbg(gspca_dev, D_FRAM, "qbuf bad state\n"); - ret = -EINVAL; - goto out; - } - - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; - - if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { - frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; - frame->v4l2_buf.length = v4l2_buf->length; - } - - /* put the buffer in the 'queued' queue */ - i = atomic_read(&gspca_dev->fr_q); - gspca_dev->fr_queue[i] = index; - atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); - - v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; - v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -/* - * allocate the resources for read() - */ -static int read_alloc(struct gspca_dev *gspca_dev, - struct file *file) -{ - struct v4l2_buffer v4l2_buf; - int i, ret; - - gspca_dbg(gspca_dev, D_STREAM, "read alloc\n"); - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (gspca_dev->nframes == 0) { - struct v4l2_requestbuffers rb; - - memset(&rb, 0, sizeof rb); - rb.count = gspca_dev->nbufread; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = GSPCA_MEMORY_READ; - ret = vidioc_reqbufs(file, gspca_dev, &rb); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read reqbuf err %d\n", - ret); - goto out; - } - memset(&v4l2_buf, 0, sizeof v4l2_buf); - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = GSPCA_MEMORY_READ; - for (i = 0; i < gspca_dev->nbufread; i++) { - v4l2_buf.index = i; - ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read qbuf err: %d\n", - ret); - goto out; - } - } - } - - /* start streaming */ - ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret != 0) - gspca_dbg(gspca_dev, D_STREAM, "read streamon err %d\n", ret); -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; -} - -static __poll_t dev_poll(struct file *file, poll_table *wait) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - __poll_t req_events = poll_requested_events(wait); - __poll_t ret = 0; - - gspca_dbg(gspca_dev, D_FRAM, "poll\n"); - - if (req_events & EPOLLPRI) - ret |= v4l2_ctrl_poll(file, wait); - - if (req_events & (EPOLLIN | EPOLLRDNORM)) { - /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->memory == GSPCA_MEMORY_NO) { - if (read_alloc(gspca_dev, file) != 0) { - ret |= EPOLLERR; - goto out; - } - } - - poll_wait(file, &gspca_dev->wq, wait); - - /* check if an image has been received */ - if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { - ret |= EPOLLERR; - goto out; - } - if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) - ret |= EPOLLIN | EPOLLRDNORM; - mutex_unlock(&gspca_dev->queue_lock); - } - -out: - if (!gspca_dev->present) - ret |= EPOLLHUP; - - return ret; -} - -static ssize_t dev_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - struct v4l2_buffer v4l2_buf; - struct timeval timestamp; - int n, ret, ret2; - - gspca_dbg(gspca_dev, D_FRAM, "read (%zd)\n", count); - if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ - ret = read_alloc(gspca_dev, file); - if (ret != 0) - return ret; - } - - /* get a frame */ - v4l2_get_timestamp(×tamp); - timestamp.tv_sec--; - n = 2; - for (;;) { - memset(&v4l2_buf, 0, sizeof v4l2_buf); - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = GSPCA_MEMORY_READ; - ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read dqbuf err %d\n", - ret); - return ret; - } - - /* if the process slept for more than 1 second, - * get a newer frame */ - frame = &gspca_dev->frame[v4l2_buf.index]; - if (--n < 0) - break; /* avoid infinite loop */ - if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) - break; - ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read qbuf err %d\n", - ret); - return ret; - } - } - - /* copy the frame */ - if (count > frame->v4l2_buf.bytesused) - count = frame->v4l2_buf.bytesused; - ret = copy_to_user(data, frame->data, count); - if (ret != 0) { - gspca_err(gspca_dev, "read cp to user lack %d / %zd\n", - ret, count); - ret = -EFAULT; - goto out; - } - ret = count; -out: - /* in each case, requeue the buffer */ - ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret2 != 0) - return ret2; - return ret; -} +static const struct vb2_ops gspca_qops = { + .queue_setup = gspca_queue_setup, + .buf_prepare = gspca_buffer_prepare, + .buf_finish = gspca_buffer_finish, + .buf_queue = gspca_buffer_queue, + .start_streaming = gspca_start_streaming, + .stop_streaming = gspca_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; static const struct v4l2_file_operations dev_fops = { .owner = THIS_MODULE, - .open = dev_open, - .release = dev_close, - .read = dev_read, - .mmap = dev_mmap, + .open = v4l2_fh_open, + .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, - .poll = dev_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, }; static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_qbuf = vidioc_qbuf, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_streamon = vidioc_streamon, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_streamoff = vidioc_streamoff, .vidioc_g_jpegcomp = vidioc_g_jpegcomp, .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, @@ -2034,6 +1444,7 @@ int gspca_dev_probe2(struct usb_interface *intf, { struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); + struct vb2_queue *q; int ret; pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", @@ -2078,20 +1489,37 @@ int gspca_dev_probe2(struct usb_interface *intf, ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); if (ret) goto out; + gspca_dev->present = true; gspca_dev->sd_desc = sd_desc; - gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; video_set_drvdata(&gspca_dev->vdev, gspca_dev); gspca_dev->module = module; - gspca_dev->present = 1; mutex_init(&gspca_dev->usb_lock); gspca_dev->vdev.lock = &gspca_dev->usb_lock; - mutex_init(&gspca_dev->queue_lock); init_waitqueue_head(&gspca_dev->wq); + /* Initialize the vb2 queue */ + q = &gspca_dev->queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = gspca_dev; + q->buf_struct_size = sizeof(struct gspca_buffer); + q->ops = &gspca_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &gspca_dev->usb_lock; + ret = vb2_queue_init(q); + if (ret) + goto out; + gspca_dev->vdev.queue = q; + + INIT_LIST_HEAD(&gspca_dev->buf_list); + spin_lock_init(&gspca_dev->qlock); + /* configure the subdriver and initialize the USB device */ ret = sd_desc->config(gspca_dev, id); if (ret < 0) @@ -2109,14 +1537,6 @@ int gspca_dev_probe2(struct usb_interface *intf, if (ret) goto out; - /* - * Don't take usb_lock for these ioctls. This improves latency if - * usb_lock is taken for a long time, e.g. when changing a control - * value, and a new frame is ready to be dequeued. - */ - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); #ifdef CONFIG_VIDEO_ADV_DEBUG if (!gspca_dev->sd_desc->get_register) v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER); @@ -2198,24 +1618,17 @@ void gspca_disconnect(struct usb_interface *intf) video_device_node_name(&gspca_dev->vdev)); mutex_lock(&gspca_dev->usb_lock); + gspca_dev->present = false; - gspca_dev->present = 0; - destroy_urbs(gspca_dev); + vb2_queue_error(&gspca_dev->queue); #if IS_ENABLED(CONFIG_INPUT) - gspca_input_destroy_urb(gspca_dev); input_dev = gspca_dev->input_dev; if (input_dev) { gspca_dev->input_dev = NULL; input_unregister_device(input_dev); } #endif - /* Free subdriver's streaming resources / stop sd workqueue(s) */ - if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) - gspca_dev->sd_desc->stop0(gspca_dev); - gspca_dev->streaming = 0; - gspca_dev->dev = NULL; - wake_up_interruptible(&gspca_dev->wq); v4l2_device_disconnect(&gspca_dev->v4l2_dev); video_unregister_device(&gspca_dev->vdev); @@ -2234,7 +1647,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) gspca_input_destroy_urb(gspca_dev); - if (!gspca_dev->streaming) + if (!vb2_start_streaming_called(&gspca_dev->queue)) return 0; mutex_lock(&gspca_dev->usb_lock); @@ -2266,8 +1679,7 @@ int gspca_resume(struct usb_interface *intf) * only write to the device registers on s_ctrl when streaming -> * Clear streaming to avoid setting all ctrls twice. */ - streaming = gspca_dev->streaming; - gspca_dev->streaming = 0; + streaming = vb2_start_streaming_called(&gspca_dev->queue); if (streaming) ret = gspca_init_transfer(gspca_dev); else diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h index 249cb38a542f..b0ced2e14006 100644 --- a/drivers/media/usb/gspca/gspca.h +++ b/drivers/media/usb/gspca/gspca.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include @@ -138,19 +140,22 @@ enum gspca_packet_type { LAST_PACKET }; -struct gspca_frame { - __u8 *data; /* frame buffer */ - int vma_use_count; - struct v4l2_buffer v4l2_buf; +struct gspca_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; }; +static inline struct gspca_buffer *to_gspca_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct gspca_buffer, vb.vb2_buf); +} + struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ struct v4l2_device v4l2_dev; struct usb_device *dev; - struct file *capt_file; /* file doing video capture */ - /* protected by queue_lock */ + #if IS_ENABLED(CONFIG_INPUT) struct input_dev *input_dev; char phys[64]; /* physical device path */ @@ -176,34 +181,29 @@ struct gspca_dev { struct urb *int_urb; #endif - __u8 *frbuf; /* buffer for nframes */ - struct gspca_frame frame[GSPCA_MAX_FRAMES]; - u8 *image; /* image beeing filled */ - __u32 frsz; /* frame size */ + u8 *image; /* image being filled */ u32 image_len; /* current length of image */ - atomic_t fr_q; /* next frame to queue */ - atomic_t fr_i; /* frame being filled */ - signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ - char nframes; /* number of frames */ - u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ - __u8 streaming; /* protected by both mutexes (*) */ + bool streaming; __u8 curr_mode; /* current camera mode */ struct v4l2_pix_format pixfmt; /* current mode parameters */ __u32 sequence; /* frame sequence number */ + struct vb2_queue queue; + + spinlock_t qlock; + struct list_head buf_list; + wait_queue_head_t wq; /* wait queue */ struct mutex usb_lock; /* usb exchange protection */ - struct mutex queue_lock; /* ISOC queue protection */ int usb_err; /* USB error - protected by usb_lock */ u16 pkt_size; /* ISOC packet size */ #ifdef CONFIG_PM char frozen; /* suspend - resume */ #endif - char present; /* device connected */ - char nbufread; /* number of buffers for read() */ + bool present; char memory; /* memory type (V4L2_MEMORY_xxx) */ __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ diff --git a/drivers/media/usb/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c index b83ec4285a0b..30b7cf1feedd 100644 --- a/drivers/media/usb/gspca/m5602/m5602_core.c +++ b/drivers/media/usb/gspca/m5602/m5602_core.c @@ -342,7 +342,7 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, data += 4; len -= 4; - if (cur_frame_len + len <= gspca_dev->frsz) { + if (cur_frame_len + len <= gspca_dev->pixfmt.sizeimage) { gspca_dbg(gspca_dev, D_FRAM, "Continuing frame %d copying %d bytes\n", sd->frame_count, len); @@ -351,7 +351,7 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, } else { /* Add the remaining data up to frame size */ gspca_frame_add(gspca_dev, INTER_PACKET, data, - gspca_dev->frsz - cur_frame_len); + gspca_dev->pixfmt.sizeimage - cur_frame_len); } } } diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c index 6b11597977c9..52d071659634 100644 --- a/drivers/media/usb/gspca/vc032x.c +++ b/drivers/media/usb/gspca/vc032x.c @@ -3642,7 +3642,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int size, l; l = gspca_dev->image_len; - size = gspca_dev->frsz; + size = gspca_dev->pixfmt.sizeimage; if (len > size - l) len = size - l; }