stm32_ota/HARDWARE/LTE/EC20/ec20ftp.c

402 lines
16 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "ec20ftp.h"
#include <stdlib.h>
#include "syslib.h" //#define STR2(R) STR1(R)
/********************************************************************************
* @file ec20ftp.c
* @author 江苏九比特信息系统有限公司 Mr.Wang
* @version V1.0.0
* @date 11-Dec-2018
* @brief 提供Quectel模块EC20关于FTP硬件驱动程序
******************************************************************************
* @attention
* 约定基本名词如下:
* contextID:链路ID
* EC20模块链路ID范围1~16每一个链路ID都会对应一个本地IP
* 本驱动强制规定FTP协议默认只用一个链路ID即contextID=3用于ftp链路。
*******************************************************************************/
/*****************************************
*内部使用的常变量定义
****************************************/
uint8_t ftpLocalIp[MAX_IP_LEN] ; //FTP链路本地IPP
#define FTP_CMDPACK_LEN 256 //FTP命令的最大长度
/********************************************************
ec20模块FTP相关AT指令处理
*********************************************************/
enum eFtpCmdNum
{
SETCONTEXTID =0, SETACCOUNT =1, SETFILETYPE =2, SETTRANSMODE =3, SETRSTIMEOUT =4,
FTPLOGIN =5, GETLOGINSTATE =6, SETDIRECTORY =7, GETDIRECTORY =8, LISTFILENAME =9,
GETFILESIZE =10, DOWNFILE =11, GETHASDOWNSIZE =12, RENAMEFILE =13, FTPLOGOUT = 14
} ;//枚举ec20模块FTP相关指令枚举
volatile EC20_CMD_DATA_s sFtpCmd[15]=
{
// cmdNum cmdStr, timeout(100ms), trueStr, trueOffset falseStr revResult rtyNum
{SETCONTEXTID, "AT+QFTPCFG=\"contextid\",%d\r\n", 10, "OK", -1, "ERROR", TIMEOUT, 2 }, //手册回码等待150S
{SETACCOUNT, "AT+QFTPCFG=\"account\",\"%s\",\"%s\"\r\n",10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //账户、密码
{SETFILETYPE, "AT+QFTPCFG=\"filetype\",%d\r\n" , 10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //文件类型
{SETTRANSMODE, "AT+QFTPCFG=\"transmode\",%d\r\n" , 10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //传输模式
{SETRSTIMEOUT, "AT+QFTPCFG=\"rsptimeout\",%d\r\n" , 10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //返回超时时间
{FTPLOGIN, "AT+QFTPOPEN=\"%s\",%d\r\n", (20*10), "OK", -1, "ERROR", TIMEOUT, 2 }, //FTP登录
{GETLOGINSTATE, "AT+QFTPSTAT\r\n", 10, "+QFTPSTAT: 0", -1, "ERROR", TIMEOUT, 2 }, //0Opening 1idle 2Transferring 3Closing 4Closed
{SETDIRECTORY, "AT+QFTPCWD=\"%s\"\r\n" , 10, "+QFTPCWD: 0,0", -1, "ERROR", TIMEOUT, 2 }, //ftp目录路径
{GETDIRECTORY, "AT+QFTPPWD\r\n" , 10, "+QFTPPWD: 0", -1, "ERROR", TIMEOUT, 2 }, //传输模式
{LISTFILENAME, "AT+QFTPNLST=\"%s\"\r\n" , 10, "+QFTPNLST: 0", -1, "ERROR", TIMEOUT, 2 }, //ftp目录路径 列出FTP目录中所有文件的文件名字
{GETFILESIZE, "AT+QFTPSIZE=\"%s\"\r\n" , 10, "+QFTPSIZE: 0", -1, "ERROR", TIMEOUT, 2 }, //文件名 获取文件名文件大小:+QFTPSIZE: 0,36048
{DOWNFILE, "AT+QFTPGET=\"%s\",\"COM:\",%d,%d\r\n" , 40, "\r\nOK\r\n\r\n+QFTPGET: 0", -1, "ERROR", TIMEOUT, 1 }, //文件名+起始偏移量+下载字节数 下载文件
{GETHASDOWNSIZE, "AT+QFTPLEN\r\n" , 10, "+OK", -1, "ERROR", TIMEOUT, 2 }, //ftp目录路径 列出FTP目录中所有文件的文件名字
{RENAMEFILE, "AT+QFTPRENAME=\"%s\",\"%s\"\r\n" , 20, "+QFTPRENAME: 0", -1, "ERROR", TIMEOUT, 2 }, //旧文件名+新文件名
{FTPLOGOUT, "AT+QFTPCLOSE\r\n", (2*10), "+QFTPCLOSE", -1, "ERROR", TIMEOUT, 2 }, //FTP退出登录
} ; //EC20模块module相关指令的EC20_CMD_DATA_s结构体类型参数
/**************************************************************************************************
* 名 称: static const char *FtpCmdNumToString(enum eFtpCmdNum result)
* 功能说明: 输出枚举成员名的字符串指针。
* 入口参数: eFtpCmdNum类型的枚举
* 出口参数: 为枚举的成员名字符串指针
**************************************************************************************************/
static inline const char *FtpCmdNumToString(enum eFtpCmdNum result)
{
switch (result)
{
ENUM_CHIP_TYPE_CASE(SETCONTEXTID)
ENUM_CHIP_TYPE_CASE(SETACCOUNT)
ENUM_CHIP_TYPE_CASE(SETFILETYPE)
ENUM_CHIP_TYPE_CASE(SETTRANSMODE)
ENUM_CHIP_TYPE_CASE(SETRSTIMEOUT)
ENUM_CHIP_TYPE_CASE(FTPLOGIN)
ENUM_CHIP_TYPE_CASE(GETLOGINSTATE)
ENUM_CHIP_TYPE_CASE(SETDIRECTORY)
ENUM_CHIP_TYPE_CASE(GETDIRECTORY)
ENUM_CHIP_TYPE_CASE(LISTFILENAME)
ENUM_CHIP_TYPE_CASE(GETFILESIZE)
ENUM_CHIP_TYPE_CASE(DOWNFILE)
ENUM_CHIP_TYPE_CASE(GETHASDOWNSIZE)
ENUM_CHIP_TYPE_CASE(RENAMEFILE)
ENUM_CHIP_TYPE_CASE(FTPLOGOUT)
}
return "无此命令";
}
/**************************************************************************************************
* 名 称: RunResult Ftp_Config( void )
* 功能说明: 对FTP相关参数进行配置
* 入口参数:
* @param1 *psFtp FtpP_s结构体变量指针
* 出口参数:
* @param1 runResult RunResult枚举类型变量返回函数运行结果
**************************************************************************************************/
RunResult Ftp_Config( FtpP_s *psFtp )
{
RunResult runResult = TIMEOUT ;
runResult = EC20_SendFtpCmd(SETCONTEXTID, NULL, psFtp->contextId ) ;
if( runResult != RUNOK )
return runResult ;
runResult = EC20_SendFtpCmd(SETACCOUNT, NULL, psFtp->userName, psFtp->password ) ;
AppLogPrintf("配置FTP服务器Username:%s, Password:%s .", psFtp->userName, psFtp->password ) ;
if( runResult != RUNOK )
return runResult ;
EC20_SendFtpCmd(SETFILETYPE, NULL, psFtp->eFiletype ) ;
EC20_SendFtpCmd(SETTRANSMODE, NULL, psFtp->eTransmode ) ;
EC20_SendFtpCmd(SETRSTIMEOUT, NULL, psFtp->rsptimeout ) ;
return runResult ;
}
/**************************************************************************************************
* 名 称: RunResult Ftp_PDP_Init( FtpP_s *psFtp )
* 功能说明: 初始化FTP链路
* 出口参数:
* @param1 runResult RunResult枚举类型变量返回函数运行结果
**************************************************************************************************/
RunResult Ftp_PDP_Init( FtpP_s *psFtp )
{
RunResult runResult = TIMEOUT ;
uint8_t *ftpLocalIp = portMalloc(MAX_IP_LEN) ;
runResult = Query_Context( psFtp->contextId, ftpLocalIp ) ; //查询psFtp->contextId是否激活
if( RUNOK == runResult ) /*psFtp->contextId已激活*/ //去激活->再次激活
{
// runResult = Deact_Context(HTTP_CONTEXTID) ;
// if( RUNOK != runResult ) /*HTTP_CONTEXTID去激活失败*/ //直接返回错误
// {
// return RUNERR ;
// }
return RUNOK ;
}
runResult = ActivePDP(psFtp->contextId, ftpLocalIp) ;
if( RUNOK == runResult )
{
AppLogPrintf("FTP本地IP%s", ftpLocalIp) ;
}
portFree(ftpLocalIp) ;
return(runResult) ;
}
/**************************************************************************************************
* 名 称: Ftp_Login( FtpP_s *psFtp )
* 功能说明: 登录FTP服务器
* 入口参数:
* @param1 *psFtp FtpP_s结构体变量指针
* 出口参数:
* @param1 runResult RunResult枚举类型变量返回函数运行结果
**************************************************************************************************/
RunResult Ftp_Login(FtpP_s *psFtp)
{
RunResult runResult = TIMEOUT ;
runResult = EC20_SendFtpCmd(FTPLOGIN, NULL, psFtp->ftpServerIP, psFtp->ftpServerPort ) ;
if( runResult != RUNOK )
return runResult ;
int checkCsTimes = 10 ; //10*2S等待FTP进入idle状态
while( checkCsTimes-- )
{
Wait_For_Nms(30) ;
runResult = EC20_SendFtpCmd(GETLOGINSTATE, NULL ) ;
if( RUNOK == runResult )
{
if( (sFtpCmd[GETLOGINSTATE].trueOffset > 0)&&
(ec20AtBuf[sFtpCmd[GETLOGINSTATE].trueOffset+13] == 0x31))
{
runResult =RUNOK ;
break;
}
else
{
runResult =RUNERR ;
}
}
}
return (runResult) ;
}
/**************************************************************************************************
* 名 称: Ftp_Logout( FtpP_s *psFtp )
* 功能说明: 登出FTP服务器
* 入口参数:
* @param1 *psFtp FtpP_s结构体变量指针
* 出口参数:
* @param1 runResult RunResult枚举类型变量返回函数运行结果
**************************************************************************************************/
RunResult Ftp_Logout(FtpP_s *psFtp)
{
RunResult runResult = TIMEOUT ;
runResult = EC20_SendFtpCmd(FTPLOGOUT, NULL ) ;
return runResult ;
}
/**************************************************************************************************
* 名 称: Ftp_Set_Dir( FtpP_s *psFtp )
* 功能说明: 设置Ftp操作目录
* 入口参数:
* @param1 *psFtp FtpP_s结构体变量指针
* 出口参数:
* @param1 runResult RunResult枚举类型变量返回函数运行结果
**************************************************************************************************/
RunResult Ftp_Set_Dir( FtpP_s *psFtp )
{
RunResult runResult = TIMEOUT ;
runResult = EC20_SendFtpCmd(SETDIRECTORY, NULL, psFtp->ftpDirectory ) ; //设置用户目录
if( runResult != RUNOK )
return runResult ;
runResult = EC20_SendFtpCmd(GETDIRECTORY, NULL ) ;
if( RUNOK == runResult )
{
if( (sFtpCmd[GETDIRECTORY].trueOffset > 0)&&
(kmp(ec20AtBuf, (const char*)psFtp->ftpDirectory) > 0)) //确认查询到的目录为用户设置的目录
{
runResult =RUNOK ;
}
else
{
runResult =RUNERR ;
}
}
return (runResult) ;
}
/**************************************************************************************************
* 名 称: RunResult Ftp_Find_File( uint8_t *dir, uint8_t *fileName )
* 功能说明: 在dir目录中查找名为fileName的文件
* 入口参数:
* @param1 *dir 查找的目录
* @param2 *fileName 所查找的文件文件名
* 出口参数:
* @param1 RUNOK 在目录中能找到文件
* @param2 RUNTIMEOUT 在目录中不能找到文件
* @param3 RUNERR 列出dir目录中文件名出错
* 说 明: ec20AtBuf[EC20_ATBUF_LEN] 256字节 所以当dir中文件名总字节数大于(EC20_ATBUF_LEN-50)时也会出现查找失败。
**************************************************************************************************/
RunResult Ftp_Find_File( uint8_t *dir, uint8_t *fileName )
{
if( RUNOK != EC20_SendFtpCmd(LISTFILENAME, NULL, dir ))
return RUNERR ;
if( kmp(ec20AtBuf, (const char*)fileName) > 0 )
return RUNOK ;
else
return TIMEOUT ;
}
/**************************************************************************************************
* 名 称: u32 Ftp_Get_FileSize( uint8_t *fileName )
* 功能说明: 查询*fileName 文件的大小
* 入口参数:
* @param1 *fileName 索要查询文件大小的文件名
* 出口参数:
* @param1 u32 返回文件大小bytes
**************************************************************************************************/
u32 Ftp_Get_FileSize( uint8_t *fileName )
{
RunResult runResult = TIMEOUT ;
u32 fileSize = 0 ;
uint8_t fileSizeBuf[7] = {0} ;
runResult = EC20_SendFtpCmd(GETFILESIZE, NULL, fileName ) ; //设置用户目录
if( runResult != RUNOK )
return 0 ;
CopyValues(fileSizeBuf, (uint8_t*)&ec20AtBuf[ sFtpCmd[GETFILESIZE].trueOffset+13], 0x0D, 6) ;
fileSize = atoi((const char*)fileSizeBuf) ;
return (fileSize) ;
}
/**************************************************************************************************
* 名 称: RunResult Ftp_File_Rename( uint8_t *oldName, uint8_t *newName )
* 功能说明: 文件重命名
* 入口参数:
* @param1 *oldName 旧文件名
* @param2 *newName 新文件名
* 出口参数:
* @param1 runResult RunResult枚举类型数据
**************************************************************************************************/
RunResult Ftp_File_Rename( uint8_t *oldName, uint8_t *newName )
{
RunResult runResult = TIMEOUT ;
runResult = EC20_SendFtpCmd(RENAMEFILE, NULL, oldName, newName ) ; //
return (runResult) ;
}
/**************************************************************************************************
* 名 称: int Ftp_Down_File( uint8_t *fileName, uint32_t startPos, uint16_t transLen)
* 功能说明: 从名为fileName的文件startPos位置开始下载transLen长度的数据
* 入口参数:
* @param1 *fileName 下载的文件文件名
* @param2 startPos 开始下载的位置
* @param3 transLen 下载的字节数
* 出口参数:
* @param1 headPos 如果校验正确的话返回"CONNECT"在ec20FtpBuf内的偏移地址 错误返回-1
* 注 意下载文件时EC20串口模式切换到FTP_MODE,届时接收数据将会存储在ec20FtpBuf缓冲区中
* ec20FtpBuf数据校验方法是判断CONNECT和+QFTPGET: 0,2048之间的字节数是否为我们请求的字节数
* “CONNECT 0x0D 0x0A 数据 0x0D 0x0A OK 0x0D 0x0A 0x0D 0x0A +QFTPGET: 0,2048”
**************************************************************************************************/
int Ftp_Down_File( uint8_t *fileName, uint32_t startPos, uint16_t transLen)
{
int headPos = -1 ;
uint8_t revTimes = 0 ;
ftpDataMode = true ; //EC20串口接收模式切换到FTP模式
Ec20FtpBufReset() ; //FTP接收缓冲区初始化
UARTx_SendString(EC20_UART, (uint8_t *)sFtpCmd[DOWNFILE].cmdStr, fileName, startPos, transLen ) ;
while( revTimes++ < sFtpCmd[DOWNFILE].timeout*2 )
{
Wait_For_Nms(15) ;
headPos = kmp(ec20FtpBuf, "CONNECT")+9 ; //下载的文件数据开始的地方相对用户数据开始处 在ec20FtpBuf中的偏移量
sFtpCmd[DOWNFILE].trueOffset = kmp(&ec20FtpBuf[transLen+headPos], sFtpCmd[DOWNFILE].trueStr) ; //+QFTPGET: 0,2048在相对用户数据结尾处 在ec20FtpBuf中的偏移量
if( sFtpCmd[DOWNFILE].trueOffset == 0)
{
if( headPos >= 0 ) //CONNECT和+QFTPGET: 0,2048之间的字节数 是否正常
{
break ;
}
else
{
headPos = -1 ;
break ;
}
}
else if( kmp(ec20FtpBuf, sFtpCmd[DOWNFILE].falseStr) >= 0)
{
headPos = -1 ;
break ;
}
}
ftpDataMode = false ; //EC20串口接收模式切出FTP模式
return (headPos) ;
}
/**************************************************************************************************
* 名 称: RunResult EC20_SendFtpCmd( uint8_t cmdNum, char *format,... )
* 功能说明: MCU串口向EC20发送Tcp相关命令
* 入口参数:
* @param1 cmdNum EC20_CMD_DATA_s中cmdNum成员命令编号
* @param2 char *format,... 可变参变量
* 出口参数:
* @param1 status RunResult枚举类型变量返回函数运行结果
**************************************************************************************************/
RunResult EC20_SendFtpCmd( uint8_t cmdNum, char *format,... )
{
uint8_t revTimes = 0 ;
RunResult status = TIMEOUT ;
uint8_t retryTimes = sFtpCmd[cmdNum].rtyNum ;
char *cmdPack = NULL ;
format = sFtpCmd[cmdNum].cmdStr ;
cmdPack = portMalloc(FTP_CMDPACK_LEN*sizeof(uint8_t)) ;
if( cmdPack == NULL )
{
ErrorLogPrintf("EC20 FtpCmdPack 内存分配失败!") ;
portFree(cmdPack) ;
return RUNERR ;
}
va_list ap;
va_start (ap, format);
int outLen = vsnprintf(cmdPack, FTP_CMDPACK_LEN, (const char*)format, ap); //vsprintf (temp, cmd, ap); //到此为止所有的参数情况已经汇总到temp了
if((outLen<=0)||( outLen > FTP_CMDPACK_LEN))
{
ErrorLogPrintf("FTP cmdPack 溢出!--增加FTP_CMDPACK_LEN数值。") ;
status = RUNERR ;
goto tcpCmdOut ;
}
while(retryTimes--)
{
Ec20AtBufReset() ;
revTimes = 0 ;
UARTx_SendData(EC20_UART, cmdPack, outLen ) ; //DMA发送
while( revTimes++ < sFtpCmd[cmdNum].timeout )
{
Wait_For_Nms(100) ;
sFtpCmd[cmdNum].trueOffset = kmp(ec20AtBuf, sFtpCmd[cmdNum].trueStr) ;
if( sFtpCmd[cmdNum].trueOffset >= 0)
{
status = RUNOK ;
goto tcpCmdOut ;
}
else if( kmp(ec20AtBuf, sFtpCmd[cmdNum].falseStr) >= 0)
{
status = RUNERR ;
goto tcpCmdOut ;
}
}
Wait_For_Nms( 1000 ) ;
}
tcpCmdOut:portFree(cmdPack) ;
va_end(ap) ;
DebugLogPrintf("%s %s\r\n[%s]", FtpCmdNumToString((enum eFtpCmdNum)cmdNum), RunResultToString(status), ec20AtBuf ) ;
return (status) ;
}