Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jlbec...
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / media / radio / dsbr100.c
index 6135762022940ec4095538bfa446803fb2be6373..3d8cc425fa6b6206e394548e8abdf482f7aa3a73 100644 (file)
 
  History:
 
+ Version 0.46:
+       Removed usb_dsbr100_open/close calls and radio->users counter. Also,
+       radio->muted changed to radio->status and suspend/resume calls updated.
+
  Version 0.45:
        Converted to v4l2_device.
 
  */
 #include <linux/version.h>     /* for KERNEL_VERSION MACRO     */
 
-#define DRIVER_VERSION "v0.45"
-#define RADIO_VERSION KERNEL_VERSION(0, 4, 5)
+#define DRIVER_VERSION "v0.46"
+#define RADIO_VERSION KERNEL_VERSION(0, 4, 6)
 
 #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
 #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
@@ -121,13 +125,15 @@ devices, that would be 76 and 91.  */
 #define FREQ_MAX 108.0
 #define FREQ_MUL 16000
 
-#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
+/* defines for radio->status */
+#define STARTED        0
+#define STOPPED        1
+
+#define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev)
 
 static int usb_dsbr100_probe(struct usb_interface *intf,
                             const struct usb_device_id *id);
 static void usb_dsbr100_disconnect(struct usb_interface *intf);
-static int usb_dsbr100_open(struct file *file);
-static int usb_dsbr100_close(struct file *file);
 static int usb_dsbr100_suspend(struct usb_interface *intf,
                                                pm_message_t message);
 static int usb_dsbr100_resume(struct usb_interface *intf);
@@ -142,12 +148,10 @@ struct dsbr100_device {
        struct v4l2_device v4l2_dev;
 
        u8 *transfer_buffer;
-       struct mutex lock;      /* buffer locking */
+       struct mutex v4l2_lock;
        int curfreq;
        int stereo;
-       int users;
-       int removed;
-       int muted;
+       int status;
 };
 
 static struct usb_device_id usb_dsbr100_device_table [] = {
@@ -177,8 +181,6 @@ static int dsbr100_start(struct dsbr100_device *radio)
        int retval;
        int request;
 
-       mutex_lock(&radio->lock);
-
        retval = usb_control_msg(radio->usbdev,
                usb_rcvctrlpipe(radio->usbdev, 0),
                USB_REQ_GET_STATUS,
@@ -201,12 +203,10 @@ static int dsbr100_start(struct dsbr100_device *radio)
                goto usb_control_msg_failed;
        }
 
-       radio->muted = 0;
-       mutex_unlock(&radio->lock);
+       radio->status = STARTED;
        return (radio->transfer_buffer)[0];
 
 usb_control_msg_failed:
-       mutex_unlock(&radio->lock);
        dev_err(&radio->usbdev->dev,
                "%s - usb_control_msg returned %i, request %i\n",
                        __func__, retval, request);
@@ -220,8 +220,6 @@ static int dsbr100_stop(struct dsbr100_device *radio)
        int retval;
        int request;
 
-       mutex_lock(&radio->lock);
-
        retval = usb_control_msg(radio->usbdev,
                usb_rcvctrlpipe(radio->usbdev, 0),
                USB_REQ_GET_STATUS,
@@ -244,12 +242,10 @@ static int dsbr100_stop(struct dsbr100_device *radio)
                goto usb_control_msg_failed;
        }
 
-       radio->muted = 1;
-       mutex_unlock(&radio->lock);
+       radio->status = STOPPED;
        return (radio->transfer_buffer)[0];
 
 usb_control_msg_failed:
-       mutex_unlock(&radio->lock);
        dev_err(&radio->usbdev->dev,
                "%s - usb_control_msg returned %i, request %i\n",
                        __func__, retval, request);
@@ -258,13 +254,11 @@ usb_control_msg_failed:
 }
 
 /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
-static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
+static int dsbr100_setfreq(struct dsbr100_device *radio)
 {
        int retval;
        int request;
-
-       freq = (freq / 16 * 80) / 1000 + 856;
-       mutex_lock(&radio->lock);
+       int freq = (radio->curfreq / 16 * 80) / 1000 + 856;
 
        retval = usb_control_msg(radio->usbdev,
                usb_rcvctrlpipe(radio->usbdev, 0),
@@ -301,12 +295,10 @@ static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
        }
 
        radio->stereo = !((radio->transfer_buffer)[0] & 0x01);
-       mutex_unlock(&radio->lock);
        return (radio->transfer_buffer)[0];
 
 usb_control_msg_failed:
        radio->stereo = -1;
-       mutex_unlock(&radio->lock);
        dev_err(&radio->usbdev->dev,
                "%s - usb_control_msg returned %i, request %i\n",
                        __func__, retval, request);
@@ -319,8 +311,6 @@ static void dsbr100_getstat(struct dsbr100_device *radio)
 {
        int retval;
 
-       mutex_lock(&radio->lock);
-
        retval = usb_control_msg(radio->usbdev,
                usb_rcvctrlpipe(radio->usbdev, 0),
                USB_REQ_GET_STATUS,
@@ -335,33 +325,8 @@ static void dsbr100_getstat(struct dsbr100_device *radio)
        } else {
                radio->stereo = !(radio->transfer_buffer[0] & 0x01);
        }
-
-       mutex_unlock(&radio->lock);
 }
 
-/* USB subsystem interface begins here */
-
-/*
- * Handle unplugging of the device.
- * We call video_unregister_device in any case.
- * The last function called in this procedure is
- * usb_dsbr100_video_device_release
- */
-static void usb_dsbr100_disconnect(struct usb_interface *intf)
-{
-       struct dsbr100_device *radio = usb_get_intfdata(intf);
-
-       usb_set_intfdata (intf, NULL);
-
-       mutex_lock(&radio->lock);
-       radio->removed = 1;
-       mutex_unlock(&radio->lock);
-
-       video_unregister_device(&radio->videodev);
-       v4l2_device_disconnect(&radio->v4l2_dev);
-}
-
-
 static int vidioc_querycap(struct file *file, void *priv,
                                        struct v4l2_capability *v)
 {
@@ -380,10 +345,6 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 {
        struct dsbr100_device *radio = video_drvdata(file);
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
        if (v->index > 0)
                return -EINVAL;
 
@@ -405,16 +366,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 static int vidioc_s_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
-       struct dsbr100_device *radio = video_drvdata(file);
-
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
-       if (v->index > 0)
-               return -EINVAL;
-
-       return 0;
+       return v->index ? -EINVAL : 0;
 }
 
 static int vidioc_s_frequency(struct file *file, void *priv,
@@ -423,15 +375,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
        struct dsbr100_device *radio = video_drvdata(file);
        int retval;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
-       mutex_lock(&radio->lock);
        radio->curfreq = f->frequency;
-       mutex_unlock(&radio->lock);
 
-       retval = dsbr100_setfreq(radio, radio->curfreq);
+       retval = dsbr100_setfreq(radio);
        if (retval < 0)
                dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
        return 0;
@@ -442,10 +388,6 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 {
        struct dsbr100_device *radio = video_drvdata(file);
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
        f->type = V4L2_TUNER_RADIO;
        f->frequency = radio->curfreq;
        return 0;
@@ -467,13 +409,9 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
 {
        struct dsbr100_device *radio = video_drvdata(file);
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               ctrl->value = radio->muted;
+               ctrl->value = radio->status;
                return 0;
        }
        return -EINVAL;
@@ -485,10 +423,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
        struct dsbr100_device *radio = video_drvdata(file);
        int retval;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
                if (ctrl->value) {
@@ -530,78 +464,57 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
 
 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 {
-       if (i != 0)
-               return -EINVAL;
-       return 0;
+       return i ? -EINVAL : 0;
 }
 
 static int vidioc_s_audio(struct file *file, void *priv,
                                        struct v4l2_audio *a)
 {
-       if (a->index != 0)
-               return -EINVAL;
-       return 0;
+       return a->index ? -EINVAL : 0;
 }
 
-static int usb_dsbr100_open(struct file *file)
-{
-       struct dsbr100_device *radio = video_drvdata(file);
-       int retval;
-
-       lock_kernel();
-       radio->users = 1;
-       radio->muted = 1;
-
-       retval = dsbr100_start(radio);
-       if (retval < 0) {
-               dev_warn(&radio->usbdev->dev,
-                        "Radio did not start up properly\n");
-               radio->users = 0;
-               unlock_kernel();
-               return -EIO;
-       }
-
-       retval = dsbr100_setfreq(radio, radio->curfreq);
-       if (retval < 0)
-               dev_warn(&radio->usbdev->dev,
-                       "set frequency failed\n");
-
-       unlock_kernel();
-       return 0;
-}
+/* USB subsystem interface begins here */
 
-static int usb_dsbr100_close(struct file *file)
+/*
+ * Handle unplugging of the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_dsbr100_video_device_release
+ */
+static void usb_dsbr100_disconnect(struct usb_interface *intf)
 {
-       struct dsbr100_device *radio = video_drvdata(file);
-       int retval;
-
-       if (!radio)
-               return -ENODEV;
-
-       mutex_lock(&radio->lock);
-       radio->users = 0;
-       mutex_unlock(&radio->lock);
-
-       if (!radio->removed) {
-               retval = dsbr100_stop(radio);
-               if (retval < 0) {
-                       dev_warn(&radio->usbdev->dev,
-                               "dsbr100_stop failed\n");
-               }
+       struct dsbr100_device *radio = usb_get_intfdata(intf);
 
-       }
-       return 0;
+       v4l2_device_get(&radio->v4l2_dev);
+       mutex_lock(&radio->v4l2_lock);
+       usb_set_intfdata(intf, NULL);
+       video_unregister_device(&radio->videodev);
+       v4l2_device_disconnect(&radio->v4l2_dev);
+       mutex_unlock(&radio->v4l2_lock);
+       v4l2_device_put(&radio->v4l2_dev);
 }
 
+
 /* Suspend device - stop device. */
 static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct dsbr100_device *radio = usb_get_intfdata(intf);
        int retval;
 
-       retval = dsbr100_stop(radio);
-       if (retval < 0)
-               dev_warn(&intf->dev, "dsbr100_stop failed\n");
+       mutex_lock(&radio->v4l2_lock);
+       if (radio->status == STARTED) {
+               retval = dsbr100_stop(radio);
+               if (retval < 0)
+                       dev_warn(&intf->dev, "dsbr100_stop failed\n");
+
+               /* After dsbr100_stop() status set to STOPPED.
+                * If we want driver to start radio on resume
+                * we set status equal to STARTED.
+                * On resume we will check status and run radio if needed.
+                */
+               radio->status = STARTED;
+       }
+       mutex_unlock(&radio->v4l2_lock);
 
        dev_info(&intf->dev, "going into suspend..\n");
 
@@ -614,9 +527,13 @@ static int usb_dsbr100_resume(struct usb_interface *intf)
        struct dsbr100_device *radio = usb_get_intfdata(intf);
        int retval;
 
-       retval = dsbr100_start(radio);
-       if (retval < 0)
-               dev_warn(&intf->dev, "dsbr100_start failed\n");
+       mutex_lock(&radio->v4l2_lock);
+       if (radio->status == STARTED) {
+               retval = dsbr100_start(radio);
+               if (retval < 0)
+                       dev_warn(&intf->dev, "dsbr100_start failed\n");
+       }
+       mutex_unlock(&radio->v4l2_lock);
 
        dev_info(&intf->dev, "coming out of suspend..\n");
 
@@ -624,9 +541,9 @@ static int usb_dsbr100_resume(struct usb_interface *intf)
 }
 
 /* free data structures */
-static void usb_dsbr100_video_device_release(struct video_device *videodev)
+static void usb_dsbr100_release(struct v4l2_device *v4l2_dev)
 {
-       struct dsbr100_device *radio = videodev_to_radio(videodev);
+       struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev);
 
        v4l2_device_unregister(&radio->v4l2_dev);
        kfree(radio->transfer_buffer);
@@ -636,9 +553,7 @@ static void usb_dsbr100_video_device_release(struct video_device *videodev)
 /* File system interface */
 static const struct v4l2_file_operations usb_dsbr100_fops = {
        .owner          = THIS_MODULE,
-       .open           = usb_dsbr100_open,
-       .release        = usb_dsbr100_close,
-       .ioctl          = video_ioctl2,
+       .unlocked_ioctl = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
@@ -677,6 +592,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
        }
 
        v4l2_dev = &radio->v4l2_dev;
+       v4l2_dev->release = usb_dsbr100_release;
 
        retval = v4l2_device_register(&intf->dev, v4l2_dev);
        if (retval < 0) {
@@ -686,18 +602,17 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
                return retval;
        }
 
+       mutex_init(&radio->v4l2_lock);
        strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name));
        radio->videodev.v4l2_dev = v4l2_dev;
        radio->videodev.fops = &usb_dsbr100_fops;
        radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops;
-       radio->videodev.release = usb_dsbr100_video_device_release;
-
-       mutex_init(&radio->lock);
+       radio->videodev.release = video_device_release_empty;
+       radio->videodev.lock = &radio->v4l2_lock;
 
-       radio->removed = 0;
-       radio->users = 0;
        radio->usbdev = interface_to_usbdev(intf);
        radio->curfreq = FREQ_MIN * FREQ_MUL;
+       radio->status = STOPPED;
 
        video_set_drvdata(&radio->videodev, radio);