linux/net/rfkill/rfkill-input.c
<<
>>
Prefs
   1/*
   2 * Input layer to RF Kill interface connector
   3 *
   4 * Copyright (c) 2007 Dmitry Torokhov
   5 */
   6
   7/*
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License version 2 as published
  10 * by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/input.h>
  15#include <linux/slab.h>
  16#include <linux/workqueue.h>
  17#include <linux/init.h>
  18#include <linux/rfkill.h>
  19#include <linux/sched.h>
  20
  21#include "rfkill-input.h"
  22
  23MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
  24MODULE_DESCRIPTION("Input layer to RF switch connector");
  25MODULE_LICENSE("GPL");
  26
  27struct rfkill_task {
  28        struct work_struct work;
  29        enum rfkill_type type;
  30        struct mutex mutex; /* ensures that task is serialized */
  31        spinlock_t lock; /* for accessing last and desired state */
  32        unsigned long last; /* last schedule */
  33        enum rfkill_state desired_state; /* on/off */
  34};
  35
  36static void rfkill_task_handler(struct work_struct *work)
  37{
  38        struct rfkill_task *task = container_of(work, struct rfkill_task, work);
  39
  40        mutex_lock(&task->mutex);
  41
  42        rfkill_switch_all(task->type, task->desired_state);
  43
  44        mutex_unlock(&task->mutex);
  45}
  46
  47static void rfkill_task_epo_handler(struct work_struct *work)
  48{
  49        rfkill_epo();
  50}
  51
  52static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
  53
  54static void rfkill_schedule_epo(void)
  55{
  56        schedule_work(&epo_work);
  57}
  58
  59static void rfkill_schedule_set(struct rfkill_task *task,
  60                                enum rfkill_state desired_state)
  61{
  62        unsigned long flags;
  63
  64        if (unlikely(work_pending(&epo_work)))
  65                return;
  66
  67        spin_lock_irqsave(&task->lock, flags);
  68
  69        if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
  70                task->desired_state = desired_state;
  71                task->last = jiffies;
  72                schedule_work(&task->work);
  73        }
  74
  75        spin_unlock_irqrestore(&task->lock, flags);
  76}
  77
  78static void rfkill_schedule_toggle(struct rfkill_task *task)
  79{
  80        unsigned long flags;
  81
  82        if (unlikely(work_pending(&epo_work)))
  83                return;
  84
  85        spin_lock_irqsave(&task->lock, flags);
  86
  87        if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
  88                task->desired_state =
  89                                rfkill_state_complement(task->desired_state);
  90                task->last = jiffies;
  91                schedule_work(&task->work);
  92        }
  93
  94        spin_unlock_irqrestore(&task->lock, flags);
  95}
  96
  97#define DEFINE_RFKILL_TASK(n, t)                                \
  98        struct rfkill_task n = {                                \
  99                .work = __WORK_INITIALIZER(n.work,              \
 100                                rfkill_task_handler),           \
 101                .type = t,                                      \
 102                .mutex = __MUTEX_INITIALIZER(n.mutex),          \
 103                .lock = __SPIN_LOCK_UNLOCKED(n.lock),           \
 104                .desired_state = RFKILL_STATE_UNBLOCKED,        \
 105        }
 106
 107static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
 108static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
 109static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
 110static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
 111static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
 112
 113static void rfkill_schedule_evsw_rfkillall(int state)
 114{
 115        /* EVERY radio type. state != 0 means radios ON */
 116        /* handle EPO (emergency power off) through shortcut */
 117        if (state) {
 118                rfkill_schedule_set(&rfkill_wwan,
 119                                    RFKILL_STATE_UNBLOCKED);
 120                rfkill_schedule_set(&rfkill_wimax,
 121                                    RFKILL_STATE_UNBLOCKED);
 122                rfkill_schedule_set(&rfkill_uwb,
 123                                    RFKILL_STATE_UNBLOCKED);
 124                rfkill_schedule_set(&rfkill_bt,
 125                                    RFKILL_STATE_UNBLOCKED);
 126                rfkill_schedule_set(&rfkill_wlan,
 127                                    RFKILL_STATE_UNBLOCKED);
 128        } else
 129                rfkill_schedule_epo();
 130}
 131
 132static void rfkill_event(struct input_handle *handle, unsigned int type,
 133                        unsigned int code, int data)
 134{
 135        if (type == EV_KEY && data == 1) {
 136                switch (code) {
 137                case KEY_WLAN:
 138                        rfkill_schedule_toggle(&rfkill_wlan);
 139                        break;
 140                case KEY_BLUETOOTH:
 141                        rfkill_schedule_toggle(&rfkill_bt);
 142                        break;
 143                case KEY_UWB:
 144                        rfkill_schedule_toggle(&rfkill_uwb);
 145                        break;
 146                case KEY_WIMAX:
 147                        rfkill_schedule_toggle(&rfkill_wimax);
 148                        break;
 149                default:
 150                        break;
 151                }
 152        } else if (type == EV_SW) {
 153                switch (code) {
 154                case SW_RFKILL_ALL:
 155                        rfkill_schedule_evsw_rfkillall(data);
 156                        break;
 157                default:
 158                        break;
 159                }
 160        }
 161}
 162
 163static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
 164                          const struct input_device_id *id)
 165{
 166        struct input_handle *handle;
 167        int error;
 168
 169        handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
 170        if (!handle)
 171                return -ENOMEM;
 172
 173        handle->dev = dev;
 174        handle->handler = handler;
 175        handle->name = "rfkill";
 176
 177        /* causes rfkill_start() to be called */
 178        error = input_register_handle(handle);
 179        if (error)
 180                goto err_free_handle;
 181
 182        error = input_open_device(handle);
 183        if (error)
 184                goto err_unregister_handle;
 185
 186        return 0;
 187
 188 err_unregister_handle:
 189        input_unregister_handle(handle);
 190 err_free_handle:
 191        kfree(handle);
 192        return error;
 193}
 194
 195static void rfkill_start(struct input_handle *handle)
 196{
 197        /* Take event_lock to guard against configuration changes, we
 198         * should be able to deal with concurrency with rfkill_event()
 199         * just fine (which event_lock will also avoid). */
 200        spin_lock_irq(&handle->dev->event_lock);
 201
 202        if (test_bit(EV_SW, handle->dev->evbit)) {
 203                if (test_bit(SW_RFKILL_ALL, handle->dev->swbit))
 204                        rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
 205                                                        handle->dev->sw));
 206                /* add resync for further EV_SW events here */
 207        }
 208
 209        spin_unlock_irq(&handle->dev->event_lock);
 210}
 211
 212static void rfkill_disconnect(struct input_handle *handle)
 213{
 214        input_close_device(handle);
 215        input_unregister_handle(handle);
 216        kfree(handle);
 217}
 218
 219static const struct input_device_id rfkill_ids[] = {
 220        {
 221                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
 222                .evbit = { BIT_MASK(EV_KEY) },
 223                .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) },
 224        },
 225        {
 226                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
 227                .evbit = { BIT_MASK(EV_KEY) },
 228                .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) },
 229        },
 230        {
 231                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
 232                .evbit = { BIT_MASK(EV_KEY) },
 233                .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) },
 234        },
 235        {
 236                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
 237                .evbit = { BIT_MASK(EV_KEY) },
 238                .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
 239        },
 240        {
 241                .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
 242                .evbit = { BIT(EV_SW) },
 243                .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
 244        },
 245        { }
 246};
 247
 248static struct input_handler rfkill_handler = {
 249        .event =        rfkill_event,
 250        .connect =      rfkill_connect,
 251        .disconnect =   rfkill_disconnect,
 252        .start =        rfkill_start,
 253        .name =         "rfkill",
 254        .id_table =     rfkill_ids,
 255};
 256
 257static int __init rfkill_handler_init(void)
 258{
 259        unsigned long last_run = jiffies - msecs_to_jiffies(500);
 260        rfkill_wlan.last = last_run;
 261        rfkill_bt.last = last_run;
 262        rfkill_uwb.last = last_run;
 263        rfkill_wimax.last = last_run;
 264        return input_register_handler(&rfkill_handler);
 265}
 266
 267static void __exit rfkill_handler_exit(void)
 268{
 269        input_unregister_handler(&rfkill_handler);
 270        flush_scheduled_work();
 271}
 272
 273module_init(rfkill_handler_init);
 274module_exit(rfkill_handler_exit);
 275
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.