From 389778776fd6ae7d48f849462c0a23e01febcf0a Mon Sep 17 00:00:00 2001 From: Markus Lassila Date: Mon, 9 Oct 2023 17:30:09 +0300 Subject: [PATCH] applications: serial_lte_modem: SLM server fixes. - Recv even when the connection is being closed. - Fix statuses of operations to correspond to correct errno values. - Documentation fixes. Signed-off-by: Markus Lassila --- .../doc/TCPUDP_AT_commands.rst | 50 +++++++--- .../serial_lte_modem/doc/slm_testing.rst | 93 +++++++------------ .../serial_lte_modem/src/slm_at_host.c | 5 +- .../serial_lte_modem/src/slm_at_tcp_proxy.c | 46 ++++----- .../releases/release-notes-changelog.rst | 1 + 5 files changed, 102 insertions(+), 93 deletions(-) diff --git a/applications/serial_lte_modem/doc/TCPUDP_AT_commands.rst b/applications/serial_lte_modem/doc/TCPUDP_AT_commands.rst index b0717e56a9a..689bf9b15d8 100644 --- a/applications/serial_lte_modem/doc/TCPUDP_AT_commands.rst +++ b/applications/serial_lte_modem/doc/TCPUDP_AT_commands.rst @@ -42,6 +42,7 @@ Syntax * The ```` parameter is an integer. If it is given, a TLS server will be started. It indicates to the modem the credential of the security tag used for establishing a secure connection. + Can only be used when the :file:`overlay-native_tls.conf` configuration file is used. Response syntax ~~~~~~~~~~~~~~~ @@ -57,13 +58,40 @@ Response syntax * The ```` value is an integer. It represents the error value according to the standard POSIX *errno*. -Examples +Unsolicited notification +~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + #XTCPSVR: "","connected" + +This is emitted when a new connection has been created to the server. + +* The ```` value is the IPv4 or IPv6 address of the peer. + +.. tcpsvr_disconnect_notif_start + +:: + + #XTCPSVR: ,"disconnected" + +This is emitted when the client has been disconnected. + +* The ```` value is an integer. + It is either ``0`` when the client is disconnected normally, or an *-errno* code. + +.. tcpsvr_disconnect_notif_end + + +Example ~~~~~~~~ :: AT#XTCPSVR=1,3442,600 #XTCPSVR: 2,"started" + #XTCPSVR: "123.456.789.123","connected" + #XTCPSVR: 0,"disconnected" OK Read command @@ -319,11 +347,9 @@ Syntax Response syntax ~~~~~~~~~~~~~~~ -:: - - #XTCPSVR: ,"disconnected" - -* The ```` value is an integer of -111 or ECONNREFUSED. +.. include:: TCPUDP_AT_commands.rst + :start-after: tcpsvr_disconnect_notif_start + :end-before: tcpsvr_disconnect_notif_end Examples ~~~~~~~~ @@ -334,7 +360,7 @@ Examples #XTCPSVR: 1,2,1 OK AT#XTCPHANGUP=2 - #XTCPSVR: -111,"disconnected" + #XTCPSVR: 0,"disconnected" OK Read command @@ -376,11 +402,12 @@ TCP receive data :: - #XTCPDATA: + +* The ```` parameter is an integer that indicates the size of the received data. + This notification comes only when SLM is not operating in :ref:`data mode `. * The ```` parameter is a string that contains the data received. -* The ```` parameter is the size of the string, which is present only when SLM is not operating in ``slm_data_mode``. UDP server #XUDPSVR =================== @@ -655,8 +682,9 @@ UDP receive data :: - #XUDPDATA: + +* The ```` parameter is an integer that indicates the size of the received data. + This notification comes only when SLM is not operating in :ref:`data mode `. * The ```` parameter is a string that contains the data received. -* The ```` parameter is the size of the string, which is present only when SLM is not operating in ``slm_data_mode``. diff --git a/applications/serial_lte_modem/doc/slm_testing.rst b/applications/serial_lte_modem/doc/slm_testing.rst index ebca83ac7e8..dc5e0020fb9 100644 --- a/applications/serial_lte_modem/doc/slm_testing.rst +++ b/applications/serial_lte_modem/doc/slm_testing.rst @@ -225,7 +225,7 @@ TCP client #XTCPCLI: 1,0 OK - #. Send plain text data to the TCP server and retrieve ten bytes of the returned data. + #. Send plain text data to the TCP server and receive ten bytes of the returned data. .. parsed-literal:: :class: highlight @@ -234,9 +234,8 @@ TCP client #XTCPSEND: 8 OK - **AT#XTCPRECV=10** + #XTCPDATA: 10 PONG: b'Te - #XTCPRECV: 10 OK #. Disconnect and confirm the status of the connection. @@ -450,7 +449,7 @@ You must register the corresponding credentials on the server side. #XTCPCLI: 1,0 OK - #. Send plain text data to the TLS server and retrieve the returned data. + #. Send plain text data to the TLS server and receive the returned data. .. parsed-literal:: :class: highlight @@ -458,12 +457,9 @@ You must register the corresponding credentials on the server side. **AT#XTCPSEND="Test TLS client"** #XTCPSEND: 15 OK - #XTCPDATA: 24 - **AT#XTCPRECV** + #XTCPDATA: 24 PONG: b'Test TLS client' - #XTCPRECV: 24 - OK #. Disconnect from the server. @@ -624,7 +620,7 @@ To act as a TCP server, |global_private_address| :class: highlight **AT#XSOCKET=1,1,1** - #XSOCKET: 2,1,6 + #XSOCKET: 0,1,6 OK **AT#XBIND=**\ *1234* @@ -640,16 +636,16 @@ To act as a TCP server, |global_private_address| .. parsed-literal:: :class: highlight - **AT#XACCEPT** - #XACCEPT: connected with *IP address* - #XACCEPT: 3 - OK + **AT#XACCEPT=60** + #XACCEPT: 1,"*IP address*" + + OK **AT#XRECV=0** + #XRECV: 26 Hello, TCP#1!Hello, TCP#2! OK - **AT#XSEND="TCP1/2 received"** #XSEND: 15 OK @@ -682,7 +678,7 @@ To act as a TCP server, |global_private_address| :class: highlight **AT#XSOCKET=0** - #XSOCKET: "closed" + #XSOCKET: 0,"closed" OK @@ -698,7 +694,7 @@ To act as a TCP server, |global_private_address| OK **AT#XTCPSVR?** - #XTCPSVR: -1,-1 + #XTCPSVR: -1,-1,0 OK #. Create a TCP server and read the information about the current state. @@ -708,59 +704,59 @@ To act as a TCP server, |global_private_address| :class: highlight **AT#XTCPSVR=1,**\ *1234* - #XTCPSVR: 2,"started" + #XTCPSVR: 0,"started" OK **AT#XTCPSVR?** - #XTCPSVR: 1,-1,0 + #XTCPSVR: 0,-1,1 OK #. Run the :file:`client_tcp.py` script to start sending data to the server. - #. Observe that the server accepts the connection from the client. + #. Observe that the server accepts the connection from the client and receives the first packets. Read the information about the current state again. .. parsed-literal:: :class: highlight - #XTCPSVR: *IP address* connected + #XTCPSVR: "*IP address*","connected" + #XTCPDATA: 13 + Hello, TCP#1! #XTCPDATA: 13 + Hello, TCP#2! **AT#XTCPSVR?** - #XTCPSVR: 1,2,0 + #XTCPSVR: 0,1,1 OK - #. Start receiving and acknowledging the data. + #. Send responses and receive the rest of the data. + Client disconnects after receiving the last response. .. parsed-literal:: :class: highlight - **AT#XTCPRECV** - Hello, TCP#1!Hello, TCP#2! - #XTCPRECV: 26 - OK - **AT#XTCPSEND="TCP1/2 received"** + #XTCPSEND: 15 + OK + #XTCPDATA: 13 + Hello, TCP#3! #XTCPDATA: 13 + Hello, TCP#4! #XTCPDATA: 13 + Hello, TCP#5! - **AT#XTCPSVR?** - #XTCPSVR: 1,2,0 - OK - - **AT#XTCPRECV** - Hello, TCP#3!Hello, TCP#4!Hello, TCP#5! - #XTCPRECV: 39 - OK + **AT#XTCPSEND="TCP3/4/5 received"** - **AT#XTCPSEND=1,"TCP3/4/5 received"** #XTCPSEND: 17 + OK + #XTCPSVR: 0,"disconnected" + #. Observe the output of the Python script:: $ python client_tcp.py @@ -780,7 +776,7 @@ To act as a TCP server, |global_private_address| :class: highlight **AT#XTCPSVR?** - #XTCPSVR: 1,2,0 + #XTCPSVR: 0,-1,1 OK #. Stop the server. @@ -789,11 +785,11 @@ To act as a TCP server, |global_private_address| :class: highlight **AT#XTCPSVR=0** - #XTCPSVR:-1,"stopped" + #XTCPSVR:0,"stopped" OK **AT#XTCPSVR?** - #XTCPSVR: -1,-1 + #XTCPSVR: -1,-1,0 OK UDP server @@ -1003,31 +999,14 @@ To act as a UDP server, |global_private_address| TLS server ========== -The TLS server role is currently not supported. +The TLS server role is currently only supported when using the :file:`overlay-native_tls.conf` configuration file. -.. parsed-literal:: - :class: highlight - - **AT#XSOCKET=1,1,1,16842753** - #XSOCKET: "(D)TLS Server not supported" - ERROR - - **AT#XTCPSVR=1,3443,16842753** - #XTCPSVR: "TLS Server not supported" - ERROR DTLS server =========== The DTLS server role is currently not supported (modem limitation). -.. parsed-literal:: - :class: highlight - - **AT#XSOCKET=1,2,1,16842755** - #XSOCKET: "(D)TLS Server not supported" - ERROR - DNS lookup ========== diff --git a/applications/serial_lte_modem/src/slm_at_host.c b/applications/serial_lte_modem/src/slm_at_host.c index 00d2085ba95..1825860a946 100644 --- a/applications/serial_lte_modem/src/slm_at_host.c +++ b/applications/serial_lte_modem/src/slm_at_host.c @@ -221,8 +221,8 @@ K_TIMER_DEFINE(inactivity_timer, inactivity_timer_handler, NULL); static size_t raw_rx_handler(const uint8_t *buf, const size_t len) { size_t processed; - bool quit_str_match; - bool prev_quit_str_match; + bool quit_str_match = false; + bool prev_quit_str_match = false; uint8_t quit_str_match_count; uint8_t prev_quit_str_match_count; @@ -230,7 +230,6 @@ static size_t raw_rx_handler(const uint8_t *buf, const size_t len) /* Initialize from previous time.*/ prev_quit_str_match_count = quit_str_partial_match; - quit_str_match = false; quit_str_match_count = prev_quit_str_match_count; if (prev_quit_str_match_count != 0) { prev_quit_str_match = true; diff --git a/applications/serial_lte_modem/src/slm_at_tcp_proxy.c b/applications/serial_lte_modem/src/slm_at_tcp_proxy.c index 82677b4f734..326e3c5e15a 100644 --- a/applications/serial_lte_modem/src/slm_at_tcp_proxy.c +++ b/applications/serial_lte_modem/src/slm_at_tcp_proxy.c @@ -239,6 +239,7 @@ static int do_tcp_server_stop(void) return ret; } proxy.sock = INVALID_SOCKET; + proxy.family = AF_UNSPEC; } if (k_thread_join(&tcp_thread, K_SECONDS(CONFIG_SLM_TCP_POLL_TIME + 1)) != 0) { LOG_WRN("Wait for thread terminate failed"); @@ -416,7 +417,9 @@ static void tcpsvr_terminate_connection(int cause) (void)exit_datamode_handler(cause); } if (proxy.sock_peer != INVALID_SOCKET) { - close(proxy.sock_peer); + if (close(proxy.sock_peer) < 0) { + LOG_WRN("close() error: %d", -errno); + } proxy.sock_peer = INVALID_SOCKET; rsp_send("\r\n#XTCPSVR: %d,\"disconnected\"\r\n", cause); } @@ -518,6 +521,23 @@ static void tcpsvr_thread_func(void *p1, void *p2, void *p3) client_events: /* Incoming socket events */ if (fds[1].revents) { + /* Process POLLIN first to get the data, even if there are errors. */ + if ((fds[1].revents & POLLIN) == POLLIN) { + ret = recv(fds[1].fd, (void *)slm_data_buf, + sizeof(slm_data_buf), 0); + if (ret < 0) { + LOG_ERR("recv() error: %d", -errno); + tcpsvr_terminate_connection(-errno); + fds[1].fd = INVALID_SOCKET; + continue; + } + if (ret > 0) { + if (!in_datamode()) { + rsp_send("\r\n#XTCPDATA: %d\r\n", ret); + } + data_send(slm_data_buf, ret); + } + } if ((fds[1].revents & POLLERR) == POLLERR) { LOG_ERR("1: POLLERR"); tcpsvr_terminate_connection(-EIO); @@ -526,32 +546,14 @@ static void tcpsvr_thread_func(void *p1, void *p2, void *p3) } if ((fds[1].revents & POLLHUP) == POLLHUP) { LOG_ERR("1: POLLHUP"); - tcpsvr_terminate_connection(-ECONNRESET); + tcpsvr_terminate_connection(0); fds[1].fd = INVALID_SOCKET; continue; } if ((fds[1].revents & POLLNVAL) == POLLNVAL) { LOG_WRN("1: POLLNVAL"); - tcpsvr_terminate_connection(-ENETDOWN); + tcpsvr_terminate_connection(-EBADF); fds[1].fd = INVALID_SOCKET; - continue; - } - if ((fds[1].revents & POLLIN) != POLLIN) { - continue; - } - ret = recv(fds[1].fd, (void *)slm_data_buf, sizeof(slm_data_buf), 0); - if (ret < 0) { - LOG_WRN("recv() error: %d", -errno); - continue; - } - if (ret == 0) { - continue; - } - if (in_datamode()) { - data_send(slm_data_buf, ret); - } else { - rsp_send("\r\n#XTCPDATA: %d\r\n", ret); - data_send(slm_data_buf, ret); } } } @@ -805,7 +807,7 @@ int handle_at_tcp_hangup(enum at_cmd_type cmd_type) if (handle != proxy.sock_peer) { return -EINVAL; } - tcpsvr_terminate_connection(-ECONNREFUSED); + tcpsvr_terminate_connection(0); err = 0; break; diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 7d04f424bb7..2b522151f6a 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -241,6 +241,7 @@ Serial LTE modem The full chip reset (using the ``#XRESET`` AT command) remains supported. * ``#XGPSDEL`` AT command to disallow deleting local clock (TCXO) frequency offset data because it is an internal value that should not be deleted when simulating a cold start. * Socket option ``TLS_DTLS_HANDSHAKE_TIMEO`` to a new name value. + * ``#XTCPSVR`` connection closure status and documentation. * Removed: