Hi,

I have been quite busy lately, hence the reason for this late continuance
of the Hardware button support for Wireless cards discussion.
I have CC'ed the people who discussed this in earlier threads.

With the suggestions made by Vojtech Pavlik I have created the rfkill driver,
for which I would like to know if this is the acceptable approach.

This rfkill driver looks a bit like the previous version I had send,
but this version does pay attention if the input device has been opened
and if an event should be send to userspace in that case.

I was however unsure about where the KEY_RFKILL should be added
in linux/input.h. I again placed this driver in drivers/input/misc but perhaps
this should go into drivers/net instead?

I hope I am on the right track. :)

Signed-off-by Ivo van Doorn <[EMAIL PROTECTED]>

---
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 4bad588..837021a 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..da576e5
--- /dev/null
+++ b/drivers/input/misc/rfkill.c
@@ -0,0 +1,209 @@
+/*
+       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/timer.h>
+#include <linux/rfkill.h>
+
+#include <asm/atomic.h>
+
+MODULE_AUTHOR("Ivo van Doorn <[EMAIL PROTECTED]>");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("RF button support");
+MODULE_LICENSE("GPL");
+
+/*
+ * List of all registered buttons.
+ */
+static struct list_head rfkill_list;
+static spinlock_t rfkill_list_lock;
+
+/*
+ * Polling timer, poll_delay and use count.
+ */
+static struct timer_list poll_timer;
+static atomic_t poll_required;
+
+static void rfkill_toggle_radio(int new_status)
+{
+       struct list_head *entry;
+       struct rfkill *rfkill;
+
+       /*
+        * Go through the list of all radio's to toggle the radio state.
+        */
+       list_for_each(entry, &rfkill_list) {
+               rfkill =  list_entry(entry, struct rfkill, entry);
+
+               rfkill->current_status = new_status;
+
+               /*
+               * If the input_device has been opened
+               * all radio events should be send to user space.
+               */
+               if (rfkill->input_dev->users) {
+                       input_report_key(rfkill->input_dev,
+                               KEY_RFKILL, new_status);
+                       input_sync(rfkill->input_dev);
+               }
+       
+               /*
+               * If the hardware does not toggle the radio status automaticly,
+               * we should take care of it.
+               */
+               if (new_status && rfkill->enable_radio)
+                       rfkill->enable_radio(rfkill->data);
+               else if (!new_status && rfkill->disable_radio)
+                       rfkill->disable_radio(rfkill->data);
+       }
+}
+
+static void rfkill_poll_button(unsigned long data)
+{
+       struct list_head *entry;
+       struct rfkill *rfkill;
+       int status;
+
+       spin_lock(&rfkill_list_lock);
+
+       list_for_each(entry, &rfkill_list) {
+               rfkill =  list_entry(entry, struct rfkill, entry);
+
+               if (!rfkill->poll)
+                       continue;
+
+               status = !!rfkill->poll(rfkill->data);
+
+               if (status != rfkill->current_status) {
+                       rfkill_toggle_radio(status);
+                       break;
+               }
+       }
+
+       spin_unlock(&rfkill_list_lock);
+
+       /*
+        * Check if polling is still required.
+        */
+       if (atomic_read(&poll_required)) {
+               poll_timer.expires = jiffies + RFKILL_POLL_DELAY;
+               add_timer(&poll_timer);
+       }
+}
+
+void rfkill_button_event(struct rfkill *rfkill, int status)
+{
+       if (status != rfkill->current_status) {
+               spin_lock(&rfkill_list_lock);
+               rfkill_toggle_radio(status);
+               spin_unlock(&rfkill_list_lock);
+       }
+}
+EXPORT_SYMBOL(rfkill_button_event);
+
+int rfkill_add_device(struct rfkill *rfkill)
+{
+       int status;
+
+       /*
+        * Allocate, initialize and register input device.
+        */
+       rfkill->input_dev = input_allocate_device();
+       if (!rfkill->input_dev) {
+               printk(KERN_ERR "Failed to allocate input device %s.\n",
+                       rfkill->dev_name);
+               return -ENOMEM;
+       }
+
+       rfkill->input_dev->name = "Radio button";
+       rfkill->input_dev->phys = rfkill->dev_name;
+       rfkill->input_dev->id.bustype = BUS_HOST;
+       set_bit(KEY_RFKILL, rfkill->input_dev->keybit);
+
+       status = input_register_device(rfkill->input_dev);
+       if (status) {
+               printk(KERN_ERR "Failed to register input device %s.\n",
+                       rfkill->dev_name);
+               input_free_device(rfkill->input_dev);
+               return status;
+       }
+
+       INIT_LIST_HEAD(&rfkill->entry);
+
+       spin_lock(&rfkill_list_lock);
+       list_add(&rfkill->entry, &rfkill_list);
+       spin_unlock(&rfkill_list_lock);
+
+       /*
+        * If polling is required the poll_required counter should be
+        * increased. If it was previously 0 we should start the polling timer.
+        */
+       if (rfkill->poll) {
+               if (!atomic_read(&poll_required)) {
+                       poll_timer.expires = jiffies + RFKILL_POLL_DELAY;
+                       add_timer(&poll_timer);
+               }
+               atomic_inc(&poll_required);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(rfkill_add_device);
+
+void rfkill_del_device(struct rfkill *rfkill)
+{
+       spin_lock(&rfkill_list_lock);
+
+       /*
+        * Check if this button required polling and if this
+        * was the last button that required polling.
+        */
+       if (rfkill->poll && atomic_dec_and_test(&poll_required))
+               del_timer(&poll_timer);
+
+       list_del(&rfkill->entry);
+
+       spin_unlock(&rfkill_list_lock);
+}
+EXPORT_SYMBOL(rfkill_del_device);
+
+static int __init radiobtn_init(void)
+{
+       printk(KERN_INFO "Loading rfkill driver.\n");
+
+       INIT_LIST_HEAD(&rfkill_list);
+       spin_lock_init(&rfkill_list_lock);
+
+       init_timer(&poll_timer);
+       poll_timer.function = rfkill_poll_button;
+       poll_timer.data = 0;
+       atomic_set(&poll_required, 0);
+
+       return 0;
+}
+
+static void __exit radiobtn_exit(void)
+{
+       printk(KERN_INFO "Unloading rfkill driver.\n");
+}
+
+module_init(radiobtn_init);
+module_exit(radiobtn_exit);
diff --git a/include/linux/input.h b/include/linux/input.h
index ce1a756..d460620 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -486,6 +486,7 @@ #define KEY_PREVIOUS                0x19c
#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..2e1c740
--- /dev/null
+++ b/include/linux/rfkill.h
@@ -0,0 +1,98 @@
+/*
+       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 button to enable or
+       disable the radio of the network device attached to that button.
+       This network device usually is an integrated wireless network device,
+       or bluetooth device.
+       Some of these devices will disable the radio automaticly when the
+       RF button has been pressed, while other devices need to be polled
+       for the RF button status.
+       But in all cases the only interface that will have its radio disabled
+       will be the device that has the RF button attached to it. But it could
+       be desired that userspace performs this disabling of all radios in case
+       there are also interfaces without a RF button that need to be disabled.
+
+       The rfkill driver will contain a list of all devices with a RF button,
+       hardware drivers need to register their hardware to teh rfkill
+       interface it can take care of everything. If the RF button requires
+       polling to obtain the status this will be handled by rfkill as well.
+       Once the status of the button has changed and the hardware does not
+       automaticly enable or disable the radio rfkill provides with 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/list.h>
+#include <linux/input.h>
+
+#define RFKILL_POLL_DELAY      ( HZ / 10 ) /* 100 ms */
+
+/**
+ * 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.
+ * @data: Private data which will be passed along with the radio and polling
+ *     handlers.
+ * @poll(unsigned long data): Optional handler which will frequently be
+ *     called to determine the current status of the RF button.
+ * @enable_radio(unsigned long data): Optional handler to enable the radio
+ *     once the RF button has been pressed and the hardware does enable
+ *     the radio automaticly.
+ * @disable_radio(unsigned long data): Optional handler to disable the radio
+ *     once the RF button has been pressed and the hardware does disable
+ *     the radio automaticly.
+ * @current_status: Contains the current status of the radio as it was
+ *     previously indicated by the radio. This field may only be changed
+ *     by the driver at initialization time.
+ */
+struct rfkill {
+       const char *dev_name;
+
+       unsigned long data;
+
+       int (*poll)(unsigned long data);
+       void (*enable_radio)(unsigned long data);
+       void (*disable_radio)(unsigned long data);
+
+       unsigned int current_status;
+
+       /*
+        * These fields are private to rfkill, and
+        * should not be used by the RF button driver.
+        */
+       struct list_head entry;
+       struct input_dev *input_dev;
+};
+
+void rfkill_button_event(struct rfkill *rfkill, int status);
+int rfkill_add_device(struct rfkill *rfkill);
+void rfkill_del_device(struct rfkill *rfkill);
+
+#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

Reply via email to