📄 nic_send.c
字号:
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
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.
Module Name:
nic_send.c
Abstract:
This module contains routines to write packets.
Environment:
Kernel mode
Revision History:
DChen 11-01-99 created
EliyasY Feb 13, 2003 converted to WDM
--*/
#include "precomp.h"
#if defined(EVENT_TRACING)
#include "nic_send.tmh"
#endif
__inline VOID
MP_FREE_SEND_PACKET(
IN PFDO_DATA FdoData,
IN PMP_TCB pMpTcb,
IN NTSTATUS Status
)
/*++
Routine Description:
Recycle a MP_TCB and complete the packet if necessary
Assumption: Send spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
pMpTcb Pointer to MP_TCB
Status Irp completion status
Return Value:
None
--*/
{
PIRP Irp;
ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE));
Irp = pMpTcb->Irp;
pMpTcb->Irp = NULL;
pMpTcb->Count = 0;
MP_CLEAR_FLAGS(pMpTcb);
FdoData->CurrSendHead = FdoData->CurrSendHead->Next;
FdoData->nBusySend--;
ASSERT(FdoData->nBusySend >= 0);
if (Irp)
{
KeReleaseSpinLockFromDpcLevel(&FdoData->SendLock);
NICCompleteSendRequest(FdoData,
Irp,
Status,
pMpTcb->PacketLength,
TRUE // Yes, we are calling at DISPATCH_LEVEL
);
KeAcquireSpinLockAtDpcLevel(&FdoData->SendLock);
}
}
NTSTATUS
NICWrite(
IN PFDO_DATA FdoData,
IN PIRP Irp
)
/*++
Routine Description:
This routine handles the hardware specific write request.
If the device is not ready, fail the request. Otherwise
get scatter-gather list for the request buffer and send the
list to the hardware for DMA.
Arguments:
FdoData - Pointer to the device context.
Irp - Pointer to the write request.
Return Value:
NT Status code.
--*/
{
NTSTATUS returnStatus, status;
PVOID virtualAddress;
ULONG pageCount = 0, length = 0;
PMDL tempMdl, mdl;
KIRQL oldIrql;
#if defined(DMA_VER2)
PVOID sgListBuffer;
#endif
DebugPrint(TRACE, DBG_WRITE, "--> PciDrvWrite %p\n", Irp);
Irp->Tail.Overlay.DriverContext[3] = NULL;
Irp->Tail.Overlay.DriverContext[2] = NULL;
returnStatus = status = STATUS_SUCCESS;
//
// Is this adapter ready for sending?
//
if (MP_SHOULD_FAIL_SEND(FdoData))
{
DebugPrint(ERROR, DBG_WRITE, "Device not ready %p\n", Irp);
returnStatus = status = STATUS_DEVICE_NOT_READY;
goto Error;
}
tempMdl = mdl = Irp->MdlAddress;
//
// Check for zero length buffer
//
if (mdl == NULL || MmGetMdlByteCount(mdl) == 0)
{
DebugPrint(ERROR, DBG_WRITE, "Zero length buffer %p\n", Irp);
status = returnStatus = STATUS_INVALID_DEVICE_REQUEST;
goto Error;
}
//
// Calculate the total packet length and the number of pages
// spanned by all the buffers by walking the MDL chain.
// NOTE: If this driver is used in the miniport configuration, it will
// not get chained MDLs because the upper filter (NDISEDGE.SYS)
// coalesces the fragements to a single contiguous buffer before presenting
// the packet to us.
//
while(tempMdl != NULL)
{
virtualAddress = MmGetMdlVirtualAddress(tempMdl);
length += MmGetMdlByteCount(tempMdl);
pageCount += ADDRESS_AND_SIZE_TO_SPAN_PAGES(virtualAddress, length);
tempMdl = tempMdl->Next;
}
if (length < NIC_MIN_PACKET_SIZE)
{
//
// This will never happen in our case because the ndis-edge
// pads smaller size packets with zero to make it NIC_MIN_PACKET_SIZE
// long.
//
DebugPrint(ERROR, DBG_WRITE, "Packet size is less than %d\n", NIC_MIN_PACKET_SIZE);
status = returnStatus = STATUS_INVALID_DEVICE_REQUEST;
goto Error;
}
//
// Check to see if the packet spans more than the physical pages
// our hardware can handle or the pageCount exceeds the total number of
// map registers allocated. If so, we should coalesce the scattered
// buffers to fit the limit. We can't really break the transfers and
// DMA in small chunks because each packets has to be DMA'ed in one shot.
// The code on how to colesce the packet for this hardware is present
// in the original E100BEX sample.
//
if (pageCount > NIC_MAX_PHYS_BUF_COUNT ||
pageCount > FdoData->AllocatedMapRegisters)
{
// TODO: Packet needs to be coalesced
DebugPrint(ERROR, DBG_WRITE, "Packet needs to be coalesced\n");
status = returnStatus = STATUS_INVALID_DEVICE_REQUEST;
goto Error;
}
//
// Build a scatter-gather list of the packet buffer and send the packet.
//
// If DMA_VER2 is not defined, use GetScatterGatherList. If the driver
// is meant to work on XP and above, define DMA_VER2, so that you can
// use BuildScatterGatherList.
//
// Since Build/GetScatterGatherList should be called at DISPATCH_LEVEL
// let us raise the IRQL.
//
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
//
// Let us mark the IRP pending, because NICProcessSGList is an asynchronous
// callback and we wouldn't know the status of the IRP. This IRP may either
// get completed by the DPC handler after the DMA transfer or may
// get queued if we are low on resources. So the safest thing
// to do for us here is return STATUS_PENDING irrespective of what happens
// to the IRP.
//
IoMarkIrpPending(Irp);
returnStatus = STATUS_PENDING;
#if defined(DMA_VER2)
sgListBuffer = ExAllocateFromNPagedLookasideList(
&FdoData->SGListLookasideList);
if (sgListBuffer)
{
Irp->Tail.Overlay.DriverContext[2] = sgListBuffer;
status = FdoData->DmaAdapterObject->DmaOperations->BuildScatterGatherList(
FdoData->DmaAdapterObject,
FdoData->Self,
mdl,
MmGetMdlVirtualAddress(mdl),
length,
NICProcessSGList,
Irp,
TRUE,
sgListBuffer,
FdoData->ScatterGatherListSize);
if (!NT_SUCCESS(status))
{
DebugPrint(ERROR, DBG_WRITE, "BuildScatterGatherList %x\n", status);
ExFreeToNPagedLookasideList(&FdoData->SGListLookasideList, sgListBuffer);
Irp->Tail.Overlay.DriverContext[2] = NULL;
}
}
#else
status = FdoData->DmaAdapterObject->DmaOperations->GetScatterGatherList(
FdoData->DmaAdapterObject,
FdoData->Self,
mdl,
MmGetMdlVirtualAddress(mdl),
length,
NICProcessSGList,
Irp,
TRUE);
if (!NT_SUCCESS(status))
{
DebugPrint(ERROR, DBG_WRITE, "GetScatterGatherList %x\n", status);
}
#endif
KeLowerIrql(oldIrql);
Error:
if(!NT_SUCCESS(status)){
//
// Our call to get the scatter-gather list failed. We know the
// NICProcessSGList is not called for sure in that case. So let us
// complete the IRP here with failure status. Since we marked the
// IRP pending, we have no choice but to return status-pending
// even though we are completing the IRP in the incoming thread
// context.
//
NICCompleteSendRequest(FdoData, Irp, status, 0, FALSE);
}
DebugPrint(LOUD, DBG_WRITE, "<-- PciDrvWrite %x\n", returnStatus);
return returnStatus;
}
VOID
NICProcessSGList(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,//unused
IN PSCATTER_GATHER_LIST ScatterGather,
IN PVOID Context
)
/*++
Routine Description:
This routine is called at IRQL = DISPATCH_LEVEL when the
bus-master adapter is available.
Arguments:
DeviceObject - This is the device object for the target device,
previously created by the driver's AddDevice routine.
Irp - Useful only if the driver has a StartIo routine.
ScatterGather - Structure describing scatter/gather regions.
Context - Pointer to the Request.
Return Value:
None.
--*/
{
PFDO_DATA fdoData;
PIRP irp = (PIRP)Context;
DebugPrint(TRACE, DBG_WRITE, "--> NICProcessSGList\n");
fdoData = DeviceObject->DeviceExtension;
//
// Save the ScatterGather pointer in the DriverContext so that we can free
// the list when we complete the request.
//
irp->Tail.Overlay.DriverContext[3] = ScatterGather;
KeAcquireSpinLockAtDpcLevel(&fdoData->SendLock);
//
// If tcb is not available or the device is doing link detection (during init),
// queue the request
//
if (!MP_TCB_RESOURCES_AVAIABLE(fdoData) ||
MP_TEST_FLAG(fdoData, fMP_ADAPTER_LINK_DETECTION))
{
//
// Instead of locking up the map registers while the request is
// waiting in the queue or, we could free it up and reallocate it whenever
// we are ready to handle the request later.
//
//
DebugPrint(TRACE, DBG_WRITE, "Resource or the link is not available, queue packet\n");
InsertTailList(&fdoData->SendQueueHead, &irp->Tail.Overlay.ListEntry);
fdoData->nWaitSend++;
} else {
NICWritePacket(fdoData, irp, FALSE);
}
KeReleaseSpinLockFromDpcLevel(&fdoData->SendLock);
DebugPrint(TRACE, DBG_WRITE, "<-- NICProcessSGList\n");
return;
}
VOID
NICWritePacket(
IN PFDO_DATA FdoData,
IN PIRP Irp,
IN BOOLEAN bFromQueue
)
/*++
Routine Description:
Do the work to send a packet
Assumption: Send spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
Packet The packet
bFromQueue TRUE if it's taken from the send wait queue
Return Value:
--*/
{
PMP_TCB pMpTcb = NULL;
ULONG packetLength;
PVOID virtualAddress;
DebugPrint(TRACE, DBG_WRITE, "--> NICWritePacket, Irp= %p\n", Irp);
//
// Get the next free TCB and initialize it to represent the
// request buffer.
//
pMpTcb = FdoData->CurrSendTail;
ASSERT(!MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE));
//
// If the adapter is not ready, fail the request.
//
if(MP_IS_NOT_READY(FdoData)) {
MP_FREE_SEND_PACKET(FdoData, pMpTcb, STATUS_DEVICE_NOT_READY);
return;
}
pMpTcb->FirstBuffer = Irp->MdlAddress;
virtualAddress = MmGetMdlVirtualAddress(Irp->MdlAddress);
pMpTcb->BufferCount = 1;
pMpTcb->PacketLength = packetLength = MmGetMdlByteCount(Irp->MdlAddress);
pMpTcb->PhysBufCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(virtualAddress,
packetLength);
pMpTcb->Irp = Irp;
MP_SET_FLAG(pMpTcb, fMP_TCB_IN_USE);
//
// Call the send handler, it only needs to deal with the frag list
//
NICSendPacket(FdoData, pMpTcb, Irp->Tail.Overlay.DriverContext[3]);
FdoData->nBusySend++;
ASSERT(FdoData->nBusySend <= FdoData->NumTcb);
FdoData->CurrSendTail = FdoData->CurrSendTail->Next;
DebugPrint(TRACE, DBG_WRITE, "<-- NICWritePacket\n");
return;
}
NTSTATUS
NICSendPacket(
IN PFDO_DATA FdoData,
IN PMP_TCB pMpTcb,
IN PSCATTER_GATHER_LIST pFragList
)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -