* 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
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 <linux/config.h>
}
/* 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;
cpw8 (Config5, options);
cp->wol_enabled = (wol->wolopts) ? 1 : 0;
+
+ return 0;
}
/* Get the ethtool Wake-on-LAN settings */
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)
{
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);
#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;