include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / media / video / pvrusb2 / pvrusb2-v4l2.c
index 5b1260c..bf1e0fe 100644 (file)
@@ -1,6 +1,5 @@
 /*
  *
- *  $Id$
  *
  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
@@ -21,6 +20,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/version.h>
 #include "pvrusb2-context.h"
 #include "pvrusb2-hdw.h"
@@ -31,6 +31,7 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
 
 struct pvr2_v4l2_dev;
 struct pvr2_v4l2_fh;
@@ -57,7 +58,9 @@ struct pvr2_v4l2_fh {
        struct pvr2_v4l2_fh *vprev;
        wait_queue_head_t wait_data;
        int fw_mode_flag;
-       int prev_input_val;
+       /* Map contiguous ordinal value to input id */
+       unsigned char *input_map;
+       unsigned int input_cnt;
 };
 
 struct pvr2_v4l2 {
@@ -88,8 +91,8 @@ static struct v4l2_capability pvr_capability ={
        .driver         = "pvrusb2",
        .card           = "Hauppauge WinTV pvr-usb2",
        .bus_info       = "usb",
-       .version        = KERNEL_VERSION(0,8,0),
-       .capabilities   = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
+       .version        = KERNEL_VERSION(0, 9, 0),
+       .capabilities   = (V4L2_CAP_VIDEO_CAPTURE |
                           V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
                           V4L2_CAP_READWRITE),
        .reserved       = {0,0,0,0}
@@ -149,31 +152,19 @@ static struct v4l2_format pvr_format [] = {
 };
 
 
-static const char *get_v4l_name(int v4l_type)
-{
-       switch (v4l_type) {
-       case VFL_TYPE_GRABBER: return "video";
-       case VFL_TYPE_RADIO: return "radio";
-       case VFL_TYPE_VBI: return "vbi";
-       default: return "?";
-       }
-}
-
-
 /*
  * pvr_ioctl()
  *
  * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
  *
  */
-static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
-                             unsigned int cmd, void *arg)
+static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
 {
        struct pvr2_v4l2_fh *fh = file->private_data;
        struct pvr2_v4l2 *vp = fh->vhead;
        struct pvr2_v4l2_dev *dev_info = fh->dev_info;
        struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-       int ret = -EINVAL;
+       long ret = -EINVAL;
 
        if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
                v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd);
@@ -203,6 +194,9 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                struct v4l2_capability *cap = arg;
 
                memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+               strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
+                       sizeof(cap->bus_info));
+               strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card));
 
                ret = 0;
                break;
@@ -256,14 +250,21 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                struct v4l2_input *vi = (struct v4l2_input *)arg;
                struct v4l2_input tmp;
                unsigned int cnt;
+               int val;
 
                cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
 
                memset(&tmp,0,sizeof(tmp));
                tmp.index = vi->index;
                ret = 0;
-               switch (vi->index) {
+               if (vi->index >= fh->input_cnt) {
+                       ret = -EINVAL;
+                       break;
+               }
+               val = fh->input_map[vi->index];
+               switch (val) {
                case PVR2_CVAL_INPUT_TV:
+               case PVR2_CVAL_INPUT_DTV:
                case PVR2_CVAL_INPUT_RADIO:
                        tmp.type = V4L2_INPUT_TYPE_TUNER;
                        break;
@@ -278,7 +279,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                if (ret < 0) break;
 
                cnt = 0;
-               pvr2_ctrl_get_valname(cptr,vi->index,
+               pvr2_ctrl_get_valname(cptr,val,
                                      tmp.name,sizeof(tmp.name)-1,&cnt);
                tmp.name[cnt] = 0;
 
@@ -300,34 +301,71 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
 
        case VIDIOC_G_INPUT:
        {
+               unsigned int idx;
                struct pvr2_ctrl *cptr;
                struct v4l2_input *vi = (struct v4l2_input *)arg;
                int val;
                cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
                val = 0;
                ret = pvr2_ctrl_get_value(cptr,&val);
-               vi->index = val;
+               vi->index = 0;
+               for (idx = 0; idx < fh->input_cnt; idx++) {
+                       if (fh->input_map[idx] == val) {
+                               vi->index = idx;
+                               break;
+                       }
+               }
                break;
        }
 
        case VIDIOC_S_INPUT:
        {
                struct v4l2_input *vi = (struct v4l2_input *)arg;
+               if (vi->index >= fh->input_cnt) {
+                       ret = -ERANGE;
+                       break;
+               }
                ret = pvr2_ctrl_set_value(
                        pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
-                       vi->index);
+                       fh->input_map[vi->index]);
                break;
        }
 
        case VIDIOC_ENUMAUDIO:
        {
+               /* pkt: FIXME: We are returning one "fake" input here
+                  which could very well be called "whatever_we_like".
+                  This is for apps that want to see an audio input
+                  just to feel comfortable, as well as to test if
+                  it can do stereo or sth. There is actually no guarantee
+                  that the actual audio input cannot change behind the app's
+                  back, but most applications should not mind that either.
+
+                  Hopefully, mplayer people will work with us on this (this
+                  whole mess is to support mplayer pvr://), or Hans will come
+                  up with a more standard way to say "we have inputs but we
+                  don 't want you to change them independent of video" which
+                  will sort this mess.
+                */
+               struct v4l2_audio *vin = arg;
                ret = -EINVAL;
+               if (vin->index > 0) break;
+               strncpy(vin->name, "PVRUSB2 Audio",14);
+               vin->capability = V4L2_AUDCAP_STEREO;
+               ret = 0;
+               break;
                break;
        }
 
        case VIDIOC_G_AUDIO:
        {
-               ret = -EINVAL;
+               /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
+               struct v4l2_audio *vin = arg;
+               memset(vin,0,sizeof(*vin));
+               vin->index = 0;
+               strncpy(vin->name, "PVRUSB2 Audio",14);
+               vin->capability = V4L2_AUDCAP_STEREO;
+               ret = 0;
                break;
        }
 
@@ -339,6 +377,9 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
        case VIDIOC_G_TUNER:
        {
                struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+
+               if (vt->index != 0) break; /* Only answer for the 1st tuner */
+
                pvr2_hdw_execute_tuner_poll(hdw);
                ret = pvr2_hdw_get_tuner_status(hdw,vt);
                break;
@@ -472,7 +513,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                ret = 0;
                switch(vf->type) {
                case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
-                       int lmin,lmax;
+                       int lmin,lmax,ldef;
                        struct pvr2_ctrl *hcp,*vcp;
                        int h = vf->fmt.pix.height;
                        int w = vf->fmt.pix.width;
@@ -481,14 +522,20 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
 
                        lmin = pvr2_ctrl_get_min(hcp);
                        lmax = pvr2_ctrl_get_max(hcp);
-                       if (w < lmin) {
+                       pvr2_ctrl_get_def(hcp, &ldef);
+                       if (w == -1) {
+                               w = ldef;
+                       } else if (w < lmin) {
                                w = lmin;
                        } else if (w > lmax) {
                                w = lmax;
                        }
                        lmin = pvr2_ctrl_get_min(vcp);
                        lmax = pvr2_ctrl_get_max(vcp);
-                       if (h < lmin) {
+                       pvr2_ctrl_get_def(vcp, &ldef);
+                       if (h == -1) {
+                               h = ldef;
+                       } else if (h < lmin) {
                                h = lmin;
                        } else if (h > lmax) {
                                h = lmax;
@@ -546,6 +593,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
        case VIDIOC_QUERYCTRL:
        {
                struct pvr2_ctrl *cptr;
+               int val;
                struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
                ret = 0;
                if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
@@ -569,7 +617,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                           pvr2_ctrl_get_desc(cptr));
                strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name));
                vc->flags = pvr2_ctrl_get_v4lflags(cptr);
-               vc->default_value = pvr2_ctrl_get_def(cptr);
+               pvr2_ctrl_get_def(cptr, &val);
+               vc->default_value = val;
                switch (pvr2_ctrl_get_type(cptr)) {
                case pvr2_ctl_enum:
                        vc->type = V4L2_CTRL_TYPE_MENU;
@@ -636,6 +685,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                struct v4l2_ext_control *ctrl;
                unsigned int idx;
                int val;
+               ret = 0;
                for (idx = 0; idx < ctls->count; idx++) {
                        ctrl = ctls->controls + idx;
                        ret = pvr2_ctrl_get_value(
@@ -658,6 +708,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                        (struct v4l2_ext_controls *)arg;
                struct v4l2_ext_control *ctrl;
                unsigned int idx;
+               ret = 0;
                for (idx = 0; idx < ctls->count; idx++) {
                        ctrl = ctls->controls + idx;
                        ret = pvr2_ctrl_set_value(
@@ -680,6 +731,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                unsigned int idx;
                /* For the moment just validate that the requested control
                   actually exists. */
+               ret = 0;
                for (idx = 0; idx < ctls->count; idx++) {
                        ctrl = ctls->controls + idx;
                        pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id);
@@ -692,6 +744,92 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
+       case VIDIOC_CROPCAP:
+       {
+               struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg;
+               if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = pvr2_hdw_get_cropcap(hdw, cap);
+               cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
+               break;
+       }
+       case VIDIOC_G_CROP:
+       {
+               struct v4l2_crop *crop = (struct v4l2_crop *)arg;
+               int val = 0;
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = pvr2_ctrl_get_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               crop->c.left = val;
+               ret = pvr2_ctrl_get_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               crop->c.top = val;
+               ret = pvr2_ctrl_get_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               crop->c.width = val;
+               ret = pvr2_ctrl_get_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               crop->c.height = val;
+       }
+       case VIDIOC_S_CROP:
+       {
+               struct v4l2_crop *crop = (struct v4l2_crop *)arg;
+               struct v4l2_cropcap cap;
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       ret = -EINVAL;
+                       break;
+               }
+               cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               ret = pvr2_ctrl_set_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
+                       crop->c.left);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = pvr2_ctrl_set_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
+                       crop->c.top);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = pvr2_ctrl_set_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
+                       crop->c.width);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = pvr2_ctrl_set_value(
+                       pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
+                       crop->c.height);
+               if (ret != 0) {
+                       ret = -EINVAL;
+                       break;
+               }
+       }
        case VIDIOC_LOG_STATUS:
        {
                pvr2_hdw_trigger_module_log(hdw);
@@ -699,23 +837,23 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
                break;
        }
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-       case VIDIOC_INT_G_REGISTER:
-       case VIDIOC_INT_S_REGISTER:
+       case VIDIOC_DBG_S_REGISTER:
+       case VIDIOC_DBG_G_REGISTER:
        {
-               u32 val;
-               struct v4l2_register *req = (struct v4l2_register *)arg;
-               if (cmd == VIDIOC_INT_S_REGISTER) val = req->val;
+               u64 val;
+               struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg;
+               if (cmd == VIDIOC_DBG_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;
+                       hdw, &req->match, req->reg,
+                       cmd == VIDIOC_DBG_S_REGISTER, &val);
+               if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val;
                break;
        }
 #endif
 
        default :
-               ret = v4l_compat_translate_ioctl(inode,file,cmd,
-                                                arg,pvr2_v4l2_do_ioctl);
+               ret = v4l_compat_translate_ioctl(file, cmd,
+                                                arg, pvr2_v4l2_do_ioctl);
        }
 
        pvr2_hdw_commit_ctl(hdw);
@@ -723,31 +861,28 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
        if (ret < 0) {
                if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
                        pvr2_trace(PVR2_TRACE_V4LIOCTL,
-                                  "pvr2_v4l2_do_ioctl failure, ret=%d",ret);
+                                  "pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
                } else {
                        if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
                                pvr2_trace(PVR2_TRACE_V4LIOCTL,
-                                          "pvr2_v4l2_do_ioctl failure, ret=%d"
-                                          " command was:",ret);
+                                          "pvr2_v4l2_do_ioctl failure, ret=%ld"
+                                          " command was:", ret);
                                v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
                                                cmd);
                        }
                }
        } else {
                pvr2_trace(PVR2_TRACE_V4LIOCTL,
-                          "pvr2_v4l2_do_ioctl complete, ret=%d (0x%x)",
-                          ret,ret);
+                          "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
+                          ret, ret);
        }
        return ret;
 }
 
-
 static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
 {
-       int minor_id = dip->devbase.minor;
        struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
        enum pvr2_config cfg = dip->config;
-       int v4l_type = dip->v4l_type;
 
        pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
 
@@ -759,22 +894,31 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
           are gone. */
        video_unregister_device(&dip->devbase);
 
-       printk(KERN_INFO "pvrusb2: unregistered device %s%u [%s]\n",
-              get_v4l_name(v4l_type),minor_id & 0x1f,
+       printk(KERN_INFO "pvrusb2: unregistered device %s [%s]\n",
+              video_device_node_name(&dip->devbase),
               pvr2_config_get_name(cfg));
 
 }
 
 
+static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
+{
+       if (!dip) return;
+       if (!dip->devbase.parent) return;
+       dip->devbase.parent = NULL;
+       device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
+}
+
+
 static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
 {
        if (vp->dev_video) {
                pvr2_v4l2_dev_destroy(vp->dev_video);
-               vp->dev_video = 0;
+               vp->dev_video = NULL;
        }
        if (vp->dev_radio) {
                pvr2_v4l2_dev_destroy(vp->dev_radio);
-               vp->dev_radio = 0;
+               vp->dev_radio = NULL;
        }
 
        pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
@@ -796,28 +940,25 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
        struct pvr2_v4l2 *vp;
        vp = container_of(chp,struct pvr2_v4l2,channel);
        if (!vp->channel.mc_head->disconnect_flag) return;
+       pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
+       pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
        if (vp->vfirst) return;
        pvr2_v4l2_destroy_no_lock(vp);
 }
 
 
-static int pvr2_v4l2_ioctl(struct inode *inode, struct file *file,
+static long pvr2_v4l2_ioctl(struct file *file,
                           unsigned int cmd, unsigned long arg)
 {
 
-/* Temporary hack : use ivtv api until a v4l2 one is available. */
-#define IVTV_IOC_G_CODEC        0xFFEE7703
-#define IVTV_IOC_S_CODEC        0xFFEE7704
-       if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0;
-       return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl);
+       return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
 }
 
 
-static int pvr2_v4l2_release(struct inode *inode, struct file *file)
+static int pvr2_v4l2_release(struct file *file)
 {
        struct pvr2_v4l2_fh *fhp = file->private_data;
        struct pvr2_v4l2 *vp = fhp->vhead;
-       struct pvr2_context *mp = fhp->vhead->channel.mc_head;
        struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
 
        pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
@@ -834,52 +975,43 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file)
        v4l2_prio_close(&vp->prio, &fhp->prio);
        file->private_data = NULL;
 
-       pvr2_context_enter(mp); do {
-               /* Restore the previous input selection, if it makes sense
-                  to do so. */
-               if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) {
-                       struct pvr2_ctrl *cp;
-                       int pval;
-                       cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
-                       pvr2_ctrl_get_value(cp,&pval);
-                       /* Only restore if we're still selecting the radio */
-                       if (pval == PVR2_CVAL_INPUT_RADIO) {
-                               pvr2_ctrl_set_value(cp,fhp->prev_input_val);
-                               pvr2_hdw_commit_ctl(hdw);
-                       }
-               }
-
-               if (fhp->vnext) {
-                       fhp->vnext->vprev = fhp->vprev;
-               } else {
-                       vp->vlast = fhp->vprev;
-               }
-               if (fhp->vprev) {
-                       fhp->vprev->vnext = fhp->vnext;
-               } else {
-                       vp->vfirst = fhp->vnext;
-               }
-               fhp->vnext = NULL;
-               fhp->vprev = NULL;
-               fhp->vhead = NULL;
-               pvr2_channel_done(&fhp->channel);
-               pvr2_trace(PVR2_TRACE_STRUCT,
-                          "Destroying pvr_v4l2_fh id=%p",fhp);
-               kfree(fhp);
-               if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
-                       pvr2_v4l2_destroy_no_lock(vp);
-               }
-       } while (0); pvr2_context_exit(mp);
+       if (fhp->vnext) {
+               fhp->vnext->vprev = fhp->vprev;
+       } else {
+               vp->vlast = fhp->vprev;
+       }
+       if (fhp->vprev) {
+               fhp->vprev->vnext = fhp->vnext;
+       } else {
+               vp->vfirst = fhp->vnext;
+       }
+       fhp->vnext = NULL;
+       fhp->vprev = NULL;
+       fhp->vhead = NULL;
+       pvr2_channel_done(&fhp->channel);
+       pvr2_trace(PVR2_TRACE_STRUCT,
+                  "Destroying pvr_v4l2_fh id=%p",fhp);
+       if (fhp->input_map) {
+               kfree(fhp->input_map);
+               fhp->input_map = NULL;
+       }
+       kfree(fhp);
+       if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+               pvr2_v4l2_destroy_no_lock(vp);
+       }
        return 0;
 }
 
 
-static int pvr2_v4l2_open(struct inode *inode, struct file *file)
+static int pvr2_v4l2_open(struct file *file)
 {
        struct pvr2_v4l2_dev *dip; /* Our own context pointer */
        struct pvr2_v4l2_fh *fhp;
        struct pvr2_v4l2 *vp;
        struct pvr2_hdw *hdw;
+       unsigned int input_mask = 0;
+       unsigned int input_cnt,idx;
+       int ret = 0;
 
        dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
 
@@ -902,32 +1034,62 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
        init_waitqueue_head(&fhp->wait_data);
        fhp->dev_info = dip;
 
-       pvr2_context_enter(vp->channel.mc_head); do {
-               pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
-               pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
+       pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
+       pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
 
-               fhp->vnext = NULL;
-               fhp->vprev = vp->vlast;
-               if (vp->vlast) {
-                       vp->vlast->vnext = fhp;
-               } else {
-                       vp->vfirst = fhp;
-               }
-               vp->vlast = fhp;
-               fhp->vhead = vp;
-
-               /* Opening the /dev/radioX device implies a mode switch.
-                  So execute that here.  Note that you can get the
-                  IDENTICAL effect merely by opening the normal video
-                  device and setting the input appropriately. */
-               if (dip->v4l_type == VFL_TYPE_RADIO) {
-                       struct pvr2_ctrl *cp;
-                       cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
-                       pvr2_ctrl_get_value(cp,&fhp->prev_input_val);
-                       pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO);
-                       pvr2_hdw_commit_ctl(hdw);
-               }
-       } while (0); pvr2_context_exit(vp->channel.mc_head);
+       if (dip->v4l_type == VFL_TYPE_RADIO) {
+               /* Opening device as a radio, legal input selection subset
+                  is just the radio. */
+               input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
+       } else {
+               /* Opening the main V4L device, legal input selection
+                  subset includes all analog inputs. */
+               input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
+                             (1 << PVR2_CVAL_INPUT_TV) |
+                             (1 << PVR2_CVAL_INPUT_COMPOSITE) |
+                             (1 << PVR2_CVAL_INPUT_SVIDEO));
+       }
+       ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
+       if (ret) {
+               pvr2_channel_done(&fhp->channel);
+               pvr2_trace(PVR2_TRACE_STRUCT,
+                          "Destroying pvr_v4l2_fh id=%p (input mask error)",
+                          fhp);
+
+               kfree(fhp);
+               return ret;
+       }
+
+       input_mask &= pvr2_hdw_get_input_available(hdw);
+       input_cnt = 0;
+       for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+               if (input_mask & (1 << idx)) input_cnt++;
+       }
+       fhp->input_cnt = input_cnt;
+       fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
+       if (!fhp->input_map) {
+               pvr2_channel_done(&fhp->channel);
+               pvr2_trace(PVR2_TRACE_STRUCT,
+                          "Destroying pvr_v4l2_fh id=%p (input map failure)",
+                          fhp);
+               kfree(fhp);
+               return -ENOMEM;
+       }
+       input_cnt = 0;
+       for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+               if (!(input_mask & (1 << idx))) continue;
+               fhp->input_map[input_cnt++] = idx;
+       }
+
+       fhp->vnext = NULL;
+       fhp->vprev = vp->vlast;
+       if (vp->vlast) {
+               vp->vlast->vnext = fhp;
+       } else {
+               vp->vfirst = fhp;
+       }
+       vp->vlast = fhp;
+       fhp->vhead = vp;
 
        fhp->file = file;
        file->private_data = fhp;
@@ -975,10 +1137,8 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
        sp = fh->dev_info->stream->stream;
        pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
        pvr2_hdw_set_stream_type(hdw,fh->dev_info->config);
-       pvr2_hdw_set_streaming(hdw,!0);
-       ret = pvr2_ioread_set_enabled(fh->rhp,!0);
-
-       return ret;
+       if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
+       return pvr2_ioread_set_enabled(fh->rhp,!0);
 }
 
 
@@ -1070,26 +1230,17 @@ static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
 }
 
 
-static const struct file_operations vdev_fops = {
+static const struct v4l2_file_operations vdev_fops = {
        .owner      = THIS_MODULE,
        .open       = pvr2_v4l2_open,
        .release    = pvr2_v4l2_release,
        .read       = pvr2_v4l2_read,
        .ioctl      = pvr2_v4l2_ioctl,
-       .llseek     = no_llseek,
        .poll       = pvr2_v4l2_poll,
 };
 
 
-#define VID_HARDWARE_PVRUSB2    38  /* FIXME : need a good value */
-
 static struct video_device vdev_template = {
-       .owner      = THIS_MODULE,
-       .type       = VID_TYPE_CAPTURE | VID_TYPE_TUNER,
-       .type2      = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE
-                      | V4L2_CAP_TUNER | V4L2_CAP_AUDIO
-                      | V4L2_CAP_READWRITE),
-       .hardware   = VID_HARDWARE_PVRUSB2,
        .fops       = &vdev_fops,
 };
 
@@ -1098,12 +1249,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
                               struct pvr2_v4l2 *vp,
                               int v4l_type)
 {
+       struct usb_device *usbdev;
        int mindevnum;
        int unit_number;
-       int *nr_ptr = 0;
+       int *nr_ptr = NULL;
        dip->v4lp = vp;
 
-
+       usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
        dip->v4l_type = v4l_type;
        switch (v4l_type) {
        case VFL_TYPE_GRABBER:
@@ -1112,8 +1264,9 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
                dip->minor_type = pvr2_v4l_type_video;
                nr_ptr = video_nr;
                if (!dip->stream) {
-                       err("Failed to set up pvrusb2 v4l video dev"
-                           " due to missing stream instance");
+                       pr_err(KBUILD_MODNAME
+                               ": Failed to set up pvrusb2 v4l video dev"
+                               " due to missing stream instance\n");
                        return;
                }
                break;
@@ -1130,8 +1283,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
                break;
        default:
                /* Bail out (this should be impossible) */
-               err("Failed to set up pvrusb2 v4l dev"
-                   " due to unrecognized config");
+               pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
+                   " due to unrecognized config\n");
                return;
        }
 
@@ -1143,15 +1296,17 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
        if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
                mindevnum = nr_ptr[unit_number];
        }
+       dip->devbase.parent = &usbdev->dev;
        if ((video_register_device(&dip->devbase,
                                   dip->v4l_type, mindevnum) < 0) &&
            (video_register_device(&dip->devbase,
                                   dip->v4l_type, -1) < 0)) {
-               err("Failed to register pvrusb2 v4l device");
+               pr_err(KBUILD_MODNAME
+                       ": Failed to register pvrusb2 v4l device\n");
        }
 
-       printk(KERN_INFO "pvrusb2: registered device %s%u [%s]\n",
-              get_v4l_name(dip->v4l_type),dip->devbase.minor & 0x1f,
+       printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
+              video_device_node_name(&dip->devbase),
               pvr2_config_get_name(dip->config));
 
        pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
@@ -1165,24 +1320,27 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
 
        vp = kzalloc(sizeof(*vp),GFP_KERNEL);
        if (!vp) return vp;
-       vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
-       vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
-       if (!(vp->dev_video && vp->dev_radio)) {
-               kfree(vp->dev_video);
-               kfree(vp->dev_radio);
-               kfree(vp);
-               return NULL;
-       }
        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 */
+       vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
+       if (!vp->dev_video) goto fail;
        pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
-       pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
+       if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
+           (1 << PVR2_CVAL_INPUT_RADIO)) {
+               vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
+               if (!vp->dev_radio) goto fail;
+               pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
+       }
 
        return vp;
+ fail:
+       pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
+       pvr2_v4l2_destroy_no_lock(vp);
+       return NULL;
 }
 
 /*