/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   X25listen.c

Abstract:

   ISDN firmware API requires a seperate channel or stream posting listens, called the 
   listen stream.  All incoming calls are indicated and handled via the listen stream.
   This module is that code.

Author:

   Michael Zarns

--*/

#include "digix25.h"

STATIC X25_EVENT_HANDLERS ListenMsgHandlers;

VOID X25StopListening(	PX25_ADAPTER pAdapter );

VOID X25CallOnDisconnectIndication( IN PVOID pOwner, PVOID pControl, PVOID pData);


NTSTATUS
X25StartListening(
	PX25_ADAPTER pAdapter )
/*++

Description:



--*/
{
	NTSTATUS Status;

	DigiDump( DIGILISTEN, ( "DigiX25!StartListening: pAdapter 0x%x\n", pAdapter ) );
	InitializeListHead( &pAdapter->Listen.OfferingListHead );

	// Open listen stream
	{
		ULONG Error;
		Status = DsOpen( pAdapter->hDevice, X25_DEVICE_NAME , pAdapter, NULL, 0, &pAdapter->Listen.hStream, &Error );
		if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIWARN, ( "X25StartListen: DsOpen failed, status 0x%x, error 0x%x\n", Status, Error ) );
		}
	}

	
	// Send Listen request 
	if ( NT_SUCCESS( Status ) )
		Status = XnListen( pAdapter, pAdapter->Listen.hStream, pAdapter->Snid );

	if ( NT_SUCCESS( Status ) )
		Status = RxEngineStart( &pAdapter->Listen.RxEngine, pAdapter->hMiniport, 
		                        pAdapter->Listen.hStream, pAdapter, &ListenMsgHandlers,
		                        pAdapter->XnDispatch.XnInterpreter, 
		                        XnCbControl(pAdapter), DEFAULT_EXTRA_DATA_SIZE);

	if ( NT_ERROR( Status ) )
		X25StopListening( pAdapter );

	return Status;
}


VOID
X25StopListening(
	PX25_ADAPTER pAdapter )
/*++

Description:

	Stops listening for incoming calls.  Stop RxEngine on Listen stream and close the
	stream.  This is called when shutting down the adapter.

--*/
{
	DigiDump( DIGILISTEN, ( "DigiX25!StopListening: pAdapter 0x%x\n", pAdapter ) );
	if ( pAdapter->Listen.hStream )
	{
		RxEngineStop( &pAdapter->Listen.RxEngine );
		(void)DsClose( pAdapter->Listen.hStream );
		pAdapter->Listen.hStream = NULL;
	}
}


STATIC VOID
X25ListenOnConnect(	
	IN PVOID          pOwner,
	IN PVOID		  pControl,
	IN PVOID			  pData )
/*++

Description:

	Called to process a received N_CI message which signals an incoming call.  

--*/
{
	PX25_ADAPTER CONST pAdapter = pOwner;
	PX25_CALL pCall;
	struct xcallf *pConnect = (PVOID)pControl;
	int i;

	ASSERT( MmIsAddressValid( pAdapter ) );
	ASSERT( pAdapter->Type == X25_TYPE_ADAPTER );

	DigiDump( DIGILISTEN, ( "DigiX25!ListenOnConnect: pAdapter 0x%x \n", pOwner ) );

	// TODO: Check Bearer caps before indicating up to Tapi
	pCall = X25CallConstruct( pAdapter, X25_LINE_ANY );
	if ( pCall )
	{

				pCall->conn_id = pConnect->conn_id;
				pCall->pAdapter = pAdapter;

               DigiDump( DIGIX25, ("   N_CI: "
                                       "   conn_id = 0x%x\n"
                                       "   CONS_call = 0x%x\n"
                                       "   negotiate_qos = 0x%x\n"
                                       "   lsap_len = 0x%x\n"
                                       "   calledaddr.sn_id = '%c' 0x%x\n",
                                       pConnect->conn_id,
                                       pConnect->CONS_call,
                                       pConnect->negotiate_qos,
                                       pConnect->calledaddr.DTE_MAC.lsap_len,
                                       pConnect->calledaddr.sn_id,
                                       pConnect->calledaddr.sn_id) );

					DigiDump( DIGIX25, ("   Calledaddr.lsap_add: ") );
               for ( i = 0; i < LSAPMAXSIZE; i++)
	               DigiDump(DIGIX25,("%x-",pConnect->calledaddr.DTE_MAC.lsap_add[i]));
			   	DigiDump( DIGIX25, ("\n") );

					DigiDump( DIGIX25, ("   Callingaddr.lsap_add: ") );
               for ( i = 0; i < LSAPMAXSIZE; i++)
	              DigiDump(DIGIX25,("%x-",pConnect->callingaddr.DTE_MAC.lsap_add[i]));
			   	DigiDump( DIGIX25, ("\n") );

               //
               // Check the Call User Data field and determine if it is
               // a connection we wish to accept.  Currently, we only
               // accept connections for PPP and PAD clients.
               //
               if( ((PUCHAR)pData)[0] == 0xCF )
               {
                  pCall->SpecialFlags |= CALL_OBJ_PPP_FRAMING;
                  pCall->CurrentMaxMtuSize = MAX_MTU_SIZE;
               }
               else if( ((PUCHAR)pData)[0] == 0x01 )
               {
                  pCall->SpecialFlags |= CALL_OBJ_PAD_CONNECTION;
                  pCall->CurrentMaxMtuSize = MAX_MTU_SIZE * 2;
               }
               else
               {
						DigiDump( DIGIERROR, 
						          ("*** Unexpected call user data 0x%x ***\n\tNEED  TO SEND A DOSCONNECT\n",
						          ((PUCHAR)pData)[0] ) );
						X25CallDestruct ( pCall );

						/*
							PARAG TODO

						DisconnectRequest( pAdapter->hListenStream, 
						                   pConnect->conn_id,
						                   ListenDiscComplete );
						*/
					   
               }
               if( pConnect->negotiate_qos )
               {
                  DigiDump( DIGIX25, ("   negotiate_qos:"
                                          "      qos.reqtclass=0x%x\n"
                                          "      qos.locthroughput=0x%x\n"
                                          "      qos.remthroughput=0x%x\n"
                                          "      qos.reqminthruput=0x%x\n"
                                          "      qos.locminthru=0x%x\n"
                                          "      qos.remminthru=0x%x\n"
                                          "      qos.reqtransitdelay=0x%x\n"
                                          "      qos.transitdelay=0x%x\n"
                                          "      qos.reqmaxtransitdelay=0x%x\n"
                                          "      qos.acceptable=0x%x\n"
                                          "      qos.reqpriority=0x%x\n"
                                          "      qos.reqprtygain=0x%x\n"
                                          "      qos.reqprtykeep=0x%x\n",
                                          pConnect->qos.reqtclass,
                                          pConnect->qos.locthroughput,
                                          pConnect->qos.remthroughput,
                                          pConnect->qos.reqminthruput,
                                          pConnect->qos.locminthru,
                                          pConnect->qos.remminthru,
                                          pConnect->qos.reqtransitdelay,
                                          pConnect->qos.transitdelay,
                                          pConnect->qos.reqmaxtransitdelay,
                                          pConnect->qos.acceptable,
                                          pConnect->qos.reqpriority,
                                          pConnect->qos.reqprtygain,
                                          pConnect->qos.reqprtykeep) );
                  DigiDump( DIGIX25, ("      qos.prtydata=0x%x\n"
                                          "      qos.prtygain=0x%x\n"
                                          "      qos.prtykeep=0x%x\n"
                                          "      qos.reqlowprtydata=0x%x\n"
                                          "      qos.reqlowprtygain=0x%x\n"
                                          "      qos.reqlowprtykeep=0x%x\n"
                                          "      qos.lowprtydata=0x%x\n"
                                          "      qos.lowprtygain=0x%x\n"
                                          "      qos.lowprtykeep=0x%x\n"
                                          "      qos.protection_type=0x%x\n"
                                          "      qos.prot_len=0x%x\n"
                                          "      qos.lowprot_len=0x%x\n"
                                          "      qos.reqexpedited=0x%x\n"
                                          "      qos.reqackservice=0x%x\n",
                                          pConnect->qos.prtydata,
                                          pConnect->qos.prtygain,
                                          pConnect->qos.prtykeep,
                                          pConnect->qos.reqlowprtydata,
                                          pConnect->qos.reqlowprtygain,
                                          pConnect->qos.reqlowprtykeep,
                                          pConnect->qos.lowprtydata,
                                          pConnect->qos.lowprtygain,
                                          pConnect->qos.lowprtykeep,
                                          pConnect->qos.protection_type,
                                          pConnect->qos.prot_len,
                                          pConnect->qos.lowprot_len,
                                          pConnect->qos.reqexpedited,
                                          pConnect->qos.reqackservice) );
			   if( pCall->SpecialFlags & CALL_OBJ_PAD_CONNECTION )
			   {
					pCall->RemoteLinkSpeed = 
					PADThroughputClassTable[pConnect->qos.remthroughput];
					pCall->LocalLinkSpeed =  PADThroughputClassTable[pConnect->qos.locthroughput];
				}
				else
				{
					// Should use configured line speed here... how to get it?
					pCall->RemoteLinkSpeed = pAdapter->LinkSpeed;
					pCall->LocalLinkSpeed = pAdapter->LinkSpeed;
				}
		}
		else
		{
			// Should use configured line speed here... how to get it?
				pCall->RemoteLinkSpeed = pAdapter->LinkSpeed;
               	pCall->LocalLinkSpeed = pAdapter->LinkSpeed;
		}
		DigiDump( DIGIX25, ("   RemoteLinkSpeed = 0x%x\n"
	                        "   LocalLinkSpeed = 0x%x\n",
	                        pCall->RemoteLinkSpeed,
	                        pCall->LocalLinkSpeed ) );

										
               //
               // Indicate a new incoming call to TAPI
               //

			pCall->htCall = 0;



		AdapterAcquireLock( pAdapter );
		InsertTailList( &pAdapter->Listen.OfferingListHead, &pCall->OfferingListEntry );
		AdapterReleaseLock( pAdapter );
		X25CallOffer( pCall );


               

	}  
	else
	{
		// Couldn't accept call.  Reject it.
		DigiDump( DIGIWARN, ( "X25!ListenOnXnConnect: CallConstruct failed, rejecting call!\n" ) );
		
	}
}





STATIC PX25_CALL 
FindOfferedCall( 
	IN PX25_ADAPTER pAdapter,
	IN ULONG         connid)

/*++

Description

	Find call on the offering list which matches the connid
	Remove it from the list and return it.

--*/
{
	PX25_CALL pCall = NULL;
	AdapterAcquireLock( pAdapter );
	{
		PLIST_ENTRY pHead = &pAdapter->Listen.OfferingListHead;
		PLIST_ENTRY pEntry = pAdapter->Listen.OfferingListHead.Flink;
		while ( pEntry != pHead )
		{
			PX25_CALL pc = CONTAINING_RECORD( pEntry, X25_CALL, OfferingListEntry );
			if ( pc->conn_id == connid )
			{
				RemoveEntryList( pEntry );
				// Why call initializeListHead here?  Because, RemoveEntryList will be
				// called at close of the call  and
				// cannot be called multiple times!  Initializing the structure to empty
				// causes any subsequent RemoveListEntry calls to do nothing harmful.
				InitializeListHead( pEntry );
				pCall = pc;
				break;
			}
		}
	}
	AdapterReleaseLock( pAdapter );
	return pCall;
}

STATIC VOID
X25ListenOnDisconn( 				
	IN PVOID pOwner,
	IN PVOID pControl,
	IN PVOID pData )
/*++

Description:

	Called to process an incoming N_DI indication.  

--*/
{
	PX25_ADAPTER CONST pAdapter = pOwner;
	PX25_CALL pCall;
	struct xdiscf *pDisconnect = (PVOID)pControl;

	ASSERT( MmIsAddressValid( pAdapter ) );
	ASSERT( pAdapter->Type == X25_TYPE_ADAPTER );

	DigiDump( DIGILISTEN, ( "X25!ListenOnDisconn: pAdapter 0x%x \n", pAdapter ) );
	pCall = FindOfferedCall( pAdapter,  pDisconnect->conn_id);

	if ( pCall )
		X25CallOnDisconnectIndication( pCall,pControl,pData );
	else
	{
		DigiDump( DIGIWARN, ( "X25!ListenOnXnConnend: Call not found, pAdapter 0x%x\n", pAdapter ) );
	}


}


STATIC VOID
X25ListenOnConnend( 
	IN PVOID pOwner,
	IN PVOID  pControl,
	IN PVOID pData )
/*++

Description:

	Called to process an incoming N_DC indication.  
--*/
{
	PX25_ADAPTER CONST pAdapter = pOwner;

	ASSERT( MmIsAddressValid( pAdapter ) );
	ASSERT( pAdapter->Type == X25_TYPE_ADAPTER );

	DigiDump( DIGILISTEN, ( "X25!ListenOnXnConnend: pAdapter 0x%x \n", pAdapter ) );
	
		
}


STATIC VOID
X25ListenOnListen(
	IN PVOID pOwner )
/*++

Description:

	Called to process an incoming XN_LISTEN indication.  This message is an acknowledge
	from the board that a previous XN_LISTEN request was received.  No action is 
	necessary.

--*/
{
	DigiDump( DIGILISTEN, ( "X25!ListenOnListen: XN_LISTEN received, pAdapter 0x%x\n", pOwner ) );
}


STATIC VOID
X25ListenOnConnectConfirm(IN PVOID pOwner,
	IN PVOID pControl,
	IN PVOID pData
	 )
/*++

Description:

	Called to process an incoming XN_LISTEN indication.  This message is an acknowledge
	from the board that a previous XN_LISTEN request was received.  No action is 
	necessary.

--*/
{
	DigiDump( DIGILISTEN, ( "X25!ListenOnConnectConfirm: N_CC received, pAdapter 0x%x\n", pOwner ) );
}


STATIC VOID
X25ListenOnData(
	IN PVOID pOwner,
	IN PVOID pControl,
	IN ULONG cbControl,
	IN PVOID pData,
	IN ULONG cbData )
/*++

Description:

	Listen stream should never get data.

--*/
{
	DigiDump( DIGILISTEN|DIGIERROR, ( "X25!ListenOnData: Error, unexpected data received on Listen stream, pAdapter 0x%x!\n", pOwner ) );
}


// Dispatch table used by RxEngine.  Calls these handlers for incoming messages.
STATIC X25_EVENT_HANDLERS ListenMsgHandlers = 
{
	X25ListenOnConnect,
	X25ListenOnConnectConfirm,
	X25ListenOnDisconn,
	X25ListenOnConnend,
	X25ListenOnListen,
	X25ListenOnData
};
