3646 lines
118 KiB
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;
|
|
}
|