Commit 79399a8d authored by Henrique de Moraes Holschuh's avatar Henrique de Moraes Holschuh Committed by John W. Linville

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: default avatarHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Acked-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 526324b6
...@@ -117,4 +117,11 @@ static inline char *rfkill_get_led_name(struct rfkill *rfkill) ...@@ -117,4 +117,11 @@ static inline char *rfkill_get_led_name(struct rfkill *rfkill)
#endif #endif
} }
/* rfkill notification chain */
#define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill
switch has changed */
int register_rfkill_notifier(struct notifier_block *nb);
int unregister_rfkill_notifier(struct notifier_block *nb);
#endif /* RFKILL_H */ #endif /* RFKILL_H */
...@@ -46,6 +46,49 @@ MODULE_PARM_DESC(default_state, ...@@ -46,6 +46,49 @@ MODULE_PARM_DESC(default_state,
static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; 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, static void rfkill_led_trigger(struct rfkill *rfkill,
enum rfkill_state state) enum rfkill_state state)
...@@ -62,14 +105,25 @@ static void rfkill_led_trigger(struct rfkill *rfkill, ...@@ -62,14 +105,25 @@ static void rfkill_led_trigger(struct rfkill *rfkill,
#endif /* CONFIG_RFKILL_LEDS */ #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) static void update_rfkill_state(struct rfkill *rfkill)
{ {
enum rfkill_state newstate; enum rfkill_state newstate, oldstate;
if (rfkill->get_state) { if (rfkill->get_state) {
mutex_lock(&rfkill->mutex); mutex_lock(&rfkill->mutex);
if (!rfkill->get_state(rfkill->data, &newstate)) if (!rfkill->get_state(rfkill->data, &newstate)) {
oldstate = rfkill->state;
rfkill->state = newstate; rfkill->state = newstate;
if (oldstate != newstate)
notify_rfkill_state_change(rfkill);
}
mutex_unlock(&rfkill->mutex); mutex_unlock(&rfkill->mutex);
} }
} }
...@@ -93,8 +147,10 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, ...@@ -93,8 +147,10 @@ static int rfkill_toggle_radio(struct rfkill *rfkill,
rfkill->state = state; rfkill->state = state;
} }
if (force || rfkill->state != oldstate) if (force || rfkill->state != oldstate) {
rfkill_led_trigger(rfkill, rfkill->state); rfkill_led_trigger(rfkill, rfkill->state);
notify_rfkill_state_change(rfkill);
}
return retval; return retval;
} }
...@@ -139,12 +195,20 @@ EXPORT_SYMBOL(rfkill_switch_all); ...@@ -139,12 +195,20 @@ EXPORT_SYMBOL(rfkill_switch_all);
*/ */
int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state)
{ {
enum rfkill_state oldstate;
if (state != RFKILL_STATE_OFF && if (state != RFKILL_STATE_OFF &&
state != RFKILL_STATE_ON) state != RFKILL_STATE_ON)
return -EINVAL; return -EINVAL;
mutex_lock(&rfkill->mutex); mutex_lock(&rfkill->mutex);
oldstate = rfkill->state;
rfkill->state = state; rfkill->state = state;
if (state != oldstate)
notify_rfkill_state_change(rfkill);
mutex_unlock(&rfkill->mutex); mutex_unlock(&rfkill->mutex);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment