1291 lines · 29.2 KB · C
RawDownload
/* * 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 * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #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>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/device.h>#include <linux/miscdevice.h>#include <linux/wait.h>#include <linux/poll.h>#include <linux/fs.h>#include <linux/slab.h> #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; 	enum rfkill_type	type; 	unsigned long		state; 	u32			idx; 	bool			registered;	bool			persistent;	bool			polling_paused;	bool			suspended; 	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;	char			name[];};#define to_rfkill(d)	container_of(d, struct rfkill, dev) 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;};  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);static LIST_HEAD(rfkill_fds);	/* list of open fds of /dev/rfkill */ 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 {	bool cur, sav;} rfkill_global_states[NUM_RFKILL_TYPES]; static bool rfkill_epo_lock_active;  #ifdef CONFIG_RFKILL_LEDSstatic 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);} 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); 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);}#elsestatic 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){}#endif /* CONFIG_RFKILL_LEDS */ 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){	if (!rfkill->registered)		return; 	kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); 	/* also send event to /dev/rfkill */	rfkill_send_events(rfkill, RFKILL_OP_CHANGE);} /** * 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;	bool prev, curr;	int err; 	if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))		return; 	/*	 * 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);	prev = rfkill->state & RFKILL_BLOCK_SW; 	if (prev)		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); 	err = rfkill->ops->set_block(rfkill->data, blocked); 	spin_lock_irqsave(&rfkill->lock, flags);	if (err) {		/*		 * Failed -- reset status to _PREV, which may be different		 * from what we have set _PREV to earlier in this function		 * 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;	curr = rfkill->state & RFKILL_BLOCK_SW;	spin_unlock_irqrestore(&rfkill->lock, flags); 	rfkill_led_trigger_event(rfkill); 	if (prev != curr)		rfkill_event(rfkill);} 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;} #ifdef CONFIG_RFKILL_INPUTstatic atomic_t rfkill_input_disabled = ATOMIC_INIT(0); /** * __rfkill_switch_all - Toggle state of all switches of given type * @type: type of interfaces to be affected * @blocked: the new state * * This function sets the state of all switches of given type, * unless a specific switch is suspended. * * Caller must have acquired rfkill_global_mutex. */static void __rfkill_switch_all(const enum rfkill_type type, bool blocked){	struct rfkill *rfkill; 	rfkill_update_global_state(type, blocked);	list_for_each_entry(rfkill, &rfkill_list, node) {		if (rfkill->type != type && type != RFKILL_TYPE_ALL)			continue; 		rfkill_set_block(rfkill, blocked);	}} /** * rfkill_switch_all - Toggle state of all switches of given type * @type: type of interfaces to be affected * @blocked: the new state * * 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){	if (atomic_read(&rfkill_input_disabled))		return; 	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; 	if (atomic_read(&rfkill_input_disabled))		return; 	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++) {		rfkill_global_states[i].sav = rfkill_global_states[i].cur;		rfkill_global_states[i].cur = true;	} 	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; 	if (atomic_read(&rfkill_input_disabled))		return; 	mutex_lock(&rfkill_global_mutex); 	rfkill_epo_lock_active = false;	for (i = 0; i < NUM_RFKILL_TYPES; i++)		__rfkill_switch_all(i, rfkill_global_states[i].sav);	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){	if (atomic_read(&rfkill_input_disabled))		return; 	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;}#endif bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked){	unsigned long flags;	bool ret, prev; 	BUG_ON(!rfkill); 	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); 	rfkill_led_trigger_event(rfkill); 	if (!rfkill->registered)		return ret; 	if (prev != blocked)		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;

Lines 1–500 of 1291.