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 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 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 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 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 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 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 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 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 } 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 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