Skip to content

Commit

Permalink
V4L/DVB (8490): s2255drv Sensoray 2255 driver fixes
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dean Anderson authored and Mauro Carvalho Chehab committed Jul 26, 2008
1 parent 655b840 commit f78d92c
Showing 1 changed file with 65 additions and 46 deletions.
111 changes: 65 additions & 46 deletions drivers/media/video/s2255drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ struct s2255_dmaqueue {
#define S2255_FW_LOADED_DSPWAIT 1
#define S2255_FW_SUCCESS 2
#define S2255_FW_FAILED 3
#define S2255_FW_DISCONNECTING 4

struct s2255_fw {
int fw_loaded;
Expand Down Expand Up @@ -264,7 +265,6 @@ struct s2255_buffer {

struct s2255_fh {
struct s2255_dev *dev;
unsigned int resources;
const struct s2255_fmt *fmt;
unsigned int width;
unsigned int height;
Expand All @@ -274,14 +274,9 @@ struct s2255_fh {
/* mode below is the desired mode.
mode in s2255_dev is the current mode that was last set */
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 S2255_MAJOR_VERSION 1
#define S2255_MINOR_VERSION 13
Expand Down Expand Up @@ -477,10 +472,9 @@ static void s2255_timer(unsigned long user_data)
dprintk(100, "s2255 timer\n");
if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
printk(KERN_ERR "s2255: can't submit urb\n");
if (data->fw) {
release_firmware(data->fw);
data->fw = NULL;
}
atomic_set(&data->fw_state, S2255_FW_FAILED);
/* wake up anything waiting for the firmware */
wake_up(&data->wait_fw);
return;
}
}
Expand Down Expand Up @@ -510,13 +504,18 @@ static void s2255_fwchunk_complete(struct urb *urb)
struct usb_device *udev = urb->dev;
int len;
dprintk(100, "udev %p urb %p", udev, urb);
/* TODO: fixme. reflect change in status */
if (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;
}
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;
}
#define CHUNK_SIZE 512
Expand Down Expand Up @@ -790,7 +789,8 @@ static int res_get(struct s2255_dev *dev, struct s2255_fh *fh)
}
/* it's free, grab it */
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);
return 1;
}
Expand All @@ -800,9 +800,18 @@ static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh)
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)
{
mutex_lock(&dev->lock);
dev->resources[fh->channel] = 0;
fh->resources[fh->channel] = 0;
mutex_unlock(&dev->lock);
dprintk(1, "res: put\n");
}

Expand Down Expand Up @@ -1233,7 +1242,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
}

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;
}

Expand Down Expand Up @@ -1289,8 +1298,10 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
}
s2255_stop_acquire(dev, fh->channel);
res = videobuf_streamoff(&fh->vb_vidq);
if (res < 0)
return res;
res_free(dev, fh);
return res;
return 0;
}

static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
Expand Down Expand Up @@ -1463,12 +1474,7 @@ static int s2255_open(struct inode *inode, struct file *file)
mutex_lock(&dev->open_lock);

dev->users[cur_channel]++;
if (dev->users[cur_channel] > S2255_MAX_USERS) {
dev->users[cur_channel]--;
mutex_unlock(&dev->open_lock);
printk(KERN_INFO "s2255drv: too many open handles!\n");
return -EBUSY;
}
dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]);

if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) {
err("2255 firmware load failed. retrying.\n");
Expand All @@ -1479,7 +1485,8 @@ static int s2255_open(struct inode *inode, struct file *file)
msecs_to_jiffies(S2255_LOAD_TIMEOUT));
if (atomic_read(&dev->fw_data->fw_state)
!= 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);
return -EFAULT;
}
Expand All @@ -1495,6 +1502,7 @@ static int s2255_open(struct inode *inode, struct file *file)
!= S2255_FW_SUCCESS) {
printk(KERN_INFO "2255 firmware not loaded"
"try again\n");
dev->users[cur_channel]--;
mutex_unlock(&dev->open_lock);
return -EBUSY;
}
Expand All @@ -1503,6 +1511,7 @@ static int s2255_open(struct inode *inode, struct file *file)
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
if (NULL == fh) {
dev->users[cur_channel]--;
mutex_unlock(&dev->open_lock);
return -ENOMEM;
}
Expand Down Expand Up @@ -1562,44 +1571,48 @@ static void s2255_destroy(struct kref *kref)
printk(KERN_ERR "s2255drv: kref problem\n");
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 */
mutex_lock(&dev->open_lock);
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);

/* board shutdown stops the read pipe if it is running */
s2255_board_shutdown(dev);

/* make sure firmware still not trying to load */
del_timer(&dev->timer); /* only started in .probe and .open */

if (dev->fw_data->fw_urb) {
dprintk(2, "kill fw_urb\n");
usb_kill_urb(dev->fw_data->fw_urb);
usb_free_urb(dev->fw_data->fw_urb);
dev->fw_data->fw_urb = NULL;
}

/*
* TODO: fixme(above, below): potentially leaving timers alive.
* do not ignore timeout below if
* it occurs.
* delete the dsp_wait timer, which sets the firmware
* state on completion. This is done before fw_data
* is freed below.
*/

/* make sure we aren't waiting for the DSP */
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));
}
del_timer(&dev->fw_data->dsp_wait); /* only started in .open */

if (dev->fw_data) {
if (dev->fw_data->fw)
release_firmware(dev->fw_data->fw);
kfree(dev->fw_data->pfw_data);
kfree(dev->fw_data);
}
if (dev->fw_data->fw)
release_firmware(dev->fw_data->fw);
kfree(dev->fw_data->pfw_data);
kfree(dev->fw_data);

usb_put_dev(dev->udev);
dprintk(1, "%s", __func__);
Expand All @@ -1616,17 +1629,23 @@ static int s2255_close(struct inode *inode, struct file *file)

mutex_lock(&dev->open_lock);

if (dev->b_acquire[fh->channel])
s2255_stop_acquire(dev, fh->channel);
res_free(dev, fh);
/* turn off stream */
if (res_check(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);
kfree(fh);
dev->users[fh->channel]--;

mutex_unlock(&dev->open_lock);

kref_put(&dev->kref, s2255_destroy);
dprintk(1, "s2255: close called (minor=%d, users=%d)\n",
minor, dev->users[fh->channel]);
kfree(fh);
return 0;
}

Expand Down

0 comments on commit f78d92c

Please sign in to comment.