Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 307ba6dd authored by David Vrabel's avatar David Vrabel
Browse files

uwb: don't unbind the radio controller driver when resetting



Use pre_reset and post_reset methods to avoid unbinding the radio
controller driver after a uwb_rc_reset_all() call.  This avoids a
deadlock in uwb_rc_rm() when waiting for the uwb event thread to stop.

Signed-off-by: default avatarDavid Vrabel <david.vrabel@csr.com>
parent fec1a593
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -881,6 +881,24 @@ static void hwarc_disconnect(struct usb_interface *iface)
	uwb_rc_put(uwb_rc);	/* when creating the device, refcount = 1 */
}

static int hwarc_pre_reset(struct usb_interface *iface)
{
	struct hwarc *hwarc = usb_get_intfdata(iface);
	struct uwb_rc *uwb_rc = hwarc->uwb_rc;

	uwb_rc_pre_reset(uwb_rc);
	return 0;
}

static int hwarc_post_reset(struct usb_interface *iface)
{
	struct hwarc *hwarc = usb_get_intfdata(iface);
	struct uwb_rc *uwb_rc = hwarc->uwb_rc;

	uwb_rc_post_reset(uwb_rc);
	return 0;
}

/** USB device ID's that we handle */
static struct usb_device_id hwarc_id_table[] = {
	/* D-Link DUB-1210 */
@@ -897,9 +915,11 @@ MODULE_DEVICE_TABLE(usb, hwarc_id_table);

static struct usb_driver hwarc_driver = {
	.name =		"hwa-rc",
	.id_table =	hwarc_id_table,
	.probe =	hwarc_probe,
	.disconnect =	hwarc_disconnect,
	.id_table =	hwarc_id_table,
	.pre_reset =    hwarc_pre_reset,
	.post_reset =   hwarc_post_reset,
};

static int __init hwarc_driver_init(void)
+41 −8
Original line number Diff line number Diff line
@@ -323,17 +323,16 @@ int uwbd_msg_handle_reset(struct uwb_event *evt)
	struct uwb_rc *rc = evt->rc;
	int ret;

	/* Need to prevent the RC hardware module going away while in
	   the rc->reset() call. */
	if (!try_module_get(rc->owner))
		return 0;

	dev_info(&rc->uwb_dev.dev, "resetting radio controller\n");
	ret = rc->reset(rc);
	if (ret)
	if (ret) {
		dev_err(&rc->uwb_dev.dev, "failed to reset hardware: %d\n", ret);

	module_put(rc->owner);
		goto error;
	}
	return 0;
error:
	/* Nothing can be done except try the reset again. */
	uwb_rc_reset_all(rc);
	return ret;
}

@@ -360,3 +359,37 @@ void uwb_rc_reset_all(struct uwb_rc *rc)
	uwbd_event_queue(evt);
}
EXPORT_SYMBOL_GPL(uwb_rc_reset_all);

void uwb_rc_pre_reset(struct uwb_rc *rc)
{
	rc->stop(rc);
	uwbd_flush(rc);

	mutex_lock(&rc->uwb_dev.mutex);
	rc->beaconing = -1;
	rc->scanning = -1;
	mutex_unlock(&rc->uwb_dev.mutex);

	uwb_rsv_remove_all(rc);
}
EXPORT_SYMBOL_GPL(uwb_rc_pre_reset);

void uwb_rc_post_reset(struct uwb_rc *rc)
{
	int ret;

	ret = rc->start(rc);
	if (ret)
		goto error;
	ret = uwb_rc_mac_addr_set(rc, &rc->uwb_dev.mac_addr);
	if (ret)
		goto error;
	ret = uwb_rc_dev_addr_set(rc, &rc->uwb_dev.dev_addr);
	if (ret)
		goto error;
	return;
error:
	/* Nothing can be done except try the reset again. */
	uwb_rc_reset_all(rc);
}
EXPORT_SYMBOL_GPL(uwb_rc_post_reset);
+20 −9
Original line number Diff line number Diff line
@@ -659,6 +659,25 @@ static void uwb_rsv_timer(unsigned long arg)
	uwb_rsv_sched_update(rsv->rc);
}

/**
 * uwb_rsv_remove_all - remove all reservations
 * @rc: the radio controller
 *
 * A DRP IE update is not done.
 */
void uwb_rsv_remove_all(struct uwb_rc *rc)
{
	struct uwb_rsv *rsv, *t;

	mutex_lock(&rc->rsvs_mutex);
	list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
		uwb_rsv_remove(rsv);
	}
	mutex_unlock(&rc->rsvs_mutex);

	cancel_work_sync(&rc->rsv_update_work);
}

void uwb_rsv_init(struct uwb_rc *rc)
{
	INIT_LIST_HEAD(&rc->reservations);
@@ -682,14 +701,6 @@ int uwb_rsv_setup(struct uwb_rc *rc)

void uwb_rsv_cleanup(struct uwb_rc *rc)
{
	struct uwb_rsv *rsv, *t;

	mutex_lock(&rc->rsvs_mutex);
	list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
		uwb_rsv_remove(rsv);
	}
	mutex_unlock(&rc->rsvs_mutex);

	cancel_work_sync(&rc->rsv_update_work);
	uwb_rsv_remove_all(rc);
	destroy_workqueue(rc->rsv_workq);
}
+43 −19
Original line number Diff line number Diff line
@@ -11,23 +11,48 @@
#include <linux/uwb/umc.h>
#include <linux/pci.h>

static int umc_bus_unbind_helper(struct device *dev, void *data)
static int umc_bus_pre_reset_helper(struct device *dev, void *data)
{
	struct device *parent = data;
	int ret = 0;

	if (dev->driver) {
		struct umc_dev *umc = to_umc_dev(dev);
		struct umc_driver *umc_drv = to_umc_driver(dev->driver);

	if (dev->parent == parent && dev->driver)
		if (umc_drv->pre_reset)
			ret = umc_drv->pre_reset(umc);
		else
			device_release_driver(dev);
	return 0;
	}
	return ret;
}

static int umc_bus_post_reset_helper(struct device *dev, void *data)
{
	int ret = 0;

	if (dev->driver) {
		struct umc_dev *umc = to_umc_dev(dev);
		struct umc_driver *umc_drv = to_umc_driver(dev->driver);

		if (umc_drv->post_reset)
			ret = umc_drv->post_reset(umc);
	} else
		ret = device_attach(dev);

	return ret;
}

/**
 * umc_controller_reset - reset the whole UMC controller
 * @umc: the UMC device for the radio controller.
 *
 * Drivers will be unbound from all UMC devices belonging to the
 * controller and then the radio controller will be rebound.  The
 * radio controller is expected to do a full hardware reset when it is
 * probed.
 * Drivers or all capabilities of the controller will have their
 * pre_reset methods called or be unbound from their device.  Then all
 * post_reset methods will be called or the drivers will be rebound.
 *
 * Radio controllers must provide pre_reset and post_reset methods and
 * reset the hardware in their start method.
 *
 * If this is called while a probe() or remove() is in progress it
 * will return -EAGAIN and not perform the reset.
@@ -35,14 +60,13 @@ static int umc_bus_unbind_helper(struct device *dev, void *data)
int umc_controller_reset(struct umc_dev *umc)
{
	struct device *parent = umc->dev.parent;
	int ret;
	int ret = 0;

	if(down_trylock(&parent->sem))
		return -EAGAIN;
	bus_for_each_dev(&umc_bus_type, NULL, parent, umc_bus_unbind_helper);
	ret = device_attach(&umc->dev);
	if (ret == 1)
		ret = 0;
	ret = device_for_each_child(parent, parent, umc_bus_pre_reset_helper);
	if (ret >= 0)
		device_for_each_child(parent, parent, umc_bus_post_reset_helper);
	up(&parent->sem);

	return ret;
@@ -75,10 +99,10 @@ static int umc_bus_rescan_helper(struct device *dev, void *data)
	if (!dev->driver)
		ret = device_attach(dev);

	return ret < 0 ? ret : 0;
	return ret;
}

static void umc_bus_rescan(void)
static void umc_bus_rescan(struct device *parent)
{
	int err;

@@ -86,7 +110,7 @@ static void umc_bus_rescan(void)
	 * We can't use bus_rescan_devices() here as it deadlocks when
	 * it tries to retake the dev->parent semaphore.
	 */
	err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper);
	err = device_for_each_child(parent, NULL, umc_bus_rescan_helper);
	if (err < 0)
		printk(KERN_WARNING "%s: rescan of bus failed: %d\n",
		       KBUILD_MODNAME, err);
@@ -120,7 +144,7 @@ static int umc_device_probe(struct device *dev)
	if (err)
		put_device(dev);
	else
		umc_bus_rescan();
		umc_bus_rescan(dev->parent);

	return err;
}
+1 −0
Original line number Diff line number Diff line
@@ -248,6 +248,7 @@ extern struct device_attribute dev_attr_scan;
void uwb_rsv_init(struct uwb_rc *rc);
int uwb_rsv_setup(struct uwb_rc *rc);
void uwb_rsv_cleanup(struct uwb_rc *rc);
void uwb_rsv_remove_all(struct uwb_rc *rc);

void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state);
void uwb_rsv_remove(struct uwb_rsv *rsv);
Loading