patch-2.3.17 linux/drivers/usb/hp_scanner.c
Next file: linux/drivers/usb/inits.h
Previous file: linux/drivers/usb/ezusb.h
Back to the patch index
Back to the overall index
- Lines: 345
- Date:
Wed Sep 1 15:31:47 1999
- Orig file:
v2.3.16/linux/drivers/usb/hp_scanner.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.16/linux/drivers/usb/hp_scanner.c linux/drivers/usb/hp_scanner.c
@@ -0,0 +1,344 @@
+/* -*- linux-c -*- */
+
+/*
+ * Driver for USB HP Scanners
+ *
+ * David E. Nelson (dnelson@jump.net)
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
+ *
+ * History
+ * 0.1 8/31/1999
+ *
+ * Developed/tested using linux-2.3.15 with minor ohci.c changes to
+ * support short packes during bulk xfer mode. Some testing was
+ * done with ohci-hcd but the performace was low. Very limited
+ * testing was performed with uhci but I was unable to get it to
+ * work. Initial relase to the linux-usb development effort.
+ *
+ * */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+/* stall/wait timeout for scanner */
+#define NAK_TIMEOUT (HZ)
+
+/* For some reason, an IBUF_SIZE of 8192 causes REALLY big problems
+ * with linux-2.3.15. Anything more than 4k seems to not have an
+ * effect on increasing performance. Anything smaller than 4k hurts
+ * it. */
+#define IBUF_SIZE 4096
+
+/* This is a scanner, so not much data is sent to it. The largest
+ * stuff may be some kind of maps and stuff but that's kinda rare. */
+#define OBUF_SIZE 128
+
+#define USB_SCANNER_MAJOR 16
+
+struct hpscan_usb_data {
+ struct usb_device *hpscan_dev; /* init: probe_scanner */
+ __u8 isopen; /* nz if open */
+
+ __u8 present; /* Device is present on the bus */
+ char *obuf; /* transfer buffers */
+ char *ibuf;
+ wait_queue_head_t wait_q; /* for timeouts */
+};
+
+static struct hpscan_usb_data hpscan;
+
+static int
+open_scanner(struct inode * inode, struct file * file)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ if (hps->isopen) {
+ return -EBUSY;
+ }
+ hps->isopen = 1;
+
+ init_waitqueue_head(&hps->wait_q);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+close_scanner(struct inode * inode, struct file * file)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ hps->isopen = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static ssize_t
+write_scanner(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned long partial;
+
+ int result = 0;
+ int maxretry;
+
+ do {
+ unsigned long thistime;
+ char *obuf = hps->obuf;
+
+ thistime = copy_size = (count > OBUF_SIZE) ? OBUF_SIZE : count;
+ if (copy_from_user(hps->obuf, buffer, copy_size))
+ return -EFAULT;
+ maxretry = 5;
+ while (thistime) {
+ if (!hps->hpscan_dev)
+ return -ENODEV;
+ if (signal_pending(current)) {
+ return bytes_written ? bytes_written : -EINTR;
+ }
+
+ result = hps->hpscan_dev->bus->op->bulk_msg(hps->hpscan_dev,usb_sndbulkpipe(hps->hpscan_dev, 2), obuf, thistime, &partial);
+
+ //printk(KERN_DEBUG "write stats: result:%d thistime:%lu partial:%lu\n", result, thistime, partial);
+
+ if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
+ if(!maxretry--) {
+ return -ETIME;
+ }
+ interruptible_sleep_on_timeout(&hps->wait_q, NAK_TIMEOUT);
+ continue;
+ } else if (!result & partial) {
+ obuf += partial;
+ thistime -= partial;
+ } else
+ break;
+ };
+ if (result) {
+ printk("Write Whoops - %x\n", result);
+ return -EIO;
+ }
+ bytes_written += copy_size;
+ count -= copy_size;
+ buffer += copy_size;
+ } while ( count > 0 );
+
+ return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t
+read_scanner(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ ssize_t read_count;
+
+ unsigned long partial;
+
+ int this_read;
+ int result;
+
+/* Wait for the scanner to get it's act together. This may involve
+ * resetting the head, warming up the lamp, etc. maxretry is number
+ * of seconds. */
+ int maxretry = 30;
+
+ char *ibuf = hps->ibuf;
+
+ read_count = 0;
+
+ while (count) {
+ if (signal_pending(current)) {
+ return read_count ? read_count : -EINTR;
+ }
+ if (!hps->hpscan_dev)
+ return -ENODEV;
+ this_read = (count > IBUF_SIZE) ? IBUF_SIZE : count;
+
+ result = hps->hpscan_dev->bus->op->bulk_msg(hps->hpscan_dev, usb_rcvbulkpipe(hps->hpscan_dev, 1), ibuf, this_read, &partial);
+
+ printk(KERN_DEBUG "read stats: result:%d this_read:%u partial:%lu\n", result, this_read, partial);
+
+ if (partial) {
+ count = this_read = partial;
+ } else if (result == USB_ST_TIMEOUT || result == 15) {
+ if(!maxretry--) {
+ printk(KERN_DEBUG "read_scanner: maxretry timeout\n");
+ return -ETIME;
+ }
+ interruptible_sleep_on_timeout(&hps->wait_q, NAK_TIMEOUT);
+ continue;
+ } else if (result != USB_ST_DATAUNDERRUN) {
+ printk("Read Whoops - result:%u partial:%lu this_read:%u\n", result, partial, this_read);
+ return -EIO;
+ } else {
+ return (0);
+ }
+
+ if (this_read) {
+ if (copy_to_user(buffer, ibuf, this_read))
+ return -EFAULT;
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ return read_count;
+}
+
+static int
+probe_scanner(struct usb_device *dev)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ /*
+ * Don't bother using an HP 4200C since it does NOT understand
+ * SCL and HP isn't going to be releasing the specs any time
+ * soon. */
+ if (dev->descriptor.idVendor != 0x3f0 ) {
+ printk(KERN_INFO "Scanner is not an HP Scanner.\n");
+ return -1;
+ }
+
+ if (dev->descriptor.idProduct != 0x101 && /* HP 4100C */
+ dev->descriptor.idProduct != 0x202 && /* HP 5100C */
+ dev->descriptor.idProduct != 0x601) { /* HP 6300C */
+ printk(KERN_INFO "Scanner model not supported/tested.\n");
+ return -1;
+ }
+
+ printk(KERN_DEBUG "USB Scanner found at address %d\n", dev->devnum);
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk(KERN_DEBUG "Failed to set configuration\n");
+ return -1;
+ }
+
+ hps->present = 1;
+ hps->hpscan_dev = dev;
+
+ if (!(hps->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (!(hps->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void
+disconnect_scanner(struct usb_device *dev)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ if (hps->isopen) {
+ /* better let it finish - the release will do whats needed */
+ hps->hpscan_dev = NULL;
+ return;
+ }
+ kfree(hps->ibuf);
+ kfree(hps->obuf);
+
+ dev->private = NULL; /* just in case */
+ hps->present = 0;
+}
+
+static struct
+usb_driver scanner_driver = {
+ "usbscanner",
+ probe_scanner,
+ disconnect_scanner,
+ { NULL, NULL }
+};
+
+static struct
+file_operations usb_scanner_fops = {
+ NULL, /* seek */
+ read_scanner,
+ write_scanner,
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_scanner,
+ NULL, /* flush */
+ close_scanner,
+ NULL,
+ NULL, /* fasync */
+};
+
+int
+usb_hp_scanner_init(void)
+{
+ int result;
+
+ if ((result = register_chrdev(USB_SCANNER_MAJOR, "usbscanner", &usb_scanner_fops)) < 0) {
+ printk(KERN_WARNING "hp_scanner: Cannot register device\n");
+ return result;
+ }
+ usb_register(&scanner_driver);
+ printk(KERN_DEBUG "USB Scanner support registered.\n");
+ return 0;
+}
+
+
+void
+usb_hp_scanner_cleanup(void)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ hps->present = 0;
+ usb_deregister(&scanner_driver);
+ unregister_chrdev(USB_SCANNER_MAJOR, "usbscanner");
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ return usb_hp_scanner_init();
+}
+
+void
+cleanup_module(void)
+{
+ usb_hp_scanner_cleanup();
+}
+#endif
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)