// 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>
/* * 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.
*/
/* 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;
/* * Validate the configured netdev is the same as the one associated with * the LED driver in hw control.
*/ staticbool 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;
/* * Interval must be set to the default * value. Any different value is rejected if in hw * control.
*/ if (interval != default_interval) returnfalse;
/* * 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)) returnfalse;
/* 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) returnfalse; if (ret) {
dev_warn(led_cdev->dev, "Current mode check failed with error %d\n", ret); returnfalse;
}
if (__ethtool_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);
}
/* * Take RTNL lock before trigger_data lock to prevent potential * deadlock with netdev notifier registration.
*/
rtnl_lock();
mutex_lock(&trigger_data->lock);
if (trigger_data->net_dev) {
dev_put(trigger_data->net_dev);
trigger_data->net_dev = NULL;
}
/* 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);
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_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;
}
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_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);
/* * Search in the supported link mode mask a matching supported mode. * Stop at the first matching entry as we care only to check if a particular * speed is supported and not the kind.
*/
for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) { struct ethtool_link_ksettings link_ksettings;
/* If we dont have a device, insure we are off */ if (!trigger_data->net_dev) {
led_set_brightness(trigger_data->led_cdev, LED_OFF); return;
}
/* If we are not looking for RX/TX then return */ 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)) return;
/* Check if hw control is active by default on the LED. * Init already enabled mode in hw control.
*/ if (supports_hw_control(led_cdev)) {
dev = led_cdev->hw_control_get_device(led_cdev); if (dev) { constchar *name = dev_name(dev);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.