/*
*******************************************************************************
* Copyright (c) 1996-97 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 copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1996-97                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <crtdbg.h>

#include "detect.h"
#include "detectisa.h"

typedef LONG NTSTATUS; 
#include <ntddnetd.h>   // from DDK\src\network\detect directory

#include "resource.h"


typedef struct
{
	UINT uiValue;
	BOOL fUsed;
}
RESOURCE;

static BOOL GetNextListValue(char const ** const ppszValue, UINT * puiValue)
{
	BOOL fSuccess = FALSE;
	char const szDigits[] = "0123456789";
	char const * pszValue = strpbrk(*ppszValue, szDigits);

	if (pszValue)
	{
		*puiValue = strtoul(pszValue, (char **) ppszValue, 10);
		fSuccess = TRUE;
	}
	
	return fSuccess;
}

static void FlagValueUsed(UINT const uiValue, RESOURCE * const pResources, int const cResources)
{
	int i;

	for (i = 0; i < cResources; i++)
		if (pResources[i].uiValue == uiValue)
			pResources[i].fUsed = TRUE;
}

static void FlagUsedResources(char const * const pszUsed, RESOURCE * const pResources, int const cResources)
{
	char const * pszValue = pszUsed;
	UINT uiValue;

	while (GetNextListValue(&pszValue, &uiValue))
		FlagValueUsed(uiValue, pResources, cResources);
}

static BOOL StartNetDetectDriver(void)
{
	BOOL Success = FALSE;

	SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCManager != NULL)
	{
		SC_HANDLE hService = OpenService(hSCManager, TEXT("NetDetect"), SERVICE_START);
		if (hService != NULL)
		{
			Success = StartService(hService, 0, NULL);
			if (!Success)
				Success = (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING);

			CloseServiceHandle(hService);
		}

		CloseServiceHandle(hSCManager);
	}

	return Success;
}

static BOOL FindMemBase(INTERFACE_TYPE BusType, ULONG BusNumber, char const * const pszUsed, UINT * puiMemBase, BOOL * const pfSharedMemory)
{
	BOOL fSuccess = FALSE;
	int i;
	RESOURCE MemAddressList[] =
	{
		{ 0xD0000, FALSE },
		{ 0xD8000, FALSE },
		{ 0xE0000, FALSE },
		{ 0xE8000, FALSE },
		{ 0xC8000, FALSE },
		{ 0xC0000, FALSE }
	};
	int const cMemAddresses = ElementCount(MemAddressList);
	int const cbMemoryWindow = KBytesToBytes(32);

	FlagUsedResources(pszUsed, MemAddressList, cMemAddresses);

	for (i = 0; !fSuccess && i < cMemAddresses; i++)
	{
		RESOURCE const * const pMemAddress = &MemAddressList[i];

		if (!pMemAddress->fUsed)
		{
			UINT uiMemAddress = pMemAddress->uiValue;

			if (DetectCheckMemoryUsage(BusType, BusNumber, uiMemAddress, cbMemoryWindow) == ERROR_SUCCESS)
			{
				*puiMemBase = uiMemAddress;
				fSuccess = TRUE;
			}
		}
	}

	if (fSuccess)
	{
		*pfSharedMemory = FALSE;
	}
	else
	{
		char const * pszTemp = pszUsed;

		if (GetNextListValue(&pszTemp, puiMemBase))
		{
			/* Use first item of used memory as shared */
			*pfSharedMemory = TRUE;
			fSuccess = TRUE;
		}
	}

	return fSuccess;
}

static BOOL FindIOBase(INTERFACE_TYPE const BusType, DWORD const BusNumber, DWORD const uiMemBase, BOOL const fSharedMemory, char const * const pszUsed, DWORD * const puiIOBase)
{
	BOOL fSuccess = FALSE;
	int i;
	RESOURCE IOBaseList[] =
	{
		{ 0x328, FALSE },
		{ 0x108, FALSE }, 
		{ 0x118, FALSE }, 
		{ 0x128, FALSE }, 
		{ 0x208, FALSE }, 
		{ 0x228, FALSE },
		{ 0x308, FALSE }
	};
	int const cIOBases = ElementCount(IOBaseList);
	int const cbIOPort = 4;
	UCHAR const MemEnable = 0x80; 
	UCHAR const MemDisable = 0;

	FlagUsedResources(pszUsed, IOBaseList, cIOBases);

	for (i = 0; i < cIOBases; i++)
	{
		RESOURCE const * const pIOBase = &IOBaseList[i];

		if (!pIOBase->fUsed)
		{
			UINT uiIOBase = pIOBase->uiValue;
			
			if (DetectCheckPortUsage(BusType, BusNumber, uiIOBase, cbIOPort) == ERROR_SUCCESS)
			{
				UCHAR Value;

				if ((DetectReadPortUchar(BusType, BusNumber, uiIOBase + 1, &Value) == ERROR_SUCCESS) &&
					((Value & 0x0F) == 0x05))
				{
					BOOL fMemTestPassed = TRUE;

					if (!fSharedMemory)
					{
						USHORT MemoryBase = (USHORT)(uiMemBase >> 8);

						if (!((DetectWritePortUchar(BusType, BusNumber, uiIOBase + 3, HIBYTE(MemoryBase)) == ERROR_SUCCESS) &&
							  (DetectWritePortUchar(BusType, BusNumber, uiIOBase + 2, LOBYTE(MemoryBase)) == ERROR_SUCCESS) &&
							  (DetectWritePortUchar(BusType, BusNumber, uiIOBase + 1, MemEnable) == ERROR_SUCCESS)))
						{
							fMemTestPassed = FALSE;
						}
						else
						{
							DWORD adwAddress[] =
							{
								0x0000,
								0x7FFF - sizeof(DWORD)
							};
							int const cAddresses = ElementCount(adwAddress);
							DWORD adwTestValue[] =
							{
								0xC33C5AA5,
								0xA55A3CC3
							};
							int const cTests = ElementCount(adwTestValue);
							int iTest;

							_ASSERTE(cAddresses == cTests);

							for (iTest = 0; fMemTestPassed && iTest < cTests; iTest++)
							{
								DWORD dwAddress = uiMemBase + adwAddress[iTest];
								DWORD dwWriteValue = adwTestValue[iTest];
								DWORD dwReadValue;

								fMemTestPassed =
									(DetectWriteMappedMemory(BusType, BusNumber, dwAddress, sizeof dwWriteValue, &dwWriteValue) == ERROR_SUCCESS) &&
									(DetectReadMappedMemory(BusType, BusNumber, dwAddress, sizeof dwReadValue, &dwReadValue) == ERROR_SUCCESS) &&
									(dwWriteValue == dwReadValue);
							}

							(void) DetectWritePortUchar(BusType, BusNumber, uiIOBase + 1, MemDisable);
						}
					}

					if (fMemTestPassed)
					{
						*puiIOBase = uiIOBase;
						fSuccess = TRUE;
						break;
					}
				}
			}
		}
	}

	return fSuccess;
}

#define	MAX_IRQS	8

static BOOL FindIRQ(INTERFACE_TYPE const BusType, DWORD const BusNumber, char const * const pszUsed, DWORD * const puiIRQ)
{
	BOOL fSuccess = FALSE;
	int i;
	RESOURCE IRQList[] =
	{
		{ 15, FALSE },
		{  3, FALSE }, 
		{  5, FALSE }, 
		{  7, FALSE }, 
		{ 10, FALSE }, 
		{ 11, FALSE }, 
		{ 12, FALSE },
	};
	int const cIRQs = ElementCount(IRQList);

	_ASSERTE(cIRQs <= MAX_IRQS);

	FlagUsedResources(pszUsed, IRQList, cIRQs);

	{
		HANDLE hTrap;
		UCHAR aUnusedIRQs[MAX_IRQS];
		int cUnusedIRQs = 0;

		for (i = 0; i < cIRQs; i++)
		{
			RESOURCE const * const pIRQ = &IRQList[i];

			if (!pIRQ->fUsed)
				aUnusedIRQs[cUnusedIRQs++] = (UCHAR) pIRQ->uiValue;
		}

		if ((cUnusedIRQs > 0) &&
			(DetectSetInterruptTrap(BusType, BusNumber, &hTrap, aUnusedIRQs, cUnusedIRQs) == ERROR_SUCCESS))
		{
			UCHAR aTrapIRQs[MAX_IRQS];

			if (DetectQueryInterruptTrap(hTrap, aTrapIRQs, cUnusedIRQs) == ERROR_SUCCESS)
			{
				int iIRQ;
				
				for (iIRQ = 0; !fSuccess && iIRQ < cUnusedIRQs; iIRQ++)
					if (aTrapIRQs[iIRQ] != 3)
					{
						*puiIRQ = aUnusedIRQs[iIRQ];
						fSuccess = TRUE;
					}
			}

			DetectRemoveInterruptTrap(hTrap);
		}
	}

	return fSuccess;
}

extern BOOL WINAPI DetectISA(INTERFACE_TYPE const BusType,
                             DWORD const BusNumber,
                             char const * const pszIOBaseList,
                             char const * const pszMemBaseList,
                             char const * const pszIRQList,
                             UINT * const pIOBase,
                             UINT * pMemBase,
                             UINT * const pIRQ)
{
   BOOL fSuccess = FALSE;

   _ASSERTE(pszIOBaseList != NULL);
   if (pszIOBaseList == NULL) goto Exit;

   _ASSERTE(pszMemBaseList != NULL);
   if (pszMemBaseList == NULL) goto Exit;

   _ASSERTE(pszIRQList != NULL);
   if (pszIRQList == NULL) goto Exit;

   _ASSERTE(pIOBase != NULL);
   if (pIOBase == NULL) goto Exit;

   _ASSERTE(pMemBase != NULL);
   if (pMemBase == NULL) goto Exit;

   _ASSERTE(pIRQ != NULL);
   if (pIRQ == NULL) goto Exit;

	if (!StartNetDetectDriver())
	{
		Message(IDS_SERVICESTARTFAIL, MB_OK);
		goto Exit;
	}
		
	{
		BOOL fSharedMemory;

		if (!FindMemBase(BusType, BusNumber, pszMemBaseList, pMemBase, &fSharedMemory))
		{
			Message(IDS_MEMADDRESSFAIL, MB_OK);
			goto Exit;
		}

		if (!FindIOBase(BusType, BusNumber, *pMemBase, fSharedMemory, pszIOBaseList, pIOBase))
		{
			Message(IDS_IOADDRESSFAIL, MB_OK);
			goto Exit;
		}

		if (!FindIRQ(BusType, BusNumber, pszIRQList, pIRQ))
		{
			Message(IDS_IRQFAIL, MB_OK);
         goto Exit;
		}
	}

   fSuccess = TRUE;
		
Exit:
	return fSuccess;
}
