diff options
Diffstat (limited to 'drivers/media/video/pvrusb2')
-rw-r--r-- | drivers/media/video/pvrusb2/Kconfig | 13 | ||||
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-hdw.c | 37 | ||||
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-hdw.h | 8 | ||||
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c | 4 | ||||
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 125 |
5 files changed, 102 insertions, 85 deletions
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index a52171ef6134c..5645c9318890d 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -18,8 +18,8 @@ config VIDEO_PVRUSB2_29XXX select VIDEO_SAA711X select VIDEO_MSP3400 ---help--- - This option enables support for WinTV-PVR USB2 devices whose - model number is of the form "29xxx" (leading prefix of "29" + This option enables support for WinTV-PVR USB2 devices whose + model number is of the form "29xxx" (leading prefix of "29" followed by 3 digits). To see if you may need this option, examine the white sticker on the underside of your device. @@ -37,14 +37,9 @@ config VIDEO_PVRUSB2_24XXX form "24xxx" (leading prefix of "24" followed by 3 digits). To see if you may need this option, examine the white sticker on the underside of your device. Enabling this - option will not harm support for older devices, however it - is a separate option because of the experimental nature of - this new feature. + option will not harm support for older devices. - If you are in doubt, say N. - - Note: This feature is _very_ experimental. You have been - warned. + If you are in doubt, say Y. config VIDEO_PVRUSB2_SYSFS bool "pvrusb2 sysfs support (EXPERIMENTAL)" diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 88604365777c3..3d8cd0daf6a93 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/firmware.h> #include <linux/videodev2.h> +#include <media/v4l2-common.h> #include <asm/semaphore.h> #include "pvrusb2.h" #include "pvrusb2-std.h" @@ -3131,6 +3132,42 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) } +int pvr2_hdw_register_access(struct pvr2_hdw *hdw, + u32 chip_id,unsigned long reg_id, + int setFl,u32 *val_ptr) +{ +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct list_head *item; + struct pvr2_i2c_client *cp; + struct v4l2_register req; + int stat = 0; + int okFl = 0; + + req.i2c_id = chip_id; + req.reg = reg_id; + if (setFl) req.val = *val_ptr; + mutex_lock(&hdw->i2c_list_lock); do { + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + if (cp->client->driver->id != chip_id) continue; + stat = pvr2_i2c_client_cmd( + cp,(setFl ? VIDIOC_INT_S_REGISTER : + VIDIOC_INT_G_REGISTER),&req); + if (!setFl) *val_ptr = req.val; + okFl = !0; + break; + } + } while (0); mutex_unlock(&hdw->i2c_list_lock); + if (okFl) { + return stat; + } + return -EINVAL; +#else + return -ENOSYS; +#endif +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index fd931b5da490b..29979bb2a7689 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -211,6 +211,14 @@ int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *); /* Store the v4l minor device number */ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,int); +/* Direct read/write access to chip's registers: + chip_id - unique id of chip (e.g. I2C_DRIVERD_xxxx) + reg_id - register number to access + setFl - true to set the register, false to read it + val_ptr - storage location for source / result. */ +int pvr2_hdw_register_access(struct pvr2_hdw *, + u32 chip_id,unsigned long reg_id, + int setFl,u32 *val_ptr); /* The following entry points are all lower level things you normally don't want to worry about. */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index ed3e8105292a2..05121666b9ba5 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -19,6 +19,7 @@ * */ +#include <linux/kernel.h> #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" @@ -89,7 +90,8 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx) { - if (idx >= sizeof(ops)/sizeof(ops[0])) return 0; + if (idx >= ARRAY_SIZE(ops)) + return NULL; return ops[idx]; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 3608c2f81df91..97e974d9b9c34 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -29,27 +29,17 @@ #include "pvrusb2-v4l2.h" #include "pvrusb2-ioread.h" #include <linux/videodev2.h> +#include <media/v4l2-dev.h> #include <media/v4l2-common.h> 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 video_device devbase; /* MUST be first! */ struct pvr2_v4l2 *v4lp; - struct video_device *vdev; struct pvr2_context_stream *stream; - int ctxt_idx; enum pvr2_config config; }; @@ -74,7 +64,7 @@ struct pvr2_v4l2 { struct v4l2_prio_state prio; /* streams */ - struct pvr2_v4l2_dev video_dev; + struct pvr2_v4l2_dev *vdev; }; static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; @@ -671,6 +661,20 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, ret = 0; break; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + case VIDIOC_INT_S_REGISTER: + { + u32 val; + struct v4l2_register *req = (struct v4l2_register *)arg; + if (cmd == VIDIOC_INT_S_REGISTER) val = req->val; + ret = pvr2_hdw_register_access( + hdw,req->i2c_id,req->reg, + cmd == VIDIOC_INT_S_REGISTER,&val); + if (cmd == VIDIOC_INT_G_REGISTER) req->val = val; + break; + } +#endif default : ret = v4l_compat_translate_ioctl(inode,file,cmd, @@ -704,21 +708,22 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { printk(KERN_INFO "pvrusb2: unregistering device video%d [%s]\n", - 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); + dip->devbase.minor,pvr2_config_get_name(dip->config)); + + /* Paranoia */ + dip->v4lp = 0; + dip->stream = 0; + + /* Actual deallocation happens later when all internal references + are gone. */ + video_unregister_device(&dip->devbase); } static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) { pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,-1); - pvr2_v4l2_dev_destroy(&vp->video_dev); + pvr2_v4l2_dev_destroy(vp->vdev); pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); pvr2_channel_done(&vp->channel); @@ -726,6 +731,14 @@ static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) } +static void pvr2_video_device_release(struct video_device *vdev) +{ + struct pvr2_v4l2_dev *dev; + dev = container_of(vdev,struct pvr2_v4l2_dev,devbase); + kfree(dev); +} + + static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) { struct pvr2_v4l2 *vp; @@ -797,40 +810,12 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) static int pvr2_v4l2_open(struct inode *inode, struct file *file) { - struct pvr2_v4l2_dev *dip = NULL; /* Our own context pointer */ + struct pvr2_v4l2_dev *dip; /* Our own context pointer */ struct pvr2_v4l2_fh *fhp; 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 */ + dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); vp = dip->v4lp; hdw = vp->channel.hdw; @@ -1060,38 +1045,24 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, return; } - dip->vdev = video_device_alloc(); - if (!dip->vdev) { - err("Alloc of pvrusb2 v4l video device failed"); - return; - } - - memcpy(dip->vdev,&vdev_template,sizeof(vdev_template)); - dip->vdev->release = video_device_release; - mutex_lock(&device_lock); + memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); + dip->devbase.release = pvr2_video_device_release; mindevnum = -1; unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); if ((unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = video_nr[unit_number]; } - if ((video_register_device(dip->vdev, v4l_type, mindevnum) < 0) && - (video_register_device(dip->vdev, v4l_type, -1) < 0)) { + if ((video_register_device(&dip->devbase, v4l_type, mindevnum) < 0) && + (video_register_device(&dip->devbase, v4l_type, -1) < 0)) { err("Failed to register pvrusb2 v4l video device"); } else { printk(KERN_INFO "pvrusb2: registered device video%d [%s]\n", - 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; + dip->devbase.minor,pvr2_config_get_name(dip->config)); } - mutex_unlock(&device_lock); pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, - dip->vdev->minor); + dip->devbase.minor); } @@ -1102,15 +1073,19 @@ 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; + vp->vdev = kmalloc(sizeof(*vp->vdev),GFP_KERNEL); + if (!vp->vdev) { + kfree(vp); + return 0; + } + memset(vp->vdev,0,sizeof(*vp->vdev)); pvr2_channel_init(&vp->channel,mnp); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); vp->channel.check_func = pvr2_v4l2_internal_check; /* register streams */ - pvr2_v4l2_dev_init(&vp->video_dev,vp,pvr2_config_mpeg); - + pvr2_v4l2_dev_init(vp->vdev,vp,pvr2_config_mpeg); return vp; } |