400-v4.16-leds-trigger-Introduce-a-NETDEV-trigger.patch 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. From 06f502f57d0d7728f9fa0f157ec5e4111ddb98f6 Mon Sep 17 00:00:00 2001
  2. From: Ben Whitten <ben.whitten@gmail.com>
  3. Date: Sun, 10 Dec 2017 21:17:55 +0000
  4. Subject: [PATCH] leds: trigger: Introduce a NETDEV trigger
  5. This commit introduces a NETDEV trigger for named device
  6. activity. Available triggers are link, rx, and tx.
  7. Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
  8. Acked-by: Pavel Machek <pavel@ucw.cz>
  9. Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
  10. ---
  11. .../ABI/testing/sysfs-class-led-trigger-netdev | 45 ++
  12. drivers/leds/trigger/Kconfig | 7 +
  13. drivers/leds/trigger/Makefile | 1 +
  14. drivers/leds/trigger/ledtrig-netdev.c | 496 +++++++++++++++++++++
  15. 4 files changed, 549 insertions(+)
  16. create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-netdev
  17. create mode 100644 drivers/leds/trigger/ledtrig-netdev.c
  18. --- /dev/null
  19. +++ b/Documentation/ABI/testing/sysfs-class-led-trigger-netdev
  20. @@ -0,0 +1,45 @@
  21. +What: /sys/class/leds/<led>/device_name
  22. +Date: Dec 2017
  23. +KernelVersion: 4.16
  24. +Contact: linux-leds@vger.kernel.org
  25. +Description:
  26. + Specifies the network device name to monitor.
  27. +
  28. +What: /sys/class/leds/<led>/interval
  29. +Date: Dec 2017
  30. +KernelVersion: 4.16
  31. +Contact: linux-leds@vger.kernel.org
  32. +Description:
  33. + Specifies the duration of the LED blink in milliseconds.
  34. + Defaults to 50 ms.
  35. +
  36. +What: /sys/class/leds/<led>/link
  37. +Date: Dec 2017
  38. +KernelVersion: 4.16
  39. +Contact: linux-leds@vger.kernel.org
  40. +Description:
  41. + Signal the link state of the named network device.
  42. + If set to 0 (default), the LED's normal state is off.
  43. + If set to 1, the LED's normal state reflects the link state
  44. + of the named network device.
  45. + Setting this value also immediately changes the LED state.
  46. +
  47. +What: /sys/class/leds/<led>/tx
  48. +Date: Dec 2017
  49. +KernelVersion: 4.16
  50. +Contact: linux-leds@vger.kernel.org
  51. +Description:
  52. + Signal transmission of data on the named network device.
  53. + If set to 0 (default), the LED will not blink on transmission.
  54. + If set to 1, the LED will blink for the milliseconds specified
  55. + in interval to signal transmission.
  56. +
  57. +What: /sys/class/leds/<led>/rx
  58. +Date: Dec 2017
  59. +KernelVersion: 4.16
  60. +Contact: linux-leds@vger.kernel.org
  61. +Description:
  62. + Signal reception of data on the named network device.
  63. + If set to 0 (default), the LED will not blink on reception.
  64. + If set to 1, the LED will blink for the milliseconds specified
  65. + in interval to signal reception.
  66. --- a/drivers/leds/trigger/Kconfig
  67. +++ b/drivers/leds/trigger/Kconfig
  68. @@ -126,4 +126,11 @@ config LEDS_TRIGGER_PANIC
  69. a different trigger.
  70. If unsure, say Y.
  71. +config LEDS_TRIGGER_NETDEV
  72. + tristate "LED Netdev Trigger"
  73. + depends on NET && LEDS_TRIGGERS
  74. + help
  75. + This allows LEDs to be controlled by network device activity.
  76. + If unsure, say Y.
  77. +
  78. endif # LEDS_TRIGGERS
  79. --- a/drivers/leds/trigger/Makefile
  80. +++ b/drivers/leds/trigger/Makefile
  81. @@ -11,3 +11,4 @@ obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) +=
  82. obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
  83. obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
  84. obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
  85. +obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
  86. --- /dev/null
  87. +++ b/drivers/leds/trigger/ledtrig-netdev.c
  88. @@ -0,0 +1,496 @@
  89. +// SPDX-License-Identifier: GPL-2.0
  90. +// Copyright 2017 Ben Whitten <ben.whitten@gmail.com>
  91. +// Copyright 2007 Oliver Jowett <oliver@opencloud.com>
  92. +//
  93. +// LED Kernel Netdev Trigger
  94. +//
  95. +// Toggles the LED to reflect the link and traffic state of a named net device
  96. +//
  97. +// Derived from ledtrig-timer.c which is:
  98. +// Copyright 2005-2006 Openedhand Ltd.
  99. +// Author: Richard Purdie <rpurdie@openedhand.com>
  100. +
  101. +#include <linux/atomic.h>
  102. +#include <linux/ctype.h>
  103. +#include <linux/device.h>
  104. +#include <linux/init.h>
  105. +#include <linux/jiffies.h>
  106. +#include <linux/kernel.h>
  107. +#include <linux/leds.h>
  108. +#include <linux/list.h>
  109. +#include <linux/module.h>
  110. +#include <linux/netdevice.h>
  111. +#include <linux/spinlock.h>
  112. +#include <linux/timer.h>
  113. +#include "../leds.h"
  114. +
  115. +/*
  116. + * Configurable sysfs attributes:
  117. + *
  118. + * device_name - network device name to monitor
  119. + * interval - duration of LED blink, in milliseconds
  120. + * link - LED's normal state reflects whether the link is up
  121. + * (has carrier) or not
  122. + * tx - LED blinks on transmitted data
  123. + * rx - LED blinks on receive data
  124. + *
  125. + */
  126. +
  127. +struct led_netdev_data {
  128. + spinlock_t lock;
  129. +
  130. + struct delayed_work work;
  131. + struct notifier_block notifier;
  132. +
  133. + struct led_classdev *led_cdev;
  134. + struct net_device *net_dev;
  135. +
  136. + char device_name[IFNAMSIZ];
  137. + atomic_t interval;
  138. + unsigned int last_activity;
  139. +
  140. + unsigned long mode;
  141. +#define NETDEV_LED_LINK 0
  142. +#define NETDEV_LED_TX 1
  143. +#define NETDEV_LED_RX 2
  144. +#define NETDEV_LED_MODE_LINKUP 3
  145. +};
  146. +
  147. +enum netdev_led_attr {
  148. + NETDEV_ATTR_LINK,
  149. + NETDEV_ATTR_TX,
  150. + NETDEV_ATTR_RX
  151. +};
  152. +
  153. +static void set_baseline_state(struct led_netdev_data *trigger_data)
  154. +{
  155. + int current_brightness;
  156. + struct led_classdev *led_cdev = trigger_data->led_cdev;
  157. +
  158. + current_brightness = led_cdev->brightness;
  159. + if (current_brightness)
  160. + led_cdev->blink_brightness = current_brightness;
  161. + if (!led_cdev->blink_brightness)
  162. + led_cdev->blink_brightness = led_cdev->max_brightness;
  163. +
  164. + if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode))
  165. + led_set_brightness(led_cdev, LED_OFF);
  166. + else {
  167. + if (test_bit(NETDEV_LED_LINK, &trigger_data->mode))
  168. + led_set_brightness(led_cdev,
  169. + led_cdev->blink_brightness);
  170. + else
  171. + led_set_brightness(led_cdev, LED_OFF);
  172. +
  173. + /* If we are looking for RX/TX start periodically
  174. + * checking stats
  175. + */
  176. + if (test_bit(NETDEV_LED_TX, &trigger_data->mode) ||
  177. + test_bit(NETDEV_LED_RX, &trigger_data->mode))
  178. + schedule_delayed_work(&trigger_data->work, 0);
  179. + }
  180. +}
  181. +
  182. +static ssize_t device_name_show(struct device *dev,
  183. + struct device_attribute *attr, char *buf)
  184. +{
  185. + struct led_classdev *led_cdev = dev_get_drvdata(dev);
  186. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  187. + ssize_t len;
  188. +
  189. + spin_lock_bh(&trigger_data->lock);
  190. + len = sprintf(buf, "%s\n", trigger_data->device_name);
  191. + spin_unlock_bh(&trigger_data->lock);
  192. +
  193. + return len;
  194. +}
  195. +
  196. +static ssize_t device_name_store(struct device *dev,
  197. + struct device_attribute *attr, const char *buf,
  198. + size_t size)
  199. +{
  200. + struct led_classdev *led_cdev = dev_get_drvdata(dev);
  201. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  202. +
  203. + if (size >= IFNAMSIZ)
  204. + return -EINVAL;
  205. +
  206. + cancel_delayed_work_sync(&trigger_data->work);
  207. +
  208. + spin_lock_bh(&trigger_data->lock);
  209. +
  210. + if (trigger_data->net_dev) {
  211. + dev_put(trigger_data->net_dev);
  212. + trigger_data->net_dev = NULL;
  213. + }
  214. +
  215. + strncpy(trigger_data->device_name, buf, size);
  216. + if (size > 0 && trigger_data->device_name[size - 1] == '\n')
  217. + trigger_data->device_name[size - 1] = 0;
  218. +
  219. + if (trigger_data->device_name[0] != 0)
  220. + trigger_data->net_dev =
  221. + dev_get_by_name(&init_net, trigger_data->device_name);
  222. +
  223. + clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  224. + if (trigger_data->net_dev != NULL)
  225. + if (netif_carrier_ok(trigger_data->net_dev))
  226. + set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  227. +
  228. + trigger_data->last_activity = 0;
  229. +
  230. + set_baseline_state(trigger_data);
  231. + spin_unlock_bh(&trigger_data->lock);
  232. +
  233. + return size;
  234. +}
  235. +
  236. +static DEVICE_ATTR_RW(device_name);
  237. +
  238. +static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
  239. + enum netdev_led_attr attr)
  240. +{
  241. + struct led_classdev *led_cdev = dev_get_drvdata(dev);
  242. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  243. + int bit;
  244. +
  245. + switch (attr) {
  246. + case NETDEV_ATTR_LINK:
  247. + bit = NETDEV_LED_LINK;
  248. + break;
  249. + case NETDEV_ATTR_TX:
  250. + bit = NETDEV_LED_TX;
  251. + break;
  252. + case NETDEV_ATTR_RX:
  253. + bit = NETDEV_LED_RX;
  254. + break;
  255. + default:
  256. + return -EINVAL;
  257. + }
  258. +
  259. + return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
  260. +}
  261. +
  262. +static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
  263. + size_t size, enum netdev_led_attr attr)
  264. +{
  265. + struct led_classdev *led_cdev = dev_get_drvdata(dev);
  266. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  267. + unsigned long state;
  268. + int ret;
  269. + int bit;
  270. +
  271. + ret = kstrtoul(buf, 0, &state);
  272. + if (ret)
  273. + return ret;
  274. +
  275. + switch (attr) {
  276. + case NETDEV_ATTR_LINK:
  277. + bit = NETDEV_LED_LINK;
  278. + break;
  279. + case NETDEV_ATTR_TX:
  280. + bit = NETDEV_LED_TX;
  281. + break;
  282. + case NETDEV_ATTR_RX:
  283. + bit = NETDEV_LED_RX;
  284. + break;
  285. + default:
  286. + return -EINVAL;
  287. + }
  288. +
  289. + cancel_delayed_work_sync(&trigger_data->work);
  290. +
  291. + if (state)
  292. + set_bit(bit, &trigger_data->mode);
  293. + else
  294. + clear_bit(bit, &trigger_data->mode);
  295. +
  296. + set_baseline_state(trigger_data);
  297. +
  298. + return size;
  299. +}
  300. +
  301. +static ssize_t link_show(struct device *dev,
  302. + struct device_attribute *attr, char *buf)
  303. +{
  304. + return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK);
  305. +}
  306. +
  307. +static ssize_t link_store(struct device *dev,
  308. + struct device_attribute *attr, const char *buf, size_t size)
  309. +{
  310. + return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK);
  311. +}
  312. +
  313. +static DEVICE_ATTR_RW(link);
  314. +
  315. +static ssize_t tx_show(struct device *dev,
  316. + struct device_attribute *attr, char *buf)
  317. +{
  318. + return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX);
  319. +}
  320. +
  321. +static ssize_t tx_store(struct device *dev,
  322. + struct device_attribute *attr, const char *buf, size_t size)
  323. +{
  324. + return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX);
  325. +}
  326. +
  327. +static DEVICE_ATTR_RW(tx);
  328. +
  329. +static ssize_t rx_show(struct device *dev,
  330. + struct device_attribute *attr, char *buf)
  331. +{
  332. + return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX);
  333. +}
  334. +
  335. +static ssize_t rx_store(struct device *dev,
  336. + struct device_attribute *attr, const char *buf, size_t size)
  337. +{
  338. + return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX);
  339. +}
  340. +
  341. +static DEVICE_ATTR_RW(rx);
  342. +
  343. +static ssize_t interval_show(struct device *dev,
  344. + struct device_attribute *attr, char *buf)
  345. +{
  346. + struct led_classdev *led_cdev = dev_get_drvdata(dev);
  347. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  348. +
  349. + return sprintf(buf, "%u\n",
  350. + jiffies_to_msecs(atomic_read(&trigger_data->interval)));
  351. +}
  352. +
  353. +static ssize_t interval_store(struct device *dev,
  354. + struct device_attribute *attr, const char *buf,
  355. + size_t size)
  356. +{
  357. + struct led_classdev *led_cdev = dev_get_drvdata(dev);
  358. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  359. + unsigned long value;
  360. + int ret;
  361. +
  362. + ret = kstrtoul(buf, 0, &value);
  363. + if (ret)
  364. + return ret;
  365. +
  366. + /* impose some basic bounds on the timer interval */
  367. + if (value >= 5 && value <= 10000) {
  368. + cancel_delayed_work_sync(&trigger_data->work);
  369. +
  370. + atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
  371. + set_baseline_state(trigger_data); /* resets timer */
  372. + }
  373. +
  374. + return size;
  375. +}
  376. +
  377. +static DEVICE_ATTR_RW(interval);
  378. +
  379. +static int netdev_trig_notify(struct notifier_block *nb,
  380. + unsigned long evt, void *dv)
  381. +{
  382. + struct net_device *dev =
  383. + netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
  384. + struct led_netdev_data *trigger_data = container_of(nb,
  385. + struct
  386. + led_netdev_data,
  387. + notifier);
  388. +
  389. + if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
  390. + && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
  391. + && evt != NETDEV_CHANGENAME)
  392. + return NOTIFY_DONE;
  393. +
  394. + if (strcmp(dev->name, trigger_data->device_name))
  395. + return NOTIFY_DONE;
  396. +
  397. + cancel_delayed_work_sync(&trigger_data->work);
  398. +
  399. + spin_lock_bh(&trigger_data->lock);
  400. +
  401. + clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  402. + switch (evt) {
  403. + case NETDEV_REGISTER:
  404. + if (trigger_data->net_dev)
  405. + dev_put(trigger_data->net_dev);
  406. + dev_hold(dev);
  407. + trigger_data->net_dev = dev;
  408. + break;
  409. + case NETDEV_CHANGENAME:
  410. + case NETDEV_UNREGISTER:
  411. + if (trigger_data->net_dev) {
  412. + dev_put(trigger_data->net_dev);
  413. + trigger_data->net_dev = NULL;
  414. + }
  415. + break;
  416. + case NETDEV_UP:
  417. + case NETDEV_CHANGE:
  418. + if (netif_carrier_ok(dev))
  419. + set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode);
  420. + break;
  421. + }
  422. +
  423. + set_baseline_state(trigger_data);
  424. +
  425. + spin_unlock_bh(&trigger_data->lock);
  426. +
  427. + return NOTIFY_DONE;
  428. +}
  429. +
  430. +/* here's the real work! */
  431. +static void netdev_trig_work(struct work_struct *work)
  432. +{
  433. + struct led_netdev_data *trigger_data = container_of(work,
  434. + struct
  435. + led_netdev_data,
  436. + work.work);
  437. + struct rtnl_link_stats64 *dev_stats;
  438. + unsigned int new_activity;
  439. + struct rtnl_link_stats64 temp;
  440. + unsigned long interval;
  441. + int invert;
  442. +
  443. + /* If we dont have a device, insure we are off */
  444. + if (!trigger_data->net_dev) {
  445. + led_set_brightness(trigger_data->led_cdev, LED_OFF);
  446. + return;
  447. + }
  448. +
  449. + /* If we are not looking for RX/TX then return */
  450. + if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) &&
  451. + !test_bit(NETDEV_LED_RX, &trigger_data->mode))
  452. + return;
  453. +
  454. + dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
  455. + new_activity =
  456. + (test_bit(NETDEV_LED_TX, &trigger_data->mode) ?
  457. + dev_stats->tx_packets : 0) +
  458. + (test_bit(NETDEV_LED_RX, &trigger_data->mode) ?
  459. + dev_stats->rx_packets : 0);
  460. +
  461. + if (trigger_data->last_activity != new_activity) {
  462. + led_stop_software_blink(trigger_data->led_cdev);
  463. +
  464. + invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode);
  465. + interval = jiffies_to_msecs(
  466. + atomic_read(&trigger_data->interval));
  467. + /* base state is ON (link present) */
  468. + led_blink_set_oneshot(trigger_data->led_cdev,
  469. + &interval,
  470. + &interval,
  471. + invert);
  472. + trigger_data->last_activity = new_activity;
  473. + }
  474. +
  475. + schedule_delayed_work(&trigger_data->work,
  476. + (atomic_read(&trigger_data->interval)*2));
  477. +}
  478. +
  479. +static void netdev_trig_activate(struct led_classdev *led_cdev)
  480. +{
  481. + struct led_netdev_data *trigger_data;
  482. + int rc;
  483. +
  484. + trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
  485. + if (!trigger_data)
  486. + return;
  487. +
  488. + spin_lock_init(&trigger_data->lock);
  489. +
  490. + trigger_data->notifier.notifier_call = netdev_trig_notify;
  491. + trigger_data->notifier.priority = 10;
  492. +
  493. + INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
  494. +
  495. + trigger_data->led_cdev = led_cdev;
  496. + trigger_data->net_dev = NULL;
  497. + trigger_data->device_name[0] = 0;
  498. +
  499. + trigger_data->mode = 0;
  500. + atomic_set(&trigger_data->interval, msecs_to_jiffies(50));
  501. + trigger_data->last_activity = 0;
  502. +
  503. + led_cdev->trigger_data = trigger_data;
  504. +
  505. + rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
  506. + if (rc)
  507. + goto err_out;
  508. + rc = device_create_file(led_cdev->dev, &dev_attr_link);
  509. + if (rc)
  510. + goto err_out_device_name;
  511. + rc = device_create_file(led_cdev->dev, &dev_attr_rx);
  512. + if (rc)
  513. + goto err_out_link;
  514. + rc = device_create_file(led_cdev->dev, &dev_attr_tx);
  515. + if (rc)
  516. + goto err_out_rx;
  517. + rc = device_create_file(led_cdev->dev, &dev_attr_interval);
  518. + if (rc)
  519. + goto err_out_tx;
  520. + rc = register_netdevice_notifier(&trigger_data->notifier);
  521. + if (rc)
  522. + goto err_out_interval;
  523. + return;
  524. +
  525. +err_out_interval:
  526. + device_remove_file(led_cdev->dev, &dev_attr_interval);
  527. +err_out_tx:
  528. + device_remove_file(led_cdev->dev, &dev_attr_tx);
  529. +err_out_rx:
  530. + device_remove_file(led_cdev->dev, &dev_attr_rx);
  531. +err_out_link:
  532. + device_remove_file(led_cdev->dev, &dev_attr_link);
  533. +err_out_device_name:
  534. + device_remove_file(led_cdev->dev, &dev_attr_device_name);
  535. +err_out:
  536. + led_cdev->trigger_data = NULL;
  537. + kfree(trigger_data);
  538. +}
  539. +
  540. +static void netdev_trig_deactivate(struct led_classdev *led_cdev)
  541. +{
  542. + struct led_netdev_data *trigger_data = led_cdev->trigger_data;
  543. +
  544. + if (trigger_data) {
  545. + unregister_netdevice_notifier(&trigger_data->notifier);
  546. +
  547. + device_remove_file(led_cdev->dev, &dev_attr_device_name);
  548. + device_remove_file(led_cdev->dev, &dev_attr_link);
  549. + device_remove_file(led_cdev->dev, &dev_attr_rx);
  550. + device_remove_file(led_cdev->dev, &dev_attr_tx);
  551. + device_remove_file(led_cdev->dev, &dev_attr_interval);
  552. +
  553. + cancel_delayed_work_sync(&trigger_data->work);
  554. +
  555. + if (trigger_data->net_dev)
  556. + dev_put(trigger_data->net_dev);
  557. +
  558. + kfree(trigger_data);
  559. + }
  560. +}
  561. +
  562. +static struct led_trigger netdev_led_trigger = {
  563. + .name = "netdev",
  564. + .activate = netdev_trig_activate,
  565. + .deactivate = netdev_trig_deactivate,
  566. +};
  567. +
  568. +static int __init netdev_trig_init(void)
  569. +{
  570. + return led_trigger_register(&netdev_led_trigger);
  571. +}
  572. +
  573. +static void __exit netdev_trig_exit(void)
  574. +{
  575. + led_trigger_unregister(&netdev_led_trigger);
  576. +}
  577. +
  578. +module_init(netdev_trig_init);
  579. +module_exit(netdev_trig_exit);
  580. +
  581. +MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
  582. +MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
  583. +MODULE_DESCRIPTION("Netdev LED trigger");
  584. +MODULE_LICENSE("GPL v2");