rfkill: Add rfkill-any LED trigger
authorMichał Kępień <kernel@kempniu.pl>
Thu, 8 Dec 2016 07:30:52 +0000 (08:30 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 13 Dec 2016 15:20:29 +0000 (16:20 +0100)
Add a new "global" (i.e. not per-rfkill device) LED trigger, rfkill-any,
which may be useful on laptops with a single "radio LED" and multiple
radio transmitters.  The trigger is meant to turn a LED on whenever
there is at least one radio transmitter active and turn it off
otherwise.

This requires taking rfkill_global_mutex before calling rfkill_set_block()
in rfkill_resume(): since __rfkill_any_led_trigger_event() is called from
rfkill_set_block() unconditionally, each caller of the latter needs to
take care of locking rfkill_global_mutex.

Signed-off-by: Michał Kępień <kernel@kempniu.pl>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/rfkill/core.c

index 9eb26a4cdac8a690912b6cb637b902888258834c..f6b01f3616e5a07ebc709e4cb4cfc2262db9802f 100644 (file)
@@ -176,6 +176,47 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 {
        led_trigger_unregister(&rfkill->led_trigger);
 }
+
+static struct led_trigger rfkill_any_led_trigger;
+
+static void __rfkill_any_led_trigger_event(void)
+{
+       enum led_brightness brightness = LED_OFF;
+       struct rfkill *rfkill;
+
+       list_for_each_entry(rfkill, &rfkill_list, node) {
+               if (!(rfkill->state & RFKILL_BLOCK_ANY)) {
+                       brightness = LED_FULL;
+                       break;
+               }
+       }
+
+       led_trigger_event(&rfkill_any_led_trigger, brightness);
+}
+
+static void rfkill_any_led_trigger_event(void)
+{
+       mutex_lock(&rfkill_global_mutex);
+       __rfkill_any_led_trigger_event();
+       mutex_unlock(&rfkill_global_mutex);
+}
+
+static void rfkill_any_led_trigger_activate(struct led_classdev *led_cdev)
+{
+       rfkill_any_led_trigger_event();
+}
+
+static int rfkill_any_led_trigger_register(void)
+{
+       rfkill_any_led_trigger.name = "rfkill-any";
+       rfkill_any_led_trigger.activate = rfkill_any_led_trigger_activate;
+       return led_trigger_register(&rfkill_any_led_trigger);
+}
+
+static void rfkill_any_led_trigger_unregister(void)
+{
+       led_trigger_unregister(&rfkill_any_led_trigger);
+}
 #else
 static void rfkill_led_trigger_event(struct rfkill *rfkill)
 {
@@ -189,6 +230,23 @@ static inline int rfkill_led_trigger_register(struct rfkill *rfkill)
 static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 {
 }
+
+static void __rfkill_any_led_trigger_event(void)
+{
+}
+
+static void rfkill_any_led_trigger_event(void)
+{
+}
+
+static int rfkill_any_led_trigger_register(void)
+{
+       return 0;
+}
+
+static void rfkill_any_led_trigger_unregister(void)
+{
+}
 #endif /* CONFIG_RFKILL_LEDS */
 
 static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill,
@@ -297,6 +355,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
+       __rfkill_any_led_trigger_event();
 
        if (prev != curr)
                rfkill_event(rfkill);
@@ -477,6 +536,7 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
+       rfkill_any_led_trigger_event();
 
        if (rfkill->registered && prev != blocked)
                schedule_work(&rfkill->uevent_work);
@@ -520,6 +580,7 @@ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
                schedule_work(&rfkill->uevent_work);
 
        rfkill_led_trigger_event(rfkill);
+       rfkill_any_led_trigger_event();
 
        return blocked;
 }
@@ -569,6 +630,7 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
                        schedule_work(&rfkill->uevent_work);
 
                rfkill_led_trigger_event(rfkill);
+               rfkill_any_led_trigger_event();
        }
 }
 EXPORT_SYMBOL(rfkill_set_states);
@@ -812,8 +874,10 @@ static int rfkill_resume(struct device *dev)
        rfkill->suspended = false;
 
        if (!rfkill->persistent) {
+               mutex_lock(&rfkill_global_mutex);
                cur = !!(rfkill->state & RFKILL_BLOCK_SW);
                rfkill_set_block(rfkill, cur);
+               mutex_unlock(&rfkill_global_mutex);
        }
 
        if (rfkill->ops->poll && !rfkill->polling_paused)
@@ -985,6 +1049,7 @@ int __must_check rfkill_register(struct rfkill *rfkill)
 #endif
        }
 
+       __rfkill_any_led_trigger_event();
        rfkill_send_events(rfkill, RFKILL_OP_ADD);
 
        mutex_unlock(&rfkill_global_mutex);
@@ -1017,6 +1082,7 @@ void rfkill_unregister(struct rfkill *rfkill)
        mutex_lock(&rfkill_global_mutex);
        rfkill_send_events(rfkill, RFKILL_OP_DEL);
        list_del_init(&rfkill->node);
+       __rfkill_any_led_trigger_event();
        mutex_unlock(&rfkill_global_mutex);
 
        rfkill_led_trigger_unregister(rfkill);
@@ -1269,6 +1335,10 @@ static int __init rfkill_init(void)
        if (error)
                goto error_misc;
 
+       error = rfkill_any_led_trigger_register();
+       if (error)
+               goto error_led_trigger;
+
 #ifdef CONFIG_RFKILL_INPUT
        error = rfkill_handler_init();
        if (error)
@@ -1278,6 +1348,8 @@ static int __init rfkill_init(void)
        return 0;
 
 error_input:
+       rfkill_any_led_trigger_unregister();
+error_led_trigger:
        misc_deregister(&rfkill_miscdev);
 error_misc:
        class_unregister(&rfkill_class);
@@ -1291,6 +1363,7 @@ static void __exit rfkill_exit(void)
 #ifdef CONFIG_RFKILL_INPUT
        rfkill_handler_exit();
 #endif
+       rfkill_any_led_trigger_unregister();
        misc_deregister(&rfkill_miscdev);
        class_unregister(&rfkill_class);
 }