2006-12-03 18:36:55

by Ivo Van Doorn

[permalink] [raw]
Subject: [RFC] rfkill - Add support for input key to control wireless radio

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 <[email protected]>

---

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 <[email protected]>");
+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 */


2006-12-03 19:18:17

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-03 19:20:22

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-03 19:43:16

by Dan Williams

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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 <[email protected]>
>
> ---
>
> 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 <[email protected]>");
> +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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2006-12-03 22:03:39

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-03 22:05:34

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-03 22:17:15

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-03 22:28:32

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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 <[email protected]>

---

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 <[email protected]>");
+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 */

2006-12-04 08:54:20

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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


2006-12-04 22:15:14

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On 12/3/06, Ivo van Doorn <[email protected]> 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

2006-12-04 23:27:21

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-05 00:19:32

by Randy Dunlap

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-05 10:32:46

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

> +/*
> + * 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.

2006-12-05 21:21:12

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-05 21:20:56

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

[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

2006-12-06 14:37:14

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On 12/4/06, Ivo van Doorn <[email protected]> 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

2006-12-06 15:17:34

by Dan Williams

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On Wed, 2006-12-06 at 09:37 -0500, Dmitry Torokhov wrote:
> On 12/4/06, Ivo van Doorn <[email protected]> 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.
>

2006-12-06 15:24:13

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On 12/6/06, Dan Williams <[email protected]> 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

2006-12-06 19:32:07

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On Wednesday 06 December 2006 15:37, Dmitry Torokhov wrote:
> On 12/4/06, Ivo van Doorn <[email protected]> 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

2006-12-06 20:18:16

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On 12/6/06, Ivo van Doorn <[email protected]> wrote:
> On Wednesday 06 December 2006 15:37, Dmitry Torokhov wrote:
> > On 12/4/06, Ivo van Doorn <[email protected]> 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

2006-12-06 21:42:10

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-06 22:05:00

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On 12/6/06, Ivo van Doorn <[email protected]> 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

2006-12-06 22:05:56

by Jiri Benc

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On Wed, 6 Dec 2006 15:18:12 -0500, Dmitry Torokhov wrote:
> On 12/6/06, Ivo van Doorn <[email protected]> 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

2006-12-06 22:10:59

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

On 12/6/06, Jiri Benc <[email protected]> wrote:
> On Wed, 6 Dec 2006 15:18:12 -0500, Dmitry Torokhov wrote:
> > On 12/6/06, Ivo van Doorn <[email protected]> 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

2006-12-07 13:21:35

by Dan Williams

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2006-12-07 21:58:23

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-07 21:53:24

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-12 05:12:34

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-12 07:47:11

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2006-12-17 17:43:30

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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 <[email protected]>

---

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 <[email protected]>");
+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 */

2007-01-30 16:33:35

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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 <[email protected]>

---

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 <[email protected]>");
+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 */

2007-01-31 03:42:03

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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?

> +};

2007-01-31 10:39:27

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

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

2007-01-31 11:21:15

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill - Add support for input key to control wireless radio

> 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 <[email protected]>

----

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 <[email protected]>");
+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 */