diff options
Diffstat (limited to 'drivers/staging/benet/be_netif.c')
-rw-r--r-- | drivers/staging/benet/be_netif.c | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/drivers/staging/benet/be_netif.c b/drivers/staging/benet/be_netif.c new file mode 100644 index 00000000000..2b8daf63dc7 --- /dev/null +++ b/drivers/staging/benet/be_netif.c @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2005 - 2008 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ +/* + * be_netif.c + * + * This file contains various entry points of drivers seen by tcp/ip stack. + */ + +#include <linux/if_vlan.h> +#include <linux/in.h> +#include "benet.h" +#include <linux/ip.h> +#include <linux/inet_lro.h> + +/* Strings to print Link properties */ +static const char *link_speed[] = { + "Invalid link Speed Value", + "10 Mbps", + "100 Mbps", + "1 Gbps", + "10 Gbps" +}; + +static const char *link_duplex[] = { + "Invalid Duplex Value", + "Half Duplex", + "Full Duplex" +}; + +static const char *link_state[] = { + "", + "(active)" +}; + +void be_print_link_info(struct BE_LINK_STATUS *lnk_status) +{ + u16 si, di, ai; + + /* Port 0 */ + if (lnk_status->mac0_speed && lnk_status->mac0_duplex) { + /* Port is up and running */ + si = (lnk_status->mac0_speed < 5) ? lnk_status->mac0_speed : 0; + di = (lnk_status->mac0_duplex < 3) ? + lnk_status->mac0_duplex : 0; + ai = (lnk_status->active_port == 0) ? 1 : 0; + printk(KERN_INFO "PortNo. 0: Speed - %s %s %s\n", + link_speed[si], link_duplex[di], link_state[ai]); + } else + printk(KERN_INFO "PortNo. 0: Down\n"); + + /* Port 1 */ + if (lnk_status->mac1_speed && lnk_status->mac1_duplex) { + /* Port is up and running */ + si = (lnk_status->mac1_speed < 5) ? lnk_status->mac1_speed : 0; + di = (lnk_status->mac1_duplex < 3) ? + lnk_status->mac1_duplex : 0; + ai = (lnk_status->active_port == 0) ? 1 : 0; + printk(KERN_INFO "PortNo. 1: Speed - %s %s %s\n", + link_speed[si], link_duplex[di], link_state[ai]); + } else + printk(KERN_INFO "PortNo. 1: Down\n"); + + return; +} + +static int +be_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr, + void **ip_hdr, void **tcpudp_hdr, + u64 *hdr_flags, void *priv) +{ + struct ethhdr *eh; + struct vlan_ethhdr *veh; + struct iphdr *iph; + u8 *va = page_address(frag->page) + frag->page_offset; + unsigned long ll_hlen; + + /* find the mac header, abort if not IPv4 */ + + prefetch(va); + eh = (struct ethhdr *)va; + *mac_hdr = eh; + ll_hlen = ETH_HLEN; + if (eh->h_proto != htons(ETH_P_IP)) { + if (eh->h_proto == htons(ETH_P_8021Q)) { + veh = (struct vlan_ethhdr *)va; + if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP)) + return -1; + + ll_hlen += VLAN_HLEN; + + } else { + return -1; + } + } + *hdr_flags = LRO_IPV4; + + iph = (struct iphdr *)(va + ll_hlen); + *ip_hdr = iph; + if (iph->protocol != IPPROTO_TCP) + return -1; + *hdr_flags |= LRO_TCP; + *tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2); + + return 0; +} + +static int benet_open(struct net_device *netdev) +{ + struct be_net_object *pnob = netdev_priv(netdev); + struct be_adapter *adapter = pnob->adapter; + struct net_lro_mgr *lro_mgr; + + if (adapter->dev_state < BE_DEV_STATE_INIT) + return -EAGAIN; + + lro_mgr = &pnob->lro_mgr; + lro_mgr->dev = netdev; + + lro_mgr->features = LRO_F_NAPI; + lro_mgr->ip_summed = CHECKSUM_UNNECESSARY; + lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; + lro_mgr->max_desc = BE_MAX_LRO_DESCRIPTORS; + lro_mgr->lro_arr = pnob->lro_desc; + lro_mgr->get_frag_header = be_get_frag_header; + lro_mgr->max_aggr = adapter->max_rx_coal; + lro_mgr->frag_align_pad = 2; + if (lro_mgr->max_aggr > MAX_SKB_FRAGS) + lro_mgr->max_aggr = MAX_SKB_FRAGS; + + adapter->max_rx_coal = BE_LRO_MAX_PKTS; + + be_update_link_status(adapter); + + /* + * Set carrier on only if Physical Link up + * Either of the port link status up signifies this + */ + if ((adapter->port0_link_sts == BE_PORT_LINK_UP) || + (adapter->port1_link_sts == BE_PORT_LINK_UP)) { + netif_start_queue(netdev); + netif_carrier_on(netdev); + } + + adapter->dev_state = BE_DEV_STATE_OPEN; + napi_enable(&pnob->napi); + be_enable_intr(pnob); + be_enable_eq_intr(pnob); + /* + * RX completion queue may be in dis-armed state. Arm it. + */ + be_notify_cmpl(pnob, 0, pnob->rx_cq_id, 1); + + return 0; +} + +static int benet_close(struct net_device *netdev) +{ + struct be_net_object *pnob = netdev_priv(netdev); + struct be_adapter *adapter = pnob->adapter; + + netif_stop_queue(netdev); + synchronize_irq(netdev->irq); + + be_wait_nic_tx_cmplx_cmpl(pnob); + adapter->dev_state = BE_DEV_STATE_INIT; + netif_carrier_off(netdev); + + adapter->port0_link_sts = BE_PORT_LINK_DOWN; + adapter->port1_link_sts = BE_PORT_LINK_DOWN; + be_disable_intr(pnob); + be_disable_eq_intr(pnob); + napi_disable(&pnob->napi); + + return 0; +} + +/* + * Setting a Mac Address for BE + * Takes netdev and a void pointer as arguments. + * The pointer holds the new addres to be used. + */ +static int benet_set_mac_addr(struct net_device *netdev, void *p) +{ + struct sockaddr *addr = p; + struct be_net_object *pnob = netdev_priv(netdev); + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + be_rxf_mac_address_read_write(&pnob->fn_obj, 0, 0, false, true, false, + netdev->dev_addr, NULL, NULL); + /* + * Since we are doing Active-Passive failover, both + * ports should have matching MAC addresses everytime. + */ + be_rxf_mac_address_read_write(&pnob->fn_obj, 1, 0, false, true, false, + netdev->dev_addr, NULL, NULL); + + return 0; +} + +void be_get_stats_timer_handler(unsigned long context) +{ + struct be_timer_ctxt *ctxt = (struct be_timer_ctxt *)context; + + if (atomic_read(&ctxt->get_stat_flag)) { + atomic_dec(&ctxt->get_stat_flag); + up((void *)ctxt->get_stat_sem_addr); + } + del_timer(&ctxt->get_stats_timer); + return; +} + +void be_get_stat_cb(void *context, int status, + struct MCC_WRB_AMAP *optional_wrb) +{ + struct be_timer_ctxt *ctxt = (struct be_timer_ctxt *)context; + /* + * just up the semaphore if the get_stat_flag + * reads 1. so that the waiter can continue. + * If it is 0, then it was handled by the timer handler. + */ + del_timer(&ctxt->get_stats_timer); + if (atomic_read(&ctxt->get_stat_flag)) { + atomic_dec(&ctxt->get_stat_flag); + up((void *)ctxt->get_stat_sem_addr); + } +} + +struct net_device_stats *benet_get_stats(struct net_device *dev) +{ + struct be_net_object *pnob = netdev_priv(dev); + struct be_adapter *adapter = pnob->adapter; + u64 pa; + struct be_timer_ctxt *ctxt = &adapter->timer_ctxt; + + if (adapter->dev_state != BE_DEV_STATE_OPEN) { + /* Return previously read stats */ + return &(adapter->benet_stats); + } + /* Get Physical Addr */ + pa = pci_map_single(adapter->pdev, adapter->eth_statsp, + sizeof(struct FWCMD_ETH_GET_STATISTICS), + PCI_DMA_FROMDEVICE); + ctxt->get_stat_sem_addr = (unsigned long)&adapter->get_eth_stat_sem; + atomic_inc(&ctxt->get_stat_flag); + + be_rxf_query_eth_statistics(&pnob->fn_obj, adapter->eth_statsp, + cpu_to_le64(pa), be_get_stat_cb, ctxt, + NULL); + + ctxt->get_stats_timer.data = (unsigned long)ctxt; + mod_timer(&ctxt->get_stats_timer, (jiffies + (HZ * 2))); + down((void *)ctxt->get_stat_sem_addr); /* callback will unblock us */ + + /* Adding port0 and port1 stats. */ + adapter->benet_stats.rx_packets = + adapter->eth_statsp->params.response.p0recvdtotalframes + + adapter->eth_statsp->params.response.p1recvdtotalframes; + adapter->benet_stats.tx_packets = + adapter->eth_statsp->params.response.p0xmitunicastframes + + adapter->eth_statsp->params.response.p1xmitunicastframes; + adapter->benet_stats.tx_bytes = + adapter->eth_statsp->params.response.p0xmitbyteslsd + + adapter->eth_statsp->params.response.p1xmitbyteslsd; + adapter->benet_stats.rx_errors = + adapter->eth_statsp->params.response.p0crcerrors + + adapter->eth_statsp->params.response.p1crcerrors; + adapter->benet_stats.rx_errors += + adapter->eth_statsp->params.response.p0alignmentsymerrs + + adapter->eth_statsp->params.response.p1alignmentsymerrs; + adapter->benet_stats.rx_errors += + adapter->eth_statsp->params.response.p0inrangelenerrors + + adapter->eth_statsp->params.response.p1inrangelenerrors; + adapter->benet_stats.rx_bytes = + adapter->eth_statsp->params.response.p0recvdtotalbytesLSD + + adapter->eth_statsp->params.response.p1recvdtotalbytesLSD; + adapter->benet_stats.rx_crc_errors = + adapter->eth_statsp->params.response.p0crcerrors + + adapter->eth_statsp->params.response.p1crcerrors; + + adapter->benet_stats.tx_packets += + adapter->eth_statsp->params.response.p0xmitmulticastframes + + adapter->eth_statsp->params.response.p1xmitmulticastframes; + adapter->benet_stats.tx_packets += + adapter->eth_statsp->params.response.p0xmitbroadcastframes + + adapter->eth_statsp->params.response.p1xmitbroadcastframes; + adapter->benet_stats.tx_errors = 0; + + adapter->benet_stats.multicast = + adapter->eth_statsp->params.response.p0xmitmulticastframes + + adapter->eth_statsp->params.response.p1xmitmulticastframes; + + adapter->benet_stats.rx_fifo_errors = + adapter->eth_statsp->params.response.p0rxfifooverflowdropped + + adapter->eth_statsp->params.response.p1rxfifooverflowdropped; + adapter->benet_stats.rx_frame_errors = + adapter->eth_statsp->params.response.p0alignmentsymerrs + + adapter->eth_statsp->params.response.p1alignmentsymerrs; + adapter->benet_stats.rx_length_errors = + adapter->eth_statsp->params.response.p0inrangelenerrors + + adapter->eth_statsp->params.response.p1inrangelenerrors; + adapter->benet_stats.rx_length_errors += + adapter->eth_statsp->params.response.p0outrangeerrors + + adapter->eth_statsp->params.response.p1outrangeerrors; + adapter->benet_stats.rx_length_errors += + adapter->eth_statsp->params.response.p0frametoolongerrors + + adapter->eth_statsp->params.response.p1frametoolongerrors; + + pci_unmap_single(adapter->pdev, (ulong) adapter->eth_statsp, + sizeof(struct FWCMD_ETH_GET_STATISTICS), + PCI_DMA_FROMDEVICE); + return &(adapter->benet_stats); + +} + +static void be_start_tx(struct be_net_object *pnob, u32 nposted) +{ +#define CSR_ETH_MAX_SQPOSTS 255 + struct SQ_DB_AMAP sqdb; + + sqdb.dw[0] = 0; + + AMAP_SET_BITS_PTR(SQ_DB, cid, &sqdb, pnob->tx_q_id); + while (nposted) { + if (nposted > CSR_ETH_MAX_SQPOSTS) { + AMAP_SET_BITS_PTR(SQ_DB, numPosted, &sqdb, + CSR_ETH_MAX_SQPOSTS); + nposted -= CSR_ETH_MAX_SQPOSTS; + } else { + AMAP_SET_BITS_PTR(SQ_DB, numPosted, &sqdb, nposted); + nposted = 0; + } + PD_WRITE(&pnob->fn_obj, etx_sq_db, sqdb.dw[0]); + } + + return; +} + +static void update_tx_rate(struct be_adapter *adapter) +{ + /* update the rate once in two seconds */ + if ((jiffies - adapter->eth_tx_jiffies) > 2 * (HZ)) { + u32 r; + r = adapter->eth_tx_bytes / + ((jiffies - adapter->eth_tx_jiffies) / (HZ)); + r = (r / 1000000); /* M bytes/s */ + adapter->be_stat.bes_eth_tx_rate = (r * 8); /* M bits/s */ + adapter->eth_tx_jiffies = jiffies; + adapter->eth_tx_bytes = 0; + } +} + +static int wrb_cnt_in_skb(struct sk_buff *skb) +{ + int cnt = 0; + while (skb) { + if (skb->len > skb->data_len) + cnt++; + cnt += skb_shinfo(skb)->nr_frags; + skb = skb_shinfo(skb)->frag_list; + } + BUG_ON(cnt > BE_MAX_TX_FRAG_COUNT); + return cnt; +} + +static void wrb_fill(struct ETH_WRB_AMAP *wrb, u64 addr, int len) +{ + AMAP_SET_BITS_PTR(ETH_WRB, frag_pa_hi, wrb, addr >> 32); + AMAP_SET_BITS_PTR(ETH_WRB, frag_pa_lo, wrb, addr & 0xFFFFFFFF); + AMAP_SET_BITS_PTR(ETH_WRB, frag_len, wrb, len); +} + +static void wrb_fill_extra(struct ETH_WRB_AMAP *wrb, struct sk_buff *skb, + struct be_net_object *pnob) +{ + wrb->dw[2] = 0; + wrb->dw[3] = 0; + AMAP_SET_BITS_PTR(ETH_WRB, crc, wrb, 1); + if (skb_shinfo(skb)->gso_segs > 1 && skb_shinfo(skb)->gso_size) { + AMAP_SET_BITS_PTR(ETH_WRB, lso, wrb, 1); + AMAP_SET_BITS_PTR(ETH_WRB, lso_mss, wrb, + skb_shinfo(skb)->gso_size); + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + u8 proto = ((struct iphdr *)ip_hdr(skb))->protocol; + if (proto == IPPROTO_TCP) + AMAP_SET_BITS_PTR(ETH_WRB, tcpcs, wrb, 1); + else if (proto == IPPROTO_UDP) + AMAP_SET_BITS_PTR(ETH_WRB, udpcs, wrb, 1); + } + if (pnob->vlan_grp && vlan_tx_tag_present(skb)) { + AMAP_SET_BITS_PTR(ETH_WRB, vlan, wrb, 1); + AMAP_SET_BITS_PTR(ETH_WRB, vlan_tag, wrb, vlan_tx_tag_get(skb)); + } +} + +static inline void wrb_copy_extra(struct ETH_WRB_AMAP *to, + struct ETH_WRB_AMAP *from) +{ + + to->dw[2] = from->dw[2]; + to->dw[3] = from->dw[3]; +} + +/* Returns the actual count of wrbs used including a possible dummy */ +static int copy_skb_to_txq(struct be_net_object *pnob, struct sk_buff *skb, + u32 wrb_cnt, u32 *copied) +{ + u64 busaddr; + struct ETH_WRB_AMAP *wrb = NULL, *first = NULL; + u32 i; + bool dummy = true; + struct pci_dev *pdev = pnob->adapter->pdev; + + if (wrb_cnt & 1) + wrb_cnt++; + else + dummy = false; + + atomic_add(wrb_cnt, &pnob->tx_q_used); + + while (skb) { + if (skb->len > skb->data_len) { + int len = skb->len - skb->data_len; + busaddr = pci_map_single(pdev, skb->data, len, + PCI_DMA_TODEVICE); + busaddr = cpu_to_le64(busaddr); + wrb = &pnob->tx_q[pnob->tx_q_hd]; + if (first == NULL) { + wrb_fill_extra(wrb, skb, pnob); + first = wrb; + } else { + wrb_copy_extra(wrb, first); + } + wrb_fill(wrb, busaddr, len); + be_adv_txq_hd(pnob); + *copied += len; + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[i]; + busaddr = pci_map_page(pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); + busaddr = cpu_to_le64(busaddr); + wrb = &pnob->tx_q[pnob->tx_q_hd]; + if (first == NULL) { + wrb_fill_extra(wrb, skb, pnob); + first = wrb; + } else { + wrb_copy_extra(wrb, first); + } + wrb_fill(wrb, busaddr, frag->size); + be_adv_txq_hd(pnob); + *copied += frag->size; + } + skb = skb_shinfo(skb)->frag_list; + } + + if (dummy) { + wrb = &pnob->tx_q[pnob->tx_q_hd]; + BUG_ON(first == NULL); + wrb_copy_extra(wrb, first); + wrb_fill(wrb, 0, 0); + be_adv_txq_hd(pnob); + } + AMAP_SET_BITS_PTR(ETH_WRB, complete, wrb, 1); + AMAP_SET_BITS_PTR(ETH_WRB, last, wrb, 1); + return wrb_cnt; +} + +/* For each skb transmitted, tx_ctxt stores the num of wrbs in the + * start index and skb pointer in the end index + */ +static inline void be_tx_wrb_info_remember(struct be_net_object *pnob, + struct sk_buff *skb, int wrb_cnt, + u32 start) +{ + *(u32 *) (&pnob->tx_ctxt[start]) = wrb_cnt; + index_adv(&start, wrb_cnt - 1, pnob->tx_q_len); + pnob->tx_ctxt[start] = skb; +} + +static int benet_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct be_net_object *pnob = netdev_priv(netdev); + struct be_adapter *adapter = pnob->adapter; + u32 wrb_cnt, copied = 0; + u32 start = pnob->tx_q_hd; + + adapter->be_stat.bes_tx_reqs++; + + wrb_cnt = wrb_cnt_in_skb(skb); + spin_lock_bh(&adapter->txq_lock); + if ((pnob->tx_q_len - 2 - atomic_read(&pnob->tx_q_used)) <= wrb_cnt) { + netif_stop_queue(pnob->netdev); + spin_unlock_bh(&adapter->txq_lock); + adapter->be_stat.bes_tx_fails++; + return NETDEV_TX_BUSY; + } + spin_unlock_bh(&adapter->txq_lock); + + wrb_cnt = copy_skb_to_txq(pnob, skb, wrb_cnt, &copied); + be_tx_wrb_info_remember(pnob, skb, wrb_cnt, start); + + be_start_tx(pnob, wrb_cnt); + + adapter->eth_tx_bytes += copied; + adapter->be_stat.bes_tx_wrbs += wrb_cnt; + update_tx_rate(adapter); + netdev->trans_start = jiffies; + + return NETDEV_TX_OK; +} + +/* + * This is the driver entry point to change the mtu of the device + * Returns 0 for success and errno for failure. + */ +static int benet_change_mtu(struct net_device *netdev, int new_mtu) +{ + /* + * BE supports jumbo frame size upto 9000 bytes including the link layer + * header. Considering the different variants of frame formats possible + * like VLAN, SNAP/LLC, the maximum possible value for MTU is 8974 bytes + */ + + if (new_mtu < (ETH_ZLEN + ETH_FCS_LEN) || (new_mtu > BE_MAX_MTU)) { + dev_info(&netdev->dev, "Invalid MTU requested. " + "Must be between %d and %d bytes\n", + (ETH_ZLEN + ETH_FCS_LEN), BE_MAX_MTU); + return -EINVAL; + } + dev_info(&netdev->dev, "MTU changed from %d to %d\n", + netdev->mtu, new_mtu); + netdev->mtu = new_mtu; + return 0; +} + +/* + * This is the driver entry point to register a vlan with the device + */ +static void benet_vlan_register(struct net_device *netdev, + struct vlan_group *grp) +{ + struct be_net_object *pnob = netdev_priv(netdev); + + be_disable_eq_intr(pnob); + pnob->vlan_grp = grp; + pnob->num_vlans = 0; + be_enable_eq_intr(pnob); +} + +/* + * This is the driver entry point to add a vlan vlan_id + * with the device netdev + */ +static void benet_vlan_add_vid(struct net_device *netdev, u16 vlan_id) +{ + struct be_net_object *pnob = netdev_priv(netdev); + + if (pnob->num_vlans == (BE_NUM_VLAN_SUPPORTED - 1)) { + /* no way to return an error */ + dev_info(&netdev->dev, + "BladeEngine: Cannot configure more than %d Vlans\n", + BE_NUM_VLAN_SUPPORTED); + return; + } + /* The new vlan tag will be in the slot indicated by num_vlans. */ + pnob->vlan_tag[pnob->num_vlans++] = vlan_id; + be_rxf_vlan_config(&pnob->fn_obj, false, pnob->num_vlans, + pnob->vlan_tag, NULL, NULL, NULL); +} + +/* + * This is the driver entry point to remove a vlan vlan_id + * with the device netdev + */ +static void benet_vlan_rem_vid(struct net_device *netdev, u16 vlan_id) +{ + struct be_net_object *pnob = netdev_priv(netdev); + + u32 i, value; + + /* + * In Blade Engine, we support 32 vlan tag filters across both ports. + * To program a vlan tag, the RXF_RTPR_CSR register is used. + * Each 32-bit value of RXF_RTDR_CSR can address 2 vlan tag entries. + * The Vlan table is of depth 16. thus we support 32 tags. + */ + + value = vlan_id | VLAN_VALID_BIT; + for (i = 0; i < BE_NUM_VLAN_SUPPORTED; i++) { + if (pnob->vlan_tag[i] == vlan_id) + break; + } + + if (i == BE_NUM_VLAN_SUPPORTED) + return; + /* Now compact the vlan tag array by removing hole created. */ + while ((i + 1) < BE_NUM_VLAN_SUPPORTED) { + pnob->vlan_tag[i] = pnob->vlan_tag[i + 1]; + i++; + } + if ((i + 1) == BE_NUM_VLAN_SUPPORTED) + pnob->vlan_tag[i] = (u16) 0x0; + pnob->num_vlans--; + be_rxf_vlan_config(&pnob->fn_obj, false, pnob->num_vlans, + pnob->vlan_tag, NULL, NULL, NULL); +} + +/* + * This function is called to program multicast + * address in the multicast filter of the ASIC. + */ +static void be_set_multicast_filter(struct net_device *netdev) +{ + struct be_net_object *pnob = netdev_priv(netdev); + struct dev_mc_list *mc_ptr; + u8 mac_addr[32][ETH_ALEN]; + int i; + + if (netdev->flags & IFF_ALLMULTI) { + /* set BE in Multicast promiscuous */ + be_rxf_multicast_config(&pnob->fn_obj, true, 0, NULL, NULL, + NULL, NULL); + return; + } + + for (mc_ptr = netdev->mc_list, i = 0; mc_ptr; + mc_ptr = mc_ptr->next, i++) { + memcpy(&mac_addr[i][0], mc_ptr->dmi_addr, ETH_ALEN); + } + + /* reset the promiscuous mode also. */ + be_rxf_multicast_config(&pnob->fn_obj, false, i, + &mac_addr[0][0], NULL, NULL, NULL); +} + +/* + * This is the driver entry point to set multicast list + * with the device netdev. This function will be used to + * set promiscuous mode or multicast promiscuous mode + * or multicast mode.... + */ +static void benet_set_multicast_list(struct net_device *netdev) +{ + struct be_net_object *pnob = netdev_priv(netdev); + + if (netdev->flags & IFF_PROMISC) { + be_rxf_promiscuous(&pnob->fn_obj, 1, 1, NULL, NULL, NULL); + } else { + be_rxf_promiscuous(&pnob->fn_obj, 0, 0, NULL, NULL, NULL); + be_set_multicast_filter(netdev); + } +} + +int benet_init(struct net_device *netdev) +{ + struct be_net_object *pnob = netdev_priv(netdev); + struct be_adapter *adapter = pnob->adapter; + + ether_setup(netdev); + + netdev->open = &benet_open; + netdev->stop = &benet_close; + netdev->hard_start_xmit = &benet_xmit; + + netdev->get_stats = &benet_get_stats; + + netdev->set_multicast_list = &benet_set_multicast_list; + + netdev->change_mtu = &benet_change_mtu; + netdev->set_mac_address = &benet_set_mac_addr; + + netdev->vlan_rx_register = benet_vlan_register; + netdev->vlan_rx_add_vid = benet_vlan_add_vid; + netdev->vlan_rx_kill_vid = benet_vlan_rem_vid; + + netdev->features = + NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_RX | NETIF_F_TSO | + NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_IP_CSUM; + + netdev->flags |= IFF_MULTICAST; + + /* If device is DAC Capable, set the HIGHDMA flag for netdevice. */ + if (adapter->dma_64bit_cap) + netdev->features |= NETIF_F_HIGHDMA; + + SET_ETHTOOL_OPS(netdev, &be_ethtool_ops); + return 0; +} |