reman3/Rayman_X/cpa/tempgrp/NET/l0serial.c

3646 lines
118 KiB
C

/*
* universal multiplayer library level 0 implementation file
* transport layer: serial PC-windows 95
*
*
* remark: there is no little endian/big endian swap management within the low level communication thread
* as netlib currently exists only for pc, this is not a problem
*
*/
#include "warnings.h"
/*#define NETLIB_LEVEL0COM_TRACE_FULL*/
/*
* we need this one to fill the interface structure
* with pointers to the correct functions
*/
#include "PrivL0serial.h"
#include "PrivNetDef.h"
#include "L0GlDef.h"
#include "NetMemCo.h"
#include "NetEnd.h"
#include <NET\Netdebug.h>
#include <windows.h>
#ifdef NET_USE_DEBUG
#include <stdio.h>
#endif
#define LINK_WINDOW 4
#define LINK_TIMEOUT(link_rate) ((unsigned long)(10+1000*(LINK_WINDOW)*sizeof(tdstPacket)*8*1.25/link_rate))
#define LINK_LINKNUMBER (1+C_usSerialBufferSize/LINKSIZE)
/* Defines the max number of port :*/
#define C_uw_Net_L0PCWin95Serial_PortNumber ((unsigned short)5)
/* array of descriptors to access a physical channel. */
/* not static, because also used by the modem module */
tdstL0PCWin95SerialChannel gs_a_stL0PCWin95SerialChannels[C_uw_Net_L0PCWin95Serial_PortNumber];
long lSerialChannelsOnceInitialized=0;
/* Initialisation state flag */
static char gs_bL0PCWin95SerialInitState;
/* the baud rate at which the com ports are opened */
static unsigned long gs_ulL0PCWin95SerialBaudRate;
/*******************************************************************/
#if defined(NET_USE_DEBUG)
void vDisplayComQueue(LPCOMSTAT pComStat)
{
vDebugFormat(Net_C_Debug_Serial, "cbInQue :%d", pComStat->cbInQue);
vDebugFormat(Net_C_Debug_Serial, "cbOutQue :%d", pComStat->cbOutQue);
}
#endif /* NET_USE_DEBUG */
/*******************************************************************/
#if defined(NET_USE_DEBUG)
void vDisplayComStat(unsigned short uwFileNum, LPCOMSTAT pComStat)
{
vDebugFormat(uwFileNum, "fCtsHold :%d", pComStat->fCtsHold);
vDebugFormat(uwFileNum, "fDsrHold :%d", pComStat->fDsrHold);
vDebugFormat(uwFileNum, "fRlsdHold :%d", pComStat->fRlsdHold);
vDebugFormat(uwFileNum, "fXoffHold :%d", pComStat->fXoffHold);
vDebugFormat(uwFileNum, "fXoffSent :%d", pComStat->fXoffSent);
vDebugFormat(uwFileNum, "fEof :%d", pComStat->fEof);
vDebugFormat(uwFileNum, "fTxim :%d", pComStat->fTxim);
vDebugFormat(uwFileNum, "cbInQue :%d", pComStat->cbInQue);
vDebugFormat(uwFileNum, "cbOutQue :%d", pComStat->cbOutQue);
}
#endif /* NET_USE_DEBUG */
/*******************************************************************/
#if defined(NET_USE_DEBUG)
void vDisplayErrorState(unsigned short uwFileNum, unsigned long ulCommError)
{
char acString[150];
if(ulCommError) {
vDebugFormat(uwFileNum ,"erreur:0x%.8x", ulCommError);
if(ulCommError & CE_BREAK) sprintf(acString, "CE_BREAK ");
if(ulCommError & CE_DNS) sprintf(acString, "CE_DNS ");
if(ulCommError & CE_FRAME) sprintf(acString, "CE_FRAME ");
if(ulCommError & CE_IOE) sprintf(acString, "CE_IOE ");
if(ulCommError & CE_MODE) sprintf(acString, "CE_MODE ");
if(ulCommError & CE_OOP) sprintf(acString, "CE_OOP ");
if(ulCommError & CE_OVERRUN) sprintf(acString, "CE_OVERRUN ");
if(ulCommError & CE_PTO) sprintf(acString, "CE_PTO ");
if(ulCommError & CE_RXOVER) sprintf(acString, "CE_RXOVER ");
if(ulCommError & CE_RXPARITY) sprintf(acString, "CE_RXPARITY ");
if(ulCommError & CE_TXFULL) sprintf(acString, "CE_TXFULL ");
vDebugFormat(uwFileNum, "%s", acString);
}
}
#endif /* NET_USE_DEBUG */
/*******************************************************************/
/*
* threaded low level send/receive system
*/
/************************************************************************/
/* functions that manage the sending, receiving and eventual reemitting */
/* of numbered blocks of data, per channel. */
/************************************************************************/
/* static functions */
static void vBuildPacket(tdstL0PCWin95SerialChannel *, int, tdstPacket *, unsigned char);
static int iSendPacket(tdstL0PCWin95SerialChannel *, tdstPacket *);
static void vCompleteSendPacket(tdstL0PCWin95SerialChannel *);
int iCheckTimeOut(tdstL0PCWin95SerialChannel *);
static int iChooseSendSlot(tdstWindow *);
static unsigned short usComputeChecksum(tdstPacket *);
static unsigned char ucGetAckNumber(tdstWindow *);
static int iChooseRecvSlot(tdstWindow *, unsigned char);
static int iGetAckSendSlot(tdstWindow *, unsigned char);
static int iGetPacket(tdstL0PCWin95SerialChannel *, tdstPacket *);
static int iGetFreeSlot(tdstWindow *);
static void vNumberSlot(tdstL0PCWin95SerialChannel *, int);
static void vDoRecv(tdstL0PCWin95SerialChannel *);
static void vDoSend(tdstL0PCWin95SerialChannel *);
static int iUnloadSendBuffer(tdstL0PCWin95SerialChannel *);
static int iLoadRecvBuffer(tdstL0PCWin95SerialChannel *, tdstPacket *);
/************************************************************************/
/* functions for manipulating the output 'ring' buffer */
/************************************************************************/
#ifdef NET_USE_DEBUG
char acCouple[40];
#endif /* NET_USE_DEBUG */
#define BufferSpace(pstRing) (RINGBUFFERSIZE-1)
#define FreeSpace(pstRing) ( (RINGBUFFERSIZE - 1 - (pstRing)->ulHighMark + (pstRing)->ulLowMark) \
% RINGBUFFERSIZE )
#define BusySpace(pstRing) ( (RINGBUFFERSIZE + (pstRing)->ulHighMark - (pstRing)->ulLowMark) \
% RINGBUFFERSIZE )
/**********************************************************************/
void vInitRingBuffer(tdstRingBuffer *pstRing)
{
pstRing->ulLowMark=pstRing->ulHighMark=pstRing->ulSize=pstRing->ulBottom=0L;
pstRing->pcBigMsg=(char *)C_pNull;
}
/**********************************************************************/
/* copy a new block into the ring */
int iPushBlockRingBuffer(tdstRingBuffer *pstRing, char *pcBlock, unsigned long ulSize)
{
long lLowChunk, lHighChunk;
/* no test on the validity of the parameters are performed in this function */
/* the function is supposed to always be called with valid parameters */
/* put a block into the ring */
#ifdef NET_USE_DEBUG
sprintf(acCouple, "(%lu,%lu)>>\n", pstRing->ulLowMark, pstRing->ulHighMark);
vDebugSDump(Net_C_Debug_SBuffin, acCouple, pcBlock, ulSize);
#endif /* NET_USE_DEBUG */
lLowChunk=ulSize-(RINGBUFFERSIZE-pstRing->ulHighMark);
if(lLowChunk<0)
lLowChunk=0;
lHighChunk=ulSize-lLowChunk;
memcpy(&pstRing->acBuffer[pstRing->ulHighMark], pcBlock, lHighChunk);
if(lLowChunk>0)
memcpy( pstRing->acBuffer, &pcBlock[lHighChunk], lLowChunk);
pstRing->ulHighMark= (pstRing->ulHighMark + ulSize) % RINGBUFFERSIZE;
#ifdef NET_USE_DEBUG
sprintf(acCouple, "(%ld,%ld)", lLowChunk, lHighChunk);
vDebugFormat(Net_C_Debug_SBuffin, "%s", acCouple);
#endif /* NET_USE_DEBUG */
return eRingOk;
}
/**********************************************************************/
/* try to put a new message into the ring buffer */
/* making a distinction between 'small' messages */
/* and 'big' messages (who are bigger than the buffer size, and have */
/* to be cut in many chunks that fit into the buffer */
int iPushMessageRingBuffer(tdstRingBuffer *pstRing, char *pcBlock, unsigned long ulSize)
{
if(pstRing->pcBigMsg)
return eRingFull;
/* is the new message a big one ? */
if(ulSize>BufferSpace(pstRing)) { /* the message is a big one */
/* store the message */
pstRing->pcBigMsg=pcBlock;
pstRing->ulSize=ulSize;
/* prepare to put a chunk of it into the ring */
pstRing->ulBottom=ulSize=FreeSpace(pstRing);
/* if there is room in the ring, put a chunk of message into it */
if(FreeSpace(pstRing)>0)
iPushBlockRingBuffer(pstRing, pcBlock, ulSize);
return eRingBigOk; /* i.e. the caller shouldn't deallocate the message */
} else /* the message is a small one */
/* if there isn't enough room to store this block in the buffer, return */
if(FreeSpace(pstRing)<ulSize)
return eRingFull;
return iPushBlockRingBuffer(pstRing, pcBlock, ulSize);
}
/**********************************************************************/
/* get a block of data from the ring buffer */
/* if a big message is being sent, put a new chunk of it into the */
/* ring f needed */
int iPopBlockRingBuffer(tdstRingBuffer *pstRing, char *pcBlock, unsigned long ulSize)
{
long lLowChunk, lHighChunk;
long lFreeSpace, lLeftToCopy, lToCopy;
/* is there enough content in the buffer to fill the block ? */
if(BusySpace(pstRing)<ulSize) {
/* there are fewer bytes in the ring than requested */
/* if a big message is currently being transmitted, fill the ring with a chunk of it */
if(pstRing->pcBigMsg) {
lFreeSpace=FreeSpace(pstRing);
lLeftToCopy=pstRing->ulSize-pstRing->ulBottom;
if(lLeftToCopy<lFreeSpace)
lToCopy=lLeftToCopy;
else
lToCopy=lFreeSpace;
iPushBlockRingBuffer(pstRing, &pstRing->pcBigMsg[pstRing->ulBottom], lToCopy);
pstRing->ulBottom+=lToCopy;
/* if the big message has entirely been copied, then we can free the block */
if(pstRing->ulBottom==pstRing->ulSize) {
vFree(pstRing->pcBigMsg);
pstRing->pcBigMsg=(char *)C_pNull;
pstRing->ulBottom=pstRing->ulSize=0;
}
}
/* then again, how much data can we actually send ? */
if(BusySpace(pstRing)<ulSize)
ulSize=BusySpace(pstRing);
/* if the buffer is empty, there is nothing to do */
if(ulSize==0)
return -1;
}
/* transfer a block from the ring */
#ifdef NET_USE_DEBUG
sprintf(acCouple, "(%lu,%lu)<<\n", pstRing->ulLowMark, pstRing->ulHighMark);
vDebugFormat(Net_C_Debug_SBuffout, "%s", acCouple);
#endif /* NET_USE_DEBUG */
lLowChunk=ulSize-(RINGBUFFERSIZE-pstRing->ulLowMark);
if(lLowChunk<0)
lLowChunk=0;
lHighChunk=ulSize-lLowChunk;
memcpy(pcBlock, &pstRing->acBuffer[pstRing->ulLowMark], lHighChunk);
if(lLowChunk>0)
memcpy(&pcBlock[lHighChunk], pstRing->acBuffer, lLowChunk);
pstRing->ulLowMark= (pstRing->ulLowMark + ulSize) % RINGBUFFERSIZE;
#ifdef NET_USE_DEBUG
vDebugSDump(Net_C_Debug_SBuffout, "", pcBlock, ulSize);
#endif /* NET_USE_DEBUG */
/* return the number of bytes effectively written to caller's block */
return ulSize;
}
/************************************************************************/
/* functions for manipulating the input 'link' buffer, and reassemble */
/* blocks in sequence */
/************************************************************************/
/*************************************************************************/
/* tell if a sequence number is lower than another due to overflow */
int iBeforeSequence(unsigned char seq1, unsigned char seq2)
{
unsigned char diff;
diff=seq2-seq1;
/* return true iff the distance from seq1 to seq2 is lower than ???
and greater than zero*/
return diff!=0 && diff<128;
}
/*************************************************************************/
int iNextSequence(unsigned char seq1, unsigned char seq2)
{
return (seq1+1==INVALID_SLOT)?seq2==0:seq2==seq1+1;
}
/*************************************************************************/
unsigned char ucIncSequence(unsigned char seq1)
{
return (seq1+1==INVALID_SLOT)?0:seq1+1;
}
/*************************************************************************/
unsigned char ucDiffSequence(unsigned char seq2, unsigned char seq1)
{
/* the result is correct when seq1 is BEFORE seq2 */
if(seq2>=seq1)
return seq2-seq1; /* seq1 and seq2 are of the same side of the INVALID_SLOT */
else
return seq2-seq1-1; /* because 0xff is the INVALID_SLOT */
}
/*************************************************************************/
void vInitLinkBuffer(tdstLinkBuffer *pstLinkBuffer, int iNLinks)
{
int i;
pstLinkBuffer->pstLink=(tdstLink *)pMalloc(iNLinks*sizeof(tdstLink));
if(pstLinkBuffer->pstLink==(tdstLink *)C_pNull) {
pstLinkBuffer->iNLinks=0;
return;
}
pstLinkBuffer->iNLinks=iNLinks;
for(i=0; i<pstLinkBuffer->iNLinks; i++) {
pstLinkBuffer->pstLink[i].ucSeqN=INVALID_SLOT; /* unused sequence number */
pstLinkBuffer->pstLink[i].iNextIndex=-1;
}
pstLinkBuffer->iHead=-1;
pstLinkBuffer->ucAwaitedSeqN=0;
pstLinkBuffer->iNFreeLinks=iNLinks;
}
/*************************************************************************/
void vDeleteLinkBuffer(tdstLinkBuffer *pstLinkBuffer)
{
if(pstLinkBuffer->pstLink!=(tdstLink *)C_pNull) {
vFree((tdpPointer)pstLinkBuffer->pstLink);
pstLinkBuffer->iNLinks=0;
}
}
/*************************************************************************/
int iGetLink(tdstLinkBuffer *pstLinkBuffer)
{
int i;
if(pstLinkBuffer->iNFreeLinks==0) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "GetLink():no free links !");
#endif /* NET_USE_DEBUG */
return -1;
}
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "GetLink():%d free", pstLinkBuffer->iNFreeLinks);
#endif /* NET_USE_DEBUG */
for(i=0; i<pstLinkBuffer->iNLinks; i++)
if(pstLinkBuffer->pstLink[i].ucSeqN==INVALID_SLOT) {
pstLinkBuffer->iNFreeLinks--;
return i;
}
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "GetLink():shouldn't get there !");
#endif /* NET_USE_DEBUG */
return -1;
}
/*************************************************************************/
void vFreeLink(tdstLinkBuffer *pstLinkBuffer, int iSlot)
{
#ifdef NET_USE_DEBUG
if(pstLinkBuffer->pstLink[iSlot].ucSeqN==INVALID_SLOT) {
vDebugFormat(Net_C_Debug_SLinks, "FreeLink(invalid slot): shouldn't happen !");
return;
}
if(pstLinkBuffer->iNFreeLinks==pstLinkBuffer->iNLinks) {
vDebugFormat(Net_C_Debug_SLinks, "FreeLink(): all links free, shouldn't happen !");
return;
}
#endif /* NET_USE_DEBUG */
pstLinkBuffer->pstLink[iSlot].ucSeqN=INVALID_SLOT;
pstLinkBuffer->iNFreeLinks++;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "FreeLink():%d free", pstLinkBuffer->iNFreeLinks);
#endif /* NET_USE_DEBUG */
}
/*************************************************************************/
void vMakeLink(tdstLinkBuffer *pstLinkBuffer, int iLinkIndex, unsigned char ucSeqN, unsigned char ucSize, char *pcData, int iNextIndex)
{
pstLinkBuffer->pstLink[iLinkIndex].ucSeqN=ucSeqN;
pstLinkBuffer->pstLink[iLinkIndex].ucSize=ucSize;
memcpy(pstLinkBuffer->pstLink[iLinkIndex].acData, pcData, ucSize);
pstLinkBuffer->pstLink[iLinkIndex].iNextIndex=iNextIndex;
}
/*************************************************************************/
int iPushBlockLinkBuffer(tdstLinkBuffer *pstLinkBuffer, unsigned char ucSeqN, unsigned char ucSize, char *pcData)
/* insert a new block in the list according to its sequence number */
/* and maintain the first sequence length */
{
int iPrevious, iCurrent, iNew;
unsigned char ucHole; /* number of missing links between the head packet and the new packet */
/* if the block sequence number is smaller than the awaited one, then this is a block */
/* that has been reemitted by the remote side because we acknowledged it too late */
/* just ignore it */
if(iBeforeSequence(ucSeqN, pstLinkBuffer->ucAwaitedSeqN))
return eLinkOk;
if(pstLinkBuffer->iHead==-1) { /* no list exists */
/* paranoid: test whether there are enough links to complete the sequence */
if(((unsigned char)ucSeqN-pstLinkBuffer->ucAwaitedSeqN)>pstLinkBuffer->iNFreeLinks) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "refusing packet %d to leave room (1)", ucSeqN);
#endif /* NET_USE_DEBUG */
return eLinkFull;
}
iNew=iGetLink(pstLinkBuffer);
if(iNew==-1)
return eLinkFull; /* shouldn't happen */
vMakeLink(pstLinkBuffer, iNew, ucSeqN, ucSize, pcData, -1);
pstLinkBuffer->iHead=iNew;
pstLinkBuffer->iFirstSequence=1;
pstLinkBuffer->ulFirstSequenceLength=ucSize;
return eLinkOk;
} else {
/* run through the list until we find a link whose sequence number */
/* is >= than the new sequence number */
ucHole=0;
iPrevious=-1;
iCurrent=pstLinkBuffer->iHead;
while(iCurrent!=-1 && iBeforeSequence(pstLinkBuffer->pstLink[iCurrent].ucSeqN, ucSeqN)) {
if(iPrevious!=-1 && !iNextSequence(pstLinkBuffer->pstLink[iPrevious].ucSeqN, pstLinkBuffer->pstLink[iCurrent].ucSeqN))
ucHole+=ucDiffSequence(pstLinkBuffer->pstLink[iCurrent].ucSeqN, pstLinkBuffer->pstLink[iPrevious].ucSeqN)-1;
iPrevious=iCurrent;
iCurrent=pstLinkBuffer->pstLink[iCurrent].iNextIndex;
}
if(iPrevious!=-1) {
ucHole+=ucDiffSequence(ucSeqN, pstLinkBuffer->pstLink[iPrevious].ucSeqN)-1;
/* if this packet is after the head packet and if we need all the remaining free links */
/* to complete the sequence before this new packet then refuse this new packet */
if(ucHole + ucDiffSequence(pstLinkBuffer->pstLink[pstLinkBuffer->iHead].ucSeqN , pstLinkBuffer->ucAwaitedSeqN)
>= pstLinkBuffer->iNFreeLinks) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "refusing packet %d to leave room, hole=%d", ucSeqN, ucHole);
#endif /* NET_USE_DEBUG */
return eLinkFull;
}
}
if(iCurrent==-1) {
/* the new sequence number is greater than all others */
/* insert the new block at the tail of the list */
iNew=iGetLink(pstLinkBuffer);
if(iNew==-1)
return eLinkFull; /* shouldn't happen */
vMakeLink(pstLinkBuffer, iNew, ucSeqN, ucSize, pcData, -1);
pstLinkBuffer->pstLink[iPrevious].iNextIndex=iNew;
if(ucHole==0/* && iNextSequence(pstLinkBuffer->pstLink[iPrevious].ucSeqN, ucSeqN)*/) {
pstLinkBuffer->iFirstSequence++;
pstLinkBuffer->ulFirstSequenceLength+=ucSize;
}
return eLinkOk;
}
if(pstLinkBuffer->pstLink[iCurrent].ucSeqN==ucSeqN) {
/* we already have a block with the same sequence number */
return eLinkOk;
}
/* the list contains sequence numbers that are greater than the new one */
if(iPrevious==-1) {
/* all sequence numbers are greater than the new one */
/* insert the new block at the head of the list */
iNew=iGetLink(pstLinkBuffer);
if(iNew==-1)
return eLinkFull; /* shouldn't happen */
vMakeLink(pstLinkBuffer, iNew, ucSeqN, ucSize, pcData, iCurrent);
pstLinkBuffer->iHead=iNew;
if(iNextSequence(ucSeqN, pstLinkBuffer->pstLink[iCurrent].ucSeqN)) {
pstLinkBuffer->iFirstSequence++;
pstLinkBuffer->ulFirstSequenceLength+=ucSize;
} else {
pstLinkBuffer->iFirstSequence=1;
pstLinkBuffer->ulFirstSequenceLength=ucSize;
}
return eLinkOk;
}
/* insert the new block in the middle of the list */
iNew=iGetLink(pstLinkBuffer);
if(iNew==-1)
return eLinkFull; /* shouldn't happen */
vMakeLink(pstLinkBuffer, iNew, ucSeqN, ucSize, pcData, iCurrent);
pstLinkBuffer->pstLink[iPrevious].iNextIndex=iNew;
iCurrent=iNew;
if(ucHole==0)
while( iCurrent!=-1
&& iNextSequence(pstLinkBuffer->pstLink[iPrevious].ucSeqN,
pstLinkBuffer->pstLink[iCurrent].ucSeqN)) {
pstLinkBuffer->iFirstSequence++;
pstLinkBuffer->ulFirstSequenceLength+=pstLinkBuffer->pstLink[iCurrent].ucSize;
iPrevious=iCurrent;
iCurrent=pstLinkBuffer->pstLink[iCurrent].iNextIndex;
}
return eLinkOk;
}
}
/*************************************************************************/
int iPopDataLinkBuffer(tdstLinkBuffer *pstLinkBuffer, char *pcData, unsigned long ulSize)
{
int iCurrent, iPrevious;
unsigned long ulBytesCopied, ulLeftToCopy;
char cEnd;
iCurrent=pstLinkBuffer->iHead;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "Head:%d, SeqN:%d/%d, FirstSeq:%d Size:%lu ",
iCurrent, pstLinkBuffer->ucAwaitedSeqN, pstLinkBuffer->pstLink[iCurrent].ucSeqN,
pstLinkBuffer->iFirstSequence,
ulSize);
#endif /* NET_USE_DEBUG */
if( iCurrent==-1
|| pstLinkBuffer->pstLink[iCurrent].ucSeqN!=pstLinkBuffer->ucAwaitedSeqN
|| (unsigned long)(LINKSIZE*pstLinkBuffer->iFirstSequence)<ulSize
|| pstLinkBuffer->ulFirstSequenceLength<ulSize)
return eLinkEmpty;
/* now we know that there is enough data: copy it into the caller's buffer */
ulBytesCopied=0;
cEnd=0;
while (pstLinkBuffer->pstLink[iCurrent].ucSize<(ulLeftToCopy=ulSize-ulBytesCopied)) {
/* the whole slot gets copied */
memcpy(&pcData[ulBytesCopied], pstLinkBuffer->pstLink[iCurrent].acData, pstLinkBuffer->pstLink[iCurrent].ucSize);
ulBytesCopied+=pstLinkBuffer->pstLink[iCurrent].ucSize;
pstLinkBuffer->iFirstSequence--;
pstLinkBuffer->ulFirstSequenceLength-=pstLinkBuffer->pstLink[iCurrent].ucSize;
vFreeLink(pstLinkBuffer, iCurrent);
iCurrent=pstLinkBuffer->pstLink[iCurrent].iNextIndex;
#ifdef NET_USE_DEBUG
if(iCurrent==-1)
vDebugFormat(Net_C_Debug_SLinks, "iCurrent==-1 ???");
#endif /* NET_USE_DEBUG */
}
/* this is the last slot to copy */
memcpy(&pcData[ulBytesCopied], pstLinkBuffer->pstLink[iCurrent].acData, ulLeftToCopy);
pstLinkBuffer->pstLink[iCurrent].ucSize-=(unsigned char)ulLeftToCopy;
pstLinkBuffer->ulFirstSequenceLength-=ulLeftToCopy;
if(pstLinkBuffer->pstLink[iCurrent].ucSize==0) /* nothing left in slot */ {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "exact match");
#endif /* NET_USE_DEBUG */
pstLinkBuffer->iFirstSequence--;
pstLinkBuffer->ucAwaitedSeqN=ucIncSequence(pstLinkBuffer->pstLink[iCurrent].ucSeqN);
vFreeLink(pstLinkBuffer, iCurrent);
iCurrent=pstLinkBuffer->pstLink[iCurrent].iNextIndex;
} else { /* move the remaining bytes to the beginning of the slot */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SLinks, "no match");
#endif /* NET_USE_DEBUG */
memmove(pstLinkBuffer->pstLink[iCurrent].acData, pstLinkBuffer->pstLink[iCurrent].acData+ulLeftToCopy, pstLinkBuffer->pstLink[iCurrent].ucSize);
pstLinkBuffer->ucAwaitedSeqN=pstLinkBuffer->pstLink[iCurrent].ucSeqN;
}
pstLinkBuffer->iHead=iCurrent;
/* if we ate the whole first sequence, compute the length of the new one */
if(pstLinkBuffer->iFirstSequence==0) {
cEnd=iCurrent==-1;
#ifdef NET_USE_DEBUG
if(!cEnd)
vDebugFormat(Net_C_Debug_SLinks, "first sequence exists");
#endif /* NET_USE_DEBUG */
while(!cEnd) {
pstLinkBuffer->iFirstSequence++;
pstLinkBuffer->ulFirstSequenceLength+=pstLinkBuffer->pstLink[iCurrent].ucSize;
iPrevious=iCurrent;
iCurrent=pstLinkBuffer->pstLink[iCurrent].iNextIndex;
cEnd=iCurrent==-1 ||
!iNextSequence( pstLinkBuffer->pstLink[iPrevious].ucSeqN,
pstLinkBuffer->pstLink[iCurrent].ucSeqN);
}
}
return eLinkOk;
}
/************************************************************************/
unsigned long ulLengthLinkBuffer(tdstLinkBuffer *pstLinkBuffer)
{
if(pstLinkBuffer->iHead==-1 ||
pstLinkBuffer->pstLink[pstLinkBuffer->iHead].ucSeqN!=pstLinkBuffer->ucAwaitedSeqN)
return 0L;
return pstLinkBuffer->ulFirstSequenceLength;
}
/************************************************************************/
/* global variables for the threaded control system */
static HANDLE gs_hL0PCWin95SerialThread;
static DWORD gs_dwL0PCWin95SerialThreadID;
static HANDLE gs_hL0PCWin95SerialIOToMain; /* used to synchronize thread termination */
static HANDLE gs_hL0PCWin95SerialMainToIO;
static int gs_iNEvents;
HANDLE gs_ahWaitHandles[1+2*C_uw_Net_L0PCWin95Serial_PortNumber];
tduwNetChannel gs_auwChannels[1+2*C_uw_Net_L0PCWin95Serial_PortNumber];
unsigned char gs_aucIsReadEvent[1+2*C_uw_Net_L0PCWin95Serial_PortNumber];
void vWorker(DWORD *);
void vLaunchThread(void);
void vDeleteThread(void);
void vGetThreadControl(void);
void vGiveThreadControl(void);
void vInitThreadController(void);
void vAddThreadClient(tduwNetChannel uwChannel);
void vRemoveThreadClient(tduwNetChannel uwChannel);
/************************************************************************/
void vLaunchThread(void)
{
long lErrorCode;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "Block size:%d bytes",
BLOCKSIZE);
#endif /* NET_USE_DEBUG */
gs_hL0PCWin95SerialThread=CreateThread(
NULL, /* no security attributes */
0, /* use default stack size */
(LPTHREAD_START_ROUTINE) vWorker, /* thread function */
NULL, /* argument to thread function */
CREATE_SUSPENDED,
&gs_dwL0PCWin95SerialThreadID);
if(gs_hL0PCWin95SerialThread==NULL) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "CreateThread(): erreur %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
exit(1);
}
if(SetThreadPriority(gs_hL0PCWin95SerialThread,
/*THREAD_PRIORITY_ABOVE_NORMAL*/
/*THREAD_PRIORITY_HIGHEST*/
THREAD_PRIORITY_TIME_CRITICAL)==FALSE) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "SetThreadPriority(): erreur %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
exit(1);
}
gs_hL0PCWin95SerialMainToIO=CreateEvent(NULL, FALSE, FALSE, NULL); /* auto reset event, initially set to non signaled */
if(gs_hL0PCWin95SerialMainToIO==INVALID_HANDLE_VALUE) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "MainToIO=CreateEvent(): erreur %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
}
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "MainToIO=CreateEvent()-> %ld", gs_hL0PCWin95SerialMainToIO);
#endif /* NET_USE_DEBUG */
gs_ahWaitHandles[0]=gs_hL0PCWin95SerialMainToIO;
gs_iNEvents=1;
gs_hL0PCWin95SerialIOToMain=CreateEvent(NULL, FALSE, FALSE, NULL); /* auto reset event, initially set to non signaled */
if(ResumeThread(gs_hL0PCWin95SerialThread)==-1) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "ResumeThread(): erreur %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
}
}
/*********************************************************/
void vDeleteThread()
{
TerminateThread(gs_hL0PCWin95SerialThread, 0);
CloseHandle(gs_hL0PCWin95SerialMainToIO);
CloseHandle(gs_hL0PCWin95SerialIOToMain);
gs_hL0PCWin95SerialThread = INVALID_HANDLE_VALUE;
gs_dwL0PCWin95SerialThreadID = 0;
}
/*********************************************************/
void vGetThreadControl()
{
SetEvent(gs_hL0PCWin95SerialMainToIO);
WaitForSingleObject(gs_hL0PCWin95SerialIOToMain, INFINITE);
}
/*********************************************************/
void vGiveThreadControl()
{
SetEvent(gs_hL0PCWin95SerialMainToIO);
}
/********************************************************************************/
/* this is the IO thread main function */
/* it blocks on an array of the events to monitor */
/* and performs the appropriate work when an event gets signaled */
/********************************************************************************/
void vWorker(DWORD *dwParam)
{
DWORD dwSignaledHandle;
int iChannel, iActivatedChannel;
long lErrorCode;
#ifdef NET_USE_DEBUG
unsigned long ulBlockTime;
int iEvent;
#endif /* NET_USE_DEBUG */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "WaitForMultipleObjects:");
for(iEvent=0; iEvent<gs_iNEvents; iEvent++)
vDebugFormat(Net_C_Debug_SThread, "%x ", gs_ahWaitHandles[iEvent]);
vDebugFormat(Net_C_Debug_SThread, "\n");
#endif /* NET_USE_DEBUG */
for(;;) {
/* block until an I/O operation is completed */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "blocking ");
ulBlockTime=GetTickCount();
#endif /* NET_USE_DEBUG */
dwSignaledHandle=WaitForMultipleObjects(gs_iNEvents, gs_ahWaitHandles, FALSE, 100);
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "debloque: %d ", dwSignaledHandle);
if(GetTickCount()-ulBlockTime>50)
vDebugFormat(Net_C_Debug_SThread, "long delay ?");
for(iEvent=0; iEvent<gs_iNEvents; iEvent++) {
switch(WaitForSingleObject(gs_ahWaitHandles[iEvent], 0)) {
case WAIT_ABANDONED:
vDebugFormat(Net_C_Debug_SThread, "%d ??????", iEvent);
break;
case WAIT_OBJECT_0:
vDebugFormat(Net_C_Debug_SThread, "%d ", iEvent);
break;
case WAIT_TIMEOUT:
break;
}
}
vDebugFormat(Net_C_Debug_SThread, "\n");
#endif /* NET_USE_DEBUG */
switch(dwSignaledHandle) {
case WAIT_FAILED:
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
lErrorCode=GetLastError();
vDebugFormat(Net_C_Debug_SThread, "WAIT_FAILED : erreur %ld", lErrorCode);
#endif
ExitThread(1);
break;
case WAIT_TIMEOUT:
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "WAIT_TIMEOUT: checking for timeouts");
#endif
for(iChannel=0; iChannel<C_uw_Net_L0PCWin95Serial_PortNumber; iChannel++)
if(iCheckTimeOut(&gs_a_stL0PCWin95SerialChannels[iChannel])) {
SetEvent(gs_a_stL0PCWin95SerialChannels[iChannel].stOverlappedWrite.hEvent);
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "%d timed out", iChannel);
#endif
}
break;
case WAIT_OBJECT_0:
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "WAIT_OBJECT_0: basculement");
#endif /* NET_USE_DEBUG */
/* give control to the main thread and wait for reactivation */
SetEvent(gs_hL0PCWin95SerialIOToMain);
WaitForSingleObject(gs_hL0PCWin95SerialMainToIO, INFINITE);
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "new event array:");
for(iEvent=0; iEvent<gs_iNEvents; iEvent++)
vDebugFormat(Net_C_Debug_SThread, "%x ", gs_ahWaitHandles[iEvent]);
vDebugFormat(Net_C_Debug_SThread, "\n");
#endif /* NET_USE_DEBUG */
break;
default:
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "dwSignaledHandle: %d ", dwSignaledHandle);
#endif
iActivatedChannel=gs_auwChannels[dwSignaledHandle-WAIT_OBJECT_0];
if(gs_aucIsReadEvent[dwSignaledHandle-WAIT_OBJECT_0])
vDoRecv(&gs_a_stL0PCWin95SerialChannels[iActivatedChannel]);
else
vDoSend(&gs_a_stL0PCWin95SerialChannels[iActivatedChannel]);
/* check for time outs on the other channels */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "timeout check ");
#endif
for(iChannel=0; iChannel<C_uw_Net_L0PCWin95Serial_PortNumber; iChannel++)
if(iChannel!=iActivatedChannel && iCheckTimeOut(&gs_a_stL0PCWin95SerialChannels[iChannel])) {
SetEvent(gs_a_stL0PCWin95SerialChannels[iChannel].stOverlappedWrite.hEvent);
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "%d timed out", iChannel);
#endif
}
}
}
}
/*********************************************************/
void vInitThreadController()
{
gs_hL0PCWin95SerialThread = INVALID_HANDLE_VALUE;
gs_dwL0PCWin95SerialThreadID = 0;
gs_hL0PCWin95SerialIOToMain = gs_hL0PCWin95SerialMainToIO = INVALID_HANDLE_VALUE;
gs_iNEvents=0;
}
/*********************************************************/
void vAddThreadClient(tduwNetChannel uwChannel)
{
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"AddThreadClient(%d) ", uwChannel);
#endif
/* if the channel is invalid, return */
if(uwChannel>=C_uw_Net_L0PCWin95Serial_PortNumber)
return;
/* if no thread is active, we have to create one first */
if(gs_hL0PCWin95SerialThread == INVALID_HANDLE_VALUE)
vLaunchThread();
vGetThreadControl();
/* add the channel's read and write events to the controller */
gs_ahWaitHandles[gs_iNEvents]=gs_a_stL0PCWin95SerialChannels[uwChannel].stOverlappedRead.hEvent;
gs_auwChannels[gs_iNEvents]=uwChannel;
gs_aucIsReadEvent[gs_iNEvents++]=1;
gs_ahWaitHandles[gs_iNEvents]=gs_a_stL0PCWin95SerialChannels[uwChannel].stOverlappedWrite.hEvent;
gs_auwChannels[gs_iNEvents]=uwChannel;
gs_aucIsReadEvent[gs_iNEvents++]=0;
vGiveThreadControl();
}
/*********************************************************/
void vRemoveThreadClient(tduwNetChannel uwChannel)
{
int iEvent;
int iFound;
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"RemoveThreadClient(%d) ", uwChannel);
#endif
/* if the channel is invalid return */
if(uwChannel>=C_uw_Net_L0PCWin95Serial_PortNumber)
return;
/* if no thread is active, there is nothing to do */
if(gs_hL0PCWin95SerialThread == INVALID_HANDLE_VALUE)
return;
vGetThreadControl();
/* remove the channel's read and write events from the controller */
iFound=0;
for(iEvent=1; iEvent<gs_iNEvents; iEvent+=2)
if(iFound) {
gs_ahWaitHandles[iEvent-2]=gs_ahWaitHandles[iEvent];
gs_auwChannels[iEvent-2]=gs_auwChannels[iEvent];
gs_aucIsReadEvent[iEvent-2]=gs_aucIsReadEvent[iEvent];
gs_ahWaitHandles[iEvent-1]=gs_ahWaitHandles[iEvent+1];
gs_auwChannels[iEvent-1]=gs_auwChannels[iEvent+1];
gs_aucIsReadEvent[iEvent-1]=gs_aucIsReadEvent[iEvent+1];
} else if(gs_auwChannels[iEvent]==uwChannel)
iFound=1;
if(iFound)
gs_iNEvents-=2;
if(gs_iNEvents==1) /* if no client remains, delete the thread */
vDeleteThread();
else
vGiveThreadControl(); /* if at least one client remains, let the thread work */
}
/*********************************************************/
void vBuildPacket(tdstL0PCWin95SerialChannel *pstChannel, int iSlot, tdstPacket *pstPacket, unsigned char ucAckNumber)
{
tdstSlot *pstSlot;
pstSlot=&pstChannel->stSendWindow.pstSlot[iSlot];
memcpy(pstPacket->acData, pstSlot->acData, BLOCKSIZE);
pstPacket->ucStartMark=STARTMARK;
pstPacket->ucSeqn=pstSlot->ucSeqn;
pstPacket->ucAckn=ucAckNumber;
pstPacket->ucSize=pstSlot->ucSize;
pstPacket->usChecksum=usComputeChecksum(pstPacket);
}
/*********************************************************/
int iSendPacket(tdstL0PCWin95SerialChannel *pstChannel, tdstPacket *pstPacket)
{
unsigned long ulWrittenBytes;
long lErrorCode;
EnterCriticalSection(&pstChannel->stChannelAccess);
if(pstChannel->ubf1IsSlotInUse) {
if(WriteFile(pstChannel->hCom, pstPacket, sizeof(tdstPacket), &ulWrittenBytes, &pstChannel->stOverlappedWrite)==FALSE) {
lErrorCode=GetLastError();
if(lErrorCode!=ERROR_IO_PENDING) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "WriteFile(): error %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
exit(1);
}
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "sending (%d, %d)",pstPacket->ucSeqn, pstPacket->ucAckn);
#endif /* NET_USE_DEBUG */
pstChannel->ubf1WriteInProgress=1; /* the operation is executing asynchronously */
} else {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "sending (%d, %d) (finished)",pstPacket->ucSeqn, pstPacket->ucAckn);
#endif /* NET_USE_DEBUG */
pstChannel->ubf1WriteInProgress=0; /* the operation finished synchronously */
SetEvent(pstChannel->stOverlappedWrite.hEvent);
}
} else {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "channel not in use ???");
#endif /* NET_USE_DEBUG */
/* shouldn't happen ... */
ResetEvent(pstChannel->stOverlappedWrite.hEvent);
}
LeaveCriticalSection(&pstChannel->stChannelAccess);
return 1;
}
/*********************************************************/
void vCompleteSendPacket(tdstL0PCWin95SerialChannel *pstChannel)
{
unsigned long ulWrittenBytes;
unsigned long ulLineError;
long lErrorCode;
COMSTAT stComStat;
EnterCriticalSection(&pstChannel->stChannelAccess);
if(pstChannel->ubf1IsSlotInUse) {
if(GetOverlappedResult(pstChannel->hCom, &pstChannel->stOverlappedWrite, &ulWrittenBytes, FALSE)==FALSE) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "GetOverlappedResult(write): error %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
/*
exit(1);
on Windows NT on a biprocessor system, the control happens to come here
if I comment out the call to exit(), the system works anyway ...
*/
}
pstChannel->ubf1WriteInProgress=0;
#ifdef NET_USE_DEBUG
if(ulWrittenBytes!=sizeof(tdstPacket)) {
vDebugFormat(Net_C_Debug_SBlocks, "written bytes (%lu) != sizeof(tdstPacket) ???", ulWrittenBytes);
}
#endif /* NET_USE_DEBUG */
if(ClearCommError(pstChannel->hCom, &ulLineError, &stComStat)==FALSE) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "ClearCommError(): error %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
}
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "CompleteSendPacket(): OutQueue=%d", stComStat.cbOutQue);
/* report the error state */
if(ulLineError) {
vDisplayErrorState(Net_C_Debug_SBlocks, ulLineError);
}
if( stComStat.fCtsHold || stComStat.fDsrHold || stComStat.fRlsdHold
|| stComStat.fXoffHold || stComStat.fXoffSent || stComStat.fEof || stComStat.fTxim) {
vDisplayComStat(Net_C_Debug_SBlocks, &stComStat);
}
#endif /* NET_USE_DEBUG */
}
LeaveCriticalSection(&pstChannel->stChannelAccess);
}
/*********************************************************/
void vInitWindow(tdstWindow *pstWindow, int iSize, unsigned long ulTimeout)
{
int iSlot;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "InitWindow(): size: %d, timeout: %lu", iSize, ulTimeout);
#endif /* NET_USE_DEBUG */
pstWindow->iSize=iSize;
pstWindow->ulTimeout=ulTimeout;
pstWindow->pstSlot=(tdstSlot *)pMalloc(iSize*sizeof(tdstSlot));
memset(pstWindow->pstSlot, 0, iSize*sizeof(tdstSlot));
for(iSlot=0; iSlot<iSize; iSlot++) {
pstWindow->pstSlot[iSlot].ucState=FREE;
}
}
/*********************************************************/
void vDeleteWindow(tdstWindow *pstWindow)
{
if(pstWindow->pstSlot!=(tdstSlot *)C_pNull) {
vFree((tdpPointer)pstWindow->pstSlot);
pstWindow->pstSlot=(tdstSlot *)C_pNull;
pstWindow->iSize=0;
pstWindow->ulTimeout=0;
}
}
/*********************************************************/
int iGetFreeSlot(tdstWindow *pstWindow)
{
int i, iElectedSlot;
unsigned long ulOldestDate;
tdstSlot *astSendSlot;
astSendSlot=pstWindow->pstSlot;
ulOldestDate=0xffffffff;
iElectedSlot=INVALID_SLOT;
/* find a free slot */
for(i=0; i<pstWindow->iSize; i++)
if(astSendSlot[i].ucState==FREE) {
iElectedSlot=i;
break;
}
/* or, find the oldest acknowledged slot */
if(iElectedSlot==INVALID_SLOT)
for(i=0; i<pstWindow->iSize; i++)
if(astSendSlot[i].ucState==ACK && astSendSlot[i].ulDate<ulOldestDate) {
ulOldestDate=astSendSlot[i].ulDate;
iElectedSlot=i;
}
return iElectedSlot;
}
/*********************************************************/
void vNumberSlot(tdstL0PCWin95SerialChannel *pstChannel, int iSlot)
{
pstChannel->stSendWindow.pstSlot[iSlot].ucSeqn=pstChannel->ucSeqn;
pstChannel->ucSeqn= (pstChannel->ucSeqn+1) % 0xff; /* 0xff is reserved for 'no acknowledgement' */
}
/*********************************************************/
int iChooseSendSlot(tdstWindow *pstWindow)
{
int i, iElectedSlot;
unsigned long ulDate, ulOldestDate;
unsigned char ucLowestSeqn;
tdstSlot *astSendSlot;
astSendSlot=pstWindow->pstSlot;
ulDate=GetTickCount();
ulOldestDate=0xffffffff;
iElectedSlot=INVALID_SLOT;
/* find the slots that have timed out: they have to be sent again */
for(i=0; i<pstWindow->iSize; i++)
if(astSendSlot[i].ucState==TIMED_OUT || astSendSlot[i].ucState==SENT && astSendSlot[i].ulDate+pstWindow->ulTimeout<ulDate) {
astSendSlot[i].ucState=TIMED_OUT;
if(astSendSlot[i].ulDate<ulOldestDate) {
ulOldestDate=astSendSlot[i].ulDate;
iElectedSlot=i;
}
}
if(iElectedSlot!=INVALID_SLOT) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "<%d : time out>@%lu", astSendSlot[iElectedSlot].ucSeqn, ulDate);
#endif /* NET_USE_DEBUG */
return iElectedSlot;
}
/* find the slot to send with the lowest sequence number */
ucLowestSeqn=0xff;
iElectedSlot=INVALID_SLOT;
for(i=0; i<pstWindow->iSize; i++)
if(astSendSlot[i].ucState==TO_SEND && astSendSlot[i].ucSeqn<ucLowestSeqn) {
ucLowestSeqn=astSendSlot[i].ucSeqn;
iElectedSlot=i;
}
if(iElectedSlot==INVALID_SLOT) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "<no slot to send>");
#endif /* NET_USE_DEBUG */
}
return iElectedSlot;
}
/*********************************************************/
int iCheckTimeOut(tdstL0PCWin95SerialChannel *pstChannel)
{
unsigned long ulDate;
int i;
int iTimedOut;
EnterCriticalSection(&pstChannel->stChannelAccess);
iTimedOut=0;
if( pstChannel->ubf1IsSlotInUse && pstChannel->ubf1IsSlotConnected /* if the channel is invalid, return immediately */
&& !pstChannel->ubf1WriteInProgress /* if the channel is currently sending a message, return immediately */
) {
/* check whether a slot has timed out */
ulDate=GetTickCount();
for(i=0; i<pstChannel->stSendWindow.iSize; i++)
if(pstChannel->stSendWindow.pstSlot[i].ucState==SENT && pstChannel->stSendWindow.pstSlot[i].ulDate+pstChannel->stSendWindow.ulTimeout<ulDate) {
iTimedOut=1; /* this slot has timed out */
break;
}
}
LeaveCriticalSection(&pstChannel->stChannelAccess);
return iTimedOut;
}
/*********************************************************/
int iSlotsNotAck(tdstWindow *pstSendWindow)
{
int i, iCount;
/* compute the number of data send slots still requiring to be sent and/or acknowledged */
iCount=0;
for(i=0; i<pstSendWindow->iSize; i++)
if( pstSendWindow->pstSlot[i].ucSeqn!=INVALID_SLOT &&
( pstSendWindow->pstSlot[i].ucState==TO_SEND ||
pstSendWindow->pstSlot[i].ucState==SENT ||
pstSendWindow->pstSlot[i].ucState==TIMED_OUT ) )
iCount++;
return iCount;
}
/*********************************************************/
unsigned short usComputeChecksum(tdstPacket *pstPacket)
{
int i;
unsigned short usChecksum;
usChecksum=pstPacket->ucSeqn+pstPacket->ucAckn+pstPacket->ucSize;
for(i=0; i<BLOCKSIZE; i++)
usChecksum+=pstPacket->acData[i];
return usChecksum;
}
/*********************************************************/
unsigned char ucGetAckNumber(tdstWindow *pstWindow)
{
tdstSlot *astRecvSlot;
#define SECOND_METHOD
#ifdef FIRST_METHOD
int i, iOldestSlot;
unsigned long ulOldestDate;
astRecvSlot=pstWindow->pstSlot;
ulOldestDate=0xffffffff;
iOldestSlot=INVALID_SLOT;
/* first method: get the oldest received slot */
for(i=0; i<pstWindow->iSize; i++)
if(astRecvSlot[i].ucState==RECVD && astRecvSlot[i].ulDate<ulOldestDate) {
ulOldestDate=astRecvSlot[i].ulDate;
iOldestSlot=i;
}
if(iOldestSlot==INVALID_SLOT)
return INVALID_SLOT; /* 'no acknowledgement' */
else { /* mark the block as acknowledged and return its sequence number */
astRecvSlot[iOldestSlot].ucState=ACK;
return astRecvSlot[iOldestSlot].ucSeqn;
}
#endif /* FIRST_METHOD */
#ifdef SECOND_METHOD
/* second method: get the slot with lowest sequence number */
int i, iLowestSlot;
unsigned char ucLowestn;
astRecvSlot=pstWindow->pstSlot;
iLowestSlot=INVALID_SLOT;
for(i=0; i<pstWindow->iSize; i++)
if(astRecvSlot[i].ucState==RECVD) {
ucLowestn=astRecvSlot[i].ucSeqn;
iLowestSlot=i;
break;
}
for(i++; i<pstWindow->iSize; i++)
if(astRecvSlot[i].ucState==RECVD && iBeforeSequence(astRecvSlot[i].ucSeqn, ucLowestn)) {
ucLowestn=astRecvSlot[i].ucSeqn;
iLowestSlot=i;
}
if(iLowestSlot==INVALID_SLOT)
return INVALID_SLOT; /* 'no acknowledgement' */
else { /* mark the block as acknowledged and return its sequence number */
astRecvSlot[iLowestSlot].ucState=ACK;
return ucLowestn;
}
#endif /* SECOND_METHOD */
}
/*********************************************************/
int iChooseRecvSlot(tdstWindow *pstWindow, unsigned char ucSeqn)
{
int i, iOldestSlot;
unsigned long ulOldestDate;
tdstSlot *astRecvSlot;
char cNoFreeSlot;
#ifdef NET_USE_DEBUG
int n;
#endif /* NET_USE_DEBUG */
astRecvSlot=pstWindow->pstSlot;
#ifdef NET_USE_DEBUG
/* how many receive slots are waiting for acknowledgement ? */
for(i=0, n=0; i<pstWindow->iSize; i++)
if( astRecvSlot[i].ucState==RECVD )
n++;
vDebugFormat(Net_C_Debug_SBlocks, "%d received slots not yet ack", n);
#endif /* NET_USE_DEBUG */
cNoFreeSlot=1;
ulOldestDate=0xffffffff;
iOldestSlot=INVALID_SLOT;
/* find a slot with the same sequence number */
/* or take the oldest free slot */
for(i=0; i<pstWindow->iSize; i++)
if( astRecvSlot[i].ucState==RECVD && astRecvSlot[i].ucSeqn==ucSeqn )
return i;
for(i=0; i<pstWindow->iSize; i++)
if((astRecvSlot[i].ucState==ACK || astRecvSlot[i].ucState==FREE) && astRecvSlot[i].ulDate<ulOldestDate) {
cNoFreeSlot=0;
ulOldestDate=astRecvSlot[i].ulDate;
iOldestSlot=i;
}
#ifdef NET_USE_DEBUG
if(cNoFreeSlot)
vDebugFormat(Net_C_Debug_SBlocks, "NoFreeSlot ! ");
#endif /* NET_USE_DEBUG */
return iOldestSlot;
}
/*********************************************************/
int iGetAckSendSlot(tdstWindow *pstWindow, unsigned char ucAckn)
{
int i;
tdstSlot *astSendSlot;
#ifdef NET_USE_DEBUG
int n;
#endif /* NET_USE_DEBUG */
astSendSlot=pstWindow->pstSlot;
#ifdef NET_USE_DEBUG
/* how many slots are waiting for acknowledgement ?*/
for(i=0, n=0; i<pstWindow->iSize; i++)
if( astSendSlot[i].ucState==SENT || astSendSlot[i].ucState==TIMED_OUT )
n++;
vDebugFormat(Net_C_Debug_SBlocks, "%d unacknowledged send slots", n);
#endif /* NET_USE_DEBUG */
/* find the send slot whose sequence number is acknowledged */
for(i=0; i<pstWindow->iSize; i++)
if( ( astSendSlot[i].ucState==SENT || astSendSlot[i].ucState==TIMED_OUT)
&& astSendSlot[i].ucSeqn==ucAckn)
return i;
return INVALID_SLOT;
}
/*********************************************************/
/* bitmasks to build GetPacket() function return value */
#define GET_PACKET__NO_PACKET (0)
#define GET_PACKET__PACKET_OK (1<<1)
#define GET_PACKET__MORE_AVAILABLE (1<<2)
int iGetPacket(tdstL0PCWin95SerialChannel *pstChannel, tdstPacket *pstReturnPacket)
{
long lErrorCode;
unsigned long ulLineError;
unsigned long ulReadBytes;
unsigned long ulLostBytes;
unsigned long ulBytesToRead;
char cEnoughChar;
char cChecksumCorrect;
COMSTAT stComStat;
tdstPacket *pstPacket;
int iRetVal; /* return value */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "entree GetPacket(%d)", pstChannel-gs_a_stL0PCWin95SerialChannels);
#endif /* NET_USE_DEBUG */
iRetVal=GET_PACKET__NO_PACKET; /* a priori, we have no new packet */
/* clear the error state */
EnterCriticalSection(&pstChannel->stChannelAccess);
if(pstChannel->ubf1IsSlotInUse) {
if(ClearCommError(pstChannel->hCom, &ulLineError, &stComStat)==FALSE) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "ClearCommError(): error %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "ClearCommError(): error %ld, hCom=%x", lErrorCode, pstChannel->hCom);
#endif /* NET_USE_DEBUG */
}
#ifdef NET_USE_DEBUG
/* report the error state */
if(ulLineError) {
vDisplayErrorState(Net_C_Debug_SBlocks, ulLineError);
}
if( stComStat.fCtsHold || stComStat.fDsrHold || stComStat.fRlsdHold
|| stComStat.fXoffHold || stComStat.fXoffSent || stComStat.fEof || stComStat.fTxim) {
vDisplayComStat(Net_C_Debug_SBlocks, &stComStat);
}
if( stComStat.cbInQue>0 ) {
vDebugFormat(Net_C_Debug_SBlocks, "octets residuels: %ld", stComStat.cbInQue);
}
#endif /* NET_USE_DEBUG */
if(ulLineError) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "line error:%ld", ulLineError);
#endif /* NET_USE_DEBUG */
}
/* eventually complete the asynchronous read operation */
if(pstChannel->ubf1ReadInProgress) {
ulReadBytes=0;
if(GetOverlappedResult(pstChannel->hCom, &pstChannel->stOverlappedRead, &ulReadBytes, FALSE)==FALSE) {
lErrorCode=GetLastError();
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "GetOverlappedResult(read): error %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
}
pstChannel->ulReadPos+=ulReadBytes;
pstChannel->ubf1ReadInProgress=0;
} else
ResetEvent(pstChannel->stOverlappedRead.hEvent);
/* try to find a packet */
ulLostBytes=0;
do {
cEnoughChar= pstChannel->ulReadPos-ulLostBytes >= sizeof(tdstPacket);
if(cEnoughChar) {
pstPacket=(tdstPacket *)(pstChannel->acReadBuffer+ulLostBytes);
cChecksumCorrect= pstPacket->ucStartMark == STARTMARK
&& pstPacket->usChecksum == usComputeChecksum(pstPacket);
if(!cChecksumCorrect)
ulLostBytes++;
}
} while(cEnoughChar && !cChecksumCorrect);
if(ulLostBytes>0) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "%lu bytes lost !", ulLostBytes);
#endif /* NET_USE_DEBUG */
pstChannel->ulReadPos-=ulLostBytes;
MoveMemory(pstChannel->acReadBuffer, pstChannel->acReadBuffer+ulLostBytes,
pstChannel->ulReadPos);
}
if(cEnoughChar) { /* we have a correct packet */
CopyMemory(pstReturnPacket, pstChannel->acReadBuffer, sizeof(tdstPacket));
pstChannel->ulReadPos-=sizeof(tdstPacket);
MoveMemory(pstChannel->acReadBuffer, pstChannel->acReadBuffer+sizeof(tdstPacket),
pstChannel->ulReadPos);
iRetVal|=GET_PACKET__PACKET_OK; /* inform the caller that we have a packet */
}
/* resume reading */
/* we know we can read at least sizeof(tdstPacket) bytes in the buffer */
/* because the buffer is of size 2*sizeof(tdstPacket) */
/* and at this point we have at most sizeof(tdstPacket)-1 bytes in the buffer */
/* first method */
/* if the input buffer contains no bytes, try to read one byte */
/* if the input buffer already contains bytes, read min(number of bytes, sizeof(tdstPacket) ) */
/* this is necessary to be sure that the asynchronous read operation always completes as soon */
/* as bytes are available for read */
/*
if( stComStat.cbInQue > 0 )
if( stComStat.cbInQue < sizeof(tdstPacket) )
ulBytesToRead=stComStat.cbInQue;
else ulBytesToRead=sizeof(tdstPacket);
else ulBytesToRead=1;
*/
/* second method */
/* read the number of bytes necessary to complete a packet */
/* ulBytesToRead=sizeof(tdstPacket)-pstChannel->ulReadPos;
*/
/* third method: use finite timeouts for the read operations */
/* fourth method: if an error occured , use the first method, if not, use the second method */
if(!cEnoughChar) {
if( stComStat.cbInQue > 0 )
if( stComStat.cbInQue < sizeof(tdstPacket) )
ulBytesToRead=stComStat.cbInQue;
else ulBytesToRead=sizeof(tdstPacket);
else ulBytesToRead=1;
} else {
ulBytesToRead=sizeof(tdstPacket)-pstChannel->ulReadPos;
}
/* if the next read operation is to return immediately, indicate it in the return code */
if(ulBytesToRead <= stComStat.cbInQue)
iRetVal|=GET_PACKET__MORE_AVAILABLE;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "ReadFile(%lu->%lu): ", ulBytesToRead, pstChannel->ulReadPos);
#endif /* NET_USE_DEBUG */
if(ReadFile(pstChannel->hCom, pstChannel->acReadBuffer+pstChannel->ulReadPos, /*sizeof(tdstPacket)*/ulBytesToRead, &ulReadBytes, &pstChannel->stOverlappedRead)==FALSE) {
lErrorCode=GetLastError();
if(lErrorCode!=ERROR_IO_PENDING) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "ReadFile(): error %ld", lErrorCode);
#endif /* NET_USE_DEBUG */
} else
pstChannel->ubf1ReadInProgress=1; /* the operation is asynchronous */
} else {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "ReadFile(): finished !!!");
#endif /* NET_USE_DEBUG */
/* the operation finished synchronously */
pstChannel->ulReadPos+=ulReadBytes;
pstChannel->ubf1ReadInProgress=0;
SetEvent(pstChannel->stOverlappedRead.hEvent);
}
} else {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "channel not in use ???");
#endif /* NET_USE_DEBUG */
/* shouldn't happen ... */
ResetEvent(pstChannel->stOverlappedRead.hEvent);
}
#ifdef NET_USE_DEBUG
if(iRetVal&GET_PACKET__PACKET_OK)
vDebugFormat(Net_C_Debug_SBlocks, "got a packet (%d, %d) !", pstReturnPacket->ucSeqn,
pstReturnPacket->ucAckn);
#endif /* NET_USE_DEBUG */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "sortie GetPacket()");
#endif /* NET_USE_DEBUG */
LeaveCriticalSection(&pstChannel->stChannelAccess);
return iRetVal;
}
/*********************************************************/
void vDoSend(tdstL0PCWin95SerialChannel *pstChannel)
{
int iSlot;
tdstSlot *pstSlot;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "entree DoSend(%d)", pstChannel-gs_a_stL0PCWin95SerialChannels);
#endif /* NET_USE_DEBUG */
EnterCriticalSection(&pstChannel->stChannelAccess);
/* eventually complete the former send operation */
if(pstChannel->ubf1WriteInProgress)
vCompleteSendPacket(pstChannel);
else ResetEvent(pstChannel->stOverlappedWrite.hEvent);
/* get some data to send */
iUnloadSendBuffer(pstChannel);
iSlot=iChooseSendSlot(&pstChannel->stSendWindow);
if(iSlot!=INVALID_SLOT) { /* send a real packet */
pstSlot=&pstChannel->stSendWindow.pstSlot[iSlot];
vBuildPacket(pstChannel, iSlot, &pstChannel->stPacket, ucGetAckNumber(&pstChannel->stRecvWindow));
iSendPacket(pstChannel, &pstChannel->stPacket);
pstSlot->ucState=SENT;
pstSlot->ulDate=GetTickCount();
} else if((pstChannel->stPacket.ucAckn=ucGetAckNumber(&pstChannel->stRecvWindow))!=INVALID_SLOT) {
/* there is no slot to send, however we have to send the acknowledgement */
/* of a received packet: so we build a dummy packet */
memset(pstChannel->stPacket.acData, 0, BLOCKSIZE);/* so as to avoid checksum computation */
pstChannel->stPacket.ucStartMark=STARTMARK;
pstChannel->stPacket.ucSeqn=INVALID_SLOT;
pstChannel->stPacket.usChecksum=pstChannel->stPacket.ucSeqn+pstChannel->stPacket.ucAckn;
pstChannel->stPacket.ucSize=0;
iSendPacket(pstChannel, &pstChannel->stPacket);
}
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "sortie DoSend()");
#endif /* NET_USE_DEBUG */
LeaveCriticalSection(&pstChannel->stChannelAccess);
}
/*********************************************************/
void vDoRecv(tdstL0PCWin95SerialChannel *pstChannel)
{
int iSlot;
int iPacketFlag;
int iLoopNumber;
tdstPacket stPacket;
tdstSlot *pstSlot;
#define DORECV_LOOP_LIMIT 4
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "entree DoRecv(%d)", pstChannel-gs_a_stL0PCWin95SerialChannels);
#endif /* NET_USE_DEBUG */
EnterCriticalSection(&pstChannel->stChannelAccess);
/* read packets while packets are available, without looping forever */
iLoopNumber=0;
do {
/* try to read a packet from the line */
if((iPacketFlag=iGetPacket(pstChannel, &stPacket)) & GET_PACKET__PACKET_OK) {
/* if this is not a dummy packet, store the packet in the receive buffer */
/* and prepare to acknowledge it */
if(stPacket.ucSeqn!=INVALID_SLOT) {
/* choose a slot to store the received packet into */
iSlot=iChooseRecvSlot(&pstChannel->stRecvWindow, stPacket.ucSeqn);
if(iSlot!=INVALID_SLOT) {
if(iLoadRecvBuffer(pstChannel, &stPacket)) {
pstSlot=&pstChannel->stRecvWindow.pstSlot[iSlot];
pstSlot->ucSeqn=stPacket.ucSeqn;
pstSlot->ucState=RECVD; /* so as to be acknowledged */
pstSlot->ulDate=GetTickCount();
memcpy(pstSlot->acData, stPacket.acData, BLOCKSIZE); /* not necessary in fact */
} else {
/* the packet is lost because there is no room to store it in the link buffer */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "packet %d refused by linkbuffer", stPacket.ucSeqn);
#endif /* NET_USE_DEBUG */
}
} else { /* the packet is lost because there is no slot to store its sequence number (which must be known when the acknowledgement is sent) */
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "no slot to keep packet %d", stPacket.ucSeqn);
#endif /* NET_USE_DEBUG */
}
}
/* acknowledge the send slot indicated in the packet */
iSlot=iGetAckSendSlot(&pstChannel->stSendWindow, stPacket.ucAckn);
if(iSlot!=INVALID_SLOT) {
pstChannel->stSendWindow.pstSlot[iSlot].ucState=ACK;
/* if all send slots have been acknowledged, signal the event */
if(iSlotsNotAck(&pstChannel->stSendWindow)==0)
SetEvent(pstChannel->hSlotsEmpty);
}
/* if the channel is not sending, wake it up */
if(!pstChannel->ubf1WriteInProgress) {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "DoRecv(): set write event");
#endif /* NET_USE_DEBUG */
SetEvent(pstChannel->stOverlappedWrite.hEvent);
} else {
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SBlocks, "DoRecv(): already sending: don't set write event ");
#endif /* NET_USE_DEBUG */
}
}
} while(iPacketFlag & GET_PACKET__MORE_AVAILABLE && iLoopNumber++<DORECV_LOOP_LIMIT);
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "sortie DoRecv()");
#endif /* NET_USE_DEBUG */
LeaveCriticalSection(&pstChannel->stChannelAccess);
}
/*********************************************************/
unsigned long ulMaxSendSize(int iChannel)
{
unsigned long ReturnValue;
tdstL0PCWin95SerialChannel *pstChannel;
pstChannel=&gs_a_stL0PCWin95SerialChannels[iChannel];
EnterCriticalSection(&pstChannel->stSendBufferAccess);
ReturnValue=FreeSpace(&pstChannel->stSendBuffer);
LeaveCriticalSection(&pstChannel->stSendBufferAccess);
return ReturnValue;
}
/*********************************************************/
int iSendMsg(int iChannel, char *pcMsg, unsigned long ulSize)
{
int iReturnValue;
tdstL0PCWin95SerialChannel *pstChannel;
/* try to store the message into the send buffer */
pstChannel=&gs_a_stL0PCWin95SerialChannels[iChannel];
EnterCriticalSection(&pstChannel->stSendBufferAccess);
iReturnValue=iPushMessageRingBuffer(&pstChannel->stSendBuffer, pcMsg, ulSize);
/* if the channel is inactive, wake it up */
if(!pstChannel->ubf1WriteInProgress)
SetEvent(pstChannel->stOverlappedWrite.hEvent);
LeaveCriticalSection(&pstChannel->stSendBufferAccess);
return iReturnValue;
}
/*********************************************************/
int iUnloadSendBuffer(tdstL0PCWin95SerialChannel *pstChannel)
{
int iSlot;
tdstSlot *pstSlot;
int iBlockSize;
/* if a send slot is available and there is data in the send buffer */
/* copy that data into the send slot */
/* get a send slot with a new sequence number */
iSlot=iGetFreeSlot(&pstChannel->stSendWindow);
if(iSlot!=INVALID_SLOT) {
pstSlot=&pstChannel->stSendWindow.pstSlot[iSlot];
/* get a block from the send buffer */
EnterCriticalSection(&pstChannel->stSendBufferAccess);
iBlockSize=iPopBlockRingBuffer(&pstChannel->stSendBuffer, pstSlot->acData, BLOCKSIZE);
if(iBlockSize>=0) {
pstSlot->ucState=TO_SEND;
pstSlot->ucSize=iBlockSize;
vNumberSlot(pstChannel, iSlot);
} else {
/* if there is no data left in the buffer, signal it */
SetEvent(pstChannel->hBufferEmpty);
}
LeaveCriticalSection(&pstChannel->stSendBufferAccess);
}
return iSlot!=INVALID_SLOT && pstSlot->ucState==TO_SEND;
}
/*********************************************************/
int iLoadRecvBuffer(tdstL0PCWin95SerialChannel *pstChannel, tdstPacket *pstPacket)
{
int iReturnValue;
EnterCriticalSection(&pstChannel->stRecvBufferAccess);
iReturnValue=iPushBlockLinkBuffer(&pstChannel->stRecvBuffer, pstPacket->ucSeqn, pstPacket->ucSize, pstPacket->acData)==eLinkOk;
LeaveCriticalSection(&pstChannel->stRecvBufferAccess);
return iReturnValue;
}
/*********************************************************/
int iRecvMsg(int iChannel, char *pcMsg, unsigned long ulSize)
{
int iReturnValue;
tdstL0PCWin95SerialChannel *pstChannel;
/* try to read the message from the receive buffer */
pstChannel=&gs_a_stL0PCWin95SerialChannels[iChannel];
EnterCriticalSection(&pstChannel->stRecvBufferAccess);
iReturnValue=iPopDataLinkBuffer(&pstChannel->stRecvBuffer, pcMsg, ulSize)==eLinkOk;
LeaveCriticalSection(&pstChannel->stRecvBufferAccess);
return iReturnValue;
}
/*********************************************************/
int iMaxRecvMsg(int iChannel, char *pcMsg, unsigned long *pulSize)
{
int iReturnValue;
unsigned long ulMaxRead;
/* try to read the message from the receive buffer */
tdstL0PCWin95SerialChannel *pstChannel;
pstChannel=&gs_a_stL0PCWin95SerialChannels[iChannel];
EnterCriticalSection(&pstChannel->stRecvBufferAccess);
ulMaxRead=ulLengthLinkBuffer(&pstChannel->stRecvBuffer);
if(ulMaxRead>0) {
if(*pulSize>ulMaxRead)
*pulSize=ulMaxRead;
iReturnValue=iPopDataLinkBuffer(&pstChannel->stRecvBuffer, pcMsg, *pulSize)==eLinkOk;
if(!iReturnValue) /* if a problem appeared, no data was read */
*pulSize=0L;
} else {
*pulSize=0L;
iReturnValue=~0;
}
LeaveCriticalSection(&pstChannel->stRecvBufferAccess);
return iReturnValue;
}
/*****************************************************************************
*
* Description: uwL0PCWin95SerialStartChannelScan
*
* returns the identification number of the first channel for the protocol
*
*****************************************************************************
*
* Input: none
*
* Output: identification of the channel
*
*****************************************************************************
* Creation Date: April 15, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
*
****************************************************************************/
tduwNetChannel uwL0PCWin95SerialStartChannelScan(void)
{
tduwNetChannel uwCurrentChannel;
/* scan the array of channel definitions */
for (uwCurrentChannel = 0; uwCurrentChannel < C_uw_Net_L0PCWin95Serial_PortNumber; uwCurrentChannel ++)
/* if the slot is used, and can transmit data to another individual player */
if
(
gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotInUse
&& !gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotForModem
)
/* return its index value */
return uwCurrentChannel;
/* else no slot is in use */
return C_uwNetInvalidChannel ;
}
/*****************************************************************************
*
* Description: uwL0PCWin95SerialNextChannel
*
* returns the identification number of the channel following the last
*
*****************************************************************************
*
* Input: uwLastScannedChannel, index returned by the previous call
*
* Output: identification of the channel following the specified one
*
*****************************************************************************
* Creation Date: April 15, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
*
****************************************************************************/
tduwNetChannel uwL0PCWin95SerialNextChannel(tduwNetChannel uwLastScannedChannel)
{
tduwNetChannel uwCurrentChannel;
/*
* scan the array of channel definitions, starting with the channel following
* the last channel returned
*/
for
(
uwCurrentChannel = (tduwNetChannel)(uwLastScannedChannel + 1);
uwCurrentChannel < C_uw_Net_L0PCWin95Serial_PortNumber;
uwCurrentChannel ++
)
/* if the slot is used, and can transmit data to another individual player */
if
(
gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotInUse
&& !gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotForModem
)
/* return its index value */
return uwCurrentChannel;
/* else no slot is in use */
return C_uwNetInvalidChannel;
}
/*****************************************************************************
*
* Description: uwL0PCWin95SerialStartBroadcastChannelScan
*
* returns the identification number of the first broadcast channel
* for the protocol
*
*****************************************************************************
*
* Input: none
*
* Output: identification of the channel
*
*****************************************************************************
* Creation Date: April 15, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
*
****************************************************************************/
tduwNetChannel uwL0PCWin95SerialStartBroadcastChannelScan(void)
{
tduwNetChannel uwCurrentChannel;
/* scan the array of channel definitions */
for (uwCurrentChannel = 0; uwCurrentChannel < C_uw_Net_L0PCWin95Serial_PortNumber; uwCurrentChannel ++)
/* if the slot is used, and the channel can send broadcast data */
if
(
gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotInUse
&& !gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotForModem
&& gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsBroadcast
)
/* return its index value */
return uwCurrentChannel;
/* else no slot is in use */
return C_uwNetInvalidChannel;
}
/*****************************************************************************
*
* Description: uwL0PCWin95SerialNextBroadcastChannel
*
* returns the identification number of the broadcast channel following
* the specified one
*
*****************************************************************************
*
* Input: uwLastScannedChannel, index returned by the previous call
*
* Output: identification of the channel following the specified one
*
*****************************************************************************
* Creation Date: April 15, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
*
****************************************************************************/
tduwNetChannel uwL0PCWin95SerialNextBroadcastChannel(tduwNetChannel uwLastScannedChannel)
{
tduwNetChannel uwCurrentChannel;
/*
* scan the array of channel definitions, starting with the channel following
* the last channel returned
*/
for
(
uwCurrentChannel = (tduwNetChannel)(uwLastScannedChannel + 1);
uwCurrentChannel < C_uw_Net_L0PCWin95Serial_PortNumber;
uwCurrentChannel ++
)
/* if the slot is used, and can transmit broadcast data */
if
(
gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotInUse
&& !gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsSlotForModem
&& gs_a_stL0PCWin95SerialChannels[uwCurrentChannel].ubf1IsBroadcast
)
/* return its index value */
return uwCurrentChannel;
/* else no slot is in use */
return C_uwNetInvalidChannel;
}
/*****************************************************************************
*
* Description: eL0PCWin95SerialReadData
*
* buffer the data in the incoming buffer. if a full message is present,
* allocate a block to copy the message into it and put its address in the
* given pointer.
*
*****************************************************************************
*
* Input: p_uwChannel, pointer on a field to store the identification of
* a new channel in case of access to a newly created broacast channel
* (irrelevant for the serial layer)
* ppData, address of the pointer to retrieve a full message if
* necessary.
*
* Output: an error condition.
*
*****************************************************************************
* Creation Date: April 15, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
* 05/11/96 Christophe Roguet
* corrected a bug in the swap code
* 04/02/97 Christophe Roguet
* removed the start mark stuff, no longer needed thanks to the work of the thread
****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95SerialReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData)
{
tdstL0PCWin95SerialChannel *p_stChannel;
unsigned long ulTotalSizeOfMessage;
unsigned long ulBytesCurrentlyRead;
NetLib_tdeErrorStatus eErrorReturned;
tdstNetMessage *p_stNewMsg;
/* by default, there is no full message in the buffer */
*ppData = C_pNull;
/* if the specified channel is valid */
if (*p_uwChannel >= C_uw_Net_L0PCWin95Serial_PortNumber)
/* the channel does not exist in our database -> error!! */
return NetLib_E_es_InvalidChannel;
p_stChannel = &gs_a_stL0PCWin95SerialChannels[*p_uwChannel];
/* if the slot is properly initialized */
if (!p_stChannel->ubf1IsSlotInUse)
/* the slot is not initialized for use -> error!! */
return NetLib_E_es_ChannelUninitialized;
ulBytesCurrentlyRead=C_uwMaxAsyncLength-p_stChannel->ulBytesInReadBuffer;
iMaxRecvMsg(*p_uwChannel, p_stChannel->pCurrentReceivingBuffer+p_stChannel->ulBytesInReadBuffer, &ulBytesCurrentlyRead);
#ifdef NETLIB_LEVEL0COM_TRACE_FULL
vDebugFormat(Net_C_Debug_Serial,"Bytes Received (%u) :", ulBytesCurrentlyRead);
vDebugSDump(Net_C_Debug_Serial, "", p_stChannel->pCurrentReceivingBuffer+p_stChannel->ulBytesInReadBuffer,
ulBytesCurrentlyRead);
#endif /* NETLIB_LEVEL0COM_TRACE_FULL */
p_stChannel->ulBytesInReadBuffer +=ulBytesCurrentlyRead;
if(!p_stChannel->ulBytesInReadBuffer)
{
/* Nothing comes...*/
/* (nothing to do, with the threaded version) */
return NetLib_E_es_NoFullMessage;
}
/* if a message is being received but is not complete yet */
if (p_stChannel->pCurrentReceivedMessage)
{
unsigned long ulBytesToCopy;
/* if there are enough bytes to complete the message */
if (p_stChannel->ulBytesToCompleteIncomingMessage <= p_stChannel->ulBytesInReadBuffer)
{
/* copy only the bytes needed to complete the message */
ulBytesToCopy = p_stChannel->ulBytesToCompleteIncomingMessage;
/* we can give the message to the user */
*ppData = p_stChannel->pCurrentReceivedMessage;
}
else
/* else simply copy everything we got */
ulBytesToCopy = p_stChannel->ulBytesInReadBuffer;
/* copy in the message the bytes in the instant buffer that belong to it */
g_pfn_vNetMemcpy(
p_stChannel->pCurrentMessageIncomingPoint,
p_stChannel->pCurrentReceivingBuffer,
ulBytesToCopy
);
/* there are that many bytes less in the incoming buffer */
p_stChannel->ulBytesInReadBuffer -= ulBytesToCopy;
/* If p_stChannel->ulBytesInReadBuffer > 0, it means that a new message is
following the current one */
if(p_stChannel->ulBytesInReadBuffer > 0)
{/* So move them to the beginning of the incoming buffer */
memmove( /* and not memcpy */
p_stChannel->pCurrentReceivingBuffer,
p_stChannel->pCurrentReceivingBuffer + ulBytesToCopy,
p_stChannel->ulBytesInReadBuffer
);
}
/* if we have completed the message */
if (*ppData)
{
/*
* unreference the message block (its address is passed to the caller
* in the *ppData argument)
*/
p_stChannel->pCurrentReceivedMessage
= p_stChannel->pCurrentMessageIncomingPoint = C_pNull;
p_stChannel->ulBytesToCompleteIncomingMessage = 0;
/* resume reading (i.e. nothing to do, with the threaded version) */
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Msg Received @%lu: %d (%d)",
GetTickCount(), (int)(((tdstNetMessage*)(*ppData))->eMessageType),
(int)(((tdstNetMessage*)(*ppData))->uwMessageSizeInBytes));
#endif
return NetLib_E_es_NoError;/*eL0PCWin95SerialHandleMsg(ppData,*p_uwChannel);*/
}
else
/* we did not complete the message */
{
/* there are that many bytes less to complete the message */
p_stChannel->ulBytesToCompleteIncomingMessage -= ulBytesToCopy;
p_stChannel->pCurrentMessageIncomingPoint += ulBytesToCopy;
eErrorReturned = NetLib_E_es_NoFullMessage;
}
/* resume reading (i.e. nothing to do, with the threaded version) */
/* no error, the message address is in *ppData */
return eErrorReturned;
}
else
/* no new message is being separately stored yet */
{
/* try to read a correct message header */
/* if at least the size in bytes of one message header is in the buffer */
if (p_stChannel->ulBytesInReadBuffer<sizeof(tdstNetMessage)) {
/* not enough bytes to make a buffer : Resume reading (i.e. nothing to do, with the threaded version)*/
return NetLib_E_es_NoFullMessage;
}
p_stNewMsg = (tdstNetMessage *)(p_stChannel->pCurrentReceivingBuffer);
if (p_stNewMsg->uxHeadBigEndian!=NetLib_ucGetLittleBigEndian())
NetLib_eSwapLittleBigEndianMsgHeader(p_stNewMsg);
if( (M_uwCheckSumSlot(p_stNewMsg) != M_uwProcessCheckSum(p_stNewMsg)) ||
(p_stNewMsg->eMessageType > E_Net_mt_LastSysMsg) ||
(p_stNewMsg->eMessageType <= E_Net_mt_InvalidMessage) ) {
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial ,"msg incorrect");
#ifdef NETLIB_LEVEL0COM_TRACE_FULL
vDebugSDump(Net_C_Debug_Serial ,"msg recu:", p_stNewMsg, sizeof(tdstNetMessage));
#endif /* NETLIB_LEVEL0COM_TRACE_FULL */
#endif /* NET_USE_DEBUG */
}
/* we have found a correct header */
p_stChannel->ulBytesToCompleteIncomingMessage = ulTotalSizeOfMessage
= sizeof(tdstNetMessage)
+ p_stNewMsg->uwMessageSizeInBytes;
/* allocate a buffer to store the full message */
p_stChannel->pCurrentReceivedMessage = pMalloc(ulTotalSizeOfMessage);
if(!p_stChannel->pCurrentReceivedMessage)
p_stChannel->pCurrentReceivedMessage = C_pNull;
/* if the message is already completely received */
if (ulTotalSizeOfMessage <= p_stChannel->ulBytesInReadBuffer)
/* tell its address */
*ppData = p_stChannel->pCurrentReceivedMessage;
else
/* prepare to copy what we have received for now in the buffer */
ulTotalSizeOfMessage = p_stChannel->ulBytesInReadBuffer;
/* copy in the message the bytes in the instant buffer that belong to it */
if(p_stChannel->pCurrentReceivedMessage)
g_pfn_vNetMemcpy(
p_stChannel->pCurrentReceivedMessage,
p_stChannel->pCurrentReceivingBuffer,
ulTotalSizeOfMessage
);
/* there are that many bytes less to complete this message */
p_stChannel->ulBytesToCompleteIncomingMessage -= ulTotalSizeOfMessage;
/* compute the remaining number of unprocessed bytes in the buffer */
p_stChannel->ulBytesInReadBuffer -= ulTotalSizeOfMessage;
/* move the remaining bytes to the beginning of the buffer */
if (p_stChannel->ulBytesInReadBuffer)
memmove( /* and not memcpy */
p_stChannel->pCurrentReceivingBuffer,
p_stChannel->pCurrentReceivingBuffer + ulTotalSizeOfMessage,
p_stChannel->ulBytesInReadBuffer
);
/* if the message is complete */
if (*ppData)
{
/* mark the message copy as propagated to the upper level */
p_stChannel->pCurrentMessageIncomingPoint
= p_stChannel->pCurrentReceivedMessage = C_pNull;
/* resume reading (i.e. nothing to do, with the threaded version)*/
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Msg Received @%lu: %d (%d)",
GetTickCount(), (int)(((tdstNetMessage*)(*ppData))->eMessageType),
(int)(((tdstNetMessage*)(*ppData))->uwMessageSizeInBytes));
#endif
return NetLib_E_es_NoError;/*eL0PCWin95SerialHandleMsg(ppData,*p_uwChannel);*/
}
else
{
/* further incoming bytes for the current message will be stored there */
p_stChannel->pCurrentMessageIncomingPoint
= p_stChannel->pCurrentReceivedMessage + ulTotalSizeOfMessage;
eErrorReturned = NetLib_E_es_NoFullMessage;
}
/* resume reading (i.e. nothing to do, with the threaded version) */
/* no error, the message address is in *ppData */
return eErrorReturned;
}
}
/*****************************************************************************
*
* Description: eL0PCWin95SerialSendData
*
* buffer the data in the outgoing buffer.
*
*****************************************************************************
*
* Input: uwChannel, channel to send the message into.
* pData, pointer on the block to send
*
* Output: an error condition.
*
*****************************************************************************
* Creation Date: July 20, 1996 Author: Christophe Roguet
*****************************************************************************
*
* Modification log:
*
* Date: Author:
* 31/11/96 Christophe Roguet
* added a limit on the quantity of data being sent asynchronously, so as not to bufferize too much
* 04/02/97 Christophe Roguet
* removed the start mark stuff, no longer needed thanks to the work of the thread
****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95SerialSendData(tduwNetChannel uwChannel, tdpPointer pData)
{
tdstL0PCWin95SerialChannel *p_stChannel;
unsigned long ulDataSize;
int iErrorCode;
/* if the specified channel is valid */
if (uwChannel >= C_uw_Net_L0PCWin95Serial_PortNumber)
/* the channel does not exist in our database -> error!! */
return NetLib_E_es_InvalidChannel;
p_stChannel = &gs_a_stL0PCWin95SerialChannels[uwChannel];
/* if the slot is properly initialized */
if (!p_stChannel->ubf1IsSlotInUse)
/* the slot is not initialized for use -> error!! */
return NetLib_E_es_ChannelUninitialized;
/* if there is a new message to send, try to fulfill the request */
if(pData) {
ulDataSize=sizeof(tdstNetMessage) + ((tdstNetMessage *)pData)->uwMessageSizeInBytes;
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial, "SendData: DataSize:%ld, MaxSendSize:%ld ",
ulDataSize, ulMaxSendSize(uwChannel));
#endif
if((iErrorCode=iSendMsg(uwChannel, pData, ulDataSize))==eRingFull)
return NetLib_E_es_BufferIsFull; /* the system lack resources to execute the request */
/* the message is being sent */
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial, "Msg Sent @%lu: %d (%d)",
GetTickCount(),
(int)(((tdstNetMessage*)(pData))->eMessageType),
(int)(((tdstNetMessage*)(pData))->uwMessageSizeInBytes));
vDebugFormat(Net_C_Debug_Serial, "send buffer: %d b", BusySpace(&p_stChannel->stSendBuffer));
#ifdef NETLIB_LEVEL0COM_TRACE_FULL
vDebugSDump(Net_C_Debug_Serial, "", pData, ulDataSize);
#endif /* NETLIB_LEVEL0COM_TRACE_FULL */
#endif /* NET_USE_DEBUG */
/* free the message block */
if(iErrorCode!=eRingBigOk)
vFree(pData);
}
p_stChannel->eChannelStatus = E_ts_OK;
return NetLib_E_es_NoError;
}
/*****************************************************************************
*
* Description: eL0PCWin95SerialQueryChannelStatus
*
* returns the current status of the channel
*
*****************************************************************************
*
* Input: uwChannel, channel to query
*
* Output: an error condition.
*
*****************************************************************************
* Creation Date: April 16, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
*
****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95SerialQueryChannelStatus(tduwNetChannel uwChannel)
{
tdstL0PCWin95SerialChannel *p_stChannel;
/* if the specified channel is valid */
if (uwChannel >= C_uw_Net_L0PCWin95Serial_PortNumber)
/* the channel does not exist in our database -> error!! */
return NetLib_E_es_InvalidChannel;
p_stChannel = &gs_a_stL0PCWin95SerialChannels[uwChannel];
/* if the slot is properly initialized */
if (!p_stChannel->ubf1IsSlotInUse)
/* the slot is not initialized for use -> error!! */
return NetLib_E_es_ChannelUninitialized;
if(p_stChannel->eBaudStatus==E_Net_L0SerialBaud_Set)
return NetLib_E_es_EmissionInProgress;
/* return the internal error status */
switch(p_stChannel->eChannelStatus)
{
case E_ts_OK:
return NetLib_E_es_NoError;
case E_ts_EmissionInProgress :
/* Check if it is still busy or not :*/
if(eL0PCWin95SerialSendData(uwChannel,C_pNull)==NetLib_E_es_NoError)
{
p_stChannel->eChannelStatus = E_ts_OK;
return NetLib_E_es_NoError;
}
else
return NetLib_E_es_EmissionInProgress;
default :
return NetLib_E_es_UnknownError;
}
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : void eL0PCWin95SerialNetEngine(void)
The level 0 NetEngine function
////////////////////////////////////////////////////////////////////////////////
Input :
None
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_NoError
An tdeErrorStatus if an error occured
////////////////////////////////////////////////////////////////////////////////
Creation date : April 23, 96
Author : Albert Pais
Modification log:
Date: Author: Christophe Roguet
rewritten for the thread system
////////////////////////////////////////////////////////////////////////////////
*/
void vL0PCWin95SerialNetEngine(void)
{
/* threaded version : nothing to do */
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialAddPort
Initialise a serial port
////////////////////////////////////////////////////////////////////////////////
Input :
szPortName : "COMx" for the serial port x
ulBaudRate : the baude rate
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_NoError
An tdeErrorStatus if an error occured
////////////////////////////////////////////////////////////////////////////////
Creation date : May 3, 96
Author : Albert Pais
Comment : part of code from previous function vLevel0SetupWin95PCSerialInterface
////////////////////////////////////////////////////////////////////////////////
A 0 value for the baud rate makes the port invalid. That is to say, if you don't
want to use the port COM 1 for example, call this function with the argument
"COM1" for szPortName and 0 for ulBaudRate
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95SerialAddPort(char *szPortName)
{
tduwNetChannel uwChannelToInit;
tdstL0PCWin95SerialChannel *pstChannel;
/* handle to the device file */
HANDLE hCom;
/* For communication properties*/
COMMPROP stComProp;
COMMTIMEOUTS stCommTimeouts;
long lErrorCode;
int iNotOk; /* flag: has anything gone wrong before ?*/
NetLib_tdeErrorStatus eErrorCode; /* return value */
uwChannelToInit = (tduwNetChannel)(szPortName[3] - '1');
pstChannel=&gs_a_stL0PCWin95SerialChannels[uwChannelToInit];
/* any failure will cause the channel to be marked as unavailable */
pstChannel->ubf1IsSlotInUse = 0;
if(gs_a_stL0PCWin95SerialChannels[uwChannelToInit].hCom == INVALID_HANDLE_VALUE)
{
/* open the serial channel */
hCom = CreateFile(
szPortName,
GENERIC_READ |GENERIC_WRITE,
0, /* exclusive access */
NULL, /* no security attrs */
OPEN_EXISTING,
/*FILE_ATTRIBUTE_NORMAL*/FILE_FLAG_OVERLAPPED,
NULL
);
pstChannel->hCom = hCom;
}
else
hCom = pstChannel->hCom;
/* The file could not be opened (may be the com is already opened by some one else */
if (hCom == INVALID_HANDLE_VALUE)
return NetLib_E_es_SerialFileCreationFailure;
if(!SetupComm (hCom,4*sizeof(tdstPacket),2*sizeof(tdstPacket)))
{
lErrorCode = GetLastError();
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"SetupComm Error: %lu",lErrorCode);
#endif
}
stCommTimeouts.ReadIntervalTimeout=0;
stCommTimeouts.ReadTotalTimeoutConstant=50;
stCommTimeouts.ReadTotalTimeoutMultiplier=0;
stCommTimeouts.WriteTotalTimeoutConstant=MAXDWORD;
stCommTimeouts.WriteTotalTimeoutMultiplier=0;
if(!SetCommTimeouts(hCom,&stCommTimeouts))
{
lErrorCode = GetLastError();
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Setcommtimeout() Error: %lu",lErrorCode);
#endif
}
/* this is a channel usable to send broadcast and private messages */
pstChannel->ubf1IsBroadcast = 1;
/* this a channel for the serial port layer */
pstChannel->ubf1IsSlotForModem = 0;
/* Getting the max baud rate :*/
if(GetCommProperties(hCom,&stComProp))
{
if(stComProp.dwMaxBaud&&BAUD_USER)
{
/* search the max baud rate available :*/
if(stComProp.dwSettableBaud&BAUD_110)
pstChannel->ulMaxBaudRate = CBR_110;
if(stComProp.dwSettableBaud&BAUD_134_5)
pstChannel->ulMaxBaudRate = CBR_110;
if(stComProp.dwSettableBaud&BAUD_150)
pstChannel->ulMaxBaudRate = CBR_110;
if(stComProp.dwSettableBaud&BAUD_300)
pstChannel->ulMaxBaudRate = CBR_300;
if(stComProp.dwSettableBaud&BAUD_600)
pstChannel->ulMaxBaudRate = CBR_600;
if(stComProp.dwSettableBaud&BAUD_1200)
pstChannel->ulMaxBaudRate = CBR_1200;
if(stComProp.dwSettableBaud&BAUD_1800)
pstChannel->ulMaxBaudRate = CBR_1200;
if(stComProp.dwSettableBaud&BAUD_2400)
pstChannel->ulMaxBaudRate = CBR_2400;
if(stComProp.dwSettableBaud&BAUD_4800)
pstChannel->ulMaxBaudRate = CBR_4800;
if(stComProp.dwSettableBaud&BAUD_7200)
pstChannel->ulMaxBaudRate = CBR_4800;
if(stComProp.dwSettableBaud&BAUD_9600)
pstChannel->ulMaxBaudRate = CBR_9600;
if(stComProp.dwSettableBaud&BAUD_14400)
pstChannel->ulMaxBaudRate = CBR_14400;
if(stComProp.dwSettableBaud&BAUD_19200)
pstChannel->ulMaxBaudRate = CBR_19200;
if(stComProp.dwSettableBaud&BAUD_38400)
pstChannel->ulMaxBaudRate = CBR_38400;
if(stComProp.dwSettableBaud&BAUD_56K)
pstChannel->ulMaxBaudRate = CBR_56000;
if(stComProp.dwSettableBaud&BAUD_128K)
pstChannel->ulMaxBaudRate = CBR_128000;
if(stComProp.dwSettableBaud&BAUD_115200)
pstChannel->ulMaxBaudRate = CBR_115200;
if(stComProp.dwSettableBaud&BAUD_57600)
pstChannel->ulMaxBaudRate = CBR_57600;
}
else
{
switch(stComProp.dwMaxBaud)
{
case BAUD_110:
pstChannel->ulMaxBaudRate = CBR_110;
break;
case BAUD_134_5:
pstChannel->ulMaxBaudRate = CBR_110;
break;
case BAUD_150:
pstChannel->ulMaxBaudRate = CBR_110;
break;
case BAUD_300:
pstChannel->ulMaxBaudRate = CBR_300;
break;
case BAUD_600:
pstChannel->ulMaxBaudRate = CBR_600;
break;
case BAUD_1200:
pstChannel->ulMaxBaudRate = CBR_1200;
break;
case BAUD_1800:
pstChannel->ulMaxBaudRate = CBR_1200;
break;
case BAUD_2400:
pstChannel->ulMaxBaudRate = CBR_2400;
break;
case BAUD_4800:
pstChannel->ulMaxBaudRate = CBR_4800;
break;
case BAUD_7200:
pstChannel->ulMaxBaudRate = CBR_4800;
break;
case BAUD_9600:
pstChannel->ulMaxBaudRate = CBR_9600;
break;
case BAUD_14400:
pstChannel->ulMaxBaudRate = CBR_14400;
break;
case BAUD_19200:
pstChannel->ulMaxBaudRate = CBR_19200;
break;
case BAUD_38400:
pstChannel->ulMaxBaudRate = CBR_38400;
break;
case BAUD_56K:
pstChannel->ulMaxBaudRate = CBR_56000;
break;
case BAUD_57600:
pstChannel->ulMaxBaudRate = CBR_57600;
break;
case BAUD_115200:
pstChannel->ulMaxBaudRate = CBR_115200;
break;
case BAUD_128K:
pstChannel->ulMaxBaudRate = CBR_128000;
break;
default :
pstChannel->ulMaxBaudRate = CBR_9600;
break;
}
}
}
else
{
CloseHandle(pstChannel->hCom);
pstChannel->hCom = INVALID_HANDLE_VALUE;
return NetLib_E_es_SerialInitFailure;
}
/* set baud rate for the device */
if(eL0PCWin95SerialResetChannel(uwChannelToInit)!=NetLib_E_es_NoError)
{
/* close the handle */
CloseHandle(pstChannel->hCom);
pstChannel->hCom = INVALID_HANDLE_VALUE;
/* go to next device in case of failure */
return NetLib_E_es_SerialInitFailure;
}
/* purge transmission channel from any extraneous lingering data */
if (!PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR))
{
/* close the handle */
CloseHandle(pstChannel->hCom);
pstChannel->hCom = INVALID_HANDLE_VALUE;
return NetLib_E_es_SerialInitFailure;
}
iNotOk=0;
/* initialize the low level emission/reception buffers */
vInitWindow(&pstChannel->stSendWindow, LINK_WINDOW, LINK_TIMEOUT(gs_ulL0PCWin95SerialBaudRate));
vInitWindow(&pstChannel->stRecvWindow, LINK_WINDOW, 0);
pstChannel->ucSeqn=0;
pstChannel->ulReadPos=0;
vInitRingBuffer(&pstChannel->stSendBuffer);
vInitLinkBuffer(&pstChannel->stRecvBuffer, LINK_LINKNUMBER);
pstChannel->hBufferEmpty=CreateEvent(NULL, FALSE, TRUE, NULL);
pstChannel->hSlotsEmpty=CreateEvent(NULL, FALSE, TRUE, NULL);
InitializeCriticalSection(&pstChannel->stSendBufferAccess);
InitializeCriticalSection(&pstChannel->stRecvBufferAccess);
/* if we arrived here, the channel is ready for use */
pstChannel->ubf1IsSlotInUse = 1;
/* no operation in progress yet */
pstChannel->ubf1ReadInProgress
= pstChannel->ubf1WriteInProgress = 0;
/* no byte received or sent in any I/O operation yet */
pstChannel->ulBytesInReadBuffer = 0;
/* allocate buffer for reception */
pstChannel->pCurrentReceivingBuffer = pMalloc(C_uwMaxAsyncLength);
if(pstChannel->pCurrentReceivingBuffer!=C_pNull)
{
pstChannel->pCurrentReceivedMessage=C_pNull;
pstChannel->pCurrentMessageIncomingPoint=C_pNull;
pstChannel->ulBytesToCompleteIncomingMessage=0L;
/* register with the thread controller */
vAddThreadClient(uwChannelToInit);
/* start the ball rolling: signal the read event */
if(SetEvent(pstChannel->stOverlappedRead.hEvent)==TRUE) {
/* the channel is working properly */
pstChannel->eChannelStatus = E_ts_OK;
/* the channel is 'connected' */
pstChannel->ubf1IsSlotConnected = 1;
gs_bL0PCWin95SerialInitState = gs_bL0PCWin95SerialInitState|(char)0x04;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_SThread, "sortie correcte AddPort(%d)", pstChannel-gs_a_stL0PCWin95SerialChannels);
#endif /* NET_USE_DEBUG */
eErrorCode=NetLib_E_es_NoError;
} else {
iNotOk=1;
eErrorCode=NetLib_E_es_SerialInitFailure;
}
if(iNotOk) {
vRemoveThreadClient(uwChannelToInit);
}
} else {
iNotOk=1;
eErrorCode=NetLib_E_es_NotEnoughMemory;
}
if(iNotOk) {
/* close the handle */
CloseHandle(pstChannel->hCom);
pstChannel->hCom = INVALID_HANDLE_VALUE;
DeleteCriticalSection(&pstChannel->stSendBufferAccess);
DeleteCriticalSection(&pstChannel->stRecvBufferAccess);
CloseHandle(pstChannel->hBufferEmpty);
CloseHandle(pstChannel->hSlotsEmpty);
vDeleteLinkBuffer(&pstChannel->stRecvBuffer);
vDeleteWindow(&pstChannel->stRecvWindow);
vDeleteWindow(&pstChannel->stSendWindow);
pstChannel->eChannelStatus = E_ts_ChannelUnitialized; /* ? */
}
return eErrorCode;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95SerialCloseChannel
serial-Level 0 closing function
////////////////////////////////////////////////////////////////////////////////
Input :
the channel to close
////////////////////////////////////////////////////////////////////////////////
Output :
none
////////////////////////////////////////////////////////////////////////////////
Creation date : June 17,96
Author : Albert Pais
Modification log:
.....
Date: March 21, 97 Author: Christophe Roguet
thread synchronization: deregister from thread controller before releasing resources -> works better
////////////////////////////////////////////////////////////////////////////////
*/
void vL0PCWin95SerialCloseChannel(tduwNetChannel uwChannel)
{
tdstL0PCWin95SerialChannel *p_stChannel;
#ifdef NET_USE_DEBUG
vDebugFormat(Net_C_Debug_Serial, "CloseChannel(%d) ", uwChannel);
#endif /* NET_USE_DEBUG */
/* if the specified channel is valid */
if (uwChannel < C_uw_Net_L0PCWin95Serial_PortNumber)
{
p_stChannel = &gs_a_stL0PCWin95SerialChannels[uwChannel];
/* if the slot is properly initialized */
if(p_stChannel->ubf1IsSlotInUse && p_stChannel->hCom!=INVALID_HANDLE_VALUE)
{
long lErrorCode;
/* Purge the comms and close the handles :*/
eL0PCWin95SerialFlushChannel(uwChannel);
if(p_stChannel->ubf1IsSlotConnected) {
/* de-register from thread system */
vRemoveThreadClient(uwChannel);
p_stChannel->ubf1IsSlotConnected=0;
/* release the critical sections for buffer access */
DeleteCriticalSection(&p_stChannel->stSendBufferAccess);
DeleteCriticalSection(&p_stChannel->stRecvBufferAccess);
}
if(CloseHandle(p_stChannel->hCom)==FALSE)
lErrorCode=GetLastError();
/* reset the overlapped operations events */
if(ResetEvent(p_stChannel->stOverlappedRead.hEvent)==FALSE)
lErrorCode=GetLastError();
if(ResetEvent(p_stChannel->stOverlappedWrite.hEvent)==FALSE)
lErrorCode=GetLastError();
p_stChannel->hCom = INVALID_HANDLE_VALUE;
p_stChannel->ubf1IsSlotInUse = 0;
if(p_stChannel->pCurrentReceivingBuffer)
{
vFree((tdpPointer)p_stChannel->pCurrentReceivingBuffer);
p_stChannel->pCurrentReceivingBuffer = C_pNull;
}
/* free the send and receive slot window */
vDeleteWindow(&p_stChannel->stSendWindow);
vDeleteWindow(&p_stChannel->stRecvWindow);
vDeleteLinkBuffer(&p_stChannel->stRecvBuffer);
CloseHandle(p_stChannel->hBufferEmpty);
CloseHandle(p_stChannel->hSlotsEmpty);
}
}
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95SerialCloseProtocol(void)
serial-Level 0 closing function
////////////////////////////////////////////////////////////////////////////////
Input :
none
////////////////////////////////////////////////////////////////////////////////
Output :
none
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
void vL0PCWin95SerialCloseProtocol(void)
{
unsigned short c_uwCount;
/* close all channels */
for(c_uwCount = 0;c_uwCount <C_uw_Net_L0PCWin95Serial_PortNumber;c_uwCount++)
{
if(!gs_a_stL0PCWin95SerialChannels[c_uwCount].ubf1IsSlotForModem)
vL0PCWin95SerialCloseChannel(c_uwCount);
}
/* release the critical sections used to synchronize channel access, */
/* as well as the communication overlapped operation structures */
for(c_uwCount = 0;c_uwCount <C_uw_Net_L0PCWin95Serial_PortNumber;c_uwCount++)
{
if(!gs_a_stL0PCWin95SerialChannels[c_uwCount].ubf1IsSlotForModem) {
DeleteCriticalSection(&gs_a_stL0PCWin95SerialChannels[c_uwCount].stChannelAccess);
CloseHandle(gs_a_stL0PCWin95SerialChannels[c_uwCount].stOverlappedRead.hEvent);
CloseHandle(gs_a_stL0PCWin95SerialChannels[c_uwCount].stOverlappedWrite.hEvent);
gs_a_stL0PCWin95SerialChannels[c_uwCount].stOverlappedRead.hEvent=INVALID_HANDLE_VALUE;
gs_a_stL0PCWin95SerialChannels[c_uwCount].stOverlappedWrite.hEvent=INVALID_HANDLE_VALUE;
}
}
vLevel1RemoveProtocol(E_Net_pr_Windows95SerialProtocol);
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"\n\nBye-bye\n\n");
#endif
}
/*****************************************************************************
*
* Description: vL0PCWin95SerialOpenProtocol
*
* initialize the specified interface structure with the correct function
* pointers to enable a protocol-independant data transmission process.
*
*****************************************************************************
*
* Input: p_stProtocolInterface, pointer to the interface structure
*
* Output: none
*
*****************************************************************************
* Creation Date: April 15, 1996 Author: Benoit GERMAIN
*****************************************************************************
*
* Modification log:
*
* Date: Author:
*
****************************************************************************/
void _NET_CALLING_CONV_ vL0PCWin95SerialOpenProtocol(unsigned long ulRate)
{
tduwNetChannel uwChannelToInit;
tdstL0PCWin95SerialChannel *pstChannel;
tdstNetProtocolInterface *p_stProtocolInterface;
#ifdef NET_USE_DEBUG
/*
vDebugOpen(Net_C_Debug_Serial);
vDebugOpen(Net_C_Debug_SLinks);
vDebugOpen(Net_C_Debug_SBlocks);
vDebugOpen(Net_C_Debug_SThread);
*/
vDebugClear(Net_C_Debug_Serial);
vDebugClear(Net_C_Debug_SLinks);
vDebugClear(Net_C_Debug_SBlocks);
vDebugClear(Net_C_Debug_SThread);
vDebugFormat(Net_C_Debug_Serial,"Netlib - Level 0 - Com WIN95 : Log file \n\n");
#endif
gs_bL0PCWin95SerialInitState = 0x00;
gs_ulL0PCWin95SerialBaudRate=ulRate;
p_stProtocolInterface=pstLevel1AddProtocol();
/* logical identification of the protocol */
p_stProtocolInterface->eProtocol = E_Net_pr_Windows95SerialProtocol;
/* setup the function pointers */
p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95SerialStartChannelScan;
p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95SerialStartBroadcastChannelScan;
p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95SerialNextChannel;
p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95SerialNextBroadcastChannel;
p_stProtocolInterface->fn_eReadData= eL0PCWin95SerialReadData;
p_stProtocolInterface->fn_eSendData= eL0PCWin95SerialSendData;
p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95SerialQueryChannelStatus;
p_stProtocolInterface->fn_vLevel0NetEngine = vL0PCWin95SerialNetEngine;
p_stProtocolInterface->fn_vCloseChannel = (tdfn_vCloseChannel)C_pNull;
p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95SerialCloseProtocol;
p_stProtocolInterface->eIsInternet = 0;
/* process all potentially available channels */
for (uwChannelToInit = 0; uwChannelToInit < C_uw_Net_L0PCWin95Serial_PortNumber; uwChannelToInit ++)
{
pstChannel=&gs_a_stL0PCWin95SerialChannels[uwChannelToInit];
pstChannel->hCom = INVALID_HANDLE_VALUE;
pstChannel->ubf1IsSlotInUse = 0;
pstChannel->eBaudStatus = E_Net_L0SerialBaud_Invalid;
/* we need to initialize the overlap structure events */
/* because they are used for synchronisation even if */
/* the corresponding channel is not open (well, this isn't true any more ... could be changed then) */
memset(&pstChannel->stOverlappedRead, 0, sizeof(pstChannel->stOverlappedRead));
memset(&pstChannel->stOverlappedWrite, 0, sizeof(pstChannel->stOverlappedWrite));
pstChannel->stOverlappedRead.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
pstChannel->stOverlappedWrite.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
/* initialize the mutual exclusion object for channel access */
InitializeCriticalSection(&pstChannel->stChannelAccess);
}
vInitThreadController();
lSerialChannelsOnceInitialized=1;
gs_bL0PCWin95SerialInitState = gs_bL0PCWin95SerialInitState |(char)0x01;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95SerialClosePort
Close a serial port
////////////////////////////////////////////////////////////////////////////////
Input :
szPortName : "COMx" for the com port x
////////////////////////////////////////////////////////////////////////////////
Output :
None
////////////////////////////////////////////////////////////////////////////////
Creation date : June 17,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
void _NET_CALLING_CONV_ vL0PCWin95SerialClosePort(char *szPortName)
{
tduwNetChannel uwChannel;
uwChannel = (tduwNetChannel)(szPortName[3] - '1');
if(uwChannel < C_uw_Net_L0PCWin95Serial_PortNumber)
vL0PCWin95SerialCloseChannel(uwChannel);
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialIsPortAModem
returns true if a com port is linked to a modem
////////////////////////////////////////////////////////////////////////////////
Input :
hPort : handle to the com port to test
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : 20/02/97
Author : Christophe Roguet
Modification log:
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95SerialIsPortAModem(HANDLE hCom)
{
DCB stDcb;
COMMTIMEOUTS stCommTimeouts;
OVERLAPPED stOverlap;
char acReadBuffer[10];
unsigned long ulWrittenBytes, ulReadBytes;
NetLib_tdeErrorStatus eReturnValue;
if(hCom!=INVALID_HANDLE_VALUE) /* the COM port is free to use */
{
/* configure the port */
memset(&stDcb, 0, sizeof(DCB));
GetCommState(hCom, &stDcb);
stDcb.BaudRate = CBR_19200;
/* those are the universal settings for nowadays modems. */
stDcb.fBinary = TRUE; /* not text */
stDcb.ByteSize = 8; /* 8 bit chars */
stDcb.fParity = FALSE; /* no parity checking */
stDcb.Parity = NOPARITY; /* no parity checking (redundant ?) */
stDcb.StopBits = ONESTOPBIT;
stDcb.fErrorChar = FALSE; /* do not replace bad chars */
stDcb.fAbortOnError = FALSE;
/* flow control */
stDcb.fOutxCtsFlow = TRUE; /* hardware flow control */
stDcb.fOutxDsrFlow = FALSE; /* hardware flow control */
stDcb.fDtrControl = DTR_CONTROL_ENABLE; /* hardware flow control */
stDcb.fDsrSensitivity = FALSE; /* hardware flow control */
stDcb.fRtsControl = RTS_CONTROL_HANDSHAKE; /* hardware flow control */
stDcb.fOutX = FALSE;
stDcb.fInX = FALSE;
stDcb.fNull = FALSE;
SetCommState(hCom, &stDcb);
stCommTimeouts.ReadIntervalTimeout=0;
stCommTimeouts.ReadTotalTimeoutConstant=100;
stCommTimeouts.ReadTotalTimeoutMultiplier=0;
stCommTimeouts.WriteTotalTimeoutConstant=100;
stCommTimeouts.WriteTotalTimeoutMultiplier=0;
SetCommTimeouts(hCom, &stCommTimeouts);
stOverlap.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
/* send ATI and read the response */
WriteFile(hCom, "ATI0\n", 4, &ulWrittenBytes, &stOverlap);
GetOverlappedResult(hCom, &stOverlap, &ulWrittenBytes, TRUE);
ReadFile(hCom, acReadBuffer, sizeof(acReadBuffer), &ulReadBytes, &stOverlap);
GetOverlappedResult(hCom, &stOverlap, &ulReadBytes, TRUE);
if(ulReadBytes>0 && !strncmp(acReadBuffer, "ATI0", 3))
eReturnValue=NetLib_E_es_True;
else eReturnValue = NetLib_E_es_False;
CloseHandle(stOverlap.hEvent);
}
return eReturnValue;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialIsPortAvailable
returns if a port is avaible or not
////////////////////////////////////////////////////////////////////////////////
Input :
szPortName : "COMx" for the com port x
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
Modification log:
Date: Author: Christophe Roguet
adapted to thread system
Date: 20/02/97 Author: Christophe Roguet
attempt to detect the presence of a modem on the port
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95SerialIsPortAvailable(char *szPortName)
{
tduwNetChannel uwChannel;
HANDLE hCom;
NetLib_tdeErrorStatus eReturnValue;
uwChannel = (tduwNetChannel)(szPortName[3] - '1');
if(uwChannel >= C_uw_Net_L0PCWin95Serial_PortNumber)
return NetLib_E_es_InvalidChannel;
eReturnValue=NetLib_E_es_False;
/* try to open the corresponding COM port */
hCom = CreateFile(
szPortName,
GENERIC_READ |GENERIC_WRITE,
0, /* exclusive access */
NULL, /* no security attrs */
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if(hCom!=INVALID_HANDLE_VALUE) { /* the COM port is free to use */
/* if(eL0PCWin95SerialIsPortAModem(hCom)==NetLib_E_es_False)*/
eReturnValue=NetLib_E_es_True;
CloseHandle(hCom);
}
return eReturnValue;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialIsProtocolSet
Returns wether the protocol has been set or not
////////////////////////////////////////////////////////////////////////////////
Input :
None
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : June 18,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95SerialIsProtocolSet(void)
{
if(!(gs_bL0PCWin95SerialInitState&0x01))
return NetLib_E_es_ProtocolNotInitialized;
if(!(gs_bL0PCWin95SerialInitState&0x04))
return NetLib_E_es_NoPortSelected;
return NetLib_E_es_True;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialIsProtocolAvailable
Returns wether the protocol can be used or not
////////////////////////////////////////////////////////////////////////////////
Input :
None
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : June 26,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95SerialIsProtocolAvailable(void)
{
NetLib_tdeErrorStatus eErrorReturned;
unsigned short uwCount;
char *szPortName = "COMx";
uwCount = 0;
eErrorReturned = NetLib_E_es_False;
while
(
(uwCount <C_uw_Net_L0PCWin95Serial_PortNumber)&&
(eErrorReturned != NetLib_E_es_True)
)
{
szPortName[3] = (char)('1'+uwCount);
eErrorReturned = eL0PCWin95SerialIsPortAvailable(szPortName);
uwCount++;
}
return eErrorReturned;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialIsPortOpened
Returns wether the port is opened or not
////////////////////////////////////////////////////////////////////////////////
Input :
A port
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : June 27,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95SerialIsPortOpened(char*szPortName)
{
tduwNetChannel uwChannel;
uwChannel = (tduwNetChannel)(szPortName[3] - '1');
if(uwChannel >= C_uw_Net_L0PCWin95Serial_PortNumber)
return NetLib_E_es_InvalidPortNumber;
if(gs_a_stL0PCWin95SerialChannels[uwChannel].hCom == INVALID_HANDLE_VALUE)
return NetLib_E_es_False;
if(!gs_a_stL0PCWin95SerialChannels[uwChannel].ubf1IsSlotInUse)
return NetLib_E_es_False;
return NetLib_E_es_True;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialPurgeChannel
Purge the channel specified in parameter
////////////////////////////////////////////////////////////////////////////////
Input :
A channel
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : July 1st,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus eL0PCWin95SerialPurgeChannel(tduwNetChannel uwChannel)
{
tdstL0PCWin95SerialChannel *p_stChannel;
if(uwChannel>=C_uw_Net_L0PCWin95Serial_PortNumber)
/* the channel does not exist in our database -> error!! */
return NetLib_E_es_InvalidChannel;
p_stChannel = &gs_a_stL0PCWin95SerialChannels[uwChannel];
/* stop emission and reception, and clear buffers */
/* NOT WRITTEN YET */
return NetLib_E_es_NoError;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialResetChannel
Reset the baud rate of the channel to the default value
////////////////////////////////////////////////////////////////////////////////
Input :
A channel
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : July 2nd,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus eL0PCWin95SerialResetChannel(tduwNetChannel uwChannel)
{
tdstL0PCWin95SerialChannel *p_stChannel;
if(uwChannel>=C_uw_Net_L0PCWin95Serial_PortNumber)
/* the channel does not exist in our database -> error!! */
return NetLib_E_es_InvalidChannel;
p_stChannel = &gs_a_stL0PCWin95SerialChannels[uwChannel];
memset(&p_stChannel->stChannelStatus, 0, sizeof(DCB));
/*p_stChannel->stChannelStatus->dwSize=sizeof(DCB);*/
/* Reset the default baud rate value :*/
if
(GetCommState
(
p_stChannel->hCom,
&(p_stChannel->stChannelStatus)
)
)
{
p_stChannel->stChannelStatus.BaudRate = gs_ulL0PCWin95SerialBaudRate;
/* those are the universal settings for nowadays modems. use it for the serial link */
p_stChannel->stChannelStatus.fBinary = TRUE; /* not text */
p_stChannel->stChannelStatus.ByteSize = 8; /* 8 bit chars */
p_stChannel->stChannelStatus.fParity = FALSE; /* no parity checking */
p_stChannel->stChannelStatus.Parity = NOPARITY; /* no parity checking (redundant ?) */
p_stChannel->stChannelStatus.StopBits = ONESTOPBIT;
p_stChannel->stChannelStatus.fErrorChar = FALSE; /* do not replace bad chars */
p_stChannel->stChannelStatus.fAbortOnError = TRUE;
/* flow control. the link cable has to be a null modem cable */
p_stChannel->stChannelStatus.fOutxCtsFlow = TRUE; /* hardware flow control */
p_stChannel->stChannelStatus.fOutxDsrFlow = FALSE; /* hardware flow control */
p_stChannel->stChannelStatus.fDtrControl = DTR_CONTROL_ENABLE; /* hardware flow control */
p_stChannel->stChannelStatus.fDsrSensitivity = FALSE; /* hardware flow control */
p_stChannel->stChannelStatus.fRtsControl = RTS_CONTROL_HANDSHAKE; /* hardware flow control */
p_stChannel->stChannelStatus.fOutX = FALSE;
p_stChannel->stChannelStatus.fInX = FALSE;
p_stChannel->stChannelStatus.fNull = FALSE;
if
(SetCommState
(
p_stChannel->hCom,
&(p_stChannel->stChannelStatus)
)
)
{
/* that's ok the baud rates should be the same:*/
p_stChannel->eBaudStatus = E_Net_L0SerialBaud_Default;
return NetLib_E_es_NoError;
}
}
p_stChannel->eBaudStatus = E_Net_L0SerialBaud_Invalid;
return NetLib_E_es_UnknownError;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95SerialFlushChannel
flush the sending buffers of the channel specified in parameter
////////////////////////////////////////////////////////////////////////////////
Input :
A channel
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : July 3rd,96
Author : Albert Pais
Modification log:
....
Date: Author:
12/02/97 Christophe Roguet
rewritten for the thread system
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus eL0PCWin95SerialFlushChannel(tduwNetChannel uwChannel)
{
tdstL0PCWin95SerialChannel *p_stChannel;
unsigned long ulLeftToSendBeforeWait, ulLeftToSend;
long lWaitResult;
#ifdef NET_USE_DEBUG
long lErrorCode;
#endif /* NET_USE_DEBUG */
if(uwChannel>=C_uw_Net_L0PCWin95Serial_PortNumber)
/* the channel does not exist in our database -> error!! */
return NetLib_E_es_InvalidChannel;
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Entering Flush ");
#endif
p_stChannel = &gs_a_stL0PCWin95SerialChannels[uwChannel];
EnterCriticalSection(&p_stChannel->stChannelAccess);
if(p_stChannel->ubf1IsSlotInUse) {
if(p_stChannel->ubf1IsSlotConnected) {
/* if the send buffer is not empty, wait for its content to be sent */
if((ulLeftToSend=BusySpace(&p_stChannel->stSendBuffer))>0) {
ResetEvent(p_stChannel->hBufferEmpty);
do {
ulLeftToSendBeforeWait=ulLeftToSend;
LeaveCriticalSection(&p_stChannel->stChannelAccess);
lWaitResult=WaitForSingleObject(p_stChannel->hBufferEmpty, 100);
if(lWaitResult==WAIT_FAILED) {
#if defined(NET_USE_DEBUG)
lErrorCode=GetLastError();
vDebugFormat(Net_C_Debug_Serial,"WaitForSingleObject(): %lu", lErrorCode);
#endif
}
EnterCriticalSection(&p_stChannel->stChannelAccess);
ulLeftToSend=BusySpace(&p_stChannel->stSendBuffer);
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Wait for buffer: %d left before, %d left after, ret val:%lu",
ulLeftToSendBeforeWait, ulLeftToSend, lWaitResult);
#endif
/* repeat while there is data in the send buffer and we see that the send system is not blocked */
} while(lWaitResult!=WAIT_OBJECT_0 && ulLeftToSend<ulLeftToSendBeforeWait);
}
/* the high level buffer is empty : now wait for all send slots to be transmitted ... and acknowledged */
if((ulLeftToSend=iSlotsNotAck(&p_stChannel->stSendWindow))>0) {
ResetEvent(p_stChannel->hSlotsEmpty);
do {
ulLeftToSendBeforeWait=ulLeftToSend;
LeaveCriticalSection(&p_stChannel->stChannelAccess);
lWaitResult=WaitForSingleObject(p_stChannel->hSlotsEmpty, 100);
EnterCriticalSection(&p_stChannel->stChannelAccess);
if(lWaitResult==WAIT_FAILED) {
#if defined(NET_USE_DEBUG)
lErrorCode=GetLastError();
vDebugFormat(Net_C_Debug_Serial,"WaitForSingleObject(): %lu", lErrorCode);
#endif
}
ulLeftToSend=iSlotsNotAck(&p_stChannel->stSendWindow);
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Wait for slots: %d left before, %d left after, ret val:%lu",
ulLeftToSendBeforeWait, ulLeftToSend, lWaitResult);
#endif
/* repeat while there are slots left to send and we see that the send system is not blocked */
} while(lWaitResult!=WAIT_OBJECT_0 && ulLeftToSend<ulLeftToSendBeforeWait);
}
}
}
LeaveCriticalSection(&p_stChannel->stChannelAccess);
#if defined(NET_USE_DEBUG)
vDebugFormat(Net_C_Debug_Serial,"Leaving Flush ");
#endif
return NetLib_E_es_NoError;
}