[media] radio-si470x-usb: remove autosuspend, implement suspend/resume

The radio-si470x-usb driver supported both autosuspend and it stopped the
radio the moment the last user of the radio device closed it. However, that
was very confusing since if you play the audio from the device (e.g. through
arecord -D ... | aplay) then no sound would play unless you had the radio
device open at the same time, even though there is no need to do anything
with that node.

On the other hand, the actual suspend/resume functions didn't do anything,
which would fail if you *did* have the radio node open at that time.

So:

- remove autosuspend (bad idea in general for USB radio devices)
- move the start/stop out of the open/release functions into the resume/suspend
  functions.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Tobias Lorenz <tobias.lorenz@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Hans Verkuil 2012-05-04 09:42:29 -03:00 committed by Mauro Carvalho Chehab
parent 340bd4c16c
commit 6fd522a6d1
2 changed files with 70 additions and 80 deletions

View file

@ -570,7 +570,6 @@ static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
else else
radio->registers[POWERCFG] |= POWERCFG_DMUTE; radio->registers[POWERCFG] |= POWERCFG_DMUTE;
return si470x_set_register(radio, POWERCFG); return si470x_set_register(radio, POWERCFG);
break;
default: default:
return -EINVAL; return -EINVAL;
} }

View file

@ -481,91 +481,20 @@ static void si470x_int_in_callback(struct urb *urb)
} }
/**************************************************************************
* File Operations Interface
**************************************************************************/
/*
* si470x_fops_open - file open
*/
int si470x_fops_open(struct file *file) int si470x_fops_open(struct file *file)
{ {
struct si470x_device *radio = video_drvdata(file); return v4l2_fh_open(file);
int retval = v4l2_fh_open(file);
if (retval)
return retval;
retval = usb_autopm_get_interface(radio->intf);
if (retval < 0)
goto done;
if (v4l2_fh_is_singular_file(file)) {
/* start radio */
retval = si470x_start(radio);
if (retval < 0) {
usb_autopm_put_interface(radio->intf);
goto done;
}
/* initialize interrupt urb */
usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
usb_rcvintpipe(radio->usbdev,
radio->int_in_endpoint->bEndpointAddress),
radio->int_in_buffer,
le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
si470x_int_in_callback,
radio,
radio->int_in_endpoint->bInterval);
radio->int_in_running = 1;
mb();
retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
if (retval) {
dev_info(&radio->intf->dev,
"submitting int urb failed (%d)\n", retval);
radio->int_in_running = 0;
usb_autopm_put_interface(radio->intf);
}
}
done:
if (retval)
v4l2_fh_release(file);
return retval;
} }
/*
* si470x_fops_release - file release
*/
int si470x_fops_release(struct file *file) int si470x_fops_release(struct file *file)
{ {
struct si470x_device *radio = video_drvdata(file);
if (v4l2_fh_is_singular_file(file)) {
/* shutdown interrupt handler */
if (radio->int_in_running) {
radio->int_in_running = 0;
if (radio->int_in_urb)
usb_kill_urb(radio->int_in_urb);
}
/* cancel read processes */
wake_up_interruptible(&radio->read_queue);
/* stop radio */
si470x_stop(radio);
usb_autopm_put_interface(radio->intf);
}
return v4l2_fh_release(file); return v4l2_fh_release(file);
} }
static void si470x_usb_release(struct video_device *vdev) static void si470x_usb_release(struct v4l2_device *v4l2_dev)
{ {
struct si470x_device *radio = video_get_drvdata(vdev); struct si470x_device *radio =
container_of(v4l2_dev, struct si470x_device, v4l2_dev);
usb_free_urb(radio->int_in_urb); usb_free_urb(radio->int_in_urb);
v4l2_ctrl_handler_free(&radio->hdl); v4l2_ctrl_handler_free(&radio->hdl);
@ -599,6 +528,38 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
} }
static int si470x_start_usb(struct si470x_device *radio)
{
int retval;
/* start radio */
retval = si470x_start(radio);
if (retval < 0)
return retval;
v4l2_ctrl_handler_setup(&radio->hdl);
/* initialize interrupt urb */
usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
usb_rcvintpipe(radio->usbdev,
radio->int_in_endpoint->bEndpointAddress),
radio->int_in_buffer,
le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
si470x_int_in_callback,
radio,
radio->int_in_endpoint->bInterval);
radio->int_in_running = 1;
mb();
retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
if (retval) {
dev_info(&radio->intf->dev,
"submitting int urb failed (%d)\n", retval);
radio->int_in_running = 0;
}
return retval;
}
/************************************************************************** /**************************************************************************
* USB Interface * USB Interface
@ -658,6 +619,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
goto err_intbuffer; goto err_intbuffer;
} }
radio->v4l2_dev.release = si470x_usb_release;
retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
if (retval < 0) { if (retval < 0) {
dev_err(&intf->dev, "couldn't register v4l2_device\n"); dev_err(&intf->dev, "couldn't register v4l2_device\n");
@ -678,7 +640,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
radio->videodev.ctrl_handler = &radio->hdl; radio->videodev.ctrl_handler = &radio->hdl;
radio->videodev.lock = &radio->lock; radio->videodev.lock = &radio->lock;
radio->videodev.v4l2_dev = &radio->v4l2_dev; radio->videodev.v4l2_dev = &radio->v4l2_dev;
radio->videodev.release = si470x_usb_release; radio->videodev.release = video_device_release_empty;
set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags);
video_set_drvdata(&radio->videodev, radio); video_set_drvdata(&radio->videodev, radio);
@ -754,11 +716,16 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
init_waitqueue_head(&radio->read_queue); init_waitqueue_head(&radio->read_queue);
usb_set_intfdata(intf, radio); usb_set_intfdata(intf, radio);
/* start radio */
retval = si470x_start_usb(radio);
if (retval < 0)
goto err_all;
/* register video device */ /* register video device */
retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
radio_nr); radio_nr);
if (retval) { if (retval) {
dev_warn(&intf->dev, "Could not register video device\n"); dev_err(&intf->dev, "Could not register video device\n");
goto err_all; goto err_all;
} }
@ -786,8 +753,22 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
static int si470x_usb_driver_suspend(struct usb_interface *intf, static int si470x_usb_driver_suspend(struct usb_interface *intf,
pm_message_t message) pm_message_t message)
{ {
struct si470x_device *radio = usb_get_intfdata(intf);
dev_info(&intf->dev, "suspending now...\n"); dev_info(&intf->dev, "suspending now...\n");
/* shutdown interrupt handler */
if (radio->int_in_running) {
radio->int_in_running = 0;
if (radio->int_in_urb)
usb_kill_urb(radio->int_in_urb);
}
/* cancel read processes */
wake_up_interruptible(&radio->read_queue);
/* stop radio */
si470x_stop(radio);
return 0; return 0;
} }
@ -797,9 +778,12 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf,
*/ */
static int si470x_usb_driver_resume(struct usb_interface *intf) static int si470x_usb_driver_resume(struct usb_interface *intf)
{ {
struct si470x_device *radio = usb_get_intfdata(intf);
dev_info(&intf->dev, "resuming now...\n"); dev_info(&intf->dev, "resuming now...\n");
return 0; /* start radio */
return si470x_start_usb(radio);
} }
@ -815,11 +799,18 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
video_unregister_device(&radio->videodev); video_unregister_device(&radio->videodev);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
mutex_unlock(&radio->lock); mutex_unlock(&radio->lock);
v4l2_device_put(&radio->v4l2_dev);
} }
/* /*
* si470x_usb_driver - usb driver interface * si470x_usb_driver - usb driver interface
*
* A note on suspend/resume: this driver had only empty suspend/resume
* functions, and when I tried to test suspend/resume it always disconnected
* instead of resuming (using my ADS InstantFM stick). So I've decided to
* remove these callbacks until someone else with better hardware can
* implement and test this.
*/ */
static struct usb_driver si470x_usb_driver = { static struct usb_driver si470x_usb_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
@ -827,8 +818,8 @@ static struct usb_driver si470x_usb_driver = {
.disconnect = si470x_usb_driver_disconnect, .disconnect = si470x_usb_driver_disconnect,
.suspend = si470x_usb_driver_suspend, .suspend = si470x_usb_driver_suspend,
.resume = si470x_usb_driver_resume, .resume = si470x_usb_driver_resume,
.reset_resume = si470x_usb_driver_resume,
.id_table = si470x_usb_driver_id_table, .id_table = si470x_usb_driver_id_table,
.supports_autosuspend = 1,
}; };
module_usb_driver(si470x_usb_driver); module_usb_driver(si470x_usb_driver);