/*
@module fota
@summary 底层固件升级
@version core V0007
@date 2022.05.26
@demo fota
@tag LUAT_USE_FOTA
@usage
-- 如果是从http获取升级包, 那么看demo/fota就可以了
-- 以下是从其他途径获取更新包后, 调用本fota库的基本逻辑
-- 逐段传入
sys.taskInit(function()
fota.init()
while 1 do
local buf = xxx -- 这里是从其他途径获取的升级包片段
-- buf 可以是zbuff 也可以是string
-- 每次写入的数据长度最大不应超过4k
local result, isDone, cache = fota.run(buf)
if not result then
log.info("fota", "出错了")
break
end
if isDone then
while true do
local succ,fotaDone = fota.isDone()
if not succ then
log.info("fota", "出错了")
break
end
if fotaDone then
log.info("fota", "已完成")
break
end
sys.wait(100)
end
break
end
sys.wait(100)
end
end)
-- 使用文件一次性传入
sys.taskInit(function()
fota.init()
fota.file("/xxx") -- 传入具体的路径
end)
*/
#include "luat_base.h"
#include "luat_fota.h"
#include "luat_zbuff.h"
#include "luat_spi.h"
#include "luat_fs.h"
#include "luat_mem.h"
#define LUAT_LOG_TAG "fota"
#include "luat_log.h"
/**
初始化fota流程
@api fota.init(storge_location, len, param1, param2)
@int/string fota数据存储的起始位置
如果是int,则是由芯片平台具体判断
如果是string,则存储在文件系统中
如果为nil,则由底层决定存储位置
@int 数据存储的最大空间
@userdata param1,如果数据存储在spiflash时,为spi_device
@int param2,目前只用于外部flash更新时, spiflash电源控制脚
@return boolean 成功返回true, 失败返回false
@usage
-- 初始化fota流程
local result = fota.init(0, 0x00300000, spi_device) --由于105的flash从0x01000000开始,所以0就是外部spiflash
local result = fota.init() --Air780EXXX系列使用固定内部地址,所以不需要参数了
local result = fota.init(0xe0000000, 0, spi_device, 27) --EC7XX系列允许使用外部flash更新,但是地址必须加上0xe0000000的偏移
*/
static int l_fota_init(lua_State* L)
{
uint32_t address = 0xffffffff;
size_t len = 0;
uint32_t length;
const char *buf = NULL;
luat_spi_device_t* spi_device = NULL;
if (lua_type(L, 1) == LUA_TSTRING)
{
buf = lua_tolstring(L, 1, &len);//取出字符串数据
}
else
{
address = luaL_optinteger(L, 1, 0xffffffff);
}
length = luaL_optinteger(L, 2, 0);
if (lua_isuserdata(L, 3))
{
spi_device = (luat_spi_device_t*)lua_touserdata(L, 3);
}
uint8_t power_pin = luaL_optinteger(L, 4, 0xffffffff);
if (spi_device)
{
spi_device->user_data = &power_pin;
}
lua_pushboolean(L, !luat_fota_init(address, length, spi_device, buf, len));
return 1;
}
/**
等待底层fota流程准备好
@api fota.wait()
@return boolean 是否完整走完流程,true 表示正确走完流程了
@usage
local isDone = fota.wait()
*/
static int l_fota_wait(lua_State* L)
{
lua_pushboolean(L, luat_fota_wait_ready());
return 1;
}
/**
写入fota数据
@api fota.run(buff, offset, len)
@zbuff/string fota数据,尽量用zbuff
@int 起始偏移量,传入zbuff时有效,默认是0
@int 写入长度,传入zbuff时有效,默认是zbuff:used()
@return boolean 有异常返回false,无异常返回true
@return boolean 接收到最后一块返回true
@return int 还未写入的数据量,超过64K必须做等待
@usage
local result, isDone, cache = fota.run(buf) -- 写入fota流程
-- 提示: ,如果传入的是zbuff,写入成功后,请自行清空zbuff内的数据
-- 2024.4.3新增offset, len参数, 仅对zbuff有效
fota.run(buff, 0, 1024)
*/
static int l_fota_write(lua_State* L)
{
int result = 0;
size_t len = 0;
const char *buf = NULL;
if(lua_isuserdata(L, 1))
{
luat_zbuff_t *buff = ((luat_zbuff_t *)luaL_checkudata(L, 1, LUAT_ZBUFF_TYPE));
size_t offset = luaL_optinteger(L, 2, 0);
len = luaL_optinteger(L, 3, buff->used - offset);
if (len + offset > buff->len) {
LLOGE("len too long %d > %d", len, buff->len);
result = -1;
}
else {
result = luat_fota_write(buff->addr + offset, len);
}
}
else
{
buf = lua_tolstring(L, 1, &len);//取出字符串数据
result = luat_fota_write((uint8_t*)buf, len);
}
if (result > 0)
{
lua_pushboolean(L, 1);
lua_pushboolean(L, 0);
}
else if (result == 0)
{
lua_pushboolean(L, 1);
lua_pushboolean(L, 1);
}
else
{
lua_pushboolean(L, 0);
lua_pushboolean(L, 1);
}
lua_pushinteger(L, result);
return 3;
}
/**
从指定文件读取fota数据
@api fota.file(path)
@string 文件路径
@return boolean 有异常返回false,无异常返回true
@return boolean 接收到最后一块返回true
@return int 还未写入的数据量,超过64K必须做等待
@usage
local result, isDone, cache = fota.file("/xxx.bin") -- 写入fota流程
-- 本API于2023.03.23 添加
*/
static int l_fota_file(lua_State* L)
{
int result = 0;
const char *path = luaL_checkstring(L, 1);
FILE* fd = luat_fs_fopen(path, "rb");
if (fd == NULL) {
LLOGE("no such file for FOTA %s", path);
lua_pushboolean(L, 0);
lua_pushboolean(L, 0);
lua_pushinteger(L, 0);
return 3;
}
#define BUFF_SIZE (4096)
char *buff = luat_heap_malloc(BUFF_SIZE);
if (buff == NULL) {
luat_fs_fclose(fd);
LLOGE("out of memory when reading file %s", path);
lua_pushboolean(L, 0);
lua_pushboolean(L, 0);
lua_pushinteger(L, 0);
return 3;
}
int len = 0;
while (1) {
len = luat_fs_fread(buff , BUFF_SIZE, 1, fd);
if (len < 1) {
// EOF 结束了
break;
}
result = luat_fota_write((uint8_t*)buff, len);
if (result < 0) {
break;
}
}
luat_heap_free(buff);
luat_fs_fclose(fd);
if (result == 0) {
LLOGI("fota file write done, call fota.done()");
result = luat_fota_done();
if (result == 0) {
LLOGI("fota done success, call fota.end()");
result = luat_fota_end(1);
}
else {
LLOGE("fota done fail %d", result);
}
}
if (result > 0)
{
lua_pushboolean(L, 1);
lua_pushboolean(L, 0);
}
else if (result == 0)
{
lua_pushboolean(L, 1);
lua_pushboolean(L, 1);
}
else
{
lua_pushboolean(L, 0);
lua_pushboolean(L, 1);
}
lua_pushinteger(L, result);
return 3;
}
/**
等待底层fota流程完成
@api fota.isDone()
@return boolean 有异常返回false,无异常返回true
@return boolean 写入到最后一块返回true
@usage
local result, isDone = fota.isDone()
*/
static int l_fota_done(lua_State* L)
{
int result = luat_fota_done();
if (result > 0)
{
lua_pushboolean(L, 1);
lua_pushboolean(L, 0);
}
else if (result == 0)
{
lua_pushboolean(L, 1);
lua_pushboolean(L, 1);
}
else
{
lua_pushboolean(L, 0);
lua_pushboolean(L, 1);
}
return 2;
}
/**
结束fota流程
@api fota.finish(is_ok)
@boolean 是否完整走完流程,true 表示正确走完流程了
@return boolean 成功返回true, 失败返回false
@usage
-- 结束fota流程
local result = fota.finish(true)
*/
static int l_fota_end(lua_State* L)
{
lua_pushboolean(L, !luat_fota_end(lua_toboolean(L, 1)));
return 1;
}
#include "rotable2.h"
static const rotable_Reg_t reg_fota[] =
{
{ "init", ROREG_FUNC(l_fota_init)},
{ "wait", ROREG_FUNC(l_fota_wait)},
{ "run", ROREG_FUNC(l_fota_write)},
{ "isDone", ROREG_FUNC(l_fota_done)},
{ "finish", ROREG_FUNC(l_fota_end)},
{ "file", ROREG_FUNC(l_fota_file)},
{ NULL, ROREG_INT(0) }
};
LUAMOD_API int luaopen_fota( lua_State *L ) {
luat_newlib2(L, reg_fota);
return 1;
}