static int _pwc_mpt_reset(struct pwc_device *pdev, int flags)
{
unsigned char buf;
+ int r;
+
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ r = -ENODEV;
+ goto leave;
+ }
buf = flags & 0x03; // only lower two bits are currently used
- return send_control_msg(pdev,
+ r = send_control_msg(pdev,
SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, &buf, sizeof(buf));
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return r;
}
int pwc_mpt_reset(struct pwc_device *pdev, int flags)
static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
{
unsigned char buf[4];
+ int r;
+
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ r = -ENODEV;
+ goto leave;
+ }
/* set new relative angle; angles are expressed in degrees * 100,
but cam as .5 degree resolution, hence divide by 200. Also
buf[1] = (pan >> 8) & 0xFF;
buf[2] = tilt & 0xFF;
buf[3] = (tilt >> 8) & 0xFF;
- return send_control_msg(pdev,
+ r = send_control_msg(pdev,
SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, &buf, sizeof(buf));
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return r;
}
int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
int ret;
unsigned char buf[5];
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
ret = recv_control_msg(pdev,
GET_MPT_CTL, PT_STATUS_FORMATTER, &buf, sizeof(buf));
if (ret < 0)
- return ret;
+ goto leave;
status->status = buf[0] & 0x7; // 3 bits are used for reporting
status->time_pan = (buf[1] << 8) + buf[2];
status->time_tilt = (buf[3] << 8) + buf[4];
- return 0;
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return ret;
}
#ifdef CONFIG_USB_PWC_DEBUG
switch(cmd) {
case VIDIOCPWCRUSER:
- ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
+ ret = v4l2_ctrl_s_ctrl(pdev->restore_user, 0);
break;
case VIDIOCPWCSUSER:
- ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
+ ret = v4l2_ctrl_s_ctrl(pdev->save_user, 0);
break;
case VIDIOCPWCFACTORY:
- ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER);
+ ret = v4l2_ctrl_s_ctrl(pdev->restore_factory, 0);
break;
case VIDIOCPWCSCQUAL:
{
ARG_DEF(int, qual)
- if (vb2_is_streaming(&pdev->vb_queue)) {
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (pdev->iso_init) {
ret = -EBUSY;
- break;
+ goto leave;
}
ARG_IN(qual)
ret = -EINVAL;
else
ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
+leave:
+ mutex_unlock(&pdev->udevlock);
break;
}
{
ARG_DEF(struct pwc_leds, leds)
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ break;
+ }
+
ARG_IN(leds)
ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off);
+
+ mutex_unlock(&pdev->udevlock);
break;
}
{
ARG_DEF(struct pwc_leds, leds)
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ break;
+ }
+
ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off);
ARG_OUT(leds)
+
+ mutex_unlock(&pdev->udevlock);
break;
}
}
pdec = pwc->decompress_data;
+ mutex_init(&pdec->lock);
+
if (DEVICE_USE_CODEC3(type)) {
flags = cmd[2] & 0x18;
if (flags == 8)
int flags)
{
int bandlines_left, stride, bytes_per_block;
+ struct pwc_dec23_private *pdec = pwc->decompress_data;
+
+ mutex_lock(&pdec->lock);
bandlines_left = pwc->image.y / 4;
bytes_per_block = pwc->view.x * 4;
}
}
+
+ mutex_unlock(&pdec->lock);
}
struct pwc_dec23_private
{
+ struct mutex lock;
+
unsigned int scalebits;
unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */
PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
}
-/*
- * Release all queued buffers, no need to take queued_bufs_lock, since all
- * iso urbs have been killed when we're called so pwc_isoc_handler won't run.
- */
static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
while (!list_empty(&pdev->queued_bufs)) {
struct pwc_frame_buf *buf;
list_del(&buf->list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
}
/*********
/***************************************************************************/
/* Video4Linux functions */
+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;
+leave:
+ mutex_unlock(&pdev->capt_file_lock);
+ return r;
+}
+
static void pwc_video_release(struct v4l2_device *v)
{
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
if (!pdev->udev)
return -ENODEV;
- if (pdev->capt_file != NULL &&
- pdev->capt_file != file)
+ if (pwc_test_n_set_capt_file(pdev, file))
return -EBUSY;
- pdev->capt_file = file;
-
return vb2_read(&pdev->vb_queue, buf, count, ppos,
file->f_flags & O_NONBLOCK);
}
unsigned long flags = 0;
spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
- list_add_tail(&buf->list, &pdev->queued_bufs);
+ /* Check the device has not disconnected between prep and queuing */
+ if (pdev->udev)
+ list_add_tail(&buf->list, &pdev->queued_bufs);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
}
static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ int r;
- if (!pdev->udev)
- return -ENODEV;
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ r = -ENODEV;
+ goto leave;
+ }
/* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
}
pwc_set_leds(pdev, led_on, led_off);
- return pwc_isoc_init(pdev);
+ r = pwc_isoc_init(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 pwc_lock(struct vb2_queue *vq)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
- mutex_lock(&pdev->modlock);
-}
-
-static void pwc_unlock(struct vb2_queue *vq)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
- mutex_unlock(&pdev->modlock);
-}
-
static struct vb2_ops pwc_vb_queue_ops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
- .wait_prepare = pwc_unlock,
- .wait_finish = pwc_lock,
};
/***************************************************************************/
}
pwc_construct(pdev); /* set min/max sizes correct */
- mutex_init(&pdev->modlock);
+ mutex_init(&pdev->capt_file_lock);
mutex_init(&pdev->udevlock);
spin_lock_init(&pdev->queued_bufs_lock);
INIT_LIST_HEAD(&pdev->queued_bufs);
/* Init video_device structure */
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
- pdev->vdev.lock = &pdev->modlock;
strcpy(pdev->vdev.name, name);
set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
video_set_drvdata(&pdev->vdev, pdev);
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
mutex_lock(&pdev->udevlock);
- mutex_lock(&pdev->modlock);
-
/* No need to keep the urbs around after disconnection */
pwc_isoc_cleanup(pdev);
- pwc_cleanup_queued_bufs(pdev);
pdev->udev = NULL;
-
- mutex_unlock(&pdev->modlock);
mutex_unlock(&pdev->udevlock);
+ pwc_cleanup_queued_bufs(pdev);
+
pwc_remove_sysfs_files(pdev);
video_unregister_device(&pdev->vdev);
v4l2_device_unregister(&pdev->v4l2_dev);
struct pwc_device *pdev = video_drvdata(file);
int ret, fps, snapshot, compression, pixelformat;
- if (!pdev->udev)
- return -ENODEV;
-
- if (pdev->capt_file != NULL &&
- pdev->capt_file != file)
+ if (pwc_test_n_set_capt_file(pdev, file))
return -EBUSY;
- pdev->capt_file = file;
-
ret = pwc_vidioc_try_fmt(pdev, f);
- if (ret<0)
+ if (ret < 0)
return ret;
pixelformat = f->fmt.pix.pixelformat;
pixelformat != V4L2_PIX_FMT_PWC2)
return -EINVAL;
- if (vb2_is_streaming(&pdev->vb_queue))
- return -EBUSY;
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (pdev->iso_init) {
+ ret = -EBUSY;
+ goto leave;
+ }
PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
"compression=%d snapshot=%d format=%c%c%c%c\n",
PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
- if (ret)
- return ret;
-
- pdev->pixfmt = pixelformat;
-
- pwc_vidioc_fill_fmt(pdev, f);
-
- return 0;
+ if (ret == 0) {
+ pdev->pixfmt = pixelformat;
+ pwc_vidioc_fill_fmt(pdev, f);
+ }
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return ret;
}
static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
container_of(ctrl->handler, struct pwc_device, ctrl_handler);
int ret = 0;
- /*
- * Sometimes it can take quite long for the pwc to complete usb control
- * transfers, so release the modlock to give streaming by another
- * process / thread the chance to continue with a dqbuf.
- */
- mutex_unlock(&pdev->modlock);
-
- /*
- * Take the udev-lock to protect against the disconnect handler
- * completing and setting dev->udev to NULL underneath us. Other code
- * does not need to do this since it is protected by the modlock.
- */
mutex_lock(&pdev->udevlock);
if (!pdev->udev) {
leave:
mutex_unlock(&pdev->udevlock);
- mutex_lock(&pdev->modlock);
return ret;
}
container_of(ctrl->handler, struct pwc_device, ctrl_handler);
int ret = 0;
- /* See the comments on locking in pwc_g_volatile_ctrl */
- mutex_unlock(&pdev->modlock);
mutex_lock(&pdev->udevlock);
if (!pdev->udev) {
leave:
mutex_unlock(&pdev->udevlock);
- mutex_lock(&pdev->modlock);
return ret;
}
{
struct pwc_device *pdev = video_drvdata(file);
+ mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */
PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
pdev->image.x, pdev->image.y);
pwc_vidioc_fill_fmt(pdev, f);
+ mutex_unlock(&pdev->udevlock);
return 0;
}
{
struct pwc_device *pdev = video_drvdata(file);
- if (pdev->capt_file != NULL &&
- pdev->capt_file != file)
+ if (pwc_test_n_set_capt_file(pdev, file))
return -EBUSY;
- pdev->capt_file = file;
-
return vb2_reqbufs(&pdev->vb_queue, rb);
}
{
struct video_device vdev;
struct v4l2_device v4l2_dev;
- struct mutex modlock;
/* Pointer to our usb_device, may be NULL after unplug */
struct usb_device *udev;
- /* Protects the setting of udev to NULL by our disconnect handler */
struct mutex udevlock;
/* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
/*** Video data ***/
struct file *capt_file; /* file doing video capture */
+ struct mutex capt_file_lock;
int vendpoint; /* video isoc endpoint */
int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */
extern int pwc_trace;
#endif
+int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file);
+
/** Functions in pwc-misc.c */
/* sizes in pixels */
extern const struct pwc_coord pwc_image_sizes[PSZ_MAX];