xref: /openbmc/linux/net/rfkill/core.c (revision ce55c22ec8b223a90ff3e084d842f73cfba35588)
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