diff options
author | Mike Isely <isely@pobox.com> | 2006-06-08 00:01:19 -0500 |
---|---|---|
committer | Mike Isely <isely@pobox.com> | 2006-06-08 00:01:19 -0500 |
commit | c0e1a168c8871c030502120b0f02fda348e14db7 (patch) | |
tree | 33862aa726a22df4a5c1ff3c955eb30c255379a8 | |
parent | c9afdbe77a95a51cc16765495700227b3870e4cf (diff) | |
download | mediapointer-dvb-s2-c0e1a168c8871c030502120b0f02fda348e14db7.tar.gz mediapointer-dvb-s2-c0e1a168c8871c030502120b0f02fda348e14db7.tar.bz2 |
Remove last bits of V4L1 dependant code
From: Mike Isely <isely@pobox.com>
With the removal of the V4L1 API, we also lose the functionality
provided by the video_get_drvdata() and video_set_drvdata() functions.
These functions had made it possible for a driver to stuff a pointer
to its own context inside the V4L device handle context - a useful
ability. However now that's going away, so the driver is on its own
to find its context when called into from a more generic context
(e.g. the open() entrypoint). The changes in this patch implement a
global context mapping array, an array index back-reference per
instance, and a new global mutex to protect the entire thing. The
array is indexed by the minor device id of the V4L device inode. All
this fun is present to work around what used to be roughly 4 lines of
code. Sigh...
Signed-off-by: Mike Isely <isely@pobox.com>
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 71 |
1 files changed, 63 insertions, 8 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index f4284f927..78cdd01c8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -22,8 +22,6 @@ #include "compat.h" #include <linux/kernel.h> -#include <linux/videodev.h> - #include "pvrusb2-context.h" #include "pvrusb2-hdw.h" #include "pvrusb2.h" @@ -37,10 +35,21 @@ struct pvr2_v4l2_dev; struct pvr2_v4l2_fh; struct pvr2_v4l2; +/* V4L no longer provide the ability to set / get a private context pointer + (i.e. video_get_drvdata / video_set_drvdata), which means we have to + concoct our own context locating mechanism. Supposedly this is intended + to simplify driver implementation. It's not clear to me how that can + possibly be true. Our solution here is to maintain a lookup table of + our context instances, indexed by the minor device number of the V4L + device. See pvr2_v4l2_open() for some implications of this approach. */ +static struct pvr2_v4l2_dev *devices[256]; +static DEFINE_MUTEX(device_lock); + struct pvr2_v4l2_dev { struct pvr2_v4l2 *v4lp; struct video_device *vdev; struct pvr2_context_stream *stream; + int ctxt_idx; enum pvr2_config config; }; @@ -641,6 +650,12 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) pvr2_trace(PVR2_TRACE_INIT, "unregistering device video%d [%s]", dip->vdev->minor,pvr2_config_get_name(dip->config)); + if (dip->ctxt_idx >= 0) { + mutex_lock(&device_lock); + devices[dip->ctxt_idx] = NULL; + dip->ctxt_idx = -1; + mutex_unlock(&device_lock); + } video_unregister_device(dip->vdev); } @@ -727,12 +742,43 @@ int pvr2_v4l2_release(struct inode *inode, struct file *file) int pvr2_v4l2_open(struct inode *inode, struct file *file) { - struct video_device *vdev = video_devdata(file); - struct pvr2_v4l2_dev *dip = - (struct pvr2_v4l2_dev *)video_get_drvdata(vdev); + struct pvr2_v4l2_dev *dip = 0; /* Our own context pointer */ struct pvr2_v4l2_fh *fhp; - struct pvr2_v4l2 *vp = dip->v4lp; - struct pvr2_hdw *hdw = vp->channel.hdw; + struct pvr2_v4l2 *vp; + struct pvr2_hdw *hdw; + + mutex_lock(&device_lock); + /* MCI 7-Jun-2006 Even though we're just doing what amounts to an + atomic read of the device mapping array here, we still need the + mutex. The problem is that there is a tiny race possible when + we register the device. We can't update the device mapping + array until after the device has been registered, owing to the + fact that we can't know the minor device number until after the + registration succeeds. And if another thread tries to open the + device in the window of time after registration but before the + map is updated, then it will get back an erroneous null pointer + and the open will result in a spurious failure. The only way to + prevent that is to (a) be inside the mutex here before we access + the array, and (b) cover the entire registration process later + on with this same mutex. Thus if we get inside the mutex here, + then we can be assured that the registration process actually + completed correctly. This is an unhappy complication from the + use of global data in a driver that lives in a preemptible + environment. It sure would be nice if the video device itself + had a means for storing and retrieving a local context pointer. + Oh wait. It did. But now it's gone. Silly me. */ + { + unsigned int midx = iminor(file->f_dentry->d_inode); + if (midx < sizeof(devices)/sizeof(devices[0])) { + dip = devices[midx]; + } + } + mutex_unlock(&device_lock); + + if (!dip) return -ENODEV; /* Should be impossible but I'm paranoid */ + + vp = dip->v4lp; + hdw = vp->channel.hdw; pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open"); @@ -980,7 +1026,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->vdev->dev = &usbdev->dev; #endif dip->vdev->release = video_device_release; - video_set_drvdata(dip->vdev,dip); + mutex_lock(&device_lock); mindevnum = -1; unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); @@ -995,6 +1041,14 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, "registered device video%d [%s]", dip->vdev->minor,pvr2_config_get_name(dip->config)); } + + if ((dip->vdev->minor < sizeof(devices)/sizeof(devices[0])) && + (devices[dip->vdev->minor] == NULL)) { + dip->ctxt_idx = dip->vdev->minor; + devices[dip->ctxt_idx] = dip; + } + mutex_unlock(&device_lock); + pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, dip->vdev->minor); } @@ -1007,6 +1061,7 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) vp = kmalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; memset(vp,0,sizeof(*vp)); + vp->video_dev.ctxt_idx = -1; pvr2_channel_init(&vp->channel,mnp); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); |