/*****************************************************************************
* 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
*
*****************************************************************************/
// DIGICSNT.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "DIGICSNT.h"
#include "LineSelect.h"
#include "PortPage.h"
#include "dxbreg.h"
#include <winsvc.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// The following are globals specific to the currently installed OS.
DWORD gdwOSMajorVersion;
CString strAdapterRegPath;


// Still TODO:
// How are configured entries (Similar to MSSNA DLCI's) deleted
// Where does the driver/dll get copied
// How is the driver installed. How is comm server informed.


//
//	Note!
//
//		If this DLL is dynamically linked against the MFC
//		DLLs, any functions exported from this DLL which
//		call into MFC must have the AFX_MANAGE_STATE macro
//		added at the very beginning of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//

/////////////////////////////////////////////////////////////////////////////
// CDIGICSNTApp

BEGIN_MESSAGE_MAP(CDIGICSNTApp, CWinApp)
	//{{AFX_MSG_MAP(CDIGICSNTApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDIGICSNTApp construction

CDIGICSNTApp::CDIGICSNTApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
	m_pPortData = NULL;
	m_pPortPage = NULL;
}

CDIGICSNTApp::~CDIGICSNTApp()
{
	// note: we do not delete the m_pPortData pointer since its not ours
	delete m_pPortPage;
}


/////////////////////////////////////////////////////////////////////////////
// The one and only CDIGICSNTApp object

CDIGICSNTApp theApp;

#define DIGISNA			WANLINKS_SNA_NAME_TEXT
#define LINKSERVICEKEY	_T("LinkServices")
#define REGKEY_DIGISNA	_T("System\\CurrentControlSet\\Services\\") DIGISNA _T("\\") DXB_REGKEY_PARAMETERS
#define INUSE			_T("InUse")
#define LINKPATH		DIGISNA _T("\\") DXB_REGKEY_PARAMETERS _T("\\") LINKSERVICEKEY
#define DIGISNA_SERVICE	DIGISNA
#define DIGISNA_DRIVER  DIGISNA _T(".sys")
// values that are written to the PortName key
#define DIGIDXBPATH		_T("DigiDxbPath")
#define LINESPEED		_T("LineSpeed")
#define INTERNALCLOCK	_T("InternalClock")
#define CARRIER			_T("Carrier")
#define LINKSERVICEPATH	_T("LinkServicePath")
#define LINETYPE		_T("LineType")
#define SNADEVICE		_T("SnaDevice")
#define DEVICE			_T("Device")
#define IBMCSNT_FLAG	_T("IBMCSNT")
#define SNID			_T("Snid")

// NT5 Stuff
#define	REGADAPTERPATH_NT4	_T("System\\CurrentControlSet\\Services\\")
#define REGADAPTERPATH_NT5	_T("SYSTEM\\CurrentControlSet\\Control\\Class\\")
#define REGNETCARDGUID_NT5  _T("{4D36E972-E325-11CE-BFC1-08002BE10318}\\")


/*
**	Purpose:
**		Queries a value in the registry.
**
**	Arguments:
**		szRegistryKey		: Registry Key to open.
**		szRegistryValue		: Registry Value to query.
**		szRegistryData		: String Buffer to contain the registry data.
**		dSize				: DWORD variable to contain the size of the returned data.
**
**	Returns:
**		TRUE if we can administer the remote box, otherwise returns FALSE.
**		
***************************************************************************************/
static BOOL QueryRegistryValue(CString const & szRegistryKey, CString const & szRegistryValue, 
						 LPSTR szRegistryData, LPDWORD dSize)
{
	HKEY	hKey = NULL;
	BOOL	bReturnCode = FALSE;
	DWORD	dType;

	if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
									  szRegistryKey,
									  0,
									  KEY_READ,
									  &hKey))
	{
		if (ERROR_SUCCESS == RegQueryValueEx(hKey, 
										     szRegistryValue,
										     0,
											 (LPDWORD) &dType,
											 (unsigned char*) szRegistryData,
											 dSize))

			bReturnCode = TRUE;
	}

	RegCloseKey(hKey);
			
	return bReturnCode;
}


/*
**	Purpose:
**		Determine the NT version Set the Global Variables for each config
**	Arguments:
**		N/A
**	Returns:
**		N/A
***************************************************************************/

void SetOSGlobals()
{
	BOOL bSuccess = FALSE;
	OSVERSIONINFO version;
	version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	GetVersionEx(&version);
	gdwOSMajorVersion = version.dwMajorVersion;

	if (gdwOSMajorVersion <= 4)
	{
		strAdapterRegPath = REGADAPTERPATH_NT4;
	}
	else
	{
		strAdapterRegPath = REGADAPTERPATH_NT5;
	}

}

/*
**	Purpose:
**	search system for all DigiDxB/Digi570MP entries and within those entries look for
**	SNA service availability on a line. If that line is not in use, return its registry
**	path in the supplied CString
**	Arguments:
**		reference to CString which will hold line path
**	Returns:
**		TRUE if available line found
***************************************************************************/
static BOOL AvailableSNALineNT4(CString& szLinePath,int* pnInstance,int* pnLine)
{

	#define SNASERVICEPATH	_T("\\") DXB_REGKEY_PARAMETERS _T("\\") DXB_REGVAL_LINE_SERVICE_SNA
	#define MARKER			_T(':')
	#define	REGSERVICE		_T("System\\CurrentControlSet\\Services\\")
	#define MAX_STRING		255
	#define SERVICENAME		_T("ServiceName")
	#define TITLE			_T("Title")

	ASSERT(pnInstance);
	ASSERT(pnLine);
	
	HKEY hKey;
	CString szRoot(_T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"));
	if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,szRoot,0,KEY_ALL_ACCESS,&hKey))
		return FALSE;

	DWORD dwIndex = 0,dwKeySize = MAX_STRING;
	char szNetCardInstance[MAX_STRING];
	PFILETIME	lpftLastWrite = NULL;

	// cheap way of storing columns of data.
	CStringArray astrInstances,astrDisplayInstances;
	CDWordArray adwInstance;
	
	// enumerate network card instances.
	while (RegEnumKeyEx(hKey, dwIndex, szNetCardInstance, &dwKeySize,NULL,NULL,NULL, lpftLastWrite) ==
		     ERROR_SUCCESS)
	{
		char szDigiService[MAX_STRING];
		DWORD dwValSize = MAX_STRING;
		CString szNetCard(szRoot);
		szNetCard += _T("\\");
		szNetCard += szNetCardInstance;
		// read the ServiceName value and look to find DigiDxb or Digi570
		if (QueryRegistryValue(szNetCard,SERVICENAME,szDigiService,&dwValSize))
		{
			if (_strnicmp(WANLINKS_DXB_NAME_TEXT,szDigiService,strlen(WANLINKS_DXB_NAME_TEXT)) == 0 ||
				_strnicmp(WANLINKS_570MP_NAME_TEXT,szDigiService,strlen(WANLINKS_570MP_NAME_TEXT)) == 0)
			{
				char szTitle[MAX_STRING];
				dwValSize = MAX_STRING;
				QueryRegistryValue(szNetCard,TITLE,szTitle,&dwValSize);

				// we have a match. Open this instance and try to find an unused SNA port
				CString szService(REGSERVICE);
				szService += szDigiService;
				szService += SNASERVICEPATH;

				HKEY hInstKey;
				if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,szService,0,KEY_READ,&hInstKey) == ERROR_SUCCESS)
				{
					// SNAService key exists. Enum all line keys under it 
					char szLine[8];		// a sufficiently big enough buffer. All keys are 0,..9
					DWORD dwLineIndex=0;
					DWORD dwLineSize=sizeof(szLine);
					while(RegEnumKeyEx(hInstKey,dwLineIndex,szLine,&dwLineSize,
						NULL,NULL,NULL,lpftLastWrite) == ERROR_SUCCESS)
					{
						CString szLinePath(szService);
						szLinePath += _T("\\");
						szLinePath += szLine;
						DWORD nInUse=0;
						DWORD dwSize=sizeof(nInUse);
						QueryRegistryValue(szLinePath,INUSE,(char*)&nInUse,&dwSize);
						// if our line is not marked in use
						if (nInUse==0)
						{
							// store the registry instance path in an array
							CString szInstancePath(szDigiService);
							szInstancePath += SNASERVICEPATH;
							szInstancePath += _T("\\");
							szInstancePath += szLine;
							astrInstances.Add(szInstancePath);

							int linenumber = atoi(szLine);
							
							// build a display string. Remember linenumbers are 0 based
							// this is what the user sees
							CString szDisplayInstance;
							szDisplayInstance.Format("%s - Line %d",szTitle,linenumber+1);
							astrDisplayInstances.Add(szDisplayInstance);
							// extract instance & Line number and store in dword array
							// instance is HIWORD, line# is LOWORD
							DWORD dwParam = ((DWORD)atoi(szNetCardInstance))<<16;
							dwParam |= (DWORD)linenumber;
							adwInstance.Add(dwParam);
							
						}
						++dwLineIndex;
						dwLineSize=sizeof(szLine);
					}
					RegCloseKey(hInstKey);
				}
			}// strnicmp 
			
		}// ServiceName query
		++dwIndex;
		dwKeySize = MAX_STRING;
	}// while
	RegCloseKey(hKey);

	// if no instances were found, we have an empty array
	// otherwise prompt user with a list of all available instances
	BOOL bFound = FALSE;
	int nIndex = 0;
	if (astrInstances.GetUpperBound() >= 0)
	{
		CLineSelect dlg;
		dlg.SetDisplayList(&astrDisplayInstances);
		if (dlg.DoModal() == IDOK)
		{
			nIndex = dlg.GetSelection();
			// get the instance whose SNA line is being used
			szLinePath = astrInstances[nIndex];
			DWORD dwParam = adwInstance[nIndex];
			*pnInstance = HIWORD(dwParam);
			*pnLine = LOWORD(dwParam);
			bFound = TRUE;
		}
	}
	else
	{
		CString str;
		str.LoadString(IDS_NOSNA_LINE);
		AfxMessageBox(str);
	}
	return bFound;
	
}


/*
**	Purpose:
**	search system for all DigiDxB/Digi570MP entries and within those entries look for
**	SNA service availability on a line. If that line is not in use, return its registry
**	path in the supplied CString
**	Arguments:
**		reference to CString which will hold line path
**	Returns:
**		TRUE if available line found
***************************************************************************/
static BOOL AvailableSNALineNT5(CString& szLinePath,int* pnInstance,int* pnLine)
{

	ASSERT(pnInstance);
	ASSERT(pnLine);

	// cheap way of storing columns of data.
	CStringArray astrInstances,astrDisplayInstances;
	CDWordArray adwInstance;

	#define SNASERVICEPATH_NT5 _T("SNAService")

	// cheap way of storing columns of data.
	CString strNetCardClass(REGADAPTERPATH_NT5);
	strNetCardClass += REGNETCARDGUID_NT5;

	TRACE("bAvailableSNALineNT5: strNetCardClass = %s\n", strNetCardClass);

	HKEY hkNetCards;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strNetCardClass, 0, KEY_ALL_ACCESS, &hkNetCards) == ERROR_SUCCESS)
	{
		DWORD dwValSize = MAX_STRING;
		DWORD dwIndex = 0;
		DWORD dwSize;
		TCHAR KeyName[MAX_PATH];
		FILETIME time;

		// enumerate all keys and look for the Digi specific signature
		while(dwSize = sizeof KeyName, RegEnumKeyEx(hkNetCards, dwIndex++, KeyName, &dwSize, NULL, NULL, NULL, &time) == ERROR_SUCCESS)
		{
			HKEY hkParams;
			CString strKey(strAdapterRegPath);
			strKey += REGNETCARDGUID_NT5;
			strKey += KeyName;
			strKey += _T('\\');
			strKey += DXB_REGKEY_PARAMETERS;
			
			// open key\Parameter registry key
			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKey, 0 ,KEY_ALL_ACCESS, &hkParams) == ERROR_SUCCESS)
			{
				// Get the Digi-specific Option value, defining the adapter type
				CString strOption;
				char szOption[MAX_STRING];
				dwValSize = MAX_STRING;
				QueryRegistryValue((char*)(LPCTSTR)strKey,DXB_REGVAL_OPTION,szOption,&dwValSize);
				strOption = szOption;

				// Finally!  Is this a supported adapter?
				if (strOption == DXB_REGVAL_OPTION_ENUM_CLYDE ||
					strOption == DXB_REGVAL_OPTION_ENUM_SYNC570)
				{
					char szTitle[MAX_STRING];
					dwValSize = MAX_STRING;
					QueryRegistryValue((char*)(LPCTSTR)strKey,"ServiceName",szTitle,&dwValSize);

					// we have a match. Open this instance and try to find an unused SNA port
					CString strService(SNASERVICEPATH_NT5);

					HKEY hInstKey;
					if (RegOpenKeyEx(hkParams,strService,0,KEY_READ,&hInstKey) == ERROR_SUCCESS)
					{

						// SNA key exists. Enum all line keys under it 
						PFILETIME	lpftLastWrite = NULL;
						char szLine[8];
						DWORD dwLineIndex=0;
						DWORD dwLineSize=sizeof(szLine);
						while(RegEnumKeyEx(hInstKey,dwLineIndex,szLine,&dwLineSize,
							NULL,NULL,NULL,lpftLastWrite) == ERROR_SUCCESS)
						{

							CString strTemp(strKey);
							strTemp += _T("\\");
							strTemp += strService;
							strTemp += _T("\\");
							strTemp += szLine;
							DWORD nInUse=0;
							DWORD nSnid=0;
							DWORD dwSize=sizeof(nInUse);
							QueryRegistryValue((char*)(LPCTSTR)strTemp,INUSE,(char*)&nInUse,&dwSize);
							dwSize=sizeof(nSnid);
							QueryRegistryValue((char*)(LPCTSTR)strTemp,SNID,(char*)&nSnid,&dwSize);
							if (!nInUse && nSnid)
							{
								strTemp = REGNETCARDGUID_NT5;
								strTemp += KeyName;
								strTemp += _T('\\');
								strTemp += DXB_REGKEY_PARAMETERS;
								strTemp += _T("\\");
								strTemp += strService;
								strTemp += _T("\\");
								strTemp += szLine;
								astrInstances.Add(strTemp);
								int linenumber = atoi(szLine);
								strTemp.Format("%s - Line %d",szTitle,linenumber+1);
								astrDisplayInstances.Add(strTemp);
								// extract instance & Line number and store in dword array
								// instance is HIWORD, line# is LOWORD
								DWORD dwParam = ((DWORD)atoi(KeyName))<<16;
								dwParam |= (DWORD)linenumber;
								adwInstance.Add(dwParam);
							}
							++dwLineIndex;
							dwLineSize=sizeof(szLine);
						}
						RegCloseKey(hInstKey);
					}
				}
				RegCloseKey(hkParams);
			}
		}
		RegCloseKey(hkNetCards);
	}
	else
	{
		return FALSE;
	}

	// if no instances were found, we have an empty array
	// otherwise prompt user with a list of all available instances
	BOOL bFound = FALSE;
	int nIndex = 0;
	if (astrInstances.GetUpperBound() >= 0)
	{
		CLineSelect dlg;
		dlg.SetDisplayList(&astrDisplayInstances);
		if (dlg.DoModal() == IDOK)
		{
			nIndex = dlg.GetSelection();
			// get the instance whose SNA line is being used
			szLinePath = astrInstances[nIndex];
			DWORD dwParam = adwInstance[nIndex];
			*pnInstance = HIWORD(dwParam);
			*pnLine = LOWORD(dwParam);
			bFound = TRUE;
		}
	}
	else
	{
		CString str;
		str.LoadString(IDS_NOSNA_LINE);
		AfxMessageBox(str);
	}
	return bFound;
	
}

// startup the DigiSna driver

static BOOL StartDriver(void)
{
   BOOLEAN fSuccess = FALSE;

   SC_HANDLE hScManager = NULL;
   SC_HANDLE hService = NULL;

   hScManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (hScManager == NULL)
   {
      TRACE("OpenSCManager failed, GetLastError 0x%x\n", GetLastError());
      goto Exit;
   }
   
   hService = OpenService(hScManager, DIGISNA_SERVICE, SERVICE_ALL_ACCESS);
   if (hService == NULL)
   {
      TRACE("OpenService failed, GetLastError 0x%x\n", GetLastError());
      goto Exit;
   }

   if (!StartService(hService, 0, NULL))
   {
      TRACE("StartService failed, GetLastError 0x%x\n", GetLastError());
      goto Exit;
   }

   fSuccess = TRUE;

Exit:
   if (hScManager != NULL)
      CloseServiceHandle(hScManager);

   if (hService != NULL)
      CloseServiceHandle(hService);

   return fSuccess;
}


// stop the digisna driver
static void StopDriver(void)
{
   SC_HANDLE hScManager = NULL;
   SC_HANDLE hService = NULL;
   SERVICE_STATUS Status = {0};

   hScManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (hScManager == NULL)
   {
      TRACE("OpenSCManager failed, GetLastError 0x%x\n", GetLastError());
      goto Exit;
   }
   
   hService = OpenService(hScManager, DIGISNA_SERVICE, SERVICE_ALL_ACCESS);
   if (hService == NULL)
   {
      TRACE("OpenService failed, GetLastError 0x%x\n", GetLastError());
      goto Exit;
   }

   if (!ControlService(hService, SERVICE_CONTROL_STOP, &Status))
   {
      TRACE("ControlService failed, GetLastError 0x%x\n", GetLastError());
      goto Exit;
   }

Exit:
   if (hScManager != NULL)
      CloseServiceHandle(hScManager);

   if (hService != NULL)
      CloseServiceHandle(hService);

   return;
}




/*
**	Purpose:
**		Creating a Service.
**
**	Arguments:
**		sComputerName		: String pointer containing the name of the computer.
**		sServiceName 		: String pointer containing the name of the service.
**		sServicePath 		: Path to the service binary.
**		sServiceDependencies 	: Pointer to a list of null terminated strings + null
**		dServiceLoadType 	: Load type for the service (System Start, Auto Start ...)
**
**	Returns:
**		TRUE if service is created successfully, FALSE if the service could not be created.
**		
***************************************************************************/
static BOOL CreateService(LPSTR szComputerName, LPSTR szServiceName, LPSTR szServicePath, 
					LPSTR szServiceDependencies, DWORD dServiceType, DWORD dServiceLoadType)
{
	SC_HANDLE hSCManager;
	SC_HANDLE hNewService = NULL;
	SC_HANDLE hService;
	SC_LOCK   lSCLock;
	BOOL      fStatus = TRUE;
	DWORD	  dReturnCode = 0;

	hSCManager = NULL;
	hService   = NULL;
	lSCLock    = NULL;

	/*
	 * Open the Service Control Manager
	 */
	hSCManager = OpenSCManager( szComputerName, NULL, SC_MANAGER_ALL_ACCESS );
	if( hSCManager == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	/*
	 * Lock the active database for our exclusive use
	 */
	lSCLock = LockServiceDatabase( hSCManager );
	if( lSCLock == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	hNewService = CreateService(hSCManager,	
				    			szServiceName,
				    			szServiceName,
				    			SERVICE_ALL_ACCESS,
				    			dServiceType,
				    			dServiceLoadType,
				    			SERVICE_ERROR_NORMAL,
				    			szServicePath,
				    			NULL,
				    			NULL,
				    			szServiceDependencies,
								NULL,
								NULL);

	if (NULL == hNewService)
	{
		dReturnCode = GetLastError();
		if (dReturnCode != ERROR_SERVICE_EXISTS)
			fStatus = FALSE;
	}

QueryCleanup:

	if( lSCLock != NULL )    UnlockServiceDatabase( lSCLock );
	if( hSCManager != NULL ) CloseServiceHandle( hSCManager );
	if( hNewService != NULL )   CloseServiceHandle( hNewService );

	return fStatus;
}




// Install the SNA driver service. If the service already exists, then
// return a handle to the Parameters\LinkServices key. Callers of this routine must
// issue a RegCloseKey() on the returned handle.
static BOOL InstallSNADriver(HKEY* phKey)
{

	CString strDriverPath(REGKEY_DIGISNA);
	strDriverPath += _T("\\");
	strDriverPath += LINKSERVICEKEY;
	*phKey = (HKEY)INVALID_HANDLE_VALUE;

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,strDriverPath,0,KEY_ALL_ACCESS,phKey) == ERROR_SUCCESS)
	{
		return TRUE;
	}

	// get the computer name
	char szComputerName[MAX_COMPUTERNAME_LENGTH+1];
	DWORD dwSize = sizeof(szComputerName);
	if (!GetComputerName(szComputerName,&dwSize))
		return FALSE;
	// build path to the driver image
	TCHAR szSysdir[MAX_PATH];
	if (!GetSystemDirectory(szSysdir,sizeof(szSysdir)))
		return FALSE;
    
	strDriverPath = szSysdir;
	strDriverPath += _T("\\");
	strDriverPath += DIGISNA_DRIVER;

	//
	// Create the Digi SNA Miniport Device Driver
	//
	if (!CreateService(szComputerName, DIGISNA_SERVICE, (LPSTR)(LPCTSTR)strDriverPath,
				  "", SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START))
		return FALSE;

	HKEY hKey;
	strDriverPath = REGKEY_DIGISNA;
	DWORD dwDisposition=0;
	if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,strDriverPath,0,NULL,
		REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwDisposition) != ERROR_SUCCESS)
		return FALSE;

	LONG lResult = RegCreateKeyEx(hKey,LINKSERVICEKEY,0,NULL,REG_OPTION_NON_VOLATILE,
		KEY_ALL_ACCESS,NULL,phKey,&dwDisposition);
	RegCloseKey(hKey);

	if (lResult)
		return FALSE;
	else
		return TRUE;
	

}


// Entry Point 1: Called by CommServer when adding a Port
// if any ports are available, let the user select one of those ports.
// 
int FAR PASCAL IHVShallowPortEntry(CPropertySheet *ps, PS_PORT_SHALLOW_DATA *pPortData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

	ASSERT(ps);
	ASSERT(pPortData);
	theApp.m_pPortData = pPortData;
	// create our property page
	CPortPage* pPortPage = theApp.m_pPortPage;
	if (theApp.m_pPortPage == NULL)
	{
		pPortPage = new CPortPage;
		ASSERT(pPortPage);
		theApp.m_pPortPage = pPortPage;
	}

	// Set the Global Global Variables
	SetOSGlobals();
   
        // If new port configuration, set defaults
	if (pPortData->port_name.IsEmpty()) {
        pPortData->constant_RTS      = TRUE;		  // card supports constant_RTS or switched RTS, set on OEM panel for select/unselect
		pPortData->portSpecLength  = 0;               // OEM port spec. data length

		theApp.m_OemData.nLineSpeed = DEFAULT_LINESPEED;
		theApp.m_OemData.bInternalClocking = FALSE;	  // default to external

		// for a new port, ask the user which adapter instance/line pair
		// they will be running on.
		CString szSnaLinePath;
		int nInstance=0,nLine=0;
		BOOL bSuccess = FALSE;

		if (gdwOSMajorVersion <= 4)
			bSuccess = AvailableSNALineNT4(szSnaLinePath,&nInstance,&nLine);
		else
			bSuccess = AvailableSNALineNT5(szSnaLinePath,&nInstance,&nLine);

		TRACE("IHVShallowPortEntry: bSuccess = %d, nInstance = %d, szSnaLinePath = %s, nLine=%d\n",
										bSuccess, nInstance, szSnaLinePath, nLine);

		if (bSuccess)
		{
			// we have the path to the service instance/line pair on which SNA is installed
			// of the form DigiDxb7\Parameters\SNAService\0 
			int nLength = szSnaLinePath.GetLength();
			ASSERT(nLength);
			ASSERT(nLength <= PATH_LENGTH);
			ASSERT(nInstance != 0);
			if (!nLength || !nInstance)
				return FALSE;
			strcpy(theApp.m_OemData.szSNALinePath,szSnaLinePath);
			// also build the PortName out of adapter instance & line #
			// this is of the form PXXXX_YY where XXXX is adapter instance
			// and YY is line number. This is what the user initially sees
			//
			// In NT5 we use the name already set in the registry
			if (gdwOSMajorVersion <= 4)
			{
				pPortData->port_name.Format(_T("P%4.4d%2.2d"),nInstance,nLine);
			}
			else
			{
				HKEY hKey;
				CString strDxbPath(strAdapterRegPath);
				strDxbPath += theApp.m_OemData.szSNALinePath;

				TRACE("IHVShallowPortEntry: strDxbPath = %s\n",strDxbPath);

				if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,strDxbPath,0,KEY_ALL_ACCESS,&hKey) ==
					ERROR_SUCCESS)
				{
					CString strSnaDevice;
					char szSnaDevice[MAX_STRING];
					DWORD dwValSize = MAX_STRING;
					QueryRegistryValue((char*)(LPCTSTR)strDxbPath,DXB_REGVAL_SNA_DEVICE,szSnaDevice,&dwValSize);

					TRACE("IHVShallowPortEntry: szSnaDevice = %s\n",szSnaDevice);

					TRACE("IHVShallowPortEntry: Port Name = %s\n", pPortData->port_name);

					pPortData->port_name.Format("%s",szSnaDevice);
					RegCloseKey(hKey);
				}
			}

			TRACE("IHVShallowPortEntry: Port Name = %s\n", pPortData->port_name);
			
		}
		else
			return FALSE;
	}
	else
	{	

		// data coming back from IBM is bogus. Until they can figure out what's going on
		// reread the registry and get our data using that approach.
		// to understand the registry layout, read the comments for IHVShallowPortExit
		CString strDriverPath(REGKEY_DIGISNA);
		strDriverPath += _T('\\');
		strDriverPath += LINKSERVICEKEY;
		strDriverPath += _T('\\');
		strDriverPath += pPortData->port_name;
		DWORD dwSize=0;
		memset(&theApp.m_OemData,0,sizeof(OEM_PORT_SPECIFIC));
		dwSize = sizeof(theApp.m_OemData.nLineSpeed);
		if (!QueryRegistryValue(strDriverPath,LINESPEED,(LPSTR)&theApp.m_OemData.nLineSpeed,&dwSize))
			return FALSE;
		dwSize = sizeof(theApp.m_OemData.bInternalClocking);
		if (!QueryRegistryValue(strDriverPath,INTERNALCLOCK,(LPSTR)&theApp.m_OemData.bInternalClocking,&dwSize))
			return FALSE;
		dwSize = sizeof(theApp.m_OemData.szSNALinePath);
		if (!QueryRegistryValue(strDriverPath,DIGIDXBPATH,theApp.m_OemData.szSNALinePath,&dwSize))
			return FALSE;
		
			

//		memcpy(&theApp.m_OemData,&pPortData->portSpecData[0],sizeof(OEM_PORT_SPECIFIC));
	}
    // set card capabilities
	pPortData->adapterType       = TRUE;             // card supports synchronous vs asynchronous, TRUE=sync
	pPortData->connection_type   = eboth;            // card supports leased, switched or both network connection type 
	pPortData->capability_type   = ehalf_full_duplex;     // Card supports two-way-simultaneous,two-way-alternating or both
	pPortData->encoding          = TRUE;			  // Card supports encoding, NRZI and non NRZI, TRUE -support	
	pPortData->dumb_card_support = TRUE;			  // Shallow vs Dumb card, TRUE=Dumb card

	// set data to display on page. 
	pPortPage->m_szPortName = pPortData->port_name;
	pPortPage->m_bConstantRTS = pPortData->constant_RTS;
	pPortPage->m_nLineSpeed = theApp.m_OemData.nLineSpeed;
	pPortPage->m_bInternalClocking = theApp.m_OemData.bInternalClocking;

    // Finally add to the property sheet    
    ps->AddPage(pPortPage);
    return TRUE;
}

// Entry Point 2: called when the user presses OK or APPLY on the property sheet
// install DigiSna service if it already doesn't exist. 
// Create registry structure under servicename for DigiSna.sys to read data
// 
// The following structure gets created when the driver is installed initially
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\DigiSna\Parameters\LinkServices
// For each new port (that corresponds to an SNA line) we add a key under LinkServices
// The name of the key is formatted as P%4.4d%2.2d. where the 1st arg is adapter instance
// and 2nd arg is line #.  i..e P000701 - adapter 7, line 2 (remember its 0 based)
// under this key config data is written. DigiSna will startup and scan all keys
// under the linkServices key.  The name of each key will become the exported device name
// CSNT uses that name to communicate with the driver
int FAR PASCAL IHVShallowPortExit()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
	// remember, m_pPortPage was cached during IHVShallowEntry. 
	// extract values from the page
	theApp.m_pPortData->constant_RTS = theApp.m_pPortPage->m_bConstantRTS;
	theApp.m_OemData.nLineSpeed = theApp.m_pPortPage->m_nLineSpeed;
	theApp.m_OemData.bInternalClocking = theApp.m_pPortPage->m_bInternalClocking;

	delete theApp.m_pPortPage;
	theApp.m_pPortPage = NULL;

	
	HKEY hKey;
	// Install the DIGISNA service and get a key to DigiSna\Parameters\LinkServices.
	if (!InstallSNADriver(&hKey))
		return FALSE;
	// Create a key of the form <PortName>
	HKEY hLSKey;
	CString strPortName(theApp.m_pPortData->port_name);
	DWORD dwDisposition;
	if (RegCreateKeyEx(hKey,strPortName,0,NULL,REG_OPTION_NON_VOLATILE,
						KEY_ALL_ACCESS,NULL,&hLSKey,&dwDisposition) != ERROR_SUCCESS)
	{
		RegCloseKey(hKey);
		return FALSE;
	}
	// write the DeviceName which DigiSna.sys will expose
	RegSetValueEx(hLSKey,DEVICE,0,REG_SZ,
		(BYTE*)(LPCTSTR)strPortName,strPortName.GetLength()+1);
	// tie to the base level driver (DigiDxb or Digi570MP).
	// SNA driver knows which line this port is defined upon
	CString strTemp(theApp.m_OemData.szSNALinePath);

	RegSetValueEx(hLSKey,DIGIDXBPATH,0,REG_SZ,
		(BYTE*)(LPCTSTR)strTemp,strTemp.GetLength()+1);
	// point back to ourselves. This is done because the DigiSna driver
	// expects to read data in the location pointed by LinkServicePath
	// This was done to facilitate Microsoft's SNA server. We are simply
	// emulating that model.
	strTemp = LINKPATH;
	strTemp += _T("\\");
	strTemp += strPortName;
	RegSetValueEx(hLSKey,LINKSERVICEPATH,0,REG_SZ,
		(BYTE*)(LPCTSTR)strTemp,strTemp.GetLength()+1);
	// write the LineSpeed
	RegSetValueEx(hLSKey,LINESPEED,0,REG_DWORD,
		(BYTE*)&theApp.m_OemData.nLineSpeed,sizeof(DWORD));
	// write the clocking
	RegSetValueEx(hLSKey,INTERNALCLOCK,0,REG_DWORD,
		(BYTE*)&theApp.m_OemData.bInternalClocking,sizeof(DWORD));

	// duplicate the line type value. Not really needed for CSNT but used by
	// SNA driver for MSSNA
	DWORD dwTemp=1;
	RegSetValueEx(hLSKey,LINETYPE,0,REG_DWORD,(BYTE*)&dwTemp,sizeof(DWORD));
	dwTemp = theApp.m_pPortData->constant_RTS;
	RegSetValueEx(hLSKey,CARRIER,0,REG_DWORD,(BYTE*)&dwTemp,sizeof(DWORD));

	// write a signature that informs driver that this is the IBM CommServer
	dwTemp = 1;
	RegSetValueEx(hLSKey,IBMCSNT_FLAG,0,REG_DWORD,(BYTE*)&dwTemp,sizeof(DWORD));

	RegCloseKey(hLSKey);
	RegCloseKey(hKey);

	// Update DXB 
	// mark the line inuse to avoid using it for another SNA port
	CString strDxbPath(strAdapterRegPath);
	strDxbPath += theApp.m_OemData.szSNALinePath;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,strDxbPath,0,KEY_ALL_ACCESS,&hKey) ==
		ERROR_SUCCESS)
	{
		DWORD value = 1;
		RegSetValueEx(hKey,INUSE,0,REG_DWORD,(BYTE*)&value,sizeof(value));
		RegCloseKey(hKey);
	}

	// copy the port specific oem structure into port spec. data
	theApp.m_pPortData->portSpecLength = sizeof(OEM_PORT_SPECIFIC);
    memcpy(&theApp.m_pPortData->portSpecData[0], &theApp.m_OemData, sizeof(OEM_PORT_SPECIFIC));

	// restart the digisna driver
	// In W2K everything is read on the device open.  PnP exposes the device.
	if (gdwOSMajorVersion <= 4)
	{
		StopDriver();
		StartDriver();
	}

    return (TRUE);
}




