summaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
Diffstat (limited to 'backend')
-rw-r--r--backend/Makefile.am24
-rw-r--r--backend/avision.c367
-rw-r--r--backend/avision.h70
-rw-r--r--backend/canon_dr.c74
-rw-r--r--backend/dmc.c2
-rw-r--r--backend/dmc.h2
-rw-r--r--backend/epson2_usb.c1
-rw-r--r--backend/epsonds.c6
-rw-r--r--backend/escl/escl.c14
-rw-r--r--backend/escl/escl.h2
-rw-r--r--backend/escl/escl_capabilities.c11
-rw-r--r--backend/escl/escl_jpeg.c14
-rw-r--r--backend/escl/escl_newjob.c22
-rw-r--r--backend/fujitsu.c14
-rw-r--r--backend/fujitsu.conf.in13
-rw-r--r--backend/genesys.conf.in6
-rw-r--r--backend/hpljm1005.c414
-rw-r--r--backend/lexmark.c2
-rw-r--r--backend/lexmark_x2600.c1287
-rw-r--r--backend/lexmark_x2600.conf.in2
-rw-r--r--backend/lexmark_x2600.h151
-rw-r--r--backend/net.c31
-rw-r--r--backend/pixma/pixma.c48
-rw-r--r--backend/pixma/pixma_imageclass.c2
-rw-r--r--backend/pixma/pixma_io_sanei.c7
-rw-r--r--backend/pixma/pixma_mp150.c50
-rw-r--r--backend/snapscan-options.c14
-rw-r--r--backend/test.c63
-rw-r--r--backend/xerox_mfp-usb.c23
-rw-r--r--backend/xerox_mfp.c60
-rw-r--r--backend/xerox_mfp.conf.in6
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