[v2,2/2] rtl8139: add vlan tag extraction

Submitted by Benjamin Poirier on Nov. 9, 2010, 1:46 a.m.

Details

Message ID 1289267181-20613-2-git-send-email-benjamin.poirier@polymtl.ca
State New
Headers show

Commit Message

Benjamin Poirier Nov. 9, 2010, 1:46 a.m.
Add support to the emulated hardware to remove vlan tags in packets
going from the network to the guest.

Signed-off-by: Benjamin Poirier <benjamin.poirier@polymtl.ca>
Cc: Igor V. Kovalenko <igor.v.kovalenko@gmail.com>

---
Changes since v1:
* moved the debug print statement inside the if block and reworded
  accordingly. (as suggested by Igor)

AFAIK, extraction is optional to get vlans working. The driver
requests rx detagging but should not assume that it was done. Under
Linux, the mac layer will catch the vlan ethertype. I only added this
part for completeness (to emulate the hardware more truthfully..?).

 hw/rtl8139.c |   40 +++++++++++++++++++++++++++++++++++++---
 1 files changed, 37 insertions(+), 3 deletions(-)

Patch hide | download patch | download mbox

diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index b599945..7762dbb 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -1024,6 +1024,43 @@  static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_
 
         target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
 
+        if (s->CpCmd & CPlusRxVLAN && size >= ETHER_ADDR_LEN * 2 +
+            VLAN_HDR_LEN && be16_to_cpup((uint16_t *) &buf[ETHER_ADDR_LEN *
+                2]) == ETHERTYPE_VLAN)
+        {
+            size_t new_size = size - VLAN_HDR_LEN;
+
+            rxdw1 &= ~CP_RX_VLAN_TAG_MASK;
+            rxdw1 |= CP_RX_TAVA |
+                le16_to_cpup((uint16_t *)&buf[ETHER_HDR_LEN]);
+
+            if (buf == buf1 || new_size < MIN_BUF_SIZE)
+            {
+                /* move the end and pad */
+                memmove((uint8_t *)buf + ETHER_ADDR_LEN * 2, buf +
+                    ETHER_ADDR_LEN * 2 + VLAN_HDR_LEN, new_size -
+                    ETHER_ADDR_LEN * 2);
+                memset((uint8_t *)buf + new_size, 0, MIN_BUF_SIZE - new_size);
+                size = MIN_BUF_SIZE;
+            }
+            else
+            {
+                /* move the beginning */
+                memmove((uint8_t *)buf + VLAN_HDR_LEN, buf, ETHER_ADDR_LEN *
+                    2);
+                buf += VLAN_HDR_LEN;
+                size = new_size;
+            }
+
+            DEBUG_PRINT(("RTL8139: C+ Rx mode : extracted vlan tag with tci: "
+                    "%u\n", bswap16(rxdw1 & CP_RX_VLAN_TAG_MASK)));
+        }
+        else
+        {
+            /* reset VLAN tag flag */
+            rxdw1 &= ~CP_RX_TAVA;
+        }
+
         /* receive/copy to target memory */
         cpu_physical_memory_write( rx_addr, buf, size );
 
@@ -1082,9 +1119,6 @@  static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_
         rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK;
         rxdw0 |= (size+4);
 
-        /* reset VLAN tag flag */
-        rxdw1 &= ~CP_RX_TAVA;
-
         /* update ring data */
         val = cpu_to_le32(rxdw0);
         cpu_physical_memory_write(cplus_rx_ring_desc,    (uint8_t *)&val, 4);