X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=1a9b4415438422afebc989a6b20cc6f6767a32a7;hb=32dbe2d2984c330642e2f7cad30fc7501a923e4c;hp=74d46eede7953d7ac0c761c18001cd0a3a102ffe;hpb=b457865b8f42b68953bf2910dcc02d3fe27fc2a8;p=libserialport.git diff --git a/serialport.c b/serialport.c index 74d46ee..1a9b441 100644 --- a/serialport.c +++ b/serialport.c @@ -55,6 +55,128 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data, static enum sp_return set_config(struct sp_port *port, struct port_data *data, const struct sp_port_config *config); +#ifndef _WIN32 + +/* Timing abstraction */ + +struct time { + struct timeval tv; +}; + +struct timeout { + unsigned int ms; + struct time start, delta, now, end; + struct timeval delta_tv; + bool overflow; +}; + +#define TIME_ZERO {.tv = {0, 0}} +#define TIME_MS(ms) {.tv = {ms / 1000, (ms % 1000) * 1000}} + +const struct time max_delta = TIME_MS(INT_MAX); + +static void time_get(struct time *time) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) + clock_gettime(CLOCK_REALTIME, &ts); + time->tv.tv_sec = ts.tv_sec; + time->tv.tv_usec = ts.tv_nsec / 1000; +#elif defined(__APPLE__) + mach_timebase_info_data_t info; + mach_timebase_info(&info); + uint64_t ticks = mach_absolute_time(); + uint64_t ns = (ticks * info.numer) / info.denom; + time->tv.tv_sec = ns / 1000000000; + time->tv.tv_usec = (ns % 1000000000) / 1000; +#else + gettimeofday(&time->tv, NULL); +#endif +} + +static void time_set_ms(struct time *time, unsigned int ms) +{ + time->tv.tv_sec = ms / 1000; + time->tv.tv_usec = (ms % 1000) * 1000; +} + +static void time_add(const struct time *a, + const struct time *b, struct time *result) +{ + timeradd(&a->tv, &b->tv, &result->tv); +} + +static void time_sub(const struct time *a, + const struct time *b, struct time *result) +{ + timersub(&a->tv, &b->tv, &result->tv); +} + +static bool time_greater(const struct time *a, const struct time *b) +{ + return timercmp(&a->tv, &b->tv, >); +} + +static void time_as_timeval(const struct time *time, struct timeval *tv) +{ + *tv = time->tv; +} + +static unsigned int time_as_ms(const struct time *time) +{ + return time->tv.tv_sec * 1000 + time->tv.tv_usec / 1000; +} + +static void timeout_start(struct timeout *timeout, unsigned int timeout_ms) +{ + timeout->ms = timeout_ms; + + timeout->overflow = (timeout->ms > INT_MAX); + + /* Get time at start of operation. */ + time_get(&timeout->start); + /* Define duration of timeout. */ + time_set_ms(&timeout->delta, timeout_ms); + /* Calculate time at which we should give up. */ + time_add(&timeout->start, &timeout->delta, &timeout->end); +} + +static bool timeout_check(struct timeout *timeout) +{ + if (timeout->ms == 0) + return false; + + time_get(&timeout->now); + time_sub(&timeout->end, &timeout->now, &timeout->delta); + if ((timeout->overflow = time_greater(&timeout->delta, &max_delta))) + timeout->delta = max_delta; + + return time_greater(&timeout->now, &timeout->end); +} + +static struct timeval *timeout_timeval(struct timeout *timeout) +{ + if (timeout->ms == 0) + return NULL; + + time_as_timeval(&timeout->delta, &timeout->delta_tv); + + return &timeout->delta_tv; +} + +static unsigned int timeout_remaining_ms(struct timeout *timeout) +{ + if (timeout->ms == 0) + return -1; + else if (timeout->overflow) + return INT_MAX; + else + return time_as_ms(&timeout->delta); +} + +#endif + SP_API enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr) { struct sp_port *port; @@ -541,7 +663,7 @@ SP_API enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) RETURN_CODEVAL(ret); } #else - int flags_local = O_NONBLOCK | O_NOCTTY; + int flags_local = O_NONBLOCK | O_NOCTTY | O_CLOEXEC; /* Map 'flags' to the OS-specific settings. */ if ((flags & SP_MODE_READ_WRITE) == SP_MODE_READ_WRITE) @@ -720,7 +842,9 @@ SP_API enum sp_return sp_drain(struct sp_port *port) #else int result; while (1) { -#ifdef __ANDROID__ +#if defined(__ANDROID__) && (__ANDROID_API__ < 21) + /* Android only has tcdrain from platform 21 onwards. + * On previous API versions, use the ioctl directly. */ int arg = 1; result = ioctl(port->fd, TCSBRK, &arg); #else @@ -819,20 +943,12 @@ SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, #else size_t bytes_written = 0; unsigned char *ptr = (unsigned char *) buf; - struct timeval start, delta, now, end = {0, 0}; + struct timeout timeout; 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); - } + timeout_start(&timeout, timeout_ms); FD_ZERO(&fds); FD_SET(port->fd, &fds); @@ -844,14 +960,10 @@ SP_API enum sp_return sp_blocking_write(struct sp_port *port, const void *buf, * 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, NULL, &fds, NULL, timeout_ms ? &delta : NULL); + if (started && timeout_check(&timeout)) + break; + + result = select(port->fd + 1, NULL, &fds, NULL, timeout_timeval(&timeout)); started = 1; if (result < 0) { if (errno == EINTR) { @@ -1038,21 +1150,13 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, #else size_t bytes_read = 0; - unsigned char *ptr = (unsigned char *)buf; - struct timeval start, delta, now, end = {0, 0}; + unsigned char *ptr = (unsigned char *) buf; + struct timeout timeout; 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); - } + timeout_start(&timeout, timeout_ms); FD_ZERO(&fds); FD_SET(port->fd, &fds); @@ -1064,14 +1168,11 @@ SP_API enum sp_return sp_blocking_read(struct sp_port *port, void *buf, * 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); + if (started && timeout_check(&timeout)) + /* Timeout has expired. */ + break; + + result = select(port->fd + 1, &fds, NULL, NULL, timeout_timeval(&timeout)); started = 1; if (result < 0) { if (errno == EINTR) { @@ -1176,20 +1277,12 @@ SP_API enum sp_return sp_blocking_read_next(struct sp_port *port, void *buf, #else size_t bytes_read = 0; - struct timeval start, delta, now, end = {0, 0}; + struct timeout timeout; 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); - } + timeout_start(&timeout, timeout_ms); FD_ZERO(&fds); FD_SET(port->fd, &fds); @@ -1201,14 +1294,11 @@ SP_API enum sp_return sp_blocking_read_next(struct sp_port *port, void *buf, * 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); + if (started && timeout_check(&timeout)) + /* Timeout has expired. */ + break; + + result = select(port->fd + 1, &fds, NULL, NULL, timeout_timeval(&timeout)); started = 1; if (result < 0) { if (errno == EINTR) { @@ -1459,12 +1549,8 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, RETURN_OK(); #else - struct timeval start, delta, now, end = {0, 0}; - const struct timeval max_delta = { - (INT_MAX / 1000), (INT_MAX % 1000) * 1000 - }; - int started = 0, timeout_overflow = 0; - int result, timeout_remaining_ms; + struct timeout timeout; + int started = 0, result; struct pollfd *pollfds; unsigned int i; @@ -1483,15 +1569,7 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, pollfds[i].events |= POLLERR; } - 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); - } + timeout_start(&timeout, timeout_ms); /* Loop until an event occurs. */ while (1) { @@ -1500,24 +1578,12 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, * to avoid any issues if a short timeout is reached before * poll() is even run. */ - if (!timeout_ms) { - timeout_remaining_ms = -1; - } else if (!started) { - timeout_overflow = (timeout_ms > INT_MAX); - timeout_remaining_ms = timeout_overflow ? INT_MAX : timeout_ms; - } else { - gettimeofday(&now, NULL); - if (timercmp(&now, &end, >)) { - DEBUG("Wait timed out"); - break; - } - timersub(&end, &now, &delta); - if ((timeout_overflow = timercmp(&delta, &max_delta, >))) - delta = max_delta; - timeout_remaining_ms = delta.tv_sec * 1000 + delta.tv_usec / 1000; + if (started && timeout_check(&timeout)) { + DEBUG("Wait timed out"); + break; } - result = poll(pollfds, event_set->count, timeout_remaining_ms); + result = poll(pollfds, event_set->count, timeout_remaining_ms(&timeout)); started = 1; if (result < 0) { @@ -1530,7 +1596,7 @@ SP_API enum sp_return sp_wait(struct sp_event_set *event_set, } } else if (result == 0) { DEBUG("poll() timed out"); - if (!timeout_overflow) + if (!timeout.overflow) break; } else { DEBUG("poll() completed");