From 1496f6df029f75cfa78662c0f2dde2105952fc7b Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 26 Aug 2003 07:39:10 -0400 Subject: [PATCH] [netdrvr 8139cp] ethtool_ops support --- drivers/net/8139cp.c | 444 +++++++++++++++++-------------------------- 1 file changed, 174 insertions(+), 270 deletions(-) diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 995bc19afaad6..9ba98da4eaa0e 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -30,8 +30,6 @@ * Constants (module parms?) for Rx work limit * Complete reset on PciErr * Consider Rx interrupt mitigation using TimerIntr - * Implement 8139C+ statistics dump; maybe not... - h/w stats can be reset only by software reset * Handle netif_rx return value * Investigate using skb->priority with h/w VLAN priority * Investigate using High Priority Tx Queue with skb->priority @@ -41,14 +39,13 @@ Tx descriptor bit * The real minimum of CP_MIN_MTU is 4 bytes. However, for this to be supported, one must(?) turn on packet padding. - * Support 8169 GMII * Support external MII transceivers */ #define DRV_NAME "8139cp" -#define DRV_VERSION "0.3.0" -#define DRV_RELDATE "Sep 29, 2002" +#define DRV_VERSION "0.5" +#define DRV_RELDATE "Aug 26, 2003" #include @@ -1304,8 +1301,8 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, } /* Set the ethtool Wake-on-LAN settings */ -static void netdev_set_wol (struct cp_private *cp, - const struct ethtool_wolinfo *wol) +static int netdev_set_wol (struct cp_private *cp, + const struct ethtool_wolinfo *wol) { u8 options; @@ -1332,6 +1329,8 @@ static void netdev_set_wol (struct cp_private *cp, cpw8 (Config5, options); cp->wol_enabled = (wol->wolopts) ? 1 : 0; + + return 0; } /* Get the ethtool Wake-on-LAN settings */ @@ -1357,308 +1356,215 @@ static void netdev_get_wol (struct cp_private *cp, if (options & MWF) wol->wolopts |= WAKE_MCAST; } -static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr) +static void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - strcpy (info.bus_info, pci_name(cp->pdev)); - info.regdump_len = CP_REGS_SIZE; - info.n_stats = CP_NUM_STATS; - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } + struct cp_private *cp = dev->priv; - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&cp->lock); - mii_ethtool_gset(&cp->mii_if, &ecmd); - spin_unlock_irq(&cp->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&cp->lock); - r = mii_ethtool_sset(&cp->mii_if, &ecmd); - spin_unlock_irq(&cp->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&cp->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&cp->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } + strcpy (info->driver, DRV_NAME); + strcpy (info->version, DRV_VERSION); + strcpy (info->bus_info, pci_name(cp->pdev)); +} - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = cp->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - cp->msg_enable = edata.data; - return 0; - } +static int cp_get_regs_len(struct net_device *dev) +{ + return CP_REGS_SIZE; +} - /* NIC register dump */ - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - u8 *regbuf = kmalloc(CP_REGS_SIZE, GFP_KERNEL); - int rc; +static int cp_get_stats_count (struct net_device *dev) +{ + return CP_NUM_STATS; +} - if (!regbuf) - return -ENOMEM; - memset(regbuf, 0, CP_REGS_SIZE); +static int cp_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct cp_private *cp = dev->priv; + int rc; - rc = copy_from_user(®s, useraddr, sizeof(regs)); - if (rc) { - rc = -EFAULT; - goto err_out_gregs; - } - - if (regs.len > CP_REGS_SIZE) - regs.len = CP_REGS_SIZE; - if (regs.len < CP_REGS_SIZE) { - rc = -EINVAL; - goto err_out_gregs; - } + spin_lock_irq(&cp->lock); + rc = mii_ethtool_gset(&cp->mii_if, cmd); + spin_unlock_irq(&cp->lock); - regs.version = CP_REGS_VER; - rc = copy_to_user(useraddr, ®s, sizeof(regs)); - if (rc) { - rc = -EFAULT; - goto err_out_gregs; - } + return rc; +} - useraddr += offsetof(struct ethtool_regs, data); +static int cp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct cp_private *cp = dev->priv; + int rc; - spin_lock_irq(&cp->lock); - memcpy_fromio(regbuf, cp->regs, CP_REGS_SIZE); - spin_unlock_irq(&cp->lock); + spin_lock_irq(&cp->lock); + rc = mii_ethtool_sset(&cp->mii_if, cmd); + spin_unlock_irq(&cp->lock); - if (copy_to_user(useraddr, regbuf, regs.len)) - rc = -EFAULT; + return rc; +} -err_out_gregs: - kfree(regbuf); - return rc; - } +static int cp_nway_reset(struct net_device *dev) +{ + struct cp_private *cp = dev->priv; + return mii_nway_restart(&cp->mii_if); +} - /* get/set RX checksumming */ - case ETHTOOL_GRXCSUM: { - struct ethtool_value edata = { ETHTOOL_GRXCSUM }; - u16 cmd = cpr16(CpCmd) & RxChkSum; +static u32 cp_get_msglevel(struct net_device *dev) +{ + struct cp_private *cp = dev->priv; + return cp->msg_enable; +} - edata.data = cmd ? 1 : 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SRXCSUM: { - struct ethtool_value edata; - u16 cmd = cpr16(CpCmd), newcmd; +static void cp_set_msglevel(struct net_device *dev, u32 value) +{ + struct cp_private *cp = dev->priv; + cp->msg_enable = value; +} - newcmd = cmd; +static u32 cp_get_rx_csum(struct net_device *dev) +{ + struct cp_private *cp = dev->priv; + return (cpr16(CpCmd) & RxChkSum) ? 1 : 0; +} - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; +static int cp_set_rx_csum(struct net_device *dev, u32 data) +{ + struct cp_private *cp = dev->priv; + u16 cmd = cpr16(CpCmd), newcmd; - if (edata.data) - newcmd |= RxChkSum; - else - newcmd &= ~RxChkSum; + newcmd = cmd; - if (newcmd == cmd) - return 0; + if (data) + newcmd |= RxChkSum; + else + newcmd &= ~RxChkSum; + if (newcmd != cmd) { spin_lock_irq(&cp->lock); cpw16_f(CpCmd, newcmd); spin_unlock_irq(&cp->lock); } - /* get/set TX checksumming */ - case ETHTOOL_GTXCSUM: { - struct ethtool_value edata = { ETHTOOL_GTXCSUM }; - - edata.data = (cp->dev->features & NETIF_F_IP_CSUM) != 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_STXCSUM: { - struct ethtool_value edata; - - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; + return 0; +} - if (edata.data) - cp->dev->features |= NETIF_F_IP_CSUM; - else - cp->dev->features &= ~NETIF_F_IP_CSUM; +/* move this to net/core/ethtool.c */ +static int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) +{ + if (data) + dev->features |= NETIF_F_IP_CSUM; + else + dev->features &= ~NETIF_F_IP_CSUM; - return 0; - } + return 0; +} - /* get/set scatter-gather */ - case ETHTOOL_GSG: { - struct ethtool_value edata = { ETHTOOL_GSG }; +static void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct cp_private *cp = dev->priv; - edata.data = (cp->dev->features & NETIF_F_SG) != 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSG: { - struct ethtool_value edata; + if (regs->len < CP_REGS_SIZE) + return /* -EINVAL */; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; + regs->version = CP_REGS_VER; - if (edata.data) - cp->dev->features |= NETIF_F_SG; - else - cp->dev->features &= ~NETIF_F_SG; + spin_lock_irq(&cp->lock); + memcpy_fromio(p, cp->regs, CP_REGS_SIZE); + spin_unlock_irq(&cp->lock); +} - return 0; - } +static void cp_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct cp_private *cp = dev->priv; - /* get string list(s) */ - case ETHTOOL_GSTRINGS: { - struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS }; - - if (copy_from_user(&estr, useraddr, sizeof(estr))) - return -EFAULT; - if (estr.string_set != ETH_SS_STATS) - return -EINVAL; - - estr.len = CP_NUM_STATS; - if (copy_to_user(useraddr, &estr, sizeof(estr))) - return -EFAULT; - if (copy_to_user(useraddr + sizeof(estr), - ðtool_stats_keys, - sizeof(ethtool_stats_keys))) - return -EFAULT; - return 0; - } + spin_lock_irq (&cp->lock); + netdev_get_wol (cp, wol); + spin_unlock_irq (&cp->lock); +} - /* get NIC-specific statistics */ - case ETHTOOL_GSTATS: { - struct ethtool_stats estats = { ETHTOOL_GSTATS }; - u64 *tmp_stats; - unsigned int work = 100; - const unsigned int sz = sizeof(u64) * CP_NUM_STATS; - int i; - - /* begin NIC statistics dump */ - cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */ - cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); - cpr32(StatsAddr); - - estats.n_stats = CP_NUM_STATS; - if (copy_to_user(useraddr, &estats, sizeof(estats))) - return -EFAULT; - - while (work-- > 0) { - if ((cpr32(StatsAddr) & DumpStats) == 0) - break; - cpu_relax(); - } +static int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct cp_private *cp = dev->priv; + int rc; - if (cpr32(StatsAddr) & DumpStats) - return -EIO; - - tmp_stats = kmalloc(sz, GFP_KERNEL); - if (!tmp_stats) - return -ENOMEM; - memset(tmp_stats, 0, sz); - - i = 0; - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_err); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_err); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->rx_fifo); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->frame_align); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_1col); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_mcol); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_phys); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_bcast); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_ok_mcast); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_abort); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_underrun); - tmp_stats[i++] = cp->cp_stats.rx_frags; - if (i != CP_NUM_STATS) - BUG(); + spin_lock_irq (&cp->lock); + rc = netdev_set_wol (cp, wol); + spin_unlock_irq (&cp->lock); - i = copy_to_user(useraddr + sizeof(estats), - tmp_stats, sz); - kfree(tmp_stats); + return rc; +} - if (i) - return -EFAULT; - return 0; +static void cp_get_strings (struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); + break; + default: + BUG(); + break; } +} - /* get/set Wake-on-LAN settings */ - case ETHTOOL_GWOL: { - struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; - - spin_lock_irq (&cp->lock); - netdev_get_wol (cp, &wol); - spin_unlock_irq (&cp->lock); - return ((copy_to_user (useraddr, &wol, sizeof (wol)))? -EFAULT : 0); - } - - case ETHTOOL_SWOL: { - struct ethtool_wolinfo wol; - - if (copy_from_user (&wol, useraddr, sizeof (wol))) - return -EFAULT; - spin_lock_irq (&cp->lock); - netdev_set_wol (cp, &wol); - spin_unlock_irq (&cp->lock); - return 0; - } +static void cp_get_ethtool_stats (struct net_device *dev, + struct ethtool_stats *estats, u64 *tmp_stats) +{ + struct cp_private *cp = dev->priv; + unsigned int work = 100; + int i; - default: - break; + /* begin NIC statistics dump */ + cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */ + cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); + cpr32(StatsAddr); + + while (work-- > 0) { + if ((cpr32(StatsAddr) & DumpStats) == 0) + break; + cpu_relax(); } - return -EOPNOTSUPP; + if (cpr32(StatsAddr) & DumpStats) + return /* -EIO */; + + i = 0; + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_err); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_err); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->rx_fifo); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->frame_align); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_1col); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_mcol); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_phys); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_bcast); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_ok_mcast); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_abort); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_underrun); + tmp_stats[i++] = cp->cp_stats.rx_frags; + if (i != CP_NUM_STATS) + BUG(); } +static struct ethtool_ops cp_ethtool_ops = { + .get_drvinfo = cp_get_drvinfo, + .get_regs_len = cp_get_regs_len, + .get_stats_count = cp_get_stats_count, + .get_settings = cp_get_settings, + .set_settings = cp_set_settings, + .nway_reset = cp_nway_reset, + .get_link = ethtool_op_get_link, + .get_msglevel = cp_get_msglevel, + .set_msglevel = cp_set_msglevel, + .get_rx_csum = cp_get_rx_csum, + .set_rx_csum = cp_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, /* local! */ + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_regs = cp_get_regs, + .get_wol = cp_get_wol, + .set_wol = cp_set_wol, + .get_strings = cp_get_strings, + .get_ethtool_stats = cp_get_ethtool_stats, +}; static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { @@ -1669,9 +1575,6 @@ static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) if (!netif_running(dev)) return -EINVAL; - if (cmd == SIOCETHTOOL) - return cp_ethtool_ioctl(cp, (void *) rq->ifr_data); - spin_lock_irq(&cp->lock); rc = generic_mii_ioctl(&cp->mii_if, mii, cmd, NULL); spin_unlock_irq(&cp->lock); @@ -1885,6 +1788,7 @@ static int __devinit cp_init_one (struct pci_dev *pdev, #ifdef BROKEN dev->change_mtu = cp_change_mtu; #endif + dev->ethtool_ops = &cp_ethtool_ops; #if 0 dev->tx_timeout = cp_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; -- 2.53.0