Blame view

net/rfkill/core.c 31 KB
19d337dff   Johannes Berg   rfkill: rewrite
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * Copyright (C) 2006 - 2007 Ivo van Doorn
   * Copyright (C) 2007 Dmitry Torokhov
   * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
8232f1f3d   Jeff Kirsher   rfkill: Fix FSF a...
17
   * along with this program; if not, see <http://www.gnu.org/licenses/>.
19d337dff   Johannes Berg   rfkill: rewrite
18
19
20
21
22
23
24
25
26
27
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/workqueue.h>
  #include <linux/capability.h>
  #include <linux/list.h>
  #include <linux/mutex.h>
  #include <linux/rfkill.h>
a99bbaf5e   Alexey Dobriyan   headers: remove s...
28
  #include <linux/sched.h>
19d337dff   Johannes Berg   rfkill: rewrite
29
  #include <linux/spinlock.h>
51990e825   Paul Gortmaker   device.h: cleanup...
30
  #include <linux/device.h>
c64fb0162   Johannes Berg   rfkill: create us...
31
32
33
34
  #include <linux/miscdevice.h>
  #include <linux/wait.h>
  #include <linux/poll.h>
  #include <linux/fs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
35
  #include <linux/slab.h>
19d337dff   Johannes Berg   rfkill: rewrite
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  
  #include "rfkill.h"
  
  #define POLL_INTERVAL		(5 * HZ)
  
  #define RFKILL_BLOCK_HW		BIT(0)
  #define RFKILL_BLOCK_SW		BIT(1)
  #define RFKILL_BLOCK_SW_PREV	BIT(2)
  #define RFKILL_BLOCK_ANY	(RFKILL_BLOCK_HW |\
  				 RFKILL_BLOCK_SW |\
  				 RFKILL_BLOCK_SW_PREV)
  #define RFKILL_BLOCK_SW_SETCALL	BIT(31)
  
  struct rfkill {
  	spinlock_t		lock;
19d337dff   Johannes Berg   rfkill: rewrite
51
52
53
  	enum rfkill_type	type;
  
  	unsigned long		state;
c64fb0162   Johannes Berg   rfkill: create us...
54
  	u32			idx;
19d337dff   Johannes Berg   rfkill: rewrite
55
  	bool			registered;
b3fa1329e   Alan Jenkins   rfkill: remove se...
56
  	bool			persistent;
dd21dfc64   Johannes Berg   rfkill: disentang...
57
58
  	bool			polling_paused;
  	bool			suspended;
19d337dff   Johannes Berg   rfkill: rewrite
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  
  	const struct rfkill_ops	*ops;
  	void			*data;
  
  #ifdef CONFIG_RFKILL_LEDS
  	struct led_trigger	led_trigger;
  	const char		*ledtrigname;
  #endif
  
  	struct device		dev;
  	struct list_head	node;
  
  	struct delayed_work	poll_work;
  	struct work_struct	uevent_work;
  	struct work_struct	sync_work;
b7bb11000   Johannes Berg   rfkill: copy the ...
74
  	char			name[];
19d337dff   Johannes Berg   rfkill: rewrite
75
76
  };
  #define to_rfkill(d)	container_of(d, struct rfkill, dev)
c64fb0162   Johannes Berg   rfkill: create us...
77
78
79
80
81
82
83
84
85
86
87
88
  struct rfkill_int_event {
  	struct list_head	list;
  	struct rfkill_event	ev;
  };
  
  struct rfkill_data {
  	struct list_head	list;
  	struct list_head	events;
  	struct mutex		mtx;
  	wait_queue_head_t	read_wait;
  	bool			input_handler;
  };
19d337dff   Johannes Berg   rfkill: rewrite
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  
  
  MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
  MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
  MODULE_DESCRIPTION("RF switch support");
  MODULE_LICENSE("GPL");
  
  
  /*
   * The locking here should be made much smarter, we currently have
   * a bit of a stupid situation because drivers might want to register
   * the rfkill struct under their own lock, and take this lock during
   * rfkill method calls -- which will cause an AB-BA deadlock situation.
   *
   * To fix that, we need to rework this code here to be mostly lock-free
   * and only use the mutex for list manipulations, not to protect the
   * various other global variables. Then we can avoid holding the mutex
   * around driver operations, and all is happy.
   */
  static LIST_HEAD(rfkill_list);	/* list of registered rf switches */
  static DEFINE_MUTEX(rfkill_global_mutex);
c64fb0162   Johannes Berg   rfkill: create us...
110
  static LIST_HEAD(rfkill_fds);	/* list of open fds of /dev/rfkill */
19d337dff   Johannes Berg   rfkill: rewrite
111
112
113
114
115
116
117
  
  static unsigned int rfkill_default_state = 1;
  module_param_named(default_state, rfkill_default_state, uint, 0444);
  MODULE_PARM_DESC(default_state,
  		 "Default initial state for all radio types, 0 = radio off");
  
  static struct {
b3fa1329e   Alan Jenkins   rfkill: remove se...
118
  	bool cur, sav;
19d337dff   Johannes Berg   rfkill: rewrite
119
  } rfkill_global_states[NUM_RFKILL_TYPES];
19d337dff   Johannes Berg   rfkill: rewrite
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  static bool rfkill_epo_lock_active;
  
  
  #ifdef CONFIG_RFKILL_LEDS
  static void rfkill_led_trigger_event(struct rfkill *rfkill)
  {
  	struct led_trigger *trigger;
  
  	if (!rfkill->registered)
  		return;
  
  	trigger = &rfkill->led_trigger;
  
  	if (rfkill->state & RFKILL_BLOCK_ANY)
  		led_trigger_event(trigger, LED_OFF);
  	else
  		led_trigger_event(trigger, LED_FULL);
  }
  
  static void rfkill_led_trigger_activate(struct led_classdev *led)
  {
  	struct rfkill *rfkill;
  
  	rfkill = container_of(led->trigger, struct rfkill, led_trigger);
  
  	rfkill_led_trigger_event(rfkill);
  }
06d7de831   AceLan Kao   Revert "rfkill: r...
147
148
149
150
151
152
153
154
155
156
157
158
159
  const char *rfkill_get_led_trigger_name(struct rfkill *rfkill)
  {
  	return rfkill->led_trigger.name;
  }
  EXPORT_SYMBOL(rfkill_get_led_trigger_name);
  
  void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
  {
  	BUG_ON(!rfkill);
  
  	rfkill->ledtrigname = name;
  }
  EXPORT_SYMBOL(rfkill_set_led_trigger_name);
19d337dff   Johannes Berg   rfkill: rewrite
160
161
162
163
164
165
166
167
168
169
170
171
  static int rfkill_led_trigger_register(struct rfkill *rfkill)
  {
  	rfkill->led_trigger.name = rfkill->ledtrigname
  					? : dev_name(&rfkill->dev);
  	rfkill->led_trigger.activate = rfkill_led_trigger_activate;
  	return led_trigger_register(&rfkill->led_trigger);
  }
  
  static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
  {
  	led_trigger_unregister(&rfkill->led_trigger);
  }
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  
  static struct led_trigger rfkill_any_led_trigger;
  static struct work_struct rfkill_any_work;
  
  static void rfkill_any_led_trigger_worker(struct work_struct *work)
  {
  	enum led_brightness brightness = LED_OFF;
  	struct rfkill *rfkill;
  
  	mutex_lock(&rfkill_global_mutex);
  	list_for_each_entry(rfkill, &rfkill_list, node) {
  		if (!(rfkill->state & RFKILL_BLOCK_ANY)) {
  			brightness = LED_FULL;
  			break;
  		}
  	}
  	mutex_unlock(&rfkill_global_mutex);
  
  	led_trigger_event(&rfkill_any_led_trigger, brightness);
  }
  
  static void rfkill_any_led_trigger_event(void)
  {
  	schedule_work(&rfkill_any_work);
  }
  
  static void rfkill_any_led_trigger_activate(struct led_classdev *led_cdev)
  {
  	rfkill_any_led_trigger_event();
  }
  
  static int rfkill_any_led_trigger_register(void)
  {
  	INIT_WORK(&rfkill_any_work, rfkill_any_led_trigger_worker);
  	rfkill_any_led_trigger.name = "rfkill-any";
  	rfkill_any_led_trigger.activate = rfkill_any_led_trigger_activate;
  	return led_trigger_register(&rfkill_any_led_trigger);
  }
  
  static void rfkill_any_led_trigger_unregister(void)
  {
  	led_trigger_unregister(&rfkill_any_led_trigger);
  	cancel_work_sync(&rfkill_any_work);
  }
19d337dff   Johannes Berg   rfkill: rewrite
216
217
218
219
220
221
222
223
224
225
226
227
228
  #else
  static void rfkill_led_trigger_event(struct rfkill *rfkill)
  {
  }
  
  static inline int rfkill_led_trigger_register(struct rfkill *rfkill)
  {
  	return 0;
  }
  
  static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
  {
  }
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
229
230
231
232
233
234
235
236
237
238
239
240
241
  
  static void rfkill_any_led_trigger_event(void)
  {
  }
  
  static int rfkill_any_led_trigger_register(void)
  {
  	return 0;
  }
  
  static void rfkill_any_led_trigger_unregister(void)
  {
  }
19d337dff   Johannes Berg   rfkill: rewrite
242
  #endif /* CONFIG_RFKILL_LEDS */
c64fb0162   Johannes Berg   rfkill: create us...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill,
  			      enum rfkill_operation op)
  {
  	unsigned long flags;
  
  	ev->idx = rfkill->idx;
  	ev->type = rfkill->type;
  	ev->op = op;
  
  	spin_lock_irqsave(&rfkill->lock, flags);
  	ev->hard = !!(rfkill->state & RFKILL_BLOCK_HW);
  	ev->soft = !!(rfkill->state & (RFKILL_BLOCK_SW |
  					RFKILL_BLOCK_SW_PREV));
  	spin_unlock_irqrestore(&rfkill->lock, flags);
  }
  
  static void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op)
  {
  	struct rfkill_data *data;
  	struct rfkill_int_event *ev;
  
  	list_for_each_entry(data, &rfkill_fds, list) {
  		ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  		if (!ev)
  			continue;
  		rfkill_fill_event(&ev->ev, rfkill, op);
  		mutex_lock(&data->mtx);
  		list_add_tail(&ev->list, &data->events);
  		mutex_unlock(&data->mtx);
  		wake_up_interruptible(&data->read_wait);
  	}
  }
  
  static void rfkill_event(struct rfkill *rfkill)
19d337dff   Johannes Berg   rfkill: rewrite
277
  {
06d5caf47   Alan Jenkins   rfkill: don't res...
278
  	if (!rfkill->registered)
19d337dff   Johannes Berg   rfkill: rewrite
279
280
281
  		return;
  
  	kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
c64fb0162   Johannes Berg   rfkill: create us...
282
283
284
  
  	/* also send event to /dev/rfkill */
  	rfkill_send_events(rfkill, RFKILL_OP_CHANGE);
19d337dff   Johannes Berg   rfkill: rewrite
285
  }
19d337dff   Johannes Berg   rfkill: rewrite
286
287
288
289
290
291
292
293
294
295
296
297
  /**
   * rfkill_set_block - wrapper for set_block method
   *
   * @rfkill: the rfkill struct to use
   * @blocked: the new software state
   *
   * Calls the set_block method (when applicable) and handles notifications
   * etc. as well.
   */
  static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
  {
  	unsigned long flags;
eab48345c   Vitaly Wool   rfkill: prevent u...
298
  	bool prev, curr;
19d337dff   Johannes Berg   rfkill: rewrite
299
  	int err;
7fa20a7f6   Alan Jenkins   rfkill: rfkill_se...
300
301
  	if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
  		return;
19d337dff   Johannes Berg   rfkill: rewrite
302
303
304
305
306
307
308
309
310
  	/*
  	 * Some platforms (...!) generate input events which affect the
  	 * _hard_ kill state -- whenever something tries to change the
  	 * current software state query the hardware state too.
  	 */
  	if (rfkill->ops->query)
  		rfkill->ops->query(rfkill, rfkill->data);
  
  	spin_lock_irqsave(&rfkill->lock, flags);
eab48345c   Vitaly Wool   rfkill: prevent u...
311
  	prev = rfkill->state & RFKILL_BLOCK_SW;
f3e7fae24   João Paulo Rechi Vita   rfkill: use varia...
312
  	if (prev)
19d337dff   Johannes Berg   rfkill: rewrite
313
314
315
316
317
318
319
320
321
322
323
  		rfkill->state |= RFKILL_BLOCK_SW_PREV;
  	else
  		rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
  
  	if (blocked)
  		rfkill->state |= RFKILL_BLOCK_SW;
  	else
  		rfkill->state &= ~RFKILL_BLOCK_SW;
  
  	rfkill->state |= RFKILL_BLOCK_SW_SETCALL;
  	spin_unlock_irqrestore(&rfkill->lock, flags);
19d337dff   Johannes Berg   rfkill: rewrite
324
325
326
327
328
  	err = rfkill->ops->set_block(rfkill->data, blocked);
  
  	spin_lock_irqsave(&rfkill->lock, flags);
  	if (err) {
  		/*
3ff707d66   João Paulo Rechi Vita   rfkill: Improve d...
329
330
  		 * Failed -- reset status to _PREV, which may be different
  		 * from what we have set _PREV to earlier in this function
19d337dff   Johannes Berg   rfkill: rewrite
331
332
333
334
335
336
337
338
339
  		 * if rfkill_set_sw_state was invoked.
  		 */
  		if (rfkill->state & RFKILL_BLOCK_SW_PREV)
  			rfkill->state |= RFKILL_BLOCK_SW;
  		else
  			rfkill->state &= ~RFKILL_BLOCK_SW;
  	}
  	rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
  	rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
eab48345c   Vitaly Wool   rfkill: prevent u...
340
  	curr = rfkill->state & RFKILL_BLOCK_SW;
19d337dff   Johannes Berg   rfkill: rewrite
341
342
343
  	spin_unlock_irqrestore(&rfkill->lock, flags);
  
  	rfkill_led_trigger_event(rfkill);
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
344
  	rfkill_any_led_trigger_event();
eab48345c   Vitaly Wool   rfkill: prevent u...
345
346
347
  
  	if (prev != curr)
  		rfkill_event(rfkill);
19d337dff   Johannes Berg   rfkill: rewrite
348
  }
9487bd6b9   João Paulo Rechi Vita   rfkill: Factor rf...
349
350
351
352
353
354
355
356
357
358
359
360
  static void rfkill_update_global_state(enum rfkill_type type, bool blocked)
  {
  	int i;
  
  	if (type != RFKILL_TYPE_ALL) {
  		rfkill_global_states[type].cur = blocked;
  		return;
  	}
  
  	for (i = 0; i < NUM_RFKILL_TYPES; i++)
  		rfkill_global_states[i].cur = blocked;
  }
c64fb0162   Johannes Berg   rfkill: create us...
361
362
  #ifdef CONFIG_RFKILL_INPUT
  static atomic_t rfkill_input_disabled = ATOMIC_INIT(0);
19d337dff   Johannes Berg   rfkill: rewrite
363
364
365
  /**
   * __rfkill_switch_all - Toggle state of all switches of given type
   * @type: type of interfaces to be affected
2f29fed3f   Fabian Frederick   net: rfkill: kern...
366
   * @blocked: the new state
19d337dff   Johannes Berg   rfkill: rewrite
367
368
   *
   * This function sets the state of all switches of given type,
e2a35e892   João Paulo Rechi Vita   rfkill: Remove ob...
369
   * unless a specific switch is suspended.
19d337dff   Johannes Berg   rfkill: rewrite
370
371
372
373
374
375
   *
   * Caller must have acquired rfkill_global_mutex.
   */
  static void __rfkill_switch_all(const enum rfkill_type type, bool blocked)
  {
  	struct rfkill *rfkill;
9487bd6b9   João Paulo Rechi Vita   rfkill: Factor rf...
376
  	rfkill_update_global_state(type, blocked);
19d337dff   Johannes Berg   rfkill: rewrite
377
  	list_for_each_entry(rfkill, &rfkill_list, node) {
27e49ca95   Alex Hung   rfkill: Add the c...
378
  		if (rfkill->type != type && type != RFKILL_TYPE_ALL)
19d337dff   Johannes Berg   rfkill: rewrite
379
380
381
382
383
384
385
386
387
  			continue;
  
  		rfkill_set_block(rfkill, blocked);
  	}
  }
  
  /**
   * rfkill_switch_all - Toggle state of all switches of given type
   * @type: type of interfaces to be affected
2f29fed3f   Fabian Frederick   net: rfkill: kern...
388
   * @blocked: the new state
19d337dff   Johannes Berg   rfkill: rewrite
389
390
391
392
393
394
395
396
   *
   * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
   * Please refer to __rfkill_switch_all() for details.
   *
   * Does nothing if the EPO lock is active.
   */
  void rfkill_switch_all(enum rfkill_type type, bool blocked)
  {
c64fb0162   Johannes Berg   rfkill: create us...
397
398
  	if (atomic_read(&rfkill_input_disabled))
  		return;
19d337dff   Johannes Berg   rfkill: rewrite
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	mutex_lock(&rfkill_global_mutex);
  
  	if (!rfkill_epo_lock_active)
  		__rfkill_switch_all(type, blocked);
  
  	mutex_unlock(&rfkill_global_mutex);
  }
  
  /**
   * rfkill_epo - emergency power off all transmitters
   *
   * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED,
   * ignoring everything in its path but rfkill_global_mutex and rfkill->mutex.
   *
   * The global state before the EPO is saved and can be restored later
   * using rfkill_restore_states().
   */
  void rfkill_epo(void)
  {
  	struct rfkill *rfkill;
  	int i;
c64fb0162   Johannes Berg   rfkill: create us...
420
421
  	if (atomic_read(&rfkill_input_disabled))
  		return;
19d337dff   Johannes Berg   rfkill: rewrite
422
423
424
425
426
427
428
  	mutex_lock(&rfkill_global_mutex);
  
  	rfkill_epo_lock_active = true;
  	list_for_each_entry(rfkill, &rfkill_list, node)
  		rfkill_set_block(rfkill, true);
  
  	for (i = 0; i < NUM_RFKILL_TYPES; i++) {
b3fa1329e   Alan Jenkins   rfkill: remove se...
429
  		rfkill_global_states[i].sav = rfkill_global_states[i].cur;
19d337dff   Johannes Berg   rfkill: rewrite
430
431
  		rfkill_global_states[i].cur = true;
  	}
c64fb0162   Johannes Berg   rfkill: create us...
432

19d337dff   Johannes Berg   rfkill: rewrite
433
434
435
436
437
438
439
440
441
442
443
444
445
  	mutex_unlock(&rfkill_global_mutex);
  }
  
  /**
   * rfkill_restore_states - restore global states
   *
   * Restore (and sync switches to) the global state from the
   * states in rfkill_default_states.  This can undo the effects of
   * a call to rfkill_epo().
   */
  void rfkill_restore_states(void)
  {
  	int i;
c64fb0162   Johannes Berg   rfkill: create us...
446
447
  	if (atomic_read(&rfkill_input_disabled))
  		return;
19d337dff   Johannes Berg   rfkill: rewrite
448
449
450
451
  	mutex_lock(&rfkill_global_mutex);
  
  	rfkill_epo_lock_active = false;
  	for (i = 0; i < NUM_RFKILL_TYPES; i++)
b3fa1329e   Alan Jenkins   rfkill: remove se...
452
  		__rfkill_switch_all(i, rfkill_global_states[i].sav);
19d337dff   Johannes Berg   rfkill: rewrite
453
454
455
456
457
458
459
460
461
462
463
  	mutex_unlock(&rfkill_global_mutex);
  }
  
  /**
   * rfkill_remove_epo_lock - unlock state changes
   *
   * Used by rfkill-input manually unlock state changes, when
   * the EPO switch is deactivated.
   */
  void rfkill_remove_epo_lock(void)
  {
c64fb0162   Johannes Berg   rfkill: create us...
464
465
  	if (atomic_read(&rfkill_input_disabled))
  		return;
19d337dff   Johannes Berg   rfkill: rewrite
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
  	mutex_lock(&rfkill_global_mutex);
  	rfkill_epo_lock_active = false;
  	mutex_unlock(&rfkill_global_mutex);
  }
  
  /**
   * rfkill_is_epo_lock_active - returns true EPO is active
   *
   * Returns 0 (false) if there is NOT an active EPO contidion,
   * and 1 (true) if there is an active EPO contition, which
   * locks all radios in one of the BLOCKED states.
   *
   * Can be called in atomic context.
   */
  bool rfkill_is_epo_lock_active(void)
  {
  	return rfkill_epo_lock_active;
  }
  
  /**
   * rfkill_get_global_sw_state - returns global state for a type
   * @type: the type to get the global state of
   *
   * Returns the current global state for a given wireless
   * device type.
   */
  bool rfkill_get_global_sw_state(const enum rfkill_type type)
  {
  	return rfkill_global_states[type].cur;
  }
c64fb0162   Johannes Berg   rfkill: create us...
496
  #endif
19d337dff   Johannes Berg   rfkill: rewrite
497

19d337dff   Johannes Berg   rfkill: rewrite
498
499
  bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
  {
1926e260d   João Paulo Rechi Vita   rfkill: remove/in...
500
501
502
503
  	unsigned long flags;
  	bool ret, prev;
  
  	BUG_ON(!rfkill);
19d337dff   Johannes Berg   rfkill: rewrite
504

1926e260d   João Paulo Rechi Vita   rfkill: remove/in...
505
506
507
508
509
510
511
512
  	spin_lock_irqsave(&rfkill->lock, flags);
  	prev = !!(rfkill->state & RFKILL_BLOCK_HW);
  	if (blocked)
  		rfkill->state |= RFKILL_BLOCK_HW;
  	else
  		rfkill->state &= ~RFKILL_BLOCK_HW;
  	ret = !!(rfkill->state & RFKILL_BLOCK_ANY);
  	spin_unlock_irqrestore(&rfkill->lock, flags);
19d337dff   Johannes Berg   rfkill: rewrite
513

1926e260d   João Paulo Rechi Vita   rfkill: remove/in...
514
  	rfkill_led_trigger_event(rfkill);
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
515
  	rfkill_any_led_trigger_event();
19d337dff   Johannes Berg   rfkill: rewrite
516

74204f8fa   Johannes Berg   rfkill: simplify ...
517
  	if (rfkill->registered && prev != blocked)
19d337dff   Johannes Berg   rfkill: rewrite
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  		schedule_work(&rfkill->uevent_work);
  
  	return ret;
  }
  EXPORT_SYMBOL(rfkill_set_hw_state);
  
  static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
  {
  	u32 bit = RFKILL_BLOCK_SW;
  
  	/* if in a ops->set_block right now, use other bit */
  	if (rfkill->state & RFKILL_BLOCK_SW_SETCALL)
  		bit = RFKILL_BLOCK_SW_PREV;
  
  	if (blocked)
  		rfkill->state |= bit;
  	else
  		rfkill->state &= ~bit;
  }
  
  bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
  {
  	unsigned long flags;
  	bool prev, hwblock;
  
  	BUG_ON(!rfkill);
  
  	spin_lock_irqsave(&rfkill->lock, flags);
  	prev = !!(rfkill->state & RFKILL_BLOCK_SW);
  	__rfkill_set_sw_state(rfkill, blocked);
  	hwblock = !!(rfkill->state & RFKILL_BLOCK_HW);
  	blocked = blocked || hwblock;
  	spin_unlock_irqrestore(&rfkill->lock, flags);
06d5caf47   Alan Jenkins   rfkill: don't res...
551
552
  	if (!rfkill->registered)
  		return blocked;
19d337dff   Johannes Berg   rfkill: rewrite
553

06d5caf47   Alan Jenkins   rfkill: don't res...
554
555
556
557
  	if (prev != blocked && !hwblock)
  		schedule_work(&rfkill->uevent_work);
  
  	rfkill_led_trigger_event(rfkill);
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
558
  	rfkill_any_led_trigger_event();
19d337dff   Johannes Berg   rfkill: rewrite
559
560
561
562
  
  	return blocked;
  }
  EXPORT_SYMBOL(rfkill_set_sw_state);
06d5caf47   Alan Jenkins   rfkill: don't res...
563
564
565
566
567
568
569
570
571
572
573
574
575
  void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked)
  {
  	unsigned long flags;
  
  	BUG_ON(!rfkill);
  	BUG_ON(rfkill->registered);
  
  	spin_lock_irqsave(&rfkill->lock, flags);
  	__rfkill_set_sw_state(rfkill, blocked);
  	rfkill->persistent = true;
  	spin_unlock_irqrestore(&rfkill->lock, flags);
  }
  EXPORT_SYMBOL(rfkill_init_sw_state);
19d337dff   Johannes Berg   rfkill: rewrite
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
  void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
  {
  	unsigned long flags;
  	bool swprev, hwprev;
  
  	BUG_ON(!rfkill);
  
  	spin_lock_irqsave(&rfkill->lock, flags);
  
  	/*
  	 * No need to care about prev/setblock ... this is for uevent only
  	 * and that will get triggered by rfkill_set_block anyway.
  	 */
  	swprev = !!(rfkill->state & RFKILL_BLOCK_SW);
  	hwprev = !!(rfkill->state & RFKILL_BLOCK_HW);
  	__rfkill_set_sw_state(rfkill, sw);
48ab3578a   Alan Jenkins   rfkill: fix rfkil...
592
593
594
595
  	if (hw)
  		rfkill->state |= RFKILL_BLOCK_HW;
  	else
  		rfkill->state &= ~RFKILL_BLOCK_HW;
19d337dff   Johannes Berg   rfkill: rewrite
596
597
  
  	spin_unlock_irqrestore(&rfkill->lock, flags);
b3fa1329e   Alan Jenkins   rfkill: remove se...
598
599
600
601
602
  	if (!rfkill->registered) {
  		rfkill->persistent = true;
  	} else {
  		if (swprev != sw || hwprev != hw)
  			schedule_work(&rfkill->uevent_work);
19d337dff   Johannes Berg   rfkill: rewrite
603

b3fa1329e   Alan Jenkins   rfkill: remove se...
604
  		rfkill_led_trigger_event(rfkill);
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
605
  		rfkill_any_led_trigger_event();
b3fa1329e   Alan Jenkins   rfkill: remove se...
606
  	}
19d337dff   Johannes Berg   rfkill: rewrite
607
608
  }
  EXPORT_SYMBOL(rfkill_set_states);
648b50dd6   Heikki Krogerus   net: rfkill: add ...
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  static const char * const rfkill_types[] = {
  	NULL, /* RFKILL_TYPE_ALL */
  	"wlan",
  	"bluetooth",
  	"ultrawideband",
  	"wimax",
  	"wwan",
  	"gps",
  	"fm",
  	"nfc",
  };
  
  enum rfkill_type rfkill_find_type(const char *name)
  {
  	int i;
  
  	BUILD_BUG_ON(ARRAY_SIZE(rfkill_types) != NUM_RFKILL_TYPES);
  
  	if (!name)
  		return RFKILL_TYPE_ALL;
  
  	for (i = 1; i < NUM_RFKILL_TYPES; i++)
  		if (!strcmp(name, rfkill_types[i]))
  			return i;
  	return RFKILL_TYPE_ALL;
  }
  EXPORT_SYMBOL(rfkill_find_type);
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
636
637
  static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  			 char *buf)
19d337dff   Johannes Berg   rfkill: rewrite
638
639
640
641
642
643
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  
  	return sprintf(buf, "%s
  ", rfkill->name);
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
644
  static DEVICE_ATTR_RO(name);
19d337dff   Johannes Berg   rfkill: rewrite
645

e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
646
647
  static ssize_t type_show(struct device *dev, struct device_attribute *attr,
  			 char *buf)
19d337dff   Johannes Berg   rfkill: rewrite
648
649
  {
  	struct rfkill *rfkill = to_rfkill(dev);
648b50dd6   Heikki Krogerus   net: rfkill: add ...
650
651
  	return sprintf(buf, "%s
  ", rfkill_types[rfkill->type]);
19d337dff   Johannes Berg   rfkill: rewrite
652
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
653
  static DEVICE_ATTR_RO(type);
19d337dff   Johannes Berg   rfkill: rewrite
654

e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
655
656
  static ssize_t index_show(struct device *dev, struct device_attribute *attr,
  			  char *buf)
c64fb0162   Johannes Berg   rfkill: create us...
657
658
659
660
661
662
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  
  	return sprintf(buf, "%d
  ", rfkill->idx);
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
663
  static DEVICE_ATTR_RO(index);
c64fb0162   Johannes Berg   rfkill: create us...
664

e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
665
666
  static ssize_t persistent_show(struct device *dev,
  			       struct device_attribute *attr, char *buf)
464902e81   Alan Jenkins   rfkill: export pe...
667
668
669
670
671
672
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  
  	return sprintf(buf, "%d
  ", rfkill->persistent);
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
673
  static DEVICE_ATTR_RO(persistent);
464902e81   Alan Jenkins   rfkill: export pe...
674

e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
675
676
  static ssize_t hard_show(struct device *dev, struct device_attribute *attr,
  			 char *buf)
6c26361e4   florian@mickler.org   enhance sysfs rfk...
677
678
  {
  	struct rfkill *rfkill = to_rfkill(dev);
6c26361e4   florian@mickler.org   enhance sysfs rfk...
679

819bfecc4   florian@mickler.org   rename new rfkill...
680
681
  	return sprintf(buf, "%d
  ", (rfkill->state & RFKILL_BLOCK_HW) ? 1 : 0 );
6c26361e4   florian@mickler.org   enhance sysfs rfk...
682
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
683
  static DEVICE_ATTR_RO(hard);
6c26361e4   florian@mickler.org   enhance sysfs rfk...
684

e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
685
686
  static ssize_t soft_show(struct device *dev, struct device_attribute *attr,
  			 char *buf)
6c26361e4   florian@mickler.org   enhance sysfs rfk...
687
688
  {
  	struct rfkill *rfkill = to_rfkill(dev);
6c26361e4   florian@mickler.org   enhance sysfs rfk...
689

819bfecc4   florian@mickler.org   rename new rfkill...
690
691
  	return sprintf(buf, "%d
  ", (rfkill->state & RFKILL_BLOCK_SW) ? 1 : 0 );
6c26361e4   florian@mickler.org   enhance sysfs rfk...
692
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
693
694
  static ssize_t soft_store(struct device *dev, struct device_attribute *attr,
  			  const char *buf, size_t count)
6c26361e4   florian@mickler.org   enhance sysfs rfk...
695
696
697
698
699
700
701
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  	unsigned long state;
  	int err;
  
  	if (!capable(CAP_NET_ADMIN))
  		return -EPERM;
1bac92cac   Julia Lawall   net/rfkill/core.c...
702
  	err = kstrtoul(buf, 0, &state);
6c26361e4   florian@mickler.org   enhance sysfs rfk...
703
704
705
706
707
708
709
710
711
  	if (err)
  		return err;
  
  	if (state > 1 )
  		return -EINVAL;
  
  	mutex_lock(&rfkill_global_mutex);
  	rfkill_set_block(rfkill, state);
  	mutex_unlock(&rfkill_global_mutex);
6f7c962c0   Alan Cox   rfkill: error can...
712
  	return count;
6c26361e4   florian@mickler.org   enhance sysfs rfk...
713
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
714
  static DEVICE_ATTR_RW(soft);
6c26361e4   florian@mickler.org   enhance sysfs rfk...
715

19d337dff   Johannes Berg   rfkill: rewrite
716
717
718
719
720
721
722
723
724
  static u8 user_state_from_blocked(unsigned long state)
  {
  	if (state & RFKILL_BLOCK_HW)
  		return RFKILL_USER_STATE_HARD_BLOCKED;
  	if (state & RFKILL_BLOCK_SW)
  		return RFKILL_USER_STATE_SOFT_BLOCKED;
  
  	return RFKILL_USER_STATE_UNBLOCKED;
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
725
726
  static ssize_t state_show(struct device *dev, struct device_attribute *attr,
  			  char *buf)
19d337dff   Johannes Berg   rfkill: rewrite
727
728
  {
  	struct rfkill *rfkill = to_rfkill(dev);
19d337dff   Johannes Berg   rfkill: rewrite
729

819bfecc4   florian@mickler.org   rename new rfkill...
730
731
  	return sprintf(buf, "%d
  ", user_state_from_blocked(rfkill->state));
19d337dff   Johannes Berg   rfkill: rewrite
732
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
733
734
  static ssize_t state_store(struct device *dev, struct device_attribute *attr,
  			   const char *buf, size_t count)
19d337dff   Johannes Berg   rfkill: rewrite
735
  {
f54c14272   Johannes Berg   rfkill: allow tog...
736
737
738
739
740
741
  	struct rfkill *rfkill = to_rfkill(dev);
  	unsigned long state;
  	int err;
  
  	if (!capable(CAP_NET_ADMIN))
  		return -EPERM;
1bac92cac   Julia Lawall   net/rfkill/core.c...
742
  	err = kstrtoul(buf, 0, &state);
f54c14272   Johannes Berg   rfkill: allow tog...
743
744
745
746
747
748
749
750
751
752
  	if (err)
  		return err;
  
  	if (state != RFKILL_USER_STATE_SOFT_BLOCKED &&
  	    state != RFKILL_USER_STATE_UNBLOCKED)
  		return -EINVAL;
  
  	mutex_lock(&rfkill_global_mutex);
  	rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED);
  	mutex_unlock(&rfkill_global_mutex);
19d337dff   Johannes Berg   rfkill: rewrite
753

6f7c962c0   Alan Cox   rfkill: error can...
754
  	return count;
19d337dff   Johannes Berg   rfkill: rewrite
755
  }
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
756
  static DEVICE_ATTR_RW(state);
19d337dff   Johannes Berg   rfkill: rewrite
757

e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
758
759
760
761
762
763
  static struct attribute *rfkill_dev_attrs[] = {
  	&dev_attr_name.attr,
  	&dev_attr_type.attr,
  	&dev_attr_index.attr,
  	&dev_attr_persistent.attr,
  	&dev_attr_state.attr,
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
764
765
766
  	&dev_attr_soft.attr,
  	&dev_attr_hard.attr,
  	NULL,
19d337dff   Johannes Berg   rfkill: rewrite
767
  };
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
768
  ATTRIBUTE_GROUPS(rfkill_dev);
19d337dff   Johannes Berg   rfkill: rewrite
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
  
  static void rfkill_release(struct device *dev)
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  
  	kfree(rfkill);
  }
  
  static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  	unsigned long flags;
  	u32 state;
  	int error;
  
  	error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
  	if (error)
  		return error;
  	error = add_uevent_var(env, "RFKILL_TYPE=%s",
648b50dd6   Heikki Krogerus   net: rfkill: add ...
788
  			       rfkill_types[rfkill->type]);
19d337dff   Johannes Berg   rfkill: rewrite
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
  	if (error)
  		return error;
  	spin_lock_irqsave(&rfkill->lock, flags);
  	state = rfkill->state;
  	spin_unlock_irqrestore(&rfkill->lock, flags);
  	error = add_uevent_var(env, "RFKILL_STATE=%d",
  			       user_state_from_blocked(state));
  	return error;
  }
  
  void rfkill_pause_polling(struct rfkill *rfkill)
  {
  	BUG_ON(!rfkill);
  
  	if (!rfkill->ops->poll)
  		return;
dd21dfc64   Johannes Berg   rfkill: disentang...
805
  	rfkill->polling_paused = true;
19d337dff   Johannes Berg   rfkill: rewrite
806
807
808
809
810
811
812
813
814
815
  	cancel_delayed_work_sync(&rfkill->poll_work);
  }
  EXPORT_SYMBOL(rfkill_pause_polling);
  
  void rfkill_resume_polling(struct rfkill *rfkill)
  {
  	BUG_ON(!rfkill);
  
  	if (!rfkill->ops->poll)
  		return;
dd21dfc64   Johannes Berg   rfkill: disentang...
816
817
818
819
  	rfkill->polling_paused = false;
  
  	if (rfkill->suspended)
  		return;
67235cbca   Shaibal Dutta   net: rfkill: move...
820
821
  	queue_delayed_work(system_power_efficient_wq,
  			   &rfkill->poll_work, 0);
19d337dff   Johannes Berg   rfkill: rewrite
822
823
  }
  EXPORT_SYMBOL(rfkill_resume_polling);
28f297a7a   Lars-Peter Clausen   net: rfkill: Swit...
824
825
  #ifdef CONFIG_PM_SLEEP
  static int rfkill_suspend(struct device *dev)
19d337dff   Johannes Berg   rfkill: rewrite
826
827
  {
  	struct rfkill *rfkill = to_rfkill(dev);
dd21dfc64   Johannes Berg   rfkill: disentang...
828
829
  	rfkill->suspended = true;
  	cancel_delayed_work_sync(&rfkill->poll_work);
19d337dff   Johannes Berg   rfkill: rewrite
830

19d337dff   Johannes Berg   rfkill: rewrite
831
832
833
834
835
836
837
  	return 0;
  }
  
  static int rfkill_resume(struct device *dev)
  {
  	struct rfkill *rfkill = to_rfkill(dev);
  	bool cur;
dd21dfc64   Johannes Berg   rfkill: disentang...
838
  	rfkill->suspended = false;
06d5caf47   Alan Jenkins   rfkill: don't res...
839
840
841
842
  	if (!rfkill->persistent) {
  		cur = !!(rfkill->state & RFKILL_BLOCK_SW);
  		rfkill_set_block(rfkill, cur);
  	}
19d337dff   Johannes Berg   rfkill: rewrite
843

dd21dfc64   Johannes Berg   rfkill: disentang...
844
845
846
  	if (rfkill->ops->poll && !rfkill->polling_paused)
  		queue_delayed_work(system_power_efficient_wq,
  				   &rfkill->poll_work, 0);
19d337dff   Johannes Berg   rfkill: rewrite
847
848
849
  
  	return 0;
  }
28f297a7a   Lars-Peter Clausen   net: rfkill: Swit...
850
851
852
853
854
  static SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume);
  #define RFKILL_PM_OPS (&rfkill_pm_ops)
  #else
  #define RFKILL_PM_OPS NULL
  #endif
19d337dff   Johannes Berg   rfkill: rewrite
855
856
857
  static struct class rfkill_class = {
  	.name		= "rfkill",
  	.dev_release	= rfkill_release,
e49df67df   Greg Kroah-Hartman   net: rfkill: conv...
858
  	.dev_groups	= rfkill_dev_groups,
19d337dff   Johannes Berg   rfkill: rewrite
859
  	.dev_uevent	= rfkill_dev_uevent,
28f297a7a   Lars-Peter Clausen   net: rfkill: Swit...
860
  	.pm		= RFKILL_PM_OPS,
19d337dff   Johannes Berg   rfkill: rewrite
861
  };
6081162e2   Johannes Berg   rfkill: add funct...
862
863
864
865
866
867
868
869
870
871
872
873
  bool rfkill_blocked(struct rfkill *rfkill)
  {
  	unsigned long flags;
  	u32 state;
  
  	spin_lock_irqsave(&rfkill->lock, flags);
  	state = rfkill->state;
  	spin_unlock_irqrestore(&rfkill->lock, flags);
  
  	return !!(state & RFKILL_BLOCK_ANY);
  }
  EXPORT_SYMBOL(rfkill_blocked);
19d337dff   Johannes Berg   rfkill: rewrite
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
  
  struct rfkill * __must_check rfkill_alloc(const char *name,
  					  struct device *parent,
  					  const enum rfkill_type type,
  					  const struct rfkill_ops *ops,
  					  void *ops_data)
  {
  	struct rfkill *rfkill;
  	struct device *dev;
  
  	if (WARN_ON(!ops))
  		return NULL;
  
  	if (WARN_ON(!ops->set_block))
  		return NULL;
  
  	if (WARN_ON(!name))
  		return NULL;
c64fb0162   Johannes Berg   rfkill: create us...
892
  	if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES))
19d337dff   Johannes Berg   rfkill: rewrite
893
  		return NULL;
b7bb11000   Johannes Berg   rfkill: copy the ...
894
  	rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL);
19d337dff   Johannes Berg   rfkill: rewrite
895
896
897
898
899
900
  	if (!rfkill)
  		return NULL;
  
  	spin_lock_init(&rfkill->lock);
  	INIT_LIST_HEAD(&rfkill->node);
  	rfkill->type = type;
b7bb11000   Johannes Berg   rfkill: copy the ...
901
  	strcpy(rfkill->name, name);
19d337dff   Johannes Berg   rfkill: rewrite
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
  	rfkill->ops = ops;
  	rfkill->data = ops_data;
  
  	dev = &rfkill->dev;
  	dev->class = &rfkill_class;
  	dev->parent = parent;
  	device_initialize(dev);
  
  	return rfkill;
  }
  EXPORT_SYMBOL(rfkill_alloc);
  
  static void rfkill_poll(struct work_struct *work)
  {
  	struct rfkill *rfkill;
  
  	rfkill = container_of(work, struct rfkill, poll_work.work);
  
  	/*
  	 * Poll hardware state -- driver will use one of the
  	 * rfkill_set{,_hw,_sw}_state functions and use its
  	 * return value to update the current status.
  	 */
  	rfkill->ops->poll(rfkill, rfkill->data);
67235cbca   Shaibal Dutta   net: rfkill: move...
926
927
  	queue_delayed_work(system_power_efficient_wq,
  		&rfkill->poll_work,
19d337dff   Johannes Berg   rfkill: rewrite
928
929
930
931
932
933
934
935
  		round_jiffies_relative(POLL_INTERVAL));
  }
  
  static void rfkill_uevent_work(struct work_struct *work)
  {
  	struct rfkill *rfkill;
  
  	rfkill = container_of(work, struct rfkill, uevent_work);
c64fb0162   Johannes Berg   rfkill: create us...
936
937
938
  	mutex_lock(&rfkill_global_mutex);
  	rfkill_event(rfkill);
  	mutex_unlock(&rfkill_global_mutex);
19d337dff   Johannes Berg   rfkill: rewrite
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
  }
  
  static void rfkill_sync_work(struct work_struct *work)
  {
  	struct rfkill *rfkill;
  	bool cur;
  
  	rfkill = container_of(work, struct rfkill, sync_work);
  
  	mutex_lock(&rfkill_global_mutex);
  	cur = rfkill_global_states[rfkill->type].cur;
  	rfkill_set_block(rfkill, cur);
  	mutex_unlock(&rfkill_global_mutex);
  }
  
  int __must_check rfkill_register(struct rfkill *rfkill)
  {
  	static unsigned long rfkill_no;
  	struct device *dev = &rfkill->dev;
  	int error;
  
  	BUG_ON(!rfkill);
  
  	mutex_lock(&rfkill_global_mutex);
  
  	if (rfkill->registered) {
  		error = -EALREADY;
  		goto unlock;
  	}
c64fb0162   Johannes Berg   rfkill: create us...
968
  	rfkill->idx = rfkill_no;
19d337dff   Johannes Berg   rfkill: rewrite
969
970
  	dev_set_name(dev, "rfkill%lu", rfkill_no);
  	rfkill_no++;
19d337dff   Johannes Berg   rfkill: rewrite
971
972
973
974
975
976
977
978
979
980
981
  	list_add_tail(&rfkill->node, &rfkill_list);
  
  	error = device_add(dev);
  	if (error)
  		goto remove;
  
  	error = rfkill_led_trigger_register(rfkill);
  	if (error)
  		goto devdel;
  
  	rfkill->registered = true;
2ec2c68c1   Johannes Berg   rfkill: always in...
982
  	INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll);
19d337dff   Johannes Berg   rfkill: rewrite
983
  	INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work);
19d337dff   Johannes Berg   rfkill: rewrite
984
  	INIT_WORK(&rfkill->sync_work, rfkill_sync_work);
2ec2c68c1   Johannes Berg   rfkill: always in...
985
986
  
  	if (rfkill->ops->poll)
67235cbca   Shaibal Dutta   net: rfkill: move...
987
988
  		queue_delayed_work(system_power_efficient_wq,
  			&rfkill->poll_work,
2ec2c68c1   Johannes Berg   rfkill: always in...
989
  			round_jiffies_relative(POLL_INTERVAL));
b3fa1329e   Alan Jenkins   rfkill: remove se...
990
991
992
993
994
995
996
997
998
999
1000
  
  	if (!rfkill->persistent || rfkill_epo_lock_active) {
  		schedule_work(&rfkill->sync_work);
  	} else {
  #ifdef CONFIG_RFKILL_INPUT
  		bool soft_blocked = !!(rfkill->state & RFKILL_BLOCK_SW);
  
  		if (!atomic_read(&rfkill_input_disabled))
  			__rfkill_switch_all(rfkill->type, soft_blocked);
  #endif
  	}
2ec2c68c1   Johannes Berg   rfkill: always in...
1001

9b8e34e21   Michał Kępień   rfkill: Add rfkil...
1002
  	rfkill_any_led_trigger_event();
c64fb0162   Johannes Berg   rfkill: create us...
1003
  	rfkill_send_events(rfkill, RFKILL_OP_ADD);
19d337dff   Johannes Berg   rfkill: rewrite
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
  
  	mutex_unlock(&rfkill_global_mutex);
  	return 0;
  
   devdel:
  	device_del(&rfkill->dev);
   remove:
  	list_del_init(&rfkill->node);
   unlock:
  	mutex_unlock(&rfkill_global_mutex);
  	return error;
  }
  EXPORT_SYMBOL(rfkill_register);
  
  void rfkill_unregister(struct rfkill *rfkill)
  {
  	BUG_ON(!rfkill);
  
  	if (rfkill->ops->poll)
  		cancel_delayed_work_sync(&rfkill->poll_work);
  
  	cancel_work_sync(&rfkill->uevent_work);
  	cancel_work_sync(&rfkill->sync_work);
  
  	rfkill->registered = false;
  
  	device_del(&rfkill->dev);
  
  	mutex_lock(&rfkill_global_mutex);
c64fb0162   Johannes Berg   rfkill: create us...
1033
  	rfkill_send_events(rfkill, RFKILL_OP_DEL);
19d337dff   Johannes Berg   rfkill: rewrite
1034
  	list_del_init(&rfkill->node);
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
1035
  	rfkill_any_led_trigger_event();
19d337dff   Johannes Berg   rfkill: rewrite
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
  	mutex_unlock(&rfkill_global_mutex);
  
  	rfkill_led_trigger_unregister(rfkill);
  }
  EXPORT_SYMBOL(rfkill_unregister);
  
  void rfkill_destroy(struct rfkill *rfkill)
  {
  	if (rfkill)
  		put_device(&rfkill->dev);
  }
  EXPORT_SYMBOL(rfkill_destroy);
c64fb0162   Johannes Berg   rfkill: create us...
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
  static int rfkill_fop_open(struct inode *inode, struct file *file)
  {
  	struct rfkill_data *data;
  	struct rfkill *rfkill;
  	struct rfkill_int_event *ev, *tmp;
  
  	data = kzalloc(sizeof(*data), GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
  
  	INIT_LIST_HEAD(&data->events);
  	mutex_init(&data->mtx);
  	init_waitqueue_head(&data->read_wait);
  
  	mutex_lock(&rfkill_global_mutex);
  	mutex_lock(&data->mtx);
  	/*
  	 * start getting events from elsewhere but hold mtx to get
  	 * startup events added first
  	 */
c64fb0162   Johannes Berg   rfkill: create us...
1068
1069
1070
1071
1072
1073
1074
1075
  
  	list_for_each_entry(rfkill, &rfkill_list, node) {
  		ev = kzalloc(sizeof(*ev), GFP_KERNEL);
  		if (!ev)
  			goto free;
  		rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD);
  		list_add_tail(&ev->list, &data->events);
  	}
bd2281b85   Julia Lawall   net/rfkill/core.c...
1076
  	list_add(&data->list, &rfkill_fds);
c64fb0162   Johannes Berg   rfkill: create us...
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  	mutex_unlock(&data->mtx);
  	mutex_unlock(&rfkill_global_mutex);
  
  	file->private_data = data;
  
  	return nonseekable_open(inode, file);
  
   free:
  	mutex_unlock(&data->mtx);
  	mutex_unlock(&rfkill_global_mutex);
  	mutex_destroy(&data->mtx);
  	list_for_each_entry_safe(ev, tmp, &data->events, list)
  		kfree(ev);
  	kfree(data);
  	return -ENOMEM;
  }
  
  static unsigned int rfkill_fop_poll(struct file *file, poll_table *wait)
  {
  	struct rfkill_data *data = file->private_data;
  	unsigned int res = POLLOUT | POLLWRNORM;
  
  	poll_wait(file, &data->read_wait, wait);
  
  	mutex_lock(&data->mtx);
  	if (!list_empty(&data->events))
  		res = POLLIN | POLLRDNORM;
  	mutex_unlock(&data->mtx);
  
  	return res;
  }
c64fb0162   Johannes Berg   rfkill: create us...
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
  static ssize_t rfkill_fop_read(struct file *file, char __user *buf,
  			       size_t count, loff_t *pos)
  {
  	struct rfkill_data *data = file->private_data;
  	struct rfkill_int_event *ev;
  	unsigned long sz;
  	int ret;
  
  	mutex_lock(&data->mtx);
  
  	while (list_empty(&data->events)) {
  		if (file->f_flags & O_NONBLOCK) {
  			ret = -EAGAIN;
  			goto out;
  		}
  		mutex_unlock(&data->mtx);
6736fde96   Johannes Berg   rfkill: fix rfkil...
1124
1125
1126
  		/* since we re-check and it just compares pointers,
  		 * using !list_empty() without locking isn't a problem
  		 */
c64fb0162   Johannes Berg   rfkill: create us...
1127
  		ret = wait_event_interruptible(data->read_wait,
6736fde96   Johannes Berg   rfkill: fix rfkil...
1128
  					       !list_empty(&data->events));
c64fb0162   Johannes Berg   rfkill: create us...
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
  		mutex_lock(&data->mtx);
  
  		if (ret)
  			goto out;
  	}
  
  	ev = list_first_entry(&data->events, struct rfkill_int_event,
  				list);
  
  	sz = min_t(unsigned long, sizeof(ev->ev), count);
  	ret = sz;
  	if (copy_to_user(buf, &ev->ev, sz))
  		ret = -EFAULT;
  
  	list_del(&ev->list);
  	kfree(ev);
   out:
  	mutex_unlock(&data->mtx);
  	return ret;
  }
  
  static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
  				size_t count, loff_t *pos)
  {
  	struct rfkill *rfkill;
  	struct rfkill_event ev;
1948b2a2e   João Paulo Rechi Vita   rfkill: Use switc...
1155
  	int ret;
c64fb0162   Johannes Berg   rfkill: create us...
1156
1157
  
  	/* we don't need the 'hard' variable but accept it */
1be491fca   Johannes Berg   rfkill: prep for ...
1158
  	if (count < RFKILL_EVENT_SIZE_V1 - 1)
c64fb0162   Johannes Berg   rfkill: create us...
1159
  		return -EINVAL;
1be491fca   Johannes Berg   rfkill: prep for ...
1160
1161
1162
1163
1164
1165
1166
  	/*
  	 * Copy as much data as we can accept into our 'ev' buffer,
  	 * but tell userspace how much we've copied so it can determine
  	 * our API version even in a write() call, if it cares.
  	 */
  	count = min(count, sizeof(ev));
  	if (copy_from_user(&ev, buf, count))
c64fb0162   Johannes Berg   rfkill: create us...
1167
  		return -EFAULT;
c64fb0162   Johannes Berg   rfkill: create us...
1168
1169
1170
1171
  	if (ev.type >= NUM_RFKILL_TYPES)
  		return -EINVAL;
  
  	mutex_lock(&rfkill_global_mutex);
1948b2a2e   João Paulo Rechi Vita   rfkill: Use switc...
1172
1173
  	switch (ev.op) {
  	case RFKILL_OP_CHANGE_ALL:
9487bd6b9   João Paulo Rechi Vita   rfkill: Factor rf...
1174
  		rfkill_update_global_state(ev.type, ev.soft);
1948b2a2e   João Paulo Rechi Vita   rfkill: Use switc...
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
  		list_for_each_entry(rfkill, &rfkill_list, node)
  			if (rfkill->type == ev.type ||
  			    ev.type == RFKILL_TYPE_ALL)
  				rfkill_set_block(rfkill, ev.soft);
  		ret = 0;
  		break;
  	case RFKILL_OP_CHANGE:
  		list_for_each_entry(rfkill, &rfkill_list, node)
  			if (rfkill->idx == ev.idx &&
  			    (rfkill->type == ev.type ||
  			     ev.type == RFKILL_TYPE_ALL))
  				rfkill_set_block(rfkill, ev.soft);
  		ret = 0;
  		break;
  	default:
  		ret = -EINVAL;
  		break;
c64fb0162   Johannes Berg   rfkill: create us...
1192
  	}
1948b2a2e   João Paulo Rechi Vita   rfkill: Use switc...
1193

c64fb0162   Johannes Berg   rfkill: create us...
1194
  	mutex_unlock(&rfkill_global_mutex);
1948b2a2e   João Paulo Rechi Vita   rfkill: Use switc...
1195
  	return ret ?: count;
c64fb0162   Johannes Berg   rfkill: create us...
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
  }
  
  static int rfkill_fop_release(struct inode *inode, struct file *file)
  {
  	struct rfkill_data *data = file->private_data;
  	struct rfkill_int_event *ev, *tmp;
  
  	mutex_lock(&rfkill_global_mutex);
  	list_del(&data->list);
  	mutex_unlock(&rfkill_global_mutex);
  
  	mutex_destroy(&data->mtx);
  	list_for_each_entry_safe(ev, tmp, &data->events, list)
  		kfree(ev);
  
  #ifdef CONFIG_RFKILL_INPUT
  	if (data->input_handler)
207ee1621   Johannes Berg   rfkill: print eve...
1213
1214
1215
  		if (atomic_dec_return(&rfkill_input_disabled) == 0)
  			printk(KERN_DEBUG "rfkill: input handler enabled
  ");
c64fb0162   Johannes Berg   rfkill: create us...
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
  #endif
  
  	kfree(data);
  
  	return 0;
  }
  
  #ifdef CONFIG_RFKILL_INPUT
  static long rfkill_fop_ioctl(struct file *file, unsigned int cmd,
  			     unsigned long arg)
  {
  	struct rfkill_data *data = file->private_data;
  
  	if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC)
  		return -ENOSYS;
  
  	if (_IOC_NR(cmd) != RFKILL_IOC_NOINPUT)
  		return -ENOSYS;
  
  	mutex_lock(&data->mtx);
  
  	if (!data->input_handler) {
207ee1621   Johannes Berg   rfkill: print eve...
1238
1239
1240
  		if (atomic_inc_return(&rfkill_input_disabled) == 1)
  			printk(KERN_DEBUG "rfkill: input handler disabled
  ");
c64fb0162   Johannes Berg   rfkill: create us...
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
  		data->input_handler = true;
  	}
  
  	mutex_unlock(&data->mtx);
  
  	return 0;
  }
  #endif
  
  static const struct file_operations rfkill_fops = {
45ba564d7   Johannes Berg   rfkill: fix miscd...
1251
  	.owner		= THIS_MODULE,
c64fb0162   Johannes Berg   rfkill: create us...
1252
1253
1254
1255
1256
1257
1258
1259
1260
  	.open		= rfkill_fop_open,
  	.read		= rfkill_fop_read,
  	.write		= rfkill_fop_write,
  	.poll		= rfkill_fop_poll,
  	.release	= rfkill_fop_release,
  #ifdef CONFIG_RFKILL_INPUT
  	.unlocked_ioctl	= rfkill_fop_ioctl,
  	.compat_ioctl	= rfkill_fop_ioctl,
  #endif
6038f373a   Arnd Bergmann   llseek: automatic...
1261
  	.llseek		= no_llseek,
c64fb0162   Johannes Berg   rfkill: create us...
1262
1263
1264
1265
1266
1267
1268
  };
  
  static struct miscdevice rfkill_miscdev = {
  	.name	= "rfkill",
  	.fops	= &rfkill_fops,
  	.minor	= MISC_DYNAMIC_MINOR,
  };
19d337dff   Johannes Berg   rfkill: rewrite
1269
1270
1271
1272
  
  static int __init rfkill_init(void)
  {
  	int error;
19d337dff   Johannes Berg   rfkill: rewrite
1273

9487bd6b9   João Paulo Rechi Vita   rfkill: Factor rf...
1274
  	rfkill_update_global_state(RFKILL_TYPE_ALL, !rfkill_default_state);
19d337dff   Johannes Berg   rfkill: rewrite
1275
1276
1277
  
  	error = class_register(&rfkill_class);
  	if (error)
6124c53ed   Michał Kępień   rfkill: Cleanup e...
1278
  		goto error_class;
19d337dff   Johannes Berg   rfkill: rewrite
1279

c64fb0162   Johannes Berg   rfkill: create us...
1280
  	error = misc_register(&rfkill_miscdev);
6124c53ed   Michał Kępień   rfkill: Cleanup e...
1281
1282
  	if (error)
  		goto error_misc;
c64fb0162   Johannes Berg   rfkill: create us...
1283

9b8e34e21   Michał Kępień   rfkill: Add rfkil...
1284
1285
1286
  	error = rfkill_any_led_trigger_register();
  	if (error)
  		goto error_led_trigger;
19d337dff   Johannes Berg   rfkill: rewrite
1287
1288
  #ifdef CONFIG_RFKILL_INPUT
  	error = rfkill_handler_init();
6124c53ed   Michał Kępień   rfkill: Cleanup e...
1289
1290
  	if (error)
  		goto error_input;
19d337dff   Johannes Berg   rfkill: rewrite
1291
  #endif
6124c53ed   Michał Kępień   rfkill: Cleanup e...
1292
  	return 0;
f6b4122c9   Arnd Bergmann   rfkill: hide unus...
1293
  #ifdef CONFIG_RFKILL_INPUT
6124c53ed   Michał Kępień   rfkill: Cleanup e...
1294
  error_input:
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
1295
  	rfkill_any_led_trigger_unregister();
7b854982b   Johannes Berg   Revert "rfkill: A...
1296
  #endif
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
1297
1298
  error_led_trigger:
  	misc_deregister(&rfkill_miscdev);
6124c53ed   Michał Kępień   rfkill: Cleanup e...
1299
1300
1301
  error_misc:
  	class_unregister(&rfkill_class);
  error_class:
19d337dff   Johannes Berg   rfkill: rewrite
1302
1303
1304
1305
1306
1307
1308
1309
1310
  	return error;
  }
  subsys_initcall(rfkill_init);
  
  static void __exit rfkill_exit(void)
  {
  #ifdef CONFIG_RFKILL_INPUT
  	rfkill_handler_exit();
  #endif
9b8e34e21   Michał Kępień   rfkill: Add rfkil...
1311
  	rfkill_any_led_trigger_unregister();
c64fb0162   Johannes Berg   rfkill: create us...
1312
  	misc_deregister(&rfkill_miscdev);
19d337dff   Johannes Berg   rfkill: rewrite
1313
1314
1315
  	class_unregister(&rfkill_class);
  }
  module_exit(rfkill_exit);