diff options
Diffstat (limited to 'linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c')
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 190 |
1 files changed, 167 insertions, 23 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 5a36369b8..3bf4e014a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -64,13 +64,22 @@ struct pvr2_v4l2 { struct v4l2_prio_state prio; - /* streams */ - struct pvr2_v4l2_dev *vdev; + /* streams - Note that these must be separately, individually, + * allocated pointers. This is because the v4l core is going to + * manage their deletion - separately, individually... */ + struct pvr2_v4l2_dev *dev_video; + struct pvr2_v4l2_dev *dev_radio; }; static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; module_param_array(video_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "Offset for device's minor"); +MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor"); +static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor"); +static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(vbi_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor"); static struct v4l2_capability pvr_capability ={ .driver = "pvrusb2", @@ -78,7 +87,7 @@ static struct v4l2_capability pvr_capability ={ .bus_info = "usb", .version = KERNEL_VERSION(0,8,0), .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_TUNER | V4L2_CAP_AUDIO | + V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), .reserved = {0,0,0,0} }; @@ -395,9 +404,15 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_S_FREQUENCY: { const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; + unsigned long fv; + fv = vf->frequency; + if (vf->type == V4L2_TUNER_RADIO) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; + } ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), - vf->frequency * 62500); + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); break; } @@ -405,11 +420,23 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; int val = 0; + int cur_input = PVR2_CVAL_INPUT_TV; ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), &val); - val /= 62500; - vf->frequency = val; + if (ret != 0) break; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + &cur_input); + if (cur_input == PVR2_CVAL_INPUT_RADIO) { + val = (val * 2) / 125; + vf->frequency = val; + vf->type = V4L2_TUNER_RADIO; + } else { + val /= 62500; + vf->frequency = val; + vf->type = V4L2_TUNER_ANALOG_TV; + } break; } @@ -723,8 +750,26 @@ 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->devbase.minor,pvr2_config_get_name(dip->config)); + enum pvr2_config cfg = dip->config; + int minor_id = dip->devbase.minor; + enum pvr2_v4l_type pvt; + struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw; + + switch (cfg) { + case pvr2_config_mpeg: + pvt = pvr2_v4l_type_video; + break; + case pvr2_config_vbi: + pvt = pvr2_v4l_type_vbi; + break; + case pvr2_config_radio: + pvt = pvr2_v4l_type_radio; + break; + default: /* paranoia */ + pvt = pvr2_v4l_type_video; + break; + } + pvr2_hdw_v4l_store_minor_number(hdw,pvt,-1); /* Paranoia */ dip->v4lp = NULL; @@ -733,13 +778,40 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) /* Actual deallocation happens later when all internal references are gone. */ video_unregister_device(&dip->devbase); + + switch (cfg) { + case pvr2_config_mpeg: + printk(KERN_INFO "pvrusb2: unregistered device video%d [%s]\n", + minor_id & 0x1f, + pvr2_config_get_name(cfg)); + break; + case pvr2_config_radio: + printk(KERN_INFO "pvrusb2: unregistered device radio%d [%s]\n", + minor_id & 0x1f, + pvr2_config_get_name(cfg)); + break; + case pvr2_config_vbi: + printk(KERN_INFO "pvrusb2: unregistered device vbi%d [%s]\n", + minor_id & 0x1f, + pvr2_config_get_name(cfg)); + break; + default: + break; + } + } 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->vdev); + if (vp->dev_video) { + pvr2_v4l2_dev_destroy(vp->dev_video); + vp->dev_video = 0; + } + if (vp->dev_radio) { + pvr2_v4l2_dev_destroy(vp->dev_radio); + vp->dev_radio = 0; + } pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); pvr2_channel_done(&vp->channel); @@ -795,6 +867,18 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) pvr2_ioread_destroy(fhp->rhp); fhp->rhp = NULL; } + + if (fhp->dev_info->config == pvr2_config_radio) { + int ret; + struct pvr2_hdw *hdw; + hdw = fhp->channel.mc_head->hdw; + if ((ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + PVR2_CVAL_INPUT_TV))) { + return ret; + } + } + v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; @@ -856,6 +940,17 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) 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); + + /* 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->config == pvr2_config_radio) { + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + PVR2_CVAL_INPUT_RADIO); + } + fhp->vnext = NULL; fhp->vprev = vp->vlast; if (vp->vlast) { @@ -953,6 +1048,12 @@ static ssize_t pvr2_v4l2_read(struct file *file, return tcnt; } + if (fh->dev_info->config == pvr2_config_radio) { + /* Radio device nodes on this device + cannot be read or written. */ + return -EPERM; + } + if (!fh->rhp) { ret = pvr2_v4l2_iosetup(fh); if (ret) { @@ -987,6 +1088,12 @@ static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait) return mask; } + if (fh->dev_info->config == pvr2_config_radio) { + /* Radio device nodes on this device + cannot be read or written. */ + return -EPERM; + } + if (!fh->rhp) { ret = pvr2_v4l2_iosetup(fh); if (ret) return POLLERR; @@ -1036,6 +1143,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, int mindevnum; int unit_number; int v4l_type; + enum pvr2_v4l_type pvt; dip->v4lp = vp; dip->config = cfg; @@ -1043,16 +1151,19 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); #endif - switch (cfg) { + switch (dip->config) { case pvr2_config_mpeg: v4l_type = VFL_TYPE_GRABBER; + pvt = pvr2_v4l_type_video; dip->stream = &vp->channel.mc_head->video_stream; break; case pvr2_config_vbi: v4l_type = VFL_TYPE_VBI; + pvt = pvr2_v4l_type_vbi; break; case pvr2_config_radio: v4l_type = VFL_TYPE_RADIO; + pvt = pvr2_v4l_type_radio; break; default: /* Bail out (this should be impossible) */ @@ -1061,7 +1172,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, return; } - if (!dip->stream) { + /* radio device doesn 't need its own stream */ + if (!dip->stream && dip->config != pvr2_config_radio) { err("Failed to set up pvrusb2 v4l dev" " due to missing stream instance"); return; @@ -1080,18 +1192,45 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, 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]; + switch (v4l_type) { + case VFL_TYPE_VBI: + mindevnum = vbi_nr[unit_number]; + break; + case VFL_TYPE_RADIO: + mindevnum = radio_nr[unit_number]; + break; + case VFL_TYPE_GRABBER: + default: + mindevnum = video_nr[unit_number]; + break; + } } 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 { + err("Failed to register pvrusb2 v4l device"); + } + switch (dip->config) { + case pvr2_config_mpeg: printk(KERN_INFO "pvrusb2: registered device video%d [%s]\n", - dip->devbase.minor,pvr2_config_get_name(dip->config)); + dip->devbase.minor & 0x1f, + pvr2_config_get_name(dip->config)); + break; + case pvr2_config_radio: + printk(KERN_INFO "pvrusb2: registered device radio%d [%s]\n", + dip->devbase.minor & 0x1f, + pvr2_config_get_name(dip->config)); + break; + case pvr2_config_vbi: + printk(KERN_INFO "pvrusb2: registered device vbi%d [%s]\n", + dip->devbase.minor & 0x1f, + pvr2_config_get_name(dip->config)); + break; + default: + break; } pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, - dip->devbase.minor); + pvt,dip->devbase.minor); } @@ -1102,19 +1241,24 @@ 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->vdev = kmalloc(sizeof(*vp->vdev),GFP_KERNEL); - if (!vp->vdev) { + vp->dev_video = kmalloc(sizeof(*vp->dev_video),GFP_KERNEL); + vp->dev_radio = kmalloc(sizeof(*vp->dev_radio),GFP_KERNEL); + if (!(vp->dev_video && vp->dev_radio)) { + if (vp->dev_video) kfree(vp->dev_video); + if (vp->dev_radio) kfree(vp->dev_radio); kfree(vp); return NULL; } - memset(vp->vdev,0,sizeof(*vp->vdev)); + memset(vp->dev_video,0,sizeof(*vp->dev_video)); + memset(vp->dev_radio,0,sizeof(*vp->dev_radio)); 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->vdev,vp,pvr2_config_mpeg); + pvr2_v4l2_dev_init(vp->dev_video,vp,pvr2_config_mpeg); + pvr2_v4l2_dev_init(vp->dev_radio,vp,pvr2_config_radio); return vp; } |