Commit dfb4fdc9 authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Jeff Garzik

[netdrvr 8139too] ethtool_ops support

parent 662222ae
...@@ -643,6 +643,7 @@ static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); ...@@ -643,6 +643,7 @@ static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
static void rtl8139_set_rx_mode (struct net_device *dev); static void rtl8139_set_rx_mode (struct net_device *dev);
static void __set_rx_mode (struct net_device *dev); static void __set_rx_mode (struct net_device *dev);
static void rtl8139_hw_start (struct net_device *dev); static void rtl8139_hw_start (struct net_device *dev);
static struct ethtool_ops rtl8139_ethtool_ops;
#ifdef USE_IO_OPS #ifdef USE_IO_OPS
...@@ -992,6 +993,7 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, ...@@ -992,6 +993,7 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev,
dev->get_stats = rtl8139_get_stats; dev->get_stats = rtl8139_get_stats;
dev->set_multicast_list = rtl8139_set_rx_mode; dev->set_multicast_list = rtl8139_set_rx_mode;
dev->do_ioctl = netdev_ioctl; dev->do_ioctl = netdev_ioctl;
dev->ethtool_ops = &rtl8139_ethtool_ops;
dev->tx_timeout = rtl8139_tx_timeout; dev->tx_timeout = rtl8139_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT; dev->watchdog_timeo = TX_TIMEOUT;
...@@ -2172,11 +2174,12 @@ static int rtl8139_close (struct net_device *dev) ...@@ -2172,11 +2174,12 @@ static int rtl8139_close (struct net_device *dev)
/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to /* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
other threads or interrupts aren't messing with the 8139. */ other threads or interrupts aren't messing with the 8139. */
static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{ {
struct rtl8139_private *np = dev->priv; struct rtl8139_private *np = dev->priv;
void *ioaddr = np->mmio_addr; void *ioaddr = np->mmio_addr;
spin_lock_irq(&np->lock);
if (rtl_chip_info[np->chipset].flags & HasLWake) { if (rtl_chip_info[np->chipset].flags & HasLWake) {
u8 cfg3 = RTL_R8 (Config3); u8 cfg3 = RTL_R8 (Config3);
u8 cfg5 = RTL_R8 (Config5); u8 cfg5 = RTL_R8 (Config5);
...@@ -2198,14 +2201,14 @@ static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) ...@@ -2198,14 +2201,14 @@ static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol)
if (cfg5 & Cfg5_BWF) if (cfg5 & Cfg5_BWF)
wol->wolopts |= WAKE_BCAST; wol->wolopts |= WAKE_BCAST;
} }
spin_unlock_irq(&np->lock);
} }
/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes /* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
that wol points to kernel memory and other threads or interrupts that wol points to kernel memory and other threads or interrupts
aren't messing with the 8139. */ aren't messing with the 8139. */
static int netdev_set_wol (struct net_device *dev, static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
const struct ethtool_wolinfo *wol)
{ {
struct rtl8139_private *np = dev->priv; struct rtl8139_private *np = dev->priv;
void *ioaddr = np->mmio_addr; void *ioaddr = np->mmio_addr;
...@@ -2219,6 +2222,7 @@ static int netdev_set_wol (struct net_device *dev, ...@@ -2219,6 +2222,7 @@ static int netdev_set_wol (struct net_device *dev,
if (wol->wolopts & ~support) if (wol->wolopts & ~support)
return -EINVAL; return -EINVAL;
spin_lock_irq(&np->lock);
cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
if (wol->wolopts & WAKE_PHY) if (wol->wolopts & WAKE_PHY)
cfg3 |= Cfg3_LinkUp; cfg3 |= Cfg3_LinkUp;
...@@ -2239,213 +2243,120 @@ static int netdev_set_wol (struct net_device *dev, ...@@ -2239,213 +2243,120 @@ static int netdev_set_wol (struct net_device *dev,
if (wol->wolopts & WAKE_BCAST) if (wol->wolopts & WAKE_BCAST)
cfg5 |= Cfg5_BWF; cfg5 |= Cfg5_BWF;
RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
spin_unlock_irq(&np->lock);
return 0; return 0;
} }
static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{ {
struct rtl8139_private *np = dev->priv; struct rtl8139_private *np = dev->priv;
u32 ethcmd; strcpy(info->driver, DRV_NAME);
strcpy(info->version, DRV_VERSION);
/* dev_ioctl() in ../../net/core/dev.c has already checked strcpy(info->bus_info, pci_name(np->pci_dev));
capable(CAP_NET_ADMIN), so don't bother with that here. */ info->regdump_len = np->regs_len;
}
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(np->pci_dev));
info.regdump_len = np->regs_len;
if (copy_to_user (useraddr, &info, sizeof (info)))
return -EFAULT;
return 0;
}
/* get settings */ static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
case ETHTOOL_GSET: { {
struct ethtool_cmd ecmd = { ETHTOOL_GSET }; struct rtl8139_private *np = dev->priv;
spin_lock_irq(&np->lock); spin_lock_irq(&np->lock);
mii_ethtool_gset(&np->mii, &ecmd); mii_ethtool_gset(&np->mii, cmd);
spin_unlock_irq(&np->lock); spin_unlock_irq(&np->lock);
if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0; return 0;
} }
/* set settings */
case ETHTOOL_SSET: { static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
int r; {
struct ethtool_cmd ecmd; struct rtl8139_private *np = dev->priv;
if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) int rc;
return -EFAULT;
spin_lock_irq(&np->lock); spin_lock_irq(&np->lock);
r = mii_ethtool_sset(&np->mii, &ecmd); rc = mii_ethtool_sset(&np->mii, cmd);
spin_unlock_irq(&np->lock); spin_unlock_irq(&np->lock);
return r; return rc;
} }
/* restart autonegotiation */
case ETHTOOL_NWAY_RST: { static int rtl8139_nway_reset(struct net_device *dev)
{
struct rtl8139_private *np = dev->priv;
return mii_nway_restart(&np->mii); return mii_nway_restart(&np->mii);
} }
/* get link status */
case ETHTOOL_GLINK: {
struct ethtool_value edata = {ETHTOOL_GLINK};
edata.data = mii_link_ok(&np->mii);
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
/* get message-level */ static u32 rtl8139_get_link(struct net_device *dev)
case ETHTOOL_GMSGLVL: { {
struct ethtool_value edata = {ETHTOOL_GMSGLVL}; struct rtl8139_private *np = dev->priv;
edata.data = debug; return mii_link_ok(&np->mii);
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;
debug = edata.data;
return 0;
}
case ETHTOOL_GWOL: static u32 rtl8139_get_msglevel(struct net_device *dev)
{ {
struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; return debug;
spin_lock_irq (&np->lock); }
netdev_get_wol (dev, &wol);
spin_unlock_irq (&np->lock);
if (copy_to_user (useraddr, &wol, sizeof (wol)))
return -EFAULT;
return 0;
}
case ETHTOOL_SWOL: static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
{ {
struct ethtool_wolinfo wol; debug = datum;
int rc; }
if (copy_from_user (&wol, useraddr, sizeof (wol)))
return -EFAULT;
spin_lock_irq (&np->lock);
rc = netdev_set_wol (dev, &wol);
spin_unlock_irq (&np->lock);
return rc;
}
/* TODO: we are too slack to do reg dumping for pio, for now */ /* TODO: we are too slack to do reg dumping for pio, for now */
#ifndef CONFIG_8139TOO_PIO #ifdef CONFIG_8139TOO_PIO
/* NIC register dump */ #define rtl8139_get_regs_len NULL
case ETHTOOL_GREGS: { #define rtl8139_get_regs NULL
struct ethtool_regs regs; #else
unsigned int regs_len = np->regs_len; static int rtl8139_get_regs_len(struct net_device *dev)
u8 *regbuf = kmalloc(regs_len, GFP_KERNEL); {
int rc; struct rtl8139_private *np = dev->priv;
return np->regs_len;
if (!regbuf) }
return -ENOMEM;
memset(regbuf, 0, regs_len);
rc = copy_from_user(&regs, useraddr, sizeof(regs));
if (rc) {
rc = -EFAULT;
goto err_out_gregs;
}
if (regs.len > regs_len)
regs.len = regs_len;
if (regs.len < regs_len) {
rc = -EINVAL;
goto err_out_gregs;
}
regs.version = RTL_REGS_VER; static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
rc = copy_to_user(useraddr, &regs, sizeof(regs)); {
if (rc) { struct rtl8139_private *np = dev->priv;
rc = -EFAULT;
goto err_out_gregs;
}
useraddr += offsetof(struct ethtool_regs, data); regs->version = RTL_REGS_VER;
spin_lock_irq(&np->lock); spin_lock_irq(&np->lock);
memcpy_fromio(regbuf, np->mmio_addr, regs_len); memcpy_fromio(regbuf, np->mmio_addr, regs->len);
spin_unlock_irq(&np->lock); spin_unlock_irq(&np->lock);
}
#endif /* CONFIG_8139TOO_MMIO */
if (copy_to_user(useraddr, regbuf, regs_len)) static int rtl8139_get_stats_count(struct net_device *dev)
rc = -EFAULT; {
return RTL_NUM_STATS;
err_out_gregs: }
kfree(regbuf);
return rc;
}
#endif /* CONFIG_8139TOO_PIO */
/* 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 = RTL_NUM_STATS;
if (copy_to_user(useraddr, &estr, sizeof(estr)))
return -EFAULT;
if (copy_to_user(useraddr + sizeof(estr),
&ethtool_stats_keys,
sizeof(ethtool_stats_keys)))
return -EFAULT;
return 0;
}
/* get NIC-specific statistics */
case ETHTOOL_GSTATS: {
struct ethtool_stats estats = { ETHTOOL_GSTATS };
u64 *tmp_stats;
const unsigned int sz = sizeof(u64) * RTL_NUM_STATS;
int i;
estats.n_stats = RTL_NUM_STATS;
if (copy_to_user(useraddr, &estats, sizeof(estats)))
return -EFAULT;
tmp_stats = kmalloc(sz, GFP_KERNEL);
if (!tmp_stats)
return -ENOMEM;
memset(tmp_stats, 0, sz);
i = 0;
tmp_stats[i++] = np->xstats.early_rx;
tmp_stats[i++] = np->xstats.tx_buf_mapped;
tmp_stats[i++] = np->xstats.tx_timeouts;
tmp_stats[i++] = np->xstats.rx_lost_in_ring;
if (i != RTL_NUM_STATS)
BUG();
i = copy_to_user(useraddr + sizeof(estats), tmp_stats, sz); static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
kfree(tmp_stats); {
struct rtl8139_private *np = dev->priv;
if (i) data[0] = np->xstats.early_rx;
return -EFAULT; data[1] = np->xstats.tx_buf_mapped;
return 0; data[2] = np->xstats.tx_timeouts;
} data[3] = np->xstats.rx_lost_in_ring;
default: }
break;
}
return -EOPNOTSUPP; static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
} }
static struct ethtool_ops rtl8139_ethtool_ops = {
.get_drvinfo = rtl8139_get_drvinfo,
.get_settings = rtl8139_get_settings,
.set_settings = rtl8139_set_settings,
.get_regs_len = rtl8139_get_regs_len,
.get_regs = rtl8139_get_regs,
.nway_reset = rtl8139_nway_reset,
.get_link = rtl8139_get_link,
.get_msglevel = rtl8139_get_msglevel,
.set_msglevel = rtl8139_set_msglevel,
.get_wol = rtl8139_get_wol,
.set_wol = rtl8139_set_wol,
.get_strings = rtl8139_get_strings,
.get_stats_count = rtl8139_get_stats_count,
.get_ethtool_stats = rtl8139_get_ethtool_stats,
};
static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{ {
...@@ -2456,14 +2367,9 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -2456,14 +2367,9 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!netif_running(dev)) if (!netif_running(dev))
return -EINVAL; return -EINVAL;
if (cmd == SIOCETHTOOL)
rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
else {
spin_lock_irq(&np->lock); spin_lock_irq(&np->lock);
rc = generic_mii_ioctl(&np->mii, data, cmd, NULL); rc = generic_mii_ioctl(&np->mii, data, cmd, NULL);
spin_unlock_irq(&np->lock); spin_unlock_irq(&np->lock);
}
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment