/*++ Copyright (c) 1989 Microsoft Corporation Module Name: tdihdlr.c Abstract: This module implements the NT TDI event handler routines. Revision History: Balan Sethu Raman [SethuR] 15-Feb-1995 Notes: --*/ #include "precomp.h" #pragma hdrstop #include "tdikrnl.h" #include "rxtdip.h" // // The debug trace level // #define Dbg (DEBUG_TRACE_RXCETDI) extern NTSTATUS ReceiveEventHandler( IN BOOLEAN fExpedited, IN PRXCE_ADDRESS pAddress, IN PRXCE_VC pVc, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes OUT PIRP *pIrp // TdiReceive IRP if MORE_PROCESSING_REQUIRED. ); extern NTSTATUS ReceiveEventPostProcessing( PRXCE_VC pVc, PRXCE_CONNECTION pConnection, PMDL pDataBuffer, ULONG DataBufferSize, PIRP *pIrpPointer); extern VOID ReceiveDatagramEventPostProcessing( PRXCE_ADDRESS pAddress, PMDL pDataBuffer, ULONG DataBufferSize, PIRP *pIrpPointer); NTSTATUS RxTdiConnectEventHandler( IN PVOID TdiEventContext, IN LONG RemoteAddressLength, IN PVOID RemoteAddress, IN LONG UserDataLength, IN PVOID UserData, IN LONG OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp ) /*++ Routine Description: This routine is called when a connect request has completed. The connection is fully functional when the indication occurs. Arguments: TdiEventContext - the context value passed in by the user in the Set Event Handler call RemoteAddressLength, RemoteAddress, UserDataLength, UserData, OptionsLength, Options, ConnectionId Return Value: The function value is the final status from the initialization operation. --*/ { UNREFERENCED_PARAMETER (TdiEventContext); UNREFERENCED_PARAMETER (RemoteAddressLength); UNREFERENCED_PARAMETER (RemoteAddress); UNREFERENCED_PARAMETER (UserDataLength); UNREFERENCED_PARAMETER (UserData); UNREFERENCED_PARAMETER (OptionsLength); UNREFERENCED_PARAMETER (Options); UNREFERENCED_PARAMETER (ConnectionContext); return STATUS_INSUFFICIENT_RESOURCES; // do nothing } NTSTATUS RxTdiDisconnectEventHandler( IN PVOID EventContext, IN CONNECTION_CONTEXT ConnectionContext, IN LONG DisconnectDataLength, IN PVOID DisconnectData, IN LONG DisconnectInformationLength, IN PVOID DisconnectInformation, IN ULONG DisconnectFlags ) /*++ Routine Description: This routine is used as the demultiplexing point for handling any connection disconnects for any address registered with the RxCe. Arguments: EventContext - the hAddress of the associated endpoint. ConnectionContext - the hVc associated with the connection. DisconnectIndicators - Value indicating reason for disconnection indication. Return Value: NTSTATUS - status of operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; PRXCE_VC pVc = (PRXCE_VC)ConnectionContext; PRXCE_ADDRESS pAddress = (PRXCE_ADDRESS)EventContext; PRXCE_TRANSPORT pTransport; PRXCE_CONNECTION pConnection; RxProfile(RxTdi,RxTdiDisconnectEventHandler); try { if (RxCeIsVcValid(pVc) && (pVc->pConnection->pAddress == pAddress)) { pConnection = pVc->pConnection; ASSERT(RxCeIsConnectionValid(pConnection)); if ( // There is a event handler associated with this connection (pConnection->pHandler != NULL) && // and the disconnect event handler has been specified (pConnection->pHandler->RxCeDisconnectEventHandler != NULL)) { Status = pConnection->pHandler->RxCeDisconnectEventHandler( pConnection->pContext, pVc, DisconnectDataLength, DisconnectData, DisconnectInformationLength, DisconnectInformation, DisconnectFlags); if (!NT_SUCCESS(Status)) { RxDbgTrace(0, Dbg, ("Disconnect event handler notification returned %lx\n",Status)); } } // The final action irrespective of the action taken by the handlers specified. if (DisconnectFlags & TDI_DISCONNECT_RELEASE) { // The disconnect has to be confirmed. // Status = RxTdiDisconnect( // pTransport, // pAddress, // pConnection, // pVc, // RXCE_DISCONNECT_RELEASE); } // Mark the status of the local connection to prevent any subsequent sends // on this connection. InterlockedCompareExchange( &pVc->State, RXCE_VC_DISCONNECTED, RXCE_VC_ACTIVE); } } except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_INVALID_PARAMETER; } return Status; } NTSTATUS RxTdiErrorEventHandler( IN PVOID TdiEventContext, IN NTSTATUS Status // status code indicating error type. ) /*++ Routine Description: This routine is used as the default error event handler for the transport endpoint. It is pointed to by a field in the TP_ENDPOINT structure for an endpoint when the endpoint is created, and also whenever the TdiSetEventHandler request is submitted with a NULL EventHandler field. Arguments: TransportEndpoint - Pointer to open file object. Status - Status code indicated by this event. Return Value: NTSTATUS - status of operation. --*/ { UNREFERENCED_PARAMETER (TdiEventContext); UNREFERENCED_PARAMETER (Status); return STATUS_SUCCESS; } NTSTATUS RxTdiReceiveEventHandler( IN PVOID EventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. ) /*++ Routine Description: This routine is used as the receive event handler for the transport endpoint. Arguments: EventContext - hAddresst. ConnectionContext - The client-supplied context associated with the connection on which this connection-oriented TSDU was received. ReceiveIndicators - Bitflags which indicate the circumstances surrounding this TSDU's reception. Tsdu - Pointer to an MDL chain that describes the (first) part of the (partially) received Transport Service Data Unit, less headers. IoRequestPacket - Pointer to a location where the event handler may chose to return a pointer to an I/O Request Packet (IRP) to satisfy the incoming data. If returned, this IRP must be formatted as a valid TdiReceive request, except that the ConnectionId field of the TdiRequest is ignored and is automatically filled in by the transport provider. Return Value: NTSTATUS - status of operation. --*/ { return ReceiveEventHandler( FALSE, // regular receive (PRXCE_ADDRESS)EventContext, (PRXCE_VC)ConnectionContext, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); } NTSTATUS RxTdiReceiveDatagramEventHandler( IN PVOID EventContext, // the event context IN LONG SourceAddressLength, // length of the originator of the datagram IN PVOID SourceAddress, // string describing the originator of the datagram IN LONG OptionsLength, // options for the receive IN PVOID Options, // IN ULONG ReceiveDatagramFlags, // IN ULONG BytesIndicated, // number of bytes this indication IN ULONG BytesAvailable, // number of bytes in complete Tsdu OUT ULONG *BytesTaken, // number of bytes used IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes OUT PIRP *pIrp // TdiReceive IRP if MORE_PROCESSING_REQUIRED. ) /*++ Routine Description: This routine is used as the default receive datagram event handler for the transport endpoint. It is pointed to by a field in the TP_ENDPOINT structure for an endpoint when the endpoint is created, and also whenever the TdiSetEventHandler request is submitted with a NULL EventHandler field. Arguments: EventContext - event context ( hAddress ) SourceAddress - Pointer to the network name of the source from which the datagram originated. Return Value: NTSTATUS - status of operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; PRXCE_ADDRESS pAddress = (PRXCE_ADDRESS)EventContext; PRXCE_TRANSPORT pTransport; PMDL pDataBuffer; ULONG DataBufferSize; RxProfile(RxTdi,RxCeReceiveDatagramEventHandler); ASSERT(RxCeIsAddressValid(pAddress)); // Check if we have an event handler associated with this address if ( // There is a event handler associated with this address (pAddress->pHandler != NULL) && // and the expedited receive datagram handler has been specified (pAddress->pHandler->RxCeReceiveDatagramEventHandler != NULL) ) { Status = pAddress->pHandler->RxCeReceiveDatagramEventHandler( pAddress->pContext, SourceAddressLength, SourceAddress, OptionsLength, Options, ReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, &pDataBuffer, &DataBufferSize); switch (Status) { case STATUS_SUCCESS: case STATUS_DATA_NOT_ACCEPTED: break; case STATUS_MORE_PROCESSING_REQUIRED: ReceiveDatagramEventPostProcessing( pAddress, pDataBuffer, DataBufferSize, pIrp); break; default: // log the error. break; } } else { // No handler is associated. Take the default action. Status = STATUS_DATA_NOT_ACCEPTED; } return Status; } NTSTATUS RxTdiReceiveExpeditedEventHandler( IN PVOID EventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, // IN ULONG BytesIndicated, // number of bytes in this indication IN ULONG BytesAvailable, // number of bytes in complete Tsdu OUT ULONG *BytesTaken, // number of bytes used by indication routine IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED. ) /*++ Routine Description: Arguments: EventContext - the context value passed in by the user in the Set Event Handler call Return Value: The function value is the final status from the initialization operation. --*/ { return ReceiveEventHandler( TRUE, // expedited receive (PRXCE_ADDRESS)EventContext, (PRXCE_VC)ConnectionContext, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); } NTSTATUS RxTdiSendPossibleEventHandler ( IN PVOID EventContext, IN PVOID ConnectionContext, IN ULONG BytesAvailable) /*++ Routine Description: Arguments: EventContext - the context value passed in by the user in the Set Event Handler call ConnectionContext - connection context of connection which can be sent on BytesAvailable - number of bytes which can now be sent Return Value: ignored by the transport --*/ { NTSTATUS Status = STATUS_SUCCESS; PRXCE_TRANSPORT pTransport; PRXCE_ADDRESS pAddress; PRXCE_VC pVc; PRXCE_CONNECTION pConnection; RxProfile(RxTdi,RxCeSendPossibleEventHandler); pVc = (PRXCE_VC)ConnectionContext; pConnection = pVc->pConnection; pAddress = pConnection->pAddress; pTransport = pAddress->pTransport; if (NT_SUCCESS(Status)) { // Check if we have an event handler associated with this connection. if ( // There is a event handler associated with this connection (pConnection->pHandler != NULL) && // and the expedited send possible event handler has been specified (pConnection->pHandler->RxCeSendPossibleEventHandler != NULL) ) { Status = pConnection->pHandler->RxCeSendPossibleEventHandler( pConnection->pContext, pVc, BytesAvailable); } else { // No handler is associated. Take the default action. Status = STATUS_SUCCESS; } } return Status; } NTSTATUS ReceiveEventHandler( IN BOOLEAN fExpedited, IN PRXCE_ADDRESS pAddress, IN PRXCE_VC pVc, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes OUT PIRP *pIrp // TdiReceive IRP if MORE_PROCESSING_REQUIRED. ) /*++ Routine Description: This routine is used as the receive event handler for the transport endpoint. Arguments: fExpedited - TRUE if it was a TDI_EXPEDITED_RECEIVE event EventContext - hAddress. ConnectionContext - The client-supplied context associated with the connection on which this connection-oriented TSDU was received. ReceiveIndicators - Bitflags which indicate the circumstances surrounding this TSDU's reception. Tsdu - Pointer to an MDL chain that describes the (first) part of the (partially) received Transport Service Data Unit, less headers. IoRequestPacket - Pointer to a location where the event handler may chose to return a pointer to an I/O Request Packet (IRP) to satisfy the incoming data. If returned, this IRP must be formatted as a valid TdiReceive request, except that the ConnectionId field of the TdiRequest is ignored and is automatically filled in by the transport provider. Return Value: NTSTATUS - status of operation. --*/ { NTSTATUS Status = STATUS_UNSUCCESSFUL; PMDL pDataBuffer = NULL; ULONG DataBufferSize; PRXCE_CONNECTION pConnection; RxProfile(RxTdi,ReceiveEventHandler); ASSERT(RxCeIsVcValid(pVc)); if (ReceiveFlags & TDI_RECEIVE_PARTIAL) { // Stream mode transports always set this flag. They need to be handled in a // different way. The clients of RxCe need only be notified when we have // receieved a complete TSDU. Status = STATUS_DATA_NOT_ACCEPTED; } else { pConnection = pVc->pConnection; ASSERT(RxCeIsConnectionValid(pConnection)); // Check if we have an event handler associated with this connection. if ( // There is a event handler associated with this connection (pConnection->pHandler != NULL) ) { if (fExpedited) { // Expedited receive // and the expedited receive event handler has been specified if (pConnection->pHandler->RxCeReceiveExpeditedEventHandler != NULL) { Status = pConnection->pHandler->RxCeReceiveExpeditedEventHandler( pConnection->pContext, pVc, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, &pDataBuffer, &DataBufferSize); } } else if (pConnection->pHandler->RxCeReceiveEventHandler != NULL) { Status = pConnection->pHandler->RxCeReceiveEventHandler( pConnection->pContext, pVc, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, &pDataBuffer, &DataBufferSize); } switch (Status) { case STATUS_SUCCESS: case STATUS_DATA_NOT_ACCEPTED: break; case STATUS_MORE_PROCESSING_REQUIRED: { Status = ReceiveEventPostProcessing( pVc, pConnection, pDataBuffer, DataBufferSize, pIrp); } break; default: RxDbgTrace(0, Dbg, ("Receive Event Notification returned %lx\n",Status)); break; } } } return Status; } NTSTATUS RxTdiReceiveCompletion( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID pContext) /*++ Routine Description: This routine is invoked upon completion of the reception of the desired amount of data. Arguments: pDeviceObject - the device object pIrp - the IRP pContext - the connection handle --*/ { PRXCE_VC pVc = (PRXCE_VC)pContext; if (pVc != NULL) { NTSTATUS Status; ULONG BytesCopied = (ULONG)pIrp->IoStatus.Information; PRXCE_CONNECTION pConnection = pVc->pConnection; ASSERT( RxCeIsVcValid(pVc) && RxCeIsConnectionValid(pConnection)); if (pConnection->pHandler->RxCeDataReadyEventHandler != NULL) { Status = pConnection->pHandler->RxCeDataReadyEventHandler( pConnection->pContext, pVc->pReceiveMdl, BytesCopied, pIrp->IoStatus.Status); } pVc->pReceiveMdl = NULL; } else { ASSERT(!"Valid connection handle for receive completion"); } RxCeFreeIrp(pIrp); // // Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest // will stop working on the IRP. // return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS ReceiveEventPostProcessing( PRXCE_VC pVc, PRXCE_CONNECTION pConnection, PMDL pDataMdl, ULONG DataBufferSize, PIRP *pIrpPointer) /*++ Routine Description: This routine is invoked when a recieve event notification to a connection engine client results in further requests for copying the data out of the transport drivers buffers Arguments: pDataBuffer - the buffer into which the data should be copied DataBufferSize - the size of the data pIrpPointer - the IRP to the transport driver for processing the copy request. Return Value: STATUS_MORE_PROCESSING_REQUIRED -- if successful otherwise appropriate error code --*/ { NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED; PRXCE_ADDRESS pAddress = pConnection->pAddress; PRXCE_TRANSPORT pTransport = pAddress->pTransport; PIRP pIrp; ASSERT(RxCeIsAddressValid(pAddress)); ASSERT(RxCeIsTransportValid(pTransport)); ASSERT(pConnection->pHandler->RxCeDataReadyEventHandler != NULL); pIrp = RxCeAllocateIrpWithMDL(pTransport->pDeviceObject->StackSize,FALSE,pDataMdl); *pIrpPointer = pIrp; if (pIrp != NULL) { pVc->pReceiveMdl = pDataMdl; TdiBuildReceive( pIrp, // the IRP pTransport->pDeviceObject, // the device object pVc->pEndpointFileObject, // the connection (VC) file object RxTdiReceiveCompletion, // Completion routine pVc, // completion context pDataMdl, // the data buffer 0, // receive flags DataBufferSize); // receive buffer length // // Make the next stack location current. Normally IoCallDriver would // do this, but for this IRP it has been bypassed. // IoSetNextIrpStackLocation(pIrp); } else { // An IRP for receiving the data was not allocated. Invoke the error handler // to communicate the status back. ASSERT(pConnection->pHandler->RxCeDataReadyEventHandler != NULL); if (pConnection->pHandler->RxCeDataReadyEventHandler != NULL) { Status = pConnection->pHandler->RxCeDataReadyEventHandler( pConnection->pContext, pDataMdl, 0, STATUS_INSUFFICIENT_RESOURCES); } Status = STATUS_DATA_NOT_ACCEPTED; } return Status; } NTSTATUS RxTdiReceiveDatagramCompletion( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID pContext) /*++ Routine Description: This routine is invoked upon completion of the reception of the desired amount of data. Arguments: pDeviceObject - the device object pIrp - the IRP pContext - the connection handle --*/ { NTSTATUS Status; PRXCE_ADDRESS pAddress = (PRXCE_ADDRESS)pContext; ASSERT(RxCeIsAddressValid(pAddress)); if (pAddress != NULL) { ULONG BytesCopied = (ULONG)pIrp->IoStatus.Information; if (pAddress->pHandler->RxCeDataReadyEventHandler != NULL) { Status = pAddress->pHandler->RxCeDataReadyEventHandler( pAddress->pContext, pAddress->pReceiveMdl, BytesCopied, pIrp->IoStatus.Status); } pAddress->pReceiveMdl = NULL; } else { ASSERT(!"Valid Address handle for receive datagram completion"); } RxCeFreeIrp(pIrp); // // Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest // will stop working on the IRP. // return STATUS_MORE_PROCESSING_REQUIRED; } VOID ReceiveDatagramEventPostProcessing( PRXCE_ADDRESS pAddress, PMDL pDataMdl, ULONG DataBufferSize, PIRP *pIrpPointer) /*++ Routine Description: This routine is invoked when a recieve event notification to a connection engine client results in further requests for copying the data out of the transport drivers buffers Arguments: pDataBuffer - the buffer into which the data should be copied DataBufferSize - the size of the data pIrpPointer - the IRP to the transport driver for processing the copy request. Return Value: a STATUS_SUCCESS returned from this routine only implies that an IRP for processing the request was setup correctly. --*/ { PIRP pIrp; PRXCE_TRANSPORT pTransport = pAddress->pTransport; ASSERT(RxCeIsTransportValid(pTransport)); ASSERT(pAddress->pHandler->RxCeDataReadyEventHandler != NULL); *pIrpPointer = pIrp = RxCeAllocateIrp(pTransport->pDeviceObject->StackSize,FALSE); if (pIrp != NULL) { pAddress->pReceiveMdl = pDataMdl; TdiBuildReceive( pIrp, // the IRP pTransport->pDeviceObject, // the device object pAddress->pFileObject, // the connection (VC) file object RxTdiReceiveDatagramCompletion, // Completion routine pAddress, // completion context pDataMdl, // the data buffer 0, // send flags DataBufferSize); // send buffer length // // Make the next stack location current. Normally IoCallDriver would // do this, but for this IRP it has been bypassed. // IoSetNextIrpStackLocation(pIrp); } else { // An IRP for receiving the data was not allocated. Invoke the error handler // to communicate the status back. if (pAddress->pHandler->RxCeErrorEventHandler != NULL) { pAddress->pHandler->RxCeErrorEventHandler( pAddress->pContext, STATUS_INSUFFICIENT_RESOURCES); } else { // No error handler to handle an erroneous situation. } } }