struct sp_port {
char *name;
+ int nonblocking;
#ifdef _WIN32
HANDLE hdl;
+ OVERLAPPED write_ovl;
+ BYTE pending_byte;
+ BOOL writing;
#else
int fd;
#endif
#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 SET_ERROR(val, err, msg) do { DEBUG_ERROR(err, msg); val = err; } while (0)
-#define SET_FAIL(val, msg) do { DEBUG_FAIL(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__)
#define TRY(x) do { int ret = x; if (ret != SP_OK) RETURN_CODEVAL(ret); } while (0)
enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
{
+ struct port_data data;
+ struct sp_port_config config;
+ enum sp_return ret;
+
TRACE("%p, %x", port, flags);
CHECK_PORT();
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. */
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->writing = FALSE;
+ }
+
+ if (SetCommTimeouts(port->hdl, &timeouts) == 0) {
+ sp_close(port);
+ RETURN_FAIL("SetCommTimeouts() failed");
+ }
#else
int flags_local = 0;
- struct port_data data;
- struct sp_port_config config;
- int ret;
/* Map 'flags' to the OS-specific settings. */
if (flags & (SP_MODE_READ | SP_MODE_WRITE))
if ((port->fd = open(port->name, flags_local)) < 0)
RETURN_FAIL("open() failed");
+#endif
ret = get_config(port, &data, &config);
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);
sp_close(port);
RETURN_CODEVAL(ret);
}
-#endif
RETURN_OK();
}
if (CloseHandle(port->hdl) == 0)
RETURN_FAIL("CloseHandle() failed");
port->hdl = INVALID_HANDLE_VALUE;
+ if (port->nonblocking) {
+ /* Close event handle created for overlapped writes. */
+ if (CloseHandle(port->write_ovl.hEvent) == 0)
+ RETURN_FAIL("CloseHandle() failed");
+ }
#else
/* Returns 0 upon success, -1 upon failure. */
if (close(port->fd) == -1)
if (buffers > SP_BUF_BOTH)
RETURN_ERROR(SP_ERR_ARG, "Invalid buffer selection");
- const char *buffer_names[] = {"input", "output", "both"};
+ const char *buffer_names[] = {"no", "input", "output", "both"};
DEBUG("Flushing %s buffers on port %s", buffer_names[buffers], port->name);
DEBUG("Writing up to %d bytes to port %s", count, port->name);
+ if (count == 0)
+ RETURN_VALUE("0", 0);
+
#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;
+ } else {
+ DEBUG("Previous write not complete");
+ /* Can't take a new write until the previous one finishes. */
+ RETURN_VALUE("0", 0);
+ }
+ }
+
+ /* 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, &port->pending_byte, 1, NULL, &port->write_ovl) == 0) {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ DEBUG("Asynchronous write started");
+ port->writing = 1;
+ RETURN_VALUE("%d", ++written);
+ } else {
+ /* Actual failure of some kind. */
+ RETURN_FAIL("WriteFile() failed");
+ }
+ } else {
+ DEBUG("Single byte written immediately.");
+ written++;
+ }
+ }
+
+ DEBUG("All bytes written immediately.");
+
+ } else {
+ /* Blocking write. */
+ if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) {
+ RETURN_FAIL("WriteFile() failed");
+ }
+ }
- /* Returns non-zero upon success, 0 upon failure. */
- if (WriteFile(port->hdl, buf, count, &written, NULL) == 0)
- RETURN_FAIL("WriteFile() failed");
RETURN_VALUE("%d", written);
#else
/* Returns the number of bytes written, or -1 upon failure. */
ssize_t bytes_read;
/* Returns the number of bytes read, or -1 upon failure. */
- if ((bytes_read = read(port->fd, buf, count)) < 0)
- RETURN_FAIL("read() failed");
+ 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. */
+ bytes_read = 0;
+ else
+ /* This is an actual failure. */
+ RETURN_FAIL("read() failed");
+ }
RETURN_VALUE("%d", bytes_read);
#endif
}
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;
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;
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");
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;
case SP_PARITY_ODD:
data->term.c_cflag |= PARENB | PARODD;
break;
+ case SP_PARITY_MARK:
+ data->term.c_cflag |= PARENB | PARODD;
+#ifdef CMSPAR
+ data->term.c_cflag |= CMSPAR;
+#endif
+ break;
+ case SP_PARITY_SPACE:
+ data->term.c_cflag |= PARENB;
+#ifdef CMSPAR
+ data->term.c_cflag |= CMSPAR;
+#endif
+ break;
default:
RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting");
}
}
}
- if (tcsetattr(port->fd, TCSADRAIN, &data->term) < 0)
+ if (tcsetattr(port->fd, TCSANOW, &data->term) < 0)
RETURN_FAIL("tcsetattr() failed");
#ifdef __APPLE__
enum sp_return sp_new_config(struct sp_port_config **config_ptr)
{
TRACE("%p", config_ptr);
+ struct sp_port_config *config;
if (!config_ptr)
RETURN_ERROR(SP_ERR_ARG, "Null result pointer");
- if (!(*config_ptr = malloc(sizeof(struct sp_port_config))))
+ *config_ptr = NULL;
+
+ if (!(config = malloc(sizeof(struct sp_port_config))))
RETURN_ERROR(SP_ERR_MEM, "config malloc failed");
+ config->baudrate = -1;
+ config->bits = -1;
+ config->parity = -1;
+ config->stopbits = -1;
+ config->rts = -1;
+ config->cts = -1;
+ config->dtr = -1;
+ config->dsr = -1;
+
+ *config_ptr = config;
+
RETURN_OK();
}
va_list args;
va_start(args, format);
if (getenv("LIBSERIALPORT_DEBUG")) {
- fputs("libserialport: ", stderr);
+ fputs("sp: ", stderr);
vfprintf(stderr, format, args);
}
va_end(args);