kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			477 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			477 wiersze
		
	
	
		
			14 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).
 | |
| */
 | |
| 
 | |
| /*#define STUBS
 | |
| extern int sanei_debug_hp;*/
 | |
| #define DEBUG_DECLARE_ONLY
 | |
| #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;
 | |
| }
 | |
| 
 |