/*++
*******************************************************************************
* 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:

   isdnlisten.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 "digiisdn.h"

STATIC ISDN_EVENT_HANDLERS ListenMsgHandlers;

NTSTATUS
IsdnStartListening(
	PISDN_ADAPTER pAdapter )
/*++

Description:

	Open stream, send listen request and startup Rx Engine to receive incoming calls.

--*/
{
	NTSTATUS Status;

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

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

	
	// Send Listen request
	if ( NT_SUCCESS( Status ) )
		Status = XnListen( pAdapter, 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), ISDN_MAX_FRAME_SIZE );

	if ( NT_ERROR( Status ) )
		IsdnStopListening( pAdapter );

	return Status;
}


VOID
IsdnStopListening(
	PISDN_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, ( "ISDN!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
IsdnListenOnXnConnect(
	IN PVOID          pOwner,
	IN ULONG          ChanIndex,
	IN ULONG          Channel,
	IN ISDN_CONN_TYPE ConnType )
/*++

Description:

	Called to process a received XN_CONNECT message which signals an incoming call.  If
	we like the bearer caps, etc., offer the call up to Tapi.

--*/
{
	PISDN_ADAPTER CONST pAdapter = pOwner;
	PISDN_CALL pCall;
	ASSERT( MmIsAddressValid( pAdapter ) );
	ASSERT( pAdapter->Type == ISDN_TYPE_ADAPTER );

	DigiDump( DIGILISTEN, ( "ISDN!ListenOnXnConnect: pAdapter 0x%x, ChanIndex 0x%x, Channel 0x%x, ConnType 0x%x\n", pOwner, ChanIndex, Channel, ConnType ) );

	// TODO: Check Bearer caps before indicating up to Tapi
	pCall = IsdnCallConstruct( pAdapter, ISDN_LINE_ANY );
	if ( pCall )
	{
		pCall->Incoming.ChanIndex = ChanIndex;
		pCall->Incoming.Channel    = Channel;
		pCall->ConnType = ConnType;

		AdapterAcquireLock( pAdapter );
		InsertTailList( &pAdapter->Listen.OfferingListHead, &pCall->OfferingListEntry );
		AdapterReleaseLock( pAdapter );
		IsdnCallOffer( pCall );
	}
	else
	{
		// Couldn't accept call.  Reject it.
		DigiDump( DIGIWARN, ( "ISDN!ListenOnXnConnect: CallConstruct failed, rejecting call!\n" ) );
		XnConnend( pAdapter, pAdapter, pAdapter->Listen.hStream, ChanIndex, Channel );
	}
}


STATIC VOID
IsdnListenOnXnConnack( 
	IN PVOID pOwner,
	IN ULONG ChanIndex,
	IN ULONG Channel )
/*++

Description:

	Listen stream should never receive an XN_CONNACK.

--*/
{
	PISDN_ADAPTER CONST pAdapter = pOwner;
	ASSERT( MmIsAddressValid( pAdapter ) );
	ASSERT( pAdapter->Type == ISDN_TYPE_ADAPTER );
	DigiDump( DIGILISTEN|DIGIERROR, ( "ISDN!ListenOnXnConnack: Error, unexpected XnConnack received on Listen stream, pAdapter 0x%x!\n", pAdapter ) );
}


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

Description:

	Listen stream should never receive an XN_CACTIVE.

--*/
{
	PISDN_ADAPTER CONST pAdapter = pOwner;
	ASSERT( MmIsAddressValid( pAdapter ) );
	ASSERT( pAdapter->Type == ISDN_TYPE_ADAPTER );
	DigiDump( DIGILISTEN|DIGIERROR, ( "ISDN!ListenOnXnCActive: Error, unexpected XnCActive received on Listen stream, pAdapter 0x%x!\n", pAdapter ) );
}


STATIC PISDN_CALL 
FindOfferedCall( 
	IN PISDN_ADAPTER pAdapter,
	IN ULONG         ChanIndex,
	IN ULONG         Channel )
/*++

Description

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

--*/
{
	PISDN_CALL pCall = NULL;
	AdapterAcquireLock( pAdapter );
	{
		PLIST_ENTRY pHead = &pAdapter->Listen.OfferingListHead;
		PLIST_ENTRY pEntry = pAdapter->Listen.OfferingListHead.Flink;
		while ( pEntry != pHead )
		{
			PISDN_CALL pc = CONTAINING_RECORD( pEntry, ISDN_CALL, OfferingListEntry );
			if ( pc->Incoming.ChanIndex == ChanIndex && pc->Incoming.Channel == Channel )
			{
				RemoveEntryList( pEntry );
				// Why call initializeListHead here?  Because, RemoveEntryList will be
				// called at close of the call (isdncall.c, case CALL_THREAD_CLOSE:) 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
IsdnListenOnXnDisconn( 
	IN PVOID pOwner,
	IN ULONG ChanIndex,
	IN ULONG Channel )
/*++

Description:

	Called to process an incoming XN_DISCONN indication.  When received on the listen
	stream, it indicates the remote has timed out or given up on us answering an 
	incoming call.  This is the last indication we'll get for this call, so handle it as
	if it were an XN_CONNEND.  Find the call on our offering list and tell Tapi it's 
	been disconnected.

--*/
{
	PISDN_ADAPTER CONST pAdapter = pOwner;
	PISDN_CALL pCall;

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

	DigiDump( DIGILISTEN, ( "ISDN!ListenOnXnDisconn: pAdapter 0x%x, ChanIndex 0x%x, Channel 0x%x\n", pAdapter, ChanIndex, Channel ) );
	pCall = FindOfferedCall( pAdapter, ChanIndex, Channel );
	if ( pCall )
		IsdnCallOnConnend( pCall, ChanIndex, Channel );
	else
	{
		DigiDump( DIGIWARN, ( "ISDN!ListenOnXnDisconn: Call not found, pAdapter 0x%x\n", pAdapter ) );
	}
}


STATIC VOID
IsdnListenOnXnConnend( 
	IN PVOID pOwner,
	IN ULONG  ChanIndex,
	IN ULONG  Channel )
/*++

Description:

	Called to process an incoming XN_CONNEND indication.  When received on the listen
	stream, it indicates the remote has timed out or given up on us answering an 
	incoming call.  Find the call on our offering list and tell Tapi it's been 
	disconnected.

--*/
{
	PISDN_ADAPTER CONST pAdapter = pOwner;
	PISDN_CALL pCall;

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

	DigiDump( DIGILISTEN, ( "ISDN!ListenOnXnConnend: pAdapter 0x%x, ChanIndex 0x%x, Channel 0x%x, CRef 0x%x\n", pAdapter, ChanIndex, Channel ) );
	pCall = FindOfferedCall( pAdapter, ChanIndex, Channel );
	if ( pCall )
		IsdnCallOnConnend( pCall, ChanIndex, Channel );
	else
	{
		DigiDump( DIGIWARN, ( "ISDN!ListenOnXnConnend: Call not found, pAdapter 0x%x\n", pAdapter ) );
	}
}


STATIC VOID
IsdnListenOnXnListen(
	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, ( "ISDN!ListenOnXnListen: XN_LISTEN received, pAdapter 0x%x\n", pOwner ) );
}


STATIC VOID
IsdnListenOnData(
	IN PVOID pOwner,
	IN PVOID pData,
	IN ULONG cbData )
/*++

Description:

	Listen stream should never get data.

--*/
{
	DigiDump( DIGILISTEN|DIGIERROR, ( "ISDN!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 ISDN_EVENT_HANDLERS ListenMsgHandlers = 
{
	IsdnListenOnXnConnect,
	IsdnListenOnXnConnack,
	IsdnListenOnXnCActive,
	IsdnListenOnXnDisconn,
	IsdnListenOnXnConnend,
	IsdnListenOnXnListen,
	IsdnListenOnData
};
