/* * it87_wdt_test.c - IT87 看门狗驱动测试程序 * * 编译: gcc -o it87_wdt_test it87_wdt_test.c -Wall * 运行: sudo ./it87_wdt_test [选项] */ #include #include #include #include #include #include #include #include #include #define WATCHDOG_DEV "/dev/watchdog" #define DEFAULT_KEEPALIVE 5 // 默认喂狗间隔(秒) static int wdt_fd = -1; static volatile int running = 1; static int test_mode = 0; // 0=正常喂狗, 1=停止喂狗测试重启, 2=触发喂狗后退出 /* 信号处理:优雅退出 */ static void signal_handler(int sig) { printf("\n[%d] 收到信号 %d,准备退出...\n", getpid(), sig); running = 0; } /* 打印使用说明 */ static void print_usage(const char *prog) { printf("IT87 看门狗驱动测试程序\n\n"); printf("用法: %s [选项]\n\n", prog); printf("选项:\n"); printf(" -h 显示帮助\n"); printf(" -t <秒> 设置喂狗间隔 (默认: %d秒)\n", DEFAULT_KEEPALIVE); printf(" -T <秒> 设置看门狗超时时间 (需要驱动支持)\n"); printf(" -m 测试模式: 启动后停止喂狗,测试系统重启\n"); printf(" -e 测试模式: 喂狗一次后立即退出(测试 nowayout)\n"); printf(" -i 获取看门狗信息\n"); printf(" -c 启用魔法关闭字符 'V'\n"); printf(" -d 禁用看门狗(如果支持)\n"); printf("\n示例:\n"); printf(" %s # 正常测试,每5秒喂狗\n", prog); printf(" %s -t 2 # 每2秒喂狗一次\n", prog); printf(" %s -m -t 10 # 10秒后停止喂狗,系统应该重启\n", prog); printf(" %s -e # 测试 nowayout 模式\n", prog); printf("\n警告: 使用 -m 或 -e 选项会导致系统重启!\n"); } /* 获取看门狗信息 */ static void get_watchdog_info(int fd) { struct watchdog_info info; if (ioctl(fd, WDIOC_GETSUPPORT, &info) < 0) { perror("WDIOC_GETSUPPORT 失败"); return; } printf("\n========== 看门狗信息 ==========\n"); printf("标识: %s\n", info.identity); printf("固件版本: %u\n", info.firmware_version); printf("选项支持:\n"); if (info.options & WDIOF_OVERHEAT) printf(" - 过热检测\n"); if (info.options & WDIOF_FANFAULT) printf(" - 风扇故障\n"); if (info.options & WDIOF_EXTERN1) printf(" - 外部信号1\n"); if (info.options & WDIOF_EXTERN2) printf(" - 外部信号2\n"); if (info.options & WDIOF_POWERUNDER) printf(" - 电源不足\n"); if (info.options & WDIOF_CARDRESET) printf(" - 卡片重启\n"); if (info.options & WDIOF_POWEROVER) printf(" - 电源过载\n"); if (info.options & WDIOF_SETTIMEOUT) printf(" - 设置超时时间 ✓\n"); if (info.options & WDIOF_MAGICCLOSE) printf(" - 魔法关闭 ✓\n"); if (info.options & WDIOF_PRETIMEOUT) printf(" - 预超时\n"); if (info.options & WDIOF_ALARMONLY) printf(" - 仅报警\n"); if (info.options & WDIOF_KEEPALIVEPING) printf(" - 喂狗支持 ✓\n"); printf("================================\n\n"); } /* 获取当前超时时间 */ static int get_timeout(int fd) { int timeout = 0; if (ioctl(fd, WDIOC_GETTIMEOUT, &timeout) < 0) { perror("WDIOC_GETTIMEOUT 失败"); return -1; } return timeout; } /* 设置超时时间 */ static int set_timeout(int fd, int timeout) { printf("设置超时时间为 %d 秒...\n", timeout); if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) < 0) { perror("WDIOC_SETTIMEOUT 失败"); return -1; } printf("实际设置的超时时间: %d 秒\n", timeout); return 0; } /* 喂狗(keepalive) */ static int keepalive(int fd) { int dummy; if (ioctl(fd, WDIOC_KEEPALIVE, &dummy) < 0) { perror("WDIOC_KEEPALIVE 失败"); return -1; } return 0; } /* 禁用看门狗 */ static int disable_watchdog(int fd) { printf("尝试禁用看门狗...\n"); /* 方法1: 魔法关闭字符 */ if (write(fd, "V", 1) != 1) { perror("魔法关闭字符写入失败"); return -1; } printf("已发送魔法关闭字符 'V'\n"); return 0; } /* 主测试循环 */ static void test_loop(int fd, int keepalive_interval, int mode) { int count = 0; time_t start_time = time(NULL); printf("\n========== 开始测试 ==========\n"); printf("PID: %d\n", getpid()); printf("喂狗间隔: %d 秒\n", keepalive_interval); printf("当前超时设置: %d 秒\n", get_timeout(fd)); if (mode == 1) { printf("\n⚠️ 测试模式: 将在 %d 秒后停止喂狗,系统应该重启!\n", keepalive_interval * 3); } else if (mode == 2) { printf("\n⚠️ 测试模式: 喂狗一次后立即退出,测试 nowayout\n"); } else { printf("\n正常模式: 按 Ctrl+C 停止\n"); } printf("==============================\n\n"); /* 第一次喂狗(启动看门狗) */ if (keepalive(fd) < 0) { fprintf(stderr, "初始喂狗失败\n"); return; } printf("[%3d] 初始喂狗完成,看门狗已启动\n", count++); if (mode == 2) { /* 测试模式: 立即退出 */ printf("立即退出,如果 nowayout=1,系统将在超时后重启\n"); return; } /* 主循环 */ while (running) { sleep(keepalive_interval); if (!running) break; /* 测试模式1: 在指定次数后停止喂狗 */ if (mode == 1 && count >= 3) { printf("\n⚠️ 故意停止喂狗!系统应在 %d 秒后重启...\n", get_timeout(fd)); printf("等待重启中(按 Ctrl+C 可尝试中断)...\n"); /* 继续循环但不喂狗 */ while (1) { sleep(1); } } /* 正常喂狗 */ if (keepalive(fd) < 0) { fprintf(stderr, "喂狗失败\n"); break; } time_t elapsed = time(NULL) - start_time; printf("[%3d] 已喂狗 (运行时间: %ld 秒)\n", count++, elapsed); fflush(stdout); } } int main(int argc, char *argv[]) { int opt; int keepalive_interval = DEFAULT_KEEPALIVE; int new_timeout = 0; int show_info = 0; int magic_close = 0; int disable = 0; /* 解析命令行参数 */ while ((opt = getopt(argc, argv, "ht:T:meicd")) != -1) { switch (opt) { case 'h': print_usage(argv[0]); return 0; case 't': keepalive_interval = atoi(optarg); if (keepalive_interval < 1) { fprintf(stderr, "错误: 喂狗间隔必须 >= 1 秒\n"); return 1; } break; case 'T': new_timeout = atoi(optarg); break; case 'm': test_mode = 1; // 停止喂狗测试重启 break; case 'e': test_mode = 2; // 立即退出测试 nowayout break; case 'i': show_info = 1; break; case 'c': magic_close = 1; break; case 'd': disable = 1; break; default: print_usage(argv[0]); return 1; } } /* 注册信号处理 */ signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); printf("IT87 看门狗测试程序\n"); printf("打开设备: %s\n", WATCHDOG_DEV); /* 打开看门狗设备 */ /* O_CLOEXEC: 防止子进程继承,但看门狗通常需要保持打开 */ wdt_fd = open(WATCHDOG_DEV, O_WRONLY); if (wdt_fd < 0) { perror("无法打开看门狗设备(需要 root 权限?)"); fprintf(stderr, "提示: 尝试 sudo %s\n", argv[0]); return 1; } printf("设备打开成功 (fd=%d)\n", wdt_fd); /* 获取信息 */ if (show_info) { get_watchdog_info(wdt_fd); } /* 设置新的超时时间 */ if (new_timeout > 0) { if (set_timeout(wdt_fd, new_timeout) < 0) { close(wdt_fd); return 1; } } /* 显示当前超时 */ int current_timeout = get_timeout(wdt_fd); if (current_timeout > 0) { printf("当前看门狗超时: %d 秒\n", current_timeout); } /* 禁用看门狗 */ if (disable) { if (disable_watchdog(wdt_fd) < 0) { fprintf(stderr, "禁用看门狗失败(可能 nowayout=1)\n"); } close(wdt_fd); return 0; } /* 主测试循环 */ test_loop(wdt_fd, keepalive_interval, test_mode); /* 清理 */ printf("\n关闭看门狗设备...\n"); if (magic_close) { /* 尝试魔法关闭 */ printf("发送魔法关闭字符 'V'...\n"); if (write(wdt_fd, "V", 1) != 1) { perror("魔法关闭失败"); } } close(wdt_fd); printf("测试程序结束\n"); return 0; }