/*
 * serial port tester.
 * vi:ts=4:sw=4:
 * Copyright (c) 1995 ACKSYS SARL - all rights reserved.
 * Copying, changing, transferring to third party this source code is
 * prohibited without the written agreement of ACKSYS SARL <contact@acksys.fr>,
 * ZA Val Joyeux, 10 rue des Entrepreneurs, F-78450 Villepreux France.
 *
 * Demonstration des transferts HDLC et ASYNCHRONES.
 * Initialiser la voie avec MCXMODE avant de lancer ce programme.
 *
 * Exemple d'utilisation :
 *	mcxmode com3:64000,n,8,h,null
 *	start/realtime testloop com3 0 1024
 *
 * Historique
 * 1.0	testloop.c, sans version. Un seul port pour tx et rx. Les
 *		versions les plus anciennes ne supportent pas le parametrage par
 *		le biais des variables shell BAUD et TIMEOUTS.
 * 1.1	testtx.c, sans version. Ports TX et RX separes, syntaxe figee.
 * 1.2	syntaxe compatible entre TESTTX.C et TESTLOOP.C
 *		compteur 64 bits pour les bits transmis
 * 1.2.1 format d'affichage hexa des unsigned chars tronque a 8 bits
 *		plus de message de handshake trompeur après l'affichage de set baud 
 *		rts=hs testé n'importe ou dans la chaine BAUD, pas seulement à la fin	
 *		rappel des syntaxes alternatives ,x (xon) et ,p (physique) dans l'usage
 * 1.2.2 affichage du nom du port dans le titre de la fenetre et dans le
 *		message periodique
 * 1.3	support LINUX
 * 1.4	support des trames séparées par les etats BREAK
 *		ajout des flags /b /c /f /i /s /w
 * 1.4.1 meilleur (?) traitement des erreurs 995/998
 *		correction d'un bug qui faisait passer les ports ordinaires (non MCX)
 *		pour des ports synchrones, engendrant une erreur de calcul du débit
 * 1.4.2 option /n pour supprimer le comptage des trames
 *		option /rN pour grouper l'affichage des trames lues
 *		option /x pour asservir (synchroniser) l'émission à la réception
 *		priorité légèrement supérieure pour le thread de réception
 *		purge du buffer RX si overflow des buffers de la carte
 */
#define version "1.4.2"

#include <stdio.h>
#if LINUX	// defini sur la ligne de compilation
/* COUCHE DE COMPATIBILITE WINDOWS - DECLARATIONS */
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include "mcx.h"
typedef int HANDLE, BOOLEAN, LONG;
typedef int OVERLAPPED;
typedef unsigned long long ULONGLONG;
typedef unsigned long DWORD, ULONG;
typedef unsigned char UCHAR;
typedef void VOID, *PVOID, *LPVOID;
#define TRUE	1
#define FALSE	0
#define INVALID_HANDLE_VALUE	-1
#define FILE_FLAG_OVERLAPPED	0
#define DEV_PFX	"/dev/"
#define MCX_PFX	"mcx"
inline int GetLastError() { return errno; }
typedef void *LPTHREAD_START_ROUTINE;
#define CreateThread(n,z,fonc,arg,zz,ptid) pthread_create(ptid,NULL,fonc,arg)
#define IS_THREAD_ERR(x)	(x)
#define stricmp strcasecmp
#define SetConsoleTitle(x)
#define Sleep(x)	usleep((x)*1000)
#define FlushFileBuffers(x)
#define CloseHandle	close
/* FIN COUCHE DE COMPATIBILITE WINDOWS - DECLARATIONS */
#else
#include <windows.h>
#include <winioctl.h>
#include "mcc_mcx.h"
#define DEV_PFX	"\\\\.\\"
#define MCX_PFX	"MCX"
#define IS_THREAD_ERR(x)	!(x)
#endif

#define MINTAMPON	13	// 9 compteurs + espace + 'A' + CR + LF
#define MAXTAMPON	4095
#define TIME_DELTA	10

char initbuf[MAXTAMPON] = "123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz Test en cours..<=>";
char sendbuf[MAXTAMPON];
char recvbuf[MAXTAMPON+1];

HANDLE rxHfd;
HANDLE txHfd;
ULONG Len;
ULONG FrameCountToSend;
ULONG FrameIntervalLimit = 0;
ULONG SleepFlag = 0;
ULONG WriteFlag = 1;
ULONG ReadFlag = 10;
BOOLEAN FatalFlag = FALSE;
BOOLEAN QuietFlag = FALSE;
BOOLEAN TxFlowFlag = FALSE;
BOOLEAN ReallyQuietFlag = FALSE;
BOOLEAN NocountFlag = FALSE;
BOOLEAN BreakFlag = FALSE;
ULONG CountFlag = 0;
ULONG TimeoutRxTrame;
BOOLEAN AsyncMode;
int RxByteLen;
BOOLEAN MandatoryRead;
volatile ULONG RecvCount = 0; // RecvTask info tested by SendTask
OVERLAPPED WriteOverlap;
OVERLAPPED ReadOverlap;
char AcksysFile[20];
char *arg_rxcom;

void RecvTask(LPVOID *p);
void RecvInfoTask(LPVOID *p);
BOOLEAN IsAsynchronous(HANDLE hfd, int *pByteLen);
void InitPort();
HANDLE AcksysOpen(char *numvoie,int flags);
void InitWrite();
void InitRead();
void AsyncWrite(HANDLE hfd,char *str, DWORD len);
ULONG AsyncRead(HANDLE hfd,char *str, DWORD len);
void Fatal(void);

void usage(char *pgm)
{
	printf("\nTESTLOOP version %s\n\n", version);
	printf("usage: %s txchan [rxchan] [[count] length(>%d)]\n\t\t\t"
			"[/b] [/cN] [/f] [/iN] [/sN] [/wN]\n\n", pgm,MINTAMPON);
	printf("WARNING: spaces are required between /s /b /c... switches\n");
	printf("/q\tQuiet - shorter messages, less linefeeds\n");
	printf("/Q\tReally quiet - no stats messages, no desync messages\n");
	printf("/b\tsets Tx break state between frames (else MARK state)\n");
	printf("/f\terrors are fatal, R/W holds (else continue on error)\n");
	printf("/iN\tcheck that async frames intervals < N ms (default /sN)\n");
	printf("/n\tDo not increment frame counter\n");
	printf("/cN\tsets start value for frame counter to N (else 0)\n");
	printf("/sN\twaits N ms between frames (else no delay)(default 100)\n");
	printf("/rN\tperiod of '*' letter report (else 10) (default 10)\n");
	printf("/wN\tperiod of 'w' letter report (else 1) (default 10)\n");
	printf("/x\tTx flow control by on received frames counter + 10\n");
	printf("\n");
	printf("set BAUD=(MODE syntax) to initialize the port state, i.e.:\n");
	printf("  set BAUD=9600,n,8,1                   no handshake\n");
	printf("  set BAUD=9600,n,8,1,x                 xoff handshake\n");
	printf("  set BAUD=9600,n,8,1,p                 hardware handshake\n");
	printf("  set BAUD=baud=9600 parity=n rts=hs... new syntax\n");
	exit(1);
}

#define ARG_PGM	argv[0]
#define ARG_TX	argv[1]
#define ARG_RX	argv[2]
#define ARG_NB	argv[3]
#define ARG_LEN	argv[4]
main(int argc,char **argv)
{
	HANDLE thd;
	DWORD tid;
	DWORD count;
	ULONG i;
	char *arg_pgm = ARG_PGM;
	char *arg_txcom = ARG_TX;
	char argStr[200];

	if(argc <= 2 || isdigit(ARG_RX[0])) {
		// il manque RXCOM, décaler les autres paramètres
		argc++;
		argv--;
	}
	arg_rxcom = ARG_RX;

	/* valeurs par defaut */
	Len = strlen(initbuf);
	FrameCountToSend = 10;

	while(argv[argc-1][0] == '/') {
		switch(argv[argc-1][1]) {
			case 'b': case 'B':
				BreakFlag = TRUE;
				break;
			case 'c': case 'C':
				CountFlag = atoi(argv[argc-1]+2);
				if (!CountFlag) CountFlag = 1000000L;
				break;
			case 'f': case 'F':
				FatalFlag = TRUE;
				break;
			case 'i': case 'I':
				FrameIntervalLimit = atoi(argv[argc-1]+2);
				if (!FrameIntervalLimit)
					FrameIntervalLimit = SleepFlag;
				break;
			case 'n': case 'N':
				NocountFlag = TRUE;
				break;
			case 'Q':
				ReallyQuietFlag = TRUE;
			case 'q':
				QuietFlag = TRUE;
				break;
			case 'r': case 'R':
				ReadFlag = atoi(argv[argc-1]+2);
				if (!ReadFlag) ReadFlag = 10L;
				break;
			case 's': case 'S':
				SleepFlag = atoi(argv[argc-1]+2);
				if (!SleepFlag) SleepFlag = 100L;
				break;
			case 'w': case 'W':
				WriteFlag = atoi(argv[argc-1]+2);
				if (!WriteFlag) WriteFlag = 10L;
				break;
			case 'x': case 'X':
				TxFlowFlag = TRUE;
				break;
		}
		argc--;
		
	}
	if(argc >= 4) {
		FrameCountToSend = atoi(ARG_NB);
		if(argc >= 5)
			Len = atoi(ARG_LEN);
	}
	if(FrameCountToSend < 0 || Len < MINTAMPON || Len > MAXTAMPON
	  || argc < 3 || argc > 5){
		usage(arg_pgm);
		exit(1);
	}
	if(FrameCountToSend == 0)
		FrameCountToSend = (ULONG)-1;

	if(strcmp(arg_rxcom,"-") == 0) {
			rxHfd = INVALID_HANDLE_VALUE;
	} else {
		rxHfd = AcksysOpen(arg_rxcom,FILE_FLAG_OVERLAPPED);
		InitPort(rxHfd);
		AsyncMode = IsAsynchronous(rxHfd, &RxByteLen);
	}
	if(stricmp(arg_rxcom, arg_txcom) != 0) {
		if(strcmp(arg_txcom,"-") == 0) {
			txHfd = INVALID_HANDLE_VALUE;
		} else {
			txHfd = AcksysOpen(arg_txcom,FILE_FLAG_OVERLAPPED);
			InitPort(txHfd);
			if(rxHfd == INVALID_HANDLE_VALUE)
				AsyncMode = IsAsynchronous(txHfd, &RxByteLen);
		}
	} else {
		txHfd = rxHfd;
	}
	sprintf(argStr,"testloop TX:%s RX:%s", arg_txcom, arg_rxcom);
	SetConsoleTitle(argStr);
	setbuf(stdout,NULL);
	printf("Demarrage du programme de test (mode %s, %d bits/byte)\n", 
		AsyncMode ? "Asynchrone" : "Synchrone", RxByteLen);
	printf("chaque 'w' indique %d trame(s) ecrite par WriteFile\n",
			WriteFlag);
	printf("chaque '*' indique %d trame(s) recue(s) en sequence\n",
			ReadFlag);
	// Pourquoi ? pour reduire les ralentissements dus a l'affichage
	// (surtout sensibles a tres haute vitesse). On peut afficher toutes
	// les trames recues si on prefere.
	printf("chaque '?' indique un timeout sur ReadFile\n");
	printf("les compteurs d'erreurs indiquent:\n");
	printf("\t- erreur [type] de trame physique (parite, stop, CRC...)\n");
	printf("\t\t1=OVH 2=FRM 4=OVS 8=PAR 10=BRK 20=TXFULL "
			"40=other 80=delay\n");
	printf("\t- err sequencement des trames\n");
	printf("\t- trames incompletes\n");
	printf("les erreurs de sequencement sont signalees par un message\n");

	if(rxHfd != INVALID_HANDLE_VALUE) {
		thd = CreateThread(NULL,0,
			(LPTHREAD_START_ROUTINE)RecvTask,(LPVOID*)rxHfd,0,&tid);
		if(IS_THREAD_ERR(thd)){
			printf("err thread %d\n",GetLastError());
			exit(1);
		}
	}

	if(txHfd != INVALID_HANDLE_VALUE) {
#if !LINUX
		if(txHfd != rxHfd) {
			thd = CreateThread(NULL,0,
				(LPTHREAD_START_ROUTINE)RecvInfoTask,(LPVOID*)txHfd,0,&tid);
			if(thd == NULL){
				printf("err thread rx-on-tx %d\n",GetLastError());
				exit(1);
			}
		}
#endif

		printf("Demarrage de la tache d'ecriture\n");
		InitWrite();
		Sleep(200);

		strcpy(sendbuf, initbuf);
		for(i=strlen(sendbuf); i<Len-2; i++)
			sendbuf[i] = '!';
		sendbuf[Len-2] = '\r';
		sendbuf[Len-1] = '\n';
		sendbuf[Len-0] = '\0';

		for(i=0;i<FrameCountToSend;i++)
		{
			if (BreakFlag) { Sleep(10); SetCommBreak(txHfd); }
			if (SleepFlag) Sleep(SleepFlag);
			sprintf(sendbuf,"%09d",NocountFlag?CountFlag:(i+CountFlag));
			sendbuf[9] = ' ';
			if (TxFlowFlag) {
				while ((ULONG)(i-RecvCount) > 10)
					;
			}
			if (BreakFlag) ClearCommBreak(txHfd);
			AsyncWrite(txHfd,sendbuf,Len);
		}
		printf("Fin du test\n");
	} else {
		printf("Pas d'ecriture dans ce test\n");
	}
	printf("Tapez [Entree] ");
	while(getchar()!='\n');

	FlushFileBuffers(txHfd);
	CloseHandle(txHfd);
	exit(0);
}

void
Fatal(void)
{
	printf("\n===Error detected. Freezing program.===\n");
	FrameCountToSend = 0; // Stop send loop
	for (;;) Sleep(10000); // stop receive loop
}

void
RecvTask(LPVOID *p)
{
	ULONG startDate, nextDate, currentDate;
	ULONG instDataAmount;
	ULONGLONG dataAmount;
	ULONG errLinestateCount, errSequenceCount, errSyncCount, errLS;
	DWORD frameInterval;
	ULONG maxIntervalReached;
	ULONG hiscount;

	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
	Sleep(100);
	printf("Demarrage de la tache de lecture\n");
	InitRead();

	maxIntervalReached = 0;
	errLinestateCount = 0;
	errLS = 0;
	errSequenceCount = 0;
	errSyncCount = 0;
	startDate = time(NULL);
	nextDate = startDate+1;		// indique qu'on n'est pas synchro
	for(RecvCount = 0;;RecvCount++){
		LONG l;
		DWORD errs;

		recvbuf[10] = '\0';

		if (BreakFlag) {
			LONG ll,i;
			do {
				ll = AsyncRead(rxHfd,recvbuf,1);
				ClearCommError(rxHfd,&errs,NULL);
				//printf("%02x ",recvbuf[0]&0xff);
			} while (ll != 1 || !(errs & CE_BREAK));
			frameInterval = GetTickCount();
			l = AsyncRead(rxHfd, recvbuf, 1);
			frameInterval = GetTickCount() - frameInterval;
			frameInterval -= FrameIntervalLimit;
			if (RecvCount && FrameIntervalLimit
			  && (int)frameInterval > 20) {
				if (frameInterval > maxIntervalReached)
					maxIntervalReached = frameInterval;
				printf("DELAY %d ", frameInterval); errLS |= 0x80;
				if (FatalFlag) Fatal();
			}
			l = AsyncRead(rxHfd, recvbuf+1, Len-1) + 1;
			/*for(i=0;i<Len+1;i++)
				printf("%02x ",recvbuf[i]&0xff);
			printf("%d\n",l);*/
		} else {
			l = AsyncRead(rxHfd, recvbuf, Len);
		}
		if(l == 0 && MandatoryRead){
			printf("No data read !\n");
			exit(1);
		}
#if !LINUX
		ClearCommError(rxHfd,&errs,NULL);
		if (errs) {
			errLinestateCount++;
			if(errs&CE_OVERRUN){ printf("OVH"); errLS |= 1; }
			if(errs&CE_FRAME){ printf("FRM"); errLS |= 2; }
			if(errs&CE_RXOVER){ printf("OVS"); errLS |= 4; }
			if(errs&CE_RXPARITY){ printf("PAR"); errLS |= 8; }
			if(errs&CE_BREAK){ printf("BRK"); errLS |= 0x10; }
			if(errs&CE_TXFULL){ printf("TXF"); errLS |= 0x20; }
			if(errs & ~(CE_OVERRUN|CE_FRAME|CE_RXOVER
					|CE_RXPARITY|CE_BREAK|CE_TXFULL)) {
				printf("ERR%x ",errs); errLS |= 0x40;
			}
			if (FatalFlag) Fatal();
			if(errs&CE_RXOVER){
				PurgeComm(rxHfd,PURGE_RXCLEAR|PURGE_RXABORT);
			}
		}
#endif
		/* le numero de sequence est-il lisible ? */
		if( recvbuf[10] == 'A' ){
			sscanf(recvbuf,"%ld",&hiscount);
			if( hiscount != RecvCount ){
				if (RecvCount) {
					if (!QuietFlag) printf("<%*.*s>...\n",15,15,recvbuf);
					if (!ReallyQuietFlag) {
						if (QuietFlag)
							printf("<Err:me=%d him=%d>", RecvCount, hiscount);
						else
							printf("Desynchronisation mycount=%d hiscount=%d\n",
								RecvCount, hiscount);
					}
					errSequenceCount++;
					if (FatalFlag) Fatal();
				}
				RecvCount = hiscount;
				if (NocountFlag) RecvCount--;
			}
		}
		/* le paquet est-il correct ? */
		if (l != Len || recvbuf[l-1] != '\n') {
			UCHAR c = recvbuf[l-1];

			if( c>=' ' && c<0177)
				printf("<Len=%d,c='%c'>(resynch)\n",l,c);
			else
				printf("<Len=%d,c=%02x>(resynch)\n",l,c);
			if (FatalFlag) Fatal();
			if (AsyncMode && !BreakFlag) {
				int i;
				char *p;
				do {
					ClearCommError(rxHfd,&errs,NULL);
					i = AsyncRead(rxHfd,recvbuf,10);
					l += i;
					p = memchr(recvbuf,'\n',10);
					putchar('.');
				} while(!p);
				i = (p+1+Len) - (recvbuf+10);
				ClearCommError(rxHfd,&errs,NULL);
				l += AsyncRead(rxHfd,recvbuf,i);
			}
			errSyncCount++;
		} else {
			static int i;
			if(++i%ReadFlag == 0)
				putchar('*');
		}
		instDataAmount += l*RxByteLen;
		if(!AsyncMode){
			instDataAmount += 3*RxByteLen /* CRC + 1 FLAG */;
		}

		currentDate = time(NULL);
		if( currentDate >= nextDate )
		{
			if( nextDate == startDate+1 )
			{
				//
				// on synchronise maintenant sur le battement
				// de la seconde
				//
				startDate = currentDate;
				nextDate = currentDate + TIME_DELTA;
				dataAmount = instDataAmount = 0;
				continue;	// aucun calcul la 1ere fois
			}
			dataAmount += instDataAmount;
			if (!ReallyQuietFlag) {
				if (QuietFlag) {
					printf("\n<%d/%d in %d\", %d errs>",
						instDataAmount/(currentDate-(nextDate-TIME_DELTA)),
						(ULONG)(dataAmount/(currentDate-startDate))
						,(currentDate-startDate)/60
						,errLinestateCount
					);
				} else {
					printf("\n<%s: inst %d mean %d dt=%d minutes "
							"errors=%d[%x]/%d/%d>",
						arg_rxcom,
						instDataAmount/(currentDate-(nextDate-TIME_DELTA)),
						(ULONG)(dataAmount/(currentDate-startDate))
						,(currentDate-startDate)/60
						,errLinestateCount, errLS
						,errSequenceCount, errSyncCount
					);
				}
			}
			if (FrameIntervalLimit && maxIntervalReached)
				printf("iv: %d ", maxIntervalReached);

			instDataAmount = 0;
			nextDate = currentDate + TIME_DELTA;

			/*** prevent overflows ***/
			if(dataAmount < 0)
			{
				dataAmount = 0;
				startDate = currentDate;
			}
		}
	}
	/* thread exit */
}

#if !LINUX
void
RecvInfoTask(LPVOID *p)
{
	static OVERLAPPED ReadOverlap;	// static pour init à zéro

	Sleep(50);
	printf("Demarrage de la tache de lecture sur port ecriture\n");

	// InitRead
	ReadOverlap.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if(ReadOverlap.hEvent == NULL) {
		printf("Event failed, code %d\n",GetLastError());
		exit(1);
	}

	for(;;) {
		char rxbuf[38];
		DWORD count;

		if(!ReadFile(txHfd,rxbuf,38,&count,&ReadOverlap)) {
			int lasterror = GetLastError();
			if(lasterror != ERROR_IO_PENDING) {
				printf("Read TX failed, code %d\n",lasterror);
				exit(1);
			} else {
				if(!GetOverlappedResult(txHfd, &ReadOverlap, &count,TRUE))
					printf("GetOver TXrd failed, code %d\n",
						GetLastError());
				ResetEvent(ReadOverlap.hEvent);
			}
		}

		if(count > 0) {
			int i;
			SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
			putchar('<');
			for(i=0; i<count; i++) {
				if(rxbuf[i] >= ' ') putchar(rxbuf[i]);
				else printf("%%%02x",(UCHAR)rxbuf[i]);
			}
			putchar('>');
			SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
		}
	}
	/* thread exit */
}
#endif

BOOLEAN
IsAsynchronous(HANDLE hfd, int *pByteLen)
{
#if LINUX
	int i;
	Mcx_Line_CONFIGURE Sync;
	int j;

	if (ioctl(hfd, MCX_IOC_GETCONF, &Sync) < 0) {
		*pByteLen = 10;
		return TRUE; // peut-etre un port serie standard
	}
	*pByteLen = Sync.Protocole == MCX_ASYNC_CHAR ? 10 : 8;
	return Sync.Protocole == MCX_ASYNC_CHAR;
#else
	MCX_SYNCHRONIZATION_PARAMETERS Sync;
	DWORD count;
	DCB Dcb;

	if (DeviceIoControl(hfd, IOCTL_SERIAL_GET_SYNC_STATE,
			NULL, 0, &Sync, sizeof(Sync), &count, NULL))
		if (Sync.SynchronousMode != MCX_SYNC_CHAR) {
			*pByteLen = 8;
			return FALSE;
		}

	if(!GetCommState(hfd,&Dcb)) {
		printf("GetCommState '%s' failed, code %d\n",
			AcksysFile,GetLastError());
	}
	*pByteLen = Dcb.ByteSize + 2 + (Dcb.Parity == NOPARITY ? 0 : 1);

	return TRUE;
#endif
}

void
InitPort(HANDLE hfd)
{
#if !LINUX
	//
	// Le timeout a 9600 bauds met en evidence les cancellableIrp et
	// le crash en v1.6.2 et v1.6.3
	//
	COMMTIMEOUTS Timeouts;
	DCB Dcb;
	int n1, n2, n3, n4, n5;
	char *envString;

	envString = getenv("TIMEOUTS");
	n1 = n2 = n3 = n4 = n5 = 0;
	if(envString){
		printf("using SET TIMEOUTS=%s (RIT,RTTM,RTTC,WTTM,WTTC)\n",
			envString);
		sscanf(envString, "%d %d %d %d %d",&n1,&n2,&n3,&n4,&n5);
		MandatoryRead = envString[strlen(envString)-1] == '!';
	}
	Timeouts.ReadIntervalTimeout = n1;
	Timeouts.ReadTotalTimeoutMultiplier = n2;
	Timeouts.ReadTotalTimeoutConstant = n3;
	Timeouts.WriteTotalTimeoutMultiplier = n4;
	Timeouts.WriteTotalTimeoutConstant = n5;

	if(!SetCommTimeouts(hfd,&Timeouts)) {
		printf("SetCommTimeouts '%s' failed, code %d\n",
			AcksysFile,GetLastError());
	}

	if(!GetCommState(hfd,&Dcb)) {
		printf("GetCommState '%s' failed, code %d\n",
			AcksysFile,GetLastError());
	}
	envString = getenv("BAUD");
	if(envString){
		//int bugMode = !stricmp(envString+strlen(envString)-6,"rts=hs");
		int bugMode = (strstr(envString,"rts=hs") != NULL);

		if(bugMode)
			envString[strlen(envString)-6] = '\0';
		printf("using SET BAUD=%s (%s)\n",
			envString,
			bugMode ? "rts=hs seen & enforced" : "rts=hs not seen");

		if(!BuildCommDCB(envString,&Dcb)) {
			printf("BuidCommDCB '%s' failed, code %d\n",
				AcksysFile,GetLastError());
		}
		if(bugMode)
			Dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
		if(!SetCommState(hfd,&Dcb)) {
			printf("SetCommState '%s' failed, code %d\n",
				AcksysFile,GetLastError());
		}
	}
	n1 = Len*10;								// bits/trame
	n1 = (n1*1000+Dcb.BaudRate-1)/Dcb.BaudRate;	// ms/trame
	TimeoutRxTrame = 3*n1;						// timeout 3 trames completes
	if(TimeoutRxTrame < 1000) TimeoutRxTrame = 1000;	// au moins 1 seconde
	PurgeComm(hfd,PURGE_TXCLEAR|PURGE_RXCLEAR);
#endif
}

HANDLE
AcksysOpen(char *numvoie,int flags)
{
	HANDLE hfd;

	if (isdigit(*numvoie)) {
		strcpy(AcksysFile, DEV_PFX MCX_PFX);
		if(atoi(numvoie) < 10)
#if LINUX
			strcat(AcksysFile,"0");
#else
			strcat(AcksysFile,"10");
		else if(atoi(numvoie) < 100)
			strcat(AcksysFile,"1");
#endif
	} else if (*numvoie == '=') {
		strcpy(AcksysFile,"");
		numvoie++;
	} else {
		strcpy(AcksysFile, DEV_PFX);
	}
	strcat(AcksysFile,numvoie);

#if LINUX
	hfd = open(AcksysFile, O_RDWR);
#else
	hfd = CreateFile(AcksysFile,GENERIC_WRITE|GENERIC_READ,
				0,
				NULL,
				OPEN_EXISTING,
				flags,
				NULL);
#endif
	if (hfd == INVALID_HANDLE_VALUE) {
		printf("Open '%s' failed, code %d\n",AcksysFile,GetLastError());
		exit(1);
	}
	return hfd;
}

void
InitWrite()
{
#if !LINUX
	WriteOverlap.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if(WriteOverlap.hEvent == NULL)
		printf("Event failed, code %d\n",GetLastError());
#endif
}

void
InitRead()
{
#if !LINUX
	ReadOverlap.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if(ReadOverlap.hEvent == NULL)
		printf("Event failed, code %d\n",GetLastError());
#endif
}

void
AsyncWrite(HANDLE hfd,char *str, DWORD len)
{
	static int writeReport;
#if LINUX
	int count;

	count = write(hfd, str, len);
	if (count == -1) {
		printf("Write failed, code %d\n",GetLastError());
	} else {
		if(++writeReport%WriteFlag == 0)
			putchar('w');
	}
	if(len != count) {
		printf("Write: len=%d count=%d\n",len,count);
	}
#else
	DWORD count;
	COMSTAT cs;

	if( !WriteFile(hfd,str,len,&count,&WriteOverlap) )
	{
		int lasterror = GetLastError();
		if(lasterror != ERROR_IO_PENDING)
			printf("Write failed, code %d\n",GetLastError());
		else {
			if(++writeReport%WriteFlag == 0)
				putchar('w');
			if(!GetOverlappedResult(hfd, &WriteOverlap, &count,TRUE)) {
					DWORD errs;
					COMSTAT sta;
					ClearCommError(rxHfd,&errs,&sta);
			PurgeComm(hfd,PURGE_TXCLEAR|PURGE_TXABORT);
				printf("GetOver TX failed, code %d\n",GetLastError());
			}
		}
	}
	else if(len != count) {
		printf("Write: len=%d count=%d\n",len,count);
	}
	ResetEvent(WriteOverlap.hEvent);
#endif
}

ULONG
AsyncRead(HANDLE hfd,char *str, DWORD len)
{
#if LINUX
	int count = 0, part;

	// TODO: gerer un timeout
	do {
		part = read(hfd, str+count, len-count);
		//printf("|%*.*s|", part, part, str+count);
		if (part == -1) {
			printf("Read failed, code %d\n",GetLastError());
			break;
		}
		count += part;
	} while (part && count < len);
	if(len != count) {
		printf("Read: len=%d count=%d\n",len,count);
	}
#else
	DWORD count;
		static int deja_995 = 0;

	count=12345;
	ResetEvent(ReadOverlap.hEvent);
	if (!ReadFile(hfd,str,len,&count,&ReadOverlap)) {
		int lasterror = GetLastError();
		if (lasterror != ERROR_IO_PENDING) {
			DWORD errs;
			COMSTAT sta;
			ClearCommError(rxHfd,&errs,&sta);
		if ((lasterror == 995 && !deja_995) || lasterror != 995)
			printf("Read failed, code %d"
				", cce errs=%x %x Qi=%d o=%d\n",
					lasterror, errs, *(DWORD*)&sta,
					sta.cbInQue, sta.cbOutQue);
		if (lasterror == 995) deja_995 = 1;
		} else {
			int deja_affiche = 0;
			while(WaitForSingleObject(ReadOverlap.hEvent,TimeoutRxTrame)
					== WAIT_TIMEOUT) {
				switch(deja_affiche) {
				case 1:				// la 2eme fois affiche ???
					putchar(' ');
					putchar('?');
					putchar('?');
				case 0:				// la 1ere fois affiche ?
					putchar('?');
					deja_affiche++;
				default:;			// la 3eme fois n'affiche rien
				}
			}
			if(!GetOverlappedResult(hfd, &ReadOverlap, &count,TRUE)) {
			DWORD errs;
			COMSTAT sta;
			ClearCommError(rxHfd,&errs,&sta);
	PurgeComm(hfd,PURGE_RXCLEAR|PURGE_RXABORT);
				printf("GetOver RX failed, code %d\n",
					GetLastError());
			}
		}
	} else {
deja_995 = 0;
		if (len != count)
			printf("Read: len=%d count=%d\n",len,count);
	}
#endif

	return count;
}
