V4L/DVB (8490): s2255drv Sensoray 2255 driver fixes
This patch fixes timer issues in driver disconnect. It also removes the restriction of one user per channel at a time. Thanks to Oliver Neukum and Mauro Chehab for finding these issues. Locking of video stream partly based on saa7134 driver. Signed-off-by: Dean Anderson <dean@sensoray.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
655b840855
commit
f78d92c9ff
|
@ -185,6 +185,7 @@ struct s2255_dmaqueue {
|
||||||
#define S2255_FW_LOADED_DSPWAIT 1
|
#define S2255_FW_LOADED_DSPWAIT 1
|
||||||
#define S2255_FW_SUCCESS 2
|
#define S2255_FW_SUCCESS 2
|
||||||
#define S2255_FW_FAILED 3
|
#define S2255_FW_FAILED 3
|
||||||
|
#define S2255_FW_DISCONNECTING 4
|
||||||
|
|
||||||
struct s2255_fw {
|
struct s2255_fw {
|
||||||
int fw_loaded;
|
int fw_loaded;
|
||||||
|
@ -264,7 +265,6 @@ struct s2255_buffer {
|
||||||
|
|
||||||
struct s2255_fh {
|
struct s2255_fh {
|
||||||
struct s2255_dev *dev;
|
struct s2255_dev *dev;
|
||||||
unsigned int resources;
|
|
||||||
const struct s2255_fmt *fmt;
|
const struct s2255_fmt *fmt;
|
||||||
unsigned int width;
|
unsigned int width;
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
|
@ -274,14 +274,9 @@ struct s2255_fh {
|
||||||
/* mode below is the desired mode.
|
/* mode below is the desired mode.
|
||||||
mode in s2255_dev is the current mode that was last set */
|
mode in s2255_dev is the current mode that was last set */
|
||||||
struct s2255_mode mode;
|
struct s2255_mode mode;
|
||||||
|
int resources[MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: fixme S2255_MAX_USERS. Do not limit open driver handles.
|
|
||||||
* Limit V4L to one stream at a time.
|
|
||||||
*/
|
|
||||||
#define S2255_MAX_USERS 1
|
|
||||||
|
|
||||||
#define CUR_USB_FWVER 774 /* current cypress EEPROM firmware version */
|
#define CUR_USB_FWVER 774 /* current cypress EEPROM firmware version */
|
||||||
#define S2255_MAJOR_VERSION 1
|
#define S2255_MAJOR_VERSION 1
|
||||||
#define S2255_MINOR_VERSION 13
|
#define S2255_MINOR_VERSION 13
|
||||||
|
@ -477,10 +472,9 @@ static void s2255_timer(unsigned long user_data)
|
||||||
dprintk(100, "s2255 timer\n");
|
dprintk(100, "s2255 timer\n");
|
||||||
if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
|
if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
|
||||||
printk(KERN_ERR "s2255: can't submit urb\n");
|
printk(KERN_ERR "s2255: can't submit urb\n");
|
||||||
if (data->fw) {
|
atomic_set(&data->fw_state, S2255_FW_FAILED);
|
||||||
release_firmware(data->fw);
|
/* wake up anything waiting for the firmware */
|
||||||
data->fw = NULL;
|
wake_up(&data->wait_fw);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,13 +504,18 @@ static void s2255_fwchunk_complete(struct urb *urb)
|
||||||
struct usb_device *udev = urb->dev;
|
struct usb_device *udev = urb->dev;
|
||||||
int len;
|
int len;
|
||||||
dprintk(100, "udev %p urb %p", udev, urb);
|
dprintk(100, "udev %p urb %p", udev, urb);
|
||||||
/* TODO: fixme. reflect change in status */
|
|
||||||
if (urb->status) {
|
if (urb->status) {
|
||||||
dev_err(&udev->dev, "URB failed with status %d", urb->status);
|
dev_err(&udev->dev, "URB failed with status %d", urb->status);
|
||||||
|
atomic_set(&data->fw_state, S2255_FW_FAILED);
|
||||||
|
/* wake up anything waiting for the firmware */
|
||||||
|
wake_up(&data->wait_fw);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data->fw_urb == NULL) {
|
if (data->fw_urb == NULL) {
|
||||||
dev_err(&udev->dev, "early disconncect\n");
|
dev_err(&udev->dev, "s2255 disconnected\n");
|
||||||
|
atomic_set(&data->fw_state, S2255_FW_FAILED);
|
||||||
|
/* wake up anything waiting for the firmware */
|
||||||
|
wake_up(&data->wait_fw);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#define CHUNK_SIZE 512
|
#define CHUNK_SIZE 512
|
||||||
|
@ -790,7 +789,8 @@ static int res_get(struct s2255_dev *dev, struct s2255_fh *fh)
|
||||||
}
|
}
|
||||||
/* it's free, grab it */
|
/* it's free, grab it */
|
||||||
dev->resources[fh->channel] = 1;
|
dev->resources[fh->channel] = 1;
|
||||||
dprintk(1, "res: get\n");
|
fh->resources[fh->channel] = 1;
|
||||||
|
dprintk(1, "s2255: res: get\n");
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -800,9 +800,18 @@ static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh)
|
||||||
return dev->resources[fh->channel];
|
return dev->resources[fh->channel];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int res_check(struct s2255_fh *fh)
|
||||||
|
{
|
||||||
|
return fh->resources[fh->channel];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void res_free(struct s2255_dev *dev, struct s2255_fh *fh)
|
static void res_free(struct s2255_dev *dev, struct s2255_fh *fh)
|
||||||
{
|
{
|
||||||
|
mutex_lock(&dev->lock);
|
||||||
dev->resources[fh->channel] = 0;
|
dev->resources[fh->channel] = 0;
|
||||||
|
fh->resources[fh->channel] = 0;
|
||||||
|
mutex_unlock(&dev->lock);
|
||||||
dprintk(1, "res: put\n");
|
dprintk(1, "res: put\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1233,7 +1242,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res_get(dev, fh)) {
|
if (!res_get(dev, fh)) {
|
||||||
dev_err(&dev->udev->dev, "res get busy\n");
|
dev_err(&dev->udev->dev, "s2255: stream busy\n");
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1289,8 +1298,10 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
|
||||||
}
|
}
|
||||||
s2255_stop_acquire(dev, fh->channel);
|
s2255_stop_acquire(dev, fh->channel);
|
||||||
res = videobuf_streamoff(&fh->vb_vidq);
|
res = videobuf_streamoff(&fh->vb_vidq);
|
||||||
|
if (res < 0)
|
||||||
|
return res;
|
||||||
res_free(dev, fh);
|
res_free(dev, fh);
|
||||||
return res;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
|
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
|
||||||
|
@ -1463,12 +1474,7 @@ static int s2255_open(struct inode *inode, struct file *file)
|
||||||
mutex_lock(&dev->open_lock);
|
mutex_lock(&dev->open_lock);
|
||||||
|
|
||||||
dev->users[cur_channel]++;
|
dev->users[cur_channel]++;
|
||||||
if (dev->users[cur_channel] > S2255_MAX_USERS) {
|
dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]);
|
||||||
dev->users[cur_channel]--;
|
|
||||||
mutex_unlock(&dev->open_lock);
|
|
||||||
printk(KERN_INFO "s2255drv: too many open handles!\n");
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) {
|
if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) {
|
||||||
err("2255 firmware load failed. retrying.\n");
|
err("2255 firmware load failed. retrying.\n");
|
||||||
|
@ -1479,7 +1485,8 @@ static int s2255_open(struct inode *inode, struct file *file)
|
||||||
msecs_to_jiffies(S2255_LOAD_TIMEOUT));
|
msecs_to_jiffies(S2255_LOAD_TIMEOUT));
|
||||||
if (atomic_read(&dev->fw_data->fw_state)
|
if (atomic_read(&dev->fw_data->fw_state)
|
||||||
!= S2255_FW_SUCCESS) {
|
!= S2255_FW_SUCCESS) {
|
||||||
printk(KERN_INFO "2255 FW load failed after 2 tries\n");
|
printk(KERN_INFO "2255 FW load failed.\n");
|
||||||
|
dev->users[cur_channel]--;
|
||||||
mutex_unlock(&dev->open_lock);
|
mutex_unlock(&dev->open_lock);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
@ -1495,6 +1502,7 @@ static int s2255_open(struct inode *inode, struct file *file)
|
||||||
!= S2255_FW_SUCCESS) {
|
!= S2255_FW_SUCCESS) {
|
||||||
printk(KERN_INFO "2255 firmware not loaded"
|
printk(KERN_INFO "2255 firmware not loaded"
|
||||||
"try again\n");
|
"try again\n");
|
||||||
|
dev->users[cur_channel]--;
|
||||||
mutex_unlock(&dev->open_lock);
|
mutex_unlock(&dev->open_lock);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
@ -1503,6 +1511,7 @@ static int s2255_open(struct inode *inode, struct file *file)
|
||||||
/* allocate + initialize per filehandle data */
|
/* allocate + initialize per filehandle data */
|
||||||
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
|
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
|
||||||
if (NULL == fh) {
|
if (NULL == fh) {
|
||||||
|
dev->users[cur_channel]--;
|
||||||
mutex_unlock(&dev->open_lock);
|
mutex_unlock(&dev->open_lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -1562,44 +1571,48 @@ static void s2255_destroy(struct kref *kref)
|
||||||
printk(KERN_ERR "s2255drv: kref problem\n");
|
printk(KERN_ERR "s2255drv: kref problem\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up any firmware load waiting (only done in .open,
|
||||||
|
* which holds the open_lock mutex)
|
||||||
|
*/
|
||||||
|
atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
|
||||||
|
wake_up(&dev->fw_data->wait_fw);
|
||||||
|
|
||||||
/* prevent s2255_disconnect from racing s2255_open */
|
/* prevent s2255_disconnect from racing s2255_open */
|
||||||
mutex_lock(&dev->open_lock);
|
mutex_lock(&dev->open_lock);
|
||||||
s2255_exit_v4l(dev);
|
s2255_exit_v4l(dev);
|
||||||
/* device unregistered so no longer possible to open. open_mutex
|
/*
|
||||||
can be unlocked */
|
* device unregistered so no longer possible to open. open_mutex
|
||||||
|
* can be unlocked and timers deleted afterwards.
|
||||||
|
*/
|
||||||
mutex_unlock(&dev->open_lock);
|
mutex_unlock(&dev->open_lock);
|
||||||
|
|
||||||
/* board shutdown stops the read pipe if it is running */
|
/* board shutdown stops the read pipe if it is running */
|
||||||
s2255_board_shutdown(dev);
|
s2255_board_shutdown(dev);
|
||||||
|
|
||||||
/* make sure firmware still not trying to load */
|
/* make sure firmware still not trying to load */
|
||||||
|
del_timer(&dev->timer); /* only started in .probe and .open */
|
||||||
|
|
||||||
if (dev->fw_data->fw_urb) {
|
if (dev->fw_data->fw_urb) {
|
||||||
dprintk(2, "kill fw_urb\n");
|
dprintk(2, "kill fw_urb\n");
|
||||||
usb_kill_urb(dev->fw_data->fw_urb);
|
usb_kill_urb(dev->fw_data->fw_urb);
|
||||||
usb_free_urb(dev->fw_data->fw_urb);
|
usb_free_urb(dev->fw_data->fw_urb);
|
||||||
dev->fw_data->fw_urb = NULL;
|
dev->fw_data->fw_urb = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: fixme(above, below): potentially leaving timers alive.
|
* delete the dsp_wait timer, which sets the firmware
|
||||||
* do not ignore timeout below if
|
* state on completion. This is done before fw_data
|
||||||
* it occurs.
|
* is freed below.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* make sure we aren't waiting for the DSP */
|
del_timer(&dev->fw_data->dsp_wait); /* only started in .open */
|
||||||
if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_LOADED_DSPWAIT) {
|
|
||||||
/* if we are, wait for the wakeup for fw_success or timeout */
|
|
||||||
wait_event_timeout(dev->fw_data->wait_fw,
|
|
||||||
(atomic_read(&dev->fw_data->fw_state)
|
|
||||||
== S2255_FW_SUCCESS),
|
|
||||||
msecs_to_jiffies(S2255_LOAD_TIMEOUT));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->fw_data) {
|
if (dev->fw_data->fw)
|
||||||
if (dev->fw_data->fw)
|
release_firmware(dev->fw_data->fw);
|
||||||
release_firmware(dev->fw_data->fw);
|
kfree(dev->fw_data->pfw_data);
|
||||||
kfree(dev->fw_data->pfw_data);
|
kfree(dev->fw_data);
|
||||||
kfree(dev->fw_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_put_dev(dev->udev);
|
usb_put_dev(dev->udev);
|
||||||
dprintk(1, "%s", __func__);
|
dprintk(1, "%s", __func__);
|
||||||
|
@ -1616,17 +1629,23 @@ static int s2255_close(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
mutex_lock(&dev->open_lock);
|
mutex_lock(&dev->open_lock);
|
||||||
|
|
||||||
if (dev->b_acquire[fh->channel])
|
/* turn off stream */
|
||||||
s2255_stop_acquire(dev, fh->channel);
|
if (res_check(fh)) {
|
||||||
res_free(dev, fh);
|
if (dev->b_acquire[fh->channel])
|
||||||
|
s2255_stop_acquire(dev, fh->channel);
|
||||||
|
videobuf_streamoff(&fh->vb_vidq);
|
||||||
|
res_free(dev, fh);
|
||||||
|
}
|
||||||
|
|
||||||
videobuf_mmap_free(&fh->vb_vidq);
|
videobuf_mmap_free(&fh->vb_vidq);
|
||||||
kfree(fh);
|
|
||||||
dev->users[fh->channel]--;
|
dev->users[fh->channel]--;
|
||||||
|
|
||||||
mutex_unlock(&dev->open_lock);
|
mutex_unlock(&dev->open_lock);
|
||||||
|
|
||||||
kref_put(&dev->kref, s2255_destroy);
|
kref_put(&dev->kref, s2255_destroy);
|
||||||
dprintk(1, "s2255: close called (minor=%d, users=%d)\n",
|
dprintk(1, "s2255: close called (minor=%d, users=%d)\n",
|
||||||
minor, dev->users[fh->channel]);
|
minor, dev->users[fh->channel]);
|
||||||
|
kfree(fh);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue