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

Commit 19d337df authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

rfkill: rewrite



This patch completely rewrites the rfkill core to address
the following deficiencies:

 * all rfkill drivers need to implement polling where necessary
   rather than having one central implementation

 * updating the rfkill state cannot be done from arbitrary
   contexts, forcing drivers to use schedule_work and requiring
   lots of code

 * rfkill drivers need to keep track of soft/hard blocked
   internally -- the core should do this

 * the rfkill API has many unexpected quirks, for example being
   asymmetric wrt. alloc/free and register/unregister

 * rfkill can call back into a driver from within a function the
   driver called -- this is prone to deadlocks and generally
   should be avoided

 * rfkill-input pointlessly is a separate module

 * drivers need to #ifdef rfkill functions (unless they want to
   depend on or select RFKILL) -- rfkill should provide inlines
   that do nothing if it isn't compiled in

 * the rfkill structure is not opaque -- drivers need to initialise
   it correctly (lots of sanity checking code required) -- instead
   force drivers to pass the right variables to rfkill_alloc()

 * the documentation is hard to read because it always assumes the
   reader is completely clueless and contains way TOO MANY CAPS

 * the rfkill code needlessly uses a lot of locks and atomic
   operations in locked sections

 * fix LED trigger to actually change the LED when the radio state
   changes -- this wasn't done before

Tested-by: default avatarAlan Jenkins <alan-jenkins@tuffmail.co.uk>
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad]
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 0f6399c4
Loading
Loading
Loading
Loading
+78 −519

File changed.

Preview size limit exceeded, changes collapsed.

+3 −3
Original line number Original line Diff line number Diff line
@@ -4753,9 +4753,9 @@ S: Supported
F:	fs/reiserfs/
F:	fs/reiserfs/


RFKILL
RFKILL
P:	Ivo van Doorn
P:	Johannes Berg
M:	IvDoorn@gmail.com
M:	johannes@sipsolutions.net
L:	netdev@vger.kernel.org
L:	linux-wireless@vger.kernel.org
S:	Maintained
S:	Maintained
F	Documentation/rfkill.txt
F	Documentation/rfkill.txt
F:	net/rfkill/
F:	net/rfkill/
+15 −15
Original line number Original line Diff line number Diff line
@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_data *data)
	gpio_set_value(data->gpio_reset, 0);
	gpio_set_value(data->gpio_reset, 0);
}
}


static int tosa_bt_toggle_radio(void *data, enum rfkill_state state)
static int tosa_bt_set_block(void *data, bool blocked)
{
{
	pr_info("BT_RADIO going: %s\n",
	pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
			state == RFKILL_STATE_UNBLOCKED ? "on" : "off");


	if (state == RFKILL_STATE_UNBLOCKED) {
	if (!blocked) {
		pr_info("TOSA_BT: going ON\n");
		pr_info("TOSA_BT: going ON\n");
		tosa_bt_on(data);
		tosa_bt_on(data);
	} else {
	} else {
		pr_info("TOSA_BT: going OFF\n");
		pr_info("TOSA_BT: going OFF\n");
		tosa_bt_off(data);
		tosa_bt_off(data);
	}
	}

	return 0;
	return 0;
}
}


static const struct rfkill_ops tosa_bt_rfkill_ops = {
	.set_block = tosa_bt_set_block,
};

static int tosa_bt_probe(struct platform_device *dev)
static int tosa_bt_probe(struct platform_device *dev)
{
{
	int rc;
	int rc;
@@ -70,18 +74,14 @@ static int tosa_bt_probe(struct platform_device *dev)
	if (rc)
	if (rc)
		goto err_pwr_dir;
		goto err_pwr_dir;


	rfk = rfkill_allocate(&dev->dev, RFKILL_TYPE_BLUETOOTH);
	rfk = rfkill_alloc("tosa-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH,
			   &tosa_bt_rfkill_ops, data);
	if (!rfk) {
	if (!rfk) {
		rc = -ENOMEM;
		rc = -ENOMEM;
		goto err_rfk_alloc;
		goto err_rfk_alloc;
	}
	}


	rfk->name = "tosa-bt";
	rfkill_set_led_trigger_name(rfk, "tosa-bt");
	rfk->toggle_radio = tosa_bt_toggle_radio;
	rfk->data = data;
#ifdef CONFIG_RFKILL_LEDS
	rfk->led_trigger.name = "tosa-bt";
#endif


	rc = rfkill_register(rfk);
	rc = rfkill_register(rfk);
	if (rc)
	if (rc)
@@ -92,9 +92,7 @@ static int tosa_bt_probe(struct platform_device *dev)
	return 0;
	return 0;


err_rfkill:
err_rfkill:
	if (rfk)
	rfkill_destroy(rfk);
		rfkill_free(rfk);
	rfk = NULL;
err_rfk_alloc:
err_rfk_alloc:
	tosa_bt_off(data);
	tosa_bt_off(data);
err_pwr_dir:
err_pwr_dir:
@@ -113,8 +111,10 @@ static int __devexit tosa_bt_remove(struct platform_device *dev)


	platform_set_drvdata(dev, NULL);
	platform_set_drvdata(dev, NULL);


	if (rfk)
	if (rfk) {
		rfkill_unregister(rfk);
		rfkill_unregister(rfk);
		rfkill_destroy(rfk);
	}
	rfk = NULL;
	rfk = NULL;


	tosa_bt_off(data);
	tosa_bt_off(data);
+0 −1
Original line number Original line Diff line number Diff line
@@ -31,7 +31,6 @@
#include <linux/input.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/gpio.h>
#include <linux/pda_power.h>
#include <linux/pda_power.h>
#include <linux/rfkill.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi.h>


#include <asm/setup.h>
#include <asm/setup.h>
+22 −20
Original line number Original line Diff line number Diff line
@@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev)
	return 0;
	return 0;
}
}


static int hso_radio_toggle(void *data, enum rfkill_state state)
static int hso_rfkill_set_block(void *data, bool blocked)
{
{
	struct hso_device *hso_dev = data;
	struct hso_device *hso_dev = data;
	int enabled = (state == RFKILL_STATE_UNBLOCKED);
	int enabled = !blocked;
	int rv;
	int rv;


	mutex_lock(&hso_dev->mutex);
	mutex_lock(&hso_dev->mutex);
@@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state)
	return rv;
	return rv;
}
}


static const struct rfkill_ops hso_rfkill_ops = {
	.set_block = hso_rfkill_set_block,
};

/* Creates and sets up everything for rfkill */
/* Creates and sets up everything for rfkill */
static void hso_create_rfkill(struct hso_device *hso_dev,
static void hso_create_rfkill(struct hso_device *hso_dev,
			     struct usb_interface *interface)
			     struct usb_interface *interface)
@@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev,
	struct device *dev = &hso_net->net->dev;
	struct device *dev = &hso_net->net->dev;
	char *rfkn;
	char *rfkn;


	hso_net->rfkill = rfkill_allocate(&interface_to_usbdev(interface)->dev,
				 RFKILL_TYPE_WWAN);
	if (!hso_net->rfkill) {
		dev_err(dev, "%s - Out of memory\n", __func__);
		return;
	}
	rfkn = kzalloc(20, GFP_KERNEL);
	rfkn = kzalloc(20, GFP_KERNEL);
	if (!rfkn) {
	if (!rfkn)
		rfkill_free(hso_net->rfkill);
		hso_net->rfkill = NULL;
		dev_err(dev, "%s - Out of memory\n", __func__);
		dev_err(dev, "%s - Out of memory\n", __func__);
		return;

	}
	snprintf(rfkn, 20, "hso-%d",
	snprintf(rfkn, 20, "hso-%d",
		 interface->altsetting->desc.bInterfaceNumber);
		 interface->altsetting->desc.bInterfaceNumber);
	hso_net->rfkill->name = rfkn;

	hso_net->rfkill->state = RFKILL_STATE_UNBLOCKED;
	hso_net->rfkill = rfkill_alloc(rfkn,
	hso_net->rfkill->data = hso_dev;
				       &interface_to_usbdev(interface)->dev,
	hso_net->rfkill->toggle_radio = hso_radio_toggle;
				       RFKILL_TYPE_WWAN,
				       &hso_rfkill_ops, hso_dev);
	if (!hso_net->rfkill) {
		dev_err(dev, "%s - Out of memory\n", __func__);
		kfree(rfkn);
		return;
	}
	if (rfkill_register(hso_net->rfkill) < 0) {
	if (rfkill_register(hso_net->rfkill) < 0) {
		rfkill_destroy(hso_net->rfkill);
		kfree(rfkn);
		kfree(rfkn);
		hso_net->rfkill->name = NULL;
		rfkill_free(hso_net->rfkill);
		hso_net->rfkill = NULL;
		hso_net->rfkill = NULL;
		dev_err(dev, "%s - Failed to register rfkill\n", __func__);
		dev_err(dev, "%s - Failed to register rfkill\n", __func__);
		return;
		return;
@@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface)
			hso_stop_net_device(network_table[i]);
			hso_stop_net_device(network_table[i]);
			cancel_work_sync(&network_table[i]->async_put_intf);
			cancel_work_sync(&network_table[i]->async_put_intf);
			cancel_work_sync(&network_table[i]->async_get_intf);
			cancel_work_sync(&network_table[i]->async_get_intf);
			if (rfk)
			if (rfk) {
				rfkill_unregister(rfk);
				rfkill_unregister(rfk);
				rfkill_destroy(rfk);
			}
			hso_free_net_device(network_table[i]);
			hso_free_net_device(network_table[i]);
		}
		}
	}
	}
Loading