mirror of
git://sigrok.org/libserialport
synced 2023-08-10 21:13:24 +03:00
Add new sp_blocking_read_next() function.
This commit is contained in:
parent
0188a545c7
commit
e5c2630eee
@ -1049,6 +1049,34 @@ enum sp_return sp_set_flowcontrol(struct sp_port *port, enum sp_flowcontrol flow
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
134
serialport.c
134
serialport.c
@ -1071,6 +1071,140 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf,
|
||||
#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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user