/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: Tdiout.c Abstract: This file represents the TDI interface on the bottom edge of NBT. The procedures herein conform to the TDI I/F spec. and then convert the information to NT specific Irps etc. This implementation can be changed out to run on another OS. Author: Jim Stewart (Jimst) 10-2-92 Revision History: --*/ #include // procedure headings // function prototypes for completion routines used in this file VOID SendComplete( PVOID pContext, TDI_STATUS tdistatus, UINT cbSentSize ); VOID TcpConnectComplete( PVOID pContext, TDI_STATUS tdistatus, PVOID pv ); VOID DisconnectWaitComplete( PVOID pContext, TDI_STATUS status, ULONG Extra ) ; void VxdDelayedCallHandler( struct CTEEvent *pEvent, void * pContext ) ; //---------------------------------------------------------------------------- NTSTATUS TdiSendDatagram( IN PTDI_REQUEST pRequest, IN PTDI_CONNECTION_INFORMATION pSendDgramInfo, IN ULONG SendLength, OUT PULONG pSentSize, IN tBUFFER * pSendBuffer, IN ULONG SendFlags ) /*++ Routine Description: This routine sends a datagram to the transport Arguments: pSendBuffer - this is really an Mdl in NT land. It must be tacked on the end of the Mdl created for the Nbt datagram header. Return Value: The function value is the status of the operation. --*/ { TDI_STATUS tdistatus = TDI_SUCCESS ; PTDI_SEND_CONTEXT psendCont = NULL ; if ( !GetSendContext( &psendCont ) ) { tdistatus = STATUS_INSUFFICIENT_RESOURCES ; goto ErrorExit ; } // // Save away the old completion routine and context and replace them // with new ones. The new completion routines will call the old ones // if they are non-NULL and then free the structure. // psendCont->OldRequestNotifyObject = pRequest->RequestNotifyObject ; psendCont->OldContext = pRequest->RequestContext ; psendCont->NewContext = NULL ; pRequest->RequestContext = psendCont ; // // Set the send completion callback // pRequest->RequestNotifyObject = SendComplete; InitNDISBuff( &psendCont->ndisHdr, pSendBuffer->pDgramHdr, pSendBuffer->HdrLength, &psendCont->ndisData1 ) ; InitNDISBuff( &psendCont->ndisData1, pSendBuffer->pBuffer, pSendBuffer->Length, NULL ) ; tdistatus = TdiVxdSendDatagram( pRequest, pSendDgramInfo, SendLength, pSentSize, &psendCont->ndisHdr ) ; if ( !NT_SUCCESS( tdistatus ) ) goto ErrorExit ; return tdistatus ; ErrorExit: DbgPrint("TdiSendDatagram ErrorExit: tdistatus= ") ; DbgPrintNum( tdistatus ) ; DbgPrint("\n\r") ; // // Call *our* completion routine which frees memory etc. // if ( psendCont && pRequest->RequestNotifyObject ) { ((NBT_COMPLETION)pRequest->RequestNotifyObject)( psendCont, tdistatus, 0 ) ; return( STATUS_PENDING ); } return tdistatus ; } //---------------------------------------------------------------------------- VOID SendComplete( PVOID pContext, TDI_STATUS tdistatus, UINT cbSentSize ) /*++ Routine Description: This routine handles the completion of a datagram/session send to the transport. It must call the client completion routine and free the TDI_SEND_CONTEXT structure pointed at by pContext. Note that this routine may also be called if an error is returned from the send call. This is done to localize cleanup. Arguments: pContext - Pointer to a TDI_SEND_CONTEXT tdistatus - Completion status of the TDI request cbSentSize- Bytes taken by TDI --*/ { PTDI_SEND_CONTEXT psendCont = pContext ; if ( tdistatus != TDI_SUCCESS ) { DbgPrint("SendComplete: TDI Error reported: 0x") ; DbgPrintNum( tdistatus ) ; DbgPrint("\r\n") ; } if ( psendCont ) { if ( psendCont->OldRequestNotifyObject ) { // // This calls the name server datagram completion routine which // in turn will call CTEIoComplete (VxdIoComplete) which will call // the NCB post routine (and fill out the NCB) // psendCont->OldRequestNotifyObject( psendCont->OldContext, tdistatus, cbSentSize ) ; } FreeSendContext( psendCont ) ; } } //---------------------------------------------------------------------------- NTSTATUS TdiConnect( IN PTDI_REQUEST pRequest, IN ULONG lTimeout, IN PTDI_CONNECTION_INFORMATION pSendInfo, OUT PVOID pIrp //IN PTDI_CONNECTION_INFORMATION pReturnInfo ) /*++ Routine Description: This routine sends a connect request to the tranport provider, to setup a connection to the other side... Arguments: Return Value: The function value is the status of the operation. --*/ { TDI_STATUS status ; DbgPrint("TdiConnect Entered\n\r") ; status = TdiVxdConnect( pRequest, (PVOID)lTimeout, pSendInfo, NULL ) ; // pReturnInfo) ; if ( !NT_SUCCESS( status ) ) { DbgPrint("TdiVxdConnect: Returned error " ) ; DbgPrintNum( status ) ; DbgPrint("\n\r") ; // // call the completion routine with this status // // (*((NBT_COMPLETION)pRequest->RequestNotifyObject)) ((PVOID)pRequest->RequestContext, status, 0L); return STATUS_PENDING; } else { DbgPrint("TdiVxdConnect - Connection ID: 0x") ; DbgPrintNum( (ULONG) pRequest->Handle.ConnectionContext ) ; DbgPrint("\r\n") ; } return status ; } //---------------------------------------------------------------------------- NTSTATUS TdiDisconnect( IN PTDI_REQUEST pRequest, IN PVOID lTimeout, IN ULONG Flags, IN PTDI_CONNECTION_INFORMATION pSendInfo, IN PCTE_IRP pClientIrp, IN BOOLEAN Wait ) /*++ Routine Description: This routine sends a connect request to the tranport provider, to setup a connection to the other side... Arguments: Wait is only used for NT (used in case when deleting address object with open connections, which Vxd doesn't allow due to Netbios spec). Return Value: The function value is the status of the operation. --*/ { TDI_STATUS status ; DbgPrint("TdiDisconnect Entered\n\r") ; DbgPrint("TdiDisconnect - Disconnecting Connection ID: 0x") ; DbgPrintNum( (ULONG) pRequest->Handle.ConnectionContext ) ; DbgPrint("\r\n") ; ASSERT( Flags <= 0xffff ) ; status = TdiVxdDisconnect( pRequest, lTimeout, (ushort) Flags, pSendInfo, NULL ) ; if ( !NT_SUCCESS( status ) ) { DbgPrint("TdiVxdConnect: Returned error " ) ; DbgPrintNum( status ) ; DbgPrint("\n\r") ; } return status ; } //---------------------------------------------------------------------------- NTSTATUS TdiSend( IN PTDI_REQUEST pRequest, IN USHORT sFlags, IN ULONG SendLength, OUT PULONG pSentSize, IN tBUFFER *pBuff, IN ULONG SendFlags ) /*++ Routine Description: This routine sends a packet to the transport on a TCP connection If this is a chain send (SendFlags & CHAIN_SEND_FLAG) then pBuff will point to a tBUFFERCHAINSEND (which contains a tBUFFER as its first element). Arguments: Return Value: The function value is the status of the operation. --*/ { TDI_STATUS tdistatus = TDI_SUCCESS ; PTDI_SEND_CONTEXT psendCont = NULL ; tBUFFERCHAINSEND * pSendBuff = (tBUFFERCHAINSEND*) pBuff ; PNDIS_BUFFER pndis2 = NULL ; PNDIS_BUFFER pndis1 = NULL ; DbgPrint("TdiSend Entered - sending 0x") ; DbgPrintNum( SendLength ) ; DbgPrint(" bytes\r\n") ; if ( !GetSendContext( &psendCont )) { tdistatus = STATUS_INSUFFICIENT_RESOURCES ; if ( pRequest->RequestNotifyObject ) { ((NBT_COMPLETION)pRequest->RequestNotifyObject)( pRequest->RequestContext, tdistatus, 0 ) ; } return tdistatus ; } // // Save away the old completion routine and context and replace them // with new ones. The new completion routines will call the old ones // if they are non-NULL and then free the structure. // psendCont->OldRequestNotifyObject = pRequest->RequestNotifyObject ; psendCont->OldContext = pRequest->RequestContext ; psendCont->NewContext = NULL ; pRequest->RequestContext = psendCont ; // // Set the send completion callback // pRequest->RequestNotifyObject = SendComplete ; // // Build the ndis buffer chain (Header and data) // if ( (SendFlags & CHAIN_SEND_FLAG) && pSendBuff->Length2 ) { InitNDISBuff( &psendCont->ndisData2, pSendBuff->pBuffer2, pSendBuff->Length2, NULL ) ; pndis2 = &psendCont->ndisData2 ; } if ( pSendBuff->tBuff.Length && (SendLength > pSendBuff->tBuff.HdrLength) ) { InitNDISBuff( &psendCont->ndisData1, pSendBuff->tBuff.pBuffer, pSendBuff->tBuff.Length, pndis2 ) ; pndis1 = &psendCont->ndisData1 ; } InitNDISBuff( &psendCont->ndisHdr, pSendBuff->tBuff.pDgramHdr, pSendBuff->tBuff.HdrLength, pndis1 ) ; tdistatus = TdiVxdSend( pRequest, sFlags, SendLength, &psendCont->ndisHdr ) ; if ( !NT_SUCCESS( tdistatus ) ) goto ErrorExit ; else *pSentSize = SendLength ; return tdistatus ; ErrorExit: // // Call *our* completion routine which frees memory etc. // if ( psendCont && pRequest->RequestNotifyObject ) { ((NBT_COMPLETION)pRequest->RequestNotifyObject)( psendCont, tdistatus, 0 ) ; } DbgPrint("TdiSend: returning ") ; DbgPrintNum( tdistatus ) ; DbgPrint("\n\r") ; return tdistatus ; } /******************************************************************* NAME: VxdScheduleDelayedCall SYNOPSIS: Schedules a callback at some later time ENTRY: pClientContext - Context to pass callback CallBackRoutine - Routine to call RETURNS: STATUS_PENDING if successfully scheduled NOTES: This is aliased to CTEQueueForNonDispProcessing. The memory for the DCC is freed by the application HISTORY: Johnl 2-Sep-1993 Created ********************************************************************/ NTSTATUS VxdScheduleDelayedCall( tDGRAM_SEND_TRACKING * pTracker, PVOID pClientContext, PVOID ClientCompletion, PVOID CallBackRoutine, tDEVICECONTEXT *pDeviceContext ) { CTELockHandle OldIrq; PDELAYED_CALL_CONTEXT pDCC = CTEAllocMem( sizeof( DELAYED_CALL_CONTEXT )) ; if ( !pDCC ) return STATUS_INSUFFICIENT_RESOURCES ; ASSERT( CallBackRoutine != NULL ) ; pDCC->dc_WIC.pTracker = pTracker ; pDCC->dc_WIC.pClientContext = pClientContext ; pDCC->dc_WIC.ClientCompletion = ClientCompletion ; pDCC->dc_Callback = CallBackRoutine ; pDCC->pDeviceContext = pDeviceContext; // // put this event on the deviceContext queue if we know the devicecontext // otherwise, on the nbtconfig queue. This allows us to cancel the event // later if we wish to (e.g. adapter goes away in pnp, or lease expires) // if the adapter is marked as going down, don't schedule an event but // execute it synchronously. // if (pDeviceContext) { ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); if (!pDeviceContext->fDeviceUp) { pDCC->dc_Callback( pDCC ) ; DbgPrint("VxdScheduleDelayedCall: device going down,executing now\r\n") ; return( STATUS_SUCCESS ); } else { CTESpinLock(pDeviceContext,OldIrq); InsertTailList(&pDeviceContext->DelayedEvents,&pDCC->Linkage); CTESpinFree(pDeviceContext,OldIrq); } } else { CTESpinLock(&NbtConfig,OldIrq); InsertTailList(&NbtConfig.DelayedEvents,&pDCC->Linkage); CTESpinFree(&NbtConfig,OldIrq); } CTEInitEvent( &pDCC->dc_event, VxdDelayedCallHandler ) ; CTEScheduleEvent( &pDCC->dc_event, pDCC) ; return STATUS_PENDING ; } void VxdDelayedCallHandler( struct CTEEvent *pEvent, void * pContext ) { PDELAYED_CALL_CONTEXT pDCC = pContext ; CTELockHandle OldIrq; tDEVICECONTEXT *pDeviceContext; ASSERT( pDCC != NULL && pDCC->dc_Callback != NULL ) ; pDeviceContext = pDCC->pDeviceContext; if (pDeviceContext) { ASSERT( pDeviceContext->Verify == NBT_VERIFY_DEVCONTEXT ); CTESpinLock(pDeviceContext,OldIrq); RemoveEntryList(&pDCC->Linkage); CTESpinFree(pDeviceContext,OldIrq); } else { CTESpinLock(&NbtConfig.JointLock,OldIrq); RemoveEntryList(&pDCC->Linkage); CTESpinFree(&NbtConfig.JointLock,OldIrq); } pDCC->dc_Callback( pDCC ) ; } /******************************************************************* NAME: CancelAllDelayedEvents SYNOPSIS: Since the device (or the entire vxd!) is going away, cancel all the events that were queued to be scheduled later. If a particular device context is going away (but not the entire system) then execute all those events synchronously. ENTRY: pDeviceContext - the device context that's going away NULL if the vxd is getting unloaded RETURNS: TRUE if at least one event present and was cancelled FALSE if there were no events queued. HISTORY: Koti Jan. 9, 95 ********************************************************************/ BOOL CancelAllDelayedEvents( tDEVICECONTEXT *pDeviceContext ) { LIST_ENTRY *pHead; LIST_ENTRY *pEntry; PDELAYED_CALL_CONTEXT pDCC; CTELockHandle OldIrq; BOOL fAtLeastOne=FALSE; CTESpinLock(&NbtConfig.JointLock,OldIrq); if (pDeviceContext) pHead = &pDeviceContext->DelayedEvents; else pHead = &NbtConfig.DelayedEvents; pEntry = pHead->Flink; while (pEntry != pHead) { pDCC = CONTAINING_RECORD(pEntry,DELAYED_CALL_CONTEXT,Linkage); pEntry = pEntry->Flink; RemoveEntryList(&pDCC->Linkage); CTESpinFree(&NbtConfig.JointLock,OldIrq); CTECancelEvent( &pDCC->dc_event ); // // if only one device context is going away, execute the event now // if (pDeviceContext) { pDCC->dc_Callback( pDCC ) ; } CTESpinLock(&NbtConfig.JointLock,OldIrq); fAtLeastOne = TRUE; } CTESpinFree(&NbtConfig.JointLock,OldIrq); ASSERT( IsListEmpty( pHead ) ); return( fAtLeastOne ); }