diff options
author | Sucheta Chakraborty <sucheta.chakraborty@qlogic.com> | 2011-06-21 22:52:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-06-24 04:17:07 -0400 |
commit | 22c8c9343258feda9ea9ebb9e5f8cbb727b69454 (patch) | |
tree | fac14dcf287c032ff83c832a253178b90a2228b3 /drivers/net/qlcnic | |
parent | 9d6a6440fe30132e4d1f1aadd099345ced9178fb (diff) |
qlcnic: multi protocol internal loopback support added.
Driver will generate loopback traffic pattern and do the test. And
returns result of the test to application.
Updated driver version to 5.0.19.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/qlcnic')
-rw-r--r-- | drivers/net/qlcnic/qlcnic.h | 22 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ethtool.c | 123 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_hw.c | 50 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_init.c | 126 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_main.c | 6 |
5 files changed, 322 insertions, 5 deletions
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index 0be84bdb55b..e5454502df6 100644 --- a/drivers/net/qlcnic/qlcnic.h +++ b/drivers/net/qlcnic/qlcnic.h | |||
@@ -36,8 +36,8 @@ | |||
36 | 36 | ||
37 | #define _QLCNIC_LINUX_MAJOR 5 | 37 | #define _QLCNIC_LINUX_MAJOR 5 |
38 | #define _QLCNIC_LINUX_MINOR 0 | 38 | #define _QLCNIC_LINUX_MINOR 0 |
39 | #define _QLCNIC_LINUX_SUBVERSION 18 | 39 | #define _QLCNIC_LINUX_SUBVERSION 19 |
40 | #define QLCNIC_LINUX_VERSIONID "5.0.18" | 40 | #define QLCNIC_LINUX_VERSIONID "5.0.19" |
41 | #define QLCNIC_DRV_IDC_VER 0x01 | 41 | #define QLCNIC_DRV_IDC_VER 0x01 |
42 | #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ | 42 | #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ |
43 | (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) | 43 | (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) |
@@ -451,6 +451,7 @@ struct qlcnic_hardware_context { | |||
451 | u8 revision_id; | 451 | u8 revision_id; |
452 | u8 pci_func; | 452 | u8 pci_func; |
453 | u8 linkup; | 453 | u8 linkup; |
454 | u8 loopback_state; | ||
454 | u16 port_type; | 455 | u16 port_type; |
455 | u16 board_type; | 456 | u16 board_type; |
456 | 457 | ||
@@ -780,6 +781,13 @@ struct qlcnic_mac_list_s { | |||
780 | #define QLCNIC_IP_UP 2 | 781 | #define QLCNIC_IP_UP 2 |
781 | #define QLCNIC_IP_DOWN 3 | 782 | #define QLCNIC_IP_DOWN 3 |
782 | 783 | ||
784 | #define QLCNIC_ILB_MODE 0x1 | ||
785 | |||
786 | #define QLCNIC_LINKEVENT 0x1 | ||
787 | #define QLCNIC_LB_RESPONSE 0x2 | ||
788 | #define QLCNIC_IS_LB_CONFIGURED(VAL) \ | ||
789 | (VAL == (QLCNIC_LINKEVENT | QLCNIC_LB_RESPONSE)) | ||
790 | |||
783 | /* | 791 | /* |
784 | * Driver --> Firmware | 792 | * Driver --> Firmware |
785 | */ | 793 | */ |
@@ -789,13 +797,17 @@ struct qlcnic_mac_list_s { | |||
789 | #define QLCNIC_H2C_OPCODE_LRO_REQUEST 0x7 | 797 | #define QLCNIC_H2C_OPCODE_LRO_REQUEST 0x7 |
790 | #define QLCNIC_H2C_OPCODE_SET_MAC_RECEIVE_MODE 0xc | 798 | #define QLCNIC_H2C_OPCODE_SET_MAC_RECEIVE_MODE 0xc |
791 | #define QLCNIC_H2C_OPCODE_CONFIG_IPADDR 0x12 | 799 | #define QLCNIC_H2C_OPCODE_CONFIG_IPADDR 0x12 |
800 | |||
792 | #define QLCNIC_H2C_OPCODE_GET_LINKEVENT 0x15 | 801 | #define QLCNIC_H2C_OPCODE_GET_LINKEVENT 0x15 |
793 | #define QLCNIC_H2C_OPCODE_CONFIG_BRIDGING 0x17 | 802 | #define QLCNIC_H2C_OPCODE_CONFIG_BRIDGING 0x17 |
794 | #define QLCNIC_H2C_OPCODE_CONFIG_HW_LRO 0x18 | 803 | #define QLCNIC_H2C_OPCODE_CONFIG_HW_LRO 0x18 |
804 | #define QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK 0x13 | ||
805 | |||
795 | /* | 806 | /* |
796 | * Firmware --> Driver | 807 | * Firmware --> Driver |
797 | */ | 808 | */ |
798 | 809 | ||
810 | #define QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK 0x8f | ||
799 | #define QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE 141 | 811 | #define QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE 141 |
800 | 812 | ||
801 | #define VPORT_MISS_MODE_DROP 0 /* drop all unmatched */ | 813 | #define VPORT_MISS_MODE_DROP 0 /* drop all unmatched */ |
@@ -1430,6 +1442,12 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter); | |||
1430 | void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter, | 1442 | void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter, |
1431 | struct qlcnic_host_tx_ring *tx_ring); | 1443 | struct qlcnic_host_tx_ring *tx_ring); |
1432 | void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *); | 1444 | void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *); |
1445 | void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring); | ||
1446 | void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter); | ||
1447 | int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode); | ||
1448 | |||
1449 | /* Functions from qlcnic_ethtool.c */ | ||
1450 | int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]); | ||
1433 | 1451 | ||
1434 | /* Functions from qlcnic_main.c */ | 1452 | /* Functions from qlcnic_main.c */ |
1435 | int qlcnic_reset_context(struct qlcnic_adapter *); | 1453 | int qlcnic_reset_context(struct qlcnic_adapter *); |
diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 31f5cba6222..743035e4538 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c | |||
@@ -84,7 +84,8 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { | |||
84 | static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { | 84 | static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { |
85 | "Register_Test_on_offline", | 85 | "Register_Test_on_offline", |
86 | "Link_Test_on_offline", | 86 | "Link_Test_on_offline", |
87 | "Interrupt_Test_offline" | 87 | "Interrupt_Test_offline", |
88 | "Loopback_Test_offline" | ||
88 | }; | 89 | }; |
89 | 90 | ||
90 | #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) | 91 | #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) |
@@ -685,6 +686,123 @@ clear_it: | |||
685 | return ret; | 686 | return ret; |
686 | } | 687 | } |
687 | 688 | ||
689 | #define QLCNIC_ILB_PKT_SIZE 64 | ||
690 | #define QLCNIC_NUM_ILB_PKT 16 | ||
691 | #define QLCNIC_ILB_MAX_RCV_LOOP 10 | ||
692 | |||
693 | static void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[]) | ||
694 | { | ||
695 | unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00}; | ||
696 | |||
697 | memset(data, 0x4e, QLCNIC_ILB_PKT_SIZE); | ||
698 | |||
699 | memcpy(data, mac, ETH_ALEN); | ||
700 | memcpy(data + ETH_ALEN, mac, ETH_ALEN); | ||
701 | |||
702 | memcpy(data + 2 * ETH_ALEN, random_data, sizeof(random_data)); | ||
703 | } | ||
704 | |||
705 | int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]) | ||
706 | { | ||
707 | unsigned char buff[QLCNIC_ILB_PKT_SIZE]; | ||
708 | qlcnic_create_loopback_buff(buff, mac); | ||
709 | return memcmp(data, buff, QLCNIC_ILB_PKT_SIZE); | ||
710 | } | ||
711 | |||
712 | static int qlcnic_do_ilb_test(struct qlcnic_adapter *adapter) | ||
713 | { | ||
714 | struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; | ||
715 | struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0]; | ||
716 | struct sk_buff *skb; | ||
717 | int i, loop, cnt = 0; | ||
718 | |||
719 | for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) { | ||
720 | skb = dev_alloc_skb(QLCNIC_ILB_PKT_SIZE); | ||
721 | qlcnic_create_loopback_buff(skb->data, adapter->mac_addr); | ||
722 | skb_put(skb, QLCNIC_ILB_PKT_SIZE); | ||
723 | |||
724 | adapter->diag_cnt = 0; | ||
725 | qlcnic_xmit_frame(skb, adapter->netdev); | ||
726 | |||
727 | loop = 0; | ||
728 | do { | ||
729 | msleep(1); | ||
730 | qlcnic_process_rcv_ring_diag(sds_ring); | ||
731 | if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) | ||
732 | break; | ||
733 | } while (!adapter->diag_cnt); | ||
734 | |||
735 | dev_kfree_skb_any(skb); | ||
736 | |||
737 | if (!adapter->diag_cnt) | ||
738 | dev_warn(&adapter->pdev->dev, "ILB Test: %dth packet" | ||
739 | " not recevied\n", i + 1); | ||
740 | else | ||
741 | cnt++; | ||
742 | } | ||
743 | if (cnt != i) { | ||
744 | dev_warn(&adapter->pdev->dev, "ILB Test failed\n"); | ||
745 | return -1; | ||
746 | } | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static int qlcnic_iloopback_test(struct net_device *netdev) | ||
751 | { | ||
752 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
753 | int max_sds_rings = adapter->max_sds_rings; | ||
754 | struct qlcnic_host_sds_ring *sds_ring; | ||
755 | int loop = 0; | ||
756 | int ret; | ||
757 | |||
758 | netdev_info(netdev, "%s: in progress\n", __func__); | ||
759 | if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) { | ||
760 | netdev_warn(netdev, "Loopback test not supported for non " | ||
761 | "privilege function\n"); | ||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) | ||
766 | return -EIO; | ||
767 | |||
768 | |||
769 | ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST); | ||
770 | if (ret) | ||
771 | goto clear_it; | ||
772 | |||
773 | sds_ring = &adapter->recv_ctx->sds_rings[0]; | ||
774 | |||
775 | ret = qlcnic_set_lb_mode(adapter, QLCNIC_ILB_MODE); | ||
776 | if (ret) | ||
777 | goto free_res; | ||
778 | |||
779 | do { | ||
780 | msleep(500); | ||
781 | qlcnic_process_rcv_ring_diag(sds_ring); | ||
782 | if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) | ||
783 | break; | ||
784 | } while (!QLCNIC_IS_LB_CONFIGURED(adapter->ahw->loopback_state)); | ||
785 | |||
786 | if (!QLCNIC_IS_LB_CONFIGURED(adapter->ahw->loopback_state)) { | ||
787 | netdev_info(netdev, "firmware didnt respond to loopback " | ||
788 | "configure request\n"); | ||
789 | ret = adapter->ahw->loopback_state; | ||
790 | goto free_res; | ||
791 | } | ||
792 | |||
793 | ret = qlcnic_do_ilb_test(adapter); | ||
794 | |||
795 | qlcnic_clear_lb_mode(adapter); | ||
796 | |||
797 | free_res: | ||
798 | qlcnic_diag_free_res(netdev, max_sds_rings); | ||
799 | |||
800 | clear_it: | ||
801 | adapter->max_sds_rings = max_sds_rings; | ||
802 | clear_bit(__QLCNIC_RESETTING, &adapter->state); | ||
803 | return ret; | ||
804 | } | ||
805 | |||
688 | static void | 806 | static void |
689 | qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, | 807 | qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, |
690 | u64 *data) | 808 | u64 *data) |
@@ -704,6 +822,9 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, | |||
704 | if (data[2]) | 822 | if (data[2]) |
705 | eth_test->flags |= ETH_TEST_FL_FAILED; | 823 | eth_test->flags |= ETH_TEST_FL_FAILED; |
706 | 824 | ||
825 | data[3] = qlcnic_iloopback_test(dev); | ||
826 | if (data[3]) | ||
827 | eth_test->flags |= ETH_TEST_FL_FAILED; | ||
707 | 828 | ||
708 | } | 829 | } |
709 | } | 830 | } |
diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c index 81de563ee96..0391a04695d 100644 --- a/drivers/net/qlcnic/qlcnic_hw.c +++ b/drivers/net/qlcnic/qlcnic_hw.c | |||
@@ -533,6 +533,56 @@ void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter) | |||
533 | } | 533 | } |
534 | } | 534 | } |
535 | 535 | ||
536 | int qlcnic_set_fw_loopback(struct qlcnic_adapter *adapter, u8 flag) | ||
537 | { | ||
538 | struct qlcnic_nic_req req; | ||
539 | int rv; | ||
540 | |||
541 | memset(&req, 0, sizeof(struct qlcnic_nic_req)); | ||
542 | |||
543 | req.qhdr = cpu_to_le64(QLCNIC_HOST_REQUEST << 23); | ||
544 | req.req_hdr = cpu_to_le64(QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK | | ||
545 | ((u64) adapter->portnum << 16) | ((u64) 0x1 << 32)); | ||
546 | |||
547 | req.words[0] = cpu_to_le64(flag); | ||
548 | |||
549 | rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1); | ||
550 | if (rv != 0) | ||
551 | dev_err(&adapter->pdev->dev, "%sting loopback mode failed\n", | ||
552 | flag ? "Set" : "Reset"); | ||
553 | return rv; | ||
554 | } | ||
555 | |||
556 | int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) | ||
557 | { | ||
558 | if (qlcnic_set_fw_loopback(adapter, mode)) | ||
559 | return -EIO; | ||
560 | |||
561 | if (qlcnic_nic_set_promisc(adapter, VPORT_MISS_MODE_ACCEPT_ALL)) { | ||
562 | qlcnic_set_fw_loopback(adapter, mode); | ||
563 | return -EIO; | ||
564 | } | ||
565 | |||
566 | msleep(1000); | ||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter) | ||
571 | { | ||
572 | int mode = VPORT_MISS_MODE_DROP; | ||
573 | struct net_device *netdev = adapter->netdev; | ||
574 | |||
575 | qlcnic_set_fw_loopback(adapter, 0); | ||
576 | |||
577 | if (netdev->flags & IFF_PROMISC) | ||
578 | mode = VPORT_MISS_MODE_ACCEPT_ALL; | ||
579 | else if (netdev->flags & IFF_ALLMULTI) | ||
580 | mode = VPORT_MISS_MODE_ACCEPT_MULTI; | ||
581 | |||
582 | qlcnic_nic_set_promisc(adapter, mode); | ||
583 | msleep(1000); | ||
584 | } | ||
585 | |||
536 | /* | 586 | /* |
537 | * Send the interrupt coalescing parameter set by ethtool to the card. | 587 | * Send the interrupt coalescing parameter set by ethtool to the card. |
538 | */ | 588 | */ |
diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c index 5b8bbcf904d..9d5bee03a58 100644 --- a/drivers/net/qlcnic/qlcnic_init.c +++ b/drivers/net/qlcnic/qlcnic_init.c | |||
@@ -1281,6 +1281,7 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, | |||
1281 | u16 cable_len; | 1281 | u16 cable_len; |
1282 | u16 link_speed; | 1282 | u16 link_speed; |
1283 | u8 link_status, module, duplex, autoneg; | 1283 | u8 link_status, module, duplex, autoneg; |
1284 | u8 lb_status = 0; | ||
1284 | struct net_device *netdev = adapter->netdev; | 1285 | struct net_device *netdev = adapter->netdev; |
1285 | 1286 | ||
1286 | adapter->has_link_events = 1; | 1287 | adapter->has_link_events = 1; |
@@ -1292,6 +1293,7 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, | |||
1292 | link_status = msg->body[2] & 0xff; | 1293 | link_status = msg->body[2] & 0xff; |
1293 | duplex = (msg->body[2] >> 16) & 0xff; | 1294 | duplex = (msg->body[2] >> 16) & 0xff; |
1294 | autoneg = (msg->body[2] >> 24) & 0xff; | 1295 | autoneg = (msg->body[2] >> 24) & 0xff; |
1296 | lb_status = (msg->body[2] >> 32) & 0x3; | ||
1295 | 1297 | ||
1296 | module = (msg->body[2] >> 8) & 0xff; | 1298 | module = (msg->body[2] >> 8) & 0xff; |
1297 | if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE) | 1299 | if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE) |
@@ -1301,6 +1303,9 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, | |||
1301 | dev_info(&netdev->dev, "unsupported cable length %d\n", | 1303 | dev_info(&netdev->dev, "unsupported cable length %d\n", |
1302 | cable_len); | 1304 | cable_len); |
1303 | 1305 | ||
1306 | if (!link_status && (lb_status == 1)) | ||
1307 | adapter->ahw->loopback_state |= QLCNIC_LINKEVENT; | ||
1308 | |||
1304 | qlcnic_advert_link_change(adapter, link_status); | 1309 | qlcnic_advert_link_change(adapter, link_status); |
1305 | 1310 | ||
1306 | if (duplex == LINKEVENT_FULL_DUPLEX) | 1311 | if (duplex == LINKEVENT_FULL_DUPLEX) |
@@ -1319,7 +1324,9 @@ qlcnic_handle_fw_message(int desc_cnt, int index, | |||
1319 | { | 1324 | { |
1320 | struct qlcnic_fw_msg msg; | 1325 | struct qlcnic_fw_msg msg; |
1321 | struct status_desc *desc; | 1326 | struct status_desc *desc; |
1322 | int i = 0, opcode; | 1327 | struct qlcnic_adapter *adapter; |
1328 | struct device *dev; | ||
1329 | int i = 0, opcode, ret; | ||
1323 | 1330 | ||
1324 | while (desc_cnt > 0 && i < 8) { | 1331 | while (desc_cnt > 0 && i < 8) { |
1325 | desc = &sds_ring->desc_head[index]; | 1332 | desc = &sds_ring->desc_head[index]; |
@@ -1330,10 +1337,28 @@ qlcnic_handle_fw_message(int desc_cnt, int index, | |||
1330 | desc_cnt--; | 1337 | desc_cnt--; |
1331 | } | 1338 | } |
1332 | 1339 | ||
1340 | adapter = sds_ring->adapter; | ||
1341 | dev = &adapter->pdev->dev; | ||
1333 | opcode = qlcnic_get_nic_msg_opcode(msg.body[0]); | 1342 | opcode = qlcnic_get_nic_msg_opcode(msg.body[0]); |
1343 | |||
1334 | switch (opcode) { | 1344 | switch (opcode) { |
1335 | case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE: | 1345 | case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE: |
1336 | qlcnic_handle_linkevent(sds_ring->adapter, &msg); | 1346 | qlcnic_handle_linkevent(adapter, &msg); |
1347 | break; | ||
1348 | case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK: | ||
1349 | ret = (u32)(msg.body[1]); | ||
1350 | switch (ret) { | ||
1351 | case 0: | ||
1352 | adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE; | ||
1353 | break; | ||
1354 | case 1: | ||
1355 | dev_info(dev, "loopback already in progress\n"); | ||
1356 | break; | ||
1357 | default: | ||
1358 | dev_info(dev, "loopback configure request failed," | ||
1359 | " ret %x\n", ret); | ||
1360 | break; | ||
1361 | } | ||
1337 | break; | 1362 | break; |
1338 | default: | 1363 | default: |
1339 | break; | 1364 | break; |
@@ -1746,6 +1771,103 @@ qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, | |||
1746 | spin_unlock(&rds_ring->lock); | 1771 | spin_unlock(&rds_ring->lock); |
1747 | } | 1772 | } |
1748 | 1773 | ||
1774 | static void dump_skb(struct sk_buff *skb) | ||
1775 | { | ||
1776 | int i; | ||
1777 | unsigned char *data = skb->data; | ||
1778 | |||
1779 | printk(KERN_INFO "\n"); | ||
1780 | for (i = 0; i < skb->len; i++) { | ||
1781 | printk(KERN_INFO "%02x ", data[i]); | ||
1782 | if ((i & 0x0f) == 8) | ||
1783 | printk(KERN_INFO "\n"); | ||
1784 | } | ||
1785 | } | ||
1786 | |||
1787 | void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, | ||
1788 | struct qlcnic_host_sds_ring *sds_ring, | ||
1789 | int ring, u64 sts_data0) | ||
1790 | { | ||
1791 | struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; | ||
1792 | struct sk_buff *skb; | ||
1793 | struct qlcnic_host_rds_ring *rds_ring; | ||
1794 | int index, length, cksum, pkt_offset; | ||
1795 | |||
1796 | if (unlikely(ring >= adapter->max_rds_rings)) | ||
1797 | return; | ||
1798 | |||
1799 | rds_ring = &recv_ctx->rds_rings[ring]; | ||
1800 | |||
1801 | index = qlcnic_get_sts_refhandle(sts_data0); | ||
1802 | length = qlcnic_get_sts_totallength(sts_data0); | ||
1803 | if (unlikely(index >= rds_ring->num_desc)) | ||
1804 | return; | ||
1805 | |||
1806 | cksum = qlcnic_get_sts_status(sts_data0); | ||
1807 | pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); | ||
1808 | |||
1809 | skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); | ||
1810 | if (!skb) | ||
1811 | return; | ||
1812 | |||
1813 | if (length > rds_ring->skb_size) | ||
1814 | skb_put(skb, rds_ring->skb_size); | ||
1815 | else | ||
1816 | skb_put(skb, length); | ||
1817 | |||
1818 | if (pkt_offset) | ||
1819 | skb_pull(skb, pkt_offset); | ||
1820 | |||
1821 | if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr)) | ||
1822 | adapter->diag_cnt++; | ||
1823 | else | ||
1824 | dump_skb(skb); | ||
1825 | |||
1826 | dev_kfree_skb_any(skb); | ||
1827 | adapter->stats.rx_pkts++; | ||
1828 | adapter->stats.rxbytes += length; | ||
1829 | |||
1830 | return; | ||
1831 | } | ||
1832 | |||
1833 | void | ||
1834 | qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring) | ||
1835 | { | ||
1836 | struct qlcnic_adapter *adapter = sds_ring->adapter; | ||
1837 | struct status_desc *desc; | ||
1838 | u64 sts_data0; | ||
1839 | int ring, opcode, desc_cnt; | ||
1840 | |||
1841 | u32 consumer = sds_ring->consumer; | ||
1842 | |||
1843 | desc = &sds_ring->desc_head[consumer]; | ||
1844 | sts_data0 = le64_to_cpu(desc->status_desc_data[0]); | ||
1845 | |||
1846 | if (!(sts_data0 & STATUS_OWNER_HOST)) | ||
1847 | return; | ||
1848 | |||
1849 | desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); | ||
1850 | opcode = qlcnic_get_sts_opcode(sts_data0); | ||
1851 | switch (opcode) { | ||
1852 | case QLCNIC_RESPONSE_DESC: | ||
1853 | qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); | ||
1854 | break; | ||
1855 | default: | ||
1856 | ring = qlcnic_get_sts_type(sts_data0); | ||
1857 | qlcnic_process_rcv_diag(adapter, sds_ring, ring, sts_data0); | ||
1858 | break; | ||
1859 | } | ||
1860 | |||
1861 | for (; desc_cnt > 0; desc_cnt--) { | ||
1862 | desc = &sds_ring->desc_head[consumer]; | ||
1863 | desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM); | ||
1864 | consumer = get_next_index(consumer, sds_ring->num_desc); | ||
1865 | } | ||
1866 | |||
1867 | sds_ring->consumer = consumer; | ||
1868 | writel(consumer, sds_ring->crb_sts_consumer); | ||
1869 | } | ||
1870 | |||
1749 | void | 1871 | void |
1750 | qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2, | 1872 | qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2, |
1751 | u8 alt_mac, u8 *mac) | 1873 | u8 alt_mac, u8 *mac) |
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index ac7f8a2f7d9..357436be801 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c | |||
@@ -1391,6 +1391,12 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) | |||
1391 | qlcnic_enable_int(sds_ring); | 1391 | qlcnic_enable_int(sds_ring); |
1392 | } | 1392 | } |
1393 | } | 1393 | } |
1394 | |||
1395 | if (adapter->diag_test == QLCNIC_LOOPBACK_TEST) { | ||
1396 | adapter->ahw->loopback_state = 0; | ||
1397 | qlcnic_linkevent_request(adapter, 1); | ||
1398 | } | ||
1399 | |||
1394 | set_bit(__QLCNIC_DEV_UP, &adapter->state); | 1400 | set_bit(__QLCNIC_DEV_UP, &adapter->state); |
1395 | 1401 | ||
1396 | return 0; | 1402 | return 0; |