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);
|
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.
|
* 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
|
#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,
|
SP_API enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user