//+-----------------------------------------------------------------------------
//
// 
//  Copyright (C) Digi International. 2000.
//
//  File:       COCPYINF.C
//
//  Contents:   co-installer hook to copy INF's into INF directory
//
//              The assumption here is that the install section may
//              specify, eg, "CopyINF = a.inf,b.inf,c.inf"
//              where the inf's specified must exist in same location
//              as primary inf.
//
//              This example shows both a way to install INF's for a
//              multi-function device for Windows 2000, and also how
//              to add to the INF syntax for other purposes.
//
//
//  Author:     B.R.Sahu   May 13 2000
//              Taheer
//              ShivaMallu 
//
//
//------------------------------------------------------------------------------
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <malloc.h>
#include <msports.h>
#include <tchar.h>
#include <cfgmgr32.h>
#include <initguid.h>
#include <devguid.h>
#include <stdlib.h>

//+-----------------------------------------------------------------------------
//
// WARNING!
//
// A Coinstaller must not generate any popup to the user when called with
// DI_QUIETINSTALL. However, it's generally never a good idea to generate
// popups.

//#define _DEBUG

//
// OutputDebugString is a fine way to output information for debugging
//
#ifdef _DEBUG
#define DbgOut(Text) OutputDebugString(TEXT("CoCopyInf: " Text "\n"))
#else
#define DbgOut(Text)
#endif



#ifdef _DEBUG
   #define DbgPrintf(STRING)               DebugPrintf STRING;
VOID DebugPrintf(LPTSTR fmt, ...);
#else
   #define DbgPrintf(STRING)               (0)
#endif

//
// this is the keyword we've introduced by this co-installer
// note that the name is not case sensative
//
#define COPYINF_KEY TEXT("CopyINF")
#define MAX_VALUE_NAME              256
#define MAX_RETURN_BUFF             (MAX_VALUE_NAME*20)

HCOMDB hComDB;
UINT   RelPort;
int iIndex = 0;
CHAR ValueName[256][256];
char ParentId[128];
char *p;
char *PortInstance;
LONG Err;

void GetPortInstance();
VOID GetInstanceId( HDEVINFO            DeviceInfoSet,
                    PSP_DEVINFO_DATA    DeviceInfoData);
void RemovePorts( HDEVINFO              DeviceInfoSet,
                  PSP_DEVINFO_DATA      DeviceInfoData);


void __cdecl main()
{}

//+-----------------------------------------------------------------------------
//
//  Function:   PostProcessInstallDevice
//
//  Purpose:    Handle DIF_INSTALLDEVICE PostProcessing
//
//  Arguments:
//      DeviceInfoSet     [in]
//      DeviceInfoData    [in]
//      Context           [in,out]
//
//  Returns:    NO_ERROR or an error code.
//
DWORD
PostProcessInstallDevice (
                    IN     HDEVINFO          DeviceInfoSet,
                    IN     PSP_DEVINFO_DATA  DeviceInfoData,
                    IN OUT PCOINSTALLER_CONTEXT_DATA Context
                    )
{
    DWORD                  Status = NO_ERROR;
    INFCONTEXT             InfContext;
    HINF                   InfFile = INVALID_HANDLE_VALUE;
    SP_DRVINFO_DATA        DriverInfoData;
    SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
    PSP_INF_INFORMATION    InfInformation = NULL;
    DWORD                  ReqSize;
    SP_ORIGINAL_FILE_INFO  InfOriginalFileInformation;
    TCHAR                  InstallSectionName[LINE_LEN];
    TCHAR                  CombinedPath[MAX_PATH*2];
    TCHAR                  FullSourcePath[MAX_PATH];
    PTSTR                  FileNamePtr;
    INFCONTEXT             InfLineContext;
    DWORD                  FieldIndex;
    DWORD                  FieldCount;
    DWORD                  RetVal;

    //
    // Find name of INF and name of install section
    //

    DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
    if (!SetupDiGetSelectedDriver( DeviceInfoSet,
                                   DeviceInfoData,
                                   &DriverInfoData)) {
        Status = GetLastError();
        DbgOut("Fail: SetupDiGetSelectedDriver");
        goto clean;
    }

    DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
    if (!SetupDiGetDriverInfoDetail(DeviceInfoSet,
                                    DeviceInfoData,
                                    &DriverInfoData,
                                    &DriverInfoDetailData,
                                    sizeof(SP_DRVINFO_DETAIL_DATA),
                                    NULL)) {
        Status = GetLastError();
        if (Status == ERROR_INSUFFICIENT_BUFFER) {
            //
            // We don't need the extended information.  Ignore.
            //
        } else {
            DbgOut("Fail: SetupDiGetDriverInfoDetail");
            goto clean;
        }
    }

    //
    // We have InfFileName, open the INF
    //
    InfFile = SetupOpenInfFile(DriverInfoDetailData.InfFileName,
                               NULL,
                               INF_STYLE_WIN4,
                               NULL);
    if (InfFile == INVALID_HANDLE_VALUE) {
        Status = GetLastError();
        DbgOut("Fail: SetupOpenInfFile");
        goto clean;
    }
    //
    // determine original filename to see if it has been
    // previously copied
    // this is long-winded, and is rarely an apropriate thing
    // to do. If we don't do it in this case, we'll try and
    // copy INF files that have already been copied and that
    // we might not have source for.
    //
    ReqSize = 0;
    if (!SetupGetInfInformation(InfFile,
                                INFINFO_INF_SPEC_IS_HINF,
                                NULL,
                                0,
                                &ReqSize)) {
        Status = GetLastError();
        DbgOut("Fail: SetupGetInfInformation");
        goto clean;
    }
    InfInformation = (PSP_INF_INFORMATION)malloc(ReqSize);
    if (InfInformation == NULL) {
        Status = ERROR_NOT_ENOUGH_MEMORY;
        DbgOut("Fail: SetupGetInfInformation - malloc");
        goto clean;
    }
    if (!SetupGetInfInformation(InfFile,
                                INFINFO_INF_SPEC_IS_HINF,
                                InfInformation,
                                ReqSize,
                                NULL)) {
        Status = GetLastError();
        DbgOut("Fail: SetupGetInfInformation - 2nd call");
        goto clean;
    }
    InfOriginalFileInformation.cbSize = sizeof(InfOriginalFileInformation);
    if(!SetupQueryInfOriginalFileInformation(InfInformation,
                                     0,
                                     NULL,
                                     &InfOriginalFileInformation)) {
        Status = GetLastError();
        DbgOut("Fail: SetupQueryInfOriginalFileInformation");
        goto clean;
    }

    //
    // determine location of current INF, which will be used
    // as root of inf's specified in CopyInf
    // but we also need FileNamePtr here to do an INF Name check.
    //

    RetVal = GetFullPathName(DriverInfoDetailData.InfFileName,
                             MAX_PATH,
                             CombinedPath,
                             &FileNamePtr);
    if (RetVal == 0 || RetVal > MAX_PATH) {
        Status = GetLastError();
        DbgOut("Fail: GetFullPathName");
        goto clean;
    }

    if (lstrcmpi(FileNamePtr,
                 InfOriginalFileInformation.OriginalInfName)!=0) {
        //
        // we have determined that this is a copy of original INF
        // so we must have already processed CopyINF= bits
        //
        Status = NO_ERROR;
        DbgOut("Skip processing of CopyINF (INF not at source location)");
        goto clean;
    }

    //
    // determine section used for the install which might be different
    // from the section name specified in DriverInfoDetailData.
    //
    if(!SetupDiGetActualSectionToInstall(InfFile,
                                         DriverInfoDetailData.SectionName,
                                         InstallSectionName,
                                         LINE_LEN,
                                         NULL,
                                         NULL)) {

        Status = GetLastError();
        DbgOut("Fail: SetupDiGetActualSectionToInstall");
        goto clean;
    }

    //
    // we look for keyword "CopyInf" in the install section
    //
    if (SetupFindFirstLine(InfFile,
                           InstallSectionName,
                           COPYINF_KEY,
                           &InfLineContext)) {
        DbgOut("Found at least one CopyInf entry");
        do {
            //
            // CopyInf = a.inf,b.inf,c.inf...
            // a.inf will be at index 1.
            //
            FieldCount = SetupGetFieldCount(&InfLineContext);
            for(FieldIndex = 1;FieldIndex<=FieldCount;FieldIndex++) {
                if(SetupGetStringField(&InfLineContext,
                                       FieldIndex,
                                       FileNamePtr,
                                       MAX_PATH,
                                       NULL)) {
                    //
                    // we have a listed INF, obtain canonical pathname
                    // for this INF
                    // (note that we assume the INF is relative
                    //  to original INF and is typically just a filename)
                    //
                    RetVal = GetFullPathName(CombinedPath,
                                             MAX_PATH,
                                             FullSourcePath,
                                             NULL);
                    if (RetVal == 0 || RetVal > MAX_PATH) {
                        Status = GetLastError();
                        DbgOut("Fail: GetFullPathName");
                        //
                        // not fatal
                        //
                    } else if(!SetupCopyOEMInf(FullSourcePath,
                                    NULL,
                                    SPOST_PATH,
                                    0,
                                    NULL,
                                    0,
                                    NULL,
                                    NULL)) {
                        Status = GetLastError();
                        DbgOut("Fail: SetupCopyOEMInf");
                        //
                        // not fatal
                        //
                    }
                } else {
                    Status = GetLastError();
                    DbgOut("Fail: SetupGetStringField");
                    //
                    // not fatal
                    //
                }
            }

        } while (SetupFindNextMatchLine(&InfLineContext,
                                        COPYINF_KEY,
                                        &InfLineContext));
    }

    Status = NO_ERROR;

clean:

    if (InfInformation) {
        free(InfInformation);
    }
    if (InfFile) {
        SetupCloseInfFile(InfFile);
    }
    //
    // since what we are doing does not affect primary device installation
    // always return success
    //
    return NO_ERROR;
}

//+-----------------------------------------------------------------------------
//
//  Function:   CoCopyINF
//
//  Purpose:    Responds to co-installer messages
//
//  Arguments:
//      InstallFunction   [in]
//      DeviceInfoSet     [in]
//      DeviceInfoData    [in]
//      Context           [in,out]
//
//  Returns:    NO_ERROR, ERROR_DI_POSTPROCESSING_REQUIRED, or an error code.
//
HRESULT
 CoCopyINF (
               IN     DI_FUNCTION               InstallFunction,
               IN     HDEVINFO                  DeviceInfoSet,
               IN     PSP_DEVINFO_DATA          DeviceInfoData,
               IN OUT PCOINSTALLER_CONTEXT_DATA Context
           )
{	

    DWORD Status = NO_ERROR;

    DbgOut("Entering CoCpy\n");

    switch (InstallFunction)
    {
        case DIF_INSTALLDEVICE:
            //
            // We should not copy any INF files until the install has completed
            // like the primary INF, all secondary INF's must exist on each disk
            // of a multi-disk install.
            //

            if(!Context->PostProcessing)
            {
                DbgOut("DIF_INSTALLDEVICE");

                //
                // indicate that we need to be called after the device has been
                // installed.
                //
                Status = ERROR_DI_POSTPROCESSING_REQUIRED;
            }
            else // post processing
            {
                //
                // if driver installation failed, we're not interested
                // in processing CopyINF entries.
                //

                if (Context->InstallResult != NO_ERROR) {
                    DbgOut("DIF_INSTALLDEVICE PostProcessing on failure");
                    Status = Context->InstallResult;
                    break;
				}

                DbgOut("DIF_INSTALLDEVICE PostProcessing");

                Status = PostProcessInstallDevice(DeviceInfoSet,
                                                  DeviceInfoData,
                                                  Context);
            }
        break;

		case DIF_REMOVE:

			 if(!Context->PostProcessing)
            {				   
				   DbgOut("DIF_REMOVE");
				   RemovePorts(DeviceInfoSet,DeviceInfoData) ;
            }

        	break;	

		default:
		break;
    }

    DbgOut("Leaving CoCpy\n");

    return Status;
}

void RemovePorts( HDEVINFO                  DeviceInfoSet,
                  PSP_DEVINFO_DATA          DeviceInfoData)
{

	HKEY hKey;
	CHAR PortName[32/*MAX_PORT*/][256];
	DWORD NameLen;
	int i;
	int k = 0;
    
	DbgPrintf(("In RemovePorts\n"));
	GetInstanceId( DeviceInfoSet,DeviceInfoData);
      if (ComDBOpen(&hComDB) != ERROR_SUCCESS)
	  {
	    	DbgOut("Unable to open the ComDB handle");
	  }

	  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
				         TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
                         0,
                         KEY_READ,
                         &hKey) == ERROR_SUCCESS)
	  {
		for (i = 0 ;i<iIndex; i++)
		{

			DbgPrintf(("ParentacValueName1 i %s %d\n",ValueName[i],i));

			NameLen = 16;
			Err= RegQueryValueEx(hKey,
								ValueName[i],
								NULL,
								NULL,
								(PBYTE)PortName[i],
								&NameLen
								);                 
			if(Err == ERROR_SUCCESS)	
			{
			   	    DbgPrintf(("RemovePorts:PortName[i] %s\n",PortName[i]));
        			ComDBReleasePort(hComDB, atoi(PortName[i]+3));
			}
			else
			{
				DbgPrintf(("RemovePorts:RegQueryValueEx Failed 0x%x\n", GetLastError()));
			}
		}
	}
	else
	{
		DbgPrintf(("RemovePorts:RegOpenKeyEx Failed\n"));
	}
	RegCloseKey(hKey);

    DbgPrintf(("Out of RemovePorts"));
}

VOID GetInstanceId( HDEVINFO                  DeviceInfoSet,
                    PSP_DEVINFO_DATA          DeviceInfoData)
{
    char DeviceInstanceId[80];
    DWORD RequiredSize, NameLen;
    HKEY hKey;
	char RegistryPath[128];
	int i ;

    DbgPrintf( ("In InstanceId \n") );

    SetupDiGetDeviceInstanceId(
                              DeviceInfoSet,
                              DeviceInfoData,
                              DeviceInstanceId,
                              64,								// DeviceInstanceIdSize, 
                              &RequiredSize
                              );
    DbgPrintf( ("InstanceId %s ReqSize %d\n", DeviceInstanceId, RequiredSize) );

	strcpy(RegistryPath, "SYSTEM\\CurrentControlSet\\Enum\\");
	strcat(RegistryPath, DeviceInstanceId);
    DbgPrintf( ("Registry %s\n", RegistryPath) );

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
				         RegistryPath,
                         0,
                         KEY_READ,
                         &hKey) == ERROR_SUCCESS)
    {

		NameLen = 64;
		Err= RegQueryValueEx(   hKey,
								TEXT("Driver"),
								NULL,
								NULL,
								ParentId,//(PBYTE)ParentId,
								&NameLen
							);
		DbgPrintf( ("Driver NameLen %s  %d \n",ParentId,  NameLen));
	}
	else
	{
		DbgPrintf(("RegOpenKeyEx Failed 0x%x\n", GetLastError()));
	}
	
	i = NameLen;
	
	if ( ParentId == NULL || i == 0 )
		DbgPrintf(("ParentID is NULL\n" ));

	while ( i > 0 && ParentId[--i] != '\\' )
	{
		;
	}

	p = &ParentId[i+1];
	GetPortInstance();
	RegCloseKey(hKey);
}

void GetPortInstance()
{
   HDEVINFO InstalledDevList;
   SP_DEVINFO_DATA devData;
   BYTE  DriverName[64];
   DWORD DriverSize;
   int i;

   DbgPrintf(("In GetPortInstance"));
   // Get list of ports
   InstalledDevList = SetupDiGetClassDevs( (LPGUID)&GUID_DEVCLASS_PORTS, NULL, NULL, 0);
   if ( InstalledDevList == INVALID_HANDLE_VALUE )
   {
      DbgPrintf(("Failed SetupDiGetClassDevs to get list of installed devices\n"));
      return;
   }

   // Check each port to determine if it is attached to our adapter
   devData.cbSize = sizeof(devData);
   while (SetupDiEnumDeviceInfo( InstalledDevList, iIndex, &devData ))
   {
	   SetupDiGetDeviceRegistryProperty(InstalledDevList,
										&devData,
										SPDRP_DRIVER,
										NULL,
										(PBYTE)DriverName,
										sizeof(DriverName),
										&DriverSize
										);
        i = DriverSize;
	
    	if ( DriverName == NULL || i == 0 )
		DbgPrintf(("ParentID is NULL\n" ));

	    while ( i > 0 && DriverName[--i] != '\\' )
		{
		    ;
		}

		PortInstance = &DriverName[i+1];

		wsprintf(ValueName[iIndex],"\\Classic%d%d",atoi(p),atoi(PortInstance));  
		DbgPrintf(("ValueName[iIndex] %s\n",ValueName[iIndex] ));
        iIndex++;
   }

   SetupDiDestroyDeviceInfoList( InstalledDevList );
   DbgPrintf(("Out of GetPortInstance"));

}



#ifdef _DEBUG

VOID DebugPrintf(
                LPTSTR fmt,
                ...
                )
{
   va_list marker;
   TCHAR szBuf[MAX_RETURN_BUFF];

   va_start(marker, fmt);
   wvsprintf(szBuf, fmt, marker);
   va_end(marker);

   OutputDebugString(szBuf);
   OutputDebugString(TEXT("\r\n"));
}

#endif /* _DEBUG */


