static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, loff_t *ppos) { if (ppos != &file->f_pos) { return -ESPIPE; } if(len) { if (!nowayout) { size_t i; /* In case it was set long ago */ expect_close = 0; for (i = 0; i != len; i++) { char c; if (get_user(c, data + i)) return -EFAULT; if (c == 'V') expect_close = 1; } } mixcomwd_ping(); } return len; }
static int mixcomwd_open(struct inode *inode, struct file *file) { if(test_and_set_bit(0,&mixcomwd_opened)) { return -EBUSY; } mixcomwd_ping(); if (nowayout) { /* * fops_get() code via open() has already done * a try_module_get() so it is safe to do the * __module_get(). */ __module_get(THIS_MODULE); } else { if(mixcomwd_timer_alive) { del_timer(&mixcomwd_timer); mixcomwd_timer_alive=0; } } return 0; }
static void mixcomwd_timerfun(unsigned long d) { mixcomwd_ping(); mod_timer(&mixcomwd_timer,jiffies+ 5*HZ); }
static int mixcomwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int status; static struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "MixCOM watchdog" }; switch(cmd) { case WDIOC_GETSTATUS: status=mixcomwd_opened; if (!nowayout) { status|=mixcomwd_timer_alive; } if (copy_to_user((int *)arg, &status, sizeof(int))) { return -EFAULT; } break; case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) { return -EFAULT; } break; case WDIOC_KEEPALIVE: mixcomwd_ping(); break; default: return -ENOTTY; } return 0; } static struct file_operations mixcomwd_fops= { .owner = THIS_MODULE, .write = mixcomwd_write, .ioctl = mixcomwd_ioctl, .open = mixcomwd_open, .release = mixcomwd_release, }; static struct miscdevice mixcomwd_miscdev= { WATCHDOG_MINOR, "watchdog", &mixcomwd_fops }; static int __init mixcomwd_checkcard(int port) { int id; port += MIXCOM_WATCHDOG_OFFSET; if (!request_region(port, 1, "MixCOM watchdog")) { return 0; } id=inb_p(port) & 0x3f; if(id!=MIXCOM_ID) { release_region(port, 1); return 0; } return port; }
static long mixcomwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int status; static const struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "MixCOM watchdog", }; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; break; case WDIOC_GETSTATUS: status = mixcomwd_opened; if (!nowayout) status |= mixcomwd_timer_alive; return put_user(status, p); case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_KEEPALIVE: mixcomwd_ping(); break; default: return -ENOTTY; } return 0; } static const struct file_operations mixcomwd_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = mixcomwd_write, .unlocked_ioctl = mixcomwd_ioctl, .open = mixcomwd_open, .release = mixcomwd_release, }; static struct miscdevice mixcomwd_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &mixcomwd_fops, }; static int __init checkcard(int port, int card_id) { int id; if (!request_region(port, 1, "MixCOM watchdog")) return 0; id = inb_p(port); if (card_id == MIXCOM_ID) id &= 0x3f; if (id != card_id) { release_region(port, 1); return 0; } return 1; } static int __init mixcomwd_init(void) { int i, ret, found = 0; for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) { if (checkcard(mixcomwd_io_info[i].ioport, mixcomwd_io_info[i].id)) { found = 1; watchdog_port = mixcomwd_io_info[i].ioport; } } if (!found) { pr_err("No card detected, or port not available\n"); return -ENODEV; } ret = misc_register(&mixcomwd_miscdev); if (ret) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); goto error_misc_register_watchdog; } pr_info("MixCOM watchdog driver v%s, watchdog port at 0x%3x\n", VERSION, watchdog_port); return 0; error_misc_register_watchdog: release_region(watchdog_port, 1); watchdog_port = 0x0000; return ret; }
static void mixcomwd_timerfun(struct timer_list *unused) { mixcomwd_ping(); mod_timer(&mixcomwd_timer, jiffies + 5 * HZ); }