HAS_LNK_CHNG = 0x040000,
};
+#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
+#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
#define RTL_MIN_IO_SIZE 0x80
#define RTL8139B_IO_SIZE 256
};
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "early_rx" },
+ { "tx_buf_mapped" },
+ { "tx_timeouts" },
+ { "rx_lost_in_ring" },
+};
/* The rest of these values should never change. */
struct rtl_extra_stats xstats;
int time_to_die;
struct mii_if_info mii;
+ unsigned int regs_len;
};
MODULE_AUTHOR ("Jeff Garzik <jgarzik@mandrakesoft.com>");
ioaddr = (void *) pio_start;
dev->base_addr = pio_start;
tp->mmio_addr = ioaddr;
+ tp->regs_len = pio_len;
#else
/* ioremap MMIO region */
ioaddr = ioremap (mmio_start, mmio_len);
}
dev->base_addr = (long) ioaddr;
tp->mmio_addr = ioaddr;
+ tp->regs_len = mmio_len;
#endif /* USE_IO_OPS */
/* Bring old chips out of low-power mode. */
strcpy (info.driver, DRV_NAME);
strcpy (info.version, DRV_VERSION);
strcpy (info.bus_info, np->pci_dev->slot_name);
+ info.regdump_len = np->regs_len;
if (copy_to_user (useraddr, &info, sizeof (info)))
return -EFAULT;
return 0;
return rc;
}
+/* TODO: we are too slack to do reg dumping for pio, for now */
+#ifndef CONFIG_8139TOO_PIO
+ /* NIC register dump */
+ case ETHTOOL_GREGS: {
+ struct ethtool_regs regs;
+ unsigned int regs_len = np->regs_len;
+ u8 *regbuf = kmalloc(regs_len, GFP_KERNEL);
+ int rc;
+
+ if (!regbuf)
+ return -ENOMEM;
+ memset(regbuf, 0, regs_len);
+
+ rc = copy_from_user(®s, 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;
+ rc = copy_to_user(useraddr, ®s, sizeof(regs));
+ if (rc) {
+ rc = -EFAULT;
+ goto err_out_gregs;
+ }
+
+ useraddr += offsetof(struct ethtool_regs, data);
+
+ spin_lock_irq(&np->lock);
+ memcpy_fromio(regbuf, np->mmio_addr, regs_len);
+ spin_unlock_irq(&np->lock);
+
+ if (copy_to_user(useraddr, regbuf, regs_len))
+ rc = -EFAULT;
+
+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),
+ ðtool_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);
+ kfree(tmp_stats);
+
+ if (i)
+ return -EFAULT;
+ return 0;
+ }
default:
break;
}