Merge branch 'main' into bugfix/cli-offset

This commit is contained in:
Philippe Teuwen
2023-08-19 01:18:42 +02:00
committed by GitHub
16 changed files with 237 additions and 39 deletions
+11
View File
@@ -309,3 +309,14 @@ in a second terminal:
```
JLinkRTTClient
```
## Using SWO pin as UART to monitor NRF_LOG
One can set `NRF_LOG_UART_ON_SWO_ENABLED := 1` in `Makefile.defs` to activate this functionality.
When activated, NRF_LOG will be available if one connects a UART bridge to the SWO pin which will work as a UART TX pin.
UART works at 115200 bauds. E.g. one can use a FTDI dongle and `screen /dev/ttyUSB0 115200`.
Contrary to RTT that needs to be activated by a JTAG probe, UART logs are immediately available.
Limitations:
* SWO pin is shared with... SWO so when e.g. reflashing the device, garbage may appear on the monitoring terminal.
* SWO pin is also shared with the blue channel of the RGB slot LEDs, so faint blue may appear briefly when logs are sent and LED might not work properly when supposed to be blue.
+3
View File
@@ -27,3 +27,6 @@ CURRENT_DEVICE_TYPE ?= ${CHAMELEON_ULTRA}
# Versioning information
GIT_VERSION := "$(shell git describe --abbrev=7 --dirty --always --tags)"
# Enable NRF_LOG on SWO pin as UART TX
NRF_LOG_UART_ON_SWO_ENABLED := 0
+20
View File
@@ -17,6 +17,7 @@ SRC_FILES += \
$(PROJ_DIR)/rgb_marquee.c \
$(PROJ_DIR)/bsp/bsp_delay.c \
$(PROJ_DIR)/bsp/bsp_time.c \
$(PROJ_DIR)/bsp/bsp_wdt.c \
$(PROJ_DIR)/rfid/crc_utils.c \
$(PROJ_DIR)/rfid/hex_utils.c \
$(PROJ_DIR)/rfid/mf1_crapto1.c \
@@ -30,6 +31,7 @@ SRC_FILES += \
$(PROJ_DIR)/rfid/nfctag/hf/nfc_ntag.c \
$(PROJ_DIR)/rfid/nfctag/lf/lf_tag_em.c \
$(PROJ_DIR)/utils/dataframe.c \
$(PROJ_DIR)/utils/delayed_reset.c \
$(PROJ_DIR)/utils/fds_util.c \
$(PROJ_DIR)/utils/syssleep.c \
$(PROJ_DIR)/utils/timeslot.c \
@@ -93,6 +95,7 @@ SRC_FILES += \
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_spi.c \
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_rng.c \
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_ppi.c \
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_wdt.c \
$(SDK_ROOT)/external/segger_rtt/SEGGER_RTT.c \
$(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_Syscalls_GCC.c \
$(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_printf.c \
@@ -288,6 +291,23 @@ else
$(error Chameleon <Application>: No device type define.)
endif
ifeq (${NRF_LOG_UART_ON_SWO_ENABLED}, 1)
SRC_FILES += \
$(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_uart.c \
$(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_uart.c \
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_uarte.c
CFLAGS += -DNRFX_UARTE_ENABLED=1 -DNRFX_UARTE0_ENABLED=1
CFLAGS += -DNRF_LOG_BACKEND_UART_ENABLED=1
#define NRF_GPIO_PIN_MAP(port, pin) (((port) << 5) | ((pin) & 0x1F))
#define NRF_LOG_BACKEND_UART_TX_PIN NRF_GPIO_PIN_MAP(1, 0)
CFLAGS += -DNRF_LOG_BACKEND_UART_TX_PIN=32
# 30801920 = 115200 baud
CFLAGS += -DNRF_LOG_BACKEND_UART_BAUDRATE=30801920
CFLAGS += -DNRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE=64
$(info Chameleon <Application>: enable NRF_LOG on UART via SWO pin.)
endif
# Optimization flags
OPT = -O3 -g3
+9 -2
View File
@@ -13,6 +13,7 @@
#include "tag_persistence.h"
#include "nrf_pwr_mgmt.h"
#include "settings.h"
#include "delayed_reset.h"
#define NRF_LOG_MODULE_NAME app_cmd
@@ -357,6 +358,13 @@ data_frame_tx_t* cmd_processor_get_slot_info(uint16_t cmd, uint16_t status, uint
return data_frame_make(cmd, STATUS_DEVICE_SUCCESS, 16, slot_info);
}
data_frame_tx_t* cmd_processor_wipe_fds(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
bool success = fds_wipe();
status = success ? STATUS_DEVICE_SUCCESS : STATUS_FLASH_WRITE_FAIL;
delayed_reset(50);
return data_frame_make(cmd, status, 0, NULL);
}
data_frame_tx_t* cmd_processor_set_em410x_emu_id(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length == LF_EM410X_TAG_ID_SIZE) {
tag_data_buffer_t* buffer = get_buffer_by_tag_type(TAG_TYPE_EM410X);
@@ -473,7 +481,6 @@ data_frame_tx_t* cmd_processor_set_mf1_anti_collision_res(uint16_t cmd, uint16_t
}
data_frame_tx_t* cmd_processor_set_slot_tag_nick_name(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
// one chinese have 2byte(gbk).
if (length > 34 || length < 3) {
status = STATUS_PAR_ERR;
} else {
@@ -498,7 +505,6 @@ data_frame_tx_t* cmd_processor_set_slot_tag_nick_name(uint16_t cmd, uint16_t sta
}
data_frame_tx_t* cmd_processor_get_slot_tag_nick_name(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
// one chinese have 2byte(gbk).
if (length != 2) {
status = STATUS_PAR_ERR;
} else {
@@ -607,6 +613,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_SLOT_DATA_CONFIG_SAVE, NULL, cmd_processor_slot_data_config_save, NULL },
{ DATA_CMD_GET_ACTIVE_SLOT, NULL, cmd_processor_get_activated_slot, NULL },
{ DATA_CMD_GET_SLOT_INFO, NULL, cmd_processor_get_slot_info, NULL },
{ DATA_CMD_WIPE_FDS, NULL, cmd_processor_wipe_fds, NULL },
{ DATA_CMD_SET_EM410X_EMU_ID, NULL, cmd_processor_set_em410x_emu_id, NULL },
+4 -1
View File
@@ -27,6 +27,7 @@ NRF_LOG_MODULE_REGISTER();
#include "ble_main.h"
#include "bsp_delay.h"
#include "bsp_time.h"
#include "bsp_wdt.h"
#include "dataframe.h"
#include "fds_util.h"
#include "hex_utils.h"
@@ -38,7 +39,6 @@ NRF_LOG_MODULE_REGISTER();
#include "settings.h"
// Defining soft timers
APP_TIMER_DEF(m_button_check_timer); // Timer for button debounce
static bool m_is_read_btn_press = false;
@@ -545,6 +545,7 @@ int main(void) {
// usbd event listener
APP_ERROR_CHECK(app_usbd_power_events_enable());
bsp_wdt_init();
// Enter main loop.
NRF_LOG_INFO("Chameleon working");
while (1) {
@@ -558,6 +559,8 @@ int main(void) {
while (NRF_LOG_PROCESS());
// USB event process
while (app_usbd_event_queue_process());
// WDT refresh
bsp_wdt_feed();
// No task to process, system sleep enter.
// If system idle sometime, we can enter deep sleep state.
// Some task process done, we can enter cpu sleep state.
+30
View File
@@ -0,0 +1,30 @@
#include "nrf_drv_wdt.h"
#include "hw_connect.h"
#include "nrf_gpio.h"
static nrf_drv_wdt_channel_id m_channel_id;
static void wdt_event_handler(void)
{
//NOTE: The max amount of time we can spend in WDT interrupt is two cycles of 32768[Hz] clock - after that, reset occurs
uint32_t* p_led_array = hw_get_led_array();
for (uint8_t i = 0; i < RGB_LIST_NUM; i++) {
nrf_gpio_pin_clear(p_led_array[i]);
}
}
void bsp_wdt_init(void) {
ret_code_t err_code;
// err_code = nrf_drv_clock_init(); // already done by usb_cdc_init() -> app_usbd_init()
// APP_ERROR_CHECK(err_code);
nrf_drv_wdt_config_t config = NRF_DRV_WDT_DEAFULT_CONFIG; // typo is in the SDK...
err_code = nrf_drv_wdt_init(&config, wdt_event_handler);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_wdt_channel_alloc(&m_channel_id);
APP_ERROR_CHECK(err_code);
nrf_drv_wdt_enable();
}
void bsp_wdt_feed(void) {
nrf_drv_wdt_channel_feed(m_channel_id);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef __BSP_WDT_H__
#define __BSP_WDT_H__
#ifdef __cplusplus
extern "C" {
#endif
void bsp_wdt_init(void);
void bsp_wdt_feed(void);
#ifdef __cplusplus
}
#endif
#endif // __BSP_WDT_H__
+1
View File
@@ -27,6 +27,7 @@
#define DATA_CMD_GET_GIT_VERSION (1017)
#define DATA_CMD_GET_ACTIVE_SLOT (1018)
#define DATA_CMD_GET_SLOT_INFO (1019)
#define DATA_CMD_WIPE_FDS (1020)
//
// ******************************************************************
+11 -9
View File
@@ -4630,7 +4630,7 @@
// <e> NRFX_WDT_ENABLED - nrfx_wdt - WDT peripheral driver
//==========================================================
#ifndef NRFX_WDT_ENABLED
#define NRFX_WDT_ENABLED 0
#define NRFX_WDT_ENABLED 1
#endif
// <o> NRFX_WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode
@@ -4647,7 +4647,7 @@
#ifndef NRFX_WDT_CONFIG_RELOAD_VALUE
#define NRFX_WDT_CONFIG_RELOAD_VALUE 2000
#define NRFX_WDT_CONFIG_RELOAD_VALUE 5000
#endif
// <o> NRFX_WDT_CONFIG_NO_IRQ - Remove WDT IRQ handling from WDT driver
@@ -4677,7 +4677,7 @@
// <e> NRFX_WDT_CONFIG_LOG_ENABLED - Enables logging in the module.
//==========================================================
#ifndef NRFX_WDT_CONFIG_LOG_ENABLED
#define NRFX_WDT_CONFIG_LOG_ENABLED 0
#define NRFX_WDT_CONFIG_LOG_ENABLED 1
#endif
// <o> NRFX_WDT_CONFIG_LOG_LEVEL - Default Severity level
@@ -5868,9 +5868,10 @@
// <e> UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver - legacy layer
//==========================================================
#ifndef UART_ENABLED
#define UART_ENABLED 0
#endif
//Don't define it at all else it conflicts with NRFX
//#ifndef UART_ENABLED
//#define UART_ENABLED 0
//#endif
// <o> UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control
// <0=> Disabled
@@ -6018,9 +6019,10 @@
// <e> WDT_ENABLED - nrf_drv_wdt - WDT peripheral driver - legacy layer
//==========================================================
#ifndef WDT_ENABLED
#define WDT_ENABLED 0
#endif
//Don't define it at all else it conflicts with NRFX
//#ifndef WDT_ENABLED
//#define WDT_ENABLED 0
//#endif
// <o> WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode
// <1=> Run in SLEEP, Pause in HALT
@@ -0,0 +1,26 @@
#include "app_timer.h"
#include "delayed_reset.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
APP_TIMER_DEF(m_reset_timer);
static void delayed_reset_event_handler(void* ctx) {
while (NRF_LOG_PROCESS());
ret_code_t ret = sd_nvic_SystemReset();
APP_ERROR_CHECK(ret);
while (1) {
__NOP();
}
}
void delayed_reset(uint32_t delay) {
NRF_LOG_INFO("Resetting in %d ms...", delay);
ret_code_t ret;
ret = app_timer_create(&m_reset_timer, APP_TIMER_MODE_SINGLE_SHOT, delayed_reset_event_handler);
APP_ERROR_CHECK(ret);
ret = app_timer_start(m_reset_timer, APP_TIMER_TICKS(delay), NULL);
APP_ERROR_CHECK(ret);
}
@@ -0,0 +1,3 @@
#pragma once
void delayed_reset(uint32_t delay);
+56 -13
View File
@@ -10,10 +10,11 @@ NRF_LOG_MODULE_REGISTER();
// current write record info
static struct {
uint16_t id; // file id
uint16_t key; // file key
bool success; // task is success
bool waiting; // task waiting done.
uint32_t record_id; // record id, used for sync delete
uint16_t id; // file id
uint16_t key; // file key
bool success; // task is success
bool waiting; // task waiting done.
} fds_operation_info;
@@ -120,12 +121,7 @@ bool fds_write_sync(uint16_t id, uint16_t key, uint16_t data_length_words, void*
} else if (err_code == FDS_ERR_NO_SPACE_IN_FLASH) { // 确保还有空间可以操作,否则需要GC
// 当前报错是属于空间不足的报错,可能我们需要进行GC
NRF_LOG_INFO("FDS no space, gc auto start.");
err_code = fds_gc(); // 发起GC操作
APP_ERROR_CHECK(err_code); // 检查GC是否正常执行
// 等待GC完成
while(!fds_operation_info.success) {
__NOP();
};
fds_gc_sync();
// gc完成后,可以重新进行相应的操作了
NRF_LOG_INFO("FDS auto gc success, write record continue.");
@@ -162,6 +158,7 @@ int fds_delete_sync(uint16_t id, uint16_t key) {
ret_code_t err_code;
while(fds_find_record(id, key, &record_desc)) {
fds_operation_info.success = false;
fds_record_id_from_desc(&record_desc, &fds_operation_info.record_id);
err_code = fds_record_delete(&record_desc);
APP_ERROR_CHECK(err_code);
delete_count++;
@@ -199,9 +196,13 @@ static void fds_evt_handler(fds_evt_t const * p_evt) {
} break;
case FDS_EVT_DEL_RECORD: {
if (p_evt->result == NRF_SUCCESS) {
NRF_LOG_INFO("Record remove: FileID: 0x%04x, RecordKey: 0x%04x", p_evt->del.file_id, p_evt->del.record_key);
if (p_evt->write.file_id == fds_operation_info.id && p_evt->write.record_key == fds_operation_info.key) {
// 上面的逻辑已经确保是我们当前删除记录的任务完成了!
NRF_LOG_INFO(
"Record remove: FileID: 0x%04x, RecordKey: 0x%04x, RecordID: %08x",
p_evt->del.file_id, p_evt->del.record_key, p_evt->del.record_id
);
if (p_evt->del.record_id == fds_operation_info.record_id) {
// Only check record id because fileID and recordKey aren't available
// if deleting via fds_record_iterate. record id is guaranteed to be unique.
fds_operation_info.success = true;
}
} else {
@@ -234,3 +235,45 @@ void fds_util_init() {
err_code = fds_init();
APP_ERROR_CHECK(err_code);
}
void fds_gc_sync(void) {
fds_operation_info.success = false;
ret_code_t err_code = fds_gc();
APP_ERROR_CHECK(err_code);
while(!fds_operation_info.success) {
__NOP();
};
}
static bool fds_next_record_delete_sync() {
fds_find_token_t tok = {0};
fds_record_desc_t desc = {0};
if (fds_record_iterate(&desc, &tok) != NRF_SUCCESS) {
NRF_LOG_INFO("No more records to delete");
return false;
}
fds_record_id_from_desc(&desc, &fds_operation_info.record_id);
NRF_LOG_INFO("Deleting record with id=%08x", fds_operation_info.record_id);
fds_operation_info.success = false;
ret_code_t rc = fds_record_delete(&desc);
if (rc != NRF_SUCCESS) {
NRF_LOG_WARNING("Record id=%08x deletion failed with rc=%d!", fds_operation_info.record_id, rc);
return false;
}
while(!fds_operation_info.success) {
__NOP();
}
NRF_LOG_INFO("Record id=%08x deleted successfully", fds_operation_info.record_id);
return true;
}
bool fds_wipe(void) {
NRF_LOG_INFO("Full fds wipe requested");
while (fds_next_record_delete_sync()) {}
fds_gc_sync();
return true;
}
@@ -9,5 +9,7 @@ bool fds_write_sync(uint16_t id, uint16_t key, uint16_t data_length_words, void*
int fds_delete_sync(uint16_t id, uint16_t key);
bool fds_is_exists(uint16_t id, uint16_t key);
void fds_util_init(void);
void fds_gc_sync(void);
bool fds_wipe(void);
#endif
+7 -7
View File
@@ -27,13 +27,12 @@ LITE = r"""
# create by http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Chameleon%20Ultra
BANNER = f"""
██████╗██╗ ██╗ █████╗ ████╗███████╗██╗ ███████╗ █████╗ ███╗ ██╗
██╔════╝██║ ██║██╔══██╗██████║██╔════╝██║ ██╔════╝██╔══██╗████╗ ██║
██║ ███████║███████║████████║█████╗ ██║ █████╗ ██║ ██║████╗ ██║
██║ ██╔══██║██╔══██║██║╚██╔██║██╔══╝ ██║ ██╔══╝ ██║ ██║██║╚████║
╚██████╗██║ ██║██║ ██║██║ ╚═╝ ██║███████╗███████╗███████╗╚█████╔╝██║ ╚████║
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚════╝ ╚═╝ ╚═══╝
██████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗██╗ ███████╗ █████╗ ██ ██╗
██╔════╝██║ ██║██╔══██╗███╗ ███║██╔════╝██║ ██╔════╝██╔══██╗███ ██║
██║ ███████║███████║████████║█████╗ ██║ █████╗ ██║ ██║████╗██║
██║ ██╔══██║██╔══██║████╔██║██╔══╝ ██║ ██╔══╝ ██║ ██║██████║
╚██████╗██║ ██║██║ ██║██║╚═╝██║███████╗███████╗███████╗╚█████╔╝██║███║
╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚════╝ ╚═╝ ══╝
"""
@@ -99,6 +98,7 @@ class ChameleonCLI:
'reset': new_uint(chameleon_cli_unit.HWSettingsReset, "Reset settings to default values"),
'help': "Chameleon settings management"
},
'factory_reset': new_uint(chameleon_cli_unit.HWFactoryReset, "Wipe all data and return to factory settings"),
'help': "hardware controller",
},
'hf': {
+28 -4
View File
@@ -950,10 +950,10 @@ class HWSlotNickSet(SlotIndexRequireUint, SenseTypeRequireUint):
slot_num = args.slot
sense_type = args.sense_type
name: str = args.name
if len(name.encode(encoding="gbk")) > 32:
uname = name.encode(encoding="utf8")
if len(uname) > 32:
raise ValueError("Your tag nick name too long.")
self.cmd_positive.set_slot_tag_nick_name(slot_num, sense_type, name)
#print(f'{slot_num} selected for nickname {name}.')
self.cmd_positive.set_slot_tag_nick_name(slot_num, sense_type, uname)
print(f' - Set tag nick name for slot {slot_num} success.')
@@ -969,7 +969,7 @@ class HWSlotNickGet(SlotIndexRequireUint, SenseTypeRequireUint):
slot_num = args.slot
sense_type = args.sense_type
res = self.cmd_positive.get_slot_tag_nick_name(slot_num, sense_type)
print(f' - Get tag nick name for slot {slot_num}: {res.data.decode(encoding="gbk")}')
print(f' - Get tag nick name for slot {slot_num}: {res.data.decode(encoding="utf8")}')
class HWSlotUpdate(DeviceRequiredUnit):
@@ -1077,3 +1077,27 @@ class HWSettingsReset(DeviceRequiredUnit):
print(" - Reset success @.@~")
else:
print(" - Reset failed")
class HWFactoryReset(DeviceRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = "Permanently wipes Chameleon to factory settings. " \
"This will delete all your slot data and custom settings. " \
"There's no going back."
parser.add_argument(
"--i-know-what-im-doing",
default=False,
action="store_true",
help="Just to be sure :)"
)
return parser
def on_exec(self, args: argparse.Namespace):
if not args.i_know_what_im_doing:
print("This time your data's safe. Read the command documentation next time.")
return
resp = self.cmd_positive.factory_reset()
if resp.status == chameleon_status.Device.STATUS_DEVICE_SUCCESS:
print(" - Reset successful! Please reconnect.")
print(" - A Serial Error below is normal, please ignore it")
else:
print(" - Reset failed!")
+11 -3
View File
@@ -30,6 +30,8 @@ DATA_CMD_GET_GIT_VERSION = 1017
DATA_CMD_GET_ACTIVE_SLOT = 1018
DATA_CMD_GET_SLOT_INFO = 1019
DATA_CMD_WIPE_FDS = 1020
DATA_CMD_SCAN_14A_TAG = 2000
DATA_CMD_MF1_SUPPORT_DETECT = 2001
DATA_CMD_MF1_NT_LEVEL_DETECT = 2002
@@ -464,7 +466,7 @@ class BaseChameleonCMD:
data.extend(uid)
return self.device.send_cmd_sync(DATA_CMD_SET_MF1_ANTI_COLLISION_RES, 0X00, data)
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: str):
def set_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType, name: bytes):
"""
设置MF1的模拟卡的防冲撞资源信息
:param slot: 卡槽号码
@@ -475,7 +477,7 @@ class BaseChameleonCMD:
# SlotNumber() will raise error for us if slot not in slot range
data = bytearray()
data.extend([SlotNumber.to_fw(slot), sense_type])
data.extend(name.encode(encoding="gbk"))
data.extend(name)
return self.device.send_cmd_sync(DATA_CMD_SET_SLOT_TAG_NICK, 0x00, data)
def get_slot_tag_nick_name(self, slot: SlotNumber, sense_type: TagSenseType):
@@ -528,6 +530,12 @@ class BaseChameleonCMD:
Store settings to flash memory
"""
return self.device.send_cmd_sync(DATA_CMD_SAVE_SETTINGS, 0x00)
def factory_reset(self):
"""
Reset to factory settings
"""
return self.device.send_cmd_sync(DATA_CMD_WIPE_FDS, 0x00)
class NegativeResponseError(Exception):
@@ -653,7 +661,7 @@ class PositiveChameleonCMD(BaseChameleonCMD):
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret
def set_slot_tag_nick_name(self, slot: int, sense_type: int, name: str):
def set_slot_tag_nick_name(self, slot: int, sense_type: int, name: bytes):
ret = super(PositiveChameleonCMD, self).set_slot_tag_nick_name(slot, sense_type, name)
self.check_status(ret.status, chameleon_status.Device.STATUS_DEVICE_SUCCESS)
return ret