1 // SPDX-License-Identifier: GPL-2.0 2 #define pr_fmt(fmt) "bcmasp_ethtool: " fmt 3 4 #include <linux/ethtool.h> 5 #include <linux/netdevice.h> 6 #include <linux/platform_device.h> 7 8 #include "bcmasp.h" 9 #include "bcmasp_intf_defs.h" 10 11 static void bcmasp_get_drvinfo(struct net_device *dev, 12 struct ethtool_drvinfo *info) 13 { 14 strscpy(info->driver, "bcmasp", sizeof(info->driver)); 15 strscpy(info->bus_info, dev_name(dev->dev.parent), 16 sizeof(info->bus_info)); 17 } 18 19 static u32 bcmasp_get_msglevel(struct net_device *dev) 20 { 21 struct bcmasp_intf *intf = netdev_priv(dev); 22 23 return intf->msg_enable; 24 } 25 26 static void bcmasp_set_msglevel(struct net_device *dev, u32 level) 27 { 28 struct bcmasp_intf *intf = netdev_priv(dev); 29 30 intf->msg_enable = level; 31 } 32 33 #define BCMASP_SUPPORTED_WAKE (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER) 34 static void bcmasp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 35 { 36 struct bcmasp_intf *intf = netdev_priv(dev); 37 38 wol->supported = BCMASP_SUPPORTED_WAKE; 39 wol->wolopts = intf->wolopts; 40 memset(wol->sopass, 0, sizeof(wol->sopass)); 41 42 if (wol->wolopts & WAKE_MAGICSECURE) 43 memcpy(wol->sopass, intf->sopass, sizeof(intf->sopass)); 44 } 45 46 static int bcmasp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 47 { 48 struct bcmasp_intf *intf = netdev_priv(dev); 49 struct bcmasp_priv *priv = intf->parent; 50 struct device *kdev = &priv->pdev->dev; 51 52 if (!device_can_wakeup(kdev)) 53 return -EOPNOTSUPP; 54 55 /* Interface Specific */ 56 intf->wolopts = wol->wolopts; 57 if (intf->wolopts & WAKE_MAGICSECURE) 58 memcpy(intf->sopass, wol->sopass, sizeof(wol->sopass)); 59 60 mutex_lock(&priv->wol_lock); 61 priv->enable_wol(intf, !!intf->wolopts); 62 mutex_unlock(&priv->wol_lock); 63 64 return 0; 65 } 66 67 static int bcmasp_flow_insert(struct net_device *dev, struct ethtool_rxnfc *cmd) 68 { 69 struct bcmasp_intf *intf = netdev_priv(dev); 70 struct bcmasp_net_filter *nfilter; 71 u32 loc = cmd->fs.location; 72 bool wake = false; 73 74 if (cmd->fs.ring_cookie == RX_CLS_FLOW_WAKE) 75 wake = true; 76 77 /* Currently only supports WAKE filters */ 78 if (!wake) 79 return -EOPNOTSUPP; 80 81 switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 82 case ETHER_FLOW: 83 case IP_USER_FLOW: 84 case TCP_V4_FLOW: 85 case UDP_V4_FLOW: 86 case TCP_V6_FLOW: 87 case UDP_V6_FLOW: 88 break; 89 default: 90 return -EOPNOTSUPP; 91 } 92 93 /* Check if filter already exists */ 94 if (bcmasp_netfilt_check_dup(intf, &cmd->fs)) 95 return -EINVAL; 96 97 nfilter = bcmasp_netfilt_get_init(intf, loc, wake, true); 98 if (IS_ERR(nfilter)) 99 return PTR_ERR(nfilter); 100 101 /* Return the location where we did insert the filter */ 102 cmd->fs.location = nfilter->hw_index; 103 memcpy(&nfilter->fs, &cmd->fs, sizeof(struct ethtool_rx_flow_spec)); 104 105 /* Since we only support wake filters, defer register programming till 106 * suspend time. 107 */ 108 return 0; 109 } 110 111 static int bcmasp_flow_delete(struct net_device *dev, struct ethtool_rxnfc *cmd) 112 { 113 struct bcmasp_intf *intf = netdev_priv(dev); 114 struct bcmasp_net_filter *nfilter; 115 116 nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false); 117 if (IS_ERR(nfilter)) 118 return PTR_ERR(nfilter); 119 120 bcmasp_netfilt_release(intf, nfilter); 121 122 return 0; 123 } 124 125 static int bcmasp_flow_get(struct bcmasp_intf *intf, struct ethtool_rxnfc *cmd) 126 { 127 struct bcmasp_net_filter *nfilter; 128 129 nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false); 130 if (IS_ERR(nfilter)) 131 return PTR_ERR(nfilter); 132 133 memcpy(&cmd->fs, &nfilter->fs, sizeof(nfilter->fs)); 134 135 cmd->data = NUM_NET_FILTERS; 136 137 return 0; 138 } 139 140 static int bcmasp_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 141 { 142 struct bcmasp_intf *intf = netdev_priv(dev); 143 int ret = -EOPNOTSUPP; 144 145 mutex_lock(&intf->parent->net_lock); 146 147 switch (cmd->cmd) { 148 case ETHTOOL_SRXCLSRLINS: 149 ret = bcmasp_flow_insert(dev, cmd); 150 break; 151 case ETHTOOL_SRXCLSRLDEL: 152 ret = bcmasp_flow_delete(dev, cmd); 153 break; 154 default: 155 break; 156 } 157 158 mutex_unlock(&intf->parent->net_lock); 159 160 return ret; 161 } 162 163 static int bcmasp_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 164 u32 *rule_locs) 165 { 166 struct bcmasp_intf *intf = netdev_priv(dev); 167 int err = 0; 168 169 mutex_lock(&intf->parent->net_lock); 170 171 switch (cmd->cmd) { 172 case ETHTOOL_GRXCLSRLCNT: 173 cmd->rule_cnt = bcmasp_netfilt_get_active(intf); 174 /* We support specifying rule locations */ 175 cmd->data |= RX_CLS_LOC_SPECIAL; 176 break; 177 case ETHTOOL_GRXCLSRULE: 178 err = bcmasp_flow_get(intf, cmd); 179 break; 180 case ETHTOOL_GRXCLSRLALL: 181 bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); 182 cmd->data = NUM_NET_FILTERS; 183 break; 184 default: 185 err = -EOPNOTSUPP; 186 break; 187 } 188 189 mutex_unlock(&intf->parent->net_lock); 190 191 return err; 192 } 193 194 const struct ethtool_ops bcmasp_ethtool_ops = { 195 .get_drvinfo = bcmasp_get_drvinfo, 196 .get_link = ethtool_op_get_link, 197 .get_link_ksettings = phy_ethtool_get_link_ksettings, 198 .set_link_ksettings = phy_ethtool_set_link_ksettings, 199 .get_msglevel = bcmasp_get_msglevel, 200 .set_msglevel = bcmasp_set_msglevel, 201 .get_wol = bcmasp_get_wol, 202 .set_wol = bcmasp_set_wol, 203 .get_rxnfc = bcmasp_get_rxnfc, 204 .set_rxnfc = bcmasp_set_rxnfc, 205 }; 206