// SPDX-License-Identifier: GPL-2.0// Copyright 2017 Ben Whitten <ben.whitten@gmail.com>// Copyright 2007 Oliver Jowett <oliver@opencloud.com>//// LED Kernel Netdev Trigger//// Toggles the LED to reflect the link and traffic state of a named net device//// Derived from ledtrig-timer.c which is:// Copyright 2005-2006 Openedhand Ltd.// Author: Richard Purdie <rpurdie@openedhand.com> #include <linux/atomic.h>#include <linux/ctype.h>#include <linux/device.h>#include <linux/ethtool.h>#include <linux/init.h>#include <linux/jiffies.h>#include <linux/kernel.h>#include <linux/leds.h>#include <linux/linkmode.h>#include <linux/list.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/mutex.h>#include <linux/phy.h>#include <linux/rtnetlink.h>#include <linux/timer.h>#include <net/netdev_lock.h>#include "../leds.h" #define NETDEV_LED_DEFAULT_INTERVAL 50 /* * Configurable sysfs attributes: * * device_name - network device name to monitor * interval - duration of LED blink, in milliseconds * link - LED's normal state reflects whether the link is up * (has carrier) or not * tx - LED blinks on transmitted data * rx - LED blinks on receive data * tx_err - LED blinks on transmit error * rx_err - LED blinks on receive error * * Note: If the user selects a mode that is not supported by hw, default * behavior is to fall back to software control of the LED. However not every * hw supports software control. LED callbacks brightness_set() and * brightness_set_blocking() are NULL in this case. hw_control_is_supported() * should use available means supported by hw to inform the user that selected * mode isn't supported by hw. This could be switching off the LED or any * hw blink mode. If software control fallback isn't possible, we return * -EOPNOTSUPP to the user, but still store the selected mode. This is needed * in case an intermediate unsupported mode is necessary to switch from one * supported mode to another. */ struct led_netdev_data { struct mutex lock; struct delayed_work work; struct notifier_block notifier; struct led_classdev *led_cdev; struct net_device *net_dev; char device_name[IFNAMSIZ]; atomic_t interval; unsigned int last_activity; unsigned long mode; int link_speed; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes); u8 duplex; bool carrier_link_up; bool hw_control;}; static const struct attribute_group netdev_trig_link_speed_attrs_group; static void set_baseline_state(struct led_netdev_data *trigger_data){ int current_brightness; struct led_classdev *led_cdev = trigger_data->led_cdev; /* Already validated, hw control is possible with the requested mode */ if (trigger_data->hw_control) { led_cdev->hw_control_set(led_cdev, trigger_data->mode); return; } current_brightness = led_cdev->brightness; if (current_brightness) led_cdev->blink_brightness = current_brightness; if (!led_cdev->blink_brightness) led_cdev->blink_brightness = led_cdev->max_brightness; if (!trigger_data->carrier_link_up) { led_set_brightness(led_cdev, LED_OFF); } else { bool blink_on = false; if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode)) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) && trigger_data->link_speed == SPEED_10) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) && trigger_data->link_speed == SPEED_100) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) && trigger_data->link_speed == SPEED_1000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && trigger_data->link_speed == SPEED_2500) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && trigger_data->link_speed == SPEED_5000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && trigger_data->link_speed == SPEED_10000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) && trigger_data->link_speed == SPEED_25000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) && trigger_data->link_speed == SPEED_40000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) && trigger_data->link_speed == SPEED_50000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) && trigger_data->link_speed == SPEED_100000) blink_on = true; if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && trigger_data->duplex == DUPLEX_HALF) blink_on = true; if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) && trigger_data->duplex == DUPLEX_FULL) blink_on = true; if (blink_on) led_set_brightness(led_cdev, led_cdev->blink_brightness); else led_set_brightness(led_cdev, LED_OFF); /* If we are looking for RX/TX start periodically * checking stats */ if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode)) schedule_delayed_work(&trigger_data->work, 0); }} static bool supports_hw_control(struct led_classdev *led_cdev){ if (!led_cdev->hw_control_get || !led_cdev->hw_control_set || !led_cdev->hw_control_is_supported) return false; return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name);} /* * Validate the configured netdev is the same as the one associated with * the LED driver in hw control. */static bool validate_net_dev(struct led_classdev *led_cdev, struct net_device *net_dev){ struct device *dev = led_cdev->hw_control_get_device(led_cdev); struct net_device *ndev; if (!dev) return false; ndev = to_net_dev(dev); return ndev == net_dev;} static bool can_hw_control(struct led_netdev_data *trigger_data){ unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL); unsigned int interval = atomic_read(&trigger_data->interval); struct led_classdev *led_cdev = trigger_data->led_cdev; int ret; if (!supports_hw_control(led_cdev)) return false; /* * Interval must be set to the default * value. Any different value is rejected if in hw * control. */ if (interval != default_interval) return false; /* * net_dev must be set with hw control, otherwise no * blinking can be happening and there is nothing to * offloaded. Additionally, for hw control to be * valid, the configured netdev must be the same as * netdev associated to the LED. */ if (!validate_net_dev(led_cdev, trigger_data->net_dev)) return false; /* Check if the requested mode is supported */ ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode); /* Fall back to software blinking if not supported */ if (ret == -EOPNOTSUPP) return false; if (ret) { dev_warn(led_cdev->dev, "Current mode check failed with error %d\n", ret); return false; } return true;} static void get_device_state(struct led_netdev_data *trigger_data){ struct ethtool_link_ksettings cmd; trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); if (netif_get_link_ksettings(trigger_data->net_dev, &cmd)) return; if (trigger_data->carrier_link_up) { trigger_data->link_speed = cmd.base.speed; trigger_data->duplex = cmd.base.duplex; } /* * Have a local copy of the link speed supported to avoid rtnl lock every time * modes are refreshed on any change event */ linkmode_copy(trigger_data->supported_link_modes, cmd.link_modes.supported);} static ssize_t device_name_show(struct device *dev, struct device_attribute *attr, char *buf){ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); ssize_t len; mutex_lock(&trigger_data->lock); len = sprintf(buf, "%s\n", trigger_data->device_name); mutex_unlock(&trigger_data->lock); return len;} static int set_device_name(struct led_netdev_data *trigger_data, const char *name, size_t size){ struct net_device *new_dev = NULL; char device_name[IFNAMSIZ]; if (size >= IFNAMSIZ) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); memcpy(device_name, name, size); device_name[size] = 0; if (size > 0 && device_name[size - 1] == '\n') device_name[size - 1] = 0; /* * Lock order: rtnl_lock -> netdev instance lock -> trigger_data lock. */ rtnl_lock(); if (device_name[0]) { new_dev = dev_get_by_name(&init_net, device_name); if (new_dev) netdev_lock_ops(new_dev); } mutex_lock(&trigger_data->lock); dev_put(trigger_data->net_dev); trigger_data->net_dev = new_dev; strscpy(trigger_data->device_name, device_name); trigger_data->carrier_link_up = false; trigger_data->link_speed = SPEED_UNKNOWN; trigger_data->duplex = DUPLEX_UNKNOWN; if (trigger_data->net_dev) get_device_state(trigger_data); trigger_data->last_activity = 0; /* Skip if we're called from netdev_trig_activate() and hw_control is true */ if (!trigger_data->hw_control || led_get_trigger_data(trigger_data->led_cdev)) set_baseline_state(trigger_data); mutex_unlock(&trigger_data->lock); if (new_dev) netdev_unlock_ops(new_dev); rtnl_unlock(); return 0;} static ssize_t device_name_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size){ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); int ret; ret = set_device_name(trigger_data, buf, size); if (ret < 0) return ret; /* Refresh link_speed visibility */ sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group); return size;} static DEVICE_ATTR_RW(device_name); static ssize_t netdev_led_attr_show(struct device *dev, char *buf, enum led_trigger_netdev_modes attr){ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); int bit; switch (attr) { case TRIGGER_NETDEV_LINK: case TRIGGER_NETDEV_LINK_10: case TRIGGER_NETDEV_LINK_100: case TRIGGER_NETDEV_LINK_1000: case TRIGGER_NETDEV_LINK_2500: case TRIGGER_NETDEV_LINK_5000: case TRIGGER_NETDEV_LINK_10000: case TRIGGER_NETDEV_LINK_25000: case TRIGGER_NETDEV_LINK_40000: case TRIGGER_NETDEV_LINK_50000: case TRIGGER_NETDEV_LINK_100000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_RX: case TRIGGER_NETDEV_TX_ERR: case TRIGGER_NETDEV_RX_ERR: bit = attr; break; default: return -EINVAL; } return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));} static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, size_t size, enum led_trigger_netdev_modes attr){ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); struct led_classdev *led_cdev = trigger_data->led_cdev; unsigned long state, mode = trigger_data->mode; int ret; int bit; ret = kstrtoul(buf, 0, &state); if (ret) return ret; switch (attr) { case TRIGGER_NETDEV_LINK: case TRIGGER_NETDEV_LINK_10: case TRIGGER_NETDEV_LINK_100: case TRIGGER_NETDEV_LINK_1000: case TRIGGER_NETDEV_LINK_2500: case TRIGGER_NETDEV_LINK_5000: case TRIGGER_NETDEV_LINK_10000: case TRIGGER_NETDEV_LINK_25000: case TRIGGER_NETDEV_LINK_40000: case TRIGGER_NETDEV_LINK_50000: case TRIGGER_NETDEV_LINK_100000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_RX: case TRIGGER_NETDEV_TX_ERR: case TRIGGER_NETDEV_RX_ERR: bit = attr; break; default: return -EINVAL; } if (state) set_bit(bit, &mode); else clear_bit(bit, &mode); if (test_bit(TRIGGER_NETDEV_LINK, &mode) && (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || test_bit(TRIGGER_NETDEV_LINK_100, &mode) || test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || test_bit(TRIGGER_NETDEV_LINK_10000, &mode) || test_bit(TRIGGER_NETDEV_LINK_25000, &mode) || test_bit(TRIGGER_NETDEV_LINK_40000, &mode) || test_bit(TRIGGER_NETDEV_LINK_50000, &mode) || test_bit(TRIGGER_NETDEV_LINK_100000, &mode))) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); trigger_data->mode = mode; trigger_data->hw_control = can_hw_control(trigger_data); if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking && !trigger_data->hw_control) return -EOPNOTSUPP; set_baseline_state(trigger_data); return size;} #define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \ static ssize_t trigger_name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ return netdev_led_attr_show(dev, buf, trigger); \ } \ static ssize_t trigger_name##_store(struct device *dev, \ struct device_attribute *attr, const char *buf, size_t size) \ { \ return netdev_led_attr_store(dev, buf, size, trigger); \ } \ static DEVICE_ATTR_RW(trigger_name) DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500);DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);DEFINE_NETDEV_TRIGGER(link_25000, TRIGGER_NETDEV_LINK_25000);DEFINE_NETDEV_TRIGGER(link_40000, TRIGGER_NETDEV_LINK_40000);DEFINE_NETDEV_TRIGGER(link_50000, TRIGGER_NETDEV_LINK_50000);DEFINE_NETDEV_TRIGGER(link_100000, TRIGGER_NETDEV_LINK_100000);DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);DEFINE_NETDEV_TRIGGER(tx_err, TRIGGER_NETDEV_TX_ERR);DEFINE_NETDEV_TRIGGER(rx_err, TRIGGER_NETDEV_RX_ERR); static ssize_t interval_show(struct device *dev, struct device_attribute *attr, char *buf){ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); return sprintf(buf, "%u\n", jiffies_to_msecs(atomic_read(&trigger_data->interval)));} static ssize_t interval_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size){ struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); unsigned long value; int ret; if (trigger_data->hw_control) return -EINVAL; ret = kstrtoul(buf, 0, &value);Lines 1–500 of 814. Load more
// SPDX-License-Identifier: GPL-2.0 // Copyright 2017 Ben Whitten <ben.whitten@gmail.com> // Copyright 2007 Oliver Jowett <oliver@opencloud.com> // // LED Kernel Netdev Trigger // // Toggles the LED to reflect the link and traffic state of a named net device // // Derived from ledtrig-timer.c which is: // Copyright 2005-2006 Openedhand Ltd. // Author: Richard Purdie <rpurdie@openedhand.com> #include <linux/atomic.h> #include <linux/ctype.h> #include <linux/device.h> #include <linux/ethtool.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/leds.h> #include <linux/linkmode.h> #include <linux/list.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/mutex.h> #include <linux/phy.h> #include <linux/rtnetlink.h> #include <linux/timer.h> #include <net/netdev_lock.h> #include "../leds.h" #define NETDEV_LED_DEFAULT_INTERVAL 50 /* * Configurable sysfs attributes: * * device_name - network device name to monitor * interval - duration of LED blink, in milliseconds * link - LED's normal state reflects whether the link is up * (has carrier) or not * tx - LED blinks on transmitted data * rx - LED blinks on receive data * tx_err - LED blinks on transmit error * rx_err - LED blinks on receive error * * Note: If the user selects a mode that is not supported by hw, default * behavior is to fall back to software control of the LED. However not every * hw supports software control. LED callbacks brightness_set() and * brightness_set_blocking() are NULL in this case. hw_control_is_supported() * should use available means supported by hw to inform the user that selected * mode isn't supported by hw. This could be switching off the LED or any * hw blink mode. If software control fallback isn't possible, we return * -EOPNOTSUPP to the user, but still store the selected mode. This is needed * in case an intermediate unsupported mode is necessary to switch from one * supported mode to another. */ struct led_netdev_data { struct mutex lock; struct delayed_work work; struct notifier_block notifier; struct led_classdev *led_cdev; struct net_device *net_dev; char device_name[IFNAMSIZ]; atomic_t interval; unsigned int last_activity; unsigned long mode; int link_speed; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes); u8 duplex; bool carrier_link_up; bool hw_control; }; static const struct attribute_group netdev_trig_link_speed_attrs_group; static void set_baseline_state(struct led_netdev_data *trigger_data) { int current_brightness; struct led_classdev *led_cdev = trigger_data->led_cdev; /* Already validated, hw control is possible with the requested mode */ if (trigger_data->hw_control) { led_cdev->hw_control_set(led_cdev, trigger_data->mode); return; } current_brightness = led_cdev->brightness; if (current_brightness) led_cdev->blink_brightness = current_brightness; if (!led_cdev->blink_brightness) led_cdev->blink_brightness = led_cdev->max_brightness; if (!trigger_data->carrier_link_up) { led_set_brightness(led_cdev, LED_OFF); } else { bool blink_on = false; if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode)) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) && trigger_data->link_speed == SPEED_10) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) && trigger_data->link_speed == SPEED_100) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) && trigger_data->link_speed == SPEED_1000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && trigger_data->link_speed == SPEED_2500) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && trigger_data->link_speed == SPEED_5000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && trigger_data->link_speed == SPEED_10000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) && trigger_data->link_speed == SPEED_25000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) && trigger_data->link_speed == SPEED_40000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) && trigger_data->link_speed == SPEED_50000) blink_on = true; if (test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) && trigger_data->link_speed == SPEED_100000) blink_on = true; if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && trigger_data->duplex == DUPLEX_HALF) blink_on = true; if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) && trigger_data->duplex == DUPLEX_FULL) blink_on = true; if (blink_on) led_set_brightness(led_cdev, led_cdev->blink_brightness); else led_set_brightness(led_cdev, LED_OFF); /* If we are looking for RX/TX start periodically * checking stats */ if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) || test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode)) schedule_delayed_work(&trigger_data->work, 0); } } static bool supports_hw_control(struct led_classdev *led_cdev) { if (!led_cdev->hw_control_get || !led_cdev->hw_control_set || !led_cdev->hw_control_is_supported) return false; return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name); } /* * Validate the configured netdev is the same as the one associated with * the LED driver in hw control. */ static bool validate_net_dev(struct led_classdev *led_cdev, struct net_device *net_dev) { struct device *dev = led_cdev->hw_control_get_device(led_cdev); struct net_device *ndev; if (!dev) return false; ndev = to_net_dev(dev); return ndev == net_dev; } static bool can_hw_control(struct led_netdev_data *trigger_data) { unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL); unsigned int interval = atomic_read(&trigger_data->interval); struct led_classdev *led_cdev = trigger_data->led_cdev; int ret; if (!supports_hw_control(led_cdev)) return false; /* * Interval must be set to the default * value. Any different value is rejected if in hw * control. */ if (interval != default_interval) return false; /* * net_dev must be set with hw control, otherwise no * blinking can be happening and there is nothing to * offloaded. Additionally, for hw control to be * valid, the configured netdev must be the same as * netdev associated to the LED. */ if (!validate_net_dev(led_cdev, trigger_data->net_dev)) return false; /* Check if the requested mode is supported */ ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode); /* Fall back to software blinking if not supported */ if (ret == -EOPNOTSUPP) return false; if (ret) { dev_warn(led_cdev->dev, "Current mode check failed with error %d\n", ret); return false; } return true; } static void get_device_state(struct led_netdev_data *trigger_data) { struct ethtool_link_ksettings cmd; trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); if (netif_get_link_ksettings(trigger_data->net_dev, &cmd)) return; if (trigger_data->carrier_link_up) { trigger_data->link_speed = cmd.base.speed; trigger_data->duplex = cmd.base.duplex; } /* * Have a local copy of the link speed supported to avoid rtnl lock every time * modes are refreshed on any change event */ linkmode_copy(trigger_data->supported_link_modes, cmd.link_modes.supported); } static ssize_t device_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); ssize_t len; mutex_lock(&trigger_data->lock); len = sprintf(buf, "%s\n", trigger_data->device_name); mutex_unlock(&trigger_data->lock); return len; } static int set_device_name(struct led_netdev_data *trigger_data, const char *name, size_t size) { struct net_device *new_dev = NULL; char device_name[IFNAMSIZ]; if (size >= IFNAMSIZ) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); memcpy(device_name, name, size); device_name[size] = 0; if (size > 0 && device_name[size - 1] == '\n') device_name[size - 1] = 0; /* * Lock order: rtnl_lock -> netdev instance lock -> trigger_data lock. */ rtnl_lock(); if (device_name[0]) { new_dev = dev_get_by_name(&init_net, device_name); if (new_dev) netdev_lock_ops(new_dev); } mutex_lock(&trigger_data->lock); dev_put(trigger_data->net_dev); trigger_data->net_dev = new_dev; strscpy(trigger_data->device_name, device_name); trigger_data->carrier_link_up = false; trigger_data->link_speed = SPEED_UNKNOWN; trigger_data->duplex = DUPLEX_UNKNOWN; if (trigger_data->net_dev) get_device_state(trigger_data); trigger_data->last_activity = 0; /* Skip if we're called from netdev_trig_activate() and hw_control is true */ if (!trigger_data->hw_control || led_get_trigger_data(trigger_data->led_cdev)) set_baseline_state(trigger_data); mutex_unlock(&trigger_data->lock); if (new_dev) netdev_unlock_ops(new_dev); rtnl_unlock(); return 0; } static ssize_t device_name_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); int ret; ret = set_device_name(trigger_data, buf, size); if (ret < 0) return ret; /* Refresh link_speed visibility */ sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group); return size; } static DEVICE_ATTR_RW(device_name); static ssize_t netdev_led_attr_show(struct device *dev, char *buf, enum led_trigger_netdev_modes attr) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); int bit; switch (attr) { case TRIGGER_NETDEV_LINK: case TRIGGER_NETDEV_LINK_10: case TRIGGER_NETDEV_LINK_100: case TRIGGER_NETDEV_LINK_1000: case TRIGGER_NETDEV_LINK_2500: case TRIGGER_NETDEV_LINK_5000: case TRIGGER_NETDEV_LINK_10000: case TRIGGER_NETDEV_LINK_25000: case TRIGGER_NETDEV_LINK_40000: case TRIGGER_NETDEV_LINK_50000: case TRIGGER_NETDEV_LINK_100000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_RX: case TRIGGER_NETDEV_TX_ERR: case TRIGGER_NETDEV_RX_ERR: bit = attr; break; default: return -EINVAL; } return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode)); } static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, size_t size, enum led_trigger_netdev_modes attr) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); struct led_classdev *led_cdev = trigger_data->led_cdev; unsigned long state, mode = trigger_data->mode; int ret; int bit; ret = kstrtoul(buf, 0, &state); if (ret) return ret; switch (attr) { case TRIGGER_NETDEV_LINK: case TRIGGER_NETDEV_LINK_10: case TRIGGER_NETDEV_LINK_100: case TRIGGER_NETDEV_LINK_1000: case TRIGGER_NETDEV_LINK_2500: case TRIGGER_NETDEV_LINK_5000: case TRIGGER_NETDEV_LINK_10000: case TRIGGER_NETDEV_LINK_25000: case TRIGGER_NETDEV_LINK_40000: case TRIGGER_NETDEV_LINK_50000: case TRIGGER_NETDEV_LINK_100000: case TRIGGER_NETDEV_HALF_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_RX: case TRIGGER_NETDEV_TX_ERR: case TRIGGER_NETDEV_RX_ERR: bit = attr; break; default: return -EINVAL; } if (state) set_bit(bit, &mode); else clear_bit(bit, &mode); if (test_bit(TRIGGER_NETDEV_LINK, &mode) && (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || test_bit(TRIGGER_NETDEV_LINK_100, &mode) || test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || test_bit(TRIGGER_NETDEV_LINK_10000, &mode) || test_bit(TRIGGER_NETDEV_LINK_25000, &mode) || test_bit(TRIGGER_NETDEV_LINK_40000, &mode) || test_bit(TRIGGER_NETDEV_LINK_50000, &mode) || test_bit(TRIGGER_NETDEV_LINK_100000, &mode))) return -EINVAL; cancel_delayed_work_sync(&trigger_data->work); trigger_data->mode = mode; trigger_data->hw_control = can_hw_control(trigger_data); if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking && !trigger_data->hw_control) return -EOPNOTSUPP; set_baseline_state(trigger_data); return size; } #define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \ static ssize_t trigger_name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ return netdev_led_attr_show(dev, buf, trigger); \ } \ static ssize_t trigger_name##_store(struct device *dev, \ struct device_attribute *attr, const char *buf, size_t size) \ { \ return netdev_led_attr_store(dev, buf, size, trigger); \ } \ static DEVICE_ATTR_RW(trigger_name) DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK); DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10); DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100); DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500); DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); DEFINE_NETDEV_TRIGGER(link_25000, TRIGGER_NETDEV_LINK_25000); DEFINE_NETDEV_TRIGGER(link_40000, TRIGGER_NETDEV_LINK_40000); DEFINE_NETDEV_TRIGGER(link_50000, TRIGGER_NETDEV_LINK_50000); DEFINE_NETDEV_TRIGGER(link_100000, TRIGGER_NETDEV_LINK_100000); DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX); DEFINE_NETDEV_TRIGGER(tx_err, TRIGGER_NETDEV_TX_ERR); DEFINE_NETDEV_TRIGGER(rx_err, TRIGGER_NETDEV_RX_ERR); static ssize_t interval_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); return sprintf(buf, "%u\n", jiffies_to_msecs(atomic_read(&trigger_data->interval))); } static ssize_t interval_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); unsigned long value; int ret; if (trigger_data->hw_control) return -EINVAL; ret = kstrtoul(buf, 0, &value);