/* * 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 #include #ifdef NET_USE_DEBUG #include #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)pcBigMsg) { lFreeSpace=FreeSpace(pstRing); lLeftToCopy=pstRing->ulSize-pstRing->ulBottom; if(lLeftToCopypcBigMsg[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)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; iiNLinks; 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; iiNLinks; 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)ulFirstSequenceLengthpstLink[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; iEvent50) vDebugFormat(Net_C_Debug_SThread, "long delay ?"); for(iEvent=0; iEvent=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; iEventstSendWindow.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; iSlotpstSlot[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; iiSize; i++) if(astSendSlot[i].ucState==FREE) { iElectedSlot=i; break; } /* or, find the oldest acknowledged slot */ if(iElectedSlot==INVALID_SLOT) for(i=0; iiSize; i++) if(astSendSlot[i].ucState==ACK && astSendSlot[i].ulDatestSendWindow.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; iiSize; i++) if(astSendSlot[i].ucState==TIMED_OUT || astSendSlot[i].ucState==SENT && astSendSlot[i].ulDate+pstWindow->ulTimeout@%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; iiSize; i++) if(astSendSlot[i].ucState==TO_SEND && astSendSlot[i].ucSeqn"); #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; istSendWindow.iSize; i++) if(pstChannel->stSendWindow.pstSlot[i].ucState==SENT && pstChannel->stSendWindow.pstSlot[i].ulDate+pstChannel->stSendWindow.ulTimeoutstChannelAccess); 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; iiSize; 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; iacData[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; iiSize; i++) if(astRecvSlot[i].ucState==RECVD && astRecvSlot[i].ulDatepstSlot; iLowestSlot=INVALID_SLOT; for(i=0; iiSize; i++) if(astRecvSlot[i].ucState==RECVD) { ucLowestn=astRecvSlot[i].ucSeqn; iLowestSlot=i; break; } for(i++; iiSize; 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; iiSize; 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; iiSize; i++) if( astRecvSlot[i].ucState==RECVD && astRecvSlot[i].ucSeqn==ucSeqn ) return i; for(i=0; iiSize; i++) if((astRecvSlot[i].ucState==ACK || astRecvSlot[i].ucState==FREE) && astRecvSlot[i].ulDatepstSlot; #ifdef NET_USE_DEBUG /* how many slots are waiting for acknowledgement ?*/ for(i=0, n=0; iiSize; 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; iiSize; 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++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->ulBytesInReadBufferpCurrentReceivingBuffer); 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 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) 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 && ulLeftToSendstSendWindow))>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 && ulLeftToSendstChannelAccess); #if defined(NET_USE_DEBUG) vDebugFormat(Net_C_Debug_Serial,"Leaving Flush "); #endif return NetLib_E_es_NoError; }