V4L/DVB (13550): v4l: Use the new video_device_node_name function
[linux-2.6.git] / drivers / media / radio / dsbr100.c
index a5ca176..ed9cd7a 100644 (file)
@@ -1,5 +1,5 @@
-/* A driver for the D-Link DSB-R100 USB radio.  The R100 plugs
- into both the USB and an analog audio input, so this thing
+/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
+ The device plugs into both the USB and an analog audio input, so this thing
  only deals with initialisation and frequency setting, the
  audio data has to be handled by a sound driver.
 
 
  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.
+
+ Version 0.44:
+       Add suspend/resume functions, fix unplug of device,
+       a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com>
+
  Version 0.43:
        Oliver Neukum: avoided DMA coherency issue
 
@@ -84,7 +95,7 @@
 #include <linux/slab.h>
 #include <linux/input.h>
 #include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <linux/usb.h>
 
  */
 #include <linux/version.h>     /* for KERNEL_VERSION MACRO     */
 
-#define DRIVER_VERSION "v0.41"
-#define RADIO_VERSION KERNEL_VERSION(0,4,1)
-
-static struct v4l2_queryctrl radio_qctrl[] = {
-       {
-               .id            = V4L2_CID_AUDIO_MUTE,
-               .name          = "Mute",
-               .minimum       = 0,
-               .maximum       = 1,
-               .default_value = 1,
-               .type          = V4L2_CTRL_TYPE_BOOLEAN,
-       }
-};
+#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"
@@ -125,12 +125,18 @@ devices, that would be 76 and 91.  */
 #define FREQ_MAX 108.0
 #define FREQ_MUL 16000
 
+/* defines for radio->status */
+#define STARTED        0
+#define STOPPED        1
+
+#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
 
 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 inode *inode, struct file *file);
-static int usb_dsbr100_close(struct inode *inode, 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);
 
 static int radio_nr = -1;
 module_param(radio_nr, int, 0);
@@ -138,16 +144,17 @@ module_param(radio_nr, int, 0);
 /* Data for one (physical) device */
 struct dsbr100_device {
        struct usb_device *usbdev;
-       struct video_device *videodev;
+       struct video_device videodev;
+       struct v4l2_device v4l2_dev;
+
        u8 *transfer_buffer;
+       struct mutex lock;      /* buffer locking */
        int curfreq;
        int stereo;
-       int users;
        int removed;
-       int muted;
+       int status;
 };
 
-
 static struct usb_device_id usb_dsbr100_device_table [] = {
        { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
        { }                                             /* Terminating entry */
@@ -157,10 +164,14 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
 
 /* USB subsystem interface */
 static struct usb_driver usb_dsbr100_driver = {
-       .name =         "dsbr100",
-       .probe =        usb_dsbr100_probe,
-       .disconnect =   usb_dsbr100_disconnect,
-       .id_table =     usb_dsbr100_device_table,
+       .name                   = "dsbr100",
+       .probe                  = usb_dsbr100_probe,
+       .disconnect             = usb_dsbr100_disconnect,
+       .id_table               = usb_dsbr100_device_table,
+       .suspend                = usb_dsbr100_suspend,
+       .resume                 = usb_dsbr100_resume,
+       .reset_resume           = usb_dsbr100_resume,
+       .supports_autosuspend   = 0,
 };
 
 /* Low-level device interface begins here */
@@ -168,104 +179,202 @@ static struct usb_driver usb_dsbr100_driver = {
 /* switch on radio */
 static int dsbr100_start(struct dsbr100_device *radio)
 {
-       if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       USB_REQ_GET_STATUS,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                       0x00, 0xC7, radio->transfer_buffer, 8, 300) < 0 ||
-       usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       DSB100_ONOFF,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                       0x01, 0x00, radio->transfer_buffer, 8, 300) < 0)
-               return -1;
-       radio->muted=0;
+       int retval;
+       int request;
+
+       mutex_lock(&radio->lock);
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               USB_REQ_GET_STATUS,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+               0x00, 0xC7, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = USB_REQ_GET_STATUS;
+               goto usb_control_msg_failed;
+       }
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               DSB100_ONOFF,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+               0x01, 0x00, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = DSB100_ONOFF;
+               goto usb_control_msg_failed;
+       }
+
+       radio->status = STARTED;
+       mutex_unlock(&radio->lock);
        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);
+       return retval;
+
+}
 
 /* switch off radio */
 static int dsbr100_stop(struct dsbr100_device *radio)
 {
-       if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       USB_REQ_GET_STATUS,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                       0x16, 0x1C, radio->transfer_buffer, 8, 300) < 0 ||
-       usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       DSB100_ONOFF,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                       0x00, 0x00, radio->transfer_buffer, 8, 300) < 0)
-               return -1;
-       radio->muted=1;
+       int retval;
+       int request;
+
+       mutex_lock(&radio->lock);
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               USB_REQ_GET_STATUS,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+               0x16, 0x1C, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = USB_REQ_GET_STATUS;
+               goto usb_control_msg_failed;
+       }
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               DSB100_ONOFF,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+               0x00, 0x00, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = DSB100_ONOFF;
+               goto usb_control_msg_failed;
+       }
+
+       radio->status = STOPPED;
+       mutex_unlock(&radio->lock);
        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);
+       return retval;
+
 }
 
 /* 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)
 {
-       freq = (freq / 16 * 80) / 1000 + 856;
-       if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       DSB100_TUNE,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                       (freq >> 8) & 0x00ff, freq & 0xff,
-                       radio->transfer_buffer, 8, 300) < 0 ||
-          usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       USB_REQ_GET_STATUS,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                       0x96, 0xB7, radio->transfer_buffer, 8, 300) < 0 ||
-       usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
-                       USB_REQ_GET_STATUS,
-                       USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
-                       0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) {
-               radio->stereo = -1;
-               return -1;
+       int retval;
+       int request;
+       int freq = (radio->curfreq / 16 * 80) / 1000 + 856;
+
+       mutex_lock(&radio->lock);
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               DSB100_TUNE,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+               (freq >> 8) & 0x00ff, freq & 0xff,
+               radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = DSB100_TUNE;
+               goto usb_control_msg_failed;
+       }
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               USB_REQ_GET_STATUS,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+               0x96, 0xB7, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = USB_REQ_GET_STATUS;
+               goto usb_control_msg_failed;
+       }
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
+               USB_REQ_GET_STATUS,
+               USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
+               0x00, 0x24, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
+               request = USB_REQ_GET_STATUS;
+               goto usb_control_msg_failed;
        }
+
        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);
+       return retval;
 }
 
 /* return the device status.  This is, in effect, just whether it
 sees a stereo signal or not.  Pity. */
 static void dsbr100_getstat(struct dsbr100_device *radio)
 {
-       if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+       int retval;
+
+       mutex_lock(&radio->lock);
+
+       retval = usb_control_msg(radio->usbdev,
+               usb_rcvctrlpipe(radio->usbdev, 0),
                USB_REQ_GET_STATUS,
                USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-               0x00 , 0x24, radio->transfer_buffer, 8, 300) < 0)
+               0x00 , 0x24, radio->transfer_buffer, 8, 300);
+
+       if (retval < 0) {
                radio->stereo = -1;
-       else
+               dev_err(&radio->usbdev->dev,
+                       "%s - usb_control_msg returned %i, request %i\n",
+                               __func__, retval, USB_REQ_GET_STATUS);
+       } else {
                radio->stereo = !(radio->transfer_buffer[0] & 0x01);
-}
+       }
 
+       mutex_unlock(&radio->lock);
+}
 
 /* USB subsystem interface begins here */
 
-/* handle unplugging of the device, release data structures
-if nothing keeps us from doing it.  If something is still
-keeping us busy, the release callback of v4l will take care
-of releasing it. */
+/*
+ * 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);
-       if (radio) {
-               video_unregister_device(radio->videodev);
-               radio->videodev = NULL;
-               if (radio->users) {
-                       kfree(radio->transfer_buffer);
-                       kfree(radio);
-               } else {
-                       radio->removed = 1;
-               }
-       }
+
+       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)
 {
+       struct dsbr100_device *radio = video_drvdata(file);
+
        strlcpy(v->driver, "dsbr100", sizeof(v->driver));
        strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
-       sprintf(v->bus_info, "USB");
+       usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
        v->version = RADIO_VERSION;
        v->capabilities = V4L2_CAP_TUNER;
        return 0;
@@ -276,6 +385,10 @@ 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;
 
@@ -297,6 +410,12 @@ 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;
 
@@ -307,9 +426,18 @@ static int vidioc_s_frequency(struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
        struct dsbr100_device *radio = video_drvdata(file);
+       int retval;
 
+       /* safety check */
+       if (radio->removed)
+               return -EIO;
+
+       mutex_lock(&radio->lock);
        radio->curfreq = f->frequency;
-       if (dsbr100_setfreq(radio, radio->curfreq) == -1)
+       mutex_unlock(&radio->lock);
+
+       retval = dsbr100_setfreq(radio);
+       if (retval < 0)
                dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
        return 0;
 }
@@ -319,6 +447,10 @@ 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;
@@ -327,14 +459,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 static int vidioc_queryctrl(struct file *file, void *priv,
                                struct v4l2_queryctrl *qc)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
-               if (qc->id && qc->id == radio_qctrl[i].id) {
-                       memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
-                       return 0;
-               }
+       switch (qc->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
        }
+
        return -EINVAL;
 }
 
@@ -343,9 +472,13 @@ 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;
@@ -355,17 +488,24 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
                                struct v4l2_control *ctrl)
 {
        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) {
-                       if (dsbr100_stop(radio) == -1) {
+                       retval = dsbr100_stop(radio);
+                       if (retval < 0) {
                                dev_warn(&radio->usbdev->dev,
                                         "Radio did not respond properly\n");
                                return -EBUSY;
                        }
                } else {
-                       if (dsbr100_start(radio) == -1) {
+                       retval = dsbr100_start(radio);
+                       if (retval < 0) {
                                dev_warn(&radio->usbdev->dev,
                                         "Radio did not respond properly\n");
                                return -EBUSY;
@@ -408,56 +548,64 @@ static int vidioc_s_audio(struct file *file, void *priv,
        return 0;
 }
 
-static int usb_dsbr100_open(struct inode *inode, struct file *file)
+/* Suspend device - stop device. */
+static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
 {
-       struct dsbr100_device *radio = video_drvdata(file);
+       struct dsbr100_device *radio = usb_get_intfdata(intf);
        int retval;
 
-       lock_kernel();
-       radio->users = 1;
-       radio->muted = 1;
+       if (radio->status == STARTED) {
+               retval = dsbr100_stop(radio);
+               if (retval < 0)
+                       dev_warn(&intf->dev, "dsbr100_stop failed\n");
 
-       if (dsbr100_start(radio) < 0) {
-               dev_warn(&radio->usbdev->dev,
-                        "Radio did not start up properly\n");
-               radio->users = 0;
-               unlock_kernel();
-               return -EIO;
-       }
+               /* 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.
+                */
 
-       retval = dsbr100_setfreq(radio, radio->curfreq);
+               mutex_lock(&radio->lock);
+               radio->status = STARTED;
+               mutex_unlock(&radio->lock);
+       }
 
-       if (retval == -1)
-               printk(KERN_WARNING KBUILD_MODNAME ": Set frequency failed\n");
+       dev_info(&intf->dev, "going into suspend..\n");
 
-       unlock_kernel();
        return 0;
 }
 
-static int usb_dsbr100_close(struct inode *inode, struct file *file)
+/* Resume device - start device. */
+static int usb_dsbr100_resume(struct usb_interface *intf)
 {
-       struct dsbr100_device *radio = video_drvdata(file);
+       struct dsbr100_device *radio = usb_get_intfdata(intf);
+       int retval;
 
-       if (!radio)
-               return -ENODEV;
-       radio->users = 0;
-       if (radio->removed) {
-               kfree(radio->transfer_buffer);
-               kfree(radio);
+       if (radio->status == STARTED) {
+               retval = dsbr100_start(radio);
+               if (retval < 0)
+                       dev_warn(&intf->dev, "dsbr100_start failed\n");
        }
+
+       dev_info(&intf->dev, "coming out of suspend..\n");
+
        return 0;
 }
 
+/* free data structures */
+static void usb_dsbr100_video_device_release(struct video_device *videodev)
+{
+       struct dsbr100_device *radio = videodev_to_radio(videodev);
+
+       v4l2_device_unregister(&radio->v4l2_dev);
+       kfree(radio->transfer_buffer);
+       kfree(radio);
+}
+
 /* File system interface */
-static const struct file_operations usb_dsbr100_fops = {
+static const struct v4l2_file_operations usb_dsbr100_fops = {
        .owner          = THIS_MODULE,
-       .open           = usb_dsbr100_open,
-       .release        = usb_dsbr100_close,
        .ioctl          = video_ioctl2,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl   = v4l_compat_ioctl32,
-#endif
-       .llseek         = no_llseek,
 };
 
 static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
@@ -475,22 +623,15 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
        .vidioc_s_input     = vidioc_s_input,
 };
 
-/* V4L2 interface */
-static struct video_device dsbr100_videodev_template = {
-       .name           = "D-Link DSB-R 100",
-       .fops           = &usb_dsbr100_fops,
-       .ioctl_ops      = &usb_dsbr100_ioctl_ops,
-       .release        = video_device_release,
-};
-
-/* check if the device is present and register with v4l and
-usb if it is */
+/* check if the device is present and register with v4l and usb if it is */
 static int usb_dsbr100_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
        struct dsbr100_device *radio;
+       struct v4l2_device *v4l2_dev;
+       int retval;
 
-       radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
+       radio = kzalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
 
        if (!radio)
                return -ENOMEM;
@@ -501,23 +642,36 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
                kfree(radio);
                return -ENOMEM;
        }
-       radio->videodev = video_device_alloc();
 
-       if (!(radio->videodev)) {
+       v4l2_dev = &radio->v4l2_dev;
+
+       retval = v4l2_device_register(&intf->dev, v4l2_dev);
+       if (retval < 0) {
+               v4l2_err(v4l2_dev, "couldn't register v4l2_device\n");
                kfree(radio->transfer_buffer);
                kfree(radio);
-               return -ENOMEM;
+               return retval;
        }
-       memcpy(radio->videodev, &dsbr100_videodev_template,
-               sizeof(dsbr100_videodev_template));
+
+       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->removed = 0;
-       radio->users = 0;
        radio->usbdev = interface_to_usbdev(intf);
        radio->curfreq = FREQ_MIN * FREQ_MUL;
-       video_set_drvdata(radio->videodev, radio);
-       if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) {
-               dev_warn(&intf->dev, "Could not register video device\n");
-               video_device_release(radio->videodev);
+       radio->status = STOPPED;
+
+       video_set_drvdata(&radio->videodev, radio);
+
+       retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
+       if (retval < 0) {
+               v4l2_err(v4l2_dev, "couldn't register video device\n");
+               v4l2_device_unregister(v4l2_dev);
                kfree(radio->transfer_buffer);
                kfree(radio);
                return -EIO;