162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci* Watchdog Driver Test Program 462306a36Sopenharmony_ci* - Tests all ioctls 562306a36Sopenharmony_ci* - Tests Magic Close - CONFIG_WATCHDOG_NOWAYOUT 662306a36Sopenharmony_ci* - Could be tested against softdog driver on systems that 762306a36Sopenharmony_ci* don't have watchdog hardware. 862306a36Sopenharmony_ci* - TODO: 962306a36Sopenharmony_ci* - Enhance test to add coverage for WDIOC_GETTEMP. 1062306a36Sopenharmony_ci* 1162306a36Sopenharmony_ci* Reference: Documentation/watchdog/watchdog-api.rst 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <errno.h> 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <stdlib.h> 1762306a36Sopenharmony_ci#include <string.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <fcntl.h> 2062306a36Sopenharmony_ci#include <signal.h> 2162306a36Sopenharmony_ci#include <getopt.h> 2262306a36Sopenharmony_ci#include <sys/ioctl.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/watchdog.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DEFAULT_PING_RATE 1 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciint fd; 2962306a36Sopenharmony_ciconst char v = 'V'; 3062306a36Sopenharmony_cistatic const char sopts[] = "bdehp:st:Tn:NLf:i"; 3162306a36Sopenharmony_cistatic const struct option lopts[] = { 3262306a36Sopenharmony_ci {"bootstatus", no_argument, NULL, 'b'}, 3362306a36Sopenharmony_ci {"disable", no_argument, NULL, 'd'}, 3462306a36Sopenharmony_ci {"enable", no_argument, NULL, 'e'}, 3562306a36Sopenharmony_ci {"help", no_argument, NULL, 'h'}, 3662306a36Sopenharmony_ci {"pingrate", required_argument, NULL, 'p'}, 3762306a36Sopenharmony_ci {"status", no_argument, NULL, 's'}, 3862306a36Sopenharmony_ci {"timeout", required_argument, NULL, 't'}, 3962306a36Sopenharmony_ci {"gettimeout", no_argument, NULL, 'T'}, 4062306a36Sopenharmony_ci {"pretimeout", required_argument, NULL, 'n'}, 4162306a36Sopenharmony_ci {"getpretimeout", no_argument, NULL, 'N'}, 4262306a36Sopenharmony_ci {"gettimeleft", no_argument, NULL, 'L'}, 4362306a36Sopenharmony_ci {"file", required_argument, NULL, 'f'}, 4462306a36Sopenharmony_ci {"info", no_argument, NULL, 'i'}, 4562306a36Sopenharmony_ci {NULL, no_argument, NULL, 0x0} 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * This function simply sends an IOCTL to the driver, which in turn ticks 5062306a36Sopenharmony_ci * the PC Watchdog card to reset its internal timer so it doesn't trigger 5162306a36Sopenharmony_ci * a computer reset. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic void keep_alive(void) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int dummy; 5662306a36Sopenharmony_ci int ret; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_KEEPALIVE, &dummy); 5962306a36Sopenharmony_ci if (!ret) 6062306a36Sopenharmony_ci printf("."); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * The main program. Run the program with "-d" to disable the card, 6562306a36Sopenharmony_ci * or "-e" to enable the card. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void term(int sig) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int ret = write(fd, &v, 1); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci close(fd); 7362306a36Sopenharmony_ci if (ret < 0) 7462306a36Sopenharmony_ci printf("\nStopping watchdog ticks failed (%d)...\n", errno); 7562306a36Sopenharmony_ci else 7662306a36Sopenharmony_ci printf("\nStopping watchdog ticks...\n"); 7762306a36Sopenharmony_ci exit(0); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void usage(char *progname) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci printf("Usage: %s [options]\n", progname); 8362306a36Sopenharmony_ci printf(" -f, --file\t\tOpen watchdog device file\n"); 8462306a36Sopenharmony_ci printf("\t\t\tDefault is /dev/watchdog\n"); 8562306a36Sopenharmony_ci printf(" -i, --info\t\tShow watchdog_info\n"); 8662306a36Sopenharmony_ci printf(" -s, --status\t\tGet status & supported features\n"); 8762306a36Sopenharmony_ci printf(" -b, --bootstatus\tGet last boot status (Watchdog/POR)\n"); 8862306a36Sopenharmony_ci printf(" -d, --disable\t\tTurn off the watchdog timer\n"); 8962306a36Sopenharmony_ci printf(" -e, --enable\t\tTurn on the watchdog timer\n"); 9062306a36Sopenharmony_ci printf(" -h, --help\t\tPrint the help message\n"); 9162306a36Sopenharmony_ci printf(" -p, --pingrate=P\tSet ping rate to P seconds (default %d)\n", 9262306a36Sopenharmony_ci DEFAULT_PING_RATE); 9362306a36Sopenharmony_ci printf(" -t, --timeout=T\tSet timeout to T seconds\n"); 9462306a36Sopenharmony_ci printf(" -T, --gettimeout\tGet the timeout\n"); 9562306a36Sopenharmony_ci printf(" -n, --pretimeout=T\tSet the pretimeout to T seconds\n"); 9662306a36Sopenharmony_ci printf(" -N, --getpretimeout\tGet the pretimeout\n"); 9762306a36Sopenharmony_ci printf(" -L, --gettimeleft\tGet the time left until timer expires\n"); 9862306a36Sopenharmony_ci printf("\n"); 9962306a36Sopenharmony_ci printf("Parameters are parsed left-to-right in real-time.\n"); 10062306a36Sopenharmony_ci printf("Example: %s -d -t 10 -p 5 -e\n", progname); 10162306a36Sopenharmony_ci printf("Example: %s -t 12 -T -n 7 -N\n", progname); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct wdiof_status { 10562306a36Sopenharmony_ci int flag; 10662306a36Sopenharmony_ci const char *status_str; 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define WDIOF_NUM_STATUS 8 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct wdiof_status wdiof_status[WDIOF_NUM_STATUS] = { 11262306a36Sopenharmony_ci {WDIOF_SETTIMEOUT, "Set timeout (in seconds)"}, 11362306a36Sopenharmony_ci {WDIOF_MAGICCLOSE, "Supports magic close char"}, 11462306a36Sopenharmony_ci {WDIOF_PRETIMEOUT, "Pretimeout (in seconds), get/set"}, 11562306a36Sopenharmony_ci {WDIOF_ALARMONLY, "Watchdog triggers a management or other external alarm not a reboot"}, 11662306a36Sopenharmony_ci {WDIOF_KEEPALIVEPING, "Keep alive ping reply"}, 11762306a36Sopenharmony_ci {WDIOS_DISABLECARD, "Turn off the watchdog timer"}, 11862306a36Sopenharmony_ci {WDIOS_ENABLECARD, "Turn on the watchdog timer"}, 11962306a36Sopenharmony_ci {WDIOS_TEMPPANIC, "Kernel panic on temperature trip"}, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void print_status(int flags) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int wdiof = 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (flags == WDIOS_UNKNOWN) { 12762306a36Sopenharmony_ci printf("Unknown status error from WDIOC_GETSTATUS\n"); 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (wdiof = 0; wdiof < WDIOF_NUM_STATUS; wdiof++) { 13262306a36Sopenharmony_ci if (flags & wdiof_status[wdiof].flag) 13362306a36Sopenharmony_ci printf("Support/Status: %s\n", 13462306a36Sopenharmony_ci wdiof_status[wdiof].status_str); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define WDIOF_NUM_BOOTSTATUS 7 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct wdiof_status wdiof_bootstatus[WDIOF_NUM_BOOTSTATUS] = { 14162306a36Sopenharmony_ci {WDIOF_OVERHEAT, "Reset due to CPU overheat"}, 14262306a36Sopenharmony_ci {WDIOF_FANFAULT, "Fan failed"}, 14362306a36Sopenharmony_ci {WDIOF_EXTERN1, "External relay 1"}, 14462306a36Sopenharmony_ci {WDIOF_EXTERN2, "External relay 2"}, 14562306a36Sopenharmony_ci {WDIOF_POWERUNDER, "Power bad/power fault"}, 14662306a36Sopenharmony_ci {WDIOF_CARDRESET, "Card previously reset the CPU"}, 14762306a36Sopenharmony_ci {WDIOF_POWEROVER, "Power over voltage"}, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void print_boot_status(int flags) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int wdiof = 0; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (flags == WDIOF_UNKNOWN) { 15562306a36Sopenharmony_ci printf("Unknown flag error from WDIOC_GETBOOTSTATUS\n"); 15662306a36Sopenharmony_ci return; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (flags == 0) { 16062306a36Sopenharmony_ci printf("Last boot is caused by: Power-On-Reset\n"); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (wdiof = 0; wdiof < WDIOF_NUM_BOOTSTATUS; wdiof++) { 16562306a36Sopenharmony_ci if (flags & wdiof_bootstatus[wdiof].flag) 16662306a36Sopenharmony_ci printf("Last boot is caused by: %s\n", 16762306a36Sopenharmony_ci wdiof_bootstatus[wdiof].status_str); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciint main(int argc, char *argv[]) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int flags; 17462306a36Sopenharmony_ci unsigned int ping_rate = DEFAULT_PING_RATE; 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci int c; 17762306a36Sopenharmony_ci int oneshot = 0; 17862306a36Sopenharmony_ci char *file = "/dev/watchdog"; 17962306a36Sopenharmony_ci struct watchdog_info info; 18062306a36Sopenharmony_ci int temperature; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci setbuf(stdout, NULL); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { 18562306a36Sopenharmony_ci if (c == 'f') 18662306a36Sopenharmony_ci file = optarg; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci fd = open(file, O_WRONLY); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (fd == -1) { 19262306a36Sopenharmony_ci if (errno == ENOENT) 19362306a36Sopenharmony_ci printf("Watchdog device (%s) not found.\n", file); 19462306a36Sopenharmony_ci else if (errno == EACCES) 19562306a36Sopenharmony_ci printf("Run watchdog as root.\n"); 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci printf("Watchdog device open failed %s\n", 19862306a36Sopenharmony_ci strerror(errno)); 19962306a36Sopenharmony_ci exit(-1); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * Validate that `file` is a watchdog device 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETSUPPORT, &info); 20662306a36Sopenharmony_ci if (ret) { 20762306a36Sopenharmony_ci printf("WDIOC_GETSUPPORT error '%s'\n", strerror(errno)); 20862306a36Sopenharmony_ci close(fd); 20962306a36Sopenharmony_ci exit(ret); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci optind = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { 21562306a36Sopenharmony_ci switch (c) { 21662306a36Sopenharmony_ci case 'b': 21762306a36Sopenharmony_ci flags = 0; 21862306a36Sopenharmony_ci oneshot = 1; 21962306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETBOOTSTATUS, &flags); 22062306a36Sopenharmony_ci if (!ret) 22162306a36Sopenharmony_ci print_boot_status(flags); 22262306a36Sopenharmony_ci else 22362306a36Sopenharmony_ci printf("WDIOC_GETBOOTSTATUS error '%s'\n", strerror(errno)); 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case 'd': 22662306a36Sopenharmony_ci flags = WDIOS_DISABLECARD; 22762306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_SETOPTIONS, &flags); 22862306a36Sopenharmony_ci if (!ret) 22962306a36Sopenharmony_ci printf("Watchdog card disabled.\n"); 23062306a36Sopenharmony_ci else { 23162306a36Sopenharmony_ci printf("WDIOS_DISABLECARD error '%s'\n", strerror(errno)); 23262306a36Sopenharmony_ci oneshot = 1; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case 'e': 23662306a36Sopenharmony_ci flags = WDIOS_ENABLECARD; 23762306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_SETOPTIONS, &flags); 23862306a36Sopenharmony_ci if (!ret) 23962306a36Sopenharmony_ci printf("Watchdog card enabled.\n"); 24062306a36Sopenharmony_ci else { 24162306a36Sopenharmony_ci printf("WDIOS_ENABLECARD error '%s'\n", strerror(errno)); 24262306a36Sopenharmony_ci oneshot = 1; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case 'p': 24662306a36Sopenharmony_ci ping_rate = strtoul(optarg, NULL, 0); 24762306a36Sopenharmony_ci if (!ping_rate) 24862306a36Sopenharmony_ci ping_rate = DEFAULT_PING_RATE; 24962306a36Sopenharmony_ci printf("Watchdog ping rate set to %u seconds.\n", ping_rate); 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case 's': 25262306a36Sopenharmony_ci flags = 0; 25362306a36Sopenharmony_ci oneshot = 1; 25462306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETSTATUS, &flags); 25562306a36Sopenharmony_ci if (!ret) 25662306a36Sopenharmony_ci print_status(flags); 25762306a36Sopenharmony_ci else 25862306a36Sopenharmony_ci printf("WDIOC_GETSTATUS error '%s'\n", strerror(errno)); 25962306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETTEMP, &temperature); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci printf("WDIOC_GETTEMP: '%s'\n", strerror(errno)); 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci printf("Temperature %d\n", temperature); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case 't': 26762306a36Sopenharmony_ci flags = strtoul(optarg, NULL, 0); 26862306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_SETTIMEOUT, &flags); 26962306a36Sopenharmony_ci if (!ret) 27062306a36Sopenharmony_ci printf("Watchdog timeout set to %u seconds.\n", flags); 27162306a36Sopenharmony_ci else { 27262306a36Sopenharmony_ci printf("WDIOC_SETTIMEOUT error '%s'\n", strerror(errno)); 27362306a36Sopenharmony_ci oneshot = 1; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case 'T': 27762306a36Sopenharmony_ci oneshot = 1; 27862306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETTIMEOUT, &flags); 27962306a36Sopenharmony_ci if (!ret) 28062306a36Sopenharmony_ci printf("WDIOC_GETTIMEOUT returns %u seconds.\n", flags); 28162306a36Sopenharmony_ci else 28262306a36Sopenharmony_ci printf("WDIOC_GETTIMEOUT error '%s'\n", strerror(errno)); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case 'n': 28562306a36Sopenharmony_ci flags = strtoul(optarg, NULL, 0); 28662306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_SETPRETIMEOUT, &flags); 28762306a36Sopenharmony_ci if (!ret) 28862306a36Sopenharmony_ci printf("Watchdog pretimeout set to %u seconds.\n", flags); 28962306a36Sopenharmony_ci else { 29062306a36Sopenharmony_ci printf("WDIOC_SETPRETIMEOUT error '%s'\n", strerror(errno)); 29162306a36Sopenharmony_ci oneshot = 1; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case 'N': 29562306a36Sopenharmony_ci oneshot = 1; 29662306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETPRETIMEOUT, &flags); 29762306a36Sopenharmony_ci if (!ret) 29862306a36Sopenharmony_ci printf("WDIOC_GETPRETIMEOUT returns %u seconds.\n", flags); 29962306a36Sopenharmony_ci else 30062306a36Sopenharmony_ci printf("WDIOC_GETPRETIMEOUT error '%s'\n", strerror(errno)); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case 'L': 30362306a36Sopenharmony_ci oneshot = 1; 30462306a36Sopenharmony_ci ret = ioctl(fd, WDIOC_GETTIMELEFT, &flags); 30562306a36Sopenharmony_ci if (!ret) 30662306a36Sopenharmony_ci printf("WDIOC_GETTIMELEFT returns %u seconds.\n", flags); 30762306a36Sopenharmony_ci else 30862306a36Sopenharmony_ci printf("WDIOC_GETTIMELEFT error '%s'\n", strerror(errno)); 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci case 'f': 31162306a36Sopenharmony_ci /* Handled above */ 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 'i': 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * watchdog_info was obtained as part of file open 31662306a36Sopenharmony_ci * validation. So we just show it here. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci oneshot = 1; 31962306a36Sopenharmony_ci printf("watchdog_info:\n"); 32062306a36Sopenharmony_ci printf(" identity:\t\t%s\n", info.identity); 32162306a36Sopenharmony_ci printf(" firmware_version:\t%u\n", 32262306a36Sopenharmony_ci info.firmware_version); 32362306a36Sopenharmony_ci print_status(info.options); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci default: 32762306a36Sopenharmony_ci usage(argv[0]); 32862306a36Sopenharmony_ci goto end; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (oneshot) 33362306a36Sopenharmony_ci goto end; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci printf("Watchdog Ticking Away!\n"); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci signal(SIGINT, term); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci while (1) { 34062306a36Sopenharmony_ci keep_alive(); 34162306a36Sopenharmony_ci sleep(ping_rate); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ciend: 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * Send specific magic character 'V' just in case Magic Close is 34662306a36Sopenharmony_ci * enabled to ensure watchdog gets disabled on close. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci ret = write(fd, &v, 1); 34962306a36Sopenharmony_ci if (ret < 0) 35062306a36Sopenharmony_ci printf("Stopping watchdog ticks failed (%d)...\n", errno); 35162306a36Sopenharmony_ci close(fd); 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 354