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            && evt != NETDEV_CHANGENAME)
 307                return NOTIFY_DONE;
 308
 309        if (!(dev == trigger_data->net_dev ||
 310              (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) ||
 311              (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
 312                return NOTIFY_DONE;
 313
 314        cancel_delayed_work_sync(&trigger_data->work);
 315
 316        spin_lock_bh(&trigger_data->lock);
 317
 318        clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
 319        switch (evt) {
 320        case NETDEV_CHANGENAME:
 321        case NETDEV_REGISTER:
 322                if (trigger_data->net_dev)
 323                        dev_put(trigger_data->net_dev);
 324                dev_hold(dev);
 325                trigger_data->net_dev = dev;
 326                break;
 327        case NETDEV_UNREGISTER:
 328                dev_put(trigger_data->net_dev);
 329                trigger_data->net_dev = NULL;
 330                break;
 331        case NETDEV_UP:
 332        case NETDEV_CHANGE:
 333                if (netif_carrier_ok(dev))
 334                        set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
 335                break;
 336        }
 337
 338        set_baseline_state(trigger_data);
 339
 340        spin_unlock_bh(&trigger_data->lock);
 341
 342        return NOTIFY_DONE;
 343}
 344
 345/* here's the real work! */
 346static void netdev_trig_work(struct work_struct *work)
 347{
 348        struct led_netdev_data *trigger_data =
 349                container_of(work, struct led_netdev_data, work.work);
 350        struct rtnl_link_stats64 *dev_stats;
 351        unsigned int new_activity;
 352        struct rtnl_link_stats64 temp;
 353        unsigned long interval;
 354        int invert;
 355
 356        /* If we dont have a device, insure we are off */
 357        if (!trigger_data->net_dev) {
 358                led_set_brightness(trigger_data->led_cdev, LED_OFF);
 359                return;
 360        }
 361
 362        /* If we are not looking for RX/TX then return  */
 363        if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
 364            !test_bit(NETDEV_LED_RX, &trigger_data->mode))
 365                return;
 366
 367        dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
 368        new_activity =
 369            (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
 370                dev_stats->tx_packets : 0) +
 371            (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
 372                dev_stats->rx_packets : 0);
 373
 374        if (trigger_data->last_activity != new_activity) {
 375                led_stop_software_blink(trigger_data->led_cdev);
 376
 377                invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
 378                interval = jiffies_to_msecs(
 379                                atomic_read(&trigger_data->interval));
 380                /* base state is ON (link present) */
 381                led_blink_set_oneshot(trigger_data->led_cdev,
 382                                      &interval,
 383                                      &interval,
 384                                      invert);
 385                trigger_data->last_activity = new_activity;
 386        }
 387
 388        schedule_delayed_work(&trigger_data->work,
 389                        (atomic_read(&trigger_data->interval)*2));
 390}
 391
 392static int netdev_trig_activate(struct led_classdev *led_cdev)
 393{
 394        struct led_netdev_data *trigger_data;
 395        int rc;
 396
 397        trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
 398        if (!trigger_data)
 399                return -ENOMEM;
 400
 401        spin_lock_init(&trigger_data->lock);
 402
 403        trigger_data->notifier.notifier_call = netdev_trig_notify;
 404        trigger_data->notifier.priority = 10;
 405
 406        INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
 407
 408        trigger_data->led_cdev = led_cdev;
 409        trigger_data->net_dev = NULL;
 410        trigger_data->device_name[0] = 0;
 411
 412        trigger_data->mode = 0;
 413        atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
 414        trigger_data->last_activity = 0;
 415
 416        led_set_trigger_data(led_cdev, trigger_data);
 417
 418        rc = register_netdevice_notifier(&trigger_data->notifier);
 419        if (rc)
 420                kfree(trigger_data);
 421
 422        return rc;
 423}
 424
 425static void netdev_trig_deactivate(struct led_classdev *led_cdev)
 426{
 427        struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);
 428
 429        unregister_netdevice_notifier(&trigger_data->notifier);
 430
 431        cancel_delayed_work_sync(&trigger_data->work);
 432
 433        if (trigger_data->net_dev)
 434                dev_put(trigger_data->net_dev);
 435
 436        kfree(trigger_data);
 437}
 438
 439static struct led_trigger netdev_led_trigger = {
 440        .name = "netdev",
 441        .activate = netdev_trig_activate,
 442        .deactivate = netdev_trig_deactivate,
 443        .groups = netdev_trig_groups,
 444};
 445
 446static int __init netdev_trig_init(void)
 447{
 448        return led_trigger_register(&netdev_led_trigger);
 449}
 450
 451static void __exit netdev_trig_exit(void)
 452{
 453        led_trigger_unregister(&netdev_led_trigger);
 454}
 455
 456module_init(netdev_trig_init);
 457module_exit(netdev_trig_exit);
 458
 459MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
 460MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
 461MODULE_DESCRIPTION("Netdev LED trigger");
 462MODULE_LICENSE("GPL v2");
 463