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