فهرست منبع

修复客户反馈的watchdog问题

qidong.liu 1 ماه پیش
والد
کامیت
f415bc8b91
1فایلهای تغییر یافته به همراه303 افزوده شده و 75 حذف شده
  1. 303 75
      watchdog.c

+ 303 - 75
watchdog.c

@@ -1,22 +1,3 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- *	Watchdog Timer Driver
- *	   for ITE IT87xx Environment Control - Low Pin Count Input / Output
- *
- *	(c) Copyright 2007  Oliver Schuster <olivers137@aol.com>
- *
- *	Based on softdog.c	by Alan Cox,
- *		 83977f_wdt.c	by Jose Goncalves,
- *		 it87.c		by Chris Gauthron, Jean Delvare
- *
- *	Data-sheets: Publicly available at the ITE website
- *		    http://www.ite.com.tw/
- *
- *	Support of the watchdog timers, which are available on
- *	IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
- *	IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
- *	IT8772, IT8783 and IT8784.
- */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
@@ -26,14 +7,19 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
-#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/timer.h>
 
 #define WATCHDOG_NAME		"IT87 WDT"
+#define DEVICE_NAME		"watchdog"
+#define CLASS_NAME		"watchdog_class"
 
 /* Defaults for Module Parameter */
 #define DEFAULT_TIMEOUT		60
 #define DEFAULT_TESTMODE	0
-#define DEFAULT_NOWAYOUT	WATCHDOG_NOWAYOUT
 
 /* IO Ports */
 #define REG		0x4e
@@ -88,7 +74,6 @@ static unsigned int max_units, chip_type;
 
 static unsigned int timeout = DEFAULT_TIMEOUT;
 static int testmode = DEFAULT_TESTMODE;
-static bool nowayout = DEFAULT_NOWAYOUT;
 
 module_param(timeout, int, 0);
 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
@@ -96,9 +81,20 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
 module_param(testmode, int, 0);
 MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
 		__MODULE_STRING(DEFAULT_TESTMODE));
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
-		__MODULE_STRING(WATCHDOG_NOWAYOUT));
+
+/* Character device variables */
+static int major_number;
+static struct class *watchdog_class = NULL;
+static struct device *watchdog_device = NULL;
+
+/* Watchdog state: 0 = OFF, 1 = ON */
+static int watchdog_state = 0;
+static DEFINE_MUTEX(watchdog_mutex);
+
+/* Software timer for servicing watchdog */
+static struct timer_list watchdog_timer;
+
+extern struct kobject *vfiec_kobj;
 
 /* Superio Chip */
 
@@ -198,74 +194,268 @@ static int wdt_round_time(int t)
 	return t;
 }
 
-/* watchdog timer handling */
+/* Software timer callback - service the watchdog */
+static void watchdog_timer_callback(struct timer_list *t)
+{
+	/* Re-trigger the hardware watchdog by writing timeout again */
+	wdt_update_timeout(timeout);
+	
+	/* Restart timer (periodic servicing, e.g., every half timeout) */
+	mod_timer(&watchdog_timer, jiffies + (timeout * HZ / 2));
+}
 
-static int wdt_start(struct watchdog_device *wdd)
+/* Enable watchdog - start hardware and software timer */
+static int watchdog_enable(void)
 {
-	return wdt_update_timeout(wdd->timeout);
+	int ret;
+	
+	pr_info("Enabling watchdog timer\n");
+	
+	ret = wdt_update_timeout(timeout);
+	if (ret) {
+		pr_err("Failed to set watchdog timeout\n");
+		return ret;
+	}
+	
+	/* Start periodic software timer to service watchdog */
+	timer_setup(&watchdog_timer, watchdog_timer_callback, 0);
+	mod_timer(&watchdog_timer, jiffies + (timeout * HZ / 2));
+	
+	return 0;
 }
 
-static int wdt_stop(struct watchdog_device *wdd)
+/* Disable watchdog - stop hardware and software timer */
+static int watchdog_disable(void)
 {
-	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
+	pr_info("Disabling watchdog timer\n");
+	
+	/* Stop and delete software timer */
+	del_timer_sync(&watchdog_timer);
+	
+	/* Disable hardware watchdog by setting timeout to 0 */
 	return wdt_update_timeout(0);
 }
 
-/**
- *	wdt_set_timeout - set a new timeout value with watchdog ioctl
- *	@t: timeout value in seconds
- *
- *	The hardware device has a 8 or 16 bit watchdog timer (depends on
- *	chip version) that can be configured to count seconds or minutes.
- *
- *	Used within WDIOC_SETTIMEOUT watchdog device ioctl.
- */
-
-static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+/* Parse user command: supports "ON", "OFF", "1", "0" */
+static int parse_watchdog_command(const char *buf, size_t count)
+{	
+	/* Skip whitespace */
+	while (count > 0 && (*buf == ' ' || *buf == '\t' || *buf == '\n' || *buf == '\r')) {
+		buf++;
+		count--;
+	}
+	
+	if (count == 0)
+		return -1;
+	
+	/* Check for numeric value */
+	if (count == 1 && (buf[0] == '0' || buf[0] == '1')) {
+		return buf[0] - '0';
+	}
+	
+	/* Check for "ON" or "OFF" (case insensitive) */
+	if (count >= 2) {
+		if ((buf[0] == 'O' || buf[0] == 'o') &&
+		    (buf[1] == 'N' || buf[1] == 'n')) {
+			return 1;
+		}
+		if (count >= 3 &&
+		    (buf[0] == 'O' || buf[0] == 'o') &&
+		    (buf[1] == 'F' || buf[1] == 'f') &&
+		    (buf[2] == 'F' || buf[2] == 'f')) {
+			return 0;
+		}
+	}
+	
+	return -1;
+}
+
+/* Character device file operations */
+static ssize_t watchdog_read(struct file *file, char __user *user_buf,
+			      size_t count, loff_t *ppos)
+{	
+	char status_buf[64];
+	int len;
+	
+	if (*ppos > 0)
+		return 0;
+	
+	mutex_lock(&watchdog_mutex);
+	if (watchdog_state)
+		len = snprintf(status_buf, sizeof(status_buf),
+			       "The watchdog is on\nThe args value is 1\n");
+	else
+		len = snprintf(status_buf, sizeof(status_buf),
+			       "The watchdog is off\nThe args value is 0\n");
+	mutex_unlock(&watchdog_mutex);
+	
+	if (copy_to_user(user_buf, status_buf, len))
+		return -EFAULT;
+	
+	*ppos += len;
+	return len;
+}
+
+static ssize_t watchdog_write(struct file *file, const char __user *user_buf,
+			       size_t count, loff_t *ppos)
 {
-	int ret = 0;
+	char buf[16];
+	int cmd;
+	int ret = count;
+	
+	if (count == 0 || count >= sizeof(buf))
+		return -EINVAL;
+	
+	if (copy_from_user(buf, user_buf, count))
+		return -EFAULT;
+	
+	buf[count] = '\0';
+	
+	cmd = parse_watchdog_command(buf, count);
+	if (cmd < 0)
+		return -EINVAL;
+	
+	mutex_lock(&watchdog_mutex);
+	
+	if (cmd == watchdog_state) {
+		/* No state change */
+		if (cmd)
+			pr_info("Watchdog is already on\n");
+		else
+			pr_info("Watchdog is already off\n");
+	} else {
+		if (cmd) {
+			/* Turn ON */
+			if (watchdog_enable() == 0) {
+				watchdog_state = 1;
+				pr_info("Watchdog turned on successfully\n");
+			} else {
+				ret = -EIO;
+			}
+		} else {
+			/* Turn OFF */
+			if (watchdog_disable() == 0) {
+				watchdog_state = 0;
+				pr_info("Watchdog turned off successfully\n");
+			} else {
+				ret = -EIO;
+			}
+		}
+	}
+	
+	mutex_unlock(&watchdog_mutex);
+	return ret;
+}
 
-	if (t > max_units)
-		t = wdt_round_time(t);
+static int watchdog_open(struct inode *inode, struct file *file)
+{	
+	return 0;
+}
 
-	wdd->timeout = t;
+static int watchdog_release(struct inode *inode, struct file *file)
+{	
+	return 0;
+}
 
-	if (watchdog_hw_running(wdd))
-		ret = wdt_update_timeout(t);
+static const struct file_operations watchdog_fops = {
+	.owner = THIS_MODULE,
+	.read = watchdog_read,
+	.write = watchdog_write,
+	.open = watchdog_open,
+	.release = watchdog_release,
+};
+static char *my_devnode(struct device *dev, umode_t *mode) {
+    if (mode) {
+        *mode = 0666;
+    }
+    return NULL;
+}
 
+static ssize_t watchdog_show(struct kobject *kobj, struct kobj_attribute *attr,
+                         char *buf)
+{
+    return 0;
+}
+
+static ssize_t watchdog_store(struct kobject *kobj, struct kobj_attribute *attr,
+                          const char *buf, size_t count)
+{
+	int cmd;
+	int ret = count;
+	
+	// if (count == 0 || count >= sizeof(buf))
+	// 	return -EINVAL;
+	
+	// if (strscpy(buf, user_buf, count) < 0)
+	// 	return -EFAULT;
+	
+	// buf[count] = '\0';
+	
+	cmd = parse_watchdog_command(buf, count);
+	if (cmd < 0)
+		return -EINVAL;
+	
+	mutex_lock(&watchdog_mutex);
+	
+	if (cmd == watchdog_state) {
+		/* No state change */
+		if (cmd)
+			pr_info("Watchdog is already on\n");
+		else
+			pr_info("Watchdog is already off\n");
+	} else {
+		if (cmd) {
+			/* Turn ON */
+			if (watchdog_enable() == 0) {
+				watchdog_state = 1;
+				pr_info("Watchdog turned on successfully\n");
+			} else {
+				ret = -EIO;
+			}
+		} else {
+			/* Turn OFF */
+			if (watchdog_disable() == 0) {
+				watchdog_state = 0;
+				pr_info("Watchdog turned off successfully\n");
+			} else {
+				ret = -EIO;
+			}
+		}
+	}
+	
+	mutex_unlock(&watchdog_mutex);
 	return ret;
 }
 
-static const struct watchdog_info ident = {
-	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
-	.firmware_version = 1,
-	.identity = WATCHDOG_NAME,
-};
+static struct kobj_attribute watchdog_attr =
+    __ATTR(watchdog, 0644, watchdog_show, watchdog_store);
 
-static const struct watchdog_ops wdt_ops = {
-	.owner = THIS_MODULE,
-	.start = wdt_start,
-	.stop = wdt_stop,
-	.set_timeout = wdt_set_timeout,
+static struct attribute *watchdog_attrs[] = {
+    &watchdog_attr.attr,
+    NULL,
 };
 
-static struct watchdog_device wdt_dev = {
-	.info = &ident,
-	.ops = &wdt_ops,
-	.min_timeout = 1,
+static struct attribute_group watchdog_attr_group = {
+    .attrs = watchdog_attrs,
 };
 
 int watchdog_init(void)
-{
+{	
 	u8  chip_rev;
 	int rc;
 
+	rc = sysfs_create_group(vfiec_kobj, &watchdog_attr_group);
+    if (rc)
+    {
+        pr_err("Faiec_version to create sysfs group: %d\n", rc);
+		return -1;
+    }
+
 	rc = superio_enter();
 	if (rc)
 		return rc;
 
 	chip_type = superio_inw(CHIPID);
-    // chip_type = 0x8786;
 	chip_rev  = superio_inb(CHIPREV) & 0x0f;
 	superio_exit();
 
@@ -329,25 +519,63 @@ int watchdog_init(void)
 	if (timeout > max_units)
 		timeout = wdt_round_time(timeout);
 
-	wdt_dev.timeout = timeout;
-	wdt_dev.max_timeout = max_units * 60;
+	/* Register character device */
+	major_number = register_chrdev(0, DEVICE_NAME, &watchdog_fops);
+	if (major_number < 0) {
+		pr_err("Failed to register character device\n");
+		return major_number;
+	}
 
-	watchdog_stop_on_reboot(&wdt_dev);
-	rc = watchdog_register_device(&wdt_dev);
-	if (rc) {
-		pr_err("Cannot register watchdog device (err=%d)\n", rc);
-		return rc;
+	/* Create device class */
+	watchdog_class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(watchdog_class)) {
+		unregister_chrdev(major_number, DEVICE_NAME);
+		pr_err("Failed to create device class\n");
+		return PTR_ERR(watchdog_class);
 	}
 
-	pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
-		chip_type, chip_rev, timeout, nowayout, testmode);
+	watchdog_class->devnode = my_devnode;
+
+	/* Create device node /dev/watchdog */
+	watchdog_device = device_create(watchdog_class, NULL,
+					MKDEV(major_number, 0),
+					NULL, DEVICE_NAME);
+	if (IS_ERR(watchdog_device)) {
+		class_destroy(watchdog_class);
+		unregister_chrdev(major_number, DEVICE_NAME);
+		pr_err("Failed to create device node\n");
+		return PTR_ERR(watchdog_device);
+	}
+
+	/* Initialize state */
+	watchdog_state = 0;
+	
+	/* Initialize timer */
+	timer_setup(&watchdog_timer, watchdog_timer_callback, 0);
+
+	pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (testmode=%d)\n",
+		chip_type, chip_rev, timeout, testmode);
+	pr_info("Device /dev/watchdog created. Default state: OFF\n");
 
 	return 0;
 }
 
 void watchdog_exit(void)
-{
-	watchdog_unregister_device(&wdt_dev);
+{	
+	/* Ensure watchdog is disabled before exit */
+	if (watchdog_state) {
+		watchdog_disable();
+		watchdog_state = 0;
+	}
+	
+	/* Delete timer */
+	del_timer_sync(&watchdog_timer);
+	
+	/* Remove device */
+	device_destroy(watchdog_class, MKDEV(major_number, 0));
+	class_destroy(watchdog_class);
+	unregister_chrdev(major_number, DEVICE_NAME);
+	
+	pr_info("Watchdog driver exited\n");
 }
 
-