0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
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
0029
0030
0031
0032
0033
0034
0035
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
0086
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
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);
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
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
0357 if (!trigger_data->net_dev) {
0358 led_set_brightness(trigger_data->led_cdev, LED_OFF);
0359 return;
0360 }
0361
0362
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
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");