X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=net%2Frfkill%2Frfkill.c;h=f949a482b0078b904d88b655da399d8706a48ca8;hb=d9be28ea9110c596a05bd2d56afa94251bd19818;hp=35a9994e23397e70b7f099b1bd573ed28c361512;hpb=fe47784ba5cbb6b713c013e046859946789b45e4;p=linux-2.6-omap-h63xx.git diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 35a9994e233..3eaa39403c1 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -37,63 +37,27 @@ MODULE_DESCRIPTION("RF switch support"); MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ -static DEFINE_MUTEX(rfkill_mutex); +static DEFINE_MUTEX(rfkill_global_mutex); static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; 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 enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; - -static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); - - -/** - * register_rfkill_notifier - Add notifier to rfkill notifier chain - * @nb: pointer to the new entry to add to the chain - * - * See blocking_notifier_chain_register() for return value and further - * observations. - * - * Adds a notifier to the rfkill notifier chain. The chain will be - * called with a pointer to the relevant rfkill structure as a parameter, - * refer to include/linux/rfkill.h for the possible events. - * - * Notifiers added to this chain are to always return NOTIFY_DONE. This - * chain is a blocking notifier chain: notifiers can sleep. - * - * Calls to this chain may have been done through a workqueue. One must - * assume unordered asynchronous behaviour, there is no way to know if - * actions related to the event that generated the notification have been - * carried out already. - */ -int register_rfkill_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&rfkill_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(register_rfkill_notifier); +struct rfkill_gsw_state { + enum rfkill_state current_state; + enum rfkill_state default_state; +}; -/** - * unregister_rfkill_notifier - remove notifier from rfkill notifier chain - * @nb: pointer to the entry to remove from the chain - * - * See blocking_notifier_chain_unregister() for return value and further - * observations. - * - * Removes a notifier from the rfkill notifier chain. - */ -int unregister_rfkill_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(unregister_rfkill_notifier); +static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX]; +static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; +static bool rfkill_epo_lock_active; +#ifdef CONFIG_RFKILL_LEDS static void rfkill_led_trigger(struct rfkill *rfkill, enum rfkill_state state) { -#ifdef CONFIG_RFKILL_LEDS struct led_trigger *led = &rfkill->led_trigger; if (!led->name) @@ -102,10 +66,8 @@ static void rfkill_led_trigger(struct rfkill *rfkill, led_trigger_event(led, LED_OFF); else led_trigger_event(led, LED_FULL); -#endif /* CONFIG_RFKILL_LEDS */ } -#ifdef CONFIG_RFKILL_LEDS static void rfkill_led_trigger_activate(struct led_classdev *led) { struct rfkill *rfkill = container_of(led->trigger, @@ -115,11 +77,9 @@ static void rfkill_led_trigger_activate(struct led_classdev *led) } #endif /* CONFIG_RFKILL_LEDS */ -static void notify_rfkill_state_change(struct rfkill *rfkill) +static void rfkill_uevent(struct rfkill *rfkill) { - blocking_notifier_call_chain(&rfkill_notifier_list, - RFKILL_STATE_CHANGED, - rfkill); + kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); } static void update_rfkill_state(struct rfkill *rfkill) @@ -132,7 +92,7 @@ static void update_rfkill_state(struct rfkill *rfkill) oldstate = rfkill->state; rfkill->state = newstate; if (oldstate != newstate) - notify_rfkill_state_change(rfkill); + rfkill_uevent(rfkill); } mutex_unlock(&rfkill->mutex); } @@ -195,6 +155,11 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, * BLOCK even a transmitter that is already in state * RFKILL_STATE_HARD_BLOCKED */ break; + default: + WARN(1, KERN_WARNING + "rfkill: illegal state %d passed as parameter " + "to rfkill_toggle_radio\n", state); + return -EINVAL; } if (force || state != rfkill->state) { @@ -204,31 +169,36 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, rfkill->state = state; } - if (force || rfkill->state != oldstate) { - rfkill_led_trigger(rfkill, rfkill->state); - notify_rfkill_state_change(rfkill); - } + if (force || rfkill->state != oldstate) + rfkill_uevent(rfkill); return retval; } /** - * rfkill_switch_all - Toggle state of all switches of given type + * __rfkill_switch_all - Toggle state of all switches of given type * @type: type of interfaces to be affected * @state: the new state * * This function toggles the state of all switches of given type, * unless a specific switch is claimed by userspace (in which case, * that switch is left alone) or suspended. + * + * Caller must have acquired rfkill_global_mutex. */ -void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) +static void __rfkill_switch_all(const enum rfkill_type type, + const enum rfkill_state state) { struct rfkill *rfkill; - mutex_lock(&rfkill_mutex); - - rfkill_states[type] = state; + if (WARN((state >= RFKILL_STATE_MAX || type >= RFKILL_TYPE_MAX), + KERN_WARNING + "rfkill: illegal state %d or type %d " + "passed as parameter to __rfkill_switch_all\n", + state, type)) + return; + rfkill_global_states[type].current_state = state; list_for_each_entry(rfkill, &rfkill_list, node) { if ((!rfkill->user_claim) && (rfkill->type == type)) { mutex_lock(&rfkill->mutex); @@ -236,8 +206,24 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) mutex_unlock(&rfkill->mutex); } } +} - mutex_unlock(&rfkill_mutex); +/** + * rfkill_switch_all - Toggle state of all switches of given type + * @type: type of interfaces to be affected + * @state: 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, enum rfkill_state state) +{ + mutex_lock(&rfkill_global_mutex); + if (!rfkill_epo_lock_active) + __rfkill_switch_all(type, state); + mutex_unlock(&rfkill_global_mutex); } EXPORT_SYMBOL(rfkill_switch_all); @@ -245,22 +231,96 @@ EXPORT_SYMBOL(rfkill_switch_all); * 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_mutex and rfkill->mutex. + * 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; - mutex_lock(&rfkill_mutex); + mutex_lock(&rfkill_global_mutex); + + rfkill_epo_lock_active = true; list_for_each_entry(rfkill, &rfkill_list, node) { mutex_lock(&rfkill->mutex); rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); mutex_unlock(&rfkill->mutex); } - mutex_unlock(&rfkill_mutex); + for (i = 0; i < RFKILL_TYPE_MAX; i++) { + rfkill_global_states[i].default_state = + rfkill_global_states[i].current_state; + rfkill_global_states[i].current_state = + RFKILL_STATE_SOFT_BLOCKED; + } + mutex_unlock(&rfkill_global_mutex); } EXPORT_SYMBOL_GPL(rfkill_epo); +/** + * 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; + + mutex_lock(&rfkill_global_mutex); + + rfkill_epo_lock_active = false; + for (i = 0; i < RFKILL_TYPE_MAX; i++) + __rfkill_switch_all(i, rfkill_global_states[i].default_state); + mutex_unlock(&rfkill_global_mutex); +} +EXPORT_SYMBOL_GPL(rfkill_restore_states); + +/** + * 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) +{ + mutex_lock(&rfkill_global_mutex); + rfkill_epo_lock_active = false; + mutex_unlock(&rfkill_global_mutex); +} +EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock); + +/** + * 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; +} +EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active); + +/** + * rfkill_get_global_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. + */ +enum rfkill_state rfkill_get_global_state(const enum rfkill_type type) +{ + return rfkill_global_states[type].current_state; +} +EXPORT_SYMBOL_GPL(rfkill_get_global_state); + /** * rfkill_force_state - Force the internal rfkill radio state * @rfkill: pointer to the rfkill class to modify. @@ -282,9 +342,11 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { enum rfkill_state oldstate; - if (state != RFKILL_STATE_SOFT_BLOCKED && - state != RFKILL_STATE_UNBLOCKED && - state != RFKILL_STATE_HARD_BLOCKED) + BUG_ON(!rfkill); + if (WARN((state >= RFKILL_STATE_MAX), + KERN_WARNING + "rfkill: illegal state %d passed as parameter " + "to rfkill_force_state\n", state)) return -EINVAL; mutex_lock(&rfkill->mutex); @@ -293,7 +355,7 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) rfkill->state = state; if (state != oldstate) - notify_rfkill_state_change(rfkill); + rfkill_uevent(rfkill); mutex_unlock(&rfkill->mutex); @@ -352,20 +414,30 @@ static ssize_t rfkill_state_store(struct device *dev, const char *buf, size_t count) { struct rfkill *rfkill = to_rfkill(dev); - unsigned int state = simple_strtoul(buf, NULL, 0); + unsigned long state; int error; if (!capable(CAP_NET_ADMIN)) return -EPERM; + error = strict_strtoul(buf, 0, &state); + if (error) + return error; + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ if (state != RFKILL_STATE_UNBLOCKED && state != RFKILL_STATE_SOFT_BLOCKED) return -EINVAL; - if (mutex_lock_interruptible(&rfkill->mutex)) - return -ERESTARTSYS; - error = rfkill_toggle_radio(rfkill, state, 0); + error = mutex_lock_killable(&rfkill->mutex); + if (error) + return error; + + if (!rfkill_epo_lock_active) + error = rfkill_toggle_radio(rfkill, state, 0); + else + error = -EPERM; + mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -377,7 +449,7 @@ static ssize_t rfkill_claim_show(struct device *dev, { struct rfkill *rfkill = to_rfkill(dev); - return sprintf(buf, "%d", rfkill->user_claim); + return sprintf(buf, "%d\n", rfkill->user_claim); } static ssize_t rfkill_claim_store(struct device *dev, @@ -385,7 +457,8 @@ static ssize_t rfkill_claim_store(struct device *dev, const char *buf, size_t count) { struct rfkill *rfkill = to_rfkill(dev); - bool claim = !!simple_strtoul(buf, NULL, 0); + unsigned long claim_tmp; + bool claim; int error; if (!capable(CAP_NET_ADMIN)) @@ -394,26 +467,31 @@ static ssize_t rfkill_claim_store(struct device *dev, if (rfkill->user_claim_unsupported) return -EOPNOTSUPP; + error = strict_strtoul(buf, 0, &claim_tmp); + if (error) + return error; + claim = !!claim_tmp; + /* * Take the global lock to make sure the kernel is not in * the middle of rfkill_switch_all */ - error = mutex_lock_interruptible(&rfkill_mutex); + error = mutex_lock_killable(&rfkill_global_mutex); if (error) return error; if (rfkill->user_claim != claim) { - if (!claim) { + if (!claim && !rfkill_epo_lock_active) { mutex_lock(&rfkill->mutex); rfkill_toggle_radio(rfkill, - rfkill_states[rfkill->type], - 0); + rfkill_global_states[rfkill->type].current_state, + 0); mutex_unlock(&rfkill->mutex); } rfkill->user_claim = claim; } - mutex_unlock(&rfkill_mutex); + mutex_unlock(&rfkill_global_mutex); return error ? error : count; } @@ -439,19 +517,12 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) { struct rfkill *rfkill = to_rfkill(dev); - if (dev->power.power_state.event != state.event) { - if (state.event & PM_EVENT_SLEEP) { - /* Stop transmitter, keep state, no notifies */ - update_rfkill_state(rfkill); - - mutex_lock(&rfkill->mutex); - rfkill->toggle_radio(rfkill->data, - RFKILL_STATE_SOFT_BLOCKED); - mutex_unlock(&rfkill->mutex); - } - + /* mark class device as suspended */ + if (dev->power.power_state.event != state.event) dev->power.power_state = state; - } + + /* store state for the resume handler */ + rfkill->state_for_resume = rfkill->state; return 0; } @@ -459,14 +530,33 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) static int rfkill_resume(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); + enum rfkill_state newstate; if (dev->power.power_state.event != PM_EVENT_ON) { mutex_lock(&rfkill->mutex); dev->power.power_state.event = PM_EVENT_ON; - /* restore radio state AND notify everybody */ - rfkill_toggle_radio(rfkill, rfkill->state, 1); + /* + * rfkill->state could have been modified before we got + * called, and won't be updated by rfkill_toggle_radio() + * in force mode. Sync it FIRST. + */ + if (rfkill->get_state && + !rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; + + /* + * If we are under EPO, kick transmitter offline, + * otherwise restore to pre-suspend state. + * + * Issue a notification in any case + */ + rfkill_toggle_radio(rfkill, + rfkill_epo_lock_active ? + RFKILL_STATE_SOFT_BLOCKED : + rfkill->state_for_resume, + 1); mutex_unlock(&rfkill->mutex); } @@ -478,28 +568,6 @@ static int rfkill_resume(struct device *dev) #define rfkill_resume NULL #endif -static int rfkill_blocking_uevent_notifier(struct notifier_block *nb, - unsigned long eventid, - void *data) -{ - struct rfkill *rfkill = (struct rfkill *)data; - - switch (eventid) { - case RFKILL_STATE_CHANGED: - kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); - break; - default: - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block rfkill_blocking_uevent_nb = { - .notifier_call = rfkill_blocking_uevent_notifier, - .priority = 0, -}; - static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { struct rfkill *rfkill = to_rfkill(dev); @@ -525,24 +593,60 @@ static struct class rfkill_class = { .dev_uevent = rfkill_dev_uevent, }; +static int rfkill_check_duplicity(const struct rfkill *rfkill) +{ + struct rfkill *p; + unsigned long seen[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; + + memset(seen, 0, sizeof(seen)); + + list_for_each_entry(p, &rfkill_list, node) { + if (WARN((p == rfkill), KERN_WARNING + "rfkill: illegal attempt to register " + "an already registered rfkill struct\n")) + return -EEXIST; + set_bit(p->type, seen); + } + + /* 0: first switch of its kind */ + return (test_bit(rfkill->type, seen)) ? 1 : 0; +} + static int rfkill_add_switch(struct rfkill *rfkill) { - mutex_lock(&rfkill_mutex); + int error; - rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); + mutex_lock(&rfkill_global_mutex); + + error = rfkill_check_duplicity(rfkill); + if (error < 0) + goto unlock_out; + + if (!error) { + /* lock default after first use */ + set_bit(rfkill->type, rfkill_states_lockdflt); + rfkill_global_states[rfkill->type].current_state = + rfkill_global_states[rfkill->type].default_state; + } + + rfkill_toggle_radio(rfkill, + rfkill_global_states[rfkill->type].current_state, + 0); list_add_tail(&rfkill->node, &rfkill_list); - mutex_unlock(&rfkill_mutex); + error = 0; +unlock_out: + mutex_unlock(&rfkill_global_mutex); - return 0; + return error; } static void rfkill_remove_switch(struct rfkill *rfkill) { - mutex_lock(&rfkill_mutex); + mutex_lock(&rfkill_global_mutex); list_del_init(&rfkill->node); - mutex_unlock(&rfkill_mutex); + mutex_unlock(&rfkill_global_mutex); mutex_lock(&rfkill->mutex); rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); @@ -562,11 +666,18 @@ static void rfkill_remove_switch(struct rfkill *rfkill) * NOTE: If registration fails the structure shoudl be freed by calling * rfkill_free() otherwise rfkill_unregister() should be used. */ -struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type) +struct rfkill * __must_check rfkill_allocate(struct device *parent, + enum rfkill_type type) { struct rfkill *rfkill; struct device *dev; + if (WARN((type >= RFKILL_TYPE_MAX), + KERN_WARNING + "rfkill: illegal type %d passed as parameter " + "to rfkill_allocate\n", type)) + return NULL; + rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL); if (!rfkill) return NULL; @@ -606,7 +717,7 @@ static void rfkill_led_trigger_register(struct rfkill *rfkill) int error; if (!rfkill->led_trigger.name) - rfkill->led_trigger.name = rfkill->dev.bus_id; + rfkill->led_trigger.name = dev_name(&rfkill->dev); if (!rfkill->led_trigger.activate) rfkill->led_trigger.activate = rfkill_led_trigger_activate; error = led_trigger_register(&rfkill->led_trigger); @@ -633,19 +744,21 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill) * structure needs to be registered. Immediately from registration the * switch driver should be able to service calls to toggle_radio. */ -int rfkill_register(struct rfkill *rfkill) +int __must_check rfkill_register(struct rfkill *rfkill) { static atomic_t rfkill_no = ATOMIC_INIT(0); struct device *dev = &rfkill->dev; int error; - if (!rfkill->toggle_radio) - return -EINVAL; - if (rfkill->type >= RFKILL_TYPE_MAX) + if (WARN((!rfkill || !rfkill->toggle_radio || + rfkill->type >= RFKILL_TYPE_MAX || + rfkill->state >= RFKILL_STATE_MAX), + KERN_WARNING + "rfkill: attempt to register a " + "badly initialized rfkill struct\n")) return -EINVAL; - snprintf(dev->bus_id, sizeof(dev->bus_id), - "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); + dev_set_name(dev, "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); rfkill_led_trigger_register(rfkill); @@ -676,6 +789,7 @@ EXPORT_SYMBOL(rfkill_register); */ void rfkill_unregister(struct rfkill *rfkill) { + BUG_ON(!rfkill); device_del(&rfkill->dev); rfkill_remove_switch(rfkill); rfkill_led_trigger_unregister(rfkill); @@ -683,6 +797,57 @@ void rfkill_unregister(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_unregister); +/** + * rfkill_set_default - set initial value for a switch type + * @type - the type of switch to set the default state of + * @state - the new default state for that group of switches + * + * Sets the initial state rfkill should use for a given type. + * The following initial states are allowed: RFKILL_STATE_SOFT_BLOCKED + * and RFKILL_STATE_UNBLOCKED. + * + * This function is meant to be used by platform drivers for platforms + * that can save switch state across power down/reboot. + * + * The default state for each switch type can be changed exactly once. + * After a switch of that type is registered, the default state cannot + * be changed anymore. This guards against multiple drivers it the + * same platform trying to set the initial switch default state, which + * is not allowed. + * + * Returns -EPERM if the state has already been set once or is in use, + * so drivers likely want to either ignore or at most printk(KERN_NOTICE) + * if this function returns -EPERM. + * + * Returns 0 if the new default state was set, or an error if it + * could not be set. + */ +int rfkill_set_default(enum rfkill_type type, enum rfkill_state state) +{ + int error; + + if (WARN((type >= RFKILL_TYPE_MAX || + (state != RFKILL_STATE_SOFT_BLOCKED && + state != RFKILL_STATE_UNBLOCKED)), + KERN_WARNING + "rfkill: illegal state %d or type %d passed as " + "parameter to rfkill_set_default\n", state, type)) + return -EINVAL; + + mutex_lock(&rfkill_global_mutex); + + if (!test_and_set_bit(type, rfkill_states_lockdflt)) { + rfkill_global_states[type].default_state = state; + rfkill_global_states[type].current_state = state; + error = 0; + } else + error = -EPERM; + + mutex_unlock(&rfkill_global_mutex); + return error; +} +EXPORT_SYMBOL_GPL(rfkill_set_default); + /* * Rfkill module initialization/deinitialization. */ @@ -696,8 +861,8 @@ static int __init rfkill_init(void) rfkill_default_state != RFKILL_STATE_UNBLOCKED) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) - rfkill_states[i] = rfkill_default_state; + for (i = 0; i < RFKILL_TYPE_MAX; i++) + rfkill_global_states[i].default_state = rfkill_default_state; error = class_register(&rfkill_class); if (error) { @@ -705,14 +870,11 @@ static int __init rfkill_init(void) return error; } - register_rfkill_notifier(&rfkill_blocking_uevent_nb); - return 0; } static void __exit rfkill_exit(void) { - unregister_rfkill_notifier(&rfkill_blocking_uevent_nb); class_unregister(&rfkill_class); }