814 lines · 23.2 KB · C
RawDownload
// 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.