diff options
author | Alan Cox <alan@linux.intel.com> | 2012-05-10 15:48:59 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2012-05-30 01:54:25 -0400 |
commit | 45f5fed30a6460ec58f159ff297a2974153a97de (patch) | |
tree | d173b2d22ed1187fb7ed2cb85015ddf5c545da05 /drivers/watchdog/watchdog_dev.c | |
parent | fb5f6658163412dce22724e906e324ab7fd62c18 (diff) |
watchdog: Add multiple device support
We keep the old /dev/watchdog interface file for the first watchdog via
miscdev. This is basically a cut and paste of the relevant interface code
from the rtc driver layer tweaked for watchdog.
Revised to fix problems noted by Hans de Goede
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog/watchdog_dev.c')
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 127 |
1 files changed, 82 insertions, 45 deletions
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 | } | ||