*/
enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, unsigned int timeout_ms);
+/**
+ * Read bytes from the specified serial port, returning as soon as any data is
+ * available.
+ *
+ * @warning If your program runs on Unix, defines its own signal handlers, and
+ * needs to abort blocking reads when these are called, then you
+ * should not use this function. It repeats system calls that return
+ * with EINTR. To be able to abort a read from a signal handler, you
+ * should implement your own blocking read using sp_nonblocking_read()
+ * together with a blocking method that makes sense for your program.
+ * E.g. you can obtain the file descriptor for an open port using
+ * sp_get_port_handle() and use this to call select() or pselect(),
+ * with appropriate arrangements to return if a signal is received.
+ *
+ * @param[in] port Pointer to a port structure. Must not be NULL.
+ * @param[out] buf Buffer in which to store the bytes read. Must not be NULL.
+ * @param[in] count Maximum number of bytes to read. Must not be zero.
+ * @param[in] timeout_ms Timeout in milliseconds, or zero to wait indefinitely.
+ *
+ * @return The number of bytes read on success, or a negative error code. If
+ * the result is zero, the timeout was reached before any bytes were
+ * available. If timeout_ms is zero, the function will always return
+ * either at least one byte, or a negative error code.
+ *
+ * @since 0.1.1
+ */
+enum sp_return sp_blocking_read_next(struct sp_port *port, void *buf, size_t count, unsigned int timeout_ms);
+
/**
* Read bytes from the specified serial port, without blocking.
*
#endif
}
+SP_API enum sp_return sp_blocking_read_next(struct sp_port *port, void *buf,
+ size_t count, unsigned int timeout_ms)
+{
+ TRACE("%p, %p, %d, %d", port, buf, count, timeout_ms);
+
+ CHECK_OPEN_PORT();
+
+ if (!buf)
+ RETURN_ERROR(SP_ERR_ARG, "Null buffer");
+
+ if (count == 0)
+ RETURN_ERROR(SP_ERR_ARG, "Zero count");
+
+ if (timeout_ms)
+ DEBUG_FMT("Reading next max %d bytes from port %s, timeout %d ms",
+ count, port->name, timeout_ms);
+ else
+ DEBUG_FMT("Reading next max %d bytes from port %s, no timeout",
+ count, port->name);
+
+#ifdef _WIN32
+ DWORD bytes_read = 0;
+
+ /* If timeout_ms == 0, set maximum timeout. */
+ DWORD timeout_val = (timeout_ms == 0 ? MAXDWORD - 1 : timeout_ms);
+
+ /* Set timeout. */
+ if (port->timeouts.ReadIntervalTimeout != MAXDWORD ||
+ port->timeouts.ReadTotalTimeoutMultiplier != MAXDWORD ||
+ port->timeouts.ReadTotalTimeoutConstant != timeout_val) {
+ port->timeouts.ReadIntervalTimeout = MAXDWORD;
+ port->timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+ port->timeouts.ReadTotalTimeoutConstant = timeout_val;
+ if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
+ RETURN_FAIL("SetCommTimeouts() failed");
+ }
+
+ /* Loop until we have at least one byte, or timeout is reached. */
+ while (bytes_read == 0) {
+ /* Start read. */
+ if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl)) {
+ DEBUG("Read completed immediately");
+ bytes_read = count;
+ } else if (GetLastError() == ERROR_IO_PENDING) {
+ DEBUG("Waiting for read to complete");
+ if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0)
+ RETURN_FAIL("GetOverlappedResult() failed");
+ if (bytes_read > 0) {
+ DEBUG("Read completed");
+ } else if (timeout_ms > 0) {
+ DEBUG("Read timed out");
+ break;
+ } else {
+ DEBUG("Restarting read");
+ }
+ } else {
+ RETURN_FAIL("ReadFile() failed");
+ }
+ }
+
+ TRY(restart_wait_if_needed(port, bytes_read));
+
+ RETURN_INT(bytes_read);
+
+#else
+ size_t bytes_read = 0;
+ struct timeval start, delta, now, end = {0, 0};
+ int started = 0;
+ fd_set fds;
+ int result;
+
+ if (timeout_ms) {
+ /* Get time at start of operation. */
+ gettimeofday(&start, NULL);
+ /* Define duration of timeout. */
+ delta.tv_sec = timeout_ms / 1000;
+ delta.tv_usec = (timeout_ms % 1000) * 1000;
+ /* Calculate time at which we should give up. */
+ timeradd(&start, &delta, &end);
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(port->fd, &fds);
+
+ /* Loop until we have at least one byte, or timeout is reached. */
+ while (bytes_read == 0) {
+ /*
+ * Check timeout only if we have run select() at least once,
+ * to avoid any issues if a short timeout is reached before
+ * select() is even run.
+ */
+ if (timeout_ms && started) {
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &end, >))
+ /* Timeout has expired. */
+ break;
+ timersub(&end, &now, &delta);
+ }
+ result = select(port->fd + 1, &fds, NULL, NULL, timeout_ms ? &delta : NULL);
+ started = 1;
+ if (result < 0) {
+ if (errno == EINTR) {
+ DEBUG("select() call was interrupted, repeating");
+ continue;
+ } else {
+ RETURN_FAIL("select() failed");
+ }
+ } else if (result == 0) {
+ /* Timeout has expired. */
+ break;
+ }
+
+ /* Do read. */
+ result = read(port->fd, buf, count);
+
+ 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;
+ }
+
+ if (bytes_read == 0)
+ DEBUG("Read timed out");
+
+ RETURN_INT(bytes_read);
+#endif
+}
+
SP_API enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf,
size_t count)
{