/*! * \brief The MMI user-action task: in charge of launching an action requested * through the MMI. * */ static portTASK_FUNCTION( vSupervisorUserActionTask, pvParameters ) { bool (*pfUserAction) (void); bool result; /* Just to stop compiler warnings. */ ( void ) pvParameters; for(;;) { /* 1) Suspend ourselves. */ vTaskSuspend( NULL ); // TO KEEP // NOTE: the task should be resumed when it is necessary to perform a user action // get queue information if ( pdTRUE == xQueueReceive( xSUPERVISORQueue, &pfUserAction, ( portTickType ) 0 ) ) { /* 2) Perform the action. */ if (pfUserAction != NULL) { result = pfUserAction(); if (result) { NAKED_TRACE_COM2( "User function succeed"); } else { NAKED_TRACE_COM2( "User function failed"); } } } } }
/*! * \brief Launch the "Upload CFG Files" MMI task that copies the /CFG directory * from the USB Mass Storage device to the Control Panel. * * \return pdTRUE * */ bool b_USBHostCopyCfg( void ) { #if configCTRLPANEL_TRACE == 1 eExecStatus xRet; #endif // Display User Menu Acting vMMI_SetUserMenuMode(eUserMenuUSBHostActing, pdFALSE); vTaskDelay( 3 ); // So that the supervisor task gets the focus to update the // LCD screen. NAKED_TRACE_COM2( "Copy Config Files" ); // Perform the task #if configCTRLPANEL_TRACE == 1 xRet = #endif e_usbsys_cp_cfg_to_local( SYS_MODID_MMI, sMmiNavId, -1, NULL, NULL ); #if configCTRLPANEL_TRACE == 1 if( SHELL_EXECSTATUS_KO == xRet ) NAKED_TRACE_COM2( "Copying /CFG from USB MS device to Ctrl Panel failed.") #endif // Display User Menu vMMI_SetUserMenuMode(eUserMenuUSBHost, pdFALSE); return (pdTRUE); }
/*! * \brief Print dev info on the trace port. */ void v_supervisor_trace( void ) { #if configHEAP_INIT #if __GNUC__ prvCheckMallocHeap(); // Get an update of the malloc() heap highest consumption. NAKED_TRACE_COM2( "SUP:<%d><%d><%d>", TempoVbusOn, TempoVbusOff, pxHighestHeapAddr ); #endif #else NAKED_TRACE_COM2( "SUP:<%d><%d>", TempoVbusOn, TempoVbusOff ); #endif }
/*! * \brief Do the required actions to leave the maintenance mode. * */ static void prv_v_leave_maintenance_mode( void ) { #ifdef USB_ENABLE #if USB_DEVICE_FEATURE == true // Release the semaphores and change the MMI user menu mode. prv_v_common_leave_UsbDevice_mode(); // The actions to take to leave the maintenance mode are now done. bOutOfMaintenance = false; // We just left the USB Mass Storage device mode. // Special case: for com1shell, remount the local drive. v_com1shell_mount_local_drive(); #endif #endif NAKED_TRACE_COM2( "Leaving maintenance mode"); }
/*######## TEMPORARY #########################################*/ void prvCheckMallocHeap( void ) { portBASE_TYPE *pxMem; pxMem = ((portBASE_TYPE *)&__heap_end__) -1; while( (portBASE_TYPE *)&__heap_start__ != pxMem ) { if( (portBASE_TYPE)0xA5A5A5A5 != *pxMem ) { if( pxHighestHeapAddr < pxMem ) pxHighestHeapAddr = pxMem; NAKED_TRACE_COM2( "worst@=%d", pxHighestHeapAddr ); break; } else pxMem--; } }
/*! \brief send the static error page * * \param pxNetCon Input. The netconn to use to send and receive data. * \param s Input. The error code. * \param extra_header Input. data to push in the error answer. * \param text Input. text to display in the error page. * */ static void prvweb_SendErrorPage(struct netconn *pxNetCon, int s, char* extra_header, const char* text ) { portCHAR* title; unsigned portLONG size; portCHAR * err_data; err_data = (portCHAR *)pvPortMalloc(ERR_BUFFER_SIZE); if( NULL == err_data ) { NAKED_TRACE_COM2( "prvweb_SendErrorPage(): mem alloc failed" ); return; } /* Write the status line. */ switch ( s ) { case 400: title = "Bad Request"; break; case 401: title = "Unauthorized"; break; case 403: title = "Forbidden"; break; case 404: title = "Not Found"; break; case 408: title = "Request Timeout"; break; case 500: title = "Internal Error"; break; case 501: title = "Not Implemented"; break; case 503: title = "Service Unavailable"; break; default: title = "Something"; break; } /* add the header */ size = prulweb_BuildHeaders(err_data, s, title, extra_header, "", "text/html"); /* add the body */ size += prulweb_BuildErrorBody(&err_data[size], s, title, text ); /* add the tail */ size += prulweb_BuildErrorTail(&err_data[size]); /* send the response to network */ netconn_write(pxNetCon, err_data, size, NETCONN_COPY ); /* free the buffer */ vPortFree(err_data); }
/*! \brief parse the file request and send the requested file to the host * * \param pxNetCon Input. The netconn to use to send and receive data. * \param filename Input. The incoming filename. * */ static void prvweb_SendFile( struct netconn *pxNetCon, char* filename ) { portLONG size, header_size; portLONG fd = -1; portCHAR *resp_data; unsigned portSHORT i; err_t error; pf_read_src pfread = read; // Set the default read to a file read. // NAKED_TRACE_COM2( "SendFile(): %s", filename ); /* allocate response */ resp_data = (portCHAR *)pvPortMalloc(webMAX_DATA_TO_SEND); if (resp_data != NULL) { /* add the header */ header_size = prulweb_BuildHeaders(resp_data, 200, "OK", "", "", "text/html"); if ((fd = open((const char *)filename, O_RDONLY)) < 0) { // Set the read function to read from the pcDefaultPage pfread = prv_read_default_page; size = x_default_page_len; NAKED_TRACE_COM2( "SendFile(): file not found. Default page len:%d", x_default_page_len ); } else /* get the file size */ { size = fsaccess_file_get_size(fd); } /* check if file should be sent in a single frame */ if ( size <= (webMAX_DATA_TO_SEND - header_size)) { /* get the data from filesystem */ if( pfread(fd,&resp_data[header_size],size) != size) { NAKED_TRACE_COM2( "SendFile(): short file read access error" ); prvweb_SendErrorPage( pxNetCon, 404, filename, "Error during read access." ); goto quit_send_page; } /* send the response to network */ error = netconn_write(pxNetCon, resp_data, (u16_t)(header_size + size), NETCONN_COPY ); if (error != 0) { NAKED_TRACE_COM2( "SendFile(): short file nw write error" ); goto quit_send_page; } } else { /* send the header */ error = netconn_write(pxNetCon, resp_data, header_size, NETCONN_COPY ); if (error != 0) { NAKED_TRACE_COM2( "SendFile(): long file hdr nw write error" ); goto quit_send_page; } /* try to send the biggest frame contained in the file */ for (i = webNB_SECTOR_TO_SEND ; i > 0 ; i--) { /* get sectors of maximum size */ while(size > i * FS_SIZE_OF_SECTOR) { /* get the data from filesystem */ if( pfread(fd,resp_data, i * FS_SIZE_OF_SECTOR) != i * FS_SIZE_OF_SECTOR) { NAKED_TRACE_COM2( "SendFile(): long file read access error" ); prvweb_SendErrorPage( pxNetCon, 404, filename, "Error during read access." ); goto quit_send_page; } /* send the response to network */ error = netconn_write(pxNetCon, resp_data, (u16_t)(i * FS_SIZE_OF_SECTOR), NETCONN_COPY ); if (error != 0) { NAKED_TRACE_COM2( "SendFile(): long file nw write error" ); goto quit_send_page; } size -= (i * FS_SIZE_OF_SECTOR); } } /* finish with the few data remaining (less than 1 sector)*/ if ( size > 0 ) { /* get the data from filesystem */ if( pfread(fd,resp_data,size) != size) { NAKED_TRACE_COM2( "SendFile(): long file end read access error" ); prvweb_SendErrorPage( pxNetCon, 404, filename, "Error during read access." ); goto quit_send_page; } /* send the response to network */ error = netconn_write(pxNetCon, resp_data, (u16_t)size, NETCONN_COPY ); if (error != 0) { NAKED_TRACE_COM2( "SendFile(): long file end nw write error" ); goto quit_send_page; } } } }// end if response != NULL else { NAKED_TRACE_COM2( "SendFile(): mem alloc failed" ); __asm__ __volatile__ ("nop"); } quit_send_page: // vPortFree() handles the case where resp_data==NULL vPortFree(resp_data); /* close the file */ // close() handles the case where fd is an invalid file descriptor. close(fd); }
/*! * \brief Task for SMTP management */ portTASK_FUNCTION( vBasicSMTPClient, pvParameters ) { struct sockaddr_in stServeurSockAddr; portLONG lRetval; portLONG lSocket = -1; portLONG ulResponseCode = 0; xMailDef * pxMail; int Size, SizeRead; portCHAR * pcRespData; int fd; portCHAR cToken[6]; // Just to stop compiler warnings. ( void ) pvParameters; // Create the xMailsQueue capable of containing DATALOG_LOGSQUEUE_SIZE ptrs // to xLogDef structures. xMailsQueue = xQueueCreate( SMTP_MAILS_QUEUE_SIZE, sizeof( xMailDef * ) ); // SMTP configuration. // Get the xCFGMutex. if( pdFALSE == x_supervisor_SemaphoreTake( xCFGMutex, 500 ) ) { // Failed to get the CFG mutex, use the default HTTP config. uiSMTPPort = SMTP_PORT; cMailTo[0] = '\0'; cMailFrom[0] = '\0'; cServerName[0] = '\0'; } // Mutex has been taken else { // get the field value for port number if (config_file_get_value(SMTP_CONFIG_FILE, "port" , cToken) >= 0) { sscanf(cToken, "%u", &uiSMTPPort); } // if it does not exist, use the default value else { uiSMTPPort = SMTP_PORT; } // try to get the mailto field if (config_file_get_value(SMTP_CONFIG_FILE, "mailto", cMailTo) < 0) { cMailTo[0] = '\0'; // can't find field in config file, warn user NAKED_TRACE_COM2("Warning : No mailto configured !!Please fill mailto= field in %s\r\n", SMTP_CONFIG_FILE); } // try to get the mailfrom field if (config_file_get_value(SMTP_CONFIG_FILE, "mailfrom", cMailFrom) < 0) { cMailFrom[0] = '\0'; // can't find field in config file, warn user NAKED_TRACE_COM2("Warning : No mailfrom configured !!Please fill mailfrom= field in %s\r\n", SMTP_CONFIG_FILE); } // try to get the server field if (config_file_get_value(SMTP_CONFIG_FILE, "server", cServerName) < 0) { cServerName[0] = '\0'; // can't find field in config file, warn user NAKED_TRACE_COM2("Warning : No server name configured !! Please fill server= field in %s\r\n", SMTP_CONFIG_FILE); } // Release the xCFGMutex. x_supervisor_SemaphoreGive( xCFGMutex ); } for(;;) { // NOTE: the task should be resumed when it is necessary to send a mail // Get the oldest mail from the queue. // NOTE: we are sure there is an item to get => no block time. if( pdTRUE == xQueueReceive( xMailsQueue, &pxMail, ( portTickType )1000 ) ) { if (cServerName[0] == '\0') { // can't find field in config file, warn user NAKED_TRACE_COM2("Warning : No server name configured !! Please fill server= field in %s\r\n", SMTP_CONFIG_FILE); } else if (cMailTo[0] == '\0') { // can't find field in config file, warn user NAKED_TRACE_COM2("Warning : No mailto configured !!Please fill mailto= field in %s\r\n", SMTP_CONFIG_FILE); } else if (cMailFrom[0] == '\0') { // can't find field in config file, warn user NAKED_TRACE_COM2("Warning : No mailfrom configured !!Please fill mailfrom= field in %s\r\n", SMTP_CONFIG_FILE); } else { // Set up port memset(&stServeurSockAddr, 0, sizeof(stServeurSockAddr)); stServeurSockAddr.sin_len = sizeof(stServeurSockAddr); stServeurSockAddr.sin_addr.s_addr = inet_addr(cServerName); stServeurSockAddr.sin_port = htons(uiSMTPPort); stServeurSockAddr.sin_family = AF_INET; // socket as a stream if ( (lSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { // socket failed NAKED_TRACE_COM2("Socket Failed\r\n"); } // connect to the server if(connect(lSocket,(struct sockaddr *)&stServeurSockAddr,sizeof(stServeurSockAddr)) < 0) { // connect failed NAKED_TRACE_COM2("Connect Failed\r\n"); } else { eSMTPCurrentState = eSMTPIdle; while ( eSMTPCurrentState != eSMTPMailSent ) { // wait for SMTP Server answer do { lRetval = recv(lSocket, cTempBuffer, sizeof(cTempBuffer), 0); }while (lRetval <= 0); cTempBuffer[3] = '\0'; // Get the response code from server ulResponseCode = atoi(cTempBuffer); switch (ulResponseCode) { case 220: { // send helo send(lSocket, "HELO ", 5, 0); send(lSocket, cServerName, strlen(cServerName), 0); send(lSocket, "\r\n", 2, 0); eSMTPCurrentState = eSMTPHeloSent; break; } case 221: { // QUIT sequence has been acknowledged by server if (eSMTPCurrentState == eSMTPQuitSent) { eSMTPCurrentState = eSMTPMailSent; } break; } case 250: { if (eSMTPCurrentState == eSMTPHeloSent) { // send MAIL FROM send(lSocket, "MAIL FROM: <", 12, 0); ; send(lSocket, cMailFrom, strlen(cMailFrom), 0); send(lSocket, ">\r\n", 3, 0); eSMTPCurrentState = eSMTPMailFromSent; } else if (eSMTPCurrentState == eSMTPMailFromSent) { // send MAIL TO send(lSocket, "RCPT TO: <", 10, 0); ; send(lSocket, cMailTo, strlen(cMailTo), 0); send(lSocket, ">\r\n", 3, 0); eSMTPCurrentState = eSMTPMailToSent; } else if (eSMTPCurrentState == eSMTPMailToSent) { // send DATA send(lSocket, SMTP_DATA_STRING, 6, 0); eSMTPCurrentState = eSMTPDataSent; } else if (eSMTPCurrentState == eSMTPContentSent) { // send QUIT send(lSocket, SMTP_QUIT_STRING, 6, 0); eSMTPCurrentState = eSMTPQuitSent; } break; } case 354: { if (eSMTPCurrentState == eSMTPDataSent) { // set Subject field send(lSocket, "Subject:", 8, 0); // add subject send(lSocket, pxMail->Subject, strlen(pxMail->Subject), 0); send(lSocket, "\r\nFROM:", 7, 0); send(lSocket, cMailFrom, strlen(cMailFrom), 0); send(lSocket, "\r\nTO:", 5, 0); send(lSocket, cMailTo, strlen(cMailTo), 0); send(lSocket, "\r\n\r\n", 4, 0); // if a file has been specified, copy the content in the mail body if (pxMail->File != NULL) { // allocate response pcRespData = (portCHAR *)pvPortMalloc(SMTP_PACKET_SIZE); if (pcRespData != NULL) { if ((fd = open((const char *)pxMail->File, O_RDONLY)) >= 0) { Size = fsaccess_file_get_size(fd); // get sectors of maximum size while(Size > 0) { // get the data from filesystem SizeRead = read(fd, pcRespData, SMTP_PACKET_SIZE); // if error occurs during the read if (SizeRead <= 0) { // end the loop and send what has already been added break; } // sned data to the socket send(lSocket, pcRespData, SizeRead, 0); // decrease remaing size Size -= SizeRead; } // close the file close(fd); // free the buffer vPortFree(pcRespData); } else { // warn user : can't open the file NAKED_TRACE_COM2("Open file fails\r\n"); } } else { // warn user : can't malloc the file buffer NAKED_TRACE_COM2("SMTP : Malloc fails\r\n"); } } // add "<CRLF>.<CRLF>" send(lSocket, SMTP_MAIL_END_STRING, 5, 0); eSMTPCurrentState = eSMTPContentSent; } break; } default: { // unknown SMTP code NAKED_TRACE_COM2("Unimplented %l SMTP response from server\r\n",ulResponseCode); // break loop and reset state machine eSMTPCurrentState = eSMTPMailSent; break; } } } // close the socket close(lSocket); } // if the item was not posted from ISR if (pxMail->NeedFree == pdTRUE) { // if a file has been specified if ( pxMail->File != NULL ) { // free the item vPortFree(pxMail->File); } // free the items vPortFree(pxMail->Subject); vPortFree(pxMail); } } } } }
/*! \brief parse the incoming request * parse the HTML request and send file or execute CGI request * * \param pxNetCon Input. The netconn to use to send and receive data. * */ static void prvweb_ParseHTMLRequest( struct netconn *pxNetCon ) { struct netbuf *pxRxBuffer; portCHAR *pcRxString; portCHAR *file; portCHAR *path; portCHAR *protocol; unsigned portSHORT Length; #if ( (LWIP_VERSION) != ((1U << 24) | (3U << 16) | (2U << 8) | (LWIP_VERSION_RC)) ) err_t err_recv; #endif #if ( (LWIP_VERSION) == ((1U << 24) | (3U << 16) | (2U << 8) | (LWIP_VERSION_RC)) ) /* We expect to immediately get data. */ pxRxBuffer = netconn_recv( pxNetCon ); #else err_recv = netconn_recv( pxNetCon, &pxRxBuffer); if (err_recv != ERR_OK) { if (pxRxBuffer != NULL) goto delete_buffer; return; } #endif if( pxRxBuffer != NULL ) { /* Where is the data? */ netbuf_data( pxRxBuffer, (void *) &pcRxString, &Length ); /* Parse the first line of the request. */ if ( pcRxString == (char*) 0 ) { NAKED_TRACE_COM2( "ParseHTMLRequest(): empty request" ); prvweb_SendErrorPage( pxNetCon, 400, "", "Wrong request : no action submitted." ); goto delete_buffer; } /* get first occurrence of space char or \t or \n or \r */ path = strpbrk( pcRxString, " \t\n\r" ); if ( path == (char*) 0 ) { NAKED_TRACE_COM2( "ParseHTMLRequest(): parse error" ); prvweb_SendErrorPage( pxNetCon, 400, "", "Can't parse request." ); goto delete_buffer; } *path++ = '\0'; protocol = strpbrk( path, " \t\r\n" ); *protocol++ = '\0'; /* Is this a GET? */ if( !strncmp( pcRxString, "GET", 3 ) ) { if ( path[0] != '/' ) { NAKED_TRACE_COM2( "ParseHTMLRequest(): wrong request" ); prvweb_SendErrorPage( pxNetCon, 400, "", "Wrong request." ); goto delete_buffer; } /* if there is a ?, this is a CGI request */ protocol = strpbrk( path, "?" ); if (protocol != NULL) { /* parse the CGI request */ prvweb_ParseCGIRequest(pxNetCon, &path[1]); goto delete_buffer; } else { /* a file has been requested */ file = (portCHAR *)pvPortMalloc((MAX_FILE_PATH_LENGTH * 2) + 4); if( NULL == file ) { NAKED_TRACE_COM2( "ParseHTMLRequest(): mem alloc failed" ); prvweb_SendErrorPage( pxNetCon, 400, "", "Memory allocation failed." ); goto delete_buffer; } /* add web directory structure */ sprintf(file,webSERVER_FOLDER); strcat(file,&(path[1])); /* check filename, if none, send index.htm */ if ( file[sizeof(webSERVER_FOLDER)-1] == '\0' ) { strcat(file,"index.htm"); } /* go to the file transfer */ prvweb_SendFile( pxNetCon, file ); vPortFree(file); } } /* Is this a POST? */ else if( !strncmp( pcRxString, "POST", 4 ) ) { NAKED_TRACE_COM2( "ParseHTMLRequest(): POST request not supported" ); prvweb_SendErrorPage( pxNetCon, 400, "", "POST request not supported for now." ); } /* Is this a HEAD? */ else if( !strncmp( pcRxString, "HEAD", 4 ) ) { NAKED_TRACE_COM2( "ParseHTMLRequest(): HEAD request not supported" ); prvweb_SendErrorPage( pxNetCon, 400, "", "HEAD request not supported for now." ); } /* We don't handle anything else. */ else { NAKED_TRACE_COM2( "ParseHTMLRequest(): unexpected request" ); prvweb_SendErrorPage( pxNetCon, 400, "", "Can't parse request." ); } } else { NAKED_TRACE_COM2( "ParseHTMLRequest(): nothing rxed" ); } delete_buffer: // netbuf_delete() handles the case when pxRxBuffer == NULL netbuf_delete( pxRxBuffer ); }
/*! \brief parse the CGI request and send it to the shell * * \param pxNetCon Input. The netconn to use to send and receive data. * \param request Input. The incoming CGI request. * */ static void prvweb_ParseCGIRequest( struct netconn *pxNetCon, char* request ) { portCHAR cgi_present = false, loop; signed portCHAR * pcStringCmd; signed portCHAR * CurrentChar; signed portCHAR * pcStringReply; eExecStatus xExeStatus; portCHAR *pcResp_data; portLONG header_size; /* when entering this function, path contains : command.cgi?arg1=val1&arg2=val2 it should be command arg1=val1 arg2=val2 after parsing operation */ /* allocate command space */ pcStringCmd = pvPortMalloc( 100 ); if (pcStringCmd != NULL) { loop = true; CurrentChar = pcStringCmd; while (loop) { switch (*request) { case '.': { if (cgi_present == false) { cgi_present = true; request += strlen(".cgi"); } else { *CurrentChar++ = *request++; } break; } /* move ? and & to space */ case '?': case '&': *CurrentChar++ = ' '; request++; break; case '%': /* move %20 to space */ if ((*(request+1) == '2') && (*(request+2) == '0')) { *CurrentChar++ = ' '; request += 3; } /* keep %22 */ else if ((*(request+1) == '2') && (*(request+2) == '2')) { *CurrentChar++ = '"'; request += 3; } /* else keep the % symbol */ else { *CurrentChar++ = *request++; } break; case '\r': case '\n': case '\0': case ' ': *CurrentChar = '\0'; loop = false; break; default: *CurrentChar++ = *request++; break; } } /* send command to the shell, no error control for this module */ xExeStatus = Shell_exec(pcStringCmd, SYS_MODID_HTTP, -1, &pcStringReply); /* free buffer */ vPortFree(pcStringCmd); if (pcStringReply != NULL) { pcResp_data = pvPortMalloc( 130 + strlen( (char *)pcStringReply ) ); // 130(header size) + sizeof( reply ) if( NULL == pcResp_data ) { if( SHELL_EXECSTATUS_OK == xExeStatus ) { vPortFree(pcStringReply); } NAKED_TRACE_COM2( "ParseCGIRequest(): reply mem alloc failed" ); // Avoid returning an error page so that the remote web browser keeps // on trying. return; } /* add the header */ header_size = prulweb_BuildHeaders( pcResp_data, 200, "OK", "", "", "text/html"); // Add the data. strcpy( pcResp_data + header_size, (char *)pcStringReply ); if( SHELL_EXECSTATUS_OK == xExeStatus ) { vPortFree(pcStringReply); } /* send the response to network */ netconn_write(pxNetCon, pcResp_data, (u16_t)strlen( pcResp_data ), NETCONN_COPY ); vPortFree( pcResp_data ); } } else { NAKED_TRACE_COM2( "ParseCGIRequest(): request mem alloc failed" ); // Avoid returning an error page so that the remote web browser keeps on trying. } }
/*! \brief The set time config command: set each config field value of the time module. * Takes one parameter : field=value * * \note This function must be of the type pfShellCmd defined by the shell module. * * \param xModId Input. The module that is calling this function. * \param FsNavId Ignored. * \param ac Input. The argument counter. For this command, should be 1. * \param av Input. The argument vector. * \param ppcStringReply Input/Output. The response string. * If Input is NULL, no response string will be output. * Else a malloc for the response string is performed here; * the caller must free this string. * * \return the status of the command execution. */ eExecStatus e_cptime_cmd_set_config( eModId xModId, signed short FsNavId, int ac, signed portCHAR *av[], signed portCHAR **ppcStringReply ) { /* int fd; eFsCmdsStatus eFsStatus; */ struct tm xLogDate; time_t xRawDate; int i; int SrcIdx=0; // NOTE: currently the only configurable item of the time module is the // current time. "curtime=mm/dd/yy hh:mm:ss" // 1) Check the input. // i) Exactly two arguments (the field and the value). if( 2 != ac ) { // Syntax error. if(ppcStringReply != NULL) *ppcStringReply = (signed portCHAR *)CPTIME_ERRMSG_SETCFG_SYNTAXERROR; return( SHELL_EXECSTATUS_KO ); } // The field must be one of : {curtime} if( !strcmp("curtime", (char *)av[0]) ) { // 2) Read the current time string sscanf( (char *)av[1], "%02d/%02d/%02d %02d:%02d:%02d", &(xLogDate.tm_mon), &(xLogDate.tm_mday), &(xLogDate.tm_year), &(xLogDate.tm_hour), &(xLogDate.tm_min), &(xLogDate.tm_sec) ); (xLogDate.tm_mon)--; // Adjust to the logic where January index is 0. // WARNING: pxLogDate->tm_year == number of years since 1900. // We get the last 2 digits only : we suppose the year is >= 2000. xLogDate.tm_year += 100; xLogDate.tm_isdst = 0; // Daylight saving time info is not in effect. // Convert time broken-down representation to arithmetic representation. xRawDate = mktime( &xLogDate ); if( -1 == xRawDate ) { if(ppcStringReply != NULL) *ppcStringReply = (signed portCHAR *)CPTIME_ERRMSG_SETCFG_INVALIDDATE; return( SHELL_EXECSTATUS_KO ); } else { NAKED_TRACE_COM2( "CPTIME:NbSchedCmd=%d", NbSchedCmd ); // Update all cells of the scheduled commands array: cancel the schedules // that are in the past but keep the schedules that are still in the future. for( i=NbSchedCmd-1; i>=0; i-- ) { if( axCmdsSched[i].xScheduledTime <= xRawDate ) { // This schedule is in the past => cancel it and all other cells // that are further down the past. // NOTE: all below scheduled commands are obsolete if this one is. // => Cancel them too. SrcIdx = i+1; // Remember the last active scheduled commands index. for( i=0; i<SrcIdx; i++ ) { axCmdsSched[i].xScheduledTime = 0; // Mark this cell as unused. NbSchedCmd--; // One scheduled cmd removed. } NAKED_TRACE_COM2( "CPTIME:NbSchedCmd=%d, SrcIdx=%d", NbSchedCmd, SrcIdx ); break; } } // Shift the Scheduled Commands array down to the index 0. if( ( 0 != NbSchedCmd ) && ( CPTIME_MAX_NB_SCHED_CMDS > SrcIdx ) ) { NAKED_TRACE_COM2( "CPTIME:Shifting array from SrcIdx=%d to 0 idx", SrcIdx ); prv_v_ShiftScheduledCmdsArray( SrcIdx, 0 ); } // Switch to new time. xcptime_LocalTime = xRawDate; } } else { // Unsupported field. if(ppcStringReply != NULL) *ppcStringReply = (signed portCHAR *)CPTIME_ERRMSG_SETCFG_UNKNOWNFIELD; return( SHELL_EXECSTATUS_KO ); } return( SHELL_EXECSTATUS_OK ); /****** CODE TO USE WHEN THERE ARE OTHER FIELDS THAN CURTIME. if ((fd = open(TIME_CONFIG_FILE, (O_RDWR))) >= 0) { if (config_file_set_value(fd, ac, av) != 0) { if(ppcStringReply != NULL) { *ppcStringReply = (signed portCHAR *)SHELL_ERRMSG_CONFIGERROR; } return( SHELL_EXECSTATUS_KO ); } close (fd); return( SHELL_EXECSTATUS_OK ); } else { eFsStatus = e_fscmds_CheckNavError(); // Get the fs error. if( ppcStringReply != NULL ) { v_fscmds_GetStrMsgFromErr(eFsStatus, ppcStringReply); } return( SHELL_EXECSTATUS_KO ); } ******/ }
/*! * \brief Print dev info on the trace port. */ void v_basicweb_trace( void ) { NAKED_TRACE_COM2( "BASICWEB:<%d>", sCurrentNbHTTPConn ); }
/*! * \brief Print dev info on the trace port. */ void v_cptime_trace( void ) { NAKED_TRACE_COM2( "CPTIME:<%d><%d>", NbSchedCmd, xcptime_LocalTime ); }
/*! * \brief The switch-to-maintenance-mode command: initiate the process to \n * switch to maintenance mode. * Format: maintain * * \note This function must be of the type pfShellCmd defined by the shell module. * * \param xModId Input. The module that is calling this function. * \param FsNavId Ignored. * \param ac Ignored. * \param av Ignored. * \param ppcStringReply Input/Output. The response string. * If Input is NULL, no response string will be output. * Else a malloc for the response string is performed here; * the caller must free this string. * * \return the status of the command execution. */ eExecStatus e_supervisor_switch_to_maintenance_mode( eModId xModId, signed short FsNavId, int ac, signed portCHAR *av[], signed portCHAR **ppcStringReply ) { if( NULL != ppcStringReply ) *ppcStringReply = NULL; #ifdef USB_ENABLE #if USB_DEVICE_FEATURE == true if( ( false == bIsInMaintenance ) && ( 0 == u8IsMaintenanceRequired ) ) { // We're not in maintenance mode. // Initiate the process of switching to maintenance mode. if( 0 == u8IsMaintenanceRequired ) u8IsMaintenanceRequired++; // Take all maintenance mutex except the USB mutex. if( true == x_supervisor_SemaphoreTake( xLOGMutex, 0 ) ) u8IsMaintenanceRequired++; #if NW_INTEGRATED_IN_CONTROL_PANEL if( true == x_supervisor_SemaphoreTake( xWEBMutex, 0 ) ) u8IsMaintenanceRequired++; #endif if( true == x_supervisor_SemaphoreTake( xSHELLFSMutex, 0 ) ) u8IsMaintenanceRequired++; if( true == x_supervisor_SemaphoreTake( xCFGMutex, 0 ) ) u8IsMaintenanceRequired++; // If all mutexes have been acquired, switch to maintenance mode. if( ( SUPERVISOR_MAINTENANCE_NBMUTEX_TOTAKE +1 ) == u8IsMaintenanceRequired ) { fat_cache_flush(); // flush the FAT cache. nav_reset(); // Reset all file system navigators. We will mount // the com1shell default drive when we'll leave the // maintenance mode. // Switch to maintenance mode. xSemaphoreGive( xUSBMutex ); // If the USB clock is frozen, unfreeze it so that we can write in the // USB registers. if(true == Is_usb_clock_frozen()) { Usb_unfreeze_clock(); } // If it is not akready detached, physically detach the USB device. if(false == Is_usb_detached()) { Usb_detach(); } vTaskDelay(500); // Wait 500ms Usb_attach(); // Reconnect the device. bIsInMaintenance = true; u8IsMaintenanceRequired = 0; TRACE_COM2( "Entering maintenance mode"); #ifdef MMILCD_ENABLE vMMI_SetUserMenuMode( eUserMenuWaitHost, pdTRUE ); #endif } // ELSE: we'll switch to maintenance mode in x_supervisor_SemaphoreGive() // (when the mutex(es) that we couldn't get will be released). } else { NAKED_TRACE_COM2( "Won't go to maintenance mode:"CRLF"bIsInMaintenance=%d u8CurrentUsbRole=%d u8IsMaintenanceRequired=%d", bIsInMaintenance, u8CurrentUsbRole, u8IsMaintenanceRequired ); } #endif #endif return( SHELL_EXECSTATUS_OK ); }
/*! * \brief Print dev info on the trace port. */ void v_datalog_trace( void ) { NAKED_TRACE_COM2( "DATALOG:<%d><%d>", uxNbMsgsInLogsQueue, u8FreeLogIdx ); }
/*! * \brief Open the current log file. * * \return the file descriptor or -1 if open failed. */ static int prv_xopen_current_logfile( void ) { int fd_current_logfile; struct tm *pxDate; // TRACE_COM2( "open logfile begin==%d", xcptime_LocalTime ); while( 1 ) { fd_current_logfile = -1; // Init to default value. /* Open the log file. */ if( '\0' != *acLogFileName ) { // If we have an active current log file, simply open it in append mode. fd_current_logfile = open( acLogFileName, O_APPEND ); // Check if the max file size has been reached. if( -1 != fd_current_logfile ) { if( DATALOG_LOGFILE_MAXSIZE <= fsaccess_file_get_size( fd_current_logfile ) ) { // The current log file has reached the max size. // Get the current time in the "YYYYMMDDHHMMSSMS" string format. v_cptime_GetDateInFatStringFormat( pcTempoDate ); // Set the file date. nav_file_dateset( (FS_STRING)pcTempoDate, FS_DATE_LAST_WRITE ); close( fd_current_logfile ); // Close the file. *acLogFileName = '\0'; // Reset the current log file name. continue; // Do another loop to create/open a new file. } } else { // The file has been removed. *acLogFileName = '\0'; // Reset the current log file name. continue; // Do another loop to create/open a new file. } } else { // Create a new log file. // Get the broken-down representation of the current date. pxDate = gmtime( &xcptime_LocalTime ); // Build the filename: mmddyy_hhmm.log // WARNING: pxDate->tm_year == number of years since 1900. // For years >= 2000, we'll display the last 2 digits only. if( pxDate->tm_year >= 100 ) pxDate->tm_year -= 100; sprintf( acLogFileName, "%s/%.2d%.2d%.2d_%.2d%.2d.log", pcStringCurrentLogDirectoryName, pxDate->tm_mon +1, pxDate->tm_mday, pxDate->tm_year, pxDate->tm_hour, pxDate->tm_min ); NAKED_TRACE_COM2( "Creating log file %s", acLogFileName ); // Create the log file only if the /LOG directory exists. if( true == fsaccess_IsDirPresent( (const char *)pcStringCurrentLogDirectoryName ) ) { // The LOG/ directory exists. // Create and open the file. // if the file already exists, then the file size is reset. fd_current_logfile = open( acLogFileName, (O_CREAT|O_WRONLY) ); // previous file is closed, send a mail if ( *acPreviousLogFileName != '\0' ) { // post alarm to SMTP task v_SMTP_Post(acPreviousLogFileName, acPreviousLogFileName); } strncpy(acPreviousLogFileName, acLogFileName, strlen(acLogFileName)); } } if( -1 == fd_current_logfile ) { // The open failed. We're not in maintenance mode. // Just remove the oldest log file: TODO NAKED_TRACE_COM2( "Failed opening the current log file %s", acLogFileName ); /*########### TEMPORARY #############*/ break; /*###################################*/ } else break; } return( fd_current_logfile ); }
/*! * \brief Supervisor task. * * \return never */ static portTASK_FUNCTION( vSupervisorTask, pvParameters ) { portTickType xDelayLength = SUPERVISOR_DEFAULT_PERIOD; portTickType xLastFocusTime; #if configHEAP_INIT == 1 #if defined(__GNUC__) portLONG lCheckHeapDelay = 1; #endif #endif #if configCTRLPANEL_TRACE == 1 portLONG lPrintTrace = 3; #endif portLONG lUpdateTimeDelay = 1; #ifdef MMILCD_ENABLE portLONG lUpdateMMITimeDelay = 1; portCHAR DateTime[21]; struct tm *pxDate; bool ms_connected_displayed = pdFALSE; bool enum_connected_displayed = pdFALSE; #endif /* The parameters are not used. */ ( void )pvParameters; #if configCTRLPANEL_TRACE == 1 /* Initialize the dump port COM2. */ itracedump_Init(); #endif #ifdef MMILCD_ENABLE // The MMI module. if( pdFALSE == bMMI_start() ) { // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } #endif // Create the SHELL mutex. vSemaphoreCreateBinary( xSHELLFSMutex ); if( NULL == xSHELLFSMutex ) { // The mutex creation failed. // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } // Start the COM1 Shell module. vStartCom1Shell( mainCOMSH_TASK_PRIORITY ); // Create the CFG mutex. vSemaphoreCreateBinary( xCFGMutex ); if( NULL == xCFGMutex ) { // The mutex creation failed. // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } // Start the sensor module. if( false == bsensor_start() ) { // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } #if NW_INTEGRATED_IN_CONTROL_PANEL // Create the Web server mutex. vSemaphoreCreateBinary( xWEBMutex ); if( NULL == xWEBMutex ) { // The mutex creation failed. // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } // Start network tasks. vStartEthernetTaskLauncher( tskIDLE_PRIORITY + 1 ); #endif // Create the LOG mutex. vSemaphoreCreateBinary( xLOGMutex ); if( NULL == xLOGMutex ) { // The mutex creation failed. // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } // Start the data logger module. if( false == bdatalog_start( mainDATALOG_TASK_PRIORITY ) ) { // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } #ifdef USB_ENABLE #if USB_DEVICE_FEATURE == true // Create the USB mutex. vSemaphoreCreateBinary( xUSBMutex ); if( NULL == xUSBMutex ) { // The mutex creation failed. // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } // Immediately take the USB mutex. i.e. when we're a Mass Storage device, // we'll refuse to give r/w access to the host until a user action. This user // action will make the Ctrl Panel device switch to maintenance mode, in which // the Mass Storage USB host has r/w access to the Ctrl Panel file system. while( pdFALSE == x_supervisor_SemaphoreTake( xUSBMutex, 0 ) ); #endif // Start the USB module tasks. if( false == b_usbsys_start() ) { // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } #endif #ifdef MMILCD_ENABLE // Create the supervisor queue to deal with MMI actions xSUPERVISORQueue = xQueueCreate( SUPERVISOR_QUEUE_SIZE, sizeof(bool *) ); if( 0 == xSUPERVISORQueue ) { // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } /* Get a File System navigator for MMI actions. */ fsaccess_take_mutex(); sMmiNavId = fsaccess_alloc_nav_id(); nav_select( sMmiNavId ); // Select the navigator. fsaccess_give_mutex(); /* Spawn the User Action task. */ if( pdPASS != xTaskCreate( vSupervisorUserActionTask, ( const signed portCHAR * )"MMIACT", SUPERVISOR_USER_ACTION_STACK_SIZE, NULL, SUPERVISOR_USER_ACTION_TASK_PRIORITY, &xSupervisorUserActionHndl ) ) { vTaskDelete( xSupervisorUserActionHndl ); // TODO: Add msg on LCD. // vParTestSetLED( ERROR_LED_ID, pdTRUE ); while( 1 ); } #endif // #ifdef MMILCD_ENABLE /* We need to initialise xLastFlashTime prior to the first call to vTaskDelayUntil(). */ xLastFocusTime = xTaskGetTickCount(); #if defined(__GNUC__) NAKED_TRACE_COM2( "heap start @=%d, heap end @=%d", \ (portBASE_TYPE *)&__heap_start__, \ (portBASE_TYPE *)&__heap_end__ ); #endif /* Enable the watchdog timer. */ // wdt_enable( SUPERVISOR_WDT_TIMEOUT ); for(;;) { /* Delay for the flash period then check. */ vTaskDelayUntil( &xLastFocusTime, xDelayLength ); // wdt_clear(); // Kick the watchdog! /* MMI USB management. */ #ifdef MMILCD_ENABLE #ifdef USB_ENABLE /*** Start of Host behaviour ***/ // first occurrence of MS connection, Host mode if (ms_connected == true && ms_connected_displayed == pdFALSE) { // display connected logo ms_connected_displayed = pdTRUE; vMMI_DisplayUSBState(ms_connected_displayed); // Display User Menu vMMI_SetUserMenuMode(eUserMenuUSBHost, pdTRUE); } // first occurrence of MS disconnection, end of Host mode if (ms_connected == false && ms_connected_displayed == pdTRUE) { // remove connected logo ms_connected_displayed = pdFALSE; vMMI_DisplayUSBState(ms_connected_displayed); // clear User Menu vMMI_SetUserMenuMode(eUserMenuIdle, pdTRUE); } /*** End of Host behaviour ***/ /*** Start of Device behaviour ***/ #if USB_DEVICE_FEATURE == true // first occurrence of Device connection, Device mode if (Is_device_enumerated() && ( enum_connected_displayed == pdFALSE ) ) { if( true == bIsInMaintenance ) { // display connected logo enum_connected_displayed = pdTRUE; vMMI_DisplayUSBState(enum_connected_displayed); // Display User Menu vMMI_SetUserMenuMode(eUserMenuUSBDevice, pdTRUE); } } // first occurrence of Device disconnection, end of Device mode else if (!Is_device_enumerated() && enum_connected_displayed == pdTRUE) { // remove connected logo enum_connected_displayed = pdFALSE; vMMI_DisplayUSBState(enum_connected_displayed); // clear User Menu vMMI_SetUserMenuMode(eUserMenuIdle, pdTRUE); } else { // remove connected logo => this makes the USB logo blink when the Control // Panel is behaving as a USB key. enum_connected_displayed = pdFALSE; vMMI_DisplayUSBState(enum_connected_displayed); } /*** End of Device behaviour ***/ #endif // #if USB_DEVICE_FEATURE == true #endif // #ifdef USB_ENABLE #endif // #ifdef MMILCD_ENABLE /* update time every SUPERVISOR_DELAY_TIMEUPDATE seconds. */ if( 0 == --lUpdateTimeDelay ) { /* Update the local time. */ lUpdateTimeDelay = SUPERVISOR_DELAY_TIMEUPDATE; xcptime_LocalTime++; // v_cptime_UpdateLocalTime(); } #ifdef MMILCD_ENABLE /* Update time displayed on the LCD. */ if( 0 == --lUpdateMMITimeDelay) { // Get the broken-down representation of the current date. pxDate = gmtime( &xcptime_LocalTime ); // WARNING: pxDate->tm_year == number of years since 1900. // For years >= 2000, we'll display the last 2 digits only. if( pxDate->tm_year >= 100 ) pxDate->tm_year -= 100; #if DISPLAY_MMI_SECOND == 1 sprintf( DateTime, "%02d/%02d/20%02d %02d:%02d:%02d", pxDate->tm_mon +1, pxDate->tm_mday, pxDate->tm_year, pxDate->tm_hour, pxDate->tm_min, pxDate->tm_sec ); #else sprintf( DateTime, "%02d/%02d/20%02d %02d:%02d", pxDate->tm_mon +1, pxDate->tm_mday, pxDate->tm_year, pxDate->tm_hour, pxDate->tm_min ); #endif vMMI_DisplayDateAndTime(DateTime); lUpdateMMITimeDelay = SUPERVISOR_DELAY_MMI_TIMEUPDATE; } // Manage MMI vMMI_Manage(); // Manage MMI user action prv_v_manage_user_action(); #endif // #ifdef MMILCD_ENABLE #ifdef USB_ENABLE if( true == bOutOfMaintenance ) { prv_v_leave_maintenance_mode(); } #endif /* Execute a scheduled command if expiration date is up. */ v_cptime_ExecuteScheduledCmd(); #if configHEAP_INIT == 1 #if defined(__GNUC__) /* Record the malloc() heap highest consumption every SUPERVISOR_DELAY_HEAPCHECK seconds. */ if( 0 == --lCheckHeapDelay ) { lCheckHeapDelay = SUPERVISOR_DELAY_HEAPCHECK; prvCheckMallocHeap(); } #endif #endif #if configCTRLPANEL_TRACE == 1 // Display traces on USART1 every SUPERVISOR_DELAY_PRINTTASKLIST seconds. if( 0 == --lPrintTrace ) { lPrintTrace = SUPERVISOR_DELAY_PRINTTASKLIST; // Restart the delay. v_syscmds_display_traces(); } #endif } // for(;;) } /*lint !e715 !e818 !e830 Function definition must be standard for task creation. */