watchdog_app.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * it87_wdt_test.c - IT87 看门狗驱动测试程序
  3. *
  4. * 编译: gcc -o it87_wdt_test it87_wdt_test.c -Wall
  5. * 运行: sudo ./it87_wdt_test [选项]
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. #include <fcntl.h>
  12. #include <errno.h>
  13. #include <signal.h>
  14. #include <sys/ioctl.h>
  15. #include <linux/watchdog.h>
  16. #define WATCHDOG_DEV "/dev/watchdog"
  17. #define DEFAULT_KEEPALIVE 5 // 默认喂狗间隔(秒)
  18. static int wdt_fd = -1;
  19. static volatile int running = 1;
  20. static int test_mode = 0; // 0=正常喂狗, 1=停止喂狗测试重启, 2=触发喂狗后退出
  21. /* 信号处理:优雅退出 */
  22. static void signal_handler(int sig)
  23. {
  24. printf("\n[%d] 收到信号 %d,准备退出...\n", getpid(), sig);
  25. running = 0;
  26. }
  27. /* 打印使用说明 */
  28. static void print_usage(const char *prog)
  29. {
  30. printf("IT87 看门狗驱动测试程序\n\n");
  31. printf("用法: %s [选项]\n\n", prog);
  32. printf("选项:\n");
  33. printf(" -h 显示帮助\n");
  34. printf(" -t <秒> 设置喂狗间隔 (默认: %d秒)\n", DEFAULT_KEEPALIVE);
  35. printf(" -T <秒> 设置看门狗超时时间 (需要驱动支持)\n");
  36. printf(" -m 测试模式: 启动后停止喂狗,测试系统重启\n");
  37. printf(" -e 测试模式: 喂狗一次后立即退出(测试 nowayout)\n");
  38. printf(" -i 获取看门狗信息\n");
  39. printf(" -c 启用魔法关闭字符 'V'\n");
  40. printf(" -d 禁用看门狗(如果支持)\n");
  41. printf("\n示例:\n");
  42. printf(" %s # 正常测试,每5秒喂狗\n", prog);
  43. printf(" %s -t 2 # 每2秒喂狗一次\n", prog);
  44. printf(" %s -m -t 10 # 10秒后停止喂狗,系统应该重启\n", prog);
  45. printf(" %s -e # 测试 nowayout 模式\n", prog);
  46. printf("\n警告: 使用 -m 或 -e 选项会导致系统重启!\n");
  47. }
  48. /* 获取看门狗信息 */
  49. static void get_watchdog_info(int fd)
  50. {
  51. struct watchdog_info info;
  52. if (ioctl(fd, WDIOC_GETSUPPORT, &info) < 0) {
  53. perror("WDIOC_GETSUPPORT 失败");
  54. return;
  55. }
  56. printf("\n========== 看门狗信息 ==========\n");
  57. printf("标识: %s\n", info.identity);
  58. printf("固件版本: %u\n", info.firmware_version);
  59. printf("选项支持:\n");
  60. if (info.options & WDIOF_OVERHEAT) printf(" - 过热检测\n");
  61. if (info.options & WDIOF_FANFAULT) printf(" - 风扇故障\n");
  62. if (info.options & WDIOF_EXTERN1) printf(" - 外部信号1\n");
  63. if (info.options & WDIOF_EXTERN2) printf(" - 外部信号2\n");
  64. if (info.options & WDIOF_POWERUNDER) printf(" - 电源不足\n");
  65. if (info.options & WDIOF_CARDRESET) printf(" - 卡片重启\n");
  66. if (info.options & WDIOF_POWEROVER) printf(" - 电源过载\n");
  67. if (info.options & WDIOF_SETTIMEOUT) printf(" - 设置超时时间 ✓\n");
  68. if (info.options & WDIOF_MAGICCLOSE) printf(" - 魔法关闭 ✓\n");
  69. if (info.options & WDIOF_PRETIMEOUT) printf(" - 预超时\n");
  70. if (info.options & WDIOF_ALARMONLY) printf(" - 仅报警\n");
  71. if (info.options & WDIOF_KEEPALIVEPING) printf(" - 喂狗支持 ✓\n");
  72. printf("================================\n\n");
  73. }
  74. /* 获取当前超时时间 */
  75. static int get_timeout(int fd)
  76. {
  77. int timeout = 0;
  78. if (ioctl(fd, WDIOC_GETTIMEOUT, &timeout) < 0) {
  79. perror("WDIOC_GETTIMEOUT 失败");
  80. return -1;
  81. }
  82. return timeout;
  83. }
  84. /* 设置超时时间 */
  85. static int set_timeout(int fd, int timeout)
  86. {
  87. printf("设置超时时间为 %d 秒...\n", timeout);
  88. if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) < 0) {
  89. perror("WDIOC_SETTIMEOUT 失败");
  90. return -1;
  91. }
  92. printf("实际设置的超时时间: %d 秒\n", timeout);
  93. return 0;
  94. }
  95. /* 喂狗(keepalive) */
  96. static int keepalive(int fd)
  97. {
  98. int dummy;
  99. if (ioctl(fd, WDIOC_KEEPALIVE, &dummy) < 0) {
  100. perror("WDIOC_KEEPALIVE 失败");
  101. return -1;
  102. }
  103. return 0;
  104. }
  105. /* 禁用看门狗 */
  106. static int disable_watchdog(int fd)
  107. {
  108. printf("尝试禁用看门狗...\n");
  109. /* 方法1: 魔法关闭字符 */
  110. if (write(fd, "V", 1) != 1) {
  111. perror("魔法关闭字符写入失败");
  112. return -1;
  113. }
  114. printf("已发送魔法关闭字符 'V'\n");
  115. return 0;
  116. }
  117. /* 主测试循环 */
  118. static void test_loop(int fd, int keepalive_interval, int mode)
  119. {
  120. int count = 0;
  121. time_t start_time = time(NULL);
  122. printf("\n========== 开始测试 ==========\n");
  123. printf("PID: %d\n", getpid());
  124. printf("喂狗间隔: %d 秒\n", keepalive_interval);
  125. printf("当前超时设置: %d 秒\n", get_timeout(fd));
  126. if (mode == 1) {
  127. printf("\n⚠️ 测试模式: 将在 %d 秒后停止喂狗,系统应该重启!\n",
  128. keepalive_interval * 3);
  129. } else if (mode == 2) {
  130. printf("\n⚠️ 测试模式: 喂狗一次后立即退出,测试 nowayout\n");
  131. } else {
  132. printf("\n正常模式: 按 Ctrl+C 停止\n");
  133. }
  134. printf("==============================\n\n");
  135. /* 第一次喂狗(启动看门狗) */
  136. if (keepalive(fd) < 0) {
  137. fprintf(stderr, "初始喂狗失败\n");
  138. return;
  139. }
  140. printf("[%3d] 初始喂狗完成,看门狗已启动\n", count++);
  141. if (mode == 2) {
  142. /* 测试模式: 立即退出 */
  143. printf("立即退出,如果 nowayout=1,系统将在超时后重启\n");
  144. return;
  145. }
  146. /* 主循环 */
  147. while (running) {
  148. sleep(keepalive_interval);
  149. if (!running)
  150. break;
  151. /* 测试模式1: 在指定次数后停止喂狗 */
  152. if (mode == 1 && count >= 3) {
  153. printf("\n⚠️ 故意停止喂狗!系统应在 %d 秒后重启...\n",
  154. get_timeout(fd));
  155. printf("等待重启中(按 Ctrl+C 可尝试中断)...\n");
  156. /* 继续循环但不喂狗 */
  157. while (1) {
  158. sleep(1);
  159. }
  160. }
  161. /* 正常喂狗 */
  162. if (keepalive(fd) < 0) {
  163. fprintf(stderr, "喂狗失败\n");
  164. break;
  165. }
  166. time_t elapsed = time(NULL) - start_time;
  167. printf("[%3d] 已喂狗 (运行时间: %ld 秒)\n", count++, elapsed);
  168. fflush(stdout);
  169. }
  170. }
  171. int main(int argc, char *argv[])
  172. {
  173. int opt;
  174. int keepalive_interval = DEFAULT_KEEPALIVE;
  175. int new_timeout = 0;
  176. int show_info = 0;
  177. int magic_close = 0;
  178. int disable = 0;
  179. /* 解析命令行参数 */
  180. while ((opt = getopt(argc, argv, "ht:T:meicd")) != -1) {
  181. switch (opt) {
  182. case 'h':
  183. print_usage(argv[0]);
  184. return 0;
  185. case 't':
  186. keepalive_interval = atoi(optarg);
  187. if (keepalive_interval < 1) {
  188. fprintf(stderr, "错误: 喂狗间隔必须 >= 1 秒\n");
  189. return 1;
  190. }
  191. break;
  192. case 'T':
  193. new_timeout = atoi(optarg);
  194. break;
  195. case 'm':
  196. test_mode = 1; // 停止喂狗测试重启
  197. break;
  198. case 'e':
  199. test_mode = 2; // 立即退出测试 nowayout
  200. break;
  201. case 'i':
  202. show_info = 1;
  203. break;
  204. case 'c':
  205. magic_close = 1;
  206. break;
  207. case 'd':
  208. disable = 1;
  209. break;
  210. default:
  211. print_usage(argv[0]);
  212. return 1;
  213. }
  214. }
  215. /* 注册信号处理 */
  216. signal(SIGINT, signal_handler);
  217. signal(SIGTERM, signal_handler);
  218. printf("IT87 看门狗测试程序\n");
  219. printf("打开设备: %s\n", WATCHDOG_DEV);
  220. /* 打开看门狗设备 */
  221. /* O_CLOEXEC: 防止子进程继承,但看门狗通常需要保持打开 */
  222. wdt_fd = open(WATCHDOG_DEV, O_WRONLY);
  223. if (wdt_fd < 0) {
  224. perror("无法打开看门狗设备(需要 root 权限?)");
  225. fprintf(stderr, "提示: 尝试 sudo %s\n", argv[0]);
  226. return 1;
  227. }
  228. printf("设备打开成功 (fd=%d)\n", wdt_fd);
  229. /* 获取信息 */
  230. if (show_info) {
  231. get_watchdog_info(wdt_fd);
  232. }
  233. /* 设置新的超时时间 */
  234. if (new_timeout > 0) {
  235. if (set_timeout(wdt_fd, new_timeout) < 0) {
  236. close(wdt_fd);
  237. return 1;
  238. }
  239. }
  240. /* 显示当前超时 */
  241. int current_timeout = get_timeout(wdt_fd);
  242. if (current_timeout > 0) {
  243. printf("当前看门狗超时: %d 秒\n", current_timeout);
  244. }
  245. /* 禁用看门狗 */
  246. if (disable) {
  247. if (disable_watchdog(wdt_fd) < 0) {
  248. fprintf(stderr, "禁用看门狗失败(可能 nowayout=1)\n");
  249. }
  250. close(wdt_fd);
  251. return 0;
  252. }
  253. /* 主测试循环 */
  254. test_loop(wdt_fd, keepalive_interval, test_mode);
  255. /* 清理 */
  256. printf("\n关闭看门狗设备...\n");
  257. if (magic_close) {
  258. /* 尝试魔法关闭 */
  259. printf("发送魔法关闭字符 'V'...\n");
  260. if (write(wdt_fd, "V", 1) != 1) {
  261. perror("魔法关闭失败");
  262. }
  263. }
  264. close(wdt_fd);
  265. printf("测试程序结束\n");
  266. return 0;
  267. }