#include <windows.h>

#pragma warning(disable: 4996)

void PrintUsageMessage()
{
	_tprintf(TEXT("usage:\r\n"));
	_tprintf(TEXT("COMTest <maxport> [/t]\r\n"));
	_tprintf(TEXT("maxport (1-*): maximum com port index.\r\n"));
}

LRESULT InitCOMPort(HANDLE comport,DWORD index)
{
	DCB dcb;

	memset(&dcb,0,sizeof(DCB));

	dcb.DCBlength=sizeof(DCB);

	if (!GetCommState(comport,&dcb))
		return GetLastError();

	dcb.BaudRate=38400;
	dcb.fParity=FALSE;
	dcb.ByteSize=8;
	dcb.StopBits=ONESTOPBIT;
	dcb.fBinary=TRUE;
	dcb.fDsrSensitivity=FALSE;
	dcb.fDtrControl=FALSE;
	dcb.fOutxCtsFlow=FALSE;
	dcb.fOutxDsrFlow=FALSE;
	dcb.fRtsControl=FALSE;
	dcb.Parity=NOPARITY;

	if (!SetCommState(comport,&dcb))
		return GetLastError();

		COMMTIMEOUTS timeouts;

	if (!GetCommTimeouts(comport,&timeouts))
		return GetLastError();

	timeouts.ReadTotalTimeoutConstant=0;
	timeouts.ReadTotalTimeoutMultiplier=0;
	timeouts.ReadIntervalTimeout=MAXDWORD;

	if (!SetCommTimeouts(comport,&timeouts))
		return GetLastError();

	char message[255];

	sprintf(message,"ComTest running on COM %d\r\n",index);

	DWORD written=0;
	
	if (!WriteFile(comport,message,strlen(message),&written,NULL))
		return GetLastError();

	return 0;
}

LRESULT CALLBACK COMThreadProc(HANDLE comport)
{
	char	c=0;
	DWORD	readt;
	DWORD	written;

	while (c!='X')
	{
		if (!ReadFile(comport,&c,1,&readt,NULL))
		{
			DWORD err=GetLastError();

			_tprintf(TEXT("Readfile error: %d(%08X)\r\n"),err,err);
			return err;
		}

		if (!readt)
		{
			Sleep(0);
			continue;
		}

		if (!WriteFile(comport,&c,1,&written,NULL))
		{
			DWORD err=GetLastError();

			_tprintf(TEXT("WriteFile error: %d(%08X)\r\n"),err,err);
			return err;
		}
	}

	char message[255];

	sprintf(message,"ComTest completed\r\n");

	if (!WriteFile(comport,message,strlen(message),&written,NULL))
	{
		DWORD err=GetLastError();

		_tprintf(TEXT("WriteFile error: %d(%08X)\r\n"),err,err);
		return err;
	}

	CloseHandle(comport);
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc!=2)
	{
		PrintUsageMessage();
		return -1;
	}

	int maxports=_ttoi(argv[1]);

	if (maxports<1)
	{
		PrintUsageMessage();
		return -1;
	}

	HANDLE	ports[9];
	int		portcount=0;

	for (int i=1;i<=maxports;i++)
	{
		TCHAR portname[32];

		_stprintf(portname,TEXT("\\.\COM%d"),i);
		
		HANDLE comport=CreateFile(portname,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,NULL,NULL);

		if (comport==INVALID_HANDLE_VALUE)
		{
			_tprintf(TEXT("Port COM%d: is not present\r\n"),i);
			continue;
		}

		if (InitCOMPort(comport,i))
		{
			_tprintf(TEXT("Port COM%d: cannot be initialized\r\n"),i);
			CloseHandle(comport);
			continue;
		}

		DWORD treadid;

		CloseHandle(CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)COMThreadProc,comport,0,&treadid));

		ports[portcount++]=comport;
	}

	_tprintf(TEXT("Set your COM port to 38400,8N1 to read output.\r\n"));
	_tprintf(TEXT("All characters will be echoed back.\r\n"));
	_tprintf(TEXT("Test started, press a key to terminate\r\n"));
	
	getchar();

	for (;portcount>0;portcount--)
		CloseHandle(ports[portcount-1]);

	return 0;
}

