root/drivers/watchdog/mixcomwd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mixcomwd_ping
  2. mixcomwd_timerfun
  3. mixcomwd_open
  4. mixcomwd_release
  5. mixcomwd_write
  6. mixcomwd_ioctl
  7. checkcard
  8. mixcomwd_init
  9. mixcomwd_exit

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

/* [<][>][^][v][top][bottom][index][help] */