X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=da292ac650ffff5e1099733fe58271cf16b60248;hb=ec4b55ae25c368e30a5db6ab6cb33bb6abeca46c;hp=c77682f98566fcf903e8aa76689949cdc4b8523c;hpb=c3cee38c3b27071347811d742fec3a95ab8746a5;p=libserialport.git diff --git a/serialport.c b/serialport.c index c77682f..da292ac 100644 --- a/serialport.c +++ b/serialport.c @@ -774,6 +774,7 @@ SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, size_t bytes_written = 0; unsigned char *ptr = (unsigned char *) buf; struct timeval start, delta, now, end = {0, 0}; + int started = 0; fd_set fds; int result; @@ -792,8 +793,12 @@ SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, /* Loop until we have written the requested number of bytes. */ while (bytes_written < count) { - /* Wait until space is available. */ - if (timeout_ms) { + /* + * 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. */ @@ -801,6 +806,7 @@ SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, timersub(&end, &now, &delta); } result = select(port->fd + 1, NULL, &fds, NULL, timeout_ms ? &delta : NULL); + started = 1; if (result < 0) { if (errno == EINTR) { DEBUG("select() call was interrupted, repeating"); @@ -964,8 +970,10 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, /* Set timeout. */ if (port->timeouts.ReadIntervalTimeout != 0 || + port->timeouts.ReadTotalTimeoutMultiplier != 0 || port->timeouts.ReadTotalTimeoutConstant != timeout_ms) { port->timeouts.ReadIntervalTimeout = 0; + port->timeouts.ReadTotalTimeoutMultiplier = 0; port->timeouts.ReadTotalTimeoutConstant = timeout_ms; if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) RETURN_FAIL("SetCommTimeouts() failed"); @@ -992,6 +1000,7 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t bytes_read = 0; unsigned char *ptr = (unsigned char *) buf; struct timeval start, delta, now, end = {0, 0}; + int started = 0; fd_set fds; int result; @@ -1010,8 +1019,12 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, /* Loop until we have the requested number of bytes. */ while (bytes_read < count) { - /* Wait until data is available. */ - if (timeout_ms) { + /* + * 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. */ @@ -1019,6 +1032,7 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, 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"); @@ -1036,7 +1050,10 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, if (result < 0) { if (errno == EAGAIN) - /* This shouldn't happen because we did a select() first, but handle anyway. */ + /* + * This shouldn't happen because we did a + * select() first, but handle anyway. + */ continue; else /* This is an actual failure. */ @@ -1054,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) { @@ -1071,8 +1222,10 @@ SP_API enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, /* Set timeout. */ if (port->timeouts.ReadIntervalTimeout != MAXDWORD || + port->timeouts.ReadTotalTimeoutMultiplier != 0 || port->timeouts.ReadTotalTimeoutConstant != 0) { port->timeouts.ReadIntervalTimeout = MAXDWORD; + port->timeouts.ReadTotalTimeoutMultiplier = 0; port->timeouts.ReadTotalTimeoutConstant = 0; if (SetCommTimeouts(port->hdl, &port->timeouts) == 0) RETURN_FAIL("SetCommTimeouts() failed"); @@ -1267,6 +1420,7 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, RETURN_OK(); #else struct timeval start, delta, now, end = {0, 0}; + int started = 0; int result, timeout_remaining_ms; struct pollfd *pollfds; unsigned int i; @@ -1298,7 +1452,12 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, /* Loop until an event occurs. */ while (1) { - if (timeout_ms) { + /* + * Check timeout only if we have run poll() at least once, + * to avoid any issues if a short timeout is reached before + * poll() is even run. + */ + if (timeout_ms && started) { gettimeofday(&now, NULL); if (timercmp(&now, &end, >)) { DEBUG("Wait timed out"); @@ -1309,6 +1468,7 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, } result = poll(pollfds, event_set->count, timeout_ms ? timeout_remaining_ms : -1); + started = 1; if (result < 0) { if (errno == EINTR) {