LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RFC] rfkill - Add support for input key to control wireless radio
@ 2006-12-03 18:36 Ivo van Doorn
  2006-12-03 19:18 ` Arjan van de Ven
                   ` (5 more replies)
  0 siblings, 6 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-03 18:36 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

This patch is a resend of a patch I has been send to the linux kernel
and netdev list earlier. The most recent version of a few weeks back
didn't compile since I missed 1 line in my patch that changed
include/linux/input.h.

This patch will offer the handling of radiokeys on a laptop.
Such keys often control the wireless devices on the radio
like the wifi, bluetooth and irda.

The rfkill works as follows, when the user presses the hardware key
to control the wireless (wifi, bluetooth or irda) radio a signal will
go to rfkill. This happens by the hardware driver sending a signal
to rfkill, or rfkill polling for the button status.
The key signal will then be processed by rfkill, each key will have
its own input device, if this input device has not been openened
by the user, rfkill will keep the signal and either turn the radio
on or off based on the key status.
If the input device has been opened, rfkill will send the signal to
userspace and do nothing further. The user is in charge of controlling
the radio.

This driver (if accepted and applied) will be usefull for the rt2x00 drivers
(rt2400pci, rt2500pci, rt2500usb, rt61pci and rt73usb) in the wireless-dev
tree and the MSI laptop driver from Lennart Poettering in the main
linux kernel tree.

Before this patch can be applied to any tree, I first wish to hear
if the patch is acceptable. Since the previous time it was send I have made
several fixes based on the feedback like adding the sysfs entries for
reading the status.

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>

---

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ba0e88c..6986d59 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -79,4 +79,19 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config RFKILL
+	tristate "RF button support"
+	help
+	  If you say yes here, the rfkill driver will be build
+	  which allowed network devices to register their hardware
+	  RF button which controls the radio state. This driver
+	  will then create an input device for it.
+
+	  When the input device is not used, the rfkill driver
+	  will make sure that when the RF button is pressed the radio
+	  is enabled or disabled accordingly. When the input device
+	  has been opened by the user this radio control will be left
+	  to the user, and rfkill will only send the RF button status
+	  change to userspace.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415c491..e788a1b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_RFKILL)			+= rfkill.o
diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
new file mode 100644
index 0000000..2a896db
--- /dev/null
+++ b/drivers/input/misc/rfkill.c
@@ -0,0 +1,880 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/input.h>
+#include <linux/rfkill.h>
+
+#include <asm/semaphore.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF key support");
+MODULE_LICENSE("GPL");
+
+/*
+ * rfkill key structure.
+ */
+struct rfkill_key {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Pointer to rfkill structure
+	 * that was filled in by key driver.
+	 */
+	struct rfkill *rfkill;
+
+	/*
+	 * Pointer to type structure that this key belongs to.
+	 */
+	struct rfkill_type *type;
+
+	/*
+	 * Once key status change has been detected, the toggled
+	 * field should be set to indicate a notification to
+	 * user or driver should be performed.
+	 */
+	int toggled;
+
+	/*
+	 * Current state of the device radio, this state will
+	 * change after the radio has actually been toggled since
+	 * receiving the radio key event.
+	 */
+	int radio_status;
+
+	/*
+	 * Current status of the key which controls the radio,
+	 * this value will change after the key state has changed
+	 * after polling, or the key driver has send the new state
+	 * manually.
+	 */
+	int key_status;
+
+	/*
+	 * Input device for this key,
+	 * we also keep track of the number of
+	 * times this input device is open. This
+	 * is important for determining to whom we
+	 * should report key events.
+	 */
+	struct input_dev *input;
+	unsigned int open_count;
+
+	/*
+	 * Key index number.
+	 */
+	unsigned int key_index;
+
+	/*
+	 * List head structure to be used
+	 * to add this structure to the list.
+	 */
+	struct list_head entry;
+};
+
+/*
+ * rfkill key type structure.
+ */
+struct rfkill_type {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Name of this radio type.
+	 */
+	char *name;
+
+	/*
+	 * Key type identification. Value must be any
+	 * in the key_type enum.
+	 */
+	unsigned int key_type;
+
+	/*
+	 * Number of registered keys of this type.
+	 */
+	unsigned int key_count;
+};
+
+/*
+ * rfkill master structure.
+ */
+struct rfkill_master {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class *class;
+
+	/*
+	 * All access to the master structure
+	 * and its children (the keys) are protected
+	 * by this key lock.
+	 */
+	struct semaphore key_sem;
+
+	/*
+	 * List of available key types.
+	 */
+	struct rfkill_type type[KEY_TYPE_MAX];
+
+	/*
+	 * Total number of registered keys.
+	 */
+	unsigned int key_count;
+
+	/*
+	 * Number of keys that require polling
+	 */
+	unsigned int poll_required;
+
+	/*
+	 * List of rfkill_key structures.
+	 */
+	struct list_head key_list;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	struct work_struct toggle_work;
+	struct work_struct poll_work;
+};
+
+/*
+ * We only need 1 single master interface,
+ * make this a global variable since we always need it.
+ */
+static struct rfkill_master *master;
+
+/*
+ * Send toggle event to key driver.
+ */
+static void rfkill_toggle_radio_driver(struct rfkill_key *key)
+{
+	struct rfkill *rfkill = key->rfkill;
+
+	/*
+	 * check what the current radio status is, and perform
+	 * the correct action to toggle the radio. This will make
+	 * sure the correct event happens when either the key driver
+	 * of the user has requested to toggle this radio.
+	 */
+	if (!key->radio_status && rfkill->enable_radio)
+		rfkill->enable_radio(rfkill->data);
+	else if (key->radio_status && rfkill->disable_radio)
+		rfkill->disable_radio(rfkill->data);
+
+	/*
+	 * Independent of the presence of the enable_radio and
+	 * disable_radio handler update the radio_status field.
+	 * Note that this is valid, since if the key driver did
+	 * not provide the 2 radio handlers, the driver is in
+	 * charge of correctly setting the radio. This is usually
+	 * done because the hardware will toggle the radio itself.
+	 */
+	key->radio_status = key->key_status;
+}
+
+/*
+ * Send toggle event to userspace through the input device.
+ */
+static void rfkill_toggle_radio_input(struct rfkill_key *key)
+{
+	input_report_key(key->input, KEY_RFKILL, key->key_status);
+	input_sync(key->input);
+}
+
+/*
+ * Loop through the list of registered keys and
+ * check if key if it has been toggled.
+ */
+static void rfkill_toggle_radio(void *data)
+{
+	struct rfkill_key *key;
+
+	down(&master->key_sem);
+
+	list_for_each_entry(key, &master->key_list, entry) {
+
+		/*
+		 * If this key hasn't been toggled by either
+		 * the key driver or the user we can skip this key.
+		 */
+		if (!key->toggled)
+			continue;
+
+		/*
+		 * If the input device has been opened, all events
+		 * should be send to userspace otherwise the key driver
+		 * is supposed to handle the event.
+		 */
+		if (key->open_count)
+			rfkill_toggle_radio_input(key);
+		else
+			rfkill_toggle_radio_driver(key);
+
+		/*
+		 * Reset the toggled field to allow new toggle events.
+		 */
+		key->toggled = 0;
+	}
+
+	up(&master->key_sem);
+}
+
+/*
+ * Check the new status of the key, and determine if the key has been toggled.
+ * This function can be called upon request by the device driver or userspace.
+ */
+static int rfkill_check_status(struct rfkill_key *key, int status)
+{
+	/*
+	 * A key should be toggled if the current radio status does not
+	 * match the requested status. This check is required instead of
+	 * comparing it to the current key status since this function can
+	 * be called on request of the user as well. The user is able to
+	 * request a radio toggle to make sure the radio status will match
+	 * the key status if the new key status has been reported to
+	 * userspace only.
+	 * As a safety measure, we won't toggle a key twice.
+	 */
+	if (key->radio_status != !!status && !key->toggled) {
+		key->toggled = 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Wrapper around the rfkill_check_status function, this may only
+ * be called from the device driver status events. Besides calling
+ * rfkill_check_status it will also update the key_status field.
+ */
+static int rfkill_check_key(struct rfkill_key *key, int status)
+{
+	/*
+	 * Store the new key status value.
+	 */
+	key->key_status = !!status;
+
+	/*
+	 * The rest of the work should be done by rfkill_check_status.
+	 */
+	return rfkill_check_status(key->rfkill->key, status);
+}
+
+/*
+ * Handler that is run frequently to determine the current status
+ * of all registered keys that require polling.
+ */
+static void rfkill_list_poll(void *data)
+{
+	struct rfkill_key *key;
+	struct rfkill *rfkill;
+	int status = 0;
+
+	down(&master->key_sem);
+
+	list_for_each_entry(key, &master->key_list, entry) {
+		rfkill = key->rfkill;
+
+		/*
+		 * If the poll handler has not been given
+		 * the key driver should report events,
+		 * so we can ignore this key now.
+		 */
+		if (!rfkill->poll)
+			continue;
+
+		/*
+		 * Poll radio state and check if a radio toggle
+		 * has been requested by the key.
+		 */
+		if (rfkill_check_key(key, rfkill->poll(rfkill->data)))
+			status = 1;
+	}
+
+	up(&master->key_sem);
+
+	/*
+	 * A radio toggle has been requested, schedule the toggle work thread.
+	 */
+	if (status)
+		schedule_work(&master->toggle_work);
+
+	/*
+	 * Check if we need to rearm ourselves.
+	 */
+	if (master->poll_required)
+		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
+}
+
+/*
+ * Function called by the key driver to report the new status
+ * of the key.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status)
+{
+	down(&master->key_sem);
+
+	if (rfkill_check_key(rfkill->key, new_status))
+		schedule_work(&master->toggle_work);
+
+	up(&master->key_sem);
+}
+EXPORT_SYMBOL_GPL(rfkill_report_event);
+
+/*
+ * Sysfs handler to release the data when sysfs is cleaning
+ * up all the garbage. This is done in a late state since the
+ * user could still be reading some of the files at the moment
+ * this key was removed.
+ * This method is being used for both rfkill_key as rfkill_type
+ * class devices. So be careful not to do anything structure
+ * specific.
+ */
+static void rfkill_class_device_release(struct class_device *cdev)
+{
+	kfree(class_get_devdata(cdev));
+}
+
+/*
+ * rfkill master sysfs creation.
+ */
+static int rfkill_master_sysfs_create(struct rfkill_master *master)
+{
+	master->class = class_create(THIS_MODULE, "rfkill");
+	if (unlikely(!master->class || IS_ERR(master->class)))
+		return PTR_ERR(master->class);
+
+	master->class->release = rfkill_class_device_release;
+
+	return 0;
+}
+
+/*
+ * rfkill master sysfs destroy.
+ */
+static void rfkill_master_sysfs_release(struct rfkill_master *master)
+{
+	class_destroy(master->class);
+}
+
+/*
+ * rfkill type sysfs creation.
+ */
+static int rfkill_type_sysfs_create(struct rfkill_master *master,
+				    struct rfkill_type *type)
+{
+	type->cdev = class_device_create(master->class, NULL, type->key_type,
+					 NULL, type->name);
+	if (unlikely(!type->cdev || IS_ERR(type->cdev)))
+		return PTR_ERR(type->cdev);
+
+	return 0;
+}
+
+static void rfkill_type_sysfs_release(struct rfkill_type *type)
+{
+	class_device_destroy(type->cdev->class, type->key_type);
+}
+
+/*
+ * rfkill key attribute to display the current key status.
+ */
+static ssize_t rfkill_key_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", key->key_status);
+}
+
+/*
+ * rfkill key attribute to change the current radio status.
+ */
+static ssize_t rfkill_key_status_store(struct class_device *cdev,
+				      const char *buf, size_t count)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+	int status = simple_strtoul(buf, NULL, 0);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	down(&master->key_sem);
+
+	/*
+	 * Check if this new status implies that the radio
+	 * should be toggled to the correct state.
+	 */
+	if (rfkill_check_status(key, status))
+		schedule_work(&master->toggle_work);
+
+	up(&master->key_sem);
+
+	return count;
+}
+
+static struct class_device_attribute cdev_attr_key_status =
+	__ATTR(status, S_IRUGO | S_IWUGO,
+	       rfkill_key_status_show, rfkill_key_status_store);
+
+/*
+ * rfkill key sysfs creation.
+ */
+static int rfkill_key_sysfs_create(struct rfkill_master *master,
+				    struct rfkill_key *key)
+{
+	int status;
+
+	key->cdev = class_device_create(master->class, key->type->cdev,
+					KEY_TYPE_MAX + key->key_index,
+					key->rfkill->dev,
+					"key%d", key->key_index);
+	if (unlikely(!key->cdev || IS_ERR(key->cdev)))
+		return PTR_ERR(key->cdev);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(key->cdev, &cdev_attr_key_status);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * The key belongs to the device structure the key driver
+	 * has given us a reference to. But besides that device
+	 * this key also belongs to the input device that has been
+	 * created. We should create the link to that entry manually.
+	 */
+	status = sysfs_create_link(&key->cdev->kobj, &key->input->cdev.kobj,
+				   "idev");
+	if (unlikely(status))
+		goto exit_file;
+
+	return 0;
+
+exit_file:
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+
+exit:
+	class_device_destroy(master->class, KEY_TYPE_MAX + key->key_index);
+
+	return status;
+}
+
+static void rfkill_key_sysfs_release(struct rfkill_key *key)
+{
+	sysfs_remove_link(&key->cdev->kobj, "idev");
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+	class_device_destroy(master->class, KEY_TYPE_MAX + key->key_index);
+}
+
+/*
+ * Handlers to enable or close the input device.
+ * These handlers only have to increase or decrease the open_count,
+ * this counter will automatically make sure key events will be
+ * send to the correct location (userspace or key driver).
+ */
+static int rfkill_open(struct input_dev *dev)
+{
+	((struct rfkill_key*)(dev->private))->open_count++;
+	return 0;
+}
+	
+static void rfkill_close(struct input_dev *dev)
+{
+	((struct rfkill_key*)(dev->private))->open_count--;
+}
+
+/*
+ * Input device initialization handler.
+ */
+static struct input_dev* rfkill_register_input(struct rfkill_key *key)
+{
+	struct input_dev *input;
+	int status;
+	char *name;
+	char *phys;
+
+	input = input_allocate_device();
+	if (unlikely(!input))
+		return NULL;
+
+	/*
+	 * Link the private data to rfkill structure.
+	 */
+	input->private = key;
+
+	/*
+	 * Allocate the strings for the input device names.
+	 */
+	name = kasprintf(GFP_KERNEL, "rfkill key%d: %s",
+			 key->key_index, key->rfkill->dev_name);
+	phys = kasprintf(GFP_KERNEL, "rfkill/%s/key%d",
+			 key->type->name, key->key_index);
+
+	if (!name || !phys)
+		goto exit;
+
+	input->name = name;
+	input->phys = phys;
+
+	/*
+	 * Initialize the input_id structure.
+	 */
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0001;
+
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_RFKILL, input->keybit);
+
+	input->open = rfkill_open;
+	input->close = rfkill_close;
+
+	status = input_register_device(input);
+	if (status)
+		goto exit;
+
+	return input;
+
+exit:
+	input_free_device(input);
+
+	kfree(name);
+	kfree(phys);
+
+	return NULL;
+}
+
+/*
+ * Input device deinitialization handler.
+ */
+static void rfkill_deregister_input(struct rfkill_key *key)
+{
+	const char *name;
+	const char *phys;
+
+	/*
+	 * The name and phys fields have been allocated
+	 * using kasprintf, this means they have to be
+	 * freed seperately.
+	 */
+	name = key->input->name;
+	phys = key->input->phys;
+
+	input_unregister_device(key->input);
+	kfree(name);
+	kfree(phys);
+}
+
+/*
+ * Rfkill key initialization/deinitialization.
+ * These methods should only be called with the key_sem held.
+ */
+static struct rfkill_key* rfkill_key_init(struct rfkill *rfkill, int status)
+{
+	struct rfkill_key *key;
+
+	key = kzalloc(sizeof(struct rfkill_key), GFP_KERNEL);
+	if (unlikely(!key))
+		return NULL;
+
+	/*
+	 * Initialize all variables.
+	 */
+	key->rfkill = rfkill;
+	rfkill->key = key;
+	key->type = &master->type[rfkill->key_type];
+	key->toggled = 0;
+	key->radio_status = status;
+	key->key_status = status;
+	key->key_index = master->key_count++;
+	INIT_LIST_HEAD(&key->entry);
+
+	/*
+	 * Create input device.
+	 */
+	key->input = rfkill_register_input(key);
+	if (!key->input)
+		goto exit;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	if (rfkill_key_sysfs_create(master, key))
+		goto exit;
+
+	return key;
+
+exit:
+	rfkill_deregister_input(key);
+	rfkill->key = NULL;
+	kfree(key);
+	return NULL;
+}
+
+static void rfkill_key_deinit(struct rfkill_key *key)
+{
+	if (key) {
+		rfkill_key_sysfs_release(key);
+		rfkill_deregister_input(key);
+	}
+}
+
+/*
+ * Rfkill type handler to check if the sysfs entries should
+ * be created or removed based on the number of registered keys.
+ */
+static int rfkill_add_type_key(struct rfkill_type *type)
+{
+	int status;
+
+	if (type->key_count) {
+		type->key_count++;
+		return 0;
+	}
+
+	status = rfkill_type_sysfs_create(master, type);
+	if (status)
+		return status;
+
+	type->key_count = 1;
+	return 0;
+}
+
+static void rfkill_del_type_key(struct rfkill_type *type)
+{
+	if (!--type->key_count)
+		rfkill_type_sysfs_release(type);
+}
+
+/*
+ * Function called by the key driver when the rfkill structure
+ * needs to be registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status)
+{
+	struct rfkill_type *type = &master->type[rfkill->key_type];
+	struct rfkill_key *key;
+	int status;
+
+	if (!rfkill)
+		return -EINVAL;	
+
+	if (rfkill->key_type >= KEY_TYPE_MAX)
+		return -EINVAL;
+
+	/*
+	 * Increase module use count to prevent this
+	 * module to be unloaded while there are still
+	 * registered keys.
+	 */
+	if (!try_module_get(THIS_MODULE))
+		return -EBUSY;
+
+	down(&master->key_sem);
+
+	/*
+	 * Initialize key, and if required the type.
+	 */
+	status = rfkill_add_type_key(type);
+	if (status)
+		goto exit;
+
+	key = rfkill_key_init(rfkill, init_status);
+	if (!key) {
+		status = -ENOMEM;
+		goto exit_type;
+	}
+
+	/*
+	 * Add key to our list.
+	 */
+	list_add(&key->entry, &master->key_list);
+
+	/*
+	 * Check if we need polling, and if we do
+	 * increase the poll required counter and check
+	 * if we weren't polling yet.
+	 */
+	if (rfkill->poll && !master->poll_required++)
+		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
+
+	up(&master->key_sem);
+
+	return 0;
+
+exit_type:
+	rfkill_del_type_key(type);
+
+exit:
+	up(&master->key_sem);
+	module_put(THIS_MODULE);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(rfkill_register_key);
+
+/*
+ * Function called by the key driver when the rfkill structure
+ * needs to be deregistered.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill)
+{
+	struct rfkill_type *type;
+
+	if (!rfkill || !rfkill->key)
+		return -EINVAL;
+
+	down(&master->key_sem);
+
+	/*
+	 * Cancel delayed work if this is the last key
+	 * that requires polling. It is not bad if
+	 * if the workqueue is still running,
+	 * the workqueue won't rearm itself since the
+	 * poll_required variable has been set.
+	 * and we have protected the list with a semaphore.
+	 */
+	if (rfkill->poll && !--master->poll_required)
+		cancel_delayed_work(&master->poll_work);
+
+	/*
+	 * Delete the rfkill structure to the list.
+	 */
+	list_del(&rfkill->key->entry);
+
+	/*
+	 * Deinitialize key.
+	 */
+	type = rfkill->key->type;
+	rfkill_key_deinit(rfkill->key);
+	rfkill_del_type_key(type);
+
+	up(&master->key_sem);
+
+	/*
+	 * rfkill entry has been removed,
+	 * decrease module use count.
+	 */
+	module_put(THIS_MODULE);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rfkill_deregister_key);
+
+/*
+ * Rfkill type initialization.
+ */
+static void rfkill_type_init(unsigned int key_type, char *name)
+{
+	struct rfkill_type *type = &master->type[key_type];
+
+	/*
+	 * Initialize all variables.
+	 */
+	type->name = name;
+	type->key_type = key_type;
+	type->key_count = 0;
+}
+
+/*
+ * Rfkill master initialization/deinitialization.
+ */
+static int rfkill_master_init(void)
+{
+	int status;
+
+	master = kzalloc(sizeof(struct rfkill_master), GFP_KERNEL);
+	if (unlikely(!master))
+		return -ENOMEM;
+
+	/*
+	 * Initialize all variables.
+	 */
+	init_MUTEX(&master->key_sem);
+	INIT_LIST_HEAD(&master->key_list);
+	INIT_WORK(&master->toggle_work, &rfkill_toggle_radio, NULL);
+	INIT_WORK(&master->poll_work, &rfkill_list_poll, NULL);
+
+	/*
+	 * Initialize all type structures.
+	 */
+	rfkill_type_init(KEY_TYPE_WIFI, "wifi");
+	rfkill_type_init(KEY_TYPE_BlUETOOTH, "bluetooth");
+	rfkill_type_init(KEY_TYPE_IRDA, "irda");
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_master_sysfs_create(master);
+	if (status) {
+		kfree(master);
+		master = NULL;
+		return status;
+	}
+
+	return 0;
+}
+
+static void rfkill_master_deinit(void)
+{
+	if (master) {
+		rfkill_master_sysfs_release(master);
+		kfree(master);
+		master = NULL;
+	}
+}
+
+/*
+ * Module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	printk(KERN_INFO "Loading rfkill driver.\n");
+
+	return rfkill_master_init();
+}
+
+static void __exit rfkill_exit(void)
+{
+	rfkill_master_deinit();
+
+	printk(KERN_INFO "Unloading rfkill driver.\n");
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
diff --git a/include/linux/input.h b/include/linux/input.h
index c38507b..1b44108 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -491,6 +491,7 @@ struct input_absinfo {
 #define KEY_DIGITS		0x19d
 #define KEY_TEEN		0x19e
 #define KEY_TWEN		0x19f
+#define KEY_RFKILL		0x1a0
 
 #define KEY_DEL_EOL		0x1c0
 #define KEY_DEL_EOS		0x1c1
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
new file mode 100644
index 0000000..a455548
--- /dev/null
+++ b/include/linux/rfkill.h
@@ -0,0 +1,140 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+	RF button support
+	Laptops are quite often equiped with a RF key to enable or
+	disable the radio of the wireless device attached to that key.
+	This wireless device usually is an integrated wireless network device,
+	infrared or bluetooth device.
+	Some of these devices will disable the radio automatically when the
+	RF key has been pressed, while other devices need to be polled
+	for the RF key status, and leave the action to be taken up to the
+	driver for that particular device.
+	But in all cases the only interface that will have its radio disabled
+	will be the device that has the RF key attached to it. It could however
+	be desired that userspace performs this disabling of the radios in case
+	more things than just disabling a single radio is desired.
+
+	The rfkill driver will contain a list of all devices with a RF button,
+	and hardware drivers need to register their hardware to the rfkill
+	interface. Rfkill will then take care of everything. If the RF key
+	requires polling to obtain the status this will be handled by rfkill.
+	If the RF key does not require polling but sends for example interrupts,
+	the hardware driver can report the change of status to rfkill, without
+	having to do any other action.
+	Once the status of the key has changed and the hardware does not
+	automatically enable or disable the radio rfkill provides the
+	interface to do this correctly.
+
+	For each registered hardware button an input device will be created.
+	If this input device has been opened by the user, rfkill will send a
+	signal to userspace instead of the hardware about the new button
+	status. This will allow userpace to perform the correct steps
+	in order to bring down all interfaces.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+#include <linux/device.h>
+
+#define RFKILL_POLL_DELAY	( HZ / 10 )
+
+enum key_type {
+	KEY_TYPE_WIFI = 0,
+	KEY_TYPE_BlUETOOTH = 1,
+	KEY_TYPE_IRDA = 2,
+	KEY_TYPE_MAX = 3,
+};
+
+/**
+ * struct rfkill - rfkill button control structure.
+ *
+ * @dev_name: Name of the interface. This will become the name
+ * 	of the input device which will be created for this button.
+ * @dev: Pointer to the device structure to which this button belongs to.
+ * @data: Pointer to the RF button drivers private data which will be
+ * 	passed along with the radio and polling handlers.
+ * @poll(void *data): Optional handler which will frequently be
+ * 	called to determine the current status of the RF button.
+ * @enable_radio(void *data): Optional handler to enable the radio
+ * 	once the RF button has been pressed and the hardware does enable
+ * 	the radio automaticly.
+ * @disable_radio(void *data): Optional handler to disable the radio
+ * 	once the RF button has been pressed and the hardware does disable
+ * 	the radio automaticly.
+ * @key_type: Radio type which the button controls, the value stored
+ * 	here should be a value from enum key_type.
+ * @key: Internal pointer that should not be touched by key driver.
+ *
+ * This structure can be used by a key driver to register the key
+ * to the rfkill driver in order to take control of the reporting
+ * to userspace or handling of radio status.
+ */
+struct rfkill {
+	const char *dev_name;
+
+	struct device *dev;
+
+	void *data;
+	int (*poll)(void *data);
+	void (*enable_radio)(void *data);
+	void (*disable_radio)(void *data);
+
+	unsigned int key_type;
+
+	struct rfkill_key *key;
+};
+
+/**
+ * rfkill_register_key - Deregister a previously registered rfkill structre.
+ * @rfkill: rfkill structure to be deregistered
+ * @init_status: initial status of the key at the time this function is called
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be registered. Immediately from registration the key driver
+ * should be able to receive calls through the poll, enable_radio and
+ * disable_radio handlers if those were registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status);
+
+/**
+ * rfkill_deregister_key - Deregister a previously registered rfkill structre.
+ * @rfkill: rfkill structure to be deregistered
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be deregistered. This function may only be called if it was
+ * previously registered with rfkill_register_key.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill);
+
+/**
+ * rfkill_report_event - Report change in key status to rfkill handler.
+ * @rfkill: rfkill structure registered by key driver
+ * @new_status: new key status
+ *
+ * This function should be called by the key driver if it has not provided
+ * a poll handler with rfkill. As soon as the key driver has determined
+ * the status of the key has changed it should report the new status
+ * through this function.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status);
+
+#endif /* RFKILL_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
@ 2006-12-03 19:18 ` Arjan van de Ven
  2006-12-03 22:03   ` Ivo van Doorn
  2006-12-03 19:20 ` Arjan van de Ven
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Arjan van de Ven @ 2006-12-03 19:18 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Sun, 2006-12-03 at 19:36 +0100, Ivo van Doorn wrote:
> +
> +       down(&master->key_sem);
> + 

Hi,

this one seems to be used as a mutex only, please consider using a mutex
instead, as is the default for new code since 2.6.16 or so....

Greetings,
   Arjan van de Ven

-- 
if you want to mail me at work (you don't), use arjan (at) linux.intel.com
Test the interaction between Linux and your BIOS via http://www.linuxfirmwarekit.org


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
  2006-12-03 19:18 ` Arjan van de Ven
@ 2006-12-03 19:20 ` Arjan van de Ven
  2006-12-03 22:05   ` Ivo van Doorn
  2006-12-03 22:28   ` Ivo van Doorn
  2006-12-03 19:44 ` Dan Williams
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 42+ messages in thread
From: Arjan van de Ven @ 2006-12-03 19:20 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Sun, 2006-12-03 at 19:36 +0100, Ivo van Doorn wrote:
> +static int rfkill_open(struct input_dev *dev)
> +{
> +       ((struct rfkill_key*)(dev->private))->open_count++;
> +       return 0;
> +} 

Hi,

this open_count thing smells fishy to me; what are the locking rules for
it? What guarantees that the readers of it don't get the value changed
underneath them between looking at the value and doing whatever action
depends on it's value ?

Greetings,
   Arjan van de Ven

-- 
if you want to mail me at work (you don't), use arjan (at) linux.intel.com
Test the interaction between Linux and your BIOS via http://www.linuxfirmwarekit.org


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
  2006-12-03 19:18 ` Arjan van de Ven
  2006-12-03 19:20 ` Arjan van de Ven
@ 2006-12-03 19:44 ` Dan Williams
  2006-12-03 22:16   ` Ivo van Doorn
  2006-12-04  8:53   ` Marcel Holtmann
  2006-12-04 22:15 ` Dmitry Torokhov
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 42+ messages in thread
From: Dan Williams @ 2006-12-03 19:44 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger, davidz,
	Bastien Nocera

On Sun, 2006-12-03 at 19:36 +0100, Ivo van Doorn wrote:
> Hi,
> 
> This patch is a resend of a patch I has been send to the linux kernel
> and netdev list earlier. The most recent version of a few weeks back
> didn't compile since I missed 1 line in my patch that changed
> include/linux/input.h.
> 
> This patch will offer the handling of radiokeys on a laptop.
> Such keys often control the wireless devices on the radio
> like the wifi, bluetooth and irda.

Ok, what was the conclusion on the following issues?

1) What about rfkill keys that aren't routed via software?  There are
two modes here: (a) the key is not hardwired to the module and the
driver is responsible for disabling the radio, (b) the key is hardwired
to the module and firmware or some other mechanism disables the radio
without driver interaction.  I thought I'd heard of some (b) hardware,
mostly on older laptops, but does anyone know how prevalent (b) hardware
is?  If there isn't any, do we care about it for now?

2) Is there hardware that has separate keys for separate radios?  i.e.,
one key for Bluetooth, and one key for WiFi?  I know Bastien has a
laptop where the same rfkill key handles _both_ ipw2200 and the BT
module, but in different ways: it actually removes the BT USB device
from the USB bus, but uses the normal ipw rfkill mechanism.

3) How does this interact with HAL?  What's the userspace interface that
HAL will listen to to receive the signals?  NetworkManager will need to
listen to HAL for these, as rfkill switches are one big thing that NM
does not handle now due to lack of a standard mechanism.

In any case, any movement on rfkill interface/handling standardization
is quite welcome :)

Cheers,
Dan

> The rfkill works as follows, when the user presses the hardware key
> to control the wireless (wifi, bluetooth or irda) radio a signal will
> go to rfkill. This happens by the hardware driver sending a signal
> to rfkill, or rfkill polling for the button status.
> The key signal will then be processed by rfkill, each key will have
> its own input device, if this input device has not been openened
> by the user, rfkill will keep the signal and either turn the radio
> on or off based on the key status.
> If the input device has been opened, rfkill will send the signal to
> userspace and do nothing further. The user is in charge of controlling
> the radio.
> 
> This driver (if accepted and applied) will be usefull for the rt2x00 drivers
> (rt2400pci, rt2500pci, rt2500usb, rt61pci and rt73usb) in the wireless-dev
> tree and the MSI laptop driver from Lennart Poettering in the main
> linux kernel tree.
> 
> Before this patch can be applied to any tree, I first wish to hear
> if the patch is acceptable. Since the previous time it was send I have made
> several fixes based on the feedback like adding the sysfs entries for
> reading the status.
> 
> Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>
> 
> ---
> 
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index ba0e88c..6986d59 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -79,4 +79,19 @@ config HP_SDC_RTC
>  	  Say Y here if you want to support the built-in real time clock
>  	  of the HP SDC controller.
>  
> +config RFKILL
> +	tristate "RF button support"
> +	help
> +	  If you say yes here, the rfkill driver will be build
> +	  which allowed network devices to register their hardware
> +	  RF button which controls the radio state. This driver
> +	  will then create an input device for it.
> +
> +	  When the input device is not used, the rfkill driver
> +	  will make sure that when the RF button is pressed the radio
> +	  is enabled or disabled accordingly. When the input device
> +	  has been opened by the user this radio control will be left
> +	  to the user, and rfkill will only send the RF button status
> +	  change to userspace.
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 415c491..e788a1b 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
>  obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
>  obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
>  obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
> +obj-$(CONFIG_RFKILL)			+= rfkill.o
> diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
> new file mode 100644
> index 0000000..2a896db
> --- /dev/null
> +++ b/drivers/input/misc/rfkill.c
> @@ -0,0 +1,880 @@
> +/*
> +	Copyright (C) 2006 Ivo van Doorn
> +
> +	This program is free software; you can redistribute it and/or modify
> +	it under the terms of the GNU General Public License as published by
> +	the Free Software Foundation; either version 2 of the License, or
> +	(at your option) any later version.
> +
> +	This program is distributed in the hope that it will be useful,
> +	but WITHOUT ANY WARRANTY; without even the implied warranty of
> +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +	GNU General Public License for more details.
> +
> +	You should have received a copy of the GNU General Public License
> +	along with this program; if not, write to the
> +	Free Software Foundation, Inc.,
> +	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/workqueue.h>
> +#include <linux/list.h>
> +#include <linux/input.h>
> +#include <linux/rfkill.h>
> +
> +#include <asm/semaphore.h>
> +
> +MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
> +MODULE_VERSION("1.0");
> +MODULE_DESCRIPTION("RF key support");
> +MODULE_LICENSE("GPL");
> +
> +/*
> + * rfkill key structure.
> + */
> +struct rfkill_key {
> +	/*
> +	 * For sysfs representation.
> +	 */
> +	struct class_device *cdev;
> +
> +	/*
> +	 * Pointer to rfkill structure
> +	 * that was filled in by key driver.
> +	 */
> +	struct rfkill *rfkill;
> +
> +	/*
> +	 * Pointer to type structure that this key belongs to.
> +	 */
> +	struct rfkill_type *type;
> +
> +	/*
> +	 * Once key status change has been detected, the toggled
> +	 * field should be set to indicate a notification to
> +	 * user or driver should be performed.
> +	 */
> +	int toggled;
> +
> +	/*
> +	 * Current state of the device radio, this state will
> +	 * change after the radio has actually been toggled since
> +	 * receiving the radio key event.
> +	 */
> +	int radio_status;
> +
> +	/*
> +	 * Current status of the key which controls the radio,
> +	 * this value will change after the key state has changed
> +	 * after polling, or the key driver has send the new state
> +	 * manually.
> +	 */
> +	int key_status;
> +
> +	/*
> +	 * Input device for this key,
> +	 * we also keep track of the number of
> +	 * times this input device is open. This
> +	 * is important for determining to whom we
> +	 * should report key events.
> +	 */
> +	struct input_dev *input;
> +	unsigned int open_count;
> +
> +	/*
> +	 * Key index number.
> +	 */
> +	unsigned int key_index;
> +
> +	/*
> +	 * List head structure to be used
> +	 * to add this structure to the list.
> +	 */
> +	struct list_head entry;
> +};
> +
> +/*
> + * rfkill key type structure.
> + */
> +struct rfkill_type {
> +	/*
> +	 * For sysfs representation.
> +	 */
> +	struct class_device *cdev;
> +
> +	/*
> +	 * Name of this radio type.
> +	 */
> +	char *name;
> +
> +	/*
> +	 * Key type identification. Value must be any
> +	 * in the key_type enum.
> +	 */
> +	unsigned int key_type;
> +
> +	/*
> +	 * Number of registered keys of this type.
> +	 */
> +	unsigned int key_count;
> +};
> +
> +/*
> + * rfkill master structure.
> + */
> +struct rfkill_master {
> +	/*
> +	 * For sysfs representation.
> +	 */
> +	struct class *class;
> +
> +	/*
> +	 * All access to the master structure
> +	 * and its children (the keys) are protected
> +	 * by this key lock.
> +	 */
> +	struct semaphore key_sem;
> +
> +	/*
> +	 * List of available key types.
> +	 */
> +	struct rfkill_type type[KEY_TYPE_MAX];
> +
> +	/*
> +	 * Total number of registered keys.
> +	 */
> +	unsigned int key_count;
> +
> +	/*
> +	 * Number of keys that require polling
> +	 */
> +	unsigned int poll_required;
> +
> +	/*
> +	 * List of rfkill_key structures.
> +	 */
> +	struct list_head key_list;
> +
> +	/*
> +	 * Work structures for periodic polling,
> +	 * as well as the scheduled radio toggling.
> +	 */
> +	struct work_struct toggle_work;
> +	struct work_struct poll_work;
> +};
> +
> +/*
> + * We only need 1 single master interface,
> + * make this a global variable since we always need it.
> + */
> +static struct rfkill_master *master;
> +
> +/*
> + * Send toggle event to key driver.
> + */
> +static void rfkill_toggle_radio_driver(struct rfkill_key *key)
> +{
> +	struct rfkill *rfkill = key->rfkill;
> +
> +	/*
> +	 * check what the current radio status is, and perform
> +	 * the correct action to toggle the radio. This will make
> +	 * sure the correct event happens when either the key driver
> +	 * of the user has requested to toggle this radio.
> +	 */
> +	if (!key->radio_status && rfkill->enable_radio)
> +		rfkill->enable_radio(rfkill->data);
> +	else if (key->radio_status && rfkill->disable_radio)
> +		rfkill->disable_radio(rfkill->data);
> +
> +	/*
> +	 * Independent of the presence of the enable_radio and
> +	 * disable_radio handler update the radio_status field.
> +	 * Note that this is valid, since if the key driver did
> +	 * not provide the 2 radio handlers, the driver is in
> +	 * charge of correctly setting the radio. This is usually
> +	 * done because the hardware will toggle the radio itself.
> +	 */
> +	key->radio_status = key->key_status;
> +}
> +
> +/*
> + * Send toggle event to userspace through the input device.
> + */
> +static void rfkill_toggle_radio_input(struct rfkill_key *key)
> +{
> +	input_report_key(key->input, KEY_RFKILL, key->key_status);
> +	input_sync(key->input);
> +}
> +
> +/*
> + * Loop through the list of registered keys and
> + * check if key if it has been toggled.
> + */
> +static void rfkill_toggle_radio(void *data)
> +{
> +	struct rfkill_key *key;
> +
> +	down(&master->key_sem);
> +
> +	list_for_each_entry(key, &master->key_list, entry) {
> +
> +		/*
> +		 * If this key hasn't been toggled by either
> +		 * the key driver or the user we can skip this key.
> +		 */
> +		if (!key->toggled)
> +			continue;
> +
> +		/*
> +		 * If the input device has been opened, all events
> +		 * should be send to userspace otherwise the key driver
> +		 * is supposed to handle the event.
> +		 */
> +		if (key->open_count)
> +			rfkill_toggle_radio_input(key);
> +		else
> +			rfkill_toggle_radio_driver(key);
> +
> +		/*
> +		 * Reset the toggled field to allow new toggle events.
> +		 */
> +		key->toggled = 0;
> +	}
> +
> +	up(&master->key_sem);
> +}
> +
> +/*
> + * Check the new status of the key, and determine if the key has been toggled.
> + * This function can be called upon request by the device driver or userspace.
> + */
> +static int rfkill_check_status(struct rfkill_key *key, int status)
> +{
> +	/*
> +	 * A key should be toggled if the current radio status does not
> +	 * match the requested status. This check is required instead of
> +	 * comparing it to the current key status since this function can
> +	 * be called on request of the user as well. The user is able to
> +	 * request a radio toggle to make sure the radio status will match
> +	 * the key status if the new key status has been reported to
> +	 * userspace only.
> +	 * As a safety measure, we won't toggle a key twice.
> +	 */
> +	if (key->radio_status != !!status && !key->toggled) {
> +		key->toggled = 1;
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Wrapper around the rfkill_check_status function, this may only
> + * be called from the device driver status events. Besides calling
> + * rfkill_check_status it will also update the key_status field.
> + */
> +static int rfkill_check_key(struct rfkill_key *key, int status)
> +{
> +	/*
> +	 * Store the new key status value.
> +	 */
> +	key->key_status = !!status;
> +
> +	/*
> +	 * The rest of the work should be done by rfkill_check_status.
> +	 */
> +	return rfkill_check_status(key->rfkill->key, status);
> +}
> +
> +/*
> + * Handler that is run frequently to determine the current status
> + * of all registered keys that require polling.
> + */
> +static void rfkill_list_poll(void *data)
> +{
> +	struct rfkill_key *key;
> +	struct rfkill *rfkill;
> +	int status = 0;
> +
> +	down(&master->key_sem);
> +
> +	list_for_each_entry(key, &master->key_list, entry) {
> +		rfkill = key->rfkill;
> +
> +		/*
> +		 * If the poll handler has not been given
> +		 * the key driver should report events,
> +		 * so we can ignore this key now.
> +		 */
> +		if (!rfkill->poll)
> +			continue;
> +
> +		/*
> +		 * Poll radio state and check if a radio toggle
> +		 * has been requested by the key.
> +		 */
> +		if (rfkill_check_key(key, rfkill->poll(rfkill->data)))
> +			status = 1;
> +	}
> +
> +	up(&master->key_sem);
> +
> +	/*
> +	 * A radio toggle has been requested, schedule the toggle work thread.
> +	 */
> +	if (status)
> +		schedule_work(&master->toggle_work);
> +
> +	/*
> +	 * Check if we need to rearm ourselves.
> +	 */
> +	if (master->poll_required)
> +		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
> +}
> +
> +/*
> + * Function called by the key driver to report the new status
> + * of the key.
> + */
> +void rfkill_report_event(struct rfkill *rfkill, int new_status)
> +{
> +	down(&master->key_sem);
> +
> +	if (rfkill_check_key(rfkill->key, new_status))
> +		schedule_work(&master->toggle_work);
> +
> +	up(&master->key_sem);
> +}
> +EXPORT_SYMBOL_GPL(rfkill_report_event);
> +
> +/*
> + * Sysfs handler to release the data when sysfs is cleaning
> + * up all the garbage. This is done in a late state since the
> + * user could still be reading some of the files at the moment
> + * this key was removed.
> + * This method is being used for both rfkill_key as rfkill_type
> + * class devices. So be careful not to do anything structure
> + * specific.
> + */
> +static void rfkill_class_device_release(struct class_device *cdev)
> +{
> +	kfree(class_get_devdata(cdev));
> +}
> +
> +/*
> + * rfkill master sysfs creation.
> + */
> +static int rfkill_master_sysfs_create(struct rfkill_master *master)
> +{
> +	master->class = class_create(THIS_MODULE, "rfkill");
> +	if (unlikely(!master->class || IS_ERR(master->class)))
> +		return PTR_ERR(master->class);
> +
> +	master->class->release = rfkill_class_device_release;
> +
> +	return 0;
> +}
> +
> +/*
> + * rfkill master sysfs destroy.
> + */
> +static void rfkill_master_sysfs_release(struct rfkill_master *master)
> +{
> +	class_destroy(master->class);
> +}
> +
> +/*
> + * rfkill type sysfs creation.
> + */
> +static int rfkill_type_sysfs_create(struct rfkill_master *master,
> +				    struct rfkill_type *type)
> +{
> +	type->cdev = class_device_create(master->class, NULL, type->key_type,
> +					 NULL, type->name);
> +	if (unlikely(!type->cdev || IS_ERR(type->cdev)))
> +		return PTR_ERR(type->cdev);
> +
> +	return 0;
> +}
> +
> +static void rfkill_type_sysfs_release(struct rfkill_type *type)
> +{
> +	class_device_destroy(type->cdev->class, type->key_type);
> +}
> +
> +/*
> + * rfkill key attribute to display the current key status.
> + */
> +static ssize_t rfkill_key_status_show(struct class_device *cdev, char *buf)
> +{
> +	struct rfkill_key *key = class_get_devdata(cdev);
> +
> +	if (!capable(CAP_NET_ADMIN))
> +		return -EPERM;
> +
> +	return sprintf(buf, "%d", key->key_status);
> +}
> +
> +/*
> + * rfkill key attribute to change the current radio status.
> + */
> +static ssize_t rfkill_key_status_store(struct class_device *cdev,
> +				      const char *buf, size_t count)
> +{
> +	struct rfkill_key *key = class_get_devdata(cdev);
> +	int status = simple_strtoul(buf, NULL, 0);
> +
> +	if (!capable(CAP_NET_ADMIN))
> +		return -EPERM;
> +
> +	down(&master->key_sem);
> +
> +	/*
> +	 * Check if this new status implies that the radio
> +	 * should be toggled to the correct state.
> +	 */
> +	if (rfkill_check_status(key, status))
> +		schedule_work(&master->toggle_work);
> +
> +	up(&master->key_sem);
> +
> +	return count;
> +}
> +
> +static struct class_device_attribute cdev_attr_key_status =
> +	__ATTR(status, S_IRUGO | S_IWUGO,
> +	       rfkill_key_status_show, rfkill_key_status_store);
> +
> +/*
> + * rfkill key sysfs creation.
> + */
> +static int rfkill_key_sysfs_create(struct rfkill_master *master,
> +				    struct rfkill_key *key)
> +{
> +	int status;
> +
> +	key->cdev = class_device_create(master->class, key->type->cdev,
> +					KEY_TYPE_MAX + key->key_index,
> +					key->rfkill->dev,
> +					"key%d", key->key_index);
> +	if (unlikely(!key->cdev || IS_ERR(key->cdev)))
> +		return PTR_ERR(key->cdev);
> +
> +	/*
> +	 * Create the sysfs files.
> +	 */
> +	status = class_device_create_file(key->cdev, &cdev_attr_key_status);
> +	if (unlikely(status))
> +		goto exit;
> +
> +	/*
> +	 * The key belongs to the device structure the key driver
> +	 * has given us a reference to. But besides that device
> +	 * this key also belongs to the input device that has been
> +	 * created. We should create the link to that entry manually.
> +	 */
> +	status = sysfs_create_link(&key->cdev->kobj, &key->input->cdev.kobj,
> +				   "idev");
> +	if (unlikely(status))
> +		goto exit_file;
> +
> +	return 0;
> +
> +exit_file:
> +	class_device_remove_file(key->cdev, &cdev_attr_key_status);
> +
> +exit:
> +	class_device_destroy(master->class, KEY_TYPE_MAX + key->key_index);
> +
> +	return status;
> +}
> +
> +static void rfkill_key_sysfs_release(struct rfkill_key *key)
> +{
> +	sysfs_remove_link(&key->cdev->kobj, "idev");
> +	class_device_remove_file(key->cdev, &cdev_attr_key_status);
> +	class_device_destroy(master->class, KEY_TYPE_MAX + key->key_index);
> +}
> +
> +/*
> + * Handlers to enable or close the input device.
> + * These handlers only have to increase or decrease the open_count,
> + * this counter will automatically make sure key events will be
> + * send to the correct location (userspace or key driver).
> + */
> +static int rfkill_open(struct input_dev *dev)
> +{
> +	((struct rfkill_key*)(dev->private))->open_count++;
> +	return 0;
> +}
> +	
> +static void rfkill_close(struct input_dev *dev)
> +{
> +	((struct rfkill_key*)(dev->private))->open_count--;
> +}
> +
> +/*
> + * Input device initialization handler.
> + */
> +static struct input_dev* rfkill_register_input(struct rfkill_key *key)
> +{
> +	struct input_dev *input;
> +	int status;
> +	char *name;
> +	char *phys;
> +
> +	input = input_allocate_device();
> +	if (unlikely(!input))
> +		return NULL;
> +
> +	/*
> +	 * Link the private data to rfkill structure.
> +	 */
> +	input->private = key;
> +
> +	/*
> +	 * Allocate the strings for the input device names.
> +	 */
> +	name = kasprintf(GFP_KERNEL, "rfkill key%d: %s",
> +			 key->key_index, key->rfkill->dev_name);
> +	phys = kasprintf(GFP_KERNEL, "rfkill/%s/key%d",
> +			 key->type->name, key->key_index);
> +
> +	if (!name || !phys)
> +		goto exit;
> +
> +	input->name = name;
> +	input->phys = phys;
> +
> +	/*
> +	 * Initialize the input_id structure.
> +	 */
> +	input->id.bustype = BUS_HOST;
> +	input->id.vendor = 0x0001;
> +	input->id.product = 0x0001;
> +	input->id.version = 0x0001;
> +
> +	input->evbit[0] = BIT(EV_KEY);
> +	set_bit(KEY_RFKILL, input->keybit);
> +
> +	input->open = rfkill_open;
> +	input->close = rfkill_close;
> +
> +	status = input_register_device(input);
> +	if (status)
> +		goto exit;
> +
> +	return input;
> +
> +exit:
> +	input_free_device(input);
> +
> +	kfree(name);
> +	kfree(phys);
> +
> +	return NULL;
> +}
> +
> +/*
> + * Input device deinitialization handler.
> + */
> +static void rfkill_deregister_input(struct rfkill_key *key)
> +{
> +	const char *name;
> +	const char *phys;
> +
> +	/*
> +	 * The name and phys fields have been allocated
> +	 * using kasprintf, this means they have to be
> +	 * freed seperately.
> +	 */
> +	name = key->input->name;
> +	phys = key->input->phys;
> +
> +	input_unregister_device(key->input);
> +	kfree(name);
> +	kfree(phys);
> +}
> +
> +/*
> + * Rfkill key initialization/deinitialization.
> + * These methods should only be called with the key_sem held.
> + */
> +static struct rfkill_key* rfkill_key_init(struct rfkill *rfkill, int status)
> +{
> +	struct rfkill_key *key;
> +
> +	key = kzalloc(sizeof(struct rfkill_key), GFP_KERNEL);
> +	if (unlikely(!key))
> +		return NULL;
> +
> +	/*
> +	 * Initialize all variables.
> +	 */
> +	key->rfkill = rfkill;
> +	rfkill->key = key;
> +	key->type = &master->type[rfkill->key_type];
> +	key->toggled = 0;
> +	key->radio_status = status;
> +	key->key_status = status;
> +	key->key_index = master->key_count++;
> +	INIT_LIST_HEAD(&key->entry);
> +
> +	/*
> +	 * Create input device.
> +	 */
> +	key->input = rfkill_register_input(key);
> +	if (!key->input)
> +		goto exit;
> +
> +	/*
> +	 * Create sysfs entry.
> +	 */
> +	if (rfkill_key_sysfs_create(master, key))
> +		goto exit;
> +
> +	return key;
> +
> +exit:
> +	rfkill_deregister_input(key);
> +	rfkill->key = NULL;
> +	kfree(key);
> +	return NULL;
> +}
> +
> +static void rfkill_key_deinit(struct rfkill_key *key)
> +{
> +	if (key) {
> +		rfkill_key_sysfs_release(key);
> +		rfkill_deregister_input(key);
> +	}
> +}
> +
> +/*
> + * Rfkill type handler to check if the sysfs entries should
> + * be created or removed based on the number of registered keys.
> + */
> +static int rfkill_add_type_key(struct rfkill_type *type)
> +{
> +	int status;
> +
> +	if (type->key_count) {
> +		type->key_count++;
> +		return 0;
> +	}
> +
> +	status = rfkill_type_sysfs_create(master, type);
> +	if (status)
> +		return status;
> +
> +	type->key_count = 1;
> +	return 0;
> +}
> +
> +static void rfkill_del_type_key(struct rfkill_type *type)
> +{
> +	if (!--type->key_count)
> +		rfkill_type_sysfs_release(type);
> +}
> +
> +/*
> + * Function called by the key driver when the rfkill structure
> + * needs to be registered.
> + */
> +int rfkill_register_key(struct rfkill *rfkill, int init_status)
> +{
> +	struct rfkill_type *type = &master->type[rfkill->key_type];
> +	struct rfkill_key *key;
> +	int status;
> +
> +	if (!rfkill)
> +		return -EINVAL;	
> +
> +	if (rfkill->key_type >= KEY_TYPE_MAX)
> +		return -EINVAL;
> +
> +	/*
> +	 * Increase module use count to prevent this
> +	 * module to be unloaded while there are still
> +	 * registered keys.
> +	 */
> +	if (!try_module_get(THIS_MODULE))
> +		return -EBUSY;
> +
> +	down(&master->key_sem);
> +
> +	/*
> +	 * Initialize key, and if required the type.
> +	 */
> +	status = rfkill_add_type_key(type);
> +	if (status)
> +		goto exit;
> +
> +	key = rfkill_key_init(rfkill, init_status);
> +	if (!key) {
> +		status = -ENOMEM;
> +		goto exit_type;
> +	}
> +
> +	/*
> +	 * Add key to our list.
> +	 */
> +	list_add(&key->entry, &master->key_list);
> +
> +	/*
> +	 * Check if we need polling, and if we do
> +	 * increase the poll required counter and check
> +	 * if we weren't polling yet.
> +	 */
> +	if (rfkill->poll && !master->poll_required++)
> +		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
> +
> +	up(&master->key_sem);
> +
> +	return 0;
> +
> +exit_type:
> +	rfkill_del_type_key(type);
> +
> +exit:
> +	up(&master->key_sem);
> +	module_put(THIS_MODULE);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(rfkill_register_key);
> +
> +/*
> + * Function called by the key driver when the rfkill structure
> + * needs to be deregistered.
> + */
> +int rfkill_deregister_key(struct rfkill *rfkill)
> +{
> +	struct rfkill_type *type;
> +
> +	if (!rfkill || !rfkill->key)
> +		return -EINVAL;
> +
> +	down(&master->key_sem);
> +
> +	/*
> +	 * Cancel delayed work if this is the last key
> +	 * that requires polling. It is not bad if
> +	 * if the workqueue is still running,
> +	 * the workqueue won't rearm itself since the
> +	 * poll_required variable has been set.
> +	 * and we have protected the list with a semaphore.
> +	 */
> +	if (rfkill->poll && !--master->poll_required)
> +		cancel_delayed_work(&master->poll_work);
> +
> +	/*
> +	 * Delete the rfkill structure to the list.
> +	 */
> +	list_del(&rfkill->key->entry);
> +
> +	/*
> +	 * Deinitialize key.
> +	 */
> +	type = rfkill->key->type;
> +	rfkill_key_deinit(rfkill->key);
> +	rfkill_del_type_key(type);
> +
> +	up(&master->key_sem);
> +
> +	/*
> +	 * rfkill entry has been removed,
> +	 * decrease module use count.
> +	 */
> +	module_put(THIS_MODULE);
> +	
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(rfkill_deregister_key);
> +
> +/*
> + * Rfkill type initialization.
> + */
> +static void rfkill_type_init(unsigned int key_type, char *name)
> +{
> +	struct rfkill_type *type = &master->type[key_type];
> +
> +	/*
> +	 * Initialize all variables.
> +	 */
> +	type->name = name;
> +	type->key_type = key_type;
> +	type->key_count = 0;
> +}
> +
> +/*
> + * Rfkill master initialization/deinitialization.
> + */
> +static int rfkill_master_init(void)
> +{
> +	int status;
> +
> +	master = kzalloc(sizeof(struct rfkill_master), GFP_KERNEL);
> +	if (unlikely(!master))
> +		return -ENOMEM;
> +
> +	/*
> +	 * Initialize all variables.
> +	 */
> +	init_MUTEX(&master->key_sem);
> +	INIT_LIST_HEAD(&master->key_list);
> +	INIT_WORK(&master->toggle_work, &rfkill_toggle_radio, NULL);
> +	INIT_WORK(&master->poll_work, &rfkill_list_poll, NULL);
> +
> +	/*
> +	 * Initialize all type structures.
> +	 */
> +	rfkill_type_init(KEY_TYPE_WIFI, "wifi");
> +	rfkill_type_init(KEY_TYPE_BlUETOOTH, "bluetooth");
> +	rfkill_type_init(KEY_TYPE_IRDA, "irda");
> +
> +	/*
> +	 * Create sysfs entry.
> +	 */
> +	status = rfkill_master_sysfs_create(master);
> +	if (status) {
> +		kfree(master);
> +		master = NULL;
> +		return status;
> +	}
> +
> +	return 0;
> +}
> +
> +static void rfkill_master_deinit(void)
> +{
> +	if (master) {
> +		rfkill_master_sysfs_release(master);
> +		kfree(master);
> +		master = NULL;
> +	}
> +}
> +
> +/*
> + * Module initialization/deinitialization.
> + */
> +static int __init rfkill_init(void)
> +{
> +	printk(KERN_INFO "Loading rfkill driver.\n");
> +
> +	return rfkill_master_init();
> +}
> +
> +static void __exit rfkill_exit(void)
> +{
> +	rfkill_master_deinit();
> +
> +	printk(KERN_INFO "Unloading rfkill driver.\n");
> +}
> +
> +module_init(rfkill_init);
> +module_exit(rfkill_exit);
> diff --git a/include/linux/input.h b/include/linux/input.h
> index c38507b..1b44108 100644
> --- a/include/linux/input.h
> +++ b/include/linux/input.h
> @@ -491,6 +491,7 @@ struct input_absinfo {
>  #define KEY_DIGITS		0x19d
>  #define KEY_TEEN		0x19e
>  #define KEY_TWEN		0x19f
> +#define KEY_RFKILL		0x1a0
>  
>  #define KEY_DEL_EOL		0x1c0
>  #define KEY_DEL_EOS		0x1c1
> diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
> new file mode 100644
> index 0000000..a455548
> --- /dev/null
> +++ b/include/linux/rfkill.h
> @@ -0,0 +1,140 @@
> +/*
> +	Copyright (C) 2006 Ivo van Doorn
> +
> +	This program is free software; you can redistribute it and/or modify
> +	it under the terms of the GNU General Public License as published by
> +	the Free Software Foundation; either version 2 of the License, or
> +	(at your option) any later version.
> +
> +	This program is distributed in the hope that it will be useful,
> +	but WITHOUT ANY WARRANTY; without even the implied warranty of
> +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +	GNU General Public License for more details.
> +
> +	You should have received a copy of the GNU General Public License
> +	along with this program; if not, write to the
> +	Free Software Foundation, Inc.,
> +	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +/*
> +	RF button support
> +	Laptops are quite often equiped with a RF key to enable or
> +	disable the radio of the wireless device attached to that key.
> +	This wireless device usually is an integrated wireless network device,
> +	infrared or bluetooth device.
> +	Some of these devices will disable the radio automatically when the
> +	RF key has been pressed, while other devices need to be polled
> +	for the RF key status, and leave the action to be taken up to the
> +	driver for that particular device.
> +	But in all cases the only interface that will have its radio disabled
> +	will be the device that has the RF key attached to it. It could however
> +	be desired that userspace performs this disabling of the radios in case
> +	more things than just disabling a single radio is desired.
> +
> +	The rfkill driver will contain a list of all devices with a RF button,
> +	and hardware drivers need to register their hardware to the rfkill
> +	interface. Rfkill will then take care of everything. If the RF key
> +	requires polling to obtain the status this will be handled by rfkill.
> +	If the RF key does not require polling but sends for example interrupts,
> +	the hardware driver can report the change of status to rfkill, without
> +	having to do any other action.
> +	Once the status of the key has changed and the hardware does not
> +	automatically enable or disable the radio rfkill provides the
> +	interface to do this correctly.
> +
> +	For each registered hardware button an input device will be created.
> +	If this input device has been opened by the user, rfkill will send a
> +	signal to userspace instead of the hardware about the new button
> +	status. This will allow userpace to perform the correct steps
> +	in order to bring down all interfaces.
> + */
> +
> +#ifndef RFKILL_H
> +#define RFKILL_H
> +
> +#include <linux/device.h>
> +
> +#define RFKILL_POLL_DELAY	( HZ / 10 )
> +
> +enum key_type {
> +	KEY_TYPE_WIFI = 0,
> +	KEY_TYPE_BlUETOOTH = 1,
> +	KEY_TYPE_IRDA = 2,
> +	KEY_TYPE_MAX = 3,
> +};
> +
> +/**
> + * struct rfkill - rfkill button control structure.
> + *
> + * @dev_name: Name of the interface. This will become the name
> + * 	of the input device which will be created for this button.
> + * @dev: Pointer to the device structure to which this button belongs to.
> + * @data: Pointer to the RF button drivers private data which will be
> + * 	passed along with the radio and polling handlers.
> + * @poll(void *data): Optional handler which will frequently be
> + * 	called to determine the current status of the RF button.
> + * @enable_radio(void *data): Optional handler to enable the radio
> + * 	once the RF button has been pressed and the hardware does enable
> + * 	the radio automaticly.
> + * @disable_radio(void *data): Optional handler to disable the radio
> + * 	once the RF button has been pressed and the hardware does disable
> + * 	the radio automaticly.
> + * @key_type: Radio type which the button controls, the value stored
> + * 	here should be a value from enum key_type.
> + * @key: Internal pointer that should not be touched by key driver.
> + *
> + * This structure can be used by a key driver to register the key
> + * to the rfkill driver in order to take control of the reporting
> + * to userspace or handling of radio status.
> + */
> +struct rfkill {
> +	const char *dev_name;
> +
> +	struct device *dev;
> +
> +	void *data;
> +	int (*poll)(void *data);
> +	void (*enable_radio)(void *data);
> +	void (*disable_radio)(void *data);
> +
> +	unsigned int key_type;
> +
> +	struct rfkill_key *key;
> +};
> +
> +/**
> + * rfkill_register_key - Deregister a previously registered rfkill structre.
> + * @rfkill: rfkill structure to be deregistered
> + * @init_status: initial status of the key at the time this function is called
> + *
> + * This function should be called by the key driver when the rfkill structure
> + * needs to be registered. Immediately from registration the key driver
> + * should be able to receive calls through the poll, enable_radio and
> + * disable_radio handlers if those were registered.
> + */
> +int rfkill_register_key(struct rfkill *rfkill, int init_status);
> +
> +/**
> + * rfkill_deregister_key - Deregister a previously registered rfkill structre.
> + * @rfkill: rfkill structure to be deregistered
> + *
> + * This function should be called by the key driver when the rfkill structure
> + * needs to be deregistered. This function may only be called if it was
> + * previously registered with rfkill_register_key.
> + */
> +int rfkill_deregister_key(struct rfkill *rfkill);
> +
> +/**
> + * rfkill_report_event - Report change in key status to rfkill handler.
> + * @rfkill: rfkill structure registered by key driver
> + * @new_status: new key status
> + *
> + * This function should be called by the key driver if it has not provided
> + * a poll handler with rfkill. As soon as the key driver has determined
> + * the status of the key has changed it should report the new status
> + * through this function.
> + */
> +void rfkill_report_event(struct rfkill *rfkill, int new_status);
> +
> +#endif /* RFKILL_H */
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 19:18 ` Arjan van de Ven
@ 2006-12-03 22:03   ` Ivo van Doorn
  0 siblings, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-03 22:03 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Sunday 03 December 2006 20:18, Arjan van de Ven wrote:
> On Sun, 2006-12-03 at 19:36 +0100, Ivo van Doorn wrote:
> > +
> > +       down(&master->key_sem);
> > + 
> 
> Hi,
> 
> this one seems to be used as a mutex only, please consider using a mutex
> instead, as is the default for new code since 2.6.16 or so....

It was indeed intended to be a mutex, I had however missed
the presence of the mutex header in the kernel.
Will fix this immediately.

Thanks,

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 19:20 ` Arjan van de Ven
@ 2006-12-03 22:05   ` Ivo van Doorn
  2006-12-03 22:28   ` Ivo van Doorn
  1 sibling, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-03 22:05 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Sunday 03 December 2006 20:20, Arjan van de Ven wrote:
> this open_count thing smells fishy to me; what are the locking rules for
> it? What guarantees that the readers of it don't get the value changed
> underneath them between looking at the value and doing whatever action
> depends on it's value ?

Good point, a race condition could indeed occur in the only reader
that sends the signal to the userspace through the input device.
I'll fix this immediately.

Thanks,

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 19:44 ` Dan Williams
@ 2006-12-03 22:16   ` Ivo van Doorn
  2006-12-04  8:53   ` Marcel Holtmann
  1 sibling, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-03 22:16 UTC (permalink / raw)
  To: Dan Williams
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger, davidz,
	Bastien Nocera

Hi,

> > This patch is a resend of a patch I has been send to the linux kernel
> > and netdev list earlier. The most recent version of a few weeks back
> > didn't compile since I missed 1 line in my patch that changed
> > include/linux/input.h.
> > 
> > This patch will offer the handling of radiokeys on a laptop.
> > Such keys often control the wireless devices on the radio
> > like the wifi, bluetooth and irda.
> 
> Ok, what was the conclusion on the following issues?
> 
> 1) What about rfkill keys that aren't routed via software?  There are
> two modes here: (a) the key is not hardwired to the module and the
> driver is responsible for disabling the radio, (b) the key is hardwired
> to the module and firmware or some other mechanism disables the radio
> without driver interaction.  I thought I'd heard of some (b) hardware,
> mostly on older laptops, but does anyone know how prevalent (b) hardware
> is?  If there isn't any, do we care about it for now?

On condition
a)
The driver is supposed to read the button status by the method provided by
the device. (i.e. reading the register) the rfkill will periodically poll the driver
(if the polling method has been provided by the driver) and the rfkill
will do its work when the status of the register has changed.
With the input device open, the user can listen to the events and switch off
the radio manually (by using a tool like ifconfig or through the sysfs field)
When the input device is not open, rfkill will use the driver provided callback
functions to enable or disable the radio.

b)
In this case an interrupt is usually send to the driver about the event, or still the
register will be possible. On both occassions the signal can still be caught by
rfkill and handled further.
If the event is send to userspace so the user can still perform tasks,
the user can however not use the sysfs field to change the radio status
since it is only allowed to switch the radio to the status that the button indicates.
But the user can still perform tasks that should be handled (like stopping
programs that need the network).

I have heard that the broadcom chipsets toggle the radio state without
intervention of the driver, and instead only send an interrupt event.

> 2) Is there hardware that has separate keys for separate radios?  i.e.,
> one key for Bluetooth, and one key for WiFi?  I know Bastien has a
> laptop where the same rfkill key handles _both_ ipw2200 and the BT
> module, but in different ways: it actually removes the BT USB device
> from the USB bus, but uses the normal ipw rfkill mechanism.

Don't know about this hardware, my laptop has 2 seperate buttons for wifi
and bluetooth.
But it would be quite hard to caught such events cleanly, in this case the
option would be to register 2 seperate rfkill_key structures. That way the
key is represented twice to the user. But the enable_radio and disable _radio
callback functions from the driver would make the callback for the wifi and
bluetooth radio  individually possibly.

> 3) How does this interact with HAL?  What's the userspace interface that
> HAL will listen to to receive the signals?  NetworkManager will need to
> listen to HAL for these, as rfkill switches are one big thing that NM
> does not handle now due to lack of a standard mechanism.

In userspace there are 2 ways to listen, either regularly check the sysfs entry
for the status. Or the prefered way listen to the input device that is created for
each key.

> In any case, any movement on rfkill interface/handling standardization
> is quite welcome :)

True, there are currently a lot of methods floating around. And a single way
to notify the user would be a nice idea. :)

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 19:20 ` Arjan van de Ven
  2006-12-03 22:05   ` Ivo van Doorn
@ 2006-12-03 22:28   ` Ivo van Doorn
  2006-12-05  0:18     ` Randy Dunlap
  1 sibling, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-03 22:28 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

rfkill with the fixes as suggested by Arjan.
 - instead of a semaphore a mutex is now being used.
 - open_count changing is now locked by the mutex.

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>

---

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ba0e88c..6986d59 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -79,4 +79,19 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config RFKILL
+	tristate "RF button support"
+	help
+	  If you say yes here, the rfkill driver will be build
+	  which allowed network devices to register their hardware
+	  RF button which controls the radio state. This driver
+	  will then create an input device for it.
+
+	  When the input device is not used, the rfkill driver
+	  will make sure that when the RF button is pressed the radio
+	  is enabled or disabled accordingly. When the input device
+	  has been opened by the user this radio control will be left
+	  to the user, and rfkill will only send the RF button status
+	  change to userspace.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415c491..e788a1b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_RFKILL)			+= rfkill.o
diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
new file mode 100644
index 0000000..4777d73
--- /dev/null
+++ b/drivers/input/misc/rfkill.c
@@ -0,0 +1,887 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF key support");
+MODULE_LICENSE("GPL");
+
+/*
+ * rfkill key structure.
+ */
+struct rfkill_key {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Pointer to rfkill structure
+	 * that was filled in by key driver.
+	 */
+	struct rfkill *rfkill;
+
+	/*
+	 * Pointer to type structure that this key belongs to.
+	 */
+	struct rfkill_type *type;
+
+	/*
+	 * Once key status change has been detected, the toggled
+	 * field should be set to indicate a notification to
+	 * user or driver should be performed.
+	 */
+	int toggled;
+
+	/*
+	 * Current state of the device radio, this state will
+	 * change after the radio has actually been toggled since
+	 * receiving the radio key event.
+	 */
+	int radio_status;
+
+	/*
+	 * Current status of the key which controls the radio,
+	 * this value will change after the key state has changed
+	 * after polling, or the key driver has send the new state
+	 * manually.
+	 */
+	int key_status;
+
+	/*
+	 * Input device for this key,
+	 * we also keep track of the number of
+	 * times this input device is open. This
+	 * is important for determining to whom we
+	 * should report key events.
+	 */
+	struct input_dev *input;
+	unsigned int open_count;
+
+	/*
+	 * Key index number.
+	 */
+	unsigned int key_index;
+
+	/*
+	 * List head structure to be used
+	 * to add this structure to the list.
+	 */
+	struct list_head entry;
+};
+
+/*
+ * rfkill key type structure.
+ */
+struct rfkill_type {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Name of this radio type.
+	 */
+	char *name;
+
+	/*
+	 * Key type identification. Value must be any
+	 * in the key_type enum.
+	 */
+	unsigned int key_type;
+
+	/*
+	 * Number of registered keys of this type.
+	 */
+	unsigned int key_count;
+};
+
+/*
+ * rfkill master structure.
+ */
+struct rfkill_master {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class *class;
+
+	/*
+	 * All access to the master structure
+	 * and its children (the keys) are protected
+	 * by this key lock.
+	 */
+	struct mutex mutex;
+
+	/*
+	 * List of available key types.
+	 */
+	struct rfkill_type type[KEY_TYPE_MAX];
+
+	/*
+	 * Total number of registered keys.
+	 */
+	unsigned int key_count;
+
+	/*
+	 * Number of keys that require polling
+	 */
+	unsigned int poll_required;
+
+	/*
+	 * List of rfkill_key structures.
+	 */
+	struct list_head key_list;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	struct work_struct toggle_work;
+	struct work_struct poll_work;
+};
+
+/*
+ * We only need 1 single master interface,
+ * make this a global variable since we always need it.
+ */
+static struct rfkill_master *master;
+
+/*
+ * Send toggle event to key driver.
+ */
+static void rfkill_toggle_radio_driver(struct rfkill_key *key)
+{
+	struct rfkill *rfkill = key->rfkill;
+
+	/*
+	 * check what the current radio status is, and perform
+	 * the correct action to toggle the radio. This will make
+	 * sure the correct event happens when either the key driver
+	 * of the user has requested to toggle this radio.
+	 */
+	if (!key->radio_status && rfkill->enable_radio)
+		rfkill->enable_radio(rfkill->data);
+	else if (key->radio_status && rfkill->disable_radio)
+		rfkill->disable_radio(rfkill->data);
+
+	/*
+	 * Independent of the presence of the enable_radio and
+	 * disable_radio handler update the radio_status field.
+	 * Note that this is valid, since if the key driver did
+	 * not provide the 2 radio handlers, the driver is in
+	 * charge of correctly setting the radio. This is usually
+	 * done because the hardware will toggle the radio itself.
+	 */
+	key->radio_status = key->key_status;
+}
+
+/*
+ * Send toggle event to userspace through the input device.
+ */
+static void rfkill_toggle_radio_input(struct rfkill_key *key)
+{
+	input_report_key(key->input, KEY_RFKILL, key->key_status);
+	input_sync(key->input);
+}
+
+/*
+ * Loop through the list of registered keys and
+ * check if key if it has been toggled.
+ */
+static void rfkill_toggle_radio(void *data)
+{
+	struct rfkill_key *key;
+
+	mutex_lock(&master->mutex);
+
+	list_for_each_entry(key, &master->key_list, entry) {
+
+		/*
+		 * If this key hasn't been toggled by either
+		 * the key driver or the user we can skip this key.
+		 */
+		if (!key->toggled)
+			continue;
+
+		/*
+		 * If the input device has been opened, all events
+		 * should be send to userspace otherwise the key driver
+		 * is supposed to handle the event.
+		 */
+		if (key->open_count)
+			rfkill_toggle_radio_input(key);
+		else
+			rfkill_toggle_radio_driver(key);
+
+		/*
+		 * Reset the toggled field to allow new toggle events.
+		 */
+		key->toggled = 0;
+	}
+
+	mutex_unlock(&master->mutex);
+}
+
+/*
+ * Check the new status of the key, and determine if the key has been toggled.
+ * This function can be called upon request by the device driver or userspace.
+ */
+static int rfkill_check_status(struct rfkill_key *key, int status)
+{
+	/*
+	 * A key should be toggled if the current radio status does not
+	 * match the requested status. This check is required instead of
+	 * comparing it to the current key status since this function can
+	 * be called on request of the user as well. The user is able to
+	 * request a radio toggle to make sure the radio status will match
+	 * the key status if the new key status has been reported to
+	 * userspace only.
+	 * As a safety measure, we won't toggle a key twice.
+	 */
+	if (key->radio_status != !!status && !key->toggled) {
+		key->toggled = 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Wrapper around the rfkill_check_status function, this may only
+ * be called from the device driver status events. Besides calling
+ * rfkill_check_status it will also update the key_status field.
+ */
+static int rfkill_check_key(struct rfkill_key *key, int status)
+{
+	/*
+	 * Store the new key status value.
+	 */
+	key->key_status = !!status;
+
+	/*
+	 * The rest of the work should be done by rfkill_check_status.
+	 */
+	return rfkill_check_status(key->rfkill->key, status);
+}
+
+/*
+ * Handler that is run frequently to determine the current status
+ * of all registered keys that require polling.
+ */
+static void rfkill_list_poll(void *data)
+{
+	struct rfkill_key *key;
+	struct rfkill *rfkill;
+	int status = 0;
+
+	mutex_lock(&master->mutex);
+
+	list_for_each_entry(key, &master->key_list, entry) {
+		rfkill = key->rfkill;
+
+		/*
+		 * If the poll handler has not been given
+		 * the key driver should report events,
+		 * so we can ignore this key now.
+		 */
+		if (!rfkill->poll)
+			continue;
+
+		/*
+		 * Poll radio state and check if a radio toggle
+		 * has been requested by the key.
+		 */
+		if (rfkill_check_key(key, rfkill->poll(rfkill->data)))
+			status = 1;
+	}
+
+	mutex_unlock(&master->mutex);
+
+	/*
+	 * A radio toggle has been requested, schedule the toggle work thread.
+	 */
+	if (status)
+		schedule_work(&master->toggle_work);
+
+	/*
+	 * Check if we need to rearm ourselves.
+	 */
+	if (master->poll_required)
+		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
+}
+
+/*
+ * Function called by the key driver to report the new status
+ * of the key.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status)
+{
+	mutex_lock(&master->mutex);
+
+	if (rfkill_check_key(rfkill->key, new_status))
+		schedule_work(&master->toggle_work);
+
+	mutex_unlock(&master->mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_report_event);
+
+/*
+ * Sysfs handler to release the data when sysfs is cleaning
+ * up all the garbage. This is done in a late state since the
+ * user could still be reading some of the files at the moment
+ * this key was removed.
+ * This method is being used for both rfkill_key as rfkill_type
+ * class devices. So be careful not to do anything structure
+ * specific.
+ */
+static void rfkill_class_device_release(struct class_device *cdev)
+{
+	kfree(class_get_devdata(cdev));
+}
+
+/*
+ * rfkill master sysfs creation.
+ */
+static int rfkill_master_sysfs_create(struct rfkill_master *master)
+{
+	master->class = class_create(THIS_MODULE, "rfkill");
+	if (unlikely(!master->class || IS_ERR(master->class)))
+		return PTR_ERR(master->class);
+
+	master->class->release = rfkill_class_device_release;
+
+	return 0;
+}
+
+/*
+ * rfkill master sysfs destroy.
+ */
+static void rfkill_master_sysfs_release(struct rfkill_master *master)
+{
+	class_destroy(master->class);
+}
+
+/*
+ * rfkill type sysfs creation.
+ */
+static int rfkill_type_sysfs_create(struct rfkill_master *master,
+				    struct rfkill_type *type)
+{
+	type->cdev = class_device_create(master->class, NULL, type->key_type,
+					 NULL, type->name);
+	if (unlikely(!type->cdev || IS_ERR(type->cdev)))
+		return PTR_ERR(type->cdev);
+
+	return 0;
+}
+
+static void rfkill_type_sysfs_release(struct rfkill_type *type)
+{
+	class_device_destroy(type->cdev->class, type->key_type);
+}
+
+/*
+ * rfkill key attribute to display the current key status.
+ */
+static ssize_t rfkill_key_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", key->key_status);
+}
+
+/*
+ * rfkill key attribute to change the current radio status.
+ */
+static ssize_t rfkill_key_status_store(struct class_device *cdev,
+				      const char *buf, size_t count)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+	int status = simple_strtoul(buf, NULL, 0);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	mutex_lock(&master->mutex);
+
+	/*
+	 * Check if this new status implies that the radio
+	 * should be toggled to the correct state.
+	 */
+	if (rfkill_check_status(key, status))
+		schedule_work(&master->toggle_work);
+
+	mutex_unlock(&master->mutex);
+
+	return count;
+}
+
+static struct class_device_attribute cdev_attr_key_status =
+	__ATTR(status, S_IRUGO | S_IWUGO,
+	       rfkill_key_status_show, rfkill_key_status_store);
+
+/*
+ * rfkill key sysfs creation.
+ */
+static int rfkill_key_sysfs_create(struct rfkill_master *master,
+				    struct rfkill_key *key)
+{
+	int status;
+
+	key->cdev = class_device_create(master->class, key->type->cdev,
+					KEY_TYPE_MAX + key->key_index,
+					key->rfkill->dev,
+					"key%d", key->key_index);
+	if (unlikely(!key->cdev || IS_ERR(key->cdev)))
+		return PTR_ERR(key->cdev);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(key->cdev, &cdev_attr_key_status);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * The key belongs to the device structure the key driver
+	 * has given us a reference to. But besides that device
+	 * this key also belongs to the input device that has been
+	 * created. We should create the link to that entry manually.
+	 */
+	status = sysfs_create_link(&key->cdev->kobj, &key->input->cdev.kobj,
+				   "idev");
+	if (unlikely(status))
+		goto exit_file;
+
+	return 0;
+
+exit_file:
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+
+exit:
+	class_device_destroy(master->class, KEY_TYPE_MAX + key->key_index);
+
+	return status;
+}
+
+static void rfkill_key_sysfs_release(struct rfkill_key *key)
+{
+	sysfs_remove_link(&key->cdev->kobj, "idev");
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+	class_device_destroy(master->class, KEY_TYPE_MAX + key->key_index);
+}
+
+/*
+ * Handlers to enable or close the input device.
+ * These handlers only have to increase or decrease the open_count,
+ * this counter will automatically make sure key events will be
+ * send to the correct location (userspace or key driver).
+ */
+static int rfkill_open(struct input_dev *dev)
+{
+	mutex_lock(&master->mutex);
+
+	((struct rfkill_key*)(dev->private))->open_count++;
+
+	mutex_unlock(&master->mutex);
+	return 0;
+}
+	
+static void rfkill_close(struct input_dev *dev)
+{
+	mutex_lock(&master->mutex);
+
+	((struct rfkill_key*)(dev->private))->open_count--;
+
+	mutex_unlock(&master->mutex);
+}
+
+/*
+ * Input device initialization handler.
+ */
+static struct input_dev* rfkill_register_input(struct rfkill_key *key)
+{
+	struct input_dev *input;
+	int status;
+	char *name;
+	char *phys;
+
+	input = input_allocate_device();
+	if (unlikely(!input))
+		return NULL;
+
+	/*
+	 * Link the private data to rfkill structure.
+	 */
+	input->private = key;
+
+	/*
+	 * Allocate the strings for the input device names.
+	 */
+	name = kasprintf(GFP_KERNEL, "rfkill key%d: %s",
+			 key->key_index, key->rfkill->dev_name);
+	phys = kasprintf(GFP_KERNEL, "rfkill/%s/key%d",
+			 key->type->name, key->key_index);
+
+	if (!name || !phys)
+		goto exit;
+
+	input->name = name;
+	input->phys = phys;
+
+	/*
+	 * Initialize the input_id structure.
+	 */
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0001;
+
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_RFKILL, input->keybit);
+
+	input->open = rfkill_open;
+	input->close = rfkill_close;
+
+	status = input_register_device(input);
+	if (status)
+		goto exit;
+
+	return input;
+
+exit:
+	input_free_device(input);
+
+	kfree(name);
+	kfree(phys);
+
+	return NULL;
+}
+
+/*
+ * Input device deinitialization handler.
+ */
+static void rfkill_deregister_input(struct rfkill_key *key)
+{
+	const char *name;
+	const char *phys;
+
+	/*
+	 * The name and phys fields have been allocated
+	 * using kasprintf, this means they have to be
+	 * freed seperately.
+	 */
+	name = key->input->name;
+	phys = key->input->phys;
+
+	input_unregister_device(key->input);
+	kfree(name);
+	kfree(phys);
+}
+
+/*
+ * Rfkill key initialization/deinitialization.
+ * These methods should only be called with the mutex held.
+ */
+static struct rfkill_key* rfkill_key_init(struct rfkill *rfkill, int status)
+{
+	struct rfkill_key *key;
+
+	key = kzalloc(sizeof(struct rfkill_key), GFP_KERNEL);
+	if (unlikely(!key))
+		return NULL;
+
+	/*
+	 * Initialize all variables.
+	 */
+	key->rfkill = rfkill;
+	rfkill->key = key;
+	key->type = &master->type[rfkill->key_type];
+	key->toggled = 0;
+	key->radio_status = status;
+	key->key_status = status;
+	key->key_index = master->key_count++;
+	INIT_LIST_HEAD(&key->entry);
+
+	/*
+	 * Create input device.
+	 */
+	key->input = rfkill_register_input(key);
+	if (!key->input)
+		goto exit;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	if (rfkill_key_sysfs_create(master, key))
+		goto exit;
+
+	return key;
+
+exit:
+	rfkill_deregister_input(key);
+	rfkill->key = NULL;
+	kfree(key);
+	return NULL;
+}
+
+static void rfkill_key_deinit(struct rfkill_key *key)
+{
+	if (key) {
+		rfkill_key_sysfs_release(key);
+		rfkill_deregister_input(key);
+	}
+}
+
+/*
+ * Rfkill type handler to check if the sysfs entries should
+ * be created or removed based on the number of registered keys.
+ */
+static int rfkill_add_type_key(struct rfkill_type *type)
+{
+	int status;
+
+	if (type->key_count) {
+		type->key_count++;
+		return 0;
+	}
+
+	status = rfkill_type_sysfs_create(master, type);
+	if (status)
+		return status;
+
+	type->key_count = 1;
+	return 0;
+}
+
+static void rfkill_del_type_key(struct rfkill_type *type)
+{
+	if (!--type->key_count)
+		rfkill_type_sysfs_release(type);
+}
+
+/*
+ * Function called by the key driver when the rfkill structure
+ * needs to be registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status)
+{
+	struct rfkill_type *type = &master->type[rfkill->key_type];
+	struct rfkill_key *key;
+	int status;
+
+	if (!rfkill)
+		return -EINVAL;	
+
+	if (rfkill->key_type >= KEY_TYPE_MAX)
+		return -EINVAL;
+
+	/*
+	 * Increase module use count to prevent this
+	 * module to be unloaded while there are still
+	 * registered keys.
+	 */
+	if (!try_module_get(THIS_MODULE))
+		return -EBUSY;
+
+	mutex_lock(&master->mutex);
+
+	/*
+	 * Initialize key, and if required the type.
+	 */
+	status = rfkill_add_type_key(type);
+	if (status)
+		goto exit;
+
+	key = rfkill_key_init(rfkill, init_status);
+	if (!key) {
+		status = -ENOMEM;
+		goto exit_type;
+	}
+
+	/*
+	 * Add key to our list.
+	 */
+	list_add(&key->entry, &master->key_list);
+
+	/*
+	 * Check if we need polling, and if we do
+	 * increase the poll required counter and check
+	 * if we weren't polling yet.
+	 */
+	if (rfkill->poll && !master->poll_required++)
+		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
+
+	mutex_unlock(&master->mutex);
+
+	return 0;
+
+exit_type:
+	rfkill_del_type_key(type);
+
+exit:
+	mutex_unlock(&master->mutex);
+	module_put(THIS_MODULE);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(rfkill_register_key);
+
+/*
+ * Function called by the key driver when the rfkill structure
+ * needs to be deregistered.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill)
+{
+	struct rfkill_type *type;
+
+	if (!rfkill || !rfkill->key)
+		return -EINVAL;
+
+	mutex_lock(&master->mutex);
+
+	/*
+	 * Cancel delayed work if this is the last key
+	 * that requires polling. It is not bad if
+	 * if the workqueue is still running,
+	 * the workqueue won't rearm itself since the
+	 * poll_required variable has been set.
+	 * and we have protected the list with a semaphore.
+	 */
+	if (rfkill->poll && !--master->poll_required)
+		cancel_delayed_work(&master->poll_work);
+
+	/*
+	 * Delete the rfkill structure to the list.
+	 */
+	list_del(&rfkill->key->entry);
+
+	/*
+	 * Deinitialize key.
+	 */
+	type = rfkill->key->type;
+	rfkill_key_deinit(rfkill->key);
+	rfkill_del_type_key(type);
+
+	mutex_unlock(&master->mutex);
+
+	/*
+	 * rfkill entry has been removed,
+	 * decrease module use count.
+	 */
+	module_put(THIS_MODULE);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rfkill_deregister_key);
+
+/*
+ * Rfkill type initialization.
+ */
+static void rfkill_type_init(unsigned int key_type, char *name)
+{
+	struct rfkill_type *type = &master->type[key_type];
+
+	/*
+	 * Initialize all variables.
+	 */
+	type->name = name;
+	type->key_type = key_type;
+	type->key_count = 0;
+}
+
+/*
+ * Rfkill master initialization/deinitialization.
+ */
+static int rfkill_master_init(void)
+{
+	int status;
+
+	master = kzalloc(sizeof(struct rfkill_master), GFP_KERNEL);
+	if (unlikely(!master))
+		return -ENOMEM;
+
+	/*
+	 * Initialize all variables.
+	 */
+	mutex_init(&master->mutex);
+	INIT_LIST_HEAD(&master->key_list);
+	INIT_WORK(&master->toggle_work, &rfkill_toggle_radio, NULL);
+	INIT_WORK(&master->poll_work, &rfkill_list_poll, NULL);
+
+	/*
+	 * Initialize all type structures.
+	 */
+	rfkill_type_init(KEY_TYPE_WIFI, "wifi");
+	rfkill_type_init(KEY_TYPE_BlUETOOTH, "bluetooth");
+	rfkill_type_init(KEY_TYPE_IRDA, "irda");
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_master_sysfs_create(master);
+	if (status) {
+		kfree(master);
+		master = NULL;
+		return status;
+	}
+
+	return 0;
+}
+
+static void rfkill_master_deinit(void)
+{
+	if (master) {
+		rfkill_master_sysfs_release(master);
+		kfree(master);
+		master = NULL;
+	}
+}
+
+/*
+ * Module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	printk(KERN_INFO "Loading rfkill driver.\n");
+
+	return rfkill_master_init();
+}
+
+static void __exit rfkill_exit(void)
+{
+	rfkill_master_deinit();
+
+	printk(KERN_INFO "Unloading rfkill driver.\n");
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
diff --git a/include/linux/input.h b/include/linux/input.h
index c38507b..1b44108 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -491,6 +491,7 @@ struct input_absinfo {
 #define KEY_DIGITS		0x19d
 #define KEY_TEEN		0x19e
 #define KEY_TWEN		0x19f
+#define KEY_RFKILL		0x1a0
 
 #define KEY_DEL_EOL		0x1c0
 #define KEY_DEL_EOS		0x1c1
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
new file mode 100644
index 0000000..a455548
--- /dev/null
+++ b/include/linux/rfkill.h
@@ -0,0 +1,140 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+	RF button support
+	Laptops are quite often equiped with a RF key to enable or
+	disable the radio of the wireless device attached to that key.
+	This wireless device usually is an integrated wireless network device,
+	infrared or bluetooth device.
+	Some of these devices will disable the radio automatically when the
+	RF key has been pressed, while other devices need to be polled
+	for the RF key status, and leave the action to be taken up to the
+	driver for that particular device.
+	But in all cases the only interface that will have its radio disabled
+	will be the device that has the RF key attached to it. It could however
+	be desired that userspace performs this disabling of the radios in case
+	more things than just disabling a single radio is desired.
+
+	The rfkill driver will contain a list of all devices with a RF button,
+	and hardware drivers need to register their hardware to the rfkill
+	interface. Rfkill will then take care of everything. If the RF key
+	requires polling to obtain the status this will be handled by rfkill.
+	If the RF key does not require polling but sends for example interrupts,
+	the hardware driver can report the change of status to rfkill, without
+	having to do any other action.
+	Once the status of the key has changed and the hardware does not
+	automatically enable or disable the radio rfkill provides the
+	interface to do this correctly.
+
+	For each registered hardware button an input device will be created.
+	If this input device has been opened by the user, rfkill will send a
+	signal to userspace instead of the hardware about the new button
+	status. This will allow userpace to perform the correct steps
+	in order to bring down all interfaces.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+#include <linux/device.h>
+
+#define RFKILL_POLL_DELAY	( HZ / 10 )
+
+enum key_type {
+	KEY_TYPE_WIFI = 0,
+	KEY_TYPE_BlUETOOTH = 1,
+	KEY_TYPE_IRDA = 2,
+	KEY_TYPE_MAX = 3,
+};
+
+/**
+ * struct rfkill - rfkill button control structure.
+ *
+ * @dev_name: Name of the interface. This will become the name
+ * 	of the input device which will be created for this button.
+ * @dev: Pointer to the device structure to which this button belongs to.
+ * @data: Pointer to the RF button drivers private data which will be
+ * 	passed along with the radio and polling handlers.
+ * @poll(void *data): Optional handler which will frequently be
+ * 	called to determine the current status of the RF button.
+ * @enable_radio(void *data): Optional handler to enable the radio
+ * 	once the RF button has been pressed and the hardware does enable
+ * 	the radio automaticly.
+ * @disable_radio(void *data): Optional handler to disable the radio
+ * 	once the RF button has been pressed and the hardware does disable
+ * 	the radio automaticly.
+ * @key_type: Radio type which the button controls, the value stored
+ * 	here should be a value from enum key_type.
+ * @key: Internal pointer that should not be touched by key driver.
+ *
+ * This structure can be used by a key driver to register the key
+ * to the rfkill driver in order to take control of the reporting
+ * to userspace or handling of radio status.
+ */
+struct rfkill {
+	const char *dev_name;
+
+	struct device *dev;
+
+	void *data;
+	int (*poll)(void *data);
+	void (*enable_radio)(void *data);
+	void (*disable_radio)(void *data);
+
+	unsigned int key_type;
+
+	struct rfkill_key *key;
+};
+
+/**
+ * rfkill_register_key - Deregister a previously registered rfkill structre.
+ * @rfkill: rfkill structure to be deregistered
+ * @init_status: initial status of the key at the time this function is called
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be registered. Immediately from registration the key driver
+ * should be able to receive calls through the poll, enable_radio and
+ * disable_radio handlers if those were registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status);
+
+/**
+ * rfkill_deregister_key - Deregister a previously registered rfkill structre.
+ * @rfkill: rfkill structure to be deregistered
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be deregistered. This function may only be called if it was
+ * previously registered with rfkill_register_key.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill);
+
+/**
+ * rfkill_report_event - Report change in key status to rfkill handler.
+ * @rfkill: rfkill structure registered by key driver
+ * @new_status: new key status
+ *
+ * This function should be called by the key driver if it has not provided
+ * a poll handler with rfkill. As soon as the key driver has determined
+ * the status of the key has changed it should report the new status
+ * through this function.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status);
+
+#endif /* RFKILL_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 19:44 ` Dan Williams
  2006-12-03 22:16   ` Ivo van Doorn
@ 2006-12-04  8:53   ` Marcel Holtmann
  1 sibling, 0 replies; 42+ messages in thread
From: Marcel Holtmann @ 2006-12-04  8:53 UTC (permalink / raw)
  To: Dan Williams
  Cc: Ivo van Doorn, Dmitry Torokhov, linux-kernel, netdev,
	John Linville, Jiri Benc, Lennart Poettering, Johannes Berg,
	Larry Finger, davidz, Bastien Nocera

Hi Dan,

> 3) How does this interact with HAL?  What's the userspace interface that
> HAL will listen to to receive the signals?  NetworkManager will need to
> listen to HAL for these, as rfkill switches are one big thing that NM
> does not handle now due to lack of a standard mechanism.
> 
> In any case, any movement on rfkill interface/handling standardization
> is quite welcome :)

I want some handling for the Bluetooth rfkill in HAL, so our config
application can physically turn on/off the Bluetooth chip with a simple
method call to the HAL D-Bus interface. We also need to discover the
existence of such a rfkill switch.

Regards

Marcel



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
                   ` (2 preceding siblings ...)
  2006-12-03 19:44 ` Dan Williams
@ 2006-12-04 22:15 ` Dmitry Torokhov
  2006-12-04 23:27   ` Ivo van Doorn
  2006-12-05 10:32 ` Christoph Hellwig
  2007-01-31  3:40 ` Stephen Hemminger
  5 siblings, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-04 22:15 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On 12/3/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> Hi,
>
> This patch is a resend of a patch I has been send to the linux kernel
> and netdev list earlier. The most recent version of a few weeks back
> didn't compile since I missed 1 line in my patch that changed
> include/linux/input.h.
>
> This patch will offer the handling of radiokeys on a laptop.
> Such keys often control the wireless devices on the radio
> like the wifi, bluetooth and irda.
>
> The rfkill works as follows, when the user presses the hardware key
> to control the wireless (wifi, bluetooth or irda) radio a signal will
> go to rfkill. This happens by the hardware driver sending a signal
> to rfkill, or rfkill polling for the button status.
> The key signal will then be processed by rfkill, each key will have
> its own input device, if this input device has not been openened
> by the user, rfkill will keep the signal and either turn the radio
> on or off based on the key status.
> If the input device has been opened, rfkill will send the signal to
> userspace and do nothing further. The user is in charge of controlling
> the radio.
>
> This driver (if accepted and applied) will be usefull for the rt2x00 drivers
> (rt2400pci, rt2500pci, rt2500usb, rt61pci and rt73usb) in the wireless-dev
> tree and the MSI laptop driver from Lennart Poettering in the main
> linux kernel tree.
>
> Before this patch can be applied to any tree, I first wish to hear
> if the patch is acceptable. Since the previous time it was send I have made
> several fixes based on the feedback like adding the sysfs entries for
> reading the status.
>

Hi Ivo,

I apologize for not responding to your post earlier, it was a busy week.

I am still not sure that tight coupling of input device with rfkill
structure is such a good idea. Quite often the button is separated
from the device itself and radio control is done via BIOS SMM (see
wistron driver) or there is no special button at all and users might
want to assign one of their standard keyboard buttons to be an RF
switch.

I think it would be better if there was an rfkill class listing all
controlled devices (preferrably grouped by their type - WiFi, BT,
IRDA, etc) and if every group would provide an attribute allowing to
control state of the whole group (do we realistically need to kill
just one interface? Wouldn't ifconfig be suitable for that?). The
attribute should be a tri-state on/off/auto, "auto" meaning the driver
itself manages radio state. This would avoid another tacky IMHO point
that in your implementation mere opening of an input device takes over
RF driver. Explicit control allow applications "snoop" RF state
without disturbing it.

If there are concerns that drivers will have to re-implement most of
the button handling you are still free to create a simple
implementation of polled RF button (I don't think that interrupt
driver RF buttons would share alot of code) so that users would only
need to implement a polling function.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-04 22:15 ` Dmitry Torokhov
@ 2006-12-04 23:27   ` Ivo van Doorn
  2006-12-06 14:37     ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-04 23:27 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> > This patch is a resend of a patch I has been send to the linux kernel
> > and netdev list earlier. The most recent version of a few weeks back
> > didn't compile since I missed 1 line in my patch that changed
> > include/linux/input.h.
> >
> > This patch will offer the handling of radiokeys on a laptop.
> > Such keys often control the wireless devices on the radio
> > like the wifi, bluetooth and irda.
> >
> > The rfkill works as follows, when the user presses the hardware key
> > to control the wireless (wifi, bluetooth or irda) radio a signal will
> > go to rfkill. This happens by the hardware driver sending a signal
> > to rfkill, or rfkill polling for the button status.
> > The key signal will then be processed by rfkill, each key will have
> > its own input device, if this input device has not been openened
> > by the user, rfkill will keep the signal and either turn the radio
> > on or off based on the key status.
> > If the input device has been opened, rfkill will send the signal to
> > userspace and do nothing further. The user is in charge of controlling
> > the radio.
> >
> > This driver (if accepted and applied) will be usefull for the rt2x00 drivers
> > (rt2400pci, rt2500pci, rt2500usb, rt61pci and rt73usb) in the wireless-dev
> > tree and the MSI laptop driver from Lennart Poettering in the main
> > linux kernel tree.
> >
> > Before this patch can be applied to any tree, I first wish to hear
> > if the patch is acceptable. Since the previous time it was send I have made
> > several fixes based on the feedback like adding the sysfs entries for
> > reading the status.
> >
> 
> Hi Ivo,
> 
> I apologize for not responding to your post earlier, it was a busy week.

No problem, it didn't compile anyway. And this time I have CC'ed all
people that have previously shown interest in rfkill, so they are now
updated about rfkill as well. ;)

> I am still not sure that tight coupling of input device with rfkill
> structure is such a good idea. Quite often the button is separated
> from the device itself and radio control is done via BIOS SMM (see
> wistron driver) or there is no special button at all and users might
> want to assign one of their standard keyboard buttons to be an RF
> switch.

Making sure rfkill supports keys that are not handled by the driver
is a bit hard. Just as drivers that can only check if the button is
toggled and not what the current state is.
The problem is that it is hard to make a clean split between the
2 different button controls. Not all drivers allow the radio to be
enabled while the button status are indicating the radio should
be off.
The buttons that are already integrated into the keyboard,
by example by using a Fn key combo don't control the device
directly. So the driver cannot offer anything to the rfkill driver.
Such buttons should be mapped in userspace without the help of rfkill,
since the kernel cannot detect if that key belonged to a radio
control key or not.

> I think it would be better if there was an rfkill class listing all
> controlled devices (preferrably grouped by their type - WiFi, BT,
> IRDA, etc) and if every group would provide an attribute allowing to
> control state of the whole group (do we realistically need to kill
> just one interface? Wouldn't ifconfig be suitable for that?). The

There have been mixed feelings on the netdev list about what should
exactly happen when the button is pressed. The possible options are:

1 - rfkill will kill all interfaces
2 - rfkill will kill all interfaces of the same type (wifi, bt, irda)
3 - rfkill will kill the interface it belongs to
 
Personally I would favour the second option, but used the third after hearing
objections to the second method. So since there are also fans of
the third option I think there should be a decision made about what the
correct option is, so rfkill can follow that method.

> attribute should be a tri-state on/off/auto, "auto" meaning the driver
> itself manages radio state. This would avoid another tacky IMHO point
> that in your implementation mere opening of an input device takes over
> RF driver. Explicit control allow applications "snoop" RF state
> without disturbing it.

Currently userspace can always check the state of the button whenever
they like by checking the sysfs entry. 


> If there are concerns that drivers will have to re-implement most of
> the button handling you are still free to create a simple
> implementation of polled RF button (I don't think that interrupt
> driver RF buttons would share alot of code) so that users would only
> need to implement a polling function.

Isn't the current interface to the driver not clean enough?
It only asks for the (optional) poll method, and the enable/disable method.
I believe this should not add too much code into the drivers, especially when
all methods are optional.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 22:28   ` Ivo van Doorn
@ 2006-12-05  0:18     ` Randy Dunlap
  2006-12-05 21:20       ` Ivo van Doorn
  0 siblings, 1 reply; 42+ messages in thread
From: Randy Dunlap @ 2006-12-05  0:18 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Arjan van de Ven, Dmitry Torokhov, linux-kernel, netdev,
	John Linville, Jiri Benc, Lennart Poettering, Johannes Berg,
	Larry Finger

On Sun, 3 Dec 2006 23:28:11 +0100 Ivo van Doorn wrote:

> rfkill with the fixes as suggested by Arjan.
>  - instead of a semaphore a mutex is now being used.
>  - open_count changing is now locked by the mutex.
> 
> ---
> 
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index ba0e88c..6986d59 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -79,4 +79,19 @@ config HP_SDC_RTC
>  	  Say Y here if you want to support the built-in real time clock
>  	  of the HP SDC controller.
>  
> +config RFKILL
> +	tristate "RF button support"

	depends on SYSFS

> +	help
> +	  If you say yes here, the rfkill driver will be build

s/build/built/

> +	  which allowed network devices to register their hardware

s/allowed/allows/

> +	  RF button which controls the radio state. This driver
> +	  will then create an input device for it.
> +
> +	  When the input device is not used, the rfkill driver
> +	  will make sure that when the RF button is pressed the radio
> +	  is enabled or disabled accordingly. When the input device
> +	  has been opened by the user this radio control will be left
> +	  to the user, and rfkill will only send the RF button status
> +	  change to userspace.
> +
>  endif
> diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
> new file mode 100644
> index 0000000..4777d73
> --- /dev/null
> +++ b/drivers/input/misc/rfkill.c
> @@ -0,0 +1,887 @@

[snip]

> +/*
> + * Function called by the key driver to report the new status
> + * of the key.
> + */
> +void rfkill_report_event(struct rfkill *rfkill, int new_status)
> +{
> +	mutex_lock(&master->mutex);
> +
> +	if (rfkill_check_key(rfkill->key, new_status))
> +		schedule_work(&master->toggle_work);
> +
> +	mutex_unlock(&master->mutex);
> +}
> +EXPORT_SYMBOL_GPL(rfkill_report_event);

Please use kernel-doc notation for non-static functions.
See Documentation/kernel-doc-nano-HOWTO.txt for more info.

> +/*
> + * Function called by the key driver when the rfkill structure
> + * needs to be registered.
> + */

kernel-doc please.

> +int rfkill_register_key(struct rfkill *rfkill, int init_status)
> +{
> +	struct rfkill_type *type = &master->type[rfkill->key_type];
> +	struct rfkill_key *key;
> +	int status;
> +
> +	if (!rfkill)
> +		return -EINVAL;	
> +
> +	if (rfkill->key_type >= KEY_TYPE_MAX)
> +		return -EINVAL;
> +
> +	/*
> +	 * Increase module use count to prevent this
> +	 * module to be unloaded while there are still
> +	 * registered keys.
> +	 */
> +	if (!try_module_get(THIS_MODULE))
> +		return -EBUSY;
> +
> +	mutex_lock(&master->mutex);
> +
> +	/*
> +	 * Initialize key, and if required the type.
> +	 */
> +	status = rfkill_add_type_key(type);
> +	if (status)
> +		goto exit;
> +
> +	key = rfkill_key_init(rfkill, init_status);
> +	if (!key) {
> +		status = -ENOMEM;
> +		goto exit_type;
> +	}
> +
> +	/*
> +	 * Add key to our list.
> +	 */
> +	list_add(&key->entry, &master->key_list);
> +
> +	/*
> +	 * Check if we need polling, and if we do
> +	 * increase the poll required counter and check
> +	 * if we weren't polling yet.
> +	 */
> +	if (rfkill->poll && !master->poll_required++)
> +		schedule_delayed_work(&master->poll_work, RFKILL_POLL_DELAY);
> +
> +	mutex_unlock(&master->mutex);
> +
> +	return 0;
> +
> +exit_type:
> +	rfkill_del_type_key(type);
> +
> +exit:
> +	mutex_unlock(&master->mutex);
> +	module_put(THIS_MODULE);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL_GPL(rfkill_register_key);
> +
> +/*
> + * Function called by the key driver when the rfkill structure
> + * needs to be deregistered.
> + */

kernel-doc

> +int rfkill_deregister_key(struct rfkill *rfkill)
> +{
> +	struct rfkill_type *type;
> +
> +	if (!rfkill || !rfkill->key)
> +		return -EINVAL;
> +
> +	mutex_lock(&master->mutex);
> +
> +	/*
> +	 * Cancel delayed work if this is the last key
> +	 * that requires polling. It is not bad if
> +	 * if the workqueue is still running,
> +	 * the workqueue won't rearm itself since the
> +	 * poll_required variable has been set.
> +	 * and we have protected the list with a semaphore.
> +	 */
> +	if (rfkill->poll && !--master->poll_required)
> +		cancel_delayed_work(&master->poll_work);
> +
> +	/*
> +	 * Delete the rfkill structure to the list.
> +	 */
> +	list_del(&rfkill->key->entry);
> +
> +	/*
> +	 * Deinitialize key.
> +	 */
> +	type = rfkill->key->type;
> +	rfkill_key_deinit(rfkill->key);
> +	rfkill_del_type_key(type);
> +
> +	mutex_unlock(&master->mutex);
> +
> +	/*
> +	 * rfkill entry has been removed,
> +	 * decrease module use count.
> +	 */
> +	module_put(THIS_MODULE);
> +	
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(rfkill_deregister_key);

> diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
> new file mode 100644
> index 0000000..a455548
> --- /dev/null
> +++ b/include/linux/rfkill.h
> @@ -0,0 +1,140 @@
> +/*
> +	RF button support
> +	Laptops are quite often equiped with a RF key to enable or

                                equipped with an RF key

> +	disable the radio of the wireless device attached to that key.
> +	This wireless device usually is an integrated wireless network device,
> +	infrared or bluetooth device.
> +	Some of these devices will disable the radio automatically when the
> +	RF key has been pressed, while other devices need to be polled
> +	for the RF key status, and leave the action to be taken up to the
> +	driver for that particular device.
> +	But in all cases the only interface that will have its radio disabled
> +	will be the device that has the RF key attached to it. It could however
> +	be desired that userspace performs this disabling of the radios in case
> +	more things than just disabling a single radio is desired.
> +
> +	The rfkill driver will contain a list of all devices with a RF button,

                                                             with an RF button,

> +	and hardware drivers need to register their hardware to the rfkill
> +	interface. Rfkill will then take care of everything. If the RF key
> +	requires polling to obtain the status this will be handled by rfkill.
> +	If the RF key does not require polling but sends for example interrupts,
> +	the hardware driver can report the change of status to rfkill, without
> +	having to do any other action.
> +	Once the status of the key has changed and the hardware does not
> +	automatically enable or disable the radio rfkill provides the
> +	interface to do this correctly.
> +
> +	For each registered hardware button an input device will be created.
> +	If this input device has been opened by the user, rfkill will send a
> +	signal to userspace instead of the hardware about the new button
> +	status. This will allow userpace to perform the correct steps
> +	in order to bring down all interfaces.
> + */
> +
> +#ifndef RFKILL_H
> +#define RFKILL_H
> +
> +#include <linux/device.h>
> +
> +#define RFKILL_POLL_DELAY	( HZ / 10 )
> +
> +enum key_type {
> +	KEY_TYPE_WIFI = 0,
> +	KEY_TYPE_BlUETOOTH = 1,
> +	KEY_TYPE_IRDA = 2,
> +	KEY_TYPE_MAX = 3,
> +};
> +
> +/**
> + * struct rfkill - rfkill button control structure.
> + *

No "blank line" between the struct name and its parameters.

> + * @dev_name: Name of the interface. This will become the name
> + * 	of the input device which will be created for this button.
> + * @dev: Pointer to the device structure to which this button belongs to.
> + * @data: Pointer to the RF button drivers private data which will be
> + * 	passed along with the radio and polling handlers.
> + * @poll(void *data): Optional handler which will frequently be
> + * 	called to determine the current status of the RF button.
> + * @enable_radio(void *data): Optional handler to enable the radio
> + * 	once the RF button has been pressed and the hardware does enable
> + * 	the radio automaticly.
> + * @disable_radio(void *data): Optional handler to disable the radio
> + * 	once the RF button has been pressed and the hardware does disable
> + * 	the radio automaticly.
> + * @key_type: Radio type which the button controls, the value stored
> + * 	here should be a value from enum key_type.
> + * @key: Internal pointer that should not be touched by key driver.
> + *
> + * This structure can be used by a key driver to register the key
> + * to the rfkill driver in order to take control of the reporting
> + * to userspace or handling of radio status.
> + */
> +struct rfkill {
> +	const char *dev_name;
> +
> +	struct device *dev;
> +
> +	void *data;
> +	int (*poll)(void *data);
> +	void (*enable_radio)(void *data);
> +	void (*disable_radio)(void *data);
> +
> +	unsigned int key_type;
> +
> +	struct rfkill_key *key;
> +};
> +
> +/**
> + * rfkill_register_key - Deregister a previously registered rfkill structre.

                                                                     structure.

> + * @rfkill: rfkill structure to be deregistered
> + * @init_status: initial status of the key at the time this function is called
> + *
> + * This function should be called by the key driver when the rfkill structure
> + * needs to be registered. Immediately from registration the key driver
> + * should be able to receive calls through the poll, enable_radio and
> + * disable_radio handlers if those were registered.
> + */
> +int rfkill_register_key(struct rfkill *rfkill, int init_status);
> +
> +/**
> + * rfkill_deregister_key - Deregister a previously registered rfkill structre.

"structure"

> + * @rfkill: rfkill structure to be deregistered
> + *
> + * This function should be called by the key driver when the rfkill structure
> + * needs to be deregistered. This function may only be called if it was
> + * previously registered with rfkill_register_key.
> + */
> +int rfkill_deregister_key(struct rfkill *rfkill);
> +
> +/**
> + * rfkill_report_event - Report change in key status to rfkill handler.
> + * @rfkill: rfkill structure registered by key driver
> + * @new_status: new key status
> + *
> + * This function should be called by the key driver if it has not provided
> + * a poll handler with rfkill. As soon as the key driver has determined
> + * the status of the key has changed it should report the new status
> + * through this function.
> + */
> +void rfkill_report_event(struct rfkill *rfkill, int new_status);
> +
> +#endif /* RFKILL_H */

---
~Randy

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
                   ` (3 preceding siblings ...)
  2006-12-04 22:15 ` Dmitry Torokhov
@ 2006-12-05 10:32 ` Christoph Hellwig
  2006-12-05 21:21   ` Ivo van Doorn
  2007-01-31  3:40 ` Stephen Hemminger
  5 siblings, 1 reply; 42+ messages in thread
From: Christoph Hellwig @ 2006-12-05 10:32 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

> +/*
> + * Function called by the key driver when the rfkill structure
> + * needs to be registered.
> + */
> +int rfkill_register_key(struct rfkill *rfkill, int init_status)
> +{
> +	struct rfkill_type *type = &master->type[rfkill->key_type];
> +	struct rfkill_key *key;
> +	int status;
> +
> +	if (!rfkill)
> +		return -EINVAL;	
> +
> +	if (rfkill->key_type >= KEY_TYPE_MAX)
> +		return -EINVAL;
> +
> +	/*
> +	 * Increase module use count to prevent this
> +	 * module to be unloaded while there are still
> +	 * registered keys.
> +	 */
> +	if (!try_module_get(THIS_MODULE))
> +		return -EBUSY;

This is obviously broken.  Please add a "struct module *owner;"
field to struct rfkill instead.


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-05  0:18     ` Randy Dunlap
@ 2006-12-05 21:20       ` Ivo van Doorn
  0 siblings, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-05 21:20 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: Arjan van de Ven, Dmitry Torokhov, linux-kernel, netdev,
	John Linville, Jiri Benc, Lennart Poettering, Johannes Berg,
	Larry Finger

[snip]
 
> > +/*
> > + * Function called by the key driver to report the new status
> > + * of the key.
> > + */
> > +void rfkill_report_event(struct rfkill *rfkill, int new_status)
> > +{
> > +	mutex_lock(&master->mutex);
> > +
> > +	if (rfkill_check_key(rfkill->key, new_status))
> > +		schedule_work(&master->toggle_work);
> > +
> > +	mutex_unlock(&master->mutex);
> > +}
> > +EXPORT_SYMBOL_GPL(rfkill_report_event);
> 
> Please use kernel-doc notation for non-static functions.
> See Documentation/kernel-doc-nano-HOWTO.txt for more info.

All kernel-doc notations were placed in the rfkill.h header.
I'll move them to the rfkill.c file.


[snip]

> > + * @rfkill: rfkill structure to be deregistered
> > + * @init_status: initial status of the key at the time this function is called
> > + *
> > + * This function should be called by the key driver when the rfkill structure
> > + * needs to be registered. Immediately from registration the key driver
> > + * should be able to receive calls through the poll, enable_radio and
> > + * disable_radio handlers if those were registered.
> > + */
> > +int rfkill_register_key(struct rfkill *rfkill, int init_status);
> > +
> > +/**
> > + * rfkill_deregister_key - Deregister a previously registered rfkill structre.
> 
> "structure"

Thanks for the pointers. I'll fix them asap.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-05 10:32 ` Christoph Hellwig
@ 2006-12-05 21:21   ` Ivo van Doorn
  0 siblings, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-05 21:21 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Tuesday 05 December 2006 11:32, Christoph Hellwig wrote:
> > +/*
> > + * Function called by the key driver when the rfkill structure
> > + * needs to be registered.
> > + */
> > +int rfkill_register_key(struct rfkill *rfkill, int init_status)
> > +{
> > +	struct rfkill_type *type = &master->type[rfkill->key_type];
> > +	struct rfkill_key *key;
> > +	int status;
> > +
> > +	if (!rfkill)
> > +		return -EINVAL;	
> > +
> > +	if (rfkill->key_type >= KEY_TYPE_MAX)
> > +		return -EINVAL;
> > +
> > +	/*
> > +	 * Increase module use count to prevent this
> > +	 * module to be unloaded while there are still
> > +	 * registered keys.
> > +	 */
> > +	if (!try_module_get(THIS_MODULE))
> > +		return -EBUSY;
> 
> This is obviously broken.  Please add a "struct module *owner;"
> field to struct rfkill instead.

Thanks, will fix this asap.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-04 23:27   ` Ivo van Doorn
@ 2006-12-06 14:37     ` Dmitry Torokhov
  2006-12-06 15:18       ` Dan Williams
  2006-12-06 19:31       ` Ivo van Doorn
  0 siblings, 2 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-06 14:37 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On 12/4/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > I am still not sure that tight coupling of input device with rfkill
> > structure is such a good idea. Quite often the button is separated
> > from the device itself and radio control is done via BIOS SMM (see
> > wistron driver) or there is no special button at all and users might
> > want to assign one of their standard keyboard buttons to be an RF
> > switch.
>
> Making sure rfkill supports keys that are not handled by the driver
> is a bit hard. Just as drivers that can only check if the button is
> toggled and not what the current state is.
> The problem is that it is hard to make a clean split between the
> 2 different button controls. Not all drivers allow the radio to be
> enabled while the button status are indicating the radio should
> be off.

If they do not allow controlling the state of the radio
programmatically then it should not be part of rfkill I am afraid. It
is like the power switch - if you hold it for so long it kills the
power to the box and there is nothing you can do about it.

> The buttons that are already integrated into the keyboard,
> by example by using a Fn key combo don't control the device
> directly. So the driver cannot offer anything to the rfkill driver.
> Such buttons should be mapped in userspace without the help of rfkill,
> since the kernel cannot detect if that key belonged to a radio
> control key or not.
>

That is my point. Given the fact that there are keys that are not
directly connected with the radio switch userspace will have to handle
them (wait for events then turn off radios somehow). You are
advocating that userspace should also implement 2nd method for buttons
that belong to rfkill interface. I do not understand the need for 2nd
interface. If you separate radio switch from button code then
userspace only need to implement 1st interface and be done with it.
You will have set of cards that provide interface to enable/disable
their transmitters and set of buttons that signal userspace desired
state change. If both switch and button is implemented by the same
driver then the driver can implement automatic button handling.
Otherwise userspace help is necessary.

> > I think it would be better if there was an rfkill class listing all
> > controlled devices (preferrably grouped by their type - WiFi, BT,
> > IRDA, etc) and if every group would provide an attribute allowing to
> > control state of the whole group (do we realistically need to kill
> > just one interface? Wouldn't ifconfig be suitable for that?). The
>
> There have been mixed feelings on the netdev list about what should
> exactly happen when the button is pressed. The possible options are:
>
> 1 - rfkill will kill all interfaces
> 2 - rfkill will kill all interfaces of the same type (wifi, bt, irda)
> 3 - rfkill will kill the interface it belongs to
>
> Personally I would favour the second option, but used the third after hearing
> objections to the second method. So since there are also fans of
> the third option I think there should be a decision made about what the
> correct option is, so rfkill can follow that method.

Fans of the 3rd method, speak up ;)

>
> > attribute should be a tri-state on/off/auto, "auto" meaning the driver
> > itself manages radio state. This would avoid another tacky IMHO point
> > that in your implementation mere opening of an input device takes over
> > RF driver. Explicit control allow applications "snoop" RF state
> > without disturbing it.
>
> Currently userspace can always check the state of the button whenever
> they like by checking the sysfs entry.
>

Unless the key is not directly connected to the driver (so there is no
sysfs entry). Again you force 2 different interfaces.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 14:37     ` Dmitry Torokhov
@ 2006-12-06 15:18       ` Dan Williams
  2006-12-06 15:24         ` Dmitry Torokhov
  2006-12-06 19:31       ` Ivo van Doorn
  1 sibling, 1 reply; 42+ messages in thread
From: Dan Williams @ 2006-12-06 15:18 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Ivo van Doorn, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Wed, 2006-12-06 at 09:37 -0500, Dmitry Torokhov wrote:
> On 12/4/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > > I am still not sure that tight coupling of input device with rfkill
> > > structure is such a good idea. Quite often the button is separated
> > > from the device itself and radio control is done via BIOS SMM (see
> > > wistron driver) or there is no special button at all and users might
> > > want to assign one of their standard keyboard buttons to be an RF
> > > switch.
> >
> > Making sure rfkill supports keys that are not handled by the driver
> > is a bit hard. Just as drivers that can only check if the button is
> > toggled and not what the current state is.
> > The problem is that it is hard to make a clean split between the
> > 2 different button controls. Not all drivers allow the radio to be
> > enabled while the button status are indicating the radio should
> > be off.
> 
> If they do not allow controlling the state of the radio
> programmatically then it should not be part of rfkill I am afraid. It
> is like the power switch - if you hold it for so long it kills the
> power to the box and there is nothing you can do about it.
> 
> > The buttons that are already integrated into the keyboard,
> > by example by using a Fn key combo don't control the device
> > directly. So the driver cannot offer anything to the rfkill driver.
> > Such buttons should be mapped in userspace without the help of rfkill,
> > since the kernel cannot detect if that key belonged to a radio
> > control key or not.
> >
> 
> That is my point. Given the fact that there are keys that are not
> directly connected with the radio switch userspace will have to handle
> them (wait for events then turn off radios somehow). You are
> advocating that userspace should also implement 2nd method for buttons
> that belong to rfkill interface. I do not understand the need for 2nd
> interface. If you separate radio switch from button code then
> userspace only need to implement 1st interface and be done with it.
> You will have set of cards that provide interface to enable/disable
> their transmitters and set of buttons that signal userspace desired
> state change. If both switch and button is implemented by the same
> driver then the driver can implement automatic button handling.
> Otherwise userspace help is necessary.
> 
> > > I think it would be better if there was an rfkill class listing all
> > > controlled devices (preferrably grouped by their type - WiFi, BT,
> > > IRDA, etc) and if every group would provide an attribute allowing to
> > > control state of the whole group (do we realistically need to kill
> > > just one interface? Wouldn't ifconfig be suitable for that?). The
> >
> > There have been mixed feelings on the netdev list about what should
> > exactly happen when the button is pressed. The possible options are:
> >
> > 1 - rfkill will kill all interfaces
> > 2 - rfkill will kill all interfaces of the same type (wifi, bt, irda)
> > 3 - rfkill will kill the interface it belongs to
> >
> > Personally I would favour the second option, but used the third after hearing
> > objections to the second method. So since there are also fans of
> > the third option I think there should be a decision made about what the
> > correct option is, so rfkill can follow that method.
> 
> Fans of the 3rd method, speak up ;)

I think I brought up the 3rd method initially in this thread.  I'm not
necessarily advocating it, but I wanted to be sure people realized that
this was a case, so that a clear decision would be made to support it or
not to support it.

(2) makes the most sense to me.  I don't think we need to care about
edge-cases like "But I only wanted to rfkill _one_ of my bluetooth
dongles!!!", that's just insane.

But using (2) also begs the question, can we _always_ identify what
interface the rfkill belongs to?  In Bastien's laptop, the rfkill switch
_automatically_ disconnects the internal USB Bluetooth device from the
USB bus, and uses the normal ipw2200 rfkill mechanism, whatever that is.
In this case, you simply do not get an event that the bluetooth device
is disabled from a button somewhere; it's just gone, and you'd have to
do some magic to disable other bluetooth devices as well.

Dan

> >
> > > attribute should be a tri-state on/off/auto, "auto" meaning the driver
> > > itself manages radio state. This would avoid another tacky IMHO point
> > > that in your implementation mere opening of an input device takes over
> > > RF driver. Explicit control allow applications "snoop" RF state
> > > without disturbing it.
> >
> > Currently userspace can always check the state of the button whenever
> > they like by checking the sysfs entry.
> >
> 
> Unless the key is not directly connected to the driver (so there is no
> sysfs entry). Again you force 2 different interfaces.
> 


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 15:18       ` Dan Williams
@ 2006-12-06 15:24         ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-06 15:24 UTC (permalink / raw)
  To: Dan Williams
  Cc: Ivo van Doorn, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On 12/6/06, Dan Williams <dcbw@redhat.com> wrote:
> On Wed, 2006-12-06 at 09:37 -0500, Dmitry Torokhov wrote:
> >
> > Fans of the 3rd method, speak up ;)
>
> I think I brought up the 3rd method initially in this thread.  I'm not
> necessarily advocating it, but I wanted to be sure people realized that
> this was a case, so that a clear decision would be made to support it or
> not to support it.
>
> (2) makes the most sense to me.  I don't think we need to care about
> edge-cases like "But I only wanted to rfkill _one_ of my bluetooth
> dongles!!!", that's just insane.
>
> But using (2) also begs the question, can we _always_ identify what
> interface the rfkill belongs to?  In Bastien's laptop, the rfkill switch
> _automatically_ disconnects the internal USB Bluetooth device from the
> USB bus, and uses the normal ipw2200 rfkill mechanism, whatever that is.
> In this case, you simply do not get an event that the bluetooth device
> is disabled from a button somewhere; it's just gone, and you'd have to
> do some magic to disable other bluetooth devices as well.
>

Is this the same physical button? If so then for this particular box
we'd just have to send 2 events - KEY_WIFI and KEY_BLUETOOTH at the
same time.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 14:37     ` Dmitry Torokhov
  2006-12-06 15:18       ` Dan Williams
@ 2006-12-06 19:31       ` Ivo van Doorn
  2006-12-06 20:18         ` Dmitry Torokhov
  1 sibling, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-06 19:31 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Wednesday 06 December 2006 15:37, Dmitry Torokhov wrote:
> On 12/4/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > > I am still not sure that tight coupling of input device with rfkill
> > > structure is such a good idea. Quite often the button is separated
> > > from the device itself and radio control is done via BIOS SMM (see
> > > wistron driver) or there is no special button at all and users might
> > > want to assign one of their standard keyboard buttons to be an RF
> > > switch.
> >
> > Making sure rfkill supports keys that are not handled by the driver
> > is a bit hard. Just as drivers that can only check if the button is
> > toggled and not what the current state is.
> > The problem is that it is hard to make a clean split between the
> > 2 different button controls. Not all drivers allow the radio to be
> > enabled while the button status are indicating the radio should
> > be off.
> 
> If they do not allow controlling the state of the radio
> programmatically then it should not be part of rfkill I am afraid. It
> is like the power switch - if you hold it for so long it kills the
> power to the box and there is nothing you can do about it.

Ok, this will give rfkill more possibilities as I could in that case
also allow the user to toggle the radio to the state that is different
than indicated by the key.
Currently this was not possible since I had to keep in mind that there
were keys that would directly control the radio.

> > The buttons that are already integrated into the keyboard,
> > by example by using a Fn key combo don't control the device
> > directly. So the driver cannot offer anything to the rfkill driver.
> > Such buttons should be mapped in userspace without the help of rfkill,
> > since the kernel cannot detect if that key belonged to a radio
> > control key or not.
> >
> 
> That is my point. Given the fact that there are keys that are not
> directly connected with the radio switch userspace will have to handle
> them (wait for events then turn off radios somehow). You are
> advocating that userspace should also implement 2nd method for buttons
> that belong to rfkill interface. I do not understand the need for 2nd
> interface. If you separate radio switch from button code then
> userspace only need to implement 1st interface and be done with it.
> You will have set of cards that provide interface to enable/disable
> their transmitters and set of buttons that signal userspace desired
> state change. If both switch and button is implemented by the same
> driver then the driver can implement automatic button handling.
> Otherwise userspace help is necessary.

Well there are 3 possible hardware key approaches:

 1 - Hardware key that controls the hardware radio, and does not report anything to userspace
 2 - Hardware key that does not control the hardware radio and does not report anything to userspace
 3 - Hardware key that does not control the hardware radio and reports the key to userspace

So rfkill should not be used in the case of (1) and (3), but we still need something to support (2)
or should the keys not be handled by userspace and always by the driver?
This is making rfkill moving slowly away from the generic approach for all rfkill keys as the initial
intention was.

> > > attribute should be a tri-state on/off/auto, "auto" meaning the driver
> > > itself manages radio state. This would avoid another tacky IMHO point
> > > that in your implementation mere opening of an input device takes over
> > > RF driver. Explicit control allow applications "snoop" RF state
> > > without disturbing it.
> >
> > Currently userspace can always check the state of the button whenever
> > they like by checking the sysfs entry.
> >
> 
> Unless the key is not directly connected to the driver (so there is no
> sysfs entry). Again you force 2 different interfaces.

Ok, so input device opening should not block the rfkill signal and the rfkill handler
should still go through with its work unless a different config option indicates that
userspace wants to handle the event.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 19:31       ` Ivo van Doorn
@ 2006-12-06 20:18         ` Dmitry Torokhov
  2006-12-06 21:41           ` Ivo van Doorn
  2006-12-06 22:05           ` Jiri Benc
  0 siblings, 2 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-06 20:18 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On 12/6/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> On Wednesday 06 December 2006 15:37, Dmitry Torokhov wrote:
> > On 12/4/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > > > I am still not sure that tight coupling of input device with rfkill
> > > > structure is such a good idea. Quite often the button is separated
> > > > from the device itself and radio control is done via BIOS SMM (see
> > > > wistron driver) or there is no special button at all and users might
> > > > want to assign one of their standard keyboard buttons to be an RF
> > > > switch.
> > >
> > > Making sure rfkill supports keys that are not handled by the driver
> > > is a bit hard. Just as drivers that can only check if the button is
> > > toggled and not what the current state is.
> > > The problem is that it is hard to make a clean split between the
> > > 2 different button controls. Not all drivers allow the radio to be
> > > enabled while the button status are indicating the radio should
> > > be off.
> >
> > If they do not allow controlling the state of the radio
> > programmatically then it should not be part of rfkill I am afraid. It
> > is like the power switch - if you hold it for so long it kills the
> > power to the box and there is nothing you can do about it.
>
> Ok, this will give rfkill more possibilities as I could in that case
> also allow the user to toggle the radio to the state that is different
> than indicated by the key.
> Currently this was not possible since I had to keep in mind that there
> were keys that would directly control the radio.
>
> > > The buttons that are already integrated into the keyboard,
> > > by example by using a Fn key combo don't control the device
> > > directly. So the driver cannot offer anything to the rfkill driver.
> > > Such buttons should be mapped in userspace without the help of rfkill,
> > > since the kernel cannot detect if that key belonged to a radio
> > > control key or not.
> > >
> >
> > That is my point. Given the fact that there are keys that are not
> > directly connected with the radio switch userspace will have to handle
> > them (wait for events then turn off radios somehow). You are
> > advocating that userspace should also implement 2nd method for buttons
> > that belong to rfkill interface. I do not understand the need for 2nd
> > interface. If you separate radio switch from button code then
> > userspace only need to implement 1st interface and be done with it.
> > You will have set of cards that provide interface to enable/disable
> > their transmitters and set of buttons that signal userspace desired
> > state change. If both switch and button is implemented by the same
> > driver then the driver can implement automatic button handling.
> > Otherwise userspace help is necessary.
>
> Well there are 3 possible hardware key approaches:
>
>  1 - Hardware key that controls the hardware radio, and does not report anything to userspace

Can't do anything here so just ignore it.

>  2 - Hardware key that does not control the hardware radio and does not report anything to userspace

Kind of uninteresting button ;)

>  3 - Hardware key that does not control the hardware radio and reports the key to userspace
>
> So rfkill should not be used in the case of (1) and (3), but we still need something to support (2)
> or should the keys not be handled by userspace and always by the driver?
> This is making rfkill moving slowly away from the generic approach for all rfkill keys as the initial
> intention was.
>

I my "vision" rfkill would represent userspace namageable radio
switch. We have the followng possible configurations:

1. A device that does not allow controlling its transmitter from
userspace. The driver should not use/register with rfkill subsystem as
userspace can't do anyhting with it. If device has a button killing
the transmitter the driver can still signal userspace appropriate
event (KEY_WIFI, KEY_BLUETOOTH, etc) if it can detect that button was
presssed so userspace can monitor state of the transmitter and
probably shut down other transmitters to keep everything in sync.

2. A device that does allow controlling its transmitter. The driver
may (should) register with rfkill subsystem. Additionally, if there is
a button, the driver should register it with input subsystem. Driver
should manage transmitter state in response to button presses unless
userspace takes over the process.

3. A device without transmitter but with a button - just register with
input core. Userspace will have to manage state of other devices with
transmitters in response to button presses.

Does this make sense?

> > > > attribute should be a tri-state on/off/auto, "auto" meaning the driver
> > > > itself manages radio state. This would avoid another tacky IMHO point
> > > > that in your implementation mere opening of an input device takes over
> > > > RF driver. Explicit control allow applications "snoop" RF state
> > > > without disturbing it.
> > >
> > > Currently userspace can always check the state of the button whenever
> > > they like by checking the sysfs entry.
> > >
> >
> > Unless the key is not directly connected to the driver (so there is no
> > sysfs entry). Again you force 2 different interfaces.
>
> Ok, so input device opening should not block the rfkill signal and the rfkill handler
> should still go through with its work unless a different config option indicates that
> userspace wants to handle the event.
>

I don't think a config option is a good idea unless by config option
you mean a sysfs attribute.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 20:18         ` Dmitry Torokhov
@ 2006-12-06 21:41           ` Ivo van Doorn
  2006-12-06 22:04             ` Dmitry Torokhov
  2006-12-07 13:22             ` Dan Williams
  2006-12-06 22:05           ` Jiri Benc
  1 sibling, 2 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-06 21:41 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> > > That is my point. Given the fact that there are keys that are not
> > > directly connected with the radio switch userspace will have to handle
> > > them (wait for events then turn off radios somehow). You are
> > > advocating that userspace should also implement 2nd method for buttons
> > > that belong to rfkill interface. I do not understand the need for 2nd
> > > interface. If you separate radio switch from button code then
> > > userspace only need to implement 1st interface and be done with it.
> > > You will have set of cards that provide interface to enable/disable
> > > their transmitters and set of buttons that signal userspace desired
> > > state change. If both switch and button is implemented by the same
> > > driver then the driver can implement automatic button handling.
> > > Otherwise userspace help is necessary.
> >
> > Well there are 3 possible hardware key approaches:
> >
> >  1 - Hardware key that controls the hardware radio, and does not report anything to userspace
> 
> Can't do anything here so just ignore it.

Ok.

> >  2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> 
> Kind of uninteresting button ;)

And this is the button that rfkill was originally designed for.
Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
userspace (unless the ACPI event is read) and does not directly control the radio itself.

> >  3 - Hardware key that does not control the hardware radio and reports the key to userspace
> >
> > So rfkill should not be used in the case of (1) and (3), but we still need something to support (2)
> > or should the keys not be handled by userspace and always by the driver?
> > This is making rfkill moving slowly away from the generic approach for all rfkill keys as the initial
> > intention was.
> >
> 
> I my "vision" rfkill would represent userspace namageable radio
> switch. We have the followng possible configurations:
> 
> 1. A device that does not allow controlling its transmitter from
> userspace. The driver should not use/register with rfkill subsystem as
> userspace can't do anyhting with it. If device has a button killing
> the transmitter the driver can still signal userspace appropriate
> event (KEY_WIFI, KEY_BLUETOOTH, etc) if it can detect that button was
> presssed so userspace can monitor state of the transmitter and
> probably shut down other transmitters to keep everything in sync.

And this event should be reported by a generic approach right? So it should
be similar as with your point 2 below. But this would mean that the driver
should create the input device. Or can a driver send the KEY_WIFI event
over a main layer without the need of a personal input device?
I am not that familiar with the input device layer in the kernel, and this is
my first attempt on creating something for it, so I might have missed something. ;)

Because it could still register with rfkill, only not give the callback functions
for changing the radio or polling. Then the driver can use the send_event function
to inform rfkill of the event and process it further. The sysfs attributes could
even be reduced to only add the change_status attribute when the radio_enable
and radio_disable callback functions are implemented.

> 2. A device that does allow controlling its transmitter. The driver
> may (should) register with rfkill subsystem. Additionally, if there is
> a button, the driver should register it with input subsystem. Driver
> should manage transmitter state in response to button presses unless
> userspace takes over the process.

This is indeed the main goal of rfkill. :)

> 3. A device without transmitter but with a button - just register with
> input core. Userspace will have to manage state of other devices with
> transmitters in response to button presses.

This is clear too. Rfkill is only intended for drivers that control a device with
a transmitter (WiFi, Bluetooth, IRDA) that have a button that is intended to
do something with the radio/transmitter.

> Does this make sense?

Yes, this was what I intended to do with rfkill, so at that point we have
the same goal.

> > > > > attribute should be a tri-state on/off/auto, "auto" meaning the driver
> > > > > itself manages radio state. This would avoid another tacky IMHO point
> > > > > that in your implementation mere opening of an input device takes over
> > > > > RF driver. Explicit control allow applications "snoop" RF state
> > > > > without disturbing it.
> > > >
> > > > Currently userspace can always check the state of the button whenever
> > > > they like by checking the sysfs entry.
> > > >
> > >
> > > Unless the key is not directly connected to the driver (so there is no
> > > sysfs entry). Again you force 2 different interfaces.
> >
> > Ok, so input device opening should not block the rfkill signal and the rfkill handler
> > should still go through with its work unless a different config option indicates that
> > userspace wants to handle the event.
> >
> 
> I don't think a config option is a good idea unless by config option
> you mean a sysfs attribute.

I indeed meant a sysfs attribute. I should have been more clear on this. :)

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 21:41           ` Ivo van Doorn
@ 2006-12-06 22:04             ` Dmitry Torokhov
  2006-12-07 21:53               ` Ivo van Doorn
  2006-12-07 13:22             ` Dan Williams
  1 sibling, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-06 22:04 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On 12/6/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
>
> > >  2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> >
> > Kind of uninteresting button ;)
>
> And this is the button that rfkill was originally designed for.
> Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
> userspace (unless the ACPI event is read) and does not directly control the radio itself.
>

So what does such a button do? I am confused here...

...
>
> And this event should be reported by a generic approach right? So it should
> be similar as with your point 2 below. But this would mean that the driver
> should create the input device. Or can a driver send the KEY_WIFI event
> over a main layer without the need of a personal input device?
> I am not that familiar with the input device layer in the kernel, and this is
> my first attempt on creating something for it, so I might have missed something. ;)

Yes, I think the driver should just create an input device. You may
provide a generic implementation for a polled button and have driver
instantiate it but I do not think that a single RFkill button device
is needed - you won't have too many of them in a single system anyway
(I think you will normally have 1, 2 at the most).

...
> > 3. A device without transmitter but with a button - just register with
> > input core. Userspace will have to manage state of other devices with
> > transmitters in response to button presses.
>
> This is clear too. Rfkill is only intended for drivers that control a device with
> a transmitter (WiFi, Bluetooth, IRDA) that have a button that is intended to
> do something with the radio/transmitter.
>
> > Does this make sense?
>
> Yes, this was what I intended to do with rfkill, so at that point we have
> the same goal.
>

I think it is almost the same. I also want support RF devices that can
control radio state but lack a button. This is covered by mixing 2)
and 3) in kernel and for userspace looks exactly like 2) with a
button.

...
> >
> > I don't think a config option is a good idea unless by config option
> > you mean a sysfs attribute.
>
> I indeed meant a sysfs attribute. I should have been more clear on this. :)
>

OK :)

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 20:18         ` Dmitry Torokhov
  2006-12-06 21:41           ` Ivo van Doorn
@ 2006-12-06 22:05           ` Jiri Benc
  2006-12-06 22:10             ` Dmitry Torokhov
  1 sibling, 1 reply; 42+ messages in thread
From: Jiri Benc @ 2006-12-06 22:05 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Ivo van Doorn, linux-kernel, netdev, John Linville,
	Lennart Poettering, Johannes Berg, Larry Finger

On Wed, 6 Dec 2006 15:18:12 -0500, Dmitry Torokhov wrote:
> On 12/6/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > Ok, so input device opening should not block the rfkill signal and the rfkill handler
> > should still go through with its work unless a different config option indicates that
> > userspace wants to handle the event.
> 
> I don't think a config option is a good idea unless by config option
> you mean a sysfs attribute.

What about using EVIOCGRAB ioctl for this?

 Jiri

-- 
Jiri Benc
SUSE Labs

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 22:05           ` Jiri Benc
@ 2006-12-06 22:10             ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-06 22:10 UTC (permalink / raw)
  To: Jiri Benc
  Cc: Ivo van Doorn, linux-kernel, netdev, John Linville,
	Lennart Poettering, Johannes Berg, Larry Finger

On 12/6/06, Jiri Benc <jbenc@suse.cz> wrote:
> On Wed, 6 Dec 2006 15:18:12 -0500, Dmitry Torokhov wrote:
> > On 12/6/06, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > > Ok, so input device opening should not block the rfkill signal and the rfkill handler
> > > should still go through with its work unless a different config option indicates that
> > > userspace wants to handle the event.
> >
> > I don't think a config option is a good idea unless by config option
> > you mean a sysfs attribute.
>
> What about using EVIOCGRAB ioctl for this?
>

Will not work when the button is on your USB keyboard ;)

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 21:41           ` Ivo van Doorn
  2006-12-06 22:04             ` Dmitry Torokhov
@ 2006-12-07 13:22             ` Dan Williams
  2006-12-07 21:58               ` Ivo van Doorn
  1 sibling, 1 reply; 42+ messages in thread
From: Dan Williams @ 2006-12-07 13:22 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

On Wed, 2006-12-06 at 22:41 +0100, Ivo van Doorn wrote:
> Hi,
> 
> > > > That is my point. Given the fact that there are keys that are not
> > > > directly connected with the radio switch userspace will have to handle
> > > > them (wait for events then turn off radios somehow). You are
> > > > advocating that userspace should also implement 2nd method for buttons
> > > > that belong to rfkill interface. I do not understand the need for 2nd
> > > > interface. If you separate radio switch from button code then
> > > > userspace only need to implement 1st interface and be done with it.
> > > > You will have set of cards that provide interface to enable/disable
> > > > their transmitters and set of buttons that signal userspace desired
> > > > state change. If both switch and button is implemented by the same
> > > > driver then the driver can implement automatic button handling.
> > > > Otherwise userspace help is necessary.
> > >
> > > Well there are 3 possible hardware key approaches:
> > >
> > >  1 - Hardware key that controls the hardware radio, and does not report anything to userspace
> > 
> > Can't do anything here so just ignore it.
> 
> Ok.
> 
> > >  2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> > 
> > Kind of uninteresting button ;)
> 
> And this is the button that rfkill was originally designed for.
> Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
> userspace (unless the ACPI event is read) and does not directly control the radio itself.

My take: if there is a button on your keyboard or laptop labeled "Kill
my radio now", it _NEEDS_ to be somehow communicated to userspace what
happened when the user just pressed it a second ago.  Personally, I
don't particularly care how that happens, and I don't particularly care
what the driver does.  But if the driver, or the hardware, decides that
the button press means turning off the transmitter on whatever device
that button is for, a tool like NetworkManager needs to know this
somehow.  Ideally, this would be a HAL event, and HAL would get it from
somewhere.

The current situation with NM is unacceptable, and I can't do anything
about it because there is no standard interface for determining whether
the wireless card was disabled/enabled via rfkill.  I simply refuse to
code solutions to every vendor's rfkill mechanism (for ipw, reading
iwpriv or sysfs, for example).  I don't care how HAL gets the event, but
when HAL gets the event, it needs to broadcast it and NM needs to tear
down the connection and release the device.

That means (a) an event gets sent to userspace in some way that HAL can
read it, and (b) the event is clearly associated with specific piece[s]
of hardware on your system.  If HAL can't easily figure out what device
the event is for, then the event is also useless to both HAL and
NetworkManager and whatever else might use it.

Again, I don't care how that happens, but I like the fact that there's
renewed interest in getting this fixed.

Dan

> > >  3 - Hardware key that does not control the hardware radio and reports the key to userspace
> > >
> > > So rfkill should not be used in the case of (1) and (3), but we still need something to support (2)
> > > or should the keys not be handled by userspace and always by the driver?
> > > This is making rfkill moving slowly away from the generic approach for all rfkill keys as the initial
> > > intention was.
> > >
> > 
> > I my "vision" rfkill would represent userspace namageable radio
> > switch. We have the followng possible configurations:
> > 
> > 1. A device that does not allow controlling its transmitter from
> > userspace. The driver should not use/register with rfkill subsystem as
> > userspace can't do anyhting with it. If device has a button killing
> > the transmitter the driver can still signal userspace appropriate
> > event (KEY_WIFI, KEY_BLUETOOTH, etc) if it can detect that button was
> > presssed so userspace can monitor state of the transmitter and
> > probably shut down other transmitters to keep everything in sync.
> 
> And this event should be reported by a generic approach right? So it should
> be similar as with your point 2 below. But this would mean that the driver
> should create the input device. Or can a driver send the KEY_WIFI event
> over a main layer without the need of a personal input device?
> I am not that familiar with the input device layer in the kernel, and this is
> my first attempt on creating something for it, so I might have missed something. ;)
> 
> Because it could still register with rfkill, only not give the callback functions
> for changing the radio or polling. Then the driver can use the send_event function
> to inform rfkill of the event and process it further. The sysfs attributes could
> even be reduced to only add the change_status attribute when the radio_enable
> and radio_disable callback functions are implemented.
> 
> > 2. A device that does allow controlling its transmitter. The driver
> > may (should) register with rfkill subsystem. Additionally, if there is
> > a button, the driver should register it with input subsystem. Driver
> > should manage transmitter state in response to button presses unless
> > userspace takes over the process.
> 
> This is indeed the main goal of rfkill. :)
> 
> > 3. A device without transmitter but with a button - just register with
> > input core. Userspace will have to manage state of other devices with
> > transmitters in response to button presses.
> 
> This is clear too. Rfkill is only intended for drivers that control a device with
> a transmitter (WiFi, Bluetooth, IRDA) that have a button that is intended to
> do something with the radio/transmitter.
> 
> > Does this make sense?
> 
> Yes, this was what I intended to do with rfkill, so at that point we have
> the same goal.
> 
> > > > > > attribute should be a tri-state on/off/auto, "auto" meaning the driver
> > > > > > itself manages radio state. This would avoid another tacky IMHO point
> > > > > > that in your implementation mere opening of an input device takes over
> > > > > > RF driver. Explicit control allow applications "snoop" RF state
> > > > > > without disturbing it.
> > > > >
> > > > > Currently userspace can always check the state of the button whenever
> > > > > they like by checking the sysfs entry.
> > > > >
> > > >
> > > > Unless the key is not directly connected to the driver (so there is no
> > > > sysfs entry). Again you force 2 different interfaces.
> > >
> > > Ok, so input device opening should not block the rfkill signal and the rfkill handler
> > > should still go through with its work unless a different config option indicates that
> > > userspace wants to handle the event.
> > >
> > 
> > I don't think a config option is a good idea unless by config option
> > you mean a sysfs attribute.
> 
> I indeed meant a sysfs attribute. I should have been more clear on this. :)
> 
> Ivo
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-06 22:04             ` Dmitry Torokhov
@ 2006-12-07 21:53               ` Ivo van Doorn
  2006-12-12  5:12                 ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-07 21:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> > > >  2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> > >
> > > Kind of uninteresting button ;)
> >
> > And this is the button that rfkill was originally designed for.
> > Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
> > userspace (unless the ACPI event is read) and does not directly control the radio itself.
> >
> 
> So what does such a button do? I am confused here...

Without a handler like rfkill, it does nothing besides toggling a bit in a register.
The Ralink chipsets have a couple of registers that represent the state of that key.
Besides that, there are no notifications to the userspace nor does it directly control the
radio.
That is where rfkill came in with the toggle handler that will listen to the register
to check if the key has been pressed and properly process the key event.

> > And this event should be reported by a generic approach right? So it should
> > be similar as with your point 2 below. But this would mean that the driver
> > should create the input device. Or can a driver send the KEY_WIFI event
> > over a main layer without the need of a personal input device?
> > I am not that familiar with the input device layer in the kernel, and this is
> > my first attempt on creating something for it, so I might have missed something. ;)
> 
> Yes, I think the driver should just create an input device. You may
> provide a generic implementation for a polled button and have driver
> instantiate it but I do not think that a single RFkill button device
> is needed - you won't have too many of them in a single system anyway
> (I think you will normally have 1, 2 at the most).

Ok, this is something that can be added as notice in the rfkill description
to make sure drivers which supports keys that handle the radio event themselves
should handle everything themselves and just use the KEY_RFKILL event for the
input device.

> > > 3. A device without transmitter but with a button - just register with
> > > input core. Userspace will have to manage state of other devices with
> > > transmitters in response to button presses.
> >
> > This is clear too. Rfkill is only intended for drivers that control a device with
> > a transmitter (WiFi, Bluetooth, IRDA) that have a button that is intended to
> > do something with the radio/transmitter.
> >
> > > Does this make sense?
> >
> > Yes, this was what I intended to do with rfkill, so at that point we have
> > the same goal.
> >
> 
> I think it is almost the same. I also want support RF devices that can
> control radio state but lack a button. This is covered by mixing 2)
> and 3) in kernel and for userspace looks exactly like 2) with a
> button.

Ok, this means making the change in rfkill to instead support 1) and 2)
and change it into 2) and 3) that would be possible and would make it possible
again to change the radio state to something different then the key indicates.
That was previously removed because of support for 1) devices.

> ...
> > >
> > > I don't think a config option is a good idea unless by config option
> > > you mean a sysfs attribute.
> >
> > I indeed meant a sysfs attribute. I should have been more clear on this. :)
> >
> 
> OK :)
> 

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-07 13:22             ` Dan Williams
@ 2006-12-07 21:58               ` Ivo van Doorn
  0 siblings, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-07 21:58 UTC (permalink / raw)
  To: Dan Williams
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> > > >  2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> > > 
> > > Kind of uninteresting button ;)
> > 
> > And this is the button that rfkill was originally designed for.
> > Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
> > userspace (unless the ACPI event is read) and does not directly control the radio itself.
> 
> My take: if there is a button on your keyboard or laptop labeled "Kill
> my radio now", it _NEEDS_ to be somehow communicated to userspace what
> happened when the user just pressed it a second ago.  Personally, I
> don't particularly care how that happens, and I don't particularly care
> what the driver does.  But if the driver, or the hardware, decides that
> the button press means turning off the transmitter on whatever device
> that button is for, a tool like NetworkManager needs to know this
> somehow.  Ideally, this would be a HAL event, and HAL would get it from
> somewhere.
>
> The current situation with NM is unacceptable, and I can't do anything
> about it because there is no standard interface for determining whether
> the wireless card was disabled/enabled via rfkill.  I simply refuse to
> code solutions to every vendor's rfkill mechanism (for ipw, reading
> iwpriv or sysfs, for example).  I don't care how HAL gets the event, but
> when HAL gets the event, it needs to broadcast it and NM needs to tear
> down the connection and release the device.
> 
> That means (a) an event gets sent to userspace in some way that HAL can
> read it, and (b) the event is clearly associated with specific piece[s]
> of hardware on your system.  If HAL can't easily figure out what device
> the event is for, then the event is also useless to both HAL and
> NetworkManager and whatever else might use it.

This would be possible with rfkill and the ideas from Dmitry.
The vendors that have a button that directly toggle the radio, should
create an input device themselves and just send the KEY_RFKILL event when toggled.

All other types should use rfkill for the toggling handling, that way HAL only needs to
listen to KEY_RFKILL coming from the input devices that are associated to the keys.

> Again, I don't care how that happens, but I like the fact that there's
> renewed interest in getting this fixed.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-07 21:53               ` Ivo van Doorn
@ 2006-12-12  5:12                 ` Dmitry Torokhov
  2006-12-12  7:47                   ` Ivo Van Doorn
  2006-12-17 17:43                   ` Ivo van Doorn
  0 siblings, 2 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2006-12-12  5:12 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi Ivo,

On Thursday 07 December 2006 16:53, Ivo van Doorn wrote:
> Hi,
> 
> > > > >  2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> > > >
> > > > Kind of uninteresting button ;)
> > >
> > > And this is the button that rfkill was originally designed for.
> > > Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
> > > userspace (unless the ACPI event is read) and does not directly control the radio itself.
> > >
> > 
> > So what does such a button do? I am confused here...
> 
> Without a handler like rfkill, it does nothing besides toggling a bit in a register.
> The Ralink chipsets have a couple of registers that represent the state of that key.
> Besides that, there are no notifications to the userspace nor does it directly control the
> radio.
> That is where rfkill came in with the toggle handler that will listen to the register
> to check if the key has been pressed and properly process the key event.

In this case the driver can make the button state available to userspace so
thsi is really type 2) driver as far as I can see. The fact that the button
is not reported to userspace yet should not get into our way of classifying
it.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-12  5:12                 ` Dmitry Torokhov
@ 2006-12-12  7:47                   ` Ivo Van Doorn
  2006-12-17 17:43                   ` Ivo van Doorn
  1 sibling, 0 replies; 42+ messages in thread
From: Ivo Van Doorn @ 2006-12-12  7:47 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> > > > > > 2 - Hardware key that does not control the hardware radio and does not report anything to userspace
> > > > >
> > > > > Kind of uninteresting button ;)
> > > >
> > > > And this is the button that rfkill was originally designed for.
> > > > Laptops with integrated WiFi cards from Ralink have a hardware button that don't send anything to
> > > > userspace (unless the ACPI event is read) and does not directly control the radio itself.
> > > >
> > >
> > > So what does such a button do? I am confused here...
> >
> > Without a handler like rfkill, it does nothing besides toggling a bit in a register.
> > The Ralink chipsets have a couple of registers that represent the state of that key.
> > Besides that, there are no notifications to the userspace nor does it directly control the
> > radio.
> > That is where rfkill came in with the toggle handler that will listen to the register
> > to check if the key has been pressed and properly process the key event.
>
> In this case the driver can make the button state available to userspace so
> thsi is really type 2) driver as far as I can see. The fact that the button
> is not reported to userspace yet should not get into our way of classifying
> it.

I was indeed considering it as a type 2) device.
I am currently working on revising the rfkill driver to work as you suggested,
so I hope to have a new patch ready for you soon.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-12  5:12                 ` Dmitry Torokhov
  2006-12-12  7:47                   ` Ivo Van Doorn
@ 2006-12-17 17:43                   ` Ivo van Doorn
  2007-01-30 16:33                     ` Ivo van Doorn
  1 sibling, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2006-12-17 17:43 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger,
	Christoph Hellwig, Randy Dunlap

This is the latest version of rfkill.
The changes since the version that was originally send are:

Spelling fixes (Thanks to Randy Dunlap)
THIS_MODULE is now a field in the rkfill_master (Suggested by Christoph Hellwig)

The open_count has been completely removed, decision making on which action should
be taken is now handled by the user_claim field, which can be set through sysfs.
The possible choice include
 1 - let rfkill handle everything without bothering the user
 2 - let rfkill handle everything but send a notification to the user
 3 - let rfkill send a notification only

The toggling of the keys is now type based, this means that if 1 key is being toggled
all keys of the same type will be toggled.

As optimization and clearly seperate the keys per type, the rfkill_type structure
now holds the list of the keys that belong to him. This has greatly reduced
the size of the rfkill_master structure.

sysfs will hold the following entries:

- The main folder: "rfkill"
- The main folder contains the type folders "wlan", "bluetooth" and "irda".
- Each type folder contains the files
	- "claim" where the user claim can be read/written
	- "status" The radio status of this type
	- The folders for each key belonging to this type
- Each key folder contains the files
	- "status" The status of this key
	- "idev" The symlink to the input device entry in sysfs
	- "dev" The symlink to the drivers device entry in sysfs

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>

---

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ba0e88c..e58c0bf 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -79,4 +79,20 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config RFKILL
+	tristate "RF button support"
+	depends on SYSFS
+	help
+	  If you say yes here, the rfkill driver will be built
+	  which allows network devices to register their hardware
+	  RF button which controls the radio state. This driver
+	  will then create an input device for it.
+
+	  When the input device is not used, the rfkill driver
+	  will make sure that when the RF button is pressed the radio
+	  is enabled or disabled accordingly. When the input device
+	  has been opened by the user this radio control will be left
+	  to the user, and rfkill will only send the RF button status
+	  change to userspace.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415c491..e788a1b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_RFKILL)			+= rfkill.o
diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
new file mode 100644
index 0000000..065ff56
--- /dev/null
+++ b/drivers/input/misc/rfkill.c
@@ -0,0 +1,986 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF key support");
+MODULE_LICENSE("GPL");
+
+/*
+ * rfkill key structure.
+ */
+struct rfkill_key {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Pointer to rfkill structure
+	 * that was filled in by key driver.
+	 */
+	struct rfkill *rfkill;
+
+	/*
+	 * Pointer to type structure
+	 * that this key belongs to.
+	 */
+	struct rfkill_type *type;
+
+	/*
+	 * Current status of the key which controls the radio,
+	 * this value will change after the key state has changed
+	 * after polling, or the key driver has send the new state
+	 * manually.
+	 */
+	int key_status;
+
+	/*
+	 * Input device for this key.
+	 */
+	struct input_dev *input;
+
+	/*
+	 * List head structure to be used
+	 * to add this structure to the list.
+	 */
+	struct list_head entry;
+};
+
+/*
+ * rfkill type structure.
+ */
+struct rfkill_type {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * All access to the type structure and its
+	 * children (the keys) are protected by this mutex.
+	 */
+	struct mutex mutex;
+
+	/*
+	 * Name of this radio type.
+	 */
+	char *name;
+
+	/*
+	 * Key type identification. Value must be any
+	 * in the key_type enum.
+	 */
+	unsigned int key_type;
+
+	/*
+	 * List of rfkill_key structures.
+	 */
+	struct list_head key_list;
+
+	/*
+	 * Number of registered keys of this type.
+	 */
+	unsigned int key_count;
+
+	/*
+	 * Once key status change has been detected, the toggled
+	 * field should be set to indicate a notification to
+	 * user or driver should be performed. This is stored
+	 * globally since all keys within this type need to be
+	 * toggled.
+	 */
+	unsigned int toggled;
+
+	/*
+	 * The claim the user has over the toggling of the radio,
+	 * this can be any value of the user_claim enumeration.
+	 */
+	unsigned int user_claim;
+
+	/*
+	 * Current state of the radios in this type.
+	 */
+	unsigned int radio_status;
+
+	/*
+	 * Number of keys that require polling
+	 */
+	unsigned int poll_required;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	struct work_struct toggle_work;
+	struct work_struct poll_work;
+};
+
+/*
+ * rfkill master structure.
+ */
+struct rfkill_master {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class *class;
+
+	/*
+	 * Reference to this modules structure.
+	 */
+	struct module *owner;
+
+	/*
+	 * List of available key types.
+	 */
+	struct rfkill_type* type[KEY_TYPE_MAX];
+};
+
+/*
+ * We only need 1 single master interface,
+ * make this a global variable since we always need it.
+ */
+static struct rfkill_master *master;
+
+/*
+ * Send toggle event to key driver.
+ */
+static void rfkill_toggle_radio_driver(struct rfkill_key *key)
+{
+	struct rfkill *rfkill = key->rfkill;
+	struct rfkill_type *type = key->type;
+
+	/*
+	 * Check what the current radio status is, and perform
+	 * the correct action to toggle the radio. This will make
+	 * sure the correct event happens when either the key driver
+	 * of the user has requested to toggle this radio.
+	 */
+	if (!type->radio_status)
+		rfkill->enable_radio(rfkill->data);
+	else if (type->radio_status)
+		rfkill->disable_radio(rfkill->data);
+}
+
+/*
+ * Send toggle event to userspace through the input device.
+ */
+static void rfkill_toggle_radio_input(struct rfkill_key *key)
+{
+	input_report_key(key->input, KEY_RFKILL, !key->type->radio_status);
+	input_sync(key->input);
+}
+
+/*
+ * Loop through the list of registered keys and toggle each
+ * key if this type has been toggled (by either user or driver).
+ */
+static void rfkill_toggle_radio(void *data)
+{
+	struct rfkill_type *type = data;
+	struct rfkill_key *key;
+
+	mutex_lock(&type->mutex);
+
+	/*
+	 * If this type hasn't been toggled by any of the keys
+	 * or the user we can skip the toggle run.
+	 */
+	if (!type->toggled) {
+		mutex_unlock(&type->mutex);
+		return;
+	}
+
+	list_for_each_entry(key, &type->key_list, entry) {
+		/*
+		 * Check what kind of claim the user has requested
+		 * on this key. This determined if we should send
+		 * a user and/or a driver notification.
+		 */
+		if (type->user_claim != USER_CLAIM_IGNORE)
+			rfkill_toggle_radio_input(key);
+		else if (type->user_claim != USER_CLAIM_SINGLE)
+			rfkill_toggle_radio_driver(key);
+	}
+
+	/*
+	 * Reset the radio_status and toggled field,
+	 * to respresent the correct status to the user,
+	 * and allow the status to be toggled again.
+	 */
+	type->radio_status = !type->radio_status;
+	type->toggled = 0;
+
+	mutex_unlock(&type->mutex);
+}
+
+/*
+ * Check the new status of the key, and determine if the key has been toggled.
+ * This function can be called upon request by the device driver or userspace.
+ */
+static int rfkill_check_status(struct rfkill_key *key, int status)
+{
+	struct rfkill_type *type = key->type;
+
+	/*
+	 * A key should be toggled if the current radio status does not
+	 * match the requested status. This check is required instead of
+	 * comparing it to the current key status since this function can
+	 * be called on request of the user as well. The user is able to
+	 * request a radio toggle to make sure the radio status will match
+	 * the key status if the new key status has been reported to
+	 * userspace only.
+	 * As a safety measure, we won't toggle a key twice.
+	 */
+	if (type->radio_status != !!status && !type->toggled) {
+		type->toggled = 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Wrapper around the rfkill_check_status function, this may only
+ * be called from the device driver status events. Besides calling
+ * rfkill_check_status it will also update the key_status field.
+ */
+static int rfkill_check_key(struct rfkill_key *key, int status)
+{
+	/*
+	 * Store the new key status, and only if the 2
+	 * are different the new status status should be
+	 * passed on to rfkill_check_status to determine
+	 * if any action should be performed.
+	 */
+	if (key->key_status == !!status)
+		return 0;
+
+	key->key_status = !!status;
+	return rfkill_check_status(key->rfkill->key, status);
+}
+
+/*
+ * Handler that is run frequently to determine the current status
+ * of all registered keys that require polling.
+ */
+static void rfkill_list_poll(void *data)
+{
+	struct rfkill_type *type = data;
+	struct rfkill_key *key;
+	struct rfkill *rfkill;
+	int status = 0;
+
+	mutex_lock(&type->mutex);
+
+	list_for_each_entry(key, &type->key_list, entry) {
+		rfkill = key->rfkill;
+
+		/*
+		 * If the poll handler has not been given
+		 * the key driver should report events,
+		 * so we can ignore this key now.
+		 */
+		if (!rfkill->poll)
+			continue;
+
+		/*
+		 * Poll radio state and check if a radio toggle
+		 * has been requested by the key.
+		 */
+		if (rfkill_check_key(key, rfkill->poll(rfkill->data)))
+			status = 1;
+	}
+
+	mutex_unlock(&type->mutex);
+
+	/*
+	 * A radio toggle has been requested, schedule the toggle work thread.
+	 */
+	if (status)
+		schedule_work(&type->toggle_work);
+
+	/*
+	 * Check if we need to rearm ourselves.
+	 */
+	if (type->poll_required)
+		schedule_delayed_work(&type->poll_work, RFKILL_POLL_DELAY);
+}
+
+/**
+ * rfkill_report_event - Report change in key status to rfkill handler.
+ * @rfkill: rfkill structure registered by key driver
+ * @new_status: new key status
+ *
+ * This function should be called by the key driver if it has not provided
+ * a poll handler with rfkill. As soon as the key driver has determined
+ * the status of the key has changed it should report the new status
+ * through this function.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status)
+{
+	struct rfkill_type *type = rfkill->key->type;
+
+	mutex_lock(&type->mutex);
+
+	if (rfkill_check_key(rfkill->key, new_status))
+		schedule_work(&type->toggle_work);
+
+	mutex_unlock(&type->mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_report_event);
+
+/*
+ * Sysfs release functions.
+ */
+static void rfkill_class_device_release(struct class_device *cdev)
+{
+	kfree(class_get_devdata(cdev));
+}
+
+static void rfkill_class_release(struct class *class)
+{
+	kfree(master);
+	master = NULL;
+}
+
+/*
+ * rfkill key attribute to display the current key status.
+ */
+static ssize_t rfkill_key_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", key->key_status);
+}
+
+static struct class_device_attribute cdev_attr_key_status =
+	__ATTR(status, S_IRUGO, rfkill_key_status_show, NULL);
+
+/*
+ * rfkill type attribute to display the current radio status.
+ */
+static ssize_t rfkill_type_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", type->radio_status);
+}
+
+static struct class_device_attribute cdev_attr_type_status =
+	__ATTR(status, S_IRUGO, rfkill_type_status_show, NULL);
+
+/*
+ * rfkill type attribute to display/change the user claim
+ */
+static ssize_t rfkill_type_claim_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", type->user_claim);
+}
+
+static ssize_t rfkill_type_claim_store(struct class_device *cdev,
+	const char * buf, size_t count)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+	unsigned int claim = simple_strtoul(buf, NULL, 0);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (claim >= USER_CLAIM_MAX)
+		return -EINVAL;
+
+	mutex_lock(&type->mutex);
+
+	type->user_claim = claim;
+
+	mutex_unlock(&type->mutex);
+
+	return count;
+}
+
+static struct class_device_attribute cdev_attr_type_claim =
+	__ATTR(claim, S_IRUGO | S_IWUSR,
+		rfkill_type_claim_show, rfkill_type_claim_store);
+
+/*
+ * rfkill key sysfs creation/destruction.
+ */
+static int rfkill_key_sysfs_create(struct rfkill_key *key)
+{
+	int status;
+
+	key->cdev = class_device_create(master->class,
+					key->type->cdev,
+					key->rfkill->dev->devt,
+					key->rfkill->dev,
+					key->rfkill->dev_name);
+	if (unlikely(!key->cdev || IS_ERR(key->cdev)))
+		return PTR_ERR(key->cdev);
+
+	/*
+	 * Make sure this key can be freed when the class_device is freed.
+	 */
+	class_set_devdata(key->cdev, key);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(key->cdev, &cdev_attr_key_status);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * The key belongs to the device structure the key driverdevt
+	 * has given us a reference to. But besides that device
+	 * this key also belongs to the input device that has been
+	 * created. We should create the link to that entry manually.
+	 */
+	status = sysfs_create_link(&key->cdev->kobj, &key->input->cdev.kobj,
+				   "idev");
+	if (unlikely(status))
+		goto exit_file;
+
+	return 0;
+
+exit_file:
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+
+exit:
+	class_device_destroy(master->class, key->rfkill->dev->devt);
+	class_set_devdata (key->cdev, NULL);
+
+	return status;
+}
+
+static void rfkill_key_sysfs_release(struct rfkill_key *key)
+{
+	sysfs_remove_link(&key->cdev->kobj, "idev");
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+	class_device_destroy(master->class, key->rfkill->dev->devt);
+}
+
+/*
+ * rfkill type sysfs creation/destruction.
+ */
+static int rfkill_type_sysfs_create(struct rfkill_type *type)
+{
+	int status;
+
+	type->cdev = class_device_create(master->class, NULL, type->key_type,
+					 NULL, type->name);
+	if (unlikely(!type->cdev || IS_ERR(type->cdev)))
+		return PTR_ERR(type->cdev);
+
+	/*
+	 * Make sure this key can be freed when the class_device is freed.
+	 */
+	class_set_devdata(type->cdev, type);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(type->cdev, &cdev_attr_type_claim);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(type->cdev, &cdev_attr_type_status);
+	if (unlikely(status))
+		goto exit_claim;
+
+	return 0;
+
+exit_claim:
+	class_device_remove_file(type->cdev, &cdev_attr_type_claim);
+
+exit:
+	class_device_destroy(type->cdev->class, type->key_type);
+	class_set_devdata (type->cdev, NULL);
+
+	return status;
+}
+
+static void rfkill_type_sysfs_release(struct rfkill_type *type)
+{
+	class_device_remove_file(type->cdev, &cdev_attr_type_status);
+	class_device_remove_file(type->cdev, &cdev_attr_type_claim);
+	class_device_destroy(type->cdev->class, type->key_type);
+}
+
+/*
+ * rfkill master sysfs creation/destruction.
+ */
+static int rfkill_master_sysfs_create(struct rfkill_master *master)
+{
+	master->class = class_create(master->owner, "rfkill");
+	if (unlikely(!master->class || IS_ERR(master->class)))
+		return PTR_ERR(master->class);
+
+	master->class->release = rfkill_class_device_release;
+	master->class->class_release = rfkill_class_release;
+
+	return 0;
+}
+
+static void rfkill_master_sysfs_release(struct rfkill_master *master)
+{
+	class_destroy(master->class);
+}
+
+/*
+ * Input device initialization/deinitialization handler.
+ */
+static struct input_dev* rfkill_register_input(struct rfkill_key *key)
+{
+	struct input_dev *input;
+	int status;
+	char *name;
+	char *phys;
+
+	input = input_allocate_device();
+	if (unlikely(!input))
+		return NULL;
+
+	/*
+	 * Link the private data to rfkill structure.
+	 */
+	input->private = key;
+
+	/*
+	 * Allocate the strings for the input device names.
+	 */
+	name = kasprintf(GFP_KERNEL, "rfkill %s",
+			 key->rfkill->dev_name);
+	phys = kasprintf(GFP_KERNEL, "rfkill/%s/%s",
+			 key->type->name, key->rfkill->dev_name);
+
+	if (!name || !phys)
+		goto exit;
+
+	input->name = name;
+	input->phys = phys;
+
+	/*
+	 * Initialize the input_id structure.
+	 */
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0001;
+
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_RFKILL, input->keybit);
+
+	status = input_register_device(input);
+	if (status)
+		goto exit;
+
+	return input;
+
+exit:
+	kfree(name);
+	kfree(phys);
+
+	input_free_device(input);
+
+	return NULL;
+}
+
+static void rfkill_deregister_input(struct rfkill_key *key)
+{
+	const char *name;
+	const char *phys;
+
+	/*
+	 * The name and phys fields have been allocated
+	 * using kasprintf, this means they have to be
+	 * freed seperately.
+	 */
+	name = key->input->name;
+	phys = key->input->phys;
+
+	input_unregister_device(key->input);
+	kfree(name);
+	kfree(phys);
+	input_free_device(key->input);
+}
+
+/*
+ * Rfkill key initialization/deinitialization.
+ * These methods should only be called with the mutex held.
+ */
+static int rfkill_key_init(struct rfkill *rfkill, struct rfkill_key *key,
+	int status)
+{
+	/*
+	 * Initialize all variables.
+	 */
+	key->rfkill = rfkill;
+	rfkill->key = key;
+	key->type = master->type[rfkill->key_type];
+	key->key_status = status;
+	INIT_LIST_HEAD(&key->entry);
+
+	/*
+	 * Create input device.
+	 */
+	key->input = rfkill_register_input(key);
+	if (!key->input)
+		goto exit;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	if (rfkill_key_sysfs_create(key))
+		goto exit_input;
+
+	return 0;
+
+exit_input:
+	rfkill_deregister_input(key);
+
+exit:
+	rfkill->key = NULL;
+
+	return -ENOMEM;
+}
+
+static void rfkill_key_deinit(struct rfkill_key *key)
+{
+	if (key) {
+		rfkill_key_sysfs_release(key);
+		rfkill_deregister_input(key);
+	}
+}
+
+/*
+ * Rfkill type handler to check if the sysfs entries should
+ * be created or removed based on the number of registered keys.
+ */
+static void rfkill_add_type_key(struct rfkill_type *type,
+	struct rfkill_key *key)
+{
+	/*
+	 * Add key to the list.
+	 */
+	list_add(&key->entry, &type->key_list);
+	type->key_count++;
+}
+
+static void rfkill_del_type_key(struct rfkill_type *type,
+	struct rfkill_key *key)
+{
+	/*
+	 * Delete the rfkill structure from the list.
+	 */
+	list_del(&key->entry);
+	type->key_count--;
+}
+
+/**
+ * rfkill_register_key - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ * @init_status: initial status of the key at the time this function is called
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be registered. Immediately from registration the key driver
+ * should be able to receive calls through the poll, enable_radio and
+ * disable_radio handlers if those were registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status)
+{
+	struct rfkill_type *type;
+	struct rfkill_key *key;
+	int status;
+
+	if (!rfkill || !rfkill->enable_radio || !rfkill->disable_radio)
+		return -EINVAL;
+
+	/*
+	 * Check if the requested key_type was valid.
+	 */
+	if (rfkill->key_type >= KEY_TYPE_MAX)
+		return -EINVAL;
+
+	type = master->type[rfkill->key_type];
+
+	/*
+	 * Increase module use count to prevent this
+	 * module to be unloaded while there are still
+	 * registered keys.
+	 */
+	if (!try_module_get(master->owner))
+		return -EBUSY;
+
+	mutex_lock(&type->mutex);
+
+	key = kzalloc(sizeof(struct rfkill_key), GFP_KERNEL);
+	if (unlikely(!key)) {
+		status = -ENOMEM;
+		goto exit;
+	}
+
+	/*
+	 * Initialize key, and add it to the type structure.
+	 */
+	key->type = type;
+	status = rfkill_key_init(rfkill, key, init_status);
+	if (status)
+		goto exit_key;
+
+	/*
+	 * Add key to the specified type.
+	 */
+	rfkill_add_type_key(type, key);
+
+	/*
+	 * Check if we need polling, and if we do
+	 * increase the poll required counter and check
+	 * if we weren't polling yet.
+	 */
+	if (rfkill->poll && !type->poll_required++)
+		schedule_delayed_work(&type->poll_work, RFKILL_POLL_DELAY);
+
+	mutex_unlock(&type->mutex);
+
+	return 0;
+
+exit_key:
+	kfree(key);
+
+exit:
+	mutex_unlock(&type->mutex);
+	module_put(master->owner);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(rfkill_register_key);
+
+/**
+ * rfkill_deregister_key - Deregister a previously registered rfkill structure.
+ * @rfkill: rfkill structure to be deregistered
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be deregistered. This function may only be called if it was
+ * previously registered with rfkill_register_key.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill)
+{
+	struct rfkill_type *type;
+
+	if (!rfkill || !rfkill->key)
+		return -EINVAL;
+
+	type = rfkill->key->type;
+
+	mutex_lock(&type->mutex);
+
+	/*
+	 * Cancel delayed work if this is the last key
+	 * that requires polling. It is not bad if the
+	 * workqueue is still running, the workqueue
+	 * will not rearm itself since the poll_required
+	 * variable has been cleared, and we have
+	 * protected the list with a mutex.
+	 */
+	if (rfkill->poll && !--type->poll_required)
+		cancel_delayed_work(&type->poll_work);
+
+	/*
+	 * Deinitialize key, and remove it from the type.
+	 */
+	rfkill_del_type_key(type, rfkill->key);
+	rfkill_key_deinit(rfkill->key);
+
+	mutex_unlock(&type->mutex);
+
+	/*
+	 * rfkill entry has been removed,
+	 * decrease module use count.
+	 */
+	module_put(master->owner);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rfkill_deregister_key);
+
+/*
+ * Rfkill type initialization/deinitialization.
+ */
+static int rfkill_type_init(unsigned int key_type, char *name)
+{
+	struct rfkill_type *type;
+	int status;
+
+	type = kzalloc(sizeof(struct rfkill_type), GFP_KERNEL);
+	if (unlikely(!type))
+		return -ENOMEM;
+
+	/*
+	 * Initialize all variables.
+	 */
+	mutex_init(&type->mutex);
+	type->name = name;
+	type->key_type = key_type;
+	INIT_LIST_HEAD(&type->key_list);
+	type->toggled = 0;
+	type->key_count = 0;
+	type->poll_required = 0;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	INIT_WORK(&type->toggle_work, &rfkill_toggle_radio, type);
+	INIT_WORK(&type->poll_work, &rfkill_list_poll, type);
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_type_sysfs_create(type);
+	if (status) {
+		printk(KERN_ERR "Failed to create sysfs entry %s", name);
+		goto exit;
+	}
+
+	master->type[key_type] = type;
+
+	return 0;
+
+exit:
+	kfree(type);
+
+	return status;
+}
+
+static void rfkill_type_deinit(unsigned int key_type)
+{
+	rfkill_type_sysfs_release(master->type[key_type]);
+}
+
+/*
+ * Rfkill master initialization/deinitialization.
+ */
+static int rfkill_master_init(void)
+{
+	int status;
+
+	master = kzalloc(sizeof(struct rfkill_master), GFP_KERNEL);
+	if (unlikely(!master))
+		return -ENOMEM;
+
+	/*
+	 * Set the module owner reference.
+	 */
+	master->owner = THIS_MODULE;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_master_sysfs_create(master);
+	if (status) {
+		printk(KERN_ERR "Failed to create master sysfs entry");
+		goto exit;
+	}
+
+	/*
+	 * Create and initialize all type structures.
+	 */
+	status = rfkill_type_init(KEY_TYPE_WLAN, "wlan");
+	if (status)
+		goto exit_master;
+
+	status = rfkill_type_init(KEY_TYPE_BlUETOOTH, "bluetooth");
+	if (status)
+		goto exit_wlan;
+
+	status = rfkill_type_init(KEY_TYPE_IRDA, "irda");
+	if (status)
+		goto exit_bluetooth;
+
+	return 0;
+
+exit_bluetooth:
+	rfkill_type_deinit(KEY_TYPE_BlUETOOTH);
+
+exit_wlan:
+	rfkill_type_deinit(KEY_TYPE_WLAN);
+
+exit_master:
+	rfkill_master_sysfs_release(master);
+
+exit:
+	kfree(master);
+	master = NULL;
+
+	return status;
+}
+
+static void rfkill_master_deinit(void)
+{
+	if (master) {
+		rfkill_type_deinit(KEY_TYPE_IRDA);
+		rfkill_type_deinit(KEY_TYPE_BlUETOOTH);
+		rfkill_type_deinit(KEY_TYPE_WLAN);
+		rfkill_master_sysfs_release(master);
+	}
+}
+
+/*
+ * Module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	printk(KERN_INFO "Loading rfkill driver.\n");
+
+	return rfkill_master_init();
+}
+
+static void __exit rfkill_exit(void)
+{
+	rfkill_master_deinit();
+
+	printk(KERN_INFO "Unloading rfkill driver.\n");
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
diff --git a/include/linux/input.h b/include/linux/input.h
index c38507b..1b44108 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -491,6 +491,7 @@ struct input_absinfo {
 #define KEY_DIGITS		0x19d
 #define KEY_TEEN		0x19e
 #define KEY_TWEN		0x19f
+#define KEY_RFKILL		0x1a0
 
 #define KEY_DEL_EOL		0x1c0
 #define KEY_DEL_EOS		0x1c1
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
new file mode 100644
index 0000000..4f45429
--- /dev/null
+++ b/include/linux/rfkill.h
@@ -0,0 +1,140 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+	RF button support
+	Laptops are quite often equipped with an RF key to enable or
+	disable the radio of the wireless device attached to that key.
+	This wireless device usually is an integrated wireless network device,
+	infrared or bluetooth device.
+	There are 3 catagories of radio control keys:
+	 A) The key directly toggles the hardware radio, and does not send an
+	    event to userspace.
+	 B) The key does not toggle the hardware radio, and does not send an
+	    event to userspace.
+	 C) The key does not toggle the hardware radio, but does send an event
+	    to userspace.
+	Catagory (A) should create an input device themselves and send an
+	KEY_RFKILL event over that input device.
+	Catagory (B) should register themselves with rkfkill and allow rfkill
+	to toggle the radio and report events to userspace.
+	Catagory (C) should register with rfkill, who will listen to userspace
+	requests to toggle the radio and will send the signal to the driver.
+
+	The rfkill driver will contain a list of all devices with an RF button,
+	and hardware drivers need to register their hardware to the rfkill
+	interface. Rfkill will then take care of everything. If the RF key
+	requires polling to obtain the status this will be handled by rfkill.
+	If the RF key does not require polling but sends for example interrupts,
+	the hardware driver can report the change of status to rfkill, without
+	having to do any other action.
+
+	For each registered hardware button an input device will be created.
+	If this input device has been opened by the user, rfkill will send a
+	signal to userspace instead of the hardware about the new button
+	status. This will allow userpace to perform the correct steps
+	in order to bring down all interfaces.
+
+	Through sysfs it is also possible the user requests the toggling of
+	the radio, this means that the radio could be toggled even without
+	pressing the radio key.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+#include <linux/device.h>
+
+/*
+ * The keys will be periodically checked if the
+ * key has been toggled. By default this will be 100ms.
+ */
+#define RFKILL_POLL_DELAY	( HZ / 10 )
+
+/**
+ * enum key_type - Key type for rfkill keys.
+ * KEY_TYPE_WLAN: key type for Wireless network devices.
+ * KEY_TYPE_BlUETOOTH: key type for bluetooth devices.
+ * KEY_TYPE_IRDA: key type for infrared devices.
+ */
+enum key_type {
+	KEY_TYPE_WLAN = 0,
+	KEY_TYPE_BlUETOOTH = 1,
+	KEY_TYPE_IRDA = 2,
+	KEY_TYPE_MAX = 3,
+};
+
+/**
+ * enum user_claim - Users' claim on rfkill key.
+ * @USER_CLAIM_IGNORE: Don't notify user of key events.
+ * @USER_CLAIM_NOTIFY: Notify user of key events, but
+ * 	still automatically toggle the radio.
+ * @USER_CLAIM_SINGLE: Notify user of key events, and
+ * 	do not toggle the radio anymore.
+ */
+enum user_claim {
+	USER_CLAIM_IGNORE = 0,
+	USER_CLAIM_NOTIFY = 1,
+	USER_CLAIM_SINGLE = 2,
+	USER_CLAIM_MAX = 3,
+};
+
+/**
+ * struct rfkill - rfkill button control structure.
+ * @dev_name: Name of the interface. This will become the name
+ * 	of the input device which will be created for this button.
+ * @dev: Pointer to the device structure to which this button belongs to.
+ * @data: Pointer to the RF button drivers private data which will be
+ * 	passed along with the radio and polling handlers.
+ * @poll(void *data): Optional handler which will frequently be
+ * 	called to determine the current status of the RF button.
+ * @enable_radio(void *data): Optional handler to enable the radio
+ * 	once the RF button has been pressed and the hardware does enable
+ * 	the radio automaticly.
+ * @disable_radio(void *data): Optional handler to disable the radio
+ * 	once the RF button has been pressed and the hardware does disable
+ * 	the radio automaticly.
+ * @key_type: Radio type which the button controls, the value stored
+ * 	here should be a value from enum key_type.
+ * @key: Internal pointer that should not be touched by key driver.
+ *
+ * This structure can be used by a key driver to register the key
+ * to the rfkill driver in order to take control of the reporting
+ * to userspace or handling of radio status.
+ */
+struct rfkill {
+	char *dev_name;
+
+	struct device *dev;
+
+	void *data;
+	int (*poll)(void *data);
+	void (*enable_radio)(void *data);
+	void (*disable_radio)(void *data);
+
+	unsigned int key_type;
+
+	struct rfkill_key *key;
+};
+
+int rfkill_register_key(struct rfkill *rfkill, int init_status);
+int rfkill_deregister_key(struct rfkill *rfkill);
+void rfkill_report_event(struct rfkill *rfkill, int new_status);
+
+#endif /* RFKILL_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-17 17:43                   ` Ivo van Doorn
@ 2007-01-30 16:33                     ` Ivo van Doorn
  0 siblings, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2007-01-30 16:33 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger,
	Christoph Hellwig, Randy Dunlap

Well it's been a while, but here is an updated version of rfkill.

The changes since the version that was originally send are:

Spelling fixes (Thanks to Randy Dunlap)
THIS_MODULE is now a field in the rkfill_master (Suggested by Christoph Hellwig)
Move to the new Workqueue API

The open_count has been completely removed, decision making on which action should
be taken is now handled by the user_claim field, which can be set through sysfs.
The possible choice include
 1 - let rfkill handle everything without bothering the user
 2 - let rfkill handle everything but send a notification to the user
 3 - let rfkill send a notification only

The toggling of the keys is now type based, this means that if 1 key is being toggled
all keys of the same type will be toggled.

As optimization and clearly seperate the keys per type, the rfkill_type structure
now holds the list of the keys that belong to him. This has greatly reduced
the size of the rfkill_master structure.

sysfs will hold the following entries:

- The main folder: "rfkill"
- The main folder contains the type folders "wlan", "bluetooth" and "irda".
- Each type folder contains the files
	- "claim" where the user claim can be read/written
	- "status" The radio status of this type
	- The folders for each key belonging to this type
- Each key folder contains the files
	- "status" The status of this key
	- "idev" The symlink to the input device entry in sysfs
	- "dev" The symlink to the drivers device entry in sysfs

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>

---

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ba0e88c..6986d59 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -79,4 +79,19 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config RFKILL
+	tristate "RF button support"
+	help
+	  If you say yes here, the rfkill driver will be build
+	  which allowed network devices to register their hardware
+	  RF button which controls the radio state. This driver
+	  will then create an input device for it.
+
+	  When the input device is not used, the rfkill driver
+	  will make sure that when the RF button is pressed the radio
+	  is enabled or disabled accordingly. When the input device
+	  has been opened by the user this radio control will be left
+	  to the user, and rfkill will only send the RF button status
+	  change to userspace.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415c491..e788a1b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_RFKILL)			+= rfkill.o
diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
new file mode 100644
index 0000000..6719962
--- /dev/null
+++ b/drivers/input/misc/rfkill.c
@@ -0,0 +1,988 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF key support");
+MODULE_LICENSE("GPL");
+
+/*
+ * rfkill key structure.
+ */
+struct rfkill_key {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Pointer to rfkill structure
+	 * that was filled in by key driver.
+	 */
+	struct rfkill *rfkill;
+
+	/*
+	 * Pointer to type structure
+	 * that this key belongs to.
+	 */
+	struct rfkill_type *type;
+
+	/*
+	 * Current status of the key which controls the radio,
+	 * this value will change after the key state has changed
+	 * after polling, or the key driver has send the new state
+	 * manually.
+	 */
+	int key_status;
+
+	/*
+	 * Input device for this key.
+	 */
+	struct input_dev *input;
+
+	/*
+	 * List head structure to be used
+	 * to add this structure to the list.
+	 */
+	struct list_head entry;
+};
+
+/*
+ * rfkill type structure.
+ */
+struct rfkill_type {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * All access to the type structure and its
+	 * children (the keys) are protected by this mutex.
+	 */
+	struct mutex mutex;
+
+	/*
+	 * Name of this radio type.
+	 */
+	char *name;
+
+	/*
+	 * Key type identification. Value must be any
+	 * in the key_type enum.
+	 */
+	unsigned int key_type;
+
+	/*
+	 * List of rfkill_key structures.
+	 */
+	struct list_head key_list;
+
+	/*
+	 * Number of registered keys of this type.
+	 */
+	unsigned int key_count;
+
+	/*
+	 * Once key status change has been detected, the toggled
+	 * field should be set to indicate a notification to
+	 * user or driver should be performed. This is stored
+	 * globally since all keys within this type need to be
+	 * toggled.
+	 */
+	unsigned int toggled;
+
+	/*
+	 * The claim the user has over the toggling of the radio,
+	 * this can be any value of the user_claim enumeration.
+	 */
+	unsigned int user_claim;
+
+	/*
+	 * Current state of the radios in this type.
+	 */
+	unsigned int radio_status;
+
+	/*
+	 * Number of keys that require polling
+	 */
+	unsigned int poll_required;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	struct work_struct toggle_work;
+	struct delayed_work poll_work;
+};
+
+/*
+ * rfkill master structure.
+ */
+struct rfkill_master {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class *class;
+
+	/*
+	 * Reference to this modules structure.
+	 */
+	struct module *owner;
+
+	/*
+	 * List of available key types.
+	 */
+	struct rfkill_type* type[KEY_TYPE_MAX];
+};
+
+/*
+ * We only need 1 single master interface,
+ * make this a global variable since we always need it.
+ */
+static struct rfkill_master *master;
+
+/*
+ * Send toggle event to key driver.
+ */
+static void rfkill_toggle_radio_driver(struct rfkill_key *key)
+{
+	struct rfkill *rfkill = key->rfkill;
+	struct rfkill_type *type = key->type;
+
+	/*
+	 * Check what the current radio status is, and perform
+	 * the correct action to toggle the radio. This will make
+	 * sure the correct event happens when either the key driver
+	 * of the user has requested to toggle this radio.
+	 */
+	if (!type->radio_status)
+		rfkill->enable_radio(rfkill->data);
+	else if (type->radio_status)
+		rfkill->disable_radio(rfkill->data);
+}
+
+/*
+ * Send toggle event to userspace through the input device.
+ */
+static void rfkill_toggle_radio_input(struct rfkill_key *key)
+{
+	input_report_key(key->input, KEY_RFKILL, !key->type->radio_status);
+	input_sync(key->input);
+}
+
+/*
+ * Loop through the list of registered keys and toggle each
+ * key if this type has been toggled (by either user or driver).
+ */
+static void rfkill_toggle_radio(struct work_struct *work)
+{
+	struct rfkill_type *type =
+		container_of(work, struct rfkill_type, toggle_work);
+	struct rfkill_key *key;
+
+	mutex_lock(&type->mutex);
+
+	/*
+	 * If this type hasn't been toggled by any of the keys
+	 * or the user we can skip the toggle run.
+	 */
+	if (!type->toggled) {
+		mutex_unlock(&type->mutex);
+		return;
+	}
+
+	list_for_each_entry(key, &type->key_list, entry) {
+		/*
+		 * Check what kind of claim the user has requested
+		 * on this key. This determined if we should send
+		 * a user and/or a driver notification.
+		 */
+		if (type->user_claim != USER_CLAIM_IGNORE)
+			rfkill_toggle_radio_input(key);
+		else if (type->user_claim != USER_CLAIM_SINGLE)
+			rfkill_toggle_radio_driver(key);
+	}
+
+	/*
+	 * Reset the radio_status and toggled field,
+	 * to respresent the correct status to the user,
+	 * and allow the status to be toggled again.
+	 */
+	type->radio_status = !type->radio_status;
+	type->toggled = 0;
+
+	mutex_unlock(&type->mutex);
+}
+
+/*
+ * Check the new status of the key, and determine if the key has been toggled.
+ * This function can be called upon request by the device driver or userspace.
+ */
+static int rfkill_check_status(struct rfkill_key *key, int status)
+{
+	struct rfkill_type *type = key->type;
+
+	/*
+	 * A key should be toggled if the current radio status does not
+	 * match the requested status. This check is required instead of
+	 * comparing it to the current key status since this function can
+	 * be called on request of the user as well. The user is able to
+	 * request a radio toggle to make sure the radio status will match
+	 * the key status if the new key status has been reported to
+	 * userspace only.
+	 * As a safety measure, we won't toggle a key twice.
+	 */
+	if (type->radio_status != !!status && !type->toggled) {
+		type->toggled = 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Wrapper around the rfkill_check_status function, this may only
+ * be called from the device driver status events. Besides calling
+ * rfkill_check_status it will also update the key_status field.
+ */
+static int rfkill_check_key(struct rfkill_key *key, int status)
+{
+	/*
+	 * Store the new key status, and only if the 2
+	 * are different the new status status should be
+	 * passed on to rfkill_check_status to determine
+	 * if any action should be performed.
+	 */
+	if (key->key_status == !!status)
+		return 0;
+
+	key->key_status = !!status;
+	return rfkill_check_status(key->rfkill->key, status);
+}
+
+/*
+ * Handler that is run frequently to determine the current status
+ * of all registered keys that require polling.
+ */
+static void rfkill_list_poll(struct work_struct *work)
+{
+	struct rfkill_type *type =
+		container_of(work, struct rfkill_type, poll_work.work);
+	struct rfkill_key *key;
+	struct rfkill *rfkill;
+	int status = 0;
+
+	mutex_lock(&type->mutex);
+
+	list_for_each_entry(key, &type->key_list, entry) {
+		rfkill = key->rfkill;
+
+		/*
+		 * If the poll handler has not been given
+		 * the key driver should report events,
+		 * so we can ignore this key now.
+		 */
+		if (!rfkill->poll)
+			continue;
+
+		/*
+		 * Poll radio state and check if a radio toggle
+		 * has been requested by the key.
+		 */
+		if (rfkill_check_key(key, rfkill->poll(rfkill->data)))
+			status = 1;
+	}
+
+	mutex_unlock(&type->mutex);
+
+	/*
+	 * A radio toggle has been requested, schedule the toggle work thread.
+	 */
+	if (status)
+		schedule_work(&type->toggle_work);
+
+	/*
+	 * Check if we need to rearm ourselves.
+	 */
+	if (type->poll_required)
+		schedule_delayed_work(&type->poll_work, RFKILL_POLL_DELAY);
+}
+
+/**
+ * rfkill_report_event - Report change in key status to rfkill handler.
+ * @rfkill: rfkill structure registered by key driver
+ * @new_status: new key status
+ *
+ * This function should be called by the key driver if it has not provided
+ * a poll handler with rfkill. As soon as the key driver has determined
+ * the status of the key has changed it should report the new status
+ * through this function.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status)
+{
+	struct rfkill_type *type = rfkill->key->type;
+
+	mutex_lock(&type->mutex);
+
+	if (rfkill_check_key(rfkill->key, new_status))
+		schedule_work(&type->toggle_work);
+
+	mutex_unlock(&type->mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_report_event);
+
+/*
+ * Sysfs release functions.
+ */
+static void rfkill_class_device_release(struct class_device *cdev)
+{
+	kfree(class_get_devdata(cdev));
+}
+
+static void rfkill_class_release(struct class *class)
+{
+	kfree(master);
+	master = NULL;
+}
+
+/*
+ * rfkill key attribute to display the current key status.
+ */
+static ssize_t rfkill_key_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", key->key_status);
+}
+
+static struct class_device_attribute cdev_attr_key_status =
+	__ATTR(status, S_IRUGO, rfkill_key_status_show, NULL);
+
+/*
+ * rfkill type attribute to display the current radio status.
+ */
+static ssize_t rfkill_type_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", type->radio_status);
+}
+
+static struct class_device_attribute cdev_attr_type_status =
+	__ATTR(status, S_IRUGO, rfkill_type_status_show, NULL);
+
+/*
+ * rfkill type attribute to display/change the user claim
+ */
+static ssize_t rfkill_type_claim_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", type->user_claim);
+}
+
+static ssize_t rfkill_type_claim_store(struct class_device *cdev,
+	const char * buf, size_t count)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+	unsigned int claim = simple_strtoul(buf, NULL, 0);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (claim >= USER_CLAIM_MAX)
+		return -EINVAL;
+
+	mutex_lock(&type->mutex);
+
+	type->user_claim = claim;
+
+	mutex_unlock(&type->mutex);
+
+	return count;
+}
+
+static struct class_device_attribute cdev_attr_type_claim =
+	__ATTR(claim, S_IRUGO | S_IWUSR,
+		rfkill_type_claim_show, rfkill_type_claim_store);
+
+/*
+ * rfkill key sysfs creation/destruction.
+ */
+static int rfkill_key_sysfs_create(struct rfkill_key *key)
+{
+	int status;
+
+	key->cdev = class_device_create(master->class,
+					key->type->cdev,
+					key->rfkill->dev->devt,
+					key->rfkill->dev,
+					key->rfkill->dev_name);
+	if (unlikely(!key->cdev || IS_ERR(key->cdev)))
+		return PTR_ERR(key->cdev);
+
+	/*
+	 * Make sure this key can be freed when the class_device is freed.
+	 */
+	class_set_devdata(key->cdev, key);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(key->cdev, &cdev_attr_key_status);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * The key belongs to the device structure the key driverdevt
+	 * has given us a reference to. But besides that device
+	 * this key also belongs to the input device that has been
+	 * created. We should create the link to that entry manually.
+	 */
+	status = sysfs_create_link(&key->cdev->kobj, &key->input->cdev.kobj,
+				   "idev");
+	if (unlikely(status))
+		goto exit_file;
+
+	return 0;
+
+exit_file:
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+
+exit:
+	class_device_destroy(master->class, key->rfkill->dev->devt);
+	class_set_devdata (key->cdev, NULL);
+
+	return status;
+}
+
+static void rfkill_key_sysfs_release(struct rfkill_key *key)
+{
+	sysfs_remove_link(&key->cdev->kobj, "idev");
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+	class_device_destroy(master->class, key->rfkill->dev->devt);
+}
+
+/*
+ * rfkill type sysfs creation/destruction.
+ */
+static int rfkill_type_sysfs_create(struct rfkill_type *type)
+{
+	int status;
+
+	type->cdev = class_device_create(master->class, NULL, type->key_type,
+					 NULL, type->name);
+	if (unlikely(!type->cdev || IS_ERR(type->cdev)))
+		return PTR_ERR(type->cdev);
+
+	/*
+	 * Make sure this key can be freed when the class_device is freed.
+	 */
+	class_set_devdata(type->cdev, type);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(type->cdev, &cdev_attr_type_claim);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(type->cdev, &cdev_attr_type_status);
+	if (unlikely(status))
+		goto exit_claim;
+
+	return 0;
+
+exit_claim:
+	class_device_remove_file(type->cdev, &cdev_attr_type_claim);
+
+exit:
+	class_device_destroy(type->cdev->class, type->key_type);
+	class_set_devdata (type->cdev, NULL);
+
+	return status;
+}
+
+static void rfkill_type_sysfs_release(struct rfkill_type *type)
+{
+	class_device_remove_file(type->cdev, &cdev_attr_type_status);
+	class_device_remove_file(type->cdev, &cdev_attr_type_claim);
+	class_device_destroy(type->cdev->class, type->key_type);
+}
+
+/*
+ * rfkill master sysfs creation/destruction.
+ */
+static int rfkill_master_sysfs_create(struct rfkill_master *master)
+{
+	master->class = class_create(master->owner, "rfkill");
+	if (unlikely(!master->class || IS_ERR(master->class)))
+		return PTR_ERR(master->class);
+
+	master->class->release = rfkill_class_device_release;
+	master->class->class_release = rfkill_class_release;
+
+	return 0;
+}
+
+static void rfkill_master_sysfs_release(struct rfkill_master *master)
+{
+	class_destroy(master->class);
+}
+
+/*
+ * Input device initialization/deinitialization handler.
+ */
+static struct input_dev* rfkill_register_input(struct rfkill_key *key)
+{
+	struct input_dev *input;
+	int status;
+	char *name;
+	char *phys;
+
+	input = input_allocate_device();
+	if (unlikely(!input))
+		return NULL;
+
+	/*
+	 * Link the private data to rfkill structure.
+	 */
+	input->private = key;
+
+	/*
+	 * Allocate the strings for the input device names.
+	 */
+	name = kasprintf(GFP_KERNEL, "rfkill %s",
+			 key->rfkill->dev_name);
+	phys = kasprintf(GFP_KERNEL, "rfkill/%s/%s",
+			 key->type->name, key->rfkill->dev_name);
+
+	if (!name || !phys)
+		goto exit;
+
+	input->name = name;
+	input->phys = phys;
+
+	/*
+	 * Initialize the input_id structure.
+	 */
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0001;
+
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_RFKILL, input->keybit);
+
+	status = input_register_device(input);
+	if (status)
+		goto exit;
+
+	return input;
+
+exit:
+	kfree(name);
+	kfree(phys);
+
+	input_free_device(input);
+
+	return NULL;
+}
+
+static void rfkill_deregister_input(struct rfkill_key *key)
+{
+	const char *name;
+	const char *phys;
+
+	/*
+	 * The name and phys fields have been allocated
+	 * using kasprintf, this means they have to be
+	 * freed seperately.
+	 */
+	name = key->input->name;
+	phys = key->input->phys;
+
+	input_unregister_device(key->input);
+	kfree(name);
+	kfree(phys);
+	input_free_device(key->input);
+}
+
+/*
+ * Rfkill key initialization/deinitialization.
+ * These methods should only be called with the mutex held.
+ */
+static int rfkill_key_init(struct rfkill *rfkill, struct rfkill_key *key,
+	int status)
+{
+	/*
+	 * Initialize all variables.
+	 */
+	key->rfkill = rfkill;
+	rfkill->key = key;
+	key->type = master->type[rfkill->key_type];
+	key->key_status = status;
+	INIT_LIST_HEAD(&key->entry);
+
+	/*
+	 * Create input device.
+	 */
+	key->input = rfkill_register_input(key);
+	if (!key->input)
+		goto exit;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	if (rfkill_key_sysfs_create(key))
+		goto exit_input;
+
+	return 0;
+
+exit_input:
+	rfkill_deregister_input(key);
+
+exit:
+	rfkill->key = NULL;
+
+	return -ENOMEM;
+}
+
+static void rfkill_key_deinit(struct rfkill_key *key)
+{
+	if (key) {
+		rfkill_key_sysfs_release(key);
+		rfkill_deregister_input(key);
+	}
+}
+
+/*
+ * Rfkill type handler to check if the sysfs entries should
+ * be created or removed based on the number of registered keys.
+ */
+static void rfkill_add_type_key(struct rfkill_type *type,
+	struct rfkill_key *key)
+{
+	/*
+	 * Add key to the list.
+	 */
+	list_add(&key->entry, &type->key_list);
+	type->key_count++;
+}
+
+static void rfkill_del_type_key(struct rfkill_type *type,
+	struct rfkill_key *key)
+{
+	/*
+	 * Delete the rfkill structure from the list.
+	 */
+	list_del(&key->entry);
+	type->key_count--;
+}
+
+/**
+ * rfkill_register_key - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ * @init_status: initial status of the key at the time this function is called
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be registered. Immediately from registration the key driver
+ * should be able to receive calls through the poll, enable_radio and
+ * disable_radio handlers if those were registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status)
+{
+	struct rfkill_type *type;
+	struct rfkill_key *key;
+	int status;
+
+	if (!rfkill || !rfkill->enable_radio || !rfkill->disable_radio)
+		return -EINVAL;
+
+	/*
+	 * Check if the requested key_type was valid.
+	 */
+	if (rfkill->key_type >= KEY_TYPE_MAX)
+		return -EINVAL;
+
+	type = master->type[rfkill->key_type];
+
+	/*
+	 * Increase module use count to prevent this
+	 * module to be unloaded while there are still
+	 * registered keys.
+	 */
+	if (!try_module_get(master->owner))
+		return -EBUSY;
+
+	mutex_lock(&type->mutex);
+
+	key = kzalloc(sizeof(struct rfkill_key), GFP_KERNEL);
+	if (unlikely(!key)) {
+		status = -ENOMEM;
+		goto exit;
+	}
+
+	/*
+	 * Initialize key, and add it to the type structure.
+	 */
+	key->type = type;
+	status = rfkill_key_init(rfkill, key, init_status);
+	if (status)
+		goto exit_key;
+
+	/*
+	 * Add key to the specified type.
+	 */
+	rfkill_add_type_key(type, key);
+
+	/*
+	 * Check if we need polling, and if we do
+	 * increase the poll required counter and check
+	 * if we weren't polling yet.
+	 */
+	if (rfkill->poll && !type->poll_required++)
+		schedule_delayed_work(&type->poll_work, RFKILL_POLL_DELAY);
+
+	mutex_unlock(&type->mutex);
+
+	return 0;
+
+exit_key:
+	kfree(key);
+
+exit:
+	mutex_unlock(&type->mutex);
+	module_put(master->owner);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(rfkill_register_key);
+
+/**
+ * rfkill_deregister_key - Deregister a previously registered rfkill structure.
+ * @rfkill: rfkill structure to be deregistered
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be deregistered. This function may only be called if it was
+ * previously registered with rfkill_register_key.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill)
+{
+	struct rfkill_type *type;
+
+	if (!rfkill || !rfkill->key)
+		return -EINVAL;
+
+	type = rfkill->key->type;
+
+	mutex_lock(&type->mutex);
+
+	/*
+	 * Cancel delayed work if this is the last key
+	 * that requires polling. It is not bad if the
+	 * workqueue is still running, the workqueue
+	 * will not rearm itself since the poll_required
+	 * variable has been cleared, and we have
+	 * protected the list with a mutex.
+	 */
+	if (rfkill->poll && !--type->poll_required)
+		cancel_delayed_work(&type->poll_work);
+
+	/*
+	 * Deinitialize key, and remove it from the type.
+	 */
+	rfkill_del_type_key(type, rfkill->key);
+	rfkill_key_deinit(rfkill->key);
+
+	mutex_unlock(&type->mutex);
+
+	/*
+	 * rfkill entry has been removed,
+	 * decrease module use count.
+	 */
+	module_put(master->owner);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rfkill_deregister_key);
+
+/*
+ * Rfkill type initialization/deinitialization.
+ */
+static int rfkill_type_init(unsigned int key_type, char *name)
+{
+	struct rfkill_type *type;
+	int status;
+
+	type = kzalloc(sizeof(struct rfkill_type), GFP_KERNEL);
+	if (unlikely(!type))
+		return -ENOMEM;
+
+	/*
+	 * Initialize all variables.
+	 */
+	mutex_init(&type->mutex);
+	type->name = name;
+	type->key_type = key_type;
+	INIT_LIST_HEAD(&type->key_list);
+	type->toggled = 0;
+	type->key_count = 0;
+	type->poll_required = 0;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	INIT_WORK(&type->toggle_work, &rfkill_toggle_radio);
+	INIT_DELAYED_WORK(&type->poll_work, &rfkill_list_poll);
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_type_sysfs_create(type);
+	if (status) {
+		printk(KERN_ERR "Failed to create sysfs entry %s", name);
+		goto exit;
+	}
+
+	master->type[key_type] = type;
+
+	return 0;
+
+exit:
+	kfree(type);
+
+	return status;
+}
+
+static void rfkill_type_deinit(unsigned int key_type)
+{
+	rfkill_type_sysfs_release(master->type[key_type]);
+}
+
+/*
+ * Rfkill master initialization/deinitialization.
+ */
+static int rfkill_master_init(void)
+{
+	int status;
+
+	master = kzalloc(sizeof(struct rfkill_master), GFP_KERNEL);
+	if (unlikely(!master))
+		return -ENOMEM;
+
+	/*
+	 * Set the module owner reference.
+	 */
+	master->owner = THIS_MODULE;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_master_sysfs_create(master);
+	if (status) {
+		printk(KERN_ERR "Failed to create master sysfs entry");
+		goto exit;
+	}
+
+	/*
+	 * Create and initialize all type structures.
+	 */
+	status = rfkill_type_init(KEY_TYPE_WLAN, "wlan");
+	if (status)
+		goto exit_master;
+
+	status = rfkill_type_init(KEY_TYPE_BlUETOOTH, "bluetooth");
+	if (status)
+		goto exit_wlan;
+
+	status = rfkill_type_init(KEY_TYPE_IRDA, "irda");
+	if (status)
+		goto exit_bluetooth;
+
+	return 0;
+
+exit_bluetooth:
+	rfkill_type_deinit(KEY_TYPE_BlUETOOTH);
+
+exit_wlan:
+	rfkill_type_deinit(KEY_TYPE_WLAN);
+
+exit_master:
+	rfkill_master_sysfs_release(master);
+
+exit:
+	kfree(master);
+	master = NULL;
+
+	return status;
+}
+
+static void rfkill_master_deinit(void)
+{
+	if (master) {
+		rfkill_type_deinit(KEY_TYPE_IRDA);
+		rfkill_type_deinit(KEY_TYPE_BlUETOOTH);
+		rfkill_type_deinit(KEY_TYPE_WLAN);
+		rfkill_master_sysfs_release(master);
+	}
+}
+
+/*
+ * Module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	printk(KERN_INFO "Loading rfkill driver.\n");
+
+	return rfkill_master_init();
+}
+
+static void __exit rfkill_exit(void)
+{
+	rfkill_master_deinit();
+
+	printk(KERN_INFO "Unloading rfkill driver.\n");
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
new file mode 100644
index 0000000..4f45429
--- /dev/null
+++ b/include/linux/rfkill.h
@@ -0,0 +1,140 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+	RF button support
+	Laptops are quite often equipped with an RF key to enable or
+	disable the radio of the wireless device attached to that key.
+	This wireless device usually is an integrated wireless network device,
+	infrared or bluetooth device.
+	There are 3 catagories of radio control keys:
+	 A) The key directly toggles the hardware radio, and does not send an
+	    event to userspace.
+	 B) The key does not toggle the hardware radio, and does not send an
+	    event to userspace.
+	 C) The key does not toggle the hardware radio, but does send an event
+	    to userspace.
+	Catagory (A) should create an input device themselves and send an
+	KEY_RFKILL event over that input device.
+	Catagory (B) should register themselves with rkfkill and allow rfkill
+	to toggle the radio and report events to userspace.
+	Catagory (C) should register with rfkill, who will listen to userspace
+	requests to toggle the radio and will send the signal to the driver.
+
+	The rfkill driver will contain a list of all devices with an RF button,
+	and hardware drivers need to register their hardware to the rfkill
+	interface. Rfkill will then take care of everything. If the RF key
+	requires polling to obtain the status this will be handled by rfkill.
+	If the RF key does not require polling but sends for example interrupts,
+	the hardware driver can report the change of status to rfkill, without
+	having to do any other action.
+
+	For each registered hardware button an input device will be created.
+	If this input device has been opened by the user, rfkill will send a
+	signal to userspace instead of the hardware about the new button
+	status. This will allow userpace to perform the correct steps
+	in order to bring down all interfaces.
+
+	Through sysfs it is also possible the user requests the toggling of
+	the radio, this means that the radio could be toggled even without
+	pressing the radio key.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+#include <linux/device.h>
+
+/*
+ * The keys will be periodically checked if the
+ * key has been toggled. By default this will be 100ms.
+ */
+#define RFKILL_POLL_DELAY	( HZ / 10 )
+
+/**
+ * enum key_type - Key type for rfkill keys.
+ * KEY_TYPE_WLAN: key type for Wireless network devices.
+ * KEY_TYPE_BlUETOOTH: key type for bluetooth devices.
+ * KEY_TYPE_IRDA: key type for infrared devices.
+ */
+enum key_type {
+	KEY_TYPE_WLAN = 0,
+	KEY_TYPE_BlUETOOTH = 1,
+	KEY_TYPE_IRDA = 2,
+	KEY_TYPE_MAX = 3,
+};
+
+/**
+ * enum user_claim - Users' claim on rfkill key.
+ * @USER_CLAIM_IGNORE: Don't notify user of key events.
+ * @USER_CLAIM_NOTIFY: Notify user of key events, but
+ * 	still automatically toggle the radio.
+ * @USER_CLAIM_SINGLE: Notify user of key events, and
+ * 	do not toggle the radio anymore.
+ */
+enum user_claim {
+	USER_CLAIM_IGNORE = 0,
+	USER_CLAIM_NOTIFY = 1,
+	USER_CLAIM_SINGLE = 2,
+	USER_CLAIM_MAX = 3,
+};
+
+/**
+ * struct rfkill - rfkill button control structure.
+ * @dev_name: Name of the interface. This will become the name
+ * 	of the input device which will be created for this button.
+ * @dev: Pointer to the device structure to which this button belongs to.
+ * @data: Pointer to the RF button drivers private data which will be
+ * 	passed along with the radio and polling handlers.
+ * @poll(void *data): Optional handler which will frequently be
+ * 	called to determine the current status of the RF button.
+ * @enable_radio(void *data): Optional handler to enable the radio
+ * 	once the RF button has been pressed and the hardware does enable
+ * 	the radio automaticly.
+ * @disable_radio(void *data): Optional handler to disable the radio
+ * 	once the RF button has been pressed and the hardware does disable
+ * 	the radio automaticly.
+ * @key_type: Radio type which the button controls, the value stored
+ * 	here should be a value from enum key_type.
+ * @key: Internal pointer that should not be touched by key driver.
+ *
+ * This structure can be used by a key driver to register the key
+ * to the rfkill driver in order to take control of the reporting
+ * to userspace or handling of radio status.
+ */
+struct rfkill {
+	char *dev_name;
+
+	struct device *dev;
+
+	void *data;
+	int (*poll)(void *data);
+	void (*enable_radio)(void *data);
+	void (*disable_radio)(void *data);
+
+	unsigned int key_type;
+
+	struct rfkill_key *key;
+};
+
+int rfkill_register_key(struct rfkill *rfkill, int init_status);
+int rfkill_deregister_key(struct rfkill *rfkill);
+void rfkill_report_event(struct rfkill *rfkill, int new_status);
+
+#endif /* RFKILL_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
                   ` (4 preceding siblings ...)
  2006-12-05 10:32 ` Christoph Hellwig
@ 2007-01-31  3:40 ` Stephen Hemminger
  2007-01-31 10:39   ` Ivo van Doorn
  2007-01-31 11:20   ` Ivo van Doorn
  5 siblings, 2 replies; 42+ messages in thread
From: Stephen Hemminger @ 2007-01-31  3:40 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hope you will be resubmitting this.

> +/*
> + * rfkill key structure.
> + */
> +struct rfkill_key {
> +	/*
> +	 * For sysfs representation.
> +	 */
> +	struct class_device *cdev;
> +
> +	/*
> +	 * Pointer to rfkill structure
> +	 * that was filled in by key driver.
> +	 */
> +	struct rfkill *rfkill;

Since rfkill is basically a function pointer table,
can it be made const?


> +	/*
> +	 * Pointer to type structure that this key belongs to.
> +	 */
> +	struct rfkill_type *type;
> +
> +	/*
> +	 * Once key status change has been detected, the toggled
> +	 * field should be set to indicate a notification to
> +	 * user or driver should be performed.
> +	 */
> +	int toggled;
> +
> +	/*
> +	 * Current state of the device radio, this state will
> +	 * change after the radio has actually been toggled since
> +	 * receiving the radio key event.
> +	 */
> +	int radio_status;
> +
> +	/*
> +	 * Current status of the key which controls the radio,
> +	 * this value will change after the key state has changed
> +	 * after polling, or the key driver has send the new state
> +	 * manually.
> +	 */
> +	int key_status;


Maybe turn these bits into a bit values (set_bit/clear_bit) in an unsigned long.

> +	/*
> +	 * Input device for this key,
> +	 * we also keep track of the number of
> +	 * times this input device is open. This
> +	 * is important for determining to whom we
> +	 * should report key events.
> +	 */
> +	struct input_dev *input;
> +	unsigned int open_count;

atomic on open_count?

> +	/*
> +	 * Key index number.
> +	 */
> +	unsigned int key_index;
> +
> +	/*
> +	 * List head structure to be used
> +	 * to add this structure to the list.
> +	 */
> +	struct list_head entry;
> +};
> +
> +/*
> + * rfkill key type structure.
> + */
> +struct rfkill_type {
> +	/*
> +	 * For sysfs representation.
> +	 */
> +	struct class_device *cdev;
> +
> +	/*
> +	 * Name of this radio type.
> +	 */
> +	char *name;

const?

> +	/*
> +	 * Key type identification. Value must be any
> +	 * in the key_type enum.
> +	 */
> +	unsigned int key_type;
> +
> +	/*
> +	 * Number of registered keys of this type.
> +	 */
> +	unsigned int key_count;
> +};
> +
> +/*
> + * rfkill master structure.
> + */
> +struct rfkill_master {
> +	/*
> +	 * For sysfs representation.
> +	 */
> +	struct class *class;
> +
> +	/*
> +	 * All access to the master structure
> +	 * and its children (the keys) are protected
> +	 * by this key lock.
> +	 */
> +	struct semaphore key_sem;

mutex instead of semaphort

> +	/*
> +	 * List of available key types.
> +	 */
> +	struct rfkill_type type[KEY_TYPE_MAX];
> +
> +	/*
> +	 * Total number of registered keys.
> +	 */
> +	unsigned int key_count;
> +
> +	/*
> +	 * Number of keys that require polling
> +	 */
> +	unsigned int poll_required;
> +
> +	/*
> +	 * List of rfkill_key structures.
> +	 */
> +	struct list_head key_list;
> +
> +	/*
> +	 * Work structures for periodic polling,
> +	 * as well as the scheduled radio toggling.
> +	 */
> +	struct work_struct toggle_work;
> +	struct work_struct poll_work;

delayed_rearming_work instead?

> +};

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-01-31  3:40 ` Stephen Hemminger
@ 2007-01-31 10:39   ` Ivo van Doorn
  2007-01-31 11:20   ` Ivo van Doorn
  1 sibling, 0 replies; 42+ messages in thread
From: Ivo van Doorn @ 2007-01-31 10:39 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> > +	/*
> > +	 * Pointer to rfkill structure
> > +	 * that was filled in by key driver.
> > +	 */
> > +	struct rfkill *rfkill;
> 
> Since rfkill is basically a function pointer table,
> can it be made const?

Sounds good to me.

> > +	/*
> > +	 * Once key status change has been detected, the toggled
> > +	 * field should be set to indicate a notification to
> > +	 * user or driver should be performed.
> > +	 */
> > +	int toggled;
> > +
> > +	/*
> > +	 * Current state of the device radio, this state will
> > +	 * change after the radio has actually been toggled since
> > +	 * receiving the radio key event.
> > +	 */
> > +	int radio_status;
> > +
> > +	/*
> > +	 * Current status of the key which controls the radio,
> > +	 * this value will change after the key state has changed
> > +	 * after polling, or the key driver has send the new state
> > +	 * manually.
> > +	 */
> > +	int key_status;
> 
> 
> Maybe turn these bits into a bit values (set_bit/clear_bit) in an unsigned long.

Will do.

> > +	/*
> > +	 * Input device for this key,
> > +	 * we also keep track of the number of
> > +	 * times this input device is open. This
> > +	 * is important for determining to whom we
> > +	 * should report key events.
> > +	 */
> > +	struct input_dev *input;
> > +	unsigned int open_count;
> 
> atomic on open_count?

There seems to have gone something wrong with the patch,
latest version should have had this field removed.

> > +	/*
> > +	 * Name of this radio type.
> > +	 */
> > +	char *name;
> 
> const?

Will do.

> > +	/*
> > +	 * All access to the master structure
> > +	 * and its children (the keys) are protected
> > +	 * by this key lock.
> > +	 */
> > +	struct semaphore key_sem;
> 
> mutex instead of semaphort

Strange, this should have already be fixed. :S

> > +	/*
> > +	 * Work structures for periodic polling,
> > +	 * as well as the scheduled radio toggling.
> > +	 */
> > +	struct work_struct toggle_work;
> > +	struct work_struct poll_work;
> 
> delayed_rearming_work instead?

Same here, rfkill should already have the new workqueue api...

I'll resubmit this within a few moments.

Thanks

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-01-31  3:40 ` Stephen Hemminger
  2007-01-31 10:39   ` Ivo van Doorn
@ 2007-01-31 11:20   ` Ivo van Doorn
  2007-03-30  5:27     ` Dmitry Torokhov
  1 sibling, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2007-01-31 11:20 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Dmitry Torokhov, linux-kernel, netdev, John Linville, Jiri Benc,
	Lennart Poettering, Johannes Berg, Larry Finger

> Hope you will be resubmitting this.

And here is the new version,
I didn't make the name const as requested
that field is being passed to the class_device_create
method which requires a char* argument.

But I have made the flag field.
And now this time the patch actually includes the
changes I promised in last mail.
(mutex, workqueue api, etc)

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>

----

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ba0e88c..6986d59 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -79,4 +79,19 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config RFKILL
+	tristate "RF button support"
+	help
+	  If you say yes here, the rfkill driver will be build
+	  which allowed network devices to register their hardware
+	  RF button which controls the radio state. This driver
+	  will then create an input device for it.
+
+	  When the input device is not used, the rfkill driver
+	  will make sure that when the RF button is pressed the radio
+	  is enabled or disabled accordingly. When the input device
+	  has been opened by the user this radio control will be left
+	  to the user, and rfkill will only send the RF button status
+	  change to userspace.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415c491..e788a1b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
 obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wistron_btns.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_RFKILL)			+= rfkill.o
diff --git a/drivers/input/misc/rfkill.c b/drivers/input/misc/rfkill.c
new file mode 100644
index 0000000..d8fda73
--- /dev/null
+++ b/drivers/input/misc/rfkill.c
@@ -0,0 +1,999 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF key support");
+MODULE_LICENSE("GPL");
+
+/*
+ * rfkill key structure.
+ */
+struct rfkill_key {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * Pointer to rfkill structure
+	 * that was filled in by key driver.
+	 */
+	const struct rfkill *rfkill;
+
+	/*
+	 * Pointer to type structure
+	 * that this key belongs to.
+	 */
+	struct rfkill_type *type;
+
+	/*
+	 * Current status of the key which controls the radio,
+	 * this value will change after the key state has changed
+	 * after polling, or the key driver has send the new state
+	 * manually.
+	 */
+#define RFKILL_KEY_RADIO_STATUS	1
+
+	unsigned long flags;
+
+	/*
+	 * Input device for this key.
+	 */
+	struct input_dev *input;
+
+	/*
+	 * List head structure to be used
+	 * to add this structure to the list.
+	 */
+	struct list_head entry;
+};
+
+/*
+ * rfkill type structure.
+ */
+struct rfkill_type {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class_device *cdev;
+
+	/*
+	 * All access to the type structure and its
+	 * children (the keys) are protected by this mutex.
+	 */
+	struct mutex mutex;
+
+	/*
+	 * Name of this radio type.
+	 */
+	char *name;
+
+	/*
+	 * Key type identification. Value must be any
+	 * in the key_type enum.
+	 */
+	unsigned int key_type;
+
+	/*
+	 * List of rfkill_key structures.
+	 */
+	struct list_head key_list;
+
+	/*
+	 * Number of registered keys of this type.
+	 */
+	unsigned int key_count;
+
+	/*
+	 * The claim the user has over the toggling of the radio,
+	 * this can be any value of the user_claim enumeration.
+	 */
+	unsigned int user_claim;
+
+	/*
+	 * Number of keys that require polling
+	 */
+	unsigned int poll_required;
+
+	/*
+	 * Once key status change has been detected, the toggled
+	 * field should be set to indicate a notification to
+	 * user or driver should be performed. This is stored
+	 * globally since all keys within this type need to be
+	 * toggled.
+	 */
+#define RFKILL_TYPE_TOGGLED		1
+
+	/*
+	 * Current state of the radios in this type.
+	 */
+#define RFKILL_TYPE_RADIO_STATUS	2
+
+	unsigned long flags;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	struct work_struct toggle_work;
+	struct delayed_work poll_work;
+};
+
+/*
+ * rfkill master structure.
+ */
+struct rfkill_master {
+	/*
+	 * For sysfs representation.
+	 */
+	struct class *class;
+
+	/*
+	 * Reference to this modules structure.
+	 */
+	struct module *owner;
+
+	/*
+	 * List of available key types.
+	 */
+	struct rfkill_type* type[KEY_TYPE_MAX];
+};
+
+/*
+ * We only need 1 single master interface,
+ * make this a global variable since we always need it.
+ */
+static struct rfkill_master *master;
+
+/*
+ * Send toggle event to key driver.
+ */
+static void rfkill_toggle_radio_driver(struct rfkill_key *key)
+{
+	const struct rfkill *rfkill = key->rfkill;
+	struct rfkill_type *type = key->type;
+
+	/*
+	 * Check what the current radio status is, and perform
+	 * the correct action to toggle the radio. This will make
+	 * sure the correct event happens when either the key driver
+	 * of the user has requested to toggle this radio.
+	 */
+	if (!test_bit(RFKILL_TYPE_RADIO_STATUS, &type->flags))
+		rfkill->enable_radio(rfkill->data);
+	else
+		rfkill->disable_radio(rfkill->data);
+}
+
+/*
+ * Send toggle event to userspace through the input device.
+ */
+static void rfkill_toggle_radio_input(struct rfkill_key *key)
+{
+	input_report_key(key->input, KEY_RFKILL,
+		!test_bit(RFKILL_TYPE_RADIO_STATUS, &key->type->flags));
+	input_sync(key->input);
+}
+
+/*
+ * Loop through the list of registered keys and toggle each
+ * key if this type has been toggled (by either user or driver).
+ */
+static void rfkill_toggle_radio(struct work_struct *work)
+{
+	struct rfkill_type *type =
+		container_of(work, struct rfkill_type, toggle_work);
+	struct rfkill_key *key;
+
+	mutex_lock(&type->mutex);
+
+	/*
+	 * If this type hasn't been toggled by any of the keys
+	 * or the user we can skip the toggle run.
+	 */
+	if (!test_bit(RFKILL_TYPE_TOGGLED, &type->flags)) {
+		mutex_unlock(&type->mutex);
+		return;
+	}
+
+	list_for_each_entry(key, &type->key_list, entry) {
+		/*
+		 * Check what kind of claim the user has requested
+		 * on this key. This determined if we should send
+		 * a user and/or a driver notification.
+		 */
+		if (type->user_claim != USER_CLAIM_IGNORE)
+			rfkill_toggle_radio_input(key);
+		else if (type->user_claim != USER_CLAIM_SINGLE)
+			rfkill_toggle_radio_driver(key);
+	}
+
+	/*
+	 * Reset the radio_status and toggled field,
+	 * to respresent the correct status to the user,
+	 * and allow the status to be toggled again.
+	 */
+	change_bit(RFKILL_TYPE_RADIO_STATUS, &type->flags);
+	clear_bit(RFKILL_TYPE_TOGGLED, &type->flags);
+
+	mutex_unlock(&type->mutex);
+}
+
+/*
+ * Check the new status of the key, and determine if the key has been toggled.
+ * This function can be called upon request by the device driver or userspace.
+ */
+static int rfkill_check_status(struct rfkill_key *key, int status)
+{
+	struct rfkill_type *type = key->type;
+
+	/*
+	 * A key should be toggled if the current radio status does not
+	 * match the requested status. This check is required instead of
+	 * comparing it to the current key status since this function can
+	 * be called on request of the user as well. The user is able to
+	 * request a radio toggle to make sure the radio status will match
+	 * the key status if the new key status has been reported to
+	 * userspace only.
+	 * As a safety measure, we won't toggle a key twice.
+	 */
+	if (test_bit(RFKILL_TYPE_RADIO_STATUS, &type->flags) != !!status &&
+	    !test_bit(RFKILL_TYPE_TOGGLED, &type->flags)) {
+		set_bit(RFKILL_TYPE_TOGGLED, &type->flags);
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Wrapper around the rfkill_check_status function, this may only
+ * be called from the device driver status events. Besides calling
+ * rfkill_check_status it will also update the key_status field.
+ */
+static int rfkill_check_key(struct rfkill_key *key, int status)
+{
+	/*
+	 * Store the new key status, and only if the 2
+	 * are different the new status status should be
+	 * passed on to rfkill_check_status to determine
+	 * if any action should be performed.
+	 */
+	if (test_bit(RFKILL_KEY_RADIO_STATUS, &key->flags) == !!status)
+		return 0;
+
+	change_bit(RFKILL_KEY_RADIO_STATUS, &key->flags);
+	return rfkill_check_status(key, status);
+}
+
+/*
+ * Handler that is run frequently to determine the current status
+ * of all registered keys that require polling.
+ */
+static void rfkill_list_poll(struct work_struct *work)
+{
+	struct rfkill_type *type =
+		container_of(work, struct rfkill_type, poll_work.work);
+	const struct rfkill *rfkill;
+	struct rfkill_key *key;
+	int status = 0;
+
+	mutex_lock(&type->mutex);
+
+	list_for_each_entry(key, &type->key_list, entry) {
+		rfkill = key->rfkill;
+
+		/*
+		 * If the poll handler has not been given
+		 * the key driver should report events,
+		 * so we can ignore this key now.
+		 */
+		if (!rfkill->poll)
+			continue;
+
+		/*
+		 * Poll radio state and check if a radio toggle
+		 * has been requested by the key.
+		 */
+		if (rfkill_check_key(key, rfkill->poll(rfkill->data)))
+			status = 1;
+	}
+
+	mutex_unlock(&type->mutex);
+
+	/*
+	 * A radio toggle has been requested, schedule the toggle work thread.
+	 */
+	if (status)
+		schedule_work(&type->toggle_work);
+
+	/*
+	 * Check if we need to rearm ourselves.
+	 */
+	if (type->poll_required)
+		schedule_delayed_work(&type->poll_work, RFKILL_POLL_DELAY);
+}
+
+/**
+ * rfkill_report_event - Report change in key status to rfkill handler.
+ * @rfkill: rfkill structure registered by key driver
+ * @new_status: new key status
+ *
+ * This function should be called by the key driver if it has not provided
+ * a poll handler with rfkill. As soon as the key driver has determined
+ * the status of the key has changed it should report the new status
+ * through this function.
+ */
+void rfkill_report_event(struct rfkill *rfkill, int new_status)
+{
+	struct rfkill_type *type = rfkill->key->type;
+
+	mutex_lock(&type->mutex);
+
+	if (rfkill_check_key(rfkill->key, new_status))
+		schedule_work(&type->toggle_work);
+
+	mutex_unlock(&type->mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_report_event);
+
+/*
+ * Sysfs release functions.
+ */
+static void rfkill_class_device_release(struct class_device *cdev)
+{
+	kfree(class_get_devdata(cdev));
+}
+
+static void rfkill_class_release(struct class *class)
+{
+	kfree(master);
+	master = NULL;
+}
+
+/*
+ * rfkill key attribute to display the current key status.
+ */
+static ssize_t rfkill_key_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_key *key = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d",
+		test_bit(RFKILL_KEY_RADIO_STATUS, &key->flags));
+}
+
+static struct class_device_attribute cdev_attr_key_status =
+	__ATTR(status, S_IRUGO, rfkill_key_status_show, NULL);
+
+/*
+ * rfkill type attribute to display the current radio status.
+ */
+static ssize_t rfkill_type_status_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d",
+		test_bit(RFKILL_TYPE_RADIO_STATUS, &type->flags));
+}
+
+static struct class_device_attribute cdev_attr_type_status =
+	__ATTR(status, S_IRUGO, rfkill_type_status_show, NULL);
+
+/*
+ * rfkill type attribute to display/change the user claim
+ */
+static ssize_t rfkill_type_claim_show(struct class_device *cdev, char *buf)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	return sprintf(buf, "%d", type->user_claim);
+}
+
+static ssize_t rfkill_type_claim_store(struct class_device *cdev,
+	const char * buf, size_t count)
+{
+	struct rfkill_type *type = class_get_devdata(cdev);
+	unsigned int claim = simple_strtoul(buf, NULL, 0);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (claim >= USER_CLAIM_MAX)
+		return -EINVAL;
+
+	mutex_lock(&type->mutex);
+
+	type->user_claim = claim;
+
+	mutex_unlock(&type->mutex);
+
+	return count;
+}
+
+static struct class_device_attribute cdev_attr_type_claim =
+	__ATTR(claim, S_IRUGO | S_IWUSR,
+		rfkill_type_claim_show, rfkill_type_claim_store);
+
+/*
+ * rfkill key sysfs creation/destruction.
+ */
+static int rfkill_key_sysfs_create(struct rfkill_key *key)
+{
+	int status;
+
+	key->cdev = class_device_create(master->class,
+					key->type->cdev,
+					key->rfkill->dev->devt,
+					key->rfkill->dev,
+					key->rfkill->dev_name);
+	if (unlikely(!key->cdev || IS_ERR(key->cdev)))
+		return PTR_ERR(key->cdev);
+
+	/*
+	 * Make sure this key can be freed when the class_device is freed.
+	 */
+	class_set_devdata(key->cdev, key);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(key->cdev, &cdev_attr_key_status);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * The key belongs to the device structure the key driverdevt
+	 * has given us a reference to. But besides that device
+	 * this key also belongs to the input device that has been
+	 * created. We should create the link to that entry manually.
+	 */
+	status = sysfs_create_link(&key->cdev->kobj, &key->input->cdev.kobj,
+				   "idev");
+	if (unlikely(status))
+		goto exit_file;
+
+	return 0;
+
+exit_file:
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+
+exit:
+	class_device_destroy(master->class, key->rfkill->dev->devt);
+	class_set_devdata (key->cdev, NULL);
+
+	return status;
+}
+
+static void rfkill_key_sysfs_release(struct rfkill_key *key)
+{
+	sysfs_remove_link(&key->cdev->kobj, "idev");
+	class_device_remove_file(key->cdev, &cdev_attr_key_status);
+	class_device_destroy(master->class, key->rfkill->dev->devt);
+}
+
+/*
+ * rfkill type sysfs creation/destruction.
+ */
+static int rfkill_type_sysfs_create(struct rfkill_type *type)
+{
+	int status;
+
+	type->cdev = class_device_create(master->class, NULL, type->key_type,
+					 NULL, type->name);
+	if (unlikely(!type->cdev || IS_ERR(type->cdev)))
+		return PTR_ERR(type->cdev);
+
+	/*
+	 * Make sure this key can be freed when the class_device is freed.
+	 */
+	class_set_devdata(type->cdev, type);
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(type->cdev, &cdev_attr_type_claim);
+	if (unlikely(status))
+		goto exit;
+
+	/*
+	 * Create the sysfs files.
+	 */
+	status = class_device_create_file(type->cdev, &cdev_attr_type_status);
+	if (unlikely(status))
+		goto exit_claim;
+
+	return 0;
+
+exit_claim:
+	class_device_remove_file(type->cdev, &cdev_attr_type_claim);
+
+exit:
+	class_device_destroy(type->cdev->class, type->key_type);
+	class_set_devdata (type->cdev, NULL);
+
+	return status;
+}
+
+static void rfkill_type_sysfs_release(struct rfkill_type *type)
+{
+	class_device_remove_file(type->cdev, &cdev_attr_type_status);
+	class_device_remove_file(type->cdev, &cdev_attr_type_claim);
+	class_device_destroy(type->cdev->class, type->key_type);
+}
+
+/*
+ * rfkill master sysfs creation/destruction.
+ */
+static int rfkill_master_sysfs_create(struct rfkill_master *master)
+{
+	master->class = class_create(master->owner, "rfkill");
+	if (unlikely(!master->class || IS_ERR(master->class)))
+		return PTR_ERR(master->class);
+
+	master->class->release = rfkill_class_device_release;
+	master->class->class_release = rfkill_class_release;
+
+	return 0;
+}
+
+static void rfkill_master_sysfs_release(struct rfkill_master *master)
+{
+	class_destroy(master->class);
+}
+
+/*
+ * Input device initialization/deinitialization handler.
+ */
+static struct input_dev* rfkill_register_input(struct rfkill_key *key)
+{
+	struct input_dev *input;
+	int status;
+	char *name;
+	char *phys;
+
+	input = input_allocate_device();
+	if (unlikely(!input))
+		return NULL;
+
+	/*
+	 * Link the private data to rfkill structure.
+	 */
+	input->private = key;
+
+	/*
+	 * Allocate the strings for the input device names.
+	 */
+	name = kasprintf(GFP_KERNEL, "rfkill %s",
+			 key->rfkill->dev_name);
+	phys = kasprintf(GFP_KERNEL, "rfkill/%s/%s",
+			 key->type->name, key->rfkill->dev_name);
+
+	if (!name || !phys)
+		goto exit;
+
+	input->name = name;
+	input->phys = phys;
+
+	/*
+	 * Initialize the input_id structure.
+	 */
+	input->id.bustype = BUS_HOST;
+	input->id.vendor = 0x0001;
+	input->id.product = 0x0001;
+	input->id.version = 0x0001;
+
+	input->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_RFKILL, input->keybit);
+
+	status = input_register_device(input);
+	if (status)
+		goto exit;
+
+	return input;
+
+exit:
+	kfree(name);
+	kfree(phys);
+
+	input_free_device(input);
+
+	return NULL;
+}
+
+static void rfkill_deregister_input(struct rfkill_key *key)
+{
+	const char *name;
+	const char *phys;
+
+	/*
+	 * The name and phys fields have been allocated
+	 * using kasprintf, this means they have to be
+	 * freed seperately.
+	 */
+	name = key->input->name;
+	phys = key->input->phys;
+
+	input_unregister_device(key->input);
+	kfree(name);
+	kfree(phys);
+	input_free_device(key->input);
+}
+
+/*
+ * Rfkill key initialization/deinitialization.
+ * These methods should only be called with the mutex held.
+ */
+static int rfkill_key_init(struct rfkill *rfkill, struct rfkill_key *key,
+	int status)
+{
+	/*
+	 * Initialize all variables.
+	 */
+	key->rfkill = rfkill;
+	rfkill->key = key;
+	key->type = master->type[rfkill->key_type];
+	if (status)
+		set_bit(RFKILL_KEY_RADIO_STATUS, &key->flags);
+	else
+		clear_bit(RFKILL_KEY_RADIO_STATUS, &key->flags);
+	INIT_LIST_HEAD(&key->entry);
+
+	/*
+	 * Create input device.
+	 */
+	key->input = rfkill_register_input(key);
+	if (!key->input)
+		goto exit;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	if (rfkill_key_sysfs_create(key))
+		goto exit_input;
+
+	return 0;
+
+exit_input:
+	rfkill_deregister_input(key);
+
+exit:
+	rfkill->key = NULL;
+
+	return -ENOMEM;
+}
+
+static void rfkill_key_deinit(struct rfkill_key *key)
+{
+	if (key) {
+		rfkill_key_sysfs_release(key);
+		rfkill_deregister_input(key);
+	}
+}
+
+/*
+ * Rfkill type handler to check if the sysfs entries should
+ * be created or removed based on the number of registered keys.
+ */
+static void rfkill_add_type_key(struct rfkill_type *type,
+	struct rfkill_key *key)
+{
+	/*
+	 * Add key to the list.
+	 */
+	list_add(&key->entry, &type->key_list);
+	type->key_count++;
+}
+
+static void rfkill_del_type_key(struct rfkill_type *type,
+	struct rfkill_key *key)
+{
+	/*
+	 * Delete the rfkill structure from the list.
+	 */
+	list_del(&key->entry);
+	type->key_count--;
+}
+
+/**
+ * rfkill_register_key - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ * @init_status: initial status of the key at the time this function is called
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be registered. Immediately from registration the key driver
+ * should be able to receive calls through the poll, enable_radio and
+ * disable_radio handlers if those were registered.
+ */
+int rfkill_register_key(struct rfkill *rfkill, int init_status)
+{
+	struct rfkill_type *type;
+	struct rfkill_key *key;
+	int status;
+
+	if (!rfkill || !rfkill->enable_radio || !rfkill->disable_radio)
+		return -EINVAL;
+
+	/*
+	 * Check if the requested key_type was valid.
+	 */
+	if (rfkill->key_type >= KEY_TYPE_MAX)
+		return -EINVAL;
+
+	type = master->type[rfkill->key_type];
+
+	/*
+	 * Increase module use count to prevent this
+	 * module to be unloaded while there are still
+	 * registered keys.
+	 */
+	if (!try_module_get(master->owner))
+		return -EBUSY;
+
+	mutex_lock(&type->mutex);
+
+	key = kzalloc(sizeof(struct rfkill_key), GFP_KERNEL);
+	if (unlikely(!key)) {
+		status = -ENOMEM;
+		goto exit;
+	}
+
+	/*
+	 * Initialize key, and add it to the type structure.
+	 */
+	key->type = type;
+	status = rfkill_key_init(rfkill, key, init_status);
+	if (status)
+		goto exit_key;
+
+	/*
+	 * Add key to the specified type.
+	 */
+	rfkill_add_type_key(type, key);
+
+	/*
+	 * Check if we need polling, and if we do
+	 * increase the poll required counter and check
+	 * if we weren't polling yet.
+	 */
+	if (rfkill->poll && !type->poll_required++)
+		schedule_delayed_work(&type->poll_work, RFKILL_POLL_DELAY);
+
+	mutex_unlock(&type->mutex);
+
+	return 0;
+
+exit_key:
+	kfree(key);
+
+exit:
+	mutex_unlock(&type->mutex);
+	module_put(master->owner);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(rfkill_register_key);
+
+/**
+ * rfkill_deregister_key - Deregister a previously registered rfkill structure.
+ * @rfkill: rfkill structure to be deregistered
+ *
+ * This function should be called by the key driver when the rfkill structure
+ * needs to be deregistered. This function may only be called if it was
+ * previously registered with rfkill_register_key.
+ */
+int rfkill_deregister_key(struct rfkill *rfkill)
+{
+	struct rfkill_type *type;
+
+	if (!rfkill || !rfkill->key)
+		return -EINVAL;
+
+	type = rfkill->key->type;
+
+	mutex_lock(&type->mutex);
+
+	/*
+	 * Cancel delayed work if this is the last key
+	 * that requires polling. It is not bad if the
+	 * workqueue is still running, the workqueue
+	 * will not rearm itself since the poll_required
+	 * variable has been cleared, and we have
+	 * protected the list with a mutex.
+	 */
+	if (rfkill->poll && !--type->poll_required)
+		cancel_delayed_work(&type->poll_work);
+
+	/*
+	 * Deinitialize key, and remove it from the type.
+	 */
+	rfkill_del_type_key(type, rfkill->key);
+	rfkill_key_deinit(rfkill->key);
+
+	mutex_unlock(&type->mutex);
+
+	/*
+	 * rfkill entry has been removed,
+	 * decrease module use count.
+	 */
+	module_put(master->owner);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rfkill_deregister_key);
+
+/*
+ * Rfkill type initialization/deinitialization.
+ */
+static int rfkill_type_init(unsigned int key_type, char *name)
+{
+	struct rfkill_type *type;
+	int status;
+
+	type = kzalloc(sizeof(struct rfkill_type), GFP_KERNEL);
+	if (unlikely(!type))
+		return -ENOMEM;
+
+	/*
+	 * Initialize all variables.
+	 */
+	mutex_init(&type->mutex);
+	type->name = name;
+	type->key_type = key_type;
+	INIT_LIST_HEAD(&type->key_list);
+	clear_bit(RFKILL_TYPE_TOGGLED, &type->flags);
+	type->key_count = 0;
+	type->poll_required = 0;
+
+	/*
+	 * Work structures for periodic polling,
+	 * as well as the scheduled radio toggling.
+	 */
+	INIT_WORK(&type->toggle_work, &rfkill_toggle_radio);
+	INIT_DELAYED_WORK(&type->poll_work, &rfkill_list_poll);
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_type_sysfs_create(type);
+	if (status) {
+		printk(KERN_ERR "Failed to create sysfs entry %s", name);
+		goto exit;
+	}
+
+	master->type[key_type] = type;
+
+	return 0;
+
+exit:
+	kfree(type);
+
+	return status;
+}
+
+static void rfkill_type_deinit(unsigned int key_type)
+{
+	rfkill_type_sysfs_release(master->type[key_type]);
+}
+
+/*
+ * Rfkill master initialization/deinitialization.
+ */
+static int rfkill_master_init(void)
+{
+	int status;
+
+	master = kzalloc(sizeof(struct rfkill_master), GFP_KERNEL);
+	if (unlikely(!master))
+		return -ENOMEM;
+
+	/*
+	 * Set the module owner reference.
+	 */
+	master->owner = THIS_MODULE;
+
+	/*
+	 * Create sysfs entry.
+	 */
+	status = rfkill_master_sysfs_create(master);
+	if (status) {
+		printk(KERN_ERR "Failed to create master sysfs entry");
+		goto exit;
+	}
+
+	/*
+	 * Create and initialize all type structures.
+	 */
+	status = rfkill_type_init(KEY_TYPE_WLAN, "wlan");
+	if (status)
+		goto exit_master;
+
+	status = rfkill_type_init(KEY_TYPE_BlUETOOTH, "bluetooth");
+	if (status)
+		goto exit_wlan;
+
+	status = rfkill_type_init(KEY_TYPE_IRDA, "irda");
+	if (status)
+		goto exit_bluetooth;
+
+	return 0;
+
+exit_bluetooth:
+	rfkill_type_deinit(KEY_TYPE_BlUETOOTH);
+
+exit_wlan:
+	rfkill_type_deinit(KEY_TYPE_WLAN);
+
+exit_master:
+	rfkill_master_sysfs_release(master);
+
+exit:
+	kfree(master);
+	master = NULL;
+
+	return status;
+}
+
+static void rfkill_master_deinit(void)
+{
+	if (master) {
+		rfkill_type_deinit(KEY_TYPE_IRDA);
+		rfkill_type_deinit(KEY_TYPE_BlUETOOTH);
+		rfkill_type_deinit(KEY_TYPE_WLAN);
+		rfkill_master_sysfs_release(master);
+	}
+}
+
+/*
+ * Module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	printk(KERN_INFO "Loading rfkill driver.\n");
+
+	return rfkill_master_init();
+}
+
+static void __exit rfkill_exit(void)
+{
+	rfkill_master_deinit();
+
+	printk(KERN_INFO "Unloading rfkill driver.\n");
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
new file mode 100644
index 0000000..4f45429
--- /dev/null
+++ b/include/linux/rfkill.h
@@ -0,0 +1,140 @@
+/*
+	Copyright (C) 2006 Ivo van Doorn
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the
+	Free Software Foundation, Inc.,
+	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+	RF button support
+	Laptops are quite often equipped with an RF key to enable or
+	disable the radio of the wireless device attached to that key.
+	This wireless device usually is an integrated wireless network device,
+	infrared or bluetooth device.
+	There are 3 catagories of radio control keys:
+	 A) The key directly toggles the hardware radio, and does not send an
+	    event to userspace.
+	 B) The key does not toggle the hardware radio, and does not send an
+	    event to userspace.
+	 C) The key does not toggle the hardware radio, but does send an event
+	    to userspace.
+	Catagory (A) should create an input device themselves and send an
+	KEY_RFKILL event over that input device.
+	Catagory (B) should register themselves with rkfkill and allow rfkill
+	to toggle the radio and report events to userspace.
+	Catagory (C) should register with rfkill, who will listen to userspace
+	requests to toggle the radio and will send the signal to the driver.
+
+	The rfkill driver will contain a list of all devices with an RF button,
+	and hardware drivers need to register their hardware to the rfkill
+	interface. Rfkill will then take care of everything. If the RF key
+	requires polling to obtain the status this will be handled by rfkill.
+	If the RF key does not require polling but sends for example interrupts,
+	the hardware driver can report the change of status to rfkill, without
+	having to do any other action.
+
+	For each registered hardware button an input device will be created.
+	If this input device has been opened by the user, rfkill will send a
+	signal to userspace instead of the hardware about the new button
+	status. This will allow userpace to perform the correct steps
+	in order to bring down all interfaces.
+
+	Through sysfs it is also possible the user requests the toggling of
+	the radio, this means that the radio could be toggled even without
+	pressing the radio key.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+#include <linux/device.h>
+
+/*
+ * The keys will be periodically checked if the
+ * key has been toggled. By default this will be 100ms.
+ */
+#define RFKILL_POLL_DELAY	( HZ / 10 )
+
+/**
+ * enum key_type - Key type for rfkill keys.
+ * KEY_TYPE_WLAN: key type for Wireless network devices.
+ * KEY_TYPE_BlUETOOTH: key type for bluetooth devices.
+ * KEY_TYPE_IRDA: key type for infrared devices.
+ */
+enum key_type {
+	KEY_TYPE_WLAN = 0,
+	KEY_TYPE_BlUETOOTH = 1,
+	KEY_TYPE_IRDA = 2,
+	KEY_TYPE_MAX = 3,
+};
+
+/**
+ * enum user_claim - Users' claim on rfkill key.
+ * @USER_CLAIM_IGNORE: Don't notify user of key events.
+ * @USER_CLAIM_NOTIFY: Notify user of key events, but
+ * 	still automatically toggle the radio.
+ * @USER_CLAIM_SINGLE: Notify user of key events, and
+ * 	do not toggle the radio anymore.
+ */
+enum user_claim {
+	USER_CLAIM_IGNORE = 0,
+	USER_CLAIM_NOTIFY = 1,
+	USER_CLAIM_SINGLE = 2,
+	USER_CLAIM_MAX = 3,
+};
+
+/**
+ * struct rfkill - rfkill button control structure.
+ * @dev_name: Name of the interface. This will become the name
+ * 	of the input device which will be created for this button.
+ * @dev: Pointer to the device structure to which this button belongs to.
+ * @data: Pointer to the RF button drivers private data which will be
+ * 	passed along with the radio and polling handlers.
+ * @poll(void *data): Optional handler which will frequently be
+ * 	called to determine the current status of the RF button.
+ * @enable_radio(void *data): Optional handler to enable the radio
+ * 	once the RF button has been pressed and the hardware does enable
+ * 	the radio automaticly.
+ * @disable_radio(void *data): Optional handler to disable the radio
+ * 	once the RF button has been pressed and the hardware does disable
+ * 	the radio automaticly.
+ * @key_type: Radio type which the button controls, the value stored
+ * 	here should be a value from enum key_type.
+ * @key: Internal pointer that should not be touched by key driver.
+ *
+ * This structure can be used by a key driver to register the key
+ * to the rfkill driver in order to take control of the reporting
+ * to userspace or handling of radio status.
+ */
+struct rfkill {
+	char *dev_name;
+
+	struct device *dev;
+
+	void *data;
+	int (*poll)(void *data);
+	void (*enable_radio)(void *data);
+	void (*disable_radio)(void *data);
+
+	unsigned int key_type;
+
+	struct rfkill_key *key;
+};
+
+int rfkill_register_key(struct rfkill *rfkill, int init_status);
+int rfkill_deregister_key(struct rfkill *rfkill);
+void rfkill_report_event(struct rfkill *rfkill, int new_status);
+
+#endif /* RFKILL_H */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-01-31 11:20   ` Ivo van Doorn
@ 2007-03-30  5:27     ` Dmitry Torokhov
  2007-03-30  5:29       ` Dmitry Torokhov
  2007-03-30 14:59       ` Ivo van Doorn
  0 siblings, 2 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2007-03-30  5:27 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

On Wednesday 31 January 2007 06:20, Ivo van Doorn wrote:
> > Hope you will be resubmitting this.
> 
> And here is the new version,
> I didn't make the name const as requested
> that field is being passed to the class_device_create
> method which requires a char* argument.
> 
> But I have made the flag field.
> And now this time the patch actually includes the
> changes I promised in last mail.
> (mutex, workqueue api, etc)
>

Hi Ivo,

I am very sorry for taking so much time to respond but finally I went
through the patch and I still have the same objection as before -
it mixes two logically (and often physically) separated objects into
a single entity. I think that RF switch and button should be separate
entities, created and destroyed independently of each other. This way
everything handled uniformly by the kernel.

I attempted to rework the rfkill core supprt and simplify it and
came up with the patch below. Network card drivers that are able
to control state of their RF transmitters allocate and register
rfkill structures. Every rfkill structure belongs to one of
RF classes (WLAN, Bluetooth or IRDA) and exports its name, type,
state and "user_claim" through sysfs.

There is also an input handler that catces KEY_WLAN and KEY_BLUETOOTH
events passing through input system and toggles state of all RF
switches of appropriate type registered with rfkill system (unless
a switch was claimed by userspace in which case it is left alone).
I think this provides basic functionality that most of the users need
and any advanced control will have to be done from userspace.

In a followup patch there is a skeleton code for creating polled
input devices. For cards that have button physically connected
their drivers will have to register a separate input device and
let either input handler or userspace application take care of
switching RF state appropriately.

My only concern is where rfkill code should live. Since it is no
longer dependent on input core (embedded systems might disable
rfkill-input and use bare rfkill and control state from userspace)
it does not need to live in drivers/input.

Please let me know what you think.

-- 
Dmitry

From: Ivo van Doorn <ivdoorn@gmail.com>

Input: rfkill - Add support for input key to control wireless radio

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/misc/Kconfig        |   26 ++
 drivers/input/misc/Makefile       |    2 
 drivers/input/misc/rfkill-input.c |  169 ++++++++++++++++
 drivers/input/misc/rfkill.c       |  400 ++++++++++++++++++++++++++++++++++++++
 include/linux/rfkill.h            |   85 ++++++++
 5 files changed, 682 insertions(+)

Index: work/drivers/input/misc/Kconfig
===================================================================
--- work.orig/drivers/input/misc/Kconfig
+++ work/drivers/input/misc/Kconfig
@@ -98,4 +98,30 @@ config HP_SDC_RTC
 	  Say Y here if you want to support the built-in real time clock
 	  of the HP SDC controller.
 
+config RFKILL
+	tristate "RF switch support"
+	help
+	  Say Y here if you want to have control over RF switches
+	  found on many WiFi, Bluetooth and IRDA cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rfkill.
+
+if RFKILL
+
+config RFKILL_INPUT
+	tristate "Input layer to RF switch bridge"
+	depends on INPUT
+	help
+	  Say Y here if you want kernel automatically toggle state
+	  of RF switches on and off when user presses appropriate
+	  button an a key on the keyboard. Without this module
+	  you need a some kind of userspace application to control
+	  state of the switches.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rfkill-input.
+
+endif
+
 endif
Index: work/drivers/input/misc/Makefile
===================================================================
--- work.orig/drivers/input/misc/Makefile
+++ work/drivers/input/misc/Makefile
@@ -13,3 +13,5 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS)	+= wist
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
+obj-$(CONFIG_RFKILL)			+= rfkill.o
+obj-$(CONFIG_RFKILL_INPUT)		+= rfkill-input.o
Index: work/drivers/input/misc/rfkill.c
===================================================================
--- /dev/null
+++ work/drivers/input/misc/rfkill.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2006 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF switch support");
+MODULE_LICENSE("GPL");
+
+static LIST_HEAD(rfkill_list);	/* list of registered rf switches */
+static DEFINE_MUTEX(rfkill_mutex);
+
+static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX];
+
+static int rfkill_toggle_radio(struct rfkill *rfkill,
+				enum rfkill_state state)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&rfkill->mutex);
+	if (retval)
+		return retval;
+
+	if (state != rfkill->state) {
+		retval = rfkill->toggle_radio(rfkill->data, state);
+		if (!retval)
+			rfkill->state = state;
+	}
+
+	mutex_unlock(&rfkill->mutex);
+	return retval;
+}
+
+/**
+ * rfkill_switch_all - Toggle state of all switches of given type
+ * @type: type of interfaces to be affeceted
+ * @state: the new state
+ *
+ * This function toggles state of all switches of given type unless
+ * a specific switch is claimed by userspace in which case it is
+ * left alone.
+ */
+
+void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
+{
+	struct rfkill *rfkill;
+
+	mutex_lock(&rfkill_mutex);
+
+	rfkill_states[type] = state;
+
+	list_for_each_entry(rfkill, &rfkill_list, node) {
+		if (!rfkill->user_claim)
+			rfkill_toggle_radio(rfkill, state);
+	}
+
+	mutex_unlock(&rfkill_mutex);
+}
+EXPORT_SYMBOL(rfkill_switch_all);
+
+static ssize_t rfkill_name_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	return sprintf(buf, "%s\n", rfkill->name);
+}
+
+static ssize_t rfkill_type_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+	const char *type;
+
+	switch (rfkill->type) {
+	case RFKILL_TYPE_WLAN:
+		type = "wlan";
+		break;
+	case RFKILL_TYPE_BLUETOOTH:
+		type = "bluetooth";
+		break;
+	case RFKILL_TYPE_IRDA:
+		type = "irda";
+		break;
+	default:
+		BUG();
+	}
+
+	return sprintf(buf, "%s\n", type);
+}
+
+static ssize_t rfkill_state_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	return sprintf(buf, "%d\n", rfkill->state);
+}
+
+static ssize_t rfkill_state_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+	unsigned int state = simple_strtoul(buf, NULL, 0);
+	int error;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	error = rfkill_toggle_radio(rfkill,
+			state ? RFKILL_STATE_ON : RFKILL_STATE_OFF);
+	if (error)
+		return error;
+
+	return count;
+}
+
+static ssize_t rfkill_claim_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	return sprintf(buf, "%d", rfkill->user_claim);
+}
+
+static ssize_t rfkill_claim_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+	bool claim = !!simple_strtoul(buf, NULL, 0);
+	int error;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	/*
+	 * Take the global lock to make sure the kernel is not in
+	 * the middle of rfkill_toggle_all
+	 */
+	error = mutex_lock_interruptible(&rfkill_mutex);
+	if (error)
+		return error;
+
+	if (rfkill->user_claim != claim) {
+		if (!claim)
+			rfkill_toggle_radio(rfkill,
+					    rfkill_states[rfkill->type]);
+		rfkill->user_claim = claim;
+	}
+
+	mutex_unlock(&rfkill_mutex);
+
+	return count;
+}
+
+static struct device_attribute rfkill_dev_attrs[] = {
+	__ATTR(name, S_IRUGO, rfkill_name_show, NULL),
+	__ATTR(type, S_IRUGO, rfkill_type_show, NULL),
+	__ATTR(state, S_IRUGO, rfkill_state_show, rfkill_state_store),
+	__ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store),
+	__ATTR_NULL
+};
+
+static void rfkill_release(struct device *dev)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	kfree(rfkill);
+	module_put(THIS_MODULE);
+}
+
+static int rfkill_suspend(struct device *dev, pm_message_t state)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	if (dev->power.power_state.event != state.event) {
+		if (state.event == PM_EVENT_SUSPEND) {
+			mutex_lock(&rfkill->mutex);
+
+			if (rfkill->state == RFKILL_STATE_ON)
+				rfkill->toggle_radio(rfkill->data,
+						     RFKILL_STATE_OFF);
+
+			mutex_unlock(&rfkill->mutex);
+		}
+
+		dev->power.power_state = state;
+	}
+
+	return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	if (dev->power.power_state.event != PM_EVENT_ON) {
+		mutex_lock(&rfkill->mutex);
+
+		if (rfkill->state == RFKILL_STATE_ON)
+			rfkill->toggle_radio(rfkill->data, RFKILL_STATE_ON);
+
+		mutex_unlock(&rfkill->mutex);
+	}
+
+	dev->power.power_state = PMSG_ON;
+	return 0;
+}
+
+static struct class rfkill_class = {
+	.name		= "rfkill",
+	.dev_release	= rfkill_release,
+	.dev_attrs	= rfkill_dev_attrs,
+	.suspend	= rfkill_suspend,
+	.resume		= rfkill_resume,
+};
+
+static int rfkill_add_switch(struct rfkill *rfkill)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&rfkill_mutex);
+	if (retval)
+		return retval;
+
+	retval = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type]);
+	if (retval)
+		goto out;
+
+	list_add_tail(&rfkill->node, &rfkill_list);
+
+ out:
+	mutex_unlock(&rfkill_mutex);
+	return retval;
+}
+
+static void rfkill_remove_switch(struct rfkill *rfkill)
+{
+	mutex_lock(&rfkill_mutex);
+	list_del_init(&rfkill->node);
+	rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF);
+	mutex_unlock(&rfkill_mutex);
+}
+
+/**
+ * rfkill_allocate - allocate memory for rfkill structure.
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (wlan, bluetooth, irda)
+ *
+ * This function should be called by the network driver when it needs
+ * rfkill structure. Once the structure is allocated the driver shoud
+ * finish its initialization by setting name, private data, enable_radio
+ * and disable_radio methods and then register it with rfkill_register().
+ * NOTE: If registration fails the structure shoudl be freed by calling
+ * rfkill_free() otherwise rfkill_unregister() should be used.
+ */
+struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type)
+{
+	struct rfkill *rfkill;
+	struct device *dev;
+
+	rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL);
+	if (rfkill)
+		return NULL;
+
+	mutex_init(&rfkill->mutex);
+	INIT_LIST_HEAD(&rfkill->node);
+	rfkill->type = type;
+
+	dev = &rfkill->dev;
+	dev->class = &rfkill_class;
+	dev->parent = parent;
+	device_initialize(dev);
+
+	return rfkill;
+}
+EXPORT_SYMBOL(rfkill_allocate);
+
+/**
+ * rfkill_free - Mark rfkill structure for deletion
+ * @rfkill: rfkill structure to be destroyed
+ *
+ * Decrements reference count of rfkill structure so it is destoryed.
+ * Note that rfkill_free() should _not_ be called after rfkill_unregister().
+ */
+void rfkill_free(struct rfkill *rfkill)
+{
+	if (rfkill)
+		put_device(&rfkill->dev);
+}
+EXPORT_SYMBOL(rfkill_free);
+
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the network driver when the rfkill
+ * structure needs to be registered. Immediately from registration the
+ * switch driver should be able to service calls to toggle_radio.
+ */
+int rfkill_register(struct rfkill *rfkill)
+{
+	static atomic_t rfkill_no = ATOMIC_INIT(0);
+	struct device *dev = &rfkill->dev;
+	int error;
+
+	if (!rfkill->toggle_radio)
+		return -EINVAL;
+
+	error = rfkill_add_switch(rfkill);
+	if (error)
+		return error;
+
+	snprintf(dev->bus_id, sizeof(dev->bus_id),
+		 "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1);
+
+	error = device_add(dev);
+	if (error) {
+		rfkill_remove_switch(rfkill);
+		return error;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rfkill_register);
+
+/**
+ * rfkill_unregister - Uegister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Note that rfkill_free() should
+ * _not_ be called after rfkill_unregister().
+ */
+void rfkill_unregister(struct rfkill *rfkill)
+{
+	device_del(&rfkill->dev);
+	rfkill_remove_switch(rfkill);
+	put_device(&rfkill->dev);
+}
+EXPORT_SYMBOL(rfkill_unregister);
+
+/*
+ * Rfkill module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	int error;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rfkill_states); i++)
+		rfkill_states[i] = RFKILL_STATE_ON;
+
+	error = class_register(&rfkill_class);
+	if (error) {
+		printk(KERN_ERR "rfkill: unable to register rfkill class\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit rfkill_exit(void)
+{
+	class_unregister(&rfkill_class);
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
+
Index: work/include/linux/rfkill.h
===================================================================
--- /dev/null
+++ work/include/linux/rfkill.h
@@ -0,0 +1,85 @@
+#ifndef __RFKILL_H
+#define __RFKILL_H
+
+/*
+ * Copyright (C) 2006 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/device.h>
+
+/**
+ * enum rfkill_type - type of rfkill switch.
+ * RFKILL_TYPE_WLAN: switch is no a Wireless network devices.
+ * RFKILL_TYPE_BlUETOOTH: switch is on a bluetooth device.
+ * RFKILL_TYPE_IRDA: switch is on an infrared devices.
+ */
+enum rfkill_type {
+	RFKILL_TYPE_WLAN = 0,
+	RFKILL_TYPE_BLUETOOTH = 1,
+	RFKILL_TYPE_IRDA = 2,
+	RFKILL_TYPE_MAX = 3,
+};
+
+enum rfkill_state {
+	RFKILL_STATE_OFF	= 0,
+	RFKILL_STATE_ON		= 1,
+};
+
+/**
+ * struct rfkill - rfkill control structure.
+ * @name: Name of the switch.
+ * @type: Radio type which the button controls, the value stored
+ *	here should be a value from enum rfkill_type.
+ * @state: State of the switch (on/off).
+ * @user_claim: Set when the switch is controlled exlusively by userspace.
+ * @mutex: Guards switch state transitions
+ * @data: Pointer to the RF button drivers private data which will be
+ *	passed along when toggling radio state.
+ * @toggle_radio(): Mandatory handler to control state of the radio.
+ * @dev: Device structure integrating the switch into device tree.
+ * @node: Used to place switch into list of all switches known to the
+ *	the system.
+ *
+ * This structure represents a RF switch located on a network device.
+ */
+struct rfkill {
+	char *name;
+	enum rfkill_type type;
+
+	enum rfkill_state state;
+	bool user_claim;
+
+	struct mutex mutex;
+
+	void *data;
+	int (*toggle_radio)(void *data, enum rfkill_state state);
+
+	struct device dev;
+	struct list_head node;
+};
+#define to_rfkill(d)	container_of(d, struct rfkill, dev)
+
+struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type);
+void rfkill_free(struct rfkill *rfkill);
+int rfkill_register(struct rfkill *rfkill);
+void rfkill_unregister(struct rfkill *rfkill);
+
+void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
+
+#endif /* RFKILL_H */
Index: work/drivers/input/misc/rfkill-input.c
===================================================================
--- /dev/null
+++ work/drivers/input/misc/rfkill-input.c
@@ -0,0 +1,169 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Input layer to RF Kill connector");
+MODULE_LICENSE("GPL");
+
+struct rfkill_task {
+	struct work_struct work;
+	enum rfkill_type type;
+	struct mutex mutex; /* ensures that task is serialized */
+	spinlock_t lock; /* for accessing last and desired state */
+	unsigned long last; /* last schedule */
+	enum rfkill_state desired_state; /* on/off */
+	enum rfkill_state current_state; /* on/off */
+};
+
+static void rfkill_task_handler(struct work_struct *work)
+{
+	struct rfkill_task *task = container_of(work, struct rfkill_task, work);
+	enum rfkill_state state;
+
+	mutex_lock(&task->mutex);
+
+	spin_lock_irq(&task->lock);
+	state = task->desired_state;
+	spin_unlock_irq(&task->lock);
+
+	if (state != task->current_state) {
+		rfkill_switch_all(task->type, state);
+		task->current_state = state;
+	}
+
+	mutex_unlock(&task->mutex);
+}
+
+static void rfkill_schedule_toggle(struct rfkill_task *task)
+{
+	unsigned int flags;
+
+	spin_lock_irqsave(&task->lock, flags);
+
+	if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
+		task->desired_state = !task->desired_state;
+		task->last = jiffies;
+		schedule_work(&task->work);
+	}
+
+	spin_unlock_irqrestore(&task->lock, flags);
+}
+
+#define DECLARE_RFKILL_TASK(n, t)			\
+	struct rfkill_task n = {			\
+		.work = __WORK_INITIALIZER(n.work,	\
+				rfkill_task_handler),	\
+		.type = t,				\
+		.mutex = __MUTEX_INITIALIZER(n.mutex),	\
+		.lock = __SPIN_LOCK_UNLOCKED(n.lock),	\
+		.desired_state = RFKILL_STATE_ON,	\
+		.current_state = RFKILL_STATE_ON,	\
+	}
+
+static DECLARE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
+static DECLARE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
+
+static void rfkill_event(struct input_handle *handle, unsigned int type,
+		        unsigned int code, int down)
+{
+	if (type == EV_KEY && down == 1) {
+		switch (code) {
+			case KEY_WLAN:
+				rfkill_schedule_toggle(&rfkill_wlan);
+				break;
+			case KEY_BLUETOOTH:
+				rfkill_schedule_toggle(&rfkill_bt);
+				break;
+			default:
+				break;
+		}
+	}
+}
+
+static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
+			  const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "rfkill";
+
+	error = input_register_handle(handle);
+	if (error)
+		goto err_free_handle;
+
+	error = input_open_device(handle);
+	if (error)
+		goto err_unregister_handle;
+
+	return 0;
+
+ err_unregister_handle:
+	input_unregister_handle(handle);
+ err_free_handle:
+	kfree(handle);
+	return error;
+}
+
+static void rfkill_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id rfkill_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(KEY_WLAN)] = BIT(KEY_WLAN) }
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(KEY_BLUETOOTH)] = BIT(KEY_BLUETOOTH) }
+	},
+};
+
+static struct input_handler rfkill_handler = {
+	.event =	rfkill_event,
+	.connect =	rfkill_connect,
+	.disconnect =	rfkill_disconnect,
+	.name =		"rfkill",
+	.id_table =	rfkill_ids,
+};
+
+static int __init rfkill_handler_init(void)
+{
+	return input_register_handler(&rfkill_handler);
+}
+
+static void __exit rfkill_handler_exit(void)
+{
+	input_unregister_handler(&rfkill_handler);
+	flush_scheduled_work();
+}
+
+module_init(rfkill_handler_init);
+module_exit(rfkill_handler_exit);

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-30  5:27     ` Dmitry Torokhov
@ 2007-03-30  5:29       ` Dmitry Torokhov
  2007-03-30 14:59       ` Ivo van Doorn
  1 sibling, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2007-03-30  5:29 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

Input: polled device skeleton

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
 drivers/input/misc/Kconfig         |   11 ++
 drivers/input/misc/Makefile        |    1 
 drivers/input/misc/input-polldev.c |  149 +++++++++++++++++++++++++++++++++++++
 include/linux/input-polldev.h      |   46 +++++++++++
 4 files changed, 207 insertions(+)

Index: linux/drivers/input/misc/Kconfig
===================================================================
--- linux.orig/drivers/input/misc/Kconfig
+++ linux/drivers/input/misc/Kconfig
@@ -81,6 +81,17 @@ config INPUT_UINPUT
 	  To compile this driver as a module, choose M here: the
 	  module will be called uinput.
 
+config INPUT_POLLDEV
+	tristate "Polled input device skeleton"
+	help
+	  Say Y here if you are using a driver for an input
+	  device that periodically polls hardware state. This
+	  option is only useful for out-of-tree drivers since
+	  in-tree drivers select it automatically.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called input-polldev.
+
 config HP_SDC_RTC
 	tristate "HP SDC Real Time Clock"       
 	depends on GSC || HP300
Index: linux/drivers/input/misc/Makefile
===================================================================
--- linux.orig/drivers/input/misc/Makefile
+++ linux/drivers/input/misc/Makefile
@@ -4,6 +4,7 @@
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_INPUT_POLLDEV)		+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
 obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o
 obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
Index: linux/drivers/input/misc/input-polldev.c
===================================================================
--- /dev/null
+++ linux/drivers/input/misc/input-polldev.c
@@ -0,0 +1,149 @@
+/*
+ * Generic implementation of a polled input device
+
+ * Copyright (c) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/input-polldev.h>
+
+static DEFINE_MUTEX(polldev_mutex);
+static int polldev_users;
+static struct workqueue_struct *polldev_wq;
+
+static void input_polled_device_work(struct work_struct *work)
+{
+	struct input_polled_dev *dev =
+		container_of(work, struct input_polled_dev, work.work);
+
+	dev->poll(dev);
+	queue_delayed_work(polldev_wq, &dev->work,
+			   msecs_to_jiffies(dev->poll_interval));
+}
+
+static int input_open_polled_device(struct input_dev *input)
+{
+	struct input_polled_dev *dev = input->private;
+	int retval;
+
+	retval = mutex_lock_interruptible(&polldev_mutex);
+	if (retval)
+		return retval;
+
+	if (!polldev_users++) {
+		polldev_wq = create_singlethread_workqueue("ipolldevd");
+		if (!polldev_wq) {
+			printk(KERN_ERR "input-polldev: failed to create "
+				"ipolldevd workqueue\n");
+			goto out;
+		}
+	}
+
+	if (dev->flush)
+		dev->flush(dev);
+	queue_delayed_work(polldev_wq, &dev->work,
+			   msecs_to_jiffies(dev->poll_interval));
+
+ out:
+	mutex_unlock(&polldev_mutex);
+	return retval;
+}
+
+static void input_close_polled_device(struct input_dev *input)
+{
+	struct input_polled_dev *dev = input->private;
+
+	cancel_rearming_delayed_workqueue(polldev_wq, &dev->work);
+
+	mutex_lock(&polldev_mutex);
+	if (!--polldev_users)
+		destroy_workqueue(polldev_wq);
+	mutex_unlock(&polldev_mutex);
+}
+
+/**
+ * input_allocate_polled_device - allocated memory polled device
+ *
+ * The function allocates memory for a polled device and also
+ * for an input device associated with this polled device.
+ */
+struct input_polled_dev *input_allocate_polled_device(void)
+{
+	struct input_polled_dev *dev;
+
+	dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	dev->input = input_allocate_device();
+	if (!dev->input) {
+		kfree(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL(input_allocate_polled_device);
+
+/**
+ * input_free_polled_device - free memory allocated for polled device
+ * @dev: device to free
+ *
+ * The function frees memory allocated for pollign device and drops
+ * reference to the associated input device (if present).
+ */
+void input_free_polled_device(struct input_polled_dev *dev)
+{
+	if (dev) {
+		input_free_device(dev->input);
+		kfree(dev);
+	}
+}
+EXPORT_SYMBOL(input_free_polled_device);
+
+/**
+ * input_register_polled_device - register polled device
+ * @dev: device to register
+ *
+ * The function registers previously initialized polled input device
+ * with input layer. The device should be allocated with call to
+ * input_allocate_polled_device(). Callers should also set up poll()
+ * method and set up capabilities (id, name, phys, bits) of the
+ * corresponing input_dev structure.
+ */
+int input_register_polled_device(struct input_polled_dev *dev)
+{
+	struct input_dev *input = dev->input;
+
+	INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
+	if (!dev->poll_interval)
+		dev->poll_interval = 500;
+	input->private = dev;
+	input->open = input_open_polled_device;
+	input->close = input_close_polled_device;
+
+	return input_register_device(input);
+}
+EXPORT_SYMBOL(input_register_polled_device);
+
+/**
+ * input_unregister_polled_device - unregister polled device
+ * @dev: device to unregister
+ *
+ * The function unregisters previously registered polled input
+ * device from input layer. Polling is stopped and device is
+ * ready to be freed with call to input_free_polled_device().
+ * Callers should not attempt to access dev->input pointer
+ * after calling this function.
+ */
+void input_unregister_polled_device(struct input_polled_dev *dev)
+{
+	input_unregister_device(dev->input);
+	dev->input = NULL;
+}
+EXPORT_SYMBOL(input_unregister_polled_device);
+
Index: linux/include/linux/input-polldev.h
===================================================================
--- /dev/null
+++ linux/include/linux/input-polldev.h
@@ -0,0 +1,46 @@
+#ifndef _INPUT_POLLDEV_H
+#define _INPUT_POLLDEV_H
+
+/*
+ * Copyright (c) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/workqueue.h>
+
+/**
+ * struct input_polled_dev - simple polled input device
+ * @private: private driver data
+ * @flush: driver-supplied method that flushes device's state upon
+ *	opening (optional)
+ * @poll: driver-supplied method that polls the device and posts
+ *	input events (mandatory).
+ * @poll_interval: specifies how often the poll() method shoudl be called.
+ * @input: input device structire associated with the polled device.
+ *	Must be properly initialized by the driver (id, name, phys, bits).
+ *
+ * Polled input device provides a skeleton for supporting simple input
+ * devices that do not raise interrupts but have to be periodically
+ * scanned to detect changes in their state.
+ */
+struct input_polled_dev {
+	void *private;
+
+	void (*flush)(struct input_polled_dev *dev);
+	void (*poll)(struct input_polled_dev *dev);
+	unsigned int poll_interval; /* msec */
+
+	struct input_dev *input;
+	struct delayed_work work;
+};
+
+struct input_polled_dev *input_allocate_polled_device(void);
+void input_free_polled_device(struct input_polled_dev *dev);
+int input_register_polled_device(struct input_polled_dev *dev);
+void input_unregister_polled_device(struct input_polled_dev *dev);
+
+#endif

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-30  5:27     ` Dmitry Torokhov
  2007-03-30  5:29       ` Dmitry Torokhov
@ 2007-03-30 14:59       ` Ivo van Doorn
  2007-03-30 15:28         ` Dmitry Torokhov
  1 sibling, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2007-03-30 14:59 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

Hi,

> I am very sorry for taking so much time to respond but finally I went
> through the patch and I still have the same objection as before -
> it mixes two logically (and often physically) separated objects into
> a single entity. I think that RF switch and button should be separate
> entities, created and destroyed independently of each other. This way
> everything handled uniformly by the kernel.

ok. Sounds good as well. :)

> I attempted to rework the rfkill core supprt and simplify it and
> came up with the patch below. Network card drivers that are able
> to control state of their RF transmitters allocate and register
> rfkill structures. Every rfkill structure belongs to one of
> RF classes (WLAN, Bluetooth or IRDA) and exports its name, type,
> state and "user_claim" through sysfs.
> 
> There is also an input handler that catces KEY_WLAN and KEY_BLUETOOTH
> events passing through input system and toggles state of all RF
> switches of appropriate type registered with rfkill system (unless
> a switch was claimed by userspace in which case it is left alone).
> I think this provides basic functionality that most of the users need
> and any advanced control will have to be done from userspace.

Shouldn't a KEY_IRDA be added as well?
It isn't currently defined in input.h yet, but perhaps it actually should?
Or is IRDA treated differently for a specific reason?

> In a followup patch there is a skeleton code for creating polled
> input devices. For cards that have button physically connected
> their drivers will have to register a separate input device and
> let either input handler or userspace application take care of
> switching RF state appropriately.
>
> My only concern is where rfkill code should live. Since it is no
> longer dependent on input core (embedded systems might disable
> rfkill-input and use bare rfkill and control state from userspace)
> it does not need to live in drivers/input.

How about:
rfkill -> drivers/misc
rfkill_input -> input/misc
input_polldev -> lib/ (perhaps small namechange to rfkill_polldev?)
It would scatter the components a bit, but since each of those modules
has its own specific task this would make the most sense.
And by setting the depends and select fields for the menu entries correctly
it shouldn't be too big of a problem.

> Please let me know what you think.

Just to get it straight on how these 3 modules would cooperate (before I start mixing things up) ;)

 - rfkill
	- Drivers register to the rfkill module, rfkill 
	- Provides the sysfs interface
	- Drivers that don't require polling should report the events to this module

 - rfkill_input
	- Provides input device visible to userspace

 - input_polldev
	- Handlers polling, where the driver should check what the previous state was and the driver
	  should send the event to rfkill.

Could input_polldev perhaps not be setup to poll, and keep track of the current button status
and send the signal directly to rfkill?

What I am also not sure of is that input_polldev and rfkill_input both register a input device.
Instead of starting and stopping the polling through the input device it would perhaps make more
sense to either use the input device from rfkill_input and/or make use of a sysfs attribute.

> +/**
> + * rfkill_unregister - Uegister a rfkill structure.
> + * @rfkill: rfkill structure to be unregistered
> + *
> + * This function should be called by the network driver during device
> + * teardown to destroy rfkill structure. Note that rfkill_free() should
> + * _not_ be called after rfkill_unregister().
> + */
> +void rfkill_unregister(struct rfkill *rfkill)
> +{
> +       device_del(&rfkill->dev);
> +       rfkill_remove_switch(rfkill);
> +       put_device(&rfkill->dev);
> +}
> +EXPORT_SYMBOL(rfkill_unregister);

personally I would prefer enforcing drivers to call
allocate()
register()
unregister()
free()

Especially with unregister() doing the same steps as free() (put_device)
might be somwhat confusing. But might be just me. ;)

The remainder looks good, unfortunately I can't test it since the laptop with an rfkill button I have
requires isn't in a state for testing at this moment, and I would need to figure out how button status
reading happens in bcm43xx.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-30 14:59       ` Ivo van Doorn
@ 2007-03-30 15:28         ` Dmitry Torokhov
  2007-03-30 17:13           ` Ivo van Doorn
  0 siblings, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2007-03-30 15:28 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

On 3/30/07, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> Hi,
>
> > I am very sorry for taking so much time to respond but finally I went
> > through the patch and I still have the same objection as before -
> > it mixes two logically (and often physically) separated objects into
> > a single entity. I think that RF switch and button should be separate
> > entities, created and destroyed independently of each other. This way
> > everything handled uniformly by the kernel.
>
> ok. Sounds good as well. :)
>
> > I attempted to rework the rfkill core supprt and simplify it and
> > came up with the patch below. Network card drivers that are able
> > to control state of their RF transmitters allocate and register
> > rfkill structures. Every rfkill structure belongs to one of
> > RF classes (WLAN, Bluetooth or IRDA) and exports its name, type,
> > state and "user_claim" through sysfs.
> >
> > There is also an input handler that catces KEY_WLAN and KEY_BLUETOOTH
> > events passing through input system and toggles state of all RF
> > switches of appropriate type registered with rfkill system (unless
> > a switch was claimed by userspace in which case it is left alone).
> > I think this provides basic functionality that most of the users need
> > and any advanced control will have to be done from userspace.
>
> Shouldn't a KEY_IRDA be added as well?
> It isn't currently defined in input.h yet, but perhaps it actually should?
> Or is IRDA treated differently for a specific reason?

Do we actually have cards with IRDA switches? We are kind of tight in
input KEY_ namespace so I am hesitant to add defines before there
actual users.

>
> > In a followup patch there is a skeleton code for creating polled
> > input devices. For cards that have button physically connected
> > their drivers will have to register a separate input device and
> > let either input handler or userspace application take care of
> > switching RF state appropriately.
> >
> > My only concern is where rfkill code should live. Since it is no
> > longer dependent on input core (embedded systems might disable
> > rfkill-input and use bare rfkill and control state from userspace)
> > it does not need to live in drivers/input.
>
> How about:
> rfkill -> drivers/misc

Not in net?

> rfkill_input -> input/misc

I can go both ways on this one as it crosses line between input and
rfkill. I think from user/Kconfig point of view it is better to keep
it together with rfkill: user woudl select rfkill option and right
then and there decide if he wants to give the kernel ability to
automatically control RF switche

> input_polldev -> lib/ (perhaps small namechange to rfkill_polldev?)

No, I do not want to change name - I have bunch of drivers that can me
converted to use this skeleton - wistron, aaedkbd, hdaps, ams,
cobalt_btns. It is also pure input-related function so it is the only
module that definitely belongs to drivers/input/misc.

> It would scatter the components a bit, but since each of those modules
> has its own specific task this would make the most sense.
> And by setting the depends and select fields for the menu entries correctly
> it shouldn't be too big of a problem.
>
> > Please let me know what you think.
>
> Just to get it straight on how these 3 modules would cooperate (before I start mixing things up) ;)
>
>  - rfkill
>        - Drivers register to the rfkill module, rfkill
>        - Provides the sysfs interface
>        - Drivers that don't require polling should report the events to this module

Not quite. Drivers that have buttons do not require polling still
should create an input device and register it with input layer. Except
that with interrupt-driven devices there is not much you can stub out
so you just have to do input_allocate_device/input_register_device.

>
>  - rfkill_input
>        - Provides input device visible to userspace
>

No, rfkill-input is not an input device but input handler (or input
interface). It routes input events from buttons into switches (see
below).

>  - input_polldev
>        - Handlers polling, where the driver should check what the previous state was and the driver
>          should send the event to rfkill.

This is the input device visible to userspace and kernel. It polls
state of the button and sends KEY_WLAN/KEY_BLUETOOTH events through
input layer. They get distributed through various input handlers such
as evdev and rfkill-input. Evdev provides route for events to
userspace where application can listen to events and then toggle RF
switches according to the current policy via
/sys/class/rfkill/rfkillXXX/state. Rfkill-input provides in-kernel
route for events into rfkill system. If rfkill-input is loaded
switches that are not claimed by userspace will be toggled
automatically.

Does this make sense?

Note that we don't start polling the state of button until tare are
users of that input device. If rfkill-input is loaded then there is a
user right away. Otherwise we wait for userspace to open evdev node.

>
> personally I would prefer enforcing drivers to call
> allocate()
> register()
> unregister()
> free()
>
> Especially with unregister() doing the same steps as free() (put_device)
> might be somwhat confusing. But might be just me. ;)
>

I know but for refcounted objects you can't really tell when they will
actually be freed. It depends when their last user drops off.

BTW, I can't either as I don't have any laptops with RF swtches ;)

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-30 15:28         ` Dmitry Torokhov
@ 2007-03-30 17:13           ` Ivo van Doorn
  2007-03-30 18:37             ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2007-03-30 17:13 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

> > > There is also an input handler that catces KEY_WLAN and KEY_BLUETOOTH
> > > events passing through input system and toggles state of all RF
> > > switches of appropriate type registered with rfkill system (unless
> > > a switch was claimed by userspace in which case it is left alone).
> > > I think this provides basic functionality that most of the users need
> > > and any advanced control will have to be done from userspace.
> >
> > Shouldn't a KEY_IRDA be added as well?
> > It isn't currently defined in input.h yet, but perhaps it actually should?
> > Or is IRDA treated differently for a specific reason?
> 
> Do we actually have cards with IRDA switches? We are kind of tight in
> input KEY_ namespace so I am hesitant to add defines before there
> actual users.

Ah ok. Well that shouldn't be a problem, IRDA could be added once there
are users for it. I am not sure if there are any devices with IRDA switches,
so it shouldn't be a big problem.

> > > In a followup patch there is a skeleton code for creating polled
> > > input devices. For cards that have button physically connected
> > > their drivers will have to register a separate input device and
> > > let either input handler or userspace application take care of
> > > switching RF state appropriately.
> > >
> > > My only concern is where rfkill code should live. Since it is no
> > > longer dependent on input core (embedded systems might disable
> > > rfkill-input and use bare rfkill and control state from userspace)
> > > it does not need to live in drivers/input.
> >
> > How about:
> > rfkill -> drivers/misc
> 
> Not in net?

Well in drivers/net are the network drivers but not the irda and bluetooth drivers,
those are located in different folders in drivers/ so I think misc would be the most
suitable location.

> > rfkill_input -> input/misc
> 
> I can go both ways on this one as it crosses line between input and
> rfkill. I think from user/Kconfig point of view it is better to keep
> it together with rfkill: user woudl select rfkill option and right
> then and there decide if he wants to give the kernel ability to
> automatically control RF switche

Makes sense.

> > input_polldev -> lib/ (perhaps small namechange to rfkill_polldev?)
> 
> No, I do not want to change name - I have bunch of drivers that can me
> converted to use this skeleton - wistron, aaedkbd, hdaps, ams,
> cobalt_btns. It is also pure input-related function so it is the only
> module that definitely belongs to drivers/input/misc.

Ok. :)

> > It would scatter the components a bit, but since each of those modules
> > has its own specific task this would make the most sense.
> > And by setting the depends and select fields for the menu entries correctly
> > it shouldn't be too big of a problem.
> >
> > > Please let me know what you think.
> >
> > Just to get it straight on how these 3 modules would cooperate (before I start mixing things up) ;)
> >
> >  - rfkill
> >        - Drivers register to the rfkill module, rfkill
> >        - Provides the sysfs interface
> >        - Drivers that don't require polling should report the events to this module
> 
> Not quite. Drivers that have buttons do not require polling still
> should create an input device and register it with input layer. Except
> that with interrupt-driven devices there is not much you can stub out
> so you just have to do input_allocate_device/input_register_device.
> 
> >
> >  - rfkill_input
> >        - Provides input device visible to userspace
> >
> 
> No, rfkill-input is not an input device but input handler (or input
> interface). It routes input events from buttons into switches (see
> below).
> 
> >  - input_polldev
> >        - Handlers polling, where the driver should check what the previous state was and the driver
> >          should send the event to rfkill.
> 
> This is the input device visible to userspace and kernel. It polls
> state of the button and sends KEY_WLAN/KEY_BLUETOOTH events through
> input layer. They get distributed through various input handlers such
> as evdev and rfkill-input. Evdev provides route for events to
> userspace where application can listen to events and then toggle RF
> switches according to the current policy via
> /sys/class/rfkill/rfkillXXX/state. Rfkill-input provides in-kernel
> route for events into rfkill system. If rfkill-input is loaded
> switches that are not claimed by userspace will be toggled
> automatically.
> 
> Does this make sense?

Yes, but what if the user loads both modules or has them both compiled in?
Shouldn't there be some protection against that, since both handlers should not
be active at the same time.

> Note that we don't start polling the state of button until tare are
> users of that input device. If rfkill-input is loaded then there is a
> user right away. Otherwise we wait for userspace to open evdev node.

Sounds good.

> > personally I would prefer enforcing drivers to call
> > allocate()
> > register()
> > unregister()
> > free()
> >
> > Especially with unregister() doing the same steps as free() (put_device)
> > might be somwhat confusing. But might be just me. ;)
> >
> 
> I know but for refcounted objects you can't really tell when they will
> actually be freed. It depends when their last user drops off.

Then perhaps rfkill_register could call put_device() when it fails, and free()
can be removed entirely. That way it would we prevent some driver
to call free() anyway.

> BTW, I can't either as I don't have any laptops with RF swtches ;)

If I am not mistaken, Larry (CC'ed during most of this discussion) does have such
a device _and_ he knows more about the bcm43xx drivers. :)
Larry, if you have time, could you make a test run with the patches Dmitry has send?

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-30 17:13           ` Ivo van Doorn
@ 2007-03-30 18:37             ` Dmitry Torokhov
  2007-03-31 12:49               ` Ivo van Doorn
  0 siblings, 1 reply; 42+ messages in thread
From: Dmitry Torokhov @ 2007-03-30 18:37 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

On 3/30/07, Ivo van Doorn <ivdoorn@gmail.com> wrote:
> > > >
> > > > My only concern is where rfkill code should live. Since it is no
> > > > longer dependent on input core (embedded systems might disable
> > > > rfkill-input and use bare rfkill and control state from userspace)
> > > > it does not need to live in drivers/input.
> > >
> > > How about:
> > > rfkill -> drivers/misc
> >
> > Not in net?
>
> Well in drivers/net are the network drivers but not the irda and bluetooth drivers,
> those are located in different folders in drivers/ so I think misc would be the most
> suitable location.

We could also consider the ./net itself. rfkill is not a driver, it is
a facility.

> > >  - input_polldev
> > >        - Handlers polling, where the driver should check what the previous state was and the driver
> > >          should send the event to rfkill.
> >
> > This is the input device visible to userspace and kernel. It polls
> > state of the button and sends KEY_WLAN/KEY_BLUETOOTH events through
> > input layer. They get distributed through various input handlers such
> > as evdev and rfkill-input. Evdev provides route for events to
> > userspace where application can listen to events and then toggle RF
> > switches according to the current policy via
> > /sys/class/rfkill/rfkillXXX/state. Rfkill-input provides in-kernel
> > route for events into rfkill system. If rfkill-input is loaded
> > switches that are not claimed by userspace will be toggled
> > automatically.
> >
> > Does this make sense?
>
> Yes, but what if the user loads both modules or has them both compiled in?
> Shouldn't there be some protection against that, since both handlers should not
> be active at the same time.

Why not? evdev is just a delivery transport for input events to
userspace. Even if user wants the kernel to control state of RF
switches (which I expect most users woudl want) there still could be
applications interested in knowing that used turned off wireless. And
if userspace really wishes to control switch all by itself it can
"claim" it.

I guess what you are missing is that input event generated by a device
is pushed through every handler bound to the device so there is no way
for a "wrong" handler to "steals" an event from "right" handler. They
all work simultaneously.

> > > personally I would prefer enforcing drivers to call
> > > allocate()
> > > register()
> > > unregister()
> > > free()
> > >
> > > Especially with unregister() doing the same steps as free() (put_device)
> > > might be somwhat confusing. But might be just me. ;)
> > >
> >
> > I know but for refcounted objects you can't really tell when they will
> > actually be freed. It depends when their last user drops off.
>
> Then perhaps rfkill_register could call put_device() when it fails, and free()
> can be removed entirely. That way it would we prevent some driver
> to call free() anyway.
>

That would make error handling in ->probe() methods a bit unwieldy I
think - if you are using the standard "goto err_xxx; goto err_yyy;"
technique then you have to conditionally call rfkill_free(). Hovewer
if register simply fails and does not free anything and you call
rfkill_register() last (which you need to do because driver has to be
almost fully functional to be able to serve toggle_radio calls) you
can always call rfkill_free() if something fails.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-30 18:37             ` Dmitry Torokhov
@ 2007-03-31 12:49               ` Ivo van Doorn
  2007-04-02  4:38                 ` Dmitry Torokhov
  0 siblings, 1 reply; 42+ messages in thread
From: Ivo van Doorn @ 2007-03-31 12:49 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger

> > Well in drivers/net are the network drivers but not the irda and bluetooth drivers,
> > those are located in different folders in drivers/ so I think misc would be the most
> > suitable location.
> 
> We could also consider the ./net itself. rfkill is not a driver, it is
> a facility.

True, in that case ./net would be good.

> > > Does this make sense?
> >
> > Yes, but what if the user loads both modules or has them both compiled in?
> > Shouldn't there be some protection against that, since both handlers should not
> > be active at the same time.
> 
> Why not? evdev is just a delivery transport for input events to
> userspace. Even if user wants the kernel to control state of RF
> switches (which I expect most users woudl want) there still could be
> applications interested in knowing that used turned off wireless. And
> if userspace really wishes to control switch all by itself it can
> "claim" it.

Right, I forgot about that user_claim thingy. ;)

> I guess what you are missing is that input event generated by a device
> is pushed through every handler bound to the device so there is no way
> for a "wrong" handler to "steals" an event from "right" handler. They
> all work simultaneously.
> 
> > > > personally I would prefer enforcing drivers to call
> > > > allocate()
> > > > register()
> > > > unregister()
> > > > free()
> > > >
> > > > Especially with unregister() doing the same steps as free() (put_device)
> > > > might be somwhat confusing. But might be just me. ;)
> > > >
> > >
> > > I know but for refcounted objects you can't really tell when they will
> > > actually be freed. It depends when their last user drops off.
> >
> > Then perhaps rfkill_register could call put_device() when it fails, and free()
> > can be removed entirely. That way it would we prevent some driver
> > to call free() anyway.
> >
> 
> That would make error handling in ->probe() methods a bit unwieldy I
> think - if you are using the standard "goto err_xxx; goto err_yyy;"
> technique then you have to conditionally call rfkill_free(). Hovewer
> if register simply fails and does not free anything and you call
> rfkill_register() last (which you need to do because driver has to be
> almost fully functional to be able to serve toggle_radio calls) you
> can always call rfkill_free() if something fails.

Ok.

Well that would mean rfkill would be ready to applied to one of the kernel trees right? :)
The first user of rfkill would be rt2x00, which is in wireless-dev as well as -mm.
The second user could be the driver from Lennart, but I haven't heard from him quite a while
(although he is on the CC list) so I am not sure if his MSI driver can be fixed to use rfkill. His MSI
driver is already in the linus' tree.
A third user could be bcm43xx but I don't know how far they are with hardware key detection and
status reading (to make it work with rfkill), perhaps Larry could give more information about that.

Ivo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [RFC] rfkill - Add support for input key to control wireless radio
  2007-03-31 12:49               ` Ivo van Doorn
@ 2007-04-02  4:38                 ` Dmitry Torokhov
  0 siblings, 0 replies; 42+ messages in thread
From: Dmitry Torokhov @ 2007-04-02  4:38 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: Stephen Hemminger, linux-kernel, netdev, John Linville,
	Jiri Benc, Lennart Poettering, Johannes Berg, Larry Finger,
	David Miller

On Saturday 31 March 2007 08:49, Ivo van Doorn wrote:
> 
> Well that would mean rfkill would be ready to applied to one of the kernel trees right? :)

Well, that would be up to that particular tree maintainer. I am not sure
who maintains the net/... David Miller maybe? Anyway, below is the same
rfkill patch that I posted before except that everything is moved in
net/ instead of being in drivers/input/misc. 

I will make sure that input-polldev will get into input three tough.
 
-- 
Dmitry

From: Ivo van Doorn <ivdoorn@gmail.com>

Input: rfkill - Add support for input key to control wireless radio

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 include/linux/rfkill.h    |   85 +++++++++
 net/Kconfig               |    1 
 net/Makefile              |    1 
 net/rfkill/Kconfig        |   24 ++
 net/rfkill/Makefile       |    6 
 net/rfkill/rfkill-input.c |  169 +++++++++++++++++++
 net/rfkill/rfkill.c       |  400 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 686 insertions(+)

Index: work/include/linux/rfkill.h
===================================================================
--- /dev/null
+++ work/include/linux/rfkill.h
@@ -0,0 +1,85 @@
+#ifndef __RFKILL_H
+#define __RFKILL_H
+
+/*
+ * Copyright (C) 2006 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/device.h>
+
+/**
+ * enum rfkill_type - type of rfkill switch.
+ * RFKILL_TYPE_WLAN: switch is no a Wireless network devices.
+ * RFKILL_TYPE_BlUETOOTH: switch is on a bluetooth device.
+ * RFKILL_TYPE_IRDA: switch is on an infrared devices.
+ */
+enum rfkill_type {
+	RFKILL_TYPE_WLAN = 0,
+	RFKILL_TYPE_BLUETOOTH = 1,
+	RFKILL_TYPE_IRDA = 2,
+	RFKILL_TYPE_MAX = 3,
+};
+
+enum rfkill_state {
+	RFKILL_STATE_OFF	= 0,
+	RFKILL_STATE_ON		= 1,
+};
+
+/**
+ * struct rfkill - rfkill control structure.
+ * @name: Name of the switch.
+ * @type: Radio type which the button controls, the value stored
+ *	here should be a value from enum rfkill_type.
+ * @state: State of the switch (on/off).
+ * @user_claim: Set when the switch is controlled exlusively by userspace.
+ * @mutex: Guards switch state transitions
+ * @data: Pointer to the RF button drivers private data which will be
+ *	passed along when toggling radio state.
+ * @toggle_radio(): Mandatory handler to control state of the radio.
+ * @dev: Device structure integrating the switch into device tree.
+ * @node: Used to place switch into list of all switches known to the
+ *	the system.
+ *
+ * This structure represents a RF switch located on a network device.
+ */
+struct rfkill {
+	char *name;
+	enum rfkill_type type;
+
+	enum rfkill_state state;
+	bool user_claim;
+
+	struct mutex mutex;
+
+	void *data;
+	int (*toggle_radio)(void *data, enum rfkill_state state);
+
+	struct device dev;
+	struct list_head node;
+};
+#define to_rfkill(d)	container_of(d, struct rfkill, dev)
+
+struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type);
+void rfkill_free(struct rfkill *rfkill);
+int rfkill_register(struct rfkill *rfkill);
+void rfkill_unregister(struct rfkill *rfkill);
+
+void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
+
+#endif /* RFKILL_H */
Index: work/net/Kconfig
===================================================================
--- work.orig/net/Kconfig
+++ work/net/Kconfig
@@ -220,6 +220,7 @@ source "net/ax25/Kconfig"
 source "net/irda/Kconfig"
 source "net/bluetooth/Kconfig"
 source "net/ieee80211/Kconfig"
+source "net/rfkill/Kconfig"
 
 config WIRELESS_EXT
 	bool
Index: work/net/Makefile
===================================================================
--- work.orig/net/Makefile
+++ work/net/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_IEEE80211)		+= ieee80211/
 obj-$(CONFIG_TIPC)		+= tipc/
 obj-$(CONFIG_NETLABEL)		+= netlabel/
 obj-$(CONFIG_IUCV)		+= iucv/
+obj-$(CONFIG_RFKILL)		+= rfkill/
 
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
Index: work/net/rfkill/Kconfig
===================================================================
--- /dev/null
+++ work/net/rfkill/Kconfig
@@ -0,0 +1,24 @@
+#
+# RF switch subsystem configuration
+#
+menuconfig RFKILL
+	tristate "RF switch subsystem support"
+	help
+	  Say Y here if you want to have control over RF switches
+	  found on many WiFi, Bluetooth and IRDA cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rfkill.
+
+config RFKILL_INPUT
+	tristate "Input layer to RF switch connector"
+	depends on RFKILL && INPUT
+	help
+	  Say Y here if you want kernel automatically toggle state
+	  of RF switches on and off when user presses appropriate
+	  button or a key on the keyboard. Without this module you
+	  need a some kind of userspace application to control
+	  state of the switches.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rfkill-input.
Index: work/net/rfkill/Makefile
===================================================================
--- /dev/null
+++ work/net/rfkill/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the RF switch subsystem.
+#
+
+obj-$(CONFIG_RFKILL)			+= rfkill.o
+obj-$(CONFIG_RFKILL_INPUT)		+= rfkill-input.o
Index: work/net/rfkill/rfkill-input.c
===================================================================
--- /dev/null
+++ work/net/rfkill/rfkill-input.c
@@ -0,0 +1,169 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Input layer to RF switch connector");
+MODULE_LICENSE("GPL");
+
+struct rfkill_task {
+	struct work_struct work;
+	enum rfkill_type type;
+	struct mutex mutex; /* ensures that task is serialized */
+	spinlock_t lock; /* for accessing last and desired state */
+	unsigned long last; /* last schedule */
+	enum rfkill_state desired_state; /* on/off */
+	enum rfkill_state current_state; /* on/off */
+};
+
+static void rfkill_task_handler(struct work_struct *work)
+{
+	struct rfkill_task *task = container_of(work, struct rfkill_task, work);
+	enum rfkill_state state;
+
+	mutex_lock(&task->mutex);
+
+	spin_lock_irq(&task->lock);
+	state = task->desired_state;
+	spin_unlock_irq(&task->lock);
+
+	if (state != task->current_state) {
+		rfkill_switch_all(task->type, state);
+		task->current_state = state;
+	}
+
+	mutex_unlock(&task->mutex);
+}
+
+static void rfkill_schedule_toggle(struct rfkill_task *task)
+{
+	unsigned int flags;
+
+	spin_lock_irqsave(&task->lock, flags);
+
+	if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
+		task->desired_state = !task->desired_state;
+		task->last = jiffies;
+		schedule_work(&task->work);
+	}
+
+	spin_unlock_irqrestore(&task->lock, flags);
+}
+
+#define DECLARE_RFKILL_TASK(n, t)			\
+	struct rfkill_task n = {			\
+		.work = __WORK_INITIALIZER(n.work,	\
+				rfkill_task_handler),	\
+		.type = t,				\
+		.mutex = __MUTEX_INITIALIZER(n.mutex),	\
+		.lock = __SPIN_LOCK_UNLOCKED(n.lock),	\
+		.desired_state = RFKILL_STATE_ON,	\
+		.current_state = RFKILL_STATE_ON,	\
+	}
+
+static DECLARE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
+static DECLARE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
+
+static void rfkill_event(struct input_handle *handle, unsigned int type,
+		        unsigned int code, int down)
+{
+	if (type == EV_KEY && down == 1) {
+		switch (code) {
+			case KEY_WLAN:
+				rfkill_schedule_toggle(&rfkill_wlan);
+				break;
+			case KEY_BLUETOOTH:
+				rfkill_schedule_toggle(&rfkill_bt);
+				break;
+			default:
+				break;
+		}
+	}
+}
+
+static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
+			  const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "rfkill";
+
+	error = input_register_handle(handle);
+	if (error)
+		goto err_free_handle;
+
+	error = input_open_device(handle);
+	if (error)
+		goto err_unregister_handle;
+
+	return 0;
+
+ err_unregister_handle:
+	input_unregister_handle(handle);
+ err_free_handle:
+	kfree(handle);
+	return error;
+}
+
+static void rfkill_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id rfkill_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(KEY_WLAN)] = BIT(KEY_WLAN) }
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(KEY_BLUETOOTH)] = BIT(KEY_BLUETOOTH) }
+	},
+};
+
+static struct input_handler rfkill_handler = {
+	.event =	rfkill_event,
+	.connect =	rfkill_connect,
+	.disconnect =	rfkill_disconnect,
+	.name =		"rfkill",
+	.id_table =	rfkill_ids,
+};
+
+static int __init rfkill_handler_init(void)
+{
+	return input_register_handler(&rfkill_handler);
+}
+
+static void __exit rfkill_handler_exit(void)
+{
+	input_unregister_handler(&rfkill_handler);
+	flush_scheduled_work();
+}
+
+module_init(rfkill_handler_init);
+module_exit(rfkill_handler_exit);
Index: work/net/rfkill/rfkill.c
===================================================================
--- /dev/null
+++ work/net/rfkill/rfkill.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2006 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rfkill.h>
+
+MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF switch support");
+MODULE_LICENSE("GPL");
+
+static LIST_HEAD(rfkill_list);	/* list of registered rf switches */
+static DEFINE_MUTEX(rfkill_mutex);
+
+static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX];
+
+static int rfkill_toggle_radio(struct rfkill *rfkill,
+				enum rfkill_state state)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&rfkill->mutex);
+	if (retval)
+		return retval;
+
+	if (state != rfkill->state) {
+		retval = rfkill->toggle_radio(rfkill->data, state);
+		if (!retval)
+			rfkill->state = state;
+	}
+
+	mutex_unlock(&rfkill->mutex);
+	return retval;
+}
+
+/**
+ * rfkill_switch_all - Toggle state of all switches of given type
+ * @type: type of interfaces to be affeceted
+ * @state: the new state
+ *
+ * This function toggles state of all switches of given type unless
+ * a specific switch is claimed by userspace in which case it is
+ * left alone.
+ */
+
+void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
+{
+	struct rfkill *rfkill;
+
+	mutex_lock(&rfkill_mutex);
+
+	rfkill_states[type] = state;
+
+	list_for_each_entry(rfkill, &rfkill_list, node) {
+		if (!rfkill->user_claim)
+			rfkill_toggle_radio(rfkill, state);
+	}
+
+	mutex_unlock(&rfkill_mutex);
+}
+EXPORT_SYMBOL(rfkill_switch_all);
+
+static ssize_t rfkill_name_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	return sprintf(buf, "%s\n", rfkill->name);
+}
+
+static ssize_t rfkill_type_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+	const char *type;
+
+	switch (rfkill->type) {
+	case RFKILL_TYPE_WLAN:
+		type = "wlan";
+		break;
+	case RFKILL_TYPE_BLUETOOTH:
+		type = "bluetooth";
+		break;
+	case RFKILL_TYPE_IRDA:
+		type = "irda";
+		break;
+	default:
+		BUG();
+	}
+
+	return sprintf(buf, "%s\n", type);
+}
+
+static ssize_t rfkill_state_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	return sprintf(buf, "%d\n", rfkill->state);
+}
+
+static ssize_t rfkill_state_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+	unsigned int state = simple_strtoul(buf, NULL, 0);
+	int error;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	error = rfkill_toggle_radio(rfkill,
+			state ? RFKILL_STATE_ON : RFKILL_STATE_OFF);
+	if (error)
+		return error;
+
+	return count;
+}
+
+static ssize_t rfkill_claim_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	return sprintf(buf, "%d", rfkill->user_claim);
+}
+
+static ssize_t rfkill_claim_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+	bool claim = !!simple_strtoul(buf, NULL, 0);
+	int error;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	/*
+	 * Take the global lock to make sure the kernel is not in
+	 * the middle of rfkill_toggle_all
+	 */
+	error = mutex_lock_interruptible(&rfkill_mutex);
+	if (error)
+		return error;
+
+	if (rfkill->user_claim != claim) {
+		if (!claim)
+			rfkill_toggle_radio(rfkill,
+					    rfkill_states[rfkill->type]);
+		rfkill->user_claim = claim;
+	}
+
+	mutex_unlock(&rfkill_mutex);
+
+	return count;
+}
+
+static struct device_attribute rfkill_dev_attrs[] = {
+	__ATTR(name, S_IRUGO, rfkill_name_show, NULL),
+	__ATTR(type, S_IRUGO, rfkill_type_show, NULL),
+	__ATTR(state, S_IRUGO, rfkill_state_show, rfkill_state_store),
+	__ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store),
+	__ATTR_NULL
+};
+
+static void rfkill_release(struct device *dev)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	kfree(rfkill);
+	module_put(THIS_MODULE);
+}
+
+static int rfkill_suspend(struct device *dev, pm_message_t state)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	if (dev->power.power_state.event != state.event) {
+		if (state.event == PM_EVENT_SUSPEND) {
+			mutex_lock(&rfkill->mutex);
+
+			if (rfkill->state == RFKILL_STATE_ON)
+				rfkill->toggle_radio(rfkill->data,
+						     RFKILL_STATE_OFF);
+
+			mutex_unlock(&rfkill->mutex);
+		}
+
+		dev->power.power_state = state;
+	}
+
+	return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+	struct rfkill *rfkill = to_rfkill(dev);
+
+	if (dev->power.power_state.event != PM_EVENT_ON) {
+		mutex_lock(&rfkill->mutex);
+
+		if (rfkill->state == RFKILL_STATE_ON)
+			rfkill->toggle_radio(rfkill->data, RFKILL_STATE_ON);
+
+		mutex_unlock(&rfkill->mutex);
+	}
+
+	dev->power.power_state = PMSG_ON;
+	return 0;
+}
+
+static struct class rfkill_class = {
+	.name		= "rfkill",
+	.dev_release	= rfkill_release,
+	.dev_attrs	= rfkill_dev_attrs,
+	.suspend	= rfkill_suspend,
+	.resume		= rfkill_resume,
+};
+
+static int rfkill_add_switch(struct rfkill *rfkill)
+{
+	int retval;
+
+	retval = mutex_lock_interruptible(&rfkill_mutex);
+	if (retval)
+		return retval;
+
+	retval = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type]);
+	if (retval)
+		goto out;
+
+	list_add_tail(&rfkill->node, &rfkill_list);
+
+ out:
+	mutex_unlock(&rfkill_mutex);
+	return retval;
+}
+
+static void rfkill_remove_switch(struct rfkill *rfkill)
+{
+	mutex_lock(&rfkill_mutex);
+	list_del_init(&rfkill->node);
+	rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF);
+	mutex_unlock(&rfkill_mutex);
+}
+
+/**
+ * rfkill_allocate - allocate memory for rfkill structure.
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (wlan, bluetooth, irda)
+ *
+ * This function should be called by the network driver when it needs
+ * rfkill structure. Once the structure is allocated the driver shoud
+ * finish its initialization by setting name, private data, enable_radio
+ * and disable_radio methods and then register it with rfkill_register().
+ * NOTE: If registration fails the structure shoudl be freed by calling
+ * rfkill_free() otherwise rfkill_unregister() should be used.
+ */
+struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type)
+{
+	struct rfkill *rfkill;
+	struct device *dev;
+
+	rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL);
+	if (rfkill)
+		return NULL;
+
+	mutex_init(&rfkill->mutex);
+	INIT_LIST_HEAD(&rfkill->node);
+	rfkill->type = type;
+
+	dev = &rfkill->dev;
+	dev->class = &rfkill_class;
+	dev->parent = parent;
+	device_initialize(dev);
+
+	return rfkill;
+}
+EXPORT_SYMBOL(rfkill_allocate);
+
+/**
+ * rfkill_free - Mark rfkill structure for deletion
+ * @rfkill: rfkill structure to be destroyed
+ *
+ * Decrements reference count of rfkill structure so it is destoryed.
+ * Note that rfkill_free() should _not_ be called after rfkill_unregister().
+ */
+void rfkill_free(struct rfkill *rfkill)
+{
+	if (rfkill)
+		put_device(&rfkill->dev);
+}
+EXPORT_SYMBOL(rfkill_free);
+
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the network driver when the rfkill
+ * structure needs to be registered. Immediately from registration the
+ * switch driver should be able to service calls to toggle_radio.
+ */
+int rfkill_register(struct rfkill *rfkill)
+{
+	static atomic_t rfkill_no = ATOMIC_INIT(0);
+	struct device *dev = &rfkill->dev;
+	int error;
+
+	if (!rfkill->toggle_radio)
+		return -EINVAL;
+
+	error = rfkill_add_switch(rfkill);
+	if (error)
+		return error;
+
+	snprintf(dev->bus_id, sizeof(dev->bus_id),
+		 "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1);
+
+	error = device_add(dev);
+	if (error) {
+		rfkill_remove_switch(rfkill);
+		return error;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rfkill_register);
+
+/**
+ * rfkill_unregister - Uegister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Note that rfkill_free() should
+ * _not_ be called after rfkill_unregister().
+ */
+void rfkill_unregister(struct rfkill *rfkill)
+{
+	device_del(&rfkill->dev);
+	rfkill_remove_switch(rfkill);
+	put_device(&rfkill->dev);
+}
+EXPORT_SYMBOL(rfkill_unregister);
+
+/*
+ * Rfkill module initialization/deinitialization.
+ */
+static int __init rfkill_init(void)
+{
+	int error;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rfkill_states); i++)
+		rfkill_states[i] = RFKILL_STATE_ON;
+
+	error = class_register(&rfkill_class);
+	if (error) {
+		printk(KERN_ERR "rfkill: unable to register rfkill class\n");
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit rfkill_exit(void)
+{
+	class_unregister(&rfkill_class);
+}
+
+module_init(rfkill_init);
+module_exit(rfkill_exit);
+

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2007-04-02  4:38 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-12-03 18:36 [RFC] rfkill - Add support for input key to control wireless radio Ivo van Doorn
2006-12-03 19:18 ` Arjan van de Ven
2006-12-03 22:03   ` Ivo van Doorn
2006-12-03 19:20 ` Arjan van de Ven
2006-12-03 22:05   ` Ivo van Doorn
2006-12-03 22:28   ` Ivo van Doorn
2006-12-05  0:18     ` Randy Dunlap
2006-12-05 21:20       ` Ivo van Doorn
2006-12-03 19:44 ` Dan Williams
2006-12-03 22:16   ` Ivo van Doorn
2006-12-04  8:53   ` Marcel Holtmann
2006-12-04 22:15 ` Dmitry Torokhov
2006-12-04 23:27   ` Ivo van Doorn
2006-12-06 14:37     ` Dmitry Torokhov
2006-12-06 15:18       ` Dan Williams
2006-12-06 15:24         ` Dmitry Torokhov
2006-12-06 19:31       ` Ivo van Doorn
2006-12-06 20:18         ` Dmitry Torokhov
2006-12-06 21:41           ` Ivo van Doorn
2006-12-06 22:04             ` Dmitry Torokhov
2006-12-07 21:53               ` Ivo van Doorn
2006-12-12  5:12                 ` Dmitry Torokhov
2006-12-12  7:47                   ` Ivo Van Doorn
2006-12-17 17:43                   ` Ivo van Doorn
2007-01-30 16:33                     ` Ivo van Doorn
2006-12-07 13:22             ` Dan Williams
2006-12-07 21:58               ` Ivo van Doorn
2006-12-06 22:05           ` Jiri Benc
2006-12-06 22:10             ` Dmitry Torokhov
2006-12-05 10:32 ` Christoph Hellwig
2006-12-05 21:21   ` Ivo van Doorn
2007-01-31  3:40 ` Stephen Hemminger
2007-01-31 10:39   ` Ivo van Doorn
2007-01-31 11:20   ` Ivo van Doorn
2007-03-30  5:27     ` Dmitry Torokhov
2007-03-30  5:29       ` Dmitry Torokhov
2007-03-30 14:59       ` Ivo van Doorn
2007-03-30 15:28         ` Dmitry Torokhov
2007-03-30 17:13           ` Ivo van Doorn
2007-03-30 18:37             ` Dmitry Torokhov
2007-03-31 12:49               ` Ivo van Doorn
2007-04-02  4:38                 ` Dmitry Torokhov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).