#include #include #include "ql_adc.h" #include "ql_application.h" #include "ql_data_call.h" #include "ql_dev.h" #include "ql_gpio.h" #include "ql_iic.h" #include "ql_nw.h" #include "ql_ping.h" #include "ql_power.h" #include "ql_rtos.h" #include "ql_sim.h" #include "sockets.h" #include "DP1312EA.h" #include "factory_audio.h" #include "factory_protocol.h" #include "factory_test.h" #include "nfc.h" #include "reg.h" #define FT_PING_COUNT 3 #define FT_PING_REQUIRED 2 #define FT_PING_TIMEOUT_MS 4000 #define FT_PING_WAIT_TICKS (20000 / 5) #define FT_NFC_VERSION_EXPECTED 0x82 int g_testmode = 1; int g_nfc_bind_status = 0; extern int8 output_uuid[64]; extern s8 Read_NTAG213(u8 *uid_buf, u8 *data_buf, u16 *data_len); void handle_nfc_bind(const char *uid) { (void)uid; } void handle_nfc_trigger(const char *uid) { (void)uid; } static ql_sem_t g_ping_sem; static ql_ping_stat_t g_ping_stats; static volatile int g_ping_finished; static char g_ip_address[64]; static int iccid_valid(const char *iccid) { unsigned int i; unsigned int len; if (!iccid) return 0; len = strlen(iccid); if (len < 18 || len > 22) return 0; for (i = 0; i < len; ++i) { if (iccid[i] < '0' || iccid[i] > '9') return 0; } return 1; } static ft_error_t run_boot_audio(char *data, unsigned int data_size) { int ret = factory_audio_init(); if (ret != 0) return FT_ERR_AUDIO_CODEC_INIT; ret = factory_audio_play_prompt(FT_AUDIO_BOOT_PATH); if (ret == -2) return FT_ERR_AUDIO_FILE_MISSING; if (ret != 0) return FT_ERR_AUDIO_PLAY; snprintf(data, data_size, "{\"path\":\"%s\",\"volume_percent\":100}", FT_AUDIO_BOOT_PATH); return FT_OK; } static ft_error_t run_power_adc(char *data, unsigned int data_size) { unsigned short samples[FT_ADC_SAMPLE_COUNT]; unsigned short min_value = 0; unsigned short max_value = 0; unsigned int sum = 0; unsigned int i; unsigned int average; if (ql_adc_init() != 0) return FT_ERR_ADC_READ; for (i = 0; i < FT_ADC_SAMPLE_COUNT; ++i) { ql_adc_onff(1); ql_rtos_task_sleep_ms(5); ql_adc_onff(0); samples[i] = ql_get_battery_vol(); if (samples[i] == 0) return FT_ERR_ADC_READ; if (i == 0 || samples[i] < min_value) min_value = samples[i]; if (i == 0 || samples[i] > max_value) max_value = samples[i]; sum += samples[i]; ql_rtos_task_sleep_ms(45); } average = (sum - min_value - max_value) / (FT_ADC_SAMPLE_COUNT - 2); snprintf(data, data_size, "{\"voltage_mv\":%u,\"sample_min_mv\":%u," "\"sample_max_mv\":%u,\"limit_min_mv\":%d," "\"limit_max_mv\":%d}", average, min_value, max_value, FT_ADC_MIN_MV, FT_ADC_MAX_MV); if (average < FT_ADC_MIN_MV) return FT_ERR_ADC_LOW; if (average > FT_ADC_MAX_MV) return FT_ERR_ADC_HIGH; return FT_OK; } static ft_error_t run_audio_loopback(char *data, unsigned int data_size) { u32 recorded_ms = 0; int ret = factory_audio_wait_and_loopback(FT_DIALOG_KEY_TIMEOUT_MS, &recorded_ms); snprintf(data, data_size, "{\"recorded_ms\":%lu,\"max_ms\":%d,\"volume_percent\":100}", (unsigned long)recorded_ms, FT_AUDIO_MAX_MS); if (ret == -4) return FT_ERR_DIALOG_KEY_TIMEOUT; if (ret == -1) return FT_ERR_RECORD_OPEN; if (ret == -2) return FT_ERR_RECORD_EMPTY; if (ret != 0) return FT_ERR_LOOPBACK_PLAY; return FT_OK; } static int wait_gpio_level(unsigned int pin, PIN_LEVEL_E expected, u32 timeout_ms) { PIN_LEVEL_E level = PIN_LEVEL_HIGH; u32 start = ft_now_ms(); do { ql_gpio_get_level(pin, &level); if (level == expected) { ql_rtos_task_sleep_ms(40); ql_gpio_get_level(pin, &level); if (level == expected) return 0; } ql_rtos_task_sleep_ms(10); } while (ft_now_ms() - start < timeout_ms); return -1; } static ft_error_t run_key_triple(char *data, unsigned int data_size) { int count = 0; u32 start = ft_now_ms(); ql_gpio_init(FT_TRIPLE_KEY_PIN, PIN_DIRECTION_IN, PIN_PULL_PU, PIN_LEVEL_HIGH); if (wait_gpio_level(FT_TRIPLE_KEY_PIN, PIN_LEVEL_HIGH, 5000) != 0) { return FT_ERR_TRIPLE_KEY_TIMEOUT; } while (count < 3 && ft_now_ms() - start < FT_TRIPLE_KEY_TIMEOUT_MS) { u32 remaining = FT_TRIPLE_KEY_TIMEOUT_MS - (ft_now_ms() - start); if (wait_gpio_level(FT_TRIPLE_KEY_PIN, PIN_LEVEL_LOW, remaining) != 0) break; ++count; ft_protocol_node_progress(FT_NODE_KEY_TRIPLE, count, 3); remaining = FT_TRIPLE_KEY_TIMEOUT_MS - (ft_now_ms() - start); if (wait_gpio_level(FT_TRIPLE_KEY_PIN, PIN_LEVEL_HIGH, remaining) != 0) break; } snprintf(data, data_size, "{\"press_count\":%d,\"required\":3}", count); return count == 3 ? FT_OK : FT_ERR_TRIPLE_KEY_TIMEOUT; } static ft_error_t run_esim(char *data, unsigned int data_size) { int status = QL_SIM_STATUS_UNKNOWN; int ret = -1; char iccid[32] = {0}; u32 start = ft_now_ms(); do { ret = ql_sim_get_card_status(&status); if (ret == QL_SIM_SUCCESS && status == QL_SIM_STATUS_READY) break; ql_rtos_task_sleep_ms(500); } while (ft_now_ms() - start < FT_SIM_TIMEOUT_MS); if (ret != QL_SIM_SUCCESS || status != QL_SIM_STATUS_READY) { snprintf(data, data_size, "{\"sim_status\":%d}", status); return FT_ERR_SIM_NOT_READY; } if (ql_sim_get_iccid(iccid, sizeof(iccid)) != QL_SIM_SUCCESS) { return FT_ERR_ICCID_READ; } if (!iccid_valid(iccid)) { snprintf(data, data_size, "{\"sim_status\":%d,\"iccid\":\"%s\"}", status, iccid); return FT_ERR_ICCID_FORMAT; } ft_protocol_set_iccid(iccid); snprintf(data, data_size, "{\"sim_status\":\"READY\",\"iccid\":\"%s\"}", iccid); return FT_OK; } static ft_error_t run_data_call(char *data, unsigned int data_size) { struct ql_data_call_info info; int ret; int attempt; if (ql_network_register_wait(120) != 0) return FT_ERR_NETWORK_REGISTER; ql_set_data_call_asyn_mode(0, NULL); ret = ql_start_data_call(1, 0, NULL, NULL, NULL, 0); if (ret != 0) return FT_ERR_PDP_ACTIVATE; memset(&info, 0, sizeof(info)); memset(g_ip_address, 0, sizeof(g_ip_address)); for (attempt = 0; attempt < 10; ++attempt) { if (ql_get_data_call_info(1, 0, &info) == 0 && info.v4.state) { inet_ntop(AF_INET, &info.v4.addr.ip, g_ip_address, sizeof(g_ip_address)); if (g_ip_address[0] && strcmp(g_ip_address, "0.0.0.0") != 0) break; } ql_rtos_task_sleep_ms(1000); } snprintf(data, data_size, "{\"ip\":\"%s\",\"ip_type\":\"IPv4\"}", g_ip_address); if (!g_ip_address[0] || strcmp(g_ip_address, "0.0.0.0") == 0) { return FT_ERR_IP_INVALID; } return FT_OK; } static void factory_ping_callback(ql_ping_state_e state, int err, ql_ping_stat_t *stats) { (void)err; if (stats) memcpy(&g_ping_stats, stats, sizeof(g_ping_stats)); if (state == QL_PING_FINISHED) { g_ping_finished = 1; if (g_ping_sem) ql_rtos_semaphore_release(g_ping_sem); } } static ft_error_t run_ping(char *data, unsigned int data_size) { ql_ping_ctx_t *ctx; ql_ping_cfg_t cfg = { .pdpCid = 1, .host = FT_PING_HOST, .timeout = FT_PING_TIMEOUT_MS, .dataSize = 32, .pingNum = FT_PING_COUNT }; int ret; memset(&g_ping_stats, 0, sizeof(g_ping_stats)); g_ping_finished = 0; if (ql_rtos_semaphore_create(&g_ping_sem, 0) != 0) return FT_ERR_PING_INIT; ctx = ql_ping_init(cfg, factory_ping_callback); if (!ctx) { ql_rtos_semaphore_delete(g_ping_sem); g_ping_sem = NULL; return FT_ERR_PING_INIT; } ret = ql_ping(ctx); if (ret == 0) ret = ql_rtos_semaphore_wait(g_ping_sem, FT_PING_WAIT_TICKS); ql_ping_deinit(ctx); ql_rtos_semaphore_delete(g_ping_sem); g_ping_sem = NULL; snprintf(data, data_size, "{\"host\":\"%s\",\"sent\":%u,\"received\":%u," "\"lost\":%u,\"avg_rtt_ms\":%lu}", FT_PING_HOST, g_ping_stats.sentNum, g_ping_stats.rcvdNum, g_ping_stats.lostNum, (unsigned long)g_ping_stats.avgRtt); if (ret != 0 || !g_ping_finished) return FT_ERR_PING_START; if (g_ping_stats.rcvdNum < FT_PING_REQUIRED) return FT_ERR_PING_LOSS; return FT_OK; } static ft_error_t run_nfc(char *data, unsigned int data_size) { u8 uid[7] = {0}; u8 ntag_data[180] = {0}; u16 ntag_len = 0; u8 version; u32 start = ft_now_ms(); DP1312EA_Reset(); version = DP1312EA_ReadRawRC(VersionReg); if (version != FT_NFC_VERSION_EXPECTED) { snprintf(data, data_size, "{\"chip_version\":\"0x%02X\"}", version); return FT_ERR_NFC_CHIP; } do { memset(output_uuid, 0, 64); if (Read_NTAG213(uid, ntag_data, &ntag_len) == MI_OK && output_uuid[0]) { ft_protocol_set_nfc_uid((char *)output_uuid); snprintf(data, data_size, "{\"chip_version\":\"0x%02X\",\"uid\":\"%s\"}", version, (char *)output_uuid); return FT_OK; } ql_rtos_task_sleep_ms(200); } while (ft_now_ms() - start < FT_NFC_TIMEOUT_MS); return FT_ERR_NFC_TIMEOUT; } static ft_error_t run_node(ft_node_t node, char *data, unsigned int data_size) { data[0] = '\0'; switch (node) { case FT_NODE_BOOT_AUDIO: return run_boot_audio(data, data_size); case FT_NODE_POWER_ADC: return run_power_adc(data, data_size); case FT_NODE_AUDIO_LOOPBACK: return run_audio_loopback(data, data_size); case FT_NODE_KEY_TRIPLE: return run_key_triple(data, data_size); case FT_NODE_ESIM: return run_esim(data, data_size); case FT_NODE_DATA_CALL: return run_data_call(data, data_size); case FT_NODE_PING: return run_ping(data, data_size); case FT_NODE_NFC: return run_nfc(data, data_size); default: return FT_ERR_NFC_LOCKED; } } static void factory_test_task(void *arg) { char imei[32] = {0}; char data[384]; ft_node_t node; ft_error_t result = FT_OK; u32 start; int audio_ret; (void)arg; ql_rtos_task_sleep_s(2); ft_protocol_init(); if (ql_dev_get_imei(imei, sizeof(imei)) == 0) { ft_protocol_set_imei(imei); } ft_protocol_session_start(); for (node = FT_NODE_BOOT_AUDIO; node < FT_NODE_COUNT; ++node) { ft_protocol_node_start(node); start = ft_now_ms(); result = run_node(node, data, sizeof(data)); ft_protocol_node_result(node, result == FT_OK, result, ft_now_ms() - start, data); if (result != FT_OK) { ft_protocol_session_result(0, result); while (1) ql_rtos_task_sleep_s(60); } } audio_ret = factory_audio_play_prompt(FT_AUDIO_PASS_PATH); if (audio_ret == 0) { ql_rtos_task_sleep_ms(300); audio_ret = factory_audio_play_prompt(FT_AUDIO_SHUTDOWN_PATH); } if (audio_ret != 0) { ft_protocol_session_result(0, FT_ERR_FINAL_AUDIO); while (1) ql_rtos_task_sleep_s(60); } ft_protocol_session_result(1, FT_OK); ft_protocol_power_off(); ql_rtos_task_sleep_ms(500); ql_power_down(1); ft_protocol_session_result(0, FT_ERR_POWER_DOWN); while (1) ql_rtos_task_sleep_s(60); } application_init(factory_test_task, "factory_test", 10, 0);