summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c')
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c190
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;
}