msousa@0: /* msousa@0: * Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt) msousa@0: * msousa@0: * This file is part of the Modbus library for Beremiz and matiec. msousa@0: * msousa@0: * This Modbus library is free software: you can redistribute it and/or modify msousa@0: * it under the terms of the GNU Lesser General Public License as published by msousa@0: * the Free Software Foundation, either version 3 of the License, or msousa@0: * (at your option) any later version. msousa@0: * msousa@0: * This program is distributed in the hope that it will be useful, but msousa@0: * WITHOUT ANY WARRANTY; without even the implied warranty of msousa@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser msousa@0: * General Public License for more details. msousa@0: * msousa@0: * You should have received a copy of the GNU Lesser General Public License msousa@0: * along with this Modbus library. If not, see . msousa@0: * msousa@0: * This code is made available on the understanding that it will not be msousa@0: * used in safety-critical situations without a full and competent review. msousa@0: */ msousa@0: msousa@0: msousa@0: msousa@0: /* Time handling functions used by the modbus protocols... */ msousa@0: msousa@0: msousa@0: #ifndef __MODBUS_TIME_UTIL_H msousa@0: #define __MODBUS_TIME_UTIL_H msousa@0: msousa@0: msousa@0: /************************************/ msousa@0: /** **/ msousa@0: /** Time format conversion **/ msousa@0: /** **/ msousa@0: /************************************/ msousa@0: msousa@0: /* Function to load a struct timeval correctly from a double. */ msousa@0: static inline struct timeval d_to_timeval(double time) { msousa@0: struct timeval tmp; msousa@0: msousa@0: tmp.tv_sec = time; msousa@0: tmp.tv_usec = 1e6*(time - tmp.tv_sec); msousa@0: return tmp; msousa@0: } msousa@0: msousa@0: msousa@0: /* Function to load a struct timespec correctly from a double. */ msousa@0: static inline struct timespec d_to_timespec(double time) { msousa@0: struct timespec tmp; msousa@0: msousa@0: tmp.tv_sec = time; msousa@0: tmp.tv_nsec = 1e9*(time - tmp.tv_sec); msousa@0: return tmp; msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: /* Function to ... */ msousa@0: static inline struct timespec timespec_dif(struct timespec ts1, struct timespec ts2) { msousa@0: struct timespec ts; msousa@0: msousa@0: ts.tv_sec = ts1.tv_sec - ts2.tv_sec; msousa@0: if(ts1.tv_nsec > ts2.tv_nsec) { msousa@0: ts.tv_nsec = ts1.tv_nsec - ts2.tv_nsec; msousa@0: } else { msousa@0: ts.tv_nsec = 1000000000 + ts1.tv_nsec - ts2.tv_nsec; msousa@0: ts.tv_sec--; msousa@0: } msousa@0: msousa@0: if (ts.tv_sec < 0) msousa@0: ts.tv_sec = ts.tv_nsec = 0; msousa@0: msousa@0: return ts; msousa@0: } msousa@0: msousa@0: /* Function to ... */ msousa@0: static inline struct timespec timespec_add(struct timespec ts1, struct timespec ts2) { msousa@0: struct timespec ts; msousa@0: msousa@0: ts.tv_sec = ts1.tv_sec + ts2.tv_sec; msousa@0: ts.tv_nsec = ts1.tv_nsec + ts2.tv_nsec; msousa@0: ts.tv_sec += ts.tv_nsec / 1000000000; msousa@0: ts.tv_nsec = ts.tv_nsec % 1000000000; msousa@0: return ts; msousa@0: } msousa@0: msousa@0: /* Function to convert a struct timespec to a struct timeval. */ msousa@0: static inline struct timeval timespec_to_timeval(struct timespec ts) { msousa@0: struct timeval tv; msousa@0: msousa@0: tv.tv_sec = ts.tv_sec; msousa@0: tv.tv_usec = ts.tv_nsec/1000; msousa@0: return tv; msousa@0: } msousa@0: msousa@0: msousa@0: /* msousa@0: * NOTE: clock_gettime() is rather expensive, between 7000 and 7500 clock msousa@0: * cycles (measured with rdtsc on an Intel Pentium) msousa@0: * gettimeofday() is half as expensive (3000 to 3500 clock cycles), msousa@0: * but is not POSIX compliant... :-( msousa@0: * Nevertheless this is peanuts (20 us on a 350 MHz cpu) compared to msousa@0: * the timescales required to read a modbus frame over a serial bus msousa@0: * (aprox. 10 ms for a 10 byte frame on a 9600 baud bus!) msousa@0: */ msousa@0: static inline struct timespec timespec_add_curtime(struct timespec ts) { msousa@0: struct timespec res = {.tv_sec = 0, .tv_nsec = 0}; msousa@0: msousa@0: /* is ts = 0 also return 0 !! */ msousa@0: if ((ts.tv_sec != 0) || (ts.tv_nsec != 0)) msousa@0: if (clock_gettime(CLOCK_MONOTONIC, &res) >= 0) msousa@0: res = timespec_add(res, ts); msousa@0: return res; msousa@0: } msousa@0: msousa@0: /************************************/ msousa@0: /** **/ msousa@0: /** select() with absolute timeout **/ msousa@0: /** **/ msousa@0: /************************************/ msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: /* My private version of select using an absolute timeout, instead of the msousa@0: * usual relative timeout. msousa@0: * msousa@0: * NOTE: Ususal select semantics for (a: end_time == NULL) and msousa@0: * (b: *end_time == 0) also apply. msousa@0: * msousa@0: * (a) Indefinite timeout msousa@0: * (b) Try once, and and quit if no data available. msousa@0: */ msousa@0: /* Returns: -1 on error msousa@0: * 0 on timeout msousa@0: * >0 on success msousa@0: */ msousa@0: static int my_select(int fd, fd_set *rfds, fd_set *wfds, const struct timespec *end_time) { msousa@0: msousa@0: int res; msousa@0: struct timespec cur_time; msousa@0: struct timeval timeout, *tv_ptr; msousa@0: fd_set tmp_rfds, *tmp_rfds_ptr, tmp_wfds, *tmp_wfds_ptr; msousa@0: msousa@0: tmp_rfds_ptr = NULL; msousa@0: tmp_wfds_ptr = NULL; msousa@0: if (rfds != NULL) tmp_rfds_ptr = &tmp_rfds; msousa@0: if (wfds != NULL) tmp_wfds_ptr = &tmp_wfds; msousa@0: msousa@0: /*============================* msousa@0: * wait for data availability * msousa@0: *============================*/ msousa@0: do { msousa@0: if (rfds != NULL) tmp_rfds = *rfds; msousa@0: if (wfds != NULL) tmp_wfds = *wfds; msousa@0: /* NOTE: To do the timeout correctly we would have to revert to timers msousa@0: * and asociated signals. That is not very thread friendly, and is msousa@0: * probably too much of a hassle trying to figure out which signal msousa@0: * to use. What if we don't have any free signals? msousa@0: * msousa@0: * The following solution is not correct, as it includes a race msousa@0: * condition. The following five lines of code should really msousa@0: * be atomic! msousa@0: * msousa@0: * NOTE: see also the timeout related comment in the msousa@0: * modbus_tcp_read() function! msousa@0: */ msousa@0: if (end_time == NULL) { msousa@0: tv_ptr = NULL; msousa@0: } else { msousa@0: tv_ptr = &timeout; msousa@0: if ((end_time->tv_sec == 0) && (end_time->tv_nsec == 0)) { msousa@0: timeout.tv_sec = timeout.tv_usec = 0; msousa@0: } else { msousa@0: /* ATOMIC - start */ msousa@0: if (clock_gettime(CLOCK_MONOTONIC, &cur_time) < 0) msousa@0: return -1; msousa@0: timeout = timespec_to_timeval(timespec_dif(*end_time, cur_time)); msousa@0: } msousa@0: } msousa@0: msousa@0: res = select(fd, tmp_rfds_ptr, tmp_wfds_ptr, NULL, tv_ptr); msousa@0: /* ATOMIC - end */ msousa@0: msousa@0: #ifdef DEBUG msousa@0: {int i; msousa@0: if (tmp_rfds_ptr != NULL) msousa@0: for (i = 0; i < fd; i++) msousa@0: if (FD_ISSET(i, tmp_rfds_ptr)) msousa@0: fprintf(stderr,"fd=%d is ready for reading\n", i); msousa@0: if (tmp_wfds_ptr != NULL) msousa@0: for (i = 0; i < fd; i++) msousa@0: if (FD_ISSET(i, tmp_wfds_ptr)) msousa@0: fprintf(stderr,"fd=%d is ready for writing\n", i); msousa@0: } msousa@0: #endif msousa@0: if (res == 0) { msousa@0: #ifdef DEBUG msousa@0: printf("Comms time out\n"); msousa@0: #endif msousa@0: return 0; msousa@0: } msousa@0: if ((res < 0) && (errno != EINTR)) { msousa@0: return -1; msousa@0: } msousa@0: } while (res <= 0); msousa@0: msousa@0: if (rfds != NULL) *rfds = tmp_rfds; msousa@0: if (wfds != NULL) *wfds = tmp_wfds; msousa@0: return res; msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: #endif /* __MODBUS_TIME_UTIL_H */