Initial import.

This commit is contained in:
Jef Driesen 2007-10-29 09:22:19 +00:00
commit 3f33f82bc2
3 changed files with 1246 additions and 0 deletions

82
serial.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef SERIAL_H
#define SERIAL_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct serial serial;
enum parity_t {
SERIAL_PARITY_NONE,
SERIAL_PARITY_EVEN,
SERIAL_PARITY_ODD
};
enum flowcontrol_t {
SERIAL_FLOWCONTROL_NONE,
SERIAL_FLOWCONTROL_HARDWARE,
SERIAL_FLOWCONTROL_SOFTWARE
};
enum queue_t {
SERIAL_QUEUE_INPUT = 0x01,
SERIAL_QUEUE_OUTPUT = 0x02,
SERIAL_QUEUE_BOTH = SERIAL_QUEUE_INPUT | SERIAL_QUEUE_OUTPUT
};
int serial_errcode ();
const char* serial_errmsg ();
int serial_open (serial **device, const char* name);
int serial_close (serial *device);
int serial_configure (serial *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol);
//
// Available read modes:
//
// * Blocking (timeout < 0):
//
// The read function is blocked until all the requested bytes have
// been received. If the requested number of bytes does not arrive,
// the function will block forever.
//
// * Non-blocking (timeout == 0):
//
// The read function returns immediately with the bytes that have already
// been received, even if no bytes have been received.
//
// * Timeout (timeout > 0):
//
// The read function is blocked until all the requested bytes have
// been received. If the requested number of bytes does not arrive
// within the specified amount of time, the function will return
// with the bytes that have already been received.
//
int serial_set_timeout (serial *device, long timeout /* milliseconds */);
int serial_read (serial *device, void* data, unsigned int size);
int serial_write (serial *device, const void* data, unsigned int size);
int serial_flush (serial *device, int queue);
int serial_drain (serial *device);
int serial_send_break (serial *device);
int serial_set_dtr (serial *device, int level);
int serial_set_rts (serial *device, int level);
int serial_get_received (serial *device);
int serial_get_transmitted (serial *device);
int serial_sleep (unsigned long timeout /* milliseconds */);
int serial_timer ();
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SERIAL_H */

678
serial_posix.c Normal file
View File

@ -0,0 +1,678 @@
#define _POSIX_C_SOURCE 199309
#include <stdlib.h> // malloc, free
#include <string.h> // strerror
#include <errno.h> // errno
#include <unistd.h> // open, close, read, write, isatty, usleep
#include <fcntl.h> // fcntl
#include <termios.h> // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak
#include <poll.h> // poll
#include <sys/ioctl.h> // ioctl
#include <sys/time.h> // gettimeofday
#include <time.h> // nanosleep
#include "serial.h"
#include <stdio.h> // perror
#define TRACE(expr) \
{ \
int error = errno; \
fprintf (stderr, "TRACE %s:%d: ", __FILE__, __LINE__); \
perror (expr); \
errno = error; \
}
struct serial {
/*
* The file descriptor corresponding to the serial port.
*/
int fd;
int timeout;
/*
* Serial port settings are saved into this variable immediately
* after the port is opened. These settings are restored when the
* serial port is closed.
*/
struct termios tty;
};
//
// Error reporting.
//
int serial_errcode ()
{
return errno;
}
const char* serial_errmsg ()
{
return strerror (errno);
}
//
// Open the serial port.
//
int
serial_open (serial** out, const char* name)
{
if (out == NULL)
return -1; // EINVAL (Invalid argument)
// Allocate memory.
struct serial *device = malloc (sizeof (struct serial));
if (device == NULL) {
TRACE ("malloc");
return -1; // ENOMEM (Not enough space)
}
// Default to blocking reads.
device->timeout = -1;
// Open the device in non-blocking mode, to return immediately
// without waiting for the modem connection to complete.
device->fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (device->fd == -1) {
TRACE ("open");
free (device);
return -1; // Error during open call.
}
// Restore blocking mode after opening the device. The CLOCAL bit
// of the termios struct will be set to ignore modem control lines.
int flags = fcntl (device->fd, F_GETFL, 0);
if (fcntl (device->fd, F_SETFL, flags & ~O_NONBLOCK) != 0) {
TRACE ("fcntl");
close (device->fd);
free (device);
return -1; // Failed to restore blocking mode.
}
// Retrieve the current terminal attributes, to
// be able to restore them when closing the device.
// It is also used to check if the obtained
// file descriptor represents a terminal device.
if (tcgetattr (device->fd, &device->tty) != 0) {
TRACE ("tcgetattr");
close (device->fd);
free (device);
return -1;
}
*out = device;
return 0;
}
//
// Close the serial port.
//
int
serial_close (serial* device)
{
if (device == NULL)
return 0;
// Restore the initial terminal attributes.
if (tcsetattr (device->fd, TCSANOW, &device->tty) != 0) {
TRACE ("tcsetattr");
close (device->fd);
free (device);
return -1;
}
// Close the device.
if (close (device->fd) != 0) {
TRACE ("close");
free (device);
return -1;
}
// Free memory.
free (device);
return 0;
}
//
// Configure the serial port (baudrate, databits, parity, stopbits and flowcontrol).
//
int
serial_configure (serial *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
// Retrieve the current settings.
struct termios tty = {0};
if (tcgetattr (device->fd, &tty) != 0) {
TRACE ("tcgetattr");
return -1;
}
// Setup raw input/output mode without echo.
tty.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_oflag &= ~(OPOST);
tty.c_lflag &= ~(ICANON | ECHO | ISIG | IEXTEN);
// Enable the receiver (CREAD) and ignore modem control lines (CLOCAL).
tty.c_cflag |= (CLOCAL | CREAD);
// VMIN is the minimum number of characters for non-canonical read
// and VTIME is the timeout in deciseconds for non-canonical read.
// Setting both of these parameters to zero implies that a read
// will return immediately, only giving the currently available
// characters (non-blocking read behaviour).
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
// Set the baud rate.
speed_t baud = 0;
switch (baudrate) {
case 0: baud = B0; break;
case 50: baud = B50; break;
case 75: baud = B75; break;
case 110: baud = B110; break;
case 134: baud = B134; break;
case 150: baud = B150; break;
case 200: baud = B200; break;
case 300: baud = B300; break;
case 600: baud = B600; break;
case 1200: baud = B1200; break;
case 1800: baud = B1800; break;
case 2400: baud = B2400; break;
case 4800: baud = B4800; break;
case 9600: baud = B9600; break;
case 19200: baud = B19200; break;
case 38400: baud = B38400; break;
#ifdef B57600
case 57600: baud = B57600; break;
#endif
#ifdef B115200
case 115200: baud = B115200; break;
#endif
#ifdef B230400
case 230400: baud = B230400; break;
#endif
default:
return -1;
}
if (cfsetispeed (&tty, baud) != 0 ||
cfsetospeed (&tty, baud) != 0) {
TRACE ("cfsetispeed/cfsetospeed");
return -1;
}
// Set the character size.
tty.c_cflag &= ~CSIZE;
switch (databits) {
case 5:
tty.c_cflag |= CS5;
break;
case 6:
tty.c_cflag |= CS6;
break;
case 7:
tty.c_cflag |= CS7;
break;
case 8:
tty.c_cflag |= CS8;
break;
default:
return -1;
}
// Set the parity type.
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
switch (parity) {
case SERIAL_PARITY_NONE: // No parity
tty.c_iflag |= IGNPAR;
break;
case SERIAL_PARITY_EVEN: // Even parity
tty.c_cflag |= PARENB;
tty.c_iflag |= INPCK;
break;
case SERIAL_PARITY_ODD: // Odd parity
tty.c_cflag |= (PARENB | PARODD);
tty.c_iflag |= INPCK;
break;
default:
return -1;
}
// Set the number of stop bits.
switch (stopbits) {
case 1: // One stopbit
tty.c_cflag &= ~CSTOPB;
break;
case 2: // Two stopbits
tty.c_cflag |= CSTOPB;
break;
default:
return -1;
}
// Set the flow control.
switch (flowcontrol) {
case SERIAL_FLOWCONTROL_NONE: // No flow control.
#ifdef CRTSCTS
tty.c_cflag &= ~CRTSCTS;
#endif
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
break;
case SERIAL_FLOWCONTROL_HARDWARE: // Hardware (RTS/CTS) flow control.
#ifdef CRTSCTS
tty.c_cflag |= CRTSCTS;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
break;
#else
return -1; // Hardware flow control is unsupported.
#endif
case SERIAL_FLOWCONTROL_SOFTWARE: // Software (XON/XOFF) flow control.
#ifdef CRTSCTS
tty.c_cflag &= ~CRTSCTS;
#endif
tty.c_iflag |= (IXON | IXOFF);
break;
default:
return -1;
}
// Flush the input and output buffers.
if (tcflush (device->fd, TCIOFLUSH) != 0) {
TRACE ("tcflush");
return -1;
}
// Apply the new settings.
if (tcsetattr (device->fd, TCSANOW, &tty) != 0) {
TRACE ("tcsetattr");
return -1;
}
// tcsetattr() returns success if any of the requested changes could be
// successfully carried out. Therefore, when making multiple changes
// it may be necessary to follow this call with a further call to
// tcgetattr() to check that all changes have been performed successfully.
struct termios active = {0};
if (tcgetattr (device->fd, &active) != 0) {
TRACE ("tcgetattr");
return -1;
}
if (memcmp (&tty, &active, sizeof (struct termios) != 0)) {
TRACE ("memcmp");
return -1;
}
return 0;
}
//
// Configure the serial port (timeouts).
//
int
serial_set_timeout (serial *device, long timeout)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
device->timeout = timeout;
return 0;
}
struct timeouts_t {
int interval;
int total;
int end;
};
static void
timeouts_init_read (serial* device, struct timeouts_t* timeouts, unsigned int count)
{
timeouts->interval = -1;
timeouts->total = (device->timeout >= 0 ? device->timeout : -1);
if (timeouts->total > 0)
timeouts->end = serial_timer () + timeouts->total;
}
static void
timeouts_init_write (serial* device, struct timeouts_t* timeouts, unsigned int count)
{
timeouts->interval = -1;
timeouts->total = -1;
if (timeouts->total > 0)
timeouts->end = serial_timer () + timeouts->total;
}
static int
timeouts_next (const struct timeouts_t* timeouts, unsigned int already)
{
// Default timeout (INFINITE)
int result = -1;
// Calculate the remaining total timeout.
if (timeouts->total != -1) {
if (timeouts->total > 0) {
result = timeouts->end - serial_timer ();
if (result < 0)
result = 0;
} else {
result = 0;
}
}
// Adjust with the interval timeout.
if (already && timeouts->interval != -1) {
if (result == -1 || result > timeouts->interval)
result = timeouts->interval;
}
// Return timeout value.
return result;
}
static int
posix_wait (int fd, const struct timeouts_t* timeouts, int input, unsigned int already)
{
int rc = 0;
do {
// Calculate the remaining timeout.
int timeout = timeouts_next (timeouts, already);
// Wait until the file descriptor is ready for reading/writing, or
// the timeout expires. A file descriptor is considered ready for
// reading/writing when a call to an input/output function with
// O_NONBLOCK clear would not block, whether or not the function
// would transfer data successfully.
/*
fd_set fds;
FD_ZERO (&fds);
FD_SET (fd, &fds);
if (timeout >= 0) {
struct timeval tv;
tv.tv_sec = (timeout / 1000);
tv.tv_usec = (timeout % 1000) * 1000;
if (input)
rc = select (fd + 1, &fds, NULL, NULL, &tv);
else
rc = select (fd + 1, NULL, &fds, NULL, &tv);
} else {
if (input)
rc = select (fd + 1, &fds, NULL, NULL, NULL);
else
rc = select (fd + 1, NULL, &fds, NULL, NULL);
}
*/
struct pollfd pfd = {0};
pfd.fd = fd;
pfd.events = (input ? POLLIN : POLLOUT);
rc = poll (&pfd, 1, timeout);
} while (rc < 0 && errno == EINTR);
return rc;
}
static int
posix_read (int fd, void* buffer, unsigned int count)
{
int rc = 0;
do {
rc = read (fd, buffer, count);
} while (rc == -1 && errno == EINTR);
return rc;
}
static int
posix_write (int fd, const void* buffer, unsigned int count)
{
int rc = 0;
do {
rc = write (fd, buffer, count);
} while (rc == -1 && errno == EINTR);
return rc;
}
int
serial_read (serial* device, void* data, unsigned int size)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
// Initialize the timeout calculation.
struct timeouts_t timeouts = {0};
timeouts_init_read (device, &timeouts, size);
unsigned int nbytes = 0;
while (nbytes < size) {
// Wait until the file descriptor is ready for reading, or the timeout expires.
int rc = posix_wait (device->fd, &timeouts, 1, nbytes);
if (rc < 0) {
TRACE ("posix_wait");
return -1; // Error during select/poll call.
} else if (rc == 0)
break; // Timeout.
// Attempt to read data from the file descriptor.
int n = posix_read (device->fd, data + nbytes, size - nbytes);
if (n < 0) {
TRACE ("posix_read");
return -1; // Error during read call.
} else if (n == 0) {
break; // EOF reached.
} else {
nbytes += n; // Success.
}
}
return nbytes;
}
int
serial_write (serial* device, const void* data, unsigned int size)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
unsigned int nbytes = 0;
while (nbytes < size) {
// Attempt to write data to the file descriptor.
int n = posix_write (device->fd, data + nbytes, size - nbytes);
if (n < 0) {
TRACE ("posix_write");
return -1; // Error during write call.
}
nbytes += n;
}
return nbytes;
}
int
serial_flush (serial *device, int queue)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
int flags = 0;
switch (queue) {
case SERIAL_QUEUE_INPUT:
flags = TCIFLUSH;
case SERIAL_QUEUE_OUTPUT:
flags = TCOFLUSH;
default:
flags = TCIOFLUSH;
}
if (tcflush (device->fd, flags) != 0) {
TRACE ("tcflush");
return -1;
}
return 0;
}
int
serial_drain (serial *device)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
int rc = 0;
do {
rc = tcdrain (device->fd);
} while (rc == -1 && errno == EINTR);
if (rc != 0) {
TRACE ("tcdrain");
return -1;
}
return 0;
}
int
serial_send_break (serial *device)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
if (tcsendbreak (device->fd, 0) != 0) {
TRACE ("tcsendbreak");
return -1;
}
return 0;
}
static int
serial_set_status (int fd, int value, int level)
{
int bits;
if (ioctl (fd, TIOCMGET, &bits)) {
TRACE ("ioctl");
return -1;
}
if (level)
bits |= value;
else
bits &= value;
if (ioctl (fd, TIOCMSET, &bits)) {
TRACE ("ioctl");
return -1;
}
return 0;
}
int
serial_set_dtr (serial *device, int level)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
return serial_set_status (device->fd, TIOCM_DTR, level);
}
int
serial_set_rts (serial *device, int level)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
return serial_set_status (device->fd, TIOCM_RTS, level);
}
int
serial_get_received (serial *device)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
int bytes = 0;
if (ioctl (device->fd, TIOCINQ, &bytes) != 0) {
TRACE ("ioctl");
return -1;
}
return bytes;
}
int
serial_get_transmitted (serial *device)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
int bytes = 0;
if (ioctl (device->fd, TIOCOUTQ, &bytes) != 0) {
TRACE ("ioctl");
return -1;
}
return bytes;
}
int
serial_sleep (unsigned long timeout)
{
struct timespec ts = {0};
ts.tv_sec = (timeout / 1000);
ts.tv_nsec = (timeout % 1000) * 1000000;
int rc = 0;
do {
rc = nanosleep (&ts, &ts);
} while (rc == -1 && errno == EINTR);
if (rc != 0) {
TRACE ("nanosleep");
return -1;
}
return 0;
}
int
serial_timer ()
{
struct timeval tv = {0};
if (gettimeofday (&tv, NULL) != 0) {
TRACE ("gettimeofday");
return 0;
}
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

486
serial_win32.c Normal file
View File

@ -0,0 +1,486 @@
#include <stddef.h>
#include <windows.h>
#include "serial.h"
#include <stdio.h>
#define TRACE(expr) \
{ \
DWORD error = GetLastError (); \
fprintf (stderr, "TRACE %s:%d: ", __FILE__, __LINE__); \
if (expr) \
fprintf (stderr, "%s: %s\n", expr, serial_errmsg ()); \
else \
fprintf (stderr, "%s\n", serial_errmsg ()); \
SetLastError (error); \
}
struct serial {
/*
* The file descriptor corresponding to the serial port.
*/
HANDLE hFile;
/*
* Serial port settings are saved into this variables immediately
* after the port is opened. These settings are restored when the
* serial port is closed.
*/
DCB dcb;
COMMTIMEOUTS timeouts;
};
//
// Error reporting.
//
int serial_errcode ()
{
return GetLastError ();
}
const char* serial_errmsg ()
{
static char buffer[256] = {0};
unsigned int size = sizeof (buffer) / sizeof (char);
DWORD errcode = GetLastError ();
DWORD rc = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errcode, 0, buffer, size, NULL);
// Remove certain characters ('\r', '\n' and '.')
// at the end of the error message.
while (rc > 0 && (
buffer[rc-1] == '\n' ||
buffer[rc-1] == '\r' ||
buffer[rc-1] == '.')) {
buffer[rc-1] = '\0';
rc--;
}
if (rc) {
return buffer;
} else {
return NULL;
}
}
//
// Open the serial port.
//
int
serial_open (serial** out, const char* name)
{
if (out == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
// Allocate memory.
struct serial *device = malloc (sizeof (struct serial));
if (device == NULL) {
TRACE ("malloc");
return -1; // ERROR_OUTOFMEMORY (Not enough storage is available to complete this operation)
}
// Open the device.
device->hFile = CreateFile (name,
GENERIC_READ | GENERIC_WRITE, 0,
NULL, // No security attributes.
OPEN_EXISTING,
0, // Non-overlapped I/O.
NULL);
if (device->hFile == INVALID_HANDLE_VALUE) {
TRACE ("CreateFile");
free (device);
return -1;
}
// Retrieve the current communication settings and timeouts,
// to be able to restore them when closing the device.
// It is also used to check if the obtained handle
// represents a serial device.
if (!GetCommState (device->hFile, &device->dcb) ||
!GetCommTimeouts (device->hFile, &device->timeouts)) {
TRACE ("GetCommState/GetCommTimeouts");
CloseHandle (device->hFile);
free (device);
return -1;
}
*out = device;
return 0;
}
//
// Close the serial port.
//
int
serial_close (serial* device)
{
if (device == NULL)
return 0;
// Restore the initial communication settings and timeouts.
if (!SetCommState (device->hFile, &device->dcb) ||
!SetCommTimeouts (device->hFile, &device->timeouts)) {
TRACE ("SetCommState/SetCommTimeouts");
CloseHandle (device->hFile);
free (device);
return -1;
}
// Close the device.
if (!CloseHandle (device->hFile)) {
TRACE ("CloseHandle");
free (device);
return -1;
}
// Free memory.
free (device);
return 0;
}
//
// Configure the serial port (baudrate, databits, parity, stopbits and flowcontrol).
//
int
serial_configure (serial *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
// Retrieve the current settings.
DCB dcb = {0};
if (!GetCommState (device->hFile, &dcb)) {
TRACE ("GetCommState");
return -1;
}
dcb.fBinary = TRUE; // Enable Binary Transmission
// Baudrate.
switch (baudrate) {
case 110: dcb.BaudRate = CBR_110; break;
case 300: dcb.BaudRate = CBR_300; break;
case 600: dcb.BaudRate = CBR_600; break;
case 1200: dcb.BaudRate = CBR_1200; break;
case 2400: dcb.BaudRate = CBR_2400; break;
case 4800: dcb.BaudRate = CBR_4800; break;
case 9600: dcb.BaudRate = CBR_9600; break;
case 14400: dcb.BaudRate = CBR_14400; break;
case 19200: dcb.BaudRate = CBR_19200; break;
case 38400: dcb.BaudRate = CBR_38400; break;
case 57600: dcb.BaudRate = CBR_57600; break;
case 115200: dcb.BaudRate = CBR_115200; break;
case 128000: dcb.BaudRate = CBR_128000; break;
case 256000: dcb.BaudRate = CBR_256000; break;
default:
return -1;
}
// Character size.
if (databits >= 5 && databits <= 8)
dcb.ByteSize = databits;
else
return -1;
// Parity checking.
switch (parity) {
case SERIAL_PARITY_NONE: // No parity
dcb.Parity = NOPARITY;
dcb.fParity = FALSE;
break;
case SERIAL_PARITY_EVEN: // Even parity
dcb.Parity = EVENPARITY;
dcb.fParity = TRUE;
break;
case SERIAL_PARITY_ODD: // Odd parity
dcb.Parity = ODDPARITY;
dcb.fParity = TRUE;
break;
default:
return -1;
}
// Stopbits.
switch (stopbits) {
case 1: // One stopbit
dcb.StopBits = ONESTOPBIT;
break;
case 2: // Two stopbits
dcb.StopBits = TWOSTOPBITS;
break;
default:
return -1;
}
// Flow control.
switch (flowcontrol) {
case SERIAL_FLOWCONTROL_NONE: // No flow control.
dcb.fInX = FALSE;
dcb.fOutX = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
break;
case SERIAL_FLOWCONTROL_HARDWARE: // Hardware (RTS/CTS) flow control.
dcb.fInX = FALSE;
dcb.fOutX = FALSE;
dcb.fOutxCtsFlow = TRUE;
dcb.fOutxDsrFlow = TRUE;
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
break;
case SERIAL_FLOWCONTROL_SOFTWARE: // Software (XON/XOFF) flow control.
dcb.fInX = TRUE;
dcb.fOutX = TRUE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
break;
default:
return -1;
}
// Flush the input and output buffers.
if (!PurgeComm (device->hFile, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR)) {
TRACE ("PurgeComm");
return -1;
}
// Apply the new settings.
if (!SetCommState (device->hFile, &dcb)) {
TRACE ("SetCommState");
return -1;
}
return 0;
}
//
// Configure the serial port (timeouts).
//
int
serial_set_timeout (serial* device, long timeout)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
// Retrieve the current timeouts.
COMMTIMEOUTS timeouts = {0};
if (!GetCommTimeouts (device->hFile, &timeouts)) {
TRACE ("GetCommTimeouts");
return -1;
}
// Update the settings.
if (timeout < 0) {
// Blocking mode.
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
} else if (timeout == 0) {
// Non-blocking mode.
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
} else {
// Standard timeout mode.
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = timeout;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
}
// Activate the new timeouts.
if (!SetCommTimeouts (device->hFile, &timeouts)) {
TRACE ("SetCommTimeouts");
return -1;
}
return 0;
}
int
serial_read (serial* device, void* data, unsigned int size)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
DWORD dwRead = 0;
if (!ReadFile (device->hFile, data, size, &dwRead, NULL)) {
TRACE ("ReadFile");
return -1;
}
return dwRead;
}
int
serial_write (serial* device, const void* data, unsigned int size)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
DWORD dwWritten = 0;
if (!WriteFile (device->hFile, data, size, &dwWritten, NULL)) {
TRACE ("WriteFile");
return -1;
}
return dwWritten;
}
int
serial_flush (serial* device, int queue)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
DWORD flags = 0;
switch (queue) {
case SERIAL_QUEUE_INPUT:
flags = PURGE_RXABORT | PURGE_RXCLEAR;
case SERIAL_QUEUE_OUTPUT:
flags = PURGE_TXABORT | PURGE_TXCLEAR;
default:
flags = PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR;
}
if (!PurgeComm (device->hFile, flags)) {
TRACE ("PurgeComm");
return -1;
}
return 0;
}
int
serial_drain (serial* device)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
return 0;
}
int
serial_send_break (serial* device)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
if (!SetCommBreak (device->hFile)) {
TRACE ("SetCommBreak");
return -1;
}
Sleep (250);
if (!ClearCommBreak (device->hFile)) {
TRACE ("ClearCommBreak");
return -1;
}
return 0;
}
int
serial_set_dtr (serial* device, int level)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
int status = (level ? SETDTR : CLRDTR);
if (!EscapeCommFunction (device->hFile, status)) {
TRACE ("EscapeCommFunction");
return -1;
}
return 0;
}
int
serial_set_rts (serial* device, int level)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
int status = (level ? SETRTS : CLRRTS);
if (!EscapeCommFunction (device->hFile, status)) {
TRACE ("EscapeCommFunction");
return -1;
}
return 0;
}
int
serial_get_received (serial* device)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
COMSTAT stats = {0};
if (!ClearCommError (device->hFile, NULL, &stats)) {
TRACE ("ClearCommError");
return -1;
}
return stats.cbInQue;
}
int
serial_get_transmitted (serial* device)
{
if (device == NULL)
return -1; // ERROR_INVALID_PARAMETER (The parameter is incorrect)
COMSTAT stats = {0};
if (!ClearCommError (device->hFile, NULL, &stats)) {
TRACE ("ClearCommError");
return -1;
}
return stats.cbOutQue;
}
int
serial_sleep (unsigned long timeout)
{
Sleep (timeout);
return 0;
}
int
serial_timer ()
{
return GetTickCount ();
}