diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 43 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.h | 4 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 127 |
3 files changed, 125 insertions, 49 deletions
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 8598308278d3..5f9879369003 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -34,9 +34,12 @@ | |||
34 | #include <linux/kernel.h> /* For printk/panic/... */ | 34 | #include <linux/kernel.h> /* For printk/panic/... */ |
35 | #include <linux/watchdog.h> /* For watchdog specific items */ | 35 | #include <linux/watchdog.h> /* For watchdog specific items */ |
36 | #include <linux/init.h> /* For __init/__exit/... */ | 36 | #include <linux/init.h> /* For __init/__exit/... */ |
37 | #include <linux/idr.h> /* For ida_* macros */ | ||
37 | 38 | ||
38 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ | 39 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ |
39 | 40 | ||
41 | static DEFINE_IDA(watchdog_ida); | ||
42 | |||
40 | /** | 43 | /** |
41 | * watchdog_register_device() - register a watchdog device | 44 | * watchdog_register_device() - register a watchdog device |
42 | * @wdd: watchdog device | 45 | * @wdd: watchdog device |
@@ -49,7 +52,7 @@ | |||
49 | */ | 52 | */ |
50 | int watchdog_register_device(struct watchdog_device *wdd) | 53 | int watchdog_register_device(struct watchdog_device *wdd) |
51 | { | 54 | { |
52 | int ret; | 55 | int ret, id; |
53 | 56 | ||
54 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) | 57 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) |
55 | return -EINVAL; | 58 | return -EINVAL; |
@@ -74,11 +77,28 @@ int watchdog_register_device(struct watchdog_device *wdd) | |||
74 | * corrupted in a later stage then we expect a kernel panic! | 77 | * corrupted in a later stage then we expect a kernel panic! |
75 | */ | 78 | */ |
76 | 79 | ||
77 | /* We only support 1 watchdog device via the /dev/watchdog interface */ | 80 | id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); |
81 | if (id < 0) | ||
82 | return id; | ||
83 | wdd->id = id; | ||
84 | |||
78 | ret = watchdog_dev_register(wdd); | 85 | ret = watchdog_dev_register(wdd); |
79 | if (ret) { | 86 | if (ret) { |
80 | pr_err("error registering /dev/watchdog (err=%d)\n", ret); | 87 | ida_simple_remove(&watchdog_ida, id); |
81 | return ret; | 88 | if (!(id == 0 && ret == -EBUSY)) |
89 | return ret; | ||
90 | |||
91 | /* Retry in case a legacy watchdog module exists */ | ||
92 | id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL); | ||
93 | if (id < 0) | ||
94 | return id; | ||
95 | wdd->id = id; | ||
96 | |||
97 | ret = watchdog_dev_register(wdd); | ||
98 | if (ret) { | ||
99 | ida_simple_remove(&watchdog_ida, id); | ||
100 | return ret; | ||
101 | } | ||
82 | } | 102 | } |
83 | 103 | ||
84 | return 0; | 104 | return 0; |
@@ -102,9 +122,24 @@ void watchdog_unregister_device(struct watchdog_device *wdd) | |||
102 | ret = watchdog_dev_unregister(wdd); | 122 | ret = watchdog_dev_unregister(wdd); |
103 | if (ret) | 123 | if (ret) |
104 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); | 124 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); |
125 | ida_simple_remove(&watchdog_ida, wdd->id); | ||
105 | } | 126 | } |
106 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | 127 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); |
107 | 128 | ||
129 | static int __init watchdog_init(void) | ||
130 | { | ||
131 | return watchdog_dev_init(); | ||
132 | } | ||
133 | |||
134 | static void __exit watchdog_exit(void) | ||
135 | { | ||
136 | watchdog_dev_exit(); | ||
137 | ida_destroy(&watchdog_ida); | ||
138 | } | ||
139 | |||
140 | subsys_initcall(watchdog_init); | ||
141 | module_exit(watchdog_exit); | ||
142 | |||
108 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | 143 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); |
109 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); | 144 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); |
110 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); | 145 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); |
diff --git a/drivers/watchdog/watchdog_core.h b/drivers/watchdog/watchdog_core.h index 80503f229385..6c951418fca7 100644 --- a/drivers/watchdog/watchdog_core.h +++ b/drivers/watchdog/watchdog_core.h | |||
@@ -26,8 +26,12 @@ | |||
26 | * This material is provided "AS-IS" and at no charge. | 26 | * This material is provided "AS-IS" and at no charge. |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #define MAX_DOGS 32 /* Maximum number of watchdog devices */ | ||
30 | |||
29 | /* | 31 | /* |
30 | * Functions/procedures to be called by the core | 32 | * Functions/procedures to be called by the core |
31 | */ | 33 | */ |
32 | extern int watchdog_dev_register(struct watchdog_device *); | 34 | extern int watchdog_dev_register(struct watchdog_device *); |
33 | extern int watchdog_dev_unregister(struct watchdog_device *); | 35 | extern int watchdog_dev_unregister(struct watchdog_device *); |
36 | extern int __init watchdog_dev_init(void); | ||
37 | extern void __exit watchdog_dev_exit(void); | ||
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index beaf9cb5541a..3b22582bcb04 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c | |||
@@ -44,10 +44,10 @@ | |||
44 | 44 | ||
45 | #include "watchdog_core.h" | 45 | #include "watchdog_core.h" |
46 | 46 | ||
47 | /* make sure we only register one /dev/watchdog device */ | 47 | /* the dev_t structure to store the dynamically allocated watchdog devices */ |
48 | static unsigned long watchdog_dev_busy; | 48 | static dev_t watchdog_devt; |
49 | /* the watchdog device behind /dev/watchdog */ | 49 | /* the watchdog device behind /dev/watchdog */ |
50 | static struct watchdog_device *wdd; | 50 | static struct watchdog_device *old_wdd; |
51 | 51 | ||
52 | /* | 52 | /* |
53 | * watchdog_ping: ping the watchdog. | 53 | * watchdog_ping: ping the watchdog. |
@@ -138,6 +138,7 @@ static int watchdog_stop(struct watchdog_device *wddev) | |||
138 | static ssize_t watchdog_write(struct file *file, const char __user *data, | 138 | static ssize_t watchdog_write(struct file *file, const char __user *data, |
139 | size_t len, loff_t *ppos) | 139 | size_t len, loff_t *ppos) |
140 | { | 140 | { |
141 | struct watchdog_device *wdd = file->private_data; | ||
141 | size_t i; | 142 | size_t i; |
142 | char c; | 143 | char c; |
143 | 144 | ||
@@ -177,6 +178,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, | |||
177 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | 178 | static long watchdog_ioctl(struct file *file, unsigned int cmd, |
178 | unsigned long arg) | 179 | unsigned long arg) |
179 | { | 180 | { |
181 | struct watchdog_device *wdd = file->private_data; | ||
180 | void __user *argp = (void __user *)arg; | 182 | void __user *argp = (void __user *)arg; |
181 | int __user *p = argp; | 183 | int __user *p = argp; |
182 | unsigned int val; | 184 | unsigned int val; |
@@ -249,11 +251,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
249 | } | 251 | } |
250 | 252 | ||
251 | /* | 253 | /* |
252 | * watchdog_open: open the /dev/watchdog device. | 254 | * watchdog_open: open the /dev/watchdog* devices. |
253 | * @inode: inode of device | 255 | * @inode: inode of device |
254 | * @file: file handle to device | 256 | * @file: file handle to device |
255 | * | 257 | * |
256 | * When the /dev/watchdog device gets opened, we start the watchdog. | 258 | * When the /dev/watchdog* device gets opened, we start the watchdog. |
257 | * Watch out: the /dev/watchdog device is single open, so we make sure | 259 | * Watch out: the /dev/watchdog device is single open, so we make sure |
258 | * it can only be opened once. | 260 | * it can only be opened once. |
259 | */ | 261 | */ |
@@ -261,6 +263,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
261 | static int watchdog_open(struct inode *inode, struct file *file) | 263 | static int watchdog_open(struct inode *inode, struct file *file) |
262 | { | 264 | { |
263 | int err = -EBUSY; | 265 | int err = -EBUSY; |
266 | struct watchdog_device *wdd; | ||
267 | |||
268 | /* Get the corresponding watchdog device */ | ||
269 | if (imajor(inode) == MISC_MAJOR) | ||
270 | wdd = old_wdd; | ||
271 | else | ||
272 | wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); | ||
264 | 273 | ||
265 | /* the watchdog is single open! */ | 274 | /* the watchdog is single open! */ |
266 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) | 275 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) |
@@ -277,6 +286,8 @@ static int watchdog_open(struct inode *inode, struct file *file) | |||
277 | if (err < 0) | 286 | if (err < 0) |
278 | goto out_mod; | 287 | goto out_mod; |
279 | 288 | ||
289 | file->private_data = wdd; | ||
290 | |||
280 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | 291 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
281 | return nonseekable_open(inode, file); | 292 | return nonseekable_open(inode, file); |
282 | 293 | ||
@@ -288,9 +299,9 @@ out: | |||
288 | } | 299 | } |
289 | 300 | ||
290 | /* | 301 | /* |
291 | * watchdog_release: release the /dev/watchdog device. | 302 | * watchdog_release: release the watchdog device. |
292 | * @inode: inode of device | 303 | * @inode: inode of device |
293 | * @file: file handle to device | 304 | * @file: file handle to device |
294 | * | 305 | * |
295 | * This is the code for when /dev/watchdog gets closed. We will only | 306 | * This is the code for when /dev/watchdog gets closed. We will only |
296 | * stop the watchdog when we have received the magic char (and nowayout | 307 | * stop the watchdog when we have received the magic char (and nowayout |
@@ -299,6 +310,7 @@ out: | |||
299 | 310 | ||
300 | static int watchdog_release(struct inode *inode, struct file *file) | 311 | static int watchdog_release(struct inode *inode, struct file *file) |
301 | { | 312 | { |
313 | struct watchdog_device *wdd = file->private_data; | ||
302 | int err = -EBUSY; | 314 | int err = -EBUSY; |
303 | 315 | ||
304 | /* | 316 | /* |
@@ -340,62 +352,87 @@ static struct miscdevice watchdog_miscdev = { | |||
340 | }; | 352 | }; |
341 | 353 | ||
342 | /* | 354 | /* |
343 | * watchdog_dev_register: | 355 | * watchdog_dev_register: register a watchdog device |
344 | * @watchdog: watchdog device | 356 | * @watchdog: watchdog device |
345 | * | 357 | * |
346 | * Register a watchdog device as /dev/watchdog. /dev/watchdog | 358 | * Register a watchdog device including handling the legacy |
347 | * is actually a miscdevice and thus we set it up like that. | 359 | * /dev/watchdog node. /dev/watchdog is actually a miscdevice and |
360 | * thus we set it up like that. | ||
348 | */ | 361 | */ |
349 | 362 | ||
350 | int watchdog_dev_register(struct watchdog_device *watchdog) | 363 | int watchdog_dev_register(struct watchdog_device *watchdog) |
351 | { | 364 | { |
352 | int err; | 365 | int err, devno; |
353 | 366 | ||
354 | /* Only one device can register for /dev/watchdog */ | 367 | if (watchdog->id == 0) { |
355 | if (test_and_set_bit(0, &watchdog_dev_busy)) { | 368 | err = misc_register(&watchdog_miscdev); |
356 | pr_err("only one watchdog can use /dev/watchdog\n"); | 369 | if (err != 0) { |
357 | return -EBUSY; | 370 | pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", |
371 | watchdog->info->identity, WATCHDOG_MINOR, err); | ||
372 | if (err == -EBUSY) | ||
373 | pr_err("%s: a legacy watchdog module is probably present.\n", | ||
374 | watchdog->info->identity); | ||
375 | return err; | ||
376 | } | ||
377 | old_wdd = watchdog; | ||
358 | } | 378 | } |
359 | 379 | ||
360 | wdd = watchdog; | 380 | /* Fill in the data structures */ |
361 | 381 | devno = MKDEV(MAJOR(watchdog_devt), watchdog->id); | |
362 | err = misc_register(&watchdog_miscdev); | 382 | cdev_init(&watchdog->cdev, &watchdog_fops); |
363 | if (err != 0) { | 383 | watchdog->cdev.owner = watchdog->ops->owner; |
364 | pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n", | 384 | |
365 | watchdog->info->identity, WATCHDOG_MINOR, err); | 385 | /* Add the device */ |
366 | goto out; | 386 | err = cdev_add(&watchdog->cdev, devno, 1); |
387 | if (err) { | ||
388 | pr_err("watchdog%d unable to add device %d:%d\n", | ||
389 | watchdog->id, MAJOR(watchdog_devt), watchdog->id); | ||
390 | if (watchdog->id == 0) { | ||
391 | misc_deregister(&watchdog_miscdev); | ||
392 | old_wdd = NULL; | ||
393 | } | ||
367 | } | 394 | } |
368 | |||
369 | return 0; | ||
370 | |||
371 | out: | ||
372 | wdd = NULL; | ||
373 | clear_bit(0, &watchdog_dev_busy); | ||
374 | return err; | 395 | return err; |
375 | } | 396 | } |
376 | 397 | ||
377 | /* | 398 | /* |
378 | * watchdog_dev_unregister: | 399 | * watchdog_dev_unregister: unregister a watchdog device |
379 | * @watchdog: watchdog device | 400 | * @watchdog: watchdog device |
380 | * | 401 | * |
381 | * Deregister the /dev/watchdog device. | 402 | * Unregister the watchdog and if needed the legacy /dev/watchdog device. |
382 | */ | 403 | */ |
383 | 404 | ||
384 | int watchdog_dev_unregister(struct watchdog_device *watchdog) | 405 | int watchdog_dev_unregister(struct watchdog_device *watchdog) |
385 | { | 406 | { |
386 | /* Check that a watchdog device was registered in the past */ | 407 | cdev_del(&watchdog->cdev); |
387 | if (!test_bit(0, &watchdog_dev_busy) || !wdd) | 408 | if (watchdog->id == 0) { |
388 | return -ENODEV; | 409 | misc_deregister(&watchdog_miscdev); |
389 | 410 | old_wdd = NULL; | |
390 | /* We can only unregister the watchdog device that was registered */ | ||
391 | if (watchdog != wdd) { | ||
392 | pr_err("%s: watchdog was not registered as /dev/watchdog\n", | ||
393 | watchdog->info->identity); | ||
394 | return -ENODEV; | ||
395 | } | 411 | } |
396 | |||
397 | misc_deregister(&watchdog_miscdev); | ||
398 | wdd = NULL; | ||
399 | clear_bit(0, &watchdog_dev_busy); | ||
400 | return 0; | 412 | return 0; |
401 | } | 413 | } |
414 | |||
415 | /* | ||
416 | * watchdog_dev_init: init dev part of watchdog core | ||
417 | * | ||
418 | * Allocate a range of chardev nodes to use for watchdog devices | ||
419 | */ | ||
420 | |||
421 | int __init watchdog_dev_init(void) | ||
422 | { | ||
423 | int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); | ||
424 | if (err < 0) | ||
425 | pr_err("watchdog: unable to allocate char dev region\n"); | ||
426 | return err; | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * watchdog_dev_exit: exit dev part of watchdog core | ||
431 | * | ||
432 | * Release the range of chardev nodes used for watchdog devices | ||
433 | */ | ||
434 | |||
435 | void __exit watchdog_dev_exit(void) | ||
436 | { | ||
437 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); | ||
438 | } | ||