From: Francois Romieu <romieu@fr.zoreil.com>

802.1Q support.
Mostly stolen from the 8139cp.c driver. The relevant registers and
descriptors bits are identical for both chipsets.

Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/net/Kconfig |    9 ++++
 25-akpm/drivers/net/r8169.c |   96 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 103 insertions(+), 2 deletions(-)

diff -puN drivers/net/Kconfig~r8169-vlan-support drivers/net/Kconfig
--- 25/drivers/net/Kconfig~r8169-vlan-support	2004-08-24 19:18:39.096103640 -0700
+++ 25-akpm/drivers/net/Kconfig	2004-08-24 19:18:39.103102576 -0700
@@ -2061,6 +2061,15 @@ config R8169_NAPI
 
 	  If in doubt, say N.
 
+config R8169_VLAN
+	bool "VLAN support"
+	depends on R8169 && VLAN_8021Q
+	---help---
+	  Say Y here for the r8169 driver to support the functions required
+	  by the kernel 802.1Q code.
+
+	  If in doubt, say Y.
+
 config SK98LIN
 	tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support"
 	depends on PCI
diff -puN drivers/net/r8169.c~r8169-vlan-support drivers/net/r8169.c
--- 25/drivers/net/r8169.c~r8169-vlan-support	2004-08-24 19:18:39.098103336 -0700
+++ 25-akpm/drivers/net/r8169.c	2004-08-24 19:18:39.105102272 -0700
@@ -50,6 +50,7 @@ VERSION 1.6LK	<2004/04/14>
 #include <linux/delay.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
+#include <linux/if_vlan.h>
 #include <linux/crc32.h>
 #include <linux/in.h>
 #include <linux/ip.h>
@@ -78,9 +79,11 @@ VERSION 1.6LK	<2004/04/14>
 
 #ifdef CONFIG_R8169_NAPI
 #define rtl8169_rx_skb			netif_receive_skb
+#define rtl8169_rx_hwaccel_skb		vlan_hwaccel_rx
 #define rtl8169_rx_quota(count, quota)	min(count, quota)
 #else
 #define rtl8169_rx_skb			netif_rx
+#define rtl8169_rx_hwaccel_skb		vlan_hwaccel_receive_skb
 #define rtl8169_rx_quota(count, quota)	count
 #endif
 
@@ -320,6 +323,7 @@ enum _DescStatusBit {
 	IPCS		= (1 << 18), /* Calculate IP checksum */
 	UDPCS		= (1 << 17), /* Calculate UDP/IP checksum */
 	TCPCS		= (1 << 16), /* Calculate TCP/IP checksum */
+	TxVlanTag	= (1 << 17), /* Add VLAN tag */
 
 	/* Rx private */
 	PID1		= (1 << 18), /* Protocol ID bit 1/2 */
@@ -333,6 +337,7 @@ enum _DescStatusBit {
 	IPFail		= (1 << 16), /* IP checksum failed */
 	UDPFail		= (1 << 15), /* UDP/IP checksum failed */
 	TCPFail		= (1 << 14), /* TCP/IP checksum failed */
+	RxVlanTag	= (1 << 16), /* VLAN tag available */
 };
 
 #define RsvdMask	0x3fffc000
@@ -379,7 +384,9 @@ struct rtl8169_private {
 	u16 intr_mask;
 	int phy_auto_nego_reg;
 	int phy_1000_ctrl_reg;
-
+#ifdef CONFIG_R8169_VLAN
+	struct vlan_group *vlgrp;
+#endif
 	int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex);
 	void (*get_settings)(struct net_device *, struct ethtool_cmd *);
 	void (*phy_reset_enable)(void *);
@@ -664,6 +671,77 @@ static int rtl8169_set_rx_csum(struct ne
 	return 0;
 }
 
+#ifdef CONFIG_R8169_VLAN
+
+static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp,
+				      struct sk_buff *skb)
+{
+	return (tp->vlgrp && vlan_tx_tag_present(skb)) ?
+		TxVlanTag | cpu_to_be16(vlan_tx_tag_get(skb)) : 0x00;
+}
+
+static void rtl8169_vlan_rx_register(struct net_device *dev,
+				     struct vlan_group *grp)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tp->lock, flags);
+	tp->vlgrp = grp;
+	tp->cp_cmd |= RxVlan;
+	RTL_W16(CPlusCmd, tp->cp_cmd);
+	RTL_R16(CPlusCmd);
+	spin_unlock_irqrestore(&tp->lock, flags);
+}
+
+static void rtl8169_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	void *ioaddr = tp->mmio_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tp->lock, flags);
+	tp->cp_cmd &= ~RxVlan;
+	RTL_W16(CPlusCmd, tp->cp_cmd);
+	RTL_R16(CPlusCmd);
+	if (tp->vlgrp)
+		tp->vlgrp->vlan_devices[vid] = NULL;
+	spin_unlock_irqrestore(&tp->lock, flags);
+}
+
+static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc,
+			       struct sk_buff *skb)
+{
+	u32 opts2 = desc->opts2;
+	int ret;
+
+	if (tp->vlgrp && (opts2 & RxVlanTag)) {
+		rtl8169_rx_hwaccel_skb(skb, tp->vlgrp,
+				       be16_to_cpu(opts2 & 0xffff));
+		ret = 0;
+	} else
+		ret = -1;
+	desc->opts2 = 0;
+	return ret;
+}
+
+#else /* !CONFIG_R8169_VLAN */
+
+static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp,
+				      struct sk_buff *skb)
+{
+	return 0;
+}
+
+static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc,
+			       struct sk_buff *skb)
+{
+	return -1;
+}
+
+#endif
+
 static void rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd)
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
@@ -1072,6 +1150,8 @@ rtl8169_init_board(struct pci_dev *pdev,
 
 	tp->cp_cmd = PCIMulRW | RxChkSum;
 
+	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+
 	if ((sizeof(dma_addr_t) > 32) && !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
 		tp->cp_cmd |= PCIDAC;
 		dev->features |= NETIF_F_HIGHDMA;
@@ -1206,11 +1286,19 @@ rtl8169_init_one(struct pci_dev *pdev, c
 	dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
 	dev->irq = pdev->irq;
 	dev->base_addr = (unsigned long) ioaddr;
+
 #ifdef CONFIG_R8169_NAPI
 	dev->poll = rtl8169_poll;
 	dev->weight = R8169_NAPI_WEIGHT;
 	printk(KERN_INFO PFX "NAPI enabled\n");
 #endif
+
+#ifdef CONFIG_R8169_VLAN
+	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+	dev->vlan_rx_register = rtl8169_vlan_rx_register;
+	dev->vlan_rx_kill_vid = rtl8169_vlan_rx_kill_vid;
+#endif
+
 	tp->intr_mask = 0xffff;
 	tp->pci_dev = pdev;
 	tp->mmio_addr = ioaddr;
@@ -1560,6 +1648,7 @@ static void rtl8169_unmap_tx_skb(struct 
 	unsigned int len = tx_skb->len;
 
 	pci_unmap_single(pdev, le64_to_cpu(desc->addr), len, PCI_DMA_TODEVICE);
+	desc->opts2 = 0x00;
 	desc->addr = 0x00;
 	tx_skb->len = 0;
 }
@@ -1711,6 +1800,7 @@ static int rtl8169_start_xmit(struct sk_
 
 	tp->tx_skb[entry].len = len;
 	txd->addr = cpu_to_le64(mapping);
+	txd->opts2 = rtl8169_tx_vlan_tag(tp, skb);
 
 	wmb();
 
@@ -1878,7 +1968,9 @@ rtl8169_rx_interrupt(struct net_device *
 			skb->dev = dev;
 			skb_put(skb, pkt_size);
 			skb->protocol = eth_type_trans(skb, dev);
-			rtl8169_rx_skb(skb);
+
+			if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0)
+				rtl8169_rx_skb(skb);
 
 			dev->last_rx = jiffies;
 			tp->stats.rx_bytes += pkt_size;
_