Files
zTC1/mico-os/MiCO/system/config_server/config_server.c
2025-03-11 15:54:45 +08:00

568 lines
22 KiB
C

/**
******************************************************************************
* @file config_server.c
* @author William Xu
* @version V1.0.0
* @date 05-May-2014
* @brief Local TCP server for mico device configuration
******************************************************************************
* UNPUBLISHED PROPRIETARY SOURCE CODE
* Copyright (c) 2016 MXCHIP Inc.
*
* The contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of MXCHIP Corporation.
******************************************************************************
*/
#include "mico.h"
#include "system_internal.h"
#include "platform.h"
#include "easylink_internal.h"
#include "SocketUtils.h"
#include "HTTPUtils.h"
#include "StringUtils.h"
#include "CheckSumUtils.h"
#define kCONFIGURLRead "/config-read"
#define kCONFIGURLWrite "/config-write"
#define kCONFIGURLWriteByUAP "/config-write-uap" /* Don't reboot but connect to AP immediately */
#define kCONFIGURLOTA "/OTA"
#define kMIMEType_MXCHIP_OTA "application/ota-stream"
typedef struct _configContext_t{
uint32_t offset;
bool isFlashLocked;
CRC16_Context crc16_contex;
} configContext_t;
extern OSStatus ConfigIncommingJsonMessage( int fd, const char *input, bool *need_reboot, mico_Context_t * const inContext );
extern json_object* ConfigCreateReportJsonMessage( mico_Context_t * const inContext );
static void localConfiglistener_thread(uint32_t inContext);
static void localConfig_thread(uint32_t inFd);
static OSStatus _LocalConfigRespondInComingMessage(int fd, HTTPHeader_t* inHeader, system_context_t * const inContext);
static OSStatus onReceivedData(struct _HTTPHeader_t * httpHeader, uint32_t pos, uint8_t * data, size_t len, void * userContext );
static void onClearHTTPHeader(struct _HTTPHeader_t * httpHeader, void * userContext );
bool is_config_server_established = false;
/* Defined in uAP config mode */
extern OSStatus ConfigIncommingJsonMessageUAP( int fd, const uint8_t *input, size_t size, system_context_t * const inContext );
extern system_context_t* sys_context;
static mico_semaphore_t close_listener_sem = NULL, close_client_sem[ MAX_TCP_CLIENT_PER_SERVER ] = { NULL };
WEAK void config_server_delegate_report( json_object *app_menu, mico_Context_t *in_context )
{
UNUSED_PARAMETER(app_menu);
UNUSED_PARAMETER(in_context);
return;
}
WEAK void config_server_delegate_recv( const char *key, json_object *value, bool *need_reboot, mico_Context_t *in_context )
{
UNUSED_PARAMETER(key);
UNUSED_PARAMETER(value);
UNUSED_PARAMETER(need_reboot);
UNUSED_PARAMETER(in_context);
return;
}
OSStatus config_server_start ( void )
{
int i = 0;
OSStatus err = kNoErr;
require( sys_context, exit );
if( is_config_server_established )
return kNoErr;
is_config_server_established = true;
close_listener_sem = NULL;
for (; i < MAX_TCP_CLIENT_PER_SERVER; i++)
close_client_sem[ i ] = NULL;
err = mico_rtos_create_thread( NULL, MICO_APPLICATION_PRIORITY, "Config Server", localConfiglistener_thread, STACK_SIZE_LOCAL_CONFIG_SERVER_THREAD, 0 );
require_noerr(err, exit);
mico_thread_msleep(200);
exit:
return err;
}
OSStatus config_server_stop( void )
{
int i = 0;
OSStatus err = kNoErr;
if( !is_config_server_established )
return kNoErr;
for (; i < MAX_TCP_CLIENT_PER_SERVER; i++){
if( close_client_sem[ i ] != NULL )
mico_rtos_set_semaphore( &close_client_sem[ i ] );
}
mico_thread_msleep(50);
if( close_listener_sem != NULL )
mico_rtos_set_semaphore( &close_listener_sem );
mico_thread_msleep(500);
is_config_server_established = false;
return err;
}
void localConfiglistener_thread( mico_thread_arg_t arg)
{
OSStatus err = kUnknownErr;
int j;
struct sockaddr_in addr;
int sockaddr_t_size;
fd_set readfds;
char ip_address[16];
int localConfiglistener_fd = -1;
int close_listener_fd = -1;
mico_rtos_init_semaphore( &close_listener_sem, 1);
close_listener_fd = mico_create_event_fd( close_listener_sem );
/*Establish a TCP server fd that accept the tcp clients connections*/
localConfiglistener_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
require_action(IsValidSocket( localConfiglistener_fd ), exit, err = kNoResourcesErr );
addr.sin_family = AF_INET;
addr.sin_addr.s_addr =INADDR_ANY;
addr.sin_port = htons(MICO_CONFIG_SERVER_PORT);
err = bind(localConfiglistener_fd, (struct sockaddr *)&addr, sizeof(addr));
require_noerr( err, exit );
err = listen(localConfiglistener_fd, 1);
require_noerr( err, exit );
system_log("Config Server established at port: %d, fd: %d", MICO_CONFIG_SERVER_PORT, localConfiglistener_fd);
while(1){
FD_ZERO(&readfds);
FD_SET(localConfiglistener_fd, &readfds);
FD_SET(close_listener_fd, &readfds);
select( Max(localConfiglistener_fd, close_listener_fd) + 1, &readfds, NULL, NULL, NULL);
/* Check close requests */
if(FD_ISSET(close_listener_fd, &readfds)){
mico_rtos_get_semaphore( &close_listener_sem, 0 );
goto exit;
}
/* Check tcp connection requests */
if(FD_ISSET(localConfiglistener_fd, &readfds)){
sockaddr_t_size = sizeof(struct sockaddr_in);
j = accept(localConfiglistener_fd, (struct sockaddr *)&addr, (socklen_t *)&sockaddr_t_size);
if ( IsValidSocket( j ) ) {
strcpy(ip_address,inet_ntoa( addr.sin_addr ));
system_log("Config Client %s:%d connected, fd: %d", ip_address, addr.sin_port, j);
if(kNoErr != mico_rtos_create_thread(NULL, MICO_APPLICATION_PRIORITY, "Config Clients", localConfig_thread, STACK_SIZE_LOCAL_CONFIG_CLIENT_THREAD, (mico_thread_arg_t)j) )
SocketClose(&j);
}
}
}
exit:
if( close_listener_sem != NULL ){
mico_delete_event_fd( close_listener_fd );
mico_rtos_deinit_semaphore( &close_listener_sem );
close_listener_sem = NULL;
};
system_log("Exit: Config listener exit with err = %d", err);
SocketClose( &localConfiglistener_fd );
is_config_server_established = false;
mico_rtos_delete_thread(NULL);
return;
}
void localConfig_thread(uint32_t inFd)
{
OSStatus err = kNoErr;
int clientFd = (int)inFd;
int clientFdIsSet;
int close_sem_index;
fd_set readfds;
struct timeval t;
HTTPHeader_t *httpHeader = NULL;
int close_client_fd = -1;
configContext_t httpContext = {0, false};
for( close_sem_index = 0; close_sem_index < MAX_TCP_CLIENT_PER_SERVER; close_sem_index++ ){
if( close_client_sem[close_sem_index] == NULL )
break;
}
if( close_sem_index == MAX_TCP_CLIENT_PER_SERVER){
mico_rtos_delete_thread(NULL);
return;
}
mico_rtos_init_semaphore( &close_client_sem[close_sem_index], 1);
close_client_fd = mico_create_event_fd( close_client_sem[close_sem_index] );
httpHeader = HTTPHeaderCreateWithCallback( 512, onReceivedData, onClearHTTPHeader, &httpContext );
require_action( httpHeader, exit, err = kNoMemoryErr );
HTTPHeaderClear( httpHeader );
t.tv_sec = 60;
t.tv_usec = 0;
system_log("Free memory %d bytes", MicoGetMemoryInfo()->free_memory) ;
while(1){
FD_ZERO(&readfds);
FD_SET(clientFd, &readfds);
FD_SET(close_client_fd, &readfds);
clientFdIsSet = 0;
if(httpHeader->len == 0){
require(select( Max(clientFd, close_client_fd) + 1 , &readfds, NULL, NULL, &t) >= 0, exit);
clientFdIsSet = FD_ISSET(clientFd, &readfds);
}
/* Check close requests */
if(FD_ISSET(close_client_fd, &readfds)){
mico_rtos_get_semaphore( &close_client_sem[close_sem_index], 0 );
err = kConnectionErr;
goto exit;
}
if(clientFdIsSet||httpHeader->len){
err = SocketReadHTTPHeader( clientFd, httpHeader );
switch ( err )
{
case kNoErr:
// Read the rest of the HTTP body if necessary
//do{
err = SocketReadHTTPBody( clientFd, httpHeader );
if(httpHeader->dataEndedbyClose == true){
err = _LocalConfigRespondInComingMessage( clientFd, httpHeader, sys_context );
require_noerr(err, exit);
err = kConnectionErr;
goto exit;
}else{
require_noerr(err, exit);
err = _LocalConfigRespondInComingMessage( clientFd, httpHeader, sys_context );
require_noerr(err, exit);
}
HTTPHeaderClear( httpHeader );
break;
case EWOULDBLOCK:
// NO-OP, keep reading
break;
case kNoSpaceErr:
system_log("ERROR: Cannot fit HTTPHeader.");
goto exit;
case kConnectionErr:
// NOTE: kConnectionErr from SocketReadHTTPHeader means it's closed
system_log("ERROR: Connection closed.");
goto exit;
default:
system_log("ERROR: HTTP Header parse internal error: %d", err);
goto exit;
}
}
}
exit:
system_log("Exit: Client exit with err = %d", err);
SocketClose(&clientFd);
if( close_client_sem[close_sem_index] != NULL )
{
mico_delete_event_fd( close_client_fd );
mico_rtos_deinit_semaphore( &close_client_sem[close_sem_index] );
close_client_sem[close_sem_index] = NULL;
};
HTTPHeaderDestory( &httpHeader );
mico_rtos_delete_thread(NULL);
return;
}
static OSStatus onReceivedData(struct _HTTPHeader_t * inHeader, uint32_t inPos, uint8_t * inData, size_t inLen, void * inUserContext )
{
OSStatus err = kUnknownErr;
const char * value;
size_t valueSize;
configContext_t *context = (configContext_t *)inUserContext;
mico_logic_partition_t* ota_partition = MicoFlashGetInfo( MICO_PARTITION_OTA_TEMP );
err = HTTPGetHeaderField( inHeader->buf, inHeader->len, "Content-Type", NULL, NULL, &value, &valueSize, NULL );
if(err == kNoErr && strnicmpx( value, valueSize, kMIMEType_MXCHIP_OTA ) == 0){
if( ota_partition->partition_owner == MICO_FLASH_NONE ){
system_log("OTA storage is not exist");
return kUnsupportedErr;
}
if(inPos == 0){
context->offset = 0x0;
CRC16_Init( &context->crc16_contex );
mico_rtos_lock_mutex(&sys_context->flashContentInRam_mutex); //We are write the Flash content, no other write is possible
context->isFlashLocked = true;
err = MicoFlashErase( MICO_PARTITION_OTA_TEMP, 0x0, ota_partition->partition_length);
require_noerr(err, flashErrExit);
err = MicoFlashWrite( MICO_PARTITION_OTA_TEMP, &context->offset, (uint8_t *)inData, inLen);
require_noerr(err, flashErrExit);
CRC16_Update( &context->crc16_contex, inData, inLen);
}else{
err = MicoFlashWrite( MICO_PARTITION_OTA_TEMP, &context->offset, (uint8_t *)inData, inLen);
require_noerr(err, flashErrExit);
CRC16_Update( &context->crc16_contex, inData, inLen);
}
}
else{
return kUnsupportedErr;
}
if(err!=kNoErr) system_log("onReceivedData");
return err;
flashErrExit:
mico_rtos_unlock_mutex(&sys_context->flashContentInRam_mutex);
return err;
}
static void onClearHTTPHeader(struct _HTTPHeader_t * inHeader, void * inUserContext )
{
UNUSED_PARAMETER(inHeader);
configContext_t *context = (configContext_t *)inUserContext;
if(context->isFlashLocked == true){
mico_rtos_unlock_mutex(&sys_context->flashContentInRam_mutex);
context->isFlashLocked = false;
}
}
OSStatus _LocalConfigRespondInComingMessage(int fd, HTTPHeader_t* inHeader, system_context_t * const inContext)
{
OSStatus err = kUnknownErr;
const char * json_str;
uint8_t *httpResponse = NULL;
size_t httpResponseLen = 0;
json_object* report = NULL, *config = NULL;
bool need_reboot = false;
uint16_t crc;
configContext_t *http_context = (configContext_t *)inHeader->userContext;
mico_logic_partition_t* ota_partition = MicoFlashGetInfo( MICO_PARTITION_OTA_TEMP );
char name[50];
json_object *sectors, *sector = NULL;
if(HTTPHeaderMatchURL( inHeader, kCONFIGURLRead ) == kNoErr){
//report = ConfigCreateReportJsonMessage( inContext );
mico_rtos_lock_mutex(&inContext->flashContentInRam_mutex);
snprintf(name, 50, "%s(%c%c%c%c%c%c)",MODEL,
inContext->micoStatus.mac[9], inContext->micoStatus.mac[10],
inContext->micoStatus.mac[12], inContext->micoStatus.mac[13],
inContext->micoStatus.mac[15], inContext->micoStatus.mac[16]);
report = json_object_new_object();
require_action(report, exit, err = kNoMemoryErr);
sectors = json_object_new_array();
require( sectors, exit );
json_object_object_add(report, "T", json_object_new_string("Current Configuration"));
json_object_object_add(report, "N", json_object_new_string(name));
json_object_object_add(report, "C", sectors);
json_object_object_add(report, "PO", json_object_new_string(PROTOCOL));
json_object_object_add(report, "HD", json_object_new_string(HARDWARE_REVISION));
json_object_object_add(report, "FW", json_object_new_string(FIRMWARE_REVISION));
json_object_object_add(report, "RF", json_object_new_string(inContext->micoStatus.rf_version));
#ifdef MICO_CONFIG_SERVER_REPORT_SYSTEM_DATA
/*Sector 1*/
sector = json_object_new_array();
require( sector, exit );
err = config_server_create_sector(sectors, "MICO SYSTEM", sector);
require_noerr(err, exit);
/*name cell*/
err = config_server_create_string_cell(sector, "Device Name", inContext->flashContentInRam.micoSystemConfig.name, "RW", NULL);
require_noerr(err, exit);
//RF power save switcher cell
err = config_server_create_bool_cell(sector, "RF power save", inContext->flashContentInRam.micoSystemConfig.rfPowerSaveEnable, "RW");
require_noerr(err, exit);
//MCU power save switcher cell
err = config_server_create_bool_cell(sector, "MCU power save", inContext->flashContentInRam.micoSystemConfig.mcuPowerSaveEnable, "RW");
require_noerr(err, exit);
/*SSID cell*/
err = config_server_create_string_cell(sector, "Wi-Fi", inContext->flashContentInRam.micoSystemConfig.ssid, "RW", NULL);
require_noerr(err, exit);
/*PASSWORD cell*/
err = config_server_create_string_cell(sector, "Password", inContext->flashContentInRam.micoSystemConfig.user_key, "RW", NULL);
require_noerr(err, exit);
/*DHCP cell*/
err = config_server_create_bool_cell(sector, "DHCP", inContext->flashContentInRam.micoSystemConfig.dhcpEnable, "RW");
require_noerr(err, exit);
/*Local cell*/
err = config_server_create_string_cell(sector, "IP address", inContext->micoStatus.localIp, "RW", NULL);
require_noerr(err, exit);
/*Netmask cell*/
err = config_server_create_string_cell(sector, "Net Mask", inContext->micoStatus.netMask, "RW", NULL);
require_noerr(err, exit);
/*Gateway cell*/
err = config_server_create_string_cell(sector, "Gateway", inContext->micoStatus.gateWay, "RW", NULL);
require_noerr(err, exit);
/*DNS server cell*/
err = config_server_create_string_cell(sector, "DNS Server", inContext->micoStatus.dnsServer, "RW", NULL);
require_noerr(err, exit);
#endif
/*Sector 2*/
sector = json_object_new_array();
require( sector, exit );
err = config_server_create_sector(sectors, "APPLICATION", sector);
require_noerr(err, exit);
config_server_delegate_report( sector, &inContext->flashContentInRam );
mico_rtos_unlock_mutex(&inContext->flashContentInRam_mutex);
json_str = json_object_to_json_string(report);
require_action( json_str, exit, err = kNoMemoryErr );
system_log("Send config object=%s", json_str);
err = CreateSimpleHTTPMessageNoCopy( kMIMEType_JSON, strlen(json_str), &httpResponse, &httpResponseLen );
require_noerr( err, exit );
require( httpResponse, exit );
err = SocketSend( fd, httpResponse, httpResponseLen );
require_noerr( err, exit );
err = SocketSend( fd, (uint8_t *)json_str, strlen(json_str) );
require_noerr( err, exit );
system_log("Current configuration sent");
goto exit;
}
else if(HTTPHeaderMatchURL( inHeader, kCONFIGURLWrite ) == kNoErr){
if(inHeader->contentLength > 0){
system_log("Recv new configuration, apply");
err = CreateSimpleHTTPOKMessage( &httpResponse, &httpResponseLen );
require_noerr( err, exit );
require( httpResponse, exit );
err = SocketSend( fd, httpResponse, httpResponseLen );
require_noerr( err, exit );
config = json_tokener_parse(inHeader->extraDataPtr);
require_action(config, exit, err = kUnknownErr);
system_log("Recv config object=%s", json_object_to_json_string(config));
mico_rtos_lock_mutex(&inContext->flashContentInRam_mutex);
json_object_object_foreach(config, key, val) {
if(!strcmp(key, "Device Name")){
strncpy(inContext->flashContentInRam.micoSystemConfig.name, json_object_get_string(val), maxNameLen);
need_reboot = true;
}else if(!strcmp(key, "RF power save")){
inContext->flashContentInRam.micoSystemConfig.rfPowerSaveEnable = json_object_get_boolean(val);
need_reboot = true;
}else if(!strcmp(key, "MCU power save")){
inContext->flashContentInRam.micoSystemConfig.mcuPowerSaveEnable = json_object_get_boolean(val);
need_reboot = true;
}else if(!strcmp(key, "Wi-Fi")){
strncpy(inContext->flashContentInRam.micoSystemConfig.ssid, json_object_get_string(val), maxSsidLen);
inContext->flashContentInRam.micoSystemConfig.channel = 0;
memset(inContext->flashContentInRam.micoSystemConfig.bssid, 0x0, 6);
inContext->flashContentInRam.micoSystemConfig.security = SECURITY_TYPE_AUTO;
memcpy(inContext->flashContentInRam.micoSystemConfig.key, inContext->flashContentInRam.micoSystemConfig.user_key, maxKeyLen);
inContext->flashContentInRam.micoSystemConfig.keyLength = inContext->flashContentInRam.micoSystemConfig.user_keyLength;
need_reboot = true;
}else if(!strcmp(key, "Password")){
inContext->flashContentInRam.micoSystemConfig.security = SECURITY_TYPE_AUTO;
strncpy(inContext->flashContentInRam.micoSystemConfig.key, json_object_get_string(val), maxKeyLen);
strncpy(inContext->flashContentInRam.micoSystemConfig.user_key, json_object_get_string(val), maxKeyLen);
inContext->flashContentInRam.micoSystemConfig.keyLength = strlen(inContext->flashContentInRam.micoSystemConfig.key);
inContext->flashContentInRam.micoSystemConfig.user_keyLength = strlen(inContext->flashContentInRam.micoSystemConfig.key);
need_reboot = true;
}else if(!strcmp(key, "DHCP")){
inContext->flashContentInRam.micoSystemConfig.dhcpEnable = json_object_get_boolean(val);
need_reboot = true;
}else if(!strcmp(key, "IP address")){
strncpy(inContext->flashContentInRam.micoSystemConfig.localIp, json_object_get_string(val), maxIpLen);
need_reboot = true;
}else if(!strcmp(key, "Net Mask")){
strncpy(inContext->flashContentInRam.micoSystemConfig.netMask, json_object_get_string(val), maxIpLen);
need_reboot = true;
}else if(!strcmp(key, "Gateway")){
strncpy(inContext->flashContentInRam.micoSystemConfig.gateWay, json_object_get_string(val), maxIpLen);
need_reboot = true;
}else if(!strcmp(key, "DNS Server")){
strncpy(inContext->flashContentInRam.micoSystemConfig.dnsServer, json_object_get_string(val), maxIpLen);
need_reboot = true;
}else{
config_server_delegate_recv( key, val, &need_reboot, &inContext->flashContentInRam );
}
}
mico_rtos_unlock_mutex(&inContext->flashContentInRam_mutex);
json_object_put(config);
inContext->flashContentInRam.micoSystemConfig.configured = allConfigured;
mico_system_context_update( &inContext->flashContentInRam );
if( need_reboot == true ){
mico_system_power_perform( &inContext->flashContentInRam, eState_Software_Reset );
}
}
goto exit;
}
else if(HTTPHeaderMatchURL( inHeader, kCONFIGURLWriteByUAP ) == kNoErr){
if(inHeader->contentLength > 0){
system_log( "Recv new configuration from uAP, apply and connect to AP" );
err = ConfigIncommingJsonMessageUAP( fd, (uint8_t *)inHeader->extraDataPtr, inHeader->extraDataLen, inContext );
require_noerr( err, exit );
}
goto exit;
}
else if(HTTPHeaderMatchURL( inHeader, kCONFIGURLOTA ) == kNoErr && ota_partition->partition_owner != MICO_FLASH_NONE){
if(inHeader->contentLength > 0){
system_log("Receive OTA data!");
CRC16_Final( &http_context->crc16_contex, &crc);
memset(&inContext->flashContentInRam.bootTable, 0, sizeof(boot_table_t));
inContext->flashContentInRam.bootTable.length = inHeader->contentLength;
inContext->flashContentInRam.bootTable.start_address = ota_partition->partition_start_addr;
inContext->flashContentInRam.bootTable.type = 'A';
inContext->flashContentInRam.bootTable.upgrade_type = 'U';
inContext->flashContentInRam.bootTable.crc = crc;
mico_system_context_update( &inContext->flashContentInRam );
SocketClose( &fd );
mico_system_power_perform( &inContext->flashContentInRam, eState_Software_Reset );
mico_thread_sleep( MICO_WAIT_FOREVER );
}
goto exit;
}
else{
return kNotFoundErr;
};
exit:
if(inHeader->persistent == false) //Return an err to close socket and exit the current thread
err = kConnectionErr;
if(httpResponse) free(httpResponse);
if(report) json_object_put(report);
if(config) json_object_put(config);
return err;
}