diff --git a/bacnet-stack/ethernet.h b/bacnet-stack/ethernet.h index 0a249105..d1cfc84c 100644 --- a/bacnet-stack/ethernet.h +++ b/bacnet-stack/ethernet.h @@ -69,7 +69,8 @@ int ethernet_send_pdu( uint16_t ethernet_receive( BACNET_ADDRESS *src, // source address uint8_t *pdu, // PDU data - uint16_t max_pdu); // amount of space available in the PDU + uint16_t max_pdu, // amount of space available in the PDU + unsigned timeout); // milliseconds to wait for a packet void ethernet_get_my_address(BACNET_ADDRESS *my_address); diff --git a/bacnet-stack/ports/linux/ethernet.c b/bacnet-stack/ports/linux/ethernet.c index f1f41f04..4a4f1660 100644 --- a/bacnet-stack/ports/linux/ethernet.c +++ b/bacnet-stack/ports/linux/ethernet.c @@ -32,14 +32,57 @@ ------------------------------------------- ####COPYRIGHTEND####*/ -#include /* Standard I/O */ -#include /* Standard Library */ -#include /* Error number and related */ #include // for standard integer types uint8_t etc. #include // for the standard bool type. -#include /* System data types */ -#include /* Command-line options */ -#include /* string hanfling functions */ + +/* common unix sockets headers needed */ +#include /* basic system data types */ +#include /* timeval{} for select() */ +#include /* timespec{} for pselect() */ +#include /* sockaddr_in{} and other Internet defns */ +#include /* inet(3) functions */ +#include +#include /* for nonblocking */ +#include +#include +#include +#include +#include +#include /* for S_xxx file mode constants */ +#include /* for iovec{} and readv/writev */ +#include +#include +#include /* for Unix domain sockets */ + +#ifdef HAVE_SYS_SELECT_H +# include /* for convenience */ +#endif + +#ifdef HAVE_POLL_H +# include /* for convenience */ +#endif + +#ifdef HAVE_STRINGS_H +# include /* for convenience */ +#endif + +/* Three headers are normally needed for socket/file ioctl's: + * , , and . + */ +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif +#ifdef HAVE_SYS_SOCKIO_H +# include +#endif + +#ifdef HAVE_PTHREAD_H +# include +#endif + #define ENUMS #include @@ -89,6 +132,24 @@ void ethernet_cleanup(void) return; } +/*---------------------------------------------------------------------- + Portable function to set a socket into nonblocking mode. + Calling this on a socket causes all future read() and write() calls on + that socket to do only as much as they can immediately, and return + without waiting. + If no data can be read or written, they return -1 and set errno + to EAGAIN (or EWOULDBLOCK). + Thanks to Bjorn Reese for this code. +----------------------------------------------------------------------*/ +int setNonblocking(int fd) +{ + int flags; + + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + /* opens an 802.2 socket to receive and send packets */ static int ethernet_bind(struct sockaddr *eth_addr, char *interface_name) { @@ -295,25 +356,51 @@ int ethernet_send_pdu( uint16_t ethernet_receive( BACNET_ADDRESS *src, // source address uint8_t *pdu, // PDU data - uint16_t max_pdu) // amount of space available in the PDU + uint16_t max_pdu, // amount of space available in the PDU + unsigned timeout) // number of milliseconds to wait for a packet { int received_bytes; uint8_t buf[MAX_MPDU] = {0}; // data uint16_t pdu_len = 0; // return value + fd_set read_fds; + int max; + struct timeval select_timeout; /* Make sure the socket is open */ if (eth802_sockfd <= 0) return 0; - // FIXME: what about accept()? + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) + { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } + else + { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(eth802_sockfd, &read_fds); + max = eth802_sockfd; - /* Attempt a read */ - received_bytes = read(eth802_sockfd, &buf[0], MAX_MPDU); + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = read(eth802_sockfd, &buf[0], MAX_MPDU); + else + return 0; /* See if there is a problem */ if (received_bytes < 0) { - fprintf(stderr,"ethernet: Read error in receiving packet: %s\n", - strerror(errno)); + // EAGAIN Non-blocking I/O has been selected + // using O_NONBLOCK and no data + // was immediately available for reading. + if (errno != EAGAIN) + fprintf(stderr,"ethernet: Read error in receiving packet: %s\n", + strerror(errno)); return 0; } diff --git a/bacnet-stack/ports/linux/main.c b/bacnet-stack/ports/linux/main.c index 2b6ca409..5b395379 100644 --- a/bacnet-stack/ports/linux/main.c +++ b/bacnet-stack/ports/linux/main.c @@ -257,8 +257,10 @@ static void Init_Device_Parameters(void) static void Init_Service_Handlers(void) { - // custom handlers - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,WhoIsHandler); + // we need to handle who-is to support dynamic device binding + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_WHO_IS, + WhoIsHandler); // set the handler for all the services we don't implement // It is required to send the proper reject message... @@ -274,6 +276,7 @@ int main(int argc, char *argv[]) { BACNET_ADDRESS src = {0}; // address where message came from uint16_t pdu_len = 0; + unsigned timeout = 100; // milliseconds Init_Device_Parameters(); Init_Service_Handlers(); @@ -281,15 +284,17 @@ int main(int argc, char *argv[]) if (!ethernet_init("eth0")) return 1; - Send_IAm(); // loop forever for (;;) { // input + + // returns 0 bytes on timeout pdu_len = ethernet_receive( &src, &Rx_Buf[0], - MAX_MPDU); + MAX_MPDU, + timeout); // process if (pdu_len) @@ -307,7 +312,6 @@ int main(int argc, char *argv[]) // output // blink LEDs, Turn on or off outputs, etc - } return 0;