* harbour/external/Makefile
+ harbour/external/hbpmcom
+ harbour/external/hbpmcom/Makefile
+ harbour/external/hbpmcom/wrap_ow.c
+ harbour/external/hbpmcom/com.c
+ harbour/external/hbpmcom/irq.h
+ harbour/external/hbpmcom/irq_ow.c
+ harbour/external/hbpmcom/irq_g.c
+ harbour/external/hbpmcom/com.h
+ harbour/external/hbpmcom/wrap_g.sx
+ harbour/external/hbpmcom/irqwrap.h
+ harbour/external/hbpmcom/doc
+ harbour/external/hbpmcom/doc/readme.txt
+ harbour/external/hbpmcom/doc/com.txt
+ added PMCOM library - RS-232 DOS communication library by Peter Marinov.
The original PMCOM 1.0 library has not been updated for very long
time and this code is quite strongly modified so I haven't tried to
generate any .diff files. I do not think it will be possible to make
autoamtic merege with newwer version (if any appears).
This is also the reason I decided to use HBPMCOM name instead of
PMCOM to avoid potential conflicts with some local versions of
this library.
TODO: add support for flow control - now flow control functions are
dummy and do not enable any handshaking.
* harbour/src/rtl/hbcom.c
* harbour/config/dos/watcom.mk
* harbour/config/dos/djgpp.mk
+ added support for serial port in DOS builds (hb_com*() PRG and C API)
using HBPMCOM library.
2836 lines
76 KiB
C
2836 lines
76 KiB
C
/* com.c */
|
|
|
|
/* ************************************************************************
|
|
|
|
COMLib, version 1.0
|
|
|
|
RS-232 comunication library
|
|
|
|
started: may/1998
|
|
|
|
Program and Text Copyright, ( C ) 1998, Peter Marinov
|
|
All Rights Reserved.
|
|
|
|
************************************************************************ */
|
|
|
|
/*
|
|
Compile options:
|
|
/DDISABLE_TIMING -- will disable the library to use the system timer
|
|
and this will disable all the xxxTimed() functions from operation as well
|
|
/DDISABLE_PREEMPTING -- will turn off the ability to be preempted the
|
|
process of handling COM IRQs.
|
|
*/
|
|
|
|
#include <dos.h>
|
|
#include <mem.h>
|
|
#include <stdlib.h>
|
|
#include <string.h> /* memset() for djgpp is declared here */
|
|
|
|
#if defined( __WATCOMC__ )
|
|
#include <conio.h>
|
|
#define inportb(x) inp(x)
|
|
#define outportb(x,y) outp(x,y)
|
|
#define enable() _enable()
|
|
#define disable() _disable()
|
|
#if !defined(__INLINE_FUNCTIONS__)
|
|
//#error NO INLINE FUNCTIONS
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
|
|
#include "irq.h"
|
|
#include "com.h"
|
|
#ifndef DISABLE_TIMING
|
|
#include "timer.h"
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#define ASSERT(x) assert(x)
|
|
#else
|
|
#define ASSERT(x)
|
|
#endif
|
|
|
|
#define gettimer() _peekd(0x40, 0x6c)
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
/* COM registers */
|
|
#define regTHR 0 /* (w) Transmitter holding register */
|
|
#define regRBR 0 /* (r) Receive Buffer register */
|
|
#define regDLL 0 /* (rw) Divisor latch low byte (DLL) when DLAB = 1 */
|
|
#define regDLM 1 /* (rw) Divisor latch high byte (DLM) when DLAB = 1 */
|
|
#define regIER 1 /* (rw) Interrupt enable register when DLAB = 0 */
|
|
#define regIIR 2 /* (r) Interrupt identification register */
|
|
#define regFCR 2 /* (w) FIFO control register */
|
|
#define regLCR 3 /* (rw) Line control register */
|
|
#define regMCR 4 /* (rw) Modem control register */
|
|
#define regLSR 5 /* (r) Line status register */
|
|
#define regMSR 6 /* (r) Modem status register */
|
|
#define regSCR 7 /* (rw) Scratch register */
|
|
|
|
/* Some macroses to improve the the source comprehension */
|
|
#define THR(pCOM) (pCOM->nCOMAddress + regTHR)
|
|
#define RBR(pCOM) (pCOM->nCOMAddress + regRBR)
|
|
#define DLL(pCOM) (pCOM->nCOMAddress + regDLL)
|
|
#define DLM(pCOM) (pCOM->nCOMAddress + regDLM)
|
|
#define IER(pCOM) (pCOM->nCOMAddress + regIER)
|
|
#define IIR(pCOM) (pCOM->nCOMAddress + regIIR)
|
|
#define FCR(pCOM) (pCOM->nCOMAddress + regFCR)
|
|
#define LCR(pCOM) (pCOM->nCOMAddress + regLCR)
|
|
#define MCR(pCOM) (pCOM->nCOMAddress + regMCR)
|
|
#define LSR(pCOM) (pCOM->nCOMAddress + regLSR)
|
|
#define MSR(pCOM) (pCOM->nCOMAddress + regMSR)
|
|
#define SCR(pCOM) (pCOM->nCOMAddress + regSCR)
|
|
|
|
/*
|
|
Pair of macroses to arrange proper using of the queues
|
|
of a particular COM.
|
|
Check if an IRQ service is in progress and disable IRQs
|
|
to start a critical section to enable using a queue.
|
|
*/
|
|
#define ENTER_QUEUE_CRITICAL_SECTION(pCOM)\
|
|
{if (!pCOM->bHandlingIRQ) disable();}
|
|
#define LEAVE_QUEUE_CRITICAL_SECTION(pCOM)\
|
|
{if (!pCOM->bHandlingIRQ) enable();}
|
|
|
|
#define DEFAULT_QUEUE_SIZE 255
|
|
#define DEFAULT_TX_THRESHOLD 16
|
|
#define DEFAULT_RX_THRESHOLD 8
|
|
|
|
char COM_LIB_ID[] = "*** COM library ***";
|
|
|
|
static int bCOMInit = FALSE; /* Set TRUE by COMInit */
|
|
|
|
struct TQueue
|
|
{
|
|
int nTail, nHead;
|
|
char *pBuf;
|
|
int nBufSize;
|
|
};
|
|
|
|
struct COMDesc
|
|
{
|
|
/* Hardware settings */
|
|
int nIRQ; /* IRQ line, not int vector! */
|
|
int nCOMAddress; /* IO Base address of the COM port */
|
|
int bHardwareSet; /* Whether harware settings were set */
|
|
|
|
/* Hardware FIFO Settings */
|
|
int nChipset; /* 1: 8250, 2: 16450, 3: 16550, 4: 16550A, 0: No chip */
|
|
int bFIFOAvailable; /* If nChipset >= 4 this flag is TRUE */
|
|
int bFIFOEnabled; /* Managed by COMEnable/DisableHardwareFIFO() */
|
|
int nRXThreshold; /* RX fifo threshold, if not set 8 is used */
|
|
int nTXThreshold; /* TX fifo threshold (no phisycal meaning!) */
|
|
|
|
/* IRQ attachement list */
|
|
struct COMDesc *pNextPort; /* Next port attached to same IRQ, null - last */
|
|
|
|
/* Transmit and receive queues */
|
|
int nRXQueueSize; /* Allocation size, to be changed by the user */
|
|
int nTXQueueSize; /* Allocation size, to be changed by the user */
|
|
struct TQueue RXQueue; /* The receive queue */
|
|
struct TQueue RXStatQueue; /* The receive queue -- stat info */
|
|
struct TQueue TXQueue; /* The transmit queue */
|
|
struct TQueue TXStatQueue; /* The transmit queue -- stat info */
|
|
|
|
void (*EventHandler)(int nEvent); /* Event call-back routine */
|
|
int nFlowControl; /* NONE, XONOFF, RTSCTS */
|
|
int nFlowState; /* Current flow control state */
|
|
int bInstalled; /* Whether COM is activated by COMPortOpen() */
|
|
long nBauds; /* Transmition parameter */
|
|
int nParity; /* Transmition parameter */
|
|
int nWordLen; /* Transmition parameter */
|
|
int nStopBits; /* Transmition parameter */
|
|
int nCOMError; /* If an error while processing particular COM */
|
|
|
|
int nSaveIER; /* Used by COMWrap() as a storage of IER */
|
|
int nSaveMSR; /* COMHandler() stores here the last modem status */
|
|
int nSaveLCR; /* To ease switching for 9bit protocols */
|
|
|
|
int bHandlingIRQ; /* Indicates whether COMHandler is executing */
|
|
|
|
#ifdef _DEBUG
|
|
/* Diagnostic counters */
|
|
int nRX; /* Received characters, not RX IRQs number */
|
|
int nTX; /* Transmition IRQs */
|
|
int nModem; /* Modem IRQs */
|
|
int nStat; /* Line error characters */
|
|
int nIRQs; /* Total number of IRQs handled */
|
|
#endif
|
|
|
|
int nLastBit9;
|
|
struct COMStat cStat; /* Collect rx char information here before storing in queue */
|
|
char cBuf;
|
|
|
|
char cXon;
|
|
char cXoff;
|
|
};
|
|
|
|
/*
|
|
All the COMs that can be opened simultaneously
|
|
*/
|
|
static struct COMDesc COMs[COMMAX];
|
|
|
|
/*
|
|
Opening COM port attaches a COMDesc instance to specific position
|
|
depending on nIRQ value
|
|
*/
|
|
static struct COMDesc *pAttachedPorts[16] =
|
|
{
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
static void COMHandler(struct COMDesc *pCOM); /* Forward declaration */
|
|
|
|
/* ************************************************************************
|
|
Function: EnableIRQ
|
|
Description:
|
|
Enables specific IRQ in 8259 interrupt controller.
|
|
*/
|
|
static void EnableIRQ(int nIRQ)
|
|
{
|
|
if (nIRQ >= 8)
|
|
outportb(0xa1, inportb(0xa1) & ~(1 << (nIRQ - 8)));
|
|
else
|
|
outportb(0x21, inportb(0x21) & ~(1 << nIRQ));
|
|
}
|
|
END_OF_FUNCTION(EnableIRQ);
|
|
|
|
/* ************************************************************************
|
|
Function: DisableIRQ
|
|
Description:
|
|
Disables specific IRQ in 8259 interrupt controller.
|
|
*/
|
|
static void DisableIRQ(int nIRQ)
|
|
{
|
|
if (nIRQ >= 8)
|
|
outportb(0xa1, inportb(0xa1) | (1 << (nIRQ - 8)));
|
|
else
|
|
outportb(0x21, inportb(0x21) | (1 << nIRQ));
|
|
}
|
|
END_OF_FUNCTION(DisableIRQ);
|
|
|
|
/* ************************************************************************
|
|
Function: EndIRQ
|
|
Description:
|
|
Issues and end of IRQ command toward 8259 interrupt controller.
|
|
*/
|
|
static void EndIRQ(int nIRQ)
|
|
{
|
|
outportb(0x20, 0x20);
|
|
if (nIRQ > 7)
|
|
outportb(0xa0, 0x20);
|
|
}
|
|
END_OF_FUNCTION(EndIRQ);
|
|
|
|
/* ************************************************************************
|
|
Function: COMDisableIRQs
|
|
Description:
|
|
Disables IRQs depending on DISABLE_PREEMPTING definition
|
|
*/
|
|
void COMDisableIRQs(void)
|
|
{
|
|
#ifndef DISABLE_PREEMPTING
|
|
disable();
|
|
#endif
|
|
}
|
|
END_OF_FUNCTION(COMDisableIRQs);
|
|
|
|
/* ************************************************************************
|
|
Function: COMEnableIRQs
|
|
Description:
|
|
Enables IRQs depending on DISABLE_PREEMPTING definition
|
|
*/
|
|
void COMEnableIRQs(void)
|
|
{
|
|
#ifndef DISABLE_PREEMPTING
|
|
enable();
|
|
#endif
|
|
}
|
|
END_OF_FUNCTION(COMEnableIRQs);
|
|
|
|
/*
|
|
Define com port wrappers for each IRQ vector.
|
|
1. Disable the specific irq line in 8259 interrupt controller.
|
|
2. Issue EndOfInterrupt to 8259. Now we can issue EndOfInterrupt
|
|
because this particular interrupt is disabled.
|
|
3. sti. This will enable all other IRQs to be handled during processing
|
|
this particular COM.
|
|
4. Handle all the attached COMs to this IRQ line.
|
|
5. cli.
|
|
6. Disable and enable all the irq masks for all the attached COMs.
|
|
This will generate an edge event to 8259 and that will be registered if
|
|
some COM event was not served during this session.
|
|
7. Enable back this IRQ in 8259 interrupt controller.
|
|
|
|
Q: Why is DISABLE_PREEMPTING necessary?
|
|
A: Putting COM library to work together with a preempting multi-thread
|
|
support library would harm COM IRQ serving if preempting is enabled.
|
|
Example: COM IRQ occures while a low priority thread is in progress, then
|
|
timer0 IRQ occures and preempts this thread (actually preempts COMHandler)
|
|
the thread (and the COMHandler) will take execution quant after
|
|
all the high priority threads are serviced, which will improperly delay
|
|
the handing of the COM event.
|
|
*/
|
|
#define COMWRAP(x)\
|
|
static int COMWrap##x(void)\
|
|
{\
|
|
struct COMDesc *pCOM;\
|
|
\
|
|
DisableIRQ(x);\
|
|
EndIRQ(x);\
|
|
\
|
|
COMEnableIRQs();\
|
|
\
|
|
for (pCOM = pAttachedPorts[x]; pCOM != NULL; pCOM = pCOM->pNextPort)\
|
|
COMHandler(pCOM);\
|
|
\
|
|
COMDisableIRQs();\
|
|
\
|
|
for (pCOM = pAttachedPorts[x]; pCOM != NULL; pCOM = pCOM->pNextPort)\
|
|
outportb(IER(pCOM), 0);\
|
|
for (pCOM = pAttachedPorts[x]; pCOM != NULL; pCOM = pCOM->pNextPort)\
|
|
outportb(IER(pCOM), pCOM->nSaveIER);\
|
|
\
|
|
EnableIRQ(x);\
|
|
return (0);\
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef COMWRAP
|
|
#define COMWRAP(x)\
|
|
static int COMWrap##x(void)\
|
|
{\
|
|
struct COMDesc *pCOM;\
|
|
\
|
|
DisableIRQ(x);\
|
|
EndIRQ(x);\
|
|
\
|
|
COMEnableIRQs();\
|
|
\
|
|
for (pCOM = pAttachedPorts[x]; pCOM != NULL; pCOM = pCOM->pNextPort)\
|
|
COMHandler(pCOM);\
|
|
\
|
|
COMDisableIRQs();\
|
|
\
|
|
for (pCOM = pAttachedPorts[x]; pCOM != NULL; pCOM = pCOM->pNextPort)\
|
|
outportb(IER(pCOM), 0);\
|
|
for (pCOM = pAttachedPorts[x]; pCOM != NULL; pCOM = pCOM->pNextPort)\
|
|
outportb(IER(pCOM), pCOM->nSaveIER);\
|
|
\
|
|
EnableIRQ(x);\
|
|
return (0);\
|
|
}
|
|
|
|
|
|
COMWRAP(0);
|
|
COMWRAP(1);
|
|
COMWRAP(2);
|
|
COMWRAP(3); /* Comment this when uncommenting COMWrap3 below! */
|
|
/* This is COMWrap3 -- COM2, uncomment for debugging */
|
|
/*
|
|
static int COMWrap3(void)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
DisableIRQ(3);
|
|
EndIRQ(3);
|
|
|
|
COMEnableIRQs();
|
|
|
|
for (pCOM = pAttachedPorts[3]; pCOM != NULL; pCOM = pCOM->pNextPort)
|
|
COMHandler(pCOM);
|
|
|
|
COMDisableIRQs();
|
|
|
|
for (pCOM = pAttachedPorts[3]; pCOM != NULL; pCOM = pCOM->pNextPort)
|
|
outportb(IER(pCOM), 0);
|
|
for (pCOM = pAttachedPorts[3]; pCOM != NULL; pCOM = pCOM->pNextPort)
|
|
outportb(IER(pCOM), pCOM->nSaveIER);
|
|
|
|
EnableIRQ(3);
|
|
return (0);
|
|
}
|
|
*/
|
|
|
|
COMWRAP(4);
|
|
COMWRAP(5);
|
|
COMWRAP(6);
|
|
COMWRAP(7);
|
|
COMWRAP(8);
|
|
COMWRAP(9);
|
|
COMWRAP(10);
|
|
COMWRAP(11);
|
|
COMWRAP(12);
|
|
COMWRAP(13);
|
|
COMWRAP(14);
|
|
COMWRAP(15);
|
|
void COMWrap0_End(void)
|
|
{
|
|
} /* Fictive function to mark the end of the COMWrapX() code region. */
|
|
|
|
typedef int (*TCOMWrapper)(void);
|
|
|
|
/*
|
|
COMWraperX addresses to be easy accessible at nIRQ index positions
|
|
*/
|
|
TCOMWrapper COMWrappers[16] =
|
|
{
|
|
COMWrap0,
|
|
COMWrap1,
|
|
COMWrap2,
|
|
COMWrap3,
|
|
COMWrap4,
|
|
COMWrap5,
|
|
COMWrap6,
|
|
COMWrap7,
|
|
COMWrap8,
|
|
COMWrap9,
|
|
COMWrap10,
|
|
COMWrap11,
|
|
COMWrap12,
|
|
COMWrap13,
|
|
COMWrap14,
|
|
COMWrap15
|
|
};
|
|
|
|
/*
|
|
QUEUE managing functions.
|
|
|
|
Take care to enclose each call of queue operating functions
|
|
with ENTER_QUEUE_CRITICAL_SECTION() and LEAVE_QUEUE_CRITICAL_SECTION()
|
|
for the specific COM port.
|
|
*/
|
|
|
|
static void * XMemCpy( void * to, const void * from, int count )
|
|
{
|
|
const char * src = ( const char * ) from;
|
|
char * dst = ( char * ) to;
|
|
|
|
while( --count >= 0 )
|
|
*dst++ = *src++;
|
|
|
|
return to;
|
|
}
|
|
END_OF_FUNCTION(XMemCpy);
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUEPut
|
|
Description:
|
|
Stores array of bytes into the FIFO buffer.
|
|
On entry:
|
|
pBuf, nSize -- array of bytes to put in the queue.
|
|
QUEUECalcFifoFree() should be called to calculate the amount
|
|
of free space prior to invoke this function.
|
|
*/
|
|
static void QUEUEPut(struct TQueue *pQueue, const char *pBuf, int nSize)
|
|
{
|
|
int nToCopy;
|
|
|
|
ASSERT(pQueue != NULL);
|
|
|
|
nToCopy = pQueue->nBufSize - pQueue->nTail;
|
|
|
|
/* Don't copy more than nSize */
|
|
if (nToCopy > nSize)
|
|
nToCopy = nSize; /* This ensures that nTail wont wrap nHead as well */
|
|
|
|
XMemCpy(pQueue->pBuf + pQueue->nTail, pBuf, nToCopy);
|
|
|
|
if ((nSize -= nToCopy) > 0)
|
|
{
|
|
/* Rest of the bytes at the start of the buffer */
|
|
XMemCpy(pQueue->pBuf, pBuf + nToCopy, nSize);
|
|
pQueue->nTail = nSize;
|
|
}
|
|
else
|
|
{
|
|
/* Adjust the tail */
|
|
if ((pQueue->nTail += nToCopy) == pQueue->nBufSize)
|
|
pQueue->nTail = 0;
|
|
}
|
|
}
|
|
END_OF_FUNCTION(QUEUEPut);
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUEGet
|
|
Description:
|
|
Reads a sequence of bytes from the FIFO buffer.
|
|
On exit:
|
|
pBuf will hold nSize bytes from the queue.
|
|
*/
|
|
static void QUEUEGet(struct TQueue *pQueue, char *pBuf, int nSize)
|
|
{
|
|
int nToCopy;
|
|
|
|
ASSERT(pQueue != NULL);
|
|
ASSERT(pBuf != NULL);
|
|
|
|
nToCopy = pQueue->nBufSize - pQueue->nHead;
|
|
|
|
/* Don't copy more than nSize */
|
|
if (nToCopy > nSize)
|
|
nToCopy = nSize;
|
|
|
|
XMemCpy(pBuf, pQueue->pBuf + pQueue->nHead, nToCopy);
|
|
|
|
if ((nSize -= nToCopy) > 0)
|
|
{
|
|
XMemCpy(pBuf + nToCopy, pQueue->pBuf, nSize);
|
|
pQueue->nHead = nSize;
|
|
}
|
|
else
|
|
{
|
|
/* Adjust head */
|
|
if ((pQueue->nHead += nToCopy) == pQueue->nBufSize)
|
|
pQueue->nHead = 0;
|
|
}
|
|
}
|
|
END_OF_FUNCTION(QUEUEGet);
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUECalcFree
|
|
Description:
|
|
Calculates the amount of free space in a queue
|
|
*/
|
|
static int QUEUECalcFree(struct TQueue *pQueue)
|
|
{
|
|
int f;
|
|
int nHead;
|
|
int nTail;
|
|
|
|
ASSERT(pQueue != NULL);
|
|
|
|
nHead = pQueue->nHead;
|
|
nTail = pQueue->nTail;
|
|
|
|
if (nTail >= nHead)
|
|
f = nTail - nHead;
|
|
else /* Tail wrapped arround */
|
|
f = nTail + (pQueue->nBufSize - nHead);
|
|
|
|
return (pQueue->nBufSize - f - 1); /* -1 because Tail never treads on Head */
|
|
}
|
|
END_OF_FUNCTION(QUEUECalcFree);
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUECalcOcupied
|
|
Description:
|
|
Calculates the amount of occupied space in a queue.
|
|
*/
|
|
static int QUEUECalcOccupied(struct TQueue *pQueue)
|
|
{
|
|
int f;
|
|
int nHead;
|
|
int nTail;
|
|
|
|
ASSERT(pQueue != NULL);
|
|
|
|
nHead = pQueue->nHead;
|
|
nTail = pQueue->nTail;
|
|
|
|
if (nTail >= nHead)
|
|
f = nTail - nHead;
|
|
else /* Tail wrapped arround */
|
|
f = nTail + (pQueue->nBufSize - nHead);
|
|
|
|
return (f);
|
|
}
|
|
END_OF_FUNCTION(QUEUECalcOccupied);
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUEAdvance
|
|
Description:
|
|
Calculates the amount of occupied space in a queue.
|
|
*/
|
|
static void QUEUEAdvance(struct TQueue *pQueue, int *pIndex, int nAdvanceBy)
|
|
{
|
|
ASSERT(pQueue != NULL);
|
|
ASSERT(pIndex != NULL);
|
|
|
|
if ((*pIndex += nAdvanceBy) >= pQueue->nBufSize)
|
|
*pIndex = *pIndex - pQueue->nBufSize;
|
|
}
|
|
END_OF_FUNCTION(QUEUEAdvance);
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUEInit
|
|
Description:
|
|
Inital setup of a queue
|
|
*/
|
|
static void QUEUEInit(struct TQueue *pQueue, char *pBuf, int nBufSize)
|
|
{
|
|
ASSERT(pQueue != NULL);
|
|
|
|
pQueue->pBuf = pBuf;
|
|
pQueue->nBufSize = nBufSize;
|
|
pQueue->nHead = pQueue->nTail = 0;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: QUEUEDetachBuf
|
|
Description:
|
|
Detaches the queue buffer.
|
|
*/
|
|
static char *QUEUEDetachBuf(struct TQueue *pQueue)
|
|
{
|
|
char *pBuf;
|
|
ASSERT(pQueue != NULL);
|
|
|
|
pQueue->nHead = pQueue->nTail = 0;
|
|
pBuf = pQueue->pBuf;
|
|
pQueue->pBuf = NULL;
|
|
return (pBuf);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMHandler
|
|
Description:
|
|
Handles a COM port IRQ request.
|
|
Entering this function IRQs are enabled depending on DISABLE_PREEMPTING
|
|
definition.
|
|
*/
|
|
|
|
static void COMHandler(struct COMDesc *pCOM)
|
|
{
|
|
int nIntID;
|
|
int nToPut;
|
|
int nLCR;
|
|
int nTXThreshold;
|
|
|
|
pCOM->bHandlingIRQ = TRUE;
|
|
|
|
pCOM->cStat.nStat = 0;
|
|
pCOM->cStat.nBit9 = 0;
|
|
|
|
#ifdef _DEBUG
|
|
++pCOM->nIRQs;
|
|
#endif
|
|
|
|
/* Bit 0 of IIR indicates for pending interrupt requests */
|
|
while (((nIntID = inportb(IIR(pCOM))) & 1) == 0)
|
|
switch (nIntID & 0x6)
|
|
{
|
|
case 0x6: /* status interrupt */
|
|
#ifdef _DEBUG
|
|
++pCOM->nStat;
|
|
#endif
|
|
pCOM->cStat.nStat = inportb(LSR(pCOM)) & 0x1e;
|
|
/* TODO: Test BREAK signal -- if rx event will come, otherwise the event will be lost */
|
|
|
|
/* Call user event handler -- atomic operation */
|
|
/* User will get info about the stat code on the next
|
|
character read by COMReadChar() in the nStat field */
|
|
if (pCOM->EventHandler != NULL)
|
|
{
|
|
COMDisableIRQs();
|
|
pCOM->EventHandler(evCOMStat);
|
|
COMEnableIRQs();
|
|
}
|
|
break;
|
|
case 0x4: /* rx interrupt */
|
|
#ifdef _DEBUG
|
|
++pCOM->nRX;
|
|
#endif
|
|
pCOM->cBuf = inportb(RBR(pCOM));
|
|
|
|
if (pCOM->nParity == '9')
|
|
{
|
|
if ((inportb(LCR(pCOM)) & 0x10) == 0) /* Current parity is mark */
|
|
{
|
|
if ((pCOM->cStat.nStat & statParity) == 0)
|
|
pCOM->cStat.nBit9 = 1;
|
|
}
|
|
else /* Current parity is space */
|
|
if ((pCOM->cStat.nStat & statParity) != 0)
|
|
pCOM->cStat.nBit9 = 1;
|
|
}
|
|
|
|
if (QUEUECalcFree(&pCOM->RXQueue) < 1)
|
|
pCOM->nCOMError = COMERR_RXOVERFLOW; /* Indicate outside IRQ hndl */
|
|
else
|
|
{
|
|
QUEUEPut(&pCOM->RXQueue, &pCOM->cBuf, 1);
|
|
|
|
ASSERT(QUEUECalcFree(&pCOM->RXStatQueue) >= (int)sizeof(struct COMStat));
|
|
QUEUEPut(&pCOM->RXStatQueue, (char *)&pCOM->cStat, sizeof(struct COMStat));
|
|
}
|
|
|
|
/* Prepare for the next iteration */
|
|
pCOM->cStat.nStat = 0;
|
|
pCOM->cStat.nBit9 = 0;
|
|
|
|
/* Call user event handler -- atomic operation */
|
|
if (pCOM->EventHandler != NULL)
|
|
{
|
|
COMDisableIRQs();
|
|
pCOM->EventHandler(evCOMRx);
|
|
COMEnableIRQs();
|
|
}
|
|
break;
|
|
case 0x2: /* tx interrupt */
|
|
#ifdef _DEBUG
|
|
++pCOM->nTX;
|
|
#endif
|
|
|
|
/*
|
|
9bit mode:
|
|
Q: How this TX IRQ actually works?
|
|
A: There are 2 registers in COM port 1-transmit reg and 2-shifting
|
|
reg. IRQ occures whenever a byte from reg 1 is moved to reg 2. The
|
|
9th bit adhesion takes place when all the byte has been shifted out
|
|
(i.e. transmitted) from reg 2. So when an IRQ occures our previous
|
|
byte has just been put in the shift reg and in a while will need
|
|
its 9th bit. That's why whenever TX IRQ occures it is necessary to
|
|
put in LCR the 9bit of this byte (previous byte) and then to load the
|
|
transmit reg (reg 1).
|
|
*/
|
|
if (pCOM->nParity == '9')
|
|
{
|
|
nLCR = inportb(LCR(pCOM));
|
|
if (pCOM->nLastBit9) /* Set mark parity */
|
|
{
|
|
if ((nLCR & 0x10) != 0)
|
|
outportb(LCR(pCOM), nLCR & 0xef);
|
|
}
|
|
else
|
|
{
|
|
if ((nLCR & 0x10) == 0) /* Set space parity */
|
|
outportb(LCR(pCOM), nLCR | 0x10);
|
|
}
|
|
}
|
|
|
|
nTXThreshold = 1; /* Assume no hardware FIFO enabled */
|
|
if (pCOM->bFIFOEnabled)
|
|
{
|
|
nTXThreshold = 16; /* Assume default value */
|
|
if (pCOM->nTXThreshold != 0)
|
|
nTXThreshold = pCOM->nTXThreshold;
|
|
}
|
|
|
|
nToPut = QUEUECalcOccupied(&pCOM->TXQueue);
|
|
if (nToPut > 0)
|
|
{
|
|
if (nToPut > nTXThreshold)
|
|
nToPut = nTXThreshold;
|
|
|
|
do
|
|
{
|
|
QUEUEGet(&pCOM->TXQueue, &pCOM->cBuf, 1);
|
|
outportb(THR(pCOM), pCOM->cBuf);
|
|
QUEUEGet(&pCOM->TXStatQueue, (char *)&pCOM->cStat, sizeof(struct COMStat));
|
|
pCOM->nLastBit9 = pCOM->cStat.nBit9;
|
|
}
|
|
while (--nToPut);
|
|
|
|
/* Prepare for the next iteration -- if rx event is about to come */
|
|
pCOM->cStat.nStat = 0;
|
|
pCOM->cStat.nBit9 = 0;
|
|
|
|
/* Call user event handler -- atomic operation */
|
|
/* Indicate that the last transmition operation has finished */
|
|
if (pCOM->EventHandler != NULL)
|
|
{
|
|
COMDisableIRQs();
|
|
pCOM->EventHandler(evCOMTx);
|
|
COMEnableIRQs();
|
|
}
|
|
break;
|
|
}
|
|
/* No more characters -- mask tx interrupt in IER */
|
|
pCOM->nSaveIER &= 0xd; /* COMWrap() will put the value to IER */
|
|
break;
|
|
case 0: /* modem interrupt */
|
|
#ifdef _DEBUG
|
|
++pCOM->nModem;
|
|
#endif
|
|
/* save delta bits to simulate real MSR read operation */
|
|
pCOM->nSaveMSR &= ( DELTA_CTS | DELTA_DSR | DELTA_RI | DELTA_CD );
|
|
pCOM->nSaveMSR |= inportb(MSR(pCOM));
|
|
|
|
/* Call user event handler -- atomic operation */
|
|
/* Get the new modem status by calling COMGetModemStatus() */
|
|
if (pCOM->EventHandler != NULL)
|
|
{
|
|
disable();
|
|
COMDisableIRQs();
|
|
pCOM->EventHandler(evCOMModem);
|
|
COMEnableIRQs();
|
|
}
|
|
break;
|
|
}
|
|
|
|
pCOM->bHandlingIRQ = FALSE;
|
|
|
|
}
|
|
END_OF_FUNCTION(COMHandler);
|
|
|
|
/* ************************************************************************
|
|
Function: _DetachCOMPort
|
|
Description:
|
|
Detaches a comport from the list of attached com ports.
|
|
*/
|
|
static void _DetachCOMPort(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
struct COMDesc *pPort;
|
|
struct COMDesc *pPrevPort;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
|
|
/*
|
|
Find the specific COM port in the COM wrapper chain.
|
|
*/
|
|
pPort = pAttachedPorts[pCOM->nIRQ];
|
|
ASSERT(pPort != NULL);
|
|
pPrevPort = NULL;
|
|
while (pPort != &COMs[nCOM])
|
|
{
|
|
ASSERT(pPort != NULL); /* COM not found in the attachement chain */
|
|
pPrevPort = pPort;
|
|
pPort = pPort->pNextPort;
|
|
}
|
|
/*
|
|
Detach from the COM wrapper chain.
|
|
*/
|
|
if (pPrevPort == NULL) /* First in the chain */
|
|
pAttachedPorts[pCOM->nIRQ] = pPort->pNextPort;
|
|
else
|
|
pPrevPort->pNextPort = pPort->pNextPort;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: AttachCOMPort
|
|
Description:
|
|
Attaches COM port to a specific IRQ.
|
|
*/
|
|
static int AttachCOMPort(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
struct COMDesc *pPort;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
|
|
/*
|
|
Attach the com port to the specific COM wrapper chain.
|
|
*/
|
|
pPort = pAttachedPorts[pCOM->nIRQ];
|
|
pAttachedPorts[pCOM->nIRQ] = pCOM;
|
|
pCOM->pNextPort = pPort;
|
|
|
|
/*
|
|
Install specific COM wrapper if still not installed
|
|
*/
|
|
if (pPort == NULL) /* If this is the first com port attached to the chain */
|
|
{
|
|
if (!InstallIRQ(pCOM->nIRQ, COMWrappers[pCOM->nIRQ])) /* Install the wrapper */
|
|
{
|
|
/* Failed to install IRQ wrapper */
|
|
_DetachCOMPort(nCOM);
|
|
return (FALSE);
|
|
}
|
|
EnableIRQ(pCOM->nIRQ);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: DetachCOMPort
|
|
Description:
|
|
Detaches a COM port from its IRQ.
|
|
*/
|
|
static void DetachCOMPort(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
_DetachCOMPort(nCOM);
|
|
|
|
/*
|
|
Check whether to detach the COM wrapper.
|
|
*/
|
|
if (pAttachedPorts[pCOM->nIRQ] == NULL) /* No more COMs attached */
|
|
{
|
|
DisableIRQ(pCOM->nIRQ);
|
|
UninstallIRQ(pCOM->nIRQ);
|
|
}
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetHardwareParameters
|
|
Description:
|
|
Changes the port hardware settings as IRQ vector and IO address.
|
|
NOTE: All COMs > 4 should be set by this function as there are
|
|
no default values.
|
|
*/
|
|
void COMSetHardwareParameters(int nCOM, int nIRQ, int nCOMAddress)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(nIRQ < 16 && nIRQ >= 0);
|
|
|
|
pCOM->nIRQ = nIRQ;
|
|
pCOM->nCOMAddress = nCOMAddress;
|
|
pCOM->bHardwareSet = TRUE;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetHardwareParameters
|
|
Description:
|
|
Returns the port hardware settings as IRQ vector and IO address.
|
|
*/
|
|
void COMGetHardwareParameters(int nCOM, int *pIRQ, int *pCOMAddress)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->nIRQ < 16 && pCOM->nIRQ >= 0);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
ASSERT(pIRQ != NULL);
|
|
ASSERT(pCOMAddress != NULL);
|
|
|
|
*pIRQ = pCOM->nIRQ;
|
|
*pCOMAddress = pCOM->nCOMAddress;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMDetect
|
|
Description:
|
|
Detects COM port chipset
|
|
Returns:
|
|
1: 8250
|
|
2: 16450 or 8250 with scratch register
|
|
3: 16550
|
|
4: 16550A
|
|
5: No chip
|
|
*/
|
|
int COMDetect(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int x;
|
|
int olddata;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
ASSERT(!pCOM->bInstalled); /* Should not be called on an open port */
|
|
|
|
/*
|
|
Check if a UART is present anyway
|
|
*/
|
|
olddata = inportb(MCR(pCOM));
|
|
outportb(MCR(pCOM), 0x10); /* Loopback and RTS&DTR down */
|
|
if ((inportb(MSR(pCOM)) & 0x30) != 0) /* Inspect RTS and DTR to be 0 */
|
|
return (NOCHIP); /* No chip at this port address */
|
|
|
|
outportb(MCR(pCOM), 0x1f); /* Loopback and RTS&DTR up */
|
|
if ((inportb(MSR(pCOM)) & 0x30) != 0x30) /* Inspect RTS and DTR to be up */
|
|
return (NOCHIP);
|
|
|
|
outportb(MCR(pCOM), olddata); /* Restore original value */
|
|
|
|
/*
|
|
Look for the scratch register
|
|
*/
|
|
olddata = inportb(SCR(pCOM));
|
|
outportb(SCR(pCOM), 0x55);
|
|
if (inportb(SCR(pCOM)) != 0x55)
|
|
return (CHIP8250);
|
|
|
|
outportb(SCR(pCOM), 0xaa);
|
|
if (inportb(SCR(pCOM)) != 0xaa)
|
|
return (CHIP8250);
|
|
|
|
outportb(SCR(pCOM), olddata); /* Restore original value */
|
|
|
|
/*
|
|
Check if there's a FIFO
|
|
*/
|
|
outportb(FCR(pCOM), 1);
|
|
x = inportb(IIR(pCOM));
|
|
outportb(FCR(pCOM), 0x0);
|
|
if ((x & 0x80) == 0)
|
|
return (CHIP16450);
|
|
if ((x & 0x40) == 0)
|
|
return (CHIP16550);
|
|
return (CHIP16550A);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetChipset
|
|
Description:
|
|
Returns the COM port chip set as detected after COMPortOpen.
|
|
*/
|
|
int COMGetChipset(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
return (pCOM->nChipset);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMIsFIFOAvailable
|
|
Description:
|
|
Detects whether support for hardware tx/rx FIFOs is available.
|
|
*/
|
|
int COMIsFIFOAvailable(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
ASSERT(pCOM->nChipset > 0); /* Should be initialized by COMDetect() */
|
|
|
|
if (pCOM->nChipset == 4)
|
|
return (TRUE);
|
|
return (FALSE);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: EnableHardwareFIFO
|
|
Description:
|
|
Enables the hardware FIFO if present.
|
|
*/
|
|
void COMEnableHardwareFIFO(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
|
|
if (pCOM->nParity == '9')
|
|
return;
|
|
|
|
if (pCOM->bFIFOAvailable)
|
|
{
|
|
switch (pCOM->nRXThreshold)
|
|
{
|
|
case 0: /* Use default */
|
|
case 8:
|
|
outportb(FCR(pCOM), 0x87); /* Clear rx/tx FIFOs, set rx FIFO to 8bytes */
|
|
break;
|
|
case 1:
|
|
outportb(FCR(pCOM), 0x07); /* Clear rx/tx FIFOs, set rx FIFO to 1 byte */
|
|
break;
|
|
case 4:
|
|
outportb(FCR(pCOM), 0x47); /* Clear rx/tx FIFOs, set rx FIFO to 4 bytes */
|
|
break;
|
|
case 14:
|
|
outportb(FCR(pCOM), 0xc7); /* Clear rx/tx FIFOs, set rx FIFO to 14 bytes */
|
|
break;
|
|
default:
|
|
ASSERT(0); /* Invalid RX Threshold value */
|
|
}
|
|
pCOM->bFIFOEnabled = TRUE;
|
|
}
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: DisableHardwareFIFO
|
|
Description:
|
|
Disables the hardware FIFO.
|
|
*/
|
|
void COMDisableHardwareFIFO(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
|
|
if (pCOM->bFIFOAvailable)
|
|
{
|
|
outportb(FCR(pCOM), 0);
|
|
pCOM->bFIFOEnabled = FALSE;
|
|
}
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMIsFIFOEnabled
|
|
Description:
|
|
Returns 0 if the hardware fifos for the specific COM port are disabled.
|
|
*/
|
|
int COMIsFIFOEnabled(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
|
|
return (pCOM->bFIFOEnabled);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetRXThreshold
|
|
Description:
|
|
Sets threshold to be set as RX hardware fifo threshold.
|
|
Valid values are 1, 4, 8 and 14 to correspond to
|
|
hardware trigger levels.
|
|
*/
|
|
void COMSetRXThreshold(int nCOM, int nRXThreshold)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(!pCOM->bInstalled); /* Can be called only prior COMPortOpen() */
|
|
ASSERT(nRXThreshold == 1 || nRXThreshold == 4 ||
|
|
nRXThreshold == 8 || nRXThreshold == 14);
|
|
|
|
pCOM->nRXThreshold = nRXThreshold;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetTXThreshold
|
|
Description:
|
|
*/
|
|
void COMSetTXThreshold(int nCOM, int nTXThreshold)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(!pCOM->bInstalled); /* Can be called only prior COMPortOpen() */
|
|
ASSERT(nTXThreshold >= 1 && nTXThreshold <= 16);
|
|
|
|
pCOM->nTXThreshold = nTXThreshold;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetRXThreshold
|
|
Description:
|
|
*/
|
|
int COMGetRXThreshold(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
if (pCOM->nRXThreshold == 0)
|
|
return (DEFAULT_RX_THRESHOLD);
|
|
return (pCOM->nRXThreshold);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetTXThreshold
|
|
Description:
|
|
*/
|
|
int COMGetTXThreshold(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
if (pCOM->nTXThreshold == 0)
|
|
return (DEFAULT_RX_THRESHOLD);
|
|
return (pCOM->nTXThreshold);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMPortOpen
|
|
Description:
|
|
Opens a specific COM port for operation. The initial operating
|
|
conditions are determined by the arguments.
|
|
*/
|
|
int COMPortOpen(int nCOM, long nBauds, int nWordLen, int nParity,
|
|
int nStopBits, int nFlowControl, void (*EventHandler)(int nEvent))
|
|
{
|
|
struct COMDesc *pCOM;
|
|
unsigned char x;
|
|
int nRXQueueSize;
|
|
int nTXQueueSize;
|
|
char *pRXQueue;
|
|
char *pTXQueue;
|
|
char *pRXStatQueue;
|
|
char *pTXStatQueue;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
if (!bCOMInit)
|
|
if (!COMInit())
|
|
return (COMERR_GENERAL);
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bHardwareSet);
|
|
ASSERT(!pCOM->bInstalled); /* Should be opened only once */
|
|
|
|
if ((pCOM->nChipset = COMDetect(nCOM)) == NOCHIP)
|
|
return (COMERR_NOCHIP);
|
|
|
|
pCOM->bFIFOAvailable = COMIsFIFOAvailable(nCOM);
|
|
if (nParity == '9')
|
|
COMDisableHardwareFIFO(nCOM);
|
|
else
|
|
COMEnableHardwareFIFO(nCOM);
|
|
|
|
/*
|
|
Clear all present events that cause IRQ pending.
|
|
*/
|
|
do
|
|
{
|
|
inportb(RBR(pCOM));
|
|
inportb(LSR(pCOM));
|
|
inportb(MSR(pCOM));
|
|
x = inportb(IIR(pCOM));
|
|
}
|
|
while ((x & 0x1) == 0); /* Until IRQ pending */
|
|
|
|
/*
|
|
Set the operating parameters -- bauds, parity, etc.
|
|
*/
|
|
COMSetTransmitParameters(nCOM, nBauds, nWordLen, nParity, nStopBits);
|
|
COMSetFlowControl(nCOM, nFlowControl);
|
|
COMSetFlowChars(nCOM, 0x11, 0x13);
|
|
COMSetEventHandler(nCOM, EventHandler);
|
|
|
|
/*
|
|
Init receive and transmit queues
|
|
*/
|
|
nRXQueueSize = DEFAULT_QUEUE_SIZE;
|
|
nTXQueueSize = DEFAULT_QUEUE_SIZE;
|
|
if (pCOM->nRXQueueSize != 0)
|
|
nRXQueueSize = pCOM->nRXQueueSize;
|
|
if (pCOM->nTXQueueSize != 0)
|
|
nTXQueueSize = pCOM->nTXQueueSize;
|
|
pRXQueue = NULL;
|
|
pTXQueue = NULL;
|
|
pRXStatQueue = NULL;
|
|
pTXStatQueue = NULL;
|
|
pRXQueue = (char*)malloc(nRXQueueSize + 1); /* + 1: As Head never should tread on Tail */
|
|
pTXQueue = (char*)malloc(nTXQueueSize + 1);
|
|
pRXStatQueue = (char*)malloc(nRXQueueSize * sizeof(struct COMStat) + 1);
|
|
pTXStatQueue = (char*)malloc(nTXQueueSize * sizeof(struct COMStat) + 1);
|
|
if (pRXQueue == NULL || pTXQueue == NULL || pRXStatQueue == NULL || pTXStatQueue == NULL)
|
|
{
|
|
free_queues:
|
|
if (pRXQueue != NULL)
|
|
free(pRXQueue);
|
|
if (pTXQueue != NULL)
|
|
free(pTXQueue);
|
|
if (pRXStatQueue != NULL)
|
|
free(pRXStatQueue);
|
|
if (pTXStatQueue != NULL)
|
|
free(pTXStatQueue);
|
|
return (COMERR_NOMEMORY);
|
|
}
|
|
if (LockData(pRXQueue, nRXQueueSize) == -1)
|
|
goto free_queues;
|
|
if (LockData(pTXQueue, nTXQueueSize) == -1)
|
|
{
|
|
UnlockData(pRXQueue, nRXQueueSize);
|
|
goto free_queues;
|
|
}
|
|
if (LockData(pRXStatQueue, nRXQueueSize * sizeof(struct COMStat)) == -1)
|
|
{
|
|
UnlockData(pRXQueue, nRXQueueSize);
|
|
UnlockData(pTXQueue, nTXQueueSize);
|
|
goto free_queues;
|
|
}
|
|
if (LockData(pTXStatQueue, nTXQueueSize * sizeof(struct COMStat)) == -1)
|
|
{
|
|
UnlockData(pRXQueue, nRXQueueSize);
|
|
UnlockData(pTXQueue, nTXQueueSize);
|
|
UnlockData(pRXStatQueue, nRXQueueSize * sizeof(struct COMStat));
|
|
goto free_queues;
|
|
}
|
|
/* Attach the allocated buffers to the queues */
|
|
QUEUEInit(&pCOM->RXQueue, pRXQueue, nRXQueueSize + 1);
|
|
QUEUEInit(&pCOM->TXQueue, pTXQueue, nTXQueueSize + 1);
|
|
QUEUEInit(&pCOM->RXStatQueue, pRXStatQueue, nRXQueueSize * sizeof(struct COMStat) + 1);
|
|
QUEUEInit(&pCOM->TXStatQueue, pTXStatQueue, nTXQueueSize * sizeof(struct COMStat) + 1);
|
|
|
|
/*
|
|
Disable the interrupts.
|
|
Install the IRQ handler.
|
|
Set OUT2 bit.
|
|
Enable all IRQs in the Interrupt Enable Register (IER).
|
|
Enable the IRQ in the 8259 Interrupt controller.
|
|
*/
|
|
outportb(IER(pCOM), 0); /* Disable all interrupts (if before were enabled) */
|
|
disable();
|
|
|
|
AttachCOMPort(nCOM);
|
|
|
|
outportb(MCR(pCOM), inportb(MCR(pCOM)) | 0x8); /* OUT2 bit */
|
|
outportb(IER(pCOM), 0x0d); /* Enable all interrupts but tx */
|
|
pCOM->nSaveIER = 0x0d;
|
|
pCOM->bHandlingIRQ = FALSE; /* Altered in COMHandler */
|
|
enable();
|
|
|
|
pCOM->bInstalled = TRUE;
|
|
return (0);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetTransmitParameters
|
|
Description:
|
|
Sets the transmition parameters for a specific COM port.
|
|
*/
|
|
void COMSetTransmitParameters(int nCOM, long nBauds, int nWordLen,
|
|
int nParity, int nStopBits)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
long nDivisor;
|
|
int nDivLow;
|
|
int nDivHi;
|
|
int nParityIndex;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(nWordLen >= 5 && nWordLen <= 8);
|
|
ASSERT(nStopBits == 1 || nStopBits == 2);
|
|
|
|
pCOM->nBauds = nBauds;
|
|
pCOM->nParity = nParity;
|
|
pCOM->nWordLen = nWordLen;
|
|
pCOM->nStopBits = nStopBits;
|
|
|
|
nParityIndex = 0; /* No parity */
|
|
switch (nParity)
|
|
{
|
|
case 'N':
|
|
break;
|
|
case 'O':
|
|
nParityIndex = 1; /* Odd parity */
|
|
break;
|
|
case 'E':
|
|
nParityIndex = 3; /* Even parity */
|
|
break;
|
|
case 'M':
|
|
nParityIndex = 5; /* Mark bit */
|
|
break;
|
|
case 'S':
|
|
nParityIndex = 7; /* Space bit */
|
|
break;
|
|
case '9':
|
|
nParityIndex = 7; /* Emulate 9th bit NOT READY */
|
|
break;
|
|
default:
|
|
ASSERT(0); /* Invalid parity specification */
|
|
}
|
|
|
|
nDivisor = 115200L / nBauds;
|
|
nDivLow = (int)(nDivisor & 0xff);
|
|
nDivHi = (int)((nDivisor >> 8) * 0xff);
|
|
|
|
outportb(LCR(pCOM), 0x80); /* DLAB = 1 to access baud rate regs */
|
|
outportb(DLL(pCOM), nDivLow);
|
|
outportb(DLM(pCOM), nDivHi);
|
|
|
|
/* Set parity flag, stop bits and data size and DLAB = 0 */
|
|
pCOM->nSaveLCR = (nWordLen - 5) | (nParityIndex << 3) | ((nStopBits - 1) << 2);
|
|
outportb(LCR(pCOM), pCOM->nSaveLCR);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetTransmitParameters
|
|
Description:
|
|
Returns the transmition parameters set by COMSetTransmitParamaters()
|
|
or COMPortOpen().
|
|
*/
|
|
void COMGetTransmitParameters(int nCOM, long *pBauds, int *pWordLen,
|
|
int *pParity, int *pStopBits)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pBauds != NULL);
|
|
ASSERT(pWordLen != NULL);
|
|
ASSERT(pParity != NULL);
|
|
ASSERT(pStopBits != NULL);
|
|
|
|
*pBauds = pCOM->nBauds;
|
|
*pParity = pCOM->nParity;
|
|
*pWordLen = pCOM->nWordLen;
|
|
*pStopBits = pCOM->nStopBits;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetFlowControl
|
|
Description:
|
|
NOT READY
|
|
*/
|
|
void COMSetFlowControl(int nCOM, int nFlowControl)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
pCOM->nFlowControl = nFlowControl;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetFlowControl
|
|
Description:
|
|
NOT READY
|
|
*/
|
|
int COMGetFlowControl(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
return (pCOM->nFlowControl);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetFlowChars
|
|
Description:
|
|
NOT READY
|
|
*/
|
|
void COMSetFlowChars(int nCOM, int iXONchar, int iXOFFchar)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
if( iXONchar >= 0 )
|
|
pCOM->cXon = (char)iXONchar;
|
|
if( iXOFFchar >= 0 )
|
|
pCOM->cXoff = (char)iXOFFchar;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetEventHandler
|
|
Description:
|
|
Attaches an user event handler to be called on every COM port event
|
|
that occures.
|
|
*/
|
|
void COMSetEventHandler(int nCOM, void (*EventHandler)(int nEvent))
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
pCOM->EventHandler = EventHandler;
|
|
}
|
|
END_OF_FUNCTION(COMSetEventHandler);
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetEventHandler
|
|
Description:
|
|
Returns the address of user handler attached to be invoked on COM
|
|
events. Returns NULL if no user handler attahed.
|
|
*/
|
|
void COMGetEventHandler(int nCOM, void (**EventHandler)(int nEvent))
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(EventHandler != NULL);
|
|
|
|
*EventHandler = pCOM->EventHandler;
|
|
}
|
|
END_OF_FUNCTION(COMGetEventHandler);
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetTXQueueSize
|
|
Description:
|
|
Sets new value for the tx fifo size.
|
|
*/
|
|
void COMSetTXQueueSize(int nCOM, int nTXQueueSize)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(!pCOM->bInstalled); /* Can be called only prior COMPortOpen() */
|
|
ASSERT(nTXQueueSize > 0);
|
|
|
|
pCOM->nTXQueueSize = nTXQueueSize;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetTXQueueSize
|
|
Description:
|
|
Returns the current size of the tx fifo.
|
|
*/
|
|
int COMGetTXQueueSize(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
if (pCOM->nTXQueueSize == 0)
|
|
return (DEFAULT_QUEUE_SIZE);
|
|
return (pCOM->nTXQueueSize);
|
|
}
|
|
END_OF_FUNCTION(COMGetTXQueueSize);
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetRXQueueSize
|
|
Description:
|
|
Sets new value for the rx fifo size.
|
|
*/
|
|
void COMSetRXQueueSize(int nCOM, int nRXQueueSize)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(!pCOM->bInstalled); /* Can be called only prior COMPortOpen() */
|
|
ASSERT(nRXQueueSize > 0);
|
|
|
|
pCOM->nRXQueueSize = nRXQueueSize;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetRXQueueSize
|
|
Description:
|
|
Returns the current size of the rx fifo.
|
|
*/
|
|
int COMGetRXQueueSize(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
|
|
if (pCOM->nRXQueueSize == 0)
|
|
return (DEFAULT_QUEUE_SIZE);
|
|
return (pCOM->nRXQueueSize);
|
|
}
|
|
END_OF_FUNCTION(COMGetRXQueueSize);
|
|
|
|
/* ************************************************************************
|
|
Function: COMPortClose
|
|
Description:
|
|
Terminates all operations for a specific COM.
|
|
*/
|
|
void COMPortClose(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
/*
|
|
Mask all the interrupts in the COM port
|
|
*/
|
|
outportb(MCR(pCOM), inportb(MCR(pCOM)) & ~0x8); /* OUT2 bit to 0 */
|
|
outportb(IER(pCOM), 0);
|
|
DisableIRQ(pCOM->nIRQ);
|
|
disable();
|
|
|
|
/*
|
|
Dispose the rx and tx queues.
|
|
*/
|
|
UnlockData(pCOM->RXQueue.pBuf, pCOM->RXQueue.nBufSize);
|
|
UnlockData(pCOM->TXQueue.pBuf, pCOM->TXQueue.nBufSize);
|
|
UnlockData(pCOM->RXStatQueue.pBuf, pCOM->RXStatQueue.nBufSize);
|
|
UnlockData(pCOM->TXStatQueue.pBuf, pCOM->TXStatQueue.nBufSize);
|
|
free(QUEUEDetachBuf(&pCOM->RXQueue));
|
|
free(QUEUEDetachBuf(&pCOM->TXQueue));
|
|
free(QUEUEDetachBuf(&pCOM->RXStatQueue));
|
|
free(QUEUEDetachBuf(&pCOM->TXStatQueue));
|
|
|
|
/*
|
|
Detach from the COM wrapper.
|
|
*/
|
|
DetachCOMPort(nCOM);
|
|
enable();
|
|
|
|
COMDisableHardwareFIFO(nCOM); /* Some programs expect disabled FIFOs */
|
|
|
|
pCOM->bInstalled = FALSE;
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMPortCloseAll
|
|
Description:
|
|
Closes all the opened COM ports.
|
|
*/
|
|
void COMPortCloseAll(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < COMMAX; ++i)
|
|
if (COMs[i].bInstalled)
|
|
COMPortClose(i);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMActivateTransmition
|
|
Description:
|
|
This function is to be invoked after something is put in the
|
|
tx fifo queue and is necessary to start sending the data.
|
|
*/
|
|
static void COMActivateTransmition(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
/*
|
|
Check if transmittion is activated.
|
|
*/
|
|
if ((pCOM->nSaveIER & 0x2) != 0)
|
|
return; /* Transmittion is already running */
|
|
|
|
/*
|
|
Activate transmition:
|
|
Set IER to enable tx interrupts and as the last char
|
|
of the last tx is already sent, by enabling
|
|
IER an IRQ will be generated and tx will be initiated.
|
|
The tx handler will start to extract bytes from the tx fifo queue.
|
|
*/
|
|
pCOM->nSaveIER |= 0x2;
|
|
outportb(IER(pCOM), pCOM->nSaveIER);
|
|
}
|
|
END_OF_FUNCTION(COMActivateTransmition);
|
|
|
|
/* ************************************************************************
|
|
Function: COMWriteChar
|
|
Description:
|
|
Writes a character to the tx fifo buffer and initiates
|
|
transmition if still not running.
|
|
*/
|
|
int COMWriteChar(int nCOM, char c, const struct COMStat *pStat)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nTXFree;
|
|
#ifdef _DEBUG
|
|
int nTXStatFree;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
#ifdef _DEBUG
|
|
if (pCOM->nParity == '9')
|
|
ASSERT(pStat != NULL); /* It is mandatory to supply 9th bit values */
|
|
#endif
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nTXFree = QUEUECalcFree(&pCOM->TXQueue);
|
|
#ifdef _DEBUG
|
|
nTXStatFree = QUEUECalcFree(&pCOM->TXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nTXStatFree % sizeof(struct COMStat)) == 0);
|
|
|
|
if (nTXFree == 0) /* No room for one more character */
|
|
return (COMERR_TXOVERFLOW);
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
QUEUEPut(&pCOM->TXQueue, &c, 1);
|
|
if (pStat != NULL)
|
|
QUEUEPut(&pCOM->TXStatQueue, (char *)pStat, sizeof(struct COMStat));
|
|
else
|
|
QUEUEAdvance(&pCOM->TXStatQueue, &pCOM->TXStatQueue.nTail, sizeof(struct COMStat));
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
COMActivateTransmition(nCOM);
|
|
return (0);
|
|
}
|
|
END_OF_FUNCTION(COMWriteChar);
|
|
|
|
#ifndef DISABLE_TIMING
|
|
/* ************************************************************************
|
|
Function: COMWriteCharTimed
|
|
Description:
|
|
Writes one characters to the tx buffer, waits for the character
|
|
to be sent, checks timeout.
|
|
nTimeOut = -1 - wait indefinitely
|
|
*/
|
|
int COMWriteCharTimed(int nCOM, char c, const struct COMStat *pStat, int nTimeOut)
|
|
{
|
|
#ifdef _DEBUG
|
|
struct COMDesc *pCOM;
|
|
#endif
|
|
unsigned long int nTime;
|
|
|
|
#ifdef _DEBUG
|
|
pCOM = &COMs[nCOM];
|
|
#endif
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(nTimeOut != 0);
|
|
|
|
nTime = nTimerValue; /* Get current timer value */
|
|
if (nTimeOut != -1)
|
|
nTimeOut /= 50; /* According to the current timer precision */
|
|
|
|
/* Wait to put the char */
|
|
while (COMWriteChar(nCOM, c, pStat) == COMERR_TXOVERFLOW)
|
|
{
|
|
if (nTimeOut != -1)
|
|
if (nTimerValue - nTime > (unsigned long int)nTimeOut)
|
|
return (COM_TIMEOUT);
|
|
}
|
|
|
|
/* Wait for the character to be sent */
|
|
while (!COMIsTXBufferSent(nCOM))
|
|
{
|
|
if (nTimeOut != -1)
|
|
if (nTimerValue - nTime > (unsigned long int)nTimeOut)
|
|
return (COM_TIMEOUT);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
#endif /* ifndef DISABLE_TIMING */
|
|
|
|
/* ************************************************************************
|
|
Function: COMWriteBuffer
|
|
Description:
|
|
Writes a block of data to the port.
|
|
This routine will only write the number of characters that will fit
|
|
in the output buffer. It may return before the requested number has
|
|
been sent if the buffer fills up with COMERR_TXOVERFLOW. *nCount will
|
|
contain the actual number of characters sent from the buffer.
|
|
*/
|
|
int COMWriteBuffer(int nCOM, const char *pBuf, const struct COMStat *pStatBuf,
|
|
int nSize, int *nCount)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nTXFree;
|
|
int nStoreCount;
|
|
#ifdef _DEBUG
|
|
int nTXStatFree;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pBuf != NULL);
|
|
ASSERT(nSize > 0);
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nTXFree = QUEUECalcFree(&pCOM->TXQueue);
|
|
#ifdef _DEBUG
|
|
nTXStatFree = QUEUECalcFree(&pCOM->TXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nTXStatFree % sizeof(struct COMStat)) == 0);
|
|
|
|
nStoreCount = nSize;
|
|
if (nStoreCount > nTXFree)
|
|
nStoreCount = nTXFree;
|
|
|
|
if (nStoreCount > 0)
|
|
{
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
QUEUEPut(&pCOM->TXQueue, pBuf, nStoreCount);
|
|
if (pStatBuf != NULL)
|
|
QUEUEPut(&pCOM->TXStatQueue, (char *)pStatBuf, nStoreCount * sizeof(struct COMStat));
|
|
else
|
|
QUEUEAdvance(&pCOM->TXStatQueue, &pCOM->TXStatQueue.nTail, nStoreCount * sizeof(struct COMStat));
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
COMActivateTransmition(nCOM);
|
|
}
|
|
|
|
if (nCount != NULL)
|
|
*nCount = nStoreCount;
|
|
if (nStoreCount != nSize)
|
|
return (COMERR_TXOVERFLOW);
|
|
return (0);
|
|
}
|
|
END_OF_FUNCTION(COMWriteBuffer);
|
|
|
|
#ifndef DISABLE_TIMING
|
|
/* ************************************************************************
|
|
Function: COMWriteBufferTimed
|
|
Description:
|
|
Writes block of characters to the tx buffer, waits to be sent,
|
|
checks timeout.
|
|
nTimeOut = -1 - wait indefinitely
|
|
If time out expires *nCount will contain the number of the
|
|
characters sent or stored in the tx buffer.
|
|
*/
|
|
int COMWriteBufferTimed(int nCOM, const char *pBuf, const struct COMStat *pStatBuf,
|
|
int nSize, int *nCount, int nTimeOut)
|
|
{
|
|
#ifdef _DEBUG
|
|
struct COMDesc *pCOM;
|
|
#endif
|
|
unsigned long int nTime;
|
|
int nBlockCount;
|
|
|
|
#ifdef _DEBUG
|
|
pCOM = &COMs[nCOM];
|
|
#endif
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pBuf != NULL);
|
|
ASSERT(nTimeOut != 0);
|
|
ASSERT(nSize > 0);
|
|
|
|
nTime = nTimerValue; /* Get current timer value */
|
|
if (nTimeOut != -1)
|
|
nTimeOut /= 50; /* According to the current timer precision */
|
|
|
|
/* Wait to put whole block */
|
|
while (nSize > 0)
|
|
{
|
|
if (COMWriteBuffer(nCOM, pBuf, pStatBuf, nSize, &nBlockCount) == COMERR_TXOVERFLOW)
|
|
{
|
|
/* Update nSize, pBuf and pStatBuf by the portion that
|
|
has been sent */
|
|
nSize -= nBlockCount;
|
|
pBuf += nBlockCount;
|
|
if (pStatBuf != NULL)
|
|
pStatBuf += nBlockCount;
|
|
}
|
|
else
|
|
{
|
|
/* All the buffer is sent */
|
|
nSize = 0;
|
|
break;
|
|
}
|
|
|
|
if (nTimeOut != -1)
|
|
if (nTimerValue - nTime > (unsigned long int)nTimeOut)
|
|
{
|
|
*nCount = nSize;
|
|
return (COM_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
/* Wait for the character block to be sent */
|
|
while (!COMIsTXBufferSent(nCOM))
|
|
{
|
|
if (nTimeOut != -1)
|
|
if (nTimerValue - nTime > (unsigned long int)nTimeOut)
|
|
{
|
|
*nCount = nSize;
|
|
return (COM_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
#endif /* ifndef DISABLE_TIMING */
|
|
|
|
/* ************************************************************************
|
|
Function: COMClearTXBuffer
|
|
Description:
|
|
Clear all the characters currently in the tx buffer.
|
|
*/
|
|
void COMClearTXBuffer(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
pCOM->TXQueue.nHead = pCOM->TXQueue.nTail = 0;
|
|
pCOM->TXStatQueue.nHead = pCOM->TXStatQueue.nTail = 0;
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
}
|
|
END_OF_FUNCTION(COMClearTXBuffer);
|
|
|
|
/* ************************************************************************
|
|
Function: COMTXBufferFree
|
|
Description:
|
|
Returns an integer indicating how much space is availabe in
|
|
the transmit queue.
|
|
*/
|
|
int COMTXBufferFree(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nFree;
|
|
#ifdef _DEBUG
|
|
int nStatFree;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nFree = QUEUECalcFree(&pCOM->TXQueue);
|
|
#ifdef _DEBUG
|
|
nStatFree = QUEUECalcFree(&pCOM->TXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatFree % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nFree == nStatFree / (int)sizeof(struct COMStat));
|
|
|
|
return (nFree);
|
|
}
|
|
END_OF_FUNCTION(COMTXBufferFree);
|
|
|
|
/* ************************************************************************
|
|
Function: COMTXBufferUsed
|
|
Description:
|
|
Returns an integer telling the number of characters currently in
|
|
the transmit queue.
|
|
*/
|
|
int COMTXBufferUsed(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nUsed;
|
|
#ifdef _DEBUG
|
|
int nStatUsed;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nUsed = QUEUECalcOccupied(&pCOM->TXQueue);
|
|
#ifdef _DEBUG
|
|
nStatUsed = QUEUECalcOccupied(&pCOM->TXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatUsed % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nUsed == nStatUsed / (int)sizeof(struct COMStat));
|
|
|
|
return (nUsed);
|
|
}
|
|
END_OF_FUNCTION(COMTXBufferUsed);
|
|
|
|
/* ************************************************************************
|
|
Function: COMIsTXBufferSent
|
|
Description:
|
|
Checks whether last transmition completed.
|
|
This function doesn't check whether the TX buffer is empty, which
|
|
in case of hardware TX FIFO buffers is very unreliable indication,
|
|
but instead checks whether the COM port transmittion is still
|
|
running or not.
|
|
*/
|
|
int COMIsTXBufferSent(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
/*
|
|
Check if transmittion is activated.
|
|
*/
|
|
if ((pCOM->nSaveIER & 0x2) != 0)
|
|
return (FALSE); /* Transmittion is still running */
|
|
|
|
return (TRUE); /* Last transmittion has completed */
|
|
}
|
|
END_OF_FUNCTION(COMIsTXBufferSent);
|
|
|
|
/* ************************************************************************
|
|
Function: COMReadChar
|
|
Description:
|
|
Reads a character from the COM port receive buffer.
|
|
*/
|
|
int COMReadChar(int nCOM, char *pChar, struct COMStat *pStat)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nOccupied;
|
|
int nCOMError;
|
|
#ifdef _DEBUG
|
|
int nStatOccupied;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pChar != NULL);
|
|
|
|
nCOMError = pCOM->nCOMError;
|
|
if (nCOMError)
|
|
{
|
|
pCOM->nCOMError = 0; /* Clear the error */
|
|
return (nCOMError);
|
|
}
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nOccupied = QUEUECalcOccupied(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatOccupied = QUEUECalcOccupied(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatOccupied % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nOccupied == nStatOccupied / (int)sizeof(struct COMStat));
|
|
|
|
if (nOccupied > 0)
|
|
{
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
QUEUEGet(&pCOM->RXQueue, pChar, 1);
|
|
if (pStat != NULL)
|
|
QUEUEGet(&pCOM->RXStatQueue, (char *)pStat, sizeof(struct COMStat));
|
|
else
|
|
QUEUEAdvance(&pCOM->RXStatQueue, &pCOM->RXStatQueue.nHead, sizeof(struct COMStat));
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
}
|
|
else
|
|
return (COM_BUFEMPTY);
|
|
|
|
return (0); /* No error and a character has been extracted */
|
|
}
|
|
END_OF_FUNCTION(COMReadChar);
|
|
|
|
#ifndef DISABLE_TIMING
|
|
/* ************************************************************************
|
|
Function: COMReadCharTimed
|
|
Description:
|
|
Reads a character from the COM port receive buffer. If no characters
|
|
available will wait up to nTimeOut miliseconds to retrieve one.
|
|
nTimeOut = -1 - wait indefinitely
|
|
*/
|
|
int COMReadCharTimed(int nCOM, char *pChar, struct COMStat *pStat, int nTimeOut)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nOccupied;
|
|
int nCOMError;
|
|
int bWaitChar;
|
|
unsigned long int nTime;
|
|
#ifdef _DEBUG
|
|
int nStatOccupied;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pChar != NULL);
|
|
ASSERT(nTimeOut != 0);
|
|
|
|
nCOMError = pCOM->nCOMError;
|
|
if (nCOMError)
|
|
{
|
|
pCOM->nCOMError = 0; /* Clear the error */
|
|
return (nCOMError);
|
|
}
|
|
|
|
nTime = nTimerValue; /* Get current timer value */
|
|
if (nTimeOut != -1)
|
|
nTimeOut /= 50; /* According to the current timer precision */
|
|
|
|
bWaitChar = TRUE;
|
|
while (bWaitChar)
|
|
{
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nOccupied = QUEUECalcOccupied(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatOccupied = QUEUECalcOccupied(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatOccupied % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nOccupied == nStatOccupied / sizeof(struct COMStat));
|
|
|
|
if (nTimeOut != -1)
|
|
if (nTimerValue - nTime > (unsigned long int)nTimeOut)
|
|
return (COM_BUFEMPTY);
|
|
|
|
if (nOccupied > 0)
|
|
{
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
QUEUEGet(&pCOM->RXQueue, pChar, 1);
|
|
if (pStat != NULL)
|
|
QUEUEGet(&pCOM->RXStatQueue, (char *)pStat, sizeof(struct COMStat));
|
|
else
|
|
QUEUEAdvance(&pCOM->RXStatQueue, &pCOM->RXStatQueue.nHead, sizeof(struct COMStat));
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
bWaitChar = FALSE;
|
|
}
|
|
}
|
|
|
|
return (0); /* No error and a character has been extracted */
|
|
}
|
|
#endif /* ifndef DISABLE_TIMING */
|
|
|
|
/* ************************************************************************
|
|
Function: COMReadBuffer
|
|
Description:
|
|
Reads a maximum of nCount characters from COMs rx queue
|
|
to a specific buffer.
|
|
In the rx buffer should have at least nCount characters available
|
|
upon calling this function.
|
|
*/
|
|
int COMReadBuffer(int nCOM, char *pBuf, struct COMStat *pStatBuf, int nCount)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nOccupied;
|
|
int nCOMError;
|
|
#ifdef _DEBUG
|
|
int nStatOccupied;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pBuf != NULL);
|
|
ASSERT(nCount > 0);
|
|
|
|
nCOMError = pCOM->nCOMError;
|
|
if (nCOMError)
|
|
{
|
|
pCOM->nCOMError = 0; /* Clear the error */
|
|
return (nCOMError);
|
|
}
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nOccupied = QUEUECalcOccupied(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatOccupied = QUEUECalcOccupied(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatOccupied % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nOccupied == nStatOccupied / (int)sizeof(struct COMStat));
|
|
|
|
if (nOccupied < nCount)
|
|
return (COM_BUFEMPTY);
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
QUEUEGet(&pCOM->RXQueue, pBuf, nCount);
|
|
if (pStatBuf != NULL)
|
|
QUEUEGet(&pCOM->RXStatQueue, (char *)pStatBuf, nCount * sizeof(struct COMStat));
|
|
else
|
|
QUEUEAdvance(&pCOM->RXStatQueue, &pCOM->RXStatQueue.nHead, nCount * sizeof(struct COMStat));
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
return (0);
|
|
}
|
|
END_OF_FUNCTION(COMReadBuffer);
|
|
|
|
#ifndef DISABLE_TIMING
|
|
/* ************************************************************************
|
|
Function: COMReadBufferTimed
|
|
Description:
|
|
Reads a maximum of nCount characters from COMs rx queue
|
|
to a specific buffer. Will wait up to nTimeOut milliseconds
|
|
to retreive the requested number of characters.
|
|
*nActual will contain the number of characters retreived in pBuf.
|
|
if nActual is NULL no such information will be exported.
|
|
nTimeOut = -1 - wait indefinitely
|
|
*/
|
|
int COMReadBufferTimed(int nCOM, char *pBuf, struct COMStat *pStatBuf, int nCount,
|
|
int *nActual, int nTimeOut)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nOccupied;
|
|
int nCOMError;
|
|
int bWaitChar;
|
|
int nCount2;
|
|
unsigned long int nTime;
|
|
#ifdef _DEBUG
|
|
int nStatOccupied;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pBuf != NULL);
|
|
ASSERT(nTimeOut != 0);
|
|
ASSERT(nCount > 0);
|
|
|
|
nCount2 = nCount; /* Preserve requested */
|
|
|
|
nCOMError = pCOM->nCOMError;
|
|
if (nCOMError)
|
|
{
|
|
pCOM->nCOMError = 0; /* Clear the error */
|
|
return (nCOMError);
|
|
}
|
|
|
|
nTime = nTimerValue; /* Get current timer value */
|
|
if (nTimeOut)
|
|
nTimeOut /= 50; /* According to the current timer precision */
|
|
|
|
bWaitChar = TRUE;
|
|
while (bWaitChar)
|
|
{
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nOccupied = QUEUECalcOccupied(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatOccupied = QUEUECalcOccupied(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatOccupied % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nOccupied == nStatOccupied / sizeof(struct COMStat));
|
|
|
|
if (nTimeOut != -1)
|
|
if (nTimerValue - nTime > (unsigned long int)nTimeOut)
|
|
{
|
|
if (nActual != NULL)
|
|
*nActual = nCount2 - nCount; /* Requested - what remains */
|
|
return (COM_TIMEOUT);
|
|
}
|
|
|
|
if (nOccupied > 0)
|
|
{
|
|
if (nOccupied > nCount)
|
|
nOccupied = nCount; /* Don't exceed pBuf size */
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
QUEUEGet(&pCOM->RXQueue, pBuf, nOccupied);
|
|
if (pStatBuf != NULL)
|
|
QUEUEGet(&pCOM->RXStatQueue, (char *)pStatBuf, nOccupied * sizeof(struct COMStat));
|
|
else
|
|
QUEUEAdvance(&pCOM->RXStatQueue, &pCOM->RXStatQueue.nHead, nOccupied * sizeof(struct COMStat));
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nCount -= nOccupied; /* nCount shows how much remains to be retrieved */
|
|
pBuf += nOccupied;
|
|
if (pStatBuf != NULL)
|
|
pStatBuf += nOccupied;
|
|
if (nCount == 0)
|
|
bWaitChar = FALSE;
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
#endif
|
|
|
|
/* ************************************************************************
|
|
Function: COMPeekChar
|
|
Description:
|
|
Reads the next character availabe in the receive buffer. The
|
|
character is not extracted and remains in the buffer. You can
|
|
only peek one-deep into the buffer.
|
|
*/
|
|
int COMPeekChar(int nCOM, char *pChar, struct COMStat *pStat)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nOccupied;
|
|
int nCOMError;
|
|
int nHead;
|
|
#ifdef _DEBUG
|
|
int nStatOccupied;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
ASSERT(pChar != NULL);
|
|
|
|
nCOMError = pCOM->nCOMError;
|
|
if (nCOMError)
|
|
{
|
|
pCOM->nCOMError = 0; /* Clear the error */
|
|
return (nCOMError);
|
|
}
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nOccupied = QUEUECalcOccupied(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatOccupied = QUEUECalcOccupied(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatOccupied % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nOccupied == nStatOccupied / (int)sizeof(struct COMStat));
|
|
|
|
if (nOccupied > 0)
|
|
{
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nHead = pCOM->RXQueue.nHead; /* Store the current queue head position */
|
|
QUEUEGet(&pCOM->RXQueue, pChar, 1);
|
|
pCOM->RXQueue.nHead = nHead; /* Restore the prior queue head position */
|
|
if (pStat != NULL)
|
|
{
|
|
nHead = pCOM->RXStatQueue.nHead; /* Store the current queue head position */
|
|
QUEUEGet(&pCOM->RXStatQueue, (char *)pStat, sizeof(struct COMStat));
|
|
pCOM->RXStatQueue.nHead = nHead; /* Restore the prior queue head position */
|
|
}
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
}
|
|
else
|
|
return (COM_BUFEMPTY);
|
|
|
|
return (0); /* No error and a character has been extracted */
|
|
}
|
|
END_OF_FUNCTION(COMPeekChar);
|
|
|
|
/* ************************************************************************
|
|
Function: COMClearRXBuffer
|
|
Description:
|
|
Clears all the characters currently in the rx buffer.
|
|
*/
|
|
void COMClearRXBuffer(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
pCOM->RXQueue.nHead = pCOM->RXQueue.nTail = 0;
|
|
pCOM->RXStatQueue.nHead = pCOM->RXStatQueue.nTail = 0;
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
}
|
|
END_OF_FUNCTION(COMClearRXBuffer);
|
|
|
|
/* ************************************************************************
|
|
Function: COMRXBufferFree
|
|
Description:
|
|
Returns an integer indicating how much space is availabe in
|
|
the receive queue.
|
|
*/
|
|
int COMRXBufferFree(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nFree;
|
|
#ifdef _DEBUG
|
|
int nStatFree;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nFree = QUEUECalcFree(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatFree = QUEUECalcFree(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatFree % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nFree == nStatFree / (int)sizeof(struct COMStat));
|
|
|
|
return (nFree);
|
|
}
|
|
END_OF_FUNCTION(COMRXBufferFree);
|
|
|
|
/* ************************************************************************
|
|
Function: COMRXBufferUsed
|
|
Description:
|
|
Returns an integer telling the number of characters currently in
|
|
the receive queue.
|
|
*/
|
|
int COMRXBufferUsed(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nUsed;
|
|
#ifdef _DEBUG
|
|
int nStatUsed;
|
|
#endif
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
ENTER_QUEUE_CRITICAL_SECTION(pCOM);
|
|
nUsed = QUEUECalcOccupied(&pCOM->RXQueue);
|
|
#ifdef _DEBUG
|
|
nStatUsed = QUEUECalcOccupied(&pCOM->RXStatQueue);
|
|
#endif
|
|
LEAVE_QUEUE_CRITICAL_SECTION(pCOM);
|
|
|
|
ASSERT((nStatUsed % sizeof(struct COMStat)) == 0);
|
|
ASSERT(nUsed == nStatUsed / (int)sizeof(struct COMStat));
|
|
|
|
return (nUsed);
|
|
}
|
|
END_OF_FUNCTION(COMRXBufferUsed);
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetDtr
|
|
Description:
|
|
Sets the DTR line to 0 or 1 depending on nControl is 0 or nonzero.
|
|
*/
|
|
void COMSetDtr(int nCOM, int nControl)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
if (nControl)
|
|
outportb(MCR(pCOM), inportb(MCR(pCOM)) | 0x1); /* set DTR bit */
|
|
else
|
|
outportb(MCR(pCOM), inportb(MCR(pCOM)) & ~0x1); /* clear DTR bit */
|
|
}
|
|
END_OF_FUNCTION(COMSetDtr);
|
|
|
|
/* ************************************************************************
|
|
Function: COMSetRts
|
|
Description:
|
|
Sets the RTS line to 0 or 1 depending on nControl is 0 or nonzero.
|
|
*/
|
|
void COMSetRts(int nCOM, int nControl)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
if (nControl)
|
|
outportb(MCR(pCOM), inportb(MCR(pCOM)) | 0x2); /* set RTS bit */
|
|
else
|
|
outportb(MCR(pCOM), inportb(MCR(pCOM)) & ~0x2); /* clear RTS bit */
|
|
}
|
|
END_OF_FUNCTION(COMSetRts);
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetModemStatus
|
|
Description:
|
|
Returns the contents of the modem status register. It is read from
|
|
a variable. Direct reading of MSR may improperly deactivate pending
|
|
IRQ.
|
|
*/
|
|
int COMGetModemStatus(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
int nMSR;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
nMSR = pCOM->nSaveMSR;
|
|
/* clear delta bits to simulate real MSR read operation */
|
|
pCOM->nSaveMSR &= ~( DELTA_CTS | DELTA_DSR | DELTA_RI | DELTA_CD );
|
|
|
|
return (nMSR);
|
|
}
|
|
END_OF_FUNCTION(COMGetModemStatus);
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetCts
|
|
Description:
|
|
Returns the current state of Clear To Send (CTS) modem status line.
|
|
*/
|
|
int COMGetCts(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
return ((pCOM->nSaveMSR & CTS_LINE) == 0 ? 0 : 1);
|
|
}
|
|
END_OF_FUNCTION(COMGetCts);
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetDsr
|
|
Description:
|
|
Returns the current state of Data Set Ready (DSR) modem status line.
|
|
*/
|
|
int COMGetDsr(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
return ((pCOM->nSaveMSR & DSR_LINE) == 0 ? 0 : 1);
|
|
}
|
|
END_OF_FUNCTION(COMGetDsr);
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetRI
|
|
Description:
|
|
Returns the state of the incoming modem status line
|
|
Ring Indicator (RI). Will be 1 when the line is ringing.
|
|
*/
|
|
int COMGetRI(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
return ((pCOM->nSaveMSR & RI_LINE) == 0 ? 0 : 1);
|
|
}
|
|
END_OF_FUNCTION(COMGetRI);
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetCD
|
|
Description:
|
|
Returns the current state of Carrier Detect (CD) line.
|
|
*/
|
|
int COMGetCD(int nCOM)
|
|
{
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
return ((pCOM->nSaveMSR & CD_LINE) == 0 ? 0 : 1);
|
|
}
|
|
END_OF_FUNCTION(COMGetCD);
|
|
|
|
/* ************************************************************************
|
|
Function: COMDisplayDiagCounters
|
|
Description:
|
|
*/
|
|
void COMDisplayDiagCounters(int nCOM)
|
|
{
|
|
#ifdef _DEBUG
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
printf("rx: %d, tx: %d, stat: %d, modem: %d, IRQs: %d\n",
|
|
pCOM->nRX, pCOM->nTX, pCOM->nStat, pCOM->nModem, pCOM->nIRQs);
|
|
#else
|
|
(void)nCOM;
|
|
#endif
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMDisplayCompileSettings
|
|
Description:
|
|
*/
|
|
void COMDisplayCompileSettings(void)
|
|
{
|
|
#ifdef _DEBUG
|
|
printf("COM library version %x.%x\n", COMVER >> 8, COMVER & 0xff);
|
|
#ifdef DISABLE_PREEMPTING
|
|
printf("definition: DISABLE_PREEMPTING\n");
|
|
#endif
|
|
#ifdef DISABLE_TIMING
|
|
printf("definition: DISABLE_TIMING\n");
|
|
#endif
|
|
#ifdef PUSH386
|
|
printf("definition: PUSH386\n");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMGetDiagnosticCounters
|
|
Description:
|
|
*/
|
|
void COMGetDiagnosticCounters(int nCOM, int *nRX, int *nTX, int *nStat,
|
|
int *nModem, int *nIRQs)
|
|
{
|
|
#ifdef _DEBUG
|
|
struct COMDesc *pCOM;
|
|
|
|
pCOM = &COMs[nCOM];
|
|
|
|
ASSERT(bCOMInit); /* Library should be initialized before calling any function */
|
|
ASSERT(nCOM >= 0 && nCOM < COMMAX);
|
|
ASSERT(pCOM->bInstalled); /* Call COMPortOpen() first */
|
|
|
|
if (nRX != NULL)
|
|
*nRX = pCOM->nRX;
|
|
if (nTX != NULL)
|
|
*nTX = pCOM->nTX;
|
|
if (nStat != NULL)
|
|
*nStat = pCOM->nStat;
|
|
if (nModem != NULL)
|
|
*nModem = pCOM->nModem;
|
|
if (nIRQs != NULL)
|
|
*nIRQs = pCOM->nIRQs;
|
|
#else
|
|
(void)nCOM;
|
|
(void)nRX;
|
|
(void)nTX;
|
|
(void)nStat;
|
|
(void)nModem;
|
|
(void)nIRQs;
|
|
#endif
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMExit
|
|
Description:
|
|
Call-back function invoked at the end of the program.
|
|
*/
|
|
static void COMExit(void)
|
|
{
|
|
if (bCOMInit)
|
|
COMShutDown();
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMInit
|
|
Description:
|
|
Initial setup of the library.
|
|
*/
|
|
int COMInit(void)
|
|
{
|
|
ASSERT(!bCOMInit); /* Should be called only once */
|
|
|
|
bCOMInit = TRUE;
|
|
|
|
/* Get hardware descriptions from BIOS */
|
|
if (!COMs[COM1].bHardwareSet)
|
|
{
|
|
COMs[COM1].nCOMAddress = _peekw(0x40, COM1 * 2);
|
|
COMs[COM1].nIRQ = 4;
|
|
COMs[COM1].bHardwareSet = TRUE;
|
|
}
|
|
if (!COMs[COM2].bHardwareSet)
|
|
{
|
|
COMs[COM2].nCOMAddress = _peekw(0x40, COM2 * 2);
|
|
COMs[COM2].nIRQ = 3;
|
|
COMs[COM2].bHardwareSet = TRUE;
|
|
}
|
|
if (!COMs[COM3].bHardwareSet)
|
|
{
|
|
COMs[COM3].nCOMAddress = _peekw(0x40, COM3 * 2);
|
|
COMs[COM3].nIRQ = 4;
|
|
COMs[COM3].bHardwareSet = TRUE;
|
|
}
|
|
if (!COMs[COM4].bHardwareSet)
|
|
{
|
|
COMs[COM4].nCOMAddress = _peekw(0x40, COM4 * 2);
|
|
COMs[COM4].nIRQ = 3;
|
|
COMs[COM4].bHardwareSet = TRUE;
|
|
}
|
|
|
|
/*
|
|
Lock code and data regions that can be used from IRQ routines.
|
|
*/
|
|
if (LOCK_VARIABLE(bCOMInit) == -1)
|
|
return (FALSE);
|
|
if (LOCK_VARIABLE(COMs) == -1)
|
|
return (FALSE);
|
|
if (LOCK_VARIABLE(pAttachedPorts) == -1)
|
|
return (FALSE);
|
|
if (LOCK_VARIABLE(COMWrappers) == -1)
|
|
return (FALSE);
|
|
|
|
if (LOCK_FUNCTION(EnableIRQ) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(DisableIRQ) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(EndIRQ) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMDisableIRQs) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMEnableIRQs) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMWrap0) == -1) /* This will lock COMWrap0()-COMWrap15() */
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(XMemCpy) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(QUEUEPut) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(QUEUEGet) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(QUEUECalcFree) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(QUEUECalcOccupied) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(QUEUEAdvance) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMHandler) == -1)
|
|
return (FALSE);
|
|
|
|
if (LOCK_FUNCTION(COMSetEventHandler) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetEventHandler) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetTXQueueSize) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetRXQueueSize) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMActivateTransmition) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMWriteChar) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMWriteBuffer) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMClearTXBuffer) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMTXBufferFree) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMTXBufferUsed) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMIsTXBufferSent) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMReadChar) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMReadBuffer) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMPeekChar) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMClearRXBuffer) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMRXBufferFree) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMRXBufferUsed) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMSetDtr) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMSetRts) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetModemStatus) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetCts) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetDsr) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetRI) == -1)
|
|
return (FALSE);
|
|
if (LOCK_FUNCTION(COMGetCD) == -1)
|
|
return (FALSE);
|
|
|
|
if (atexit(COMExit) != 0)
|
|
return (FALSE);
|
|
|
|
#ifndef DISABLE_TIMING
|
|
if (!TIMERInit())
|
|
return (FALSE);
|
|
#endif
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/* ************************************************************************
|
|
Function: COMShutDown
|
|
Description:
|
|
Terminates libirary session, should be called upon exiting
|
|
the program.
|
|
*/
|
|
void COMShutDown(void)
|
|
{
|
|
#ifndef DISABLE_TIMING
|
|
TIMERShutDown();
|
|
#endif
|
|
COMPortCloseAll();
|
|
bCOMInit = FALSE;
|
|
}
|