📄 nic_send.c
字号:
/*++
Routine Description:
NIC specific send handler
Assumption: Send spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
pMpTcb Pointer to MP_TCB
pFragList The pointer to the frag list to be filled
Return Value:
NTSTATUS code
--*/
{
NTSTATUS status;
ULONG index;
UCHAR TbdCount = 0;
PHW_TCB pHwTcb = pMpTcb->HwTcb;
PTBD_STRUC pHwTbd = pMpTcb->HwTbd;
DebugPrint(TRACE, DBG_WRITE, "--> NICSendPacket\n");
for (index = 0; index < pFragList->NumberOfElements; index++)
{
if (pFragList->Elements[index].Length)
{
pHwTbd->TbdBufferAddress = pFragList->Elements[index].Address.LowPart;
pHwTbd->TbdCount = pFragList->Elements[index].Length;
pHwTbd++;
TbdCount++;
}
}
pHwTcb->TxCbHeader.CbStatus = 0;
pHwTcb->TxCbHeader.CbCommand = CB_S_BIT | CB_TRANSMIT | CB_TX_SF_BIT;
pHwTcb->TxCbTbdPointer = pMpTcb->HwTbdPhys;
pHwTcb->TxCbTbdNumber = TbdCount;
pHwTcb->TxCbCount = 0;
pHwTcb->TxCbThreshold = (UCHAR) FdoData->AiThreshold;
status = NICStartSend(FdoData, pMpTcb);
if(!NT_SUCCESS(status)){
DebugPrint(ERROR, DBG_WRITE, "NICStartSend returned error %x\n", status);
}
DebugPrint(TRACE, DBG_WRITE, "<-- NICSendPacket\n");
return status;
}
NTSTATUS
NICStartSend(
IN PFDO_DATA FdoData,
IN PMP_TCB pMpTcb
)
/*++
Routine Description:
Issue a send command to the NIC
Assumption: Send spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
pMpTcb Pointer to MP_TCB
Return Value:
NTSTATUS code
--*/
{
NTSTATUS status;
DebugPrint(TRACE, DBG_WRITE, "--> NICStartSend\n");
//
// If the transmit unit is idle (very first transmit) then we must
// setup the general pointer and issue a full CU-start
//
if (FdoData->TransmitIdle)
{
DebugPrint(TRACE, DBG_WRITE, "CU is idle -- First TCB added to Active List\n");
//
// Wait for the SCB to clear before we set the general pointer
//
if (!WaitScb(FdoData))
{
DebugPrint(ERROR, DBG_WRITE, "NICStartSend -- WaitScb returned error\n");
status = STATUS_DEVICE_DATA_ERROR;
goto exit;
}
//
// Don't try to start the transmitter if the command unit is not
// idle ((not idle) == (Cu-Suspended or Cu-Active)).
//
if ((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_IDLE)
{
DebugPrint(ERROR, DBG_WRITE, "FdoData = %p, CU Not IDLE\n", FdoData);
MP_SET_HARDWARE_ERROR(FdoData);
KeStallExecutionProcessor(25);
}
FdoData->CSRAddress->ScbGeneralPointer = pMpTcb->HwTcbPhys;
status = D100IssueScbCommand(FdoData, SCB_CUC_START, FALSE);
FdoData->TransmitIdle = FALSE;
FdoData->ResumeWait = TRUE;
}
else
{
//
// If the command unit has already been started, then append this
// TCB onto the end of the transmit chain, and issue a CU-Resume.
//
DebugPrint(LOUD, DBG_WRITE, "adding TCB to Active chain\n");
//
// Clear the suspend bit on the previous packet.
//
pMpTcb->PrevHwTcb->TxCbHeader.CbCommand &= ~CB_S_BIT;
//
// Issue a CU-Resume command to the device. We only need to do a
// WaitScb if the last command was NOT a RESUME.
//
status = D100IssueScbCommand(FdoData, SCB_CUC_RESUME, FdoData->ResumeWait);
}
exit:
DebugPrint(TRACE, DBG_WRITE, "<-- NICStartSend\n");
return status;
}
NTSTATUS
NICHandleSendInterrupt(
IN PFDO_DATA FdoData
)
/*++
Routine Description:
Interrupt handler for sending processing. Re-claim the send resources,
complete sends and get more to send from the send wait queue.
Assumption: Send spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
Return Value:
NTSTATUS code
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PMP_TCB pMpTcb;
#if DBG
LONG i;
#endif
DebugPrint(TRACE, DBG_WRITE, "---> NICHandleSendInterrupt\n");
//
// Any packets being sent? Any packet waiting in the send queue?
//
if (FdoData->nBusySend == 0 &&
IsListEmpty(&FdoData->SendQueueHead))
{
ASSERT(FdoData->CurrSendHead == FdoData->CurrSendTail);
DebugPrint(TRACE, DBG_WRITE, "<--- NICHandleSendInterrupt\n");
return status;
}
//
// Check the first TCB on the send list
//
while (FdoData->nBusySend > 0)
{
#if DBG
pMpTcb = FdoData->CurrSendHead;
for (i = 0; i < FdoData->nBusySend; i++)
{
pMpTcb = pMpTcb->Next;
}
if (pMpTcb != FdoData->CurrSendTail)
{
DebugPrint(ERROR, DBG_WRITE, "nBusySend= %d\n", FdoData->nBusySend);
DebugPrint(ERROR, DBG_WRITE, "CurrSendhead= %p\n", FdoData->CurrSendHead);
DebugPrint(ERROR, DBG_WRITE, "CurrSendTail= %p\n", FdoData->CurrSendTail);
ASSERT(FALSE);
}
#endif
pMpTcb = FdoData->CurrSendHead;
//
// Is this TCB completed?
//
if (pMpTcb->HwTcb->TxCbHeader.CbStatus & CB_STATUS_COMPLETE)
{
//
// Check if this is a multicast hw workaround packet
//
if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST)
{
MP_FREE_SEND_PACKET(FdoData, pMpTcb, STATUS_SUCCESS);
} else {
ASSERTMSG("Not sure what to do", FALSE);
}
}
else
{
break;
}
}
//
// If we queued any transmits because we didn't have any TCBs earlier,
// dequeue and send those packets now, as long as we have free TCBs.
//
while (!IsListEmpty(&FdoData->SendQueueHead) &&
MP_TCB_RESOURCES_AVAIABLE(FdoData))
{
PIRP irp;
PLIST_ENTRY pEntry;
pEntry = RemoveHeadList(&FdoData->SendQueueHead);
ASSERT(pEntry);
FdoData->nWaitSend--;
irp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
DebugPrint(LOUD, DBG_WRITE, "NICHandleSendInterrupt - send a queued packet\n");
NICWritePacket(FdoData, irp, TRUE);
}
DebugPrint(TRACE, DBG_WRITE, "<--- NICHandleSendInterrupt\n");
return status;
}
VOID
NICCompleteSendRequest(
IN PFDO_DATA FdoData,
IN PIRP Irp,
IN NTSTATUS Status,
IN ULONG Information,
IN BOOLEAN AtDispatchLevel
)
/*++
Routine Description:
This routine complete the IRP and free all the resources associated
with the IRP. This function can be called at passive or dispatch
level.
Arguments:
FdoData - Pointer to the device context.
Irp - Pointer to the write request.
Status - Final completion status of the IRP
Information - Value to be set in the information field of the IRP
AtDispatchLevel - Raising IRQL is as expensive as checking the current
IRQL level. So, this parameter enables us to know the caller's
current IRQL level and avoid raising and lowering IRQL
unnecessarily.
Return Value:
VOID
--*/
{
PSCATTER_GATHER_LIST sgl = Irp->Tail.Overlay.DriverContext[3];
PVOID sglBuffer = Irp->Tail.Overlay.DriverContext[2];
KIRQL oldIrql;
DebugPrint(TRACE, DBG_WRITE, "NICCompleteSendRequest, Pkt= %p Sgl %p\n",
Irp,
sgl);
if(sgl){
//
// Since PutScatterGatherList must be called at DISPATCH_LEVEL,
// raise the IRQL if we are not at that level.
//
if(!AtDispatchLevel) {
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
}
ASSERT(FdoData->DmaAdapterObject);
FdoData->DmaAdapterObject->DmaOperations->PutScatterGatherList(
FdoData->DmaAdapterObject,
sgl,
TRUE);
if(!AtDispatchLevel) {
KeLowerIrql(oldIrql);
}
}
if(sglBuffer){
ExFreeToNPagedLookasideList(&FdoData->SGListLookasideList, sglBuffer);
}
Irp->Tail.Overlay.DriverContext[3] = NULL;
Irp->Tail.Overlay.DriverContext[2] = NULL;
Irp->IoStatus.Information = Information;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
PciDrvIoDecrement (FdoData);
}
VOID
NICFreeBusySendPackets(
IN PFDO_DATA FdoData
)
/*++
Routine Description:
Free and complete the stopped active sends
Assumption: Send spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
Return Value:
None
--*/
{
PMP_TCB pMpTcb;
NTSTATUS status = MP_GET_STATUS_FROM_FLAGS(FdoData);
DebugPrint(TRACE, DBG_WRITE, "--> NICFreeBusySendPackets\n");
//
// Any packets being sent? Check the first TCB on the send list
//
while (FdoData->nBusySend > 0)
{
pMpTcb = FdoData->CurrSendHead;
//
// Is this TCB completed?
//
if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST)
{
MP_FREE_SEND_PACKET(FdoData, pMpTcb, status);
}
else
{
break;
}
}
DebugPrint(TRACE, DBG_WRITE, "<-- NICFreeBusySendPackets\n");
}
VOID
NICFreeQueuedSendPackets(
IN PFDO_DATA FdoData
)
/*++
Routine Description:
Free and complete the pended sends on SendQueueHead
Assumption: spinlock has been acquired
Arguments:
FdoData Pointer to our FdoData
Return Value:
None
--*/
{
PLIST_ENTRY pEntry;
PIRP irp;
NTSTATUS status = MP_GET_STATUS_FROM_FLAGS(FdoData);
DebugPrint(TRACE, DBG_WRITE, "--> NICFreeQueuedSendPackets\n");
while (!IsListEmpty(&FdoData->SendQueueHead))
{
pEntry = RemoveHeadList(&FdoData->SendQueueHead);
FdoData->nWaitSend--;
KeReleaseSpinLockFromDpcLevel(&FdoData->SendLock);
ASSERT(pEntry);
irp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
NICCompleteSendRequest(FdoData, irp, status, 0, TRUE);
KeAcquireSpinLockAtDpcLevel(&FdoData->SendLock);
}
DebugPrint(TRACE, DBG_WRITE, "<-- NICFreeQueuedSendPackets\n");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -