1c564b38970b8473966125d4ed75db9e6b6e76b8
[openwrt/staging/ldir.git] /
1 From d5e01266e7f5fa12400d4c8aa4e86fe89dcc61e9 Mon Sep 17 00:00:00 2001
2 From: Christian Marangi <ansuelsmth@gmail.com>
3 Date: Mon, 19 Jun 2023 22:46:58 +0200
4 Subject: [PATCH 1/3] leds: trigger: netdev: add additional specific link speed
5 mode
6
7 Add additional modes for specific link speed. Use ethtool APIs to get the
8 current link speed and enable the LED accordingly. Under netdev event
9 handler the rtnl lock is already held and is not needed to be set to
10 access ethtool APIs.
11
12 This is especially useful for PHY and Switch that supports LEDs hw
13 control for specific link speed. (example scenario a PHY that have 2 LED
14 connected one green and one orange where the green is turned on with
15 1000mbps speed and orange is turned on with 10mpbs speed)
16
17 On mode set from sysfs we check if we have enabled split link speed mode
18 and reject enabling generic link mode to prevent wrong and redundant
19 configuration.
20
21 Rework logic on the set baseline state to support these new modes to
22 select if we need to turn on or off the LED.
23
24 Add additional modes:
25 - link_10: Turn on LED when link speed is 10mbps
26 - link_100: Turn on LED when link speed is 100mbps
27 - link_1000: Turn on LED when link speed is 1000mbps
28
29 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
30 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
31 Acked-by: Lee Jones <lee@kernel.org>
32 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
33 ---
34 drivers/leds/trigger/ledtrig-netdev.c | 80 +++++++++++++++++++++++----
35 include/linux/leds.h | 3 +
36 2 files changed, 73 insertions(+), 10 deletions(-)
37
38 --- a/drivers/leds/trigger/ledtrig-netdev.c
39 +++ b/drivers/leds/trigger/ledtrig-netdev.c
40 @@ -13,6 +13,7 @@
41 #include <linux/atomic.h>
42 #include <linux/ctype.h>
43 #include <linux/device.h>
44 +#include <linux/ethtool.h>
45 #include <linux/init.h>
46 #include <linux/jiffies.h>
47 #include <linux/kernel.h>
48 @@ -21,6 +22,7 @@
49 #include <linux/module.h>
50 #include <linux/netdevice.h>
51 #include <linux/mutex.h>
52 +#include <linux/rtnetlink.h>
53 #include <linux/timer.h>
54 #include "../leds.h"
55
56 @@ -52,6 +54,8 @@ struct led_netdev_data {
57 unsigned int last_activity;
58
59 unsigned long mode;
60 + int link_speed;
61 +
62 bool carrier_link_up;
63 bool hw_control;
64 };
65 @@ -77,7 +81,24 @@ static void set_baseline_state(struct le
66 if (!trigger_data->carrier_link_up) {
67 led_set_brightness(led_cdev, LED_OFF);
68 } else {
69 + bool blink_on = false;
70 +
71 if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode))
72 + blink_on = true;
73 +
74 + if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) &&
75 + trigger_data->link_speed == SPEED_10)
76 + blink_on = true;
77 +
78 + if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) &&
79 + trigger_data->link_speed == SPEED_100)
80 + blink_on = true;
81 +
82 + if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) &&
83 + trigger_data->link_speed == SPEED_1000)
84 + blink_on = true;
85 +
86 + if (blink_on)
87 led_set_brightness(led_cdev,
88 led_cdev->blink_brightness);
89 else
90 @@ -161,6 +182,18 @@ static bool can_hw_control(struct led_ne
91 return true;
92 }
93
94 +static void get_device_state(struct led_netdev_data *trigger_data)
95 +{
96 + struct ethtool_link_ksettings cmd;
97 +
98 + trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
99 + if (!trigger_data->carrier_link_up)
100 + return;
101 +
102 + if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
103 + trigger_data->link_speed = cmd.base.speed;
104 +}
105 +
106 static ssize_t device_name_show(struct device *dev,
107 struct device_attribute *attr, char *buf)
108 {
109 @@ -196,8 +229,12 @@ static int set_device_name(struct led_ne
110 dev_get_by_name(&init_net, trigger_data->device_name);
111
112 trigger_data->carrier_link_up = false;
113 - if (trigger_data->net_dev != NULL)
114 - trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev);
115 + trigger_data->link_speed = SPEED_UNKNOWN;
116 + if (trigger_data->net_dev != NULL) {
117 + rtnl_lock();
118 + get_device_state(trigger_data);
119 + rtnl_unlock();
120 + }
121
122 trigger_data->last_activity = 0;
123
124 @@ -234,6 +271,9 @@ static ssize_t netdev_led_attr_show(stru
125
126 switch (attr) {
127 case TRIGGER_NETDEV_LINK:
128 + case TRIGGER_NETDEV_LINK_10:
129 + case TRIGGER_NETDEV_LINK_100:
130 + case TRIGGER_NETDEV_LINK_1000:
131 case TRIGGER_NETDEV_TX:
132 case TRIGGER_NETDEV_RX:
133 bit = attr;
134 @@ -249,7 +289,7 @@ static ssize_t netdev_led_attr_store(str
135 size_t size, enum led_trigger_netdev_modes attr)
136 {
137 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
138 - unsigned long state;
139 + unsigned long state, mode = trigger_data->mode;
140 int ret;
141 int bit;
142
143 @@ -259,6 +299,9 @@ static ssize_t netdev_led_attr_store(str
144
145 switch (attr) {
146 case TRIGGER_NETDEV_LINK:
147 + case TRIGGER_NETDEV_LINK_10:
148 + case TRIGGER_NETDEV_LINK_100:
149 + case TRIGGER_NETDEV_LINK_1000:
150 case TRIGGER_NETDEV_TX:
151 case TRIGGER_NETDEV_RX:
152 bit = attr;
153 @@ -267,13 +310,20 @@ static ssize_t netdev_led_attr_store(str
154 return -EINVAL;
155 }
156
157 - cancel_delayed_work_sync(&trigger_data->work);
158 -
159 if (state)
160 - set_bit(bit, &trigger_data->mode);
161 + set_bit(bit, &mode);
162 else
163 - clear_bit(bit, &trigger_data->mode);
164 + clear_bit(bit, &mode);
165 +
166 + if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
167 + (test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
168 + test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
169 + test_bit(TRIGGER_NETDEV_LINK_1000, &mode)))
170 + return -EINVAL;
171 +
172 + cancel_delayed_work_sync(&trigger_data->work);
173
174 + trigger_data->mode = mode;
175 trigger_data->hw_control = can_hw_control(trigger_data);
176
177 set_baseline_state(trigger_data);
178 @@ -295,6 +345,9 @@ static ssize_t netdev_led_attr_store(str
179 static DEVICE_ATTR_RW(trigger_name)
180
181 DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
182 +DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
183 +DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
184 +DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
185 DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
186 DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
187
188 @@ -338,6 +391,9 @@ static DEVICE_ATTR_RW(interval);
189 static struct attribute *netdev_trig_attrs[] = {
190 &dev_attr_device_name.attr,
191 &dev_attr_link.attr,
192 + &dev_attr_link_10.attr,
193 + &dev_attr_link_100.attr,
194 + &dev_attr_link_1000.attr,
195 &dev_attr_rx.attr,
196 &dev_attr_tx.attr,
197 &dev_attr_interval.attr,
198 @@ -368,9 +424,10 @@ static int netdev_trig_notify(struct not
199 mutex_lock(&trigger_data->lock);
200
201 trigger_data->carrier_link_up = false;
202 + trigger_data->link_speed = SPEED_UNKNOWN;
203 switch (evt) {
204 case NETDEV_CHANGENAME:
205 - trigger_data->carrier_link_up = netif_carrier_ok(dev);
206 + get_device_state(trigger_data);
207 fallthrough;
208 case NETDEV_REGISTER:
209 if (trigger_data->net_dev)
210 @@ -384,7 +441,7 @@ static int netdev_trig_notify(struct not
211 break;
212 case NETDEV_UP:
213 case NETDEV_CHANGE:
214 - trigger_data->carrier_link_up = netif_carrier_ok(dev);
215 + get_device_state(trigger_data);
216 break;
217 }
218
219 @@ -427,7 +484,10 @@ static void netdev_trig_work(struct work
220 if (trigger_data->last_activity != new_activity) {
221 led_stop_software_blink(trigger_data->led_cdev);
222
223 - invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode);
224 + invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) ||
225 + test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
226 + test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
227 + test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode);
228 interval = jiffies_to_msecs(
229 atomic_read(&trigger_data->interval));
230 /* base state is ON (link present) */
231 --- a/include/linux/leds.h
232 +++ b/include/linux/leds.h
233 @@ -530,6 +530,9 @@ static inline void *led_get_trigger_data
234 /* Trigger specific enum */
235 enum led_trigger_netdev_modes {
236 TRIGGER_NETDEV_LINK = 0,
237 + TRIGGER_NETDEV_LINK_10,
238 + TRIGGER_NETDEV_LINK_100,
239 + TRIGGER_NETDEV_LINK_1000,
240 TRIGGER_NETDEV_TX,
241 TRIGGER_NETDEV_RX,
242