move device settings to a separate struct
[project/netifd.git] / system-linux.c
index f2ee10a..0ff2b3b 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/sockios.h>
 #include <linux/if_vlan.h>
 #include <linux/if_bridge.h>
+#include <linux/ethtool.h>
 
 #include <unistd.h>
 #include <string.h>
@@ -569,31 +570,30 @@ int system_vlan_del(struct device *dev)
 }
 
 static void
-system_if_apply_settings(struct device *dev)
+system_if_apply_settings(struct device *dev, struct device_settings *s)
 {
        struct ifreq ifr;
 
        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name));
-       if (dev->flags & DEV_OPT_MTU) {
-               ifr.ifr_mtu = dev->mtu;
+       if (s->flags & DEV_OPT_MTU) {
+               ifr.ifr_mtu = s->mtu;
                ioctl(sock_ioctl, SIOCSIFMTU, &ifr);
        }
-       if (dev->flags & DEV_OPT_TXQUEUELEN) {
-               ifr.ifr_qlen = dev->txqueuelen;
+       if (s->flags & DEV_OPT_TXQUEUELEN) {
+               ifr.ifr_qlen = s->txqueuelen;
                ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr);
        }
-       if (dev->flags & DEV_OPT_MACADDR) {
-               memcpy(&ifr.ifr_hwaddr, dev->macaddr, sizeof(dev->macaddr));
+       if (s->flags & DEV_OPT_MACADDR) {
+               memcpy(&ifr.ifr_hwaddr, s->macaddr, sizeof(s->macaddr));
                ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr);
        }
-
-       dev->ifindex = system_if_resolve(dev);
 }
 
 int system_if_up(struct device *dev)
 {
-       system_if_apply_settings(dev);
+       system_if_apply_settings(dev, &dev->settings);
+       dev->ifindex = system_if_resolve(dev);
        return system_if_flags(dev->ifname, IFF_UP, 0);
 }
 
@@ -640,24 +640,28 @@ system_if_get_parent(struct device *dev)
 }
 
 static bool
-read_int_file(int dir_fd, const char *file, int *val)
+read_string_file(int dir_fd, const char *file, char *buf, int len)
 {
-       char buf[64];
-       int len, fd;
        bool ret = false;
+       char *c;
+       int fd;
 
        fd = openat(dir_fd, file, O_RDONLY);
        if (fd < 0)
                return false;
 
 retry:
-       len = read(fd, buf, sizeof(buf));
+       len = read(fd, buf, len - 1);
        if (len < 0) {
                if (errno == EINTR)
                        goto retry;
        } else if (len > 0) {
                        buf[len] = 0;
-                       *val = strtoul(buf, NULL, 0);
+
+                       c = strchr(buf, '\n');
+                       if (c)
+                               *c = 0;
+
                        ret = true;
        }
 
@@ -666,10 +670,48 @@ retry:
        return ret;
 }
 
+static bool
+read_int_file(int dir_fd, const char *file, int *val)
+{
+       char buf[64];
+       bool ret = false;
+
+       ret = read_string_file(dir_fd, file, buf, sizeof(buf));
+       if (ret)
+               *val = strtoul(buf, NULL, 0);
+
+       return ret;
+}
+
+/* Assume advertised flags == supported flags */
+static const struct {
+       uint32_t mask;
+       const char *name;
+} ethtool_link_modes[] = {
+       { ADVERTISED_10baseT_Half, "10H" },
+       { ADVERTISED_10baseT_Full, "10F" },
+       { ADVERTISED_100baseT_Half, "100H" },
+       { ADVERTISED_100baseT_Full, "100F" },
+       { ADVERTISED_1000baseT_Half, "1000H" },
+       { ADVERTISED_1000baseT_Full, "1000F" },
+};
+
+static void system_add_link_modes(struct blob_buf *b, __u32 mask)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(ethtool_link_modes); i++) {
+               if (mask & ethtool_link_modes[i].mask)
+                       blobmsg_add_string(b, NULL, ethtool_link_modes[i].name);
+       }
+}
+
 int
 system_if_dump_info(struct device *dev, struct blob_buf *b)
 {
-       char buf[64];
+       struct ethtool_cmd ecmd;
+       struct ifreq ifr;
+       char buf[64], *s;
+       void *c;
        int dir_fd, val = 0;
 
        snprintf(buf, sizeof(buf), "/sys/class/net/%s", dev->ifname);
@@ -677,6 +719,29 @@ system_if_dump_info(struct device *dev, struct blob_buf *b)
 
        if (read_int_file(dir_fd, "carrier", &val))
                blobmsg_add_u8(b, "link", !!val);
+       if (read_string_file(dir_fd, "address", buf, sizeof(buf)))
+               blobmsg_add_string(b, "macaddr", buf);
+
+       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) {
+               c = blobmsg_open_array(b, "link-advertising");
+               system_add_link_modes(b, ecmd.advertising);
+               blobmsg_close_array(b, c);
+
+               c = blobmsg_open_array(b, "link-supported");
+               system_add_link_modes(b, ecmd.supported);
+               blobmsg_close_array(b, c);
+
+               s = blobmsg_alloc_string_buffer(b, "speed", 8);
+               snprintf(s, 8, "%d%c", ethtool_cmd_speed(&ecmd),
+                       ecmd.duplex == DUPLEX_HALF ? 'H' : 'F');
+               blobmsg_add_string_buffer(b);
+       }
 
        close(dir_fd);
        return 0;