Commit 21f5a32e authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab

[media] dvb_usb_v2: delay firmware download as it blocks module init

Delay firmware download and whole driver initialization using
workqueue. udev causes problems when blocking firmware download
was done during module init. This will likely resolve all
DVB USB firmware downloading issues we have had during recent years.

Fixes bug in case of DVB USB driver:
https://bugzilla.redhat.com/show_bug.cgi?id=827538Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 1c9c73b7
......@@ -217,7 +217,7 @@ int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff)
/*
* USB
*/
int dvb_usbv2_device_init(struct usb_interface *intf,
int dvb_usbv2_device_init_(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
......@@ -295,12 +295,85 @@ int dvb_usbv2_device_init(struct usb_interface *intf,
return ret;
}
/*
* udev, which is used for the firmware downloading, requires we cannot
* block during module_init(). module_init() calls USB probe() which
* is this routine. Due to that we delay actual operation using workqueue
* and return always success here.
*/
struct dvb_usb_delayed_init {
struct usb_interface *intf;
const struct usb_device_id *id;
struct work_struct work;
};
static void dvb_usbv2_init_work(struct work_struct *work)
{
int ret;
struct dvb_usb_delayed_init *delayed_init =
container_of(work, struct dvb_usb_delayed_init, work);
ret = dvb_usbv2_device_init_(delayed_init->intf, delayed_init->id);
if (ret < 0) {
usb_driver_release_interface(
to_usb_driver(delayed_init->intf->dev.driver),
delayed_init->intf);
kfree(delayed_init);
goto err;
}
kfree(delayed_init);
return;
err:
pr_debug("%s: failed=%d\n", __func__, ret);
return;
}
int dvb_usbv2_device_init(struct usb_interface *intf,
const struct usb_device_id *id)
{
int ret;
struct dvb_usb_delayed_init *delayed_init;
delayed_init = kzalloc(sizeof(struct dvb_usb_delayed_init), GFP_KERNEL);
if (!delayed_init) {
pr_err("%s: kzalloc() failed", DVB_USB_LOG_PREFIX);
ret = -ENOMEM;
goto err;
}
delayed_init->intf = intf;
delayed_init->id = id;
INIT_WORK(&delayed_init->work, dvb_usbv2_init_work);
ret = schedule_work(&delayed_init->work);
if (ret < 0) {
pr_err("%s: schedule_work() failed", DVB_USB_LOG_PREFIX);
goto err_kfree;
}
return 0;
err_kfree:
kfree(delayed_init);
err:
pr_debug("%s: failed=%d\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL(dvb_usbv2_device_init);
void dvb_usbv2_device_exit(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
const char *name = NULL;
const char *name = "generic DVB-USB module";
/*
* FIXME: we should ensure our device initialization work is finished
* until exit from this routine (cancel_work_sync?)
*/
usb_set_intfdata(intf, NULL);
if (d) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment