linux/drivers/leds/trigger/ledtrig-netdev.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright 2017 Ben Whitten <ben.whitten@gmail.com>
   3// Copyright 2007 Oliver Jowett <oliver@opencloud.com>
   4//
   5// LED Kernel Netdev Trigger
   6//
   7// Toggles the LED to reflect the link and traffic state of a named net device
   8//
   9// Derived from ledtrig-timer.c which is:
  10//  Copyright 2005-2006 Openedhand Ltd.
  11//  Author: Richard Purdie <rpurdie@openedhand.com>
  12
  13#include <linux/atomic.h>
  14#include <linux/ctype.h>
  15#include <linux/device.h>
  16#include <linux/init.h>
  17#include <linux/jiffies.h>
  18#include <linux/kernel.h>
  19#include <linux/leds.h>
  20#include <linux/list.h>
  21#include <linux/module.h>
  22#include <linux/netdevice.h>
  23#include <linux/spinlock.h>
  24#include <linux/timer.h>
  25#include "../leds.h"
  26
  27/*
  28 * Configurable sysfs attributes:
  29 *
  30 * device_name - network device name to monitor
  31 * interval - duration of LED blink, in milliseconds
  32 * link -  LED's normal state reflects whether the link is up
  33 *         (has carrier) or not
  34 * tx -  LED blinks on transmitted data
  35 * rx -  LED blinks on receive data
  36 *
  37 */
  38
  39struct led_netdev_data {
  40        spinlock_t lock;
  41
  42        struct delayed_work work;
  43        struct notifier_block notifier;
  44
  45        struct led_classdev *led_cdev;
  46        struct net_device *net_dev;
  47
  48        char device_name[IFNAMSIZ];
  49        atomic_t interval;
  50        unsigned int last_activity;
  51
  52        unsigned long mode;
  53#define NETDEV_LED_LINK 0
  54#define NETDEV_LED_TX   1
  55#define NETDEV_LED_RX   2
  56#define NETDEV_LED_MODE_LINKUP  3
  57};
  58
  59enum netdev_led_attr {
  60        NETDEV_ATTR_LINK,
  61        NETDEV_ATTR_TX,
  62        NETDEV_ATTR_RX
  63};
  64
  65static void set_baseline_state(struct led_netdev_data *trigger_data)
  66{
  67        int current_brightness;
  68        struct led_classdev *led_cdev = trigger_data->led_cdev;
  69
  70        current_brightness = led_cdev->brightness;
  71        if (current_brightness)
  72                led_cdev->blink_brightness = current_brightness;
  73        if (!led_cdev->blink_brightness)
  74                led_cdev->blink_brightness = led_cdev->max_brightness;
  75
  76        if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode))
  77                led_set_brightness(led_cdev, LED_OFF);
  78        else {
  79                if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
  80                        led_set_brightness(led_cdev,
  81                                           led_cdev->blink_brightness);
  82                else
  83                        led_set_brightness(led_cdev, LED_OFF);
  84
  85                /* If we are looking for RX/TX start periodically
  86                 * checking stats
  87                 */
  88                if (test_bit(NETDEV_LED_TX, &trigger_data->mode) ||
  89                    test_bit(NETDEV_LED_RX, &trigger_data->mode))
  90                        schedule_delayed_work(&trigger_data->work, 0);
  91        }
  92}
  93
  94static ssize_t device_name_show(struct device *dev,
  95                                struct device_attribute *attr, char *buf)
  96{
  97        struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
  98        ssize_t len;
  99
 100        spin_lock_bh(&trigger_data->lock);
 101        len = sprintf(buf, "%s\n", trigger_data->device_name);
 102        spin_unlock_bh(&trigger_data->lock);
 103
 104        return len;
 105}
 106
 107static ssize_t device_name_store(struct device *dev,
 108                                 struct device_attribute *attr, const char *buf,
 109                                 size_t size)
 110{
 111        struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 112
 113        if (size >= IFNAMSIZ)
 114                return -EINVAL;
 115
 116        cancel_delayed_work_sync(&trigger_data->work);
 117
 118        spin_lock_bh(&trigger_data->lock);
 119
 120        if (trigger_data->net_dev) {
 121                dev_put(trigger_data->net_dev);
 122                trigger_data->net_dev = NULL;
 123        }
 124
 125        memcpy(trigger_data->device_name, buf, size);
 126        trigger_data->device_name[size] = 0;
 127        if (size > 0 && trigger_data->device_name[size - 1] == '\n')
 128                trigger_data->device_name[size - 1] = 0;
 129
 130        if (trigger_data->device_name[0] != 0)
 131                trigger_data->net_dev =
 132                    dev_get_by_name(&init_net, trigger_data->device_name);
 133
 134        clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
 135        if (trigger_data->net_dev != NULL)
 136                if (netif_carrier_ok(trigger_data->net_dev))
 137                        set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
 138
 139        trigger_data->last_activity = 0;
 140
 141        set_baseline_state(trigger_data);
 142        spin_unlock_bh(&trigger_data->lock);
 143
 144        return size;
 145}
 146
 147static DEVICE_ATTR_RW(device_name);
 148
 149static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
 150        enum netdev_led_attr attr)
 151{
 152        struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 153        int bit;
 154
 155        switch (attr) {
 156        case NETDEV_ATTR_LINK:
 157                bit = NETDEV_LED_LINK;
 158                break;
 159        case NETDEV_ATTR_TX:
 160                bit = NETDEV_LED_TX;
 161                break;
 162        case NETDEV_ATTR_RX:
 163                bit = NETDEV_LED_RX;
 164                break;
 165        default:
 166                return -EINVAL;
 167        }
 168
 169        return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
 170}
 171
 172static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
 173        size_t size, enum netdev_led_attr attr)
 174{
 175        struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 176        unsigned long state;
 177        int ret;
 178        int bit;
 179
 180        ret = kstrtoul(buf, 0, &state);
 181        if (ret)
 182                return ret;
 183
 184        switch (attr) {
 185        case NETDEV_ATTR_LINK:
 186                bit = NETDEV_LED_LINK;
 187                break;
 188        case NETDEV_ATTR_TX:
 189                bit = NETDEV_LED_TX;
 190                break;
 191        case NETDEV_ATTR_RX:
 192                bit = NETDEV_LED_RX;
 193                break;
 194        default:
 195                return -EINVAL;
 196        }
 197
 198        cancel_delayed_work_sync(&trigger_data->work);
 199
 200        if (state)
 201                set_bit(bit, &trigger_data->mode);
 202        else
 203                clear_bit(bit, &trigger_data->mode);
 204
 205        set_baseline_state(trigger_data);
 206
 207        return size;
 208}
 209
 210static ssize_t link_show(struct device *dev,
 211        struct device_attribute *attr, char *buf)
 212{
 213        return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
 214}
 215
 216static ssize_t link_store(struct device *dev,
 217        struct device_attribute *attr, const char *buf, size_t size)
 218{
 219        return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
 220}
 221
 222static DEVICE_ATTR_RW(link);
 223
 224static ssize_t tx_show(struct device *dev,
 225        struct device_attribute *attr, char *buf)
 226{
 227        return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
 228}
 229
 230static ssize_t tx_store(struct device *dev,
 231        struct device_attribute *attr, const char *buf, size_t size)
 232{
 233        return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
 234}
 235
 236static DEVICE_ATTR_RW(tx);
 237
 238static ssize_t rx_show(struct device *dev,
 239        struct device_attribute *attr, char *buf)
 240{
 241        return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
 242}
 243
 244static ssize_t rx_store(struct device *dev,
 245        struct device_attribute *attr, const char *buf, size_t size)
 246{
 247        return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
 248}
 249
 250static DEVICE_ATTR_RW(rx);
 251
 252static ssize_t interval_show(struct device *dev,
 253                             struct device_attribute *attr, char *buf)
 254{
 255        struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 256
 257        return sprintf(buf, "%u\n",
 258                       jiffies_to_msecs(atomic_read(&trigger_data->interval)));
 259}
 260
 261static ssize_t interval_store(struct device *dev,
 262                              struct device_attribute *attr, const char *buf,
 263                              size_t size)
 264{
 265        struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 266        unsigned long value;
 267        int ret;
 268
 269        ret = kstrtoul(buf, 0, &value);
 270        if (ret)
 271                return ret;
 272
 273        /* impose some basic bounds on the timer interval */
 274        if (value >= 5 && value <= 10000) {
 275                cancel_delayed_work_sync(&trigger_data->work);
 276
 277                atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
 278                set_baseline_state(trigger_data);       /* resets timer */
 279        }
 280
 281        return size;
 282}
 283
 284static DEVICE_ATTR_RW(interval);
 285
 286static struct attribute *netdev_trig_attrs[] = {
 287        &dev_attr_device_name.attr,
 288        &dev_attr_link.attr,
 289        &dev_attr_rx.attr,
 290        &dev_attr_tx.attr,
 291        &dev_attr_interval.attr,
 292        NULL
 293};
 294ATTRIBUTE_GROUPS(netdev_trig);
 295
 296static int netdev_trig_notify(struct notifier_block *nb,
 297                              unsigned long evt, void *dv)
 298{
 299        struct net_device *dev =
 300                netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
 301        struct led_netdev_data *trigger_data =
 302                container_of(nb, struct led_netdev_data, notifier);
 303
 304        if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
 305            && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER)
 306                return NOTIFY_DONE;
 307
 308        if (!(dev == trigger_data->net_dev ||
 309              (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
 310                return NOTIFY_DONE;
 311
 312        cancel_delayed_work_sync(&trigger_data->work);
 313
 314        spin_lock_bh(&trigger_data->lock);
 315
 316        clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
 317        switch (evt) {
 318        case NETDEV_REGISTER:
 319                if (trigger_data->net_dev)
 320                        dev_put(trigger_data->net_dev);
 321                dev_hold(dev);
 322                trigger_data->net_dev = dev;
 323                break;
 324        case NETDEV_UNREGISTER:
 325                dev_put(trigger_data->net_dev);
 326                trigger_data->net_dev = NULL;
 327                break;
 328        case NETDEV_UP:
 329        case NETDEV_CHANGE:
 330                if (netif_carrier_ok(dev))
 331                        set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
 332                break;
 333        }
 334
 335        set_baseline_state(trigger_data);
 336
 337        spin_unlock_bh(&trigger_data->lock);
 338
 339        return NOTIFY_DONE;
 340}
 341
 342/* here's the real work! */
 343static void netdev_trig_work(struct work_struct *work)
 344{
 345        struct led_netdev_data *trigger_data =
 346                container_of(work, struct led_netdev_data, work.work);
 347        struct rtnl_link_stats64 *dev_stats;
 348        unsigned int new_activity;
 349        struct rtnl_link_stats64 temp;
 350        unsigned long interval;
 351        int invert;
 352
 353        /* If we dont have a device, insure we are off */
 354        if (!trigger_data->net_dev) {
 355                led_set_brightness(trigger_data->led_cdev, LED_OFF);
 356                return;
 357        }
 358
 359        /* If we are not looking for RX/TX then return  */
 360        if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
 361            !test_bit(NETDEV_LED_RX, &trigger_data->mode))
 362                return;
 363
 364        dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
 365        new_activity =
 366            (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
 367                dev_stats->tx_packets : 0) +
 368            (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
 369                dev_stats->rx_packets : 0);
 370
 371        if (trigger_data->last_activity != new_activity) {
 372                led_stop_software_blink(trigger_data->led_cdev);
 373
 374                invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
 375                interval = jiffies_to_msecs(
 376                                atomic_read(&trigger_data->interval));
 377                /* base state is ON (link present) */
 378                led_blink_set_oneshot(trigger_data->led_cdev,
 379                                      &interval,
 380                                      &interval,
 381                                      invert);
 382                trigger_data->last_activity = new_activity;
 383        }
 384
 385        schedule_delayed_work(&trigger_data->work,
 386                        (atomic_read(&trigger_data->interval)*2));
 387}
 388
 389static int netdev_trig_activate(struct led_classdev *led_cdev)
 390{
 391        struct led_netdev_data *trigger_data;
 392        int rc;
 393
 394        trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
 395        if (!trigger_data)
 396                return -ENOMEM;
 397
 398        spin_lock_init(&trigger_data->lock);
 399
 400        trigger_data->notifier.notifier_call = netdev_trig_notify;
 401        trigger_data->notifier.priority = 10;
 402
 403        INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
 404
 405        trigger_data->led_cdev = led_cdev;
 406        trigger_data->net_dev = NULL;
 407        trigger_data->device_name[0] = 0;
 408
 409        trigger_data->mode = 0;
 410        atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
 411        trigger_data->last_activity = 0;
 412
 413        led_set_trigger_data(led_cdev, trigger_data);
 414
 415        rc = register_netdevice_notifier(&trigger_data->notifier);
 416        if (rc)
 417                kfree(trigger_data);
 418
 419        return rc;
 420}
 421
 422static void netdev_trig_deactivate(struct led_classdev *led_cdev)
 423{
 424        struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);
 425
 426        unregister_netdevice_notifier(&trigger_data->notifier);
 427
 428        cancel_delayed_work_sync(&trigger_data->work);
 429
 430        if (trigger_data->net_dev)
 431                dev_put(trigger_data->net_dev);
 432
 433        kfree(trigger_data);
 434}
 435
 436static struct led_trigger netdev_led_trigger = {
 437        .name = "netdev",
 438        .activate = netdev_trig_activate,
 439        .deactivate = netdev_trig_deactivate,
 440        .groups = netdev_trig_groups,
 441};
 442
 443static int __init netdev_trig_init(void)
 444{
 445        return led_trigger_register(&netdev_led_trigger);
 446}
 447
 448static void __exit netdev_trig_exit(void)
 449{
 450        led_trigger_unregister(&netdev_led_trigger);
 451}
 452
 453module_init(netdev_trig_init);
 454module_exit(netdev_trig_exit);
 455
 456MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
 457MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
 458MODULE_DESCRIPTION("Netdev LED trigger");
 459MODULE_LICENSE("GPL v2");
 460