Commit 1f5965c4 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

media: gspca: convert to vb2

The gspca core has its own buffere implementation. Use the
core VB 2 instead.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 90b2da89
......@@ -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
......
......@@ -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;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
int mode;
ret = try_fmt_vid_cap(gspca_dev, fmt);
if (ret < 0)
goto out;
if (vb2_is_busy(&gspca_dev->queue))
return -EBUSY;
if (gspca_dev->nframes != 0
&& fmt->fmt.pix.sizeimage > gspca_dev->frsz) {
ret = -EINVAL;
goto out;
}
mode = try_fmt_vid_cap(gspca_dev, fmt);
if (mode < 0)
return -EINVAL;
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)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
struct page *page;
unsigned long addr, start, size;
int i, ret;
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;
}
static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file,
enum v4l2_memory memory)
static int gspca_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
if (!gspca_dev->present)
return -ENODEV;
if (gspca_dev->capt_file != file || gspca_dev->memory != memory ||
!gspca_dev->streaming)
return -EINVAL;
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
/* check if a frame is ready */
return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i);
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(struct gspca_dev *gspca_dev, struct file *file,
enum v4l2_memory memory)
static int gspca_buffer_prepare(struct vb2_buffer *vb)
{
int ret;
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
unsigned long size = gspca_dev->pixfmt.sizeimage;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
ret = frame_ready_nolock(gspca_dev, file, memory);
mutex_unlock(&gspca_dev->queue_lock);
return ret;
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;
}
return 0;
}
/*
* 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_buffer_finish(struct vb2_buffer *vb)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
int i, j, ret;
gspca_dbg(gspca_dev, D_FRAM, "dqbuf\n");
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);
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
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 (!gspca_dev->sd_desc->dq_callback)
return;
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;
}
/*
* 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)
static void gspca_buffer_queue(struct vb2_buffer *vb)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
int i, index, ret;
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
struct gspca_buffer *buf = to_gspca_buffer(vb);
unsigned long flags;
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;
spin_lock_irqsave(&gspca_dev->qlock, flags);
list_add_tail(&buf->list, &gspca_dev->buf_list);
spin_unlock_irqrestore(&gspca_dev->qlock, flags);
}
/*
* allocate the resources for read()
*/
static int read_alloc(struct gspca_dev *gspca_dev,
struct file *file)
static void gspca_return_all_buffers(struct gspca_dev *gspca_dev,
enum vb2_buffer_state state)
{
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;
struct gspca_buffer *buf, *node;
unsigned long flags;
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;
}
}
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);
}
/* 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;
spin_unlock_irqrestore(&gspca_dev->qlock, flags);
}
static __poll_t dev_poll(struct file *file, poll_table *wait)
static int gspca_start_streaming(struct vb2_queue *vq, unsigned int count)
{
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);
}
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
int ret;
out:
if (!gspca_dev->present)
ret |= EPOLLHUP;
gspca_dev->sequence = 0;
ret = gspca_init_transfer(gspca_dev);
if (ret)
gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_QUEUED);
return ret;
}
static ssize_t dev_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
static void gspca_stop_streaming(struct vb2_queue *vq)
{
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(&timestamp);
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;
}
struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
/* 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;
}
}
gspca_stream_off(gspca_dev);
/* 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;
/* Release all active buffers */
gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_ERROR);
}
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
......
......@@ -9,6 +9,8 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
#include <linux/mutex.h>
......@@ -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 */
......
......@@ -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);
}
}
}
......
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment