Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 306299
b: refs/heads/master
c: ceede9f
h: refs/heads/master
i:
  306297: effec6a
  306295: 76c2c3a
v: v3
  • Loading branch information
Hans de Goede authored and Mauro Carvalho Chehab committed May 14, 2012
1 parent 235c562 commit eb459ba
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 154 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a67e17221429c0322e543ae4bf0b25259c3416a5
refs/heads/master: ceede9fa8939e40ad0ddb4ad1355f45c6f1d3478
190 changes: 121 additions & 69 deletions trunk/drivers/media/video/pwc/pwc-if.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ static void pwc_isoc_handler(struct urb *urb)
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}

/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
static int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
Expand All @@ -366,9 +367,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
struct usb_host_interface *idesc = NULL;
int compression = 0; /* 0..3 = uncompressed..high */

if (pdev->iso_init)
return 0;

pdev->vsync = 0;
pdev->vlast_packet_size = 0;
pdev->fill_buf = NULL;
Expand Down Expand Up @@ -418,7 +416,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
PWC_ERROR("Failed to allocate urb %d\n", i);
pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return -ENOMEM;
}
Expand All @@ -435,7 +432,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
PWC_ERROR("Failed to allocate urb buffer %d\n", i);
pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return -ENOMEM;
}
Expand All @@ -455,21 +451,18 @@ static int pwc_isoc_init(struct pwc_device *pdev)
ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
if (ret == -ENOSPC && compression < 3) {
compression++;
pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
goto retry;
}
if (ret) {
PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return ret;
}
PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
}

/* All is done... */
pdev->iso_init = 1;
PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
return 0;
}
Expand Down Expand Up @@ -507,21 +500,19 @@ static void pwc_iso_free(struct pwc_device *pdev)
}
}

/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
static void pwc_isoc_cleanup(struct pwc_device *pdev)
{
PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");

if (pdev->iso_init == 0)
return;

pwc_iso_stop(pdev);
pwc_iso_free(pdev);
usb_set_interface(pdev->udev, 0, 0);

pdev->iso_init = 0;
PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
}

/* Must be called with vb_queue_lock hold */
static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
{
unsigned long flags = 0;
Expand Down Expand Up @@ -573,25 +564,21 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type)

int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file)
{
int r = 0;

mutex_lock(&pdev->capt_file_lock);
if (pdev->capt_file != NULL &&
pdev->capt_file != file) {
r = -EBUSY;
goto leave;
}
pdev->capt_file != file)
return -EBUSY;

pdev->capt_file = file;
leave:
mutex_unlock(&pdev->capt_file_lock);
return r;

return 0;
}

static void pwc_video_release(struct v4l2_device *v)
{
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);

v4l2_ctrl_handler_free(&pdev->ctrl_handler);
v4l2_device_unregister(&pdev->v4l2_dev);
kfree(pdev->ctrl_buf);
kfree(pdev);
}
Expand All @@ -600,55 +587,107 @@ static int pwc_video_close(struct file *file)
{
struct pwc_device *pdev = video_drvdata(file);

/*
* If we're still streaming vb2_queue_release will call stream_stop
* so we must take both the v4l2_lock and the vb_queue_lock.
*/
if (mutex_lock_interruptible(&pdev->v4l2_lock))
return -ERESTARTSYS;
if (mutex_lock_interruptible(&pdev->vb_queue_lock)) {
mutex_unlock(&pdev->v4l2_lock);
return -ERESTARTSYS;
}

if (pdev->capt_file == file) {
vb2_queue_release(&pdev->vb_queue);
pdev->capt_file = NULL;
}

mutex_unlock(&pdev->vb_queue_lock);
mutex_unlock(&pdev->v4l2_lock);

return v4l2_fh_release(file);
}

static ssize_t pwc_video_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct pwc_device *pdev = video_drvdata(file);
int lock_v4l2 = 0;
ssize_t ret;

if (!pdev->udev)
return -ENODEV;
if (mutex_lock_interruptible(&pdev->vb_queue_lock))
return -ERESTARTSYS;

if (pwc_test_n_set_capt_file(pdev, file))
return -EBUSY;
ret = pwc_test_n_set_capt_file(pdev, file);
if (ret)
goto out;

/* stream_start will get called so we must take the v4l2_lock */
if (pdev->vb_queue.fileio == NULL)
lock_v4l2 = 1;

return vb2_read(&pdev->vb_queue, buf, count, ppos,
file->f_flags & O_NONBLOCK);
/* Use try_lock, since we're taking the locks in the *wrong* order! */
if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) {
ret = -ERESTARTSYS;
goto out;
}
ret = vb2_read(&pdev->vb_queue, buf, count, ppos,
file->f_flags & O_NONBLOCK);
if (lock_v4l2)
mutex_unlock(&pdev->v4l2_lock);
out:
mutex_unlock(&pdev->vb_queue_lock);
return ret;
}

static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
{
struct pwc_device *pdev = video_drvdata(file);
struct vb2_queue *q = &pdev->vb_queue;
unsigned long req_events = poll_requested_events(wait);
unsigned int ret = POLL_ERR;
int lock_v4l2 = 0;

if (!pdev->udev)
if (mutex_lock_interruptible(&pdev->vb_queue_lock))
return POLL_ERR;

/* Will this start fileio and thus call start_stream? */
if ((req_events & (POLLIN | POLLRDNORM)) &&
pdev->vb_queue.num_buffers == 0 &&
!pdev->iso_init) {
/* This poll will start a read stream, check capt_file */
q->num_buffers == 0 && !q->streaming && q->fileio == NULL) {
if (pwc_test_n_set_capt_file(pdev, file))
return POLL_ERR;
goto out;
lock_v4l2 = 1;
}

return vb2_poll(&pdev->vb_queue, file, wait);
/* Use try_lock, since we're taking the locks in the *wrong* order! */
if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock))
goto out;
ret = vb2_poll(&pdev->vb_queue, file, wait);
if (lock_v4l2)
mutex_unlock(&pdev->v4l2_lock);

out:
if (!pdev->udev)
ret |= POLLHUP;
mutex_unlock(&pdev->vb_queue_lock);
return ret;
}

static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct pwc_device *pdev = video_drvdata(file);
int ret;

if (pdev->capt_file != file)
return -EBUSY;
if (mutex_lock_interruptible(&pdev->vb_queue_lock))
return -ERESTARTSYS;

ret = pwc_test_n_set_capt_file(pdev, file);
if (ret == 0)
ret = vb2_mmap(&pdev->vb_queue, vma);

return vb2_mmap(&pdev->vb_queue, vma);
mutex_unlock(&pdev->vb_queue_lock);
return ret;
}

/***************************************************************************/
Expand Down Expand Up @@ -724,12 +763,14 @@ static void buffer_queue(struct vb2_buffer *vb)
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
unsigned long flags = 0;

spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
/* Check the device has not disconnected between prep and queuing */
if (pdev->udev)
list_add_tail(&buf->list, &pdev->queued_bufs);
else
if (!pdev->udev) {
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
return;
}

spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
list_add_tail(&buf->list, &pdev->queued_bufs);
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
}

Expand All @@ -738,11 +779,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct pwc_device *pdev = vb2_get_drv_priv(vq);
int r;

mutex_lock(&pdev->udevlock);
if (!pdev->udev) {
r = -ENODEV;
goto leave;
}
if (!pdev->udev)
return -ENODEV;

/* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
Expand All @@ -756,28 +794,37 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
/* And cleanup any queued bufs!! */
pwc_cleanup_queued_bufs(pdev);
}
leave:
mutex_unlock(&pdev->udevlock);

return r;
}

static int stop_streaming(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);

mutex_lock(&pdev->udevlock);
if (pdev->udev) {
pwc_set_leds(pdev, 0, 0);
pwc_camera_power(pdev, 0);
pwc_isoc_cleanup(pdev);
}
mutex_unlock(&pdev->udevlock);

pwc_cleanup_queued_bufs(pdev);

return 0;
}

static void wait_prepare(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
mutex_unlock(&pdev->vb_queue_lock);
}

static void wait_finish(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
mutex_lock(&pdev->vb_queue_lock);
}

static struct vb2_ops pwc_vb_queue_ops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
Expand All @@ -787,6 +834,8 @@ static struct vb2_ops pwc_vb_queue_ops = {
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
.wait_prepare = wait_prepare,
.wait_finish = wait_finish,
};

/***************************************************************************/
Expand Down Expand Up @@ -1066,8 +1115,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->features = features;
pwc_construct(pdev); /* set min/max sizes correct */

mutex_init(&pdev->capt_file_lock);
mutex_init(&pdev->udevlock);
mutex_init(&pdev->v4l2_lock);
mutex_init(&pdev->vb_queue_lock);
spin_lock_init(&pdev->queued_bufs_lock);
INIT_LIST_HEAD(&pdev->queued_bufs);

Expand Down Expand Up @@ -1139,6 +1188,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id

pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
pdev->vdev.lock = &pdev->v4l2_lock;

/*
* Don't take v4l2_lock for these ioctls. This improves latency if
* v4l2_lock is taken for a long time, e.g. when changing a control
* value, and a new frame is ready to be dequeued.
*/
v4l2_dont_use_lock(&pdev->vdev, VIDIOC_DQBUF);
v4l2_dont_use_lock(&pdev->vdev, VIDIOC_QBUF);
v4l2_dont_use_lock(&pdev->vdev, VIDIOC_QUERYBUF);

rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
if (rc < 0) {
Expand Down Expand Up @@ -1194,16 +1253,20 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
struct v4l2_device *v = usb_get_intfdata(intf);
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);

mutex_lock(&pdev->udevlock);
mutex_lock(&pdev->v4l2_lock);

mutex_lock(&pdev->vb_queue_lock);
/* No need to keep the urbs around after disconnection */
pwc_isoc_cleanup(pdev);
if (pdev->vb_queue.streaming)
pwc_isoc_cleanup(pdev);
pdev->udev = NULL;
mutex_unlock(&pdev->udevlock);

pwc_cleanup_queued_bufs(pdev);
mutex_unlock(&pdev->vb_queue_lock);

v4l2_device_disconnect(&pdev->v4l2_dev);
video_unregister_device(&pdev->vdev);
v4l2_device_unregister(&pdev->v4l2_dev);

mutex_unlock(&pdev->v4l2_lock);

#ifdef CONFIG_USB_PWC_INPUT_EVDEV
if (pdev->button_dev)
Expand Down Expand Up @@ -1238,15 +1301,4 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("pwcx");
MODULE_VERSION( PWC_VERSION );

static int __init usb_pwc_init(void)
{
return usb_register(&pwc_driver);
}

static void __exit usb_pwc_exit(void)
{
usb_deregister(&pwc_driver);
}

module_init(usb_pwc_init);
module_exit(usb_pwc_exit);
module_usb_driver(pwc_driver);
Loading

0 comments on commit eb459ba

Please sign in to comment.