rfkill: Add rfkill-any LED trigger
authorMichał Kępień <kernel@kempniu.pl>
Fri, 6 Jan 2017 06:07:47 +0000 (07:07 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 9 Jan 2017 10:40:33 +0000 (11:40 +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.

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

index afa4f71..2064c3a 100644 (file)
@@ -176,6 +176,50 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 {
        led_trigger_unregister(&rfkill->led_trigger);
 }
+
+static struct led_trigger rfkill_any_led_trigger;
+static struct work_struct rfkill_any_work;
+
+static void rfkill_any_led_trigger_worker(struct work_struct *work)
+{
+       enum led_brightness brightness = LED_OFF;
+       struct rfkill *rfkill;
+
+       mutex_lock(&rfkill_global_mutex);
+       list_for_each_entry(rfkill, &rfkill_list, node) {
+               if (!(rfkill->state & RFKILL_BLOCK_ANY)) {
+                       brightness = LED_FULL;
+                       break;
+               }
+       }
+       mutex_unlock(&rfkill_global_mutex);
+
+       led_trigger_event(&rfkill_any_led_trigger, brightness);
+}
+
+static void rfkill_any_led_trigger_event(void)
+{
+       schedule_work(&rfkill_any_work);
+}
+
+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)
+{
+       INIT_WORK(&rfkill_any_work, rfkill_any_led_trigger_worker);
+       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);
+       cancel_work_sync(&rfkill_any_work);
+}
 #else
 static void rfkill_led_trigger_event(struct rfkill *rfkill)
 {
@@ -189,6 +233,19 @@ 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 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 +354,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 +535,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 +579,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 +629,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);
@@ -985,6 +1046,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 +1079,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 +1332,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)
@@ -1279,8 +1346,10 @@ static int __init rfkill_init(void)
 
 #ifdef CONFIG_RFKILL_INPUT
 error_input:
-       misc_deregister(&rfkill_miscdev);
+       rfkill_any_led_trigger_unregister();
 #endif
+error_led_trigger:
+       misc_deregister(&rfkill_miscdev);
 error_misc:
        class_unregister(&rfkill_class);
 error_class:
@@ -1293,6 +1362,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);
 }