[OpenWrt-Devel] [PATCH netifd] device: Support setting autoneg, speed and duplex

ben at benjii.net ben at benjii.net
Fri Mar 4 05:33:35 EST 2016


From: Ben Kelly <ben at benjii.net>

Adds configuration attributes for auto negotiation on/off, link speed
and duplex. Uses ethtool ioctls to modify link settings.

Some net drivers will not accept ethtool operations until after the
IFF_UP interface flag is set, so these ioctls are being called from
system_if_up() (after the IFF_UP flag is set), rather than
system_if_apply_settings().

Signed-off-by: Ben Kelly <ben at benjii.net>
---
 device.c       | 38 ++++++++++++++++++++++++++++++++++++++
 device.h       | 11 +++++++++++
 system-linux.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/device.c b/device.c
index 9344e1b..cf4337d 100644
--- a/device.c
+++ b/device.c
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <net/ethernet.h>
+#include <linux/ethtool.h>
 
 #ifdef linux
 #include <netinet/ether.h>
@@ -51,6 +52,9 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = {
 	[DEV_ATTR_MULTICAST_TO_UNICAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL },
 	[DEV_ATTR_MULTICAST_ROUTER] = { .name = "multicast_router", .type = BLOBMSG_TYPE_INT32 },
 	[DEV_ATTR_MULTICAST] = { .name ="multicast", .type = BLOBMSG_TYPE_BOOL },
+	[DEV_ATTR_AUTO_NEG] = { .name = "auto_negotiate", .type = BLOBMSG_TYPE_BOOL },
+	[DEV_ATTR_DUPLEX] = { .name = "duplex", .type = BLOBMSG_TYPE_STRING },
+	[DEV_ATTR_SPEED] = { .name = "speed", .type = BLOBMSG_TYPE_INT32 },
 };
 
 const struct uci_blob_param_list device_attr_list = {
@@ -177,6 +181,9 @@ device_merge_settings(struct device *dev, struct device_settings *n)
 		s->multicast : os->multicast;
 	n->multicast_to_unicast = s->multicast_to_unicast;
 	n->multicast_router = s->multicast_router;
+	n->auto_negotiate = s->auto_negotiate;
+	n->duplex = s->duplex;
+	n->speed = s->speed;
 	n->flags = s->flags | os->flags | os->valid_flags;
 }
 
@@ -187,6 +194,7 @@ device_init_settings(struct device *dev, struct blob_attr **tb)
 	struct blob_attr *cur;
 	struct ether_addr *ea;
 	bool disabled = false;
+	const char *duplex;
 
 	s->flags = 0;
 	if ((cur = tb[DEV_ATTR_ENABLED]))
@@ -295,6 +303,36 @@ device_init_settings(struct device *dev, struct blob_attr **tb)
 		s->flags |= DEV_OPT_MULTICAST;
 	}
 
+	if ((cur = tb[DEV_ATTR_AUTO_NEG])) {
+		s->auto_negotiate = blobmsg_get_bool(cur) ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+		s->flags |= DEV_OPT_AUTO_NEG;
+	}
+
+	if ((cur = tb[DEV_ATTR_DUPLEX])) {
+		duplex = blobmsg_get_string(cur);
+		if (duplex) {
+			s->duplex = !strcmp(duplex, "half") ? DUPLEX_HALF : DUPLEX_FULL;
+			s->flags |= DEV_OPT_DUPLEX;
+		}
+	}
+
+	if ((cur = tb[DEV_ATTR_SPEED])) {
+		s->speed = blobmsg_get_u32(cur);
+		switch (s->speed) {
+			case SPEED_10:
+			case SPEED_100:
+			case SPEED_1000:
+			case SPEED_2500:
+			case SPEED_10000:
+				break;
+			default:
+				DPRINTF("Invalid speed: %d", s->speed);
+				s->speed = 0;
+		}
+		if (s->speed)
+			s->flags |= DEV_OPT_SPEED;
+	}
+
 	device_set_disabled(dev, disabled);
 }
 
diff --git a/device.h b/device.h
index ac77cfb..a833830 100644
--- a/device.h
+++ b/device.h
@@ -45,6 +45,9 @@ enum {
 	DEV_ATTR_MULTICAST_TO_UNICAST,
 	DEV_ATTR_MULTICAST_ROUTER,
 	DEV_ATTR_MULTICAST,
+	DEV_ATTR_AUTO_NEG,
+	DEV_ATTR_DUPLEX,
+	DEV_ATTR_SPEED,
 	__DEV_ATTR_MAX,
 };
 
@@ -88,6 +91,9 @@ enum {
 	DEV_OPT_MULTICAST_TO_UNICAST	= (1 << 14),
 	DEV_OPT_MULTICAST_ROUTER	= (1 << 15),
 	DEV_OPT_MULTICAST		= (1 << 16),
+	DEV_OPT_AUTO_NEG		= (1 << 17),
+	DEV_OPT_DUPLEX			= (1 << 18),
+	DEV_OPT_SPEED			= (1 << 19)
 };
 
 /* events broadcasted to all users of a device */
@@ -149,6 +155,9 @@ struct device_settings {
 	bool multicast_to_unicast;
 	unsigned int multicast_router;
 	bool multicast;
+	bool auto_negotiate;
+	unsigned int duplex;
+	unsigned int speed;
 };
 
 /*
@@ -198,6 +207,8 @@ struct device {
 
 	struct device_settings orig_settings;
 	struct device_settings settings;
+
+	bool ethtool_not_supported;
 };
 
 struct device_hotplug_ops {
diff --git a/system-linux.c b/system-linux.c
index 86e373c..c7db328 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -1231,14 +1231,61 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned
 	system_if_apply_rps_xps(dev, s);
 }
 
+static void
+system_if_apply_link_settings(struct device *dev)
+{
+	struct device_settings *s = &dev->settings;
+	struct ifreq ifr;
+	struct ethtool_cmd ecmd;
+	bool ecmd_changed = false;
+
+	memset(&ecmd, 0, sizeof(ecmd));
+	memset(&ifr, 0, sizeof(ifr));
+	strcpy(ifr.ifr_name, dev->ifname);
+
+	ifr.ifr_data = (caddr_t) &ecmd;
+	ecmd.cmd = ETHTOOL_GSET;
+
+	if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) == 0) {
+		if (s->flags & DEV_OPT_AUTO_NEG && ecmd.autoneg != s->auto_negotiate) {
+			ecmd.autoneg = s->auto_negotiate;
+			ecmd_changed = true;
+		}
+		if (s->flags & DEV_OPT_DUPLEX && ecmd.duplex != s->duplex) {
+			ecmd.duplex = s->duplex;
+			ecmd_changed = true;
+		}
+		if (s->flags & DEV_OPT_SPEED && ethtool_cmd_speed(&ecmd) != s->speed) {
+			ethtool_cmd_speed_set(&ecmd, s->speed);
+			ecmd_changed = true;
+		}
+
+		if (ecmd_changed) {
+			ecmd.cmd = ETHTOOL_SSET;
+			if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) < 0)
+				netifd_log_message(L_WARNING, "Failed to set link settings for %s (%s)", dev->ifname, strerror(errno));
+		}
+	}
+	else if (errno == EOPNOTSUPP) {
+		dev->ethtool_not_supported = true;
+	}
+	else {
+		netifd_log_message(L_WARNING, "Failed to get link settings for %s (%s)", dev->ifname, strerror(errno));
+	}
+}
+
 int system_if_up(struct device *dev)
 {
+	int ret;
 	system_if_get_settings(dev, &dev->orig_settings);
 	/* Only keep orig settings based on what needs to be set */
 	dev->orig_settings.valid_flags = dev->orig_settings.flags;
 	dev->orig_settings.flags &= dev->settings.flags;
 	system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
-	return system_if_flags(dev->ifname, IFF_UP, 0);
+	ret = system_if_flags(dev->ifname, IFF_UP, 0);
+	if (!ret && !dev->ethtool_not_supported)
+		system_if_apply_link_settings(dev);
+	return ret;
 }
 
 int system_if_down(struct device *dev)
@@ -1464,6 +1511,8 @@ system_if_dump_info(struct device *dev, struct blob_buf *b)
 		system_add_link_modes(b, ecmd.supported);
 		blobmsg_close_array(b, c);
 
+		blobmsg_add_string(b, "auto-negotiation", ecmd.autoneg == AUTONEG_ENABLE ? "on" : "off");
+
 		s = blobmsg_alloc_string_buffer(b, "speed", 8);
 		snprintf(s, 8, "%d%c", ethtool_cmd_speed(&ecmd),
 			ecmd.duplex == DUPLEX_HALF ? 'H' : 'F');
-- 
1.9.1
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list