+/**
+ * spider_net_release_tx_descr - processes a used tx descriptor
+ * @card: card structure
+ * @descr: descriptor to release
+ *
+ * releases a used tx descriptor (unmapping, freeing of skb)
+ */
+static void
+spider_net_release_tx_descr(struct spider_net_card *card,
+ struct spider_net_descr *descr)
+{
+ struct sk_buff *skb;
+
+ /* unmap the skb */
+ skb = descr->skb;
+ pci_unmap_single(card->pdev, descr->buf_addr, skb->len,
+ PCI_DMA_BIDIRECTIONAL);
+
+ dev_kfree_skb_any(skb);
+
+ /* set status to not used */
+ spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
+}
+
+/**
+ * spider_net_release_tx_chain - processes sent tx descriptors
+ * @card: adapter structure
+ * @brutal: if set, don't care about whether descriptor seems to be in use
+ *
+ * returns 0 if the tx ring is empty, otherwise 1.
+ *
+ * spider_net_release_tx_chain releases the tx descriptors that spider has
+ * finished with (if non-brutal) or simply release tx descriptors (if brutal).
+ * If some other context is calling this function, we return 1 so that we're
+ * scheduled again (if we were scheduled) and will not loose initiative.
+ */
+static int
+spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
+{
+ struct spider_net_descr_chain *tx_chain = &card->tx_chain;
+ enum spider_net_descr_status status;
+
+ if (atomic_inc_return(&card->tx_chain_release) != 1) {
+ atomic_dec(&card->tx_chain_release);
+ return 1;
+ }
+
+ for (;;) {
+ status = spider_net_get_descr_status(tx_chain->tail);
+ switch (status) {
+ case SPIDER_NET_DESCR_CARDOWNED:
+ if (!brutal)
+ goto out;
+ /* fallthrough, if we release the descriptors
+ * brutally (then we don't care about
+ * SPIDER_NET_DESCR_CARDOWNED) */
+ case SPIDER_NET_DESCR_RESPONSE_ERROR:
+ case SPIDER_NET_DESCR_PROTECTION_ERROR:
+ case SPIDER_NET_DESCR_FORCE_END:
+ if (netif_msg_tx_err(card))
+ pr_err("%s: forcing end of tx descriptor "
+ "with status x%02x\n",
+ card->netdev->name, status);
+ card->netdev_stats.tx_dropped++;
+ break;
+
+ case SPIDER_NET_DESCR_COMPLETE:
+ card->netdev_stats.tx_packets++;
+ card->netdev_stats.tx_bytes +=
+ tx_chain->tail->skb->len;
+ break;
+
+ default: /* any other value (== SPIDER_NET_DESCR_NOT_IN_USE) */
+ goto out;
+ }
+ spider_net_release_tx_descr(card, tx_chain->tail);
+ tx_chain->tail = tx_chain->tail->next;
+ }
+out:
+ atomic_dec(&card->tx_chain_release);
+
+ netif_wake_queue(card->netdev);
+
+ if (status == SPIDER_NET_DESCR_CARDOWNED)
+ return 1;
+ return 0;
+}
+
+/**
+ * spider_net_cleanup_tx_ring - cleans up the TX ring
+ * @card: card structure
+ *
+ * spider_net_cleanup_tx_ring is called by the tx_timer (as we don't use
+ * interrupts to cleanup our TX ring) and returns sent packets to the stack
+ * by freeing them
+ */
+static void
+spider_net_cleanup_tx_ring(struct spider_net_card *card)
+{
+ if ( (spider_net_release_tx_chain(card, 0)) &&
+ (card->netdev->flags & IFF_UP) ) {
+ mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER);
+ }
+}
+