/** * \file * \brief Radio/remote Standard I/O module. * \author (c) 2013-2014 Erich Styger, http://mcuoneclipse.com/ * \note MIT License (http://opensource.org/licenses/mit-license.html), see 'RNet_License.txt' * * This module uses queues for remote/radio standard I/O handling. */ #include "McuRNetConfig.h" #if McuRNET_CONFIG_IS_ENABLED #include "RStdIO.h" #include "McuRTOS.h" #include "McuUtility.h" #include "McuShell.h" #include "Radio.h" #include "RMSG.h" #include "RNWK.h" #include "RApp.h" /* three shell queues, one for each channel. * E.g. the command "radio send stdin help" will place "help" into the RSTDIO_RxStdIn queue, which then will be processed by the shell. * */ static QueueHandle_t RSTDIO_RxStdInQ, RSTDIO_RxStdOutQ, RSTDIO_RxStdErrQ; static QueueHandle_t RSTDIO_TxStdInQ, RSTDIO_TxStdOutQ, RSTDIO_TxStdErrQ; #define RSTDIO_PAYLOAD_SIZE (RNWK_PAYLOAD_SIZE-1/*type*/-1/*size*/) /* data size we can transmit in one message. stdout string will be added */ #define RSTDIO_QUEUE_LENGTH RSTDIO_CONFIG_QUEUE_LENGTH /* items in queue, that's my buffer size */ #define RSTDIO_QUEUE_ITEM_SIZE 1 /* each item is a single character */ #define RSTDIO_QUEUE_TIMEOUT_MS 500 /* timeout for stdio queues */ static RNWK_ShortAddrType RSTDIO_dstAddr; /* destination address */ uint8_t RSTDIO_DefaultShellBuffer[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; /* default buffer which can be used by the application */ void RSTDIO_SetDestinationAddress(RNWK_ShortAddrType addr) { RSTDIO_dstAddr = addr; } /*! * \brief Returns a queue handle for a Remote Standard I/O type * \param queueType Type of queue * \return Queue handle */ QueueHandle_t RSTDIO_GetQueueForType(RSTDIO_QueueType queueType) { switch(queueType) { case RSTDIO_QUEUE_RX_IN: return RSTDIO_RxStdInQ; case RSTDIO_QUEUE_RX_OUT: return RSTDIO_RxStdOutQ; case RSTDIO_QUEUE_RX_ERR: return RSTDIO_RxStdErrQ; case RSTDIO_QUEUE_TX_IN: return RSTDIO_TxStdInQ; case RSTDIO_QUEUE_TX_OUT: return RSTDIO_TxStdOutQ; case RSTDIO_QUEUE_TX_ERR: return RSTDIO_TxStdErrQ; default: break; } return NULL; } /*! * \brief Adds binary data to the queue. * \param queue Queue to be used * \param data Pointer to the binary data * \param dataSize Size of data in bytes */ static uint8_t AddToQueue(QueueHandle_t queue, const unsigned char *data, size_t dataSize) { while(dataSize!=0) { if (McuRTOS_xQueueSendToBack(queue, data, pdMS_TO_TICKS(RSTDIO_QUEUE_TIMEOUT_MS))!=pdPASS) { return ERR_FAULT; } data++; dataSize--; } return ERR_OK; } uint8_t RSTDIO_AddToQueue(RSTDIO_QueueType queueType, const unsigned char *data, size_t dataSize) { switch(queueType) { case RSTDIO_QUEUE_RX_IN: return AddToQueue(RSTDIO_RxStdInQ, data, dataSize); case RSTDIO_QUEUE_RX_OUT: return AddToQueue(RSTDIO_RxStdOutQ, data, dataSize); case RSTDIO_QUEUE_RX_ERR: return AddToQueue(RSTDIO_RxStdErrQ, data, dataSize); case RSTDIO_QUEUE_TX_IN: return AddToQueue(RSTDIO_TxStdInQ, data, dataSize); case RSTDIO_QUEUE_TX_OUT: return AddToQueue(RSTDIO_TxStdOutQ, data, dataSize); case RSTDIO_QUEUE_TX_ERR: return AddToQueue(RSTDIO_TxStdErrQ, data, dataSize); default: break; } return ERR_FAILED; } /*! * \brief Peeks a character from the queue without removing it. * \param queue Queue to be used * \return '\0' if the queue is empty, otherwise it contains the character. */ static unsigned short RSTDIO_NofElements(QueueHandle_t queue) { return (unsigned short)McuRTOS_uxQueueMessagesWaiting(queue); } /*! * \brief Returns the number of elements in the queue * \param queueType Queue type * \return Number of elements in the queue, 0 if the queue is not known. */ uint8_t RSTDIO_NofInQueue(RSTDIO_QueueType queueType) { switch(queueType) { case RSTDIO_QUEUE_RX_IN: return RSTDIO_NofElements(RSTDIO_RxStdInQ); case RSTDIO_QUEUE_RX_OUT: return RSTDIO_NofElements(RSTDIO_RxStdOutQ); case RSTDIO_QUEUE_RX_ERR: return RSTDIO_NofElements(RSTDIO_RxStdErrQ); case RSTDIO_QUEUE_TX_IN: return RSTDIO_NofElements(RSTDIO_TxStdInQ); case RSTDIO_QUEUE_TX_OUT: return RSTDIO_NofElements(RSTDIO_TxStdOutQ); case RSTDIO_QUEUE_TX_ERR: return RSTDIO_NofElements(RSTDIO_TxStdErrQ); default: break; } return 0; } /*! * \brief Receives an a message character from the queue, and returns immediately if the queue is empty. * \return Message character, or '\0' if there was no message. */ static unsigned char RSTDIO_ReceiveChar(QueueHandle_t queue) { unsigned char ch; portBASE_TYPE res; res = McuRTOS_xQueueReceive(queue, &ch, 0); if (res==errQUEUE_EMPTY) { return '\0'; } else { return ch; } } /*! * \brief Returns a character from a queue * \param queueType Queue type * \return Character, or zero byte if queue is empty or unknown */ uint8_t RSTDIO_ReceiveQueueChar(RSTDIO_QueueType queueType) { switch(queueType) { case RSTDIO_QUEUE_RX_IN: return RSTDIO_ReceiveChar(RSTDIO_RxStdInQ); case RSTDIO_QUEUE_RX_OUT: return RSTDIO_ReceiveChar(RSTDIO_RxStdOutQ); case RSTDIO_QUEUE_RX_ERR: return RSTDIO_ReceiveChar(RSTDIO_RxStdErrQ); case RSTDIO_QUEUE_TX_IN: return RSTDIO_ReceiveChar(RSTDIO_TxStdInQ); case RSTDIO_QUEUE_TX_OUT: return RSTDIO_ReceiveChar(RSTDIO_TxStdOutQ); case RSTDIO_QUEUE_TX_ERR: return RSTDIO_ReceiveChar(RSTDIO_TxStdErrQ); default: break; } return '\0'; } uint8_t RSTDIO_HandleStdioRxMessage(RAPP_MSG_Type type, uint8_t size, uint8_t *data, RNWK_ShortAddrType srcAddr, bool *handled, RPHY_PacketDesc *packet) { (void)srcAddr; (void)packet; switch(type) { case RAPP_MSG_TYPE_STDIN: /* */ *handled = TRUE; return RSTDIO_AddToQueue(RSTDIO_QUEUE_RX_IN, data, size); case RAPP_MSG_TYPE_STDOUT: /* */ *handled = TRUE; return RSTDIO_AddToQueue(RSTDIO_QUEUE_RX_OUT, data, size); case RAPP_MSG_TYPE_STDERR: /* */ *handled = TRUE; return RSTDIO_AddToQueue(RSTDIO_QUEUE_RX_ERR, data, size); default: break; } /* switch */ return ERR_OK; } /*! * \brief Flushes the given buffer/queue. The buffer will be sent over the radio. * \return Error code, ERR_OK for no error. */ static uint8_t FlushAndTxQueue(RSTDIO_QueueType queueType, RAPP_MSG_Type msgType) { unsigned char buf[RAPP_BUFFER_SIZE]; uint8_t i; uint8_t res = ERR_OK; uint8_t *p, ch; p = RAPP_BUF_PAYLOAD_START(buf); i = 0; while (istdErr('*'); io->stdErr('F'); io->stdErr('A'); io->stdErr('I'); io->stdErr('L'); io->stdErr('*'); io->stdErr('\n'); return res; } return res; } /*! * \brief Radio StdIn: sends a character to the wireless communication channel. * \param ch Character to send */ static void RSTDIO_TxStdInSendChar(uint8_t ch) { (void)RSTDIO_AddToQueue(RSTDIO_QUEUE_TX_IN, &ch, 1); if (ch=='\n' || RSTDIO_NofInQueue(RSTDIO_QUEUE_TX_IN)>RSTDIO_PAYLOAD_SIZE) { /* can send string over radio */ (void)FlushAndTxQueue(RSTDIO_QUEUE_TX_IN, RAPP_MSG_TYPE_STDIN); } } /*! * \brief Radio StdOut: sends a character to the wireless communication channel. * \param ch Character to send */ static void RSTDIO_TxStdOut(uint8_t ch) { (void)RSTDIO_AddToQueue(RSTDIO_QUEUE_TX_OUT, &ch, 1); if (ch=='\n' || RSTDIO_NofInQueue(RSTDIO_QUEUE_TX_OUT)>RSTDIO_PAYLOAD_SIZE) { /* can send string over radio */ (void)FlushAndTxQueue(RSTDIO_QUEUE_TX_OUT, RAPP_MSG_TYPE_STDOUT); } } /*! * \brief Radio StdErr: sends a character to the wireless communication channel. * \param ch Character to send */ static void RSTDIO_TxStdErr(uint8_t ch) { (void)RSTDIO_AddToQueue(RSTDIO_QUEUE_TX_ERR, &ch, sizeof(ch)); if (ch=='\n' || RSTDIO_NofInQueue(RSTDIO_QUEUE_TX_ERR)>RSTDIO_PAYLOAD_SIZE) { /* can send string over radio */ (void)FlushAndTxQueue(RSTDIO_QUEUE_TX_ERR, RAPP_MSG_TYPE_STDERR); } } uint8_t RSTDIO_SendToTxStdio(RSTDIO_QueueType queueType, uint8_t *buf, size_t bufSize) { if (queueType==RSTDIO_QUEUE_TX_IN) { while(bufSize!=0) { RSTDIO_TxStdInSendChar(*buf); buf++; bufSize--; } } else if (queueType==RSTDIO_QUEUE_TX_OUT) { while(bufSize!=0) { RSTDIO_TxStdOut(*buf); buf++; bufSize--; } } else if (queueType==RSTDIO_QUEUE_TX_ERR) { while(bufSize!=0) { RSTDIO_TxStdErr(*buf); buf++; bufSize--; } } else { return ERR_FAILED; /* unknown queue? */ } return ERR_OK; } /*! * \brief Radio StdIn: Reads a character from the StdIn queue and echoes it on the standard shell stdOut */ static void RSTDIO_RxStdInReadChar(uint8_t *ch) { *ch = RSTDIO_ReceiveQueueChar(RSTDIO_QUEUE_RX_IN); /* will return '\0' if queue is empty */ } /*! * \brief Checks if we have a character in the stdin queue without removing it. * \return TRUE if character is present. */ static bool RSTDIO_RxStdInKeyPressed(void) { return (bool)(RSTDIO_NofElements(RSTDIO_RxStdInQ)!=0); } McuShell_ConstStdIOType RSTDIO_stdio = { .stdIn = (McuShell_StdIO_In_FctType)RSTDIO_RxStdInReadChar, /* stdin */ .stdOut = (McuShell_StdIO_OutErr_FctType)RSTDIO_TxStdOut, /* stdout */ .stdErr = (McuShell_StdIO_OutErr_FctType)RSTDIO_TxStdErr, /* stderr */ .keyPressed = RSTDIO_RxStdInKeyPressed, /* if input is not empty */ #if McuShell_CONFIG_ECHO_ENABLED .echoEnabled = false, #endif }; McuShell_ConstStdIOTypePtr RSTDIO_GetStdio(void) { return &RSTDIO_stdio; } /*! * \brief Called from the application task. This function checks the Radio RX queue and checks if it contains stdio messages. * If so, it dispatches it to the corresponding shell queues. */ void RSTDIO_Print(McuShell_ConstStdIOTypePtr io) { unsigned char ch; for(;;) { /* breaks */ ch = RSTDIO_ReceiveChar(RSTDIO_RxStdOutQ); if(ch=='\0') { break; /* get out of for loop */ } io->stdOut(ch); /* output character */ } for(;;) { /* breaks */ ch = RSTDIO_ReceiveChar(RSTDIO_RxStdErrQ); if(ch=='\0') { break; /* get out of for loop */ } io->stdErr(ch); /* output character */ } } unsigned int RSTDIO_AddIntoBuffer(unsigned char *buffer, size_t bufSize) { unsigned char ch; unsigned int nofChars = 0; for(;;) { /* breaks */ ch = RSTDIO_ReceiveChar(RSTDIO_RxStdOutQ); if(ch=='\0') { break; /* get out of for loop */ } McuUtility_chcat(buffer, bufSize, ch); nofChars++; } for(;;) { /* breaks */ ch = RSTDIO_ReceiveChar(RSTDIO_RxStdErrQ); if(ch=='\0') { break; /* get out of for loop */ } McuUtility_chcat(buffer, bufSize, ch); nofChars++; } return nofChars; } static void PrintHelp(const McuShell_StdIOType *io) { McuShell_SendHelpStr((unsigned char*)"rstdio", (unsigned char*)"Group of rstdio commands\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows help or status\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" daddr 0x", (unsigned char*)"Set destination node address\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" send (in/out/err)", (unsigned char*)"Send a string to remote stdio\r\n", io->stdOut); } static uint8_t PrintStatus(const McuShell_StdIOType *io) { uint8_t buf[32]; McuShell_SendStatusStr((unsigned char*)"rstdio", (unsigned char*)"\r\n", io->stdOut); McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x"); #if RNWK_SHORT_ADDR_SIZE==1 McuUtility_strcatNum8Hex(buf, sizeof(buf), RSTDIO_dstAddr); #else McuUtility_strcatNum16Hex(buf, sizeof(buf), RSTDIO_dstAddr); #endif McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" dest addr", buf, io->stdOut); McuUtility_Num8uToStr(buf, sizeof(buf), RSTDIO_QUEUE_LENGTH); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" queue size", buf, io->stdOut); McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"in: "); McuUtility_strcatNum8u(buf, sizeof(buf), RSTDIO_NofInQueue(RSTDIO_QUEUE_RX_IN)); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", out: "); McuUtility_strcatNum8u(buf, sizeof(buf), RSTDIO_NofInQueue(RSTDIO_QUEUE_RX_OUT)); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", err: "); McuUtility_strcatNum8u(buf, sizeof(buf), RSTDIO_NofInQueue(RSTDIO_QUEUE_RX_ERR)); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" Rx queue", buf, io->stdOut); McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"in: "); McuUtility_strcatNum8u(buf, sizeof(buf), RSTDIO_NofInQueue(RSTDIO_QUEUE_TX_IN)); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", out: "); McuUtility_strcatNum8u(buf, sizeof(buf), RSTDIO_NofInQueue(RSTDIO_QUEUE_TX_OUT)); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", err: "); McuUtility_strcatNum8u(buf, sizeof(buf), RSTDIO_NofInQueue(RSTDIO_QUEUE_TX_ERR)); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); McuShell_SendStatusStr((unsigned char*)" Tx queue", buf, io->stdOut); return ERR_OK; } uint8_t RSTDIO_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) { uint8_t res = ERR_OK; const uint8_t *p; uint16_t val16; if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"rstdio help")==0) { PrintHelp(io); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"rstdio status")==0) { *handled = TRUE; return PrintStatus(io); } else if (McuUtility_strncmp((char*)cmd, (char*)"rstdio send", sizeof("rstdio send")-1)==0) { unsigned char buf[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; RSTDIO_QueueType queue; if (McuUtility_strncmp((char*)cmd, (char*)"rstdio send in", sizeof("rstdio send in")-1)==0) { queue = RSTDIO_QUEUE_TX_IN; cmd += sizeof("rstdio send in"); } else if (McuUtility_strncmp((char*)cmd, (char*)"rstdio send out", sizeof("rstdio send out")-1)==0) { queue = RSTDIO_QUEUE_TX_OUT; cmd += sizeof("rstdio send out"); } else if (McuUtility_strncmp((char*)cmd, (char*)"rstdio send err", sizeof("rstdio send err")-1)==0) { queue = RSTDIO_QUEUE_TX_ERR; cmd += sizeof("rstdio send err"); } else { return ERR_OK; /* not handled */ } McuUtility_strcpy(buf, sizeof(buf), cmd); McuUtility_chcat(buf, sizeof(buf), '\n'); buf[sizeof(buf)-2] = '\n'; /* have a '\n' in any case */ if (RSTDIO_SendToTxStdio(queue, buf, McuUtility_strlen((char*)buf))!=ERR_OK) { McuShell_SendStr((unsigned char*)"failed!\r\n", io->stdErr); } *handled = TRUE; } else if (McuUtility_strncmp((char*)cmd, (char*)"rstdio daddr", sizeof("rstdio daddr")-1)==0) { p = cmd + sizeof("rstdio daddr")-1; *handled = TRUE; if (McuUtility_ScanHex16uNumber(&p, &val16)==ERR_OK) { RSTDIO_dstAddr = val16; } else { McuShell_SendStr((unsigned char*)"ERR: wrong address\r\n", io->stdErr); return ERR_FAILED; } } return res; } /*! \brief Deinitializes the queue module */ void RSTDIO_Deinit(void) { McuRTOS_vQueueUnregisterQueue(RSTDIO_RxStdInQ); McuRTOS_vQueueDelete(RSTDIO_RxStdInQ); RSTDIO_RxStdInQ = NULL; McuRTOS_vQueueUnregisterQueue(RSTDIO_RxStdOutQ); McuRTOS_vQueueDelete(RSTDIO_RxStdOutQ); RSTDIO_RxStdOutQ = NULL; McuRTOS_vQueueUnregisterQueue(RSTDIO_RxStdErrQ); McuRTOS_vQueueDelete(RSTDIO_RxStdErrQ); RSTDIO_RxStdErrQ = NULL; McuRTOS_vQueueUnregisterQueue(RSTDIO_TxStdInQ); McuRTOS_vQueueDelete(RSTDIO_TxStdInQ); RSTDIO_TxStdInQ = NULL; McuRTOS_vQueueUnregisterQueue(RSTDIO_TxStdOutQ); McuRTOS_vQueueDelete(RSTDIO_TxStdOutQ); RSTDIO_TxStdOutQ = NULL; McuRTOS_vQueueUnregisterQueue(RSTDIO_TxStdErrQ); McuRTOS_vQueueDelete(RSTDIO_TxStdErrQ); RSTDIO_TxStdErrQ = NULL; } void RSTDIO_Init(void) { RSTDIO_SetDestinationAddress(RSTDIO_CONFIG_SETTING_RSTDIO_DEFAULT_DESTINATION_ADDRESS); RSTDIO_RxStdInQ = McuRTOS_xQueueCreate(RSTDIO_QUEUE_LENGTH, RSTDIO_QUEUE_ITEM_SIZE); if (RSTDIO_RxStdInQ==NULL) { for(;;){} /* out of memory? */ } McuRTOS_vQueueAddToRegistry(RSTDIO_RxStdInQ, "RxStdInQ"); #if configUSE_TRACE_HOOKS vTraceSetQueueName(RSTDIO_RxStdInQ, "RxStdInQ"); #endif RSTDIO_RxStdOutQ = McuRTOS_xQueueCreate(RSTDIO_QUEUE_LENGTH, RSTDIO_QUEUE_ITEM_SIZE); if (RSTDIO_RxStdOutQ==NULL) { for(;;){} /* out of memory? */ } McuRTOS_vQueueAddToRegistry(RSTDIO_RxStdOutQ, "RxStdOutQ"); #if configUSE_TRACE_HOOKS vTraceSetQueueName(RSTDIO_RxStdOutQ, "RxStdOutQ"); #endif RSTDIO_RxStdErrQ = McuRTOS_xQueueCreate(RSTDIO_QUEUE_LENGTH, RSTDIO_QUEUE_ITEM_SIZE); if (RSTDIO_RxStdErrQ==NULL) { for(;;){} /* out of memory? */ } McuRTOS_vQueueAddToRegistry(RSTDIO_RxStdErrQ, "RxStdErrQ"); #if configUSE_TRACE_HOOKS vTraceSetQueueName(RSTDIO_RxStdErrQ, "RxStdErrQ"); #endif RSTDIO_TxStdInQ = McuRTOS_xQueueCreate(RSTDIO_QUEUE_LENGTH, RSTDIO_QUEUE_ITEM_SIZE); if (RSTDIO_TxStdInQ==NULL) { for(;;){} /* out of memory? */ } McuRTOS_vQueueAddToRegistry(RSTDIO_TxStdInQ, "TxStdInQ"); #if configUSE_TRACE_HOOKS vTraceSetQueueName(RSTDIO_TxStdInQ, "TxStdInQ"); #endif RSTDIO_TxStdOutQ = McuRTOS_xQueueCreate(RSTDIO_QUEUE_LENGTH, RSTDIO_QUEUE_ITEM_SIZE); if (RSTDIO_TxStdOutQ==NULL) { for(;;){} /* out of memory? */ } McuRTOS_vQueueAddToRegistry(RSTDIO_TxStdOutQ , "TxStdOutQ"); #if configUSE_TRACE_HOOKS vTraceSetQueueName(RSTDIO_TxStdOutQ, "TxStdOutQ"); #endif RSTDIO_TxStdErrQ = McuRTOS_xQueueCreate(RSTDIO_QUEUE_LENGTH, RSTDIO_QUEUE_ITEM_SIZE); if (RSTDIO_TxStdErrQ==NULL) { for(;;){} /* out of memory? */ } McuRTOS_vQueueAddToRegistry(RSTDIO_TxStdErrQ , "TxStdErrQ"); #if configUSE_TRACE_HOOKS vTraceSetQueueName(RSTDIO_TxStdErrQ, "TxStdErrQ"); #endif } #endif /* McuRNET_CONFIG_IS_ENABLED */