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