summaryrefslogtreecommitdiff
path: root/backend/hp-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hp-device.c')
-rw-r--r--backend/hp-device.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/backend/hp-device.c b/backend/hp-device.c
new file mode 100644
index 0000000..2b96ad6
--- /dev/null
+++ b/backend/hp-device.c
@@ -0,0 +1,476 @@
+/* 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).
+*/
+
+/*#define STUBS
+extern int sanei_debug_hp;*/
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "../include/lassert.h"
+#include "hp-device.h"
+#include "hp-accessor.h"
+#include "hp-option.h"
+#include "hp-scsi.h"
+#include "hp-scl.h"
+
+/* Mark an scl-command to be simulated */
+SANE_Status
+sanei_hp_device_simulate_set (const char *devname, HpScl scl, int flag)
+
+{HpDeviceInfo *info;
+ int inqid;
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return SANE_STATUS_INVAL;
+
+ inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN;
+ info->simulate.sclsimulate[inqid] = flag;
+
+ DBG(3, "hp_device_simulate_set: %d set to %ssimulated\n",
+ inqid+HP_SCL_INQID_MIN, flag ? "" : "not ");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Clear all simulation flags */
+void
+sanei_hp_device_simulate_clear (const char *devname)
+
+{HpDeviceInfo *info;
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return;
+
+ memset (&(info->simulate.sclsimulate[0]), 0,
+ sizeof (info->simulate.sclsimulate));
+
+ info->simulate.gamma_simulate = 0;
+}
+
+/* Get simulate flag for an scl-command */
+hp_bool_t
+sanei_hp_device_simulate_get (const char *devname, HpScl scl)
+
+{HpDeviceInfo *info;
+ int inqid;
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return 0;
+
+ inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN;
+ return info->simulate.sclsimulate[inqid];
+}
+
+SANE_Status
+sanei_hp_device_support_get (const char *devname, HpScl scl,
+ int *minval, int *maxval)
+
+{HpDeviceInfo *info;
+ HpSclSupport *sclsupport;
+ int inqid;
+
+/* #define HP_TEST_SIMULATE */
+#ifdef HP_TEST_SIMULATE
+ if (scl == SCL_BRIGHTNESS) return SANE_STATUS_UNSUPPORTED;
+ if (scl == SCL_CONTRAST) return SANE_STATUS_UNSUPPORTED;
+ if (scl == SCL_DOWNLOAD_TYPE)
+ {
+ *minval = 2; *maxval = 14;
+ return SANE_STATUS_GOOD;
+ }
+#endif
+
+ info = sanei_hp_device_info_get ( devname );
+ if (!info) return SANE_STATUS_INVAL;
+
+ inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN;
+ sclsupport = &(info->sclsupport[inqid]);
+
+ if ( !(sclsupport->checked) ) return SANE_STATUS_INVAL;
+ if ( !(sclsupport->is_supported) ) return SANE_STATUS_UNSUPPORTED;
+
+ if (minval) *minval = sclsupport->minval;
+ if (maxval) *maxval = sclsupport->maxval;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Update the list of supported commands */
+SANE_Status
+sanei_hp_device_support_probe (HpScsi scsi)
+
+{HpDeviceInfo *info;
+ HpSclSupport *sclsupport;
+ SANE_Status status;
+ int k, val, inqid;
+ static HpScl sclprobe[] = /* The commands that should be probed */
+ {
+ SCL_AUTO_BKGRND,
+ SCL_COMPRESSION,
+ SCL_DOWNLOAD_TYPE,
+ SCL_X_SCALE,
+ SCL_Y_SCALE,
+ SCL_DATA_WIDTH,
+ SCL_INVERSE_IMAGE,
+ SCL_BW_DITHER,
+ SCL_CONTRAST,
+ SCL_BRIGHTNESS,
+#ifdef SCL_SHARPENING
+ SCL_SHARPENING,
+#endif
+ SCL_MIRROR_IMAGE,
+ SCL_X_RESOLUTION,
+ SCL_Y_RESOLUTION,
+ SCL_OUTPUT_DATA_TYPE,
+ SCL_PRELOAD_ADF,
+ SCL_MEDIA,
+ SCL_X_EXTENT,
+ SCL_Y_EXTENT,
+ SCL_X_POS,
+ SCL_Y_POS,
+ SCL_SPEED,
+ SCL_FILTER,
+ SCL_TONE_MAP,
+ SCL_MATRIX,
+ SCL_UNLOAD,
+ SCL_CHANGE_DOC,
+ SCL_ADF_BFEED
+ };
+ enum hp_device_compat_e compat;
+
+ DBG(1, "hp_device_support_probe: Check supported commands for %s\n",
+ sanei_hp_scsi_devicename (scsi) );
+
+ info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) );
+ assert (info);
+
+ memset (&(info->sclsupport[0]), 0, sizeof (info->sclsupport));
+
+ for (k = 0; k < (int)(sizeof (sclprobe) / sizeof (sclprobe[0])); k++)
+ {
+ inqid = SCL_INQ_ID(sclprobe[k])-HP_SCL_INQID_MIN;
+ sclsupport = &(info->sclsupport[inqid]);
+ status = sanei_hp_scl_inquire (scsi, sclprobe[k], &val,
+ &(sclsupport->minval),
+ &(sclsupport->maxval));
+ sclsupport->is_supported = (status == SANE_STATUS_GOOD);
+ sclsupport->checked = 1;
+
+ /* The OfficeJets seem to ignore brightness and contrast settings,
+ * so we'll pretend they're not supported at all. */
+ if (((sclprobe[k]==SCL_BRIGHTNESS) || (sclprobe[k]==SCL_CONTRAST)) &&
+ (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) &&
+ (compat & HP_COMPAT_OJ_1150C)) {
+ sclsupport->is_supported=0;
+ }
+
+ if (sclsupport->is_supported)
+ {
+ DBG(1, "hp_device_support_probe: %d supported (%d..%d, %d)\n",
+ inqid+HP_SCL_INQID_MIN, sclsupport->minval, sclsupport->maxval, val);
+ }
+ else
+ {
+ DBG(1, "hp_device_support_probe: %d not supported\n",
+ inqid+HP_SCL_INQID_MIN);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_device_probe_model (enum hp_device_compat_e *compat, HpScsi scsi,
+ int *model_num, const char **model_name)
+{
+ static struct {
+ HpScl cmd;
+ int model_num;
+ const char * model;
+ enum hp_device_compat_e flag;
+ } probes[] = {
+ { SCL_HP_MODEL_1, 1, "ScanJet Plus", HP_COMPAT_PLUS },
+ { SCL_HP_MODEL_2, 2, "ScanJet IIc", HP_COMPAT_2C },
+ { SCL_HP_MODEL_3, 3, "ScanJet IIp", HP_COMPAT_2P },
+ { SCL_HP_MODEL_4, 4, "ScanJet IIcx", HP_COMPAT_2CX },
+ { SCL_HP_MODEL_5, 5, "ScanJet 3c/4c/6100C", HP_COMPAT_4C },
+ { SCL_HP_MODEL_6, 6, "ScanJet 3p", HP_COMPAT_3P },
+ { SCL_HP_MODEL_8, 8, "ScanJet 4p", HP_COMPAT_4P },
+ { SCL_HP_MODEL_9, 9, "ScanJet 5p/4100C/5100C", HP_COMPAT_5P },
+ { SCL_HP_MODEL_10,10,"PhotoSmart Photo Scanner", HP_COMPAT_PS },
+ { SCL_HP_MODEL_11,11,"OfficeJet 1150C", HP_COMPAT_OJ_1150C },
+ { SCL_HP_MODEL_12,12,"OfficeJet 1170C or later", HP_COMPAT_OJ_1170C },
+ { SCL_HP_MODEL_14,14,"ScanJet 62x0C", HP_COMPAT_6200C },
+ { SCL_HP_MODEL_16,15,"ScanJet 5200C", HP_COMPAT_5200C },
+ { SCL_HP_MODEL_17,17,"ScanJet 63x0C", HP_COMPAT_6300C }
+ };
+ int i;
+ char buf[8];
+ size_t len;
+ SANE_Status status;
+ static char *last_device = NULL;
+ static enum hp_device_compat_e last_compat;
+ static int last_model_num = -1;
+ static const char *last_model_name = "Model Unknown";
+
+ assert(scsi);
+ DBG(1, "probe_scanner: Probing %s\n", sanei_hp_scsi_devicename (scsi));
+
+ if (last_device != NULL) /* Look if we already probed the device */
+ {
+ if (strcmp (last_device, sanei_hp_scsi_devicename (scsi)) == 0)
+ {
+ DBG(3, "probe_scanner: use cached compatibility flags\n");
+ *compat = last_compat;
+ if (model_num) *model_num = last_model_num;
+ if (model_name) *model_name = last_model_name;
+ return SANE_STATUS_GOOD;
+ }
+ sanei_hp_free (last_device);
+ last_device = NULL;
+ }
+ *compat = 0;
+ last_model_num = -1;
+ last_model_name = "Model Unknown";
+ for (i = 0; i < (int)(sizeof(probes)/sizeof(probes[0])); i++)
+ {
+ DBG(1,"probing %s\n",probes[i].model);
+
+ len = sizeof(buf);
+ if (!FAILED( status = sanei_hp_scl_upload(scsi, probes[i].cmd,
+ buf, sizeof(buf)) ))
+ {
+ DBG(1, "probe_scanner: %s compatible (%5s)\n", probes[i].model, buf);
+ last_model_name = probes[i].model;
+ /* Some scanners have different responses */
+ if (probes[i].model_num == 9)
+ {
+ if (strncmp (buf, "5110A", 5) == 0)
+ last_model_name = "ScanJet 5p";
+ else if (strncmp (buf, "5190A", 5) == 0)
+ last_model_name = "ScanJet 5100C";
+ else if (strncmp (buf, "6290A", 5) == 0)
+ last_model_name = "ScanJet 4100C";
+ }
+ *compat |= probes[i].flag;
+ last_model_num = probes[i].model_num;
+ }
+ else if (!UNSUPPORTED( status ))
+ return status; /* SCL inquiry failed */
+ }
+ /* Save values for next call */
+ last_device = sanei_hp_strdup (sanei_hp_scsi_devicename (scsi));
+ last_compat = *compat;
+ if (model_num) *model_num = last_model_num;
+ if (model_name) *model_name = last_model_name;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_device_probe (enum hp_device_compat_e *compat, HpScsi scsi)
+{
+ return sanei_hp_device_probe_model (compat, scsi, 0, 0);
+}
+
+hp_bool_t
+sanei_hp_device_compat (HpDevice this, enum hp_device_compat_e which)
+{
+ return (this->compat & which) != 0;
+}
+
+static SANE_Status
+hp_nonscsi_device_new (HpDevice * newp, const char * devname, HpConnect connect)
+{
+ HpDevice this;
+ HpScsi scsi;
+ SANE_Status status;
+ const char * model_name = "ScanJet";
+
+ if (FAILED( sanei_hp_nonscsi_new(&scsi, devname, connect) ))
+ {
+ DBG(1, "%s: Can't open nonscsi device\n", devname);
+ return SANE_STATUS_INVAL; /* Can't open device */
+ }
+
+ /* reset scanner; returns all parameters to defaults */
+ if (FAILED( sanei_hp_scl_reset(scsi) ))
+ {
+ DBG(1, "hp_nonscsi_device_new: SCL reset failed\n");
+ sanei_hp_scsi_destroy(scsi,1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Things seem okay, allocate new device */
+ this = sanei_hp_allocz(sizeof(*this));
+ this->data = sanei_hp_data_new();
+
+ if (!this || !this->data)
+ return SANE_STATUS_NO_MEM;
+
+ this->sanedev.name = sanei_hp_strdup(devname);
+ if (!this->sanedev.name)
+ return SANE_STATUS_NO_MEM;
+ this->sanedev.vendor = "Hewlett-Packard";
+ this->sanedev.type = "flatbed scanner";
+
+ status = sanei_hp_device_probe_model (&(this->compat), scsi, 0, &model_name);
+ if (!FAILED(status))
+ {
+ sanei_hp_device_support_probe (scsi);
+ status = sanei_hp_optset_new(&(this->options), scsi, this);
+ }
+ sanei_hp_scsi_destroy(scsi,1);
+
+ if (!model_name) model_name = "ScanJet";
+ this->sanedev.model = sanei_hp_strdup (model_name);
+ if (!this->sanedev.model)
+ return SANE_STATUS_NO_MEM;
+
+ if (FAILED(status))
+ {
+ DBG(1, "hp_nonscsi_device_new: %s: probe failed (%s)\n",
+ devname, sane_strstatus(status));
+ sanei_hp_data_destroy(this->data);
+ sanei_hp_free((void *)this->sanedev.name);
+ sanei_hp_free((void *)this->sanedev.model);
+ sanei_hp_free(this);
+ return status;
+ }
+
+ DBG(1, "hp_nonscsi_device_new: %s: found HP ScanJet model %s\n",
+ devname, this->sanedev.model);
+
+ *newp = this;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_hp_device_new (HpDevice * newp, const char * devname)
+{
+ HpDevice this;
+ HpScsi scsi;
+ HpConnect connect;
+ SANE_Status status;
+ char * str;
+
+ DBG(3, "sanei_hp_device_new: %s\n", devname);
+
+ connect = sanei_hp_get_connect (devname);
+ if ( connect != HP_CONNECT_SCSI )
+ return hp_nonscsi_device_new (newp, devname, connect);
+
+ if (FAILED( sanei_hp_scsi_new(&scsi, devname) ))
+ {
+ DBG(1, "%s: Can't open scsi device\n", devname);
+ return SANE_STATUS_INVAL; /* Can't open device */
+ }
+
+ if (sanei_hp_scsi_inq(scsi)[0] != 0x03
+ || memcmp(sanei_hp_scsi_vendor(scsi), "HP ", 8) != 0)
+ {
+ DBG(1, "%s: does not seem to be an HP scanner\n", devname);
+ sanei_hp_scsi_destroy(scsi,1);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* reset scanner; returns all parameters to defaults */
+ if (FAILED( sanei_hp_scl_reset(scsi) ))
+ {
+ DBG(1, "sanei_hp_device_new: SCL reset failed\n");
+ sanei_hp_scsi_destroy(scsi,1);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ /* Things seem okay, allocate new device */
+ this = sanei_hp_allocz(sizeof(*this));
+ this->data = sanei_hp_data_new();
+
+ if (!this || !this->data)
+ return SANE_STATUS_NO_MEM;
+
+ this->sanedev.name = sanei_hp_strdup(devname);
+ str = sanei_hp_strdup(sanei_hp_scsi_model(scsi));
+ if (!this->sanedev.name || !str)
+ return SANE_STATUS_NO_MEM;
+ this->sanedev.model = str;
+ if ((str = strchr(str, ' ')) != 0)
+ *str = '\0';
+ this->sanedev.vendor = "Hewlett-Packard";
+ this->sanedev.type = "flatbed scanner";
+
+ status = sanei_hp_device_probe(&(this->compat), scsi);
+ if (!FAILED(status))
+ {
+ sanei_hp_device_support_probe (scsi);
+ status = sanei_hp_optset_new(&this->options, scsi, this);
+ }
+ sanei_hp_scsi_destroy(scsi,1);
+
+ if (FAILED(status))
+ {
+ DBG(1, "sanei_hp_device_new: %s: probe failed (%s)\n",
+ devname, sane_strstatus(status));
+ sanei_hp_data_destroy(this->data);
+ sanei_hp_free((void *)this->sanedev.name);
+ sanei_hp_free((void *)this->sanedev.model);
+ sanei_hp_free(this);
+ return status;
+ }
+
+ DBG(1, "sanei_hp_device_new: %s: found HP ScanJet model %s\n",
+ devname, this->sanedev.model);
+
+ *newp = this;
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Device *
+sanei_hp_device_sanedevice (HpDevice this)
+{
+ return &this->sanedev;
+}
+