--- /dev/null
+obj-$(CONFIG_USB_CATC) += crc32.o
--- /dev/null
+#
+# USB Imageing devices configuration
+#
+comment 'USB Imaging devices'
+dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
+dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL
+dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
+dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI
+dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL
+
+# Turn on CONFIG_USB_IMAGE if any of the drivers are compiled into the kernel to
+# make our Makefile logic a bit simpler
+if [ "$CONFIG_USB_DC2XX" = "y" -o "$CONFIG_USB_MDC800" = "y" ]; then
+ define_bool CONFIG_USB_IMAGE y
+fi
+if [ "$CONFIG_USB_SCANNER" = "y" -o "$CONFIG_USB_MICROTEK" = "y" -o "$CONFIG_USB_HPUSBSCSI" = "y" ]; then
+ define_bool CONFIG_USB_IMAGE y
+fi
+
--- /dev/null
+#
+# Makefile for USB Image drivers
+#
+
+O_TARGET := usb-image.o
+
+obj-$(CONFIG_USB_DC2XX) += dc2xx.o
+obj-$(CONFIG_USB_MDC800) += mdc800.o
+obj-$(CONFIG_USB_HPUSBSCSI) += hpusbscsi.o
+obj-$(CONFIG_USB_MICROTEK) += microtek.o
+obj-$(CONFIG_USB_SCANNER) += scanner.o
+
+include $(TOPDIR)/Rules.make
--- /dev/null
+/*
+ * Copyright (C) 1999-2000 by David Brownell <dbrownell@users.sourceforge.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.
+ */
+
+
+/*
+ * USB driver for Kodak DC-2XX series digital still cameras
+ *
+ * The protocol here is the same as the one going over a serial line, but
+ * it uses USB for speed. Set up /dev/kodak, get gphoto (www.gphoto.org),
+ * and have fun!
+ *
+ * This should also work for a number of other digital (non-Kodak) cameras,
+ * by adding the vendor and product IDs to the table below. They'll need
+ * to be the sort using USB just as a fast bulk data channel.
+ */
+
+/*
+ * HISTORY
+ *
+ * 26 August, 1999 -- first release (0.1), works with my DC-240.
+ * The DC-280 (2Mpixel) should also work, but isn't tested.
+ * If you use gphoto, make sure you have the USB updates.
+ * Lives in a 2.3.14 or so Linux kernel, in drivers/usb.
+ * 31 August, 1999 -- minor update to recognize DC-260 and handle
+ * its endpoints being in a different order. Note that as
+ * of gPhoto 0.36pre, the USB updates are integrated.
+ * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0);
+ * added timeouts to bulk_msg calls. Minor updates, docs.
+ * 03 Nov, 1999 -- update for 2.3.25 kernel API changes.
+ * 08 Jan, 2000 .. multiple camera support
+ * 12 Aug, 2000 .. add some real locking, remove an Oops
+ * 10 Oct, 2000 .. usb_device_id table created.
+ * 01 Nov, 2000 .. usb_device_id support added by Adam J. Richter
+ * 08 Apr, 2001 .. Identify version on module load. gb
+ *
+ * Thanks to: the folk who've provided USB product IDs, sent in
+ * patches, and shared their successes!
+ */
+
+#include <linux/config.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/slab.h>
+#include <linux/module.h>
+#include <linux/devfs_fs_kernel.h>
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+
+/* /dev/usb dir. */
+extern devfs_handle_t usb_devfs_handle;
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0.0"
+#define DRIVER_AUTHOR "David Brownell, <dbrownell@users.sourceforge.net>"
+#define DRIVER_DESC "USB Camera Driver for Kodak DC-2xx series cameras"
+
+
+/* current USB framework handles max of 16 USB devices per driver */
+#define MAX_CAMERAS 16
+
+/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */
+#define USB_CAMERA_MINOR_BASE 80
+
+
+// XXX remove packet size limit, now that bulk transfers seem fixed
+
+/* Application protocol limit is 0x8002; USB has disliked that limit! */
+#define MAX_PACKET_SIZE 0x2000 /* e.g. image downloading */
+
+#define MAX_READ_RETRY 5 /* times to retry reads */
+#define MAX_WRITE_RETRY 5 /* times to retry writes */
+#define RETRY_TIMEOUT (HZ) /* sleep between retries */
+
+
+/* table of cameras that work through this driver */
+static struct usb_device_id camera_table [] = {
+ /* These have the same application level protocol */
+ { USB_DEVICE(0x040a, 0x0120) }, // Kodak DC-240
+ { USB_DEVICE(0x040a, 0x0130) }, // Kodak DC-280
+ { USB_DEVICE(0x040a, 0x0131) }, // Kodak DC-5000
+ { USB_DEVICE(0x040a, 0x0132) }, // Kodak DC-3400
+
+ /* These have a different application level protocol which
+ * is part of the Flashpoint "DigitaOS". That supports some
+ * non-camera devices, and some non-Kodak cameras.
+ * Use this driver to get USB and "OpenDis" to talk.
+ */
+ { USB_DEVICE(0x040a, 0x0100) }, // Kodak DC-220
+ { USB_DEVICE(0x040a, 0x0110) }, // Kodak DC-260
+ { USB_DEVICE(0x040a, 0x0111) }, // Kodak DC-265
+ { USB_DEVICE(0x040a, 0x0112) }, // Kodak DC-290
+ { USB_DEVICE(0xf003, 0x6002) }, // HP PhotoSmart C500
+ { USB_DEVICE(0x03f0, 0x4102) }, // HP PhotoSmart C618
+ { USB_DEVICE(0x0a17, 0x1001) }, // Pentax EI-200
+
+ /* Other USB devices may well work here too, so long as they
+ * just stick to half duplex bulk packet exchanges. That
+ * means, among other things, no iso or interrupt endpoints.
+ */
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, camera_table);
+
+
+struct camera_state {
+ struct usb_device *dev; /* USB device handle */
+ int inEP; /* read endpoint */
+ int outEP; /* write endpoint */
+ const struct usb_device_id *info; /* DC-240, etc */
+ int subminor; /* which minor dev #? */
+ struct semaphore sem; /* locks this struct */
+
+ /* this is non-null iff the device is open */
+ char *buf; /* buffer for I/O */
+
+ devfs_handle_t devfs; /* devfs device */
+
+ /* always valid */
+ wait_queue_head_t wait; /* for timed waits */
+};
+
+/* Support multiple cameras, possibly of different types. */
+static struct camera_state *minor_data [MAX_CAMERAS];
+
+/* make this an rwlock if contention becomes an issue */
+static DECLARE_MUTEX (state_table_mutex);
+
+static ssize_t camera_read (struct file *file,
+ char *buf, size_t len, loff_t *ppos)
+{
+ struct camera_state *camera;
+ int retries;
+ int retval = 0;
+
+ if (len > MAX_PACKET_SIZE)
+ return -EINVAL;
+
+ camera = (struct camera_state *) file->private_data;
+ down (&camera->sem);
+ if (!camera->dev) {
+ up (&camera->sem);
+ return -ENODEV;
+ }
+
+ /* Big reads are common, for image downloading. Smaller ones
+ * are also common (even "directory listing" commands don't
+ * send very much data). We preserve packet boundaries here,
+ * they matter in the application protocol.
+ */
+ for (retries = 0; retries < MAX_READ_RETRY; retries++) {
+ int count;
+
+ if (signal_pending (current)) {
+ retval = -EINTR;
+ break;
+ }
+
+ retval = usb_bulk_msg (camera->dev,
+ usb_rcvbulkpipe (camera->dev, camera->inEP),
+ camera->buf, len, &count, HZ*10);
+
+ dbg ("read (%Zd) - 0x%x %d", len, retval, count);
+
+ if (!retval) {
+ if (copy_to_user (buf, camera->buf, count))
+ retval = -EFAULT;
+ else
+ retval = count;
+ break;
+ }
+ if (retval != -ETIMEDOUT)
+ break;
+ interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT);
+
+ dbg ("read (%Zd) - retry", len);
+ }
+ up (&camera->sem);
+ return retval;
+}
+
+static ssize_t camera_write (struct file *file,
+ const char *buf, size_t len, loff_t *ppos)
+{
+ struct camera_state *camera;
+ ssize_t bytes_written = 0;
+
+ if (len > MAX_PACKET_SIZE)
+ return -EINVAL;
+
+ camera = (struct camera_state *) file->private_data;
+ down (&camera->sem);
+ if (!camera->dev) {
+ up (&camera->sem);
+ return -ENODEV;
+ }
+
+ /* most writes will be small: simple commands, sometimes with
+ * parameters. putting images (like borders) into the camera
+ * would be the main use of big writes.
+ */
+ while (len > 0) {
+ char *obuf = camera->buf;
+ int maxretry = MAX_WRITE_RETRY;
+ unsigned long copy_size, thistime;
+
+ /* it's not clear that retrying can do any good ... or that
+ * fragmenting application packets into N writes is correct.
+ */
+ thistime = copy_size = len;
+ if (copy_from_user (obuf, buf, copy_size)) {
+ bytes_written = -EFAULT;
+ break;
+ }
+ while (thistime) {
+ int result;
+ int count;
+
+ if (signal_pending (current)) {
+ if (!bytes_written)
+ bytes_written = -EINTR;
+ goto done;
+ }
+
+ result = usb_bulk_msg (camera->dev,
+ usb_sndbulkpipe (camera->dev, camera->outEP),
+ obuf, thistime, &count, HZ*10);
+
+ if (result)
+ dbg ("write USB err - %d", result);
+
+ if (count) {
+ obuf += count;
+ thistime -= count;
+ maxretry = MAX_WRITE_RETRY;
+ continue;
+ } else if (!result)
+ break;
+
+ if (result == -ETIMEDOUT) { /* NAK - delay a bit */
+ if (!maxretry--) {
+ if (!bytes_written)
+ bytes_written = -ETIME;
+ goto done;
+ }
+ interruptible_sleep_on_timeout (&camera->wait,
+ RETRY_TIMEOUT);
+ continue;
+ }
+ if (!bytes_written)
+ bytes_written = -EIO;
+ goto done;
+ }
+ bytes_written += copy_size;
+ len -= copy_size;
+ buf += copy_size;
+ }
+done:
+ up (&camera->sem);
+ dbg ("wrote %Zd", bytes_written);
+ return bytes_written;
+}
+
+static int camera_open (struct inode *inode, struct file *file)
+{
+ struct camera_state *camera = NULL;
+ int subminor;
+ int value = 0;
+
+ down (&state_table_mutex);
+ subminor = minor (inode->i_rdev) - USB_CAMERA_MINOR_BASE;
+ if (subminor < 0 || subminor >= MAX_CAMERAS
+ || !(camera = minor_data [subminor])) {
+ up (&state_table_mutex);
+ return -ENODEV;
+ }
+ down (&camera->sem);
+ up (&state_table_mutex);
+
+ if (camera->buf) {
+ value = -EBUSY;
+ goto done;
+ }
+
+ if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) {
+ value = -ENOMEM;
+ goto done;
+ }
+
+ dbg ("open #%d", subminor);
+
+ file->private_data = camera;
+done:
+ up (&camera->sem);
+ return value;
+}
+
+static int camera_release (struct inode *inode, struct file *file)
+{
+ struct camera_state *camera;
+ int subminor;
+
+ camera = (struct camera_state *) file->private_data;
+ down (&state_table_mutex);
+ down (&camera->sem);
+
+ if (camera->buf) {
+ kfree (camera->buf);
+ camera->buf = 0;
+ }
+ subminor = camera->subminor;
+
+ /* If camera was unplugged with open file ... */
+ if (!camera->dev) {
+ minor_data [subminor] = NULL;
+ kfree (camera);
+ } else
+ up (&camera->sem);
+
+ up (&state_table_mutex);
+
+ dbg ("close #%d", subminor);
+
+ return 0;
+}
+
+ /* XXX should define some ioctls to expose camera type
+ * to applications ... what USB exposes should suffice.
+ * apps should be able to see the camera type.
+ */
+static /* const */ struct file_operations usb_camera_fops = {
+ /* Uses GCC initializer extension; simpler to maintain */
+ owner: THIS_MODULE,
+ read: camera_read,
+ write: camera_write,
+ open: camera_open,
+ release: camera_release,
+};
+
+
+
+static void *
+camera_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *camera_info)
+{
+ int i;
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ int direction, ep;
+ char name[8];
+ struct camera_state *camera = NULL;
+
+
+ /* these have one config, one interface */
+ if (dev->descriptor.bNumConfigurations != 1
+ || dev->config[0].bNumInterfaces != 1) {
+ dbg ("Bogus camera config info");
+ return NULL;
+ }
+
+ /* models differ in how they report themselves */
+ interface = &dev->actconfig->interface[ifnum].altsetting[0];
+ if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE
+ && interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+ || interface->bInterfaceSubClass != 0
+ || interface->bInterfaceProtocol != 0
+ || interface->bNumEndpoints != 2
+ ) {
+ dbg ("Bogus camera interface info");
+ return NULL;
+ }
+
+
+ /* select "subminor" number (part of a minor number) */
+ down (&state_table_mutex);
+ for (i = 0; i < MAX_CAMERAS; i++) {
+ if (!minor_data [i])
+ break;
+ }
+ if (i >= MAX_CAMERAS) {
+ info ("Ignoring additional USB Camera");
+ goto bye;
+ }
+
+ /* allocate & init camera state */
+ camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL);
+ if (!camera) {
+ err ("no memory!");
+ goto bye;
+ }
+
+ init_MUTEX (&camera->sem);
+ camera->info = camera_info;
+ camera->subminor = i;
+ camera->buf = NULL;
+ init_waitqueue_head (&camera->wait);
+
+
+ /* get input and output endpoints (either order) */
+ endpoint = interface->endpoint;
+ camera->outEP = camera->inEP = -1;
+
+ ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ if (direction == USB_DIR_IN)
+ camera->inEP = ep;
+ else
+ camera->outEP = ep;
+
+ ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ if (direction == USB_DIR_IN)
+ camera->inEP = ep;
+ else
+ camera->outEP = ep;
+
+ if (camera->outEP == -1 || camera->inEP == -1
+ || endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK
+ || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK
+ ) {
+ dbg ("Bogus endpoints");
+ goto error;
+ }
+
+ info ("USB Camera #%d connected, major/minor %d/%d", camera->subminor,
+ USB_MAJOR, USB_CAMERA_MINOR_BASE + camera->subminor);
+
+ camera->dev = dev;
+ usb_inc_dev_use (dev);
+
+ /* If we have devfs, register the device */
+ sprintf(name, "dc2xx%d", camera->subminor);
+ camera->devfs = devfs_register(usb_devfs_handle, name,
+ DEVFS_FL_DEFAULT, USB_MAJOR,
+ USB_CAMERA_MINOR_BASE + camera->subminor,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP, &usb_camera_fops, NULL);
+
+ goto bye;
+
+error:
+ minor_data [camera->subminor] = NULL;
+ kfree (camera);
+ camera = NULL;
+bye:
+ up (&state_table_mutex);
+ return camera;
+}
+
+static void camera_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct camera_state *camera = (struct camera_state *) ptr;
+ int subminor = camera->subminor;
+
+ down (&state_table_mutex);
+ down (&camera->sem);
+
+ devfs_unregister(camera->devfs);
+
+ /* If camera's not opened, we can clean up right away.
+ * Else apps see a disconnect on next I/O; the release cleans.
+ */
+ if (!camera->buf) {
+ minor_data [subminor] = NULL;
+ kfree (camera);
+ camera = NULL;
+ } else
+ camera->dev = NULL;
+
+ info ("USB Camera #%d disconnected", subminor);
+ usb_dec_dev_use (dev);
+
+ if (camera != NULL)
+ up (&camera->sem);
+ up (&state_table_mutex);
+}
+
+static /* const */ struct usb_driver camera_driver = {
+ name: "dc2xx",
+
+ id_table: camera_table,
+ probe: camera_probe,
+ disconnect: camera_disconnect,
+
+ fops: &usb_camera_fops,
+ minor: USB_CAMERA_MINOR_BASE
+};
+
+
+int __init usb_dc2xx_init(void)
+{
+ if (usb_register (&camera_driver) < 0)
+ return -1;
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return 0;
+}
+
+void __exit usb_dc2xx_cleanup(void)
+{
+ usb_deregister (&camera_driver);
+}
+
+module_init (usb_dc2xx_init);
+module_exit (usb_dc2xx_cleanup);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
--- /dev/null
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "../../scsi/sd.h"
+
+#include "hpusbscsi.h"
+
+#define DEBUG(x...) \
+ printk( KERN_DEBUG x )
+
+static char *states[]={"FREE", "BEGINNING", "WORKING", "ERROR", "WAIT", "PREMATURE"};
+
+#define TRACE_STATE printk(KERN_DEBUG"hpusbscsi->state = %s at line %d\n", states[hpusbscsi->state], __LINE__)
+
+/* global variables */
+
+struct list_head hpusbscsi_devices;
+//LIST_HEAD(hpusbscsi_devices);
+
+/* USB related parts */
+
+static void *
+hpusbscsi_usb_probe (struct usb_device *dev, unsigned int interface,
+ const struct usb_device_id *id)
+{
+ struct hpusbscsi *new;
+ struct usb_interface_descriptor *altsetting =
+ &(dev->actconfig->interface[interface].altsetting[0]);
+
+ int i, result;
+
+ /* basic check */
+
+ if (altsetting->bNumEndpoints != 3) {
+ printk (KERN_ERR "Wrong number of endpoints\n");
+ return NULL;
+ }
+
+ /* descriptor allocation */
+
+ new =
+ (struct hpusbscsi *) kmalloc (sizeof (struct hpusbscsi),
+ GFP_KERNEL);
+ if (new == NULL)
+ return NULL;
+ DEBUG ("Allocated memory\n");
+ memset (new, 0, sizeof (struct hpusbscsi));
+ new->dataurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!new->dataurb) {
+ kfree (new);
+ return NULL;
+ }
+ new->controlurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!new->controlurb) {
+ usb_free_urb (new->dataurb);
+ kfree (new);
+ return NULL;
+ }
+ new->dev = dev;
+ init_waitqueue_head (&new->pending);
+ init_waitqueue_head (&new->deathrow);
+ INIT_LIST_HEAD (&new->lh);
+
+
+
+ /* finding endpoints */
+
+ for (i = 0; i < altsetting->bNumEndpoints; i++) {
+ if (
+ (altsetting->endpoint[i].
+ bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK) {
+ if (altsetting->endpoint[i].
+ bEndpointAddress & USB_DIR_IN) {
+ new->ep_in =
+ altsetting->endpoint[i].
+ bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ } else {
+ new->ep_out =
+ altsetting->endpoint[i].
+ bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ } else {
+ new->ep_int =
+ altsetting->endpoint[i].
+ bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ new->interrupt_interval= altsetting->endpoint[i].bInterval;
+ }
+ }
+
+ /* USB initialisation magic for the simple case */
+
+ result = usb_set_interface (dev, altsetting->bInterfaceNumber, 0);
+
+ switch (result) {
+ case 0: /* no error */
+ break;
+
+ case -EPIPE:
+ usb_clear_halt (dev, usb_sndctrlpipe (dev, 0));
+ break;
+
+ default:
+ printk (KERN_ERR "unknown error %d from usb_set_interface\n",
+ result);
+ goto err_out;
+ }
+
+ /* making a template for the scsi layer to fake detection of a scsi device */
+
+ memcpy (&(new->ctempl), &hpusbscsi_scsi_host_template,
+ sizeof (hpusbscsi_scsi_host_template));
+ (struct hpusbscsi *) new->ctempl.proc_dir = new;
+ new->ctempl.module = THIS_MODULE;
+
+ if (scsi_register_host(&new->ctempl))
+ goto err_out;
+
+ new->sense_command[0] = REQUEST_SENSE;
+ new->sense_command[4] = HPUSBSCSI_SENSE_LENGTH;
+
+ /* adding to list for module unload */
+ list_add (&hpusbscsi_devices, &new->lh);
+
+ return new;
+
+ err_out:
+ usb_free_urb (new->controlurb);
+ usb_free_urb (new->dataurb);
+ kfree (new);
+ return NULL;
+}
+
+static void
+hpusbscsi_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+ usb_unlink_urb((((struct hpusbscsi *) ptr)->controlurb));
+ ((struct hpusbscsi *) ptr)->dev = NULL;
+}
+
+static struct usb_device_id hpusbscsi_usb_ids[] = {
+ {USB_DEVICE (0x03f0, 0x0701)}, /* HP 53xx */
+ {USB_DEVICE (0x03f0, 0x0801)}, /* HP 7400 */
+ {USB_DEVICE (0x0638, 0x026a)}, /*Scan Dual II */
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hpusbscsi_usb_ids);
+MODULE_LICENSE("GPL");
+
+
+static struct usb_driver hpusbscsi_usb_driver = {
+ name:"hpusbscsi",
+ probe:hpusbscsi_usb_probe,
+ disconnect:hpusbscsi_usb_disconnect,
+ id_table:hpusbscsi_usb_ids,
+};
+
+/* module initialisation */
+
+int __init
+hpusbscsi_init (void)
+{
+ int result;
+
+ INIT_LIST_HEAD (&hpusbscsi_devices);
+ DEBUG ("Driver loaded\n");
+
+ if ((result = usb_register (&hpusbscsi_usb_driver)) < 0) {
+ printk (KERN_ERR "hpusbscsi: driver registration failed\n");
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+void __exit
+hpusbscsi_exit (void)
+{
+ struct list_head *tmp;
+ struct list_head *old;
+ struct hpusbscsi * o;
+
+ for (tmp = hpusbscsi_devices.next; tmp != &hpusbscsi_devices;/*nothing */) {
+ old = tmp;
+ tmp = tmp->next;
+ o = (struct hpusbscsi *)old;
+ usb_unlink_urb(o->controlurb);
+ scsi_unregister_host(&o->ctempl);
+ usb_free_urb(o->controlurb);
+ usb_free_urb(o->dataurb);
+ kfree(old);
+ }
+
+ usb_deregister (&hpusbscsi_usb_driver);
+}
+
+module_init (hpusbscsi_init);
+module_exit (hpusbscsi_exit);
+
+/* interface to the scsi layer */
+
+static int
+hpusbscsi_scsi_detect (struct SHT *sht)
+{
+ /* Whole function stolen from usb-storage */
+
+ struct hpusbscsi *desc = (struct hpusbscsi *) sht->proc_dir;
+ /* What a hideous hack! */
+
+ char local_name[48];
+
+
+ /* set up the name of our subdirectory under /proc/scsi/ */
+ sprintf (local_name, "hpusbscsi-%d", desc->number);
+ sht->proc_name = kmalloc (strlen (local_name) + 1, GFP_KERNEL);
+ /* FIXME: where is this freed ? */
+
+ if (!sht->proc_name) {
+ return 0;
+ }
+
+ strcpy (sht->proc_name, local_name);
+
+ sht->proc_dir = NULL;
+
+ /* build and submit an interrupt URB for status byte handling */
+ FILL_INT_URB(desc->controlurb,
+ desc->dev,
+ usb_rcvintpipe(desc->dev,desc->ep_int),
+ &desc->scsi_state_byte,
+ 1,
+ control_interrupt_callback,
+ desc,
+ desc->interrupt_interval
+ );
+
+ if ( 0 > usb_submit_urb(desc->controlurb, GFP_KERNEL)) {
+ kfree(sht->proc_name);
+ return 0;
+ }
+
+ /* In host->hostdata we store a pointer to desc */
+ desc->host = scsi_register (sht, sizeof (desc));
+ if (desc->host == NULL) {
+ kfree (sht->proc_name);
+ usb_unlink_urb(desc->controlurb);
+ return 0;
+ }
+ desc->host->hostdata[0] = (unsigned long) desc;
+
+
+ return 1;
+}
+
+static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback)
+{
+ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+ usb_urb_callback usb_callback;
+ int res;
+
+ hpusbscsi->use_count++;
+
+ /* we don't answer for anything but our single device on any faked host controller */
+ if ( srb->device->lun || srb->device->id || srb->device->channel ) {
+ if (callback) {
+ srb->result = DID_BAD_TARGET;
+ callback(srb);
+ }
+ goto out;
+ }
+
+ /* Now we need to decide which callback to give to the urb we send the command with */
+
+ if (!srb->bufflen) {
+ if (srb->cmnd[0] == REQUEST_SENSE){
+ hpusbscsi->current_data_pipe = usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in);
+ usb_callback = request_sense_callback;
+ } else {
+ usb_callback = simple_command_callback;
+ }
+ } else {
+ if (likely(srb->use_sg)) {
+ usb_callback = scatter_gather_callback;
+ hpusbscsi->fragment = 0;
+ } else {
+ usb_callback = simple_payload_callback;
+ }
+ /* Now we find out which direction data is to be transfered in */
+ hpusbscsi->current_data_pipe = DIRECTION_IS_IN(srb->cmnd[0]) ?
+ usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in)
+ :
+ usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out)
+ ;
+ }
+
+
+ TRACE_STATE;
+
+ /* We zero the sense buffer to avoid confusing user space */
+ memset(srb->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+
+ hpusbscsi->state = HP_STATE_BEGINNING;
+ TRACE_STATE;
+
+ /* We prepare the urb for writing out the scsi command */
+ FILL_BULK_URB(
+ hpusbscsi->dataurb,
+ hpusbscsi->dev,
+ usb_sndbulkpipe(hpusbscsi->dev,hpusbscsi->ep_out),
+ srb->cmnd,
+ srb->cmd_len,
+ usb_callback,
+ hpusbscsi
+ );
+ hpusbscsi->scallback = callback;
+ hpusbscsi->srb = srb;
+
+ res = usb_submit_urb(hpusbscsi->dataurb, GFP_ATOMIC);
+ if (unlikely(res)) {
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ if (likely(callback != NULL)) {
+ srb->result = DID_ERROR;
+ callback(srb);
+ }
+ }
+
+out:
+ hpusbscsi->use_count--;
+ return 0;
+}
+
+static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb)
+{
+ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+
+ printk(KERN_DEBUG"SCSI reset requested.\n");
+ //usb_reset_device(hpusbscsi->dev);
+ //printk(KERN_DEBUG"SCSI reset completed.\n");
+ hpusbscsi->state = HP_STATE_FREE;
+
+ return 0;
+}
+
+static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb)
+{
+ struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
+ printk(KERN_DEBUG"Requested is canceled.\n");
+
+ usb_unlink_urb(hpusbscsi->dataurb);
+ usb_unlink_urb(hpusbscsi->controlurb);
+ hpusbscsi->state = HP_STATE_FREE;
+
+ return SCSI_ABORT_PENDING;
+}
+
+/* usb interrupt handlers - they are all running IN INTERRUPT ! */
+
+static void handle_usb_error (struct hpusbscsi *hpusbscsi)
+{
+ if (likely(hpusbscsi->scallback != NULL)) {
+ hpusbscsi->srb->result = DID_ERROR;
+ hpusbscsi->scallback(hpusbscsi->srb);
+ }
+ hpusbscsi->state = HP_STATE_FREE;
+}
+
+static void control_interrupt_callback (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ u8 scsi_state;
+
+DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte);
+ if(unlikely(u->status < 0)) {
+ if (likely(hpusbscsi->state != HP_STATE_FREE))
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+
+ scsi_state = hpusbscsi->scsi_state_byte;
+ if (hpusbscsi->state != HP_STATE_ERROR) {
+ hpusbscsi->srb->result &= SCSI_ERR_MASK;
+ hpusbscsi->srb->result |= scsi_state;
+ }
+
+ if (scsi_state == CHECK_CONDITION << 1) {
+ if (hpusbscsi->state == HP_STATE_WAIT) {
+ issue_request_sense(hpusbscsi);
+ } else {
+ /* we request sense after an eventual data transfer */
+ hpusbscsi->state = HP_STATE_ERROR;
+ }
+ }
+
+ if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT && scsi_state != CHECK_CONDITION <<1 )
+ /* we do a callback to the scsi layer if and only if all data has been transfered */
+ hpusbscsi->scallback(hpusbscsi->srb);
+
+ TRACE_STATE;
+ switch (hpusbscsi->state) {
+ case HP_STATE_WAIT:
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ break;
+ case HP_STATE_WORKING:
+ case HP_STATE_BEGINNING:
+ hpusbscsi->state = HP_STATE_PREMATURE;
+ TRACE_STATE;
+ break;
+ case HP_STATE_ERROR:
+ break;
+ default:
+ printk(KERN_ERR"hpusbscsi: Unexpected status report.\n");
+ TRACE_STATE;
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ break;
+ }
+}
+
+static void simple_command_callback(struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ if (unlikely(u->status<0)) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE) {
+ TRACE_STATE;
+ hpusbscsi->state = HP_STATE_WAIT;
+ } else {
+ if (likely(hpusbscsi->scallback != NULL))
+ hpusbscsi->scallback(hpusbscsi->srb);
+ hpusbscsi->state = HP_STATE_FREE;
+ TRACE_STATE;
+ }
+}
+
+static void scatter_gather_callback(struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ struct scatterlist *sg = hpusbscsi->srb->buffer;
+ usb_urb_callback callback;
+ int res;
+
+ DEBUG("Going through scatter/gather\n");
+ if (unlikely(u->status < 0)) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+
+ if (hpusbscsi->fragment + 1 != hpusbscsi->srb->use_sg)
+ callback = scatter_gather_callback;
+ else
+ callback = simple_done;
+
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE)
+ hpusbscsi->state = HP_STATE_WORKING;
+ TRACE_STATE;
+
+ FILL_BULK_URB(
+ u,
+ hpusbscsi->dev,
+ hpusbscsi->current_data_pipe,
+ page_address(sg[hpusbscsi->fragment].page) +
+ sg[hpusbscsi->fragment].offset,
+ sg[hpusbscsi->fragment++].length,
+ callback,
+ hpusbscsi
+ );
+
+ res = usb_submit_urb(u, GFP_ATOMIC);
+ if (unlikely(res))
+ handle_usb_error(hpusbscsi);
+ TRACE_STATE;
+}
+
+static void simple_done (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+
+ if (unlikely(u->status < 0)) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ DEBUG("Data transfer done\n");
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE) {
+ if (unlikely(u->status < 0)) {
+ handle_usb_error(hpusbscsi);
+ } else {
+ if (hpusbscsi->state != HP_STATE_ERROR) {
+ hpusbscsi->state = HP_STATE_WAIT;
+ } else {
+ issue_request_sense(hpusbscsi);
+ }
+ }
+ } else {
+ if (likely(hpusbscsi->scallback != NULL))
+ hpusbscsi->scallback(hpusbscsi->srb);
+ hpusbscsi->state = HP_STATE_FREE;
+ }
+}
+
+static void simple_payload_callback (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+ int res;
+
+ if (unlikely(u->status<0)) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+
+ FILL_BULK_URB(
+ u,
+ hpusbscsi->dev,
+ hpusbscsi->current_data_pipe,
+ hpusbscsi->srb->buffer,
+ hpusbscsi->srb->bufflen,
+ simple_done,
+ hpusbscsi
+ );
+
+ res = usb_submit_urb(u, GFP_ATOMIC);
+ if (unlikely(res)) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ TRACE_STATE;
+ if (hpusbscsi->state != HP_STATE_PREMATURE) {
+ hpusbscsi->state = HP_STATE_WORKING;
+ TRACE_STATE;
+ }
+}
+
+static void request_sense_callback (struct urb *u)
+{
+ struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
+
+ if (u->status<0) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+
+ FILL_BULK_URB(
+ u,
+ hpusbscsi->dev,
+ hpusbscsi->current_data_pipe,
+ hpusbscsi->srb->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ simple_done,
+ hpusbscsi
+ );
+
+ if (0 > usb_submit_urb(u, GFP_ATOMIC)) {
+ handle_usb_error(hpusbscsi);
+ return;
+ }
+ if (hpusbscsi->state != HP_STATE_PREMATURE && hpusbscsi->state != HP_STATE_ERROR)
+ hpusbscsi->state = HP_STATE_WORKING;
+}
+
+static void issue_request_sense (struct hpusbscsi *hpusbscsi)
+{
+ FILL_BULK_URB(
+ hpusbscsi->dataurb,
+ hpusbscsi->dev,
+ usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out),
+ &hpusbscsi->sense_command,
+ SENSE_COMMAND_SIZE,
+ request_sense_callback,
+ hpusbscsi
+ );
+
+ hpusbscsi->current_data_pipe = usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in);
+
+ if (0 > usb_submit_urb(hpusbscsi->dataurb, GFP_ATOMIC)) {
+ handle_usb_error(hpusbscsi);
+ }
+}
+
--- /dev/null
+/* Header file for the hpusbscsi driver */
+/* (C) Copyright 2001 Oliver Neukum */
+/* sponsored by the Linux Usb Project */
+/* large parts based on or taken from code by John Fremlin and Matt Dharm */
+/* this file is licensed under the GPL */
+
+/* A big thanks to Jose for untiring testing */
+
+typedef void (*usb_urb_callback) (struct urb *);
+typedef void (*scsi_callback)(Scsi_Cmnd *);
+
+#define SENSE_COMMAND_SIZE 6
+#define HPUSBSCSI_SENSE_LENGTH 0x16
+
+struct hpusbscsi
+{
+ struct list_head lh;
+ struct usb_device *dev; /* NULL indicates unplugged device */
+ int ep_out;
+ int ep_in;
+ int ep_int;
+ int interrupt_interval;
+
+ struct Scsi_Host *host;
+ Scsi_Host_Template ctempl;
+ int number;
+ scsi_callback scallback;
+ Scsi_Cmnd *srb;
+ u8 sense_command[SENSE_COMMAND_SIZE];
+
+ int use_count;
+ wait_queue_head_t pending;
+ wait_queue_head_t deathrow;
+
+ struct urb *dataurb;
+ struct urb *controlurb;
+ int fragment;
+
+ int state;
+ int current_data_pipe;
+
+ u8 scsi_state_byte;
+};
+
+#define SCSI_ERR_MASK ~0x3fu
+
+static const unsigned char scsi_command_direction[256/8] = {
+ 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+ 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define DIRECTION_IS_IN(x) ((scsi_command_direction[x>>3] >> (x & 7)) & 1)
+
+static int hpusbscsi_scsi_detect (struct SHT * sht);
+static void simple_command_callback(struct urb *u);
+static void scatter_gather_callback(struct urb *u);
+static void simple_payload_callback (struct urb *u);
+static void request_sense_callback (struct urb *u);
+static void control_interrupt_callback (struct urb *u);
+static void simple_done (struct urb *u);
+static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback);
+static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb);
+static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb);
+static void issue_request_sense (struct hpusbscsi *hpusbscsi);
+
+static Scsi_Host_Template hpusbscsi_scsi_host_template = {
+ name: "hpusbscsi",
+ detect: hpusbscsi_scsi_detect,
+// release: hpusbscsi_scsi_release,
+ queuecommand: hpusbscsi_scsi_queuecommand,
+
+ eh_abort_handler: hpusbscsi_scsi_abort,
+ eh_host_reset_handler: hpusbscsi_scsi_host_reset,
+
+ sg_tablesize: SG_ALL,
+ can_queue: 1,
+ this_id: -1,
+ cmd_per_lun: 1,
+ present: 0,
+ unchecked_isa_dma: FALSE,
+ use_clustering: TRUE,
+ emulated: TRUE
+};
+
+/* defines for internal driver state */
+#define HP_STATE_FREE 0 /*ready for next request */
+#define HP_STATE_BEGINNING 1 /*command being transfered */
+#define HP_STATE_WORKING 2 /* data transfer stage */
+#define HP_STATE_ERROR 3 /* error has been reported */
+#define HP_STATE_WAIT 4 /* waiting for status transfer */
+#define HP_STATE_PREMATURE 5 /* status prematurely reported */
+
+
+
--- /dev/null
+/*
+ * copyright (C) 1999/2000 by Henning Zabel <henning@uni-paderborn.de>
+ *
+ * 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.
+ */
+
+
+/*
+ * USB-Kernel Driver for the Mustek MDC800 Digital Camera
+ * (c) 1999/2000 Henning Zabel <henning@uni-paderborn.de>
+ *
+ *
+ * The driver brings the USB functions of the MDC800 to Linux.
+ * To use the Camera you must support the USB Protocoll of the camera
+ * to the Kernel Node.
+ * The Driver uses a misc device Node. Create it with :
+ * mknod /dev/mustek c 180 32
+ *
+ * The driver supports only one camera.
+ *
+ * Fix: mdc800 used sleep_on and slept with io_lock held.
+ * Converted sleep_on to waitqueues with schedule_timeout and made io_lock
+ * a semaphore from a spinlock.
+ * by Oliver Neukum <520047054719-0001@t-online.de>
+ * (02/12/2001)
+ *
+ * Identify version on module load.
+ * (08/04/2001) gb
+ *
+ * version 0.7.5
+ * Fixed potential SMP races with Spinlocks.
+ * Thanks to Oliver Neukum <oliver.neukum@lrz.uni-muenchen.de> who
+ * noticed the race conditions.
+ * (30/10/2000)
+ *
+ * Fixed: Setting urb->dev before submitting urb.
+ * by Greg KH <greg@kroah.com>
+ * (13/10/2000)
+ *
+ * version 0.7.3
+ * bugfix : The mdc800->state field gets set to READY after the
+ * the diconnect function sets it to NOT_CONNECTED. This makes the
+ * driver running like the camera is connected and causes some
+ * hang ups.
+ *
+ * version 0.7.1
+ * MOD_INC and MOD_DEC are changed in usb_probe to prevent load/unload
+ * problems when compiled as Module.
+ * (04/04/2000)
+ *
+ * The mdc800 driver gets assigned the USB Minor 32-47. The Registration
+ * was updated to use these values.
+ * (26/03/2000)
+ *
+ * The Init und Exit Module Function are updated.
+ * (01/03/2000)
+ *
+ * version 0.7.0
+ * Rewrite of the driver : The driver now uses URB's. The old stuff
+ * has been removed.
+ *
+ * version 0.6.0
+ * Rewrite of this driver: The Emulation of the rs232 protocoll
+ * has been removed from the driver. A special executeCommand function
+ * for this driver is included to gphoto.
+ * The driver supports two kind of communication to bulk endpoints.
+ * Either with the dev->bus->ops->bulk... or with callback function.
+ * (09/11/1999)
+ *
+ * version 0.5.0:
+ * first Version that gets a version number. Most of the needed
+ * functions work.
+ * (20/10/1999)
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+
+#include <linux/usb.h>
+#include <linux/fs.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.7.5 (30/10/2000)"
+#define DRIVER_AUTHOR "Henning Zabel <henning@uni-paderborn.de>"
+#define DRIVER_DESC "USB Driver for Mustek MDC800 Digital Camera"
+
+/* Vendor and Product Information */
+#define MDC800_VENDOR_ID 0x055f
+#define MDC800_PRODUCT_ID 0xa800
+
+/* Timeouts (msec) */
+#define TO_DOWNLOAD_GET_READY 1500
+#define TO_DOWNLOAD_GET_BUSY 1500
+#define TO_WRITE_GET_READY 1000
+#define TO_DEFAULT_COMMAND 5000
+#define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND
+#define TO_GET_READY TO_DEFAULT_COMMAND
+
+/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */
+#define MDC800_DEVICE_MINOR_BASE 32
+
+
+/**************************************************************************
+ Data and structs
+***************************************************************************/
+
+
+typedef enum {
+ NOT_CONNECTED, READY, WORKING, DOWNLOAD
+} mdc800_state;
+
+
+/* Data for the driver */
+struct mdc800_data
+{
+ struct usb_device * dev; // Device Data
+ mdc800_state state;
+
+ unsigned int endpoint [4];
+
+ struct urb * irq_urb;
+ wait_queue_head_t irq_wait;
+ int irq_woken;
+ char* irq_urb_buffer;
+
+ int camera_busy; // is camera busy ?
+ int camera_request_ready; // Status to synchronize with irq
+ char camera_response [8]; // last Bytes send after busy
+
+ struct urb * write_urb;
+ char* write_urb_buffer;
+ wait_queue_head_t write_wait;
+ int written;
+
+
+ struct urb * download_urb;
+ char* download_urb_buffer;
+ wait_queue_head_t download_wait;
+ int downloaded;
+ int download_left; // Bytes left to download ?
+
+
+ /* Device Data */
+ char out [64]; // Answer Buffer
+ int out_ptr; // Index to the first not readen byte
+ int out_count; // Bytes in the buffer
+
+ int open; // Camera device open ?
+ struct semaphore io_lock; // IO -lock
+
+ char in [8]; // Command Input Buffer
+ int in_count;
+
+ int pic_index; // Cache for the Imagesize (-1 for nothing cached )
+ int pic_len;
+};
+
+
+/* Specification of the Endpoints */
+static struct usb_endpoint_descriptor mdc800_ed [4] =
+{
+ { 0,0, 0x01, 0x02, 8, 0,0,0 },
+ { 0,0, 0x82, 0x03, 8, 0,0,0 },
+ { 0,0, 0x03, 0x02, 64, 0,0,0 },
+ { 0,0, 0x84, 0x02, 64, 0,0,0 }
+};
+
+
+/* The Variable used by the driver */
+static struct mdc800_data* mdc800=0;
+
+
+/***************************************************************************
+ The USB Part of the driver
+****************************************************************************/
+
+static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b)
+{
+ return (
+ ( a->bEndpointAddress == b->bEndpointAddress )
+ && ( a->bmAttributes == b->bmAttributes )
+ && ( a->wMaxPacketSize == b->wMaxPacketSize )
+ );
+}
+
+
+/*
+ * Checks wether the camera responds busy
+ */
+static int mdc800_isBusy (char* ch)
+{
+ int i=0;
+ while (i<8)
+ {
+ if (ch [i] != (char)0x99)
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+
+/*
+ * Checks wether the Camera is ready
+ */
+static int mdc800_isReady (char *ch)
+{
+ int i=0;
+ while (i<8)
+ {
+ if (ch [i] != (char)0xbb)
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+
+
+/*
+ * USB IRQ Handler for InputLine
+ */
+static void mdc800_usb_irq (struct urb *urb)
+{
+ int data_received=0, wake_up;
+ unsigned char* b=urb->transfer_buffer;
+ struct mdc800_data* mdc800=urb->context;
+
+ if (urb->status >= 0)
+ {
+
+ //dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+ if (mdc800_isBusy (b))
+ {
+ if (!mdc800->camera_busy)
+ {
+ mdc800->camera_busy=1;
+ dbg ("gets busy");
+ }
+ }
+ else
+ {
+ if (mdc800->camera_busy && mdc800_isReady (b))
+ {
+ mdc800->camera_busy=0;
+ dbg ("gets ready");
+ }
+ }
+ if (!(mdc800_isBusy (b) || mdc800_isReady (b)))
+ {
+ /* Store Data in camera_answer field */
+ dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+ memcpy (mdc800->camera_response,b,8);
+ data_received=1;
+ }
+ }
+ wake_up= ( mdc800->camera_request_ready > 0 )
+ &&
+ (
+ ((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
+ ||
+ ((mdc800->camera_request_ready == 2) && data_received)
+ ||
+ ((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
+ ||
+ (urb->status < 0)
+ );
+
+ if (wake_up)
+ {
+ mdc800->camera_request_ready=0;
+ mdc800->irq_woken=1;
+ wake_up_interruptible (&mdc800->irq_wait);
+ }
+}
+
+
+/*
+ * Waits a while until the irq responds that camera is ready
+ *
+ * mode : 0: Wait for camera gets ready
+ * 1: Wait for receiving data
+ * 2: Wait for camera gets busy
+ *
+ * msec: Time to wait
+ */
+static int mdc800_usb_waitForIRQ (int mode, int msec)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ mdc800->camera_request_ready=1+mode;
+
+ add_wait_queue(&mdc800->irq_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!mdc800->irq_woken)
+ {
+ schedule_timeout (msec*HZ/1000);
+ }
+ remove_wait_queue(&mdc800->irq_wait, &wait);
+ set_current_state(TASK_RUNNING);
+ mdc800->irq_woken = 0;
+
+ if (mdc800->camera_request_ready>0)
+ {
+ mdc800->camera_request_ready=0;
+ err ("timeout waiting for camera.");
+ return -1;
+ }
+
+ if (mdc800->state == NOT_CONNECTED)
+ {
+ warn ("Camera gets disconnected during waiting for irq.");
+ mdc800->camera_request_ready=0;
+ return -2;
+ }
+
+ return 0;
+}
+
+
+/*
+ * The write_urb callback function
+ */
+static void mdc800_usb_write_notify (struct urb *urb)
+{
+ struct mdc800_data* mdc800=urb->context;
+
+ if (urb->status != 0)
+ {
+ err ("writing command fails (status=%i)", urb->status);
+ }
+ else
+ {
+ mdc800->state=READY;
+ }
+ mdc800->written = 1;
+ wake_up_interruptible (&mdc800->write_wait);
+}
+
+
+/*
+ * The download_urb callback function
+ */
+static void mdc800_usb_download_notify (struct urb *urb)
+{
+ struct mdc800_data* mdc800=urb->context;
+
+ if (urb->status == 0)
+ {
+ /* Fill output buffer with these data */
+ memcpy (mdc800->out, urb->transfer_buffer, 64);
+ mdc800->out_count=64;
+ mdc800->out_ptr=0;
+ mdc800->download_left-=64;
+ if (mdc800->download_left == 0)
+ {
+ mdc800->state=READY;
+ }
+ }
+ else
+ {
+ err ("request bytes fails (status:%i)", urb->status);
+ }
+ mdc800->downloaded = 1;
+ wake_up_interruptible (&mdc800->download_wait);
+}
+
+
+/***************************************************************************
+ Probing for the Camera
+ ***************************************************************************/
+
+static struct usb_driver mdc800_usb_driver;
+
+/*
+ * Callback to search the Mustek MDC800 on the USB Bus
+ */
+static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum,
+ const struct usb_device_id *id)
+{
+ int i,j;
+ struct usb_interface_descriptor *intf_desc;
+ int irq_interval=0;
+
+ dbg ("(mdc800_usb_probe) called.");
+
+
+ if (mdc800->dev != 0)
+ {
+ warn ("only one Mustek MDC800 is supported.");
+ return 0;
+ }
+
+ if (dev->descriptor.bNumConfigurations != 1)
+ {
+ err ("probe fails -> wrong Number of Configuration");
+ return 0;
+ }
+ intf_desc=&dev->actconfig->interface[ifnum].altsetting[0];
+
+ if (
+ ( intf_desc->bInterfaceClass != 0xff )
+ || ( intf_desc->bInterfaceSubClass != 0 )
+ || ( intf_desc->bInterfaceProtocol != 0 )
+ || ( intf_desc->bNumEndpoints != 4)
+ )
+ {
+ err ("probe fails -> wrong Interface");
+ return 0;
+ }
+
+ /* Check the Endpoints */
+ for (i=0; i<4; i++)
+ {
+ mdc800->endpoint[i]=-1;
+ for (j=0; j<4; j++)
+ {
+ if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i]))
+ {
+ mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ;
+ if (i==1)
+ {
+ irq_interval=intf_desc->endpoint [j].bInterval;
+ }
+
+ continue;
+ }
+ }
+ if (mdc800->endpoint[i] == -1)
+ {
+ err ("probe fails -> Wrong Endpoints.");
+ return 0;
+ }
+ }
+
+
+ usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800);
+ if (usb_set_interface (dev, ifnum, 0) < 0)
+ {
+ err ("MDC800 Configuration fails.");
+ return 0;
+ }
+
+ info ("Found Mustek MDC800 on USB.");
+
+ down (&mdc800->io_lock);
+
+ mdc800->dev=dev;
+ mdc800->open=0;
+
+ /* Setup URB Structs */
+ FILL_INT_URB (
+ mdc800->irq_urb,
+ mdc800->dev,
+ usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]),
+ mdc800->irq_urb_buffer,
+ 8,
+ mdc800_usb_irq,
+ mdc800,
+ irq_interval
+ );
+
+ FILL_BULK_URB (
+ mdc800->write_urb,
+ mdc800->dev,
+ usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]),
+ mdc800->write_urb_buffer,
+ 8,
+ mdc800_usb_write_notify,
+ mdc800
+ );
+
+ FILL_BULK_URB (
+ mdc800->download_urb,
+ mdc800->dev,
+ usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]),
+ mdc800->download_urb_buffer,
+ 64,
+ mdc800_usb_download_notify,
+ mdc800
+ );
+
+ mdc800->state=READY;
+
+ up (&mdc800->io_lock);
+
+ return mdc800;
+}
+
+
+/*
+ * Disconnect USB device (maybe the MDC800)
+ */
+static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr)
+{
+ struct mdc800_data* mdc800=(struct mdc800_data*) ptr;
+
+ dbg ("(mdc800_usb_disconnect) called");
+
+ if (mdc800->state == NOT_CONNECTED)
+ return;
+
+ mdc800->state=NOT_CONNECTED;
+
+ usb_unlink_urb (mdc800->irq_urb);
+ usb_unlink_urb (mdc800->write_urb);
+ usb_unlink_urb (mdc800->download_urb);
+
+ usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]);
+
+ mdc800->dev=0;
+ info ("Mustek MDC800 disconnected from USB.");
+}
+
+
+/***************************************************************************
+ The Misc device Part (file_operations)
+****************************************************************************/
+
+/*
+ * This Function calc the Answersize for a command.
+ */
+static int mdc800_getAnswerSize (char command)
+{
+ switch ((unsigned char) command)
+ {
+ case 0x2a:
+ case 0x49:
+ case 0x51:
+ case 0x0d:
+ case 0x20:
+ case 0x07:
+ case 0x01:
+ case 0x25:
+ case 0x00:
+ return 8;
+
+ case 0x05:
+ case 0x3e:
+ return mdc800->pic_len;
+
+ case 0x09:
+ return 4096;
+
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * Init the device: (1) alloc mem (2) Increase MOD Count ..
+ */
+static int mdc800_device_open (struct inode* inode, struct file *file)
+{
+ int retval=0;
+ int errn=0;
+
+ down (&mdc800->io_lock);
+
+ if (mdc800->state == NOT_CONNECTED)
+ {
+ errn=-EBUSY;
+ goto error_out;
+ }
+ if (mdc800->open)
+ {
+ errn=-EBUSY;
+ goto error_out;
+ }
+
+ mdc800->in_count=0;
+ mdc800->out_count=0;
+ mdc800->out_ptr=0;
+ mdc800->pic_index=0;
+ mdc800->pic_len=-1;
+ mdc800->download_left=0;
+
+ mdc800->camera_busy=0;
+ mdc800->camera_request_ready=0;
+
+ retval=0;
+ mdc800->irq_urb->dev = mdc800->dev;
+ if (usb_submit_urb (mdc800->irq_urb, GFP_KERNEL))
+ {
+ err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
+ errn = -EIO;
+ goto error_out;
+ }
+
+ mdc800->open=1;
+ dbg ("Mustek MDC800 device opened.");
+
+error_out:
+ up (&mdc800->io_lock);
+ return errn;
+}
+
+
+/*
+ * Close the Camera and release Memory
+ */
+static int mdc800_device_release (struct inode* inode, struct file *file)
+{
+ int retval=0;
+ dbg ("Mustek MDC800 device closed.");
+
+ down (&mdc800->io_lock);
+ if (mdc800->open && (mdc800->state != NOT_CONNECTED))
+ {
+ usb_unlink_urb (mdc800->irq_urb);
+ usb_unlink_urb (mdc800->write_urb);
+ usb_unlink_urb (mdc800->download_urb);
+ mdc800->open=0;
+ }
+ else
+ {
+ retval=-EIO;
+ }
+
+ up(&mdc800->io_lock);
+ return retval;
+}
+
+
+/*
+ * The Device read callback Function
+ */
+static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos)
+{
+ int left=len, sts=len; /* single transfer size */
+ char* ptr=buf;
+ DECLARE_WAITQUEUE(wait, current);
+
+ down (&mdc800->io_lock);
+ if (mdc800->state == NOT_CONNECTED)
+ {
+ up (&mdc800->io_lock);
+ return -EBUSY;
+ }
+ if (mdc800->state == WORKING)
+ {
+ warn ("Illegal State \"working\" reached during read ?!");
+ up (&mdc800->io_lock);
+ return -EBUSY;
+ }
+ if (!mdc800->open)
+ {
+ up (&mdc800->io_lock);
+ return -EBUSY;
+ }
+
+ while (left)
+ {
+ if (signal_pending (current))
+ {
+ up (&mdc800->io_lock);
+ return -EINTR;
+ }
+
+ sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
+
+ if (sts <= 0)
+ {
+ /* Too less Data in buffer */
+ if (mdc800->state == DOWNLOAD)
+ {
+ mdc800->out_count=0;
+ mdc800->out_ptr=0;
+
+ /* Download -> Request new bytes */
+ mdc800->download_urb->dev = mdc800->dev;
+ if (usb_submit_urb (mdc800->download_urb, GFP_KERNEL))
+ {
+ err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
+ up (&mdc800->io_lock);
+ return len-left;
+ }
+ add_wait_queue(&mdc800->download_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!mdc800->downloaded)
+ {
+ schedule_timeout (TO_DOWNLOAD_GET_READY*HZ/1000);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&mdc800->download_wait, &wait);
+ mdc800->downloaded = 0;
+ if (mdc800->download_urb->status != 0)
+ {
+ err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
+ up (&mdc800->io_lock);
+ return len-left;
+ }
+ }
+ else
+ {
+ /* No more bytes -> that's an error*/
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+ }
+ else
+ {
+ /* memcpy Bytes */
+ memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts);
+ ptr+=sts;
+ left-=sts;
+ mdc800->out_ptr+=sts;
+ }
+ }
+
+ up (&mdc800->io_lock);
+ return len-left;
+}
+
+
+/*
+ * The Device write callback Function
+ * If a 8Byte Command is received, it will be send to the camera.
+ * After this the driver initiates the request for the answer or
+ * just waits until the camera becomes ready.
+ */
+static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
+{
+ int i=0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ down (&mdc800->io_lock);
+ if (mdc800->state != READY)
+ {
+ up (&mdc800->io_lock);
+ return -EBUSY;
+ }
+ if (!mdc800->open )
+ {
+ up (&mdc800->io_lock);
+ return -EBUSY;
+ }
+
+ while (i<len)
+ {
+ if (signal_pending (current))
+ {
+ up (&mdc800->io_lock);
+ return -EINTR;
+ }
+
+ /* check for command start */
+ if (buf [i] == (char) 0x55)
+ {
+ mdc800->in_count=0;
+ mdc800->out_count=0;
+ mdc800->out_ptr=0;
+ mdc800->download_left=0;
+ }
+
+ /* save command byte */
+ if (mdc800->in_count < 8)
+ {
+ mdc800->in[mdc800->in_count]=buf[i];
+ mdc800->in_count++;
+ }
+ else
+ {
+ err ("Command is to long !\n");
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+
+ /* Command Buffer full ? -> send it to camera */
+ if (mdc800->in_count == 8)
+ {
+ int answersize;
+
+ if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
+ {
+ err ("Camera didn't get ready.\n");
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+
+ answersize=mdc800_getAnswerSize (mdc800->in[1]);
+
+ mdc800->state=WORKING;
+ memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8);
+ mdc800->write_urb->dev = mdc800->dev;
+ if (usb_submit_urb (mdc800->write_urb, GFP_KERNEL))
+ {
+ err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+ add_wait_queue(&mdc800->write_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!mdc800->written)
+ {
+ schedule_timeout (TO_WRITE_GET_READY*HZ/1000);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&mdc800->write_wait, &wait);
+ mdc800->written = 0;
+ if (mdc800->state == WORKING)
+ {
+ usb_unlink_urb (mdc800->write_urb);
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+
+ switch ((unsigned char) mdc800->in[1])
+ {
+ case 0x05: /* Download Image */
+ case 0x3e: /* Take shot in Fine Mode (WCam Mode) */
+ if (mdc800->pic_len < 0)
+ {
+ err ("call 0x07 before 0x05,0x3e");
+ mdc800->state=READY;
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+ mdc800->pic_len=-1;
+
+ case 0x09: /* Download Thumbnail */
+ mdc800->download_left=answersize+64;
+ mdc800->state=DOWNLOAD;
+ mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY);
+ break;
+
+
+ default:
+ if (answersize)
+ {
+
+ if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
+ {
+ err ("requesting answer from irq fails");
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+
+ /* Write dummy data, (this is ugly but part of the USB Protokoll */
+ /* if you use endpoint 1 as bulk and not as irq */
+ memcpy (mdc800->out, mdc800->camera_response,8);
+
+ /* This is the interpreted answer */
+ memcpy (&mdc800->out[8], mdc800->camera_response,8);
+
+ mdc800->out_ptr=0;
+ mdc800->out_count=16;
+
+ /* Cache the Imagesize, if command was getImageSize */
+ if (mdc800->in [1] == (char) 0x07)
+ {
+ mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2];
+
+ dbg ("cached imagesize = %i",mdc800->pic_len);
+ }
+
+ }
+ else
+ {
+ if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
+ {
+ err ("Command Timeout.");
+ up (&mdc800->io_lock);
+ return -EIO;
+ }
+ }
+ mdc800->state=READY;
+ break;
+ }
+ }
+ i++;
+ }
+ up (&mdc800->io_lock);
+ return i;
+}
+
+
+/***************************************************************************
+ Init and Cleanup this driver (Structs and types)
+****************************************************************************/
+
+/* File Operations of this drivers */
+static struct file_operations mdc800_device_ops =
+{
+ owner: THIS_MODULE,
+ read: mdc800_device_read,
+ write: mdc800_device_write,
+ open: mdc800_device_open,
+ release: mdc800_device_release,
+};
+
+
+
+static struct usb_device_id mdc800_table [] = {
+ { USB_DEVICE(MDC800_VENDOR_ID, MDC800_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, mdc800_table);
+/*
+ * USB Driver Struct for this device
+ */
+static struct usb_driver mdc800_usb_driver =
+{
+ name: "mdc800",
+ probe: mdc800_usb_probe,
+ disconnect: mdc800_usb_disconnect,
+ fops: &mdc800_device_ops,
+ minor: MDC800_DEVICE_MINOR_BASE,
+ id_table: mdc800_table
+};
+
+
+
+/************************************************************************
+ Init and Cleanup this driver (Main Functions)
+*************************************************************************/
+
+#define try(A) if ((A) == 0) goto cleanup_on_fail;
+#define try_free_mem(A) if (A != 0) { kfree (A); A=0; }
+#define try_free_urb(A) if (A != 0) { usb_free_urb (A); A=0; }
+
+int __init usb_mdc800_init (void)
+{
+ /* Allocate Memory */
+ try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
+
+ memset(mdc800, 0, sizeof(struct mdc800_data));
+ mdc800->dev=0;
+ mdc800->open=0;
+ mdc800->state=NOT_CONNECTED;
+ init_MUTEX (&mdc800->io_lock);
+
+ init_waitqueue_head (&mdc800->irq_wait);
+ init_waitqueue_head (&mdc800->write_wait);
+ init_waitqueue_head (&mdc800->download_wait);
+
+ mdc800->irq_woken = 0;
+ mdc800->downloaded = 0;
+ mdc800->written = 0;
+
+ try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
+ try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
+ try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL));
+
+ try (mdc800->irq_urb=usb_alloc_urb (0, GFP_KERNEL));
+ try (mdc800->download_urb=usb_alloc_urb (0, GFP_KERNEL));
+ try (mdc800->write_urb=usb_alloc_urb (0, GFP_KERNEL));
+
+ /* Register the driver */
+ if (usb_register (&mdc800_usb_driver) < 0)
+ goto cleanup_on_fail;
+
+ info (DRIVER_VERSION ":" DRIVER_DESC);
+
+ return 0;
+
+ /* Clean driver up, when something fails */
+
+cleanup_on_fail:
+
+ if (mdc800 != 0)
+ {
+ err ("can't alloc memory!");
+
+ try_free_mem (mdc800->download_urb_buffer);
+ try_free_mem (mdc800->write_urb_buffer);
+ try_free_mem (mdc800->irq_urb_buffer);
+
+ try_free_urb (mdc800->write_urb);
+ try_free_urb (mdc800->download_urb);
+ try_free_urb (mdc800->irq_urb);
+
+ kfree (mdc800);
+ }
+ mdc800=0;
+ return -1;
+}
+
+
+void __exit usb_mdc800_cleanup (void)
+{
+ usb_deregister (&mdc800_usb_driver);
+
+ usb_free_urb (mdc800->irq_urb);
+ usb_free_urb (mdc800->download_urb);
+ usb_free_urb (mdc800->write_urb);
+
+ kfree (mdc800->irq_urb_buffer);
+ kfree (mdc800->write_urb_buffer);
+ kfree (mdc800->download_urb_buffer);
+
+ kfree (mdc800);
+ mdc800=0;
+}
+
+module_init (usb_mdc800_init);
+module_exit (usb_mdc800_cleanup);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
--- /dev/null
+/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
+ *
+ * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
+ * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ *
+ * Parts shamelessly stolen from usb-storage and copyright by their
+ * authors. Thanks to Matt Dharm for giving us permission!
+ *
+ * This driver implements a SCSI host controller driver and a USB
+ * device driver. To avoid confusion, all the USB related stuff is
+ * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
+ *
+ * Microtek (www.microtek.com) did not release the specifications for
+ * their USB protocol to us, so we had to reverse engineer them. We
+ * don't know for which models they are valid.
+ *
+ * The X6 USB has three bulk endpoints, one output (0x1) down which
+ * commands and outgoing data are sent, and two input: 0x82 from which
+ * normal data is read from the scanner (in packets of maximum 32
+ * bytes) and from which the status byte is read, and 0x83 from which
+ * the results of a scan (or preview) are read in up to 64 * 1024 byte
+ * chunks by the Windows driver. We don't know how much it is possible
+ * to read at a time from 0x83.
+ *
+ * It seems possible to read (with URB transfers) everything from 0x82
+ * in one go, without bothering to read in 32 byte chunks.
+ *
+ * There seems to be an optimisation of a further READ implicit if
+ * you simply read from 0x83.
+ *
+ * Guessed protocol:
+ *
+ * Send raw SCSI command to EP 0x1
+ *
+ * If there is data to receive:
+ * If the command was READ datatype=image:
+ * Read a lot of data from EP 0x83
+ * Else:
+ * Read data from EP 0x82
+ * Else:
+ * If there is data to transmit:
+ * Write it to EP 0x1
+ *
+ * Read status byte from EP 0x82
+ *
+ * References:
+ *
+ * The SCSI command set for the scanner is available from
+ * ftp://ftp.microtek.com/microtek/devpack/
+ *
+ * Microtek NV sent us a more up to date version of the document. If
+ * you want it, just send mail.
+ *
+ * Status:
+ *
+ * Untested with multiple scanners.
+ * Untested on SMP.
+ * Untested on a bigendian machine.
+ *
+ * History:
+ *
+ * 20000417 starting history
+ * 20000417 fixed load oops
+ * 20000417 fixed unload oops
+ * 20000419 fixed READ IMAGE detection
+ * 20000424 started conversion to use URBs
+ * 20000502 handled short transfers as errors
+ * 20000513 rename and organisation of functions (john)
+ * 20000513 added IDs for all products supported by Windows driver (john)
+ * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
+ * 20000514 Version 0.0.8j
+ * 20000514 Fix reporting of non-existant devices to SCSI layer (john)
+ * 20000514 Added MTS_DEBUG_INT (john)
+ * 20000514 Changed "usb-microtek" to "microtek" for consistency (john)
+ * 20000514 Stupid bug fixes (john)
+ * 20000514 Version 0.0.9j
+ * 20000515 Put transfer context and URB in mts_desc (john)
+ * 20000515 Added prelim turn off debugging support (john)
+ * 20000515 Version 0.0.10j
+ * 20000515 Fixed up URB allocation (clear URB on alloc) (john)
+ * 20000515 Version 0.0.11j
+ * 20000516 Removed unnecessary spinlock in mts_transfer_context (john)
+ * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
+ * 20000516 Implemented (badly) scsi_abort (john)
+ * 20000516 Version 0.0.12j
+ * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
+ * 20000517 Added mts_debug_dump to print ll USB info (john)
+ * 20000518 Tweaks and documentation updates (john)
+ * 20000518 Version 0.0.13j
+ * 20000518 Cleaned up abort handling (john)
+ * 20000523 Removed scsi_command and various scsi_..._resets (john)
+ * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
+ * 20000523 Fixed last tiresome compile warning (john)
+ * 20000523 Version 0.0.14j (though version 0.1 has come out?)
+ * 20000602 Added primitive reset
+ * 20000602 Version 0.2.0
+ * 20000603 various cosmetic changes
+ * 20000603 Version 0.2.1
+ * 20000620 minor cosmetic changes
+ * 20000620 Version 0.2.2
+ * 20000822 Hopefully fixed deadlock in mts_remove_nolock()
+ * 20000822 Fixed minor race in mts_transfer_cleanup()
+ * 20000822 Fixed deadlock on submission error in queuecommand
+ * 20000822 Version 0.2.3
+ * 20000913 Reduced module size if debugging is off
+ * 20000913 Version 0.2.4
+ * 20010210 New abort logic
+ * 20010210 Version 0.3.0
+ * 20010217 Merged scatter/gather
+ * 20010218 Version 0.4.0
+ * 20010218 Cosmetic fixes
+ * 20010218 Version 0.4.1
+ * 20010306 Abort while using scatter/gather
+ * 20010306 Version 0.4.2
+ * 20010311 Remove all timeouts and tidy up generally (john)
+ * 20010320 check return value of scsi_register()
+ * 20010320 Version 0.4.3
+ * 20010408 Identify version on module load.
+ * 20011003 Fix multiple requests
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/proc_fs.h>
+
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "../../scsi/sd.h"
+
+#include "microtek.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.4.3"
+#define DRIVER_AUTHOR "John Fremlin <vii@penguinpowered.com>, Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>"
+#define DRIVER_DESC "Microtek Scanmaker X6 USB scanner driver"
+
+/* Should we do debugging? */
+
+//#define MTS_DO_DEBUG
+
+/* USB layer driver interface */
+
+static void *mts_usb_probe(struct usb_device *dev, unsigned int interface,
+ const struct usb_device_id *id);
+static void mts_usb_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_device_id mts_usb_ids [];
+
+static struct usb_driver mts_usb_driver = {
+ name: "microtekX6",
+ probe: mts_usb_probe,
+ disconnect: mts_usb_disconnect,
+ id_table: mts_usb_ids,
+};
+
+
+/* Internal driver stuff */
+
+#define MTS_VERSION "0.4.3"
+#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
+
+#define MTS_WARNING(x...) \
+ printk( KERN_WARNING MTS_NAME x )
+#define MTS_ERROR(x...) \
+ printk( KERN_ERR MTS_NAME x )
+#define MTS_INT_ERROR(x...) \
+ MTS_ERROR(x)
+#define MTS_MESSAGE(x...) \
+ printk( KERN_INFO MTS_NAME x )
+
+#if defined MTS_DO_DEBUG
+
+#define MTS_DEBUG(x...) \
+ printk( KERN_DEBUG MTS_NAME x )
+
+#define MTS_DEBUG_GOT_HERE() \
+ MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ )
+#define MTS_DEBUG_INT() \
+ do { MTS_DEBUG_GOT_HERE(); \
+ MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
+ MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
+ mts_debug_dump(context->instance);\
+ } while(0)
+#else
+
+#define MTS_NUL_STATEMENT do { } while(0)
+
+#define MTS_DEBUG(x...) MTS_NUL_STATEMENT
+#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
+#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
+
+#endif
+
+
+
+#define MTS_INT_INIT()\
+ struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \
+ MTS_DEBUG_INT();\
+
+#ifdef MTS_DO_DEBUG
+
+static inline void mts_debug_dump(struct mts_desc* desc) {
+ MTS_DEBUG("desc at 0x%x: halted = %02x%02x, toggle = %02x%02x\n",
+ (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0],
+ (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
+ );
+ MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
+ usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
+ usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
+ usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
+ );
+}
+
+
+static inline void mts_show_command(Scsi_Cmnd *srb)
+{
+ char *what = NULL;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+ case REZERO_UNIT: what = "REZERO_UNIT"; break;
+ case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+ case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+ case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+ case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+ case READ_6: what = "READ_6"; break;
+ case WRITE_6: what = "WRITE_6"; break;
+ case SEEK_6: what = "SEEK_6"; break;
+ case READ_REVERSE: what = "READ_REVERSE"; break;
+ case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+ case SPACE: what = "SPACE"; break;
+ case INQUIRY: what = "INQUIRY"; break;
+ case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+ case MODE_SELECT: what = "MODE_SELECT"; break;
+ case RESERVE: what = "RESERVE"; break;
+ case RELEASE: what = "RELEASE"; break;
+ case COPY: what = "COPY"; break;
+ case ERASE: what = "ERASE"; break;
+ case MODE_SENSE: what = "MODE_SENSE"; break;
+ case START_STOP: what = "START_STOP"; break;
+ case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+ case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+ case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+ case SET_WINDOW: what = "SET_WINDOW"; break;
+ case READ_CAPACITY: what = "READ_CAPACITY"; break;
+ case READ_10: what = "READ_10"; break;
+ case WRITE_10: what = "WRITE_10"; break;
+ case SEEK_10: what = "SEEK_10"; break;
+ case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+ case VERIFY: what = "VERIFY"; break;
+ case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+ case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+ case SEARCH_LOW: what = "SEARCH_LOW"; break;
+ case SET_LIMITS: what = "SET_LIMITS"; break;
+ case READ_POSITION: what = "READ_POSITION"; break;
+ case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+ case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+ case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+ case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+ case COMPARE: what = "COMPARE"; break;
+ case COPY_VERIFY: what = "COPY_VERIFY"; break;
+ case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+ case READ_BUFFER: what = "READ_BUFFER"; break;
+ case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+ case READ_LONG: what = "READ_LONG"; break;
+ case WRITE_LONG: what = "WRITE_LONG"; break;
+ case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+ case WRITE_SAME: what = "WRITE_SAME"; break;
+ case READ_TOC: what = "READ_TOC"; break;
+ case LOG_SELECT: what = "LOG_SELECT"; break;
+ case LOG_SENSE: what = "LOG_SENSE"; break;
+ case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case READ_12: what = "READ_12"; break;
+ case WRITE_12: what = "WRITE_12"; break;
+ case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+ case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+ case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+ case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+ default:
+ MTS_DEBUG("can't decode command\n");
+ goto out;
+ break;
+ }
+ MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
+
+ out:
+ MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
+ srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
+
+#else
+
+static inline void mts_show_command(Scsi_Cmnd * dummy)
+{
+}
+
+static inline void mts_debug_dump(struct mts_desc* dummy)
+{
+}
+
+#endif
+
+static inline void mts_urb_abort(struct mts_desc* desc) {
+ MTS_DEBUG_GOT_HERE();
+ mts_debug_dump(desc);
+
+ usb_unlink_urb( desc->urb );
+}
+
+static struct mts_desc * mts_list; /* list of active scanners */
+struct semaphore mts_list_semaphore;
+
+/* Internal list operations */
+
+static
+void mts_remove_nolock( struct mts_desc* to_remove )
+{
+ MTS_DEBUG( "removing 0x%x from list\n",
+ (int)to_remove );
+
+ lock_kernel();
+ mts_urb_abort(to_remove);
+
+ MTS_DEBUG_GOT_HERE();
+
+ if ( to_remove != mts_list ) {
+ MTS_DEBUG_GOT_HERE();
+ if (to_remove->prev && to_remove->next)
+ to_remove->prev->next = to_remove->next;
+ } else {
+ MTS_DEBUG_GOT_HERE();
+ mts_list = to_remove->next;
+ if (mts_list) {
+ MTS_DEBUG_GOT_HERE();
+ mts_list->prev = 0;
+ }
+ }
+
+ if ( to_remove->next ) {
+ MTS_DEBUG_GOT_HERE();
+ to_remove->next->prev = to_remove->prev;
+ }
+
+ MTS_DEBUG_GOT_HERE();
+ scsi_unregister_host(&to_remove->ctempl);
+ unlock_kernel();
+
+ usb_free_urb(to_remove->urb);
+ kfree( to_remove );
+}
+
+static
+void mts_add_nolock( struct mts_desc* to_add )
+{
+ MTS_DEBUG( "adding 0x%x to list\n", (int)to_add );
+
+ to_add->prev = 0;
+ to_add->next = mts_list;
+ if ( mts_list ) {
+ mts_list->prev = to_add;
+ }
+
+ mts_list = to_add;
+}
+
+
+
+
+/* SCSI driver interface */
+
+/* scsi related functions - dummies for now mostly */
+
+static int mts_scsi_release(struct Scsi_Host *psh)
+{
+ MTS_DEBUG_GOT_HERE();
+
+ return 0;
+}
+
+static int mts_scsi_abort (Scsi_Cmnd *srb)
+{
+ struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+
+ MTS_DEBUG_GOT_HERE();
+
+ mts_urb_abort(desc);
+
+ return SCSI_ABORT_PENDING;
+}
+
+static int mts_scsi_host_reset (Scsi_Cmnd *srb)
+{
+ struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+
+ MTS_DEBUG_GOT_HERE();
+ mts_debug_dump(desc);
+
+ usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */
+ return 0; /* RANT why here 0 and not SUCCESS */
+}
+
+/* the core of the scsi part */
+
+/* faking a detection - which can't fail :-) */
+
+static int mts_scsi_detect (struct SHT * sht)
+{
+ /* Whole function stolen from usb-storage */
+
+ struct mts_desc * desc = (struct mts_desc *)sht->proc_dir;
+ /* What a hideous hack! */
+
+ char local_name[48];
+
+ MTS_DEBUG_GOT_HERE();
+
+ /* set up the name of our subdirectory under /proc/scsi/ */
+ sprintf(local_name, "microtek-%d", desc->host_number);
+ sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+ /* FIXME: where is this freed ? */
+
+ if (!sht->proc_name) {
+ MTS_ERROR( "unable to allocate memory for proc interface!!\n" );
+ return 0;
+ }
+
+ strcpy(sht->proc_name, local_name);
+
+ sht->proc_dir = NULL;
+
+ /* In host->hostdata we store a pointer to desc */
+ desc->host = scsi_register(sht, sizeof(desc));
+ if (desc->host == NULL) {
+ MTS_ERROR("Cannot register due to low memory");
+ kfree(sht->proc_name);
+ return 0;
+ }
+ desc->host->hostdata[0] = (unsigned long)desc;
+/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */
+
+ return 1;
+}
+
+
+
+/* Main entrypoint: SCSI commands are dispatched to here */
+
+
+
+static
+int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
+
+static void mts_transfer_cleanup( struct urb *transfer );
+static void mts_do_sg(struct urb * transfer);
+
+
+inline static
+void mts_int_submit_urb (struct urb* transfer,
+ int pipe,
+ void* data,
+ unsigned length,
+ mts_usb_urb_callback callback )
+/* Interrupt context! */
+
+/* Holding transfer->context->lock! */
+{
+ int res;
+
+ MTS_INT_INIT();
+
+ FILL_BULK_URB(transfer,
+ context->instance->usb_dev,
+ pipe,
+ data,
+ length,
+ callback,
+ context
+ );
+
+ transfer->status = 0;
+
+ res = usb_submit_urb( transfer, GFP_ATOMIC );
+ if ( unlikely(res) ) {
+ MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
+ context->srb->result = DID_ERROR << 16;
+ mts_transfer_cleanup(transfer);
+ }
+ return;
+}
+
+
+static void mts_transfer_cleanup( struct urb *transfer )
+/* Interrupt context! */
+{
+ MTS_INT_INIT();
+
+ if ( likely(context->final_callback != NULL) )
+ context->final_callback(context->srb);
+
+}
+
+static void mts_transfer_done( struct urb *transfer )
+{
+ MTS_INT_INIT();
+
+ context->srb->result &= MTS_SCSI_ERR_MASK;
+ context->srb->result |= (unsigned)context->status<<1;
+
+ mts_transfer_cleanup(transfer);
+
+ return;
+}
+
+
+static void mts_get_status( struct urb *transfer )
+/* Interrupt context! */
+{
+ MTS_INT_INIT();
+
+ mts_int_submit_urb(transfer,
+ usb_rcvbulkpipe(context->instance->usb_dev,
+ context->instance->ep_response),
+ &context->status,
+ 1,
+ mts_transfer_done );
+}
+
+static void mts_data_done( struct urb* transfer )
+/* Interrupt context! */
+{
+ MTS_INT_INIT();
+
+ if ( context->data_length != transfer->actual_length ) {
+ context->srb->resid = context->data_length - transfer->actual_length;
+ } else if ( unlikely(transfer->status) ) {
+ context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
+ }
+
+ mts_get_status(transfer);
+
+ return;
+}
+
+
+static void mts_command_done( struct urb *transfer )
+/* Interrupt context! */
+{
+ MTS_INT_INIT();
+
+ if ( unlikely(transfer->status) ) {
+ if (transfer->status == -ENOENT) {
+ /* We are being killed */
+ MTS_DEBUG_GOT_HERE();
+ context->srb->result = DID_ABORT<<16;
+ } else {
+ /* A genuine error has occured */
+ MTS_DEBUG_GOT_HERE();
+
+ context->srb->result = DID_ERROR<<16;
+ }
+ mts_transfer_cleanup(transfer);
+
+ return;
+ }
+
+ if ( context->data ) {
+ mts_int_submit_urb(transfer,
+ context->data_pipe,
+ context->data,
+ context->data_length,
+ context->srb->use_sg ? mts_do_sg : mts_data_done);
+ } else mts_get_status(transfer);
+
+ return;
+}
+
+static void mts_do_sg (struct urb* transfer)
+{
+ struct scatterlist * sg;
+ MTS_INT_INIT();
+
+ MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,context->srb->use_sg);
+
+ if (unlikely(transfer->status)) {
+ context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
+ mts_transfer_cleanup(transfer);
+ }
+
+ sg = context->srb->buffer;
+ context->fragment++;
+ mts_int_submit_urb(transfer,
+ context->data_pipe,
+ page_address(sg[context->fragment].page) +
+ sg[context->fragment].offset,
+ sg[context->fragment].length,
+ context->fragment + 1 == context->srb->use_sg ? mts_data_done : mts_do_sg);
+ return;
+}
+
+static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
+static const u8 mts_read_image_sig_len = 4;
+static const unsigned char mts_direction[256/8] = {
+ 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+ 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
+
+static void
+mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
+{
+ int pipe;
+ struct scatterlist * sg;
+
+ MTS_DEBUG_GOT_HERE();
+
+ desc->context.instance = desc;
+ desc->context.srb = srb;
+ desc->context.fragment = 0;
+
+ if (!srb->use_sg) {
+ if ( !srb->bufflen ){
+ desc->context.data = 0;
+ desc->context.data_length = 0;
+ return;
+ } else {
+ desc->context.data = srb->buffer;
+ desc->context.data_length = srb->bufflen;
+ MTS_DEBUG("length = %d or %d\n",
+ srb->request_bufflen, srb->bufflen);
+ }
+ } else {
+ MTS_DEBUG("Using scatter/gather\n");
+ sg = srb->buffer;
+ desc->context.data = page_address(sg[0].page) + sg[0].offset;
+ desc->context.data_length = sg[0].length;
+ }
+
+
+ /* can't rely on srb->sc_data_direction */
+
+ /* Brutally ripped from usb-storage */
+
+ if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
+) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
+ MTS_DEBUG( "transfering from desc->ep_image == %d\n",
+ (int)desc->ep_image );
+ } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
+ pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
+ MTS_DEBUG( "transfering from desc->ep_response == %d\n",
+ (int)desc->ep_response);
+ } else {
+ MTS_DEBUG("transfering to desc->ep_out == %d\n",
+ (int)desc->ep_out);
+ pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
+ }
+ desc->context.data_pipe = pipe;
+}
+
+
+static
+int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
+{
+ struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
+ int err = 0;
+ int res;
+
+ MTS_DEBUG_GOT_HERE();
+ mts_show_command(srb);
+ mts_debug_dump(desc);
+
+ if ( srb->device->lun || srb->device->id || srb->device->channel ) {
+
+ MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
+
+ MTS_DEBUG("this device doesn't exist\n");
+
+ srb->result = DID_BAD_TARGET << 16;
+
+ if(likely(callback != NULL))
+ callback(srb);
+
+ goto out;
+ }
+
+
+ FILL_BULK_URB(desc->urb,
+ desc->usb_dev,
+ usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
+ srb->cmnd,
+ srb->cmd_len,
+ mts_command_done,
+ &desc->context
+ );
+
+
+ mts_build_transfer_context( srb, desc );
+ desc->context.final_callback = callback;
+
+ /* here we need ATOMIC as we are called with the iolock */
+ res=usb_submit_urb(desc->urb, GFP_ATOMIC);
+
+ if(unlikely(res)){
+ MTS_ERROR("error %d submitting URB\n",(int)res);
+ srb->result = DID_ERROR << 16;
+
+ if(likely(callback != NULL))
+ callback(srb);
+
+ }
+
+out:
+ return err;
+}
+/*
+ * this defines our 'host'
+ */
+
+/* NOTE: This is taken from usb-storage, should be right. */
+
+
+static Scsi_Host_Template mts_scsi_host_template = {
+ name: "microtekX6",
+ detect: mts_scsi_detect,
+ release: mts_scsi_release,
+ queuecommand: mts_scsi_queuecommand,
+
+ eh_abort_handler: mts_scsi_abort,
+ eh_host_reset_handler: mts_scsi_host_reset,
+
+ sg_tablesize: SG_ALL,
+ can_queue: 1,
+ this_id: -1,
+ cmd_per_lun: 1,
+ present: 0,
+ unchecked_isa_dma: FALSE,
+ use_clustering: TRUE,
+ emulated: TRUE
+};
+
+
+/* USB layer driver interface implementation */
+
+static void mts_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+ struct mts_desc* to_remove = (struct mts_desc*)ptr;
+
+ MTS_DEBUG_GOT_HERE();
+
+ /* leave the list - lock it */
+ down(&mts_list_semaphore);
+
+ mts_remove_nolock(to_remove);
+
+ up(&mts_list_semaphore);
+}
+
+struct vendor_product
+{
+ char* name;
+ enum
+ {
+ mts_sup_unknown=0,
+ mts_sup_alpha,
+ mts_sup_full
+ }
+ support_status;
+} ;
+
+
+/* These are taken from the msmUSB.inf file on the Windows driver CD */
+const static struct vendor_product mts_supported_products[] =
+{
+ { "Phantom 336CX", mts_sup_unknown},
+ { "Phantom 336CX", mts_sup_unknown},
+ { "Scanmaker X6", mts_sup_alpha},
+ { "Phantom C6", mts_sup_unknown},
+ { "Phantom 336CX", mts_sup_unknown},
+ { "ScanMaker V6USL", mts_sup_unknown},
+ { "ScanMaker V6USL", mts_sup_unknown},
+ { "Scanmaker V6UL", mts_sup_unknown},
+ { "Scanmaker V6UPL", mts_sup_alpha},
+};
+
+/* The entries of microtek_table must correspond, line-by-line to
+ the entries of mts_supported_products[]. */
+
+static struct usb_device_id mts_usb_ids [] =
+{
+ { USB_DEVICE(0x4ce, 0x0300) },
+ { USB_DEVICE(0x5da, 0x0094) },
+ { USB_DEVICE(0x5da, 0x0099) },
+ { USB_DEVICE(0x5da, 0x009a) },
+ { USB_DEVICE(0x5da, 0x00a0) },
+ { USB_DEVICE(0x5da, 0x00a3) },
+ { USB_DEVICE(0x5da, 0x80a3) },
+ { USB_DEVICE(0x5da, 0x80ac) },
+ { USB_DEVICE(0x5da, 0x00b6) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, mts_usb_ids);
+
+
+static void * mts_usb_probe (struct usb_device *dev, unsigned int interface,
+ const struct usb_device_id *id)
+{
+ int i;
+ int result;
+ int ep_out = -1;
+ int ep_in_set[3]; /* this will break if we have more than three endpoints
+ which is why we check */
+ int *ep_in_current = ep_in_set;
+
+ struct mts_desc * new_desc;
+ struct vendor_product const* p;
+
+ /* the altsettting 0 on the interface we're probing */
+ struct usb_interface_descriptor *altsetting;
+
+ MTS_DEBUG_GOT_HERE();
+ MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
+
+ MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
+ (int)dev->descriptor.idProduct,
+ (int)dev->descriptor.idVendor );
+
+ MTS_DEBUG_GOT_HERE();
+
+ p = &mts_supported_products[id - mts_usb_ids];
+
+ MTS_DEBUG_GOT_HERE();
+
+ MTS_DEBUG( "found model %s\n", p->name );
+ if ( p->support_status != mts_sup_full )
+ MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
+ p->name );
+
+ /* the altsettting 0 on the interface we're probing */
+ altsetting =
+ &(dev->actconfig->interface[interface].altsetting[0]);
+
+
+ /* Check if the config is sane */
+
+ if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) {
+ MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
+ (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints );
+ return NULL;
+ }
+
+ for( i = 0; i < altsetting->bNumEndpoints; i++ ) {
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
+
+ MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
+ (int)altsetting->endpoint[i].bEndpointAddress );
+ } else {
+ if (altsetting->endpoint[i].bEndpointAddress &
+ USB_DIR_IN)
+ *ep_in_current++
+ = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else {
+ if ( ep_out != -1 ) {
+ MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
+ return NULL;
+ }
+
+ ep_out = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ }
+
+ }
+
+
+ if ( ep_out == -1 ) {
+ MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
+ return NULL;
+ }
+
+
+ /* I don't understand the following fully (it's from usb-storage) -- John */
+
+ /* set the interface -- STALL is an acceptable response here */
+ result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+
+ MTS_DEBUG("usb_set_interface returned %d.\n",result);
+ switch( result )
+ {
+ case 0: /* no error */
+ break;
+
+ case -EPIPE:
+ usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+ MTS_DEBUG( "clearing clearing stall on control interface\n" );
+ break;
+
+ default:
+ MTS_DEBUG( "unknown error %d from usb_set_interface\n",
+ (int)result );
+ return NULL;
+ }
+
+
+ /* allocating a new descriptor */
+ new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
+ if (new_desc == NULL)
+ {
+ MTS_ERROR("couldn't allocate scanner desc, bailing out!\n");
+ return NULL;
+ }
+
+ memset( new_desc, 0, sizeof(*new_desc) );
+ new_desc->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!new_desc->urb) {
+ kfree(new_desc);
+ return NULL;
+ }
+
+ /* initialising that descriptor */
+ new_desc->usb_dev = dev;
+ new_desc->interface = interface;
+
+ init_MUTEX(&new_desc->lock);
+
+ if(mts_list){
+ new_desc->host_number = mts_list->host_number+1;
+ } else {
+ new_desc->host_number = 0;
+ }
+
+ /* endpoints */
+
+ new_desc->ep_out = ep_out;
+ new_desc->ep_response = ep_in_set[0];
+ new_desc->ep_image = ep_in_set[1];
+
+
+ if ( new_desc->ep_out != MTS_EP_OUT )
+ MTS_WARNING( "will this work? Command EP is not usually %d\n",
+ (int)new_desc->ep_out );
+
+ if ( new_desc->ep_response != MTS_EP_RESPONSE )
+ MTS_WARNING( "will this work? Response EP is not usually %d\n",
+ (int)new_desc->ep_response );
+
+ if ( new_desc->ep_image != MTS_EP_IMAGE )
+ MTS_WARNING( "will this work? Image data EP is not usually %d\n",
+ (int)new_desc->ep_image );
+
+
+ /* Initialize the host template based on the default one */
+ memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template));
+ /* HACK from usb-storage - this is needed for scsi detection */
+ (struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */
+
+ MTS_DEBUG("registering SCSI module\n");
+
+ new_desc->ctempl.module = THIS_MODULE;
+ result = scsi_register_host(&new_desc->ctempl);
+ /* Will get hit back in microtek_detect by this func */
+ if ( result )
+ {
+ MTS_ERROR( "error %d from scsi_register_host! Help!\n",
+ (int)result );
+
+ /* FIXME: need more cleanup? */
+ kfree( new_desc );
+ return NULL;
+ }
+ MTS_DEBUG_GOT_HERE();
+
+ /* FIXME: the bomb is armed, must the host be registered under lock ? */
+ /* join the list - lock it */
+ down(&mts_list_semaphore);
+
+ mts_add_nolock( new_desc );
+
+ up(&mts_list_semaphore);
+
+
+ MTS_DEBUG("completed probe and exiting happily\n");
+
+ return (void *)new_desc;
+}
+
+
+
+/* get us noticed by the rest of the kernel */
+
+int __init microtek_drv_init(void)
+{
+ int result;
+
+ MTS_DEBUG_GOT_HERE();
+ init_MUTEX(&mts_list_semaphore);
+
+ if ((result = usb_register(&mts_usb_driver)) < 0) {
+ MTS_DEBUG("usb_register returned %d\n", result );
+ return -1;
+ } else {
+ MTS_DEBUG("driver registered.\n");
+ }
+
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+
+ return 0;
+}
+
+void __exit microtek_drv_exit(void)
+{
+ struct mts_desc* next;
+
+ MTS_DEBUG_GOT_HERE();
+
+ usb_deregister(&mts_usb_driver);
+
+ down(&mts_list_semaphore);
+
+ while (mts_list) {
+ /* keep track of where the next one is */
+ next = mts_list->next;
+
+ mts_remove_nolock( mts_list );
+
+ /* advance the list pointer */
+ mts_list = next;
+ }
+
+ up(&mts_list_semaphore);
+}
+
+module_init(microtek_drv_init);
+module_exit(microtek_drv_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+
--- /dev/null
+ /*
+ * Driver for Microtek Scanmaker X6 USB scanner and possibly others.
+ *
+ * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
+ * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ *
+ * See microtek.c for history
+ *
+ */
+
+typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
+typedef void (*mts_usb_urb_callback) (struct urb *);
+
+
+struct mts_transfer_context
+{
+ struct mts_desc* instance;
+ mts_scsi_cmnd_callback final_callback;
+ Scsi_Cmnd *srb;
+
+ void* data;
+ unsigned data_length;
+ int data_pipe;
+ int fragment;
+
+ u8 status; /* status returned from ep_response after command completion */
+};
+
+
+struct mts_desc {
+ struct mts_desc *next;
+ struct mts_desc *prev;
+
+ struct usb_device *usb_dev;
+
+ int interface;
+
+ /* Endpoint addresses */
+ u8 ep_out;
+ u8 ep_response;
+ u8 ep_image;
+
+ struct Scsi_Host * host;
+ Scsi_Host_Template ctempl;
+ int host_number;
+
+ struct semaphore lock;
+
+ struct urb *urb;
+ struct mts_transfer_context context;
+};
+
+
+#define MTS_EP_OUT 0x1
+#define MTS_EP_RESPONSE 0x2
+#define MTS_EP_IMAGE 0x3
+#define MTS_EP_TOTAL 0x3
+
+#define MTS_SCSI_ERR_MASK ~0x3fu
+
--- /dev/null
+/* -*- linux-c -*- */
+
+/*
+ * Driver for USB Scanners (linux-2.4.12)
+ *
+ * Copyright (C) 1999, 2000, 2001 David E. Nelson
+ *
+ * Portions may be copyright Brad Keryan and Michael Gee.
+ *
+ * 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.
+ *
+ * Originally 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.
+ *
+ *
+ * 0.2 10/16/1999
+ *
+ * - Device can't be opened unless a scanner is plugged into the USB.
+ * - Finally settled on a reasonable value for the I/O buffer's.
+ * - Cleaned up write_scanner()
+ * - Disabled read/write stats
+ * - A little more code cleanup
+ *
+ *
+ * 0.3 10/18/1999
+ *
+ * - Device registration changed to reflect new device
+ * allocation/registration for linux-2.3.22+.
+ * - Adopted David Brownell's <david-b@pacbell.net> technique for
+ * assigning bulk endpoints.
+ * - Removed unnessesary #include's
+ * - Scanner model now reported via syslog INFO after being detected
+ * *and* configured.
+ * - Added user specified vendor:product USB ID's which can be passed
+ * as module parameters.
+ *
+ *
+ * 0.3.1
+ *
+ * - Applied patches for linux-2.3.25.
+ * - Error number reporting changed to reflect negative return codes.
+ *
+ *
+ * 0.3.2
+ *
+ * - Applied patches for linux-2.3.26 to scanner_init().
+ * - Debug read/write stats now report values as signed decimal.
+ *
+ *
+ * 0.3.3
+ *
+ * - Updated the bulk_msg() calls to usb usb_bulk_msg().
+ * - Added a small delay in the write_scanner() method to aid in
+ * avoiding NULL data reads on HP scanners. We'll see how this works.
+ * - Return values from usb_bulk_msg() now ignore positive values for
+ * use with the ohci driver.
+ * - Added conditional debugging instead of commenting/uncommenting
+ * all over the place.
+ * - kfree()'d the pointer after using usb_string() as documented in
+ * linux-usb-api.txt.
+ * - Added usb_set_configuration(). It got lost in version 0.3 -- ack!
+ * - Added the HP 5200C USB Vendor/Product ID's.
+ *
+ *
+ * 0.3.4 1/23/2000
+ *
+ * - Added Greg K-H's <greg@kroah.com> patch for better handling of
+ * Product/Vendor detection.
+ * - The driver now autoconfigures its endpoints including interrupt
+ * endpoints if one is detected. The concept was originally based
+ * upon David Brownell's method.
+ * - Added some Seiko/Epson ID's. Thanks to Karl Heinz
+ * Kremer <khk@khk.net>.
+ * - Added some preliminary ioctl() calls for the PV8630 which is used
+ * by the HP4200. The ioctl()'s still have to be registered. Thanks
+ * to Adrian Perez Jorge <adrianpj@easynews.com>.
+ * - Moved/migrated stuff to scanner.h
+ * - Removed the usb_set_configuration() since this is handled by
+ * the usb_new_device() routine in usb.c.
+ * - Added the HP 3300C. Thanks to Bruce Tenison.
+ * - Changed user specified vendor/product id so that root hub doesn't
+ * get falsely attached to. Thanks to Greg K-H.
+ * - Added some Mustek ID's. Thanks to Gernot Hoyler
+ * <Dr.Hoyler@t-online.de>.
+ * - Modified the usb_string() reporting. See kfree() comment above.
+ * - Added Umax Astra 2000U. Thanks to Doug Alcorn <doug@lathi.net>.
+ * - Updated the printk()'s to use the info/warn/dbg macros.
+ * - Updated usb_bulk_msg() argument types to fix gcc warnings.
+ *
+ *
+ * 0.4 2/4/2000
+ *
+ * - Removed usb_string() from probe_scanner since the core now does a
+ * good job of reporting what was connnected.
+ * - Finally, simultaneous multiple device attachment!
+ * - Fixed some potential memory freeing issues should memory allocation
+ * fail in probe_scanner();
+ * - Some fixes to disconnect_scanner().
+ * - Added interrupt endpoint support.
+ * - Added Agfa SnapScan Touch. Thanks to Jan Van den Bergh
+ * <jan.vandenbergh@cs.kuleuven.ac.be>.
+ * - Added Umax 1220U ID's. Thanks to Maciek Klimkowski
+ * <mac@nexus.carleton.ca>.
+ * - Fixed bug in write_scanner(). The buffer was not being properly
+ * updated for writes larger than OBUF_SIZE. Thanks to Henrik
+ * Johansson <henrikjo@post.utfors.se> for identifying it.
+ * - Added Microtek X6 ID's. Thanks to Oliver Neukum
+ * <Oliver.Neukum@lrz.uni-muenchen.de>.
+ *
+ *
+ * 0.4.1 2/15/2000
+ *
+ * - Fixed 'count' bug in read_scanner(). Thanks to Henrik
+ * Johansson <henrikjo@post.utfors.se> for identifying it. Amazing
+ * it has worked this long.
+ * - Fixed '>=' bug in both read/write_scanner methods.
+ * - Cleaned up both read/write_scanner() methods so that they are
+ * a little more readable.
+ * - Added a lot of Microtek ID's. Thanks to Adrian Perez Jorge.
+ * - Adopted the __initcall().
+ * - Added #include <linux/init.h> to scanner.h for __initcall().
+ * - Added one liner in irq_scanner() to keep gcc from complaining
+ * about an unused variable (data) if debugging was disabled
+ * in scanner.c.
+ * - Increased the timeout parameter in read_scanner() to 120 Secs.
+ *
+ *
+ * 0.4.2 3/23/2000
+ *
+ * - Added Umax 1236U ID. Thanks to Philipp Baer <ph_baer@npw.net>.
+ * - Added Primax, ReadyScan, Visioneer, Colorado, and Genius ID's.
+ * Thanks to Adrian Perez Jorge <adrianpj@easynews.com>.
+ * - Fixed error number reported for non-existant devices. Thanks to
+ * Spyridon Papadimitriou <Spyridon_Papadimitriou@gs91.sp.cs.cmu.edu>.
+ * - Added Acer Prisascan 620U ID's. Thanks to Joao <joey@knoware.nl>.
+ * - Replaced __initcall() with module_init()/module_exit(). Updates
+ * from patch-2.3.48.
+ * - Replaced file_operations structure with new syntax. Updates
+ * from patch-2.3.49.
+ * - Changed #include "usb.h" to #include <linux/usb.h>
+ * - Added #define SCN_IOCTL to exclude development areas
+ * since 2.4.x is about to be released. This mainly affects the
+ * ioctl() stuff. See scanner.h for more details.
+ * - Changed the return value for signal_pending() from -ERESTARTSYS to
+ * -EINTR.
+ *
+ *
+ * 0.4.3 4/30/2000
+ *
+ * - Added Umax Astra 2200 ID. Thanks to Flynn Marquardt
+ * <flynn@isr.uni-stuttgart.de>.
+ * - Added iVina 1200U ID. Thanks to Dyson Lin <dyson@avision.com.tw>.
+ * - Added access time update for the device file courtesy of Paul
+ * Mackerras <paulus@samba.org>. This allows a user space daemon
+ * to turn the lamp off for a Umax 1220U scanner after a prescribed
+ * time.
+ * - Fixed HP S20 ID's. Thanks to Ruud Linders <rlinders@xs4all.nl>.
+ * - Added Acer ScanPrisa 620U ID. Thanks to Oliver
+ * Schwartz <Oliver.Schwartz@gmx.de> via sane-devel mail list.
+ * - Fixed bug in read_scanner for copy_to_user() function. The returned
+ * value should be 'partial' not 'this_read'.
+ * - Fixed bug in read_scanner. 'count' should be decremented
+ * by 'this_read' and not by 'partial'. This resulted in twice as many
+ * calls to read_scanner() for small amounts of data and possibly
+ * unexpected returns of '0'. Thanks to Karl Heinz
+ * Kremer <khk@khk.net> and Alain Knaff <Alain.Knaff@ltnb.lu>
+ * for discovering this.
+ * - Integrated Randy Dunlap's <randy.dunlap@intel.com> patch for a
+ * scanner lookup/ident table. Thanks Randy.
+ * - Documentation updates.
+ * - Added wait queues to read_scanner().
+ *
+ *
+ * 0.4.3.1
+ *
+ * - Fixed HP S20 ID's...again..sigh. Thanks to Ruud
+ * Linders <rlinders@xs4all.nl>.
+ *
+ * 0.4.4
+ * - Added addtional Mustek ID's (BearPaw 1200, 600 CU, 1200 USB,
+ * and 1200 UB. Thanks to Henning Meier-Geinitz <henningmg@gmx.de>.
+ * - Added the Vuego Scan Brisa 340U ID's. Apparently this scanner is
+ * marketed by Acer Peripherals as a cheap 300 dpi model. Thanks to
+ * David Gundersen <gundersd@paradise.net.nz>.
+ * - Added the Epson Expression1600 ID's. Thanks to Karl Heinz
+ * Kremer <khk@khk.net>.
+ *
+ * 0.4.5 2/28/2001
+ * - Added Mustek ID's (BearPaw 2400, 1200 CU Plus, BearPaw 1200F).
+ * Thanks to Henning Meier-Geinitz <henningmg@gmx.de>.
+ * - Added read_timeout module parameter to override RD_NAK_TIMEOUT
+ * when read()'ing from devices.
+ * - Stalled pipes are now checked and cleared with
+ * usb_clear_halt() for the read_scanner() function. This should
+ * address the "funky result: -32" error messages.
+ * - Removed Microtek scanner ID's. Microtek scanners are now
+ * supported via the drivers/usb/microtek.c driver.
+ * - Added scanner specific read timeout's.
+ * - Return status errors are NEGATIVE!!! This should address the
+ * "funky result: -110" error messages.
+ * - Replaced USB_ST_TIMEOUT with ETIMEDOUT.
+ * - rd_nak was still defined in MODULE_PARM. It's been updated with
+ * read_timeout. Thanks to Mark W. Webb <markwebb@adelphia.net> for
+ * reporting this bug.
+ * - Added Epson Perfection 1640SU and 1640SU Photo. Thanks to
+ * Jean-Luc <f5ibh@db0bm.ampr.org> and Manuel
+ * Pelayo <Manuel.Pelayo@sesips.org>. Reported to work fine by Manuel.
+ *
+ * 0.4.6 9/27/2001
+ * - Added IOCTL's to report back scanner USB ID's. Thanks to
+ * Karl Heinz <khk@lynx.phpwebhosting.com>
+ * - Added Umax Astra 2100U ID's. Thanks to Ron
+ * Wellsted <ron@wellsted.org.uk>.
+ * and Manuel Pelayo <Manuel.Pelayo@sesips.org>.
+ * - Added HP 3400 ID's. Thanks to Harald Hannelius <harald@iki.fi>
+ * and Bertrik Sikken <bertrik@zonnet.nl>. Reported to work at
+ * htpp://home.zonnet.nl/bertrik/hp3300c/hp3300c.htm.
+ * - Added Minolta Dimage Scan Dual II ID's. Thanks to Jose Paulo
+ * Moitinho de Almeida <moitinho@civil.ist.utl.pt>
+ * - Confirmed addition for SnapScan E20. Thanks to Steffen Hübner
+ * <hueb_s@gmx.de>.
+ * - Added Lifetec LT9385 ID's. Thanks to Van Bruwaene Kris
+ * <krvbr@yahoo.co.uk>
+ * - Added Agfa SnapScan e26 ID's. Reported to work with SANE
+ * 1.0.5. Thanks to Falk Sauer <falk@mgnkatze.franken.de>.
+ * - Added HP 4300 ID's. Thanks to Stefan Schlosser
+ * <castla@grmmbl.org>.
+ * - Added Relisis Episode ID's. Thanks to Manfred
+ * Morgner <odb-devel@gmx.net>.
+ * - Added many Acer ID's. Thanks to Oliver
+ * Schwartz <Oliver.Schwartz@gmx.de>.
+ * - Added Snapscan e40 ID's. Thanks to Oliver
+ * Schwartz <Oliver.Schwartz@gmx.de>.
+ * - Thanks to Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
+ * for helping with races.
+ * - Added Epson Perfection 1650 ID's. Thanks to Karl Heinz
+ * Kremer <khk@khk.net>.
+ * - Added Epson Perfection 2450 ID's (aka GT-9700 for the Japanese
+ * market). Thanks to Karl Heinz Kremer <khk@khk.net>.
+ * - Added Mustek 600 USB ID's. Thanks to Marcus
+ * Alanen <maalanen@ra.abo.fi>.
+ * - Added Acer ScanPrisa 1240UT ID's. Thanks to Morgan
+ * Collins <sirmorcant@morcant.org>.
+ * - Incorporated devfs patches!! Thanks to Tom Rini
+ * <trini@kernel.crashing.org>, Pavel Roskin <proski@gnu.org>,
+ * Greg KH <greg@kroah.com>, Yves Duret <yduret@mandrakesoft.com>,
+ * Flavio Stanchina <flavio.stanchina@tin.it>.
+ * - Removed Minolta ScanImage II. This scanner uses USB SCSI. Thanks
+ * to Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de> for pointing
+ * this out.
+ * - Added additional SMP locking. Thanks to David Brownell and
+ * Oliver Neukum for their help.
+ * - Added version reporting - reports for both module load and modinfo
+ * - Started path to hopefully straighten/clean out ioctl()'s.
+ * - Users are now notified to consult the Documentation/usb/scanner.txt
+ * for common error messages rather than the maintainer.
+ *
+ * 0.4.7 11/28/2001
+ * - Fixed typo in Documentation/scanner.txt. Thanks to
+ * Karel <karel.vervaeke@pandora.be> for pointing it out.
+ * - Added ID's for a Memorex 6136u. Thanks to Álvaro Gaspar de
+ * Valenzuela" <agaspard@utsi.edu>.
+ * - Added ID's for Agfa e25. Thanks to Heinrich
+ * Rust <Heinrich.Rust@gmx.de>. Also reported to work with
+ * Linux and SANE (?).
+ * - Added Canon FB620U, D646U, and 1220U ID's. Thanks to Paul
+ * Rensing <Paul_Rensing@StanfordAlumni.org>. For more info
+ * on Linux support for these models, contact
+ * salvestrini@users.sourceforge.net.
+ * - Added Plustek OpticPro UT12, OpticPro U24, KYE/Genius
+ * ColorPage-HR6 V2 ID's in addition to many "Unknown" models
+ * under those vendors. Thanks to
+ * Jaeger, Gerhard" <g.jaeger@earthling.net>. These scanner are
+ * apparently based upon the LM983x IC's.
+ * - Applied Frank's patch that addressed some locking and module
+ * referencing counts. Thanks to both
+ * Frank Zago <fzago@greshamstorage.com> and
+ * Oliver Neukum <520047054719-0001@t-online.de> for reviewing/testing.
+ *
+ * TODO
+ * - Performance
+ * - Select/poll methods
+ * - More testing
+ * - Proper registry/assignment for LM9830 ioctl's
+ *
+ *
+ * Thanks to:
+ *
+ * - All the folks on the linux-usb list who put up with me. :) This
+ * has been a great learning experience for me.
+ * - To Linus Torvalds for this great OS.
+ * - The GNU folks.
+ * - The folks that forwarded Vendor:Product ID's to me.
+ * - Johannes Erdfelt for the loaning of a USB analyzer for tracking an
+ * issue with HP-4100 and uhci.
+ * - Adolfo Montero for his assistance.
+ * - All the folks who chimed in with reports and suggestions.
+ * - All the developers that are working on USB SANE backends or other
+ * applications to use USB scanners.
+ *
+ * Performance:
+ *
+ * System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner
+ * 300 dpi scan of the entire bed
+ * 24 Bit Color ~ 70 secs - 3.6 Mbit/sec
+ * 8 Bit Gray ~ 17 secs - 4.2 Mbit/sec */
+
+#include <asm/byteorder.h>
+
+/*
+ * Scanner definitions, macros, module info,
+ * debug/ioctl/data_dump enable, and other constants.
+ */
+#include "scanner.h"
+
+static void
+irq_scanner(struct urb *urb)
+{
+
+/*
+ * For the meantime, this is just a placeholder until I figure out what
+ * all I want to do with it -- or somebody else for that matter.
+ */
+
+ struct scn_usb_data *scn;
+ unsigned char *data;
+ scn = urb->context;
+
+ data = &scn->button;
+ data += 0; /* Keep gcc from complaining about unused var */
+
+ if (urb->status) {
+ return;
+ }
+
+ dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data);
+
+ return;
+}
+
+static int
+open_scanner(struct inode * inode, struct file * file)
+{
+ struct scn_usb_data *scn;
+ struct usb_device *dev;
+
+ int scn_minor;
+
+ int err=0;
+
+ MOD_INC_USE_COUNT;
+
+ down(&scn_mutex);
+
+ scn_minor = USB_SCN_MINOR(inode);
+
+ dbg("open_scanner: scn_minor:%d", scn_minor);
+
+ if (!p_scn_table[scn_minor]) {
+ up(&scn_mutex);
+ MOD_DEC_USE_COUNT;
+ err("open_scanner(%d): Unable to access minor data", scn_minor);
+ return -ENODEV;
+ }
+
+ scn = p_scn_table[scn_minor];
+
+ dev = scn->scn_dev;
+
+ down(&(scn->sem)); /* Now protect the scn_usb_data structure */
+
+ up(&scn_mutex); /* Now handled by the above */
+
+ if (!dev) {
+ err("open_scanner(%d): Scanner device not present", scn_minor);
+ err = -ENODEV;
+ goto out_error;
+ }
+
+ if (!scn->present) {
+ err("open_scanner(%d): Scanner is not present", scn_minor);
+ err = -ENODEV;
+ goto out_error;
+ }
+
+ if (scn->isopen) {
+ err("open_scanner(%d): Scanner device is already open", scn_minor);
+ err = -EBUSY;
+ goto out_error;
+ }
+
+ init_waitqueue_head(&scn->rd_wait_q);
+
+ scn->isopen = 1;
+
+ file->private_data = scn; /* Used by the read and write methods */
+
+
+out_error:
+
+ up(&(scn->sem)); /* Wake up any possible contending processes */
+
+ if (err)
+ MOD_DEC_USE_COUNT;
+
+ return err;
+}
+
+static int
+close_scanner(struct inode * inode, struct file * file)
+{
+ struct scn_usb_data *scn;
+
+ int scn_minor;
+
+ scn_minor = USB_SCN_MINOR (inode);
+
+ dbg("close_scanner: scn_minor:%d", scn_minor);
+
+ if (!p_scn_table[scn_minor]) {
+ err("close_scanner(%d): invalid scn_minor", scn_minor);
+ return -ENODEV;
+ }
+
+ down(&scn_mutex);
+
+ scn = p_scn_table[scn_minor];
+ down(&(scn->sem));
+ scn->isopen = 0;
+
+ file->private_data = NULL;
+
+ up(&scn_mutex);
+ up(&(scn->sem));
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static ssize_t
+write_scanner(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct scn_usb_data *scn;
+ struct usb_device *dev;
+
+ ssize_t bytes_written = 0; /* Overall count of bytes written */
+ ssize_t ret = 0;
+
+ int scn_minor;
+
+ int this_write; /* Number of bytes to write */
+ int partial; /* Number of bytes successfully written */
+ int result = 0;
+
+ char *obuf;
+
+ scn = file->private_data;
+
+ down(&(scn->sem));
+
+ scn_minor = scn->scn_minor;
+
+ obuf = scn->obuf;
+
+ dev = scn->scn_dev;
+
+ file->f_dentry->d_inode->i_atime = CURRENT_TIME;
+
+ while (count > 0) {
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count;
+
+ if (copy_from_user(scn->obuf, buffer, this_write)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ);
+ dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor, result, this_write, partial);
+
+ if (result == -ETIMEDOUT) { /* NAK -- shouldn't happen */
+ warn("write_scanner: NAK received.");
+ ret = result;
+ break;
+ } else if (result < 0) { /* We should not get any I/O errors */
+ warn("write_scanner(%d): funky result: %d. Consult Documentataion/usb/scanner.txt.", scn_minor, result);
+ ret = -EIO;
+ break;
+ }
+
+#ifdef WR_DATA_DUMP
+ if (partial) {
+ unsigned char cnt, cnt_max;
+ cnt_max = (partial > 24) ? 24 : partial;
+ printk(KERN_DEBUG "dump(%d): ", scn_minor);
+ for (cnt=0; cnt < cnt_max; cnt++) {
+ printk("%X ", obuf[cnt]);
+ }
+ printk("\n");
+ }
+#endif
+ if (partial != this_write) { /* Unable to write all contents of obuf */
+ ret = -EIO;
+ break;
+ }
+
+ if (partial) { /* Data written */
+ buffer += partial;
+ count -= partial;
+ bytes_written += partial;
+ } else { /* No data written */
+ ret = 0;
+ break;
+ }
+ }
+ up(&(scn->sem));
+ mdelay(5); /* This seems to help with SANE queries */
+ return ret ? ret : bytes_written;
+}
+
+static ssize_t
+read_scanner(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct scn_usb_data *scn;
+ struct usb_device *dev;
+
+ ssize_t bytes_read; /* Overall count of bytes_read */
+ ssize_t ret;
+
+ int scn_minor;
+ int partial; /* Number of bytes successfully read */
+ int this_read; /* Max number of bytes to read */
+ int result;
+ int rd_expire = RD_EXPIRE;
+
+ char *ibuf;
+
+ scn = file->private_data;
+
+ down(&(scn->sem));
+
+ scn_minor = scn->scn_minor;
+
+ ibuf = scn->ibuf;
+
+ dev = scn->scn_dev;
+
+ bytes_read = 0;
+ ret = 0;
+
+ file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* Update the
+ atime of
+ the device
+ node */
+ while (count > 0) {
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
+
+ result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, scn->rd_nak_timeout);
+ dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d", scn_minor, result, this_read, partial, count);
+
+/*
+ * Scanners are sometimes inheriently slow since they are mechanical
+ * in nature. USB bulk reads tend to timeout while the scanner is
+ * positioning, resetting, warming up the lamp, etc if the timeout is
+ * set too low. A very long timeout parameter for bulk reads was used
+ * to overcome this limitation, but this sometimes resulted in folks
+ * having to wait for the timeout to expire after pressing Ctrl-C from
+ * an application. The user was sometimes left with the impression
+ * that something had hung or crashed when in fact the USB read was
+ * just waiting on data. So, the below code retains the same long
+ * timeout period, but splits it up into smaller parts so that
+ * Ctrl-C's are acted upon in a reasonable amount of time.
+ */
+
+ if (result == -ETIMEDOUT) { /* NAK */
+ if (!partial) { /* No data */
+ if (--rd_expire <= 0) { /* Give it up */
+ warn("read_scanner(%d): excessive NAK's received", scn_minor);
+ ret = result;
+ break;
+ } else { /* Keep trying to read data */
+ interruptible_sleep_on_timeout(&scn->rd_wait_q, scn->rd_nak_timeout);
+ continue;
+ }
+ } else { /* Timeout w/ some data */
+ goto data_recvd;
+ }
+ }
+
+ if (result == -EPIPE) { /* No hope */
+ if(usb_clear_halt(dev, scn->bulk_in_ep)) {
+ err("read_scanner(%d): Failure to clear endpoint halt condition (%Zd).", scn_minor, ret);
+ }
+ ret = result;
+ break;
+ } else if ((result < 0) && (result != -EREMOTEIO)) {
+ warn("read_scanner(%d): funky result:%d. Consult Documentation/usb/scanner.txt.", scn_minor, (int)result);
+ ret = -EIO;
+ break;
+ }
+
+ data_recvd:
+
+#ifdef RD_DATA_DUMP
+ if (partial) {
+ unsigned char cnt, cnt_max;
+ cnt_max = (partial > 24) ? 24 : partial;
+ printk(KERN_DEBUG "dump(%d): ", scn_minor);
+ for (cnt=0; cnt < cnt_max; cnt++) {
+ printk("%X ", ibuf[cnt]);
+ }
+ printk("\n");
+ }
+#endif
+
+ if (partial) { /* Data returned */
+ if (copy_to_user(buffer, ibuf, partial)) {
+ ret = -EFAULT;
+ break;
+ }
+ count -= this_read; /* Compensate for short reads */
+ bytes_read += partial; /* Keep tally of what actually was read */
+ buffer += partial;
+ } else {
+ ret = 0;
+ break;
+ }
+ }
+ up(&(scn->sem));
+ return ret ? ret : bytes_read;
+}
+
+static int
+ioctl_scanner(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_device *dev;
+
+ int scn_minor;
+
+ scn_minor = USB_SCN_MINOR(inode);
+
+ if (!p_scn_table[scn_minor]) {
+ err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
+ return -ENODEV;
+ }
+
+ dev = p_scn_table[scn_minor]->scn_dev;
+
+ switch (cmd)
+ {
+ case SCANNER_IOCTL_VENDOR :
+ return (put_user(dev->descriptor.idVendor, (unsigned int *) arg));
+ case SCANNER_IOCTL_PRODUCT :
+ return (put_user(dev->descriptor.idProduct, (unsigned int *) arg));
+#ifdef PV8630
+ case PV8630_IOCTL_INREQUEST :
+ {
+ int result;
+
+ struct {
+ __u8 data;
+ __u8 request;
+ __u16 value;
+ __u16 index;
+ } args;
+
+ if (copy_from_user(&args, (void *)arg, sizeof(args)))
+ return -EFAULT;
+
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ args.request, USB_TYPE_VENDOR|
+ USB_RECIP_DEVICE|USB_DIR_IN,
+ args.value, args.index, &args.data,
+ 1, HZ*5);
+
+ dbg("ioctl_scanner(%d): inreq: args.data:%x args.value:%x args.index:%x args.request:%x\n", scn_minor, args.data, args.value, args.index, args.request);
+
+ if (copy_to_user((void *)arg, &args, sizeof(args)))
+ return -EFAULT;
+
+ dbg("ioctl_scanner(%d): inreq: result:%d\n", scn_minor, result);
+
+ return result;
+ }
+ case PV8630_IOCTL_OUTREQUEST :
+ {
+ int result;
+
+ struct {
+ __u8 request;
+ __u16 value;
+ __u16 index;
+ } args;
+
+ if (copy_from_user(&args, (void *)arg, sizeof(args)))
+ return -EFAULT;
+
+ dbg("ioctl_scanner(%d): outreq: args.value:%x args.index:%x args.request:%x\n", scn_minor, args.value, args.index, args.request);
+
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ args.request, USB_TYPE_VENDOR|
+ USB_RECIP_DEVICE|USB_DIR_OUT,
+ args.value, args.index, NULL,
+ 0, HZ*5);
+
+ dbg("ioctl_scanner(%d): outreq: result:%d\n", scn_minor, result);
+
+ return result;
+ }
+#endif /* PV8630 */
+ case SCANNER_IOCTL_CTRLMSG:
+ {
+ struct ctrlmsg_ioctl {
+ struct usb_ctrlrequest req;
+ void *data;
+ } cmsg;
+ int pipe, nb, ret;
+ unsigned char buf[64];
+
+ if (copy_from_user(&cmsg, (void *)arg, sizeof(cmsg)))
+ return -EFAULT;
+
+ nb = le16_to_cpup(&cmsg.req.wLength);
+
+ if (nb > sizeof(buf))
+ return -EINVAL;
+
+ if ((cmsg.req.bRequestType & 0x80) == 0) {
+ pipe = usb_sndctrlpipe(dev, 0);
+ if (nb > 0 && copy_from_user(buf, cmsg.data, nb))
+ return -EFAULT;
+ } else {
+ pipe = usb_rcvctrlpipe(dev, 0);
+ }
+
+ ret = usb_control_msg(dev, pipe, cmsg.req.bRequest,
+ cmsg.req.bRequestType,
+ le16_to_cpup(&cmsg.req.wValue),
+ le16_to_cpup(&cmsg.req.wIndex),
+ buf, nb, HZ);
+
+ if (ret < 0) {
+ err("ioctl_scanner(%d): control_msg returned %d\n", scn_minor, ret);
+ return -EIO;
+ }
+
+ if (nb > 0 && (cmsg.req.bRequestType & 0x80) && copy_to_user(cmsg.data, buf, nb))
+ return -EFAULT;
+
+ return 0;
+ }
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static struct
+file_operations usb_scanner_fops = {
+ read: read_scanner,
+ write: write_scanner,
+ ioctl: ioctl_scanner,
+ open: open_scanner,
+ release: close_scanner,
+};
+
+static void *
+probe_scanner(struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *id)
+{
+ struct scn_usb_data *scn;
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+
+ int ep_cnt;
+ int ix;
+ int scn_minor;
+
+ char valid_device = 0;
+ char have_bulk_in, have_bulk_out, have_intr;
+ char name[10];
+
+ if (vendor != -1 && product != -1) {
+ info("probe_scanner: User specified USB scanner -- Vendor:Product - %x:%x", vendor, product);
+ }
+
+ dbg("probe_scanner: USB dev address:%p", dev);
+ dbg("probe_scanner: ifnum:%u", ifnum);
+
+/*
+ * 1. Check Vendor/Product
+ * 2. Determine/Assign Bulk Endpoints
+ * 3. Determine/Assign Intr Endpoint
+ */
+
+/*
+ * There doesn't seem to be an imaging class defined in the USB
+ * Spec. (yet). If there is, HP isn't following it and it doesn't
+ * look like anybody else is either. Therefore, we have to test the
+ * Vendor and Product ID's to see what we have. Also, other scanners
+ * may be able to use this driver by specifying both vendor and
+ * product ID's as options to the scanner module in conf.modules.
+ *
+ * NOTE: Just because a product is supported here does not mean that
+ * applications exist that support the product. It's in the hopes
+ * that this will allow developers a means to produce applications
+ * that will support USB products.
+ *
+ * Until we detect a device which is pleasing, we silently punt.
+ */
+
+ for (ix = 0; ix < sizeof (scanner_device_ids) / sizeof (struct usb_device_id); ix++) {
+ if ((dev->descriptor.idVendor == scanner_device_ids [ix].idVendor) &&
+ (dev->descriptor.idProduct == scanner_device_ids [ix].idProduct)) {
+ valid_device = 1;
+ break;
+ }
+ }
+ if (dev->descriptor.idVendor == vendor && /* User specified */
+ dev->descriptor.idProduct == product) { /* User specified */
+ valid_device = 1;
+ }
+
+ if (!valid_device)
+ return NULL; /* We didn't find anything pleasing */
+
+/*
+ * After this point we can be a little noisy about what we are trying to
+ * configure.
+ */
+
+ if (dev->descriptor.bNumConfigurations != 1) {
+ info("probe_scanner: Only one device configuration is supported.");
+ return NULL;
+ }
+
+ if (dev->config[0].bNumInterfaces != 1) {
+ info("probe_scanner: Only one device interface is supported.");
+ return NULL;
+ }
+
+ interface = dev->config[0].interface[ifnum].altsetting;
+ endpoint = interface[ifnum].endpoint;
+
+/*
+ * Start checking for two bulk endpoints OR two bulk endpoints *and* one
+ * interrupt endpoint. If we have an interrupt endpoint go ahead and
+ * setup the handler. FIXME: This is a future enhancement...
+ */
+
+ dbg("probe_scanner: Number of Endpoints:%d", (int) interface->bNumEndpoints);
+
+ if ((interface->bNumEndpoints != 2) && (interface->bNumEndpoints != 3)) {
+ info("probe_scanner: Only two or three endpoints supported.");
+ return NULL;
+ }
+
+ ep_cnt = have_bulk_in = have_bulk_out = have_intr = 0;
+
+ while (ep_cnt < interface->bNumEndpoints) {
+
+ if (!have_bulk_in && IS_EP_BULK_IN(endpoint[ep_cnt])) {
+ ep_cnt++;
+ have_bulk_in = ep_cnt;
+ dbg("probe_scanner: bulk_in_ep:%d", have_bulk_in);
+ continue;
+ }
+
+ if (!have_bulk_out && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
+ ep_cnt++;
+ have_bulk_out = ep_cnt;
+ dbg("probe_scanner: bulk_out_ep:%d", have_bulk_out);
+ continue;
+ }
+
+ if (!have_intr && IS_EP_INTR(endpoint[ep_cnt])) {
+ ep_cnt++;
+ have_intr = ep_cnt;
+ dbg("probe_scanner: intr_ep:%d", have_intr);
+ continue;
+ }
+ info("probe_scanner: Undetected endpoint -- consult Documentation/usb/scanner.txt.");
+ return NULL; /* Shouldn't ever get here unless we have something weird */
+ }
+
+
+/*
+ * Perform a quick check to make sure that everything worked as it
+ * should have.
+ */
+
+ switch(interface->bNumEndpoints) {
+ case 2:
+ if (!have_bulk_in || !have_bulk_out) {
+ info("probe_scanner: Two bulk endpoints required.");
+ return NULL;
+ }
+ break;
+ case 3:
+ if (!have_bulk_in || !have_bulk_out || !have_intr) {
+ info("probe_scanner: Two bulk endpoints and one interrupt endpoint required.");
+ return NULL;
+ }
+ break;
+ default:
+ info("probe_scanner: Endpoint determination failed -- consult Documentation/usb/scanner.txt");
+ return NULL;
+ }
+
+
+/*
+ * Determine a minor number and initialize the structure associated
+ * with it. The problem with this is that we are counting on the fact
+ * that the user will sequentially add device nodes for the scanner
+ * devices. */
+
+ down(&scn_mutex);
+
+ for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
+ if (!p_scn_table[scn_minor])
+ break;
+ }
+
+/* Check to make sure that the last slot isn't already taken */
+ if (p_scn_table[scn_minor]) {
+ err("probe_scanner: No more minor devices remaining.");
+ up(&scn_mutex);
+ return NULL;
+ }
+
+ dbg("probe_scanner: Allocated minor:%d", scn_minor);
+
+ if (!(scn = kmalloc (sizeof (struct scn_usb_data), GFP_KERNEL))) {
+ err("probe_scanner: Out of memory.");
+ up(&scn_mutex);
+ return NULL;
+ }
+ memset (scn, 0, sizeof(struct scn_usb_data));
+
+ scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!scn->scn_irq) {
+ kfree(scn);
+ up(&scn_mutex);
+ return NULL;
+ }
+
+ init_MUTEX(&(scn->sem)); /* Initializes to unlocked */
+
+ dbg ("probe_scanner(%d): Address of scn:%p", scn_minor, scn);
+
+/* Ok, if we detected an interrupt EP, setup a handler for it */
+ if (have_intr) {
+ dbg("probe_scanner(%d): Configuring IRQ handler for intr EP:%d", scn_minor, have_intr);
+ FILL_INT_URB(scn->scn_irq, dev,
+ usb_rcvintpipe(dev, have_intr),
+ &scn->button, 1, irq_scanner, scn,
+ // endpoint[(int)have_intr].bInterval);
+ 250);
+
+ if (usb_submit_urb(scn->scn_irq, GFP_KERNEL)) {
+ err("probe_scanner(%d): Unable to allocate INT URB.", scn_minor);
+ kfree(scn);
+ up(&scn_mutex);
+ return NULL;
+ }
+ }
+
+
+/* Ok, now initialize all the relevant values */
+ if (!(scn->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+ err("probe_scanner(%d): Not enough memory for the output buffer.", scn_minor);
+ kfree(scn);
+ up(&scn_mutex);
+ return NULL;
+ }
+ dbg("probe_scanner(%d): obuf address:%p", scn_minor, scn->obuf);
+
+ if (!(scn->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+ err("probe_scanner(%d): Not enough memory for the input buffer.", scn_minor);
+ kfree(scn->obuf);
+ kfree(scn);
+ up(&scn_mutex);
+ return NULL;
+ }
+ dbg("probe_scanner(%d): ibuf address:%p", scn_minor, scn->ibuf);
+
+
+ switch (dev->descriptor.idVendor) { /* Scanner specific read timeout parameters */
+ case 0x04b8: /* Seiko/Epson */
+ scn->rd_nak_timeout = HZ * 40;
+ break;
+ case 0x055f: /* Mustek */
+ case 0x0400: /* Another Mustek */
+ case 0x0ff5: /* And yet another Mustek */
+ scn->rd_nak_timeout = HZ * 1;
+ default:
+ scn->rd_nak_timeout = RD_NAK_TIMEOUT;
+ }
+
+
+ if (read_timeout > 0) { /* User specified read timeout overrides everything */
+ info("probe_scanner: User specified USB read timeout - %d", read_timeout);
+ scn->rd_nak_timeout = read_timeout;
+ }
+
+
+ scn->bulk_in_ep = have_bulk_in;
+ scn->bulk_out_ep = have_bulk_out;
+ scn->intr_ep = have_intr;
+ scn->present = 1;
+ scn->scn_dev = dev;
+ scn->scn_minor = scn_minor;
+ scn->isopen = 0;
+
+ sprintf(name, "scanner%d", scn->scn_minor);
+
+ scn->devfs = devfs_register(usb_devfs_handle, name,
+ DEVFS_FL_DEFAULT, USB_MAJOR,
+ SCN_BASE_MNR + scn->scn_minor,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH, &usb_scanner_fops, NULL);
+ if (scn->devfs == NULL)
+ dbg("scanner%d: device node registration failed", scn_minor);
+
+ p_scn_table[scn_minor] = scn;
+
+ up(&scn_mutex);
+
+ return scn;
+}
+
+static void
+disconnect_scanner(struct usb_device *dev, void *ptr)
+{
+ struct scn_usb_data *scn = (struct scn_usb_data *) ptr;
+
+ down (&scn_mutex);
+ down (&(scn->sem));
+
+ if(scn->intr_ep) {
+ dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor);
+ usb_unlink_urb(scn->scn_irq);
+ }
+ usb_driver_release_interface(&scanner_driver,
+ &scn->scn_dev->actconfig->interface[scn->ifnum]);
+
+ kfree(scn->ibuf);
+ kfree(scn->obuf);
+
+ dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
+ devfs_unregister(scn->devfs);
+ p_scn_table[scn->scn_minor] = NULL;
+ usb_free_urb(scn->scn_irq);
+ up (&(scn->sem));
+ kfree (scn);
+ up (&scn_mutex);
+}
+
+static struct
+usb_driver scanner_driver = {
+ name: "usbscanner",
+ probe: probe_scanner,
+ disconnect: disconnect_scanner,
+ fops: &usb_scanner_fops,
+ minor: SCN_BASE_MNR,
+ id_table: NULL, /* This would be scanner_device_ids, but we
+ need to check every USB device, in case
+ we match a user defined vendor/product ID. */
+};
+
+void __exit
+usb_scanner_exit(void)
+{
+ usb_deregister(&scanner_driver);
+}
+
+int __init
+usb_scanner_init (void)
+{
+ if (usb_register(&scanner_driver) < 0)
+ return -1;
+
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return 0;
+}
+
+module_init(usb_scanner_init);
+module_exit(usb_scanner_exit);
--- /dev/null
+/*
+ * Driver for USB Scanners (linux-2.4.12)
+ *
+ * Copyright (C) 1999, 2000, 2001 David E. Nelson
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+
+// #define DEBUG
+
+/* Enable this to support the older ioctl interfaces scanners that
+ * a PV8630 Scanner-On-Chip. The prefered method is the
+ * SCANNER_IOCTL_CTRLMSG ioctl.
+ */
+// #define PV8630
+
+#define DRIVER_VERSION "0.4.6"
+#define DRIVER_DESC "USB Scanner Driver"
+
+#include <linux/usb.h>
+
+static __s32 vendor=-1, product=-1, read_timeout=0;
+
+MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson");
+MODULE_DESCRIPTION(DRIVER_DESC" "DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+MODULE_PARM(read_timeout, "i");
+MODULE_PARM_DESC(read_timeout, "User specified read timeout in seconds");
+
+
+/* WARNING: These DATA_DUMP's can produce a lot of data. Caveat Emptor. */
+// #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
+// #define WR_DATA_DUMP /* DEBUG does not have to be defined. */
+
+static struct usb_device_id scanner_device_ids [] = {
+ /* Acer */
+ { USB_DEVICE(0x04a5, 0x2060) }, /* Prisa Acerscan 620U & 640U (!)*/
+ { USB_DEVICE(0x04a5, 0x2040) }, /* Prisa AcerScan 620U (!) */
+ { USB_DEVICE(0x04a5, 0x20c0) }, /* Prisa AcerScan 1240UT */
+ { USB_DEVICE(0x04a5, 0x2022) }, /* Vuego Scan Brisa 340U */
+ { USB_DEVICE(0x04a5, 0x1a20) }, /* Unknown - Oliver Schwartz */
+ { USB_DEVICE(0x04a5, 0x1a2a) }, /* Unknown - Oliver Schwartz */
+ { USB_DEVICE(0x04a5, 0x207e) }, /* Prisa 640BU */
+ { USB_DEVICE(0x04a5, 0x20be) }, /* Unknown - Oliver Schwartz */
+ { USB_DEVICE(0x04a5, 0x20c0) }, /* Unknown - Oliver Schwartz */
+ { USB_DEVICE(0x04a5, 0x20de) }, /* S2W 3300U */
+ { USB_DEVICE(0x04a5, 0x20b0) }, /* Unknown - Oliver Schwartz */
+ { USB_DEVICE(0x04a5, 0x20fe) }, /* Unknown - Oliver Schwartz */
+ /* Agfa */
+ { USB_DEVICE(0x06bd, 0x0001) }, /* SnapScan 1212U */
+ { USB_DEVICE(0x06bd, 0x0002) }, /* SnapScan 1236U */
+ { USB_DEVICE(0x06bd, 0x2061) }, /* Another SnapScan 1212U (?)*/
+ { USB_DEVICE(0x06bd, 0x0100) }, /* SnapScan Touch */
+ { USB_DEVICE(0x06bd, 0x2091) }, /* SnapScan e20 */
+ { USB_DEVICE(0x06bd, 0x2095) }, /* SnapScan e25 */
+ { USB_DEVICE(0x06bd, 0x2097) }, /* SnapScan e26 */
+ { USB_DEVICE(0x06bd, 0x208d) }, /* Snapscan e40 */
+ /* Canon */
+ { USB_DEVICE(0x04a9, 0x2202) }, /* FB620U */
+ { USB_DEVICE(0x04a9, 0x220b) }, /* D646U */
+ { USB_DEVICE(0x04a9, 0x2207) }, /* 1220U */
+ /* Colorado -- See Primax/Colorado below */
+ /* Epson -- See Seiko/Epson below */
+ /* Genius */
+ { USB_DEVICE(0x0458, 0x2001) }, /* ColorPage-Vivid Pro */
+ { USB_DEVICE(0x0458, 0x2007) }, /* ColorPage HR6 V2 */
+ { USB_DEVICE(0x0458, 0x2008) }, /* Unknown */
+ { USB_DEVICE(0x0458, 0x2009) }, /* Unknown */
+ { USB_DEVICE(0x0458, 0x2013) }, /* Unknown */
+ { USB_DEVICE(0x0458, 0x2015) }, /* Unknown */
+ { USB_DEVICE(0x0458, 0x2016) }, /* Unknown */
+ /* Hewlett Packard */
+ { USB_DEVICE(0x03f0, 0x0205) }, /* 3300C */
+ { USB_DEVICE(0x03f0, 0x0405) }, /* 3400C */
+ { USB_DEVICE(0x03f0, 0x0101) }, /* 4100C */
+ { USB_DEVICE(0x03f0, 0x0105) }, /* 4200C */
+ { USB_DEVICE(0x03f0, 0x0305) }, /* 4300C */
+ { USB_DEVICE(0x03f0, 0x0102) }, /* PhotoSmart S20 */
+ { USB_DEVICE(0x03f0, 0x0401) }, /* 5200C */
+ // { USB_DEVICE(0x03f0, 0x0701) }, /* 5300C - NOT SUPPORTED - see http://www.neatech.nl/oss/HP5300C/ */
+ { USB_DEVICE(0x03f0, 0x0201) }, /* 6200C */
+ { USB_DEVICE(0x03f0, 0x0601) }, /* 6300C */
+ { USB_DEVICE(0x03f0, 0x605) }, /* 2200C */
+ /* iVina */
+ { USB_DEVICE(0x0638, 0x0268) }, /* 1200U */
+ /* Lifetec */
+ { USB_DEVICE(0x05d8, 0x4002) }, /* Lifetec LT9385 */
+ /* Memorex */
+ { USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */
+ /* Microtek -- No longer supported - Enable SCSI and USB Microtek in kernel config */
+ // { USB_DEVICE(0x05da, 0x0099) }, /* ScanMaker X6 - X6U */
+ // { USB_DEVICE(0x05da, 0x0094) }, /* Phantom 336CX - C3 */
+ // { USB_DEVICE(0x05da, 0x00a0) }, /* Phantom 336CX - C3 #2 */
+ // { USB_DEVICE(0x05da, 0x009a) }, /* Phantom C6 */
+ // { USB_DEVICE(0x05da, 0x00a3) }, /* ScanMaker V6USL */
+ // { USB_DEVICE(0x05da, 0x80a3) }, /* ScanMaker V6USL #2 */
+ // { USB_DEVICE(0x05da, 0x80ac) }, /* ScanMaker V6UL - SpicyU */
+ /* Minolta */
+ // { USB_DEVICE(0x0638,0x026a) }, /* Minolta Dimage Scan Dual II */
+ /* Mustek */
+ { USB_DEVICE(0x055f, 0x0001) }, /* 1200 CU */
+ { USB_DEVICE(0x0400, 0x1000) }, /* BearPaw 1200 */
+ { USB_DEVICE(0x055f, 0x0002) }, /* 600 CU */
+ { USB_DEVICE(0x055f, 0x0873) }, /* 600 USB */
+ { USB_DEVICE(0x055f, 0x0003) }, /* 1200 USB */
+ { USB_DEVICE(0x055f, 0x0006) }, /* 1200 UB */
+ { USB_DEVICE(0x0400, 0x1001) }, /* BearPaw 2400 */
+ { USB_DEVICE(0x055f, 0x0008) }, /* 1200 CU Plus */
+ { USB_DEVICE(0x0ff5, 0x0010) }, /* BearPaw 1200F */
+ /* Plustek */
+ { USB_DEVICE(0x07b3, 0x0017) }, /* OpticPro UT12 */
+ { USB_DEVICE(0x07b3, 0x0011) }, /* OpticPro UT24 */
+ { USB_DEVICE(0x07b3, 0x0005) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0007) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x000F) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0010) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0013) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0014) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0015) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0016) }, /* Unknown */
+ { USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
+ /* Primax/Colorado */
+ { USB_DEVICE(0x0461, 0x0300) }, /* G2-300 #1 */
+ { USB_DEVICE(0x0461, 0x0380) }, /* G2-600 #1 */
+ { USB_DEVICE(0x0461, 0x0301) }, /* G2E-300 #1 */
+ { USB_DEVICE(0x0461, 0x0381) }, /* ReadyScan 636i */
+ { USB_DEVICE(0x0461, 0x0302) }, /* G2-300 #2 */
+ { USB_DEVICE(0x0461, 0x0382) }, /* G2-600 #2 */
+ { USB_DEVICE(0x0461, 0x0303) }, /* G2E-300 #2 */
+ { USB_DEVICE(0x0461, 0x0383) }, /* G2E-600 */
+ { USB_DEVICE(0x0461, 0x0340) }, /* Colorado USB 9600 */
+ // { USB_DEVICE(0x0461, 0x0360) }, /* Colorado USB 19200 - undetected endpoint */
+ { USB_DEVICE(0x0461, 0x0341) }, /* Colorado 600u */
+ { USB_DEVICE(0x0461, 0x0361) }, /* Colorado 1200u */
+ /* Relisis */
+ // { USB_DEVICE(0x0475, 0x0103) }, /* Episode - undetected endpoint */
+ /* Seiko/Epson Corp. */
+ { USB_DEVICE(0x04b8, 0x0101) }, /* Perfection 636U and 636Photo */
+ { USB_DEVICE(0x04b8, 0x0103) }, /* Perfection 610 */
+ { USB_DEVICE(0x04b8, 0x0104) }, /* Perfection 1200U and 1200Photo*/
+ { USB_DEVICE(0x04b8, 0x0106) }, /* Stylus Scan 2500 */
+ { USB_DEVICE(0x04b8, 0x0107) }, /* Expression 1600 */
+ { USB_DEVICE(0x04b8, 0x010a) }, /* Perfection 1640SU and 1640SU Photo */
+ { USB_DEVICE(0x04b8, 0x010b) }, /* Perfection 1240U */
+ { USB_DEVICE(0x04b8, 0x010c) }, /* Perfection 640U */
+ { USB_DEVICE(0x04b8, 0x010e) }, /* Expression 1680 */
+ { USB_DEVICE(0x04b8, 0x0110) }, /* Perfection 1650 */
+ { USB_DEVICE(0x04b8, 0x0112) }, /* Perfection 2450 - GT-9700 for the Japanese mkt */
+ /* Umax */
+ { USB_DEVICE(0x1606, 0x0010) }, /* Astra 1220U */
+ { USB_DEVICE(0x1606, 0x0030) }, /* Astra 2000U */
+ { USB_DEVICE(0x1606, 0x0130) }, /* Astra 2100U */
+ { USB_DEVICE(0x1606, 0x0230) }, /* Astra 2200U */
+ /* Visioneer */
+ { USB_DEVICE(0x04a7, 0x0221) }, /* OneTouch 5300 USB */
+ { USB_DEVICE(0x04a7, 0x0211) }, /* OneTouch 7600 USB */
+ { USB_DEVICE(0x04a7, 0x0231) }, /* 6100 USB */
+ { USB_DEVICE(0x04a7, 0x0311) }, /* 6200 EPP/USB */
+ { USB_DEVICE(0x04a7, 0x0321) }, /* OneTouch 8100 EPP/USB */
+ { USB_DEVICE(0x04a7, 0x0331) }, /* OneTouch 8600 EPP/USB */
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, scanner_device_ids);
+
+#define IS_EP_BULK(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
+#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+#define IS_EP_INTR(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_INT ? 1 : 0)
+
+#define USB_SCN_MINOR(X) minor((X)->i_rdev) - SCN_BASE_MNR
+
+#ifdef DEBUG
+#define SCN_DEBUG(X) X
+#else
+#define SCN_DEBUG(X)
+#endif
+
+#define IBUF_SIZE 32768
+#define OBUF_SIZE 4096
+
+/* read_scanner timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */
+#define RD_NAK_TIMEOUT (10*HZ) /* Default number of X seconds to wait */
+#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */
+
+
+/* FIXME: These are NOT registered ioctls()'s */
+#ifdef PV8630
+#define PV8630_IOCTL_INREQUEST 69
+#define PV8630_IOCTL_OUTREQUEST 70
+#endif /* PV8630 */
+
+
+/* read vendor and product IDs from the scanner */
+#define SCANNER_IOCTL_VENDOR _IOR('U', 0x20, int)
+#define SCANNER_IOCTL_PRODUCT _IOR('U', 0x21, int)
+/* send/recv a control message to the scanner */
+#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, struct usb_ctrlrequest)
+
+
+#define SCN_MAX_MNR 16 /* We're allocated 16 minors */
+#define SCN_BASE_MNR 48 /* USB Scanners start at minor 48 */
+
+static DECLARE_MUTEX (scn_mutex); /* Initializes to unlocked */
+
+struct scn_usb_data {
+ struct usb_device *scn_dev;
+ devfs_handle_t devfs; /* devfs device */
+ struct urb *scn_irq;
+ unsigned int ifnum; /* Interface number of the USB device */
+ int scn_minor; /* Scanner minor - used in disconnect() */
+ unsigned char button; /* Front panel buffer */
+ char isopen; /* Not zero if the device is open */
+ char present; /* Not zero if device is present */
+ char *obuf, *ibuf; /* transfer buffers */
+ char bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */
+ wait_queue_head_t rd_wait_q; /* read timeouts */
+ struct semaphore sem; /* lock to prevent concurrent reads or writes */
+ unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */
+};
+
+extern devfs_handle_t usb_devfs_handle;
+
+static struct scn_usb_data *p_scn_table[SCN_MAX_MNR] = { NULL, /* ... */};
+
+static struct usb_driver scanner_driver;
+++ /dev/null
-obj-$(CONFIG_USB_CATC) += crc32.o
+++ /dev/null
-#
-# USB Imageing devices configuration
-#
-comment 'USB Imaging devices'
-dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
-dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL
-dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
-dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI
-dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL
-
-# Turn on CONFIG_USB_IMAGE if any of the drivers are compiled into the kernel to
-# make our Makefile logic a bit simpler
-if [ "$CONFIG_USB_DC2XX" = "y" -o "$CONFIG_USB_MDC800" = "y" ]; then
- define_bool CONFIG_USB_IMAGE y
-fi
-if [ "$CONFIG_USB_SCANNER" = "y" -o "$CONFIG_USB_MICROTEK" = "y" -o "$CONFIG_USB_HPUSBSCSI" = "y" ]; then
- define_bool CONFIG_USB_IMAGE y
-fi
-
+++ /dev/null
-#
-# Makefile for USB Image drivers
-#
-
-O_TARGET := usb-image.o
-
-obj-$(CONFIG_USB_DC2XX) += dc2xx.o
-obj-$(CONFIG_USB_MDC800) += mdc800.o
-obj-$(CONFIG_USB_HPUSBSCSI) += hpusbscsi.o
-obj-$(CONFIG_USB_MICROTEK) += microtek.o
-obj-$(CONFIG_USB_SCANNER) += scanner.o
-
-include $(TOPDIR)/Rules.make
+++ /dev/null
-/*
- * Copyright (C) 1999-2000 by David Brownell <dbrownell@users.sourceforge.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.
- */
-
-
-/*
- * USB driver for Kodak DC-2XX series digital still cameras
- *
- * The protocol here is the same as the one going over a serial line, but
- * it uses USB for speed. Set up /dev/kodak, get gphoto (www.gphoto.org),
- * and have fun!
- *
- * This should also work for a number of other digital (non-Kodak) cameras,
- * by adding the vendor and product IDs to the table below. They'll need
- * to be the sort using USB just as a fast bulk data channel.
- */
-
-/*
- * HISTORY
- *
- * 26 August, 1999 -- first release (0.1), works with my DC-240.
- * The DC-280 (2Mpixel) should also work, but isn't tested.
- * If you use gphoto, make sure you have the USB updates.
- * Lives in a 2.3.14 or so Linux kernel, in drivers/usb.
- * 31 August, 1999 -- minor update to recognize DC-260 and handle
- * its endpoints being in a different order. Note that as
- * of gPhoto 0.36pre, the USB updates are integrated.
- * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0);
- * added timeouts to bulk_msg calls. Minor updates, docs.
- * 03 Nov, 1999 -- update for 2.3.25 kernel API changes.
- * 08 Jan, 2000 .. multiple camera support
- * 12 Aug, 2000 .. add some real locking, remove an Oops
- * 10 Oct, 2000 .. usb_device_id table created.
- * 01 Nov, 2000 .. usb_device_id support added by Adam J. Richter
- * 08 Apr, 2001 .. Identify version on module load. gb
- *
- * Thanks to: the folk who've provided USB product IDs, sent in
- * patches, and shared their successes!
- */
-
-#include <linux/config.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/slab.h>
-#include <linux/module.h>
-#include <linux/devfs_fs_kernel.h>
-
-#ifdef CONFIG_USB_DEBUG
- #define DEBUG
-#else
- #undef DEBUG
-#endif
-#include <linux/usb.h>
-
-
-/* /dev/usb dir. */
-extern devfs_handle_t usb_devfs_handle;
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.0.0"
-#define DRIVER_AUTHOR "David Brownell, <dbrownell@users.sourceforge.net>"
-#define DRIVER_DESC "USB Camera Driver for Kodak DC-2xx series cameras"
-
-
-/* current USB framework handles max of 16 USB devices per driver */
-#define MAX_CAMERAS 16
-
-/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */
-#define USB_CAMERA_MINOR_BASE 80
-
-
-// XXX remove packet size limit, now that bulk transfers seem fixed
-
-/* Application protocol limit is 0x8002; USB has disliked that limit! */
-#define MAX_PACKET_SIZE 0x2000 /* e.g. image downloading */
-
-#define MAX_READ_RETRY 5 /* times to retry reads */
-#define MAX_WRITE_RETRY 5 /* times to retry writes */
-#define RETRY_TIMEOUT (HZ) /* sleep between retries */
-
-
-/* table of cameras that work through this driver */
-static struct usb_device_id camera_table [] = {
- /* These have the same application level protocol */
- { USB_DEVICE(0x040a, 0x0120) }, // Kodak DC-240
- { USB_DEVICE(0x040a, 0x0130) }, // Kodak DC-280
- { USB_DEVICE(0x040a, 0x0131) }, // Kodak DC-5000
- { USB_DEVICE(0x040a, 0x0132) }, // Kodak DC-3400
-
- /* These have a different application level protocol which
- * is part of the Flashpoint "DigitaOS". That supports some
- * non-camera devices, and some non-Kodak cameras.
- * Use this driver to get USB and "OpenDis" to talk.
- */
- { USB_DEVICE(0x040a, 0x0100) }, // Kodak DC-220
- { USB_DEVICE(0x040a, 0x0110) }, // Kodak DC-260
- { USB_DEVICE(0x040a, 0x0111) }, // Kodak DC-265
- { USB_DEVICE(0x040a, 0x0112) }, // Kodak DC-290
- { USB_DEVICE(0xf003, 0x6002) }, // HP PhotoSmart C500
- { USB_DEVICE(0x03f0, 0x4102) }, // HP PhotoSmart C618
- { USB_DEVICE(0x0a17, 0x1001) }, // Pentax EI-200
-
- /* Other USB devices may well work here too, so long as they
- * just stick to half duplex bulk packet exchanges. That
- * means, among other things, no iso or interrupt endpoints.
- */
-
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, camera_table);
-
-
-struct camera_state {
- struct usb_device *dev; /* USB device handle */
- int inEP; /* read endpoint */
- int outEP; /* write endpoint */
- const struct usb_device_id *info; /* DC-240, etc */
- int subminor; /* which minor dev #? */
- struct semaphore sem; /* locks this struct */
-
- /* this is non-null iff the device is open */
- char *buf; /* buffer for I/O */
-
- devfs_handle_t devfs; /* devfs device */
-
- /* always valid */
- wait_queue_head_t wait; /* for timed waits */
-};
-
-/* Support multiple cameras, possibly of different types. */
-static struct camera_state *minor_data [MAX_CAMERAS];
-
-/* make this an rwlock if contention becomes an issue */
-static DECLARE_MUTEX (state_table_mutex);
-
-static ssize_t camera_read (struct file *file,
- char *buf, size_t len, loff_t *ppos)
-{
- struct camera_state *camera;
- int retries;
- int retval = 0;
-
- if (len > MAX_PACKET_SIZE)
- return -EINVAL;
-
- camera = (struct camera_state *) file->private_data;
- down (&camera->sem);
- if (!camera->dev) {
- up (&camera->sem);
- return -ENODEV;
- }
-
- /* Big reads are common, for image downloading. Smaller ones
- * are also common (even "directory listing" commands don't
- * send very much data). We preserve packet boundaries here,
- * they matter in the application protocol.
- */
- for (retries = 0; retries < MAX_READ_RETRY; retries++) {
- int count;
-
- if (signal_pending (current)) {
- retval = -EINTR;
- break;
- }
-
- retval = usb_bulk_msg (camera->dev,
- usb_rcvbulkpipe (camera->dev, camera->inEP),
- camera->buf, len, &count, HZ*10);
-
- dbg ("read (%Zd) - 0x%x %d", len, retval, count);
-
- if (!retval) {
- if (copy_to_user (buf, camera->buf, count))
- retval = -EFAULT;
- else
- retval = count;
- break;
- }
- if (retval != -ETIMEDOUT)
- break;
- interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT);
-
- dbg ("read (%Zd) - retry", len);
- }
- up (&camera->sem);
- return retval;
-}
-
-static ssize_t camera_write (struct file *file,
- const char *buf, size_t len, loff_t *ppos)
-{
- struct camera_state *camera;
- ssize_t bytes_written = 0;
-
- if (len > MAX_PACKET_SIZE)
- return -EINVAL;
-
- camera = (struct camera_state *) file->private_data;
- down (&camera->sem);
- if (!camera->dev) {
- up (&camera->sem);
- return -ENODEV;
- }
-
- /* most writes will be small: simple commands, sometimes with
- * parameters. putting images (like borders) into the camera
- * would be the main use of big writes.
- */
- while (len > 0) {
- char *obuf = camera->buf;
- int maxretry = MAX_WRITE_RETRY;
- unsigned long copy_size, thistime;
-
- /* it's not clear that retrying can do any good ... or that
- * fragmenting application packets into N writes is correct.
- */
- thistime = copy_size = len;
- if (copy_from_user (obuf, buf, copy_size)) {
- bytes_written = -EFAULT;
- break;
- }
- while (thistime) {
- int result;
- int count;
-
- if (signal_pending (current)) {
- if (!bytes_written)
- bytes_written = -EINTR;
- goto done;
- }
-
- result = usb_bulk_msg (camera->dev,
- usb_sndbulkpipe (camera->dev, camera->outEP),
- obuf, thistime, &count, HZ*10);
-
- if (result)
- dbg ("write USB err - %d", result);
-
- if (count) {
- obuf += count;
- thistime -= count;
- maxretry = MAX_WRITE_RETRY;
- continue;
- } else if (!result)
- break;
-
- if (result == -ETIMEDOUT) { /* NAK - delay a bit */
- if (!maxretry--) {
- if (!bytes_written)
- bytes_written = -ETIME;
- goto done;
- }
- interruptible_sleep_on_timeout (&camera->wait,
- RETRY_TIMEOUT);
- continue;
- }
- if (!bytes_written)
- bytes_written = -EIO;
- goto done;
- }
- bytes_written += copy_size;
- len -= copy_size;
- buf += copy_size;
- }
-done:
- up (&camera->sem);
- dbg ("wrote %Zd", bytes_written);
- return bytes_written;
-}
-
-static int camera_open (struct inode *inode, struct file *file)
-{
- struct camera_state *camera = NULL;
- int subminor;
- int value = 0;
-
- down (&state_table_mutex);
- subminor = minor (inode->i_rdev) - USB_CAMERA_MINOR_BASE;
- if (subminor < 0 || subminor >= MAX_CAMERAS
- || !(camera = minor_data [subminor])) {
- up (&state_table_mutex);
- return -ENODEV;
- }
- down (&camera->sem);
- up (&state_table_mutex);
-
- if (camera->buf) {
- value = -EBUSY;
- goto done;
- }
-
- if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) {
- value = -ENOMEM;
- goto done;
- }
-
- dbg ("open #%d", subminor);
-
- file->private_data = camera;
-done:
- up (&camera->sem);
- return value;
-}
-
-static int camera_release (struct inode *inode, struct file *file)
-{
- struct camera_state *camera;
- int subminor;
-
- camera = (struct camera_state *) file->private_data;
- down (&state_table_mutex);
- down (&camera->sem);
-
- if (camera->buf) {
- kfree (camera->buf);
- camera->buf = 0;
- }
- subminor = camera->subminor;
-
- /* If camera was unplugged with open file ... */
- if (!camera->dev) {
- minor_data [subminor] = NULL;
- kfree (camera);
- } else
- up (&camera->sem);
-
- up (&state_table_mutex);
-
- dbg ("close #%d", subminor);
-
- return 0;
-}
-
- /* XXX should define some ioctls to expose camera type
- * to applications ... what USB exposes should suffice.
- * apps should be able to see the camera type.
- */
-static /* const */ struct file_operations usb_camera_fops = {
- /* Uses GCC initializer extension; simpler to maintain */
- owner: THIS_MODULE,
- read: camera_read,
- write: camera_write,
- open: camera_open,
- release: camera_release,
-};
-
-
-
-static void *
-camera_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *camera_info)
-{
- int i;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *endpoint;
- int direction, ep;
- char name[8];
- struct camera_state *camera = NULL;
-
-
- /* these have one config, one interface */
- if (dev->descriptor.bNumConfigurations != 1
- || dev->config[0].bNumInterfaces != 1) {
- dbg ("Bogus camera config info");
- return NULL;
- }
-
- /* models differ in how they report themselves */
- interface = &dev->actconfig->interface[ifnum].altsetting[0];
- if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE
- && interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC)
- || interface->bInterfaceSubClass != 0
- || interface->bInterfaceProtocol != 0
- || interface->bNumEndpoints != 2
- ) {
- dbg ("Bogus camera interface info");
- return NULL;
- }
-
-
- /* select "subminor" number (part of a minor number) */
- down (&state_table_mutex);
- for (i = 0; i < MAX_CAMERAS; i++) {
- if (!minor_data [i])
- break;
- }
- if (i >= MAX_CAMERAS) {
- info ("Ignoring additional USB Camera");
- goto bye;
- }
-
- /* allocate & init camera state */
- camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL);
- if (!camera) {
- err ("no memory!");
- goto bye;
- }
-
- init_MUTEX (&camera->sem);
- camera->info = camera_info;
- camera->subminor = i;
- camera->buf = NULL;
- init_waitqueue_head (&camera->wait);
-
-
- /* get input and output endpoints (either order) */
- endpoint = interface->endpoint;
- camera->outEP = camera->inEP = -1;
-
- ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
- if (direction == USB_DIR_IN)
- camera->inEP = ep;
- else
- camera->outEP = ep;
-
- ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
- if (direction == USB_DIR_IN)
- camera->inEP = ep;
- else
- camera->outEP = ep;
-
- if (camera->outEP == -1 || camera->inEP == -1
- || endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK
- || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK
- ) {
- dbg ("Bogus endpoints");
- goto error;
- }
-
- info ("USB Camera #%d connected, major/minor %d/%d", camera->subminor,
- USB_MAJOR, USB_CAMERA_MINOR_BASE + camera->subminor);
-
- camera->dev = dev;
- usb_inc_dev_use (dev);
-
- /* If we have devfs, register the device */
- sprintf(name, "dc2xx%d", camera->subminor);
- camera->devfs = devfs_register(usb_devfs_handle, name,
- DEVFS_FL_DEFAULT, USB_MAJOR,
- USB_CAMERA_MINOR_BASE + camera->subminor,
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
- S_IWGRP, &usb_camera_fops, NULL);
-
- goto bye;
-
-error:
- minor_data [camera->subminor] = NULL;
- kfree (camera);
- camera = NULL;
-bye:
- up (&state_table_mutex);
- return camera;
-}
-
-static void camera_disconnect(struct usb_device *dev, void *ptr)
-{
- struct camera_state *camera = (struct camera_state *) ptr;
- int subminor = camera->subminor;
-
- down (&state_table_mutex);
- down (&camera->sem);
-
- devfs_unregister(camera->devfs);
-
- /* If camera's not opened, we can clean up right away.
- * Else apps see a disconnect on next I/O; the release cleans.
- */
- if (!camera->buf) {
- minor_data [subminor] = NULL;
- kfree (camera);
- camera = NULL;
- } else
- camera->dev = NULL;
-
- info ("USB Camera #%d disconnected", subminor);
- usb_dec_dev_use (dev);
-
- if (camera != NULL)
- up (&camera->sem);
- up (&state_table_mutex);
-}
-
-static /* const */ struct usb_driver camera_driver = {
- name: "dc2xx",
-
- id_table: camera_table,
- probe: camera_probe,
- disconnect: camera_disconnect,
-
- fops: &usb_camera_fops,
- minor: USB_CAMERA_MINOR_BASE
-};
-
-
-int __init usb_dc2xx_init(void)
-{
- if (usb_register (&camera_driver) < 0)
- return -1;
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return 0;
-}
-
-void __exit usb_dc2xx_cleanup(void)
-{
- usb_deregister (&camera_driver);
-}
-
-module_init (usb_dc2xx_init);
-module_exit (usb_dc2xx_cleanup);
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-
+++ /dev/null
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
-#include <linux/usb.h>
-#include <asm/atomic.h>
-#include <linux/blk.h>
-#include "../../scsi/scsi.h"
-#include "../../scsi/hosts.h"
-#include "../../scsi/sd.h"
-
-#include "hpusbscsi.h"
-
-#define DEBUG(x...) \
- printk( KERN_DEBUG x )
-
-static char *states[]={"FREE", "BEGINNING", "WORKING", "ERROR", "WAIT", "PREMATURE"};
-
-#define TRACE_STATE printk(KERN_DEBUG"hpusbscsi->state = %s at line %d\n", states[hpusbscsi->state], __LINE__)
-
-/* global variables */
-
-struct list_head hpusbscsi_devices;
-//LIST_HEAD(hpusbscsi_devices);
-
-/* USB related parts */
-
-static void *
-hpusbscsi_usb_probe (struct usb_device *dev, unsigned int interface,
- const struct usb_device_id *id)
-{
- struct hpusbscsi *new;
- struct usb_interface_descriptor *altsetting =
- &(dev->actconfig->interface[interface].altsetting[0]);
-
- int i, result;
-
- /* basic check */
-
- if (altsetting->bNumEndpoints != 3) {
- printk (KERN_ERR "Wrong number of endpoints\n");
- return NULL;
- }
-
- /* descriptor allocation */
-
- new =
- (struct hpusbscsi *) kmalloc (sizeof (struct hpusbscsi),
- GFP_KERNEL);
- if (new == NULL)
- return NULL;
- DEBUG ("Allocated memory\n");
- memset (new, 0, sizeof (struct hpusbscsi));
- new->dataurb = usb_alloc_urb(0, GFP_KERNEL);
- if (!new->dataurb) {
- kfree (new);
- return NULL;
- }
- new->controlurb = usb_alloc_urb(0, GFP_KERNEL);
- if (!new->controlurb) {
- usb_free_urb (new->dataurb);
- kfree (new);
- return NULL;
- }
- new->dev = dev;
- init_waitqueue_head (&new->pending);
- init_waitqueue_head (&new->deathrow);
- INIT_LIST_HEAD (&new->lh);
-
-
-
- /* finding endpoints */
-
- for (i = 0; i < altsetting->bNumEndpoints; i++) {
- if (
- (altsetting->endpoint[i].
- bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_BULK) {
- if (altsetting->endpoint[i].
- bEndpointAddress & USB_DIR_IN) {
- new->ep_in =
- altsetting->endpoint[i].
- bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- } else {
- new->ep_out =
- altsetting->endpoint[i].
- bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- }
- } else {
- new->ep_int =
- altsetting->endpoint[i].
- bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- new->interrupt_interval= altsetting->endpoint[i].bInterval;
- }
- }
-
- /* USB initialisation magic for the simple case */
-
- result = usb_set_interface (dev, altsetting->bInterfaceNumber, 0);
-
- switch (result) {
- case 0: /* no error */
- break;
-
- case -EPIPE:
- usb_clear_halt (dev, usb_sndctrlpipe (dev, 0));
- break;
-
- default:
- printk (KERN_ERR "unknown error %d from usb_set_interface\n",
- result);
- goto err_out;
- }
-
- /* making a template for the scsi layer to fake detection of a scsi device */
-
- memcpy (&(new->ctempl), &hpusbscsi_scsi_host_template,
- sizeof (hpusbscsi_scsi_host_template));
- (struct hpusbscsi *) new->ctempl.proc_dir = new;
- new->ctempl.module = THIS_MODULE;
-
- if (scsi_register_host(&new->ctempl))
- goto err_out;
-
- new->sense_command[0] = REQUEST_SENSE;
- new->sense_command[4] = HPUSBSCSI_SENSE_LENGTH;
-
- /* adding to list for module unload */
- list_add (&hpusbscsi_devices, &new->lh);
-
- return new;
-
- err_out:
- usb_free_urb (new->controlurb);
- usb_free_urb (new->dataurb);
- kfree (new);
- return NULL;
-}
-
-static void
-hpusbscsi_usb_disconnect (struct usb_device *dev, void *ptr)
-{
- usb_unlink_urb((((struct hpusbscsi *) ptr)->controlurb));
- ((struct hpusbscsi *) ptr)->dev = NULL;
-}
-
-static struct usb_device_id hpusbscsi_usb_ids[] = {
- {USB_DEVICE (0x03f0, 0x0701)}, /* HP 53xx */
- {USB_DEVICE (0x03f0, 0x0801)}, /* HP 7400 */
- {USB_DEVICE (0x0638, 0x026a)}, /*Scan Dual II */
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, hpusbscsi_usb_ids);
-MODULE_LICENSE("GPL");
-
-
-static struct usb_driver hpusbscsi_usb_driver = {
- name:"hpusbscsi",
- probe:hpusbscsi_usb_probe,
- disconnect:hpusbscsi_usb_disconnect,
- id_table:hpusbscsi_usb_ids,
-};
-
-/* module initialisation */
-
-int __init
-hpusbscsi_init (void)
-{
- int result;
-
- INIT_LIST_HEAD (&hpusbscsi_devices);
- DEBUG ("Driver loaded\n");
-
- if ((result = usb_register (&hpusbscsi_usb_driver)) < 0) {
- printk (KERN_ERR "hpusbscsi: driver registration failed\n");
- return -1;
- } else {
- return 0;
- }
-}
-
-void __exit
-hpusbscsi_exit (void)
-{
- struct list_head *tmp;
- struct list_head *old;
- struct hpusbscsi * o;
-
- for (tmp = hpusbscsi_devices.next; tmp != &hpusbscsi_devices;/*nothing */) {
- old = tmp;
- tmp = tmp->next;
- o = (struct hpusbscsi *)old;
- usb_unlink_urb(o->controlurb);
- scsi_unregister_host(&o->ctempl);
- usb_free_urb(o->controlurb);
- usb_free_urb(o->dataurb);
- kfree(old);
- }
-
- usb_deregister (&hpusbscsi_usb_driver);
-}
-
-module_init (hpusbscsi_init);
-module_exit (hpusbscsi_exit);
-
-/* interface to the scsi layer */
-
-static int
-hpusbscsi_scsi_detect (struct SHT *sht)
-{
- /* Whole function stolen from usb-storage */
-
- struct hpusbscsi *desc = (struct hpusbscsi *) sht->proc_dir;
- /* What a hideous hack! */
-
- char local_name[48];
-
-
- /* set up the name of our subdirectory under /proc/scsi/ */
- sprintf (local_name, "hpusbscsi-%d", desc->number);
- sht->proc_name = kmalloc (strlen (local_name) + 1, GFP_KERNEL);
- /* FIXME: where is this freed ? */
-
- if (!sht->proc_name) {
- return 0;
- }
-
- strcpy (sht->proc_name, local_name);
-
- sht->proc_dir = NULL;
-
- /* build and submit an interrupt URB for status byte handling */
- FILL_INT_URB(desc->controlurb,
- desc->dev,
- usb_rcvintpipe(desc->dev,desc->ep_int),
- &desc->scsi_state_byte,
- 1,
- control_interrupt_callback,
- desc,
- desc->interrupt_interval
- );
-
- if ( 0 > usb_submit_urb(desc->controlurb, GFP_KERNEL)) {
- kfree(sht->proc_name);
- return 0;
- }
-
- /* In host->hostdata we store a pointer to desc */
- desc->host = scsi_register (sht, sizeof (desc));
- if (desc->host == NULL) {
- kfree (sht->proc_name);
- usb_unlink_urb(desc->controlurb);
- return 0;
- }
- desc->host->hostdata[0] = (unsigned long) desc;
-
-
- return 1;
-}
-
-static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback)
-{
- struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
- usb_urb_callback usb_callback;
- int res;
-
- hpusbscsi->use_count++;
-
- /* we don't answer for anything but our single device on any faked host controller */
- if ( srb->device->lun || srb->device->id || srb->device->channel ) {
- if (callback) {
- srb->result = DID_BAD_TARGET;
- callback(srb);
- }
- goto out;
- }
-
- /* Now we need to decide which callback to give to the urb we send the command with */
-
- if (!srb->bufflen) {
- if (srb->cmnd[0] == REQUEST_SENSE){
- hpusbscsi->current_data_pipe = usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in);
- usb_callback = request_sense_callback;
- } else {
- usb_callback = simple_command_callback;
- }
- } else {
- if (likely(srb->use_sg)) {
- usb_callback = scatter_gather_callback;
- hpusbscsi->fragment = 0;
- } else {
- usb_callback = simple_payload_callback;
- }
- /* Now we find out which direction data is to be transfered in */
- hpusbscsi->current_data_pipe = DIRECTION_IS_IN(srb->cmnd[0]) ?
- usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in)
- :
- usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out)
- ;
- }
-
-
- TRACE_STATE;
-
- /* We zero the sense buffer to avoid confusing user space */
- memset(srb->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-
- hpusbscsi->state = HP_STATE_BEGINNING;
- TRACE_STATE;
-
- /* We prepare the urb for writing out the scsi command */
- FILL_BULK_URB(
- hpusbscsi->dataurb,
- hpusbscsi->dev,
- usb_sndbulkpipe(hpusbscsi->dev,hpusbscsi->ep_out),
- srb->cmnd,
- srb->cmd_len,
- usb_callback,
- hpusbscsi
- );
- hpusbscsi->scallback = callback;
- hpusbscsi->srb = srb;
-
- res = usb_submit_urb(hpusbscsi->dataurb, GFP_ATOMIC);
- if (unlikely(res)) {
- hpusbscsi->state = HP_STATE_FREE;
- TRACE_STATE;
- if (likely(callback != NULL)) {
- srb->result = DID_ERROR;
- callback(srb);
- }
- }
-
-out:
- hpusbscsi->use_count--;
- return 0;
-}
-
-static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb)
-{
- struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
-
- printk(KERN_DEBUG"SCSI reset requested.\n");
- //usb_reset_device(hpusbscsi->dev);
- //printk(KERN_DEBUG"SCSI reset completed.\n");
- hpusbscsi->state = HP_STATE_FREE;
-
- return 0;
-}
-
-static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb)
-{
- struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
- printk(KERN_DEBUG"Requested is canceled.\n");
-
- usb_unlink_urb(hpusbscsi->dataurb);
- usb_unlink_urb(hpusbscsi->controlurb);
- hpusbscsi->state = HP_STATE_FREE;
-
- return SCSI_ABORT_PENDING;
-}
-
-/* usb interrupt handlers - they are all running IN INTERRUPT ! */
-
-static void handle_usb_error (struct hpusbscsi *hpusbscsi)
-{
- if (likely(hpusbscsi->scallback != NULL)) {
- hpusbscsi->srb->result = DID_ERROR;
- hpusbscsi->scallback(hpusbscsi->srb);
- }
- hpusbscsi->state = HP_STATE_FREE;
-}
-
-static void control_interrupt_callback (struct urb *u)
-{
- struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
- u8 scsi_state;
-
-DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte);
- if(unlikely(u->status < 0)) {
- if (likely(hpusbscsi->state != HP_STATE_FREE))
- handle_usb_error(hpusbscsi);
- return;
- }
-
- scsi_state = hpusbscsi->scsi_state_byte;
- if (hpusbscsi->state != HP_STATE_ERROR) {
- hpusbscsi->srb->result &= SCSI_ERR_MASK;
- hpusbscsi->srb->result |= scsi_state;
- }
-
- if (scsi_state == CHECK_CONDITION << 1) {
- if (hpusbscsi->state == HP_STATE_WAIT) {
- issue_request_sense(hpusbscsi);
- } else {
- /* we request sense after an eventual data transfer */
- hpusbscsi->state = HP_STATE_ERROR;
- }
- }
-
- if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT && scsi_state != CHECK_CONDITION <<1 )
- /* we do a callback to the scsi layer if and only if all data has been transfered */
- hpusbscsi->scallback(hpusbscsi->srb);
-
- TRACE_STATE;
- switch (hpusbscsi->state) {
- case HP_STATE_WAIT:
- hpusbscsi->state = HP_STATE_FREE;
- TRACE_STATE;
- break;
- case HP_STATE_WORKING:
- case HP_STATE_BEGINNING:
- hpusbscsi->state = HP_STATE_PREMATURE;
- TRACE_STATE;
- break;
- case HP_STATE_ERROR:
- break;
- default:
- printk(KERN_ERR"hpusbscsi: Unexpected status report.\n");
- TRACE_STATE;
- hpusbscsi->state = HP_STATE_FREE;
- TRACE_STATE;
- break;
- }
-}
-
-static void simple_command_callback(struct urb *u)
-{
- struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
- if (unlikely(u->status<0)) {
- handle_usb_error(hpusbscsi);
- return;
- }
- TRACE_STATE;
- if (hpusbscsi->state != HP_STATE_PREMATURE) {
- TRACE_STATE;
- hpusbscsi->state = HP_STATE_WAIT;
- } else {
- if (likely(hpusbscsi->scallback != NULL))
- hpusbscsi->scallback(hpusbscsi->srb);
- hpusbscsi->state = HP_STATE_FREE;
- TRACE_STATE;
- }
-}
-
-static void scatter_gather_callback(struct urb *u)
-{
- struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
- struct scatterlist *sg = hpusbscsi->srb->buffer;
- usb_urb_callback callback;
- int res;
-
- DEBUG("Going through scatter/gather\n");
- if (unlikely(u->status < 0)) {
- handle_usb_error(hpusbscsi);
- return;
- }
-
- if (hpusbscsi->fragment + 1 != hpusbscsi->srb->use_sg)
- callback = scatter_gather_callback;
- else
- callback = simple_done;
-
- TRACE_STATE;
- if (hpusbscsi->state != HP_STATE_PREMATURE)
- hpusbscsi->state = HP_STATE_WORKING;
- TRACE_STATE;
-
- FILL_BULK_URB(
- u,
- hpusbscsi->dev,
- hpusbscsi->current_data_pipe,
- page_address(sg[hpusbscsi->fragment].page) +
- sg[hpusbscsi->fragment].offset,
- sg[hpusbscsi->fragment++].length,
- callback,
- hpusbscsi
- );
-
- res = usb_submit_urb(u, GFP_ATOMIC);
- if (unlikely(res))
- handle_usb_error(hpusbscsi);
- TRACE_STATE;
-}
-
-static void simple_done (struct urb *u)
-{
- struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
-
- if (unlikely(u->status < 0)) {
- handle_usb_error(hpusbscsi);
- return;
- }
- DEBUG("Data transfer done\n");
- TRACE_STATE;
- if (hpusbscsi->state != HP_STATE_PREMATURE) {
- if (unlikely(u->status < 0)) {
- handle_usb_error(hpusbscsi);
- } else {
- if (hpusbscsi->state != HP_STATE_ERROR) {
- hpusbscsi->state = HP_STATE_WAIT;
- } else {
- issue_request_sense(hpusbscsi);
- }
- }
- } else {
- if (likely(hpusbscsi->scallback != NULL))
- hpusbscsi->scallback(hpusbscsi->srb);
- hpusbscsi->state = HP_STATE_FREE;
- }
-}
-
-static void simple_payload_callback (struct urb *u)
-{
- struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
- int res;
-
- if (unlikely(u->status<0)) {
- handle_usb_error(hpusbscsi);
- return;
- }
-
- FILL_BULK_URB(
- u,
- hpusbscsi->dev,
- hpusbscsi->current_data_pipe,
- hpusbscsi->srb->buffer,
- hpusbscsi->srb->bufflen,
- simple_done,
- hpusbscsi
- );
-
- res = usb_submit_urb(u, GFP_ATOMIC);
- if (unlikely(res)) {
- handle_usb_error(hpusbscsi);
- return;
- }
- TRACE_STATE;
- if (hpusbscsi->state != HP_STATE_PREMATURE) {
- hpusbscsi->state = HP_STATE_WORKING;
- TRACE_STATE;
- }
-}
-
-static void request_sense_callback (struct urb *u)
-{
- struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context;
-
- if (u->status<0) {
- handle_usb_error(hpusbscsi);
- return;
- }
-
- FILL_BULK_URB(
- u,
- hpusbscsi->dev,
- hpusbscsi->current_data_pipe,
- hpusbscsi->srb->sense_buffer,
- SCSI_SENSE_BUFFERSIZE,
- simple_done,
- hpusbscsi
- );
-
- if (0 > usb_submit_urb(u, GFP_ATOMIC)) {
- handle_usb_error(hpusbscsi);
- return;
- }
- if (hpusbscsi->state != HP_STATE_PREMATURE && hpusbscsi->state != HP_STATE_ERROR)
- hpusbscsi->state = HP_STATE_WORKING;
-}
-
-static void issue_request_sense (struct hpusbscsi *hpusbscsi)
-{
- FILL_BULK_URB(
- hpusbscsi->dataurb,
- hpusbscsi->dev,
- usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out),
- &hpusbscsi->sense_command,
- SENSE_COMMAND_SIZE,
- request_sense_callback,
- hpusbscsi
- );
-
- hpusbscsi->current_data_pipe = usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in);
-
- if (0 > usb_submit_urb(hpusbscsi->dataurb, GFP_ATOMIC)) {
- handle_usb_error(hpusbscsi);
- }
-}
-
+++ /dev/null
-/* Header file for the hpusbscsi driver */
-/* (C) Copyright 2001 Oliver Neukum */
-/* sponsored by the Linux Usb Project */
-/* large parts based on or taken from code by John Fremlin and Matt Dharm */
-/* this file is licensed under the GPL */
-
-/* A big thanks to Jose for untiring testing */
-
-typedef void (*usb_urb_callback) (struct urb *);
-typedef void (*scsi_callback)(Scsi_Cmnd *);
-
-#define SENSE_COMMAND_SIZE 6
-#define HPUSBSCSI_SENSE_LENGTH 0x16
-
-struct hpusbscsi
-{
- struct list_head lh;
- struct usb_device *dev; /* NULL indicates unplugged device */
- int ep_out;
- int ep_in;
- int ep_int;
- int interrupt_interval;
-
- struct Scsi_Host *host;
- Scsi_Host_Template ctempl;
- int number;
- scsi_callback scallback;
- Scsi_Cmnd *srb;
- u8 sense_command[SENSE_COMMAND_SIZE];
-
- int use_count;
- wait_queue_head_t pending;
- wait_queue_head_t deathrow;
-
- struct urb *dataurb;
- struct urb *controlurb;
- int fragment;
-
- int state;
- int current_data_pipe;
-
- u8 scsi_state_byte;
-};
-
-#define SCSI_ERR_MASK ~0x3fu
-
-static const unsigned char scsi_command_direction[256/8] = {
- 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
- 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-#define DIRECTION_IS_IN(x) ((scsi_command_direction[x>>3] >> (x & 7)) & 1)
-
-static int hpusbscsi_scsi_detect (struct SHT * sht);
-static void simple_command_callback(struct urb *u);
-static void scatter_gather_callback(struct urb *u);
-static void simple_payload_callback (struct urb *u);
-static void request_sense_callback (struct urb *u);
-static void control_interrupt_callback (struct urb *u);
-static void simple_done (struct urb *u);
-static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback);
-static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb);
-static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb);
-static void issue_request_sense (struct hpusbscsi *hpusbscsi);
-
-static Scsi_Host_Template hpusbscsi_scsi_host_template = {
- name: "hpusbscsi",
- detect: hpusbscsi_scsi_detect,
-// release: hpusbscsi_scsi_release,
- queuecommand: hpusbscsi_scsi_queuecommand,
-
- eh_abort_handler: hpusbscsi_scsi_abort,
- eh_host_reset_handler: hpusbscsi_scsi_host_reset,
-
- sg_tablesize: SG_ALL,
- can_queue: 1,
- this_id: -1,
- cmd_per_lun: 1,
- present: 0,
- unchecked_isa_dma: FALSE,
- use_clustering: TRUE,
- emulated: TRUE
-};
-
-/* defines for internal driver state */
-#define HP_STATE_FREE 0 /*ready for next request */
-#define HP_STATE_BEGINNING 1 /*command being transfered */
-#define HP_STATE_WORKING 2 /* data transfer stage */
-#define HP_STATE_ERROR 3 /* error has been reported */
-#define HP_STATE_WAIT 4 /* waiting for status transfer */
-#define HP_STATE_PREMATURE 5 /* status prematurely reported */
-
-
-
+++ /dev/null
-/*
- * copyright (C) 1999/2000 by Henning Zabel <henning@uni-paderborn.de>
- *
- * 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.
- */
-
-
-/*
- * USB-Kernel Driver for the Mustek MDC800 Digital Camera
- * (c) 1999/2000 Henning Zabel <henning@uni-paderborn.de>
- *
- *
- * The driver brings the USB functions of the MDC800 to Linux.
- * To use the Camera you must support the USB Protocoll of the camera
- * to the Kernel Node.
- * The Driver uses a misc device Node. Create it with :
- * mknod /dev/mustek c 180 32
- *
- * The driver supports only one camera.
- *
- * Fix: mdc800 used sleep_on and slept with io_lock held.
- * Converted sleep_on to waitqueues with schedule_timeout and made io_lock
- * a semaphore from a spinlock.
- * by Oliver Neukum <520047054719-0001@t-online.de>
- * (02/12/2001)
- *
- * Identify version on module load.
- * (08/04/2001) gb
- *
- * version 0.7.5
- * Fixed potential SMP races with Spinlocks.
- * Thanks to Oliver Neukum <oliver.neukum@lrz.uni-muenchen.de> who
- * noticed the race conditions.
- * (30/10/2000)
- *
- * Fixed: Setting urb->dev before submitting urb.
- * by Greg KH <greg@kroah.com>
- * (13/10/2000)
- *
- * version 0.7.3
- * bugfix : The mdc800->state field gets set to READY after the
- * the diconnect function sets it to NOT_CONNECTED. This makes the
- * driver running like the camera is connected and causes some
- * hang ups.
- *
- * version 0.7.1
- * MOD_INC and MOD_DEC are changed in usb_probe to prevent load/unload
- * problems when compiled as Module.
- * (04/04/2000)
- *
- * The mdc800 driver gets assigned the USB Minor 32-47. The Registration
- * was updated to use these values.
- * (26/03/2000)
- *
- * The Init und Exit Module Function are updated.
- * (01/03/2000)
- *
- * version 0.7.0
- * Rewrite of the driver : The driver now uses URB's. The old stuff
- * has been removed.
- *
- * version 0.6.0
- * Rewrite of this driver: The Emulation of the rs232 protocoll
- * has been removed from the driver. A special executeCommand function
- * for this driver is included to gphoto.
- * The driver supports two kind of communication to bulk endpoints.
- * Either with the dev->bus->ops->bulk... or with callback function.
- * (09/11/1999)
- *
- * version 0.5.0:
- * first Version that gets a version number. Most of the needed
- * functions work.
- * (20/10/1999)
- */
-
-#include <linux/version.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-
-#include <linux/usb.h>
-#include <linux/fs.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.7.5 (30/10/2000)"
-#define DRIVER_AUTHOR "Henning Zabel <henning@uni-paderborn.de>"
-#define DRIVER_DESC "USB Driver for Mustek MDC800 Digital Camera"
-
-/* Vendor and Product Information */
-#define MDC800_VENDOR_ID 0x055f
-#define MDC800_PRODUCT_ID 0xa800
-
-/* Timeouts (msec) */
-#define TO_DOWNLOAD_GET_READY 1500
-#define TO_DOWNLOAD_GET_BUSY 1500
-#define TO_WRITE_GET_READY 1000
-#define TO_DEFAULT_COMMAND 5000
-#define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND
-#define TO_GET_READY TO_DEFAULT_COMMAND
-
-/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */
-#define MDC800_DEVICE_MINOR_BASE 32
-
-
-/**************************************************************************
- Data and structs
-***************************************************************************/
-
-
-typedef enum {
- NOT_CONNECTED, READY, WORKING, DOWNLOAD
-} mdc800_state;
-
-
-/* Data for the driver */
-struct mdc800_data
-{
- struct usb_device * dev; // Device Data
- mdc800_state state;
-
- unsigned int endpoint [4];
-
- struct urb * irq_urb;
- wait_queue_head_t irq_wait;
- int irq_woken;
- char* irq_urb_buffer;
-
- int camera_busy; // is camera busy ?
- int camera_request_ready; // Status to synchronize with irq
- char camera_response [8]; // last Bytes send after busy
-
- struct urb * write_urb;
- char* write_urb_buffer;
- wait_queue_head_t write_wait;
- int written;
-
-
- struct urb * download_urb;
- char* download_urb_buffer;
- wait_queue_head_t download_wait;
- int downloaded;
- int download_left; // Bytes left to download ?
-
-
- /* Device Data */
- char out [64]; // Answer Buffer
- int out_ptr; // Index to the first not readen byte
- int out_count; // Bytes in the buffer
-
- int open; // Camera device open ?
- struct semaphore io_lock; // IO -lock
-
- char in [8]; // Command Input Buffer
- int in_count;
-
- int pic_index; // Cache for the Imagesize (-1 for nothing cached )
- int pic_len;
-};
-
-
-/* Specification of the Endpoints */
-static struct usb_endpoint_descriptor mdc800_ed [4] =
-{
- { 0,0, 0x01, 0x02, 8, 0,0,0 },
- { 0,0, 0x82, 0x03, 8, 0,0,0 },
- { 0,0, 0x03, 0x02, 64, 0,0,0 },
- { 0,0, 0x84, 0x02, 64, 0,0,0 }
-};
-
-
-/* The Variable used by the driver */
-static struct mdc800_data* mdc800=0;
-
-
-/***************************************************************************
- The USB Part of the driver
-****************************************************************************/
-
-static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b)
-{
- return (
- ( a->bEndpointAddress == b->bEndpointAddress )
- && ( a->bmAttributes == b->bmAttributes )
- && ( a->wMaxPacketSize == b->wMaxPacketSize )
- );
-}
-
-
-/*
- * Checks wether the camera responds busy
- */
-static int mdc800_isBusy (char* ch)
-{
- int i=0;
- while (i<8)
- {
- if (ch [i] != (char)0x99)
- return 0;
- i++;
- }
- return 1;
-}
-
-
-/*
- * Checks wether the Camera is ready
- */
-static int mdc800_isReady (char *ch)
-{
- int i=0;
- while (i<8)
- {
- if (ch [i] != (char)0xbb)
- return 0;
- i++;
- }
- return 1;
-}
-
-
-
-/*
- * USB IRQ Handler for InputLine
- */
-static void mdc800_usb_irq (struct urb *urb)
-{
- int data_received=0, wake_up;
- unsigned char* b=urb->transfer_buffer;
- struct mdc800_data* mdc800=urb->context;
-
- if (urb->status >= 0)
- {
-
- //dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
-
- if (mdc800_isBusy (b))
- {
- if (!mdc800->camera_busy)
- {
- mdc800->camera_busy=1;
- dbg ("gets busy");
- }
- }
- else
- {
- if (mdc800->camera_busy && mdc800_isReady (b))
- {
- mdc800->camera_busy=0;
- dbg ("gets ready");
- }
- }
- if (!(mdc800_isBusy (b) || mdc800_isReady (b)))
- {
- /* Store Data in camera_answer field */
- dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
-
- memcpy (mdc800->camera_response,b,8);
- data_received=1;
- }
- }
- wake_up= ( mdc800->camera_request_ready > 0 )
- &&
- (
- ((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
- ||
- ((mdc800->camera_request_ready == 2) && data_received)
- ||
- ((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
- ||
- (urb->status < 0)
- );
-
- if (wake_up)
- {
- mdc800->camera_request_ready=0;
- mdc800->irq_woken=1;
- wake_up_interruptible (&mdc800->irq_wait);
- }
-}
-
-
-/*
- * Waits a while until the irq responds that camera is ready
- *
- * mode : 0: Wait for camera gets ready
- * 1: Wait for receiving data
- * 2: Wait for camera gets busy
- *
- * msec: Time to wait
- */
-static int mdc800_usb_waitForIRQ (int mode, int msec)
-{
- DECLARE_WAITQUEUE(wait, current);
-
- mdc800->camera_request_ready=1+mode;
-
- add_wait_queue(&mdc800->irq_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- if (!mdc800->irq_woken)
- {
- schedule_timeout (msec*HZ/1000);
- }
- remove_wait_queue(&mdc800->irq_wait, &wait);
- set_current_state(TASK_RUNNING);
- mdc800->irq_woken = 0;
-
- if (mdc800->camera_request_ready>0)
- {
- mdc800->camera_request_ready=0;
- err ("timeout waiting for camera.");
- return -1;
- }
-
- if (mdc800->state == NOT_CONNECTED)
- {
- warn ("Camera gets disconnected during waiting for irq.");
- mdc800->camera_request_ready=0;
- return -2;
- }
-
- return 0;
-}
-
-
-/*
- * The write_urb callback function
- */
-static void mdc800_usb_write_notify (struct urb *urb)
-{
- struct mdc800_data* mdc800=urb->context;
-
- if (urb->status != 0)
- {
- err ("writing command fails (status=%i)", urb->status);
- }
- else
- {
- mdc800->state=READY;
- }
- mdc800->written = 1;
- wake_up_interruptible (&mdc800->write_wait);
-}
-
-
-/*
- * The download_urb callback function
- */
-static void mdc800_usb_download_notify (struct urb *urb)
-{
- struct mdc800_data* mdc800=urb->context;
-
- if (urb->status == 0)
- {
- /* Fill output buffer with these data */
- memcpy (mdc800->out, urb->transfer_buffer, 64);
- mdc800->out_count=64;
- mdc800->out_ptr=0;
- mdc800->download_left-=64;
- if (mdc800->download_left == 0)
- {
- mdc800->state=READY;
- }
- }
- else
- {
- err ("request bytes fails (status:%i)", urb->status);
- }
- mdc800->downloaded = 1;
- wake_up_interruptible (&mdc800->download_wait);
-}
-
-
-/***************************************************************************
- Probing for the Camera
- ***************************************************************************/
-
-static struct usb_driver mdc800_usb_driver;
-
-/*
- * Callback to search the Mustek MDC800 on the USB Bus
- */
-static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum,
- const struct usb_device_id *id)
-{
- int i,j;
- struct usb_interface_descriptor *intf_desc;
- int irq_interval=0;
-
- dbg ("(mdc800_usb_probe) called.");
-
-
- if (mdc800->dev != 0)
- {
- warn ("only one Mustek MDC800 is supported.");
- return 0;
- }
-
- if (dev->descriptor.bNumConfigurations != 1)
- {
- err ("probe fails -> wrong Number of Configuration");
- return 0;
- }
- intf_desc=&dev->actconfig->interface[ifnum].altsetting[0];
-
- if (
- ( intf_desc->bInterfaceClass != 0xff )
- || ( intf_desc->bInterfaceSubClass != 0 )
- || ( intf_desc->bInterfaceProtocol != 0 )
- || ( intf_desc->bNumEndpoints != 4)
- )
- {
- err ("probe fails -> wrong Interface");
- return 0;
- }
-
- /* Check the Endpoints */
- for (i=0; i<4; i++)
- {
- mdc800->endpoint[i]=-1;
- for (j=0; j<4; j++)
- {
- if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i]))
- {
- mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ;
- if (i==1)
- {
- irq_interval=intf_desc->endpoint [j].bInterval;
- }
-
- continue;
- }
- }
- if (mdc800->endpoint[i] == -1)
- {
- err ("probe fails -> Wrong Endpoints.");
- return 0;
- }
- }
-
-
- usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800);
- if (usb_set_interface (dev, ifnum, 0) < 0)
- {
- err ("MDC800 Configuration fails.");
- return 0;
- }
-
- info ("Found Mustek MDC800 on USB.");
-
- down (&mdc800->io_lock);
-
- mdc800->dev=dev;
- mdc800->open=0;
-
- /* Setup URB Structs */
- FILL_INT_URB (
- mdc800->irq_urb,
- mdc800->dev,
- usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]),
- mdc800->irq_urb_buffer,
- 8,
- mdc800_usb_irq,
- mdc800,
- irq_interval
- );
-
- FILL_BULK_URB (
- mdc800->write_urb,
- mdc800->dev,
- usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]),
- mdc800->write_urb_buffer,
- 8,
- mdc800_usb_write_notify,
- mdc800
- );
-
- FILL_BULK_URB (
- mdc800->download_urb,
- mdc800->dev,
- usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]),
- mdc800->download_urb_buffer,
- 64,
- mdc800_usb_download_notify,
- mdc800
- );
-
- mdc800->state=READY;
-
- up (&mdc800->io_lock);
-
- return mdc800;
-}
-
-
-/*
- * Disconnect USB device (maybe the MDC800)
- */
-static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr)
-{
- struct mdc800_data* mdc800=(struct mdc800_data*) ptr;
-
- dbg ("(mdc800_usb_disconnect) called");
-
- if (mdc800->state == NOT_CONNECTED)
- return;
-
- mdc800->state=NOT_CONNECTED;
-
- usb_unlink_urb (mdc800->irq_urb);
- usb_unlink_urb (mdc800->write_urb);
- usb_unlink_urb (mdc800->download_urb);
-
- usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]);
-
- mdc800->dev=0;
- info ("Mustek MDC800 disconnected from USB.");
-}
-
-
-/***************************************************************************
- The Misc device Part (file_operations)
-****************************************************************************/
-
-/*
- * This Function calc the Answersize for a command.
- */
-static int mdc800_getAnswerSize (char command)
-{
- switch ((unsigned char) command)
- {
- case 0x2a:
- case 0x49:
- case 0x51:
- case 0x0d:
- case 0x20:
- case 0x07:
- case 0x01:
- case 0x25:
- case 0x00:
- return 8;
-
- case 0x05:
- case 0x3e:
- return mdc800->pic_len;
-
- case 0x09:
- return 4096;
-
- default:
- return 0;
- }
-}
-
-
-/*
- * Init the device: (1) alloc mem (2) Increase MOD Count ..
- */
-static int mdc800_device_open (struct inode* inode, struct file *file)
-{
- int retval=0;
- int errn=0;
-
- down (&mdc800->io_lock);
-
- if (mdc800->state == NOT_CONNECTED)
- {
- errn=-EBUSY;
- goto error_out;
- }
- if (mdc800->open)
- {
- errn=-EBUSY;
- goto error_out;
- }
-
- mdc800->in_count=0;
- mdc800->out_count=0;
- mdc800->out_ptr=0;
- mdc800->pic_index=0;
- mdc800->pic_len=-1;
- mdc800->download_left=0;
-
- mdc800->camera_busy=0;
- mdc800->camera_request_ready=0;
-
- retval=0;
- mdc800->irq_urb->dev = mdc800->dev;
- if (usb_submit_urb (mdc800->irq_urb, GFP_KERNEL))
- {
- err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
- errn = -EIO;
- goto error_out;
- }
-
- mdc800->open=1;
- dbg ("Mustek MDC800 device opened.");
-
-error_out:
- up (&mdc800->io_lock);
- return errn;
-}
-
-
-/*
- * Close the Camera and release Memory
- */
-static int mdc800_device_release (struct inode* inode, struct file *file)
-{
- int retval=0;
- dbg ("Mustek MDC800 device closed.");
-
- down (&mdc800->io_lock);
- if (mdc800->open && (mdc800->state != NOT_CONNECTED))
- {
- usb_unlink_urb (mdc800->irq_urb);
- usb_unlink_urb (mdc800->write_urb);
- usb_unlink_urb (mdc800->download_urb);
- mdc800->open=0;
- }
- else
- {
- retval=-EIO;
- }
-
- up(&mdc800->io_lock);
- return retval;
-}
-
-
-/*
- * The Device read callback Function
- */
-static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos)
-{
- int left=len, sts=len; /* single transfer size */
- char* ptr=buf;
- DECLARE_WAITQUEUE(wait, current);
-
- down (&mdc800->io_lock);
- if (mdc800->state == NOT_CONNECTED)
- {
- up (&mdc800->io_lock);
- return -EBUSY;
- }
- if (mdc800->state == WORKING)
- {
- warn ("Illegal State \"working\" reached during read ?!");
- up (&mdc800->io_lock);
- return -EBUSY;
- }
- if (!mdc800->open)
- {
- up (&mdc800->io_lock);
- return -EBUSY;
- }
-
- while (left)
- {
- if (signal_pending (current))
- {
- up (&mdc800->io_lock);
- return -EINTR;
- }
-
- sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
-
- if (sts <= 0)
- {
- /* Too less Data in buffer */
- if (mdc800->state == DOWNLOAD)
- {
- mdc800->out_count=0;
- mdc800->out_ptr=0;
-
- /* Download -> Request new bytes */
- mdc800->download_urb->dev = mdc800->dev;
- if (usb_submit_urb (mdc800->download_urb, GFP_KERNEL))
- {
- err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
- up (&mdc800->io_lock);
- return len-left;
- }
- add_wait_queue(&mdc800->download_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- if (!mdc800->downloaded)
- {
- schedule_timeout (TO_DOWNLOAD_GET_READY*HZ/1000);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&mdc800->download_wait, &wait);
- mdc800->downloaded = 0;
- if (mdc800->download_urb->status != 0)
- {
- err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
- up (&mdc800->io_lock);
- return len-left;
- }
- }
- else
- {
- /* No more bytes -> that's an error*/
- up (&mdc800->io_lock);
- return -EIO;
- }
- }
- else
- {
- /* memcpy Bytes */
- memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts);
- ptr+=sts;
- left-=sts;
- mdc800->out_ptr+=sts;
- }
- }
-
- up (&mdc800->io_lock);
- return len-left;
-}
-
-
-/*
- * The Device write callback Function
- * If a 8Byte Command is received, it will be send to the camera.
- * After this the driver initiates the request for the answer or
- * just waits until the camera becomes ready.
- */
-static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
-{
- int i=0;
- DECLARE_WAITQUEUE(wait, current);
-
- down (&mdc800->io_lock);
- if (mdc800->state != READY)
- {
- up (&mdc800->io_lock);
- return -EBUSY;
- }
- if (!mdc800->open )
- {
- up (&mdc800->io_lock);
- return -EBUSY;
- }
-
- while (i<len)
- {
- if (signal_pending (current))
- {
- up (&mdc800->io_lock);
- return -EINTR;
- }
-
- /* check for command start */
- if (buf [i] == (char) 0x55)
- {
- mdc800->in_count=0;
- mdc800->out_count=0;
- mdc800->out_ptr=0;
- mdc800->download_left=0;
- }
-
- /* save command byte */
- if (mdc800->in_count < 8)
- {
- mdc800->in[mdc800->in_count]=buf[i];
- mdc800->in_count++;
- }
- else
- {
- err ("Command is to long !\n");
- up (&mdc800->io_lock);
- return -EIO;
- }
-
- /* Command Buffer full ? -> send it to camera */
- if (mdc800->in_count == 8)
- {
- int answersize;
-
- if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
- {
- err ("Camera didn't get ready.\n");
- up (&mdc800->io_lock);
- return -EIO;
- }
-
- answersize=mdc800_getAnswerSize (mdc800->in[1]);
-
- mdc800->state=WORKING;
- memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8);
- mdc800->write_urb->dev = mdc800->dev;
- if (usb_submit_urb (mdc800->write_urb, GFP_KERNEL))
- {
- err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
- up (&mdc800->io_lock);
- return -EIO;
- }
- add_wait_queue(&mdc800->write_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- if (!mdc800->written)
- {
- schedule_timeout (TO_WRITE_GET_READY*HZ/1000);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&mdc800->write_wait, &wait);
- mdc800->written = 0;
- if (mdc800->state == WORKING)
- {
- usb_unlink_urb (mdc800->write_urb);
- up (&mdc800->io_lock);
- return -EIO;
- }
-
- switch ((unsigned char) mdc800->in[1])
- {
- case 0x05: /* Download Image */
- case 0x3e: /* Take shot in Fine Mode (WCam Mode) */
- if (mdc800->pic_len < 0)
- {
- err ("call 0x07 before 0x05,0x3e");
- mdc800->state=READY;
- up (&mdc800->io_lock);
- return -EIO;
- }
- mdc800->pic_len=-1;
-
- case 0x09: /* Download Thumbnail */
- mdc800->download_left=answersize+64;
- mdc800->state=DOWNLOAD;
- mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY);
- break;
-
-
- default:
- if (answersize)
- {
-
- if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
- {
- err ("requesting answer from irq fails");
- up (&mdc800->io_lock);
- return -EIO;
- }
-
- /* Write dummy data, (this is ugly but part of the USB Protokoll */
- /* if you use endpoint 1 as bulk and not as irq */
- memcpy (mdc800->out, mdc800->camera_response,8);
-
- /* This is the interpreted answer */
- memcpy (&mdc800->out[8], mdc800->camera_response,8);
-
- mdc800->out_ptr=0;
- mdc800->out_count=16;
-
- /* Cache the Imagesize, if command was getImageSize */
- if (mdc800->in [1] == (char) 0x07)
- {
- mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2];
-
- dbg ("cached imagesize = %i",mdc800->pic_len);
- }
-
- }
- else
- {
- if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
- {
- err ("Command Timeout.");
- up (&mdc800->io_lock);
- return -EIO;
- }
- }
- mdc800->state=READY;
- break;
- }
- }
- i++;
- }
- up (&mdc800->io_lock);
- return i;
-}
-
-
-/***************************************************************************
- Init and Cleanup this driver (Structs and types)
-****************************************************************************/
-
-/* File Operations of this drivers */
-static struct file_operations mdc800_device_ops =
-{
- owner: THIS_MODULE,
- read: mdc800_device_read,
- write: mdc800_device_write,
- open: mdc800_device_open,
- release: mdc800_device_release,
-};
-
-
-
-static struct usb_device_id mdc800_table [] = {
- { USB_DEVICE(MDC800_VENDOR_ID, MDC800_PRODUCT_ID) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, mdc800_table);
-/*
- * USB Driver Struct for this device
- */
-static struct usb_driver mdc800_usb_driver =
-{
- name: "mdc800",
- probe: mdc800_usb_probe,
- disconnect: mdc800_usb_disconnect,
- fops: &mdc800_device_ops,
- minor: MDC800_DEVICE_MINOR_BASE,
- id_table: mdc800_table
-};
-
-
-
-/************************************************************************
- Init and Cleanup this driver (Main Functions)
-*************************************************************************/
-
-#define try(A) if ((A) == 0) goto cleanup_on_fail;
-#define try_free_mem(A) if (A != 0) { kfree (A); A=0; }
-#define try_free_urb(A) if (A != 0) { usb_free_urb (A); A=0; }
-
-int __init usb_mdc800_init (void)
-{
- /* Allocate Memory */
- try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
-
- memset(mdc800, 0, sizeof(struct mdc800_data));
- mdc800->dev=0;
- mdc800->open=0;
- mdc800->state=NOT_CONNECTED;
- init_MUTEX (&mdc800->io_lock);
-
- init_waitqueue_head (&mdc800->irq_wait);
- init_waitqueue_head (&mdc800->write_wait);
- init_waitqueue_head (&mdc800->download_wait);
-
- mdc800->irq_woken = 0;
- mdc800->downloaded = 0;
- mdc800->written = 0;
-
- try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
- try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
- try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL));
-
- try (mdc800->irq_urb=usb_alloc_urb (0, GFP_KERNEL));
- try (mdc800->download_urb=usb_alloc_urb (0, GFP_KERNEL));
- try (mdc800->write_urb=usb_alloc_urb (0, GFP_KERNEL));
-
- /* Register the driver */
- if (usb_register (&mdc800_usb_driver) < 0)
- goto cleanup_on_fail;
-
- info (DRIVER_VERSION ":" DRIVER_DESC);
-
- return 0;
-
- /* Clean driver up, when something fails */
-
-cleanup_on_fail:
-
- if (mdc800 != 0)
- {
- err ("can't alloc memory!");
-
- try_free_mem (mdc800->download_urb_buffer);
- try_free_mem (mdc800->write_urb_buffer);
- try_free_mem (mdc800->irq_urb_buffer);
-
- try_free_urb (mdc800->write_urb);
- try_free_urb (mdc800->download_urb);
- try_free_urb (mdc800->irq_urb);
-
- kfree (mdc800);
- }
- mdc800=0;
- return -1;
-}
-
-
-void __exit usb_mdc800_cleanup (void)
-{
- usb_deregister (&mdc800_usb_driver);
-
- usb_free_urb (mdc800->irq_urb);
- usb_free_urb (mdc800->download_urb);
- usb_free_urb (mdc800->write_urb);
-
- kfree (mdc800->irq_urb_buffer);
- kfree (mdc800->write_urb_buffer);
- kfree (mdc800->download_urb_buffer);
-
- kfree (mdc800);
- mdc800=0;
-}
-
-module_init (usb_mdc800_init);
-module_exit (usb_mdc800_cleanup);
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-
+++ /dev/null
-/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
- *
- * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
- * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
- *
- * Parts shamelessly stolen from usb-storage and copyright by their
- * authors. Thanks to Matt Dharm for giving us permission!
- *
- * This driver implements a SCSI host controller driver and a USB
- * device driver. To avoid confusion, all the USB related stuff is
- * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
- *
- * Microtek (www.microtek.com) did not release the specifications for
- * their USB protocol to us, so we had to reverse engineer them. We
- * don't know for which models they are valid.
- *
- * The X6 USB has three bulk endpoints, one output (0x1) down which
- * commands and outgoing data are sent, and two input: 0x82 from which
- * normal data is read from the scanner (in packets of maximum 32
- * bytes) and from which the status byte is read, and 0x83 from which
- * the results of a scan (or preview) are read in up to 64 * 1024 byte
- * chunks by the Windows driver. We don't know how much it is possible
- * to read at a time from 0x83.
- *
- * It seems possible to read (with URB transfers) everything from 0x82
- * in one go, without bothering to read in 32 byte chunks.
- *
- * There seems to be an optimisation of a further READ implicit if
- * you simply read from 0x83.
- *
- * Guessed protocol:
- *
- * Send raw SCSI command to EP 0x1
- *
- * If there is data to receive:
- * If the command was READ datatype=image:
- * Read a lot of data from EP 0x83
- * Else:
- * Read data from EP 0x82
- * Else:
- * If there is data to transmit:
- * Write it to EP 0x1
- *
- * Read status byte from EP 0x82
- *
- * References:
- *
- * The SCSI command set for the scanner is available from
- * ftp://ftp.microtek.com/microtek/devpack/
- *
- * Microtek NV sent us a more up to date version of the document. If
- * you want it, just send mail.
- *
- * Status:
- *
- * Untested with multiple scanners.
- * Untested on SMP.
- * Untested on a bigendian machine.
- *
- * History:
- *
- * 20000417 starting history
- * 20000417 fixed load oops
- * 20000417 fixed unload oops
- * 20000419 fixed READ IMAGE detection
- * 20000424 started conversion to use URBs
- * 20000502 handled short transfers as errors
- * 20000513 rename and organisation of functions (john)
- * 20000513 added IDs for all products supported by Windows driver (john)
- * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
- * 20000514 Version 0.0.8j
- * 20000514 Fix reporting of non-existant devices to SCSI layer (john)
- * 20000514 Added MTS_DEBUG_INT (john)
- * 20000514 Changed "usb-microtek" to "microtek" for consistency (john)
- * 20000514 Stupid bug fixes (john)
- * 20000514 Version 0.0.9j
- * 20000515 Put transfer context and URB in mts_desc (john)
- * 20000515 Added prelim turn off debugging support (john)
- * 20000515 Version 0.0.10j
- * 20000515 Fixed up URB allocation (clear URB on alloc) (john)
- * 20000515 Version 0.0.11j
- * 20000516 Removed unnecessary spinlock in mts_transfer_context (john)
- * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
- * 20000516 Implemented (badly) scsi_abort (john)
- * 20000516 Version 0.0.12j
- * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
- * 20000517 Added mts_debug_dump to print ll USB info (john)
- * 20000518 Tweaks and documentation updates (john)
- * 20000518 Version 0.0.13j
- * 20000518 Cleaned up abort handling (john)
- * 20000523 Removed scsi_command and various scsi_..._resets (john)
- * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
- * 20000523 Fixed last tiresome compile warning (john)
- * 20000523 Version 0.0.14j (though version 0.1 has come out?)
- * 20000602 Added primitive reset
- * 20000602 Version 0.2.0
- * 20000603 various cosmetic changes
- * 20000603 Version 0.2.1
- * 20000620 minor cosmetic changes
- * 20000620 Version 0.2.2
- * 20000822 Hopefully fixed deadlock in mts_remove_nolock()
- * 20000822 Fixed minor race in mts_transfer_cleanup()
- * 20000822 Fixed deadlock on submission error in queuecommand
- * 20000822 Version 0.2.3
- * 20000913 Reduced module size if debugging is off
- * 20000913 Version 0.2.4
- * 20010210 New abort logic
- * 20010210 Version 0.3.0
- * 20010217 Merged scatter/gather
- * 20010218 Version 0.4.0
- * 20010218 Cosmetic fixes
- * 20010218 Version 0.4.1
- * 20010306 Abort while using scatter/gather
- * 20010306 Version 0.4.2
- * 20010311 Remove all timeouts and tidy up generally (john)
- * 20010320 check return value of scsi_register()
- * 20010320 Version 0.4.3
- * 20010408 Identify version on module load.
- * 20011003 Fix multiple requests
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
-#include <linux/usb.h>
-#include <linux/proc_fs.h>
-
-#include <asm/atomic.h>
-#include <linux/blk.h>
-#include "../../scsi/scsi.h"
-#include "../../scsi/hosts.h"
-#include "../../scsi/sd.h"
-
-#include "microtek.h"
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.4.3"
-#define DRIVER_AUTHOR "John Fremlin <vii@penguinpowered.com>, Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>"
-#define DRIVER_DESC "Microtek Scanmaker X6 USB scanner driver"
-
-/* Should we do debugging? */
-
-//#define MTS_DO_DEBUG
-
-/* USB layer driver interface */
-
-static void *mts_usb_probe(struct usb_device *dev, unsigned int interface,
- const struct usb_device_id *id);
-static void mts_usb_disconnect(struct usb_device *dev, void *ptr);
-
-static struct usb_device_id mts_usb_ids [];
-
-static struct usb_driver mts_usb_driver = {
- name: "microtekX6",
- probe: mts_usb_probe,
- disconnect: mts_usb_disconnect,
- id_table: mts_usb_ids,
-};
-
-
-/* Internal driver stuff */
-
-#define MTS_VERSION "0.4.3"
-#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
-
-#define MTS_WARNING(x...) \
- printk( KERN_WARNING MTS_NAME x )
-#define MTS_ERROR(x...) \
- printk( KERN_ERR MTS_NAME x )
-#define MTS_INT_ERROR(x...) \
- MTS_ERROR(x)
-#define MTS_MESSAGE(x...) \
- printk( KERN_INFO MTS_NAME x )
-
-#if defined MTS_DO_DEBUG
-
-#define MTS_DEBUG(x...) \
- printk( KERN_DEBUG MTS_NAME x )
-
-#define MTS_DEBUG_GOT_HERE() \
- MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ )
-#define MTS_DEBUG_INT() \
- do { MTS_DEBUG_GOT_HERE(); \
- MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
- MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
- mts_debug_dump(context->instance);\
- } while(0)
-#else
-
-#define MTS_NUL_STATEMENT do { } while(0)
-
-#define MTS_DEBUG(x...) MTS_NUL_STATEMENT
-#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
-#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
-
-#endif
-
-
-
-#define MTS_INT_INIT()\
- struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \
- MTS_DEBUG_INT();\
-
-#ifdef MTS_DO_DEBUG
-
-static inline void mts_debug_dump(struct mts_desc* desc) {
- MTS_DEBUG("desc at 0x%x: halted = %02x%02x, toggle = %02x%02x\n",
- (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0],
- (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
- );
- MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
- usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
- usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
- usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
- );
-}
-
-
-static inline void mts_show_command(Scsi_Cmnd *srb)
-{
- char *what = NULL;
-
- switch (srb->cmnd[0]) {
- case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
- case REZERO_UNIT: what = "REZERO_UNIT"; break;
- case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
- case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
- case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
- case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
- case READ_6: what = "READ_6"; break;
- case WRITE_6: what = "WRITE_6"; break;
- case SEEK_6: what = "SEEK_6"; break;
- case READ_REVERSE: what = "READ_REVERSE"; break;
- case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
- case SPACE: what = "SPACE"; break;
- case INQUIRY: what = "INQUIRY"; break;
- case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
- case MODE_SELECT: what = "MODE_SELECT"; break;
- case RESERVE: what = "RESERVE"; break;
- case RELEASE: what = "RELEASE"; break;
- case COPY: what = "COPY"; break;
- case ERASE: what = "ERASE"; break;
- case MODE_SENSE: what = "MODE_SENSE"; break;
- case START_STOP: what = "START_STOP"; break;
- case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
- case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
- case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
- case SET_WINDOW: what = "SET_WINDOW"; break;
- case READ_CAPACITY: what = "READ_CAPACITY"; break;
- case READ_10: what = "READ_10"; break;
- case WRITE_10: what = "WRITE_10"; break;
- case SEEK_10: what = "SEEK_10"; break;
- case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
- case VERIFY: what = "VERIFY"; break;
- case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
- case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
- case SEARCH_LOW: what = "SEARCH_LOW"; break;
- case SET_LIMITS: what = "SET_LIMITS"; break;
- case READ_POSITION: what = "READ_POSITION"; break;
- case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
- case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
- case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
- case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
- case COMPARE: what = "COMPARE"; break;
- case COPY_VERIFY: what = "COPY_VERIFY"; break;
- case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
- case READ_BUFFER: what = "READ_BUFFER"; break;
- case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
- case READ_LONG: what = "READ_LONG"; break;
- case WRITE_LONG: what = "WRITE_LONG"; break;
- case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
- case WRITE_SAME: what = "WRITE_SAME"; break;
- case READ_TOC: what = "READ_TOC"; break;
- case LOG_SELECT: what = "LOG_SELECT"; break;
- case LOG_SENSE: what = "LOG_SENSE"; break;
- case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
- case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
- case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
- case READ_12: what = "READ_12"; break;
- case WRITE_12: what = "WRITE_12"; break;
- case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
- case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
- case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
- case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
- case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
- case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
- case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
- default:
- MTS_DEBUG("can't decode command\n");
- goto out;
- break;
- }
- MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
-
- out:
- MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
- srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
- srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
-}
-
-#else
-
-static inline void mts_show_command(Scsi_Cmnd * dummy)
-{
-}
-
-static inline void mts_debug_dump(struct mts_desc* dummy)
-{
-}
-
-#endif
-
-static inline void mts_urb_abort(struct mts_desc* desc) {
- MTS_DEBUG_GOT_HERE();
- mts_debug_dump(desc);
-
- usb_unlink_urb( desc->urb );
-}
-
-static struct mts_desc * mts_list; /* list of active scanners */
-struct semaphore mts_list_semaphore;
-
-/* Internal list operations */
-
-static
-void mts_remove_nolock( struct mts_desc* to_remove )
-{
- MTS_DEBUG( "removing 0x%x from list\n",
- (int)to_remove );
-
- lock_kernel();
- mts_urb_abort(to_remove);
-
- MTS_DEBUG_GOT_HERE();
-
- if ( to_remove != mts_list ) {
- MTS_DEBUG_GOT_HERE();
- if (to_remove->prev && to_remove->next)
- to_remove->prev->next = to_remove->next;
- } else {
- MTS_DEBUG_GOT_HERE();
- mts_list = to_remove->next;
- if (mts_list) {
- MTS_DEBUG_GOT_HERE();
- mts_list->prev = 0;
- }
- }
-
- if ( to_remove->next ) {
- MTS_DEBUG_GOT_HERE();
- to_remove->next->prev = to_remove->prev;
- }
-
- MTS_DEBUG_GOT_HERE();
- scsi_unregister_host(&to_remove->ctempl);
- unlock_kernel();
-
- usb_free_urb(to_remove->urb);
- kfree( to_remove );
-}
-
-static
-void mts_add_nolock( struct mts_desc* to_add )
-{
- MTS_DEBUG( "adding 0x%x to list\n", (int)to_add );
-
- to_add->prev = 0;
- to_add->next = mts_list;
- if ( mts_list ) {
- mts_list->prev = to_add;
- }
-
- mts_list = to_add;
-}
-
-
-
-
-/* SCSI driver interface */
-
-/* scsi related functions - dummies for now mostly */
-
-static int mts_scsi_release(struct Scsi_Host *psh)
-{
- MTS_DEBUG_GOT_HERE();
-
- return 0;
-}
-
-static int mts_scsi_abort (Scsi_Cmnd *srb)
-{
- struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
-
- MTS_DEBUG_GOT_HERE();
-
- mts_urb_abort(desc);
-
- return SCSI_ABORT_PENDING;
-}
-
-static int mts_scsi_host_reset (Scsi_Cmnd *srb)
-{
- struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
-
- MTS_DEBUG_GOT_HERE();
- mts_debug_dump(desc);
-
- usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */
- return 0; /* RANT why here 0 and not SUCCESS */
-}
-
-/* the core of the scsi part */
-
-/* faking a detection - which can't fail :-) */
-
-static int mts_scsi_detect (struct SHT * sht)
-{
- /* Whole function stolen from usb-storage */
-
- struct mts_desc * desc = (struct mts_desc *)sht->proc_dir;
- /* What a hideous hack! */
-
- char local_name[48];
-
- MTS_DEBUG_GOT_HERE();
-
- /* set up the name of our subdirectory under /proc/scsi/ */
- sprintf(local_name, "microtek-%d", desc->host_number);
- sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
- /* FIXME: where is this freed ? */
-
- if (!sht->proc_name) {
- MTS_ERROR( "unable to allocate memory for proc interface!!\n" );
- return 0;
- }
-
- strcpy(sht->proc_name, local_name);
-
- sht->proc_dir = NULL;
-
- /* In host->hostdata we store a pointer to desc */
- desc->host = scsi_register(sht, sizeof(desc));
- if (desc->host == NULL) {
- MTS_ERROR("Cannot register due to low memory");
- kfree(sht->proc_name);
- return 0;
- }
- desc->host->hostdata[0] = (unsigned long)desc;
-/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */
-
- return 1;
-}
-
-
-
-/* Main entrypoint: SCSI commands are dispatched to here */
-
-
-
-static
-int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
-
-static void mts_transfer_cleanup( struct urb *transfer );
-static void mts_do_sg(struct urb * transfer);
-
-
-inline static
-void mts_int_submit_urb (struct urb* transfer,
- int pipe,
- void* data,
- unsigned length,
- mts_usb_urb_callback callback )
-/* Interrupt context! */
-
-/* Holding transfer->context->lock! */
-{
- int res;
-
- MTS_INT_INIT();
-
- FILL_BULK_URB(transfer,
- context->instance->usb_dev,
- pipe,
- data,
- length,
- callback,
- context
- );
-
- transfer->status = 0;
-
- res = usb_submit_urb( transfer, GFP_ATOMIC );
- if ( unlikely(res) ) {
- MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
- context->srb->result = DID_ERROR << 16;
- mts_transfer_cleanup(transfer);
- }
- return;
-}
-
-
-static void mts_transfer_cleanup( struct urb *transfer )
-/* Interrupt context! */
-{
- MTS_INT_INIT();
-
- if ( likely(context->final_callback != NULL) )
- context->final_callback(context->srb);
-
-}
-
-static void mts_transfer_done( struct urb *transfer )
-{
- MTS_INT_INIT();
-
- context->srb->result &= MTS_SCSI_ERR_MASK;
- context->srb->result |= (unsigned)context->status<<1;
-
- mts_transfer_cleanup(transfer);
-
- return;
-}
-
-
-static void mts_get_status( struct urb *transfer )
-/* Interrupt context! */
-{
- MTS_INT_INIT();
-
- mts_int_submit_urb(transfer,
- usb_rcvbulkpipe(context->instance->usb_dev,
- context->instance->ep_response),
- &context->status,
- 1,
- mts_transfer_done );
-}
-
-static void mts_data_done( struct urb* transfer )
-/* Interrupt context! */
-{
- MTS_INT_INIT();
-
- if ( context->data_length != transfer->actual_length ) {
- context->srb->resid = context->data_length - transfer->actual_length;
- } else if ( unlikely(transfer->status) ) {
- context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
- }
-
- mts_get_status(transfer);
-
- return;
-}
-
-
-static void mts_command_done( struct urb *transfer )
-/* Interrupt context! */
-{
- MTS_INT_INIT();
-
- if ( unlikely(transfer->status) ) {
- if (transfer->status == -ENOENT) {
- /* We are being killed */
- MTS_DEBUG_GOT_HERE();
- context->srb->result = DID_ABORT<<16;
- } else {
- /* A genuine error has occured */
- MTS_DEBUG_GOT_HERE();
-
- context->srb->result = DID_ERROR<<16;
- }
- mts_transfer_cleanup(transfer);
-
- return;
- }
-
- if ( context->data ) {
- mts_int_submit_urb(transfer,
- context->data_pipe,
- context->data,
- context->data_length,
- context->srb->use_sg ? mts_do_sg : mts_data_done);
- } else mts_get_status(transfer);
-
- return;
-}
-
-static void mts_do_sg (struct urb* transfer)
-{
- struct scatterlist * sg;
- MTS_INT_INIT();
-
- MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,context->srb->use_sg);
-
- if (unlikely(transfer->status)) {
- context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
- mts_transfer_cleanup(transfer);
- }
-
- sg = context->srb->buffer;
- context->fragment++;
- mts_int_submit_urb(transfer,
- context->data_pipe,
- page_address(sg[context->fragment].page) +
- sg[context->fragment].offset,
- sg[context->fragment].length,
- context->fragment + 1 == context->srb->use_sg ? mts_data_done : mts_do_sg);
- return;
-}
-
-static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
-static const u8 mts_read_image_sig_len = 4;
-static const unsigned char mts_direction[256/8] = {
- 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
- 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-
-#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
-
-static void
-mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
-{
- int pipe;
- struct scatterlist * sg;
-
- MTS_DEBUG_GOT_HERE();
-
- desc->context.instance = desc;
- desc->context.srb = srb;
- desc->context.fragment = 0;
-
- if (!srb->use_sg) {
- if ( !srb->bufflen ){
- desc->context.data = 0;
- desc->context.data_length = 0;
- return;
- } else {
- desc->context.data = srb->buffer;
- desc->context.data_length = srb->bufflen;
- MTS_DEBUG("length = %d or %d\n",
- srb->request_bufflen, srb->bufflen);
- }
- } else {
- MTS_DEBUG("Using scatter/gather\n");
- sg = srb->buffer;
- desc->context.data = page_address(sg[0].page) + sg[0].offset;
- desc->context.data_length = sg[0].length;
- }
-
-
- /* can't rely on srb->sc_data_direction */
-
- /* Brutally ripped from usb-storage */
-
- if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
-) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
- MTS_DEBUG( "transfering from desc->ep_image == %d\n",
- (int)desc->ep_image );
- } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
- pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
- MTS_DEBUG( "transfering from desc->ep_response == %d\n",
- (int)desc->ep_response);
- } else {
- MTS_DEBUG("transfering to desc->ep_out == %d\n",
- (int)desc->ep_out);
- pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
- }
- desc->context.data_pipe = pipe;
-}
-
-
-static
-int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
-{
- struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
- int err = 0;
- int res;
-
- MTS_DEBUG_GOT_HERE();
- mts_show_command(srb);
- mts_debug_dump(desc);
-
- if ( srb->device->lun || srb->device->id || srb->device->channel ) {
-
- MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
-
- MTS_DEBUG("this device doesn't exist\n");
-
- srb->result = DID_BAD_TARGET << 16;
-
- if(likely(callback != NULL))
- callback(srb);
-
- goto out;
- }
-
-
- FILL_BULK_URB(desc->urb,
- desc->usb_dev,
- usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
- srb->cmnd,
- srb->cmd_len,
- mts_command_done,
- &desc->context
- );
-
-
- mts_build_transfer_context( srb, desc );
- desc->context.final_callback = callback;
-
- /* here we need ATOMIC as we are called with the iolock */
- res=usb_submit_urb(desc->urb, GFP_ATOMIC);
-
- if(unlikely(res)){
- MTS_ERROR("error %d submitting URB\n",(int)res);
- srb->result = DID_ERROR << 16;
-
- if(likely(callback != NULL))
- callback(srb);
-
- }
-
-out:
- return err;
-}
-/*
- * this defines our 'host'
- */
-
-/* NOTE: This is taken from usb-storage, should be right. */
-
-
-static Scsi_Host_Template mts_scsi_host_template = {
- name: "microtekX6",
- detect: mts_scsi_detect,
- release: mts_scsi_release,
- queuecommand: mts_scsi_queuecommand,
-
- eh_abort_handler: mts_scsi_abort,
- eh_host_reset_handler: mts_scsi_host_reset,
-
- sg_tablesize: SG_ALL,
- can_queue: 1,
- this_id: -1,
- cmd_per_lun: 1,
- present: 0,
- unchecked_isa_dma: FALSE,
- use_clustering: TRUE,
- emulated: TRUE
-};
-
-
-/* USB layer driver interface implementation */
-
-static void mts_usb_disconnect (struct usb_device *dev, void *ptr)
-{
- struct mts_desc* to_remove = (struct mts_desc*)ptr;
-
- MTS_DEBUG_GOT_HERE();
-
- /* leave the list - lock it */
- down(&mts_list_semaphore);
-
- mts_remove_nolock(to_remove);
-
- up(&mts_list_semaphore);
-}
-
-struct vendor_product
-{
- char* name;
- enum
- {
- mts_sup_unknown=0,
- mts_sup_alpha,
- mts_sup_full
- }
- support_status;
-} ;
-
-
-/* These are taken from the msmUSB.inf file on the Windows driver CD */
-const static struct vendor_product mts_supported_products[] =
-{
- { "Phantom 336CX", mts_sup_unknown},
- { "Phantom 336CX", mts_sup_unknown},
- { "Scanmaker X6", mts_sup_alpha},
- { "Phantom C6", mts_sup_unknown},
- { "Phantom 336CX", mts_sup_unknown},
- { "ScanMaker V6USL", mts_sup_unknown},
- { "ScanMaker V6USL", mts_sup_unknown},
- { "Scanmaker V6UL", mts_sup_unknown},
- { "Scanmaker V6UPL", mts_sup_alpha},
-};
-
-/* The entries of microtek_table must correspond, line-by-line to
- the entries of mts_supported_products[]. */
-
-static struct usb_device_id mts_usb_ids [] =
-{
- { USB_DEVICE(0x4ce, 0x0300) },
- { USB_DEVICE(0x5da, 0x0094) },
- { USB_DEVICE(0x5da, 0x0099) },
- { USB_DEVICE(0x5da, 0x009a) },
- { USB_DEVICE(0x5da, 0x00a0) },
- { USB_DEVICE(0x5da, 0x00a3) },
- { USB_DEVICE(0x5da, 0x80a3) },
- { USB_DEVICE(0x5da, 0x80ac) },
- { USB_DEVICE(0x5da, 0x00b6) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, mts_usb_ids);
-
-
-static void * mts_usb_probe (struct usb_device *dev, unsigned int interface,
- const struct usb_device_id *id)
-{
- int i;
- int result;
- int ep_out = -1;
- int ep_in_set[3]; /* this will break if we have more than three endpoints
- which is why we check */
- int *ep_in_current = ep_in_set;
-
- struct mts_desc * new_desc;
- struct vendor_product const* p;
-
- /* the altsettting 0 on the interface we're probing */
- struct usb_interface_descriptor *altsetting;
-
- MTS_DEBUG_GOT_HERE();
- MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
-
- MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
- (int)dev->descriptor.idProduct,
- (int)dev->descriptor.idVendor );
-
- MTS_DEBUG_GOT_HERE();
-
- p = &mts_supported_products[id - mts_usb_ids];
-
- MTS_DEBUG_GOT_HERE();
-
- MTS_DEBUG( "found model %s\n", p->name );
- if ( p->support_status != mts_sup_full )
- MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
- p->name );
-
- /* the altsettting 0 on the interface we're probing */
- altsetting =
- &(dev->actconfig->interface[interface].altsetting[0]);
-
-
- /* Check if the config is sane */
-
- if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) {
- MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
- (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints );
- return NULL;
- }
-
- for( i = 0; i < altsetting->bNumEndpoints; i++ ) {
- if ((altsetting->endpoint[i].bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
-
- MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
- (int)altsetting->endpoint[i].bEndpointAddress );
- } else {
- if (altsetting->endpoint[i].bEndpointAddress &
- USB_DIR_IN)
- *ep_in_current++
- = altsetting->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- else {
- if ( ep_out != -1 ) {
- MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
- return NULL;
- }
-
- ep_out = altsetting->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- }
- }
-
- }
-
-
- if ( ep_out == -1 ) {
- MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
- return NULL;
- }
-
-
- /* I don't understand the following fully (it's from usb-storage) -- John */
-
- /* set the interface -- STALL is an acceptable response here */
- result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
-
- MTS_DEBUG("usb_set_interface returned %d.\n",result);
- switch( result )
- {
- case 0: /* no error */
- break;
-
- case -EPIPE:
- usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
- MTS_DEBUG( "clearing clearing stall on control interface\n" );
- break;
-
- default:
- MTS_DEBUG( "unknown error %d from usb_set_interface\n",
- (int)result );
- return NULL;
- }
-
-
- /* allocating a new descriptor */
- new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL);
- if (new_desc == NULL)
- {
- MTS_ERROR("couldn't allocate scanner desc, bailing out!\n");
- return NULL;
- }
-
- memset( new_desc, 0, sizeof(*new_desc) );
- new_desc->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!new_desc->urb) {
- kfree(new_desc);
- return NULL;
- }
-
- /* initialising that descriptor */
- new_desc->usb_dev = dev;
- new_desc->interface = interface;
-
- init_MUTEX(&new_desc->lock);
-
- if(mts_list){
- new_desc->host_number = mts_list->host_number+1;
- } else {
- new_desc->host_number = 0;
- }
-
- /* endpoints */
-
- new_desc->ep_out = ep_out;
- new_desc->ep_response = ep_in_set[0];
- new_desc->ep_image = ep_in_set[1];
-
-
- if ( new_desc->ep_out != MTS_EP_OUT )
- MTS_WARNING( "will this work? Command EP is not usually %d\n",
- (int)new_desc->ep_out );
-
- if ( new_desc->ep_response != MTS_EP_RESPONSE )
- MTS_WARNING( "will this work? Response EP is not usually %d\n",
- (int)new_desc->ep_response );
-
- if ( new_desc->ep_image != MTS_EP_IMAGE )
- MTS_WARNING( "will this work? Image data EP is not usually %d\n",
- (int)new_desc->ep_image );
-
-
- /* Initialize the host template based on the default one */
- memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template));
- /* HACK from usb-storage - this is needed for scsi detection */
- (struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */
-
- MTS_DEBUG("registering SCSI module\n");
-
- new_desc->ctempl.module = THIS_MODULE;
- result = scsi_register_host(&new_desc->ctempl);
- /* Will get hit back in microtek_detect by this func */
- if ( result )
- {
- MTS_ERROR( "error %d from scsi_register_host! Help!\n",
- (int)result );
-
- /* FIXME: need more cleanup? */
- kfree( new_desc );
- return NULL;
- }
- MTS_DEBUG_GOT_HERE();
-
- /* FIXME: the bomb is armed, must the host be registered under lock ? */
- /* join the list - lock it */
- down(&mts_list_semaphore);
-
- mts_add_nolock( new_desc );
-
- up(&mts_list_semaphore);
-
-
- MTS_DEBUG("completed probe and exiting happily\n");
-
- return (void *)new_desc;
-}
-
-
-
-/* get us noticed by the rest of the kernel */
-
-int __init microtek_drv_init(void)
-{
- int result;
-
- MTS_DEBUG_GOT_HERE();
- init_MUTEX(&mts_list_semaphore);
-
- if ((result = usb_register(&mts_usb_driver)) < 0) {
- MTS_DEBUG("usb_register returned %d\n", result );
- return -1;
- } else {
- MTS_DEBUG("driver registered.\n");
- }
-
- info(DRIVER_VERSION ":" DRIVER_DESC);
-
- return 0;
-}
-
-void __exit microtek_drv_exit(void)
-{
- struct mts_desc* next;
-
- MTS_DEBUG_GOT_HERE();
-
- usb_deregister(&mts_usb_driver);
-
- down(&mts_list_semaphore);
-
- while (mts_list) {
- /* keep track of where the next one is */
- next = mts_list->next;
-
- mts_remove_nolock( mts_list );
-
- /* advance the list pointer */
- mts_list = next;
- }
-
- up(&mts_list_semaphore);
-}
-
-module_init(microtek_drv_init);
-module_exit(microtek_drv_exit);
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-
-
+++ /dev/null
- /*
- * Driver for Microtek Scanmaker X6 USB scanner and possibly others.
- *
- * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
- * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
- *
- * See microtek.c for history
- *
- */
-
-typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
-typedef void (*mts_usb_urb_callback) (struct urb *);
-
-
-struct mts_transfer_context
-{
- struct mts_desc* instance;
- mts_scsi_cmnd_callback final_callback;
- Scsi_Cmnd *srb;
-
- void* data;
- unsigned data_length;
- int data_pipe;
- int fragment;
-
- u8 status; /* status returned from ep_response after command completion */
-};
-
-
-struct mts_desc {
- struct mts_desc *next;
- struct mts_desc *prev;
-
- struct usb_device *usb_dev;
-
- int interface;
-
- /* Endpoint addresses */
- u8 ep_out;
- u8 ep_response;
- u8 ep_image;
-
- struct Scsi_Host * host;
- Scsi_Host_Template ctempl;
- int host_number;
-
- struct semaphore lock;
-
- struct urb *urb;
- struct mts_transfer_context context;
-};
-
-
-#define MTS_EP_OUT 0x1
-#define MTS_EP_RESPONSE 0x2
-#define MTS_EP_IMAGE 0x3
-#define MTS_EP_TOTAL 0x3
-
-#define MTS_SCSI_ERR_MASK ~0x3fu
-
+++ /dev/null
-/* -*- linux-c -*- */
-
-/*
- * Driver for USB Scanners (linux-2.4.12)
- *
- * Copyright (C) 1999, 2000, 2001 David E. Nelson
- *
- * Portions may be copyright Brad Keryan and Michael Gee.
- *
- * 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.
- *
- * Originally 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.
- *
- *
- * 0.2 10/16/1999
- *
- * - Device can't be opened unless a scanner is plugged into the USB.
- * - Finally settled on a reasonable value for the I/O buffer's.
- * - Cleaned up write_scanner()
- * - Disabled read/write stats
- * - A little more code cleanup
- *
- *
- * 0.3 10/18/1999
- *
- * - Device registration changed to reflect new device
- * allocation/registration for linux-2.3.22+.
- * - Adopted David Brownell's <david-b@pacbell.net> technique for
- * assigning bulk endpoints.
- * - Removed unnessesary #include's
- * - Scanner model now reported via syslog INFO after being detected
- * *and* configured.
- * - Added user specified vendor:product USB ID's which can be passed
- * as module parameters.
- *
- *
- * 0.3.1
- *
- * - Applied patches for linux-2.3.25.
- * - Error number reporting changed to reflect negative return codes.
- *
- *
- * 0.3.2
- *
- * - Applied patches for linux-2.3.26 to scanner_init().
- * - Debug read/write stats now report values as signed decimal.
- *
- *
- * 0.3.3
- *
- * - Updated the bulk_msg() calls to usb usb_bulk_msg().
- * - Added a small delay in the write_scanner() method to aid in
- * avoiding NULL data reads on HP scanners. We'll see how this works.
- * - Return values from usb_bulk_msg() now ignore positive values for
- * use with the ohci driver.
- * - Added conditional debugging instead of commenting/uncommenting
- * all over the place.
- * - kfree()'d the pointer after using usb_string() as documented in
- * linux-usb-api.txt.
- * - Added usb_set_configuration(). It got lost in version 0.3 -- ack!
- * - Added the HP 5200C USB Vendor/Product ID's.
- *
- *
- * 0.3.4 1/23/2000
- *
- * - Added Greg K-H's <greg@kroah.com> patch for better handling of
- * Product/Vendor detection.
- * - The driver now autoconfigures its endpoints including interrupt
- * endpoints if one is detected. The concept was originally based
- * upon David Brownell's method.
- * - Added some Seiko/Epson ID's. Thanks to Karl Heinz
- * Kremer <khk@khk.net>.
- * - Added some preliminary ioctl() calls for the PV8630 which is used
- * by the HP4200. The ioctl()'s still have to be registered. Thanks
- * to Adrian Perez Jorge <adrianpj@easynews.com>.
- * - Moved/migrated stuff to scanner.h
- * - Removed the usb_set_configuration() since this is handled by
- * the usb_new_device() routine in usb.c.
- * - Added the HP 3300C. Thanks to Bruce Tenison.
- * - Changed user specified vendor/product id so that root hub doesn't
- * get falsely attached to. Thanks to Greg K-H.
- * - Added some Mustek ID's. Thanks to Gernot Hoyler
- * <Dr.Hoyler@t-online.de>.
- * - Modified the usb_string() reporting. See kfree() comment above.
- * - Added Umax Astra 2000U. Thanks to Doug Alcorn <doug@lathi.net>.
- * - Updated the printk()'s to use the info/warn/dbg macros.
- * - Updated usb_bulk_msg() argument types to fix gcc warnings.
- *
- *
- * 0.4 2/4/2000
- *
- * - Removed usb_string() from probe_scanner since the core now does a
- * good job of reporting what was connnected.
- * - Finally, simultaneous multiple device attachment!
- * - Fixed some potential memory freeing issues should memory allocation
- * fail in probe_scanner();
- * - Some fixes to disconnect_scanner().
- * - Added interrupt endpoint support.
- * - Added Agfa SnapScan Touch. Thanks to Jan Van den Bergh
- * <jan.vandenbergh@cs.kuleuven.ac.be>.
- * - Added Umax 1220U ID's. Thanks to Maciek Klimkowski
- * <mac@nexus.carleton.ca>.
- * - Fixed bug in write_scanner(). The buffer was not being properly
- * updated for writes larger than OBUF_SIZE. Thanks to Henrik
- * Johansson <henrikjo@post.utfors.se> for identifying it.
- * - Added Microtek X6 ID's. Thanks to Oliver Neukum
- * <Oliver.Neukum@lrz.uni-muenchen.de>.
- *
- *
- * 0.4.1 2/15/2000
- *
- * - Fixed 'count' bug in read_scanner(). Thanks to Henrik
- * Johansson <henrikjo@post.utfors.se> for identifying it. Amazing
- * it has worked this long.
- * - Fixed '>=' bug in both read/write_scanner methods.
- * - Cleaned up both read/write_scanner() methods so that they are
- * a little more readable.
- * - Added a lot of Microtek ID's. Thanks to Adrian Perez Jorge.
- * - Adopted the __initcall().
- * - Added #include <linux/init.h> to scanner.h for __initcall().
- * - Added one liner in irq_scanner() to keep gcc from complaining
- * about an unused variable (data) if debugging was disabled
- * in scanner.c.
- * - Increased the timeout parameter in read_scanner() to 120 Secs.
- *
- *
- * 0.4.2 3/23/2000
- *
- * - Added Umax 1236U ID. Thanks to Philipp Baer <ph_baer@npw.net>.
- * - Added Primax, ReadyScan, Visioneer, Colorado, and Genius ID's.
- * Thanks to Adrian Perez Jorge <adrianpj@easynews.com>.
- * - Fixed error number reported for non-existant devices. Thanks to
- * Spyridon Papadimitriou <Spyridon_Papadimitriou@gs91.sp.cs.cmu.edu>.
- * - Added Acer Prisascan 620U ID's. Thanks to Joao <joey@knoware.nl>.
- * - Replaced __initcall() with module_init()/module_exit(). Updates
- * from patch-2.3.48.
- * - Replaced file_operations structure with new syntax. Updates
- * from patch-2.3.49.
- * - Changed #include "usb.h" to #include <linux/usb.h>
- * - Added #define SCN_IOCTL to exclude development areas
- * since 2.4.x is about to be released. This mainly affects the
- * ioctl() stuff. See scanner.h for more details.
- * - Changed the return value for signal_pending() from -ERESTARTSYS to
- * -EINTR.
- *
- *
- * 0.4.3 4/30/2000
- *
- * - Added Umax Astra 2200 ID. Thanks to Flynn Marquardt
- * <flynn@isr.uni-stuttgart.de>.
- * - Added iVina 1200U ID. Thanks to Dyson Lin <dyson@avision.com.tw>.
- * - Added access time update for the device file courtesy of Paul
- * Mackerras <paulus@samba.org>. This allows a user space daemon
- * to turn the lamp off for a Umax 1220U scanner after a prescribed
- * time.
- * - Fixed HP S20 ID's. Thanks to Ruud Linders <rlinders@xs4all.nl>.
- * - Added Acer ScanPrisa 620U ID. Thanks to Oliver
- * Schwartz <Oliver.Schwartz@gmx.de> via sane-devel mail list.
- * - Fixed bug in read_scanner for copy_to_user() function. The returned
- * value should be 'partial' not 'this_read'.
- * - Fixed bug in read_scanner. 'count' should be decremented
- * by 'this_read' and not by 'partial'. This resulted in twice as many
- * calls to read_scanner() for small amounts of data and possibly
- * unexpected returns of '0'. Thanks to Karl Heinz
- * Kremer <khk@khk.net> and Alain Knaff <Alain.Knaff@ltnb.lu>
- * for discovering this.
- * - Integrated Randy Dunlap's <randy.dunlap@intel.com> patch for a
- * scanner lookup/ident table. Thanks Randy.
- * - Documentation updates.
- * - Added wait queues to read_scanner().
- *
- *
- * 0.4.3.1
- *
- * - Fixed HP S20 ID's...again..sigh. Thanks to Ruud
- * Linders <rlinders@xs4all.nl>.
- *
- * 0.4.4
- * - Added addtional Mustek ID's (BearPaw 1200, 600 CU, 1200 USB,
- * and 1200 UB. Thanks to Henning Meier-Geinitz <henningmg@gmx.de>.
- * - Added the Vuego Scan Brisa 340U ID's. Apparently this scanner is
- * marketed by Acer Peripherals as a cheap 300 dpi model. Thanks to
- * David Gundersen <gundersd@paradise.net.nz>.
- * - Added the Epson Expression1600 ID's. Thanks to Karl Heinz
- * Kremer <khk@khk.net>.
- *
- * 0.4.5 2/28/2001
- * - Added Mustek ID's (BearPaw 2400, 1200 CU Plus, BearPaw 1200F).
- * Thanks to Henning Meier-Geinitz <henningmg@gmx.de>.
- * - Added read_timeout module parameter to override RD_NAK_TIMEOUT
- * when read()'ing from devices.
- * - Stalled pipes are now checked and cleared with
- * usb_clear_halt() for the read_scanner() function. This should
- * address the "funky result: -32" error messages.
- * - Removed Microtek scanner ID's. Microtek scanners are now
- * supported via the drivers/usb/microtek.c driver.
- * - Added scanner specific read timeout's.
- * - Return status errors are NEGATIVE!!! This should address the
- * "funky result: -110" error messages.
- * - Replaced USB_ST_TIMEOUT with ETIMEDOUT.
- * - rd_nak was still defined in MODULE_PARM. It's been updated with
- * read_timeout. Thanks to Mark W. Webb <markwebb@adelphia.net> for
- * reporting this bug.
- * - Added Epson Perfection 1640SU and 1640SU Photo. Thanks to
- * Jean-Luc <f5ibh@db0bm.ampr.org> and Manuel
- * Pelayo <Manuel.Pelayo@sesips.org>. Reported to work fine by Manuel.
- *
- * 0.4.6 9/27/2001
- * - Added IOCTL's to report back scanner USB ID's. Thanks to
- * Karl Heinz <khk@lynx.phpwebhosting.com>
- * - Added Umax Astra 2100U ID's. Thanks to Ron
- * Wellsted <ron@wellsted.org.uk>.
- * and Manuel Pelayo <Manuel.Pelayo@sesips.org>.
- * - Added HP 3400 ID's. Thanks to Harald Hannelius <harald@iki.fi>
- * and Bertrik Sikken <bertrik@zonnet.nl>. Reported to work at
- * htpp://home.zonnet.nl/bertrik/hp3300c/hp3300c.htm.
- * - Added Minolta Dimage Scan Dual II ID's. Thanks to Jose Paulo
- * Moitinho de Almeida <moitinho@civil.ist.utl.pt>
- * - Confirmed addition for SnapScan E20. Thanks to Steffen Hübner
- * <hueb_s@gmx.de>.
- * - Added Lifetec LT9385 ID's. Thanks to Van Bruwaene Kris
- * <krvbr@yahoo.co.uk>
- * - Added Agfa SnapScan e26 ID's. Reported to work with SANE
- * 1.0.5. Thanks to Falk Sauer <falk@mgnkatze.franken.de>.
- * - Added HP 4300 ID's. Thanks to Stefan Schlosser
- * <castla@grmmbl.org>.
- * - Added Relisis Episode ID's. Thanks to Manfred
- * Morgner <odb-devel@gmx.net>.
- * - Added many Acer ID's. Thanks to Oliver
- * Schwartz <Oliver.Schwartz@gmx.de>.
- * - Added Snapscan e40 ID's. Thanks to Oliver
- * Schwartz <Oliver.Schwartz@gmx.de>.
- * - Thanks to Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
- * for helping with races.
- * - Added Epson Perfection 1650 ID's. Thanks to Karl Heinz
- * Kremer <khk@khk.net>.
- * - Added Epson Perfection 2450 ID's (aka GT-9700 for the Japanese
- * market). Thanks to Karl Heinz Kremer <khk@khk.net>.
- * - Added Mustek 600 USB ID's. Thanks to Marcus
- * Alanen <maalanen@ra.abo.fi>.
- * - Added Acer ScanPrisa 1240UT ID's. Thanks to Morgan
- * Collins <sirmorcant@morcant.org>.
- * - Incorporated devfs patches!! Thanks to Tom Rini
- * <trini@kernel.crashing.org>, Pavel Roskin <proski@gnu.org>,
- * Greg KH <greg@kroah.com>, Yves Duret <yduret@mandrakesoft.com>,
- * Flavio Stanchina <flavio.stanchina@tin.it>.
- * - Removed Minolta ScanImage II. This scanner uses USB SCSI. Thanks
- * to Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de> for pointing
- * this out.
- * - Added additional SMP locking. Thanks to David Brownell and
- * Oliver Neukum for their help.
- * - Added version reporting - reports for both module load and modinfo
- * - Started path to hopefully straighten/clean out ioctl()'s.
- * - Users are now notified to consult the Documentation/usb/scanner.txt
- * for common error messages rather than the maintainer.
- *
- * 0.4.7 11/28/2001
- * - Fixed typo in Documentation/scanner.txt. Thanks to
- * Karel <karel.vervaeke@pandora.be> for pointing it out.
- * - Added ID's for a Memorex 6136u. Thanks to Álvaro Gaspar de
- * Valenzuela" <agaspard@utsi.edu>.
- * - Added ID's for Agfa e25. Thanks to Heinrich
- * Rust <Heinrich.Rust@gmx.de>. Also reported to work with
- * Linux and SANE (?).
- * - Added Canon FB620U, D646U, and 1220U ID's. Thanks to Paul
- * Rensing <Paul_Rensing@StanfordAlumni.org>. For more info
- * on Linux support for these models, contact
- * salvestrini@users.sourceforge.net.
- * - Added Plustek OpticPro UT12, OpticPro U24, KYE/Genius
- * ColorPage-HR6 V2 ID's in addition to many "Unknown" models
- * under those vendors. Thanks to
- * Jaeger, Gerhard" <g.jaeger@earthling.net>. These scanner are
- * apparently based upon the LM983x IC's.
- * - Applied Frank's patch that addressed some locking and module
- * referencing counts. Thanks to both
- * Frank Zago <fzago@greshamstorage.com> and
- * Oliver Neukum <520047054719-0001@t-online.de> for reviewing/testing.
- *
- * TODO
- * - Performance
- * - Select/poll methods
- * - More testing
- * - Proper registry/assignment for LM9830 ioctl's
- *
- *
- * Thanks to:
- *
- * - All the folks on the linux-usb list who put up with me. :) This
- * has been a great learning experience for me.
- * - To Linus Torvalds for this great OS.
- * - The GNU folks.
- * - The folks that forwarded Vendor:Product ID's to me.
- * - Johannes Erdfelt for the loaning of a USB analyzer for tracking an
- * issue with HP-4100 and uhci.
- * - Adolfo Montero for his assistance.
- * - All the folks who chimed in with reports and suggestions.
- * - All the developers that are working on USB SANE backends or other
- * applications to use USB scanners.
- *
- * Performance:
- *
- * System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner
- * 300 dpi scan of the entire bed
- * 24 Bit Color ~ 70 secs - 3.6 Mbit/sec
- * 8 Bit Gray ~ 17 secs - 4.2 Mbit/sec */
-
-#include <asm/byteorder.h>
-
-/*
- * Scanner definitions, macros, module info,
- * debug/ioctl/data_dump enable, and other constants.
- */
-#include "scanner.h"
-
-static void
-irq_scanner(struct urb *urb)
-{
-
-/*
- * For the meantime, this is just a placeholder until I figure out what
- * all I want to do with it -- or somebody else for that matter.
- */
-
- struct scn_usb_data *scn;
- unsigned char *data;
- scn = urb->context;
-
- data = &scn->button;
- data += 0; /* Keep gcc from complaining about unused var */
-
- if (urb->status) {
- return;
- }
-
- dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data);
-
- return;
-}
-
-static int
-open_scanner(struct inode * inode, struct file * file)
-{
- struct scn_usb_data *scn;
- struct usb_device *dev;
-
- int scn_minor;
-
- int err=0;
-
- MOD_INC_USE_COUNT;
-
- down(&scn_mutex);
-
- scn_minor = USB_SCN_MINOR(inode);
-
- dbg("open_scanner: scn_minor:%d", scn_minor);
-
- if (!p_scn_table[scn_minor]) {
- up(&scn_mutex);
- MOD_DEC_USE_COUNT;
- err("open_scanner(%d): Unable to access minor data", scn_minor);
- return -ENODEV;
- }
-
- scn = p_scn_table[scn_minor];
-
- dev = scn->scn_dev;
-
- down(&(scn->sem)); /* Now protect the scn_usb_data structure */
-
- up(&scn_mutex); /* Now handled by the above */
-
- if (!dev) {
- err("open_scanner(%d): Scanner device not present", scn_minor);
- err = -ENODEV;
- goto out_error;
- }
-
- if (!scn->present) {
- err("open_scanner(%d): Scanner is not present", scn_minor);
- err = -ENODEV;
- goto out_error;
- }
-
- if (scn->isopen) {
- err("open_scanner(%d): Scanner device is already open", scn_minor);
- err = -EBUSY;
- goto out_error;
- }
-
- init_waitqueue_head(&scn->rd_wait_q);
-
- scn->isopen = 1;
-
- file->private_data = scn; /* Used by the read and write methods */
-
-
-out_error:
-
- up(&(scn->sem)); /* Wake up any possible contending processes */
-
- if (err)
- MOD_DEC_USE_COUNT;
-
- return err;
-}
-
-static int
-close_scanner(struct inode * inode, struct file * file)
-{
- struct scn_usb_data *scn;
-
- int scn_minor;
-
- scn_minor = USB_SCN_MINOR (inode);
-
- dbg("close_scanner: scn_minor:%d", scn_minor);
-
- if (!p_scn_table[scn_minor]) {
- err("close_scanner(%d): invalid scn_minor", scn_minor);
- return -ENODEV;
- }
-
- down(&scn_mutex);
-
- scn = p_scn_table[scn_minor];
- down(&(scn->sem));
- scn->isopen = 0;
-
- file->private_data = NULL;
-
- up(&scn_mutex);
- up(&(scn->sem));
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-}
-
-static ssize_t
-write_scanner(struct file * file, const char * buffer,
- size_t count, loff_t *ppos)
-{
- struct scn_usb_data *scn;
- struct usb_device *dev;
-
- ssize_t bytes_written = 0; /* Overall count of bytes written */
- ssize_t ret = 0;
-
- int scn_minor;
-
- int this_write; /* Number of bytes to write */
- int partial; /* Number of bytes successfully written */
- int result = 0;
-
- char *obuf;
-
- scn = file->private_data;
-
- down(&(scn->sem));
-
- scn_minor = scn->scn_minor;
-
- obuf = scn->obuf;
-
- dev = scn->scn_dev;
-
- file->f_dentry->d_inode->i_atime = CURRENT_TIME;
-
- while (count > 0) {
-
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
-
- this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count;
-
- if (copy_from_user(scn->obuf, buffer, this_write)) {
- ret = -EFAULT;
- break;
- }
-
- result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ);
- dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor, result, this_write, partial);
-
- if (result == -ETIMEDOUT) { /* NAK -- shouldn't happen */
- warn("write_scanner: NAK received.");
- ret = result;
- break;
- } else if (result < 0) { /* We should not get any I/O errors */
- warn("write_scanner(%d): funky result: %d. Consult Documentataion/usb/scanner.txt.", scn_minor, result);
- ret = -EIO;
- break;
- }
-
-#ifdef WR_DATA_DUMP
- if (partial) {
- unsigned char cnt, cnt_max;
- cnt_max = (partial > 24) ? 24 : partial;
- printk(KERN_DEBUG "dump(%d): ", scn_minor);
- for (cnt=0; cnt < cnt_max; cnt++) {
- printk("%X ", obuf[cnt]);
- }
- printk("\n");
- }
-#endif
- if (partial != this_write) { /* Unable to write all contents of obuf */
- ret = -EIO;
- break;
- }
-
- if (partial) { /* Data written */
- buffer += partial;
- count -= partial;
- bytes_written += partial;
- } else { /* No data written */
- ret = 0;
- break;
- }
- }
- up(&(scn->sem));
- mdelay(5); /* This seems to help with SANE queries */
- return ret ? ret : bytes_written;
-}
-
-static ssize_t
-read_scanner(struct file * file, char * buffer,
- size_t count, loff_t *ppos)
-{
- struct scn_usb_data *scn;
- struct usb_device *dev;
-
- ssize_t bytes_read; /* Overall count of bytes_read */
- ssize_t ret;
-
- int scn_minor;
- int partial; /* Number of bytes successfully read */
- int this_read; /* Max number of bytes to read */
- int result;
- int rd_expire = RD_EXPIRE;
-
- char *ibuf;
-
- scn = file->private_data;
-
- down(&(scn->sem));
-
- scn_minor = scn->scn_minor;
-
- ibuf = scn->ibuf;
-
- dev = scn->scn_dev;
-
- bytes_read = 0;
- ret = 0;
-
- file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* Update the
- atime of
- the device
- node */
- while (count > 0) {
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
-
- this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
-
- result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, scn->rd_nak_timeout);
- dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d", scn_minor, result, this_read, partial, count);
-
-/*
- * Scanners are sometimes inheriently slow since they are mechanical
- * in nature. USB bulk reads tend to timeout while the scanner is
- * positioning, resetting, warming up the lamp, etc if the timeout is
- * set too low. A very long timeout parameter for bulk reads was used
- * to overcome this limitation, but this sometimes resulted in folks
- * having to wait for the timeout to expire after pressing Ctrl-C from
- * an application. The user was sometimes left with the impression
- * that something had hung or crashed when in fact the USB read was
- * just waiting on data. So, the below code retains the same long
- * timeout period, but splits it up into smaller parts so that
- * Ctrl-C's are acted upon in a reasonable amount of time.
- */
-
- if (result == -ETIMEDOUT) { /* NAK */
- if (!partial) { /* No data */
- if (--rd_expire <= 0) { /* Give it up */
- warn("read_scanner(%d): excessive NAK's received", scn_minor);
- ret = result;
- break;
- } else { /* Keep trying to read data */
- interruptible_sleep_on_timeout(&scn->rd_wait_q, scn->rd_nak_timeout);
- continue;
- }
- } else { /* Timeout w/ some data */
- goto data_recvd;
- }
- }
-
- if (result == -EPIPE) { /* No hope */
- if(usb_clear_halt(dev, scn->bulk_in_ep)) {
- err("read_scanner(%d): Failure to clear endpoint halt condition (%Zd).", scn_minor, ret);
- }
- ret = result;
- break;
- } else if ((result < 0) && (result != -EREMOTEIO)) {
- warn("read_scanner(%d): funky result:%d. Consult Documentation/usb/scanner.txt.", scn_minor, (int)result);
- ret = -EIO;
- break;
- }
-
- data_recvd:
-
-#ifdef RD_DATA_DUMP
- if (partial) {
- unsigned char cnt, cnt_max;
- cnt_max = (partial > 24) ? 24 : partial;
- printk(KERN_DEBUG "dump(%d): ", scn_minor);
- for (cnt=0; cnt < cnt_max; cnt++) {
- printk("%X ", ibuf[cnt]);
- }
- printk("\n");
- }
-#endif
-
- if (partial) { /* Data returned */
- if (copy_to_user(buffer, ibuf, partial)) {
- ret = -EFAULT;
- break;
- }
- count -= this_read; /* Compensate for short reads */
- bytes_read += partial; /* Keep tally of what actually was read */
- buffer += partial;
- } else {
- ret = 0;
- break;
- }
- }
- up(&(scn->sem));
- return ret ? ret : bytes_read;
-}
-
-static int
-ioctl_scanner(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct usb_device *dev;
-
- int scn_minor;
-
- scn_minor = USB_SCN_MINOR(inode);
-
- if (!p_scn_table[scn_minor]) {
- err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
- return -ENODEV;
- }
-
- dev = p_scn_table[scn_minor]->scn_dev;
-
- switch (cmd)
- {
- case SCANNER_IOCTL_VENDOR :
- return (put_user(dev->descriptor.idVendor, (unsigned int *) arg));
- case SCANNER_IOCTL_PRODUCT :
- return (put_user(dev->descriptor.idProduct, (unsigned int *) arg));
-#ifdef PV8630
- case PV8630_IOCTL_INREQUEST :
- {
- int result;
-
- struct {
- __u8 data;
- __u8 request;
- __u16 value;
- __u16 index;
- } args;
-
- if (copy_from_user(&args, (void *)arg, sizeof(args)))
- return -EFAULT;
-
- result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- args.request, USB_TYPE_VENDOR|
- USB_RECIP_DEVICE|USB_DIR_IN,
- args.value, args.index, &args.data,
- 1, HZ*5);
-
- dbg("ioctl_scanner(%d): inreq: args.data:%x args.value:%x args.index:%x args.request:%x\n", scn_minor, args.data, args.value, args.index, args.request);
-
- if (copy_to_user((void *)arg, &args, sizeof(args)))
- return -EFAULT;
-
- dbg("ioctl_scanner(%d): inreq: result:%d\n", scn_minor, result);
-
- return result;
- }
- case PV8630_IOCTL_OUTREQUEST :
- {
- int result;
-
- struct {
- __u8 request;
- __u16 value;
- __u16 index;
- } args;
-
- if (copy_from_user(&args, (void *)arg, sizeof(args)))
- return -EFAULT;
-
- dbg("ioctl_scanner(%d): outreq: args.value:%x args.index:%x args.request:%x\n", scn_minor, args.value, args.index, args.request);
-
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- args.request, USB_TYPE_VENDOR|
- USB_RECIP_DEVICE|USB_DIR_OUT,
- args.value, args.index, NULL,
- 0, HZ*5);
-
- dbg("ioctl_scanner(%d): outreq: result:%d\n", scn_minor, result);
-
- return result;
- }
-#endif /* PV8630 */
- case SCANNER_IOCTL_CTRLMSG:
- {
- struct ctrlmsg_ioctl {
- struct usb_ctrlrequest req;
- void *data;
- } cmsg;
- int pipe, nb, ret;
- unsigned char buf[64];
-
- if (copy_from_user(&cmsg, (void *)arg, sizeof(cmsg)))
- return -EFAULT;
-
- nb = le16_to_cpup(&cmsg.req.wLength);
-
- if (nb > sizeof(buf))
- return -EINVAL;
-
- if ((cmsg.req.bRequestType & 0x80) == 0) {
- pipe = usb_sndctrlpipe(dev, 0);
- if (nb > 0 && copy_from_user(buf, cmsg.data, nb))
- return -EFAULT;
- } else {
- pipe = usb_rcvctrlpipe(dev, 0);
- }
-
- ret = usb_control_msg(dev, pipe, cmsg.req.bRequest,
- cmsg.req.bRequestType,
- le16_to_cpup(&cmsg.req.wValue),
- le16_to_cpup(&cmsg.req.wIndex),
- buf, nb, HZ);
-
- if (ret < 0) {
- err("ioctl_scanner(%d): control_msg returned %d\n", scn_minor, ret);
- return -EIO;
- }
-
- if (nb > 0 && (cmsg.req.bRequestType & 0x80) && copy_to_user(cmsg.data, buf, nb))
- return -EFAULT;
-
- return 0;
- }
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static struct
-file_operations usb_scanner_fops = {
- read: read_scanner,
- write: write_scanner,
- ioctl: ioctl_scanner,
- open: open_scanner,
- release: close_scanner,
-};
-
-static void *
-probe_scanner(struct usb_device *dev, unsigned int ifnum,
- const struct usb_device_id *id)
-{
- struct scn_usb_data *scn;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *endpoint;
-
- int ep_cnt;
- int ix;
- int scn_minor;
-
- char valid_device = 0;
- char have_bulk_in, have_bulk_out, have_intr;
- char name[10];
-
- if (vendor != -1 && product != -1) {
- info("probe_scanner: User specified USB scanner -- Vendor:Product - %x:%x", vendor, product);
- }
-
- dbg("probe_scanner: USB dev address:%p", dev);
- dbg("probe_scanner: ifnum:%u", ifnum);
-
-/*
- * 1. Check Vendor/Product
- * 2. Determine/Assign Bulk Endpoints
- * 3. Determine/Assign Intr Endpoint
- */
-
-/*
- * There doesn't seem to be an imaging class defined in the USB
- * Spec. (yet). If there is, HP isn't following it and it doesn't
- * look like anybody else is either. Therefore, we have to test the
- * Vendor and Product ID's to see what we have. Also, other scanners
- * may be able to use this driver by specifying both vendor and
- * product ID's as options to the scanner module in conf.modules.
- *
- * NOTE: Just because a product is supported here does not mean that
- * applications exist that support the product. It's in the hopes
- * that this will allow developers a means to produce applications
- * that will support USB products.
- *
- * Until we detect a device which is pleasing, we silently punt.
- */
-
- for (ix = 0; ix < sizeof (scanner_device_ids) / sizeof (struct usb_device_id); ix++) {
- if ((dev->descriptor.idVendor == scanner_device_ids [ix].idVendor) &&
- (dev->descriptor.idProduct == scanner_device_ids [ix].idProduct)) {
- valid_device = 1;
- break;
- }
- }
- if (dev->descriptor.idVendor == vendor && /* User specified */
- dev->descriptor.idProduct == product) { /* User specified */
- valid_device = 1;
- }
-
- if (!valid_device)
- return NULL; /* We didn't find anything pleasing */
-
-/*
- * After this point we can be a little noisy about what we are trying to
- * configure.
- */
-
- if (dev->descriptor.bNumConfigurations != 1) {
- info("probe_scanner: Only one device configuration is supported.");
- return NULL;
- }
-
- if (dev->config[0].bNumInterfaces != 1) {
- info("probe_scanner: Only one device interface is supported.");
- return NULL;
- }
-
- interface = dev->config[0].interface[ifnum].altsetting;
- endpoint = interface[ifnum].endpoint;
-
-/*
- * Start checking for two bulk endpoints OR two bulk endpoints *and* one
- * interrupt endpoint. If we have an interrupt endpoint go ahead and
- * setup the handler. FIXME: This is a future enhancement...
- */
-
- dbg("probe_scanner: Number of Endpoints:%d", (int) interface->bNumEndpoints);
-
- if ((interface->bNumEndpoints != 2) && (interface->bNumEndpoints != 3)) {
- info("probe_scanner: Only two or three endpoints supported.");
- return NULL;
- }
-
- ep_cnt = have_bulk_in = have_bulk_out = have_intr = 0;
-
- while (ep_cnt < interface->bNumEndpoints) {
-
- if (!have_bulk_in && IS_EP_BULK_IN(endpoint[ep_cnt])) {
- ep_cnt++;
- have_bulk_in = ep_cnt;
- dbg("probe_scanner: bulk_in_ep:%d", have_bulk_in);
- continue;
- }
-
- if (!have_bulk_out && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
- ep_cnt++;
- have_bulk_out = ep_cnt;
- dbg("probe_scanner: bulk_out_ep:%d", have_bulk_out);
- continue;
- }
-
- if (!have_intr && IS_EP_INTR(endpoint[ep_cnt])) {
- ep_cnt++;
- have_intr = ep_cnt;
- dbg("probe_scanner: intr_ep:%d", have_intr);
- continue;
- }
- info("probe_scanner: Undetected endpoint -- consult Documentation/usb/scanner.txt.");
- return NULL; /* Shouldn't ever get here unless we have something weird */
- }
-
-
-/*
- * Perform a quick check to make sure that everything worked as it
- * should have.
- */
-
- switch(interface->bNumEndpoints) {
- case 2:
- if (!have_bulk_in || !have_bulk_out) {
- info("probe_scanner: Two bulk endpoints required.");
- return NULL;
- }
- break;
- case 3:
- if (!have_bulk_in || !have_bulk_out || !have_intr) {
- info("probe_scanner: Two bulk endpoints and one interrupt endpoint required.");
- return NULL;
- }
- break;
- default:
- info("probe_scanner: Endpoint determination failed -- consult Documentation/usb/scanner.txt");
- return NULL;
- }
-
-
-/*
- * Determine a minor number and initialize the structure associated
- * with it. The problem with this is that we are counting on the fact
- * that the user will sequentially add device nodes for the scanner
- * devices. */
-
- down(&scn_mutex);
-
- for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
- if (!p_scn_table[scn_minor])
- break;
- }
-
-/* Check to make sure that the last slot isn't already taken */
- if (p_scn_table[scn_minor]) {
- err("probe_scanner: No more minor devices remaining.");
- up(&scn_mutex);
- return NULL;
- }
-
- dbg("probe_scanner: Allocated minor:%d", scn_minor);
-
- if (!(scn = kmalloc (sizeof (struct scn_usb_data), GFP_KERNEL))) {
- err("probe_scanner: Out of memory.");
- up(&scn_mutex);
- return NULL;
- }
- memset (scn, 0, sizeof(struct scn_usb_data));
-
- scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!scn->scn_irq) {
- kfree(scn);
- up(&scn_mutex);
- return NULL;
- }
-
- init_MUTEX(&(scn->sem)); /* Initializes to unlocked */
-
- dbg ("probe_scanner(%d): Address of scn:%p", scn_minor, scn);
-
-/* Ok, if we detected an interrupt EP, setup a handler for it */
- if (have_intr) {
- dbg("probe_scanner(%d): Configuring IRQ handler for intr EP:%d", scn_minor, have_intr);
- FILL_INT_URB(scn->scn_irq, dev,
- usb_rcvintpipe(dev, have_intr),
- &scn->button, 1, irq_scanner, scn,
- // endpoint[(int)have_intr].bInterval);
- 250);
-
- if (usb_submit_urb(scn->scn_irq, GFP_KERNEL)) {
- err("probe_scanner(%d): Unable to allocate INT URB.", scn_minor);
- kfree(scn);
- up(&scn_mutex);
- return NULL;
- }
- }
-
-
-/* Ok, now initialize all the relevant values */
- if (!(scn->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
- err("probe_scanner(%d): Not enough memory for the output buffer.", scn_minor);
- kfree(scn);
- up(&scn_mutex);
- return NULL;
- }
- dbg("probe_scanner(%d): obuf address:%p", scn_minor, scn->obuf);
-
- if (!(scn->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
- err("probe_scanner(%d): Not enough memory for the input buffer.", scn_minor);
- kfree(scn->obuf);
- kfree(scn);
- up(&scn_mutex);
- return NULL;
- }
- dbg("probe_scanner(%d): ibuf address:%p", scn_minor, scn->ibuf);
-
-
- switch (dev->descriptor.idVendor) { /* Scanner specific read timeout parameters */
- case 0x04b8: /* Seiko/Epson */
- scn->rd_nak_timeout = HZ * 40;
- break;
- case 0x055f: /* Mustek */
- case 0x0400: /* Another Mustek */
- case 0x0ff5: /* And yet another Mustek */
- scn->rd_nak_timeout = HZ * 1;
- default:
- scn->rd_nak_timeout = RD_NAK_TIMEOUT;
- }
-
-
- if (read_timeout > 0) { /* User specified read timeout overrides everything */
- info("probe_scanner: User specified USB read timeout - %d", read_timeout);
- scn->rd_nak_timeout = read_timeout;
- }
-
-
- scn->bulk_in_ep = have_bulk_in;
- scn->bulk_out_ep = have_bulk_out;
- scn->intr_ep = have_intr;
- scn->present = 1;
- scn->scn_dev = dev;
- scn->scn_minor = scn_minor;
- scn->isopen = 0;
-
- sprintf(name, "scanner%d", scn->scn_minor);
-
- scn->devfs = devfs_register(usb_devfs_handle, name,
- DEVFS_FL_DEFAULT, USB_MAJOR,
- SCN_BASE_MNR + scn->scn_minor,
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
- S_IWGRP | S_IROTH | S_IWOTH, &usb_scanner_fops, NULL);
- if (scn->devfs == NULL)
- dbg("scanner%d: device node registration failed", scn_minor);
-
- p_scn_table[scn_minor] = scn;
-
- up(&scn_mutex);
-
- return scn;
-}
-
-static void
-disconnect_scanner(struct usb_device *dev, void *ptr)
-{
- struct scn_usb_data *scn = (struct scn_usb_data *) ptr;
-
- down (&scn_mutex);
- down (&(scn->sem));
-
- if(scn->intr_ep) {
- dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor);
- usb_unlink_urb(scn->scn_irq);
- }
- usb_driver_release_interface(&scanner_driver,
- &scn->scn_dev->actconfig->interface[scn->ifnum]);
-
- kfree(scn->ibuf);
- kfree(scn->obuf);
-
- dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
- devfs_unregister(scn->devfs);
- p_scn_table[scn->scn_minor] = NULL;
- usb_free_urb(scn->scn_irq);
- up (&(scn->sem));
- kfree (scn);
- up (&scn_mutex);
-}
-
-static struct
-usb_driver scanner_driver = {
- name: "usbscanner",
- probe: probe_scanner,
- disconnect: disconnect_scanner,
- fops: &usb_scanner_fops,
- minor: SCN_BASE_MNR,
- id_table: NULL, /* This would be scanner_device_ids, but we
- need to check every USB device, in case
- we match a user defined vendor/product ID. */
-};
-
-void __exit
-usb_scanner_exit(void)
-{
- usb_deregister(&scanner_driver);
-}
-
-int __init
-usb_scanner_init (void)
-{
- if (usb_register(&scanner_driver) < 0)
- return -1;
-
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return 0;
-}
-
-module_init(usb_scanner_init);
-module_exit(usb_scanner_exit);
+++ /dev/null
-/*
- * Driver for USB Scanners (linux-2.4.12)
- *
- * Copyright (C) 1999, 2000, 2001 David E. Nelson
- *
- * 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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <asm/uaccess.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/ioctl.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
-#include <linux/devfs_fs_kernel.h>
-
-// #define DEBUG
-
-/* Enable this to support the older ioctl interfaces scanners that
- * a PV8630 Scanner-On-Chip. The prefered method is the
- * SCANNER_IOCTL_CTRLMSG ioctl.
- */
-// #define PV8630
-
-#define DRIVER_VERSION "0.4.6"
-#define DRIVER_DESC "USB Scanner Driver"
-
-#include <linux/usb.h>
-
-static __s32 vendor=-1, product=-1, read_timeout=0;
-
-MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson");
-MODULE_DESCRIPTION(DRIVER_DESC" "DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-MODULE_PARM(vendor, "i");
-MODULE_PARM_DESC(vendor, "User specified USB idVendor");
-
-MODULE_PARM(product, "i");
-MODULE_PARM_DESC(product, "User specified USB idProduct");
-
-MODULE_PARM(read_timeout, "i");
-MODULE_PARM_DESC(read_timeout, "User specified read timeout in seconds");
-
-
-/* WARNING: These DATA_DUMP's can produce a lot of data. Caveat Emptor. */
-// #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
-// #define WR_DATA_DUMP /* DEBUG does not have to be defined. */
-
-static struct usb_device_id scanner_device_ids [] = {
- /* Acer */
- { USB_DEVICE(0x04a5, 0x2060) }, /* Prisa Acerscan 620U & 640U (!)*/
- { USB_DEVICE(0x04a5, 0x2040) }, /* Prisa AcerScan 620U (!) */
- { USB_DEVICE(0x04a5, 0x20c0) }, /* Prisa AcerScan 1240UT */
- { USB_DEVICE(0x04a5, 0x2022) }, /* Vuego Scan Brisa 340U */
- { USB_DEVICE(0x04a5, 0x1a20) }, /* Unknown - Oliver Schwartz */
- { USB_DEVICE(0x04a5, 0x1a2a) }, /* Unknown - Oliver Schwartz */
- { USB_DEVICE(0x04a5, 0x207e) }, /* Prisa 640BU */
- { USB_DEVICE(0x04a5, 0x20be) }, /* Unknown - Oliver Schwartz */
- { USB_DEVICE(0x04a5, 0x20c0) }, /* Unknown - Oliver Schwartz */
- { USB_DEVICE(0x04a5, 0x20de) }, /* S2W 3300U */
- { USB_DEVICE(0x04a5, 0x20b0) }, /* Unknown - Oliver Schwartz */
- { USB_DEVICE(0x04a5, 0x20fe) }, /* Unknown - Oliver Schwartz */
- /* Agfa */
- { USB_DEVICE(0x06bd, 0x0001) }, /* SnapScan 1212U */
- { USB_DEVICE(0x06bd, 0x0002) }, /* SnapScan 1236U */
- { USB_DEVICE(0x06bd, 0x2061) }, /* Another SnapScan 1212U (?)*/
- { USB_DEVICE(0x06bd, 0x0100) }, /* SnapScan Touch */
- { USB_DEVICE(0x06bd, 0x2091) }, /* SnapScan e20 */
- { USB_DEVICE(0x06bd, 0x2095) }, /* SnapScan e25 */
- { USB_DEVICE(0x06bd, 0x2097) }, /* SnapScan e26 */
- { USB_DEVICE(0x06bd, 0x208d) }, /* Snapscan e40 */
- /* Canon */
- { USB_DEVICE(0x04a9, 0x2202) }, /* FB620U */
- { USB_DEVICE(0x04a9, 0x220b) }, /* D646U */
- { USB_DEVICE(0x04a9, 0x2207) }, /* 1220U */
- /* Colorado -- See Primax/Colorado below */
- /* Epson -- See Seiko/Epson below */
- /* Genius */
- { USB_DEVICE(0x0458, 0x2001) }, /* ColorPage-Vivid Pro */
- { USB_DEVICE(0x0458, 0x2007) }, /* ColorPage HR6 V2 */
- { USB_DEVICE(0x0458, 0x2008) }, /* Unknown */
- { USB_DEVICE(0x0458, 0x2009) }, /* Unknown */
- { USB_DEVICE(0x0458, 0x2013) }, /* Unknown */
- { USB_DEVICE(0x0458, 0x2015) }, /* Unknown */
- { USB_DEVICE(0x0458, 0x2016) }, /* Unknown */
- /* Hewlett Packard */
- { USB_DEVICE(0x03f0, 0x0205) }, /* 3300C */
- { USB_DEVICE(0x03f0, 0x0405) }, /* 3400C */
- { USB_DEVICE(0x03f0, 0x0101) }, /* 4100C */
- { USB_DEVICE(0x03f0, 0x0105) }, /* 4200C */
- { USB_DEVICE(0x03f0, 0x0305) }, /* 4300C */
- { USB_DEVICE(0x03f0, 0x0102) }, /* PhotoSmart S20 */
- { USB_DEVICE(0x03f0, 0x0401) }, /* 5200C */
- // { USB_DEVICE(0x03f0, 0x0701) }, /* 5300C - NOT SUPPORTED - see http://www.neatech.nl/oss/HP5300C/ */
- { USB_DEVICE(0x03f0, 0x0201) }, /* 6200C */
- { USB_DEVICE(0x03f0, 0x0601) }, /* 6300C */
- { USB_DEVICE(0x03f0, 0x605) }, /* 2200C */
- /* iVina */
- { USB_DEVICE(0x0638, 0x0268) }, /* 1200U */
- /* Lifetec */
- { USB_DEVICE(0x05d8, 0x4002) }, /* Lifetec LT9385 */
- /* Memorex */
- { USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */
- /* Microtek -- No longer supported - Enable SCSI and USB Microtek in kernel config */
- // { USB_DEVICE(0x05da, 0x0099) }, /* ScanMaker X6 - X6U */
- // { USB_DEVICE(0x05da, 0x0094) }, /* Phantom 336CX - C3 */
- // { USB_DEVICE(0x05da, 0x00a0) }, /* Phantom 336CX - C3 #2 */
- // { USB_DEVICE(0x05da, 0x009a) }, /* Phantom C6 */
- // { USB_DEVICE(0x05da, 0x00a3) }, /* ScanMaker V6USL */
- // { USB_DEVICE(0x05da, 0x80a3) }, /* ScanMaker V6USL #2 */
- // { USB_DEVICE(0x05da, 0x80ac) }, /* ScanMaker V6UL - SpicyU */
- /* Minolta */
- // { USB_DEVICE(0x0638,0x026a) }, /* Minolta Dimage Scan Dual II */
- /* Mustek */
- { USB_DEVICE(0x055f, 0x0001) }, /* 1200 CU */
- { USB_DEVICE(0x0400, 0x1000) }, /* BearPaw 1200 */
- { USB_DEVICE(0x055f, 0x0002) }, /* 600 CU */
- { USB_DEVICE(0x055f, 0x0873) }, /* 600 USB */
- { USB_DEVICE(0x055f, 0x0003) }, /* 1200 USB */
- { USB_DEVICE(0x055f, 0x0006) }, /* 1200 UB */
- { USB_DEVICE(0x0400, 0x1001) }, /* BearPaw 2400 */
- { USB_DEVICE(0x055f, 0x0008) }, /* 1200 CU Plus */
- { USB_DEVICE(0x0ff5, 0x0010) }, /* BearPaw 1200F */
- /* Plustek */
- { USB_DEVICE(0x07b3, 0x0017) }, /* OpticPro UT12 */
- { USB_DEVICE(0x07b3, 0x0011) }, /* OpticPro UT24 */
- { USB_DEVICE(0x07b3, 0x0005) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0007) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x000F) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0010) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0013) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0014) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0015) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0016) }, /* Unknown */
- { USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
- /* Primax/Colorado */
- { USB_DEVICE(0x0461, 0x0300) }, /* G2-300 #1 */
- { USB_DEVICE(0x0461, 0x0380) }, /* G2-600 #1 */
- { USB_DEVICE(0x0461, 0x0301) }, /* G2E-300 #1 */
- { USB_DEVICE(0x0461, 0x0381) }, /* ReadyScan 636i */
- { USB_DEVICE(0x0461, 0x0302) }, /* G2-300 #2 */
- { USB_DEVICE(0x0461, 0x0382) }, /* G2-600 #2 */
- { USB_DEVICE(0x0461, 0x0303) }, /* G2E-300 #2 */
- { USB_DEVICE(0x0461, 0x0383) }, /* G2E-600 */
- { USB_DEVICE(0x0461, 0x0340) }, /* Colorado USB 9600 */
- // { USB_DEVICE(0x0461, 0x0360) }, /* Colorado USB 19200 - undetected endpoint */
- { USB_DEVICE(0x0461, 0x0341) }, /* Colorado 600u */
- { USB_DEVICE(0x0461, 0x0361) }, /* Colorado 1200u */
- /* Relisis */
- // { USB_DEVICE(0x0475, 0x0103) }, /* Episode - undetected endpoint */
- /* Seiko/Epson Corp. */
- { USB_DEVICE(0x04b8, 0x0101) }, /* Perfection 636U and 636Photo */
- { USB_DEVICE(0x04b8, 0x0103) }, /* Perfection 610 */
- { USB_DEVICE(0x04b8, 0x0104) }, /* Perfection 1200U and 1200Photo*/
- { USB_DEVICE(0x04b8, 0x0106) }, /* Stylus Scan 2500 */
- { USB_DEVICE(0x04b8, 0x0107) }, /* Expression 1600 */
- { USB_DEVICE(0x04b8, 0x010a) }, /* Perfection 1640SU and 1640SU Photo */
- { USB_DEVICE(0x04b8, 0x010b) }, /* Perfection 1240U */
- { USB_DEVICE(0x04b8, 0x010c) }, /* Perfection 640U */
- { USB_DEVICE(0x04b8, 0x010e) }, /* Expression 1680 */
- { USB_DEVICE(0x04b8, 0x0110) }, /* Perfection 1650 */
- { USB_DEVICE(0x04b8, 0x0112) }, /* Perfection 2450 - GT-9700 for the Japanese mkt */
- /* Umax */
- { USB_DEVICE(0x1606, 0x0010) }, /* Astra 1220U */
- { USB_DEVICE(0x1606, 0x0030) }, /* Astra 2000U */
- { USB_DEVICE(0x1606, 0x0130) }, /* Astra 2100U */
- { USB_DEVICE(0x1606, 0x0230) }, /* Astra 2200U */
- /* Visioneer */
- { USB_DEVICE(0x04a7, 0x0221) }, /* OneTouch 5300 USB */
- { USB_DEVICE(0x04a7, 0x0211) }, /* OneTouch 7600 USB */
- { USB_DEVICE(0x04a7, 0x0231) }, /* 6100 USB */
- { USB_DEVICE(0x04a7, 0x0311) }, /* 6200 EPP/USB */
- { USB_DEVICE(0x04a7, 0x0321) }, /* OneTouch 8100 EPP/USB */
- { USB_DEVICE(0x04a7, 0x0331) }, /* OneTouch 8600 EPP/USB */
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, scanner_device_ids);
-
-#define IS_EP_BULK(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
-#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
-#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
-#define IS_EP_INTR(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_INT ? 1 : 0)
-
-#define USB_SCN_MINOR(X) minor((X)->i_rdev) - SCN_BASE_MNR
-
-#ifdef DEBUG
-#define SCN_DEBUG(X) X
-#else
-#define SCN_DEBUG(X)
-#endif
-
-#define IBUF_SIZE 32768
-#define OBUF_SIZE 4096
-
-/* read_scanner timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */
-#define RD_NAK_TIMEOUT (10*HZ) /* Default number of X seconds to wait */
-#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */
-
-
-/* FIXME: These are NOT registered ioctls()'s */
-#ifdef PV8630
-#define PV8630_IOCTL_INREQUEST 69
-#define PV8630_IOCTL_OUTREQUEST 70
-#endif /* PV8630 */
-
-
-/* read vendor and product IDs from the scanner */
-#define SCANNER_IOCTL_VENDOR _IOR('U', 0x20, int)
-#define SCANNER_IOCTL_PRODUCT _IOR('U', 0x21, int)
-/* send/recv a control message to the scanner */
-#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, struct usb_ctrlrequest)
-
-
-#define SCN_MAX_MNR 16 /* We're allocated 16 minors */
-#define SCN_BASE_MNR 48 /* USB Scanners start at minor 48 */
-
-static DECLARE_MUTEX (scn_mutex); /* Initializes to unlocked */
-
-struct scn_usb_data {
- struct usb_device *scn_dev;
- devfs_handle_t devfs; /* devfs device */
- struct urb *scn_irq;
- unsigned int ifnum; /* Interface number of the USB device */
- int scn_minor; /* Scanner minor - used in disconnect() */
- unsigned char button; /* Front panel buffer */
- char isopen; /* Not zero if the device is open */
- char present; /* Not zero if device is present */
- char *obuf, *ibuf; /* transfer buffers */
- char bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */
- wait_queue_head_t rd_wait_q; /* read timeouts */
- struct semaphore sem; /* lock to prevent concurrent reads or writes */
- unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */
-};
-
-extern devfs_handle_t usb_devfs_handle;
-
-static struct scn_usb_data *p_scn_table[SCN_MAX_MNR] = { NULL, /* ... */};
-
-static struct usb_driver scanner_driver;
obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
include $(TOPDIR)/drivers/net/Makefile.lib
-include $(TOPDIR)/drivers/usb/Makefile.lib
+include $(TOPDIR)/drivers/usb/class/Makefile.lib
include $(TOPDIR)/fs/Makefile.lib
include $(TOPDIR)/Rules.make