123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- #include "FreeRTOS.h"
- #include "task.h"
- #include "semphr.h"
- #include "queue.h"
- #include <errno.h>
- #include "common_config.h"
- #include "lwip/sockets.h"
- #include "lwip/inet.h"
- #include "settings_api.h"
- #include "portgw.h"
- #include "tinystdio.h"
- #include "usart.h"
- #ifdef PRINTF_STDLIB
- #include <stdio.h>
- #endif
- #ifdef PRINTF_CUSTOM
- #include "tinystdio.h"
- #endif
- #include <string.h>
- #ifdef PORTGW_ENABLE
- #define PORTGW_PORT_NUM 1001
- #define PORTGW_DATA_CHUNK_SIZE 512
- /* Set option to drop old connection if the new one is accepted */
- #define TCP_DROP_PREV_CONNECTION 1
- static int listen_sd;
- static struct sockaddr_in sa;
- static struct fd_set master_set, read_set, write_set;
- static int max_sd;
- static uint8_t data_buffer[PORTGW_DATA_CHUNK_SIZE];
- #undef DBG
- #define DBG if(0)
- /* (Re)init serial port */
- void serial_reinit()
- {
- uint16_t parity, stop, wordlen;
- /* Parity set */
- switch (sSettings.sPortGw.parity) {
- case GW_EVEN_PAR:
- parity = USART_Parity_Even;
- break;
- case GW_ODD_PAR:
- parity = USART_Parity_Odd;
- break;
- case GW_NO_PAR:
- default:
- parity = USART_Parity_No;
- break;
- }
- /* Stop bits set */
- switch (sSettings.sPortGw.stopbits) {
- case 2:
- stop = USART_StopBits_2;
- break;
- case 1:
- default:
- stop = USART_StopBits_1;
- break;
- }
- /* Word length set */
- switch (sSettings.sPortGw.databits) {
- case 8:
- if ((sSettings.sPortGw.parity == GW_EVEN_PAR) ||
- (sSettings.sPortGw.parity == GW_ODD_PAR)) {
- /* 8-bit data + Parity */
- wordlen = 9;
- } else {
- wordlen = 8;
- }
- break;
- case 7:
- default:
- /* 7-bit data + Parity */
- wordlen = 8;
- break;
- }
- uart_config_reinit(RS485_USART, sSettings.sPortGw.baud, wordlen, parity, stop);
- }
- /* Stop server */
- static void stop_server(void) {
- /* Clean up all of the sockets that are open */
- for (int i = 0; i <= max_sd; ++i)
- {
- if (FD_ISSET(i, &master_set)) {
- DBG printf("Close sock %d\n", i);
- closesocket(i);
- FD_CLR(i, &master_set);
- }
- }
- DBG printf("Portgw stopped\n");
- }
- /* Start TCP server */
- static bool start_tcp_server(uint16_t port)
- {
- int res;
- listen_sd = socket(PF_INET, SOCK_STREAM, 0);
- if (listen_sd < 0) {
- DBG printf("Socket create failed\r\n");
- return false;
- }
- res = fcntl(listen_sd, F_SETFL, O_NONBLOCK);
- if (res < 0) {
- DBG printf("fcntl() failed");
- closesocket(listen_sd);
- return false;
- }
- #if 0
- /* Not used */
- lwip_setsockopt(listen_sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
- if (res < 0) {
- printf("setsockopt() failed");
- closesocket(listen_sd);
- return false;
- }
- /* Not used (SO_REUSE==1 should be enabled) */
- int on = 1;
- res = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
- if (res < 0) {
- printf("setsockopt() failed");
- closesocket(listen_sd);
- return false;
- }
- #endif
- memset(&sa, 0, sizeof(struct sockaddr_in));
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = IPADDR_ANY;
- sa.sin_port = htons(port);
- if (bind(listen_sd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
- {
- DBG printf("Bind to port %d failed\n", port);
- closesocket(listen_sd);
- return false;
- }
- res = listen(listen_sd, 1);
- if (res < 0) {
- DBG printf("Listen failed failed\r\n");
- closesocket(listen_sd);
- return false;
- }
- FD_ZERO(&master_set);
- max_sd = listen_sd;
- FD_SET(listen_sd, &master_set);
- printf("Port %d opened\n", port);
- return true;
- }
- /* Start UDP server */
- static bool start_udp_server(uint16_t port)
- {
- int res;
- listen_sd = socket(PF_INET, SOCK_DGRAM, 0);
- if (listen_sd < 0) {
- printf("Socket create failed\r\n");
- return false;
- }
- else {
- printf("Socket create OK\r\n");
- }
- res = fcntl(listen_sd, F_SETFL, O_NONBLOCK);
- if (res < 0) {
- DBG printf("fcntl() failed");
- closesocket(listen_sd);
- return false;
- }
- #if 0
- /* Not used */
- lwip_setsockopt(listen_sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
- if (res < 0) {
- printf("setsockopt() failed");
- closesocket(listen_sd);
- return false;
- }
- /* Not used (SO_REUSE==1 should be enabled) */
- int on = 1;
- res = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
- if (res < 0) {
- printf("setsockopt() failed");
- closesocket(listen_sd);
- return false;
- }
- #endif
- memset(&sa, 0, sizeof(struct sockaddr_in));
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = IPADDR_ANY;
- sa.sin_port = htons(port);
- if (bind(listen_sd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
- {
- DBG printf("Bind to port %d failed\n", port);
- closesocket(listen_sd);
- return false;
- }
- FD_ZERO(&master_set);
- max_sd = listen_sd;
- FD_SET(listen_sd, &master_set);
- printf("Port %d opened\n", port);
- return true;
- }
- /* Start server */
- static bool start_server(gwtrans_t transtype, uint16_t port)
- {
- switch (transtype) {
- case GW_TCP:
- return start_tcp_server(port);
- case GW_UDP:
- return start_udp_server(port);
- default:
- return false;
- }
- }
- /* TCP server main loop */
- static void tcp_server(void)
- {
- int recvd, sent;
- uint8_t c;
- int new_sd;
- static int active_sd = -1;
- int desc_ready, rc;
- struct timeval timeout;
- bool close_conn;
- timeout.tv_sec = 5;
- timeout.tv_usec = 0;
- memcpy(&read_set, &master_set, sizeof(master_set));
- memcpy(&write_set, &master_set, sizeof(master_set));
- DBG printf("Waiting on select()...\n");
- rc = select(max_sd + 1, &read_set, &write_set, NULL, &timeout);
- DBG printf(" select() returned %d\n", rc);
- if (rc < 0) {
- DBG printf(" select() failed\n");
- }
- if (rc == 0) {
- DBG printf(" select() timed out.\n");
- }
- /* One or more descriptors are readable. Need to \
- * determine which ones they are. */
- desc_ready = rc;
- for (int i=0; i <= max_sd && desc_ready > 0; ++i) {
- /*******************************************************/
- /* Check to see if this descriptor is ready */
- /*******************************************************/
- if (FD_ISSET(i, &read_set)) {
- /* A descriptor was found that was readable - one \
- * less has to be looked for. This is being done \
- * so that we can stop looking at the working set \
- * once we have found all of the descriptors that \
- * were ready. */
- desc_ready -= 1;
- /* Check to see if this is the listening socket */
- if (i == listen_sd) {
- DBG printf(" Listening socket is readable\n");
- /* Accept all incoming connections that are */
- /* queued up on the listening socket before we */
- /* loop back and call select again. */
- do {
- /* Accept each incoming connection. If */
- /* accept fails with EWOULDBLOCK, then we */
- /* have accepted all of them. Any other */
- /* failure on accept will cause us to end the */
- /* server. */
- new_sd = accept(listen_sd, NULL, NULL);
- if (new_sd < 0 && errno != 0) {
- if (errno != EWOULDBLOCK) {
- DBG printf(" accept() failed: %d\n", errno);
- }
- break;
- }
- /* Add the new incoming connection to the */
- /* master read set */
- DBG printf(" New incoming connection - %d\n", new_sd);
- FD_SET(new_sd, &master_set);
- if (new_sd > max_sd) {
- max_sd = new_sd;
- }
- #if TCP_DROP_PREV_CONNECTION
- /* Close previous active connection */
- if (active_sd != -1 && active_sd != new_sd) {
- DBG printf(" Close prev active connection %d\n", active_sd);
- close(active_sd);
- FD_CLR(active_sd, &master_set);
- if (active_sd == max_sd) {
- while (FD_ISSET(max_sd, &master_set) == false) {
- max_sd -= 1;
- }
- }
- }
- /* Mark new connection as active */
- active_sd = new_sd;
- DBG printf(" New active connection %d\n", active_sd);
- #endif
- /* Loop back up and accept another incoming */
- /* connection */
- } while (new_sd != -1);
- }
- /* This is not the listening socket, therefore an */
- /* existing connection must be readable */
- else {
- DBG printf(" Descriptor %d is readable\n", i);
- close_conn = false;
- /* Receive data on this connection until the */
- /* recv fails with EWOULDBLOCK. If any other */
- /* failure occurs, we will close the */
- /* connection. */
- if ((recvd = recv(i, data_buffer, sizeof(data_buffer), 0)) > 0) {
- DBG {
- printf("received %d bytes:", recvd);
- for (uint32_t i = 0; i < 3; i++) {
- printf(" 0x%X", data_buffer[i]);
- }
- printf("...\r\n");
- }
- /* Put RX data to the queue */
- for (uint32_t i = 0; i < (uint32_t)recvd; i++) {
- if (xQueueSend(rs485TxQ, &data_buffer[i], 1000) != pdTRUE) {
- printf("[portgw] Rx data lost\r\n");
- }
- }
- /* Start RS485 transmission */
- rs485_enable_tx();
- }
- if (recvd < 0) {
- if (errno != EWOULDBLOCK){
- DBG printf(" recv() failed: %d\n", errno);
- close_conn = true;
- }
- }
- /* Check to see if the connection has been */
- /* closed by the client */
- if (recvd == 0) {
- DBG printf(" Connection closed\n");
- close_conn = true;
- }
- /* If the close_conn flag was turned on, we need */
- /* to clean up this active connection. This */
- /* clean up process includes removing the */
- /* descriptor from the master set and */
- /* determining the new maximum descriptor value */
- /* based on the bits that are still turned on in */
- /* the master set. */
- if (close_conn) {
- closesocket(i);
- FD_CLR(i, &master_set);
- if (i == max_sd) {
- while (FD_ISSET(max_sd, &master_set) == false) {
- max_sd -= 1;
- }
- }
- }
- }
- }
- /* Socket is ready to write */
- else if (FD_ISSET(i, &write_set)){
- /* Put RX data to the queue */
- uint32_t len = 0;
- while (len < sizeof(data_buffer) && xQueueReceive(rs485RxQ, &c, 5) == pdTRUE) {
- data_buffer[len++] = c;
- }
- if (len > 0) {
- DBG printf("%d bytes to send\r\n", len);
- sent = send(i, data_buffer, len, 0);
- if (sent <= 0) {
- DBG printf("send failed (%d)\n", sent);
- }
- }
- }
- }
- }
- /* UDP server main loop */
- static void udp_server(void)
- {
- struct timeval timeout;
- int recvd, sent;
- uint8_t c;
- int rc;
- bool close_conn;
- timeout.tv_sec = 5;
- timeout.tv_usec = 0;
- memcpy(&read_set, &master_set, sizeof(master_set));
- memcpy(&write_set, &master_set, sizeof(master_set));
- /* TODO Remove select for UDP */
- DBG printf("Waiting on select()...\n");
- rc = select(max_sd + 1, &read_set, &write_set, NULL, &timeout);
- DBG printf(" select() returned %d\n", rc);
- if (rc < 0) {
- DBG printf(" select() failed\n");
- }
- if (rc == 0) {
- DBG printf(" select() timed out.\n");
- }
- else{
- /*******************************************************/
- /* Check to see if this descriptor is ready */
- /*******************************************************/
- if (FD_ISSET(listen_sd, &read_set)) {
- DBG printf(" Descriptor %d is readable\n", listen_sd);
- close_conn = false;
- /* Receive data on this connection until the */
- /* recv fails with EWOULDBLOCK. If any other */
- /* failure occurs, we will close the */
- /* connection.
- */
- socklen_t from_len;
- /* Save addr & port where data (requests) come from */
- if ((recvd = recvfrom(listen_sd, data_buffer, sizeof(data_buffer), 0,
- (struct sockaddr*)&sa, &from_len)) > 0) {
- DBG {
- printf("From %s:%d received %d bytes:",
- inet_ntoa(sa.sin_addr.s_addr), sa.sin_port, recvd);
- for (uint32_t i = 0; i < 3; i++) {
- printf(" 0x%X", data_buffer[i]);
- }
- printf("...\r\n");
- }
- /* Put RX data to the queue */
- for (uint32_t i = 0; i < (uint32_t)recvd; i++) {
- if (xQueueSend(rs485TxQ, &data_buffer[i], 1000) != pdTRUE) {
- printf("[portgw] Rx data lost\r\n");
- }
- }
- /* Start RS485 transmission */
- rs485_enable_tx();
- }
- if (recvd < 0) {
- if (errno != EWOULDBLOCK){
- DBG printf(" recv() failed\n");
- close_conn = true;
- }
- }
- /* Check to see if the connection has been */
- /* closed by the client */
- if (recvd == 0) {
- DBG printf(" Connection closed\n");
- close_conn = true;
- }
- /* If the close_conn flag was turned on, we need */
- /* to clean up this active connection. This */
- /* clean up process includes removing the */
- /* descriptor from the master set and */
- /* determining the new maximum descriptor value */
- /* based on the bits that are still turned on in */
- /* the master set. */
- if (close_conn) {
- closesocket(listen_sd);
- FD_CLR(listen_sd, &master_set);
- }
- }
- /* Socket is ready to write */
- else if (FD_ISSET(listen_sd, &write_set)) {
- uint32_t len = 0;
- while (len < sizeof(data_buffer) && xQueueReceive(rs485RxQ, &c, 5) == pdTRUE) {
- data_buffer[len++] = c;
- }
- if (len > 0) {
- DBG printf("%d bytes to send\r\n", len);
- /* Send data (responce) to the latest saved addr & port */
- sent = sendto(listen_sd, data_buffer, len, 0, (struct sockaddr*)&sa, sizeof(sa));
- if (sent <= 0) {
- DBG printf("send failed (%d)\n", sent);
- }
- }
- } else {
- /* Nothing to do */
- taskYIELD();
- }
- }
- }
- /* Main task */
- void portgw_thread(void *arg)
- {
- uint16_t port;
- gwtrans_t transtype;
- gwmode_t mode;
- bool enabled;
- gwparity_t parity;
- uint8_t stopbits;
- uint8_t databits;
- uint32_t baud;
- bool firstrun = true;
- (void)arg;
- FD_ZERO(&master_set);
- enabled = sSettings.sPortGw.enabled;
- port = sSettings.sPortGw.port;
- transtype = sSettings.sPortGw.transtype;
- mode = sSettings.sPortGw.mode;
- parity = sSettings.sPortGw.parity;
- stopbits = sSettings.sPortGw.stopbits;
- databits = sSettings.sPortGw.databits;
- baud = sSettings.sPortGw.baud;
- (void)mode;
- while (1) {
- /* Check if network settings was changed */
- if ((sSettings.sPortGw.port != port) ||
- (sSettings.sPortGw.enabled != enabled) ||
- (sSettings.sPortGw.transtype != transtype) ||
- (firstrun))
- {
- port = sSettings.sPortGw.port;
- enabled = sSettings.sPortGw.enabled;
- transtype = sSettings.sPortGw.transtype;
- if (!firstrun) {
- /* Stop server */
- stop_server();
- }
- if (enabled) {
- /* (Re)start server */
- if (!start_server(transtype, port)) {
- DBG printf("Server start error\n");
- vTaskDelay(5000);
- continue;
- }
- }
- else {
- /* Clear buffers */
- if (uxQueueMessagesWaiting(rs485TxQ) > 0) {
- xQueueReset(rs485TxQ);
- }
- if (uxQueueMessagesWaiting(rs485RxQ) > 0) {
- xQueueReset(rs485RxQ);
- }
- vTaskDelay(5000);
- continue;
- }
- }
- /* Check if serial settings was changed */
- if ((sSettings.sPortGw.parity != parity) ||
- (sSettings.sPortGw.stopbits != stopbits) ||
- (sSettings.sPortGw.databits != databits) ||
- (sSettings.sPortGw.baud != baud) ||
- (firstrun))
- {
- parity = sSettings.sPortGw.parity;
- stopbits = sSettings.sPortGw.stopbits;
- databits = sSettings.sPortGw.databits;
- baud = sSettings.sPortGw.baud;
- serial_reinit();
- DBG printf("Serial reinitialized\r\n");
- }
- if (!enabled) {
- vTaskDelay(5000);
- continue;
- }
- switch (transtype) {
- case GW_UDP:
- udp_server();
- break;
- case GW_TCP:
- tcp_server();
- break;
- default:
- vTaskDelay(5000);
- break;
- }
- firstrun = false;
- }
- }
- /* Main init */
- void portgw_init(void)
- {
- xTaskCreate(portgw_thread, ( char * ) "portgw_thr", configMINIMAL_STACK_SIZE * 2,
- NULL, tskIDLE_PRIORITY, NULL);
- }
- #endif
|