mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-30 10:39:04 +00:00
243 lines
7.6 KiB
Diff
243 lines
7.6 KiB
Diff
|
From d5e01266e7f5fa12400d4c8aa4e86fe89dcc61e9 Mon Sep 17 00:00:00 2001
|
||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||
|
Date: Mon, 19 Jun 2023 22:46:58 +0200
|
||
|
Subject: [PATCH 1/3] leds: trigger: netdev: add additional specific link speed
|
||
|
mode
|
||
|
|
||
|
Add additional modes for specific link speed. Use ethtool APIs to get the
|
||
|
current link speed and enable the LED accordingly. Under netdev event
|
||
|
handler the rtnl lock is already held and is not needed to be set to
|
||
|
access ethtool APIs.
|
||
|
|
||
|
This is especially useful for PHY and Switch that supports LEDs hw
|
||
|
control for specific link speed. (example scenario a PHY that have 2 LED
|
||
|
connected one green and one orange where the green is turned on with
|
||
|
1000mbps speed and orange is turned on with 10mpbs speed)
|
||
|
|
||
|
On mode set from sysfs we check if we have enabled split link speed mode
|
||
|
and reject enabling generic link mode to prevent wrong and redundant
|
||
|
configuration.
|
||
|
|
||
|
Rework logic on the set baseline state to support these new modes to
|
||
|
select if we need to turn on or off the LED.
|
||
|
|
||
|
Add additional modes:
|
||
|
- link_10: Turn on LED when link speed is 10mbps
|
||
|
- link_100: Turn on LED when link speed is 100mbps
|
||
|
- link_1000: Turn on LED when link speed is 1000mbps
|
||
|
|
||
|
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||
|
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||
|
Acked-by: Lee Jones <lee@kernel.org>
|
||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||
|
---
|
||
|
drivers/leds/trigger/ledtrig-netdev.c | 80 +++++++++++++++++++++++----
|
||
|
include/linux/leds.h | 3 +
|
||
|
2 files changed, 73 insertions(+), 10 deletions(-)
|
||
|
|
||
|
--- a/drivers/leds/trigger/ledtrig-netdev.c
|
||
|
+++ b/drivers/leds/trigger/ledtrig-netdev.c
|
||
|
@@ -13,6 +13,7 @@
|
||
|
#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>
|
||
|
@@ -21,6 +22,7 @@
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/mutex.h>
|
||
|
+#include <linux/rtnetlink.h>
|
||
|
#include <linux/timer.h>
|
||
|
#include "../leds.h"
|
||
|
|
||
|
@@ -52,6 +54,8 @@ struct led_netdev_data {
|
||
|
unsigned int last_activity;
|
||
|
|
||
|
unsigned long mode;
|
||
|
+ int link_speed;
|
||
|
+
|
||
|
bool carrier_link_up;
|
||
|
bool hw_control;
|
||
|
};
|
||
|
@@ -77,7 +81,24 @@ static void set_baseline_state(struct le
|
||
|
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 (blink_on)
|
||
|
led_set_brightness(led_cdev,
|
||
|
led_cdev->blink_brightness);
|
||
|
else
|
||
|
@@ -161,6 +182,18 @@ static bool can_hw_control(struct led_ne
|
||
|
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 (!trigger_data->carrier_link_up)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
|
||
|
+ trigger_data->link_speed = cmd.base.speed;
|
||
|
+}
|
||
|
+
|
||
|
static ssize_t device_name_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
@@ -196,8 +229,12 @@ static int set_device_name(struct led_ne
|
||
|
dev_get_by_name(&init_net, trigger_data->device_name);
|
||
|
|
||
|
trigger_data->carrier_link_up = false;
|
||
|
- if (trigger_data->net_dev != NULL)
|
||
|
- trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
|
||
|
+ trigger_data->link_speed = SPEED_UNKNOWN;
|
||
|
+ if (trigger_data->net_dev != NULL) {
|
||
|
+ rtnl_lock();
|
||
|
+ get_device_state(trigger_data);
|
||
|
+ rtnl_unlock();
|
||
|
+ }
|
||
|
|
||
|
trigger_data->last_activity = 0;
|
||
|
|
||
|
@@ -234,6 +271,9 @@ static ssize_t netdev_led_attr_show(stru
|
||
|
|
||
|
switch (attr) {
|
||
|
case TRIGGER_NETDEV_LINK:
|
||
|
+ case TRIGGER_NETDEV_LINK_10:
|
||
|
+ case TRIGGER_NETDEV_LINK_100:
|
||
|
+ case TRIGGER_NETDEV_LINK_1000:
|
||
|
case TRIGGER_NETDEV_TX:
|
||
|
case TRIGGER_NETDEV_RX:
|
||
|
bit = attr;
|
||
|
@@ -249,7 +289,7 @@ static ssize_t netdev_led_attr_store(str
|
||
|
size_t size, enum led_trigger_netdev_modes attr)
|
||
|
{
|
||
|
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
|
||
|
- unsigned long state;
|
||
|
+ unsigned long state, mode = trigger_data->mode;
|
||
|
int ret;
|
||
|
int bit;
|
||
|
|
||
|
@@ -259,6 +299,9 @@ static ssize_t netdev_led_attr_store(str
|
||
|
|
||
|
switch (attr) {
|
||
|
case TRIGGER_NETDEV_LINK:
|
||
|
+ case TRIGGER_NETDEV_LINK_10:
|
||
|
+ case TRIGGER_NETDEV_LINK_100:
|
||
|
+ case TRIGGER_NETDEV_LINK_1000:
|
||
|
case TRIGGER_NETDEV_TX:
|
||
|
case TRIGGER_NETDEV_RX:
|
||
|
bit = attr;
|
||
|
@@ -267,13 +310,20 @@ static ssize_t netdev_led_attr_store(str
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
- cancel_delayed_work_sync(&trigger_data->work);
|
||
|
-
|
||
|
if (state)
|
||
|
- set_bit(bit, &trigger_data->mode);
|
||
|
+ set_bit(bit, &mode);
|
||
|
else
|
||
|
- clear_bit(bit, &trigger_data->mode);
|
||
|
+ 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)))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ cancel_delayed_work_sync(&trigger_data->work);
|
||
|
|
||
|
+ trigger_data->mode = mode;
|
||
|
trigger_data->hw_control = can_hw_control(trigger_data);
|
||
|
|
||
|
set_baseline_state(trigger_data);
|
||
|
@@ -295,6 +345,9 @@ static ssize_t netdev_led_attr_store(str
|
||
|
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(tx, TRIGGER_NETDEV_TX);
|
||
|
DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
|
||
|
|
||
|
@@ -338,6 +391,9 @@ static DEVICE_ATTR_RW(interval);
|
||
|
static struct attribute *netdev_trig_attrs[] = {
|
||
|
&dev_attr_device_name.attr,
|
||
|
&dev_attr_link.attr,
|
||
|
+ &dev_attr_link_10.attr,
|
||
|
+ &dev_attr_link_100.attr,
|
||
|
+ &dev_attr_link_1000.attr,
|
||
|
&dev_attr_rx.attr,
|
||
|
&dev_attr_tx.attr,
|
||
|
&dev_attr_interval.attr,
|
||
|
@@ -368,9 +424,10 @@ static int netdev_trig_notify(struct not
|
||
|
mutex_lock(&trigger_data->lock);
|
||
|
|
||
|
trigger_data->carrier_link_up = false;
|
||
|
+ trigger_data->link_speed = SPEED_UNKNOWN;
|
||
|
switch (evt) {
|
||
|
case NETDEV_CHANGENAME:
|
||
|
- trigger_data->carrier_link_up = netif_carrier_ok(dev);
|
||
|
+ get_device_state(trigger_data);
|
||
|
fallthrough;
|
||
|
case NETDEV_REGISTER:
|
||
|
if (trigger_data->net_dev)
|
||
|
@@ -384,7 +441,7 @@ static int netdev_trig_notify(struct not
|
||
|
break;
|
||
|
case NETDEV_UP:
|
||
|
case NETDEV_CHANGE:
|
||
|
- trigger_data->carrier_link_up = netif_carrier_ok(dev);
|
||
|
+ get_device_state(trigger_data);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
@@ -427,7 +484,10 @@ static void netdev_trig_work(struct work
|
||
|
if (trigger_data->last_activity != new_activity) {
|
||
|
led_stop_software_blink(trigger_data->led_cdev);
|
||
|
|
||
|
- invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode);
|
||
|
+ invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
|
||
|
+ test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
|
||
|
+ test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
|
||
|
+ test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode);
|
||
|
interval = jiffies_to_msecs(
|
||
|
atomic_read(&trigger_data->interval));
|
||
|
/* base state is ON (link present) */
|
||
|
--- a/include/linux/leds.h
|
||
|
+++ b/include/linux/leds.h
|
||
|
@@ -530,6 +530,9 @@ static inline void *led_get_trigger_data
|
||
|
/* Trigger specific enum */
|
||
|
enum led_trigger_netdev_modes {
|
||
|
TRIGGER_NETDEV_LINK = 0,
|
||
|
+ TRIGGER_NETDEV_LINK_10,
|
||
|
+ TRIGGER_NETDEV_LINK_100,
|
||
|
+ TRIGGER_NETDEV_LINK_1000,
|
||
|
TRIGGER_NETDEV_TX,
|
||
|
TRIGGER_NETDEV_RX,
|
||
|
|