Staging: hv: add the Hyper-V virtual network driver
authorHank Janssen <hjanssen@microsoft.com>
Mon, 13 Jul 2009 22:34:54 +0000 (15:34 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Sep 2009 19:01:43 +0000 (12:01 -0700)
This is the virtual network driver when running Linux on top of Hyper-V.

Signed-off-by: Hank Janssen <hjanssen@microsoft.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/hv/NetVsc.c [new file with mode: 0644]
drivers/staging/hv/NetVsc.h [new file with mode: 0644]
drivers/staging/hv/RndisFilter.c [new file with mode: 0644]
drivers/staging/hv/RndisFilter.h [new file with mode: 0644]
drivers/staging/hv/netvsc_drv.c [new file with mode: 0644]

diff --git a/drivers/staging/hv/NetVsc.c b/drivers/staging/hv/NetVsc.c
new file mode 100644 (file)
index 0000000..6cc3143
--- /dev/null
@@ -0,0 +1,1499 @@
+/*
+ *
+ * Copyright (c) 2009, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ *   Hank Janssen  <hjanssen@microsoft.com>
+ *
+ */
+
+
+#include "logging.h"
+#include "NetVsc.h"
+#include "RndisFilter.h"
+
+
+//
+// Globals
+//
+static const char* gDriverName="netvsc";
+
+// {F8615163-DF3E-46c5-913F-F2D2F965ED0E}
+static const GUID gNetVscDeviceType={
+       .Data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}
+};
+
+
+//
+// Internal routines
+//
+static int
+NetVscOnDeviceAdd(
+       DEVICE_OBJECT   *Device,
+       void                    *AdditionalInfo
+       );
+
+static int
+NetVscOnDeviceRemove(
+       DEVICE_OBJECT *Device
+       );
+
+static void
+NetVscOnCleanup(
+       DRIVER_OBJECT *Driver
+       );
+
+static void
+NetVscOnChannelCallback(
+       PVOID context
+       );
+
+static int
+NetVscInitializeSendBufferWithNetVsp(
+       DEVICE_OBJECT                   *Device
+       );
+
+static int
+NetVscInitializeReceiveBufferWithNetVsp(
+       DEVICE_OBJECT                   *Device
+       );
+
+static int
+NetVscDestroySendBuffer(
+       NETVSC_DEVICE   *NetDevice
+       );
+
+static int
+NetVscDestroyReceiveBuffer(
+       NETVSC_DEVICE   *NetDevice
+       );
+
+static int
+NetVscConnectToVsp(
+       DEVICE_OBJECT           *Device
+       );
+
+static void
+NetVscOnSendCompletion(
+       DEVICE_OBJECT           *Device,
+       VMPACKET_DESCRIPTOR *Packet
+       );
+
+static int
+NetVscOnSend(
+       DEVICE_OBJECT   *Device,
+       NETVSC_PACKET   *Packet
+       );
+
+static void
+NetVscOnReceive(
+       DEVICE_OBJECT           *Device,
+       VMPACKET_DESCRIPTOR *Packet
+       );
+
+static void
+NetVscOnReceiveCompletion(
+       PVOID Context
+       );
+
+static void
+NetVscSendReceiveCompletion(
+       DEVICE_OBJECT   *Device,
+       UINT64                  TransactionId
+       );
+
+static inline NETVSC_DEVICE* AllocNetDevice(DEVICE_OBJECT *Device)
+{
+       NETVSC_DEVICE *netDevice;
+
+       netDevice = MemAllocZeroed(sizeof(NETVSC_DEVICE));
+       if (!netDevice)
+               return NULL;
+
+       // Set to 2 to allow both inbound and outbound traffic
+       InterlockedCompareExchange(&netDevice->RefCount, 2, 0);
+
+       netDevice->Device = Device;
+       Device->Extension = netDevice;
+
+       return netDevice;
+}
+
+static inline void FreeNetDevice(NETVSC_DEVICE *Device)
+{
+       ASSERT(Device->RefCount == 0);
+       Device->Device->Extension = NULL;
+       MemFree(Device);
+}
+
+
+// Get the net device object iff exists and its refcount > 1
+static inline NETVSC_DEVICE* GetOutboundNetDevice(DEVICE_OBJECT        *Device)
+{
+       NETVSC_DEVICE *netDevice;
+
+       netDevice = (NETVSC_DEVICE*)Device->Extension;
+       if (netDevice && netDevice->RefCount > 1)
+       {
+               InterlockedIncrement(&netDevice->RefCount);
+       }
+       else
+       {
+               netDevice = NULL;
+       }
+
+       return netDevice;
+}
+
+// Get the net device object iff exists and its refcount > 0
+static inline NETVSC_DEVICE* GetInboundNetDevice(DEVICE_OBJECT *Device)
+{
+       NETVSC_DEVICE *netDevice;
+
+       netDevice = (NETVSC_DEVICE*)Device->Extension;
+       if (netDevice && netDevice->RefCount)
+       {
+               InterlockedIncrement(&netDevice->RefCount);
+       }
+       else
+       {
+               netDevice = NULL;
+       }
+
+       return netDevice;
+}
+
+static inline void PutNetDevice(DEVICE_OBJECT *Device)
+{
+       NETVSC_DEVICE *netDevice;
+
+       netDevice = (NETVSC_DEVICE*)Device->Extension;
+       ASSERT(netDevice);
+
+       InterlockedDecrement(&netDevice->RefCount);
+}
+
+static inline NETVSC_DEVICE* ReleaseOutboundNetDevice(DEVICE_OBJECT *Device)
+{
+       NETVSC_DEVICE *netDevice;
+
+       netDevice = (NETVSC_DEVICE*)Device->Extension;
+       if (netDevice == NULL)
+               return NULL;
+
+       // Busy wait until the ref drop to 2, then set it to 1
+       while (InterlockedCompareExchange(&netDevice->RefCount, 1, 2) != 2)
+       {
+               Sleep(100);
+       }
+
+       return netDevice;
+}
+
+static inline NETVSC_DEVICE* ReleaseInboundNetDevice(DEVICE_OBJECT *Device)
+{
+       NETVSC_DEVICE *netDevice;
+
+       netDevice = (NETVSC_DEVICE*)Device->Extension;
+       if (netDevice == NULL)
+               return NULL;
+
+       // Busy wait until the ref drop to 1, then set it to 0
+       while (InterlockedCompareExchange(&netDevice->RefCount, 0, 1) != 1)
+       {
+               Sleep(100);
+       }
+
+       Device->Extension = NULL;
+       return netDevice;
+}
+
+/*++;
+
+
+Name:
+       NetVscInitialize()
+
+Description:
+       Main entry point
+
+--*/
+int
+NetVscInitialize(
+       DRIVER_OBJECT *drv
+       )
+{
+       NETVSC_DRIVER_OBJECT* driver = (NETVSC_DRIVER_OBJECT*)drv;
+       int ret=0;
+
+       DPRINT_ENTER(NETVSC);
+
+       DPRINT_DBG(NETVSC, "sizeof(NETVSC_PACKET)=%d, sizeof(NVSP_MESSAGE)=%d, sizeof(VMTRANSFER_PAGE_PACKET_HEADER)=%d",
+               sizeof(NETVSC_PACKET), sizeof(NVSP_MESSAGE), sizeof(VMTRANSFER_PAGE_PACKET_HEADER));
+
+       // Make sure we are at least 2 pages since 1 page is used for control
+       ASSERT(driver->RingBufferSize >= (PAGE_SIZE << 1));
+
+       drv->name = gDriverName;
+       memcpy(&drv->deviceType, &gNetVscDeviceType, sizeof(GUID));
+
+       // Make sure it is set by the caller
+       ASSERT(driver->OnReceiveCallback);
+       ASSERT(driver->OnLinkStatusChanged);
+
+       // Setup the dispatch table
+       driver->Base.OnDeviceAdd                = NetVscOnDeviceAdd;
+       driver->Base.OnDeviceRemove             = NetVscOnDeviceRemove;
+       driver->Base.OnCleanup                  = NetVscOnCleanup;
+
+       driver->OnSend                                  = NetVscOnSend;
+
+       RndisFilterInit(driver);
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+static int
+NetVscInitializeReceiveBufferWithNetVsp(
+       DEVICE_OBJECT   *Device
+       )
+{
+       int ret=0;
+       NETVSC_DEVICE *netDevice;
+       NVSP_MESSAGE *initPacket;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = GetOutboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "unable to get net device...device being destroyed?");
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+       ASSERT(netDevice->ReceiveBufferSize > 0);
+       ASSERT((netDevice->ReceiveBufferSize & (PAGE_SIZE-1)) == 0); // page-size grandularity
+
+       netDevice->ReceiveBuffer = PageAlloc(netDevice->ReceiveBufferSize >> PAGE_SHIFT);
+       if (!netDevice->ReceiveBuffer)
+       {
+               DPRINT_ERR(NETVSC, "unable to allocate receive buffer of size %d", netDevice->ReceiveBufferSize);
+               ret = -1;
+               goto Cleanup;
+       }
+       ASSERT(((ULONG_PTR)netDevice->ReceiveBuffer & (PAGE_SIZE-1)) == 0); // page-aligned buffer
+
+       DPRINT_INFO(NETVSC, "Establishing receive buffer's GPADL...");
+
+       // Establish the gpadl handle for this buffer on this channel.
+       // Note: This call uses the vmbus connection rather than the channel to establish
+       // the gpadl handle.
+       ret = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device,
+                                                                                                                               netDevice->ReceiveBuffer,
+                                                                                                                               netDevice->ReceiveBufferSize,
+                                                                                                                               &netDevice->ReceiveBufferGpadlHandle);
+
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to establish receive buffer's gpadl");
+               goto Cleanup;
+       }
+
+       //WaitEventWait(ext->ChannelInitEvent);
+
+       // Notify the NetVsp of the gpadl handle
+       DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendReceiveBuffer...");
+
+       initPacket = &netDevice->ChannelInitPacket;
+
+       memset(initPacket, 0, sizeof(NVSP_MESSAGE));
+
+    initPacket->Header.MessageType = NvspMessage1TypeSendReceiveBuffer;
+    initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->ReceiveBufferGpadlHandle;
+    initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID;
+
+       // Send the gpadl notification request
+       ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+                                                                                                                       initPacket,
+                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                       (ULONG_PTR)initPacket,
+                                                                                                                       VmbusPacketTypeDataInBand,
+                                                                                                                       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to send receive buffer's gpadl to netvsp");
+               goto Cleanup;
+       }
+
+       WaitEventWait(netDevice->ChannelInitEvent);
+
+       // Check the response
+       if (initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status != NvspStatusSuccess)
+       {
+               DPRINT_ERR(NETVSC,
+                       "Unable to complete receive buffer initialzation with NetVsp - status %d",
+                       initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Status);
+               ret = -1;
+               goto Cleanup;
+       }
+
+       // Parse the response
+       ASSERT(netDevice->ReceiveSectionCount == 0);
+       ASSERT(netDevice->ReceiveSections == NULL);
+
+       netDevice->ReceiveSectionCount = initPacket->Messages.Version1Messages.SendReceiveBufferComplete.NumSections;
+
+       netDevice->ReceiveSections = MemAlloc(netDevice->ReceiveSectionCount * sizeof(NVSP_1_RECEIVE_BUFFER_SECTION));
+       if (netDevice->ReceiveSections == NULL)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       memcpy(netDevice->ReceiveSections,
+               initPacket->Messages.Version1Messages.SendReceiveBufferComplete.Sections,
+               netDevice->ReceiveSectionCount * sizeof(NVSP_1_RECEIVE_BUFFER_SECTION));
+
+       DPRINT_INFO(NETVSC,
+               "Receive sections info (count %d, offset %d, endoffset %d, suballoc size %d, num suballocs %d)",
+               netDevice->ReceiveSectionCount, netDevice->ReceiveSections[0].Offset, netDevice->ReceiveSections[0].EndOffset,
+               netDevice->ReceiveSections[0].SubAllocationSize, netDevice->ReceiveSections[0].NumSubAllocations);
+
+
+       //For 1st release, there should only be 1 section that represents the entire receive buffer
+       if (netDevice->ReceiveSectionCount != 1 ||
+               netDevice->ReceiveSections->Offset != 0 )
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       goto Exit;
+
+Cleanup:
+       NetVscDestroyReceiveBuffer(netDevice);
+
+Exit:
+       PutNetDevice(Device);
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+
+static int
+NetVscInitializeSendBufferWithNetVsp(
+       DEVICE_OBJECT   *Device
+       )
+{
+       int ret=0;
+       NETVSC_DEVICE *netDevice;
+       NVSP_MESSAGE *initPacket;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = GetOutboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "unable to get net device...device being destroyed?");
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+       ASSERT(netDevice->SendBufferSize > 0);
+       ASSERT((netDevice->SendBufferSize & (PAGE_SIZE-1)) == 0); // page-size grandularity
+
+       netDevice->SendBuffer = PageAlloc(netDevice->SendBufferSize >> PAGE_SHIFT);
+       if (!netDevice->SendBuffer)
+       {
+               DPRINT_ERR(NETVSC, "unable to allocate send buffer of size %d", netDevice->SendBufferSize);
+               ret = -1;
+               goto Cleanup;
+       }
+       ASSERT(((ULONG_PTR)netDevice->SendBuffer & (PAGE_SIZE-1)) == 0); // page-aligned buffer
+
+       DPRINT_INFO(NETVSC, "Establishing send buffer's GPADL...");
+
+       // Establish the gpadl handle for this buffer on this channel.
+       // Note: This call uses the vmbus connection rather than the channel to establish
+       // the gpadl handle.
+       ret = Device->Driver->VmbusChannelInterface.EstablishGpadl(Device,
+                                                                                                                               netDevice->SendBuffer,
+                                                                                                                               netDevice->SendBufferSize,
+                                                                                                                               &netDevice->SendBufferGpadlHandle);
+
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to establish send buffer's gpadl");
+               goto Cleanup;
+       }
+
+       //WaitEventWait(ext->ChannelInitEvent);
+
+       // Notify the NetVsp of the gpadl handle
+       DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendSendBuffer...");
+
+       initPacket = &netDevice->ChannelInitPacket;
+
+       memset(initPacket, 0, sizeof(NVSP_MESSAGE));
+
+    initPacket->Header.MessageType = NvspMessage1TypeSendSendBuffer;
+    initPacket->Messages.Version1Messages.SendReceiveBuffer.GpadlHandle = netDevice->SendBufferGpadlHandle;
+    initPacket->Messages.Version1Messages.SendReceiveBuffer.Id = NETVSC_SEND_BUFFER_ID;
+
+       // Send the gpadl notification request
+       ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+                                                                                                                       initPacket,
+                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                       (ULONG_PTR)initPacket,
+                                                                                                                       VmbusPacketTypeDataInBand,
+                                                                                                                       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to send receive buffer's gpadl to netvsp");
+               goto Cleanup;
+       }
+
+       WaitEventWait(netDevice->ChannelInitEvent);
+
+       // Check the response
+       if (initPacket->Messages.Version1Messages.SendSendBufferComplete.Status != NvspStatusSuccess)
+       {
+               DPRINT_ERR(NETVSC,
+                       "Unable to complete send buffer initialzation with NetVsp - status %d",
+                       initPacket->Messages.Version1Messages.SendSendBufferComplete.Status);
+               ret = -1;
+               goto Cleanup;
+       }
+
+       netDevice->SendSectionSize = initPacket->Messages.Version1Messages.SendSendBufferComplete.SectionSize;
+
+       goto Exit;
+
+Cleanup:
+       NetVscDestroySendBuffer(netDevice);
+
+Exit:
+       PutNetDevice(Device);
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+static int
+NetVscDestroyReceiveBuffer(
+       NETVSC_DEVICE   *NetDevice
+       )
+{
+       NVSP_MESSAGE *revokePacket;
+       int ret=0;
+
+
+       DPRINT_ENTER(NETVSC);
+
+       // If we got a section count, it means we received a SendReceiveBufferComplete msg
+       // (ie sent NvspMessage1TypeSendReceiveBuffer msg) therefore, we need to send a revoke msg here
+       if (NetDevice->ReceiveSectionCount)
+       {
+               DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeRevokeReceiveBuffer...");
+
+               // Send the revoke receive buffer
+               revokePacket = &NetDevice->RevokePacket;
+               memset(revokePacket, 0, sizeof(NVSP_MESSAGE));
+
+               revokePacket->Header.MessageType = NvspMessage1TypeRevokeReceiveBuffer;
+               revokePacket->Messages.Version1Messages.RevokeReceiveBuffer.Id = NETVSC_RECEIVE_BUFFER_ID;
+
+               ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket(NetDevice->Device,
+                                                                                                                                                       revokePacket,
+                                                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                                                       (ULONG_PTR)revokePacket,
+                                                                                                                                                       VmbusPacketTypeDataInBand,
+                                                                                                                                                       0);
+               // If we failed here, we might as well return and have a leak rather than continue and a bugchk
+               if (ret != 0)
+               {
+                       DPRINT_ERR(NETVSC, "unable to send revoke receive buffer to netvsp");
+                       DPRINT_EXIT(NETVSC);
+                       return -1;
+               }
+       }
+
+       // Teardown the gpadl on the vsp end
+       if (NetDevice->ReceiveBufferGpadlHandle)
+       {
+               DPRINT_INFO(NETVSC, "Tearing down receive buffer's GPADL...");
+
+               ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl(NetDevice->Device,
+                                                                                                                                                               NetDevice->ReceiveBufferGpadlHandle);
+
+               // If we failed here, we might as well return and have a leak rather than continue and a bugchk
+               if (ret != 0)
+               {
+                       DPRINT_ERR(NETVSC, "unable to teardown receive buffer's gpadl");
+                       DPRINT_EXIT(NETVSC);
+                       return -1;
+               }
+               NetDevice->ReceiveBufferGpadlHandle = 0;
+       }
+
+       if (NetDevice->ReceiveBuffer)
+       {
+               DPRINT_INFO(NETVSC, "Freeing up receive buffer...");
+
+               // Free up the receive buffer
+               PageFree(NetDevice->ReceiveBuffer, NetDevice->ReceiveBufferSize >> PAGE_SHIFT);
+               NetDevice->ReceiveBuffer = NULL;
+       }
+
+       if (NetDevice->ReceiveSections)
+       {
+               MemFree(NetDevice->ReceiveSections);
+               NetDevice->ReceiveSections = NULL;
+               NetDevice->ReceiveSectionCount = 0;
+       }
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+
+
+
+static int
+NetVscDestroySendBuffer(
+       NETVSC_DEVICE   *NetDevice
+       )
+{
+       NVSP_MESSAGE *revokePacket;
+       int ret=0;
+
+
+       DPRINT_ENTER(NETVSC);
+
+       // If we got a section count, it means we received a SendReceiveBufferComplete msg
+       // (ie sent NvspMessage1TypeSendReceiveBuffer msg) therefore, we need to send a revoke msg here
+       if (NetDevice->SendSectionSize)
+       {
+               DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeRevokeSendBuffer...");
+
+               // Send the revoke send buffer
+               revokePacket = &NetDevice->RevokePacket;
+               memset(revokePacket, 0, sizeof(NVSP_MESSAGE));
+
+               revokePacket->Header.MessageType = NvspMessage1TypeRevokeSendBuffer;
+               revokePacket->Messages.Version1Messages.RevokeSendBuffer.Id = NETVSC_SEND_BUFFER_ID;
+
+               ret = NetDevice->Device->Driver->VmbusChannelInterface.SendPacket(NetDevice->Device,
+                                                                                                                                                       revokePacket,
+                                                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                                                       (ULONG_PTR)revokePacket,
+                                                                                                                                                       VmbusPacketTypeDataInBand,
+                                                                                                                                                       0);
+               // If we failed here, we might as well return and have a leak rather than continue and a bugchk
+               if (ret != 0)
+               {
+                       DPRINT_ERR(NETVSC, "unable to send revoke send buffer to netvsp");
+                       DPRINT_EXIT(NETVSC);
+                       return -1;
+               }
+       }
+
+       // Teardown the gpadl on the vsp end
+       if (NetDevice->SendBufferGpadlHandle)
+       {
+               DPRINT_INFO(NETVSC, "Tearing down send buffer's GPADL...");
+
+               ret = NetDevice->Device->Driver->VmbusChannelInterface.TeardownGpadl(NetDevice->Device,
+                                                                                                                                                               NetDevice->SendBufferGpadlHandle);
+
+               // If we failed here, we might as well return and have a leak rather than continue and a bugchk
+               if (ret != 0)
+               {
+                       DPRINT_ERR(NETVSC, "unable to teardown send buffer's gpadl");
+                       DPRINT_EXIT(NETVSC);
+                       return -1;
+               }
+               NetDevice->SendBufferGpadlHandle = 0;
+       }
+
+       if (NetDevice->SendBuffer)
+       {
+               DPRINT_INFO(NETVSC, "Freeing up send buffer...");
+
+               // Free up the receive buffer
+               PageFree(NetDevice->SendBuffer, NetDevice->SendBufferSize >> PAGE_SHIFT);
+               NetDevice->SendBuffer = NULL;
+       }
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+
+
+static int
+NetVscConnectToVsp(
+       DEVICE_OBJECT   *Device
+       )
+{
+       int ret=0;
+       NETVSC_DEVICE *netDevice;
+       NVSP_MESSAGE *initPacket;
+       int ndisVersion;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = GetOutboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "unable to get net device...device being destroyed?");
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+
+       initPacket = &netDevice->ChannelInitPacket;
+
+       memset(initPacket, 0, sizeof(NVSP_MESSAGE));
+       initPacket->Header.MessageType = NvspMessageTypeInit;
+    initPacket->Messages.InitMessages.Init.MinProtocolVersion = NVSP_MIN_PROTOCOL_VERSION;
+    initPacket->Messages.InitMessages.Init.MaxProtocolVersion = NVSP_MAX_PROTOCOL_VERSION;
+
+       DPRINT_INFO(NETVSC, "Sending NvspMessageTypeInit...");
+
+       // Send the init request
+       ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+                                                                                                                       initPacket,
+                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                       (ULONG_PTR)initPacket,
+                                                                                                                       VmbusPacketTypeDataInBand,
+                                                                                                                       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+       if( ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to send NvspMessageTypeInit");
+               goto Cleanup;
+       }
+
+       WaitEventWait(netDevice->ChannelInitEvent);
+
+       // Now, check the response
+       //ASSERT(initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength <= MAX_MULTIPAGE_BUFFER_COUNT);
+       DPRINT_INFO(NETVSC, "NvspMessageTypeInit status(%d) max mdl chain (%d)",
+               initPacket->Messages.InitMessages.InitComplete.Status,
+               initPacket->Messages.InitMessages.InitComplete.MaximumMdlChainLength);
+
+       if (initPacket->Messages.InitMessages.InitComplete.Status != NvspStatusSuccess)
+       {
+               DPRINT_ERR(NETVSC, "unable to initialize with netvsp (status 0x%x)", initPacket->Messages.InitMessages.InitComplete.Status);
+               ret = -1;
+               goto Cleanup;
+       }
+
+       if (initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion != NVSP_PROTOCOL_VERSION_1)
+       {
+               DPRINT_ERR(NETVSC, "unable to initialize with netvsp (version expected 1 got %d)",
+                       initPacket->Messages.InitMessages.InitComplete.NegotiatedProtocolVersion);
+               ret = -1;
+               goto Cleanup;
+       }
+       DPRINT_INFO(NETVSC, "Sending NvspMessage1TypeSendNdisVersion...");
+
+       // Send the ndis version
+       memset(initPacket, 0, sizeof(NVSP_MESSAGE));
+
+    ndisVersion = 0x00050000;
+
+    initPacket->Header.MessageType = NvspMessage1TypeSendNdisVersion;
+    initPacket->Messages.Version1Messages.SendNdisVersion.NdisMajorVersion = (ndisVersion & 0xFFFF0000) >> 16;
+    initPacket->Messages.Version1Messages.SendNdisVersion.NdisMinorVersion = ndisVersion & 0xFFFF;
+
+       // Send the init request
+       ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+                                                                                                                       initPacket,
+                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                       (ULONG_PTR)initPacket,
+                                                                                                                       VmbusPacketTypeDataInBand,
+                                                                                                                       0);
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to send NvspMessage1TypeSendNdisVersion");
+               ret = -1;
+               goto Cleanup;
+       }
+       //
+       // BUGBUG - We have to wait for the above msg since the netvsp uses KMCL which acknowledges packet (completion packet)
+       // since our Vmbus always set the VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED flag
+       //WaitEventWait(NetVscChannel->ChannelInitEvent);
+
+       // Post the big receive buffer to NetVSP
+       ret = NetVscInitializeReceiveBufferWithNetVsp(Device);
+       if (ret == 0)
+       {
+               ret = NetVscInitializeSendBufferWithNetVsp(Device);
+       }
+
+Cleanup:
+       PutNetDevice(Device);
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+static void
+NetVscDisconnectFromVsp(
+       NETVSC_DEVICE   *NetDevice
+       )
+{
+       DPRINT_ENTER(NETVSC);
+
+       NetVscDestroyReceiveBuffer(NetDevice);
+       NetVscDestroySendBuffer(NetDevice);
+
+       DPRINT_EXIT(NETVSC);
+}
+
+
+/*++
+
+Name:
+       NetVscOnDeviceAdd()
+
+Description:
+       Callback when the device belonging to this driver is added
+
+--*/
+int
+NetVscOnDeviceAdd(
+       DEVICE_OBJECT   *Device,
+       void                    *AdditionalInfo
+       )
+{
+       int ret=0;
+       int i;
+
+       NETVSC_DEVICE* netDevice;
+       NETVSC_PACKET* packet;
+       LIST_ENTRY *entry;
+
+       NETVSC_DRIVER_OBJECT *netDriver = (NETVSC_DRIVER_OBJECT*) Device->Driver;;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = AllocNetDevice(Device);
+       if (!netDevice)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       DPRINT_DBG(NETVSC, "netvsc channel object allocated - %p", netDevice);
+
+       // Initialize the NetVSC channel extension
+       netDevice->ReceiveBufferSize = NETVSC_RECEIVE_BUFFER_SIZE;
+       netDevice->ReceivePacketListLock = SpinlockCreate();
+
+       netDevice->SendBufferSize = NETVSC_SEND_BUFFER_SIZE;
+
+       INITIALIZE_LIST_HEAD(&netDevice->ReceivePacketList);
+
+       for (i=0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++)
+       {
+               packet = MemAllocZeroed(sizeof(NETVSC_PACKET) + (NETVSC_RECEIVE_SG_COUNT* sizeof(PAGE_BUFFER)));
+               if (!packet)
+               {
+                       DPRINT_DBG(NETVSC, "unable to allocate netvsc pkts for receive pool (wanted %d got %d)", NETVSC_RECEIVE_PACKETLIST_COUNT, i);
+                       break;
+               }
+
+               INSERT_TAIL_LIST(&netDevice->ReceivePacketList, &packet->ListEntry);
+       }
+       netDevice->ChannelInitEvent = WaitEventCreate();
+
+       // Open the channel
+       ret = Device->Driver->VmbusChannelInterface.Open(Device,
+                                                                                                               netDriver->RingBufferSize,
+                                                                                                               netDriver->RingBufferSize,
+                                                                                                               NULL, 0,
+                                                                                                               NetVscOnChannelCallback,
+                                                                                                               Device
+                                                                                                               );
+
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to open channel: %d", ret);
+               ret = -1;
+               goto Cleanup;
+       }
+
+       // Channel is opened
+       DPRINT_INFO(NETVSC, "*** NetVSC channel opened successfully! ***");
+
+       // Connect with the NetVsp
+       ret = NetVscConnectToVsp(Device);
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "unable to connect to NetVSP - %d", ret);
+               ret = -1;
+               goto Close;
+       }
+
+       DPRINT_INFO(NETVSC, "*** NetVSC channel handshake result - %d ***", ret);
+
+       DPRINT_EXIT(NETVSC);
+       return ret;
+
+Close:
+       // Now, we can close the channel safely
+       Device->Driver->VmbusChannelInterface.Close(Device);
+
+Cleanup:
+
+       if (netDevice)
+       {
+               WaitEventClose(netDevice->ChannelInitEvent);
+
+               while (!IsListEmpty(&netDevice->ReceivePacketList))
+               {
+                       entry = REMOVE_HEAD_LIST(&netDevice->ReceivePacketList);
+                       packet = CONTAINING_RECORD(entry, NETVSC_PACKET, ListEntry);
+                       MemFree(packet);
+               }
+
+               SpinlockClose(netDevice->ReceivePacketListLock);
+
+               ReleaseOutboundNetDevice(Device);
+               ReleaseInboundNetDevice(Device);
+
+               FreeNetDevice(netDevice);
+       }
+
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+
+/*++
+
+Name:
+       NetVscOnDeviceRemove()
+
+Description:
+       Callback when the root bus device is removed
+
+--*/
+int
+NetVscOnDeviceRemove(
+       DEVICE_OBJECT *Device
+       )
+{
+       NETVSC_DEVICE *netDevice;
+       NETVSC_PACKET *netvscPacket;
+       int ret=0;
+       LIST_ENTRY *entry;
+
+       DPRINT_ENTER(NETVSC);
+
+       DPRINT_INFO(NETVSC, "Disabling outbound traffic on net device (%p)...", Device->Extension);
+
+       // Stop outbound traffic ie sends and receives completions
+       netDevice = ReleaseOutboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "No net device present!!");
+               return -1;
+       }
+
+       // Wait for all send completions
+       while (netDevice->NumOutstandingSends)
+       {
+               DPRINT_INFO(NETVSC, "waiting for %d requests to complete...", netDevice->NumOutstandingSends);
+
+               Sleep(100);
+       }
+
+       DPRINT_INFO(NETVSC, "Disconnecting from netvsp...");
+
+       NetVscDisconnectFromVsp(netDevice);
+
+       DPRINT_INFO(NETVSC, "Disabling inbound traffic on net device (%p)...", Device->Extension);
+
+       // Stop inbound traffic ie receives and sends completions
+       netDevice = ReleaseInboundNetDevice(Device);
+
+       // At this point, no one should be accessing netDevice except in here
+       DPRINT_INFO(NETVSC, "net device (%p) safe to remove", netDevice);
+
+       // Now, we can close the channel safely
+       Device->Driver->VmbusChannelInterface.Close(Device);
+
+       // Release all resources
+       while (!IsListEmpty(&netDevice->ReceivePacketList))
+       {
+               entry = REMOVE_HEAD_LIST(&netDevice->ReceivePacketList);
+               netvscPacket = CONTAINING_RECORD(entry, NETVSC_PACKET, ListEntry);
+
+               MemFree(netvscPacket);
+       }
+
+       SpinlockClose(netDevice->ReceivePacketListLock);
+       WaitEventClose(netDevice->ChannelInitEvent);
+       FreeNetDevice(netDevice);
+
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+
+
+/*++
+
+Name:
+       NetVscOnCleanup()
+
+Description:
+       Perform any cleanup when the driver is removed
+
+--*/
+void
+NetVscOnCleanup(
+       DRIVER_OBJECT *drv
+       )
+{
+       DPRINT_ENTER(NETVSC);
+
+       DPRINT_EXIT(NETVSC);
+}
+
+static void
+NetVscOnSendCompletion(
+       DEVICE_OBJECT           *Device,
+       VMPACKET_DESCRIPTOR *Packet
+       )
+{
+       NETVSC_DEVICE* netDevice;
+       NVSP_MESSAGE *nvspPacket;
+       NETVSC_PACKET *nvscPacket;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = GetInboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "unable to get net device...device being destroyed?");
+               DPRINT_EXIT(NETVSC);
+               return;
+       }
+
+       nvspPacket = (NVSP_MESSAGE*)((ULONG_PTR)Packet + (Packet->DataOffset8 << 3));
+
+       DPRINT_DBG(NETVSC, "send completion packet - type %d", nvspPacket->Header.MessageType);
+
+       if (nvspPacket->Header.MessageType == NvspMessageTypeInitComplete ||
+               nvspPacket->Header.MessageType == NvspMessage1TypeSendReceiveBufferComplete ||
+               nvspPacket->Header.MessageType == NvspMessage1TypeSendSendBufferComplete)
+       {
+               // Copy the response back
+               memcpy(&netDevice->ChannelInitPacket, nvspPacket, sizeof(NVSP_MESSAGE));
+               WaitEventSet(netDevice->ChannelInitEvent);
+       }
+       else if (nvspPacket->Header.MessageType == NvspMessage1TypeSendRNDISPacketComplete)
+       {
+               // Get the send context
+               nvscPacket = (NETVSC_PACKET *)(ULONG_PTR)Packet->TransactionId;
+               ASSERT(nvscPacket);
+
+               // Notify the layer above us
+               nvscPacket->Completion.Send.OnSendCompletion(nvscPacket->Completion.Send.SendCompletionContext);
+
+               InterlockedDecrement(&netDevice->NumOutstandingSends);
+       }
+       else
+       {
+               DPRINT_ERR(NETVSC, "Unknown send completion packet type - %d received!!", nvspPacket->Header.MessageType);
+       }
+
+       PutNetDevice(Device);
+       DPRINT_EXIT(NETVSC);
+}
+
+
+
+static int
+NetVscOnSend(
+       DEVICE_OBJECT *Device,
+       NETVSC_PACKET *Packet
+       )
+{
+       NETVSC_DEVICE* netDevice;
+       int ret=0;
+
+       NVSP_MESSAGE sendMessage;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = GetOutboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "net device (%p) shutting down...ignoring outbound packets", netDevice);
+               DPRINT_EXIT(NETVSC);
+               return -2;
+       }
+
+       sendMessage.Header.MessageType = NvspMessage1TypeSendRNDISPacket;
+       if (Packet->IsDataPacket)
+           sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 0;// 0 is RMC_DATA;
+       else
+               sendMessage.Messages.Version1Messages.SendRNDISPacket.ChannelType = 1;// 1 is RMC_CONTROL;
+
+       // Not using send buffer section
+    sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionIndex = 0xFFFFFFFF;
+    sendMessage.Messages.Version1Messages.SendRNDISPacket.SendBufferSectionSize = 0;
+
+       if (Packet->PageBufferCount)
+       {
+               ret = Device->Driver->VmbusChannelInterface.SendPacketPageBuffer(Device,
+                                                                                                                                                       Packet->PageBuffers,
+                                                                                                                                                       Packet->PageBufferCount,
+                                                                                                                                                       &sendMessage,
+                                                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                                                       (ULONG_PTR)Packet);
+       }
+       else
+       {
+               ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+                                                                                                                               &sendMessage,
+                                                                                                                               sizeof(NVSP_MESSAGE),
+                                                                                                                               (ULONG_PTR)Packet,
+                                                                                                                               VmbusPacketTypeDataInBand,
+                                                                                                                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+       }
+
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC, "Unable to send packet %p ret %d", Packet, ret);
+       }
+
+       InterlockedIncrement(&netDevice->NumOutstandingSends);
+       PutNetDevice(Device);
+
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+
+static void
+NetVscOnReceive(
+       DEVICE_OBJECT           *Device,
+       VMPACKET_DESCRIPTOR *Packet
+       )
+{
+       NETVSC_DEVICE* netDevice;
+       VMTRANSFER_PAGE_PACKET_HEADER *vmxferpagePacket;
+       NVSP_MESSAGE *nvspPacket;
+       NETVSC_PACKET *netvscPacket=NULL;
+       LIST_ENTRY* entry;
+       ULONG_PTR start;
+       ULONG_PTR end, endVirtual;
+       //NETVSC_DRIVER_OBJECT *netvscDriver;
+       XFERPAGE_PACKET *xferpagePacket=NULL;
+       LIST_ENTRY listHead;
+
+       int i=0, j=0;
+       int count=0, bytesRemain=0;
+
+       DPRINT_ENTER(NETVSC);
+
+       netDevice = GetInboundNetDevice(Device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "unable to get net device...device being destroyed?");
+               DPRINT_EXIT(NETVSC);
+               return;
+       }
+
+       // All inbound packets other than send completion should be xfer page packet
+       if (Packet->Type != VmbusPacketTypeDataUsingTransferPages)
+       {
+               DPRINT_ERR(NETVSC, "Unknown packet type received - %d", Packet->Type);
+               PutNetDevice(Device);
+               return;
+       }
+
+       nvspPacket = (NVSP_MESSAGE*)((ULONG_PTR)Packet + (Packet->DataOffset8 << 3));
+
+       // Make sure this is a valid nvsp packet
+       if (nvspPacket->Header.MessageType != NvspMessage1TypeSendRNDISPacket )
+       {
+               DPRINT_ERR(NETVSC, "Unknown nvsp packet type received - %d", nvspPacket->Header.MessageType);
+               PutNetDevice(Device);
+               return;
+       }
+
+       DPRINT_DBG(NETVSC, "NVSP packet received - type %d", nvspPacket->Header.MessageType);
+
+       vmxferpagePacket = (VMTRANSFER_PAGE_PACKET_HEADER*)Packet;
+
+       if (vmxferpagePacket->TransferPageSetId != NETVSC_RECEIVE_BUFFER_ID)
+       {
+               DPRINT_ERR(NETVSC, "Invalid xfer page set id - expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID, vmxferpagePacket->TransferPageSetId);
+               PutNetDevice(Device);
+               return;
+       }
+
+       DPRINT_DBG(NETVSC, "xfer page - range count %d", vmxferpagePacket->RangeCount);
+
+       INITIALIZE_LIST_HEAD(&listHead);
+
+       // Grab free packets (range count + 1) to represent this xfer page packet. +1 to represent
+       // the xfer page packet itself. We grab it here so that we know exactly how many we can fulfil
+       SpinlockAcquire(netDevice->ReceivePacketListLock);
+       while (!IsListEmpty(&netDevice->ReceivePacketList))
+       {
+               entry = REMOVE_HEAD_LIST(&netDevice->ReceivePacketList);
+               netvscPacket = CONTAINING_RECORD(entry, NETVSC_PACKET, ListEntry);
+
+               INSERT_TAIL_LIST(&listHead, &netvscPacket->ListEntry);
+
+               if (++count == vmxferpagePacket->RangeCount + 1)
+                       break;
+       }
+       SpinlockRelease(netDevice->ReceivePacketListLock);
+
+       // We need at least 2 netvsc pkts (1 to represent the xfer page and at least 1 for the range)
+       // i.e. we can handled some of the xfer page packet ranges...
+       if (count < 2)
+       {
+               DPRINT_ERR(NETVSC, "Got only %d netvsc pkt...needed %d pkts. Dropping this xfer page packet completely!", count, vmxferpagePacket->RangeCount+1);
+
+               // Return it to the freelist
+               SpinlockAcquire(netDevice->ReceivePacketListLock);
+               for (i=count; i != 0; i--)
+               {
+                       entry = REMOVE_HEAD_LIST(&listHead);
+                       netvscPacket = CONTAINING_RECORD(entry, NETVSC_PACKET, ListEntry);
+
+                       INSERT_TAIL_LIST(&netDevice->ReceivePacketList, &netvscPacket->ListEntry);
+               }
+               SpinlockRelease(netDevice->ReceivePacketListLock);
+
+               NetVscSendReceiveCompletion(Device, vmxferpagePacket->d.TransactionId);
+
+               PutNetDevice(Device);
+               return;
+       }
+
+       // Remove the 1st packet to represent the xfer page packet itself
+       entry = REMOVE_HEAD_LIST(&listHead);
+       xferpagePacket = CONTAINING_RECORD(entry, XFERPAGE_PACKET, ListEntry);
+       xferpagePacket->Count = count - 1; // This is how much we can satisfy
+       ASSERT(xferpagePacket->Count > 0 && xferpagePacket->Count <= vmxferpagePacket->RangeCount);
+
+       if (xferpagePacket->Count != vmxferpagePacket->RangeCount)
+       {
+               DPRINT_INFO(NETVSC, "Needed %d netvsc pkts to satisy this xfer page...got %d", vmxferpagePacket->RangeCount, xferpagePacket->Count);
+       }
+
+       // Each range represents 1 RNDIS pkt that contains 1 ethernet frame
+       for (i=0; i < (count - 1); i++)
+       {
+               entry = REMOVE_HEAD_LIST(&listHead);
+               netvscPacket = CONTAINING_RECORD(entry, NETVSC_PACKET, ListEntry);
+
+               // Initialize the netvsc packet
+               netvscPacket->XferPagePacket = xferpagePacket;
+               netvscPacket->Completion.Recv.OnReceiveCompletion = NetVscOnReceiveCompletion;
+               netvscPacket->Completion.Recv.ReceiveCompletionContext = netvscPacket;
+               netvscPacket->Device = Device;
+               netvscPacket->Completion.Recv.ReceiveCompletionTid = vmxferpagePacket->d.TransactionId; // Save this so that we can send it back
+
+               netvscPacket->TotalDataBufferLength = vmxferpagePacket->Ranges[i].ByteCount;
+               netvscPacket->PageBufferCount = 1;
+
+               ASSERT(vmxferpagePacket->Ranges[i].ByteOffset + vmxferpagePacket->Ranges[i].ByteCount < netDevice->ReceiveBufferSize);
+
+               netvscPacket->PageBuffers[0].Length = vmxferpagePacket->Ranges[i].ByteCount;
+
+               start = GetPhysicalAddress((void*)((ULONG_PTR)netDevice->ReceiveBuffer + vmxferpagePacket->Ranges[i].ByteOffset));
+
+               netvscPacket->PageBuffers[0].Pfn = start >> PAGE_SHIFT;
+               endVirtual = (ULONG_PTR)netDevice->ReceiveBuffer
+                   + vmxferpagePacket->Ranges[i].ByteOffset
+                   + vmxferpagePacket->Ranges[i].ByteCount -1;
+               end = GetPhysicalAddress((void*)endVirtual);
+
+               // Calculate the page relative offset
+               netvscPacket->PageBuffers[0].Offset = vmxferpagePacket->Ranges[i].ByteOffset & (PAGE_SIZE -1);
+               if ((end >> PAGE_SHIFT) != (start>>PAGE_SHIFT)) {
+                   //Handle frame across multiple pages:
+                   netvscPacket->PageBuffers[0].Length =
+                       (netvscPacket->PageBuffers[0].Pfn <<PAGE_SHIFT) + PAGE_SIZE - start;
+                   bytesRemain = netvscPacket->TotalDataBufferLength - netvscPacket->PageBuffers[0].Length;
+                   for (j=1; j<NETVSC_PACKET_MAXPAGE; j++) {
+                       netvscPacket->PageBuffers[j].Offset = 0;
+                       if (bytesRemain <= PAGE_SIZE) {
+                           netvscPacket->PageBuffers[j].Length = bytesRemain;
+                           bytesRemain = 0;
+                       } else {
+                           netvscPacket->PageBuffers[j].Length = PAGE_SIZE;
+                           bytesRemain -= PAGE_SIZE;
+                       }
+                       netvscPacket->PageBuffers[j].Pfn =
+                           GetPhysicalAddress((void*)(endVirtual - bytesRemain)) >> PAGE_SHIFT;
+                       netvscPacket->PageBufferCount++;
+                       if (bytesRemain == 0)
+                           break;
+                   }
+                   ASSERT(bytesRemain == 0);
+               }
+               DPRINT_DBG(NETVSC, "[%d] - (abs offset %u len %u) => (pfn %llx, offset %u, len %u)",
+                       i,
+                       vmxferpagePacket->Ranges[i].ByteOffset,
+                       vmxferpagePacket->Ranges[i].ByteCount,
+                       netvscPacket->PageBuffers[0].Pfn,
+                       netvscPacket->PageBuffers[0].Offset,
+                       netvscPacket->PageBuffers[0].Length);
+
+               // Pass it to the upper layer
+               ((NETVSC_DRIVER_OBJECT*)Device->Driver)->OnReceiveCallback(Device, netvscPacket);
+
+               NetVscOnReceiveCompletion(netvscPacket->Completion.Recv.ReceiveCompletionContext);
+       }
+
+       ASSERT(IsListEmpty(&listHead));
+
+       PutNetDevice(Device);
+       DPRINT_EXIT(NETVSC);
+}
+
+
+static void
+NetVscSendReceiveCompletion(
+       DEVICE_OBJECT   *Device,
+       UINT64                  TransactionId
+       )
+{
+       NVSP_MESSAGE recvcompMessage;
+       int retries=0;
+       int ret=0;
+
+       DPRINT_DBG(NETVSC, "Sending receive completion pkt - %llx", TransactionId);
+
+       recvcompMessage.Header.MessageType = NvspMessage1TypeSendRNDISPacketComplete;
+
+       // FIXME: Pass in the status
+       recvcompMessage.Messages.Version1Messages.SendRNDISPacketComplete.Status = NvspStatusSuccess;
+
+retry_send_cmplt:
+       // Send the completion
+       ret = Device->Driver->VmbusChannelInterface.SendPacket(Device,
+                                                                                                                       &recvcompMessage,
+                                                                                                                       sizeof(NVSP_MESSAGE),
+                                                                                                                       TransactionId,
+                                                                                                                       VmbusPacketTypeCompletion,
+                                                                                                                       0);
+       if (ret == 0) // success
+       {
+               // no-op
+       }
+       else if (ret == -1) // no more room...wait a bit and attempt to retry 3 times
+       {
+               retries++;
+               DPRINT_ERR(NETVSC, "unable to send receive completion pkt (tid %llx)...retrying %d", TransactionId, retries);
+
+               if (retries < 4)
+               {
+                       Sleep(100);
+                       goto retry_send_cmplt;
+               }
+               else
+               {
+                       DPRINT_ERR(NETVSC, "unable to send receive completion pkt (tid %llx)...give up retrying", TransactionId);
+               }
+       }
+       else
+       {
+               DPRINT_ERR(NETVSC, "unable to send receive completion pkt - %llx", TransactionId);
+       }
+}
+
+//
+// Send a receive completion packet to RNDIS device (ie NetVsp)
+//
+static void
+NetVscOnReceiveCompletion(
+       PVOID Context)
+{
+       NETVSC_PACKET *packet = (NETVSC_PACKET*)Context;
+       DEVICE_OBJECT *device = (DEVICE_OBJECT*)packet->Device;
+       NETVSC_DEVICE* netDevice;
+       UINT64  transactionId=0;
+       BOOL fSendReceiveComp = FALSE;
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(packet->XferPagePacket);
+
+       // Even though it seems logical to do a GetOutboundNetDevice() here to send out receive completion,
+       // we are using GetInboundNetDevice() since we may have disable outbound traffic already.
+       netDevice = GetInboundNetDevice(device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "unable to get net device...device being destroyed?");
+               DPRINT_EXIT(NETVSC);
+               return;
+       }
+
+       // Overloading use of the lock.
+       SpinlockAcquire(netDevice->ReceivePacketListLock);
+
+       ASSERT(packet->XferPagePacket->Count > 0);
+       packet->XferPagePacket->Count--;
+
+       // Last one in the line that represent 1 xfer page packet.
+       // Return the xfer page packet itself to the freelist
+       if (packet->XferPagePacket->Count == 0)
+       {
+               fSendReceiveComp = TRUE;
+               transactionId = packet->Completion.Recv.ReceiveCompletionTid;
+
+               INSERT_TAIL_LIST(&netDevice->ReceivePacketList, &packet->XferPagePacket->ListEntry);
+       }
+
+       // Put the packet back
+       INSERT_TAIL_LIST(&netDevice->ReceivePacketList, &packet->ListEntry);
+       SpinlockRelease(netDevice->ReceivePacketListLock);
+
+       // Send a receive completion for the xfer page packet
+       if (fSendReceiveComp)
+       {
+               NetVscSendReceiveCompletion(device, transactionId);
+       }
+
+       PutNetDevice(device);
+       DPRINT_EXIT(NETVSC);
+}
+
+
+
+void
+NetVscOnChannelCallback(
+       PVOID Context
+       )
+{
+       const int netPacketSize=2048;
+       int ret=0;
+       DEVICE_OBJECT *device=(DEVICE_OBJECT*)Context;
+       NETVSC_DEVICE *netDevice;
+
+       UINT32 bytesRecvd;
+       UINT64 requestId;
+       UCHAR packet[netPacketSize];
+       VMPACKET_DESCRIPTOR *desc;
+       UCHAR   *buffer=packet;
+       int             bufferlen=netPacketSize;
+
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(device);
+
+       netDevice = GetInboundNetDevice(device);
+       if (!netDevice)
+       {
+               DPRINT_ERR(NETVSC, "net device (%p) shutting down...ignoring inbound packets", netDevice);
+               DPRINT_EXIT(NETVSC);
+               return;
+       }
+
+       do
+       {
+               ret = device->Driver->VmbusChannelInterface.RecvPacketRaw(device,
+                                                                                                                                       buffer,
+                                                                                                                                       bufferlen,
+                                                                                                                                       &bytesRecvd,
+                                                                                                                                       &requestId);
+
+               if (ret == 0)
+               {
+                       if (bytesRecvd > 0)
+                       {
+                               DPRINT_DBG(NETVSC, "receive %d bytes, tid %llx", bytesRecvd, requestId);
+
+                               desc = (VMPACKET_DESCRIPTOR*)buffer;
+                               switch (desc->Type)
+                               {
+                                       case VmbusPacketTypeCompletion:
+                                               NetVscOnSendCompletion(device, desc);
+                                               break;
+
+                                       case VmbusPacketTypeDataUsingTransferPages:
+                                               NetVscOnReceive(device, desc);
+                                               break;
+
+                                       default:
+                                               DPRINT_ERR(NETVSC, "unhandled packet type %d, tid %llx len %d\n", desc->Type, requestId, bytesRecvd);
+                                               break;
+                               }
+
+                               // reset
+                               if (bufferlen > netPacketSize)
+                               {
+                                       MemFree(buffer);
+
+                                       buffer = packet;
+                                       bufferlen = netPacketSize;
+                               }
+                       }
+                       else
+                       {
+                               //DPRINT_DBG(NETVSC, "nothing else to read...");
+
+                               // reset
+                               if (bufferlen > netPacketSize)
+                               {
+                                       MemFree(buffer);
+
+                                       buffer = packet;
+                                       bufferlen = netPacketSize;
+                               }
+
+                               break;
+                       }
+               }
+               else if (ret == -2) // Handle large packet
+               {
+                       buffer = MemAllocAtomic(bytesRecvd);
+                       if (buffer == NULL)
+                       {
+                               // Try again next time around
+                               DPRINT_ERR(NETVSC, "unable to allocate buffer of size (%d)!!", bytesRecvd);
+                               break;
+                       }
+
+                       bufferlen = bytesRecvd;
+               }
+               else
+               {
+                       ASSERT(0);
+               }
+       } while (1);
+
+       PutNetDevice(device);
+       DPRINT_EXIT(NETVSC);
+       return;
+}
diff --git a/drivers/staging/hv/NetVsc.h b/drivers/staging/hv/NetVsc.h
new file mode 100644 (file)
index 0000000..37d0744
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright (c) 2009, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ *   Hank Janssen  <hjanssen@microsoft.com>
+ *
+ */
+
+
+#ifndef _NETVSC_H_
+#define _NETVSC_H_
+
+#include "VmbusPacketFormat.h"
+#include "nvspprotocol.h"
+
+#include "List.h"
+
+#include "NetVscApi.h"
+//
+// #defines
+//
+//#define NVSC_MIN_PROTOCOL_VERSION                       1
+//#define NVSC_MAX_PROTOCOL_VERSION                       1
+
+#define NETVSC_SEND_BUFFER_SIZE                                64*1024 // 64K
+#define NETVSC_SEND_BUFFER_ID                          0xface
+
+
+#define NETVSC_RECEIVE_BUFFER_SIZE                     1024*1024 // 1MB
+
+#define NETVSC_RECEIVE_BUFFER_ID                       0xcafe
+
+#define NETVSC_RECEIVE_SG_COUNT                                1
+
+// Preallocated receive packets
+#define NETVSC_RECEIVE_PACKETLIST_COUNT                256
+
+//
+// Data types
+//
+
+// Per netvsc channel-specific
+typedef struct _NETVSC_DEVICE {
+       DEVICE_OBJECT                                   *Device;
+
+       int                                                             RefCount;
+
+       int                                                             NumOutstandingSends;
+       // List of free preallocated NETVSC_PACKET to represent receive packet
+       LIST_ENTRY                                              ReceivePacketList;
+       HANDLE                                                  ReceivePacketListLock;
+
+       // Send buffer allocated by us but manages by NetVSP
+       PVOID                                                   SendBuffer;
+       UINT32                                                  SendBufferSize;
+       UINT32                                                  SendBufferGpadlHandle;
+       UINT32                                                  SendSectionSize;
+
+       // Receive buffer allocated by us but manages by NetVSP
+       PVOID                                                   ReceiveBuffer;
+       UINT32                                                  ReceiveBufferSize;
+       UINT32                                                  ReceiveBufferGpadlHandle;
+       UINT32                                                  ReceiveSectionCount;
+       PNVSP_1_RECEIVE_BUFFER_SECTION  ReceiveSections;
+
+       // Used for NetVSP initialization protocol
+       HANDLE                                                  ChannelInitEvent;
+       NVSP_MESSAGE                                    ChannelInitPacket;
+
+       NVSP_MESSAGE                                    RevokePacket;
+       //UCHAR                                                 HwMacAddr[HW_MACADDR_LEN];
+
+       // Holds rndis device info
+       void                                                    *Extension;
+} NETVSC_DEVICE;
+
+#endif // _NETVSC_H_
diff --git a/drivers/staging/hv/RndisFilter.c b/drivers/staging/hv/RndisFilter.c
new file mode 100644 (file)
index 0000000..57b828b
--- /dev/null
@@ -0,0 +1,1162 @@
+/*
+ *
+ * Copyright (c) 2009, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ *   Haiyang Zhang <haiyangz@microsoft.com>
+ *   Hank Janssen  <hjanssen@microsoft.com>
+ *
+ */
+
+
+#include "logging.h"
+
+#include "NetVscApi.h"
+#include "RndisFilter.h"
+
+//
+// Data types
+//
+
+typedef struct _RNDIS_FILTER_DRIVER_OBJECT {
+       // The original driver
+       NETVSC_DRIVER_OBJECT            InnerDriver;
+
+} RNDIS_FILTER_DRIVER_OBJECT;
+
+typedef enum {
+       RNDIS_DEV_UNINITIALIZED = 0,
+       RNDIS_DEV_INITIALIZING,
+       RNDIS_DEV_INITIALIZED,
+       RNDIS_DEV_DATAINITIALIZED,
+} RNDIS_DEVICE_STATE;
+
+typedef struct _RNDIS_DEVICE {
+       NETVSC_DEVICE                   *NetDevice;
+
+       RNDIS_DEVICE_STATE              State;
+       UINT32                                  LinkStatus;
+       UINT32                                  NewRequestId;
+
+       HANDLE                                  RequestLock;
+       LIST_ENTRY                              RequestList;
+
+       UCHAR                                   HwMacAddr[HW_MACADDR_LEN];
+} RNDIS_DEVICE;
+
+
+typedef struct _RNDIS_REQUEST {
+       LIST_ENTRY                                      ListEntry;
+       HANDLE                                          WaitEvent;
+
+       // FIXME: We assumed a fixed size response here. If we do ever need to handle a bigger response,
+       // we can either define a max response message or add a response buffer variable above this field
+       RNDIS_MESSAGE                           ResponseMessage;
+
+       // Simplify allocation by having a netvsc packet inline
+       NETVSC_PACKET                           Packet;
+       PAGE_BUFFER                                     Buffer;
+       // FIXME: We assumed a fixed size request here.
+       RNDIS_MESSAGE                           RequestMessage;
+} RNDIS_REQUEST;
+
+
+typedef struct _RNDIS_FILTER_PACKET {
+       void                                            *CompletionContext;
+       PFN_ON_SENDRECVCOMPLETION       OnCompletion;
+
+       RNDIS_MESSAGE                           Message;
+} RNDIS_FILTER_PACKET;
+
+//
+// Internal routines
+//
+static int
+RndisFilterSendRequest(
+       RNDIS_DEVICE    *Device,
+       RNDIS_REQUEST   *Request
+       );
+
+static void
+RndisFilterReceiveResponse(
+       RNDIS_DEVICE    *Device,
+       RNDIS_MESSAGE   *Response
+       );
+
+static void
+RndisFilterReceiveIndicateStatus(
+       RNDIS_DEVICE    *Device,
+       RNDIS_MESSAGE   *Response
+       );
+
+static void
+RndisFilterReceiveData(
+       RNDIS_DEVICE    *Device,
+       RNDIS_MESSAGE   *Message,
+       NETVSC_PACKET   *Packet
+       );
+
+static int
+RndisFilterOnReceive(
+       DEVICE_OBJECT           *Device,
+       NETVSC_PACKET           *Packet
+       );
+
+static int
+RndisFilterQueryDevice(
+       RNDIS_DEVICE    *Device,
+       UINT32                  Oid,
+       VOID                    *Result,
+       UINT32                  *ResultSize
+       );
+
+static inline int
+RndisFilterQueryDeviceMac(
+       RNDIS_DEVICE    *Device
+       );
+
+static inline int
+RndisFilterQueryDeviceLinkStatus(
+       RNDIS_DEVICE    *Device
+       );
+
+static int
+RndisFilterSetPacketFilter(
+       RNDIS_DEVICE    *Device,
+       UINT32                  NewFilter
+       );
+
+static int
+RndisFilterInitDevice(
+       RNDIS_DEVICE            *Device
+       );
+
+static int
+RndisFilterOpenDevice(
+       RNDIS_DEVICE            *Device
+       );
+
+static int
+RndisFilterCloseDevice(
+       RNDIS_DEVICE            *Device
+       );
+
+static int
+RndisFilterOnDeviceAdd(
+       DEVICE_OBJECT   *Device,
+       void                    *AdditionalInfo
+       );
+
+static int
+RndisFilterOnDeviceRemove(
+       DEVICE_OBJECT *Device
+       );
+
+static void
+RndisFilterOnCleanup(
+       DRIVER_OBJECT *Driver
+       );
+
+static int
+RndisFilterOnOpen(
+       DEVICE_OBJECT           *Device
+       );
+
+static int
+RndisFilterOnClose(
+       DEVICE_OBJECT           *Device
+       );
+
+static int
+RndisFilterOnSend(
+       DEVICE_OBJECT           *Device,
+       NETVSC_PACKET           *Packet
+       );
+
+static void
+RndisFilterOnSendCompletion(
+   void *Context
+       );
+
+static void
+RndisFilterOnSendRequestCompletion(
+   void *Context
+       );
+
+//
+// Global var
+//
+
+// The one and only
+RNDIS_FILTER_DRIVER_OBJECT gRndisFilter;
+
+static inline RNDIS_DEVICE* GetRndisDevice(void)
+{
+       RNDIS_DEVICE *device;
+
+       device = MemAllocZeroed(sizeof(RNDIS_DEVICE));
+       if (!device)
+       {
+               return NULL;
+       }
+
+       device->RequestLock = SpinlockCreate();
+       if (!device->RequestLock)
+       {
+               MemFree(device);
+               return NULL;
+       }
+
+       INITIALIZE_LIST_HEAD(&device->RequestList);
+
+       device->State = RNDIS_DEV_UNINITIALIZED;
+
+       return device;
+}
+
+static inline void PutRndisDevice(RNDIS_DEVICE *Device)
+{
+       SpinlockClose(Device->RequestLock);
+       MemFree(Device);
+}
+
+static inline RNDIS_REQUEST* GetRndisRequest(RNDIS_DEVICE *Device, UINT32 MessageType, UINT32 MessageLength)
+{
+       RNDIS_REQUEST *request;
+       RNDIS_MESSAGE *rndisMessage;
+       RNDIS_SET_REQUEST *set;
+
+       request = MemAllocZeroed(sizeof(RNDIS_REQUEST));
+       if (!request)
+       {
+               return NULL;
+       }
+
+       request->WaitEvent = WaitEventCreate();
+       if (!request->WaitEvent)
+       {
+               MemFree(request);
+               return NULL;
+       }
+
+       rndisMessage = &request->RequestMessage;
+       rndisMessage->NdisMessageType = MessageType;
+       rndisMessage->MessageLength = MessageLength;
+
+       // Set the request id. This field is always after the rndis header for request/response packet types so
+       // we just used the SetRequest as a template
+       set = &rndisMessage->Message.SetRequest;
+       set->RequestId = InterlockedIncrement((int*)&Device->NewRequestId);
+
+       // Add to the request list
+       SpinlockAcquire(Device->RequestLock);
+       INSERT_TAIL_LIST(&Device->RequestList, &request->ListEntry);
+       SpinlockRelease(Device->RequestLock);
+
+       return request;
+}
+
+static inline void PutRndisRequest(RNDIS_DEVICE *Device, RNDIS_REQUEST *Request)
+{
+       SpinlockAcquire(Device->RequestLock);
+       REMOVE_ENTRY_LIST(&Request->ListEntry);
+       SpinlockRelease(Device->RequestLock);
+
+       WaitEventClose(Request->WaitEvent);
+       MemFree(Request);
+}
+
+static inline void DumpRndisMessage(RNDIS_MESSAGE *RndisMessage)
+{
+       switch (RndisMessage->NdisMessageType)
+       {
+       case REMOTE_NDIS_PACKET_MSG:
+               DPRINT_DBG(NETVSC, "REMOTE_NDIS_PACKET_MSG (len %u, data offset %u data len %u, # oob %u, oob offset %u, oob len %u, pkt offset %u, pkt len %u",
+                       RndisMessage->MessageLength,
+                       RndisMessage->Message.Packet.DataOffset,
+                       RndisMessage->Message.Packet.DataLength,
+                       RndisMessage->Message.Packet.NumOOBDataElements,
+                       RndisMessage->Message.Packet.OOBDataOffset,
+                       RndisMessage->Message.Packet.OOBDataLength,
+                       RndisMessage->Message.Packet.PerPacketInfoOffset,
+                       RndisMessage->Message.Packet.PerPacketInfoLength);
+               break;
+
+       case REMOTE_NDIS_INITIALIZE_CMPLT:
+               DPRINT_DBG(NETVSC, "REMOTE_NDIS_INITIALIZE_CMPLT (len %u, id 0x%x, status 0x%x, major %d, minor %d, device flags %d, max xfer size 0x%x, max pkts %u, pkt aligned %u)",
+                       RndisMessage->MessageLength,
+                       RndisMessage->Message.InitializeComplete.RequestId,
+                       RndisMessage->Message.InitializeComplete.Status,
+                       RndisMessage->Message.InitializeComplete.MajorVersion,
+                       RndisMessage->Message.InitializeComplete.MinorVersion,
+                       RndisMessage->Message.InitializeComplete.DeviceFlags,
+                       RndisMessage->Message.InitializeComplete.MaxTransferSize,
+                       RndisMessage->Message.InitializeComplete.MaxPacketsPerMessage,
+                       RndisMessage->Message.InitializeComplete.PacketAlignmentFactor);
+               break;
+
+       case REMOTE_NDIS_QUERY_CMPLT:
+               DPRINT_DBG(NETVSC, "REMOTE_NDIS_QUERY_CMPLT (len %u, id 0x%x, status 0x%x, buf len %u, buf offset %u)",
+                       RndisMessage->MessageLength,
+                       RndisMessage->Message.QueryComplete.RequestId,
+                       RndisMessage->Message.QueryComplete.Status,
+                       RndisMessage->Message.QueryComplete.InformationBufferLength,
+                       RndisMessage->Message.QueryComplete.InformationBufferOffset);
+               break;
+
+       case REMOTE_NDIS_SET_CMPLT:
+               DPRINT_DBG(NETVSC, "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)",
+                       RndisMessage->MessageLength,
+                       RndisMessage->Message.SetComplete.RequestId,
+                       RndisMessage->Message.SetComplete.Status);
+               break;
+
+       case REMOTE_NDIS_INDICATE_STATUS_MSG:
+               DPRINT_DBG(NETVSC, "REMOTE_NDIS_INDICATE_STATUS_MSG (len %u, status 0x%x, buf len %u, buf offset %u)",
+                       RndisMessage->MessageLength,
+                       RndisMessage->Message.IndicateStatus.Status,
+                       RndisMessage->Message.IndicateStatus.StatusBufferLength,
+                       RndisMessage->Message.IndicateStatus.StatusBufferOffset);
+               break;
+
+       default:
+               DPRINT_DBG(NETVSC, "0x%x (len %u)",
+                       RndisMessage->NdisMessageType,
+                       RndisMessage->MessageLength);
+               break;
+       }
+}
+
+static int
+RndisFilterSendRequest(
+       RNDIS_DEVICE    *Device,
+       RNDIS_REQUEST   *Request
+       )
+{
+       int ret=0;
+       NETVSC_PACKET *packet;
+
+       DPRINT_ENTER(NETVSC);
+
+       // Setup the packet to send it
+       packet = &Request->Packet;
+
+       packet->IsDataPacket = FALSE;
+       packet->TotalDataBufferLength = Request->RequestMessage.MessageLength;
+       packet->PageBufferCount = 1;
+
+       packet->PageBuffers[0].Pfn = GetPhysicalAddress(&Request->RequestMessage) >> PAGE_SHIFT;
+       packet->PageBuffers[0].Length = Request->RequestMessage.MessageLength;
+       packet->PageBuffers[0].Offset = (ULONG_PTR)&Request->RequestMessage & (PAGE_SIZE -1);
+
+       packet->Completion.Send.SendCompletionContext = Request;//packet;
+       packet->Completion.Send.OnSendCompletion = RndisFilterOnSendRequestCompletion;
+       packet->Completion.Send.SendCompletionTid = (ULONG_PTR)Device;
+
+       ret = gRndisFilter.InnerDriver.OnSend(Device->NetDevice->Device, packet);
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+
+static void
+RndisFilterReceiveResponse(
+       RNDIS_DEVICE    *Device,
+       RNDIS_MESSAGE   *Response
+       )
+{
+       LIST_ENTRY *anchor;
+       LIST_ENTRY *curr;
+       RNDIS_REQUEST *request=NULL;
+       BOOL found=FALSE;
+
+       DPRINT_ENTER(NETVSC);
+
+       SpinlockAcquire(Device->RequestLock);
+       ITERATE_LIST_ENTRIES(anchor, curr, &Device->RequestList)
+       {
+               request = CONTAINING_RECORD(curr, RNDIS_REQUEST, ListEntry);
+
+               // All request/response message contains RequestId as the 1st field
+               if (request->RequestMessage.Message.InitializeRequest.RequestId == Response->Message.InitializeComplete.RequestId)
+               {
+                       DPRINT_DBG(NETVSC, "found rndis request for this response (id 0x%x req type 0x%x res type 0x%x)",
+                               request->RequestMessage.Message.InitializeRequest.RequestId, request->RequestMessage.NdisMessageType, Response->NdisMessageType);
+
+                       found = TRUE;
+                       break;
+               }
+       }
+       SpinlockRelease(Device->RequestLock);
+
+       if (found)
+       {
+               if (Response->MessageLength <= sizeof(RNDIS_MESSAGE))
+               {
+                       memcpy(&request->ResponseMessage, Response, Response->MessageLength);
+               }
+               else
+               {
+                       DPRINT_ERR(NETVSC, "rndis response buffer overflow detected (size %u max %u)", Response->MessageLength, sizeof(RNDIS_FILTER_PACKET));
+
+                       if (Response->NdisMessageType == REMOTE_NDIS_RESET_CMPLT) // does not have a request id field
+                       {
+                               request->ResponseMessage.Message.ResetComplete.Status = STATUS_BUFFER_OVERFLOW;
+                       }
+                       else
+                       {
+                               request->ResponseMessage.Message.InitializeComplete.Status = STATUS_BUFFER_OVERFLOW;
+                       }
+               }
+
+               WaitEventSet(request->WaitEvent);
+       }
+       else
+       {
+               DPRINT_ERR(NETVSC, "no rndis request found for this response (id 0x%x res type 0x%x)",
+                               Response->Message.InitializeComplete.RequestId, Response->NdisMessageType);
+       }
+
+       DPRINT_EXIT(NETVSC);
+}
+
+static void
+RndisFilterReceiveIndicateStatus(
+       RNDIS_DEVICE    *Device,
+       RNDIS_MESSAGE   *Response
+       )
+{
+       RNDIS_INDICATE_STATUS *indicate = &Response->Message.IndicateStatus;
+
+       if (indicate->Status == RNDIS_STATUS_MEDIA_CONNECT)
+       {
+               gRndisFilter.InnerDriver.OnLinkStatusChanged(Device->NetDevice->Device, 1);
+       }
+       else if (indicate->Status == RNDIS_STATUS_MEDIA_DISCONNECT)
+       {
+               gRndisFilter.InnerDriver.OnLinkStatusChanged(Device->NetDevice->Device, 0);
+       }
+       else
+       {
+               // TODO:
+       }
+}
+
+static void
+RndisFilterReceiveData(
+       RNDIS_DEVICE    *Device,
+       RNDIS_MESSAGE   *Message,
+       NETVSC_PACKET   *Packet
+       )
+{
+       RNDIS_PACKET *rndisPacket;
+       UINT32 dataOffset;
+
+       DPRINT_ENTER(NETVSC);
+
+       // empty ethernet frame ??
+       ASSERT(Packet->PageBuffers[0].Length > RNDIS_MESSAGE_SIZE(RNDIS_PACKET));
+
+       rndisPacket = &Message->Message.Packet;
+
+       // FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this
+       // netvsc packet (ie TotalDataBufferLength != MessageLength)
+
+       // Remove the rndis header and pass it back up the stack
+       dataOffset = RNDIS_HEADER_SIZE + rndisPacket->DataOffset;
+
+       Packet->TotalDataBufferLength -= dataOffset;
+       Packet->PageBuffers[0].Offset += dataOffset;
+       Packet->PageBuffers[0].Length -= dataOffset;
+
+       Packet->IsDataPacket = TRUE;
+
+       gRndisFilter.InnerDriver.OnReceiveCallback(Device->NetDevice->Device, Packet);
+
+       DPRINT_EXIT(NETVSC);
+}
+
+static int
+RndisFilterOnReceive(
+       DEVICE_OBJECT           *Device,
+       NETVSC_PACKET           *Packet
+       )
+{
+       NETVSC_DEVICE *netDevice = (NETVSC_DEVICE*)Device->Extension;
+       RNDIS_DEVICE *rndisDevice;
+       RNDIS_MESSAGE rndisMessage;
+       RNDIS_MESSAGE *rndisHeader;
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(netDevice);
+       //Make sure the rndis device state is initialized
+       if (!netDevice->Extension)
+       {
+               DPRINT_ERR(NETVSC, "got rndis message but no rndis device...dropping this message!");
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+
+       rndisDevice = (RNDIS_DEVICE*)netDevice->Extension;
+       if (rndisDevice->State == RNDIS_DEV_UNINITIALIZED)
+       {
+               DPRINT_ERR(NETVSC, "got rndis message but rndis device uninitialized...dropping this message!");
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+
+       rndisHeader = (RNDIS_MESSAGE*)PageMapVirtualAddress(Packet->PageBuffers[0].Pfn);
+
+       rndisHeader = (void*)((ULONG_PTR)rndisHeader + Packet->PageBuffers[0].Offset);
+
+       // Make sure we got a valid rndis message
+       // FIXME: There seems to be a bug in set completion msg where its MessageLength is 16 bytes but
+       // the ByteCount field in the xfer page range shows 52 bytes
+#if 0
+       if ( Packet->TotalDataBufferLength != rndisHeader->MessageLength )
+       {
+               PageUnmapVirtualAddress((void*)(ULONG_PTR)rndisHeader - Packet->PageBuffers[0].Offset);
+
+               DPRINT_ERR(NETVSC, "invalid rndis message? (expected %u bytes got %u)...dropping this message!",
+                       rndisHeader->MessageLength, Packet->TotalDataBufferLength);
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+#endif
+
+       if ((rndisHeader->NdisMessageType != REMOTE_NDIS_PACKET_MSG) && (rndisHeader->MessageLength > sizeof(RNDIS_MESSAGE)))
+       {
+               DPRINT_ERR(NETVSC, "incoming rndis message buffer overflow detected (got %u, max %u)...marking it an error!",
+                       rndisHeader->MessageLength, sizeof(RNDIS_MESSAGE));
+       }
+
+       memcpy(&rndisMessage, rndisHeader, (rndisHeader->MessageLength > sizeof(RNDIS_MESSAGE))?sizeof(RNDIS_MESSAGE):rndisHeader->MessageLength);
+
+       PageUnmapVirtualAddress((void*)(ULONG_PTR)rndisHeader - Packet->PageBuffers[0].Offset);
+
+       DumpRndisMessage(&rndisMessage);
+
+       switch (rndisMessage.NdisMessageType)
+       {
+               // data msg
+       case REMOTE_NDIS_PACKET_MSG:
+               RndisFilterReceiveData(rndisDevice, &rndisMessage, Packet);
+               break;
+
+               // completion msgs
+       case REMOTE_NDIS_INITIALIZE_CMPLT:
+       case REMOTE_NDIS_QUERY_CMPLT:
+       case REMOTE_NDIS_SET_CMPLT:
+       //case REMOTE_NDIS_RESET_CMPLT:
+       //case REMOTE_NDIS_KEEPALIVE_CMPLT:
+               RndisFilterReceiveResponse(rndisDevice, &rndisMessage);
+               break;
+
+               // notification msgs
+       case REMOTE_NDIS_INDICATE_STATUS_MSG:
+               RndisFilterReceiveIndicateStatus(rndisDevice, &rndisMessage);
+               break;
+       default:
+               DPRINT_ERR(NETVSC, "unhandled rndis message (type %u len %u)", rndisMessage.NdisMessageType, rndisMessage.MessageLength);
+               break;
+       }
+
+       DPRINT_EXIT(NETVSC);
+       return 0;
+}
+
+
+static int
+RndisFilterQueryDevice(
+       RNDIS_DEVICE    *Device,
+       UINT32                  Oid,
+       VOID                    *Result,
+       UINT32                  *ResultSize
+       )
+{
+       RNDIS_REQUEST *request;
+       UINT32 inresultSize = *ResultSize;
+       RNDIS_QUERY_REQUEST *query;
+       RNDIS_QUERY_COMPLETE *queryComplete;
+       int ret=0;
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(Result);
+
+       *ResultSize = 0;
+       request = GetRndisRequest(Device, REMOTE_NDIS_QUERY_MSG, RNDIS_MESSAGE_SIZE(RNDIS_QUERY_REQUEST));
+       if (!request)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       // Setup the rndis query
+       query = &request->RequestMessage.Message.QueryRequest;
+       query->Oid = Oid;
+       query->InformationBufferOffset = sizeof(RNDIS_QUERY_REQUEST);
+       query->InformationBufferLength = 0;
+       query->DeviceVcHandle = 0;
+
+       ret = RndisFilterSendRequest(Device, request);
+       if (ret != 0)
+       {
+               goto Cleanup;
+       }
+
+       WaitEventWait(request->WaitEvent);
+
+       // Copy the response back
+       queryComplete = &request->ResponseMessage.Message.QueryComplete;
+
+       if (queryComplete->InformationBufferLength > inresultSize)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       memcpy(Result,
+                       (void*)((ULONG_PTR)queryComplete + queryComplete->InformationBufferOffset),
+                       queryComplete->InformationBufferLength);
+
+       *ResultSize = queryComplete->InformationBufferLength;
+
+Cleanup:
+       if (request)
+       {
+               PutRndisRequest(Device, request);
+       }
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+static inline int
+RndisFilterQueryDeviceMac(
+       RNDIS_DEVICE    *Device
+       )
+{
+       UINT32 size=HW_MACADDR_LEN;
+
+       return RndisFilterQueryDevice(Device,
+                                                                       RNDIS_OID_802_3_PERMANENT_ADDRESS,
+                                                                       Device->HwMacAddr,
+                                                                       &size);
+}
+
+static inline int
+RndisFilterQueryDeviceLinkStatus(
+       RNDIS_DEVICE    *Device
+       )
+{
+       UINT32 size=sizeof(UINT32);
+
+       return RndisFilterQueryDevice(Device,
+                                                                       RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
+                                                                       &Device->LinkStatus,
+                                                                       &size);
+}
+
+static int
+RndisFilterSetPacketFilter(
+       RNDIS_DEVICE    *Device,
+       UINT32                  NewFilter
+       )
+{
+       RNDIS_REQUEST *request;
+       RNDIS_SET_REQUEST *set;
+       RNDIS_SET_COMPLETE *setComplete;
+       UINT32 status;
+       int ret;
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(RNDIS_MESSAGE_SIZE(RNDIS_SET_REQUEST) + sizeof(UINT32) <= sizeof(RNDIS_MESSAGE));
+
+       request = GetRndisRequest(Device, REMOTE_NDIS_SET_MSG, RNDIS_MESSAGE_SIZE(RNDIS_SET_REQUEST) + sizeof(UINT32));
+       if (!request)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       // Setup the rndis set
+       set = &request->RequestMessage.Message.SetRequest;
+       set->Oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
+       set->InformationBufferLength = sizeof(UINT32);
+       set->InformationBufferOffset = sizeof(RNDIS_SET_REQUEST);
+
+       memcpy((void*)(ULONG_PTR)set + sizeof(RNDIS_SET_REQUEST), &NewFilter, sizeof(UINT32));
+
+       ret = RndisFilterSendRequest(Device, request);
+       if (ret != 0)
+       {
+               goto Cleanup;
+       }
+
+       ret = WaitEventWaitEx(request->WaitEvent, 2000/*2sec*/);
+       if (!ret)
+       {
+               ret = -1;
+               DPRINT_ERR(NETVSC, "timeout before we got a set response...");
+               // We cant deallocate the request since we may still receive a send completion for it.
+               goto Exit;
+       }
+       else
+       {
+               if (ret > 0)
+               {
+                       ret = 0;
+               }
+               setComplete = &request->ResponseMessage.Message.SetComplete;
+               status = setComplete->Status;
+       }
+
+Cleanup:
+       if (request)
+       {
+               PutRndisRequest(Device, request);
+       }
+Exit:
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+int
+RndisFilterInit(
+       NETVSC_DRIVER_OBJECT    *Driver
+       )
+{
+       DPRINT_ENTER(NETVSC);
+
+       DPRINT_DBG(NETVSC, "sizeof(RNDIS_FILTER_PACKET) == %d", sizeof(RNDIS_FILTER_PACKET));
+
+       Driver->RequestExtSize = sizeof(RNDIS_FILTER_PACKET);
+       Driver->AdditionalRequestPageBufferCount = 1; // For rndis header
+
+       //Driver->Context = rndisDriver;
+
+       memset(&gRndisFilter, 0, sizeof(RNDIS_FILTER_DRIVER_OBJECT));
+
+       /*rndisDriver->Driver = Driver;
+
+       ASSERT(Driver->OnLinkStatusChanged);
+       rndisDriver->OnLinkStatusChanged = Driver->OnLinkStatusChanged;*/
+
+       // Save the original dispatch handlers before we override it
+       gRndisFilter.InnerDriver.Base.OnDeviceAdd = Driver->Base.OnDeviceAdd;
+       gRndisFilter.InnerDriver.Base.OnDeviceRemove = Driver->Base.OnDeviceRemove;
+       gRndisFilter.InnerDriver.Base.OnCleanup = Driver->Base.OnCleanup;
+
+       ASSERT(Driver->OnSend);
+       ASSERT(Driver->OnReceiveCallback);
+       gRndisFilter.InnerDriver.OnSend = Driver->OnSend;
+       gRndisFilter.InnerDriver.OnReceiveCallback = Driver->OnReceiveCallback;
+       gRndisFilter.InnerDriver.OnLinkStatusChanged = Driver->OnLinkStatusChanged;
+
+       // Override
+       Driver->Base.OnDeviceAdd = RndisFilterOnDeviceAdd;
+       Driver->Base.OnDeviceRemove = RndisFilterOnDeviceRemove;
+       Driver->Base.OnCleanup = RndisFilterOnCleanup;
+       Driver->OnSend = RndisFilterOnSend;
+       Driver->OnOpen = RndisFilterOnOpen;
+       Driver->OnClose = RndisFilterOnClose;
+       //Driver->QueryLinkStatus = RndisFilterQueryDeviceLinkStatus;
+       Driver->OnReceiveCallback = RndisFilterOnReceive;
+
+       DPRINT_EXIT(NETVSC);
+
+       return 0;
+}
+
+static int
+RndisFilterInitDevice(
+       RNDIS_DEVICE    *Device
+       )
+{
+       RNDIS_REQUEST *request;
+       RNDIS_INITIALIZE_REQUEST *init;
+       RNDIS_INITIALIZE_COMPLETE *initComplete;
+       UINT32 status;
+       int ret;
+
+       DPRINT_ENTER(NETVSC);
+
+       request = GetRndisRequest(Device, REMOTE_NDIS_INITIALIZE_MSG, RNDIS_MESSAGE_SIZE(RNDIS_INITIALIZE_REQUEST));
+       if (!request)
+       {
+               ret = -1;
+               goto Cleanup;
+       }
+
+       // Setup the rndis set
+       init = &request->RequestMessage.Message.InitializeRequest;
+       init->MajorVersion = RNDIS_MAJOR_VERSION;
+       init->MinorVersion = RNDIS_MINOR_VERSION;
+       init->MaxTransferSize = 2048; // FIXME: Use 1536 - rounded ethernet frame size
+
+       Device->State = RNDIS_DEV_INITIALIZING;
+
+       ret = RndisFilterSendRequest(Device, request);
+       if (ret != 0)
+       {
+               Device->State = RNDIS_DEV_UNINITIALIZED;
+               goto Cleanup;
+       }
+
+       WaitEventWait(request->WaitEvent);
+
+       initComplete = &request->ResponseMessage.Message.InitializeComplete;
+       status = initComplete->Status;
+       if (status == RNDIS_STATUS_SUCCESS)
+       {
+               Device->State = RNDIS_DEV_INITIALIZED;
+               ret = 0;
+       }
+       else
+       {
+               Device->State = RNDIS_DEV_UNINITIALIZED;
+               ret = -1;
+       }
+
+Cleanup:
+       if (request)
+       {
+               PutRndisRequest(Device, request);
+       }
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+static void
+RndisFilterHaltDevice(
+       RNDIS_DEVICE    *Device
+       )
+{
+       RNDIS_REQUEST *request;
+       RNDIS_HALT_REQUEST *halt;
+
+       DPRINT_ENTER(NETVSC);
+
+       // Attempt to do a rndis device halt
+       request = GetRndisRequest(Device, REMOTE_NDIS_HALT_MSG, RNDIS_MESSAGE_SIZE(RNDIS_HALT_REQUEST));
+       if (!request)
+       {
+               goto Cleanup;
+       }
+
+       // Setup the rndis set
+       halt = &request->RequestMessage.Message.HaltRequest;
+       halt->RequestId = InterlockedIncrement((int*)&Device->NewRequestId);
+
+       // Ignore return since this msg is optional.
+       RndisFilterSendRequest(Device, request);
+
+       Device->State = RNDIS_DEV_UNINITIALIZED;
+
+Cleanup:
+       if (request)
+       {
+               PutRndisRequest(Device, request);
+       }
+       DPRINT_EXIT(NETVSC);
+       return;
+}
+
+
+static int
+RndisFilterOpenDevice(
+       RNDIS_DEVICE    *Device
+       )
+{
+       int ret=0;
+
+       DPRINT_ENTER(NETVSC);
+
+       if (Device->State != RNDIS_DEV_INITIALIZED)
+               return 0;
+
+       ret = RndisFilterSetPacketFilter(Device, NDIS_PACKET_TYPE_BROADCAST|NDIS_PACKET_TYPE_DIRECTED);
+       if (ret == 0)
+       {
+               Device->State = RNDIS_DEV_DATAINITIALIZED;
+       }
+
+       DPRINT_EXIT(NETVSC);
+       return ret;
+}
+
+static int
+RndisFilterCloseDevice(
+       RNDIS_DEVICE            *Device
+       )
+{
+       int ret;
+
+       DPRINT_ENTER(NETVSC);
+
+       if (Device->State != RNDIS_DEV_DATAINITIALIZED)
+               return 0;
+
+       ret = RndisFilterSetPacketFilter(Device, 0);
+       if (ret == 0)
+       {
+               Device->State = RNDIS_DEV_INITIALIZED;
+       }
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+
+int
+RndisFilterOnDeviceAdd(
+       DEVICE_OBJECT   *Device,
+       void                    *AdditionalInfo
+       )
+{
+       int ret;
+       NETVSC_DEVICE *netDevice;
+       RNDIS_DEVICE *rndisDevice;
+       NETVSC_DEVICE_INFO *deviceInfo = (NETVSC_DEVICE_INFO*)AdditionalInfo;
+
+       DPRINT_ENTER(NETVSC);
+
+       //rndisDevice = MemAlloc(sizeof(RNDIS_DEVICE));
+       rndisDevice = GetRndisDevice();
+       if (!rndisDevice)
+       {
+               DPRINT_EXIT(NETVSC);
+               return -1;
+       }
+
+       DPRINT_DBG(NETVSC, "rndis device object allocated - %p", rndisDevice);
+
+       // Let the inner driver handle this first to create the netvsc channel
+       // NOTE! Once the channel is created, we may get a receive callback
+       // (RndisFilterOnReceive()) before this call is completed
+       ret = gRndisFilter.InnerDriver.Base.OnDeviceAdd(Device, AdditionalInfo);
+       if (ret != 0)
+       {
+               PutRndisDevice(rndisDevice);
+               DPRINT_EXIT(NETVSC);
+               return ret;
+       }
+
+       //
+       // Initialize the rndis device
+       //
+       netDevice = (NETVSC_DEVICE*)Device->Extension;
+       ASSERT(netDevice);
+       ASSERT(netDevice->Device);
+
+       netDevice->Extension = rndisDevice;
+       rndisDevice->NetDevice = netDevice;
+
+       // Send the rndis initialization message
+       ret = RndisFilterInitDevice(rndisDevice);
+       if (ret != 0)
+       {
+               // TODO: If rndis init failed, we will need to shut down the channel
+       }
+
+       // Get the mac address
+       ret = RndisFilterQueryDeviceMac(rndisDevice);
+       if (ret != 0)
+       {
+               // TODO: shutdown rndis device and the channel
+       }
+
+       DPRINT_INFO(NETVSC, "Device 0x%p mac addr %02x%02x%02x%02x%02x%02x",
+                               rndisDevice,
+                               rndisDevice->HwMacAddr[0],
+                               rndisDevice->HwMacAddr[1],
+                               rndisDevice->HwMacAddr[2],
+                               rndisDevice->HwMacAddr[3],
+                               rndisDevice->HwMacAddr[4],
+                               rndisDevice->HwMacAddr[5]);
+
+       memcpy(deviceInfo->MacAddr, rndisDevice->HwMacAddr, HW_MACADDR_LEN);
+
+       RndisFilterQueryDeviceLinkStatus(rndisDevice);
+
+       deviceInfo->LinkState = rndisDevice->LinkStatus;
+       DPRINT_INFO(NETVSC, "Device 0x%p link state %s", rndisDevice, ((deviceInfo->LinkState)?("down"):("up")));
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+
+static int
+RndisFilterOnDeviceRemove(
+       DEVICE_OBJECT *Device
+       )
+{
+       NETVSC_DEVICE *netDevice = (NETVSC_DEVICE*)Device->Extension;
+       RNDIS_DEVICE *rndisDevice = (RNDIS_DEVICE*)netDevice->Extension;
+
+       DPRINT_ENTER(NETVSC);
+
+       // Halt and release the rndis device
+       RndisFilterHaltDevice(rndisDevice);
+
+       PutRndisDevice(rndisDevice);
+       netDevice->Extension = NULL;
+
+       // Pass control to inner driver to remove the device
+       gRndisFilter.InnerDriver.Base.OnDeviceRemove(Device);
+
+       DPRINT_EXIT(NETVSC);
+
+       return 0;
+}
+
+
+static void
+RndisFilterOnCleanup(
+       DRIVER_OBJECT *Driver
+       )
+{
+       DPRINT_ENTER(NETVSC);
+
+       DPRINT_EXIT(NETVSC);
+}
+
+static int
+RndisFilterOnOpen(
+       DEVICE_OBJECT           *Device
+       )
+{
+       int ret;
+       NETVSC_DEVICE *netDevice = (NETVSC_DEVICE*)Device->Extension;
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(netDevice);
+       ret = RndisFilterOpenDevice((RNDIS_DEVICE*)netDevice->Extension);
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+static int
+RndisFilterOnClose(
+       DEVICE_OBJECT           *Device
+       )
+{
+       int ret;
+       NETVSC_DEVICE *netDevice = (NETVSC_DEVICE*)Device->Extension;
+
+       DPRINT_ENTER(NETVSC);
+
+       ASSERT(netDevice);
+       ret = RndisFilterCloseDevice((RNDIS_DEVICE*)netDevice->Extension);
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+
+static int
+RndisFilterOnSend(
+       DEVICE_OBJECT           *Device,
+       NETVSC_PACKET           *Packet
+       )
+{
+       int ret=0;
+       RNDIS_FILTER_PACKET *filterPacket;
+       RNDIS_MESSAGE *rndisMessage;
+       RNDIS_PACKET *rndisPacket;
+       UINT32 rndisMessageSize;
+
+       DPRINT_ENTER(NETVSC);
+
+       // Add the rndis header
+       filterPacket = (RNDIS_FILTER_PACKET*)Packet->Extension;
+       ASSERT(filterPacket);
+
+       memset(filterPacket, 0, sizeof(RNDIS_FILTER_PACKET));
+
+       rndisMessage = &filterPacket->Message;
+       rndisMessageSize = RNDIS_MESSAGE_SIZE(RNDIS_PACKET);
+
+       rndisMessage->NdisMessageType = REMOTE_NDIS_PACKET_MSG;
+       rndisMessage->MessageLength = Packet->TotalDataBufferLength + rndisMessageSize;
+
+       rndisPacket = &rndisMessage->Message.Packet;
+       rndisPacket->DataOffset = sizeof(RNDIS_PACKET);
+       rndisPacket->DataLength = Packet->TotalDataBufferLength;
+
+       Packet->IsDataPacket = TRUE;
+       Packet->PageBuffers[0].Pfn              = GetPhysicalAddress(rndisMessage) >> PAGE_SHIFT;
+       Packet->PageBuffers[0].Offset   = (ULONG_PTR)rndisMessage & (PAGE_SIZE-1);
+       Packet->PageBuffers[0].Length   = rndisMessageSize;
+
+       // Save the packet send completion and context
+       filterPacket->OnCompletion = Packet->Completion.Send.OnSendCompletion;
+       filterPacket->CompletionContext = Packet->Completion.Send.SendCompletionContext;
+
+       // Use ours
+       Packet->Completion.Send.OnSendCompletion = RndisFilterOnSendCompletion;
+       Packet->Completion.Send.SendCompletionContext = filterPacket;
+
+       ret = gRndisFilter.InnerDriver.OnSend(Device, Packet);
+       if (ret != 0)
+       {
+               // Reset the completion to originals to allow retries from above
+               Packet->Completion.Send.OnSendCompletion = filterPacket->OnCompletion;
+               Packet->Completion.Send.SendCompletionContext = filterPacket->CompletionContext;
+       }
+
+       DPRINT_EXIT(NETVSC);
+
+       return ret;
+}
+
+static void
+RndisFilterOnSendCompletion(
+   void *Context)
+{
+       RNDIS_FILTER_PACKET *filterPacket = (RNDIS_FILTER_PACKET *)Context;
+
+       DPRINT_ENTER(NETVSC);
+
+       // Pass it back to the original handler
+       filterPacket->OnCompletion(filterPacket->CompletionContext);
+
+       DPRINT_EXIT(NETVSC);
+}
+
+
+static void
+RndisFilterOnSendRequestCompletion(
+   void *Context
+   )
+{
+       DPRINT_ENTER(NETVSC);
+
+       // Noop
+       DPRINT_EXIT(NETVSC);
+}
diff --git a/drivers/staging/hv/RndisFilter.h b/drivers/staging/hv/RndisFilter.h
new file mode 100644 (file)
index 0000000..f31e7bc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright (c) 2009, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ *   Haiyang Zhang <haiyangz@microsoft.com>
+ *   Hank Janssen  <hjanssen@microsoft.com>
+ *
+ */
+
+
+#ifndef _RNDISFILTER_H_
+#define _RNDISFILTER_H_
+
+#define __struct_bcount(x)
+
+#include "osd.h"
+#include "NetVsc.h"
+
+#include "rndis.h"
+
+#define RNDIS_HEADER_SIZE      (sizeof(RNDIS_MESSAGE) - sizeof(RNDIS_MESSAGE_CONTAINER))
+
+#define NDIS_PACKET_TYPE_DIRECTED      0x00000001
+#define NDIS_PACKET_TYPE_MULTICAST     0x00000002
+#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
+#define NDIS_PACKET_TYPE_BROADCAST     0x00000008
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING        0x00000010
+#define NDIS_PACKET_TYPE_PROMISCUOUS   0x00000020
+#define NDIS_PACKET_TYPE_SMT           0x00000040
+#define NDIS_PACKET_TYPE_ALL_LOCAL     0x00000080
+#define NDIS_PACKET_TYPE_GROUP         0x00000100
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL        0x00000200
+#define NDIS_PACKET_TYPE_FUNCTIONAL    0x00000400
+#define NDIS_PACKET_TYPE_MAC_FRAME     0x00000800
+
+
+
+//
+// Interface
+//
+int
+RndisFilterInit(
+       NETVSC_DRIVER_OBJECT *Driver
+       );
+
+
+#endif // _RNDISFILTER_H_
diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c
new file mode 100644 (file)
index 0000000..1b3174a
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+ *
+ * Copyright (c) 2009, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Authors:
+ *   Hank Janssen  <hjanssen@microsoft.com>
+ *
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/device.h>
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+#include <asm/io.h>
+#else
+#include <linux/io.h>
+#endif
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <net/arp.h>
+#include <net/route.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+#include "logging.h"
+#include "vmbus.h"
+
+#include "NetVscApi.h"
+
+MODULE_LICENSE("GPL");
+
+//
+// Static decl
+//
+static int netvsc_probe(struct device *device);
+static int netvsc_remove(struct device *device);
+static int netvsc_open(struct net_device *net);
+static void netvsc_xmit_completion(void *context);
+static int netvsc_start_xmit (struct sk_buff *skb, struct net_device *net);
+static int netvsc_recv_callback(DEVICE_OBJECT *device_obj, NETVSC_PACKET* Packet);
+static int netvsc_close(struct net_device *net);
+static struct net_device_stats *netvsc_get_stats(struct net_device *net);
+static void netvsc_linkstatus_callback(DEVICE_OBJECT *device_obj, unsigned int status);
+
+//
+// Data types
+//
+struct net_device_context {
+       struct device_context   *device_ctx; // point back to our device context
+       struct net_device_stats stats;
+};
+
+struct netvsc_driver_context {
+       // !! These must be the first 2 fields !!
+       struct driver_context   drv_ctx;
+       NETVSC_DRIVER_OBJECT    drv_obj;
+};
+
+//
+// Globals
+//
+
+static int netvsc_ringbuffer_size = NETVSC_DEVICE_RING_BUFFER_SIZE;
+
+// The one and only one
+static struct netvsc_driver_context g_netvsc_drv;
+
+//
+// Routines
+//
+
+/*++
+
+Name:  netvsc_drv_init()
+
+Desc:  NetVsc driver initialization
+
+--*/
+int netvsc_drv_init(PFN_DRIVERINITIALIZE pfn_drv_init)
+{
+       int ret=0;
+       NETVSC_DRIVER_OBJECT *net_drv_obj=&g_netvsc_drv.drv_obj;
+       struct driver_context *drv_ctx=&g_netvsc_drv.drv_ctx;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       vmbus_get_interface(&net_drv_obj->Base.VmbusChannelInterface);
+
+       net_drv_obj->RingBufferSize = netvsc_ringbuffer_size;
+       net_drv_obj->OnReceiveCallback = netvsc_recv_callback;
+       net_drv_obj->OnLinkStatusChanged = netvsc_linkstatus_callback;
+
+       // Callback to client driver to complete the initialization
+       pfn_drv_init(&net_drv_obj->Base);
+
+       drv_ctx->driver.name = net_drv_obj->Base.name;
+       memcpy(&drv_ctx->class_id, &net_drv_obj->Base.deviceType, sizeof(GUID));
+
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+       drv_ctx->driver.probe = netvsc_probe;
+       drv_ctx->driver.remove = netvsc_remove;
+#else
+       drv_ctx->probe = netvsc_probe;
+       drv_ctx->remove = netvsc_remove;
+#endif
+
+       // The driver belongs to vmbus
+       vmbus_child_driver_register(drv_ctx);
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return ret;
+}
+
+/*++
+
+Name:  netvsc_get_stats()
+
+Desc:  Get the network stats
+
+--*/
+static struct net_device_stats *netvsc_get_stats(struct net_device *net)
+{
+       struct net_device_context *net_device_ctx = netdev_priv(net);
+
+       return &net_device_ctx->stats;
+}
+
+/*++
+
+Name:  netvsc_set_multicast_list()
+
+Desc:  Set the multicast list
+
+Remark:        No-op here
+--*/
+static void netvsc_set_multicast_list(UNUSED_VAR(struct net_device *net))
+{
+}
+
+
+/*++
+
+Name:  netvsc_probe()
+
+Desc:  Add the specified new device to this driver
+
+--*/
+static int netvsc_probe(struct device *device)
+{
+       int ret=0;
+
+       struct driver_context *driver_ctx = driver_to_driver_context(device->driver);
+       struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context*)driver_ctx;
+       NETVSC_DRIVER_OBJECT *net_drv_obj = &net_drv_ctx->drv_obj;
+
+       struct device_context *device_ctx = device_to_device_context(device);
+       DEVICE_OBJECT *device_obj = &device_ctx->device_obj;
+
+       struct net_device *net = NULL;
+       struct net_device_context *net_device_ctx;
+       NETVSC_DEVICE_INFO device_info;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       if (!net_drv_obj->Base.OnDeviceAdd)
+       {
+               return -1;
+       }
+
+       net = alloc_netdev(sizeof(struct net_device_context), "seth%d", ether_setup);
+       //net = alloc_etherdev(sizeof(struct net_device_context));
+       if (!net)
+       {
+               return -1;
+       }
+
+       // Set initial state
+       netif_carrier_off(net);
+       netif_stop_queue(net);
+
+       net_device_ctx = netdev_priv(net);
+       net_device_ctx->device_ctx = device_ctx;
+       device->driver_data = net;
+
+       // Notify the netvsc driver of the new device
+       ret = net_drv_obj->Base.OnDeviceAdd(device_obj, (void*)&device_info);
+       if (ret != 0)
+       {
+               free_netdev(net);
+               device->driver_data = NULL;
+
+               DPRINT_ERR(NETVSC_DRV, "unable to add netvsc device (ret %d)", ret);
+               return ret;
+       }
+
+       // If carrier is still off ie we did not get a link status callback, update it if necessary
+       // FIXME: We should use a atomic or test/set instead to avoid getting out of sync with the device's link status
+       if (!netif_carrier_ok(net))
+       {
+               if (!device_info.LinkState)
+               {
+                       netif_carrier_on(net);
+               }
+       }
+
+       memcpy(net->dev_addr, device_info.MacAddr, ETH_ALEN);
+
+       net->open                               = netvsc_open;
+       net->hard_start_xmit    = netvsc_start_xmit;
+       net->stop                               = netvsc_close;
+       net->get_stats                  = netvsc_get_stats;
+       net->set_multicast_list = netvsc_set_multicast_list;
+
+#if !defined(KERNEL_2_6_27)
+       SET_MODULE_OWNER(net);
+#endif
+       SET_NETDEV_DEV(net, device);
+
+       ret = register_netdev(net);
+       if (ret != 0)
+       {
+               // Remove the device and release the resource
+               net_drv_obj->Base.OnDeviceRemove(device_obj);
+               free_netdev(net);
+       }
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return ret;
+}
+
+static int netvsc_remove(struct device *device)
+{
+       int ret=0;
+       struct driver_context *driver_ctx = driver_to_driver_context(device->driver);
+       struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context*)driver_ctx;
+       NETVSC_DRIVER_OBJECT *net_drv_obj = &net_drv_ctx->drv_obj;
+
+       struct device_context *device_ctx = device_to_device_context(device);
+       struct net_device *net = (struct net_device *)device_ctx->device.driver_data;
+       DEVICE_OBJECT *device_obj = &device_ctx->device_obj;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       if (net == NULL)
+       {
+               DPRINT_INFO(NETVSC, "no net device to remove");
+               DPRINT_EXIT(NETVSC_DRV);
+               return 0;
+       }
+
+       if (!net_drv_obj->Base.OnDeviceRemove)
+       {
+               DPRINT_EXIT(NETVSC_DRV);
+               return -1;
+       }
+
+       // Stop outbound asap
+       netif_stop_queue(net);
+       //netif_carrier_off(net);
+
+       unregister_netdev(net);
+
+       // Call to the vsc driver to let it know that the device is being removed
+       ret = net_drv_obj->Base.OnDeviceRemove(device_obj);
+       if (ret != 0)
+       {
+               // TODO:
+               DPRINT_ERR(NETVSC, "unable to remove vsc device (ret %d)", ret);
+       }
+
+       free_netdev(net);
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return ret;
+}
+
+/*++
+
+Name:  netvsc_open()
+
+Desc:  Open the specified interface device
+
+--*/
+static int netvsc_open(struct net_device *net)
+{
+       int ret=0;
+       struct net_device_context *net_device_ctx = netdev_priv(net);
+       struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver);
+       struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context*)driver_ctx;
+       NETVSC_DRIVER_OBJECT *net_drv_obj = &net_drv_ctx->drv_obj;
+
+       DEVICE_OBJECT *device_obj = &net_device_ctx->device_ctx->device_obj;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       if (netif_carrier_ok(net))
+       {
+               memset(&net_device_ctx->stats, 0 , sizeof(struct net_device_stats));
+
+               // Open up the device
+               ret = net_drv_obj->OnOpen(device_obj);
+               if (ret != 0)
+               {
+                       DPRINT_ERR(NETVSC_DRV, "unable to open device (ret %d).", ret);
+                       return ret;
+               }
+
+               netif_start_queue(net);
+       }
+       else
+       {
+               DPRINT_ERR(NETVSC_DRV, "unable to open device...link is down.");
+       }
+
+       DPRINT_EXIT(NETVSC_DRV);
+       return ret;
+}
+
+/*++
+
+Name:  netvsc_close()
+
+Desc:  Close the specified interface device
+
+--*/
+static int netvsc_close(struct net_device *net)
+{
+       int ret=0;
+       struct net_device_context *net_device_ctx = netdev_priv(net);
+       struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver);
+       struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context*)driver_ctx;
+       NETVSC_DRIVER_OBJECT *net_drv_obj = &net_drv_ctx->drv_obj;
+
+       DEVICE_OBJECT *device_obj = &net_device_ctx->device_ctx->device_obj;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       netif_stop_queue(net);
+
+       ret = net_drv_obj->OnClose(device_obj);
+       if (ret != 0)
+       {
+               DPRINT_ERR(NETVSC_DRV, "unable to close device (ret %d).", ret);
+       }
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return ret;
+}
+
+
+/*++
+
+Name:  netvsc_xmit_completion()
+
+Desc:  Send completion processing
+
+--*/
+static void netvsc_xmit_completion(void *context)
+{
+       NETVSC_PACKET *packet = (NETVSC_PACKET *)context;
+       struct sk_buff *skb = (struct sk_buff *)(ULONG_PTR)packet->Completion.Send.SendCompletionTid;
+       struct net_device* net;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       kfree(packet);
+
+       if (skb)
+       {
+               net = skb->dev;
+
+               dev_kfree_skb_any(skb);
+
+               if (netif_queue_stopped(net))
+               {
+                       DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...", net);
+
+                       netif_wake_queue(net);
+               }
+       }
+
+       DPRINT_EXIT(NETVSC_DRV);
+}
+
+/*++
+
+Name:  netvsc_start_xmit()
+
+Desc:  Start a send
+
+--*/
+static int netvsc_start_xmit (struct sk_buff *skb, struct net_device *net)
+{
+       int ret=0;
+       struct net_device_context *net_device_ctx = netdev_priv(net);
+       struct driver_context *driver_ctx = driver_to_driver_context(net_device_ctx->device_ctx->device.driver);
+       struct netvsc_driver_context *net_drv_ctx = (struct netvsc_driver_context*)driver_ctx;
+       NETVSC_DRIVER_OBJECT *net_drv_obj = &net_drv_ctx->drv_obj;
+
+       int i=0;
+       NETVSC_PACKET* packet;
+       int num_frags;
+       int retries=0;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       // Support only 1 chain of frags
+       ASSERT(skb_shinfo(skb)->frag_list == NULL);
+       ASSERT(skb->dev == net);
+
+       DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d", skb->len, skb->data_len);
+
+       // Add 1 for skb->data and any additional ones requested
+       num_frags = skb_shinfo(skb)->nr_frags + 1 + net_drv_obj->AdditionalRequestPageBufferCount;
+
+       // Allocate a netvsc packet based on # of frags.
+       packet = kzalloc(sizeof(NETVSC_PACKET) + (num_frags * sizeof(PAGE_BUFFER)) + net_drv_obj->RequestExtSize, GFP_ATOMIC);
+       if (!packet)
+       {
+               DPRINT_ERR(NETVSC_DRV, "unable to allocate NETVSC_PACKET");
+               return -1;
+       }
+
+       packet->Extension = (void*)(unsigned long)packet + sizeof(NETVSC_PACKET) + (num_frags * sizeof(PAGE_BUFFER)) ;
+
+       // Setup the rndis header
+       packet->PageBufferCount = num_frags;
+
+       // TODO: Flush all write buffers/ memory fence ???
+       //wmb();
+
+       // Initialize it from the skb
+       ASSERT(skb->data);
+       packet->TotalDataBufferLength   = skb->len;
+
+       // Start filling in the page buffers starting at AdditionalRequestPageBufferCount offset
+       packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Pfn          = virt_to_phys(skb->data) >> PAGE_SHIFT;
+       packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Offset       = (unsigned long)skb->data & (PAGE_SIZE -1);
+       packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Length       = skb->len - skb->data_len;
+
+       ASSERT((skb->len - skb->data_len) <= PAGE_SIZE);
+
+       for (i=net_drv_obj->AdditionalRequestPageBufferCount+1; i<num_frags; i++)
+       {
+               packet->PageBuffers[i].Pfn              = page_to_pfn(skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page);
+               packet->PageBuffers[i].Offset   = skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page_offset;
+               packet->PageBuffers[i].Length   = skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].size;
+       }
+
+       // Set the completion routine
+       packet->Completion.Send.OnSendCompletion = netvsc_xmit_completion;
+       packet->Completion.Send.SendCompletionContext = packet;
+       packet->Completion.Send.SendCompletionTid = (ULONG_PTR)skb;
+
+retry_send:
+       ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj, packet);
+
+       if (ret == 0)
+       {
+#ifdef KERNEL_2_6_5
+#define NETDEV_TX_OK   0
+#define NETDEV_TX_BUSY 0
+#endif
+               ret = NETDEV_TX_OK;
+               net_device_ctx->stats.tx_bytes += skb->len;
+               net_device_ctx->stats.tx_packets++;
+       }
+       else
+       {
+               retries++;
+               if (retries < 4)
+               {
+                       DPRINT_ERR(NETVSC_DRV, "unable to send...retrying %d...", retries);
+                       udelay(100);
+                       goto retry_send;
+               }
+
+               // no more room or we are shutting down
+               DPRINT_ERR(NETVSC_DRV, "unable to send (%d)...marking net device (%p) busy", ret, net);
+               DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net);
+
+               ret = NETDEV_TX_BUSY;
+               net_device_ctx->stats.tx_dropped++;
+
+               netif_stop_queue(net);
+
+               // Null it since the caller will free it instead of the completion routine
+               packet->Completion.Send.SendCompletionTid = 0;
+
+               // Release the resources since we will not get any send completion
+               netvsc_xmit_completion((void*)packet);
+       }
+
+       DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu", net_device_ctx->stats.tx_packets, net_device_ctx->stats.tx_bytes);
+
+       DPRINT_EXIT(NETVSC_DRV);
+       return ret;
+}
+
+
+/*++
+
+Name:  netvsc_linkstatus_callback()
+
+Desc:  Link up/down notification
+
+--*/
+static void netvsc_linkstatus_callback(DEVICE_OBJECT *device_obj, unsigned int status)
+{
+       struct device_context* device_ctx = to_device_context(device_obj);
+       struct net_device* net = (struct net_device *)device_ctx->device.driver_data;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       if (!net)
+       {
+               DPRINT_ERR(NETVSC_DRV, "got link status but net device not initialized yet");
+               return;
+       }
+
+       if (status == 1)
+       {
+               netif_carrier_on(net);
+               netif_wake_queue(net);
+       }
+       else
+       {
+               netif_carrier_off(net);
+               netif_stop_queue(net);
+       }
+       DPRINT_EXIT(NETVSC_DRV);
+}
+
+
+/*++
+
+Name:  netvsc_recv_callback()
+
+Desc:  Callback when we receive a packet from the "wire" on the specify device
+
+--*/
+static int netvsc_recv_callback(DEVICE_OBJECT *device_obj, NETVSC_PACKET* packet)
+{
+       int ret=0;
+       struct device_context *device_ctx = to_device_context(device_obj);
+       struct net_device *net = (struct net_device *)device_ctx->device.driver_data;
+       struct net_device_context *net_device_ctx;
+
+       struct sk_buff *skb;
+       void *data;
+       int i=0;
+       unsigned long flags;
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       if (!net)
+       {
+               DPRINT_ERR(NETVSC_DRV, "got receive callback but net device not initialized yet");
+               return 0;
+       }
+
+       net_device_ctx = netdev_priv(net);
+
+       // Allocate a skb - TODO preallocate this
+       //skb = alloc_skb(packet->TotalDataBufferLength, GFP_ATOMIC);
+       skb = dev_alloc_skb(packet->TotalDataBufferLength + 2); // Pad 2-bytes to align IP header to 16 bytes
+       ASSERT(skb);
+       skb_reserve(skb, 2);
+       skb->dev = net;
+
+       // for kmap_atomic
+       local_irq_save(flags);
+
+       // Copy to skb. This copy is needed here since the memory pointed by NETVSC_PACKET
+       // cannot be deallocated
+       for (i=0; i<packet->PageBufferCount; i++)
+       {
+           data = kmap_atomic(pfn_to_page(packet->PageBuffers[i].Pfn), KM_IRQ1);
+           data = (void*)(unsigned long)data + packet->PageBuffers[i].Offset;
+
+           memcpy(skb_put(skb, packet->PageBuffers[i].Length), data, packet->PageBuffers[i].Length);
+
+           kunmap_atomic((void*)((unsigned long)data - packet->PageBuffers[i].Offset), KM_IRQ1);
+       }
+
+       local_irq_restore(flags);
+
+       skb->protocol = eth_type_trans(skb, net);
+
+       skb->ip_summed = CHECKSUM_NONE;
+
+       // Pass the skb back up. Network stack will deallocate the skb when it is done
+       ret = netif_rx(skb);
+
+       switch (ret)
+       {
+       case NET_RX_DROP:
+               net_device_ctx->stats.rx_dropped++;
+               break;
+       default:
+               net_device_ctx->stats.rx_packets++;
+               net_device_ctx->stats.rx_bytes += skb->len;
+               break;
+
+       }
+       DPRINT_DBG(NETVSC_DRV, "# of recvs %lu total size %lu", net_device_ctx->stats.rx_packets, net_device_ctx->stats.rx_bytes);
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return 0;
+}
+
+static int netvsc_drv_exit_cb(struct device *dev, void *data)
+{
+       struct device **curr = (struct device **)data;
+       *curr = dev;
+       return 1; // stop iterating
+}
+
+/*++
+
+Name:  netvsc_drv_exit()
+
+Desc:
+
+--*/
+void netvsc_drv_exit(void)
+{
+       NETVSC_DRIVER_OBJECT *netvsc_drv_obj=&g_netvsc_drv.drv_obj;
+       struct driver_context *drv_ctx=&g_netvsc_drv.drv_ctx;
+
+       struct device *current_dev=NULL;
+#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
+#define driver_for_each_device(drv, start, data, fn) \
+       struct list_head *ptr, *n; \
+       list_for_each_safe(ptr, n, &((drv)->devices)) {\
+               struct device *curr_dev;\
+               curr_dev = list_entry(ptr, struct device, driver_list);\
+               fn(curr_dev, data);\
+       }
+#endif
+
+       DPRINT_ENTER(NETVSC_DRV);
+
+       while (1)
+       {
+               current_dev = NULL;
+
+               // Get the device
+               driver_for_each_device(&drv_ctx->driver, NULL, (void*)&current_dev, netvsc_drv_exit_cb);
+
+               if (current_dev == NULL)
+                       break;
+
+               // Initiate removal from the top-down
+               DPRINT_INFO(NETVSC_DRV, "unregistering device (%p)...", current_dev);
+
+               device_unregister(current_dev);
+       }
+
+       if (netvsc_drv_obj->Base.OnCleanup)
+               netvsc_drv_obj->Base.OnCleanup(&netvsc_drv_obj->Base);
+
+       vmbus_child_driver_unregister(drv_ctx);
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return;
+}
+
+static int __init netvsc_init(void)
+{
+       int ret;
+
+       DPRINT_ENTER(NETVSC_DRV);
+       DPRINT_INFO(NETVSC_DRV, "Netvsc initializing....");
+
+       ret = netvsc_drv_init(NetVscInitialize);
+
+       DPRINT_EXIT(NETVSC_DRV);
+
+       return ret;
+}
+
+static void __exit netvsc_exit(void)
+{
+       DPRINT_ENTER(NETVSC_DRV);
+
+       netvsc_drv_exit();
+
+       DPRINT_EXIT(NETVSC_DRV);
+}
+
+module_param(netvsc_ringbuffer_size, int, S_IRUGO);
+
+module_init(netvsc_init);
+module_exit(netvsc_exit);