xref: /linux/net/rfkill/input.c (revision ed7247f3)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
219d337dfSJohannes Berg /*
319d337dfSJohannes Berg  * Input layer to RF Kill interface connector
419d337dfSJohannes Berg  *
519d337dfSJohannes Berg  * Copyright (c) 2007 Dmitry Torokhov
619d337dfSJohannes Berg  * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
719d337dfSJohannes Berg  *
819d337dfSJohannes Berg  * If you ever run into a situation in which you have a SW_ type rfkill
919d337dfSJohannes Berg  * input device, then you can revive code that was removed in the patch
1019d337dfSJohannes Berg  * "rfkill-input: remove unused code".
1119d337dfSJohannes Berg  */
1219d337dfSJohannes Berg 
1319d337dfSJohannes Berg #include <linux/input.h>
1419d337dfSJohannes Berg #include <linux/slab.h>
15d9b93842SPaul Gortmaker #include <linux/moduleparam.h>
1619d337dfSJohannes Berg #include <linux/workqueue.h>
1719d337dfSJohannes Berg #include <linux/init.h>
1819d337dfSJohannes Berg #include <linux/rfkill.h>
1919d337dfSJohannes Berg #include <linux/sched.h>
2019d337dfSJohannes Berg 
2119d337dfSJohannes Berg #include "rfkill.h"
2219d337dfSJohannes Berg 
2319d337dfSJohannes Berg enum rfkill_input_master_mode {
2419d337dfSJohannes Berg 	RFKILL_INPUT_MASTER_UNLOCK = 0,
2519d337dfSJohannes Berg 	RFKILL_INPUT_MASTER_RESTORE = 1,
2619d337dfSJohannes Berg 	RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
2719d337dfSJohannes Berg 	NUM_RFKILL_INPUT_MASTER_MODES
2819d337dfSJohannes Berg };
2919d337dfSJohannes Berg 
3019d337dfSJohannes Berg /* Delay (in ms) between consecutive switch ops */
3119d337dfSJohannes Berg #define RFKILL_OPS_DELAY 200
3219d337dfSJohannes Berg 
3319d337dfSJohannes Berg static enum rfkill_input_master_mode rfkill_master_switch_mode =
3419d337dfSJohannes Berg 					RFKILL_INPUT_MASTER_UNBLOCKALL;
3519d337dfSJohannes Berg module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
3619d337dfSJohannes Berg MODULE_PARM_DESC(master_switch_mode,
3719d337dfSJohannes Berg 	"SW_RFKILL_ALL ON should: 0=do nothing (only unlock); 1=restore; 2=unblock all");
3819d337dfSJohannes Berg 
39*ed7247f3SGuobin Huang static DEFINE_SPINLOCK(rfkill_op_lock);
4019d337dfSJohannes Berg static bool rfkill_op_pending;
4119d337dfSJohannes Berg static unsigned long rfkill_sw_pending[BITS_TO_LONGS(NUM_RFKILL_TYPES)];
4219d337dfSJohannes Berg static unsigned long rfkill_sw_state[BITS_TO_LONGS(NUM_RFKILL_TYPES)];
4319d337dfSJohannes Berg 
4419d337dfSJohannes Berg enum rfkill_sched_op {
4519d337dfSJohannes Berg 	RFKILL_GLOBAL_OP_EPO = 0,
4619d337dfSJohannes Berg 	RFKILL_GLOBAL_OP_RESTORE,
4719d337dfSJohannes Berg 	RFKILL_GLOBAL_OP_UNLOCK,
4819d337dfSJohannes Berg 	RFKILL_GLOBAL_OP_UNBLOCK,
4919d337dfSJohannes Berg };
5019d337dfSJohannes Berg 
5119d337dfSJohannes Berg static enum rfkill_sched_op rfkill_master_switch_op;
5219d337dfSJohannes Berg static enum rfkill_sched_op rfkill_op;
5319d337dfSJohannes Berg 
__rfkill_handle_global_op(enum rfkill_sched_op op)5419d337dfSJohannes Berg static void __rfkill_handle_global_op(enum rfkill_sched_op op)
5519d337dfSJohannes Berg {
5619d337dfSJohannes Berg 	unsigned int i;
5719d337dfSJohannes Berg 
5819d337dfSJohannes Berg 	switch (op) {
5919d337dfSJohannes Berg 	case RFKILL_GLOBAL_OP_EPO:
6019d337dfSJohannes Berg 		rfkill_epo();
6119d337dfSJohannes Berg 		break;
6219d337dfSJohannes Berg 	case RFKILL_GLOBAL_OP_RESTORE:
6319d337dfSJohannes Berg 		rfkill_restore_states();
6419d337dfSJohannes Berg 		break;
6519d337dfSJohannes Berg 	case RFKILL_GLOBAL_OP_UNLOCK:
6619d337dfSJohannes Berg 		rfkill_remove_epo_lock();
6719d337dfSJohannes Berg 		break;
6819d337dfSJohannes Berg 	case RFKILL_GLOBAL_OP_UNBLOCK:
6919d337dfSJohannes Berg 		rfkill_remove_epo_lock();
7019d337dfSJohannes Berg 		for (i = 0; i < NUM_RFKILL_TYPES; i++)
7119d337dfSJohannes Berg 			rfkill_switch_all(i, false);
7219d337dfSJohannes Berg 		break;
7319d337dfSJohannes Berg 	default:
7419d337dfSJohannes Berg 		/* memory corruption or bug, fail safely */
7519d337dfSJohannes Berg 		rfkill_epo();
7619d337dfSJohannes Berg 		WARN(1, "Unknown requested operation %d! "
7719d337dfSJohannes Berg 			"rfkill Emergency Power Off activated\n",
7819d337dfSJohannes Berg 			op);
7919d337dfSJohannes Berg 	}
8019d337dfSJohannes Berg }
8119d337dfSJohannes Berg 
__rfkill_handle_normal_op(const enum rfkill_type type,const bool complement)8219d337dfSJohannes Berg static void __rfkill_handle_normal_op(const enum rfkill_type type,
8319d337dfSJohannes Berg 				      const bool complement)
8419d337dfSJohannes Berg {
8519d337dfSJohannes Berg 	bool blocked;
8619d337dfSJohannes Berg 
8719d337dfSJohannes Berg 	blocked = rfkill_get_global_sw_state(type);
8819d337dfSJohannes Berg 	if (complement)
8919d337dfSJohannes Berg 		blocked = !blocked;
9019d337dfSJohannes Berg 
9119d337dfSJohannes Berg 	rfkill_switch_all(type, blocked);
9219d337dfSJohannes Berg }
9319d337dfSJohannes Berg 
rfkill_op_handler(struct work_struct * work)9419d337dfSJohannes Berg static void rfkill_op_handler(struct work_struct *work)
9519d337dfSJohannes Berg {
9619d337dfSJohannes Berg 	unsigned int i;
9719d337dfSJohannes Berg 	bool c;
9819d337dfSJohannes Berg 
9919d337dfSJohannes Berg 	spin_lock_irq(&rfkill_op_lock);
10019d337dfSJohannes Berg 	do {
10119d337dfSJohannes Berg 		if (rfkill_op_pending) {
10219d337dfSJohannes Berg 			enum rfkill_sched_op op = rfkill_op;
10319d337dfSJohannes Berg 			rfkill_op_pending = false;
10419d337dfSJohannes Berg 			memset(rfkill_sw_pending, 0,
10519d337dfSJohannes Berg 				sizeof(rfkill_sw_pending));
10619d337dfSJohannes Berg 			spin_unlock_irq(&rfkill_op_lock);
10719d337dfSJohannes Berg 
10819d337dfSJohannes Berg 			__rfkill_handle_global_op(op);
10919d337dfSJohannes Berg 
11019d337dfSJohannes Berg 			spin_lock_irq(&rfkill_op_lock);
11119d337dfSJohannes Berg 
11219d337dfSJohannes Berg 			/*
11319d337dfSJohannes Berg 			 * handle global ops first -- during unlocked period
11419d337dfSJohannes Berg 			 * we might have gotten a new global op.
11519d337dfSJohannes Berg 			 */
11619d337dfSJohannes Berg 			if (rfkill_op_pending)
11719d337dfSJohannes Berg 				continue;
11819d337dfSJohannes Berg 		}
11919d337dfSJohannes Berg 
12019d337dfSJohannes Berg 		if (rfkill_is_epo_lock_active())
12119d337dfSJohannes Berg 			continue;
12219d337dfSJohannes Berg 
12319d337dfSJohannes Berg 		for (i = 0; i < NUM_RFKILL_TYPES; i++) {
12419d337dfSJohannes Berg 			if (__test_and_clear_bit(i, rfkill_sw_pending)) {
12519d337dfSJohannes Berg 				c = __test_and_clear_bit(i, rfkill_sw_state);
12619d337dfSJohannes Berg 				spin_unlock_irq(&rfkill_op_lock);
12719d337dfSJohannes Berg 
12819d337dfSJohannes Berg 				__rfkill_handle_normal_op(i, c);
12919d337dfSJohannes Berg 
13019d337dfSJohannes Berg 				spin_lock_irq(&rfkill_op_lock);
13119d337dfSJohannes Berg 			}
13219d337dfSJohannes Berg 		}
13319d337dfSJohannes Berg 	} while (rfkill_op_pending);
13419d337dfSJohannes Berg 	spin_unlock_irq(&rfkill_op_lock);
13519d337dfSJohannes Berg }
13619d337dfSJohannes Berg 
13719d337dfSJohannes Berg static DECLARE_DELAYED_WORK(rfkill_op_work, rfkill_op_handler);
13819d337dfSJohannes Berg static unsigned long rfkill_last_scheduled;
13919d337dfSJohannes Berg 
rfkill_ratelimit(const unsigned long last)14019d337dfSJohannes Berg static unsigned long rfkill_ratelimit(const unsigned long last)
14119d337dfSJohannes Berg {
14219d337dfSJohannes Berg 	const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY);
143a02cec21SEric Dumazet 	return time_after(jiffies, last + delay) ? 0 : delay;
14419d337dfSJohannes Berg }
14519d337dfSJohannes Berg 
rfkill_schedule_ratelimited(void)14619d337dfSJohannes Berg static void rfkill_schedule_ratelimited(void)
14719d337dfSJohannes Berg {
148ba0c96cdSTejun Heo 	if (schedule_delayed_work(&rfkill_op_work,
149ba0c96cdSTejun Heo 				  rfkill_ratelimit(rfkill_last_scheduled)))
15019d337dfSJohannes Berg 		rfkill_last_scheduled = jiffies;
15119d337dfSJohannes Berg }
15219d337dfSJohannes Berg 
rfkill_schedule_global_op(enum rfkill_sched_op op)15319d337dfSJohannes Berg static void rfkill_schedule_global_op(enum rfkill_sched_op op)
15419d337dfSJohannes Berg {
15519d337dfSJohannes Berg 	unsigned long flags;
15619d337dfSJohannes Berg 
15719d337dfSJohannes Berg 	spin_lock_irqsave(&rfkill_op_lock, flags);
15819d337dfSJohannes Berg 	rfkill_op = op;
15919d337dfSJohannes Berg 	rfkill_op_pending = true;
16019d337dfSJohannes Berg 	if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
16119d337dfSJohannes Berg 		/* bypass the limiter for EPO */
16241f63c53STejun Heo 		mod_delayed_work(system_wq, &rfkill_op_work, 0);
16319d337dfSJohannes Berg 		rfkill_last_scheduled = jiffies;
16419d337dfSJohannes Berg 	} else
16519d337dfSJohannes Berg 		rfkill_schedule_ratelimited();
16619d337dfSJohannes Berg 	spin_unlock_irqrestore(&rfkill_op_lock, flags);
16719d337dfSJohannes Berg }
16819d337dfSJohannes Berg 
rfkill_schedule_toggle(enum rfkill_type type)16919d337dfSJohannes Berg static void rfkill_schedule_toggle(enum rfkill_type type)
17019d337dfSJohannes Berg {
17119d337dfSJohannes Berg 	unsigned long flags;
17219d337dfSJohannes Berg 
17319d337dfSJohannes Berg 	if (rfkill_is_epo_lock_active())
17419d337dfSJohannes Berg 		return;
17519d337dfSJohannes Berg 
17619d337dfSJohannes Berg 	spin_lock_irqsave(&rfkill_op_lock, flags);
17719d337dfSJohannes Berg 	if (!rfkill_op_pending) {
17819d337dfSJohannes Berg 		__set_bit(type, rfkill_sw_pending);
17919d337dfSJohannes Berg 		__change_bit(type, rfkill_sw_state);
18019d337dfSJohannes Berg 		rfkill_schedule_ratelimited();
18119d337dfSJohannes Berg 	}
18219d337dfSJohannes Berg 	spin_unlock_irqrestore(&rfkill_op_lock, flags);
18319d337dfSJohannes Berg }
18419d337dfSJohannes Berg 
rfkill_schedule_evsw_rfkillall(int state)18519d337dfSJohannes Berg static void rfkill_schedule_evsw_rfkillall(int state)
18619d337dfSJohannes Berg {
18719d337dfSJohannes Berg 	if (state)
18819d337dfSJohannes Berg 		rfkill_schedule_global_op(rfkill_master_switch_op);
18919d337dfSJohannes Berg 	else
19019d337dfSJohannes Berg 		rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
19119d337dfSJohannes Berg }
19219d337dfSJohannes Berg 
rfkill_event(struct input_handle * handle,unsigned int type,unsigned int code,int data)19319d337dfSJohannes Berg static void rfkill_event(struct input_handle *handle, unsigned int type,
19419d337dfSJohannes Berg 			unsigned int code, int data)
19519d337dfSJohannes Berg {
19619d337dfSJohannes Berg 	if (type == EV_KEY && data == 1) {
19719d337dfSJohannes Berg 		switch (code) {
19819d337dfSJohannes Berg 		case KEY_WLAN:
19919d337dfSJohannes Berg 			rfkill_schedule_toggle(RFKILL_TYPE_WLAN);
20019d337dfSJohannes Berg 			break;
20119d337dfSJohannes Berg 		case KEY_BLUETOOTH:
20219d337dfSJohannes Berg 			rfkill_schedule_toggle(RFKILL_TYPE_BLUETOOTH);
20319d337dfSJohannes Berg 			break;
20419d337dfSJohannes Berg 		case KEY_UWB:
20519d337dfSJohannes Berg 			rfkill_schedule_toggle(RFKILL_TYPE_UWB);
20619d337dfSJohannes Berg 			break;
20719d337dfSJohannes Berg 		case KEY_WIMAX:
20819d337dfSJohannes Berg 			rfkill_schedule_toggle(RFKILL_TYPE_WIMAX);
20919d337dfSJohannes Berg 			break;
2103082a2b7SMatthew Garrett 		case KEY_RFKILL:
2113082a2b7SMatthew Garrett 			rfkill_schedule_toggle(RFKILL_TYPE_ALL);
2123082a2b7SMatthew Garrett 			break;
21319d337dfSJohannes Berg 		}
21419d337dfSJohannes Berg 	} else if (type == EV_SW && code == SW_RFKILL_ALL)
21519d337dfSJohannes Berg 		rfkill_schedule_evsw_rfkillall(data);
21619d337dfSJohannes Berg }
21719d337dfSJohannes Berg 
rfkill_connect(struct input_handler * handler,struct input_dev * dev,const struct input_device_id * id)21819d337dfSJohannes Berg static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
21919d337dfSJohannes Berg 			  const struct input_device_id *id)
22019d337dfSJohannes Berg {
22119d337dfSJohannes Berg 	struct input_handle *handle;
22219d337dfSJohannes Berg 	int error;
22319d337dfSJohannes Berg 
22419d337dfSJohannes Berg 	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
22519d337dfSJohannes Berg 	if (!handle)
22619d337dfSJohannes Berg 		return -ENOMEM;
22719d337dfSJohannes Berg 
22819d337dfSJohannes Berg 	handle->dev = dev;
22919d337dfSJohannes Berg 	handle->handler = handler;
23019d337dfSJohannes Berg 	handle->name = "rfkill";
23119d337dfSJohannes Berg 
23219d337dfSJohannes Berg 	/* causes rfkill_start() to be called */
23319d337dfSJohannes Berg 	error = input_register_handle(handle);
23419d337dfSJohannes Berg 	if (error)
23519d337dfSJohannes Berg 		goto err_free_handle;
23619d337dfSJohannes Berg 
23719d337dfSJohannes Berg 	error = input_open_device(handle);
23819d337dfSJohannes Berg 	if (error)
23919d337dfSJohannes Berg 		goto err_unregister_handle;
24019d337dfSJohannes Berg 
24119d337dfSJohannes Berg 	return 0;
24219d337dfSJohannes Berg 
24319d337dfSJohannes Berg  err_unregister_handle:
24419d337dfSJohannes Berg 	input_unregister_handle(handle);
24519d337dfSJohannes Berg  err_free_handle:
24619d337dfSJohannes Berg 	kfree(handle);
24719d337dfSJohannes Berg 	return error;
24819d337dfSJohannes Berg }
24919d337dfSJohannes Berg 
rfkill_start(struct input_handle * handle)25019d337dfSJohannes Berg static void rfkill_start(struct input_handle *handle)
25119d337dfSJohannes Berg {
25219d337dfSJohannes Berg 	/*
25319d337dfSJohannes Berg 	 * Take event_lock to guard against configuration changes, we
25419d337dfSJohannes Berg 	 * should be able to deal with concurrency with rfkill_event()
25519d337dfSJohannes Berg 	 * just fine (which event_lock will also avoid).
25619d337dfSJohannes Berg 	 */
25719d337dfSJohannes Berg 	spin_lock_irq(&handle->dev->event_lock);
25819d337dfSJohannes Berg 
25919d337dfSJohannes Berg 	if (test_bit(EV_SW, handle->dev->evbit) &&
26019d337dfSJohannes Berg 	    test_bit(SW_RFKILL_ALL, handle->dev->swbit))
26119d337dfSJohannes Berg 		rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
26219d337dfSJohannes Berg 							handle->dev->sw));
26319d337dfSJohannes Berg 
26419d337dfSJohannes Berg 	spin_unlock_irq(&handle->dev->event_lock);
26519d337dfSJohannes Berg }
26619d337dfSJohannes Berg 
rfkill_disconnect(struct input_handle * handle)26719d337dfSJohannes Berg static void rfkill_disconnect(struct input_handle *handle)
26819d337dfSJohannes Berg {
26919d337dfSJohannes Berg 	input_close_device(handle);
27019d337dfSJohannes Berg 	input_unregister_handle(handle);
27119d337dfSJohannes Berg 	kfree(handle);
27219d337dfSJohannes Berg }
27319d337dfSJohannes Berg 
27419d337dfSJohannes Berg static const struct input_device_id rfkill_ids[] = {
27519d337dfSJohannes Berg 	{
27619d337dfSJohannes Berg 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
27719d337dfSJohannes Berg 		.evbit = { BIT_MASK(EV_KEY) },
27819d337dfSJohannes Berg 		.keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) },
27919d337dfSJohannes Berg 	},
28019d337dfSJohannes Berg 	{
28119d337dfSJohannes Berg 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
28219d337dfSJohannes Berg 		.evbit = { BIT_MASK(EV_KEY) },
28319d337dfSJohannes Berg 		.keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) },
28419d337dfSJohannes Berg 	},
28519d337dfSJohannes Berg 	{
28619d337dfSJohannes Berg 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
28719d337dfSJohannes Berg 		.evbit = { BIT_MASK(EV_KEY) },
28819d337dfSJohannes Berg 		.keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) },
28919d337dfSJohannes Berg 	},
29019d337dfSJohannes Berg 	{
29119d337dfSJohannes Berg 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
29219d337dfSJohannes Berg 		.evbit = { BIT_MASK(EV_KEY) },
29319d337dfSJohannes Berg 		.keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
29419d337dfSJohannes Berg 	},
29519d337dfSJohannes Berg 	{
2963082a2b7SMatthew Garrett 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
2973082a2b7SMatthew Garrett 		.evbit = { BIT_MASK(EV_KEY) },
2983082a2b7SMatthew Garrett 		.keybit = { [BIT_WORD(KEY_RFKILL)] = BIT_MASK(KEY_RFKILL) },
2993082a2b7SMatthew Garrett 	},
3003082a2b7SMatthew Garrett 	{
30119d337dfSJohannes Berg 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
30219d337dfSJohannes Berg 		.evbit = { BIT(EV_SW) },
30319d337dfSJohannes Berg 		.swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
30419d337dfSJohannes Berg 	},
30519d337dfSJohannes Berg 	{ }
30619d337dfSJohannes Berg };
30719d337dfSJohannes Berg 
30819d337dfSJohannes Berg static struct input_handler rfkill_handler = {
30919d337dfSJohannes Berg 	.name =	"rfkill",
31019d337dfSJohannes Berg 	.event = rfkill_event,
31119d337dfSJohannes Berg 	.connect = rfkill_connect,
31219d337dfSJohannes Berg 	.start = rfkill_start,
31319d337dfSJohannes Berg 	.disconnect = rfkill_disconnect,
31419d337dfSJohannes Berg 	.id_table = rfkill_ids,
31519d337dfSJohannes Berg };
31619d337dfSJohannes Berg 
rfkill_handler_init(void)31719d337dfSJohannes Berg int __init rfkill_handler_init(void)
31819d337dfSJohannes Berg {
31919d337dfSJohannes Berg 	switch (rfkill_master_switch_mode) {
32019d337dfSJohannes Berg 	case RFKILL_INPUT_MASTER_UNBLOCKALL:
32119d337dfSJohannes Berg 		rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNBLOCK;
32219d337dfSJohannes Berg 		break;
32319d337dfSJohannes Berg 	case RFKILL_INPUT_MASTER_RESTORE:
32419d337dfSJohannes Berg 		rfkill_master_switch_op = RFKILL_GLOBAL_OP_RESTORE;
32519d337dfSJohannes Berg 		break;
32619d337dfSJohannes Berg 	case RFKILL_INPUT_MASTER_UNLOCK:
32719d337dfSJohannes Berg 		rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNLOCK;
32819d337dfSJohannes Berg 		break;
32919d337dfSJohannes Berg 	default:
33019d337dfSJohannes Berg 		return -EINVAL;
33119d337dfSJohannes Berg 	}
33219d337dfSJohannes Berg 
33319d337dfSJohannes Berg 	/* Avoid delay at first schedule */
33419d337dfSJohannes Berg 	rfkill_last_scheduled =
33519d337dfSJohannes Berg 			jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
33619d337dfSJohannes Berg 	return input_register_handler(&rfkill_handler);
33719d337dfSJohannes Berg }
33819d337dfSJohannes Berg 
rfkill_handler_exit(void)33919d337dfSJohannes Berg void __exit rfkill_handler_exit(void)
34019d337dfSJohannes Berg {
34119d337dfSJohannes Berg 	input_unregister_handler(&rfkill_handler);
34219d337dfSJohannes Berg 	cancel_delayed_work_sync(&rfkill_op_work);
34319d337dfSJohannes Berg }
344