X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=712b951daad2b765e3b661a6b506bbedd0aec54a;hb=64d996216e5cea58192d7842e4b364e19fd3d698;hp=a57f1773b856d6d569b337cce8722f604db2c437;hpb=a3cb91f5e1fc79ac089dc18299f5fe21026cafb0;p=libserialport.git diff --git a/serialport.c b/serialport.c index a57f177..712b951 100644 --- a/serialport.c +++ b/serialport.c @@ -34,8 +34,11 @@ #include #include #else +#include #include #include +#include +#include #endif #ifdef __APPLE__ #include @@ -44,14 +47,28 @@ #include #endif #ifdef __linux__ +#ifdef HAVE_LIBUDEV #include "libudev.h" +#endif +#ifndef __ANDROID__ #include "linux/serial.h" +#endif #include "linux_termios.h" + +/* TCGETX/TCSETX is not available everywhere. */ #if defined(TCGETX) && defined(TCSETX) && defined(HAVE_TERMIOX) #define USE_TERMIOX #endif #endif +/* TIOCINQ/TIOCOUTQ is not available everywhere. */ +#if !defined(TIOCINQ) && defined(FIONREAD) +#define TIOCINQ FIONREAD +#endif +#if !defined(TIOCOUTQ) && defined(FIONWRITE) +#define TIOCOUTQ FIONWRITE +#endif + #ifndef _WIN32 #include "linux_termios.h" #endif @@ -60,11 +77,12 @@ struct sp_port { char *name; - int nonblocking; #ifdef _WIN32 HANDLE hdl; + COMMTIMEOUTS timeouts; OVERLAPPED write_ovl; - BYTE *write_buf; + OVERLAPPED read_ovl; + BYTE pending_byte; BOOL writing; #else int fd; @@ -155,7 +173,11 @@ void (*sp_debug_handler)(const char *format, ...) = sp_default_debug_handler; #define RETURN_OK() RETURN_CODE(SP_OK); #define RETURN_ERROR(err, msg) do { DEBUG_ERROR(err, msg); return err; } while (0) #define RETURN_FAIL(msg) do { DEBUG_FAIL(msg); return SP_ERR_FAIL; } while (0) -#define RETURN_VALUE(fmt, x) do { DEBUG("%s returning " fmt, __func__, x); return x; } while (0) +#define RETURN_VALUE(fmt, x) do { \ + typeof(x) _x = x; \ + DEBUG("%s returning " fmt, __func__, _x); \ + return _x; \ +} while (0) #define SET_ERROR(val, err, msg) do { DEBUG_ERROR(err, msg); val = err; } while (0) #define SET_FAIL(val, msg) do { DEBUG_FAIL(msg); val = SP_ERR_FAIL; } while (0) #define TRACE(fmt, ...) DEBUG("%s(" fmt ") called", __func__, ##__VA_ARGS__) @@ -221,7 +243,7 @@ char *sp_get_port_name(const struct sp_port *port) enum sp_return sp_get_port_handle(const struct sp_port *port, void *result_ptr) { - TRACE("%p", port); + TRACE("%p, %p", port, result_ptr); if (!port) RETURN_ERROR(SP_ERR_ARG, "Null port"); @@ -261,8 +283,7 @@ void sp_free_port(struct sp_port *port) { TRACE("%p", port); - if (!port) - { + if (!port) { DEBUG("Null port"); RETURN(); } @@ -447,7 +468,7 @@ out_release: IOObjectRelease(iter); out_done: #endif -#ifdef __linux__ +#if defined(__linux__) && defined(HAVE_LIBUDEV) struct udev *ud; struct udev_enumerate *ud_enumerate; struct udev_list_entry *ud_list; @@ -573,20 +594,21 @@ void sp_free_port_list(struct sp_port **list) enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) { - TRACE("%p, %x", port, flags); + struct port_data data; + struct sp_port_config config; + enum sp_return ret; + + TRACE("%p, 0x%x", port, flags); CHECK_PORT(); - if (flags > (SP_MODE_READ | SP_MODE_WRITE | SP_MODE_NONBLOCK)) + if (flags > (SP_MODE_READ | SP_MODE_WRITE)) RETURN_ERROR(SP_ERR_ARG, "Invalid flags"); DEBUG("Opening port %s", port->name); - port->nonblocking = (flags & SP_MODE_NONBLOCK) ? 1 : 0; - #ifdef _WIN32 DWORD desired_access = 0, flags_and_attributes = 0; - COMMTIMEOUTS timeouts; char *escaped_port_name; /* Prefix port name with '\\.\' to work with ports above COM9. */ @@ -595,13 +617,11 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) sprintf(escaped_port_name, "\\\\.\\%s", port->name); /* Map 'flags' to the OS-specific settings. */ - flags_and_attributes = FILE_ATTRIBUTE_NORMAL; + flags_and_attributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED; if (flags & SP_MODE_READ) desired_access |= GENERIC_READ; if (flags & SP_MODE_WRITE) desired_access |= GENERIC_WRITE; - if (flags & SP_MODE_NONBLOCK) - flags_and_attributes |= FILE_FLAG_OVERLAPPED; port->hdl = CreateFile(escaped_port_name, desired_access, 0, 0, OPEN_EXISTING, flags_and_attributes, 0); @@ -609,37 +629,38 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) free(escaped_port_name); if (port->hdl == INVALID_HANDLE_VALUE) - RETURN_FAIL("CreateFile() failed"); - - /* All timeouts disabled. */ - timeouts.ReadIntervalTimeout = 0; - timeouts.ReadTotalTimeoutMultiplier = 0; - timeouts.ReadTotalTimeoutConstant = 0; - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - - if (port->nonblocking) { - /* Set read timeout such that all reads return immediately. */ - timeouts.ReadIntervalTimeout = MAXDWORD; - /* Prepare OVERLAPPED structure for non-blocking writes. */ - memset(&port->write_ovl, 0, sizeof(port->write_ovl)); - if (!(port->write_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { - sp_close(port); - RETURN_FAIL("CreateEvent() failed"); - } - port->write_buf = NULL; - port->writing = FALSE; - } + RETURN_FAIL("port CreateFile() failed"); - if (SetCommTimeouts(port->hdl, &timeouts) == 0) { + /* All timeouts initially disabled. */ + port->timeouts.ReadIntervalTimeout = 0; + port->timeouts.ReadTotalTimeoutMultiplier = 0; + port->timeouts.ReadTotalTimeoutConstant = 0; + port->timeouts.WriteTotalTimeoutMultiplier = 0; + port->timeouts.WriteTotalTimeoutConstant = 0; + + if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) { sp_close(port); RETURN_FAIL("SetCommTimeouts() failed"); } + + /* Prepare OVERLAPPED structures. */ + memset(&port->read_ovl, 0, sizeof(port->read_ovl)); + memset(&port->write_ovl, 0, sizeof(port->write_ovl)); + port->read_ovl.hEvent = INVALID_HANDLE_VALUE; + port->write_ovl.hEvent = INVALID_HANDLE_VALUE; + if ((port->read_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == INVALID_HANDLE_VALUE) { + sp_close(port); + RETURN_FAIL("read event CreateEvent() failed"); + } + if ((port->write_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == INVALID_HANDLE_VALUE) { + sp_close(port); + RETURN_FAIL("write event CreateEvent() failed"); + } + + port->writing = FALSE; + #else - int flags_local = 0; - struct port_data data; - struct sp_port_config config; - int ret; + int flags_local = O_NONBLOCK | O_NOCTTY; /* Map 'flags' to the OS-specific settings. */ if (flags & (SP_MODE_READ | SP_MODE_WRITE)) @@ -648,11 +669,10 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) flags_local |= O_RDONLY; else if (flags & SP_MODE_WRITE) flags_local |= O_WRONLY; - if (flags & SP_MODE_NONBLOCK) - flags_local |= O_NONBLOCK; if ((port->fd = open(port->name, flags_local)) < 0) RETURN_FAIL("open() failed"); +#endif ret = get_config(port, &data, &config); @@ -661,17 +681,51 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) RETURN_CODEVAL(ret); } - /* Turn off all serial port cooking. */ - data.term.c_iflag &= ~(ISTRIP | INLCR | ICRNL); - data.term.c_oflag &= ~(ONLCR | OCRNL | ONOCR); -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) + /* Set sane port settings. */ +#ifdef _WIN32 + data.dcb.fBinary = TRUE; + data.dcb.fDsrSensitivity = FALSE; + data.dcb.fErrorChar = FALSE; + data.dcb.fNull = FALSE; + data.dcb.fAbortOnError = TRUE; +#else + /* Turn off all fancy termios tricks, give us a raw channel. */ + data.term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IMAXBEL); +#ifdef IUCLC + data.term.c_iflag &= ~IUCLC; +#endif + data.term.c_oflag &= ~(OPOST | ONLCR | OCRNL | ONOCR | ONLRET); +#ifdef OLCUC + data.term.c_oflag &= ~OLCUC; +#endif +#ifdef NLDLY + data.term.c_oflag &= ~NLDLY; +#endif +#ifdef CRDLY + data.term.c_oflag &= ~CRDLY; +#endif +#ifdef TABDLY + data.term.c_oflag &= ~TABDLY; +#endif +#ifdef BSDLY + data.term.c_oflag &= ~BSDLY; +#endif +#ifdef VTDLY + data.term.c_oflag &= ~VTDLY; +#endif +#ifdef FFDLY + data.term.c_oflag &= ~FFDLY; +#endif +#ifdef OFILL data.term.c_oflag &= ~OFILL; #endif - /* Disable canonical mode, and don't echo input characters. */ - data.term.c_lflag &= ~(ICANON | ECHO); + data.term.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN); + data.term.c_cc[VMIN] = 0; + data.term.c_cc[VTIME] = 0; - /* Ignore modem status lines; enable receiver */ - data.term.c_cflag |= (CLOCAL | CREAD); + /* Ignore modem status lines; enable receiver; leave control lines alone on close. */ + data.term.c_cflag |= (CLOCAL | CREAD | HUPCL); +#endif ret = set_config(port, &data, &config); @@ -679,7 +733,6 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) sp_close(port); RETURN_CODEVAL(ret); } -#endif RETURN_OK(); } @@ -695,16 +748,14 @@ enum sp_return sp_close(struct sp_port *port) #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ if (CloseHandle(port->hdl) == 0) - RETURN_FAIL("CloseHandle() failed"); + RETURN_FAIL("port CloseHandle() failed"); port->hdl = INVALID_HANDLE_VALUE; - if (port->nonblocking) { - if (port->writing) - /* Write should have been stopped by closing the port, so safe to free buffer. */ - free(port->write_buf); - /* Close event handle created for overlapped writes. */ - if (CloseHandle(port->write_ovl.hEvent) == 0) - RETURN_FAIL("CloseHandle() failed"); - } + /* Close event handle created for overlapped reads. */ + if (port->read_ovl.hEvent != INVALID_HANDLE_VALUE && CloseHandle(port->read_ovl.hEvent) == 0) + RETURN_FAIL("read event CloseHandle() failed"); + /* Close event handle created for overlapped writes. */ + if (port->write_ovl.hEvent != INVALID_HANDLE_VALUE && CloseHandle(port->write_ovl.hEvent) == 0) + RETURN_FAIL("write event CloseHandle() failed"); #else /* Returns 0 upon success, -1 upon failure. */ if (close(port->fd) == -1) @@ -717,7 +768,7 @@ enum sp_return sp_close(struct sp_port *port) enum sp_return sp_flush(struct sp_port *port, enum sp_buffer buffers) { - TRACE("%p, %x", port, buffers); + TRACE("%p, 0x%x", port, buffers); CHECK_OPEN_PORT(); @@ -766,16 +817,145 @@ enum sp_return sp_drain(struct sp_port *port) /* Returns non-zero upon success, 0 upon failure. */ if (FlushFileBuffers(port->hdl) == 0) RETURN_FAIL("FlushFileBuffers() failed"); + RETURN_OK(); #else - /* Returns 0 upon success, -1 upon failure. */ - if (tcdrain(port->fd) < 0) - RETURN_FAIL("tcdrain() failed"); + int result; + while (1) { +#ifdef __ANDROID__ + int arg = 1; + result = ioctl(port->fd, TCSBRK, &arg); +#else + result = tcdrain(port->fd); +#endif + if (result < 0) { + if (errno == EINTR) { + DEBUG("tcdrain() was interrupted"); + continue; + } else { + RETURN_FAIL("tcdrain() failed"); + } + } else { + RETURN_OK(); + } + } #endif +} - RETURN_OK(); +enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t count, unsigned int timeout) +{ + TRACE("%p, %p, %d, %d", port, buf, count, timeout); + + CHECK_OPEN_PORT(); + + if (!buf) + RETURN_ERROR(SP_ERR_ARG, "Null buffer"); + + if (timeout) + DEBUG("Writing %d bytes to port %s, timeout %d ms", count, port->name, timeout); + else + DEBUG("Writing %d bytes to port %s, no timeout", count, port->name); + + if (count == 0) + RETURN_VALUE("0", 0); + +#ifdef _WIN32 + DWORD bytes_written = 0; + BOOL result; + + /* Wait for previous non-blocking write to complete, if any. */ + if (port->writing) { + DEBUG("Waiting for previous write to complete"); + result = GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, TRUE); + port->writing = 0; + if (!result) + RETURN_FAIL("Previous write failed to complete"); + DEBUG("Previous write completed"); + } + + /* Set timeout. */ + port->timeouts.WriteTotalTimeoutConstant = timeout; + if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) + RETURN_FAIL("SetCommTimeouts() failed"); + + /* Start write. */ + if (WriteFile(port->hdl, buf, count, NULL, &port->write_ovl) == 0) { + if (GetLastError() == ERROR_IO_PENDING) { + DEBUG("Waiting for write to complete"); + GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, TRUE); + DEBUG("Write completed, %d/%d bytes written", bytes_written, count); + RETURN_VALUE("%d", bytes_written); + } else { + RETURN_FAIL("WriteFile() failed"); + } + } else { + DEBUG("Write completed immediately"); + RETURN_VALUE("%d", count); + } +#else + size_t bytes_written = 0; + unsigned char *ptr = (unsigned char *) buf; + struct timeval start, delta, now, end = {0, 0}; + fd_set fds; + int result; + + if (timeout) { + /* Get time at start of operation. */ + gettimeofday(&start, NULL); + /* Define duration of timeout. */ + delta.tv_sec = timeout / 1000; + delta.tv_usec = (timeout % 1000) * 1000; + /* Calculate time at which we should give up. */ + timeradd(&start, &delta, &end); + } + + /* Loop until we have written the requested number of bytes. */ + while (bytes_written < count) + { + /* Wait until space is available. */ + FD_ZERO(&fds); + FD_SET(port->fd, &fds); + if (timeout) { + gettimeofday(&now, NULL); + if (timercmp(&now, &end, >)) { + DEBUG("write timed out"); + RETURN_VALUE("%d", bytes_written); + } + timersub(&end, &now, &delta); + } + result = select(port->fd + 1, NULL, &fds, NULL, timeout ? &delta : NULL); + if (result < 0) { + if (errno == EINTR) { + DEBUG("select() call was interrupted, repeating"); + continue; + } else { + RETURN_FAIL("select() failed"); + } + } else if (result == 0) { + DEBUG("write timed out"); + RETURN_VALUE("%d", bytes_written); + } + + /* Do write. */ + result = write(port->fd, ptr, count - bytes_written); + + if (result < 0) { + if (errno == EAGAIN) + /* This shouldn't happen because we did a select() first, but handle anyway. */ + continue; + else + /* This is an actual failure. */ + RETURN_FAIL("write() failed"); + } + + bytes_written += result; + ptr += result; + } + + RETURN_VALUE("%d", bytes_written); +#endif } -enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count) +enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_t count) { TRACE("%p, %p, %d", port, buf, count); @@ -791,54 +971,57 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count) #ifdef _WIN32 DWORD written = 0; + BYTE *ptr = (BYTE *) buf; - if (port->nonblocking) { - /* Non-blocking write. */ - - /* Check whether previous write is complete. */ - if (port->writing) { - if (HasOverlappedIoCompleted(&port->write_ovl)) { - DEBUG("Previous write completed"); - port->writing = 0; - free(port->write_buf); - port->write_buf = NULL; - } else { - DEBUG("Previous write not complete"); - /* Can't take a new write until the previous one finishes. */ - RETURN_VALUE("0", 0); - } + /* Check whether previous write is complete. */ + if (port->writing) { + if (HasOverlappedIoCompleted(&port->write_ovl)) { + DEBUG("Previous write completed"); + port->writing = 0; + } else { + DEBUG("Previous write not complete"); + /* Can't take a new write until the previous one finishes. */ + RETURN_VALUE("0", 0); } + } + + /* Set timeout. */ + port->timeouts.WriteTotalTimeoutConstant = 0; + if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) + RETURN_FAIL("SetCommTimeouts() failed"); - /* Copy user buffer. */ - if (!(port->write_buf = malloc(count))) - RETURN_ERROR(SP_ERR_MEM, "buffer copy malloc failed"); - memcpy(port->write_buf, buf, count); + /* Keep writing data until the OS has to actually start an async IO for it. + * At that point we know the buffer is full. */ + while (written < count) + { + /* Copy first byte of user buffer. */ + port->pending_byte = *ptr++; /* Start asynchronous write. */ - if (WriteFile(port->hdl, buf, count, NULL, &port->write_ovl) == 0) { + if (WriteFile(port->hdl, &port->pending_byte, 1, NULL, &port->write_ovl) == 0) { if (GetLastError() == ERROR_IO_PENDING) { - DEBUG("Asynchronous write started"); - port->writing = 1; - RETURN_VALUE("%d", count); + if (HasOverlappedIoCompleted(&port->write_ovl)) { + DEBUG("Asynchronous write completed immediately"); + port->writing = 0; + written++; + continue; + } else { + DEBUG("Asynchronous write running"); + port->writing = 1; + RETURN_VALUE("%d", ++written); + } } else { - free(port->write_buf); - port->write_buf = NULL; /* Actual failure of some kind. */ RETURN_FAIL("WriteFile() failed"); } } else { - DEBUG("Write completed immediately"); - free(port->write_buf); - port->write_buf = NULL; - RETURN_VALUE("%d", count); - } - } else { - /* Blocking write. */ - if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) { - RETURN_FAIL("WriteFile() failed"); + DEBUG("Single byte written immediately."); + written++; } } + DEBUG("All bytes written immediately."); + RETURN_VALUE("%d", written); #else /* Returns the number of bytes written, or -1 upon failure. */ @@ -851,7 +1034,110 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count) #endif } -enum sp_return sp_read(struct sp_port *port, void *buf, size_t count) +enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, unsigned int timeout) +{ + TRACE("%p, %p, %d, %d", port, buf, count, timeout); + + CHECK_OPEN_PORT(); + + if (!buf) + RETURN_ERROR(SP_ERR_ARG, "Null buffer"); + + if (timeout) + DEBUG("Reading %d bytes from port %s, timeout %d ms", count, port->name, timeout); + else + DEBUG("Reading %d bytes from port %s, no timeout", count, port->name); + + if (count == 0) + RETURN_VALUE("0", 0); + +#ifdef _WIN32 + DWORD bytes_read = 0; + + /* Set timeout. */ + port->timeouts.ReadIntervalTimeout = 0; + port->timeouts.ReadTotalTimeoutConstant = timeout; + if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) + RETURN_FAIL("SetCommTimeouts() failed"); + + /* Start read. */ + if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl) == 0) { + if (GetLastError() == ERROR_IO_PENDING) { + DEBUG("Waiting for read to complete"); + GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE); + DEBUG("Read completed, %d/%d bytes read", bytes_read, count); + RETURN_VALUE("%d", bytes_read); + } else { + RETURN_FAIL("ReadFile() failed"); + } + } else { + DEBUG("Read completed immediately"); + RETURN_VALUE("%d", count); + } +#else + size_t bytes_read = 0; + unsigned char *ptr = (unsigned char *) buf; + struct timeval start, delta, now, end = {0, 0}; + fd_set fds; + int result; + + if (timeout) { + /* Get time at start of operation. */ + gettimeofday(&start, NULL); + /* Define duration of timeout. */ + delta.tv_sec = timeout / 1000; + delta.tv_usec = (timeout % 1000) * 1000; + /* Calculate time at which we should give up. */ + timeradd(&start, &delta, &end); + } + + /* Loop until we have the requested number of bytes. */ + while (bytes_read < count) + { + /* Wait until data is available. */ + FD_ZERO(&fds); + FD_SET(port->fd, &fds); + if (timeout) { + gettimeofday(&now, NULL); + if (timercmp(&now, &end, >)) + /* Timeout has expired. */ + RETURN_VALUE("%d", bytes_read); + timersub(&end, &now, &delta); + } + result = select(port->fd + 1, &fds, NULL, NULL, timeout ? &delta : NULL); + if (result < 0) { + if (errno == EINTR) { + DEBUG("select() call was interrupted, repeating"); + continue; + } else { + RETURN_FAIL("select() failed"); + } + } else if (result == 0) { + DEBUG("read timed out"); + RETURN_VALUE("%d", bytes_read); + } + + /* Do read. */ + result = read(port->fd, ptr, count - bytes_read); + + if (result < 0) { + if (errno == EAGAIN) + /* This shouldn't happen because we did a select() first, but handle anyway. */ + continue; + else + /* This is an actual failure. */ + RETURN_FAIL("read() failed"); + } + + bytes_read += result; + ptr += result; + } + + RETURN_VALUE("%d", bytes_read); +#endif +} + +enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count) { TRACE("%p, %p, %d", port, buf, count); @@ -863,19 +1149,30 @@ enum sp_return sp_read(struct sp_port *port, void *buf, size_t count) DEBUG("Reading up to %d bytes from port %s", count, port->name); #ifdef _WIN32 - DWORD bytes_read = 0; + DWORD bytes_read; - /* Returns non-zero upon success, 0 upon failure. */ - if (ReadFile(port->hdl, buf, count, &bytes_read, NULL) == 0) + /* Set timeout. */ + port->timeouts.ReadIntervalTimeout = MAXDWORD; + port->timeouts.ReadTotalTimeoutConstant = 0; + if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) + RETURN_FAIL("SetCommTimeouts() failed"); + + /* Do read. */ + if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl) == 0) RETURN_FAIL("ReadFile() failed"); + + /* Get number of bytes read. */ + if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0) + RETURN_FAIL("GetOverlappedResult() failed"); + RETURN_VALUE("%d", bytes_read); #else ssize_t bytes_read; /* Returns the number of bytes read, or -1 upon failure. */ if ((bytes_read = read(port->fd, buf, count)) < 0) { - if (port->nonblocking && errno == EAGAIN) - /* Port is opened in nonblocking mode and there are no bytes available. */ + if (errno == EAGAIN) + /* No bytes available. */ bytes_read = 0; else /* This is an actual failure. */ @@ -885,6 +1182,52 @@ enum sp_return sp_read(struct sp_port *port, void *buf, size_t count) #endif } +enum sp_return sp_input_waiting(struct sp_port *port) +{ + TRACE("%p", port); + + CHECK_OPEN_PORT(); + + DEBUG("Checking input bytes waiting on port %s", port->name); + +#ifdef _WIN32 + DWORD errors; + COMSTAT comstat; + + if (ClearCommError(port->hdl, &errors, &comstat) == 0) + RETURN_FAIL("ClearComError() failed"); + RETURN_VALUE("%d", comstat.cbInQue); +#else + int bytes_waiting; + if (ioctl(port->fd, TIOCINQ, &bytes_waiting) < 0) + RETURN_FAIL("TIOCINQ ioctl failed"); + RETURN_VALUE("%d", bytes_waiting); +#endif +} + +enum sp_return sp_output_waiting(struct sp_port *port) +{ + TRACE("%p", port); + + CHECK_OPEN_PORT(); + + DEBUG("Checking output bytes waiting on port %s", port->name); + +#ifdef _WIN32 + DWORD errors; + COMSTAT comstat; + + if (ClearCommError(port->hdl, &errors, &comstat) == 0) + RETURN_FAIL("ClearComError() failed"); + RETURN_VALUE("%d", comstat.cbOutQue); +#else + int bytes_waiting; + if (ioctl(port->fd, TIOCOUTQ, &bytes_waiting) < 0) + RETURN_FAIL("TIOCOUTQ ioctl failed"); + RETURN_VALUE("%d", bytes_waiting); +#endif +} + #ifdef __linux__ static enum sp_return get_baudrate(int fd, int *baudrate) { @@ -1026,11 +1369,17 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, case NOPARITY: config->parity = SP_PARITY_NONE; break; + case ODDPARITY: + config->parity = SP_PARITY_ODD; + break; case EVENPARITY: config->parity = SP_PARITY_EVEN; break; - case ODDPARITY: - config->parity = SP_PARITY_ODD; + case MARKPARITY: + config->parity = SP_PARITY_MARK; + break; + case SPACEPARITY: + config->parity = SP_PARITY_SPACE; break; default: config->parity = -1; @@ -1152,6 +1501,10 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, config->parity = SP_PARITY_NONE; else if (!(data->term.c_cflag & PARENB) || (data->term.c_iflag & IGNPAR)) config->parity = -1; +#ifdef CMSPAR + else if (data->term.c_cflag & CMSPAR) + config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_MARK : SP_PARITY_SPACE; +#endif else config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_ODD : SP_PARITY_EVEN; @@ -1233,11 +1586,17 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, case SP_PARITY_NONE: data->dcb.Parity = NOPARITY; break; + case SP_PARITY_ODD: + data->dcb.Parity = ODDPARITY; + break; case SP_PARITY_EVEN: data->dcb.Parity = EVENPARITY; break; - case SP_PARITY_ODD: - data->dcb.Parity = ODDPARITY; + case SP_PARITY_MARK: + data->dcb.Parity = MARKPARITY; + break; + case SP_PARITY_SPACE: + data->dcb.Parity = SPACEPARITY; break; default: RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting"); @@ -1396,6 +1755,9 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, if (config->parity >= 0) { data->term.c_iflag &= ~IGNPAR; data->term.c_cflag &= ~(PARENB | PARODD); +#ifdef CMSPAR + data->term.c_cflag &= ~CMSPAR; +#endif switch (config->parity) { case SP_PARITY_NONE: data->term.c_iflag |= IGNPAR; @@ -1406,6 +1768,20 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, case SP_PARITY_ODD: data->term.c_cflag |= PARENB | PARODD; break; +#ifdef CMSPAR + case SP_PARITY_MARK: + data->term.c_cflag |= PARENB | PARODD; + data->term.c_cflag |= CMSPAR; + break; + case SP_PARITY_SPACE: + data->term.c_cflag |= PARENB; + data->term.c_cflag |= CMSPAR; + break; +#else + case SP_PARITY_MARK: + case SP_PARITY_SPACE: + RETURN_ERROR(SP_ERR_SUPP, "Mark/space parity not supported"); +#endif default: RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting"); } @@ -1531,7 +1907,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, } } - if (tcsetattr(port->fd, TCSADRAIN, &data->term) < 0) + if (tcsetattr(port->fd, TCSANOW, &data->term) < 0) RETURN_FAIL("tcsetattr() failed"); #ifdef __APPLE__ @@ -1559,9 +1935,10 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, enum sp_return sp_new_config(struct sp_port_config **config_ptr) { - TRACE("%p", config_ptr); struct sp_port_config *config; + TRACE("%p", config_ptr); + if (!config_ptr) RETURN_ERROR(SP_ERR_ARG, "Null result pointer"); @@ -1642,7 +2019,7 @@ enum sp_return sp_set_##x(struct sp_port *port, type x) { \ RETURN_OK(); \ } \ enum sp_return sp_get_config_##x(const struct sp_port_config *config, type *x) { \ - TRACE("%p", config); \ + TRACE("%p, %p", config, x); \ if (!config) \ RETURN_ERROR(SP_ERR_ARG, "Null config"); \ *x = config->x; \ @@ -1851,7 +2228,7 @@ void sp_default_debug_handler(const char *format, ...) va_list args; va_start(args, format); if (getenv("LIBSERIALPORT_DEBUG")) { - fputs("libserialport: ", stderr); + fputs("sp: ", stderr); vfprintf(stderr, format, args); } va_end(args);