rfkill: export global states to rfkill-input
[safe/jmp/linux-2.6] / net / rfkill / rfkill-input.c
index e5c840c..bfdade7 100644 (file)
@@ -16,6 +16,9 @@
 #include <linux/workqueue.h>
 #include <linux/init.h>
 #include <linux/rfkill.h>
+#include <linux/sched.h>
+
+#include "rfkill-input.h"
 
 MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
 MODULE_DESCRIPTION("Input layer to RF switch connector");
@@ -28,39 +31,62 @@ struct rfkill_task {
        spinlock_t lock; /* for accessing last and desired state */
        unsigned long last; /* last schedule */
        enum rfkill_state desired_state; /* on/off */
-       enum rfkill_state current_state; /* on/off */
 };
 
 static void rfkill_task_handler(struct work_struct *work)
 {
        struct rfkill_task *task = container_of(work, struct rfkill_task, work);
-       enum rfkill_state state;
 
        mutex_lock(&task->mutex);
 
-       /*
-        * Use temp variable to fetch desired state to keep it
-        * consistent even if rfkill_schedule_toggle() runs in
-        * another thread or interrupts us.
-        */
-       state = task->desired_state;
+       rfkill_switch_all(task->type, task->desired_state);
+
+       mutex_unlock(&task->mutex);
+}
+
+static void rfkill_task_epo_handler(struct work_struct *work)
+{
+       rfkill_epo();
+}
+
+static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
+
+static void rfkill_schedule_epo(void)
+{
+       schedule_work(&epo_work);
+}
 
-       if (state != task->current_state) {
-               rfkill_switch_all(task->type, state);
-               task->current_state = state;
+static void rfkill_schedule_set(struct rfkill_task *task,
+                               enum rfkill_state desired_state)
+{
+       unsigned long flags;
+
+       if (unlikely(work_pending(&epo_work)))
+               return;
+
+       spin_lock_irqsave(&task->lock, flags);
+
+       if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
+               task->desired_state = desired_state;
+               task->last = jiffies;
+               schedule_work(&task->work);
        }
 
-       mutex_unlock(&task->mutex);
+       spin_unlock_irqrestore(&task->lock, flags);
 }
 
 static void rfkill_schedule_toggle(struct rfkill_task *task)
 {
-       unsigned int flags;
+       unsigned long flags;
+
+       if (unlikely(work_pending(&epo_work)))
+               return;
 
        spin_lock_irqsave(&task->lock, flags);
 
        if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
-               task->desired_state = !task->desired_state;
+               task->desired_state =
+                               rfkill_state_complement(task->desired_state);
                task->last = jiffies;
                schedule_work(&task->work);
        }
@@ -68,24 +94,45 @@ static void rfkill_schedule_toggle(struct rfkill_task *task)
        spin_unlock_irqrestore(&task->lock, flags);
 }
 
-#define DEFINE_RFKILL_TASK(n, t)                       \
-       struct rfkill_task n = {                        \
-               .work = __WORK_INITIALIZER(n.work,      \
-                               rfkill_task_handler),   \
-               .type = t,                              \
-               .mutex = __MUTEX_INITIALIZER(n.mutex),  \
-               .lock = __SPIN_LOCK_UNLOCKED(n.lock),   \
-               .desired_state = RFKILL_STATE_ON,       \
-               .current_state = RFKILL_STATE_ON,       \
+#define DEFINE_RFKILL_TASK(n, t)                               \
+       struct rfkill_task n = {                                \
+               .work = __WORK_INITIALIZER(n.work,              \
+                               rfkill_task_handler),           \
+               .type = t,                                      \
+               .mutex = __MUTEX_INITIALIZER(n.mutex),          \
+               .lock = __SPIN_LOCK_UNLOCKED(n.lock),           \
+               .desired_state = RFKILL_STATE_UNBLOCKED,        \
        }
 
 static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
 static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
+static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
+static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
+static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
+
+static void rfkill_schedule_evsw_rfkillall(int state)
+{
+       /* EVERY radio type. state != 0 means radios ON */
+       /* handle EPO (emergency power off) through shortcut */
+       if (state) {
+               rfkill_schedule_set(&rfkill_wwan,
+                                   RFKILL_STATE_UNBLOCKED);
+               rfkill_schedule_set(&rfkill_wimax,
+                                   RFKILL_STATE_UNBLOCKED);
+               rfkill_schedule_set(&rfkill_uwb,
+                                   RFKILL_STATE_UNBLOCKED);
+               rfkill_schedule_set(&rfkill_bt,
+                                   RFKILL_STATE_UNBLOCKED);
+               rfkill_schedule_set(&rfkill_wlan,
+                                   RFKILL_STATE_UNBLOCKED);
+       } else
+               rfkill_schedule_epo();
+}
 
 static void rfkill_event(struct input_handle *handle, unsigned int type,
-                       unsigned int code, int down)
+                       unsigned int code, int data)
 {
-       if (type == EV_KEY && down == 1) {
+       if (type == EV_KEY && data == 1) {
                switch (code) {
                case KEY_WLAN:
                        rfkill_schedule_toggle(&rfkill_wlan);
@@ -93,6 +140,20 @@ static void rfkill_event(struct input_handle *handle, unsigned int type,
                case KEY_BLUETOOTH:
                        rfkill_schedule_toggle(&rfkill_bt);
                        break;
+               case KEY_UWB:
+                       rfkill_schedule_toggle(&rfkill_uwb);
+                       break;
+               case KEY_WIMAX:
+                       rfkill_schedule_toggle(&rfkill_wimax);
+                       break;
+               default:
+                       break;
+               }
+       } else if (type == EV_SW) {
+               switch (code) {
+               case SW_RFKILL_ALL:
+                       rfkill_schedule_evsw_rfkillall(data);
+                       break;
                default:
                        break;
                }
@@ -113,6 +174,7 @@ static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
        handle->handler = handler;
        handle->name = "rfkill";
 
+       /* causes rfkill_start() to be called */
        error = input_register_handle(handle);
        if (error)
                goto err_free_handle;
@@ -130,6 +192,23 @@ static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
        return error;
 }
 
+static void rfkill_start(struct input_handle *handle)
+{
+       /* Take event_lock to guard against configuration changes, we
+        * should be able to deal with concurrency with rfkill_event()
+        * just fine (which event_lock will also avoid). */
+       spin_lock_irq(&handle->dev->event_lock);
+
+       if (test_bit(EV_SW, handle->dev->evbit)) {
+               if (test_bit(SW_RFKILL_ALL, handle->dev->swbit))
+                       rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
+                                                       handle->dev->sw));
+               /* add resync for further EV_SW events here */
+       }
+
+       spin_unlock_irq(&handle->dev->event_lock);
+}
+
 static void rfkill_disconnect(struct input_handle *handle)
 {
        input_close_device(handle);
@@ -140,13 +219,28 @@ static void rfkill_disconnect(struct input_handle *handle)
 static const struct input_device_id rfkill_ids[] = {
        {
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
-               .evbit = { BIT(EV_KEY) },
-               .keybit = { [LONG(KEY_WLAN)] = BIT(KEY_WLAN) },
+               .evbit = { BIT_MASK(EV_KEY) },
+               .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) },
+       },
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+               .evbit = { BIT_MASK(EV_KEY) },
+               .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) },
        },
        {
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
-               .evbit = { BIT(EV_KEY) },
-               .keybit = { [LONG(KEY_BLUETOOTH)] = BIT(KEY_BLUETOOTH) },
+               .evbit = { BIT_MASK(EV_KEY) },
+               .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) },
+       },
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+               .evbit = { BIT_MASK(EV_KEY) },
+               .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
+       },
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
+               .evbit = { BIT(EV_SW) },
+               .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
        },
        { }
 };
@@ -155,12 +249,18 @@ static struct input_handler rfkill_handler = {
        .event =        rfkill_event,
        .connect =      rfkill_connect,
        .disconnect =   rfkill_disconnect,
+       .start =        rfkill_start,
        .name =         "rfkill",
        .id_table =     rfkill_ids,
 };
 
 static int __init rfkill_handler_init(void)
 {
+       unsigned long last_run = jiffies - msecs_to_jiffies(500);
+       rfkill_wlan.last = last_run;
+       rfkill_bt.last = last_run;
+       rfkill_uwb.last = last_run;
+       rfkill_wimax.last = last_run;
        return input_register_handler(&rfkill_handler);
 }