linux/drivers/watchdog/mixcomwd.c
<<
>>
Prefs
   1/*
   2 * MixCom Watchdog: A Simple Hardware Watchdog Device
   3 * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
   4 *
   5 * Author: Gergely Madarasz <gorgo@itc.hu>
   6 *
   7 * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License
  11 * as published by the Free Software Foundation; either version
  12 * 2 of the License, or (at your option) any later version.
  13 *
  14 * Version 0.1 (99/04/15):
  15 *              - first version
  16 *
  17 * Version 0.2 (99/06/16):
  18 *              - added kernel timer watchdog ping after close
  19 *                since the hardware does not support watchdog shutdown
  20 *
  21 * Version 0.3 (99/06/21):
  22 *              - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
  23 *
  24 * Version 0.3.1 (99/06/22):
  25 *              - allow module removal while internal timer is active,
  26 *                print warning about probable reset
  27 *
  28 * Version 0.4 (99/11/15):
  29 *              - support for one more type board
  30 *
  31 * Version 0.5 (2001/12/14) Matt Domsch <Matt_Domsch@dell.com>
  32 *              - added nowayout module option to override
  33 *                CONFIG_WATCHDOG_NOWAYOUT
  34 *
  35 * Version 0.6 (2002/04/12): Rob Radez <rob@osinvestor.com>
  36 *              - make mixcomwd_opened unsigned,
  37 *                removed lock_kernel/unlock_kernel from mixcomwd_release,
  38 *                modified ioctl a bit to conform to API
  39 *
  40 */
  41
  42#define VERSION "0.6"
  43#define WATCHDOG_NAME "mixcomwd"
  44#define PFX WATCHDOG_NAME ": "
  45
  46#include <linux/module.h>
  47#include <linux/moduleparam.h>
  48#include <linux/types.h>
  49#include <linux/miscdevice.h>
  50#include <linux/ioport.h>
  51#include <linux/watchdog.h>
  52#include <linux/fs.h>
  53#include <linux/reboot.h>
  54#include <linux/init.h>
  55#include <linux/jiffies.h>
  56#include <linux/timer.h>
  57#include <linux/uaccess.h>
  58#include <linux/io.h>
  59
  60/*
  61 * We have two types of cards that can be probed:
  62 * 1) The Mixcom cards: these cards can be found at addresses
  63 *    0x180, 0x280, 0x380 with an additional offset of 0xc10.
  64 *    (Or 0xd90, 0xe90, 0xf90).
  65 * 2) The FlashCOM cards: these cards can be set up at
  66 *    0x300 -> 0x378, in 0x8 jumps with an offset of 0x04.
  67 *    (Or 0x304 -> 0x37c in 0x8 jumps).
  68 *    Each card has it's own ID.
  69 */
  70#define MIXCOM_ID 0x11
  71#define FLASHCOM_ID 0x18
  72static struct {
  73        int ioport;
  74        int id;
  75} mixcomwd_io_info[] __devinitdata = {
  76        /* The Mixcom cards */
  77        {0x0d90, MIXCOM_ID},
  78        {0x0e90, MIXCOM_ID},
  79        {0x0f90, MIXCOM_ID},
  80        /* The FlashCOM cards */
  81        {0x0304, FLASHCOM_ID},
  82        {0x030c, FLASHCOM_ID},
  83        {0x0314, FLASHCOM_ID},
  84        {0x031c, FLASHCOM_ID},
  85        {0x0324, FLASHCOM_ID},
  86        {0x032c, FLASHCOM_ID},
  87        {0x0334, FLASHCOM_ID},
  88        {0x033c, FLASHCOM_ID},
  89        {0x0344, FLASHCOM_ID},
  90        {0x034c, FLASHCOM_ID},
  91        {0x0354, FLASHCOM_ID},
  92        {0x035c, FLASHCOM_ID},
  93        {0x0364, FLASHCOM_ID},
  94        {0x036c, FLASHCOM_ID},
  95        {0x0374, FLASHCOM_ID},
  96        {0x037c, FLASHCOM_ID},
  97        /* The end of the list */
  98        {0x0000, 0},
  99};
 100
 101static void mixcomwd_timerfun(unsigned long d);
 102
 103static unsigned long mixcomwd_opened; /* long req'd for setbit --RR */
 104
 105static int watchdog_port;
 106static int mixcomwd_timer_alive;
 107static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun, 0, 0);
 108static char expect_close;
 109
 110static int nowayout = WATCHDOG_NOWAYOUT;
 111module_param(nowayout, int, 0);
 112MODULE_PARM_DESC(nowayout,
 113                "Watchdog cannot be stopped once started (default="
 114                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 115
 116static void mixcomwd_ping(void)
 117{
 118        outb_p(55, watchdog_port);
 119        return;
 120}
 121
 122static void mixcomwd_timerfun(unsigned long d)
 123{
 124        mixcomwd_ping();
 125        mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
 126}
 127
 128/*
 129 *      Allow only one person to hold it open
 130 */
 131
 132static int mixcomwd_open(struct inode *inode, struct file *file)
 133{
 134        if (test_and_set_bit(0, &mixcomwd_opened))
 135                return -EBUSY;
 136
 137        mixcomwd_ping();
 138
 139        if (nowayout)
 140                /*
 141                 * fops_get() code via open() has already done
 142                 * a try_module_get() so it is safe to do the
 143                 * __module_get().
 144                 */
 145                __module_get(THIS_MODULE);
 146        else {
 147                if (mixcomwd_timer_alive) {
 148                        del_timer(&mixcomwd_timer);
 149                        mixcomwd_timer_alive = 0;
 150                }
 151        }
 152        return nonseekable_open(inode, file);
 153}
 154
 155static int mixcomwd_release(struct inode *inode, struct file *file)
 156{
 157        if (expect_close == 42) {
 158                if (mixcomwd_timer_alive) {
 159                        printk(KERN_ERR PFX
 160                                "release called while internal timer alive");
 161                        return -EBUSY;
 162                }
 163                mixcomwd_timer_alive = 1;
 164                mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
 165        } else
 166                printk(KERN_CRIT PFX
 167                    "WDT device closed unexpectedly.  WDT will not stop!\n");
 168
 169        clear_bit(0, &mixcomwd_opened);
 170        expect_close = 0;
 171        return 0;
 172}
 173
 174
 175static ssize_t mixcomwd_write(struct file *file, const char __user *data,
 176                                                size_t len, loff_t *ppos)
 177{
 178        if (len) {
 179                if (!nowayout) {
 180                        size_t i;
 181
 182                        /* In case it was set long ago */
 183                        expect_close = 0;
 184
 185                        for (i = 0; i != len; i++) {
 186                                char c;
 187                                if (get_user(c, data + i))
 188                                        return -EFAULT;
 189                                if (c == 'V')
 190                                        expect_close = 42;
 191                        }
 192                }
 193                mixcomwd_ping();
 194        }
 195        return len;
 196}
 197
 198static long mixcomwd_ioctl(struct file *file,
 199                                unsigned int cmd, unsigned long arg)
 200{
 201        void __user *argp = (void __user *)arg;
 202        int __user *p = argp;
 203        int status;
 204        static const struct watchdog_info ident = {
 205                .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 206                .firmware_version = 1,
 207                .identity = "MixCOM watchdog",
 208        };
 209
 210        switch (cmd) {
 211        case WDIOC_GETSUPPORT:
 212                if (copy_to_user(argp, &ident, sizeof(ident)))
 213                        return -EFAULT;
 214                break;
 215        case WDIOC_GETSTATUS:
 216                status = mixcomwd_opened;
 217                if (!nowayout)
 218                        status |= mixcomwd_timer_alive;
 219                return put_user(status, p);
 220        case WDIOC_GETBOOTSTATUS:
 221                return put_user(0, p);
 222        case WDIOC_KEEPALIVE:
 223                mixcomwd_ping();
 224                break;
 225        default:
 226                return -ENOTTY;
 227        }
 228        return 0;
 229}
 230
 231static const struct file_operations mixcomwd_fops = {
 232        .owner          = THIS_MODULE,
 233        .llseek         = no_llseek,
 234        .write          = mixcomwd_write,
 235        .unlocked_ioctl = mixcomwd_ioctl,
 236        .open           = mixcomwd_open,
 237        .release        = mixcomwd_release,
 238};
 239
 240static struct miscdevice mixcomwd_miscdev = {
 241        .minor  = WATCHDOG_MINOR,
 242        .name   = "watchdog",
 243        .fops   = &mixcomwd_fops,
 244};
 245
 246static int __init checkcard(int port, int card_id)
 247{
 248        int id;
 249
 250        if (!request_region(port, 1, "MixCOM watchdog"))
 251                return 0;
 252
 253        id = inb_p(port);
 254        if (card_id == MIXCOM_ID)
 255                id &= 0x3f;
 256
 257        if (id != card_id) {
 258                release_region(port, 1);
 259                return 0;
 260        }
 261        return 1;
 262}
 263
 264static int __init mixcomwd_init(void)
 265{
 266        int i, ret, found = 0;
 267
 268        for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) {
 269                if (checkcard(mixcomwd_io_info[i].ioport,
 270                              mixcomwd_io_info[i].id)) {
 271                        found = 1;
 272                        watchdog_port = mixcomwd_io_info[i].ioport;
 273                }
 274        }
 275
 276        if (!found) {
 277                printk(KERN_ERR PFX
 278                        "No card detected, or port not available.\n");
 279                return -ENODEV;
 280        }
 281
 282        ret = misc_register(&mixcomwd_miscdev);
 283        if (ret) {
 284                printk(KERN_ERR PFX
 285                        "cannot register miscdev on minor=%d (err=%d)\n",
 286                                        WATCHDOG_MINOR, ret);
 287                goto error_misc_register_watchdog;
 288        }
 289
 290        printk(KERN_INFO
 291                "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",
 292                                        VERSION, watchdog_port);
 293
 294        return 0;
 295
 296error_misc_register_watchdog:
 297        release_region(watchdog_port, 1);
 298        watchdog_port = 0x0000;
 299        return ret;
 300}
 301
 302static void __exit mixcomwd_exit(void)
 303{
 304        if (!nowayout) {
 305                if (mixcomwd_timer_alive) {
 306                        printk(KERN_WARNING PFX "I quit now, hardware will"
 307                               " probably reboot!\n");
 308                        del_timer_sync(&mixcomwd_timer);
 309                        mixcomwd_timer_alive = 0;
 310                }
 311        }
 312        misc_deregister(&mixcomwd_miscdev);
 313        release_region(watchdog_port, 1);
 314}
 315
 316module_init(mixcomwd_init);
 317module_exit(mixcomwd_exit);
 318
 319MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
 320MODULE_DESCRIPTION("MixCom Watchdog driver");
 321MODULE_VERSION(VERSION);
 322MODULE_LICENSE("GPL");
 323MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 324