• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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