+static void
+system_add_link_mode_name(struct blob_buf *b, int i, bool half)
+{
+ char *buf;
+
+ /* allocate string buffer large enough for the mode name and a suffix
+ * "-F" or "-H" indicating full duplex or half duplex.
+ */
+ buf = blobmsg_alloc_string_buffer(b, NULL, strlen(ethtool_modes[i].name) + 3);
+ if (!buf)
+ return;
+
+ strcpy(buf, ethtool_modes[i].name);
+ if (half)
+ strcat(buf, "-H");
+ else
+ strcat(buf, "-F");
+
+ blobmsg_add_string_buffer(b);
+}
+
+static void
+system_add_link_modes(__s8 nwords, struct blob_buf *b, __u32 *mask)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(ethtool_modes); i++) {
+ if (ethtool_link_mode_test_bit(nwords, ethtool_modes[i].bit_half, mask))
+ system_add_link_mode_name(b, i, true);
+
+ if (ethtool_link_mode_test_bit(nwords, ethtool_modes[i].bit_full, mask))
+ system_add_link_mode_name(b, i, false);
+ }
+}
+
+static void
+system_add_pause_modes(__s8 nwords, struct blob_buf *b, __u32 *mask)
+{
+ if (ethtool_link_mode_test_bit(nwords, ETHTOOL_LINK_MODE_Pause_BIT, mask))
+ blobmsg_add_string(b, NULL, "pause");
+
+ if (ethtool_link_mode_test_bit(nwords, ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
+ blobmsg_add_string(b, NULL, "asym_pause");
+}
+
+
+static void
+system_add_ethtool_pause_an(struct blob_buf *b, __s8 nwords,
+ __u32 *advertising, __u32 *lp_advertising)
+{
+ bool an_rx = false, an_tx = false;
+ void *d;
+
+ d = blobmsg_open_array(b, "negotiated");
+
+ /* Work out negotiated pause frame usage per
+ * IEEE 802.3-2005 table 28B-3.
+ */
+ if (ethtool_link_mode_test_bit(nwords,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ advertising) &&
+ ethtool_link_mode_test_bit(nwords,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ lp_advertising)) {
+ an_tx = true;
+ an_rx = true;
+ } else if (ethtool_link_mode_test_bit(nwords,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ advertising) &&
+ ethtool_link_mode_test_bit(nwords,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ lp_advertising)) {
+ if (ethtool_link_mode_test_bit(nwords,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ advertising))
+ an_rx = true;
+ else if (ethtool_link_mode_test_bit(nwords,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ lp_advertising))
+ an_tx = true;
+ }
+ if (an_tx)
+ blobmsg_add_string(b, NULL, "rx");
+
+ if (an_rx)
+ blobmsg_add_string(b, NULL, "tx");
+
+ blobmsg_close_array(b, d);
+}
+
+static void
+system_get_ethtool_pause(struct device *dev, bool *rx_pause, bool *tx_pause, bool *pause_autoneg)
+{
+ struct ethtool_pauseparam pp;
+ struct ifreq ifr = {
+ .ifr_data = (caddr_t)&pp,
+ };
+
+ strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+ memset(&pp, 0, sizeof(pp));
+ pp.cmd = ETHTOOL_GPAUSEPARAM;
+
+ /* may fail */
+ if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) == -1) {
+ *pause_autoneg = true;
+ return;
+ }
+
+ *rx_pause = pp.rx_pause;
+ *tx_pause = pp.tx_pause;
+ *pause_autoneg = pp.autoneg;
+}
+