/*++ Copyright (c) 1995-1998 Microsoft Corporation Module Name: RCACoCl.c Abstract: The module implements the RCA Co-NDIS client. Author: Shyam Pather (SPATHER) Revision History: Who When What -------- -------- ---------------------------------------------- SPATHER 04-20-99 Created / adapted from original RCA code by RMachin/JameelH --*/ #include #define MODULE_NUMBER MODULE_COCL #define _FILENUMBER 'LCOC' #if PACKET_POOL_OPTIMIZATION // RecvPPOpt - Start LONG g_alRecvPPOptBuckets[RECVPPOPT_NUM_BUCKETS]; LONG g_lRecvPPOptOutstanding; NDIS_SPIN_LOCK g_RecvPPOptLock; // RecvPPOpt - End #endif BOOLEAN RCAReferenceVc( IN PRCA_VC pRcaVc ) { PRCA_ADAPTER pAdapter = pRcaVc->pAdapter; BOOLEAN rc = FALSE; RCADEBUGP(RCA_INFO, ("RCAReferenceVc: Enter\n")); ACQUIRE_SPIN_LOCK(&pAdapter->SpinLock); if ((pRcaVc->Flags & VC_CLOSING) == 0) { pRcaVc->RefCount++; rc = TRUE; } RCADEBUGP(RCA_LOUD, ("RCAReferenceVc: pRcaVc (0x%x) ref count is %d\n", pRcaVc, pRcaVc->RefCount)); RELEASE_SPIN_LOCK(&pAdapter->SpinLock); RCADEBUGP(RCA_INFO, ("RCAReferenceVc: Exit, returning 0x%x\n", rc)); return(rc); } VOID RCADereferenceVc( IN PRCA_VC pRcaVc ) { PRCA_ADAPTER pAdapter = pRcaVc->pAdapter; RCADEBUGP(RCA_INFO, ("RCADerefenceVc: Enter\n")); ACQUIRE_SPIN_LOCK(&pAdapter->SpinLock); pRcaVc->RefCount--; RCADEBUGP(RCA_LOUD, ("RCADereferenceVc: pRcaVc (0x%x) ref count is %d\n", pRcaVc, pRcaVc->RefCount)); if (pRcaVc->RefCount == 0) { ASSERT((pRcaVc->Flags & VC_ACTIVE) == 0); // // Take VC out of the adapter list // UnlinkDouble(pRcaVc, NextVcOnAdapter, PrevVcOnAdapter); RCADEBUGP(RCA_LOUD, ("RCADereferenceVc: Took vc out of list\n")); RCAFreeLock(&(pRcaVc->Lock)); RCAFreeMem(pRcaVc); } RELEASE_SPIN_LOCK(&pAdapter->SpinLock); RCADEBUGP(RCA_INFO, ("RCADerefenceVc: Exit\n")); } VOID RCACoSendComplete( IN NDIS_STATUS Status, IN NDIS_HANDLE ProtocolVcContext, IN PNDIS_PACKET pNdisPacket) /*++ Routine Description Handle completion of a send. All we need to do is free the resources and complete the event. Arguments Status - Result of the send ProtocolVcContext - RCA VC Packet - The Packet sent Return Value: None --*/ { PRCA_PROTOCOL_CONTEXT pProtocolContext = &GlobalContext; PMDL pMdl; RCADEBUGP(RCA_INFO, ("RCACoSendComplete: enter. VC=%x, Packet=%x\n", ProtocolVcContext, pNdisPacket)); InterlockedDecrement(&((PRCA_VC)ProtocolVcContext)->PendingSends); // // Get the MDL out of the packet and give it back to the client. // pMdl = (PMDL) pNdisPacket->Private.Head; ACQUIRE_SPIN_LOCK(&(((PRCA_VC)ProtocolVcContext)->SpinLock)); if (pProtocolContext->Handlers.SendCompleteCallback) { pProtocolContext->Handlers.SendCompleteCallback((PVOID)ProtocolVcContext, ((PRCA_VC)ProtocolVcContext)->ClientSendContext, PKT_RSVD_FROM_PKT(pNdisPacket)->PacketContext, pMdl, Status); } RELEASE_SPIN_LOCK(&(((PRCA_VC)ProtocolVcContext)->SpinLock)); #if PACKET_POOL_OPTIMIZATION // SendPPOpt - Start NdisAcquireSpinLock(&g_SendPPOptLock); g_lSendPPOptOutstanding--; NdisReleaseSpinLock(&g_SendPPOptLock); // SendPPOpt - End #endif NdisFreePacket(pNdisPacket); RCADEBUGP(RCA_LOUD, ("RCACoSendComplete: exit\n")); } PNDIS_PACKET RCAAllocCopyPacket( IN PRCA_VC pRcaVc, IN PNDIS_PACKET pNdisPacket ) /*++ Routine Description: Allocate and copy a received packet to a private packet. Note: We set the returned packet's status to NDIS_STATUS_RESOURCES, which lets us know later on that this packet belongs to us and not to the miniport. Arguments: pRcaVc - Pointer to our Adapter structure pNdisPacket - Packet to be copied. Return Value: The allocated copy of the given packet, if successful. NULL otherwise. --*/ { PUCHAR pBuffer; NDIS_STATUS Status; PNDIS_PACKET pNewPacket; PNDIS_BUFFER pNdisBuffer, pNewBuffer; ULONG TotalLength; ULONG BytesCopied; pBuffer = NULL; pNewPacket = NULL; pNewBuffer = NULL; do { // // Copy the received packet into this one. First get the // length to copy. // NdisQueryPacket(pNdisPacket, NULL, NULL, NULL, &TotalLength); RCAAllocMem(pBuffer, UCHAR, TotalLength); if (pBuffer == NULL) { break; } // // Get a new NDIS buffer. // NdisAllocateBuffer(&Status, &pNewBuffer, pRcaVc->pAdapter->RecvBufferPool, pBuffer, TotalLength); if (Status != NDIS_STATUS_SUCCESS) { break; } // // Allocate a new packet. // #if PACKET_POOL_OPTIMIZATION // RecvPPOpt - Start NdisAcquireSpinLock(&g_RecvPPOptLock); g_alRecvPPOptBuckets[g_lRecvPPOptOutstanding]++; g_lRecvPPOptOutstanding++; NdisReleaseSpinLock(&g_RecvPPOptLock); // RecvPPOpt - End #endif NdisAllocatePacket(&Status, &pNewPacket, pRcaVc->pAdapter->RecvPacketPool); if (Status != NDIS_STATUS_SUCCESS) { break; } NDIS_SET_PACKET_STATUS(pNewPacket, 0); // // Link the buffer to the packet. // NdisChainBufferAtFront(pNewPacket, pNewBuffer); //copy the packet NdisCopyFromPacketToPacket(pNewPacket, 0, // Destn offset TotalLength, pNdisPacket, 0, // Src offset &BytesCopied); RCAAssert(TotalLength == BytesCopied); NdisAdjustBufferLength(pNewBuffer, TotalLength); } while (FALSE); if (pNewPacket != (PNDIS_PACKET)NULL) { NDIS_SET_PACKET_STATUS(pNewPacket, NDIS_STATUS_RESOURCES); } else { if (pNewBuffer != NULL) { NdisFreeBuffer(pNewBuffer); } if (pBuffer != NULL) { RCAFreeMem(pBuffer); } } return(pNewPacket); } UINT RCACoReceivePacket( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE ProtocolVcContext, IN PNDIS_PACKET pNdisPacket ) /*++ Routine Description: Called by NDIS when a packet is received on a VC owned by RCA. If there's a 'streamto' device indicated in teh VC, we get an IRP, point the IR at teh MDL, and send it to the next streaming driver. If there is no 'streamto' device, since this is 'live' data we currently dump the packet. Arguments: ProtocolBindingContext - our ClientBind structure for this adapter ProtocolVcContext - our VC structure pNdisPacket - NDIS packet Return Value: Success, whether we dumped or not. --*/ { PRCA_PROTOCOL_CONTEXT pProtocolContext = &GlobalContext; PFN_RCARECEIVE_CALLBACK pfnReceiveCallback; NDIS_STATUS Status = 0; PRCA_VC pRcaVc = (PRCA_VC) ProtocolVcContext; PNDIS_PACKET pCopiedPacket, pPacket; UINT PacketRefs = 0; RCADEBUGP(RCA_INFO, ("RCACoReceivePacket: Enter\n")); do { ACQUIRE_SPIN_LOCK(&pRcaVc->SpinLock); pfnReceiveCallback = pProtocolContext->Handlers.ReceiveCallback; if (pfnReceiveCallback == NULL || pRcaVc->ClientReceiveContext == NULL) { PacketRefs = 0; RELEASE_SPIN_LOCK(&pRcaVc->SpinLock); break; } if (NDIS_GET_PACKET_STATUS (pNdisPacket) == NDIS_STATUS_RESOURCES) { // // Miniport's short on resources. Copy the packet. // RCADEBUGP(RCA_LOUD, ("RCACoReceivePacket: Miniport is low on resources, have to copy packet\n")); pCopiedPacket = RCAAllocCopyPacket(pRcaVc, pNdisPacket); PacketRefs = 0; if (pCopiedPacket == NULL) { RCADEBUGP(RCA_ERROR, ("RcaCoReceivePacket: Failed to copy packet\n")); RELEASE_SPIN_LOCK(&pRcaVc->SpinLock); break; } pPacket = pCopiedPacket; WORK_ITEM_FROM_PKT(pPacket)->bFreeThisPacket = TRUE; } else { PacketRefs = 1; pPacket = pNdisPacket; WORK_ITEM_FROM_PKT(pPacket)->bFreeThisPacket = FALSE; } pfnReceiveCallback((PVOID)pRcaVc, pRcaVc->ClientReceiveContext, pPacket); RELEASE_SPIN_LOCK(&pRcaVc->SpinLock); } while (FALSE); RCADEBUGP(RCA_INFO,("CoReceivePacket: Exit return %lu\n", PacketRefs)); return(PacketRefs); } VOID RCAReceiveComplete( IN NDIS_HANDLE ProtocolBindingContext ) { RCADEBUGP(RCA_INFO, (" RCACoReceiveComplete: Enter\n")); RCADEBUGP(RCA_INFO, (" RCACoReceiveComplete: Exit\n")); } NDIS_STATUS RCACreateVc( IN NDIS_HANDLE ProtocolAfContext, IN NDIS_HANDLE NdisVcHandle, OUT PNDIS_HANDLE pProtocolVcContext ) /*++ Routine Description: Entry point called by NDIS when the Call Manager wants to create a new endpoint (VC) for a new inbound call on our SAP. We accept the VC and do the main part of the work in our RCAIncomingCall function. Arguments: ProtocolAfContext - Actually a pointer to the client bind structure we specified on OpenAddressFamily NdisVcHandle - Handle for this VC for all future references pProtocolVcContext - Place where we (protocol) return our context for the VC Return Value: NDIS_STATUS_SUCCESS if we could create a VC NDIS_STATUS_RESOURCES otherwise --*/ { PRCA_ADAPTER pAdapter; PRCA_VC pRcaVc; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; RCADEBUGP(RCA_INFO, ("RCACreateVc - Enter\n")); pAdapter = (PRCA_ADAPTER)ProtocolAfContext; RCADEBUGP(RCA_LOUD, ("RCACreateVc: pAdapter is 0x%x\n", pAdapter)); do { // // Allocate a new VC structure. // RCAAllocMem(pRcaVc, RCA_VC, sizeof(RCA_VC)); if(pRcaVc != (PRCA_VC)NULL) { RCADEBUGP(RCA_LOUD, ("RCACreateVc: Allocated Vc 0x%x\n", pRcaVc)); // // Initialize the interesting fields. // RCAMemSet(pRcaVc, 0, sizeof(RCA_VC)); #if DBG pRcaVc->rca_sig = rca_signature; #endif pRcaVc->pAdapter = pAdapter; pRcaVc->RefCount = 1; NdisAllocateSpinLock(&pRcaVc->SpinLock); pRcaVc->NdisVcHandle = NdisVcHandle; ACQUIRE_SPIN_LOCK(&pAdapter->SpinLock); LinkDoubleAtHead(pAdapter->VcList, pRcaVc, NextVcOnAdapter, PrevVcOnAdapter); RELEASE_SPIN_LOCK(&pAdapter->SpinLock); } else { RCADEBUGP(RCA_ERROR, ("RCACreateVc: Failed to allocate VC structure, " "setting Status = NDIS_STATUS_RESOURCES\n")); Status = NDIS_STATUS_RESOURCES; break; } *pProtocolVcContext = (NDIS_HANDLE)pRcaVc; } while (FALSE); RCADEBUGP(RCA_INFO, ("RCACreateVc: Exit, returning Status 0x%x\n", Status)); return(Status); } NDIS_STATUS RCADeleteVc( IN NDIS_HANDLE ProtocolVcContext ) /*++ Routine Description: Handles requests from a call mgr to delete a VC. We need to delete the VC pointer At this time, this VC structure should be free of any calls, and we simply free it. Arguments: ProtocolVcContext - pointer to our VC structure Return Value: NDIS_STATUS_SUCCESS always --*/ { PRCA_VC pRcaVc; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PRCA_ADAPTER pAdapter; RCADEBUGP(RCA_INFO, ("RCADeleteVc: Enter\n")); pRcaVc = (PRCA_VC)ProtocolVcContext; pAdapter = pRcaVc->pAdapter; RCADEBUGP(RCA_INFO, ("RCADeleteVc: pRcaVc is 0x%x\n", pRcaVc)); ASSERT((pRcaVc->ClientReceiveContext == NULL) && (pRcaVc->ClientSendContext == NULL)); ACQUIRE_SPIN_LOCK(&pAdapter->SpinLock); pRcaVc->ClosingState |= CLOSING_DELETE_VC; RELEASE_SPIN_LOCK(&pAdapter->SpinLock); RCADereferenceVc(pRcaVc); ACQUIRE_SPIN_LOCK(&pAdapter->SpinLock); if (pAdapter->VcList == NULL) { RCADEBUGP(RCA_LOUD, ("RCADeleteVc: All VCs gone, unblocking RCADeactivateAdapter().\n")); if (pAdapter->BlockedOnClose) { RCASignal(&pAdapter->CloseBlock, Status); } } else { RCADEBUGP(RCA_INFO, ("RCADeleteVc: There are still some VCs remaining.\n")); } RELEASE_SPIN_LOCK(&pAdapter->SpinLock); return(NDIS_STATUS_SUCCESS); } NDIS_STATUS RCAIncomingCall( IN NDIS_HANDLE ProtocolSapContext, IN NDIS_HANDLE ProtocolVcContext, IN OUT PCO_CALL_PARAMETERS pCallParams ) /*++ Routine Description: This handler is called when there is an incoming call matching: - a SAP we registered on behalf of a client - our own SAP (systemwide RCA for this adapter/AF. Arguments: ProtocolSapContext - Pointer to our SAP structure ProtocolVcContext - Pointer to our RCA_VC structure pCallParameters - Call parameters Return Value: NDIS_STATUS_SUCCESS if we accept this call NDIS_STATUS_FAILURE if we reject it. --*/ { PRCA_PROTOCOL_CONTEXT pProtocolContext = &GlobalContext; PRCA_VC pRcaVc = (PRCA_VC)ProtocolVcContext; ULONG HashIndex = HASH_VC((ULONG_PTR)pRcaVc); RCADEBUGP(RCA_INFO, ("RCAIncomingCall: Adapter: 0x%x, RCAVC: 0x%x, pCallParams: 0x%x\n", pRcaVc->pAdapter, pRcaVc, pCallParams)); ACQUIRE_SPIN_LOCK(&pProtocolContext->SpinLock); RCADEBUGP(RCA_LOUD, ("RCAIncomingCall: Acquired global protocol context lock\n")); // // Stick the RCA VC in the Hash Table at the hashed position. // LinkDoubleAtHead(pProtocolContext->VcHashTable[HashIndex], pRcaVc, NextVcOnHash, PrevVcOnHash); RELEASE_SPIN_LOCK(&pProtocolContext->SpinLock); RCADEBUGP(RCA_LOUD, ("RCAIncomingCall: Release global protocol context lock\n")); // // Copy the call parameters for use later. // RtlCopyMemory(&pRcaVc->CallParameters, pCallParams, sizeof(CO_CALL_PARAMETERS)); // // Accept the call // NdisClIncomingCallComplete(NDIS_STATUS_SUCCESS, pRcaVc->NdisVcHandle, pCallParams); RCADEBUGP(RCA_INFO, ("RCAIncomingCall: Exit - Returning NDIS_STATUS_SUCCESS\n")); return(NDIS_STATUS_SUCCESS); } VOID RCAIncomingCloseCall( IN NDIS_STATUS closeStatus, IN NDIS_HANDLE ProtocolVcContext, IN PVOID CloseData OPTIONAL, IN UINT Size OPTIONAL ) { PRCA_PROTOCOL_CONTEXT pProtocolContext = &GlobalContext; PRCA_VC pRcaVc; NDIS_STATUS Status = NDIS_STATUS_PENDING; RCADEBUGP(RCA_INFO, ("RCAIncomingCloseCall: Enter\n")); // // First close the call from our perspective, then let the KS client know that the // call is closed by invoking the VC close callback. // pRcaVc = (PRCA_VC)ProtocolVcContext; Status = RCACoNdisCloseCallOnVcNoWait((PVOID)pRcaVc); // // Call this callback only if we have either a send or a receive client // context - if we don't have either, assume no-one cares that this // VC is closing. The actual callback should check for NULL contexts. // ACQUIRE_SPIN_LOCK(&pRcaVc->SpinLock); if ((pRcaVc->ClientReceiveContext || pRcaVc->ClientSendContext) && (pProtocolContext->Handlers.VcCloseCallback)) { // // Release the spin lock here because this callback will very likely // call RCACoNdisReleaseXXxVcContext() etc which will want the lock. // // There is a teensy tiny race here - if someone releases the VC context // between when we release the lock and when we call the callback, we // could be in trouble. // RELEASE_SPIN_LOCK(&pRcaVc->SpinLock); pProtocolContext->Handlers.VcCloseCallback((PVOID)pRcaVc, pRcaVc->ClientReceiveContext, pRcaVc->ClientSendContext); } else { RELEASE_SPIN_LOCK(&pRcaVc->SpinLock); } RCADEBUGP(RCA_INFO, ("RCAIncomingCloseCall: Exit\n")); } VOID RCACloseCallComplete( IN NDIS_STATUS Status, IN NDIS_HANDLE ProtocolVcContext, IN NDIS_HANDLE ProtocolPartyContext OPTIONAL ) /*++ Routine Description: This routine handles completion of a previous NdisClCloseCall. It is assumed that Status is always NDIS_STATUS_SUCCESS. Arguments: Status - Status of the Close Call. ProtocolVcContext - Pointer to VC structure. ProtocolPartyContext - Not used. Return Value: None --*/ { PRCA_PROTOCOL_CONTEXT pProtocolContext = &GlobalContext; PRCA_VC pRcaVc = (PRCA_VC)ProtocolVcContext; RCADEBUGP(RCA_INFO, ("RCACloseCallComplete: Enter\n")); ACQUIRE_SPIN_LOCK(&(pRcaVc->pAdapter->SpinLock)); pRcaVc->ClosingState |= CLOSING_CLOSE_COMPLETE; RELEASE_SPIN_LOCK(&(pRcaVc->pAdapter->SpinLock)); ACQUIRE_SPIN_LOCK(&pProtocolContext->SpinLock); RCADEBUGP(RCA_LOUD, ("RCACloseCallComplete: Acquired global protocol context lock\n")); // // Take VC out of hash table // UnlinkDouble(pRcaVc, NextVcOnHash, PrevVcOnHash); RELEASE_SPIN_LOCK(&pProtocolContext->SpinLock); RCADEBUGP(RCA_LOUD, ("RCACloseCallComplete: Released global protocol context lock\n")); RCASignal(&pRcaVc->CloseBlock, Status); RCADEBUGP(RCA_INFO, ("RCACloseCallComplete: Exit\n")); } VOID RCACallConnected( IN NDIS_HANDLE ProtocolVcContext ) /*++ Routine Description: This is the final step of an incoming call. The call on this VC is now fully set up. Arguments: ProtocolVcContext - Call Manager's VC Return Value: None --*/ { PRCA_VC pRcaVc = (PRCA_VC)ProtocolVcContext; RCADEBUGP(RCA_INFO, ("RCACallConnected: Enter\n")); ACQUIRE_SPIN_LOCK(&pRcaVc->pAdapter->SpinLock); pRcaVc = (PRCA_VC)ProtocolVcContext; pRcaVc->Flags |= VC_ACTIVE; RELEASE_SPIN_LOCK(&pRcaVc->pAdapter->SpinLock); RCADEBUGP(RCA_INFO, ("RCACallConnected: Exit\n")); } NDIS_STATUS RCARequest( IN NDIS_HANDLE ProtocolAfContext, IN NDIS_HANDLE ProtocolVcContext OPTIONAL, IN NDIS_HANDLE ProtocolPartyContext OPTIONAL, IN OUT PNDIS_REQUEST NdisRequest ) /*++ Routine Description: This routine is called by NDIS when a call manager sends us an NDIS Request. Arguments: ProtocolAfContext - Our context for the client binding ProtocolVcContext - Our context for a VC ProtocolPartyContext - Our context for a Party; RCA VC's Party list. pNdisRequest - Pointer to the NDIS Request. Return Value: NDIS_STATUS_SUCCESS --*/ { PRCA_ADAPTER pAdapter = (PRCA_ADAPTER)ProtocolAfContext; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; RCADEBUGP(RCA_INFO, ("RCARequest: Enter - pAdapter->NdisAfHandle = 0X%x\n", pAdapter->NdisAfHandle)); if (NdisRequest->DATA.QUERY_INFORMATION.Oid == OID_CO_AF_CLOSE) { RCADEBUGP(RCA_LOUD, ("RCARequest: received OID_CO_AF_CLOSE\n")); ACQUIRE_SPIN_LOCK(&pAdapter->SpinLock); if (!(pAdapter->AdapterFlags & RCA_ADAPTERFLAGS_DEACTIVATE_IN_PROGRESS)) { pAdapter->AdapterFlags |= RCA_ADAPTERFLAGS_DEACTIVATE_IN_PROGRESS; RELEASE_SPIN_LOCK(&pAdapter->SpinLock); Status = NdisScheduleWorkItem(&pAdapter->DeactivateWorkItem); if (Status != NDIS_STATUS_SUCCESS) { RCADEBUGP(RCA_ERROR, ("RCARequest: NdisScheduleWorkItem failed with status 0x%x\n", Status)); } } else { RELEASE_SPIN_LOCK(&pAdapter->SpinLock); } } RCADEBUGP(RCA_INFO, ("RCARequest: Exit - Returning NDIS_STATUS_SUCCESS\n")); return Status; }