Commit f78d92c9 authored by Dean Anderson's avatar Dean Anderson Committed by Mauro Carvalho Chehab

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: default avatarDean Anderson <dean@sensoray.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 655b8408
...@@ -185,6 +185,7 @@ struct s2255_dmaqueue { ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
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