Коммит
48b2576759
|
@ -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
|
14
README.md
14
README.md
|
@ -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).
|
||||
|
|
|
@ -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
|
Двоичный файл не отображается.
|
@ -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>
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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"
|
|
@ -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
|
|
@ -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"
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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))
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
Загрузка…
Ссылка в новой задаче