/*++ 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 */