Merge tag 'leds-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
[sfrench/cifs-2.6.git] / drivers / leds / trigger / ledtrig-netdev.c
index d76214fa9ad8645441e9c53814719ba0109f30ac..8e5475819590e056142dae6ac1dd8c0dbe186d84 100644 (file)
  * tx -  LED blinks on transmitted data
  * rx -  LED blinks on receive data
  *
+ * 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 {
@@ -99,6 +109,18 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
                    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;
@@ -289,6 +311,9 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
        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:
@@ -306,6 +331,7 @@ 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;
@@ -319,6 +345,9 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
        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:
@@ -337,7 +366,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
        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_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);
@@ -345,6 +377,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
        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;
@@ -367,6 +403,9 @@ 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);
@@ -425,6 +464,9 @@ static struct attribute *netdev_trig_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,
        &dev_attr_full_duplex.attr,
        &dev_attr_half_duplex.attr,
        &dev_attr_rx.attr,
@@ -522,6 +564,9 @@ static void netdev_trig_work(struct work_struct *work)
                         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(