diff options
| author | Jörg Frings-Fürst <debian@jff.email> | 2024-03-03 09:55:14 +0100 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff.email> | 2024-03-03 09:55:14 +0100 | 
| commit | b098beb219b0b300ec7eb915bfa2b3038c3fb533 (patch) | |
| tree | 08c40dc8b180b31f504945e8da3e3ea3950e4145 /backend | |
| parent | 2938695ca4c9bca7834817465662e31570f6d32f (diff) | |
| parent | 23c348d62ab9f0a902189c70921310a5f856852c (diff) | |
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'backend')
31 files changed, 2346 insertions, 456 deletions
| 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 | 
