windows-nt/Source/XPSP1/NT/net/ndis/uio/sys/send.c
2020-09-26 16:20:57 +08:00

492 lines
13 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
send.c
Abstract:
NDIS protocol entry points and utility routines to handle sending
data.
Environment:
Kernel mode only.
Revision History:
arvindm 4/10/2000 Created
--*/
#include "precomp.h"
#define __FILENUMBER 'DNES'
NTSTATUS
NdisuioWrite(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Dispatch routine to handle IRP_MJ_WRITE.
Arguments:
pDeviceObject - pointer to our device object
pIrp - Pointer to request packet
Return Value:
NT status code.
--*/
{
PIO_STACK_LOCATION pIrpSp;
ULONG FunctionCode;
ULONG DataLength;
NTSTATUS NtStatus;
NDIS_STATUS Status;
PNDISUIO_OPEN_CONTEXT pOpenContext;
PNDIS_PACKET pNdisPacket;
PNDIS_BUFFER pNdisBuffer;
NDISUIO_ETH_HEADER UNALIGNED *pEthHeader;
#ifdef NDIS51
PVOID CancelId;
#endif
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = pIrpSp->FileObject->FsContext;
pNdisPacket = NULL;
do
{
if (pOpenContext == NULL)
{
DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n",
pIrpSp->FileObject));
NtStatus = STATUS_INVALID_HANDLE;
break;
}
NUIO_STRUCT_ASSERT(pOpenContext, oc);
if (pIrp->MdlAddress == NULL)
{
DEBUGP(DL_FATAL, ("Write: NULL MDL address on IRP %p\n", pIrp));
NtStatus = STATUS_INVALID_PARAMETER;
break;
}
//
// Try to get a virtual address for the MDL.
//
#ifndef WIN9X
pEthHeader = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
if (pEthHeader == NULL)
{
DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for"
" IRP %p, MDL %p\n",
pIrp, pIrp->MdlAddress));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
#else
pEthHeader = MmGetSystemAddressForMdl(pIrp->MdlAddress); // for Win9X
#endif
//
// Sanity-check the length.
//
DataLength = MmGetMdlByteCount(pIrp->MdlAddress);
if (DataLength < sizeof(NDISUIO_ETH_HEADER))
{
DEBUGP(DL_WARN, ("Write: too small to be a valid packet (%d bytes)\n",
DataLength));
NtStatus = STATUS_BUFFER_TOO_SMALL;
break;
}
if (DataLength > (pOpenContext->MaxFrameSize + sizeof(NDISUIO_ETH_HEADER)))
{
DEBUGP(DL_WARN, ("Write: Open %p: data length (%d)"
" larger than max frame size (%d)\n",
pOpenContext, DataLength, pOpenContext->MaxFrameSize));
NtStatus = STATUS_INVALID_BUFFER_SIZE;
break;
}
if (pEthHeader->EthType != Globals.EthType)
{
DEBUGP(DL_WARN, ("Write: Failing send with EthType %x\n",
pEthHeader->EthType));
NtStatus = STATUS_INVALID_PARAMETER;
break;
}
if (!NUIO_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NUIO_MAC_ADDR_LEN))
{
DEBUGP(DL_WARN, ("Write: Failing with invalid Source address"));
NtStatus = STATUS_INVALID_PARAMETER;
break;
}
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
if (!NUIO_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE))
{
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
DEBUGP(DL_FATAL, ("Write: Open %p is not bound"
" or in low power state\n", pOpenContext));
NtStatus = STATUS_INVALID_HANDLE;
break;
}
//
// Allocate a send packet.
//
NUIO_ASSERT(pOpenContext->SendPacketPool != NULL);
NdisAllocatePacket(
&Status,
&pNdisPacket,
pOpenContext->SendPacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send pkt\n",
pOpenContext));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Allocate a send buffer if necessary.
//
if (pOpenContext->bRunningOnWin9x)
{
NdisAllocateBuffer(
&Status,
&pNdisBuffer,
pOpenContext->SendBufferPool,
pEthHeader,
DataLength);
if (Status != NDIS_STATUS_SUCCESS)
{
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NdisFreePacket(pNdisPacket);
DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send buf\n",
pOpenContext));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
}
else
{
pNdisBuffer = pIrp->MdlAddress;
}
NdisInterlockedIncrement(&pOpenContext->PendedSendCount);
NUIO_REF_OPEN(pOpenContext); // pended send
IoMarkIrpPending(pIrp);
//
// Initialize the packet ref count. This packet will be freed
// when this count goes to zero.
//
NUIO_SEND_PKT_RSVD(pNdisPacket)->RefCount = 1;
#ifdef NDIS51
//
// NDIS 5.1 supports cancelling sends. We set up a cancel ID on
// each send packet (which maps to a Write IRP), and save the
// packet pointer in the IRP. If the IRP gets cancelled, we use
// NdisCancelSendPackets() to cancel the packet.
//
CancelId = NUIO_GET_NEXT_CANCEL_ID();
NDIS_SET_PACKET_CANCEL_ID(pNdisPacket, CancelId);
pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext;
pIrp->Tail.Overlay.DriverContext[1] = (PVOID)pNdisPacket;
NUIO_INSERT_TAIL_LIST(&pOpenContext->PendedWrites, &pIrp->Tail.Overlay.ListEntry);
IoSetCancelRoutine(pIrp, NdisuioCancelWrite);
#endif // NDIS51
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
//
// Set a back pointer from the packet to the IRP.
//
NUIO_IRP_FROM_SEND_PKT(pNdisPacket) = pIrp;
NtStatus = STATUS_PENDING;
pNdisBuffer->Next = NULL;
NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);
#if SEND_DBG
{
PUCHAR pData;
#ifndef WIN9X
pData = MmGetSystemAddressForMdlSafe(pNdisBuffer, NormalPagePriority);
NUIO_ASSERT(pEthHeader == pData);
#else
pData = MmGetSystemAddressForMdl(pNdisBuffer); // Win9x
#endif
DEBUGP(DL_VERY_LOUD,
("Write: MDL %p, MdlFlags %x, SystemAddr %p, %d bytes\n",
pIrp->MdlAddress, pIrp->MdlAddress->MdlFlags, pData, DataLength));
DEBUGPDUMP(DL_VERY_LOUD, pData, MIN(DataLength, 48));
}
#endif // SEND_DBG
NdisSendPackets(pOpenContext->BindingHandle, &pNdisPacket, 1);
}
while (FALSE);
if (NtStatus != STATUS_PENDING)
{
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
return (NtStatus);
}
#ifdef NDIS51
VOID
NdisuioCancelWrite(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Cancel a pending write IRP. This routine attempt to cancel the NDIS send.
Arguments:
pDeviceObject - pointer to our device object
pIrp - IRP to be cancelled
Return Value:
None
--*/
{
PNDISUIO_OPEN_CONTEXT pOpenContext;
PLIST_ENTRY pIrpEntry;
PNDIS_PACKET pNdisPacket;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
//
// The NDIS packet representing this Write IRP.
//
pNdisPacket = NULL;
pOpenContext = (PNDISUIO_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0];
NUIO_STRUCT_ASSERT(pOpenContext, oc);
//
// Try to locate the IRP in the pended write queue. The send completion
// routine may be running and might have removed it from there.
//
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
for (pIrpEntry = pOpenContext->PendedWrites.Flink;
pIrpEntry != &pOpenContext->PendedWrites;
pIrpEntry = pIrpEntry->Flink)
{
if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry))
{
pNdisPacket = (PNDIS_PACKET) pIrp->Tail.Overlay.DriverContext[1];
//
// Place a reference on this packet so that it won't get
// freed/reused until we are done with it.
//
NUIO_REF_SEND_PKT(pNdisPacket);
break;
}
}
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
if (pNdisPacket != NULL)
{
//
// Either the send completion routine hasn't run, or we got a peak
// at the IRP/packet before it had a chance to take it out of the
// pending IRP queue.
//
// We do not complete the IRP here - note that we didn't dequeue it
// above. This is because we always want the send complete routine to
// complete the IRP. And this in turn is because the packet that was
// prepared from the IRP has a buffer chain pointing to data associated
// with this IRP. Therefore we cannot complete the IRP before the driver
// below us is done with the data it pointed to.
//
//
// Request NDIS to cancel this send. The result of this call is that
// our SendComplete handler will be called (if not already called).
//
DEBUGP(DL_INFO, ("CancelWrite: cancelling pkt %p on Open %p\n",
pNdisPacket, pOpenContext));
NdisCancelSendPackets(
pOpenContext->BindingHandle,
NDIS_GET_PACKET_CANCEL_ID(pNdisPacket)
);
//
// It is now safe to remove the reference we had placed on the packet.
//
NUIO_DEREF_SEND_PKT(pNdisPacket);
}
//
// else the send completion routine has already picked up this IRP.
//
}
#endif // NDIS51
VOID
NdisuioSendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pNdisPacket,
IN NDIS_STATUS Status
)
/*++
Routine Description:
NDIS entry point called to signify completion of a packet send.
We pick up and complete the Write IRP corresponding to this packet.
NDIS 5.1:
Arguments:
ProtocolBindingContext - pointer to open context
pNdisPacket - packet that completed send
Status - status of send
Return Value:
None
--*/
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
PNDISUIO_OPEN_CONTEXT pOpenContext;
pOpenContext = (PNDISUIO_OPEN_CONTEXT)ProtocolBindingContext;
NUIO_STRUCT_ASSERT(pOpenContext, oc);
pIrp = NUIO_IRP_FROM_SEND_PKT(pNdisPacket);
if (pOpenContext->bRunningOnWin9x)
{
//
// We would have attached our own NDIS_BUFFER. Take it out
// and free it.
//
PNDIS_BUFFER pNdisBuffer;
PVOID VirtualAddr;
UINT BufferLength;
UINT TotalLength;
#ifdef NDIS51
NUIO_ASSERT(FALSE); // NDIS 5.1 not on Win9X!
#else
NdisGetFirstBufferFromPacket(
pNdisPacket,
&pNdisBuffer,
&VirtualAddr,
&BufferLength,
&TotalLength);
NUIO_ASSERT(pNdisBuffer != NULL);
NdisFreeBuffer(pNdisBuffer);
#endif
}
#ifdef NDIS51
IoSetCancelRoutine(pIrp, NULL);
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
NUIO_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry);
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
#endif
//
// We are done with the NDIS_PACKET:
//
NUIO_DEREF_SEND_PKT(pNdisPacket);
//
// Complete the Write IRP with the right status.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
if (Status == NDIS_STATUS_SUCCESS)
{
pIrp->IoStatus.Information = pIrpSp->Parameters.Write.Length;
pIrp->IoStatus.Status = STATUS_SUCCESS;
}
else
{
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
}
DEBUGP(DL_INFO, ("SendComplete: packet %p/IRP %p/Length %d "
"completed with status %x\n",
pNdisPacket, pIrp, pIrp->IoStatus.Information, pIrp->IoStatus.Status));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
NdisInterlockedDecrement(&pOpenContext->PendedSendCount);
NUIO_DEREF_OPEN(pOpenContext); // send complete - dequeued send IRP
}