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        struct led_classdev *led_cdev;
 263        unsigned long on, off, tpt;
 264        int i;
 265
 266        if (!tpt_trig->running)
 267                return;
 268
 269        mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
 270
 271        tpt = tpt_trig_traffic(local, tpt_trig);
 272
 273        /* default to just solid on */
 274        on = 1;
 275        off = 0;
 276
 277        for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
 278                if (tpt_trig->blink_table[i].throughput < 0 ||
 279                    tpt > tpt_trig->blink_table[i].throughput) {
 280                        off = tpt_trig->blink_table[i].blink_time / 2;
 281                        on = tpt_trig->blink_table[i].blink_time - off;
 282                        break;
 283                }
 284        }
 285
 286        read_lock(&local->tpt_led.leddev_list_lock);
 287        list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
 288                led_blink_set(led_cdev, &on, &off);
 289        read_unlock(&local->tpt_led.leddev_list_lock);
 290}
 291
 292const char *
 293__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
 294                                   unsigned int flags,
 295                                   const struct ieee80211_tpt_blink *blink_table,
 296                                   unsigned int blink_table_len)
 297{
 298        struct ieee80211_local *local = hw_to_local(hw);
 299        struct tpt_led_trigger *tpt_trig;
 300
 301        if (WARN_ON(local->tpt_led_trigger))
 302                return NULL;
 303
 304        tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
 305        if (!tpt_trig)
 306                return NULL;
 307
 308        snprintf(tpt_trig->name, sizeof(tpt_trig->name),
 309                 "%stpt", wiphy_name(local->hw.wiphy));
 310
 311        local->tpt_led.name = tpt_trig->name;
 312
 313        tpt_trig->blink_table = blink_table;
 314        tpt_trig->blink_table_len = blink_table_len;
 315        tpt_trig->want = flags;
 316        tpt_trig->local = local;
 317
 318        timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
 319
 320        local->tpt_led_trigger = tpt_trig;
 321
 322        return tpt_trig->name;
 323}
 324EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
 325
 326static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
 327{
 328        struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
 329
 330        if (tpt_trig->running)
 331                return;
 332
 333        /* reset traffic */
 334        tpt_trig_traffic(local, tpt_trig);
 335        tpt_trig->running = true;
 336
 337        tpt_trig_timer(&tpt_trig->timer);
 338        mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
 339}
 340
 341static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
 342{
 343        struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
 344        struct led_classdev *led_cdev;
 345
 346        if (!tpt_trig->running)
 347                return;
 348
 349        tpt_trig->running = false;
 350        del_timer_sync(&tpt_trig->timer);
 351
 352        read_lock(&local->tpt_led.leddev_list_lock);
 353        list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
 354                led_set_brightness(led_cdev, LED_OFF);
 355        read_unlock(&local->tpt_led.leddev_list_lock);
 356}
 357
 358void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
 359                                unsigned int types_on, unsigned int types_off)
 360{
 361        struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
 362        bool allowed;
 363
 364        WARN_ON(types_on & types_off);
 365
 366        if (!tpt_trig)
 367                return;
 368
 369        tpt_trig->active &= ~types_off;
 370        tpt_trig->active |= types_on;
 371
 372        /*
 373         * Regardless of wanted state, we shouldn't blink when
 374         * the radio is disabled -- this can happen due to some
 375         * code ordering issues with __ieee80211_recalc_idle()
 376         * being called before the radio is started.
 377         */
 378        allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
 379
 380        if (!allowed || !(tpt_trig->active & tpt_trig->want))
 381                ieee80211_stop_tpt_led_trig(local);
 382        else
 383                ieee80211_start_tpt_led_trig(local);
 384}
 385