/* sane - Scanner Access Now Easy.
   Copyright (C) 2002 Max Vorobiev <pcwizard@telecoms.sins.ru>
   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.  */

#define BUILD 3

#define BACKEND_NAME	hpsj5s
#define HPSJ5S_CONFIG_FILE "hpsj5s.conf"

#include "../include/sane/config.h"
#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"

#include "../include/sane/sanei_config.h"
#include "../include/sane/sanei_backend.h"

#include "hpsj5s.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>


#define LINES_TO_FEED	480	/*Default feed length */

static int scanner_d = -1;	/*This is handler to the only-supported. Will be fixed. */
static char scanner_path[PATH_MAX] = "";	/*String for device-file */
static SANE_Byte bLastCalibration;	/*Here we store calibration result */
static SANE_Byte bCalibration;	/*Here we store new calibration value */
static SANE_Byte bHardwareState;	/*Here we store copy of hardware flags register */

/*Here we store Parameters:*/
static SANE_Word wWidth = 2570;	/*Scan area width */
static SANE_Word wResolution = 300;	/*Resolution in DPI */
static SANE_Frame wCurrentFormat = SANE_FRAME_GRAY;	/*Type of colors in image */
static SANE_Int wCurrentDepth = 8;	/*Bits per pixel in image */

/*Here we count lines of every new image...*/
static SANE_Word wVerticalResolution;

/*Limits for resolution control*/
static const SANE_Range ImageWidthRange = {
  0,				/*minimal */
  2570,				/*maximum */
  2				/*quant */
};

static const SANE_Word ImageResolutionsList[] = {
  6,				/*Number of resolutions */
  75,
  100,
  150,
  200,
  250,
  300
};

static SANE_Option_Descriptor sod[] = {
  {				/*Number of options */
   SANE_NAME_NUM_OPTIONS,
   SANE_TITLE_NUM_OPTIONS,
   SANE_DESC_NUM_OPTIONS,
   SANE_TYPE_INT,
   SANE_UNIT_NONE,
   sizeof (SANE_Word),
   SANE_CAP_SOFT_DETECT,
   SANE_CONSTRAINT_NONE,
   {NULL}			/*No constraints required */
   }
  ,
  {				/*Width of scaned area */
   "width",
   "Width",
   "Width of area to scan",
   SANE_TYPE_INT,
   SANE_UNIT_PIXEL,
   sizeof (SANE_Word),
   SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
   SANE_CONSTRAINT_RANGE,
   {NULL}			/*Range constrain setted in sane_init */
   }
  ,
  {				/*Resolution for scan */
   "resolution",
   "Resolution",
   "Image resolution",
   SANE_TYPE_INT,
   SANE_UNIT_DPI,
   sizeof (SANE_Word),
   SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
   SANE_CONSTRAINT_WORD_LIST,
   {NULL}			/*Word list constrain setted in sane_init */
   }
};

static SANE_Parameters parms;

/*Recalculate Lenght in dependace of resolution*/
static SANE_Word
LengthForRes (SANE_Word Resolution, SANE_Word Length)
{
  switch (Resolution)
    {
    case 75:
      return Length / 4;
    case 100:
      return Length / 3;
    case 150:
      return Length / 2;
    case 200:
      return Length * 2 / 3;
    case 250:
      return Length * 5 / 6;
    case 300:
    default:
      return Length;
    }
}

static struct parport_list pl;	/*List of detected parallel ports. */

SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
  char line[PATH_MAX];		/*Line from config file */
  FILE *config_file;		/*Handle to config file of this backend */

  DBG_INIT ();
  DBG (1, ">>sane_init");
  DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
       version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
  DBG (1, "sane_init: SANE hpsj5s backend version %d.%d.%d\n",
       SANE_CURRENT_MAJOR, V_MINOR, BUILD);

  /*Inform about supported version */
  if (version_code)
    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);

  /*Open configuration file for this backend */
  config_file = sanei_config_open (HPSJ5S_CONFIG_FILE);

  if (!config_file)		/*Failed to open config file */
    {
      DBG (1, "sane_init: no config file found.");
      return SANE_STATUS_GOOD;
    }

  /*Read line by line */
  while (sanei_config_read (line, PATH_MAX, config_file))
    {
      if ((line[0] == '#') || (line[0] == '\0'))	/*comment line or empty line */
	continue;
      strcpy (scanner_path, line);	/*so, we choose last in file (uncommented) */
    }

  fclose (config_file);		/*We don't need config file any more */

  /*sanei_config_attach_matching_devices(devname, attach_one); To do latter */

  scanner_d = -1;		/*scanner device not opened yet. */
  DBG (1, "<<sane_init");

  /*Init params structure with defaults values: */
  wCurrentFormat = SANE_FRAME_GRAY;
  wCurrentDepth = 8;
  wWidth = 2570;
  wResolution = 300;

  /*Setup some option descriptors */
  sod[1].constraint.range = &ImageWidthRange;	/*Width option */
  sod[2].constraint.word_list = &ImageResolutionsList[0];	/*Resolution option */

  /*Search for ports in system: */
  ieee1284_find_ports (&pl, 0);

  return SANE_STATUS_GOOD;
}

void
sane_exit (void)
{
  if (scanner_d != -1)
    {
      CloseScanner (scanner_d);
      scanner_d = -1;
    }

  /*Free alocated ports information: */
  ieee1284_free_ports (&pl);

  DBG (2, "sane_exit\n");
  return;
}

/* Device select/open/close */
static const SANE_Device dev[] = {
  {
   "hpsj5s",
   "Hewlett-Packard",
   "ScanJet 5S",
   "sheetfed scanner"}
};

SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
  /*One device is supported and currently present */
  static const SANE_Device *devlist[] = {
    dev + 0, 0
  };

  /*No scanners presents */
  static const SANE_Device *void_devlist[] = { 0 };

  DBG (2, "sane_get_devices: local_only = %d\n", local_only);

  if (scanner_d != -1)		/*Device is opened, so it's present. */
    {
      *device_list = devlist;
      return SANE_STATUS_GOOD;
    };

  /*Device was not opened. */
  scanner_d = OpenScanner (scanner_path);

  if (scanner_d == -1)		/*No devices present */
    {
      DBG (1, "failed to open scanner.\n");
      *device_list = void_devlist;
      return SANE_STATUS_GOOD;
    }
  DBG (1, "port opened.\n");

  /*Check device. */
  DBG (1, "sane_get_devices: check scanner started.");
  if (DetectScanner () == 0)
    {				/*Device malfunction! */
      DBG (1, "sane_get_devices: Device malfunction.");
      *device_list = void_devlist;
      return SANE_STATUS_GOOD;
    }
  else
    {
      DBG (1, "sane_get_devices: Device works OK.");
      *device_list = devlist;
    }

  /*We do not need it any more */
  CloseScanner (scanner_d);
  scanner_d = -1;

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
{
  int i;

  if (!devicename)
    {
      DBG (1, "sane_open: devicename is NULL!");
      return SANE_STATUS_INVAL;
    }

  DBG (2, "sane_open: devicename = \"%s\"\n", devicename);

  if (!devicename[0])
    i = 0;
  else
    for (i = 0; i < NELEMS (dev); ++i)	/*Search for device in list */
      if (strcmp (devicename, dev[i].name) == 0)
	break;

  if (i >= NELEMS (dev))	/*No such device */
    return SANE_STATUS_INVAL;

  if (scanner_d != -1)		/*scanner opened already! */
    return SANE_STATUS_DEVICE_BUSY;

  DBG (1, "sane_open: scanner device path name is \'%s\'\n", scanner_path);

  scanner_d = OpenScanner (scanner_path);
  if (scanner_d == -1)
    return SANE_STATUS_DEVICE_BUSY;	/*This should be done more carefully */

  /*Check device. */
  DBG (1, "sane_open: check scanner started.");
  if (DetectScanner () == 0)
    {				/*Device malfunction! */
      DBG (1, "sane_open: Device malfunction.");
      CloseScanner (scanner_d);
      scanner_d = -1;
      return SANE_STATUS_IO_ERROR;
    }
  DBG (1, "sane_open: Device found.All are green.");
  *handle = (SANE_Handle) (unsigned long)scanner_d;

  return SANE_STATUS_GOOD;
}

void
sane_close (SANE_Handle handle)
{
  DBG (2, "sane_close\n");
  /*We support only single device - so ignore handle (FIX IT LATER) */
  if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
    return;			/* wrong device */
  StandByScanner ();
  CloseScanner (scanner_d);
  scanner_d = -1;
}

const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
{
  DBG (2, "sane_get_option_descriptor: option = %d\n", option);
  if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
    return NULL;		/* wrong device */

  if (option < 0 || option >= NELEMS (sod))	/*No real options supported */
    return NULL;

  return &sod[option];		/*Return demanded option */
}

SANE_Status
sane_control_option (SANE_Handle handle, SANE_Int option,
		     SANE_Action action, void *value, SANE_Int * info)
{
  if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
    return SANE_STATUS_INVAL;	/* wrong device */

  if ((option >= NELEMS (sod)) || (option < 0))	/*Supported only this option */
    return SANE_STATUS_INVAL;

  switch (option)
    {
    case 0:			/*Number of options */
      if (action != SANE_ACTION_GET_VALUE)	/*It can be only read */
	return SANE_STATUS_INVAL;

      *((SANE_Int *) value) = NELEMS (sod);
      return SANE_STATUS_GOOD;
    case 1:			/*Scan area width */
      switch (action)
	{
	case SANE_ACTION_GET_VALUE:
	  *((SANE_Word *) value) = wWidth;
	  return SANE_STATUS_GOOD;
	case SANE_ACTION_SET_VALUE:	/*info should be setted */
	  wWidth = *((SANE_Word *) value);
	  if (info != NULL)
	    *info = SANE_INFO_RELOAD_PARAMS;
	  return SANE_STATUS_GOOD;
	default:
	  return SANE_STATUS_INVAL;
	}
    case 2:			/*Resolution */
      switch (action)
	{
	case SANE_ACTION_GET_VALUE:
	  *((SANE_Word *) value) = wResolution;
	  return SANE_STATUS_GOOD;
	case SANE_ACTION_SET_VALUE:	/*info should be setted */
	  wResolution = *((SANE_Word *) value);
	  if (info != NULL)
	    *info = 0;
	  return SANE_STATUS_GOOD;
	default:
	  return SANE_STATUS_INVAL;
	}
    default:
      return SANE_STATUS_INVAL;
    }
  return SANE_STATUS_GOOD;	/*For now we have no options to control */
}

SANE_Status
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
{
  DBG (2, "sane_get_parameters\n");

  if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
    return SANE_STATUS_INVAL;	/* wrong device */

  /*Ignore handle parameter for now. FIX it latter. */
  /*These parameters are OK for gray scale mode. */
  parms.depth = /*wCurrentDepth */ 8;
  parms.format = /*wCurrentFormat */ SANE_FRAME_GRAY;
  parms.last_frame = SANE_TRUE;	/*For grayscale... */
  parms.lines = -1;		/*Unknown a priory */
  parms.pixels_per_line = LengthForRes (wResolution, wWidth);	/*For grayscale... */
  parms.bytes_per_line = parms.pixels_per_line;	/*For grayscale... */
  *params = parms;
  return SANE_STATUS_GOOD;
}

SANE_Status
sane_start (SANE_Handle handle)
{
  int i;
  DBG (2, "sane_start\n");

  if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
    return SANE_STATUS_IO_ERROR;

  CallFunctionWithParameter (0x93, 2);
  bLastCalibration = CallFunctionWithRetVal (0xA9);
  if (bLastCalibration == 0)
    bLastCalibration = -1;

  /*Turn on the lamp: */
  CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, FLAGS_HW_LAMP_ON);
  bHardwareState = FLAGS_HW_LAMP_ON;
  /*Get average white point */
  bCalibration = GetCalibration ();

  if (bLastCalibration - bCalibration > 16)
    {				/*Lamp is not warm enouth */
      DBG (1, "sane_start: warming lamp for 30 sec.\n");
      for (i = 0; i < 30; i++)
	sleep (1);
    }

  /*Check paper presents */
  if (CheckPaperPresent () == 0)
    {
      DBG (1, "sane_start: no paper detected.");
      return SANE_STATUS_NO_DOCS;
    }
  CalibrateScanElements ();
  TransferScanParameters (GrayScale, wResolution, wWidth);
  /*Turn on indicator and prepare engine. */
  SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 1);
  /*Feed paper */
  if (PaperFeed (LINES_TO_FEED) == 0)	/*Feed only for fixel lenght. Change it */
    {
      DBG (1, "sane_start: paper feed failed.");
      SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 0);
      return SANE_STATUS_JAMMED;
    }
  /*Set paper moving speed */
  TurnOnPaperPulling (GrayScale, wResolution);

  wVerticalResolution = 0;	/*Reset counter */

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * data,
	   SANE_Int max_length, SANE_Int * length)
{
  SANE_Byte bFuncResult, bTest;
  int timeout;

  if (!length)
    {
      DBG (1, "sane_read: length == NULL\n");
      return SANE_STATUS_INVAL;
    }
  *length = 0;
  if (!data)
    {
      DBG (1, "sane_read: data == NULL\n");
      return SANE_STATUS_INVAL;
    }

  if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1))
    {
      DBG (1, "sane_read: unknown handle\n");
      return SANE_STATUS_INVAL;
    }

  /*While end of paper sheet was not reached */
  /*Wait for scaned line ready */
  timeout = 0;
  while (((bFuncResult = CallFunctionWithRetVal (0xB2)) & 0x20) == 0)
    {
      bTest = CallFunctionWithRetVal (0xB5);
      usleep (1);
      timeout++;
      if ((timeout < 1000) &&
	  (((bTest & 0x80) && ((bTest & 0x3F) <= 2)) ||
	   (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))))
	continue;

      if (timeout >= 1000)
	continue;		/*do it again! */

      /*Data ready state! */

      if ((bFuncResult & 0x20) != 0)	/*End of paper reached! */
	{
	  *length = 0;
	  return SANE_STATUS_EOF;
	}

      /*Data ready */
      *length = LengthForRes (wResolution, wWidth);
      if (*length >= max_length)
	*length = max_length;

      CallFunctionWithParameter (0xCD, 0);
      CallFunctionWithRetVal (0xC8);
      WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8);
      WriteAddress (ADDRESS_RESULT);
      /*Test if we need this line for current resolution
         (scanner doesn't control vertical resolution in hardware) */
      wVerticalResolution -= wResolution;
      if (wVerticalResolution > 0)
	{
	  timeout = 0;
	  continue;
	}
      else
	wVerticalResolution = 300;	/*Reset counter */

      ReadDataBlock (data, *length);

      /*switch indicator */
      bHardwareState ^= FLAGS_HW_INDICATOR_OFF;
      CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);
      return SANE_STATUS_GOOD;
    }
  return SANE_STATUS_EOF;
}

void
sane_cancel (SANE_Handle handle)
{
  DBG (2, "sane_cancel: handle = %p\n", handle);
  /*Stop motor */
  TurnOffPaperPulling ();

  /*Indicator turn off */
  bHardwareState |= FLAGS_HW_INDICATOR_OFF;
  CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);

  /*Get out of paper */
  ReleasePaper ();

  /*Restore indicator */
  bHardwareState &= ~FLAGS_HW_INDICATOR_OFF;
  CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);

  bLastCalibration = CallFunctionWithRetVal (0xA9);
  CallFunctionWithParameter (0xA9, bLastCalibration);
  CallFunctionWithParameter (0x93, 4);

}

SANE_Status
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
{
  DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle,
       non_blocking);
  return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
{
  DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle,
       fd ? "!=" : "=");
  return SANE_STATUS_UNSUPPORTED;
}

/*
        Middle-level API:
*/

/*
        Detect if scanner present and works correctly.
        Ret Val: 0 = detection failed, 1 = detection OK.
*/
static int
DetectScanner (void)
{
  int Result1, Result2;
  int Successful, Total;

  Result1 = OutputCheck ();
  Result2 = InputCheck ();

  if (!(Result1 || Result2))	/*If all are 0 - it's error */
    {
      return 0;
    }

  WriteScannerRegister (0x7C, 0x80);
  WriteScannerRegister (0x7F, 0x1);
  WriteScannerRegister (0x72, 0x10);
  WriteScannerRegister (0x72, 0x90);
  WriteScannerRegister (0x7C, 0x24);
  WriteScannerRegister (0x75, 0x0C);
  WriteScannerRegister (0x78, 0x0);
  WriteScannerRegister (0x79, 0x10);
  WriteScannerRegister (0x71, 0x10);
  WriteScannerRegister (0x71, 0x1);
  WriteScannerRegister (0x72, 0x1);

  for (Successful = 0, Total = 0; Total < 5; Total++)
    {
      if (CallCheck ())
	Successful++;
      if (Successful >= 3)
	return 1;		/*Correct and Stable */
    }
  return 0;
}

static void
StandByScanner ()
{
  WriteScannerRegister (0x74, 0x80);
  WriteScannerRegister (0x75, 0x0C);
  WriteScannerRegister (0x77, 0x0);
  WriteScannerRegister (0x78, 0x0);
  WriteScannerRegister (0x79, 0x0);
  WriteScannerRegister (0x7A, 0x0);
  WriteScannerRegister (0x7B, 0x0);
  WriteScannerRegister (0x7C, 0x4);
  WriteScannerRegister (0x70, 0x0);
  WriteScannerRegister (0x72, 0x90);
  WriteScannerRegister (0x70, 0x0);
}

static void
SwitchHardwareState (SANE_Byte mask, SANE_Byte invert_mask)
{
  if (!invert_mask)
    {
      bHardwareState &= ~mask;
    }
  else
    bHardwareState |= mask;

  CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState);
}

/*return value: 0 - no paper, 1 - paper loaded.*/
static int
CheckPaperPresent ()
{
  if ((CallFunctionWithRetVal (0xB2) & 0x10) == 0)
    return 1;			/*Ok - paper present. */
  return 0;			/*No paper present */
}

static int
ReleasePaper ()
{
  int i;

  if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0)
    {				/*End of paper was not reached */
      CallFunctionWithParameter (0xA7, 0xF);
      CallFunctionWithParameter (0xA8, 0xFF);
      CallFunctionWithParameter (0xC2, 0);

      for (i = 0; i < 90000; i++)
	{
	  if (CallFunctionWithRetVal (0xB2) & 0x80)
	    break;
	  usleep (1);
	}
      if (i >= 90000)
	return 0;		/*Fail. */

      for (i = 0; i < 90000; i++)
	{
	  if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0)
	    break;
	  else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
	    {
	      i = 90000;
	      break;
	    }
	  usleep (1);
	}

      CallFunctionWithParameter (0xC5, 0);

      if (i >= 90000)
	return 0;		/*Fail. */

      while (CallFunctionWithRetVal (0xB2) & 0x80);	/*Wait bit dismiss */

      CallFunctionWithParameter (0xA7, 1);
      CallFunctionWithParameter (0xA8, 0x25);
      CallFunctionWithParameter (0xC2, 0);

      for (i = 0; i < 90000; i++)
	{
	  if (CallFunctionWithRetVal (0xB2) & 0x80)
	    break;
	  usleep (1);
	}
      if (i >= 90000)
	return 0;		/*Fail. */

      for (i = 0; i < 90000; i++)
	{
	  if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
	    break;
	  usleep (1);
	}
      if (i >= 90000)
	return 0;		/*Fail. */
    }

  if (CallFunctionWithRetVal (0xB2) & 0x10)
    {
      CallFunctionWithParameter (0xA7, 1);
      CallFunctionWithParameter (0xA8, 0x40);
    }
  else
    {
      CallFunctionWithParameter (0xA7, 0);
      CallFunctionWithParameter (0xA8, 0xFA);
    }
  CallFunctionWithParameter (0xC2, 0);

  for (i = 0; i < 9000; i++)
    {
      if (CallFunctionWithRetVal (0xB2) & 0x80)
	break;
      usleep (1);
    }
  if (i >= 9000)
    return 0;			/*Fail. */

  while (CallFunctionWithRetVal (0xB2) & 0x80)
    usleep (1);

  return 1;
}

static void
TransferScanParameters (enumColorDepth enColor, SANE_Word wResolution,
			SANE_Word wPixelsLength)
{
  SANE_Word wRightBourder = (2570 + wPixelsLength) / 2 + 65;
  SANE_Word wLeftBourder = (2570 - wPixelsLength) / 2 + 65;

  switch (enColor)
    {
    case Drawing:
      CallFunctionWithParameter (0x90, 2);	/*Not supported correctle. FIX ME!!! */
      break;
    case Halftone:
      CallFunctionWithParameter (0x90, 0xE3);	/*Not supported correctly. FIX ME!!! */
      CallFunctionWithParameter (0x92, 3);
      break;
    case GrayScale:
    case TrueColor:
      CallFunctionWithParameter (0x90, 0);	/*Not suppoted correctly. FIX ME!!! */
      break;
    };
  CallFunctionWithParameter (0xA1, 2);
  CallFunctionWithParameter (0xA2, 1);
  CallFunctionWithParameter (0xA3, 0x98);
  /*Resolution: */
  CallFunctionWithParameter (0x9A, (SANE_Byte) (wResolution >> 8));	/*High byte */
  CallFunctionWithParameter (0x9B, (SANE_Byte) wResolution);	/*Low byte */

  LoadingPaletteToScanner ();

  CallFunctionWithParameter (0xA4, 31);	/*Some sort of constant parameter */
  /*Left bourder */
  CallFunctionWithParameter (0xA5, wLeftBourder / 256);
  CallFunctionWithParameter (0xA6, wLeftBourder % 256);
  /*Right bourder */
  CallFunctionWithParameter (0xAA, wRightBourder / 256);
  CallFunctionWithParameter (0xAB, wRightBourder % 256);

  CallFunctionWithParameter (0xD0, 0);
  CallFunctionWithParameter (0xD1, 0);
  CallFunctionWithParameter (0xD2, 0);
  CallFunctionWithParameter (0xD3, 0);
  CallFunctionWithParameter (0xD4, 0);
  CallFunctionWithParameter (0xD5, 0);

  CallFunctionWithParameter (0x9D, 5);
}

static void
TurnOnPaperPulling (enumColorDepth enColor, SANE_Word wResolution)
{
  switch (enColor)
    {
    case Drawing:
    case Halftone:
      CallFunctionWithParameter (0x91, 0xF7);
      return;
    case GrayScale:
      switch (wResolution)
	{
	case 50:
	case 75:
	case 100:
	  CallFunctionWithParameter (0x91, 0xB7);
	  return;
	case 150:
	case 200:
	  CallFunctionWithParameter (0x91, 0x77);
	  return;
	case 250:
	case 300:
	  CallFunctionWithParameter (0x91, 0x37);
	  return;
	default:
	  return;
	}
    case TrueColor:
      switch (wResolution)
	{
	case 75:
	case 100:
	  CallFunctionWithParameter (0x91, 0xA3);
	  return;
	case 150:
	case 200:
	  CallFunctionWithParameter (0x91, 0x53);
	  return;
	case 250:
	case 300:
	  CallFunctionWithParameter (0x91, 0x3);
	  return;
	default:
	  return;
	}
    default:
      return;
    }
}

static void
TurnOffPaperPulling ()
{
  CallFunctionWithParameter (0x91, 0);
}

/*
        Returns avarage value of scaned row.
        While paper not loaded this is base "white point".
*/
static SANE_Byte
GetCalibration ()
{
  int i;
  int Result;
  SANE_Byte Buffer[2600];
  SANE_Byte bTest;

  CallFunctionWithParameter (0xA1, 2);
  CallFunctionWithParameter (0xA2, 1);
  CallFunctionWithParameter (0xA3, 0x98);

  /*Resolution to 300 DPI */
  CallFunctionWithParameter (0x9A, 1);
  CallFunctionWithParameter (0x9B, 0x2C);

  CallFunctionWithParameter (0x92, 0);
  CallFunctionWithParameter (0xC6, 0);
  CallFunctionWithParameter (0x92, 0x80);

  for (i = 1; i < 256; i++)
    CallFunctionWithParameter (0xC6, i);

  for (i = 0; i < 256; i++)
    CallFunctionWithParameter (0xC6, i);

  for (i = 0; i < 256; i++)
    CallFunctionWithParameter (0xC6, i);

  CallFunctionWithParameter (0xA4, 31);	/*Some sort of constant */

  /*Left bourder */
  CallFunctionWithParameter (0xA5, 0);
  CallFunctionWithParameter (0xA6, 0x41);

  /*Right bourder */
  CallFunctionWithParameter (0xAA, 0xA);
  CallFunctionWithParameter (0xAB, 0x39);

  CallFunctionWithParameter (0xD0, 0);
  CallFunctionWithParameter (0xD1, 0);
  CallFunctionWithParameter (0xD2, 0);
  CallFunctionWithParameter (0xD3, 0);
  CallFunctionWithParameter (0xD4, 0);
  CallFunctionWithParameter (0xD5, 0);

  CallFunctionWithParameter (0x9C, 0x1B);
  CallFunctionWithParameter (0x9D, 5);

  CallFunctionWithParameter (0x92, 0x10);
  CallFunctionWithParameter (0xC6, 0xFF);
  CallFunctionWithParameter (0x92, 0x90);

  for (i = 0; i < 2999; i++)
    CallFunctionWithParameter (0xC6, 0xFF);

  CallFunctionWithParameter (0x92, 0x50);
  CallFunctionWithParameter (0xC6, 0);
  CallFunctionWithParameter (0x92, 0xD0);

  for (i = 0; i < 2999; i++)
    CallFunctionWithParameter (0xC6, 0);

  CallFunctionWithParameter (0x98, 0xFF);	/*Up limit */
  CallFunctionWithParameter (0x95, 0);	/*Low limit */

  CallFunctionWithParameter (0x90, 0);	/*Gray scale... */

  CallFunctionWithParameter (0x91, 0x3B);	/*Turn motor on. */

  for (i = 0; i < 5; i++)
    {
      do
	{			/*WARNING!!! Deadlock possible! */
	  bTest = CallFunctionWithRetVal (0xB5);
	}
      while ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
	     (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)));

      CallFunctionWithParameter (0xCD, 0);
      /*Skip this line for ECP: */
      CallFunctionWithRetVal (0xC8);

      WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8);
      WriteAddress (0x20);
      ReadDataBlock (Buffer, 2552);
    };
  CallFunctionWithParameter (0x91, 0);	/*Turn off motor. */
  usleep (10);
  for (Result = 0, i = 0; i < 2552; i++)
    Result += Buffer[i];
  return Result / 2552;
}

static int
PaperFeed (SANE_Word wLinesToFeed)
{
  int i;

  CallFunctionWithParameter (0xA7, 0xF);
  CallFunctionWithParameter (0xA8, 0xFF);
  CallFunctionWithParameter (0xC2, 0);

  for (i = 0; i < 9000; i++)
    {
      if (CallFunctionWithRetVal (0xB2) & 0x80)
	break;
      usleep (1);
    }
  if (i >= 9000)
    return 0;			/*Fail. */

  for (i = 0; i < 9000; i += 5)
    {
      if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0)
	break;
      else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
	{
	  i = 9000;
	  break;
	}
      usleep (5);
    }

  CallFunctionWithParameter (0xC5, 0);

  if (i >= 9000)
    return 0;			/*Fail. */

  /*Potential deadlock */
  while (CallFunctionWithRetVal (0xB2) & 0x80);	/*Wait bit dismiss */

  CallFunctionWithParameter (0xA7, wLinesToFeed / 256);
  CallFunctionWithParameter (0xA8, wLinesToFeed % 256);
  CallFunctionWithParameter (0xC2, 0);

  for (i = 0; i < 9000; i++)
    {
      if (CallFunctionWithRetVal (0xB2) & 0x80)
	break;
      usleep (1);
    }
  if (i >= 9000)
    return 0;			/*Fail. */

  for (i = 0; i < 9000; i++)
    {
      if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0)
	break;
      usleep (1);
    }
  if (i >= 9000)
    return 0;			/*Fail. */

  return 1;
}

/*For now we do no calibrate elements - just set maximum limits. FIX ME?*/
static void
CalibrateScanElements ()
{
  /*Those arrays will be used in future for correct calibration. */
  /*Then we need to transfer UP brightness border, we use these registers */
  SANE_Byte arUpTransferBorders[] = { 0x10, 0x20, 0x30 };
  /*Then we need to transfer LOW brightness border, we use these registers */
  SANE_Byte arLowTransferBorders[] = { 0x50, 0x60, 0x70 };
  /*Then we need to save UP brightness border, we use these registers */
  SANE_Byte arUpSaveBorders[] = { 0x98, 0x97, 0x99 };
  /*Then we need to save LOW brightness border, we use these registers */
  SANE_Byte arLowSaveBorders[] = { 0x95, 0x94, 0x96 };
  /*Speeds, used for calibration */
  SANE_Byte arSpeeds[] = { 0x3B, 0x37, 0x3F };
  int j, Average, Temp, Index, /* Line, */ timeout,Calibration;
  SANE_Byte bTest /*, Min, Max, Result */ ;
  /*For current color component: (values from arrays). Next two lines - starting and terminating. */

  SANE_Byte CurrentUpTransferBorder;
  SANE_Byte CurrentLowTransferBorder;
  SANE_Byte CurrentUpSaveBorder;
  SANE_Byte CurrentLowSaveBorder;
  SANE_Byte CurrentSpeed1, CurrentSpeed2; 
  SANE_Byte CorrectionValue;
  SANE_Byte FilteredBuffer[2570];

  CallFunctionWithParameter (0xA1, 2);
  CallFunctionWithParameter (0xA2, 0);
  CallFunctionWithParameter (0xA3, 0x98);

  /*DPI = 300 */
  CallFunctionWithParameter (0x9A, 1);	/*High byte */
  CallFunctionWithParameter (0x9B, 0x2C);	/*Low byte */

  /*Paletter settings. */
  CallFunctionWithParameter (0x92, 0);
  CallFunctionWithParameter (0xC6, 0);
  CallFunctionWithParameter (0x92, 0x80);

  /*First color component */
  for (j = 1; j < 256; j++)
    CallFunctionWithParameter (0xC6, j);

  /*Second color component */
  for (j = 0; j < 256; j++)
    CallFunctionWithParameter (0xC6, j);

  /*Third color component */
  for (j = 0; j < 256; j++)
    CallFunctionWithParameter (0xC6, j);

  CallFunctionWithParameter (0xA4, 31);

  /*Left border */
  CallFunctionWithParameter (0xA5, 0);	/*High byte */
  CallFunctionWithParameter (0xA6, 0x41);	/*Low byte */

  /*Right border */
  CallFunctionWithParameter (0xAA, 0xA);	/*High byte */
  CallFunctionWithParameter (0xAB, 0x4B);	/*Low byte */

  /*Zero these registers... */
  CallFunctionWithParameter (0xD0, 0);
  CallFunctionWithParameter (0xD1, 0);
  CallFunctionWithParameter (0xD2, 0);
  CallFunctionWithParameter (0xD3, 0);
  CallFunctionWithParameter (0xD4, 0);
  CallFunctionWithParameter (0xD5, 0);

  CallFunctionWithParameter (0x9C, 0x1B);
  CallFunctionWithParameter (0x9D, 0x5);

  Average = 0;
  for (Index = 0; Index < 3; Index++)	/*For theree color components */
    {
      /*Up border = 0xFF */
      CallFunctionWithParameter (0x92, arUpTransferBorders[Index]);
      CallFunctionWithParameter (0xC6, 0xFF);
      CallFunctionWithParameter (0x92, arUpTransferBorders[Index] | 0x80);

      for (j = 2999; j > 0; j--)
	CallFunctionWithParameter (0xC6, 0xFF);

      /*Low border = 0x0 */
      CallFunctionWithParameter (0x92, arLowTransferBorders[Index]);
      CallFunctionWithParameter (0xC6, 0x0);
      CallFunctionWithParameter (0x92, arLowTransferBorders[Index] | 0x80);

      for (j = 2999; j > 0; j--)
	CallFunctionWithParameter (0xC6, 0x0);

      /*Save borders */
      CallFunctionWithParameter (arUpSaveBorders[Index], 0xFF);
      CallFunctionWithParameter (arLowSaveBorders[Index], 0x0);
      CallFunctionWithParameter (0x90, 0);	/*Gray Scale or True color sign :) */

      CallFunctionWithParameter (0x91, arSpeeds[Index]);

      /*waiting for scaned line... */
      timeout = 0;
      do
	{
	  bTest = CallFunctionWithRetVal (0xB5);
	  timeout++;
	  usleep (1);
	}
      while ((timeout < 1000) &&
	     ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
	      (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))));

      /*Let's read it... */
      if(timeout < 1000)
      {
        CallFunctionWithParameter (0xCD, 0);
        CallFunctionWithRetVal (0xC8);
        WriteScannerRegister (0x70, 0xC8);
        WriteAddress (0x20);

        ReadDataBlock (FilteredBuffer, 2570);
      }

      CallFunctionWithParameter (0x91, 0);	/*Stop engine. */

     /*Note: if first read failed, junk would be calculated, but if previous
	read was succeded, but last one failed, previous data'ld be used.
     */
     for(Temp = 0, j = 0; j < 2570; j++)
     Temp += FilteredBuffer[j];
     Temp /= 2570;

     if((Average == 0)||(Average > Temp))
     Average = Temp; 
    }

    for(Index = 0; Index < 3; Index++) /*Three color components*/
    {
	CurrentUpTransferBorder = arUpTransferBorders[Index];
	CallFunctionWithParameter (0xC6, 0xFF);
	CallFunctionWithParameter (0x92, CurrentUpTransferBorder|0x80);
	for(j=2999; j>0; j--)
	    CallFunctionWithParameter (0xC6, 0xFF);

	CurrentLowTransferBorder = arLowTransferBorders[Index];
	CallFunctionWithParameter (0xC6, 0x0);
	CallFunctionWithParameter (0x92, CurrentLowTransferBorder|0x80);
	for(j=2999; j>0; j--)
	    CallFunctionWithParameter (0xC6, 0);
	
	CurrentUpSaveBorder = arUpSaveBorders[Index];
	CallFunctionWithParameter (CurrentUpSaveBorder, 0xFF);

	CurrentLowSaveBorder = arLowSaveBorders[Index];
	CallFunctionWithParameter (CurrentLowSaveBorder, 0x0);
	CallFunctionWithParameter (0x90,0);
	Calibration = 0x80;
	CallFunctionWithParameter (CurrentUpSaveBorder, 0x80);
	
	CurrentSpeed1 = CurrentSpeed2 = arSpeeds[Index];
	
	for(CorrectionValue = 0x40; CorrectionValue != 0;CorrectionValue >>= 2)
	{
	    CallFunctionWithParameter (0x91, CurrentSpeed2);
	    usleep(10);

    	    /*waiting for scaned line... */
	    for(j = 0; j < 5; j++)
	    {
    		timeout = 0;
    		do
		{
		    bTest = CallFunctionWithRetVal (0xB5);
		    timeout++;
		    usleep (1);
		}
    		while ((timeout < 1000) &&
	    	((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) ||
	        (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))));

    		/*Let's read it... */
    		if(timeout < 1000)
    		{
    		    CallFunctionWithParameter (0xCD, 0);
        	    CallFunctionWithRetVal (0xC8);
		    WriteScannerRegister (0x70, 0xC8);
    	    	    WriteAddress (0x20);

    		    ReadDataBlock (FilteredBuffer, 2570);
    		}
	    }/*5 times we read. I don't understand what for, but so does HP's driver.
		Perhaps, we can optimize it in future.*/
	    WriteScannerRegister (0x91, 0);
	    usleep(10);
	    
	    for(Temp = 0,j = 0; j < 16;j++)
		Temp += FilteredBuffer[509+j]; /*At this offset calcalates HP's driver.*/
	    Temp /= 16;
	    
	    if(Average > Temp)
	    {
		Calibration += CorrectionValue;
		Calibration = 0xFF < Calibration ? 0xFF : Calibration; /*min*/
	    }
	    else
		Calibration -= CorrectionValue;
	    
	    WriteScannerRegister (CurrentUpSaveBorder, Calibration);
	}/*By CorrectionValue we tune UpSaveBorder*/

	WriteScannerRegister (0x90, 8);
	WriteScannerRegister (0x91, CurrentSpeed1);
	usleep(10);
    }/*By color components*/

  return;
}

/*
      Internal use functions:
*/

/*Returns 0 in case of fail and 1 in success.*/
static int
OutputCheck ()
{
  int i;

  WriteScannerRegister (0x7F, 0x1);
  WriteAddress (0x7E);
  for (i = 0; i < 256; i++)
    WriteData ((SANE_Byte) i);

  WriteAddress (0x3F);
  if (ReadDataByte () & 0x80)
    return 0;

  return 1;
}

static int
InputCheck ()
{
  int i;
  SANE_Byte Buffer[256];

  WriteAddress (0x3E);
  for (i = 0; i < 256; i++)
    {
      Buffer[i] = ReadDataByte ();
    }

  for (i = 0; i < 256; i++)
    {
      if (Buffer[i] != i)
	return 0;
    }

  return 1;
}

static int
CallCheck ()
{
  int i;
  SANE_Byte Buffer[256];

  CallFunctionWithParameter (0x92, 0x10);
  CallFunctionWithParameter (0xC6, 0x0);
  CallFunctionWithParameter (0x92, 0x90);
  WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6);

  WriteAddress (0x60);

  for (i = 1; i < 256; i++)
    WriteData ((SANE_Byte) i);

  CallFunctionWithParameter (0x92, 0x10);
  CallFunctionWithRetVal (0xC6);
  CallFunctionWithParameter (0x92, 0x90);
  WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6);

  WriteAddress (ADDRESS_RESULT);

  ReadDataBlock (Buffer, 256);

  for (i = 0; i < 255; i++)
    {
      if (Buffer[i + 1] != (SANE_Byte) i)
	return 0;
    }
  return 1;
}

static void
LoadingPaletteToScanner ()
{
  /*For now we have statical gamma. */
  SANE_Byte Gamma[256];
  int i;
  for (i = 0; i < 256; i++)
    Gamma[i] = i;

  CallFunctionWithParameter (0x92, 0);
  CallFunctionWithParameter (0xC6, Gamma[0]);
  CallFunctionWithParameter (0x92, 0x80);
  for (i = 1; i < 256; i++)
    CallFunctionWithParameter (0xC6, Gamma[i]);

  for (i = 0; i < 256; i++)
    CallFunctionWithParameter (0xC6, Gamma[i]);

  for (i = 0; i < 256; i++)
    CallFunctionWithParameter (0xC6, Gamma[i]);
}

/*
        Low level warappers:
*/
static void
WriteAddress (SANE_Byte Address)
{
  ieee1284_data_dir (pl.portv[scanner_d], 0);	/*Forward mode */
  ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
  ieee1284_epp_write_addr (pl.portv[scanner_d], 0, (char *) &Address, 1);
}

static void
WriteData (SANE_Byte Data)
{
  ieee1284_data_dir (pl.portv[scanner_d], 0);	/*Forward mode */
  ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
  ieee1284_epp_write_data (pl.portv[scanner_d], 0, (char *) &Data, 1);
}

static void
WriteScannerRegister (SANE_Byte Address, SANE_Byte Data)
{
  WriteAddress (Address);
  WriteData (Data);
}

static void
CallFunctionWithParameter (SANE_Byte Function, SANE_Byte Parameter)
{
  WriteScannerRegister (REGISTER_FUNCTION_CODE, Function);
  WriteScannerRegister (REGISTER_FUNCTION_PARAMETER, Parameter);
}

static SANE_Byte
CallFunctionWithRetVal (SANE_Byte Function)
{
  WriteScannerRegister (REGISTER_FUNCTION_CODE, Function);
  WriteAddress (ADDRESS_RESULT);
  return ReadDataByte ();
}

static SANE_Byte
ReadDataByte ()
{
  SANE_Byte Result;

  ieee1284_data_dir (pl.portv[scanner_d], 1);	/*Reverse mode */
  ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
  ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) &Result, 1);
  return Result;
}

static void
ReadDataBlock (SANE_Byte * Buffer, int length)
{

  ieee1284_data_dir (pl.portv[scanner_d], 1);	/*Reverse mode */
  ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT);
  ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) Buffer, length);
}

/* Send a daisy-chain-style CPP command packet. */
int
cpp_daisy (struct parport *port, int cmd)
{
  unsigned char s;

  ieee1284_data_dir (port, 0);	/*forward direction */
  ieee1284_write_control (port, C1284_NINIT);
  ieee1284_write_data (port, 0xaa);
  usleep (2);
  ieee1284_write_data (port, 0x55);
  usleep (2);
  ieee1284_write_data (port, 0x00);
  usleep (2);
  ieee1284_write_data (port, 0xff);
  usleep (2);
  s = ieee1284_read_status (port) ^ S1284_INVERTED;	/*Converted for PC-style */

  s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT);

  if (s != (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT))
    {
      DBG (1, "%s: cpp_daisy: aa5500ff(%02x)\n", port->name, s);
      return -1;
    }

  ieee1284_write_data (port, 0x87);
  usleep (2);
  s = ieee1284_read_status (port) ^ S1284_INVERTED;	/*Convert to PC-style */

  s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT);

  if (s != (S1284_SELECT | S1284_NFAULT))
    {
      DBG (1, "%s: cpp_daisy: aa5500ff87(%02x)\n", port->name, s);
      return -1;
    }

  ieee1284_write_data (port, 0x78);
  usleep (2);
  ieee1284_write_control (port, C1284_NINIT);
  ieee1284_write_data (port, cmd);
  usleep (2);
  ieee1284_frob_control (port, C1284_NSTROBE, C1284_NSTROBE);
  usleep (1);
  ieee1284_frob_control (port, C1284_NSTROBE, 0);
  usleep (1);
  s = ieee1284_read_status (port);
  ieee1284_write_data (port, 0xff);
  usleep (2);

  return s;
}

/*Daisy chain deselect operation.*/
void
daisy_deselect_all (struct parport *port)
{
  cpp_daisy (port, 0x30);
}

/*Daisy chain select operation*/
int
daisy_select (struct parport *port, int daisy, int mode)
{
  switch (mode)
    {
      /*For these modes we should switch to EPP mode: */
    case M1284_EPP:
    case M1284_EPPSL:
    case M1284_EPPSWE:
      return cpp_daisy (port, 0x20 + daisy) & S1284_NFAULT;
      /*For these modes we should switch to ECP mode: */
    case M1284_ECP:
    case M1284_ECPRLE:
    case M1284_ECPSWE:
      return cpp_daisy (port, 0xd0 + daisy) & S1284_NFAULT;
      /*Nothing was told for BECP in Daisy chain specification.
         May be it's wise to use ECP? */
    case M1284_BECP:
      /*Others use compat mode */
    case M1284_NIBBLE:
    case M1284_BYTE:
    case M1284_COMPAT:
    default:
      return cpp_daisy (port, 0xe0 + daisy) & S1284_NFAULT;
    }
}

/*Daisy chain assign address operation.*/
int
assign_addr (struct parport *port, int daisy)
{
  return cpp_daisy (port, daisy);
}

static int
OpenScanner (const char *scanner_path)
{
  int handle;
  int caps;

  /*Scaner name was specified in config file?*/
  if (strlen(scanner_path) == 0)
    return -1;

  for (handle = 0; handle < pl.portc; handle++)
    {
      if (strcmp (scanner_path, pl.portv[handle]->name) == 0)
	break;
    }
  if (handle == pl.portc)	/*No match found */
    return -1;

  /*Open port */
  if (ieee1284_open (pl.portv[handle], 0, &caps) != E1284_OK)
    return -1;

  /*Claim port */
  if (ieee1284_claim (pl.portv[handle]) != E1284_OK)
    return -1;

  /*Total chain reset. */
  daisy_deselect_all (pl.portv[handle]);

  /*Assign addresses. */
  assign_addr (pl.portv[handle], 0);	/*Assume we have device first in chain. */

  /*Select required device. For now - first in chain. */
  daisy_select (pl.portv[handle], 0, M1284_EPP);

  return handle;
}

static void
CloseScanner (int handle)
{
  if (handle == -1)
    return;

  daisy_deselect_all (pl.portv[handle]);

  ieee1284_release (pl.portv[handle]);

  ieee1284_close (pl.portv[handle]);
}
