+ JME_NAPI_DISABLE(jme);
+
+ tasklet_kill(&jme->linkch_task);
+ tasklet_kill(&jme->txclean_task);
+ tasklet_kill(&jme->rxclean_task);
+ tasklet_kill(&jme->rxempty_task);
+
+ jme_reset_ghc_speed(jme);
+ jme_disable_rx_engine(jme);
+ jme_disable_tx_engine(jme);
+ jme_reset_mac_processor(jme);
+ jme_free_rx_resources(jme);
+ jme_free_tx_resources(jme);
+ jme->phylink = 0;
+ jme_phy_off(jme);
+
+ return 0;
+}
+
+static int
+jme_alloc_txdesc(struct jme_adapter *jme,
+ struct sk_buff *skb)
+{
+ struct jme_ring *txring = jme->txring;
+ int idx, nr_alloc, mask = jme->tx_ring_mask;
+
+ idx = txring->next_to_use;
+ nr_alloc = skb_shinfo(skb)->nr_frags + 2;
+
+ if (unlikely(atomic_read(&txring->nr_free) < nr_alloc))
+ return -1;
+
+ atomic_sub(nr_alloc, &txring->nr_free);
+
+ txring->next_to_use = (txring->next_to_use + nr_alloc) & mask;
+
+ return idx;
+}
+
+static void
+jme_fill_tx_map(struct pci_dev *pdev,
+ struct txdesc *txdesc,
+ struct jme_buffer_info *txbi,
+ struct page *page,
+ u32 page_offset,
+ u32 len,
+ u8 hidma)
+{
+ dma_addr_t dmaaddr;
+
+ dmaaddr = pci_map_page(pdev,
+ page,
+ page_offset,
+ len,
+ PCI_DMA_TODEVICE);
+
+ pci_dma_sync_single_for_device(pdev,
+ dmaaddr,
+ len,
+ PCI_DMA_TODEVICE);
+
+ txdesc->dw[0] = 0;
+ txdesc->dw[1] = 0;
+ txdesc->desc2.flags = TXFLAG_OWN;
+ txdesc->desc2.flags |= (hidma) ? TXFLAG_64BIT : 0;
+ txdesc->desc2.datalen = cpu_to_le16(len);
+ txdesc->desc2.bufaddrh = cpu_to_le32((__u64)dmaaddr >> 32);
+ txdesc->desc2.bufaddrl = cpu_to_le32(
+ (__u64)dmaaddr & 0xFFFFFFFFUL);
+
+ txbi->mapping = dmaaddr;
+ txbi->len = len;
+}
+
+static void
+jme_map_tx_skb(struct jme_adapter *jme, struct sk_buff *skb, int idx)
+{
+ struct jme_ring *txring = jme->txring;
+ struct txdesc *txdesc = txring->desc, *ctxdesc;
+ struct jme_buffer_info *txbi = txring->bufinf, *ctxbi;
+ u8 hidma = jme->dev->features & NETIF_F_HIGHDMA;
+ int i, nr_frags = skb_shinfo(skb)->nr_frags;
+ int mask = jme->tx_ring_mask;
+ struct skb_frag_struct *frag;
+ u32 len;
+
+ for (i = 0 ; i < nr_frags ; ++i) {
+ frag = &skb_shinfo(skb)->frags[i];
+ ctxdesc = txdesc + ((idx + i + 2) & (mask));
+ ctxbi = txbi + ((idx + i + 2) & (mask));
+
+ jme_fill_tx_map(jme->pdev, ctxdesc, ctxbi, frag->page,
+ frag->page_offset, frag->size, hidma);
+ }
+
+ len = skb_is_nonlinear(skb) ? skb_headlen(skb) : skb->len;
+ ctxdesc = txdesc + ((idx + 1) & (mask));
+ ctxbi = txbi + ((idx + 1) & (mask));
+ jme_fill_tx_map(jme->pdev, ctxdesc, ctxbi, virt_to_page(skb->data),
+ offset_in_page(skb->data), len, hidma);
+
+}
+
+static int
+jme_expand_header(struct jme_adapter *jme, struct sk_buff *skb)
+{
+ if (unlikely(
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
+ skb_shinfo(skb)->tso_size
+#else
+ skb_shinfo(skb)->gso_size
+#endif
+ && skb_header_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+jme_tx_tso(struct sk_buff *skb, __le16 *mss, u8 *flags)
+{
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
+ *mss = cpu_to_le16(skb_shinfo(skb)->tso_size << TXDESC_MSS_SHIFT);
+#else
+ *mss = cpu_to_le16(skb_shinfo(skb)->gso_size << TXDESC_MSS_SHIFT);
+#endif
+ if (*mss) {
+ *flags |= TXFLAG_LSEN;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *iph = ip_hdr(skb);
+
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+ } else {
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+
+ tcp_hdr(skb)->check = ~csum_ipv6_magic(&ip6h->saddr,
+ &ip6h->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+jme_tx_csum(struct jme_adapter *jme, struct sk_buff *skb, u8 *flags)
+{
+#ifdef CHECKSUM_PARTIAL
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+#else
+ if (skb->ip_summed == CHECKSUM_HW)
+#endif
+ {
+ u8 ip_proto;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21)
+ if (skb->protocol == htons(ETH_P_IP))
+ ip_proto = ip_hdr(skb)->protocol;
+ else if (skb->protocol == htons(ETH_P_IPV6))
+ ip_proto = ipv6_hdr(skb)->nexthdr;
+ else
+ ip_proto = 0;
+#else
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ ip_proto = ip_hdr(skb)->protocol;
+ break;
+ case htons(ETH_P_IPV6):
+ ip_proto = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ ip_proto = 0;
+ break;
+ }
+#endif
+
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ *flags |= TXFLAG_TCPCS;
+ break;
+ case IPPROTO_UDP:
+ *flags |= TXFLAG_UDPCS;
+ break;
+ default:
+ msg_tx_err(jme, "Error upper layer protocol.\n");
+ break;
+ }
+ }
+}
+
+static inline void
+jme_tx_vlan(struct sk_buff *skb, __le16 *vlan, u8 *flags)
+{
+ if (vlan_tx_tag_present(skb)) {
+ *flags |= TXFLAG_TAGON;
+ *vlan = cpu_to_le16(vlan_tx_tag_get(skb));
+ }
+}
+
+static int
+jme_fill_tx_desc(struct jme_adapter *jme, struct sk_buff *skb, int idx)
+{
+ struct jme_ring *txring = jme->txring;
+ struct txdesc *txdesc;
+ struct jme_buffer_info *txbi;
+ u8 flags;
+
+ txdesc = (struct txdesc *)txring->desc + idx;
+ txbi = txring->bufinf + idx;
+
+ txdesc->dw[0] = 0;
+ txdesc->dw[1] = 0;
+ txdesc->dw[2] = 0;
+ txdesc->dw[3] = 0;
+ txdesc->desc1.pktsize = cpu_to_le16(skb->len);
+ /*
+ * Set OWN bit at final.
+ * When kernel transmit faster than NIC.
+ * And NIC trying to send this descriptor before we tell
+ * it to start sending this TX queue.
+ * Other fields are already filled correctly.
+ */
+ wmb();
+ flags = TXFLAG_OWN | TXFLAG_INT;
+ /*
+ * Set checksum flags while not tso
+ */
+ if (jme_tx_tso(skb, &txdesc->desc1.mss, &flags))
+ jme_tx_csum(jme, skb, &flags);
+ jme_tx_vlan(skb, &txdesc->desc1.vlan, &flags);
+ jme_map_tx_skb(jme, skb, idx);
+ txdesc->desc1.flags = flags;
+ /*
+ * Set tx buffer info after telling NIC to send
+ * For better tx_clean timing
+ */
+ wmb();
+ txbi->nr_desc = skb_shinfo(skb)->nr_frags + 2;
+ txbi->skb = skb;
+ txbi->len = skb->len;
+ txbi->start_xmit = jiffies;
+ if (!txbi->start_xmit)
+ txbi->start_xmit = (0UL-1);
+
+ return 0;
+}