xref: /linux-6.15/net/rfkill/core.c (revision cb787f4a)
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 <[email protected]>
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 <[email protected]>");
8719d337dfSJohannes Berg MODULE_AUTHOR("Johannes Berg <[email protected]>");
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,enum rfkill_hard_block_reasons reason)54114486c82SEmmanuel Grumbach bool rfkill_set_hw_state_reason(struct rfkill *rfkill,
542*373d3f8dSZijun Hu 				bool blocked,
543*373d3f8dSZijun Hu 				enum rfkill_hard_block_reasons reason)
54419d337dfSJohannes Berg {
5451926e260SJoão Paulo Rechi Vita 	unsigned long flags;
5461926e260SJoão Paulo Rechi Vita 	bool ret, prev;
54719d337dfSJohannes Berg 
5481926e260SJoão Paulo Rechi Vita 	BUG_ON(!rfkill);
5491926e260SJoão Paulo Rechi Vita 
5501926e260SJoão Paulo Rechi Vita 	spin_lock_irqsave(&rfkill->lock, flags);
55114486c82SEmmanuel Grumbach 	prev = !!(rfkill->hard_block_reasons & reason);
55214486c82SEmmanuel Grumbach 	if (blocked) {
5531926e260SJoão Paulo Rechi Vita 		rfkill->state |= RFKILL_BLOCK_HW;
55414486c82SEmmanuel Grumbach 		rfkill->hard_block_reasons |= reason;
55514486c82SEmmanuel Grumbach 	} else {
55614486c82SEmmanuel Grumbach 		rfkill->hard_block_reasons &= ~reason;
55714486c82SEmmanuel Grumbach 		if (!rfkill->hard_block_reasons)
5581926e260SJoão Paulo Rechi Vita 			rfkill->state &= ~RFKILL_BLOCK_HW;
55914486c82SEmmanuel Grumbach 	}
5601926e260SJoão Paulo Rechi Vita 	ret = !!(rfkill->state & RFKILL_BLOCK_ANY);
5611926e260SJoão Paulo Rechi Vita 	spin_unlock_irqrestore(&rfkill->lock, flags);
5621926e260SJoão Paulo Rechi Vita 
5631926e260SJoão Paulo Rechi Vita 	rfkill_led_trigger_event(rfkill);
564d874cd74SJoão Paulo Rechi Vita 	rfkill_global_led_trigger_event();
56519d337dfSJohannes Berg 
56674204f8fSJohannes Berg 	if (rfkill->registered && prev != blocked)
56719d337dfSJohannes Berg 		schedule_work(&rfkill->uevent_work);
56819d337dfSJohannes Berg 
56919d337dfSJohannes Berg 	return ret;
57019d337dfSJohannes Berg }
57114486c82SEmmanuel Grumbach EXPORT_SYMBOL(rfkill_set_hw_state_reason);
57219d337dfSJohannes Berg 
__rfkill_set_sw_state(struct rfkill * rfkill,bool blocked)57319d337dfSJohannes Berg static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
57419d337dfSJohannes Berg {
57519d337dfSJohannes Berg 	u32 bit = RFKILL_BLOCK_SW;
57619d337dfSJohannes Berg 
57719d337dfSJohannes Berg 	/* if in a ops->set_block right now, use other bit */
57819d337dfSJohannes Berg 	if (rfkill->state & RFKILL_BLOCK_SW_SETCALL)
57919d337dfSJohannes Berg 		bit = RFKILL_BLOCK_SW_PREV;
58019d337dfSJohannes Berg 
58119d337dfSJohannes Berg 	if (blocked)
58219d337dfSJohannes Berg 		rfkill->state |= bit;
58319d337dfSJohannes Berg 	else
58419d337dfSJohannes Berg 		rfkill->state &= ~bit;
58519d337dfSJohannes Berg }
58619d337dfSJohannes Berg 
rfkill_set_sw_state(struct rfkill * rfkill,bool blocked)58719d337dfSJohannes Berg bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
58819d337dfSJohannes Berg {
58919d337dfSJohannes Berg 	unsigned long flags;
59019d337dfSJohannes Berg 	bool prev, hwblock;
59119d337dfSJohannes Berg 
59219d337dfSJohannes Berg 	BUG_ON(!rfkill);
59319d337dfSJohannes Berg 
59419d337dfSJohannes Berg 	spin_lock_irqsave(&rfkill->lock, flags);
59519d337dfSJohannes Berg 	prev = !!(rfkill->state & RFKILL_BLOCK_SW);
59619d337dfSJohannes Berg 	__rfkill_set_sw_state(rfkill, blocked);
59719d337dfSJohannes Berg 	hwblock = !!(rfkill->state & RFKILL_BLOCK_HW);
59819d337dfSJohannes Berg 	blocked = blocked || hwblock;
59919d337dfSJohannes Berg 	spin_unlock_irqrestore(&rfkill->lock, flags);
60019d337dfSJohannes Berg 
60106d5caf4SAlan Jenkins 	if (!rfkill->registered)
60206d5caf4SAlan Jenkins 		return blocked;
60306d5caf4SAlan Jenkins 
60419d337dfSJohannes Berg 	if (prev != blocked && !hwblock)
60519d337dfSJohannes Berg 		schedule_work(&rfkill->uevent_work);
60619d337dfSJohannes Berg 
60719d337dfSJohannes Berg 	rfkill_led_trigger_event(rfkill);
608d874cd74SJoão Paulo Rechi Vita 	rfkill_global_led_trigger_event();
60919d337dfSJohannes Berg 
61019d337dfSJohannes Berg 	return blocked;
61119d337dfSJohannes Berg }
61219d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_set_sw_state);
61319d337dfSJohannes Berg 
rfkill_init_sw_state(struct rfkill * rfkill,bool blocked)61406d5caf4SAlan Jenkins void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked)
61506d5caf4SAlan Jenkins {
61606d5caf4SAlan Jenkins 	unsigned long flags;
61706d5caf4SAlan Jenkins 
61806d5caf4SAlan Jenkins 	BUG_ON(!rfkill);
61906d5caf4SAlan Jenkins 	BUG_ON(rfkill->registered);
62006d5caf4SAlan Jenkins 
62106d5caf4SAlan Jenkins 	spin_lock_irqsave(&rfkill->lock, flags);
62206d5caf4SAlan Jenkins 	__rfkill_set_sw_state(rfkill, blocked);
62306d5caf4SAlan Jenkins 	rfkill->persistent = true;
62406d5caf4SAlan Jenkins 	spin_unlock_irqrestore(&rfkill->lock, flags);
62506d5caf4SAlan Jenkins }
62606d5caf4SAlan Jenkins EXPORT_SYMBOL(rfkill_init_sw_state);
62706d5caf4SAlan Jenkins 
rfkill_set_states(struct rfkill * rfkill,bool sw,bool hw)62819d337dfSJohannes Berg void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
62919d337dfSJohannes Berg {
63019d337dfSJohannes Berg 	unsigned long flags;
63119d337dfSJohannes Berg 	bool swprev, hwprev;
63219d337dfSJohannes Berg 
63319d337dfSJohannes Berg 	BUG_ON(!rfkill);
63419d337dfSJohannes Berg 
63519d337dfSJohannes Berg 	spin_lock_irqsave(&rfkill->lock, flags);
63619d337dfSJohannes Berg 
63719d337dfSJohannes Berg 	/*
63819d337dfSJohannes Berg 	 * No need to care about prev/setblock ... this is for uevent only
63919d337dfSJohannes Berg 	 * and that will get triggered by rfkill_set_block anyway.
64019d337dfSJohannes Berg 	 */
64119d337dfSJohannes Berg 	swprev = !!(rfkill->state & RFKILL_BLOCK_SW);
64219d337dfSJohannes Berg 	hwprev = !!(rfkill->state & RFKILL_BLOCK_HW);
64319d337dfSJohannes Berg 	__rfkill_set_sw_state(rfkill, sw);
64448ab3578SAlan Jenkins 	if (hw)
64548ab3578SAlan Jenkins 		rfkill->state |= RFKILL_BLOCK_HW;
64648ab3578SAlan Jenkins 	else
64748ab3578SAlan Jenkins 		rfkill->state &= ~RFKILL_BLOCK_HW;
64819d337dfSJohannes Berg 
64919d337dfSJohannes Berg 	spin_unlock_irqrestore(&rfkill->lock, flags);
65019d337dfSJohannes Berg 
651b3fa1329SAlan Jenkins 	if (!rfkill->registered) {
652b3fa1329SAlan Jenkins 		rfkill->persistent = true;
653b3fa1329SAlan Jenkins 	} else {
65419d337dfSJohannes Berg 		if (swprev != sw || hwprev != hw)
65519d337dfSJohannes Berg 			schedule_work(&rfkill->uevent_work);
65619d337dfSJohannes Berg 
65719d337dfSJohannes Berg 		rfkill_led_trigger_event(rfkill);
658d874cd74SJoão Paulo Rechi Vita 		rfkill_global_led_trigger_event();
65919d337dfSJohannes Berg 	}
660b3fa1329SAlan Jenkins }
66119d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_set_states);
66219d337dfSJohannes Berg 
663648b50ddSHeikki Krogerus static const char * const rfkill_types[] = {
664648b50ddSHeikki Krogerus 	NULL, /* RFKILL_TYPE_ALL */
665648b50ddSHeikki Krogerus 	"wlan",
666648b50ddSHeikki Krogerus 	"bluetooth",
667648b50ddSHeikki Krogerus 	"ultrawideband",
668648b50ddSHeikki Krogerus 	"wimax",
669648b50ddSHeikki Krogerus 	"wwan",
670648b50ddSHeikki Krogerus 	"gps",
671648b50ddSHeikki Krogerus 	"fm",
672648b50ddSHeikki Krogerus 	"nfc",
673648b50ddSHeikki Krogerus };
674648b50ddSHeikki Krogerus 
rfkill_find_type(const char * name)675648b50ddSHeikki Krogerus enum rfkill_type rfkill_find_type(const char *name)
676648b50ddSHeikki Krogerus {
677648b50ddSHeikki Krogerus 	int i;
678648b50ddSHeikki Krogerus 
679648b50ddSHeikki Krogerus 	BUILD_BUG_ON(ARRAY_SIZE(rfkill_types) != NUM_RFKILL_TYPES);
680648b50ddSHeikki Krogerus 
681648b50ddSHeikki Krogerus 	if (!name)
682648b50ddSHeikki Krogerus 		return RFKILL_TYPE_ALL;
683648b50ddSHeikki Krogerus 
684648b50ddSHeikki Krogerus 	for (i = 1; i < NUM_RFKILL_TYPES; i++)
685648b50ddSHeikki Krogerus 		if (!strcmp(name, rfkill_types[i]))
686648b50ddSHeikki Krogerus 			return i;
687648b50ddSHeikki Krogerus 	return RFKILL_TYPE_ALL;
688648b50ddSHeikki Krogerus }
689648b50ddSHeikki Krogerus EXPORT_SYMBOL(rfkill_find_type);
690648b50ddSHeikki Krogerus 
name_show(struct device * dev,struct device_attribute * attr,char * buf)691e49df67dSGreg Kroah-Hartman static ssize_t name_show(struct device *dev, struct device_attribute *attr,
69219d337dfSJohannes Berg 			 char *buf)
69319d337dfSJohannes Berg {
69419d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
69519d337dfSJohannes Berg 
696796703baSBo Liu 	return sysfs_emit(buf, "%s\n", rfkill->name);
69719d337dfSJohannes Berg }
698e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(name);
69919d337dfSJohannes Berg 
type_show(struct device * dev,struct device_attribute * attr,char * buf)700e49df67dSGreg Kroah-Hartman static ssize_t type_show(struct device *dev, struct device_attribute *attr,
70119d337dfSJohannes Berg 			 char *buf)
70219d337dfSJohannes Berg {
70319d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
70419d337dfSJohannes Berg 
705796703baSBo Liu 	return sysfs_emit(buf, "%s\n", rfkill_types[rfkill->type]);
70619d337dfSJohannes Berg }
707e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(type);
70819d337dfSJohannes Berg 
index_show(struct device * dev,struct device_attribute * attr,char * buf)709e49df67dSGreg Kroah-Hartman static ssize_t index_show(struct device *dev, struct device_attribute *attr,
710c64fb016SJohannes Berg 			  char *buf)
711c64fb016SJohannes Berg {
712c64fb016SJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
713c64fb016SJohannes Berg 
714796703baSBo Liu 	return sysfs_emit(buf, "%d\n", rfkill->idx);
715c64fb016SJohannes Berg }
716e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(index);
717c64fb016SJohannes Berg 
persistent_show(struct device * dev,struct device_attribute * attr,char * buf)718e49df67dSGreg Kroah-Hartman static ssize_t persistent_show(struct device *dev,
719e49df67dSGreg Kroah-Hartman 			       struct device_attribute *attr, char *buf)
720464902e8SAlan Jenkins {
721464902e8SAlan Jenkins 	struct rfkill *rfkill = to_rfkill(dev);
722464902e8SAlan Jenkins 
723796703baSBo Liu 	return sysfs_emit(buf, "%d\n", rfkill->persistent);
724464902e8SAlan Jenkins }
725e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(persistent);
726464902e8SAlan Jenkins 
hard_show(struct device * dev,struct device_attribute * attr,char * buf)727e49df67dSGreg Kroah-Hartman static ssize_t hard_show(struct device *dev, struct device_attribute *attr,
7286c26361eS[email protected] 			 char *buf)
7296c26361eS[email protected] {
7306c26361eS[email protected] 	struct rfkill *rfkill = to_rfkill(dev);
7316c26361eS[email protected] 
732796703baSBo Liu 	return sysfs_emit(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_HW) ? 1 : 0);
7336c26361eS[email protected] }
734e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RO(hard);
7356c26361eS[email protected] 
soft_show(struct device * dev,struct device_attribute * attr,char * buf)736e49df67dSGreg Kroah-Hartman static ssize_t soft_show(struct device *dev, struct device_attribute *attr,
7376c26361eS[email protected] 			 char *buf)
7386c26361eS[email protected] {
7396c26361eS[email protected] 	struct rfkill *rfkill = to_rfkill(dev);
7406c26361eS[email protected] 
7412c3dfba4SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
7422c3dfba4SJohannes Berg 	rfkill_sync(rfkill);
7432c3dfba4SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
7442c3dfba4SJohannes Berg 
745796703baSBo Liu 	return sysfs_emit(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_SW) ? 1 : 0);
7466c26361eS[email protected] }
7476c26361eS[email protected] 
soft_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)748e49df67dSGreg Kroah-Hartman static ssize_t soft_store(struct device *dev, struct device_attribute *attr,
7496c26361eS[email protected] 			  const char *buf, size_t count)
7506c26361eS[email protected] {
7516c26361eS[email protected] 	struct rfkill *rfkill = to_rfkill(dev);
7526c26361eS[email protected] 	unsigned long state;
7536c26361eS[email protected] 	int err;
7546c26361eS[email protected] 
7556c26361eS[email protected] 	if (!capable(CAP_NET_ADMIN))
7566c26361eS[email protected] 		return -EPERM;
7576c26361eS[email protected] 
7581bac92caSJulia Lawall 	err = kstrtoul(buf, 0, &state);
7596c26361eS[email protected] 	if (err)
7606c26361eS[email protected] 		return err;
7616c26361eS[email protected] 
7626c26361eS[email protected] 	if (state > 1 )
7636c26361eS[email protected] 		return -EINVAL;
7646c26361eS[email protected] 
7656c26361eS[email protected] 	mutex_lock(&rfkill_global_mutex);
7662c3dfba4SJohannes Berg 	rfkill_sync(rfkill);
7676c26361eS[email protected] 	rfkill_set_block(rfkill, state);
7686c26361eS[email protected] 	mutex_unlock(&rfkill_global_mutex);
7696c26361eS[email protected] 
7706f7c962cSAlan Cox 	return count;
7716c26361eS[email protected] }
772e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RW(soft);
7736c26361eS[email protected] 
hard_block_reasons_show(struct device * dev,struct device_attribute * attr,char * buf)77414486c82SEmmanuel Grumbach static ssize_t hard_block_reasons_show(struct device *dev,
77514486c82SEmmanuel Grumbach 				       struct device_attribute *attr,
77614486c82SEmmanuel Grumbach 				       char *buf)
77714486c82SEmmanuel Grumbach {
77814486c82SEmmanuel Grumbach 	struct rfkill *rfkill = to_rfkill(dev);
77914486c82SEmmanuel Grumbach 
780796703baSBo Liu 	return sysfs_emit(buf, "0x%lx\n", rfkill->hard_block_reasons);
78114486c82SEmmanuel Grumbach }
78214486c82SEmmanuel Grumbach static DEVICE_ATTR_RO(hard_block_reasons);
78314486c82SEmmanuel Grumbach 
user_state_from_blocked(unsigned long state)78419d337dfSJohannes Berg static u8 user_state_from_blocked(unsigned long state)
78519d337dfSJohannes Berg {
78619d337dfSJohannes Berg 	if (state & RFKILL_BLOCK_HW)
78719d337dfSJohannes Berg 		return RFKILL_USER_STATE_HARD_BLOCKED;
78819d337dfSJohannes Berg 	if (state & RFKILL_BLOCK_SW)
78919d337dfSJohannes Berg 		return RFKILL_USER_STATE_SOFT_BLOCKED;
79019d337dfSJohannes Berg 
79119d337dfSJohannes Berg 	return RFKILL_USER_STATE_UNBLOCKED;
79219d337dfSJohannes Berg }
79319d337dfSJohannes Berg 
state_show(struct device * dev,struct device_attribute * attr,char * buf)794e49df67dSGreg Kroah-Hartman static ssize_t state_show(struct device *dev, struct device_attribute *attr,
79519d337dfSJohannes Berg 			  char *buf)
79619d337dfSJohannes Berg {
79719d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
79819d337dfSJohannes Berg 
7992c3dfba4SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
8002c3dfba4SJohannes Berg 	rfkill_sync(rfkill);
8012c3dfba4SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
8022c3dfba4SJohannes Berg 
803796703baSBo Liu 	return sysfs_emit(buf, "%d\n", user_state_from_blocked(rfkill->state));
80419d337dfSJohannes Berg }
80519d337dfSJohannes Berg 
state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)806e49df67dSGreg Kroah-Hartman static ssize_t state_store(struct device *dev, struct device_attribute *attr,
80719d337dfSJohannes Berg 			   const char *buf, size_t count)
80819d337dfSJohannes Berg {
809f54c1427SJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
810f54c1427SJohannes Berg 	unsigned long state;
811f54c1427SJohannes Berg 	int err;
81219d337dfSJohannes Berg 
813f54c1427SJohannes Berg 	if (!capable(CAP_NET_ADMIN))
81419d337dfSJohannes Berg 		return -EPERM;
815f54c1427SJohannes Berg 
8161bac92caSJulia Lawall 	err = kstrtoul(buf, 0, &state);
817f54c1427SJohannes Berg 	if (err)
818f54c1427SJohannes Berg 		return err;
819f54c1427SJohannes Berg 
820f54c1427SJohannes Berg 	if (state != RFKILL_USER_STATE_SOFT_BLOCKED &&
821f54c1427SJohannes Berg 	    state != RFKILL_USER_STATE_UNBLOCKED)
822f54c1427SJohannes Berg 		return -EINVAL;
823f54c1427SJohannes Berg 
824f54c1427SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
8252c3dfba4SJohannes Berg 	rfkill_sync(rfkill);
826f54c1427SJohannes Berg 	rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED);
827f54c1427SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
828f54c1427SJohannes Berg 
8296f7c962cSAlan Cox 	return count;
83019d337dfSJohannes Berg }
831e49df67dSGreg Kroah-Hartman static DEVICE_ATTR_RW(state);
83219d337dfSJohannes Berg 
833e49df67dSGreg Kroah-Hartman static struct attribute *rfkill_dev_attrs[] = {
834e49df67dSGreg Kroah-Hartman 	&dev_attr_name.attr,
835e49df67dSGreg Kroah-Hartman 	&dev_attr_type.attr,
836e49df67dSGreg Kroah-Hartman 	&dev_attr_index.attr,
837e49df67dSGreg Kroah-Hartman 	&dev_attr_persistent.attr,
838e49df67dSGreg Kroah-Hartman 	&dev_attr_state.attr,
839e49df67dSGreg Kroah-Hartman 	&dev_attr_soft.attr,
840e49df67dSGreg Kroah-Hartman 	&dev_attr_hard.attr,
84114486c82SEmmanuel Grumbach 	&dev_attr_hard_block_reasons.attr,
842e49df67dSGreg Kroah-Hartman 	NULL,
84319d337dfSJohannes Berg };
844e49df67dSGreg Kroah-Hartman ATTRIBUTE_GROUPS(rfkill_dev);
84519d337dfSJohannes Berg 
rfkill_release(struct device * dev)84619d337dfSJohannes Berg static void rfkill_release(struct device *dev)
84719d337dfSJohannes Berg {
84819d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
84919d337dfSJohannes Berg 
85019d337dfSJohannes Berg 	kfree(rfkill);
85119d337dfSJohannes Berg }
85219d337dfSJohannes Berg 
rfkill_dev_uevent(const struct device * dev,struct kobj_uevent_env * env)85323680f0bSGreg Kroah-Hartman static int rfkill_dev_uevent(const struct device *dev, struct kobj_uevent_env *env)
85419d337dfSJohannes Berg {
85519d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
85619d337dfSJohannes Berg 	unsigned long flags;
85714486c82SEmmanuel Grumbach 	unsigned long reasons;
85819d337dfSJohannes Berg 	u32 state;
85919d337dfSJohannes Berg 	int error;
86019d337dfSJohannes Berg 
86119d337dfSJohannes Berg 	error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
86219d337dfSJohannes Berg 	if (error)
86319d337dfSJohannes Berg 		return error;
86419d337dfSJohannes Berg 	error = add_uevent_var(env, "RFKILL_TYPE=%s",
865648b50ddSHeikki Krogerus 			       rfkill_types[rfkill->type]);
86619d337dfSJohannes Berg 	if (error)
86719d337dfSJohannes Berg 		return error;
86819d337dfSJohannes Berg 	spin_lock_irqsave(&rfkill->lock, flags);
86919d337dfSJohannes Berg 	state = rfkill->state;
87014486c82SEmmanuel Grumbach 	reasons = rfkill->hard_block_reasons;
87119d337dfSJohannes Berg 	spin_unlock_irqrestore(&rfkill->lock, flags);
87219d337dfSJohannes Berg 	error = add_uevent_var(env, "RFKILL_STATE=%d",
87319d337dfSJohannes Berg 			       user_state_from_blocked(state));
87414486c82SEmmanuel Grumbach 	if (error)
87519d337dfSJohannes Berg 		return error;
87614486c82SEmmanuel Grumbach 	return add_uevent_var(env, "RFKILL_HW_BLOCK_REASON=0x%lx", reasons);
87719d337dfSJohannes Berg }
87819d337dfSJohannes Berg 
rfkill_pause_polling(struct rfkill * rfkill)87919d337dfSJohannes Berg void rfkill_pause_polling(struct rfkill *rfkill)
88019d337dfSJohannes Berg {
88119d337dfSJohannes Berg 	BUG_ON(!rfkill);
88219d337dfSJohannes Berg 
88319d337dfSJohannes Berg 	if (!rfkill->ops->poll)
88419d337dfSJohannes Berg 		return;
88519d337dfSJohannes Berg 
886dd21dfc6SJohannes Berg 	rfkill->polling_paused = true;
88719d337dfSJohannes Berg 	cancel_delayed_work_sync(&rfkill->poll_work);
88819d337dfSJohannes Berg }
88919d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_pause_polling);
89019d337dfSJohannes Berg 
rfkill_resume_polling(struct rfkill * rfkill)89119d337dfSJohannes Berg void rfkill_resume_polling(struct rfkill *rfkill)
89219d337dfSJohannes Berg {
89319d337dfSJohannes Berg 	BUG_ON(!rfkill);
89419d337dfSJohannes Berg 
89519d337dfSJohannes Berg 	if (!rfkill->ops->poll)
89619d337dfSJohannes Berg 		return;
89719d337dfSJohannes Berg 
898dd21dfc6SJohannes Berg 	rfkill->polling_paused = false;
899dd21dfc6SJohannes Berg 
900dd21dfc6SJohannes Berg 	if (rfkill->suspended)
901dd21dfc6SJohannes Berg 		return;
902dd21dfc6SJohannes Berg 
90367235cbcSShaibal Dutta 	queue_delayed_work(system_power_efficient_wq,
90467235cbcSShaibal Dutta 			   &rfkill->poll_work, 0);
90519d337dfSJohannes Berg }
90619d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_resume_polling);
90719d337dfSJohannes Berg 
90828f297a7SLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
rfkill_suspend(struct device * dev)90928f297a7SLars-Peter Clausen static int rfkill_suspend(struct device *dev)
91019d337dfSJohannes Berg {
91119d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
91219d337dfSJohannes Berg 
913dd21dfc6SJohannes Berg 	rfkill->suspended = true;
914dd21dfc6SJohannes Berg 	cancel_delayed_work_sync(&rfkill->poll_work);
91519d337dfSJohannes Berg 
91619d337dfSJohannes Berg 	return 0;
91719d337dfSJohannes Berg }
91819d337dfSJohannes Berg 
rfkill_resume(struct device * dev)91919d337dfSJohannes Berg static int rfkill_resume(struct device *dev)
92019d337dfSJohannes Berg {
92119d337dfSJohannes Berg 	struct rfkill *rfkill = to_rfkill(dev);
92219d337dfSJohannes Berg 	bool cur;
92319d337dfSJohannes Berg 
924dd21dfc6SJohannes Berg 	rfkill->suspended = false;
925dd21dfc6SJohannes Berg 
92694e2bd0bSClaire Chang 	if (!rfkill->registered)
92794e2bd0bSClaire Chang 		return 0;
92894e2bd0bSClaire Chang 
92906d5caf4SAlan Jenkins 	if (!rfkill->persistent) {
930908209c1SAlan Jenkins 		cur = !!(rfkill->state & RFKILL_BLOCK_SW);
93119d337dfSJohannes Berg 		rfkill_set_block(rfkill, cur);
93206d5caf4SAlan Jenkins 	}
93319d337dfSJohannes Berg 
934dd21dfc6SJohannes Berg 	if (rfkill->ops->poll && !rfkill->polling_paused)
935dd21dfc6SJohannes Berg 		queue_delayed_work(system_power_efficient_wq,
936dd21dfc6SJohannes Berg 				   &rfkill->poll_work, 0);
93719d337dfSJohannes Berg 
93819d337dfSJohannes Berg 	return 0;
93919d337dfSJohannes Berg }
94019d337dfSJohannes Berg 
94128f297a7SLars-Peter Clausen static SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume);
94228f297a7SLars-Peter Clausen #define RFKILL_PM_OPS (&rfkill_pm_ops)
94328f297a7SLars-Peter Clausen #else
94428f297a7SLars-Peter Clausen #define RFKILL_PM_OPS NULL
94528f297a7SLars-Peter Clausen #endif
94628f297a7SLars-Peter Clausen 
94719d337dfSJohannes Berg static struct class rfkill_class = {
94819d337dfSJohannes Berg 	.name		= "rfkill",
94919d337dfSJohannes Berg 	.dev_release	= rfkill_release,
950e49df67dSGreg Kroah-Hartman 	.dev_groups	= rfkill_dev_groups,
95119d337dfSJohannes Berg 	.dev_uevent	= rfkill_dev_uevent,
95228f297a7SLars-Peter Clausen 	.pm		= RFKILL_PM_OPS,
95319d337dfSJohannes Berg };
95419d337dfSJohannes Berg 
rfkill_blocked(struct rfkill * rfkill)9556081162eSJohannes Berg bool rfkill_blocked(struct rfkill *rfkill)
9566081162eSJohannes Berg {
9576081162eSJohannes Berg 	unsigned long flags;
9586081162eSJohannes Berg 	u32 state;
9596081162eSJohannes Berg 
9606081162eSJohannes Berg 	spin_lock_irqsave(&rfkill->lock, flags);
9616081162eSJohannes Berg 	state = rfkill->state;
9626081162eSJohannes Berg 	spin_unlock_irqrestore(&rfkill->lock, flags);
9636081162eSJohannes Berg 
9646081162eSJohannes Berg 	return !!(state & RFKILL_BLOCK_ANY);
9656081162eSJohannes Berg }
9666081162eSJohannes Berg EXPORT_SYMBOL(rfkill_blocked);
9676081162eSJohannes Berg 
rfkill_soft_blocked(struct rfkill * rfkill)9685bc9a9ddSEmmanuel Grumbach bool rfkill_soft_blocked(struct rfkill *rfkill)
9695bc9a9ddSEmmanuel Grumbach {
9705bc9a9ddSEmmanuel Grumbach 	unsigned long flags;
9715bc9a9ddSEmmanuel Grumbach 	u32 state;
9725bc9a9ddSEmmanuel Grumbach 
9735bc9a9ddSEmmanuel Grumbach 	spin_lock_irqsave(&rfkill->lock, flags);
9745bc9a9ddSEmmanuel Grumbach 	state = rfkill->state;
9755bc9a9ddSEmmanuel Grumbach 	spin_unlock_irqrestore(&rfkill->lock, flags);
9765bc9a9ddSEmmanuel Grumbach 
9775bc9a9ddSEmmanuel Grumbach 	return !!(state & RFKILL_BLOCK_SW);
9785bc9a9ddSEmmanuel Grumbach }
9795bc9a9ddSEmmanuel Grumbach EXPORT_SYMBOL(rfkill_soft_blocked);
98019d337dfSJohannes Berg 
rfkill_alloc(const char * name,struct device * parent,const enum rfkill_type type,const struct rfkill_ops * ops,void * ops_data)98119d337dfSJohannes Berg struct rfkill * __must_check rfkill_alloc(const char *name,
98219d337dfSJohannes Berg 					  struct device *parent,
98319d337dfSJohannes Berg 					  const enum rfkill_type type,
98419d337dfSJohannes Berg 					  const struct rfkill_ops *ops,
98519d337dfSJohannes Berg 					  void *ops_data)
98619d337dfSJohannes Berg {
98719d337dfSJohannes Berg 	struct rfkill *rfkill;
98819d337dfSJohannes Berg 	struct device *dev;
98919d337dfSJohannes Berg 
99019d337dfSJohannes Berg 	if (WARN_ON(!ops))
99119d337dfSJohannes Berg 		return NULL;
99219d337dfSJohannes Berg 
99319d337dfSJohannes Berg 	if (WARN_ON(!ops->set_block))
99419d337dfSJohannes Berg 		return NULL;
99519d337dfSJohannes Berg 
99619d337dfSJohannes Berg 	if (WARN_ON(!name))
99719d337dfSJohannes Berg 		return NULL;
99819d337dfSJohannes Berg 
999c64fb016SJohannes Berg 	if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES))
100019d337dfSJohannes Berg 		return NULL;
100119d337dfSJohannes Berg 
1002b7bb1100SJohannes Berg 	rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL);
100319d337dfSJohannes Berg 	if (!rfkill)
100419d337dfSJohannes Berg 		return NULL;
100519d337dfSJohannes Berg 
100619d337dfSJohannes Berg 	spin_lock_init(&rfkill->lock);
100719d337dfSJohannes Berg 	INIT_LIST_HEAD(&rfkill->node);
100819d337dfSJohannes Berg 	rfkill->type = type;
1009b7bb1100SJohannes Berg 	strcpy(rfkill->name, name);
101019d337dfSJohannes Berg 	rfkill->ops = ops;
101119d337dfSJohannes Berg 	rfkill->data = ops_data;
101219d337dfSJohannes Berg 
101319d337dfSJohannes Berg 	dev = &rfkill->dev;
101419d337dfSJohannes Berg 	dev->class = &rfkill_class;
101519d337dfSJohannes Berg 	dev->parent = parent;
101619d337dfSJohannes Berg 	device_initialize(dev);
101719d337dfSJohannes Berg 
101819d337dfSJohannes Berg 	return rfkill;
101919d337dfSJohannes Berg }
102019d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_alloc);
102119d337dfSJohannes Berg 
rfkill_poll(struct work_struct * work)102219d337dfSJohannes Berg static void rfkill_poll(struct work_struct *work)
102319d337dfSJohannes Berg {
102419d337dfSJohannes Berg 	struct rfkill *rfkill;
102519d337dfSJohannes Berg 
102619d337dfSJohannes Berg 	rfkill = container_of(work, struct rfkill, poll_work.work);
102719d337dfSJohannes Berg 
102819d337dfSJohannes Berg 	/*
102919d337dfSJohannes Berg 	 * Poll hardware state -- driver will use one of the
103019d337dfSJohannes Berg 	 * rfkill_set{,_hw,_sw}_state functions and use its
103119d337dfSJohannes Berg 	 * return value to update the current status.
103219d337dfSJohannes Berg 	 */
103319d337dfSJohannes Berg 	rfkill->ops->poll(rfkill, rfkill->data);
103419d337dfSJohannes Berg 
103567235cbcSShaibal Dutta 	queue_delayed_work(system_power_efficient_wq,
103667235cbcSShaibal Dutta 		&rfkill->poll_work,
103719d337dfSJohannes Berg 		round_jiffies_relative(POLL_INTERVAL));
103819d337dfSJohannes Berg }
103919d337dfSJohannes Berg 
rfkill_uevent_work(struct work_struct * work)104019d337dfSJohannes Berg static void rfkill_uevent_work(struct work_struct *work)
104119d337dfSJohannes Berg {
104219d337dfSJohannes Berg 	struct rfkill *rfkill;
104319d337dfSJohannes Berg 
104419d337dfSJohannes Berg 	rfkill = container_of(work, struct rfkill, uevent_work);
104519d337dfSJohannes Berg 
1046c64fb016SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
1047c64fb016SJohannes Berg 	rfkill_event(rfkill);
1048c64fb016SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
104919d337dfSJohannes Berg }
105019d337dfSJohannes Berg 
rfkill_sync_work(struct work_struct * work)105119d337dfSJohannes Berg static void rfkill_sync_work(struct work_struct *work)
105219d337dfSJohannes Berg {
10532c3dfba4SJohannes Berg 	struct rfkill *rfkill = container_of(work, struct rfkill, sync_work);
105419d337dfSJohannes Berg 
105519d337dfSJohannes Berg 	mutex_lock(&rfkill_global_mutex);
10562c3dfba4SJohannes Berg 	rfkill_sync(rfkill);
105719d337dfSJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
105819d337dfSJohannes Berg }
105919d337dfSJohannes Berg 
rfkill_register(struct rfkill * rfkill)106019d337dfSJohannes Berg int __must_check rfkill_register(struct rfkill *rfkill)
106119d337dfSJohannes Berg {
106219d337dfSJohannes Berg 	static unsigned long rfkill_no;
10636fc232dbSAditya Pakki 	struct device *dev;
106419d337dfSJohannes Berg 	int error;
106519d337dfSJohannes Berg 
10666fc232dbSAditya Pakki 	if (!rfkill)
10676fc232dbSAditya Pakki 		return -EINVAL;
10686fc232dbSAditya Pakki 
10696fc232dbSAditya Pakki 	dev = &rfkill->dev;
107019d337dfSJohannes Berg 
107119d337dfSJohannes Berg 	mutex_lock(&rfkill_global_mutex);
107219d337dfSJohannes Berg 
107319d337dfSJohannes Berg 	if (rfkill->registered) {
107419d337dfSJohannes Berg 		error = -EALREADY;
107519d337dfSJohannes Berg 		goto unlock;
107619d337dfSJohannes Berg 	}
107719d337dfSJohannes Berg 
1078c64fb016SJohannes Berg 	rfkill->idx = rfkill_no;
107919d337dfSJohannes Berg 	dev_set_name(dev, "rfkill%lu", rfkill_no);
108019d337dfSJohannes Berg 	rfkill_no++;
108119d337dfSJohannes Berg 
108219d337dfSJohannes Berg 	list_add_tail(&rfkill->node, &rfkill_list);
108319d337dfSJohannes Berg 
108419d337dfSJohannes Berg 	error = device_add(dev);
108519d337dfSJohannes Berg 	if (error)
108619d337dfSJohannes Berg 		goto remove;
108719d337dfSJohannes Berg 
108819d337dfSJohannes Berg 	error = rfkill_led_trigger_register(rfkill);
108919d337dfSJohannes Berg 	if (error)
109019d337dfSJohannes Berg 		goto devdel;
109119d337dfSJohannes Berg 
109219d337dfSJohannes Berg 	rfkill->registered = true;
109319d337dfSJohannes Berg 
109419d337dfSJohannes Berg 	INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll);
10952ec2c68cSJohannes Berg 	INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work);
10962ec2c68cSJohannes Berg 	INIT_WORK(&rfkill->sync_work, rfkill_sync_work);
10972ec2c68cSJohannes Berg 
10982ec2c68cSJohannes Berg 	if (rfkill->ops->poll)
109967235cbcSShaibal Dutta 		queue_delayed_work(system_power_efficient_wq,
110067235cbcSShaibal Dutta 			&rfkill->poll_work,
110119d337dfSJohannes Berg 			round_jiffies_relative(POLL_INTERVAL));
1102b3fa1329SAlan Jenkins 
1103b3fa1329SAlan Jenkins 	if (!rfkill->persistent || rfkill_epo_lock_active) {
11042c3dfba4SJohannes Berg 		rfkill->need_sync = true;
110519d337dfSJohannes Berg 		schedule_work(&rfkill->sync_work);
1106b3fa1329SAlan Jenkins 	} else {
1107b3fa1329SAlan Jenkins #ifdef CONFIG_RFKILL_INPUT
1108b3fa1329SAlan Jenkins 		bool soft_blocked = !!(rfkill->state & RFKILL_BLOCK_SW);
1109b3fa1329SAlan Jenkins 
1110b3fa1329SAlan Jenkins 		if (!atomic_read(&rfkill_input_disabled))
1111b3fa1329SAlan Jenkins 			__rfkill_switch_all(rfkill->type, soft_blocked);
1112b3fa1329SAlan Jenkins #endif
1113b3fa1329SAlan Jenkins 	}
11142ec2c68cSJohannes Berg 
1115d874cd74SJoão Paulo Rechi Vita 	rfkill_global_led_trigger_event();
1116c64fb016SJohannes Berg 	rfkill_send_events(rfkill, RFKILL_OP_ADD);
111719d337dfSJohannes Berg 
111819d337dfSJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
111919d337dfSJohannes Berg 	return 0;
112019d337dfSJohannes Berg 
112119d337dfSJohannes Berg  devdel:
112219d337dfSJohannes Berg 	device_del(&rfkill->dev);
112319d337dfSJohannes Berg  remove:
112419d337dfSJohannes Berg 	list_del_init(&rfkill->node);
112519d337dfSJohannes Berg  unlock:
112619d337dfSJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
112719d337dfSJohannes Berg 	return error;
112819d337dfSJohannes Berg }
112919d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_register);
113019d337dfSJohannes Berg 
rfkill_unregister(struct rfkill * rfkill)113119d337dfSJohannes Berg void rfkill_unregister(struct rfkill *rfkill)
113219d337dfSJohannes Berg {
113319d337dfSJohannes Berg 	BUG_ON(!rfkill);
113419d337dfSJohannes Berg 
113519d337dfSJohannes Berg 	if (rfkill->ops->poll)
113619d337dfSJohannes Berg 		cancel_delayed_work_sync(&rfkill->poll_work);
113719d337dfSJohannes Berg 
113819d337dfSJohannes Berg 	cancel_work_sync(&rfkill->uevent_work);
113919d337dfSJohannes Berg 	cancel_work_sync(&rfkill->sync_work);
114019d337dfSJohannes Berg 
114119d337dfSJohannes Berg 	rfkill->registered = false;
114219d337dfSJohannes Berg 
114319d337dfSJohannes Berg 	device_del(&rfkill->dev);
114419d337dfSJohannes Berg 
114519d337dfSJohannes Berg 	mutex_lock(&rfkill_global_mutex);
1146c64fb016SJohannes Berg 	rfkill_send_events(rfkill, RFKILL_OP_DEL);
114719d337dfSJohannes Berg 	list_del_init(&rfkill->node);
1148d874cd74SJoão Paulo Rechi Vita 	rfkill_global_led_trigger_event();
114919d337dfSJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
115019d337dfSJohannes Berg 
115119d337dfSJohannes Berg 	rfkill_led_trigger_unregister(rfkill);
115219d337dfSJohannes Berg }
115319d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_unregister);
115419d337dfSJohannes Berg 
rfkill_destroy(struct rfkill * rfkill)115519d337dfSJohannes Berg void rfkill_destroy(struct rfkill *rfkill)
115619d337dfSJohannes Berg {
115719d337dfSJohannes Berg 	if (rfkill)
115819d337dfSJohannes Berg 		put_device(&rfkill->dev);
115919d337dfSJohannes Berg }
116019d337dfSJohannes Berg EXPORT_SYMBOL(rfkill_destroy);
116119d337dfSJohannes Berg 
rfkill_fop_open(struct inode * inode,struct file * file)1162c64fb016SJohannes Berg static int rfkill_fop_open(struct inode *inode, struct file *file)
1163c64fb016SJohannes Berg {
1164c64fb016SJohannes Berg 	struct rfkill_data *data;
1165c64fb016SJohannes Berg 	struct rfkill *rfkill;
1166c64fb016SJohannes Berg 	struct rfkill_int_event *ev, *tmp;
1167c64fb016SJohannes Berg 
1168c64fb016SJohannes Berg 	data = kzalloc(sizeof(*data), GFP_KERNEL);
1169c64fb016SJohannes Berg 	if (!data)
1170c64fb016SJohannes Berg 		return -ENOMEM;
1171c64fb016SJohannes Berg 
117254f586a9SJohannes Berg 	data->max_size = RFKILL_EVENT_SIZE_V1;
117354f586a9SJohannes Berg 
1174c64fb016SJohannes Berg 	INIT_LIST_HEAD(&data->events);
1175c64fb016SJohannes Berg 	mutex_init(&data->mtx);
1176c64fb016SJohannes Berg 	init_waitqueue_head(&data->read_wait);
1177c64fb016SJohannes Berg 
1178c64fb016SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
1179c64fb016SJohannes Berg 	/*
1180c64fb016SJohannes Berg 	 * start getting events from elsewhere but hold mtx to get
1181c64fb016SJohannes Berg 	 * startup events added first
1182c64fb016SJohannes Berg 	 */
1183c64fb016SJohannes Berg 
1184c64fb016SJohannes Berg 	list_for_each_entry(rfkill, &rfkill_list, node) {
1185c64fb016SJohannes Berg 		ev = kzalloc(sizeof(*ev), GFP_KERNEL);
1186c64fb016SJohannes Berg 		if (!ev)
1187c64fb016SJohannes Berg 			goto free;
11882c3dfba4SJohannes Berg 		rfkill_sync(rfkill);
1189c64fb016SJohannes Berg 		rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD);
1190f2ac54ebSJohannes Berg 		mutex_lock(&data->mtx);
1191c64fb016SJohannes Berg 		list_add_tail(&ev->list, &data->events);
1192f2ac54ebSJohannes Berg 		mutex_unlock(&data->mtx);
1193c64fb016SJohannes Berg 	}
1194bd2281b8SJulia Lawall 	list_add(&data->list, &rfkill_fds);
1195c64fb016SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
1196c64fb016SJohannes Berg 
1197c64fb016SJohannes Berg 	file->private_data = data;
1198c64fb016SJohannes Berg 
1199c5bf68feSKirill Smelkov 	return stream_open(inode, file);
1200c64fb016SJohannes Berg 
1201c64fb016SJohannes Berg  free:
1202c64fb016SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
1203c64fb016SJohannes Berg 	mutex_destroy(&data->mtx);
1204c64fb016SJohannes Berg 	list_for_each_entry_safe(ev, tmp, &data->events, list)
1205c64fb016SJohannes Berg 		kfree(ev);
1206c64fb016SJohannes Berg 	kfree(data);
1207c64fb016SJohannes Berg 	return -ENOMEM;
1208c64fb016SJohannes Berg }
1209c64fb016SJohannes Berg 
rfkill_fop_poll(struct file * file,poll_table * wait)1210ade994f4SAl Viro static __poll_t rfkill_fop_poll(struct file *file, poll_table *wait)
1211c64fb016SJohannes Berg {
1212c64fb016SJohannes Berg 	struct rfkill_data *data = file->private_data;
1213a9a08845SLinus Torvalds 	__poll_t res = EPOLLOUT | EPOLLWRNORM;
1214c64fb016SJohannes Berg 
1215c64fb016SJohannes Berg 	poll_wait(file, &data->read_wait, wait);
1216c64fb016SJohannes Berg 
1217c64fb016SJohannes Berg 	mutex_lock(&data->mtx);
1218c64fb016SJohannes Berg 	if (!list_empty(&data->events))
1219a9a08845SLinus Torvalds 		res = EPOLLIN | EPOLLRDNORM;
1220c64fb016SJohannes Berg 	mutex_unlock(&data->mtx);
1221c64fb016SJohannes Berg 
1222c64fb016SJohannes Berg 	return res;
1223c64fb016SJohannes Berg }
1224c64fb016SJohannes Berg 
rfkill_fop_read(struct file * file,char __user * buf,size_t count,loff_t * pos)1225c64fb016SJohannes Berg static ssize_t rfkill_fop_read(struct file *file, char __user *buf,
1226c64fb016SJohannes Berg 			       size_t count, loff_t *pos)
1227c64fb016SJohannes Berg {
1228c64fb016SJohannes Berg 	struct rfkill_data *data = file->private_data;
1229c64fb016SJohannes Berg 	struct rfkill_int_event *ev;
1230c64fb016SJohannes Berg 	unsigned long sz;
1231c64fb016SJohannes Berg 	int ret;
1232c64fb016SJohannes Berg 
1233c64fb016SJohannes Berg 	mutex_lock(&data->mtx);
1234c64fb016SJohannes Berg 
1235c64fb016SJohannes Berg 	while (list_empty(&data->events)) {
1236c64fb016SJohannes Berg 		if (file->f_flags & O_NONBLOCK) {
1237c64fb016SJohannes Berg 			ret = -EAGAIN;
1238c64fb016SJohannes Berg 			goto out;
1239c64fb016SJohannes Berg 		}
1240c64fb016SJohannes Berg 		mutex_unlock(&data->mtx);
12416736fde9SJohannes Berg 		/* since we re-check and it just compares pointers,
12426736fde9SJohannes Berg 		 * using !list_empty() without locking isn't a problem
12436736fde9SJohannes Berg 		 */
1244c64fb016SJohannes Berg 		ret = wait_event_interruptible(data->read_wait,
12456736fde9SJohannes Berg 					       !list_empty(&data->events));
1246c64fb016SJohannes Berg 		mutex_lock(&data->mtx);
1247c64fb016SJohannes Berg 
1248c64fb016SJohannes Berg 		if (ret)
1249c64fb016SJohannes Berg 			goto out;
1250c64fb016SJohannes Berg 	}
1251c64fb016SJohannes Berg 
1252c64fb016SJohannes Berg 	ev = list_first_entry(&data->events, struct rfkill_int_event,
1253c64fb016SJohannes Berg 				list);
1254c64fb016SJohannes Berg 
1255c64fb016SJohannes Berg 	sz = min_t(unsigned long, sizeof(ev->ev), count);
125654f586a9SJohannes Berg 	sz = min_t(unsigned long, sz, data->max_size);
1257c64fb016SJohannes Berg 	ret = sz;
1258c64fb016SJohannes Berg 	if (copy_to_user(buf, &ev->ev, sz))
1259c64fb016SJohannes Berg 		ret = -EFAULT;
1260c64fb016SJohannes Berg 
1261c64fb016SJohannes Berg 	list_del(&ev->list);
1262c64fb016SJohannes Berg 	kfree(ev);
1263c64fb016SJohannes Berg  out:
1264c64fb016SJohannes Berg 	mutex_unlock(&data->mtx);
1265c64fb016SJohannes Berg 	return ret;
1266c64fb016SJohannes Berg }
1267c64fb016SJohannes Berg 
rfkill_fop_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)1268c64fb016SJohannes Berg static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
1269c64fb016SJohannes Berg 				size_t count, loff_t *pos)
1270c64fb016SJohannes Berg {
127154f586a9SJohannes Berg 	struct rfkill_data *data = file->private_data;
1272c64fb016SJohannes Berg 	struct rfkill *rfkill;
127371826654SJohannes Berg 	struct rfkill_event_ext ev;
12741948b2a2SJoão Paulo Rechi Vita 	int ret;
1275c64fb016SJohannes Berg 
1276c64fb016SJohannes Berg 	/* we don't need the 'hard' variable but accept it */
12771be491fcSJohannes Berg 	if (count < RFKILL_EVENT_SIZE_V1 - 1)
1278c64fb016SJohannes Berg 		return -EINVAL;
1279c64fb016SJohannes Berg 
12801be491fcSJohannes Berg 	/*
12811be491fcSJohannes Berg 	 * Copy as much data as we can accept into our 'ev' buffer,
12821be491fcSJohannes Berg 	 * but tell userspace how much we've copied so it can determine
12831be491fcSJohannes Berg 	 * our API version even in a write() call, if it cares.
12841be491fcSJohannes Berg 	 */
12851be491fcSJohannes Berg 	count = min(count, sizeof(ev));
128654f586a9SJohannes Berg 	count = min_t(size_t, count, data->max_size);
12871be491fcSJohannes Berg 	if (copy_from_user(&ev, buf, count))
1288c64fb016SJohannes Berg 		return -EFAULT;
1289c64fb016SJohannes Berg 
1290c64fb016SJohannes Berg 	if (ev.type >= NUM_RFKILL_TYPES)
1291c64fb016SJohannes Berg 		return -EINVAL;
1292c64fb016SJohannes Berg 
1293c64fb016SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
1294c64fb016SJohannes Berg 
12951948b2a2SJoão Paulo Rechi Vita 	switch (ev.op) {
12961948b2a2SJoão Paulo Rechi Vita 	case RFKILL_OP_CHANGE_ALL:
12979487bd6bSJoão Paulo Rechi Vita 		rfkill_update_global_state(ev.type, ev.soft);
12981948b2a2SJoão Paulo Rechi Vita 		list_for_each_entry(rfkill, &rfkill_list, node)
12991948b2a2SJoão Paulo Rechi Vita 			if (rfkill->type == ev.type ||
13001948b2a2SJoão Paulo Rechi Vita 			    ev.type == RFKILL_TYPE_ALL)
1301c64fb016SJohannes Berg 				rfkill_set_block(rfkill, ev.soft);
13021948b2a2SJoão Paulo Rechi Vita 		ret = 0;
13031948b2a2SJoão Paulo Rechi Vita 		break;
13041948b2a2SJoão Paulo Rechi Vita 	case RFKILL_OP_CHANGE:
13051948b2a2SJoão Paulo Rechi Vita 		list_for_each_entry(rfkill, &rfkill_list, node)
13061948b2a2SJoão Paulo Rechi Vita 			if (rfkill->idx == ev.idx &&
13071948b2a2SJoão Paulo Rechi Vita 			    (rfkill->type == ev.type ||
13081948b2a2SJoão Paulo Rechi Vita 			     ev.type == RFKILL_TYPE_ALL))
13091948b2a2SJoão Paulo Rechi Vita 				rfkill_set_block(rfkill, ev.soft);
13101948b2a2SJoão Paulo Rechi Vita 		ret = 0;
13111948b2a2SJoão Paulo Rechi Vita 		break;
13121948b2a2SJoão Paulo Rechi Vita 	default:
13131948b2a2SJoão Paulo Rechi Vita 		ret = -EINVAL;
13141948b2a2SJoão Paulo Rechi Vita 		break;
1315c64fb016SJohannes Berg 	}
13161948b2a2SJoão Paulo Rechi Vita 
1317c64fb016SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
1318c64fb016SJohannes Berg 
13191948b2a2SJoão Paulo Rechi Vita 	return ret ?: count;
1320c64fb016SJohannes Berg }
1321c64fb016SJohannes Berg 
rfkill_fop_release(struct inode * inode,struct file * file)1322c64fb016SJohannes Berg static int rfkill_fop_release(struct inode *inode, struct file *file)
1323c64fb016SJohannes Berg {
1324c64fb016SJohannes Berg 	struct rfkill_data *data = file->private_data;
1325c64fb016SJohannes Berg 	struct rfkill_int_event *ev, *tmp;
1326c64fb016SJohannes Berg 
1327c64fb016SJohannes Berg 	mutex_lock(&rfkill_global_mutex);
1328c64fb016SJohannes Berg 	list_del(&data->list);
1329c64fb016SJohannes Berg 	mutex_unlock(&rfkill_global_mutex);
1330c64fb016SJohannes Berg 
1331c64fb016SJohannes Berg 	mutex_destroy(&data->mtx);
1332c64fb016SJohannes Berg 	list_for_each_entry_safe(ev, tmp, &data->events, list)
1333c64fb016SJohannes Berg 		kfree(ev);
1334c64fb016SJohannes Berg 
1335c64fb016SJohannes Berg #ifdef CONFIG_RFKILL_INPUT
1336c64fb016SJohannes Berg 	if (data->input_handler)
1337207ee162SJohannes Berg 		if (atomic_dec_return(&rfkill_input_disabled) == 0)
1338207ee162SJohannes Berg 			printk(KERN_DEBUG "rfkill: input handler enabled\n");
1339c64fb016SJohannes Berg #endif
1340c64fb016SJohannes Berg 
1341c64fb016SJohannes Berg 	kfree(data);
1342c64fb016SJohannes Berg 
1343c64fb016SJohannes Berg 	return 0;
1344c64fb016SJohannes Berg }
1345c64fb016SJohannes Berg 
rfkill_fop_ioctl(struct file * file,unsigned int cmd,unsigned long arg)1346c64fb016SJohannes Berg static long rfkill_fop_ioctl(struct file *file, unsigned int cmd,
1347c64fb016SJohannes Berg 			     unsigned long arg)
1348c64fb016SJohannes Berg {
1349c64fb016SJohannes Berg 	struct rfkill_data *data = file->private_data;
1350f52c8fbaSThomas Weißschuh 	int ret = -ENOTTY;
135154f586a9SJohannes Berg 	u32 size;
1352c64fb016SJohannes Berg 
1353c64fb016SJohannes Berg 	if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC)
1354f52c8fbaSThomas Weißschuh 		return -ENOTTY;
1355c64fb016SJohannes Berg 
1356c64fb016SJohannes Berg 	mutex_lock(&data->mtx);
135754f586a9SJohannes Berg 	switch (_IOC_NR(cmd)) {
135854f586a9SJohannes Berg #ifdef CONFIG_RFKILL_INPUT
135954f586a9SJohannes Berg 	case RFKILL_IOC_NOINPUT:
1360c64fb016SJohannes Berg 		if (!data->input_handler) {
1361207ee162SJohannes Berg 			if (atomic_inc_return(&rfkill_input_disabled) == 1)
1362207ee162SJohannes Berg 				printk(KERN_DEBUG "rfkill: input handler disabled\n");
1363c64fb016SJohannes Berg 			data->input_handler = true;
1364c64fb016SJohannes Berg 		}
136554f586a9SJohannes Berg 		ret = 0;
136654f586a9SJohannes Berg 		break;
136754f586a9SJohannes Berg #endif
136854f586a9SJohannes Berg 	case RFKILL_IOC_MAX_SIZE:
136954f586a9SJohannes Berg 		if (get_user(size, (__u32 __user *)arg)) {
137054f586a9SJohannes Berg 			ret = -EFAULT;
137154f586a9SJohannes Berg 			break;
137254f586a9SJohannes Berg 		}
137354f586a9SJohannes Berg 		if (size < RFKILL_EVENT_SIZE_V1 || size > U8_MAX) {
137454f586a9SJohannes Berg 			ret = -EINVAL;
137554f586a9SJohannes Berg 			break;
137654f586a9SJohannes Berg 		}
137754f586a9SJohannes Berg 		data->max_size = size;
137854f586a9SJohannes Berg 		ret = 0;
137954f586a9SJohannes Berg 		break;
138054f586a9SJohannes Berg 	default:
138154f586a9SJohannes Berg 		break;
138254f586a9SJohannes Berg 	}
1383c64fb016SJohannes Berg 	mutex_unlock(&data->mtx);
1384c64fb016SJohannes Berg 
138554f586a9SJohannes Berg 	return ret;
1386c64fb016SJohannes Berg }
1387c64fb016SJohannes Berg 
1388c64fb016SJohannes Berg static const struct file_operations rfkill_fops = {
138945ba564dSJohannes Berg 	.owner		= THIS_MODULE,
1390c64fb016SJohannes Berg 	.open		= rfkill_fop_open,
1391c64fb016SJohannes Berg 	.read		= rfkill_fop_read,
1392c64fb016SJohannes Berg 	.write		= rfkill_fop_write,
1393c64fb016SJohannes Berg 	.poll		= rfkill_fop_poll,
1394c64fb016SJohannes Berg 	.release	= rfkill_fop_release,
1395c64fb016SJohannes Berg 	.unlocked_ioctl	= rfkill_fop_ioctl,
13961832f2d8SArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
1397c64fb016SJohannes Berg };
1398c64fb016SJohannes Berg 
13998670b2b8SMarcel Holtmann #define RFKILL_NAME "rfkill"
14008670b2b8SMarcel Holtmann 
1401c64fb016SJohannes Berg static struct miscdevice rfkill_miscdev = {
1402c64fb016SJohannes Berg 	.fops	= &rfkill_fops,
14038670b2b8SMarcel Holtmann 	.name	= RFKILL_NAME,
14048670b2b8SMarcel Holtmann 	.minor	= RFKILL_MINOR,
1405c64fb016SJohannes Berg };
140619d337dfSJohannes Berg 
rfkill_init(void)140719d337dfSJohannes Berg static int __init rfkill_init(void)
140819d337dfSJohannes Berg {
140919d337dfSJohannes Berg 	int error;
141019d337dfSJohannes Berg 
14119487bd6bSJoão Paulo Rechi Vita 	rfkill_update_global_state(RFKILL_TYPE_ALL, !rfkill_default_state);
141219d337dfSJohannes Berg 
141319d337dfSJohannes Berg 	error = class_register(&rfkill_class);
141419d337dfSJohannes Berg 	if (error)
14156124c53eSMichał Kępień 		goto error_class;
141619d337dfSJohannes Berg 
1417c64fb016SJohannes Berg 	error = misc_register(&rfkill_miscdev);
14186124c53eSMichał Kępień 	if (error)
14196124c53eSMichał Kępień 		goto error_misc;
1420c64fb016SJohannes Berg 
1421d874cd74SJoão Paulo Rechi Vita 	error = rfkill_global_led_trigger_register();
14229b8e34e2SMichał Kępień 	if (error)
14239b8e34e2SMichał Kępień 		goto error_led_trigger;
14249b8e34e2SMichał Kępień 
142519d337dfSJohannes Berg #ifdef CONFIG_RFKILL_INPUT
142619d337dfSJohannes Berg 	error = rfkill_handler_init();
14276124c53eSMichał Kępień 	if (error)
14286124c53eSMichał Kępień 		goto error_input;
142919d337dfSJohannes Berg #endif
143019d337dfSJohannes Berg 
14316124c53eSMichał Kępień 	return 0;
14326124c53eSMichał Kępień 
1433f6b4122cSArnd Bergmann #ifdef CONFIG_RFKILL_INPUT
14346124c53eSMichał Kępień error_input:
1435d874cd74SJoão Paulo Rechi Vita 	rfkill_global_led_trigger_unregister();
14367b854982SJohannes Berg #endif
14379b8e34e2SMichał Kępień error_led_trigger:
14389b8e34e2SMichał Kępień 	misc_deregister(&rfkill_miscdev);
14396124c53eSMichał Kępień error_misc:
14406124c53eSMichał Kępień 	class_unregister(&rfkill_class);
14416124c53eSMichał Kępień error_class:
144219d337dfSJohannes Berg 	return error;
144319d337dfSJohannes Berg }
144419d337dfSJohannes Berg subsys_initcall(rfkill_init);
144519d337dfSJohannes Berg 
rfkill_exit(void)144619d337dfSJohannes Berg static void __exit rfkill_exit(void)
144719d337dfSJohannes Berg {
144819d337dfSJohannes Berg #ifdef CONFIG_RFKILL_INPUT
144919d337dfSJohannes Berg 	rfkill_handler_exit();
145019d337dfSJohannes Berg #endif
1451d874cd74SJoão Paulo Rechi Vita 	rfkill_global_led_trigger_unregister();
1452c64fb016SJohannes Berg 	misc_deregister(&rfkill_miscdev);
145319d337dfSJohannes Berg 	class_unregister(&rfkill_class);
145419d337dfSJohannes Berg }
145519d337dfSJohannes Berg module_exit(rfkill_exit);
14568670b2b8SMarcel Holtmann 
14578670b2b8SMarcel Holtmann MODULE_ALIAS_MISCDEV(RFKILL_MINOR);
14588670b2b8SMarcel Holtmann MODULE_ALIAS("devname:" RFKILL_NAME);
1459