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
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0052
0053
0054 #include <linux/module.h> /* For module specific items */
0055 #include <linux/moduleparam.h> /* For new moduleparam's */
0056 #include <linux/types.h> /* For standard types (like size_t) */
0057 #include <linux/errno.h> /* For the -ENODEV/... values */
0058 #include <linux/kernel.h> /* For printk/panic/... */
0059 #include <linux/miscdevice.h> /* For struct miscdevice */
0060 #include <linux/watchdog.h> /* For the watchdog specific items */
0061 #include <linux/fs.h> /* For file operations */
0062 #include <linux/ioport.h> /* For io-port access */
0063 #include <linux/platform_device.h> /* For platform_driver framework */
0064 #include <linux/init.h> /* For __init/__exit/... */
0065 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
0066 #include <linux/io.h> /* For inb/outb/... */
0067
0068
0069 #define DRV_NAME "acquirewdt"
0070 #define WATCHDOG_NAME "Acquire WDT"
0071
0072 #define WATCHDOG_HEARTBEAT 0
0073
0074
0075
0076 static struct platform_device *acq_platform_device;
0077 static unsigned long acq_is_open;
0078 static char expect_close;
0079
0080
0081
0082 static int wdt_stop = 0x43;
0083 module_param(wdt_stop, int, 0);
0084 MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)");
0085
0086
0087 static int wdt_start = 0x443;
0088 module_param(wdt_start, int, 0);
0089 MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)");
0090
0091 static bool nowayout = WATCHDOG_NOWAYOUT;
0092 module_param(nowayout, bool, 0);
0093 MODULE_PARM_DESC(nowayout,
0094 "Watchdog cannot be stopped once started (default="
0095 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0096
0097
0098
0099
0100
0101 static void acq_keepalive(void)
0102 {
0103
0104 inb_p(wdt_start);
0105 }
0106
0107 static void acq_stop(void)
0108 {
0109
0110 inb_p(wdt_stop);
0111 }
0112
0113
0114
0115
0116
0117 static ssize_t acq_write(struct file *file, const char __user *buf,
0118 size_t count, loff_t *ppos)
0119 {
0120
0121 if (count) {
0122 if (!nowayout) {
0123 size_t i;
0124
0125
0126 expect_close = 0;
0127
0128
0129 for (i = 0; i != count; i++) {
0130 char c;
0131 if (get_user(c, buf + i))
0132 return -EFAULT;
0133 if (c == 'V')
0134 expect_close = 42;
0135 }
0136 }
0137
0138
0139 acq_keepalive();
0140 }
0141 return count;
0142 }
0143
0144 static long acq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0145 {
0146 int options, retval = -EINVAL;
0147 void __user *argp = (void __user *)arg;
0148 int __user *p = argp;
0149 static const struct watchdog_info ident = {
0150 .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0151 .firmware_version = 1,
0152 .identity = WATCHDOG_NAME,
0153 };
0154
0155 switch (cmd) {
0156 case WDIOC_GETSUPPORT:
0157 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0158
0159 case WDIOC_GETSTATUS:
0160 case WDIOC_GETBOOTSTATUS:
0161 return put_user(0, p);
0162
0163 case WDIOC_SETOPTIONS:
0164 {
0165 if (get_user(options, p))
0166 return -EFAULT;
0167 if (options & WDIOS_DISABLECARD) {
0168 acq_stop();
0169 retval = 0;
0170 }
0171 if (options & WDIOS_ENABLECARD) {
0172 acq_keepalive();
0173 retval = 0;
0174 }
0175 return retval;
0176 }
0177 case WDIOC_KEEPALIVE:
0178 acq_keepalive();
0179 return 0;
0180
0181 case WDIOC_GETTIMEOUT:
0182 return put_user(WATCHDOG_HEARTBEAT, p);
0183
0184 default:
0185 return -ENOTTY;
0186 }
0187 }
0188
0189 static int acq_open(struct inode *inode, struct file *file)
0190 {
0191 if (test_and_set_bit(0, &acq_is_open))
0192 return -EBUSY;
0193
0194 if (nowayout)
0195 __module_get(THIS_MODULE);
0196
0197
0198 acq_keepalive();
0199 return stream_open(inode, file);
0200 }
0201
0202 static int acq_close(struct inode *inode, struct file *file)
0203 {
0204 if (expect_close == 42) {
0205 acq_stop();
0206 } else {
0207 pr_crit("Unexpected close, not stopping watchdog!\n");
0208 acq_keepalive();
0209 }
0210 clear_bit(0, &acq_is_open);
0211 expect_close = 0;
0212 return 0;
0213 }
0214
0215
0216
0217
0218
0219 static const struct file_operations acq_fops = {
0220 .owner = THIS_MODULE,
0221 .llseek = no_llseek,
0222 .write = acq_write,
0223 .unlocked_ioctl = acq_ioctl,
0224 .compat_ioctl = compat_ptr_ioctl,
0225 .open = acq_open,
0226 .release = acq_close,
0227 };
0228
0229 static struct miscdevice acq_miscdev = {
0230 .minor = WATCHDOG_MINOR,
0231 .name = "watchdog",
0232 .fops = &acq_fops,
0233 };
0234
0235
0236
0237
0238
0239 static int __init acq_probe(struct platform_device *dev)
0240 {
0241 int ret;
0242
0243 if (wdt_stop != wdt_start) {
0244 if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
0245 pr_err("I/O address 0x%04x already in use\n", wdt_stop);
0246 ret = -EIO;
0247 goto out;
0248 }
0249 }
0250
0251 if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
0252 pr_err("I/O address 0x%04x already in use\n", wdt_start);
0253 ret = -EIO;
0254 goto unreg_stop;
0255 }
0256 ret = misc_register(&acq_miscdev);
0257 if (ret != 0) {
0258 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0259 WATCHDOG_MINOR, ret);
0260 goto unreg_regions;
0261 }
0262 pr_info("initialized. (nowayout=%d)\n", nowayout);
0263
0264 return 0;
0265 unreg_regions:
0266 release_region(wdt_start, 1);
0267 unreg_stop:
0268 if (wdt_stop != wdt_start)
0269 release_region(wdt_stop, 1);
0270 out:
0271 return ret;
0272 }
0273
0274 static int acq_remove(struct platform_device *dev)
0275 {
0276 misc_deregister(&acq_miscdev);
0277 release_region(wdt_start, 1);
0278 if (wdt_stop != wdt_start)
0279 release_region(wdt_stop, 1);
0280
0281 return 0;
0282 }
0283
0284 static void acq_shutdown(struct platform_device *dev)
0285 {
0286
0287 acq_stop();
0288 }
0289
0290 static struct platform_driver acquirewdt_driver = {
0291 .remove = acq_remove,
0292 .shutdown = acq_shutdown,
0293 .driver = {
0294 .name = DRV_NAME,
0295 },
0296 };
0297
0298 static int __init acq_init(void)
0299 {
0300 int err;
0301
0302 pr_info("WDT driver for Acquire single board computer initialising\n");
0303
0304 acq_platform_device = platform_device_register_simple(DRV_NAME,
0305 -1, NULL, 0);
0306 if (IS_ERR(acq_platform_device))
0307 return PTR_ERR(acq_platform_device);
0308
0309 err = platform_driver_probe(&acquirewdt_driver, acq_probe);
0310 if (err)
0311 goto unreg_platform_device;
0312 return 0;
0313
0314 unreg_platform_device:
0315 platform_device_unregister(acq_platform_device);
0316 return err;
0317 }
0318
0319 static void __exit acq_exit(void)
0320 {
0321 platform_device_unregister(acq_platform_device);
0322 platform_driver_unregister(&acquirewdt_driver);
0323 pr_info("Watchdog Module Unloaded\n");
0324 }
0325
0326 module_init(acq_init);
0327 module_exit(acq_exit);
0328
0329 MODULE_AUTHOR("David Woodhouse");
0330 MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver");
0331 MODULE_LICENSE("GPL");