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

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)