[WATCHDOG] v2.6.3 pcwd_usb-watchdog

From: Wim Van Sebroeck (wim_at_iguana.be)
Date: 02/29/04

  • Next message: Andrew Morton: "2.6.4-rc1-mm1"
    Date:	Sun, 29 Feb 2004 23:05:51 +0100
    To: Linus Torvalds <torvalds@osdl.org>, Andrew Morton <akpm@osdl.org>
    
    

    Hi Linus, Andrew,

    I added 2 extra changesets since yesterday, so if you do a

            bk pull http://linux-watchdog.bkbits.net/linux-2.6-watchdog

    You will get updates for the following files:

     arch/sparc64/Kconfig | 22
     drivers/char/watchdog/Kconfig | 46 +
     drivers/char/watchdog/Makefile | 3
     drivers/char/watchdog/cpu5wdt.c | 1
     drivers/char/watchdog/eurotechwdt.c | 1
     drivers/char/watchdog/ib700wdt.c | 1
     drivers/char/watchdog/machzwd.c | 1
     drivers/char/watchdog/mixcomwd.c | 3
     drivers/char/watchdog/pcwd.c | 5
     drivers/char/watchdog/pcwd_usb.c | 810 +++++++++++++++++++++++++++++++++++
     drivers/char/watchdog/sa1100_wdt.c | 1
     drivers/char/watchdog/sc1200wdt.c | 1
     drivers/char/watchdog/w83627hf_wdt.c | 2
     drivers/char/watchdog/w83877f_wdt.c | 1
     drivers/char/watchdog/wdt285.c | 1
     drivers/char/watchdog/wdt977.c | 2
     drivers/char/watchdog/wdt_pci.c | 2
     17 files changed, 878 insertions(+), 25 deletions(-)

    And these are the ChangeSets:

    <wim@iguana.be> (04/02/29 1.1628)
       [WATCHDOG] v2.6.3 pcwd_usb-watchdog
       
       Add the Berkshire Products USB-PC Watchdog driver

    <wim@iguana.be> (04/02/29 1.1629)
       [WATCHDOG] v2.6.3 MODULE_*-patch
       
       Add MODULE_* info

    <wim@iguana.be> (04/02/29 1.1630)
       [WATCHDOG/SPARC64] v2.6.3 Kconfig-WATCHDOG_CP1XXX/WATCHDOG_RIO-patch
       
       Move WATCHDOG_CP1XXX and WATCHDOG_RIO for SPARC64 architecture
       from arch/sparc64/Kconfig to drivers/char/watchdog/Kconfig
       and made them dependant of WATCHDOG also

    The ChangeSets can also be looked at on:
            http://linux-watchdog.bkbits.net:8080/linux-2.6-watchdog

    For completeness, I added the patches below.

    Greetings,
    Wim.

    ================================================================================
    diff -Nru a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
    --- a/drivers/char/watchdog/Kconfig Sun Feb 29 22:53:13 2004
    +++ b/drivers/char/watchdog/Kconfig Sun Feb 29 22:53:13 2004
    @@ -455,4 +455,26 @@
               Fahrenheit. This works only if you have a WDT501P watchdog board
               installed.
     
    +#
    +# USB-based Watchdog Cards
    +#
    +
    +comment "USB-based Watchdog Cards"
    + depends on WATCHDOG && USB
    +
    +config USBPCWATCHDOG
    + tristate "Berkshire Products USB-PC Watchdog"
    + depends on WATCHDOG && USB
    + ---help---
    + This is the driver for the Berkshire Products USB-PC Watchdog card.
    + This card simply watches your kernel to make sure it doesn't freeze,
    + and if it does, it reboots your computer after a certain amount of
    + time. The card can also monitor the internal temperature of the PC.
    + More info is available at <http://www.berkprod.com/usb_pc_watchdog.htm>.
    +
    + To compile this driver as a module, choose M here: the
    + module will be called pcwd_usb.
    +
    + Most people will say N.
    +
     endmenu
    diff -Nru a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
    --- a/drivers/char/watchdog/Makefile Sun Feb 29 22:53:13 2004
    +++ b/drivers/char/watchdog/Makefile Sun Feb 29 22:53:13 2004
    @@ -1,5 +1,5 @@
     #
    -# Makefile for the kernel character device drivers.
    +# Makefile for the WatchDog device drivers.
     #
     
     # Only one watchdog can succeed. We probe the hardware watchdog
    @@ -35,3 +35,4 @@
     obj-$(CONFIG_AMD7XX_TCO) += amd7xx_tco.o
     obj-$(CONFIG_INDYDOG) += indydog.o
     obj-$(CONFIG_PCIPCWATCHDOG) += pcwd_pci.o
    +obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
    diff -Nru a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c
    --- /dev/null Wed Dec 31 16:00:00 1969
    +++ b/drivers/char/watchdog/pcwd_usb.c Sun Feb 29 22:53:13 2004
    @@ -0,0 +1,810 @@
    +/*
    + * Berkshire USB-PC Watchdog Card Driver
    + *
    + * (c) Copyright 2004 Wim Van Sebroeck <wim@iguana.be>.
    + *
    + * Based on source code of the following authors:
    + * Ken Hollis <kenji@bitgate.com>,
    + * Alan Cox <alan@redhat.com>,
    + * Matt Domsch <Matt_Domsch@dell.com>,
    + * Rob Radez <rob@osinvestor.com>,
    + * Greg Kroah-Hartman <greg@kroah.com>
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License
    + * as published by the Free Software Foundation; either version
    + * 2 of the License, or (at your option) any later version.
    + *
    + * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
    + * provide warranty for any of this software. This material is
    + * provided "AS-IS" and at no charge.
    + *
    + * Thanks also to Simon Machell at Berkshire Products Inc. for
    + * providing the test hardware. More info is available at
    + * http://www.berkprod.com/ or http://www.pcwatchdog.com/
    + */
    +
    +#include <linux/config.h>
    +#include <linux/kernel.h>
    +#include <linux/errno.h>
    +#include <linux/init.h>
    +#include <linux/slab.h>
    +#include <linux/module.h>
    +#include <linux/moduleparam.h>
    +#include <linux/types.h>
    +#include <linux/delay.h>
    +#include <linux/miscdevice.h>
    +#include <linux/watchdog.h>
    +#include <linux/notifier.h>
    +#include <linux/reboot.h>
    +#include <linux/fs.h>
    +#include <linux/smp_lock.h>
    +#include <linux/completion.h>
    +#include <asm/uaccess.h>
    +#include <linux/usb.h>
    +
    +
    +#ifdef CONFIG_USB_DEBUG
    + static int debug = 1;
    +#else
    + static int debug;
    +#endif
    +
    +/* Use our own dbg macro */
    +#undef dbg
    +#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG PFX format "\n" , ## arg); } while (0)
    +
    +
    +/* Module and Version Information */
    +#define DRIVER_VERSION "v1.00 (28/02/2004)"
    +#define DRIVER_AUTHOR "Wim Van Sebroeck <wim@iguana.be>"
    +#define DRIVER_DESC "Berkshire USB-PC Watchdog driver"
    +#define DRIVER_LICENSE "GPL"
    +#define DRIVER_NAME "pcwd_usb"
    +#define PFX DRIVER_NAME ": "
    +
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_LICENSE(DRIVER_LICENSE);
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    +MODULE_ALIAS_MISCDEV(TEMP_MINOR);
    +
    +/* Module Parameters */
    +module_param(debug, int, 0);
    +MODULE_PARM_DESC(debug, "Debug enabled or not");
    +
    +#define WATCHDOG_HEARTBEAT 2 /* 2 sec default heartbeat */
    +static int heartbeat = WATCHDOG_HEARTBEAT;
    +module_param(heartbeat, int, 0);
    +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0<heartbeat<65536, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
    +
    +#ifdef CONFIG_WATCHDOG_NOWAYOUT
    +static int nowayout = 1;
    +#else
    +static int nowayout = 0;
    +#endif
    +
    +module_param(nowayout, int, 0);
    +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
    +
    +/* The vendor and product id's for the USB-PC Watchdog card */
    +#define USB_PCWD_VENDOR_ID 0x0c98
    +#define USB_PCWD_PRODUCT_ID 0x1140
    +
    +/* table of devices that work with this driver */
    +static struct usb_device_id usb_pcwd_table [] = {
    + { USB_DEVICE(USB_PCWD_VENDOR_ID, USB_PCWD_PRODUCT_ID) },
    + { } /* Terminating entry */
    +};
    +MODULE_DEVICE_TABLE (usb, usb_pcwd_table);
    +
    +/* according to documentation max. time to process a command for the USB
    + * watchdog card is 100 or 200 ms, so we give it 250 ms to do it's job */
    +#define USB_COMMAND_TIMEOUT 250
    +
    +/* Watchdog's internal commands */
    +#define CMD_READ_TEMP 0x02 /* Read Temperature; Re-trigger Watchdog */
    +#define CMD_TRIGGER CMD_READ_TEMP
    +#define CMD_GET_STATUS 0x04 /* Get Status Information */
    +#define CMD_GET_FIRMWARE_VERSION 0x08 /* Get Firmware Version */
    +#define CMD_GET_DIP_SWITCH_SETTINGS 0x0c /* Get Dip Switch Settings */
    +#define CMD_READ_WATCHDOG_TIMEOUT 0x18 /* Read Current Watchdog Time */
    +#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 /* Write Current Watchdog Time */
    +#define CMD_ENABLE_WATCHDOG 0x30 /* Enable / Disable Watchdog */
    +#define CMD_DISABLE_WATCHDOG CMD_ENABLE_WATCHDOG
    +
    +/* Some defines that I like to be somewhere else like include/linux/usb_hid.h */
    +#define HID_REQ_SET_REPORT 0x09
    +#define HID_DT_REPORT (USB_TYPE_CLASS | 0x02)
    +
    +/* We can only use 1 card due to the /dev/watchdog restriction */
    +static int cards_found;
    +
    +/* some internal variables */
    +static unsigned long is_active;
    +static char expect_release;
    +
    +/* Structure to hold all of our device specific stuff */
    +struct usb_pcwd_private {
    + struct usb_device * udev; /* save off the usb device pointer */
    + struct usb_interface * interface; /* the interface for this device */
    +
    + unsigned int interface_number; /* the interface number used for cmd's */
    +
    + unsigned char * intr_buffer; /* the buffer to intr data */
    + dma_addr_t intr_dma; /* the dma address for the intr buffer */
    + size_t intr_size; /* the size of the intr buffer */
    + struct urb * intr_urb; /* the urb used for the intr pipe */
    +
    + unsigned char cmd_command; /* The command that is reported back */
    + unsigned char cmd_data_msb; /* The data MSB that is reported back */
    + unsigned char cmd_data_lsb; /* The data LSB that is reported back */
    + atomic_t cmd_received; /* true if we received a report after a command */
    +
    + int exists; /* Wether or not the device exists */
    + struct semaphore sem; /* locks this structure */
    +};
    +static struct usb_pcwd_private *usb_pcwd_device;
    +
    +/* prevent races between open() and disconnect() */
    +static DECLARE_MUTEX (disconnect_sem);
    +
    +/* local function prototypes */
    +static int usb_pcwd_probe (struct usb_interface *interface, const struct usb_device_id *id);
    +static void usb_pcwd_disconnect (struct usb_interface *interface);
    +
    +/* usb specific object needed to register this driver with the usb subsystem */
    +static struct usb_driver usb_pcwd_driver = {
    + .owner = THIS_MODULE,
    + .name = DRIVER_NAME,
    + .probe = usb_pcwd_probe,
    + .disconnect = usb_pcwd_disconnect,
    + .id_table = usb_pcwd_table,
    +};
    +
    +
    +static void usb_pcwd_intr_done(struct urb *urb, struct pt_regs *regs)
    +{
    + struct usb_pcwd_private *usb_pcwd = (struct usb_pcwd_private *)urb->context;
    + unsigned char *data = usb_pcwd->intr_buffer;
    + int retval;
    +
    + switch (urb->status) {
    + case 0: /* success */
    + break;
    + case -ECONNRESET: /* unlink */
    + case -ENOENT:
    + case -ESHUTDOWN:
    + /* this urb is terminated, clean up */
    + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
    + return;
    + /* -EPIPE: should clear the halt */
    + default: /* error */
    + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
    + goto resubmit;
    + }
    +
    + dbg("received following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
    + data[0], data[1], data[2]);
    +
    + usb_pcwd->cmd_command = data[0];
    + usb_pcwd->cmd_data_msb = data[1];
    + usb_pcwd->cmd_data_lsb = data[2];
    +
    + /* notify anyone waiting that the cmd has finished */
    + atomic_set (&usb_pcwd->cmd_received, 1);
    +
    +resubmit:
    + retval = usb_submit_urb (urb, GFP_ATOMIC);
    + if (retval)
    + printk(KERN_ERR PFX "can't resubmit intr, usb_submit_urb failed with result %d\n",
    + retval);
    +}
    +
    +static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd, unsigned char cmd,
    + unsigned char *msb, unsigned char *lsb)
    +{
    + int got_response, count;
    + unsigned char buf[6];
    +
    + /* We will not send any commands if the USB PCWD device does not exist */
    + if ((!usb_pcwd) || (!usb_pcwd->exists))
    + return -1;
    +
    + /* The USB PC Watchdog uses a 6 byte report format. The board currently uses
    + * only 3 of the six bytes of the report. */
    + buf[0] = cmd; /* Byte 0 = CMD */
    + buf[1] = *msb; /* Byte 1 = Data MSB */
    + buf[2] = *lsb; /* Byte 2 = Data LSB */
    + buf[3] = buf[4] = buf[5] = 0; /* All other bytes not used */
    +
    + dbg("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x",
    + buf[0], buf[1], buf[2]);
    +
    + atomic_set (&usb_pcwd->cmd_received, 0);
    +
    + if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0),
    + HID_REQ_SET_REPORT, HID_DT_REPORT,
    + 0x0200, usb_pcwd->interface_number, buf, sizeof(buf),
    + HZ) != sizeof(buf)) {
    + dbg("usb_pcwd_send_command: error in usb_control_msg for cmd 0x%x 0x%x 0x%x\n", cmd, *msb, *lsb);
    + }
    + /* wait till the usb card processed the command,
    + * with a max. timeout of USB_COMMAND_TIMEOUT */
    + got_response = 0;
    + for (count = 0; (count < USB_COMMAND_TIMEOUT) && (!got_response); count++) {
    + mdelay(1);
    + if (atomic_read (&usb_pcwd->cmd_received))
    + got_response = 1;
    + }
    +
    + if ((got_response) && (cmd == usb_pcwd->cmd_command)) {
    + /* read back response */
    + *msb = usb_pcwd->cmd_data_msb;
    + *lsb = usb_pcwd->cmd_data_lsb;
    + }
    +
    + return got_response;
    +}
    +
    +static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd)
    +{
    + unsigned char msb = 0x00;
    + unsigned char lsb = 0x00;
    + int retval;
    +
    + /* Enable Watchdog */
    + retval = usb_pcwd_send_command(usb_pcwd, CMD_ENABLE_WATCHDOG, &msb, &lsb);
    +
    + if ((retval == 0) || (lsb == 0)) {
    + printk(KERN_ERR PFX "Card did not acknowledge enable attempt\n");
    + return -1;
    + }
    +
    + return 0;
    +}
    +
    +static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd)
    +{
    + unsigned char msb = 0xA5;
    + unsigned char lsb = 0xC3;
    + int retval;
    +
    + /* Disable Watchdog */
    + retval = usb_pcwd_send_command(usb_pcwd, CMD_DISABLE_WATCHDOG, &msb, &lsb);
    +
    + if ((retval == 0) || (lsb != 0)) {
    + printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n");
    + return -1;
    + }
    +
    + return 0;
    +}
    +
    +static int usb_pcwd_keepalive(struct usb_pcwd_private *usb_pcwd)
    +{
    + unsigned char dummy;
    +
    + /* Re-trigger Watchdog */
    + usb_pcwd_send_command(usb_pcwd, CMD_TRIGGER, &dummy, &dummy);
    +
    + return 0;
    +}
    +
    +static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t)
    +{
    + unsigned char msb = t / 256;
    + unsigned char lsb = t % 256;
    +
    + if ((t < 0x0001) || (t > 0xFFFF))
    + return -EINVAL;
    +
    + /* Write new heartbeat to watchdog */
    + usb_pcwd_send_command(usb_pcwd, CMD_WRITE_WATCHDOG_TIMEOUT, &msb, &lsb);
    +
    + heartbeat = t;
    + return 0;
    +}
    +
    +static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temperature)
    +{
    + unsigned char msb, lsb;
    +
    + usb_pcwd_send_command(usb_pcwd, CMD_READ_TEMP, &msb, &lsb);
    +
    + /*
    + * Convert celsius to fahrenheit, since this was
    + * the decided 'standard' for this return value.
    + */
    + *temperature = (lsb * 9 / 5) + 32;
    +
    + return 0;
    +}
    +
    +/*
    + * /dev/watchdog handling
    + */
    +
    +static ssize_t usb_pcwd_write(struct file *file, const char *data,
    + size_t len, loff_t *ppos)
    +{
    + /* Can't seek (pwrite) on this device */
    + if (ppos != &file->f_pos)
    + return -ESPIPE;
    +
    + /* See if we got the magic character 'V' and reload the timer */
    + if (len) {
    + if (!nowayout) {
    + size_t i;
    +
    + /* note: just in case someone wrote the magic character
    + * five months ago... */
    + expect_release = 0;
    +
    + /* scan to see whether or not we got the magic character */
    + for (i = 0; i != len; i++) {
    + char c;
    + if(get_user(c, data+i))
    + return -EFAULT;
    + if (c == 'V')
    + expect_release = 42;
    + }
    + }
    +
    + /* someone wrote to us, we should reload the timer */
    + usb_pcwd_keepalive(usb_pcwd_device);
    + }
    + return len;
    +}
    +
    +static int usb_pcwd_ioctl(struct inode *inode, struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + static struct watchdog_info ident = {
    + .options = WDIOF_KEEPALIVEPING |
    + WDIOF_SETTIMEOUT |
    + WDIOF_MAGICCLOSE,
    + .firmware_version = 1,
    + .identity = DRIVER_NAME,
    + };
    +
    + switch (cmd) {
    + case WDIOC_GETSUPPORT:
    + return copy_to_user((struct watchdog_info *) arg, &ident,
    + sizeof (ident)) ? -EFAULT : 0;
    +
    + case WDIOC_GETSTATUS:
    + case WDIOC_GETBOOTSTATUS:
    + return put_user(0, (int *) arg);
    +
    + case WDIOC_GETTEMP:
    + {
    + int temperature;
    +
    + if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature))
    + return -EFAULT;
    +
    + return put_user(temperature, (int *) arg);
    + }
    +
    + case WDIOC_KEEPALIVE:
    + usb_pcwd_keepalive(usb_pcwd_device);
    + return 0;
    +
    + case WDIOC_SETOPTIONS:
    + {
    + int new_options, retval = -EINVAL;
    +
    + if (get_user (new_options, (int *) arg))
    + return -EFAULT;
    +
    + if (new_options & WDIOS_DISABLECARD) {
    + usb_pcwd_stop(usb_pcwd_device);
    + retval = 0;
    + }
    +
    + if (new_options & WDIOS_ENABLECARD) {
    + usb_pcwd_start(usb_pcwd_device);
    + retval = 0;
    + }
    +
    + return retval;
    + }
    +
    + case WDIOC_SETTIMEOUT:
    + {
    + int new_heartbeat;
    +
    + if (get_user(new_heartbeat, (int *) arg))
    + return -EFAULT;
    +
    + if (usb_pcwd_set_heartbeat(usb_pcwd_device, new_heartbeat))
    + return -EINVAL;
    +
    + usb_pcwd_keepalive(usb_pcwd_device);
    + /* Fall */
    + }
    +
    + case WDIOC_GETTIMEOUT:
    + return put_user(heartbeat, (int *)arg);
    +
    + default:
    + return -ENOIOCTLCMD;
    + }
    +}
    +
    +static int usb_pcwd_open(struct inode *inode, struct file *file)
    +{
    + /* /dev/watchdog can only be opened once */
    + if (test_and_set_bit(0, &is_active))
    + return -EBUSY;
    +
    + /* Activate */
    + usb_pcwd_start(usb_pcwd_device);
    + usb_pcwd_keepalive(usb_pcwd_device);
    + return 0;
    +}
    +
    +static int usb_pcwd_release(struct inode *inode, struct file *file)
    +{
    + /*
    + * Shut off the timer.
    + */
    + if (expect_release == 42) {
    + usb_pcwd_stop(usb_pcwd_device);
    + } else {
    + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
    + usb_pcwd_keepalive(usb_pcwd_device);
    + }
    + clear_bit(0, &is_active);
    + expect_release = 0;
    + return 0;
    +}
    +
    +/*
    + * /dev/temperature handling
    + */
    +
    +static ssize_t usb_pcwd_temperature_read(struct file *file, char *data,
    + size_t len, loff_t *ppos)
    +{
    + int temperature;
    +
    + /* Can't seek (pwrite) on this device */
    + if (ppos != &file->f_pos)
    + return -ESPIPE;
    +
    + if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature))
    + return -EFAULT;
    +
    + if (copy_to_user (data, &temperature, 1))
    + return -EFAULT;
    +
    + return 1;
    +}
    +
    +static int usb_pcwd_temperature_open(struct inode *inode, struct file *file)
    +{
    + return 0;
    +}
    +
    +static int usb_pcwd_temperature_release(struct inode *inode, struct file *file)
    +{
    + return 0;
    +}
    +
    +/*
    + * Notify system
    + */
    +
    +static int usb_pcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
    +{
    + if (code==SYS_DOWN || code==SYS_HALT) {
    + /* Turn the WDT off */
    + usb_pcwd_stop(usb_pcwd_device);
    + }
    +
    + return NOTIFY_DONE;
    +}
    +
    +/*
    + * Kernel Interfaces
    + */
    +
    +static struct file_operations usb_pcwd_fops = {
    + .owner = THIS_MODULE,
    + .llseek = no_llseek,
    + .write = usb_pcwd_write,
    + .ioctl = usb_pcwd_ioctl,
    + .open = usb_pcwd_open,
    + .release = usb_pcwd_release,
    +};
    +
    +static struct miscdevice usb_pcwd_miscdev = {
    + .minor = WATCHDOG_MINOR,
    + .name = "watchdog",
    + .fops = &usb_pcwd_fops,
    +};
    +
    +static struct file_operations usb_pcwd_temperature_fops = {
    + .owner = THIS_MODULE,
    + .llseek = no_llseek,
    + .read = usb_pcwd_temperature_read,
    + .open = usb_pcwd_temperature_open,
    + .release = usb_pcwd_temperature_release,
    +};
    +
    +static struct miscdevice usb_pcwd_temperature_miscdev = {
    + .minor = TEMP_MINOR,
    + .name = "temperature",
    + .fops = &usb_pcwd_temperature_fops,
    +};
    +
    +static struct notifier_block usb_pcwd_notifier = {
    + .notifier_call = usb_pcwd_notify_sys,
    +};
    +
    +/**
    + * usb_pcwd_delete
    + */
    +static inline void usb_pcwd_delete (struct usb_pcwd_private *usb_pcwd)
    +{
    + if (usb_pcwd->intr_urb != NULL)
    + usb_free_urb (usb_pcwd->intr_urb);
    + if (usb_pcwd->intr_buffer != NULL)
    + usb_buffer_free(usb_pcwd->udev, usb_pcwd->intr_size,
    + usb_pcwd->intr_buffer, usb_pcwd->intr_dma);
    + kfree (usb_pcwd);
    +}
    +
    +/**
    + * usb_pcwd_probe
    + *
    + * Called by the usb core when a new device is connected that it thinks
    + * this driver might be interested in.
    + */
    +static int usb_pcwd_probe(struct usb_interface *interface, const struct usb_device_id *id)
    +{
    + struct usb_device *udev = interface_to_usbdev(interface);
    + struct usb_host_interface *iface_desc;
    + struct usb_endpoint_descriptor *endpoint;
    + struct usb_pcwd_private *usb_pcwd = NULL;
    + int pipe, maxp;
    + int retval = -ENOMEM;
    + int got_fw_rev;
    + unsigned char fw_rev_major, fw_rev_minor;
    + char fw_ver_str[20];
    + unsigned char option_switches, dummy;
    +
    + /* See if the device offered us matches what we can accept */
    + if ((udev->descriptor.idVendor != USB_PCWD_VENDOR_ID) ||
    + (udev->descriptor.idProduct != USB_PCWD_PRODUCT_ID)) {
    + return -ENODEV;
    + }
    +
    + cards_found++;
    + if (cards_found > 1) {
    + printk(KERN_ERR PFX "This driver only supports 1 device\n");
    + return -ENODEV;
    + }
    +
    + /* get the active interface descriptor */
    + iface_desc = &interface->altsetting[interface->act_altsetting];
    +
    + /* check out that we have a HID device */
    + if (!(iface_desc->desc.bInterfaceClass == USB_CLASS_HID)) {
    + printk(KERN_ERR PFX "The device isn't a Human Interface Device\n");
    + return -ENODEV;
    + }
    +
    + /* check out the endpoint: it has to be Interrupt & IN */
    + endpoint = &iface_desc->endpoint[0].desc;
    +
    + if (!((endpoint->bEndpointAddress & USB_DIR_IN) &&
    + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
    + == USB_ENDPOINT_XFER_INT))) {
    + /* we didn't find a Interrupt endpoint with direction IN */
    + printk(KERN_ERR PFX "Couldn't find an INTR & IN endpoint\n");
    + return -ENODEV;
    + }
    +
    + /* get a handle to the interrupt data pipe */
    + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
    + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
    +
    + /* allocate memory for our device and initialize it */
    + usb_pcwd = kmalloc (sizeof(struct usb_pcwd_private), GFP_KERNEL);
    + if (usb_pcwd == NULL) {
    + printk(KERN_ERR PFX "Out of memory\n");
    + goto error;
    + }
    + memset (usb_pcwd, 0x00, sizeof (*usb_pcwd));
    +
    + usb_pcwd_device = usb_pcwd;
    +
    + init_MUTEX (&usb_pcwd->sem);
    + usb_pcwd->udev = udev;
    + usb_pcwd->interface = interface;
    + usb_pcwd->interface_number = iface_desc->desc.bInterfaceNumber;
    + usb_pcwd->intr_size = (endpoint->wMaxPacketSize > 8 ? endpoint->wMaxPacketSize : 8);
    +
    + /* set up the memory buffer's */
    + if (!(usb_pcwd->intr_buffer = usb_buffer_alloc(udev, usb_pcwd->intr_size, SLAB_ATOMIC, &usb_pcwd->intr_dma))) {
    + printk(KERN_ERR PFX "Out of memory\n");
    + goto error;
    + }
    +
    + /* allocate the urb's */
    + usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!usb_pcwd->intr_urb) {
    + printk(KERN_ERR PFX "Out of memory\n");
    + goto error;
    + }
    +
    + /* initialise the intr urb's */
    + usb_fill_int_urb(usb_pcwd->intr_urb, udev, pipe,
    + usb_pcwd->intr_buffer, usb_pcwd->intr_size,
    + usb_pcwd_intr_done, usb_pcwd, endpoint->bInterval);
    + usb_pcwd->intr_urb->transfer_dma = usb_pcwd->intr_dma;
    + usb_pcwd->intr_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    +
    + /* register our interrupt URB with the USB system */
    + if (usb_submit_urb(usb_pcwd->intr_urb, GFP_KERNEL)) {
    + printk(KERN_ERR PFX "Problem registering interrupt URB\n");
    + retval = -EIO; /* failure */
    + goto error;
    + }
    +
    + /* The device exists and can be communicated with */
    + usb_pcwd->exists = 1;
    +
    + /* disable card */
    + usb_pcwd_stop(usb_pcwd);
    +
    + /* Get the Firmware Version */
    + got_fw_rev = usb_pcwd_send_command(usb_pcwd, CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor);
    + if (got_fw_rev) {
    + sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor);
    + } else {
    + sprintf(fw_ver_str, "<card no answer>");
    + }
    +
    + printk(KERN_INFO PFX "Found card (Firmware: %s) with temp option\n",
    + fw_ver_str);
    +
    + /* Get switch settings */
    + usb_pcwd_send_command(usb_pcwd, CMD_GET_DIP_SWITCH_SETTINGS, &dummy, &option_switches);
    +
    + printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
    + option_switches,
    + ((option_switches & 0x10) ? "ON" : "OFF"),
    + ((option_switches & 0x08) ? "ON" : "OFF"));
    +
    + /* Check that the heartbeat value is within it's range ; if not reset to the default */
    + if (heartbeat < 1 || heartbeat > 0xFFFF) {
    + heartbeat = WATCHDOG_HEARTBEAT;
    + printk(KERN_INFO PFX "heartbeat value must be 0<heartbeat<65536, using %d\n",
    + heartbeat);
    + }
    +
    + /* Calculate the watchdog's heartbeat */
    + usb_pcwd_set_heartbeat(usb_pcwd, heartbeat);
    +
    + retval = register_reboot_notifier(&usb_pcwd_notifier);
    + if (retval != 0) {
    + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
    + retval);
    + goto error;
    + }
    +
    + retval = misc_register(&usb_pcwd_miscdev);
    + if (retval != 0) {
    + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
    + WATCHDOG_MINOR, retval);
    + goto err_out_unregister_reboot;
    + }
    +
    + retval = misc_register(&usb_pcwd_temperature_miscdev);
    + if (retval != 0) {
    + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
    + TEMP_MINOR, retval);
    + goto err_out_misc_deregister;
    + }
    +
    + /* we can register the device now, as it is ready */
    + usb_set_intfdata (interface, usb_pcwd);
    +
    + printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
    + heartbeat, nowayout);
    +
    + return 0;
    +
    +err_out_misc_deregister:
    + misc_deregister(&usb_pcwd_miscdev);
    +err_out_unregister_reboot:
    + unregister_reboot_notifier(&usb_pcwd_notifier);
    +error:
    + usb_pcwd_delete (usb_pcwd);
    + usb_pcwd_device = NULL;
    + return retval;
    +}
    +
    +
    +/**
    + * usb_pcwd_disconnect
    + *
    + * Called by the usb core when the device is removed from the system.
    + *
    + * This routine guarantees that the driver will not submit any more urbs
    + * by clearing dev->udev.
    + */
    +static void usb_pcwd_disconnect(struct usb_interface *interface)
    +{
    + struct usb_pcwd_private *usb_pcwd;
    +
    + /* prevent races with open() */
    + down (&disconnect_sem);
    +
    + usb_pcwd = usb_get_intfdata (interface);
    + usb_set_intfdata (interface, NULL);
    +
    + down (&usb_pcwd->sem);
    +
    + /* Stop the timer before we leave */
    + if (!nowayout)
    + usb_pcwd_stop(usb_pcwd);
    +
    + /* We should now stop communicating with the USB PCWD device */
    + usb_pcwd->exists = 0;
    +
    + /* Deregister */
    + misc_deregister(&usb_pcwd_temperature_miscdev);
    + misc_deregister(&usb_pcwd_miscdev);
    + unregister_reboot_notifier(&usb_pcwd_notifier);
    +
    + up (&usb_pcwd->sem);
    +
    + /* Delete the USB PCWD device */
    + usb_pcwd_delete(usb_pcwd);
    +
    + cards_found--;
    +
    + up (&disconnect_sem);
    +
    + printk(KERN_INFO PFX "USB PC Watchdog disconnected\n");
    +}
    +
    +
    +
    +/**
    + * usb_pcwd_init
    + */
    +static int __init usb_pcwd_init(void)
    +{
    + int result;
    +
    + /* register this driver with the USB subsystem */
    + result = usb_register(&usb_pcwd_driver);
    + if (result) {
    + printk(KERN_ERR PFX "usb_register failed. Error number %d\n",
    + result);
    + return result;
    + }
    +
    + printk(KERN_INFO PFX DRIVER_DESC " " DRIVER_VERSION "\n");
    + return 0;
    +}
    +
    +
    +/**
    + * usb_pcwd_exit
    + */
    +static void __exit usb_pcwd_exit(void)
    +{
    + /* deregister this driver with the USB subsystem */
    + usb_deregister(&usb_pcwd_driver);
    +}
    +
    +
    +module_init (usb_pcwd_init);
    +module_exit (usb_pcwd_exit);
    diff -Nru a/drivers/char/watchdog/cpu5wdt.c b/drivers/char/watchdog/cpu5wdt.c
    --- a/drivers/char/watchdog/cpu5wdt.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/cpu5wdt.c Sun Feb 29 22:53:35 2004
    @@ -293,6 +293,7 @@
     MODULE_DESCRIPTION("sma cpu5 watchdog driver");
     MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
     
     MODULE_PARM(port, "i");
     MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
    diff -Nru a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c
    --- a/drivers/char/watchdog/eurotechwdt.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/eurotechwdt.c Sun Feb 29 22:53:35 2004
    @@ -503,3 +503,4 @@
     MODULE_AUTHOR("Rodolfo Giometti");
     MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/ib700wdt.c b/drivers/char/watchdog/ib700wdt.c
    --- a/drivers/char/watchdog/ib700wdt.c Sun Feb 29 22:53:36 2004
    +++ b/drivers/char/watchdog/ib700wdt.c Sun Feb 29 22:53:36 2004
    @@ -350,5 +350,6 @@
     MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
     MODULE_DESCRIPTION("IB700 SBC watchdog driver");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
     
     /* end of ib700wdt.c */
    diff -Nru a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c
    --- a/drivers/char/watchdog/machzwd.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/machzwd.c Sun Feb 29 22:53:35 2004
    @@ -98,6 +98,7 @@
     MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
     MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
     
     #ifdef CONFIG_WATCHDOG_NOWAYOUT
     static int nowayout = 1;
    diff -Nru a/drivers/char/watchdog/mixcomwd.c b/drivers/char/watchdog/mixcomwd.c
    --- a/drivers/char/watchdog/mixcomwd.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/mixcomwd.c Sun Feb 29 22:53:35 2004
    @@ -302,4 +302,7 @@
     module_init(mixcomwd_init);
     module_exit(mixcomwd_exit);
     
    +MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
    +MODULE_DESCRIPTION("MixCom Watchdog driver");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c
    --- a/drivers/char/watchdog/pcwd.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/pcwd.c Sun Feb 29 22:53:35 2004
    @@ -705,5 +705,8 @@
     module_init(pcwatchdog_init);
     module_exit(pcwatchdog_exit);
     
    +MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>");
    +MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
     MODULE_LICENSE("GPL");
    -
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    +MODULE_ALIAS_MISCDEV(TEMP_MINOR);
    diff -Nru a/drivers/char/watchdog/sa1100_wdt.c b/drivers/char/watchdog/sa1100_wdt.c
    --- a/drivers/char/watchdog/sa1100_wdt.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/sa1100_wdt.c Sun Feb 29 22:53:35 2004
    @@ -218,3 +218,4 @@
     MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
     
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/sc1200wdt.c b/drivers/char/watchdog/sc1200wdt.c
    --- a/drivers/char/watchdog/sc1200wdt.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/sc1200wdt.c Sun Feb 29 22:53:35 2004
    @@ -486,3 +486,4 @@
     MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
     MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/w83627hf_wdt.c b/drivers/char/watchdog/w83627hf_wdt.c
    --- a/drivers/char/watchdog/w83627hf_wdt.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/w83627hf_wdt.c Sun Feb 29 22:53:35 2004
    @@ -321,4 +321,4 @@
     MODULE_LICENSE("GPL");
     MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>");
     MODULE_DESCRIPTION("w38627hf WDT driver");
    -
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/w83877f_wdt.c b/drivers/char/watchdog/w83877f_wdt.c
    --- a/drivers/char/watchdog/w83877f_wdt.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/w83877f_wdt.c Sun Feb 29 22:53:35 2004
    @@ -427,3 +427,4 @@
     MODULE_AUTHOR("Scott and Bill Jennings");
     MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/wdt285.c b/drivers/char/watchdog/wdt285.c
    --- a/drivers/char/watchdog/wdt285.c Sun Feb 29 22:53:35 2004
    +++ b/drivers/char/watchdog/wdt285.c Sun Feb 29 22:53:35 2004
    @@ -222,6 +222,7 @@
     MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
     MODULE_DESCRIPTION("Footbridge watchdog driver");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
     
     module_param(soft_margin, int, 0);
     MODULE_PARM_DESC(soft_margin,"Watchdog timeout in seconds");
    diff -Nru a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c
    --- a/drivers/char/watchdog/wdt977.c Sun Feb 29 22:53:36 2004
    +++ b/drivers/char/watchdog/wdt977.c Sun Feb 29 22:53:36 2004
    @@ -365,5 +365,7 @@
     module_init(nwwatchdog_init);
     module_exit(nwwatchdog_exit);
     
    +MODULE_AUTHOR("Woody Suwalski <woody@netwinder.org>");
     MODULE_DESCRIPTION("W83977AF Watchdog driver");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    diff -Nru a/drivers/char/watchdog/wdt_pci.c b/drivers/char/watchdog/wdt_pci.c
    --- a/drivers/char/watchdog/wdt_pci.c Sun Feb 29 22:53:36 2004
    +++ b/drivers/char/watchdog/wdt_pci.c Sun Feb 29 22:53:36 2004
    @@ -653,3 +653,5 @@
     MODULE_AUTHOR("JP Nollmann, Alan Cox");
     MODULE_DESCRIPTION("Driver for the ICS PCI watchdog cards");
     MODULE_LICENSE("GPL");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    +MODULE_ALIAS_MISCDEV(TEMP_MINOR);
    diff -Nru a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
    --- a/arch/sparc64/Kconfig Sun Feb 29 22:53:58 2004
    +++ b/arch/sparc64/Kconfig Sun Feb 29 22:53:58 2004
    @@ -450,28 +450,6 @@
               another UltraSPARC-IIi-cEngine boardset with a 7-segment display,
               you should say N to this option.
     
    -config WATCHDOG_CP1XXX
    - tristate "CP1XXX Hardware Watchdog support"
    - depends on PCI
    - ---help---
    - This is the driver for the hardware watchdog timers present on
    - Sun Microsystems CompactPCI models CP1400 and CP1500.
    -
    - To compile this driver as a module, choose M here: the
    - module will be called cpwatchdog.
    -
    - If you do not have a CompactPCI model CP1400 or CP1500, or
    - another UltraSPARC-IIi-cEngine boardset with hardware watchdog,
    - you should say N to this option.
    -
    -config WATCHDOG_RIO
    - tristate "RIO Hardware Watchdog support"
    - depends on PCI
    - help
    - Say Y here to support the hardware watchdog capability on Sun RIO
    - machines. The watchdog timeout period is normally one minute but
    - can be changed with a boot-time parameter.
    -
     config CMDLINE_BOOL
             bool "Default bootloader kernel arguments"
     
    diff -Nru a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
    --- a/drivers/char/watchdog/Kconfig Sun Feb 29 22:53:58 2004
    +++ b/drivers/char/watchdog/Kconfig Sun Feb 29 22:53:58 2004
    @@ -339,6 +339,30 @@
               To compile this driver as a module, choose M here: the
               module will be called shwdt.
     
    +# SPARC64 Architecture
    +
    +config WATCHDOG_CP1XXX
    + tristate "CP1XXX Hardware Watchdog support"
    + depends on WATCHDOG && SPARC64 && PCI
    + ---help---
    + This is the driver for the hardware watchdog timers present on
    + Sun Microsystems CompactPCI models CP1400 and CP1500.
    +
    + To compile this driver as a module, choose M here: the
    + module will be called cpwatchdog.
    +
    + If you do not have a CompactPCI model CP1400 or CP1500, or
    + another UltraSPARC-IIi-cEngine boardset with hardware watchdog,
    + you should say N to this option.
    +
    +config WATCHDOG_RIO
    + tristate "RIO Hardware Watchdog support"
    + depends on WATCHDOG && SPARC64 && PCI
    + help
    + Say Y here to support the hardware watchdog capability on Sun RIO
    + machines. The watchdog timeout period is normally one minute but
    + can be changed with a boot-time parameter.
    +
     #
     # ISA-based Watchdog Cards
     #
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Andrew Morton: "2.6.4-rc1-mm1"

    Relevant Pages

    • [WATCHDOG] linux-2.6-watchdog patches (5 new drivers + some small changes)
      ... The watchdog timer is from a Winbond's SuperIO ... I've made this driver based on two others already on the kernel tree, ... +static int nowayout = WATCHDOG_NOWAYOUT; ... +static void esb_timer_start ...
      (Linux-Kernel)
    • [WATCHDOG] v2.6.26-rc3 patches
      ... On the watchdog side we still have 2 drivers from Florian Fainelli ... After that we have the at91sam* driver, ... +static void booke_wdt_ping ... +static int safe_close; ...
      (Linux-Kernel)
    • [PATCH] 2.6.0-test3 - Watchdog patches (sbc60xxwdt.c and advantechwdt.c)
      ... [WATCHDOG] ... changed watchdog_info to correctly reflect what the driver offers ... * Usually a userspace daemon will notify the kernel WDT driver ... static int fop_open ...
      (Linux-Kernel)
    • WATCHDOG] v2.6.23 patches 2
      ... This fix makes sure that we compile the watchdog drivers after ... Add 631xESB/632xESB support to the iTCO_wdt driver. ... +# ALPHA Architecture ... +static int bfin_wdt_keepalive ...
      (Linux-Kernel)
    • [WATCHDOG] v2.6.11-mm watchdog patches
      ... correct sysfs name for watchdog devices ... udev may create the /dev/watchdog node if the driver is loaded. ... +static int mv64x60_wdt_timeout; ... +static void mv64x60_wdt_reg_write ...
      (Linux-Kernel)