diff options
author | Amit Kumar Salecha <amit.salecha@qlogic.com> | 2010-02-01 05:25:00 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-02 15:55:46 -0800 |
commit | cdaff1854f32ac9ddb4733530f617d32188665ed (patch) | |
tree | 5d1e7b5bcf656d0071840431f1f3cbc94b828e21 /drivers/net/qlcnic/qlcnic_ethtool.c | |
parent | 7eb9855d68faabe0004ed18c2af1f0974d3c2c63 (diff) |
qlcnic: add loopback diagnostic test
Loopback test (offline) added in ethtool self test.
o Set device in loopback mode
o Send packets
o Process receive packets in qlcnic_process_rcv_ring_diag()
o Compare packets
o Reset device in normal mode.
Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/qlcnic/qlcnic_ethtool.c')
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ethtool.c | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 58c50ed791d..8da6ec8c13b 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -66,7 +66,8 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = { static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { "Register_Test_on_offline", "Link_Test_on_offline", - "Interrupt_Test_offline" + "Interrupt_Test_offline", + "Loopback_Test_offline" }; #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) @@ -614,6 +615,80 @@ static int qlcnic_get_sset_count(struct net_device *dev, int sset) } } +#define QLC_ILB_PKT_SIZE 64 + +static void qlcnic_create_loopback_buff(unsigned char *data) +{ + unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00}; + memset(data, 0x4e, QLC_ILB_PKT_SIZE); + memset(data, 0xff, 12); + memcpy(data + 12, random_data, sizeof(random_data)); +} + +int qlcnic_check_loopback_buff(unsigned char *data) +{ + unsigned char buff[QLC_ILB_PKT_SIZE]; + qlcnic_create_loopback_buff(buff); + return memcmp(data, buff, QLC_ILB_PKT_SIZE); +} + +static int qlcnic_do_ilb_test(struct qlcnic_adapter *adapter) +{ + struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx; + struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0]; + struct sk_buff *skb; + int i; + + for (i = 0; i < 16; i++) { + skb = dev_alloc_skb(QLC_ILB_PKT_SIZE); + qlcnic_create_loopback_buff(skb->data); + skb_put(skb, QLC_ILB_PKT_SIZE); + + adapter->diag_cnt = 0; + + qlcnic_xmit_frame(skb, adapter->netdev); + + msleep(5); + + qlcnic_process_rcv_ring_diag(sds_ring); + + dev_kfree_skb_any(skb); + if (!adapter->diag_cnt) + return -1; + } + return 0; +} + +static int qlcnic_loopback_test(struct net_device *netdev) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + int max_sds_rings = adapter->max_sds_rings; + int ret; + + if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + return -EIO; + + ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST); + if (ret) + goto clear_it; + + ret = qlcnic_set_ilb_mode(adapter); + if (ret) + goto done; + + ret = qlcnic_do_ilb_test(adapter); + + qlcnic_clear_ilb_mode(adapter); + +done: + qlcnic_diag_free_res(netdev, max_sds_rings); + +clear_it: + adapter->max_sds_rings = max_sds_rings; + clear_bit(__QLCNIC_RESETTING, &adapter->state); + return ret; +} + static int qlcnic_irq_test(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); @@ -656,6 +731,11 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, data[2] = qlcnic_irq_test(dev); if (data[2]) eth_test->flags |= ETH_TEST_FL_FAILED; + + data[3] = qlcnic_loopback_test(dev); + if (data[3]) + eth_test->flags |= ETH_TEST_FL_FAILED; + } data[0] = qlcnic_reg_test(dev); |