diff options
Diffstat (limited to 'net/rfkill')
-rw-r--r-- | net/rfkill/rfkill-input.c | 122 | ||||
-rw-r--r-- | net/rfkill/rfkill-input.h | 1 | ||||
-rw-r--r-- | net/rfkill/rfkill.c | 377 |
3 files changed, 416 insertions, 84 deletions
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index e4b051dbed61..e5b69556bb5b 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -30,39 +30,62 @@ 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; + rfkill_switch_all(task->type, task->desired_state); - if (state != task->current_state) { - rfkill_switch_all(task->type, state); - task->current_state = state; + 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))) { + task->desired_state = desired_state; + task->last = jiffies; + schedule_work(&task->work); } - mutex_unlock(&task->mutex); + spin_unlock_irqrestore(&task->lock, flags); } 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))) { - task->desired_state = !task->desired_state; + task->desired_state = + rfkill_state_complement(task->desired_state); task->last = jiffies; schedule_work(&task->work); } @@ -70,26 +93,45 @@ 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, \ - .current_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); 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_schedule_evsw_rfkillall(int state) +{ + /* EVERY radio type. state != 0 means radios ON */ + /* handle EPO (emergency power off) through shortcut */ + if (state) { + rfkill_schedule_set(&rfkill_wwan, + RFKILL_STATE_UNBLOCKED); + rfkill_schedule_set(&rfkill_wimax, + RFKILL_STATE_UNBLOCKED); + rfkill_schedule_set(&rfkill_uwb, + RFKILL_STATE_UNBLOCKED); + rfkill_schedule_set(&rfkill_bt, + RFKILL_STATE_UNBLOCKED); + rfkill_schedule_set(&rfkill_wlan, + RFKILL_STATE_UNBLOCKED); + } else + rfkill_schedule_epo(); +} 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 +148,14 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, default: break; } + } else if (type == EV_SW) { + switch (code) { + case SW_RFKILL_ALL: + rfkill_schedule_evsw_rfkillall(data); + break; + default: + break; + } } } @@ -123,6 +173,7 @@ static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, handle->handler = handler; handle->name = "rfkill"; + /* causes rfkill_start() to be called */ error = input_register_handle(handle); if (error) goto err_free_handle; @@ -140,6 +191,23 @@ static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, return error; } +static void rfkill_start(struct input_handle *handle) +{ + /* Take event_lock to guard against configuration changes, we + * should be able to deal with concurrency with rfkill_event() + * just fine (which event_lock will also avoid). */ + spin_lock_irq(&handle->dev->event_lock); + + if (test_bit(EV_SW, handle->dev->evbit)) { + if (test_bit(SW_RFKILL_ALL, handle->dev->swbit)) + rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL, + handle->dev->sw)); + /* add resync for further EV_SW events here */ + } + + spin_unlock_irq(&handle->dev->event_lock); +} + static void rfkill_disconnect(struct input_handle *handle) { input_close_device(handle); @@ -168,6 +236,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) }, + }, { } }; @@ -175,6 +248,7 @@ static struct input_handler rfkill_handler = { .event = rfkill_event, .connect = rfkill_connect, .disconnect = rfkill_disconnect, + .start = rfkill_start, .name = "rfkill", .id_table = rfkill_ids, }; 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 4e10a95de832..d2d45655cd1a 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,8 +39,56 @@ 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_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); + +/** + * 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) @@ -50,24 +98,110 @@ 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); #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, + struct rfkill, led_trigger); + + rfkill_led_trigger(rfkill, rfkill->state); +} +#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, oldstate; + + if (rfkill->get_state) { + mutex_lock(&rfkill->mutex); + 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); + } +} + +/** + * rfkill_toggle_radio - wrapper for toggle_radio hook + * @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 + * + * 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 the @force parameter 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 acquired rfkill->mutex. + */ 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 (state != rfkill->state) { + if (rfkill->get_state && !force && + !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; - rfkill_led_trigger(rfkill, state); - } + } + + if (force || rfkill->state != oldstate) { + rfkill_led_trigger(rfkill, rfkill->state); + notify_rfkill_state_change(rfkill); } return retval; @@ -75,14 +209,13 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, /** * rfkill_switch_all - Toggle state of all switches of given type - * @type: type of interfaces to be affeceted + * @type: type of interfaces to be affected * @state: the new state * - * This function toggles state of all switches of given type unless - * a specific switch is claimed by userspace in which case it is - * left alone. + * 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). */ - void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) { struct rfkill *rfkill; @@ -92,14 +225,77 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) rfkill_states[type] = state; list_for_each_entry(rfkill, &rfkill_list, node) { - if ((!rfkill->user_claim) && (rfkill->type == type)) - rfkill_toggle_radio(rfkill, state); + if ((!rfkill->user_claim) && (rfkill->type == type)) { + mutex_lock(&rfkill->mutex); + rfkill_toggle_radio(rfkill, state, 0); + mutex_unlock(&rfkill->mutex); + } } mutex_unlock(&rfkill_mutex); } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_epo - emergency power off all transmitters + * + * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring + * everything in its path but rfkill_mutex and rfkill->mutex. + */ +void rfkill_epo(void) +{ + struct rfkill *rfkill; + + mutex_lock(&rfkill_mutex); + 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); +} +EXPORT_SYMBOL_GPL(rfkill_epo); + +/** + * 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. + * + * Devices which are subject to external changes on their rfkill + * state (such as those caused by a hardware rfkill line) MUST + * have their driver arrange to call rfkill_force_state() as soon + * as possible after such a change. + * + * This function may not be called from an atomic context. + */ +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) + 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; +} +EXPORT_SYMBOL(rfkill_force_state); + static ssize_t rfkill_name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -109,31 +305,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: + 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, @@ -142,6 +338,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); } @@ -156,10 +353,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); + error = rfkill_toggle_radio(rfkill, state, 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -185,6 +386,9 @@ static ssize_t rfkill_claim_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (rfkill->user_claim_unsupported) + return -EOPNOTSUPP; + /* * Take the global lock to make sure the kernel is not in * the middle of rfkill_switch_all @@ -193,18 +397,17 @@ static ssize_t rfkill_claim_store(struct device *dev, if (error) return error; - if (rfkill->user_claim_unsupported) { - error = -EOPNOTSUPP; - goto out_unlock; - } if (rfkill->user_claim != claim) { - if (!claim) + if (!claim) { + mutex_lock(&rfkill->mutex); rfkill_toggle_radio(rfkill, - rfkill_states[rfkill->type]); + rfkill_states[rfkill->type], + 0); + mutex_unlock(&rfkill->mutex); + } rfkill->user_claim = claim; } -out_unlock: mutex_unlock(&rfkill_mutex); return error ? error : count; @@ -233,12 +436,12 @@ 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_SOFT_BLOCKED); mutex_unlock(&rfkill->mutex); } @@ -255,8 +458,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); } @@ -269,35 +472,75 @@ 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) { - int error; - mutex_lock(&rfkill_mutex); - error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type]); - 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) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF); mutex_unlock(&rfkill_mutex); + + mutex_lock(&rfkill->mutex); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); + mutex_unlock(&rfkill->mutex); } /** @@ -306,9 +549,10 @@ static void rfkill_remove_switch(struct rfkill *rfkill) * @type: type of the switch (RFKILL_TYPE_*) * * This function should be called by the network driver when it needs - * rfkill structure. Once the structure is allocated the driver shoud - * finish its initialization by setting name, private data, enable_radio + * rfkill structure. Once the structure is allocated the driver should + * finish its initialization by setting the name, private data, enable_radio * and disable_radio methods and then register it with rfkill_register(). + * * NOTE: If registration fails the structure shoudl be freed by calling * rfkill_free() otherwise rfkill_unregister() should be used. */ @@ -340,7 +584,7 @@ EXPORT_SYMBOL(rfkill_allocate); * rfkill_free - Mark rfkill structure for deletion * @rfkill: rfkill structure to be destroyed * - * Decrements reference count of rfkill structure so it is destroyed. + * Decrements reference count of the rfkill structure so it is destroyed. * Note that rfkill_free() should _not_ be called after rfkill_unregister(). */ void rfkill_free(struct rfkill *rfkill) @@ -355,7 +599,10 @@ static void rfkill_led_trigger_register(struct rfkill *rfkill) #ifdef CONFIG_RFKILL_LEDS int error; - rfkill->led_trigger.name = rfkill->dev.bus_id; + if (!rfkill->led_trigger.name) + rfkill->led_trigger.name = rfkill->dev.bus_id; + if (!rfkill->led_trigger.activate) + rfkill->led_trigger.activate = rfkill_led_trigger_activate; error = led_trigger_register(&rfkill->led_trigger); if (error) rfkill->led_trigger.name = NULL; @@ -365,8 +612,10 @@ static void rfkill_led_trigger_register(struct rfkill *rfkill) static void rfkill_led_trigger_unregister(struct rfkill *rfkill) { #ifdef CONFIG_RFKILL_LEDS - if (rfkill->led_trigger.name) + if (rfkill->led_trigger.name) { led_trigger_unregister(&rfkill->led_trigger); + rfkill->led_trigger.name = NULL; + } #endif } @@ -402,8 +651,8 @@ int rfkill_register(struct rfkill *rfkill) error = device_add(dev); if (error) { - rfkill_led_trigger_unregister(rfkill); rfkill_remove_switch(rfkill); + rfkill_led_trigger_unregister(rfkill); return error; } @@ -412,7 +661,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 @@ -436,8 +685,13 @@ static int __init rfkill_init(void) int error; int i; + /* 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++) - rfkill_states[i] = RFKILL_STATE_ON; + rfkill_states[i] = rfkill_default_state; error = class_register(&rfkill_class); if (error) { @@ -445,11 +699,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); } |