/* * Author: Chandra Mohan C (willvalab@gmail.com) * All rights reserved. * * Use of this source code is governed by a MIT-style license that can be found * in the LICENSE file. * **-----------------------------------------------*/ #include #include #include #include #include #include "./uartshConfig.h" #include "./uartsh.h" /*-----------------------------------------------*/ #define HISTORY_COUNT (UARTSH_CONFIG_COMMAND_HISTORY_COUNT + 1) #define KEY_BS 0X08 #define KEY_DEL 0X7F #define KEY_ESC_SEQ1 0X1B #define KEY_SPECIAL_UP 'A' #define KEY_SPECIAL_DOWN 'B' #define KEY_SPECIAL_LEFT 'D' #define KEY_SPECIAL_RIGHT 'C' #define KEY_ESC_SEQ2 '[' #define KEY_ESC_SEQ3 '3' #define KEY_ESC_SEQ4 '~' #define FLAG_ESC_SEQ1 1 #define FLAG_ESC_SEQ2 2 #define FLAG_ESC_SEQ3 3 /*-----------------------------------------------*/ struct UartshEditBuffer { int cCount; char buffer[UARTSH_CONFIG_COMMAND_STRING_SIZE]; }; /*-----------------------------------------------*/ #include "hal_api.h" #define UARTSH_DEFAULT_TIMEOUT 60000 #define UARTSH_CMD_TIMEOUT 60000*2 void *g_uartsh_mutex = NULL; static int uart_putchar(sh_cust_type_t *cust_sh, int charx) { return cust_sh->write_func(cust_sh->handler,(unsigned char *)&charx,1,100); } static char uart_getchar(sh_cust_type_t *cust_sh) { char charx=0; int ret; do{ if(cust_sh->run_flag==1){ break; } ret=cust_sh->read_func(cust_sh->handler,(unsigned char *)&charx,1,100); if(ret==1){ //cust_sh->time_out_uptime = hal_os_up_time_ms(); break; } if(ret<0){ hal_os_task_sleep_ms(100); }else{ hal_os_task_sleep_ms(10); } if((cust_sh->timeout_enable)&&(hal_os_up_time_ms() - cust_sh->time_out_uptime > cust_sh->time_out)){ cust_sh->run_flag = 1; break; } }while(ret!=1); return charx; } void uartsh_puts(sh_cust_type_t *cust_sh, char const* s) { if(cust_sh->handler==NULL)return; #if 0 char c = 0; while( (c = *s++) ) { uart_putchar(c); } #else int len = strlen(s); if(s[len-2]!='\r' && s[len-1]== '\n'){ cust_sh->write_func(cust_sh->handler,(unsigned char *)s,strlen(s)-1,1000); cust_sh->write_func(cust_sh->handler,(unsigned char *)"\r\n",2,1000); }else{ cust_sh->write_func(cust_sh->handler,(unsigned char *)s,strlen(s),1000); } #endif } int uartsh_printf(sh_cust_type_t *cust_sh, const char *fmt, ...) { char buf[512]={0}; int len=0; if(cust_sh->handler==NULL)return -1; va_list ap; va_start(ap, fmt); vsnprintf(buf,sizeof(buf)-1,fmt,ap); va_end(ap); len = strlen(buf); if(buf[len-2]!='\r' && buf[len-1]== '\n'){ cust_sh->write_func(cust_sh->handler,(unsigned char *)buf,len-1,1000); uartsh_puts(cust_sh,"\r\n"); }else{ cust_sh->write_func(cust_sh->handler,(unsigned char *)buf,len,1000); } return len; } /*-----------------------------------------------*/ static inline void handleDeleteKey(sh_cust_type_t *cust_sh, struct UartshEditBuffer* pEditBuffer, size_t* pCursor) { unsigned int cursor = *pCursor; if( cursor < pEditBuffer->cCount ) { int b = (pEditBuffer->cCount - cursor); memcpy(&pEditBuffer->buffer[cursor], &pEditBuffer->buffer[cursor + 1], b); pEditBuffer->buffer[--pEditBuffer->cCount] = '\0'; uart_putchar(cust_sh,' '); uart_putchar(cust_sh,'\b'); uartsh_puts(cust_sh,&pEditBuffer->buffer[cursor]); uart_putchar(cust_sh,' '); while(b--) uart_putchar(cust_sh,'\b'); } } /*-----------------------------------------------*/ unsigned int uartsh_gets(sh_cust_type_t *cust_sh, char** input) { static struct UartshEditBuffer editBuffer[HISTORY_COUNT] = { { 0, }, }; static int bufferIndex = 0; static int bufferIndexMax = 0; if( editBuffer[bufferIndex].cCount ) { if( bufferIndexMax < (HISTORY_COUNT - 1) ) bufferIndexMax++; bufferIndex++; if( bufferIndex > bufferIndexMax ) bufferIndex = 0; } struct UartshEditBuffer* pEditBuffer = &editBuffer[bufferIndex]; pEditBuffer->cCount = 0; pEditBuffer->buffer[0] = '\0'; int hi = bufferIndex; size_t cursor = 0; char specialKey = 0; while( 1 ) { char c = (char) uart_getchar(cust_sh); if(cust_sh->run_flag==1){ return -1; } #if (UARTSH_CONFIG_END_CHAR & UARTSH_END_CHAR_CR) if( c == '\r' ) { #if (UARTSH_CONFIG_END_CHAR == UARTSH_END_CHAR_CRLF) c = (char) uart_getchar(cust_sh); if( c != '\n') continue; #endif break; } #elif (UARTSH_CONFIG_END_CHAR & UARTSH_END_CHAR_LF) if( c == '\n' ) { break; } #endif switch( specialKey ) { case 0: { specialKey = 0; switch(c) { case KEY_ESC_SEQ1: { specialKey = FLAG_ESC_SEQ1; } break; case UARTSH_CONFIG_KEY_BACKSPACE: { if( cursor ) { cursor--; int b = (pEditBuffer->cCount - cursor); memcpy(&pEditBuffer->buffer[cursor], &pEditBuffer->buffer[cursor + 1], b); pEditBuffer->buffer[--pEditBuffer->cCount] = '\0'; uart_putchar(cust_sh,'\b'); uartsh_puts(cust_sh,&pEditBuffer->buffer[cursor]); uart_putchar(cust_sh,' '); while(b--) uart_putchar(cust_sh,'\b'); } } break; #if (UARTSH_CONFIG_KEY_BACKSPACE != UARTSH_DELETE_KEY) case KEY_DEL: { handleDeleteKey(cust_sh, pEditBuffer, &cursor ); } break; #endif default: { //only alphabets and special characters allowed if( (c >= 0x20) && (c <= 0x7e) ) { if( pEditBuffer->cCount < (UARTSH_CONFIG_COMMAND_STRING_SIZE - 1) ) { // insertion int i = pEditBuffer->cCount; while( i > cursor ) { pEditBuffer->buffer[i] = pEditBuffer->buffer[i - 1]; i--; } pEditBuffer->buffer[cursor] = c; pEditBuffer->cCount++; pEditBuffer->buffer[pEditBuffer->cCount] = '\0'; uartsh_puts(cust_sh,&pEditBuffer->buffer[cursor]); cursor++; i = (pEditBuffer->cCount - cursor); while(i--) uart_putchar(cust_sh,'\b'); } } } } } break; case FLAG_ESC_SEQ1: { specialKey = 0; if( c == KEY_ESC_SEQ2 ) specialKey = FLAG_ESC_SEQ2; }break; case FLAG_ESC_SEQ2: { specialKey = 0; switch(c) { case KEY_SPECIAL_UP: { while( cursor-- ) uart_putchar(cust_sh,'\b'); int oldCount = pEditBuffer->cCount; hi--; if( hi < 0 ) hi = bufferIndexMax; if( hi == bufferIndex ) { pEditBuffer->cCount = 0; pEditBuffer->buffer[0] = '\0'; } else { pEditBuffer->cCount = editBuffer[hi].cCount; memcpy(pEditBuffer->buffer, editBuffer[hi].buffer, (pEditBuffer->cCount + 1)); } uartsh_puts(cust_sh,pEditBuffer->buffer); cursor = pEditBuffer->cCount; if( oldCount > pEditBuffer->cCount ) { int move = (oldCount - pEditBuffer->cCount); while( move-- ) uart_putchar(cust_sh,' '); move = (oldCount - pEditBuffer->cCount); while( move-- ) uart_putchar(cust_sh,'\b'); } } break; case KEY_SPECIAL_DOWN: { while( cursor-- ) uart_putchar(cust_sh,'\b'); int oldCount = pEditBuffer->cCount; hi++; if( hi > bufferIndexMax ) hi = 0; if( hi == bufferIndex ) { pEditBuffer->cCount = 0; pEditBuffer->buffer[0] = '\0'; } else { pEditBuffer->cCount = editBuffer[hi].cCount; memcpy(pEditBuffer->buffer, editBuffer[hi].buffer, (pEditBuffer->cCount + 1)); } uartsh_puts(cust_sh,pEditBuffer->buffer); cursor = pEditBuffer->cCount; if( oldCount > pEditBuffer->cCount ) { int move = (oldCount - pEditBuffer->cCount); while( move-- ) uart_putchar(cust_sh,' '); move = (oldCount - pEditBuffer->cCount); while( move-- ) uart_putchar(cust_sh,'\b'); } } break; case KEY_SPECIAL_LEFT: { if( cursor ) { uart_putchar(cust_sh,'\b'); cursor--; } } break; case KEY_SPECIAL_RIGHT: { if( cursor < pEditBuffer->cCount ) { uart_putchar(cust_sh,pEditBuffer->buffer[cursor++]); } } break; case KEY_ESC_SEQ3: { specialKey = FLAG_ESC_SEQ3; } break; } }break; case FLAG_ESC_SEQ3: { specialKey = 0; // deletion if( c == KEY_ESC_SEQ4 ) { handleDeleteKey(cust_sh, pEditBuffer, &cursor ); } }break; } //fflush(stdout); } uartsh_puts(cust_sh,"\r\n"); // copy current buffer to next buffer only if current buffer has content if( pEditBuffer->cCount ) { // as caller uses strtok passed buffer will be spoiled // copies current edit buffer's content to next buffer and passes to caller // next call makes use of passed buffer for editing int bi = (bufferIndex + 1); if( bi > (HISTORY_COUNT - 1) ) bi = 0; #if (HISTORY_COUNT >= 2) // as total buffer is 1, no point in copying to itself memcpy(editBuffer[bi].buffer, pEditBuffer->buffer, (pEditBuffer->cCount + 1)); #endif *input = editBuffer[bi].buffer; } return pEditBuffer->cCount; } /*-----------------------------------------------*/ int uartshTokenize(char commandString[], char const tokens[], int argcMax, char* argv[]) { int argc = 0; char* token = strtok(commandString, tokens); while(token != NULL) { argv[argc++] = token; if( argc >= argcMax ) break; token = strtok(NULL, tokens); } return argc; } /*-----------------------------------------------*/ UartshCommandHandler uartshGetHandler(UartshCommand cmdlist[], char command[]) { int commandLength = strlen(command); int index = 0; while( cmdlist[index].name != NULL ) { int appNameSize = strlen(cmdlist[index].name); // extra check to ensure command length is exactly matching app length if( commandLength == appNameSize ) { if( 0 == strncmp(command, cmdlist[index].name, appNameSize) ) { return cmdlist[index].handler; } } index++; } return NULL; } /*-----------------------------------------------*/ void uartsh_thread_cb(void *args) { char* argv[UARTSH_CONFIG_ARGC_MAX] = { 0, }; int argc = 0; int first_flag = 0; sh_cust_type_t *cust_sh = args; UartshCommand *cmdlist = cust_sh->cmd_list; cust_sh->time_out = UARTSH_DEFAULT_TIMEOUT; cust_sh->time_out_uptime = hal_os_up_time_ms(); cust_sh->handler = cust_sh->open_func(cust_sh->port,cust_sh->configbuf); if(!cust_sh->handler){ RLOGD("sh %s open error",cust_sh->name); goto exit; } RLOGD("shell %s runing",cust_sh->name); cust_sh->run_flag = 0; while(cust_sh->run_flag==0) { if(first_flag==1){ //uartsh_puts(cust_sh,"\r\n"); uartsh_printf(cust_sh,"cmd# "); } first_flag=1; char* commandString = NULL; int commandStringLength = 0; if( 0 == (commandStringLength = uartsh_gets(cust_sh,&commandString)) ) continue; if(cust_sh->run_flag==1){ break; } //uartsh_puts(cust_sh,commandString); argc = uartshTokenize(commandString, " \n", UARTSH_CONFIG_ARGC_MAX, argv); uartsh_puts(cust_sh,"\r\n"); size_t commandLength = strlen(argv[0]); if( 1 == argc ) { if( commandLength == 4 ) { if( 0 == strncmp("help", argv[0], 5) ) { if( NULL == cmdlist ) { uartsh_puts(cust_sh,"No commands found\r\n"); continue; } uartsh_puts(cust_sh,"Supported commands are below:\r\n\n"); int index = 0; while( cmdlist[index].name != NULL ) { uartsh_puts(cust_sh," "); uartsh_puts(cust_sh,cmdlist[index++].name); uartsh_puts(cust_sh,"\r\n"); } continue; } else if( 0 == strncmp("exit", argv[0], 4) ){ break; } } } if( NULL == cmdlist ) continue; UartshCommandHandler handler = uartshGetHandler(cmdlist, argv[0]); if( NULL == handler ){ RLOGD("\r\nUNKNOWN CMD\r\n"); uartsh_puts(cust_sh,"\r\nUNKNOWN\r\n"); //break; }else{ RLOGD("\r\nEXEC CMD:%s\r\n", argv[0]); if(g_uartsh_mutex==NULL){ g_uartsh_mutex = hal_os_mutex_create(); } hal_os_mutex_lock(g_uartsh_mutex); int myret = handler(cust_sh,argc, argv); if( myret != 0 ){ uartsh_printf(cust_sh,"\r\nERROR %d\r\n",myret); }else{ uartsh_printf(cust_sh,"\r\nOK\r\n"); } cust_sh->time_out_uptime = hal_os_up_time_ms(); cust_sh->time_out = UARTSH_CMD_TIMEOUT; hal_os_mutex_unlock(g_uartsh_mutex); } hal_os_task_sleep_ms(1); } exit: if(cust_sh->handler){ cust_sh->close_func(cust_sh->handler); } cust_sh->handler=NULL; cust_sh->run_flag =2; RLOGD("shell %s exit!", cust_sh->name); hal_os_task_destroy(&cust_sh->thread); } int uartshInit(sh_cust_type_t *cust_sh) { cust_sh->thread = hal_os_task_create(cust_sh->name,HAL_OS_TASK_PRIORITY_NORMAL,10*1024,uartsh_thread_cb,cust_sh); if(!cust_sh->thread){ RLOGE("shthread thread error"); cust_sh->run_flag = 1; return -1; } return 0; } int uartshExit(sh_cust_type_t *cust_sh) { uint64_t start_ms = hal_os_up_time_ms(); if(cust_sh->run_flag!=0)return -1; cust_sh->run_flag=1; while (cust_sh->run_flag!=2) { if(hal_os_up_time_ms()-start_ms>10000) { cust_sh->run_flag = 1; return -1; } hal_os_task_sleep_ms(10); } cust_sh->run_flag=1; return 0; } int uartshRunning(sh_cust_type_t *cust_sh) { if(cust_sh->run_flag==0 || cust_sh->run_flag==-1){ return 1; } return 0; }