[LEDE-DEV] [PATCH] generic: swconfig: add mode led attribute
Kevin Darbyshire-Bryant
ldir at darbyshire-bryant.me.uk
Wed Oct 4 06:41:24 PDT 2017
Add sysfs 'mode' attribute to swconfig controlled LEDs.
swconfig 'link state' LEDs blink in the presence of port traffic. This
behaviour becomes more obvious as switches start to support
get_port_stats() e.g. commits 0369e358916ef092a1644334f5dd1412051b68a4,
3056d09b4046e0eb0f6de0f3f5432cd9fa86fc51,
4ddbc43cc15c2fa128a2f169964ef7eb508cf2c5,
4d8a66d9346373c2a7fcac5bdae3f662a9dbd9df.
This blinking can be confusing/distracting if the switch has other LEDs
used to indicate traffic. Provide a 'mode' sysfs attribute that
controls the blink on traffic behaviour.
mode - either "none" (LED is off) or a space separated list of one or more:
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
Note that 'link' considers any port speed mask that may be applicable.
e.g. if an LED is configured to indicate 1Gbit link speed and mode is
set to 'link rx tx' but the port is connected at 100Mbit then the LED
will not light/blink.
Attribute is 'link tx rx' by default for backwards compatible behaviour.
Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
---
.../generic/files/drivers/net/phy/swconfig_leds.c | 188 +++++++++++++++++----
1 file changed, 155 insertions(+), 33 deletions(-)
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
index 20b9a12..44d8bcb 100644
--- a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
+++ b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
@@ -29,6 +29,15 @@
SWCONFIG_LED_PORT_SPEED_100 | \
SWCONFIG_LED_PORT_SPEED_1000)
+#define SWCONFIG_LED_MODE_LINK 0x01
+#define SWCONFIG_LED_MODE_TX 0x02
+#define SWCONFIG_LED_MODE_RX 0x04
+#define SWCONFIG_LED_MODE_TXRX (SWCONFIG_LED_MODE_TX | \
+ SWCONFIG_LED_MODE_RX)
+#define SWCONFIG_LED_MODE_ALL (SWCONFIG_LED_MODE_LINK | \
+ SWCONFIG_LED_MODE_TX | \
+ SWCONFIG_LED_MODE_RX)
+
struct switch_led_trigger {
struct led_trigger trig;
struct switch_dev *swdev;
@@ -36,7 +45,8 @@ struct switch_led_trigger {
struct delayed_work sw_led_work;
u32 port_mask;
u32 port_link;
- unsigned long long port_traffic[SWCONFIG_LED_NUM_PORTS];
+ unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
+ unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
u8 link_speed[SWCONFIG_LED_NUM_PORTS];
};
@@ -50,6 +60,7 @@ struct swconfig_trig_data {
bool prev_link;
unsigned long prev_traffic;
enum led_brightness prev_brightness;
+ unsigned mode;
u8 speed_mask;
};
@@ -186,6 +197,78 @@ static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
swconfig_trig_speed_mask_store);
+static ssize_t swconfig_trig_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+
+ read_lock(&trig_data->lock);
+
+ if (trig_data->mode == 0) {
+ strcpy(buf, "none\n");
+ } else {
+ if (trig_data->mode & SWCONFIG_LED_MODE_LINK)
+ strcat(buf, "link ");
+ if (trig_data->mode & SWCONFIG_LED_MODE_TX)
+ strcat(buf, "tx ");
+ if (trig_data->mode & SWCONFIG_LED_MODE_RX)
+ strcat(buf, "rx ");
+ strcat(buf, "\n");
+ }
+
+ read_unlock(&trig_data->lock);
+
+ return strlen(buf)+1;
+}
+
+static ssize_t swconfig_trig_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+ char copybuf[128];
+ int new_mode = -1;
+ char *p, *token;
+
+ /* take a copy since we don't want to trash the inbound buffer when using strsep */
+ strncpy(copybuf, buf, sizeof(copybuf));
+ copybuf[sizeof(copybuf) - 1] = 0;
+ p = copybuf;
+
+ while ((token = strsep(&p, " \t\n")) != NULL) {
+ if (!*token)
+ continue;
+
+ if (new_mode == -1)
+ new_mode = 0;
+
+ if (!strcmp(token, "none"))
+ new_mode = 0;
+ else if (!strcmp(token, "tx"))
+ new_mode |= SWCONFIG_LED_MODE_TX;
+ else if (!strcmp(token, "rx"))
+ new_mode |= SWCONFIG_LED_MODE_RX;
+ else if (!strcmp(token, "link"))
+ new_mode |= SWCONFIG_LED_MODE_LINK;
+ else
+ return -EINVAL;
+ }
+
+ if (new_mode == -1)
+ return -EINVAL;
+
+ write_lock(&trig_data->lock);
+ trig_data->mode = new_mode;
+ write_unlock(&trig_data->lock);
+
+ return size;
+}
+
+/* mode special file */
+static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
+ swconfig_trig_mode_store);
+
static void
swconfig_trig_activate(struct led_classdev *led_cdev)
{
@@ -206,6 +289,7 @@ swconfig_trig_activate(struct led_classdev *led_cdev)
trig_data->led_cdev = led_cdev;
trig_data->swdev = sw_trig->swdev;
trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
+ trig_data->mode = SWCONFIG_LED_MODE_ALL;
led_cdev->trigger_data = trig_data;
err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
@@ -216,8 +300,15 @@ swconfig_trig_activate(struct led_classdev *led_cdev)
if (err)
goto err_dev_free;
+ err = device_create_file(led_cdev->dev, &dev_attr_mode);
+ if (err)
+ goto err_mode_free;
+
return;
+err_mode_free:
+ device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+
err_dev_free:
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
@@ -237,6 +328,7 @@ swconfig_trig_deactivate(struct led_classdev *led_cdev)
if (trig_data) {
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+ device_remove_file(led_cdev->dev, &dev_attr_mode);
kfree(trig_data);
}
}
@@ -260,39 +352,69 @@ swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
read_unlock(&trig_data->lock);
link = !!(sw_trig->port_link & port_mask);
- if (!link) {
- if (link != trig_data->prev_link)
- swconfig_trig_set_brightness(trig_data, LED_OFF);
- } else {
- unsigned long traffic;
- int speedok; /* link speed flag */
- int i;
-
- traffic = 0;
- speedok = 0;
- for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
- if (port_mask & (1 << i))
- if (sw_trig->link_speed[i] & speed_mask) {
- traffic += sw_trig->port_traffic[i];
- speedok = 1;
- }
- }
+ if (trig_data->mode & SWCONFIG_LED_MODE_LINK) { /* interested in link/speed status */
+
+ if (!link) {
+ if (link != trig_data->prev_link)
+ swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
+ } else {
+ unsigned long traffic;
+ int speedok; /* link speed flag */
+ int i;
+
+ traffic = 0;
+ speedok = 0;
+ for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+ if (port_mask & (1 << i))
+ if (sw_trig->link_speed[i] & speed_mask) {
+ traffic += ((trig_data->mode & SWCONFIG_LED_MODE_TX) ?
+ sw_trig->port_tx_traffic[i] : 0) +
+ ((trig_data->mode & SWCONFIG_LED_MODE_RX) ?
+ sw_trig->port_rx_traffic[i] : 0);
+ speedok = 1;
+ }
+ }
- if (speedok) {
- /* At least one port speed matches speed_mask */
- if (trig_data->prev_brightness != LED_FULL)
- swconfig_trig_set_brightness(trig_data,
- LED_FULL);
- else if (traffic != trig_data->prev_traffic)
- swconfig_trig_set_brightness(trig_data,
- LED_OFF);
- } else if (trig_data->prev_brightness != LED_OFF)
- swconfig_trig_set_brightness(trig_data, LED_OFF);
+ if (speedok) {
+ /* At least one port speed matches speed_mask */
+ if (trig_data->prev_brightness != LED_FULL)
+ swconfig_trig_set_brightness(trig_data,
+ LED_FULL);
+ else if (trig_data->mode & SWCONFIG_LED_MODE_TXRX &&
+ traffic != trig_data->prev_traffic)
+ swconfig_trig_set_brightness(trig_data,
+ LED_OFF);
+ }
- trig_data->prev_traffic = traffic;
- }
+ trig_data->prev_traffic = traffic;
+ }
- trig_data->prev_link = link;
+ trig_data->prev_link = link;
+ }
+ else { /* not interested in link/speed status - potentially only traffic */
+
+ if (trig_data->prev_brightness != LED_OFF) /* if LED on, then turn it off */
+ swconfig_trig_set_brightness(trig_data, LED_OFF); /* and no more */
+ else if (trig_data->mode & SWCONFIG_LED_MODE_TXRX) { /* if interested in traffic */
+
+ unsigned long traffic;
+ int i;
+
+ traffic = 0;
+ for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+ if (port_mask & (1 << i)) {
+ traffic += ((trig_data->mode & SWCONFIG_LED_MODE_TX) ?
+ sw_trig->port_tx_traffic[i] : 0) +
+ ((trig_data->mode & SWCONFIG_LED_MODE_RX) ?
+ sw_trig->port_rx_traffic[i] : 0);
+ }
+ }
+ if (traffic != trig_data->prev_traffic) { /* there was traffic so turn on */
+ swconfig_trig_set_brightness(trig_data,LED_FULL);
+ trig_data->prev_traffic = traffic;
+ }
+ }
+ }
}
static void
@@ -371,8 +493,8 @@ swconfig_led_work_func(struct work_struct *work)
memset(&port_stats, '\0', sizeof(port_stats));
swdev->ops->get_port_stats(swdev, i, &port_stats);
- sw_trig->port_traffic[i] = port_stats.tx_bytes +
- port_stats.rx_bytes;
+ sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
+ sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
}
}
--
2.7.4
More information about the Lede-dev
mailing list