From c8fcd905a59a535bff93a120ac44b09ce24e13e6 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:57 -0300 Subject: rfkill: fix minor typo in kernel doc Fix a minor typo in an exported function documentation Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 4e10a95de832..f95081a4a024 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -412,7 +412,7 @@ int rfkill_register(struct rfkill *rfkill) EXPORT_SYMBOL(rfkill_register); /** - * rfkill_unregister - Uegister a rfkill structure. + * rfkill_unregister - Unregister a rfkill structure. * @rfkill: rfkill structure to be unregistered * * This function should be called by the network driver during device -- cgit v1.2.2 From 28f089c18464810ec9e91ee10a89adbb02ad7765 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:58 -0300 Subject: rfkill: handle SW_RFKILL_ALL events Teach rfkill-input how to handle SW_RFKILL_ALL events (new name for the SW_RADIO event). SW_RFKILL_ALL is an absolute enable-or-disable command that is tied to all radios in a system. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index e4b051dbed61..9d6c9255bf2c 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -55,6 +55,22 @@ static void rfkill_task_handler(struct work_struct *work) mutex_unlock(&task->mutex); } +static void rfkill_schedule_set(struct rfkill_task *task, + enum rfkill_state desired_state) +{ + unsigned long flags; + + spin_lock_irqsave(&task->lock, flags); + + if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { + task->desired_state = desired_state; + task->last = jiffies; + schedule_work(&task->work); + } + + spin_unlock_irqrestore(&task->lock, flags); +} + static void rfkill_schedule_toggle(struct rfkill_task *task) { unsigned long flags; @@ -87,9 +103,9 @@ static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); static void rfkill_event(struct input_handle *handle, unsigned int type, - unsigned int code, int down) + unsigned int code, int data) { - if (type == EV_KEY && down == 1) { + if (type == EV_KEY && data == 1) { switch (code) { case KEY_WLAN: rfkill_schedule_toggle(&rfkill_wlan); @@ -106,6 +122,26 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, default: break; } + } else if (type == EV_SW) { + switch (code) { + case SW_RFKILL_ALL: + /* EVERY radio type. data != 0 means radios ON */ + rfkill_schedule_set(&rfkill_wimax, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + rfkill_schedule_set(&rfkill_uwb, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + rfkill_schedule_set(&rfkill_bt, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + rfkill_schedule_set(&rfkill_wlan, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + break; + default: + break; + } } } @@ -168,6 +204,11 @@ static const struct input_device_id rfkill_ids[] = { .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) }, }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT, + .evbit = { BIT(EV_SW) }, + .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) }, + }, { } }; -- cgit v1.2.2 From e954b0b85b9e737564b8ad9738de5816747b5901 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:59 -0300 Subject: rfkill: add parameter to disable radios by default Currently, radios are always enabled when their rfkill interface is registered. This is not optimal, the safest state for a radio is to be offline unless the user turns it on. Add a module parameter that causes all radios to be disabled when their rfkill interface is registered. The module default is not changed so unless the parameter is used, radios will still be forced to their enabled state when they are registered. The new rfkill module parameter is called "default_state". Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index f95081a4a024..3edc585dcfa6 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,6 +39,11 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_mutex); +static unsigned int rfkill_default_state = RFKILL_STATE_ON; +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]; @@ -436,8 +441,12 @@ static int __init rfkill_init(void) int error; int i; + if (rfkill_default_state != RFKILL_STATE_OFF && + rfkill_default_state != RFKILL_STATE_ON) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) - rfkill_states[i] = RFKILL_STATE_ON; + rfkill_states[i] = rfkill_default_state; error = class_register(&rfkill_class); if (error) { -- cgit v1.2.2 From 801e49af4c1a9b988ba0d25de2b368c99c3bf2b3 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:00 -0300 Subject: rfkill: add read-write rfkill switch support Currently, rfkill support for read/write rfkill switches is hacked through a round-trip over the input layer and rfkill-input to let a driver sync rfkill->state to hardware changes. This is buggy and sub-optimal. It causes real problems. It is best to think of the rfkill class as supporting only write-only switches at the moment. In order to implement the read/write functionality properly: Add a get_state() hook that is called by the class every time it needs to fetch the current state of the switch. Add a call to this hook every time the *current* state of the radio plays a role in a decision. Also add a force_state() method that can be used to forcefully syncronize the class' idea of the current state of the switch. This allows for a faster implementation of the read/write functionality, as a driver which get events on switch changes can avoid the need for a get_state() hook. If the get_state() hook is left as NULL, current behaviour is maintained, so this change is fully backwards compatible with the current rfkill drivers. For hardware that issues events when the rfkill state changes, leave get_state() NULL in the rfkill struct, set the initial state properly before registering with the rfkill class, and use the force_state() method in the driver to keep the rfkill interface up-to-date. get_state() can be called by the class from atomic context. It must not sleep. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3edc585dcfa6..4ae4486c77ea 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -62,19 +62,39 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void update_rfkill_state(struct rfkill *rfkill) +{ + enum rfkill_state newstate; + + if (rfkill->get_state) { + mutex_lock(&rfkill->mutex); + if (!rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; + mutex_unlock(&rfkill->mutex); + } +} + static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state) { int retval = 0; + enum rfkill_state oldstate, newstate; + + oldstate = rfkill->state; + + if (rfkill->get_state && + !rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; if (state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) { + if (!retval) rfkill->state = state; - rfkill_led_trigger(rfkill, state); - } } + if (rfkill->state != oldstate) + rfkill_led_trigger(rfkill, rfkill->state); + return retval; } @@ -105,6 +125,32 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_force_state - Force the internal rfkill radio state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current radio state the class should be forced to. + * + * This function updates the internal state of the radio cached + * by the rfkill class. It should be used when the driver gets + * a notification by the firmware/hardware of the current *real* + * state of the radio rfkill switch. + * + * It may not be called from an atomic context. + */ +int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) +{ + if (state != RFKILL_STATE_OFF && + state != RFKILL_STATE_ON) + return -EINVAL; + + mutex_lock(&rfkill->mutex); + rfkill->state = state; + mutex_unlock(&rfkill->mutex); + + return 0; +} +EXPORT_SYMBOL(rfkill_force_state); + static ssize_t rfkill_name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -147,6 +193,7 @@ static ssize_t rfkill_state_show(struct device *dev, { struct rfkill *rfkill = to_rfkill(dev); + update_rfkill_state(rfkill); return sprintf(buf, "%d\n", rfkill->state); } -- cgit v1.2.2 From 477576a073699783abb53ae14993d5d41c66301d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:01 -0300 Subject: rfkill: add the WWAN radio type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately, instead of adding a generic Wireless WAN type, a technology- specific type (WiMAX) was added. That's useless for other WWAN devices, such as EDGE, UMTS, X-RTT and other such radios. Add a WWAN rfkill type for generic wireless WAN devices. No keys are added as most devices really want to use KEY_WLAN for WWAN control (in a cycle of none, WLAN, WWAN, WLAN+WWAN) and need no specific keycode added. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Iñaky Pérez-González Cc: David S. Miller Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 4 ++++ net/rfkill/rfkill.c | 3 +++ 2 files changed, 7 insertions(+) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 9d6c9255bf2c..29c13d308b31 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -101,6 +101,7 @@ static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); +static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN); static void rfkill_event(struct input_handle *handle, unsigned int type, unsigned int code, int data) @@ -126,6 +127,9 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, switch (code) { case SW_RFKILL_ALL: /* EVERY radio type. data != 0 means radios ON */ + rfkill_schedule_set(&rfkill_wwan, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); rfkill_schedule_set(&rfkill_wimax, (data)? RFKILL_STATE_ON: RFKILL_STATE_OFF); diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 4ae4486c77ea..79f3bbb027ff 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -180,6 +180,9 @@ static ssize_t rfkill_type_show(struct device *dev, case RFKILL_TYPE_WIMAX: type = "wimax"; break; + case RFKILL_TYPE_WWAN: + type = "wwan"; + break; default: BUG(); } -- cgit v1.2.2 From 526324b61a9667ed9a71f0a8a8899cf675346c76 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:02 -0300 Subject: rfkill: rework suspend and resume handlers The resume handler should reset the wireless transmitter rfkill state to exactly what it was when the system was suspended. Do it, and do it using the normal routines for state change while at it. The suspend handler should force-switch the transmitter to blocked state, ignoring caches. Do it. Also take an opportunity shot to rfkill_remove_switch() and also force the transmitter to blocked state there, bypassing caches. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 79f3bbb027ff..fb566902030a 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -75,24 +75,25 @@ static void update_rfkill_state(struct rfkill *rfkill) } static int rfkill_toggle_radio(struct rfkill *rfkill, - enum rfkill_state state) + enum rfkill_state state, + int force) { int retval = 0; enum rfkill_state oldstate, newstate; oldstate = rfkill->state; - if (rfkill->get_state && + if (rfkill->get_state && !force && !rfkill->get_state(rfkill->data, &newstate)) rfkill->state = newstate; - if (state != rfkill->state) { + if (force || state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); if (!retval) rfkill->state = state; } - if (rfkill->state != oldstate) + if (force || rfkill->state != oldstate) rfkill_led_trigger(rfkill, rfkill->state); return retval; @@ -107,7 +108,6 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, * a specific switch is claimed by userspace in which case it is * left alone. */ - void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) { struct rfkill *rfkill; @@ -118,7 +118,7 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) list_for_each_entry(rfkill, &rfkill_list, node) { if ((!rfkill->user_claim) && (rfkill->type == type)) - rfkill_toggle_radio(rfkill, state); + rfkill_toggle_radio(rfkill, state, 0); } mutex_unlock(&rfkill_mutex); @@ -214,7 +214,8 @@ static ssize_t rfkill_state_store(struct device *dev, if (mutex_lock_interruptible(&rfkill->mutex)) return -ERESTARTSYS; error = rfkill_toggle_radio(rfkill, - state ? RFKILL_STATE_ON : RFKILL_STATE_OFF); + state ? RFKILL_STATE_ON : RFKILL_STATE_OFF, + 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -255,7 +256,8 @@ static ssize_t rfkill_claim_store(struct device *dev, if (rfkill->user_claim != claim) { if (!claim) rfkill_toggle_radio(rfkill, - rfkill_states[rfkill->type]); + rfkill_states[rfkill->type], + 0); rfkill->user_claim = claim; } @@ -288,12 +290,11 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) if (dev->power.power_state.event != state.event) { if (state.event & PM_EVENT_SLEEP) { - mutex_lock(&rfkill->mutex); - - if (rfkill->state == RFKILL_STATE_ON) - rfkill->toggle_radio(rfkill->data, - RFKILL_STATE_OFF); + /* Stop transmitter, keep state, no notifies */ + update_rfkill_state(rfkill); + mutex_lock(&rfkill->mutex); + rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); mutex_unlock(&rfkill->mutex); } @@ -310,8 +311,8 @@ static int rfkill_resume(struct device *dev) if (dev->power.power_state.event != PM_EVENT_ON) { mutex_lock(&rfkill->mutex); - if (rfkill->state == RFKILL_STATE_ON) - rfkill->toggle_radio(rfkill->data, RFKILL_STATE_ON); + /* restore radio state AND notify everybody */ + rfkill_toggle_radio(rfkill, rfkill->state, 1); mutex_unlock(&rfkill->mutex); } @@ -338,7 +339,7 @@ static int rfkill_add_switch(struct rfkill *rfkill) mutex_lock(&rfkill_mutex); - error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type]); + error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); if (!error) list_add_tail(&rfkill->node, &rfkill_list); @@ -351,7 +352,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF); + rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); mutex_unlock(&rfkill_mutex); } -- cgit v1.2.2 From 79399a8d1908f6a406e82d23c5a9937e1722ed3a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:03 -0300 Subject: rfkill: add notifier chains support Add a notifier chain for use by the rfkill class. This notifier chain signals the following events (more to be added when needed): 1. rfkill: rfkill device state has changed A pointer to the rfkill struct will be passed as a parameter. The notifier message types have been added to include/linux/rfkill.h instead of to include/linux/notifier.h in order to avoid the madness of modifying a header used globally (and that triggers an almost full tree rebuild every time it is touched) with information that is of interest only to code that includes the rfkill.h header. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index fb566902030a..a561e350a70a 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -46,6 +46,49 @@ MODULE_PARM_DESC(default_state, 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); + +/** + * 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 void rfkill_led_trigger(struct rfkill *rfkill, enum rfkill_state state) @@ -62,14 +105,25 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void notify_rfkill_state_change(struct rfkill *rfkill) +{ + blocking_notifier_call_chain(&rfkill_notifier_list, + RFKILL_STATE_CHANGED, + rfkill); +} + static void update_rfkill_state(struct rfkill *rfkill) { - enum rfkill_state newstate; + enum rfkill_state newstate, oldstate; if (rfkill->get_state) { mutex_lock(&rfkill->mutex); - if (!rfkill->get_state(rfkill->data, &newstate)) + if (!rfkill->get_state(rfkill->data, &newstate)) { + oldstate = rfkill->state; rfkill->state = newstate; + if (oldstate != newstate) + notify_rfkill_state_change(rfkill); + } mutex_unlock(&rfkill->mutex); } } @@ -93,8 +147,10 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, rfkill->state = state; } - if (force || rfkill->state != oldstate) + if (force || rfkill->state != oldstate) { rfkill_led_trigger(rfkill, rfkill->state); + notify_rfkill_state_change(rfkill); + } return retval; } @@ -139,12 +195,20 @@ EXPORT_SYMBOL(rfkill_switch_all); */ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { + enum rfkill_state oldstate; + if (state != RFKILL_STATE_OFF && state != RFKILL_STATE_ON) return -EINVAL; mutex_lock(&rfkill->mutex); + + oldstate = rfkill->state; rfkill->state = state; + + if (state != oldstate) + notify_rfkill_state_change(rfkill); + mutex_unlock(&rfkill->mutex); return 0; -- cgit v1.2.2 From 99c632e5a304e1f76350eb9e8b2493514de8b60c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:04 -0300 Subject: rfkill: add type string helper We will need access to the rfkill switch type in string format for more than just sysfs. Therefore, move it to a generic helper. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index a561e350a70a..3c7773475ea6 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -224,34 +224,31 @@ static ssize_t rfkill_name_show(struct device *dev, return sprintf(buf, "%s\n", rfkill->name); } -static ssize_t rfkill_type_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static const char *rfkill_get_type_str(enum rfkill_type type) { - struct rfkill *rfkill = to_rfkill(dev); - const char *type; - - switch (rfkill->type) { + switch (type) { case RFKILL_TYPE_WLAN: - type = "wlan"; - break; + return "wlan"; case RFKILL_TYPE_BLUETOOTH: - type = "bluetooth"; - break; + return "bluetooth"; case RFKILL_TYPE_UWB: - type = "ultrawideband"; - break; + return "ultrawideband"; case RFKILL_TYPE_WIMAX: - type = "wimax"; - break; + return "wimax"; case RFKILL_TYPE_WWAN: - type = "wwan"; - break; + return "wwan"; default: BUG(); } +} + +static ssize_t rfkill_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rfkill *rfkill = to_rfkill(dev); - return sprintf(buf, "%s\n", type); + return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); } static ssize_t rfkill_state_show(struct device *dev, -- cgit v1.2.2 From ffb67c34e436fb163c4067936ccec797354fa6c6 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:05 -0300 Subject: rfkill: add uevent notifications Use the notification chains to also send uevents, so that userspace can be notified of state changes of every rfkill switch. Userspace should use these events for OSD/status report applications and rfkill GUI frontends. HAL might want to broadcast them over DBUS, for example. It might be also useful for userspace implementations of rfkill-input, or to use HAL as the platform driver which promotes rfkill switch change events into input events (to synchronize all other switches) when necessary for platforms that lack a convenient platform-specific kernel module to do it. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3c7773475ea6..dd1c3f18f31d 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -386,12 +386,51 @@ 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); + int error; + + error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); + if (error) + return error; + error = add_uevent_var(env, "RFKILL_TYPE=%s", + rfkill_get_type_str(rfkill->type)); + if (error) + return error; + error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state); + return error; +} + static struct class rfkill_class = { .name = "rfkill", .dev_release = rfkill_release, .dev_attrs = rfkill_dev_attrs, .suspend = rfkill_suspend, .resume = rfkill_resume, + .dev_uevent = rfkill_dev_uevent, }; static int rfkill_add_switch(struct rfkill *rfkill) @@ -566,11 +605,14 @@ 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); } -- cgit v1.2.2 From fbc6af2f3c46df4722f5161d0ad20dd87cd7dfa9 Mon Sep 17 00:00:00 2001 From: Fabien Crespel Date: Mon, 23 Jun 2008 17:23:06 -0300 Subject: rfkill: drop current_state from tasks in rfkill-input The whole current_state thing seems completely useless and a source of problems in rfkill-input, since state comparison is already done in rfkill, and rfkill-input is more than likely to become out of sync with the real state. Signed-off-by: Fabien Crespel Acked-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 29c13d308b31..d285f9a9d829 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -30,27 +30,15 @@ struct rfkill_task { spinlock_t lock; /* for accessing last and desired state */ unsigned long last; /* last schedule */ enum rfkill_state desired_state; /* on/off */ - enum rfkill_state current_state; /* on/off */ }; static void rfkill_task_handler(struct work_struct *work) { struct rfkill_task *task = container_of(work, struct rfkill_task, work); - enum rfkill_state state; mutex_lock(&task->mutex); - /* - * Use temp variable to fetch desired state to keep it - * consistent even if rfkill_schedule_toggle() runs in - * another thread or interrupts us. - */ - state = task->desired_state; - - if (state != task->current_state) { - rfkill_switch_all(task->type, state); - task->current_state = state; - } + rfkill_switch_all(task->type, task->desired_state); mutex_unlock(&task->mutex); } @@ -94,7 +82,6 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) .mutex = __MUTEX_INITIALIZER(n.mutex), \ .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ .desired_state = RFKILL_STATE_ON, \ - .current_state = RFKILL_STATE_ON, \ } static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); -- cgit v1.2.2 From 4081f00dc45abce6bdac352a6354c07ce15db45b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:07 -0300 Subject: rfkill: do not allow userspace to override ALL RADIOS OFF SW_RFKILL_ALL is the "emergency power-off all radios" input event. It must be handled, and must always do the same thing as far as the rfkill system is concerned: all transmitters are to go *immediately* offline. For safety, do NOT allow userspace to override EV_SW SW_RFKILL_ALL OFF. As long as rfkill-input is loaded, that event will *always* be processed, and it will *always* force all rfkill switches to disable all wireless transmitters, regardless of user_claim attribute or anything else. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 47 ++++++++++++++++++++++++++++++++--------------- net/rfkill/rfkill-input.h | 1 + net/rfkill/rfkill.c | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index d285f9a9d829..5d4c8b2446f7 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -43,11 +43,26 @@ static void rfkill_task_handler(struct work_struct *work) mutex_unlock(&task->mutex); } +static void rfkill_task_epo_handler(struct work_struct *work) +{ + rfkill_epo(); +} + +static DECLARE_WORK(epo_work, rfkill_task_epo_handler); + +static void rfkill_schedule_epo(void) +{ + schedule_work(&epo_work); +} + static void rfkill_schedule_set(struct rfkill_task *task, enum rfkill_state desired_state) { unsigned long flags; + if (unlikely(work_pending(&epo_work))) + return; + spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { @@ -63,6 +78,9 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) { unsigned long flags; + if (unlikely(work_pending(&epo_work))) + return; + spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { @@ -114,21 +132,20 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, switch (code) { case SW_RFKILL_ALL: /* EVERY radio type. data != 0 means radios ON */ - rfkill_schedule_set(&rfkill_wwan, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_wimax, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_uwb, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_bt, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_wlan, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); + /* handle EPO (emergency power off) through shortcut */ + if (data) { + rfkill_schedule_set(&rfkill_wwan, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_wimax, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_uwb, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_bt, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_wlan, + RFKILL_STATE_ON); + } else + rfkill_schedule_epo(); break; default: break; diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h index 4dae5006fc77..f63d05045685 100644 --- a/net/rfkill/rfkill-input.h +++ b/net/rfkill/rfkill-input.h @@ -12,5 +12,6 @@ #define __RFKILL_INPUT_H void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); +void rfkill_epo(void); #endif /* __RFKILL_INPUT_H */ diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index dd1c3f18f31d..7d07175c407f 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -181,6 +181,24 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_epo - emergency power off all transmitters + * + * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring + * everything in its path but rfkill_mutex. + */ +void rfkill_epo(void) +{ + struct rfkill *rfkill; + + mutex_lock(&rfkill_mutex); + list_for_each_entry(rfkill, &rfkill_list, node) { + rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + } + mutex_unlock(&rfkill_mutex); +} +EXPORT_SYMBOL_GPL(rfkill_epo); + /** * rfkill_force_state - Force the internal rfkill radio state * @rfkill: pointer to the rfkill class to modify. -- cgit v1.2.2 From 5005657cbd0fd6f277f807c0612a6b6d4396a02c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:46:42 -0300 Subject: rfkill: rename the rfkill_state states and add block-locked state The current naming of rfkill_state causes a lot of confusion: not only the "kill" in rfkill suggests negative logic, but also the fact that rfkill cannot turn anything on (it can just force something off or stop forcing something off) is often forgotten. Rename RFKILL_STATE_OFF to RFKILL_STATE_SOFT_BLOCKED (transmitter is blocked and will not operate; state can be changed by a toggle_radio request), and RFKILL_STATE_ON to RFKILL_STATE_UNBLOCKED (transmitter is not blocked, and may operate). Also, add a new third state, RFKILL_STATE_HARD_BLOCKED (transmitter is blocked and will not operate; state cannot be changed through a toggle_radio request), which is used by drivers to indicate a wireless transmiter was blocked by a hardware rfkill line that accepts no overrides. Keep the old names as #defines, but document them as deprecated. This way, drivers can be converted to the new names *and* verified to actually use rfkill correctly one by one. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 29 +++++++++--------- net/rfkill/rfkill.c | 75 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 28 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 5d4c8b2446f7..8aa822730145 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { - task->desired_state = !task->desired_state; + task->desired_state = + rfkill_state_complement(task->desired_state); task->last = jiffies; schedule_work(&task->work); } @@ -92,14 +93,14 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_unlock_irqrestore(&task->lock, flags); } -#define DEFINE_RFKILL_TASK(n, t) \ - struct rfkill_task n = { \ - .work = __WORK_INITIALIZER(n.work, \ - rfkill_task_handler), \ - .type = t, \ - .mutex = __MUTEX_INITIALIZER(n.mutex), \ - .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ - .desired_state = RFKILL_STATE_ON, \ +#define DEFINE_RFKILL_TASK(n, t) \ + struct rfkill_task n = { \ + .work = __WORK_INITIALIZER(n.work, \ + rfkill_task_handler), \ + .type = t, \ + .mutex = __MUTEX_INITIALIZER(n.mutex), \ + .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ + .desired_state = RFKILL_STATE_UNBLOCKED, \ } static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); @@ -135,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, /* handle EPO (emergency power off) through shortcut */ if (data) { rfkill_schedule_set(&rfkill_wwan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wimax, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_uwb, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_bt, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wlan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); } else rfkill_schedule_epo(); break; diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 7d07175c407f..ce0e23148cdd 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_mutex); -static unsigned int rfkill_default_state = RFKILL_STATE_ON; +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"); @@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill, if (!led->name) return; - if (state == RFKILL_STATE_OFF) + if (state != RFKILL_STATE_UNBLOCKED) led_trigger_event(led, LED_OFF); else led_trigger_event(led, LED_FULL); @@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill) } } +/** + * rfkill_toggle_radio - wrapper for toggle_radio hook + * calls toggle_radio taking into account a lot of "small" + * details. + * @rfkill: the rfkill struct to use + * @force: calls toggle_radio even if cache says it is not needed, + * and also makes sure notifications of the state will be + * sent even if it didn't change + * @state: the new state to call toggle_radio() with + * + * This wrappen protects and enforces the API for toggle_radio + * calls. Note that @force cannot override a (possibly cached) + * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of + * RFKILL_STATE_HARD_BLOCKED implements either get_state() or + * rfkill_force_state(), so the cache either is bypassed or valid. + * + * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED + * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to + * give the driver a hint that it should double-BLOCK the transmitter. + * + * Caller must have aquired rfkill_mutex. + */ static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state, int force) @@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, !rfkill->get_state(rfkill->data, &newstate)) rfkill->state = newstate; + switch (state) { + case RFKILL_STATE_HARD_BLOCKED: + /* typically happens when refreshing hardware state, + * such as on resume */ + state = RFKILL_STATE_SOFT_BLOCKED; + break; + case RFKILL_STATE_UNBLOCKED: + /* force can't override this, only rfkill_force_state() can */ + if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) + return -EPERM; + break; + case RFKILL_STATE_SOFT_BLOCKED: + /* nothing to do, we want to give drivers the hint to double + * BLOCK even a transmitter that is already in state + * RFKILL_STATE_HARD_BLOCKED */ + break; + } + if (force || state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) + /* never allow a HARD->SOFT downgrade! */ + if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) rfkill->state = state; } @@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all); /** * rfkill_epo - emergency power off all transmitters * - * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring + * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring * everything in its path but rfkill_mutex. */ void rfkill_epo(void) @@ -193,7 +234,7 @@ void rfkill_epo(void) mutex_lock(&rfkill_mutex); list_for_each_entry(rfkill, &rfkill_list, node) { - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); } mutex_unlock(&rfkill_mutex); } @@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { enum rfkill_state oldstate; - if (state != RFKILL_STATE_OFF && - state != RFKILL_STATE_ON) + if (state != RFKILL_STATE_SOFT_BLOCKED && + state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_HARD_BLOCKED) return -EINVAL; mutex_lock(&rfkill->mutex); @@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; + /* 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 ? RFKILL_STATE_ON : RFKILL_STATE_OFF, - 0); + error = rfkill_toggle_radio(rfkill, state, 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) update_rfkill_state(rfkill); mutex_lock(&rfkill->mutex); - rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); + rfkill->toggle_radio(rfkill->data, + RFKILL_STATE_SOFT_BLOCKED); mutex_unlock(&rfkill->mutex); } @@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); mutex_unlock(&rfkill_mutex); } @@ -610,8 +656,9 @@ static int __init rfkill_init(void) int error; int i; - if (rfkill_default_state != RFKILL_STATE_OFF && - rfkill_default_state != RFKILL_STATE_ON) + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && + rfkill_default_state != RFKILL_STATE_UNBLOCKED) return -EINVAL; for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) -- cgit v1.2.2 From 0f687e9aeb590e9581709379f47dd13ee9357258 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Thu, 3 Jul 2008 13:14:56 -0300 Subject: rfkill: some minor kernel-doc changes for rfkill_toggle_radio Improve rfkill_toggle_radio's kernel-doc header a bit. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index ce0e23148cdd..aa7039dfa19d 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -130,17 +130,19 @@ static void update_rfkill_state(struct rfkill *rfkill) /** * rfkill_toggle_radio - wrapper for toggle_radio hook - * calls toggle_radio taking into account a lot of "small" - * details. + * * @rfkill: the rfkill struct to use * @force: calls toggle_radio even if cache says it is not needed, * and also makes sure notifications of the state will be * sent even if it didn't change * @state: the new state to call toggle_radio() with * - * This wrappen protects and enforces the API for toggle_radio - * calls. Note that @force cannot override a (possibly cached) - * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of + * Calls rfkill->toggle_radio, enforcing the API for toggle_radio + * calls and handling all the red tape such as issuing notifications + * if the call is successful. + * + * Note that @force cannot override a (possibly cached) state of + * RFKILL_STATE_HARD_BLOCKED. Any device making use of * RFKILL_STATE_HARD_BLOCKED implements either get_state() or * rfkill_force_state(), so the cache either is bypassed or valid. * -- cgit v1.2.2 From fd4484af7c02b31bcb6090eeb0d85cf947719f2d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Thu, 3 Jul 2008 13:14:57 -0300 Subject: rfkill: ignore errors from rfkill_toggle_radio in rfkill_add_switch rfkill_add_switch() calls rfkill_toggle_radio() to set the state of a recently registered rfkill class to the current global state [for that rfkill->type]. The rfkill_toggle_radio() call is going to error out if the hardware is RFKILL_STATE_HARD_BLOCKED, and the global state is RFKILL_STATE_UNBLOCKED. That is a quite normal situation which I missed to account for. As things stand, the error return from rfkill_toggle_radio ends up causing rfkill_register to bail out with an error (de-registering the new switch in the process), which is Not Nice. Change rfkill_add_switch() to not return errors because of a failed call to rfkill_toggle_radio(). We can go back to returning errors again (if that's indeed the right thing to do) if we define the exact error codes the rfkill->toggle_radio callbacks are to return in each situation, so that we can ignore the right ones only. Bug reported by "kionez ". Signed-off-by: Henrique de Moraes Holschuh Cc: kionez Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'net/rfkill') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index aa7039dfa19d..7a560b785097 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -501,17 +501,15 @@ static struct class rfkill_class = { static int rfkill_add_switch(struct rfkill *rfkill) { - int error; - mutex_lock(&rfkill_mutex); - error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); - if (!error) - list_add_tail(&rfkill->node, &rfkill_list); + rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); + + list_add_tail(&rfkill->node, &rfkill_list); mutex_unlock(&rfkill_mutex); - return error; + return 0; } static void rfkill_remove_switch(struct rfkill *rfkill) -- cgit v1.2.2