linux/net/mac80211/led.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright 2006, Johannes Berg <johannes@sipsolutions.net>
   4 */
   5
   6/* just for IFNAMSIZ */
   7#include <linux/if.h>
   8#include <linux/slab.h>
   9#include <linux/export.h>
  10#include "led.h"
  11
  12void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
  13{
  14        if (!atomic_read(&local->assoc_led_active))
  15                return;
  16        if (associated)
  17                led_trigger_event(&local->assoc_led, LED_FULL);
  18        else
  19                led_trigger_event(&local->assoc_led, LED_OFF);
  20}
  21
  22void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
  23{
  24        if (!atomic_read(&local->radio_led_active))
  25                return;
  26        if (enabled)
  27                led_trigger_event(&local->radio_led, LED_FULL);
  28        else
  29                led_trigger_event(&local->radio_led, LED_OFF);
  30}
  31
  32void ieee80211_alloc_led_names(struct ieee80211_local *local)
  33{
  34        local->rx_led.name = kasprintf(GFP_KERNEL, "%srx",
  35                                       wiphy_name(local->hw.wiphy));
  36        local->tx_led.name = kasprintf(GFP_KERNEL, "%stx",
  37                                       wiphy_name(local->hw.wiphy));
  38        local->assoc_led.name = kasprintf(GFP_KERNEL, "%sassoc",
  39                                          wiphy_name(local->hw.wiphy));
  40        local->radio_led.name = kasprintf(GFP_KERNEL, "%sradio",
  41                                          wiphy_name(local->hw.wiphy));
  42}
  43
  44void ieee80211_free_led_names(struct ieee80211_local *local)
  45{
  46        kfree(local->rx_led.name);
  47        kfree(local->tx_led.name);
  48        kfree(local->assoc_led.name);
  49        kfree(local->radio_led.name);
  50}
  51
  52static int ieee80211_tx_led_activate(struct led_classdev *led_cdev)
  53{
  54        struct ieee80211_local *local = container_of(led_cdev->trigger,
  55                                                     struct ieee80211_local,
  56                                                     tx_led);
  57
  58        atomic_inc(&local->tx_led_active);
  59
  60        return 0;
  61}
  62
  63static void ieee80211_tx_led_deactivate(struct led_classdev *led_cdev)
  64{
  65        struct ieee80211_local *local = container_of(led_cdev->trigger,
  66                                                     struct ieee80211_local,
  67                                                     tx_led);
  68
  69        atomic_dec(&local->tx_led_active);
  70}
  71
  72static int ieee80211_rx_led_activate(struct led_classdev *led_cdev)
  73{
  74        struct ieee80211_local *local = container_of(led_cdev->trigger,
  75                                                     struct ieee80211_local,
  76                                                     rx_led);
  77
  78        atomic_inc(&local->rx_led_active);
  79
  80        return 0;
  81}
  82
  83static void ieee80211_rx_led_deactivate(struct led_classdev *led_cdev)
  84{
  85        struct ieee80211_local *local = container_of(led_cdev->trigger,
  86                                                     struct ieee80211_local,
  87                                                     rx_led);
  88
  89        atomic_dec(&local->rx_led_active);
  90}
  91
  92static int ieee80211_assoc_led_activate(struct led_classdev *led_cdev)
  93{
  94        struct ieee80211_local *local = container_of(led_cdev->trigger,
  95                                                     struct ieee80211_local,
  96                                                     assoc_led);
  97
  98        atomic_inc(&local->assoc_led_active);
  99
 100        return 0;
 101}
 102
 103static void ieee80211_assoc_led_deactivate(struct led_classdev *led_cdev)
 104{
 105        struct ieee80211_local *local = container_of(led_cdev->trigger,
 106                                                     struct ieee80211_local,
 107                                                     assoc_led);
 108
 109        atomic_dec(&local->assoc_led_active);
 110}
 111
 112static int ieee80211_radio_led_activate(struct led_classdev *led_cdev)
 113{
 114        struct ieee80211_local *local = container_of(led_cdev->trigger,
 115                                                     struct ieee80211_local,
 116                                                     radio_led);
 117
 118        atomic_inc(&local->radio_led_active);
 119
 120        return 0;
 121}
 122
 123static void ieee80211_radio_led_deactivate(struct led_classdev *led_cdev)
 124{
 125        struct ieee80211_local *local = container_of(led_cdev->trigger,
 126                                                     struct ieee80211_local,
 127                                                     radio_led);
 128
 129        atomic_dec(&local->radio_led_active);
 130}
 131
 132static int ieee80211_tpt_led_activate(struct led_classdev *led_cdev)
 133{
 134        struct ieee80211_local *local = container_of(led_cdev->trigger,
 135                                                     struct ieee80211_local,
 136                                                     tpt_led);
 137
 138        atomic_inc(&local->tpt_led_active);
 139
 140        return 0;
 141}
 142
 143static void ieee80211_tpt_led_deactivate(struct led_classdev *led_cdev)
 144{
 145        struct ieee80211_local *local = container_of(led_cdev->trigger,
 146                                                     struct ieee80211_local,
 147                                                     tpt_led);
 148
 149        atomic_dec(&local->tpt_led_active);
 150}
 151
 152void ieee80211_led_init(struct ieee80211_local *local)
 153{
 154        atomic_set(&local->rx_led_active, 0);
 155        local->rx_led.activate = ieee80211_rx_led_activate;
 156        local->rx_led.deactivate = ieee80211_rx_led_deactivate;
 157        if (local->rx_led.name && led_trigger_register(&local->rx_led)) {
 158                kfree(local->rx_led.name);
 159                local->rx_led.name = NULL;
 160        }
 161
 162        atomic_set(&local->tx_led_active, 0);
 163        local->tx_led.activate = ieee80211_tx_led_activate;
 164        local->tx_led.deactivate = ieee80211_tx_led_deactivate;
 165        if (local->tx_led.name && led_trigger_register(&local->tx_led)) {
 166                kfree(local->tx_led.name);
 167                local->tx_led.name = NULL;
 168        }
 169
 170        atomic_set(&local->assoc_led_active, 0);
 171        local->assoc_led.activate = ieee80211_assoc_led_activate;
 172        local->assoc_led.deactivate = ieee80211_assoc_led_deactivate;
 173        if (local->assoc_led.name && led_trigger_register(&local->assoc_led)) {
 174                kfree(local->assoc_led.name);
 175                local->assoc_led.name = NULL;
 176        }
 177
 178        atomic_set(&local->radio_led_active, 0);
 179        local->radio_led.activate = ieee80211_radio_led_activate;
 180        local->radio_led.deactivate = ieee80211_radio_led_deactivate;
 181        if (local->radio_led.name && led_trigger_register(&local->radio_led)) {
 182                kfree(local->radio_led.name);
 183                local->radio_led.name = NULL;
 184        }
 185
 186        atomic_set(&local->tpt_led_active, 0);
 187        if (local->tpt_led_trigger) {
 188                local->tpt_led.activate = ieee80211_tpt_led_activate;
 189                local->tpt_led.deactivate = ieee80211_tpt_led_deactivate;
 190                if (led_trigger_register(&local->tpt_led)) {
 191                        kfree(local->tpt_led_trigger);
 192                        local->tpt_led_trigger = NULL;
 193                }
 194        }
 195}
 196
 197void ieee80211_led_exit(struct ieee80211_local *local)
 198{
 199        if (local->radio_led.name)
 200                led_trigger_unregister(&local->radio_led);
 201        if (local->assoc_led.name)
 202                led_trigger_unregister(&local->assoc_led);
 203        if (local->tx_led.name)
 204                led_trigger_unregister(&local->tx_led);
 205        if (local->rx_led.name)
 206                led_trigger_unregister(&local->rx_led);
 207
 208        if (local->tpt_led_trigger) {
 209                led_trigger_unregister(&local->tpt_led);
 210                kfree(local->tpt_led_trigger);
 211        }
 212}
 213
 214const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
 215{
 216        struct ieee80211_local *local = hw_to_local(hw);
 217
 218        return local->radio_led.name;
 219}
 220EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
 221
 222const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
 223{
 224        struct ieee80211_local *local = hw_to_local(hw);
 225
 226        return local->assoc_led.name;
 227}
 228EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
 229
 230const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
 231{
 232        struct ieee80211_local *local = hw_to_local(hw);
 233
 234        return local->tx_led.name;
 235}
 236EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
 237
 238const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
 239{
 240        struct ieee80211_local *local = hw_to_local(hw);
 241
 242        return local->rx_led.name;
 243}
 244EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
 245
 246static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
 247                                      struct tpt_led_trigger *tpt_trig)
 248{
 249        unsigned long traffic, delta;
 250
 251        traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
 252
 253        delta = traffic - tpt_trig->prev_traffic;
 254        tpt_trig->prev_traffic = traffic;
 255        return DIV_ROUND_UP(delta, 1024 / 8);
 256}
 257
 258static void tpt_trig_timer(struct timer_list *t)
 259{
 260        struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer);
 261        struct ieee80211_local *local = tpt_trig->local;
 262        unsigned long on, off, tpt;
 263        int i;
 264
 265        if (!tpt_trig->running)
 266                return;
 267
 268        mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
 269
 270        tpt = tpt_trig_traffic(local, tpt_trig);
 271
 272        /* default to just solid on */
 273        on = 1;
 274        off = 0;
 275
 276        for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
 277                if (tpt_trig->blink_table[i].throughput < 0 ||
 278                    tpt > tpt_trig->blink_table[i].throughput) {
 279                        off = tpt_trig->blink_table[i].blink_time / 2;
 280                        on = tpt_trig->blink_table[i].blink_time - off;
 281                        break;
 282                }
 283        }
 284
 285        led_trigger_blink(&local->tpt_led, &on, &off);
 286}
 287
 288const char *
 289__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
 290                                   unsigned int flags,
 291                                   const struct ieee80211_tpt_blink *blink_table,
 292                                   unsigned int blink_table_len)
 293{
 294        struct ieee80211_local *local = hw_to_local(hw);
 295        struct tpt_led_trigger *tpt_trig;
 296
 297        if (WARN_ON(local->tpt_led_trigger))
 298                return NULL;
 299
 300        tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
 301        if (!tpt_trig)
 302                return NULL;
 303
 304        snprintf(tpt_trig->name, sizeof(tpt_trig->name),
 305                 "%stpt", wiphy_name(local->hw.wiphy));
 306
 307        local->tpt_led.name = tpt_trig->name;
 308
 309        tpt_trig->blink_table = blink_table;
 310        tpt_trig->blink_table_len = blink_table_len;
 311        tpt_trig->want = flags;
 312        tpt_trig->local = local;
 313
 314        timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
 315
 316        local->tpt_led_trigger = tpt_trig;
 317
 318        return tpt_trig->name;
 319}
 320EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
 321
 322static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
 323{
 324        struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
 325
 326        if (tpt_trig->running)
 327                return;
 328
 329        /* reset traffic */
 330        tpt_trig_traffic(local, tpt_trig);
 331        tpt_trig->running = true;
 332
 333        tpt_trig_timer(&tpt_trig->timer);
 334        mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
 335}
 336
 337static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
 338{
 339        struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
 340
 341        if (!tpt_trig->running)
 342                return;
 343
 344        tpt_trig->running = false;
 345        del_timer_sync(&tpt_trig->timer);
 346
 347        led_trigger_event(&local->tpt_led, LED_OFF);
 348}
 349
 350void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
 351                                unsigned int types_on, unsigned int types_off)
 352{
 353        struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
 354        bool allowed;
 355
 356        WARN_ON(types_on & types_off);
 357
 358        if (!tpt_trig)
 359                return;
 360
 361        tpt_trig->active &= ~types_off;
 362        tpt_trig->active |= types_on;
 363
 364        /*
 365         * Regardless of wanted state, we shouldn't blink when
 366         * the radio is disabled -- this can happen due to some
 367         * code ordering issues with __ieee80211_recalc_idle()
 368         * being called before the radio is started.
 369         */
 370        allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
 371
 372        if (!allowed || !(tpt_trig->active & tpt_trig->want))
 373                ieee80211_stop_tpt_led_trig(local);
 374        else
 375                ieee80211_start_tpt_led_trig(local);
 376}
 377