11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
219d337dfSJohannes Berg /*
319d337dfSJohannes Berg * Copyright (C) 2006 - 2007 Ivo van Doorn
419d337dfSJohannes Berg * Copyright (C) 2007 Dmitry Torokhov
519d337dfSJohannes Berg * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
619d337dfSJohannes Berg */
719d337dfSJohannes Berg
819d337dfSJohannes Berg #include <linux/kernel.h>
919d337dfSJohannes Berg #include <linux/module.h>
1019d337dfSJohannes Berg #include <linux/init.h>
1119d337dfSJohannes Berg #include <linux/workqueue.h>
1219d337dfSJohannes Berg #include <linux/capability.h>
1319d337dfSJohannes Berg #include <linux/list.h>
1419d337dfSJohannes Berg #include <linux/mutex.h>
1519d337dfSJohannes Berg #include <linux/rfkill.h>
16a99bbaf5SAlexey Dobriyan #include <linux/sched.h>
1719d337dfSJohannes Berg #include <linux/spinlock.h>
1851990e82SPaul Gortmaker #include <linux/device.h>
19c64fb016SJohannes Berg #include <linux/miscdevice.h>
20c64fb016SJohannes Berg #include <linux/wait.h>
21c64fb016SJohannes Berg #include <linux/poll.h>
22c64fb016SJohannes Berg #include <linux/fs.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
2419d337dfSJohannes Berg
2519d337dfSJohannes Berg #include "rfkill.h"
2619d337dfSJohannes Berg
2719d337dfSJohannes Berg #define POLL_INTERVAL (5 * HZ)
2819d337dfSJohannes Berg
2919d337dfSJohannes Berg #define RFKILL_BLOCK_HW BIT(0)
3019d337dfSJohannes Berg #define RFKILL_BLOCK_SW BIT(1)
3119d337dfSJohannes Berg #define RFKILL_BLOCK_SW_PREV BIT(2)
3219d337dfSJohannes Berg #define RFKILL_BLOCK_ANY (RFKILL_BLOCK_HW |\
3319d337dfSJohannes Berg RFKILL_BLOCK_SW |\
3419d337dfSJohannes Berg RFKILL_BLOCK_SW_PREV)
3519d337dfSJohannes Berg #define RFKILL_BLOCK_SW_SETCALL BIT(31)
3619d337dfSJohannes Berg
3719d337dfSJohannes Berg struct rfkill {
3819d337dfSJohannes Berg spinlock_t lock;
3919d337dfSJohannes Berg
4019d337dfSJohannes Berg enum rfkill_type type;
4119d337dfSJohannes Berg
4219d337dfSJohannes Berg unsigned long state;
4314486c82SEmmanuel Grumbach unsigned long hard_block_reasons;
4419d337dfSJohannes Berg
45c64fb016SJohannes Berg u32 idx;
46c64fb016SJohannes Berg
4719d337dfSJohannes Berg bool registered;
48b3fa1329SAlan Jenkins bool persistent;
49dd21dfc6SJohannes Berg bool polling_paused;
50dd21dfc6SJohannes Berg bool suspended;
512c3dfba4SJohannes Berg bool need_sync;
5219d337dfSJohannes Berg
5319d337dfSJohannes Berg const struct rfkill_ops *ops;
5419d337dfSJohannes Berg void *data;
5519d337dfSJohannes Berg
5619d337dfSJohannes Berg #ifdef CONFIG_RFKILL_LEDS
5719d337dfSJohannes Berg struct led_trigger led_trigger;
5819d337dfSJohannes Berg const char *ledtrigname;
5919d337dfSJohannes Berg #endif
6019d337dfSJohannes Berg
6119d337dfSJohannes Berg struct device dev;
6219d337dfSJohannes Berg struct list_head node;
6319d337dfSJohannes Berg
6419d337dfSJohannes Berg struct delayed_work poll_work;
6519d337dfSJohannes Berg struct work_struct uevent_work;
6619d337dfSJohannes Berg struct work_struct sync_work;
67b7bb1100SJohannes Berg char name[];
6819d337dfSJohannes Berg };
6919d337dfSJohannes Berg #define to_rfkill(d) container_of(d, struct rfkill, dev)
7019d337dfSJohannes Berg
71c64fb016SJohannes Berg struct rfkill_int_event {
72c64fb016SJohannes Berg struct list_head list;
7371826654SJohannes Berg struct rfkill_event_ext ev;
74c64fb016SJohannes Berg };
75c64fb016SJohannes Berg
76c64fb016SJohannes Berg struct rfkill_data {
77c64fb016SJohannes Berg struct list_head list;
78c64fb016SJohannes Berg struct list_head events;
79c64fb016SJohannes Berg struct mutex mtx;
80c64fb016SJohannes Berg wait_queue_head_t read_wait;
81c64fb016SJohannes Berg bool input_handler;
8254f586a9SJohannes Berg u8 max_size;
83c64fb016SJohannes Berg };
8419d337dfSJohannes Berg
8519d337dfSJohannes Berg
8619d337dfSJohannes Berg MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
8719d337dfSJohannes Berg MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
8819d337dfSJohannes Berg MODULE_DESCRIPTION("RF switch support");
8919d337dfSJohannes Berg MODULE_LICENSE("GPL");
9019d337dfSJohannes Berg
9119d337dfSJohannes Berg
9219d337dfSJohannes Berg /*
9319d337dfSJohannes Berg * The locking here should be made much smarter, we currently have
9419d337dfSJohannes Berg * a bit of a stupid situation because drivers might want to register
9519d337dfSJohannes Berg * the rfkill struct under their own lock, and take this lock during
9619d337dfSJohannes Berg * rfkill method calls -- which will cause an AB-BA deadlock situation.
9719d337dfSJohannes Berg *
9819d337dfSJohannes Berg * To fix that, we need to rework this code here to be mostly lock-free
9919d337dfSJohannes Berg * and only use the mutex for list manipulations, not to protect the
10019d337dfSJohannes Berg * various other global variables. Then we can avoid holding the mutex
10119d337dfSJohannes Berg * around driver operations, and all is happy.
10219d337dfSJohannes Berg */
10319d337dfSJohannes Berg static LIST_HEAD(rfkill_list); /* list of registered rf switches */
10419d337dfSJohannes Berg static DEFINE_MUTEX(rfkill_global_mutex);
105c64fb016SJohannes Berg static LIST_HEAD(rfkill_fds); /* list of open fds of /dev/rfkill */
10619d337dfSJohannes Berg
10719d337dfSJohannes Berg static unsigned int rfkill_default_state = 1;
10819d337dfSJohannes Berg module_param_named(default_state, rfkill_default_state, uint, 0444);
10919d337dfSJohannes Berg MODULE_PARM_DESC(default_state,
11019d337dfSJohannes Berg "Default initial state for all radio types, 0 = radio off");
11119d337dfSJohannes Berg
11219d337dfSJohannes Berg static struct {
113b3fa1329SAlan Jenkins bool cur, sav;
11419d337dfSJohannes Berg } rfkill_global_states[NUM_RFKILL_TYPES];
11519d337dfSJohannes Berg
11619d337dfSJohannes Berg static bool rfkill_epo_lock_active;
11719d337dfSJohannes Berg
11819d337dfSJohannes Berg
11919d337dfSJohannes Berg #ifdef CONFIG_RFKILL_LEDS
rfkill_led_trigger_event(struct rfkill * rfkill)12019d337dfSJohannes Berg static void rfkill_led_trigger_event(struct rfkill *rfkill)
12119d337dfSJohannes Berg {
12219d337dfSJohannes Berg struct led_trigger *trigger;
12319d337dfSJohannes Berg
12419d337dfSJohannes Berg if (!rfkill->registered)
12519d337dfSJohannes Berg return;
12619d337dfSJohannes Berg
12719d337dfSJohannes Berg trigger = &rfkill->led_trigger;
12819d337dfSJohannes Berg
12919d337dfSJohannes Berg if (rfkill->state & RFKILL_BLOCK_ANY)
13019d337dfSJohannes Berg led_trigger_event(trigger, LED_OFF);
13119d337dfSJohannes Berg else
13219d337dfSJohannes Berg led_trigger_event(trigger, LED_FULL);
13319d337dfSJohannes Berg }
13419d337dfSJohannes Berg
rfkill_led_trigger_activate(struct led_classdev * led)1352282e125SUwe Kleine-König static int rfkill_led_trigger_activate(struct led_classdev *led)
13619d337dfSJohannes Berg {
13719d337dfSJohannes Berg struct rfkill *rfkill;
13819d337dfSJohannes Berg
13919d337dfSJohannes Berg rfkill = container_of(led->trigger, struct rfkill, led_trigger);
14019d337dfSJohannes Berg
14119d337dfSJohannes Berg rfkill_led_trigger_event(rfkill);
1422282e125SUwe Kleine-König
1432282e125SUwe Kleine-König return 0;
14419d337dfSJohannes Berg }
14519d337dfSJohannes Berg
rfkill_get_led_trigger_name(struct rfkill * rfkill)14606d7de83SAceLan Kao const char *rfkill_get_led_trigger_name(struct rfkill *rfkill)
14706d7de83SAceLan Kao {
14806d7de83SAceLan Kao return rfkill->led_trigger.name;
14906d7de83SAceLan Kao }
15006d7de83SAceLan Kao EXPORT_SYMBOL(rfkill_get_led_trigger_name);
15106d7de83SAceLan Kao
rfkill_set_led_trigger_name(struct rfkill * rfkill,const char * name)15206d7de83SAceLan Kao void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
15306d7de83SAceLan Kao {
15406d7de83SAceLan Kao BUG_ON(!rfkill);
15506d7de83SAceLan Kao
15606d7de83SAceLan Kao rfkill->ledtrigname = name;
15706d7de83SAceLan Kao }
15806d7de83SAceLan Kao EXPORT_SYMBOL(rfkill_set_led_trigger_name);
15906d7de83SAceLan Kao
rfkill_led_trigger_register(struct rfkill * rfkill)16019d337dfSJohannes Berg static int rfkill_led_trigger_register(struct rfkill *rfkill)
16119d337dfSJohannes Berg {
16219d337dfSJohannes Berg rfkill->led_trigger.name = rfkill->ledtrigname
16319d337dfSJohannes Berg ? : dev_name(&rfkill->dev);
16419d337dfSJohannes Berg rfkill->led_trigger.activate = rfkill_led_trigger_activate;
16519d337dfSJohannes Berg return led_trigger_register(&rfkill->led_trigger);
16619d337dfSJohannes Berg }
16719d337dfSJohannes Berg
rfkill_led_trigger_unregister(struct rfkill * rfkill)16819d337dfSJohannes Berg static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
16919d337dfSJohannes Berg {
17019d337dfSJohannes Berg led_trigger_unregister(&rfkill->led_trigger);
17119d337dfSJohannes Berg }
1729b8e34e2SMichał Kępień
1739b8e34e2SMichał Kępień static struct led_trigger rfkill_any_led_trigger;
174232aa23eSJoão Paulo Rechi Vita static struct led_trigger rfkill_none_led_trigger;
175d874cd74SJoão Paulo Rechi Vita static struct work_struct rfkill_global_led_trigger_work;
1769b8e34e2SMichał Kępień
rfkill_global_led_trigger_worker(struct work_struct * work)177d874cd74SJoão Paulo Rechi Vita static void rfkill_global_led_trigger_worker(struct work_struct *work)
1789b8e34e2SMichał Kępień {
1799b8e34e2SMichał Kępień enum led_brightness brightness = LED_OFF;
1809b8e34e2SMichał Kępień struct rfkill *rfkill;
1819b8e34e2SMichał Kępień
1829b8e34e2SMichał Kępień mutex_lock(&rfkill_global_mutex);
1839b8e34e2SMichał Kępień list_for_each_entry(rfkill, &rfkill_list, node) {
1849b8e34e2SMichał Kępień if (!(rfkill->state & RFKILL_BLOCK_ANY)) {
1859b8e34e2SMichał Kępień brightness = LED_FULL;
1869b8e34e2SMichał Kępień break;
1879b8e34e2SMichał Kępień }
1889b8e34e2SMichał Kępień }
1899b8e34e2SMichał Kępień mutex_unlock(&rfkill_global_mutex);
1909b8e34e2SMichał Kępień
1919b8e34e2SMichał Kępień led_trigger_event(&rfkill_any_led_trigger, brightness);
192232aa23eSJoão Paulo Rechi Vita led_trigger_event(&rfkill_none_led_trigger,
193232aa23eSJoão Paulo Rechi Vita brightness == LED_OFF ? LED_FULL : LED_OFF);
1949b8e34e2SMichał Kępień }
1959b8e34e2SMichał Kępień
rfkill_global_led_trigger_event(void)196d874cd74SJoão Paulo Rechi Vita static void rfkill_global_led_trigger_event(void)
1979b8e34e2SMichał Kępień {
198d874cd74SJoão Paulo Rechi Vita schedule_work(&rfkill_global_led_trigger_work);
1999b8e34e2SMichał Kępień }
2009b8e34e2SMichał Kępień
rfkill_global_led_trigger_register(void)201d874cd74SJoão Paulo Rechi Vita static int rfkill_global_led_trigger_register(void)
2029b8e34e2SMichał Kępień {
203232aa23eSJoão Paulo Rechi Vita int ret;
204232aa23eSJoão Paulo Rechi Vita
205d874cd74SJoão Paulo Rechi Vita INIT_WORK(&rfkill_global_led_trigger_work,
206d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_worker);
207232aa23eSJoão Paulo Rechi Vita
2089b8e34e2SMichał Kępień rfkill_any_led_trigger.name = "rfkill-any";
209232aa23eSJoão Paulo Rechi Vita ret = led_trigger_register(&rfkill_any_led_trigger);
210232aa23eSJoão Paulo Rechi Vita if (ret)
211232aa23eSJoão Paulo Rechi Vita return ret;
212232aa23eSJoão Paulo Rechi Vita
213232aa23eSJoão Paulo Rechi Vita rfkill_none_led_trigger.name = "rfkill-none";
214232aa23eSJoão Paulo Rechi Vita ret = led_trigger_register(&rfkill_none_led_trigger);
215232aa23eSJoão Paulo Rechi Vita if (ret)
216232aa23eSJoão Paulo Rechi Vita led_trigger_unregister(&rfkill_any_led_trigger);
217232aa23eSJoão Paulo Rechi Vita else
218232aa23eSJoão Paulo Rechi Vita /* Delay activation until all global triggers are registered */
219232aa23eSJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
220232aa23eSJoão Paulo Rechi Vita
221232aa23eSJoão Paulo Rechi Vita return ret;
2229b8e34e2SMichał Kępień }
2239b8e34e2SMichał Kępień
rfkill_global_led_trigger_unregister(void)224d874cd74SJoão Paulo Rechi Vita static void rfkill_global_led_trigger_unregister(void)
2259b8e34e2SMichał Kępień {
226232aa23eSJoão Paulo Rechi Vita led_trigger_unregister(&rfkill_none_led_trigger);
2279b8e34e2SMichał Kępień led_trigger_unregister(&rfkill_any_led_trigger);
228d874cd74SJoão Paulo Rechi Vita cancel_work_sync(&rfkill_global_led_trigger_work);
2299b8e34e2SMichał Kępień }
23019d337dfSJohannes Berg #else
rfkill_led_trigger_event(struct rfkill * rfkill)23119d337dfSJohannes Berg static void rfkill_led_trigger_event(struct rfkill *rfkill)
23219d337dfSJohannes Berg {
23319d337dfSJohannes Berg }
23419d337dfSJohannes Berg
rfkill_led_trigger_register(struct rfkill * rfkill)23519d337dfSJohannes Berg static inline int rfkill_led_trigger_register(struct rfkill *rfkill)
23619d337dfSJohannes Berg {
23719d337dfSJohannes Berg return 0;
23819d337dfSJohannes Berg }
23919d337dfSJohannes Berg
rfkill_led_trigger_unregister(struct rfkill * rfkill)24019d337dfSJohannes Berg static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
24119d337dfSJohannes Berg {
24219d337dfSJohannes Berg }
2439b8e34e2SMichał Kępień
rfkill_global_led_trigger_event(void)244d874cd74SJoão Paulo Rechi Vita static void rfkill_global_led_trigger_event(void)
2459b8e34e2SMichał Kępień {
2469b8e34e2SMichał Kępień }
2479b8e34e2SMichał Kępień
rfkill_global_led_trigger_register(void)248d874cd74SJoão Paulo Rechi Vita static int rfkill_global_led_trigger_register(void)
2499b8e34e2SMichał Kępień {
2509b8e34e2SMichał Kępień return 0;
2519b8e34e2SMichał Kępień }
2529b8e34e2SMichał Kępień
rfkill_global_led_trigger_unregister(void)253d874cd74SJoão Paulo Rechi Vita static void rfkill_global_led_trigger_unregister(void)
2549b8e34e2SMichał Kępień {
2559b8e34e2SMichał Kępień }
25619d337dfSJohannes Berg #endif /* CONFIG_RFKILL_LEDS */
25719d337dfSJohannes Berg
rfkill_fill_event(struct rfkill_event_ext * ev,struct rfkill * rfkill,enum rfkill_operation op)25871826654SJohannes Berg static void rfkill_fill_event(struct rfkill_event_ext *ev,
25971826654SJohannes Berg struct rfkill *rfkill,
260c64fb016SJohannes Berg enum rfkill_operation op)
261c64fb016SJohannes Berg {
262c64fb016SJohannes Berg unsigned long flags;
263c64fb016SJohannes Berg
264c64fb016SJohannes Berg ev->idx = rfkill->idx;
265c64fb016SJohannes Berg ev->type = rfkill->type;
266c64fb016SJohannes Berg ev->op = op;
267c64fb016SJohannes Berg
268c64fb016SJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
269c64fb016SJohannes Berg ev->hard = !!(rfkill->state & RFKILL_BLOCK_HW);
270c64fb016SJohannes Berg ev->soft = !!(rfkill->state & (RFKILL_BLOCK_SW |
271c64fb016SJohannes Berg RFKILL_BLOCK_SW_PREV));
27214486c82SEmmanuel Grumbach ev->hard_block_reasons = rfkill->hard_block_reasons;
273c64fb016SJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
274c64fb016SJohannes Berg }
275c64fb016SJohannes Berg
rfkill_send_events(struct rfkill * rfkill,enum rfkill_operation op)276c64fb016SJohannes Berg static void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op)
277c64fb016SJohannes Berg {
278c64fb016SJohannes Berg struct rfkill_data *data;
279c64fb016SJohannes Berg struct rfkill_int_event *ev;
280c64fb016SJohannes Berg
281c64fb016SJohannes Berg list_for_each_entry(data, &rfkill_fds, list) {
282c64fb016SJohannes Berg ev = kzalloc(sizeof(*ev), GFP_KERNEL);
283c64fb016SJohannes Berg if (!ev)
284c64fb016SJohannes Berg continue;
285c64fb016SJohannes Berg rfkill_fill_event(&ev->ev, rfkill, op);
286c64fb016SJohannes Berg mutex_lock(&data->mtx);
287c64fb016SJohannes Berg list_add_tail(&ev->list, &data->events);
288c64fb016SJohannes Berg mutex_unlock(&data->mtx);
289c64fb016SJohannes Berg wake_up_interruptible(&data->read_wait);
290c64fb016SJohannes Berg }
291c64fb016SJohannes Berg }
292c64fb016SJohannes Berg
rfkill_event(struct rfkill * rfkill)293c64fb016SJohannes Berg static void rfkill_event(struct rfkill *rfkill)
29419d337dfSJohannes Berg {
29506d5caf4SAlan Jenkins if (!rfkill->registered)
29619d337dfSJohannes Berg return;
29719d337dfSJohannes Berg
29819d337dfSJohannes Berg kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
299c64fb016SJohannes Berg
300c64fb016SJohannes Berg /* also send event to /dev/rfkill */
301c64fb016SJohannes Berg rfkill_send_events(rfkill, RFKILL_OP_CHANGE);
30219d337dfSJohannes Berg }
30319d337dfSJohannes Berg
30419d337dfSJohannes Berg /**
30519d337dfSJohannes Berg * rfkill_set_block - wrapper for set_block method
30619d337dfSJohannes Berg *
30719d337dfSJohannes Berg * @rfkill: the rfkill struct to use
30819d337dfSJohannes Berg * @blocked: the new software state
30919d337dfSJohannes Berg *
31019d337dfSJohannes Berg * Calls the set_block method (when applicable) and handles notifications
31119d337dfSJohannes Berg * etc. as well.
31219d337dfSJohannes Berg */
rfkill_set_block(struct rfkill * rfkill,bool blocked)31319d337dfSJohannes Berg static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
31419d337dfSJohannes Berg {
31519d337dfSJohannes Berg unsigned long flags;
316eab48345SVitaly Wool bool prev, curr;
31719d337dfSJohannes Berg int err;
31819d337dfSJohannes Berg
3197fa20a7fSAlan Jenkins if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
3207fa20a7fSAlan Jenkins return;
3217fa20a7fSAlan Jenkins
32219d337dfSJohannes Berg /*
32319d337dfSJohannes Berg * Some platforms (...!) generate input events which affect the
32419d337dfSJohannes Berg * _hard_ kill state -- whenever something tries to change the
32519d337dfSJohannes Berg * current software state query the hardware state too.
32619d337dfSJohannes Berg */
32719d337dfSJohannes Berg if (rfkill->ops->query)
32819d337dfSJohannes Berg rfkill->ops->query(rfkill, rfkill->data);
32919d337dfSJohannes Berg
33019d337dfSJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
331eab48345SVitaly Wool prev = rfkill->state & RFKILL_BLOCK_SW;
332eab48345SVitaly Wool
333f3e7fae2SJoão Paulo Rechi Vita if (prev)
33419d337dfSJohannes Berg rfkill->state |= RFKILL_BLOCK_SW_PREV;
33519d337dfSJohannes Berg else
33619d337dfSJohannes Berg rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
33719d337dfSJohannes Berg
33819d337dfSJohannes Berg if (blocked)
33919d337dfSJohannes Berg rfkill->state |= RFKILL_BLOCK_SW;
34019d337dfSJohannes Berg else
34119d337dfSJohannes Berg rfkill->state &= ~RFKILL_BLOCK_SW;
34219d337dfSJohannes Berg
34319d337dfSJohannes Berg rfkill->state |= RFKILL_BLOCK_SW_SETCALL;
34419d337dfSJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
34519d337dfSJohannes Berg
34619d337dfSJohannes Berg err = rfkill->ops->set_block(rfkill->data, blocked);
34719d337dfSJohannes Berg
34819d337dfSJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
34919d337dfSJohannes Berg if (err) {
35019d337dfSJohannes Berg /*
3513ff707d6SJoão Paulo Rechi Vita * Failed -- reset status to _PREV, which may be different
3523ff707d6SJoão Paulo Rechi Vita * from what we have set _PREV to earlier in this function
35319d337dfSJohannes Berg * if rfkill_set_sw_state was invoked.
35419d337dfSJohannes Berg */
35519d337dfSJohannes Berg if (rfkill->state & RFKILL_BLOCK_SW_PREV)
35619d337dfSJohannes Berg rfkill->state |= RFKILL_BLOCK_SW;
35719d337dfSJohannes Berg else
35819d337dfSJohannes Berg rfkill->state &= ~RFKILL_BLOCK_SW;
35919d337dfSJohannes Berg }
36019d337dfSJohannes Berg rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
36119d337dfSJohannes Berg rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
362eab48345SVitaly Wool curr = rfkill->state & RFKILL_BLOCK_SW;
36319d337dfSJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
36419d337dfSJohannes Berg
36519d337dfSJohannes Berg rfkill_led_trigger_event(rfkill);
366d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
367eab48345SVitaly Wool
368eab48345SVitaly Wool if (prev != curr)
369c64fb016SJohannes Berg rfkill_event(rfkill);
37019d337dfSJohannes Berg }
37119d337dfSJohannes Berg
rfkill_sync(struct rfkill * rfkill)3722c3dfba4SJohannes Berg static void rfkill_sync(struct rfkill *rfkill)
3732c3dfba4SJohannes Berg {
3742c3dfba4SJohannes Berg lockdep_assert_held(&rfkill_global_mutex);
3752c3dfba4SJohannes Berg
3762c3dfba4SJohannes Berg if (!rfkill->need_sync)
3772c3dfba4SJohannes Berg return;
3782c3dfba4SJohannes Berg
3792c3dfba4SJohannes Berg rfkill_set_block(rfkill, rfkill_global_states[rfkill->type].cur);
3802c3dfba4SJohannes Berg rfkill->need_sync = false;
3812c3dfba4SJohannes Berg }
3822c3dfba4SJohannes Berg
rfkill_update_global_state(enum rfkill_type type,bool blocked)3839487bd6bSJoão Paulo Rechi Vita static void rfkill_update_global_state(enum rfkill_type type, bool blocked)
3849487bd6bSJoão Paulo Rechi Vita {
3859487bd6bSJoão Paulo Rechi Vita int i;
3869487bd6bSJoão Paulo Rechi Vita
3879487bd6bSJoão Paulo Rechi Vita if (type != RFKILL_TYPE_ALL) {
3889487bd6bSJoão Paulo Rechi Vita rfkill_global_states[type].cur = blocked;
3899487bd6bSJoão Paulo Rechi Vita return;
3909487bd6bSJoão Paulo Rechi Vita }
3919487bd6bSJoão Paulo Rechi Vita
3929487bd6bSJoão Paulo Rechi Vita for (i = 0; i < NUM_RFKILL_TYPES; i++)
3939487bd6bSJoão Paulo Rechi Vita rfkill_global_states[i].cur = blocked;
3949487bd6bSJoão Paulo Rechi Vita }
3959487bd6bSJoão Paulo Rechi Vita
396c64fb016SJohannes Berg #ifdef CONFIG_RFKILL_INPUT
397c64fb016SJohannes Berg static atomic_t rfkill_input_disabled = ATOMIC_INIT(0);
398c64fb016SJohannes Berg
39919d337dfSJohannes Berg /**
40019d337dfSJohannes Berg * __rfkill_switch_all - Toggle state of all switches of given type
40119d337dfSJohannes Berg * @type: type of interfaces to be affected
4022f29fed3SFabian Frederick * @blocked: the new state
40319d337dfSJohannes Berg *
40419d337dfSJohannes Berg * This function sets the state of all switches of given type,
405e2a35e89SJoão Paulo Rechi Vita * unless a specific switch is suspended.
40619d337dfSJohannes Berg *
40719d337dfSJohannes Berg * Caller must have acquired rfkill_global_mutex.
40819d337dfSJohannes Berg */
__rfkill_switch_all(const enum rfkill_type type,bool blocked)40919d337dfSJohannes Berg static void __rfkill_switch_all(const enum rfkill_type type, bool blocked)
41019d337dfSJohannes Berg {
41119d337dfSJohannes Berg struct rfkill *rfkill;
41219d337dfSJohannes Berg
4139487bd6bSJoão Paulo Rechi Vita rfkill_update_global_state(type, blocked);
41419d337dfSJohannes Berg list_for_each_entry(rfkill, &rfkill_list, node) {
41527e49ca9SAlex Hung if (rfkill->type != type && type != RFKILL_TYPE_ALL)
41619d337dfSJohannes Berg continue;
41719d337dfSJohannes Berg
41819d337dfSJohannes Berg rfkill_set_block(rfkill, blocked);
41919d337dfSJohannes Berg }
42019d337dfSJohannes Berg }
42119d337dfSJohannes Berg
42219d337dfSJohannes Berg /**
42319d337dfSJohannes Berg * rfkill_switch_all - Toggle state of all switches of given type
42419d337dfSJohannes Berg * @type: type of interfaces to be affected
4252f29fed3SFabian Frederick * @blocked: the new state
42619d337dfSJohannes Berg *
42719d337dfSJohannes Berg * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
42819d337dfSJohannes Berg * Please refer to __rfkill_switch_all() for details.
42919d337dfSJohannes Berg *
43019d337dfSJohannes Berg * Does nothing if the EPO lock is active.
43119d337dfSJohannes Berg */
rfkill_switch_all(enum rfkill_type type,bool blocked)43219d337dfSJohannes Berg void rfkill_switch_all(enum rfkill_type type, bool blocked)
43319d337dfSJohannes Berg {
434c64fb016SJohannes Berg if (atomic_read(&rfkill_input_disabled))
435c64fb016SJohannes Berg return;
436c64fb016SJohannes Berg
43719d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
43819d337dfSJohannes Berg
43919d337dfSJohannes Berg if (!rfkill_epo_lock_active)
44019d337dfSJohannes Berg __rfkill_switch_all(type, blocked);
44119d337dfSJohannes Berg
44219d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
44319d337dfSJohannes Berg }
44419d337dfSJohannes Berg
44519d337dfSJohannes Berg /**
44619d337dfSJohannes Berg * rfkill_epo - emergency power off all transmitters
44719d337dfSJohannes Berg *
44819d337dfSJohannes Berg * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED,
44919d337dfSJohannes Berg * ignoring everything in its path but rfkill_global_mutex and rfkill->mutex.
45019d337dfSJohannes Berg *
45119d337dfSJohannes Berg * The global state before the EPO is saved and can be restored later
45219d337dfSJohannes Berg * using rfkill_restore_states().
45319d337dfSJohannes Berg */
rfkill_epo(void)45419d337dfSJohannes Berg void rfkill_epo(void)
45519d337dfSJohannes Berg {
45619d337dfSJohannes Berg struct rfkill *rfkill;
45719d337dfSJohannes Berg int i;
45819d337dfSJohannes Berg
459c64fb016SJohannes Berg if (atomic_read(&rfkill_input_disabled))
460c64fb016SJohannes Berg return;
461c64fb016SJohannes Berg
46219d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
46319d337dfSJohannes Berg
46419d337dfSJohannes Berg rfkill_epo_lock_active = true;
46519d337dfSJohannes Berg list_for_each_entry(rfkill, &rfkill_list, node)
46619d337dfSJohannes Berg rfkill_set_block(rfkill, true);
46719d337dfSJohannes Berg
46819d337dfSJohannes Berg for (i = 0; i < NUM_RFKILL_TYPES; i++) {
469b3fa1329SAlan Jenkins rfkill_global_states[i].sav = rfkill_global_states[i].cur;
47019d337dfSJohannes Berg rfkill_global_states[i].cur = true;
47119d337dfSJohannes Berg }
472c64fb016SJohannes Berg
47319d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
47419d337dfSJohannes Berg }
47519d337dfSJohannes Berg
47619d337dfSJohannes Berg /**
47719d337dfSJohannes Berg * rfkill_restore_states - restore global states
47819d337dfSJohannes Berg *
47919d337dfSJohannes Berg * Restore (and sync switches to) the global state from the
48019d337dfSJohannes Berg * states in rfkill_default_states. This can undo the effects of
48119d337dfSJohannes Berg * a call to rfkill_epo().
48219d337dfSJohannes Berg */
rfkill_restore_states(void)48319d337dfSJohannes Berg void rfkill_restore_states(void)
48419d337dfSJohannes Berg {
48519d337dfSJohannes Berg int i;
48619d337dfSJohannes Berg
487c64fb016SJohannes Berg if (atomic_read(&rfkill_input_disabled))
488c64fb016SJohannes Berg return;
489c64fb016SJohannes Berg
49019d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
49119d337dfSJohannes Berg
49219d337dfSJohannes Berg rfkill_epo_lock_active = false;
49319d337dfSJohannes Berg for (i = 0; i < NUM_RFKILL_TYPES; i++)
494b3fa1329SAlan Jenkins __rfkill_switch_all(i, rfkill_global_states[i].sav);
49519d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
49619d337dfSJohannes Berg }
49719d337dfSJohannes Berg
49819d337dfSJohannes Berg /**
49919d337dfSJohannes Berg * rfkill_remove_epo_lock - unlock state changes
50019d337dfSJohannes Berg *
50119d337dfSJohannes Berg * Used by rfkill-input manually unlock state changes, when
50219d337dfSJohannes Berg * the EPO switch is deactivated.
50319d337dfSJohannes Berg */
rfkill_remove_epo_lock(void)50419d337dfSJohannes Berg void rfkill_remove_epo_lock(void)
50519d337dfSJohannes Berg {
506c64fb016SJohannes Berg if (atomic_read(&rfkill_input_disabled))
507c64fb016SJohannes Berg return;
508c64fb016SJohannes Berg
50919d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
51019d337dfSJohannes Berg rfkill_epo_lock_active = false;
51119d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
51219d337dfSJohannes Berg }
51319d337dfSJohannes Berg
51419d337dfSJohannes Berg /**
51519d337dfSJohannes Berg * rfkill_is_epo_lock_active - returns true EPO is active
51619d337dfSJohannes Berg *
517f404c3ecSRichard Guy Briggs * Returns 0 (false) if there is NOT an active EPO condition,
518f404c3ecSRichard Guy Briggs * and 1 (true) if there is an active EPO condition, which
51919d337dfSJohannes Berg * locks all radios in one of the BLOCKED states.
52019d337dfSJohannes Berg *
52119d337dfSJohannes Berg * Can be called in atomic context.
52219d337dfSJohannes Berg */
rfkill_is_epo_lock_active(void)52319d337dfSJohannes Berg bool rfkill_is_epo_lock_active(void)
52419d337dfSJohannes Berg {
52519d337dfSJohannes Berg return rfkill_epo_lock_active;
52619d337dfSJohannes Berg }
52719d337dfSJohannes Berg
52819d337dfSJohannes Berg /**
52919d337dfSJohannes Berg * rfkill_get_global_sw_state - returns global state for a type
53019d337dfSJohannes Berg * @type: the type to get the global state of
53119d337dfSJohannes Berg *
53219d337dfSJohannes Berg * Returns the current global state for a given wireless
53319d337dfSJohannes Berg * device type.
53419d337dfSJohannes Berg */
rfkill_get_global_sw_state(const enum rfkill_type type)53519d337dfSJohannes Berg bool rfkill_get_global_sw_state(const enum rfkill_type type)
53619d337dfSJohannes Berg {
53719d337dfSJohannes Berg return rfkill_global_states[type].cur;
53819d337dfSJohannes Berg }
539c64fb016SJohannes Berg #endif
54019d337dfSJohannes Berg
rfkill_set_hw_state_reason(struct rfkill * rfkill,bool blocked,unsigned long reason)54114486c82SEmmanuel Grumbach bool rfkill_set_hw_state_reason(struct rfkill *rfkill,
54214486c82SEmmanuel Grumbach bool blocked, unsigned long reason)
54319d337dfSJohannes Berg {
5441926e260SJoão Paulo Rechi Vita unsigned long flags;
5451926e260SJoão Paulo Rechi Vita bool ret, prev;
54619d337dfSJohannes Berg
5471926e260SJoão Paulo Rechi Vita BUG_ON(!rfkill);
5481926e260SJoão Paulo Rechi Vita
54914486c82SEmmanuel Grumbach if (WARN(reason &
55014486c82SEmmanuel Grumbach ~(RFKILL_HARD_BLOCK_SIGNAL | RFKILL_HARD_BLOCK_NOT_OWNER),
55114486c82SEmmanuel Grumbach "hw_state reason not supported: 0x%lx", reason))
55214486c82SEmmanuel Grumbach return blocked;
55314486c82SEmmanuel Grumbach
5541926e260SJoão Paulo Rechi Vita spin_lock_irqsave(&rfkill->lock, flags);
55514486c82SEmmanuel Grumbach prev = !!(rfkill->hard_block_reasons & reason);
55614486c82SEmmanuel Grumbach if (blocked) {
5571926e260SJoão Paulo Rechi Vita rfkill->state |= RFKILL_BLOCK_HW;
55814486c82SEmmanuel Grumbach rfkill->hard_block_reasons |= reason;
55914486c82SEmmanuel Grumbach } else {
56014486c82SEmmanuel Grumbach rfkill->hard_block_reasons &= ~reason;
56114486c82SEmmanuel Grumbach if (!rfkill->hard_block_reasons)
5621926e260SJoão Paulo Rechi Vita rfkill->state &= ~RFKILL_BLOCK_HW;
56314486c82SEmmanuel Grumbach }
5641926e260SJoão Paulo Rechi Vita ret = !!(rfkill->state & RFKILL_BLOCK_ANY);
5651926e260SJoão Paulo Rechi Vita spin_unlock_irqrestore(&rfkill->lock, flags);
5661926e260SJoão Paulo Rechi Vita
5671926e260SJoão Paulo Rechi Vita rfkill_led_trigger_event(rfkill);
568d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
56919d337dfSJohannes Berg
57074204f8fSJohannes Berg if (rfkill->registered && prev != blocked)
57119d337dfSJohannes Berg schedule_work(&rfkill->uevent_work);
57219d337dfSJohannes Berg
57319d337dfSJohannes Berg return ret;
57419d337dfSJohannes Berg }
57514486c82SEmmanuel Grumbach EXPORT_SYMBOL(rfkill_set_hw_state_reason);
57619d337dfSJohannes Berg
__rfkill_set_sw_state(struct rfkill * rfkill,bool blocked)57719d337dfSJohannes Berg static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
57819d337dfSJohannes Berg {
57919d337dfSJohannes Berg u32 bit = RFKILL_BLOCK_SW;
58019d337dfSJohannes Berg
58119d337dfSJohannes Berg /* if in a ops->set_block right now, use other bit */
58219d337dfSJohannes Berg if (rfkill->state & RFKILL_BLOCK_SW_SETCALL)
58319d337dfSJohannes Berg bit = RFKILL_BLOCK_SW_PREV;
58419d337dfSJohannes Berg
58519d337dfSJohannes Berg if (blocked)
58619d337dfSJohannes Berg rfkill->state |= bit;
58719d337dfSJohannes Berg else
58819d337dfSJohannes Berg rfkill->state &= ~bit;
58919d337dfSJohannes Berg }
59019d337dfSJohannes Berg
rfkill_set_sw_state(struct rfkill * rfkill,bool blocked)59119d337dfSJohannes Berg bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
59219d337dfSJohannes Berg {
59319d337dfSJohannes Berg unsigned long flags;
59419d337dfSJohannes Berg bool prev, hwblock;
59519d337dfSJohannes Berg
59619d337dfSJohannes Berg BUG_ON(!rfkill);
59719d337dfSJohannes Berg
59819d337dfSJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
59919d337dfSJohannes Berg prev = !!(rfkill->state & RFKILL_BLOCK_SW);
60019d337dfSJohannes Berg __rfkill_set_sw_state(rfkill, blocked);
60119d337dfSJohannes Berg hwblock = !!(rfkill->state & RFKILL_BLOCK_HW);
60219d337dfSJohannes Berg blocked = blocked || hwblock;
60319d337dfSJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
60419d337dfSJohannes Berg
60506d5caf4SAlan Jenkins if (!rfkill->registered)
60606d5caf4SAlan Jenkins return blocked;
60706d5caf4SAlan Jenkins
60819d337dfSJohannes Berg if (prev != blocked && !hwblock)
60919d337dfSJohannes Berg schedule_work(&rfkill->uevent_work);
61019d337dfSJohannes Berg
61119d337dfSJohannes Berg rfkill_led_trigger_event(rfkill);
612d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
61319d337dfSJohannes Berg
61419d337dfSJohannes Berg return blocked;
61519d337dfSJohannes Berg }
61619d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_set_sw_state);
61719d337dfSJohannes Berg
rfkill_init_sw_state(struct rfkill * rfkill,bool blocked)61806d5caf4SAlan Jenkins void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked)
61906d5caf4SAlan Jenkins {
62006d5caf4SAlan Jenkins unsigned long flags;
62106d5caf4SAlan Jenkins
62206d5caf4SAlan Jenkins BUG_ON(!rfkill);
62306d5caf4SAlan Jenkins BUG_ON(rfkill->registered);
62406d5caf4SAlan Jenkins
62506d5caf4SAlan Jenkins spin_lock_irqsave(&rfkill->lock, flags);
62606d5caf4SAlan Jenkins __rfkill_set_sw_state(rfkill, blocked);
62706d5caf4SAlan Jenkins rfkill->persistent = true;
62806d5caf4SAlan Jenkins spin_unlock_irqrestore(&rfkill->lock, flags);
62906d5caf4SAlan Jenkins }
63006d5caf4SAlan Jenkins EXPORT_SYMBOL(rfkill_init_sw_state);
63106d5caf4SAlan Jenkins
rfkill_set_states(struct rfkill * rfkill,bool sw,bool hw)63219d337dfSJohannes Berg void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
63319d337dfSJohannes Berg {
63419d337dfSJohannes Berg unsigned long flags;
63519d337dfSJohannes Berg bool swprev, hwprev;
63619d337dfSJohannes Berg
63719d337dfSJohannes Berg BUG_ON(!rfkill);
63819d337dfSJohannes Berg
63919d337dfSJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
64019d337dfSJohannes Berg
64119d337dfSJohannes Berg /*
64219d337dfSJohannes Berg * No need to care about prev/setblock ... this is for uevent only
64319d337dfSJohannes Berg * and that will get triggered by rfkill_set_block anyway.
64419d337dfSJohannes Berg */
64519d337dfSJohannes Berg swprev = !!(rfkill->state & RFKILL_BLOCK_SW);
64619d337dfSJohannes Berg hwprev = !!(rfkill->state & RFKILL_BLOCK_HW);
64719d337dfSJohannes Berg __rfkill_set_sw_state(rfkill, sw);
64848ab3578SAlan Jenkins if (hw)
64948ab3578SAlan Jenkins rfkill->state |= RFKILL_BLOCK_HW;
65048ab3578SAlan Jenkins else
65148ab3578SAlan Jenkins rfkill->state &= ~RFKILL_BLOCK_HW;
65219d337dfSJohannes Berg
65319d337dfSJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
65419d337dfSJohannes Berg
655b3fa1329SAlan Jenkins if (!rfkill->registered) {
656b3fa1329SAlan Jenkins rfkill->persistent = true;
657b3fa1329SAlan Jenkins } else {
65819d337dfSJohannes Berg if (swprev != sw || hwprev != hw)
65919d337dfSJohannes Berg schedule_work(&rfkill->uevent_work);
66019d337dfSJohannes Berg
66119d337dfSJohannes Berg rfkill_led_trigger_event(rfkill);
662d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
66319d337dfSJohannes Berg }
664b3fa1329SAlan Jenkins }
66519d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_set_states);
66619d337dfSJohannes Berg
667648b50ddSHeikki Krogerus static const char * const rfkill_types[] = {
668648b50ddSHeikki Krogerus NULL, /* RFKILL_TYPE_ALL */
669648b50ddSHeikki Krogerus "wlan",
670648b50ddSHeikki Krogerus "bluetooth",
671648b50ddSHeikki Krogerus "ultrawideband",
672648b50ddSHeikki Krogerus "wimax",
673648b50ddSHeikki Krogerus "wwan",
674648b50ddSHeikki Krogerus "gps",
675648b50ddSHeikki Krogerus "fm",
676648b50ddSHeikki Krogerus "nfc",
677648b50ddSHeikki Krogerus };
678648b50ddSHeikki Krogerus
rfkill_find_type(const char * name)679648b50ddSHeikki Krogerus enum rfkill_type rfkill_find_type(const char *name)
680648b50ddSHeikki Krogerus {
681648b50ddSHeikki Krogerus int i;
682648b50ddSHeikki Krogerus
683648b50ddSHeikki Krogerus BUILD_BUG_ON(ARRAY_SIZE(rfkill_types) != NUM_RFKILL_TYPES);
684648b50ddSHeikki Krogerus
685648b50ddSHeikki Krogerus if (!name)
686648b50ddSHeikki Krogerus return RFKILL_TYPE_ALL;
687648b50ddSHeikki Krogerus
688648b50ddSHeikki Krogerus for (i = 1; i < NUM_RFKILL_TYPES; i++)
689648b50ddSHeikki Krogerus if (!strcmp(name, rfkill_types[i]))
690648b50ddSHeikki Krogerus return i;
691648b50ddSHeikki Krogerus return RFKILL_TYPE_ALL;
692648b50ddSHeikki Krogerus }
693648b50ddSHeikki Krogerus EXPORT_SYMBOL(rfkill_find_type);
694648b50ddSHeikki Krogerus
name_show(struct device * dev,struct device_attribute * attr,char * buf)695e49df67dSGreg Kroah-Hartman static ssize_t name_show(struct device *dev, struct device_attribute *attr,
69619d337dfSJohannes Berg char *buf)
69719d337dfSJohannes Berg {
69819d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
69919d337dfSJohannes Berg
700796703baSBo Liu return sysfs_emit(buf, "%s\n", rfkill->name);
70119d337dfSJohannes Berg }
702e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(name);
70319d337dfSJohannes Berg
type_show(struct device * dev,struct device_attribute * attr,char * buf)704e49df67dSGreg Kroah-Hartman static ssize_t type_show(struct device *dev, struct device_attribute *attr,
70519d337dfSJohannes Berg char *buf)
70619d337dfSJohannes Berg {
70719d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
70819d337dfSJohannes Berg
709796703baSBo Liu return sysfs_emit(buf, "%s\n", rfkill_types[rfkill->type]);
71019d337dfSJohannes Berg }
711e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(type);
71219d337dfSJohannes Berg
index_show(struct device * dev,struct device_attribute * attr,char * buf)713e49df67dSGreg Kroah-Hartman static ssize_t index_show(struct device *dev, struct device_attribute *attr,
714c64fb016SJohannes Berg char *buf)
715c64fb016SJohannes Berg {
716c64fb016SJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
717c64fb016SJohannes Berg
718796703baSBo Liu return sysfs_emit(buf, "%d\n", rfkill->idx);
719c64fb016SJohannes Berg }
720e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(index);
721c64fb016SJohannes Berg
persistent_show(struct device * dev,struct device_attribute * attr,char * buf)722e49df67dSGreg Kroah-Hartman static ssize_t persistent_show(struct device *dev,
723e49df67dSGreg Kroah-Hartman struct device_attribute *attr, char *buf)
724464902e8SAlan Jenkins {
725464902e8SAlan Jenkins struct rfkill *rfkill = to_rfkill(dev);
726464902e8SAlan Jenkins
727796703baSBo Liu return sysfs_emit(buf, "%d\n", rfkill->persistent);
728464902e8SAlan Jenkins }
729e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(persistent);
730464902e8SAlan Jenkins
hard_show(struct device * dev,struct device_attribute * attr,char * buf)731e49df67dSGreg Kroah-Hartman static ssize_t hard_show(struct device *dev, struct device_attribute *attr,
7326c26361eSflorian@mickler.org char *buf)
7336c26361eSflorian@mickler.org {
7346c26361eSflorian@mickler.org struct rfkill *rfkill = to_rfkill(dev);
7356c26361eSflorian@mickler.org
736796703baSBo Liu return sysfs_emit(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_HW) ? 1 : 0);
7376c26361eSflorian@mickler.org }
738e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(hard);
7396c26361eSflorian@mickler.org
soft_show(struct device * dev,struct device_attribute * attr,char * buf)740e49df67dSGreg Kroah-Hartman static ssize_t soft_show(struct device *dev, struct device_attribute *attr,
7416c26361eSflorian@mickler.org char *buf)
7426c26361eSflorian@mickler.org {
7436c26361eSflorian@mickler.org struct rfkill *rfkill = to_rfkill(dev);
7446c26361eSflorian@mickler.org
7452c3dfba4SJohannes Berg mutex_lock(&rfkill_global_mutex);
7462c3dfba4SJohannes Berg rfkill_sync(rfkill);
7472c3dfba4SJohannes Berg mutex_unlock(&rfkill_global_mutex);
7482c3dfba4SJohannes Berg
749796703baSBo Liu return sysfs_emit(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_SW) ? 1 : 0);
7506c26361eSflorian@mickler.org }
7516c26361eSflorian@mickler.org
soft_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)752e49df67dSGreg Kroah-Hartman static ssize_t soft_store(struct device *dev, struct device_attribute *attr,
7536c26361eSflorian@mickler.org const char *buf, size_t count)
7546c26361eSflorian@mickler.org {
7556c26361eSflorian@mickler.org struct rfkill *rfkill = to_rfkill(dev);
7566c26361eSflorian@mickler.org unsigned long state;
7576c26361eSflorian@mickler.org int err;
7586c26361eSflorian@mickler.org
7596c26361eSflorian@mickler.org if (!capable(CAP_NET_ADMIN))
7606c26361eSflorian@mickler.org return -EPERM;
7616c26361eSflorian@mickler.org
7621bac92caSJulia Lawall err = kstrtoul(buf, 0, &state);
7636c26361eSflorian@mickler.org if (err)
7646c26361eSflorian@mickler.org return err;
7656c26361eSflorian@mickler.org
7666c26361eSflorian@mickler.org if (state > 1 )
7676c26361eSflorian@mickler.org return -EINVAL;
7686c26361eSflorian@mickler.org
7696c26361eSflorian@mickler.org mutex_lock(&rfkill_global_mutex);
7702c3dfba4SJohannes Berg rfkill_sync(rfkill);
7716c26361eSflorian@mickler.org rfkill_set_block(rfkill, state);
7726c26361eSflorian@mickler.org mutex_unlock(&rfkill_global_mutex);
7736c26361eSflorian@mickler.org
7746f7c962cSAlan Cox return count;
7756c26361eSflorian@mickler.org }
776e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RW(soft);
7776c26361eSflorian@mickler.org
hard_block_reasons_show(struct device * dev,struct device_attribute * attr,char * buf)77814486c82SEmmanuel Grumbach static ssize_t hard_block_reasons_show(struct device *dev,
77914486c82SEmmanuel Grumbach struct device_attribute *attr,
78014486c82SEmmanuel Grumbach char *buf)
78114486c82SEmmanuel Grumbach {
78214486c82SEmmanuel Grumbach struct rfkill *rfkill = to_rfkill(dev);
78314486c82SEmmanuel Grumbach
784796703baSBo Liu return sysfs_emit(buf, "0x%lx\n", rfkill->hard_block_reasons);
78514486c82SEmmanuel Grumbach }
78614486c82SEmmanuel Grumbach static DEVICE_ATTR_RO(hard_block_reasons);
78714486c82SEmmanuel Grumbach
user_state_from_blocked(unsigned long state)78819d337dfSJohannes Berg static u8 user_state_from_blocked(unsigned long state)
78919d337dfSJohannes Berg {
79019d337dfSJohannes Berg if (state & RFKILL_BLOCK_HW)
79119d337dfSJohannes Berg return RFKILL_USER_STATE_HARD_BLOCKED;
79219d337dfSJohannes Berg if (state & RFKILL_BLOCK_SW)
79319d337dfSJohannes Berg return RFKILL_USER_STATE_SOFT_BLOCKED;
79419d337dfSJohannes Berg
79519d337dfSJohannes Berg return RFKILL_USER_STATE_UNBLOCKED;
79619d337dfSJohannes Berg }
79719d337dfSJohannes Berg
state_show(struct device * dev,struct device_attribute * attr,char * buf)798e49df67dSGreg Kroah-Hartman static ssize_t state_show(struct device *dev, struct device_attribute *attr,
79919d337dfSJohannes Berg char *buf)
80019d337dfSJohannes Berg {
80119d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
80219d337dfSJohannes Berg
8032c3dfba4SJohannes Berg mutex_lock(&rfkill_global_mutex);
8042c3dfba4SJohannes Berg rfkill_sync(rfkill);
8052c3dfba4SJohannes Berg mutex_unlock(&rfkill_global_mutex);
8062c3dfba4SJohannes Berg
807796703baSBo Liu return sysfs_emit(buf, "%d\n", user_state_from_blocked(rfkill->state));
80819d337dfSJohannes Berg }
80919d337dfSJohannes Berg
state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)810e49df67dSGreg Kroah-Hartman static ssize_t state_store(struct device *dev, struct device_attribute *attr,
81119d337dfSJohannes Berg const char *buf, size_t count)
81219d337dfSJohannes Berg {
813f54c1427SJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
814f54c1427SJohannes Berg unsigned long state;
815f54c1427SJohannes Berg int err;
81619d337dfSJohannes Berg
817f54c1427SJohannes Berg if (!capable(CAP_NET_ADMIN))
81819d337dfSJohannes Berg return -EPERM;
819f54c1427SJohannes Berg
8201bac92caSJulia Lawall err = kstrtoul(buf, 0, &state);
821f54c1427SJohannes Berg if (err)
822f54c1427SJohannes Berg return err;
823f54c1427SJohannes Berg
824f54c1427SJohannes Berg if (state != RFKILL_USER_STATE_SOFT_BLOCKED &&
825f54c1427SJohannes Berg state != RFKILL_USER_STATE_UNBLOCKED)
826f54c1427SJohannes Berg return -EINVAL;
827f54c1427SJohannes Berg
828f54c1427SJohannes Berg mutex_lock(&rfkill_global_mutex);
8292c3dfba4SJohannes Berg rfkill_sync(rfkill);
830f54c1427SJohannes Berg rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED);
831f54c1427SJohannes Berg mutex_unlock(&rfkill_global_mutex);
832f54c1427SJohannes Berg
8336f7c962cSAlan Cox return count;
83419d337dfSJohannes Berg }
835e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RW(state);
83619d337dfSJohannes Berg
837e49df67dSGreg Kroah-Hartman static struct attribute *rfkill_dev_attrs[] = {
838e49df67dSGreg Kroah-Hartman &dev_attr_name.attr,
839e49df67dSGreg Kroah-Hartman &dev_attr_type.attr,
840e49df67dSGreg Kroah-Hartman &dev_attr_index.attr,
841e49df67dSGreg Kroah-Hartman &dev_attr_persistent.attr,
842e49df67dSGreg Kroah-Hartman &dev_attr_state.attr,
843e49df67dSGreg Kroah-Hartman &dev_attr_soft.attr,
844e49df67dSGreg Kroah-Hartman &dev_attr_hard.attr,
84514486c82SEmmanuel Grumbach &dev_attr_hard_block_reasons.attr,
846e49df67dSGreg Kroah-Hartman NULL,
84719d337dfSJohannes Berg };
848e49df67dSGreg Kroah-Hartman ATTRIBUTE_GROUPS(rfkill_dev);
84919d337dfSJohannes Berg
rfkill_release(struct device * dev)85019d337dfSJohannes Berg static void rfkill_release(struct device *dev)
85119d337dfSJohannes Berg {
85219d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
85319d337dfSJohannes Berg
85419d337dfSJohannes Berg kfree(rfkill);
85519d337dfSJohannes Berg }
85619d337dfSJohannes Berg
rfkill_dev_uevent(const struct device * dev,struct kobj_uevent_env * env)85723680f0bSGreg Kroah-Hartman static int rfkill_dev_uevent(const struct device *dev, struct kobj_uevent_env *env)
85819d337dfSJohannes Berg {
85919d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
86019d337dfSJohannes Berg unsigned long flags;
86114486c82SEmmanuel Grumbach unsigned long reasons;
86219d337dfSJohannes Berg u32 state;
86319d337dfSJohannes Berg int error;
86419d337dfSJohannes Berg
86519d337dfSJohannes Berg error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
86619d337dfSJohannes Berg if (error)
86719d337dfSJohannes Berg return error;
86819d337dfSJohannes Berg error = add_uevent_var(env, "RFKILL_TYPE=%s",
869648b50ddSHeikki Krogerus rfkill_types[rfkill->type]);
87019d337dfSJohannes Berg if (error)
87119d337dfSJohannes Berg return error;
87219d337dfSJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
87319d337dfSJohannes Berg state = rfkill->state;
87414486c82SEmmanuel Grumbach reasons = rfkill->hard_block_reasons;
87519d337dfSJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
87619d337dfSJohannes Berg error = add_uevent_var(env, "RFKILL_STATE=%d",
87719d337dfSJohannes Berg user_state_from_blocked(state));
87814486c82SEmmanuel Grumbach if (error)
87919d337dfSJohannes Berg return error;
88014486c82SEmmanuel Grumbach return add_uevent_var(env, "RFKILL_HW_BLOCK_REASON=0x%lx", reasons);
88119d337dfSJohannes Berg }
88219d337dfSJohannes Berg
rfkill_pause_polling(struct rfkill * rfkill)88319d337dfSJohannes Berg void rfkill_pause_polling(struct rfkill *rfkill)
88419d337dfSJohannes Berg {
88519d337dfSJohannes Berg BUG_ON(!rfkill);
88619d337dfSJohannes Berg
88719d337dfSJohannes Berg if (!rfkill->ops->poll)
88819d337dfSJohannes Berg return;
88919d337dfSJohannes Berg
890dd21dfc6SJohannes Berg rfkill->polling_paused = true;
89119d337dfSJohannes Berg cancel_delayed_work_sync(&rfkill->poll_work);
89219d337dfSJohannes Berg }
89319d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_pause_polling);
89419d337dfSJohannes Berg
rfkill_resume_polling(struct rfkill * rfkill)89519d337dfSJohannes Berg void rfkill_resume_polling(struct rfkill *rfkill)
89619d337dfSJohannes Berg {
89719d337dfSJohannes Berg BUG_ON(!rfkill);
89819d337dfSJohannes Berg
89919d337dfSJohannes Berg if (!rfkill->ops->poll)
90019d337dfSJohannes Berg return;
90119d337dfSJohannes Berg
902dd21dfc6SJohannes Berg rfkill->polling_paused = false;
903dd21dfc6SJohannes Berg
904dd21dfc6SJohannes Berg if (rfkill->suspended)
905dd21dfc6SJohannes Berg return;
906dd21dfc6SJohannes Berg
90767235cbcSShaibal Dutta queue_delayed_work(system_power_efficient_wq,
90867235cbcSShaibal Dutta &rfkill->poll_work, 0);
90919d337dfSJohannes Berg }
91019d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_resume_polling);
91119d337dfSJohannes Berg
91228f297a7SLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
rfkill_suspend(struct device * dev)91328f297a7SLars-Peter Clausen static int rfkill_suspend(struct device *dev)
91419d337dfSJohannes Berg {
91519d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
91619d337dfSJohannes Berg
917dd21dfc6SJohannes Berg rfkill->suspended = true;
918dd21dfc6SJohannes Berg cancel_delayed_work_sync(&rfkill->poll_work);
91919d337dfSJohannes Berg
92019d337dfSJohannes Berg return 0;
92119d337dfSJohannes Berg }
92219d337dfSJohannes Berg
rfkill_resume(struct device * dev)92319d337dfSJohannes Berg static int rfkill_resume(struct device *dev)
92419d337dfSJohannes Berg {
92519d337dfSJohannes Berg struct rfkill *rfkill = to_rfkill(dev);
92619d337dfSJohannes Berg bool cur;
92719d337dfSJohannes Berg
928dd21dfc6SJohannes Berg rfkill->suspended = false;
929dd21dfc6SJohannes Berg
93094e2bd0bSClaire Chang if (!rfkill->registered)
93194e2bd0bSClaire Chang return 0;
93294e2bd0bSClaire Chang
93306d5caf4SAlan Jenkins if (!rfkill->persistent) {
934908209c1SAlan Jenkins cur = !!(rfkill->state & RFKILL_BLOCK_SW);
93519d337dfSJohannes Berg rfkill_set_block(rfkill, cur);
93606d5caf4SAlan Jenkins }
93719d337dfSJohannes Berg
938dd21dfc6SJohannes Berg if (rfkill->ops->poll && !rfkill->polling_paused)
939dd21dfc6SJohannes Berg queue_delayed_work(system_power_efficient_wq,
940dd21dfc6SJohannes Berg &rfkill->poll_work, 0);
94119d337dfSJohannes Berg
94219d337dfSJohannes Berg return 0;
94319d337dfSJohannes Berg }
94419d337dfSJohannes Berg
94528f297a7SLars-Peter Clausen static SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume);
94628f297a7SLars-Peter Clausen #define RFKILL_PM_OPS (&rfkill_pm_ops)
94728f297a7SLars-Peter Clausen #else
94828f297a7SLars-Peter Clausen #define RFKILL_PM_OPS NULL
94928f297a7SLars-Peter Clausen #endif
95028f297a7SLars-Peter Clausen
95119d337dfSJohannes Berg static struct class rfkill_class = {
95219d337dfSJohannes Berg .name = "rfkill",
95319d337dfSJohannes Berg .dev_release = rfkill_release,
954e49df67dSGreg Kroah-Hartman .dev_groups = rfkill_dev_groups,
95519d337dfSJohannes Berg .dev_uevent = rfkill_dev_uevent,
95628f297a7SLars-Peter Clausen .pm = RFKILL_PM_OPS,
95719d337dfSJohannes Berg };
95819d337dfSJohannes Berg
rfkill_blocked(struct rfkill * rfkill)9596081162eSJohannes Berg bool rfkill_blocked(struct rfkill *rfkill)
9606081162eSJohannes Berg {
9616081162eSJohannes Berg unsigned long flags;
9626081162eSJohannes Berg u32 state;
9636081162eSJohannes Berg
9646081162eSJohannes Berg spin_lock_irqsave(&rfkill->lock, flags);
9656081162eSJohannes Berg state = rfkill->state;
9666081162eSJohannes Berg spin_unlock_irqrestore(&rfkill->lock, flags);
9676081162eSJohannes Berg
9686081162eSJohannes Berg return !!(state & RFKILL_BLOCK_ANY);
9696081162eSJohannes Berg }
9706081162eSJohannes Berg EXPORT_SYMBOL(rfkill_blocked);
9716081162eSJohannes Berg
rfkill_soft_blocked(struct rfkill * rfkill)9725bc9a9ddSEmmanuel Grumbach bool rfkill_soft_blocked(struct rfkill *rfkill)
9735bc9a9ddSEmmanuel Grumbach {
9745bc9a9ddSEmmanuel Grumbach unsigned long flags;
9755bc9a9ddSEmmanuel Grumbach u32 state;
9765bc9a9ddSEmmanuel Grumbach
9775bc9a9ddSEmmanuel Grumbach spin_lock_irqsave(&rfkill->lock, flags);
9785bc9a9ddSEmmanuel Grumbach state = rfkill->state;
9795bc9a9ddSEmmanuel Grumbach spin_unlock_irqrestore(&rfkill->lock, flags);
9805bc9a9ddSEmmanuel Grumbach
9815bc9a9ddSEmmanuel Grumbach return !!(state & RFKILL_BLOCK_SW);
9825bc9a9ddSEmmanuel Grumbach }
9835bc9a9ddSEmmanuel Grumbach EXPORT_SYMBOL(rfkill_soft_blocked);
98419d337dfSJohannes Berg
rfkill_alloc(const char * name,struct device * parent,const enum rfkill_type type,const struct rfkill_ops * ops,void * ops_data)98519d337dfSJohannes Berg struct rfkill * __must_check rfkill_alloc(const char *name,
98619d337dfSJohannes Berg struct device *parent,
98719d337dfSJohannes Berg const enum rfkill_type type,
98819d337dfSJohannes Berg const struct rfkill_ops *ops,
98919d337dfSJohannes Berg void *ops_data)
99019d337dfSJohannes Berg {
99119d337dfSJohannes Berg struct rfkill *rfkill;
99219d337dfSJohannes Berg struct device *dev;
99319d337dfSJohannes Berg
99419d337dfSJohannes Berg if (WARN_ON(!ops))
99519d337dfSJohannes Berg return NULL;
99619d337dfSJohannes Berg
99719d337dfSJohannes Berg if (WARN_ON(!ops->set_block))
99819d337dfSJohannes Berg return NULL;
99919d337dfSJohannes Berg
100019d337dfSJohannes Berg if (WARN_ON(!name))
100119d337dfSJohannes Berg return NULL;
100219d337dfSJohannes Berg
1003c64fb016SJohannes Berg if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES))
100419d337dfSJohannes Berg return NULL;
100519d337dfSJohannes Berg
1006b7bb1100SJohannes Berg rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL);
100719d337dfSJohannes Berg if (!rfkill)
100819d337dfSJohannes Berg return NULL;
100919d337dfSJohannes Berg
101019d337dfSJohannes Berg spin_lock_init(&rfkill->lock);
101119d337dfSJohannes Berg INIT_LIST_HEAD(&rfkill->node);
101219d337dfSJohannes Berg rfkill->type = type;
1013b7bb1100SJohannes Berg strcpy(rfkill->name, name);
101419d337dfSJohannes Berg rfkill->ops = ops;
101519d337dfSJohannes Berg rfkill->data = ops_data;
101619d337dfSJohannes Berg
101719d337dfSJohannes Berg dev = &rfkill->dev;
101819d337dfSJohannes Berg dev->class = &rfkill_class;
101919d337dfSJohannes Berg dev->parent = parent;
102019d337dfSJohannes Berg device_initialize(dev);
102119d337dfSJohannes Berg
102219d337dfSJohannes Berg return rfkill;
102319d337dfSJohannes Berg }
102419d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_alloc);
102519d337dfSJohannes Berg
rfkill_poll(struct work_struct * work)102619d337dfSJohannes Berg static void rfkill_poll(struct work_struct *work)
102719d337dfSJohannes Berg {
102819d337dfSJohannes Berg struct rfkill *rfkill;
102919d337dfSJohannes Berg
103019d337dfSJohannes Berg rfkill = container_of(work, struct rfkill, poll_work.work);
103119d337dfSJohannes Berg
103219d337dfSJohannes Berg /*
103319d337dfSJohannes Berg * Poll hardware state -- driver will use one of the
103419d337dfSJohannes Berg * rfkill_set{,_hw,_sw}_state functions and use its
103519d337dfSJohannes Berg * return value to update the current status.
103619d337dfSJohannes Berg */
103719d337dfSJohannes Berg rfkill->ops->poll(rfkill, rfkill->data);
103819d337dfSJohannes Berg
103967235cbcSShaibal Dutta queue_delayed_work(system_power_efficient_wq,
104067235cbcSShaibal Dutta &rfkill->poll_work,
104119d337dfSJohannes Berg round_jiffies_relative(POLL_INTERVAL));
104219d337dfSJohannes Berg }
104319d337dfSJohannes Berg
rfkill_uevent_work(struct work_struct * work)104419d337dfSJohannes Berg static void rfkill_uevent_work(struct work_struct *work)
104519d337dfSJohannes Berg {
104619d337dfSJohannes Berg struct rfkill *rfkill;
104719d337dfSJohannes Berg
104819d337dfSJohannes Berg rfkill = container_of(work, struct rfkill, uevent_work);
104919d337dfSJohannes Berg
1050c64fb016SJohannes Berg mutex_lock(&rfkill_global_mutex);
1051c64fb016SJohannes Berg rfkill_event(rfkill);
1052c64fb016SJohannes Berg mutex_unlock(&rfkill_global_mutex);
105319d337dfSJohannes Berg }
105419d337dfSJohannes Berg
rfkill_sync_work(struct work_struct * work)105519d337dfSJohannes Berg static void rfkill_sync_work(struct work_struct *work)
105619d337dfSJohannes Berg {
10572c3dfba4SJohannes Berg struct rfkill *rfkill = container_of(work, struct rfkill, sync_work);
105819d337dfSJohannes Berg
105919d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
10602c3dfba4SJohannes Berg rfkill_sync(rfkill);
106119d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
106219d337dfSJohannes Berg }
106319d337dfSJohannes Berg
rfkill_register(struct rfkill * rfkill)106419d337dfSJohannes Berg int __must_check rfkill_register(struct rfkill *rfkill)
106519d337dfSJohannes Berg {
106619d337dfSJohannes Berg static unsigned long rfkill_no;
10676fc232dbSAditya Pakki struct device *dev;
106819d337dfSJohannes Berg int error;
106919d337dfSJohannes Berg
10706fc232dbSAditya Pakki if (!rfkill)
10716fc232dbSAditya Pakki return -EINVAL;
10726fc232dbSAditya Pakki
10736fc232dbSAditya Pakki dev = &rfkill->dev;
107419d337dfSJohannes Berg
107519d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
107619d337dfSJohannes Berg
107719d337dfSJohannes Berg if (rfkill->registered) {
107819d337dfSJohannes Berg error = -EALREADY;
107919d337dfSJohannes Berg goto unlock;
108019d337dfSJohannes Berg }
108119d337dfSJohannes Berg
1082c64fb016SJohannes Berg rfkill->idx = rfkill_no;
108319d337dfSJohannes Berg dev_set_name(dev, "rfkill%lu", rfkill_no);
108419d337dfSJohannes Berg rfkill_no++;
108519d337dfSJohannes Berg
108619d337dfSJohannes Berg list_add_tail(&rfkill->node, &rfkill_list);
108719d337dfSJohannes Berg
108819d337dfSJohannes Berg error = device_add(dev);
108919d337dfSJohannes Berg if (error)
109019d337dfSJohannes Berg goto remove;
109119d337dfSJohannes Berg
109219d337dfSJohannes Berg error = rfkill_led_trigger_register(rfkill);
109319d337dfSJohannes Berg if (error)
109419d337dfSJohannes Berg goto devdel;
109519d337dfSJohannes Berg
109619d337dfSJohannes Berg rfkill->registered = true;
109719d337dfSJohannes Berg
109819d337dfSJohannes Berg INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll);
10992ec2c68cSJohannes Berg INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work);
11002ec2c68cSJohannes Berg INIT_WORK(&rfkill->sync_work, rfkill_sync_work);
11012ec2c68cSJohannes Berg
11022ec2c68cSJohannes Berg if (rfkill->ops->poll)
110367235cbcSShaibal Dutta queue_delayed_work(system_power_efficient_wq,
110467235cbcSShaibal Dutta &rfkill->poll_work,
110519d337dfSJohannes Berg round_jiffies_relative(POLL_INTERVAL));
1106b3fa1329SAlan Jenkins
1107b3fa1329SAlan Jenkins if (!rfkill->persistent || rfkill_epo_lock_active) {
11082c3dfba4SJohannes Berg rfkill->need_sync = true;
110919d337dfSJohannes Berg schedule_work(&rfkill->sync_work);
1110b3fa1329SAlan Jenkins } else {
1111b3fa1329SAlan Jenkins #ifdef CONFIG_RFKILL_INPUT
1112b3fa1329SAlan Jenkins bool soft_blocked = !!(rfkill->state & RFKILL_BLOCK_SW);
1113b3fa1329SAlan Jenkins
1114b3fa1329SAlan Jenkins if (!atomic_read(&rfkill_input_disabled))
1115b3fa1329SAlan Jenkins __rfkill_switch_all(rfkill->type, soft_blocked);
1116b3fa1329SAlan Jenkins #endif
1117b3fa1329SAlan Jenkins }
11182ec2c68cSJohannes Berg
1119d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
1120c64fb016SJohannes Berg rfkill_send_events(rfkill, RFKILL_OP_ADD);
112119d337dfSJohannes Berg
112219d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
112319d337dfSJohannes Berg return 0;
112419d337dfSJohannes Berg
112519d337dfSJohannes Berg devdel:
112619d337dfSJohannes Berg device_del(&rfkill->dev);
112719d337dfSJohannes Berg remove:
112819d337dfSJohannes Berg list_del_init(&rfkill->node);
112919d337dfSJohannes Berg unlock:
113019d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
113119d337dfSJohannes Berg return error;
113219d337dfSJohannes Berg }
113319d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_register);
113419d337dfSJohannes Berg
rfkill_unregister(struct rfkill * rfkill)113519d337dfSJohannes Berg void rfkill_unregister(struct rfkill *rfkill)
113619d337dfSJohannes Berg {
113719d337dfSJohannes Berg BUG_ON(!rfkill);
113819d337dfSJohannes Berg
113919d337dfSJohannes Berg if (rfkill->ops->poll)
114019d337dfSJohannes Berg cancel_delayed_work_sync(&rfkill->poll_work);
114119d337dfSJohannes Berg
114219d337dfSJohannes Berg cancel_work_sync(&rfkill->uevent_work);
114319d337dfSJohannes Berg cancel_work_sync(&rfkill->sync_work);
114419d337dfSJohannes Berg
114519d337dfSJohannes Berg rfkill->registered = false;
114619d337dfSJohannes Berg
114719d337dfSJohannes Berg device_del(&rfkill->dev);
114819d337dfSJohannes Berg
114919d337dfSJohannes Berg mutex_lock(&rfkill_global_mutex);
1150c64fb016SJohannes Berg rfkill_send_events(rfkill, RFKILL_OP_DEL);
115119d337dfSJohannes Berg list_del_init(&rfkill->node);
1152d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_event();
115319d337dfSJohannes Berg mutex_unlock(&rfkill_global_mutex);
115419d337dfSJohannes Berg
115519d337dfSJohannes Berg rfkill_led_trigger_unregister(rfkill);
115619d337dfSJohannes Berg }
115719d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_unregister);
115819d337dfSJohannes Berg
rfkill_destroy(struct rfkill * rfkill)115919d337dfSJohannes Berg void rfkill_destroy(struct rfkill *rfkill)
116019d337dfSJohannes Berg {
116119d337dfSJohannes Berg if (rfkill)
116219d337dfSJohannes Berg put_device(&rfkill->dev);
116319d337dfSJohannes Berg }
116419d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_destroy);
116519d337dfSJohannes Berg
rfkill_fop_open(struct inode * inode,struct file * file)1166c64fb016SJohannes Berg static int rfkill_fop_open(struct inode *inode, struct file *file)
1167c64fb016SJohannes Berg {
1168c64fb016SJohannes Berg struct rfkill_data *data;
1169c64fb016SJohannes Berg struct rfkill *rfkill;
1170c64fb016SJohannes Berg struct rfkill_int_event *ev, *tmp;
1171c64fb016SJohannes Berg
1172c64fb016SJohannes Berg data = kzalloc(sizeof(*data), GFP_KERNEL);
1173c64fb016SJohannes Berg if (!data)
1174c64fb016SJohannes Berg return -ENOMEM;
1175c64fb016SJohannes Berg
117654f586a9SJohannes Berg data->max_size = RFKILL_EVENT_SIZE_V1;
117754f586a9SJohannes Berg
1178c64fb016SJohannes Berg INIT_LIST_HEAD(&data->events);
1179c64fb016SJohannes Berg mutex_init(&data->mtx);
1180c64fb016SJohannes Berg init_waitqueue_head(&data->read_wait);
1181c64fb016SJohannes Berg
1182c64fb016SJohannes Berg mutex_lock(&rfkill_global_mutex);
1183c64fb016SJohannes Berg /*
1184c64fb016SJohannes Berg * start getting events from elsewhere but hold mtx to get
1185c64fb016SJohannes Berg * startup events added first
1186c64fb016SJohannes Berg */
1187c64fb016SJohannes Berg
1188c64fb016SJohannes Berg list_for_each_entry(rfkill, &rfkill_list, node) {
1189c64fb016SJohannes Berg ev = kzalloc(sizeof(*ev), GFP_KERNEL);
1190c64fb016SJohannes Berg if (!ev)
1191c64fb016SJohannes Berg goto free;
11922c3dfba4SJohannes Berg rfkill_sync(rfkill);
1193c64fb016SJohannes Berg rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD);
1194*f2ac54ebSJohannes Berg mutex_lock(&data->mtx);
1195c64fb016SJohannes Berg list_add_tail(&ev->list, &data->events);
1196*f2ac54ebSJohannes Berg mutex_unlock(&data->mtx);
1197c64fb016SJohannes Berg }
1198bd2281b8SJulia Lawall list_add(&data->list, &rfkill_fds);
1199c64fb016SJohannes Berg mutex_unlock(&rfkill_global_mutex);
1200c64fb016SJohannes Berg
1201c64fb016SJohannes Berg file->private_data = data;
1202c64fb016SJohannes Berg
1203c5bf68feSKirill Smelkov return stream_open(inode, file);
1204c64fb016SJohannes Berg
1205c64fb016SJohannes Berg free:
1206c64fb016SJohannes Berg mutex_unlock(&rfkill_global_mutex);
1207c64fb016SJohannes Berg mutex_destroy(&data->mtx);
1208c64fb016SJohannes Berg list_for_each_entry_safe(ev, tmp, &data->events, list)
1209c64fb016SJohannes Berg kfree(ev);
1210c64fb016SJohannes Berg kfree(data);
1211c64fb016SJohannes Berg return -ENOMEM;
1212c64fb016SJohannes Berg }
1213c64fb016SJohannes Berg
rfkill_fop_poll(struct file * file,poll_table * wait)1214ade994f4SAl Viro static __poll_t rfkill_fop_poll(struct file *file, poll_table *wait)
1215c64fb016SJohannes Berg {
1216c64fb016SJohannes Berg struct rfkill_data *data = file->private_data;
1217a9a08845SLinus Torvalds __poll_t res = EPOLLOUT | EPOLLWRNORM;
1218c64fb016SJohannes Berg
1219c64fb016SJohannes Berg poll_wait(file, &data->read_wait, wait);
1220c64fb016SJohannes Berg
1221c64fb016SJohannes Berg mutex_lock(&data->mtx);
1222c64fb016SJohannes Berg if (!list_empty(&data->events))
1223a9a08845SLinus Torvalds res = EPOLLIN | EPOLLRDNORM;
1224c64fb016SJohannes Berg mutex_unlock(&data->mtx);
1225c64fb016SJohannes Berg
1226c64fb016SJohannes Berg return res;
1227c64fb016SJohannes Berg }
1228c64fb016SJohannes Berg
rfkill_fop_read(struct file * file,char __user * buf,size_t count,loff_t * pos)1229c64fb016SJohannes Berg static ssize_t rfkill_fop_read(struct file *file, char __user *buf,
1230c64fb016SJohannes Berg size_t count, loff_t *pos)
1231c64fb016SJohannes Berg {
1232c64fb016SJohannes Berg struct rfkill_data *data = file->private_data;
1233c64fb016SJohannes Berg struct rfkill_int_event *ev;
1234c64fb016SJohannes Berg unsigned long sz;
1235c64fb016SJohannes Berg int ret;
1236c64fb016SJohannes Berg
1237c64fb016SJohannes Berg mutex_lock(&data->mtx);
1238c64fb016SJohannes Berg
1239c64fb016SJohannes Berg while (list_empty(&data->events)) {
1240c64fb016SJohannes Berg if (file->f_flags & O_NONBLOCK) {
1241c64fb016SJohannes Berg ret = -EAGAIN;
1242c64fb016SJohannes Berg goto out;
1243c64fb016SJohannes Berg }
1244c64fb016SJohannes Berg mutex_unlock(&data->mtx);
12456736fde9SJohannes Berg /* since we re-check and it just compares pointers,
12466736fde9SJohannes Berg * using !list_empty() without locking isn't a problem
12476736fde9SJohannes Berg */
1248c64fb016SJohannes Berg ret = wait_event_interruptible(data->read_wait,
12496736fde9SJohannes Berg !list_empty(&data->events));
1250c64fb016SJohannes Berg mutex_lock(&data->mtx);
1251c64fb016SJohannes Berg
1252c64fb016SJohannes Berg if (ret)
1253c64fb016SJohannes Berg goto out;
1254c64fb016SJohannes Berg }
1255c64fb016SJohannes Berg
1256c64fb016SJohannes Berg ev = list_first_entry(&data->events, struct rfkill_int_event,
1257c64fb016SJohannes Berg list);
1258c64fb016SJohannes Berg
1259c64fb016SJohannes Berg sz = min_t(unsigned long, sizeof(ev->ev), count);
126054f586a9SJohannes Berg sz = min_t(unsigned long, sz, data->max_size);
1261c64fb016SJohannes Berg ret = sz;
1262c64fb016SJohannes Berg if (copy_to_user(buf, &ev->ev, sz))
1263c64fb016SJohannes Berg ret = -EFAULT;
1264c64fb016SJohannes Berg
1265c64fb016SJohannes Berg list_del(&ev->list);
1266c64fb016SJohannes Berg kfree(ev);
1267c64fb016SJohannes Berg out:
1268c64fb016SJohannes Berg mutex_unlock(&data->mtx);
1269c64fb016SJohannes Berg return ret;
1270c64fb016SJohannes Berg }
1271c64fb016SJohannes Berg
rfkill_fop_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)1272c64fb016SJohannes Berg static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
1273c64fb016SJohannes Berg size_t count, loff_t *pos)
1274c64fb016SJohannes Berg {
127554f586a9SJohannes Berg struct rfkill_data *data = file->private_data;
1276c64fb016SJohannes Berg struct rfkill *rfkill;
127771826654SJohannes Berg struct rfkill_event_ext ev;
12781948b2a2SJoão Paulo Rechi Vita int ret;
1279c64fb016SJohannes Berg
1280c64fb016SJohannes Berg /* we don't need the 'hard' variable but accept it */
12811be491fcSJohannes Berg if (count < RFKILL_EVENT_SIZE_V1 - 1)
1282c64fb016SJohannes Berg return -EINVAL;
1283c64fb016SJohannes Berg
12841be491fcSJohannes Berg /*
12851be491fcSJohannes Berg * Copy as much data as we can accept into our 'ev' buffer,
12861be491fcSJohannes Berg * but tell userspace how much we've copied so it can determine
12871be491fcSJohannes Berg * our API version even in a write() call, if it cares.
12881be491fcSJohannes Berg */
12891be491fcSJohannes Berg count = min(count, sizeof(ev));
129054f586a9SJohannes Berg count = min_t(size_t, count, data->max_size);
12911be491fcSJohannes Berg if (copy_from_user(&ev, buf, count))
1292c64fb016SJohannes Berg return -EFAULT;
1293c64fb016SJohannes Berg
1294c64fb016SJohannes Berg if (ev.type >= NUM_RFKILL_TYPES)
1295c64fb016SJohannes Berg return -EINVAL;
1296c64fb016SJohannes Berg
1297c64fb016SJohannes Berg mutex_lock(&rfkill_global_mutex);
1298c64fb016SJohannes Berg
12991948b2a2SJoão Paulo Rechi Vita switch (ev.op) {
13001948b2a2SJoão Paulo Rechi Vita case RFKILL_OP_CHANGE_ALL:
13019487bd6bSJoão Paulo Rechi Vita rfkill_update_global_state(ev.type, ev.soft);
13021948b2a2SJoão Paulo Rechi Vita list_for_each_entry(rfkill, &rfkill_list, node)
13031948b2a2SJoão Paulo Rechi Vita if (rfkill->type == ev.type ||
13041948b2a2SJoão Paulo Rechi Vita ev.type == RFKILL_TYPE_ALL)
1305c64fb016SJohannes Berg rfkill_set_block(rfkill, ev.soft);
13061948b2a2SJoão Paulo Rechi Vita ret = 0;
13071948b2a2SJoão Paulo Rechi Vita break;
13081948b2a2SJoão Paulo Rechi Vita case RFKILL_OP_CHANGE:
13091948b2a2SJoão Paulo Rechi Vita list_for_each_entry(rfkill, &rfkill_list, node)
13101948b2a2SJoão Paulo Rechi Vita if (rfkill->idx == ev.idx &&
13111948b2a2SJoão Paulo Rechi Vita (rfkill->type == ev.type ||
13121948b2a2SJoão Paulo Rechi Vita ev.type == RFKILL_TYPE_ALL))
13131948b2a2SJoão Paulo Rechi Vita rfkill_set_block(rfkill, ev.soft);
13141948b2a2SJoão Paulo Rechi Vita ret = 0;
13151948b2a2SJoão Paulo Rechi Vita break;
13161948b2a2SJoão Paulo Rechi Vita default:
13171948b2a2SJoão Paulo Rechi Vita ret = -EINVAL;
13181948b2a2SJoão Paulo Rechi Vita break;
1319c64fb016SJohannes Berg }
13201948b2a2SJoão Paulo Rechi Vita
1321c64fb016SJohannes Berg mutex_unlock(&rfkill_global_mutex);
1322c64fb016SJohannes Berg
13231948b2a2SJoão Paulo Rechi Vita return ret ?: count;
1324c64fb016SJohannes Berg }
1325c64fb016SJohannes Berg
rfkill_fop_release(struct inode * inode,struct file * file)1326c64fb016SJohannes Berg static int rfkill_fop_release(struct inode *inode, struct file *file)
1327c64fb016SJohannes Berg {
1328c64fb016SJohannes Berg struct rfkill_data *data = file->private_data;
1329c64fb016SJohannes Berg struct rfkill_int_event *ev, *tmp;
1330c64fb016SJohannes Berg
1331c64fb016SJohannes Berg mutex_lock(&rfkill_global_mutex);
1332c64fb016SJohannes Berg list_del(&data->list);
1333c64fb016SJohannes Berg mutex_unlock(&rfkill_global_mutex);
1334c64fb016SJohannes Berg
1335c64fb016SJohannes Berg mutex_destroy(&data->mtx);
1336c64fb016SJohannes Berg list_for_each_entry_safe(ev, tmp, &data->events, list)
1337c64fb016SJohannes Berg kfree(ev);
1338c64fb016SJohannes Berg
1339c64fb016SJohannes Berg #ifdef CONFIG_RFKILL_INPUT
1340c64fb016SJohannes Berg if (data->input_handler)
1341207ee162SJohannes Berg if (atomic_dec_return(&rfkill_input_disabled) == 0)
1342207ee162SJohannes Berg printk(KERN_DEBUG "rfkill: input handler enabled\n");
1343c64fb016SJohannes Berg #endif
1344c64fb016SJohannes Berg
1345c64fb016SJohannes Berg kfree(data);
1346c64fb016SJohannes Berg
1347c64fb016SJohannes Berg return 0;
1348c64fb016SJohannes Berg }
1349c64fb016SJohannes Berg
rfkill_fop_ioctl(struct file * file,unsigned int cmd,unsigned long arg)1350c64fb016SJohannes Berg static long rfkill_fop_ioctl(struct file *file, unsigned int cmd,
1351c64fb016SJohannes Berg unsigned long arg)
1352c64fb016SJohannes Berg {
1353c64fb016SJohannes Berg struct rfkill_data *data = file->private_data;
135454f586a9SJohannes Berg int ret = -ENOSYS;
135554f586a9SJohannes Berg u32 size;
1356c64fb016SJohannes Berg
1357c64fb016SJohannes Berg if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC)
1358c64fb016SJohannes Berg return -ENOSYS;
1359c64fb016SJohannes Berg
1360c64fb016SJohannes Berg mutex_lock(&data->mtx);
136154f586a9SJohannes Berg switch (_IOC_NR(cmd)) {
136254f586a9SJohannes Berg #ifdef CONFIG_RFKILL_INPUT
136354f586a9SJohannes Berg case RFKILL_IOC_NOINPUT:
1364c64fb016SJohannes Berg if (!data->input_handler) {
1365207ee162SJohannes Berg if (atomic_inc_return(&rfkill_input_disabled) == 1)
1366207ee162SJohannes Berg printk(KERN_DEBUG "rfkill: input handler disabled\n");
1367c64fb016SJohannes Berg data->input_handler = true;
1368c64fb016SJohannes Berg }
136954f586a9SJohannes Berg ret = 0;
137054f586a9SJohannes Berg break;
137154f586a9SJohannes Berg #endif
137254f586a9SJohannes Berg case RFKILL_IOC_MAX_SIZE:
137354f586a9SJohannes Berg if (get_user(size, (__u32 __user *)arg)) {
137454f586a9SJohannes Berg ret = -EFAULT;
137554f586a9SJohannes Berg break;
137654f586a9SJohannes Berg }
137754f586a9SJohannes Berg if (size < RFKILL_EVENT_SIZE_V1 || size > U8_MAX) {
137854f586a9SJohannes Berg ret = -EINVAL;
137954f586a9SJohannes Berg break;
138054f586a9SJohannes Berg }
138154f586a9SJohannes Berg data->max_size = size;
138254f586a9SJohannes Berg ret = 0;
138354f586a9SJohannes Berg break;
138454f586a9SJohannes Berg default:
138554f586a9SJohannes Berg break;
138654f586a9SJohannes Berg }
1387c64fb016SJohannes Berg mutex_unlock(&data->mtx);
1388c64fb016SJohannes Berg
138954f586a9SJohannes Berg return ret;
1390c64fb016SJohannes Berg }
1391c64fb016SJohannes Berg
1392c64fb016SJohannes Berg static const struct file_operations rfkill_fops = {
139345ba564dSJohannes Berg .owner = THIS_MODULE,
1394c64fb016SJohannes Berg .open = rfkill_fop_open,
1395c64fb016SJohannes Berg .read = rfkill_fop_read,
1396c64fb016SJohannes Berg .write = rfkill_fop_write,
1397c64fb016SJohannes Berg .poll = rfkill_fop_poll,
1398c64fb016SJohannes Berg .release = rfkill_fop_release,
1399c64fb016SJohannes Berg .unlocked_ioctl = rfkill_fop_ioctl,
14001832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
14016038f373SArnd Bergmann .llseek = no_llseek,
1402c64fb016SJohannes Berg };
1403c64fb016SJohannes Berg
14048670b2b8SMarcel Holtmann #define RFKILL_NAME "rfkill"
14058670b2b8SMarcel Holtmann
1406c64fb016SJohannes Berg static struct miscdevice rfkill_miscdev = {
1407c64fb016SJohannes Berg .fops = &rfkill_fops,
14088670b2b8SMarcel Holtmann .name = RFKILL_NAME,
14098670b2b8SMarcel Holtmann .minor = RFKILL_MINOR,
1410c64fb016SJohannes Berg };
141119d337dfSJohannes Berg
rfkill_init(void)141219d337dfSJohannes Berg static int __init rfkill_init(void)
141319d337dfSJohannes Berg {
141419d337dfSJohannes Berg int error;
141519d337dfSJohannes Berg
14169487bd6bSJoão Paulo Rechi Vita rfkill_update_global_state(RFKILL_TYPE_ALL, !rfkill_default_state);
141719d337dfSJohannes Berg
141819d337dfSJohannes Berg error = class_register(&rfkill_class);
141919d337dfSJohannes Berg if (error)
14206124c53eSMichał Kępień goto error_class;
142119d337dfSJohannes Berg
1422c64fb016SJohannes Berg error = misc_register(&rfkill_miscdev);
14236124c53eSMichał Kępień if (error)
14246124c53eSMichał Kępień goto error_misc;
1425c64fb016SJohannes Berg
1426d874cd74SJoão Paulo Rechi Vita error = rfkill_global_led_trigger_register();
14279b8e34e2SMichał Kępień if (error)
14289b8e34e2SMichał Kępień goto error_led_trigger;
14299b8e34e2SMichał Kępień
143019d337dfSJohannes Berg #ifdef CONFIG_RFKILL_INPUT
143119d337dfSJohannes Berg error = rfkill_handler_init();
14326124c53eSMichał Kępień if (error)
14336124c53eSMichał Kępień goto error_input;
143419d337dfSJohannes Berg #endif
143519d337dfSJohannes Berg
14366124c53eSMichał Kępień return 0;
14376124c53eSMichał Kępień
1438f6b4122cSArnd Bergmann #ifdef CONFIG_RFKILL_INPUT
14396124c53eSMichał Kępień error_input:
1440d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_unregister();
14417b854982SJohannes Berg #endif
14429b8e34e2SMichał Kępień error_led_trigger:
14439b8e34e2SMichał Kępień misc_deregister(&rfkill_miscdev);
14446124c53eSMichał Kępień error_misc:
14456124c53eSMichał Kępień class_unregister(&rfkill_class);
14466124c53eSMichał Kępień error_class:
144719d337dfSJohannes Berg return error;
144819d337dfSJohannes Berg }
144919d337dfSJohannes Berg subsys_initcall(rfkill_init);
145019d337dfSJohannes Berg
rfkill_exit(void)145119d337dfSJohannes Berg static void __exit rfkill_exit(void)
145219d337dfSJohannes Berg {
145319d337dfSJohannes Berg #ifdef CONFIG_RFKILL_INPUT
145419d337dfSJohannes Berg rfkill_handler_exit();
145519d337dfSJohannes Berg #endif
1456d874cd74SJoão Paulo Rechi Vita rfkill_global_led_trigger_unregister();
1457c64fb016SJohannes Berg misc_deregister(&rfkill_miscdev);
145819d337dfSJohannes Berg class_unregister(&rfkill_class);
145919d337dfSJohannes Berg }
146019d337dfSJohannes Berg module_exit(rfkill_exit);
14618670b2b8SMarcel Holtmann
14628670b2b8SMarcel Holtmann MODULE_ALIAS_MISCDEV(RFKILL_MINOR);
14638670b2b8SMarcel Holtmann MODULE_ALIAS("devname:" RFKILL_NAME);
1464