static ssize_t wdog_write(struct file *file, const char __user *buf,
						size_t count, loff_t *ppos)
{
	if (count) {
		if (!nowayout) {
			size_t i;

			/* In case it was set long ago */
			expect_close = 0;

			for (i = 0; i != count; i++) {
				char c;
				if (get_user(c, buf + i))
					return -EFAULT;
				if (c == 'V')
					expect_close = 42;
			}
		}
		wdog_ping();
	}
	return count;
}
Example #2
0
static int testit(void)
{
	int id;
	unsigned int ack;

	log("Verifying watchdog connectivity");
	if (wdog_ping())
		errx(1, "Failed connectivity check");

	log("Subscribing to process supervisor");
	id = wdog_subscribe(NULL, tmo, &ack);
	if (id < 0) {
		perror("Failed connecting to wdog");
		return 1;
	}

	if (false_ack)
		ack += 42;
	if (false_unsubscribe) {
		ack += 42;
		count = 0;
	}
	if (disable_enable)
		count += 10;
	if (no_kick) {
		count = 0;
		usleep(tmo * 1000 * 5);
	}

	log("Starting test loop:\n"
	    "\tcount             : %d\n"
	    "\tfalse ack         : %d\n"
	    "\tfalse unsubscribe : %d\n"
	    "\tdisable enable    : %d\n"
	    "\tno kick           : %d\n"
	    "\tpremature trigger : %d\n", count, false_ack, false_unsubscribe,
	    disable_enable, no_kick, premature);

	while (count-- > 0) {
		log("Sleeping %d msec", tmo / 2);
		usleep(tmo / 2 * 1000);

		log("Kicking watchdog: id %d, ack %d", id, ack);
		if (wdog_kick2(id, &ack))
			errx(1, "Failed kicking");

		if (count == 8)
			wdog_enable(0);
		if (count == 4)
			wdog_enable(1);
		if (failed_kick)
			ack += 42;
		if (premature)
			/* => 2000 / 2 * 1000 - 500000 = 500 ms */
			usleep(tmo / 2 * 1000 - 500000);
	}

	log("Unsubscribing: id %d, ack %d", id, ack);
	if (wdog_unsubscribe(id, ack))
		errx(1, "Failed unsubscribe");

	return 0;
}
static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *argp = (void __user *)arg;
	int __user *p = argp;
	int new_heartbeat;
	int status;
	int options;
	uint32_t remaining;

	struct watchdog_info ident = {
		.options =		WDIOF_SETTIMEOUT|
					WDIOF_MAGICCLOSE|
					WDIOF_KEEPALIVEPING,
		.firmware_version =	1,
		.identity =		"BCM2708",
	};

	switch (cmd) {
	case WDIOC_GETSUPPORT:
		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
	case WDIOC_GETSTATUS:
		status = wdog_get_status();
		return put_user(status, p);
	case WDIOC_GETBOOTSTATUS:
		return put_user(0, p);
	case WDIOC_KEEPALIVE:
		wdog_ping();
		return 0;
	case WDIOC_SETTIMEOUT:
		if (get_user(new_heartbeat, p))
			return -EFAULT;
		if (wdog_set_heartbeat(new_heartbeat))
			return -EINVAL;
		wdog_ping();
		/* Fall */
	case WDIOC_GETTIMEOUT:
		return put_user(heartbeat, p);
	case WDIOC_GETTIMELEFT:
		remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining());
		return put_user(remaining, p);
	case WDIOC_SETOPTIONS:
		if (get_user(options, p))
			return -EFAULT;
		if (options & WDIOS_DISABLECARD)
			wdog_stop();
		if (options & WDIOS_ENABLECARD)
			wdog_start(wdog_ticks);
		return 0;
	default:
		return -ENOTTY;
	}
}

/**
 *	@inode: inode of device
 *	@file: file handle to device
 *
 *	The watchdog device has been opened. The watchdog device is single
 *	open and on opening we load the counters.
 */

static int wdog_open(struct inode *inode, struct file *file)
{
	if (test_and_set_bit(0, &wdog_is_open))
		return -EBUSY;
	/*
	 *	Activate
	 */
	wdog_start(wdog_ticks);
	return nonseekable_open(inode, file);
}

/**
 *	@inode: inode to board
 *	@file: file handle to board
 *
 *	The watchdog has a configurable API. There is a religious dispute
 *	between people who want their watchdog to be able to shut down and
 *	those who want to be sure if the watchdog manager dies the machine
 *	reboots. In the former case we disable the counters, in the latter
 *	case you have to open it again very soon.
 */

static int wdog_release(struct inode *inode, struct file *file)
{
	if (expect_close == 42) {
		wdog_stop();
	} else {
		printk(KERN_CRIT
		 "wdt: WDT device closed unexpectedly.  WDT will not stop!\n");
		wdog_ping();
	}
	clear_bit(0, &wdog_is_open);
	expect_close = 0;
	return 0;
}

/**
 *	@this: our notifier block
 *	@code: the event being reported
 *	@unused: unused
 *
 *	Our notifier is called on system shutdowns. Turn the watchdog
 *	off so that it does not fire during the next reboot.
 */

static int wdog_notify_sys(struct notifier_block *this, unsigned long code,
	void *unused)
{
	if (code == SYS_DOWN || code == SYS_HALT)
		wdog_stop();
	return NOTIFY_DONE;
}

/*
 *	Kernel Interfaces
 */


static const struct file_operations wdog_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.write		= wdog_write,
	.unlocked_ioctl	= wdog_ioctl,
	.open		= wdog_open,
	.release	= wdog_release,
};

static struct miscdevice wdog_miscdev = {
	.minor	= WATCHDOG_MINOR,
	.name	= "watchdog",
	.fops	= &wdog_fops,
};

/*
 *	The WDT card needs to learn about soft shutdowns in order to
 *	turn the timebomb registers off.
 */

static struct notifier_block wdog_notifier = {
	.notifier_call = wdog_notify_sys,
};

/**
 *	cleanup_module:
 *
 *	Unload the watchdog. You cannot do this with any file handles open.
 *	If your watchdog is set to continue ticking on close and you unload
 *	it, well it keeps ticking. We won't get the interrupt but the board
 *	will not touch PC memory so all is fine. You just have to load a new
 *	module in 60 seconds or reboot.
 */

static void __exit wdog_exit(void)
{
	misc_deregister(&wdog_miscdev);
	unregister_reboot_notifier(&wdog_notifier);
}

static int __init wdog_init(void)
{
	int ret;

	/* Check that the heartbeat value is within it's range;
	   if not reset to the default */
	if (wdog_set_heartbeat(heartbeat)) {
		wdog_set_heartbeat(WD_TIMO);
		printk(KERN_INFO "bcm2708_wdog: heartbeat value must be "
			"0 < heartbeat < %d, using %d\n",
				WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
				WD_TIMO);
	}

	ret = register_reboot_notifier(&wdog_notifier);
	if (ret) {
		printk(KERN_ERR
		      "wdt: cannot register reboot notifier (err=%d)\n", ret);
		goto out_reboot;
	}

	ret = misc_register(&wdog_miscdev);
	if (ret) {
		printk(KERN_ERR
			"wdt: cannot register miscdev on minor=%d (err=%d)\n",
							WATCHDOG_MINOR, ret);
		goto out_misc;
	}

	printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n",
		heartbeat, nowayout);
	return 0;

out_misc:
	unregister_reboot_notifier(&wdog_notifier);
out_reboot:
	return ret;
}