#include <sys/syslimits.h>
#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
#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__)
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;
*list_ptr = list;
RETURN_OK();
case SP_ERR_SUPP:
- DEBUG_ERROR(SP_ERR_SUPP, "Enumeration not supported on this platform.");
+ DEBUG_ERROR(SP_ERR_SUPP, "Enumeration not supported on this platform");
default:
if (list)
sp_free_port_list(list);
DEBUG("Opening port %s", port->name);
#ifdef _WIN32
- DWORD desired_access = 0, flags_and_attributes = 0;
+ DWORD desired_access = 0, flags_and_attributes = 0, errors;
char *escaped_port_name;
+ COMSTAT status;
/* Prefix port name with '\\.\' to work with ports above COM9. */
if (!(escaped_port_name = malloc(strlen(port->name + 5))))
data.term.c_cflag |= (CLOCAL | CREAD | HUPCL);
#endif
+#ifdef _WIN32
+ if (ClearCommError(port->hdl, &errors, &status) == 0)
+ RETURN_FAIL("ClearCommError() failed");
+#endif
+
ret = set_config(port, &data, &config);
if (ret < 0) {
/* 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)
timersub(&end, &now, &delta);
}
result = select(port->fd + 1, NULL, &fds, NULL, timeout ? &delta : NULL);
- if (result < 0)
- RETURN_FAIL("select() failed");
- if (result == 0) {
+ 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);
}
/* 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);
+ 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 {
/* Actual failure of some kind. */
RETURN_FAIL("WriteFile() failed");
}
} else {
- DEBUG("Single byte written immediately.");
+ DEBUG("Single byte written immediately");
written++;
}
}
- DEBUG("All bytes written immediately.");
+ DEBUG("All bytes written immediately");
RETURN_VALUE("%d", written);
#else
timersub(&end, &now, &delta);
}
result = select(port->fd + 1, &fds, NULL, NULL, timeout ? &delta : NULL);
- if (result < 0)
- RETURN_FAIL("select() failed");
- if (result == 0) {
+ 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);
}
RETURN_FAIL("ReadFile() failed");
/* Get number of bytes read. */
- GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE);
+ if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0)
+ RETURN_FAIL("GetOverlappedResult() failed");
RETURN_VALUE("%d", bytes_read);
#else
COMSTAT comstat;
if (ClearCommError(port->hdl, &errors, &comstat) == 0)
- RETURN_FAIL("ClearComError() failed");
+ RETURN_FAIL("ClearCommError() failed");
RETURN_VALUE("%d", comstat.cbInQue);
#else
int bytes_waiting;
COMSTAT comstat;
if (ClearCommError(port->hdl, &errors, &comstat) == 0)
- RETURN_FAIL("ClearComError() failed");
+ RETURN_FAIL("ClearCommError() failed");
RETURN_VALUE("%d", comstat.cbOutQue);
#else
int bytes_waiting;
if (config->parity >= 0) {
switch (config->parity) {
- /* Note: There's also SPACEPARITY, MARKPARITY (unneeded so far). */
case SP_PARITY_NONE:
data->dcb.Parity = NOPARITY;
break;