diff options
Diffstat (limited to 'net/rfkill/rfkill-input.c')
| -rw-r--r-- | net/rfkill/rfkill-input.c | 337 |
1 files changed, 261 insertions, 76 deletions
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index bfdade72e066..84efde97c5a7 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c | |||
| @@ -24,138 +24,318 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); | |||
| 24 | MODULE_DESCRIPTION("Input layer to RF switch connector"); | 24 | MODULE_DESCRIPTION("Input layer to RF switch connector"); |
| 25 | MODULE_LICENSE("GPL"); | 25 | MODULE_LICENSE("GPL"); |
| 26 | 26 | ||
| 27 | enum rfkill_input_master_mode { | ||
| 28 | RFKILL_INPUT_MASTER_DONOTHING = 0, | ||
| 29 | RFKILL_INPUT_MASTER_RESTORE = 1, | ||
| 30 | RFKILL_INPUT_MASTER_UNBLOCKALL = 2, | ||
| 31 | RFKILL_INPUT_MASTER_MAX, /* marker */ | ||
| 32 | }; | ||
| 33 | |||
| 34 | /* Delay (in ms) between consecutive switch ops */ | ||
| 35 | #define RFKILL_OPS_DELAY 200 | ||
| 36 | |||
| 37 | static enum rfkill_input_master_mode rfkill_master_switch_mode = | ||
| 38 | RFKILL_INPUT_MASTER_UNBLOCKALL; | ||
| 39 | module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0); | ||
| 40 | MODULE_PARM_DESC(master_switch_mode, | ||
| 41 | "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all"); | ||
| 42 | |||
| 43 | enum rfkill_global_sched_op { | ||
| 44 | RFKILL_GLOBAL_OP_EPO = 0, | ||
| 45 | RFKILL_GLOBAL_OP_RESTORE, | ||
| 46 | RFKILL_GLOBAL_OP_UNLOCK, | ||
| 47 | RFKILL_GLOBAL_OP_UNBLOCK, | ||
| 48 | }; | ||
| 49 | |||
| 50 | /* | ||
| 51 | * Currently, the code marked with RFKILL_NEED_SWSET is inactive. | ||
| 52 | * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the | ||
| 53 | * future, when such events are added, that code will be necessary. | ||
| 54 | */ | ||
| 55 | |||
| 27 | struct rfkill_task { | 56 | struct rfkill_task { |
| 28 | struct work_struct work; | 57 | struct delayed_work dwork; |
| 29 | enum rfkill_type type; | 58 | |
| 30 | struct mutex mutex; /* ensures that task is serialized */ | 59 | /* ensures that task is serialized */ |
| 31 | spinlock_t lock; /* for accessing last and desired state */ | 60 | struct mutex mutex; |
| 32 | unsigned long last; /* last schedule */ | 61 | |
| 33 | enum rfkill_state desired_state; /* on/off */ | 62 | /* protects everything below */ |
| 63 | spinlock_t lock; | ||
| 64 | |||
| 65 | /* pending regular switch operations (1=pending) */ | ||
| 66 | unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; | ||
| 67 | |||
| 68 | #ifdef RFKILL_NEED_SWSET | ||
| 69 | /* set operation pending (1=pending) */ | ||
| 70 | unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; | ||
| 71 | |||
| 72 | /* desired state for pending set operation (1=unblock) */ | ||
| 73 | unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; | ||
| 74 | #endif | ||
| 75 | |||
| 76 | /* should the state be complemented (1=yes) */ | ||
| 77 | unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; | ||
| 78 | |||
| 79 | bool global_op_pending; | ||
| 80 | enum rfkill_global_sched_op op; | ||
| 81 | |||
| 82 | /* last time it was scheduled */ | ||
| 83 | unsigned long last_scheduled; | ||
| 34 | }; | 84 | }; |
| 35 | 85 | ||
| 86 | static void __rfkill_handle_global_op(enum rfkill_global_sched_op op) | ||
| 87 | { | ||
| 88 | unsigned int i; | ||
| 89 | |||
| 90 | switch (op) { | ||
| 91 | case RFKILL_GLOBAL_OP_EPO: | ||
| 92 | rfkill_epo(); | ||
| 93 | break; | ||
| 94 | case RFKILL_GLOBAL_OP_RESTORE: | ||
| 95 | rfkill_restore_states(); | ||
| 96 | break; | ||
| 97 | case RFKILL_GLOBAL_OP_UNLOCK: | ||
| 98 | rfkill_remove_epo_lock(); | ||
| 99 | break; | ||
| 100 | case RFKILL_GLOBAL_OP_UNBLOCK: | ||
| 101 | rfkill_remove_epo_lock(); | ||
| 102 | for (i = 0; i < RFKILL_TYPE_MAX; i++) | ||
| 103 | rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED); | ||
| 104 | break; | ||
| 105 | default: | ||
| 106 | /* memory corruption or bug, fail safely */ | ||
| 107 | rfkill_epo(); | ||
| 108 | WARN(1, "Unknown requested operation %d! " | ||
| 109 | "rfkill Emergency Power Off activated\n", | ||
| 110 | op); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | #ifdef RFKILL_NEED_SWSET | ||
| 115 | static void __rfkill_handle_normal_op(const enum rfkill_type type, | ||
| 116 | const bool sp, const bool s, const bool c) | ||
| 117 | { | ||
| 118 | enum rfkill_state state; | ||
| 119 | |||
| 120 | if (sp) | ||
| 121 | state = (s) ? RFKILL_STATE_UNBLOCKED : | ||
| 122 | RFKILL_STATE_SOFT_BLOCKED; | ||
| 123 | else | ||
| 124 | state = rfkill_get_global_state(type); | ||
| 125 | |||
| 126 | if (c) | ||
| 127 | state = rfkill_state_complement(state); | ||
| 128 | |||
| 129 | rfkill_switch_all(type, state); | ||
| 130 | } | ||
| 131 | #else | ||
| 132 | static void __rfkill_handle_normal_op(const enum rfkill_type type, | ||
| 133 | const bool c) | ||
| 134 | { | ||
| 135 | enum rfkill_state state; | ||
| 136 | |||
| 137 | state = rfkill_get_global_state(type); | ||
| 138 | if (c) | ||
| 139 | state = rfkill_state_complement(state); | ||
| 140 | |||
| 141 | rfkill_switch_all(type, state); | ||
| 142 | } | ||
| 143 | #endif | ||
| 144 | |||
| 36 | static void rfkill_task_handler(struct work_struct *work) | 145 | static void rfkill_task_handler(struct work_struct *work) |
| 37 | { | 146 | { |
| 38 | struct rfkill_task *task = container_of(work, struct rfkill_task, work); | 147 | struct rfkill_task *task = container_of(work, |
| 148 | struct rfkill_task, dwork.work); | ||
| 149 | bool doit = true; | ||
| 39 | 150 | ||
| 40 | mutex_lock(&task->mutex); | 151 | mutex_lock(&task->mutex); |
| 41 | 152 | ||
| 42 | rfkill_switch_all(task->type, task->desired_state); | 153 | spin_lock_irq(&task->lock); |
| 154 | while (doit) { | ||
| 155 | if (task->global_op_pending) { | ||
| 156 | enum rfkill_global_sched_op op = task->op; | ||
| 157 | task->global_op_pending = false; | ||
| 158 | memset(task->sw_pending, 0, sizeof(task->sw_pending)); | ||
| 159 | spin_unlock_irq(&task->lock); | ||
| 160 | |||
| 161 | __rfkill_handle_global_op(op); | ||
| 162 | |||
| 163 | /* make sure we do at least one pass with | ||
| 164 | * !task->global_op_pending */ | ||
| 165 | spin_lock_irq(&task->lock); | ||
| 166 | continue; | ||
| 167 | } else if (!rfkill_is_epo_lock_active()) { | ||
| 168 | unsigned int i = 0; | ||
| 169 | |||
| 170 | while (!task->global_op_pending && | ||
| 171 | i < RFKILL_TYPE_MAX) { | ||
| 172 | if (test_and_clear_bit(i, task->sw_pending)) { | ||
| 173 | bool c; | ||
| 174 | #ifdef RFKILL_NEED_SWSET | ||
| 175 | bool sp, s; | ||
| 176 | sp = test_and_clear_bit(i, | ||
| 177 | task->sw_setpending); | ||
| 178 | s = test_bit(i, task->sw_newstate); | ||
| 179 | #endif | ||
| 180 | c = test_and_clear_bit(i, | ||
| 181 | task->sw_togglestate); | ||
| 182 | spin_unlock_irq(&task->lock); | ||
| 183 | |||
| 184 | #ifdef RFKILL_NEED_SWSET | ||
| 185 | __rfkill_handle_normal_op(i, sp, s, c); | ||
| 186 | #else | ||
| 187 | __rfkill_handle_normal_op(i, c); | ||
| 188 | #endif | ||
| 189 | |||
| 190 | spin_lock_irq(&task->lock); | ||
| 191 | } | ||
| 192 | i++; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | doit = task->global_op_pending; | ||
| 196 | } | ||
| 197 | spin_unlock_irq(&task->lock); | ||
| 43 | 198 | ||
| 44 | mutex_unlock(&task->mutex); | 199 | mutex_unlock(&task->mutex); |
| 45 | } | 200 | } |
| 46 | 201 | ||
| 47 | static void rfkill_task_epo_handler(struct work_struct *work) | 202 | static struct rfkill_task rfkill_task = { |
| 203 | .dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork, | ||
| 204 | rfkill_task_handler), | ||
| 205 | .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex), | ||
| 206 | .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock), | ||
| 207 | }; | ||
| 208 | |||
| 209 | static unsigned long rfkill_ratelimit(const unsigned long last) | ||
| 48 | { | 210 | { |
| 49 | rfkill_epo(); | 211 | const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY); |
| 212 | return (time_after(jiffies, last + delay)) ? 0 : delay; | ||
| 50 | } | 213 | } |
| 51 | 214 | ||
| 52 | static DECLARE_WORK(epo_work, rfkill_task_epo_handler); | 215 | static void rfkill_schedule_ratelimited(void) |
| 216 | { | ||
| 217 | if (!delayed_work_pending(&rfkill_task.dwork)) { | ||
| 218 | schedule_delayed_work(&rfkill_task.dwork, | ||
| 219 | rfkill_ratelimit(rfkill_task.last_scheduled)); | ||
| 220 | rfkill_task.last_scheduled = jiffies; | ||
| 221 | } | ||
| 222 | } | ||
| 53 | 223 | ||
| 54 | static void rfkill_schedule_epo(void) | 224 | static void rfkill_schedule_global_op(enum rfkill_global_sched_op op) |
| 55 | { | 225 | { |
| 56 | schedule_work(&epo_work); | 226 | unsigned long flags; |
| 227 | |||
| 228 | spin_lock_irqsave(&rfkill_task.lock, flags); | ||
| 229 | rfkill_task.op = op; | ||
| 230 | rfkill_task.global_op_pending = true; | ||
| 231 | if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) { | ||
| 232 | /* bypass the limiter for EPO */ | ||
| 233 | cancel_delayed_work(&rfkill_task.dwork); | ||
| 234 | schedule_delayed_work(&rfkill_task.dwork, 0); | ||
| 235 | rfkill_task.last_scheduled = jiffies; | ||
| 236 | } else | ||
| 237 | rfkill_schedule_ratelimited(); | ||
| 238 | spin_unlock_irqrestore(&rfkill_task.lock, flags); | ||
| 57 | } | 239 | } |
| 58 | 240 | ||
| 59 | static void rfkill_schedule_set(struct rfkill_task *task, | 241 | #ifdef RFKILL_NEED_SWSET |
| 242 | /* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */ | ||
| 243 | |||
| 244 | static void rfkill_schedule_set(enum rfkill_type type, | ||
| 60 | enum rfkill_state desired_state) | 245 | enum rfkill_state desired_state) |
| 61 | { | 246 | { |
| 62 | unsigned long flags; | 247 | unsigned long flags; |
| 63 | 248 | ||
| 64 | if (unlikely(work_pending(&epo_work))) | 249 | if (rfkill_is_epo_lock_active()) |
| 65 | return; | 250 | return; |
| 66 | 251 | ||
| 67 | spin_lock_irqsave(&task->lock, flags); | 252 | spin_lock_irqsave(&rfkill_task.lock, flags); |
| 68 | 253 | if (!rfkill_task.global_op_pending) { | |
| 69 | if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { | 254 | set_bit(type, rfkill_task.sw_pending); |
| 70 | task->desired_state = desired_state; | 255 | set_bit(type, rfkill_task.sw_setpending); |
| 71 | task->last = jiffies; | 256 | clear_bit(type, rfkill_task.sw_togglestate); |
| 72 | schedule_work(&task->work); | 257 | if (desired_state) |
| 258 | set_bit(type, rfkill_task.sw_newstate); | ||
| 259 | else | ||
| 260 | clear_bit(type, rfkill_task.sw_newstate); | ||
| 261 | rfkill_schedule_ratelimited(); | ||
| 73 | } | 262 | } |
| 74 | 263 | spin_unlock_irqrestore(&rfkill_task.lock, flags); | |
| 75 | spin_unlock_irqrestore(&task->lock, flags); | ||
| 76 | } | 264 | } |
| 265 | #endif | ||
| 77 | 266 | ||
| 78 | static void rfkill_schedule_toggle(struct rfkill_task *task) | 267 | static void rfkill_schedule_toggle(enum rfkill_type type) |
| 79 | { | 268 | { |
| 80 | unsigned long flags; | 269 | unsigned long flags; |
| 81 | 270 | ||
| 82 | if (unlikely(work_pending(&epo_work))) | 271 | if (rfkill_is_epo_lock_active()) |
| 83 | return; | 272 | return; |
| 84 | 273 | ||
| 85 | spin_lock_irqsave(&task->lock, flags); | 274 | spin_lock_irqsave(&rfkill_task.lock, flags); |
| 86 | 275 | if (!rfkill_task.global_op_pending) { | |
| 87 | if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { | 276 | set_bit(type, rfkill_task.sw_pending); |
| 88 | task->desired_state = | 277 | change_bit(type, rfkill_task.sw_togglestate); |
| 89 | rfkill_state_complement(task->desired_state); | 278 | rfkill_schedule_ratelimited(); |
| 90 | task->last = jiffies; | ||
| 91 | schedule_work(&task->work); | ||
| 92 | } | 279 | } |
| 93 | 280 | spin_unlock_irqrestore(&rfkill_task.lock, flags); | |
| 94 | spin_unlock_irqrestore(&task->lock, flags); | ||
| 95 | } | 281 | } |
| 96 | 282 | ||
| 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 | |||
| 107 | static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); | ||
| 108 | static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); | ||
| 109 | static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); | ||
| 110 | static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); | ||
| 111 | static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN); | ||
| 112 | |||
| 113 | static void rfkill_schedule_evsw_rfkillall(int state) | 283 | static void rfkill_schedule_evsw_rfkillall(int state) |
| 114 | { | 284 | { |
| 115 | /* EVERY radio type. state != 0 means radios ON */ | ||
| 116 | /* handle EPO (emergency power off) through shortcut */ | ||
| 117 | if (state) { | 285 | if (state) { |
| 118 | rfkill_schedule_set(&rfkill_wwan, | 286 | switch (rfkill_master_switch_mode) { |
| 119 | RFKILL_STATE_UNBLOCKED); | 287 | case RFKILL_INPUT_MASTER_UNBLOCKALL: |
| 120 | rfkill_schedule_set(&rfkill_wimax, | 288 | rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK); |
| 121 | RFKILL_STATE_UNBLOCKED); | 289 | break; |
| 122 | rfkill_schedule_set(&rfkill_uwb, | 290 | case RFKILL_INPUT_MASTER_RESTORE: |
| 123 | RFKILL_STATE_UNBLOCKED); | 291 | rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE); |
| 124 | rfkill_schedule_set(&rfkill_bt, | 292 | break; |
| 125 | RFKILL_STATE_UNBLOCKED); | 293 | case RFKILL_INPUT_MASTER_DONOTHING: |
| 126 | rfkill_schedule_set(&rfkill_wlan, | 294 | rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK); |
| 127 | RFKILL_STATE_UNBLOCKED); | 295 | break; |
| 296 | default: | ||
| 297 | /* memory corruption or driver bug! fail safely */ | ||
| 298 | rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); | ||
| 299 | WARN(1, "Unknown rfkill_master_switch_mode (%d), " | ||
| 300 | "driver bug or memory corruption detected!\n", | ||
| 301 | rfkill_master_switch_mode); | ||
| 302 | break; | ||
| 303 | } | ||
| 128 | } else | 304 | } else |
| 129 | rfkill_schedule_epo(); | 305 | rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); |
| 130 | } | 306 | } |
| 131 | 307 | ||
| 132 | static void rfkill_event(struct input_handle *handle, unsigned int type, | 308 | static void rfkill_event(struct input_handle *handle, unsigned int type, |
| 133 | unsigned int code, int data) | 309 | unsigned int code, int data) |
| 134 | { | 310 | { |
| 135 | if (type == EV_KEY && data == 1) { | 311 | if (type == EV_KEY && data == 1) { |
| 312 | enum rfkill_type t; | ||
| 313 | |||
| 136 | switch (code) { | 314 | switch (code) { |
| 137 | case KEY_WLAN: | 315 | case KEY_WLAN: |
| 138 | rfkill_schedule_toggle(&rfkill_wlan); | 316 | t = RFKILL_TYPE_WLAN; |
| 139 | break; | 317 | break; |
| 140 | case KEY_BLUETOOTH: | 318 | case KEY_BLUETOOTH: |
| 141 | rfkill_schedule_toggle(&rfkill_bt); | 319 | t = RFKILL_TYPE_BLUETOOTH; |
| 142 | break; | 320 | break; |
| 143 | case KEY_UWB: | 321 | case KEY_UWB: |
| 144 | rfkill_schedule_toggle(&rfkill_uwb); | 322 | t = RFKILL_TYPE_UWB; |
| 145 | break; | 323 | break; |
| 146 | case KEY_WIMAX: | 324 | case KEY_WIMAX: |
| 147 | rfkill_schedule_toggle(&rfkill_wimax); | 325 | t = RFKILL_TYPE_WIMAX; |
| 148 | break; | 326 | break; |
| 149 | default: | 327 | default: |
| 150 | break; | 328 | return; |
| 151 | } | 329 | } |
| 330 | rfkill_schedule_toggle(t); | ||
| 331 | return; | ||
| 152 | } else if (type == EV_SW) { | 332 | } else if (type == EV_SW) { |
| 153 | switch (code) { | 333 | switch (code) { |
| 154 | case SW_RFKILL_ALL: | 334 | case SW_RFKILL_ALL: |
| 155 | rfkill_schedule_evsw_rfkillall(data); | 335 | rfkill_schedule_evsw_rfkillall(data); |
| 156 | break; | 336 | return; |
| 157 | default: | 337 | default: |
| 158 | break; | 338 | return; |
| 159 | } | 339 | } |
| 160 | } | 340 | } |
| 161 | } | 341 | } |
| @@ -256,18 +436,23 @@ static struct input_handler rfkill_handler = { | |||
| 256 | 436 | ||
| 257 | static int __init rfkill_handler_init(void) | 437 | static int __init rfkill_handler_init(void) |
| 258 | { | 438 | { |
| 259 | unsigned long last_run = jiffies - msecs_to_jiffies(500); | 439 | if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX) |
| 260 | rfkill_wlan.last = last_run; | 440 | return -EINVAL; |
| 261 | rfkill_bt.last = last_run; | 441 | |
| 262 | rfkill_uwb.last = last_run; | 442 | /* |
| 263 | rfkill_wimax.last = last_run; | 443 | * The penalty to not doing this is a possible RFKILL_OPS_DELAY delay |
| 444 | * at the first use. Acceptable, but if we can avoid it, why not? | ||
| 445 | */ | ||
| 446 | rfkill_task.last_scheduled = | ||
| 447 | jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1; | ||
| 264 | return input_register_handler(&rfkill_handler); | 448 | return input_register_handler(&rfkill_handler); |
| 265 | } | 449 | } |
| 266 | 450 | ||
| 267 | static void __exit rfkill_handler_exit(void) | 451 | static void __exit rfkill_handler_exit(void) |
| 268 | { | 452 | { |
| 269 | input_unregister_handler(&rfkill_handler); | 453 | input_unregister_handler(&rfkill_handler); |
| 270 | flush_scheduled_work(); | 454 | cancel_delayed_work_sync(&rfkill_task.dwork); |
| 455 | rfkill_remove_epo_lock(); | ||
| 271 | } | 456 | } |
| 272 | 457 | ||
| 273 | module_init(rfkill_handler_init); | 458 | module_init(rfkill_handler_init); |
