patch-2.4.19 linux-2.4.19/drivers/net/acenic.c

Next file: linux-2.4.19/drivers/net/acenic.h
Previous file: linux-2.4.19/drivers/net/ac3200.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/net/acenic.c linux-2.4.19/drivers/net/acenic.c
@@ -2,7 +2,7 @@
  * acenic.c: Linux driver for the Alteon AceNIC Gigabit Ethernet card
  *           and other Tigon based cards.
  *
- * Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>.
+ * Copyright 1998-2002 by Jes Sorensen, <jes@trained-monkey.org>.
  *
  * Thanks to Alteon and 3Com for providing hardware and documentation
  * enabling me to write this driver.
@@ -30,6 +30,7 @@
  *   Pierrick Pinasseau (CERN): For lending me an Ultra 5 to test the
  *                              driver under Linux/Sparc64
  *   Matt Domsch <Matt_Domsch@dell.com>: Detect Alteon 1000baseT cards
+ *                                       ETHTOOL_GDRVINFO support
  *   Chip Salzenberg <chip@valinux.com>: Fix race condition between tx
  *                                       handler and close() cleanup.
  *   Ken Aaker <kdaaker@rchland.vnet.ibm.com>: Correct check for whether
@@ -65,6 +66,10 @@
 #include <linux/highmem.h>
 #include <linux/sockios.h>
 
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#include <linux/if_vlan.h>
+#endif
+
 #ifdef SIOCETHTOOL
 #include <linux/ethtool.h>
 #endif
@@ -83,8 +88,10 @@
 
 #ifdef CONFIG_ACENIC_OMIT_TIGON_I
 #define ACE_IS_TIGON_I(ap)	0
+#define ACE_TX_RING_ENTRIES(ap)	MAX_TX_RING_ENTRIES
 #else
 #define ACE_IS_TIGON_I(ap)	(ap->version == 1)
+#define ACE_TX_RING_ENTRIES(ap)	ap->tx_ring_entries
 #endif
 
 #ifndef PCI_VENDOR_ID_ALTEON
@@ -113,6 +120,10 @@
 #ifndef PCI_DEVICE_ID_FARALLON_PN9000SX
 #define PCI_DEVICE_ID_FARALLON_PN9000SX	0x1a
 #endif
+#ifndef PCI_DEVICE_ID_FARALLON_PN9100T
+#define PCI_DEVICE_ID_FARALLON_PN9100T  0xfa
+#endif
+
 #ifndef PCI_VENDOR_ID_SGI
 #define PCI_VENDOR_ID_SGI		0x10a9
 #endif
@@ -137,6 +148,9 @@
 	 */
 	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX,
 	  PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+	{ PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_FARALLON_PN9100T,
+	  PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+
 	{ PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC,
 	  PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
 	{ }
@@ -314,6 +328,14 @@
 #define BOARD_IDX_STATIC	0
 #define BOARD_IDX_OVERFLOW	-1
 
+#if (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)) && \
+	defined(NETIF_F_HW_VLAN_RX)
+#define ACENIC_DO_VLAN		1
+#define ACE_RCB_VLAN_FLAG	RCB_FLG_VLAN_ASSIST
+#else
+#define ACENIC_DO_VLAN		0
+#define ACE_RCB_VLAN_FLAG	0
+#endif
 
 #include "acenic.h"
 
@@ -553,7 +575,7 @@
 static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
 
 static char version[] __initdata = 
-  "acenic.c: v0.85 11/08/2001  Jes Sorensen, linux-acenic@SunSITE.dk\n"
+  "acenic.c: v0.89 03/15/2002  Jes Sorensen, linux-acenic@SunSITE.dk\n"
   "                            http://home.cern.ch/~jes/gige/acenic.html\n";
 
 static struct net_device *root_dev;
@@ -596,6 +618,8 @@
 		 */
 		    !((pdev->vendor == PCI_VENDOR_ID_DEC) &&
 		      (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) &&
+		    !((pdev->vendor == PCI_VENDOR_ID_ALTEON) &&
+		      (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T)) &&
 		    !((pdev->vendor == PCI_VENDOR_ID_SGI) &&
 		      (pdev->device == PCI_DEVICE_ID_SGI_ACENIC)))
 			continue;
@@ -620,10 +644,14 @@
 		ap = dev->priv;
 		ap->pdev = pdev;
 
-		dev->irq = pdev->irq;
 		dev->open = &ace_open;
 		dev->hard_start_xmit = &ace_start_xmit;
 		dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+#if ACENIC_DO_VLAN
+		dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+		dev->vlan_rx_register = ace_vlan_rx_register;
+		dev->vlan_rx_kill_vid = ace_vlan_rx_kill_vid;
+#endif
 		if (1) {
 			static void ace_watchdog(struct net_device *dev);
 			dev->tx_timeout = &ace_watchdog;
@@ -689,6 +717,13 @@
 
 		switch(pdev->vendor) {
 		case PCI_VENDOR_ID_ALTEON:
+			if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) {
+				strncpy(ap->name, "Farallon PN9100-T "
+					"Gigabit Ethernet", sizeof (ap->name));
+				printk(KERN_INFO "%s: Farallon PN9100-T ",
+				       dev->name);
+				break;
+			}
 			strncpy(ap->name, "AceNIC Gigabit Ethernet",
 				sizeof (ap->name));
 			printk(KERN_INFO "%s: Alteon AceNIC ", dev->name);
@@ -725,9 +760,9 @@
 		ap->name [sizeof (ap->name) - 1] = '\0';
 		printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr);
 #ifdef __sparc__
-		printk("irq %s\n", __irq_itoa(dev->irq));
+		printk("irq %s\n", __irq_itoa(pdev->irq));
 #else
-		printk("irq %i\n", dev->irq);
+		printk("irq %i\n", pdev->irq);
 #endif
 
 #ifdef CONFIG_ACENIC_OMIT_TIGON_I
@@ -960,6 +995,13 @@
 				    ap->evt_ring_dma);
 		ap->evt_ring = NULL;
 	}
+	if (ap->tx_ring != NULL && !ACE_IS_TIGON_I(ap)) {
+		size = (sizeof(struct tx_desc) * MAX_TX_RING_ENTRIES);
+		pci_free_consistent(ap->pdev, size, ap->tx_ring,
+				    ap->tx_ring_dma);
+	}
+	ap->tx_ring = NULL;
+
 	if (ap->evt_prd != NULL) {
 		pci_free_consistent(ap->pdev, sizeof(u32),
 				    (void *)ap->evt_prd, ap->evt_prd_dma);
@@ -1006,12 +1048,19 @@
 	if (ap->evt_ring == NULL)
 		goto fail;
 
-	size = (sizeof(struct tx_desc) * TX_RING_ENTRIES);
+	/*
+	 * Only allocate a host TX ring for the Tigon II, the Tigon I
+	 * has to use PCI registers for this ;-(
+	 */
+	if (!ACE_IS_TIGON_I(ap)) {
+		size = (sizeof(struct tx_desc) * MAX_TX_RING_ENTRIES);
 
-	ap->tx_ring = pci_alloc_consistent(ap->pdev, size, &ap->tx_ring_dma);
+		ap->tx_ring = pci_alloc_consistent(ap->pdev, size,
+						   &ap->tx_ring_dma);
 
-	if (ap->tx_ring == NULL)
-		goto fail;
+		if (ap->tx_ring == NULL)
+			goto fail;
+	}
 
 	ap->evt_prd = pci_alloc_consistent(ap->pdev, sizeof(u32),
 					   &ap->evt_prd_dma);
@@ -1086,6 +1135,7 @@
 	struct ace_private *ap;
 	struct ace_regs *regs;
 	struct ace_info *info = NULL;
+	struct pci_dev *pdev;
 	unsigned long myjif;
 	u64 tmp_ptr;
 	u32 tig_ver, mac1, mac2, tmp, pci_state;
@@ -1144,6 +1194,7 @@
 		       tigonFwReleaseFix);
 		writel(0, &regs->LocalCtrl);
 		ap->version = 1;
+		ap->tx_ring_entries = TIGON_I_TX_RING_ENTRIES;
 		break;
 #endif
 	case 6:
@@ -1159,6 +1210,7 @@
 		writel(SRAM_BANK_512K, &regs->LocalCtrl);
 		writel(SYNC_SRAM_TIMING, &regs->MiscCfg);
 		ap->version = 2;
+		ap->tx_ring_entries = MAX_TX_RING_ENTRIES;
 		break;
 	default:
 		printk(KERN_WARNING "  Unsupported Tigon version detected "
@@ -1224,7 +1276,8 @@
 	 * Ie. having two NICs in the machine, one will have the cache
 	 * line set at boot time, the other will not.
 	 */
-	pci_read_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, &cache_size);
+	pdev = ap->pdev;
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_size);
 	cache_size <<= 2;
 	if (cache_size != SMP_CACHE_BYTES) {
 		printk(KERN_INFO "  PCI cache line size set incorrectly "
@@ -1233,7 +1286,7 @@
 			printk("expecting %i\n", SMP_CACHE_BYTES);
 		else {
 			printk("correcting to %i\n", SMP_CACHE_BYTES);
-			pci_write_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE,
+			pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
 					      SMP_CACHE_BYTES >> 2);
 		}
 	}
@@ -1265,7 +1318,7 @@
 		    dis_pci_mem_inval[board_idx]) {
 			if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
 				ap->pci_command &= ~PCI_COMMAND_INVALIDATE;
-				pci_write_config_word(ap->pdev, PCI_COMMAND,
+				pci_write_config_word(pdev, PCI_COMMAND,
 						      ap->pci_command);
 				printk(KERN_INFO "  Disabling PCI memory "
 				       "write and invalidate\n");
@@ -1292,7 +1345,7 @@
 				       "supported, PCI write and invalidate "
 				       "disabled\n", SMP_CACHE_BYTES);
 				ap->pci_command &= ~PCI_COMMAND_INVALIDATE;
-				pci_write_config_word(ap->pdev, PCI_COMMAND,
+				pci_write_config_word(pdev, PCI_COMMAND,
 						      ap->pci_command);
 			}
 		}
@@ -1334,16 +1387,16 @@
 	if (!(ap->pci_command & PCI_COMMAND_FAST_BACK)) {
 		printk(KERN_INFO "  Enabling PCI Fast Back to Back\n");
 		ap->pci_command |= PCI_COMMAND_FAST_BACK;
-		pci_write_config_word(ap->pdev, PCI_COMMAND, ap->pci_command);
+		pci_write_config_word(pdev, PCI_COMMAND, ap->pci_command);
 	}
 #endif
 		
 	/*
 	 * Configure DMA attributes.
 	 */
-	if (!pci_set_dma_mask(ap->pdev, 0xffffffffffffffffULL)) {
+	if (!pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
 		ap->pci_using_dac = 1;
-	} else if (!pci_set_dma_mask(ap->pdev, 0xffffffffULL)) {
+	} else if (!pci_set_dma_mask(pdev, 0xffffffffULL)) {
 		ap->pci_using_dac = 0;
 	} else {
 		ecode = -ENODEV;
@@ -1370,12 +1423,14 @@
 		goto init_error;
 	}
 
-	ecode = request_irq(dev->irq, ace_interrupt, SA_SHIRQ, dev->name, dev);
+	ecode = request_irq(pdev->irq, ace_interrupt, SA_SHIRQ,
+			    dev->name, dev);
 	if (ecode) {
 		printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
-		       dev->name, dev->irq);
+		       dev->name, pdev->irq);
 		goto init_error;
-	}
+	} else
+		dev->irq = pdev->irq;
 
 	/*
 	 * Register the device here to be able to catch allocated
@@ -1386,7 +1441,7 @@
 
 #ifdef INDEX_DEBUG
 	spin_lock_init(&ap->debug_lock);
-	ap->last_tx = TX_RING_ENTRIES - 1;
+	ap->last_tx = ACE_TX_RING_ENTRIES(ap) - 1;
 	ap->last_std_rx = 0;
 	ap->last_mini_rx = 0;
 #endif
@@ -1427,7 +1482,8 @@
 
 	set_aceaddr(&info->rx_std_ctrl.rngptr, ap->rx_ring_base_dma);
 	info->rx_std_ctrl.max_len = ACE_STD_MTU + ETH_HLEN + 4;
-	info->rx_std_ctrl.flags = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR;
+	info->rx_std_ctrl.flags =
+	  RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | ACE_RCB_VLAN_FLAG;
 
 	memset(ap->rx_std_ring, 0,
 	       RX_STD_RING_ENTRIES * sizeof(struct rx_desc));
@@ -1442,7 +1498,8 @@
 		    (ap->rx_ring_base_dma +
 		     (sizeof(struct rx_desc) * RX_STD_RING_ENTRIES)));
 	info->rx_jumbo_ctrl.max_len = 0;
-	info->rx_jumbo_ctrl.flags = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR;
+	info->rx_jumbo_ctrl.flags =
+	  RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | ACE_RCB_VLAN_FLAG;
 
 	memset(ap->rx_jumbo_ring, 0,
 	       RX_JUMBO_RING_ENTRIES * sizeof(struct rx_desc));
@@ -1464,7 +1521,7 @@
 			       RX_JUMBO_RING_ENTRIES))));
 		info->rx_mini_ctrl.max_len = ACE_MINI_SIZE;
 		info->rx_mini_ctrl.flags = 
-			RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR;
+		  RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR|ACE_RCB_VLAN_FLAG;
 
 		for (i = 0; i < RX_MINI_RING_ENTRIES; i++)
 			ap->rx_mini_ring[i].flags =
@@ -1494,12 +1551,30 @@
 	*(ap->rx_ret_prd) = 0;
 
 	writel(TX_RING_BASE, &regs->WinBase);
-	memset(ap->tx_ring, 0, TX_RING_ENTRIES * sizeof(struct tx_desc));
 
-	set_aceaddr(&info->tx_ctrl.rngptr, ap->tx_ring_dma);
+	if (ACE_IS_TIGON_I(ap)) {
+		ap->tx_ring = (struct tx_desc *)regs->Window;
+		for (i = 0; i < (TIGON_I_TX_RING_ENTRIES * 
+				 sizeof(struct tx_desc) / 4); i++) {
+			writel(0, (unsigned long)ap->tx_ring + i * 4);
+		}
+
+		set_aceaddr(&info->tx_ctrl.rngptr, TX_RING_BASE);
+	} else {
+		memset(ap->tx_ring, 0,
+		       MAX_TX_RING_ENTRIES * sizeof(struct tx_desc));
+
+		set_aceaddr(&info->tx_ctrl.rngptr, ap->tx_ring_dma);
+	}
 
-	info->tx_ctrl.max_len = TX_RING_ENTRIES;
-	tmp = RCB_FLG_TCP_UDP_SUM|RCB_FLG_NO_PSEUDO_HDR|RCB_FLG_TX_HOST_RING;
+	info->tx_ctrl.max_len = ACE_TX_RING_ENTRIES(ap);
+	tmp = RCB_FLG_TCP_UDP_SUM | RCB_FLG_NO_PSEUDO_HDR | ACE_RCB_VLAN_FLAG;
+
+	/*
+	 * The Tigon I does not like having the TX ring in host memory ;-(
+	 */
+	if (!ACE_IS_TIGON_I(ap))
+		tmp |= RCB_FLG_TX_HOST_RING;
 #if TX_COAL_INTS_ONLY
 	tmp |= RCB_FLG_COAL_INT_ONLY;
 #endif
@@ -1528,7 +1603,7 @@
 	ace_set_rxtx_parms(dev, 0);
 
 	if (board_idx == BOARD_IDX_OVERFLOW) {
-		printk(KERN_WARNING "%s: more then %i NICs detected, "
+		printk(KERN_WARNING "%s: more than %i NICs detected, "
 		       "ignoring module parameters!\n",
 		       dev->name, ACE_MAX_MOD_PARMS);
 	} else if (board_idx >= 0) {
@@ -2201,7 +2276,13 @@
 			skb->ip_summed = CHECKSUM_NONE;
 		}
 
-		netif_rx(skb);		/* send it up */
+		/* send it up */
+#if ACENIC_DO_VLAN
+		if (ap->vlgrp && (bd_flags & BD_FLG_VLAN_TAG)) {
+			vlan_hwaccel_rx(skb, ap->vlgrp, retdesc->vlan);
+		} else
+#endif
+			netif_rx(skb);
 
 		dev->last_rx = jiffies;
 		ap->stats.rx_packets++;
@@ -2260,7 +2341,7 @@
 			info->skb = NULL;
 		}
 
-		idx = (idx + 1) % TX_RING_ENTRIES;
+		idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
 	} while (idx != txcsm);
 
 	if (netif_queue_stopped(dev))
@@ -2353,7 +2434,7 @@
 		 * update releases enough of space, otherwise we just
 		 * wait for device to make more work.
 		 */
-		if (!tx_ring_full(txcsm, ap->tx_prd))
+		if (!tx_ring_full(ap, txcsm, ap->tx_prd))
 			ace_tx_int(dev, txcsm, idx);
 	}
 
@@ -2425,6 +2506,37 @@
 }
 
 
+#if ACENIC_DO_VLAN
+static void ace_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
+{
+	struct ace_private *ap = dev->priv;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	ap->vlgrp = grp;
+
+	restore_flags(flags);
+}
+
+
+static void ace_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+	struct ace_private *ap = dev->priv;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if (ap->vlgrp)
+		ap->vlgrp->vlan_devices[vid] = NULL;
+
+	restore_flags(flags);
+}
+#endif /* ACENIC_DO_VLAN */
+
+
 static int ace_open(struct net_device *dev)
 {
 	struct ace_private *ap;
@@ -2527,7 +2639,7 @@
 	save_flags(flags);
 	cli();
 
-	for (i = 0; i < TX_RING_ENTRIES; i++) {
+	for (i = 0; i < ACE_TX_RING_ENTRIES(ap); i++) {
 		struct sk_buff *skb;
 		dma_addr_t mapping;
 		struct tx_ring_info *info;
@@ -2537,7 +2649,13 @@
 		mapping = pci_unmap_addr(info, mapping);
 
 		if (mapping) {
-			memset(ap->tx_ring + i, 0, sizeof(struct tx_desc));
+			if (ACE_IS_TIGON_I(ap)) {
+				writel(0, &ap->tx_ring[i].addr.addrhi);
+				writel(0, &ap->tx_ring[i].addr.addrlo);
+				writel(0, &ap->tx_ring[i].flagsize);
+			} else
+				memset(ap->tx_ring + i, 0,
+				       sizeof(struct tx_desc));
 			pci_unmap_page(ap->pdev, mapping,
 				       pci_unmap_len(info, maplen),
 				       PCI_DMA_TODEVICE);
@@ -2562,6 +2680,7 @@
 	return 0;
 }
 
+
 static inline dma_addr_t
 ace_map_tx_skb(struct ace_private *ap, struct sk_buff *skb,
 	       struct sk_buff *tail, u32 idx)
@@ -2582,15 +2701,28 @@
 
 
 static inline void
-ace_load_tx_bd(struct tx_desc *desc, u64 addr, u32 flagsize)
+ace_load_tx_bd(struct ace_private *ap, struct tx_desc *desc, u64 addr,
+	       u32 flagsize, u32 vlan_tag)
 {
 #if !USE_TX_COAL_NOW
 	flagsize &= ~BD_FLG_COAL_NOW;
 #endif
 
-	desc->addr.addrhi = addr >> 32;
-	desc->addr.addrlo = addr;
-	desc->flagsize = flagsize;
+	if (ACE_IS_TIGON_I(ap)) {
+		writel(addr >> 32, &desc->addr.addrhi);
+		writel(addr & 0xffffffff, &desc->addr.addrlo);
+		writel(flagsize, &desc->flagsize);
+#if ACENIC_DO_VLAN
+		writel(vlan_tag, &desc->vlanres);
+#endif
+	} else {
+		desc->addr.addrhi = addr >> 32;
+		desc->addr.addrlo = addr;
+		desc->flagsize = flagsize;
+#if ACENIC_DO_VLAN
+		desc->vlanres = vlan_tag;
+#endif
+	}
 }
 
 
@@ -2607,11 +2739,10 @@
 	if (early_stop_netif_stop_queue(dev))
  		return 1;
 
-
 restart:
 	idx = ap->tx_prd;
 
-	if (tx_ring_full(ap->tx_ret_csm, idx))
+	if (tx_ring_full(ap, ap->tx_ret_csm, idx))
 		goto overflow;
 
 #if MAX_SKB_FRAGS
@@ -2619,33 +2750,47 @@
 #endif
 	{
 		dma_addr_t mapping;
+		u32 vlan_tag = 0;
 
 		mapping = ace_map_tx_skb(ap, skb, skb, idx);
 		flagsize = (skb->len << 16) | (BD_FLG_END);
 		if (skb->ip_summed == CHECKSUM_HW)
 			flagsize |= BD_FLG_TCP_UDP_SUM;
+#if ACENIC_DO_VLAN
+		if (vlan_tx_tag_present(skb)) {
+			flagsize |= BD_FLG_VLAN_TAG;
+			vlan_tag = vlan_tx_tag_get(skb);
+		}
+#endif
 		desc = ap->tx_ring + idx;
-		idx = (idx + 1) % TX_RING_ENTRIES;
+		idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
 
 		/* Look at ace_tx_int for explanations. */
-		if (tx_ring_full(ap->tx_ret_csm, idx))
+		if (tx_ring_full(ap, ap->tx_ret_csm, idx))
 			flagsize |= BD_FLG_COAL_NOW;
 
-		ace_load_tx_bd(desc, mapping, flagsize);
+		ace_load_tx_bd(ap, desc, mapping, flagsize, vlan_tag);
 	}
 #if MAX_SKB_FRAGS
 	else {
 		dma_addr_t mapping;
+		u32 vlan_tag = 0;
 		int i, len = 0;
 
 		mapping = ace_map_tx_skb(ap, skb, NULL, idx);
 		flagsize = ((skb->len - skb->data_len) << 16);
 		if (skb->ip_summed == CHECKSUM_HW)
 			flagsize |= BD_FLG_TCP_UDP_SUM;
+#if ACENIC_DO_VLAN
+		if (vlan_tx_tag_present(skb)) {
+			flagsize |= BD_FLG_VLAN_TAG;
+			vlan_tag = vlan_tx_tag_get(skb);
+		}
+#endif
 
-		ace_load_tx_bd(ap->tx_ring + idx, mapping, flagsize);
+		ace_load_tx_bd(ap, ap->tx_ring + idx, mapping, flagsize, vlan_tag);
 
-		idx = (idx + 1) % TX_RING_ENTRIES;
+		idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
 
 		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
@@ -2662,11 +2807,11 @@
 			flagsize = (frag->size << 16);
 			if (skb->ip_summed == CHECKSUM_HW)
 				flagsize |= BD_FLG_TCP_UDP_SUM;
-			idx = (idx + 1) % TX_RING_ENTRIES;
+			idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap);
 
 			if (i == skb_shinfo(skb)->nr_frags - 1) {
 				flagsize |= BD_FLG_END;
-				if (tx_ring_full(ap->tx_ret_csm, idx))
+				if (tx_ring_full(ap, ap->tx_ret_csm, idx))
 					flagsize |= BD_FLG_COAL_NOW;
 
 				/*
@@ -2679,7 +2824,7 @@
 			}
 			pci_unmap_addr_set(info, mapping, mapping);
 			pci_unmap_len_set(info, maplen, frag->size);
-			ace_load_tx_bd(desc, mapping, flagsize);
+			ace_load_tx_bd(ap, desc, mapping, flagsize, vlan_tag);
 		}
 	}
 #endif
@@ -2697,7 +2842,7 @@
 		 * serialized, this is the only situation we have to
 		 * re-test.
 		 */
-		if (!tx_ring_full(ap->tx_ret_csm, idx))
+		if (!tx_ring_full(ap, ap->tx_ret_csm, idx))
 			netif_wake_queue(dev);
 	}
 
@@ -2776,8 +2921,8 @@
 		return -EOPNOTSUPP;
 	if (copy_from_user(&ecmd, ifr->ifr_data, sizeof(ecmd)))
 		return -EFAULT;
-
-	if (ecmd.cmd == ETHTOOL_GSET) {
+	switch (ecmd.cmd) {
+	case ETHTOOL_GSET:
 		ecmd.supported =
 			(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
 			 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
@@ -2825,7 +2970,8 @@
 		if(copy_to_user(ifr->ifr_data, &ecmd, sizeof(ecmd)))
 			return -EFAULT;
 		return 0;
-	} else if (ecmd.cmd == ETHTOOL_SSET) {
+
+	case ETHTOOL_SSET:
 		if(!capable(CAP_NET_ADMIN))
 			return -EPERM;
 
@@ -2882,7 +3028,24 @@
 			ace_issue_cmd(regs, &cmd);
 		}
 		return 0;
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
+		strncpy(info.driver, "acenic", sizeof(info.driver) - 1);
+		sprintf(info.fw_version, "%i.%i.%i", 
+			 tigonFwReleaseMajor, tigonFwReleaseMinor,
+			 tigonFwReleaseFix);
+		strncpy(info.version, version, sizeof(info.version) - 1);
+		if (ap && ap->pdev)
+			strcpy(info.bus_info, ap->pdev->slot_name);
+		if (copy_to_user(ifr->ifr_data, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
 	}
+	default:
+		break;
+	}
+	
 #endif
 
 	return -EOPNOTSUPP;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)