Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright 2017 Ben Whitten <ben.whitten@gmail.com>
0003 // Copyright 2007 Oliver Jowett <oliver@opencloud.com>
0004 //
0005 // LED Kernel Netdev Trigger
0006 //
0007 // Toggles the LED to reflect the link and traffic state of a named net device
0008 //
0009 // Derived from ledtrig-timer.c which is:
0010 //  Copyright 2005-2006 Openedhand Ltd.
0011 //  Author: Richard Purdie <rpurdie@openedhand.com>
0012 
0013 #include <linux/atomic.h>
0014 #include <linux/ctype.h>
0015 #include <linux/device.h>
0016 #include <linux/init.h>
0017 #include <linux/jiffies.h>
0018 #include <linux/kernel.h>
0019 #include <linux/leds.h>
0020 #include <linux/list.h>
0021 #include <linux/module.h>
0022 #include <linux/netdevice.h>
0023 #include <linux/spinlock.h>
0024 #include <linux/timer.h>
0025 #include "../leds.h"
0026 
0027 /*
0028  * Configurable sysfs attributes:
0029  *
0030  * device_name - network device name to monitor
0031  * interval - duration of LED blink, in milliseconds
0032  * link -  LED's normal state reflects whether the link is up
0033  *         (has carrier) or not
0034  * tx -  LED blinks on transmitted data
0035  * rx -  LED blinks on receive data
0036  *
0037  */
0038 
0039 struct led_netdev_data {
0040     spinlock_t lock;
0041 
0042     struct delayed_work work;
0043     struct notifier_block notifier;
0044 
0045     struct led_classdev *led_cdev;
0046     struct net_device *net_dev;
0047 
0048     char device_name[IFNAMSIZ];
0049     atomic_t interval;
0050     unsigned int last_activity;
0051 
0052     unsigned long mode;
0053 #define NETDEV_LED_LINK 0
0054 #define NETDEV_LED_TX   1
0055 #define NETDEV_LED_RX   2
0056 #define NETDEV_LED_MODE_LINKUP  3
0057 };
0058 
0059 enum netdev_led_attr {
0060     NETDEV_ATTR_LINK,
0061     NETDEV_ATTR_TX,
0062     NETDEV_ATTR_RX
0063 };
0064 
0065 static void set_baseline_state(struct led_netdev_data *trigger_data)
0066 {
0067     int current_brightness;
0068     struct led_classdev *led_cdev = trigger_data->led_cdev;
0069 
0070     current_brightness = led_cdev->brightness;
0071     if (current_brightness)
0072         led_cdev->blink_brightness = current_brightness;
0073     if (!led_cdev->blink_brightness)
0074         led_cdev->blink_brightness = led_cdev->max_brightness;
0075 
0076     if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode))
0077         led_set_brightness(led_cdev, LED_OFF);
0078     else {
0079         if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
0080             led_set_brightness(led_cdev,
0081                        led_cdev->blink_brightness);
0082         else
0083             led_set_brightness(led_cdev, LED_OFF);
0084 
0085         /* If we are looking for RX/TX start periodically
0086          * checking stats
0087          */
0088         if (test_bit(NETDEV_LED_TX, &trigger_data->mode) ||
0089             test_bit(NETDEV_LED_RX, &trigger_data->mode))
0090             schedule_delayed_work(&trigger_data->work, 0);
0091     }
0092 }
0093 
0094 static ssize_t device_name_show(struct device *dev,
0095                 struct device_attribute *attr, char *buf)
0096 {
0097     struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
0098     ssize_t len;
0099 
0100     spin_lock_bh(&trigger_data->lock);
0101     len = sprintf(buf, "%s\n", trigger_data->device_name);
0102     spin_unlock_bh(&trigger_data->lock);
0103 
0104     return len;
0105 }
0106 
0107 static ssize_t device_name_store(struct device *dev,
0108                  struct device_attribute *attr, const char *buf,
0109                  size_t size)
0110 {
0111     struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
0112 
0113     if (size >= IFNAMSIZ)
0114         return -EINVAL;
0115 
0116     cancel_delayed_work_sync(&trigger_data->work);
0117 
0118     spin_lock_bh(&trigger_data->lock);
0119 
0120     if (trigger_data->net_dev) {
0121         dev_put(trigger_data->net_dev);
0122         trigger_data->net_dev = NULL;
0123     }
0124 
0125     memcpy(trigger_data->device_name, buf, size);
0126     trigger_data->device_name[size] = 0;
0127     if (size > 0 && trigger_data->device_name[size - 1] == '\n')
0128         trigger_data->device_name[size - 1] = 0;
0129 
0130     if (trigger_data->device_name[0] != 0)
0131         trigger_data->net_dev =
0132             dev_get_by_name(&init_net, trigger_data->device_name);
0133 
0134     clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
0135     if (trigger_data->net_dev != NULL)
0136         if (netif_carrier_ok(trigger_data->net_dev))
0137             set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
0138 
0139     trigger_data->last_activity = 0;
0140 
0141     set_baseline_state(trigger_data);
0142     spin_unlock_bh(&trigger_data->lock);
0143 
0144     return size;
0145 }
0146 
0147 static DEVICE_ATTR_RW(device_name);
0148 
0149 static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
0150     enum netdev_led_attr attr)
0151 {
0152     struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
0153     int bit;
0154 
0155     switch (attr) {
0156     case NETDEV_ATTR_LINK:
0157         bit = NETDEV_LED_LINK;
0158         break;
0159     case NETDEV_ATTR_TX:
0160         bit = NETDEV_LED_TX;
0161         break;
0162     case NETDEV_ATTR_RX:
0163         bit = NETDEV_LED_RX;
0164         break;
0165     default:
0166         return -EINVAL;
0167     }
0168 
0169     return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
0170 }
0171 
0172 static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
0173     size_t size, enum netdev_led_attr attr)
0174 {
0175     struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
0176     unsigned long state;
0177     int ret;
0178     int bit;
0179 
0180     ret = kstrtoul(buf, 0, &state);
0181     if (ret)
0182         return ret;
0183 
0184     switch (attr) {
0185     case NETDEV_ATTR_LINK:
0186         bit = NETDEV_LED_LINK;
0187         break;
0188     case NETDEV_ATTR_TX:
0189         bit = NETDEV_LED_TX;
0190         break;
0191     case NETDEV_ATTR_RX:
0192         bit = NETDEV_LED_RX;
0193         break;
0194     default:
0195         return -EINVAL;
0196     }
0197 
0198     cancel_delayed_work_sync(&trigger_data->work);
0199 
0200     if (state)
0201         set_bit(bit, &trigger_data->mode);
0202     else
0203         clear_bit(bit, &trigger_data->mode);
0204 
0205     set_baseline_state(trigger_data);
0206 
0207     return size;
0208 }
0209 
0210 static ssize_t link_show(struct device *dev,
0211     struct device_attribute *attr, char *buf)
0212 {
0213     return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
0214 }
0215 
0216 static ssize_t link_store(struct device *dev,
0217     struct device_attribute *attr, const char *buf, size_t size)
0218 {
0219     return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
0220 }
0221 
0222 static DEVICE_ATTR_RW(link);
0223 
0224 static ssize_t tx_show(struct device *dev,
0225     struct device_attribute *attr, char *buf)
0226 {
0227     return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
0228 }
0229 
0230 static ssize_t tx_store(struct device *dev,
0231     struct device_attribute *attr, const char *buf, size_t size)
0232 {
0233     return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
0234 }
0235 
0236 static DEVICE_ATTR_RW(tx);
0237 
0238 static ssize_t rx_show(struct device *dev,
0239     struct device_attribute *attr, char *buf)
0240 {
0241     return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
0242 }
0243 
0244 static ssize_t rx_store(struct device *dev,
0245     struct device_attribute *attr, const char *buf, size_t size)
0246 {
0247     return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
0248 }
0249 
0250 static DEVICE_ATTR_RW(rx);
0251 
0252 static ssize_t interval_show(struct device *dev,
0253                  struct device_attribute *attr, char *buf)
0254 {
0255     struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
0256 
0257     return sprintf(buf, "%u\n",
0258                jiffies_to_msecs(atomic_read(&trigger_data->interval)));
0259 }
0260 
0261 static ssize_t interval_store(struct device *dev,
0262                   struct device_attribute *attr, const char *buf,
0263                   size_t size)
0264 {
0265     struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
0266     unsigned long value;
0267     int ret;
0268 
0269     ret = kstrtoul(buf, 0, &value);
0270     if (ret)
0271         return ret;
0272 
0273     /* impose some basic bounds on the timer interval */
0274     if (value >= 5 && value <= 10000) {
0275         cancel_delayed_work_sync(&trigger_data->work);
0276 
0277         atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
0278         set_baseline_state(trigger_data);   /* resets timer */
0279     }
0280 
0281     return size;
0282 }
0283 
0284 static DEVICE_ATTR_RW(interval);
0285 
0286 static struct attribute *netdev_trig_attrs[] = {
0287     &dev_attr_device_name.attr,
0288     &dev_attr_link.attr,
0289     &dev_attr_rx.attr,
0290     &dev_attr_tx.attr,
0291     &dev_attr_interval.attr,
0292     NULL
0293 };
0294 ATTRIBUTE_GROUPS(netdev_trig);
0295 
0296 static int netdev_trig_notify(struct notifier_block *nb,
0297                   unsigned long evt, void *dv)
0298 {
0299     struct net_device *dev =
0300         netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
0301     struct led_netdev_data *trigger_data =
0302         container_of(nb, struct led_netdev_data, notifier);
0303 
0304     if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
0305         && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
0306         && evt != NETDEV_CHANGENAME)
0307         return NOTIFY_DONE;
0308 
0309     if (!(dev == trigger_data->net_dev ||
0310           (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) ||
0311           (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
0312         return NOTIFY_DONE;
0313 
0314     cancel_delayed_work_sync(&trigger_data->work);
0315 
0316     spin_lock_bh(&trigger_data->lock);
0317 
0318     clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
0319     switch (evt) {
0320     case NETDEV_CHANGENAME:
0321     case NETDEV_REGISTER:
0322         if (trigger_data->net_dev)
0323             dev_put(trigger_data->net_dev);
0324         dev_hold(dev);
0325         trigger_data->net_dev = dev;
0326         break;
0327     case NETDEV_UNREGISTER:
0328         dev_put(trigger_data->net_dev);
0329         trigger_data->net_dev = NULL;
0330         break;
0331     case NETDEV_UP:
0332     case NETDEV_CHANGE:
0333         if (netif_carrier_ok(dev))
0334             set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
0335         break;
0336     }
0337 
0338     set_baseline_state(trigger_data);
0339 
0340     spin_unlock_bh(&trigger_data->lock);
0341 
0342     return NOTIFY_DONE;
0343 }
0344 
0345 /* here's the real work! */
0346 static void netdev_trig_work(struct work_struct *work)
0347 {
0348     struct led_netdev_data *trigger_data =
0349         container_of(work, struct led_netdev_data, work.work);
0350     struct rtnl_link_stats64 *dev_stats;
0351     unsigned int new_activity;
0352     struct rtnl_link_stats64 temp;
0353     unsigned long interval;
0354     int invert;
0355 
0356     /* If we dont have a device, insure we are off */
0357     if (!trigger_data->net_dev) {
0358         led_set_brightness(trigger_data->led_cdev, LED_OFF);
0359         return;
0360     }
0361 
0362     /* If we are not looking for RX/TX then return  */
0363     if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
0364         !test_bit(NETDEV_LED_RX, &trigger_data->mode))
0365         return;
0366 
0367     dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
0368     new_activity =
0369         (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
0370         dev_stats->tx_packets : 0) +
0371         (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
0372         dev_stats->rx_packets : 0);
0373 
0374     if (trigger_data->last_activity != new_activity) {
0375         led_stop_software_blink(trigger_data->led_cdev);
0376 
0377         invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
0378         interval = jiffies_to_msecs(
0379                 atomic_read(&trigger_data->interval));
0380         /* base state is ON (link present) */
0381         led_blink_set_oneshot(trigger_data->led_cdev,
0382                       &interval,
0383                       &interval,
0384                       invert);
0385         trigger_data->last_activity = new_activity;
0386     }
0387 
0388     schedule_delayed_work(&trigger_data->work,
0389             (atomic_read(&trigger_data->interval)*2));
0390 }
0391 
0392 static int netdev_trig_activate(struct led_classdev *led_cdev)
0393 {
0394     struct led_netdev_data *trigger_data;
0395     int rc;
0396 
0397     trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
0398     if (!trigger_data)
0399         return -ENOMEM;
0400 
0401     spin_lock_init(&trigger_data->lock);
0402 
0403     trigger_data->notifier.notifier_call = netdev_trig_notify;
0404     trigger_data->notifier.priority = 10;
0405 
0406     INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
0407 
0408     trigger_data->led_cdev = led_cdev;
0409     trigger_data->net_dev = NULL;
0410     trigger_data->device_name[0] = 0;
0411 
0412     trigger_data->mode = 0;
0413     atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
0414     trigger_data->last_activity = 0;
0415 
0416     led_set_trigger_data(led_cdev, trigger_data);
0417 
0418     rc = register_netdevice_notifier(&trigger_data->notifier);
0419     if (rc)
0420         kfree(trigger_data);
0421 
0422     return rc;
0423 }
0424 
0425 static void netdev_trig_deactivate(struct led_classdev *led_cdev)
0426 {
0427     struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);
0428 
0429     unregister_netdevice_notifier(&trigger_data->notifier);
0430 
0431     cancel_delayed_work_sync(&trigger_data->work);
0432 
0433     if (trigger_data->net_dev)
0434         dev_put(trigger_data->net_dev);
0435 
0436     kfree(trigger_data);
0437 }
0438 
0439 static struct led_trigger netdev_led_trigger = {
0440     .name = "netdev",
0441     .activate = netdev_trig_activate,
0442     .deactivate = netdev_trig_deactivate,
0443     .groups = netdev_trig_groups,
0444 };
0445 
0446 static int __init netdev_trig_init(void)
0447 {
0448     return led_trigger_register(&netdev_led_trigger);
0449 }
0450 
0451 static void __exit netdev_trig_exit(void)
0452 {
0453     led_trigger_unregister(&netdev_led_trigger);
0454 }
0455 
0456 module_init(netdev_trig_init);
0457 module_exit(netdev_trig_exit);
0458 
0459 MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
0460 MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
0461 MODULE_DESCRIPTION("Netdev LED trigger");
0462 MODULE_LICENSE("GPL v2");