V4L/DVB: radio-am800: let v4l2_device_(un)register handle usb_get/set_intfdata
[safe/jmp/linux-2.6] / drivers / media / radio / radio-mr800.c
index 399032e..353b828 100644 (file)
@@ -28,7 +28,7 @@
  * http://av-usbradio.sourceforge.net/index.php
  * http://sourceforge.net/projects/av-usbradio/
  * Latest release of theirs project was in 2005.
- * Probably, this driver could be improved trough using their
+ * Probably, this driver could be improved through using their
  * achievements (specifications given).
  * Also, Faidon Liambotis <paravoid@debian.org> wrote nice driver for this radio
  * in 2007. He allowed to use his driver to improve current mr800 radio driver.
@@ -85,6 +85,9 @@ MODULE_LICENSE("GPL");
 #define amradio_dev_warn(dev, fmt, arg...)                             \
                dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
 
+#define amradio_dev_err(dev, fmt, arg...) \
+               dev_err(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
+
 /* Probably USB_TIMEOUT should be modified in module parameter */
 #define BUFFER_LENGTH 8
 #define USB_TIMEOUT 500
@@ -129,6 +132,7 @@ static int usb_amradio_resume(struct usb_interface *intf);
 struct amradio_device {
        /* reference to USB and video device */
        struct usb_device *usbdev;
+       struct usb_interface *intf;
        struct video_device videodev;
        struct v4l2_device v4l2_dev;
 
@@ -137,9 +141,13 @@ struct amradio_device {
        int curfreq;
        int stereo;
        int muted;
+       int initialized;
 };
 
-#define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev)
+static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev)
+{
+       return container_of(v4l2_dev, struct amradio_device, v4l2_dev);
+}
 
 /* USB Device ID List */
 static struct usb_device_id usb_amradio_device_table[] = {
@@ -159,7 +167,7 @@ static struct usb_driver usb_amradio_driver = {
        .resume                 = usb_amradio_resume,
        .reset_resume           = usb_amradio_resume,
        .id_table               = usb_amradio_device_table,
-       .supports_autosuspend   = 0,
+       .supports_autosuspend   = 1,
 };
 
 /* switch on/off the radio. Send 8 bytes to device */
@@ -182,8 +190,10 @@ static int amradio_set_mute(struct amradio_device *radio, char argument)
        retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
-       if (retval < 0 || size != BUFFER_LENGTH)
+       if (retval < 0 || size != BUFFER_LENGTH) {
+               amradio_dev_warn(&radio->videodev.dev, "set mute failed\n");
                return retval;
+       }
 
        radio->muted = argument;
 
@@ -212,7 +222,7 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
        if (retval < 0 || size != BUFFER_LENGTH)
-               return retval;
+               goto out_err;
 
        /* frequency is calculated from freq_send and placed in first 2 bytes */
        radio->buffer[0] = (freq_send >> 8) & 0xff;
@@ -226,6 +236,15 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
        retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
+       if (retval < 0 || size != BUFFER_LENGTH)
+               goto out_err;
+
+       radio->curfreq = freq;
+       goto out;
+
+out_err:
+       amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n");
+out:
        return retval;
 }
 
@@ -249,11 +268,14 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument)
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
        if (retval < 0 || size != BUFFER_LENGTH) {
-               radio->stereo = -1;
+               amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n");
                return retval;
        }
 
-       radio->stereo = 1;
+       if (argument == WANT_STEREO)
+               radio->stereo = 1;
+       else
+               radio->stereo = 0;
 
        return retval;
 }
@@ -265,13 +287,12 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument)
  */
 static void usb_amradio_disconnect(struct usb_interface *intf)
 {
-       struct amradio_device *radio = usb_get_intfdata(intf);
+       struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
 
        mutex_lock(&radio->lock);
        radio->usbdev = NULL;
        mutex_unlock(&radio->lock);
 
-       usb_set_intfdata(intf, NULL);
        v4l2_device_disconnect(&radio->v4l2_dev);
        video_unregister_device(&radio->videodev);
 }
@@ -309,9 +330,6 @@ static int vidioc_g_tuner(struct file *file, void *priv,
  * amradio_set_stereo shouldn't be here
  */
        retval = amradio_set_stereo(radio, WANT_STEREO);
-       if (retval < 0)
-               amradio_dev_warn(&radio->videodev.dev,
-                       "set stereo failed\n");
 
        strcpy(v->name, "FM");
        v->type = V4L2_TUNER_RADIO;
@@ -343,15 +361,9 @@ static int vidioc_s_tuner(struct file *file, void *priv,
        switch (v->audmode) {
        case V4L2_TUNER_MODE_MONO:
                retval = amradio_set_stereo(radio, WANT_MONO);
-               if (retval < 0)
-                       amradio_dev_warn(&radio->videodev.dev,
-                               "set mono failed\n");
                break;
        case V4L2_TUNER_MODE_STEREO:
                retval = amradio_set_stereo(radio, WANT_STEREO);
-               if (retval < 0)
-                       amradio_dev_warn(&radio->videodev.dev,
-                               "set stereo failed\n");
                break;
        }
 
@@ -363,16 +375,10 @@ static int vidioc_s_frequency(struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
        struct amradio_device *radio = file->private_data;
-       int retval = 0;
-
-       radio->curfreq = f->frequency;
 
-       retval = amradio_setfreq(radio, radio->curfreq);
-       if (retval < 0)
-               amradio_dev_warn(&radio->videodev.dev,
-                       "set frequency failed\n");
-
-       return retval;
+       if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+               return -EINVAL;
+       return amradio_setfreq(radio, f->frequency);
 }
 
 /* vidioc_g_frequency - get tuner radio frequency */
@@ -381,6 +387,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 {
        struct amradio_device *radio = file->private_data;
 
+       if (f->tuner != 0)
+               return -EINVAL;
        f->type = V4L2_TUNER_RADIO;
        f->frequency = radio->curfreq;
 
@@ -423,19 +431,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               if (ctrl->value) {
+               if (ctrl->value)
                        retval = amradio_set_mute(radio, AMRADIO_STOP);
-                       if (retval < 0) {
-                               amradio_dev_warn(&radio->videodev.dev,
-                                       "amradio_stop failed\n");
-                       }
-               } else {
+               else
                        retval = amradio_set_mute(radio, AMRADIO_START);
-                       if (retval < 0) {
-                               amradio_dev_warn(&radio->videodev.dev,
-                                       "amradio_start failed\n");
-                       }
-               }
+
                break;
        }
 
@@ -478,10 +478,31 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
        return 0;
 }
 
+static int usb_amradio_init(struct amradio_device *radio)
+{
+       int retval;
+
+       retval = amradio_set_mute(radio, AMRADIO_STOP);
+       if (retval)
+               goto out_err;
+
+       retval = amradio_set_stereo(radio, WANT_STEREO);
+       if (retval)
+               goto out_err;
+
+       radio->initialized = 1;
+       goto out;
+
+out_err:
+       amradio_dev_err(&radio->videodev.dev, "initialization failed\n");
+out:
+       return retval;
+}
+
 /* open device - amradio_start() and amradio_setfreq() */
 static int usb_amradio_open(struct file *file)
 {
-       struct amradio_device *radio = vdev_to_amradio(video_devdata(file));
+       struct amradio_device *radio = video_drvdata(file);
        int retval = 0;
 
        mutex_lock(&radio->lock);
@@ -492,6 +513,15 @@ static int usb_amradio_open(struct file *file)
        }
 
        file->private_data = radio;
+       retval = usb_autopm_get_interface(radio->intf);
+       if (retval)
+               goto unlock;
+
+       if (unlikely(!radio->initialized)) {
+               retval = usb_amradio_init(radio);
+               if (retval)
+                       usb_autopm_put_interface(radio->intf);
+       }
 
 unlock:
        mutex_unlock(&radio->lock);
@@ -508,6 +538,8 @@ static int usb_amradio_close(struct file *file)
 
        if (!radio->usbdev)
                retval = -EIO;
+       else
+               usb_autopm_put_interface(radio->intf);
 
        mutex_unlock(&radio->lock);
        return retval;
@@ -536,14 +568,14 @@ unlock:
 /* Suspend device - stop device. Need to be checked and fixed */
 static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
 {
-       struct amradio_device *radio = usb_get_intfdata(intf);
-       int retval;
+       struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
 
        mutex_lock(&radio->lock);
 
-       retval = amradio_set_mute(radio, AMRADIO_STOP);
-       if (retval < 0)
-               dev_warn(&intf->dev, "amradio_stop failed\n");
+       if (!radio->muted && radio->initialized) {
+               amradio_set_mute(radio, AMRADIO_STOP);
+               radio->muted = 0;
+       }
 
        dev_info(&intf->dev, "going into suspend..\n");
 
@@ -554,15 +586,24 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
 /* Resume device - start device. Need to be checked and fixed */
 static int usb_amradio_resume(struct usb_interface *intf)
 {
-       struct amradio_device *radio = usb_get_intfdata(intf);
-       int retval;
+       struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
 
        mutex_lock(&radio->lock);
 
-       retval = amradio_set_mute(radio, AMRADIO_START);
-       if (retval < 0)
-               dev_warn(&intf->dev, "amradio_start failed\n");
+       if (unlikely(!radio->initialized))
+               goto unlock;
+
+       if (radio->stereo)
+               amradio_set_stereo(radio, WANT_STEREO);
+       else
+               amradio_set_stereo(radio, WANT_MONO);
+
+       amradio_setfreq(radio, radio->curfreq);
+
+       if (!radio->muted)
+               amradio_set_mute(radio, AMRADIO_START);
 
+unlock:
        dev_info(&intf->dev, "coming out of suspend..\n");
 
        mutex_unlock(&radio->lock);
@@ -594,9 +635,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
 
 static void usb_amradio_video_device_release(struct video_device *videodev)
 {
-       struct amradio_device *radio = vdev_to_amradio(videodev);
-
-       v4l2_device_unregister(&radio->v4l2_dev);
+       struct amradio_device *radio = video_get_drvdata(videodev);
 
        /* free rest memory */
        kfree(radio->buffer);
@@ -640,9 +679,8 @@ static int usb_amradio_probe(struct usb_interface *intf,
        radio->videodev.release = usb_amradio_video_device_release;
 
        radio->usbdev = interface_to_usbdev(intf);
+       radio->intf = intf;
        radio->curfreq = 95.16 * FREQ_MUL;
-       radio->stereo = -1;
-       radio->muted = 1;
 
        mutex_init(&radio->lock);
 
@@ -655,7 +693,6 @@ static int usb_amradio_probe(struct usb_interface *intf,
                goto err_vdev;
        }
 
-       usb_set_intfdata(intf, radio);
        return 0;
 
 err_vdev: