Commit 4f208d4e authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab
Browse files

[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: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 0a867356
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+72 −99
Original line number Diff line number Diff line
@@ -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);