From: Antti Palosaari Date: Wed, 6 Jun 2012 03:05:06 +0000 (-0300) Subject: [media] dvb_usb_v2: refactor delayed init X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=4f208d4e2d871c2416c9a86695d6f7d17e76349b;p=linux-beck.git [media] dvb_usb_v2: refactor delayed init Move work to the struct dvb_usb_device that we can access it inside .disconnect(). Also many other minor changes. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/dvb/dvb-usb/dvb_usb.h b/drivers/media/dvb/dvb-usb/dvb_usb.h index e338b4777a11..44c799cedc87 100644 --- a/drivers/media/dvb/dvb-usb/dvb_usb.h +++ b/drivers/media/dvb/dvb-usb/dvb_usb.h @@ -358,6 +358,8 @@ struct dvb_usb_device { const char *rc_map; struct dvb_usb_rc rc; struct usb_device *udev; + struct work_struct probe_work; + struct usb_interface *intf; #define DVB_USB_STATE_INIT 0x000 #define DVB_USB_STATE_I2C 0x001 diff --git a/drivers/media/dvb/dvb-usb/dvb_usb_init.c b/drivers/media/dvb/dvb-usb/dvb_usb_init.c index 3078371d626b..d7a6efc9a52e 100644 --- a/drivers/media/dvb/dvb-usb/dvb_usb_init.c +++ b/drivers/media/dvb/dvb-usb/dvb_usb_init.c @@ -251,44 +251,29 @@ int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff) /* * USB */ -int dvb_usbv2_device_init_(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct dvb_usb_device *d = NULL; - struct dvb_usb_driver_info *driver_info = - (struct dvb_usb_driver_info *) id->driver_info; - const struct dvb_usb_device_properties *props; - int ret = -ENOMEM; - bool cold = false; - - if (!id->driver_info) { - pr_err("%s: driver_info is null", KBUILD_MODNAME); - ret = -ENODEV; - goto err; - } - props = driver_info->props; +/* + * 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. + */ - d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); - if (d == NULL) { - err("no memory for 'struct dvb_usb_device'"); - return -ENOMEM; - } +static void dvb_usbv2_init_work(struct work_struct *work) +{ + int ret; + struct dvb_usb_device *d = + container_of(work, struct dvb_usb_device, probe_work); + bool cold = false; - d->udev = udev; - d->name = driver_info->name; - d->rc_map = driver_info->rc_map; - memcpy(&d->props, props, sizeof(struct dvb_usb_device_properties)); - mutex_init(&d->usb_mutex); - mutex_init(&d->i2c_mutex); + pr_debug("%s:\n", __func__); - if (d->props.size_of_priv > 0) { + if (d->props.size_of_priv) { d->priv = kzalloc(d->props.size_of_priv, GFP_KERNEL); - if (d->priv == NULL) { - err("no memory for priv in 'struct dvb_usb_device'"); + if (!d->priv) { + pr_err("%s: kzalloc() failed\n", KBUILD_MODNAME); ret = -ENOMEM; - goto err_kfree; + goto err_usb_driver_release_interface; } } @@ -300,77 +285,41 @@ int dvb_usbv2_device_init_(struct usb_interface *intf, cold = true; ret = 0; } else { - goto err_kfree; + goto err_usb_driver_release_interface; } } if (cold) { - info("found a '%s' in cold state, will try to load a firmware", - d->name); + pr_info("%s: found a '%s' in cold state\n", + KBUILD_MODNAME, d->name); ret = dvb_usb_download_firmware(d); if (ret == 0) { ; } else if (ret == RECONNECTS_USB) { ret = 0; - goto err_kfree; + goto exit_usb_driver_release_interface; } else { - goto err_kfree; + goto err_usb_driver_release_interface; } } - info("found a '%s' in warm state.", d->name); - - usb_set_intfdata(intf, d); + pr_info("%s: found a '%s' in warm state\n", KBUILD_MODNAME, d->name); ret = dvb_usb_init(d); + if (ret < 0) + goto err_usb_driver_release_interface; - if (ret == 0) - info("%s successfully initialized and connected.", d->name); - else - info("%s error while loading driver (%d)", d->name, ret); - - return 0; - -err_kfree: - kfree(d->priv); - kfree(d); -err: - pr_debug("%s: failed=%d\n", __func__, ret); - 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); + pr_info("%s: '%s' successfully initialized and connected\n", + KBUILD_MODNAME, d->name); return; -err: +err_usb_driver_release_interface: + pr_info("%s: '%s' error while loading driver (%d)\n", KBUILD_MODNAME, + d->name, ret); +exit_usb_driver_release_interface: + /* it finally calls .disconnect() which frees mem */ + usb_driver_release_interface(to_usb_driver(d->intf->dev.driver), + d->intf); pr_debug("%s: failed=%d\n", __func__, ret); return; } @@ -379,28 +328,45 @@ int dvb_usbv2_device_init(struct usb_interface *intf, const struct usb_device_id *id) { int ret; - struct dvb_usb_delayed_init *delayed_init; + struct dvb_usb_device *d; + struct dvb_usb_driver_info *driver_info = + (struct dvb_usb_driver_info *) id->driver_info; - 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; + pr_debug("%s:\n", __func__); + + if (!id->driver_info) { + pr_err("%s: driver_info failed\n", KBUILD_MODNAME); + ret = -ENODEV; goto err; } - delayed_init->intf = intf; - delayed_init->id = id; - INIT_WORK(&delayed_init->work, dvb_usbv2_init_work); + d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); + if (!d) { + pr_err("%s: kzalloc() failed\n", KBUILD_MODNAME); + ret = -ENOMEM; + goto err; + } - ret = schedule_work(&delayed_init->work); + d->name = driver_info->name; + d->rc_map = driver_info->rc_map; + d->udev = interface_to_usbdev(intf); + d->intf = intf; + memcpy(&d->props, driver_info->props, + sizeof(struct dvb_usb_device_properties)); + mutex_init(&d->usb_mutex); + mutex_init(&d->i2c_mutex); + INIT_WORK(&d->probe_work, dvb_usbv2_init_work); + usb_set_intfdata(intf, d); + ret = schedule_work(&d->probe_work); if (ret < 0) { - pr_err("%s: schedule_work() failed", DVB_USB_LOG_PREFIX); + pr_err("%s: schedule_work() failed\n", KBUILD_MODNAME); goto err_kfree; } return 0; err_kfree: - kfree(delayed_init); + usb_set_intfdata(intf, NULL); + kfree(d); err: pr_debug("%s: failed=%d\n", __func__, ret); return ret; @@ -413,9 +379,14 @@ void dvb_usbv2_device_exit(struct usb_interface *intf) struct dvb_usb_device *d = usb_get_intfdata(intf); const char *name = "generic DVB-USB module"; + pr_debug("%s:\n", __func__); + /* - * FIXME: we should ensure our device initialization work is finished - * until exit from this routine (cancel_work_sync?) + * FIXME: We should ensure initialization work is finished + * until exit from this routine (cancel_work_sync / flush_work). + * Unfortunately usb_driver_release_interface() call finally goes + * here too and in that case we endup deadlock. How to perform + * operation conditionally only on disconned / unload? */ usb_set_intfdata(intf, NULL); @@ -423,7 +394,9 @@ void dvb_usbv2_device_exit(struct usb_interface *intf) name = d->name; dvb_usb_exit(d); } - info("%s successfully deinitialized and disconnected.", name); + + pr_info("%s: '%s' successfully deinitialized and disconnected\n", + KBUILD_MODNAME, name); } EXPORT_SYMBOL(dvb_usbv2_device_exit);