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