diff options
| author | Taku Izumi <izumi.taku@jp.fujitsu.com> | 2015-08-21 04:29:24 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2015-08-24 17:06:34 -0400 |
| commit | 9acf51cbf73de8952e3df75200d7a7755c16d0f6 (patch) | |
| tree | 198757a9c339d305878b578a326bed43b25533d0 /drivers/net/fjes | |
| parent | e5d486dcaa45dd365970c09395ae24df5a0e3f77 (diff) | |
fjes: net_device_ops.ndo_start_xmit
This patch adds net_device_ops.ndo_start_xmit callback,
which is called when sending packets.
Signed-off-by: Taku Izumi <izumi.taku@jp.fujitsu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/fjes')
| -rw-r--r-- | drivers/net/fjes/fjes.h | 1 | ||||
| -rw-r--r-- | drivers/net/fjes/fjes_hw.c | 55 | ||||
| -rw-r--r-- | drivers/net/fjes/fjes_hw.h | 12 | ||||
| -rw-r--r-- | drivers/net/fjes/fjes_main.c | 177 |
4 files changed, 245 insertions, 0 deletions
diff --git a/drivers/net/fjes/fjes.h b/drivers/net/fjes/fjes.h index f182ed3a4cd1..7af4304f5425 100644 --- a/drivers/net/fjes/fjes.h +++ b/drivers/net/fjes/fjes.h | |||
| @@ -29,6 +29,7 @@ | |||
| 29 | #define FJES_ACPI_SYMBOL "Extended Socket" | 29 | #define FJES_ACPI_SYMBOL "Extended Socket" |
| 30 | #define FJES_MAX_QUEUES 1 | 30 | #define FJES_MAX_QUEUES 1 |
| 31 | #define FJES_TX_RETRY_INTERVAL (20 * HZ) | 31 | #define FJES_TX_RETRY_INTERVAL (20 * HZ) |
| 32 | #define FJES_TX_RETRY_TIMEOUT (100) | ||
| 32 | #define FJES_OPEN_ZONE_UPDATE_WAIT (300) /* msec */ | 33 | #define FJES_OPEN_ZONE_UPDATE_WAIT (300) /* msec */ |
| 33 | 34 | ||
| 34 | /* board specific private data structure */ | 35 | /* board specific private data structure */ |
diff --git a/drivers/net/fjes/fjes_hw.c b/drivers/net/fjes/fjes_hw.c index 1935f48e9e43..487dbc6b7c43 100644 --- a/drivers/net/fjes/fjes_hw.c +++ b/drivers/net/fjes/fjes_hw.c | |||
| @@ -791,3 +791,58 @@ int fjes_hw_wait_epstop(struct fjes_hw *hw) | |||
| 791 | return (wait_time < FJES_COMMAND_EPSTOP_WAIT_TIMEOUT * 1000) | 791 | return (wait_time < FJES_COMMAND_EPSTOP_WAIT_TIMEOUT * 1000) |
| 792 | ? 0 : -EBUSY; | 792 | ? 0 : -EBUSY; |
| 793 | } | 793 | } |
| 794 | |||
| 795 | bool fjes_hw_check_epbuf_version(struct epbuf_handler *epbh, u32 version) | ||
| 796 | { | ||
| 797 | union ep_buffer_info *info = epbh->info; | ||
| 798 | |||
| 799 | return (info->common.version == version); | ||
| 800 | } | ||
| 801 | |||
| 802 | bool fjes_hw_check_mtu(struct epbuf_handler *epbh, u32 mtu) | ||
| 803 | { | ||
| 804 | union ep_buffer_info *info = epbh->info; | ||
| 805 | |||
| 806 | return (info->v1i.frame_max == FJES_MTU_TO_FRAME_SIZE(mtu)); | ||
| 807 | } | ||
| 808 | |||
| 809 | bool fjes_hw_check_vlan_id(struct epbuf_handler *epbh, u16 vlan_id) | ||
| 810 | { | ||
| 811 | union ep_buffer_info *info = epbh->info; | ||
| 812 | bool ret = false; | ||
| 813 | int i; | ||
| 814 | |||
| 815 | if (vlan_id == 0) { | ||
| 816 | ret = true; | ||
| 817 | } else { | ||
| 818 | for (i = 0; i < EP_BUFFER_SUPPORT_VLAN_MAX; i++) { | ||
| 819 | if (vlan_id == info->v1i.vlan_id[i]) { | ||
| 820 | ret = true; | ||
| 821 | break; | ||
| 822 | } | ||
| 823 | } | ||
| 824 | } | ||
| 825 | return ret; | ||
| 826 | } | ||
| 827 | |||
| 828 | int fjes_hw_epbuf_tx_pkt_send(struct epbuf_handler *epbh, | ||
| 829 | void *frame, size_t size) | ||
| 830 | { | ||
| 831 | union ep_buffer_info *info = epbh->info; | ||
| 832 | struct esmem_frame *ring_frame; | ||
| 833 | |||
| 834 | if (EP_RING_FULL(info->v1i.head, info->v1i.tail, info->v1i.count_max)) | ||
| 835 | return -ENOBUFS; | ||
| 836 | |||
| 837 | ring_frame = (struct esmem_frame *)&(epbh->ring[EP_RING_INDEX | ||
| 838 | (info->v1i.tail - 1, | ||
| 839 | info->v1i.count_max) * | ||
| 840 | info->v1i.frame_max]); | ||
| 841 | |||
| 842 | ring_frame->frame_size = size; | ||
| 843 | memcpy((void *)(ring_frame->frame_data), (void *)frame, size); | ||
| 844 | |||
| 845 | EP_RING_INDEX_INC(epbh->info->v1i.tail, info->v1i.count_max); | ||
| 846 | |||
| 847 | return 0; | ||
| 848 | } | ||
diff --git a/drivers/net/fjes/fjes_hw.h b/drivers/net/fjes/fjes_hw.h index 9b8df553f730..07e122614a76 100644 --- a/drivers/net/fjes/fjes_hw.h +++ b/drivers/net/fjes/fjes_hw.h | |||
| @@ -50,6 +50,9 @@ struct fjes_hw; | |||
| 50 | 50 | ||
| 51 | #define FJES_ZONING_ZONE_TYPE_NONE (0xFF) | 51 | #define FJES_ZONING_ZONE_TYPE_NONE (0xFF) |
| 52 | 52 | ||
| 53 | #define FJES_TX_DELAY_SEND_NONE (0) | ||
| 54 | #define FJES_TX_DELAY_SEND_PENDING (1) | ||
| 55 | |||
| 53 | #define FJES_RX_STOP_REQ_NONE (0x0) | 56 | #define FJES_RX_STOP_REQ_NONE (0x0) |
| 54 | #define FJES_RX_STOP_REQ_DONE (0x1) | 57 | #define FJES_RX_STOP_REQ_DONE (0x1) |
| 55 | #define FJES_RX_STOP_REQ_REQUEST (0x2) | 58 | #define FJES_RX_STOP_REQ_REQUEST (0x2) |
| @@ -61,6 +64,11 @@ struct fjes_hw; | |||
| 61 | 64 | ||
| 62 | #define EP_RING_NUM(buffer_size, frame_size) \ | 65 | #define EP_RING_NUM(buffer_size, frame_size) \ |
| 63 | (u32)((buffer_size) / (frame_size)) | 66 | (u32)((buffer_size) / (frame_size)) |
| 67 | #define EP_RING_INDEX(_num, _max) (((_num) + (_max)) % (_max)) | ||
| 68 | #define EP_RING_INDEX_INC(_num, _max) \ | ||
| 69 | ((_num) = EP_RING_INDEX((_num) + 1, (_max))) | ||
| 70 | #define EP_RING_FULL(_head, _tail, _max) \ | ||
| 71 | (0 == EP_RING_INDEX(((_tail) - (_head)), (_max))) | ||
| 64 | 72 | ||
| 65 | #define FJES_MTU_TO_BUFFER_SIZE(mtu) \ | 73 | #define FJES_MTU_TO_BUFFER_SIZE(mtu) \ |
| 66 | (ETH_HLEN + VLAN_HLEN + (mtu) + ETH_FCS_LEN) | 74 | (ETH_HLEN + VLAN_HLEN + (mtu) + ETH_FCS_LEN) |
| @@ -309,5 +317,9 @@ enum ep_partner_status | |||
| 309 | 317 | ||
| 310 | bool fjes_hw_epid_is_same_zone(struct fjes_hw *, int); | 318 | bool fjes_hw_epid_is_same_zone(struct fjes_hw *, int); |
| 311 | int fjes_hw_epid_is_shared(struct fjes_device_shared_info *, int); | 319 | int fjes_hw_epid_is_shared(struct fjes_device_shared_info *, int); |
| 320 | bool fjes_hw_check_epbuf_version(struct epbuf_handler *, u32); | ||
| 321 | bool fjes_hw_check_mtu(struct epbuf_handler *, u32); | ||
| 322 | bool fjes_hw_check_vlan_id(struct epbuf_handler *, u16); | ||
| 323 | int fjes_hw_epbuf_tx_pkt_send(struct epbuf_handler *, void *, size_t); | ||
| 312 | 324 | ||
| 313 | #endif /* FJES_HW_H_ */ | 325 | #endif /* FJES_HW_H_ */ |
diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index bd50cbd6915f..220ff3da4521 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c | |||
| @@ -51,6 +51,7 @@ static int fjes_open(struct net_device *); | |||
| 51 | static int fjes_close(struct net_device *); | 51 | static int fjes_close(struct net_device *); |
| 52 | static int fjes_setup_resources(struct fjes_adapter *); | 52 | static int fjes_setup_resources(struct fjes_adapter *); |
| 53 | static void fjes_free_resources(struct fjes_adapter *); | 53 | static void fjes_free_resources(struct fjes_adapter *); |
| 54 | static netdev_tx_t fjes_xmit_frame(struct sk_buff *, struct net_device *); | ||
| 54 | static irqreturn_t fjes_intr(int, void*); | 55 | static irqreturn_t fjes_intr(int, void*); |
| 55 | 56 | ||
| 56 | static int fjes_acpi_add(struct acpi_device *); | 57 | static int fjes_acpi_add(struct acpi_device *); |
| @@ -212,6 +213,7 @@ static void fjes_free_irq(struct fjes_adapter *adapter) | |||
| 212 | static const struct net_device_ops fjes_netdev_ops = { | 213 | static const struct net_device_ops fjes_netdev_ops = { |
| 213 | .ndo_open = fjes_open, | 214 | .ndo_open = fjes_open, |
| 214 | .ndo_stop = fjes_close, | 215 | .ndo_stop = fjes_close, |
| 216 | .ndo_start_xmit = fjes_xmit_frame, | ||
| 215 | }; | 217 | }; |
| 216 | 218 | ||
| 217 | /* fjes_open - Called when a network interface is made active */ | 219 | /* fjes_open - Called when a network interface is made active */ |
| @@ -402,6 +404,181 @@ static void fjes_free_resources(struct fjes_adapter *adapter) | |||
| 402 | } | 404 | } |
| 403 | } | 405 | } |
| 404 | 406 | ||
| 407 | static int fjes_tx_send(struct fjes_adapter *adapter, int dest, | ||
| 408 | void *data, size_t len) | ||
| 409 | { | ||
| 410 | int retval; | ||
| 411 | |||
| 412 | retval = fjes_hw_epbuf_tx_pkt_send(&adapter->hw.ep_shm_info[dest].tx, | ||
| 413 | data, len); | ||
| 414 | if (retval) | ||
| 415 | return retval; | ||
| 416 | |||
| 417 | adapter->hw.ep_shm_info[dest].tx.info->v1i.tx_status = | ||
| 418 | FJES_TX_DELAY_SEND_PENDING; | ||
| 419 | |||
| 420 | retval = 0; | ||
| 421 | return retval; | ||
| 422 | } | ||
| 423 | |||
| 424 | static netdev_tx_t | ||
| 425 | fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | ||
| 426 | { | ||
| 427 | struct fjes_adapter *adapter = netdev_priv(netdev); | ||
| 428 | struct fjes_hw *hw = &adapter->hw; | ||
| 429 | |||
| 430 | int max_epid, my_epid, dest_epid; | ||
| 431 | enum ep_partner_status pstatus; | ||
| 432 | struct netdev_queue *cur_queue; | ||
| 433 | char shortpkt[VLAN_ETH_HLEN]; | ||
| 434 | bool is_multi, vlan; | ||
| 435 | struct ethhdr *eth; | ||
| 436 | u16 queue_no = 0; | ||
| 437 | u16 vlan_id = 0; | ||
| 438 | netdev_tx_t ret; | ||
| 439 | char *data; | ||
| 440 | int len; | ||
| 441 | |||
| 442 | ret = NETDEV_TX_OK; | ||
| 443 | is_multi = false; | ||
| 444 | cur_queue = netdev_get_tx_queue(netdev, queue_no); | ||
| 445 | |||
| 446 | eth = (struct ethhdr *)skb->data; | ||
| 447 | my_epid = hw->my_epid; | ||
| 448 | |||
| 449 | vlan = (vlan_get_tag(skb, &vlan_id) == 0) ? true : false; | ||
| 450 | |||
| 451 | data = skb->data; | ||
| 452 | len = skb->len; | ||
| 453 | |||
| 454 | if (is_multicast_ether_addr(eth->h_dest)) { | ||
| 455 | dest_epid = 0; | ||
| 456 | max_epid = hw->max_epid; | ||
| 457 | is_multi = true; | ||
| 458 | } else if (is_local_ether_addr(eth->h_dest)) { | ||
| 459 | dest_epid = eth->h_dest[ETH_ALEN - 1]; | ||
| 460 | max_epid = dest_epid + 1; | ||
| 461 | |||
| 462 | if ((eth->h_dest[0] == 0x02) && | ||
| 463 | (0x00 == (eth->h_dest[1] | eth->h_dest[2] | | ||
| 464 | eth->h_dest[3] | eth->h_dest[4])) && | ||
| 465 | (dest_epid < hw->max_epid)) { | ||
| 466 | ; | ||
| 467 | } else { | ||
| 468 | dest_epid = 0; | ||
| 469 | max_epid = 0; | ||
| 470 | ret = NETDEV_TX_OK; | ||
| 471 | |||
| 472 | adapter->stats64.tx_packets += 1; | ||
| 473 | hw->ep_shm_info[my_epid].net_stats.tx_packets += 1; | ||
| 474 | adapter->stats64.tx_bytes += len; | ||
| 475 | hw->ep_shm_info[my_epid].net_stats.tx_bytes += len; | ||
| 476 | } | ||
| 477 | } else { | ||
| 478 | dest_epid = 0; | ||
| 479 | max_epid = 0; | ||
| 480 | ret = NETDEV_TX_OK; | ||
| 481 | |||
| 482 | adapter->stats64.tx_packets += 1; | ||
| 483 | hw->ep_shm_info[my_epid].net_stats.tx_packets += 1; | ||
| 484 | adapter->stats64.tx_bytes += len; | ||
| 485 | hw->ep_shm_info[my_epid].net_stats.tx_bytes += len; | ||
| 486 | } | ||
| 487 | |||
| 488 | for (; dest_epid < max_epid; dest_epid++) { | ||
| 489 | if (my_epid == dest_epid) | ||
| 490 | continue; | ||
| 491 | |||
| 492 | pstatus = fjes_hw_get_partner_ep_status(hw, dest_epid); | ||
| 493 | if (pstatus != EP_PARTNER_SHARED) { | ||
| 494 | ret = NETDEV_TX_OK; | ||
| 495 | } else if (!fjes_hw_check_epbuf_version( | ||
| 496 | &adapter->hw.ep_shm_info[dest_epid].rx, 0)) { | ||
| 497 | /* version is NOT 0 */ | ||
| 498 | adapter->stats64.tx_carrier_errors += 1; | ||
| 499 | hw->ep_shm_info[my_epid].net_stats | ||
| 500 | .tx_carrier_errors += 1; | ||
| 501 | |||
| 502 | ret = NETDEV_TX_OK; | ||
| 503 | } else if (!fjes_hw_check_mtu( | ||
| 504 | &adapter->hw.ep_shm_info[dest_epid].rx, | ||
| 505 | netdev->mtu)) { | ||
| 506 | adapter->stats64.tx_dropped += 1; | ||
| 507 | hw->ep_shm_info[my_epid].net_stats.tx_dropped += 1; | ||
| 508 | adapter->stats64.tx_errors += 1; | ||
| 509 | hw->ep_shm_info[my_epid].net_stats.tx_errors += 1; | ||
| 510 | |||
| 511 | ret = NETDEV_TX_OK; | ||
| 512 | } else if (vlan && | ||
| 513 | !fjes_hw_check_vlan_id( | ||
| 514 | &adapter->hw.ep_shm_info[dest_epid].rx, | ||
| 515 | vlan_id)) { | ||
| 516 | ret = NETDEV_TX_OK; | ||
| 517 | } else { | ||
| 518 | if (len < VLAN_ETH_HLEN) { | ||
| 519 | memset(shortpkt, 0, VLAN_ETH_HLEN); | ||
| 520 | memcpy(shortpkt, skb->data, skb->len); | ||
| 521 | len = VLAN_ETH_HLEN; | ||
| 522 | data = shortpkt; | ||
| 523 | } | ||
| 524 | |||
| 525 | if (adapter->tx_retry_count == 0) { | ||
| 526 | adapter->tx_start_jiffies = jiffies; | ||
| 527 | adapter->tx_retry_count = 1; | ||
| 528 | } else { | ||
| 529 | adapter->tx_retry_count++; | ||
| 530 | } | ||
| 531 | |||
| 532 | if (fjes_tx_send(adapter, dest_epid, data, len)) { | ||
| 533 | if (is_multi) { | ||
| 534 | ret = NETDEV_TX_OK; | ||
| 535 | } else if ( | ||
| 536 | ((long)jiffies - | ||
| 537 | (long)adapter->tx_start_jiffies) >= | ||
| 538 | FJES_TX_RETRY_TIMEOUT) { | ||
| 539 | adapter->stats64.tx_fifo_errors += 1; | ||
| 540 | hw->ep_shm_info[my_epid].net_stats | ||
| 541 | .tx_fifo_errors += 1; | ||
| 542 | adapter->stats64.tx_errors += 1; | ||
| 543 | hw->ep_shm_info[my_epid].net_stats | ||
| 544 | .tx_errors += 1; | ||
| 545 | |||
| 546 | ret = NETDEV_TX_OK; | ||
| 547 | } else { | ||
| 548 | netdev->trans_start = jiffies; | ||
| 549 | netif_tx_stop_queue(cur_queue); | ||
| 550 | |||
| 551 | ret = NETDEV_TX_BUSY; | ||
| 552 | } | ||
| 553 | } else { | ||
| 554 | if (!is_multi) { | ||
| 555 | adapter->stats64.tx_packets += 1; | ||
| 556 | hw->ep_shm_info[my_epid].net_stats | ||
| 557 | .tx_packets += 1; | ||
| 558 | adapter->stats64.tx_bytes += len; | ||
| 559 | hw->ep_shm_info[my_epid].net_stats | ||
| 560 | .tx_bytes += len; | ||
| 561 | } | ||
| 562 | |||
| 563 | adapter->tx_retry_count = 0; | ||
| 564 | ret = NETDEV_TX_OK; | ||
| 565 | } | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | if (ret == NETDEV_TX_OK) { | ||
| 570 | dev_kfree_skb(skb); | ||
| 571 | if (is_multi) { | ||
| 572 | adapter->stats64.tx_packets += 1; | ||
| 573 | hw->ep_shm_info[my_epid].net_stats.tx_packets += 1; | ||
| 574 | adapter->stats64.tx_bytes += 1; | ||
| 575 | hw->ep_shm_info[my_epid].net_stats.tx_bytes += len; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | return ret; | ||
| 580 | } | ||
| 581 | |||
| 405 | static irqreturn_t fjes_intr(int irq, void *data) | 582 | static irqreturn_t fjes_intr(int irq, void *data) |
| 406 | { | 583 | { |
| 407 | struct fjes_adapter *adapter = data; | 584 | struct fjes_adapter *adapter = data; |
