#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/module.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
+#include <linux/rtnetlink.h>
#include <linux/timer.h>
#include "../leds.h"
unsigned int last_activity;
unsigned long mode;
+ int link_speed;
+
bool carrier_link_up;
bool hw_control;
};
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
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)
{
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;
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;
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;
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;
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);
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);
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,
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)
break;
case NETDEV_UP:
case NETDEV_CHANGE:
- trigger_data->carrier_link_up = netif_carrier_ok(dev);
+ get_device_state(trigger_data);
break;
}
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) */