V4L/DVB (7300): pvrusb2: v4l2 implementation fixes for input selection
authorMike Isely <isely@pobox.com>
Tue, 22 Apr 2008 17:45:38 +0000 (14:45 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Thu, 24 Apr 2008 17:07:47 +0000 (14:07 -0300)
Now that the pvrusb2 driver can dynamically choose which inputs to
make available depending on the hardware, the enumeration of input
choices is no longer a contiguous range of integers.  Unfortunately
this causes a problem in the v4l2 implementation since the input
enumeration requires continuity in the API.  This change implements a
mapping in order to preserve the v4l2 interface requirement.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/pvrusb2/pvrusb2-v4l2.c

index 5e22927..e878c64 100644 (file)
@@ -67,6 +67,10 @@ struct pvr2_v4l2 {
 
        struct v4l2_prio_state prio;
 
+       /* Map contiguous ordinal value to input id */
+       unsigned char *input_map;
+       unsigned int input_cnt;
+
        /* streams - Note that these must be separately, individually,
         * allocated pointers.  This is because the v4l core is going to
         * manage their deletion - separately, individually...  */
@@ -259,13 +263,19 @@ 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 < 0) || (vi->index >= vp->input_cnt)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               val = vp->input_map[vi->index];
+               switch (val) {
                case PVR2_CVAL_INPUT_TV:
                case PVR2_CVAL_INPUT_DTV:
                case PVR2_CVAL_INPUT_RADIO:
@@ -282,7 +292,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;
 
@@ -304,22 +314,33 @@ 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 < vp->input_cnt; idx++) {
+                       if (vp->input_map[idx] == val) {
+                               vi->index = idx;
+                               break;
+                       }
+               }
                break;
        }
 
        case VIDIOC_S_INPUT:
        {
                struct v4l2_input *vi = (struct v4l2_input *)arg;
+               if ((vi->index < 0) || (vi->index >= vp->input_cnt)) {
+                       ret = -ERANGE;
+                       break;
+               }
                ret = pvr2_ctrl_set_value(
                        pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
-                       vi->index);
+                       vp->input_map[vi->index]);
                break;
        }
 
@@ -818,6 +839,10 @@ static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
                pvr2_v4l2_dev_destroy(vp->dev_radio);
                vp->dev_radio = NULL;
        }
+       if (vp->input_map) {
+               kfree(vp->input_map);
+               vp->input_map = NULL;
+       }
 
        pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
        pvr2_channel_done(&vp->channel);
@@ -1199,27 +1224,46 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
 struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
 {
        struct pvr2_v4l2 *vp;
+       struct pvr2_hdw *hdw;
+       unsigned int input_mask,input_cnt,idx;
 
        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;
 
+       hdw = vp->channel.mc_head->hdw;
+       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++;
+       }
+       vp->input_cnt = input_cnt;
+       vp->input_map = kzalloc(input_cnt,GFP_KERNEL);
+       if (!vp->input_map) goto fail;
+       input_cnt = 0;
+       for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+               if (!(input_mask & (1 << idx))) continue;
+               vp->input_map[input_cnt++] = idx;
+       }
+
        /* 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 (input_mask & (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 0;
 }
 
 /*