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