switch: allow Ethernet port LEDs to show specific port speeds only
This patch adds speed_mask special file to LEDs connected to switch ports via 'switch' trigger. It allows to choose which speeds to signal when link is up. If router has more than one LED per port, they may light up differently depending on how fast connection is. Default setting is 'all speeds' so backward compatibility with system scripts (for example uci) is maintained. Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl> SVN-Revision: 48775
This commit is contained in:
parent
f61a80444c
commit
d527b82862
1 changed files with 99 additions and 5 deletions
|
@ -20,6 +20,15 @@
|
||||||
#define SWCONFIG_LED_TIMER_INTERVAL (HZ / 10)
|
#define SWCONFIG_LED_TIMER_INTERVAL (HZ / 10)
|
||||||
#define SWCONFIG_LED_NUM_PORTS 32
|
#define SWCONFIG_LED_NUM_PORTS 32
|
||||||
|
|
||||||
|
#define SWCONFIG_LED_PORT_SPEED_NA 0x01 /* unknown speed */
|
||||||
|
#define SWCONFIG_LED_PORT_SPEED_10 0x02 /* 10 Mbps */
|
||||||
|
#define SWCONFIG_LED_PORT_SPEED_100 0x04 /* 100 Mbps */
|
||||||
|
#define SWCONFIG_LED_PORT_SPEED_1000 0x08 /* 1000 Mbps */
|
||||||
|
#define SWCONFIG_LED_PORT_SPEED_ALL (SWCONFIG_LED_PORT_SPEED_NA | \
|
||||||
|
SWCONFIG_LED_PORT_SPEED_10 | \
|
||||||
|
SWCONFIG_LED_PORT_SPEED_100 | \
|
||||||
|
SWCONFIG_LED_PORT_SPEED_1000)
|
||||||
|
|
||||||
struct switch_led_trigger {
|
struct switch_led_trigger {
|
||||||
struct led_trigger trig;
|
struct led_trigger trig;
|
||||||
struct switch_dev *swdev;
|
struct switch_dev *swdev;
|
||||||
|
@ -28,6 +37,7 @@ struct switch_led_trigger {
|
||||||
u32 port_mask;
|
u32 port_mask;
|
||||||
u32 port_link;
|
u32 port_link;
|
||||||
unsigned long port_traffic[SWCONFIG_LED_NUM_PORTS];
|
unsigned long port_traffic[SWCONFIG_LED_NUM_PORTS];
|
||||||
|
u8 link_speed[SWCONFIG_LED_NUM_PORTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct swconfig_trig_data {
|
struct swconfig_trig_data {
|
||||||
|
@ -40,6 +50,7 @@ struct swconfig_trig_data {
|
||||||
bool prev_link;
|
bool prev_link;
|
||||||
unsigned long prev_traffic;
|
unsigned long prev_traffic;
|
||||||
enum led_brightness prev_brightness;
|
enum led_brightness prev_brightness;
|
||||||
|
u8 speed_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -135,6 +146,46 @@ swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
|
||||||
static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
|
static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
|
||||||
swconfig_trig_port_mask_store);
|
swconfig_trig_port_mask_store);
|
||||||
|
|
||||||
|
/* speed_mask file handler - display value */
|
||||||
|
static ssize_t swconfig_trig_speed_mask_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);
|
||||||
|
sprintf(buf, "%#x\n", trig_data->speed_mask);
|
||||||
|
read_unlock(&trig_data->lock);
|
||||||
|
|
||||||
|
return strlen(buf) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* speed_mask file handler - store value */
|
||||||
|
static ssize_t swconfig_trig_speed_mask_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;
|
||||||
|
u8 speed_mask;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtou8(buf, 0, &speed_mask);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
write_lock(&trig_data->lock);
|
||||||
|
trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
|
||||||
|
write_unlock(&trig_data->lock);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* speed_mask special file */
|
||||||
|
static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
|
||||||
|
swconfig_trig_speed_mask_store);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
swconfig_trig_activate(struct led_classdev *led_cdev)
|
swconfig_trig_activate(struct led_classdev *led_cdev)
|
||||||
{
|
{
|
||||||
|
@ -154,14 +205,22 @@ swconfig_trig_activate(struct led_classdev *led_cdev)
|
||||||
rwlock_init(&trig_data->lock);
|
rwlock_init(&trig_data->lock);
|
||||||
trig_data->led_cdev = led_cdev;
|
trig_data->led_cdev = led_cdev;
|
||||||
trig_data->swdev = sw_trig->swdev;
|
trig_data->swdev = sw_trig->swdev;
|
||||||
|
trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
|
||||||
led_cdev->trigger_data = trig_data;
|
led_cdev->trigger_data = trig_data;
|
||||||
|
|
||||||
err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
|
err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
|
||||||
|
err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
|
||||||
|
if (err)
|
||||||
|
goto err_dev_free;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
err_dev_free:
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
|
||||||
|
|
||||||
err_free:
|
err_free:
|
||||||
led_cdev->trigger_data = NULL;
|
led_cdev->trigger_data = NULL;
|
||||||
kfree(trig_data);
|
kfree(trig_data);
|
||||||
|
@ -177,6 +236,7 @@ swconfig_trig_deactivate(struct led_classdev *led_cdev)
|
||||||
trig_data = (void *) led_cdev->trigger_data;
|
trig_data = (void *) led_cdev->trigger_data;
|
||||||
if (trig_data) {
|
if (trig_data) {
|
||||||
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
|
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
|
||||||
kfree(trig_data);
|
kfree(trig_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,6 +248,7 @@ swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
|
||||||
struct swconfig_trig_data *trig_data;
|
struct swconfig_trig_data *trig_data;
|
||||||
u32 port_mask;
|
u32 port_mask;
|
||||||
bool link;
|
bool link;
|
||||||
|
u8 speed_mask;
|
||||||
|
|
||||||
trig_data = led_cdev->trigger_data;
|
trig_data = led_cdev->trigger_data;
|
||||||
if (!trig_data)
|
if (!trig_data)
|
||||||
|
@ -195,6 +256,7 @@ swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
|
||||||
|
|
||||||
read_lock(&trig_data->lock);
|
read_lock(&trig_data->lock);
|
||||||
port_mask = trig_data->port_mask;
|
port_mask = trig_data->port_mask;
|
||||||
|
speed_mask = trig_data->speed_mask;
|
||||||
read_unlock(&trig_data->lock);
|
read_unlock(&trig_data->lock);
|
||||||
|
|
||||||
link = !!(sw_trig->port_link & port_mask);
|
link = !!(sw_trig->port_link & port_mask);
|
||||||
|
@ -203,17 +265,28 @@ swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
|
||||||
swconfig_trig_set_brightness(trig_data, LED_OFF);
|
swconfig_trig_set_brightness(trig_data, LED_OFF);
|
||||||
} else {
|
} else {
|
||||||
unsigned long traffic;
|
unsigned long traffic;
|
||||||
|
int speedok; /* link speed flag */
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
traffic = 0;
|
traffic = 0;
|
||||||
|
speedok = 0;
|
||||||
for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
|
for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
|
||||||
if (port_mask & (1 << i))
|
if (port_mask & (1 << i))
|
||||||
|
if (sw_trig->link_speed[i] & speed_mask) {
|
||||||
traffic += sw_trig->port_traffic[i];
|
traffic += sw_trig->port_traffic[i];
|
||||||
|
speedok = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (speedok) {
|
||||||
|
/* At least one port speed matches speed_mask */
|
||||||
if (trig_data->prev_brightness != LED_FULL)
|
if (trig_data->prev_brightness != LED_FULL)
|
||||||
swconfig_trig_set_brightness(trig_data, LED_FULL);
|
swconfig_trig_set_brightness(trig_data,
|
||||||
|
LED_FULL);
|
||||||
else if (traffic != trig_data->prev_traffic)
|
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);
|
swconfig_trig_set_brightness(trig_data, LED_OFF);
|
||||||
|
|
||||||
trig_data->prev_traffic = traffic;
|
trig_data->prev_traffic = traffic;
|
||||||
|
@ -258,6 +331,8 @@ swconfig_led_work_func(struct work_struct *work)
|
||||||
for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
|
for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
|
||||||
u32 port_bit;
|
u32 port_bit;
|
||||||
|
|
||||||
|
sw_trig->link_speed[i] = 0;
|
||||||
|
|
||||||
port_bit = BIT(i);
|
port_bit = BIT(i);
|
||||||
if ((port_mask & port_bit) == 0)
|
if ((port_mask & port_bit) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -268,8 +343,27 @@ swconfig_led_work_func(struct work_struct *work)
|
||||||
memset(&port_link, '\0', sizeof(port_link));
|
memset(&port_link, '\0', sizeof(port_link));
|
||||||
swdev->ops->get_port_link(swdev, i, &port_link);
|
swdev->ops->get_port_link(swdev, i, &port_link);
|
||||||
|
|
||||||
if (port_link.link)
|
if (port_link.link) {
|
||||||
link |= port_bit;
|
link |= port_bit;
|
||||||
|
switch (port_link.speed) {
|
||||||
|
case SWITCH_PORT_SPEED_UNKNOWN:
|
||||||
|
sw_trig->link_speed[i] =
|
||||||
|
SWCONFIG_LED_PORT_SPEED_NA;
|
||||||
|
break;
|
||||||
|
case SWITCH_PORT_SPEED_10:
|
||||||
|
sw_trig->link_speed[i] =
|
||||||
|
SWCONFIG_LED_PORT_SPEED_10;
|
||||||
|
break;
|
||||||
|
case SWITCH_PORT_SPEED_100:
|
||||||
|
sw_trig->link_speed[i] =
|
||||||
|
SWCONFIG_LED_PORT_SPEED_100;
|
||||||
|
break;
|
||||||
|
case SWITCH_PORT_SPEED_1000:
|
||||||
|
sw_trig->link_speed[i] =
|
||||||
|
SWCONFIG_LED_PORT_SPEED_1000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swdev->ops->get_port_stats) {
|
if (swdev->ops->get_port_stats) {
|
||||||
|
|
Loading…
Reference in a new issue