kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			988 wiersze
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			988 wiersze
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
/* sane - Scanner Access Now Easy.
 | 
						|
   Copyright (C) 1997 Gordon Matzigkeit
 | 
						|
   Copyright (C) 1997 David Mosberger-Tang
 | 
						|
   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.  */
 | 
						|
 | 
						|
#include "../include/sane/config.h"
 | 
						|
 | 
						|
#include <limits.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
extern int errno;
 | 
						|
 | 
						|
#include "../include/sane/sane.h"
 | 
						|
#include "../include/sane/saneopts.h"
 | 
						|
 | 
						|
#include <unistd.h>
 | 
						|
#include <fcntl.h>
 | 
						|
 | 
						|
#include "../include/sane/sanei_backend.h"
 | 
						|
 | 
						|
#ifndef PATH_MAX
 | 
						|
# define PATH_MAX	1024
 | 
						|
#endif
 | 
						|
 | 
						|
#include "../include/sane/sanei_config.h"
 | 
						|
#define PINT_CONFIG_FILE "pint.conf"
 | 
						|
 | 
						|
#include "pint.h"
 | 
						|
 | 
						|
#define DECIPOINTS_PER_MM	(720.0 / MM_PER_INCH)
 | 
						|
#define TWELVEHUNDS_PER_MM	(1200.0 / MM_PER_INCH)
 | 
						|
 | 
						|
 | 
						|
static int num_devices;
 | 
						|
static PINT_Device *first_dev;
 | 
						|
static PINT_Scanner *first_handle;
 | 
						|
 | 
						|
/* A zero-terminated list of valid scanner modes. */
 | 
						|
static SANE_String_Const mode_list[8];
 | 
						|
 | 
						|
static const SANE_Range s7_range =
 | 
						|
  {
 | 
						|
    -127,				/* minimum */
 | 
						|
     127,				/* maximum */
 | 
						|
       1				/* quantization */
 | 
						|
  };
 | 
						|
 | 
						|
static size_t
 | 
						|
max_string_size (const SANE_String_Const strings[])
 | 
						|
{
 | 
						|
  size_t size, max_size = 0;
 | 
						|
  int i;
 | 
						|
 | 
						|
  for (i = 0; strings[i]; ++i)
 | 
						|
    {
 | 
						|
      size = strlen (strings[i]) + 1;
 | 
						|
      if (size > max_size)
 | 
						|
	max_size = size;
 | 
						|
    }
 | 
						|
  return max_size;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach (const char *devname, PINT_Device **devp)
 | 
						|
{
 | 
						|
  int fd;
 | 
						|
  long lastguess, inc;
 | 
						|
  PINT_Device *dev;
 | 
						|
  struct scan_io scanio;
 | 
						|
 | 
						|
  for (dev = first_dev; dev; dev = dev->next)
 | 
						|
    if (strcmp (dev->sane.name, devname) == 0)
 | 
						|
      {
 | 
						|
	if (devp)
 | 
						|
	  *devp = dev;
 | 
						|
	return SANE_STATUS_GOOD;
 | 
						|
      }
 | 
						|
 | 
						|
  DBG(3, "attach: opening %s\n", devname);
 | 
						|
  fd = open (devname, O_RDONLY, 0);
 | 
						|
  if (fd < 0)
 | 
						|
    {
 | 
						|
      DBG(1, "attach: open failed (%s)\n", strerror (errno));
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG(3, "attach: sending SCIOCGET\n");
 | 
						|
  if (ioctl (fd, SCIOCGET, &scanio) < 0)
 | 
						|
    {
 | 
						|
      DBG(1, "attach: get status failed (%s)\n", strerror (errno));
 | 
						|
      close (fd);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  dev = malloc (sizeof (*dev));
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  memset(dev, 0, sizeof (*dev));
 | 
						|
 | 
						|
  /* Copy the original scanner state to the device structure. */
 | 
						|
  memcpy (&dev->scanio, &scanio, sizeof (dev->scanio));
 | 
						|
 | 
						|
  /* FIXME: PINT currently has no good way to determine maxima and minima.
 | 
						|
     So, do binary searches to find out what limits the driver has. */
 | 
						|
 | 
						|
  /* Assume that minimum range of x and y is 0. */
 | 
						|
  dev->x_range.min = SANE_FIX (0);
 | 
						|
  dev->y_range.min = SANE_FIX (0);
 | 
						|
  dev->x_range.quant = 0;
 | 
						|
  dev->y_range.quant = 0;
 | 
						|
 | 
						|
  /* x range */
 | 
						|
  inc = 8.5 * 1200;
 | 
						|
 | 
						|
  /* Converge on the maximum scan width. */
 | 
						|
  while ((inc /= 2) != 0)
 | 
						|
    {
 | 
						|
      /* Move towards the extremum until we overflow. */
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  lastguess = scanio.scan_width;
 | 
						|
	  scanio.scan_width += inc;
 | 
						|
	}
 | 
						|
      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
 | 
						|
 | 
						|
      /* Pick the last valid guess, divide by two, and try again. */
 | 
						|
      scanio.scan_width = lastguess;
 | 
						|
    }
 | 
						|
  dev->x_range.max = SANE_FIX (scanio.scan_width / TWELVEHUNDS_PER_MM);
 | 
						|
 | 
						|
  /* y range */
 | 
						|
  inc = 11 * 1200;
 | 
						|
  while ((inc /= 2) != 0)
 | 
						|
    {
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  lastguess = scanio.scan_height;
 | 
						|
	  scanio.scan_height += inc;
 | 
						|
	}
 | 
						|
      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
 | 
						|
      scanio.scan_height = lastguess;
 | 
						|
    }
 | 
						|
  dev->y_range.max = SANE_FIX (scanio.scan_height / TWELVEHUNDS_PER_MM);
 | 
						|
 | 
						|
  /* Converge on the minimum scan resolution. */
 | 
						|
  dev->dpi_range.quant = 1;
 | 
						|
 | 
						|
  if (scanio.scan_x_resolution > scanio.scan_y_resolution)
 | 
						|
    scanio.scan_x_resolution = scanio.scan_y_resolution;
 | 
						|
  else
 | 
						|
    scanio.scan_y_resolution = scanio.scan_x_resolution;
 | 
						|
 | 
						|
  inc = -scanio.scan_x_resolution;
 | 
						|
  while ((inc /= 2) != 0)
 | 
						|
    {
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  lastguess = scanio.scan_x_resolution;
 | 
						|
	  scanio.scan_x_resolution = scanio.scan_y_resolution += inc;
 | 
						|
	}
 | 
						|
      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
 | 
						|
      scanio.scan_x_resolution = scanio.scan_y_resolution = lastguess;
 | 
						|
    }
 | 
						|
  dev->dpi_range.min = scanio.scan_x_resolution;
 | 
						|
 | 
						|
  /* Converge on the maximum scan resolution. */
 | 
						|
  inc = 600;
 | 
						|
  while ((inc /= 2) != 0)
 | 
						|
    {
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  lastguess = scanio.scan_x_resolution;
 | 
						|
	  scanio.scan_x_resolution = scanio.scan_y_resolution += inc;
 | 
						|
	}
 | 
						|
      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
 | 
						|
      scanio.scan_x_resolution = scanio.scan_y_resolution = lastguess;
 | 
						|
    }
 | 
						|
  dev->dpi_range.max = scanio.scan_x_resolution;
 | 
						|
 | 
						|
  /* Determine the valid scan modes for mode_list. */
 | 
						|
  lastguess = 0;
 | 
						|
#define CHECK_MODE(flag,modename) \
 | 
						|
  scanio.scan_image_mode = flag; \
 | 
						|
  if (ioctl (fd, SCIOCSET, &scanio) >= 0) \
 | 
						|
    mode_list[lastguess ++] = modename
 | 
						|
 | 
						|
  CHECK_MODE(SIM_BINARY_MONOCHROME, SANE_VALUE_SCAN_MODE_LINEART);
 | 
						|
  CHECK_MODE(SIM_DITHERED_MONOCHROME, SANE_VALUE_SCAN_MODE_HALFTONE);
 | 
						|
  CHECK_MODE(SIM_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY);
 | 
						|
  CHECK_MODE(SIM_COLOR, SANE_VALUE_SCAN_MODE_COLOR);
 | 
						|
  CHECK_MODE(SIM_RED, "Red");
 | 
						|
  CHECK_MODE(SIM_GREEN, "Green");
 | 
						|
  CHECK_MODE(SIM_BLUE, "Blue");
 | 
						|
#undef CHECK_MODE
 | 
						|
 | 
						|
  /* Zero-terminate the list of modes. */
 | 
						|
  mode_list[lastguess] = 0;
 | 
						|
 | 
						|
  /* Restore the scanner state. */
 | 
						|
  if (ioctl (fd, SCIOCSET, &dev->scanio))
 | 
						|
    DBG (2, "cannot reset original scanner state: %s\n", strerror (errno));
 | 
						|
  close (fd);
 | 
						|
 | 
						|
  dev->sane.name   = strdup (devname);
 | 
						|
 | 
						|
  /* Determine vendor. */
 | 
						|
  switch (scanio.scan_scanner_type)
 | 
						|
    {
 | 
						|
    case EPSON_ES300C:
 | 
						|
      dev->sane.vendor = "Epson";
 | 
						|
      break;
 | 
						|
 | 
						|
    case FUJITSU_M3096G:
 | 
						|
      dev->sane.vendor = "Fujitsu";
 | 
						|
      break;
 | 
						|
 | 
						|
    case HP_SCANJET_IIC:
 | 
						|
      dev->sane.vendor = "HP";
 | 
						|
      break;
 | 
						|
 | 
						|
    case IBM_2456:
 | 
						|
      dev->sane.vendor = "IBM";
 | 
						|
      break;
 | 
						|
 | 
						|
    case MUSTEK_06000CX:
 | 
						|
    case MUSTEK_12000CX:
 | 
						|
      dev->sane.vendor = "Mustek";
 | 
						|
      break;
 | 
						|
 | 
						|
    case RICOH_FS1:
 | 
						|
    case RICOH_IS410:
 | 
						|
    case RICOH_IS50:
 | 
						|
      dev->sane.vendor = "Ricoh";
 | 
						|
      break;
 | 
						|
 | 
						|
    case SHARP_JX600:
 | 
						|
      dev->sane.vendor = "Sharp";
 | 
						|
      break;
 | 
						|
 | 
						|
    case UMAX_UC630:
 | 
						|
    case UMAX_UG630:
 | 
						|
      dev->sane.vendor = "UMAX";
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      dev->sane.vendor = "PINT";
 | 
						|
    }
 | 
						|
 | 
						|
  /* Determine model. */
 | 
						|
  switch (scanio.scan_scanner_type)
 | 
						|
    {
 | 
						|
    case EPSON_ES300C:
 | 
						|
      dev->sane.vendor = "Epson";
 | 
						|
      break;
 | 
						|
 | 
						|
    case FUJITSU_M3096G:
 | 
						|
      dev->sane.model = "M3096G";
 | 
						|
      break;
 | 
						|
 | 
						|
    case HP_SCANJET_IIC:
 | 
						|
      dev->sane.model = "ScanJet IIc";
 | 
						|
      break;
 | 
						|
 | 
						|
    case IBM_2456:
 | 
						|
      dev->sane.vendor = "IBM";
 | 
						|
      break;
 | 
						|
 | 
						|
    case MUSTEK_06000CX:
 | 
						|
    case MUSTEK_12000CX:
 | 
						|
      dev->sane.vendor = "Mustek";
 | 
						|
      break;
 | 
						|
 | 
						|
    case RICOH_FS1:
 | 
						|
      dev->sane.model = "FS1";
 | 
						|
      break;
 | 
						|
 | 
						|
    case RICOH_IS410:
 | 
						|
      dev->sane.model = "IS-410";
 | 
						|
      break;
 | 
						|
 | 
						|
    case RICOH_IS50:
 | 
						|
      dev->sane.vendor = "Ricoh";
 | 
						|
      break;
 | 
						|
 | 
						|
    case SHARP_JX600:
 | 
						|
      dev->sane.vendor = "Sharp";
 | 
						|
      break;
 | 
						|
 | 
						|
    case UMAX_UC630:
 | 
						|
    case UMAX_UG630:
 | 
						|
      dev->sane.vendor = "UMAX";
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      dev->sane.model = "unknown";
 | 
						|
    }
 | 
						|
 | 
						|
  /* Determine the scanner type. */
 | 
						|
  switch (scanio.scan_scanner_type)
 | 
						|
    {
 | 
						|
    case HP_SCANJET_IIC:
 | 
						|
      dev->sane.type = "flatbed scanner";
 | 
						|
 | 
						|
      /* FIXME: which of these are flatbed or handhelds? */
 | 
						|
    case EPSON_ES300C:
 | 
						|
    case FUJITSU_M3096G:
 | 
						|
    case IBM_2456:
 | 
						|
    case MUSTEK_06000CX:
 | 
						|
    case MUSTEK_12000CX:
 | 
						|
    case RICOH_FS1:
 | 
						|
    case RICOH_IS410:
 | 
						|
    case RICOH_IS50:
 | 
						|
    case SHARP_JX600:
 | 
						|
    case UMAX_UC630:
 | 
						|
    case UMAX_UG630:
 | 
						|
    default:
 | 
						|
      dev->sane.type = "generic scanner";
 | 
						|
    }
 | 
						|
 | 
						|
  DBG(1, "attach: found %s %s, x=%g-%gmm, y=%g-%gmm, "
 | 
						|
      "resolution=%d-%ddpi\n", dev->sane.vendor, dev->sane.model,
 | 
						|
      SANE_UNFIX (dev->x_range.min), SANE_UNFIX (dev->x_range.max),
 | 
						|
      SANE_UNFIX (dev->y_range.min), SANE_UNFIX (dev->y_range.max),
 | 
						|
      dev->dpi_range.min, dev->dpi_range.max);
 | 
						|
 | 
						|
  ++num_devices;
 | 
						|
  dev->next = first_dev;
 | 
						|
  first_dev = dev;
 | 
						|
 | 
						|
  if (devp)
 | 
						|
    *devp = dev;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
init_options (PINT_Scanner *s)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  int x0, x1, y0, y1;
 | 
						|
 | 
						|
  memset (s->opt, 0, sizeof (s->opt));
 | 
						|
  memset (s->val, 0, sizeof (s->val));
 | 
						|
 | 
						|
  for (i = 0; i < NUM_OPTIONS; ++i)
 | 
						|
    {
 | 
						|
      s->opt[i].size = sizeof (SANE_Word);
 | 
						|
      s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
    }
 | 
						|
 | 
						|
  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | 
						|
  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | 
						|
 | 
						|
  /* "Mode" group: */
 | 
						|
  s->opt[OPT_MODE_GROUP].title = "Scan Mode";
 | 
						|
  s->opt[OPT_MODE_GROUP].desc = "";
 | 
						|
  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_MODE_GROUP].cap = 0;
 | 
						|
  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* scan mode */
 | 
						|
  s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
 | 
						|
  s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
 | 
						|
  s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
 | 
						|
  s->opt[OPT_MODE].type = SANE_TYPE_STRING;
 | 
						|
  s->opt[OPT_MODE].size = max_string_size (mode_list);
 | 
						|
  s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  s->opt[OPT_MODE].constraint.string_list = mode_list;
 | 
						|
 | 
						|
  /* Translate the current PINT mode into a string. */
 | 
						|
  switch (s->hw->scanio.scan_image_mode)
 | 
						|
    {
 | 
						|
    case SIM_BINARY_MONOCHROME:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[0]);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SIM_DITHERED_MONOCHROME:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[1]);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SIM_COLOR:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[3]);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SIM_RED:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[4]);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SIM_GREEN:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[5]);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SIM_BLUE:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[6]);
 | 
						|
      break;
 | 
						|
 | 
						|
    case SIM_GRAYSCALE:
 | 
						|
    default:
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list[2]);
 | 
						|
    }
 | 
						|
 | 
						|
  /* resolution */
 | 
						|
  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
 | 
						|
  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
 | 
						|
  s->val[OPT_RESOLUTION].w =
 | 
						|
    (s->hw->scanio.scan_x_resolution > s->hw->scanio.scan_y_resolution) ?
 | 
						|
    s->hw->scanio.scan_x_resolution : s->hw->scanio.scan_y_resolution;
 | 
						|
 | 
						|
  /* "Geometry" group: */
 | 
						|
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* Calculate the x and y millimetre coordinates from the scanio. */
 | 
						|
  x0 = SANE_FIX (s->hw->scanio.scan_x_origin / TWELVEHUNDS_PER_MM);
 | 
						|
  y0 = SANE_FIX (s->hw->scanio.scan_y_origin / TWELVEHUNDS_PER_MM);
 | 
						|
  x1 = SANE_FIX ((s->hw->scanio.scan_x_origin + s->hw->scanio.scan_width)
 | 
						|
		 / TWELVEHUNDS_PER_MM);
 | 
						|
  y1 = SANE_FIX ((s->hw->scanio.scan_y_origin + s->hw->scanio.scan_height)
 | 
						|
		 / TWELVEHUNDS_PER_MM);
 | 
						|
 | 
						|
  /* top-left x */
 | 
						|
  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
 | 
						|
  s->val[OPT_TL_X].w = x0;
 | 
						|
 | 
						|
  /* top-left y */
 | 
						|
  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
 | 
						|
  s->val[OPT_TL_Y].w = y0;
 | 
						|
 | 
						|
  /* bottom-right x */
 | 
						|
  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
 | 
						|
  s->val[OPT_BR_X].w = x1;
 | 
						|
 | 
						|
  /* bottom-right y */
 | 
						|
  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
 | 
						|
  s->val[OPT_BR_Y].w = y1;
 | 
						|
 | 
						|
  /* "Enhancement" group: */
 | 
						|
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* brightness */
 | 
						|
  s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BRIGHTNESS].constraint.range = &s7_range;
 | 
						|
  s->val[OPT_BRIGHTNESS].w = s->hw->scanio.scan_brightness - 128;
 | 
						|
 | 
						|
  /* contrast */
 | 
						|
  s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_CONTRAST].constraint.range = &s7_range;
 | 
						|
  s->val[OPT_CONTRAST].w = s->hw->scanio.scan_contrast - 128;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_cancel (PINT_Scanner *s)
 | 
						|
{
 | 
						|
  /* FIXME: PINT doesn't have any good way to cancel ScanJets right now. */
 | 
						|
#define gobble_up_buf_len 1024
 | 
						|
  char buf[gobble_up_buf_len];
 | 
						|
 | 
						|
  /* Send the restart code. */
 | 
						|
  buf[0] = ioctl (s->fd, SCIOCRESTART, 0);
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    return SANE_STATUS_CANCELLED;
 | 
						|
 | 
						|
  s->scanning = SANE_FALSE;
 | 
						|
 | 
						|
  /* Read to the end of the file. */
 | 
						|
  while (read (s->fd, buf, gobble_up_buf_len) > 0)
 | 
						|
    ;
 | 
						|
#undef gobble_up_buf_len
 | 
						|
 | 
						|
  /* Finally, close the file descriptor. */
 | 
						|
  if (s->fd >= 0)
 | 
						|
    {
 | 
						|
      close (s->fd);
 | 
						|
      s->fd = -1;
 | 
						|
    }
 | 
						|
  return SANE_STATUS_CANCELLED;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
 | 
						|
{
 | 
						|
  char dev_name[PATH_MAX];
 | 
						|
  size_t len;
 | 
						|
  FILE *fp;
 | 
						|
 | 
						|
  DBG_INIT();
 | 
						|
 | 
						|
  if (version_code)
 | 
						|
    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
 | 
						|
 | 
						|
  fp = sanei_config_open (PINT_CONFIG_FILE);
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      /* default to /dev/scanner instead of insisting on config file */
 | 
						|
      attach ("/dev/scanner", 0);
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
 | 
						|
  while (sanei_config_read (dev_name, sizeof (dev_name), fp))
 | 
						|
    {
 | 
						|
      if (dev_name[0] == '#')		/* ignore line comments */
 | 
						|
	continue;
 | 
						|
      len = strlen (dev_name);
 | 
						|
 | 
						|
      if (!len)
 | 
						|
	continue;			/* ignore empty lines */
 | 
						|
 | 
						|
      attach (dev_name, 0);
 | 
						|
    }
 | 
						|
  fclose (fp);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_exit (void)
 | 
						|
{
 | 
						|
  PINT_Device *dev, *next;
 | 
						|
 | 
						|
  for (dev = first_dev; dev; dev = next)
 | 
						|
    {
 | 
						|
      next = dev->next;
 | 
						|
      free ((void *) dev->sane.name);
 | 
						|
      free (dev);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
 | 
						|
{
 | 
						|
  static const SANE_Device **devlist = 0;
 | 
						|
  PINT_Device *dev;
 | 
						|
  int i;
 | 
						|
 | 
						|
  if (devlist)
 | 
						|
    free (devlist);
 | 
						|
 | 
						|
  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
 | 
						|
  if (!devlist)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  i = 0;
 | 
						|
  for (dev = first_dev; i < num_devices; dev = dev->next)
 | 
						|
    devlist[i++] = &dev->sane;
 | 
						|
  devlist[i++] = 0;
 | 
						|
 | 
						|
  *device_list = devlist;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_open (SANE_String_Const devicename, SANE_Handle *handle)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  PINT_Device *dev;
 | 
						|
  PINT_Scanner *s;
 | 
						|
 | 
						|
  if (devicename[0])
 | 
						|
    {
 | 
						|
      for (dev = first_dev; dev; dev = dev->next)
 | 
						|
	if (strcmp (dev->sane.name, devicename) == 0)
 | 
						|
	  break;
 | 
						|
 | 
						|
      if (!dev)
 | 
						|
	{
 | 
						|
	  status = attach (devicename, &dev);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    /* empty devicename -> use first device */
 | 
						|
    dev = first_dev;
 | 
						|
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  s = malloc (sizeof (*s));
 | 
						|
  if (!s)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  memset (s, 0, sizeof (*s));
 | 
						|
  s->hw = dev;
 | 
						|
  s->fd = -1;
 | 
						|
 | 
						|
  init_options (s);
 | 
						|
 | 
						|
  /* insert newly opened handle into list of open handles: */
 | 
						|
  s->next = first_handle;
 | 
						|
  first_handle = s;
 | 
						|
 | 
						|
  *handle = s;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_close (SANE_Handle handle)
 | 
						|
{
 | 
						|
  PINT_Scanner *prev, *s;
 | 
						|
 | 
						|
  /* remove handle from list of open handles: */
 | 
						|
  prev = 0;
 | 
						|
  for (s = first_handle; s; s = s->next)
 | 
						|
    {
 | 
						|
      if (s == handle)
 | 
						|
	break;
 | 
						|
      prev = s;
 | 
						|
    }
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG(1, "close: invalid handle %p\n", handle);
 | 
						|
      return;		/* oops, not a handle we know about */
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    do_cancel (handle);
 | 
						|
 | 
						|
  if (prev)
 | 
						|
    prev->next = s->next;
 | 
						|
  else
 | 
						|
    first_handle = s->next;
 | 
						|
 | 
						|
  free (handle);
 | 
						|
}
 | 
						|
 | 
						|
const SANE_Option_Descriptor *
 | 
						|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | 
						|
{
 | 
						|
  PINT_Scanner *s = handle;
 | 
						|
 | 
						|
  if ((unsigned) option >= NUM_OPTIONS)
 | 
						|
    return 0;
 | 
						|
  return s->opt + option;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_control_option (SANE_Handle handle, SANE_Int option,
 | 
						|
		     SANE_Action action, void *val, SANE_Int *info)
 | 
						|
{
 | 
						|
  PINT_Scanner *s = handle;
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Word cap;
 | 
						|
 | 
						|
  if (info)
 | 
						|
    *info = 0;
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
 | 
						|
  if (option >= NUM_OPTIONS)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  cap = s->opt[option].cap;
 | 
						|
 | 
						|
  if (!SANE_OPTION_IS_ACTIVE (cap))
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (action == SANE_ACTION_GET_VALUE)
 | 
						|
    {
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	  /* word options: */
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	  *(SANE_Word *) val = s->val[option].w;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* string options: */
 | 
						|
	case OPT_MODE:
 | 
						|
	  strcpy (val, s->val[option].s);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (action == SANE_ACTION_SET_VALUE)
 | 
						|
    {
 | 
						|
      if (!SANE_OPTION_IS_SETTABLE (cap))
 | 
						|
	return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
      status = sanei_constrain_value (s->opt + option, val, info);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	  /* (mostly) side-effect-free word options: */
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  /* fall through */
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	  s->val[option].w = *(SANE_Word *) val;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_MODE:
 | 
						|
	  if (s->val[option].s)
 | 
						|
	    free (s->val[option].s);
 | 
						|
	  s->val[option].s = strdup (val);
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
 | 
						|
{
 | 
						|
  PINT_Scanner *s = handle;
 | 
						|
  struct scan_io scanio;
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    {
 | 
						|
      u_long x0, y0, width, height;
 | 
						|
      const char *mode;
 | 
						|
 | 
						|
      /* Grab the scanio for this device. */
 | 
						|
      if (s->fd < 0)
 | 
						|
	{
 | 
						|
	  s->fd = open (s->hw->sane.name, O_RDONLY, 0);
 | 
						|
	  if (s->fd < 0)
 | 
						|
	    {
 | 
						|
	      DBG(1, "open of %s failed: %s\n",
 | 
						|
		  s->hw->sane.name, strerror (errno));
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (ioctl (s->fd, SCIOCGET, &scanio) < 0)
 | 
						|
	{
 | 
						|
	  DBG(1, "getting scanner state failed: %s", strerror (errno));
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
 | 
						|
      memset (&s->params, 0, sizeof (s->params));
 | 
						|
 | 
						|
      /* FIXME: there is some lossage here: the parameters change due to
 | 
						|
	 roundoff errors between converting to fixed point millimetres
 | 
						|
	 and back. */
 | 
						|
      x0 = SANE_UNFIX (s->val[OPT_TL_X].w * TWELVEHUNDS_PER_MM);
 | 
						|
      y0 = SANE_UNFIX (s->val[OPT_TL_Y].w * TWELVEHUNDS_PER_MM);
 | 
						|
      width  = SANE_UNFIX ((s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
 | 
						|
			   * TWELVEHUNDS_PER_MM);
 | 
						|
      height = SANE_UNFIX ((s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
 | 
						|
			   * TWELVEHUNDS_PER_MM);
 | 
						|
 | 
						|
      /* x and y dpi: */
 | 
						|
      scanio.scan_x_resolution = s->val[OPT_RESOLUTION].w;
 | 
						|
      scanio.scan_y_resolution = s->val[OPT_RESOLUTION].w;
 | 
						|
 | 
						|
      /* set scan extents, in 1/1200'ths of an inch */
 | 
						|
      scanio.scan_x_origin = x0;
 | 
						|
      scanio.scan_y_origin = y0;
 | 
						|
      scanio.scan_width = width;
 | 
						|
      scanio.scan_height = height;
 | 
						|
 | 
						|
      /* brightness and contrast */
 | 
						|
      scanio.scan_brightness = s->val[OPT_BRIGHTNESS].w + 128;
 | 
						|
      scanio.scan_contrast = s->val[OPT_CONTRAST].w + 128;
 | 
						|
 | 
						|
      /* set the scan image mode */
 | 
						|
      mode = s->val[OPT_MODE].s;
 | 
						|
      if (!strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART))
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GRAY;
 | 
						|
	  scanio.scan_image_mode = SIM_BINARY_MONOCHROME;
 | 
						|
	}
 | 
						|
      else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE))
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GRAY;
 | 
						|
	  scanio.scan_image_mode = SIM_DITHERED_MONOCHROME;
 | 
						|
	}
 | 
						|
      else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY))
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GRAY;
 | 
						|
	  scanio.scan_image_mode = SIM_GRAYSCALE;
 | 
						|
	}
 | 
						|
      else if (!strcmp (mode, "Red"))
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_RED;
 | 
						|
	  scanio.scan_image_mode = SIM_RED;
 | 
						|
	}
 | 
						|
      else if (!strcmp (mode, "Green"))
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GREEN;
 | 
						|
	  scanio.scan_image_mode = SIM_GREEN;
 | 
						|
	}
 | 
						|
      else if (!strcmp (mode, "Blue"))
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_BLUE;
 | 
						|
	  scanio.scan_image_mode = SIM_BLUE;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_RGB;
 | 
						|
	  scanio.scan_image_mode = SIM_COLOR;
 | 
						|
	}
 | 
						|
 | 
						|
      /* inquire resulting size of image after setting it up */
 | 
						|
      if (ioctl (s->fd, SCIOCSET, &scanio) < 0)
 | 
						|
	{
 | 
						|
	  DBG(1, "setting scan parameters failed: %s", strerror (errno));
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
      if (ioctl (s->fd, SCIOCGET, &scanio) < 0)
 | 
						|
	{
 | 
						|
	  DBG(1, "getting scan parameters failed: %s", strerror (errno));
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Save all the PINT-computed values. */
 | 
						|
      s->params.pixels_per_line = scanio.scan_pixels_per_line;
 | 
						|
      s->params.bytes_per_line =
 | 
						|
	(scanio.scan_bits_per_pixel * scanio.scan_pixels_per_line + 7) / 8;
 | 
						|
      s->params.lines = scanio.scan_lines;
 | 
						|
      s->params.depth = (scanio.scan_image_mode == SIM_COLOR) ?
 | 
						|
	scanio.scan_bits_per_pixel / 3 : scanio.scan_bits_per_pixel;
 | 
						|
 | 
						|
      /* FIXME: this will need to be different for hand scanners. */
 | 
						|
      s->params.last_frame = SANE_TRUE;
 | 
						|
    }
 | 
						|
  if (params)
 | 
						|
    *params = s->params;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_start (SANE_Handle handle)
 | 
						|
{
 | 
						|
  PINT_Scanner *s = handle;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  /* First make sure we have a current parameter set.  This call actually
 | 
						|
     uses the PINT driver to do the calculations, so we trust its results. */
 | 
						|
  status = sane_get_parameters (s, 0);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  DBG(1, "%d pixels per line, %d bytes, %d lines high, dpi=%d\n",
 | 
						|
      s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines,
 | 
						|
      s->val[OPT_RESOLUTION].w);
 | 
						|
 | 
						|
  /* The scan is triggered in sane_read. */
 | 
						|
  s->scanning = SANE_TRUE;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
 | 
						|
{
 | 
						|
  PINT_Scanner *s = handle;
 | 
						|
  ssize_t nread;
 | 
						|
 | 
						|
  *len = 0;
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    return do_cancel (s);
 | 
						|
 | 
						|
  /* Verrry simple.  Just suck up all the data PINT passes to us. */
 | 
						|
  nread = read (s->fd, buf, max_len);
 | 
						|
  if (nread <= 0)
 | 
						|
    {
 | 
						|
      do_cancel (s);
 | 
						|
      return (nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  *len = nread;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_cancel (SANE_Handle handle)
 | 
						|
{
 | 
						|
  PINT_Scanner *s = handle;
 | 
						|
  do_cancel (s);
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | 
						|
{
 | 
						|
  return SANE_STATUS_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
 | 
						|
{
 | 
						|
  return SANE_STATUS_UNSUPPORTED;
 | 
						|
}
 |