Generic support for port speed/link advertisement control.

This add generic support for port speed/link advertisement control
to swconfig, similar to how the link attribute is handled.

I kept it deliberately simple, the main goals are to be able to
limit the maximum speed and set
full-duplex/flow-control/auto-negotiation attributes.

Thus the supported flags for the 'adv' attribute are:
speed:1000
speed:100
speed:10
full-duplex
flow
auto

The speed flag sets the speed limit (if more than one is specified,
the last one takes effect).
flow sets both txflow and rxflow.
If auto is not specified the speed if forced (if supported by the
underlying driver/device).

e.g.
swconfig dev rtl8366rb port 4 set adv "speed:100 full-duplex flow auto"
would limit the advertised port speed to 100.

swconfig dev rtl8366rb port 4 set adv "speed:100 full-duplex"
will disable auto-negotiation and set the speed to 100.

This is useful if you want to deliberately limit the speed, for
example because of a bad network cable causing the link between two gige
device to flap.

Signed-off-by: Tobias Diedrich <ranma+open...@tdiedrich.de>


Index: target/linux/generic/patches-3.3/730-swconfig-port-adv.patch
===================================================================
--- target/linux/generic/patches-3.3/730-swconfig-port-adv.patch        
(revision 0)
+++ target/linux/generic/patches-3.3/730-swconfig-port-adv.patch        
(working copy)
@@ -0,0 +1,242 @@
+Index: linux-3.3.8/drivers/net/phy/swconfig.c
+===================================================================
+--- linux-3.3.8.orig/drivers/net/phy/swconfig.c        2012-08-25 
19:56:18.223801584 +0200
++++ linux-3.3.8/drivers/net/phy/swconfig.c     2012-08-25 21:18:57.110727709 
+0200
+@@ -23,6 +23,8 @@
+ #include <linux/capability.h>
+ #include <linux/skbuff.h>
+ #include <linux/switch.h>
++#include <linux/string.h>
++#include <linux/parser.h>
+ 
+ //#define DEBUG 1
+ #ifdef DEBUG
+@@ -185,8 +187,157 @@
+       return 0;
+ }
+ 
++enum {
++      /* simple options */
++      Opt_aneg,
++      Opt_flow,
++      Opt_full, Opt_half,
++
++      /* options with argument */
++      Opt_speed,
++
++      /* error option */
++      Opt_err
++};
++
++static const match_table_t adv_option_tokens = {
++      {Opt_aneg, "auto" },
++      {Opt_flow, "flow" },
++      {Opt_full, "full-duplex" },
++      {Opt_half, "half-duplex" },
++      {Opt_speed, "speed:%s" },
++      {Opt_err, NULL },
++};
++
+ static int
+-swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr, 
struct switch_val *val)
++swconfig_parse_adv_options(const char *raw,
++                         struct switch_port_adv *adv)
++{
++      char *p, *copy, *state;
++      bool invalid_option = 0;
++
++      if (!raw)
++              return 0;
++
++      adv->speed = SWITCH_PORT_SPEED_DEFAULT;
++
++      /* strsep will modify the string, so we'll copy it. */
++      copy = kstrdup(raw, GFP_KERNEL);
++      state = copy;
++
++      while ((p = strsep(&state, " ")) != NULL) {
++              substring_t args[MAX_OPT_ARGS];
++              int token, speed;
++
++              if (!*p)
++                      continue;
++
++              token = match_token(p, adv_option_tokens, args);
++              switch (token) {
++
++              /* boolean options */
++              case Opt_aneg:
++                      adv->aneg = 1;
++                      break;
++              case Opt_full:
++                      adv->duplex = 1;
++                      break;
++              case Opt_flow:
++                      adv->flow = 1;
++                      break;
++
++              /* numeric options */
++              case Opt_speed:
++                      match_int(args, &speed);
++                      switch (speed) {
++                      case 10:
++                              adv->speed = SWITCH_PORT_SPEED_10;
++                              break;
++                      case 100:
++                              adv->speed = SWITCH_PORT_SPEED_100;
++                              break;
++                      case 1000:
++                              adv->speed = SWITCH_PORT_SPEED_1000;
++                              break;
++                      default:
++                              invalid_option = 1;
++                      }
++                      break;
++
++              default:
++                      invalid_option = 1;
++              }
++
++              if (invalid_option) {
++                      pr_warn("swconfig: Invalid adv option '%s'.\n", p);
++                      break;
++              }
++      }
++
++      kfree(copy);
++      return !invalid_option;
++}
++
++static int
++swconfig_get_adv(struct switch_dev *dev,
++               const struct switch_attr *attr,
++               struct switch_val *val)
++{
++      struct switch_port_adv adv;
++      int len;
++      int ret;
++
++      if (val->port_vlan >= dev->ports)
++              return -EINVAL;
++
++      if (!dev->ops->get_port_adv)
++              return -EOPNOTSUPP;
++
++      memset(&adv, 0, sizeof(adv));
++      ret = dev->ops->get_port_adv(dev, val->port_vlan, &adv);
++      if (ret)
++              return ret;
++
++      memset(dev->buf, 0, sizeof(dev->buf));
++
++      len = snprintf(dev->buf, sizeof(dev->buf),
++                     "port:%d speed:%s %s-duplex %s%s",
++                     val->port_vlan,
++                     swconfig_speed_str(adv.speed),
++                     adv.duplex ? "full" : "half",
++                     adv.flow ? "flow " : "",
++                     adv.aneg ? "auto" : "");
++
++      val->value.s = dev->buf;
++      val->len = len;
++
++      return 0;
++}
++
++static int
++swconfig_set_adv(struct switch_dev *dev,
++               const struct switch_attr *attr,
++               struct switch_val *val)
++{
++      struct switch_port_adv adv;
++
++      if (val->port_vlan >= dev->ports)
++              return -EINVAL;
++
++      if (!dev->ops->set_port_adv)
++              return -EOPNOTSUPP;
++
++      memset(&adv, 0, sizeof(adv));
++      if (!swconfig_parse_adv_options(val->value.s, &adv))
++              return -EINVAL;
++
++      return dev->ops->set_port_adv(dev, val->port_vlan, &adv);
++}
++
++static int
++swconfig_apply_config(struct switch_dev *dev,
++                    const struct switch_attr *attr,
++                    struct switch_val *val)
+ {
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->apply_config)
+@@ -217,6 +368,7 @@
+ enum port_defaults {
+       PORT_PVID,
+       PORT_LINK,
++      PORT_ADV,
+ };
+ 
+ static struct switch_attr default_global[] = {
+@@ -248,6 +400,13 @@
+               .description = "Get port link information",
+               .set = NULL,
+               .get = swconfig_get_link,
++      },
++      [PORT_ADV] = {
++              .type = SWITCH_TYPE_STRING,
++              .name = "adv",
++              .description = "Get port advertisement information",
++              .set = swconfig_set_adv,
++              .get = swconfig_get_adv,
+       }
+ };
+ 
+@@ -291,6 +450,10 @@
+           !swconfig_find_attr_by_name(&ops->attr_port, "link"))
+               set_bit(PORT_LINK, &dev->def_port);
+ 
++      if (ops->set_port_adv &&
++          !swconfig_find_attr_by_name(&ops->attr_port, "adv"))
++              set_bit(PORT_ADV, &dev->def_port);
++
+       /* always present, can be no-op */
+       set_bit(GLOBAL_APPLY, &dev->def_global);
+       set_bit(GLOBAL_RESET, &dev->def_global);
+Index: linux-3.3.8/include/linux/switch.h
+===================================================================
+--- linux-3.3.8.orig/include/linux/switch.h    2012-08-25 19:56:18.223801584 
+0200
++++ linux-3.3.8/include/linux/switch.h 2012-08-25 19:56:20.663804993 +0200
+@@ -116,6 +116,7 @@
+ };
+ 
+ enum switch_port_speed {
++      SWITCH_PORT_SPEED_DEFAULT = -1, /* For phy advertisement */
+       SWITCH_PORT_SPEED_UNKNOWN = 0,
+       SWITCH_PORT_SPEED_10 = 10,
+       SWITCH_PORT_SPEED_100 = 100,
+@@ -131,6 +132,13 @@
+       enum switch_port_speed speed;
+ };
+ 
++struct switch_port_adv {
++      bool aneg;
++      bool duplex;
++      bool flow;
++      enum switch_port_speed speed;
++};
++
+ struct switch_port_stats {
+       unsigned long tx_bytes;
+       unsigned long rx_bytes;
+@@ -166,6 +174,10 @@
+       int (*apply_config)(struct switch_dev *dev);
+       int (*reset_switch)(struct switch_dev *dev);
+ 
++      int (*get_port_adv)(struct switch_dev *dev, int port,
++                          struct switch_port_adv *adv);
++      int (*set_port_adv)(struct switch_dev *dev, int port,
++                          struct switch_port_adv *adv);
+       int (*get_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*get_port_stats)(struct switch_dev *dev, int port,
_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to