[sane-devel] UMAX 2200 USB support

Frank Zago fzago at greshamstorage.com
Wed Nov 21 03:47:52 GMT 2001


Hi,

I've hacked the umax backend to support that scanner. It works fine, at
least on linux.

My main problem is the integration into SANE.

* I had to disable the other scanners support to redirect the 6 sanei_scsi_*
functions to some usb functions.  Since this scanner is a scsi scanner
based on the powervision 8630, the rest of the code is virtually
unchanged.  That why I'd like to change the umax backed to support both
scsi and usb scanners. I think this can be solved by using some function
ptr magic. I'd like to have some comments from the maintainer of that
backend.

* I used the libusb library to support usb. As a result, the backend
autodetect the scanner and does not use /dev/usbscanner nor the
configuration files. And it does not use the scanner driver. Is the use of
the libusb a good idea?

To use the scanner, apply the patch, rebuild and install Sane.
type "setenv LD_PRELOAD /usr/local/lib/libusb-0.1.so.4" (or whatever the
path is) so the libusb will be preloaded. and then "xscanimage umax:"

This is a usable but unfinished work. Patch attached.

Frank.
-------------- next part --------------
diff -u -r sane-backends-1.0.6.org/backend/umax.c sane-backends-1.0.6/backend/umax.c
--- sane-backends-1.0.6.org/backend/umax.c	Sat Oct 27 06:57:45 2001
+++ sane-backends-1.0.6/backend/umax.c	Tue Nov 20 21:38:37 2001
@@ -188,7 +188,7 @@
 /* number of lines that shall be scanned in one buffer for scan if possible */
 static int umax_scan_lines    = 40; /* default: 40 scan lines */
 
-static int umax_scsi_maxqueue = 2; /* use command queueing depth 2 as default */
+static int umax_scsi_maxqueue = 1; /* use command queueing depth 2 as default */
 static int umax_handle_bad_sense_error = 0; /* default: handle bad sense error code as device busy */
 static int umax_execute_request_sense  = 0; /* default: do not use request sense in do_calibration */
 static int umax_force_preview_bit_rgb  = 0; /* default: do not force preview bit in real color scan */
@@ -291,6 +291,373 @@
   return SANE_FALSE;
 }
 
+
+/* USB specific parts */
+
+#include <usb.h>
+
+/* Ids for the UMAX 2200U. */
+static int idVendor = 0x1606;
+static int idProduct = 0x0230;
+
+
+/* request */
+#define PV8630_REQ_READBYTE         0x00
+#define PV8630_REQ_WRITEBYTE        0x01
+#define PV8630_REQ_EPPBULKREAD      0x02
+#define PV8630_REQ_EPPBULKWRITE     0x03
+#define PV8630_REQ_FLUSHBUFFER      0x04
+#define PV8630_REQ_ENABLEINTERRUPT  0x05
+#define PV8630_REQ_DISABLEINTERRUPT 0x06
+#define PV8630_REQ_READWORD         0x08
+#define PV8630_REQ_WRITEWORD        0x09
+
+/* index */
+#define PV8630_RDATA       0x00
+#define PV8630_REPPADDRESS 0x01
+#define PV8630_UNKNOWN	   0x02
+#define PV8630_RMODE       0x03
+#define PV8630_RSTATUS     0x04
+
+void pv8630_write_byte(usb_dev_handle *dev, int value, int index)
+{
+	if (usb_control_msg(dev, 0x40, PV8630_REQ_WRITEBYTE, value, index, NULL, 0, 10000) == -1) {
+		perror("usb_control_msg PV8630_REQ_WRITEBYTE");
+		exit(1);
+	}
+}
+
+void pv8630_read_byte(usb_dev_handle *dev, unsigned char *data, int index)
+{
+	if (usb_control_msg(dev, 0xc0, PV8630_REQ_READBYTE, 0, index, data, 1, 10000) == -1) {
+		perror("usb_control_msg PV8630_REQ_READBYTE");
+		exit(1);
+	}
+}
+
+void pv8630_flush_buffer(usb_dev_handle *dev)
+{
+	if (usb_control_msg(dev, 0x40, PV8630_REQ_FLUSHBUFFER, 0, 0, NULL, 0, 10000) == -1) {
+		perror("usb_control_msg PV8630_REQ_FLUSHBUFFER");
+		exit(1);
+	}
+}
+
+void pv8630_prep_bulkwrite(usb_dev_handle *dev, int len)
+{
+	if (usb_control_msg(dev, 0x40, PV8630_REQ_EPPBULKWRITE, len & 0xffff, len >> 16, NULL, 0, 10000) == -1) {
+		perror("usb_control_msg PV8630_REQ_EPPBULKWRITE");
+		exit(1);
+	}
+}
+
+void pv8630_prep_bulkread(usb_dev_handle *dev, int len)
+{
+	if (usb_control_msg(dev, 0x40, PV8630_REQ_EPPBULKREAD, len & 0xffff, len >> 16, NULL, 0, 10000) == -1) {
+		perror("usb_control_msg PV8630_REQ_EPPBULKREAD");
+		exit(1);
+	}
+}
+
+void pv8630_bulkwrite(usb_dev_handle *dev, const void *data, int len)
+{
+	if (usb_bulk_write(dev, 0x01, (char *)data, len, 10000) != len) {
+		perror("usb_bulk_write");
+		exit(-1);
+	}
+}
+
+size_t pv8630_bulkread(usb_dev_handle *dev, void *data, size_t len)
+{
+	ssize_t len_ret;
+
+	if ((len_ret = usb_bulk_read(dev, 0x82, data, len, 10000)) == -1) {
+		perror("usb_bulk_read");
+	}
+
+	return(len_ret);
+}
+
+void pv8630_status_wait(usb_dev_handle *dev, unsigned char status)
+{
+	unsigned char s;
+	int i;
+
+	for (i=0; i<1000; i++) {
+		pv8630_read_byte(dev, &s, PV8630_RSTATUS);
+		if (s == status)
+			return;
+		usleep(100000);
+	}
+	DBG(DBG_info, "status never ok\n");
+	exit(1);
+}
+
+void mini_init_scanner(usb_dev_handle *dev)
+{
+	DBG(DBG_info, "mini_init_scanner\n");
+
+	/* Init the device (?) */
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+
+	pv8630_status_wait(dev, 0xd0);
+}
+
+/* FZ: cdb_sizes is incorrect. at least one 16 byte is missing. */
+static u_char cdb_sizes[8] =
+  {
+    6, 10, 10, 12, 12, 12, 10, 10
+  };
+#define CDB_SIZE(opcode)	cdb_sizes[(((opcode) >> 5) & 7)]
+
+static SANE_Status sanei_umaxusb_cmd(usb_dev_handle *dev, const void *src, size_t src_size, void *dst, size_t * dst_size)
+{
+	unsigned char result;
+	size_t cmd_size = CDB_SIZE (*(char *) src);
+	size_t param_size = src_size - cmd_size;
+	char * param_ptr = ((char *) src) + cmd_size;
+
+	DBG(DBG_info, "Sending SCSI cmd 0x%02x cdb len %d, param len %d, result len %d\n", ((unsigned char *)src)[0], cmd_size, param_size, dst_size? *dst_size:0);
+
+	pv8630_write_byte(dev, 0x0c, PV8630_UNKNOWN);
+	pv8630_status_wait(dev, 0xf0);
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN);
+
+	/* Send the CDB and check it's been received OK. */
+	pv8630_write_byte(dev, 0x16, PV8630_RMODE);
+	pv8630_flush_buffer(dev);
+	pv8630_prep_bulkwrite(dev, cmd_size);
+	
+	pv8630_bulkwrite(dev, src, cmd_size);
+	pv8630_status_wait(dev, 0xf8);
+
+	pv8630_flush_buffer(dev);
+	pv8630_prep_bulkread(dev, 1);
+
+	result = 0xA5;				/* to be sure */
+	pv8630_bulkread(dev, &result, 1);
+	if (result != 0) {
+		DBG(DBG_info, "error in pv8630_bulkread (got %02x)\n", result);
+		if (result == 8) {
+			mini_init_scanner(dev);
+		}
+		return(SANE_STATUS_IO_ERROR);
+	}
+	
+	/* Send the parameters and check they've been received OK. */
+	if (param_size) {
+		pv8630_flush_buffer(dev);
+		pv8630_prep_bulkwrite(dev, param_size);
+		
+		pv8630_bulkwrite(dev, param_ptr, param_size);
+		pv8630_status_wait(dev, 0xf8);
+		
+		pv8630_flush_buffer(dev);
+		pv8630_prep_bulkread(dev, 1);
+		
+		result = 0xA5;				/* to be sure */
+		pv8630_bulkread(dev, &result, 1);
+		if (result != 0) {
+			DBG(DBG_info, "error in pv8630_bulkread (got %02x)\n", result);
+			if (result == 8) {
+				mini_init_scanner(dev);
+			}
+			return(SANE_STATUS_IO_ERROR);
+		}
+	}
+
+	if (dst_size != NULL && *dst_size != 0 && dst != NULL) {
+		/* This command expect a return. */
+		pv8630_flush_buffer(dev);
+		pv8630_prep_bulkread(dev, *dst_size);
+		*dst_size = pv8630_bulkread(dev, dst, *dst_size);
+
+		DBG(DBG_info, "  SCSI cmd returned %d bytes\n", *dst_size);
+
+		pv8630_status_wait(dev, 0xf8);
+
+		pv8630_flush_buffer(dev);
+		pv8630_prep_bulkread(dev, 1);
+
+		result = 0x5A;			/* just to be sure */
+		pv8630_bulkread(dev, &result, 1);
+		if (result != 0) {
+			DBG(DBG_info, "error in pv8630_bulkread (got %02x)\n", result);
+			if (result == 8) {
+				mini_init_scanner(dev);
+			}
+			return(SANE_STATUS_IO_ERROR);
+		}
+	}
+
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+	pv8630_status_wait(dev, 0xd0);
+
+	DBG(DBG_info, "  SCSI cmd 0x%02x sent\n", ((char *)src)[0]);
+ 
+	return(SANE_STATUS_GOOD);
+}
+
+
+void *umaxusb_req_buffer;		/* keep the buffer ptr as an ID */
+SANE_Status
+sanei_umaxusb_req_wait (void *id)
+{
+	if (id != umaxusb_req_buffer) {
+		DBG(DBG_info, "sanei_umaxusb_req_wait: AIE, invalid id\n");
+		return(SANE_STATUS_IO_ERROR);
+	}
+	return(SANE_STATUS_GOOD);
+}
+
+SANE_Status sanei_umaxusb_req_enter (usb_dev_handle *dev,
+								  const void *src, size_t src_size,
+								  void *dst, size_t * dst_size, void **idp)
+{
+	umaxusb_req_buffer = *idp = dst;
+	return(sanei_umaxusb_cmd(dev, src, src_size, dst, dst_size));
+}
+
+int init_umaxusb_scanner(usb_dev_handle *dev)
+{
+	DBG(DBG_info, "Initialize the device\n");
+
+	/* Init the device */
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+
+	pv8630_status_wait(dev, 0xd0);
+
+	pv8630_write_byte(dev, 0x0c, PV8630_UNKNOWN);
+	pv8630_status_wait(dev, 0xf0);
+
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN);
+	pv8630_status_wait(dev, 0xf0);
+
+	pv8630_write_byte(dev, 0x0c, PV8630_UNKNOWN);
+	pv8630_status_wait(dev, 0xf0);
+	pv8630_status_wait(dev, 0xf8);
+
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN); /* 197 */
+
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+	pv8630_write_byte(dev, 0x02, PV8630_RMODE);
+	pv8630_status_wait(dev, 0xd0);
+
+	pv8630_write_byte(dev, 0x0c, PV8630_UNKNOWN); /* 201 */
+	pv8630_status_wait(dev, 0xf0);
+
+	pv8630_write_byte(dev, 0x04, PV8630_UNKNOWN); /* 203 */
+
+	pv8630_write_byte(dev, 0x16, PV8630_RMODE);	/* 204 */
+
+	DBG(DBG_info, "Device initialized\n");
+	
+	return(0); 
+}
+
+
+usb_dev_handle *
+open_device (struct usb_device *device)
+{
+	usb_dev_handle *handle;
+
+	DBG(DBG_info, "opening device %s on bus %s\n",
+			device->filename, device->bus->dirname);
+
+	handle = usb_open (device);
+
+	if (!handle) {
+		perror("open");
+		return 0;
+	}
+
+	return handle;
+}
+
+usb_dev_handle *
+scan_bus (struct usb_bus *bus)
+{
+	struct usb_device *roottree = bus->devices;
+	struct usb_device *device;
+	usb_dev_handle *device_handle;
+
+	DBG(DBG_info, "scanning bus %s\n", bus->dirname);
+
+	for (device = roottree; device; device = device->next) {
+		if ((device->descriptor.idVendor == idVendor) && 
+			(device->descriptor.idProduct == idProduct)) {
+
+			DBG(DBG_info, "found device %s on bus %s (idVendor 0x%x idProduct 0x%x)\n",
+				 device->filename, device->bus->dirname,
+				 device->descriptor.idVendor, device->descriptor.idProduct);
+
+			if ((device_handle = open_device (device))) {
+				return(device_handle);
+			}
+
+			DBG(DBG_info, "continuing to scan bus %s\n", bus->dirname);
+		}
+		else
+			DBG(DBG_info, "device %s on bus %s does not match\n",
+					device->filename, device->bus->dirname);
+	}
+
+	return(NULL);
+}
+
+/* Ignore dev. Returns the first good device. */
+SANE_Status
+sanei_umaxusb_open (const char *dev, usb_dev_handle **fdp,
+					SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+	struct usb_bus* bus;
+
+	usb_init ();
+	usb_find_busses ();
+	usb_find_devices ();
+
+	for( bus = usb_busses; bus; bus = bus->next ) {
+		
+		*fdp = scan_bus (bus);
+		if (*fdp) break;
+	}
+
+	if (*fdp == NULL) {
+		return(SANE_STATUS_INVAL);
+	} else {
+		init_umaxusb_scanner(*fdp);
+		return(SANE_STATUS_GOOD);
+	}
+}
+
+SANE_Status
+sanei_umaxusb_open_extended (const char *dev, usb_dev_handle **fdp,
+					SANEI_SCSI_Sense_Handler handler, void *handler_arg, int *buffersize)
+{
+	return(sanei_umaxusb_open(dev, fdp, handler, handler_arg));
+}
+
+void
+sanei_umaxusb_close (usb_dev_handle *fdp)
+{
+	usb_close(fdp);
+}
+
+#define sanei_scsi_cmd sanei_umaxusb_cmd
+#define sanei_scsi_open sanei_umaxusb_open
+#define sanei_scsi_close sanei_umaxusb_close
+#define sanei_scsi_open_extended sanei_umaxusb_open_extended
+#define sanei_scsi_req_enter sanei_umaxusb_req_enter
+#define sanei_scsi_req_wait sanei_umaxusb_req_wait
+
+/* End of USB specific parts */
+
 /* ------------------------------------------------------------ DBG_inq_nz --------------------------------- */
 
 
@@ -1288,7 +1655,7 @@
   }
 
   sanei_scsi_close(scanner->device->sfd);
-  scanner->device->sfd = -1;
+  scanner->device->sfd = NULL;
 
  return status;
 }
@@ -4052,7 +4419,7 @@
   DBG(DBG_proc,"init\n");
 
   dev->devicename                        = NULL;
-  dev->sfd                               = -1;
+  dev->sfd                               = NULL;
   dev->pixelbuffer                       = NULL;
 
   /* config file or predefined settings */
@@ -4234,12 +4601,12 @@
     }
   }
 
-  if (scanner->device->sfd >= 0)
+  if (scanner->device->sfd)
   {
     umax_give_scanner(scanner->device); /* reposition and release scanner */
     DBG(DBG_sane_info,"closing scannerdevice filedescriptor\n");
     sanei_scsi_close(scanner->device->sfd);
-    scanner->device->sfd = -1;
+    scanner->device->sfd = NULL;
   }
 
   scanner->device->three_pass_color = 1; /* reset color in color scanning */
@@ -4254,7 +4621,7 @@
 static SANE_Status attach_scanner(const char *devicename, Umax_Device **devp)
 {
  Umax_Device *dev;
- int sfd;
+ usb_dev_handle *sfd;
  int i;
 
   DBG(DBG_sane_proc,"attach_scanner: %s\n", devicename);
@@ -4338,7 +4705,7 @@
   {
     DBG(DBG_error, "ERROR: attach_scanner: scanner-identification failed\n");
     sanei_scsi_close(dev->sfd);
-    dev->sfd=-1;
+    dev->sfd=NULL;
     free(dev->buffer[0]);
     free(dev);
     return SANE_STATUS_INVAL;
@@ -4366,7 +4733,7 @@
   DBG(DBG_inquiry,"\n");
 
   sanei_scsi_close(dev->sfd);
-  dev->sfd=-1;
+  dev->sfd=NULL;
 
   dev->sane.name   = dev->devicename;
   dev->sane.vendor = dev->vendor;
@@ -5625,7 +5992,7 @@
   memset(scanner, 0, sizeof (*scanner));
 
   scanner->device      = dev;
-  scanner->device->sfd = -1;
+  scanner->device->sfd = NULL;
 
   if (scanner->device->inquiry_GIB & 32)
   {
@@ -6666,7 +7033,7 @@
 
   mode = scanner->val[OPT_MODE].s;
 
-  if (scanner->device->sfd < 0)   /* first call, don`t run this routine again on multi frame or multi image scan */
+  if (scanner->device->sfd == NULL)   /* first call, don`t run this routine again on multi frame or multi image scan */
   {
     umax_initialize_values(scanner->device);								    /* reset values */
 
@@ -6685,7 +7052,7 @@
       {
         DBG(DBG_error,"ERROR: Transparency Adapter not available\n");
         sanei_scsi_close(scanner->device->sfd);
-        scanner->device->sfd=-1;
+        scanner->device->sfd=NULL;
        return SANE_STATUS_INVAL;
       }
     }
@@ -6703,7 +7070,7 @@
         {
           DBG(DBG_error,"ERROR: Automatic Document Feeder not available\n");
          sanei_scsi_close(scanner->device->sfd);
-         scanner->device->sfd=-1;
+         scanner->device->sfd=NULL;
          return SANE_STATUS_INVAL;
         }
       }
@@ -7121,7 +7488,7 @@
     if (umax_grab_scanner(scanner->device))
     {
       sanei_scsi_close(scanner->device->sfd);
-      scanner->device->sfd=-1;
+      scanner->device->sfd=NULL;
       scanner->scanning = SANE_FALSE;
       DBG(DBG_warning,"WARNING: unable to reserve scanner: device busy\n");
      return SANE_STATUS_DEVICE_BUSY;
@@ -7305,7 +7672,7 @@
     scanner->scanning = SANE_FALSE;
     umax_give_scanner(scanner->device); /* reposition and release scanner */
     sanei_scsi_close(scanner->device->sfd);
-    scanner->device->sfd=-1;
+    scanner->device->sfd=NULL;
    return SANE_STATUS_IO_ERROR;
   }
 
diff -u -r sane-backends-1.0.6.org/backend/umax.h sane-backends-1.0.6/backend/umax.h
--- sane-backends-1.0.6.org/backend/umax.h	Sun Jun 17 17:49:17 2001
+++ sane-backends-1.0.6/backend/umax.h	Tue Nov 20 21:35:11 2001
@@ -54,6 +54,7 @@
 # include "sane/sanei_ipc.h"
 #endif
 
+#include <usb.h>
 
 /* --------------------------------------------------------------------------------------------------------- */
 /* COMPILER OPTIONS: */
@@ -188,10 +189,10 @@
 
 /* -------------------------------------------------------------------------------------------------------- */
 
-
 /* LIST OF AVAILABLE SCANNERS, THE VALUES LISTED HERE ARE THE SAME FOR DIFFERENT APPLICATIONS
    THAT USE THE SAME DEVICE */
-   
+
+
 /* Umax_Device contains values relevant for the device that are not intersting for the sane interface */
 
 typedef struct Umax_Device
@@ -236,7 +237,7 @@
 #  define CCD_color_blue  2
 
   char			*devicename;					       /* name of the scanner device */
-  int			sfd;					   /* output file descriptor, scanner device */
+  usb_dev_handle *sfd;					   /* output file descriptor, scanner device */
 
   char			vendor[9];							     /* will be UMAX */
   char			product[17];					      /* e.g. "SuperVista_S12" or so */


More information about the sane-devel mailing list