1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2018-04-02 armink first version
9 */
10
11 #include <at.h>
12 #include <stdio.h>
13 #include <string.h>
14
15 #include <rtthread.h>
16 #include <rtdevice.h>
17 #include <rthw.h>
18
19 #ifdef AT_USING_CLI
20
21 #define AT_CLI_FIFO_SIZE 256
22
23 static struct rt_semaphore console_rx_notice;
24 static struct rt_ringbuffer *console_rx_fifo = RT_NULL;
25 static rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL;
26
27 #ifdef AT_USING_CLIENT
28 static struct rt_semaphore client_rx_notice;
29 static struct rt_ringbuffer *client_rx_fifo = RT_NULL;
30 #endif
31
console_getchar(void)32 static char console_getchar(void)
33 {
34 char ch;
35
36 rt_sem_take(&console_rx_notice, RT_WAITING_FOREVER);
37 rt_ringbuffer_getchar(console_rx_fifo, (rt_uint8_t *)&ch);
38
39 return ch;
40 }
41
console_getchar_rx_ind(rt_device_t dev,rt_size_t size)42 static rt_err_t console_getchar_rx_ind(rt_device_t dev, rt_size_t size)
43 {
44 uint8_t ch;
45 rt_size_t i;
46
47 for (i = 0; i < size; i++)
48 {
49 /* read a char */
50 if (rt_device_read(dev, 0, &ch, 1))
51 {
52 rt_ringbuffer_put_force(console_rx_fifo, &ch, 1);
53 rt_sem_release(&console_rx_notice);
54 }
55 }
56
57 return RT_EOK;
58 }
59
at_cli_init(void)60 void at_cli_init(void)
61 {
62 rt_base_t int_lvl;
63 rt_device_t console;
64
65 rt_sem_init(&console_rx_notice, "cli_c", 0, RT_IPC_FLAG_FIFO);
66
67 /* create RX FIFO */
68 console_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
69 /* created must success */
70 RT_ASSERT(console_rx_fifo);
71
72 int_lvl = rt_hw_interrupt_disable();
73 console = rt_console_get_device();
74 if (console)
75 {
76 /* backup RX indicate */
77 odev_rx_ind = console->rx_indicate;
78 rt_device_set_rx_indicate(console, console_getchar_rx_ind);
79 }
80
81 rt_hw_interrupt_enable(int_lvl);
82 }
83
at_cli_deinit(void)84 void at_cli_deinit(void)
85 {
86 rt_base_t int_lvl;
87 rt_device_t console;
88
89 int_lvl = rt_hw_interrupt_disable();
90 console = rt_console_get_device();
91 if (console && odev_rx_ind)
92 {
93 /* restore RX indicate */
94 rt_device_set_rx_indicate(console, odev_rx_ind);
95 }
96 rt_hw_interrupt_enable(int_lvl);
97
98 rt_sem_detach(&console_rx_notice);
99 rt_ringbuffer_destroy(console_rx_fifo);
100 }
101
102 #ifdef AT_USING_SERVER
server_cli_parser(void)103 static void server_cli_parser(void)
104 {
105 extern at_server_t at_get_server(void);
106
107 at_server_t server = at_get_server();
108 rt_base_t int_lvl;
109 static rt_device_t device_bak;
110 static char (*getchar_bak)(void);
111 static char endmark_back[AT_END_MARK_LEN];
112
113 /* backup server device and getchar function */
114 {
115 int_lvl = rt_hw_interrupt_disable();
116
117 device_bak = server->device;
118 getchar_bak = server->get_char;
119
120 memset(endmark_back, 0x00, AT_END_MARK_LEN);
121 memcpy(endmark_back, server->end_mark, strlen(server->end_mark));
122
123 /* setup server device as console device */
124 server->device = rt_console_get_device();
125 server->get_char = console_getchar;
126
127 memset(server->end_mark, 0x00, AT_END_MARK_LEN);
128 server->end_mark[0] = '\r';
129
130 rt_hw_interrupt_enable(int_lvl);
131 }
132
133 if (server)
134 {
135 rt_kprintf("======== Welcome to using RT-Thread AT command server cli ========\n");
136 rt_kprintf("Input your at command for test server. Press 'ESC' to exit.\n");
137 server->parser_entry(server);
138 }
139 else
140 {
141 rt_kprintf("AT client not initialized\n");
142 }
143
144 /* restore server device and getchar function */
145 {
146 int_lvl = rt_hw_interrupt_disable();
147
148 server->device = device_bak;
149 server->get_char = getchar_bak;
150
151 memset(server->end_mark, 0x00, AT_END_MARK_LEN);
152 memcpy(server->end_mark, endmark_back, strlen(endmark_back));
153
154 rt_hw_interrupt_enable(int_lvl);
155 }
156 }
157 #endif /* AT_USING_SERVER */
158
159 #ifdef AT_USING_CLIENT
client_getchar(void)160 static char client_getchar(void)
161 {
162 char ch;
163
164 rt_sem_take(&client_rx_notice, RT_WAITING_FOREVER);
165 rt_ringbuffer_getchar(client_rx_fifo, (rt_uint8_t *)&ch);
166
167 return ch;
168 }
169
at_client_entry(void * param)170 static void at_client_entry(void *param)
171 {
172 char ch;
173
174 while(1)
175 {
176 ch = client_getchar();
177 rt_kprintf("%c", ch);
178 }
179 }
180
client_getchar_rx_ind(rt_device_t dev,rt_size_t size)181 static rt_err_t client_getchar_rx_ind(rt_device_t dev, rt_size_t size)
182 {
183 uint8_t ch;
184 rt_size_t i;
185
186 for (i = 0; i < size; i++)
187 {
188 /* read a char */
189 if (rt_device_read(dev, 0, &ch, 1))
190 {
191 rt_ringbuffer_put_force(client_rx_fifo, &ch, 1);
192 rt_sem_release(&client_rx_notice);
193 }
194 }
195
196 return RT_EOK;
197 }
client_cli_parser(at_client_t client)198 static void client_cli_parser(at_client_t client)
199 {
200 #define ESC_KEY 0x1B
201 #define BACKSPACE_KEY 0x08
202 #define DELECT_KEY 0x7F
203
204 char ch;
205 char cur_line[FINSH_CMD_SIZE] = { 0 };
206 rt_size_t cur_line_len = 0;
207 static rt_err_t (*client_odev_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL;
208 rt_base_t int_lvl;
209 rt_thread_t at_client;
210
211 if (client)
212 {
213 /* backup client device RX indicate */
214 {
215 int_lvl = rt_hw_interrupt_disable();
216 client_odev_rx_ind = client->device->rx_indicate;
217 rt_device_set_rx_indicate(client->device, client_getchar_rx_ind);
218 rt_hw_interrupt_enable(int_lvl);
219 }
220
221 rt_sem_init(&client_rx_notice, "cli_r", 0, RT_IPC_FLAG_FIFO);
222 client_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
223
224 at_client = rt_thread_create("at_cli", at_client_entry, RT_NULL, 512, 8, 8);
225 if (client_rx_fifo && at_client)
226 {
227 rt_kprintf("======== Welcome to using RT-Thread AT command client cli ========\n");
228 rt_kprintf("Cli will forward your command to server port(%s). Press 'ESC' to exit.\n", client->device->parent.name);
229 rt_thread_startup(at_client);
230 /* process user input */
231 while (ESC_KEY != (ch = console_getchar()))
232 {
233 if (ch == BACKSPACE_KEY || ch == DELECT_KEY)
234 {
235 if (cur_line_len)
236 {
237 cur_line[--cur_line_len] = 0;
238 rt_kprintf("\b \b");
239 }
240 continue;
241 }
242 else if (ch == '\r' || ch == '\n')
243 {
244 /* execute a AT request */
245 if (cur_line_len)
246 {
247 rt_kprintf("\n");
248 at_obj_exec_cmd(client, RT_NULL, "%.*s", cur_line_len, cur_line);
249 }
250 cur_line_len = 0;
251 }
252 else
253 {
254 rt_kprintf("%c", ch);
255 cur_line[cur_line_len++] = ch;
256 }
257 }
258
259 /* restore client device RX indicate */
260 {
261 int_lvl = rt_hw_interrupt_disable();
262 rt_device_set_rx_indicate(client->device, client_odev_rx_ind);
263 rt_hw_interrupt_enable(int_lvl);
264 }
265
266 rt_thread_delete(at_client);
267 rt_sem_detach(&client_rx_notice);
268 rt_ringbuffer_destroy(client_rx_fifo);
269 }
270 else
271 {
272 rt_kprintf("No mem for AT cli client\n");
273 }
274 }
275 else
276 {
277 rt_kprintf("AT client not initialized\n");
278 }
279 }
280 #endif /* AT_USING_CLIENT */
281
at(int argc,char ** argv)282 static void at(int argc, char **argv)
283 {
284
285 if (argc != 2 && argc != 3)
286 {
287 rt_kprintf("Please input '<server|client [dev_name]>' \n");
288 return;
289 }
290
291 at_cli_init();
292
293 if (!strcmp(argv[1], "server"))
294 {
295 #ifdef AT_USING_SERVER
296 server_cli_parser();
297 #else
298 rt_kprintf("Not support AT server, please check your configure!\n");
299 #endif /* AT_USING_SERVER */
300 }
301 else if (!strcmp(argv[1], "client"))
302 {
303 #ifdef AT_USING_CLIENT
304 at_client_t client = RT_NULL;
305
306 if (argc == 2)
307 {
308 client_cli_parser(at_client_get_first());
309 }
310 else if (argc == 3)
311 {
312 client = at_client_get(argv[2]);
313 if (client == RT_NULL)
314 {
315 rt_kprintf("input AT client device name(%s) error.\n", argv[2]);
316 }
317 else
318 {
319 client_cli_parser(client);
320 }
321 }
322 #else
323 rt_kprintf("Not support AT client, please check your configure!\n");
324 #endif /* AT_USING_CLIENT */
325 }
326 else
327 {
328 rt_kprintf("Please input '<server|client [dev_name]>' \n");
329 }
330
331 at_cli_deinit();
332 }
333 MSH_CMD_EXPORT(at, RT-Thread AT component cli: at <server|client [dev_name]>);
334
335 #endif /* AT_USING_CLI */
336