18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Watchdog Driver Test Program
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <errno.h>
78c2ecf20Sopenharmony_ci#include <stdio.h>
88c2ecf20Sopenharmony_ci#include <stdlib.h>
98c2ecf20Sopenharmony_ci#include <string.h>
108c2ecf20Sopenharmony_ci#include <unistd.h>
118c2ecf20Sopenharmony_ci#include <fcntl.h>
128c2ecf20Sopenharmony_ci#include <signal.h>
138c2ecf20Sopenharmony_ci#include <getopt.h>
148c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
158c2ecf20Sopenharmony_ci#include <linux/types.h>
168c2ecf20Sopenharmony_ci#include <linux/watchdog.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DEFAULT_PING_RATE	1
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciint fd;
218c2ecf20Sopenharmony_ciconst char v = 'V';
228c2ecf20Sopenharmony_cistatic const char sopts[] = "bdehp:t:Tn:NLf:i";
238c2ecf20Sopenharmony_cistatic const struct option lopts[] = {
248c2ecf20Sopenharmony_ci	{"bootstatus",          no_argument, NULL, 'b'},
258c2ecf20Sopenharmony_ci	{"disable",             no_argument, NULL, 'd'},
268c2ecf20Sopenharmony_ci	{"enable",              no_argument, NULL, 'e'},
278c2ecf20Sopenharmony_ci	{"help",                no_argument, NULL, 'h'},
288c2ecf20Sopenharmony_ci	{"pingrate",      required_argument, NULL, 'p'},
298c2ecf20Sopenharmony_ci	{"timeout",       required_argument, NULL, 't'},
308c2ecf20Sopenharmony_ci	{"gettimeout",          no_argument, NULL, 'T'},
318c2ecf20Sopenharmony_ci	{"pretimeout",    required_argument, NULL, 'n'},
328c2ecf20Sopenharmony_ci	{"getpretimeout",       no_argument, NULL, 'N'},
338c2ecf20Sopenharmony_ci	{"gettimeleft",		no_argument, NULL, 'L'},
348c2ecf20Sopenharmony_ci	{"file",          required_argument, NULL, 'f'},
358c2ecf20Sopenharmony_ci	{"info",		no_argument, NULL, 'i'},
368c2ecf20Sopenharmony_ci	{NULL,                  no_argument, NULL, 0x0}
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * This function simply sends an IOCTL to the driver, which in turn ticks
418c2ecf20Sopenharmony_ci * the PC Watchdog card to reset its internal timer so it doesn't trigger
428c2ecf20Sopenharmony_ci * a computer reset.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_cistatic void keep_alive(void)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int dummy;
478c2ecf20Sopenharmony_ci	int ret;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	ret = ioctl(fd, WDIOC_KEEPALIVE, &dummy);
508c2ecf20Sopenharmony_ci	if (!ret)
518c2ecf20Sopenharmony_ci		printf(".");
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/*
558c2ecf20Sopenharmony_ci * The main program.  Run the program with "-d" to disable the card,
568c2ecf20Sopenharmony_ci * or "-e" to enable the card.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void term(int sig)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	int ret = write(fd, &v, 1);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	close(fd);
648c2ecf20Sopenharmony_ci	if (ret < 0)
658c2ecf20Sopenharmony_ci		printf("\nStopping watchdog ticks failed (%d)...\n", errno);
668c2ecf20Sopenharmony_ci	else
678c2ecf20Sopenharmony_ci		printf("\nStopping watchdog ticks...\n");
688c2ecf20Sopenharmony_ci	exit(0);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void usage(char *progname)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	printf("Usage: %s [options]\n", progname);
748c2ecf20Sopenharmony_ci	printf(" -f, --file\t\tOpen watchdog device file\n");
758c2ecf20Sopenharmony_ci	printf("\t\t\tDefault is /dev/watchdog\n");
768c2ecf20Sopenharmony_ci	printf(" -i, --info\t\tShow watchdog_info\n");
778c2ecf20Sopenharmony_ci	printf(" -b, --bootstatus\tGet last boot status (Watchdog/POR)\n");
788c2ecf20Sopenharmony_ci	printf(" -d, --disable\t\tTurn off the watchdog timer\n");
798c2ecf20Sopenharmony_ci	printf(" -e, --enable\t\tTurn on the watchdog timer\n");
808c2ecf20Sopenharmony_ci	printf(" -h, --help\t\tPrint the help message\n");
818c2ecf20Sopenharmony_ci	printf(" -p, --pingrate=P\tSet ping rate to P seconds (default %d)\n",
828c2ecf20Sopenharmony_ci	       DEFAULT_PING_RATE);
838c2ecf20Sopenharmony_ci	printf(" -t, --timeout=T\tSet timeout to T seconds\n");
848c2ecf20Sopenharmony_ci	printf(" -T, --gettimeout\tGet the timeout\n");
858c2ecf20Sopenharmony_ci	printf(" -n, --pretimeout=T\tSet the pretimeout to T seconds\n");
868c2ecf20Sopenharmony_ci	printf(" -N, --getpretimeout\tGet the pretimeout\n");
878c2ecf20Sopenharmony_ci	printf(" -L, --gettimeleft\tGet the time left until timer expires\n");
888c2ecf20Sopenharmony_ci	printf("\n");
898c2ecf20Sopenharmony_ci	printf("Parameters are parsed left-to-right in real-time.\n");
908c2ecf20Sopenharmony_ci	printf("Example: %s -d -t 10 -p 5 -e\n", progname);
918c2ecf20Sopenharmony_ci	printf("Example: %s -t 12 -T -n 7 -N\n", progname);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int flags;
978c2ecf20Sopenharmony_ci	unsigned int ping_rate = DEFAULT_PING_RATE;
988c2ecf20Sopenharmony_ci	int ret;
998c2ecf20Sopenharmony_ci	int c;
1008c2ecf20Sopenharmony_ci	int oneshot = 0;
1018c2ecf20Sopenharmony_ci	char *file = "/dev/watchdog";
1028c2ecf20Sopenharmony_ci	struct watchdog_info info;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	setbuf(stdout, NULL);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
1078c2ecf20Sopenharmony_ci		if (c == 'f')
1088c2ecf20Sopenharmony_ci			file = optarg;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	fd = open(file, O_WRONLY);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (fd == -1) {
1148c2ecf20Sopenharmony_ci		if (errno == ENOENT)
1158c2ecf20Sopenharmony_ci			printf("Watchdog device (%s) not found.\n", file);
1168c2ecf20Sopenharmony_ci		else if (errno == EACCES)
1178c2ecf20Sopenharmony_ci			printf("Run watchdog as root.\n");
1188c2ecf20Sopenharmony_ci		else
1198c2ecf20Sopenharmony_ci			printf("Watchdog device open failed %s\n",
1208c2ecf20Sopenharmony_ci				strerror(errno));
1218c2ecf20Sopenharmony_ci		exit(-1);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/*
1258c2ecf20Sopenharmony_ci	 * Validate that `file` is a watchdog device
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	ret = ioctl(fd, WDIOC_GETSUPPORT, &info);
1288c2ecf20Sopenharmony_ci	if (ret) {
1298c2ecf20Sopenharmony_ci		printf("WDIOC_GETSUPPORT error '%s'\n", strerror(errno));
1308c2ecf20Sopenharmony_ci		close(fd);
1318c2ecf20Sopenharmony_ci		exit(ret);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	optind = 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
1378c2ecf20Sopenharmony_ci		switch (c) {
1388c2ecf20Sopenharmony_ci		case 'b':
1398c2ecf20Sopenharmony_ci			flags = 0;
1408c2ecf20Sopenharmony_ci			oneshot = 1;
1418c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_GETBOOTSTATUS, &flags);
1428c2ecf20Sopenharmony_ci			if (!ret)
1438c2ecf20Sopenharmony_ci				printf("Last boot is caused by: %s.\n", (flags != 0) ?
1448c2ecf20Sopenharmony_ci					"Watchdog" : "Power-On-Reset");
1458c2ecf20Sopenharmony_ci			else
1468c2ecf20Sopenharmony_ci				printf("WDIOC_GETBOOTSTATUS error '%s'\n", strerror(errno));
1478c2ecf20Sopenharmony_ci			break;
1488c2ecf20Sopenharmony_ci		case 'd':
1498c2ecf20Sopenharmony_ci			flags = WDIOS_DISABLECARD;
1508c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_SETOPTIONS, &flags);
1518c2ecf20Sopenharmony_ci			if (!ret)
1528c2ecf20Sopenharmony_ci				printf("Watchdog card disabled.\n");
1538c2ecf20Sopenharmony_ci			else {
1548c2ecf20Sopenharmony_ci				printf("WDIOS_DISABLECARD error '%s'\n", strerror(errno));
1558c2ecf20Sopenharmony_ci				oneshot = 1;
1568c2ecf20Sopenharmony_ci			}
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci		case 'e':
1598c2ecf20Sopenharmony_ci			flags = WDIOS_ENABLECARD;
1608c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_SETOPTIONS, &flags);
1618c2ecf20Sopenharmony_ci			if (!ret)
1628c2ecf20Sopenharmony_ci				printf("Watchdog card enabled.\n");
1638c2ecf20Sopenharmony_ci			else {
1648c2ecf20Sopenharmony_ci				printf("WDIOS_ENABLECARD error '%s'\n", strerror(errno));
1658c2ecf20Sopenharmony_ci				oneshot = 1;
1668c2ecf20Sopenharmony_ci			}
1678c2ecf20Sopenharmony_ci			break;
1688c2ecf20Sopenharmony_ci		case 'p':
1698c2ecf20Sopenharmony_ci			ping_rate = strtoul(optarg, NULL, 0);
1708c2ecf20Sopenharmony_ci			if (!ping_rate)
1718c2ecf20Sopenharmony_ci				ping_rate = DEFAULT_PING_RATE;
1728c2ecf20Sopenharmony_ci			printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
1738c2ecf20Sopenharmony_ci			break;
1748c2ecf20Sopenharmony_ci		case 't':
1758c2ecf20Sopenharmony_ci			flags = strtoul(optarg, NULL, 0);
1768c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_SETTIMEOUT, &flags);
1778c2ecf20Sopenharmony_ci			if (!ret)
1788c2ecf20Sopenharmony_ci				printf("Watchdog timeout set to %u seconds.\n", flags);
1798c2ecf20Sopenharmony_ci			else {
1808c2ecf20Sopenharmony_ci				printf("WDIOC_SETTIMEOUT error '%s'\n", strerror(errno));
1818c2ecf20Sopenharmony_ci				oneshot = 1;
1828c2ecf20Sopenharmony_ci			}
1838c2ecf20Sopenharmony_ci			break;
1848c2ecf20Sopenharmony_ci		case 'T':
1858c2ecf20Sopenharmony_ci			oneshot = 1;
1868c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_GETTIMEOUT, &flags);
1878c2ecf20Sopenharmony_ci			if (!ret)
1888c2ecf20Sopenharmony_ci				printf("WDIOC_GETTIMEOUT returns %u seconds.\n", flags);
1898c2ecf20Sopenharmony_ci			else
1908c2ecf20Sopenharmony_ci				printf("WDIOC_GETTIMEOUT error '%s'\n", strerror(errno));
1918c2ecf20Sopenharmony_ci			break;
1928c2ecf20Sopenharmony_ci		case 'n':
1938c2ecf20Sopenharmony_ci			flags = strtoul(optarg, NULL, 0);
1948c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_SETPRETIMEOUT, &flags);
1958c2ecf20Sopenharmony_ci			if (!ret)
1968c2ecf20Sopenharmony_ci				printf("Watchdog pretimeout set to %u seconds.\n", flags);
1978c2ecf20Sopenharmony_ci			else {
1988c2ecf20Sopenharmony_ci				printf("WDIOC_SETPRETIMEOUT error '%s'\n", strerror(errno));
1998c2ecf20Sopenharmony_ci				oneshot = 1;
2008c2ecf20Sopenharmony_ci			}
2018c2ecf20Sopenharmony_ci			break;
2028c2ecf20Sopenharmony_ci		case 'N':
2038c2ecf20Sopenharmony_ci			oneshot = 1;
2048c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_GETPRETIMEOUT, &flags);
2058c2ecf20Sopenharmony_ci			if (!ret)
2068c2ecf20Sopenharmony_ci				printf("WDIOC_GETPRETIMEOUT returns %u seconds.\n", flags);
2078c2ecf20Sopenharmony_ci			else
2088c2ecf20Sopenharmony_ci				printf("WDIOC_GETPRETIMEOUT error '%s'\n", strerror(errno));
2098c2ecf20Sopenharmony_ci			break;
2108c2ecf20Sopenharmony_ci		case 'L':
2118c2ecf20Sopenharmony_ci			oneshot = 1;
2128c2ecf20Sopenharmony_ci			ret = ioctl(fd, WDIOC_GETTIMELEFT, &flags);
2138c2ecf20Sopenharmony_ci			if (!ret)
2148c2ecf20Sopenharmony_ci				printf("WDIOC_GETTIMELEFT returns %u seconds.\n", flags);
2158c2ecf20Sopenharmony_ci			else
2168c2ecf20Sopenharmony_ci				printf("WDIOC_GETTIMELEFT error '%s'\n", strerror(errno));
2178c2ecf20Sopenharmony_ci			break;
2188c2ecf20Sopenharmony_ci		case 'f':
2198c2ecf20Sopenharmony_ci			/* Handled above */
2208c2ecf20Sopenharmony_ci			break;
2218c2ecf20Sopenharmony_ci		case 'i':
2228c2ecf20Sopenharmony_ci			/*
2238c2ecf20Sopenharmony_ci			 * watchdog_info was obtained as part of file open
2248c2ecf20Sopenharmony_ci			 * validation. So we just show it here.
2258c2ecf20Sopenharmony_ci			 */
2268c2ecf20Sopenharmony_ci			oneshot = 1;
2278c2ecf20Sopenharmony_ci			printf("watchdog_info:\n");
2288c2ecf20Sopenharmony_ci			printf(" identity:\t\t%s\n", info.identity);
2298c2ecf20Sopenharmony_ci			printf(" firmware_version:\t%u\n",
2308c2ecf20Sopenharmony_ci			       info.firmware_version);
2318c2ecf20Sopenharmony_ci			printf(" options:\t\t%08x\n", info.options);
2328c2ecf20Sopenharmony_ci			break;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		default:
2358c2ecf20Sopenharmony_ci			usage(argv[0]);
2368c2ecf20Sopenharmony_ci			goto end;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (oneshot)
2418c2ecf20Sopenharmony_ci		goto end;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	printf("Watchdog Ticking Away!\n");
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	signal(SIGINT, term);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	while (1) {
2488c2ecf20Sopenharmony_ci		keep_alive();
2498c2ecf20Sopenharmony_ci		sleep(ping_rate);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ciend:
2528c2ecf20Sopenharmony_ci	ret = write(fd, &v, 1);
2538c2ecf20Sopenharmony_ci	if (ret < 0)
2548c2ecf20Sopenharmony_ci		printf("Stopping watchdog ticks failed (%d)...\n", errno);
2558c2ecf20Sopenharmony_ci	close(fd);
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
258