diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/cassini.c | 502 |
1 files changed, 214 insertions, 288 deletions
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 45831fb377a0..2e617424d3fb 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -4423,18 +4423,14 @@ static struct { #define CAS_REG_LEN (sizeof(ethtool_register_table)/sizeof(int)) #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; int i; unsigned long flags; - if (!ptr) - return NULL; - 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; u32 val; 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)); } spin_unlock_irqrestore(&cp->lock, flags); - - return ptr; } 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); } -/* 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); u16 bmcr; int full_duplex, speed, pause; - struct ethtool_cmd ecmd; unsigned long flags; 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 { - 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) { 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); switch (cmd) { - case SIOCETHTOOL: - rc = cas_ethtool_ioctl(dev, ifr->ifr_data); - break; - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ data->phy_id = cp->phy_addr; /* Fallthrough... */ @@ -5112,6 +5037,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, dev->get_stats = cas_get_stats; dev->set_multicast_list = cas_set_multicast; dev->do_ioctl = cas_ioctl; + dev->ethtool_ops = &cas_ethtool_ops; dev->tx_timeout = cas_tx_timeout; dev->watchdog_timeo = CAS_TX_TIMEOUT; dev->change_mtu = cas_change_mtu; |