summaryrefslogtreecommitdiff
path: root/backend/hp-option.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hp-option.c')
-rw-r--r--backend/hp-option.c4087
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, &current_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);
+}