diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 02a541fbeff9..3cea6101e9f0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -32,6 +32,8 @@ #include #include +#define PVR2_NR_STREAMS 3 + struct pvr2_v4l2_dev; struct pvr2_v4l2_fh; struct pvr2_v4l2; @@ -77,7 +79,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} }; @@ -784,6 +786,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; @@ -845,6 +859,32 @@ 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); + + /* pk: warning, severe ugliness follows. 18+ only. + please blaim V4L(ivtv) for braindamaged interfaces, + not the implementor. This is probably flawed, but + suggestions on how to do this "right" are welcome! */ + if (dip->config == pvr2_config_radio) { + int ret; + if ((pvr2_channel_check_stream_no_lock(&fhp->channel, + fhp->dev_info->stream)) != 0) { + /* We can 't switch modes while streaming */ + pvr2_channel_done(&fhp->channel); + kfree(fhp); + pvr2_context_exit(vp->channel.mc_head); + return -EBUSY; + } + + if ((ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + PVR2_CVAL_INPUT_RADIO))) { + pvr2_channel_done(&fhp->channel); + kfree(fhp); + pvr2_context_exit(vp->channel.mc_head); + return ret; + } + } + fhp->vnext = NULL; fhp->vprev = vp->vlast; if (vp->vlast) { @@ -942,6 +982,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) { @@ -976,6 +1022,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; @@ -1044,7 +1096,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 && cfg != pvr2_config_radio) { err("Failed to set up pvrusb2 v4l dev" " due to missing stream instance"); return; @@ -1060,10 +1113,25 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, } 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 (cfg) { + case pvr2_config_mpeg: printk(KERN_INFO "pvrusb2: registered device video%d [%s]\n", dip->devbase.minor,pvr2_config_get_name(dip->config)); + break; + case pvr2_config_vbi: + printk(KERN_INFO "pvrusb2: registered device vbi%d [%s]\n", + dip->devbase.minor - MINOR_VFL_TYPE_VBI_MIN, + pvr2_config_get_name(dip->config)); + break; + case pvr2_config_radio: + printk(KERN_INFO "pvrusb2: registered device radio%d [%s]\n", + dip->devbase.minor - MINOR_VFL_TYPE_RADIO_MIN, + pvr2_config_get_name(dip->config)); + break; + default: + break; } pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, @@ -1078,19 +1146,20 @@ 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); + vp->vdev = kmalloc(sizeof(*vp->vdev)*PVR2_NR_STREAMS,GFP_KERNEL); if (!vp->vdev) { kfree(vp); return NULL; } - memset(vp->vdev,0,sizeof(*vp->vdev)); + memset(vp->vdev,0,sizeof(*vp->vdev)*PVR2_NR_STREAMS); 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->vdev[0],vp,pvr2_config_mpeg); + pvr2_v4l2_dev_init(&vp->vdev[2],vp,pvr2_config_radio); return vp; }