0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0024
0025 #include <linux/module.h> /* For module specific items */
0026 #include <linux/moduleparam.h> /* For new moduleparam's */
0027 #include <linux/types.h> /* For standard types (like size_t) */
0028 #include <linux/errno.h> /* For the -ENODEV/... values */
0029 #include <linux/kernel.h> /* For printk/panic/... */
0030 #include <linux/delay.h> /* For mdelay function */
0031 #include <linux/miscdevice.h> /* For struct miscdevice */
0032 #include <linux/watchdog.h> /* For the watchdog specific items */
0033 #include <linux/notifier.h> /* For notifier support */
0034 #include <linux/reboot.h> /* For reboot_notifier stuff */
0035 #include <linux/init.h> /* For __init/__exit/... */
0036 #include <linux/fs.h> /* For file operations */
0037 #include <linux/usb.h> /* For USB functions */
0038 #include <linux/slab.h> /* For kmalloc, ... */
0039 #include <linux/mutex.h> /* For mutex locking */
0040 #include <linux/hid.h> /* For HID_REQ_SET_REPORT & HID_DT_REPORT */
0041 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
0042
0043
0044
0045 #define DRIVER_VERSION "1.02"
0046 #define DRIVER_AUTHOR "Wim Van Sebroeck <wim@iguana.be>"
0047 #define DRIVER_DESC "Berkshire USB-PC Watchdog driver"
0048 #define DRIVER_NAME "pcwd_usb"
0049
0050 MODULE_AUTHOR(DRIVER_AUTHOR);
0051 MODULE_DESCRIPTION(DRIVER_DESC);
0052 MODULE_LICENSE("GPL");
0053
0054 #define WATCHDOG_HEARTBEAT 0
0055
0056 static int heartbeat = WATCHDOG_HEARTBEAT;
0057 module_param(heartbeat, int, 0);
0058 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
0059 "(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
0060 __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
0061
0062 static bool nowayout = WATCHDOG_NOWAYOUT;
0063 module_param(nowayout, bool, 0);
0064 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0065 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0066
0067
0068 #define USB_PCWD_VENDOR_ID 0x0c98
0069 #define USB_PCWD_PRODUCT_ID 0x1140
0070
0071
0072 static const struct usb_device_id usb_pcwd_table[] = {
0073 { USB_DEVICE(USB_PCWD_VENDOR_ID, USB_PCWD_PRODUCT_ID) },
0074 { }
0075 };
0076 MODULE_DEVICE_TABLE(usb, usb_pcwd_table);
0077
0078
0079
0080 #define USB_COMMAND_TIMEOUT 250
0081
0082
0083 #define CMD_READ_TEMP 0x02
0084
0085 #define CMD_TRIGGER CMD_READ_TEMP
0086 #define CMD_GET_STATUS 0x04
0087 #define CMD_GET_FIRMWARE_VERSION 0x08
0088 #define CMD_GET_DIP_SWITCH_SETTINGS 0x0c
0089 #define CMD_READ_WATCHDOG_TIMEOUT 0x18
0090 #define CMD_WRITE_WATCHDOG_TIMEOUT 0x19
0091 #define CMD_ENABLE_WATCHDOG 0x30
0092 #define CMD_DISABLE_WATCHDOG CMD_ENABLE_WATCHDOG
0093
0094
0095 static const int heartbeat_tbl[] = {
0096 5,
0097 10,
0098 30,
0099 60,
0100 300,
0101 600,
0102 1800,
0103 3600,
0104 };
0105
0106
0107 static int cards_found;
0108
0109
0110 static unsigned long is_active;
0111 static char expect_release;
0112
0113
0114 struct usb_pcwd_private {
0115
0116 struct usb_device *udev;
0117
0118 struct usb_interface *interface;
0119
0120
0121 unsigned int interface_number;
0122
0123
0124 unsigned char *intr_buffer;
0125
0126 dma_addr_t intr_dma;
0127
0128 size_t intr_size;
0129
0130 struct urb *intr_urb;
0131
0132
0133 unsigned char cmd_command;
0134
0135 unsigned char cmd_data_msb;
0136
0137 unsigned char cmd_data_lsb;
0138
0139 atomic_t cmd_received;
0140
0141
0142 int exists;
0143
0144 struct mutex mtx;
0145 };
0146 static struct usb_pcwd_private *usb_pcwd_device;
0147
0148
0149 static DEFINE_MUTEX(disconnect_mutex);
0150
0151
0152 static int usb_pcwd_probe(struct usb_interface *interface,
0153 const struct usb_device_id *id);
0154 static void usb_pcwd_disconnect(struct usb_interface *interface);
0155
0156
0157 static struct usb_driver usb_pcwd_driver = {
0158 .name = DRIVER_NAME,
0159 .probe = usb_pcwd_probe,
0160 .disconnect = usb_pcwd_disconnect,
0161 .id_table = usb_pcwd_table,
0162 };
0163
0164
0165 static void usb_pcwd_intr_done(struct urb *urb)
0166 {
0167 struct usb_pcwd_private *usb_pcwd =
0168 (struct usb_pcwd_private *)urb->context;
0169 unsigned char *data = usb_pcwd->intr_buffer;
0170 struct device *dev = &usb_pcwd->interface->dev;
0171 int retval;
0172
0173 switch (urb->status) {
0174 case 0:
0175 break;
0176 case -ECONNRESET:
0177 case -ENOENT:
0178 case -ESHUTDOWN:
0179
0180 dev_dbg(dev, "%s - urb shutting down with status: %d",
0181 __func__, urb->status);
0182 return;
0183
0184 default:
0185 dev_dbg(dev, "%s - nonzero urb status received: %d",
0186 __func__, urb->status);
0187 goto resubmit;
0188 }
0189
0190 dev_dbg(dev, "received following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
0191 data[0], data[1], data[2]);
0192
0193 usb_pcwd->cmd_command = data[0];
0194 usb_pcwd->cmd_data_msb = data[1];
0195 usb_pcwd->cmd_data_lsb = data[2];
0196
0197
0198 atomic_set(&usb_pcwd->cmd_received, 1);
0199
0200 resubmit:
0201 retval = usb_submit_urb(urb, GFP_ATOMIC);
0202 if (retval)
0203 pr_err("can't resubmit intr, usb_submit_urb failed with result %d\n",
0204 retval);
0205 }
0206
0207 static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
0208 unsigned char cmd, unsigned char *msb, unsigned char *lsb)
0209 {
0210 int got_response, count;
0211 unsigned char *buf;
0212
0213
0214
0215 if ((!usb_pcwd) || (!usb_pcwd->exists))
0216 return -1;
0217
0218 buf = kmalloc(6, GFP_KERNEL);
0219 if (buf == NULL)
0220 return 0;
0221
0222
0223
0224 buf[0] = cmd;
0225 buf[1] = *msb;
0226 buf[2] = *lsb;
0227 buf[3] = buf[4] = buf[5] = 0;
0228
0229 dev_dbg(&usb_pcwd->interface->dev,
0230 "sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
0231 buf[0], buf[1], buf[2]);
0232
0233 atomic_set(&usb_pcwd->cmd_received, 0);
0234
0235 if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0),
0236 HID_REQ_SET_REPORT, HID_DT_REPORT,
0237 0x0200, usb_pcwd->interface_number, buf, 6,
0238 USB_COMMAND_TIMEOUT) != 6) {
0239 dev_dbg(&usb_pcwd->interface->dev,
0240 "usb_pcwd_send_command: error in usb_control_msg for cmd 0x%x 0x%x 0x%x\n",
0241 cmd, *msb, *lsb);
0242 }
0243
0244
0245 got_response = 0;
0246 for (count = 0; (count < USB_COMMAND_TIMEOUT) && (!got_response);
0247 count++) {
0248 mdelay(1);
0249 if (atomic_read(&usb_pcwd->cmd_received))
0250 got_response = 1;
0251 }
0252
0253 if ((got_response) && (cmd == usb_pcwd->cmd_command)) {
0254
0255 *msb = usb_pcwd->cmd_data_msb;
0256 *lsb = usb_pcwd->cmd_data_lsb;
0257 }
0258
0259 kfree(buf);
0260
0261 return got_response;
0262 }
0263
0264 static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd)
0265 {
0266 unsigned char msb = 0x00;
0267 unsigned char lsb = 0x00;
0268 int retval;
0269
0270
0271 retval = usb_pcwd_send_command(usb_pcwd, CMD_ENABLE_WATCHDOG,
0272 &msb, &lsb);
0273
0274 if ((retval == 0) || (lsb == 0)) {
0275 pr_err("Card did not acknowledge enable attempt\n");
0276 return -1;
0277 }
0278
0279 return 0;
0280 }
0281
0282 static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd)
0283 {
0284 unsigned char msb = 0xA5;
0285 unsigned char lsb = 0xC3;
0286 int retval;
0287
0288
0289 retval = usb_pcwd_send_command(usb_pcwd, CMD_DISABLE_WATCHDOG,
0290 &msb, &lsb);
0291
0292 if ((retval == 0) || (lsb != 0)) {
0293 pr_err("Card did not acknowledge disable attempt\n");
0294 return -1;
0295 }
0296
0297 return 0;
0298 }
0299
0300 static int usb_pcwd_keepalive(struct usb_pcwd_private *usb_pcwd)
0301 {
0302 unsigned char dummy;
0303
0304
0305 usb_pcwd_send_command(usb_pcwd, CMD_TRIGGER, &dummy, &dummy);
0306
0307 return 0;
0308 }
0309
0310 static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t)
0311 {
0312 unsigned char msb = t / 256;
0313 unsigned char lsb = t % 256;
0314
0315 if ((t < 0x0001) || (t > 0xFFFF))
0316 return -EINVAL;
0317
0318
0319 usb_pcwd_send_command(usb_pcwd, CMD_WRITE_WATCHDOG_TIMEOUT, &msb, &lsb);
0320
0321 heartbeat = t;
0322 return 0;
0323 }
0324
0325 static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd,
0326 int *temperature)
0327 {
0328 unsigned char msb, lsb;
0329
0330 usb_pcwd_send_command(usb_pcwd, CMD_READ_TEMP, &msb, &lsb);
0331
0332
0333
0334
0335
0336 *temperature = (lsb * 9 / 5) + 32;
0337
0338 return 0;
0339 }
0340
0341 static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd,
0342 int *time_left)
0343 {
0344 unsigned char msb, lsb;
0345
0346
0347
0348 usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
0349
0350 *time_left = (msb << 8) + lsb;
0351
0352 return 0;
0353 }
0354
0355
0356
0357
0358
0359 static ssize_t usb_pcwd_write(struct file *file, const char __user *data,
0360 size_t len, loff_t *ppos)
0361 {
0362
0363 if (len) {
0364 if (!nowayout) {
0365 size_t i;
0366
0367
0368
0369 expect_release = 0;
0370
0371
0372
0373 for (i = 0; i != len; i++) {
0374 char c;
0375 if (get_user(c, data + i))
0376 return -EFAULT;
0377 if (c == 'V')
0378 expect_release = 42;
0379 }
0380 }
0381
0382
0383 usb_pcwd_keepalive(usb_pcwd_device);
0384 }
0385 return len;
0386 }
0387
0388 static long usb_pcwd_ioctl(struct file *file, unsigned int cmd,
0389 unsigned long arg)
0390 {
0391 void __user *argp = (void __user *)arg;
0392 int __user *p = argp;
0393 static const struct watchdog_info ident = {
0394 .options = WDIOF_KEEPALIVEPING |
0395 WDIOF_SETTIMEOUT |
0396 WDIOF_MAGICCLOSE,
0397 .firmware_version = 1,
0398 .identity = DRIVER_NAME,
0399 };
0400
0401 switch (cmd) {
0402 case WDIOC_GETSUPPORT:
0403 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0404
0405 case WDIOC_GETSTATUS:
0406 case WDIOC_GETBOOTSTATUS:
0407 return put_user(0, p);
0408
0409 case WDIOC_GETTEMP:
0410 {
0411 int temperature;
0412
0413 if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature))
0414 return -EFAULT;
0415
0416 return put_user(temperature, p);
0417 }
0418
0419 case WDIOC_SETOPTIONS:
0420 {
0421 int new_options, retval = -EINVAL;
0422
0423 if (get_user(new_options, p))
0424 return -EFAULT;
0425
0426 if (new_options & WDIOS_DISABLECARD) {
0427 usb_pcwd_stop(usb_pcwd_device);
0428 retval = 0;
0429 }
0430
0431 if (new_options & WDIOS_ENABLECARD) {
0432 usb_pcwd_start(usb_pcwd_device);
0433 retval = 0;
0434 }
0435
0436 return retval;
0437 }
0438
0439 case WDIOC_KEEPALIVE:
0440 usb_pcwd_keepalive(usb_pcwd_device);
0441 return 0;
0442
0443 case WDIOC_SETTIMEOUT:
0444 {
0445 int new_heartbeat;
0446
0447 if (get_user(new_heartbeat, p))
0448 return -EFAULT;
0449
0450 if (usb_pcwd_set_heartbeat(usb_pcwd_device, new_heartbeat))
0451 return -EINVAL;
0452
0453 usb_pcwd_keepalive(usb_pcwd_device);
0454 }
0455 fallthrough;
0456
0457 case WDIOC_GETTIMEOUT:
0458 return put_user(heartbeat, p);
0459
0460 case WDIOC_GETTIMELEFT:
0461 {
0462 int time_left;
0463
0464 if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left))
0465 return -EFAULT;
0466
0467 return put_user(time_left, p);
0468 }
0469
0470 default:
0471 return -ENOTTY;
0472 }
0473 }
0474
0475 static int usb_pcwd_open(struct inode *inode, struct file *file)
0476 {
0477
0478 if (test_and_set_bit(0, &is_active))
0479 return -EBUSY;
0480
0481
0482 usb_pcwd_start(usb_pcwd_device);
0483 usb_pcwd_keepalive(usb_pcwd_device);
0484 return stream_open(inode, file);
0485 }
0486
0487 static int usb_pcwd_release(struct inode *inode, struct file *file)
0488 {
0489
0490
0491
0492 if (expect_release == 42) {
0493 usb_pcwd_stop(usb_pcwd_device);
0494 } else {
0495 pr_crit("Unexpected close, not stopping watchdog!\n");
0496 usb_pcwd_keepalive(usb_pcwd_device);
0497 }
0498 expect_release = 0;
0499 clear_bit(0, &is_active);
0500 return 0;
0501 }
0502
0503
0504
0505
0506
0507 static ssize_t usb_pcwd_temperature_read(struct file *file, char __user *data,
0508 size_t len, loff_t *ppos)
0509 {
0510 int temperature;
0511
0512 if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature))
0513 return -EFAULT;
0514
0515 if (copy_to_user(data, &temperature, 1))
0516 return -EFAULT;
0517
0518 return 1;
0519 }
0520
0521 static int usb_pcwd_temperature_open(struct inode *inode, struct file *file)
0522 {
0523 return stream_open(inode, file);
0524 }
0525
0526 static int usb_pcwd_temperature_release(struct inode *inode, struct file *file)
0527 {
0528 return 0;
0529 }
0530
0531
0532
0533
0534
0535 static int usb_pcwd_notify_sys(struct notifier_block *this, unsigned long code,
0536 void *unused)
0537 {
0538 if (code == SYS_DOWN || code == SYS_HALT)
0539 usb_pcwd_stop(usb_pcwd_device);
0540
0541 return NOTIFY_DONE;
0542 }
0543
0544
0545
0546
0547
0548 static const struct file_operations usb_pcwd_fops = {
0549 .owner = THIS_MODULE,
0550 .llseek = no_llseek,
0551 .write = usb_pcwd_write,
0552 .unlocked_ioctl = usb_pcwd_ioctl,
0553 .compat_ioctl = compat_ptr_ioctl,
0554 .open = usb_pcwd_open,
0555 .release = usb_pcwd_release,
0556 };
0557
0558 static struct miscdevice usb_pcwd_miscdev = {
0559 .minor = WATCHDOG_MINOR,
0560 .name = "watchdog",
0561 .fops = &usb_pcwd_fops,
0562 };
0563
0564 static const struct file_operations usb_pcwd_temperature_fops = {
0565 .owner = THIS_MODULE,
0566 .llseek = no_llseek,
0567 .read = usb_pcwd_temperature_read,
0568 .open = usb_pcwd_temperature_open,
0569 .release = usb_pcwd_temperature_release,
0570 };
0571
0572 static struct miscdevice usb_pcwd_temperature_miscdev = {
0573 .minor = TEMP_MINOR,
0574 .name = "temperature",
0575 .fops = &usb_pcwd_temperature_fops,
0576 };
0577
0578 static struct notifier_block usb_pcwd_notifier = {
0579 .notifier_call = usb_pcwd_notify_sys,
0580 };
0581
0582
0583
0584
0585 static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd)
0586 {
0587 usb_free_urb(usb_pcwd->intr_urb);
0588 usb_free_coherent(usb_pcwd->udev, usb_pcwd->intr_size,
0589 usb_pcwd->intr_buffer, usb_pcwd->intr_dma);
0590 kfree(usb_pcwd);
0591 }
0592
0593
0594
0595
0596
0597
0598
0599 static int usb_pcwd_probe(struct usb_interface *interface,
0600 const struct usb_device_id *id)
0601 {
0602 struct usb_device *udev = interface_to_usbdev(interface);
0603 struct usb_host_interface *iface_desc;
0604 struct usb_endpoint_descriptor *endpoint;
0605 struct usb_pcwd_private *usb_pcwd = NULL;
0606 int pipe;
0607 int retval = -ENOMEM;
0608 int got_fw_rev;
0609 unsigned char fw_rev_major, fw_rev_minor;
0610 char fw_ver_str[20];
0611 unsigned char option_switches, dummy;
0612
0613 cards_found++;
0614 if (cards_found > 1) {
0615 pr_err("This driver only supports 1 device\n");
0616 return -ENODEV;
0617 }
0618
0619
0620 iface_desc = interface->cur_altsetting;
0621
0622
0623 if (!(iface_desc->desc.bInterfaceClass == USB_CLASS_HID)) {
0624 pr_err("The device isn't a Human Interface Device\n");
0625 return -ENODEV;
0626 }
0627
0628 if (iface_desc->desc.bNumEndpoints < 1)
0629 return -ENODEV;
0630
0631
0632 endpoint = &iface_desc->endpoint[0].desc;
0633
0634 if (!usb_endpoint_is_int_in(endpoint)) {
0635
0636 pr_err("Couldn't find an INTR & IN endpoint\n");
0637 return -ENODEV;
0638 }
0639
0640
0641 pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
0642
0643
0644 usb_pcwd = kzalloc(sizeof(struct usb_pcwd_private), GFP_KERNEL);
0645 if (usb_pcwd == NULL)
0646 goto error;
0647
0648 usb_pcwd_device = usb_pcwd;
0649
0650 mutex_init(&usb_pcwd->mtx);
0651 usb_pcwd->udev = udev;
0652 usb_pcwd->interface = interface;
0653 usb_pcwd->interface_number = iface_desc->desc.bInterfaceNumber;
0654 usb_pcwd->intr_size = (le16_to_cpu(endpoint->wMaxPacketSize) > 8 ?
0655 le16_to_cpu(endpoint->wMaxPacketSize) : 8);
0656
0657
0658 usb_pcwd->intr_buffer = usb_alloc_coherent(udev, usb_pcwd->intr_size,
0659 GFP_KERNEL, &usb_pcwd->intr_dma);
0660 if (!usb_pcwd->intr_buffer) {
0661 pr_err("Out of memory\n");
0662 goto error;
0663 }
0664
0665
0666 usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
0667 if (!usb_pcwd->intr_urb)
0668 goto error;
0669
0670
0671 usb_fill_int_urb(usb_pcwd->intr_urb, udev, pipe,
0672 usb_pcwd->intr_buffer, usb_pcwd->intr_size,
0673 usb_pcwd_intr_done, usb_pcwd, endpoint->bInterval);
0674 usb_pcwd->intr_urb->transfer_dma = usb_pcwd->intr_dma;
0675 usb_pcwd->intr_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
0676
0677
0678 if (usb_submit_urb(usb_pcwd->intr_urb, GFP_KERNEL)) {
0679 pr_err("Problem registering interrupt URB\n");
0680 retval = -EIO;
0681 goto error;
0682 }
0683
0684
0685 usb_pcwd->exists = 1;
0686
0687
0688 usb_pcwd_stop(usb_pcwd);
0689
0690
0691 got_fw_rev = usb_pcwd_send_command(usb_pcwd, CMD_GET_FIRMWARE_VERSION,
0692 &fw_rev_major, &fw_rev_minor);
0693 if (got_fw_rev)
0694 sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor);
0695 else
0696 sprintf(fw_ver_str, "<card no answer>");
0697
0698 pr_info("Found card (Firmware: %s) with temp option\n", fw_ver_str);
0699
0700
0701 usb_pcwd_send_command(usb_pcwd, CMD_GET_DIP_SWITCH_SETTINGS, &dummy,
0702 &option_switches);
0703
0704 pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
0705 option_switches,
0706 ((option_switches & 0x10) ? "ON" : "OFF"),
0707 ((option_switches & 0x08) ? "ON" : "OFF"));
0708
0709
0710 if (heartbeat == 0)
0711 heartbeat = heartbeat_tbl[(option_switches & 0x07)];
0712
0713
0714
0715 if (usb_pcwd_set_heartbeat(usb_pcwd, heartbeat)) {
0716 usb_pcwd_set_heartbeat(usb_pcwd, WATCHDOG_HEARTBEAT);
0717 pr_info("heartbeat value must be 0<heartbeat<65536, using %d\n",
0718 WATCHDOG_HEARTBEAT);
0719 }
0720
0721 retval = register_reboot_notifier(&usb_pcwd_notifier);
0722 if (retval != 0) {
0723 pr_err("cannot register reboot notifier (err=%d)\n", retval);
0724 goto error;
0725 }
0726
0727 retval = misc_register(&usb_pcwd_temperature_miscdev);
0728 if (retval != 0) {
0729 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0730 TEMP_MINOR, retval);
0731 goto err_out_unregister_reboot;
0732 }
0733
0734 retval = misc_register(&usb_pcwd_miscdev);
0735 if (retval != 0) {
0736 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0737 WATCHDOG_MINOR, retval);
0738 goto err_out_misc_deregister;
0739 }
0740
0741
0742 usb_set_intfdata(interface, usb_pcwd);
0743
0744 pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
0745 heartbeat, nowayout);
0746
0747 return 0;
0748
0749 err_out_misc_deregister:
0750 misc_deregister(&usb_pcwd_temperature_miscdev);
0751 err_out_unregister_reboot:
0752 unregister_reboot_notifier(&usb_pcwd_notifier);
0753 error:
0754 if (usb_pcwd)
0755 usb_pcwd_delete(usb_pcwd);
0756 usb_pcwd_device = NULL;
0757 return retval;
0758 }
0759
0760
0761
0762
0763
0764
0765
0766
0767
0768
0769 static void usb_pcwd_disconnect(struct usb_interface *interface)
0770 {
0771 struct usb_pcwd_private *usb_pcwd;
0772
0773
0774 mutex_lock(&disconnect_mutex);
0775
0776 usb_pcwd = usb_get_intfdata(interface);
0777 usb_set_intfdata(interface, NULL);
0778
0779 mutex_lock(&usb_pcwd->mtx);
0780
0781
0782 if (!nowayout)
0783 usb_pcwd_stop(usb_pcwd);
0784
0785
0786 usb_pcwd->exists = 0;
0787
0788
0789 misc_deregister(&usb_pcwd_miscdev);
0790 misc_deregister(&usb_pcwd_temperature_miscdev);
0791 unregister_reboot_notifier(&usb_pcwd_notifier);
0792
0793 mutex_unlock(&usb_pcwd->mtx);
0794
0795
0796 usb_pcwd_delete(usb_pcwd);
0797
0798 cards_found--;
0799
0800 mutex_unlock(&disconnect_mutex);
0801
0802 pr_info("USB PC Watchdog disconnected\n");
0803 }
0804
0805 module_usb_driver(usb_pcwd_driver);