// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
//
// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
# include "esp8266.h"
# include "cmd.h"
# include "crc16.h"
# include "uart.h"
# ifdef CMD_DBG
# define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
# else
# define DBG(format, ...) do { } while(0)
# endif
//===== ESP -> Serial responses
static void ICACHE_FLASH_ATTR
cmdProtoWrite ( uint8_t data ) {
switch ( data ) {
case SLIP_END :
uart0_write_char ( SLIP_ESC ) ;
uart0_write_char ( SLIP_ESC_END ) ;
break ;
case SLIP_ESC :
uart0_write_char ( SLIP_ESC ) ;
uart0_write_char ( SLIP_ESC_ESC ) ;
break ;
default :
uart0_write_char ( data ) ;
}
}
static void ICACHE_FLASH_ATTR
cmdProtoWriteBuf ( const uint8_t * data , short len ) {
while ( len - - ) cmdProtoWrite ( * data + + ) ;
}
static uint16_t resp_crc ;
// Start a response, returns the partial CRC
void ICACHE_FLASH_ATTR
cmdResponseStart ( uint16_t cmd , uint32_t value , uint16_t argc ) {
DBG ( " cmdResponse: cmd=%d val=%d argc=%d \n " , cmd , value , argc ) ;
uart0_write_char ( SLIP_END ) ;
cmdProtoWriteBuf ( ( uint8_t * ) & cmd , 2 ) ;
resp_crc = crc16_data ( ( uint8_t * ) & cmd , 2 , 0 ) ;
cmdProtoWriteBuf ( ( uint8_t * ) & argc , 2 ) ;
resp_crc = crc16_data ( ( uint8_t * ) & argc , 2 , resp_crc ) ;
cmdProtoWriteBuf ( ( uint8_t * ) & value , 4 ) ;
resp_crc = crc16_data ( ( uint8_t * ) & value , 4 , resp_crc ) ;
}
// Adds data to a response, returns the partial CRC
void ICACHE_FLASH_ATTR
cmdResponseBody ( const void * data , uint16_t len ) {
cmdProtoWriteBuf ( ( uint8_t * ) & len , 2 ) ;
resp_crc = crc16_data ( ( uint8_t * ) & len , 2 , resp_crc ) ;
cmdProtoWriteBuf ( data , len ) ;
resp_crc = crc16_data ( data , len , resp_crc ) ;
uint16_t pad = ( 4 - ( ( len + 2 ) & 3 ) ) & 3 ; // get to multiple of 4
if ( pad > 0 ) {
uint32_t temp = 0 ;
cmdProtoWriteBuf ( ( uint8_t * ) & temp , pad ) ;
resp_crc = crc16_data ( ( uint8_t * ) & temp , pad , resp_crc ) ;
}
}
// Ends a response
void ICACHE_FLASH_ATTR
cmdResponseEnd ( ) {
cmdProtoWriteBuf ( ( uint8_t * ) & resp_crc , 2 ) ;
uart0_write_char ( SLIP_END ) ;
}
//===== serial -> ESP commands
// Execute a parsed command
static void ICACHE_FLASH_ATTR
cmdExec ( const CmdList * scp , CmdPacket * packet ) {
// Iterate through the command table and call the appropriate function
while ( scp - > sc_function ! = NULL ) {
if ( scp - > sc_name = = packet - > cmd ) {
DBG ( " cmdExec: Dispatching cmd=%s \n " , scp - > sc_text ) ;
// call command function
scp - > sc_function ( packet ) ;
return ;
}
scp + + ;
}
DBG ( " cmdExec: cmd=%d not found \n " , packet - > cmd ) ;
}
// Parse a packet and print info about it
void ICACHE_FLASH_ATTR
cmdParsePacket ( uint8_t * buf , short len ) {
// minimum command length
if ( len < sizeof ( CmdPacket ) ) return ;
// init pointers into buffer
CmdPacket * packet = ( CmdPacket * ) buf ;
uint8_t * data_ptr = ( uint8_t * ) & packet - > args ;
uint8_t * data_limit = data_ptr + len ;
DBG ( " cmdParsePacket: cmd=%d argc=%d value=%u \n " ,
packet - > cmd ,
packet - > argc ,
packet - > value
) ;
#if 0
// print out arguments
uint16_t argn = 0 ;
uint16_t argc = packet - > argc ;
while ( data_ptr + 2 < data_limit & & argc - - ) {
short l = * ( uint16_t * ) data_ptr ;
os_printf ( " cmdParsePacket: arg[%d] len=%d: " , argn + + , l ) ;
data_ptr + = 2 ;
while ( data_ptr < data_limit & & l - - ) {
os_printf ( " %02X " , * data_ptr + + ) ;
}
os_printf ( " \n " ) ;
}
# endif
if ( ! cmdInSync & & packet - > cmd ! = CMD_SYNC ) {
// we have not received a sync, perhaps we reset? Tell MCU to do a sync
cmdResponseStart ( CMD_SYNC , 0 , 0 ) ;
cmdResponseEnd ( ) ;
} else if ( data_ptr < = data_limit ) {
cmdExec ( commands , packet ) ;
} else {
DBG ( " cmdParsePacket: packet length overrun, parsing arg %d \n " , packet - > argc ) ;
}
}
//===== Helpers to parse a command packet
// Fill out a CmdRequest struct given a CmdPacket
void ICACHE_FLASH_ATTR
cmdRequest ( CmdRequest * req , CmdPacket * cmd ) {
req - > cmd = cmd ;
req - > arg_num = 0 ;
req - > arg_ptr = ( uint8_t * ) & cmd - > args ;
}
// Return the number of arguments given a command struct
uint32_t ICACHE_FLASH_ATTR
cmdGetArgc ( CmdRequest * req ) {
return req - > cmd - > argc ;
}
// Copy the next argument from a command structure into the data pointer, returns 0 on success
// -1 on error
int32_t ICACHE_FLASH_ATTR
cmdPopArg ( CmdRequest * req , void * data , uint16_t len ) {
uint16_t length ;
if ( req - > arg_num > = req - > cmd - > argc )
return - 1 ;
length = * ( uint16_t * ) req - > arg_ptr ;
if ( length ! = len ) return - 1 ; // safety check
req - > arg_ptr + = 2 ;
os_memcpy ( data , req - > arg_ptr , length ) ;
req - > arg_ptr + = ( length + 3 ) & ~ 3 ; // round up to multiple of 4
req - > arg_num + + ;
return 0 ;
}
// Skip the next argument
void ICACHE_FLASH_ATTR
cmdSkipArg ( CmdRequest * req ) {
uint16_t length ;
if ( req - > arg_num > = req - > cmd - > argc ) return ;
length = * ( uint16_t * ) req - > arg_ptr ;
req - > arg_ptr + = 2 ;
req - > arg_ptr + = ( length + 3 ) & ~ 3 ;
req - > arg_num + + ;
}
// Return the length of the next argument
uint16_t ICACHE_FLASH_ATTR
cmdArgLen ( CmdRequest * req ) {
return * ( uint16_t * ) req - > arg_ptr ;
}