CHROMIUMOS: daisydog: convert test/demo code to a daemon

Chromium.org copyright was missing.
Setup main loop so we would determine HW watchdog reset interval and pet
more often than that. :)

BUG=chrome-os-partner:9403
TEST=manual

Change-Id: I399e010b6ebefdd164572952a056a28b6a726371
Signed-off-by: Grant Grundler <grundler@chromium.org>
diff --git a/daisydog.c b/daisydog.c
index 071a7db..d1c1a23 100644
--- a/daisydog.c
+++ b/daisydog.c
@@ -1,6 +1,8 @@
 /*
- * Linux watchdog demo for LPC313x
+ * Simple Linux HW watchdog daemon
+ *
  * Copyright (c) 2010 Daniel Widyanko. All rights reserved.
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,6 +31,8 @@
 #include <linux/watchdog.h>
 
 #define WATCHDOGDEV "/dev/watchdog"
+#define MIN_WD_TIMEOUT 5	/* WD must be at least 5 seconds */
+
 static const char *const short_options = "hd:i:";
 static const struct option long_options[] = {
 	{"help", 0, NULL, 'h'},
@@ -42,32 +46,31 @@
 	fprintf(stream, "Usage: %s [options]\n", app_name);
 	fprintf(stream,
 " -h  --help                Display this usage information.\n"
-" -d  --dev <device_file>   Use <device_file> as watchdog device file.\n"
-"                           The default device file is '/dev/watchdog'\n"
-" -i  --interval <interval> Change the watchdog interval time\n");
+" -d  --dev <device_file>   Use <device_file> as HW watchdog device file.\n"
+"                           The default is '/dev/watchdog'\n"
+" -i  --interval <interval> Change the HW watchdog interval time\n"
+"                           Must be at least %d seconds\n", MIN_WD_TIMEOUT
+	);
 
 	exit(exit_code);
 }
 
 int main(int argc, char **argv)
 {
-	int fd;			/* File handler for watchdog */
-	int interval;		/* Watchdog timeout interval (in secs) */
+	int fd;			/* File handler for HW watchdog */
 	int bootstatus;		/* Wathdog last boot status */
-	char *dev;		/* Watchdog default device file */
+	char *dev = WATCHDOGDEV;/* Watchdog default device file */
 
 	int next_option;	/* getopt iteration var */
-	char kick_watchdog;	/* kick_watchdog options */
-
-	/* Init variables */
-	dev = WATCHDOGDEV;
-	interval = 0;
-	kick_watchdog = 0;
+	int wd_timeout;		/* when HW watchdog goes balistic */
+	int interval = 0;	/* user parameter for wd_timeout */
+	int pet_interval;	/* how often we should pet the HW watchdog */
+	int slept_well;		/* return of sleep call */
 
 	/* Parse options if any */
 	do {
 		next_option = getopt_long(argc, argv, short_options,
-					  long_options, NULL);
+					long_options, NULL);
 		switch (next_option) {
 		case 'h':
 			print_usage(stdout, argv[0], EXIT_SUCCESS);
@@ -76,9 +79,11 @@
 			break;
 		case 'i':
 			interval = atoi(optarg);
+			if (interval < MIN_WD_TIMEOUT)
+				print_usage(stderr, argv[0], -EINVAL);
 			break;
 		case '?':	/* Invalid options */
-			print_usage(stderr, argv[0], EXIT_FAILURE);
+			print_usage(stderr, argv[0], -EINVAL);
 		case -1:	/* Done with options */
 			break;
 		default:	/* Unexpected stuffs */
@@ -95,26 +100,39 @@
 		exit(EXIT_FAILURE);
 	}
 
-	/* If user wants to change the watchdog interval */
-	if (interval != 0) {
-		fprintf(stdout, "Set watchdog interval to %d\n", interval);
+	/* If user wants to change the HW watchdog timeout */
+	if (interval) {
 		if (ioctl(fd, WDIOC_SETTIMEOUT, &interval) != 0) {
-			fprintf(stderr,
-				"Error: Set watchdog interval failed\n");
+			fprintf(stderr, "Error: Could not set HW watchdog"
+					"interval to %d\n", interval);
 			exit(EXIT_FAILURE);
 		}
+
 	}
 
-	/* Display current watchdog interval */
-	if (ioctl(fd, WDIOC_GETTIMEOUT, &interval) == 0) {
-		fprintf(stdout, "Current watchdog interval is %d\n",
-			interval);
+	/* Get/Display current HW watchdog interval.
+	 * Let user know if it's not exactly what they specified.
+	 */
+	if (ioctl(fd, WDIOC_GETTIMEOUT, &wd_timeout) == 0) {
+		fprintf(stdout, "HW watchdog interval is %d seconds",
+				wd_timeout);
+
+		if (interval && interval != wd_timeout)
+			fprintf(stdout, " (user asked for %d seconds)\n",
+					interval);
+
+		fprintf(stdout, "\n");
 	} else {
-		fprintf(stderr, "Error: Cannot read watchdog interval\n");
+		fprintf(stderr, "Error: Cannot read HW watchdog interval\n");
 		exit(EXIT_FAILURE);
 	}
 
-	/* Check if last boot is caused by watchdog */
+	/* Adjust petting interval to give some "margin of error" vs
+	 * when the HW watchdog will fire.
+	 */
+	pet_interval = wd_timeout / 2;
+
+	/* Check if last boot is caused by HW watchdog */
 	if (ioctl(fd, WDIOC_GETBOOTSTATUS, &bootstatus) == 0) {
 		fprintf(stdout, "Last boot is caused by : %s\n",
 			(bootstatus != 0) ? "Watchdog" : "Power-On-Reset");
@@ -123,41 +141,35 @@
 		exit(EXIT_FAILURE);
 	}
 
-	/* There are two ways to kick the watchdog:
-	   - by writing any dummy value into watchdog device file, or
-	   - by using IOCTL WDIOC_KEEPALIVE
+	/* There are two ways to pet the watchdog:
+	 * 1) by writing any dummy value into watchdog device file, or
+	 * 2) by using IOCTL WDIOC_KEEPALIVE
+	 *
+	 * Use the first method since it's easier. :)
 	 */
-	fprintf(stdout,
-		"Use:\n"
-		" <w> to kick through writing over device file\n"
-		" <i> to kick through IOCTL\n"
-		" <x> to exit the program\n");
 	do {
-		kick_watchdog = getchar();
-		switch (kick_watchdog) {
-		case 'w':
-			write(fd, "w", 1);
-			fprintf(stdout,
-				"Kick watchdog through writing over device file\n");
-			break;
-		case 'i':
-			ioctl(fd, WDIOC_KEEPALIVE, NULL);
-			fprintf(stdout, "Kick watchdog through IOCTL\n");
-			break;
-		case 'x':
-			fprintf(stdout, "Goodbye !\n");
-			break;
-		default:
-			fprintf(stdout, "Unknown command\n");
-			break;
-		}
-	} while (kick_watchdog != 'x');
+		write(fd, "w", 1);
 
-	/* The 'V' value needs to be written into watchdog device file to indicate
-	   that we intend to close/stop the watchdog. Otherwise, debug message
-	   'Watchdog timer closed unexpectedly' will be printed
+		/* force immediate exit of loop if write fails. */
+		if (errno)
+			slept_well = -errno;
+		else
+			slept_well = sleep(pet_interval);
+
+	} while (slept_well == 0);
+
+	fprintf(stdout, "Exiting (%d)\n", slept_well);
+
+	/* Writing 'V' into watchdog device indicates the close/stop
+	 * of the watchdog was intentional. Otherwise, debug message
+	 * 'Watchdog timer closed unexpectedly' will be printed to
+	 * dmesg and the system will reboot in wd_timeout seconds since
+	 * the last time the watchdog was pet.
 	 */
 	write(fd, "V", 1);
+
 	/* Closing the watchdog device will deactivate the watchdog. */
 	close(fd);
+
+	exit(slept_well);
 }