network/tcp_multiclient/main.c

/*
* EVALUATION AND USE OF THIS SOFTWARE IS SUBJECT TO THE TERMS AND
* CONDITIONS OF THE CONTROLLING LICENSE AGREEMENT FOUND AT LICENSE.md
* IN THIS SDK. IF YOU DO NOT AGREE TO THE LICENSE TERMS AND CONDITIONS,
* PLEASE RETURN ALL SOURCE FILES TO SILICON LABORATORIES.
* (c) Copyright 2018, Silicon Laboratories Inc. All rights reserved.
*/
/*
* Documentation for this app is available online.
* See https://docs.silabs.com/gecko-os/4/standard/latest/sdk/examples/network/tcp-multiclient
*/
#include "gos.h"
#include "example_app_util.h"
#define UART_RX_TIMEOUT 100
#define UART_RX_THRESHOLD_LENGTH 1000
#define APPLICATION_START_LINE "\r\nTCP Multi-Client example starting ..."
static uint32_t client_handle_mask;
static uint8_t uart_rx_buffer[4096];
static uint32_t uart_rx_timestamp;
/*************************************************************************************************/
void gos_app_init(void)
{
gos_result_t result;
GOS_LOG(APPLICATION_START_LINE);
if (GOS_FAILED(result, gos_load_app_settings("settings.ini")))
{
GOS_LOG("Failed to load settings, err:%d", result);
return;
}
if (GOS_FAILED(result, example_app_util_network_up(GOS_INTERFACE_ANY, false, network_event_handler)))
{
}
}
/*************************************************************************************************/
static void network_event_handler(bool is_up)
{
if (is_up == true)
{
gos_result_t result;
uint32_t port;
gos_settings_get_uint32("tcp.server.port", &port);
gos_tcp_register_server_event_handlers(tcp_client_connect_handler, tcp_client_disconnect_handler, tcp_client_receive_handler);
client_handle_mask = 0;
GOS_LOG("Starting TCP Server...");
{
GOS_LOG("Failed to start TCP Server: %d", result);
return;
}
GOS_LOG("TCP Multi-client Server listening on: %s:%u", example_app_util_get_network_interface_ip_address_str(), port);
GOS_LOG("- All data sent to the UART will be forwarded to all connected clients");
GOS_LOG("- All client data will be forwarded to the UART");
}
else
{
client_handle_mask = 0;
set_uart_data_forwarding_enabled(false);
}
}
/*************************************************************************************************/
static void tcp_client_connect_handler(gos_handle_t handle)
{
if (client_handle_mask == 0)
{
set_uart_data_forwarding_enabled(true);
}
client_handle_mask |= (1 << handle);
GOS_LOG("TCP Client-%d connected", handle);
}
/*************************************************************************************************/
static void tcp_client_disconnect_handler(gos_handle_t handle)
{
GOS_LOG("TCP Client-%d disconnected", handle);
client_handle_mask &= ~(1 << handle);
if (client_handle_mask == 0)
{
set_uart_data_forwarding_enabled(false);
}
}
/*************************************************************************************************/
static void tcp_client_receive_handler(gos_handle_t unused)
{
gos_handle_t handle_mask = client_handle_mask;
for (gos_handle_t handle = 0; handle_mask != 0; ++handle, handle_mask >>= 1)
{
if (handle_mask & 0x01)
{
gos_result_t result;
gos_buffer_t buffer =
{
.size = INT32_MAX
};
if (GOS_FAILED(result, gos_tcp_read_with_buffer(handle, &buffer)))
{
if (result != GOS_TIMEOUT)
{
GOS_LOG("Failed to read from client TCP stream %d, err:%d", handle, result);
}
}
else if (buffer.size > 0)
{
gos_uart_transmit_bytes(PLATFORM_STD_UART, buffer.data, buffer.size, NULL, GOS_NO_WAIT);
}
}
}
}
/*************************************************************************************************/
static void set_uart_data_forwarding_enabled(bool enabled)
{
if (enabled)
{
GOS_LOG("Disabling command console");
gos_uart_set_rx_callback(PLATFORM_STD_UART, uart_rx_callback);
}
else
{
gos_uart_clear_rx_callback(PLATFORM_STD_UART, uart_rx_callback);
GOS_LOG("Enabling command console");
}
}
/*************************************************************************************************
* This is called from the UART RX IRQ. It is called for each received UART RX character.
* It is extremely important only minimal work in done in this callback.
* To avoid starving the system, we only invoke the uart_rx_event_handler() when:
* - The time elapsed since the last RX event handler is >= UART_RX_TIMEOUT
* - The number of RX pending bytes >= UART_RX_THRESHOLD_LENGTH
*/
static gos_result_t uart_rx_callback(void *arg)
{
uint32_t bytes_read = 0;
bool invoke_rx_event = false;
const uint32_t now = gos_rtos_get_time();
if ((now - uart_rx_timestamp) >= UART_RX_TIMEOUT)
{
// if the time elapsed since the last rx event was invoked is greater than UART_RX_TIMEOUT
// then invoke the rx event again
invoke_rx_event = true;
}
else
{
gos_uart_peek_bytes(PLATFORM_STD_UART, NULL, &bytes_read);
if (bytes_read >= UART_RX_THRESHOLD_LENGTH)
{
// if the number of RX bytes pending is greater than UART_RX_THRESHOLD_LENGTH
// then invoke the rx event
invoke_rx_event = true;
}
}
if (invoke_rx_event)
{
// update the RX event timestamp and issue the RX event
uart_rx_timestamp = now;
gos_event_issue(uart_rx_event_handler, NULL, GOS_EVENT_FLAG_NONE);
}
return GOS_SUCCESS;
}
/*************************************************************************************************/
static void uart_rx_event_handler(void *arg)
{
gos_result_t result;
uint32_t bytes_read = 0;
if (GOS_FAILED(result, gos_uart_peek_bytes(PLATFORM_STD_UART, NULL, &bytes_read)))
{
return;
}
else if (bytes_read == 0)
{
return;
}
bytes_read = MIN(bytes_read, sizeof(uart_rx_buffer));
if (GOS_FAILED(result, gos_uart_receive_bytes(PLATFORM_STD_UART,
uart_rx_buffer,
bytes_read,
&bytes_read, GOS_NO_WAIT)))
{
return;
}
else if (bytes_read == 0)
{
return;
}
gos_handle_t handle_mask = client_handle_mask;
for (gos_handle_t handle = 0; handle_mask != 0; ++handle, handle_mask >>= 1)
{
if (handle_mask & 0x01)
{
if (GOS_FAILED(result, gos_tcp_write(handle, uart_rx_buffer, bytes_read, false)))
{
GOS_LOG("Failed to forward data to client: %d, err:%d)", handle, result);
if (result == GOS_NOTUP)
{
// if the network isn't up then no point in reading more client streams
return;
}
}
}
}
}