ledtrig-netdev.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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_classdev *led_cdev = dev_get_drvdata(dev);
  85. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  86. ssize_t len;
  87. spin_lock_bh(&trigger_data->lock);
  88. len = sprintf(buf, "%s\n", trigger_data->device_name);
  89. spin_unlock_bh(&trigger_data->lock);
  90. return len;
  91. }
  92. static ssize_t device_name_store(struct device *dev,
  93. struct device_attribute *attr, const char *buf,
  94. size_t size)
  95. {
  96. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  97. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  98. if (size >= IFNAMSIZ)
  99. return -EINVAL;
  100. cancel_delayed_work_sync(&trigger_data->work);
  101. spin_lock_bh(&trigger_data->lock);
  102. if (trigger_data->net_dev) {
  103. dev_put(trigger_data->net_dev);
  104. trigger_data->net_dev = NULL;
  105. }
  106. strncpy(trigger_data->device_name, buf, size);
  107. if (size > 0 && trigger_data->device_name[size - 1] == '\n')
  108. trigger_data->device_name[size - 1] = 0;
  109. if (trigger_data->device_name[0] != 0)
  110. trigger_data->net_dev =
  111. dev_get_by_name(&init_net, trigger_data->device_name);
  112. clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  113. if (trigger_data->net_dev != NULL)
  114. if (netif_carrier_ok(trigger_data->net_dev))
  115. set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  116. trigger_data->last_activity = 0;
  117. set_baseline_state(trigger_data);
  118. spin_unlock_bh(&trigger_data->lock);
  119. return size;
  120. }
  121. static DEVICE_ATTR_RW(device_name);
  122. static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
  123. enum netdev_led_attr attr)
  124. {
  125. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  126. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  127. int bit;
  128. switch (attr) {
  129. case NETDEV_ATTR_LINK:
  130. bit = NETDEV_LED_LINK;
  131. break;
  132. case NETDEV_ATTR_TX:
  133. bit = NETDEV_LED_TX;
  134. break;
  135. case NETDEV_ATTR_RX:
  136. bit = NETDEV_LED_RX;
  137. break;
  138. default:
  139. return -EINVAL;
  140. }
  141. return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
  142. }
  143. static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
  144. size_t size, enum netdev_led_attr attr)
  145. {
  146. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  147. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  148. unsigned long state;
  149. int ret;
  150. int bit;
  151. ret = kstrtoul(buf, 0, &state);
  152. if (ret)
  153. return ret;
  154. switch (attr) {
  155. case NETDEV_ATTR_LINK:
  156. bit = NETDEV_LED_LINK;
  157. break;
  158. case NETDEV_ATTR_TX:
  159. bit = NETDEV_LED_TX;
  160. break;
  161. case NETDEV_ATTR_RX:
  162. bit = NETDEV_LED_RX;
  163. break;
  164. default:
  165. return -EINVAL;
  166. }
  167. cancel_delayed_work_sync(&trigger_data->work);
  168. if (state)
  169. set_bit(bit, &trigger_data->mode);
  170. else
  171. clear_bit(bit, &trigger_data->mode);
  172. set_baseline_state(trigger_data);
  173. return size;
  174. }
  175. static ssize_t link_show(struct device *dev,
  176. struct device_attribute *attr, char *buf)
  177. {
  178. return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
  179. }
  180. static ssize_t link_store(struct device *dev,
  181. struct device_attribute *attr, const char *buf, size_t size)
  182. {
  183. return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
  184. }
  185. static DEVICE_ATTR_RW(link);
  186. static ssize_t tx_show(struct device *dev,
  187. struct device_attribute *attr, char *buf)
  188. {
  189. return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
  190. }
  191. static ssize_t tx_store(struct device *dev,
  192. struct device_attribute *attr, const char *buf, size_t size)
  193. {
  194. return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
  195. }
  196. static DEVICE_ATTR_RW(tx);
  197. static ssize_t rx_show(struct device *dev,
  198. struct device_attribute *attr, char *buf)
  199. {
  200. return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
  201. }
  202. static ssize_t rx_store(struct device *dev,
  203. struct device_attribute *attr, const char *buf, size_t size)
  204. {
  205. return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
  206. }
  207. static DEVICE_ATTR_RW(rx);
  208. static ssize_t interval_show(struct device *dev,
  209. struct device_attribute *attr, char *buf)
  210. {
  211. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  212. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  213. return sprintf(buf, "%u\n",
  214. jiffies_to_msecs(atomic_read(&trigger_data->interval)));
  215. }
  216. static ssize_t interval_store(struct device *dev,
  217. struct device_attribute *attr, const char *buf,
  218. size_t size)
  219. {
  220. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  221. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  222. unsigned long value;
  223. int ret;
  224. ret = kstrtoul(buf, 0, &value);
  225. if (ret)
  226. return ret;
  227. /* impose some basic bounds on the timer interval */
  228. if (value >= 5 && value <= 10000) {
  229. cancel_delayed_work_sync(&trigger_data->work);
  230. atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
  231. set_baseline_state(trigger_data); /* resets timer */
  232. }
  233. return size;
  234. }
  235. static DEVICE_ATTR_RW(interval);
  236. static int netdev_trig_notify(struct notifier_block *nb,
  237. unsigned long evt, void *dv)
  238. {
  239. struct net_device *dev =
  240. netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
  241. struct led_netdev_data *trigger_data = container_of(nb,
  242. struct
  243. led_netdev_data,
  244. notifier);
  245. if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
  246. && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
  247. && evt != NETDEV_CHANGENAME)
  248. return NOTIFY_DONE;
  249. if (strcmp(dev->name, trigger_data->device_name))
  250. return NOTIFY_DONE;
  251. cancel_delayed_work_sync(&trigger_data->work);
  252. spin_lock_bh(&trigger_data->lock);
  253. clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  254. switch (evt) {
  255. case NETDEV_REGISTER:
  256. if (trigger_data->net_dev)
  257. dev_put(trigger_data->net_dev);
  258. dev_hold(dev);
  259. trigger_data->net_dev = dev;
  260. break;
  261. case NETDEV_CHANGENAME:
  262. case NETDEV_UNREGISTER:
  263. if (trigger_data->net_dev) {
  264. dev_put(trigger_data->net_dev);
  265. trigger_data->net_dev = NULL;
  266. }
  267. break;
  268. case NETDEV_UP:
  269. case NETDEV_CHANGE:
  270. if (netif_carrier_ok(dev))
  271. set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  272. break;
  273. }
  274. set_baseline_state(trigger_data);
  275. spin_unlock_bh(&trigger_data->lock);
  276. return NOTIFY_DONE;
  277. }
  278. /* here's the real work! */
  279. static void netdev_trig_work(struct work_struct *work)
  280. {
  281. struct led_netdev_data *trigger_data = container_of(work,
  282. struct
  283. led_netdev_data,
  284. 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 void 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;
  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_cdev->trigger_data = trigger_data;
  338. rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
  339. if (rc)
  340. goto err_out;
  341. rc = device_create_file(led_cdev->dev, &dev_attr_link);
  342. if (rc)
  343. goto err_out_device_name;
  344. rc = device_create_file(led_cdev->dev, &dev_attr_rx);
  345. if (rc)
  346. goto err_out_link;
  347. rc = device_create_file(led_cdev->dev, &dev_attr_tx);
  348. if (rc)
  349. goto err_out_rx;
  350. rc = device_create_file(led_cdev->dev, &dev_attr_interval);
  351. if (rc)
  352. goto err_out_tx;
  353. rc = register_netdevice_notifier(&trigger_data->notifier);
  354. if (rc)
  355. goto err_out_interval;
  356. return;
  357. err_out_interval:
  358. device_remove_file(led_cdev->dev, &dev_attr_interval);
  359. err_out_tx:
  360. device_remove_file(led_cdev->dev, &dev_attr_tx);
  361. err_out_rx:
  362. device_remove_file(led_cdev->dev, &dev_attr_rx);
  363. err_out_link:
  364. device_remove_file(led_cdev->dev, &dev_attr_link);
  365. err_out_device_name:
  366. device_remove_file(led_cdev->dev, &dev_attr_device_name);
  367. err_out:
  368. led_cdev->trigger_data = NULL;
  369. kfree(trigger_data);
  370. }
  371. static void netdev_trig_deactivate(struct led_classdev *led_cdev)
  372. {
  373. struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  374. if (trigger_data) {
  375. unregister_netdevice_notifier(&trigger_data->notifier);
  376. device_remove_file(led_cdev->dev, &dev_attr_device_name);
  377. device_remove_file(led_cdev->dev, &dev_attr_link);
  378. device_remove_file(led_cdev->dev, &dev_attr_rx);
  379. device_remove_file(led_cdev->dev, &dev_attr_tx);
  380. device_remove_file(led_cdev->dev, &dev_attr_interval);
  381. cancel_delayed_work_sync(&trigger_data->work);
  382. if (trigger_data->net_dev)
  383. dev_put(trigger_data->net_dev);
  384. kfree(trigger_data);
  385. }
  386. }
  387. static struct led_trigger netdev_led_trigger = {
  388. .name = "netdev",
  389. .activate = netdev_trig_activate,
  390. .deactivate = netdev_trig_deactivate,
  391. };
  392. static int __init netdev_trig_init(void)
  393. {
  394. return led_trigger_register(&netdev_led_trigger);
  395. }
  396. static void __exit netdev_trig_exit(void)
  397. {
  398. led_trigger_unregister(&netdev_led_trigger);
  399. }
  400. module_init(netdev_trig_init);
  401. module_exit(netdev_trig_exit);
  402. MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
  403. MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
  404. MODULE_DESCRIPTION("Netdev LED trigger");
  405. MODULE_LICENSE("GPL v2");