Merge pull request #2 from Microsoft/stage

Upload RtEthSample driver
This commit is contained in:
jtippet 2017-05-02 13:27:17 -07:00 коммит произвёл GitHub
Родитель b53b76df8a fd63ab9ae0
Коммит 48b2576759
37 изменённых файлов: 6956 добавлений и 0 удалений

397
IoHelpers/dma/txdmafx.h Normal file
Просмотреть файл

@ -0,0 +1,397 @@
/*++
Copyright (C) Microsoft Corporation. All rights reserved.
NetAdapterCx NETTXQUEUE DMA Scatter/Gather Framework
This framework aims to help developers simplify the code they write to
deal with the DMA engine in a NETTXQUEUE. It creates a queue on behalf
of the NIC driver and intercepts the EvtTxQueueAdvance callback, breaking
it down in four function calls the NIC driver should define using macros:
Macro Name | Required | Type
-------------------------------|------------|--------------------------------------
TX_DMA_FX_PROGRAM_DESCRIPTORS | Yes | EVT_TX_DMA_QUEUE_PROGRAM_DESCRIPTORS
TX_DMA_FX_GET_PACKET_STATUS | Yes | EVT_TX_DMA_QUEUE_GET_PACKET_STATUS
TX_DMA_FX_FLUSH_TRANSACTION | Yes | EVT_TX_DMA_QUEUE_FLUSH_TRANSACTION
TX_DMA_FX_BOUNCE_ANALYSIS | No | EVT_TX_DMA_QUEUE_BOUNCE_ANALYSIS
TX_DMA_FX_ALLOC_TAG | No | Pool tag to use in internal allocations
To use this framework the NIC driver should:
- Include txdmafxtypes.h
- Define at least the required macros
- Include txdmafx.h
Example:
#include "txdmafxtypes.h
EVT_TX_DMA_QUEUE_PROGRAM_DESCRIPTORS EvtProgramDescriptors;
EVT_TX_DMA_QUEUE_FLUSH_TRANSACTION EvtFlushTransation;
EVT_TX_DMA_QUEUE_GET_PACKET_STATUS EvtGetPacketStatus;
#define TX_DMA_FX_PROGRAM_DESCRIPTORS EvtProgramDescriptors
#define TX_DMA_FX_FLUSH_TRANSACTION EvtFlushTransation
#define TX_DMA_FX_GET_PACKET_STATUS EvtGetPacketStatus
#define TX_DMA_FX_ALLOC_TAG 'tseT'
#include "txdmafx.h"
TX_DMA_FX_PROGRAM_DESCRIPTORS: Called one time for each NET_PACKET
that needs to be transmitted. The framework will take care of
mapping/unmapping the buffers and will pass in a SCATTER_GATHER_LIST
along with the packet so that the NIC driver can program the descriptors
to hardware.
TX_DMA_FX_GET_PACKET_STATUS: Called one time for each NET_PACKET pending
transmission. If this function returns STATUS_SUCCESS, the framework will
release any resources it acquired to map the buffers and return the packet
to the OS.
If this function returns STATUS_PENDING the packet is not completed, any
other status code will cause the packet to be returned to the OS and will
be counted as a failed completion.
TX_DMA_FX_FLUSH_TRANSACTION: Called once per EvtTxQueueAdvance callback
if any new descriptors were programmed to hardware. The NIC should do
whatever it is necessary to flush their DMA transaction.
TX_DMA_FX_BOUNCE_ANALYSIS: If the internal framework bounce analysis is not
enough for the NIC driver, it can use this function to implement any checks
relevant to their hardware. Called once for each NET_PACKET that needs to
be transmitted.
To see what kind of bounce analysis the framework does see NET_SCATTER_GATHER_TXQUEUE_CONFIG
definition or _TxDmaFxBounceAnalysis implementation.
--*/
#ifndef _TXDMAFX_H_
#define _TXDMAFX_H_
#include "txdmafx_details.h"
#ifndef TX_DMA_FX_PROGRAM_DESCRIPTORS
#error To use this framework you need to define TX_DMA_FX_PROGRAM_DESCRIPTORS
#endif
#ifndef TX_DMA_FX_GET_PACKET_STATUS
#error To use this framework you need to define TX_DMA_FX_GET_PACKET_STATUS
#endif
#ifndef TX_DMA_FX_FLUSH_TRANSACTION
#error To use this framework you need to define TX_DMA_FX_FLUSH_TRANSACTION
#endif
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
VOID
_TxDmaFxCompleteTxPackets(
_In_ TxDmaFx *DmaFx
)
/*
Description:
Iterates over the packets in the ring buffer that belong to the NIC driver and
were already programmed to hardware. It then calls TX_DMA_FX_GET_PACKET_STATUS
to let the NIC driver check if the DMA transfer is complete or not. If it is, any
resources associated with the transfer are returned and the packet is marked
as complete.
*/
{
NET_RING_BUFFER *ringBuffer = DmaFx->RingBuffer;
while (ringBuffer->BeginIndex != ringBuffer->NextIndex)
{
NET_PACKET *packet = NetRingBufferGetPacketAtIndex(ringBuffer, ringBuffer->BeginIndex);
// If the packet is already marked as completed it is because we
// failed to program its descriptors and we should just drop it
if (!packet->Data.Completed)
{
NTSTATUS packetStatus =
TX_DMA_FX_GET_PACKET_STATUS(
DmaFx->QueueHandle,
packet);
// We need to complete packets in order, if the current returned
// pending there is no point in keep trying
if (packetStatus == STATUS_PENDING)
break;
if (packetStatus != STATUS_SUCCESS)
DmaFx->Statistics.Packet.CompletedWithError += 1;
if (!DmaFx->DmaBypass)
{
// If we are using DMA APIs make sure we return the resources we
// acquired
TX_DMA_FX_PACKET_CONTEXT *fxPacketContext = _TxDmaFxGetPacketContext(packet, DmaFx);
// Even when using DMA APIs, we might still have a NULL SGL if
// the packet was bounced
if (fxPacketContext->ScatterGatherList != NULL)
{
DmaFx->DmaAdapter->DmaOperations->PutScatterGatherList(
DmaFx->DmaAdapter,
fxPacketContext->ScatterGatherList,
TRUE);
fxPacketContext->ScatterGatherList = NULL;
}
}
if (_TX_DMA_FX_IS_PACKET_BOUNCED(packet))
{
DmaFx->BounceFreeIndex += 1;
_TX_DMA_FX_PACKET_CLEAR_BOUNCED_FLAG(packet);
}
}
ringBuffer->BeginIndex = NetRingBufferIncrementIndex(ringBuffer, ringBuffer->BeginIndex);
}
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
ULONG
_TxDmaFxTransmitPackets(
_In_ TxDmaFx *DmaFx
)
/*
Description:
This function iterates over the packets in the ring buffer
owned by the NIC but not yet programmed to hardware. It then
performs an internal bounce analysis to decide if the Tx buffer
needs to be bounced before transmiting or not. Lastly it calls a
function to handle the transmit operation.
The NIC driver can optionally register a TX_DMA_FX_BOUNCE_ANALYSIS
callback in which it can analyze the Tx buffers and decide if it they
need to be bounced or not.
*/
{
ULONG numPacketsProgrammed = 0;
NET_PACKET *netPacket;
NET_RING_BUFFER *ringBuffer = DmaFx->RingBuffer;
while (NULL != (netPacket = NetRingBufferGetNextPacket(ringBuffer)))
{
NTSTATUS status = STATUS_SUCCESS;
if (netPacket->IgnoreThisPacket)
{
DmaFx->Statistics.Packet.Skipped += 1;
status = STATUS_UNSUCCESSFUL;
}
else
{
TX_DMA_BOUNCE_ANALYSIS bounceAnalysis = _TxDmaFxBounceAnalysis(DmaFx, netPacket);
if (bounceAnalysis == TxDmaTransmitInPlace)
{
#ifdef TX_DMA_FX_BOUNCE_ANALYSIS
bounceAnalysis = TX_DMA_FX_BOUNCE_ANALYSIS(DmaFx->QueueHandle, netPacket);
#endif
}
switch (bounceAnalysis)
{
case TxDmaTransmitInPlace:
{
status = _TxDmaFxMapAndTransmitPacket(DmaFx, netPacket);
break;
}
case TxDmaTransmitAfterBouncing:
{
status = _TxDmaFxBounceAndTransmitPacket(DmaFx, netPacket);
break;
}
case TxDmaCannotTransmit:
DmaFx->Statistics.Packet.CannotTransmit += 1;
__fallthrough;
default:
status = STATUS_UNSUCCESSFUL;
break;
}
}
// If DMA doesn't have enough buffers for us,
// or if we didn't have enough bounce buffers
// give up now and try again when resources
// are available.
if (status == STATUS_INSUFFICIENT_RESOURCES)
break;
if (status == STATUS_SUCCESS)
numPacketsProgrammed++;
else
netPacket->Data.Completed = TRUE;
NetRingBufferAdvanceNextPacket(ringBuffer);
}
return numPacketsProgrammed;
}
_Use_decl_annotations_
__inline
VOID
_TxDmaFxAdvance(
_In_ NETTXQUEUE TxQueue
)
/*
Description:
This function handles the NETTXQUEUE EvtTxQueueAdvance on behalf of the
NIC driver. If any number of packets are programmed to hardware during
this callback we call TX_DMA_FX_FLUSH_TRANSACTION to let the consumer
of this framework do whatever is necessary to flush their DMA transaction.
Arguments:
TxQueue - NETTXQUEUE handle
*/
{
TxDmaFx *TxDmaFx = _TxDmaFxGetContext(TxQueue);
ULONG numPacketsProgrammed = _TxDmaFxTransmitPackets(TxDmaFx);
if (numPacketsProgrammed > 0)
TX_DMA_FX_FLUSH_TRANSACTION(TxDmaFx->QueueHandle);
_TxDmaFxCompleteTxPackets(TxDmaFx);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
__inline
NetTxDmaQueueCreate(
_Inout_ PNETTXQUEUE_INIT NetTxQueueInit,
_In_opt_ PWDF_OBJECT_ATTRIBUTES TxQueueAttributes,
_In_ PNET_TX_DMA_QUEUE_CONFIG Configuration,
_Out_ NETTXQUEUE* TxQueue
)
/*
Description:
This function will create a NETTXQUEUE on behalf of the caller and set up
the necessary state to intercept incoming NET_PACKETs and map the buffers
to use in DMA transactions.
Arguments:
NetTxQueueInit - Opaque handle containing information about the queue the OS
is asking us to create
TxQueueAttributes - Object attributes the NIC driver wants in the NETTXQUEUE
Configuration - Contains configuration this framework will use to make decisions
about what to do with incoming NET_PACKETs as well as the
callback functions needed to operate this queue
TxQueue - Handle to the created NETTXQUEUE
*/
{
*TxQueue = NULL;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(_TxDmaFxValidateConfig(Configuration));
BOOLEAN dmaBypass;
if (Configuration->AllowDmaBypass)
{
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
_TxDmaFxCheckDmaBypass(
Configuration->DmaEnabler,
&dmaBypass));
}
else
{
dmaBypass = FALSE;
}
// The framework only intercepts the Advance callback (to map and unmap the
// incoming packets), the others go directly to the ones the NIC driver provides
NET_TXQUEUE_CONFIG txConfig;
NET_TXQUEUE_CONFIG_INIT(
&txConfig,
_TxDmaFxAdvance,
Configuration->EvtTxQueueSetNotificationEnabled,
Configuration->EvtTxQueueCancel);
ULONG_PTR fxPacketContextOffset = 0;
// We only need a packet context if using DMA APIs
if (!dmaBypass)
{
ULONG_PTR alignedClientContextSize = ALIGN_UP_BY(Configuration->ContextTypeInfo->ContextSize, __alignof(_TX_DMA_FX_PACKET_CONTEXT));
fxPacketContextOffset = alignedClientContextSize;
ULONG_PTR newContextSize = alignedClientContextSize + sizeof(_TX_DMA_FX_PACKET_CONTEXT);
WDF_OBJECT_CONTEXT_TYPE_INFO *typeInfo = (WDF_OBJECT_CONTEXT_TYPE_INFO *)Configuration->ContextTypeInfo;
typeInfo->ContextSize = newContextSize;
}
txConfig.ContextTypeInfo = Configuration->ContextTypeInfo;
// Configure a private Tx Queue context to hold DMA information, the NIC
// is not allowed to modify the data stored in it
WDF_OBJECT_ATTRIBUTES privateAttribs;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&privateAttribs, TxDmaFx);
NETTXQUEUE txQueue;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
NetTxQueueCreate(
NetTxQueueInit,
&privateAttribs,
&txConfig,
&txQueue));
// Now allocate space for the NIC context (if any)
if (TxQueueAttributes != NULL)
{
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
WdfObjectAllocateContext(
txQueue,
TxQueueAttributes,
NULL));
}
// Initialize TxDmaFx object
TxDmaFx *dmaFx = _TxDmaFxGetContext(txQueue);
dmaFx->QueueHandle = txQueue;
dmaFx->RingBuffer = NetTxQueueGetRingBuffer(txQueue);
dmaFx->Config = *Configuration;
if (!dmaBypass)
{
dmaFx->FxPacketContextOffset = fxPacketContextOffset;
dmaFx->FxPacketContextTypeInfo = Configuration->ContextTypeInfo;
}
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
_TxDmaFxInitialize(
dmaFx,
dmaBypass));
*TxQueue = txQueue;
return STATUS_SUCCESS;
}
#endif

Просмотреть файл

@ -0,0 +1,665 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#ifndef _TXDMAFX_DETAILS_H_
#define _TXDMAFX_DETAILS_H_
_IRQL_requires_max_(PASSIVE_LEVEL)
__inline
NTSTATUS
_TxDmaFxCheckDmaBypass(
_In_ WDFDMAENABLER Enabler,
_Out_ BOOLEAN *DmaBypass
)
{
DMA_ADAPTER *dmaAdapter = WdfDmaEnablerWdmGetDmaAdapter(Enabler, WdfDmaDirectionWriteToDevice);
DMA_ADAPTER_INFO adapterInfo = { DMA_ADAPTER_INFO_VERSION1 };
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
dmaAdapter->DmaOperations->GetDmaAdapterInfo(
dmaAdapter,
&adapterInfo));
*DmaBypass = (0 != (adapterInfo.V1.Flags & ADAPTER_INFO_API_BYPASS));
return STATUS_SUCCESS;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
__inline
NTSTATUS
_TxDmaFxValidateConfig(
_In_ NET_TX_DMA_QUEUE_CONFIG *Config
)
/*
Description:
Validades the configuration object provided by
the NIC driver.
*/
{
// Check mandatory event callbacks
if (Config->EvtTxQueueSetNotificationEnabled == NULL ||
Config->EvtTxQueueCancel == NULL)
{
return STATUS_INVALID_PARAMETER;
}
// We need a WDFDEVICE and WDFDMAENABLER to use DMA APIs, check they
// are present
if (Config->Device == NULL || Config->DmaEnabler == NULL)
return STATUS_INVALID_PARAMETER;
// Check if AlignmentRequirement is a 2^N - 1 number
if (Config->AlignmentRequirement != -1 && (Config->AlignmentRequirement & (Config->AlignmentRequirement + 1)) != 0)
return STATUS_INVALID_PARAMETER;
// Check if a maximum packet size is given, the framework needs this information
// to make internal allocations
if (Config->MaximumPacketSize == 0)
return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
__inline
NTSTATUS
_TxDmaFxInitializeForDirectMapping(
_In_ TxDmaFx *DmaFx
)
/*
Description:
Initializes TxDmaFx with the necessary resources
to do direct mapping of buffers.
*/
{
DmaFx->DmaBypass = TRUE;
size_t sglAllocationSize;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
RtlSizeTMult(
sizeof(SCATTER_GATHER_ELEMENT),
DmaFx->Config.MaximumScatterGatherElements,
&sglAllocationSize));
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
RtlSizeTAdd(
sglAllocationSize,
FIELD_OFFSET(SCATTER_GATHER_LIST, Elements),
&sglAllocationSize));
// Parent the memory allocation to the NETTXQUEUE
WDF_OBJECT_ATTRIBUTES memoryAttributes;
WDF_OBJECT_ATTRIBUTES_INIT(&memoryAttributes);
memoryAttributes.ParentObject = DmaFx->QueueHandle;
WDFMEMORY sglAllocation;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
WdfMemoryCreate(
&memoryAttributes,
NonPagedPoolNx,
TX_DMA_FX_ALLOC_TAG,
sglAllocationSize,
&sglAllocation,
(void**)&DmaFx->SpareSgl));
return STATUS_SUCCESS;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
__inline
NTSTATUS
_TxDmaFxInitializeForDma(
_In_ TxDmaFx *DmaFx
)
/*
Description:
Initializes TxDmaFx with the necessary resources
to use with DMA APIs when mapping buffers.
*/
{
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
DmaFx->DmaAdapter->DmaOperations->CalculateScatterGatherList(
DmaFx->DmaAdapter,
NULL,
ULongToPtr(PAGE_SIZE - 1),
DmaFx->Config.MaximumPacketSize,
&DmaFx->ScatterGatherListSize,
NULL));
//
// Allocate memory for scatter-gather list
//
NET_RING_BUFFER *ringBuffer = DmaFx->RingBuffer;
size_t memSize;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
RtlSizeTMult(
ringBuffer->NumberOfElements,
DmaFx->ScatterGatherListSize,
&memSize));
// Parent the memory allocation to the NETTXQUEUE
WDF_OBJECT_ATTRIBUTES memoryAttributes;
WDF_OBJECT_ATTRIBUTES_INIT(&memoryAttributes);
memoryAttributes.ParentObject = DmaFx->QueueHandle;
WDFMEMORY memory;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
WdfMemoryCreate(
&memoryAttributes,
NonPagedPoolNx,
TX_DMA_FX_ALLOC_TAG,
memSize,
&memory,
&DmaFx->SgListMem));
RtlZeroMemory(DmaFx->SgListMem, memSize);
size_t dmaAllocationSize;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
RtlSizeTMult(
DMA_TRANSFER_CONTEXT_SIZE_V1,
ringBuffer->NumberOfElements,
&dmaAllocationSize));
WDF_OBJECT_ATTRIBUTES_INIT(&memoryAttributes);
memoryAttributes.ParentObject = DmaFx->QueueHandle;
void *dmaArray;
WDFMEMORY dmaAllocation;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
WdfMemoryCreate(
&memoryAttributes,
NonPagedPoolNx,
TX_DMA_FX_ALLOC_TAG,
dmaAllocationSize,
&dmaAllocation,
&dmaArray));
// Initialize the private packet context for each packet in the ring buffer
for (UINT32 i = 0; i < ringBuffer->NumberOfElements; i++)
{
NET_PACKET *packet = NetRingBufferGetPacketAtIndex(ringBuffer, i);
TX_DMA_FX_PACKET_CONTEXT *fxPacketContext = _TxDmaFxGetPacketContext(packet, DmaFx);
fxPacketContext->ScatterGatherBuffer = (PUCHAR)DmaFx->SgListMem + i * DmaFx->ScatterGatherListSize;
fxPacketContext->DmaTransferContext = (UCHAR*)dmaArray + i * DMA_TRANSFER_CONTEXT_SIZE_V1;
}
return STATUS_SUCCESS;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
__inline
NTSTATUS
_TxDmaFxInitializeBounceBuffers(
_In_ TxDmaFx *DmaFx
)
/*
Description:
Allocates the buffers used to bounce packets.
*/
{
// Make sure the bounce buffer size is aligned to something, if
// the NIC driver provided an alignment requirement, use that value,
// otherwise use MEMORY_ALLOCATION_ALIGNMENT
ULONG_PTR alignUpBy = DmaFx->Config.AlignmentRequirement != -1
? DmaFx->Config.AlignmentRequirement + 1
: MEMORY_ALLOCATION_ALIGNMENT;
ULONG_PTR bounceBufferSize = ALIGN_UP_BY(DmaFx->Config.MaximumPacketSize, alignUpBy);
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
RtlULongPtrToULong(
bounceBufferSize,
&DmaFx->BounceBufferSize));
// Allocate bounce buffers
size_t bounceSize;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
RtlSizeTMult(
DmaFx->BounceBufferSize,
DmaFx->NumBounceBuffers,
&bounceSize));
WDFCOMMONBUFFER commonBuffer;
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
WdfCommonBufferCreate(
DmaFx->Config.DmaEnabler,
bounceSize,
WDF_NO_OBJECT_ATTRIBUTES,
&commonBuffer));
DmaFx->BounceBasePA = WdfCommonBufferGetAlignedLogicalAddress(commonBuffer);
DmaFx->BounceBaseVA = WdfCommonBufferGetAlignedVirtualAddress(commonBuffer);
return STATUS_SUCCESS;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
__inline
NTSTATUS
_TxDmaFxInitialize(
_In_ TxDmaFx *DmaFx,
_In_ BOOLEAN BypassDma
)
/*
Description:
This function will initialze the framework to properly map/unmap buffers
using DMA. It will decide if direct mapping can be done or not, and
allocate the appropriate resources.
*/
{
if (DmaFx->Config.MaximumScatterGatherElements == 0)
DmaFx->Config.MaximumScatterGatherElements = _TX_DMA_FX_DEFAULT_SCATTER_GATHER_ELEMENTS;
if (DmaFx->Config.AddressWidth > 0)
DmaFx->MaximumAddress = 1ull << DmaFx->Config.AddressWidth;
C_ASSERT(_TX_DMA_FX_IS_POWER_OF_TWO(_TX_DMA_FX_NUM_BOUNCE_BUFFERS));
DmaFx->NumBounceBuffers = _TX_DMA_FX_NUM_BOUNCE_BUFFERS;
DmaFx->DmaAdapter = WdfDmaEnablerWdmGetDmaAdapter(
DmaFx->Config.DmaEnabler,
WdfDmaDirectionWriteToDevice);
DmaFx->DeviceObject = WdfDeviceWdmGetDeviceObject(DmaFx->Config.Device);
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
_TxDmaFxInitializeBounceBuffers(DmaFx));
if (BypassDma)
{
// If ADAPTER_INFO_API_BYPASS flag is set, it means we can map
// the buffers without using the DMA HAL APIs, which requires less
// resource allocation and less complicated calls
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
_TxDmaFxInitializeForDirectMapping(DmaFx));
}
else
{
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
_TxDmaFxInitializeForDma(DmaFx));
}
return STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
ULONG
_TxDmaFxCopyPacketToBuffer(
_In_ NET_PACKET *packet,
_Out_writes_bytes_(bufferSize) VOID *buffer,
_In_ ULONG bufferSize
)
/*
Description:
Copy the contents of a NET_PACKET to a continuous buffer.
*/
{
UCHAR *p = (UCHAR*)buffer;
ULONG bytesRemaining = bufferSize;
NET_PACKET_FRAGMENT *fragment;
for (fragment = &packet->Data; ; fragment = NET_PACKET_FRAGMENT_GET_NEXT(fragment))
{
if (!NT_VERIFY(bytesRemaining >= fragment->ValidLength))
break;
RtlCopyMemory(p, (UCHAR*)fragment->VirtualAddress + fragment->Offset, (size_t)fragment->ValidLength);
p += fragment->ValidLength;
bytesRemaining -= (ULONG)fragment->ValidLength;
if (fragment->LastFragmentOfFrame)
break;
}
return (ULONG)(p - (UCHAR*)buffer);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
NTSTATUS
_TxDmaFxTransmitPacketViaDirectMapping(
_In_ TxDmaFx *DmaFx,
_In_ NET_PACKET *NetPacket
)
/*
Description:
Maps the fragments of a NET_PACKET using direct mapping and then
calls EvtSgProgramDescriptors to let the NIC driver program the
buffers to hardware.
*/
{
SCATTER_GATHER_LIST *sgl = DmaFx->SpareSgl;
NET_PACKET_FRAGMENT *fragment;
sgl->NumberOfElements = 0;
for (fragment = &NetPacket->Data; ; fragment = NET_PACKET_FRAGMENT_GET_NEXT(fragment))
{
ULONG_PTR vaStart = (ULONG_PTR)fragment->VirtualAddress + (ULONG)fragment->Offset;
ULONG_PTR vaEnd = vaStart + (ULONG)fragment->ValidLength;
if (vaStart == vaEnd)
continue;
for (ULONG_PTR va = vaStart; va < vaEnd; va = (ULONG_PTR)(PAGE_ALIGN(va)) + PAGE_SIZE)
{
NT_ASSERT(sgl->NumberOfElements < DmaFx->Config.MaximumScatterGatherElements);
SCATTER_GATHER_ELEMENT *sgElement = &sgl->Elements[sgl->NumberOfElements];
// Performance can be optimized by coalescing adjacent SGEs
sgElement->Address = MmGetPhysicalAddress((PVOID)va);
if (PAGE_ALIGN(va) != PAGE_ALIGN(vaEnd))
sgElement->Length = PAGE_SIZE - BYTE_OFFSET(va);
else
sgElement->Length = (ULONG)(vaEnd - va);
sgl->NumberOfElements += 1;
}
if (fragment->LastFragmentOfFrame)
break;
}
NT_ASSERT(sgl->NumberOfElements > 0);
TX_DMA_FX_PROGRAM_DESCRIPTORS(
DmaFx->QueueHandle,
NetPacket,
sgl);
return STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
NTSTATUS
_TxDmaFxTransmitPacketViaDma(
_In_ TxDmaFx *DmaFx,
_In_ NET_PACKET *NetPacket
)
/*
Description:
Maps the fragments of a NET_PACKET using DMA HAL APIs and then
calls EvtSgProgramDescriptors to let the NIC driver program the
buffers to hardware.
*/
{
ULONG totalFrameLength = 0;
ULONG mdlChainOffset = 0;
NET_PACKET_FRAGMENT *fragment;
for (fragment = &NetPacket->Data; ; fragment = NET_PACKET_FRAGMENT_GET_NEXT(fragment))
{
if (fragment->Offset > 0)
{
if (totalFrameLength == 0)
{
mdlChainOffset += (ULONG)fragment->Offset;
}
}
totalFrameLength += (ULONG)fragment->ValidLength;
if (fragment->LastFragmentOfFrame)
break;
}
DMA_ADAPTER *dmaAdapter = DmaFx->DmaAdapter;
TX_DMA_FX_PACKET_CONTEXT *fxPacketContext = _TxDmaFxGetPacketContext(NetPacket, DmaFx);
_TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(
dmaAdapter->DmaOperations->InitializeDmaTransferContext(
dmaAdapter,
fxPacketContext->DmaTransferContext));
// We know for sure there is a MDL pointer in the fragment,
// so disable the loss of data warning
#pragma warning (push)
#pragma warning (disable : 4305)
MDL *firstMdl = (MDL*)NetPacket->Data.DmaLogicalAddress.QuadPart;
#pragma warning (pop)
NTSTATUS buildSGLStatus = dmaAdapter->DmaOperations->BuildScatterGatherListEx(
dmaAdapter,
DmaFx->DeviceObject,
fxPacketContext->DmaTransferContext,
firstMdl,
mdlChainOffset,
totalFrameLength,
DMA_SYNCHRONOUS_CALLBACK,
NULL, // ExecutionRoutine
NULL, // Context
TRUE, // WriteToDevice
fxPacketContext->ScatterGatherBuffer,
DmaFx->ScatterGatherListSize,
NULL, // DmaCompletionRoutine,
NULL, // CompletionContext,
&fxPacketContext->ScatterGatherList);
if (buildSGLStatus == STATUS_SUCCESS)
{
TX_DMA_FX_PROGRAM_DESCRIPTORS(
DmaFx->QueueHandle,
NetPacket,
fxPacketContext->ScatterGatherList);
}
dmaAdapter->DmaOperations->FreeAdapterObject(
dmaAdapter,
DeallocateObjectKeepRegisters);
return buildSGLStatus;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
NTSTATUS
_TxDmaFxMapAndTransmitPacket(
_In_ TxDmaFx *DmaFx,
_Inout_ NET_PACKET *NetPacket
)
/*
Description:
Calls the appropriate transmit function based on if we
can bypass DMA APIs or not
*/
{
if (DmaFx->DmaBypass)
{
return _TxDmaFxTransmitPacketViaDirectMapping(DmaFx, NetPacket);
}
else
{
NTSTATUS dmaStatus = _TxDmaFxTransmitPacketViaDma(DmaFx, NetPacket);
if (dmaStatus != STATUS_SUCCESS)
{
switch (dmaStatus)
{
case STATUS_INSUFFICIENT_RESOURCES:
DmaFx->Statistics.DMA.InsufficientResourcesCount += 1;
break;
default:
DmaFx->Statistics.DMA.OtherErrors += 1;
break;
}
}
return dmaStatus;
}
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
NTSTATUS
_TxDmaFxBounceAndTransmitPacket(
_In_ TxDmaFx *DmaFx,
_In_ NET_PACKET *NetPacket
)
/*
Description:
This function tries to bounce the buffers in a NET_PACKET and build the
necessary SCATTER_GATHER_LIST to map the new buffer. If it succeeds it
calls EvtSgProgramDescriptors to let the NIC driver do whatever is needed
to program the buffer to hardware.
*/
{
union
{
SCATTER_GATHER_LIST sgl;
UCHAR sglStorage[FIELD_OFFSET(SCATTER_GATHER_LIST, Elements) + sizeof(SCATTER_GATHER_ELEMENT)];
} u;
// Is there a free bounce buffer?
if (DmaFx->BounceBusyIndex - DmaFx->BounceFreeIndex == DmaFx->NumBounceBuffers)
{
DmaFx->Statistics.Packet.BounceFailure += 1;
return STATUS_INSUFFICIENT_RESOURCES;
}
ULONG bounce = DmaFx->BounceBusyIndex % DmaFx->NumBounceBuffers;
PVOID buffer = (UCHAR*)DmaFx->BounceBaseVA + bounce * DmaFx->BounceBufferSize;
ULONG packetLength = _TxDmaFxCopyPacketToBuffer(NetPacket, buffer, DmaFx->BounceBufferSize);
u.sgl.NumberOfElements = 1;
u.sgl.Elements[0].Length = packetLength;
u.sgl.Elements[0].Address.QuadPart = DmaFx->BounceBasePA.QuadPart + bounce * DmaFx->BounceBufferSize;
TX_DMA_FX_PROGRAM_DESCRIPTORS(
DmaFx->QueueHandle,
NetPacket,
&u.sgl);
_TX_DMA_FX_PACKET_SET_BOUNCED_FLAG(NetPacket);
DmaFx->Statistics.Packet.BounceSuccess += 1;
DmaFx->BounceBusyIndex += 1;
return STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
__inline
TX_DMA_BOUNCE_ANALYSIS
_TxDmaFxBounceAnalysis(
_In_ TxDmaFx *DmaFx,
_In_ NET_PACKET *NetPacket
)
{
ULONG alignmentRequirement = DmaFx->Config.AlignmentRequirement;
ULONG maximumScatterGatherElements = DmaFx->Config.MaximumScatterGatherElements;
ULONG maximumPacketSize = DmaFx->Config.MaximumPacketSize;
BOOLEAN checkAddrWidth = DmaFx->Config.AddressWidth != 0;
ULONG numDescriptorsRequired = 0;
ULONGLONG totalPacketSize = 0;
BOOLEAN bounce = FALSE;
for (NET_PACKET_FRAGMENT *fragment = &NetPacket->Data;
fragment;
fragment = NET_PACKET_FRAGMENT_GET_NEXT(fragment))
{
// If a fragment other than the first one has an offset, the DMA
// APIs won't be able to properly map the buffers.
if (fragment->Offset > 0 && fragment != &NetPacket->Data)
bounce = TRUE;
// We know for sure there is a MDL pointer in the fragment,
// so disable the loss of data warning
#pragma warning (push)
#pragma warning (disable : 4305)
MDL *mdl = (MDL*)fragment->DmaLogicalAddress.QuadPart;
#pragma warning (pop)
// If a fragment other than the last one does not completly fill
// the memory described by the MDL, the DMA APIs won't be able to
// properly map the buffers
if (fragment->Offset + fragment->ValidLength < MmGetMdlByteCount(mdl) && !fragment->LastFragmentOfFrame)
bounce = TRUE;
// Calculate how many Scatter/Gather elements we need to transmit
// this fragment.
// This is overly pessimistic if the physical pages are contiguous.
ULONG_PTR va = (ULONG_PTR)fragment->VirtualAddress + (ULONG)fragment->Offset;
numDescriptorsRequired += (ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(va, fragment->ValidLength);
totalPacketSize += fragment->ValidLength;
// If the configuration has an alignment requirement, check if the virtual
// address meets the requirement. We're assuming the alignment requirement
// is less than 4096.
if ((alignmentRequirement != -1) && (va & alignmentRequirement) != 0)
bounce = TRUE;
if (checkAddrWidth)
{
PHYSICAL_ADDRESS pa = MmGetPhysicalAddress((VOID*)va);
if ((ULONGLONG)pa.QuadPart > DmaFx->MaximumAddress)
bounce = TRUE;
}
if (fragment->LastFragmentOfFrame)
break;
}
// First check if we can transmit the buffers at all
if (maximumPacketSize != 0 && totalPacketSize > maximumPacketSize)
return TxDmaCannotTransmit;
// Then check if we detected any condition that requires
// buffer bouncing
if (bounce)
return TxDmaTransmitAfterBouncing;
// Lastly check if we can DMA the number of required descriptors
if (numDescriptorsRequired > maximumScatterGatherElements)
return TxDmaTransmitAfterBouncing;
return TxDmaTransmitInPlace;
}
#endif

Просмотреть файл

@ -0,0 +1,261 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#ifndef _TXDMAFXTYPES_H_
#define _TXDMAFXTYPES_H_
#define _TX_DMA_FX_RETURN_IF_NTSTATUS_FAILED(status) { NTSTATUS __statusRet = (status); if (__statusRet < 0) { return __statusRet;} }
#define _TX_DMA_FX_IS_POWER_OF_TWO(_n) (((_n) != 0) && !((_n) & ((_n) - 1)))
#define _TX_DMA_FX_PACKET_SET_BOUNCED_FLAG(_packet) (_packet)->Data.Scratch = TRUE
#define _TX_DMA_FX_PACKET_CLEAR_BOUNCED_FLAG(_packet) (_packet)->Data.Scratch = FALSE
#define _TX_DMA_FX_IS_PACKET_BOUNCED(_packet) (_packet)->Data.Scratch
#define _TX_DMA_FX_NUM_BOUNCE_BUFFERS ( 1 << 4 )
#define _TX_DMA_FX_DEFAULT_SCATTER_GATHER_ELEMENTS 16
#ifndef TX_DMA_FX_ALLOC_TAG
#pragma message(": warning: It is a good practice to define TX_DMA_FX_ALLOC_TAG. Defaulting to WdfDriverGlobals->DriverTag.")
#define TX_DMA_FX_ALLOC_TAG WdfDriverGlobals->DriverTag
#endif
typedef enum _TX_DMA_BOUNCE_ANALYSIS
{
TxDmaTransmitInPlace,
TxDmaTransmitAfterBouncing,
TxDmaCannotTransmit,
} TX_DMA_BOUNCE_ANALYSIS;
typedef
_Function_class_(EVT_TX_DMA_QUEUE_BOUNCE_ANALYSIS)
_IRQL_requires_same_
_IRQL_requires_max_(DISPATCH_LEVEL)
TX_DMA_BOUNCE_ANALYSIS
EVT_TX_DMA_QUEUE_BOUNCE_ANALYSIS(
_In_ NETTXQUEUE TxQueue,
_In_ NET_PACKET *NetPacket
);
typedef EVT_TX_DMA_QUEUE_BOUNCE_ANALYSIS *PFN_SG_TXQUEUE_BOUNCE_ANALYSIS;
typedef
_Function_class_(EVT_TX_DMA_QUEUE_PROGRAM_DESCRIPTORS)
_IRQL_requires_same_
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
EVT_TX_DMA_QUEUE_PROGRAM_DESCRIPTORS(
_In_ NETTXQUEUE TxQueue,
_In_ NET_PACKET *NetPacket,
_In_ SCATTER_GATHER_LIST *Sgl
);
typedef EVT_TX_DMA_QUEUE_PROGRAM_DESCRIPTORS *PFN_SG_TXQUEUE_PROGRAM_DESCRIPTORS;
typedef
_Function_class_(EVT_TX_DMA_QUEUE_GET_PACKET_STATUS)
_IRQL_requires_same_
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
EVT_TX_DMA_QUEUE_GET_PACKET_STATUS(
_In_ NETTXQUEUE TxQueue,
_In_ NET_PACKET *NetPacket
);
typedef EVT_TX_DMA_QUEUE_GET_PACKET_STATUS *PFN_SG_TXQUEUE_GET_PACKET_STATUS;
typedef
_Function_class_(EVT_TX_DMA_QUEUE_FLUSH_TRANSACTION)
_IRQL_requires_same_
_IRQL_requires_max_(DISPATCH_LEVEL)
VOID
EVT_TX_DMA_QUEUE_FLUSH_TRANSACTION(
_In_ NETTXQUEUE TxQueue
);
typedef EVT_TX_DMA_QUEUE_FLUSH_TRANSACTION *PFN_SG_TXQUEUE_FLUSH_TRANSACTION;
typedef struct _NET_TX_DMA_QUEUE_CONFIG
{
//
// Has information needed by DMA APIs
//
WDFDEVICE Device;
//
// DMA enabler to use when mapping/unmapping buffers
//
WDFDMAENABLER DmaEnabler;
//
// In certain conditions the framework might be able
// to bypass DMA APIs and map buffers directly, set
// this to TRUE if you want to allow this behavior
//
BOOLEAN AllowDmaBypass;
//
// 0 for no limit
//
ULONG MaximumScatterGatherElements;
//
// Maximum packet size, in bytes, the Tx Queue can transmit
//
ULONG MaximumPacketSize;
//
// FILE_XXX_ALIGNMENT, or -1 to determine automatically
//
ULONG AlignmentRequirement;
//
// 2 ^ AddressWidth gives the maximum physical address supported by the hardware
//
ULONG AddressWidth;
//
// Mandatory Callback - Normal EvtSetNotificationEnabled callback
//
PFN_TXQUEUE_SET_NOTIFICATION_ENABLED EvtTxQueueSetNotificationEnabled;
//
// Mandatory Callback - Normal EvtCancel callback
//
PFN_TXQUEUE_CANCEL EvtTxQueueCancel;
//
// NIC driver packet context information
//
PCNET_CONTEXT_TYPE_INFO ContextTypeInfo;
} NET_TX_DMA_QUEUE_CONFIG, *PNET_TX_DMA_QUEUE_CONFIG;
VOID
FORCEINLINE
NET_TX_DMA_QUEUE_CONFIG_INIT(
_Out_ PNET_TX_DMA_QUEUE_CONFIG NetTxQueueConfig,
_In_ WDFDEVICE Device,
_In_ WDFDMAENABLER DmaEnabler,
_In_ ULONG MaximumPacketSize,
_In_ PFN_TXQUEUE_SET_NOTIFICATION_ENABLED EvtTxQueueSetNotificationEnabled,
_In_ PFN_TXQUEUE_CANCEL EvtTxQueueCancel
)
{
RtlZeroMemory(NetTxQueueConfig, sizeof(*NetTxQueueConfig));
NetTxQueueConfig->Device = Device;
NetTxQueueConfig->DmaEnabler = DmaEnabler;
NetTxQueueConfig->EvtTxQueueSetNotificationEnabled = EvtTxQueueSetNotificationEnabled;
NetTxQueueConfig->EvtTxQueueCancel = EvtTxQueueCancel;
NetTxQueueConfig->MaximumPacketSize = MaximumPacketSize;
NetTxQueueConfig->AlignmentRequirement = (ULONG)-1;
}
#define NET_TX_DMA_QUEUE_CONFIG_SET_DEFAULT_PACKET_CONTEXT_TYPE(_txdmaconfig, _type) \
(_txdmaconfig)->ContextTypeInfo = WDF_GET_CONTEXT_TYPE_INFO(_type); \
typedef struct _TxDmaFxStats
{
struct _Packet
{
// How many NET_PACKETs with IgnoreThisPacket flag set
ULONG Skipped;
// Number of times we had to bounce a packet and succeeded
ULONG BounceSuccess;
// Number of times we had to bounce a packet but failed
ULONG BounceFailure;
// Number of times the bounce analysis returned TxDmaCannotTransmit
ULONG CannotTransmit;
// Number of times TX_DMA_FX_GET_PACKET_STATUS returned an error
ULONG CompletedWithError;
} Packet;
struct _DMA
{
// Number of times an attempt to transmit a packet using DMA APIs
// returned STATUS_INSUFFICIENT_RESOURCES
ULONG InsufficientResourcesCount;
// Counts other errors from DMA APIs
ULONG OtherErrors;
} DMA;
} TxDmaFxStats;
typedef struct _TxDmaFx
{
// Queue handle, created by the framework
NETTXQUEUE QueueHandle;
// Copy of the configuration provided by
// the NIC driver
NET_TX_DMA_QUEUE_CONFIG Config;
// Cache of the NET_RING_BUFFER pointer
NET_RING_BUFFER *RingBuffer;
// If the NIC driver provided the AddressWidth
// parameter in the configuration, this stores
// the maximum physical address supported by
// the hardware
ULONGLONG MaximumAddress;
// Used to retrieve the framework's packet
// context from a NET_PACKET
PCNET_CONTEXT_TYPE_INFO FxPacketContextTypeInfo;
ULONG_PTR FxPacketContextOffset;
// WDM objects
DEVICE_OBJECT *DeviceObject;
DMA_ADAPTER *DmaAdapter;
// If TRUE, we can bypass HAL's DMA APIs
BOOLEAN DmaBypass;
// Used when we can do direct mapping
SCATTER_GATHER_LIST *SpareSgl;
// Used when we need to use HAL APIs
VOID *SgListMem;
ULONG ScatterGatherListSize;
// Bounce buffer management
PHYSICAL_ADDRESS BounceBasePA;
VOID *BounceBaseVA;
ULONG BounceBufferSize;
ULONG NumBounceBuffers;
ULONG BounceFreeIndex;
ULONG BounceBusyIndex;
TxDmaFxStats Statistics;
} TxDmaFx;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(TxDmaFx, _TxDmaFxGetContext);
// Used only with HAL APIs
typedef struct _TX_DMA_FX_PACKET_CONTEXT
{
SCATTER_GATHER_LIST *ScatterGatherList;
VOID *ScatterGatherBuffer;
VOID *DmaTransferContext;
} TX_DMA_FX_PACKET_CONTEXT;
__inline
TX_DMA_FX_PACKET_CONTEXT *
_TxDmaFxGetPacketContext(
_In_ NET_PACKET *NetPacket,
_In_ TxDmaFx *DmaFx
)
{
UCHAR *context = (UCHAR*)NetPacketGetTypedContext(NetPacket, DmaFx->FxPacketContextTypeInfo);
return (TX_DMA_FX_PACKET_CONTEXT *)(context + DmaFx->FxPacketContextOffset);
}
EVT_TXQUEUE_ADVANCE _TxDmaFxAdvance;
#endif

Просмотреть файл

@ -1,3 +1,17 @@
# NetAdapter Class Extension Samples
The Network Adapter Class Extension to WDF (NetAdapter Cx) brings together the productivity of WDF with the networking performance of NDIS, and makes it easy to write a driver for your NIC.
Here you can find all the latest samples of the NetAdapter class extension.
# Contents
- [RTL8168D Sample Driver](RtEthSample/README.md)
- [DMA IOHelper](IoHelpers/dma)
# Contributing
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
# Related Repos
NetAdapter Class Extension itself is open source on GitHub [Network-Adapter-Class-Extension](https://github.com/Microsoft/Network-Adapter-Class-Extension).

122
RtEthSample/README.md Normal file
Просмотреть файл

@ -0,0 +1,122 @@
# RTL8168D Sample Driver
This is an implementation of a NetAdapter driver targetting the RTL8168D chipset. It implements OID handling, data path, and some basic offloads, including checksum offload.
## How to use this sample
This sample will guide you through implementing a complete network driver using the NetAdapter Class Extension to WDF.
## Landmarks
### [DriverEntry](driver.cpp)
This is where your driver gets started. Check out DriverEntry to see how to initialize a WDF Driver with logging. Then check out EvtDriverDeviceAdd to see how to initialize a device with a NetAdapter object.
### [WdfDevice](device.cpp)
One of the biggest benefits of the new NetAdapter class extension is that you have full access to the WDF framework. This contains the implementation of the RTL8168D resource assignment.
### [NetAdapter](adapter.cpp)
Check out the unique features of the NetAdapter class extension. Here's where the adapter reports capabilities to the network stack and creates its datapath queues.
### [Power](power.cpp)
NetAdapter drivers can take full advantage of the WDF power state machine. Check out how power tranisitions are implemented, and how NetAdapter concepts like WakeOnMagicPacket integrate directly with WDF power transitions.
### [NetRequestQueue](oid.cpp)
The adapter's control path is how the network stack sets and queries adapter parameters at runtime. NetAdapter allows you to provide specific query/set handlers that only apply to certain requests. Specific handlers reduce cognitive overhead while writing and understanding the query/set code.
### [NetTxQueue](txqueue.cpp)
The sample driver implements its transmit data queue using the DMA IO Helper. This IO Helper simplifies scatter gather DMA operations and mapping NET_PACKET descriptors to RTL8168D's software transmit descriptors.
### [NetRxQueue](rxqueue.cpp)
Implement your receive queue in 200 lines of code! The NetAdapter data fits perfectly with RTL8168D's data path.
## Debugging and Development
### Build the Driver
Prerequisites:
- Visual Studio 2015
- Windows SDK for Windows 10, version 1703
- WDK for Windows 10, version 1703
Instructions to install all of these are available [here on MSDN](https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit).
The driver can be built using Visual Studio - open [the solution file](RtEthSample.sln) in Visual Studio and use the Build menu to build the driver. The driver can also be built from the command line using msbuild.exe.
### Build Output
The driver package will be available, relative to this directory, in:
```
; x86
Debug\RtEthSample
; amd64
x64\Debug\RtEthSample
```
### Test Machine Setup
First, locate and install the RTL8168D NIC into your test machine.
Next, [enable kernel debugging](https://msdn.microsoft.com/en-us/library/windows/hardware/hh450944(v=vs.85).aspx).
You'll also need to [enable test signing](https://msdn.microsoft.com/en-us/windows/hardware/drivers/install/the-testsigning-boot-configuration-option) to allow you to install your unsigned driver.
### Installing the Driver
Open Device Manager: Win+X, M
Expand the "Network Adapters" tab.
Find the target device - for the RTL8168D, typically "Realtek PCIe GBE Family Controller".
Right click, click "Update Driver".
Click "Browse my computer for driver software"
Click "Let me pick from a list of available drivers on my computer"
Click "Have Disk..."
Browse to driver package
Click "OK"
Select "Realtek PCIe GBE Family Controller NetAdapter Sample Driver" and click Next
### Enable Logging
You can enable logging to the debug console in WinDbg by using !wmitrace.
```
!wmitrace.start RtTrace -kd
!wmitrace.enable RtTrace {5D364AAF-5B49-41A0-9E03-D3CB2AA2E03E}
```
### .kdfiles
Iterate quickly using the windbg .kdfiles command to quickly replace your driver binary.
Add a kdfiles entry in the kernel debugger using -m.
```
.kdfiles -m RtEthSample.sys C:\directory\to\new\RtEthSample.sys
```
You can also use a map file. For example:
```
; kdmap.txt
map
RtEthSample.sys
C:\directory\to\new\RtEthSample.sys
```
Then, in WinDbg:
```
.kdfiles .\path\to\kdmap.txt
```
Whenever the driver is loaded, your new driver will be used.
## Known Issues
- IPv6 Checksum Offload not yet implemented
- Windows 1703 bugchecks when OS tries to send packet with 20 or more fragments

Двоичные данные
RtEthSample/RtEthSample.inx Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,32 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RtEthSample", "RtEthSample.vcxproj", "{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Debug|x64.ActiveCfg = Debug|x64
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Debug|x64.Build.0 = Debug|x64
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Debug|x64.Deploy.0 = Debug|x64
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Debug|x86.ActiveCfg = Debug|Win32
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Debug|x86.Build.0 = Debug|Win32
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Debug|x86.Deploy.0 = Debug|Win32
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Release|x64.ActiveCfg = Release|x64
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Release|x64.Build.0 = Release|x64
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Release|x64.Deploy.0 = Release|x64
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Release|x86.ActiveCfg = Release|Win32
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Release|x86.Build.0 = Release|Win32
{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}.Release|x86.Deploy.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="PropertySheets">
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>KMDF</DriverType>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{EAC78963-C6D0-4C8C-918D-5B5996AE80AC}</ProjectGuid>
<RootNamespace>$(MSBuildProjectName)</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<NetAdapterDriver>true</NetAdapterDriver>
<NETADAPTER_VERSION_MAJOR>1</NETADAPTER_VERSION_MAJOR>
<NETADAPTER_VERSION_MINOR>0</NETADAPTER_VERSION_MINOR>
<UseDebugLibraries Condition="'$(Configuration)'=='Debug'">True</UseDebugLibraries>
<UseDebugLibraries Condition="'$(Configuration)'=='Release'">False</UseDebugLibraries>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<NetAdapterDriver>true</NetAdapterDriver>
<NETADAPTER_VERSION_MAJOR>1</NETADAPTER_VERSION_MAJOR>
<NETADAPTER_VERSION_MINOR>0</NETADAPTER_VERSION_MINOR>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<KMDF_VERSION_MINOR>21</KMDF_VERSION_MINOR>
</PropertyGroup>
<!-- Needed by any VcxProj -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<EnableInf2cat>true</EnableInf2cat>
</PropertyGroup>
<ItemDefinitionGroup>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>$(DDK_LIB_PATH)\ndis.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\iohelpers\dma;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Inf>
<VersionHeaderPath>
</VersionHeaderPath>
<SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
<TimeStamp>1.0</TimeStamp>
</Inf>
</ItemDefinitionGroup>
<!-- The WrappedTaskItems label is used by the conversion tool to identify the location where items
associated with wrapped tasks will reside.-->
<ItemGroup Label="WrappedTaskItems">
<ClInclude Include="adapter.h" />
<ClInclude Include="configuration.h" />
<ClInclude Include="device.h" />
<ClInclude Include="forward.h" />
<ClInclude Include="interrupt.h" />
<ClInclude Include="link.h" />
<ClInclude Include="oid.h" />
<ClInclude Include="phy.h" />
<ClInclude Include="power.h" />
<ClInclude Include="precomp.h" />
<ClInclude Include="rt_def.h" />
<ClInclude Include="rxqueue.h" />
<ClInclude Include="statistics.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="txqueue.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="adapter.cpp" />
<ClCompile Include="configuration.cpp" />
<ClCompile Include="device.cpp" />
<ClCompile Include="driver.cpp" />
<ClCompile Include="interrupt.cpp" />
<ClCompile Include="link.cpp" />
<ClCompile Include="oid.cpp" />
<ClCompile Include="phy.cpp" />
<ClCompile Include="power.cpp" />
<ClCompile Include="rxqueue.cpp" />
<ClCompile Include="statistics.cpp" />
<ClCompile Include="txqueue.cpp" />
<ResourceCompile Include="rtk.rc" />
<FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
<FilesToPackage Include="$(DDK_PACKAGE_FILES)" />
<Inf Include="RtEthSample.inx" />
</ItemGroup>
<!-- Set default environment variables, e.g. for stampinf -->
<ItemGroup>
<BuildMacro Include="SDK_INC_PATH">
<Value>$(KIT_SHARED_INC_PATH)</Value>
<EnvironmentVariable>true</EnvironmentVariable>
</BuildMacro>
</ItemGroup>
<!-- /Necessary to pick up proper files from local directory when in the IDE-->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{70529b02-b3b3-4b26-bc58-0e987c13c435}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{6036ba3d-662c-485a-a71c-c336fe98a1a7}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{f25fc16e-56a7-4988-8dc8-eef86d6077bc}</UniqueIdentifier>
</Filter>
<Filter Include="Setup Files">
<UniqueIdentifier>{b8d69e00-9ddf-4a09-846c-7a45e490c44a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="precomp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rt_def.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="adapter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="device.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="oid.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="txqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rxqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="txqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="configuration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="statistics.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="link.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="phy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="power.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="forward.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="interrupt.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="driver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="adapter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="device.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="oid.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="txqueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rxqueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="txqueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="configuration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="statistics.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="link.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="phy.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="power.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="interrupt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="rtk.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Inf Include="RtEthSample.inx">
<Filter>Setup Files</Filter>
</Inf>
</ItemGroup>
</Project>

796
RtEthSample/adapter.cpp Normal file
Просмотреть файл

@ -0,0 +1,796 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "device.h"
#include "adapter.h"
#include "oid.h"
#include "txqueue.h"
#include "rxqueue.h"
NTSTATUS
RtInitializeAdapterContext(
_In_ RT_ADAPTER *adapter,
_In_ WDFDEVICE device,
_In_ NETADAPTER netAdapter)
/*++
Routine Description:
Allocate RT_ADAPTER data block and do some initialization
Arguments:
adapter Pointer to receive pointer to our adapter
Return Value:
NTSTATUS failure code, or STATUS_SUCCESS
--*/
{
TraceEntry();
NTSTATUS status = STATUS_SUCCESS;
adapter->NetAdapter = netAdapter;
adapter->WdfDevice = device;
//
// Get WDF miniport device context.
//
RtGetDeviceContext(adapter->WdfDevice)->Adapter = adapter;
adapter->OffloadEncapsulation.Header.Revision = NDIS_OFFLOAD_ENCAPSULATION_REVISION_1;
adapter->OffloadEncapsulation.Header.Size = NDIS_SIZEOF_OFFLOAD_ENCAPSULATION_REVISION_1;
adapter->OffloadEncapsulation.Header.Type = NDIS_OBJECT_TYPE_OFFLOAD_ENCAPSULATION;
//spinlock
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = adapter->WdfDevice;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfSpinLockCreate(&attributes, &adapter->Lock));
Exit:
TraceExitResult(status);
return status;
}
void
RtAdapterQueryOffloadConfiguration(
_In_ RT_ADAPTER const *adapter,
_Out_ NDIS_OFFLOAD *offloadCaps)
{
RtlZeroMemory(offloadCaps, sizeof(*offloadCaps));
offloadCaps->Header.Type = NDIS_OBJECT_TYPE_OFFLOAD;
offloadCaps->Header.Size = NDIS_SIZEOF_NDIS_OFFLOAD_REVISION_5;
offloadCaps->Header.Revision = NDIS_OFFLOAD_REVISION_5;
// IPv4 : Tx
offloadCaps->Checksum.IPv4Transmit.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
if (adapter->IPChksumOffv4 == RtChecksumOffloadTxEnabled ||
adapter->IPChksumOffv4 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv4Transmit.IpChecksum = NDIS_OFFLOAD_SUPPORTED;
offloadCaps->Checksum.IPv4Transmit.IpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
}
if (adapter->TCPChksumOffv4 == RtChecksumOffloadTxEnabled ||
adapter->TCPChksumOffv4 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv4Transmit.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
offloadCaps->Checksum.IPv4Transmit.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
}
if (adapter->UDPChksumOffv4 == RtChecksumOffloadTxEnabled ||
adapter->UDPChksumOffv4 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv4Transmit.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
}
// IPv4 : Rx
offloadCaps->Checksum.IPv4Receive.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
if (adapter->IPChksumOffv4 == RtChecksumOffloadRxEnabled ||
adapter->IPChksumOffv4 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv4Receive.IpChecksum = NDIS_OFFLOAD_SUPPORTED;
offloadCaps->Checksum.IPv4Receive.IpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
}
if (adapter->TCPChksumOffv4 == RtChecksumOffloadRxEnabled ||
adapter->TCPChksumOffv4 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv4Receive.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
offloadCaps->Checksum.IPv4Receive.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
}
if (adapter->UDPChksumOffv4 == RtChecksumOffloadRxEnabled ||
adapter->UDPChksumOffv4 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv4Receive.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
}
// IPv6 : Tx
offloadCaps->Checksum.IPv6Transmit.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
offloadCaps->Checksum.IPv6Transmit.IpExtensionHeadersSupported = NDIS_OFFLOAD_SUPPORTED;
if (adapter->TCPChksumOffv6 == RtChecksumOffloadTxEnabled ||
adapter->TCPChksumOffv6 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv6Transmit.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
offloadCaps->Checksum.IPv6Transmit.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
}
if (adapter->UDPChksumOffv6 == RtChecksumOffloadTxEnabled ||
adapter->UDPChksumOffv6 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv6Transmit.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
}
// IPv6 : Rx
offloadCaps->Checksum.IPv6Receive.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
offloadCaps->Checksum.IPv6Receive.IpExtensionHeadersSupported = NDIS_OFFLOAD_SUPPORTED;
if (adapter->TCPChksumOffv6 == RtChecksumOffloadRxEnabled ||
adapter->TCPChksumOffv6 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv6Receive.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
offloadCaps->Checksum.IPv6Receive.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
}
if (adapter->UDPChksumOffv6 == RtChecksumOffloadRxEnabled ||
adapter->UDPChksumOffv6 == RtChecksumOffloadTxRxEnabled)
{
offloadCaps->Checksum.IPv6Receive.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
}
}
_Use_decl_annotations_
void
RtAdapterUpdateEnabledChecksumOffloads(_In_ RT_ADAPTER *adapter)
{
adapter->IpRxHwChkSumv4 =
(adapter->IPChksumOffv4 == RtChecksumOffloadRxEnabled ||
adapter->IPChksumOffv4 == RtChecksumOffloadTxRxEnabled);
adapter->TcpRxHwChkSumv4 =
(adapter->TCPChksumOffv4 == RtChecksumOffloadRxEnabled ||
adapter->TCPChksumOffv4 == RtChecksumOffloadTxRxEnabled);
adapter->UdpRxHwChkSumv4 =
(adapter->UDPChksumOffv4 == RtChecksumOffloadRxEnabled ||
adapter->UDPChksumOffv4 == RtChecksumOffloadTxRxEnabled);
adapter->TcpRxHwChkSumv6 =
(adapter->TCPChksumOffv6 == RtChecksumOffloadRxEnabled ||
adapter->TCPChksumOffv6 == RtChecksumOffloadTxRxEnabled);
adapter->UdpRxHwChkSumv6 =
(adapter->UDPChksumOffv6 == RtChecksumOffloadRxEnabled ||
adapter->UDPChksumOffv6 == RtChecksumOffloadTxRxEnabled);
WdfSpinLockAcquire(adapter->Lock); {
USHORT cpcr = adapter->CSRAddress->CPCR;
// if one kind of offload needed, enable RX HW checksum
if (adapter->IpRxHwChkSumv4 || adapter->TcpRxHwChkSumv4 ||
adapter->UdpRxHwChkSumv4 || adapter->TcpRxHwChkSumv6 ||
adapter->UdpRxHwChkSumv6)
{
cpcr |= CPCR_RX_CHECKSUM;
}
else
{
cpcr &= ~CPCR_RX_CHECKSUM;
}
adapter->CSRAddress->CPCR = cpcr;
} WdfSpinLockRelease(adapter->Lock);
}
void
RtAdapterQueryHardwareCapabilities(
_Out_ NDIS_OFFLOAD *hardwareCaps)
{
RtlZeroMemory(hardwareCaps, sizeof(*hardwareCaps));
hardwareCaps->Header.Type = NDIS_OBJECT_TYPE_OFFLOAD;
hardwareCaps->Header.Size = NDIS_SIZEOF_NDIS_OFFLOAD_REVISION_5;
hardwareCaps->Header.Revision = NDIS_OFFLOAD_REVISION_5;
// IPv4 checksum offloads supported
hardwareCaps->Checksum.IPv4Transmit.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
hardwareCaps->Checksum.IPv4Transmit.IpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Transmit.IpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Transmit.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Transmit.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Transmit.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Receive.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
hardwareCaps->Checksum.IPv4Receive.IpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Receive.IpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Receive.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Receive.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv4Receive.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
// IPv6 checksum offloads supported
hardwareCaps->Checksum.IPv6Transmit.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
hardwareCaps->Checksum.IPv6Transmit.IpExtensionHeadersSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Transmit.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Transmit.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Transmit.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Receive.Encapsulation = NDIS_ENCAPSULATION_IEEE_802_3;
hardwareCaps->Checksum.IPv6Receive.IpExtensionHeadersSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Receive.TcpChecksum = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Receive.TcpOptionsSupported = NDIS_OFFLOAD_SUPPORTED;
hardwareCaps->Checksum.IPv6Receive.UdpChecksum = NDIS_OFFLOAD_SUPPORTED;
}
_Use_decl_annotations_
NTSTATUS
EvtAdapterSetCapabilities(
_In_ NETADAPTER netAdapter)
{
TraceEntryNetAdapter(netAdapter);
NTSTATUS status = STATUS_SUCCESS;
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
#pragma region Link Layer Capabilities (Required)
ULONG64 maxXmitLinkSpeed = RT_MEDIA_MAX_SPEED;
ULONG64 maxRcvLinkSpeed = RT_MEDIA_MAX_SPEED;
NET_ADAPTER_LINK_LAYER_CAPABILITIES linkLayerCapabilities;
NET_ADAPTER_LINK_LAYER_CAPABILITIES_INIT(
&linkLayerCapabilities,
RT_SUPPORTED_FILTERS,
RT_MAX_MCAST_LIST,
NIC_SUPPORTED_STATISTICS,
maxXmitLinkSpeed,
maxRcvLinkSpeed,
ETH_LENGTH_OF_ADDRESS,
adapter->PermanentAddress,
adapter->CurrentAddress);
NetAdapterSetLinkLayerCapabilities(netAdapter, &linkLayerCapabilities);
#pragma endregion
#pragma region MTU Size (Required)
NetAdapterSetLinkLayerMtuSize(netAdapter, RT_MAX_PACKET_SIZE - ETH_LENGTH_OF_HEADER);
#pragma endregion
#pragma region Power Capabilities (Optional)
NET_ADAPTER_POWER_CAPABILITIES powerCapabilities;
NET_ADAPTER_POWER_CAPABILITIES_INIT(&powerCapabilities);
if (adapter->WakeOnMagicPacketEnabled)
{
powerCapabilities.SupportedWakePatterns = NET_ADAPTER_WAKE_MAGIC_PACKET;
}
NetAdapterSetPowerCapabilities(netAdapter, &powerCapabilities);
#pragma endregion
#pragma region Datapath Capabilities (Optional)
NET_ADAPTER_DATAPATH_CAPABILITIES dataPathCapabilities;
NET_ADAPTER_DATAPATH_CAPABILITIES_INIT(&dataPathCapabilities);
dataPathCapabilities.NumTxQueues = 1;
dataPathCapabilities.NumRxQueues = 1;
NetAdapterSetDataPathCapabilities(netAdapter, &dataPathCapabilities);
#pragma endregion
#pragma region Offload Attributes (Legacy)
NDIS_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES offloadAttributes;
RtlZeroMemory(&offloadAttributes, sizeof(NDIS_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES));
offloadAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES;
offloadAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES);
offloadAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_OFFLOAD_ATTRIBUTES_REVISION_1;
NDIS_OFFLOAD hardwareCaps;
RtAdapterQueryHardwareCapabilities(&hardwareCaps);
offloadAttributes.HardwareOffloadCapabilities = &hardwareCaps;
NDIS_OFFLOAD offloadCaps;
RtAdapterQueryOffloadConfiguration(adapter, &offloadCaps);
offloadAttributes.DefaultOffloadConfiguration = &offloadCaps;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NdisMSetMiniportAttributes(
adapter->NdisLegacyAdapterHandle,
reinterpret_cast<NDIS_MINIPORT_ADAPTER_ATTRIBUTES*>(
&offloadAttributes)));
#pragma endregion
Exit:
TraceExitResult(status);
return status;
}
_Use_decl_annotations_
NTSTATUS
EvtAdapterCreateTxQueue(
_In_ NETADAPTER netAdapter,
_Inout_ PNETTXQUEUE_INIT txQueueInit)
{
NTSTATUS status = STATUS_SUCCESS;
TraceEntryNetAdapter(netAdapter);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
#pragma region Create NETTXQUEUE
WDF_OBJECT_ATTRIBUTES txAttributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&txAttributes, RT_TXQUEUE);
txAttributes.EvtDestroyCallback = EvtTxQueueDestroy;
NET_TX_DMA_QUEUE_CONFIG sgConfig;
NET_TX_DMA_QUEUE_CONFIG_INIT(
&sgConfig,
adapter->WdfDevice,
adapter->DmaEnabler,
RT_MAX_PACKET_SIZE,
EvtTxQueueSetNotificationEnabled,
EvtTxQueueCancel);
sgConfig.MaximumScatterGatherElements = RT_MAX_PHYS_BUF_COUNT;
sgConfig.AllowDmaBypass = TRUE;
NET_TX_DMA_QUEUE_CONFIG_SET_DEFAULT_PACKET_CONTEXT_TYPE(&sgConfig, RT_TCB);
NETTXQUEUE txQueue;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetTxDmaQueueCreate(
txQueueInit,
&txAttributes,
&sgConfig,
&txQueue));
#pragma endregion
#pragma region Initialize RTL8168D Transmit Queue
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtTxQueueInitialize(txQueue, adapter));
#pragma endregion
WdfSpinLockAcquire(adapter->Lock); {
RT_TXQUEUE *tx = RtGetTxQueueContext(txQueue);
RtTxQueueStart(tx);
adapter->TxQueue = txQueue;
} WdfSpinLockRelease(adapter->Lock);
Exit:
TraceExitResult(status);
return status;
}
_Use_decl_annotations_
NTSTATUS
EvtAdapterCreateRxQueue(
_In_ NETADAPTER netAdapter,
_Inout_ PNETRXQUEUE_INIT rxQueueInit)
{
NTSTATUS status = STATUS_SUCCESS;
TraceEntryNetAdapter(netAdapter);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
#pragma region Create NETRXQUEUE
WDF_OBJECT_ATTRIBUTES rxAttributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&rxAttributes, RT_RXQUEUE);
rxAttributes.EvtDestroyCallback = EvtRxQueueDestroy;
NET_RXQUEUE_CONFIG rxConfig;
NET_RXQUEUE_CONFIG_INIT(
&rxConfig,
EvtRxQueueAdvance,
EvtRxQueueSetNotificationEnabled,
EvtRxQueueCancel);
rxConfig.AlignmentRequirement = FILE_64_BYTE_ALIGNMENT;
rxConfig.AllocationSize =
RT_MAX_PACKET_SIZE + FRAME_CRC_SIZE + RSVD_BUF_SIZE;
NETRXQUEUE rxQueue;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetRxQueueCreate(rxQueueInit, &rxAttributes, &rxConfig, &rxQueue));
#pragma endregion
#pragma region Initialize RTL8168D Receive Queue
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtRxQueueInitialize(rxQueue, adapter));
#pragma endregion
WdfSpinLockAcquire(adapter->Lock); {
// Starting the receive queue must be synchronized with any OIDs that
// modify the receive queue's behavior.
RtRxQueueStart(RtGetRxQueueContext(rxQueue));
adapter->RxQueue = rxQueue;
} WdfSpinLockRelease(adapter->Lock);
Exit:
TraceExitResult(status);
return status;
}
NTSTATUS
RtAdapterReadAddress(
_In_ RT_ADAPTER *adapter)
/*++
Routine Description:
Read the mac addresss from the adapter
Arguments:
adapter Pointer to our adapter
Return Value:
STATUS_SUCCESS
STATUS_NDIS_INVALID_ADDRESS
--*/
{
TraceEntryRtAdapter(adapter);
NTSTATUS status = STATUS_SUCCESS;
for (UINT i = 0; i < ETHERNET_ADDRESS_LENGTH; i++)
{
adapter->PermanentAddress[i] = adapter->CSRAddress->ID[i];
}
if (ETH_IS_MULTICAST(adapter->PermanentAddress) ||
ETH_IS_BROADCAST(adapter->PermanentAddress))
{
NdisWriteErrorLogEntry(
adapter->NdisLegacyAdapterHandle,
NDIS_ERROR_CODE_NETWORK_ADDRESS,
0);
GOTO_IF_NOT_NT_SUCCESS(Exit, status, STATUS_NDIS_INVALID_ADDRESS);
}
if (!adapter->OverrideAddress)
{
memcpy(
adapter->CurrentAddress,
adapter->PermanentAddress,
sizeof(adapter->PermanentAddress));
}
TraceLoggingWrite(
RealtekTraceProvider,
"PermanentAddressRead",
TraceLoggingBinary(adapter->CurrentAddress, 6, "CurrentAddress"));
Exit:
TraceExitResult(status);
return status;
}
void
RtAdapterRefreshCurrentAddress(
_In_ RT_ADAPTER *adapter)
{
UCHAR TmpUchar = adapter->CSRAddress->REG_F0_F3.RESV_F3;
TmpUchar |= BIT_2;
adapter->CSRAddress->REG_F0_F3.RESV_F3 = TmpUchar;
// NIC hardware register to default
RtAdapterIssueFullReset(adapter);
// Program current address
RtAdapterSetupCurrentAddress(adapter);
}
void
RtAdapterEnableCR9346Write(_In_ RT_ADAPTER *adapter)
{
UCHAR ucCr9346Value;
ucCr9346Value = adapter->CSRAddress->CR9346;
ucCr9346Value |= (CR9346_EEM1 | CR9346_EEM0);
adapter->CSRAddress->CR9346 = ucCr9346Value;
}
void
RtAdapterDisableCR9346Write(_In_ RT_ADAPTER *adapter)
{
UCHAR ucCr9346Value;
ucCr9346Value = adapter->CSRAddress->CR9346;
ucCr9346Value &= ~(CR9346_EEM1 | CR9346_EEM0);
adapter->CSRAddress->CR9346 = ucCr9346Value;
}
void
RtAdapterSetupCurrentAddress(
_In_ RT_ADAPTER *adapter)
{
RtAdapterEnableCR9346Write(adapter); {
for (UINT i = 0; i < ETHERNET_ADDRESS_LENGTH; i++)
{
adapter->CSRAddress->ID[i] = adapter->CurrentAddress[i];
}
} RtAdapterDisableCR9346Write(adapter);
}
ULONG
ComputeCrc(
_In_ UCHAR *buffer,
UINT length)
{
ULONG crc = 0xffffffff;
for (UINT i = 0; i < length; i++)
{
UCHAR curByte = buffer[i];
for (UINT j = 0; j < 8; j++)
{
ULONG carry = ((crc & 0x80000000) ? 1 : 0) ^ (curByte & 0x01);
crc <<= 1;
curByte >>= 1;
if (carry)
{
crc = (crc ^ 0x04c11db6) | carry;
}
}
}
return crc;
}
void
GetMulticastBit(
_In_ UCHAR address[],
_Out_ UCHAR *byte,
_Out_ UCHAR *value)
/*++
Routine Description:
For a given multicast address, returns the byte and bit in
the card multicast registers that it hashes to. Calls
ComputeCrc() to determine the CRC value.
--*/
{
ULONG crc = ComputeCrc(address, ETH_LENGTH_OF_ADDRESS);
// The bit number is now in the 6 most significant bits of CRC.
UINT bitNumber = (UINT)((crc >> 26) & 0x3f);
*byte = (UCHAR)(bitNumber / 8);
*value = (UCHAR)((UCHAR)1 << (bitNumber % 8));
}
void
RtAdapterSetMulticastReg(
_In_ RT_ADAPTER *adapter,
ULONG multiCast_03,
ULONG multiCast_47)
{
PULONG pMultiCastReg;
pMultiCastReg = (PULONG)(&adapter->CSRAddress->MulticastReg0);
*pMultiCastReg = multiCast_03;
pMultiCastReg = (PULONG)(&adapter->CSRAddress->MulticastReg4);
*pMultiCastReg = multiCast_47;
}
void RtAdapterPushMulticastList(_In_ RT_ADAPTER *adapter)
{
UCHAR Byte;
UCHAR Bit;
ULONG MultiCast_03;
ULONG MultiCast_47;
ULONG AddressCount = adapter->MCAddressCount;
ULONG MultiCastR1;
ULONG MultiCastR2;
UCHAR NicMulticastRegs[MAX_NIC_MULTICAST_REG] = { 0 };
// Now turn on the bit for each address in the multicast list.
for (UINT i = 0; (i < AddressCount) && (i < RT_MAX_MCAST_LIST); i++)
{
GetMulticastBit(adapter->MCList[i], &Byte, &Bit);
NicMulticastRegs[Byte] |= Bit;
}
//Write Multicast bits to register
MultiCast_03 = 0;
MultiCast_47 = 0;
for (UINT i = 0; i < 4; i++)
{
MultiCast_03 = MultiCast_03 + (NicMulticastRegs[i] << (8 * i));
}
for (UINT i = 4; i < 8; i++)
{
MultiCast_47 = MultiCast_47 + (NicMulticastRegs[i] << (8 * (i - 4)));
}
MultiCastR1 = 0;
MultiCastR2 = 0;
MultiCastR1 |= (MultiCast_47 & 0x000000ff) << 24;
MultiCastR1 |= (MultiCast_47 & 0x0000ff00) << 8;
MultiCastR1 |= (MultiCast_47 & 0x00ff0000) >> 8;
MultiCastR1 |= (MultiCast_47 & 0xff000000) >> 24;
MultiCastR2 |= (MultiCast_03 & 0x000000ff) << 24;
MultiCastR2 |= (MultiCast_03 & 0x0000ff00) << 8;
MultiCastR2 |= (MultiCast_03 & 0x00ff0000) >> 8;
MultiCastR2 |= (MultiCast_03 & 0xff000000) >> 24;
MultiCast_03 = MultiCastR1;
MultiCast_47 = MultiCastR2;
RtAdapterSetMulticastReg(adapter, MultiCast_03, MultiCast_47);
}
bool
RtAdapterQueryChipType(_In_ RT_ADAPTER *adapter, _Out_ RT_CHIP_TYPE *chipType)
{
ULONG MacVer = adapter->CSRAddress->TCR;
MacVer &= 0x7C800000;
*chipType = RTLUNKNOWN;
if (MacVer == 0x28000000)
{
*chipType = RTL8168D;
return true;
}
return false;
}
void
RtAdapterSetupHardware(RT_ADAPTER *adapter)
{
UCHAR TmpUchar;
TmpUchar = adapter->CSRAddress->REG_F0_F3.RESV_F3;
TmpUchar |= BIT_2;
adapter->CSRAddress->REG_F0_F3.RESV_F3 = TmpUchar;
TmpUchar = adapter->CSRAddress->REG_D0_D3.RESV_D1;
TmpUchar |= (BIT_7 | BIT_1);
adapter->CSRAddress->REG_D0_D3.RESV_D1 = TmpUchar;
}
void
RtAdapterIssueFullReset(
RT_ADAPTER *adapter)
{
adapter->CSRAddress->CmdReg = CR_RST;
for (UINT i = 0; i < 20; i++)
{
KeStallExecutionProcessor(20);
if (!(adapter->CSRAddress->CmdReg & CR_RST))
{
break;
}
}
}
void
RtAdapterUpdateInterruptModeration(_In_ RT_ADAPTER *adapter)
{
WdfSpinLockAcquire(adapter->Lock); {
USHORT timerFlags = 0;
if (adapter->InterruptModerationDisabled ||
adapter->InterruptModerationMode == RtInterruptModerationOff)
{
// No interrupt moderation
adapter->CSRAddress->IntMiti.RxTimerNum = 0;
adapter->CSRAddress->IntMiti.TxTimerNum = 0;
}
else
{
switch (adapter->InterruptModerationMode)
{
case RtInterruptModerationLow:
timerFlags |= CPCR_INT_MITI_TIMER_UNIT_0;
timerFlags |= CPCR_INT_MITI_TIMER_UNIT_1;
// Rx: Approximately 500us delay before interrupting
adapter->CSRAddress->IntMiti.RxTimerNum = 0x30;
// Tx: Completion isn't time-critical; give it the maximum slack.
adapter->CSRAddress->IntMiti.TxTimerNum = 0xf0;
break;
case RtInterruptModerationMedium:
timerFlags |= CPCR_INT_MITI_TIMER_UNIT_0;
timerFlags |= CPCR_INT_MITI_TIMER_UNIT_1;
// Rx: Approximately 2400us delay before interrupting
adapter->CSRAddress->IntMiti.RxTimerNum = 0xf0;
// Tx: Completion isn't time-critical; give it the maximum slack.
adapter->CSRAddress->IntMiti.TxTimerNum = 0xf0;
break;
}
}
USHORT currentCpcr = adapter->CSRAddress->CPCR;
USHORT newCpcr = currentCpcr;
newCpcr &= ~(CPCR_INT_MITI_TIMER_UNIT_0 | CPCR_INT_MITI_TIMER_UNIT_1);
newCpcr |= timerFlags;
if (currentCpcr != newCpcr)
{
adapter->CSRAddress->CPCR = newCpcr;
}
} WdfSpinLockRelease(adapter->Lock);
}

237
RtEthSample/adapter.h Normal file
Просмотреть файл

@ -0,0 +1,237 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
typedef struct _RT_INTERRUPT RT_INTERRUPT;
typedef struct _RT_TALLY RT_TALLY;
#pragma region Setting Enumerations
typedef enum _RT_DUPLEX_STATE : UCHAR {
RtDuplexNone = 0,
RtDuplexHalf = 1,
RtDuplexFull = 2,
} RT_DUPLEX_STATE;
typedef enum _RT_CHKSUM_OFFLOAD : UCHAR
{
RtChecksumOffloadDisabled = 0,
RtChecksumOffloadTxEnabled = 1,
RtChecksumOffloadRxEnabled = 2,
RtChecksumOffloadTxRxEnabled = 3,
} RT_CHKSUM_OFFLOAD;
typedef enum _RT_IM_MODE
{
RtInterruptModerationOff = 0,
RtInterruptModerationLow = 1,
RtInterruptModerationMedium = 2,
} RT_IM_MODE;
typedef enum _RT_CHIP_TYPE
{
RTLUNKNOWN,
RTL8168D
} RT_CHIP_TYPE;
typedef enum _RT_SPEED_DUPLEX_MODE {
RtSpeedDuplexModeAutoNegotiation = 0,
RtSpeedDuplexMode10MHalfDuplex = 1,
RtSpeedDuplexMode10MFullDuplex = 2,
RtSpeedDuplexMode100MHalfDuplex = 3,
RtSpeedDuplexMode100MFullDuplex = 4,
RtSpeedDuplexMode1GFullDuplex = 5,
} RT_SPEED_DUPLEX_MODE;
#pragma endregion
// Context for NETADAPTER
typedef struct _RT_ADAPTER
{
// WDF handles associated with this context
NETADAPTER NetAdapter;
WDFDEVICE WdfDevice;
// Handle to default Tx and Rx Queues
NETTXQUEUE TxQueue;
NETRXQUEUE RxQueue;
// Pointer to interrupt object
RT_INTERRUPT *Interrupt;
// Handle given by NDIS when the NetAdapter registered itself.
NDIS_HANDLE NdisLegacyAdapterHandle;
// configuration
UCHAR PermanentAddress[ETH_LENGTH_OF_ADDRESS];
UCHAR CurrentAddress[ETH_LENGTH_OF_ADDRESS];
BOOLEAN OverrideAddress;
ULONG NumTcb; // Total number of TCBs
// spin locks
WDFSPINLOCK Lock;
// Packet Filter and look ahead size.
ULONG PacketFilter;
USHORT LinkSpeed;
NET_IF_MEDIA_DUPLEX_STATE DuplexMode;
// multicast list
UINT MCAddressCount;
UCHAR MCList[RT_MAX_MCAST_LIST][ETH_LENGTH_OF_ADDRESS];
// Packet counts
ULONG64 InUcastOctets;
ULONG64 InMulticastOctets;
ULONG64 InBroadcastOctets;
ULONG64 OutUCastPkts;
ULONG64 OutMulticastPkts;
ULONG64 OutBroadcastPkts;
ULONG64 OutUCastOctets;
ULONG64 OutMulticastOctets;
ULONG64 OutBroadcastOctets;
ULONG64 TotalTxErr;
ULONG TotalRxErr;
ULONG64 HwTotalRxMatchPhy;
ULONG64 HwTotalRxBroadcast;
ULONG64 HwTotalRxMulticast;
// Count of transmit errors
ULONG TxAbortExcessCollisions;
ULONG TxDmaUnderrun;
ULONG TxOneRetry;
ULONG TxMoreThanOneRetry;
// Count of receive errors
ULONG RcvResourceErrors;
RT_MAC *volatile CSRAddress;
// user "*SpeedDuplex" setting
RT_SPEED_DUPLEX_MODE SpeedDuplex;
WDFDMAENABLER DmaEnabler;
PHYSICAL_ADDRESS TallyPhy;
RT_TALLY *GTally;
RT_CHIP_TYPE ChipType;
BOOLEAN LinkAutoNeg;
NDIS_OFFLOAD_ENCAPSULATION OffloadEncapsulation;
RT_CHKSUM_OFFLOAD UDPChksumOffv4;
RT_CHKSUM_OFFLOAD UDPChksumOffv6;
RT_CHKSUM_OFFLOAD IPChksumOffv4;
RT_CHKSUM_OFFLOAD TCPChksumOffv4;
RT_CHKSUM_OFFLOAD TCPChksumOffv6;
USHORT TransmitBuffers;
BOOLEAN IpRxHwChkSumv4;
BOOLEAN TcpRxHwChkSumv4;
BOOLEAN UdpRxHwChkSumv4;
BOOLEAN TcpRxHwChkSumv6;
BOOLEAN UdpRxHwChkSumv6;
ULONG ChksumErrRxIpv4Cnt;
ULONG ChksumErrRxTcpIpv6Cnt;
ULONG ChksumErrRxTcpIpv4Cnt;
ULONG ChksumErrRxUdpIpv6Cnt;
ULONG ChksumErrRxUdpIpv4Cnt;
// Tracks *WakeOnLan Keyword
bool WakeOnMagicPacketEnabled;
// Hardware capability, managed by INF keyword
RT_IM_MODE InterruptModerationMode;
// Runtime disablement, controlled by OID
bool InterruptModerationDisabled;
} RT_ADAPTER;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(RT_ADAPTER, RtGetAdapterContext);
EVT_NET_ADAPTER_SET_CAPABILITIES EvtAdapterSetCapabilities;
EVT_NET_ADAPTER_CREATE_TXQUEUE EvtAdapterCreateTxQueue;
EVT_NET_ADAPTER_CREATE_RXQUEUE EvtAdapterCreateRxQueue;
EVT_WDF_DEVICE_CONTEXT_DESTROY RtDestroyAdapterContext;
inline
NTSTATUS
RtConvertNdisStatusToNtStatus(
_In_ NDIS_STATUS ndisStatus)
{
if (ndisStatus == NDIS_STATUS_BUFFER_TOO_SHORT)
{
return STATUS_BUFFER_TOO_SMALL;
}
else if (ndisStatus == NDIS_STATUS_REQUEST_ABORTED)
{
return STATUS_CANCELLED;
}
else
{
return (NTSTATUS)ndisStatus;
}
}
NTSTATUS
RtInitializeAdapterContext(
_In_ RT_ADAPTER *adapter,
_In_ WDFDEVICE device,
_In_ NETADAPTER netAdapter);
void RtAdapterUpdateInterruptModeration(_In_ RT_ADAPTER *adapter);
void
RtAdapterQueryOffloadConfiguration(
_In_ RT_ADAPTER const *adapter,
_Out_ NDIS_OFFLOAD *offloadCaps);
_Requires_lock_held_(adapter->Lock)
void
RtAdapterUpdateEnabledChecksumOffloads(_In_ RT_ADAPTER *adapter);
NTSTATUS
RtAdapterReadAddress(_In_ RT_ADAPTER *adapter);
void
RtAdapterRefreshCurrentAddress(_In_ RT_ADAPTER *adapter);
void
RtAdapterSetupHardware(RT_ADAPTER *adapter);
void
RtAdapterIssueFullReset(_In_ RT_ADAPTER *adapter);
void
RtAdapterEnableCR9346Write(_In_ RT_ADAPTER *adapter);
void
RtAdapterDisableCR9346Write(_In_ RT_ADAPTER *adapter);
void
RtAdapterSetupCurrentAddress(_In_ RT_ADAPTER *adapter);
void
RtAdapterPushMulticastList(_In_ RT_ADAPTER *adapter);
bool
RtAdapterQueryChipType(_In_ RT_ADAPTER *adapter, _Out_ RT_CHIP_TYPE *chipType);

Просмотреть файл

@ -0,0 +1,190 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "configuration.h"
#include "adapter.h"
typedef struct _RT_REG_ENTRY
{
NDIS_STRING RegName; // variable name text
UINT FieldOffset; // offset to RT_ADAPTER field
UINT FieldSize; // size (in bytes) of the field
UINT Default; // default value to use
UINT Min; // minimum value allowed
UINT Max; // maximum value allowed
} RT_ADVANCED_PROPERTIES;
#define RT_OFFSET(field) ((UINT)FIELD_OFFSET(RT_ADAPTER,field))
#define RT_SIZE(field) sizeof(((RT_ADAPTER *)0)->field)
RT_ADVANCED_PROPERTIES RtSupportedProperties[] =
{
// reg value name Offset in RT_ADAPTER Field size Default Value Min Max
{ NDIS_STRING_CONST("*SpeedDuplex"), RT_OFFSET(SpeedDuplex), RT_SIZE(SpeedDuplex), RtSpeedDuplexModeAutoNegotiation, RtSpeedDuplexModeAutoNegotiation, RtSpeedDuplexMode1GFullDuplex },
{ NDIS_STRING_CONST("*TransmitBuffers"), RT_OFFSET(TransmitBuffers), RT_SIZE(TransmitBuffers), 128, RT_MIN_TCB, RT_MAX_TCB },
{ NDIS_STRING_CONST("*IPChecksumOffloadIPv4"), RT_OFFSET(IPChksumOffv4), RT_SIZE(IPChksumOffv4), RtChecksumOffloadTxRxEnabled, RtChecksumOffloadDisabled, RtChecksumOffloadTxRxEnabled },
{ NDIS_STRING_CONST("*UDPChecksumOffloadIPv6"), RT_OFFSET(UDPChksumOffv6), RT_SIZE(UDPChksumOffv6), RtChecksumOffloadTxRxEnabled, RtChecksumOffloadDisabled, RtChecksumOffloadTxRxEnabled },
{ NDIS_STRING_CONST("*UDPChecksumOffloadIPv4"), RT_OFFSET(UDPChksumOffv4), RT_SIZE(UDPChksumOffv4), RtChecksumOffloadTxRxEnabled, RtChecksumOffloadDisabled, RtChecksumOffloadTxRxEnabled },
{ NDIS_STRING_CONST("*TCPChecksumOffloadIPv4"), RT_OFFSET(TCPChksumOffv4), RT_SIZE(TCPChksumOffv4), RtChecksumOffloadTxRxEnabled, RtChecksumOffloadDisabled, RtChecksumOffloadTxRxEnabled },
{ NDIS_STRING_CONST("*TCPChecksumOffloadIPv6"), RT_OFFSET(TCPChksumOffv6), RT_SIZE(TCPChksumOffv6), RtChecksumOffloadTxRxEnabled, RtChecksumOffloadDisabled, RtChecksumOffloadTxRxEnabled },
{ NDIS_STRING_CONST("*WakeOnMagicPacket"), RT_OFFSET(WakeOnMagicPacketEnabled), RT_SIZE(WakeOnMagicPacketEnabled), true, false, true },
{ NDIS_STRING_CONST("*InterruptModeration"), RT_OFFSET(InterruptModerationMode), RT_SIZE(InterruptModerationMode), RtInterruptModerationLow, RtInterruptModerationOff, RtInterruptModerationMedium },
};
NTSTATUS
RtAdapterReadConfiguration(
_In_ RT_ADAPTER *adapter)
/*++
Routine Description:
Read the following from the registry
1. All the parameters
2. NetworkAddres
Arguments:
adapter Pointer to our adapter
Return Value:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES
--*/
{
TraceEntryRtAdapter(adapter);
NTSTATUS status = STATUS_SUCCESS;
NETCONFIGURATION configuration;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetAdapterOpenConfiguration(adapter->NetAdapter, WDF_NO_OBJECT_ATTRIBUTES, &configuration));
// read all the registry values
for (UINT i = 0; i < ARRAYSIZE(RtSupportedProperties); i++)
{
RT_ADVANCED_PROPERTIES *property = &RtSupportedProperties[i];
// Driver should NOT fail the initialization only because it can not
// read the registry
PUCHAR pointer = (PUCHAR)adapter + property->FieldOffset;
// Get the configuration value for a specific parameter. Under NT the
// parameters are all read in as DWORDs.
ULONG value = 0;
status = NetConfigurationQueryUlong(
configuration,
NET_CONFIGURATION_QUERY_ULONG_NO_FLAGS,
&property->RegName,
&value);
// If the parameter was present, then check its value for validity.
if (NT_SUCCESS(status))
{
// Check that param value is not too small or too large
if (value < property->Min ||
value > property->Max)
{
value = property->Default;
}
}
else
{
value = property->Default;
status = STATUS_SUCCESS;
}
TraceLoggingWrite(
RealtekTraceProvider,
"ReadConfiguration",
TraceLoggingRtAdapter(adapter),
TraceLoggingUnicodeString(&property->RegName, "Key"),
TraceLoggingUInt32(value, "Value"));
// Store the value in the adapter structure.
switch (property->FieldSize)
{
case 1:
*((PUCHAR)pointer) = (UCHAR)value;
break;
case 2:
*((PUSHORT)pointer) = (USHORT)value;
break;
case 4:
*((PULONG)pointer) = (ULONG)value;
break;
default:
TraceLoggingWrite(
RealtekTraceProvider,
"InvalidFieldSize",
TraceLoggingLevel(TRACE_LEVEL_ERROR),
TraceLoggingRtAdapter(adapter),
TraceLoggingUnicodeString(&property->RegName, "Key"),
TraceLoggingUInt32(value, "Value"),
TraceLoggingUInt32(property->FieldSize, "FieldSize"));
break;
}
}
// Read NetworkAddress registry value
// Use it as the current address if any
UCHAR networkAddress[ETH_LENGTH_OF_ADDRESS];
ULONG addressSize;
status = NetConfigurationQueryNetworkAddress(
configuration,
ARRAYSIZE(networkAddress),
networkAddress,
&addressSize);
if ((status == STATUS_SUCCESS))
{
if (addressSize != ETH_LENGTH_OF_ADDRESS ||
ETH_IS_MULTICAST(networkAddress) ||
ETH_IS_BROADCAST(networkAddress))
{
TraceLoggingWrite(
RealtekTraceProvider,
"InvalidNetworkAddress",
TraceLoggingBinary(networkAddress, addressSize));
}
else
{
memcpy(adapter->CurrentAddress, networkAddress, ETH_LENGTH_OF_ADDRESS);
adapter->OverrideAddress = TRUE;
}
}
status = STATUS_SUCCESS;
// initial number of TX and RX
adapter->NumTcb = adapter->TransmitBuffers;
if (adapter->NumTcb > RT_MAX_TCB) adapter->NumTcb = RT_MAX_TCB;
if (adapter->NumTcb < RT_MIN_TCB) adapter->NumTcb = RT_MIN_TCB;
Exit:
if (configuration)
{
NetConfigurationClose(configuration);
}
TraceExitResult(status);
return STATUS_SUCCESS;
}

Просмотреть файл

@ -0,0 +1,16 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
NTSTATUS
RtAdapterReadConfiguration(
_In_ RT_ADAPTER *adapter);

265
RtEthSample/device.cpp Normal file
Просмотреть файл

@ -0,0 +1,265 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "device.h"
#include "adapter.h"
#include "configuration.h"
#include "statistics.h"
#include "interrupt.h"
#include "link.h"
#include "phy.h"
NTSTATUS
RtGetResources(
_In_ RT_ADAPTER *adapter,
_In_ WDFCMRESLIST resourcesRaw,
_In_ WDFCMRESLIST resourcesTranslated)
{
TraceEntry();
NTSTATUS status = STATUS_SUCCESS;
ULONG errorCode = 0;
ULONG errorValue = 0;
bool hasMemoryResource = false;
ULONG memRegCnt = 0;
// According to https://msdn.microsoft.com/windows/hardware/drivers/wdf/raw-and-translated-resources
// "Both versions represent the same set of hardware resources, in the same order."
ULONG rawCount = WdfCmResourceListGetCount(resourcesRaw);
NT_ASSERT(rawCount == WdfCmResourceListGetCount(resourcesTranslated));
PHYSICAL_ADDRESS memAddressTranslated;
memAddressTranslated = {};
for (ULONG i = 0; i < rawCount; i++)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR rawDescriptor = WdfCmResourceListGetDescriptor(resourcesRaw, i);
PCM_PARTIAL_RESOURCE_DESCRIPTOR translatedDescriptor = WdfCmResourceListGetDescriptor(resourcesTranslated, i);
if (rawDescriptor->Type == CmResourceTypeMemory)
{
// RTL8168D has 2 memory IO regions, first region is MAC regs, second region is MSI-X
if (memRegCnt == 0)
{
NT_ASSERT(rawDescriptor->u.Memory.Length >= sizeof(RT_MAC));
memAddressTranslated = translatedDescriptor->u.Memory.Start;
hasMemoryResource = true;
}
memRegCnt++;
}
}
if (!hasMemoryResource)
{
status = STATUS_RESOURCE_TYPE_NOT_FOUND;
errorCode = NDIS_ERROR_CODE_RESOURCE_CONFLICT;
errorValue = ERRLOG_NO_MEMORY_RESOURCE;
GOTO_IF_NOT_NT_SUCCESS(Exit, status, STATUS_NDIS_RESOURCE_CONFLICT);
}
GOTO_WITH_INSUFFICIENT_RESOURCES_IF_NULL(Exit, status,
adapter->CSRAddress =
static_cast<RT_MAC*>(
MmMapIoSpaceEx(
memAddressTranslated,
sizeof(RT_MAC),
PAGE_READWRITE | PAGE_NOCACHE)));
Exit:
if (!NT_SUCCESS(status))
{
NdisWriteErrorLogEntry(
adapter->NdisLegacyAdapterHandle,
errorCode,
1,
errorValue);
}
TraceExitResult(status);
return status;
}
NTSTATUS
RtRegisterScatterGatherDma(
_In_ RT_ADAPTER *adapter)
{
TraceEntryRtAdapter(adapter);
WdfDeviceSetAlignmentRequirement(adapter->WdfDevice, FILE_256_BYTE_ALIGNMENT);
WDF_DMA_ENABLER_CONFIG dmaEnablerConfig;
WDF_DMA_ENABLER_CONFIG_INIT(&dmaEnablerConfig, WdfDmaProfileScatterGather64, RT_MAX_PACKET_SIZE);
dmaEnablerConfig.Flags |= WDF_DMA_ENABLER_CONFIG_REQUIRE_SINGLE_TRANSFER;
dmaEnablerConfig.WdmDmaVersionOverride = 3;
NTSTATUS status = STATUS_SUCCESS;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfDmaEnablerCreate(
adapter->WdfDevice,
&dmaEnablerConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&adapter->DmaEnabler),
TraceLoggingRtAdapter(adapter));
Exit:
if (!NT_SUCCESS(status))
{
NdisWriteErrorLogEntry(
adapter->NdisLegacyAdapterHandle,
NDIS_ERROR_CODE_OUT_OF_RESOURCES,
1,
ERRLOG_OUT_OF_SG_RESOURCES);
}
TraceExitResult(status);
return status;
}
NTSTATUS
RtInitializeHardware(
_In_ RT_ADAPTER *adapter,
_In_ WDFCMRESLIST resourcesRaw,
_In_ WDFCMRESLIST resourcesTranslated)
{
//
// Read the registry parameters
//
NTSTATUS status = STATUS_SUCCESS;
adapter->NdisLegacyAdapterHandle =
NetAdapterWdmGetNdisHandle(adapter->NetAdapter);
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtAdapterReadConfiguration(adapter));
// Map in phy
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtGetResources(adapter, resourcesRaw, resourcesTranslated));
adapter->Interrupt->IMR = &adapter->CSRAddress->IMR;
adapter->Interrupt->ISR = &adapter->CSRAddress->ISR;
if (!RtAdapterQueryChipType(adapter, &adapter->ChipType))
{
//
// Unsupported card
//
NdisWriteErrorLogEntry(
adapter->NdisLegacyAdapterHandle,
NDIS_ERROR_CODE_ADAPTER_NOT_FOUND,
0);
GOTO_IF_NOT_NT_SUCCESS(Exit, status, STATUS_NOT_FOUND);
}
RtAdapterSetupHardware(adapter);
RtAdapterIssueFullReset(adapter);
RtAdapterPowerUpPhy(adapter);
RtAdapterIssueFullReset(adapter);
//
// Read additional info from NIC such as MAC address
//
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtAdapterReadAddress(adapter));
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtRegisterScatterGatherDma(adapter),
TraceLoggingRtAdapter(adapter));
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtAdapterInitializeStatistics(adapter),
TraceLoggingRtAdapter(adapter));
// Init the hardware and set up everything
RtAdapterRefreshCurrentAddress(adapter);
// PHY Version
RtAdapterWritePhyUint16(adapter, 0x1F, 0x0000);
adapter->CSRAddress->CPCR = 0;
// Gathers and indicates current link state to NDIS
//
// Normally need to take the adapter lock before updating the NIC's
// media state, but preparehardware already is serialized against all
// other callbacks to the NetAdapter.
NET_ADAPTER_LINK_STATE linkState;
RtAdapterQueryLinkState(adapter, &linkState);
NetAdapterSetCurrentLinkState(adapter->NetAdapter, &linkState);
Exit:
TraceExitResult(status);
return status;
}
void
RtReleaseHardware(
_In_ RT_ADAPTER *adapter)
{
if (adapter->CSRAddress)
{
MmUnmapIoSpace(
adapter->CSRAddress,
sizeof(RT_MAC));
adapter->CSRAddress = NULL;
}
}
_Use_decl_annotations_
NTSTATUS
EvtDevicePrepareHardware(
_In_ WDFDEVICE device,
_In_ WDFCMRESLIST resourcesRaw,
_In_ WDFCMRESLIST resourcesTranslated)
{
RT_ADAPTER *adapter = RtGetDeviceContext(device)->Adapter;
TraceEntryRtAdapter(adapter);
NTSTATUS status = STATUS_SUCCESS;
GOTO_IF_NOT_NT_SUCCESS(Exit, status, RtInitializeHardware(adapter, resourcesRaw, resourcesTranslated));
Exit:
TraceExitResult(status);
return status;
}
_Use_decl_annotations_
NTSTATUS
EvtDeviceReleaseHardware(
_In_ WDFDEVICE device,
_In_ WDFCMRESLIST resourcesTranslated)
{
UNREFERENCED_PARAMETER(resourcesTranslated);
RT_ADAPTER *adapter = RtGetDeviceContext(device)->Adapter;
TraceEntryRtAdapter(adapter);
RtReleaseHardware(adapter);
NTSTATUS status = STATUS_SUCCESS;
TraceExitResult(status);
return status;
}

22
RtEthSample/device.h Normal file
Просмотреть файл

@ -0,0 +1,22 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
typedef struct _RT_DEVICE
{
RT_ADAPTER *Adapter;
} RT_DEVICE;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(RT_DEVICE, RtGetDeviceContext);
EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware;
EVT_WDF_DEVICE_RELEASE_HARDWARE EvtDeviceReleaseHardware;

166
RtEthSample/driver.cpp Normal file
Просмотреть файл

@ -0,0 +1,166 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "device.h"
#include "adapter.h"
#include "oid.h"
#include "power.h"
#include "interrupt.h"
// {5D364AAF-5B49-41A0-9E03-D3CB2AA2E03E}
TRACELOGGING_DEFINE_PROVIDER(
RealtekTraceProvider,
"Realtek.Trace.Provider",
(0x5d364aaf, 0x5b49, 0x41a0, 0x9e, 0x3, 0xd3, 0xcb, 0x2a, 0xa2, 0xe0, 0x3e));
EXTERN_C __declspec(code_seg("INIT")) DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd;
_Use_decl_annotations_
__declspec(code_seg("INIT"))
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT driverObject,
_In_ PUNICODE_STRING registryPath)
{
BOOLEAN traceLoggingRegistered = FALSE;
NTSTATUS status = STATUS_SUCCESS;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
TraceLoggingRegister(RealtekTraceProvider));
traceLoggingRegistered = TRUE;
TraceEntry(TraceLoggingUnicodeString(registryPath));
WDF_DRIVER_CONFIG driverConfig;
WDF_DRIVER_CONFIG_INIT(&driverConfig, EvtDriverDeviceAdd);
driverConfig.DriverPoolTag = 'RTEK';
driverConfig.EvtDriverUnload = EvtDriverUnload;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfDriverCreate(driverObject, registryPath, WDF_NO_OBJECT_ATTRIBUTES, &driverConfig, NULL));
Exit:
if (traceLoggingRegistered)
{
TraceExitResult(status);
if (!NT_SUCCESS(status))
{
TraceLoggingUnregister(RealtekTraceProvider);
}
}
return status;
}
_Use_decl_annotations_
NTSTATUS
EvtDriverDeviceAdd(
_In_ WDFDRIVER driver,
_Inout_ PWDFDEVICE_INIT deviceInit)
{
UNREFERENCED_PARAMETER((driver));
TraceEntry();
NTSTATUS status = STATUS_SUCCESS;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetAdapterDeviceInitConfig(deviceInit));
WDF_OBJECT_ATTRIBUTES deviceAttributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, RT_DEVICE);
{
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware = EvtDeviceReleaseHardware;
pnpPowerCallbacks.EvtDeviceD0Entry = EvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = EvtDeviceD0Exit;
WdfDeviceInitSetPnpPowerEventCallbacks(deviceInit, &pnpPowerCallbacks);
}
{
WDF_POWER_POLICY_EVENT_CALLBACKS powerPolicyCallbacks;
WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks);
powerPolicyCallbacks.EvtDeviceArmWakeFromSx = EvtDeviceArmWakeFromSx;
powerPolicyCallbacks.EvtDeviceDisarmWakeFromSx = EvtDeviceDisarmWakeFromSx;
WdfDeviceInitSetPowerPolicyEventCallbacks(deviceInit, &powerPolicyCallbacks);
}
WDFDEVICE wdfDevice;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfDeviceCreate(&deviceInit, &deviceAttributes, &wdfDevice));
// Default wake settings is good enough
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfDeviceAssignSxWakeSettings(wdfDevice, &wakeSettings));
NET_ADAPTER_CONFIG adapterConfig;
NET_ADAPTER_CONFIG_INIT(
&adapterConfig,
EvtAdapterSetCapabilities,
EvtAdapterCreateTxQueue,
EvtAdapterCreateRxQueue);
WDF_OBJECT_ATTRIBUTES adapterAttributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&adapterAttributes, RT_ADAPTER);
NETADAPTER netAdapter;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetAdapterCreate(wdfDevice, &adapterAttributes, &adapterConfig, &netAdapter));
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
RT_DEVICE *device = RtGetDeviceContext(wdfDevice);
device->Adapter = adapter;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtInitializeAdapterContext(adapter, wdfDevice, netAdapter));
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtInitializeAdapterRequestQueue(adapter));
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtInterruptCreate(wdfDevice, adapter, &adapter->Interrupt));
Exit:
TraceExitResult(status);
return status;
}
_Use_decl_annotations_
VOID
EvtDriverUnload(
_In_ WDFDRIVER driver)
{
UNREFERENCED_PARAMETER((driver));
TraceEntry();
TraceLoggingUnregister(RealtekTraceProvider);
}

16
RtEthSample/forward.h Normal file
Просмотреть файл

@ -0,0 +1,16 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
typedef struct _RT_ADAPTER RT_ADAPTER;
typedef struct _RT_DEVICE RT_DEVICE;
typedef struct _RT_INTERRUPT RT_INTERRUPT;

225
RtEthSample/interrupt.cpp Normal file
Просмотреть файл

@ -0,0 +1,225 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "interrupt.h"
#include "adapter.h"
#include "link.h"
NTSTATUS
RtInterruptCreate(
_In_ WDFDEVICE wdfDevice,
_In_ RT_ADAPTER *adapter,
_Out_ RT_INTERRUPT **interrupt)
{
TraceEntryRtAdapter(adapter);
*interrupt = nullptr;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, RT_INTERRUPT);
WDF_INTERRUPT_CONFIG config;
WDF_INTERRUPT_CONFIG_INIT(&config, EvtInterruptIsr, EvtInterruptDpc);
config.EvtInterruptEnable = EvtInterruptEnable;
config.EvtInterruptDisable = EvtInterruptDisable;
NTSTATUS status = STATUS_SUCCESS;
WDFINTERRUPT wdfInterrupt;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfInterruptCreate(wdfDevice, &config, &attributes, &wdfInterrupt));
*interrupt = RtGetInterruptContext(wdfInterrupt);
(*interrupt)->Adapter = adapter;
(*interrupt)->Handle = wdfInterrupt;
Exit:
TraceExitResult(status);
return status;
}
void
RtInterruptInitialize(_In_ RT_INTERRUPT *interrupt)
{
*interrupt->IMR = 0;
}
USHORT
RtCalculateImr(_In_ RT_INTERRUPT *interrupt)
{
USHORT imr = RtDefaultInterruptFlags;
if (interrupt->TxNotifyArmed)
{
imr |= RtTxInterruptFlags;
}
if (interrupt->RxNotifyArmed)
{
imr |= RtRxInterruptFlags;
}
return imr;
}
void
RtUpdateImr(_In_ RT_INTERRUPT *interrupt)
{
WdfInterruptAcquireLock(interrupt->Handle); {
*interrupt->IMR = RtCalculateImr(interrupt);
} WdfInterruptReleaseLock(interrupt->Handle);
}
_Use_decl_annotations_
NTSTATUS
EvtInterruptEnable(
_In_ WDFINTERRUPT wdfInterrupt,
_In_ WDFDEVICE wdfDevice)
{
UNREFERENCED_PARAMETER((wdfDevice));
TraceEntry(TraceLoggingPointer(wdfInterrupt));
// Framework sychronizes EvtInterruptEnable with WdfInterruptAcquireLock
// so do not grab the lock internally
RT_INTERRUPT *interrupt = RtGetInterruptContext(wdfInterrupt);
*interrupt->IMR = RtCalculateImr(interrupt);
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS
EvtInterruptDisable(
_In_ WDFINTERRUPT wdfInterrupt,
_In_ WDFDEVICE wdfDevice)
{
UNREFERENCED_PARAMETER((wdfDevice));
TraceEntry(TraceLoggingPointer(wdfInterrupt));
// Framework sychronizes EvtInterruptDisable with WdfInterruptAcquireLock
// so do not grab the lock internally
RT_INTERRUPT *interrupt = RtGetInterruptContext(wdfInterrupt);
*interrupt->IMR = 0;
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
BOOLEAN
EvtInterruptIsr(
_In_ WDFINTERRUPT wdfInterrupt,
ULONG MessageID)
{
UNREFERENCED_PARAMETER((MessageID));
RT_INTERRUPT *interrupt = RtGetInterruptContext(wdfInterrupt);
interrupt->NumInterrupts++;
USHORT interruptStatusRegister = *interrupt->ISR;
USHORT newWork = interruptStatusRegister & RtExpectedInterruptFlags;
// Check if our hardware is active
if (interruptStatusRegister == RtInactiveInterrupt)
{
interrupt->NumInterruptsDisabled++;
return false;
}
// Check if the interrupt was ours
if (0 == newWork)
{
interrupt->NumInterruptsNotOurs++;
return false;
}
// Acknowledge the interrupt
*interrupt->ISR = interruptStatusRegister;
// queue up interrupt work
InterlockedOr16((SHORT volatile *)&interrupt->SavedIsr, newWork);
// Typically the interrupt lock would be acquired before modifying IMR, but
// the Interrupt lock is already held for the length of the EvtInterruptIsr.
USHORT newImr = RtCalculateImr(interrupt);
// Disable any signals for queued work.
// The ISR fields that indicate the interrupt reason map directly to the
// IMR fields that enable them, so the ISR can be simply masked off the IMR
// to disable those fields that are being serviced.
newImr &= ~newWork;
// always re-enable link change notifications
newImr |= RtDefaultInterruptFlags;
*interrupt->IMR = newImr;
if (newWork != 0)
{
if (newWork & RtTxInterruptFlags)
interrupt->NumTxInterrupts++;
if (newWork & RtRxInterruptFlags)
interrupt->NumRxInterrupts++;
WdfInterruptQueueDpcForIsr(wdfInterrupt);
}
return true;
}
_Use_decl_annotations_
VOID
EvtInterruptDpc(
_In_ WDFINTERRUPT Interrupt,
_In_ WDFOBJECT AssociatedObject)
{
UNREFERENCED_PARAMETER(AssociatedObject);
RT_INTERRUPT *interrupt = RtGetInterruptContext(Interrupt);
RT_ADAPTER *adapter = interrupt->Adapter;
USHORT newWork = InterlockedExchange16((SHORT volatile *)&interrupt->SavedIsr, 0);
if (newWork & RtRxInterruptFlags)
{
if (InterlockedExchange(&interrupt->RxNotifyArmed, false))
{
NetRxQueueNotifyMoreReceivedPacketsAvailable(adapter->RxQueue);
}
}
if (newWork & RtTxInterruptFlags)
{
if (InterlockedExchange(&interrupt->TxNotifyArmed, false))
{
NetTxQueueNotifyMoreCompletedPacketsAvailable(adapter->TxQueue);
}
}
if (newWork & ISRIMR_LINK_CHG)
{
RtAdapterNotifyLinkChange(adapter);
}
}

59
RtEthSample/interrupt.h Normal file
Просмотреть файл

@ -0,0 +1,59 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
typedef struct _RT_INTERRUPT
{
RT_ADAPTER *Adapter;
WDFINTERRUPT Handle;
// Armed Notifications
LONG RxNotifyArmed;
LONG TxNotifyArmed;
// Fired Notificiations
// Tracks un-served ISR interrupt fields. Masks in only
// the RtExpectedInterruptFlags
USHORT SavedIsr;
USHORT volatile * IMR;
USHORT volatile * ISR;
// Statistical counters, for diagnostics only
ULONG64 NumInterrupts;
ULONG64 NumInterruptsNotOurs;
ULONG64 NumInterruptsDisabled;
ULONG64 NumRxInterrupts;
ULONG64 NumTxInterrupts;
} RT_INTERRUPT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(RT_INTERRUPT, RtGetInterruptContext);
static const USHORT RtTxInterruptFlags = ISRIMR_TOK | ISRIMR_TER;
static const USHORT RtRxInterruptFlags = ISRIMR_ROK | ISRIMR_RER | ISRIMR_RDU;
static const USHORT RtDefaultInterruptFlags = ISRIMR_LINK_CHG;
static const USHORT RtExpectedInterruptFlags = (RtTxInterruptFlags | RtRxInterruptFlags | RtDefaultInterruptFlags | ISRIMR_RX_FOVW);
static const USHORT RtInactiveInterrupt = 0xFFFF;
NTSTATUS
RtInterruptCreate(
_In_ WDFDEVICE wdfDevice,
_In_ RT_ADAPTER *adapter,
_Out_ RT_INTERRUPT **interrupt);
void RtInterruptInitialize(_In_ RT_INTERRUPT *interrupt);
void RtUpdateImr(_In_ RT_INTERRUPT *interrupt);
EVT_WDF_INTERRUPT_ISR EvtInterruptIsr;
EVT_WDF_INTERRUPT_DPC EvtInterruptDpc;
EVT_WDF_INTERRUPT_ENABLE EvtInterruptEnable;
EVT_WDF_INTERRUPT_DISABLE EvtInterruptDisable;

277
RtEthSample/link.cpp Normal file
Просмотреть файл

@ -0,0 +1,277 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "link.h"
#include "phy.h"
#include "adapter.h"
NET_IF_MEDIA_DUPLEX_STATE
RtAdapterGetDuplexSetting(_In_ RT_ADAPTER const *adapter)
{
switch (adapter->SpeedDuplex)
{
case RtSpeedDuplexMode10MHalfDuplex:
case RtSpeedDuplexMode100MHalfDuplex:
return MediaDuplexStateHalf;
case RtSpeedDuplexMode10MFullDuplex:
case RtSpeedDuplexMode100MFullDuplex:
case RtSpeedDuplexMode1GFullDuplex:
return MediaDuplexStateFull;
default:
return MediaDuplexStateUnknown;
}
}
USHORT
RtAdapterGetLinkSpeedSetting(_In_ RT_ADAPTER const *adapter)
{
switch (adapter->SpeedDuplex)
{
case RtSpeedDuplexMode10MHalfDuplex:
case RtSpeedDuplexMode10MFullDuplex:
return 10;
case RtSpeedDuplexMode100MHalfDuplex:
case RtSpeedDuplexMode100MFullDuplex:
return 100;
case RtSpeedDuplexMode1GFullDuplex:
return 1000;
default:
return 0;
}
}
void
RtAdapterPowerUpPhy(_In_ RT_ADAPTER *adapter)
{
RtAdapterWritePhyUint16(adapter, 0x1F, 0x0000);
RtAdapterWritePhyUint16(adapter, 0x0e, 0x0000);
RtAdapterWritePhyUint16(adapter, PHY_REG_BMCR, MDI_CR_AUTO_SELECT);
}
void
RtAdapterPushPhySettings(_In_ RT_ADAPTER *adapter)
{
RtAdapterWritePhyUint16(adapter, 0x1f, 0x0000);
USHORT PhyRegDataGBCR = RtAdapterReadPhyUint16(adapter, PHY_REG_GBCR);
// clear 1000 capability
PhyRegDataGBCR &= ~(GBCR_1000_FULL | GBCR_1000_HALF);
//
// Find out what kind of technology this Phy is capable of.
//
USHORT PhyRegDataANAR = RtAdapterReadPhyUint16(adapter, PHY_REG_ANAR);
PhyRegDataANAR &= ~(ANAR_10_HALF | ANAR_10_FULL | ANAR_100_HALF | ANAR_100_FULL | ANAR_MAC_PAUSE | ANAR_ASYM_PAUSE);
switch (adapter->SpeedDuplex)
{
case RtSpeedDuplexMode10MHalfDuplex:
PhyRegDataANAR |= ANAR_10_HALF;
break;
case RtSpeedDuplexMode10MFullDuplex:
PhyRegDataANAR |= (ANAR_10_FULL | ANAR_10_HALF);
break;
case RtSpeedDuplexMode100MHalfDuplex:
PhyRegDataANAR |= (ANAR_100_HALF | ANAR_10_FULL | ANAR_10_HALF);
break;
case RtSpeedDuplexMode100MFullDuplex:
PhyRegDataANAR |= (ANAR_100_FULL | ANAR_100_HALF | ANAR_10_FULL | ANAR_10_HALF);
break;
case RtSpeedDuplexMode1GFullDuplex:
PhyRegDataGBCR |= (GBCR_1000_FULL | GBCR_1000_HALF);
break;
case RtSpeedDuplexModeAutoNegotiation:
PhyRegDataANAR |= (ANAR_100_FULL | ANAR_100_HALF | ANAR_10_FULL | ANAR_10_HALF);
PhyRegDataGBCR |= (GBCR_1000_FULL | GBCR_1000_HALF);
break;
}
// update 10/100 register
RtAdapterWritePhyUint16(adapter, PHY_REG_ANAR, PhyRegDataANAR);
// update 1000 register
RtAdapterWritePhyUint16(adapter, PHY_REG_GBCR, PhyRegDataGBCR);
}
void
RtAdapterResetPhy(RT_ADAPTER *adapter)
{
RtAdapterWritePhyUint16(adapter, 0x1f, 0x0000);
// Reset the Phy
RtAdapterWritePhyUint16(adapter, PHY_REG_BMCR, MDI_CR_RESET | MDI_CR_AUTO_SELECT);
for (UINT i = PHY_RESET_TIME; i != 0; i--)
{
USHORT PhyRegData = RtAdapterReadPhyUint16(adapter, PHY_REG_BMCR);
if ((PhyRegData & 0x8000) == 0)
break;
KeStallExecutionProcessor(20);
}
}
void
RtAdapterInitializeAutoNegotiation(_In_ RT_ADAPTER *adapter)
{
RtAdapterWritePhyUint16(adapter, 0x1f, 0x0000);
RtAdapterWritePhyUint16(
adapter, PHY_REG_BMCR, MDI_CR_AUTO_SELECT | MDI_CR_RESTART_AUTO_NEG);
}
void
RtAdapterUpdateLinkStatus(
_In_ RT_ADAPTER *adapter)
{
// deafult to user setting
adapter->LinkSpeed = RtAdapterGetLinkSpeedSetting(adapter);
adapter->DuplexMode = RtAdapterGetDuplexSetting(adapter);
ULONG const PhyStatus = adapter->CSRAddress->PhyStatus;
if (PhyStatus & PHY_STATUS_1000MF) adapter->LinkSpeed = 1000;
else if (PhyStatus & PHY_STATUS_100M) adapter->LinkSpeed = 100;
else if (PhyStatus & PHY_STATUS_10M) adapter->LinkSpeed = 10;
// Get current duplex setting -- if bit is set then FDX is enabled
if (PhyStatus & PHY_STATUS_FULL_DUPLEX)
{
adapter->DuplexMode = MediaDuplexStateFull;
}
else
{
adapter->DuplexMode = MediaDuplexStateHalf;
}
}
void
RtAdapterCompleteAutoNegotiation(
_In_ RT_ADAPTER *adapter)
{
RtAdapterWritePhyUint16(adapter, 0x1F, 0x0000);
// If there is a valid link
if (adapter->CSRAddress->PhyStatus & PHY_STATUS_LINK_ON)
{
USHORT const PhyRegDataANER =
RtAdapterReadPhyUint16(adapter, PHY_REG_ANER);
USHORT const PhyRegDataBMSR =
RtAdapterReadPhyUint16(adapter, PHY_REG_BMSR);
// If both 1) the partner supports link auto-negotiation, and 2) auto-
// negotiation is complete, then the Link is auto negotiated.
adapter->LinkAutoNeg =
(PhyRegDataANER & ANER_LP_AUTO_NEG_ABLE) &&
(PhyRegDataBMSR & MDI_SR_AUTO_NEG_COMPLETE);
// Ensure that context link status is up to date.
RtAdapterUpdateLinkStatus(adapter);
}
else
{
adapter->LinkAutoNeg = FALSE;
}
}
void
RtAdapterNotifyLinkChange(RT_ADAPTER *adapter)
{
NET_ADAPTER_LINK_STATE linkState;
WdfSpinLockAcquire(adapter->Lock); {
RtAdapterQueryLinkState(adapter, &linkState);
} WdfSpinLockRelease(adapter->Lock);
NetAdapterSetCurrentLinkState(adapter->NetAdapter, &linkState);
}
// Detects the NIC's current media state and updates the adapter context
// to reflect that updated media state of the hardware. The driver must serialize access
// to RT_ADAPTER.
NDIS_MEDIA_CONNECT_STATE
RtAdapterQueryMediaState(
_In_ RT_ADAPTER *adapter)
{
// Detect if auto-negotiation is complete and update adapter state with
// link information.
RtAdapterCompleteAutoNegotiation(adapter);
UCHAR const phyStatus = adapter->CSRAddress->PhyStatus;
if (phyStatus & PHY_LINK_STATUS)
{
return MediaConnectStateConnected;
}
else
{
return MediaConnectStateDisconnected;
}
}
_Use_decl_annotations_
void
RtAdapterQueryLinkState(
_In_ RT_ADAPTER *adapter,
_Inout_ NET_ADAPTER_LINK_STATE *linkState
)
/*++
Routine Description:
This routine sends a NDIS_STATUS_LINK_STATE status up to NDIS
Arguments:
adapter Pointer to our adapter
Return Value:
None
--*/
{
NDIS_MEDIA_CONNECT_STATE mediaState = RtAdapterQueryMediaState(adapter);
NET_ADAPTER_AUTO_NEGOTIATION_FLAGS autoNegotiationFlags = NET_ADAPTER_AUTO_NEGOTIATION_NO_FLAGS;
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(mediaState));
if (adapter->LinkAutoNeg)
{
autoNegotiationFlags =
NET_ADAPTER_LINK_STATE_XMIT_LINK_SPEED_AUTO_NEGOTIATED |
NET_ADAPTER_LINK_STATE_RCV_LINK_SPEED_AUTO_NEGOTIATED |
NET_ADAPTER_LINK_STATE_DUPLEX_AUTO_NEGOTIATED;
}
NET_ADAPTER_LINK_STATE_INIT(
linkState,
adapter->LinkSpeed * 1'000'000,
mediaState,
adapter->DuplexMode,
NetAdapterPauseFunctionsUnknown,
autoNegotiationFlags);
TraceExit();
}

58
RtEthSample/link.h Normal file
Просмотреть файл

@ -0,0 +1,58 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
void
RtAdapterPowerUpPhy(_In_ RT_ADAPTER *adapter);
void
RtAdapterPushPhySettings(_In_ RT_ADAPTER *adapter);
void
RtAdapterResetPhy(_In_ RT_ADAPTER *adapter);
void
RtAdapterInitializeAutoNegotiation(_In_ RT_ADAPTER *adapter);
void
RtAdapterNotifyLinkChange(_In_ RT_ADAPTER *adapter);
_Requires_lock_held_(adapter->Lock)
void
RtAdapterQueryLinkState(
_In_ RT_ADAPTER *adapter,
_Inout_ NET_ADAPTER_LINK_STATE *linkState);
// MDI Control register bit definitions
#define MDI_CR_1000 BIT_6 //
#define MDI_CR_COLL_TEST_ENABLE BIT_7 // Collision test enable
#define MDI_CR_FULL_HALF BIT_8 // FDX =1, half duplex =0
#define MDI_CR_RESTART_AUTO_NEG BIT_9 // Restart auto negotiation
#define MDI_CR_ISOLATE BIT_10 // Isolate PHY from MII
#define MDI_CR_POWER_DOWN BIT_11 // Power down
#define MDI_CR_AUTO_SELECT BIT_12 // Auto speed select enable
#define MDI_CR_10_100 BIT_13 // 0 = 10Mbs, 1 = 100Mbs
#define MDI_CR_LOOPBACK BIT_14 // 0 = normal, 1 = loopback
#define MDI_CR_RESET BIT_15 // 0 = normal, 1 = PHY reset
// MDI Status register bit definitions
#define MDI_SR_EXT_REG_CAPABLE BIT_0 // Extended register capabilities
#define MDI_SR_JABBER_DETECT BIT_1 // Jabber detected
#define MDI_SR_LINK_STATUS BIT_2 // Link Status -- 1 = link
#define MDI_SR_AUTO_SELECT_CAPABLE BIT_3 // Auto speed select capable
#define MDI_SR_REMOTE_FAULT_DETECT BIT_4 // Remote fault detect
#define MDI_SR_AUTO_NEG_COMPLETE BIT_5 // Auto negotiation complete
#define MDI_SR_10T_HALF_DPX BIT_11 // 10BaseT Half Duplex capable
#define MDI_SR_10T_FULL_DPX BIT_12 // 10BaseT full duplex capable
#define MDI_SR_TX_HALF_DPX BIT_13 // TX Half Duplex capable
#define MDI_SR_TX_FULL_DPX BIT_14 // TX full duplex capable
#define MDI_SR_T4_CAPABLE BIT_15 // T4 capable

743
RtEthSample/oid.cpp Normal file
Просмотреть файл

@ -0,0 +1,743 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "adapter.h"
#include "device.h"
#include "oid.h"
#include "statistics.h"
#include "rxqueue.h"
#include "link.h"
const NDIS_OID NICSupportedOids[] =
{
//query
OID_GEN_SUPPORTED_LIST,
OID_GEN_HARDWARE_STATUS,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_TRANSMIT_BUFFER_SPACE,
OID_GEN_RECEIVE_BUFFER_SPACE,
OID_GEN_TRANSMIT_BLOCK_SIZE,
OID_GEN_RECEIVE_BLOCK_SIZE,
OID_GEN_VENDOR_ID,
OID_GEN_VENDOR_DESCRIPTION,
OID_GEN_VENDOR_DRIVER_VERSION,
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_MAC_OPTIONS,
OID_GEN_PHYSICAL_MEDIUM_EX,
// stats
OID_GEN_STATISTICS,
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_802_3_MAXIMUM_LIST_SIZE,
OID_802_3_XMIT_ONE_COLLISION,
OID_802_3_XMIT_MORE_COLLISIONS,
OID_802_3_XMIT_MAX_COLLISIONS,
OID_802_3_XMIT_UNDERRUN,
// query + set
OID_GEN_CURRENT_LOOKAHEAD,
OID_OFFLOAD_ENCAPSULATION,
// set
OID_GEN_CURRENT_PACKET_FILTER,
OID_802_3_MULTICAST_LIST,
OID_TCP_OFFLOAD_PARAMETERS,
};
_Requires_lock_held_(adapter->Lock)
void
RtAdapterSetOffloadParameters(
_In_ RT_ADAPTER *adapter,
_In_ NDIS_OFFLOAD_PARAMETERS *offloadParameters,
_Out_ NDIS_OFFLOAD *offloadConfiguration
)
{
switch (offloadParameters->IPv4Checksum)
{
case NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED:
adapter->IPChksumOffv4 = RtChecksumOffloadDisabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED:
adapter->IPChksumOffv4 = RtChecksumOffloadTxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED:
adapter->IPChksumOffv4 = RtChecksumOffloadRxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED:
adapter->IPChksumOffv4 = RtChecksumOffloadTxRxEnabled;
break;
}
switch (offloadParameters->TCPIPv4Checksum)
{
case NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED:
adapter->TCPChksumOffv4 = RtChecksumOffloadDisabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED:
adapter->TCPChksumOffv4 = RtChecksumOffloadTxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED:
adapter->TCPChksumOffv4 = RtChecksumOffloadRxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED:
adapter->TCPChksumOffv4 = RtChecksumOffloadTxRxEnabled;
break;
}
switch (offloadParameters->UDPIPv4Checksum)
{
case NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED:
adapter->UDPChksumOffv4 = RtChecksumOffloadDisabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED:
adapter->UDPChksumOffv4 = RtChecksumOffloadTxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED:
adapter->UDPChksumOffv4 = RtChecksumOffloadRxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED:
adapter->UDPChksumOffv4 = RtChecksumOffloadTxRxEnabled;
break;
}
switch (offloadParameters->TCPIPv6Checksum)
{
case NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED:
adapter->TCPChksumOffv6 = RtChecksumOffloadDisabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED:
adapter->TCPChksumOffv6 = RtChecksumOffloadTxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED:
adapter->TCPChksumOffv6 = RtChecksumOffloadRxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED:
adapter->TCPChksumOffv6 = RtChecksumOffloadTxRxEnabled;
break;
}
switch (offloadParameters->UDPIPv6Checksum)
{
case NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED:
adapter->UDPChksumOffv6 = RtChecksumOffloadDisabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED:
adapter->UDPChksumOffv6 = RtChecksumOffloadTxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED:
adapter->UDPChksumOffv6 = RtChecksumOffloadRxEnabled;
break;
case NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED:
adapter->UDPChksumOffv6 = RtChecksumOffloadTxRxEnabled;
break;
}
RtAdapterUpdateEnabledChecksumOffloads(adapter);
RtAdapterQueryOffloadConfiguration(adapter, offloadConfiguration);
}
void
EvtNetRequestQuerySupportedOids(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
__analysis_assume(OutputBufferLength >= sizeof(NICSupportedOids));
UNREFERENCED_PARAMETER((RequestQueue, OutputBufferLength));
TraceEntry();
RtlCopyMemory(OutputBuffer, NICSupportedOids, sizeof(NICSupportedOids));
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(NICSupportedOids));
TraceExit();
}
#define RTK_NIC_GBE_PCIE_ADAPTER_NAME "Realtek PCIe GBE Family Controller"
// OID_GEN_VENDOR_DESCRIPTION
void
EvtNetRequestQueryVendorDescription(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
__analysis_assume(OutputBufferLength >= sizeof(RTK_NIC_GBE_PCIE_ADAPTER_NAME));
UNREFERENCED_PARAMETER((RequestQueue, OutputBufferLength));
TraceEntry();
RtlCopyMemory(OutputBuffer, RTK_NIC_GBE_PCIE_ADAPTER_NAME, sizeof(RTK_NIC_GBE_PCIE_ADAPTER_NAME));
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(RTK_NIC_GBE_PCIE_ADAPTER_NAME));
TraceExit();
}
// OID_OFFLOAD_ENCAPSULATION
void
EvtNetRequestQueryOffloadEncapsulation(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
__analysis_assume(OutputBufferLength >= sizeof(NDIS_OFFLOAD_ENCAPSULATION));
UNREFERENCED_PARAMETER((OutputBufferLength));
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter);
RtlCopyMemory(OutputBuffer, &adapter->OffloadEncapsulation, sizeof(NDIS_OFFLOAD_ENCAPSULATION));
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(NDIS_OFFLOAD_ENCAPSULATION));
TraceExit();
}
void
EvtNetRequestQuerySuccess(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
UNREFERENCED_PARAMETER((RequestQueue, OutputBuffer, OutputBufferLength));
TraceEntry();
NetRequestCompleteWithoutInformation(Request, STATUS_SUCCESS);
TraceExit();
}
void
EvtNetRequestQueryUlong(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
__analysis_assume(OutputBufferLength >= sizeof(ULONG));
UNREFERENCED_PARAMETER((OutputBufferLength));
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid));
ULONG result = 0;
switch (oid)
{
case OID_GEN_HARDWARE_STATUS:
result = NdisHardwareStatusReady;
break;
case OID_GEN_VENDOR_ID:
result = *(PULONG)adapter->PermanentAddress;
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
result = NdisMedium802_3;
break;
case OID_GEN_PHYSICAL_MEDIUM_EX:
result = NdisPhysicalMedium802_3;
break;
case OID_GEN_CURRENT_LOOKAHEAD:
// "Current Lookahead" is the number of bytes following the Ethernet header
// that the NIC should indicate in the first NET_PACKET_FRAGMENT. Essentially
// a Current Lookahead of 8 would mean that each indicated NET_PACKET's first
// NET_PACKET_FRAGMENT would point to a buffer of at *least* size
// ETH_LENGTH_OF_HEADER + 8.
//
// Since the RTL8168D *always* indicates all traffic in a single, contiguous buffer,
// its driver just reports the maximum ethernet payload size as the current lookahead.
__fallthrough;
case OID_GEN_MAXIMUM_FRAME_SIZE:
result = RT_MAX_PACKET_SIZE - ETH_LENGTH_OF_HEADER;
break;
case OID_GEN_MAXIMUM_TOTAL_SIZE:
case OID_GEN_TRANSMIT_BLOCK_SIZE:
case OID_GEN_RECEIVE_BLOCK_SIZE:
result = (ULONG)RT_MAX_PACKET_SIZE;
break;
case OID_GEN_MAC_OPTIONS:
result = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
NDIS_MAC_OPTION_NO_LOOPBACK;
break;
case OID_GEN_TRANSMIT_BUFFER_SPACE:
result = RT_MAX_PACKET_SIZE * adapter->NumTcb;
break;
case OID_GEN_RECEIVE_BUFFER_SPACE:
if (adapter->RxQueue)
{
result = RT_MAX_PACKET_SIZE * NetRxQueueGetRingBuffer(adapter->RxQueue)->NumberOfElements;
}
else
{
result = 0;
}
break;
case OID_GEN_VENDOR_DRIVER_VERSION:
result = RT_VENDOR_DRIVER_VERSION;
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
result = RT_MAX_MCAST_LIST;
break;
default:
NT_ASSERTMSG("Unexpected OID", false);
break;
}
*(PULONG UNALIGNED)OutputBuffer = result;
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(ULONG));
TraceExit();
}
void
EvtNetRequestSetMulticastList(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_In_reads_bytes_(InputBufferLength)
PVOID InputBuffer,
UINT InputBufferLength)
{
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid));
if (InputBufferLength % ETH_LENGTH_OF_ADDRESS != 0)
{
NetRequestCompleteWithoutInformation(Request, NDIS_STATUS_INVALID_LENGTH);
goto Exit;
}
ULONG mcAddressCount = InputBufferLength / ETH_LENGTH_OF_ADDRESS;
NT_ASSERT(mcAddressCount <= RT_MAX_MCAST_LIST);
if (mcAddressCount > RT_MAX_MCAST_LIST)
{
NetRequestCompleteWithoutInformation(Request, NDIS_STATUS_INVALID_DATA);
goto Exit;
}
WdfSpinLockAcquire(adapter->Lock); {
adapter->MCAddressCount = mcAddressCount;
//
// Save the MC list
//
memcpy(
adapter->MCList,
InputBuffer,
mcAddressCount * ETH_LENGTH_OF_ADDRESS);
} WdfSpinLockRelease(adapter->Lock);
NetRequestSetDataComplete(Request, STATUS_SUCCESS, mcAddressCount * ETH_LENGTH_OF_ADDRESS);
Exit:
TraceExit();
}
void
EvtNetRequestSetPacketFilter(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_In_reads_bytes_(InputBufferLength)
PVOID InputBuffer,
UINT InputBufferLength)
{
UNREFERENCED_PARAMETER((InputBufferLength));
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid));
ULONG PacketFilter = *(PULONG UNALIGNED)InputBuffer;
if (PacketFilter & ~RT_SUPPORTED_FILTERS)
{
NetRequestCompleteWithoutInformation(Request, STATUS_NOT_SUPPORTED);
goto Exit;
}
WdfSpinLockAcquire(adapter->Lock); {
adapter->PacketFilter = PacketFilter;
RtAdapterUpdateRcr(adapter);
} WdfSpinLockRelease(adapter->Lock);
NetRequestSetDataComplete(Request, STATUS_SUCCESS, sizeof(ULONG));
Exit:
TraceExit();
}
void
EvtNetRequestSetCurrentLookahead(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_In_reads_bytes_(InputBufferLength)
PVOID InputBuffer,
UINT InputBufferLength)
{
UNREFERENCED_PARAMETER((InputBufferLength));
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid));
if (*(UNALIGNED PULONG) InputBuffer > (RT_MAX_PACKET_SIZE - ETH_LENGTH_OF_HEADER))
{
NetRequestCompleteWithoutInformation(Request, NDIS_STATUS_INVALID_DATA);
goto Exit;
}
// the set "Current Lookahead" value is not used as the "Current Lookahead" for the
// RTL8168D is *always* the maximum payload size.
NetRequestSetDataComplete(Request, STATUS_SUCCESS, sizeof(ULONG));
Exit:
TraceExit();
}
void
EvtNetRequestSetTcpOffloadParameters(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_In_reads_bytes_(InputBufferLength)
PVOID InputBuffer,
UINT InputBufferLength)
{
UNREFERENCED_PARAMETER(InputBufferLength);
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid));
NDIS_OFFLOAD offloadConfiguration;
WdfSpinLockAcquire(adapter->Lock); {
RtAdapterSetOffloadParameters(adapter, (NDIS_OFFLOAD_PARAMETERS*)InputBuffer, &offloadConfiguration);
} WdfSpinLockRelease(adapter->Lock);
{
NDIS_STATUS_INDICATION statusIndication;
RtlZeroMemory(&statusIndication, sizeof(NDIS_STATUS_INDICATION));
statusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
statusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
statusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION);
statusIndication.SourceHandle = adapter->NdisLegacyAdapterHandle;
statusIndication.StatusCode = NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG;
statusIndication.StatusBuffer = &offloadConfiguration;
statusIndication.StatusBufferSize = sizeof(offloadConfiguration);
NdisMIndicateStatusEx(adapter->NdisLegacyAdapterHandle, &statusIndication);
}
NetRequestSetDataComplete(Request, STATUS_SUCCESS, sizeof(NDIS_OFFLOAD_PARAMETERS));
TraceExit();
}
void
EvtNetRequestSetOffloadEncapsulation(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_In_reads_bytes_(InputBufferLength)
PVOID InputBuffer,
UINT InputBufferLength)
{
UNREFERENCED_PARAMETER(InputBufferLength);
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid));
NDIS_OFFLOAD_ENCAPSULATION *setEncapsulation = (NDIS_OFFLOAD_ENCAPSULATION*)InputBuffer;
adapter->OffloadEncapsulation.IPv4.Enabled = setEncapsulation->IPv4.Enabled;
adapter->OffloadEncapsulation.IPv4.EncapsulationType = setEncapsulation->IPv4.EncapsulationType;
adapter->OffloadEncapsulation.IPv4.HeaderSize = setEncapsulation->IPv4.HeaderSize;
adapter->OffloadEncapsulation.IPv6.Enabled = setEncapsulation->IPv6.Enabled;
adapter->OffloadEncapsulation.IPv6.EncapsulationType = setEncapsulation->IPv6.EncapsulationType;
adapter->OffloadEncapsulation.IPv6.HeaderSize = setEncapsulation->IPv6.HeaderSize;
NetRequestSetDataComplete(Request, STATUS_SUCCESS, sizeof(NDIS_OFFLOAD_ENCAPSULATION));
TraceExit();
}
void
EvtNetRequestQueryInterruptModeration(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
UNREFERENCED_PARAMETER((OutputBufferLength));
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
NDIS_INTERRUPT_MODERATION_PARAMETERS *imParameters = (NDIS_INTERRUPT_MODERATION_PARAMETERS*)OutputBuffer;
TraceEntryRtAdapter(adapter);
RtlZeroMemory(imParameters, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1);
imParameters->Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
imParameters->Header.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
imParameters->Header.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
if (adapter->InterruptModerationMode == RtInterruptModerationOff)
{
imParameters->InterruptModeration = NdisInterruptModerationNotSupported;
}
else if (adapter->InterruptModerationDisabled)
{
imParameters->InterruptModeration = NdisInterruptModerationDisabled;
}
else
{
imParameters->InterruptModeration = NdisInterruptModerationEnabled;
}
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1);
TraceExit();
}
void
EvtNetRequestSetInterruptModeration(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_In_reads_bytes_(InputBufferLength)
PVOID InputBuffer,
UINT InputBufferLength)
{
UNREFERENCED_PARAMETER((InputBufferLength));
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
NDIS_INTERRUPT_MODERATION_PARAMETERS *imParameters = (NDIS_INTERRUPT_MODERATION_PARAMETERS*)InputBuffer;
TraceEntryRtAdapter(adapter);
if (adapter->InterruptModerationMode == RtInterruptModerationOff)
{
NetRequestSetDataComplete(Request, NDIS_STATUS_INVALID_DATA, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1);
}
else
{
switch (imParameters->InterruptModeration)
{
case NdisInterruptModerationEnabled:
adapter->InterruptModerationDisabled = false;
break;
case NdisInterruptModerationDisabled:
adapter->InterruptModerationDisabled = true;
break;
}
RtAdapterUpdateInterruptModeration(adapter);
NetRequestSetDataComplete(Request, STATUS_SUCCESS, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1);
}
TraceExit();
}
typedef struct _RT_OID_QUERY {
NDIS_OID Oid;
PFN_NET_REQUEST_QUERY_DATA EvtQueryData;
UINT MinimumSize;
} RT_OID_QUERY, *PRT_OID_QUERY;
const RT_OID_QUERY ComplexQueries[] = {
{ OID_GEN_SUPPORTED_LIST, EvtNetRequestQuerySupportedOids, sizeof(NICSupportedOids) },
{ OID_GEN_STATISTICS, EvtNetRequestQueryAllStatistics, sizeof(NDIS_STATISTICS_INFO) },
{ OID_GEN_VENDOR_DESCRIPTION, EvtNetRequestQueryVendorDescription, sizeof(RTK_NIC_GBE_PCIE_ADAPTER_NAME) },
{ OID_OFFLOAD_ENCAPSULATION, EvtNetRequestQueryOffloadEncapsulation, sizeof(NDIS_OFFLOAD_ENCAPSULATION) },
{ OID_PNP_QUERY_POWER, EvtNetRequestQuerySuccess, 0 },
{ OID_GEN_INTERRUPT_MODERATION, EvtNetRequestQueryInterruptModeration, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1 },
};
typedef struct _RT_OID_SET {
NDIS_OID Oid;
PFN_NET_REQUEST_SET_DATA EvtSetData;
UINT MinimumSize;
} RT_OID_SET, *PRT_OID_SET;
const RT_OID_SET ComplexSets[] = {
{ OID_802_3_MULTICAST_LIST, EvtNetRequestSetMulticastList, 0 },
{ OID_GEN_CURRENT_PACKET_FILTER, EvtNetRequestSetPacketFilter, sizeof(ULONG) },
{ OID_GEN_CURRENT_LOOKAHEAD, EvtNetRequestSetCurrentLookahead, sizeof(ULONG) },
{ OID_TCP_OFFLOAD_PARAMETERS, EvtNetRequestSetTcpOffloadParameters, sizeof(NDIS_OFFLOAD_PARAMETERS) },
{ OID_OFFLOAD_ENCAPSULATION, EvtNetRequestSetOffloadEncapsulation, sizeof(NDIS_OFFLOAD_ENCAPSULATION) },
{ OID_GEN_INTERRUPT_MODERATION, EvtNetRequestSetInterruptModeration, NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1 },
};
const NDIS_OID UlongQueries[] = {
OID_GEN_HARDWARE_STATUS,
OID_GEN_VENDOR_ID,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_PHYSICAL_MEDIUM_EX,
OID_GEN_CURRENT_LOOKAHEAD,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_TRANSMIT_BLOCK_SIZE,
OID_GEN_RECEIVE_BLOCK_SIZE,
OID_GEN_MAC_OPTIONS,
OID_GEN_TRANSMIT_BUFFER_SPACE,
OID_GEN_RECEIVE_BUFFER_SPACE,
OID_GEN_VENDOR_DRIVER_VERSION,
OID_802_3_MAXIMUM_LIST_SIZE,
};
const NDIS_OID StatisticQueries[] = {
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_802_3_XMIT_ONE_COLLISION,
OID_802_3_XMIT_MORE_COLLISIONS,
OID_802_3_XMIT_MAX_COLLISIONS,
OID_802_3_XMIT_UNDERRUN,
};
NTSTATUS
RtInitializeAdapterRequestQueue(
_In_ RT_ADAPTER *adapter)
{
NTSTATUS status;
NET_REQUEST_QUEUE_CONFIG queueConfig;
TraceEntryRtAdapter(adapter);
NET_REQUEST_QUEUE_CONFIG_INIT_DEFAULT_SEQUENTIAL(
&queueConfig,
adapter->NetAdapter);
// registers those OIDs that can be completed as ULONGs
for (ULONG i = 0; i < ARRAYSIZE(UlongQueries); i++)
{
NET_REQUEST_QUEUE_CONFIG_ADD_QUERY_DATA_HANDLER(
&queueConfig,
UlongQueries[i],
EvtNetRequestQueryUlong,
sizeof(ULONG));
}
// registers individual statistic OIDs
for (ULONG i = 0; i < ARRAYSIZE(StatisticQueries); i++)
{
NET_REQUEST_QUEUE_CONFIG_ADD_QUERY_DATA_HANDLER(
&queueConfig,
StatisticQueries[i],
EvtNetRequestQueryIndividualStatistics,
sizeof(ULONG));
}
// registers query OIDs with complex behaviors
for (ULONG i = 0; i < ARRAYSIZE(ComplexQueries); i++)
{
NET_REQUEST_QUEUE_CONFIG_ADD_QUERY_DATA_HANDLER(
&queueConfig,
ComplexQueries[i].Oid,
ComplexQueries[i].EvtQueryData,
ComplexQueries[i].MinimumSize);
}
// registers set OIDs with complex behaviors
for (ULONG i = 0; i < ARRAYSIZE(ComplexSets); i++)
{
NET_REQUEST_QUEUE_CONFIG_ADD_SET_DATA_HANDLER(
&queueConfig,
ComplexSets[i].Oid,
ComplexSets[i].EvtSetData,
ComplexSets[i].MinimumSize);
}
//
// Create the default NETREQUESTQUEUE.
//
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetRequestQueueCreate(&queueConfig, WDF_NO_OBJECT_ATTRIBUTES, NULL));
Exit:
TraceExitResult(status);
return status;
}

16
RtEthSample/oid.h Normal file
Просмотреть файл

@ -0,0 +1,16 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
NTSTATUS RtInitializeAdapterRequestQueue(_In_ RT_ADAPTER *adapter);
EVT_NET_REQUEST_DEFAULT_SET_DATA EvtAdapterGenericSetInformation;

63
RtEthSample/phy.cpp Normal file
Просмотреть файл

@ -0,0 +1,63 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "phy.h"
#include "adapter.h"
void
RtAdapterWritePhyUint16(_In_ RT_ADAPTER *adapter, UCHAR regAddr, USHORT regData)
{
adapter->CSRAddress->PhyAccessReg = PHYAR_Flag | ((ULONG)regAddr << 16) | (ULONG)regData;
ULONG accessReg = adapter->CSRAddress->PhyAccessReg;
for (ULONG timeout = 0; timeout < 20; timeout++)
{
if (!(accessReg & PHYAR_Flag))
break;
KeStallExecutionProcessor(50);
accessReg = adapter->CSRAddress->PhyAccessReg;
}
KeStallExecutionProcessor(20);
}
USHORT
RtAdapterReadPhyUint16(
RT_ADAPTER *adapter,
UCHAR regAddr)
{
USHORT RetVal = USHORT_MAX;
adapter->CSRAddress->PhyAccessReg = ((ULONG)regAddr << 16);
ULONG accessReg = adapter->CSRAddress->PhyAccessReg;
for (ULONG timeout = 0; timeout < 20; timeout++)
{
if (accessReg & PHYAR_Flag)
{
RetVal = (USHORT)(accessReg & 0x0000ffff);
break;
}
KeStallExecutionProcessor(50);
accessReg = adapter->CSRAddress->PhyAccessReg;
}
KeStallExecutionProcessor(20);
return RetVal;
}

18
RtEthSample/phy.h Normal file
Просмотреть файл

@ -0,0 +1,18 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
void
RtAdapterWritePhyUint16(_In_ RT_ADAPTER *adapter, UCHAR RegAddr, USHORT RegData);
USHORT
RtAdapterReadPhyUint16(RT_ADAPTER *adapter, UCHAR regAddr);

175
RtEthSample/power.cpp Normal file
Просмотреть файл

@ -0,0 +1,175 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "trace.h"
#include "power.h"
#include "device.h"
#include "adapter.h"
#include "link.h"
#include "phy.h"
#include "interrupt.h"
void
RtAdapterEnableMagicPacket(_In_ RT_ADAPTER *adapter)
{
// enable cr9346 before writing config registers
RtAdapterEnableCR9346Write(adapter); {
adapter->CSRAddress->CONFIG3 |= CONFIG3_Magic;
} RtAdapterDisableCR9346Write(adapter);
}
void
RtAdapterDisableMagicPacket(_In_ RT_ADAPTER *adapter)
{
// enable cr9346 before writing config registers
RtAdapterEnableCR9346Write(adapter); {
adapter->CSRAddress->CONFIG3 &= ~CONFIG3_Magic;
} RtAdapterDisableCR9346Write(adapter);
}
void
RtAdapterRaiseToD0(_In_ RT_ADAPTER *adapter)
{
RtAdapterSetupHardware(adapter);
RtAdapterIssueFullReset(adapter);
RtAdapterPowerUpPhy(adapter);
RtAdapterResetPhy(adapter);
RtAdapterIssueFullReset(adapter);
WdfSpinLockAcquire(adapter->Lock); {
RtAdapterSetupCurrentAddress(adapter);
} WdfSpinLockRelease(adapter->Lock);
RtAdapterPushPhySettings(adapter);
RtAdapterInitializeAutoNegotiation(adapter);
}
_Use_decl_annotations_
NTSTATUS
EvtDeviceD0Entry(
_In_ WDFDEVICE wdfDevice,
WDF_POWER_DEVICE_STATE previousState)
{
RT_ADAPTER *adapter = RtGetDeviceContext(wdfDevice)->Adapter;
TraceEntryRtAdapter(
adapter,
TraceLoggingUInt32(previousState, "PreviousState"));
// Interrupts will be fully enabled in EvtInterruptEnable
RtInterruptInitialize(adapter->Interrupt);
RtAdapterUpdateEnabledChecksumOffloads(adapter);
RtAdapterUpdateInterruptModeration(adapter);
if (previousState != WdfPowerDeviceD3Final)
{
// We're coming back from low power, undo what
// we did in EvtDeviceD0Exit
RtAdapterRaiseToD0(adapter);
// Set up the multicast list address
// return to D0, WOL no more require to RX all multicast packets
RtAdapterPushMulticastList(adapter);
// Update link state
// Lock not required because of serialized power transition.
NET_ADAPTER_LINK_STATE linkState;
RtAdapterQueryLinkState(adapter, &linkState);
NetAdapterSetCurrentLinkState(adapter->NetAdapter, &linkState);
}
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS
EvtDeviceD0Exit(
_In_ WDFDEVICE Device,
_In_ WDF_POWER_DEVICE_STATE TargetState
)
{
TraceEntry();
if (TargetState != WdfPowerDeviceD3Final)
{
RT_ADAPTER *adapter = RtGetDeviceContext(Device)->Adapter;
NET_ADAPTER_LINK_STATE linkState;
NET_ADAPTER_LINK_STATE_INIT(
&linkState,
NDIS_LINK_SPEED_UNKNOWN,
MediaConnectStateUnknown,
MediaDuplexStateUnknown,
NetAdapterPauseFunctionsUnknown,
NET_ADAPTER_AUTO_NEGOTIATION_NO_FLAGS);
NetAdapterSetCurrentLinkState(adapter->NetAdapter, &linkState);
// acknowledge interrupt
USHORT isr = adapter->CSRAddress->ISR;
adapter->CSRAddress->ISR = isr;
}
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
NTSTATUS
EvtDeviceArmWakeFromSx(
_In_ WDFDEVICE wdfDevice)
{
RT_ADAPTER *adapter = RtGetDeviceContext(wdfDevice)->Adapter;
TraceEntryRtAdapter(adapter);
// Use NETPOWERSETTINGS to check if we should enable wake from magic packet
NETPOWERSETTINGS powerSettings = NetAdapterGetPowerSettings(adapter->NetAdapter);
ULONG enabledWakePatterns = NetPowerSettingsGetEnabledWakePatterns(powerSettings);
if (enabledWakePatterns & NET_ADAPTER_WAKE_MAGIC_PACKET)
{
RtAdapterEnableMagicPacket(adapter);
}
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
void
EvtDeviceDisarmWakeFromSx(
_In_ WDFDEVICE wdfDevice)
{
RT_ADAPTER *adapter = RtGetDeviceContext(wdfDevice)->Adapter;
TraceEntryRtAdapter(adapter);
RtAdapterDisableMagicPacket(adapter);
TraceExit();
}

17
RtEthSample/power.h Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
EVT_WDF_DEVICE_D0_ENTRY EvtDeviceD0Entry;
EVT_WDF_DEVICE_D0_EXIT EvtDeviceD0Exit;
EVT_WDF_DEVICE_ARM_WAKE_FROM_SX EvtDeviceArmWakeFromSx;
EVT_WDF_DEVICE_DISARM_WAKE_FROM_SX EvtDeviceDisarmWakeFromSx;

27
RtEthSample/precomp.h Normal file
Просмотреть файл

@ -0,0 +1,27 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
#include <ntddk.h>
#include <wdf.h>
#include <netadaptercx.h>
#include <ntintsafe.h>
#include <netiodef.h>
// Avoid putting user headers into the precomp header.
// Exceptions here include:
// 1. Constant definitions
// 2. Definitions for hardware structures that cannot change
// 3. Forward declarations
#include "forward.h"
#include "rt_def.h"

574
RtEthSample/rt_def.h Normal file
Просмотреть файл

@ -0,0 +1,574 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
===========================================================================
This file contains definitions for structures, registers, and constants for
Realtek Gigabit Ethernet network cards. This is all specific to Realtek
hardware.
--*/
#pragma once
#pragma region Bit Definitions
#define BIT_0 0x0001
#define BIT_1 0x0002
#define BIT_2 0x0004
#define BIT_3 0x0008
#define BIT_4 0x0010
#define BIT_5 0x0020
#define BIT_6 0x0040
#define BIT_7 0x0080
#define BIT_8 0x0100
#define BIT_9 0x0200
#define BIT_10 0x0400
#define BIT_11 0x0800
#define BIT_12 0x1000
#define BIT_13 0x2000
#define BIT_14 0x4000
#define BIT_15 0x8000
#define BIT_16 0x010000
#define BIT_17 0x020000
#define BIT_18 0x040000
#define BIT_19 0x080000
#define BIT_20 0x100000
#define BIT_21 0x200000
#define BIT_22 0x400000
#define BIT_23 0x800000
#define BIT_24 0x01000000
#define BIT_25 0x02000000
#define BIT_26 0x04000000
#define BIT_27 0x08000000
#define BIT_28 0x10000000
#define BIT_29 0x20000000
#define BIT_30 0x40000000
#define BIT_31 0x80000000
#pragma endregion
#pragma region Standards Limits
#define FRAME_CRC_SIZE 4
#define VLAN_HEADER_SIZE 4
#define RSVD_BUF_SIZE 8
// Ethernet Frame Sizes
#define ETHERNET_ADDRESS_LENGTH 6
#pragma endregion
#pragma region Hardware Limits
// packet and header sizes
#define RT_MAX_PACKET_SIZE (1514)
#define RT_MAX_FRAME_SIZE (RT_MAX_PACKET_SIZE + VLAN_HEADER_SIZE + FRAME_CRC_SIZE)
// maximum link speed for send and recv in bps
#define RT_MEDIA_MAX_SPEED 1'000'000'000
// Phy related constants
#define PHY_RESET_TIME (1250 * 4) // (1250 * 4 *100 = 500000) phy reset should complete within 500ms (from spec)
#pragma endregion
#pragma region Software Limits
// max number of physical fragments supported per TCB
#define RT_MAX_PHYS_BUF_COUNT 16
// multicast list size
#define RT_MAX_MCAST_LIST 32
#define RT_MIN_TCB 32
#define RT_MAX_TCB 128
#pragma endregion
#pragma region Version
// Driver version numbers
// update the driver version number every time you release a new driver
// The high word is the major version. The low word is the minor version.
// this should be the same as the version reported in miniport driver characteristics
#define RT_MAJOR_DRIVER_VERSION 1
#define RT_MINOR_DRIVER_VERISON 0
#define RT_VENDOR_DRIVER_VERSION ((RT_MAJOR_DRIVER_VERSION << 16) | RT_MINOR_DRIVER_VERISON)
#pragma endregion
#pragma region Error Codes
// Error log definitions
#define ERRLOG_OUT_OF_SG_RESOURCES 0x00000409L
#define ERRLOG_NO_MEMORY_RESOURCE 0x00000605L
#pragma endregion
#pragma region Capabilities
// supported filters
#define RT_SUPPORTED_FILTERS ( \
NET_PACKET_FILTER_TYPE_DIRECTED | \
NET_PACKET_FILTER_TYPE_MULTICAST | \
NET_PACKET_FILTER_TYPE_BROADCAST | \
NET_PACKET_FILTER_TYPE_PROMISCUOUS | \
NET_PACKET_FILTER_TYPE_ALL_MULTICAST)
#define NIC_SUPPORTED_STATISTICS ( \
NET_ADAPTER_STATISTICS_XMIT_OK | \
NET_ADAPTER_STATISTICS_RCV_OK | \
NET_ADAPTER_STATISTICS_XMIT_ERROR | \
NET_ADAPTER_STATISTICS_RCV_ERROR | \
NET_ADAPTER_STATISTICS_RCV_CRC_ERROR | \
NET_ADAPTER_STATISTICS_RCV_NO_BUFFER | \
NET_ADAPTER_STATISTICS_TRANSMIT_QUEUE_LENGTH | \
NET_ADAPTER_STATISTICS_GEN_STATISTICS)
#pragma endregion
#pragma region Hardware Memory Descriptors
typedef struct _RT_TAG_802_1Q
{
union
{
struct
{
USHORT VLanID1 : 4;
USHORT CFI : 1;
USHORT Priority: 3;
USHORT VLanID2 : 8;
} TagHeader;
USHORT Value;
};
} RT_TAG_802_1Q;
typedef struct _RT_TX_DESC
{
struct
{
unsigned short length;
unsigned short status;
RT_TAG_802_1Q VLAN_TAG;
unsigned short OffloadGsoMssTagc;
} TxDescDataIpv6Rss_All;
PHYSICAL_ADDRESS BufferAddress;
} RT_TX_DESC;
typedef struct _RT_RX_DESC
{
struct
{
unsigned short length : 14;
unsigned short TcpUdpFailure : 2;
unsigned short status;
RT_TAG_802_1Q VLAN_TAG;
unsigned short IpRssTava;
} RxDescDataIpv6Rss;
PHYSICAL_ADDRESS BufferAddress;
} RT_RX_DESC;
typedef struct _RT_TALLY
{
ULONGLONG TxOK;
ULONGLONG RxOK;
ULONGLONG TxERR;
ULONG RxERR;
USHORT MissPkt;
USHORT FAE;
ULONG Tx1Col;
ULONG TxMCol;
ULONGLONG RxOKPhy;
ULONGLONG RxOKBrd;
ULONG RxOKMul;
USHORT TxAbt;
USHORT TxUndrn; //only possible on jumbo frames
} RT_TALLY;
#pragma endregion
#pragma region Control Block
#pragma pack(1)
//-------------------------------------------------------------------------
// Control/Status Registers (CSR)
//-------------------------------------------------------------------------
typedef struct _RT_MAC
{
// 00
UCHAR ID[6];
USHORT RESV_06_07;
UCHAR MulticastReg0;
UCHAR MulticastReg1;
UCHAR MulticastReg2;
UCHAR MulticastReg3;
UCHAR MulticastReg4;
UCHAR MulticastReg5;
UCHAR MulticastReg6;
UCHAR MulticastReg7;
// 10
ULONG DTCCRLow;
ULONG DTCCRHigh;
ULONG REG_18_1B;
ULONG REG_1C_1F;
// 20
ULONG TNPDSLow;
ULONG TNPDSHigh;
ULONG THPDSLow;
ULONG THPDSHigh;
// 30
USHORT ResendIntrTmr;
USHORT ResendIntrTmrCnt;
USHORT RTRxDMABC;
UCHAR ERSR;
UCHAR CmdReg;
UCHAR TPPoll;
UCHAR RESV_39;
UCHAR RESV_3A;
UCHAR RESV_3B;
USHORT IMR;
USHORT ISR;
// 40
ULONG TCR;
ULONG RCR;
ULONG TimerCTR;
ULONG REG_4C_4F;
// 50
UCHAR CR9346;
UCHAR CONFIG0;
UCHAR CONFIG1;
UCHAR CONFIG2;
UCHAR CONFIG3;
UCHAR CONFIG4;
UCHAR CONFIG5;
// Transmit Descriptor Fetch Number - controls
// the transmit descriptor fetch number
UCHAR TDFNR;
ULONG Timer;
ULONG REG_5C_5F;
// 60
ULONG PhyAccessReg;
ULONG REG_64_67;
ULONG REG_68_6B;
UCHAR PhyStatus;
UCHAR REG_6D;
UCHAR REG_6E;
UCHAR REG_6F;
// 70
ULONG REG_70_73;
ULONG REG_74_77;
ULONG REG_78_7B;
ULONG REG_7C_7F;
// 80
ULONG REG_80_83;
ULONG REG_84_87;
ULONG REG_88_8B;
ULONG REG_8C_8F;
// 90
ULONG REG_90_93;
ULONG REG_94_97;
ULONG REG_98_9B;
ULONG REG_9C_9F;
// A0
ULONG REG_A0_A3;
ULONG REG_A4_A7;
ULONG REG_A8_AB;
ULONG REG_AC_AF;
// B0
ULONG REG_B0_B3;
ULONG REG_B4_B7;
ULONG REG_B8_BB;
ULONG REG_BC_BF;
// C0
ULONG REG_C0_C3;
ULONG REG_C4_C7;
ULONG REG_C8_CB;
ULONG REG_CC_CF;
// D0
struct
{
UCHAR RESV_D0;
UCHAR RESV_D1;
UCHAR RESV_D2;
UCHAR RESV_D3;
} REG_D0_D3;
ULONG REG_D4_D7;
USHORT RESV_D8_D9;
USHORT RMS;
ULONG REG_DC_DF;
// E0
USHORT CPCR;
struct
{
UCHAR RxTimerNum;
UCHAR TxTimerNum;
} IntMiti;
ULONG RDSARLow;
ULONG RDSARHigh;
struct
{
UCHAR MTPS;
UCHAR RESV_ED;
UCHAR RESV_EE;
UCHAR RESV_EF;
} MtpsReg;
// F0
struct
{
UCHAR RESV_F0;
UCHAR RESV_F1;
UCHAR RESV_F2;
UCHAR RESV_F3;
} REG_F0_F3;
ULONG REG_F4_F7;
ULONG REG_F8_FB;
ULONG REG_FC_FF;
} RT_MAC;
static_assert(sizeof(RT_MAC) == 0x100, "Size of RT_MAC is specified by hardware");
#pragma pack()
#pragma endregion
#pragma region Register Constants
// MulticastReg0: 0x8
#define MAX_NIC_MULTICAST_REG 8
// DTCCR: 0x10
#define DTCCR_Cmd BIT_3
#define DTCCR_Clr BIT_0
// CmdReg: 0x37
#define CR_RST 0x10
#define CR_RE 0x08
#define CR_TE 0x04
#define CR_STOP_REQ 0x80
// TPPoll: 0x38
#define TPPoll_NPQ 0x40 // normal priority queue polling
// IMR: 0x3C, ISR: 0x3E
#define ISRIMR_ROK BIT_0
#define ISRIMR_RER BIT_1
#define ISRIMR_TOK BIT_2
#define ISRIMR_TER BIT_3
#define ISRIMR_RDU BIT_4
#define ISRIMR_LINK_CHG BIT_5
#define ISRIMR_RX_FOVW BIT_6
#define ISRIMR_TDU BIT_7
#define ISRIMR_SW_INT BIT_8
#define ISRIMR_TDMAOK BIT_10
#define ISRIMR_TIME_INT BIT_14
#define ISRIMR_SERR BIT_15
// TCR: 0x40
#define TCR_MXDMA_OFFSET 8
#define TCR_IFG2 BIT_19
#define TCR_IFG0 BIT_24 // Interframe Gap0
#define TCR_IFG1 BIT_25 // Interframe Gap1
// RCR: 0x44
#define RCR_AAP BIT_0 // accept all physical address
#define RCR_APM BIT_1 // accept physical match
#define RCR_AM BIT_2 // accept multicast
#define RCR_AB BIT_3 // accept broadcast
#define RCR_AR BIT_4 // accept runt packet
#define RCR_AER BIT_5 // accept error packet
#define RCR_9356SEL BIT_6 // EEPROM type
#define RCR_MXDMA_OFFSET 8
#define RCR_FIFO_OFFSET 13
#define RCR_Ipv6Rss_RX_ENABLE_FETCH_NUM BIT_14
#define RCR_Ipv6Rss_RX_FETCH_NUM_OFFSET 17
typedef enum _RT_TCR_RCR_MXDMA : UCHAR
{
TCR_RCR_MXDMA_16_BYTES = 0,
TCR_RCR_MXDMA_32_BYTES,
TCR_RCR_MXDMA_64_BYTES,
TCR_RCR_MXDMA_128_BYTES,
TCR_RCR_MXDMA_256_BYTES,
TCR_RCR_MXDMA_512_BYTES,
TCR_RCR_MXDMA_1024_BYTES,
TCR_RCR_MXDMA_UNLIMITED
} RT_TCR_RCR_MXDMA;
// CR9346: 0x50
#define CR9346_EEDO BIT_0
#define CR9346_EEDI BIT_1
#define CR9346_EESK BIT_2
#define CR9346_EECS BIT_3
#define CR9346_EEPROM_FREE BIT_5
#define CR9346_EEM0 BIT_6 // select 8139 operating mode
#define CR9346_EEM1 BIT_7 // 00: normal
// CONFIG3: 0x54
#define CONFIG3_Magic 0x20 // Wake on Magic packet
// PhyAccessReg: 0x60
#define PHYAR_Flag BIT_31
#define PHY_REG_BMSR_AUTO_NEG_COMPLETE BIT_5
typedef enum _RT_PHY_REG
{
PHY_REG_BMCR = 0,
PHY_REG_BMSR,
PHY_REG_PHYAD1,
PHY_REG_PHYAD2,
PHY_REG_ANAR,
PHY_REG_ANLPAR,
PHY_REG_ANER,
PHY_REG_ANNPTR,
PHY_REG_ANNRPR,
PHY_REG_GBCR,
PHY_REG_GBSR,
PHY_REG_RESV_11,
PHY_REG_RESV_12,
PHY_REG_RESV_13,
PHY_REG_RESV_14,
PHY_REG_GBESR
} RT_PHY_REG;
#define ANER_LP_AUTO_NEG_ABLE BIT_0
#define ANAR_10_HALF BIT_5
#define ANAR_10_FULL BIT_6
#define ANAR_100_HALF BIT_7
#define ANAR_100_FULL BIT_8
#define ANAR_MAC_PAUSE BIT_10
#define ANAR_ASYM_PAUSE BIT_11
#define GBCR_1000_FULL BIT_9
#define GBCR_1000_HALF BIT_8
// PhyStatus: 0x6C
#define PHY_STATUS_CABLE_PLUG BIT_7
#define PHY_STATUS_TX_FLOW_CTRL BIT_6
#define PHY_STATUS_RX_FLOW_CTRL BIT_5
#define PHY_STATUS_1000MF BIT_4
#define PHY_STATUS_100M BIT_3
#define PHY_STATUS_10M BIT_2
#define PHY_STATUS_LINK_ON BIT_1
#define PHY_STATUS_FULL_DUPLEX BIT_0
// PhyStatus: 0x6C
#define PHY_LINK_STATUS BIT_1
// CPCR: 0xE0
#define CPCR_RX_VLAN BIT_6
#define CPCR_RX_CHECKSUM BIT_5
#define CPCR_PCI_DAC BIT_4
#define CPCR_MUL_RW BIT_3
#define CPCR_INT_MITI_TIMER_UNIT_1 BIT_1
#define CPCR_INT_MITI_TIMER_UNIT_0 BIT_0
#define CPCR_DISABLE_INT_MITI_PKT_NUM BIT_7
#pragma endregion
#pragma region Descriptor Constants
// Receive status
#define RXS_OWN BIT_15
#define RXS_EOR BIT_14
#define RXS_FS BIT_13
#define RXS_LS BIT_12
#define RXS_OWN_HIGHBYTE BIT_7
#define RXS_FS_HIGHBYTE BIT_5
#define RXS_LS_HIGHBYTE BIT_4
#define RXS_MAR BIT_11
#define RXS_PAM BIT_10
#define RXS_BAR BIT_9
#define RXS_BOVF BIT_8
#define RXS_FOVF BIT_7
#define RXS_RWT BIT_6
#define RXS_RES BIT_5
#define RXS_RUNT BIT_4
#define RXS_CRC BIT_3
#define RXS_UDPIP_PACKET BIT_2
#define RXS_TCPIP_PACKET BIT_1
#define RXS_IP_PACKET BIT_2|BIT_1
#define RXS_IPF BIT_0
#define RXS_UDPF BIT_1
#define RXS_TCPF BIT_0
// receive extended status
#define RXS_IPV6RSS_IS_IPV6 BIT_15
#define RXS_IPV6RSS_IS_IPV4 BIT_14
#define RXS_IPV6RSS_UDPF BIT_10
#define RXS_IPV6RSS_TCPF BIT_9
// Transmit status
#define TXS_CC3_0 BIT_0|BIT_1|BIT_2|BIT_3
#define TXS_EXC BIT_4
#define TXS_LNKF BIT_5
#define TXS_OWC BIT_6
#define TXS_TES BIT_7
#define TXS_UNF BIT_9
#define TXS_LGSEN BIT_11
#define TXS_LS BIT_12
#define TXS_FS BIT_13
#define TXS_EOR BIT_14
#define TXS_OWN BIT_15
#define TXS_OWN_HIGHBYTE BIT_7
#define TXS_TCPCS BIT_0
#define TXS_UDPCS BIT_1
#define TXS_IPCS BIT_2
// transmit extended status
#define TXS_IPV6RSS_UDPCS BIT_15
#define TXS_IPV6RSS_TCPCS BIT_14
#define TXS_IPV6RSS_IPV4CS BIT_13
#define TXS_IPV6RSS_IS_IPV6 BIT_12
#define TXS_IPV6RSS_TAGC BIT_1
#define TXS_IPV6RSS_TCPHDR_OFFSET 2
#define TXS_IPV6RSS_MSS_OFFSET 2
#pragma endregion

54
RtEthSample/rtk.rc Normal file
Просмотреть файл

@ -0,0 +1,54 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
Module Name:
rtk.rc
Abstract:
Internal resource file for filter.
--*/
#include "windows.h"
#include "ntverp.h"
//
// the spaces characters in string are reserved for patch tool
//
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_NETWORK
#define VER_FILEDESCRIPTION_STR "Realtek PCIe GBE Family Controller NetAdapter Sample Driver"
#define VER_INTERNALNAME_STR "RtEthSample.sys"
#define VER_ORIGINALFILENAME_STR "RtEthSample.sys"
#define VER_FILEVERSION 1,0
#define VER_FILEVERSION_STR "1.0"
#undef VER_PRODUCTVERSION
#define VER_PRODUCTVERSION VER_FILEVERSION
#undef VER_PRODUCTVERSION_STR
#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
#define VER_LEGALCOPYRIGHT_STR "Copyright (c) Microsoft Corporation. All rights reserved."
#undef VER_COMPANYNAME_STR
#define VER_COMPANYNAME_STR "Realtek"
#undef VER_PRODUCTNAME_STR
#define VER_PRODUCTNAME_STR "Realtek PCIe GBE Family Adapters"
#define VER_LANGNEUTRAL
#include "common.ver"

346
RtEthSample/rxqueue.cpp Normal file
Просмотреть файл

@ -0,0 +1,346 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "device.h"
#include "rxqueue.h"
#include "trace.h"
#include "adapter.h"
#include "interrupt.h"
void
RtUpdateRecvStats(
_In_ RT_RXQUEUE *rx,
_In_ RT_RX_DESC const *rxd,
ULONG length)
{
// Unlike for Tx, the hardware has counters for Broadcast, Multicast,
// and Unicast inbound packets. So, we defer to the hardware counter for
// # of Rx transmissions.
if (rxd->RxDescDataIpv6Rss.status & RXS_BAR)
{
rx->Adapter->InBroadcastOctets += length;
}
else if (rxd->RxDescDataIpv6Rss.status & RXS_MAR)
{
rx->Adapter->InMulticastOctets += length;
}
else
{
rx->Adapter->InUcastOctets += length;
}
}
void
RxFillChecksumInfo(
_In_ RT_RXQUEUE const *rx,
_In_ RT_RX_DESC const *rxd,
_Inout_ NET_PACKET *packet)
{
packet->Layout.Layer2Type = NET_PACKET_LAYER2_TYPE_ETHERNET;
packet->Checksum.Layer2 =
(rxd->RxDescDataIpv6Rss.status & RXS_CRC)
? NET_PACKET_RX_CHECKSUM_INVALID
: NET_PACKET_RX_CHECKSUM_VALID;
USHORT const isIpv4 = rxd->RxDescDataIpv6Rss.IpRssTava & RXS_IPV6RSS_IS_IPV4;
USHORT const isIpv6 = rxd->RxDescDataIpv6Rss.IpRssTava & RXS_IPV6RSS_IS_IPV6;
NT_ASSERT(!(isIpv4 && isIpv6));
if (isIpv4)
{
packet->Layout.Layer3Type = NET_PACKET_LAYER3_TYPE_IPV4_UNSPECIFIED_OPTIONS;
if (rx->Adapter->IpRxHwChkSumv4)
{
packet->Checksum.Layer3 =
(rxd->RxDescDataIpv6Rss.status & RXS_IPF)
? NET_PACKET_RX_CHECKSUM_INVALID
: NET_PACKET_RX_CHECKSUM_VALID;
}
}
else if (isIpv6)
{
packet->Layout.Layer3Type = NET_PACKET_LAYER3_TYPE_IPV6_UNSPECIFIED_EXTENSIONS;
}
else
{
return;
}
USHORT const isTcp = rxd->RxDescDataIpv6Rss.status & RXS_TCPIP_PACKET;
USHORT const isUdp = rxd->RxDescDataIpv6Rss.status & RXS_UDPIP_PACKET;
NT_ASSERT(!(isTcp && isUdp));
if (isTcp)
{
packet->Layout.Layer4Type = NET_PACKET_LAYER4_TYPE_TCP;
if ((isIpv4 && rx->Adapter->TcpRxHwChkSumv4) ||
(isIpv6 && rx->Adapter->TcpRxHwChkSumv6))
{
packet->Checksum.Layer4 =
(rxd->RxDescDataIpv6Rss.IpRssTava & RXS_IPV6RSS_TCPF)
? NET_PACKET_RX_CHECKSUM_INVALID
: NET_PACKET_RX_CHECKSUM_VALID;
}
}
else if (isUdp)
{
packet->Layout.Layer4Type = NET_PACKET_LAYER4_TYPE_UDP;
if ((isIpv4 && rx->Adapter->UdpRxHwChkSumv4) ||
(isIpv6 && rx->Adapter->UdpRxHwChkSumv6))
{
packet->Checksum.Layer4 =
(rxd->RxDescDataIpv6Rss.IpRssTava & RXS_IPV6RSS_UDPF)
? NET_PACKET_RX_CHECKSUM_INVALID
: NET_PACKET_RX_CHECKSUM_VALID;
}
}
}
void
RxIndicateReceives(
_In_ RT_RXQUEUE *rx)
{
NET_RING_BUFFER *rb = rx->RingBuffer;
UINT32 i;
for (i = rb->BeginIndex; i != rb->NextIndex; i = NetRingBufferIncrementIndex(rb, i))
{
RT_RX_DESC *rxd = &rx->RxdBase[i];
NET_PACKET *packet = NetRingBufferGetPacketAtIndex(rb, i);
if (0 != (rxd->RxDescDataIpv6Rss.status & RXS_OWN))
break;
packet->Data.ValidLength = rxd->RxDescDataIpv6Rss.length - FRAME_CRC_SIZE;
packet->Data.Offset = 0;
RxFillChecksumInfo(rx, rxd, packet);
RtUpdateRecvStats(rx, rxd, packet->Data.ValidLength);
}
rb->BeginIndex = i;
}
void
RxPostBuffers(_In_ RT_RXQUEUE *rx)
{
NET_RING_BUFFER *rb = rx->RingBuffer;
UINT32 initialIndex = rb->NextIndex;
while (true)
{
UINT32 index = rb->NextIndex;
RT_RX_DESC *rxd = &rx->RxdBase[index];
NET_PACKET *packet = NetRingBufferAdvanceNextPacket(rb);
if (!packet)
break;
rxd->BufferAddress = packet->Data.DmaLogicalAddress;
rxd->RxDescDataIpv6Rss.TcpUdpFailure = 0;
rxd->RxDescDataIpv6Rss.length = packet->Data.Capacity;
rxd->RxDescDataIpv6Rss.VLAN_TAG.Value = 0;
MemoryBarrier();
rxd->RxDescDataIpv6Rss.status = RXS_OWN | ((rb->NextIndex == 0) ? RXS_EOR : 0);
}
if (initialIndex != rb->NextIndex)
{
// jtippet: here's where to ring the doorbell to inform HW that more RXDs were posted.
// But I can't find any doorbell in the old code.
}
}
NTSTATUS
RtRxQueueInitialize(_In_ NETRXQUEUE rxQueue, _In_ RT_ADAPTER *adapter)
{
NTSTATUS status = STATUS_SUCCESS;
RT_RXQUEUE *rx = RtGetRxQueueContext(rxQueue);
rx->Adapter = adapter;
rx->Interrupt = adapter->Interrupt;
rx->RingBuffer = NetRxQueueGetRingBuffer(rxQueue);
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
NetRxQueueConfigureDmaAllocator(rxQueue, rx->Adapter->DmaEnabler));
// allocate descriptors
{
WdfDeviceSetAlignmentRequirement(adapter->WdfDevice, FILE_256_BYTE_ALIGNMENT);
SIZE_T rxdSize = rx->RingBuffer->NumberOfElements * sizeof(RT_RX_DESC);
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfCommonBufferCreate(
rx->Adapter->DmaEnabler,
rxdSize,
WDF_NO_OBJECT_ATTRIBUTES,
&rx->RxdArray));
rx->RxdBase = static_cast<RT_RX_DESC*>(WdfCommonBufferGetAlignedVirtualAddress(rx->RxdArray));
RtlZeroMemory(rx->RxdBase, rxdSize);
}
Exit:
return status;
}
ULONG
RtConvertPacketFilterToRcr(ULONG PacketFilter)
{
if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
{
return (RCR_AAP | RCR_APM | RCR_AM | RCR_AB | RCR_AR | RCR_AER);
}
return
((PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) ? RCR_AM : 0) |
((PacketFilter & NDIS_PACKET_TYPE_MULTICAST) ? RCR_AM : 0) |
((PacketFilter & NDIS_PACKET_TYPE_BROADCAST) ? RCR_AB : 0) |
((PacketFilter & NDIS_PACKET_TYPE_DIRECTED) ? RCR_APM : 0);
}
_Use_decl_annotations_
void
RtAdapterUpdateRcr(_In_ RT_ADAPTER *adapter)
{
adapter->CSRAddress->RCR =
(TCR_RCR_MXDMA_UNLIMITED << RCR_MXDMA_OFFSET) |
RtConvertPacketFilterToRcr(adapter->PacketFilter);
}
_Use_decl_annotations_
void
RtRxQueueStart(_In_ RT_RXQUEUE *rx)
{
RT_ADAPTER *adapter = rx->Adapter;
adapter->CSRAddress->RMS = RT_MAX_FRAME_SIZE;
USHORT cpcr = adapter->CSRAddress->CPCR | CPCR_RX_VLAN;
if (adapter->IpRxHwChkSumv4 || adapter->TcpRxHwChkSumv4 || adapter->UdpRxHwChkSumv4)
{
cpcr |= CPCR_RX_CHECKSUM;
}
else
{
cpcr &= ~CPCR_RX_CHECKSUM;
}
adapter->CSRAddress->CPCR = cpcr;
PHYSICAL_ADDRESS pa = WdfCommonBufferGetAlignedLogicalAddress(rx->RxdArray);
adapter->CSRAddress->RDSARLow = pa.LowPart;
adapter->CSRAddress->RDSARHigh = pa.HighPart;
RtAdapterUpdateRcr(adapter);
adapter->CSRAddress->CmdReg |= CR_RE;
}
void
RtRxQueueSetInterrupt(_In_ RT_RXQUEUE *rx, _In_ BOOLEAN notificationEnabled)
{
InterlockedExchange(&rx->Interrupt->RxNotifyArmed, notificationEnabled);
RtUpdateImr(rx->Interrupt);
if (!notificationEnabled)
// block this thread until we're sure any outstanding DPCs are complete.
// This is to guarantee we don't return from this function call until
// any oustanding rx notification is complete.
KeFlushQueuedDpcs();
}
_Use_decl_annotations_
void
EvtRxQueueDestroy(_In_ WDFOBJECT rxQueue)
{
TraceEntry(TraceLoggingPointer(rxQueue, "RxQueue"));
RT_RXQUEUE *rx = RtGetRxQueueContext(rxQueue);
WdfSpinLockAcquire(rx->Adapter->Lock); {
rx->Adapter->CSRAddress->CmdReg &= ~CR_RE;
RtRxQueueSetInterrupt(rx, false);
rx->Adapter->CSRAddress->CPCR &= ~(CPCR_RX_VLAN | CPCR_RX_CHECKSUM);
rx->Adapter->RxQueue = WDF_NO_HANDLE;
} WdfSpinLockRelease(rx->Adapter->Lock);
WdfObjectDelete(rx->RxdArray);
rx->RxdArray = NULL;
TraceExit();
}
_Use_decl_annotations_
NTSTATUS
EvtRxQueueSetNotificationEnabled(
_In_ NETRXQUEUE rxQueue,
_In_ BOOLEAN notificationEnabled)
{
TraceEntry(TraceLoggingPointer(rxQueue), TraceLoggingBoolean(notificationEnabled));
RT_RXQUEUE *rx = RtGetRxQueueContext(rxQueue);
RtRxQueueSetInterrupt(rx, notificationEnabled);
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
void
EvtRxQueueAdvance(
_In_ NETRXQUEUE rxQueue)
{
TraceEntry(TraceLoggingPointer(rxQueue, "RxQueue"));
RT_RXQUEUE *rx = RtGetRxQueueContext(rxQueue);
RxIndicateReceives(rx);
RxPostBuffers(rx);
TraceExit();
}
_Use_decl_annotations_
void
EvtRxQueueCancel(
_In_ NETRXQUEUE rxQueue)
{
TraceEntry(TraceLoggingPointer(rxQueue, "RxQueue"));
NET_RING_BUFFER *ringBuffer = NetRxQueueGetRingBuffer(rxQueue);
ringBuffer->BeginIndex = ringBuffer->NextIndex = ringBuffer->EndIndex;
TraceExit();
}

39
RtEthSample/rxqueue.h Normal file
Просмотреть файл

@ -0,0 +1,39 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
struct RT_RXQUEUE
{
RT_ADAPTER *Adapter;
RT_INTERRUPT *Interrupt;
NET_RING_BUFFER *RingBuffer;
WDFCOMMONBUFFER RxdArray;
RT_RX_DESC *RxdBase;
};
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(RT_RXQUEUE, RtGetRxQueueContext);
NTSTATUS RtRxQueueInitialize(_In_ NETRXQUEUE rxQueue, _In_ RT_ADAPTER * adapter);
_Requires_lock_held_(adapter->Lock)
void RtAdapterUpdateRcr(_In_ RT_ADAPTER *adapter);
_Requires_lock_held_(rx->Adapter->Lock)
void RtRxQueueStart(_In_ RT_RXQUEUE *rx);
EVT_WDF_OBJECT_CONTEXT_DESTROY EvtRxQueueDestroy;
EVT_RXQUEUE_SET_NOTIFICATION_ENABLED EvtRxQueueSetNotificationEnabled;
EVT_RXQUEUE_ADVANCE EvtRxQueueAdvance;
EVT_RXQUEUE_CANCEL EvtRxQueueCancel;

289
RtEthSample/statistics.cpp Normal file
Просмотреть файл

@ -0,0 +1,289 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "statistics.h"
#include "trace.h"
#include "adapter.h"
bool
RtTryUpdateStats(_In_ RT_ADAPTER *adapter)
{
adapter->CSRAddress->DTCCRHigh = adapter->TallyPhy.HighPart;
adapter->CSRAddress->DTCCRLow = adapter->TallyPhy.LowPart | DTCCR_Cmd;
for (UINT i = 1; i <= 20; i++)
{
if (DTCCR_Cmd != (adapter->CSRAddress->DTCCRLow & DTCCR_Cmd))
return true;
KeStallExecutionProcessor(i);
}
return false;
}
void
RtAdapterDumpStatsCounters(
_In_ RT_ADAPTER *adapter)
{
if (RtTryUpdateStats(adapter))
{
adapter->HwTotalRxMatchPhy = adapter->GTally->RxOKPhy;
adapter->HwTotalRxBroadcast = adapter->GTally->RxOKBrd;
adapter->HwTotalRxMulticast = adapter->GTally->RxOKMul;
adapter->TotalTxErr = adapter->GTally->TxERR;
adapter->TotalRxErr = adapter->GTally->RxERR;
adapter->RcvResourceErrors = adapter->GTally->MissPkt;
adapter->TxOneRetry = adapter->GTally->Tx1Col;
adapter->TxMoreThanOneRetry = adapter->GTally->TxMCol;
adapter->TxAbortExcessCollisions = adapter->GTally->TxAbt;
adapter->TxDmaUnderrun = adapter->GTally->TxUndrn;
}
else
{
TraceLoggingWrite(RealtekTraceProvider, "StatsUpdateFailed");
}
}
NTSTATUS
RtAdapterInitializeStatistics(
_In_ RT_ADAPTER *adapter)
/*++
Routine Description:
Allocate all the memory blocks for send, receive and others
Arguments:
adapter Pointer to our adapter
Return Value:
STATUS_SUCCESS
STATUS_FAILURE
STATUS_INSUFFICIENT_RESOURCES
--*/
{
TraceEntryRtAdapter(adapter);
NTSTATUS status = STATUS_SUCCESS;
// Allocate memory for Tally counter
WDFCOMMONBUFFER HwTallyMemAlloc = WDF_NO_HANDLE;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfCommonBufferCreate(
adapter->DmaEnabler,
sizeof(RT_TALLY),
WDF_NO_OBJECT_ATTRIBUTES,
&HwTallyMemAlloc));
adapter->GTally = static_cast<RT_TALLY*>(WdfCommonBufferGetAlignedVirtualAddress(HwTallyMemAlloc));
adapter->TallyPhy = WdfCommonBufferGetAlignedLogicalAddress(HwTallyMemAlloc);
RtlZeroMemory(adapter->GTally, sizeof(*adapter->GTally));
adapter->CSRAddress->DTCCRHigh = adapter->TallyPhy.HighPart;
adapter->CSRAddress->DTCCRLow = adapter->TallyPhy.LowPart;
// Clear the internal counters
RtAdapterResetStatistics(adapter);
Exit:
TraceExitResult(status);
return status;
}
void
RtAdapterResetStatistics(
_In_ RT_ADAPTER *adapter)
/*++
Routine Description:
This routine will clear the hardware error statistic counters
Arguments:
NetAdapter Pointer to our adapter
--*/
{
adapter->CSRAddress->DTCCRLow = adapter->TallyPhy.LowPart | DTCCR_Clr;
}
_Use_decl_annotations_
void
EvtNetRequestQueryAllStatistics(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
__analysis_assume(OutputBufferLength >= sizeof(NDIS_STATISTICS_INFO));
UNREFERENCED_PARAMETER((OutputBufferLength));
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter);
RtAdapterDumpStatsCounters(adapter);
NDIS_STATISTICS_INFO *statisticsInfo = (NDIS_STATISTICS_INFO *)OutputBuffer;
statisticsInfo->Header.Revision = NDIS_OBJECT_REVISION_1;
statisticsInfo->Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
statisticsInfo->Header.Size = sizeof(NDIS_STATISTICS_INFO);
statisticsInfo->SupportedStatistics
=
NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR |
NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS |
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV |
NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV |
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV |
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV |
NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV |
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV |
NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV |
NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR |
NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS |
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT |
NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT |
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT |
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT |
NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT |
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT |
NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT;
// Rx statistics
statisticsInfo->ifInErrors = adapter->TotalRxErr;
statisticsInfo->ifInDiscards = adapter->TotalRxErr + adapter->RcvResourceErrors;
statisticsInfo->ifHCInUcastPkts = adapter->HwTotalRxMatchPhy;
statisticsInfo->ifHCInMulticastPkts = adapter->HwTotalRxMulticast;
statisticsInfo->ifHCInBroadcastPkts = adapter->HwTotalRxBroadcast;
statisticsInfo->ifHCInUcastOctets = adapter->InUcastOctets;
statisticsInfo->ifHCInMulticastOctets = adapter->InMulticastOctets;
statisticsInfo->ifHCInBroadcastOctets = adapter->InBroadcastOctets;
statisticsInfo->ifHCInOctets = adapter->InBroadcastOctets + adapter->InMulticastOctets + adapter->InUcastOctets;
// Tx statistics
statisticsInfo->ifOutErrors = adapter->TotalTxErr;
statisticsInfo->ifOutDiscards = adapter->TxAbortExcessCollisions;
statisticsInfo->ifHCOutUcastPkts = adapter->OutUCastPkts;
statisticsInfo->ifHCOutMulticastPkts = adapter->OutMulticastPkts;
statisticsInfo->ifHCOutBroadcastPkts = adapter->OutBroadcastPkts;
statisticsInfo->ifHCOutUcastOctets = adapter->OutUCastOctets;
statisticsInfo->ifHCOutMulticastOctets = adapter->OutMulticastOctets;
statisticsInfo->ifHCOutBroadcastOctets = adapter->OutBroadcastOctets;
statisticsInfo->ifHCOutOctets = adapter->OutMulticastOctets + adapter->OutBroadcastOctets + adapter->OutUCastOctets;
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(NDIS_STATISTICS_INFO));
TraceExit();
}
_Use_decl_annotations_
void
EvtNetRequestQueryIndividualStatistics(
_In_ NETREQUESTQUEUE RequestQueue,
_In_ NETREQUEST Request,
_Out_writes_bytes_(OutputBufferLength)
PVOID OutputBuffer,
UINT OutputBufferLength)
{
__analysis_assume(OutputBufferLength >= sizeof(ULONG));
UNREFERENCED_PARAMETER((OutputBufferLength));
NDIS_OID oid = NetRequestGetId(Request);
NETADAPTER netAdapter = NetRequestQueueGetAdapter(RequestQueue);
RT_ADAPTER *adapter = RtGetAdapterContext(netAdapter);
TraceEntryRtAdapter(adapter, TraceLoggingUInt32(oid, "Oid"));
switch (oid)
{
case OID_GEN_XMIT_OK:
// Purely software counter
NOTHING;
break;
default:
// Dependent on counters from hardware
RtAdapterDumpStatsCounters(adapter);
break;
}
ULONG64 result = 0;
switch (oid)
{
case OID_GEN_XMIT_OK:
result = adapter->OutUCastPkts + adapter->OutMulticastPkts + adapter->OutBroadcastPkts;
break;
case OID_GEN_RCV_OK:
result = adapter->HwTotalRxMatchPhy + adapter->HwTotalRxMulticast + adapter->HwTotalRxBroadcast;
break;
case OID_802_3_XMIT_ONE_COLLISION:
result = adapter->TxOneRetry;
break;
case OID_802_3_XMIT_MORE_COLLISIONS:
result = adapter->TxMoreThanOneRetry;
break;
case OID_802_3_XMIT_MAX_COLLISIONS:
result = adapter->TxAbortExcessCollisions;
break;
case OID_802_3_XMIT_UNDERRUN:
result = adapter->TxDmaUnderrun;
break;
default:
NT_ASSERTMSG("Unexpected OID", false);
break;
}
// The NETREQUESTQUEUE ensures that OutputBufferLength is at least sizeof(ULONG)
NT_ASSERT(OutputBufferLength >= sizeof(ULONG));
if (OutputBufferLength < sizeof(ULONG64))
{
*(PULONG UNALIGNED)OutputBuffer = (ULONG)result;
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(ULONG));
}
else
{
*(PULONG64 UNALIGNED)OutputBuffer = result;
NetRequestQueryDataComplete(Request, STATUS_SUCCESS, sizeof(ULONG64));
}
TraceExit();
}

26
RtEthSample/statistics.h Normal file
Просмотреть файл

@ -0,0 +1,26 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
void RtAdapterDumpStatsCounters(
_In_ RT_ADAPTER *adapter);
NTSTATUS
RtAdapterInitializeStatistics(
_In_ RT_ADAPTER *adapter);
void
RtAdapterResetStatistics(
_In_ RT_ADAPTER *adapter);
EVT_NET_REQUEST_QUERY_DATA EvtNetRequestQueryAllStatistics;
EVT_NET_REQUEST_QUERY_DATA EvtNetRequestQueryIndividualStatistics;

104
RtEthSample/trace.h Normal file
Просмотреть файл

@ -0,0 +1,104 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
#include <evntrace.h>
#include <TraceLoggingProvider.h>
#include <evntrace.h>
TRACELOGGING_DECLARE_PROVIDER(RealtekTraceProvider);
#define TraceLoggingNetAdapter(adapter) \
TraceLoggingPointer((adapter), "NetAdapter")
#define TraceLoggingRtAdapter(adapter) \
TraceLoggingNetAdapter((adapter)->NetAdapter)
#define TraceLoggingFunctionName() TraceLoggingWideString(__FUNCTIONW__, "Function")
#define TraceEntry(...) \
TraceLoggingWrite( \
RealtekTraceProvider, \
"FunctionEntry", \
TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \
TraceLoggingFunctionName(), \
__VA_ARGS__)
#define TraceEntryRtAdapter(adapter, ...) \
TraceLoggingWrite( \
RealtekTraceProvider, \
"FunctionEntry", \
TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \
TraceLoggingFunctionName(), \
TraceLoggingRtAdapter(adapter), \
__VA_ARGS__)
#define TraceEntryNetAdapter(netAdapter, ...) \
TraceLoggingWrite( \
RealtekTraceProvider, \
"FunctionEntry", \
TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \
TraceLoggingFunctionName(), \
TraceLoggingNetAdapter(netAdapter), \
__VA_ARGS__)
#define TraceExit(...) \
TraceLoggingWrite( \
RealtekTraceProvider, \
"FunctionExit", \
TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \
TraceLoggingFunctionName(), \
__VA_ARGS__)
#define TraceExitResult(Status, ...) \
TraceLoggingWrite( \
RealtekTraceProvider, \
"FunctionExitResult", \
TraceLoggingLevel(TRACE_LEVEL_VERBOSE), \
TraceLoggingFunctionName(), \
TraceLoggingNTStatus((Status), "Status"), \
__VA_ARGS__)
#define LOG_NTSTATUS(Status, ...) do {\
TraceLoggingWrite( \
RealtekTraceProvider, \
"StatusFailure", \
TraceLoggingLevel(TRACE_LEVEL_ERROR), \
TraceLoggingFunctionName(), \
TraceLoggingUInt32(__LINE__, "Line"), \
TraceLoggingNTStatus(Status, "Status"), \
__VA_ARGS__); \
} while (0,0)
#define LOG_IF_NOT_NT_SUCCESS(Expression, ...) do {\
NTSTATUS p_status = (Expression); \
if (!NT_SUCCESS(p_status)) \
{ \
LOG_NTSTATUS(p_status, \
TraceLoggingWideString(L#Expression, "Expression"), \
__VA_ARGS__); \
} \
} while(0,0)
#define GOTO_IF_NOT_NT_SUCCESS(Label, StatusLValue, Expression, ...) do {\
StatusLValue = (Expression); \
if (!NT_SUCCESS(StatusLValue)) \
{ \
LOG_NTSTATUS(StatusLValue, \
TraceLoggingWideString(L#Expression, "Expression"), \
__VA_ARGS__); \
goto Label; \
} \
} while(0,0)
#define GOTO_WITH_INSUFFICIENT_RESOURCES_IF_NULL(Label, StatusLValue, Object) \
GOTO_IF_NOT_NT_SUCCESS(Label, StatusLValue, (((Object) == NULL) ? STATUS_INSUFFICIENT_RESOURCES : STATUS_SUCCESS))

336
RtEthSample/txqueue.cpp Normal file
Просмотреть файл

@ -0,0 +1,336 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#include "precomp.h"
#include "device.h"
#include "txqueue.h"
#include "trace.h"
#include "adapter.h"
#include "interrupt.h"
void
RtUpdateSendStats(
_In_ RT_TXQUEUE *tx,
_In_ NET_PACKET *packet)
{
if (packet->Layout.Layer2Type != NET_PACKET_LAYER2_TYPE_ETHERNET)
{
return;
}
// Ethernet header should be in first fragment
if (packet->Data.ValidLength < sizeof(ETHERNET_HEADER))
{
return;
}
PUCHAR ethHeader = (PUCHAR)packet->Data.VirtualAddress + packet->Data.Offset;
ULONG length = 0;
for (NET_PACKET_FRAGMENT *fragment = &packet->Data;
fragment;
fragment = NET_PACKET_FRAGMENT_GET_NEXT(fragment))
{
length += (ULONG)fragment->ValidLength;
if (fragment->LastFragmentOfFrame)
break;
}
RT_ADAPTER *adapter = tx->Adapter;
if (ETH_IS_BROADCAST(ethHeader))
{
adapter->OutBroadcastPkts++;
adapter->OutBroadcastOctets += length;
}
else if (ETH_IS_MULTICAST(ethHeader))
{
adapter->OutMulticastPkts++;
adapter->OutMulticastOctets += length;
}
else
{
adapter->OutUCastPkts++;
adapter->OutUCastOctets += length;
}
}
TX_DMA_BOUNCE_ANALYSIS
EvtSgBounceAnalysis(
_In_ NETTXQUEUE txQueue,
_In_ NET_PACKET *packet)
{
UNREFERENCED_PARAMETER((txQueue, packet));
return TxDmaTransmitInPlace;
}
USHORT
RtGetPacketChecksumSetting(NET_PACKET *packet)
{
if (packet->Layout.Layer3Type == NET_PACKET_LAYER3_TYPE_IPV4_NO_OPTIONS ||
packet->Layout.Layer3Type == NET_PACKET_LAYER3_TYPE_IPV4_WITH_OPTIONS ||
packet->Layout.Layer3Type == NET_PACKET_LAYER3_TYPE_IPV4_UNSPECIFIED_OPTIONS)
{
// Prioritize layer4 checksum first
if (packet->Checksum.Layer4 == NET_PACKET_TX_CHECKSUM_REQUIRED)
{
if (packet->Layout.Layer4Type == NET_PACKET_LAYER4_TYPE_TCP)
{
return TXS_IPV6RSS_TCPCS | TXS_IPV6RSS_IPV4CS;
}
if (packet->Layout.Layer4Type == NET_PACKET_LAYER4_TYPE_UDP)
{
return TXS_IPV6RSS_UDPCS | TXS_IPV6RSS_IPV4CS;
}
}
// If no layer4 checksum is required, then just do layer 3 checksum
if (packet->Checksum.Layer3 == NET_PACKET_TX_CHECKSUM_REQUIRED)
{
return TXS_IPV6RSS_IPV4CS;
}
return 0;
}
if (packet->Layout.Layer3Type == NET_PACKET_LAYER3_TYPE_IPV6_NO_EXTENSIONS ||
packet->Layout.Layer3Type == NET_PACKET_LAYER3_TYPE_IPV6_WITH_EXTENSIONS ||
packet->Layout.Layer3Type == NET_PACKET_LAYER3_TYPE_IPV6_UNSPECIFIED_EXTENSIONS)
{
if (packet->Checksum.Layer4 == NET_PACKET_TX_CHECKSUM_REQUIRED)
{
const USHORT layer4HeaderOffset =
packet->Layout.Layer2HeaderLength +
packet->Layout.Layer3HeaderLength;
if (packet->Layout.Layer4Type == NET_PACKET_LAYER4_TYPE_TCP)
{
return TXS_IPV6RSS_TCPCS | TXS_IPV6RSS_IS_IPV6 |
(layer4HeaderOffset << TXS_IPV6RSS_TCPHDR_OFFSET);
}
if (packet->Layout.Layer4Type == NET_PACKET_LAYER4_TYPE_UDP)
{
return TXS_IPV6RSS_UDPCS | TXS_IPV6RSS_IS_IPV6 |
(layer4HeaderOffset << TXS_IPV6RSS_TCPHDR_OFFSET);
}
}
// No IPv6 layer3 checksum
return 0;
}
return 0;
}
void
EvtSgProgramDescriptors(
_In_ NETTXQUEUE txQueue,
_In_ NET_PACKET *packet,
_In_ SCATTER_GATHER_LIST *sgl)
{
RT_TXQUEUE *tx = RtGetTxQueueContext(txQueue);
RtUpdateSendStats(tx, packet);
for (ULONG sgeIndex = 0; sgeIndex < sgl->NumberOfElements; sgeIndex++)
{
SCATTER_GATHER_ELEMENT *sge = &sgl->Elements[sgeIndex];
RT_TX_DESC *txd = &tx->TxdBase[tx->TxDescGetptr];
USHORT status = TXS_OWN;
// Last TXD; next should wrap
if (tx->TxDescGetptr == tx->NumTxDesc - 1)
{
status |= TXS_EOR;
}
// First fragment of packet
if (sgeIndex == 0)
{
status |= TXS_FS;
}
// Last fragment of packet
if (sgeIndex + 1 == sgl->NumberOfElements)
{
status |= TXS_LS;
// Store the hardware descriptor of the last
// scatter/gather element
RT_TCB *tcb = GetTcbFromPacket(packet);
tcb->LastTxDesc = &tx->TxdBase[tx->TxDescGetptr];
}
// TODO: vlan
txd->BufferAddress = sge->Address;
txd->TxDescDataIpv6Rss_All.length = (USHORT)sge->Length;
txd->TxDescDataIpv6Rss_All.VLAN_TAG.Value = 0;
txd->TxDescDataIpv6Rss_All.OffloadGsoMssTagc = RtGetPacketChecksumSetting(packet);
MemoryBarrier();
txd->TxDescDataIpv6Rss_All.status = status;
tx->TxDescGetptr = (tx->TxDescGetptr + 1) % tx->NumTxDesc;
}
}
void
EvtSgFlushTransation(_In_ NETTXQUEUE txQueue)
{
auto tx = RtGetTxQueueContext(txQueue);
MemoryBarrier();
*tx->TPPoll = TPPoll_NPQ;
}
NTSTATUS
EvtSgGetPacketStatus(
_In_ NETTXQUEUE txQueue,
_In_ NET_PACKET *packet)
{
UNREFERENCED_PARAMETER(txQueue);
RT_TX_DESC *txd = GetTcbFromPacket(packet)->LastTxDesc;
// Look at the status flags on the last fragment in the packet.
// If the hardware-ownership flag is still set, then the packet isn't done.
if (0 != (txd->TxDescDataIpv6Rss_All.status & TXS_OWN))
return STATUS_PENDING;
return STATUS_SUCCESS;
}
NTSTATUS
RtTxQueueInitialize(_In_ NETTXQUEUE txQueue, _In_ RT_ADAPTER * adapter)
{
NTSTATUS status = STATUS_SUCCESS;
RT_TXQUEUE *tx = RtGetTxQueueContext(txQueue);
tx->Adapter = adapter;
tx->TPPoll = &adapter->CSRAddress->TPPoll;
tx->Interrupt = adapter->Interrupt;
tx->NumTxDesc = (USHORT)(adapter->NumTcb * RT_MAX_PHYS_BUF_COUNT);
// Allocate descriptors
{
ULONG allocSize;
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
RtlULongMult(tx->NumTxDesc, sizeof(RT_TX_DESC), &allocSize));
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
WdfCommonBufferCreate(
tx->Adapter->DmaEnabler,
allocSize,
WDF_NO_OBJECT_ATTRIBUTES,
&tx->TxdArray));
tx->TxdBase = static_cast<RT_TX_DESC*>(
WdfCommonBufferGetAlignedVirtualAddress(tx->TxdArray));
tx->TxDescGetptr = 0;
RtlZeroMemory(tx->TxdBase, allocSize);
}
Exit:
return status;
}
_Use_decl_annotations_
void RtTxQueueStart(_In_ RT_TXQUEUE *tx)
{
RT_ADAPTER *adapter = tx->Adapter;
adapter->CSRAddress->TDFNR = 8;
// Max transmit packet size
adapter->CSRAddress->MtpsReg.MTPS = (RT_MAX_FRAME_SIZE + 128 - 1) / 128;
PHYSICAL_ADDRESS pa = WdfCommonBufferGetAlignedLogicalAddress(tx->TxdArray);
// let hardware know where transmit descriptors are at
adapter->CSRAddress->TNPDSLow = pa.LowPart;
adapter->CSRAddress->TNPDSHigh = pa.HighPart;
adapter->CSRAddress->CmdReg |= CR_TE;
// data sheet says TCR should only be modified after the transceiver is enabled
adapter->CSRAddress->TCR = (TCR_RCR_MXDMA_UNLIMITED << TCR_MXDMA_OFFSET) | (TCR_IFG0 | TCR_IFG1);
}
void
RtTxQueueSetInterrupt(_In_ RT_TXQUEUE *tx, _In_ BOOLEAN notificationEnabled)
{
InterlockedExchange(&tx->Interrupt->TxNotifyArmed, notificationEnabled);
RtUpdateImr(tx->Interrupt);
if (!notificationEnabled)
// block this thread until we're sure any outstanding DPCs are complete.
// This is to guarantee we don't return from this function call until
// any oustanding tx notification is complete.
KeFlushQueuedDpcs();
}
_Use_decl_annotations_
void
EvtTxQueueDestroy(_In_ WDFOBJECT txQueue)
{
RT_TXQUEUE *tx = RtGetTxQueueContext(txQueue);
WdfSpinLockAcquire(tx->Adapter->Lock); {
tx->Adapter->CSRAddress->CmdReg &= ~CR_TE;
RtTxQueueSetInterrupt(tx, false);
tx->Adapter->TxQueue = WDF_NO_HANDLE;
} WdfSpinLockRelease(tx->Adapter->Lock);
WdfObjectDelete(tx->TxdArray);
tx->TxdArray = NULL;
}
_Use_decl_annotations_
NTSTATUS
EvtTxQueueSetNotificationEnabled(
_In_ NETTXQUEUE txQueue,
_In_ BOOLEAN notificationEnabled)
{
TraceEntry(TraceLoggingPointer(txQueue), TraceLoggingBoolean(notificationEnabled));
RT_TXQUEUE *tx = RtGetTxQueueContext(txQueue);
RtTxQueueSetInterrupt(tx, notificationEnabled);
TraceExit();
return STATUS_SUCCESS;
}
_Use_decl_annotations_
void
EvtTxQueueCancel(
_In_ NETTXQUEUE txQueue)
{
TraceEntry(TraceLoggingPointer(txQueue, "TxQueue"));
//
// If the chipset is able to cancel outstanding IOs, then it should do so
// here. However, the RTL8168D does not seem to support such a feature, so
// the queue will continue to be drained like normal.
//
TraceExit();
}

67
RtEthSample/txqueue.h Normal file
Просмотреть файл

@ -0,0 +1,67 @@
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All rights reserved
--*/
#pragma once
#define TX_DMA_FX_ALLOC_TAG 'xTtR'
#include "txdmafxtypes.h"
EVT_TX_DMA_QUEUE_PROGRAM_DESCRIPTORS EvtSgProgramDescriptors;
EVT_TX_DMA_QUEUE_FLUSH_TRANSACTION EvtSgFlushTransation;
EVT_TX_DMA_QUEUE_GET_PACKET_STATUS EvtSgGetPacketStatus;
EVT_TX_DMA_QUEUE_BOUNCE_ANALYSIS EvtSgBounceAnalysis;
#define TX_DMA_FX_PROGRAM_DESCRIPTORS EvtSgProgramDescriptors
#define TX_DMA_FX_GET_PACKET_STATUS EvtSgGetPacketStatus
#define TX_DMA_FX_FLUSH_TRANSACTION EvtSgFlushTransation
#define TX_DMA_FX_BOUNCE_ANALYSIS EvtSgBounceAnalysis
#include "txdmafx.h"
typedef struct _RT_TXQUEUE
{
RT_ADAPTER *Adapter;
RT_INTERRUPT *Interrupt;
// descriptor information
WDFCOMMONBUFFER TxdArray;
RT_TX_DESC *TxdBase;
USHORT NumTxDesc;
USHORT TxDescGetptr;
UCHAR volatile *TPPoll;
} RT_TXQUEUE;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(RT_TXQUEUE, RtGetTxQueueContext);
//--------------------------------------
// TCB (Transmit Control Block)
//--------------------------------------
typedef struct _RT_TCB
{
RT_TX_DESC *LastTxDesc;
} RT_TCB;
NET_PACKET_DECLARE_CONTEXT_TYPE_WITH_NAME(RT_TCB, GetTcbFromPacket);
NTSTATUS RtTxQueueInitialize(_In_ NETTXQUEUE txQueue, _In_ RT_ADAPTER *adapter);
_Requires_lock_held_(tx->adapter->Lock)
void RtTxQueueStart(_In_ RT_TXQUEUE *tx);
EVT_WDF_OBJECT_CONTEXT_DESTROY EvtTxQueueDestroy;
EVT_TXQUEUE_SET_NOTIFICATION_ENABLED EvtTxQueueSetNotificationEnabled;
EVT_TXQUEUE_CANCEL EvtTxQueueCancel;