66#include <zephyr/logging/log.h>
77#include <zephyr/kernel.h>
88#include <stdio.h>
9+ #include <stdlib.h>
910#include <string.h>
1011#include <modem/sms.h>
1112#include "sm_util.h"
1213#include "sm_at_host.h"
1314
1415LOG_MODULE_REGISTER (sm_sms , CONFIG_SM_LOG_LEVEL );
1516
16- #define MAX_CONCATENATED_MESSAGE 3
17+ #define MAX_CONCATENATED_MESSAGE 10
18+ #define SM_SMS_AT_HEADER_INFO_MAX_LEN 64
19+ /* Maximum time to wait for the next part of the concatenated message to be received in minutes */
20+ #define MAX_CONCATENATED_MESSAGE_AGE K_MINUTES(3)
1721
1822/**@brief SMS operations. */
1923enum sm_sms_operation {
@@ -22,102 +26,161 @@ enum sm_sms_operation {
2226 AT_SMS_SEND
2327};
2428
25- static int sms_handle = -1 ;
26- static struct modem_pipe * sms_pipe ;
29+ static void sms_concat_cleanup_work_fn (struct k_work * work );
2730
28- static void sms_callback (struct sms_data * const data , void * context )
31+ struct sm_sms_context {
32+ int sms_handle ;
33+ struct modem_pipe * pipe ; /* Pipe requesting SMS service */
34+ uint16_t ref_number ; /* Reference number in the concatenated message */
35+ uint8_t total_msgs ; /* Total number of messages in the concatenated message */
36+ uint8_t count ; /* Current number of messages received in the concatenated message */
37+ char * concat_rsp_buf ; /* Buffer for the concatenated message */
38+ struct k_work_delayable cleanup_work ;
39+ };
40+
41+ static struct sm_sms_context sms_ctx = {
42+ .sms_handle = -1 ,
43+ .concat_rsp_buf = NULL ,
44+ .cleanup_work = Z_WORK_DELAYABLE_INITIALIZER (sms_concat_cleanup_work_fn ),
45+ };
46+
47+ static void sms_concat_clear (struct sm_sms_context * ctx )
48+ {
49+ k_work_cancel_delayable (& ctx -> cleanup_work );
50+ if (ctx -> concat_rsp_buf != NULL ) {
51+ free (ctx -> concat_rsp_buf );
52+ ctx -> concat_rsp_buf = NULL ;
53+ }
54+ ctx -> ref_number = 0 ;
55+ ctx -> total_msgs = 0 ;
56+ ctx -> count = 0 ;
57+ }
58+
59+ static void sms_concat_cleanup_work_fn (struct k_work * work )
60+ {
61+ struct k_work_delayable * dwork = CONTAINER_OF (work , struct k_work_delayable , work );
62+ struct sm_sms_context * ctx = CONTAINER_OF (dwork , struct sm_sms_context , cleanup_work );
63+
64+ sms_concat_clear (ctx );
65+ LOG_INF ("Concat msg timed out, ref_number %u" , ctx -> ref_number );
66+ }
67+
68+ static void sms_concat_handle (struct sms_data * const data )
2969{
30- static uint16_t ref_number ;
31- static uint8_t total_msgs ;
32- static uint8_t count ;
33- static char messages [MAX_CONCATENATED_MESSAGE - 1 ][SMS_MAX_PAYLOAD_LEN_CHARS + 1 ];
34- static char rsp_buf [MAX_CONCATENATED_MESSAGE * SMS_MAX_PAYLOAD_LEN_CHARS + 64 ] = {0 };
70+ struct sms_deliver_header * header = & data -> header .deliver ;
71+
72+ LOG_DBG ("Concat msg %d, %d, %d" ,
73+ header -> concatenated .ref_number ,
74+ header -> concatenated .total_msgs ,
75+ header -> concatenated .seq_number );
3576
77+ /* ref_number and total_msgs should remain unchanged.
78+ * If they are different, this is a different concatenated message
79+ * and we discard the current and try with the new message.
80+ * We may end up in changing from one message to another one if two
81+ * concatenated messages are received at the same time in mixed order.
82+ */
83+ if (sms_ctx .ref_number != 0 && sms_ctx .ref_number != header -> concatenated .ref_number ) {
84+ LOG_ERR ("Concat msg ref_number error: %d, %d" ,
85+ sms_ctx .ref_number , header -> concatenated .ref_number );
86+ sms_concat_clear (& sms_ctx );
87+ }
88+ if (sms_ctx .ref_number == 0 ) {
89+ sms_ctx .ref_number = header -> concatenated .ref_number ;
90+
91+ if (header -> concatenated .total_msgs > MAX_CONCATENATED_MESSAGE ) {
92+ LOG_WRN ("Ignoring concat msg with %d messages (max: %d)" ,
93+ header -> concatenated .total_msgs , MAX_CONCATENATED_MESSAGE );
94+ goto done ;
95+ }
96+
97+ /* Allocate buffer for concatenated message. The allocation
98+ * size is an upper boundary as headers and last message part
99+ * are slightly less in practice.
100+ */
101+ uint16_t concat_msg_len =
102+ SM_SMS_AT_HEADER_INFO_MAX_LEN +
103+ SMS_MAX_PAYLOAD_LEN_CHARS * header -> concatenated .total_msgs ;
104+ sms_ctx .concat_rsp_buf = calloc (1 , concat_msg_len );
105+ if (sms_ctx .concat_rsp_buf == NULL ) {
106+ LOG_ERR ("Concat msg no memory for %d bytes, %d messages" ,
107+ concat_msg_len , header -> concatenated .total_msgs );
108+ goto done ;
109+ }
110+ }
111+ if (sms_ctx .total_msgs == 0 ) {
112+ sms_ctx .total_msgs = header -> concatenated .total_msgs ;
113+ }
114+ if (sms_ctx .total_msgs != header -> concatenated .total_msgs ) {
115+ LOG_ERR ("Concat msg total_msgs error: %d, %d" ,
116+ sms_ctx .total_msgs , header -> concatenated .total_msgs );
117+ goto done ;
118+ }
119+ /* seq_number should start with 1 but could arrive in random order */
120+ if (header -> concatenated .seq_number == 0 ||
121+ header -> concatenated .seq_number > sms_ctx .total_msgs ) {
122+ LOG_ERR ("Concat msg seq_number error: %d, %d" ,
123+ header -> concatenated .seq_number , sms_ctx .total_msgs );
124+ goto done ;
125+ }
126+ if (header -> concatenated .seq_number == 1 ) {
127+ sprintf (sms_ctx .concat_rsp_buf ,
128+ "\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d "
129+ "UTC%+03d:%02d\",\"%s\",\"%s\"" ,
130+ header -> time .year , header -> time .month , header -> time .day ,
131+ header -> time .hour , header -> time .minute , header -> time .second ,
132+ header -> time .timezone * 15 / 60 ,
133+ abs (header -> time .timezone ) * 15 % 60 ,
134+ header -> originating_address .address_str ,
135+ data -> payload );
136+ } else {
137+ strcpy (sms_ctx .concat_rsp_buf + SM_SMS_AT_HEADER_INFO_MAX_LEN +
138+ (header -> concatenated .seq_number - 1 ) * SMS_MAX_PAYLOAD_LEN_CHARS ,
139+ data -> payload );
140+ }
141+ sms_ctx .count ++ ;
142+ if (sms_ctx .count == sms_ctx .total_msgs ) {
143+ for (int i = 1 ; i < (sms_ctx .total_msgs ); i ++ ) {
144+ strncat (sms_ctx .concat_rsp_buf ,
145+ sms_ctx .concat_rsp_buf + SM_SMS_AT_HEADER_INFO_MAX_LEN +
146+ i * SMS_MAX_PAYLOAD_LEN_CHARS ,
147+ SMS_MAX_PAYLOAD_LEN_CHARS );
148+ }
149+ strcat (sms_ctx .concat_rsp_buf , "\"\r\n" );
150+ urc_send_to (sms_ctx .pipe , "%s" , sms_ctx .concat_rsp_buf );
151+ } else {
152+ /* If new messages for the concatenated message are not received
153+ * within 3 minutes, discard the concatenated message.
154+ */
155+ (void )k_work_reschedule (& sms_ctx .cleanup_work , MAX_CONCATENATED_MESSAGE_AGE );
156+ return ;
157+ }
158+ done :
159+ sms_concat_clear (& sms_ctx );
160+ }
161+
162+ static void sms_callback (struct sms_data * const data , void * context )
163+ {
36164 ARG_UNUSED (context );
37165
38166 if (data == NULL ) {
39- LOG_WRN ("NULL data" );
40167 return ;
41168 }
42169
43170 if (data -> type == SMS_TYPE_DELIVER ) {
44171 struct sms_deliver_header * header = & data -> header .deliver ;
45172
46173 if (!header -> concatenated .present ) {
47- sprintf (rsp_buf ,
48- "\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d UTC%+03d:%02d\",\"" ,
174+ urc_send_to (sms_ctx .pipe ,
175+ "\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d UTC%+03d:%02d\",\""
176+ "%s\",\"%s\"\r\n" ,
49177 header -> time .year , header -> time .month , header -> time .day ,
50178 header -> time .hour , header -> time .minute , header -> time .second ,
51179 header -> time .timezone * 15 / 60 ,
52- abs (header -> time .timezone ) * 15 % 60 );
53- strcat (rsp_buf , header -> originating_address .address_str );
54- strcat (rsp_buf , "\",\"" );
55- strcat (rsp_buf , data -> payload );
56- strcat (rsp_buf , "\"\r\n" );
57- urc_send_to (sms_pipe , "%s" , rsp_buf );
180+ abs (header -> time .timezone ) * 15 % 60 ,
181+ header -> originating_address .address_str , data -> payload );
58182 } else {
59- LOG_DBG ("concatenated message %d, %d, %d" ,
60- header -> concatenated .ref_number ,
61- total_msgs = header -> concatenated .total_msgs ,
62- header -> concatenated .seq_number );
63- /* ref_number and total_msgs should remain unchanged */
64- if (ref_number == 0 ) {
65- ref_number = header -> concatenated .ref_number ;
66- }
67- if (ref_number != header -> concatenated .ref_number ) {
68- LOG_ERR ("SMS concatenated message ref_number error: %d, %d" ,
69- ref_number , header -> concatenated .ref_number );
70- goto done ;
71- }
72- if (total_msgs == 0 ) {
73- total_msgs = header -> concatenated .total_msgs ;
74- }
75- if (total_msgs != header -> concatenated .total_msgs ) {
76- LOG_ERR ("SMS concatenated message total_msgs error: %d, %d" ,
77- total_msgs , header -> concatenated .total_msgs );
78- goto done ;
79- }
80- if (total_msgs > MAX_CONCATENATED_MESSAGE ) {
81- LOG_ERR ("SMS concatenated message no memory: %d" , total_msgs );
82- goto done ;
83- }
84- /* seq_number should start with 1 but could arrive in random order */
85- if (header -> concatenated .seq_number == 0 ||
86- header -> concatenated .seq_number > total_msgs ) {
87- LOG_ERR ("SMS concatenated message seq_number error: %d, %d" ,
88- header -> concatenated .seq_number , total_msgs );
89- goto done ;
90- }
91- if (header -> concatenated .seq_number == 1 ) {
92- sprintf (rsp_buf ,
93- "\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d "
94- "UTC%+03d:%02d\",\"" ,
95- header -> time .year , header -> time .month , header -> time .day ,
96- header -> time .hour , header -> time .minute , header -> time .second ,
97- header -> time .timezone * 15 / 60 ,
98- abs (header -> time .timezone ) * 15 % 60 );
99- strcat (rsp_buf , header -> originating_address .address_str );
100- strcat (rsp_buf , "\",\"" );
101- strcat (rsp_buf , data -> payload );
102- count ++ ;
103- } else {
104- strcpy (messages [header -> concatenated .seq_number - 2 ],
105- data -> payload );
106- count ++ ;
107- }
108- if (count == total_msgs ) {
109- for (int i = 0 ; i < (total_msgs - 1 ); i ++ ) {
110- strcat (rsp_buf , messages [i ]);
111- }
112- strcat (rsp_buf , "\"\r\n" );
113- urc_send_to (sms_pipe , "%s" , rsp_buf );
114- } else {
115- return ;
116- }
117- done :
118- ref_number = 0 ;
119- total_msgs = 0 ;
120- count = 0 ;
183+ sms_concat_handle (data );
121184 }
122185 } else {
123186 LOG_WRN ("Unknown type: %d" , data -> type );
@@ -128,25 +191,26 @@ static int do_sms_start(void)
128191{
129192 int err = 0 ;
130193
131- if (sms_handle >= 0 ) {
194+ if (sms_ctx . sms_handle >= 0 ) {
132195 /* already registered */
133196 return - EBUSY ;
134197 }
135198
136- sms_handle = sms_register_listener (sms_callback , NULL );
137- if (sms_handle < 0 ) {
138- err = sms_handle ;
139- LOG_ERR ("SMS start error: %d" , err );
140- sms_handle = -1 ;
199+ sms_ctx . sms_handle = sms_register_listener (sms_callback , NULL );
200+ if (sms_ctx . sms_handle < 0 ) {
201+ err = sms_ctx . sms_handle ;
202+ LOG_ERR ("Start error: %d" , err );
203+ sms_ctx . sms_handle = -1 ;
141204 }
142205
143206 return err ;
144207}
145208
146209static int do_sms_stop (void )
147210{
148- sms_unregister_listener (sms_handle );
149- sms_handle = -1 ;
211+ sms_unregister_listener (sms_ctx .sms_handle );
212+ sms_ctx .sms_handle = -1 ;
213+ sms_concat_clear (& sms_ctx );
150214
151215 return 0 ;
152216}
@@ -155,14 +219,14 @@ static int do_sms_send(const char *number, const char *message, uint16_t message
155219{
156220 int err ;
157221
158- if (sms_handle < 0 ) {
159- LOG_ERR ("SMS not registered" );
222+ if (sms_ctx . sms_handle < 0 ) {
223+ LOG_ERR ("Not registered" );
160224 return - EPERM ;
161225 }
162226
163227 err = sms_send (number , message , message_len , SMS_DATA_TYPE_ASCII );
164228 if (err ) {
165- LOG_ERR ("SMS send error: %d" , err );
229+ LOG_ERR ("Send error: %d" , err );
166230 }
167231
168232 return err ;
@@ -183,7 +247,7 @@ static int handle_at_sms(enum at_parser_cmd_type cmd_type, struct at_parser *par
183247 if (op == AT_SMS_STOP ) {
184248 err = do_sms_stop ();
185249 } else if (op == AT_SMS_START ) {
186- sms_pipe = sm_at_host_get_current_pipe ();
250+ sms_ctx . pipe = sm_at_host_get_current_pipe ();
187251 err = do_sms_start ();
188252 } else if (op == AT_SMS_SEND ) {
189253 char number [SMS_MAX_ADDRESS_LEN_CHARS + 1 ];
@@ -201,7 +265,7 @@ static int handle_at_sms(enum at_parser_cmd_type cmd_type, struct at_parser *par
201265 }
202266 err = do_sms_send (number , msg_ptr , size );
203267 } else {
204- LOG_WRN ("Unknown SMS operation: %d" , op );
268+ LOG_WRN ("Unknown operation: %d" , op );
205269 err = - EINVAL ;
206270 }
207271 break ;
0 commit comments