[sane-devel] Fujitsu USB patches

Ron Cemer ron@roncemer.com
Mon, 17 Feb 2003 11:44:32 -0800


This is a multi-part message in MIME format.
--------------030703000607030909030808
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

I've fixed the patchfile based on your feedback below. I'm using a newer 
version of gcc which allows you to declare variables inline with local 
scope, instead of at the top of the function. I just moved the setwinB 
declaration to the top of the function. And you're right, the 
fujitsu.man patch is reversed. I fixed that also.

I'll re-send this patch to the fujitsu maintaner. No response from him yet.

Thanks!
Ron


Henning Meier-Geinitz wrote:

>Hi,
>
>On Thu, Feb 13, 2003 at 03:40:44PM -0800, Ron Cemer wrote:
>  
>
>>Here are updated patches that get the Fujitsu fi-4220C scanner working, 
>>and add USB support for Fujitsu scanners.
>>
>>It now supports libusb or regular usb scanner devices, as described in 
>>the sane-usb man page.
>>
>>It uses SCSI commands over USB, using a non-standard implementation of 
>>the USB mass storage protocol.
>>
>>Thanks to Henning for various suggestions to get it in line with the 
>>SANE standards! :-)
>>
>>I'm sending this to both the sane-devel mailing list and the current 
>>Fujitsu backend maintaner, in hopes that the changes will be implemented 
>>as quickly as possible.
>>    
>>
>
>If you don't get a response from the maintainer (say in 3 weeks),
>please contact us again. Just in case he isn't active any longer.
>
>  
>
>>--- ./backend/fujitsu.c.orig	2002-09-16 05:19:52.000000000 -0700
>>+++ ./backend/fujitsu.c	2003-02-13 15:03:17.000000000 -0800
>>    
>>
>
>[...]
>
>  
>
>>@@ -4352,18 +4609,26 @@
>>    * follows without a header of its own. 
>>    */
>> 
>>+
>>+    scsiblk *setwinB;
>>    
>>
>
>gcc complains:
>
>fujitsu.c: In function setWindowParam':
>fujitsu.c:4613: parse error before *'
>fujitsu.c:4615: setwinB' undeclared (first use in this function)
>
>setwinB doesn't seem to be defined anywhere.
>
>  
>
>>+++ ./doc/descriptions/fujitsu.desc	2003-02-13 15:04:51.000000000 -0800
>>@@ -37,6 +37,8 @@
>> :interface "SCSI"
>> :model "M4097"
>> :interface "SCSI"
>>+:model "fi-4220C"
>>+:interface "SCSI USB"
>> :model "fi-4340C"
>>-:interface "SCSI"
>>+:interface "SCSI USB"
>>    
>>
>
>That's ok if you (or someone else) have tested these models with both
>USB and SCSI and they work.
>
>If you think other models may also work but you can't test (because
>you don't have a scanner), you can use entries like that:
>
>:model "whatever"
>:interface "SCSI"
>:status :untested
>:comment "Looks similar to foo, please test"
>
>  
>
>>--- ./doc/sane-fujitsu.man.orig	2003-02-13 15:13:15.000000000 -0800
>>+++ ./doc/sane-fujitsu.man	2002-11-29 12:15:30.000000000 -0800
>>@@ -4,13 +4,13 @@
>> .IX sane-m3096g
>> 
>> .SH NAME
>>-sane-fujitsu \- SANE backend for Fujitsu flatbed and ADF scanners
>>+sane-fujitsu \- SANE backend for Fujitsu flatbed scanners
>>    
>>
>
>That patch is reversed (the other way round)!
>
>Bye,
>  Henning
>_______________________________________________
>Sane-devel mailing list
>Sane-devel@www.mostang.com
>http://www.mostang.com/mailman/listinfo/sane-devel
>
>  
>


--------------030703000607030909030808
Content-Type: text/plain;
 name="fi-4220C-USB.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="fi-4220C-USB.diff"

--- backend/Makefile.in.orig	2003-01-13 11:49:46.000000000 -0800
+++ backend/Makefile.in	2003-02-12 14:00:17.000000000 -0800
@@ -302,6 +302,7 @@
 libsane-fujitsu.la: ../sanei/sanei_config2.lo
 libsane-fujitsu.la: ../sanei/sanei_constrain_value.lo
 libsane-fujitsu.la: ../sanei/sanei_scsi.lo
+libsane-fujitsu.la: ../sanei/sanei_usb.lo
 libsane-gphoto2.la: ../sanei/sanei_constrain_value.lo djpeg.lo
 libsane-gt68xx.la: ../sanei/sanei_constrain_value.lo
 libsane-gt68xx.la: ../sanei/sanei_usb.lo
--- backend/fujitsu-scsi.h.orig	2002-09-16 05:19:52.000000000 -0700
+++ backend/fujitsu-scsi.h	2003-02-12 14:00:17.000000000 -0800
@@ -230,6 +230,16 @@
 static scsiblk set_windowB = { set_windowC, sizeof (set_windowC) };
 #define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
 
+	/* With the fi-series scanners, we have to use a 12-byte command
+	 * instead of a 10-byte command when communicating via USB.  This
+	 * may be a firmware bug. */
+static unsigned char set_usb_windowC[] =
+  { SET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00 };
+/* opcode,  lun,  _____4 X reserved____,  transfer length, control byte */
+static scsiblk set_usb_windowB = { set_usb_windowC, sizeof (set_usb_windowC) };
+#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3)
+
 /* ==================================================================== */
 
 static unsigned char object_positionC[] =
@@ -378,6 +388,17 @@
 };
 
 
+	/* With the fi-series scanners, we have to use a 10-byte header
+	 * instead of a 4-byte header when communicating via USB.  This
+	 * may be a firmware bug. */
+static unsigned char mode_select_usb_headerC[] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static scsiblk mode_select_usb_headerB = {
+  mode_select_usb_headerC, sizeof (mode_select_usb_headerC)
+};
+
+
 static unsigned char mode_select_parameter_blockC[] = {
   0x00, 0x00, 0x00,
   0x00, 0x00, 0x00,
--- backend/fujitsu.c.orig	2002-09-16 05:19:52.000000000 -0700
+++ backend/fujitsu.c	2003-02-17 11:34:37.000000000 -0800
@@ -77,6 +77,10 @@
          - 3092 support (mgoppold@tbz-pariv.de)
          - tested 4097 support
          - changed some functions to receive compressed data
+      V 1.4, 13-Feb-2003
+         - fi-4220C support (ron@roncemer.com)
+         - USB support for scanners which send SCSI commands over usb
+           (ron@roncemer.com)
 
    SANE FLOW DIAGRAM
 
@@ -124,6 +128,7 @@
 
 #include "sane/sanei_backend.h"
 #include "sane/sanei_scsi.h"
+#include "sane/sanei_usb.h"
 #include "sane/saneopts.h"
 #include "sane/sanei_config.h"
 
@@ -338,6 +343,11 @@
 static const SANE_Device **devlist = 0;
 
 /*
+ * used by attachScanner and attachOne
+ */
+static Fujitsu_Connection_Type mostRecentConfigConnectionType = SANE_FUJITSU_SCSI;
+
+/*
  * @@ Section 2 - SANE Interface
  */
 
@@ -364,11 +374,14 @@
   size_t len;
   FILE *fp;
 
+  mostRecentConfigConnectionType = SANE_FUJITSU_SCSI;
   authorize = authorize;        /* get rid of compiler warning */
 
   DBG_INIT ();
   DBG (10, "sane_init\n");
 
+  sanei_usb_init();
+
   if (version_code)
     *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0);
   fp = sanei_config_open (FUJITSU_CONFIG_FILE);
@@ -393,6 +406,7 @@
 
   while (sanei_config_read (line, PATH_MAX, fp))
     {
+      int vendor, product;
 
       /* ignore comments */
       if (line[0] == '#')
@@ -457,11 +471,23 @@
                    lp);
             }
         }
-      else                      /* must be a device name if it's not an option */
+        else if (sscanf(lp, "usb %i %i", &vendor, &product) == 2)
         {
+	    mostRecentConfigConnectionType = SANE_FUJITSU_USB;
+	    sanei_usb_attach_matching_devices(lp, attachOne);
+	    mostRecentConfigConnectionType = SANE_FUJITSU_SCSI;
+        }
+        else                      /* must be a device name if it's not an option */
+        {
+	  if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
+	    lp += 3;
+	    lp = sanei_config_skip_whitespace (lp);
+	    mostRecentConfigConnectionType = SANE_FUJITSU_USB;
+	  }
           strncpy (devName, lp, sizeof (devName));
           devName[sizeof (devName) - 1] = '\0';
           sanei_config_attach_matching_devices (devName, attachOne);
+	  mostRecentConfigConnectionType = SANE_FUJITSU_SCSI;
         }
     }
   fclose (fp);
@@ -568,7 +594,10 @@
     case MODEL_3093:
     case MODEL_4097:
     case MODEL_FI:
-      setDefaults3096 (scanner);
+      if ( strstr (scanner->productName, "4220") ) 
+        setDefaults3091 (scanner);
+      else
+        setDefaults3096 (scanner);
       break;
 
     case MODEL_SP15:
@@ -1351,7 +1380,10 @@
             case MODEL_3097:
             case MODEL_4097:
             case MODEL_FI:
-              return (setMode3096 (scanner, newMode));
+    		if ( strstr (scanner->productName, "4220") ) 
+            	    return (setMode3091 (scanner, newMode));
+    		else
+            	    return (setMode3096 (scanner, newMode));
             case MODEL_SP15:
               return (setModeSP15 (scanner, newMode));
             }
@@ -1871,23 +1903,35 @@
   if (scanner->sfd < 0)
     {
       /* first call */
-      if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd),
-                           senseHandler, 0) != SANE_STATUS_GOOD)
-        {
-          DBG (MSG_ERR, 
-               "sane_start: open of %s failed:\n", scanner->sane.name);
-          return SANE_STATUS_INVAL;
-        }
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    DBG (10, "sane_start opening USB device\n");
+	    if (sanei_usb_open (scanner->sane.name, &(scanner->sfd)) !=
+		SANE_STATUS_GOOD) {
+		DBG (MSG_ERR, 
+		     "sane_start: open of %s failed:\n", scanner->sane.name);
+		return SANE_STATUS_INVAL;
+	    }
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    DBG (10, "sane_start opening SCSI device\n");
+	    if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd),
+				 scsiSenseHandler, 0) != SANE_STATUS_GOOD) {
+		DBG (MSG_ERR, 
+		     "sane_start: open of %s failed:\n", scanner->sane.name);
+		return SANE_STATUS_INVAL;
+	    }
+      }
     }
   scanner->object_count = 1;
   scanner->eof = SANE_FALSE;
 
-
-
   if ((ret = grabScanner (scanner)))
     {
       DBG (5, "sane_start: unable to reserve scanner\n");
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->object_count = 0;
       scanner->sfd = -1;
       return ret;
@@ -1907,7 +1951,11 @@
     {
       DBG (5, "sane_start: ERROR: failed to start send command\n");
       freeScanner (scanner);
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->object_count = 0;
       scanner->sfd = -1;
       return ret;
@@ -1917,7 +1965,11 @@
     {
       DBG (5, "sane_start: ERROR: failed to start imprinter command\n");
       freeScanner (scanner);
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->object_count = 0;
       scanner->sfd = -1;
       return ret;
@@ -1929,7 +1981,11 @@
     {
       DBG (5, "sane_start: WARNING: ADF empty\n");
       freeScanner (scanner);
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->object_count = 0;
       scanner->sfd = -1;
       return ret;
@@ -1942,7 +1998,11 @@
     {
       DBG (5, "sane_start: ERROR: failed to set window\n");
       freeScanner (scanner);
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->object_count = 0;
       scanner->sfd = -1;
       return ret;
@@ -1965,7 +2025,11 @@
       DBG (MSG_ERR, "ERROR: could not create pipe\n");
       scanner->object_count = 0;
       freeScanner (scanner);
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->sfd = -1;
       return SANE_STATUS_IO_ERROR;
     }
@@ -1980,7 +2044,11 @@
               DBG (MSG_ERR, "ERROR: could not create temporary file.\n");
               scanner->object_count = 0;
               freeScanner (scanner);
-              sanei_scsi_close (scanner->sfd);
+	      if (scanner->connection == SANE_FUJITSU_USB) {
+		    sanei_usb_close (scanner->sfd);
+	      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+		    sanei_scsi_close (scanner->sfd);
+	      }
               scanner->sfd = -1;
               return SANE_STATUS_IO_ERROR;
             }
@@ -1992,7 +2060,11 @@
               DBG (MSG_ERR, "ERROR: could not create duplex pipe.\n");
               scanner->object_count = 0;
               freeScanner (scanner);
-              sanei_scsi_close (scanner->sfd);
+	      if (scanner->connection == SANE_FUJITSU_USB) {
+		    sanei_usb_close (scanner->sfd);
+	      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+		    sanei_scsi_close (scanner->sfd);
+	      }
               scanner->sfd = -1;
               return SANE_STATUS_IO_ERROR;
             }
@@ -2294,7 +2366,7 @@
 attachScanner (const char *devicename, struct fujitsu **devp)
 {
   struct fujitsu *dev;
-  int sfd;
+  SANE_Int sfd;
 
   DBG (15, "attach_scanner: %s\n", devicename);
 
@@ -2312,11 +2384,19 @@
     }
 
   DBG (15, "attach_scanner: opening %s\n", devicename);
-  if (sanei_scsi_open (devicename, &sfd, senseHandler, 0) != 0)
-    {
-      DBG (5, "attach_scanner: open failed\n");
-      return SANE_STATUS_INVAL;
+  if (mostRecentConfigConnectionType == SANE_FUJITSU_USB) {
+    DBG (15, "attachScanner opening USB device\n");
+    if (sanei_usb_open (devicename, &sfd) != SANE_STATUS_GOOD) {
+        DBG (5, "attach_scanner: open failed\n");
+	return SANE_STATUS_INVAL;
+    }
+  } else if (mostRecentConfigConnectionType == SANE_FUJITSU_SCSI) {
+    DBG (15, "attachScanner opening SCSI device\n");
+    if (sanei_scsi_open (devicename, &sfd, scsiSenseHandler, 0) != 0) {
+	DBG (5, "attach_scanner: open failed\n");
+	return SANE_STATUS_INVAL;
     }
+  }
 
   if (NULL == (dev = malloc (sizeof (*dev))))
     return SANE_STATUS_NO_MEM;
@@ -2327,6 +2407,7 @@
     return SANE_STATUS_NO_MEM;
 
   dev->devicename = strdup (devicename);
+  dev->connection = mostRecentConfigConnectionType;
   dev->sfd = sfd;
 
   /*
@@ -2335,14 +2416,22 @@
   if (identifyScanner (dev) != 0)
     {
       DBG (5, "attach_scanner: scanner identification failed\n");
-      sanei_scsi_close (dev->sfd);
+      if (dev->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (dev->sfd);
+      } else if (dev->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (dev->sfd);
+      }
       free (dev->buffer);
       free (dev);
       return SANE_STATUS_INVAL;
     }
 
   /* Why? */
-  sanei_scsi_close (dev->sfd);
+  if (dev->connection == SANE_FUJITSU_USB) {
+	sanei_usb_close (dev->sfd);
+  } else if (dev->connection == SANE_FUJITSU_SCSI) {
+	sanei_scsi_close (dev->sfd);
+  }
   dev->sfd = -1;
 
   dev->sane.name = dev->devicename;
@@ -2380,7 +2469,7 @@
  * Responsible for producing a meaningful debug message.
  */
 static SANE_Status
-senseHandler (int scsi_fd, u_char * sensed_data, void *arg)
+scsiSenseHandler (int scsi_fd, u_char * sensed_data, void *arg)
 {
   unsigned int ret = SANE_STATUS_IO_ERROR;
   unsigned int sense = get_RS_sense_key (sensed_data);
@@ -2615,7 +2704,8 @@
  
   hexdump (MSG_IO, "inquiry", inquiryB.cmd, inquiryB.size);
 
-  do_scsi_cmd (s->sfd, inquiryB.cmd, inquiryB.size, s->buffer, 96, NULL);
+  do_cmd (s->connection, s->sfd, inquiryB.cmd, inquiryB.size,
+	       s->buffer, 96, NULL);
 }
 
 static SANE_Status
@@ -2631,26 +2721,37 @@
     {
       int sfd;
 
-      if (sanei_scsi_open (s->devicename, &sfd, senseHandler, 0) != 0)
-        {
-          DBG (5, "get_hardware_status: open failed\n");
-          return SANE_STATUS_INVAL;
-        }
-      
+      if (s->connection == SANE_FUJITSU_USB) {
+	    DBG (10, "get_hardware_status opening USB device\n");
+	    if (sanei_usb_open (s->devicename, &sfd) != SANE_STATUS_GOOD) {
+		DBG (5, "get_hardware_status: open failed\n");
+		return SANE_STATUS_INVAL;
+	    }
+      } else if (s->connection == SANE_FUJITSU_SCSI) {
+	    DBG (10, "get_hardware_status opening SCSI device\n");
+	    if (sanei_scsi_open (s->devicename, &sfd, scsiSenseHandler, 0)!=0) {
+		DBG (5, "get_hardware_status: open failed\n");
+		return SANE_STATUS_INVAL;
+	    }
+      }      
       hexdump (MSG_IO, "get_hardware_status", 
                hw_statusB.cmd, hw_statusB.size);
 
-      ret = do_scsi_cmd (sfd, hw_statusB.cmd, hw_statusB.size,
-                         s->buffer, 10, NULL);
-      sanei_scsi_close (sfd);
+      ret = do_cmd (s->connection, sfd, hw_statusB.cmd, hw_statusB.size,
+                    s->buffer, 10, NULL);
+      if (s->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (sfd);
+      } else if (s->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (sfd);
+      }
     }
   else
     {
       hexdump (MSG_IO, "get_hardware_status", 
                hw_statusB.cmd, hw_statusB.size);
 
-      ret = do_scsi_cmd (s->sfd, hw_statusB.cmd, hw_statusB.size,
-                         s->buffer, 10, NULL);
+      ret = do_cmd (s->connection, s->sfd, hw_statusB.cmd, hw_statusB.size,
+                    s->buffer, 10, NULL);
     }
 
   if (ret == SANE_STATUS_GOOD)
@@ -2686,8 +2787,8 @@
   set_IN_page_code (inquiryB.cmd, 0xf0);
 
   hexdump (MSG_IO, "get_vital_product_data", inquiryB.cmd, inquiryB.size);
-  ret = do_scsi_cmd (s->sfd, inquiryB.cmd, inquiryB.size, 
-                     s->buffer, 0x64, NULL);
+  ret = do_cmd (s->connection, s->sfd, inquiryB.cmd, inquiryB.size, 
+                s->buffer, 0x64, NULL);
   if (ret == SANE_STATUS_GOOD)
     {
       DBG (MSG_INFO, "standard options\n");
@@ -2780,15 +2881,31 @@
   return ret;
 }
 
-
+/**
+ * Sends a command to the device. This calls do_scsi_cmd or do_usb_cmd.
+ */
+static int
+do_cmd (Fujitsu_Connection_Type connection, int fd, unsigned char *cmd,
+	     int cmd_len, unsigned char *out, size_t req_out_len,
+	     size_t *res_out_len)
+{
+    if (connection == SANE_FUJITSU_SCSI) {
+	return do_scsi_cmd(fd, cmd, cmd_len, out, req_out_len, res_out_len);
+    }
+    if (connection == SANE_FUJITSU_USB) {
+	return do_usb_cmd(fd, cmd, cmd_len, out, req_out_len, res_out_len);
+    }
+    return SANE_STATUS_INVAL;
+}
 
 /**
  * Sends a SCSI command to the device. This is just a wrapper around 
  * sanei_scsi_cmd with some debug printing.
  */
 static int
-do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len,
-             unsigned char *out, size_t req_out_len, size_t *res_out_len)
+do_scsi_cmd (int fd, unsigned char *cmd,
+	     int cmd_len, unsigned char *out, size_t req_out_len,
+	     size_t *res_out_len)
 {
   int ret;
   size_t ol = req_out_len;
@@ -2823,6 +2940,118 @@
   return ret;
 }
 
+#define USB_CMD_HEADER_BYTES 19
+#define USB_CMD_MIN_BYTES 31
+
+/**
+ * Sends a USB command to the device.
+ */
+static int
+do_usb_cmd (int fd, unsigned char *cmd,
+	     int cmd_len, unsigned char *out, size_t req_out_len,
+	     size_t *res_out_len)
+{
+    int ret = SANE_STATUS_GOOD;
+    size_t cnt, ol;
+    int op_code = 0;
+    int i, j;
+    int tries = 0;
+    unsigned char buf[1024];
+/*    unsigned char sense_bytes[64]; */
+
+retry:
+    hexdump (IO_CMD, "<cmd<", cmd, cmd_len);
+
+    if (cmd_len > 0) op_code = ((int)cmd[0]) & 0xff;
+
+    if ((cmd_len+USB_CMD_HEADER_BYTES) > (int)sizeof(buf)) {
+	    /* Command too long. */
+	return SANE_STATUS_INVAL;
+    }
+    buf[0] = (unsigned char)'C';
+    for (i = 1; i < USB_CMD_HEADER_BYTES; i++) buf[i] = (unsigned char)0;
+    memcpy(&buf[USB_CMD_HEADER_BYTES], cmd, cmd_len);
+    for (i = USB_CMD_HEADER_BYTES+cmd_len; i < USB_CMD_MIN_BYTES; i++) {
+	buf[i] = (unsigned char)0;
+    }
+	/* The SCAN command must be at least 32 bytes long. */
+    if ( (op_code == SCAN) && (i < 32) ) {
+	for (; i < 32; i++) buf[i] = (unsigned char)0;
+    }
+
+    for (j = 0; j < i;) {
+	cnt = i-j;
+	    /* First URB has to be 31 bytes. */
+	    /* All other URBs must be 64 bytes (max) per URB. */
+	if ( (j == 0) && (cnt > 31) ) cnt = 31; else if (cnt > 64) cnt = 64;
+	hexdump (IO_CMD, "*** URB going out:", &buf[j], cnt);
+	DBG (IO_CMD, "try to write %u bytes\n", cnt);
+	ret = sanei_usb_write_bulk(fd, &buf[j], &cnt);
+	DBG (IO_CMD, "wrote %u bytes\n", cnt);
+	if (ret != SANE_STATUS_GOOD) break;
+	j += cnt;
+    }
+    if (ret != SANE_STATUS_GOOD) {
+	DBG (MSG_ERR, "*** Got error %d trying to write\n", ret);
+    }
+
+    ol = 0;
+    if (ret == SANE_STATUS_GOOD) {
+	if ( (out != NULL) && (req_out_len > 0) ) {
+	    while (ol < req_out_len) {
+		cnt = (size_t)(req_out_len-ol);
+		DBG (IO_CMD, "try to read %u bytes\n", cnt);
+		ret = sanei_usb_read_bulk(fd, &out[ol], &cnt);
+		DBG (IO_CMD, "read %u bytes\n", cnt);
+		if (cnt > 0) {
+		    hexdump (IO_CMD, "*** Data read:", &out[ol], cnt);
+		}
+		if (ret != SANE_STATUS_GOOD) {
+		    DBG(MSG_ERR, "*** Got error %d trying to read\n", ret);
+		}
+		if (ret != SANE_STATUS_GOOD) break;
+		ol += cnt;
+	    }
+	}
+
+	DBG(MSG_ERR, "*** Try to read CSW\n");
+	cnt = sizeof(buf);
+	sanei_usb_read_bulk(fd, buf, &cnt);
+	hexdump (IO_CMD, "*** Read CSW", buf, cnt);
+    }
+
+	/* Auto-retry failed data reads, in case the scanner is busy. */
+    if ( (op_code == READ) && (tries < 100) && (ol == 0) ) {
+	usleep(100000L);
+	tries++;
+	goto retry;
+    }
+
+  if (res_out_len != NULL)
+    {
+      *res_out_len = ol;
+    }
+
+  if ((req_out_len != 0) && (req_out_len != ol))
+    {
+      DBG (MSG_ERR, "do_usb_cmd: asked %lu bytes, got %lu\n",
+           (u_long) req_out_len, (u_long) ol);
+    }
+
+  if (ret)
+    {
+      DBG (MSG_ERR, "do_usb_cmd: returning 0x%08x\n", ret);
+    }
+
+  DBG (IO_CMD_RES, "do_usb_cmd: returning %lu bytes:\n", (u_long) ol);
+
+  if (out != NULL && ol != 0)
+    {
+      hexdump (IO_CMD_RES, ">rslt>", out, (ol > 0x60) ? 0x60 : ol);
+    }
+
+  return ret;
+}
 
 /**
  * Prints a hex dump of the given buffer onto the debug output stream.
@@ -2927,8 +3156,8 @@
 #endif
 
   hexdump (MSG_IO, "release_unit", release_unitB.cmd, release_unitB.size);
-  ret = do_scsi_cmd (s->sfd, release_unitB.cmd, release_unitB.size, 
-                     NULL, 0, NULL);
+  ret = do_cmd (s->connection, s->sfd, release_unitB.cmd,
+		release_unitB.size, NULL, 0, NULL);
   if (ret)
     return ret;
 
@@ -2959,8 +3188,8 @@
   wait_scanner (s);
 
   hexdump (MSG_IO, "reserve_unit", reserve_unitB.cmd, reserve_unitB.size);
-  ret = do_scsi_cmd (s->sfd, reserve_unitB.cmd, reserve_unitB.size, 
-                     NULL, 0, NULL);
+  ret = do_cmd (s->connection, s->sfd, reserve_unitB.cmd,
+		reserve_unitB.size, NULL, 0, NULL);
   if (ret)
     return ret;
 
@@ -2968,7 +3197,7 @@
   return 0;
 }
 
-static int fujitsu_wait_scanner(int fd) 
+static int fujitsu_wait_scanner(Fujitsu_Connection_Type connection, int fd) 
 {
   int ret = -1;
   int cnt = 0;
@@ -2979,8 +3208,8 @@
     {
       hexdump (MSG_IO, "test_unit_ready", test_unit_readyB.cmd,
                test_unit_readyB.size);
-      ret = do_scsi_cmd (fd, test_unit_readyB.cmd,
-                         test_unit_readyB.size, 0, 0, NULL);
+      ret = do_cmd (connection, fd, test_unit_readyB.cmd,
+                    test_unit_readyB.size, 0, 0, NULL);
       if (ret == SANE_STATUS_DEVICE_BUSY)
         {
           usleep (500000);      /* wait 0.5 seconds */
@@ -3009,7 +3238,7 @@
 static int
 wait_scanner (struct fujitsu *s)
 {
-  return fujitsu_wait_scanner(s->sfd);
+  return fujitsu_wait_scanner(s->connection, s->sfd);
 }
 
 /**
@@ -3041,7 +3270,8 @@
     }
 
   hexdump (MSG_IO, "object_position", s->buffer, object_positionB.size);
-  ret = do_scsi_cmd (s->sfd, s->buffer, object_positionB.size, NULL, 0, NULL);
+  ret = do_cmd (s->connection, s->sfd, s->buffer, object_positionB.size,
+		NULL, 0, NULL);
   if (ret != SANE_STATUS_GOOD)
     return ret;
   wait_scanner (s);
@@ -3073,7 +3303,8 @@
      set_OP_autofeed (s->buffer, OP_Discharge);
   }
   hexdump (MSG_IO, "object_position", s->buffer, object_positionB.size);
-  ret = do_scsi_cmd (s->sfd, s->buffer, object_positionB.size, NULL, 0, NULL);
+  ret = do_cmd (s->connection, s->sfd, s->buffer, object_positionB.size,
+		NULL, 0, NULL);
   wait_scanner (s);
   DBG (10, "objectDischarge: ok\n");
   return ret;
@@ -3090,8 +3321,8 @@
 
   DBG (10, "doReset\n");
   if (scanner->model == MODEL_3092) {
-     ret = do_scsi_cmd (scanner->sfd, reset_unitB.cmd, reset_unitB.size, 
-			NULL, 0, NULL);
+     ret = do_cmd (scanner->connection, scanner->sfd, reset_unitB.cmd,
+		   reset_unitB.size, NULL, 0, NULL);
      if (ret)
        return ret;
   }
@@ -3140,7 +3371,11 @@
     {
       freeScanner (scanner);
       DBG (10, "doCancel: close filedescriptor\n");
-      sanei_scsi_close (scanner->sfd);
+      if (scanner->connection == SANE_FUJITSU_USB) {
+	    sanei_usb_close (scanner->sfd);
+      } else if (scanner->connection == SANE_FUJITSU_SCSI) {
+	    sanei_scsi_close (scanner->sfd);
+      }
       scanner->sfd = -1;
     }
 
@@ -3210,7 +3445,7 @@
     }
 
   hexdump (MSG_IO, "start_scan", command, cmdsize);
-  ret = do_scsi_cmd (s->sfd, command, cmdsize, NULL, 0, NULL);
+  ret = do_cmd (s->connection, s->sfd, command, cmdsize, NULL, 0, NULL);
 
   free (command);
 
@@ -3259,8 +3494,8 @@
       hexdump (MSG_IO, "mode_select", s->buffer, 
                i_param_size + mode_selectB.size);
 
-      ret = do_scsi_cmd (s->sfd, s->buffer,
-                         i_param_size + mode_selectB.size, NULL, 0, NULL);
+      ret = do_cmd (s->connection, s->sfd, s->buffer,
+                    i_param_size + mode_selectB.size, NULL, 0, NULL);
 
     }
 
@@ -3286,30 +3521,40 @@
 
   if (s->model == MODEL_FI) /*...and others */
     {
+	/* With the fi-series scanners, we have to use a 10-byte header
+	 * instead of a 4-byte header when communicating via USB.  This
+	 * may be a firmware bug. */
+      scsiblk *headerB;
+      int xfer_length_pos_adj;
+      if (s->connection == SANE_FUJITSU_USB) {
+	  headerB = &mode_select_usb_headerB;
+	  xfer_length_pos_adj = mode_select_headerB.size-mode_select_usb_headerB.size;
+      } else {
+	  headerB = &mode_select_headerB;
+	  xfer_length_pos_adj = 0;
+      }
       memcpy (s->buffer, mode_selectB.cmd, mode_selectB.size);
-      memcpy (s->buffer + mode_selectB.size, mode_select_headerB.cmd,
-              mode_select_headerB.size);
-      memcpy (s->buffer + mode_selectB.size + mode_select_headerB.size,
+      memcpy (s->buffer + mode_selectB.size, headerB->cmd,
+              headerB->size);
+      memcpy (s->buffer + mode_selectB.size + headerB->size,
               mode_select_parameter_blockB.cmd,
               mode_select_parameter_blockB.size);
   
-  
-  
-      command = s->buffer + mode_selectB.size + mode_select_headerB.size;
+      command = s->buffer + mode_selectB.size + headerB->size;
       i_cmd_size = 6;
       set_MSEL_len (command, i_cmd_size);
 
       set_MSEL_pagecode (command, MSEL_sleep);
       set_MSEL_sleep_mode(command, s->sleep_time);
   
-      i_param_size = mode_select_headerB.size + i_cmd_size + 2;
-      set_MSEL_xfer_length (s->buffer, i_param_size);
+      i_param_size = headerB->size + i_cmd_size + 2;
+      set_MSEL_xfer_length (s->buffer, i_param_size + xfer_length_pos_adj);
 
       hexdump (MSG_IO, "mode_select", s->buffer, 
                i_param_size + mode_selectB.size);
   
-      ret = do_scsi_cmd (s->sfd, s->buffer,
-                         i_param_size + mode_selectB.size, NULL, 0, NULL);
+      ret = do_cmd (s->connection, s->sfd, s->buffer,
+                    i_param_size + mode_selectB.size, NULL, 0, NULL);
 
       if (!ret)
         {
@@ -3429,8 +3674,8 @@
       set_R_window_id (readB.cmd, i_window_id);
       set_R_xfer_length (readB.cmd, dataToRead);
 
-      status = do_scsi_cmd (s->sfd, readB.cmd, readB.size, myBuffer, 
-                            dataToRead, &data_read);    
+      status = do_cmd (s->connection, s->sfd, readB.cmd, readB.size,
+		       myBuffer, dataToRead, &data_read);    
 
       if (status == SANE_STATUS_EOF)
         {
@@ -4105,22 +4350,34 @@
         {
           int sfd;
 
-          if (sanei_scsi_open (s->devicename, &sfd, senseHandler, 0) != 0)
-            {
-              DBG (5, "imprinter: open failed\n");
-              return SANE_STATUS_INVAL;
-            }
+	  if (s->connection == SANE_FUJITSU_USB) {
+		DBG (10, "imprinter opening USB device\n");
+		if (sanei_usb_open (s->devicename, &sfd) != SANE_STATUS_GOOD) {
+		    DBG (5, "imprinter: open failed\n");
+		    return SANE_STATUS_INVAL;
+		}
+	  } else if (s->connection == SANE_FUJITSU_SCSI) {
+		DBG (10, "imprinter opening SCSI device\n");
+		if (sanei_scsi_open
+			(s->devicename, &sfd, scsiSenseHandler, 0) != 0) {
+		    DBG (5, "imprinter: open failed\n");
+		    return SANE_STATUS_INVAL;
+		}
+	  }
       
-          fujitsu_wait_scanner(sfd);
-          ret = do_scsi_cmd (sfd, s->buffer,
-                             imprinterB.size + i_size,
-                             NULL, 0, NULL);
-          sanei_scsi_close (sfd);
+          fujitsu_wait_scanner(s->connection, sfd);
+          ret = do_cmd (s->connection, sfd, s->buffer, imprinterB.size + i_size,
+                        NULL, 0, NULL);
+	  if (s->connection == SANE_FUJITSU_USB) {
+		sanei_usb_close (sfd);
+	  } else if (s->connection == SANE_FUJITSU_SCSI) {
+		sanei_scsi_close (sfd);
+	  }
         } 
       else 
         {
-          ret = do_scsi_cmd (s->sfd, s->buffer,
-                             imprinterB.size + i_size,
+          ret = do_cmd (s->connection, s->sfd, s->buffer,
+                        imprinterB.size + i_size,
                              NULL, 0, NULL);
         }
 
@@ -4191,9 +4448,9 @@
       hexdump (MSG_IO, "send", s->buffer, 
                i_strlen +  send_imprinterB.size + sendB.size);
 
-      ret = do_scsi_cmd (s->sfd, s->buffer,
-                         i_strlen +  send_imprinterB.size + sendB.size,
-                         NULL, 0, NULL);
+      ret = do_cmd (s->connection, s->sfd, s->buffer,
+                    i_strlen +  send_imprinterB.size + sendB.size,
+                    NULL, 0, NULL);
 
     }
 
@@ -4219,6 +4476,7 @@
   unsigned char buffer[max_WDB_size];
   int ret;
   int scsiCommandLength = 0;
+  scsiblk *setwinB;
 
   /* the amout of window data we're going to send. may have to be reduced 
    * depending on scanner model. */
@@ -4352,18 +4610,24 @@
    * follows without a header of its own. 
    */
 
+    if ( (s->model == MODEL_FI) && (s->connection == SANE_FUJITSU_USB) ) {
+	setwinB = &set_usb_windowB;
+    } else {
+	setwinB = &set_windowB;
+    }
+
   /* SET WINDOW command and command descriptor block. 
    * The transfer length will be filled in later using set_SW_xferlen.
    */
-  memcpy (s->buffer, set_windowB.cmd, set_windowB.size);
+  memcpy (s->buffer, setwinB->cmd, setwinB->size);
 
   /* header for first set of window data */
-  memcpy ((s->buffer + set_windowB.size),
+  memcpy ((s->buffer + setwinB->size),
           window_parameter_data_blockB.cmd,
           window_parameter_data_blockB.size);
 
   /* set window data size */
-  set_WPDB_wdblen ((s->buffer + set_windowB.size), windowDataSize);
+  set_WPDB_wdblen ((s->buffer + setwinB->size), windowDataSize);
 
   if (s->duplex_mode == DUPLEX_BACK)
     {
@@ -4375,7 +4639,7 @@
     }
 
   /* now copy window data itself */
-  memcpy (s->buffer + set_windowB.size + window_parameter_data_blockB.size,
+  memcpy (s->buffer + setwinB->size + window_parameter_data_blockB.size,
           buffer, windowDataSize);
 
   /* when in duplex mode, add a second window */
@@ -4394,7 +4658,7 @@
           set_WD_paper_length_Y (buffer, 0);
         }
       hexdump (MSG_IO, "Window set - back", buffer, windowDataSize);
-      memcpy (s->buffer + set_windowB.size +
+      memcpy (s->buffer + setwinB->size +
               window_parameter_data_blockB.size + windowDataSize, buffer,
               windowDataSize);
       set_SW_xferlen (s->buffer,
@@ -4402,7 +4666,7 @@
                        2 * windowDataSize));
       scsiCommandLength =
         window_parameter_data_blockB.size + 2 * windowDataSize +
-        set_windowB.size;
+        setwinB->size;
     }
   else
     {
@@ -4410,10 +4674,11 @@
       set_SW_xferlen (s->buffer, (window_parameter_data_blockB.size +
                                   windowDataSize));
       scsiCommandLength =
-        window_parameter_data_blockB.size + windowDataSize + set_windowB.size;
+        window_parameter_data_blockB.size + windowDataSize + setwinB->size;
     }
 
-  ret = do_scsi_cmd (s->sfd, s->buffer, scsiCommandLength, NULL, 0, NULL);
+  ret = do_cmd (s->connection, s->sfd, s->buffer, scsiCommandLength,
+		NULL, 0, NULL);
   if (ret)
     return ret;
   DBG (10, "set_window_param: ok\n");
@@ -6427,7 +6692,7 @@
   scanner->val[OPT_TL_X].w = 0;
   scanner->val[OPT_TL_Y].w = 0;
   /* this size is just big enough to match letter and A4 formats */
-  scanner->bottom_right_x = SANE_FIX (215.9);
+  scanner->bottom_right_x = SANE_FIX (215.0);
   scanner->bottom_right_y = SANE_FIX (297.0);
   scanner->page_width = FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_x);
   scanner->page_height = FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_y);
@@ -6453,6 +6718,8 @@
 
   scanner->opt[OPT_DROPOUT_COLOR].cap = SANE_CAP_INACTIVE;
   scanner->dropout_color = MSEL_dropout_DEFAULT;
+  if ( strstr (scanner->productName, "4220" ))
+    scanner->gamma = 0x80;
 
   scanner->sleep_time = 15;
   scanner->use_imprinter = SANE_FALSE;
--- backend/fujitsu.h.orig	2002-09-16 05:19:54.000000000 -0700
+++ backend/fujitsu.h	2003-02-12 14:00:17.000000000 -0800
@@ -102,6 +102,13 @@
 };
 
 
+typedef enum {				/* hardware connection to the scanner */
+        SANE_FUJITSU_NODEV,		/* default, no HW specified yet */
+	SANE_FUJITSU_SCSI,		/* SCSI interface */
+	SANE_FUJITSU_PIO,		/* parallel interface */
+	SANE_FUJITSU_USB		/* USB interface */
+} Fujitsu_Connection_Type;
+
 struct fujitsu
 {
   struct fujitsu *next;
@@ -121,6 +128,9 @@
   int model;			/* The scanner model.                        */
 
   char *devicename;		/* The name of the scanner device.           */
+
+  Fujitsu_Connection_Type connection;	/* hardware interface type */
+
   int sfd;			/* The scanner device file descriptor.       */
 
   int color_raster_offset;	/* offset between r and b scan line and    */
@@ -466,16 +476,26 @@
 static SANE_Status attachScanner (const char *devicename,
 				  struct fujitsu **devp);
 
-static SANE_Status senseHandler (int scsi_fd, u_char * result, void *arg);
+static SANE_Status scsiSenseHandler (int scsi_fd, u_char * result, void *arg);
 
 static int identifyScanner (struct fujitsu *s);
 
 static void doInquiry (struct fujitsu *s);
 
+static int
+do_cmd (Fujitsu_Connection_Type connection, int fd, unsigned char *cmd,
+	     int cmd_len, unsigned char *out, size_t req_out_len,
+	     size_t *res_out_len);
+
 static int do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len,
 			unsigned char *out, size_t req_out_len, 
 			size_t *res_out_len);
 
+static int
+do_usb_cmd (int fd, unsigned char *cmd,
+	     int cmd_len, unsigned char *out, size_t req_out_len,
+	     size_t *res_out_len);
+
 static void hexdump (int level, char *comment, unsigned char *p, int l);
 
 static SANE_Status init_options (struct fujitsu *scanner);
--- backend/fujitsu.conf.orig	2002-04-17 11:50:36.000000000 -0700
+++ backend/fujitsu.conf	2003-02-13 14:57:00.000000000 -0800
@@ -1,3 +1,8 @@
 #option force-model fi-4340Cdi
 #/dev/sg1
 scsi FUJITSU
+# For Fujitsu scanners connected via USB on a known device:
+# usb /dev/usb/scanner0
+# For Fujitsu scanners connected via USB where vendor/product Id are known
+# (use lsusb to find these):
+# usb 0x04c5 0x1042
--- doc/descriptions/fujitsu.desc.orig	2002-09-23 03:16:54.000000000 -0700
+++ doc/descriptions/fujitsu.desc	2003-02-13 15:04:51.000000000 -0800
@@ -37,6 +37,8 @@
 :interface "SCSI"
 :model "M4097"
 :interface "SCSI"
+:model "fi-4220C"
+:interface "SCSI USB"
 :model "fi-4340C"
-:interface "SCSI"
+:interface "SCSI USB"
 
--- doc/sane-fujitsu.man.orig	2002-11-29 12:15:30.000000000 -0800
+++ doc/sane-fujitsu.man	2003-02-13 15:13:15.000000000 -0800
@@ -4,13 +4,13 @@
 .IX sane-m3096g
 
 .SH NAME
-sane-fujitsu \- SANE backend for Fujitsu flatbed scanners
+sane-fujitsu \- SANE backend for Fujitsu flatbed and ADF scanners
 
 .SH DESCRIPTION
 The
 .B sane-fujitsu
 library implements a SANE (Scanner Access Now Easy) backend which
-provides access to Fujitsu flatbed scanners.
+provides access to Fujitsu flatbed and ADF scanners.
 At present, the following
 scanners are known to work with this backend:
 .PP
@@ -27,6 +27,8 @@
 .br
 FUJITSU  M4097
 .br
+FUJITSU  fi-4220C           
+.br
 FUJITSU  fi-4340C           
 .br
 FUJITSU  M3091DCd         BF21
@@ -157,6 +159,7 @@
 sane(7),
 sane\-scsi(5),
 sane\-sp15c(5)
+sane\-usb(5),
 .br
 Fujitsu ScanPartner 15C OEM Manual, Doc. No. 250-0081-0
 .br
@@ -172,6 +175,7 @@
 3091 driver: Frederik Ramm <frederik@remote.org>
 3093GD,fi-4340C, ipc and cmp options: Oliver Schirrmeister <oschirr@abm.de>
 3092 patch: Mario Goppold <mgoppold@tbzpariv.tcc-chemnitz.de>
+fi-4220C patch and USB support: Ronald B. Cemer <ron@roncemer.com>
 .SH LIMITATIONS
 Only tested with Linux 2.4 
 
@@ -180,4 +184,4 @@
 but I haven't seen them yet.
 I don't know if the ScanPartner 15C still works, because I'm not able
 to test it.
-3091/3092 don't support halftone
\ No newline at end of file
+3091/3092 don't support halftone

--------------030703000607030909030808--