106f502f5SBen Whitten // SPDX-License-Identifier: GPL-2.0
206f502f5SBen Whitten // Copyright 2017 Ben Whitten <[email protected]>
306f502f5SBen Whitten // Copyright 2007 Oliver Jowett <[email protected]>
406f502f5SBen Whitten //
506f502f5SBen Whitten // LED Kernel Netdev Trigger
606f502f5SBen Whitten //
706f502f5SBen Whitten // Toggles the LED to reflect the link and traffic state of a named net device
806f502f5SBen Whitten //
906f502f5SBen Whitten // Derived from ledtrig-timer.c which is:
1006f502f5SBen Whitten // Copyright 2005-2006 Openedhand Ltd.
1106f502f5SBen Whitten // Author: Richard Purdie <[email protected]>
1206f502f5SBen Whitten
1306f502f5SBen Whitten #include <linux/atomic.h>
1406f502f5SBen Whitten #include <linux/ctype.h>
1506f502f5SBen Whitten #include <linux/device.h>
16d5e01266SChristian Marangi #include <linux/ethtool.h>
1706f502f5SBen Whitten #include <linux/init.h>
1806f502f5SBen Whitten #include <linux/jiffies.h>
1906f502f5SBen Whitten #include <linux/kernel.h>
2006f502f5SBen Whitten #include <linux/leds.h>
2106cdca01SChristian Marangi #include <linux/linkmode.h>
2206f502f5SBen Whitten #include <linux/list.h>
2306f502f5SBen Whitten #include <linux/module.h>
2406f502f5SBen Whitten #include <linux/netdevice.h>
25d1b9e139SChristian Marangi #include <linux/mutex.h>
2606cdca01SChristian Marangi #include <linux/phy.h>
27d5e01266SChristian Marangi #include <linux/rtnetlink.h>
2806f502f5SBen Whitten #include <linux/timer.h>
2906f502f5SBen Whitten #include "../leds.h"
3006f502f5SBen Whitten
317c145a34SChristian Marangi #define NETDEV_LED_DEFAULT_INTERVAL 50
327c145a34SChristian Marangi
3306f502f5SBen Whitten /*
3406f502f5SBen Whitten * Configurable sysfs attributes:
3506f502f5SBen Whitten *
3606f502f5SBen Whitten * device_name - network device name to monitor
3706f502f5SBen Whitten * interval - duration of LED blink, in milliseconds
3806f502f5SBen Whitten * link - LED's normal state reflects whether the link is up
3906f502f5SBen Whitten * (has carrier) or not
4006f502f5SBen Whitten * tx - LED blinks on transmitted data
4106f502f5SBen Whitten * rx - LED blinks on receive data
426b08d07cSLukasz Majewski * tx_err - LED blinks on transmit error
436b08d07cSLukasz Majewski * rx_err - LED blinks on receive error
4406f502f5SBen Whitten *
454289e434SHeiner Kallweit * Note: If the user selects a mode that is not supported by hw, default
464289e434SHeiner Kallweit * behavior is to fall back to software control of the LED. However not every
474289e434SHeiner Kallweit * hw supports software control. LED callbacks brightness_set() and
484289e434SHeiner Kallweit * brightness_set_blocking() are NULL in this case. hw_control_is_supported()
494289e434SHeiner Kallweit * should use available means supported by hw to inform the user that selected
504289e434SHeiner Kallweit * mode isn't supported by hw. This could be switching off the LED or any
514289e434SHeiner Kallweit * hw blink mode. If software control fallback isn't possible, we return
524289e434SHeiner Kallweit * -EOPNOTSUPP to the user, but still store the selected mode. This is needed
534289e434SHeiner Kallweit * in case an intermediate unsupported mode is necessary to switch from one
544289e434SHeiner Kallweit * supported mode to another.
5506f502f5SBen Whitten */
5606f502f5SBen Whitten
5706f502f5SBen Whitten struct led_netdev_data {
58d1b9e139SChristian Marangi struct mutex lock;
5906f502f5SBen Whitten
6006f502f5SBen Whitten struct delayed_work work;
6106f502f5SBen Whitten struct notifier_block notifier;
6206f502f5SBen Whitten
6306f502f5SBen Whitten struct led_classdev *led_cdev;
6406f502f5SBen Whitten struct net_device *net_dev;
6506f502f5SBen Whitten
6606f502f5SBen Whitten char device_name[IFNAMSIZ];
6706f502f5SBen Whitten atomic_t interval;
6806f502f5SBen Whitten unsigned int last_activity;
6906f502f5SBen Whitten
7006f502f5SBen Whitten unsigned long mode;
71*c629c972SMarek Vasut unsigned long blink_delay;
72d5e01266SChristian Marangi int link_speed;
7306cdca01SChristian Marangi __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes);
74f22f95b9SChristian Marangi u8 duplex;
75d5e01266SChristian Marangi
76e2f24cb1SChristian Marangi bool carrier_link_up;
774fd1b6d4SChristian Marangi bool hw_control;
7806f502f5SBen Whitten };
7906f502f5SBen Whitten
8006cdca01SChristian Marangi static const struct attribute_group netdev_trig_link_speed_attrs_group;
8106cdca01SChristian Marangi
set_baseline_state(struct led_netdev_data * trigger_data)8206f502f5SBen Whitten static void set_baseline_state(struct led_netdev_data *trigger_data)
8306f502f5SBen Whitten {
8406f502f5SBen Whitten int current_brightness;
8506f502f5SBen Whitten struct led_classdev *led_cdev = trigger_data->led_cdev;
8606f502f5SBen Whitten
877c145a34SChristian Marangi /* Already validated, hw control is possible with the requested mode */
887c145a34SChristian Marangi if (trigger_data->hw_control) {
897c145a34SChristian Marangi led_cdev->hw_control_set(led_cdev, trigger_data->mode);
90*c629c972SMarek Vasut if (led_cdev->blink_set) {
91*c629c972SMarek Vasut led_cdev->blink_set(led_cdev, &trigger_data->blink_delay,
92*c629c972SMarek Vasut &trigger_data->blink_delay);
93*c629c972SMarek Vasut }
947c145a34SChristian Marangi
957c145a34SChristian Marangi return;
967c145a34SChristian Marangi }
977c145a34SChristian Marangi
9806f502f5SBen Whitten current_brightness = led_cdev->brightness;
9906f502f5SBen Whitten if (current_brightness)
10006f502f5SBen Whitten led_cdev->blink_brightness = current_brightness;
10106f502f5SBen Whitten if (!led_cdev->blink_brightness)
10206f502f5SBen Whitten led_cdev->blink_brightness = led_cdev->max_brightness;
10306f502f5SBen Whitten
104e2f24cb1SChristian Marangi if (!trigger_data->carrier_link_up) {
10506f502f5SBen Whitten led_set_brightness(led_cdev, LED_OFF);
106e2f24cb1SChristian Marangi } else {
107d5e01266SChristian Marangi bool blink_on = false;
108d5e01266SChristian Marangi
109bdec9cb8SChristian Marangi if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
110d5e01266SChristian Marangi blink_on = true;
111d5e01266SChristian Marangi
112d5e01266SChristian Marangi if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) &&
113d5e01266SChristian Marangi trigger_data->link_speed == SPEED_10)
114d5e01266SChristian Marangi blink_on = true;
115d5e01266SChristian Marangi
116d5e01266SChristian Marangi if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) &&
117d5e01266SChristian Marangi trigger_data->link_speed == SPEED_100)
118d5e01266SChristian Marangi blink_on = true;
119d5e01266SChristian Marangi
120d5e01266SChristian Marangi if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) &&
121d5e01266SChristian Marangi trigger_data->link_speed == SPEED_1000)
122d5e01266SChristian Marangi blink_on = true;
123d5e01266SChristian Marangi
12459b3e31eSDaniel Golle if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
12559b3e31eSDaniel Golle trigger_data->link_speed == SPEED_2500)
12659b3e31eSDaniel Golle blink_on = true;
12759b3e31eSDaniel Golle
12859b3e31eSDaniel Golle if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
12959b3e31eSDaniel Golle trigger_data->link_speed == SPEED_5000)
13059b3e31eSDaniel Golle blink_on = true;
13159b3e31eSDaniel Golle
13259b3e31eSDaniel Golle if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
13359b3e31eSDaniel Golle trigger_data->link_speed == SPEED_10000)
13459b3e31eSDaniel Golle blink_on = true;
13559b3e31eSDaniel Golle
136f22f95b9SChristian Marangi if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
137f22f95b9SChristian Marangi trigger_data->duplex == DUPLEX_HALF)
138f22f95b9SChristian Marangi blink_on = true;
139f22f95b9SChristian Marangi
140f22f95b9SChristian Marangi if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) &&
141f22f95b9SChristian Marangi trigger_data->duplex == DUPLEX_FULL)
142f22f95b9SChristian Marangi blink_on = true;
143f22f95b9SChristian Marangi
144d5e01266SChristian Marangi if (blink_on)
14506f502f5SBen Whitten led_set_brightness(led_cdev,
14606f502f5SBen Whitten led_cdev->blink_brightness);
14706f502f5SBen Whitten else
14806f502f5SBen Whitten led_set_brightness(led_cdev, LED_OFF);
14906f502f5SBen Whitten
15006f502f5SBen Whitten /* If we are looking for RX/TX start periodically
15106f502f5SBen Whitten * checking stats
15206f502f5SBen Whitten */
153bdec9cb8SChristian Marangi if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
1546b08d07cSLukasz Majewski test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
1556b08d07cSLukasz Majewski test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
1566b08d07cSLukasz Majewski test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
15706f502f5SBen Whitten schedule_delayed_work(&trigger_data->work, 0);
15806f502f5SBen Whitten }
15906f502f5SBen Whitten }
16006f502f5SBen Whitten
supports_hw_control(struct led_classdev * led_cdev)1616352f25fSChristian Marangi static bool supports_hw_control(struct led_classdev *led_cdev)
1626352f25fSChristian Marangi {
1636352f25fSChristian Marangi if (!led_cdev->hw_control_get || !led_cdev->hw_control_set ||
1646352f25fSChristian Marangi !led_cdev->hw_control_is_supported)
1656352f25fSChristian Marangi return false;
1666352f25fSChristian Marangi
1676352f25fSChristian Marangi return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name);
1686352f25fSChristian Marangi }
1696352f25fSChristian Marangi
17033ec0b53SAndrew Lunn /*
17133ec0b53SAndrew Lunn * Validate the configured netdev is the same as the one associated with
17233ec0b53SAndrew Lunn * the LED driver in hw control.
17333ec0b53SAndrew Lunn */
validate_net_dev(struct led_classdev * led_cdev,struct net_device * net_dev)17433ec0b53SAndrew Lunn static bool validate_net_dev(struct led_classdev *led_cdev,
17533ec0b53SAndrew Lunn struct net_device *net_dev)
17633ec0b53SAndrew Lunn {
17733ec0b53SAndrew Lunn struct device *dev = led_cdev->hw_control_get_device(led_cdev);
17833ec0b53SAndrew Lunn struct net_device *ndev;
17933ec0b53SAndrew Lunn
18033ec0b53SAndrew Lunn if (!dev)
18133ec0b53SAndrew Lunn return false;
18233ec0b53SAndrew Lunn
18333ec0b53SAndrew Lunn ndev = to_net_dev(dev);
18433ec0b53SAndrew Lunn
18533ec0b53SAndrew Lunn return ndev == net_dev;
18633ec0b53SAndrew Lunn }
18733ec0b53SAndrew Lunn
can_hw_control(struct led_netdev_data * trigger_data)1884fd1b6d4SChristian Marangi static bool can_hw_control(struct led_netdev_data *trigger_data)
1894fd1b6d4SChristian Marangi {
1907c145a34SChristian Marangi unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL);
1917c145a34SChristian Marangi unsigned int interval = atomic_read(&trigger_data->interval);
1926352f25fSChristian Marangi struct led_classdev *led_cdev = trigger_data->led_cdev;
1937c145a34SChristian Marangi int ret;
1946352f25fSChristian Marangi
1956352f25fSChristian Marangi if (!supports_hw_control(led_cdev))
1966352f25fSChristian Marangi return false;
1976352f25fSChristian Marangi
1987c145a34SChristian Marangi /*
1997c145a34SChristian Marangi * Interval must be set to the default
2007c145a34SChristian Marangi * value. Any different value is rejected if in hw
2017c145a34SChristian Marangi * control.
2027c145a34SChristian Marangi */
2037c145a34SChristian Marangi if (interval != default_interval)
2044fd1b6d4SChristian Marangi return false;
2057c145a34SChristian Marangi
2067c145a34SChristian Marangi /*
2077c145a34SChristian Marangi * net_dev must be set with hw control, otherwise no
2087c145a34SChristian Marangi * blinking can be happening and there is nothing to
20933ec0b53SAndrew Lunn * offloaded. Additionally, for hw control to be
21033ec0b53SAndrew Lunn * valid, the configured netdev must be the same as
21133ec0b53SAndrew Lunn * netdev associated to the LED.
2127c145a34SChristian Marangi */
21333ec0b53SAndrew Lunn if (!validate_net_dev(led_cdev, trigger_data->net_dev))
2147c145a34SChristian Marangi return false;
2157c145a34SChristian Marangi
2167c145a34SChristian Marangi /* Check if the requested mode is supported */
2177c145a34SChristian Marangi ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode);
2187c145a34SChristian Marangi /* Fall back to software blinking if not supported */
2197c145a34SChristian Marangi if (ret == -EOPNOTSUPP)
2207c145a34SChristian Marangi return false;
2217c145a34SChristian Marangi if (ret) {
2227c145a34SChristian Marangi dev_warn(led_cdev->dev,
2237c145a34SChristian Marangi "Current mode check failed with error %d\n", ret);
2247c145a34SChristian Marangi return false;
2257c145a34SChristian Marangi }
2267c145a34SChristian Marangi
2277c145a34SChristian Marangi return true;
2284fd1b6d4SChristian Marangi }
2294fd1b6d4SChristian Marangi
get_device_state(struct led_netdev_data * trigger_data)230d5e01266SChristian Marangi static void get_device_state(struct led_netdev_data *trigger_data)
231d5e01266SChristian Marangi {
232d5e01266SChristian Marangi struct ethtool_link_ksettings cmd;
233d5e01266SChristian Marangi
234d5e01266SChristian Marangi trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
23506cdca01SChristian Marangi
23606cdca01SChristian Marangi if (__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
237d5e01266SChristian Marangi return;
238d5e01266SChristian Marangi
23906cdca01SChristian Marangi if (trigger_data->carrier_link_up) {
240d5e01266SChristian Marangi trigger_data->link_speed = cmd.base.speed;
241f22f95b9SChristian Marangi trigger_data->duplex = cmd.base.duplex;
242f22f95b9SChristian Marangi }
24306cdca01SChristian Marangi
24406cdca01SChristian Marangi /*
24506cdca01SChristian Marangi * Have a local copy of the link speed supported to avoid rtnl lock every time
24606cdca01SChristian Marangi * modes are refreshed on any change event
24706cdca01SChristian Marangi */
24806cdca01SChristian Marangi linkmode_copy(trigger_data->supported_link_modes, cmd.link_modes.supported);
249d5e01266SChristian Marangi }
250d5e01266SChristian Marangi
device_name_show(struct device * dev,struct device_attribute * attr,char * buf)25106f502f5SBen Whitten static ssize_t device_name_show(struct device *dev,
25206f502f5SBen Whitten struct device_attribute *attr, char *buf)
25306f502f5SBen Whitten {
254f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
25506f502f5SBen Whitten ssize_t len;
25606f502f5SBen Whitten
257d1b9e139SChristian Marangi mutex_lock(&trigger_data->lock);
25806f502f5SBen Whitten len = sprintf(buf, "%s\n", trigger_data->device_name);
259d1b9e139SChristian Marangi mutex_unlock(&trigger_data->lock);
26006f502f5SBen Whitten
26106f502f5SBen Whitten return len;
26206f502f5SBen Whitten }
26306f502f5SBen Whitten
set_device_name(struct led_netdev_data * trigger_data,const char * name,size_t size)26428a6a2efSAndrew Lunn static int set_device_name(struct led_netdev_data *trigger_data,
26528a6a2efSAndrew Lunn const char *name, size_t size)
26606f502f5SBen Whitten {
267259e33cbSChristian Marangi if (size >= IFNAMSIZ)
268259e33cbSChristian Marangi return -EINVAL;
269259e33cbSChristian Marangi
27006f502f5SBen Whitten cancel_delayed_work_sync(&trigger_data->work);
27106f502f5SBen Whitten
272fe2b1226SHeiner Kallweit /*
273fe2b1226SHeiner Kallweit * Take RTNL lock before trigger_data lock to prevent potential
274fe2b1226SHeiner Kallweit * deadlock with netdev notifier registration.
275fe2b1226SHeiner Kallweit */
276fe2b1226SHeiner Kallweit rtnl_lock();
277d1b9e139SChristian Marangi mutex_lock(&trigger_data->lock);
27806f502f5SBen Whitten
27906f502f5SBen Whitten if (trigger_data->net_dev) {
28006f502f5SBen Whitten dev_put(trigger_data->net_dev);
28106f502f5SBen Whitten trigger_data->net_dev = NULL;
28206f502f5SBen Whitten }
28306f502f5SBen Whitten
28428a6a2efSAndrew Lunn memcpy(trigger_data->device_name, name, size);
28590934643SRasmus Villemoes trigger_data->device_name[size] = 0;
28606f502f5SBen Whitten if (size > 0 && trigger_data->device_name[size - 1] == '\n')
28706f502f5SBen Whitten trigger_data->device_name[size - 1] = 0;
28806f502f5SBen Whitten
28906f502f5SBen Whitten if (trigger_data->device_name[0] != 0)
29006f502f5SBen Whitten trigger_data->net_dev =
29106f502f5SBen Whitten dev_get_by_name(&init_net, trigger_data->device_name);
29206f502f5SBen Whitten
293e2f24cb1SChristian Marangi trigger_data->carrier_link_up = false;
294d5e01266SChristian Marangi trigger_data->link_speed = SPEED_UNKNOWN;
295f22f95b9SChristian Marangi trigger_data->duplex = DUPLEX_UNKNOWN;
296fe2b1226SHeiner Kallweit if (trigger_data->net_dev)
297d5e01266SChristian Marangi get_device_state(trigger_data);
29806f502f5SBen Whitten
29906f502f5SBen Whitten trigger_data->last_activity = 0;
30006f502f5SBen Whitten
301f574751cSHeiner Kallweit /* Skip if we're called from netdev_trig_activate() and hw_control is true */
302f574751cSHeiner Kallweit if (!trigger_data->hw_control || led_get_trigger_data(trigger_data->led_cdev))
30306f502f5SBen Whitten set_baseline_state(trigger_data);
304f574751cSHeiner Kallweit
305d1b9e139SChristian Marangi mutex_unlock(&trigger_data->lock);
306fe2b1226SHeiner Kallweit rtnl_unlock();
30706f502f5SBen Whitten
30828a6a2efSAndrew Lunn return 0;
30928a6a2efSAndrew Lunn }
31028a6a2efSAndrew Lunn
device_name_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)31128a6a2efSAndrew Lunn static ssize_t device_name_store(struct device *dev,
31228a6a2efSAndrew Lunn struct device_attribute *attr, const char *buf,
31328a6a2efSAndrew Lunn size_t size)
31428a6a2efSAndrew Lunn {
31528a6a2efSAndrew Lunn struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
31628a6a2efSAndrew Lunn int ret;
31728a6a2efSAndrew Lunn
31828a6a2efSAndrew Lunn ret = set_device_name(trigger_data, buf, size);
31928a6a2efSAndrew Lunn
32028a6a2efSAndrew Lunn if (ret < 0)
32128a6a2efSAndrew Lunn return ret;
32206cdca01SChristian Marangi
32306cdca01SChristian Marangi /* Refresh link_speed visibility */
32406cdca01SChristian Marangi sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group);
32506cdca01SChristian Marangi
32606f502f5SBen Whitten return size;
32706f502f5SBen Whitten }
32806f502f5SBen Whitten
32906f502f5SBen Whitten static DEVICE_ATTR_RW(device_name);
33006f502f5SBen Whitten
netdev_led_attr_show(struct device * dev,char * buf,enum led_trigger_netdev_modes attr)33106f502f5SBen Whitten static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
332bdec9cb8SChristian Marangi enum led_trigger_netdev_modes attr)
33306f502f5SBen Whitten {
334f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
33506f502f5SBen Whitten int bit;
33606f502f5SBen Whitten
33706f502f5SBen Whitten switch (attr) {
338bdec9cb8SChristian Marangi case TRIGGER_NETDEV_LINK:
339d5e01266SChristian Marangi case TRIGGER_NETDEV_LINK_10:
340d5e01266SChristian Marangi case TRIGGER_NETDEV_LINK_100:
341d5e01266SChristian Marangi case TRIGGER_NETDEV_LINK_1000:
34259b3e31eSDaniel Golle case TRIGGER_NETDEV_LINK_2500:
34359b3e31eSDaniel Golle case TRIGGER_NETDEV_LINK_5000:
34459b3e31eSDaniel Golle case TRIGGER_NETDEV_LINK_10000:
345f22f95b9SChristian Marangi case TRIGGER_NETDEV_HALF_DUPLEX:
346f22f95b9SChristian Marangi case TRIGGER_NETDEV_FULL_DUPLEX:
347bdec9cb8SChristian Marangi case TRIGGER_NETDEV_TX:
348bdec9cb8SChristian Marangi case TRIGGER_NETDEV_RX:
3496b08d07cSLukasz Majewski case TRIGGER_NETDEV_TX_ERR:
3506b08d07cSLukasz Majewski case TRIGGER_NETDEV_RX_ERR:
351bdec9cb8SChristian Marangi bit = attr;
35206f502f5SBen Whitten break;
35306f502f5SBen Whitten default:
35406f502f5SBen Whitten return -EINVAL;
35506f502f5SBen Whitten }
35606f502f5SBen Whitten
35706f502f5SBen Whitten return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
35806f502f5SBen Whitten }
35906f502f5SBen Whitten
netdev_led_attr_store(struct device * dev,const char * buf,size_t size,enum led_trigger_netdev_modes attr)36006f502f5SBen Whitten static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
361bdec9cb8SChristian Marangi size_t size, enum led_trigger_netdev_modes attr)
36206f502f5SBen Whitten {
363f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
3644289e434SHeiner Kallweit struct led_classdev *led_cdev = trigger_data->led_cdev;
365d5e01266SChristian Marangi unsigned long state, mode = trigger_data->mode;
36606f502f5SBen Whitten int ret;
36706f502f5SBen Whitten int bit;
36806f502f5SBen Whitten
36906f502f5SBen Whitten ret = kstrtoul(buf, 0, &state);
37006f502f5SBen Whitten if (ret)
37106f502f5SBen Whitten return ret;
37206f502f5SBen Whitten
37306f502f5SBen Whitten switch (attr) {
374bdec9cb8SChristian Marangi case TRIGGER_NETDEV_LINK:
375d5e01266SChristian Marangi case TRIGGER_NETDEV_LINK_10:
376d5e01266SChristian Marangi case TRIGGER_NETDEV_LINK_100:
377d5e01266SChristian Marangi case TRIGGER_NETDEV_LINK_1000:
37859b3e31eSDaniel Golle case TRIGGER_NETDEV_LINK_2500:
37959b3e31eSDaniel Golle case TRIGGER_NETDEV_LINK_5000:
38059b3e31eSDaniel Golle case TRIGGER_NETDEV_LINK_10000:
381f22f95b9SChristian Marangi case TRIGGER_NETDEV_HALF_DUPLEX:
382f22f95b9SChristian Marangi case TRIGGER_NETDEV_FULL_DUPLEX:
383bdec9cb8SChristian Marangi case TRIGGER_NETDEV_TX:
384bdec9cb8SChristian Marangi case TRIGGER_NETDEV_RX:
3856b08d07cSLukasz Majewski case TRIGGER_NETDEV_TX_ERR:
3866b08d07cSLukasz Majewski case TRIGGER_NETDEV_RX_ERR:
387bdec9cb8SChristian Marangi bit = attr;
38806f502f5SBen Whitten break;
38906f502f5SBen Whitten default:
39006f502f5SBen Whitten return -EINVAL;
39106f502f5SBen Whitten }
39206f502f5SBen Whitten
393d5e01266SChristian Marangi if (state)
394d5e01266SChristian Marangi set_bit(bit, &mode);
395d5e01266SChristian Marangi else
396d5e01266SChristian Marangi clear_bit(bit, &mode);
397d5e01266SChristian Marangi
398d5e01266SChristian Marangi if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
399d5e01266SChristian Marangi (test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
400d5e01266SChristian Marangi test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
40159b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_1000, &mode) ||
40259b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_2500, &mode) ||
40359b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_5000, &mode) ||
40459b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_10000, &mode)))
405d5e01266SChristian Marangi return -EINVAL;
406d5e01266SChristian Marangi
40706f502f5SBen Whitten cancel_delayed_work_sync(&trigger_data->work);
40806f502f5SBen Whitten
409d5e01266SChristian Marangi trigger_data->mode = mode;
4104fd1b6d4SChristian Marangi trigger_data->hw_control = can_hw_control(trigger_data);
4114fd1b6d4SChristian Marangi
4124289e434SHeiner Kallweit if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking &&
4134289e434SHeiner Kallweit !trigger_data->hw_control)
4144289e434SHeiner Kallweit return -EOPNOTSUPP;
4154289e434SHeiner Kallweit
41606f502f5SBen Whitten set_baseline_state(trigger_data);
41706f502f5SBen Whitten
41806f502f5SBen Whitten return size;
41906f502f5SBen Whitten }
42006f502f5SBen Whitten
421164b67d5SChristian Marangi #define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \
422164b67d5SChristian Marangi static ssize_t trigger_name##_show(struct device *dev, \
423164b67d5SChristian Marangi struct device_attribute *attr, char *buf) \
424164b67d5SChristian Marangi { \
425164b67d5SChristian Marangi return netdev_led_attr_show(dev, buf, trigger); \
426164b67d5SChristian Marangi } \
427164b67d5SChristian Marangi static ssize_t trigger_name##_store(struct device *dev, \
428164b67d5SChristian Marangi struct device_attribute *attr, const char *buf, size_t size) \
429164b67d5SChristian Marangi { \
430164b67d5SChristian Marangi return netdev_led_attr_store(dev, buf, size, trigger); \
431164b67d5SChristian Marangi } \
432164b67d5SChristian Marangi static DEVICE_ATTR_RW(trigger_name)
43306f502f5SBen Whitten
434164b67d5SChristian Marangi DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
435d5e01266SChristian Marangi DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
436d5e01266SChristian Marangi DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
437d5e01266SChristian Marangi DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
43859b3e31eSDaniel Golle DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500);
43959b3e31eSDaniel Golle DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);
44059b3e31eSDaniel Golle DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);
441f22f95b9SChristian Marangi DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
442f22f95b9SChristian Marangi DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
443164b67d5SChristian Marangi DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
444164b67d5SChristian Marangi DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
4456b08d07cSLukasz Majewski DEFINE_NETDEV_TRIGGER(tx_err, TRIGGER_NETDEV_TX_ERR);
4466b08d07cSLukasz Majewski DEFINE_NETDEV_TRIGGER(rx_err, TRIGGER_NETDEV_RX_ERR);
44706f502f5SBen Whitten
interval_show(struct device * dev,struct device_attribute * attr,char * buf)44806f502f5SBen Whitten static ssize_t interval_show(struct device *dev,
44906f502f5SBen Whitten struct device_attribute *attr, char *buf)
45006f502f5SBen Whitten {
451f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
45206f502f5SBen Whitten
45306f502f5SBen Whitten return sprintf(buf, "%u\n",
45406f502f5SBen Whitten jiffies_to_msecs(atomic_read(&trigger_data->interval)));
45506f502f5SBen Whitten }
45606f502f5SBen Whitten
interval_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)45706f502f5SBen Whitten static ssize_t interval_store(struct device *dev,
45806f502f5SBen Whitten struct device_attribute *attr, const char *buf,
45906f502f5SBen Whitten size_t size)
46006f502f5SBen Whitten {
461f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
462*c629c972SMarek Vasut struct led_classdev *led_cdev = trigger_data->led_cdev;
46306f502f5SBen Whitten unsigned long value;
46406f502f5SBen Whitten int ret;
46506f502f5SBen Whitten
466*c629c972SMarek Vasut if (trigger_data->hw_control && !led_cdev->blink_set)
467c84c80c7SChristian Marangi return -EINVAL;
468c84c80c7SChristian Marangi
46906f502f5SBen Whitten ret = kstrtoul(buf, 0, &value);
47006f502f5SBen Whitten if (ret)
47106f502f5SBen Whitten return ret;
47206f502f5SBen Whitten
47306f502f5SBen Whitten /* impose some basic bounds on the timer interval */
47406f502f5SBen Whitten if (value >= 5 && value <= 10000) {
475*c629c972SMarek Vasut if (trigger_data->hw_control) {
476*c629c972SMarek Vasut trigger_data->blink_delay = value;
477*c629c972SMarek Vasut } else {
47806f502f5SBen Whitten cancel_delayed_work_sync(&trigger_data->work);
47906f502f5SBen Whitten
48006f502f5SBen Whitten atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
481*c629c972SMarek Vasut }
48206f502f5SBen Whitten set_baseline_state(trigger_data); /* resets timer */
48306f502f5SBen Whitten }
48406f502f5SBen Whitten
48506f502f5SBen Whitten return size;
48606f502f5SBen Whitten }
48706f502f5SBen Whitten
48806f502f5SBen Whitten static DEVICE_ATTR_RW(interval);
48906f502f5SBen Whitten
offloaded_show(struct device * dev,struct device_attribute * attr,char * buf)49044f0fb8dSMarek Behún static ssize_t offloaded_show(struct device *dev,
491b655892fSChristian Marangi struct device_attribute *attr, char *buf)
492b655892fSChristian Marangi {
493b655892fSChristian Marangi struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
494b655892fSChristian Marangi
495b655892fSChristian Marangi return sprintf(buf, "%d\n", trigger_data->hw_control);
496b655892fSChristian Marangi }
497b655892fSChristian Marangi
49844f0fb8dSMarek Behún static DEVICE_ATTR_RO(offloaded);
499b655892fSChristian Marangi
50006cdca01SChristian Marangi #define CHECK_LINK_MODE_ATTR(link_speed) \
50106cdca01SChristian Marangi do { \
50206cdca01SChristian Marangi if (attr == &dev_attr_link_##link_speed.attr && \
50306cdca01SChristian Marangi link_ksettings.base.speed == SPEED_##link_speed) \
50406cdca01SChristian Marangi return attr->mode; \
50506cdca01SChristian Marangi } while (0)
50606cdca01SChristian Marangi
netdev_trig_link_speed_visible(struct kobject * kobj,struct attribute * attr,int n)50706cdca01SChristian Marangi static umode_t netdev_trig_link_speed_visible(struct kobject *kobj,
50806cdca01SChristian Marangi struct attribute *attr, int n)
50906cdca01SChristian Marangi {
51006cdca01SChristian Marangi struct device *dev = kobj_to_dev(kobj);
51106cdca01SChristian Marangi struct led_netdev_data *trigger_data;
51206cdca01SChristian Marangi unsigned long *supported_link_modes;
51306cdca01SChristian Marangi u32 mode;
51406cdca01SChristian Marangi
51506cdca01SChristian Marangi trigger_data = led_trigger_get_drvdata(dev);
51606cdca01SChristian Marangi supported_link_modes = trigger_data->supported_link_modes;
51706cdca01SChristian Marangi
51806cdca01SChristian Marangi /*
51906cdca01SChristian Marangi * Search in the supported link mode mask a matching supported mode.
52006cdca01SChristian Marangi * Stop at the first matching entry as we care only to check if a particular
52106cdca01SChristian Marangi * speed is supported and not the kind.
52206cdca01SChristian Marangi */
52306cdca01SChristian Marangi for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) {
52406cdca01SChristian Marangi struct ethtool_link_ksettings link_ksettings;
52506cdca01SChristian Marangi
52606cdca01SChristian Marangi ethtool_params_from_link_mode(&link_ksettings, mode);
52706cdca01SChristian Marangi
52806cdca01SChristian Marangi CHECK_LINK_MODE_ATTR(10);
52906cdca01SChristian Marangi CHECK_LINK_MODE_ATTR(100);
53006cdca01SChristian Marangi CHECK_LINK_MODE_ATTR(1000);
53106cdca01SChristian Marangi CHECK_LINK_MODE_ATTR(2500);
53206cdca01SChristian Marangi CHECK_LINK_MODE_ATTR(5000);
53306cdca01SChristian Marangi CHECK_LINK_MODE_ATTR(10000);
53406cdca01SChristian Marangi }
53506cdca01SChristian Marangi
53606cdca01SChristian Marangi return 0;
53706cdca01SChristian Marangi }
53806cdca01SChristian Marangi
53906cdca01SChristian Marangi static struct attribute *netdev_trig_link_speed_attrs[] = {
540d5e01266SChristian Marangi &dev_attr_link_10.attr,
541d5e01266SChristian Marangi &dev_attr_link_100.attr,
542d5e01266SChristian Marangi &dev_attr_link_1000.attr,
54359b3e31eSDaniel Golle &dev_attr_link_2500.attr,
54459b3e31eSDaniel Golle &dev_attr_link_5000.attr,
54559b3e31eSDaniel Golle &dev_attr_link_10000.attr,
54606cdca01SChristian Marangi NULL
54706cdca01SChristian Marangi };
54806cdca01SChristian Marangi
54906cdca01SChristian Marangi static const struct attribute_group netdev_trig_link_speed_attrs_group = {
55006cdca01SChristian Marangi .attrs = netdev_trig_link_speed_attrs,
55106cdca01SChristian Marangi .is_visible = netdev_trig_link_speed_visible,
55206cdca01SChristian Marangi };
55306cdca01SChristian Marangi
55406cdca01SChristian Marangi static struct attribute *netdev_trig_attrs[] = {
55506cdca01SChristian Marangi &dev_attr_device_name.attr,
55606cdca01SChristian Marangi &dev_attr_link.attr,
557f22f95b9SChristian Marangi &dev_attr_full_duplex.attr,
558f22f95b9SChristian Marangi &dev_attr_half_duplex.attr,
559f8112a1dSUwe Kleine-König &dev_attr_rx.attr,
560f8112a1dSUwe Kleine-König &dev_attr_tx.attr,
5616b08d07cSLukasz Majewski &dev_attr_rx_err.attr,
5626b08d07cSLukasz Majewski &dev_attr_tx_err.attr,
563f8112a1dSUwe Kleine-König &dev_attr_interval.attr,
56444f0fb8dSMarek Behún &dev_attr_offloaded.attr,
565f8112a1dSUwe Kleine-König NULL
566f8112a1dSUwe Kleine-König };
56706cdca01SChristian Marangi
56806cdca01SChristian Marangi static const struct attribute_group netdev_trig_attrs_group = {
56906cdca01SChristian Marangi .attrs = netdev_trig_attrs,
57006cdca01SChristian Marangi };
57106cdca01SChristian Marangi
57206cdca01SChristian Marangi static const struct attribute_group *netdev_trig_groups[] = {
57306cdca01SChristian Marangi &netdev_trig_attrs_group,
57406cdca01SChristian Marangi &netdev_trig_link_speed_attrs_group,
57506cdca01SChristian Marangi NULL,
57606cdca01SChristian Marangi };
577f8112a1dSUwe Kleine-König
netdev_trig_notify(struct notifier_block * nb,unsigned long evt,void * dv)57806f502f5SBen Whitten static int netdev_trig_notify(struct notifier_block *nb,
57906f502f5SBen Whitten unsigned long evt, void *dv)
58006f502f5SBen Whitten {
58106f502f5SBen Whitten struct net_device *dev =
58206f502f5SBen Whitten netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
583f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data =
584f8112a1dSUwe Kleine-König container_of(nb, struct led_netdev_data, notifier);
58506cdca01SChristian Marangi struct led_classdev *led_cdev = trigger_data->led_cdev;
58606f502f5SBen Whitten
58706f502f5SBen Whitten if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
5885f820ed5SMartin Schiller && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
5895f820ed5SMartin Schiller && evt != NETDEV_CHANGENAME)
59006f502f5SBen Whitten return NOTIFY_DONE;
59106f502f5SBen Whitten
5924cb65605SRafał Miłecki if (!(dev == trigger_data->net_dev ||
5935f820ed5SMartin Schiller (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) ||
5944cb65605SRafał Miłecki (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
59506f502f5SBen Whitten return NOTIFY_DONE;
59606f502f5SBen Whitten
59706f502f5SBen Whitten cancel_delayed_work_sync(&trigger_data->work);
59806f502f5SBen Whitten
599d1b9e139SChristian Marangi mutex_lock(&trigger_data->lock);
60006f502f5SBen Whitten
601e2f24cb1SChristian Marangi trigger_data->carrier_link_up = false;
602d5e01266SChristian Marangi trigger_data->link_speed = SPEED_UNKNOWN;
603f22f95b9SChristian Marangi trigger_data->duplex = DUPLEX_UNKNOWN;
60406f502f5SBen Whitten switch (evt) {
6055f820ed5SMartin Schiller case NETDEV_CHANGENAME:
60606f502f5SBen Whitten case NETDEV_REGISTER:
60706f502f5SBen Whitten dev_put(trigger_data->net_dev);
60806f502f5SBen Whitten dev_hold(dev);
60906f502f5SBen Whitten trigger_data->net_dev = dev;
610415798bcSChristian Marangi if (evt == NETDEV_CHANGENAME)
611415798bcSChristian Marangi get_device_state(trigger_data);
61206f502f5SBen Whitten break;
61306f502f5SBen Whitten case NETDEV_UNREGISTER:
61406f502f5SBen Whitten dev_put(trigger_data->net_dev);
61506f502f5SBen Whitten trigger_data->net_dev = NULL;
61606f502f5SBen Whitten break;
61706f502f5SBen Whitten case NETDEV_UP:
6180dfda509SMarek Vasut trigger_data->hw_control = can_hw_control(trigger_data);
6190dfda509SMarek Vasut fallthrough;
62006f502f5SBen Whitten case NETDEV_CHANGE:
621d5e01266SChristian Marangi get_device_state(trigger_data);
62206cdca01SChristian Marangi /* Refresh link_speed visibility */
62306cdca01SChristian Marangi if (evt == NETDEV_CHANGE)
62406cdca01SChristian Marangi sysfs_update_group(&led_cdev->dev->kobj,
62506cdca01SChristian Marangi &netdev_trig_link_speed_attrs_group);
62606f502f5SBen Whitten break;
62706f502f5SBen Whitten }
62806f502f5SBen Whitten
62906f502f5SBen Whitten set_baseline_state(trigger_data);
63006f502f5SBen Whitten
631d1b9e139SChristian Marangi mutex_unlock(&trigger_data->lock);
63206f502f5SBen Whitten
63306f502f5SBen Whitten return NOTIFY_DONE;
63406f502f5SBen Whitten }
63506f502f5SBen Whitten
63606f502f5SBen Whitten /* here's the real work! */
netdev_trig_work(struct work_struct * work)63706f502f5SBen Whitten static void netdev_trig_work(struct work_struct *work)
63806f502f5SBen Whitten {
639f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data =
640f8112a1dSUwe Kleine-König container_of(work, struct led_netdev_data, work.work);
64106f502f5SBen Whitten struct rtnl_link_stats64 *dev_stats;
64206f502f5SBen Whitten unsigned int new_activity;
64306f502f5SBen Whitten struct rtnl_link_stats64 temp;
64406f502f5SBen Whitten unsigned long interval;
64506f502f5SBen Whitten int invert;
64606f502f5SBen Whitten
64706f502f5SBen Whitten /* If we dont have a device, insure we are off */
64806f502f5SBen Whitten if (!trigger_data->net_dev) {
64906f502f5SBen Whitten led_set_brightness(trigger_data->led_cdev, LED_OFF);
65006f502f5SBen Whitten return;
65106f502f5SBen Whitten }
65206f502f5SBen Whitten
65306f502f5SBen Whitten /* If we are not looking for RX/TX then return */
654bdec9cb8SChristian Marangi if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
6556b08d07cSLukasz Majewski !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) &&
6566b08d07cSLukasz Majewski !test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) &&
6576b08d07cSLukasz Majewski !test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
65806f502f5SBen Whitten return;
65906f502f5SBen Whitten
66006f502f5SBen Whitten dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
66106f502f5SBen Whitten new_activity =
662bdec9cb8SChristian Marangi (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ?
66306f502f5SBen Whitten dev_stats->tx_packets : 0) +
664bdec9cb8SChristian Marangi (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ?
6656b08d07cSLukasz Majewski dev_stats->rx_packets : 0) +
6666b08d07cSLukasz Majewski (test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ?
6676b08d07cSLukasz Majewski dev_stats->tx_errors : 0) +
6686b08d07cSLukasz Majewski (test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode) ?
6696b08d07cSLukasz Majewski dev_stats->rx_errors : 0);
67006f502f5SBen Whitten
67106f502f5SBen Whitten if (trigger_data->last_activity != new_activity) {
67206f502f5SBen Whitten led_stop_software_blink(trigger_data->led_cdev);
67306f502f5SBen Whitten
674d5e01266SChristian Marangi invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
675d5e01266SChristian Marangi test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
676d5e01266SChristian Marangi test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
677f22f95b9SChristian Marangi test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) ||
67859b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) ||
67959b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) ||
68059b3e31eSDaniel Golle test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) ||
681f22f95b9SChristian Marangi test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
682f22f95b9SChristian Marangi test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
68306f502f5SBen Whitten interval = jiffies_to_msecs(
68406f502f5SBen Whitten atomic_read(&trigger_data->interval));
68506f502f5SBen Whitten /* base state is ON (link present) */
68606f502f5SBen Whitten led_blink_set_oneshot(trigger_data->led_cdev,
68706f502f5SBen Whitten &interval,
68806f502f5SBen Whitten &interval,
68906f502f5SBen Whitten invert);
69006f502f5SBen Whitten trigger_data->last_activity = new_activity;
69106f502f5SBen Whitten }
69206f502f5SBen Whitten
69306f502f5SBen Whitten schedule_delayed_work(&trigger_data->work,
69406f502f5SBen Whitten (atomic_read(&trigger_data->interval)*2));
69506f502f5SBen Whitten }
69606f502f5SBen Whitten
netdev_trig_activate(struct led_classdev * led_cdev)6972282e125SUwe Kleine-König static int netdev_trig_activate(struct led_classdev *led_cdev)
69806f502f5SBen Whitten {
69906f502f5SBen Whitten struct led_netdev_data *trigger_data;
70097c5209bSDan Carpenter unsigned long mode = 0;
7010316cc56SChristian Marangi struct device *dev;
70206f502f5SBen Whitten int rc;
70306f502f5SBen Whitten
70406f502f5SBen Whitten trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
70506f502f5SBen Whitten if (!trigger_data)
706f8112a1dSUwe Kleine-König return -ENOMEM;
70706f502f5SBen Whitten
708d1b9e139SChristian Marangi mutex_init(&trigger_data->lock);
70906f502f5SBen Whitten
71006f502f5SBen Whitten trigger_data->notifier.notifier_call = netdev_trig_notify;
71106f502f5SBen Whitten trigger_data->notifier.priority = 10;
71206f502f5SBen Whitten
71306f502f5SBen Whitten INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
71406f502f5SBen Whitten
71506f502f5SBen Whitten trigger_data->led_cdev = led_cdev;
71606f502f5SBen Whitten trigger_data->net_dev = NULL;
71706f502f5SBen Whitten trigger_data->device_name[0] = 0;
71806f502f5SBen Whitten
71906f502f5SBen Whitten trigger_data->mode = 0;
7207c145a34SChristian Marangi atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL));
72106f502f5SBen Whitten trigger_data->last_activity = 0;
72206f502f5SBen Whitten
7230316cc56SChristian Marangi /* Check if hw control is active by default on the LED.
7240316cc56SChristian Marangi * Init already enabled mode in hw control.
7250316cc56SChristian Marangi */
7267df1f14cSAndrew Lunn if (supports_hw_control(led_cdev)) {
7270316cc56SChristian Marangi dev = led_cdev->hw_control_get_device(led_cdev);
7280316cc56SChristian Marangi if (dev) {
7290316cc56SChristian Marangi const char *name = dev_name(dev);
7300316cc56SChristian Marangi
7310316cc56SChristian Marangi trigger_data->hw_control = true;
732f574751cSHeiner Kallweit set_device_name(trigger_data, name, strlen(name));
7337df1f14cSAndrew Lunn
7347df1f14cSAndrew Lunn rc = led_cdev->hw_control_get(led_cdev, &mode);
7357df1f14cSAndrew Lunn if (!rc)
7360316cc56SChristian Marangi trigger_data->mode = mode;
7370316cc56SChristian Marangi }
7380316cc56SChristian Marangi }
7390316cc56SChristian Marangi
740f8112a1dSUwe Kleine-König led_set_trigger_data(led_cdev, trigger_data);
74106f502f5SBen Whitten
74206f502f5SBen Whitten rc = register_netdevice_notifier(&trigger_data->notifier);
74306f502f5SBen Whitten if (rc)
74406f502f5SBen Whitten kfree(trigger_data);
7452282e125SUwe Kleine-König
746f8112a1dSUwe Kleine-König return rc;
74706f502f5SBen Whitten }
74806f502f5SBen Whitten
netdev_trig_deactivate(struct led_classdev * led_cdev)74906f502f5SBen Whitten static void netdev_trig_deactivate(struct led_classdev *led_cdev)
75006f502f5SBen Whitten {
751f8112a1dSUwe Kleine-König struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);
75206f502f5SBen Whitten
75306f502f5SBen Whitten unregister_netdevice_notifier(&trigger_data->notifier);
75406f502f5SBen Whitten
75506f502f5SBen Whitten cancel_delayed_work_sync(&trigger_data->work);
75606f502f5SBen Whitten
75706f502f5SBen Whitten dev_put(trigger_data->net_dev);
75806f502f5SBen Whitten
75906f502f5SBen Whitten kfree(trigger_data);
76006f502f5SBen Whitten }
76106f502f5SBen Whitten
76206f502f5SBen Whitten static struct led_trigger netdev_led_trigger = {
76306f502f5SBen Whitten .name = "netdev",
76406f502f5SBen Whitten .activate = netdev_trig_activate,
76506f502f5SBen Whitten .deactivate = netdev_trig_deactivate,
766f8112a1dSUwe Kleine-König .groups = netdev_trig_groups,
76706f502f5SBen Whitten };
76806f502f5SBen Whitten
76974cd23e8SLi Zetao module_led_trigger(netdev_led_trigger);
77006f502f5SBen Whitten
77106f502f5SBen Whitten MODULE_AUTHOR("Ben Whitten <[email protected]>");
77206f502f5SBen Whitten MODULE_AUTHOR("Oliver Jowett <[email protected]>");
77306f502f5SBen Whitten MODULE_DESCRIPTION("Netdev LED trigger");
77406f502f5SBen Whitten MODULE_LICENSE("GPL v2");
775fd14a872SHeiner Kallweit MODULE_ALIAS("ledtrig:netdev");
776