diff options
Diffstat (limited to 'backend/hp-option.c')
-rw-r--r-- | backend/hp-option.c | 4087 |
1 files changed, 4087 insertions, 0 deletions
diff --git a/backend/hp-option.c b/backend/hp-option.c new file mode 100644 index 0000000..10bcb3e --- /dev/null +++ b/backend/hp-option.c @@ -0,0 +1,4087 @@ +/* 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + 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). +*/ + +/* + $Log$ + 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 invers 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: dont 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 <stdio.h> +#include <string.h> +#include "../include/lassert.h" +#ifndef SANE_HOME_HP +#include <pwd.h> +#endif +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <math.h> +#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 + +/* Dont 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 specifiying 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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *info) +{ + return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR; +} + +static hp_bool_t +_cenable_notcolor (HpChoice UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 dont 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 dont 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 dont 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 UNUSEDARG scsi, + HpOptSet UNUSEDARG optset, HpData data) +{ + /* If we dont 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 UNUSEDARG 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 UNUSEDARG 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 streched 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 UNUSEDARG 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 UNUSEDARG this, HpScsi scsi, + HpOptSet UNUSEDARG optset, HpData UNUSEDARG 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); /* dont 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 UNUSEDARG this, HpScsi scsi, + HpOptSet UNUSEDARG optset, HpData UNUSEDARG 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 UNUSEDARG this, HpScsi scsi, + HpOptSet UNUSEDARG optset, HpData UNUSEDARG 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 UNUSEDARG 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 dont 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 UNUSEDARG 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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *info) +{ + return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE; +} + +static hp_bool_t +_enable_halftonevec (HpOption UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *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 UNUSEDARG 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 UNUSEDARG this, HpOptSet optset, HpData data, + const HpDeviceInfo UNUSEDARG *info) +{ + HpOption media = hp_optset_get(optset, MEDIA); + + /* If we dont 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); +} |