📄 win32serial.c
字号:
/* win32Serial.c - Serial communications routines using WIN32 API functions */
/*
Modification History
--------------------
01f,25jan99,elg Add Flow Control on Serial Backend (SPR 22159)
01e,18nov98,c_c Worked around a problem with WaitCommEvent ().
+ added a mutex to synchronize all driver accesses.
01d,28oct98,c_c Rewritten to be fully supported by WIN95.
01c,13feb98,c_c Solved some timeout Pb.
01b,21jan98,c_c Now using asynchronous mechanism for I/O.
Implemented select-like API. to pause the thread.
1a 17jan95 wmd written.
*/
/*
* We will use the asynchronous notification which can be set with the overlap
* mechanism under NT. With this, we can put a thread into a sleep while
* waiting for incoming char.
*/
/* includes */
#include <windows.h>
#include <stdio.h>
#include "win32Serial.h"
/* defines */
#define TIMEOUT_VALUE 10 /* timeout default value */
#define MULTIPLIER(time, nbytes) (time/nbytes * 1000)
/* typedefs */
/* externals */
/* locals */
static HANDLE hComm = NULL; /* COMM handle */
static OVERLAPPED overlappedRead; /* overlap struct for read ops */
static OVERLAPPED overlappedWrite;/* overlap struct for write ops */
static HANDLE char2readEvent; /* Characters to read Event */
static HANDLE want2writeEvent;/* A thread want to write */
static HANDLE writtenEvent; /* all chars written Event */
static HANDLE driverMutex; /* Serial driver Mutex */
static char InputBuffer[1800];/* input buffer (at least one MTU) */
static DWORD ReadIndex;/* Index of fisrt char to read in buffer */
static DWORD nbByteRead; /* nb char read in a call */
static BOOL pendingChar; /* Is there something pending ? */
static BOOL charWritten=FALSE; /* char have been written */
/* Forward declarations */
static int win32FlushOutput
(
HANDLE hcom
);
static int win32FlushInput
(
HANDLE hcom
);
/*****************************************************************************
*
* win32SerialRead - Reads characters from the serial line.
*
* This routine reads len bytes from the serial communications device.
*
* RETURNS :
* number of read chars,
* -1 on error;
*/
int win32SerialRead
(
HANDLE hcom, /* where to read from ... */
char *buf, /* placeholder */
DWORD len /* length of buffer */
)
{
DWORD buflen = 0;
BOOL status;
/* If we have no chars presents in our buffer do a read op. */
if (!pendingChar)
{
if (WaitForSingleObject (driverMutex, INFINITE) != WAIT_OBJECT_0)
{
return -1;
}
ReadIndex = 0;
nbByteRead = 0;
ResetEvent (char2readEvent);
status = ReadFile (hcom,
InputBuffer,
len<sizeof (InputBuffer)?len:sizeof (InputBuffer),
&nbByteRead,
&overlappedRead);
if (status == FALSE)
{
if (GetLastError ()!= ERROR_IO_PENDING)
{
ReleaseMutex (driverMutex);
return -1; /* error during read op */
}
status = GetOverlappedResult (hcom, &overlappedRead,
&nbByteRead, TRUE);
if (status == FALSE)
{
ReleaseMutex (driverMutex);
return -1; /* error during read op */
}
}
ResetEvent (char2readEvent);
ReleaseMutex (driverMutex);
}
/* Now we have something to read */
/* compute number char to copy */
buflen = (nbByteRead < len?nbByteRead:len);
memcpy (buf, &InputBuffer[ReadIndex], buflen );
/* adjust index */
if (len < nbByteRead) /* still bytes to read */
{
pendingChar = TRUE;
nbByteRead -= len;
ReadIndex += len;
}
else /* buffer empty */
{
pendingChar = FALSE;
ReadIndex = 0;
nbByteRead = 0;
}
return buflen;
}
/*****************************************************************************
*
* win32SerialWrite - Write bytes on the serial line.
*
* This routine writes len chars from str to the serial port.
*
* RETURNS :
* number of chars written,
* -1 on error;
*/
int win32SerialWrite
(
HANDLE hcom, /* where to send the data */
const char *str, /* data buffer */
DWORD len /* length of data */
)
{
BOOL status;
DWORD writtenBytes;
DWORD totalWrittenBytes = 0;
/*
* Release the eventual thread blocked in the win32SerialSelect (), just to
* be sure that we are now the only thead dealing with the serial line
*/
PulseEvent (want2writeEvent);
if (WaitForSingleObject (driverMutex, INFINITE) != WAIT_OBJECT_0)
{
return -1;
}
/* write data to the line... */
do
{
ResetEvent (writtenEvent); /* clear the event */
status = WriteFile (hcom, &str[totalWrittenBytes],
len-totalWrittenBytes, &writtenBytes,
&overlappedWrite);
/* ...and wait till last char is sent. */
if (status == FALSE)
{
if (GetLastError () == ERROR_IO_PENDING)
{
status = GetOverlappedResult (hcom, &overlappedWrite,
&writtenBytes, TRUE);
if (!status)
{
ReleaseMutex (driverMutex);
return 0;
}
}
else
{
ReleaseMutex (driverMutex);
return -1;
}
}
totalWrittenBytes += writtenBytes;
} while (totalWrittenBytes < len);
ReleaseMutex (driverMutex);
ResetEvent (writtenEvent);
charWritten = TRUE;
return totalWrittenBytes;
}
/*****************************************************************************
*
* win32SerialOpen - Opens a serial communications channel,
*
* This routine opens the given serial device for reading and writting.
* <baudRate> specifies the speed of the serial link with the target.
* <hardFlowControl> specifies if there is a CTS/RTS flow control.
*
* RETURNS: the handle on the open device on success, or -1 otherwise.
*
*/
HANDLE win32SerialOpen
(
char * devname, /* device to open (COM1, ...) */
int baudRate, /* line speed */
BOOL hardFlowControl /* CTS/RTS flow control */
)
{
DCB dcb;
COMMTIMEOUTS timeouts;
HANDLE hCom;
BOOL fSuccess;
char deviceName[] = "COMX";
deviceName[3] = devname[3];
/* Serial driver Mutex creation */
driverMutex = CreateMutex (
NULL, // pointer to security attributes
FALSE, // not owned
NULL // no name
);
/* End select event creation */
want2writeEvent = CreateEvent (
NULL, // pointer to security attributes
TRUE, // Manual reset
FALSE, // No char present
NULL // no name
);
/* Char presence Event creation */
char2readEvent = CreateEvent (
NULL, // pointer to security attributes
TRUE, // Manual reset
FALSE, // No char present
NULL // no name
);
/* Written chars Event creation */
writtenEvent = CreateEvent (
NULL, // pointer to security attributes
TRUE, // Manual reset
FALSE, // No char present
NULL // no name
);
if ((char2readEvent == (HANDLE)INVALID_HANDLE_VALUE)||
(writtenEvent == (HANDLE)INVALID_HANDLE_VALUE) ||
(want2writeEvent == (HANDLE)INVALID_HANDLE_VALUE))
{
/* too bad !!*/
return (HANDLE) -1;
}
/* afterward, open the device */
hCom = CreateFile (deviceName,
GENERIC_READ | GENERIC_WRITE,
0, /* share: Must be zero for comm resouces */
NULL, /* no security attrs */
OPEN_EXISTING, /* must use for comm devices */
FILE_FLAG_OVERLAPPED, /* we will process async I/O */
(void*)NULL); /* hTemplate */
if (hCom == (HANDLE)INVALID_HANDLE_VALUE)
{
/* unlucky */
return (HANDLE) -1;
}
/* get the current configuration */
if ( (fSuccess = GetCommState (hCom, &dcb)) == FALSE)
{
return (HANDLE) -1;
}
/*
* fill in DCB with:
* - baud = baudRate (9600 is the default),
* - 8 data bits,
* - no parity,
* - 1 stop bit
* no special signal handled (DTR, CTS, DSR)
*/
dcb.BaudRate = baudRate;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
/* set the CTS/RTS flow control */
if (hardFlowControl)
{
dcb.fOutxCtsFlow = TRUE; /* CTS output flow control */
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; /* RTS handshaking */
dcb.fDtrControl = DTR_CONTROL_ENABLE; /* set DTR signal */
}
if ((fSuccess = SetCommState (hCom, &dcb)) == FALSE)
{
return (HANDLE) -1;
}
/*
* Init the timeout value :
* Set a timeout of 2s max for synchronous read.
*/
GetCommTimeouts (hCom, &timeouts);
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = MULTIPLIER (TIMEOUT_VALUE, 1);
timeouts.ReadTotalTimeoutConstant = 2000; /* set a timeout of 2s max */
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hCom, &timeouts);
ReadIndex = 0; /* init the index */
memset (&overlappedRead, 0, sizeof (overlappedRead));
overlappedRead.hEvent = char2readEvent;
memset (&overlappedWrite, 0, sizeof (overlappedWrite));
overlappedWrite.hEvent = writtenEvent;
pendingChar = FALSE;
SetCommMask (hCom, EV_RXCHAR); /* we will poll the Rx line */
/* OK !! */
hComm = hCom; /* save the Handle */
return hCom;
}
/*****************************************************************************
*
* win32SerialSelect - Select-like routine for WIN32 serial.
*
* Routine to poll the serial port for waitval seconds.
* If waitval = -1 blocks infinitely.
*
* RETURNS:
* 1 : serial port has character(s) for input.
* 0 : nothing to read within the timeout.
* -1 : error.
*/
int win32SerialSelect
(
int waitval /* timeout (in second) */
)
{
int status, returnedStatus;
DWORD commEvent; /* expected event */
HANDLE objects2Wait4[2]; /* objects to wait for array */
/* Check if we have characters left in our internal buffer */
if (pendingChar)
{
return 1;
}
/* adjust for milliseconds */
if (waitval != -1) /* polling forever */
waitval *= 1000;
/* prevent other to perform I/O */
if (WaitForSingleObject (driverMutex, INFINITE) != WAIT_OBJECT_0)
{
return -1;
}
/* Check if we are receiving some characters... */
select_again :
ResetEvent (char2readEvent);
status = WaitCommEvent (hComm, &commEvent, &overlappedRead);
if ( (!status) && (GetLastError ()!= ERROR_IO_PENDING) )
{
ReleaseMutex (driverMutex); /* other can write */
return -1; /* error during wait op */
}
/*
* wait till :
* - timeout,
* - char incomes,
* - another thread wants to write
*/
objects2Wait4[0] = want2writeEvent;
objects2Wait4[1] = char2readEvent;
switch ( WaitForMultipleObjects (2, objects2Wait4, FALSE, waitval) )
{
case WAIT_OBJECT_0 : /* Another thread wants the line !! */
case WAIT_TIMEOUT :
/*
* On timeout or want2writeEvent, dont'consider that we
* have something to read
* We will cancel the posted request, to make sure
* that another thread write request can succeed.
* resetting the event mask will make the GetOverlappedResult call
* to set the commEvent to 0. This will be interpreted as
* nothing to read.
*/
SetCommMask (hComm, EV_RXCHAR); /* fall through the next case */
case (WAIT_OBJECT_0+1) : /* We have received something. */
status = GetOverlappedResult (hComm, &overlappedRead,
&nbByteRead, TRUE);
if (status)
{
returnedStatus = (commEvent&EV_RXCHAR)?1:0;
// For some unknown reasons, we may be awaken although there's
// no pending char in the buffer. It seems to happen after a
// write Op has been issued. If the select occurs after a write
// Op (charWritten flag) will will retry once the select Op to
// work around this error.
if (returnedStatus)
{
DWORD commErrors;
COMSTAT commStatus;// To get the pending input bytes
if (ClearCommError (hComm, &commErrors, &commStatus))
{
returnedStatus = (commStatus.cbInQue>0)?1:0;
// Retry once if this select comes after a write Op
if ((!returnedStatus) && (charWritten))
{
charWritten = FALSE;
goto select_again;
}
}
}
break;
}
default : /* Problem !! */
returnedStatus = -1;
}
/*
* Three reasons can bring us here :
* - Char have been read,
* - Timeout occured,
* - win32SerialWrite pulsed want2write event.
* Anyway, we will clear the event to be coordonated with the current state.
*/
charWritten = FALSE;
ResetEvent (char2readEvent);
ReleaseMutex (driverMutex); /* other can write */
return returnedStatus;
}
/*****************************************************************************
*
* win32SerialClose - close the handle for the communications resource.
*
*/
void win32SerialClose
(
HANDLE hcom /* device to close */
)
{
win32FlushOutput (hcom);
win32FlushInput (hcom);
CloseHandle (hcom);
CloseHandle (char2readEvent);
CloseHandle (want2writeEvent);
CloseHandle (writtenEvent);
CloseHandle (driverMutex);
}
/*****************************************************************************
*
* win32FlushOutput - Flush pending output.
*
*/
int win32FlushOutput
(
HANDLE hcom
)
{
return (FlushFileBuffers (hcom));
}
/*****************************************************************************
*
* win32FlushInput - Flush pending input buffer, the data is lost.
*
*/
int win32FlushInput
(
HANDLE hcom
)
{
return (PurgeComm (hcom, PURGE_RXCLEAR));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -