1 /**
2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License. \n
14 *
15 * Description: Provides AT process source \n
16 */
17
18 #include "at_base.h"
19 #include "at_channel.h"
20 #include "at_cmd.h"
21 #include "at_parse.h"
22 #include "at_msg.h"
23 #include "at_notify.h"
24 #include "at_process.h"
25
26 typedef struct {
27 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
28 bool in_progress;
29 bool block_urc;
30 bool allowed_abort;
31 #endif
32 uint16_t channel_id;
33 const at_cmd_entry_t *entry;
34 at_format_t format;
35 at_cmd_type_t type;
36 void *args;
37 } at_control_block_t;
38
39 at_control_block_t g_at_control_block = {0};
40
41 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
42 static at_abort_func_t g_at_default_abort_func = NULL;
43 static at_abort_func_t g_at_abort_func = NULL;
44 static void *g_at_abort_func_arg = NULL;
45 static at_interactivity_func_t g_at_interactivity_func = NULL;
46 #endif
47
at_proc_get_current_channel_id(void)48 uint16_t at_proc_get_current_channel_id(void)
49 {
50 return g_at_control_block.channel_id;
51 }
52
at_proc_perform_command_cmd(void)53 static at_ret_t at_proc_perform_command_cmd(void)
54 {
55 at_ret_t ret;
56 if (g_at_control_block.entry->cmd) {
57 ret = g_at_control_block.entry->cmd();
58 } else {
59 return AT_RET_PROC_CMD_FUNC_MISSING;
60 }
61 return ret;
62 }
63
at_proc_perform_read_cmd(void)64 static at_ret_t at_proc_perform_read_cmd(void)
65 {
66 at_ret_t ret;
67 if (g_at_control_block.entry->read) {
68 ret = g_at_control_block.entry->read();
69 } else {
70 return AT_RET_PROC_READ_FUNC_MISSING;
71 }
72 return ret;
73 }
74
at_proc_perform_test_cmd(void)75 static at_ret_t at_proc_perform_test_cmd(void)
76 {
77 at_ret_t ret;
78 if (g_at_control_block.entry->test) {
79 ret = g_at_control_block.entry->test();
80 } else {
81 return AT_RET_PROC_TEST_FUNC_MISSING;
82 }
83 return ret;
84 }
85
at_proc_perform_set_cmd(void)86 static at_ret_t at_proc_perform_set_cmd(void)
87 {
88 at_ret_t ret;
89 if (g_at_control_block.entry->set) {
90 ret = g_at_control_block.entry->set(g_at_control_block.args);
91 } else {
92 return AT_RET_PROC_SET_FUNC_MISSING;
93 }
94 return ret;
95 }
96
97 #ifdef CONFIG_AT_SUPPORT_QUERY
at_proc_perform_query_cmd(void)98 static at_ret_t at_proc_perform_query_cmd(void)
99 {
100 at_ret_t ret;
101 if (g_at_control_block.entry->query) {
102 ret = g_at_control_block.entry->query(g_at_control_block.args);
103 } else {
104 return AT_RET_PROC_SET_FUNC_MISSING;
105 }
106 return ret;
107 }
108 #endif
109
at_proc_perform_current_cmd(void)110 static at_ret_t at_proc_perform_current_cmd(void)
111 {
112 at_ret_t ret;
113
114 switch (g_at_control_block.type) {
115 case AT_CMD_TYPE_CMD:
116 ret = at_proc_perform_command_cmd();
117 break;
118 case AT_CMD_TYPE_READ:
119 ret = at_proc_perform_read_cmd();
120 break;
121 case AT_CMD_TYPE_TEST:
122 ret = at_proc_perform_test_cmd();
123 break;
124 case AT_CMD_TYPE_SET:
125 ret = at_proc_perform_set_cmd();
126 break;
127 #ifdef CONFIG_AT_SUPPORT_QUERY
128 case AT_CMD_TYPE_QUERY:
129 ret = at_proc_perform_query_cmd();
130 break;
131 #endif
132 default:
133 return AT_RET_CMD_TYPE_ERROR;
134 }
135
136 return ret;
137 }
138
at_proc_para_arguments_finish(void)139 static void at_proc_para_arguments_finish(void)
140 {
141 if (g_at_control_block.args == NULL) {
142 return;
143 }
144
145 at_parse_free_arguments(g_at_control_block.args, g_at_control_block.entry->syntax);
146 at_free(g_at_control_block.args);
147 g_at_control_block.args = NULL;
148 }
149
at_proc_para_arguments_prepare(const char * str)150 static at_ret_t at_proc_para_arguments_prepare(const char *str)
151 {
152 at_ret_t ret;
153 uint32_t struct_max_size;
154
155 #ifdef CONFIG_AT_SUPPORT_QUERY
156 if (((g_at_control_block.type != AT_CMD_TYPE_SET) && (g_at_control_block.type != AT_CMD_TYPE_QUERY))
157 || (g_at_control_block.entry->syntax == NULL)) {
158 #else
159 if ((g_at_control_block.type != AT_CMD_TYPE_SET) || (g_at_control_block.entry->syntax == NULL)) {
160 #endif
161 return AT_RET_OK;
162 }
163
164 struct_max_size = at_cmd_get_max_struct_size();
165 g_at_control_block.args = at_malloc(struct_max_size);
166 if (g_at_control_block.args == NULL) {
167 return AT_RET_MALLOC_ERROR;
168 }
169 memset_s(g_at_control_block.args, struct_max_size, 0, struct_max_size);
170
171 ret = at_parse_para_arguments(str, g_at_control_block.args,
172 g_at_control_block.entry->syntax);
173 if (ret != AT_RET_OK) {
174 at_proc_para_arguments_finish();
175 return ret;
176 }
177 return AT_RET_OK;
178 }
179
180 static at_ret_t at_proc_exec_cmd(const at_cmd_info_t *cmd_info)
181 {
182 at_ret_t ret;
183 uint16_t str_offset = 0;
184
185 g_at_control_block.channel_id = cmd_info->channel_id;
186
187 g_at_control_block.format = at_parse_format_of_cmd(cmd_info, &str_offset);
188 if (g_at_control_block.format == AT_FORMAT_ERROR) {
189 return AT_RET_CMD_FORMAT_ERROR;
190 }
191
192 g_at_control_block.entry = at_cmd_find_entry(cmd_info->cmd_str, &str_offset);
193 if (g_at_control_block.entry == NULL) {
194 return AT_RET_CMD_NO_MATCH;
195 }
196
197 #ifdef CONFIG_AT_SUPPORT_CMD_ATTR
198 if (g_at_control_block.entry->attribute != 0) {
199 if (at_cmd_attr(g_at_control_block.entry->attribute) == false) {
200 return AT_RET_CMD_ATTR_NOT_ALLOW;
201 }
202 }
203 #endif
204
205 g_at_control_block.type = at_parse_cmd_type(cmd_info->cmd_str, &str_offset);
206
207 /* The AT command parameters are transferred by filling the corresponding fields in the parameter structure. */
208 ret = at_proc_para_arguments_prepare(cmd_info->cmd_str + str_offset);
209 if (ret != AT_RET_OK) {
210 return ret;
211 }
212
213 ret = at_proc_perform_current_cmd();
214 at_proc_para_arguments_finish();
215 return ret;
216 }
217
218 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
219 static void at_timer_callback(void *arg)
220 {
221 unused(arg);
222 at_msg_block_t msg;
223 msg.type = AT_CMD_TIMEOUT_MSG;
224 at_msg_send(&msg);
225 }
226
227 static void at_proc_progress_block(uint16_t attribute)
228 {
229 g_at_control_block.in_progress = true;
230 if ((attribute & AT_FLAG_ABORTABLE) != 0) {
231 g_at_control_block.allowed_abort = false;
232 at_timer_start(AT_MAX_TIME_OUT, at_timer_callback, NULL);
233 }
234
235 if ((attribute & AT_FLAG_NOT_BLOCK_URC) == 0) {
236 g_at_control_block.block_urc = true;
237 }
238 }
239
240 errcode_t uapi_at_cmd_default_abort_register(at_abort_func_t func)
241 {
242 if (func == NULL) {
243 return ERRCODE_INVALID_PARAM;
244 }
245 g_at_default_abort_func = func;
246 return ERRCODE_SUCC;
247 }
248
249 errcode_t uapi_at_cmd_abort_register(at_abort_func_t func, void *arg)
250 {
251 if (func == NULL) {
252 return ERRCODE_INVALID_PARAM;
253 }
254 g_at_abort_func = func;
255 g_at_abort_func_arg = arg;
256 return ERRCODE_SUCC;
257 }
258
259 static at_ret_t at_abort_current_cmd(void)
260 {
261 if ((g_at_default_abort_func == NULL) && (g_at_abort_func == NULL)) {
262 return AT_RET_CMD_IN_PROGRESS_BLOCK;
263 }
264
265 at_ret_t ret;
266 if (g_at_abort_func != NULL) {
267 ret = g_at_abort_func(g_at_abort_func_arg);
268 } else {
269 ret = g_at_default_abort_func(NULL);
270 }
271
272 if (ret != AT_RET_OK) {
273 return ret;
274 }
275
276 g_at_control_block.in_progress = false;
277 g_at_control_block.allowed_abort = false;
278 g_at_control_block.block_urc = false;
279 return AT_RET_PROC_ABORT_CURRENT_COMMAND;
280 }
281
282 static at_ret_t at_abort(uint16_t channel_id)
283 {
284 if (g_at_control_block.in_progress == false) {
285 return AT_RET_OK;
286 }
287
288 if (g_at_control_block.allowed_abort == false) {
289 uapi_at_report_to_single_channel(channel_id, AT_RESPONSE_BUSY);
290 return AT_RET_CMD_IN_PROGRESS_BLOCK;
291 }
292
293 uapi_at_report_to_single_channel(channel_id, AT_RESPONSE_ABORTING);
294 return at_abort_current_cmd();
295 }
296
297 errcode_t uapi_at_interactivity_func_register(at_interactivity_func_t func)
298 {
299 if (func == NULL) {
300 return ERRCODE_INVALID_PARAM;
301 }
302 g_at_interactivity_func = func;
303 return ERRCODE_SUCC;
304 }
305
306 static void at_proc_wait_interactivity(void)
307 {
308 uint16_t id = at_proc_get_current_channel_id();
309 g_at_control_block.in_progress = true;
310 g_at_control_block.block_urc = true;
311 at_channel_data_reset(id);
312 at_channel_data_wait_interactivity(id);
313 }
314
315 static void at_proc_release_interactivity(void)
316 {
317 uint16_t id = at_proc_get_current_channel_id();
318 g_at_control_block.in_progress = false;
319 g_at_control_block.block_urc = false;
320 g_at_interactivity_func = NULL;
321 at_channel_data_reset(id);
322 }
323 #endif
324
325 static bool at_proc_need_exit(at_ret_t result)
326 {
327 switch (result) {
328 case AT_RET_OK:
329 uapi_at_report(AT_RESPONSE_OK);
330 break;
331 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
332 case AT_RET_CMD_IN_PROGRESS_BLOCK:
333 at_proc_progress_block(g_at_control_block.entry->attribute);
334 return true;
335 case AT_RET_PROC_WAIT_INTERACTIVITY:
336 at_proc_wait_interactivity();
337 return true;
338 #endif
339 default:
340 uapi_at_report(AT_RESPONSE_ERROR);
341 break;
342 }
343 return false;
344 }
345
346 static void at_proc_cmd_process(void)
347 {
348 at_ret_t ret = AT_RET_OK;
349 at_cmd_info_t *cmd_info;
350
351 cmd_info = at_parse_get_next_remain_cmd();
352 while (cmd_info != NULL) {
353 ret = at_proc_exec_cmd(cmd_info);
354 at_parse_del_one_remain_cmd(cmd_info);
355 if (at_proc_need_exit(ret) == true) {
356 break;
357 }
358 cmd_info = at_parse_get_next_remain_cmd();
359 }
360 }
361
362 at_ret_t at_proc_cmd_handle(uint16_t channel_id)
363 {
364 at_ret_t ret = AT_RET_OK;
365 char *str = NULL;
366
367 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
368 ret = at_abort(channel_id);
369 if (ret != AT_RET_OK) {
370 at_channel_data_reset(channel_id);
371 return ret;
372 }
373 #endif
374
375 str = (char*)at_channel_get_data(channel_id);
376 if (str == NULL) {
377 at_channel_data_reset(channel_id);
378 return AT_RET_CHANNEL_DATA_NULL;
379 }
380 at_log_normal(str, strlen(str), 0);
381
382 ret = at_parse_split_combine_cmd(str, (uint32_t)strlen(str), channel_id);
383 at_channel_data_reset(channel_id);
384 if (ret != AT_RET_OK) {
385 return ret;
386 }
387
388 at_proc_cmd_process();
389 return ret;
390 }
391
392 #ifdef CONFIG_AT_SUPPORT_NOTIFY_REPORT
393 void at_proc_cmd_urc_handle(void)
394 {
395 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
396 if (g_at_control_block.block_urc == true) {
397 return;
398 }
399 #endif
400 at_notify_process_list();
401 }
402 #endif
403
404 #ifdef CONFIG_AT_SUPPORT_ASYNCHRONOUS
405 static at_ret_t at_proc_interactivity_process(uint16_t channel_id, char *str, uint32_t len)
406 {
407 if (g_at_interactivity_func == NULL) {
408 at_proc_release_interactivity();
409 return AT_RET_OK;
410 }
411
412 at_ret_t ret = g_at_interactivity_func(str, len);
413 if (ret == AT_RET_PROC_WAIT_INTERACTIVITY) {
414 at_proc_wait_interactivity();
415 return ret;
416 }
417
418 if (ret != AT_RET_OK) {
419 uapi_at_report_to_single_channel(channel_id, AT_RESPONSE_ERROR);
420 }
421 at_proc_release_interactivity();
422 return ret;
423 }
424
425 at_ret_t at_proc_interactivity_handle(uint16_t channel_id)
426 {
427 at_ret_t ret = AT_RET_OK;
428 char *str = NULL;
429
430 str = (char*)at_channel_get_data(channel_id);
431 if (str == NULL) {
432 at_proc_release_interactivity();
433 return AT_RET_CHANNEL_DATA_NULL;
434 }
435
436 ret = at_proc_interactivity_process(channel_id, str, strlen(str));
437 return ret;
438 }
439
440 static void at_proc_at_remain_task_process(void)
441 {
442 at_proc_cmd_process();
443 #ifdef CONFIG_AT_SUPPORT_NOTIFY_REPORT
444 at_proc_cmd_urc_handle();
445 #endif
446 }
447
448 at_ret_t at_proc_cmd_result_handle(uint16_t result)
449 {
450 if (g_at_control_block.entry == NULL) {
451 return AT_RET_OK;
452 }
453 at_timer_delete();
454 g_at_control_block.in_progress = false;
455 g_at_control_block.block_urc = false;
456
457 if (result == 0) {
458 uapi_at_report(AT_RESPONSE_OK);
459 } else {
460 uapi_at_report(AT_RESPONSE_ERROR);
461 }
462
463 at_proc_at_remain_task_process();
464 return AT_RET_OK;
465 }
466
467 at_ret_t at_proc_timeout_handle(void)
468 {
469 at_timer_delete();
470 /* This means that even though the AT command execution times out,
471 it will not be interrupted when there are no other AT commands. */
472 if (at_parse_has_remain_cmd() != true) {
473 g_at_control_block.allowed_abort = true;
474 return AT_RET_ABORT_DELAY;
475 }
476
477 at_ret_t ret = at_abort_current_cmd();
478 if (ret != AT_RET_PROC_ABORT_CURRENT_COMMAND) {
479 return ret;
480 }
481
482 at_proc_at_remain_task_process();
483 return ret;
484 }
485 #endif
486