/*++ Copyright (c) 1997 Microsoft Corporation Module Name: D:\nt\private\ntos\tdi\rawwan\core\receive.c Abstract: Routines for receiving data, including TDI and NDIS entry points and completions. Revision History: Who When What -------- -------- ---------------------------------------------- arvindm 05-16-97 Created Notes: --*/ #include #define _FILENUMBER 'VCER' #if STATS ULONG RecvPktsOk = 0; ULONG RecvBytesOk = 0; ULONG RecvPktsFail = 0; ULONG RecvBytesFail = 0; #endif // STATS #if DBG BOOLEAN bVerifyData = FALSE; UCHAR CheckChar = 'X'; VOID RWanCheckDataForChar( IN PCHAR pHelpString, IN PVOID Context, IN PUCHAR pBuffer, IN ULONG Length, IN UCHAR Char ); #define RWAN_CHECK_DATA(_pHelp, _Ctxt, _pBuf, _Len) \ { \ if (bVerifyData) \ { \ RWanCheckDataForChar(_pHelp, _Ctxt, _pBuf, _Len, CheckChar); \ } \ } #else #define RWAN_CHECK_DATA(_pHelp, _Ctxt, _pBuf, _Len) #endif // DBG #if DBG VOID RWanCheckDataForChar( IN PCHAR pHelpString, IN PVOID Context, IN PUCHAR pBuffer, IN ULONG Length, IN UCHAR Char ) { ULONG i; PUCHAR pBuf = pBuffer+1; for (i = 1; i < Length; i++) { if (*pBuf == Char) { DbgPrint("RAWWAN: %s: %p: Found char %c at offset %d, 0x%p, of buffer 0x%p\n", pHelpString, Context, Char, i, pBuf, pBuffer); DbgBreakPoint(); } pBuf++; } } #endif // DBG RWAN_STATUS RWanInitReceive( VOID ) /*++ Routine Description: Initialize our receive structures. We allocate a buffer pool and a packet pool for keeping copies of received packets that we aren't allowed to keep by the miniport. Arguments: None Return Value: RWAN_STATUS_SUCCESS if initialized successfully, else RWAN_STATUS_RESOURCES. --*/ { RWAN_STATUS RWanStatus; NDIS_STATUS Status; // // Initialize. // RWanCopyPacketPool = NULL; RWanCopyBufferPool = NULL; RWanStatus = RWAN_STATUS_SUCCESS; NdisAllocatePacketPoolEx( &Status, &RWanCopyPacketPool, RWAN_INITIAL_COPY_PACKET_COUNT, RWAN_OVERFLOW_COPY_PACKET_COUNT, 0 ); if (Status != NDIS_STATUS_SUCCESS) { RWanStatus = RWAN_STATUS_RESOURCES; } else { NdisAllocateBufferPool( &Status, &RWanCopyBufferPool, RWAN_INITIAL_COPY_PACKET_COUNT+RWAN_OVERFLOW_COPY_PACKET_COUNT ); if (Status != NDIS_STATUS_SUCCESS) { NdisFreePacketPool(RWanCopyPacketPool); RWanCopyPacketPool = NULL; RWanStatus = RWAN_STATUS_RESOURCES; } } return (RWanStatus); } VOID RWanShutdownReceive( VOID ) /*++ Routine Description: This is shutdown code, to clean up our receive structures. We free the packet pool and buffer pool allocated when we init'ed. Arguments: None Return Value: None --*/ { if (RWanCopyPacketPool != NULL) { NdisFreePacketPool(RWanCopyPacketPool); RWanCopyPacketPool = NULL; } if (RWanCopyBufferPool != NULL) { NdisFreeBufferPool(RWanCopyBufferPool); RWanCopyBufferPool = NULL; } return; } TDI_STATUS RWanTdiReceive( IN PTDI_REQUEST pTdiRequest, OUT PUSHORT pFlags, IN PUINT pReceiveLength, IN PNDIS_BUFFER pNdisBuffer ) /*++ Routine Description: This is the TDI Entry point for receiving data over a connection. Arguments: pTdiRequest - Pointer to the TDI Request pFlags - Place to return additional information about this receive pReceiveLength - Points to the total length of the receive buffer chain pNdisBuffer - Start of receive buffer chain Return Value: TDI_PENDING if we queued this receive request successfully TDI_INVALID_CONNECTION if the Connection context isn't valid TDI_NO_RESOURCES if we had a resource problem with this receive --*/ { RWAN_CONN_ID ConnId; PRWAN_TDI_CONNECTION pConnObject; BOOLEAN bIsLockAcquired; // Do we hold the Conn Object lock PRWAN_NDIS_VC pVc; TDI_STATUS TdiStatus; PRWAN_RECEIVE_REQUEST pRcvReq; PRWAN_NDIS_ADAPTER pAdapter; // // Initialize. // TdiStatus = TDI_PENDING; bIsLockAcquired = FALSE; ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext); pRcvReq = NULL; do { // // Allocate space to hold context for this receive // pRcvReq = RWanAllocateReceiveReq(); if (pRcvReq == NULL) { RWANDEBUGP(DL_INFO, DC_WILDCARD, ("Rcv: Failed to allocate receive req!\n")); TdiStatus = TDI_NO_RESOURCES; break; } // // Prepare the receive request. // pRcvReq->Request.pReqComplete = pTdiRequest->RequestNotifyObject; pRcvReq->Request.ReqContext = pTdiRequest->RequestContext; pRcvReq->TotalBufferLength = *pReceiveLength; pRcvReq->AvailableBufferLength = *pReceiveLength; pRcvReq->pUserFlags = pFlags; pRcvReq->pBuffer = pNdisBuffer; NdisQueryBufferSafe( pNdisBuffer, &(pRcvReq->pWriteData), &(pRcvReq->BytesLeftInBuffer), NormalPagePriority ); if (pRcvReq->pWriteData == NULL) { RWANDEBUGP(DL_INFO, DC_WILDCARD, ("Rcv: Failed to query req buffer!\n")); TdiStatus = TDI_NO_RESOURCES; break; } pRcvReq->pNextRcvReq = NULL; if (pRcvReq->BytesLeftInBuffer > pRcvReq->AvailableBufferLength) { RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Rcv: pRcvReq %x, BytesLeft %d > Available %d, pTdiRequest %x\n", pRcvReq, pRcvReq->BytesLeftInBuffer, pRcvReq->AvailableBufferLength, pTdiRequest)); pRcvReq->BytesLeftInBuffer = pRcvReq->AvailableBufferLength; } // // See if the given Connection is valid. // RWAN_ACQUIRE_CONN_TABLE_LOCK(); pConnObject = RWanGetConnFromId(ConnId); RWAN_RELEASE_CONN_TABLE_LOCK(); if (pConnObject == NULL_PRWAN_TDI_CONNECTION) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: Invalid connection!\n")); TdiStatus = TDI_INVALID_CONNECTION; break; } bIsLockAcquired = TRUE; RWAN_ACQUIRE_CONN_LOCK(pConnObject); // // Check the Connection state. // if ((pConnObject->State != RWANS_CO_CONNECTED) || (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING))) { RWANDEBUGP(DL_INFO, DC_DATA_RX, ("TdiReceive: Conn %x bad state %d/flags %x\n", pConnObject, pConnObject->State, pConnObject->Flags)); TdiStatus = TDI_INVALID_STATE; break; } pVc = pConnObject->NdisConnection.pNdisVc; RWAN_ASSERT(pVc != NULL); RWAN_STRUCT_ASSERT(pVc, nvc); pAdapter = pVc->pNdisAf->pAdapter; // // Queue the receive request at the end of the queue on this VC. // if (pVc->pRcvReqHead == NULL) { pVc->pRcvReqHead = pVc->pRcvReqTail = pRcvReq; } else { RWAN_ASSERT(pVc->pRcvReqTail != NULL); pVc->pRcvReqTail->pNextRcvReq = pRcvReq; pVc->pRcvReqTail = pRcvReq; } RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Rcv: VC %x, queued RcvReq %x, space available %d bytes\n", pVc, pRcvReq, pRcvReq->AvailableBufferLength)); // // Start the common indicate code (for receive requests as well // as for receive indications). // RWAN_RESET_BIT(pConnObject->Flags, RWANF_CO_PAUSE_RECEIVE); RWanIndicateData(pConnObject); bIsLockAcquired = FALSE; // // Force return of any received packets that we have completed // processing, to the miniport. // RWanNdisReceiveComplete((NDIS_HANDLE)pAdapter); break; } while (FALSE); if (bIsLockAcquired) { RWAN_RELEASE_CONN_LOCK(pConnObject); } if (TdiStatus != TDI_PENDING) { // // Error - clean up. // if (pRcvReq != NULL) { RWanFreeReceiveReq(pRcvReq); } } return (TdiStatus); } UINT RWanNdisCoReceivePacket( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE ProtocolVcContext, IN PNDIS_PACKET pNdisPacket ) /*++ Routine Description: This is the NDIS entry point announcing arrival of a packet on a VC known to us. Arguments: ProtocolBindingContext - Pointer to our Adapter context ProtocolVcContext - Pointer to our VC context pNdisPacket - the received packet Return Value: UINT - this is the reference count we place on the packet. This is 0 if we either dropped the packet, or if the miniport had marked the packet with NDIS_STATUS_RESOURCES. Otherwise, this is 1 (we queue the packet and will call NdisReturnPackets later). --*/ { PRWAN_NDIS_VC pVc; PRWAN_TDI_CONNECTION pConnObject; UINT PacketRefCount; NDIS_STATUS ReceiveStatus; PRWAN_RECEIVE_INDICATION pRcvInd; BOOLEAN bIsMiniportPacket; // Are we queueing the miniport's packet? BOOLEAN bIsLockAcquired; #if STATS PNDIS_BUFFER StpNdisBuffer; PVOID StFirstBufferVa; UINT StFirstBufferLength; UINT StTotalLength; #endif // STATS UNREFERENCED_PARAMETER(ProtocolBindingContext); pVc = (PRWAN_NDIS_VC)ProtocolVcContext; RWAN_STRUCT_ASSERT(pVc, nvc); RWAN_ASSERT(pNdisPacket); pConnObject = pVc->pConnObject; RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Rcv: VC %x, NdisVCHandle %x, Pkt %x\n", ProtocolVcContext, ((PRWAN_NDIS_VC)ProtocolVcContext)->NdisVcHandle, pNdisPacket)); // // Initialize. // PacketRefCount = 1; ReceiveStatus = NDIS_STATUS_SUCCESS; bIsMiniportPacket = TRUE; bIsLockAcquired = TRUE; do { #if STATS NdisGetFirstBufferFromPacket( pNdisPacket, &StpNdisBuffer, &StFirstBufferVa, &StFirstBufferLength, &StTotalLength ); #endif // STATS #if DBG // // Debugging miniports indicating up garbage packets. // { ULONG DbgTotalLength; PNDIS_BUFFER pDbgFirstBuffer; PVOID pFirstBufferVA; UINT DbgFirstBufferLength; UINT DbgTotalBufferLength; if ((pNdisPacket->Private.Head == NULL) || (pNdisPacket->Private.Tail == NULL)) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: VC %x, Pkt %x, Head/Tail is NULL!\n", ProtocolVcContext, pNdisPacket)); RWAN_ASSERT(FALSE); } NdisGetFirstBufferFromPacket( pNdisPacket, &pDbgFirstBuffer, &pFirstBufferVA, &DbgFirstBufferLength, &DbgTotalBufferLength ); if (pDbgFirstBuffer == NULL) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: VC %x, Pkt %x, first buffer is NULL!\n", ProtocolVcContext, pNdisPacket)); RWAN_ASSERT(FALSE); } if (DbgFirstBufferLength == 0) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: VC %x, Pkt %x, first buffer length is 0!\n", ProtocolVcContext, pNdisPacket)); // RWAN_ASSERT(FALSE); } if (DbgTotalBufferLength == 0) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: VC %x, Pkt %x, Total buffer length is 0, FirstBufferLength %d!\n", ProtocolVcContext, pNdisPacket, DbgFirstBufferLength)); // RWAN_ASSERT(FALSE); } if (pFirstBufferVA == NULL) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: VC %x, Pkt %x, FirstBufferVA is NULL, FirstLength %d, Total %d\n", ProtocolVcContext, pNdisPacket, DbgFirstBufferLength, DbgTotalBufferLength)); RWAN_ASSERT(FALSE); } RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Recv: VC %x, Pkt %x, TotalLength %d bytes\n", ProtocolVcContext, pNdisPacket, DbgTotalBufferLength)); if (DbgTotalBufferLength == 0) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Recv: VC %x, Pkt %x: discarding cuz zero length\n", ProtocolVcContext, pNdisPacket)); bIsLockAcquired = FALSE; PacketRefCount = 0; ReceiveStatus = NDIS_STATUS_FAILURE; break; } } #endif // // See if we are aborting this connection. If so, drop this packet. // if (pConnObject == NULL) { bIsLockAcquired = FALSE; PacketRefCount = 0; ReceiveStatus = NDIS_STATUS_FAILURE; RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: dropping cuz ConnObj is NULL\n")); break; } RWAN_STRUCT_ASSERT(pConnObject, ntc); RWAN_ACQUIRE_CONN_LOCK(pConnObject); // // See if connection is closing. If so, drop this packet. // if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING) || ((pConnObject->State != RWANS_CO_CONNECTED) && (pConnObject->State != RWANS_CO_IN_CALL_ACCEPTING))) { PacketRefCount = 0; ReceiveStatus = NDIS_STATUS_FAILURE; RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: dropping on Conn %p, Flags %x, State %d\n", pConnObject, pConnObject->Flags, pConnObject->State)); break; } // // If the packet cannot be queued, attempt to make a copy. // if (NDIS_GET_PACKET_STATUS(pNdisPacket) == NDIS_STATUS_RESOURCES) { PacketRefCount = 0; // we cannot hang on to this packet pNdisPacket = RWanMakeReceiveCopy(pNdisPacket); if (pNdisPacket == NULL) { RWANDEBUGP(DL_WARN, DC_WILDCARD, ("Rcv: failed to allocate receive copy!\n")); ReceiveStatus = NDIS_STATUS_RESOURCES; PacketRefCount = 0; break; } bIsMiniportPacket = FALSE; } // // Prepare a receive indication element to keep track of this // receive. // pRcvInd = RWanAllocateReceiveInd(); if (pRcvInd == NULL) { PacketRefCount = 0; ReceiveStatus = NDIS_STATUS_RESOURCES; RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Rcv: dropping cuz of failure allocating receive Ind!\n")); break; } pRcvInd->pPacket = pNdisPacket; pRcvInd->bIsMiniportPacket = bIsMiniportPacket; pRcvInd->pNextRcvInd = NULL; pRcvInd->pVc = pVc; NdisGetFirstBufferFromPacket( pNdisPacket, &(pRcvInd->pBuffer), (PVOID *)&(pRcvInd->pReadData), &(pRcvInd->BytesLeftInBuffer), &(pRcvInd->PacketLength) ); pRcvInd->TotalBytesLeft = pRcvInd->PacketLength; // // Queue the receive indication at the end of the receive queue on // this VC. // if (pVc->pRcvIndHead == NULL) { pVc->pRcvIndHead = pVc->pRcvIndTail = pRcvInd; } else { RWAN_ASSERT(pVc->pRcvIndTail != NULL); pVc->pRcvIndTail->pNextRcvInd = pRcvInd; pVc->pRcvIndTail = pRcvInd; } RWANDEBUGP(DL_EXTRA_LOUD, DC_DATA_RX, ("CoRcvPacket: Pkt x%x, pVc x%x, pRcvInd x%x, BytesLeft %d, PktLen %d, Head x%x, Tail x%x\n", pNdisPacket, pVc, pRcvInd, pRcvInd->BytesLeftInBuffer, pRcvInd->PacketLength, pVc->pRcvIndHead, pVc->pRcvIndTail)); pVc->PendingPacketCount++; // Receive Ind // // Start the common indicate code (for receive requests as well // as for receive indications). // if (pConnObject->State != RWANS_CO_IN_CALL_ACCEPTING) { RWanIndicateData(pConnObject); } else { RWAN_RELEASE_CONN_LOCK(pConnObject); RWANDEBUGP(DL_FATAL, DC_DATA_RX, ("Rcv: queued packet %d bytes on accepting VC %x, pConn %x\n", pRcvInd->PacketLength, pVc, pConnObject)); } bIsLockAcquired = FALSE; // It's released within RWanIndicateData break; } while (FALSE); if (bIsLockAcquired) { RWAN_RELEASE_CONN_LOCK(pConnObject); } if (ReceiveStatus != NDIS_STATUS_SUCCESS) { #if STATS INCR_STAT(&RecvPktsFail); ADD_STAT(&RecvBytesFail, StTotalLength); #endif // STATS // // Clean up. // if (!bIsMiniportPacket && (pNdisPacket != NULL)) { RWanFreeReceiveCopy(pNdisPacket); } } #if STATS else { INCR_STAT(&RecvPktsOk); ADD_STAT(&RecvBytesOk, StTotalLength); } #endif // STATS return (PacketRefCount); } VOID RWanIndicateData( IN PRWAN_TDI_CONNECTION pConnObject ) /*++ Routine Description: Core internal receive processing routine. This tries to match up queued receive requests with queued receive indications and completes as many requests as it can. It calls the receive event handler, if any, for a receive indication that reaches the head of its queue without matching up with a receive request. Arguments: pConnObject - Points to our TDI Connection context. Locks on Entry: pConnObject Locks on Exit: None Return Value: None --*/ { PRWAN_TDI_ADDRESS pAddrObject; PRWAN_NDIS_VC pVc; PRWAN_NDIS_ADAPTER pAdapter; PRcvEvent pRcvIndEvent; INT rc; PRWAN_RECEIVE_REQUEST pRcvReq; PRWAN_RECEIVE_INDICATION pRcvInd; PRWAN_RECEIVE_INDICATION pNextRcvInd; UINT BytesToCopy; // // List of receive indications that have been completed here. // PRWAN_RECEIVE_INDICATION pCompletedRcvIndHead; PRWAN_RECEIVE_INDICATION pCompletedRcvIndTail; BOOLEAN IsMessageMode = TRUE; // // TBD: Set IsMessageMode based on the connection type/protocol type. // PVOID TdiEventContext; BOOLEAN bConnectionInBadState = FALSE; BOOLEAN bContinue = TRUE; pVc = pConnObject->NdisConnection.pNdisVc; pAddrObject = pConnObject->pAddrObject; RWAN_ASSERT(pAddrObject != NULL); pRcvIndEvent = pAddrObject->pRcvInd; TdiEventContext = pAddrObject->RcvIndContext; pCompletedRcvIndHead = NULL; pCompletedRcvIndTail = NULL; pAdapter = pVc->pNdisAf->pAdapter; // // Check if the client has paused receiving. // if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_PAUSE_RECEIVE)) { RWAN_RELEASE_CONN_LOCK(pConnObject); return; } // // Re-entrancy check. // if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_INDICATING_DATA)) { RWAN_RELEASE_CONN_LOCK(pConnObject); return; } RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_INDICATING_DATA); // // Make sure the Connection Object doesn't go away as long // as we are in this routine. // RWanReferenceConnObject(pConnObject); // temp ref: RWanIndicateData RWANDEBUGP(DL_INFO, DC_DATA_RX, ("=> Ind-Rcv: VC %x/%x, ReqHead %x, IndHead %x\n", pVc, pVc->Flags, pVc->pRcvReqHead, pVc->pRcvIndHead)); // // Loop till we run out of receive requests/indications. // for (/* Nothing */; /* Nothing */; /* Nothing */) { if (pVc->pRcvIndHead == NULL) { // // No data to pass up. Quit. // break; } // // See if we have data available in the current receive indication. // pRcvInd = pVc->pRcvIndHead; if (pRcvInd->TotalBytesLeft == 0) { // // Move to the next receive indication. // pNextRcvInd = pRcvInd->pNextRcvInd; // // Add the current receive indication to the list of receive // indications to be freed up. // pRcvInd->pNextRcvInd = NULL; if (pCompletedRcvIndTail != NULL) { pCompletedRcvIndTail->pNextRcvInd = pRcvInd; } else { pCompletedRcvIndHead = pRcvInd; } pCompletedRcvIndTail = pRcvInd; pVc->PendingPacketCount--; // Moved packet to completed list // // Move to the next receive indication. // pVc->pRcvIndHead = pNextRcvInd; pRcvInd = pNextRcvInd; // // See if there are no more receive indications. // if (pRcvInd == NULL) { pVc->pRcvIndTail = NULL; break; } } #if DBG if (pRcvInd) { RWAN_CHECK_DATA("IndicateData:", pRcvInd, pRcvInd->pReadData, pRcvInd->BytesLeftInBuffer); } #endif // DBG // // We have data available to pass up. // // If we don't have any pending receive requests, and there // is a Receive Indication event handler available, call the // handler. We may get back a receive request. // if ((pVc->pRcvReqHead == NULL) && (pRcvIndEvent != NULL)) { CONNECTION_CONTEXT ConnectionHandle; ULONG ReceiveFlags; ULONG BytesIndicated; ULONG BytesTaken; ULONG BytesAvailable; PVOID pTSDU; TDI_STATUS TdiStatus; #ifdef NT EventRcvBuffer * ERB; EventRcvBuffer ** pERB = &ERB; PTDI_REQUEST_KERNEL_RECEIVE pRequestInformation; PIO_STACK_LOCATION pIrpSp; #else EventRcvBuffer ERB; EventRcvBuffer * pERB = &ERB; #endif // !NT // // Pre-allocate a receive request. // pRcvReq = RWanAllocateReceiveReq(); if (pRcvReq == NULL) { RWAN_ASSERT(FALSE); break; } pRcvInd = pVc->pRcvIndHead; ConnectionHandle = pConnObject->ConnectionHandle; BytesIndicated = pRcvInd->BytesLeftInBuffer; BytesAvailable = pRcvInd->TotalBytesLeft; pTSDU = (PVOID)pRcvInd->pReadData; RWAN_RELEASE_CONN_LOCK(pConnObject); ReceiveFlags = TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE; BytesTaken = 0; RWAN_ASSERT(BytesIndicated != 0); RWAN_ASSERT(BytesAvailable != 0); TdiStatus = (*pRcvIndEvent)( TdiEventContext, ConnectionHandle, ReceiveFlags, BytesIndicated, BytesAvailable, &BytesTaken, pTSDU, pERB ); RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Ind-Rcv: VC %x, Head %x, Indicated %d, Available %d, Bytes taken %d, Status %x\n", pVc, pVc->pRcvReqHead, BytesIndicated, BytesAvailable, BytesTaken, TdiStatus)); RWAN_ACQUIRE_CONN_LOCK(pConnObject); // // Check if anything bad happened to this connection // while we were indicating. // if ((pConnObject->State != RWANS_CO_CONNECTED) || (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING))) { RWanFreeReceiveReq(pRcvReq); bConnectionInBadState = TRUE; break; } // // See if a receive request is given to us. // if (TdiStatus == TDI_MORE_PROCESSING) { // // We have a receive request. Get at it. // #ifdef NT NTSTATUS Status; RWAN_ASSERT(ERB != NULL); pIrpSp = IoGetCurrentIrpStackLocation(*pERB); Status = RWanPrepareIrpForCancel( (PRWAN_ENDPOINT) pIrpSp->FileObject->FsContext, ERB, RWanCancelRequest ); if (NT_SUCCESS(Status)) { pRequestInformation = (PTDI_REQUEST_KERNEL_RECEIVE) &(pIrpSp->Parameters); pRcvReq->Request.pReqComplete = RWanDataRequestComplete; pRcvReq->Request.ReqContext = ERB; pRcvReq->TotalBufferLength = pRequestInformation->ReceiveLength; pRcvReq->pBuffer = ERB->MdlAddress; pRcvReq->pUserFlags = (PUSHORT) &(pRequestInformation->ReceiveFlags); #else pRcvReq->Request.pReqComplete = ERB.erb_rtn; pRcvReq->Request.ReqContext = ERB.erb_context; pRcvReq->TotalBufferLength = ERB.erb_size; pRcvReq->pBuffer = ERB.erb_buffer; pRcvReq->pUserFlags = ERB.erb_flags; #endif // NT pRcvReq->AvailableBufferLength = pRcvReq->TotalBufferLength; NdisQueryBufferSafe( pRcvReq->pBuffer, &(pRcvReq->pWriteData), &(pRcvReq->BytesLeftInBuffer), NormalPagePriority ); if (pRcvReq->pWriteData != NULL) { if (pRcvReq->BytesLeftInBuffer > pRcvReq->AvailableBufferLength) { RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Indicate: pRcvReq %x, BytesLeft %d > Available %d, pTdiRequest %x\n", pRcvReq, pRcvReq->BytesLeftInBuffer, pRcvReq->AvailableBufferLength, pRequestInformation)); pRcvReq->BytesLeftInBuffer = pRcvReq->AvailableBufferLength; } pRcvReq->pNextRcvReq = NULL; // // Insert this receive request at the head of the pending // request queue. // if (pVc->pRcvReqHead == NULL) { pVc->pRcvReqHead = pVc->pRcvReqTail = pRcvReq; } else { RWAN_ASSERT(pVc->pRcvReqTail != NULL); pRcvReq->pNextRcvReq = pVc->pRcvReqHead; pVc->pRcvReqHead = pRcvReq; } } else { // // Couldn't get virtual address of MDL passed in. // TdiStatus = TDI_SUCCESS; RWanFreeReceiveReq(pRcvReq); pRcvReq = NULL; } #ifdef NT } else { // // The IRP was cancelled before it got to us. // Continue as if the user returned SUCCESS. // TdiStatus = TDI_SUCCESS; RWanFreeReceiveReq(pRcvReq); pRcvReq = NULL; } #endif // NT // // Update based on what was consumed during the Indicate. // pRcvInd->BytesLeftInBuffer -= BytesTaken; pRcvInd->TotalBytesLeft -= BytesTaken; // // If we still don't have any pending receive requests, quit. // if (pVc->pRcvReqHead == NULL) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Ind: VC %x/%x, ConnObj %x/%x, RcvInd %x, no pending Req\n", pVc, pVc->Flags, pConnObject, pConnObject->Flags, pRcvInd)); break; } // // We have receive requests, so continue from the top. // continue; } else { // // We didn't get a receive request. // if (TdiStatus == TDI_NOT_ACCEPTED) { BytesTaken = 0; // // By returning this status, the TDI client is telling // us to stop indicating data on this connection until // it sends us a TDI receive. // RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_PAUSE_RECEIVE); } // // Update based on what was consumed during the Indicate. // pRcvInd->BytesLeftInBuffer -= BytesTaken; pRcvInd->TotalBytesLeft -= BytesTaken; RWanFreeReceiveReq(pRcvReq); if (TdiStatus == TDI_SUCCESS) { continue; } } } // if Receive Event handler exists // // If we still don't have any pending receive requests, quit. // if (pVc->pRcvReqHead == NULL) { #if DBG1 if (pVc->pRcvIndHead && (pVc->pRcvIndHead->TotalBytesLeft == 0)) { RWANDEBUGP(DL_FATAL, DC_WILDCARD, ("Ind: VC %x/%x, No pending recv reqs, RcvInd empty!\n", pVc, pVc->Flags)); RWAN_ASSERT(FALSE); } #endif break; } // // Fill in the receive request at the head of the queue // as much as we can. // pRcvReq = pVc->pRcvReqHead; pRcvInd = pVc->pRcvIndHead; RWAN_ASSERT(pRcvReq != NULL); RWAN_ASSERT(pRcvInd != NULL); while (pRcvReq->AvailableBufferLength != 0) { if (pRcvReq->BytesLeftInBuffer == 0) { // // Move to the next buffer in the chain. // RWAN_ADVANCE_RCV_REQ_BUFFER(pRcvReq); RWAN_ASSERT(pRcvReq->BytesLeftInBuffer != 0); } RWAN_ASSERT(pRcvInd != NULL); if (pRcvInd->BytesLeftInBuffer == 0) { RWAN_ADVANCE_RCV_IND_BUFFER(pRcvInd); RWAN_ASSERT(pRcvInd->BytesLeftInBuffer != 0); } BytesToCopy = MIN(pRcvReq->BytesLeftInBuffer, pRcvInd->BytesLeftInBuffer); RWANDEBUGP(DL_EXTRA_LOUD, DC_DATA_RX, ("IndicateData: pVc x%x, pRcvInd x%x, pRcvReq x%x, copying %d bytes, %x to %x\n", pVc, pRcvInd, pRcvReq, BytesToCopy, pRcvInd->pReadData, pRcvReq->pWriteData)); #if DBG if (pRcvInd) { RWAN_CHECK_DATA("IndicateData - copy:", pRcvInd, pRcvInd->pReadData, BytesToCopy); } #endif // DBG RWAN_COPY_MEM(pRcvReq->pWriteData, pRcvInd->pReadData, BytesToCopy); pRcvReq->pWriteData += BytesToCopy; pRcvReq->BytesLeftInBuffer -= BytesToCopy; pRcvReq->AvailableBufferLength -= BytesToCopy; #if DBG if (pRcvReq->AvailableBufferLength > pRcvReq->TotalBufferLength) { RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Indicate: VC %x, RcvRq %x, Avail %d > Total %d, BytesToCopy %d, RcvInd %x\n", pVc, pRcvReq, pRcvReq->AvailableBufferLength, pRcvReq->TotalBufferLength, BytesToCopy, pRcvInd)); RWAN_ASSERT(FALSE); } #endif pRcvInd->pReadData += BytesToCopy; pRcvInd->BytesLeftInBuffer -= BytesToCopy; pRcvInd->TotalBytesLeft -= BytesToCopy; // // See if we have data available in the current receive indication. // if (pRcvInd->TotalBytesLeft == 0) { // // Move to the next receive indication. // pNextRcvInd = pRcvInd->pNextRcvInd; // // Add the current receive indication to the list of receive // indications to be freed up. // pRcvInd->pNextRcvInd = NULL; if (pCompletedRcvIndTail != NULL) { pCompletedRcvIndTail->pNextRcvInd = pRcvInd; } else { pCompletedRcvIndHead = pRcvInd; } pCompletedRcvIndTail = pRcvInd; pVc->PendingPacketCount--; // Moved packet to completed list // // Move to the next receive indication. // pVc->pRcvIndHead = pNextRcvInd; pRcvInd = pNextRcvInd; // // See if there are no more receive indications. // if (pRcvInd == NULL) { pVc->pRcvIndTail = NULL; break; } // // If this connection uses message mode delivery, // we don't allow a receive request to span multiple // received packets. // if (IsMessageMode) { break; } } } // // A receive request has been filled in either completely // or partially. If we are in message mode, complete the // receive now, otherwise we will wait for more data. // if ((pRcvReq->AvailableBufferLength == 0) || IsMessageMode) { TDI_STATUS ReceiveStatus; UINT BytesCopied; // // A receive request has been fully/partially satisfied. Take it // out of the pending list and complete it. // pVc->pRcvReqHead = pRcvReq->pNextRcvReq; if (pVc->pRcvReqHead == NULL) { pVc->pRcvReqTail = NULL; } BytesCopied = pRcvReq->TotalBufferLength - pRcvReq->AvailableBufferLength; // // Check if we copied in only part of a received packet into // this receive request. If so, indicate an overflow. // if ((pRcvReq->AvailableBufferLength == 0) && (pVc->pRcvIndHead != NULL) && (pVc->pRcvIndHead->TotalBytesLeft != pVc->pRcvIndHead->PacketLength)) { RWANDEBUGP(DL_LOUD, DC_WILDCARD, ("Ind-Rcv: Overflow: VC %x/%x, Head %x, BytesCopied %d, Left %d\n", pVc, pVc->Flags, pVc->pRcvIndHead, BytesCopied, pVc->pRcvIndHead->TotalBytesLeft)); ReceiveStatus = TDI_BUFFER_OVERFLOW; *(pRcvReq->pUserFlags) = 0; } else { ReceiveStatus = TDI_SUCCESS; *(pRcvReq->pUserFlags) = TDI_RECEIVE_ENTIRE_MESSAGE; } RWAN_RELEASE_CONN_LOCK(pConnObject); *(pRcvReq->pUserFlags) |= TDI_RECEIVE_NORMAL; RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Ind-Rcv: VC %x/%x, Head %x, completing TDI Rcv %x, %d bytes, Status %x\n", pVc, pVc->Flags, pVc->pRcvReqHead, pRcvReq, BytesCopied, ReceiveStatus)); // // Complete the Receive Req // (*pRcvReq->Request.pReqComplete)( pRcvReq->Request.ReqContext, ReceiveStatus, BytesCopied ); RWanFreeReceiveReq(pRcvReq); RWAN_ACQUIRE_CONN_LOCK(pConnObject); // // Check if anything bad happened to this connection // while we were completing the receive request. // if ((pConnObject->State != RWANS_CO_CONNECTED) || (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING))) { bConnectionInBadState = TRUE; break; } } } // forever RWAN_RESET_BIT(pConnObject->Flags, RWANF_CO_INDICATING_DATA); rc = RWanDereferenceConnObject(pConnObject); // end temp ref: RWanIndicateData if (rc > 0) { // // Update receive indication queue on the VC. Only if the VC // is still around... // if (pVc == pConnObject->NdisConnection.pNdisVc) { if (bConnectionInBadState) { ULONG AbortCount = 0; RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Ind: start abort VC %x/%x state %d, pComplRcvHead %p, tail %p\n", pVc, pVc->Flags, pVc->State, pCompletedRcvIndHead, pCompletedRcvIndTail)); // // Take out all pending receives. // for (pRcvInd = pVc->pRcvIndHead; pRcvInd != NULL; pRcvInd = pNextRcvInd) { pNextRcvInd = pRcvInd->pNextRcvInd; pRcvInd->pNextRcvInd = NULL; if (pCompletedRcvIndTail != NULL) { pCompletedRcvIndTail->pNextRcvInd = pRcvInd; } else { pCompletedRcvIndHead = pRcvInd; } pCompletedRcvIndTail = pRcvInd; pVc->PendingPacketCount--; // Abort: Moved packet to completed list AbortCount++; } pVc->pRcvIndHead = pVc->pRcvIndTail = NULL; RWANDEBUGP(DL_INFO, DC_DATA_RX, ("Ind: end abort VC %x/%x state %d, pComplRcvHead %p, tail %p, Count %d\n", pVc, pVc->Flags, pVc->State, pCompletedRcvIndHead, pCompletedRcvIndTail, AbortCount)); } else { // // Update the first Receive Indication if necessary. // if (pVc->pRcvIndHead && (pVc->pRcvIndHead->TotalBytesLeft == 0)) { RWANDEBUGP(DL_LOUD, DC_WILDCARD, ("Ind: VC %x/%x, empty pRcvInd at head %x\n", pVc, pVc->Flags, pVc->pRcvIndHead)); pRcvInd = pVc->pRcvIndHead; pNextRcvInd = pRcvInd->pNextRcvInd; pRcvInd->pNextRcvInd = NULL; if (pCompletedRcvIndTail != NULL) { pCompletedRcvIndTail->pNextRcvInd = pRcvInd; } else { pCompletedRcvIndHead = pRcvInd; } pCompletedRcvIndTail = pRcvInd; pVc->PendingPacketCount--; // IndComplete: Moved packet to completed list pVc->pRcvIndHead = pNextRcvInd; if (pVc->pRcvIndHead == NULL) { pVc->pRcvIndTail = NULL; } } } } #if DBG else { RWANDEBUGP(DL_FATAL, DC_DATA_RX, ("Ind: ConnObj %p, VC %p blown away!\n", pConnObject, pVc)); } #endif // DBG // // Check if we had queued up an IncomingClose while indicating data: // if (RWAN_IS_FLAG_SET(pConnObject->Flags, RWANF_CO_PENDED_DISCON, RWANF_CO_PENDED_DISCON)) { RWAN_RESET_BIT(pConnObject->Flags, RWANF_CO_PENDED_DISCON); RWANDEBUGP(DL_FATAL, DC_DATA_RX, ("Ind: Conn %x, State %d, Addr %x, handling pended discon\n", pConnObject, pConnObject->State, pConnObject->pAddrObject)); if (pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS) { PDisconnectEvent pDisconInd; PVOID IndContext; PVOID ConnectionHandle; pDisconInd = pConnObject->pAddrObject->pDisconInd; IndContext = pConnObject->pAddrObject->DisconIndContext; if (pDisconInd != NULL) { RWANDEBUGP(DL_FATAL, DC_DATA_RX, ("IndicateData: pConnObj %x/%x, st %x, will discon ind\n", pConnObject, pConnObject->Flags, pConnObject->State)); pConnObject->State = RWANS_CO_DISCON_INDICATED; ConnectionHandle = pConnObject->ConnectionHandle; RWanScheduleDisconnect(pConnObject); bContinue = FALSE; (*pDisconInd)( IndContext, ConnectionHandle, 0, // Disconnect Data Length NULL, // Disconnect Data 0, // Disconnect Info Length NULL, // Disconnect Info TDI_DISCONNECT_RELEASE ); } else { RWAN_ASSERT(FALSE); } } else { RWAN_ASSERT(FALSE); } } // // Check if we need to close this connection. // if (bContinue) { if (RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_NEEDS_CLOSE)) { RWanStartCloseCall(pConnObject, pVc); } else { RWAN_RELEASE_CONN_LOCK(pConnObject); } } } // // Link all completed receive indications to the list on this adapter. // They will be returned to the miniport in the ReceiveComplete // handler. // RWAN_ACQUIRE_GLOBAL_LOCK(); { PRWAN_RECEIVE_INDICATION * ppRcvIndTail; ppRcvIndTail = &(pAdapter->pCompletedReceives); while (*ppRcvIndTail != NULL) { ppRcvIndTail = &((*ppRcvIndTail)->pNextRcvInd); } #if DBG if (bConnectionInBadState) { RWANDEBUGP(DL_INFO, DC_WILDCARD, ("Ind: Adapter %p &ComplRcvs %p ComplRcvs %p, will tack on %p\n", pAdapter, &pAdapter->pCompletedReceives, pAdapter->pCompletedReceives, pCompletedRcvIndHead)); } #endif // DBG *ppRcvIndTail = pCompletedRcvIndHead; } RWAN_RELEASE_GLOBAL_LOCK(); } VOID RWanNdisReceiveComplete( IN NDIS_HANDLE ProtocolBindingContext ) /*++ Routine Description: This is the entry point called by NDIS when the miniport informs it that it has completed indicating a bunch of received packets. We use this event to free up any completed receives on this adapter binding. Arguments: ProtocolBindingContext - Pointer to our Adapter structure Return Value: None --*/ { PRWAN_NDIS_ADAPTER pAdapter; PRWAN_RECEIVE_INDICATION pRcvInd; pAdapter = (PRWAN_NDIS_ADAPTER)ProtocolBindingContext; RWAN_STRUCT_ASSERT(pAdapter, nad); // // Detach the list of completed receives from the adapter. // RWAN_ACQUIRE_GLOBAL_LOCK(); pRcvInd = pAdapter->pCompletedReceives; pAdapter->pCompletedReceives = NULL; RWAN_RELEASE_GLOBAL_LOCK(); RWanFreeReceiveIndList(pRcvInd); return; } VOID RWanNdisTransferDataComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pNdisPacket, IN NDIS_STATUS Status, IN UINT BytesTransferred ) /*++ Routine Description: Arguments: Return Value: None --*/ { // Not expected. RWAN_ASSERT(FALSE); } NDIS_STATUS RWanNdisReceive( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE MacReceiveContext, IN PVOID HeaderBuffer, IN UINT HeaderBufferSize, IN PVOID pLookAheadBuffer, IN UINT LookAheadBufferSize, IN UINT PacketSize ) /*++ Routine Description: Arguments: Return Value: None --*/ { // Not expected. RWAN_ASSERT(FALSE); return (NDIS_STATUS_FAILURE); } INT RWanNdisReceivePacket( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pNdisPacket ) /*++ Routine Description: Arguments: Return Value: None --*/ { // Not expected. RWAN_ASSERT(FALSE); return (0); } PRWAN_RECEIVE_REQUEST RWanAllocateReceiveReq( VOID ) /*++ Routine Description: Allocate a structure to keep context of a TDI Receive request. Arguments: None Return Value: Pointer to the allocated receive request structure, or NULL. --*/ { PRWAN_RECEIVE_REQUEST pRcvReq; RWAN_ALLOC_MEM(pRcvReq, RWAN_RECEIVE_REQUEST, sizeof(RWAN_RECEIVE_REQUEST)); if (pRcvReq != NULL) { RWAN_SET_SIGNATURE(pRcvReq, nrr); } return (pRcvReq); } VOID RWanFreeReceiveReq( IN PRWAN_RECEIVE_REQUEST pRcvReq ) /*++ Routine Description: Free a receive request structure. Arguments: pRcvReq - Points to structure to be freed Return Value: None --*/ { RWAN_STRUCT_ASSERT(pRcvReq, nrr); RWAN_FREE_MEM(pRcvReq); } PRWAN_RECEIVE_INDICATION RWanAllocateReceiveInd( VOID ) /*++ Routine Description: Allocate a structure to keep context about an NDIS receive indication. Arguments: None Return Value: Pointer to the allocated structure, or NULL. --*/ { PRWAN_RECEIVE_INDICATION pRcvInd; RWAN_ALLOC_MEM(pRcvInd, RWAN_RECEIVE_INDICATION, sizeof(RWAN_RECEIVE_INDICATION)); if (pRcvInd != NULL) { RWAN_SET_SIGNATURE(pRcvInd, nri); } return (pRcvInd); } VOID RWanFreeReceiveInd( IN PRWAN_RECEIVE_INDICATION pRcvInd ) /*++ Routine Description: Free a receive indication structure. Arguments: pRcvInd - Points to structure to be freed. Return Value: None --*/ { RWAN_STRUCT_ASSERT(pRcvInd, nri); RWAN_FREE_MEM(pRcvInd); } PNDIS_PACKET RWanMakeReceiveCopy( IN PNDIS_PACKET pNdisPacket ) /*++ Routine Description: Make a copy of a received packet to a private packet. Arguments: pNdisPacket - Points to original packet Return Value: Pointer to private packet if successful, NULL otherwise. --*/ { PNDIS_PACKET pNewPacket; PNDIS_BUFFER pNewBuffer; PUCHAR pData; UINT TotalLength; UINT BytesCopied; NDIS_STATUS Status; // // Initialize. // pNewPacket = NULL; pNewBuffer = NULL; pData = NULL; do { NdisQueryPacket( pNdisPacket, NULL, NULL, NULL, &TotalLength ); // // Allocate space for the data. // RWAN_ALLOC_MEM(pData, UCHAR, TotalLength); if (pData == NULL) { break; } // // Make this an NDIS Buffer (MDL). // NdisAllocateBuffer( &Status, &pNewBuffer, RWanCopyBufferPool, pData, TotalLength ); if (Status != NDIS_STATUS_SUCCESS) { break; } // // Allocate a new packet. // NdisAllocatePacket( &Status, &pNewPacket, RWanCopyPacketPool ); if (Status != NDIS_STATUS_SUCCESS) { break; } NDIS_SET_PACKET_STATUS(pNewPacket, 0); // // Link the buffer to the packet. // NdisChainBufferAtFront(pNewPacket, pNewBuffer); // // Copy in the received packet. // NdisCopyFromPacketToPacket( pNewPacket, 0, // Destn offset TotalLength, pNdisPacket, 0, // Source offset &BytesCopied ); RWAN_ASSERT(BytesCopied == TotalLength); break; } while (FALSE); if (pNewPacket == NULL) { // // Clean up. // if (pData != NULL) { RWAN_FREE_MEM(pData); } if (pNewBuffer != NULL) { NdisFreeBuffer(pNewBuffer); } } return (pNewPacket); } VOID RWanFreeReceiveCopy( IN PNDIS_PACKET pCopyPacket ) /*++ Routine Description: Free a packet that was used to keep a copy of a received packet, and its components (buffer etc). Arguments: pCopyPacket - Points to packet to be freed. Return Value: None --*/ { PNDIS_BUFFER pCopyBuffer; PUCHAR pCopyData; UINT TotalLength; UINT BufferLength; NdisGetFirstBufferFromPacket( pCopyPacket, &pCopyBuffer, (PVOID *)&pCopyData, &BufferLength, &TotalLength ); RWAN_ASSERT(BufferLength == TotalLength); RWAN_ASSERT(pCopyBuffer != NULL); NdisFreePacket(pCopyPacket); NdisFreeBuffer(pCopyBuffer); RWAN_FREE_MEM(pCopyData); return; } VOID RWanFreeReceiveIndList( IN PRWAN_RECEIVE_INDICATION pRcvInd ) /*++ Routine Description: Free a list of receive indications, and return any packets in there that belong to the miniport. Arguments: pRcvInd - Head of list of receives. Return Value: None --*/ { PRWAN_RECEIVE_INDICATION pNextRcvInd; PNDIS_PACKET pNdisPacket; #if DBG RWAN_IRQL EntryIrq, ExitIrq; #endif // DBG RWAN_GET_ENTRY_IRQL(EntryIrq); while (pRcvInd != NULL) { pNextRcvInd = pRcvInd->pNextRcvInd; pNdisPacket = pRcvInd->pPacket; RWANDEBUGP(DL_EXTRA_LOUD, DC_DATA_RX, ("FreeRcvIndList: freeing Pkt x%x, RcvInd x%x\n", pNdisPacket, pRcvInd)); if (pRcvInd->bIsMiniportPacket) { NdisReturnPackets(&pNdisPacket, 1); } else { RWanFreeReceiveCopy(pNdisPacket); } RWanFreeReceiveInd(pRcvInd); pRcvInd = pNextRcvInd; } RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq); }