|
@@ -4423,18 +4423,14 @@ static struct {
|
|
#define CAS_REG_LEN (sizeof(ethtool_register_table)/sizeof(int))
|
|
#define CAS_REG_LEN (sizeof(ethtool_register_table)/sizeof(int))
|
|
#define CAS_MAX_REGS (sizeof (u32)*CAS_REG_LEN)
|
|
#define CAS_MAX_REGS (sizeof (u32)*CAS_REG_LEN)
|
|
|
|
|
|
-static u8 *cas_get_regs(struct cas *cp)
|
|
|
|
|
|
+static void cas_read_regs(struct cas *cp, u8 *ptr, int len)
|
|
{
|
|
{
|
|
- u8 *ptr = kmalloc(CAS_MAX_REGS, GFP_KERNEL);
|
|
|
|
u8 *p;
|
|
u8 *p;
|
|
int i;
|
|
int i;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
- if (!ptr)
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
spin_lock_irqsave(&cp->lock, flags);
|
|
spin_lock_irqsave(&cp->lock, flags);
|
|
- for (i = 0, p = ptr; i < CAS_REG_LEN ; i ++, p += sizeof(u32)) {
|
|
|
|
|
|
+ for (i = 0, p = ptr; i < len ; i ++, p += sizeof(u32)) {
|
|
u16 hval;
|
|
u16 hval;
|
|
u32 val;
|
|
u32 val;
|
|
if (ethtool_register_table[i].offsets < 0) {
|
|
if (ethtool_register_table[i].offsets < 0) {
|
|
@@ -4447,8 +4443,6 @@ static u8 *cas_get_regs(struct cas *cp)
|
|
memcpy(p, (u8 *)&val, sizeof(u32));
|
|
memcpy(p, (u8 *)&val, sizeof(u32));
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&cp->lock, flags);
|
|
spin_unlock_irqrestore(&cp->lock, flags);
|
|
-
|
|
|
|
- return ptr;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static struct net_device_stats *cas_get_stats(struct net_device *dev)
|
|
static struct net_device_stats *cas_get_stats(struct net_device *dev)
|
|
@@ -4561,316 +4555,251 @@ static void cas_set_multicast(struct net_device *dev)
|
|
spin_unlock_irqrestore(&cp->lock, flags);
|
|
spin_unlock_irqrestore(&cp->lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Eventually add support for changing the advertisement
|
|
|
|
- * on autoneg.
|
|
|
|
- */
|
|
|
|
-static int cas_ethtool_ioctl(struct net_device *dev, void __user *ep_user)
|
|
|
|
|
|
+static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ strncpy(info->driver, DRV_MODULE_NAME, ETHTOOL_BUSINFO_LEN);
|
|
|
|
+ strncpy(info->version, DRV_MODULE_VERSION, ETHTOOL_BUSINFO_LEN);
|
|
|
|
+ info->fw_version[0] = '\0';
|
|
|
|
+ strncpy(info->bus_info, pci_name(cp->pdev), ETHTOOL_BUSINFO_LEN);
|
|
|
|
+ info->regdump_len = cp->casreg_len < CAS_MAX_REGS ?
|
|
|
|
+ cp->casreg_len : CAS_MAX_REGS;
|
|
|
|
+ info->n_stats = CAS_NUM_STAT_KEYS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
{
|
|
{
|
|
struct cas *cp = netdev_priv(dev);
|
|
struct cas *cp = netdev_priv(dev);
|
|
u16 bmcr;
|
|
u16 bmcr;
|
|
int full_duplex, speed, pause;
|
|
int full_duplex, speed, pause;
|
|
- struct ethtool_cmd ecmd;
|
|
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
enum link_state linkstate = link_up;
|
|
enum link_state linkstate = link_up;
|
|
|
|
|
|
- if (copy_from_user(&ecmd, ep_user, sizeof(ecmd)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- switch(ecmd.cmd) {
|
|
|
|
- case ETHTOOL_GDRVINFO: {
|
|
|
|
- struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO };
|
|
|
|
-
|
|
|
|
- strncpy(info.driver, DRV_MODULE_NAME,
|
|
|
|
- ETHTOOL_BUSINFO_LEN);
|
|
|
|
- strncpy(info.version, DRV_MODULE_VERSION,
|
|
|
|
- ETHTOOL_BUSINFO_LEN);
|
|
|
|
- info.fw_version[0] = '\0';
|
|
|
|
- strncpy(info.bus_info, pci_name(cp->pdev),
|
|
|
|
- ETHTOOL_BUSINFO_LEN);
|
|
|
|
- info.regdump_len = cp->casreg_len < CAS_MAX_REGS ?
|
|
|
|
- cp->casreg_len : CAS_MAX_REGS;
|
|
|
|
- info.n_stats = CAS_NUM_STAT_KEYS;
|
|
|
|
- if (copy_to_user(ep_user, &info, sizeof(info)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ cmd->advertising = 0;
|
|
|
|
+ cmd->supported = SUPPORTED_Autoneg;
|
|
|
|
+ if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
|
|
|
|
+ cmd->supported |= SUPPORTED_1000baseT_Full;
|
|
|
|
+ cmd->advertising |= ADVERTISED_1000baseT_Full;
|
|
}
|
|
}
|
|
|
|
|
|
- case ETHTOOL_GSET:
|
|
|
|
- ecmd.advertising = 0;
|
|
|
|
- ecmd.supported = SUPPORTED_Autoneg;
|
|
|
|
- if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
|
|
|
|
- ecmd.supported |= SUPPORTED_1000baseT_Full;
|
|
|
|
- ecmd.advertising |= ADVERTISED_1000baseT_Full;
|
|
|
|
|
|
+ /* Record PHY settings if HW is on. */
|
|
|
|
+ spin_lock_irqsave(&cp->lock, flags);
|
|
|
|
+ bmcr = 0;
|
|
|
|
+ linkstate = cp->lstate;
|
|
|
|
+ if (CAS_PHY_MII(cp->phy_type)) {
|
|
|
|
+ cmd->port = PORT_MII;
|
|
|
|
+ cmd->transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ?
|
|
|
|
+ XCVR_INTERNAL : XCVR_EXTERNAL;
|
|
|
|
+ cmd->phy_address = cp->phy_addr;
|
|
|
|
+ cmd->advertising |= ADVERTISED_TP | ADVERTISED_MII |
|
|
|
|
+ ADVERTISED_10baseT_Half |
|
|
|
|
+ ADVERTISED_10baseT_Full |
|
|
|
|
+ ADVERTISED_100baseT_Half |
|
|
|
|
+ ADVERTISED_100baseT_Full;
|
|
|
|
+
|
|
|
|
+ cmd->supported |=
|
|
|
|
+ (SUPPORTED_10baseT_Half |
|
|
|
|
+ SUPPORTED_10baseT_Full |
|
|
|
|
+ SUPPORTED_100baseT_Half |
|
|
|
|
+ SUPPORTED_100baseT_Full |
|
|
|
|
+ SUPPORTED_TP | SUPPORTED_MII);
|
|
|
|
+
|
|
|
|
+ if (cp->hw_running) {
|
|
|
|
+ cas_mif_poll(cp, 0);
|
|
|
|
+ bmcr = cas_phy_read(cp, MII_BMCR);
|
|
|
|
+ cas_read_mii_link_mode(cp, &full_duplex,
|
|
|
|
+ &speed, &pause);
|
|
|
|
+ cas_mif_poll(cp, 1);
|
|
}
|
|
}
|
|
|
|
|
|
- /* Record PHY settings if HW is on. */
|
|
|
|
- spin_lock_irqsave(&cp->lock, flags);
|
|
|
|
- bmcr = 0;
|
|
|
|
- linkstate = cp->lstate;
|
|
|
|
- if (CAS_PHY_MII(cp->phy_type)) {
|
|
|
|
- ecmd.port = PORT_MII;
|
|
|
|
- ecmd.transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ?
|
|
|
|
- XCVR_INTERNAL : XCVR_EXTERNAL;
|
|
|
|
- ecmd.phy_address = cp->phy_addr;
|
|
|
|
- ecmd.advertising |= ADVERTISED_TP | ADVERTISED_MII |
|
|
|
|
- ADVERTISED_10baseT_Half |
|
|
|
|
- ADVERTISED_10baseT_Full |
|
|
|
|
- ADVERTISED_100baseT_Half |
|
|
|
|
- ADVERTISED_100baseT_Full;
|
|
|
|
-
|
|
|
|
- ecmd.supported |=
|
|
|
|
- (SUPPORTED_10baseT_Half |
|
|
|
|
- SUPPORTED_10baseT_Full |
|
|
|
|
- SUPPORTED_100baseT_Half |
|
|
|
|
- SUPPORTED_100baseT_Full |
|
|
|
|
- SUPPORTED_TP | SUPPORTED_MII);
|
|
|
|
-
|
|
|
|
- if (cp->hw_running) {
|
|
|
|
- cas_mif_poll(cp, 0);
|
|
|
|
- bmcr = cas_phy_read(cp, MII_BMCR);
|
|
|
|
- cas_read_mii_link_mode(cp, &full_duplex,
|
|
|
|
- &speed, &pause);
|
|
|
|
- cas_mif_poll(cp, 1);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
- ecmd.port = PORT_FIBRE;
|
|
|
|
- ecmd.transceiver = XCVR_INTERNAL;
|
|
|
|
- ecmd.phy_address = 0;
|
|
|
|
- ecmd.supported |= SUPPORTED_FIBRE;
|
|
|
|
- ecmd.advertising |= ADVERTISED_FIBRE;
|
|
|
|
-
|
|
|
|
- if (cp->hw_running) {
|
|
|
|
- /* pcs uses the same bits as mii */
|
|
|
|
- bmcr = readl(cp->regs + REG_PCS_MII_CTRL);
|
|
|
|
- cas_read_pcs_link_mode(cp, &full_duplex,
|
|
|
|
- &speed, &pause);
|
|
|
|
- }
|
|
|
|
|
|
+ } else {
|
|
|
|
+ cmd->port = PORT_FIBRE;
|
|
|
|
+ cmd->transceiver = XCVR_INTERNAL;
|
|
|
|
+ cmd->phy_address = 0;
|
|
|
|
+ cmd->supported |= SUPPORTED_FIBRE;
|
|
|
|
+ cmd->advertising |= ADVERTISED_FIBRE;
|
|
|
|
+
|
|
|
|
+ if (cp->hw_running) {
|
|
|
|
+ /* pcs uses the same bits as mii */
|
|
|
|
+ bmcr = readl(cp->regs + REG_PCS_MII_CTRL);
|
|
|
|
+ cas_read_pcs_link_mode(cp, &full_duplex,
|
|
|
|
+ &speed, &pause);
|
|
}
|
|
}
|
|
- spin_unlock_irqrestore(&cp->lock, flags);
|
|
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&cp->lock, flags);
|
|
|
|
|
|
- if (bmcr & BMCR_ANENABLE) {
|
|
|
|
- ecmd.advertising |= ADVERTISED_Autoneg;
|
|
|
|
- ecmd.autoneg = AUTONEG_ENABLE;
|
|
|
|
- ecmd.speed = ((speed == 10) ?
|
|
|
|
- SPEED_10 :
|
|
|
|
- ((speed == 1000) ?
|
|
|
|
- SPEED_1000 : SPEED_100));
|
|
|
|
- ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
|
|
|
|
|
|
+ if (bmcr & BMCR_ANENABLE) {
|
|
|
|
+ cmd->advertising |= ADVERTISED_Autoneg;
|
|
|
|
+ cmd->autoneg = AUTONEG_ENABLE;
|
|
|
|
+ cmd->speed = ((speed == 10) ?
|
|
|
|
+ SPEED_10 :
|
|
|
|
+ ((speed == 1000) ?
|
|
|
|
+ SPEED_1000 : SPEED_100));
|
|
|
|
+ cmd->duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
|
|
|
|
+ } else {
|
|
|
|
+ cmd->autoneg = AUTONEG_DISABLE;
|
|
|
|
+ cmd->speed =
|
|
|
|
+ (bmcr & CAS_BMCR_SPEED1000) ?
|
|
|
|
+ SPEED_1000 :
|
|
|
|
+ ((bmcr & BMCR_SPEED100) ? SPEED_100:
|
|
|
|
+ SPEED_10);
|
|
|
|
+ cmd->duplex =
|
|
|
|
+ (bmcr & BMCR_FULLDPLX) ?
|
|
|
|
+ DUPLEX_FULL : DUPLEX_HALF;
|
|
|
|
+ }
|
|
|
|
+ if (linkstate != link_up) {
|
|
|
|
+ /* Force these to "unknown" if the link is not up and
|
|
|
|
+ * autonogotiation in enabled. We can set the link
|
|
|
|
+ * speed to 0, but not cmd->duplex,
|
|
|
|
+ * because its legal values are 0 and 1. Ethtool will
|
|
|
|
+ * print the value reported in parentheses after the
|
|
|
|
+ * word "Unknown" for unrecognized values.
|
|
|
|
+ *
|
|
|
|
+ * If in forced mode, we report the speed and duplex
|
|
|
|
+ * settings that we configured.
|
|
|
|
+ */
|
|
|
|
+ if (cp->link_cntl & BMCR_ANENABLE) {
|
|
|
|
+ cmd->speed = 0;
|
|
|
|
+ cmd->duplex = 0xff;
|
|
} else {
|
|
} else {
|
|
- ecmd.autoneg = AUTONEG_DISABLE;
|
|
|
|
- ecmd.speed =
|
|
|
|
- (bmcr & CAS_BMCR_SPEED1000) ?
|
|
|
|
- SPEED_1000 :
|
|
|
|
- ((bmcr & BMCR_SPEED100) ? SPEED_100:
|
|
|
|
- SPEED_10);
|
|
|
|
- ecmd.duplex =
|
|
|
|
- (bmcr & BMCR_FULLDPLX) ?
|
|
|
|
- DUPLEX_FULL : DUPLEX_HALF;
|
|
|
|
- }
|
|
|
|
- if (linkstate != link_up) {
|
|
|
|
- /* Force these to "unknown" if the link is not up and
|
|
|
|
- * autonogotiation in enabled. We can set the link
|
|
|
|
- * speed to 0, but not ecmd.duplex,
|
|
|
|
- * because its legal values are 0 and 1. Ethtool will
|
|
|
|
- * print the value reported in parentheses after the
|
|
|
|
- * word "Unknown" for unrecognized values.
|
|
|
|
- *
|
|
|
|
- * If in forced mode, we report the speed and duplex
|
|
|
|
- * settings that we configured.
|
|
|
|
- */
|
|
|
|
- if (cp->link_cntl & BMCR_ANENABLE) {
|
|
|
|
- ecmd.speed = 0;
|
|
|
|
- ecmd.duplex = 0xff;
|
|
|
|
- } else {
|
|
|
|
- ecmd.speed = SPEED_10;
|
|
|
|
- if (cp->link_cntl & BMCR_SPEED100) {
|
|
|
|
- ecmd.speed = SPEED_100;
|
|
|
|
- } else if (cp->link_cntl & CAS_BMCR_SPEED1000) {
|
|
|
|
- ecmd.speed = SPEED_1000;
|
|
|
|
- }
|
|
|
|
- ecmd.duplex = (cp->link_cntl & BMCR_FULLDPLX)?
|
|
|
|
- DUPLEX_FULL : DUPLEX_HALF;
|
|
|
|
|
|
+ cmd->speed = SPEED_10;
|
|
|
|
+ if (cp->link_cntl & BMCR_SPEED100) {
|
|
|
|
+ cmd->speed = SPEED_100;
|
|
|
|
+ } else if (cp->link_cntl & CAS_BMCR_SPEED1000) {
|
|
|
|
+ cmd->speed = SPEED_1000;
|
|
}
|
|
}
|
|
|
|
+ cmd->duplex = (cp->link_cntl & BMCR_FULLDPLX)?
|
|
|
|
+ DUPLEX_FULL : DUPLEX_HALF;
|
|
}
|
|
}
|
|
- if (copy_to_user(ep_user, &ecmd, sizeof(ecmd)))
|
|
|
|
- return -EFAULT;
|
|
|
|
- return 0;
|
|
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- case ETHTOOL_SSET:
|
|
|
|
- if (!capable(CAP_NET_ADMIN))
|
|
|
|
- return -EPERM;
|
|
|
|
|
|
+static int cas_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
- /* Verify the settings we care about. */
|
|
|
|
- if (ecmd.autoneg != AUTONEG_ENABLE &&
|
|
|
|
- ecmd.autoneg != AUTONEG_DISABLE)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ /* Verify the settings we care about. */
|
|
|
|
+ if (cmd->autoneg != AUTONEG_ENABLE &&
|
|
|
|
+ cmd->autoneg != AUTONEG_DISABLE)
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- if (ecmd.autoneg == AUTONEG_DISABLE &&
|
|
|
|
- ((ecmd.speed != SPEED_1000 &&
|
|
|
|
- ecmd.speed != SPEED_100 &&
|
|
|
|
- ecmd.speed != SPEED_10) ||
|
|
|
|
- (ecmd.duplex != DUPLEX_HALF &&
|
|
|
|
- ecmd.duplex != DUPLEX_FULL)))
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (cmd->autoneg == AUTONEG_DISABLE &&
|
|
|
|
+ ((cmd->speed != SPEED_1000 &&
|
|
|
|
+ cmd->speed != SPEED_100 &&
|
|
|
|
+ cmd->speed != SPEED_10) ||
|
|
|
|
+ (cmd->duplex != DUPLEX_HALF &&
|
|
|
|
+ cmd->duplex != DUPLEX_FULL)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- /* Apply settings and restart link process. */
|
|
|
|
- spin_lock_irqsave(&cp->lock, flags);
|
|
|
|
- cas_begin_auto_negotiation(cp, &ecmd);
|
|
|
|
- spin_unlock_irqrestore(&cp->lock, flags);
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* Apply settings and restart link process. */
|
|
|
|
+ spin_lock_irqsave(&cp->lock, flags);
|
|
|
|
+ cas_begin_auto_negotiation(cp, cmd);
|
|
|
|
+ spin_unlock_irqrestore(&cp->lock, flags);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- case ETHTOOL_NWAY_RST:
|
|
|
|
- if ((cp->link_cntl & BMCR_ANENABLE) == 0)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+static int cas_nway_reset(struct net_device *dev)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
- /* Restart link process. */
|
|
|
|
- spin_lock_irqsave(&cp->lock, flags);
|
|
|
|
- cas_begin_auto_negotiation(cp, NULL);
|
|
|
|
- spin_unlock_irqrestore(&cp->lock, flags);
|
|
|
|
|
|
+ if ((cp->link_cntl & BMCR_ANENABLE) == 0)
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ /* Restart link process. */
|
|
|
|
+ spin_lock_irqsave(&cp->lock, flags);
|
|
|
|
+ cas_begin_auto_negotiation(cp, NULL);
|
|
|
|
+ spin_unlock_irqrestore(&cp->lock, flags);
|
|
|
|
|
|
- case ETHTOOL_GWOL:
|
|
|
|
- case ETHTOOL_SWOL:
|
|
|
|
- break; /* doesn't exist */
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- /* get link status */
|
|
|
|
- case ETHTOOL_GLINK: {
|
|
|
|
- struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
|
|
|
|
|
|
+static u32 cas_get_link(struct net_device *dev)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ return cp->lstate == link_up;
|
|
|
|
+}
|
|
|
|
|
|
- edata.data = (cp->lstate == link_up);
|
|
|
|
- if (copy_to_user(ep_user, &edata, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
|
|
+static u32 cas_get_msglevel(struct net_device *dev)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ return cp->msg_enable;
|
|
|
|
+}
|
|
|
|
|
|
- /* get message-level */
|
|
|
|
- case ETHTOOL_GMSGLVL: {
|
|
|
|
- struct ethtool_value edata = { .cmd = ETHTOOL_GMSGLVL };
|
|
|
|
|
|
+static void cas_set_msglevel(struct net_device *dev, u32 value)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ cp->msg_enable = value;
|
|
|
|
+}
|
|
|
|
|
|
- edata.data = cp->msg_enable;
|
|
|
|
- if (copy_to_user(ep_user, &edata, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
|
|
+static int cas_get_regs_len(struct net_device *dev)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ return cp->casreg_len < CAS_MAX_REGS ? cp->casreg_len: CAS_MAX_REGS;
|
|
|
|
+}
|
|
|
|
|
|
- /* set message-level */
|
|
|
|
- case ETHTOOL_SMSGLVL: {
|
|
|
|
- struct ethtool_value edata;
|
|
|
|
|
|
+static void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs,
|
|
|
|
+ void *p)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ regs->version = 0;
|
|
|
|
+ /* cas_read_regs handles locks (cp->lock). */
|
|
|
|
+ cas_read_regs(cp, p, regs->len / sizeof(u32));
|
|
|
|
+}
|
|
|
|
|
|
- if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
- return (-EPERM);
|
|
|
|
- }
|
|
|
|
- if (copy_from_user(&edata, ep_user, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
- cp->msg_enable = edata.data;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
|
|
+static int cas_get_stats_count(struct net_device *dev)
|
|
|
|
+{
|
|
|
|
+ return CAS_NUM_STAT_KEYS;
|
|
|
|
+}
|
|
|
|
|
|
- case ETHTOOL_GREGS: {
|
|
|
|
- struct ethtool_regs edata;
|
|
|
|
- u8 *ptr;
|
|
|
|
- int len = cp->casreg_len < CAS_MAX_REGS ?
|
|
|
|
- cp->casreg_len: CAS_MAX_REGS;
|
|
|
|
-
|
|
|
|
- if (copy_from_user(&edata, ep_user, sizeof (edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- if (edata.len > len)
|
|
|
|
- edata.len = len;
|
|
|
|
- edata.version = 0;
|
|
|
|
- if (copy_to_user (ep_user, &edata, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- /* cas_get_regs handles locks (cp->lock). */
|
|
|
|
- ptr = cas_get_regs(cp);
|
|
|
|
- if (ptr == NULL)
|
|
|
|
- return -ENOMEM;
|
|
|
|
- if (copy_to_user(ep_user + sizeof (edata), ptr, edata.len))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- kfree(ptr);
|
|
|
|
- return (0);
|
|
|
|
- }
|
|
|
|
- case ETHTOOL_GSTRINGS: {
|
|
|
|
- struct ethtool_gstrings edata;
|
|
|
|
- int len;
|
|
|
|
-
|
|
|
|
- if (copy_from_user(&edata, ep_user, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- len = edata.len;
|
|
|
|
- switch(edata.string_set) {
|
|
|
|
- case ETH_SS_STATS:
|
|
|
|
- edata.len = (len < CAS_NUM_STAT_KEYS) ?
|
|
|
|
- len : CAS_NUM_STAT_KEYS;
|
|
|
|
- if (copy_to_user(ep_user, &edata, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- if (copy_to_user(ep_user + sizeof(edata),
|
|
|
|
- ðtool_cassini_statnames,
|
|
|
|
- (edata.len * ETH_GSTRING_LEN)))
|
|
|
|
- return -EFAULT;
|
|
|
|
- return 0;
|
|
|
|
- default:
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- case ETHTOOL_GSTATS: {
|
|
|
|
- int i = 0;
|
|
|
|
- u64 *tmp;
|
|
|
|
- struct ethtool_stats edata;
|
|
|
|
- struct net_device_stats *stats;
|
|
|
|
- int len;
|
|
|
|
-
|
|
|
|
- if (copy_from_user(&edata, ep_user, sizeof(edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- len = edata.n_stats;
|
|
|
|
- stats = cas_get_stats(cp->dev);
|
|
|
|
- edata.cmd = ETHTOOL_GSTATS;
|
|
|
|
- edata.n_stats = (len < CAS_NUM_STAT_KEYS) ?
|
|
|
|
- len : CAS_NUM_STAT_KEYS;
|
|
|
|
- if (copy_to_user(ep_user, &edata, sizeof (edata)))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- tmp = kmalloc(sizeof(u64)*CAS_NUM_STAT_KEYS, GFP_KERNEL);
|
|
|
|
- if (tmp) {
|
|
|
|
- tmp[i++] = stats->collisions;
|
|
|
|
- tmp[i++] = stats->rx_bytes;
|
|
|
|
- tmp[i++] = stats->rx_crc_errors;
|
|
|
|
- tmp[i++] = stats->rx_dropped;
|
|
|
|
- tmp[i++] = stats->rx_errors;
|
|
|
|
- tmp[i++] = stats->rx_fifo_errors;
|
|
|
|
- tmp[i++] = stats->rx_frame_errors;
|
|
|
|
- tmp[i++] = stats->rx_length_errors;
|
|
|
|
- tmp[i++] = stats->rx_over_errors;
|
|
|
|
- tmp[i++] = stats->rx_packets;
|
|
|
|
- tmp[i++] = stats->tx_aborted_errors;
|
|
|
|
- tmp[i++] = stats->tx_bytes;
|
|
|
|
- tmp[i++] = stats->tx_dropped;
|
|
|
|
- tmp[i++] = stats->tx_errors;
|
|
|
|
- tmp[i++] = stats->tx_fifo_errors;
|
|
|
|
- tmp[i++] = stats->tx_packets;
|
|
|
|
- BUG_ON(i != CAS_NUM_STAT_KEYS);
|
|
|
|
-
|
|
|
|
- i = copy_to_user(ep_user + sizeof(edata),
|
|
|
|
- tmp, sizeof(u64)*edata.n_stats);
|
|
|
|
- kfree(tmp);
|
|
|
|
- } else {
|
|
|
|
- return -ENOMEM;
|
|
|
|
- }
|
|
|
|
- if (i)
|
|
|
|
- return -EFAULT;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+static void cas_get_strings(struct net_device *dev, u32 stringset, u8 *data)
|
|
|
|
+{
|
|
|
|
+ memcpy(data, ðtool_cassini_statnames,
|
|
|
|
+ CAS_NUM_STAT_KEYS * ETH_GSTRING_LEN);
|
|
|
|
+}
|
|
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
|
|
+static void cas_get_ethtool_stats(struct net_device *dev,
|
|
|
|
+ struct ethtool_stats *estats, u64 *data)
|
|
|
|
+{
|
|
|
|
+ struct cas *cp = netdev_priv(dev);
|
|
|
|
+ struct net_device_stats *stats = cas_get_stats(cp->dev);
|
|
|
|
+ int i = 0;
|
|
|
|
+ data[i++] = stats->collisions;
|
|
|
|
+ data[i++] = stats->rx_bytes;
|
|
|
|
+ data[i++] = stats->rx_crc_errors;
|
|
|
|
+ data[i++] = stats->rx_dropped;
|
|
|
|
+ data[i++] = stats->rx_errors;
|
|
|
|
+ data[i++] = stats->rx_fifo_errors;
|
|
|
|
+ data[i++] = stats->rx_frame_errors;
|
|
|
|
+ data[i++] = stats->rx_length_errors;
|
|
|
|
+ data[i++] = stats->rx_over_errors;
|
|
|
|
+ data[i++] = stats->rx_packets;
|
|
|
|
+ data[i++] = stats->tx_aborted_errors;
|
|
|
|
+ data[i++] = stats->tx_bytes;
|
|
|
|
+ data[i++] = stats->tx_dropped;
|
|
|
|
+ data[i++] = stats->tx_errors;
|
|
|
|
+ data[i++] = stats->tx_fifo_errors;
|
|
|
|
+ data[i++] = stats->tx_packets;
|
|
|
|
+ BUG_ON(i != CAS_NUM_STAT_KEYS);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct ethtool_ops cas_ethtool_ops = {
|
|
|
|
+ .get_drvinfo = cas_get_drvinfo,
|
|
|
|
+ .get_settings = cas_get_settings,
|
|
|
|
+ .set_settings = cas_set_settings,
|
|
|
|
+ .nway_reset = cas_nway_reset,
|
|
|
|
+ .get_link = cas_get_link,
|
|
|
|
+ .get_msglevel = cas_get_msglevel,
|
|
|
|
+ .set_msglevel = cas_set_msglevel,
|
|
|
|
+ .get_regs_len = cas_get_regs_len,
|
|
|
|
+ .get_regs = cas_get_regs,
|
|
|
|
+ .get_stats_count = cas_get_stats_count,
|
|
|
|
+ .get_strings = cas_get_strings,
|
|
|
|
+ .get_ethtool_stats = cas_get_ethtool_stats,
|
|
|
|
+};
|
|
|
|
+
|
|
static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
{
|
|
{
|
|
struct cas *cp = netdev_priv(dev);
|
|
struct cas *cp = netdev_priv(dev);
|
|
@@ -4883,10 +4812,6 @@ static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
*/
|
|
*/
|
|
down(&cp->pm_sem);
|
|
down(&cp->pm_sem);
|
|
switch (cmd) {
|
|
switch (cmd) {
|
|
- case SIOCETHTOOL:
|
|
|
|
- rc = cas_ethtool_ioctl(dev, ifr->ifr_data);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
|
|
case SIOCGMIIPHY: /* Get address of MII PHY in use. */
|
|
data->phy_id = cp->phy_addr;
|
|
data->phy_id = cp->phy_addr;
|
|
/* Fallthrough... */
|
|
/* Fallthrough... */
|
|
@@ -5112,6 +5037,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
|
|
dev->get_stats = cas_get_stats;
|
|
dev->get_stats = cas_get_stats;
|
|
dev->set_multicast_list = cas_set_multicast;
|
|
dev->set_multicast_list = cas_set_multicast;
|
|
dev->do_ioctl = cas_ioctl;
|
|
dev->do_ioctl = cas_ioctl;
|
|
|
|
+ dev->ethtool_ops = &cas_ethtool_ops;
|
|
dev->tx_timeout = cas_tx_timeout;
|
|
dev->tx_timeout = cas_tx_timeout;
|
|
dev->watchdog_timeo = CAS_TX_TIMEOUT;
|
|
dev->watchdog_timeo = CAS_TX_TIMEOUT;
|
|
dev->change_mtu = cas_change_mtu;
|
|
dev->change_mtu = cas_change_mtu;
|