Commit fceaf24a authored by Hank Janssen's avatar Hank Janssen Committed by Greg Kroah-Hartman

Staging: hv: add the Hyper-V virtual network driver

This is the virtual network driver when running Linux on top of Hyper-V.
Signed-off-by: default avatarHank Janssen <hjanssen@microsoft.com>
Signed-off-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent f82bd046
/*
*
* 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;
}
/*
*
* 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_
/*
*
* 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);
}
/*
*
* 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_
/*
*
* 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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment