[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