517 lines
13 KiB
C
517 lines
13 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
receive.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code which performs the following TDI services:
|
||
|
||
o TdiReceiveDatagram
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
|
||
VOID
|
||
IpxTransferDataComplete(
|
||
IN NDIS_HANDLE BindingContext,
|
||
IN PNDIS_PACKET NdisPacket,
|
||
IN NDIS_STATUS NdisStatus,
|
||
IN UINT BytesTransferred
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives control from the physical provider as an
|
||
indication that an NdisTransferData has completed. We use this indication
|
||
to complete any pended requests to our clients.
|
||
|
||
Arguments:
|
||
|
||
BindingContext - The Adapter Binding specified at initialization time.
|
||
|
||
NdisPacket/RequestHandle - An identifier for the request that completed.
|
||
|
||
NdisStatus - The completion status for the request.
|
||
|
||
BytesTransferred - Number of bytes actually transferred.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PADAPTER Adapter = (PADAPTER)BindingContext;
|
||
PIPX_RECEIVE_RESERVED Reserved = (PIPX_RECEIVE_RESERVED)(NdisPacket->ProtocolReserved);
|
||
PREQUEST Request, LastRequest;
|
||
PADDRESS_FILE AddressFile;
|
||
ULONG ByteOffset;
|
||
PLIST_ENTRY p;
|
||
PDEVICE Device;
|
||
|
||
|
||
switch (Reserved->Identifier) {
|
||
|
||
case IDENTIFIER_IPX:
|
||
|
||
if (!Reserved->pContext) {
|
||
|
||
if (Reserved->SingleRequest) {
|
||
|
||
//
|
||
// The transfer was directly into the client buffer,
|
||
// so simply complete the request.
|
||
//
|
||
|
||
Request = Reserved->SingleRequest;
|
||
|
||
if (NdisStatus == NDIS_STATUS_SUCCESS) {
|
||
|
||
IPX_DEBUG (RECEIVE, ("Transferred %d bytes\n", BytesTransferred));
|
||
REQUEST_INFORMATION(Request) = BytesTransferred;
|
||
REQUEST_STATUS(Request) = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
IPX_DEBUG (RECEIVE, ("Transfer failed\n"));
|
||
REQUEST_INFORMATION(Request) = 0;
|
||
REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR;
|
||
|
||
}
|
||
|
||
LastRequest = Request;
|
||
Reserved->SingleRequest = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Multiple clients requested this datagram. Save
|
||
// the last one to delay queueing it for completion.
|
||
//
|
||
|
||
LastRequest = LIST_ENTRY_TO_REQUEST (Reserved->Requests.Blink);
|
||
|
||
while (TRUE) {
|
||
|
||
p = RemoveHeadList (&Reserved->Requests);
|
||
if (p == &Reserved->Requests) {
|
||
break;
|
||
}
|
||
|
||
Request = LIST_ENTRY_TO_REQUEST(p);
|
||
AddressFile = REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
if (AddressFile->ReceiveIpxHeader) {
|
||
ByteOffset = 0;
|
||
} else {
|
||
ByteOffset = sizeof(IPX_HEADER);
|
||
}
|
||
|
||
if (NdisStatus == NDIS_STATUS_SUCCESS) {
|
||
UINT BytesToTransfer = ((PTDI_REQUEST_KERNEL_RECEIVEDG)(REQUEST_PARAMETERS(Request)))->ReceiveLength;
|
||
|
||
if (BytesToTransfer == 0) {
|
||
BytesToTransfer= IpxGetChainedMDLLength(REQUEST_NDIS_BUFFER(Request));
|
||
}
|
||
#ifdef SUNDOWN
|
||
// assume offset will not exceed 2^32.
|
||
// REQUEST_INFORMATION(Request) is a ULONG_PTR
|
||
// we are save to cast its address to PULONG.
|
||
|
||
REQUEST_STATUS(Request) =
|
||
TdiCopyBufferToMdl(
|
||
Reserved->ReceiveBuffer->Data,
|
||
(ULONG) (ByteOffset + REQUEST_INFORMATION(Request)),
|
||
BytesToTransfer,
|
||
REQUEST_NDIS_BUFFER(Request),
|
||
0,
|
||
(PULONG) &REQUEST_INFORMATION(Request));
|
||
|
||
#else
|
||
|
||
REQUEST_STATUS(Request) =
|
||
TdiCopyBufferToMdl(
|
||
Reserved->ReceiveBuffer->Data,
|
||
ByteOffset + REQUEST_INFORMATION(Request),
|
||
BytesToTransfer,
|
||
REQUEST_NDIS_BUFFER(Request),
|
||
0,
|
||
&REQUEST_INFORMATION(Request));
|
||
|
||
#endif
|
||
|
||
} else {
|
||
|
||
REQUEST_INFORMATION(Request) = 0;
|
||
REQUEST_STATUS(Request) = STATUS_ADAPTER_HARDWARE_ERROR;
|
||
|
||
}
|
||
|
||
if (Request != LastRequest) {
|
||
|
||
IPX_INSERT_TAIL_LIST(
|
||
&Adapter->RequestCompletionQueue,
|
||
REQUEST_LINKAGE(Request),
|
||
Adapter->DeviceLock);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now free the receive buffer back.
|
||
//
|
||
|
||
IPX_PUSH_ENTRY_LIST(
|
||
&Adapter->ReceiveBufferList,
|
||
&Reserved->ReceiveBuffer->PoolLinkage,
|
||
&Adapter->Device->SListsLock);
|
||
|
||
Reserved->ReceiveBuffer = NULL;
|
||
|
||
}
|
||
|
||
} else {
|
||
//IpxPrint0("IpxTransferDataComplete: Calling PassDgToRt\n");
|
||
//ByteOffset = sizeof(IPX_HEADER);
|
||
ByteOffset = 0;
|
||
PassDgToRt(IpxDevice, Reserved->pContext, Reserved->Index,
|
||
&Reserved->ReceiveBuffer->Data[ByteOffset],
|
||
BytesTransferred);
|
||
|
||
//
|
||
// Free the memory allocated for options.
|
||
//
|
||
IpxFreeMemory(Reserved->pContext, sizeof(IPX_DATAGRAM_OPTIONS2),
|
||
MEMORY_PACKET, "RT OPTIONS");
|
||
//
|
||
// Now free the receive buffer back.
|
||
//
|
||
|
||
IPX_PUSH_ENTRY_LIST(
|
||
&Adapter->ReceiveBufferList,
|
||
&Reserved->ReceiveBuffer->PoolLinkage,
|
||
Adapter->DeviceLock);
|
||
|
||
Reserved->ReceiveBuffer = NULL;
|
||
}
|
||
|
||
//
|
||
// Now free the packet.
|
||
//
|
||
|
||
NdisReinitializePacket (NdisPacket);
|
||
|
||
if (Reserved->OwnedByAddress) {
|
||
|
||
// Reserved->Address->ReceivePacketInUse = FALSE;
|
||
InterlockedDecrement(&Reserved->Address->ReceivePacketInUse);
|
||
|
||
} else {
|
||
|
||
Device = Adapter->Device;
|
||
|
||
IPX_PUSH_ENTRY_LIST(
|
||
&Device->ReceivePacketList,
|
||
&Reserved->PoolLinkage,
|
||
&Device->SListsLock);
|
||
|
||
}
|
||
|
||
if (!Reserved->pContext) {
|
||
//
|
||
// We Delay inserting the last request (or the only one)
|
||
// until after we have put the packet back, to keep the
|
||
// address around if needed (the address won't go away
|
||
// until the last address file does, and the address file
|
||
// won't go away until the datagram is completed).
|
||
//
|
||
|
||
IPX_INSERT_TAIL_LIST(
|
||
&Adapter->RequestCompletionQueue,
|
||
REQUEST_LINKAGE(LastRequest),
|
||
Adapter->DeviceLock);
|
||
}
|
||
|
||
IpxReceiveComplete ((NDIS_HANDLE)Adapter);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
Device = Adapter->Device;
|
||
|
||
(*Device->UpperDrivers[Reserved->Identifier].TransferDataCompleteHandler)(
|
||
NdisPacket,
|
||
NdisStatus,
|
||
BytesTransferred);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
} /* IpxTransferDataComplete */
|
||
|
||
|
||
VOID
|
||
IpxTransferData(
|
||
OUT PNDIS_STATUS Status,
|
||
IN NDIS_HANDLE NdisBindingHandle,
|
||
IN NDIS_HANDLE MacReceiveContext,
|
||
IN UINT ByteOffset,
|
||
IN UINT BytesToTransfer,
|
||
IN OUT PNDIS_PACKET Packet,
|
||
OUT PUINT BytesTransferred
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by all tightly bound clients instead of NdisTransferData.
|
||
If this is a loopback packet, the transfer is done directly here, else NdisTransferData
|
||
is called.
|
||
|
||
Arguments:
|
||
|
||
Status - status of operation
|
||
NdisBindingHandle - Loopback cookie or Ndis context
|
||
MacReceiveContext - Loopback packet or Mac context
|
||
ByteOffset - Source offset
|
||
BytesToTransfer - length of the transfer desired
|
||
Packet - dest packet
|
||
BytesTransferred - length of successful transfer
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// If this is a loopback packet, copy the data directly
|
||
//
|
||
if (NdisBindingHandle == (PVOID)IPX_LOOPBACK_COOKIE) {
|
||
|
||
IPX_DEBUG (LOOPB, ("LoopbXfer: src: %lx, dest: %lx, bytestoxfer: %lx\n",
|
||
MacReceiveContext, Packet, BytesToTransfer));
|
||
|
||
NdisCopyFromPacketToPacketSafe(
|
||
Packet, // Destination
|
||
0, // DestinationOffset
|
||
BytesToTransfer, // BytesToCopy
|
||
(PNDIS_PACKET)MacReceiveContext, // Source
|
||
ByteOffset, // SourceOffset
|
||
BytesTransferred, // BytesCopied
|
||
NormalPagePriority);
|
||
|
||
*Status = ((*BytesTransferred == BytesToTransfer)? NDIS_STATUS_SUCCESS : NDIS_STATUS_RESOURCES);
|
||
} else {
|
||
NdisTransferData(
|
||
Status,
|
||
NdisBindingHandle,
|
||
MacReceiveContext,
|
||
ByteOffset,
|
||
BytesToTransfer,
|
||
Packet,
|
||
BytesTransferred);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
IpxTdiReceiveDatagram(
|
||
IN PREQUEST Request
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine performs the TdiReceiveDatagram request for the transport
|
||
provider. Receive datagrams just get queued up to an address, and are
|
||
completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at
|
||
the address.
|
||
|
||
Arguments:
|
||
|
||
Irp - I/O Request Packet for this request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PADDRESS Address;
|
||
PADDRESS_FILE AddressFile;
|
||
IPX_DEFINE_SYNC_CONTEXT (SyncContext)
|
||
IPX_DEFINE_LOCK_HANDLE (LockHandle)
|
||
|
||
|
||
//
|
||
// Do a quick check of the validity of the address.
|
||
//
|
||
|
||
AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request);
|
||
|
||
if ((AddressFile->Size != sizeof (ADDRESS_FILE)) ||
|
||
(AddressFile->Type != IPX_ADDRESSFILE_SIGNATURE)) {
|
||
|
||
return STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
Address = AddressFile->Address;
|
||
|
||
if ((Address == NULL) ||
|
||
(Address->Size != sizeof (ADDRESS)) ||
|
||
(Address->Type != IPX_ADDRESS_SIGNATURE)) {
|
||
|
||
return STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
IPX_BEGIN_SYNC (&SyncContext);
|
||
|
||
IPX_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
if (AddressFile->State != ADDRESSFILE_STATE_OPEN) {
|
||
|
||
IPX_FREE_LOCK (&Address->Lock, LockHandle);
|
||
IPX_END_SYNC (&SyncContext);
|
||
return STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
|
||
InsertTailList (&AddressFile->ReceiveDatagramQueue, REQUEST_LINKAGE(Request));
|
||
|
||
IoSetCancelRoutine (Request, IpxCancelReceiveDatagram);
|
||
|
||
if (Request->Cancel) {
|
||
|
||
(VOID)RemoveTailList (&AddressFile->ReceiveDatagramQueue);
|
||
IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL);
|
||
IPX_FREE_LOCK (&Address->Lock, LockHandle);
|
||
IPX_END_SYNC (&SyncContext);
|
||
return STATUS_CANCELLED;
|
||
}
|
||
|
||
IPX_DEBUG (RECEIVE, ("RDG posted on %lx\n", AddressFile));
|
||
|
||
IpxReferenceAddressFileLock (AddressFile, AFREF_RCV_DGRAM);
|
||
|
||
IPX_FREE_LOCK (&Address->Lock, LockHandle);
|
||
|
||
IPX_END_SYNC (&SyncContext);
|
||
|
||
return STATUS_PENDING;
|
||
|
||
} /* IpxTdiReceiveDatagram */
|
||
|
||
|
||
VOID
|
||
IpxCancelReceiveDatagram(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel a receive
|
||
datagram. The datagram is found on the address file's receive
|
||
datagram queue.
|
||
|
||
NOTE: This routine is called with the CancelSpinLock held and
|
||
is responsible for releasing it.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for this driver.
|
||
|
||
Irp - Pointer to the request packet representing the I/O request.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY p;
|
||
PADDRESS_FILE AddressFile;
|
||
PADDRESS Address;
|
||
PREQUEST Request = (PREQUEST)Irp;
|
||
BOOLEAN Found;
|
||
IPX_DEFINE_LOCK_HANDLE (LockHandle)
|
||
|
||
|
||
CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(REQUEST_MINOR_FUNCTION(Request) == TDI_RECEIVE_DATAGRAM));
|
||
|
||
CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_TRANSPORT_ADDRESS_FILE);
|
||
|
||
AddressFile = (PADDRESS_FILE)REQUEST_OPEN_CONTEXT(Request);
|
||
Address = AddressFile->Address;
|
||
|
||
Found = FALSE;
|
||
|
||
IPX_GET_LOCK (&Address->Lock, &LockHandle);
|
||
|
||
for (p = AddressFile->ReceiveDatagramQueue.Flink;
|
||
p != &AddressFile->ReceiveDatagramQueue;
|
||
p = p->Flink) {
|
||
|
||
if (LIST_ENTRY_TO_REQUEST(p) == Request) {
|
||
|
||
RemoveEntryList (p);
|
||
Found = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
IPX_FREE_LOCK (&Address->Lock, LockHandle);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
if (Found) {
|
||
|
||
IPX_DEBUG(RECEIVE, ("Cancelled datagram on %lx\n", AddressFile));
|
||
|
||
REQUEST_INFORMATION(Request) = 0;
|
||
REQUEST_STATUS(Request) = STATUS_CANCELLED;
|
||
|
||
IpxCompleteRequest (Request);
|
||
ASSERT( DeviceObject->DeviceExtension == IpxDevice );
|
||
IpxFreeRequest(IpxDevice, Request);
|
||
|
||
IpxDereferenceAddressFile (AddressFile, AFREF_RCV_DGRAM);
|
||
|
||
}
|
||
|
||
} /* IpxCancelReceiveDatagram */
|
||
|
||
|