]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/radio/radio-si470x.c
ext3: Fix duplicate entries returned from getdents() system call
[linux-2.6-omap-h63xx.git] / drivers / media / radio / radio-si470x.c
index f6cedcd3ab97b82b532b16a03137eb0fd5c7e75f..5920cd306975aa7983cc2bd892141780cbef6970 100644 (file)
  *             - hardware frequency seek support
  *             - afc indication
  *             - more safety checks, let si470x_get_freq return errno
+ *             - vidioc behavior corrected according to v4l2 spec
  *
  * ToDo:
  * - add firmware download/update support
 /* USB Device ID List */
 static struct usb_device_id si470x_usb_driver_id_table[] = {
        /* Silicon Labs USB FM Radio Reference Design */
-       { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
        /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
-       { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
        /* Terminating entry */
        { }
 };
@@ -157,7 +158,7 @@ MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
 
 /* Radio Nr */
 static int radio_nr = -1;
-module_param(radio_nr, int, 0);
+module_param(radio_nr, int, 0444);
 MODULE_PARM_DESC(radio_nr, "Radio Nr");
 
 /* Spacing (kHz) */
@@ -165,42 +166,42 @@ MODULE_PARM_DESC(radio_nr, "Radio Nr");
 /* 1: 100 kHz (Europe, Japan) */
 /* 2:  50 kHz */
 static unsigned short space = 2;
-module_param(space, ushort, 0);
-MODULE_PARM_DESC(radio_nr, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
+module_param(space, ushort, 0444);
+MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
 
 /* Bottom of Band (MHz) */
 /* 0: 87.5 - 108 MHz (USA, Europe)*/
 /* 1: 76   - 108 MHz (Japan wide band) */
 /* 2: 76   -  90 MHz (Japan) */
 static unsigned short band = 1;
-module_param(band, ushort, 0);
-MODULE_PARM_DESC(radio_nr, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
+module_param(band, ushort, 0444);
+MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
 
 /* De-emphasis */
 /* 0: 75 us (USA) */
 /* 1: 50 us (Europe, Australia, Japan) */
 static unsigned short de = 1;
-module_param(de, ushort, 0);
-MODULE_PARM_DESC(radio_nr, "De-emphasis: 0=75us *1=50us*");
+module_param(de, ushort, 0444);
+MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
 
 /* USB timeout */
 static unsigned int usb_timeout = 500;
-module_param(usb_timeout, uint, 0);
+module_param(usb_timeout, uint, 0644);
 MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
 
 /* Tune timeout */
 static unsigned int tune_timeout = 3000;
-module_param(tune_timeout, uint, 0);
+module_param(tune_timeout, uint, 0644);
 MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
 
 /* Seek timeout */
 static unsigned int seek_timeout = 5000;
-module_param(seek_timeout, uint, 0);
+module_param(seek_timeout, uint, 0644);
 MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
 
 /* RDS buffer blocks */
 static unsigned int rds_buf = 100;
-module_param(rds_buf, uint, 0);
+module_param(rds_buf, uint, 0444);
 MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
 
 /* RDS maximum block errors */
@@ -209,7 +210,7 @@ static unsigned short max_rds_errors = 1;
 /* 1 means 1-2  errors requiring correction (used by original USBRadio.exe) */
 /* 2 means 3-5  errors requiring correction */
 /* 3 means   6+ errors or errors in checkword, correction not possible */
-module_param(max_rds_errors, ushort, 0);
+module_param(max_rds_errors, ushort, 0644);
 MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
 
 /* RDS poll frequency */
@@ -218,7 +219,7 @@ static unsigned int rds_poll_time = 40;
 /* 50 is used by radio-cadet */
 /* 75 should be okay */
 /* 80 is the usual RDS receive interval */
-module_param(rds_poll_time, uint, 0);
+module_param(rds_poll_time, uint, 0644);
 MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
 
 
@@ -667,23 +668,29 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
        int retval;
 
        /* Spacing (kHz) */
-       switch (space) {
+       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
        /* 0: 200 kHz (USA, Australia) */
-       case 0 : spacing = 0.200 * FREQ_MUL; break;
+       case 0:
+               spacing = 0.200 * FREQ_MUL; break;
        /* 1: 100 kHz (Europe, Japan) */
-       case 1 : spacing = 0.100 * FREQ_MUL; break;
+       case 1:
+               spacing = 0.100 * FREQ_MUL; break;
        /* 2:  50 kHz */
-       default: spacing = 0.050 * FREQ_MUL; break;
+       default:
+               spacing = 0.050 * FREQ_MUL; break;
        };
 
        /* Bottom of Band (MHz) */
-       switch (band) {
+       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
        /* 0: 87.5 - 108 MHz (USA, Europe) */
-       case 0 : band_bottom = 87.5 * FREQ_MUL; break;
+       case 0:
+               band_bottom = 87.5 * FREQ_MUL; break;
        /* 1: 76   - 108 MHz (Japan wide band) */
-       default: band_bottom = 76   * FREQ_MUL; break;
+       default:
+               band_bottom = 76   * FREQ_MUL; break;
        /* 2: 76   -  90 MHz (Japan) */
-       case 2 : band_bottom = 76   * FREQ_MUL; break;
+       case 2:
+               band_bottom = 76   * FREQ_MUL; break;
        };
 
        /* read channel */
@@ -706,23 +713,29 @@ static int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
        unsigned short chan;
 
        /* Spacing (kHz) */
-       switch (space) {
+       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
        /* 0: 200 kHz (USA, Australia) */
-       case 0 : spacing = 0.200 * FREQ_MUL; break;
+       case 0:
+               spacing = 0.200 * FREQ_MUL; break;
        /* 1: 100 kHz (Europe, Japan) */
-       case 1 : spacing = 0.100 * FREQ_MUL; break;
+       case 1:
+               spacing = 0.100 * FREQ_MUL; break;
        /* 2:  50 kHz */
-       default: spacing = 0.050 * FREQ_MUL; break;
+       default:
+               spacing = 0.050 * FREQ_MUL; break;
        };
 
        /* Bottom of Band (MHz) */
-       switch (band) {
+       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
        /* 0: 87.5 - 108 MHz (USA, Europe) */
-       case 0 : band_bottom = 87.5 * FREQ_MUL; break;
+       case 0:
+               band_bottom = 87.5 * FREQ_MUL; break;
        /* 1: 76   - 108 MHz (Japan wide band) */
-       default: band_bottom = 76   * FREQ_MUL; break;
+       default:
+               band_bottom = 76   * FREQ_MUL; break;
        /* 2: 76   -  90 MHz (Japan) */
-       case 2 : band_bottom = 76   * FREQ_MUL; break;
+       case 2:
+               band_bottom = 76   * FREQ_MUL; break;
        };
 
        /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
@@ -1164,7 +1177,6 @@ static const struct file_operations si470x_fops = {
  * si470x_v4l2_queryctrl - query control
  */
 static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
-/* HINT: the disabled controls are only here to satify kradio and such apps */
        {
                .id             = V4L2_CID_AUDIO_VOLUME,
                .type           = V4L2_CTRL_TYPE_INTEGER,
@@ -1174,18 +1186,6 @@ static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
                .step           = 1,
                .default_value  = 15,
        },
-       {
-               .id             = V4L2_CID_AUDIO_BALANCE,
-               .flags          = V4L2_CTRL_FLAG_DISABLED,
-       },
-       {
-               .id             = V4L2_CID_AUDIO_BASS,
-               .flags          = V4L2_CTRL_FLAG_DISABLED,
-       },
-       {
-               .id             = V4L2_CID_AUDIO_TREBLE,
-               .flags          = V4L2_CTRL_FLAG_DISABLED,
-       },
        {
                .id             = V4L2_CID_AUDIO_MUTE,
                .type           = V4L2_CTRL_TYPE_BOOLEAN,
@@ -1195,10 +1195,6 @@ static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
                .step           = 1,
                .default_value  = 1,
        },
-       {
-               .id             = V4L2_CID_AUDIO_LOUDNESS,
-               .flags          = V4L2_CTRL_FLAG_DISABLED,
-       },
 };
 
 
@@ -1219,57 +1215,35 @@ static int si470x_vidioc_querycap(struct file *file, void *priv,
 }
 
 
-/*
- * si470x_vidioc_g_input - get input
- */
-static int si470x_vidioc_g_input(struct file *file, void *priv,
-               unsigned int *i)
-{
-       *i = 0;
-
-       return 0;
-}
-
-
-/*
- * si470x_vidioc_s_input - set input
- */
-static int si470x_vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
-       int retval = 0;
-
-       /* safety checks */
-       if (i != 0)
-               retval = -EINVAL;
-
-       if (retval < 0)
-               printk(KERN_WARNING DRIVER_NAME
-                       ": set input failed with %d\n", retval);
-       return retval;
-}
-
-
 /*
  * si470x_vidioc_queryctrl - enumerate control items
  */
 static int si470x_vidioc_queryctrl(struct file *file, void *priv,
                struct v4l2_queryctrl *qc)
 {
-       unsigned char i;
+       unsigned char i = 0;
        int retval = -EINVAL;
 
-       /* safety checks */
-       if (!qc->id)
+       /* abort if qc->id is below V4L2_CID_BASE */
+       if (qc->id < V4L2_CID_BASE)
                goto done;
 
+       /* search video control */
        for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) {
                if (qc->id == si470x_v4l2_queryctrl[i].id) {
                        memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc));
-                       retval = 0;
+                       retval = 0; /* found */
                        break;
                }
        }
 
+       /* disable unsupported base controls */
+       /* to satisfy kradio and such apps */
+       if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
+               qc->flags = V4L2_CTRL_FLAG_DISABLED;
+               retval = 0;
+       }
+
 done:
        if (retval < 0)
                printk(KERN_WARNING DRIVER_NAME
@@ -1360,44 +1334,13 @@ done:
 static int si470x_vidioc_g_audio(struct file *file, void *priv,
                struct v4l2_audio *audio)
 {
-       int retval = 0;
-
-       /* safety checks */
-       if (audio->index != 0) {
-               retval = -EINVAL;
-               goto done;
-       }
-
+       /* driver constants */
+       audio->index = 0;
        strcpy(audio->name, "Radio");
        audio->capability = V4L2_AUDCAP_STEREO;
+       audio->mode = 0;
 
-done:
-       if (retval < 0)
-               printk(KERN_WARNING DRIVER_NAME
-                       ": get audio failed with %d\n", retval);
-       return retval;
-}
-
-
-/*
- * si470x_vidioc_s_audio - set audio attributes
- */
-static int si470x_vidioc_s_audio(struct file *file, void *priv,
-               struct v4l2_audio *audio)
-{
-       int retval = 0;
-
-       /* safety checks */
-       if (audio->index != 0) {
-               retval = -EINVAL;
-               goto done;
-       }
-
-done:
-       if (retval < 0)
-               printk(KERN_WARNING DRIVER_NAME
-                       ": set audio failed with %d\n", retval);
-       return retval;
+       return 0;
 }
 
 
@@ -1415,7 +1358,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                retval = -EIO;
                goto done;
        }
-       if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) {
+       if (tuner->index != 0) {
                retval = -EINVAL;
                goto done;
        }
@@ -1424,8 +1367,13 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
        if (retval < 0)
                goto done;
 
+       /* driver constants */
        strcpy(tuner->name, "FM");
-       switch (band) {
+       tuner->type = V4L2_TUNER_RADIO;
+       tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+
+       /* range limits */
+       switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
        /* 0: 87.5 - 108 MHz (USA, Europe, default) */
        default:
                tuner->rangelow  =  87.5 * FREQ_MUL;
@@ -1442,14 +1390,18 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
                tuner->rangehigh =  90   * FREQ_MUL;
                break;
        };
-       tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
-       tuner->capability = V4L2_TUNER_CAP_LOW;
 
-       /* Stereo indicator == Stereo (instead of Mono) */
+       /* stereo indicator == stereo (instead of mono) */
        if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1)
-               tuner->audmode = V4L2_TUNER_MODE_STEREO;
+               tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
        else
+               tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+       /* mono/stereo selector */
+       if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 1)
                tuner->audmode = V4L2_TUNER_MODE_MONO;
+       else
+               tuner->audmode = V4L2_TUNER_MODE_STEREO;
 
        /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
        tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI)
@@ -1474,22 +1426,27 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
                struct v4l2_tuner *tuner)
 {
        struct si470x_device *radio = video_drvdata(file);
-       int retval = 0;
+       int retval = -EINVAL;
 
        /* safety checks */
        if (radio->disconnected) {
                retval = -EIO;
                goto done;
        }
-       if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) {
-               retval = -EINVAL;
+       if (tuner->index != 0)
                goto done;
-       }
 
-       if (tuner->audmode == V4L2_TUNER_MODE_MONO)
+       /* mono/stereo selector */
+       switch (tuner->audmode) {
+       case V4L2_TUNER_MODE_MONO:
                radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
-       else
+               break;
+       case V4L2_TUNER_MODE_STEREO:
                radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
+               break;
+       default:
+               goto done;
+       }
 
        retval = si470x_set_register(radio, POWERCFG);
 
@@ -1515,11 +1472,12 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv,
                retval = -EIO;
                goto done;
        }
-       if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) {
+       if (freq->tuner != 0) {
                retval = -EINVAL;
                goto done;
        }
 
+       freq->type = V4L2_TUNER_RADIO;
        retval = si470x_get_freq(radio, &freq->frequency);
 
 done:
@@ -1544,7 +1502,7 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
                retval = -EIO;
                goto done;
        }
-       if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) {
+       if (freq->tuner != 0) {
                retval = -EINVAL;
                goto done;
        }
@@ -1573,7 +1531,7 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
                retval = -EIO;
                goto done;
        }
-       if ((seek->tuner != 0) && (seek->type != V4L2_TUNER_RADIO)) {
+       if (seek->tuner != 0) {
                retval = -EINVAL;
                goto done;
        }
@@ -1588,15 +1546,16 @@ done:
        return retval;
 }
 
+
+/*
+ * si470x_ioctl_ops - video device ioctl operations
+ */
 static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
        .vidioc_querycap        = si470x_vidioc_querycap,
-       .vidioc_g_input         = si470x_vidioc_g_input,
-       .vidioc_s_input         = si470x_vidioc_s_input,
        .vidioc_queryctrl       = si470x_vidioc_queryctrl,
        .vidioc_g_ctrl          = si470x_vidioc_g_ctrl,
        .vidioc_s_ctrl          = si470x_vidioc_s_ctrl,
        .vidioc_g_audio         = si470x_vidioc_g_audio,
-       .vidioc_s_audio         = si470x_vidioc_s_audio,
        .vidioc_g_tuner         = si470x_vidioc_g_tuner,
        .vidioc_s_tuner         = si470x_vidioc_s_tuner,
        .vidioc_g_frequency     = si470x_vidioc_g_frequency,
@@ -1604,14 +1563,15 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
        .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
 };
 
+
 /*
- * si470x_viddev_tamples - video device interface
+ * si470x_viddev_template - video device interface
  */
 static struct video_device si470x_viddev_template = {
        .fops                   = &si470x_fops,
-       .ioctl_ops              = &si470x_ioctl_ops,
        .name                   = DRIVER_NAME,
        .release                = video_device_release,
+       .ioctl_ops              = &si470x_ioctl_ops,
 };