/*++
*******************************************************************************
* Copyright (c) 1999 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:

   pnpmodem.c

Abstract:

	This module provides for "plug and play modem" emulation.  Given the correct
	enumeration sequence, we spit out a PNP identification string.

	The serial modem PNP enumeration sequence is defined as follows:

		- Idle state (DTR and RTS cleared, no tx or rx data)
		- DTR Asserted
		- Delay 150-250 mS
		- RTS Asserted

	Upon receipt of the assertion of RTS, we have identified the PNP enumeration
	sequence and should then send up our PNP modem ID.

	Any changes other than specified above (e.g. data received, etc.) invalidate the
	sequence and force us to wait again for Idle state.

Author:

   Michael Zarns

--*/
#include "digilib.h"
#include "pnpmodem.h"

typedef enum 
{
	INPUT_DTR_HI,	// DTR asserted
	INPUT_DTR_LO,	// DTR dropped
	INPUT_RTS_HI,	// RTS asserted
	INPUT_RTS_LO,	// RTS dropped
	INPUT_OTHER		// Any other activity causes invalidation of the enum sequence
} INPUT;


typedef enum
{
	STATE_PNP_NONE,		// Not in enumeration sequence
	STATE_PNP_IDLE,		// Both DTR and RTS are dropped
	STATE_PNP_DTR,			// DTR has been asserted, waiting for RTS
	STATE_PNP_ENUMERATED	// Full enumeration sequence detected
} STATE;


#define TO_MILLISECONDS 10000	// Convert 1 millisecond into 100 nanosecond units
#define DELAY_MIN ( 150 * TO_MILLISECONDS )	// 150 milliseconds
#define DELAY_MAX ( 250 * TO_MILLISECONDS )	// 250 milliseconds

#define TYPE_MODEM_ENUM 0xddaa0000

typedef struct MODEM_ENUM
{
	ULONG         Type;		// TYPE_MODEM_ENUM
	STATE         State;
	BOOLEAN       DtrHi;
	BOOLEAN       RtsHi;
	LARGE_INTEGER TimeStamp;
} MODEM_ENUM, *PMODEM_ENUM;




STATIC VOID
ProcessInput(
	IN HANDLE hEnum,
	IN INPUT  Input )
{
	PMODEM_ENUM pEnum = hEnum;
	STATE NewState = STATE_PNP_NONE;
	ASSERT(pEnum->Type == TYPE_MODEM_ENUM);

	switch (Input)
	{
		case INPUT_DTR_HI:
			if (pEnum->State == STATE_PNP_IDLE)
			{
				NewState = STATE_PNP_DTR;
				KeQuerySystemTime( &pEnum->TimeStamp );
			}
				pEnum->DtrHi = TRUE;
			break;

		case INPUT_RTS_HI:
			if (pEnum->State == STATE_PNP_DTR )
			{
				LARGE_INTEGER TimeNow;
				LARGE_INTEGER TimeDiff;
				KeQuerySystemTime( &TimeNow );

				TimeDiff.QuadPart = TimeNow.QuadPart - pEnum->TimeStamp.QuadPart;
				if (TimeDiff.QuadPart > DELAY_MIN && TimeDiff.QuadPart < DELAY_MAX)
					NewState = STATE_PNP_ENUMERATED;
			}
			break;

		case INPUT_DTR_LO:
			pEnum->DtrHi = FALSE;
			if (!pEnum->RtsHi)
				NewState = STATE_PNP_IDLE;
			break;

		case INPUT_RTS_LO:
			pEnum->RtsHi = FALSE;
			if (!pEnum->DtrHi)
				NewState = STATE_PNP_IDLE;
			break;

		case INPUT_OTHER:
			// Do nothing.
			break;
	}
	pEnum->State = NewState;
}


HANDLE
PnpModemInit()
{
	PMODEM_ENUM pEnum = DigiAllocMem(NonPagedPool, sizeof *pEnum);
	if (pEnum)
	{
		RtlZeroMemory(pEnum, sizeof *pEnum);
		pEnum->Type = TYPE_MODEM_ENUM;
		pEnum->State = STATE_PNP_IDLE;
	}
	return pEnum;
	
}


BOOLEAN
PnpModemIsEnumerated( 
	HANDLE hEnum )
{
	PMODEM_ENUM pEnum = hEnum;
	ASSERT(pEnum->Type == TYPE_MODEM_ENUM);
	return pEnum->State == STATE_PNP_ENUMERATED;
}


VOID
PnpModemCleanup(
	HANDLE hEnum )
{
	PMODEM_ENUM pEnum = hEnum;
	ASSERT(pEnum->Type == TYPE_MODEM_ENUM);
	DigiFreeMem(pEnum);
}


VOID 
PnpModemDtrHi(
	IN HANDLE hEnum )
{
	ProcessInput( hEnum, INPUT_DTR_HI );
}


VOID 
PnpModemDtrLo(
	IN HANDLE hEnum )
{
	ProcessInput( hEnum, INPUT_DTR_LO );
}


VOID 
PnpModemRtsHi(
	IN HANDLE hEnum )
{
	ProcessInput( hEnum, INPUT_RTS_HI );
}


VOID 
PnpModemRtsLo(
	IN HANDLE hEnum )
{
	ProcessInput( hEnum, INPUT_RTS_LO );
}


VOID 
PnpModemOther(
	IN HANDLE hEnum )
{
	ProcessInput( hEnum, INPUT_OTHER );
}


NTSTATUS 
PnpModemGetId( 
	IN OUT PCHAR pBuf, 
	IN OUT PULONG     pcbMax )
{
	int i;
	CHAR Id[] = "\x028\x01\x24\x44\x42I0001\\\\MODEM\\DBI0001\x31\x43)";
	if ( *pcbMax <= sizeof Id )
		return STATUS_BUFFER_TOO_SMALL;
	
	for ( i = 0; i < sizeof Id; i++ )
		pBuf[i] = Id[i];
      
	*pcbMax = sizeof Id;      
   return STATUS_SUCCESS;
}
