Commit 1496f6df authored by Jeff Garzik's avatar Jeff Garzik

[netdrvr 8139cp] ethtool_ops support

parent 075ec350
...@@ -30,8 +30,6 @@ ...@@ -30,8 +30,6 @@
* Constants (module parms?) for Rx work limit * Constants (module parms?) for Rx work limit
* Complete reset on PciErr * Complete reset on PciErr
* Consider Rx interrupt mitigation using TimerIntr * 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 * Handle netif_rx return value
* Investigate using skb->priority with h/w VLAN priority * Investigate using skb->priority with h/w VLAN priority
* Investigate using High Priority Tx Queue with skb->priority * Investigate using High Priority Tx Queue with skb->priority
...@@ -41,14 +39,13 @@ ...@@ -41,14 +39,13 @@
Tx descriptor bit Tx descriptor bit
* The real minimum of CP_MIN_MTU is 4 bytes. However, * The real minimum of CP_MIN_MTU is 4 bytes. However,
for this to be supported, one must(?) turn on packet padding. for this to be supported, one must(?) turn on packet padding.
* Support 8169 GMII
* Support external MII transceivers * Support external MII transceivers
*/ */
#define DRV_NAME "8139cp" #define DRV_NAME "8139cp"
#define DRV_VERSION "0.3.0" #define DRV_VERSION "0.5"
#define DRV_RELDATE "Sep 29, 2002" #define DRV_RELDATE "Aug 26, 2003"
#include <linux/config.h> #include <linux/config.h>
...@@ -1304,7 +1301,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, ...@@ -1304,7 +1301,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int location,
} }
/* Set the ethtool Wake-on-LAN settings */ /* Set the ethtool Wake-on-LAN settings */
static void netdev_set_wol (struct cp_private *cp, static int netdev_set_wol (struct cp_private *cp,
const struct ethtool_wolinfo *wol) const struct ethtool_wolinfo *wol)
{ {
u8 options; u8 options;
...@@ -1332,6 +1329,8 @@ static void netdev_set_wol (struct cp_private *cp, ...@@ -1332,6 +1329,8 @@ static void netdev_set_wol (struct cp_private *cp,
cpw8 (Config5, options); cpw8 (Config5, options);
cp->wol_enabled = (wol->wolopts) ? 1 : 0; cp->wol_enabled = (wol->wolopts) ? 1 : 0;
return 0;
} }
/* Get the ethtool Wake-on-LAN settings */ /* Get the ethtool Wake-on-LAN settings */
...@@ -1357,228 +1356,158 @@ static void netdev_get_wol (struct cp_private *cp, ...@@ -1357,228 +1356,158 @@ static void netdev_get_wol (struct cp_private *cp,
if (options & MWF) wol->wolopts |= WAKE_MCAST; 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; struct cp_private *cp = dev->priv;
/* dev_ioctl() in ../../net/core/dev.c has already checked strcpy (info->driver, DRV_NAME);
capable(CAP_NET_ADMIN), so don't bother with that here. */ strcpy (info->version, DRV_VERSION);
strcpy (info->bus_info, pci_name(cp->pdev));
}
if (get_user(ethcmd, (u32 *)useraddr)) static int cp_get_regs_len(struct net_device *dev)
return -EFAULT; {
return CP_REGS_SIZE;
}
switch (ethcmd) { static int cp_get_stats_count (struct net_device *dev)
{
return CP_NUM_STATS;
}
case ETHTOOL_GDRVINFO: { static int cp_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; {
strcpy (info.driver, DRV_NAME); struct cp_private *cp = dev->priv;
strcpy (info.version, DRV_VERSION); int rc;
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;
}
/* 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); spin_lock_irq(&cp->lock);
r = mii_ethtool_sset(&cp->mii_if, &ecmd); rc = mii_ethtool_gset(&cp->mii_if, cmd);
spin_unlock_irq(&cp->lock); 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;
}
/* get message-level */ return rc;
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;
}
/* NIC register dump */ static int cp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
case ETHTOOL_GREGS: { {
struct ethtool_regs regs; struct cp_private *cp = dev->priv;
u8 *regbuf = kmalloc(CP_REGS_SIZE, GFP_KERNEL);
int rc; int rc;
if (!regbuf)
return -ENOMEM;
memset(regbuf, 0, CP_REGS_SIZE);
rc = copy_from_user(&regs, 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;
}
regs.version = CP_REGS_VER;
rc = copy_to_user(useraddr, &regs, sizeof(regs));
if (rc) {
rc = -EFAULT;
goto err_out_gregs;
}
useraddr += offsetof(struct ethtool_regs, data);
spin_lock_irq(&cp->lock); spin_lock_irq(&cp->lock);
memcpy_fromio(regbuf, cp->regs, CP_REGS_SIZE); rc = mii_ethtool_sset(&cp->mii_if, cmd);
spin_unlock_irq(&cp->lock); spin_unlock_irq(&cp->lock);
if (copy_to_user(useraddr, regbuf, regs.len))
rc = -EFAULT;
err_out_gregs:
kfree(regbuf);
return rc; return rc;
} }
/* get/set RX checksumming */ static int cp_nway_reset(struct net_device *dev)
case ETHTOOL_GRXCSUM: { {
struct ethtool_value edata = { ETHTOOL_GRXCSUM }; struct cp_private *cp = dev->priv;
u16 cmd = cpr16(CpCmd) & RxChkSum; return mii_nway_restart(&cp->mii_if);
}
edata.data = cmd ? 1 : 0; static u32 cp_get_msglevel(struct net_device *dev)
if (copy_to_user(useraddr, &edata, sizeof(edata))) {
return -EFAULT; struct cp_private *cp = dev->priv;
return 0; return cp->msg_enable;
} }
case ETHTOOL_SRXCSUM: {
struct ethtool_value edata; static void cp_set_msglevel(struct net_device *dev, u32 value)
{
struct cp_private *cp = dev->priv;
cp->msg_enable = value;
}
static u32 cp_get_rx_csum(struct net_device *dev)
{
struct cp_private *cp = dev->priv;
return (cpr16(CpCmd) & RxChkSum) ? 1 : 0;
}
static int cp_set_rx_csum(struct net_device *dev, u32 data)
{
struct cp_private *cp = dev->priv;
u16 cmd = cpr16(CpCmd), newcmd; u16 cmd = cpr16(CpCmd), newcmd;
newcmd = cmd; newcmd = cmd;
if (copy_from_user(&edata, useraddr, sizeof(edata))) if (data)
return -EFAULT;
if (edata.data)
newcmd |= RxChkSum; newcmd |= RxChkSum;
else else
newcmd &= ~RxChkSum; newcmd &= ~RxChkSum;
if (newcmd == cmd) if (newcmd != cmd) {
return 0;
spin_lock_irq(&cp->lock); spin_lock_irq(&cp->lock);
cpw16_f(CpCmd, newcmd); cpw16_f(CpCmd, newcmd);
spin_unlock_irq(&cp->lock); 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; return 0;
} }
case ETHTOOL_STXCSUM: {
struct ethtool_value edata;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
if (edata.data) /* move this to net/core/ethtool.c */
cp->dev->features |= NETIF_F_IP_CSUM; static int ethtool_op_set_tx_csum(struct net_device *dev, u32 data)
{
if (data)
dev->features |= NETIF_F_IP_CSUM;
else else
cp->dev->features &= ~NETIF_F_IP_CSUM; dev->features &= ~NETIF_F_IP_CSUM;
return 0; return 0;
} }
/* get/set scatter-gather */ static void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs,
case ETHTOOL_GSG: { void *p)
struct ethtool_value edata = { ETHTOOL_GSG }; {
struct cp_private *cp = dev->priv;
edata.data = (cp->dev->features & NETIF_F_SG) != 0; if (regs->len < CP_REGS_SIZE)
if (copy_to_user(useraddr, &edata, sizeof(edata))) return /* -EINVAL */;
return -EFAULT;
return 0;
}
case ETHTOOL_SSG: {
struct ethtool_value edata;
if (copy_from_user(&edata, useraddr, sizeof(edata))) regs->version = CP_REGS_VER;
return -EFAULT;
if (edata.data) spin_lock_irq(&cp->lock);
cp->dev->features |= NETIF_F_SG; memcpy_fromio(p, cp->regs, CP_REGS_SIZE);
else spin_unlock_irq(&cp->lock);
cp->dev->features &= ~NETIF_F_SG; }
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) */ spin_lock_irq (&cp->lock);
case ETHTOOL_GSTRINGS: { netdev_get_wol (cp, wol);
struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS }; spin_unlock_irq (&cp->lock);
}
if (copy_from_user(&estr, useraddr, sizeof(estr))) static int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol)
return -EFAULT; {
if (estr.string_set != ETH_SS_STATS) struct cp_private *cp = dev->priv;
return -EINVAL; int rc;
estr.len = CP_NUM_STATS; spin_lock_irq (&cp->lock);
if (copy_to_user(useraddr, &estr, sizeof(estr))) rc = netdev_set_wol (cp, wol);
return -EFAULT; spin_unlock_irq (&cp->lock);
if (copy_to_user(useraddr + sizeof(estr),
&ethtool_stats_keys, return rc;
sizeof(ethtool_stats_keys))) }
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, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
break;
default:
BUG();
break;
} }
}
/* get NIC-specific statistics */ static void cp_get_ethtool_stats (struct net_device *dev,
case ETHTOOL_GSTATS: { struct ethtool_stats *estats, u64 *tmp_stats)
struct ethtool_stats estats = { ETHTOOL_GSTATS }; {
u64 *tmp_stats; struct cp_private *cp = dev->priv;
unsigned int work = 100; unsigned int work = 100;
const unsigned int sz = sizeof(u64) * CP_NUM_STATS;
int i; int i;
/* begin NIC statistics dump */ /* begin NIC statistics dump */
...@@ -1586,10 +1515,6 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr) ...@@ -1586,10 +1515,6 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr)
cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); cpw32(StatsAddr, cp->nic_stats_dma | DumpStats);
cpr32(StatsAddr); cpr32(StatsAddr);
estats.n_stats = CP_NUM_STATS;
if (copy_to_user(useraddr, &estats, sizeof(estats)))
return -EFAULT;
while (work-- > 0) { while (work-- > 0) {
if ((cpr32(StatsAddr) & DumpStats) == 0) if ((cpr32(StatsAddr) & DumpStats) == 0)
break; break;
...@@ -1597,12 +1522,7 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr) ...@@ -1597,12 +1522,7 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr)
} }
if (cpr32(StatsAddr) & DumpStats) if (cpr32(StatsAddr) & DumpStats)
return -EIO; return /* -EIO */;
tmp_stats = kmalloc(sz, GFP_KERNEL);
if (!tmp_stats)
return -ENOMEM;
memset(tmp_stats, 0, sz);
i = 0; i = 0;
tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok); tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok);
...@@ -1621,44 +1541,30 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr) ...@@ -1621,44 +1541,30 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr)
tmp_stats[i++] = cp->cp_stats.rx_frags; tmp_stats[i++] = cp->cp_stats.rx_frags;
if (i != CP_NUM_STATS) if (i != CP_NUM_STATS)
BUG(); BUG();
i = copy_to_user(useraddr + sizeof(estats),
tmp_stats, sz);
kfree(tmp_stats);
if (i)
return -EFAULT;
return 0;
}
/* 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;
}
default:
break;
}
return -EOPNOTSUPP;
} }
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) 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) ...@@ -1669,9 +1575,6 @@ static int cp_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)
return cp_ethtool_ioctl(cp, (void *) rq->ifr_data);
spin_lock_irq(&cp->lock); spin_lock_irq(&cp->lock);
rc = generic_mii_ioctl(&cp->mii_if, mii, cmd, NULL); rc = generic_mii_ioctl(&cp->mii_if, mii, cmd, NULL);
spin_unlock_irq(&cp->lock); spin_unlock_irq(&cp->lock);
...@@ -1885,6 +1788,7 @@ static int __devinit cp_init_one (struct pci_dev *pdev, ...@@ -1885,6 +1788,7 @@ static int __devinit cp_init_one (struct pci_dev *pdev,
#ifdef BROKEN #ifdef BROKEN
dev->change_mtu = cp_change_mtu; dev->change_mtu = cp_change_mtu;
#endif #endif
dev->ethtool_ops = &cp_ethtool_ops;
#if 0 #if 0
dev->tx_timeout = cp_tx_timeout; dev->tx_timeout = cp_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT; dev->watchdog_timeo = TX_TIMEOUT;
......
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