/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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 . 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. This file is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /* Revision 1.13 2005/04/13 12:50:07 ellert-guest Add missing SANE_I18N, Regenerate .po files accordingly, Update Swedish translations Revision 1.12 2003/10/09 19:32:50 kig-guest Bug #300241: fix inverse image on 3c/4c/6100C at 10 bit depth */ /* pwd.h not available ? */ #if (defined(__IBMC__) || defined(__IBMCPP__)) #ifndef _AIX # define SANE_HOME_HP "SANE_HOME_HP" #endif #endif /* To be done: don't reallocate choice accessors */ /* #define HP_ALLOC_CHOICEACC_ONCE 1 */ /* #define HP_EXPERIMENTAL */ /* #define STUBS extern int sanei_debug_hp; */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "../include/sane/sanei_backend.h" #include "../include/lalloca.h" #include #include #include "../include/lassert.h" #ifndef SANE_HOME_HP #include #endif #include #include #include #include #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "hp.h" #include "hp-option.h" #include "hp-accessor.h" #include "hp-scsi.h" #include "hp-scl.h" #include "hp-device.h" /* FIXME: descriptors should be static? */ typedef SANE_Option_Descriptor * _HpSaneOption; typedef struct hp_option_descriptor_s * _HpOptionDescriptor; typedef struct hp_option_s * _HpOption; typedef struct hp_data_info_s * HpDataInfo; typedef struct hp_data_info_s * _HpDataInfo; typedef HpAccessor HpAccessorOptd; static hp_bool_t hp_optset_isEnabled (HpOptSet this, HpData data, const char *name, const HpDeviceInfo *info); static HpOption hp_optset_get (HpOptSet this, HpOptionDescriptor optd); static HpOption hp_optset_getByName (HpOptSet this, const char * name); static SANE_Status hp_download_calib_file (HpScsi scsi); static SANE_Status hp_probe_parameter_support_table (enum hp_device_compat_e compat, HpScl scl, int value); #define HP_EOL -9999 /* Don't need requiries for commands that are probed */ #define HP_PROBE_SCL_COMMAND 1 /* Scale factor for vectors (gtk seems not to like vectors/curves * in y-range 0.0,...,1.0) */ #define HP_VECTOR_SCALE (256.0) /* * */ struct hp_option_s { HpOptionDescriptor descriptor; HpAccessorOptd optd_acsr; HpAccessor data_acsr; void * extra; }; struct hp_option_descriptor_s { const char * name; const char * title; const char * desc; SANE_Value_Type type; SANE_Unit unit; SANE_Int cap; enum hp_device_compat_e requires; /* model dependent support flags */ /* probe for option support */ SANE_Status (*probe) (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data); SANE_Status (*program) (HpOption this, HpScsi scsi, HpOptSet optset, HpData data); hp_bool_t (*enable) (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info); hp_bool_t has_global_effect; hp_bool_t affects_scan_params; hp_bool_t program_immediate; hp_bool_t suppress_for_scan; hp_bool_t may_change; /* This stuff should really be in a subclasses: */ HpScl scl_command; int minval, maxval, startval; /* for simulation */ HpChoice choices; }; struct hp_data_info_s { HpScl scl; }; static const struct hp_option_descriptor_s NUM_OPTIONS[1], PREVIEW_MODE[1], SCAN_MODE[1], SCAN_RESOLUTION[1], CUSTOM_GAMMA[1], GAMMA_VECTOR_8x8[1], #ifdef ENABLE_7x12_TONEMAPS GAMMA_VECTOR_7x12[1], RGB_TONEMAP[1], GAMMA_VECTOR_R[1], GAMMA_VECTOR_G[1], GAMMA_VECTOR_B[1], #endif HALFTONE_PATTERN[1], MEDIA[1], OUT8[1], BIT_DEPTH[1], SCAN_SOURCE[1], #ifdef FAKE_COLORSEP_MATRIXES SEPMATRIX[1], #endif MATRIX_TYPE[1]; /* Check if a certain scanner model supports a command with a given parameter * value. The function returns SANE_STATUS_GOOD if the command and the * value is found in the support table of that scanner. * It returns SANE_STATUS_UNSUPPORTED if the command is found in the support * table of that scanner, but the value is not included in the table. * It returns SANE_STATUS_EOF if there is no information about that command * and that scanner in the support table. */ static SANE_Status hp_probe_parameter_support_table (enum hp_device_compat_e compat, HpScl scl, int value) {int k, j; char *eptr; static int photosmart_output_type[] = /* HP Photosmart: only b/w, gray, color is supported */ { HP_COMPAT_PS, SCL_OUTPUT_DATA_TYPE, 0, 4, 5, HP_EOL }; static int *support_table[] = { photosmart_output_type }; eptr = getenv ("SANE_HP_CHK_TABLE"); if ((eptr != NULL) && (*eptr == '0')) return SANE_STATUS_EOF; for (k = 0; k < (int)(sizeof (support_table)/sizeof (support_table[0])); k++) { if ((scl == support_table[k][1]) && (support_table[k][0] & compat)) { for (j = 2; support_table[k][j] != HP_EOL; j++) if (support_table[k][j] == value) return SANE_STATUS_GOOD; return SANE_STATUS_UNSUPPORTED; } } return SANE_STATUS_EOF; } /* * class HpChoice */ typedef struct hp_choice_s * _HpChoice; static hp_bool_t hp_choice_isSupported (HpChoice choice, int minval, int maxval) { return ( choice->is_emulated || ( choice->val >= minval && choice->val <= maxval ) ); } static hp_bool_t hp_probed_choice_isSupported (HpScsi scsi, HpScl scl, HpChoice choice, int minval, int maxval) { hp_bool_t isSupported; SANE_Status status; enum hp_device_compat_e compat; if ( choice->is_emulated ) { DBG(3, "probed_choice: value %d is emulated\n", choice->val); return ( 1 ); } if ( choice->val < minval || choice->val > maxval ) { DBG(3, "probed_choice: value %d out of range (%d,%d)\n", choice->val, minval, maxval); return ( 0 ); } if (sanei_hp_device_probe (&compat, scsi) != SANE_STATUS_GOOD) { DBG(1, "probed_choice: Could not get compatibilities for scanner\n"); return ( 0 ); } status = hp_probe_parameter_support_table (compat, scl, choice->val); if (status == SANE_STATUS_GOOD) { DBG(3, "probed_choice: command/value found in support table\n"); return ( 1 ); } else if (status == SANE_STATUS_UNSUPPORTED) { DBG(3, "probed_choice: command found in support table, but value n.s.\n"); return ( 0 ); } /* Not in the support table. Try to inquire */ /* Fix me: It seems that the scanner does not raise a parameter error */ /* after specifying an unsupported command-value. */ sanei_hp_scl_clearErrors (scsi); sanei_hp_scl_set (scsi, scl, choice->val); isSupported = ( sanei_hp_scl_errcheck (scsi) == SANE_STATUS_GOOD ); DBG(3, "probed_choice: value %d %s\n", choice->val, isSupported ? "supported" : "not supported"); return isSupported; } hp_bool_t sanei_hp_choice_isEnabled (HpChoice this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { if (!this->enable) return 1; return (*this->enable)(this, optset, data, info); } static hp_bool_t _cenable_incolor (HpChoice __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR; } static hp_bool_t _cenable_notcolor (HpChoice __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) != HP_SCANMODE_COLOR; } /* * class HpAccessorOptd */ static HpAccessorOptd hp_accessor_optd_new (HpData data) { return sanei_hp_accessor_new(data, sizeof(SANE_Option_Descriptor)); } static _HpSaneOption hp_accessor_optd_data (HpAccessorOptd this, HpData data) { return sanei__hp_accessor_data(this, data); } /* * class OptionDescriptor */ static SANE_Status hp_option_descriptor_probe (HpOptionDescriptor desc, HpScsi scsi, HpOptSet optset, HpData data, HpOption * newoptp) { _HpOption new; SANE_Status status; _HpSaneOption optd; new = sanei_hp_alloc(sizeof(*new)); new->descriptor = desc; if (!(new->optd_acsr = hp_accessor_optd_new(data))) return SANE_STATUS_NO_MEM; new->data_acsr = 0; optd = hp_accessor_optd_data(new->optd_acsr, data); memset(optd, 0, sizeof(*optd)); optd->name = desc->name; optd->title = desc->title; optd->desc = desc->desc; optd->type = desc->type; optd->unit = desc->unit; optd->cap = desc->cap; /* * Probe function will set optd->size, optd->constraint_type, * and optd->constraint. * and also new->accessor * and possibly new->extra */ if (desc->probe) { if (FAILED( status = (*desc->probe)(new, scsi, optset, data) )) { /* hp_accessor_optd_destoy(new->optd_acsr) */ sanei_hp_free(new); return status; } } *newoptp = new; return SANE_STATUS_GOOD; } /* * class Option */ static HpSaneOption hp_option_saneoption (HpOption this, HpData data) { return hp_accessor_optd_data(this->optd_acsr, data); } static _HpSaneOption _hp_option_saneoption (HpOption this, HpData data) { return hp_accessor_optd_data(this->optd_acsr, data); } static SANE_Status hp_option_download (HpOption this, HpData data, HpOptSet optset, HpScsi scsi) { HpScl scl = this->descriptor->scl_command; int value; if (IS_SCL_CONTROL(scl)) { value = sanei_hp_accessor_getint(this->data_acsr, data); if ( (scl == SCL_DATA_WIDTH) && (sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR) ) { value *= 3; } return sanei_hp_scl_set(scsi, scl, value); } else if (IS_SCL_DATA_TYPE(scl)) return sanei_hp_scl_download(scsi, scl, sanei_hp_accessor_data(this->data_acsr, data), sanei_hp_accessor_size(this->data_acsr)); assert(!scl); return SANE_STATUS_INVAL; } static SANE_Status hp_option_upload (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; int val; if (IS_SCL_CONTROL(scl)) { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) ); if ( (scl == SCL_DATA_WIDTH) && (sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR) ) { val /= 3; } sanei_hp_accessor_setint(this->data_acsr, data, val); return SANE_STATUS_GOOD; } else if (IS_SCL_DATA_TYPE(scl)) return sanei_hp_scl_upload(scsi, scl, sanei__hp_accessor_data(this->data_acsr, data), sanei_hp_accessor_size(this->data_acsr)); assert(!scl); return SANE_STATUS_INVAL; } static SANE_Status hp_option_program (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { const HpDeviceInfo *info; DBG(10, "hp_option_program: name=%s, enable=0x%08lx, program=0x%08lx\n", this->descriptor->name, (long)this->descriptor->enable, (long)this->descriptor->program); /* Replaced by flag suppress_for_scan * if (this->descriptor->program_immediate) * { * DBG(10, "hp_option_program: is program_immediate. Dont program now.\n"); * return SANE_STATUS_GOOD; * } */ if (!this->descriptor->program) return SANE_STATUS_GOOD; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if (this->descriptor->enable && !(*this->descriptor->enable)(this, optset, data, info)) return SANE_STATUS_GOOD; return (*this->descriptor->program)(this, scsi, optset, data); } static SANE_Status hp_option_get (HpOption this, HpData data, void * valp) { if (!this->data_acsr) return SANE_STATUS_INVAL; return sanei_hp_accessor_get(this->data_acsr, data, valp); } static hp_bool_t _values_are_equal (HpOption this, HpData data, const void * val1, const void * val2) { HpSaneOption optd = hp_option_saneoption(this, data); if (optd->type == SANE_TYPE_STRING) return strncmp((const char *)val1, (const char *)val2, optd->size) == 0; else return memcmp(val1, val2, optd->size) == 0; } static hp_bool_t hp_option_isImmediate (HpOption this) { return ( this->descriptor->program_immediate && this->descriptor->program ); } static SANE_Status hp_option_imm_set (HpOptSet optset, HpOption this, HpData data, void * valp, SANE_Int * info, HpScsi scsi) { HpSaneOption optd = hp_option_saneoption(this, data); hp_byte_t * old_val = alloca(optd->size); SANE_Status status; assert (this->descriptor->program_immediate && this->descriptor->program); if (!SANE_OPTION_IS_SETTABLE(optd->cap)) return SANE_STATUS_INVAL; DBG(10,"hp_option_imm_set: %s\n", this->descriptor->name); if ( this->descriptor->type == SANE_TYPE_BUTTON ) { status = (*this->descriptor->program)(this, scsi, optset, data); if ( !FAILED(status) && info ) { if (this->descriptor->has_global_effect) *info |= SANE_INFO_RELOAD_OPTIONS; if (this->descriptor->affects_scan_params) *info |= SANE_INFO_RELOAD_PARAMS; } return status; } if ( !this->data_acsr ) return SANE_STATUS_INVAL; if (!old_val) return SANE_STATUS_NO_MEM; if (FAILED( status = sanei_constrain_value(optd, valp, info) )) { DBG(1, "option_imm_set: %s: constrain_value failed :%s\n", this->descriptor->name, sane_strstatus(status)); return status; } RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, old_val) ); if (_values_are_equal(this, data, old_val, valp)) { DBG(3, "option_imm_set: value unchanged\n"); return SANE_STATUS_GOOD; } if (info) memcpy(old_val, valp, optd->size); /* Save requested value */ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, valp) ); if ( this->descriptor->type == SANE_TYPE_STRING ) RETURN_IF_FAIL( (*this->descriptor->program)(this, scsi, optset, data) ); if (info) { if (!_values_are_equal(this, data, old_val, valp)) *info |= SANE_INFO_INEXACT; if (this->descriptor->has_global_effect) *info |= SANE_INFO_RELOAD_OPTIONS; if (this->descriptor->affects_scan_params) *info |= SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; } static SANE_Status hp_option_set (HpOption this, HpData data, void * valp, SANE_Int * info) { HpSaneOption optd = hp_option_saneoption(this, data); hp_byte_t * old_val = alloca(optd->size); SANE_Status status; char sval[64]; if (!SANE_OPTION_IS_SETTABLE(optd->cap) || !this->data_acsr) return SANE_STATUS_INVAL; if (!old_val) return SANE_STATUS_NO_MEM; sval[0] = '\0'; if (this->descriptor->type == SANE_TYPE_INT) sprintf (sval," value=%d", *(int*)valp); DBG(10,"hp_option_set: %s%s\n", this->descriptor->name, sval); if (FAILED( status = sanei_constrain_value(optd, valp, info) )) { DBG(1, "option_set: %s: constrain_value failed :%s\n", this->descriptor->name, sane_strstatus(status)); return status; } RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, old_val) ); if (_values_are_equal(this, data, old_val, valp)) { DBG(3, "option_set: %s: value unchanged\n",this->descriptor->name); return SANE_STATUS_GOOD; } if (info) memcpy(old_val, valp, optd->size); /* Save requested value */ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, valp) ); if (info) { if (!_values_are_equal(this, data, old_val, valp)) *info |= SANE_INFO_INEXACT; if (this->descriptor->has_global_effect) *info |= SANE_INFO_RELOAD_OPTIONS; if (this->descriptor->affects_scan_params) *info |= SANE_INFO_RELOAD_PARAMS; DBG(3, "option_set: %s: info=0x%lx\n",this->descriptor->name, (long)*info); } return SANE_STATUS_GOOD; } static int hp_option_getint (HpOption this, HpData data) { return sanei_hp_accessor_getint(this->data_acsr, data); } static SANE_Status hp_option_imm_control (HpOptSet optset, HpOption this, HpData data, SANE_Action action, void * valp, SANE_Int *infop, HpScsi scsi) { HpSaneOption optd = hp_option_saneoption(this, data); if (!SANE_OPTION_IS_ACTIVE(optd->cap)) return SANE_STATUS_INVAL; switch (action) { case SANE_ACTION_GET_VALUE: return hp_option_get(this, data, valp); case SANE_ACTION_SET_VALUE: return hp_option_imm_set(optset, this, data, valp, infop, scsi); case SANE_ACTION_SET_AUTO: default: return SANE_STATUS_INVAL; } } static SANE_Status hp_option_control (HpOption this, HpData data, SANE_Action action, void * valp, SANE_Int *infop) { HpSaneOption optd = hp_option_saneoption(this, data); if (!SANE_OPTION_IS_ACTIVE(optd->cap)) return SANE_STATUS_INVAL; switch (action) { case SANE_ACTION_GET_VALUE: return hp_option_get(this, data, valp); case SANE_ACTION_SET_VALUE: return hp_option_set(this, data, valp, infop); case SANE_ACTION_SET_AUTO: default: return SANE_STATUS_INVAL; } } static void hp_option_reprogram (HpOption this, HpOptSet optset, HpData data, HpScsi scsi) { if (this->descriptor->may_change) { DBG(5, "hp_option_reprogram: %s\n", this->descriptor->name); hp_option_program (this, scsi, optset, data); } } static void hp_option_reprobe (HpOption this, HpOptSet optset, HpData data, HpScsi scsi) { if (this->descriptor->may_change) { DBG(5, "hp_option_reprobe: %s\n", this->descriptor->name); (*this->descriptor->probe)((_HpOption)this, scsi, optset, data); } } static void hp_option_updateEnable (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { hp_bool_t (*f)(HpOption, HpOptSet, HpData, const HpDeviceInfo *) = this->descriptor->enable; _HpSaneOption optd = _hp_option_saneoption(this, data); if (!f || (*f)(this, optset, data, info)) optd->cap &= ~SANE_CAP_INACTIVE; else optd->cap |= SANE_CAP_INACTIVE; } static hp_bool_t hp_option_isInternal (HpOption this) { return this->descriptor->name[0] == '_'; } /* * Option probe functions */ static SANE_Status _set_range (HpOption opt, HpData data, SANE_Word min, SANE_Word quant, SANE_Word max) { _HpSaneOption optd = _hp_option_saneoption(opt, data); SANE_Range * range = sanei_hp_alloc(sizeof(*range)); /* FIXME: leak? */ if (! range) return SANE_STATUS_NO_MEM; range->min = min; range->max = max; range->quant = quant; optd->constraint.range = range; optd->constraint_type = SANE_CONSTRAINT_RANGE; return SANE_STATUS_GOOD; } static void _set_size (HpOption opt, HpData data, SANE_Int size) { _hp_option_saneoption(opt, data)->size = size; } /* #ifdef HP_EXPERIMENTAL */ static SANE_Status _probe_int (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval; int val = 0; assert(scl); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); if (minval >= maxval) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Int)); return _set_range(this, data, minval, 1, maxval); } /* #endif */ static SANE_Status _probe_int_brightness (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval; int val = 0; hp_bool_t simulate; assert(scl); simulate = ( sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi), scl, 0, 0) != SANE_STATUS_GOOD ); if ( simulate ) { val = this->descriptor->startval; minval = this->descriptor->minval; maxval = this->descriptor->maxval; } else { RETURN_IF_FAIL ( sanei_hp_scl_inquire(scsi,scl,&val,&minval,&maxval) ); } if (minval >= maxval) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Int)); return _set_range(this, data, minval, 1, maxval); } static SANE_Status _probe_resolution (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int minval, maxval, min2, max2; int val = 0, val2; int quant = 1; enum hp_device_compat_e compat; /* Check for supported resolutions in both directions */ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_X_RESOLUTION, &val, &minval, &maxval) ); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_Y_RESOLUTION, &val2, &min2, &max2)); if ( min2 > minval ) minval = min2; if ( max2 < maxval ) maxval = max2; if (minval >= maxval) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Int)); /* The HP OfficeJet Pro 1150C crashes the scan head when scanning at * resolutions less than 42 dpi. Set a safe minimum resolution. * Hopefully 50 dpi is safe enough. */ if ((sanei_hp_device_probe(&compat,scsi)==SANE_STATUS_GOOD) && ((compat&(HP_COMPAT_OJ_1150C|HP_COMPAT_OJ_1170C))==HP_COMPAT_OJ_1150C)) { if (minval<50) minval=50; } /* HP Photosmart scanner does not allow scanning at arbitrary resolutions */ /* for slides/negatives. Must be multiple of 300 dpi. Set quantization. */ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) ) { int val, mi, ma; if ( (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &val, &mi, &ma) == SANE_STATUS_GOOD) && ((val == HP_MEDIA_SLIDE) || (val == HP_MEDIA_NEGATIVE)) ) { quant = 300; minval = (minval+quant-1)/quant; minval *= quant; maxval = (maxval+quant-1)/quant; maxval *= quant; } } DBG(5, "_probe_resolution: set range %d..%d, quant=%d\n",minval,maxval,quant); return _set_range(this, data, minval, quant, maxval); } static SANE_Status _probe_bool (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int val = 0; if (scl) RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) ); /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } static SANE_Status _probe_change_doc (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) {SANE_Status status; int cap = 0; DBG(2, "probe_change_doc: inquire ADF capability\n"); status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &cap, 0, 0); if ( (status != SANE_STATUS_GOOD) || (cap == 0)) return SANE_STATUS_UNSUPPORTED; DBG(2, "probe_change_doc: check if change document is supported\n"); status = sanei_hp_scl_inquire(scsi, SCL_CHANGE_DOC, &cap, 0, 0); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, cap); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } /* The OfficeJets support SCL_UNLOAD even when no ADF is installed, so * this function was added to check for SCL_ADF_CAPABILITY, similar to * _probe_change_doc(), to hide the unnecessary "Unload" button on * non-ADF OfficeJets. */ static SANE_Status _probe_unload (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) {SANE_Status status; int cap = 0; DBG(2, "probe_unload: inquire ADF capability\n"); status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &cap, 0, 0); if ( (status != SANE_STATUS_GOOD) || (cap == 0)) return SANE_STATUS_UNSUPPORTED; DBG(2, "probe_unload: check if unload is supported\n"); status = sanei_hp_scl_inquire(scsi, SCL_UNLOAD, &cap, 0, 0); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, cap); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } static SANE_Status _probe_calibrate (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int val = 0; /* Always false */ int minval, maxval; int media; int download_calib_file = 1; enum hp_device_compat_e compat; /* The OfficeJets don't seem to support calibration, so we'll * remove it from the option list to reduce frontend clutter. */ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_OJ_1150C)) { return SANE_STATUS_UNSUPPORTED; } /* If we have a Photosmart scanner, we only download the calibration file */ /* when medium is set to prints */ media = -1; if (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &val, &minval, &maxval) == SANE_STATUS_GOOD) media = val; /* 3: prints, 2: slides, 1: negatives */ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) && (media != HP_MEDIA_PRINT)) download_calib_file = 0; /* Recalibrate can not be probed, because it has no inquire ID. */ /* And the desired ID of 10963 does not work. So we have to trust */ /* the evaluated HP model number. */ /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Bool)); /* Try to download calibration map */ if (download_calib_file) hp_download_calib_file ( scsi ); return SANE_STATUS_GOOD; } static HpChoice _make_choice_list (HpChoice choice, int minval, int maxval) { static struct hp_choice_s bad = { 0, 0, 0, 0, 0 }; /* FIXME: hack */ /* FIXME: Another memory leak */ if (!choice->name) return 0; else if (hp_choice_isSupported(choice, minval, maxval)) { _HpChoice new = sanei_hp_memdup(choice, sizeof(*new)); if (!new) return &bad; new->next = _make_choice_list(choice + 1, minval, maxval); return new; } else return _make_choice_list(choice + 1, minval, maxval); } static HpChoice _make_probed_choice_list (HpScsi scsi, HpScl scl, HpChoice choice, int minval, int maxval) { static struct hp_choice_s bad = { 0, 0, 0, 0, 0 }; /* FIXME: hack */ /* FIXME: Another memory leak */ if (!choice->name) return 0; else if (hp_probed_choice_isSupported(scsi, scl, choice, minval, maxval)) { _HpChoice new = sanei_hp_memdup(choice, sizeof(*new)); if (!new) return &bad; new->next = _make_probed_choice_list(scsi, scl, choice + 1, minval, maxval); return new; } else return _make_probed_choice_list(scsi, scl, choice + 1, minval, maxval); } static void _set_stringlist (HpOption this, HpData data, SANE_String_Const * strlist) { _HpSaneOption optd = _hp_option_saneoption(this, data); optd->constraint.string_list = strlist; optd->constraint_type = SANE_CONSTRAINT_STRING_LIST; } static SANE_Status _probe_choice (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval, val; HpChoice choices; const HpDeviceInfo *info; enum hp_device_compat_e compat; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); DBG(3, "choice_option_probe: '%s': val, min, max = %d, %d, %d\n", this->descriptor->name, val, minval, maxval); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* Datawidth needs a special handling. The choicelist consists of */ /* values of bits per sample. But the minval/maxval uses bits per pixel */ if ( scl == SCL_DATA_WIDTH ) { enum hp_scanmode_e scanmode = sanei_hp_optset_scanmode (optset, data); /* The data width inquiries seem not to work properly on PhotoSmart */ /* Sometimes they report just 24 bits, but support 30 bits too. */ /* Sometimes they report min/max to be 24/8. Assume they all support */ /* at least 10 bits per channel for RGB. Grayscale is only supported */ /* with 8 bits. */ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS)) { if (scanmode == HP_SCANMODE_GRAYSCALE) { minval = 8; if (maxval < 8) maxval = 8; } else if (scanmode == HP_SCANMODE_COLOR) { minval = 24; if (maxval < 30) maxval = 30; } DBG(1, "choice_option_probe: set max. datawidth to %d for photosmart\n", maxval); } if ( scanmode == HP_SCANMODE_COLOR ) { minval /= 3; if ( minval <= 0) minval = 1; maxval /= 3; if ( maxval <= 0) maxval = 1; val /= 3; if (val <= 0) val = 1; } #if 0 /* The OfficeJets claim to support >8 bits per color, but it may not * work on some models. This code (if not commented out) disables it. */ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_OJ_1150C)) { if (maxval>8) maxval=8; } #endif } choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If no accessor, create one here. */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!(this->data_acsr)) #endif this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change); if (!(this->data_acsr)) return SANE_STATUS_NO_MEM; sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_each_choice (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval, val; HpChoice choices; const HpDeviceInfo *info; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); DBG(3, "choice_option_probe_each: '%s': val, min, max = %d, %d, %d\n", this->descriptor->name, val, minval, maxval); DBG(3, "choice_option_probe_each: test all values for '%s' separately\n", this->descriptor->name); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); choices = _make_probed_choice_list(scsi, scl, this->descriptor->choices, minval, maxval); DBG(3, "choice_option_probe_each: restore previous value %d for '%s'\n", val, this->descriptor->name); /* Restore current value */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, scl, val) ); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } /* pseudo probe for exposure times in Photosmart */ static SANE_Status _probe_ps_exposure_time (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int minval = 0, maxval = 9, val = 0; HpChoice choices; const HpDeviceInfo *info; choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } /* probe scan type (normal, adf, xpa) */ static SANE_Status _probe_scan_type (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int val; int numchoices = 0; HpChoice choices; SANE_Status status; const HpDeviceInfo *info; struct hp_choice_s scan_types[4]; struct hp_choice_s nch = { 0, 0, 0, 0, 0 }; enum hp_device_compat_e compat; /* We always have normal scan mode */ scan_types[numchoices++] = this->descriptor->choices[0]; if ( sanei_hp_device_probe (&compat, scsi) != SANE_STATUS_GOOD ) compat = 0; /* Inquire ADF Capability. PhotoSmart scanner reports ADF capability, */ /* but it makes no sense. */ if ((compat & HP_COMPAT_PS) == 0) { status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &val, 0, 0); if ( (status == SANE_STATUS_GOOD) && (val == 1) ) { scan_types[numchoices++] = this->descriptor->choices[1]; } } /* Inquire XPA capability is supported only by IIcx and 6100c/4c/3c. */ /* But more devices support XPA scan window. So don't inquire XPA cap. */ if ( compat & ( HP_COMPAT_2CX | HP_COMPAT_4C | HP_COMPAT_4P | HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C) && !(compat&HP_COMPAT_OJ_1150C) ) { scan_types[numchoices++] = this->descriptor->choices[2]; } /* Only normal scan type available ? No need to display choice */ if (numchoices <= 1) return SANE_STATUS_UNSUPPORTED; scan_types[numchoices] = nch; val = 0; choices = _make_choice_list(scan_types, 0, numchoices); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_mirror_horiz (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval, val, sec_dir; HpChoice choices; const HpDeviceInfo *info; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); DBG(3, "probe_mirror_horiz: '%s': val, min, max = %d, %d, %d\n", this->descriptor->name, val, minval, maxval); /* Look if the device supports the (?) inquire secondary scan-direction */ if ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) == SANE_STATUS_GOOD ) minval = HP_MIRROR_HORIZ_CONDITIONAL; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_mirror_vert (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int minval = HP_MIRROR_VERT_OFF, maxval = HP_MIRROR_VERT_ON, val = HP_MIRROR_VERT_OFF; int sec_dir; HpChoice choices; const HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* Look if the device supports the (?) inquire secondary scan-direction */ if ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) == SANE_STATUS_GOOD ) maxval = HP_MIRROR_VERT_CONDITIONAL; choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_front_button(_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int val = 0; if ( sanei_hp_scl_inquire(scsi, SCL_FRONT_BUTTON, &val, 0, 0) != SANE_STATUS_GOOD ) return SANE_STATUS_UNSUPPORTED; _set_size(this, data, sizeof(SANE_Bool)); /* If we don't have an accessor, get one */ if (!this->data_acsr) { if ( !(this->data_acsr = sanei_hp_accessor_bool_new(data)) ) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, 0); return SANE_STATUS_GOOD; } static SANE_Status _probe_geometry (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; hp_bool_t is_tl = 0; hp_bool_t active_xpa = sanei_hp_is_active_xpa ( scsi ); int minval, maxval; SANE_Fixed fval; /* There might have been a reason for inquiring the extent */ /* by using the maxval of the position. But this does not work */ /* when scanning from ADF. The Y-pos is then inquired with -1..0. */ /* First try to get the values with SCL_X/Y_POS. If this is not ok, */ /* use SCL_X/Y_EXTENT */ if (scl == SCL_X_EXTENT) { scl = SCL_X_POS; } else if (scl == SCL_Y_EXTENT) { scl = SCL_Y_POS; } else is_tl = 1; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, 0, &minval, &maxval) ); if (minval >= maxval) return SANE_STATUS_INVAL; /* Bad maximum value for extent-inquiry ? */ if ( (!is_tl) && (maxval <= 0) ) { scl = (scl == SCL_X_POS) ? SCL_X_EXTENT : SCL_Y_EXTENT; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, 0, &minval, &maxval) ); if (minval >= maxval) return SANE_STATUS_INVAL; } if ((scl == SCL_X_EXTENT) || (scl == SCL_Y_EXTENT)) { /* Max. extent is larger than max. position. Reduce extent */ maxval--; DBG(3, "probe_geometry: Inquiry by extent. Reduced maxval to %lu\n", (unsigned long)maxval); } /* Need a new accessor ? */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_fixed_new(data))) return SANE_STATUS_NO_MEM; } /* The active xpa is only 5x5 inches */ if ( (!is_tl) && active_xpa && (sanei_hp_optset_scan_type (optset, data) == SCL_XPA_SCAN) ) { DBG(3,"Set maxval to 1500 because of active XPA\n"); maxval = 1500; } fval = is_tl ? SANE_FIX(0.0) : maxval * SANE_FIX(MM_PER_DEVPIX); RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, &fval) ); _set_size(this, data, sizeof(SANE_Fixed)); return _set_range(this, data, minval * SANE_FIX(MM_PER_DEVPIX), 1, maxval * SANE_FIX(MM_PER_DEVPIX)); } static SANE_Status _probe_download_type (HpScl scl, HpScsi scsi) { SANE_Status status; sanei_hp_scl_clearErrors (scsi); sanei_hp_scl_set (scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl)); status = sanei_hp_scl_errcheck (scsi); DBG(3, "probe_download_type: Download type %d %ssupported\n", SCL_INQ_ID(scl), (status == SANE_STATUS_GOOD) ? "" : "not "); return status; } static SANE_Status _probe_custom_gamma (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; HpScl scl_tonemap = SCL_8x8TONE_MAP; SANE_Status status; hp_bool_t simulate; int val = 0, minval, maxval; int id = SCL_INQ_ID(scl_tonemap); /* Check if download type supported */ status = sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi), SCL_DOWNLOAD_TYPE, &minval, &maxval); simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval); if (simulate) { DBG(3, "probe_custom_gamma: Download type 2 not supported. Simulate\n"); } else { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) ); } /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } static SANE_Status _probe_vector (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { static struct vector_type_s { HpScl scl; unsigned length, depth; HpAccessor (*creator)(HpData data, unsigned length, unsigned depth); } types[] = { { SCL_8x8TONE_MAP, 256, 8, sanei_hp_accessor_gamma_vector_new }, #ifdef ENABLE_7x12_TONEMAPS { SCL_BW7x12TONE_MAP, 129, 12, sanei_hp_accessor_gamma_vector_new }, { SCL_7x12TONE_MAP, 3 * 129, 12, sanei_hp_accessor_gamma_vector_new }, #endif #ifdef ENABLE_16x16_DITHERS { SCL_BW16x16DITHER, 256, 8, sanei_hp_accessor_vector_new }, #endif { SCL_BW8x8DITHER, 64, 8, sanei_hp_accessor_vector_new }, { SCL_8x9MATRIX_COEFF, 9, 8, sanei_hp_accessor_matrix_vector_new }, #ifdef ENABLE_10BIT_MATRIXES { SCL_10x9MATRIX_COEFF, 9, 10, sanei_hp_accessor_matrix_vector_new }, { SCL_10x3MATRIX_COEFF, 3, 10, sanei_hp_accessor_matrix_vector_new }, #endif { 0, 0, 0, 0 } }; static struct subvector_type_s { HpOptionDescriptor desc; unsigned nchan, chan; HpOptionDescriptor super; } subvec_types[] = { #ifdef ENABLE_7x12_TONEMAPS { GAMMA_VECTOR_R, 3, 0, RGB_TONEMAP }, { GAMMA_VECTOR_G, 3, 1, RGB_TONEMAP }, { GAMMA_VECTOR_B, 3, 2, RGB_TONEMAP }, #endif { 0, 0, 0, 0 }, }; HpScl scl = this->descriptor->scl_command; HpAccessorVector vec; if (scl) { struct vector_type_s *type; for (type = types; type->scl; type++) if (type->scl == scl) break; assert(type->scl); RETURN_IF_FAIL ( _probe_download_type (scl, scsi) ); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { this->data_acsr = (*type->creator)(data, type->length, type->depth); } } else { struct subvector_type_s *type; HpOption super; for (type = subvec_types; type->desc; type++) if (type->desc == this->descriptor) break; assert(type->desc); super = hp_optset_get(optset, type->super); assert(super); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { this->data_acsr = sanei_hp_accessor_subvector_new( (HpAccessorVector) super->data_acsr, type->nchan, type->chan); } } if (!this->data_acsr) return SANE_STATUS_NO_MEM; vec = (HpAccessorVector)this->data_acsr; _set_size(this, data, sizeof(SANE_Fixed) * sanei_hp_accessor_vector_length(vec)); return _set_range(this, data, sanei_hp_accessor_vector_minval(vec), 1, sanei_hp_accessor_vector_maxval(vec)); } static SANE_Status _probe_gamma_vector (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { SANE_Fixed * buf; int i; size_t size, length; RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) ); /* Initialize to linear map */ size = hp_option_saneoption(this, data)->size; if (!(buf = alloca(size))) return SANE_STATUS_NO_MEM; length = size / sizeof(SANE_Fixed); for (i = 0; i < (int)length; i++) buf[i] = (SANE_FIX(HP_VECTOR_SCALE* 1.0) * i + (length-1) / 2) / length; return sanei_hp_accessor_set(this->data_acsr, data, buf); } static SANE_Status _probe_horiz_dither (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { int dim = 8; size_t size; int i, j; SANE_Fixed * buf; if (this->descriptor->scl_command == SCL_BW16x16DITHER) dim = 16; RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) ); /* Select vertical dither pattern, and upload it */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_BW_DITHER, HP_DITHER_VERTICAL) ); RETURN_IF_FAIL( hp_option_upload(this, scsi, optset, data) ); /* Flip it to get a horizontal dither pattern */ size = hp_option_saneoption(this, data)->size; assert(size == dim * dim * sizeof(SANE_Fixed)); if (!(buf = alloca(size))) return SANE_STATUS_NO_MEM; #define SWAP_FIXED(x,y) do { SANE_Fixed tmp = x; x = y; y = tmp; } while(0) RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, buf) ); for (i = 0; i < dim; i++) for (j = i + 1; j < dim; j++) SWAP_FIXED(buf[i * dim + j], buf[j * dim + i]); return sanei_hp_accessor_set(this->data_acsr, data, buf); } static SANE_Status _probe_matrix (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) ); /* Initial value: select RGB matrix, and upload it. */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_MATRIX, HP_MATRIX_RGB) ); return hp_option_upload(this, scsi, optset, data); } static SANE_Status _probe_num_options (_HpOption this, HpScsi __sane_unused__ scsi, HpOptSet __sane_unused__ optset, HpData data) { /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } _set_size(this, data, sizeof(SANE_Int)); return SANE_STATUS_GOOD; } static SANE_Status _probe_devpix (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int resolution; if (FAILED( sanei_hp_scl_inquire(scsi, scl, &resolution, 0, 0) )) { DBG(1, "probe_devpix: inquiry failed, assume 300 ppi\n"); resolution = 300; } if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, resolution); _set_size(this, data, sizeof(SANE_Int)); return SANE_STATUS_GOOD; } /* * Simulate functions */ static SANE_Status _simulate_brightness (HpOption this, HpData data, HpScsi scsi) { int k, val, newval; unsigned char *brightness_map; HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); val = sanei_hp_accessor_getint(this->data_acsr, data); DBG(3, "simulate_brightness: value = %d\n", val); /* Update brightness map in info structure */ brightness_map = &(info->simulate.brightness_map[0]); val *= 2; /* A value of 127 should give a totally white image */ for (k = 0; k < 256; k++) { newval = k + val; if (newval < 0) newval = 0; else if (newval > 255) newval = 255; brightness_map[k] = (unsigned char)newval; } return SANE_STATUS_GOOD; } static int hp_contrast (int x, int g) {int y = 0; if (g < -127) g = -127; else if (g > 127) g = 127; if (x < 0) x = 0; else if (x > 255) x = 255; if (g == 0) { y = x; } else if (g < 0) { g = -g; y = x * (255 - 2*g); y = y/255 + g; } else { if (x <= g) y = 0; else if (x >= 255-g) y = 255; else { y = (x - g)*255; y /= (255 - 2*g); } } return y; } static SANE_Status _simulate_contrast (HpOption this, HpData data, HpScsi scsi) { int k, val, newval; unsigned char *contrast_map; HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); val = sanei_hp_accessor_getint(this->data_acsr, data); DBG(3, "simulate_contrast: value = %d\n", val); /* Update contrast map in info structure */ contrast_map = &(info->simulate.contrast_map[0]); for (k = 0; k < 256; k++) { newval = hp_contrast (k, val); if (newval < 0) newval = 0; else if (newval > 255) newval = 255; contrast_map[k] = (unsigned char)newval; } return SANE_STATUS_GOOD; } /* * Option download functions */ static SANE_Status _program_generic (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { return hp_option_download(this, data, optset, scsi); } static SANE_Status _program_geometry (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { /* #define HP_LIMIT_ADF_WINDOW */ #ifndef HP_LIMIT_ADF_WINDOW return hp_option_download(this, data, optset, scsi); #else HpScl scl = this->descriptor->scl_command; int value; SANE_Status Status; if (sanei_hp_optset_scan_type (optset, data) != SCL_ADF_SCAN) return hp_option_download(this, data, optset, scsi); /* ADF may crash when scanning only a window ? */ if ( (scl == SCL_X_POS) || (scl == SCL_Y_POS) ) { value = 0; DBG(3,"program_geometry: set %c-pos to %d\n", (scl == SCL_X_POS) ? 'x' : 'y', value); } else if ( scl == SCL_X_EXTENT ) { value = 2550; DBG(3,"program_geometry: set x-extent to %d\n", value); } else { value = 4200; DBG(3,"program_geometry: set y-extent to %d\n", value); } Status = sanei_hp_scl_set(scsi, scl, value); return Status; #endif } static SANE_Status _program_data_width (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; int value = sanei_hp_accessor_getint(this->data_acsr, data); SANE_Status status; if ( sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR ) { value *= 3; if (value < 24) { DBG(3,"program_data_width: map datawith from %d to 24\n", (int)value); value = 24; } } status = sanei_hp_scl_set(scsi, scl, value); return status; } static SANE_Status _program_generic_simulate (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; const char *devname = sanei_hp_scsi_devicename (scsi); int simulate; /* Check if command is supported */ simulate = ( sanei_hp_device_support_get (devname, scl, 0, 0) != SANE_STATUS_GOOD ); /* Save simulate flag */ sanei_hp_device_simulate_set (devname, scl, simulate); if ( !simulate ) /* Let the device do it */ return hp_option_download(this, data, optset, scsi); DBG(3, "program_generic: %lu not programmed. Will be simulated\n", (unsigned long)(SCL_INQ_ID(scl))); switch (scl) { case SCL_BRIGHTNESS: _simulate_brightness (this, data, scsi); break; case SCL_CONTRAST: _simulate_contrast (this, data,scsi); break; default: DBG(1, "program_generic: No simulation for %lu\n", (unsigned long)(SCL_INQ_ID(scl))); break; } return SANE_STATUS_GOOD; } static SANE_Status _simulate_custom_gamma (HpOption gvector, HpScsi scsi, HpData data) { size_t size = sanei_hp_accessor_size(gvector->data_acsr); const unsigned char *vector_data = (const unsigned char *)sanei_hp_accessor_data(gvector->data_acsr, data); HpDeviceInfo *info; int k, newval; DBG(3,"program_custom_gamma_simulate: save gamma map\n"); if (size != 256) { DBG(1,"program_custom_gamma_simulate: size of vector is %d.\ Should be 256.\n", (int)size); return SANE_STATUS_INVAL; } RETURN_IF_FAIL (sanei_hp_scl_set(scsi, SCL_TONE_MAP, 0)); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); info->simulate.gamma_simulate = 1; for (k = 0; k < 256; k++) { newval = 255 - vector_data[255-k]; if (newval < 0) newval = 0; else if (newval > 255) newval = 255; info->simulate.gamma_map[k] = newval; } return SANE_STATUS_GOOD; } static SANE_Status _program_tonemap (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { hp_bool_t use_custom_map = hp_option_getint(this, data); HpOption gvector = 0; int type = 0; if (!use_custom_map) return sanei_hp_scl_set(scsi, SCL_TONE_MAP, 0); #ifdef ENABLE_7x12_TONEMAPS /* Try to find the appropriate 5P style tonemap. */ if (sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR) { type = -1; gvector = hp_optset_get(optset, RGB_TONEMAP); } else { type = -2; gvector = hp_optset_get(optset, GAMMA_VECTOR_7x12); } #endif /* If that failed, just use 8x8 tonemap */ if (!gvector) { HpScl scl_tonemap = SCL_8x8TONE_MAP; hp_bool_t simulate; int id = SCL_INQ_ID(scl_tonemap); int minval, maxval; SANE_Status status; type = -1; gvector = hp_optset_get(optset, GAMMA_VECTOR_8x8); /* Check if download type supported */ status = sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi), SCL_DOWNLOAD_TYPE, &minval, &maxval); simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval); if (simulate) return _simulate_custom_gamma (gvector, scsi, data); } assert(gvector != 0); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_TONE_MAP, type) ); return hp_option_download(gvector, data, optset, scsi); } static SANE_Status _program_dither (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { enum hp_dither_type_e type = hp_option_getint(this, data); HpOption dither; switch (type) { case HP_DITHER_CUSTOM: dither = hp_optset_getByName(optset, SANE_NAME_HALFTONE_PATTERN); assert(dither != 0); break; case HP_DITHER_HORIZONTAL: dither = hp_optset_getByName(optset, HP_NAME_HORIZONTAL_DITHER); type = HP_DITHER_CUSTOM; assert(dither != 0); break; default: dither = 0; break; } RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_BW_DITHER, type) ); if (!dither) return SANE_STATUS_GOOD; return hp_option_download(dither, data, optset, scsi); } #ifdef FAKE_COLORSEP_MATRIXES static HpOption _get_sepmatrix (HpOptSet optset, HpData data, enum hp_matrix_type_e type) { SANE_Fixed buf[9]; HpOption matrix = hp_optset_get(optset, SEPMATRIX); memset(buf, 0, sizeof(buf)); if (type == HP_MATRIX_RED) buf[1] = SANE_FIX(1.0); else if (type == HP_MATRIX_GREEN) buf[4] = SANE_FIX(1.0); else if (type == HP_MATRIX_BLUE) buf[7] = SANE_FIX(1.0); else { assert(!"Bad colorsep type"); return 0; } sanei_hp_accessor_set(matrix->data_acsr, data, buf); return matrix; } #endif static SANE_Status _program_matrix (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { enum hp_matrix_type_e type = hp_option_getint(this, data); HpOption matrix = 0; if (type == HP_MATRIX_AUTO) return SANE_STATUS_GOOD; /* Default to matrix set by mode */ /* Download custom matrix, if we need it. */ if (type == HP_MATRIX_CUSTOM) { matrix = hp_optset_getByName(optset, SANE_NAME_MATRIX_RGB); assert(matrix); } #ifdef FAKE_COLORSEP_MATRIXES else if (type == HP_MATRIX_RED || type == HP_MATRIX_BLUE || type == HP_MATRIX_GREEN) { matrix = _get_sepmatrix(optset, data, type); type = HP_MATRIX_CUSTOM; assert(matrix); } #else else if (type == HP_MATRIX_GREEN) type = HP_MATRIX_PASS; #endif RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_MATRIX, type) ); if (matrix) RETURN_IF_FAIL( hp_option_download(matrix, data, optset, scsi) ); return SANE_STATUS_GOOD; } static SANE_Status _program_resolution (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int xresolution = hp_option_getint(this, data); int yresolution = xresolution; int xscale = 100, yscale = 100; #ifdef FIX_PHOTOSMART int minval, maxval, media; enum hp_device_compat_e compat; /* HP Photosmart scanner has problems with scanning slides/negatives */ /* at arbitrary resolutions. The following tests did not work: */ /* xres = yres = next lower multiple of 300, xscale = yscale > 100: */ /* xres = yres = next higher multiple of 300, xscale = yscale < 100: */ /* xres = next lower multiple of 300, xscale > 100 */ /* xres = next higher multiple of 300, xscale < 100 */ /* yres = next lower multiple of 300, yscale > 100 */ /* yres = next higher multiple of 300, yscale < 100 */ /* The image extent was ok, but the content was stretched in y-direction */ if (xresolution > 300) { if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) && (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &media, &minval, &maxval) == SANE_STATUS_GOOD) && ((media == HP_MEDIA_SLIDE) || (media == HP_MEDIA_NEGATIVE))) {int next_resolution; next_resolution = (xresolution % 300) * 300; if (next_resolution < 300) next_resolution = 300; yresolution = next_resolution; yscale = (int)(100.0 * xresolution / yresolution); } } #endif RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_X_SCALE, xscale) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_Y_SCALE, yscale) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_X_RESOLUTION, xresolution) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_Y_RESOLUTION, yresolution) ); return SANE_STATUS_GOOD; } static char * get_home_dir (void) { #ifdef SANE_HOME_HP return getenv (SANE_HOME_HP); #else struct passwd *pw; pw = getpwuid (getuid ()); /* Look if we can find our home directory */ return pw ? pw->pw_dir : NULL; #endif } static char * get_calib_filename (HpScsi scsi) { char *homedir; char *calib_filename, *cf; const char *devname = sanei_hp_scsi_devicename (scsi); int name_len; homedir = get_home_dir (); /* Look if we can find our home directory */ if (!homedir) return NULL; name_len = strlen (homedir) + 33; if ( devname ) name_len += strlen (devname); calib_filename = sanei_hp_allocz (name_len); if (!calib_filename) return NULL; strcpy (calib_filename, homedir); strcat (calib_filename, "/.sane/calib-hp"); if ( devname && devname[0] ) /* Replace '/' by "+-" */ { cf = calib_filename + strlen (calib_filename); *(cf++) = ':'; while (*devname) { if (*devname == '/') *(cf++) = '+', *(cf++) = '-'; else *(cf++) = *devname; devname++; } } strcat (calib_filename, ".dat"); return calib_filename; } static SANE_Status read_calib_file (int *nbytes, char **calib_data, HpScsi scsi) { SANE_Status status = SANE_STATUS_GOOD; char *calib_filename; FILE *calib_file; int err, c1, c2, c3, c4; *nbytes = 0; *calib_data = NULL; calib_filename = get_calib_filename ( scsi ); if (!calib_filename) return SANE_STATUS_NO_MEM; calib_file = fopen (calib_filename, "rb"); if ( calib_file ) { err = ((c1 = getc (calib_file)) == EOF); err |= ((c2 = getc (calib_file)) == EOF); err |= ((c3 = getc (calib_file)) == EOF); err |= ((c4 = getc (calib_file)) == EOF); *nbytes = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; if ( err ) { DBG(1, "read_calib_file: Error reading calibration data size\n"); status = SANE_STATUS_EOF; } else { *calib_data = sanei_hp_alloc ( *nbytes ); if ( !*calib_data ) { status = SANE_STATUS_NO_MEM; } else { err |= ((int)fread (*calib_data,1,*nbytes,calib_file) != *nbytes); if ( err ) { DBG(1, "read_calib_file: Error reading calibration data\n"); sanei_hp_free ( *calib_data ); status = SANE_STATUS_EOF; } } } fclose ( calib_file ); } else { DBG(1, "read_calib_file: Error opening calibration file %s\ for reading\n", calib_filename); status = SANE_STATUS_EOF; } sanei_hp_free (calib_filename); return ( status ); } static SANE_Status write_calib_file (int nbytes, char *data, HpScsi scsi) { SANE_Status status = SANE_STATUS_GOOD; char *calib_filename; int err; FILE *calib_file; calib_filename = get_calib_filename ( scsi ); if (!calib_filename) return SANE_STATUS_NO_MEM; calib_file = fopen (calib_filename, "wb"); if ( calib_file ) { err = (putc ((nbytes >> 24) & 0xff, calib_file) == EOF); err |= (putc ((nbytes >> 16) & 0xff, calib_file) == EOF); err |= (putc ((nbytes >> 8) & 0xff, calib_file) == EOF); err |= (putc (nbytes & 0xff, calib_file) == EOF); err |= ((int)fwrite (data, 1, nbytes, calib_file) != nbytes); fclose (calib_file); if ( err ) { DBG(1, "write_calib_file: Error writing calibration data\n"); unlink (calib_filename); status = SANE_STATUS_EOF; } } else { DBG(1, "write_calib_file: Error opening calibration file %s\ for writing\n", calib_filename); status = SANE_STATUS_EOF; } sanei_hp_free (calib_filename); return (status); } static SANE_Status _program_media (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { int req_media, minval, maxval, current_media; HpScl scl = this->descriptor->scl_command; req_media = sanei_hp_accessor_getint(this->data_acsr, data); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, ¤t_media, &minval, &maxval) ); if (current_media == req_media) return SANE_STATUS_GOOD; /* Unload scanner */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_UNLOAD, 0) ); /* Select new media */ RETURN_IF_FAIL( hp_option_download(this, data, optset, scsi)); /* Update support list */ sanei_hp_device_support_probe (scsi); if (req_media == HP_MEDIA_PRINT) hp_download_calib_file (scsi); return SANE_STATUS_GOOD; } static SANE_Status _program_unload_after_scan (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); info->unload_after_scan = sanei_hp_accessor_getint(this->data_acsr, data); DBG(3,"program_unload_after_scan: flag = %lu\n", (unsigned long)info->unload_after_scan); return SANE_STATUS_GOOD; } static SANE_Status _program_lamp_off (HpOption __sane_unused__ this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData __sane_unused__ data) { DBG(3,"program_lamp_off: shut off lamp\n"); return sanei_hp_scl_set(scsi, SCL_LAMPTEST, 0); } static SANE_Status _program_scan_type (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { int req_scan_type; req_scan_type = sanei_hp_accessor_getint(this->data_acsr, data); if ( req_scan_type == HP_SCANTYPE_XPA ) { enum hp_scanmode_e scan_mode = sanei_hp_optset_scanmode(optset, data); static unsigned char xpa_matrix_coeff[] = { 0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80 }; static unsigned char xpa_tone_map[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xfe,0x0f, 0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f, 0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e, 0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e, 0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d, 0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d, 0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c, 0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c, 0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b, 0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a, 0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a, 0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09, 0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08, 0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07, 0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05, 0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02, 0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00,0x0f,0xfe,0x0f, 0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f, 0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e, 0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e, 0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d, 0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d, 0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c, 0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c, 0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b, 0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a, 0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a, 0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09, 0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08, 0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07, 0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05, 0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02, 0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00,0x0f,0xfe,0x0f, 0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f, 0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e, 0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e, 0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d, 0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d, 0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c, 0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c, 0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b, 0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a, 0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a, 0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09, 0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08, 0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07, 0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05, 0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02, 0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00 }; sanei_hp_scl_set(scsi, SCL_RESERVED1, 0); /* don't know */ sanei_hp_scl_set(scsi, SCL_10952, 0); /* Calibration mode */ if ( sanei_hp_is_active_xpa (scsi) && ( (scan_mode==HP_SCANMODE_COLOR) || (scan_mode==HP_SCANMODE_GRAYSCALE)) ) { DBG (3,"program_scan_type: set tone map for active XPA\n"); sanei_hp_scl_download (scsi, SCL_10x9MATRIX_COEFF, xpa_matrix_coeff, sizeof (xpa_matrix_coeff)); sanei_hp_scl_set(scsi, SCL_MATRIX, -1); /* Set matrix coefficient */ sanei_hp_scl_download (scsi, SCL_7x12TONE_MAP, xpa_tone_map, sizeof (xpa_tone_map)); sanei_hp_scl_set(scsi, SCL_TONE_MAP, -1); /* Select tone map */ } } return SANE_STATUS_GOOD; } static SANE_Status _program_change_doc (HpOption __sane_unused__ this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData __sane_unused__ data) { int istat; DBG(2, "program_change_doc: inquire ADF ready\n"); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_ADF_READY, &istat, 0, 0) ); if ( istat != 1 ) /* ADF not ready */ { DBG(2, "program_change_doc: ADF not ready\n"); return SANE_STATUS_INVAL; } DBG(2, "program_change_doc: inquire paper in ADF\n"); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_ADF_BIN, &istat, 0, 0) ); if ( istat == 0 ) /* Nothing in ADF BIN */ { DBG(2, "program_change_doc: nothing in ADF BIN. Just Unload.\n"); return sanei_hp_scl_set(scsi, SCL_UNLOAD, 0); } DBG(2, "program_change_doc: Clear errors and change document.\n"); RETURN_IF_FAIL( sanei_hp_scl_clearErrors (scsi) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_CHANGE_DOC, 0) ); return sanei_hp_scl_errcheck (scsi); } static SANE_Status _program_unload (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { hp_bool_t adfscan = ( sanei_hp_optset_scan_type (optset, data) == SCL_ADF_SCAN ); /* If we have an ADF, try to see if it is ready to unload */ if (adfscan) {int val; if ( sanei_hp_scl_inquire(scsi, SCL_ADF_RDY_UNLOAD, &val, 0, 0) == SANE_STATUS_GOOD ) { DBG(3, "program_unload: ADF is%sready to unload\n", val ? " " : " not "); } else { DBG(3, "program_unload: Command 'Ready to unload' not supported\n"); } } return hp_option_download(this, data, optset, scsi); } static SANE_Status _program_calibrate (HpOption __sane_unused__ this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData __sane_unused__ data) { struct passwd *pw; SANE_Status status = SANE_STATUS_GOOD; size_t calib_size; char *calib_buf; RETURN_IF_FAIL ( sanei_hp_scl_calibrate(scsi) ); /* Start calibration */ pw = getpwuid (getuid ()); /* Look if we can find our home directory */ if (!pw) return SANE_STATUS_GOOD; DBG(3, "_program_calibrate: Read calibration data\n"); RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP, &calib_size, &calib_buf) ); DBG(3, "_program_calibrate: Got %lu bytes of calibration data\n", (unsigned long) calib_size); write_calib_file (calib_size, calib_buf, scsi); sanei_hp_free (calib_buf); return (status); } /* The exposure time of the HP Photosmart can be changed by overwriting * some headers of the calibration data. The scanner uses a slower stepping * speed for higher exposure times */ static SANE_Status _program_ps_exposure_time (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { SANE_Status status = SANE_STATUS_GOOD; size_t calib_size = 0; char *calib_buf = NULL; int i; int option = hp_option_getint(this, data); static char *exposure[] = {"\x00\x64\x00\x64\x00\x64", /* 100% */ "\x00\x7d\x00\x7d\x00\x7d", /* 125% */ "\x00\x96\x00\x96\x00\x96", /* 150% */ "\x00\xaf\x00\xaf\x00\xaf", /* 175% */ "\x00\xc0\x00\xc0\x00\xc0", /* 200% */ "\x00\xe1\x00\xe1\x00\xe1", /* 225% */ "\x00\xfa\x00\xfa\x00\xfa", /* 250% */ "\x01\x13\x01\x13\x01\x13", /* 275% */ "\x01\x24\x01\x24\x01\x24", /* 300% */ "\x00\x64\x00\xc0\x01\x24"}; /* Negatives */ /* Negatives get some extra blue to penetrate the orange mask and less red to not saturate the red channel; R:G:B = 100:200:300 */ /* We don't use the 100% case. It may cause mechanical problems */ if ((option < 1) || (option > 9)) return 0; RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP, &calib_size, &calib_buf) ); DBG(3, "_program_ps_exposure_time: Got %lu bytes of calibration data\n", (unsigned long) calib_size); for (i = 0; i < 6; i++) calib_buf[24 + i] = exposure[option][i]; status = sanei_hp_scl_download ( scsi, SCL_CALIB_MAP, calib_buf, (size_t)calib_size); /* see what the scanner did to our alterations */ /* * RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP, * &calib_size, &calib_buf) ); * * for (i = 0; i < 9; i++) * DBG(1, ">%x ", (unsigned char) calib_buf[24 + i]); */ sanei_hp_free (calib_buf); return (status); } static SANE_Status _program_scanmode (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { enum hp_scanmode_e new_mode = hp_option_getint(this, data); int invert = 0; int fw_invert = 0; /* Flag: does firmware do inversion ? */ int is_model_4c = 0; enum hp_device_compat_e compat; hp_bool_t disable_xpa = ( sanei_hp_optset_scan_type (optset, data) != SCL_XPA_SCAN ); /* Seems that models 3c/4c/6100C invert image data at 10 bit by themself. */ /* So we must not invert it by the invert command. */ if ( (sanei_hp_device_probe (&compat,scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_4C) ) { is_model_4c = 1; DBG(3, "program_scanmode: model 3c/4c/6100C recognized\n"); } if (is_model_4c) { const HpDeviceInfo *info; int data_width; HpOption option; int is_preview = 0; /* Preview uses maximum 8 bit. So we don't need to check data width */ option = hp_optset_getByName (optset, SANE_NAME_PREVIEW); if ( option ) is_preview = hp_option_getint (option, data); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if ( (!is_preview) && hp_optset_isEnabled (optset, data, SANE_NAME_BIT_DEPTH, info)) { data_width = sanei_hp_optset_data_width (optset, data); if ((data_width == 10) || (data_width == 30)) { fw_invert = 1; DBG(3, "program_scanmode: firmware is doing inversion\n"); } } } /* Disabling XPA resets some settings in the scanner. */ /* Scanmode is the first we program. So set XPA prior to scanmode */ DBG(3, "program_scanmode: disable XPA = %d\n", (int)disable_xpa); sanei_hp_scl_set(scsi, SCL_XPA_DISABLE, disable_xpa); RETURN_IF_FAIL( hp_option_download(this, data, optset, scsi) ); switch (new_mode) { case HP_SCANMODE_GRAYSCALE: /* Make sure that it is not b/w. Correct data width will be set later */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 8) ); invert = 1; if (fw_invert) invert = 0; /* For active XPA we use a tone map. Dont invert */ if ( (!disable_xpa) && sanei_hp_is_active_xpa (scsi) ) invert = 0; break; case HP_SCANMODE_COLOR: invert = 1; if (fw_invert) invert = 0; /* For active XPA we use a tone map. Dont invert */ if ( (!disable_xpa) && sanei_hp_is_active_xpa (scsi) ) invert = 0; break; default: break; } return sanei_hp_scl_set(scsi, SCL_INVERSE_IMAGE, invert); } static SANE_Status _program_mirror_horiz (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int sec_dir, mirror = hp_option_getint(this, data); if ( mirror == HP_MIRROR_HORIZ_CONDITIONAL ) { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) ); mirror = (sec_dir == 1); } return sanei_hp_scl_set(scsi, SCL_MIRROR_IMAGE, mirror); } /* * Option enable predicates */ static hp_bool_t _enable_choice (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { SANE_String_Const * strlist = sanei_hp_accessor_choice_strlist((HpAccessorChoice) this->data_acsr, optset, data, info); _set_stringlist(this, data, strlist); assert(strlist[0]); return strlist[0] != 0; } #ifdef ENABLE_7x12_TONEMAPS static hp_bool_t _enable_rgb_maps (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA); return (cgam && hp_option_getint(cgam, data) && sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR); } #endif static hp_bool_t _enable_mono_map (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA); return (cgam && hp_option_getint(cgam, data) && ( sanei_hp_optset_scanmode(optset, data) != HP_SCANMODE_COLOR || ! hp_optset_getByName(optset, SANE_NAME_GAMMA_VECTOR_R) )); } static hp_bool_t _enable_rgb_matrix (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { HpOption type = hp_optset_get(optset, MATRIX_TYPE); return type && hp_option_getint(type, data) == HP_MATRIX_CUSTOM; } static hp_bool_t _enable_brightness (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA); HpScl scl = this->descriptor->scl_command; int simulate; simulate = ( sanei_hp_device_support_get ( info->devname, scl, 0, 0 ) != SANE_STATUS_GOOD ); /* If brightness is simulated, we only do it for gray/color */ if ( simulate ) {HpOption mode = hp_optset_get(optset, SCAN_MODE); int val = hp_option_getint (mode, data); int disable; disable = (val != HP_SCANMODE_GRAYSCALE) && (val != HP_SCANMODE_COLOR); if (disable) { if ( cgam ) /* Disable custom gamma. */ { val = 0; hp_option_set (cgam, data, &val, 0); } return 0; } } return !cgam || !hp_option_getint(cgam, data); } static hp_bool_t _enable_autoback (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_LINEART; } static hp_bool_t _enable_custom_gamma (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { HpScl scl_tonemap = SCL_8x8TONE_MAP; int id = SCL_INQ_ID(scl_tonemap); int simulate, minval, maxval; SANE_Status status; /* Check if download type supported */ status = sanei_hp_device_support_get ( info->devname, SCL_DOWNLOAD_TYPE, &minval, &maxval); simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval); /* If custom gamma is simulated, we only do it for gray/color */ if ( simulate ) {HpOption mode = hp_optset_get(optset, SCAN_MODE); int val; if ( mode ) { val = hp_option_getint (mode, data); if ((val != HP_SCANMODE_GRAYSCALE) && (val != HP_SCANMODE_COLOR)) { val = 0; hp_option_set (this, data, &val, 0); return 0; } } } return 1; } static hp_bool_t _enable_halftone (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE; } static hp_bool_t _enable_halftonevec (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { if (sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE) { HpOption dither = hp_optset_get(optset, HALFTONE_PATTERN); return dither && hp_option_getint(dither, data) == HP_DITHER_CUSTOM; } return 0; } static hp_bool_t _enable_data_width (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) {enum hp_scanmode_e mode; mode = sanei_hp_optset_scanmode (optset, data); return ( (mode == HP_SCANMODE_GRAYSCALE) || (mode == HP_SCANMODE_COLOR) ); } static hp_bool_t _enable_out8 (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { if (hp_optset_isEnabled (optset, data, SANE_NAME_BIT_DEPTH, info)) { int data_width = sanei_hp_optset_data_width (optset, data); return (((data_width > 8) && (data_width <= 16)) || (data_width > 24)); } return 0; } static hp_bool_t _enable_calibrate (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { HpOption media = hp_optset_get(optset, MEDIA); /* If we don't have the media button, we should have calibrate */ if ( !media ) return 1; return hp_option_getint(media, data) == HP_MEDIA_PRINT; } static SANE_Status hp_download_calib_file (HpScsi scsi) { int nbytes; char *calib_data; SANE_Status status; RETURN_IF_FAIL ( read_calib_file ( &nbytes, &calib_data, scsi ) ); DBG(3, "hp_download_calib_file: Got %d bytes calibration data\n", nbytes); status = sanei_hp_scl_download ( scsi, SCL_CALIB_MAP, calib_data, (size_t) nbytes); sanei_hp_free ( calib_data ); DBG(3, "hp_download_calib_file: download %s\n", (status == SANE_STATUS_GOOD) ? "successful" : "failed"); return status; } /* * The actual option descriptors. */ #if (defined(__IBMC__) || defined(__IBMCPP__)) #ifndef _AIX #define INT INT #endif #endif #define SCANNER_OPTION(name,type,unit) \ PASTE(SANE_NAME_,name), \ PASTE(SANE_TITLE_,name), \ PASTE(SANE_DESC_,name), \ PASTE(SANE_TYPE_,type), \ PASTE(SANE_UNIT_,unit), \ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT #define CONSTANT_OPTION(name,type,unit) \ PASTE(SANE_NAME_,name), \ PASTE(SANE_TITLE_,name), \ PASTE(SANE_DESC_,name), \ PASTE(SANE_TYPE_,type), \ PASTE(SANE_UNIT_,unit), \ SANE_CAP_SOFT_DETECT #define INTERNAL_OPTION(name,type,unit) \ PASTE(HP_NAME_,name), "", "", \ PASTE(SANE_TYPE_,type), \ PASTE(SANE_UNIT_,unit), \ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT #define OPTION_GROUP(name) "", name, "", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0 #define ADVANCED_GROUP(name) \ "", name, "", SANE_TYPE_GROUP, SANE_UNIT_NONE, SANE_CAP_ADVANCED #define REQUIRES(req) req #define NO_REQUIRES REQUIRES(0) static const struct hp_option_descriptor_s NUM_OPTIONS[1] = {{ CONSTANT_OPTION(NUM_OPTIONS, INT, NONE), NO_REQUIRES, _probe_num_options, 0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s SCAN_MODE_GROUP[1] = {{ OPTION_GROUP(SANE_I18N("Scan Mode")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; /* Preview stuff */ static const struct hp_option_descriptor_s PREVIEW_MODE[1] = {{ SCANNER_OPTION(PREVIEW, BOOL, NONE), NO_REQUIRES, _probe_bool, 0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_choice_s _scanmode_choices[] = { { HP_SCANMODE_LINEART, SANE_VALUE_SCAN_MODE_LINEART, 0, 0, 0 }, { HP_SCANMODE_HALFTONE, SANE_VALUE_SCAN_MODE_HALFTONE, 0, 0, 0 }, { HP_SCANMODE_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY, 0, 0, 0 }, { HP_SCANMODE_COLOR, SANE_VALUE_SCAN_MODE_COLOR, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SCAN_MODE[1] = {{ SCANNER_OPTION(SCAN_MODE, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_scanmode, 0, 1, 1, 1, 0, 0, SCL_OUTPUT_DATA_TYPE, 0, 0, 0, _scanmode_choices }}; static const struct hp_option_descriptor_s SCAN_RESOLUTION[1] = {{ SCANNER_OPTION(SCAN_RESOLUTION, INT, DPI), NO_REQUIRES, _probe_resolution, _program_resolution, 0, 0, 1, 0, 0, 1, SCL_X_RESOLUTION, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s DEVPIX_RESOLUTION[1] = {{ INTERNAL_OPTION(DEVPIX_RESOLUTION, INT, DPI), NO_REQUIRES, _probe_devpix, 0, 0, 0, 0, 0, 0, 1, SCL_DEVPIX_RESOLUTION, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s ENHANCEMENT_GROUP[1] = {{ OPTION_GROUP(SANE_I18N("Enhancement")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s BRIGHTNESS[1] = {{ SCANNER_OPTION(BRIGHTNESS, INT, NONE), NO_REQUIRES, _probe_int_brightness, _program_generic_simulate, _enable_brightness, 0, 0, 0, 0, 0, SCL_BRIGHTNESS, -127, 127, 0, 0 }}; static const struct hp_option_descriptor_s CONTRAST[1] = {{ SCANNER_OPTION(CONTRAST, INT, NONE), NO_REQUIRES, _probe_int_brightness, _program_generic_simulate, _enable_brightness, 0, 0, 0, 0, 0, SCL_CONTRAST, -127, 127, 0, 0 }}; #ifdef SCL_SHARPENING static const struct hp_option_descriptor_s SHARPENING[1] = {{ SCANNER_OPTION(SHARPENING, INT, NONE), NO_REQUIRES, _probe_int, _program_generic, 0, 0, 0, 0, 0, 0, SCL_SHARPENING, -127, 127, 0, 0 }}; #endif static const struct hp_option_descriptor_s AUTO_THRESHOLD[1] = {{ SCANNER_OPTION(AUTO_THRESHOLD, BOOL, NONE), NO_REQUIRES, _probe_bool, _program_generic, _enable_autoback, 0, 0, 0, 0, 0, SCL_AUTO_BKGRND, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s ADVANCED_GROUP[1] = {{ ADVANCED_GROUP(SANE_I18N("Advanced Options")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; /* FIXME: make this a choice? (BW or RGB custom) */ static const struct hp_option_descriptor_s CUSTOM_GAMMA[1] = {{ SCANNER_OPTION(CUSTOM_GAMMA, BOOL, NONE), NO_REQUIRES, _probe_custom_gamma, _program_tonemap, _enable_custom_gamma, 1, 0, 0, 0, 0, SCL_TONE_MAP, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_8x8[1] = {{ SCANNER_OPTION(GAMMA_VECTOR, FIXED, NONE), NO_REQUIRES, _probe_gamma_vector, 0, _enable_mono_map, 0, 0, 0, 0, 0, SCL_8x8TONE_MAP, 0, 0, 0, 0 }}; #ifdef ENABLE_7x12_TONEMAPS static const struct hp_option_descriptor_s GAMMA_VECTOR_7x12[1] = {{ SCANNER_OPTION(GAMMA_VECTOR, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_mono_map, 0, 0, 0, 0, 0, SCL_BW7x12TONE_MAP, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s RGB_TONEMAP[1] = {{ INTERNAL_OPTION(RGB_TONEMAP, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, 0, 0, 0, 0, 0, 0, SCL_7x12TONE_MAP, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_R[1] = {{ SCANNER_OPTION(GAMMA_VECTOR_R, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_rgb_maps, 0,0,0,0,0,0,0,0,0,0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_G[1] = {{ SCANNER_OPTION(GAMMA_VECTOR_G, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_rgb_maps, 0,0,0,0,0,0,0,0,0,0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_B[1] = {{ SCANNER_OPTION(GAMMA_VECTOR_B, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_rgb_maps, 0,0,0,0,0,0,0,0,0,0 }}; #endif static const struct hp_choice_s _halftone_choices[] = { { HP_DITHER_COARSE, SANE_I18N("Coarse"), 0, 0, 0 }, { HP_DITHER_FINE, SANE_I18N("Fine"), 0, 0, 0 }, { HP_DITHER_BAYER, SANE_I18N("Bayer"), 0, 0, 0 }, { HP_DITHER_VERTICAL, SANE_I18N("Vertical"), 0, 0, 0 }, { HP_DITHER_HORIZONTAL, SANE_I18N("Horizontal"), 0, 1, 0 }, { HP_DITHER_CUSTOM, SANE_I18N("Custom"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s HALFTONE_PATTERN[1] = {{ SCANNER_OPTION(HALFTONE_PATTERN, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_dither, _enable_halftone, 1, 0, 0, 0, 0, SCL_BW_DITHER, 0, 0, 0, _halftone_choices }}; /* FIXME: Halftone dimension? */ #ifdef ENABLE_16X16_DITHERS static const struct hp_option_descriptor_s HALFTONE_PATTERN_16x16[1] = {{ SCANNER_OPTION(HALFTONE_PATTERN, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_4P | HP_COMPAT_4C | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_horiz_dither, 0, _enable_halftonevec, 0, 0, 0, 0, 0, SCL_BW16x16DITHER }}; static const struct hp_option_descriptor_s HORIZONTAL_DITHER_16x16[1] = {{ INTERNAL_OPTION(HORIZONTAL_DITHER, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_4P | HP_COMPAT_4C | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_horiz_dither, 0, 0, 0, 0, 0, 0, 0, SCL_BW16x16DITHER }}; #endif static const struct hp_option_descriptor_s HALFTONE_PATTERN_8x8[1] = {{ SCANNER_OPTION(HALFTONE_PATTERN, FIXED, NONE), NO_REQUIRES, _probe_horiz_dither, 0, _enable_halftonevec, 0, 0, 0, 0, 0, SCL_BW8x8DITHER, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s HORIZONTAL_DITHER_8x8[1] = {{ INTERNAL_OPTION(HORIZONTAL_DITHER, FIXED, NONE), NO_REQUIRES, _probe_horiz_dither, 0, 0, 0, 0, 0, 0, 0, SCL_BW8x8DITHER, 0, 0, 0, 0 }}; static const struct hp_choice_s _matrix_choices[] = { { HP_MATRIX_AUTO, SANE_I18N("Auto"), 0, 1, 0 }, { HP_MATRIX_RGB, SANE_I18N("NTSC RGB"), _cenable_incolor, 0, 0 }, { HP_MATRIX_XPA_RGB, SANE_I18N("XPA RGB"), _cenable_incolor, 0, 0 }, { HP_MATRIX_PASS, SANE_I18N("Pass-through"), _cenable_incolor, 0, 0 }, { HP_MATRIX_BW, SANE_I18N("NTSC Gray"), _cenable_notcolor, 0, 0 }, { HP_MATRIX_XPA_BW, SANE_I18N("XPA Gray"), _cenable_notcolor, 0, 0 }, { HP_MATRIX_RED, SANE_I18N("Red"), _cenable_notcolor, 0, 0 }, { HP_MATRIX_GREEN, SANE_I18N("Green"), _cenable_notcolor, 1, 0 }, { HP_MATRIX_BLUE, SANE_I18N("Blue"), _cenable_notcolor, 0, 0 }, #ifdef ENABLE_CUSTOM_MATRIX { HP_MATRIX_CUSTOM, SANE_I18N("Custom"), 0, 0, 0 }, #endif { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MATRIX_TYPE[1] = {{ SCANNER_OPTION(MATRIX_TYPE, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_matrix, _enable_choice, 1, 0, 0, 0, 0, SCL_MATRIX, 0, 0, 0, _matrix_choices }}; static const struct hp_option_descriptor_s MATRIX_RGB[1] = {{ SCANNER_OPTION(MATRIX_RGB, FIXED, NONE), NO_REQUIRES, _probe_matrix, 0, _enable_rgb_matrix, 0, 0, 0, 0, 0, SCL_8x9MATRIX_COEFF, 0, 0, 0, 0 }}; #ifdef FAKE_COLORSEP_MATRIXES static const struct hp_option_descriptor_s SEPMATRIX[1] = {{ INTERNAL_OPTION(SEPMATRIX, FIXED, NONE), NO_REQUIRES, _probe_vector, 0, 0, 0, 0, 0, 0, 0, SCL_8x9MATRIX_COEFF, 0, 0, 0, 0 }}; #endif #ifdef ENABLE_10BIT_MATRIXES static const struct hp_option_descriptor_s MATRIX_RGB10[1] = {{ SCANNER_OPTION(MATRIX_RGB, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_matrix, 0, _enable_rgb_matrix, 0, 0, 0, 0, 0, SCL_10x9MATRIX_COEFF, 0, 0, 0, 0 }}; #endif #ifdef NotYetSupported static const struct hp_option_descriptor_s BWMATRIX_GRAY10[1] = {{ SCANNER_OPTION(MATRIX_GRAY, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_matrix, 0, _enable_gray_matrix, 0, 0, 0, 0, 0, SCL_10x3MATRIX_COEFF, 0, 0, 0, 0 }}; #endif static const struct hp_choice_s _scan_speed_choices[] = { { 0, SANE_I18N("Auto"), 0, 0, 0 }, { 1, SANE_I18N("Slow"), 0, 0, 0 }, { 2, SANE_I18N("Normal"), 0, 0, 0 }, { 3, SANE_I18N("Fast"), 0, 0, 0 }, { 4, SANE_I18N("Extra Fast"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SCAN_SPEED[1] = {{ SCANNER_OPTION(SCAN_SPEED, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 1, SCL_SPEED, 0, 0, 0, _scan_speed_choices }}; static const struct hp_choice_s _smoothing_choices[] = { { 0, SANE_I18N("Auto"), 0, 0, 0 }, { 3, SANE_I18N("Off"), 0, 0, 0 }, { 1, SANE_I18N("2-pixel"), 0, 0, 0 }, { 2, SANE_I18N("4-pixel"), 0, 0, 0 }, { 4, SANE_I18N("8-pixel"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SMOOTHING[1] = {{ SCANNER_OPTION(SMOOTHING, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_FILTER, 0, 0, 0, _smoothing_choices }}; static const struct hp_choice_s _media_choices[] = { { HP_MEDIA_PRINT, SANE_I18N("Print"), 0, 0, 0 }, { HP_MEDIA_SLIDE, SANE_I18N("Slide"), 0, 0, 0 }, { HP_MEDIA_NEGATIVE, SANE_I18N("Film-strip"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MEDIA[1] = {{ SCANNER_OPTION(MEDIA, STRING, NONE), NO_REQUIRES, _probe_choice, _program_media, 0, 1, 1, 1, 1, 0, SCL_MEDIA, 0, 0, 0, _media_choices }}; static const struct hp_choice_s _data_widths[] = { {1, "1", 0, 0, 0}, {8, "8", 0, 0, 0}, {10, "10", 0, 0, 0}, {12, "12", 0, 0, 0}, {14, "14", 0, 0, 0}, {16, "16", 0, 0, 0}, {0, 0, 0, 0, 0} }; static const struct hp_option_descriptor_s BIT_DEPTH[1] = {{ SCANNER_OPTION(BIT_DEPTH, STRING, NONE), NO_REQUIRES, _probe_choice, _program_data_width, _enable_data_width, 1, 1, 1, 0, 1, SCL_DATA_WIDTH, 0, 0, 0, _data_widths }}; static const struct hp_option_descriptor_s OUT8[1] = { { SCANNER_OPTION(OUTPUT_8BIT, BOOL, NONE), NO_REQUIRES, /* enum hp_device_compat_e requires */ _probe_bool, /* SANE_Status (*probe)() */ 0, /* SANE_Status (*program)() */ _enable_out8, /* hp_bool_t (*enable)() */ 0, /* hp_bool_t has_global_effect */ 0, /* hp_bool_t affects_scan_params */ 0, /* hp_bool_t program_immediate */ 0, /* hp_bool_t suppress_for_scan */ 0, /* hp_bool_t may_change */ 0, /* HpScl scl_command */ 0, /* int minval */ 0, /* int maxval */ 0, /* int startval */ 0 /* HpChoice choices */ } }; /* The 100% setting may cause problems within the scanner */ static const struct hp_choice_s _ps_exposure_times[] = { /* {0, "100%", 0, 0, 0}, */ { 0, SANE_I18N("Default"), 0, 0, 0 }, {1, "125%", 0, 0, 0}, {2, "150%", 0, 0, 0}, {3, "175%", 0, 0, 0}, {4, "200%", 0, 0, 0}, {5, "225%", 0, 0, 0}, {6, "250%", 0, 0, 0}, {7, "275%", 0, 0, 0}, {8, "300%", 0, 0, 0}, {9, SANE_I18N("Negative"), 0, 0, 0}, {0, 0, 0, 0, 0} }; /* Photosmart exposure time */ static const struct hp_option_descriptor_s PS_EXPOSURE_TIME[1] = {{ SCANNER_OPTION(PS_EXPOSURE_TIME, STRING, NONE), REQUIRES( HP_COMPAT_PS ), _probe_ps_exposure_time, _program_ps_exposure_time, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _ps_exposure_times }}; /* Normal, ADF or XPA scanning. Because scanning from ADF can change */ /* the extent of the scanning area, this option is marked to change */ /* global settings. The user should switch to ADF scanning after */ /* placing paper in the ADF. */ static const struct hp_choice_s _scan_types[] = { { HP_SCANTYPE_NORMAL, SANE_I18N("Normal"), 0, 0, 0 }, { HP_SCANTYPE_ADF, SANE_I18N("ADF"), 0, 0, 0 }, { HP_SCANTYPE_XPA, SANE_I18N("XPA"), 0, 0, 0 }, {0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SCAN_SOURCE[1] = {{ SCANNER_OPTION(SCAN_SOURCE, STRING, NONE), NO_REQUIRES, _probe_scan_type, _program_scan_type, 0, 1, 1, 1, 0, 0, SCL_START_SCAN, 0, 0, 0, _scan_types }}; /* Unload after is only necessary for PhotoScanner */ static const struct hp_option_descriptor_s UNLOAD_AFTER_SCAN[1] = {{ SCANNER_OPTION(UNLOAD_AFTER_SCAN, BOOL, NONE), REQUIRES(HP_COMPAT_PS), _probe_bool, _program_unload_after_scan, 0, 0, 0, 0, 1, 0, SCL_UNLOAD, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s CHANGE_DOC[1] = {{ SCANNER_OPTION(CHANGE_DOC, BUTTON, NONE), NO_REQUIRES, _probe_change_doc, _program_change_doc, 0, 1, 1, 1, 1, 0, SCL_CHANGE_DOC, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s UNLOAD[1] = {{ SCANNER_OPTION(UNLOAD, BUTTON, NONE), NO_REQUIRES, _probe_unload, _program_unload, 0, 0, 0, 1, 1, 0, SCL_UNLOAD, 0, 0, 0, 0 }}; /* There is no inquire ID-for the calibrate command. */ /* So here we need the requiries. */ static const struct hp_option_descriptor_s CALIBRATE[1] = {{ SCANNER_OPTION(CALIBRATE, BUTTON, NONE), REQUIRES(HP_COMPAT_PS), _probe_calibrate, _program_calibrate, _enable_calibrate, 0, 0, 1, 1, 0, SCL_CALIBRATE, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s GEOMETRY_GROUP[1] = {{ ADVANCED_GROUP(SANE_I18N("Geometry")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s SCAN_TL_X[1] = {{ SCANNER_OPTION(SCAN_TL_X, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_X_POS, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s SCAN_TL_Y[1] = {{ SCANNER_OPTION(SCAN_TL_Y, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_Y_POS, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s SCAN_BR_X[1] = {{ SCANNER_OPTION(SCAN_BR_X, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_X_EXTENT, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s SCAN_BR_Y[1] = {{ SCANNER_OPTION(SCAN_BR_Y, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_Y_EXTENT, 0, 0, 0, 0 }}; static const struct hp_choice_s _mirror_horiz_choices[] = { { HP_MIRROR_HORIZ_OFF, SANE_I18N("Off"), 0, 0, 0 }, { HP_MIRROR_HORIZ_ON, SANE_I18N("On"), 0, 0, 0 }, { HP_MIRROR_HORIZ_CONDITIONAL, SANE_I18N("Conditional"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MIRROR_HORIZ[1] = {{ SCANNER_OPTION(MIRROR_HORIZ, STRING, NONE), NO_REQUIRES, _probe_mirror_horiz, _program_mirror_horiz, 0, 0, 0, 0, 0, 0, SCL_MIRROR_IMAGE, 0, 0, 0, _mirror_horiz_choices }}; static const struct hp_choice_s _mirror_vert_choices[] = { { HP_MIRROR_VERT_OFF, SANE_I18N("Off"), 0, 0, 0 }, { HP_MIRROR_VERT_ON, SANE_I18N("On"), 0, 0, 0 }, { HP_MIRROR_VERT_CONDITIONAL, SANE_I18N("Conditional"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MIRROR_VERT[1] = {{ SCANNER_OPTION(MIRROR_VERT, STRING, NONE), NO_REQUIRES, _probe_mirror_vert, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _mirror_vert_choices }}; static const struct hp_option_descriptor_s BUTTON_WAIT[1] = { { SCANNER_OPTION(BUTTON_WAIT, BOOL, NONE), NO_REQUIRES, /* enum hp_device_compat_e requires */ _probe_front_button, /* SANE_Status (*probe)() */ 0, /* SANE_Status (*program)() */ 0, /* hp_bool_t (*enable)() */ 0, /* hp_bool_t has_global_effect */ 0, /* hp_bool_t affects_scan_params */ 0, /* hp_bool_t program_immediate */ 0, /* hp_bool_t suppress_for_scan */ 0, /* hp_bool_t may_change */ 0, /* HpScl scl_command */ 0, /* int minval */ 0, /* int maxval */ 0, /* int startval */ 0 /* HpChoice choices */ } }; static const struct hp_option_descriptor_s LAMP_OFF[1] = { { SCANNER_OPTION(LAMP_OFF, BUTTON, NONE), /* Lamp off instruction not supported by Photosmart */ REQUIRES( HP_COMPAT_PLUS | HP_COMPAT_2C | HP_COMPAT_2P | HP_COMPAT_2CX | HP_COMPAT_4C | HP_COMPAT_3P | HP_COMPAT_4P | HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), /* enum hp_device_compat_e requires */ _probe_bool, /* SANE_Status (*probe)() */ _program_lamp_off, /* SANE_Status (*program)() */ 0, /* hp_bool_t (*enable)() */ 0, /* hp_bool_t has_global_effect */ 0, /* hp_bool_t affects_scan_params */ 1, /* hp_bool_t program_immediate */ 1, /* hp_bool_t suppress_for_scan */ 0, /* hp_bool_t may_change */ SCL_LAMPTEST, /* HpScl scl_command */ 0, /* int minval */ 0, /* int maxval */ 0, /* int startval */ 0 /* HpChoice choices */ } }; #ifdef HP_EXPERIMENTAL static const struct hp_choice_s _range_choices[] = { { 0, "0", 0, 0, 0 }, { 1, "1", 0, 0, 0 }, { 2, "2", 0, 0, 0 }, { 3, "3", 0, 0, 0 }, { 4, "4", 0, 0, 0 }, { 5, "5", 0, 0, 0 }, { 6, "6", 0, 0, 0 }, { 7, "7", 0, 0, 0 }, { 8, "8", 0, 0, 0 }, { 9, "9", 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s EXPERIMENT_GROUP[1] = {{ ADVANCED_GROUP(SANE_I18N("Experiment")) 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s PROBE_10470[1] = {{ SCANNER_OPTION(10470, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10470, 0, 0, 0, _range_choices }}; static const struct hp_option_descriptor_s PROBE_10485[1] = {{ SCANNER_OPTION(10485, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10485, 0, 0, 0, _range_choices }}; static const struct hp_option_descriptor_s PROBE_10952[1] = {{ SCANNER_OPTION(10952, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10952, 0, 0, 0, _range_choices }}; static const struct hp_option_descriptor_s PROBE_10967[1] = {{ SCANNER_OPTION(10967, INT, NONE), NO_REQUIRES, _probe_int, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10967, 0, 0, 0, 0 }}; #endif static HpOptionDescriptor hp_options[] = { NUM_OPTIONS, SCAN_MODE_GROUP, PREVIEW_MODE, SCAN_MODE, SCAN_RESOLUTION, DEVPIX_RESOLUTION, ENHANCEMENT_GROUP, BRIGHTNESS, CONTRAST, #ifdef SCL_SHARPENING SHARPENING, #endif AUTO_THRESHOLD, ADVANCED_GROUP, CUSTOM_GAMMA, #ifdef ENABLE_7x12_TONEMAPS GAMMA_VECTOR_7x12, RGB_TONEMAP, GAMMA_VECTOR_R, GAMMA_VECTOR_G, GAMMA_VECTOR_B, #endif GAMMA_VECTOR_8x8, MATRIX_TYPE, #ifdef FAKE_COLORSEP_MATRIXES SEPMATRIX, #endif #ifdef ENABLE_10BIT_MATRIXES MATRIX_RGB10, /* FIXME: unsupported: MATRIX_GRAY10, */ #endif MATRIX_RGB, HALFTONE_PATTERN, #ifdef ENABLE_16X16_DITHERS HALFTONE_PATTERN_16x16, HORIZONTAL_DITHER_16x16, #endif HALFTONE_PATTERN_8x8, HORIZONTAL_DITHER_8x8, SCAN_SPEED, SMOOTHING, MEDIA, PS_EXPOSURE_TIME, BIT_DEPTH, OUT8, SCAN_SOURCE, BUTTON_WAIT, LAMP_OFF, UNLOAD_AFTER_SCAN, CHANGE_DOC, UNLOAD, CALIBRATE, GEOMETRY_GROUP, SCAN_TL_X, SCAN_TL_Y, SCAN_BR_X, SCAN_BR_Y, MIRROR_HORIZ, MIRROR_VERT, #ifdef HP_EXPERIMENTAL EXPERIMENT_GROUP, PROBE_10470, PROBE_10485, PROBE_10952, PROBE_10967, #endif 0 }; /* * class HpOptSet */ struct hp_optset_s { #define OPTION_LIST_MAX sizeof(hp_options)/sizeof(hp_options[0]) HpOption options[OPTION_LIST_MAX]; size_t num_sane_opts; size_t num_opts; /* Magic accessors to get coord in actual scan pixels: */ HpAccessor tl_x, tl_y, br_x, br_y; }; static HpOption hp_optset_get (HpOptSet this, HpOptionDescriptor optd) { HpOption * optp = this->options; int i = this->num_opts; while (i--) { if ((*optp)->descriptor == optd) return *optp; optp++; } return 0; } static HpOption hp_optset_getByIndex (HpOptSet this, int optnum) { if ((optnum < 0) || (optnum >= (int)this->num_sane_opts)) return 0; return this->options[optnum]; } static HpOption hp_optset_getByName (HpOptSet this, const char * name) { HpOption * optp = this->options; int i = this->num_opts; while (i--) { if (strcmp((*optp)->descriptor->name, name) == 0) return *optp; optp++; } return 0; } static _HpOption _hp_optset_get (HpOptSet this, HpOptionDescriptor opt) { /* Cast away const-ness */ return (_HpOption) hp_optset_get(this, opt); } enum hp_scanmode_e sanei_hp_optset_scanmode (HpOptSet this, HpData data) { HpOption mode = hp_optset_get(this, SCAN_MODE); assert(mode); return hp_option_getint(mode, data); } hp_bool_t sanei_hp_optset_output_8bit (HpOptSet this, HpData data) { HpOption option_out8; int out8; option_out8 = hp_optset_get(this, OUT8); if (option_out8) { out8 = hp_option_getint(option_out8, data); return out8; } return 0; } /* Returns the data width that is send to the scanner, depending */ /* on the scanmode. (b/w: 1, gray: 8..12, color: 24..36 */ int sanei_hp_optset_data_width (HpOptSet this, HpData data) { enum hp_scanmode_e mode = sanei_hp_optset_scanmode (this, data); int datawidth = 0; HpOption opt_dwidth; switch (mode) { case HP_SCANMODE_LINEART: case HP_SCANMODE_HALFTONE: datawidth = 1; break; case HP_SCANMODE_GRAYSCALE: opt_dwidth = hp_optset_get(this, BIT_DEPTH); if (opt_dwidth) datawidth = hp_option_getint (opt_dwidth, data); else datawidth = 8; break; case HP_SCANMODE_COLOR: opt_dwidth = hp_optset_get(this, BIT_DEPTH); if (opt_dwidth) datawidth = 3 * hp_option_getint (opt_dwidth, data); else datawidth = 24; break; } return datawidth; } hp_bool_t sanei_hp_optset_mirror_vert (HpOptSet this, HpData data, HpScsi scsi) { HpOption mode; int mirror, sec_dir; mode = hp_optset_get(this, MIRROR_VERT); assert(mode); mirror = hp_option_getint(mode, data); if (mirror == HP_MIRROR_VERT_CONDITIONAL) { mirror = HP_MIRROR_VERT_OFF; if ( ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) == SANE_STATUS_GOOD ) && ( sec_dir == 1 ) ) mirror = HP_MIRROR_VERT_ON; } return mirror == HP_MIRROR_VERT_ON; } hp_bool_t sanei_hp_optset_start_wait(HpOptSet this, HpData data) { HpOption mode; int wait; if ((mode = hp_optset_get(this, BUTTON_WAIT)) == 0) return(0); wait = hp_option_getint(mode, data); return(wait); } HpScl sanei_hp_optset_scan_type (HpOptSet this, HpData data) { HpOption mode; HpScl scl = SCL_START_SCAN; int scantype; mode = hp_optset_get(this, SCAN_SOURCE); if (mode) { scantype = hp_option_getint(mode, data); DBG(5, "sanei_hp_optset_scan_type: scantype=%d\n", scantype); switch (scantype) { case 1: scl = SCL_ADF_SCAN; break; case 2: scl = SCL_XPA_SCAN; break; default: scl = SCL_START_SCAN; break; } } return scl; } static void hp_optset_add (HpOptSet this, HpOption opt) { assert(this->num_opts < OPTION_LIST_MAX); /* * Keep internal options at the end of the list. */ if (hp_option_isInternal(opt)) this->options[this->num_opts] = opt; else { if (this->num_opts != this->num_sane_opts) memmove(&this->options[this->num_sane_opts + 1], &this->options[this->num_sane_opts], ( (this->num_opts - this->num_sane_opts) * sizeof(*this->options) )); this->options[this->num_sane_opts++] = opt; } this->num_opts++; } static SANE_Status hp_optset_fix_geometry_options (HpOptSet this) { _HpOption tl_x = _hp_optset_get(this, SCAN_TL_X); _HpOption tl_y = _hp_optset_get(this, SCAN_TL_Y); _HpOption br_x = _hp_optset_get(this, SCAN_BR_X); _HpOption br_y = _hp_optset_get(this, SCAN_BR_Y); HpOption scanres = hp_optset_get(this, SCAN_RESOLUTION); HpOption devpix = hp_optset_get(this, DEVPIX_RESOLUTION); HpAccessor tl_xa, tl_ya, br_xa, br_ya; assert(tl_x && tl_y && br_x && br_y); /* Geometry options missing */ tl_xa = tl_x->data_acsr; tl_ya = tl_y->data_acsr; br_xa = br_x->data_acsr; br_ya = br_y->data_acsr; assert(tl_xa && tl_ya && br_xa && br_ya); assert(scanres->data_acsr && devpix->data_acsr); /* Magic accessors that will read out in device pixels */ tl_x->data_acsr = sanei_hp_accessor_geometry_new(tl_xa, br_xa, 0, devpix->data_acsr); tl_y->data_acsr = sanei_hp_accessor_geometry_new(tl_ya, br_ya, 0, devpix->data_acsr); br_x->data_acsr = sanei_hp_accessor_geometry_new(br_xa, tl_xa, 1, devpix->data_acsr); br_y->data_acsr = sanei_hp_accessor_geometry_new(br_ya, tl_ya, 1, devpix->data_acsr); if (!tl_x->data_acsr || !tl_y->data_acsr || !br_x->data_acsr || !br_y->data_acsr) return SANE_STATUS_NO_MEM; /* Magic accessors that will read out in scan pixels */ this->tl_x = sanei_hp_accessor_geometry_new(tl_xa, br_xa, 0, scanres->data_acsr); this->tl_y = sanei_hp_accessor_geometry_new(tl_ya, br_ya, 0, scanres->data_acsr); this->br_x = sanei_hp_accessor_geometry_new(br_xa, tl_xa, 1, scanres->data_acsr); this->br_y = sanei_hp_accessor_geometry_new(br_ya, tl_ya, 1, scanres->data_acsr); if (!this->tl_x || !this->tl_y || !this->br_x || !this->br_y) return SANE_STATUS_NO_MEM; return SANE_STATUS_GOOD; } static void hp_optset_reprogram (HpOptSet this, HpData data, HpScsi scsi) { int i; DBG(5, "hp_optset_reprogram: %lu options\n", (unsigned long) this->num_opts); for (i = 0; i < (int)this->num_opts; i++) hp_option_reprogram(this->options[i], this, data, scsi); DBG(5, "hp_optset_reprogram: finished\n"); } static void hp_optset_reprobe (HpOptSet this, HpData data, HpScsi scsi) { int i; DBG(5, "hp_optset_reprobe: %lu options\n", (unsigned long) this->num_opts); for (i = 0; i < (int)this->num_opts; i++) hp_option_reprobe(this->options[i], this, data, scsi); DBG(5, "hp_optset_reprobe: finished\n"); } static void hp_optset_updateEnables (HpOptSet this, HpData data, const HpDeviceInfo *info) { int i; DBG(5, "hp_optset_updateEnables: %lu options\n", (unsigned long) this->num_opts); for (i = 0; i < (int)this->num_opts; i++) hp_option_updateEnable(this->options[i], this, data, info); } static hp_bool_t hp_optset_isEnabled (HpOptSet this, HpData data, const char *name, const HpDeviceInfo *info) { HpOption optpt; optpt = hp_optset_getByName (this, name); if (!optpt) /* Not found ? Not enabled */ return 0; if (!(optpt->descriptor->enable)) /* No enable necessary ? Enabled */ return 1; return (*optpt->descriptor->enable)(optpt, this, data, info); } /* This function is only called from sanei_hp_handle_startScan() */ SANE_Status sanei_hp_optset_download (HpOptSet this, HpData data, HpScsi scsi) { int i, errcount = 0; DBG(3, "Start downloading parameters to scanner\n"); /* Reset scanner to wake it up */ /* Reset would switch off XPA lamp and switch on scanner lamp. */ /* Only do a reset if not in active XPA mode */ if ( (sanei_hp_optset_scan_type (this, data) != SCL_XPA_SCAN) || (!sanei_hp_is_active_xpa (scsi)) ) { RETURN_IF_FAIL(sanei_hp_scl_reset (scsi)); } RETURN_IF_FAIL(sanei_hp_scl_clearErrors (scsi)); sanei_hp_device_simulate_clear ( sanei_hp_scsi_devicename (scsi) ); for (i = 0; i < (int)this->num_opts; i++) { if ( (this->options[i])->descriptor->suppress_for_scan ) { DBG(3,"sanei_hp_optset_download: %s suppressed for scan\n", (this->options[i])->descriptor->name); } else { RETURN_IF_FAIL( hp_option_program(this->options[i], scsi, this, data) ); if ( sanei_hp_scl_errcheck (scsi) != SANE_STATUS_GOOD ) { errcount++; DBG(3, "Option %s generated scanner error\n", this->options[i]->descriptor->name); RETURN_IF_FAIL(sanei_hp_scl_clearErrors (scsi)); } } } DBG(3, "Downloading parameters finished.\n"); /* Check preview */ {HpOption option; int is_preview, data_width; const HpDeviceInfo *info; option = hp_optset_getByName (this, SANE_NAME_PREVIEW); if ( option ) { is_preview = hp_option_getint (option, data); if ( is_preview ) { /* For preview we only use 8 bit per channel */ DBG(3, "sanei_hp_optset_download: Set up preview options\n"); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if (hp_optset_isEnabled (this, data, SANE_NAME_BIT_DEPTH, info)) { data_width = sanei_hp_optset_data_width (this, data); if (data_width > 24) { sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 24); } else if ((data_width > 8) && (data_width <= 16)) { sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 8); } } } } } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_optset_new(HpOptSet * newp, HpScsi scsi, HpDevice dev) { HpOptionDescriptor * ptr; HpOptSet this = sanei_hp_allocz(sizeof(*this)); SANE_Status status; HpOption option; const HpDeviceInfo *info; if (!this) return SANE_STATUS_NO_MEM; /* FIXME: more DBG's */ for (ptr = hp_options; *ptr; ptr++) { HpOptionDescriptor desc = *ptr; DBG(8, "sanei_hp_optset_new: %s\n", desc->name); if (desc->requires && !sanei_hp_device_compat(dev, desc->requires)) continue; if (desc->type != SANE_TYPE_GROUP && hp_optset_getByName(this, desc->name)) continue; status = hp_option_descriptor_probe(desc, scsi, this, dev->data, &option); if (UNSUPPORTED(status)) continue; if (FAILED(status)) { DBG(1, "Option '%s': probe failed: %s\n", desc->name, sane_strstatus(status)); sanei_hp_free(this); return status; } hp_optset_add(this, option); } /* Set NUM_OPTIONS */ assert(this->options[0]->descriptor == NUM_OPTIONS); sanei_hp_accessor_setint(this->options[0]->data_acsr, dev->data, this->num_sane_opts); /* Now for some kludges */ status = hp_optset_fix_geometry_options(this); if (FAILED(status)) { sanei_hp_free(this); return status; } info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); hp_optset_updateEnables(this, dev->data, info); *newp = this; return SANE_STATUS_GOOD; } hp_bool_t sanei_hp_optset_isImmediate (HpOptSet this, int optnum) { HpOption opt = hp_optset_getByIndex(this, optnum); if (!opt) return 0; return hp_option_isImmediate (opt); } SANE_Status sanei_hp_optset_control (HpOptSet this, HpData data, int optnum, SANE_Action action, void * valp, SANE_Int *infop, HpScsi scsi, hp_bool_t immediate) { HpOption opt = hp_optset_getByIndex(this, optnum); SANE_Int my_info = 0, my_val = 0; DBG(3,"sanei_hp_optset_control: %s\n", opt ? opt->descriptor->name : ""); if (infop) *infop = 0; else infop = &my_info; if (!opt) return SANE_STATUS_INVAL; /* There are problems with SANE_ACTION_GET_VALUE and valp == 0. */ /* Check if we really need valp. */ if ((action == SANE_ACTION_GET_VALUE) && (!valp)) { /* Options without a value ? */ if ( (opt->descriptor->type == SANE_TYPE_BUTTON) || (opt->descriptor->type == SANE_TYPE_GROUP)) { valp = &my_val; /* Just simulate a return value locally. */ } else /* Others must return a value. So this is invalid */ { DBG(1, "sanei_hp_optset_control: get value, but valp == 0\n"); return SANE_STATUS_INVAL; } } if (immediate) RETURN_IF_FAIL( hp_option_imm_control(this, opt, data, action, valp, infop, scsi) ); else RETURN_IF_FAIL( hp_option_control(opt, data, action, valp, infop ) ); if ((*infop & SANE_INFO_RELOAD_OPTIONS) != 0) {const HpDeviceInfo *info; DBG(3,"sanei_hp_optset_control: reprobe\n"); /* At first we try to reprogram the parameters that may have changed */ /* by an option that had a global effect. This is necessary to */ /* specify options in an arbitrary order. Example: */ /* Changing scan mode resets scan resolution in the scanner. */ /* If resolution is set from the API before scan mode, we must */ /* reprogram the resolution afterwards. */ hp_optset_reprogram(this, data, scsi); hp_optset_reprobe(this, data, scsi); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); hp_optset_updateEnables(this, data, info); } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_optset_guessParameters (HpOptSet this, HpData data, SANE_Parameters * p) { /* These are magic accessors which actually get the extent, not the * absolute position... */ int xextent = sanei_hp_accessor_getint(this->br_x, data); int yextent = sanei_hp_accessor_getint(this->br_y, data); int data_width; assert(xextent > 0 && yextent > 0); p->last_frame = SANE_TRUE; p->pixels_per_line = xextent; p->lines = yextent; switch (sanei_hp_optset_scanmode(this, data)) { case HP_SCANMODE_LINEART: /* Lineart */ case HP_SCANMODE_HALFTONE: /* Halftone */ p->format = SANE_FRAME_GRAY; p->depth = 1; p->bytes_per_line = (p->pixels_per_line + 7) / 8; break; case HP_SCANMODE_GRAYSCALE: /* Grayscale */ p->format = SANE_FRAME_GRAY; p->depth = 8; p->bytes_per_line = p->pixels_per_line; if ( !sanei_hp_optset_output_8bit (this, data) ) { data_width = sanei_hp_optset_data_width (this, data); if ( data_width > 8 ) { p->depth *= 2; p->bytes_per_line *= 2; } } break; case HP_SCANMODE_COLOR: /* RGB */ p->format = SANE_FRAME_RGB; p->depth = 8; p->bytes_per_line = 3 * p->pixels_per_line; if ( !sanei_hp_optset_output_8bit (this, data) ) { data_width = sanei_hp_optset_data_width (this, data); if ( data_width > 24 ) { p->depth *= 2; p->bytes_per_line *= 2; } } break; default: assert(!"Bad scan mode?"); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } const SANE_Option_Descriptor * sanei_hp_optset_saneoption (HpOptSet this, HpData data, int optnum) { HpOption opt = hp_optset_getByIndex(this, optnum); if (!opt) return 0; return hp_option_saneoption(opt, data); }