DORMANTno replies

[PATCH 2.6.3] pcnet32.c adds loopback test

From: Don Fry <hidden>
Date: 2004-02-20 23:08:04

This adds the ability to do a local loopback test for the adapter.  This
source was submitted by James Lewis [off-list ref].  Tested on PPC
and IA32 systems.
--- linux-2.6.3/drivers/net/pci.pcnet32.c	Fri Feb 20 14:47:30 2004
+++ linux-2.6.3/drivers/net/pcnet32.c	Fri Feb 20 15:02:25 2004
@@ -125,6 +125,11 @@
     PCNET32_PORT_ASEL			   /* 15 not supported	  */
 };
 
+static const char pcnet32_gstrings_test[][ETH_GSTRING_LEN] = {
+    "Loopback test  (offline)"
+};
+#define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN)
+
 #define MAX_UNITS 8	/* More are supported, limit only on options */
 static int options[MAX_UNITS];
 static int full_duplex[MAX_UNITS];
@@ -351,6 +356,10 @@
 static void pcnet32_watchdog(struct net_device *);
 static int mdio_read(struct net_device *dev, int phy_id, int reg_num);
 static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val);
+static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits); 
+static void pcnet32_ethtool_test(struct net_device *dev,
+	struct ethtool_test *eth_test, u64 *data);
+static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1);
 
 enum pci_flags_bit {
     PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
@@ -561,6 +570,165 @@
 	ering->rx_pending = lp->cur_rx & RX_RING_MOD_MASK;
 }
 
+static void pcnet32_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+    memcpy(data, pcnet32_gstrings_test, sizeof(pcnet32_gstrings_test));
+}
+
+static int pcnet32_self_test_count(struct net_device *dev)
+{
+    return PCNET32_TEST_LEN;
+}
+
+static void pcnet32_ethtool_test(struct net_device *dev,
+	struct ethtool_test *test, u64 *data)
+{
+    struct pcnet32_private *lp = dev->priv;
+    int rc;
+
+    if (test->flags == ETH_TEST_FL_OFFLINE) {
+	rc = pcnet32_loopback_test(dev, data);
+	if (rc) {
+	    if (netif_msg_hw(lp))
+		printk(KERN_DEBUG "%s: Loopback test failed.\n", dev->name);
+	    test->flags |= ETH_TEST_FL_FAILED;
+	} else if (netif_msg_hw(lp))
+	    printk(KERN_DEBUG "%s: Loopback test passed.\n", dev->name);
+    } else
+	printk(KERN_DEBUG "%s: No tests to run (specify 'Offline' on ethtool).",	    dev->name);
+} /* end pcnet32_ethtool_test */
+
+static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1)
+{
+    struct pcnet32_private *lp = dev->priv;
+    struct pcnet32_access *a = &lp->a;	/* access to registers */
+    ulong ioaddr = dev->base_addr;	/* card base I/O address */
+    struct sk_buff *skb;		/* sk buff */
+    int x, y, i;			/* counters */
+    int numbuffs = 4;			/* number of TX/RX buffers and descs */
+    u16 status = 0x8300;		/* TX ring status */
+    int rc;				/* return code */
+    int size;				/* size of packets */
+    unsigned char *packet;		/* source packet data */
+    static int data_len = 60;		/* length of source packets */
+    unsigned long flags;
+
+    *data1 = 1;			/* status of test, default to fail */
+    rc = 1;			/* default to fail */
+
+    spin_lock_irqsave(&lp->lock, flags);
+    lp->a.write_csr(ioaddr, 0, 0x7904);
+
+    del_timer_sync(&lp->watchdog_timer);
+
+    netif_stop_queue(dev);
+
+    /* purge & init rings but don't actually restart */
+    pcnet32_restart(dev, 0x0000);
+
+    lp->a.write_csr(ioaddr, 0, 0x0004);	/* Set STOP bit */
+
+    x = a->read_bcr(ioaddr, 32);	/* set internal loopback in BSR32 */
+    x = x | 0x00000002;
+    a->write_bcr(ioaddr, 32, x);
+
+    /* Initialize Transmit buffers. */
+    size = data_len + 15;
+    for (x=0; x<numbuffs; x++) {
+	if (!(skb = dev_alloc_skb(size))) {
+	    if (netif_msg_hw(lp))
+		printk(KERN_DEBUG "%s: Cannot allocate skb at line: %d!\n",
+		    dev->name, __LINE__);
+	    goto clean_up;
+	} else {
+	    packet = skb->data;
+	    skb_put(skb, size);		/* create space for data */
+	    lp->tx_skbuff[x] = skb;
+	    lp->tx_ring[x].length = le16_to_cpu(-skb->len);
+	    lp->tx_ring[x].misc = 0x00000000;
+
+	    /* put DA and SA into the skb */
+	    for (i=0; i<12; i++)
+		*packet++ = 0xff;
+	    /* type */
+	    *packet++ = 0x08;
+	    *packet++ = 0x06;
+	    /* packet number */
+	    *packet++ = x;
+	    /* fill packet with data */
+	    for (y=0; y<data_len; y++)
+		*packet++ = y;
+
+	    lp->tx_dma_addr[x] = pci_map_single(lp->pci_dev, skb->data,
+		    skb->len, PCI_DMA_TODEVICE);
+	    lp->tx_ring[x].base = (u32)le32_to_cpu(lp->tx_dma_addr[x]);
+	    wmb(); /* Make sure owner changes after all others are visible */
+	    lp->tx_ring[x].status = le16_to_cpu(status);
+	}
+    }
+
+    lp->a.write_csr(ioaddr, 0, 0x0002);	/* Set STRT bit */
+    spin_unlock_irqrestore(&lp->lock, flags);
+
+    mdelay(50);				/* wait a bit */
+
+    spin_lock_irqsave(&lp->lock, flags);
+    lp->a.write_csr(ioaddr, 0, 0x0004);	/* Set STOP bit */
+
+    if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {
+	printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name);
+
+	for (x=0; x<numbuffs; x++) {
+	    printk(KERN_DEBUG "%s: Packet %d:\n", dev->name, x);
+	    skb=lp->rx_skbuff[x];
+	    for (i=0; i<size; i++) {
+		printk("%02x ", *(skb->data+i));
+	    }
+	    printk("\n");
+	}
+    }
+
+    x = 0;
+    rc = 0;
+    while (x<numbuffs && !rc) {
+	skb = lp->rx_skbuff[x];
+	packet = lp->tx_skbuff[x]->data;
+	for (i=0; i<size; i++) {
+	    if (*(skb->data+i) != packet[i]) {
+		if (netif_msg_hw(lp))
+		    printk(KERN_DEBUG "%s: Error in compare! %2x - %02x %02x\n",
+			    dev->name, i, *(skb->data+i), packet[i]);
+		rc = 1;
+		break;
+	    }
+	}
+	x++;
+    }
+    if (!rc) {
+	*data1 = 0;
+    }
+
+clean_up:
+    x = a->read_csr(ioaddr, 15) & 0xFFFF;
+    a->write_csr(ioaddr, 15, (x & ~0x0044));	/* reset bits 6 and 2 */
+
+    x = a->read_bcr(ioaddr, 32);		/* reset internal loopback */
+    x = x & ~0x00000002;
+    a->write_bcr(ioaddr, 32, x);
+
+    pcnet32_restart(dev, 0x0042);		/* resume normal operation */
+
+    netif_wake_queue(dev);
+
+    mod_timer(&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT);
+
+    /* Clear interrupts, and set interrupt enable. */
+    lp->a.write_csr(ioaddr, 0, 0x7940);
+    spin_unlock_irqrestore(&lp->lock, flags);
+
+    return(rc);
+} /* end pcnet32_loopback_test  */
+
 static struct ethtool_ops pcnet32_ethtool_ops = {
 	.get_settings		= pcnet32_get_settings,
 	.set_settings		= pcnet32_set_settings,
@@ -573,6 +741,9 @@
 	.get_tx_csum		= ethtool_op_get_tx_csum,
 	.get_sg			= ethtool_op_get_sg,
 	.get_tso		= ethtool_op_get_tso,
+	.get_strings		= pcnet32_get_strings,
+	.self_test_count	= pcnet32_self_test_count,
+	.self_test		= pcnet32_ethtool_test,
 };
 
 /* only probes for non-PCI devices, the rest are handled by 
-- 
Don Fry
brazilnut@us.ibm.com
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help