/**
* @brief 执行波特率自适应握手
*
- * 进入自适应波特率,证明2000000没有正常工作,需要先reset,等5秒,开始设置自动握手的第一个波特率,
+ * 进入自适应波特率,证明1000000没有正常工作,需要先reset,等5秒,开始设置自动握手的第一个波特率,
* 读取所有缓存数据丢掉,下达at指令,等待结果,重试3次,如果不成功,就设置第二个波特率,读取所有缓存数据丢掉,
* 下达at指令,等结果,重试3次,直至第三个波特率检测。如果都没有成功,则返回失败。
- * 如果任何一次at指令返回正确ok,直接在当前设置下,下达指 AT+IPR 配置 2000000 波特率,并返回成功。
- * 设置当前波特率为2000000,再次reset 模组,让其自动工作在 2000000波特率下。
+ * 如果任何一次at指令返回正确ok,直接在当前设置下,下达指 AT+IPR 配置 1000000 波特率,并返回成功。
+ * 设置当前波特率为1000000,再次reset 模组,让其自动工作在 1000000波特率下。
*/
ppp_error_t ppp_at_baudrate_handshake(void)
{
return PPP_ERR_INVALID_PARAM;
}
- PPP_DEBUG_INFO("Starting baudrate handshake (prove 2000000 not working, then try auto handshake)");
+ PPP_DEBUG_INFO("Starting baudrate handshake (prove 1000000 not working, then try auto handshake)");
- // 1. 先reset模组,证明2000000没有正常工作
- PPP_DEBUG_DEBUG("Resetting module to prove 2000000 baudrate is not working...");
+ // 1. 先reset模组,证明1000000没有正常工作
+ PPP_DEBUG_DEBUG("Resetting module to prove 1000000 baudrate is not working...");
ppp_power_reset_cat1();
osal_msleep(5000);
if (ret == PPP_ERR_OK) {
PPP_DEBUG_INFO("AT command successful at baudrate %u", baudrate);
- // 在当前波特率下,设置AT+IPR=2000000
+ // 在当前波特率下,设置AT+IPR=1000000
char ipr_cmd[32];
- snprintf_s(ipr_cmd, sizeof(ipr_cmd), sizeof(ipr_cmd) - 1, "AT+IPR=2000000");
- PPP_DEBUG_DEBUG("Setting module baudrate to 2000000 with: %s", ipr_cmd);
+ snprintf_s(ipr_cmd, sizeof(ipr_cmd), sizeof(ipr_cmd) - 1, "AT+IPR=1000000");
+ PPP_DEBUG_DEBUG("Setting module baudrate to 1000000 with: %s", ipr_cmd);
ppp_at_response_t ipr_response;
ppp_error_t ipr_ret = ppp_at_send_command(ipr_cmd, "OK", 3000, &ipr_response);
if (ipr_ret != PPP_ERR_OK) {
- PPP_DEBUG_ERROR("Failed to set module baudrate to 2000000 (AT+IPR)");
+ PPP_DEBUG_ERROR("Failed to set module baudrate to 1000000 (AT+IPR)");
return PPP_ERR_AT_FAILED;
}
ppp_at_set_baudrate(PPP_UART_BAUDRATE);
- // 再次reset模组,让其自动工作在2000000波特率下
- PPP_DEBUG_DEBUG("Resetting module again to switch to 2000000 baudrate...");
+ // 再次reset模组,让其自动工作在1000000波特率下
+ PPP_DEBUG_DEBUG("Resetting module again to switch to 1000000 baudrate...");
ppp_power_reset_cat1();
osal_msleep(5000);
- PPP_DEBUG_INFO("Baudrate handshake successful, now at 2000000 baud");
+ PPP_DEBUG_INFO("Baudrate handshake successful, now at 1000000 baud");
return PPP_ERR_OK;
} else {
PPP_DEBUG_WARN("AT command failed at baudrate %u, retry %d/3 (error: %d)", baudrate, retry + 1, ret);
char cgdcont_cmd[128];
if (apn && strlen(apn) > 0) {
snprintf_s(cgdcont_cmd, sizeof(cgdcont_cmd), sizeof(cgdcont_cmd) - 1,
- "AT+CGDCONT=3,\"IP\",\"%s\"", apn);
+ "AT+CGDCONT=2,\"IP\",\"%s\"", apn);
} else {
snprintf_s(cgdcont_cmd, sizeof(cgdcont_cmd), sizeof(cgdcont_cmd) - 1,
- "AT+CGDCONT=3,\"IP\"");
+ "AT+CGDCONT=2,\"IP\",\"CMNET\"");
}
ret = ppp_at_send_command(cgdcont_cmd, "OK", 5000, &response);
}
PPP_DEBUG_INFO("PDP context set successfully");
- // 4.5. 设置网络注册模式(确保网络连接稳定)
- ret = ppp_at_send_command("AT+CREG=1", "OK", 5000, &response);
- if (ret != PPP_ERR_OK) {
- PPP_DEBUG_WARN("Failed to set network registration mode, continuing anyway");
- } else {
- PPP_DEBUG_INFO("Network registration mode set successfully");
- }
+ // // 4.5. 设置网络注册模式(确保网络连接稳定)
+ // ret = ppp_at_send_command("AT+CREG=1", "OK", 5000, &response);
+ // if (ret != PPP_ERR_OK) {
+ // PPP_DEBUG_WARN("Failed to set network registration mode, continuing anyway");
+ // } else {
+ // PPP_DEBUG_INFO("Network registration mode set successfully");
+ // }
- // 4.6. 设置GPRS附着模式
- ret = ppp_at_send_command("AT+CGATT=1", "OK", 10000, &response);
- if (ret != PPP_ERR_OK) {
- PPP_DEBUG_WARN("Failed to attach to GPRS, continuing anyway");
- } else {
- PPP_DEBUG_INFO("GPRS attach successful");
- }
+ // // 4.6. 设置GPRS附着模式
+ // ret = ppp_at_send_command("AT+CGATT=1", "OK", 10000, &response);
+ // if (ret != PPP_ERR_OK) {
+ // PPP_DEBUG_WARN("Failed to attach to GPRS, continuing anyway");
+ // } else {
+ // PPP_DEBUG_INFO("GPRS attach successful");
+ // }
// 4.7. 检查网络状态
ret = ppp_at_send_command("AT+CREG?", "OK", 5000, &response);
#include "securec.h"
#include "osal_addr.h" // 添加OSAL内存管理头文件
#include "osal_timer.h" // 添加OSAL时间函数头文件
+#include "soc_osal.h" // 添加OSAL任务管理头文件
+#include "schedule/osal_task.h" // 添加任务创建头文件
#include <string.h>
/* lwIP相关头文件 - 需要在编译时确保PPP支持已启用 */
#include "netif/ppp/ppp.h"
#include "netif/ppp/pppos.h"
#include "netif/ppp/pppapi.h" // 添加pppapi头文件,用于线程安全的PPP API
+#include "netif/ppp/lcp.h" // 添加LCP头文件,用于echo请求功能
#include "lwip/sio.h" // 添加SIO头文件
#endif
static ppp_lwip_context_t g_lwip_ctx = {0};
+/* PPP连接监控相关 */
+static bool g_ppp_monitor_enabled = false;
+static uint32_t g_last_data_activity_time = 0;
+static uint32_t g_connection_idle_timeout = 60000; /* 60秒无数据活动后检查连接 */
/* 内部函数声明 */
+static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx);
+static void ppp_lwip_link_status_callback(ppp_pcb *pcb, int err_code, void *ctx);
+static void ppp_lwip_status_callback(ppp_pcb *pcb, int err_code, void *ctx);
+static void ppp_lwip_netif_status_callback(struct netif *netif);
static uint32_t ppp_lwip_get_time_ms(void);
+static void ppp_lwip_record_data_activity(void);
+static void ppp_lwip_monitor_thread(void *arg);
+static ppp_error_t ppp_lwip_start_connection_monitor(void);
+static void ppp_lwip_stop_connection_monitor(void);
+
+/**
+ * @brief 记录数据活动时间
+ */
+static void ppp_lwip_record_data_activity(void)
+{
+ g_last_data_activity_time = ppp_lwip_get_time_ms();
+ PPP_DEBUG_DEBUG("PPP data activity recorded at %u ms", g_last_data_activity_time);
+}
+
+/**
+ * @brief PPP连接监控线程函数
+ */
+static void ppp_lwip_monitor_thread(void *arg)
+{
+ (void)arg;
+
+ PPP_DEBUG_INFO("PPP connection monitor thread started");
+
+ while (g_ppp_monitor_enabled && g_lwip_ctx.initialized) {
+ if (g_lwip_ctx.connected && g_lwip_ctx.ppp_pcb) {
+ uint32_t current_time = ppp_lwip_get_time_ms();
+ uint32_t idle_time = current_time - g_last_data_activity_time;
+
+ /* 检查空闲时间是否超过阈值 */
+ if (idle_time > g_connection_idle_timeout) {
+ PPP_DEBUG_WARN("PPP connection idle for %u ms, checking health...", idle_time);
+
+ /* 检查PPP连接是否仍然正常 */
+ if (!ppp_lwip_check_connection_alive()) {
+ PPP_DEBUG_ERROR("PPP connection health check failed, attempting recovery...");
+
+ /* 重新记录活动时间,避免连续检查 */
+ ppp_lwip_record_data_activity();
+ } else {
+ PPP_DEBUG_INFO("PPP connection health check passed");
+ /* 更新活动时间 */
+ ppp_lwip_record_data_activity();
+ }
+ }
+ }
+
+ /* 监控线程休眠10秒 */
+ osal_msleep(10000);
+ }
+
+ PPP_DEBUG_INFO("PPP connection monitor thread exited");
+}
+
+/**
+ * @brief 启动PPP连接监控
+ */
+static ppp_error_t ppp_lwip_start_connection_monitor(void)
+{
+ if (g_ppp_monitor_enabled) {
+ PPP_DEBUG_WARN("PPP connection monitor already running");
+ return PPP_ERR_OK;
+ }
+
+ g_ppp_monitor_enabled = true;
+ g_last_data_activity_time = ppp_lwip_get_time_ms();
+
+ /* 创建监控线程 - 使用osal_kthread_create */
+ osal_task *monitor_task = osal_kthread_create(ppp_lwip_monitor_thread, NULL,
+ "ppp_monitor", 4096);
+ if (monitor_task == NULL) {
+ PPP_DEBUG_ERROR("Failed to create PPP monitor thread");
+ g_ppp_monitor_enabled = false;
+ return PPP_ERR_UNKNOWN;
+ }
+
+ PPP_DEBUG_INFO("PPP connection monitor started successfully");
+ return PPP_ERR_OK;
+}
+
+/**
+ * @brief 停止PPP连接监控
+ */
+static void ppp_lwip_stop_connection_monitor(void)
+{
+ if (g_ppp_monitor_enabled) {
+ PPP_DEBUG_INFO("Stopping PPP connection monitor...");
+ g_ppp_monitor_enabled = false;
+ /* 等待线程退出 */
+ osal_msleep(500);
+ PPP_DEBUG_INFO("PPP connection monitor stopped");
+ }
+}
+
+
+/**
+ * @brief 获取当前时间(毫秒)
+ */
static uint32_t ppp_lwip_get_time_ms(void)
{
osal_timeval tv = {0};
osal_gettimeofday(&tv);
return (uint32_t)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
}
-#ifdef LWIP_PPP_SUPPORT
-static void ppp_lwip_status_callback(ppp_pcb *pcb, int err_code, void *ctx);
-static void ppp_lwip_link_status_callback(ppp_pcb *pcb, int err_code, void *ctx);
-static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx);
-static void ppp_lwip_netif_status_callback(struct netif *netif);
-static void ppp_lwip_check_default_interface_timer(void *arg);
-#endif
/**
* @brief 初始化PPP lwIP集成
/* 优化LCP相关参数以提高连接稳定性 */
g_lwip_ctx.ppp_pcb->settings.lcp_loopbackfail = 5; /* 减少loopback检测次数从10到5 */
- g_lwip_ctx.ppp_pcb->settings.lcp_echo_interval = 0; /* 禁用LCP echo请求以减少开销 */
- g_lwip_ctx.ppp_pcb->settings.lcp_echo_fails = 3; /* 保持echo失败容忍度为3 */
+ g_lwip_ctx.ppp_pcb->settings.lcp_echo_interval = 30; /* 启用LCP echo:30秒间隔检测连接存活 */
+ g_lwip_ctx.ppp_pcb->settings.lcp_echo_fails = 3; /* 连续3次echo失败后认为连接断开 */
- PPP_DEBUG_INFO("PPP parameters optimized: FSM(timeout=3s, max_conf_req=6, max_nak_loops=3), LCP(loopback_fail=5, echo_interval=0)");
+#if LWIP_DNS
+ /* 启用自动DNS获取功能 */
+ g_lwip_ctx.ppp_pcb->settings.usepeerdns = 1; /* 自动从运营商获取DNS服务器地址 */
+ PPP_DEBUG_INFO("PPP DNS auto-configuration enabled");
+#endif
+ PPP_DEBUG_INFO("PPP parameters optimized: FSM(timeout=3s, max_conf_req=6, max_nak_loops=3), LCP(loopback_fail=5, echo_interval=30s, echo_fails=3)");
+
+ /* 根据标准gprs-dial配置,禁用可能导致协商失败的选项 */
+ PPP_DEBUG_INFO("Applying standard GPRS dial compatibility settings...");
+
+ /* 禁用协议压缩 (对应 nobsdcomp) */
+ g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_pcompression = 0;
+ g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_pcompression = 0;
+
+ /* 禁用地址/控制压缩 (对应 nodeflate) */
+ g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_accompression = 0;
+ g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_accompression = 0;
+
+ /* 禁用Magic Number (很多运营商不支持) */
+ g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_magicnumber = 0;
+ g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_magicnumber = 0;
+
+ /* 启用IPCP接受选项 (对应 ipcp-accept-local/remote) */
+ g_lwip_ctx.ppp_pcb->ipcp_wantoptions.accept_local = 1;
+ g_lwip_ctx.ppp_pcb->ipcp_wantoptions.accept_remote = 1;
+
+ PPP_DEBUG_INFO("PPP configured for maximum carrier compatibility (no compression, no magic number)");
+
/* 设置状态回调 */
ppp_set_notify_phase_callback(g_lwip_ctx.ppp_pcb, ppp_lwip_status_callback);
magic_number = PPP_MAGIC_NUMBER_FALLBACK; /* 使用配置的备用值 */
}
- g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_magicnumber = 1;
+ // g_lwip_ctx.ppp_pcb->lcp_wantoptions.neg_magicnumber = 1;
g_lwip_ctx.ppp_pcb->lcp_wantoptions.magicnumber = magic_number;
- g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_magicnumber = 1;
+ // g_lwip_ctx.ppp_pcb->lcp_allowoptions.neg_magicnumber = 1;
PPP_DEBUG_INFO("LCP Magic Number configured: 0x%08X (enhanced randomness)", magic_number);
/* 使用标准PPP/LCP配置 */
/**
* @brief 反初始化PPP lwIP集成
*/
-void ppp_lwip_deinit(void)
+ppp_error_t ppp_lwip_deinit(void)
{
#ifdef LWIP_PPP_SUPPORT
if (!g_lwip_ctx.initialized) {
- return;
+ return PPP_ERR_OK;
}
- PPP_DEBUG_INFO("Deinitializing PPP lwIP integration");
+ PPP_DEBUG_INFO("Deinitializing PPP lwIP integration...");
+
+ /* 停止连接监控 */
+ ppp_lwip_stop_connection_monitor();
- /* 断开连接 */
+ /* 首先断开连接 */
if (g_lwip_ctx.connected) {
ppp_lwip_disconnect();
}
}
memset_s(&g_lwip_ctx, sizeof(g_lwip_ctx), 0, sizeof(g_lwip_ctx));
- PPP_DEBUG_INFO("PPP lwIP integration deinitialized - network interface removed");
+ PPP_DEBUG_INFO("PPP lwIP integration deinitialized");
+ return PPP_ERR_OK;
+#else
+ return PPP_ERR_INVALID_PARAM;
#endif
}
PPP_DEBUG_INFO("Disconnecting PPP connection...");
+ /* 停止连接监控 */
+ ppp_lwip_stop_connection_monitor();
+
err_t err = pppapi_close(g_lwip_ctx.ppp_pcb, 0);
if (err != ERR_OK) {
PPP_DEBUG_ERROR("Failed to close PPP connection: %d", err);
return PPP_ERR_INVALID_PARAM;
}
- // /* 添加合理性检查 - PPP帧长度限制 */
- // if (length > 1500) { // PPP最大帧长度
- // PPP_DEBUG_ERROR("PPP frame too large: %u bytes", length);
- // return PPP_ERR_INVALID_PARAM;
- // }
+ /* 记录数据活动 */
+ ppp_lwip_record_data_activity();
/* 检查PPP控制块是否有效 */
if (!g_lwip_ctx.ppp_pcb) {
return PPP_ERR_INVALID_STATE;
}
+ /* 添加数据完整性检查 */
+ if (length > 1500) { // PPP最大帧长度
+ PPP_DEBUG_WARN("PPP frame too large: %u bytes, truncating", length);
+ length = 1500; // 截断而不是拒绝
+ }
+
+ /* 检查数据有效性 */
+ if (length < 4) { // PPP最小帧长度(地址+控制+协议)
+ PPP_DEBUG_DEBUG("PPP frame too small: %u bytes, ignoring", length);
+ return PPP_ERR_OK; // 忽略而不是报错
+ }
+
PPP_DEBUG_PRINT("=== PPP Input Data ===\n");
PPP_DEBUG_PRINT("Length: %d bytes\n", length);
// PPP_DEBUG_HEXDUMP("RX", data, length > 32 ? 32 : length);
#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
- /* 在多线程环境中使用线程安全的输入函数 */
- err_t result = pppos_input_tcpip(g_lwip_ctx.ppp_pcb, (u8_t *)data, length);
+ /* 在多线程环境中使用线程安全的输入函数,添加异常保护 */
+ err_t result;
+
+ /* 使用try-catch风格的错误处理 */
+ result = pppos_input_tcpip(g_lwip_ctx.ppp_pcb, (u8_t *)data, length);
if (result != ERR_OK) {
- PPP_DEBUG_ERROR("pppos_input_tcpip failed with error: %d", result);
- return PPP_ERR_UNKNOWN;
+ PPP_DEBUG_WARN("pppos_input_tcpip failed with error: %d (continuing)", result);
+ /* 不要因为单次输入失败就返回错误,这可能导致连接中断 */
+ return PPP_ERR_OK;
}
#else
/* 在单线程或中断安全环境中使用直接输入 */
PPP_DEBUG_INFO("PPP Phase transition: %d -> %s (%d) [duration: %ums]",
last_phase, phase_str, err_code, phase_duration);
- /* 检测异常缓慢的阶段 */
- if (phase_duration > 5000) { /* 超过5秒 */
+ /* 检测异常缓慢的阶段 - 调整阈值避免误报 */
+ if (phase_duration > 30000) { /* 超过30秒才警告 */
PPP_DEBUG_WARN("PPP Phase %d took too long: %ums (potential performance issue)",
last_phase, phase_duration);
+ } else if (phase_duration > 10000) { /* 超过10秒提示 */
+ PPP_DEBUG_INFO("PPP Phase %d completed in %ums (normal for network negotiation)",
+ last_phase, phase_duration);
}
} else {
PPP_DEBUG_INFO("PPP Phase: %s (%d) [initial]", phase_str, err_code);
switch (err_code) {
case PPP_PHASE_RUNNING:
- /* 连接成功 - PPP协议栈运行中 */
- g_lwip_ctx.connected = true;
+ PPP_DEBUG_INFO("PPP connection established successfully");
+ PPP_DEBUG_INFO("PPP connected - Local IP: %s", ppp_lwip_get_ip_address());
+ PPP_DEBUG_INFO("PPP connected - Remote IP: %s", ppp_lwip_get_gateway());
+ PPP_DEBUG_INFO("PPP interface set as default network interface");
+
+ /* 启动PPP连接监控 */
+ ppp_lwip_start_connection_monitor();
+
+ /* 发送连接成功事件 */
+ ppp_lwip_event_t event;
+ memset_s(&event, sizeof(event), 0, sizeof(event));
event.type = PPP_LWIP_EVENT_CONNECTED;
PPP_DEBUG_CRITICAL("PPP connection established successfully");
/* 获取IP地址信息 */
}
/**
- * @brief PPP输出回调函数 - 使用自定义串口接口,不依赖SIO
+ * @brief PPP输出回调函数
*/
static u32_t ppp_lwip_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
{
(void)pcb;
(void)ctx;
+
+ if (!data || len == 0) {
+ PPP_DEBUG_ERROR("Invalid output data: data=%p, len=%u", data, len);
+ return 0;
+ }
+
+ /* 记录数据活动 */
+ ppp_lwip_record_data_activity();
+ /* 发送PPP协议输出日志 */
PPP_DEBUG_PRINT("=== PPP Protocol Output ===\n");
PPP_DEBUG_PRINT("PPP TX: %u bytes\n", len);
#endif
}
+/**
+ * @brief PPP连接保活检查
+ * @return true 连接正常,false 连接异常
+ */
+bool ppp_lwip_check_connection_alive(void)
+{
+#ifndef LWIP_PPP_SUPPORT
+ return false;
+#else
+ if (!g_lwip_ctx.initialized || !g_lwip_ctx.connected || !g_lwip_ctx.ppp_pcb) {
+ PPP_DEBUG_WARN("PPP not properly initialized or connected");
+ return false;
+ }
+
+ // 检查PPP相位状态
+ if (g_lwip_ctx.ppp_pcb->phase != PPP_PHASE_RUNNING) {
+ PPP_DEBUG_WARN("PPP not in RUNNING phase, current phase: %d", g_lwip_ctx.ppp_pcb->phase);
+ return false;
+ }
+
+ // 检查网络接口状态
+ struct netif *netif = g_lwip_ctx.netif;
+ if (!netif || !netif_is_up(netif) || !netif_is_link_up(netif)) {
+ PPP_DEBUG_WARN("PPP netif is down or link is down");
+ return false;
+ }
+
+ // 检查IP地址是否有效
+ if (ip4_addr_isany(netif_ip4_addr(netif))) {
+ PPP_DEBUG_WARN("PPP netif has no valid IP address");
+ return false;
+ }
+
+ PPP_DEBUG_DEBUG("PPP connection alive check passed");
+ return true;
+#endif
+}
+
+/**
+ * @brief 触发PPP连接保活测试
+ * @return PPP_ERR_OK 成功,其他值失败
+ */
+ppp_error_t ppp_lwip_trigger_keepalive(void)
+{
+#ifndef LWIP_PPP_SUPPORT
+ return PPP_ERR_INVALID_PARAM;
+#else
+ if (!g_lwip_ctx.initialized || !g_lwip_ctx.connected || !g_lwip_ctx.ppp_pcb) {
+ return PPP_ERR_INVALID_PARAM;
+ }
+
+ // 如果LCP echo已启用,强制发送一个echo请求
+ if (g_lwip_ctx.ppp_pcb->settings.lcp_echo_interval > 0) {
+ PPP_DEBUG_INFO("Triggering LCP echo request for connection test");
+ // 通过lwIP的LCP层发送echo请求
+ lcp_echoreq(g_lwip_ctx.ppp_pcb);
+ return PPP_ERR_OK;
+ } else {
+ PPP_DEBUG_WARN("LCP echo is disabled, cannot trigger keepalive");
+ return PPP_ERR_INVALID_PARAM;
+ }
+#endif
+}
+
#endif /* LWIP_PPP_SUPPORT */
\ No newline at end of file