From 44916ca6d75e0b5f258a098a50d659f31c6625fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= <debian@jff.email>
Date: Sun, 3 Mar 2024 09:54:51 +0100
Subject: New upstream version 1.3.0

---
 backend/Makefile.am              |   24 +-
 backend/avision.c                |  367 +++++++----
 backend/avision.h                |   70 ++-
 backend/canon_dr.c               |   74 ++-
 backend/dmc.c                    |    2 +-
 backend/dmc.h                    |    2 +-
 backend/epson2_usb.c             |    1 +
 backend/epsonds.c                |    6 +
 backend/escl/escl.c              |   14 +-
 backend/escl/escl.h              |    2 +-
 backend/escl/escl_capabilities.c |   11 +-
 backend/escl/escl_jpeg.c         |   14 +-
 backend/escl/escl_newjob.c       |   22 +-
 backend/fujitsu.c                |   14 +-
 backend/fujitsu.conf.in          |   13 +-
 backend/genesys.conf.in          |    6 +
 backend/hpljm1005.c              |  414 ++++++------
 backend/lexmark.c                |    2 +-
 backend/lexmark_x2600.c          | 1287 ++++++++++++++++++++++++++++++++++++++
 backend/lexmark_x2600.conf.in    |    2 +
 backend/lexmark_x2600.h          |  151 +++++
 backend/net.c                    |   31 +-
 backend/pixma/pixma.c            |   48 +-
 backend/pixma/pixma_imageclass.c |    2 +
 backend/pixma/pixma_io_sanei.c   |    7 -
 backend/pixma/pixma_mp150.c      |   50 ++
 backend/snapscan-options.c       |   14 +
 backend/test.c                   |   63 +-
 backend/xerox_mfp-usb.c          |   23 +-
 backend/xerox_mfp.c              |   60 +-
 backend/xerox_mfp.conf.in        |    6 +
 31 files changed, 2346 insertions(+), 456 deletions(-)
 create mode 100644 backend/lexmark_x2600.c
 create mode 100644 backend/lexmark_x2600.conf.in
 create mode 100644 backend/lexmark_x2600.h

(limited to 'backend')

diff --git a/backend/Makefile.am b/backend/Makefile.am
index 35ad206..86b1d1f 100644
--- a/backend/Makefile.am
+++ b/backend/Makefile.am
@@ -75,8 +75,8 @@ BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \
 	       epson.conf epsonds.conf escl.conf fujitsu.conf genesys.conf \
 	       gphoto2.conf gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
 	       hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\
-	       kvs1025.conf \
-	       leo.conf lexmark.conf ma1509.conf magicolor.conf \
+	       kvs1025.conf leo.conf lexmark.conf lexmark_x2600.conf \
+               ma1509.conf magicolor.conf \
 	       matsushita.conf microtek2.conf microtek.conf mustek.conf \
 	       mustek_pp.conf mustek_usb.conf nec.conf net.conf \
 	       p5.conf \
@@ -173,7 +173,7 @@ be_convenience_libs = libabaton.la libagfafocus.la \
     libhp5400.la libhp5590.la libhpljm1005.la \
     libhpsj5s.la libhs2p.la libibm.la libkodak.la libkodakaio.la\
     libkvs1025.la libkvs20xx.la libkvs40xx.la \
-    libleo.la liblexmark.la libma1509.la libmagicolor.la \
+    libleo.la liblexmark.la liblexmark_x2600.la libma1509.la libmagicolor.la \
     libmatsushita.la libmicrotek.la libmicrotek2.la \
     libmustek.la libmustek_pp.la libmustek_usb.la \
     libmustek_usb2.la libnec.la libnet.la \
@@ -208,7 +208,7 @@ be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
     libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\
     libsane-kvs1025.la libsane-kvs20xx.la libsane-kvs40xx.la \
     libsane-leo.la \
-    libsane-lexmark.la libsane-ma1509.la libsane-magicolor.la \
+    libsane-lexmark.la libsane-lexmark_x2600.la libsane-ma1509.la libsane-magicolor.la \
     libsane-matsushita.la libsane-microtek.la libsane-microtek2.la \
     libsane-mustek.la libsane-mustek_pp.la libsane-mustek_usb.la \
     libsane-mustek_usb2.la libsane-nec.la libsane-net.la \
@@ -1147,6 +1147,22 @@ EXTRA_DIST += lexmark.conf.in
 # TODO: Why are these distributed but not compiled?
 EXTRA_DIST += lexmark_models.c lexmark_sensors.c
 
+liblexmark_x2600_la_SOURCES = lexmark_x2600.c lexmark_x2600.h
+liblexmark_x2600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark_x2600
+
+nodist_libsane_lexmark_x2600_la_SOURCES = lexmark_x2600-s.c
+libsane_lexmark_x2600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark_x2600
+libsane_lexmark_x2600_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
+libsane_lexmark_x2600_la_LIBADD = $(COMMON_LIBS) \
+    liblexmark_x2600.la \
+    ../sanei/sanei_init_debug.lo \
+    ../sanei/sanei_constrain_value.lo \
+    ../sanei/sanei_config.lo \
+    sane_strstatus.lo \
+    ../sanei/sanei_usb.lo \
+    $(USB_LIBS) $(RESMGR_LIBS)
+EXTRA_DIST += lexmark_x2600.conf.in
+
 libma1509_la_SOURCES = ma1509.c ma1509.h
 libma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509
 
diff --git a/backend/avision.c b/backend/avision.c
index c807d9f..a681ed2 100644
--- a/backend/avision.c
+++ b/backend/avision.c
@@ -3769,7 +3769,7 @@ static SANE_Status
 get_accessories_info (Avision_Scanner* s)
 {
   Avision_Device* dev = s->hw;
-  int try = 3;
+  int try = 1;
 
   /* read stuff */
   struct command_read rcmd;
@@ -3811,47 +3811,84 @@ get_accessories_info (Avision_Scanner* s)
        result [2],
        adf_model[ (result[2] < adf_models) ? result[2] : adf_models ]);
 
-  dev->inquiry_adf |= result [0];
-
+  /*
+   * Cope with ADF presence flag being present but the device *not* reporting
+   * ADF capability. Maybe there are some devices that do that? [RL]
+   *
+   */
+  dev->inquiry_adf_present = result [0];
+
+  /*
+   * Note: this feature_type check is a bit of a hack.
+   * Only the HP Scanjet 8200 series supports this so it is code
+   * specific to this family of scanners. [RL]
+   *
+   */
   if (dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX)
-  {
-    if (result[0] == 1)
     {
-      dev->inquiry_duplex = 1;
-      dev->inquiry_duplex_interlaced = 0;
-    } else if (result[0] == 0 && result[2] != 0 && !skip_adf) {
-      /* Sometimes the scanner will report that there is no ADF attached, yet
-       * an ADF model number will still be reported.  This happens on the
-       * HP8200 series and possibly others.  In this case we need to reset the
-       * the adf and try reading it again.  Skip this if the configuration says
-       * to do so, so that we don't fail out the scanner as being broken and
-       * unsupported if there isn't actually an ADF present.
-       */
-      DBG (3, "get_accessories_info: Found ADF model number but the ADF-present flag is not set. Trying to recover...\n");
-      status = adf_reset (s);
-      if (status != SANE_STATUS_GOOD) {
-        DBG (3, "get_accessories_info: Failed to reset ADF: %s\n", sane_strstatus (status));
-        return status;
-      }
-      DBG (1, "get_accessories_info: Waiting while ADF firmware resets...\n");
-      sleep(3);
-      status = wait_ready (&s->av_con, 1);
-      if (status != SANE_STATUS_GOOD) {
-        DBG (1, "get_accessories_info: wait_ready() failed: %s\n", sane_strstatus (status));
-        return status;
-      }
-      if (try) {
-        try--;
-        goto RETRY;
-      }
-      DBG (1, "get_accessories_info: Maximum retries attempted, ADF unresponsive.\n");
-      return SANE_STATUS_UNSUPPORTED;
+      if (result[0] == 1)
+        {
+          dev->inquiry_duplex = 1;
+          dev->inquiry_duplex_interlaced = 0;
+        }
+      else if (result[0] == 0 && result[2] != 0 && !skip_adf)
+        {
+          /* Sometimes the scanner will report that there is no ADF attached, yet
+           * an ADF model number will still be reported.  This happens on the
+           * HP8200 series and possibly others.  In this case we need to reset the
+           * the adf and try reading it again.  Skip this if the configuration says
+           * to do so, so that we don't fail out the scanner as being broken and
+           * unsupported if there isn't actually an ADF present.
+           *
+           * Note further: Some models (like the ScanJet 8300) report that they have ADF
+           * *capability* in the INQUIRY response but that doesn't necessarily mean that
+           * an ADF is plugged in. In my case it has the lightbox accessory instead and
+           * result[0] == FALSE.
+           * Trying to reset the ADF 3 times is excessive and takes an unreasonable amount
+           * of time on the 8300 with no ADF plugged in, so let's do it just once and if
+           * it fails to report presence, then don't assume it is an error, just that
+           * there is no ADF. [RL]
+           *
+           */
+          if (!try)
+            {
+              DBG (
+                  1,
+                  "get_accessories_info: Maximum retries attempted, ADF unresponsive.\n");
+              dev->inquiry_adf_present = SANE_FALSE;
+              //return SANE_STATUS_UNSUPPORTED;
+            }
+          else
+            {
+              try--;
+
+              DBG(3,
+                  "get_accessories_info: Found ADF model number but the ADF-present flag is not set. "
+                  "Trying to reset the ADF just in case it is there but unresponsive...\n");
+              status = adf_reset (s);
+              if (status != SANE_STATUS_GOOD)
+                {
+                  DBG (3, "get_accessories_info: Failed to reset ADF: %s\n", sane_strstatus (status));
+                  return status;
+                }
+
+              DBG(1,"get_accessories_info: Waiting while ADF firmware resets...\n");
+              sleep (3);
+              status = wait_ready (&s->av_con, 1);
+              if (status != SANE_STATUS_GOOD)
+                {
+                  DBG (1, "get_accessories_info: wait_ready() failed: %s\n",
+                       sane_strstatus (status));
+                  return status;
+                }
+              goto RETRY;
+            }
+        }
     }
-  }
 
   /* only honor a 1, some scanner without adapter set 0xff */
   if (result[1] == 1)
-    dev->inquiry_light_box = 1;
+    dev->inquiry_light_box_present = 1;
 
   return SANE_STATUS_GOOD;
 }
@@ -4336,41 +4373,82 @@ attach (SANE_String_Const devname, Avision_ConnectionType con_type,
 
   model_num = 0;
   found = 0;
-  /* while not at at end of list NULL terminator */
-  while (Avision_Device_List[model_num].real_mfg != NULL ||
-         Avision_Device_List[model_num].scsi_mfg != NULL)
-  {
-    int matches = 0, match_count = 0; /* count number of matches */
-    DBG (1, "attach: Checking model: %d\n", model_num);
 
-    if (Avision_Device_List[model_num].scsi_mfg) {
-      ++match_count;
-      if (strcmp(mfg, Avision_Device_List[model_num].scsi_mfg) == 0)
-        ++matches;
-    }
-    if (Avision_Device_List[model_num].scsi_model) {
-      ++match_count;
-      if (strcmp(model, Avision_Device_List[model_num].scsi_model) == 0)
-        ++matches;
+  /*
+   * Search for a matching device in the device list.
+   * Primarily we need two matches for SCSI devices.
+   * However, multiple USB device entries share the same
+   * SCSI info. For USB devices, we will also do a mandatory
+   * USB Product/Vendor check to pick the right one. Otherwise
+   * at the very least the device name is incorrect.
+   *
+   */
+  SANE_Word usb_vendor = 0;
+  SANE_Word usb_product = 0;
+
+  if (con_type == AV_USB)
+    {
+      status = sanei_usb_get_vendor_product_byname (devname, &usb_vendor, &usb_product);
+      if (status != SANE_STATUS_GOOD)
+        {
+          DBG (0, "attach: Could not retrieve USB vendor nor product for USB device.\n");
+          status = SANE_STATUS_INVAL;
+          goto close_scanner_and_return;
+        }
     }
 
-    /* we need 2 matches (mfg, model) for SCSI entries, or the ones available
-       for "we know what we are looking for" USB entries */
-    if ((attaching_hw == &(Avision_Device_List [model_num]) &&
-         matches == match_count) ||
-	matches == 2)
+  /* while not at at end of list NULL terminator */
+  while (Avision_Device_List[model_num].real_mfg != NULL
+      || Avision_Device_List[model_num].scsi_mfg != NULL)
     {
-      DBG (1, "attach: Scanner matched entry: %d: \"%s\", \"%s\", 0x%.4x, 0x%.4x\n",
-           model_num,
-	   Avision_Device_List[model_num].scsi_mfg,
-	   Avision_Device_List[model_num].scsi_model,
-	   Avision_Device_List[model_num].usb_vendor,
-	   Avision_Device_List[model_num].usb_product);
-      found = 1;
-      break;
+      int matches = 0, match_count = 0; /* count number of matches */
+      DBG (1, "attach: Checking model: %d\n", model_num);
+
+      if (Avision_Device_List[model_num].scsi_mfg)
+        {
+          ++match_count;
+          if (strcmp (mfg, Avision_Device_List[model_num].scsi_mfg) == 0)
+            ++matches;
+        }
+      if (Avision_Device_List[model_num].scsi_model)
+        {
+          ++match_count;
+          if (strcmp (model, Avision_Device_List[model_num].scsi_model) == 0)
+            ++matches;
+        }
+
+      /*
+       * Must match on USB vendor product also for USB devices.
+       * We will *always* know the vendor and product for USB devices.
+       *
+       */
+      if (con_type == AV_USB)
+        {
+          ++match_count;
+          if ((Avision_Device_List[model_num].usb_product == usb_product)
+              && (Avision_Device_List[model_num].usb_vendor == usb_vendor))
+            {
+              ++matches;
+            }
+        }
+
+      /* we need 2 matches (mfg, model) for SCSI entries, or the ones available
+       for "we know what we are looking for" USB entries */
+      if ((attaching_hw == &(Avision_Device_List[model_num]))
+          && (matches == match_count))
+        {
+          DBG (
+              1,
+              "attach: Scanner matched entry: %d: \"%s\", \"%s\", 0x%.4x, 0x%.4x\n",
+              model_num, Avision_Device_List[model_num].scsi_mfg,
+              Avision_Device_List[model_num].scsi_model,
+              Avision_Device_List[model_num].usb_vendor,
+              Avision_Device_List[model_num].usb_product);
+          found = 1;
+          break;
+        }
+      ++model_num;
     }
-    ++model_num;
-  }
 
   if (!found) {
     DBG (0, "attach: \"%s\" - \"%s\" not yet in whitelist!\n", mfg, model);
@@ -4543,7 +4621,7 @@ get_double ( &(result[48] ) ));
   DBG (3, "attach: [62]    scanner type:%s%s%s%s%s%s\n",
        BIT(result[62],7)?" Flatbed":"",
        BIT(result[62],6)?" Roller (ADF)":"",
-       BIT(result[62],5)?" Flatbed (ADF)":"",
+       BIT(result[62],5)?" Flatbed (ADF/Lightbox)":"",
        BIT(result[62],4)?" Roller":"", /* does not feed multiple pages, AV25 */
        BIT(result[62],3)?" Film scanner":"",
        BIT(result[62],2)?" Duplex":"");
@@ -4643,7 +4721,7 @@ get_double ( &(result[48] ) ));
   dev->inquiry_nvram_read = BIT(result[52],0);
   dev->inquiry_power_save_time = BIT(result[52],1);
 
-  dev->inquiry_adf = BIT (result[62], 5);
+  dev->inquiry_adf_capability = BIT (result[62], 5);
   dev->inquiry_duplex = BIT (result[62], 2) || BIT (result[94], 5);
   dev->inquiry_duplex_interlaced = BIT(result[62],2) || BIT (result[94], 4);
   /* the first avision scanners (AV3200) do not set the interlaced bit */
@@ -5147,10 +5225,10 @@ additional_probe (Avision_Scanner* s)
     {
       add_source_mode (dev, AV_NORMAL, "Normal");
 
-      if (dev->inquiry_light_box)
+      if (dev->inquiry_light_box_present)
 	add_source_mode (dev, AV_TRANSPARENT, "Transparency");
 
-      if (dev->inquiry_adf)
+      if (dev->inquiry_adf_present)
         add_source_mode (dev, AV_ADF, "ADF Front");
     }
 
@@ -6956,10 +7034,18 @@ init_options (Avision_Scanner* s)
   memset (s->opt, 0, sizeof (s->opt));
   memset (s->val, 0, sizeof (s->val));
 
-  for (i = 0; i < NUM_OPTIONS; ++ i) {
-    s->opt[i].size = sizeof (SANE_Word);
-    s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
-  }
+  /*
+   * Set defaults for all the options.
+   *
+   */
+  for (i = 0; i < NUM_OPTIONS; ++i)
+    {
+      s->opt[i].name = "";
+      s->opt[i].desc = "";
+      s->opt[i].unit = SANE_UNIT_NONE;
+      s->opt[i].size = sizeof(SANE_Word);
+      s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+    }
 
   /* Init the SANE option from the scanner inquiry data */
 
@@ -6989,9 +7075,7 @@ init_options (Avision_Scanner* s)
   dev->speed_range.max = (SANE_Int)4;
   dev->speed_range.quant = (SANE_Int)1;
 
-  s->opt[OPT_NUM_OPTS].name = "";
   s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
-  s->opt[OPT_NUM_OPTS].desc = "";
   s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
   s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
   s->opt[OPT_NUM_OPTS].size = sizeof(SANE_TYPE_INT);
@@ -6999,7 +7083,6 @@ init_options (Avision_Scanner* s)
 
   /* "Mode" group: */
   s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE;
-  s->opt[OPT_MODE_GROUP].desc = ""; /* for groups only title and type are valid */
   s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
   s->opt[OPT_MODE_GROUP].cap = 0;
   s->opt[OPT_MODE_GROUP].size = 0;
@@ -7063,7 +7146,6 @@ init_options (Avision_Scanner* s)
 
   /* "Geometry" group: */
   s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
-  s->opt[OPT_GEOMETRY_GROUP].desc = ""; /* for groups only title and type are valid */
   s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
   s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
   s->opt[OPT_GEOMETRY_GROUP].size = 0;
@@ -7111,8 +7193,8 @@ init_options (Avision_Scanner* s)
 
   /* overscan top */
   s->opt[OPT_OVERSCAN_TOP].name = "overscan-top";
-  s->opt[OPT_OVERSCAN_TOP].title = "Overscan top";
-  s->opt[OPT_OVERSCAN_TOP].desc = "The top overscan controls the additional area to scan before the paper is detected.";
+  s->opt[OPT_OVERSCAN_TOP].title = SANE_TITLE_OVERSCAN_TOP;
+  s->opt[OPT_OVERSCAN_TOP].desc = SANE_DESC_OVERSCAN_TOP;
   s->opt[OPT_OVERSCAN_TOP].type = SANE_TYPE_FIXED;
   s->opt[OPT_OVERSCAN_TOP].unit = SANE_UNIT_MM;
   s->opt[OPT_OVERSCAN_TOP].constraint_type = SANE_CONSTRAINT_RANGE;
@@ -7121,8 +7203,8 @@ init_options (Avision_Scanner* s)
 
   /* overscan bottom */
   s->opt[OPT_OVERSCAN_BOTTOM].name = "overscan-bottom";
-  s->opt[OPT_OVERSCAN_BOTTOM].title = "Overscan bottom";
-  s->opt[OPT_OVERSCAN_BOTTOM].desc = "The bottom overscan controls the additional area to scan after the paper end is detected.";
+  s->opt[OPT_OVERSCAN_BOTTOM].title = SANE_TITLE_OVERSCAN_BOTTOM;
+  s->opt[OPT_OVERSCAN_BOTTOM].desc = SANE_DESC_OVERSCAN_BOTTOM;
   s->opt[OPT_OVERSCAN_BOTTOM].type = SANE_TYPE_FIXED;
   s->opt[OPT_OVERSCAN_BOTTOM].unit = SANE_UNIT_MM;
   s->opt[OPT_OVERSCAN_BOTTOM].constraint_type = SANE_CONSTRAINT_RANGE;
@@ -7136,8 +7218,8 @@ init_options (Avision_Scanner* s)
 
   /* background raster */
   s->opt[OPT_BACKGROUND].name = "background-lines";
-  s->opt[OPT_BACKGROUND].title = "Background raster lines";
-  s->opt[OPT_BACKGROUND].desc = "The background raster controls the additional background lines to scan before the paper is feed through the scanner.";
+  s->opt[OPT_BACKGROUND].title = SANE_TITLE_BACKGROUND_LINES;
+  s->opt[OPT_BACKGROUND].desc = SANE_DESC_BACKGROUND_LINES;
   s->opt[OPT_BACKGROUND].type = SANE_TYPE_INT;
   s->opt[OPT_BACKGROUND].unit = SANE_UNIT_PIXEL;
   s->opt[OPT_BACKGROUND].constraint_type = SANE_CONSTRAINT_RANGE;
@@ -7149,8 +7231,7 @@ init_options (Avision_Scanner* s)
   }
 
   /* "Enhancement" group: */
-  s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
-  s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* for groups only title and type are valid */
+  s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT;
   s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
   s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
   s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
@@ -7182,8 +7263,8 @@ init_options (Avision_Scanner* s)
 
   /* Quality Scan */
   s->opt[OPT_QSCAN].name   = "quality-scan";
-  s->opt[OPT_QSCAN].title  = "Quality scan";
-  s->opt[OPT_QSCAN].desc   = "Turn on quality scanning (slower but better).";
+  s->opt[OPT_QSCAN].title  = SANE_TITLE_QUALITY_SCAN;
+  s->opt[OPT_QSCAN].desc   = SANE_DESC_QUALITY_SCAN;
   s->opt[OPT_QSCAN].type   = SANE_TYPE_BOOL;
   s->opt[OPT_QSCAN].unit   = SANE_UNIT_NONE;
   s->val[OPT_QSCAN].w      = SANE_TRUE;
@@ -7262,8 +7343,8 @@ init_options (Avision_Scanner* s)
 
   /* exposure */
   s->opt[OPT_EXPOSURE].name = "exposure";
-  s->opt[OPT_EXPOSURE].title = "Exposure";
-  s->opt[OPT_EXPOSURE].desc = "Manual exposure adjustment.";
+  s->opt[OPT_EXPOSURE].title = SANE_TITLE_MANUAL_EXPOSURE;
+  s->opt[OPT_EXPOSURE].desc = SANE_DESC_MANUAL_EXPOSURE;
   s->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
   s->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
   s->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
@@ -7276,8 +7357,8 @@ init_options (Avision_Scanner* s)
 
   /* Multi sample */
   s->opt[OPT_MULTISAMPLE].name  = "multi-sample";
-  s->opt[OPT_MULTISAMPLE].title = "Multi-sample";
-  s->opt[OPT_MULTISAMPLE].desc  = "Enable multi-sample scan mode.";
+  s->opt[OPT_MULTISAMPLE].title = SANE_TITLE_MULTI_SAMPLE;
+  s->opt[OPT_MULTISAMPLE].desc  = SANE_DESC_MULTI_SAMPLE;
   s->opt[OPT_MULTISAMPLE].type  = SANE_TYPE_BOOL;
   s->opt[OPT_MULTISAMPLE].unit  = SANE_UNIT_NONE;
   s->val[OPT_MULTISAMPLE].w     = SANE_FALSE;
@@ -7289,9 +7370,9 @@ init_options (Avision_Scanner* s)
   }
 
   /* Infra-red */
-  s->opt[OPT_IR].name  = "infra-red";
-  s->opt[OPT_IR].title = "Infra-red";
-  s->opt[OPT_IR].desc  = "Enable infra-red scan mode.";
+  s->opt[OPT_IR].name  = SANE_NAME_INFRARED;
+  s->opt[OPT_IR].title = SANE_TITLE_INFRARED;
+  s->opt[OPT_IR].desc  = SANE_DESC_INFRARED;
   s->opt[OPT_IR].type  = SANE_TYPE_BOOL;
   s->opt[OPT_IR].unit  = SANE_UNIT_NONE;
   s->val[OPT_IR].w     = SANE_FALSE;
@@ -7303,16 +7384,13 @@ init_options (Avision_Scanner* s)
   }
 
   /* "MISC" group: */
-  s->opt[OPT_MISC_GROUP].title = SANE_TITLE_SCAN_MODE;
-  s->opt[OPT_MISC_GROUP].desc = ""; /* for groups only title and type are valid */
+  s->opt[OPT_MISC_GROUP].title = SANE_TITLE_MISC_GROUP;
   s->opt[OPT_MISC_GROUP].type = SANE_TYPE_GROUP;
   s->opt[OPT_MISC_GROUP].cap = 0;
   s->opt[OPT_MISC_GROUP].size = 0;
   s->opt[OPT_MISC_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 
   /* film holder control */
-  if (dev->scanner_type != AV_FILM)
-    s->opt[OPT_FRAME].cap |= SANE_CAP_INACTIVE;
   s->opt[OPT_FRAME].name = SANE_NAME_FRAME;
   s->opt[OPT_FRAME].title = SANE_TITLE_FRAME;
   s->opt[OPT_FRAME].desc = SANE_DESC_FRAME;
@@ -7321,22 +7399,24 @@ init_options (Avision_Scanner* s)
   s->opt[OPT_FRAME].constraint_type = SANE_CONSTRAINT_RANGE;
   s->opt[OPT_FRAME].constraint.range = &dev->frame_range;
   s->val[OPT_FRAME].w = dev->current_frame;
+  if (dev->scanner_type != AV_FILM)
+    s->opt[OPT_FRAME].cap |= SANE_CAP_INACTIVE;
 
   /* power save time */
-  if (!dev->inquiry_power_save_time)
-    s->opt[OPT_POWER_SAVE_TIME].cap |= SANE_CAP_INACTIVE;
   s->opt[OPT_POWER_SAVE_TIME].name = "power-save-time";
-  s->opt[OPT_POWER_SAVE_TIME].title = "Power save timer control";
-  s->opt[OPT_POWER_SAVE_TIME].desc = "Allows control of the scanner's power save timer, dimming or turning off the light.";
+  s->opt[OPT_POWER_SAVE_TIME].title = SANE_TITLE_POWER_SAVE_TIME;
+  s->opt[OPT_POWER_SAVE_TIME].desc = SANE_DESC_POWER_SAVE_TIME;
   s->opt[OPT_POWER_SAVE_TIME].type = SANE_TYPE_INT;
   s->opt[OPT_POWER_SAVE_TIME].unit = SANE_UNIT_NONE;
   s->opt[OPT_POWER_SAVE_TIME].constraint_type = SANE_CONSTRAINT_NONE;
   s->val[OPT_POWER_SAVE_TIME].w = 0;
+  if (!dev->inquiry_power_save_time)
+    s->opt[OPT_POWER_SAVE_TIME].cap |= SANE_CAP_INACTIVE;
 
   /* message, like options set on the scanner, LED no. & co */
   s->opt[OPT_MESSAGE].name = "message";
-  s->opt[OPT_MESSAGE].title = "message text from the scanner";
-  s->opt[OPT_MESSAGE].desc = "This text contains device specific options controlled by the user on the scanner hardware.";
+  s->opt[OPT_MESSAGE].title = SANE_TITLE_OPTIONS_MSG;
+  s->opt[OPT_MESSAGE].desc = SANE_DESC_OPTIONS_MSG;
   s->opt[OPT_MESSAGE].type = SANE_TYPE_STRING;
   s->opt[OPT_MESSAGE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
   s->opt[OPT_MESSAGE].size = 129;
@@ -7345,44 +7425,73 @@ init_options (Avision_Scanner* s)
   s->val[OPT_MESSAGE].s[0] = 0;
 
   /* NVRAM */
-  s->opt[OPT_NVRAM].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
-  if (!dev->inquiry_nvram_read)
-    s->opt[OPT_NVRAM].cap |= SANE_CAP_INACTIVE;
   s->opt[OPT_NVRAM].name = "nvram-values";
-  s->opt[OPT_NVRAM].title = "Obtain NVRAM values";
-  s->opt[OPT_NVRAM].desc = "Allows access obtaining the scanner's NVRAM values as pretty printed text.";
+  s->opt[OPT_NVRAM].title = SANE_TITLE_NVRAM;
+  s->opt[OPT_NVRAM].desc = SANE_DESC_NVRAM;
   s->opt[OPT_NVRAM].type = SANE_TYPE_STRING;
   s->opt[OPT_NVRAM].unit = SANE_UNIT_NONE;
   s->opt[OPT_NVRAM].size = 1024;
   s->opt[OPT_NVRAM].constraint_type = SANE_CONSTRAINT_NONE;
   s->val[OPT_NVRAM].s = malloc((size_t) s->opt[OPT_NVRAM].size);
   s->val[OPT_NVRAM].s[0] = 0;
+  s->opt[OPT_NVRAM].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+  if (!dev->inquiry_nvram_read)
+    s->opt[OPT_NVRAM].cap |= SANE_CAP_INACTIVE;
 
   /* paper_length */
-  s->opt[OPT_PAPERLEN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
-  if (!dev->inquiry_paper_length)
-    s->opt[OPT_PAPERLEN].cap |= SANE_CAP_INACTIVE;
   s->opt[OPT_PAPERLEN].name  = "paper-length";
-  s->opt[OPT_PAPERLEN].title = "Use paper length";
-  s->opt[OPT_PAPERLEN].desc  = "Newer scanners can utilize this paper length to detect double feeds.  However some others (DM152) can get confused during media flush if it is set.";
+  s->opt[OPT_PAPERLEN].title = SANE_TITLE_PAPER_LENGTH;
+  s->opt[OPT_PAPERLEN].desc  = SANE_DESC_PAPER_LENGTH;
   s->opt[OPT_PAPERLEN].type  = SANE_TYPE_BOOL;
   s->opt[OPT_PAPERLEN].unit  = SANE_UNIT_NONE;
   s->opt[OPT_PAPERLEN].size = sizeof(SANE_Word);
   s->opt[OPT_PAPERLEN].constraint_type = SANE_CONSTRAINT_NONE;
   s->val[OPT_PAPERLEN].w     = SANE_FALSE;
+  s->opt[OPT_PAPERLEN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+  if (!dev->inquiry_paper_length)
+    s->opt[OPT_PAPERLEN].cap |= SANE_CAP_INACTIVE;
 
   /* ADF page flipping */
-  s->opt[OPT_ADF_FLIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
-  if (!(s->hw->hw->feature_type & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX))
-    s->opt[OPT_ADF_FLIP].cap |= SANE_CAP_INACTIVE;
   s->opt[OPT_ADF_FLIP].name = "flip-page";
-  s->opt[OPT_ADF_FLIP].title = "Flip document after duplex scanning";
-  s->opt[OPT_ADF_FLIP].desc = "Tells page-flipping document scanners to flip the paper back to its original orientation before dropping it in the output tray.  Turning this off might make scanning a little faster if you don't care about manually flipping the pages afterwards.";
+  s->opt[OPT_ADF_FLIP].title = SANE_TITLE_FLIP_PAGE;
+  s->opt[OPT_ADF_FLIP].desc = SANE_DESC_FLIP_PAGE;
   s->opt[OPT_ADF_FLIP].type = SANE_TYPE_BOOL;
   s->opt[OPT_ADF_FLIP].unit = SANE_UNIT_NONE;
   s->opt[OPT_ADF_FLIP].size = sizeof(SANE_Word);
   s->opt[OPT_ADF_FLIP].constraint_type = SANE_CONSTRAINT_NONE;
   s->val[OPT_ADF_FLIP].w = SANE_TRUE;
+  s->opt[OPT_ADF_FLIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED;
+  if (!((s->hw->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && (s->source_mode == AV_ADF_DUPLEX)))
+    s->opt[OPT_ADF_FLIP].cap |= SANE_CAP_INACTIVE;
+
+  /* "Options" group: */
+  s->opt[OPT_OPTIONS_GROUP].title = SANE_TITLE_INSTALLED_OPTS_GROUP;
+  s->opt[OPT_OPTIONS_GROUP].type = SANE_TYPE_GROUP;
+  s->opt[OPT_OPTIONS_GROUP].cap = 0;
+  s->opt[OPT_OPTIONS_GROUP].size = 0;
+  s->opt[OPT_OPTIONS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+  /* ADF Installed */
+  s->opt[OPT_OPTION_ADF].name = "adf-installed";
+  s->opt[OPT_OPTION_ADF].title = SANE_TITLE_ADF_INSTALLED;
+  s->opt[OPT_OPTION_ADF].desc = SANE_DESC_ADF_INSTALLED;
+  s->opt[OPT_OPTION_ADF].type = SANE_TYPE_BOOL;
+  s->opt[OPT_OPTION_ADF].unit = SANE_UNIT_NONE;
+  s->opt[OPT_OPTION_ADF].size = sizeof(SANE_Word);
+  s->opt[OPT_OPTION_ADF].constraint_type = SANE_CONSTRAINT_NONE;
+  s->val[OPT_OPTION_ADF].w = dev->inquiry_adf_present;
+  s->opt[OPT_OPTION_ADF].cap =  SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+
+  /* Lightbox Installed */
+  s->opt[OPT_OPTION_LIGHTBOX].name = "lightbox-installed";
+  s->opt[OPT_OPTION_LIGHTBOX].title = SANE_TITLE_LIGHTBOX_INSTALLED;
+  s->opt[OPT_OPTION_LIGHTBOX].desc = SANE_DESC_LIGHTBOX_INSTALLED;
+  s->opt[OPT_OPTION_LIGHTBOX].type = SANE_TYPE_BOOL;
+  s->opt[OPT_OPTION_LIGHTBOX].unit = SANE_UNIT_NONE;
+  s->opt[OPT_OPTION_LIGHTBOX].size = sizeof(SANE_Word);
+  s->opt[OPT_OPTION_LIGHTBOX].constraint_type = SANE_CONSTRAINT_NONE;
+  s->val[OPT_OPTION_LIGHTBOX].w = dev->inquiry_light_box_present;
+  s->opt[OPT_OPTION_LIGHTBOX].cap =  SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
 
   return SANE_STATUS_GOOD;
 }
@@ -7411,7 +7520,6 @@ reader_process (void *data)
   sigset_t sigterm_set;
   sigset_t ignore_set;
   struct SIGACTION act;
-  int old;
 
   FILE* fp;
   FILE* fp_fd = 0; /* for ADF bottom offset truncating */
@@ -7691,7 +7799,10 @@ reader_process (void *data)
 	    sigprocmask (SIG_BLOCK, &sigterm_set, 0);
 #ifdef USE_PTHREAD
 	  else
-	    pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old);
+	    {
+	      int old;
+	      pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old);
+	    }
 #endif
 
 	  status = read_data (s, stripe_data + stripe_fill, &this_read);
@@ -7700,10 +7811,12 @@ reader_process (void *data)
 	    sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
 #ifdef USE_PTHREAD
 	  else
-	    pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &old);
+	    {
+	      int old;
+	      pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &old);
+	    }
 #endif
 
-
 	  /* only EOF on the second stripe, as otherwise the rear page
 	     is shorter */
 	  if (status == SANE_STATUS_EOF && deinterlace == STRIPE) {
@@ -8908,6 +9021,12 @@ sane_control_option (SANE_Handle handle, SANE_Int option,
 	  strcpy (val, s->val[option].s);
 	  return SANE_STATUS_GOOD;
 
+	  /* Boolean options. */
+        case OPT_OPTION_ADF:
+        case OPT_OPTION_LIGHTBOX:
+          *(SANE_Bool*) val = s->val[option].b;
+          return SANE_STATUS_GOOD;
+
 	} /* end switch option */
     } /* end if GET_ACTION_GET_VALUE */
   else if (action == SANE_ACTION_SET_VALUE)
diff --git a/backend/avision.h b/backend/avision.h
index d2d84ef..cf3170f 100644
--- a/backend/avision.h
+++ b/backend/avision.h
@@ -66,6 +66,65 @@ typedef enum Avision_ConnectionType {
   AV_USB
 } Avision_ConnectionType;
 
+/*
+ * Translatable custom options text.
+ *
+ */
+#define SANE_TITLE_MISC_GROUP             SANE_I18N("Miscellaneous")
+#define SANE_TITLE_INSTALLED_OPTS_GROUP   SANE_I18N("Installed options")
+
+#define SANE_TITLE_OVERSCAN_TOP           SANE_I18N("Overscan top")
+#define SANE_TITLE_OVERSCAN_BOTTOM        SANE_I18N("Overscan bottom")
+#define SANE_TITLE_BACKGROUND_LINES       SANE_I18N("Background raster lines")
+#define SANE_TITLE_QUALITY_SCAN           SANE_I18N("Quality scan")
+#define SANE_TITLE_MANUAL_EXPOSURE        SANE_I18N("Exposure")
+#define SANE_TITLE_MULTI_SAMPLE           SANE_I18N("Multi-sample")
+#define SANE_TITLE_POWER_SAVE_TIME        SANE_I18N("Power save timer control")
+#define SANE_TITLE_OPTIONS_MSG            SANE_I18N("Message text from the scanner")
+#define SANE_TITLE_NVRAM                  SANE_I18N("Obtain NVRAM values")
+#define SANE_TITLE_PAPER_LENGTH           SANE_I18N("Use paper length")
+#define SANE_TITLE_FLIP_PAGE              SANE_I18N("Flip document after duplex scanning")
+#define SANE_TITLE_ADF_INSTALLED          SANE_I18N("ADF installed")
+#define SANE_TITLE_LIGHTBOX_INSTALLED     SANE_I18N("Lightbox installed")
+
+#define SANE_DESC_OVERSCAN_TOP            \
+SANE_I18N("The top overscan controls the additional area to scan before the "\
+          "paper is detected.")
+#define SANE_DESC_OVERSCAN_BOTTOM         \
+SANE_I18N("The bottom overscan controls the additional area to scan after "\
+          "the paper end is detected.")
+#define SANE_DESC_BACKGROUND_LINES        \
+SANE_I18N("The background raster controls the additional background lines to "\
+          "scan before the paper is feed through the scanner.")
+#define SANE_DESC_QUALITY_SCAN            \
+SANE_I18N("Turn on quality scanning (slower but better).")
+#define SANE_DESC_MANUAL_EXPOSURE         \
+SANE_I18N("Manual exposure adjustment.")
+#define SANE_DESC_MULTI_SAMPLE            \
+SANE_I18N("Enable multi-sample scan mode.")
+#define SANE_DESC_POWER_SAVE_TIME         \
+SANE_I18N("Allows control of the scanner's power save timer, dimming or "\
+          "turning off the light.")
+#define SANE_DESC_OPTIONS_MSG             \
+SANE_I18N("This text contains device specific options controlled by the "\
+          "user on the scanner hardware.")
+#define SANE_DESC_NVRAM                   \
+SANE_I18N("Allows access obtaining the scanner's NVRAM values as pretty "\
+          "printed text.")
+#define SANE_DESC_PAPER_LENGTH            \
+SANE_I18N("Newer scanners can utilize this paper length to detect double feeds. "\
+          "However some others (DM152) can get confused during media flush if it is set.")
+#define SANE_DESC_FLIP_PAGE               \
+SANE_I18N("Tells page-flipping document scanners to flip the paper back to its "\
+          "original orientation before dropping it in the output tray.  "\
+          "Turning this off might make scanning a little faster if you don't "\
+          "care about manually flipping the pages afterwards.")
+#define SANE_DESC_ADF_INSTALLED           \
+SANE_I18N("ADF option is detected as installed.")
+#define SANE_DESC_LIGHTBOX_INSTALLED      \
+SANE_I18N("Lightbox option is detected as installed.")
+
+
 /* information needed for device access */
 typedef struct Avision_Connection {
   Avision_ConnectionType connection_type;
@@ -336,6 +395,10 @@ enum Avision_Option
   OPT_PAPERLEN,          /* Use paper_length field to detect double feeds */
   OPT_ADF_FLIP,          /* For flipping duplex, reflip the document */
 
+  OPT_OPTIONS_GROUP,
+  OPT_OPTION_ADF,       // ADF installed/detected?
+  OPT_OPTION_LIGHTBOX,   // LightBox installed/detected?
+
   NUM_OPTIONS            /* must come last */
 };
 
@@ -398,8 +461,7 @@ typedef struct Avision_Device
   SANE_Bool inquiry_nvram_read;
   SANE_Bool inquiry_power_save_time;
 
-  SANE_Bool inquiry_light_box;
-  SANE_Bool inquiry_adf;
+  SANE_Bool inquiry_adf_capability;
   SANE_Bool inquiry_duplex;
   SANE_Bool inquiry_duplex_interlaced;
   SANE_Bool inquiry_paper_length;
@@ -419,6 +481,10 @@ typedef struct Avision_Device
   SANE_Bool inquiry_light_control;
   SANE_Bool inquiry_exposure_control;
 
+  // Determines from accessories query.
+  SANE_Bool inquiry_light_box_present;
+  SANE_Bool inquiry_adf_present;
+
   int       inquiry_max_shading_target;
   SANE_Bool inquiry_button_control;
   unsigned int inquiry_buttons;
diff --git a/backend/canon_dr.c b/backend/canon_dr.c
index 58299d7..359005a 100644
--- a/backend/canon_dr.c
+++ b/backend/canon_dr.c
@@ -359,6 +359,9 @@
          - add support for reading the total and roller counters
       v64 2022-11-18, CQ, MAN
          - add complete support for imprinters on X10C (#585)
+      v65 2023-06-06, MAN
+         - fix imprinter support (#672)
+         - update attach_one and other init functions
 
    SANE FLOW DIAGRAM
 
@@ -411,7 +414,7 @@
 #include "canon_dr.h"
 
 #define DEBUG 1
-#define BUILD 64
+#define BUILD 65
 
 /* values for SANE_DEBUG_CANON_DR env var:
  - errors           5
@@ -1007,23 +1010,19 @@ attach_one (const char *device_name, int connType)
   /* this detects imprinters if they are available */
   ret = init_imprinters (s);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "attach_one: errors while trying to detect optional imprinters, continuing\n");
+    DBG (5, "attach_one: unable to init_imprinters, continuing\n");
   }
 
   /* enable/read the buttons */
   ret = init_panel (s);
   if (ret != SANE_STATUS_GOOD) {
-    disconnect_fd(s);
-    free (s);
-    DBG (5, "attach_one: model failed\n");
-    return ret;
+    DBG (5, "attach_one: unable init_panel, continuing\n");
   }
 
   /* enable/read the lifecycle counters */
   ret = init_counters (s);
   if (ret != SANE_STATUS_GOOD) {
-    DBG (5, "attach_one: unable to detect lifecycle counters, continuing\n");
-    return ret;
+    DBG (5, "attach_one: unable to init_counters, continuing\n");
   }
 
   /* sets SANE option 'values' to good defaults */
@@ -1035,6 +1034,7 @@ attach_one (const char *device_name, int connType)
     return ret;
   }
 
+  /* sets the s->opt array to blank */
   ret = init_options (s);
   if (ret != SANE_STATUS_GOOD) {
     disconnect_fd(s);
@@ -1988,19 +1988,26 @@ init_imprinters (struct scanner *s)
 {
   SANE_Status ret = SANE_STATUS_GOOD;
 
-  s->has_pre_imprinter = 0;
-  s->has_post_imprinter = 0;
+  DBG (10, "init_imprinters: start\n");
 
+  /* check the pre imprinter first */
   ret = detect_imprinter(s,R_PRE_IMPRINTER);
-  if(ret != SANE_STATUS_GOOD){
-    return ret;
+  if(ret == SANE_STATUS_GOOD){
+    DBG (15, "init_imprinters: preimprinter found\n");
+    s->has_pre_imprinter = 1;
   }
 
-  ret = detect_imprinter(s,R_POST_IMPRINTER);
-  if(ret != SANE_STATUS_GOOD){
-    return ret;
+  /* these scanners only support one imprinter */
+  /* so only ask for postimp if preimp not found */
+  else if(ret == SANE_STATUS_UNSUPPORTED){
+    ret = detect_imprinter(s,R_POST_IMPRINTER);
+    if(ret == SANE_STATUS_GOOD){
+      DBG (15, "init_imprinters: postimprinter found\n");
+      s->has_post_imprinter = 1;
+    }
   }
 
+  DBG (10, "init_imprinters: finish\n");
   return ret;
 }
 
@@ -2018,7 +2025,6 @@ init_panel (struct scanner *s)
   if(ret){
     DBG (5, "init_panel: disabling read_panel\n");
     s->can_read_panel = 0;
-    ret = SANE_STATUS_GOOD;
   }
 
   s->panel_enable_led = 1;
@@ -2027,7 +2033,6 @@ init_panel (struct scanner *s)
   if(ret){
     DBG (5, "init_panel: disabling send_panel\n");
     s->can_write_panel = 0;
-    ret = SANE_STATUS_GOOD;
   }
 
   DBG (10, "init_panel: finish\n");
@@ -2049,7 +2054,6 @@ init_counters (struct scanner *s)
   if(ret){
     DBG (5, "init_counters: disabling lifecycle counters\n");
     s->can_read_lifecycle_counters = 0;
-    return ret;
   }
 
   DBG (10, "init_counters: finish\n");
@@ -5249,8 +5253,13 @@ load_imprinting_settings(struct scanner *s)
   return ret;
 }
 
+/* look for a particular imprinter
+ * SANE_STATUS_GOOD = found
+ * SANE_STATUS_UNSUPPORTED = not found
+ * SANE_STATUS_INVAL = all other errors
+ */
 static SANE_Status
-detect_imprinter(struct scanner *s,SANE_Int option)
+detect_imprinter(struct scanner *s, SANE_Int imp_side)
 {
   SANE_Status ret = SANE_STATUS_GOOD;
 
@@ -5260,12 +5269,12 @@ detect_imprinter(struct scanner *s,SANE_Int option)
   unsigned char in[R_IMPRINTER_len];
   size_t inLen = R_IMPRINTER_len;
 
-  DBG (10, "detect_imprinter: start %d\n", option);
+  DBG (10, "detect_imprinter: start %d\n", imp_side);
 
   memset(cmd,0,cmdLen);
   set_SCSI_opcode(cmd, READ_code);
   set_R_datatype_code(cmd, SR_datatype_imprinters);
-  set_R_xfer_uid(cmd, option);
+  set_R_xfer_uid(cmd, imp_side);
   set_R_xfer_length(cmd, inLen);
 
   ret = do_cmd(
@@ -5275,23 +5284,24 @@ detect_imprinter(struct scanner *s,SANE_Int option)
     in, &inLen
   );
 
-  if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) {
+  /* some scanners return eof for success, so we change it */
+  if (ret == SANE_STATUS_EOF) {
     ret = SANE_STATUS_GOOD;
   }
 
-  int imprinter_found = get_R_IMPRINTER_found(in);
-  const char* imprinter_type = "unknown";
-  if (option == R_PRE_IMPRINTER){
-    s->has_pre_imprinter = imprinter_found;
-    imprinter_type = "pre-imprinter";
-  }
-  else if (option == R_POST_IMPRINTER){
-    s->has_post_imprinter = imprinter_found;
-    imprinter_type = "post-imprinter";
+  /* failed commands are 'inval' */
+  if(ret){
+    DBG (15, "detect_imprinter: error, converting %d to invalid\n", ret);
+    ret = SANE_STATUS_INVAL;
   }
 
-  DBG (10, "detect_imprinter:  type: %s. found status bit: %d \n",imprinter_type,imprinter_found);
+  /* negative responses are 'unsupported' */
+  else if(!get_R_IMPRINTER_found(in)){
+    DBG (15, "detect_imprinter: not found, converting to unsupported\n");
+    ret = SANE_STATUS_UNSUPPORTED;
+  }
 
+  DBG (10, "detect_imprinter: finish %d\n", ret);
   return ret;
 }
 
diff --git a/backend/dmc.c b/backend/dmc.c
index 410e48a..633219b 100644
--- a/backend/dmc.c
+++ b/backend/dmc.c
@@ -1,5 +1,5 @@
 /* sane - Scanner Access Now Easy.
-   Copyright (C) 1998 David F. Skoll
+   Copyright (C) 1998 Dianne Skoll
    Heavily based on "hp.c" driver for HP Scanners, by
    David Mosberger-Tang.
 
diff --git a/backend/dmc.h b/backend/dmc.h
index 6c2fc26..e268e09 100644
--- a/backend/dmc.h
+++ b/backend/dmc.h
@@ -1,5 +1,5 @@
 /* sane - Scanner Access Now Easy.
-   Copyright (C) 1998 David F. Skoll
+   Copyright (C) 1998 Dianne Skoll
    This file is part of the SANE package.
 
    This program is free software; you can redistribute it and/or
diff --git a/backend/epson2_usb.c b/backend/epson2_usb.c
index 8c850ab..f94d49d 100644
--- a/backend/epson2_usb.c
+++ b/backend/epson2_usb.c
@@ -172,6 +172,7 @@ SANE_Word sanei_epson_usb_product_ids[] = {
   0x08ca, /* L850 Series */
   0x08cd, /* WF-R4640 Series, WF-R5690 Series */
   0x08d0, /* PX-M350F, WF-M5690 Series */
+  0x08d1, /* L360 Series */
   0x08d2, /* L365 Series, L366 Series */
   0x1102, /* PX-048A Series, XP-230 Series, XP-235 Series */
   0x1105, /* ET-2500 Series, L375 Series */
diff --git a/backend/epsonds.c b/backend/epsonds.c
index 72d01d8..e7b6736 100644
--- a/backend/epsonds.c
+++ b/backend/epsonds.c
@@ -1259,6 +1259,9 @@ const  epsonds_profile_map epsonds_models_predefined[] = {
   {0x118A, "PID 118A","L3250 Series", 7},
   {0x119B, "PID 119B","XP-2150 Series", 7},
   {0x11B1, "PID 11B1","XP-2200 Series", 7},
+  {0x0193, "ES-C220","ES-C220", 5},
+  {0x018F, "DS-C330","DS-C330", 5},
+  {0x0191, "DS-C490","DS-C490", 5},
   {0x00, "","", 0x00 }
 };
 
@@ -1971,6 +1974,7 @@ init_options(epsonds_scanner *s)
 	}
 
 	/* "Geometry" group: */
+	s->opt[OPT_GEOMETRY_GROUP].name = "";
 	s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry");
 	s->opt[OPT_GEOMETRY_GROUP].desc = "";
 	s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
@@ -2020,6 +2024,7 @@ init_options(epsonds_scanner *s)
 	s->val[OPT_BR_Y].w = s->hw->y_range->max;
 
 	/* "Optional equipment" group: */
+	s->opt[OPT_EQU_GROUP].name = "";
 	s->opt[OPT_EQU_GROUP].title = SANE_I18N("Optional equipment");
 	s->opt[OPT_EQU_GROUP].desc = "";
 	s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP;
@@ -2263,6 +2268,7 @@ getvalue(SANE_Handle handle, SANE_Int option, void *value)
 	case OPT_BR_Y:
 	case OPT_DEPTH:
 	case OPT_ADF_SKEW:
+	case OPT_ADF_CRP:
 		*((SANE_Word *) value) = sval->w;
 		break;
 
diff --git a/backend/escl/escl.c b/backend/escl/escl.c
index cbbdb60..77b753f 100644
--- a/backend/escl/escl.c
+++ b/backend/escl/escl.c
@@ -621,16 +621,20 @@ sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
     ESCL_Device *dev = NULL;
     static const SANE_Device **devlist = 0;
     SANE_Status status;
+    SANE_Status status2;
 
     if (device_list == NULL)
 	return (SANE_STATUS_INVAL);
-    status = sanei_configure_attach(ESCL_CONFIG_FILE, NULL,
+    status2 = sanei_configure_attach(ESCL_CONFIG_FILE, NULL,
 				    attach_one_config, NULL);
-    if (status != SANE_STATUS_GOOD)
-	return (status);
     escl_devices(&status);
-    if (status != SANE_STATUS_GOOD)
-	return (status);
+    if (status != SANE_STATUS_GOOD && status2 != SANE_STATUS_GOOD)
+    {
+       if (status2 != SANE_STATUS_GOOD)
+               return (status2);
+       if (status != SANE_STATUS_GOOD)
+               return (status);
+    }
     if (devlist)
 	free(devlist);
     devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0]));
diff --git a/backend/escl/escl.h b/backend/escl/escl.h
index f99bff9..b59a3ff 100644
--- a/backend/escl/escl.h
+++ b/backend/escl/escl.h
@@ -92,7 +92,7 @@ typedef struct {
 typedef struct ESCL_Device {
     struct ESCL_Device *next;
 
-    double    version;
+    char     *version;
     char     *model_name;
     int       port_nb;
     char     *ip_address;
diff --git a/backend/escl/escl_capabilities.c b/backend/escl/escl_capabilities.c
index efbd547..950efaa 100644
--- a/backend/escl/escl_capabilities.c
+++ b/backend/escl/escl_capabilities.c
@@ -72,7 +72,7 @@ convert_elements(SANE_String_Const str)
         return (SANE_VALUE_SCAN_MODE_GRAY);
     else if (strcmp(str, "RGB24") == 0)
         return (SANE_VALUE_SCAN_MODE_COLOR);
-#if(defined HAVE_POPPLER_GLIB)
+#if HAVE_POPPLER_GLIB
     else if (strcmp(str, "BlackAndWhite1") == 0)
         return (SANE_VALUE_SCAN_MODE_LINEART);
 #endif
@@ -202,7 +202,11 @@ find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner, int type)
     const char *name = (const char *)node->name;
     if (strcmp(name, "ColorMode") == 0) {
 	const char *color = (SANE_String_Const)xmlNodeGetContent(node);
+#if HAVE_POPPLER_GLIB
         if (type == PLATEN || strcmp(color, "BlackAndWhite1"))
+#else
+        if (strcmp(color, "BlackAndWhite1"))
+#endif
           scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1);
     }
     else if (strcmp(name, "ContentType") == 0)
@@ -237,7 +241,7 @@ find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner, int type)
 	       scanner->caps[type].have_tiff = i;
             }
 #endif
-#if(defined HAVE_POPPLER_GLIB)
+#if HAVE_POPPLER_GLIB
             else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "application/pdf"))
             {
                have_pdf = SANE_TRUE;
@@ -432,7 +436,7 @@ print_xml_c(xmlNode *node, ESCL_Device *device, capabilities_t *scanner, int typ
         }
         if (!strcmp((const char *)node->name, "Version")&& node->ns && node->ns->prefix){
             if (!strcmp((const char*)node->ns->prefix, "pwg"))
-                device->version = atof ((const char *)xmlNodeGetContent(node));
+                device->version = strdup((const char *)xmlNodeGetContent(node));
 	}
         if (!strcmp((const char *)node->name, "MakeAndModel")){
             device->model_name = strdup((const char *)xmlNodeGetContent(node));
@@ -588,7 +592,6 @@ escl_capabilities(ESCL_Device *device, char *blacklist, SANE_Status *status)
         strstr(header->memory, "Server: HP_Compact_Server"))
         device->hack = curl_slist_append(NULL, "Host: localhost");
 
-    device->version = 0.0;
     scanner->source = 0;
     scanner->Sources = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * 4);
     for (i = 0; i < 4; i++)
diff --git a/backend/escl/escl_jpeg.c b/backend/escl/escl_jpeg.c
index 1dd3ec9..62c20c0 100644
--- a/backend/escl/escl_jpeg.c
+++ b/backend/escl/escl_jpeg.c
@@ -232,7 +232,13 @@ get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps)
 	        y_off,
 	        w,
 	        h);
-    surface = malloc(w * h * cinfo.output_components);
+    jpeg_start_decompress(&cinfo);
+    if (x_off > 0 || w < cinfo.output_width)
+       jpeg_crop_scanline(&cinfo, &x_off, &w);
+    lineSize = w * cinfo.output_components;
+    if (y_off > 0)
+        jpeg_skip_scanlines(&cinfo, y_off);
+    surface = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
     if (surface == NULL) {
         jpeg_destroy_decompress(&cinfo);
         DBG( 1, "Escl Jpeg : Memory allocation problem\n");
@@ -242,12 +248,6 @@ get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps)
         }
         return (SANE_STATUS_NO_MEM);
     }
-    jpeg_start_decompress(&cinfo);
-    if (x_off > 0 || w < cinfo.output_width)
-       jpeg_crop_scanline(&cinfo, &x_off, &w);
-    lineSize = w * cinfo.output_components;
-    if (y_off > 0)
-        jpeg_skip_scanlines(&cinfo, y_off);
     pos = 0;
     while (cinfo.output_scanline < (unsigned int)rh) {
         rowptr[0] = (JSAMPROW)surface + (lineSize * pos); // ..cinfo.output_scanline);
diff --git a/backend/escl/escl_newjob.c b/backend/escl/escl_newjob.c
index e1b326f..cb48bd1 100644
--- a/backend/escl/escl_newjob.c
+++ b/backend/escl/escl_newjob.c
@@ -46,7 +46,7 @@ struct downloading
 static const char settings[] =
     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"                        \
     "<scan:ScanSettings xmlns:pwg=\"http://www.pwg.org/schemas/2010/12/sm\" xmlns:scan=\"http://schemas.hp.com/imaging/escl/2011/05/03\">" \
-    "   <pwg:Version>%.2f</pwg:Version>" \
+    "   <pwg:Version>%s</pwg:Version>" \
     "   <pwg:ScanRegions>" \
     "      <pwg:ScanRegion>" \
     "          <pwg:ContentRegionUnits>escl:ThreeHundredthsOfInches</pwg:ContentRegionUnits>" \
@@ -184,10 +184,24 @@ escl_newjob (capabilities_t *scanner, const ESCL_Device *device, SANE_Status *st
 	    }
     }
     else {
-	    scanner->caps[scanner->source].default_format =
-		    strdup(scanner->caps[scanner->source].DocumentFormats[have_pdf]);
+	    if (have_pdf != -1) {
+	    	    scanner->caps[scanner->source].default_format =
+		    	    strdup(scanner->caps[scanner->source].DocumentFormats[have_pdf]);
+	    }
+	    else if (have_tiff != -1) {
+		    scanner->caps[scanner->source].default_format =
+			    strdup(scanner->caps[scanner->source].DocumentFormats[have_tiff]);
+	    }
+	    else if (have_png != -1) {
+		    scanner->caps[scanner->source].default_format =
+			    strdup(scanner->caps[scanner->source].DocumentFormats[have_png]);
+	    }
+	    else if (have_jpeg != -1) {
+		    scanner->caps[scanner->source].default_format =
+			    strdup(scanner->caps[scanner->source].DocumentFormats[have_jpeg]);
+	    }
     }
-    if (device->version <= 2.0)
+    if (atof ((const char *)device->version) <= 2.0)
     {
         // For eSCL 2.0 and older clients
         snprintf(f_ext_tmp, sizeof(f_ext_tmp),
diff --git a/backend/fujitsu.c b/backend/fujitsu.c
index 52d0988..1b5adc5 100644
--- a/backend/fujitsu.c
+++ b/backend/fujitsu.c
@@ -1,12 +1,12 @@
 /* sane - Scanner Access Now Easy.
 
    This file is part of the SANE package, and implements a SANE backend
-   for various Fujitsu scanners.
+   for various Fujitsu and Ricoh scanners.
 
    Copyright (C) 2000 Randolph Bentson
    Copyright (C) 2001 Frederik Ramm
    Copyright (C) 2001-2004 Oliver Schirrmeister
-   Copyright (C) 2003-2022 m. allan noah
+   Copyright (C) 2003-2023 m. allan noah
 
    JPEG output and low memory usage support funded by:
      Archivista GmbH, www.archivista.ch
@@ -616,6 +616,8 @@
       v139 2022-11-15, MAN
          - move updated window_gamma logic to set_window
          - use internal gamma table if possible (fixes #618)
+      v140 2023-03-27, MAN
+         - add initial support for Ricoh scanners
 
    SANE FLOW DIAGRAM
 
@@ -665,7 +667,7 @@
 #include "fujitsu.h"
 
 #define DEBUG 1
-#define BUILD 139
+#define BUILD 140
 
 /* values for SANE_DEBUG_FUJITSU env var:
  - errors           5
@@ -1154,7 +1156,7 @@ connect_fd (struct fujitsu *s)
 }
 
 /*
- * This routine will check if a certain device is a Fujitsu scanner
+ * This routine will check if a certain device is a Fujitsu/Ricoh scanner
  * It also copies interesting data from INQUIRY into the handle structure
  */
 static SANE_Status
@@ -1209,9 +1211,9 @@ init_inquire (struct fujitsu *s)
   for (i = 3; s->version_name[i] == ' ' && i >= 0; i--)
     s->version_name[i] = 0;
 
-  if (strcmp ("FUJITSU", s->vendor_name)) {
+  if (strcmp ("FUJITSU", s->vendor_name) && strcmp ("RICOH", s->vendor_name)) {
     DBG (5, "The device at '%s' is reported to be made by '%s'\n", s->device_name, s->vendor_name);
-    DBG (5, "This backend only supports Fujitsu products.\n");
+    DBG (5, "This backend only supports Fujitsu and Ricoh products.\n");
     return SANE_STATUS_INVAL;
   }
 
diff --git a/backend/fujitsu.conf.in b/backend/fujitsu.conf.in
index 9da0ae9..61d0266 100644
--- a/backend/fujitsu.conf.in
+++ b/backend/fujitsu.conf.in
@@ -15,10 +15,10 @@ scsi FUJITSU
 # To use a specific scsi device
 #scsi /dev/sg1
 
-# For Fujitsu scanners connected via USB on a known device (kernel driver):
+# For scanners connected via USB on a known device (kernel driver):
 #usb /dev/usb/scanner0
 
-# For Fujitsu scanners connected via USB using vendor and device ids (libusb):
+# For scanners connected via USB using vendor and device ids (libusb):
 #usb VENDORID PRODUCTID
 
 # NOTE: if you have to add your device here- please send the id and model
@@ -303,3 +303,12 @@ usb 0x04c5 0x162c
 
 #ScanSnap iX1400
 usb 0x04c5 0x1630
+
+######################################
+# Ricoh-only scanners
+
+#fi-8040
+usb 0x05ca 0x0307
+
+#fi-70F"
+usb 0x05ca 0x0308
diff --git a/backend/genesys.conf.in b/backend/genesys.conf.in
index e6788f5..6e6a8d7 100644
--- a/backend/genesys.conf.in
+++ b/backend/genesys.conf.in
@@ -146,6 +146,12 @@ usb 0x07b3 0x0c3a
 # Plustek OpticFilm 7500i
 usb 0x07b3 0x0c13
 
+# Plustek OpticFilm 7600i
+usb 0x07b3 0x0c3b
+
+# Plustek OpticFilm 8100
+usb 0x07b3 0x130c
+
 # Plustek OpticFilm 8200i
 usb 0x07b3 0x130d
 
diff --git a/backend/hpljm1005.c b/backend/hpljm1005.c
index 2a7116c..2353369 100644
--- a/backend/hpljm1005.c
+++ b/backend/hpljm1005.c
@@ -46,16 +46,11 @@
 #define BUILD 1
 
 #include  "../include/sane/config.h"
-#include  <math.h>
-
-#include  <limits.h>
 #include  <stdio.h>
 #include  <string.h>
 #include  <stdlib.h>
-#include  <ctype.h>
 #include  <fcntl.h>
 #include  <unistd.h>
-#include  <errno.h>
 #include  <stdint.h>
 #include <netinet/in.h>
 #define  BACKEND_NAME hpljm1005
@@ -110,10 +105,10 @@ static int cur_idx;
 #define RGB 1
 #define GRAY 0
 
-#define MAX_X_H 0x350
+#define MAX_X_H 0x351
 #define MAX_Y_H 0x490
-#define MAX_X_S 220
-#define MAX_Y_S 330
+#define MAX_X_S 216
+#define MAX_Y_S 297
 
 #define OPTION_MAX 9
 
@@ -144,6 +139,12 @@ static const SANE_String_Const mode_list[] = {
 #define STATUS_SCANNING 1
 #define STATUS_CANCELING 2
 
+struct buffer_s {
+  char *buffer;
+  size_t w_offset;
+  size_t size;
+};
+
 struct device_s
 {
   struct device_s *next;
@@ -151,15 +152,16 @@ struct device_s
   int idx;			/* Index in the usbid array */
   int dn;			/* Usb "Handle" */
   SANE_Option_Descriptor optiond[OPTION_MAX];
-  char *buffer;
-  int bufs;
+  struct buffer_s buf_r; /* also for gray mode */
+  struct buffer_s buf_g;
+  struct buffer_s buf_b;
   int read_offset;
-  int write_offset_r;
-  int write_offset_g;
-  int write_offset_b;
   int status;
   int width;
   int height;
+  int height_h;
+  int data_width; /* width + some padding 0xFF which should be ignored */
+  int scanned_pixels;
   SANE_Word optionw[OPTION_MAX];
   uint32_t conf_data[512];
   uint32_t packet_data[512];
@@ -186,58 +188,6 @@ round2(double x)
     return (double)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5);
 }
 
-static void
-update_img_size (struct device_s *dev)
-{
-  int dx, dy;
-
-  /* Only update the width when not scanning,
-   * otherwise the scanner give us the correct width */
-  if (dev->status == STATUS_SCANNING)
-    {
-      dev->height = -1;
-      return;
-    }
-
-  dx = dev->optionw[X2_OFFSET] - dev->optionw[X1_OFFSET];
-  dy = dev->optionw[Y2_OFFSET] - dev->optionw[Y1_OFFSET];
-
-  switch (dev->optionw[RES_OFFSET])
-    {
-    case 75:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 640);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 880);
-      break;
-    case 100:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 848);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 1180);
-      break;
-    case 150:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 1264);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 1775);
-      break;
-    case 200:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 1696);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 2351);
-      break;
-    case 300:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 2528);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 3510);
-      break;
-    case 600:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 5088);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 7020);
-      break;
-    case 1200:
-      dev->width = round2 ((dx / ((double) MAX_X_S)) * 10208);
-      dev->height = round2 ((dy / ((double) MAX_Y_S)) * 14025);
-      break;
-    }
-
-    DBG(2,"New image size: %dx%d\n",dev->width, dev->height);
-
-}
-
 /* This function is copy/pasted from the Epson backend */
 static size_t
 max_string_size (const SANE_String_Const strings[])
@@ -721,7 +671,6 @@ sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
   p->last_frame = SANE_TRUE;
   p->depth = 8;
 
-  update_img_size (dev);
   p->pixels_per_line = dev->width;
   p->lines = dev->height;
   p->bytes_per_line = p->pixels_per_line;
@@ -805,7 +754,7 @@ send_conf (struct device_s *dev)
   dev->conf_data[21] = 0;
   dev->conf_data[22] = htonl (0x491);
   dev->conf_data[23] = htonl (0x352);
-
+  dev->height_h = y2 - y1;
   if (dev->optionw[COLOR_OFFSET] == RGB)
     {
       dev->conf_data[15] = htonl (0x2);
@@ -821,116 +770,151 @@ send_conf (struct device_s *dev)
   sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
 }
 
-static SANE_Status
-get_data (struct device_s *dev)
+static SANE_Status create_buffer(struct buffer_s *buf, int buffer_size) {
+  if (buf->buffer)
+  {
+    free(buf->buffer);
+  }
+
+  buf->buffer = malloc(buffer_size);
+  if (!buf->buffer)
+    return SANE_STATUS_NO_MEM;
+  buf->size = buffer_size;
+  buf->w_offset = 0;
+  return SANE_STATUS_GOOD;
+}
+
+static SANE_Status create_buffers(struct device_s *dev, int buf_size) {
+  if (create_buffer(&dev->buf_r, buf_size) != SANE_STATUS_GOOD)
+    return SANE_STATUS_NO_MEM;
+  if (dev->optionw[COLOR_OFFSET] == RGB)
+  {
+    if (create_buffer(&dev->buf_g, buf_size) != SANE_STATUS_GOOD)
+      return SANE_STATUS_NO_MEM;
+    if (create_buffer(&dev->buf_b, buf_size) != SANE_STATUS_GOOD)
+      return SANE_STATUS_NO_MEM;
+  }
+  return SANE_STATUS_GOOD;
+}
+
+static SANE_Status remove_buffers(struct device_s *dev) {
+  if (dev->buf_r.buffer)
+    free(dev->buf_r.buffer);
+  if (dev->buf_g.buffer)
+    free(dev->buf_g.buffer);
+  if (dev->buf_b.buffer)
+    free(dev->buf_b.buffer);
+  dev->buf_r.w_offset = dev->buf_g.w_offset = dev->buf_b.w_offset = 0;
+  dev->buf_r.size = dev->buf_g.size = dev->buf_b.size = 0;
+  dev->buf_r.buffer = dev->buf_g.buffer = dev->buf_b.buffer = NULL;
+  dev->read_offset = 0;
+  return SANE_STATUS_GOOD;
+}
+
+static SANE_Status get_data (struct device_s *dev)
 {
   int color;
   size_t size;
   int packet_size;
   unsigned char *buffer = (unsigned char *) dev->packet_data;
   if (dev->status == STATUS_IDLE)
+  {
+    DBG(101, "STATUS == IDLE\n");
     return SANE_STATUS_IO_ERROR;
+  }
   /* first wait a standard data pkt */
   do
+  {
+    size = 32;
+    sanei_usb_read_bulk (dev->dn, buffer, &size);
+    if (size)
     {
-      size = 32;
-      sanei_usb_read_bulk (dev->dn, buffer, &size);
-      if (size)
-	{
-	  if (ntohl (dev->packet_data[0]) == MAGIC_NUMBER)
-	    {
-	      if (ntohl (dev->packet_data[1]) == PKT_DATA)
-		break;
-	      if (ntohl (dev->packet_data[1]) == PKT_END_DATA)
-		{
-		  dev->status = STATUS_IDLE;
-		  DBG(100,"End of scan encountered on device %s\n",dev->devname);
-		  send_pkt (PKT_GO_IDLE, 0, dev);
-		  wait_ack (dev, NULL);
-		  wait_ack (dev, NULL);
-		  send_pkt (PKT_UNKNOW_1, 0, dev);
-		  wait_ack (dev, NULL);
-		  send_pkt (PKT_RESET, 0, dev);
-		  sleep (2);	/* Time for the scanning head to go back home */
-		  return SANE_STATUS_EOF;
-		}
-	    }
-	}
+      if (ntohl (dev->packet_data[0]) == MAGIC_NUMBER)
+      {
+        if (ntohl (dev->packet_data[1]) == PKT_DATA)
+          break;
+        if (ntohl (dev->packet_data[1]) == PKT_END_DATA)
+        {
+          dev->status = STATUS_IDLE;
+          DBG(100,"End of scan encountered on device %s\n",dev->devname);
+          send_pkt (PKT_GO_IDLE, 0, dev);
+          wait_ack (dev, NULL);
+          wait_ack (dev, NULL);
+          send_pkt (PKT_UNKNOW_1, 0, dev);
+          wait_ack (dev, NULL);
+          send_pkt (PKT_RESET, 0, dev);
+          sleep (2);    /* Time for the scanning head to go back home */
+          return SANE_STATUS_EOF;
+        }
+      }
     }
-  while (1);
+  } while (1);
   packet_size = ntohl (dev->packet_data[5]);
-  if (!dev->buffer)
-    {
-      dev->bufs = packet_size - 24 /* size of header */ ;
-      if (dev->optionw[COLOR_OFFSET] == RGB)
-	dev->bufs *= 3;
-      dev->buffer = malloc (dev->bufs);
-      if (!dev->buffer)
-	return SANE_STATUS_NO_MEM;
-      dev->write_offset_r = 0;
-      dev->write_offset_g = 1;
-      dev->write_offset_b = 2;
-
-    }
+  if (! dev->buf_r.buffer)
+  {
+    /*
+      For some reason scanner sends packets in order:
+        <start> R G B ... R G B R G B RRR GGG BBB <end>
+      To hanle the last triple portion create a triple size buffer
+    */
+    int buf_size = (packet_size - 24) * 3; /* 24 - size of header */ ;
+    if (create_buffers(dev, buf_size) != SANE_STATUS_GOOD)
+      return SANE_STATUS_NO_MEM;
+  }
   /* Get the "data header" */
   do
-    {
-      size = 24;
-      sanei_usb_read_bulk (dev->dn, buffer, &size);
-    }
-  while (!size);
+  {
+    size = 24;
+    sanei_usb_read_bulk (dev->dn, buffer, &size);
+  } while (!size);
   color = ntohl (dev->packet_data[0]);
   packet_size -= size;
-  dev->width = ntohl (dev->packet_data[5]);
-  DBG(100,"Got data size %d on device %s. Scan width: %d\n",packet_size, dev->devname, dev->width);
+  dev->width = ntohl (dev->packet_data[4]);
+  dev->height = dev->height_h * dev->optionw[RES_OFFSET] / 100;
+  dev->data_width = ntohl (dev->packet_data[5]);
+  DBG(100,"Got data size %d on device %s. Scan width: %d, data width: %d\n",packet_size, dev->devname, dev->width, dev->data_width);
   /* Now, read the data */
   do
+  {
+    int ret;
+    do
     {
-      int j;
-      int i;
-      int ret;
-      do
-	{
-	  size = packet_size > 512 ? 512 : packet_size;
-	  ret = sanei_usb_read_bulk (dev->dn, buffer, &size);
-	}
-      while (!size || ret != SANE_STATUS_GOOD);
-      packet_size -= size;
-      switch (color)
-	{
-	case RED_LAYER:
-	  DBG(101,"Got red layer data on device %s\n",dev->devname);
-	  i = dev->write_offset_r + 3 * size;
-	  if (i > dev->bufs)
-	    i = dev->bufs;
-	  for (j = 0; dev->write_offset_r < i; dev->write_offset_r += 3)
-	    dev->buffer[dev->write_offset_r] = buffer[j++];
-	  break;
-	case GREEN_LAYER:
-	  DBG(101,"Got green layer data on device %s\n",dev->devname);
-	  i = dev->write_offset_g + 3 * size;
-	  if (i > dev->bufs)
-	    i = dev->bufs;
-	  for (j = 0; dev->write_offset_g < i; dev->write_offset_g += 3)
-	    dev->buffer[dev->write_offset_g] = buffer[j++];
-	  break;
-	case BLUE_LAYER:
-          DBG(101,"Got blue layer data on device %s\n",dev->devname);
-	  i = dev->write_offset_b + 3 * size;
-	  if (i > dev->bufs)
-	    i = dev->bufs;
-	  for (j = 0; dev->write_offset_b < i; dev->write_offset_b += 3)
-	    dev->buffer[dev->write_offset_b] = buffer[j++];
-	  break;
-	case GRAY_LAYER:
-	  DBG(101,"Got gray layer data on device %s\n",dev->devname);
-	  if (dev->write_offset_r + (int)size >= dev->bufs)
-	    size = dev->bufs - dev->write_offset_r;
-	  memcpy (dev->buffer + dev->write_offset_r, buffer, size);
-	  dev->write_offset_r += size;
-	  break;
-	}
+      size = packet_size > 512 ? 512 : packet_size;
+      ret = sanei_usb_read_bulk (dev->dn, buffer, &size);
+    } while (!size || ret != SANE_STATUS_GOOD);
+    packet_size -= size;
+    struct buffer_s * current_buf;
+    char color_char;
+    switch (color)
+    {
+      case GRAY_LAYER:
+        color_char = 'g';
+        current_buf = &dev->buf_r;
+        break;
+      case RED_LAYER:
+        color_char = 'R';
+        current_buf = &dev->buf_r;
+        break;
+      case GREEN_LAYER:
+        color_char = 'G';
+        current_buf = &dev->buf_g;
+        break;
+      case BLUE_LAYER:
+        color_char = 'B';
+        current_buf = &dev->buf_b;
+        break;
+      default:
+        DBG(101, "Unknown color code: %d \n", color);
+        return SANE_STATUS_IO_ERROR;
+    }
+    DBG(101,"Got %c layer data on device %s\n", color_char, dev->devname);
+    if (current_buf->w_offset + size > current_buf->size) {
+      DBG(100, "buffer overflow\n");
+      return SANE_STATUS_IO_ERROR;
     }
+    memcpy(current_buf->buffer + current_buf->w_offset, buffer, size);
+    current_buf->w_offset += size;
+  }
   while (packet_size > 0);
   return SANE_STATUS_GOOD;
 }
@@ -943,13 +927,8 @@ sane_start (SANE_Handle h)
   size_t size;
 
   dev->read_offset = 0;
-  dev->write_offset_r = 0;
-  dev->write_offset_g = 1;
-  dev->write_offset_b = 2;
-
-  free (dev->buffer);
-  dev->buffer = NULL;
-
+  dev->scanned_pixels = 0;
+  remove_buffers(dev);
 
   send_pkt (PKT_RESET, 0, dev);
   send_pkt (PKT_READ_STATUS, 0, dev);
@@ -992,16 +971,12 @@ static void
 do_cancel(struct device_s *dev)
 {
   while (get_data (dev) == SANE_STATUS_GOOD);
-  free (dev->buffer);
-  dev->buffer = NULL;
+  remove_buffers(dev);
 }
 
 static int
 min3 (int r, int g, int b)
 {
-  /* Optimize me ! */
-  g--;
-  b -= 2;
   if (r < g && r < b)
     return r;
   if (b < r && b < g)
@@ -1009,57 +984,83 @@ min3 (int r, int g, int b)
   return g;
 }
 
+static int
+min_buf_w_offset (struct device_s * dev)
+{
+  if (dev->optionw[COLOR_OFFSET] == RGB)
+    return min3 (dev->buf_r.w_offset, dev->buf_g.w_offset, dev->buf_b.w_offset);
+  return dev->buf_r.w_offset;
+}
+
+static int is_buf_synchronized(struct device_s * dev) {
+  if (dev->optionw[COLOR_OFFSET] == RGB)
+    return dev->buf_r.w_offset == dev->buf_g.w_offset
+      && dev->buf_r.w_offset == dev->buf_b.w_offset;
+  return 1;
+}
+
 SANE_Status
 sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
 {
   struct device_s *dev = (struct device_s *) h;
   int available;
   int ret;
+  if (dev->optionw[COLOR_OFFSET] == RGB) {
+    maxlen /= 3;
+  }
   *len = 0;
   if (dev->status == STATUS_IDLE)
+  {
+    DBG(101, "STATUS == IDLE\n");
     return SANE_STATUS_IO_ERROR;
-  if (dev->optionw[COLOR_OFFSET] == RGB)
+  }
+  while (min_buf_w_offset(dev) <= dev->read_offset)
+  {
+    ret = get_data (dev);
+    if (ret != SANE_STATUS_GOOD)
     {
-      while (min3 (dev->write_offset_r, dev->write_offset_g,
-		   dev->write_offset_b) <= dev->read_offset)
-	{
-	  ret = get_data (dev);
-	  if (ret != SANE_STATUS_GOOD)
-	    {
-	      if (min3 (dev->write_offset_r,
-			dev->write_offset_g,
-			dev->write_offset_b) <= dev->read_offset)
-		return ret;
-	    }
-	}
-      available = min3 (dev->write_offset_r, dev->write_offset_g,
-			dev->write_offset_b);
+      if (min_buf_w_offset(dev) <= dev->read_offset) {
+        return ret;
+      }
     }
-  else
+  }
+  available = min_buf_w_offset(dev);
+  int pixel_len = available - dev->read_offset;
+  if (pixel_len > maxlen)
+    pixel_len = maxlen;
+  int img_size = dev->width * dev->height;
+  for(int i=0; i<pixel_len; ++i)
+  {
+    int pos = dev->read_offset+i;
+    if (pos % dev->data_width >= dev->width)
+      continue;
+    if (dev->scanned_pixels >= img_size)
     {
-      while (dev->write_offset_r <= dev->read_offset)
-	{
-	  ret = get_data (dev);
-	  if (ret != SANE_STATUS_GOOD)
-	    if (dev->write_offset_r <= dev->read_offset)
-	      return ret;
-	}
-      available = dev->write_offset_r;
+      DBG(101, "Extra pixels received.\n");
+      break;
     }
-  *len = available - dev->read_offset;
-  if (*len > maxlen)
-    *len = maxlen;
-  memcpy (buf, dev->buffer + dev->read_offset, *len);
-  dev->read_offset += *len;
-  if (dev->read_offset == dev->bufs)
+    dev->scanned_pixels++;
+    buf[(*len)++] = dev->buf_r.buffer[pos];
+    if (dev->optionw[COLOR_OFFSET] == RGB)
     {
-      free (dev->buffer);
-      dev->buffer = NULL;
-      dev->read_offset = 0;
-      dev->write_offset_r = 0;
-      dev->write_offset_g = 1;
-      dev->write_offset_b = 2;
+      buf[(*len)++] = dev->buf_g.buffer[pos];
+      buf[(*len)++] = dev->buf_b.buffer[pos];
     }
+  }
+  DBG(101, "Moved %d pixels to buffer. Total pixel scanned: %d \n", *len, dev->scanned_pixels);
+  if (dev->scanned_pixels == img_size)
+    DBG(100, "Full image received\n");
+  dev->read_offset += pixel_len;
+
+  /*
+    If w_offset is the same in all buffers and has already been completely transferred
+    to the common buffer - flush buffer. It will be recreated in get_data with a reserve
+    for the last triple portions
+  */
+  if (is_buf_synchronized(dev) && available == dev->read_offset)
+  {
+    remove_buffers(dev);
+  }
 
   /* Special case where sane_cancel is called while scanning */
   if (dev->status == STATUS_CANCELING)
@@ -1082,8 +1083,7 @@ sane_cancel (SANE_Handle h)
       return;
     }
 
-  free (dev->buffer);
-  dev->buffer = NULL;
+  remove_buffers(dev);
 }
 
 SANE_Status
diff --git a/backend/lexmark.c b/backend/lexmark.c
index ba0311d..3c171c7 100644
--- a/backend/lexmark.c
+++ b/backend/lexmark.c
@@ -829,7 +829,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
     case SANE_ACTION_SET_VALUE:
 
       if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap))
-	return SANE_STATUS_INVAL;
+        return SANE_STATUS_INVAL;
 
       /* Make sure boolean values are only TRUE or FALSE */
       if (lexmark_device->opt[option].type == SANE_TYPE_BOOL)
diff --git a/backend/lexmark_x2600.c b/backend/lexmark_x2600.c
new file mode 100644
index 0000000..610064e
--- /dev/null
+++ b/backend/lexmark_x2600.c
@@ -0,0 +1,1287 @@
+/* lexmark_x2600.c: SANE backend for Lexmark x2600 scanners.
+
+   (C) 2023 "Benoit Juin" <benoit.juin@gmail.com>
+
+   This file is part of the SANE package.
+
+   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, see <https://www.gnu.org/licenses/>.
+
+   As a special exception, the authors of SANE give permission for
+   additional uses of the libraries contained in this release of SANE.
+
+   The exception is that, if you link a SANE library with other files
+   to produce an executable, this does not by itself cause the
+   resulting executable to be covered by the GNU General Public
+   License.  Your use of that executable is in no way restricted on
+   account of linking the SANE library code into it.
+
+   This exception does not, however, invalidate any other reasons why
+   the executable file might be covered by the GNU General Public
+   License.
+
+   If you submit changes to SANE to the maintainers to be included in
+   a subsequent release, you agree by submitting the changes that
+   those changes may be distributed with this exception intact.
+
+   If you write modifications of your own for SANE, it is your choice
+   whether to permit this exception to apply to your modifications.
+   If you do not wish that, delete this exception notice.
+
+   **************************************************************************/
+
+
+#include "lexmark_x2600.h"
+
+#define BUILD 1
+#define LEXMARK_X2600_CONFIG_FILE "lexmark_x2600.conf"
+#define MAX_OPTION_STRING_SIZE 255
+static SANE_Int transfer_buffer_size = 32768;
+static Lexmark_Device *first_device = 0;
+static SANE_Int num_devices = 0;
+static const SANE_Device **devlist = 0;
+
+static SANE_Bool initialized = SANE_FALSE;
+
+// first value is the size of the wordlist!
+static SANE_Int dpi_list[] = {
+  4, 100, 200, 300, 600
+};
+static SANE_Int dpi_list_size = sizeof(dpi_list) / sizeof(dpi_list[0]);
+
+static SANE_String_Const mode_list[] = {
+  SANE_VALUE_SCAN_MODE_COLOR,
+  SANE_VALUE_SCAN_MODE_GRAY,
+  NULL
+};
+
+static SANE_Range x_range = {
+  0,				/* minimum */
+  5078,				/* maximum */
+  1				/* quantization */
+};
+
+static SANE_Range y_range = {
+  0,				/* minimum */
+  7015,				/* maximum */
+  1				/* quantization */
+};
+
+static SANE_Byte command1_block[] = {
+  0xA5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xAA, 0xBB,
+  0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x03, 0x00,
+  0x00, 0x00, 0x80, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+  0xAA, 0xBB, 0xCC, 0xDD};
+static SANE_Int command1_block_size = sizeof(command1_block);
+
+static SANE_Byte command2_block[] = {
+  0xA5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xAA, 0xBB,
+  0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x04, 0x00,
+  0x00, 0x00, 0x80, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+  0xAA, 0xBB, 0xCC, 0xDD};
+static SANE_Int command2_block_size = sizeof(command2_block);
+
+static SANE_Byte command_with_params_block[] = {
+  0xA5, 0x00, 0x31, 0x10, 0x01, 0x83, 0xAA, 0xBB,
+  0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x05, 0x00,
+  0x18, 0x00, 0x80, 0x00, 0xFF, 0x00, 0x00, 0x02,
+  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xBB, 0xCC, 0xDD,
+  0xAA, 0xBB, 0xCC, 0xDD};
+static SANE_Int command_with_params_block_size = sizeof(command_with_params_block);
+
+static SANE_Byte command_cancel1_block[] = {
+  0xa5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xaa, 0xbb,
+  0xcc, 0xdd, 0x02, 0x00, 0x1b, 0x53, 0x0f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd,
+  0xaa, 0xbb, 0xcc, 0xdd};
+static SANE_Byte command_cancel2_block[] = {
+  0xa5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xaa, 0xbb,
+  0xcc, 0xdd, 0x02, 0x00, 0x1b, 0x53, 0x06, 0x00,
+  0x00, 0x00, 0x80, 0x00, 0xaa, 0xbb, 0xcc, 0xdd,
+  0xaa, 0xbb, 0xcc, 0xdd};
+static SANE_Int command_cancel_size = sizeof(command_cancel1_block);
+
+static SANE_Byte empty_line_data_packet[] = {
+  0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00};
+static SANE_Int empty_line_data_packet_size = sizeof(empty_line_data_packet);
+
+static SANE_Byte last_data_packet[] = {
+  0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01};
+static SANE_Int last_data_packet_size = sizeof(last_data_packet);
+
+static SANE_Byte cancel_packet[] = {
+  0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x03};
+static SANE_Int cancel_packet_size = sizeof(cancel_packet);
+
+static SANE_Byte linebegin_data_packet[] = {
+  0x1b, 0x53, 0x02, 0x00};
+static SANE_Int linebegin_data_packet_size = sizeof(linebegin_data_packet);
+
+static SANE_Byte unknown_a_data_packet[] = {
+  0x1b, 0x53, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00};
+static SANE_Int unknown_a_data_packet_size = sizeof(unknown_a_data_packet);
+
+static SANE_Byte unknown_b_data_packet[] = {
+  0x1b, 0x53, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00};
+static SANE_Int unknown_b_data_packet_size = sizeof(unknown_b_data_packet);
+
+static SANE_Byte unknown_c_data_packet[] = {
+  0x1b, 0x53, 0x04, 0x00, 0x00, 0x00, 0x84, 0x00};
+static SANE_Int unknown_c_data_packet_size = sizeof(unknown_c_data_packet);
+
+static SANE_Byte unknown_d_data_packet[] = {
+  0x1b, 0x53, 0x05, 0x00, 0x00, 0x00};
+static SANE_Int unknown_d_data_packet_size = sizeof(unknown_d_data_packet);
+
+static SANE_Byte unknown_e_data_packet[] = {
+  0xa5, 0x00, 0x06, 0x10, 0x01, 0xaa, 0xbb, 0xcc,
+  0xdd};
+static SANE_Int unknown_e_data_packet_size = sizeof(unknown_e_data_packet);
+
+/* static SANE_Byte not_ready_data_packet[] = { */
+/*   0x1b, 0x53, 0x01, 0x00, 0x01, 0x00, 0x84, 0x00}; */
+/* static SANE_Int not_ready_data_packet_size = sizeof(not_ready_data_packet); */
+
+
+static SANE_Int line_header_length = 9;
+
+
+//static SANE_Byte empty_data_packet[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+SANE_Status
+clean_and_copy_data(const SANE_Byte * source, SANE_Int source_size,
+                    SANE_Byte * destination, SANE_Int * destination_length,
+                    SANE_Int mode, SANE_Int max_length, SANE_Handle dev)
+{
+  DBG (10, "clean_and_copy_data\n");
+  // if source doesnt start with 1b 53 02, then it is a continuation packet
+  // SANE_Int k = 0;
+  // SANE_Int bytes_written = 0;
+  // BW    1b 53 02 00 21 00 00 00 00  |   32 |   21 ->    33 (segmentlng=   32)
+  // BW    1b 53 02 00 41 00 00 00 00  |   64 |   41 ->    65 (segmentlng=   64)
+  // COLOR 1b 53 02 00 c1 00 00 00 00  |   64 |   c1 ->   193 (segmentlng=  192)
+  // COLOR 1b 53 02 00 01 06 00 00 00  |  512 |  601 ->  1537 (segmentlng= 1536)
+  // COLOR 1b 53 02 00 99 3a 00 00 00  | 5000 | 3a99 -> 15001 (segmentlng=15000)
+  // COLOR 1b 53 02 00 f7 0f 00        | 1362 | 0ff7 ->  4087 <- limit where sane_read can a read a line at e time, more that 1362 and then the rest
+  //                                                             of the line will be available in the next sane_read call
+  // COLOR 1b 53 02 00 fa 0f 00        |      | 0ffa ->  4090 <- in that case the line doesnt fit, clean_and_copy_data will be called again with the rest of the data
+
+
+  // edge case segment doesn(t feet in the packet size
+  /* if(segment_length > source_size - 9) */
+  /*   segment_length = source_size - 9; */
+
+  // the scanner sends series of 8 lines function param source
+  // every lines has prefix see linebegin_data_packet
+  // the source parameter as a limited length :function param source_size
+  // so the serie og 8 lines can be splited
+  // in such case, in the next call of this function, source contain the end of the
+  // broken segment.
+  // Here is the way data is read:
+  // 1 - check that source begin with a linebegin_data_packet signature
+  //     if this is the case the source[4] & source[5] contains how much data
+  //     can be read before onother header is reach (linebegin_data_packet)
+
+  Lexmark_Device * ldev = (Lexmark_Device * ) dev;
+  SANE_Int i = 0;
+  SANE_Int bytes_read = 0;
+  SANE_Byte tmp = 0;
+  SANE_Int source_read_cursor = 0;
+  SANE_Int block_pixel_data_length = 0;
+  SANE_Int size_to_realloc = 0;
+
+
+  if(!ldev->eof){
+
+    // does source start with linebegin_data_packet?
+    if (memcmp(linebegin_data_packet, source, linebegin_data_packet_size) == 0){
+      // extract the number of bytes we can read befor new header is reached
+      // store it in the device in case of continuation packet
+      ldev->read_buffer->linesize = (source[4] + ((source[5] << 8) & 0xFF00)) - 1;
+      ldev->read_buffer->last_line_bytes_read = ldev->read_buffer->linesize;
+      DBG (10, "    this is the begining of a line linesize=%ld\n",
+           ldev->read_buffer->linesize);
+    } else {
+      DBG (10, "    this is not a new line packet, continue to fill the read buffer\n");
+      //return;
+    }
+
+    if(ldev->read_buffer->linesize == 0){
+      DBG (10, "    linesize=0 something went wrong, lets ignore that USB packet\n");
+      return SANE_STATUS_CANCELLED;
+    }
+
+
+    // loop over source buffer
+    while(i < source_size){
+      // last line was full
+      if(ldev->read_buffer->last_line_bytes_read == ldev->read_buffer->linesize){
+        // if next block fit in the source
+        if(i + line_header_length + (SANE_Int) ldev->read_buffer->linesize <= source_size){
+          ldev->read_buffer->image_line_no += 1;
+          source_read_cursor = i + line_header_length;
+          block_pixel_data_length = ldev->read_buffer->linesize;
+          ldev->read_buffer->last_line_bytes_read = block_pixel_data_length;
+          size_to_realloc = ldev->read_buffer->image_line_no *
+            ldev->read_buffer->linesize * sizeof(SANE_Byte);
+          bytes_read = block_pixel_data_length + line_header_length;
+        }
+        // next block cannot be read fully because source_size is too small
+        // (USB packet fragmentation)
+        else{
+          ldev->read_buffer->image_line_no += 1;
+          source_read_cursor = i + line_header_length;
+          block_pixel_data_length = source_size - i - line_header_length;
+          ldev->read_buffer->last_line_bytes_read = block_pixel_data_length;
+          size_to_realloc = ((ldev->read_buffer->image_line_no-1) *
+            ldev->read_buffer->linesize + block_pixel_data_length) * sizeof(SANE_Byte);
+          bytes_read = block_pixel_data_length + line_header_length;
+        }
+      }
+      // last line was not full lets extract what is left
+      // this is du to USB packet fragmentation
+      else{
+        // the last line was not full so no increment
+        ldev->read_buffer->image_line_no += 0;
+        source_read_cursor = i;
+        block_pixel_data_length = ldev->read_buffer->linesize -
+          ldev->read_buffer->last_line_bytes_read;
+        // we completed the last line with missing bytes so new the line is full
+        ldev->read_buffer->last_line_bytes_read = ldev->read_buffer->linesize;
+        size_to_realloc = ldev->read_buffer->image_line_no *
+          ldev->read_buffer->linesize * sizeof(SANE_Byte);
+        bytes_read = block_pixel_data_length;
+      }
+
+      DBG (20, "    size_to_realloc=%d i=%d image_line_no=%d\n",
+           size_to_realloc, i, ldev->read_buffer->image_line_no);
+      // do realoc memory space for our buffer
+      SANE_Byte* alloc_result = realloc(ldev->read_buffer->data, size_to_realloc);
+      if(alloc_result == NULL){
+        // TODO allocation was not possible
+        DBG (20, "    REALLOC failed\n");
+        return SANE_STATUS_NO_MEM;
+      }
+      // point data to our new memary space
+      ldev->read_buffer->data = alloc_result;
+      // reposition writeptr and readptr to the correct memory adress
+      // to do that use write_byte_counter and read_byte_counter
+      ldev->read_buffer->writeptr =
+        ldev->read_buffer->data + ldev->read_buffer->write_byte_counter;
+      // copy new data
+      memcpy(
+             ldev->read_buffer->writeptr,
+             source + source_read_cursor,
+             block_pixel_data_length
+      );
+
+      // store how long is the buffer
+      ldev->read_buffer->write_byte_counter += block_pixel_data_length;
+
+      i += bytes_read;
+    }
+  }
+
+  // reposition our readptr
+  ldev->read_buffer->readptr =
+    ldev->read_buffer->data + ldev->read_buffer->read_byte_counter;
+
+
+  // read our buffer to fill the destination buffer
+  // mulitple call so read may has been already started
+  // length already read is stored in ldev->read_buffer->read_byte_counter
+
+  SANE_Int available_bytes_to_read =
+    ldev->read_buffer->write_byte_counter - ldev->read_buffer->read_byte_counter;
+
+  DBG (20, "    source read done now sending to destination \n");
+
+  // we will copy image data 3 bytes by 3 bytes if color mod to allow color swap
+  // this avoid error on color channels swapping
+  if (mode == SANE_FRAME_RGB){
+
+    // get max chunk
+    SANE_Int data_chunk_size = max_length;
+    if(data_chunk_size > available_bytes_to_read){
+      data_chunk_size = available_bytes_to_read;
+    }
+    data_chunk_size = data_chunk_size / 3;
+    data_chunk_size = data_chunk_size * 3;
+
+    // we have to invert color channels
+    SANE_Byte * color_swarp_ptr = ldev->read_buffer->readptr;
+    for(SANE_Int j=0; j < data_chunk_size;j += 3){
+      // DBG (20, "  swapping RGB <- BGR j=%d\n", j);
+      tmp = *(color_swarp_ptr + j);
+      *(color_swarp_ptr + j) = *(color_swarp_ptr + j + 2);
+      *(color_swarp_ptr + j + 2) = tmp;
+    }
+
+    memcpy (destination,
+            ldev->read_buffer->readptr,
+            data_chunk_size);
+
+    ldev->read_buffer->read_byte_counter += data_chunk_size;
+    *destination_length = data_chunk_size;
+
+  }
+  // gray mode copy until max_length
+  else{
+
+    SANE_Int data_chunk_size = max_length;
+    if(data_chunk_size > available_bytes_to_read){
+      data_chunk_size = available_bytes_to_read;
+    }
+    memcpy (
+      destination,
+      ldev->read_buffer->readptr,
+      data_chunk_size
+    );
+    ldev->read_buffer->read_byte_counter += data_chunk_size;;
+    *destination_length = data_chunk_size;
+
+  }
+
+  DBG (20, "    done destination_length=%d available_bytes_to_read=%d\n",
+       *destination_length, available_bytes_to_read);
+
+  if(available_bytes_to_read > 0){
+    return SANE_STATUS_GOOD;
+  }else{
+    ldev->eof = 0;
+    return SANE_STATUS_EOF;
+  }
+
+}
+
+SANE_Status
+usb_write_then_read (Lexmark_Device * dev, SANE_Byte * cmd, size_t cmd_size)
+{
+  size_t buf_size = 256;
+  SANE_Byte buf[buf_size];
+  SANE_Status status;
+
+  DBG (10, "usb_write_then_read: %d\n", dev->devnum);
+  sanei_usb_set_endpoint(dev->devnum, USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK, 0x02);
+  DBG (10, "    endpoint set: %d\n", dev->devnum);
+
+  /* status = sanei_usb_read_bulk (dev->devnum, buf, &buf_size); */
+  /* DBG (10, "    readdone: %d\n", dev->devnum); */
+  /* if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) */
+  /*   { */
+  /*     DBG (1, "USB READ IO Error in usb_write_then_read, fail devnum=%d\n", */
+  /*          dev->devnum); */
+  /*     return status; */
+  /*   } */
+
+  DBG (10, "    attempting to write...: %d\n", dev->devnum);
+  status = sanei_usb_write_bulk (dev->devnum, cmd, &cmd_size);
+  DBG (10, "    writedone: %d\n", dev->devnum);
+  if (status != SANE_STATUS_GOOD)
+    {
+      DBG (1, "USB WRITE IO Error in usb_write_then_read, launch fail: %d\n",
+           status);
+      return status;
+    }
+
+  debug_packet(cmd, cmd_size, WRITE);
+
+  DBG (10, "    attempting to read...: %d\n", dev->devnum);
+  status = sanei_usb_read_bulk (dev->devnum, buf, &buf_size);
+  DBG (10, "    readdone: %d\n", dev->devnum);
+  if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
+    {
+      DBG (1, "USB READ IO Error in usb_write_then_read, fail devnum=%d\n",
+           dev->devnum);
+      return status;
+    }
+  debug_packet(buf, buf_size, READ);
+  return SANE_STATUS_GOOD;
+}
+
+void
+build_packet(Lexmark_Device * dev, SANE_Byte packet_id, SANE_Byte * buffer){
+  memcpy(buffer, command_with_params_block, command_with_params_block_size);
+  // protocole related... "ID?"
+  buffer[14] = packet_id;
+
+  // mode
+  if (memcmp(dev->val[OPT_MODE].s, "Color", 5) == 0 )
+    buffer[20] = 0x03;
+  else
+    buffer[20] = 0x02;
+
+  // pixel width (swap lower byte -> higher byte)
+  buffer[24] = dev->val[OPT_BR_X].w & 0xFF;
+  buffer[25] = (dev->val[OPT_BR_X].w >> 8) & 0xFF;
+
+  // pixel height (swap lower byte -> higher byte)
+  buffer[28] = dev->val[OPT_BR_Y].w & 0xFF;
+  buffer[29] = (dev->val[OPT_BR_Y].w >> 8) & 0xFF;
+
+  // dpi x (swap lower byte -> higher byte)
+  buffer[40] = dev->val[OPT_RESOLUTION].w & 0xFF;
+  buffer[41] = (dev->val[OPT_RESOLUTION].w >> 8) & 0xFF;
+
+  // dpi y (swap lower byte -> higher byte)
+  buffer[42] = dev->val[OPT_RESOLUTION].w & 0xFF;
+  buffer[43] = (dev->val[OPT_RESOLUTION].w >> 8) & 0xFF;
+}
+
+SANE_Status
+init_options (Lexmark_Device * dev)
+{
+
+  SANE_Option_Descriptor *od;
+
+  DBG (2, "init_options: dev = %p\n", (void *) dev);
+
+  /* number of options */
+  od = &(dev->opt[OPT_NUM_OPTS]);
+  od->name = SANE_NAME_NUM_OPTIONS;
+  od->title = SANE_TITLE_NUM_OPTIONS;
+  od->desc = SANE_DESC_NUM_OPTIONS;
+  od->type = SANE_TYPE_INT;
+  od->unit = SANE_UNIT_NONE;
+  od->size = sizeof (SANE_Word);
+  od->cap = SANE_CAP_SOFT_DETECT;
+  od->constraint_type = SANE_CONSTRAINT_NONE;
+  od->constraint.range = 0;
+  dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+  /* mode - sets the scan mode: Color / Gray */
+  od = &(dev->opt[OPT_MODE]);
+  od->name = SANE_NAME_SCAN_MODE;
+  od->title = SANE_TITLE_SCAN_MODE;
+  od->desc = SANE_DESC_SCAN_MODE;;
+  od->type = SANE_TYPE_STRING;
+  od->unit = SANE_UNIT_NONE;
+  od->size = MAX_OPTION_STRING_SIZE;
+  od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+  od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+  od->constraint.string_list = mode_list;
+  dev->val[OPT_MODE].s = malloc (od->size);
+  if (!dev->val[OPT_MODE].s)
+    return SANE_STATUS_NO_MEM;
+  strcpy (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR);
+
+  /* resolution */
+  od = &(dev->opt[OPT_RESOLUTION]);
+  od->name = SANE_NAME_SCAN_RESOLUTION;
+  od->title = SANE_TITLE_SCAN_RESOLUTION;
+  od->desc = SANE_DESC_SCAN_RESOLUTION;
+  od->type = SANE_TYPE_INT;
+  od->unit = SANE_UNIT_DPI;
+  od->size = sizeof (SANE_Int);
+  od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+  od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+  od->constraint.word_list = dpi_list;
+  dev->val[OPT_RESOLUTION].w = 200;
+
+  /* preview mode */
+  od = &(dev->opt[OPT_PREVIEW]);
+  od->name = SANE_NAME_PREVIEW;
+  od->title = SANE_TITLE_PREVIEW;
+  od->desc = SANE_DESC_PREVIEW;
+  od->size = sizeof (SANE_Word);
+  od->cap = SANE_CAP_INACTIVE;
+  od->type = SANE_TYPE_BOOL;
+  od->constraint_type = SANE_CONSTRAINT_NONE;
+  dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+  /* "Geometry" group: */
+  od = &(dev->opt[OPT_GEOMETRY_GROUP]);
+  od->name = "";
+  od->title = SANE_I18N ("Geometry");
+  od->desc = "";
+  od->type = SANE_TYPE_GROUP;
+  od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+  od->size = 0;
+  od->constraint_type = SANE_CONSTRAINT_NONE;
+  //
+
+  /* top-left x */
+  od = &(dev->opt[OPT_TL_X]);
+  od->name = SANE_NAME_SCAN_TL_X;
+  od->title = SANE_TITLE_SCAN_TL_X;
+  od->desc = SANE_DESC_SCAN_TL_X;
+  od->type = SANE_TYPE_INT;
+  od->cap = SANE_CAP_INACTIVE;
+  od->size = sizeof (SANE_Word);
+  od->unit = SANE_UNIT_PIXEL;
+  od->constraint_type = SANE_CONSTRAINT_RANGE;
+  od->constraint.range = &x_range;
+  dev->val[OPT_TL_X].w = 0;
+
+  /* top-left y */
+  od = &(dev->opt[OPT_TL_Y]);
+  od->name = SANE_NAME_SCAN_TL_Y;
+  od->title = SANE_TITLE_SCAN_TL_Y;
+  od->desc = SANE_DESC_SCAN_TL_Y;
+  od->type = SANE_TYPE_INT;
+  od->cap = SANE_CAP_INACTIVE;
+  od->size = sizeof (SANE_Word);
+  od->unit = SANE_UNIT_PIXEL;
+  od->constraint_type = SANE_CONSTRAINT_RANGE;
+  od->constraint.range = &y_range;
+  dev->val[OPT_TL_Y].w = 0;
+
+  /* bottom-right x */
+  od = &(dev->opt[OPT_BR_X]);
+  od->name = SANE_NAME_SCAN_BR_X;
+  od->title = SANE_TITLE_SCAN_BR_X;
+  od->desc = SANE_DESC_SCAN_BR_X;
+  od->type = SANE_TYPE_INT;
+  od->size = sizeof (SANE_Word);
+  od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+  od->unit = SANE_UNIT_PIXEL;
+  od->constraint_type = SANE_CONSTRAINT_RANGE;
+  od->constraint.range = &x_range;
+  dev->val[OPT_BR_X].w = 1654;
+
+  /* bottom-right y */
+  od = &(dev->opt[OPT_BR_Y]);
+  od->name = SANE_NAME_SCAN_BR_Y;
+  od->title = SANE_TITLE_SCAN_BR_Y;
+  od->desc = SANE_DESC_SCAN_BR_Y;
+  od->type = SANE_TYPE_INT;
+  od->size = sizeof (SANE_Word);
+  od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+  od->unit = SANE_UNIT_PIXEL;
+  od->constraint_type = SANE_CONSTRAINT_RANGE;
+  od->constraint.range = &y_range;
+  dev->val[OPT_BR_Y].w = 2339;
+
+  return SANE_STATUS_GOOD;
+}
+
+/* callback function for sanei_usb_attach_matching_devices
+*/
+static SANE_Status
+attach_one (SANE_String_Const devname)
+{
+  Lexmark_Device *lexmark_device;
+
+  DBG (2, "attach_one: attachLexmark: devname=%s first_device=%p\n",
+       devname, (void *)first_device);
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next){
+    /* already attached devices */
+
+    if (strcmp (lexmark_device->sane.name, devname) == 0){
+      lexmark_device->missing = SANE_FALSE;
+      return SANE_STATUS_GOOD;
+    }
+  }
+
+  lexmark_device = (Lexmark_Device *) malloc (sizeof (Lexmark_Device));
+  if (lexmark_device == NULL)
+    return SANE_STATUS_NO_MEM;
+
+  lexmark_device->sane.name = strdup (devname);
+  if (lexmark_device->sane.name == NULL)
+    return SANE_STATUS_NO_MEM;
+  lexmark_device->sane.vendor = "Lexmark";
+  lexmark_device->sane.model = "X2600 series";
+  lexmark_device->sane.type = "flat bed";
+
+  /* init transfer_buffer */
+  lexmark_device->transfer_buffer = malloc (transfer_buffer_size);
+  if (lexmark_device->transfer_buffer == NULL)
+    return SANE_STATUS_NO_MEM;
+
+  /* Make the pointer to the read buffer null here */
+  lexmark_device->read_buffer = malloc (sizeof (Read_Buffer));
+  if (lexmark_device->read_buffer == NULL)
+    return SANE_STATUS_NO_MEM;
+
+  /* mark device as present */
+  lexmark_device->missing = SANE_FALSE;
+  lexmark_device->device_cancelled = SANE_FALSE;
+  /* insert it a the start of the chained list */
+  lexmark_device->next = first_device;
+  first_device = lexmark_device;
+  num_devices++;
+  DBG (2, "    first_device=%p\n", (void *)first_device);
+
+  return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+scan_devices(){
+  DBG (2, "scan_devices\n");
+  SANE_Char config_line[PATH_MAX];
+  FILE *fp;
+  const char *lp;
+  num_devices = 0;
+
+  // -- free existing device we are doning a full re-scan
+  while (first_device){
+    Lexmark_Device *this_device = first_device;
+    first_device = first_device->next;
+    DBG (2, "    free first_device\n");
+    free(this_device);
+  }
+
+  fp = sanei_config_open (LEXMARK_X2600_CONFIG_FILE);
+  if (!fp)
+    {
+      DBG (2, "    No config no prob...(%s)\n", LEXMARK_X2600_CONFIG_FILE);
+      return SANE_STATUS_GOOD;
+    }
+  while (sanei_config_read (config_line, sizeof (config_line), fp))
+    {
+      if (config_line[0] == '#')
+    continue;		/* ignore line comments */
+
+      lp = sanei_config_skip_whitespace (config_line);
+      /* skip empty lines */
+      if (*lp == 0)
+    continue;
+
+      DBG (4, "    attach_matching_devices(%s)\n", config_line);
+      sanei_usb_init();
+      sanei_usb_attach_matching_devices (config_line, attach_one);
+    }
+
+  fclose (fp);
+  return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+  DBG_INIT ();
+  DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+       version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+  DBG (1, "    SANE lexmark_x2600 backend version %d.%d.%d from %s\n",
+       SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING);
+
+  if (version_code)
+    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);
+
+
+  SANE_Status status = scan_devices();
+  initialized = SANE_TRUE;
+  return status;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
+{
+  SANE_Int index;
+  Lexmark_Device *lexmark_device;
+
+  DBG (2, "sane_get_devices: device_list=%p, local_only=%d num_devices=%d\n",
+       (void *) device_list, local_only, num_devices);
+
+  //sanei_usb_scan_devices ();
+  SANE_Status status = scan_devices();
+
+  if (devlist)
+    free (devlist);
+
+  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+  if (!devlist)
+    return (SANE_STATUS_NO_MEM);
+
+  index = 0;
+  lexmark_device = first_device;
+  while (lexmark_device != NULL)
+    {
+      DBG (2, "    lexmark_device->missing:%d\n",
+           lexmark_device->missing);
+      if (lexmark_device->missing == SANE_FALSE)
+    {
+
+      devlist[index] = &(lexmark_device->sane);
+      index++;
+    }
+      lexmark_device = lexmark_device->next;
+    }
+  devlist[index] = 0;
+
+  *device_list = devlist;
+
+  return status;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+  Lexmark_Device *lexmark_device;
+  SANE_Status status;
+
+  DBG (2, "sane_open: devicename=\"%s\", handle=%p\n", devicename,
+       (void *) handle);
+
+  /* walk the linked list of scanner device until there is a match
+   * with the device name */
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      DBG (10, "    devname from list: %s\n",
+       lexmark_device->sane.name);
+      if (strcmp (devicename, "") == 0
+      || strcmp (devicename, "lexmark") == 0
+      || strcmp (devicename, lexmark_device->sane.name) == 0)
+    break;
+    }
+
+  *handle = lexmark_device;
+
+  status = init_options (lexmark_device);
+  if (status != SANE_STATUS_GOOD)
+    return status;
+
+  DBG(2, "    device `%s' opening devnum: '%d'\n",
+      lexmark_device->sane.name, lexmark_device->devnum);
+  status = sanei_usb_open (lexmark_device->sane.name, &(lexmark_device->devnum));
+  if (status != SANE_STATUS_GOOD)
+    {
+      DBG (1, "     couldn't open device `%s': %s\n",
+           lexmark_device->sane.name,
+       sane_strstatus (status));
+      return status;
+    }
+  else
+    {
+      DBG (2, "    device `%s' successfully opened devnum: '%d'\n",
+           lexmark_device->sane.name, lexmark_device->devnum);
+    }
+
+  return status;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+  Lexmark_Device *lexmark_device;
+
+  //DBG (2, "sane_get_option_descriptor: handle=%p, option = %d\n",
+  //     (void *) handle, option);
+
+  /* Check for valid option number */
+  if ((option < 0) || (option >= NUM_OPTIONS))
+    return NULL;
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      if (lexmark_device == handle)
+    break;
+    }
+
+  if (!lexmark_device)
+    return NULL;
+
+  if (lexmark_device->opt[option].name)
+    {
+      //DBG (2, "    name=%s\n",
+      //     lexmark_device->opt[option].name);
+    }
+
+  return &(lexmark_device->opt[option]);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
+                     void * value, SANE_Word * info)
+{
+  Lexmark_Device *lexmark_device;
+  SANE_Status status;
+  SANE_Word w;
+  SANE_Int res_selected;
+
+  DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+       (void *) handle, option, action, (void *) value, (void *) info);
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next){
+    if (lexmark_device == handle)
+      break;
+  }
+
+
+  if (value == NULL)
+    return SANE_STATUS_INVAL;
+
+  switch (action){
+  case SANE_ACTION_SET_VALUE:
+    if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap)){
+      return SANE_STATUS_INVAL;
+    }
+    /* Make sure boolean values are only TRUE or FALSE */
+    if (lexmark_device->opt[option].type == SANE_TYPE_BOOL){
+      if (!
+          ((*(SANE_Bool *) value == SANE_FALSE)
+           || (*(SANE_Bool *) value == SANE_TRUE)))
+        return SANE_STATUS_INVAL;
+    }
+
+    /* Check range constraints */
+    if (lexmark_device->opt[option].constraint_type ==
+        SANE_CONSTRAINT_RANGE){
+      status =
+        sanei_constrain_value (&(lexmark_device->opt[option]), value,
+                               info);
+      if (status != SANE_STATUS_GOOD){
+        DBG (2, "    SANE_CONTROL_OPTION: Bad value for range\n");
+        return SANE_STATUS_INVAL;
+      }
+    }
+    switch (option){
+    case OPT_NUM_OPTS:
+    case OPT_RESOLUTION:
+      res_selected = *(SANE_Int *) value;
+      // first value is the size of the wordlist!
+      for(int i=1; i<dpi_list_size; i++){
+        DBG (10, "    posible res=%d selected=%d\n", dpi_list[i], res_selected);
+        if(res_selected == dpi_list[i]){
+          lexmark_device->val[option].w = *(SANE_Word *) value;
+        }
+      }
+      break;
+    case OPT_TL_X:
+    case OPT_TL_Y:
+    case OPT_BR_X:
+    case OPT_BR_Y:
+      DBG (2, "    Option value set to %d (%s)\n", *(SANE_Word *) value,
+           lexmark_device->opt[option].name);
+      lexmark_device->val[option].w = *(SANE_Word *) value;
+      if (lexmark_device->val[OPT_TL_X].w >
+          lexmark_device->val[OPT_BR_X].w){
+        w = lexmark_device->val[OPT_TL_X].w;
+        lexmark_device->val[OPT_TL_X].w =
+          lexmark_device->val[OPT_BR_X].w;
+        lexmark_device->val[OPT_BR_X].w = w;
+        if (info)
+          *info |= SANE_INFO_RELOAD_PARAMS;
+      }
+      if (lexmark_device->val[OPT_TL_Y].w >
+          lexmark_device->val[OPT_BR_Y].w){
+        w = lexmark_device->val[OPT_TL_Y].w;
+        lexmark_device->val[OPT_TL_Y].w =
+          lexmark_device->val[OPT_BR_Y].w;
+        lexmark_device->val[OPT_BR_Y].w = w;
+        if (info)
+          *info |= SANE_INFO_RELOAD_PARAMS;
+      }
+      break;
+    case OPT_MODE:
+      strcpy (lexmark_device->val[option].s, value);
+      if (info)
+        *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+      return SANE_STATUS_GOOD;
+    }
+
+
+    if (info != NULL)
+      *info |= SANE_INFO_RELOAD_PARAMS;
+
+    break;
+  case SANE_ACTION_GET_VALUE:
+    switch (option){
+    case OPT_NUM_OPTS:
+    case OPT_RESOLUTION:
+    case OPT_PREVIEW:
+    case OPT_TL_X:
+    case OPT_TL_Y:
+    case OPT_BR_X:
+    case OPT_BR_Y:
+      *(SANE_Word *) value = lexmark_device->val[option].w;
+      //DBG (2, "    Option value = %d (%s)\n", *(SANE_Word *) value,
+      //     lexmark_device->opt[option].name);
+      break;
+    case OPT_MODE:
+      strcpy (value, lexmark_device->val[option].s);
+      break;
+    }
+    break;
+
+  default:
+    return SANE_STATUS_INVAL;
+  }
+
+  return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+  Lexmark_Device *lexmark_device;
+  SANE_Parameters *device_params;
+  SANE_Int width_px;
+
+  DBG (2, "sane_get_parameters: handle=%p, params=%p\n", (void *) handle,
+       (void *) params);
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      if (lexmark_device == handle)
+    break;
+    }
+
+  if (!lexmark_device)
+    return SANE_STATUS_INVAL;
+
+  // res = lexmark_device->val[OPT_RESOLUTION].w;
+  device_params = &(lexmark_device->params);
+
+  width_px =
+    lexmark_device->val[OPT_BR_X].w - lexmark_device->val[OPT_TL_X].w;
+
+  /* 24 bit colour = 8 bits/channel for each of the RGB channels */
+  device_params->pixels_per_line = width_px;
+  device_params->format = SANE_FRAME_RGB; // SANE_FRAME_GRAY
+  device_params->depth = 8;
+  device_params->bytes_per_line =
+    (SANE_Int) (3 * device_params->pixels_per_line);
+
+  if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)
+      != 0)
+    {
+      device_params->format = SANE_FRAME_GRAY;
+      device_params->bytes_per_line =
+        (SANE_Int) (device_params->pixels_per_line);
+    }
+
+  /* geometry in pixels */
+  device_params->last_frame = SANE_TRUE;
+  device_params->lines = -1;//lexmark_device->val[OPT_BR_Y].w;
+
+  DBG (2, "    device_params->pixels_per_line=%d\n",
+       device_params->pixels_per_line);
+  DBG (2, "    device_params->bytes_per_line=%d\n",
+       device_params->bytes_per_line);
+  DBG (2, "    device_params->depth=%d\n",
+       device_params->depth);
+  DBG (2, "    device_params->format=%d\n",
+       device_params->format);
+  DBG (2, "      SANE_FRAME_GRAY: %d\n",
+       SANE_FRAME_GRAY);
+  DBG (2, "      SANE_FRAME_RGB: %d\n",
+       SANE_FRAME_RGB);
+
+  if (params != 0)
+    {
+      params->format = device_params->format;
+      params->last_frame = device_params->last_frame;
+      params->lines = device_params->lines;
+      params->depth = device_params->depth;
+      params->pixels_per_line = device_params->pixels_per_line;
+      params->bytes_per_line = device_params->bytes_per_line;
+    }
+  return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+  Lexmark_Device * lexmark_device;
+  SANE_Status status;
+  SANE_Byte * cmd = (SANE_Byte *) malloc
+    (command_with_params_block_size * sizeof (SANE_Byte));
+  if (cmd == NULL)
+    return SANE_STATUS_NO_MEM;
+
+  DBG (2, "sane_start: handle=%p initialized=%d\n", (void *) handle, initialized);
+
+  if (!initialized)
+    return SANE_STATUS_INVAL;
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      if (lexmark_device == handle)
+    break;
+    }
+
+  if(lexmark_device == NULL){
+    DBG (2, "    Cannot find device\n");
+    free(cmd);
+    return SANE_STATUS_IO_ERROR;
+  }
+
+  lexmark_device->read_buffer->data = NULL;
+  lexmark_device->read_buffer->size = 0;
+  lexmark_device->read_buffer->last_line_bytes_read = 0;
+  lexmark_device->read_buffer->image_line_no = 0;
+  lexmark_device->read_buffer->write_byte_counter = 0;
+  lexmark_device->read_buffer->read_byte_counter = 0;
+  lexmark_device->eof = SANE_FALSE;
+  lexmark_device->device_cancelled = SANE_FALSE;
+
+  //launch scan commands
+  status = usb_write_then_read(lexmark_device, command1_block,
+                               command1_block_size);
+  if (status != SANE_STATUS_GOOD){
+    free(cmd);
+    return status;
+  }
+  status = usb_write_then_read(lexmark_device, command2_block,
+                               command2_block_size);
+  if (status != SANE_STATUS_GOOD){
+    free(cmd);
+    return status;
+  }
+  build_packet(lexmark_device, 0x05, cmd);
+  status = usb_write_then_read(lexmark_device, cmd,
+                               command_with_params_block_size);
+  if (status != SANE_STATUS_GOOD){
+    free(cmd);
+    return status;
+  }
+  build_packet(lexmark_device, 0x01, cmd);;
+  status = usb_write_then_read(lexmark_device, cmd,
+                               command_with_params_block_size);
+  if (status != SANE_STATUS_GOOD){
+    free(cmd);
+    return status;
+  }
+
+  free(cmd);
+  return SANE_STATUS_GOOD;
+}
+
+
+void debug_packet(const SANE_Byte * source, SANE_Int source_size, Debug_Packet dp){
+  if(dp == READ){
+    DBG (10, "source READ <<<  size=%d\n", source_size);
+  }else{
+    DBG (10, "source WRITE >>>  size=%d\n", source_size);
+  }
+
+  DBG (10, "       %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+       source[0], source[1], source[2], source[3], source[4], source[5], source[6], source[7]);
+  DBG (10, "       %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+       source[8], source[9], source[10], source[11], source[12], source[13], source[14], source[15]);
+  int debug_offset = 4092;
+  if(source_size > debug_offset){
+    DBG (10, "       %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+         source[source_size-16-debug_offset],
+         source[source_size-15-debug_offset],
+         source[source_size-14-debug_offset],
+         source[source_size-13-debug_offset],
+         source[source_size-12-debug_offset],
+         source[source_size-11-debug_offset],
+         source[source_size-10-debug_offset],
+         source[source_size-9-debug_offset]);
+    DBG (10, "       %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+         source[source_size-8-debug_offset],
+         source[source_size-7-debug_offset],
+         source[source_size-6-debug_offset],
+         source[source_size-5-debug_offset],
+         source[source_size-4-debug_offset],
+         source[source_size-3-debug_offset],
+         source[source_size-2-debug_offset],
+         source[source_size-1-debug_offset]);
+  }
+  return;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+       SANE_Int max_length, SANE_Int * length)
+{
+  Lexmark_Device * lexmark_device;
+  SANE_Status status;
+  size_t size = transfer_buffer_size;
+  //SANE_Byte buf[size];
+  DBG (1, "\n");
+  DBG (1, "sane_read max_length=%d:\n", max_length);
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      if (lexmark_device == handle)
+    break;
+    }
+
+  if (lexmark_device->device_cancelled == SANE_TRUE) {
+      DBG (10, "device_cancelled=True \n");
+      usb_write_then_read(lexmark_device, command_cancel1_block,
+                          command_cancel_size);
+      usb_write_then_read(lexmark_device, command_cancel2_block,
+                          command_cancel_size);
+      usb_write_then_read(lexmark_device, command_cancel1_block,
+                          command_cancel_size);
+      usb_write_then_read(lexmark_device, command_cancel2_block,
+                          command_cancel_size);
+      // to empty buffers
+      status = sanei_usb_read_bulk (
+          lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+      if(status == SANE_STATUS_GOOD){
+        status = sanei_usb_read_bulk (
+            lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+      }
+      if(status == SANE_STATUS_GOOD){
+        status = sanei_usb_read_bulk (
+            lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+      }
+
+      return status;
+  }
+
+  //status = sanei_usb_read_bulk (lexmark_device->devnum, buf, &size);
+  if(!lexmark_device->eof){
+      DBG (1, "    usb_read\n");
+      status = sanei_usb_read_bulk (
+          lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+      if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
+        {
+          DBG (1, "    USB READ Error in sanei_usb_read_bulk, cannot read devnum=%d status=%d size=%ld\n",
+               lexmark_device->devnum, status, size);
+          return status;
+        }
+      DBG (1, "    usb_read done size=%ld\n", size);
+      debug_packet(lexmark_device->transfer_buffer, size, READ);
+  }else{
+    DBG (1, "    no usb_read eof reached\n");
+  }
+
+  // is last data packet ?
+  if (!lexmark_device->eof && memcmp(last_data_packet, lexmark_device->transfer_buffer, last_data_packet_size) == 0){
+
+    // we may still have data left to send in our buffer device->read_buffer->data
+    //length = 0;
+    //return SANE_STATUS_EOF;
+    lexmark_device->eof = SANE_TRUE;
+    DBG (1, "    EOF PACKET no more data from scanner\n");
+
+    return SANE_STATUS_GOOD;
+  }
+  // cancel packet received?
+  if (memcmp(cancel_packet, lexmark_device->transfer_buffer, cancel_packet_size) == 0){
+    length = 0;
+    return SANE_STATUS_CANCELLED;
+  }
+  if (memcmp(empty_line_data_packet, lexmark_device->transfer_buffer, empty_line_data_packet_size) == 0){
+    return SANE_STATUS_GOOD;
+  }
+  if (memcmp(unknown_a_data_packet, lexmark_device->transfer_buffer, unknown_a_data_packet_size) == 0){
+    return SANE_STATUS_GOOD;
+  }
+  if (memcmp(unknown_b_data_packet, lexmark_device->transfer_buffer, unknown_b_data_packet_size) == 0){
+    return SANE_STATUS_GOOD;
+  }
+  if (memcmp(unknown_c_data_packet, lexmark_device->transfer_buffer, unknown_c_data_packet_size) == 0){
+    return SANE_STATUS_GOOD;
+  }
+  if (memcmp(unknown_d_data_packet, lexmark_device->transfer_buffer, unknown_d_data_packet_size) == 0){
+    return SANE_STATUS_GOOD;
+  }
+  if (memcmp(unknown_e_data_packet, lexmark_device->transfer_buffer, unknown_e_data_packet_size) == 0){
+    return SANE_STATUS_GOOD;
+  }
+
+  status = clean_and_copy_data(
+      lexmark_device->transfer_buffer,
+      size,
+      data,
+      length,
+      lexmark_device->params.format,
+      max_length,
+      handle);
+
+  return status;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+  DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n",
+       (void *) handle, non_blocking);
+
+  if (non_blocking)
+    return SANE_STATUS_UNSUPPORTED;
+
+  return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+  DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", (void *) handle,
+       fd ? "!=" : "=");
+
+  return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+  Lexmark_Device * lexmark_device;
+
+  DBG (2, "sane_cancel: handle = %p\n", (void *) handle);
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      if (lexmark_device == handle)
+    break;
+    }
+  sanei_usb_reset (lexmark_device->devnum);
+  lexmark_device->device_cancelled = SANE_TRUE;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+  Lexmark_Device * lexmark_device;
+
+  DBG (2, "sane_close: handle=%p\n", (void *) handle);
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = lexmark_device->next)
+    {
+      if (lexmark_device == handle)
+    break;
+    }
+
+  sanei_usb_close (lexmark_device->devnum);
+}
+
+void
+sane_exit (void)
+{
+  Lexmark_Device *lexmark_device, *next_lexmark_device;
+
+  DBG (2, "sane_exit\n");
+
+  if (!initialized)
+    return;
+
+  for (lexmark_device = first_device; lexmark_device;
+       lexmark_device = next_lexmark_device)
+    {
+      next_lexmark_device = lexmark_device->next;
+      free (lexmark_device->transfer_buffer);
+      free (lexmark_device->read_buffer);
+      free (lexmark_device);
+    }
+
+  if (devlist)
+    free (devlist);
+
+  sanei_usb_exit();
+  initialized = SANE_FALSE;
+
+}
diff --git a/backend/lexmark_x2600.conf.in b/backend/lexmark_x2600.conf.in
new file mode 100644
index 0000000..c030822
--- /dev/null
+++ b/backend/lexmark_x2600.conf.in
@@ -0,0 +1,2 @@
+# X26xx series
+usb 0x043d 0x011d
diff --git a/backend/lexmark_x2600.h b/backend/lexmark_x2600.h
new file mode 100644
index 0000000..e53b48d
--- /dev/null
+++ b/backend/lexmark_x2600.h
@@ -0,0 +1,151 @@
+/* lexmark_x2600.c: SANE backend for Lexmark x2600 scanners.
+
+   (C) 2023 "Benoit Juin" <benoit.juin@gmail.com>
+
+   This file is part of the SANE package.
+
+   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, see <https://www.gnu.org/licenses/>.
+
+   As a special exception, the authors of SANE give permission for
+   additional uses of the libraries contained in this release of SANE.
+
+   The exception is that, if you link a SANE library with other files
+   to produce an executable, this does not by itself cause the
+   resulting executable to be covered by the GNU General Public
+   License.  Your use of that executable is in no way restricted on
+   account of linking the SANE library code into it.
+
+   This exception does not, however, invalidate any other reasons why
+   the executable file might be covered by the GNU General Public
+   License.
+
+   If you submit changes to SANE to the maintainers to be included in
+   a subsequent release, you agree by submitting the changes that
+   those changes may be distributed with this exception intact.
+
+   If you write modifications of your own for SANE, it is your choice
+   whether to permit this exception to apply to your modifications.
+   If you do not wish that, delete this exception notice.
+
+   **************************************************************************/
+#ifndef LEXMARK_X2600_H
+#define LEXMARK_X2600_H
+#define BACKEND_NAME lexmark_x2600
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "../include/_stdint.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_backend.h"
+
+
+typedef struct Read_Buffer
+{
+  SANE_Int gray_offset;
+  SANE_Int max_gray_offset;
+  SANE_Int region;
+  SANE_Int red_offset;
+  SANE_Int green_offset;
+  SANE_Int blue_offset;
+  SANE_Int max_red_offset;
+  SANE_Int max_green_offset;
+  SANE_Int max_blue_offset;
+  SANE_Byte *data;
+  SANE_Byte *readptr;
+  SANE_Byte *writeptr;
+  SANE_Byte *max_writeptr;
+  size_t size;
+  size_t linesize;
+  size_t last_line_bytes_read;
+  SANE_Bool empty;
+  SANE_Int image_line_no;
+  SANE_Int write_byte_counter;
+  SANE_Int read_byte_counter;
+}
+Read_Buffer;
+
+
+typedef enum
+{
+  OPT_NUM_OPTS = 0,
+  OPT_MODE,
+  OPT_RESOLUTION,
+  OPT_PREVIEW,
+
+  OPT_GEOMETRY_GROUP,
+  OPT_TL_X,			/* top-left x */
+  OPT_TL_Y,			/* top-left y */
+  OPT_BR_X,			/* bottom-right x */
+  OPT_BR_Y,			/* bottom-right y */
+
+  /* must come last: */
+  NUM_OPTIONS
+}
+Lexmark_Options;
+
+typedef enum
+{
+  READ = 0,
+  WRITE = 1,
+}
+Debug_Packet;
+
+
+typedef struct Lexmark_Device
+{
+  struct Lexmark_Device *next;
+  SANE_Bool missing;	/**< devices has been unplugged or swtiched off */
+
+  SANE_Device sane;
+  SANE_Option_Descriptor opt[NUM_OPTIONS];
+  Option_Value val[NUM_OPTIONS];
+  SANE_Parameters params;
+  SANE_Int devnum;
+  long data_size;
+  SANE_Bool initialized;
+  SANE_Bool eof;
+  SANE_Int x_dpi;
+  SANE_Int y_dpi;
+  long data_ctr;
+  SANE_Bool device_cancelled;
+  SANE_Int cancel_ctr;
+  SANE_Byte *transfer_buffer;
+  size_t bytes_read;
+  size_t bytes_remaining;
+  size_t bytes_in_buffer;
+  SANE_Byte *read_pointer;
+  Read_Buffer *read_buffer;
+}
+Lexmark_Device;
+
+
+void debug_packet(const SANE_Byte * source, SANE_Int source_size, Debug_Packet dp);
+
+#endif /* LEXMARK_X2600_H */
diff --git a/backend/net.c b/backend/net.c
index d16119a..7b1ea05 100644
--- a/backend/net.c
+++ b/backend/net.c
@@ -55,6 +55,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <pwd.h>
 #ifdef HAVE_LIBC_H
 # include <libc.h> /* NeXTStep/OpenStep */
 #endif
@@ -311,6 +312,32 @@ add_device (const char *name, Net_Device ** ndp)
 }
 #endif /* NET_USES_AF_INDEP */
 
+/* Calls getpwuid_r(). The return value must be freed by the caller. */
+char* get_current_username()
+{
+  long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+  if (bufsize == -1)
+  {
+    return NULL;
+  }
+
+  char* buf = (char*) malloc(bufsize);
+  if (buf == NULL)
+  {
+    return NULL;
+  }
+
+  struct passwd pwd;
+  struct passwd *result;
+  if (getpwuid_r(getuid(), &pwd, buf, bufsize, &result) != 0 || result == NULL)
+  {
+    return NULL;
+  }
+
+  /* pw_name is allocated somewhere within buf, so we use memmove() */
+  memmove(buf, pwd.pw_name, strlen(pwd.pw_name));
+  return buf;
+}
 
 #ifdef NET_USES_AF_INDEP
 static SANE_Status
@@ -484,12 +511,14 @@ connect_dev (Net_Device * dev)
   /* exchange version codes with the server: */
   req.version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR,
 					SANEI_NET_PROTOCOL_VERSION);
-  req.username = getlogin ();
+  req.username = get_current_username();
   DBG (2, "connect_dev: net_init (user=%s, local version=%d.%d.%d)\n",
        req.username, V_MAJOR, V_MINOR, SANEI_NET_PROTOCOL_VERSION);
   sanei_w_call (&dev->wire, SANE_NET_INIT,
 		(WireCodecFunc) sanei_w_init_req, &req,
 		(WireCodecFunc) sanei_w_init_reply, &reply);
+  free(req.username);
+  req.username = NULL;
 
   if (dev->wire.status != 0)
     {
diff --git a/backend/pixma/pixma.c b/backend/pixma/pixma.c
index 72385b2..cd89e99 100644
--- a/backend/pixma/pixma.c
+++ b/backend/pixma/pixma.c
@@ -851,18 +851,48 @@ control_option (pixma_sane_t * ss, SANE_Int n,
     case opt_source:
       if ((cfg->cap & (PIXMA_CAP_ADF|PIXMA_CAP_ADFDUP|PIXMA_CAP_TPU))
           && (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO))
-        { /* new source selected: flatbed, ADF, TPU, ... */
-          /* to avoid fatal errors,
-           * select first entry of dynamic mode_list
-           * identifiers are unknown here */
-          OVAL (opt_mode).w = ss->mode_map[0];
+        {
+          /* new source selected: flatbed, ADF, TPU, ... */
+          pixma_scan_mode_t curr_mode = ss->mode_map[OVAL (opt_mode).w];
+          SANE_Word curr_res = OVAL (opt_resolution).w;
+
           /* recreate dynamic lists */
           create_mode_list (ss);
           create_dpi_list (ss);
-          /* to avoid fatal errors,
-           * select first entry of dynamic dpi_list
-           * identifiers are unknown here */
-          OVAL (opt_resolution).w = ss->dpi_list[1];
+
+          /*
+           * Check to see if the mode and res are still valid.
+           * Replace with default mode or closest res if not.
+           *
+           */
+          for (SANE_Int mode_idx = 0;; mode_idx++)
+            {
+              if (!ss->mode_list[mode_idx])
+                {
+                  OVAL (opt_mode).w = 0;
+                  break;
+                }
+              if (curr_mode == ss->mode_map[mode_idx])
+                {
+                  OVAL (opt_mode).w = mode_idx;
+                  break;
+                }
+            }
+
+          for (SANE_Int res_idx = 1;; res_idx++)
+            {
+              if (res_idx > ss->dpi_list[0])
+                {
+                  OVAL (opt_resolution).w = ss->dpi_list[1];
+                  break;
+                }
+              if (ss->dpi_list[res_idx] >= curr_res)
+                {
+                  OVAL (opt_resolution).w = ss->dpi_list[res_idx];
+                  break;
+                }
+            }
+
           if (ss->mode_map[OVAL (opt_mode).w] == PIXMA_SCAN_MODE_LINEART)
             { /* lineart */
               enable_option (ss, opt_threshold, SANE_TRUE);
diff --git a/backend/pixma/pixma_imageclass.c b/backend/pixma/pixma_imageclass.c
index 6a485de..83b19bd 100644
--- a/backend/pixma/pixma_imageclass.c
+++ b/backend/pixma/pixma_imageclass.c
@@ -118,6 +118,7 @@
 #define MF260_PID  0x27f4
 #define MF740_PID  0x27fb
 #define MF743_PID  0x27fc
+#define MF750_PID  0x2885
 #define MF640_PID  0x27fe
 #define MF645_PID  0x27fd
 #define MF440_PID  0x2823
@@ -989,6 +990,7 @@ const pixma_config_t pixma_iclass_devices[] = {
   DEV ("Canon i-SENSYS MF260 Series", "MF260", MF260_PID, 600, 0, 640, 1050, PIXMA_CAP_JPEG | PIXMA_CAP_ADFDUP),
   DEV ("Canon i-SENSYS MF740 Series", "MF740", MF740_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
   DEV ("Canon i-SENSYS MF741C/743C", "MF741C/743C", MF743_PID, 600, 300, 640, 1050, PIXMA_CAP_ADFDUP),       /* ADFDUP restricted to 300dpi */
+  DEV ("Canon i-SENSYS MF750 Series", "MF750C", MF750_PID, 600, 300, 640, 1050, PIXMA_CAP_ADFDUP),
   DEV ("Canon i-SENSYS MF640 Series", "MF642C/643C/644C", MF640_PID, 600, 0, 640, 1050, PIXMA_CAP_ADFDUP),
   DEV ("Canon i-SENSYS MF645C", "MF645C", MF645_PID, 600, 0, 637, 877, PIXMA_CAP_ADFDUP),                    /* max. w = 216mm */
   DEV ("Canon i-SENSYS MF440 Series", "MF440", MF440_PID, 600, 300, 637, 877, PIXMA_CAP_ADFDUP),
diff --git a/backend/pixma/pixma_io_sanei.c b/backend/pixma/pixma_io_sanei.c
index ae780b3..394523e 100644
--- a/backend/pixma/pixma_io_sanei.c
+++ b/backend/pixma/pixma_io_sanei.c
@@ -62,13 +62,6 @@
 # define UNUSED(v)
 #endif
 
-/* MAC OS X does not support timeouts in darwin/libusb interrupt reads
- * This is a very basic turnaround for MAC OS X
- * Button scan will not work with this wrapper */
-#ifdef __APPLE__
-# define sanei_usb_read_int sanei_usb_read_bulk
-#endif
-
 
 struct pixma_io_t
 {
diff --git a/backend/pixma/pixma_mp150.c b/backend/pixma/pixma_mp150.c
index 171eccf..99024ca 100644
--- a/backend/pixma/pixma_mp150.c
+++ b/backend/pixma/pixma_mp150.c
@@ -336,6 +336,32 @@
 #define TS2400_PID 0x1108
 #define TS2600_PID 0x1107
 
+#define TS8630_PID 0x18F8
+#define XK110_PID 0x18F9
+#define GX3000_PID 0x18F1
+#define GX4000_PID 0x18F2
+
+/* 2023 new device (untested) */
+#define G3070_PID 0x18F4
+#define G2070_PID 0x18F5
+#define G4070_PID 0x18F3
+#define G3030_PID 0x1105
+#define G2030_PID 0x1106
+#define TC20M_PID 0x18FC
+#define TC5200M_PID 0x18FF
+
+#define TS8700_PID 0x1118
+#define XK120_PID 0x1119
+#define GX1000_PID 0x110B
+#define GX2000_PID 0x110D
+#define TS6630_PID 0x114E
+#define TS7700_PID 0x110F
+#define TS7600i_PID 0x114F
+#define TS6730_PID 0x1150
+#define TR7800_PID 0x1151
+#define TS7700i_PID 0x1152
+#define TS7700A_PID 0x1111
+#define GX6500_PID 0x1148
 
 /* Generation 4 XML messages that encapsulates the Pixma protocol messages */
 #define XML_START_1   \
@@ -1966,6 +1992,30 @@ const pixma_config_t pixma_mp150_devices[] = {
   DEVICE ("Canon PIXMA TS5400 Series", "TS5400", TS5400_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
   DEVICE ("Canon PIXMA TS2400 Series", "TS2400", TS2400_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
   DEVICE ("Canon PIXMA TS2600 Series", "TS2600", TS2600_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TS8630 Series", "TS8630", TS8630_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXUS XK110 Series", "XK110", XK110_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA GX3000 Series", "GX3000", GX3000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+  DEVICE ("Canon PIXMA GX4000 Series", "GX4000", GX4000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+  DEVICE ("Canon PIXMA G3070", "G3070", G3070_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA G2070", "G2070", G2070_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA G4070", "G4070", G4070_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA G3030", "G3030", G3030_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA G2030", "G2030", G2030_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TC-20M", "TC-20M", TC20M_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TC-5200M", "TC-5200M", TC5200M_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+
+  DEVICE ("Canon PIXMA TS8700 series", "TS8700", TS8700_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA XK120 series", "XK120", XK120_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA GX1000 series", "GX1000", GX1000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+  DEVICE ("Canon PIXMA GX2000 series", "GX2000", GX2000_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
+  DEVICE ("Canon PIXMA TS6630 series", "TS6630", TS6630_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TS7700 series", "TS7700", TS7700_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TS7600i series", "TS7600i", TS7600i_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TS6730 series", "TS6730", TS6730_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TR7800 series", "TR7800", TR7800_PID, 0, 600, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF | PIXMA_CAP_ADF_JPEG),
+  DEVICE ("Canon PIXMA TS7700i series", "TS7700i", TS7700i_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA TS7700A series", "TS7700A", TS7700A_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS),
+  DEVICE ("Canon PIXMA GX6500 series", "GX6500", GX6500_PID, 0, 1200, 0, 0, 638, 877, PIXMA_CAP_CIS | PIXMA_CAP_ADF),
 
   END_OF_DEVICE_LIST
 };
diff --git a/backend/snapscan-options.c b/backend/snapscan-options.c
index 719c1e4..310cb4b 100644
--- a/backend/snapscan-options.c
+++ b/backend/snapscan-options.c
@@ -138,6 +138,16 @@ static const SANE_Range y_range_tpo_default =
     SANE_FIX (0.0), SANE_FIX (180.0), 0
 };        /* mm */
 
+/* TPO range for the Agfa Arcus 1200 */
+static const SANE_Range x_range_tpo_arcus =
+{
+    SANE_FIX (0.0), SANE_FIX (203.0), 0
+};        /* mm */
+static const SANE_Range y_range_tpo_arcus =
+{
+    SANE_FIX (0.0), SANE_FIX (254.0), 0
+};        /* mm */
+
 /* TPO range for the Agfa 1236 */
 static const SANE_Range x_range_tpo_1236 =
 {
@@ -330,6 +340,10 @@ static void init_options (SnapScan_Scanner * ps)
         x_range_tpo = x_range_tpo_3490;
         y_range_tpo = y_range_tpo_3490;
         break;
+    case ARCUS1200:
+        x_range_tpo = x_range_tpo_arcus;
+        y_range_tpo = y_range_tpo_arcus;
+        break;
     default:
         x_range_tpo = x_range_tpo_default;
         y_range_tpo = y_range_tpo_default;
diff --git a/backend/test.c b/backend/test.c
index ea73290..4663a16 100644
--- a/backend/test.c
+++ b/backend/test.c
@@ -1432,6 +1432,43 @@ read_option (SANE_String line, SANE_String option_string,
   return SANE_STATUS_GOOD;
 }
 
+
+static SANE_Status
+read_option_str_list (SANE_String line, SANE_String option_string,
+                      parameter_type p_type, void *value,
+                      SANE_String_Const *string_list)
+{
+  SANE_String new_value = NULL;
+
+  SANE_Status ret = read_option (line, option_string, p_type, &new_value);
+  if (ret != SANE_STATUS_GOOD)
+    {
+      if (new_value)
+        {
+          free(new_value);
+        }
+      return ret;
+    }
+
+  for (SANE_String_Const *option = string_list; *option; option++)
+    {
+      if (strcmp (*option, new_value) == 0)
+        {
+
+          if (*(SANE_String*) value)
+            {
+              free (*(SANE_String*) value);
+            }
+          *(SANE_String*) value = new_value;
+
+          return SANE_STATUS_GOOD;
+        }
+    }
+
+  return SANE_STATUS_INVAL;
+}
+
+
 static SANE_Status
 reader_process (Test_Device * test_device, SANE_Int fd)
 {
@@ -1636,7 +1673,6 @@ print_options (Test_Device * test_device)
 
 /***************************** SANE API ****************************/
 
-
 SANE_Status
 sane_init (SANE_Int * __sane_unused__ version_code, SANE_Auth_Callback __sane_unused__ authorize)
 {
@@ -1736,20 +1772,23 @@ sane_init (SANE_Int * __sane_unused__ version_code, SANE_Auth_Callback __sane_un
 
 	  DBG (5, "sane_init: config file line %3d: `%s'\n",
 	       linenumber, line);
+
 	  if (read_option (line, "number_of_devices", param_int,
 			   &init_number_of_devices) == SANE_STATUS_GOOD)
 	    continue;
-	  if (read_option (line, "mode", param_string,
-			   &init_mode) == SANE_STATUS_GOOD)
-	    continue;
+
+          if (read_option_str_list (line, "mode", param_string,
+                                    &init_mode, mode_list) == SANE_STATUS_GOOD)
+              continue;
+
 	  if (read_option (line, "hand-scanner", param_bool,
 			   &init_hand_scanner) == SANE_STATUS_GOOD)
 	    continue;
 	  if (read_option (line, "three-pass", param_bool,
 			   &init_three_pass) == SANE_STATUS_GOOD)
 	    continue;
-	  if (read_option (line, "three-pass-order", param_string,
-			   &init_three_pass_order) == SANE_STATUS_GOOD)
+	  if (read_option_str_list (line, "three-pass-order", param_string,
+	                            &init_three_pass_order, order_list) == SANE_STATUS_GOOD)
 	    continue;
 	  if (read_option (line, "resolution_min", param_fixed,
 			   &resolution_range.min) == SANE_STATUS_GOOD)
@@ -1766,11 +1805,11 @@ sane_init (SANE_Int * __sane_unused__ version_code, SANE_Auth_Callback __sane_un
 	  if (read_option (line, "depth", param_int,
 			   &init_depth) == SANE_STATUS_GOOD)
 	    continue;
-	  if (read_option (line, "scan-source", param_string,
-			   &init_scan_source) == SANE_STATUS_GOOD)
+	  if (read_option_str_list (line, "scan-source", param_string,
+	                            &init_scan_source, source_list) == SANE_STATUS_GOOD)
 	    continue;
-	  if (read_option (line, "test-picture", param_string,
-			   &init_test_picture) == SANE_STATUS_GOOD)
+	  if (read_option_str_list (line, "test-picture", param_string,
+	                               &init_test_picture, test_picture_list) == SANE_STATUS_GOOD)
 	    continue;
 	  if (read_option (line, "invert-endianess", param_bool,
 			   &init_invert_endianess) == SANE_STATUS_GOOD)
@@ -1787,8 +1826,8 @@ sane_init (SANE_Int * __sane_unused__ version_code, SANE_Auth_Callback __sane_un
 	  if (read_option (line, "read-delay-duration", param_int,
 			   &init_read_delay_duration) == SANE_STATUS_GOOD)
 	    continue;
-	  if (read_option (line, "read-status-code", param_string,
-			   &init_read_status_code) == SANE_STATUS_GOOD)
+	  if (read_option_str_list (line, "read-status-code", param_string,
+	                            &init_read_status_code, read_status_code_list) == SANE_STATUS_GOOD)
 	    continue;
 	  if (read_option (line, "ppl-loss", param_int,
 			   &init_ppl_loss) == SANE_STATUS_GOOD)
diff --git a/backend/xerox_mfp-usb.c b/backend/xerox_mfp-usb.c
index 6ef1eea..519aba6 100644
--- a/backend/xerox_mfp-usb.c
+++ b/backend/xerox_mfp-usb.c
@@ -21,6 +21,21 @@
 
 extern int sanei_debug_xerox_mfp;
 
+static int
+xerox_need_clear_halt()
+{
+    char *env;
+    int workaround = 0;
+
+    env = getenv("SANE_XEROX_USB_HALT_WORKAROUND");
+    if (env) {
+        workaround = atoi(env);
+        DBG(5, "xerox_need_clear_halt: workaround: %d\n", workaround);
+        return workaround;
+    }
+    return 0;
+}
+
 int
 usb_dev_request(struct device *dev,
                 SANE_Byte *cmd, size_t cmdlen,
@@ -70,7 +85,9 @@ usb_dev_open(struct device *dev)
         dev->dn = -1;
         return status;
     }
-    sanei_usb_clear_halt(dev->dn);
+    if (xerox_need_clear_halt()) {
+        sanei_usb_clear_halt(dev->dn);
+    }
     return SANE_STATUS_GOOD;
 }
 
@@ -92,7 +109,9 @@ usb_dev_close(struct device *dev)
             ret_cancel(dev, 0);
     }
 
-    sanei_usb_clear_halt(dev->dn);	/* unstall for next users */
+    if (xerox_need_clear_halt()) {
+        sanei_usb_clear_halt(dev->dn);	/* unstall for next users */
+    }
     sanei_usb_close(dev->dn);
     dev->dn = -1;
 }
diff --git a/backend/xerox_mfp.c b/backend/xerox_mfp.c
index fc3d94f..0821754 100644
--- a/backend/xerox_mfp.c
+++ b/backend/xerox_mfp.c
@@ -209,14 +209,15 @@ static int isSupportedDevice(struct device __sane_unused__ *dev)
 #ifdef HAVE_LIBJPEG
     /* Checking device which supports JPEG Lossy compression for color scanning*/
     if (dev->compressionTypes & (1 << 6)) {
-	/* blacklist malfunctioning device(s) */
-	if (!strncmp(dev->sane.model, "SCX-4500W", 9) ||
-            !strncmp(dev->sane.model, "C460", 4) ||
-	    !!strstr(dev->sane.model, "CLX-3170") ||
-            !!strstr(dev->sane.model, "4x24") ||
-            !!strstr(dev->sane.model, "4x28") ||
-	    !strncmp(dev->sane.model, "M288x", 5))
-	    return 0;
+        /* blacklist malfunctioning device(s) */
+        if (!strncmp (dev->sane.model, "SCX-4500W", 9)
+            || !strncmp (dev->sane.model, "C460", 4)
+            || !!strstr (dev->sane.model, "WorkCentre 3225")
+            || !!strstr (dev->sane.model, "CLX-3170")
+            || !!strstr (dev->sane.model, "4x24")
+            || !!strstr (dev->sane.model, "4x28")
+            || !strncmp (dev->sane.model, "M288x", 5))
+            return 0;
         return 1;
     } else
         return 0;
@@ -246,7 +247,7 @@ static void dbg_dump(struct device *dev)
     for (i = 0; i < dlen; i++, dptr += 3)
         sprintf(dptr, " %02x", dev->res[i]);
 
-    DBG(5, "[%lu]%s%s\n", (u_long)dev->reslen, dbuf,
+    DBG(5, "[%zu]%s%s\n", dev->reslen, dbuf,
         (dlen < (int)dev->reslen)? "..." : "");
 }
 
@@ -273,8 +274,8 @@ static int dev_command(struct device *dev, SANE_Byte *cmd, size_t reqlen)
     }
 
     dev->state = 0;
-    DBG(4, ":: dev_command(%s[%#x], %lu)\n", str_cmd(cmd[2]), cmd[2],
-        (u_long)reqlen);
+    DBG(4, ":: dev_command(%s[%#x], %zu)\n", str_cmd(cmd[2]), cmd[2],
+        reqlen);
     status = dev->io->dev_request(dev, cmd, sendlen, res, &dev->reslen);
     if (status != SANE_STATUS_GOOD) {
         DBG(1, "%s: dev_request: %s\n", __func__, sane_strstatus(status));
@@ -289,8 +290,8 @@ static int dev_command(struct device *dev, SANE_Byte *cmd, size_t reqlen)
 
     /* normal command reply, some sanity checking */
     if (dev->reslen < reqlen) {
-        DBG(1, "%s: illegal response len %lu, need %lu\n",
-            __func__, (u_long)dev->reslen, (u_long)reqlen);
+        DBG(1, "%s: illegal response len %zu, need %zu\n",
+            __func__, dev->reslen, reqlen);
         dev->state = SANE_STATUS_IO_ERROR;
         return 0;
     } else {
@@ -306,14 +307,14 @@ static int dev_command(struct device *dev, SANE_Byte *cmd, size_t reqlen)
         }
         pktlen = dev->res[2] + 3;
         if (dev->reslen != pktlen) {
-            DBG(2, "%s: illegal response len %lu, should be %lu\n",
-                __func__, (u_long)pktlen, (u_long)dev->reslen);
+            DBG(2, "%s: illegal response len %zu, should be %zu\n",
+                __func__, pktlen, dev->reslen);
             dev->state = SANE_STATUS_IO_ERROR;
             return 0;
         }
         if (dev->reslen > reqlen)
-            DBG(2, "%s: too big packet len %lu, need %lu\n",
-                __func__, (u_long)dev->reslen, (u_long)reqlen);
+            DBG(2, "%s: too big packet len %zu, need %zu\n",
+                __func__, dev->reslen, reqlen);
     }
 
     dev->state = 0;
@@ -1317,7 +1318,7 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
         return SANE_STATUS_EOF;
 
     /* if there is no data to read or output from buffer */
-    if (!dev->blocklen && dev->datalen <= PADDING_SIZE) {
+    if (!dev->blocklen && (dev->datalen <= PADDING_SIZE || dev->final_block)) {
 
         /* copying uncompressed data */
         if (dev->composition == MODE_RGB24 &&
@@ -1331,6 +1332,13 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
 		    dev->total_out_size += *lenp;
                 return SANE_STATUS_GOOD;
             }
+        } else if (dev->composition != MODE_RGB24) {
+            int diff = dev->total_img_size - dev->total_out_size;
+            int bufLen = (diff < maxlen) ? diff : maxlen;
+            if (diff > 0 && copy_plain_trim(dev, buf, bufLen, lenp) > 0) {
+                dev->total_out_size += *lenp;
+                return SANE_STATUS_GOOD;
+            }
         }
 
         /* and we don't need to acquire next block */
@@ -1381,23 +1389,27 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp)
         int olen; /* output len */
 
         /* read as much data into the buffer */
-        datalen = DATAROOM(dev) & USB_BLOCK_MASK;
+        datalen = MIN(dev->blocklen, DATAROOM(dev) & USB_BLOCK_MASK);
         while (datalen && dev->blocklen) {
             SANE_Byte *rbuf = dev->data + DATATAIL(dev);
 
-            DBG(9, "<> request len: %lu, [%d, %d; %d]\n",
-                (u_long)datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
+            DBG(9, "<> request len: %zu, [%d, %d; %d]\n",
+                datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
+
             if ((status = dev->io->dev_request(dev, NULL, 0, rbuf, &datalen)) !=
                 SANE_STATUS_GOOD)
                 return status;
+
             dev->datalen += datalen;
             dev->blocklen -= datalen;
-            DBG(9, "<> got %lu, [%d, %d; %d]\n",
-                (u_long)datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
+
+            DBG(9, "<> got %zu, [%d, %d; %d]\n",
+                datalen, dev->dataoff, DATATAIL(dev), dev->datalen);
+
             if (dev->blocklen < 0)
                 return ret_cancel(dev, SANE_STATUS_IO_ERROR);
 
-            datalen = DATAROOM(dev) & USB_BLOCK_MASK;
+            datalen = MIN(dev->blocklen, DATAROOM(dev) & USB_BLOCK_MASK);
         }
 
         if (buf && lenp) { /* read mode */
diff --git a/backend/xerox_mfp.conf.in b/backend/xerox_mfp.conf.in
index 33e4b38..d9deecc 100644
--- a/backend/xerox_mfp.conf.in
+++ b/backend/xerox_mfp.conf.in
@@ -251,6 +251,9 @@ usb 0x0924 0x42da
 #Xerox WorkCentre 3225
 usb 0x0924 0x42dc
 
+#Xerox WorkCentre 3335
+usb 0x0924 0x42e2
+
 ###################
 ### Dell Models ###
 ###################
@@ -260,3 +263,6 @@ usb 0x413c 0x5124
 
 #Dell 1235cn (clone of Samsung CLX-3175)
 usb 0x413c 0x5310
+
+#Dell 1135n (clone of Samsung SCX-4623F)
+usb 0x413c 0x5318
-- 
cgit v1.2.3