Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ledtrig-netdev.c  Sprache: unbekannt

 
// SPDX-License-Identifier: GPL-2.0
// Copyright 2017 Ben Whitten <ben.whitten@gmail.com>
// Copyright 2007 Oliver Jowett <oliver@opencloud.com>
//
// LED Kernel Netdev Trigger
//
// Toggles the LED to reflect the link and traffic state of a named net device
//
// Derived from ledtrig-timer.c which is:
//  Copyright 2005-2006 Openedhand Ltd.
//  Author: Richard Purdie <rpurdie@openedhand.com>

#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/leds.h>
#include <linux/linkmode.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
#include <linux/phy.h>
#include <linux/rtnetlink.h>
#include <linux/timer.h>
#include "../leds.h"

#define NETDEV_LED_DEFAULT_INTERVAL 50

/*
 * Configurable sysfs attributes:
 *
 * device_name - network device name to monitor
 * interval - duration of LED blink, in milliseconds
 * 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
 * tx_err -  LED blinks on transmit error
 * rx_err -  LED blinks on receive error
 *
 * Note: If the user selects a mode that is not supported by hw, default
 * behavior is to fall back to software control of the LED. However not every
 * hw supports software control. LED callbacks brightness_set() and
 * brightness_set_blocking() are NULL in this case. hw_control_is_supported()
 * should use available means supported by hw to inform the user that selected
 * mode isn't supported by hw. This could be switching off the LED or any
 * hw blink mode. If software control fallback isn't possible, we return
 * -EOPNOTSUPP to the user, but still store the selected mode. This is needed
 * in case an intermediate unsupported mode is necessary to switch from one
 * supported mode to another.
 */


struct led_netdev_data {
 struct mutex lock;

 struct delayed_work work;
 struct notifier_block notifier;

 struct led_classdev *led_cdev;
 struct net_device *net_dev;

 char device_name[IFNAMSIZ];
 atomic_t interval;
 unsigned int last_activity;

 unsigned long mode;
 int link_speed;
 __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes);
 u8 duplex;

 bool carrier_link_up;
 bool hw_control;
};

static const struct attribute_group netdev_trig_link_speed_attrs_group;

static void set_baseline_state(struct led_netdev_data *trigger_data)
{
 int current_brightness;
 struct led_classdev *led_cdev = trigger_data->led_cdev;

 /* Already validated, hw control is possible with the requested mode */
 if (trigger_data->hw_control) {
  led_cdev->hw_control_set(led_cdev, trigger_data->mode);

  return;
 }

 current_brightness = led_cdev->brightness;
 if (current_brightness)
  led_cdev->blink_brightness = current_brightness;
 if (!led_cdev->blink_brightness)
  led_cdev->blink_brightness = led_cdev->max_brightness;

 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 (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
      trigger_data->link_speed == SPEED_2500)
   blink_on = true;

  if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
      trigger_data->link_speed == SPEED_5000)
   blink_on = true;

  if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
      trigger_data->link_speed == SPEED_10000)
   blink_on = true;

  if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
      trigger_data->duplex == DUPLEX_HALF)
   blink_on = true;

  if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) &&
      trigger_data->duplex == DUPLEX_FULL)
   blink_on = true;

  if (blink_on)
   led_set_brightness(led_cdev,
        led_cdev->blink_brightness);
  else
   led_set_brightness(led_cdev, LED_OFF);

  /* If we are looking for RX/TX start periodically
   * checking stats
 */

  if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
      test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
      test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
      test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
   schedule_delayed_work(&trigger_data->work, 0);
 }
}

static bool supports_hw_control(struct led_classdev *led_cdev)
{
 if (!led_cdev->hw_control_get || !led_cdev->hw_control_set ||
     !led_cdev->hw_control_is_supported)
  return false;

 return !strcmp(led_cdev->hw_control_trigger, led_cdev->trigger->name);
}

/*
 * Validate the configured netdev is the same as the one associated with
 * the LED driver in hw control.
 */

static bool validate_net_dev(struct led_classdev *led_cdev,
        struct net_device *net_dev)
{
 struct device *dev = led_cdev->hw_control_get_device(led_cdev);
 struct net_device *ndev;

 if (!dev)
  return false;

 ndev = to_net_dev(dev);

 return ndev == net_dev;
}

static bool can_hw_control(struct led_netdev_data *trigger_data)
{
 unsigned long default_interval = msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL);
 unsigned int interval = atomic_read(&trigger_data->interval);
 struct led_classdev *led_cdev = trigger_data->led_cdev;
 int ret;

 if (!supports_hw_control(led_cdev))
  return false;

 /*
  * Interval must be set to the default
  * value. Any different value is rejected if in hw
  * control.
 */

 if (interval != default_interval)
  return false;

 /*
  * net_dev must be set with hw control, otherwise no
  * blinking can be happening and there is nothing to
  * offloaded. Additionally, for hw control to be
  * valid, the configured netdev must be the same as
  * netdev associated to the LED.
 */

 if (!validate_net_dev(led_cdev, trigger_data->net_dev))
  return false;

 /* Check if the requested mode is supported */
 ret = led_cdev->hw_control_is_supported(led_cdev, trigger_data->mode);
 /* Fall back to software blinking if not supported */
 if (ret == -EOPNOTSUPP)
  return false;
 if (ret) {
  dev_warn(led_cdev->dev,
    "Current mode check failed with error %d\n", ret);
  return false;
 }

 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 (__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd))
  return;

 if (trigger_data->carrier_link_up) {
  trigger_data->link_speed = cmd.base.speed;
  trigger_data->duplex = cmd.base.duplex;
 }

 /*
  * Have a local copy of the link speed supported to avoid rtnl lock every time
  * modes are refreshed on any change event
 */

 linkmode_copy(trigger_data->supported_link_modes, cmd.link_modes.supported);
}

static ssize_t device_name_show(struct device *dev,
    struct device_attribute *attr, char *buf)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 ssize_t len;

 mutex_lock(&trigger_data->lock);
 len = sprintf(buf, "%s\n", trigger_data->device_name);
 mutex_unlock(&trigger_data->lock);

 return len;
}

static int set_device_name(struct led_netdev_data *trigger_data,
      const char *name, size_t size)
{
 if (size >= IFNAMSIZ)
  return -EINVAL;

 cancel_delayed_work_sync(&trigger_data->work);

 /*
  * Take RTNL lock before trigger_data lock to prevent potential
  * deadlock with netdev notifier registration.
 */

 rtnl_lock();
 mutex_lock(&trigger_data->lock);

 if (trigger_data->net_dev) {
  dev_put(trigger_data->net_dev);
  trigger_data->net_dev = NULL;
 }

 memcpy(trigger_data->device_name, name, size);
 trigger_data->device_name[size] = 0;
 if (size > 0 && trigger_data->device_name[size - 1] == '\n')
  trigger_data->device_name[size - 1] = 0;

 if (trigger_data->device_name[0] != 0)
  trigger_data->net_dev =
      dev_get_by_name(&init_net, trigger_data->device_name);

 trigger_data->carrier_link_up = false;
 trigger_data->link_speed = SPEED_UNKNOWN;
 trigger_data->duplex = DUPLEX_UNKNOWN;
 if (trigger_data->net_dev)
  get_device_state(trigger_data);

 trigger_data->last_activity = 0;

 /* Skip if we're called from netdev_trig_activate() and hw_control is true */
 if (!trigger_data->hw_control || led_get_trigger_data(trigger_data->led_cdev))
  set_baseline_state(trigger_data);

 mutex_unlock(&trigger_data->lock);
 rtnl_unlock();

 return 0;
}

static ssize_t device_name_store(struct device *dev,
     struct device_attribute *attr, const char *buf,
     size_t size)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 int ret;

 ret = set_device_name(trigger_data, buf, size);

 if (ret < 0)
  return ret;

 /* Refresh link_speed visibility */
 sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group);

 return size;
}

static DEVICE_ATTR_RW(device_name);

static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
        enum led_trigger_netdev_modes attr)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 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_LINK_2500:
 case TRIGGER_NETDEV_LINK_5000:
 case TRIGGER_NETDEV_LINK_10000:
 case TRIGGER_NETDEV_HALF_DUPLEX:
 case TRIGGER_NETDEV_FULL_DUPLEX:
 case TRIGGER_NETDEV_TX:
 case TRIGGER_NETDEV_RX:
 case TRIGGER_NETDEV_TX_ERR:
 case TRIGGER_NETDEV_RX_ERR:
  bit = attr;
  break;
 default:
  return -EINVAL;
 }

 return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode));
}

static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
         size_t size, enum led_trigger_netdev_modes attr)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 struct led_classdev *led_cdev = trigger_data->led_cdev;
 unsigned long state, mode = trigger_data->mode;
 int ret;
 int bit;

 ret = kstrtoul(buf, 0, &state);
 if (ret)
  return ret;

 switch (attr) {
 case TRIGGER_NETDEV_LINK:
 case TRIGGER_NETDEV_LINK_10:
 case TRIGGER_NETDEV_LINK_100:
 case TRIGGER_NETDEV_LINK_1000:
 case TRIGGER_NETDEV_LINK_2500:
 case TRIGGER_NETDEV_LINK_5000:
 case TRIGGER_NETDEV_LINK_10000:
 case TRIGGER_NETDEV_HALF_DUPLEX:
 case TRIGGER_NETDEV_FULL_DUPLEX:
 case TRIGGER_NETDEV_TX:
 case TRIGGER_NETDEV_RX:
 case TRIGGER_NETDEV_TX_ERR:
 case TRIGGER_NETDEV_RX_ERR:
  bit = attr;
  break;
 default:
  return -EINVAL;
 }

 if (state)
  set_bit(bit, &mode);
 else
  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) ||
      test_bit(TRIGGER_NETDEV_LINK_2500, &mode) ||
      test_bit(TRIGGER_NETDEV_LINK_5000, &mode) ||
      test_bit(TRIGGER_NETDEV_LINK_10000, &mode)))
  return -EINVAL;

 cancel_delayed_work_sync(&trigger_data->work);

 trigger_data->mode = mode;
 trigger_data->hw_control = can_hw_control(trigger_data);

 if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking &&
     !trigger_data->hw_control)
  return -EOPNOTSUPP;

 set_baseline_state(trigger_data);

 return size;
}

#define DEFINE_NETDEV_TRIGGER(trigger_name, trigger) \
 static ssize_t trigger_name##_show(struct device *dev, \
  struct device_attribute *attr, char *buf) \
 { \
  return netdev_led_attr_show(dev, buf, trigger); \
 } \
 static ssize_t trigger_name##_store(struct device *dev, \
  struct device_attribute *attr, const char *buf, size_t size) \
 { \
  return netdev_led_attr_store(dev, buf, size, trigger); \
 } \
 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(link_2500, TRIGGER_NETDEV_LINK_2500);
DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);
DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);
DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
DEFINE_NETDEV_TRIGGER(tx_err, TRIGGER_NETDEV_TX_ERR);
DEFINE_NETDEV_TRIGGER(rx_err, TRIGGER_NETDEV_RX_ERR);

static ssize_t interval_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);

 return sprintf(buf, "%u\n",
         jiffies_to_msecs(atomic_read(&trigger_data->interval)));
}

static ssize_t interval_store(struct device *dev,
         struct device_attribute *attr, const char *buf,
         size_t size)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
 unsigned long value;
 int ret;

 if (trigger_data->hw_control)
  return -EINVAL;

 ret = kstrtoul(buf, 0, &value);
 if (ret)
  return ret;

 /* impose some basic bounds on the timer interval */
 if (value >= 5 && value <= 10000) {
  cancel_delayed_work_sync(&trigger_data->work);

  atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
  set_baseline_state(trigger_data); /* resets timer */
 }

 return size;
}

static DEVICE_ATTR_RW(interval);

static ssize_t offloaded_show(struct device *dev,
         struct device_attribute *attr, char *buf)
{
 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);

 return sprintf(buf, "%d\n", trigger_data->hw_control);
}

static DEVICE_ATTR_RO(offloaded);

#define CHECK_LINK_MODE_ATTR(link_speed) \
 do { \
  if (attr == &dev_attr_link_##link_speed.attr && \
      link_ksettings.base.speed == SPEED_##link_speed) \
   return attr->mode; \
 } while (0)

static umode_t netdev_trig_link_speed_visible(struct kobject *kobj,
           struct attribute *attr, int n)
{
 struct device *dev = kobj_to_dev(kobj);
 struct led_netdev_data *trigger_data;
 unsigned long *supported_link_modes;
 u32 mode;

 trigger_data = led_trigger_get_drvdata(dev);
 supported_link_modes = trigger_data->supported_link_modes;

 /*
  * Search in the supported link mode mask a matching supported mode.
  * Stop at the first matching entry as we care only to check if a particular
  * speed is supported and not the kind.
 */

 for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) {
  struct ethtool_link_ksettings link_ksettings;

  ethtool_params_from_link_mode(&link_ksettings, mode);

  CHECK_LINK_MODE_ATTR(10);
  CHECK_LINK_MODE_ATTR(100);
  CHECK_LINK_MODE_ATTR(1000);
  CHECK_LINK_MODE_ATTR(2500);
  CHECK_LINK_MODE_ATTR(5000);
  CHECK_LINK_MODE_ATTR(10000);
 }

 return 0;
}

static struct attribute *netdev_trig_link_speed_attrs[] = {
 &dev_attr_link_10.attr,
 &dev_attr_link_100.attr,
 &dev_attr_link_1000.attr,
 &dev_attr_link_2500.attr,
 &dev_attr_link_5000.attr,
 &dev_attr_link_10000.attr,
 NULL
};

static const struct attribute_group netdev_trig_link_speed_attrs_group = {
 .attrs = netdev_trig_link_speed_attrs,
 .is_visible = netdev_trig_link_speed_visible,
};

static struct attribute *netdev_trig_attrs[] = {
 &dev_attr_device_name.attr,
 &dev_attr_link.attr,
 &dev_attr_full_duplex.attr,
 &dev_attr_half_duplex.attr,
 &dev_attr_rx.attr,
 &dev_attr_tx.attr,
 &dev_attr_rx_err.attr,
 &dev_attr_tx_err.attr,
 &dev_attr_interval.attr,
 &dev_attr_offloaded.attr,
 NULL
};

static const struct attribute_group netdev_trig_attrs_group = {
 .attrs = netdev_trig_attrs,
};

static const struct attribute_group *netdev_trig_groups[] = {
 &netdev_trig_attrs_group,
 &netdev_trig_link_speed_attrs_group,
 NULL,
};

static int netdev_trig_notify(struct notifier_block *nb,
         unsigned long evt, void *dv)
{
 struct net_device *dev =
  netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv);
 struct led_netdev_data *trigger_data =
  container_of(nb, struct led_netdev_data, notifier);
 struct led_classdev *led_cdev = trigger_data->led_cdev;

 if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE
     && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER
     && evt != NETDEV_CHANGENAME)
  return NOTIFY_DONE;

 if (!(dev == trigger_data->net_dev ||
       (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) ||
       (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
  return NOTIFY_DONE;

 cancel_delayed_work_sync(&trigger_data->work);

 mutex_lock(&trigger_data->lock);

 trigger_data->carrier_link_up = false;
 trigger_data->link_speed = SPEED_UNKNOWN;
 trigger_data->duplex = DUPLEX_UNKNOWN;
 switch (evt) {
 case NETDEV_CHANGENAME:
 case NETDEV_REGISTER:
  dev_put(trigger_data->net_dev);
  dev_hold(dev);
  trigger_data->net_dev = dev;
  if (evt == NETDEV_CHANGENAME)
   get_device_state(trigger_data);
  break;
 case NETDEV_UNREGISTER:
  dev_put(trigger_data->net_dev);
  trigger_data->net_dev = NULL;
  break;
 case NETDEV_UP:
  trigger_data->hw_control = can_hw_control(trigger_data);
  fallthrough;
 case NETDEV_CHANGE:
  get_device_state(trigger_data);
  /* Refresh link_speed visibility */
  if (evt == NETDEV_CHANGE)
   sysfs_update_group(&led_cdev->dev->kobj,
        &netdev_trig_link_speed_attrs_group);
  break;
 }

 set_baseline_state(trigger_data);

 mutex_unlock(&trigger_data->lock);

 return NOTIFY_DONE;
}

/* here's the real work! */
static void netdev_trig_work(struct work_struct *work)
{
 struct led_netdev_data *trigger_data =
  container_of(work, struct led_netdev_data, work.work);
 struct rtnl_link_stats64 *dev_stats;
 unsigned int new_activity;
 struct rtnl_link_stats64 temp;
 unsigned long interval;
 int invert;

 /* If we dont have a device, insure we are off */
 if (!trigger_data->net_dev) {
  led_set_brightness(trigger_data->led_cdev, LED_OFF);
  return;
 }

 /* If we are not looking for RX/TX then return  */
 if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
     !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) &&
     !test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) &&
     !test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
  return;

 dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
 new_activity =
     (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ?
  dev_stats->tx_packets : 0) +
     (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ?
  dev_stats->rx_packets : 0) +
     (test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ?
  dev_stats->tx_errors : 0) +
     (test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode) ?
  dev_stats->rx_errors : 0);

 if (trigger_data->last_activity != new_activity) {
  led_stop_software_blink(trigger_data->led_cdev);

  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) ||
    test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) ||
    test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) ||
    test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) ||
    test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
    test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
  interval = jiffies_to_msecs(
    atomic_read(&trigger_data->interval));
  /* base state is ON (link present) */
  led_blink_set_oneshot(trigger_data->led_cdev,
          &interval,
          &interval,
          invert);
  trigger_data->last_activity = new_activity;
 }

 schedule_delayed_work(&trigger_data->work,
   (atomic_read(&trigger_data->interval)*2));
}

static int netdev_trig_activate(struct led_classdev *led_cdev)
{
 struct led_netdev_data *trigger_data;
 unsigned long mode = 0;
 struct device *dev;
 int rc;

 trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
 if (!trigger_data)
  return -ENOMEM;

 mutex_init(&trigger_data->lock);

 trigger_data->notifier.notifier_call = netdev_trig_notify;
 trigger_data->notifier.priority = 10;

 INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);

 trigger_data->led_cdev = led_cdev;
 trigger_data->net_dev = NULL;
 trigger_data->device_name[0] = 0;

 trigger_data->mode = 0;
 atomic_set(&trigger_data->interval, msecs_to_jiffies(NETDEV_LED_DEFAULT_INTERVAL));
 trigger_data->last_activity = 0;

 /* Check if hw control is active by default on the LED.
  * Init already enabled mode in hw control.
 */

 if (supports_hw_control(led_cdev)) {
  dev = led_cdev->hw_control_get_device(led_cdev);
  if (dev) {
   const char *name = dev_name(dev);

   trigger_data->hw_control = true;
   set_device_name(trigger_data, name, strlen(name));

   rc = led_cdev->hw_control_get(led_cdev, &mode);
   if (!rc)
    trigger_data->mode = mode;
  }
 }

 led_set_trigger_data(led_cdev, trigger_data);

 rc = register_netdevice_notifier(&trigger_data->notifier);
 if (rc)
  kfree(trigger_data);

 return rc;
}

static void netdev_trig_deactivate(struct led_classdev *led_cdev)
{
 struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev);

 unregister_netdevice_notifier(&trigger_data->notifier);

 cancel_delayed_work_sync(&trigger_data->work);

 dev_put(trigger_data->net_dev);

 kfree(trigger_data);
}

static struct led_trigger netdev_led_trigger = {
 .name = "netdev",
 .activate = netdev_trig_activate,
 .deactivate = netdev_trig_deactivate,
 .groups = netdev_trig_groups,
};

module_led_trigger(netdev_led_trigger);

MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>");
MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
MODULE_DESCRIPTION("Netdev LED trigger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("ledtrig:netdev");

Messung V0.5 in Prozent
C=96 H=99 G=97

[Dauer der Verarbeitung: 0.19 Sekunden, vorverarbeitet 2026-09-29]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126864
#Domains=1897691