ledtrig-netdev.c 11 KB

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