Commit ae59dad4 authored by Mike Thomas's avatar Mike Thomas Committed by Greg Kroah-Hartman

staging/easycap: Eliminate BKL

No locking is required for normal operation of the driver, but locking
is needed to prevent an Oops during some hot-unplugging scenarios.  The
BKL is replaced here by mutex locks together with traps to detect null
pointers following asynchronous device disconnection.
Signed-off-by: default avatarMike Thomas <rmthomas@sciolus.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 2a87a0b9
config EASYCAP config EASYCAP
tristate "EasyCAP USB ID 05e1:0408 support" tristate "EasyCAP USB ID 05e1:0408 support"
depends on USB && VIDEO_DEV depends on USB && VIDEO_DEV
depends on BKL # please fix
---help--- ---help---
This is an integrated audio/video driver for EasyCAP cards with This is an integrated audio/video driver for EasyCAP cards with
......
...@@ -10,4 +10,5 @@ ccflags-y := -Wall ...@@ -10,4 +10,5 @@ ccflags-y := -Wall
ccflags-y += -DEASYCAP_IS_VIDEODEV_CLIENT ccflags-y += -DEASYCAP_IS_VIDEODEV_CLIENT
ccflags-y += -DEASYCAP_NEEDS_V4L2_DEVICE_H ccflags-y += -DEASYCAP_NEEDS_V4L2_DEVICE_H
ccflags-y += -DEASYCAP_NEEDS_V4L2_FOPS ccflags-y += -DEASYCAP_NEEDS_V4L2_FOPS
ccflags-y += -DEASYCAP_NEEDS_UNLOCKED_IOCTL
...@@ -251,6 +251,12 @@ INTERLACE_MANY ...@@ -251,6 +251,12 @@ INTERLACE_MANY
* STRUCTURE DEFINITIONS * STRUCTURE DEFINITIONS
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
struct easycap_dongle {
struct easycap *peasycap;
struct mutex mutex_video;
struct mutex mutex_audio;
};
/*---------------------------------------------------------------------------*/
struct data_buffer { struct data_buffer {
struct list_head list_head; struct list_head list_head;
void *pgo; void *pgo;
...@@ -491,7 +497,10 @@ struct data_buffer audio_buffer[]; ...@@ -491,7 +497,10 @@ struct data_buffer audio_buffer[];
void easycap_complete(struct urb *); void easycap_complete(struct urb *);
int easycap_open(struct inode *, struct file *); int easycap_open(struct inode *, struct file *);
int easycap_release(struct inode *, struct file *); int easycap_release(struct inode *, struct file *);
long easycap_ioctl(struct file *, unsigned int, unsigned long); long easycap_ioctl_noinode(struct file *, unsigned int, \
unsigned long);
int easycap_ioctl(struct inode *, struct file *, unsigned int, \
unsigned long);
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT) #if defined(EASYCAP_IS_VIDEODEV_CLIENT)
...@@ -538,7 +547,10 @@ void easysnd_complete(struct urb *); ...@@ -538,7 +547,10 @@ void easysnd_complete(struct urb *);
ssize_t easysnd_read(struct file *, char __user *, size_t, loff_t *); ssize_t easysnd_read(struct file *, char __user *, size_t, loff_t *);
int easysnd_open(struct inode *, struct file *); int easysnd_open(struct inode *, struct file *);
int easysnd_release(struct inode *, struct file *); int easysnd_release(struct inode *, struct file *);
long easysnd_ioctl(struct file *, unsigned int, unsigned long); long easysnd_ioctl_noinode(struct file *, unsigned int, \
unsigned long);
int easysnd_ioctl(struct inode *, struct file *, unsigned int, \
unsigned long);
unsigned int easysnd_poll(struct file *, poll_table *); unsigned int easysnd_poll(struct file *, poll_table *);
void easysnd_delete(struct kref *); void easysnd_delete(struct kref *);
int submit_audio_urbs(struct easycap *); int submit_audio_urbs(struct easycap *);
......
...@@ -26,3 +26,4 @@ ...@@ -26,3 +26,4 @@
/*****************************************************************************/ /*****************************************************************************/
extern int debug; extern int debug;
extern int gain; extern int gain;
extern struct easycap_dongle easycap_dongle[];
...@@ -953,11 +953,23 @@ SAM("WARNING: failed to adjust mute: control not found\n"); ...@@ -953,11 +953,23 @@ SAM("WARNING: failed to adjust mute: control not found\n");
return -ENOENT; return -ENOENT;
} }
/*****************************************************************************/ /*****************************************************************************/
static int easycap_ioctl_bkl(struct inode *inode, struct file *file, /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
unsigned int cmd, unsigned long arg) #if ((defined(EASYCAP_IS_VIDEODEV_CLIENT)) || \
(defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)))
long
easycap_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg) {
return (long)easycap_ioctl((struct inode *)NULL, file, cmd, arg);
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT||EASYCAP_NEEDS_UNLOCKED_IOCTL*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*---------------------------------------------------------------------------*/
int
easycap_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{ {
struct easycap *peasycap; struct easycap *peasycap;
struct usb_device *p; struct usb_device *p;
int kd;
if (NULL == file) { if (NULL == file) {
SAY("ERROR: file is NULL\n"); SAY("ERROR: file is NULL\n");
...@@ -973,6 +985,48 @@ if (NULL == p) { ...@@ -973,6 +985,48 @@ if (NULL == p) {
SAM("ERROR: peasycap->pusb_device is NULL\n"); SAM("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT; return -EFAULT;
} }
kd = isdongle(peasycap);
if (0 <= kd && DONGLE_MANY > kd) {
if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_video)) {
SAY("ERROR: cannot lock easycap_dongle[%i].mutex_video\n", kd);
return -ERESTARTSYS;
}
JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
/*---------------------------------------------------------------------------*/
/*
* MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
* IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
* IF NECESSARY, BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
if (kd != isdongle(peasycap))
return -ERESTARTSYS;
if (NULL == file) {
SAY("ERROR: file is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ERESTARTSYS;
}
p = peasycap->pusb_device;
if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ERESTARTSYS;
}
} else {
/*---------------------------------------------------------------------------*/
/*
* IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
* ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
return -ERESTARTSYS;
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
switch (cmd) { switch (cmd) {
case VIDIOC_QUERYCAP: { case VIDIOC_QUERYCAP: {
...@@ -984,7 +1038,9 @@ case VIDIOC_QUERYCAP: { ...@@ -984,7 +1038,9 @@ case VIDIOC_QUERYCAP: {
JOM(8, "VIDIOC_QUERYCAP\n"); JOM(8, "VIDIOC_QUERYCAP\n");
if (16 <= strlen(EASYCAP_DRIVER_VERSION)) { if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
SAM("ERROR: bad driver version string\n"); return -EINVAL; SAM("ERROR: bad driver version string\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL;
} }
strcpy(&version[0], EASYCAP_DRIVER_VERSION); strcpy(&version[0], EASYCAP_DRIVER_VERSION);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
...@@ -1001,6 +1057,7 @@ case VIDIOC_QUERYCAP: { ...@@ -1001,6 +1057,7 @@ case VIDIOC_QUERYCAP: {
if (0 != rc) { if (0 != rc) {
SAM("ERROR: %i=strict_strtol(%s,.,,)\n", \ SAM("ERROR: %i=strict_strtol(%s,.,,)\n", \
rc, p1); rc, p1);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
k[i] = (int)lng; k[i] = (int)lng;
...@@ -1030,8 +1087,10 @@ case VIDIOC_QUERYCAP: { ...@@ -1030,8 +1087,10 @@ case VIDIOC_QUERYCAP: {
&v4l2_capability.bus_info[0]); &v4l2_capability.bus_info[0]);
} }
if (0 != copy_to_user((void __user *)arg, &v4l2_capability, \ if (0 != copy_to_user((void __user *)arg, &v4l2_capability, \
sizeof(struct v4l2_capability))) sizeof(struct v4l2_capability))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1042,8 +1101,10 @@ case VIDIOC_ENUMINPUT: { ...@@ -1042,8 +1101,10 @@ case VIDIOC_ENUMINPUT: {
JOM(8, "VIDIOC_ENUMINPUT\n"); JOM(8, "VIDIOC_ENUMINPUT\n");
if (0 != copy_from_user(&v4l2_input, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_input, (void __user *)arg, \
sizeof(struct v4l2_input))) sizeof(struct v4l2_input))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
index = v4l2_input.index; index = v4l2_input.index;
memset(&v4l2_input, 0, sizeof(struct v4l2_input)); memset(&v4l2_input, 0, sizeof(struct v4l2_input));
...@@ -1123,13 +1184,16 @@ case VIDIOC_ENUMINPUT: { ...@@ -1123,13 +1184,16 @@ case VIDIOC_ENUMINPUT: {
} }
default: { default: {
JOM(8, "%i=index: exhausts inputs\n", index); JOM(8, "%i=index: exhausts inputs\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
if (0 != copy_to_user((void __user *)arg, &v4l2_input, \ if (0 != copy_to_user((void __user *)arg, &v4l2_input, \
sizeof(struct v4l2_input))) sizeof(struct v4l2_input))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1139,8 +1203,10 @@ case VIDIOC_G_INPUT: { ...@@ -1139,8 +1203,10 @@ case VIDIOC_G_INPUT: {
JOM(8, "VIDIOC_G_INPUT\n"); JOM(8, "VIDIOC_G_INPUT\n");
index = (__u32)peasycap->input; index = (__u32)peasycap->input;
JOM(8, "user is told: %i\n", index); JOM(8, "user is told: %i\n", index);
if (0 != copy_to_user((void __user *)arg, &index, sizeof(__u32))) if (0 != copy_to_user((void __user *)arg, &index, sizeof(__u32))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1151,8 +1217,10 @@ case VIDIOC_S_INPUT: ...@@ -1151,8 +1217,10 @@ case VIDIOC_S_INPUT:
JOM(8, "VIDIOC_S_INPUT\n"); JOM(8, "VIDIOC_S_INPUT\n");
if (0 != copy_from_user(&index, (void __user *)arg, sizeof(__u32))) if (0 != copy_from_user(&index, (void __user *)arg, sizeof(__u32))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
JOM(8, "user requests input %i\n", index); JOM(8, "user requests input %i\n", index);
...@@ -1163,6 +1231,7 @@ case VIDIOC_S_INPUT: ...@@ -1163,6 +1231,7 @@ case VIDIOC_S_INPUT:
if ((0 > index) || (INPUT_MANY <= index)) { if ((0 > index) || (INPUT_MANY <= index)) {
JOM(8, "ERROR: bad requested input: %i\n", index); JOM(8, "ERROR: bad requested input: %i\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
...@@ -1171,6 +1240,7 @@ case VIDIOC_S_INPUT: ...@@ -1171,6 +1240,7 @@ case VIDIOC_S_INPUT:
JOM(8, "newinput(.,%i) OK\n", (int)index); JOM(8, "newinput(.,%i) OK\n", (int)index);
} else { } else {
SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc); SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
break; break;
...@@ -1178,6 +1248,7 @@ case VIDIOC_S_INPUT: ...@@ -1178,6 +1248,7 @@ case VIDIOC_S_INPUT:
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_ENUMAUDIO: { case VIDIOC_ENUMAUDIO: {
JOM(8, "VIDIOC_ENUMAUDIO\n"); JOM(8, "VIDIOC_ENUMAUDIO\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1187,18 +1258,24 @@ case VIDIOC_ENUMAUDOUT: { ...@@ -1187,18 +1258,24 @@ case VIDIOC_ENUMAUDOUT: {
JOM(8, "VIDIOC_ENUMAUDOUT\n"); JOM(8, "VIDIOC_ENUMAUDOUT\n");
if (0 != copy_from_user(&v4l2_audioout, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_audioout, (void __user *)arg, \
sizeof(struct v4l2_audioout))) sizeof(struct v4l2_audioout))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
if (0 != v4l2_audioout.index) if (0 != v4l2_audioout.index) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
}
memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout)); memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
v4l2_audioout.index = 0; v4l2_audioout.index = 0;
strcpy(&v4l2_audioout.name[0], "Soundtrack"); strcpy(&v4l2_audioout.name[0], "Soundtrack");
if (0 != copy_to_user((void __user *)arg, &v4l2_audioout, \ if (0 != copy_to_user((void __user *)arg, &v4l2_audioout, \
sizeof(struct v4l2_audioout))) sizeof(struct v4l2_audioout))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1209,8 +1286,10 @@ case VIDIOC_QUERYCTRL: { ...@@ -1209,8 +1286,10 @@ case VIDIOC_QUERYCTRL: {
JOM(8, "VIDIOC_QUERYCTRL\n"); JOM(8, "VIDIOC_QUERYCTRL\n");
if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg, \
sizeof(struct v4l2_queryctrl))) sizeof(struct v4l2_queryctrl))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
i1 = 0; i1 = 0;
while (0xFFFFFFFF != easycap_control[i1].id) { while (0xFFFFFFFF != easycap_control[i1].id) {
...@@ -1225,18 +1304,21 @@ case VIDIOC_QUERYCTRL: { ...@@ -1225,18 +1304,21 @@ case VIDIOC_QUERYCTRL: {
} }
if (0xFFFFFFFF == easycap_control[i1].id) { if (0xFFFFFFFF == easycap_control[i1].id) {
JOM(8, "%i=index: exhausts controls\n", i1); JOM(8, "%i=index: exhausts controls\n", i1);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
if (0 != copy_to_user((void __user *)arg, &v4l2_queryctrl, \ if (0 != copy_to_user((void __user *)arg, &v4l2_queryctrl, \
sizeof(struct v4l2_queryctrl))) sizeof(struct v4l2_queryctrl))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QUERYMENU: { case VIDIOC_QUERYMENU: {
JOM(8, "VIDIOC_QUERYMENU unsupported\n"); JOM(8, "VIDIOC_QUERYMENU unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_CTRL: { case VIDIOC_G_CTRL: {
...@@ -1246,11 +1328,13 @@ case VIDIOC_G_CTRL: { ...@@ -1246,11 +1328,13 @@ case VIDIOC_G_CTRL: {
pv4l2_control = kzalloc(sizeof(struct v4l2_control), GFP_KERNEL); pv4l2_control = kzalloc(sizeof(struct v4l2_control), GFP_KERNEL);
if (!pv4l2_control) { if (!pv4l2_control) {
SAM("ERROR: out of memory\n"); SAM("ERROR: out of memory\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOMEM; return -ENOMEM;
} }
if (0 != copy_from_user(pv4l2_control, (void __user *)arg, \ if (0 != copy_from_user(pv4l2_control, (void __user *)arg, \
sizeof(struct v4l2_control))) { sizeof(struct v4l2_control))) {
kfree(pv4l2_control); kfree(pv4l2_control);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
...@@ -1292,12 +1376,14 @@ case VIDIOC_G_CTRL: { ...@@ -1292,12 +1376,14 @@ case VIDIOC_G_CTRL: {
SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", \ SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", \
pv4l2_control->id); pv4l2_control->id);
kfree(pv4l2_control); kfree(pv4l2_control);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
if (0 != copy_to_user((void __user *)arg, pv4l2_control, \ if (0 != copy_to_user((void __user *)arg, pv4l2_control, \
sizeof(struct v4l2_control))) { sizeof(struct v4l2_control))) {
kfree(pv4l2_control); kfree(pv4l2_control);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
kfree(pv4l2_control); kfree(pv4l2_control);
...@@ -1316,8 +1402,10 @@ case VIDIOC_S_CTRL: ...@@ -1316,8 +1402,10 @@ case VIDIOC_S_CTRL:
JOM(8, "VIDIOC_S_CTRL\n"); JOM(8, "VIDIOC_S_CTRL\n");
if (0 != copy_from_user(&v4l2_control, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_control, (void __user *)arg, \
sizeof(struct v4l2_control))) sizeof(struct v4l2_control))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
switch (v4l2_control.id) { switch (v4l2_control.id) {
case V4L2_CID_BRIGHTNESS: { case V4L2_CID_BRIGHTNESS: {
...@@ -1366,14 +1454,16 @@ case VIDIOC_S_CTRL: ...@@ -1366,14 +1454,16 @@ case VIDIOC_S_CTRL:
default: { default: {
SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", \ SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", \
v4l2_control.id); v4l2_control.id);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_EXT_CTRLS: { case VIDIOC_S_EXT_CTRLS: {
JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n"); JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1384,8 +1474,10 @@ case VIDIOC_ENUM_FMT: { ...@@ -1384,8 +1474,10 @@ case VIDIOC_ENUM_FMT: {
JOM(8, "VIDIOC_ENUM_FMT\n"); JOM(8, "VIDIOC_ENUM_FMT\n");
if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg, \
sizeof(struct v4l2_fmtdesc))) sizeof(struct v4l2_fmtdesc))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
index = v4l2_fmtdesc.index; index = v4l2_fmtdesc.index;
memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc)); memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
...@@ -1438,12 +1530,15 @@ case VIDIOC_ENUM_FMT: { ...@@ -1438,12 +1530,15 @@ case VIDIOC_ENUM_FMT: {
} }
default: { default: {
JOM(8, "%i=index: exhausts formats\n", index); JOM(8, "%i=index: exhausts formats\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
if (0 != copy_to_user((void __user *)arg, &v4l2_fmtdesc, \ if (0 != copy_to_user((void __user *)arg, &v4l2_fmtdesc, \
sizeof(struct v4l2_fmtdesc))) sizeof(struct v4l2_fmtdesc))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1459,8 +1554,10 @@ case VIDIOC_ENUM_FRAMESIZES: { ...@@ -1459,8 +1554,10 @@ case VIDIOC_ENUM_FRAMESIZES: {
JOM(8, "VIDIOC_ENUM_FRAMESIZES\n"); JOM(8, "VIDIOC_ENUM_FRAMESIZES\n");
if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg, \
sizeof(struct v4l2_frmsizeenum))) sizeof(struct v4l2_frmsizeenum))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
index = v4l2_frmsizeenum.index; index = v4l2_frmsizeenum.index;
...@@ -1510,6 +1607,7 @@ case VIDIOC_ENUM_FRAMESIZES: { ...@@ -1510,6 +1607,7 @@ case VIDIOC_ENUM_FRAMESIZES: {
} }
default: { default: {
JOM(8, "%i=index: exhausts framesizes\n", index); JOM(8, "%i=index: exhausts framesizes\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
...@@ -1567,13 +1665,16 @@ case VIDIOC_ENUM_FRAMESIZES: { ...@@ -1567,13 +1665,16 @@ case VIDIOC_ENUM_FRAMESIZES: {
} }
default: { default: {
JOM(8, "%i=index: exhausts framesizes\n", index); JOM(8, "%i=index: exhausts framesizes\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
} }
if (0 != copy_to_user((void __user *)arg, &v4l2_frmsizeenum, \ if (0 != copy_to_user((void __user *)arg, &v4l2_frmsizeenum, \
sizeof(struct v4l2_frmsizeenum))) sizeof(struct v4l2_frmsizeenum))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1600,6 +1701,7 @@ case VIDIOC_ENUM_FRAMEINTERVALS: { ...@@ -1600,6 +1701,7 @@ case VIDIOC_ENUM_FRAMEINTERVALS: {
if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg, \
sizeof(struct v4l2_frmivalenum))) { sizeof(struct v4l2_frmivalenum))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
...@@ -1626,12 +1728,15 @@ case VIDIOC_ENUM_FRAMEINTERVALS: { ...@@ -1626,12 +1728,15 @@ case VIDIOC_ENUM_FRAMEINTERVALS: {
} }
default: { default: {
JOM(8, "%i=index: exhausts frameintervals\n", index); JOM(8, "%i=index: exhausts frameintervals\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
} }
if (0 != copy_to_user((void __user *)arg, &v4l2_frmivalenum, \ if (0 != copy_to_user((void __user *)arg, &v4l2_frmivalenum, \
sizeof(struct v4l2_frmivalenum))) sizeof(struct v4l2_frmivalenum))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1643,24 +1748,28 @@ case VIDIOC_G_FMT: { ...@@ -1643,24 +1748,28 @@ case VIDIOC_G_FMT: {
pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL); pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL);
if (!pv4l2_format) { if (!pv4l2_format) {
SAM("ERROR: out of memory\n"); SAM("ERROR: out of memory\n");
return -ENOMEM; mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOMEM;
} }
pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL); pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL);
if (!pv4l2_pix_format) { if (!pv4l2_pix_format) {
SAM("ERROR: out of memory\n"); SAM("ERROR: out of memory\n");
kfree(pv4l2_format); kfree(pv4l2_format);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOMEM; return -ENOMEM;
} }
if (0 != copy_from_user(pv4l2_format, (void __user *)arg, \ if (0 != copy_from_user(pv4l2_format, (void __user *)arg, \
sizeof(struct v4l2_format))) { sizeof(struct v4l2_format))) {
kfree(pv4l2_format); kfree(pv4l2_format);
kfree(pv4l2_pix_format); kfree(pv4l2_pix_format);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
kfree(pv4l2_format); kfree(pv4l2_format);
kfree(pv4l2_pix_format); kfree(pv4l2_pix_format);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
...@@ -1676,6 +1785,7 @@ case VIDIOC_G_FMT: { ...@@ -1676,6 +1785,7 @@ case VIDIOC_G_FMT: {
sizeof(struct v4l2_format))) { sizeof(struct v4l2_format))) {
kfree(pv4l2_format); kfree(pv4l2_format);
kfree(pv4l2_pix_format); kfree(pv4l2_pix_format);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
kfree(pv4l2_format); kfree(pv4l2_format);
...@@ -1699,8 +1809,10 @@ case VIDIOC_S_FMT: { ...@@ -1699,8 +1809,10 @@ case VIDIOC_S_FMT: {
} }
if (0 != copy_from_user(&v4l2_format, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_format, (void __user *)arg, \
sizeof(struct v4l2_format))) sizeof(struct v4l2_format))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
best_format = adjust_format(peasycap, \ best_format = adjust_format(peasycap, \
v4l2_format.fmt.pix.width, \ v4l2_format.fmt.pix.width, \
...@@ -1709,9 +1821,12 @@ case VIDIOC_S_FMT: { ...@@ -1709,9 +1821,12 @@ case VIDIOC_S_FMT: {
v4l2_format.fmt.pix.field, \ v4l2_format.fmt.pix.field, \
try); try);
if (0 > best_format) { if (0 > best_format) {
if (-EBUSY == best_format) if (-EBUSY == best_format) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EBUSY; return -EBUSY;
}
JOM(8, "WARNING: adjust_format() returned %i\n", best_format); JOM(8, "WARNING: adjust_format() returned %i\n", best_format);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOENT; return -ENOENT;
} }
/*...........................................................................*/ /*...........................................................................*/
...@@ -1723,8 +1838,10 @@ case VIDIOC_S_FMT: { ...@@ -1723,8 +1838,10 @@ case VIDIOC_S_FMT: {
JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]); JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
if (0 != copy_to_user((void __user *)arg, &v4l2_format, \ if (0 != copy_to_user((void __user *)arg, &v4l2_format, \
sizeof(struct v4l2_format))) sizeof(struct v4l2_format))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1734,8 +1851,10 @@ case VIDIOC_CROPCAP: { ...@@ -1734,8 +1851,10 @@ case VIDIOC_CROPCAP: {
JOM(8, "VIDIOC_CROPCAP\n"); JOM(8, "VIDIOC_CROPCAP\n");
if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg, \
sizeof(struct v4l2_cropcap))) sizeof(struct v4l2_cropcap))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
...@@ -1756,20 +1875,24 @@ case VIDIOC_CROPCAP: { ...@@ -1756,20 +1875,24 @@ case VIDIOC_CROPCAP: {
JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height); JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
if (0 != copy_to_user((void __user *)arg, &v4l2_cropcap, \ if (0 != copy_to_user((void __user *)arg, &v4l2_cropcap, \
sizeof(struct v4l2_cropcap))) sizeof(struct v4l2_cropcap))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_CROP: case VIDIOC_G_CROP:
case VIDIOC_S_CROP: { case VIDIOC_S_CROP: {
JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n"); JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QUERYSTD: { case VIDIOC_QUERYSTD: {
JOM(8, "VIDIOC_QUERYSTD: " \ JOM(8, "VIDIOC_QUERYSTD: " \
"EasyCAP is incapable of detecting standard\n"); "EasyCAP is incapable of detecting standard\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
break; break;
} }
...@@ -1790,8 +1913,10 @@ case VIDIOC_ENUMSTD: { ...@@ -1790,8 +1913,10 @@ case VIDIOC_ENUMSTD: {
JOM(8, "VIDIOC_ENUMSTD\n"); JOM(8, "VIDIOC_ENUMSTD\n");
if (0 != copy_from_user(&v4l2_standard, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_standard, (void __user *)arg, \
sizeof(struct v4l2_standard))) sizeof(struct v4l2_standard))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
index = v4l2_standard.index; index = v4l2_standard.index;
last3 = last2; last2 = last1; last1 = last0; last0 = index; last3 = last2; last2 = last1; last1 = last0; last0 = index;
...@@ -1811,6 +1936,7 @@ case VIDIOC_ENUMSTD: { ...@@ -1811,6 +1936,7 @@ case VIDIOC_ENUMSTD: {
} }
if (0xFFFF == peasycap_standard->mask) { if (0xFFFF == peasycap_standard->mask) {
JOM(8, "%i=index: exhausts standards\n", index); JOM(8, "%i=index: exhausts standards\n", index);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
JOM(8, "%i=index: %s\n", index, \ JOM(8, "%i=index: %s\n", index, \
...@@ -1821,8 +1947,10 @@ case VIDIOC_ENUMSTD: { ...@@ -1821,8 +1947,10 @@ case VIDIOC_ENUMSTD: {
v4l2_standard.index = index; v4l2_standard.index = index;
if (0 != copy_to_user((void __user *)arg, &v4l2_standard, \ if (0 != copy_to_user((void __user *)arg, &v4l2_standard, \
sizeof(struct v4l2_standard))) sizeof(struct v4l2_standard))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1835,12 +1963,15 @@ case VIDIOC_G_STD: { ...@@ -1835,12 +1963,15 @@ case VIDIOC_G_STD: {
if (0 > peasycap->standard_offset) { if (0 > peasycap->standard_offset) {
JOM(8, "%i=peasycap->standard_offset\n", \ JOM(8, "%i=peasycap->standard_offset\n", \
peasycap->standard_offset); peasycap->standard_offset);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EBUSY; return -EBUSY;
} }
if (0 != copy_from_user(&std_id, (void __user *)arg, \ if (0 != copy_from_user(&std_id, (void __user *)arg, \
sizeof(v4l2_std_id))) sizeof(v4l2_std_id))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
peasycap_standard = &easycap_standard[peasycap->standard_offset]; peasycap_standard = &easycap_standard[peasycap->standard_offset];
std_id = peasycap_standard->v4l2_standard.id; std_id = peasycap_standard->v4l2_standard.id;
...@@ -1849,8 +1980,10 @@ case VIDIOC_G_STD: { ...@@ -1849,8 +1980,10 @@ case VIDIOC_G_STD: {
&peasycap_standard->v4l2_standard.name[0]); &peasycap_standard->v4l2_standard.name[0]);
if (0 != copy_to_user((void __user *)arg, &std_id, \ if (0 != copy_to_user((void __user *)arg, &std_id, \
sizeof(v4l2_std_id))) sizeof(v4l2_std_id))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1861,8 +1994,10 @@ case VIDIOC_S_STD: { ...@@ -1861,8 +1994,10 @@ case VIDIOC_S_STD: {
JOM(8, "VIDIOC_S_STD\n"); JOM(8, "VIDIOC_S_STD\n");
if (0 != copy_from_user(&std_id, (void __user *)arg, \ if (0 != copy_from_user(&std_id, (void __user *)arg, \
sizeof(v4l2_std_id))) sizeof(v4l2_std_id))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
JOM(8, "User requests standard: 0x%08X%08X\n", \ JOM(8, "User requests standard: 0x%08X%08X\n", \
(int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32), \ (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32), \
...@@ -1871,6 +2006,7 @@ case VIDIOC_S_STD: { ...@@ -1871,6 +2006,7 @@ case VIDIOC_S_STD: {
rc = adjust_standard(peasycap, std_id); rc = adjust_standard(peasycap, std_id);
if (0 > rc) { if (0 > rc) {
JOM(8, "WARNING: adjust_standard() returned %i\n", rc); JOM(8, "WARNING: adjust_standard() returned %i\n", rc);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOENT; return -ENOENT;
} }
break; break;
...@@ -1883,13 +2019,19 @@ case VIDIOC_REQBUFS: { ...@@ -1883,13 +2019,19 @@ case VIDIOC_REQBUFS: {
JOM(8, "VIDIOC_REQBUFS\n"); JOM(8, "VIDIOC_REQBUFS\n");
if (0 != copy_from_user(&v4l2_requestbuffers, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_requestbuffers, (void __user *)arg, \
sizeof(struct v4l2_requestbuffers))) sizeof(struct v4l2_requestbuffers))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) }
if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
}
nbuffers = v4l2_requestbuffers.count; nbuffers = v4l2_requestbuffers.count;
JOM(8, " User requests %i buffers ...\n", nbuffers); JOM(8, " User requests %i buffers ...\n", nbuffers);
if (nbuffers < 2) if (nbuffers < 2)
...@@ -1907,8 +2049,10 @@ case VIDIOC_REQBUFS: { ...@@ -1907,8 +2049,10 @@ case VIDIOC_REQBUFS: {
peasycap->frame_buffer_many = nbuffers; peasycap->frame_buffer_many = nbuffers;
if (0 != copy_to_user((void __user *)arg, &v4l2_requestbuffers, \ if (0 != copy_to_user((void __user *)arg, &v4l2_requestbuffers, \
sizeof(struct v4l2_requestbuffers))) sizeof(struct v4l2_requestbuffers))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1921,15 +2065,20 @@ case VIDIOC_QUERYBUF: { ...@@ -1921,15 +2065,20 @@ case VIDIOC_QUERYBUF: {
if (peasycap->video_eof) { if (peasycap->video_eof) {
JOM(8, "returning -EIO because %i=video_eof\n", \ JOM(8, "returning -EIO because %i=video_eof\n", \
peasycap->video_eof); peasycap->video_eof);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EIO; return -EIO;
} }
if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
sizeof(struct v4l2_buffer))) sizeof(struct v4l2_buffer))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
}
index = v4l2_buffer.index; index = v4l2_buffer.index;
if (index < 0 || index >= peasycap->frame_buffer_many) if (index < 0 || index >= peasycap->frame_buffer_many)
return -EINVAL; return -EINVAL;
...@@ -1958,8 +2107,10 @@ case VIDIOC_QUERYBUF: { ...@@ -1958,8 +2107,10 @@ case VIDIOC_QUERYBUF: {
JOM(16, " %10i=length\n", v4l2_buffer.length); JOM(16, " %10i=length\n", v4l2_buffer.length);
if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \ if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
sizeof(struct v4l2_buffer))) sizeof(struct v4l2_buffer))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
break; break;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -1969,24 +2120,34 @@ case VIDIOC_QBUF: { ...@@ -1969,24 +2120,34 @@ case VIDIOC_QBUF: {
JOM(8, "VIDIOC_QBUF\n"); JOM(8, "VIDIOC_QBUF\n");
if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
sizeof(struct v4l2_buffer))) sizeof(struct v4l2_buffer))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) }
if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
}
if (v4l2_buffer.index < 0 || \ if (v4l2_buffer.index < 0 || \
(v4l2_buffer.index >= peasycap->frame_buffer_many)) (v4l2_buffer.index >= peasycap->frame_buffer_many)) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
}
v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED; v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
peasycap->done[v4l2_buffer.index] = 0; peasycap->done[v4l2_buffer.index] = 0;
peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED; peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED;
if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \ if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
sizeof(struct v4l2_buffer))) sizeof(struct v4l2_buffer))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
JOM(8, "..... user queueing frame buffer %i\n", \ JOM(8, "..... user queueing frame buffer %i\n", \
(int)v4l2_buffer.index); (int)v4l2_buffer.index);
...@@ -2017,15 +2178,20 @@ case VIDIOC_DQBUF: ...@@ -2017,15 +2178,20 @@ case VIDIOC_DQBUF:
JOM(8, "returning -EIO because " \ JOM(8, "returning -EIO because " \
"%i=video_idle %i=video_eof\n", \ "%i=video_idle %i=video_eof\n", \
peasycap->video_idle, peasycap->video_eof); peasycap->video_idle, peasycap->video_eof);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EIO; return -EIO;
} }
if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
sizeof(struct v4l2_buffer))) sizeof(struct v4l2_buffer))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
}
if (true == peasycap->offerfields) { if (true == peasycap->offerfields) {
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
...@@ -2047,6 +2213,7 @@ case VIDIOC_DQBUF: ...@@ -2047,6 +2213,7 @@ case VIDIOC_DQBUF:
if (!peasycap->video_isoc_streaming) { if (!peasycap->video_isoc_streaming) {
JOM(16, "returning -EIO because video urbs not streaming\n"); JOM(16, "returning -EIO because video urbs not streaming\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EIO; return -EIO;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
...@@ -2063,12 +2230,15 @@ case VIDIOC_DQBUF: ...@@ -2063,12 +2230,15 @@ case VIDIOC_DQBUF:
if (-EIO == rcdq) { if (-EIO == rcdq) {
JOM(8, "returning -EIO because " \ JOM(8, "returning -EIO because " \
"dqbuf() returned -EIO\n"); "dqbuf() returned -EIO\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EIO; return -EIO;
} }
} while (0 != rcdq); } while (0 != rcdq);
} else { } else {
if (peasycap->video_eof) if (peasycap->video_eof) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EIO; return -EIO;
}
} }
if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) { if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
SAM("ERROR: V4L2_BUF_FLAG_DONE != 0x%08X\n", \ SAM("ERROR: V4L2_BUF_FLAG_DONE != 0x%08X\n", \
...@@ -2155,8 +2325,10 @@ case VIDIOC_DQBUF: ...@@ -2155,8 +2325,10 @@ case VIDIOC_DQBUF:
JOM(16, " %10i=length\n", v4l2_buffer.length); JOM(16, " %10i=length\n", v4l2_buffer.length);
if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \ if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
sizeof(struct v4l2_buffer))) sizeof(struct v4l2_buffer))) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
}
input = peasycap->frame_buffer[peasycap->frame_read][0].input; input = peasycap->frame_buffer[peasycap->frame_read][0].input;
if (0x08 & input) { if (0x08 & input) {
...@@ -2187,6 +2359,7 @@ case VIDIOC_STREAMON: { ...@@ -2187,6 +2359,7 @@ case VIDIOC_STREAMON: {
peasycap->merit[i] = 0; peasycap->merit[i] = 0;
if ((struct usb_device *)NULL == peasycap->pusb_device) { if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n"); SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
submit_video_urbs(peasycap); submit_video_urbs(peasycap);
...@@ -2202,6 +2375,7 @@ case VIDIOC_STREAMOFF: { ...@@ -2202,6 +2375,7 @@ case VIDIOC_STREAMOFF: {
if ((struct usb_device *)NULL == peasycap->pusb_device) { if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n"); SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
...@@ -2227,16 +2401,19 @@ case VIDIOC_G_PARM: { ...@@ -2227,16 +2401,19 @@ case VIDIOC_G_PARM: {
pv4l2_streamparm = kzalloc(sizeof(struct v4l2_streamparm), GFP_KERNEL); pv4l2_streamparm = kzalloc(sizeof(struct v4l2_streamparm), GFP_KERNEL);
if (!pv4l2_streamparm) { if (!pv4l2_streamparm) {
SAM("ERROR: out of memory\n"); SAM("ERROR: out of memory\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOMEM; return -ENOMEM;
} }
if (0 != copy_from_user(pv4l2_streamparm, (void __user *)arg, \ if (0 != copy_from_user(pv4l2_streamparm, (void __user *)arg, \
sizeof(struct v4l2_streamparm))) { sizeof(struct v4l2_streamparm))) {
kfree(pv4l2_streamparm); kfree(pv4l2_streamparm);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
kfree(pv4l2_streamparm); kfree(pv4l2_streamparm);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
pv4l2_streamparm->parm.capture.capability = 0; pv4l2_streamparm->parm.capture.capability = 0;
...@@ -2262,6 +2439,7 @@ case VIDIOC_G_PARM: { ...@@ -2262,6 +2439,7 @@ case VIDIOC_G_PARM: {
if (0 != copy_to_user((void __user *)arg, pv4l2_streamparm, \ if (0 != copy_to_user((void __user *)arg, pv4l2_streamparm, \
sizeof(struct v4l2_streamparm))) { sizeof(struct v4l2_streamparm))) {
kfree(pv4l2_streamparm); kfree(pv4l2_streamparm);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EFAULT; return -EFAULT;
} }
kfree(pv4l2_streamparm); kfree(pv4l2_streamparm);
...@@ -2270,21 +2448,25 @@ case VIDIOC_G_PARM: { ...@@ -2270,21 +2448,25 @@ case VIDIOC_G_PARM: {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_PARM: { case VIDIOC_S_PARM: {
JOM(8, "VIDIOC_S_PARM unsupported\n"); JOM(8, "VIDIOC_S_PARM unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_AUDIO: { case VIDIOC_G_AUDIO: {
JOM(8, "VIDIOC_G_AUDIO unsupported\n"); JOM(8, "VIDIOC_G_AUDIO unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_AUDIO: { case VIDIOC_S_AUDIO: {
JOM(8, "VIDIOC_S_AUDIO unsupported\n"); JOM(8, "VIDIOC_S_AUDIO unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_TUNER: { case VIDIOC_S_TUNER: {
JOM(8, "VIDIOC_S_TUNER unsupported\n"); JOM(8, "VIDIOC_S_TUNER unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
...@@ -2292,44 +2474,50 @@ case VIDIOC_G_FBUF: ...@@ -2292,44 +2474,50 @@ case VIDIOC_G_FBUF:
case VIDIOC_S_FBUF: case VIDIOC_S_FBUF:
case VIDIOC_OVERLAY: { case VIDIOC_OVERLAY: {
JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n"); JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_TUNER: { case VIDIOC_G_TUNER: {
JOM(8, "VIDIOC_G_TUNER unsupported\n"); JOM(8, "VIDIOC_G_TUNER unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
case VIDIOC_G_FREQUENCY: case VIDIOC_G_FREQUENCY:
case VIDIOC_S_FREQUENCY: { case VIDIOC_S_FREQUENCY: {
JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n"); JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -EINVAL; return -EINVAL;
} }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
default: { default: {
JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd); JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
} }
mutex_unlock(&easycap_dongle[kd].mutex_video);
JOM(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
return 0; return 0;
} }
long easycap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file->f_dentry->d_inode;
long ret;
lock_kernel();
ret = easycap_ioctl_bkl(inode, file, cmd, arg);
unlock_kernel();
return ret;
}
/*****************************************************************************/ /*****************************************************************************/
static int easysnd_ioctl_bkl(struct inode *inode, struct file *file, /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
unsigned int cmd, unsigned long arg) #if ((defined(EASYCAP_IS_VIDEODEV_CLIENT)) || \
(defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)))
long
easysnd_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg) {
return (long)easysnd_ioctl((struct inode *)NULL, file, cmd, arg);
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT||EASYCAP_NEEDS_UNLOCKED_IOCTL*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*---------------------------------------------------------------------------*/
int
easysnd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{ {
struct easycap *peasycap; struct easycap *peasycap;
struct usb_device *p; struct usb_device *p;
int kd;
if (NULL == file) { if (NULL == file) {
SAY("ERROR: file is NULL\n"); SAY("ERROR: file is NULL\n");
...@@ -2345,6 +2533,48 @@ if (NULL == p) { ...@@ -2345,6 +2533,48 @@ if (NULL == p) {
SAM("ERROR: peasycap->pusb_device is NULL\n"); SAM("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT; return -EFAULT;
} }
kd = isdongle(peasycap);
if (0 <= kd && DONGLE_MANY > kd) {
if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_audio)) {
SAY("ERROR: cannot lock easycap_dongle[%i].mutex_audio\n", kd);
return -ERESTARTSYS;
}
JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
/*---------------------------------------------------------------------------*/
/*
* MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
* IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
* IF NECESSARY, BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
if (kd != isdongle(peasycap))
return -ERESTARTSYS;
if (NULL == file) {
SAY("ERROR: file is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
p = peasycap->pusb_device;
if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
} else {
/*---------------------------------------------------------------------------*/
/*
* IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
* ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
return -ERESTARTSYS;
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
switch (cmd) { switch (cmd) {
case SNDCTL_DSP_GETCAPS: { case SNDCTL_DSP_GETCAPS: {
...@@ -2363,8 +2593,10 @@ case SNDCTL_DSP_GETCAPS: { ...@@ -2363,8 +2593,10 @@ case SNDCTL_DSP_GETCAPS: {
caps = 0x04400000; caps = 0x04400000;
#endif /*UPSAMPLE*/ #endif /*UPSAMPLE*/
if (0 != copy_to_user((void __user *)arg, &caps, sizeof(int))) if (0 != copy_to_user((void __user *)arg, &caps, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case SNDCTL_DSP_GETFMTS: { case SNDCTL_DSP_GETFMTS: {
...@@ -2383,15 +2615,19 @@ case SNDCTL_DSP_GETFMTS: { ...@@ -2383,15 +2615,19 @@ case SNDCTL_DSP_GETFMTS: {
incoming = AFMT_S16_LE; incoming = AFMT_S16_LE;
#endif /*UPSAMPLE*/ #endif /*UPSAMPLE*/
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case SNDCTL_DSP_SETFMT: { case SNDCTL_DSP_SETFMT: {
int incoming, outgoing; int incoming, outgoing;
JOM(8, "SNDCTL_DSP_SETFMT\n"); JOM(8, "SNDCTL_DSP_SETFMT\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
JOM(8, "........... %i=incoming\n", incoming); JOM(8, "........... %i=incoming\n", incoming);
#if defined(UPSAMPLE) #if defined(UPSAMPLE)
...@@ -2411,8 +2647,11 @@ case SNDCTL_DSP_SETFMT: { ...@@ -2411,8 +2647,11 @@ case SNDCTL_DSP_SETFMT: {
JOM(8, " cf. %i=AFMT_S16_LE\n", AFMT_S16_LE); JOM(8, " cf. %i=AFMT_S16_LE\n", AFMT_S16_LE);
JOM(8, " cf. %i=AFMT_U8\n", AFMT_U8); JOM(8, " cf. %i=AFMT_U8\n", AFMT_U8);
if (0 != copy_to_user((void __user *)arg, &outgoing, \ if (0 != copy_to_user((void __user *)arg, &outgoing, \
sizeof(int))) sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EINVAL ; return -EINVAL ;
} }
break; break;
...@@ -2420,8 +2659,10 @@ case SNDCTL_DSP_SETFMT: { ...@@ -2420,8 +2659,10 @@ case SNDCTL_DSP_SETFMT: {
case SNDCTL_DSP_STEREO: { case SNDCTL_DSP_STEREO: {
int incoming; int incoming;
JOM(8, "SNDCTL_DSP_STEREO\n"); JOM(8, "SNDCTL_DSP_STEREO\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
JOM(8, "........... %i=incoming\n", incoming); JOM(8, "........... %i=incoming\n", incoming);
#if defined(UPSAMPLE) #if defined(UPSAMPLE)
...@@ -2436,15 +2677,19 @@ case SNDCTL_DSP_STEREO: { ...@@ -2436,15 +2677,19 @@ case SNDCTL_DSP_STEREO: {
incoming = 1; incoming = 1;
#endif /*UPSAMPLE*/ #endif /*UPSAMPLE*/
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case SNDCTL_DSP_SPEED: { case SNDCTL_DSP_SPEED: {
int incoming; int incoming;
JOM(8, "SNDCTL_DSP_SPEED\n"); JOM(8, "SNDCTL_DSP_SPEED\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
JOM(8, "........... %i=incoming\n", incoming); JOM(8, "........... %i=incoming\n", incoming);
#if defined(UPSAMPLE) #if defined(UPSAMPLE)
...@@ -2459,27 +2704,35 @@ case SNDCTL_DSP_SPEED: { ...@@ -2459,27 +2704,35 @@ case SNDCTL_DSP_SPEED: {
incoming = 48000; incoming = 48000;
#endif /*UPSAMPLE*/ #endif /*UPSAMPLE*/
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case SNDCTL_DSP_GETTRIGGER: { case SNDCTL_DSP_GETTRIGGER: {
int incoming; int incoming;
JOM(8, "SNDCTL_DSP_GETTRIGGER\n"); JOM(8, "SNDCTL_DSP_GETTRIGGER\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
JOM(8, "........... %i=incoming\n", incoming); JOM(8, "........... %i=incoming\n", incoming);
incoming = PCM_ENABLE_INPUT; incoming = PCM_ENABLE_INPUT;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case SNDCTL_DSP_SETTRIGGER: { case SNDCTL_DSP_SETTRIGGER: {
int incoming; int incoming;
JOM(8, "SNDCTL_DSP_SETTRIGGER\n"); JOM(8, "SNDCTL_DSP_SETTRIGGER\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
JOM(8, "........... %i=incoming\n", incoming); JOM(8, "........... %i=incoming\n", incoming);
JOM(8, "........... cf 0x%x=PCM_ENABLE_INPUT " \ JOM(8, "........... cf 0x%x=PCM_ENABLE_INPUT " \
"0x%x=PCM_ENABLE_OUTPUT\n", \ "0x%x=PCM_ENABLE_OUTPUT\n", \
...@@ -2493,12 +2746,16 @@ case SNDCTL_DSP_SETTRIGGER: { ...@@ -2493,12 +2746,16 @@ case SNDCTL_DSP_SETTRIGGER: {
case SNDCTL_DSP_GETBLKSIZE: { case SNDCTL_DSP_GETBLKSIZE: {
int incoming; int incoming;
JOM(8, "SNDCTL_DSP_GETBLKSIZE\n"); JOM(8, "SNDCTL_DSP_GETBLKSIZE\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
JOM(8, "........... %i=incoming\n", incoming); JOM(8, "........... %i=incoming\n", incoming);
incoming = peasycap->audio_bytes_per_fragment; incoming = peasycap->audio_bytes_per_fragment;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case SNDCTL_DSP_GETISPACE: { case SNDCTL_DSP_GETISPACE: {
...@@ -2512,8 +2769,10 @@ case SNDCTL_DSP_GETISPACE: { ...@@ -2512,8 +2769,10 @@ case SNDCTL_DSP_GETISPACE: {
audio_buf_info.fragstotal = 0; audio_buf_info.fragstotal = 0;
if (0 != copy_to_user((void __user *)arg, &audio_buf_info, \ if (0 != copy_to_user((void __user *)arg, &audio_buf_info, \
sizeof(int))) sizeof(int))) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
}
break; break;
} }
case 0x00005401: case 0x00005401:
...@@ -2523,25 +2782,16 @@ case 0x00005404: ...@@ -2523,25 +2782,16 @@ case 0x00005404:
case 0x00005405: case 0x00005405:
case 0x00005406: { case 0x00005406: {
JOM(8, "SNDCTL_TMR_...: 0x%08X unsupported\n", cmd); JOM(8, "SNDCTL_TMR_...: 0x%08X unsupported\n", cmd);
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
default: { default: {
JOM(8, "ERROR: unrecognized DSP IOCTL command: 0x%08X\n", cmd); JOM(8, "ERROR: unrecognized DSP IOCTL command: 0x%08X\n", cmd);
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
} }
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return 0; return 0;
} }
long easysnd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file->f_dentry->d_inode;
long ret;
lock_kernel();
ret = easysnd_ioctl_bkl(inode, file, cmd, arg);
unlock_kernel();
return ret;
}
/*****************************************************************************/ /*****************************************************************************/
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
*/ */
/****************************************************************************/ /****************************************************************************/
#include "easycap_debug.h"
#include "easycap.h" #include "easycap.h"
#include "easycap_debug.h"
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
const struct stk1160config { int reg; int set; } stk1160configPAL[256] = { const struct stk1160config { int reg; int set; } stk1160configPAL[256] = {
...@@ -248,6 +248,9 @@ int ...@@ -248,6 +248,9 @@ int
confirm_resolution(struct usb_device *p) confirm_resolution(struct usb_device *p)
{ {
__u8 get0, get1, get2, get3, get4, get5, get6, get7; __u8 get0, get1, get2, get3, get4, get5, get6, get7;
if (NULL == p)
return -ENODEV;
GET(p, 0x0110, &get0); GET(p, 0x0110, &get0);
GET(p, 0x0111, &get1); GET(p, 0x0111, &get1);
GET(p, 0x0112, &get2); GET(p, 0x0112, &get2);
...@@ -288,6 +291,8 @@ confirm_stream(struct usb_device *p) ...@@ -288,6 +291,8 @@ confirm_stream(struct usb_device *p)
__u16 get2; __u16 get2;
__u8 igot; __u8 igot;
if (NULL == p)
return -ENODEV;
GET(p, 0x0100, &igot); get2 = 0x80 & igot; GET(p, 0x0100, &igot); get2 = 0x80 & igot;
if (0x80 == get2) if (0x80 == get2)
JOT(8, "confirm_stream: OK\n"); JOT(8, "confirm_stream: OK\n");
...@@ -301,6 +306,8 @@ setup_stk(struct usb_device *p, bool ntsc) ...@@ -301,6 +306,8 @@ setup_stk(struct usb_device *p, bool ntsc)
{ {
int i0; int i0;
if (NULL == p)
return -ENODEV;
i0 = 0; i0 = 0;
if (true == ntsc) { if (true == ntsc) {
while (0xFFF != stk1160configNTSC[i0].reg) { while (0xFFF != stk1160configNTSC[i0].reg) {
...@@ -324,6 +331,8 @@ setup_saa(struct usb_device *p, bool ntsc) ...@@ -324,6 +331,8 @@ setup_saa(struct usb_device *p, bool ntsc)
{ {
int i0, ir; int i0, ir;
if (NULL == p)
return -ENODEV;
i0 = 0; i0 = 0;
if (true == ntsc) { if (true == ntsc) {
while (0xFF != saa7113configNTSC[i0].reg) { while (0xFF != saa7113configNTSC[i0].reg) {
...@@ -346,6 +355,8 @@ write_000(struct usb_device *p, __u16 set2, __u16 set0) ...@@ -346,6 +355,8 @@ write_000(struct usb_device *p, __u16 set2, __u16 set0)
{ {
__u8 igot0, igot2; __u8 igot0, igot2;
if (NULL == p)
return -ENODEV;
GET(p, 0x0002, &igot2); GET(p, 0x0002, &igot2);
GET(p, 0x0000, &igot0); GET(p, 0x0000, &igot0);
SET(p, 0x0002, set2); SET(p, 0x0002, set2);
...@@ -356,6 +367,8 @@ return 0; ...@@ -356,6 +367,8 @@ return 0;
int int
write_saa(struct usb_device *p, __u16 reg0, __u16 set0) write_saa(struct usb_device *p, __u16 reg0, __u16 set0)
{ {
if (NULL == p)
return -ENODEV;
SET(p, 0x200, 0x00); SET(p, 0x200, 0x00);
SET(p, 0x204, reg0); SET(p, 0x204, reg0);
SET(p, 0x205, set0); SET(p, 0x205, set0);
...@@ -379,6 +392,8 @@ __u8 igot; ...@@ -379,6 +392,8 @@ __u8 igot;
__u16 got502, got503; __u16 got502, got503;
__u16 set502, set503; __u16 set502, set503;
if (NULL == p)
return -ENODEV;
SET(p, 0x0504, reg0); SET(p, 0x0504, reg0);
SET(p, 0x0500, 0x008B); SET(p, 0x0500, 0x008B);
...@@ -414,6 +429,8 @@ read_vt(struct usb_device *p, __u16 reg0) ...@@ -414,6 +429,8 @@ read_vt(struct usb_device *p, __u16 reg0)
__u8 igot; __u8 igot;
__u16 got502, got503; __u16 got502, got503;
if (NULL == p)
return -ENODEV;
SET(p, 0x0504, reg0); SET(p, 0x0504, reg0);
SET(p, 0x0500, 0x008B); SET(p, 0x0500, 0x008B);
...@@ -433,6 +450,8 @@ return (got503 << 8) | got502; ...@@ -433,6 +450,8 @@ return (got503 << 8) | got502;
int int
write_300(struct usb_device *p) write_300(struct usb_device *p)
{ {
if (NULL == p)
return -ENODEV;
SET(p, 0x300, 0x0012); SET(p, 0x300, 0x0012);
SET(p, 0x350, 0x002D); SET(p, 0x350, 0x002D);
SET(p, 0x351, 0x0001); SET(p, 0x351, 0x0001);
...@@ -453,6 +472,8 @@ check_saa(struct usb_device *p, bool ntsc) ...@@ -453,6 +472,8 @@ check_saa(struct usb_device *p, bool ntsc)
{ {
int i0, ir, rc; int i0, ir, rc;
if (NULL == p)
return -ENODEV;
i0 = 0; i0 = 0;
rc = 0; rc = 0;
if (true == ntsc) { if (true == ntsc) {
...@@ -501,6 +522,8 @@ merit_saa(struct usb_device *p) ...@@ -501,6 +522,8 @@ merit_saa(struct usb_device *p)
{ {
int rc; int rc;
if (NULL == p)
return -ENODEV;
rc = read_saa(p, 0x1F); rc = read_saa(p, 0x1F);
if ((0 > rc) || (0x02 & rc)) if ((0 > rc) || (0x02 & rc))
return 1 ; return 1 ;
...@@ -521,6 +544,8 @@ const int max = 5, marktime = PATIENCE/5; ...@@ -521,6 +544,8 @@ const int max = 5, marktime = PATIENCE/5;
* 3 FOR NON-INTERLACED 60 Hz * 3 FOR NON-INTERLACED 60 Hz
*/ */
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
if (NULL == p)
return -ENODEV;
j = 0; j = 0;
while (max > j) { while (max > j) {
rc = read_saa(p, 0x1F); rc = read_saa(p, 0x1F);
...@@ -565,6 +590,8 @@ check_stk(struct usb_device *p, bool ntsc) ...@@ -565,6 +590,8 @@ check_stk(struct usb_device *p, bool ntsc)
{ {
int i0, ir; int i0, ir;
if (NULL == p)
return -ENODEV;
i0 = 0; i0 = 0;
if (true == ntsc) { if (true == ntsc) {
while (0xFFF != stk1160configNTSC[i0].reg) { while (0xFFF != stk1160configNTSC[i0].reg) {
...@@ -637,6 +664,8 @@ read_saa(struct usb_device *p, __u16 reg0) ...@@ -637,6 +664,8 @@ read_saa(struct usb_device *p, __u16 reg0)
{ {
__u8 igot; __u8 igot;
if (NULL == p)
return -ENODEV;
SET(p, 0x208, reg0); SET(p, 0x208, reg0);
SET(p, 0x200, 0x20); SET(p, 0x200, 0x20);
if (0 != wait_i2c(p)) if (0 != wait_i2c(p))
...@@ -651,6 +680,8 @@ read_stk(struct usb_device *p, __u32 reg0) ...@@ -651,6 +680,8 @@ read_stk(struct usb_device *p, __u32 reg0)
{ {
__u8 igot; __u8 igot;
if (NULL == p)
return -ENODEV;
igot = 0; igot = 0;
GET(p, reg0, &igot); GET(p, reg0, &igot);
return igot; return igot;
...@@ -679,6 +710,8 @@ select_input(struct usb_device *p, int input, int mode) ...@@ -679,6 +710,8 @@ select_input(struct usb_device *p, int input, int mode)
{ {
int ir; int ir;
if (NULL == p)
return -ENODEV;
stop_100(p); stop_100(p);
switch (input) { switch (input) {
case 0: case 0:
...@@ -781,6 +814,8 @@ set_resolution(struct usb_device *p, \ ...@@ -781,6 +814,8 @@ set_resolution(struct usb_device *p, \
{ {
__u16 u0x0111, u0x0113, u0x0115, u0x0117; __u16 u0x0111, u0x0113, u0x0115, u0x0117;
if (NULL == p)
return -ENODEV;
u0x0111 = ((0xFF00 & set0) >> 8); u0x0111 = ((0xFF00 & set0) >> 8);
u0x0113 = ((0xFF00 & set1) >> 8); u0x0113 = ((0xFF00 & set1) >> 8);
u0x0115 = ((0xFF00 & set2) >> 8); u0x0115 = ((0xFF00 & set2) >> 8);
...@@ -804,6 +839,8 @@ start_100(struct usb_device *p) ...@@ -804,6 +839,8 @@ start_100(struct usb_device *p)
__u16 get116, get117, get0; __u16 get116, get117, get0;
__u8 igot116, igot117, igot; __u8 igot116, igot117, igot;
if (NULL == p)
return -ENODEV;
GET(p, 0x0116, &igot116); GET(p, 0x0116, &igot116);
get116 = igot116; get116 = igot116;
GET(p, 0x0117, &igot117); GET(p, 0x0117, &igot117);
...@@ -827,6 +864,8 @@ stop_100(struct usb_device *p) ...@@ -827,6 +864,8 @@ stop_100(struct usb_device *p)
__u16 get0; __u16 get0;
__u8 igot; __u8 igot;
if (NULL == p)
return -ENODEV;
GET(p, 0x0100, &igot); GET(p, 0x0100, &igot);
get0 = igot; get0 = igot;
SET(p, 0x0100, (0x7F & get0)); SET(p, 0x0100, (0x7F & get0));
...@@ -846,6 +885,8 @@ __u8 igot; ...@@ -846,6 +885,8 @@ __u8 igot;
const int max = 2; const int max = 2;
int k; int k;
if (NULL == p)
return -ENODEV;
for (k = 0; k < max; k++) { for (k = 0; k < max; k++) {
GET(p, 0x0201, &igot); get0 = igot; GET(p, 0x0201, &igot); get0 = igot;
switch (get0) { switch (get0) {
...@@ -872,8 +913,7 @@ __u16 igot; ...@@ -872,8 +913,7 @@ __u16 igot;
int rc0, rc1; int rc0, rc1;
if (!pusb_device) if (!pusb_device)
return -EFAULT; return -ENODEV;
rc1 = 0; igot = 0; rc1 = 0; igot = 0;
rc0 = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \ rc0 = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \
(__u8)0x01, \ (__u8)0x01, \
...@@ -936,8 +976,7 @@ regget(struct usb_device *pusb_device, __u16 index, void *pvoid) ...@@ -936,8 +976,7 @@ regget(struct usb_device *pusb_device, __u16 index, void *pvoid)
int ir; int ir;
if (!pusb_device) if (!pusb_device)
return -EFAULT; return -ENODEV;
ir = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), \ ir = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), \
(__u8)0x00, \ (__u8)0x00, \
(__u8)(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), \ (__u8)(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), \
...@@ -952,6 +991,8 @@ return 0xFF & ir; ...@@ -952,6 +991,8 @@ return 0xFF & ir;
int int
wakeup_device(struct usb_device *pusb_device) wakeup_device(struct usb_device *pusb_device)
{ {
if (!pusb_device)
return -ENODEV;
return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \ return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \
(__u8)USB_REQ_SET_FEATURE, \ (__u8)USB_REQ_SET_FEATURE, \
(__u8)(USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE), \ (__u8)(USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE), \
...@@ -1056,6 +1097,8 @@ check_vt(struct usb_device *pusb_device) ...@@ -1056,6 +1097,8 @@ check_vt(struct usb_device *pusb_device)
{ {
int igot; int igot;
if (!pusb_device)
return -ENODEV;
igot = read_vt(pusb_device, 0x0002); igot = read_vt(pusb_device, 0x0002);
if (0 > igot) if (0 > igot)
SAY("ERROR: failed to read VT1612A register 0x02\n"); SAY("ERROR: failed to read VT1612A register 0x02\n");
...@@ -1128,7 +1171,7 @@ int igot; ...@@ -1128,7 +1171,7 @@ int igot;
__u8 u8; __u8 u8;
__u16 mute; __u16 mute;
if ((struct usb_device *)NULL == pusb_device) if (NULL == pusb_device)
return -ENODEV; return -ENODEV;
if (0 > loud) if (0 > loud)
loud = 0; loud = 0;
......
...@@ -45,11 +45,14 @@ module_param(gain, int, S_IRUGO | S_IWUSR); ...@@ -45,11 +45,14 @@ module_param(gain, int, S_IRUGO | S_IWUSR);
* IS CALLED SUCCESSIVELY FOR INTERFACES 0, 1, 2 AND THE POINTER peasycap * IS CALLED SUCCESSIVELY FOR INTERFACES 0, 1, 2 AND THE POINTER peasycap
* ALLOCATED DURING THE PROBING OF INTERFACE 0 MUST BE REMEMBERED WHEN * ALLOCATED DURING THE PROBING OF INTERFACE 0 MUST BE REMEMBERED WHEN
* PROBING INTERFACES 1 AND 2. * PROBING INTERFACES 1 AND 2.
*
* IOCTL LOCKING IS DONE AT MODULE LEVEL, NOT DEVICE LEVEL.
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
struct easycap *peasycap_dongle[DONGLE_MANY]; struct easycap_dongle easycap_dongle[DONGLE_MANY];
static int dongle_this; static int dongle_this;
static int dongle_done;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
...@@ -80,7 +83,11 @@ const struct file_operations easycap_fops = { ...@@ -80,7 +83,11 @@ const struct file_operations easycap_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = easycap_open, .open = easycap_open,
.release = easycap_release, .release = easycap_release,
.unlocked_ioctl = easycap_ioctl, #if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
.unlocked_ioctl = easycap_ioctl_noinode,
#else
.ioctl = easycap_ioctl,
#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
.poll = easycap_poll, .poll = easycap_poll,
.mmap = easycap_mmap, .mmap = easycap_mmap,
.llseek = no_llseek, .llseek = no_llseek,
...@@ -103,7 +110,11 @@ const struct v4l2_file_operations v4l2_fops = { ...@@ -103,7 +110,11 @@ const struct v4l2_file_operations v4l2_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = easycap_open_noinode, .open = easycap_open_noinode,
.release = easycap_release_noinode, .release = easycap_release_noinode,
.unlocked_ioctl = easycap_ioctl, #if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
.unlocked_ioctl = easycap_ioctl_noinode,
#else
.ioctl = easycap_ioctl,
#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
.poll = easycap_poll, .poll = easycap_poll,
.mmap = easycap_mmap, .mmap = easycap_mmap,
}; };
...@@ -120,7 +131,11 @@ const struct file_operations easysnd_fops = { ...@@ -120,7 +131,11 @@ const struct file_operations easysnd_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = easysnd_open, .open = easysnd_open,
.release = easysnd_release, .release = easysnd_release,
.unlocked_ioctl = easysnd_ioctl, #if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
.unlocked_ioctl = easysnd_ioctl_noinode,
#else
.ioctl = easysnd_ioctl,
#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
.read = easysnd_read, .read = easysnd_read,
.llseek = no_llseek, .llseek = no_llseek,
}; };
...@@ -139,10 +154,10 @@ int ...@@ -139,10 +154,10 @@ int
isdongle(struct easycap *peasycap) isdongle(struct easycap *peasycap)
{ {
int k; int k;
if ((struct easycap *)NULL == peasycap) if (NULL == peasycap)
return -2; return -2;
for (k = 0; k < DONGLE_MANY; k++) { for (k = 0; k < DONGLE_MANY; k++) {
if (peasycap_dongle[k] == peasycap) { if (easycap_dongle[k].peasycap == peasycap) {
peasycap->isdongle = k; peasycap->isdongle = k;
return k; return k;
} }
...@@ -196,11 +211,10 @@ if ((struct video_device *)NULL == pvideo_device) { ...@@ -196,11 +211,10 @@ if ((struct video_device *)NULL == pvideo_device) {
peasycap = (struct easycap *)video_get_drvdata(pvideo_device); peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/ #endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
if ((struct easycap *)NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
file->private_data = peasycap;
if (NULL == peasycap->pusb_device) { if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n"); SAM("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT; return -EFAULT;
...@@ -208,6 +222,7 @@ if (NULL == peasycap->pusb_device) { ...@@ -208,6 +222,7 @@ if (NULL == peasycap->pusb_device) {
JOM(16, "0x%08lX=peasycap->pusb_device\n", \ JOM(16, "0x%08lX=peasycap->pusb_device\n", \
(long int)peasycap->pusb_device); (long int)peasycap->pusb_device);
} }
file->private_data = peasycap;
rc = wakeup_device(peasycap->pusb_device); rc = wakeup_device(peasycap->pusb_device);
if (0 == rc) if (0 == rc)
JOM(8, "wakeup_device() OK\n"); JOM(8, "wakeup_device() OK\n");
...@@ -243,7 +258,7 @@ struct easycap_standard const *peasycap_standard; ...@@ -243,7 +258,7 @@ struct easycap_standard const *peasycap_standard;
int i, rc, input, rate; int i, rc, input, rate;
bool ntsc, other; bool ntsc, other;
if ((struct easycap *)NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
...@@ -251,7 +266,7 @@ input = peasycap->input; ...@@ -251,7 +266,7 @@ input = peasycap->input;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* IF THE SAA7113H HAS ALREADY ACQUIRED LOCK, USE ITS HARDWARE-DETECTED * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED
* FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR
* gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE
* A SWITCH BETWEEN PAL AND NTSC. * A SWITCH BETWEEN PAL AND NTSC.
...@@ -447,7 +462,7 @@ if (NULL == peasycap) { ...@@ -447,7 +462,7 @@ if (NULL == peasycap) {
} }
JOM(8, "%i=input sought\n", input); JOM(8, "%i=input sought\n", input);
if ((0 > input) &&(INPUT_MANY <= input)) if (0 > input && INPUT_MANY <= input)
return -ENOENT; return -ENOENT;
inputnow = peasycap->input; inputnow = peasycap->input;
if (input == inputnow) if (input == inputnow)
...@@ -581,7 +596,7 @@ if (input == peasycap->inputset[input].input) { ...@@ -581,7 +596,7 @@ if (input == peasycap->inputset[input].input) {
return -ENOENT; return -ENOENT;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
if ((struct usb_device *)NULL == peasycap->pusb_device) { if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n"); SAM("ERROR: peasycap->pusb_device is NULL\n");
return -ENODEV; return -ENODEV;
} }
...@@ -617,16 +632,16 @@ struct list_head *plist_head; ...@@ -617,16 +632,16 @@ struct list_head *plist_head;
int j, isbad, nospc, m, rc; int j, isbad, nospc, m, rc;
int isbuf; int isbuf;
if ((struct easycap *)NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
if ((struct list_head *)NULL == peasycap->purb_video_head) { if (NULL == peasycap->purb_video_head) {
SAY("ERROR: peasycap->urb_video_head uninitialized\n"); SAY("ERROR: peasycap->urb_video_head uninitialized\n");
return -EFAULT; return -EFAULT;
} }
if ((struct usb_device *)NULL == peasycap->pusb_device) { if (NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n"); SAY("ERROR: peasycap->pusb_device is NULL\n");
return -ENODEV; return -ENODEV;
} }
...@@ -768,7 +783,7 @@ int m; ...@@ -768,7 +783,7 @@ int m;
struct list_head *plist_head; struct list_head *plist_head;
struct data_urb *pdata_urb; struct data_urb *pdata_urb;
if ((struct easycap *)NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
...@@ -780,8 +795,8 @@ if (peasycap->video_isoc_streaming) { ...@@ -780,8 +795,8 @@ if (peasycap->video_isoc_streaming) {
list_for_each(plist_head, (peasycap->purb_video_head)) { list_for_each(plist_head, (peasycap->purb_video_head)) {
pdata_urb = list_entry(plist_head, struct data_urb, \ pdata_urb = list_entry(plist_head, struct data_urb, \
list_head); list_head);
if ((struct data_urb *)NULL != pdata_urb) { if (NULL != pdata_urb) {
if ((struct urb *)NULL != pdata_urb->purb) { if (NULL != pdata_urb->purb) {
usb_kill_urb(pdata_urb->purb); usb_kill_urb(pdata_urb->purb);
m++; m++;
} }
...@@ -864,15 +879,17 @@ return 0; ...@@ -864,15 +879,17 @@ return 0;
/*****************************************************************************/ /*****************************************************************************/
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/* /*
* THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect(). * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS
* BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED. * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect().
* peasycap->pusb_device IS NO LONGER VALID AND SHOULD HAVE BEEN SET TO NULL. *
* BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO
* peasycap->pusb_device IS NO LONGER VALID.
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
easycap_delete(struct kref *pkref) easycap_delete(struct kref *pkref)
{ {
int k, m, gone; int k, m, gone, kd;
int allocation_video_urb, allocation_video_page, allocation_video_struct; int allocation_video_urb, allocation_video_page, allocation_video_struct;
int allocation_audio_urb, allocation_audio_page, allocation_audio_struct; int allocation_audio_urb, allocation_audio_page, allocation_audio_struct;
int registered_video, registered_audio; int registered_video, registered_audio;
...@@ -883,10 +900,11 @@ struct list_head *plist_head, *plist_next; ...@@ -883,10 +900,11 @@ struct list_head *plist_head, *plist_next;
JOT(4, "\n"); JOT(4, "\n");
peasycap = container_of(pkref, struct easycap, kref); peasycap = container_of(pkref, struct easycap, kref);
if ((struct easycap *)NULL == peasycap) { if (NULL == peasycap) {
SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); SAM("ERROR: peasycap is NULL: cannot perform deletions\n");
return; return;
} }
kd = isdongle(peasycap);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* FREE VIDEO. * FREE VIDEO.
...@@ -925,8 +943,6 @@ if ((struct list_head *)NULL != peasycap->purb_video_head) { ...@@ -925,8 +943,6 @@ if ((struct list_head *)NULL != peasycap->purb_video_head) {
JOM(4, "%i video data_urb structures freed\n", m); JOM(4, "%i video data_urb structures freed\n", m);
JOM(4, "setting peasycap->purb_video_head=NULL\n"); JOM(4, "setting peasycap->purb_video_head=NULL\n");
peasycap->purb_video_head = (struct list_head *)NULL; peasycap->purb_video_head = (struct list_head *)NULL;
} else {
JOM(4, "peasycap->purb_video_head is NULL\n");
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
JOM(4, "freeing video isoc buffers.\n"); JOM(4, "freeing video isoc buffers.\n");
...@@ -1051,15 +1067,16 @@ allocation_audio_urb = peasycap->allocation_audio_urb; ...@@ -1051,15 +1067,16 @@ allocation_audio_urb = peasycap->allocation_audio_urb;
allocation_audio_page = peasycap->allocation_audio_page; allocation_audio_page = peasycap->allocation_audio_page;
allocation_audio_struct = peasycap->allocation_audio_struct; allocation_audio_struct = peasycap->allocation_audio_struct;
registered_audio = peasycap->registered_audio; registered_audio = peasycap->registered_audio;
m = 0;
if ((struct easycap *)NULL != peasycap) { kfree(peasycap);
kfree(peasycap); peasycap = (struct easycap *)NULL; if (0 <= kd && DONGLE_MANY > kd) {
easycap_dongle[kd].peasycap = (struct easycap *)NULL;
JOT(4, " null-->easycap_dongle[%i].peasycap\n", kd);
allocation_video_struct -= sizeof(struct easycap); allocation_video_struct -= sizeof(struct easycap);
m++; } else {
SAY("ERROR: cannot purge easycap_dongle[].peasycap");
} }
JOT(4, "%i easycap structure freed\n", m);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
SAY("%8i= video urbs after all deletions\n", allocation_video_urb); SAY("%8i= video urbs after all deletions\n", allocation_video_urb);
SAY("%8i= video pages after all deletions\n", allocation_video_page); SAY("%8i= video pages after all deletions\n", allocation_video_page);
SAY("%8i= video structs after all deletions\n", allocation_video_struct); SAY("%8i= video structs after all deletions\n", allocation_video_struct);
...@@ -1076,27 +1093,75 @@ return; ...@@ -1076,27 +1093,75 @@ return;
unsigned int easycap_poll(struct file *file, poll_table *wait) unsigned int easycap_poll(struct file *file, poll_table *wait)
{ {
struct easycap *peasycap; struct easycap *peasycap;
int rc, kd;
JOT(8, "\n"); JOT(8, "\n");
if (NULL == ((poll_table *)wait)) if (NULL == ((poll_table *)wait))
JOT(8, "WARNING: poll table pointer is NULL ... continuing\n"); JOT(8, "WARNING: poll table pointer is NULL ... continuing\n");
if (NULL == ((struct file *)file)) { if ((struct file *)NULL == file) {
SAY("ERROR: file pointer is NULL\n"); SAY("ERROR: file pointer is NULL\n");
return -EFAULT; return -ERESTARTSYS;
} }
peasycap = file->private_data; peasycap = file->private_data;
if (NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
if (NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
kd = isdongle(peasycap);
if (0 <= kd && DONGLE_MANY > kd) {
if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_video)) {
SAY("ERROR: cannot down easycap_dongle[%i].mutex_video\n", kd);
return -ERESTARTSYS;
}
JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
/*-------------------------------------------------------------------*/
/*
* MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER
* peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
* IF NECESSARY, BAIL OUT.
*/
/*-------------------------------------------------------------------*/
if (kd != isdongle(peasycap))
return -ERESTARTSYS;
if (NULL == file) {
SAY("ERROR: file is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ERESTARTSYS;
}
if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_video);
return -ERESTARTSYS;
}
} else
/*-------------------------------------------------------------------*/
/*
* IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap
* BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL
* HAVE FAILED. BAIL OUT.
*/
/*-------------------------------------------------------------------*/
return -ERESTARTSYS;
/*---------------------------------------------------------------------------*/
rc = easycap_dqbuf(peasycap, 0);
peasycap->polled = 1; peasycap->polled = 1;
mutex_unlock(&easycap_dongle[kd].mutex_video);
if (0 == easycap_dqbuf(peasycap, 0)) if (0 == rc)
return POLLIN | POLLRDNORM; return POLLIN | POLLRDNORM;
else else
return POLLERR; return POLLERR;
} }
/*****************************************************************************/ /*****************************************************************************/
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
...@@ -1115,6 +1180,10 @@ if (NULL == peasycap) { ...@@ -1115,6 +1180,10 @@ if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
if (NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
ifield = 0; ifield = 0;
JOM(8, "%i=ifield\n", ifield); JOM(8, "%i=ifield\n", ifield);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
...@@ -1122,9 +1191,9 @@ JOM(8, "%i=ifield\n", ifield); ...@@ -1122,9 +1191,9 @@ JOM(8, "%i=ifield\n", ifield);
* CHECK FOR LOST INPUT SIGNAL. * CHECK FOR LOST INPUT SIGNAL.
* *
* FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED. * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
* IF INPUT 0 IS PRESENT AND LOCKED, UNPLUGGING INPUT 4 DOES NOT RESULT IN * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT
* SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE IS FLYWHEELING * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE
* ON INPUT 0. THE UPSHOT IS: * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS:
* *
* INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK
* INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK
...@@ -1342,7 +1411,7 @@ int rc, bytesperpixel, multiplier, much, more, over, rump, caches, input; ...@@ -1342,7 +1411,7 @@ int rc, bytesperpixel, multiplier, much, more, over, rump, caches, input;
__u8 mask, margin; __u8 mask, margin;
bool odd, isuy, decimatepixel, offerfields, badinput; bool odd, isuy, decimatepixel, offerfields, badinput;
if ((struct easycap *)NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n"); SAY("ERROR: peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
...@@ -1447,13 +1516,11 @@ while (cz < wz) { ...@@ -1447,13 +1516,11 @@ while (cz < wz) {
much) / 2; much) / 2;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (1 < bytesperpixel) { if (1 < bytesperpixel) {
if ((rad * \ if (rad * 2 < much * bytesperpixel) {
2) < (much * \
bytesperpixel)) {
/* /*
** INJUDICIOUS ALTERATION OF THIS ** INJUDICIOUS ALTERATION OF THIS
** BLOCK WILL CAUSE BREAKAGE. ** STATEMENT BLOCK WILL CAUSE
** BEWARE. ** BREAKAGE. BEWARE.
**/ **/
rad2 = rad + bytesperpixel - 1; rad2 = rad + bytesperpixel - 1;
much = ((((2 * \ much = ((((2 * \
...@@ -1483,7 +1550,6 @@ while (cz < wz) { ...@@ -1483,7 +1550,6 @@ while (cz < wz) {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (rump) if (rump)
caches++; caches++;
if (true == badinput) { if (true == badinput) {
JOM(8, "ERROR: 0x%02X=->field_buffer" \ JOM(8, "ERROR: 0x%02X=->field_buffer" \
"[%i][%i].input, " \ "[%i][%i].input, " \
...@@ -1492,7 +1558,6 @@ while (cz < wz) { ...@@ -1492,7 +1558,6 @@ while (cz < wz) {
[kex][mex].input, kex, mex, \ [kex][mex].input, kex, mex, \
(0x08|peasycap->input)); (0x08|peasycap->input));
} }
rc = redaub(peasycap, pad, pex, much, more, \ rc = redaub(peasycap, pad, pex, much, more, \
mask, margin, isuy); mask, margin, isuy);
if (0 > rc) { if (0 > rc) {
...@@ -1575,12 +1640,11 @@ while (cz < wz) { ...@@ -1575,12 +1640,11 @@ while (cz < wz) {
much) / 4; much) / 4;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (1 < bytesperpixel) { if (1 < bytesperpixel) {
if ((rad * 4) < (much * \ if (rad * 4 < much * bytesperpixel) {
bytesperpixel)) {
/* /*
** INJUDICIOUS ALTERATION OF THIS ** INJUDICIOUS ALTERATION OF THIS
** BLOCK WILL CAUSE BREAKAGE. ** STATEMENT BLOCK WILL CAUSE
** BEWARE. ** BREAKAGE. BEWARE.
**/ **/
rad2 = rad + bytesperpixel - 1; rad2 = rad + bytesperpixel - 1;
much = ((((2 * rad2)/bytesperpixel)/2)\ much = ((((2 * rad2)/bytesperpixel)/2)\
...@@ -1620,7 +1684,6 @@ while (cz < wz) { ...@@ -1620,7 +1684,6 @@ while (cz < wz) {
[kex][mex].input, kex, mex, \ [kex][mex].input, kex, mex, \
(0x08|peasycap->input)); (0x08|peasycap->input));
} }
rc = redaub(peasycap, pad, pex, much, more, \ rc = redaub(peasycap, pad, pex, much, more, \
mask, margin, isuy); mask, margin, isuy);
if (0 > rc) { if (0 > rc) {
...@@ -3297,8 +3360,8 @@ return; ...@@ -3297,8 +3360,8 @@ return;
* FIXME * FIXME
* *
* *
* THIS FUNCTION ASSUMES THAT, ON EACH AND EVERY OCCASION THAT THE DEVICE IS * THIS FUNCTION ASSUMES THAT, ON EACH AND EVERY OCCASION THAT THE EasyCAP
* PHYSICALLY PLUGGED IN, INTERFACE 0 IS PROBED FIRST. * IS PHYSICALLY PLUGGED IN, INTERFACE 0 IS PROBED FIRST.
* IF THIS IS NOT TRUE, THERE IS THE POSSIBILITY OF AN Oops. * IF THIS IS NOT TRUE, THERE IS THE POSSIBILITY OF AN Oops.
* *
* THIS HAS NEVER BEEN A PROBLEM IN PRACTICE, BUT SOMETHING SEEMS WRONG HERE. * THIS HAS NEVER BEEN A PROBLEM IN PRACTICE, BUT SOMETHING SEEMS WRONG HERE.
...@@ -3338,6 +3401,16 @@ __s32 value; ...@@ -3338,6 +3401,16 @@ __s32 value;
struct easycap_format *peasycap_format; struct easycap_format *peasycap_format;
JOT(4, "\n"); JOT(4, "\n");
if (!dongle_done) {
dongle_done = 1;
for (k = 0; k < DONGLE_MANY; k++) {
easycap_dongle[k].peasycap = (struct easycap *)NULL;
mutex_init(&easycap_dongle[k].mutex_video);
mutex_init(&easycap_dongle[k].mutex_audio);
}
}
peasycap = (struct easycap *)NULL; peasycap = (struct easycap *)NULL;
if ((struct usb_interface *)NULL == pusb_interface) { if ((struct usb_interface *)NULL == pusb_interface) {
...@@ -3481,12 +3554,20 @@ if (0 == bInterfaceNumber) { ...@@ -3481,12 +3554,20 @@ if (0 == bInterfaceNumber) {
init_waitqueue_head(&peasycap->wq_audio); init_waitqueue_head(&peasycap->wq_audio);
for (dongle_this = 0; dongle_this < DONGLE_MANY; dongle_this++) { for (dongle_this = 0; dongle_this < DONGLE_MANY; dongle_this++) {
if ((struct easycap *)NULL == peasycap_dongle[dongle_this]) { if (NULL == easycap_dongle[dongle_this].peasycap) {
peasycap_dongle[dongle_this] = peasycap; if (0 == mutex_is_locked(&easycap_dongle\
JOM(8, "intf[%i]: peasycap-->easycap" \ [dongle_this].mutex_video)) {
if (0 == mutex_is_locked(&easycap_dongle\
[dongle_this].mutex_audio)) {
easycap_dongle\
[dongle_this].peasycap = \
peasycap;
JOM(8, "intf[%i]: peasycap-->easycap" \
"_dongle[%i].peasycap\n", \ "_dongle[%i].peasycap\n", \
bInterfaceNumber, dongle_this); bInterfaceNumber, dongle_this);
break; break;
}
}
} }
} }
if (DONGLE_MANY <= dongle_this) { if (DONGLE_MANY <= dongle_this) {
...@@ -3665,15 +3746,15 @@ if (0 == bInterfaceNumber) { ...@@ -3665,15 +3746,15 @@ if (0 == bInterfaceNumber) {
* FOR INTERFACES 1 AND 2 THE POINTER peasycap IS OBTAINED BY ASSUMING * FOR INTERFACES 1 AND 2 THE POINTER peasycap IS OBTAINED BY ASSUMING
* THAT dongle_this HAS NOT CHANGED SINCE INTERFACE 0 WAS PROBED. IF * THAT dongle_this HAS NOT CHANGED SINCE INTERFACE 0 WAS PROBED. IF
* THIS IS NOT THE CASE, FOR EXAMPLE WHEN TWO EASYCAPs ARE PLUGGED IN * THIS IS NOT THE CASE, FOR EXAMPLE WHEN TWO EASYCAPs ARE PLUGGED IN
* SIMULTANEOUSLY, THERE WILL BE VERY SERIOUS TROUBLE. * SIMULTANEOUSLY, THERE WILL BE SERIOUS TROUBLE.
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
if ((0 > dongle_this) || (DONGLE_MANY <= dongle_this)) { if ((0 > dongle_this) || (DONGLE_MANY <= dongle_this)) {
SAY("ERROR: bad dongle count\n"); SAY("ERROR: bad dongle count\n");
return -EFAULT; return -EFAULT;
} }
peasycap = peasycap_dongle[dongle_this]; peasycap = easycap_dongle[dongle_this].peasycap;
JOT(8, "intf[%i]: peasycap_dongle[%i]-->peasycap\n", \ JOT(8, "intf[%i]: easycap_dongle[%i].peasycap-->peasycap\n", \
bInterfaceNumber, dongle_this); bInterfaceNumber, dongle_this);
if ((struct easycap *)NULL == peasycap) { if ((struct easycap *)NULL == peasycap) {
...@@ -3721,6 +3802,7 @@ if ((USB_CLASS_VIDEO == bInterfaceClass) || \ ...@@ -3721,6 +3802,7 @@ if ((USB_CLASS_VIDEO == bInterfaceClass) || \
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
isokalt = 0; isokalt = 0;
for (i = 0; i < pusb_interface->num_altsetting; i++) { for (i = 0; i < pusb_interface->num_altsetting; i++) {
pusb_host_interface = &(pusb_interface->altsetting[i]); pusb_host_interface = &(pusb_interface->altsetting[i]);
if ((struct usb_host_interface *)NULL == pusb_host_interface) { if ((struct usb_host_interface *)NULL == pusb_host_interface) {
...@@ -4245,6 +4327,9 @@ case 0: { ...@@ -4245,6 +4327,9 @@ case 0: {
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* FIXME
*
*
* THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG: * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG:
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
...@@ -4587,9 +4672,8 @@ return 0; ...@@ -4587,9 +4672,8 @@ return 0;
/*****************************************************************************/ /*****************************************************************************/
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* WHEN THIS FUNCTION IS CALLED THE DEVICE HAS ALREADY BEEN PHYSICALLY * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY
* UNPLUGGED. * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID.
* HENCE peasycap->pusb_device IS NO LONGER VALID AND MUST BE SET TO NULL.
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
...@@ -4602,7 +4686,7 @@ struct easycap *peasycap; ...@@ -4602,7 +4686,7 @@ struct easycap *peasycap;
struct list_head *plist_head; struct list_head *plist_head;
struct data_urb *pdata_urb; struct data_urb *pdata_urb;
int minor, m; int minor, m, kd;
JOT(4, "\n"); JOT(4, "\n");
...@@ -4691,49 +4775,75 @@ case 2: { ...@@ -4691,49 +4775,75 @@ case 2: {
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/* /*
* DEREGISTER * DEREGISTER
*
* THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO
* IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN
* AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE.
*/ */
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
kd = isdongle(peasycap);
switch (bInterfaceNumber) { switch (bInterfaceNumber) {
case 0: { case 0: {
if (0 <= kd && DONGLE_MANY > kd) {
wake_up_interruptible(&peasycap->wq_video);
JOM(4, "about to lock easycap_dongle[%i].mutex_video\n", kd);
if (mutex_lock_interruptible(&easycap_dongle[kd].\
mutex_video)) {
SAY("ERROR: cannot lock easycap_dongle[%i]." \
"mutex_video\n", kd);
return;
}
JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
} else
SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
/*---------------------------------------------------------------------------*/
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT)) #if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
if ((struct easycap *)NULL == peasycap) { if ((struct easycap *)NULL == peasycap) {
SAM("ERROR: peasycap has become NULL\n"); SAM("ERROR: peasycap has become NULL\n");
} else { } else {
lock_kernel();
usb_deregister_dev(pusb_interface, &easycap_class); usb_deregister_dev(pusb_interface, &easycap_class);
(peasycap->registered_video)--; (peasycap->registered_video)--;
JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber); JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
unlock_kernel();
SAM("easycap detached from minor #%d\n", minor); SAM("easycap detached from minor #%d\n", minor);
} }
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else #else
if ((struct easycap *)NULL == peasycap) video_unregister_device(&peasycap->video_device);
SAM("ERROR: peasycap has become NULL\n"); JOM(4, "unregistered with videodev: %i=minor\n", \
else {
lock_kernel();
video_unregister_device(&peasycap->video_device);
(peasycap->registered_video)--;
unlock_kernel();
JOM(4, "unregistered with videodev: %i=minor\n", \
peasycap->video_device.minor); peasycap->video_device.minor);
} (peasycap->registered_video)--;
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/ #endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
if (0 <= kd && DONGLE_MANY > kd) {
mutex_unlock(&easycap_dongle[kd].mutex_video);
JOM(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
}
break; break;
} }
case 2: { case 2: {
lock_kernel(); if (0 <= kd && DONGLE_MANY > kd) {
wake_up_interruptible(&peasycap->wq_audio);
JOM(4, "about to lock easycap_dongle[%i].mutex_audio\n", kd);
if (mutex_lock_interruptible(&easycap_dongle[kd].\
mutex_audio)) {
SAY("ERROR: cannot lock easycap_dongle[%i]." \
"mutex_audio\n", kd);
return;
}
JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
} else
SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
usb_deregister_dev(pusb_interface, &easysnd_class); usb_deregister_dev(pusb_interface, &easysnd_class);
if ((struct easycap *)NULL != peasycap) (peasycap->registered_audio)--;
(peasycap->registered_audio)--;
JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber); JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
unlock_kernel();
SAM("easysnd detached from minor #%d\n", minor); SAM("easysnd detached from minor #%d\n", minor);
if (0 <= kd && DONGLE_MANY > kd) {
mutex_unlock(&easycap_dongle[kd].mutex_audio);
JOM(4, "unlocked easycap_dongle[%i].mutex_audio\n", kd);
}
break; break;
} }
default: default:
...@@ -4744,24 +4854,41 @@ case 2: { ...@@ -4744,24 +4854,41 @@ case 2: {
* CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap
*/ */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
if ((struct easycap *)NULL == peasycap) {
SAM("ERROR: peasycap has become NULL\n");
SAM("cannot call kref_put()\n");
SAM("ending unsuccessfully: may cause memory leak\n");
return;
}
if (!peasycap->kref.refcount.counter) { if (!peasycap->kref.refcount.counter) {
SAM("ERROR: peasycap->kref.refcount.counter is zero " \ SAM("ERROR: peasycap->kref.refcount.counter is zero "
"so cannot call kref_put()\n"); "so cannot call kref_put()\n");
SAM("ending unsuccessfully: may cause memory leak\n"); SAM("ending unsuccessfully: may cause memory leak\n");
return; return;
} }
JOM(4, "intf[%i]: kref_put() with %i=peasycap->kref.refcount.counter\n", \ if (0 <= kd && DONGLE_MANY > kd) {
JOM(4, "about to lock easycap_dongle[%i].mutex_video\n", kd);
if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_video)) {
SAY("ERROR: cannot down easycap_dongle[%i].mutex_video\n", kd);
SAM("ending unsuccessfully: may cause memory leak\n");
return;
}
JOM(4, "locked easycap_dongle[%i].mutex_video\n", kd);
JOM(4, "about to lock easycap_dongle[%i].mutex_audio\n", kd);
if (mutex_lock_interruptible(&easycap_dongle[kd].mutex_audio)) {
SAY("ERROR: cannot down easycap_dongle[%i].mutex_audio\n", kd);
mutex_unlock(&(easycap_dongle[kd].mutex_video));
JOM(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
SAM("ending unsuccessfully: may cause memory leak\n");
return;
}
JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
}
JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n", \
bInterfaceNumber, (int)peasycap->kref.refcount.counter); bInterfaceNumber, (int)peasycap->kref.refcount.counter);
kref_put(&peasycap->kref, easycap_delete); kref_put(&peasycap->kref, easycap_delete);
JOM(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber); JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
if (0 <= kd && DONGLE_MANY > kd) {
mutex_unlock(&(easycap_dongle[kd].mutex_audio));
JOT(4, "unlocked easycap_dongle[%i].mutex_audio\n", kd);
mutex_unlock(&easycap_dongle[kd].mutex_video);
JOT(4, "unlocked easycap_dongle[%i].mutex_video\n", kd);
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
JOM(4, "ends\n"); JOM(4, "ends\n");
return; return;
} }
......
...@@ -636,7 +636,7 @@ else ...@@ -636,7 +636,7 @@ else
if ((struct usb_device *)NULL == peasycap->pusb_device) { if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device has become NULL\n"); SAM("ERROR: peasycap->pusb_device has become NULL\n");
return -EFAULT; return -ENODEV;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
if ((struct usb_device *)NULL == peasycap->pusb_device) { if ((struct usb_device *)NULL == peasycap->pusb_device) {
...@@ -695,7 +695,7 @@ long long int above, below, mean; ...@@ -695,7 +695,7 @@ long long int above, below, mean;
struct signed_div_result sdr; struct signed_div_result sdr;
unsigned char *p0; unsigned char *p0;
long int kount1, more, rc, l0, lm; long int kount1, more, rc, l0, lm;
int fragment; int fragment, kd;
struct easycap *peasycap; struct easycap *peasycap;
struct data_buffer *pdata_buffer; struct data_buffer *pdata_buffer;
size_t szret; size_t szret;
...@@ -713,20 +713,76 @@ size_t szret; ...@@ -713,20 +713,76 @@ size_t szret;
JOT(8, "===== easysnd_read(): kount=%i, *poff=%i\n", (int)kount, (int)(*poff)); JOT(8, "===== easysnd_read(): kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
peasycap = (struct easycap *)(file->private_data); if (NULL == file) {
SAY("ERROR: file is NULL\n");
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) { if (NULL == peasycap) {
SAY("ERROR in easysnd_read(): peasycap is NULL\n"); SAY("ERROR in easysnd_read(): peasycap is NULL\n");
return -EFAULT; return -EFAULT;
} }
if (NULL == peasycap->pusb_device) {
SAY("ERROR in easysnd_read(): peasycap->pusb_device is NULL\n");
return -EFAULT;
}
kd = isdongle(peasycap);
if (0 <= kd && DONGLE_MANY > kd) {
if (mutex_lock_interruptible(&(easycap_dongle[kd].mutex_audio))) {
SAY("ERROR: cannot lock easycap_dongle[%i].mutex_audio\n", kd);
return -ERESTARTSYS;
}
JOM(4, "locked easycap_dongle[%i].mutex_audio\n", kd);
/*---------------------------------------------------------------------------*/
/*
* MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
* IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
* IF NECESSARY, BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
if (kd != isdongle(peasycap))
return -ERESTARTSYS;
if (NULL == file) {
SAY("ERROR: file is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
} else {
/*---------------------------------------------------------------------------*/
/*
* IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
* ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
return -ERESTARTSYS;
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
if (file->f_flags & O_NONBLOCK)
JOT(16, "NONBLOCK kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
else
JOT(8, "BLOCKING kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
if ((0 > peasycap->audio_read) || \ if ((0 > peasycap->audio_read) || \
(peasycap->audio_buffer_page_many <= peasycap->audio_read)) { (peasycap->audio_buffer_page_many <= peasycap->audio_read)) {
SAM("ERROR: peasycap->audio_read out of range\n"); SAM("ERROR: peasycap->audio_read out of range\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read]; pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) { if ((struct data_buffer *)NULL == pdata_buffer) {
SAM("ERROR: pdata_buffer is NULL\n"); SAM("ERROR: pdata_buffer is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
JOM(12, "before wait, %i=frag read %i=frag fill\n", \ JOM(12, "before wait, %i=frag read %i=frag fill\n", \
...@@ -738,6 +794,7 @@ while ((fragment == (peasycap->audio_fill / \ ...@@ -738,6 +794,7 @@ while ((fragment == (peasycap->audio_fill / \
(0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) { (0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) {
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
JOM(16, "returning -EAGAIN as instructed\n"); JOM(16, "returning -EAGAIN as instructed\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EAGAIN; return -EAGAIN;
} }
rc = wait_event_interruptible(peasycap->wq_audio, \ rc = wait_event_interruptible(peasycap->wq_audio, \
...@@ -747,21 +804,25 @@ while ((fragment == (peasycap->audio_fill / \ ...@@ -747,21 +804,25 @@ while ((fragment == (peasycap->audio_fill / \
(0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))))); (0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo))))));
if (0 != rc) { if (0 != rc) {
SAM("aborted by signal\n"); SAM("aborted by signal\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS; return -ERESTARTSYS;
} }
if (peasycap->audio_eof) { if (peasycap->audio_eof) {
JOM(8, "returning 0 because %i=audio_eof\n", \ JOM(8, "returning 0 because %i=audio_eof\n", \
peasycap->audio_eof); peasycap->audio_eof);
kill_audio_urbs(peasycap); kill_audio_urbs(peasycap);
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return 0; return 0;
} }
if (peasycap->audio_idle) { if (peasycap->audio_idle) {
JOM(16, "returning 0 because %i=audio_idle\n", \ JOM(16, "returning 0 because %i=audio_idle\n", \
peasycap->audio_idle); peasycap->audio_idle);
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return 0; return 0;
} }
if (!peasycap->audio_isoc_streaming) { if (!peasycap->audio_isoc_streaming) {
JOM(16, "returning 0 because audio urbs not streaming\n"); JOM(16, "returning 0 because audio urbs not streaming\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return 0; return 0;
} }
} }
...@@ -773,15 +834,18 @@ while (fragment == (peasycap->audio_read / \ ...@@ -773,15 +834,18 @@ while (fragment == (peasycap->audio_read / \
peasycap->audio_pages_per_fragment)) { peasycap->audio_pages_per_fragment)) {
if (NULL == pdata_buffer->pgo) { if (NULL == pdata_buffer->pgo) {
SAM("ERROR: pdata_buffer->pgo is NULL\n"); SAM("ERROR: pdata_buffer->pgo is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
if (NULL == pdata_buffer->pto) { if (NULL == pdata_buffer->pto) {
SAM("ERROR: pdata_buffer->pto is NULL\n"); SAM("ERROR: pdata_buffer->pto is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo); kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
if (0 > kount1) { if (0 > kount1) {
SAM("easysnd_read: MISTAKE: kount1 is negative\n"); SAM("easysnd_read: MISTAKE: kount1 is negative\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -ERESTARTSYS; return -ERESTARTSYS;
} }
if (!kount1) { if (!kount1) {
...@@ -799,19 +863,23 @@ while (fragment == (peasycap->audio_read / \ ...@@ -799,19 +863,23 @@ while (fragment == (peasycap->audio_read / \
(peasycap->audio_buffer_page_many <= \ (peasycap->audio_buffer_page_many <= \
peasycap->audio_read)) { peasycap->audio_read)) {
SAM("ERROR: peasycap->audio_read out of range\n"); SAM("ERROR: peasycap->audio_read out of range\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read]; pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) { if ((struct data_buffer *)NULL == pdata_buffer) {
SAM("ERROR: pdata_buffer is NULL\n"); SAM("ERROR: pdata_buffer is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
if (NULL == pdata_buffer->pgo) { if (NULL == pdata_buffer->pgo) {
SAM("ERROR: pdata_buffer->pgo is NULL\n"); SAM("ERROR: pdata_buffer->pgo is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
if (NULL == pdata_buffer->pto) { if (NULL == pdata_buffer->pto) {
SAM("ERROR: pdata_buffer->pto is NULL\n"); SAM("ERROR: pdata_buffer->pto is NULL\n");
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo); kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
...@@ -840,6 +908,7 @@ while (fragment == (peasycap->audio_read / \ ...@@ -840,6 +908,7 @@ while (fragment == (peasycap->audio_read / \
rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more); rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more);
if (0 != rc) { if (0 != rc) {
SAM("ERROR: copy_to_user() returned %li\n", rc); SAM("ERROR: copy_to_user() returned %li\n", rc);
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return -EFAULT; return -EFAULT;
} }
*poff += (loff_t)more; *poff += (loff_t)more;
...@@ -902,6 +971,7 @@ JOM(8, "audio streaming at %lli bytes/second\n", sdr.quotient); ...@@ -902,6 +971,7 @@ JOM(8, "audio streaming at %lli bytes/second\n", sdr.quotient);
peasycap->dnbydt = sdr.quotient; peasycap->dnbydt = sdr.quotient;
JOM(8, "returning %li\n", (long int)szret); JOM(8, "returning %li\n", (long int)szret);
mutex_unlock(&easycap_dongle[kd].mutex_audio);
return szret; return szret;
} }
/*****************************************************************************/ /*****************************************************************************/
......
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