/*******************************************************************************

	AUTONT.C v3.0
	description : Acces MCX de bas niveau
		- acces RAM Mailbox
		- acces FIFO

	NOTES:
		ce code a ete ecrit avec des tabs de 4 caracteres et sur 80 colonnes

	(c) Copyright 2000 by Acksys -- JPT

******************************************************************************/
/*==========================================================================*/
/* Includes																	*/
/*==========================================================================*/
#include <windows.h>
#include <winioctl.h>
#include <setupapi.h>
#include "mcc_mcx.h"
#include "autont.h"
#include <io.h>  
#include <stdio.h>
#include <stddef.h>
/*==========================================================================*/
/* Debugs																	*/
/*==========================================================================*/
#define DBG_MEM		if(0)printf("MBOX:"
#define DBG_INIT	if(0)printf("INIT:"

void WaitTask(LPVOID *p);
	
static HANDLE lastHandle = INVALID_HANDLE_VALUE;

HANDLE OpenAutomcx(char *devName)
{
	static char prefix[] = "\\\\.\\";
	char *osName;

	DBG_INIT "mcx::Open\n");

	osName = malloc(strlen(devName) + sizeof(prefix));
	if(!osName)
		return INVALID_HANDLE_VALUE;

	strcpy(osName,"");
	if(strncmp(devName,prefix,strlen(prefix)) != 0)
		strcpy(osName,prefix);
	strcat(osName,devName);

	lastHandle = CreateFile((PCHAR)osName,
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL
			);
	DBG_INIT "hMem=%x\n",lastHandle);
	free(osName);
	if(lastHandle == INVALID_HANDLE_VALUE)
		printf("CreateFile(%s): error %d\n", osName, GetLastError());
	return lastHandle;
}

PVOID GetAutomcxPtr(HANDLE hDriver)
{
	PVOID Mailbox;
	DWORD cbReturned;

	if (!DeviceIoControl(hDriver,
			(DWORD)IOCTL_AUTOMCX_MAP_MAILBOX,
			NULL,
			0,
			&Mailbox,
			sizeof(PVOID),
			&cbReturned,
			0
			))
	{
		printf("DeviceIoControl: error %d\n", GetLastError());
		return NULL;
	}
	DBG_MEM "Mailbox at %p\n", Mailbox);
	return Mailbox;
}

VOID CloseAutomcx(HANDLE hDriver)
{
	DBG_INIT "mcx:Close\n");
	CloseHandle(hDriver);
}

/*--------------------*/
/* Read byte function */
/*--------------------*/
int McxInPort(HANDLE hndFile, int iNp)
// iNp			: Relative address of the port (address - base port)
// hndFile		: Handle on the GENPORT.SYS driver
// return		: Byte read
{
	BOOL		IoctlResult;
	MCX_AREA_DESCRIPTOR	Area;	/* Buffer received from driver	*/
	DWORD		ReturnedLength;

	Area.Operation = MCX_AREA_GET|MCX_AREA_IOPORT;
	Area.StartAddress = iNp;
	Area.Length = 1;
	IoctlResult = DeviceIoControl(
		hndFile,				/* Driver handle */
		IOCTL_AUTOMCX_READ_PORT,
		&Area,					/* Buffer sent to driver */
		sizeof(Area),			/* Length */
		&Area,					/* Buffer received from driver */
		sizeof(Area),
		&ReturnedLength,		/* Effective length */
		NULL					/* With wait */
		);

	if (IoctlResult)
		return(Area.Buffer[0]);
	return(-1);
}

UCHAR McxReadFifo(HANDLE fh)
{
	/* The fifo is the first register of the I/O space (base+0)*/
	return McxInPort(fh, 0);
}

/*---------------------*/
/* Write byte function */
/*---------------------*/
BOOL McxOutPort(HANDLE hndFile,int iNp,char cData)
// iNp			: Relative address of the port (address - base port)
// hndFile		: Handle of the MCX.SYS driver
// cData		: Byte to be written
// return		: Either ok (TRUE), or failure (FALSE)
{
	MCX_AREA_DESCRIPTOR Area;		/* Buffer sent to driver */
	DWORD		ReturnedLength;		/* Unused */

	Area.Operation = MCX_AREA_SET|MCX_AREA_IOPORT;
	Area.StartAddress = (ULONG) iNp;
	Area.Length = 1;
	Area.Buffer[0] = (UCHAR)cData;	/* Byte to be written */
	return DeviceIoControl(
		hndFile,					/* Driver handle */
		IOCTL_AUTOMCX_WRITE_PORT,
		&Area,						/* Buffer sent to driver */
		sizeof(Area),				/* Length */
		NULL,						/* Buffer received from driver */
		0,							/* Length */
		&ReturnedLength,			/* Effective length (0) */
		NULL						/* With wait */
	);
}

 int McxChangeMode(void * HardwareMemBase,int FirmwareRequested) {

	char * CardId;
	char KeyPath[150];
	HKEY	hSoftKey;
	DWORD BusNumber,SlotNumber;
	DWORD BuffSize,rc;
	DWORD RgWd=REG_DWORD;
	DWORD CurrentFirmware;
	
	HDEVINFO DeviceInfoSet;
	SP_DEVINFO_DATA  DeviceInfoData;
	
	//Regarde si le nom de la carte est un nom valide 
	//pour nous
	CardId = HardwareMemBase;
	if ( sscanf(CardId,"Mcx%02x%02x",&BusNumber,&SlotNumber) != 2) {
		printf("Wrong card name\n");
		return -1;
	}

	//Trouve le firmware actuel de la carte
	//MakeKeyPath
	sprintf(KeyPath,
			"SYSTEM\\CurrentControlSet\\Services\\Mcx\\Parameters\\Mcx%02x%02x"
			,BusNumber,
			SlotNumber);
		
	//Get the actual mode
	rc = RegOpenKeyEx(	HKEY_LOCAL_MACHINE,
						KeyPath,
					   	0,
						KEY_ALL_ACCESS,
						&hSoftKey);

	if ( rc == ERROR_SUCCESS) {
		rc =  RegQueryValueEx(	hSoftKey, 
								"Firmware", 
								NULL, 
								&RgWd, 
								(LPBYTE)&CurrentFirmware, 
								&BuffSize);
		
		if (rc != ERROR_SUCCESS) {
			printf("Err : %d\n",GetLastError());
			printf("Can't Read Firmware Value\n");
			RegCloseKey(hSoftKey);
			return -1;
		}								
   	}
	else {
		printf("Can't Open SoftKey\n");
		return -1;
	}


	//Passage dans le firmware demande
	if ( ERROR_SUCCESS != McxExistanceCheck(BusNumber,SlotNumber,&DeviceInfoSet,&DeviceInfoData) ) {
		//La carte n'est pas referencée par windows
		return -1;
	}

	//Ecriture en base de registre de la valeur du nouveau firmware
	rc = RegOpenKeyEx(	HKEY_LOCAL_MACHINE,
						KeyPath,
					   	0,
						KEY_ALL_ACCESS,
						&hSoftKey);

	if ( rc == ERROR_SUCCESS) {
		rc=RegSetValueEx(hSoftKey, "Firmware", 0, REG_DWORD, (BYTE *)&FirmwareRequested, sizeof(DWORD));

		if (rc != ERROR_SUCCESS) {
			printf("GetLastError : %d Err : %d\n",GetLastError(),rc);
			printf("Can't Set Firmware Value\n");
			RegCloseKey(hSoftKey);
		}	 
		RegCloseKey(hSoftKey);							
   	}
	else {
		printf("Can't Open SoftKey\n");
		return -1;
	}	

	//Redemare le driver
	if (ERROR_SUCCESS != RestartDriver(DeviceInfoSet,&DeviceInfoData )) {
		//Pensez a detruire le DeviceInfoSet
		SetupDiDestroyDriverInfoList(DeviceInfoSet,NULL,SPDIT_CLASSDRIVER );
		printf("Error during MCX driver restart\n");
		return -1;
	}
		
	return 0;
}

int McxExistanceCheck(DWORD BusNumber,DWORD SlotNumber,HDEVINFO * DeviceInfoSet,SP_DEVINFO_DATA * DeviceInfoData ) {
	
	int i;
	DWORD rc; 
	LPTSTR  Buffer;
	DWORD buffersize;

	DWORD fBusNumber;
	DWORD fSlotNumber;

	(*DeviceInfoSet)  =SetupDiGetClassDevs(NULL,0,0,DIGCF_PRESENT | DIGCF_ALLCLASSES );
	
	if ((*DeviceInfoSet) != INVALID_HANDLE_VALUE) {
		//Enumerate installed AND present Multiport Serial Card 
		(DeviceInfoData)->cbSize = sizeof(SP_DEVINFO_DATA);
		for (i=0;rc=SetupDiEnumDeviceInfo(*DeviceInfoSet,i,DeviceInfoData);i++) {
			//Get the instance Id		
			buffersize = 0;
			while (!SetupDiGetDeviceInstanceId(	*DeviceInfoSet,
										            DeviceInfoData,
										            Buffer,
										            buffersize,
										            &buffersize)) {
					rc = GetLastError();
					if (rc == ERROR_INSUFFICIENT_BUFFER) {
						// Change the buffer size.
				        if (Buffer) LocalFree(Buffer);
							Buffer = (LPTSTR)LocalAlloc(LPTR,buffersize);
        				}	
        				else {
							// Insert error handling here.
							printf("Erreur Get InstanceId : %d \n",rc);
							SetupDiDestroyDeviceInfoList(DeviceInfoSet);
		     				return -1;
						}
					}
			
			if ( (Buffer[0] =='P') && (Buffer[1] =='C') && (Buffer[2] =='I') ) {

				buffersize = sizeof(DWORD);
				//Get the BusNumber 
				if ( !SetupDiGetDeviceRegistryProperty(	
											*DeviceInfoSet, 
											DeviceInfoData, 
											0x00000015, 
											NULL, 
											(PBYTE)&fBusNumber, 
											buffersize, 
											NULL)) {
					if ( rc = GetLastError() ) {
						if ( rc != 13)
							printf("Error Getting BusNumber : %d\n",GetLastError());
							SetupDiDestroyDeviceInfoList(DeviceInfoSet);
							return -1;
					}
				}

				//Get the SlotNumber
				if ( !SetupDiGetDeviceRegistryProperty(
											*DeviceInfoSet, 
											DeviceInfoData , 
											0x0000001C, 
											NULL, 
											(PBYTE)&fSlotNumber, 
											buffersize, 
											NULL)) {
					if ( rc = GetLastError() ) {
						if ( rc != 13)
							printf("Error Getting SlotNumber : %d\n",GetLastError());
							SetupDiDestroyDeviceInfoList(DeviceInfoSet);
							return -1;
					}
				}	
				fSlotNumber = (fSlotNumber >>16) & 0x0000FFFF;
				
				//Compare Slot and Bus With Card Name given in parameters
				if ((fSlotNumber == SlotNumber) && (fBusNumber == BusNumber)) {
					return 0;
				}
			}
		}
		printf("No Card Found\n");
	}
	SetupDiDestroyDeviceInfoList(DeviceInfoSet);
	return 1;	
}

int RestartDriver(HDEVINFO DeviceInformationSet,SP_DEVINFO_DATA * DeviceInfoData ) {

	DWORD Err;
	DWORD Err1;	
	HANDLE thd;
	DWORD tid;
	SP_PROPCHANGE_PARAMS pcp;
    SP_DEVINSTALL_PARAMS devParams;
	
	printf("Restart McxDriver. This could take a few minute, please be patient.\n");
	
	//Voir la fonction ControlCallBack de DevCon			
	pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
    pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
    pcp.StateChange = DICS_PROPCHANGE;
    pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
    pcp.HwProfile = 0;
    
	//Creation du Thread qui affiche des "." pour faire patienter
	thd = CreateThread(NULL,0,
				(LPTHREAD_START_ROUTINE)WaitTask,0,0,&tid);
	if(thd == NULL){
		printf("Error during thread creation\n");
		return -1;
	}
	
    Err = SetupDiSetClassInstallParams(DeviceInformationSet,DeviceInfoData,&pcp.ClassInstallHeader,sizeof(pcp));
	//printf("ClassInstallParams : %d GetLastError : %d\n",Err,GetLastError());
		
	Err1 = SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,DeviceInformationSet,DeviceInfoData);
    //printf("ClassInstaller : %d GetLastError : %d\n",Err1,GetLastError());
	
	if ( (!Err) || (!Err1)) {
		printf("Error during restart\n");
		return -1;
	}

	//Arret duu Thread
	if ( !TerminateThread(thd,0)) {
		printf("Error during Thread stoping\n");
		return -1;		
	}
	
	
    //
    // see if device needs reboot
    //
    devParams.cbSize = sizeof(devParams);
    if(SetupDiGetDeviceInstallParams(DeviceInformationSet,DeviceInfoData,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) {
		printf("Need to Reboot\n");
    } 
	else {
    	//
    	//appears to have succeeded
        //
		printf("Restart Ok\n");
    }
    return 0;
}

void WaitTask(LPVOID *p) {
	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
	while (1) {
		Sleep(2000);
		printf(".");
	}
}

