|
@@ -44,10 +44,10 @@
|
|
|
|
|
|
#include "watchdog_core.h"
|
|
|
|
|
|
-/* make sure we only register one /dev/watchdog device */
|
|
|
-static unsigned long watchdog_dev_busy;
|
|
|
+/* the dev_t structure to store the dynamically allocated watchdog devices */
|
|
|
+static dev_t watchdog_devt;
|
|
|
/* the watchdog device behind /dev/watchdog */
|
|
|
-static struct watchdog_device *wdd;
|
|
|
+static struct watchdog_device *old_wdd;
|
|
|
|
|
|
/*
|
|
|
* watchdog_ping: ping the watchdog.
|
|
@@ -138,6 +138,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
|
|
|
static ssize_t watchdog_write(struct file *file, const char __user *data,
|
|
|
size_t len, loff_t *ppos)
|
|
|
{
|
|
|
+ struct watchdog_device *wdd = file->private_data;
|
|
|
size_t i;
|
|
|
char c;
|
|
|
|
|
@@ -177,6 +178,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data,
|
|
|
static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
|
|
unsigned long arg)
|
|
|
{
|
|
|
+ struct watchdog_device *wdd = file->private_data;
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
int __user *p = argp;
|
|
|
unsigned int val;
|
|
@@ -249,11 +251,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * watchdog_open: open the /dev/watchdog device.
|
|
|
+ * watchdog_open: open the /dev/watchdog* devices.
|
|
|
* @inode: inode of device
|
|
|
* @file: file handle to device
|
|
|
*
|
|
|
- * When the /dev/watchdog device gets opened, we start the watchdog.
|
|
|
+ * When the /dev/watchdog* device gets opened, we start the watchdog.
|
|
|
* Watch out: the /dev/watchdog device is single open, so we make sure
|
|
|
* it can only be opened once.
|
|
|
*/
|
|
@@ -261,6 +263,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
|
|
static int watchdog_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
int err = -EBUSY;
|
|
|
+ struct watchdog_device *wdd;
|
|
|
+
|
|
|
+ /* Get the corresponding watchdog device */
|
|
|
+ if (imajor(inode) == MISC_MAJOR)
|
|
|
+ wdd = old_wdd;
|
|
|
+ else
|
|
|
+ wdd = container_of(inode->i_cdev, struct watchdog_device, cdev);
|
|
|
|
|
|
/* the watchdog is single open! */
|
|
|
if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
|
|
@@ -277,6 +286,8 @@ static int watchdog_open(struct inode *inode, struct file *file)
|
|
|
if (err < 0)
|
|
|
goto out_mod;
|
|
|
|
|
|
+ file->private_data = wdd;
|
|
|
+
|
|
|
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
|
|
|
return nonseekable_open(inode, file);
|
|
|
|
|
@@ -288,9 +299,9 @@ out:
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * watchdog_release: release the /dev/watchdog device.
|
|
|
- * @inode: inode of device
|
|
|
- * @file: file handle to device
|
|
|
+ * watchdog_release: release the watchdog device.
|
|
|
+ * @inode: inode of device
|
|
|
+ * @file: file handle to device
|
|
|
*
|
|
|
* This is the code for when /dev/watchdog gets closed. We will only
|
|
|
* stop the watchdog when we have received the magic char (and nowayout
|
|
@@ -299,6 +310,7 @@ out:
|
|
|
|
|
|
static int watchdog_release(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
+ struct watchdog_device *wdd = file->private_data;
|
|
|
int err = -EBUSY;
|
|
|
|
|
|
/*
|
|
@@ -340,62 +352,87 @@ static struct miscdevice watchdog_miscdev = {
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
- * watchdog_dev_register:
|
|
|
+ * watchdog_dev_register: register a watchdog device
|
|
|
* @watchdog: watchdog device
|
|
|
*
|
|
|
- * Register a watchdog device as /dev/watchdog. /dev/watchdog
|
|
|
- * is actually a miscdevice and thus we set it up like that.
|
|
|
+ * Register a watchdog device including handling the legacy
|
|
|
+ * /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
|
|
+ * thus we set it up like that.
|
|
|
*/
|
|
|
|
|
|
int watchdog_dev_register(struct watchdog_device *watchdog)
|
|
|
{
|
|
|
- int err;
|
|
|
-
|
|
|
- /* Only one device can register for /dev/watchdog */
|
|
|
- if (test_and_set_bit(0, &watchdog_dev_busy)) {
|
|
|
- pr_err("only one watchdog can use /dev/watchdog\n");
|
|
|
- return -EBUSY;
|
|
|
+ int err, devno;
|
|
|
+
|
|
|
+ if (watchdog->id == 0) {
|
|
|
+ err = misc_register(&watchdog_miscdev);
|
|
|
+ if (err != 0) {
|
|
|
+ pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
|
|
|
+ watchdog->info->identity, WATCHDOG_MINOR, err);
|
|
|
+ if (err == -EBUSY)
|
|
|
+ pr_err("%s: a legacy watchdog module is probably present.\n",
|
|
|
+ watchdog->info->identity);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ old_wdd = watchdog;
|
|
|
}
|
|
|
|
|
|
- wdd = watchdog;
|
|
|
-
|
|
|
- err = misc_register(&watchdog_miscdev);
|
|
|
- if (err != 0) {
|
|
|
- pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n",
|
|
|
- watchdog->info->identity, WATCHDOG_MINOR, err);
|
|
|
- goto out;
|
|
|
+ /* Fill in the data structures */
|
|
|
+ devno = MKDEV(MAJOR(watchdog_devt), watchdog->id);
|
|
|
+ cdev_init(&watchdog->cdev, &watchdog_fops);
|
|
|
+ watchdog->cdev.owner = watchdog->ops->owner;
|
|
|
+
|
|
|
+ /* Add the device */
|
|
|
+ err = cdev_add(&watchdog->cdev, devno, 1);
|
|
|
+ if (err) {
|
|
|
+ pr_err("watchdog%d unable to add device %d:%d\n",
|
|
|
+ watchdog->id, MAJOR(watchdog_devt), watchdog->id);
|
|
|
+ if (watchdog->id == 0) {
|
|
|
+ misc_deregister(&watchdog_miscdev);
|
|
|
+ old_wdd = NULL;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-out:
|
|
|
- wdd = NULL;
|
|
|
- clear_bit(0, &watchdog_dev_busy);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * watchdog_dev_unregister:
|
|
|
+ * watchdog_dev_unregister: unregister a watchdog device
|
|
|
* @watchdog: watchdog device
|
|
|
*
|
|
|
- * Deregister the /dev/watchdog device.
|
|
|
+ * Unregister the watchdog and if needed the legacy /dev/watchdog device.
|
|
|
*/
|
|
|
|
|
|
int watchdog_dev_unregister(struct watchdog_device *watchdog)
|
|
|
{
|
|
|
- /* Check that a watchdog device was registered in the past */
|
|
|
- if (!test_bit(0, &watchdog_dev_busy) || !wdd)
|
|
|
- return -ENODEV;
|
|
|
-
|
|
|
- /* We can only unregister the watchdog device that was registered */
|
|
|
- if (watchdog != wdd) {
|
|
|
- pr_err("%s: watchdog was not registered as /dev/watchdog\n",
|
|
|
- watchdog->info->identity);
|
|
|
- return -ENODEV;
|
|
|
+ cdev_del(&watchdog->cdev);
|
|
|
+ if (watchdog->id == 0) {
|
|
|
+ misc_deregister(&watchdog_miscdev);
|
|
|
+ old_wdd = NULL;
|
|
|
}
|
|
|
-
|
|
|
- misc_deregister(&watchdog_miscdev);
|
|
|
- wdd = NULL;
|
|
|
- clear_bit(0, &watchdog_dev_busy);
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+ * watchdog_dev_init: init dev part of watchdog core
|
|
|
+ *
|
|
|
+ * Allocate a range of chardev nodes to use for watchdog devices
|
|
|
+ */
|
|
|
+
|
|
|
+int __init watchdog_dev_init(void)
|
|
|
+{
|
|
|
+ int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
|
|
|
+ if (err < 0)
|
|
|
+ pr_err("watchdog: unable to allocate char dev region\n");
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * watchdog_dev_exit: exit dev part of watchdog core
|
|
|
+ *
|
|
|
+ * Release the range of chardev nodes used for watchdog devices
|
|
|
+ */
|
|
|
+
|
|
|
+void __exit watchdog_dev_exit(void)
|
|
|
+{
|
|
|
+ unregister_chrdev_region(watchdog_devt, MAX_DOGS);
|
|
|
+}
|