0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0038
0039 #define VERSION "0.6"
0040 #define WATCHDOG_NAME "mixcomwd"
0041
0042 #include <linux/module.h>
0043 #include <linux/moduleparam.h>
0044 #include <linux/types.h>
0045 #include <linux/miscdevice.h>
0046 #include <linux/ioport.h>
0047 #include <linux/watchdog.h>
0048 #include <linux/fs.h>
0049 #include <linux/reboot.h>
0050 #include <linux/init.h>
0051 #include <linux/jiffies.h>
0052 #include <linux/timer.h>
0053 #include <linux/uaccess.h>
0054 #include <linux/io.h>
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066 #define MIXCOM_ID 0x11
0067 #define FLASHCOM_ID 0x18
0068 static struct {
0069 int ioport;
0070 int id;
0071 } mixcomwd_io_info[] = {
0072
0073 {0x0d90, MIXCOM_ID},
0074 {0x0e90, MIXCOM_ID},
0075 {0x0f90, MIXCOM_ID},
0076
0077 {0x0304, FLASHCOM_ID},
0078 {0x030c, FLASHCOM_ID},
0079 {0x0314, FLASHCOM_ID},
0080 {0x031c, FLASHCOM_ID},
0081 {0x0324, FLASHCOM_ID},
0082 {0x032c, FLASHCOM_ID},
0083 {0x0334, FLASHCOM_ID},
0084 {0x033c, FLASHCOM_ID},
0085 {0x0344, FLASHCOM_ID},
0086 {0x034c, FLASHCOM_ID},
0087 {0x0354, FLASHCOM_ID},
0088 {0x035c, FLASHCOM_ID},
0089 {0x0364, FLASHCOM_ID},
0090 {0x036c, FLASHCOM_ID},
0091 {0x0374, FLASHCOM_ID},
0092 {0x037c, FLASHCOM_ID},
0093
0094 {0x0000, 0},
0095 };
0096
0097 static void mixcomwd_timerfun(struct timer_list *unused);
0098
0099 static unsigned long mixcomwd_opened;
0100
0101 static int watchdog_port;
0102 static int mixcomwd_timer_alive;
0103 static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun);
0104 static char expect_close;
0105
0106 static bool nowayout = WATCHDOG_NOWAYOUT;
0107 module_param(nowayout, bool, 0);
0108 MODULE_PARM_DESC(nowayout,
0109 "Watchdog cannot be stopped once started (default="
0110 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0111
0112 static void mixcomwd_ping(void)
0113 {
0114 outb_p(55, watchdog_port);
0115 return;
0116 }
0117
0118 static void mixcomwd_timerfun(struct timer_list *unused)
0119 {
0120 mixcomwd_ping();
0121 mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
0122 }
0123
0124
0125
0126
0127
0128 static int mixcomwd_open(struct inode *inode, struct file *file)
0129 {
0130 if (test_and_set_bit(0, &mixcomwd_opened))
0131 return -EBUSY;
0132
0133 mixcomwd_ping();
0134
0135 if (nowayout)
0136
0137
0138
0139
0140
0141 __module_get(THIS_MODULE);
0142 else {
0143 if (mixcomwd_timer_alive) {
0144 del_timer(&mixcomwd_timer);
0145 mixcomwd_timer_alive = 0;
0146 }
0147 }
0148 return stream_open(inode, file);
0149 }
0150
0151 static int mixcomwd_release(struct inode *inode, struct file *file)
0152 {
0153 if (expect_close == 42) {
0154 if (mixcomwd_timer_alive) {
0155 pr_err("release called while internal timer alive\n");
0156 return -EBUSY;
0157 }
0158 mixcomwd_timer_alive = 1;
0159 mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
0160 } else
0161 pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
0162
0163 clear_bit(0, &mixcomwd_opened);
0164 expect_close = 0;
0165 return 0;
0166 }
0167
0168
0169 static ssize_t mixcomwd_write(struct file *file, const char __user *data,
0170 size_t len, loff_t *ppos)
0171 {
0172 if (len) {
0173 if (!nowayout) {
0174 size_t i;
0175
0176
0177 expect_close = 0;
0178
0179 for (i = 0; i != len; i++) {
0180 char c;
0181 if (get_user(c, data + i))
0182 return -EFAULT;
0183 if (c == 'V')
0184 expect_close = 42;
0185 }
0186 }
0187 mixcomwd_ping();
0188 }
0189 return len;
0190 }
0191
0192 static long mixcomwd_ioctl(struct file *file,
0193 unsigned int cmd, unsigned long arg)
0194 {
0195 void __user *argp = (void __user *)arg;
0196 int __user *p = argp;
0197 int status;
0198 static const struct watchdog_info ident = {
0199 .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0200 .firmware_version = 1,
0201 .identity = "MixCOM watchdog",
0202 };
0203
0204 switch (cmd) {
0205 case WDIOC_GETSUPPORT:
0206 if (copy_to_user(argp, &ident, sizeof(ident)))
0207 return -EFAULT;
0208 break;
0209 case WDIOC_GETSTATUS:
0210 status = mixcomwd_opened;
0211 if (!nowayout)
0212 status |= mixcomwd_timer_alive;
0213 return put_user(status, p);
0214 case WDIOC_GETBOOTSTATUS:
0215 return put_user(0, p);
0216 case WDIOC_KEEPALIVE:
0217 mixcomwd_ping();
0218 break;
0219 default:
0220 return -ENOTTY;
0221 }
0222 return 0;
0223 }
0224
0225 static const struct file_operations mixcomwd_fops = {
0226 .owner = THIS_MODULE,
0227 .llseek = no_llseek,
0228 .write = mixcomwd_write,
0229 .unlocked_ioctl = mixcomwd_ioctl,
0230 .compat_ioctl = compat_ptr_ioctl,
0231 .open = mixcomwd_open,
0232 .release = mixcomwd_release,
0233 };
0234
0235 static struct miscdevice mixcomwd_miscdev = {
0236 .minor = WATCHDOG_MINOR,
0237 .name = "watchdog",
0238 .fops = &mixcomwd_fops,
0239 };
0240
0241 static int __init checkcard(int port, int card_id)
0242 {
0243 int id;
0244
0245 if (!request_region(port, 1, "MixCOM watchdog"))
0246 return 0;
0247
0248 id = inb_p(port);
0249 if (card_id == MIXCOM_ID)
0250 id &= 0x3f;
0251
0252 if (id != card_id) {
0253 release_region(port, 1);
0254 return 0;
0255 }
0256 return 1;
0257 }
0258
0259 static int __init mixcomwd_init(void)
0260 {
0261 int i, ret, found = 0;
0262
0263 for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) {
0264 if (checkcard(mixcomwd_io_info[i].ioport,
0265 mixcomwd_io_info[i].id)) {
0266 found = 1;
0267 watchdog_port = mixcomwd_io_info[i].ioport;
0268 }
0269 }
0270
0271 if (!found) {
0272 pr_err("No card detected, or port not available\n");
0273 return -ENODEV;
0274 }
0275
0276 ret = misc_register(&mixcomwd_miscdev);
0277 if (ret) {
0278 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0279 WATCHDOG_MINOR, ret);
0280 goto error_misc_register_watchdog;
0281 }
0282
0283 pr_info("MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",
0284 VERSION, watchdog_port);
0285
0286 return 0;
0287
0288 error_misc_register_watchdog:
0289 release_region(watchdog_port, 1);
0290 watchdog_port = 0x0000;
0291 return ret;
0292 }
0293
0294 static void __exit mixcomwd_exit(void)
0295 {
0296 if (!nowayout) {
0297 if (mixcomwd_timer_alive) {
0298 pr_warn("I quit now, hardware will probably reboot!\n");
0299 del_timer_sync(&mixcomwd_timer);
0300 mixcomwd_timer_alive = 0;
0301 }
0302 }
0303 misc_deregister(&mixcomwd_miscdev);
0304 release_region(watchdog_port, 1);
0305 }
0306
0307 module_init(mixcomwd_init);
0308 module_exit(mixcomwd_exit);
0309
0310 MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
0311 MODULE_DESCRIPTION("MixCom Watchdog driver");
0312 MODULE_VERSION(VERSION);
0313 MODULE_LICENSE("GPL");