📄 pcwd_usb.c
字号:
/* * 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 "1.01"#define DRIVER_DATE "15 Mar 2005"#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) ")");static int nowayout = WATCHDOG_NOWAYOUT;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), USB_COMMAND_TIMEOUT) != 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 __user *data, size_t len, loff_t *ppos){ /* 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){ void __user *argp = (void __user *)arg; int __user *p = argp; 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(argp, &ident, sizeof (ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_GETTEMP: { int temperature; if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature)) return -EFAULT; return put_user(temperature, p); } case WDIOC_KEEPALIVE: usb_pcwd_keepalive(usb_pcwd_device); return 0; case WDIOC_SETOPTIONS: { int new_options, retval = -EINVAL; if (get_user (new_options, p)) return -EFAULT; if (new_options & WDIOS_DISABLECARD) { usb_pcwd_stop(usb_pcwd_device);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -