ledtrig-netdev.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. strncpy(trigger_data->device_name, buf, size);
  105. if (size > 0 && trigger_data->device_name[size - 1] == '\n')
  106. trigger_data->device_name[size - 1] = 0;
  107. if (trigger_data->device_name[0] != 0)
  108. trigger_data->net_dev =
  109. dev_get_by_name(&init_net, trigger_data->device_name);
  110. clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  111. if (trigger_data->net_dev != NULL)
  112. if (netif_carrier_ok(trigger_data->net_dev))
  113. set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  114. trigger_data->last_activity = 0;
  115. set_baseline_state(trigger_data);
  116. spin_unlock_bh(&trigger_data->lock);
  117. return size;
  118. }
  119. static DEVICE_ATTR_RW(device_name);
  120. static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
  121. enum netdev_led_attr attr)
  122. {
  123. struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
  124. int bit;
  125. switch (attr) {
  126. case NETDEV_ATTR_LINK:
  127. bit = NETDEV_LED_LINK;
  128. break;
  129. case NETDEV_ATTR_TX:
  130. bit = NETDEV_LED_TX;
  131. break;
  132. case NETDEV_ATTR_RX:
  133. bit = NETDEV_LED_RX;
  134. break;
  135. default:
  136. return -EINVAL;
  137. }
  138. return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
  139. }
  140. static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
  141. size_t size, enum netdev_led_attr attr)
  142. {
  143. struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
  144. unsigned long state;
  145. int ret;
  146. int bit;
  147. ret = kstrtoul(buf, 0, &state);
  148. if (ret)
  149. return ret;
  150. switch (attr) {
  151. case NETDEV_ATTR_LINK:
  152. bit = NETDEV_LED_LINK;
  153. break;
  154. case NETDEV_ATTR_TX:
  155. bit = NETDEV_LED_TX;
  156. break;
  157. case NETDEV_ATTR_RX:
  158. bit = NETDEV_LED_RX;
  159. break;
  160. default:
  161. return -EINVAL;
  162. }
  163. cancel_delayed_work_sync(&trigger_data->work);
  164. if (state)
  165. set_bit(bit, &trigger_data->mode);
  166. else
  167. clear_bit(bit, &trigger_data->mode);
  168. set_baseline_state(trigger_data);
  169. return size;
  170. }
  171. static ssize_t link_show(struct device *dev,
  172. struct device_attribute *attr, char *buf)
  173. {
  174. return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
  175. }
  176. static ssize_t link_store(struct device *dev,
  177. struct device_attribute *attr, const char *buf, size_t size)
  178. {
  179. return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
  180. }
  181. static DEVICE_ATTR_RW(link);
  182. static ssize_t tx_show(struct device *dev,
  183. struct device_attribute *attr, char *buf)
  184. {
  185. return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
  186. }
  187. static ssize_t tx_store(struct device *dev,
  188. struct device_attribute *attr, const char *buf, size_t size)
  189. {
  190. return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
  191. }
  192. static DEVICE_ATTR_RW(tx);
  193. static ssize_t rx_show(struct device *dev,
  194. struct device_attribute *attr, char *buf)
  195. {
  196. return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
  197. }
  198. static ssize_t rx_store(struct device *dev,
  199. struct device_attribute *attr, const char *buf, size_t size)
  200. {
  201. return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
  202. }
  203. static DEVICE_ATTR_RW(rx);
  204. static ssize_t interval_show(struct device *dev,
  205. struct device_attribute *attr, char *buf)
  206. {
  207. struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
  208. return sprintf(buf, "%u\n",
  209. jiffies_to_msecs(atomic_read(&trigger_data->interval)));
  210. }
  211. static ssize_t interval_store(struct device *dev,
  212. struct device_attribute *attr, const char *buf,
  213. size_t size)
  214. {
  215. struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
  216. unsigned long value;
  217. int ret;
  218. ret = kstrtoul(buf, 0, &value);
  219. if (ret)
  220. return ret;
  221. /* impose some basic bounds on the timer interval */
  222. if (value >= 5 && value <= 10000) {
  223. cancel_delayed_work_sync(&trigger_data->work);
  224. atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
  225. set_baseline_state(trigger_data); /* resets timer */
  226. }
  227. return size;
  228. }
  229. static DEVICE_ATTR_RW(interval);
  230. static struct attribute *netdev_trig_attrs[] = {
  231. &dev_attr_device_name.attr,
  232. &dev_attr_link.attr,
  233. &dev_attr_rx.attr,
  234. &dev_attr_tx.attr,
  235. &dev_attr_interval.attr,
  236. NULL
  237. };
  238. ATTRIBUTE_GROUPS(netdev_trig);
  239. static int netdev_trig_notify(struct notifier_block *nb,
  240. unsigned long evt, void *dv)
  241. {
  242. struct net_device *dev =
  243. netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
  244. struct led_netdev_data *trigger_data =
  245. container_of(nb, struct led_netdev_data, notifier);
  246. if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
  247. && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
  248. && evt != NETDEV_CHANGENAME)
  249. return NOTIFY_DONE;
  250. if (strcmp(dev->name, trigger_data->device_name))
  251. return NOTIFY_DONE;
  252. cancel_delayed_work_sync(&trigger_data->work);
  253. spin_lock_bh(&trigger_data->lock);
  254. clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  255. switch (evt) {
  256. case NETDEV_REGISTER:
  257. if (trigger_data->net_dev)
  258. dev_put(trigger_data->net_dev);
  259. dev_hold(dev);
  260. trigger_data->net_dev = dev;
  261. break;
  262. case NETDEV_CHANGENAME:
  263. case NETDEV_UNREGISTER:
  264. if (trigger_data->net_dev) {
  265. dev_put(trigger_data->net_dev);
  266. trigger_data->net_dev = NULL;
  267. }
  268. break;
  269. case NETDEV_UP:
  270. case NETDEV_CHANGE:
  271. if (netif_carrier_ok(dev))
  272. set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  273. break;
  274. }
  275. set_baseline_state(trigger_data);
  276. spin_unlock_bh(&trigger_data->lock);
  277. return NOTIFY_DONE;
  278. }
  279. /* here's the real work! */
  280. static void netdev_trig_work(struct work_struct *work)
  281. {
  282. struct led_netdev_data *trigger_data =
  283. container_of(work, struct led_netdev_data, work.work);
  284. struct rtnl_link_stats64 *dev_stats;
  285. unsigned int new_activity;
  286. struct rtnl_link_stats64 temp;
  287. unsigned long interval;
  288. int invert;
  289. /* If we dont have a device, insure we are off */
  290. if (!trigger_data->net_dev) {
  291. led_set_brightness(trigger_data->led_cdev, LED_OFF);
  292. return;
  293. }
  294. /* If we are not looking for RX/TX then return */
  295. if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
  296. !test_bit(NETDEV_LED_RX, &trigger_data->mode))
  297. return;
  298. dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
  299. new_activity =
  300. (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
  301. dev_stats->tx_packets : 0) +
  302. (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
  303. dev_stats->rx_packets : 0);
  304. if (trigger_data->last_activity != new_activity) {
  305. led_stop_software_blink(trigger_data->led_cdev);
  306. invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
  307. interval = jiffies_to_msecs(
  308. atomic_read(&trigger_data->interval));
  309. /* base state is ON (link present) */
  310. led_blink_set_oneshot(trigger_data->led_cdev,
  311. &interval,
  312. &interval,
  313. invert);
  314. trigger_data->last_activity = new_activity;
  315. }
  316. schedule_delayed_work(&trigger_data->work,
  317. (atomic_read(&trigger_data->interval)*2));
  318. }
  319. static int netdev_trig_activate(struct led_classdev *led_cdev)
  320. {
  321. struct led_netdev_data *trigger_data;
  322. int rc;
  323. trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
  324. if (!trigger_data)
  325. return -ENOMEM;
  326. spin_lock_init(&trigger_data->lock);
  327. trigger_data->notifier.notifier_call = netdev_trig_notify;
  328. trigger_data->notifier.priority = 10;
  329. INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
  330. trigger_data->led_cdev = led_cdev;
  331. trigger_data->net_dev = NULL;
  332. trigger_data->device_name[0] = 0;
  333. trigger_data->mode = 0;
  334. atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
  335. trigger_data->last_activity = 0;
  336. led_set_trigger_data(led_cdev, trigger_data);
  337. rc = register_netdevice_notifier(&trigger_data->notifier);
  338. if (rc)
  339. kfree(trigger_data);
  340. return rc;
  341. }
  342. static void netdev_trig_deactivate(struct led_classdev *led_cdev)
  343. {
  344. struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);
  345. unregister_netdevice_notifier(&trigger_data->notifier);
  346. cancel_delayed_work_sync(&trigger_data->work);
  347. if (trigger_data->net_dev)
  348. dev_put(trigger_data->net_dev);
  349. kfree(trigger_data);
  350. }
  351. static struct led_trigger netdev_led_trigger = {
  352. .name = "netdev",
  353. .activate = netdev_trig_activate,
  354. .deactivate = netdev_trig_deactivate,
  355. .groups = netdev_trig_groups,
  356. };
  357. static int __init netdev_trig_init(void)
  358. {
  359. return led_trigger_register(&netdev_led_trigger);
  360. }
  361. static void __exit netdev_trig_exit(void)
  362. {
  363. led_trigger_unregister(&netdev_led_trigger);
  364. }
  365. module_init(netdev_trig_init);
  366. module_exit(netdev_trig_exit);
  367. MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
  368. MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
  369. MODULE_DESCRIPTION("Netdev LED trigger");
  370. MODULE_LICENSE("GPL v2");