/* sane - Scanner Access Now Easy.

   Copyright (C) 2006 Gerard Klaver  <gerard at gkall dot hobby dot nl>
   The teco2 and gl646 backend (Frank Zago) are used as a template for 
   this backend.
   
   For the authentec_add_text routine the add_text routine and font_6x11.h file 
   are taken from the webcam.c file, part of xawtv program,
   (c) 1998-2002 Gerd Knorr (GNU GPL license 2).

   For the init of the device, parts of the following programs are used:
   biopod (Mike Smith, Micah Villmow)
   udrv (David Zeuthen)
   main (Andreas Pehnack)
   aespack	Andreas Grotz
   aes2501-WY	Wittawat Yamwong

   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.
   
*/

/*  $Id: authentec.c,v 1.4 2006/09/18 19:49:06 gerard Exp $

   authentec fingerprint  driver Gerard Klaver
*/

/*SANE FLOW DIAGRAM

   - sane_init() : initialize backend, attach fingerprints
   . - sane_get_devices() : query list of fingerprint devices
   . - sane_open() : open a particular fingerprint device
   . . - sane_set_io_mode : set blocking mode
   . . - sane_get_select_fd : get fingerprint fd
   . . - sane_get_option_descriptor() : get option information
   . . - sane_control_option() : change option values
   . .
   . . - sane_start() : start image acquisition
   . .   - sane_get_parameters() : returns actual scan parameters
   . .   - sane_read() : read image data (from pipe)
   . .     (sane_read called multiple times; 
   . .      after sane_read returns EOF)
   . .     go back to sane_start() if more frames desired
   . . - sane_cancel() : cancel operation
   . - sane_close() : close opened fingerprint device
   - sane_exit() : terminate use of backend
*/
/*--------------------------------------------------------------------------*/

#define BUILD 1			/* 2006/01/09  update 14-08-2006 */
#define BACKEND_NAME authentec
#define AUTHENTEC_CONFIG_FILE "authentec.conf"

/* --------------------- SANE INTERNATIONALISATION ------------------------ */

/* must be first include */
#include "../include/sane/config.h"

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei_usb.h"
#include "../include/sane/sanei_debug.h"
#include "../include/sane/sanei_backend.h"
#include "../include/sane/sanei_config.h"
#include "../include/lassert.h"

/* for add-text routine           */
#include <time.h>
#include "../include/font_6x11.h"
/*--------------------------------*/

/* #include "authentec_aes2501.c" */
#include "authentec.h"

#define TIMEOUT 1000

/*--------------------------------------------------------------------------*/
/* Lists of possible scan modes. */
static SANE_String_Const scan_mode_list[] = {
  GRAYSCALE_RAW_STR,
  GRAYSCALE_TEXT_STR,
  SANE_VALUE_SCAN_MODE_GRAY,
  NULL
};

/*-----------------------------------------minium, maximum, quantization----*/
static const SANE_Range brightness_range = { -128, 128, 1 };

static const SANE_Range red_level_range = { -32, 32, 1 };

static const SANE_Range white_level_range = { -32, 32, 1 };

/*--------------------------------------------------------------------------*/

static const struct dpi_color_adjust authentec_dpi_color_adjust[] = {

  /*dpi, y, x, color sequence R G or B */
  {192, 480, 0, 1, 2},		/* 30x           AES2501 */
  {192, 160, 0, 1, 2},		/* 10x           AES2501 */
  {192, 16, 0, 1, 2},		/* singel field  AES2501 */
  /* must be the last entry */
  {0, 0, 0, 0, 0}
};

static const struct dpi_color_adjust authentec_3400_dpi_color_adjust[] = {

  /*dpi, y, x, color sequence R G or B */
  {128, 128, 0, 1, 2},		/* 128 128               */
  {128, 128, 0, 1, 2},		/* added to get 2 values */
  /* must be the last entry */
  {0, 0, 0, 0, 0}
};

static const struct dpi_color_adjust authentec_3500_dpi_color_adjust[] = {

  /*dpi, y, x, color sequence R G or B */
  {128, 128, 0, 1, 2},		/* AES3500            */
  {96, 96, 0, 1, 2},		/* AES4000            */
  /* must be the last entry */
  {0, 0, 0, 0, 0}
};

static const struct fingerprint_hardware fingerprints[] = {

  {0x054c, 0x0171, USB_CLASS_VENDOR_SPEC, Authentec_AES3400,
   "SONY Microvault USB STICK", "Authentec AES3400",
   authentec_3400_dpi_color_adjust},

  {0x08ff, 0x2580, USB_CLASS_VENDOR_SPEC, Authentec_AES2501,
   "MEDION MD85264", "Authentec AES2501",
   authentec_dpi_color_adjust},

  {0x08ff, 0x5711, USB_CLASS_VENDOR_SPEC, Authentec_AES3500_X50,
   "Samsung X50", "Authentec 3500_X50",
   authentec_3500_dpi_color_adjust},

  {0x08ff, 0x5731, USB_CLASS_VENDOR_SPEC, Authentec_AES3500,
   "BIOPOD", "Authentec AES/3500/4000",
   authentec_3500_dpi_color_adjust}

};

/* List of fingerprints attached. */
static Authentec_Fingerprint *first_dev = NULL;
static int num_devices = 0;
/* used by sane_get_devices */
static const SANE_Device **devlist = NULL;

/*----------------------------------------------------------- */

/* Local functions. */

/* Display a buffer in the log. Display by lines of 16 bytes. */
static void
hexdump (int level, const char *comment, unsigned char *buf, const int length)
{
  int i;
  char line[128];
  char *ptr;
  char asc_buf[17];
  char *asc_ptr;

  DBG (level, "  %s\n", comment);

  i = 0;
  goto start;

  do
    {
      if (i < length)
	{
	  ptr += sprintf (ptr, " %2.2x", *buf);

	  if (*buf >= 32 && *buf <= 127)
	    {
	      asc_ptr += sprintf (asc_ptr, "%c", *buf);
	    }
	  else
	    {
	      asc_ptr += sprintf (asc_ptr, ".");
	    }
	}
      else
	{
	  /* After the length; do nothing. */
	  ptr += sprintf (ptr, "   ");
	}

      i++;
      buf++;

      if ((i % 16) == 0)
	{
	  /* It's a new line */
	  DBG (level, "  %s    %s\n", line, asc_buf);

	start:
	  ptr = line;
	  *ptr = '\0';
	  asc_ptr = asc_buf;
	  *asc_ptr = '\0';

	  ptr += sprintf (ptr, "  %3.3d:", i);
	}
    }
  while (i < ((length + 15) & ~15));
}

/* Returns the length of the longest string, including the terminating
 * character. */
static size_t
max_string_size (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;
}

/* Initialize a fingerprint entry. Return an allocated fingerprint with some
 *  */
static Authentec_Fingerprint *
authentec_init (void)
{
  Authentec_Fingerprint *dev;

  DBG (DBG_proc, "authentec_init: enter\n");

  /* Allocate a new fingerprint entry. */
  dev = malloc (sizeof (Authentec_Fingerprint));
  if (dev == NULL)
    {
      return NULL;
    }
  memset (dev, 0, sizeof (Authentec_Fingerprint));

/* Allocate the windoww buffer*/
  dev->windoww_size = 0x100;
  dev->windoww = malloc (dev->windoww_size);
  if (dev->windoww == NULL)
    {
      free (dev);
      return NULL;
    }

/* Allocate the windowr buffer*/
  dev->windowr_size = 0x100;
  dev->windowr = malloc (dev->windowr_size);
  if (dev->windowr == NULL)
    {
      free (dev);
      free (dev->windoww);
      return NULL;
    }

  dev->fd = -1;

  DBG (DBG_proc, "authentec_init: exit\n");

  return (dev);
}

static SANE_Status
authentec_init_2 (Authentec_Fingerprint * dev)
{
  SANE_Status status;

  DBG (DBG_proc, "authentec_init_2: enter\n");

  /* Allocate the buffer used to transfer the USB data */
  /* Check for max. format image size so buffer size can
   * be adjusted  */
  dev->buffer_size = 192 * 480;	/* for max size, data = 4bit */

  dev->buffer = malloc (dev->buffer_size);

  if (dev->buffer == NULL)
    {
      free (dev);
      free (dev->windowr);
      free (dev->windoww);
      return SANE_STATUS_NO_MEM;
    }

  /* Allocate the output buffer used for conversion */
  dev->output_size = 2 * dev->buffer_size;

  dev->output = malloc (dev->output_size);
  if (dev->output == NULL)
    {
      free (dev);
      free (dev->windowr);
      free (dev->windoww);
      free (dev->buffer);

      return SANE_STATUS_NO_MEM;
    }
  dev->image_size = 2 * dev->buffer_size;

  dev->image = malloc (dev->image_size);
  if (dev->image == NULL)
    {
      free (dev);
      free (dev->windowr);
      free (dev->windoww);
      free (dev->buffer);
      free (dev->output);

      return SANE_STATUS_NO_MEM;
    }

  DBG (DBG_proc, "authentec_init_2: exit\n");
  status = SANE_STATUS_GOOD;
  return status;
}

/* Closes an open fingerprints. */
static void
authentec_close (Authentec_Fingerprint * dev)
{
  DBG (DBG_proc, "authentec_close: enter \n");

  if (dev->fd != -1)
    {

      DBG (DBG_proc, "authentec_close: fd !=-1 \n");
      sanei_usb_close (dev->fd);
      dev->fd = -1;
    }

  DBG (DBG_proc, "authentec_close: exit\n");
}

/* Frees the memory used by a fingerprint. */
static void
authentec_free (Authentec_Fingerprint * dev)
{
  int i;

  DBG (DBG_proc, "authentec_free: enter\n");

  if (dev == NULL)
    return;

  authentec_close (dev);
  if (dev->devicename)
    {
      free (dev->devicename);
    }
  if (dev->buffer)
    {
      free (dev->buffer);
    }
  if (dev->output)
    {
      free (dev->output);
    }
  if (dev->image)
    {
      free (dev->image);
    }
  if (dev->windoww)
    {
      free (dev->windoww);
    }
  if (dev->windowr)
    {
      free (dev->windowr);
    }
  for (i = 1; i < OPT_NUM_OPTIONS; i++)
    {
      if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
	{
	  free (dev->val[i].s);
	}
    }
  if (dev->resolutions_list)
    free (dev->resolutions_list);

  free (dev);

  DBG (DBG_proc, "authentec_free: exit\n");
}

/* Reset fingerprint */
static SANE_Status
authentec_reset_fingerprint (Authentec_Fingerprint * dev)
{
  SANE_Status status;
  size_t sizew;			/* significant size of window */
  size_t sizer;

  DBG (DBG_proc, "authentec_reset_fingerprint: enter\n");

  sizew = dev->windoww_size;
  sizer = dev->windowr_size;

  memset (dev->windoww, 0, sizew);
  memset (dev->windowr, 0, sizer);

  status = SANE_STATUS_GOOD;
  DBG (DBG_proc, "authentec_reset_fingerprint: exit\n");

  return status;
}

/* Inquiry a device and returns TRUE if is supported. */
static int
authentec_identify_fingerprint (Authentec_Fingerprint * dev)
{
  SANE_Status status;
  SANE_Word vendor;
  SANE_Word product;
  int i;

  DBG (DBG_info, "authentec_identify_fingerprint: open\n");

  status = sanei_usb_get_vendor_product (dev->fd, &vendor, &product);

  /* Loop through our list to make sure this scanner is supported. */
  for (i = 0; i < NELEMS (fingerprints); i++)
    {
      if (fingerprints[i].vendor == vendor
	  && fingerprints[i].product == product)
	{

	  DBG (DBG_info,
	       "authentec_identify_fingerprint: fingerprint 0x%x:0x%x is in list\n",
	       vendor, product);

	  dev->hw = &(fingerprints[i]);

	  DBG (DBG_info,
	       "authentec_identify_fingerprint: exit fingerprint supported\n");
	  return SANE_TRUE;
	}
    }
  /* TODO, place some commands to read some ident string from device if supported */
  DBG (DBG_error,
       "authentec_identify_fingerprint: exit this is not a AUTHENTEC exit\n");
  return SANE_FALSE;
}

static SANE_Status
authentec_fingerprint_init (Authentec_Fingerprint * dev)
{
  SANE_Status status;
  size_t sizer;
  size_t sizew;
  size_t size;
  int i;
  int num_entries;
  DBG (DBG_proc, "authentec_fingerprint_init: open\n");

  sizew = dev->windoww_size;
  sizer = dev->windowr_size;

  memset (dev->windoww, 0, sizew);
  memset (dev->windowr, 0, sizer);

  usleep (500000);

  switch (dev->hw->authentecref)
    {
    case Authentec_AES3400:
      /* urb biopod  string  */
      sizew = sizeof (urb_init_biopod);
      memcpy (dev->windoww, urb_init_biopod, sizew);
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb aes3400 error\n");
	  /* return SANE_FALSE; */
	}
      hexdump (DBG_info2, "urb 3400 biopod write urb_init_biopod",
	       dev->windoww, sizew);
      break;

    case Authentec_AES3500_X50:
    case Authentec_AES3500:

      switch (dev->val[OPT_RESOLUTION].w)
	{
	case 128:
/* urb biopod  string  aes3500 */
	  sizew = sizeof (urb_init_biopod);
	  memcpy (dev->windoww, urb_init_biopod, sizew);
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc,
		   "authentec_fingerprint_init: urb biopod error\n");
	      /* return SANE_FALSE; */
	    }
	  hexdump (DBG_info2, "urb biopod write 0x7c bytes", dev->windoww,
		   sizew);
	  break;

	case 96:
/* urb udrv string  aes4000*/
	  sizew = sizeof (urb_init_udrv);
	  memcpy (dev->windoww, urb_init_udrv, sizew);
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc,
		   "authentec_fingerprint_init: urb init udrv error\n");
	      /* return SANE_FALSE; */
	    }
	  hexdump (DBG_info2, "urb init udrv write 0x06 bytes", dev->windoww,
		   sizew);
	  break;
	}
      break;

    case Authentec_AES2501:

      /* urb x1   80 01 81 02   */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;	/* master reset */
      dev->windoww[2] = 0x81;
      dev->windoww[3] = 0x02;	/* Read registers */
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 7 error\n");
	  /* return SANE_FALSE; */
	}
      hexdump (DBG_info2, "urb 7 write 4 bytes", dev->windoww, sizew);

      /* urb 5 x2   read register values */
      sizer = 126;
      status = sanei_usb_read_bulk (dev->fd, dev->windowr, &sizer);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 5 error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb 5 after 7 read 126 bytes?", dev->windowr,
	       sizer);

      usleep (20000);
      /* urb 8   b0 27   */
      sizew = 0x02;
      dev->windoww[0] = 0xb0;
      dev->windoww[1] = 0x27;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 8 error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb 8 write 2 bytes", dev->windoww, sizew);

      /* urb xx1   80 01 81 02   */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;	/* master reset */
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;	/* register update */
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 7 error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb 7 write 4 bytes", dev->windoww, sizew);

      usleep (2000);

      /* urb 9   80 01 81 02   */
         /* sizew = 0x04;
         dev->windoww[0] = 0x82;
         dev->windoww[1] = 0x01; *//* read Idn 0x10 */
      /* dev->windoww[2] = 0x80; 
         dev->windoww[3] = 0x40; *//* register update */
      /*status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
         if (status != SANE_STATUS_GOOD)
         {
         DBG (DBG_proc, "authentec_fingerprint_init: urb 8 error\n");
         return SANE_FALSE;
         }      
         hexdump (DBG_info2, "urb 9 write 4 bytes", dev->windoww, sizew);
         usleep (2000); */
      num_entries = 11;
      for (i = 0; i < num_entries; i++)
	{
	  /* urb 10   ff 00   */
	  sizew = 0x02;
	  dev->windoww[0] = 0xff;
	  dev->windoww[1] = 0x00;
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc, "authentec_fingerprint_init: urb 10 error\n");
	      return SANE_FALSE;
	    }

	  DBG (DBG_proc, "authentec_fingerprint_init: loop=%durb 10\n", i);
	  usleep (200);
	}

      /* urb aes2501  string  */
      sizew = sizeof (urb_1810_aes2501);
      memcpy (dev->windoww, urb_1810_aes2501, sizew);
      usleep (1000);
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc,
	       "authentec_fingerprint_init: urb 1810 aes2501 error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb aes2501 write 0x2a bytes", dev->windoww,
	       sizew);
      usleep (1000);
      /* urb xx  read bulk, register values? */
      size = 20;

      status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);

      if (status != SANE_STATUS_GOOD)
	{
	  return status;
	}

      hexdump (DBG_info2, "urb read register values???", dev->buffer, size);
      usleep (1000);

      /* urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);

      usleep (200);

      sizew = sizeof (urb_28_aes2501);
      memcpy (dev->windoww, urb_28_aes2501, sizew);
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc,
	       "authentec_fingerprint_init: urb 28 aes2501 error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb aes2501 write 0x0c bytes", dev->windoww,
	       sizew);

      /* urb xx  read bulk, register values? */
      size = 126;

      status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);

      if (status != SANE_STATUS_GOOD)
	{
	  return status;
	}
  hexdump (DBG_info2, "urb read register values???", dev->buffer, size);
  while (1)
    {
    /* dbg(3, "reg 0xaf = 0x%x\n", rbuf[0].buf[0x5f]);
	if (rbuf[0].buf[0x5f] != 0x6b) */

      DBG (DBG_proc, "reg 0xaf = 0x%x\n", dev->buffer[0x0]);
      if (dev->buffer[0] != 0x6b)
	break;
      usleep (1000);
      /* urb 10   ff 00   */
      sizew = 0x02;
      dev->windoww[0] = 0xff;
      dev->windoww[1] = 0x00;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb 1x\n");
      usleep (200);

      sizew = sizeof (urb_28_aes2501);
      memcpy (dev->windoww, urb_28_aes2501, sizew);
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc,
	       "authentec_fingerprint_init: urb 28 aes2501 error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb aes2501 write 0x0c bytes", dev->windoww,
	       sizew);

      /* urb xx  read bulk, register values? */
      size = 126;

      status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);

      if (status != SANE_STATUS_GOOD)
	{
	  return status;
	}
      hexdump (DBG_info2, "urb read register values???", dev->buffer, size);
    }
  sizew = sizeof (urb_31_aes2501);
  memcpy (dev->windoww, urb_31_aes2501, sizew);
  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_proc, "authentec_fingerprint_init: urb 31 aes2501 error\n");
      return SANE_FALSE;
    }
  hexdump (DBG_info2, "urb aes2501 write 0x0e bytes", dev->windoww, sizew);
  
  status = SANE_STATUS_GOOD;
  
  break;

default:
  sizer = 0x6c0;		/* real lengte 0x14? */
  status = sanei_usb_read_bulk (dev->fd, dev->windowr, &sizer);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_proc, "authentec_fingerprint_init: second urb 34 error\n");
      /* return SANE_FALSE; */
    }
  hexdump (DBG_info2, "urb 34 read 0x6c0 or 0x14 bytes", dev->windowr, sizer);
  usleep (100000);
  /* urb 35  ac 01 ad 1a 81 02  */
  sizew = 0x06;
  dev->windoww[0] = 0xac;
  dev->windoww[1] = 0x01;
  dev->windoww[2] = 0xad;
  dev->windoww[3] = 0x1a;
  dev->windoww[4] = 0x81;
  dev->windoww[5] = 0x02;
  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_proc, "authentec_fingerprint_init: urb 35 error\n");
      /* return SANE_FALSE; */
    }
  hexdump (DBG_info2, "urb 35 write 6 bytes", dev->windoww, sizew);
  usleep (100000);
  /* urb 33 back length 2 9a 00 */

  /*urb 36 control message?  00 03 01 00 00 00 00 00 */
  sizew = 0x00;			/* was 0 ? */
  status =
    sanei_usb_control_msg (dev->fd, 0x00, 0x03, 0x0100, 0, sizew,
			   dev->windoww);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_proc, "authentec_fingerprint_init: urb 36 error\n");
      /*return status; */
    }
  DBG (DBG_proc, "authentec_fingerprint_init: urb36 end\n");
  usleep (10000);
  /* urb 34 back */
  /*urb 37 control message?  00 01 01 00 00 00 00 00 */
  sizew = 0x00;			/* was 0 ? */
  status =
    sanei_usb_control_msg (dev->fd, 0x00, 0x01, 0x0100, 0, sizew,
			   dev->windoww);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_proc, "authentec_fingerprint_init: urb 37 error\n");
      /* return status; */
    }
  DBG (DBG_proc, "authentec_fingerprint_init: urb37 end\n");


  status = SANE_STATUS_GOOD;

  if (status)
    {
      DBG (DBG_error, "authentec_fingerprint_init failed : %s\n",
	   sane_strstatus (status));
      return status;
    }
  DBG (DBG_proc, "authentec_fingerprint_init: exit\n");

  }
  return status;
}

/* Attach a fingerprint to this backend. */
static SANE_Status
attach_fingerprint (SANE_String_Const devicename,
		    Authentec_Fingerprint ** devp)
{
  Authentec_Fingerprint *dev;
  int fd;
  SANE_Status status;

  DBG (DBG_proc, "attach_fingerprint: %s\n", devicename);

  if (devp)
    *devp = NULL;

  /* Check if we know this device name. */
  for (dev = first_dev; dev; dev = dev->next)
    {
      if (strcmp (dev->sane.name, devicename) == 0)
	{
	  if (devp)
	    {
	      *devp = dev;
	    }
	  DBG (DBG_info, "device is already known\n");
	  return SANE_STATUS_GOOD;
	}
    }

  /* Allocate a new fingerprint entry. */
  dev = authentec_init ();
  if (dev == NULL)
    {
      DBG (DBG_error, "authentec_init ERROR: not enough memory\n");
      return SANE_STATUS_NO_MEM;
    }

  DBG (DBG_info, "attach_fingerprint: opening USB device %s\n", devicename);

  if (sanei_usb_open (devicename, &fd) != 0)
    {
      DBG (DBG_error, "ERROR: attach_fingerprint: open failed\n");
      authentec_free (dev);
      return SANE_STATUS_INVAL;
    }
  /* Fill some scanner specific values. */
  dev->devicename = strdup (devicename);
  dev->fd = fd;

  /* Now, check that it is a fingerprint we support. */

  if (authentec_identify_fingerprint (dev) == SANE_FALSE)
    {
      DBG (DBG_error,
	   "ERROR: attach_fingerprint: fingerprint-identification failed\n");
      authentec_free (dev);
      return SANE_STATUS_INVAL;
    }

  /* Allocate a buffer memory. */
  status = authentec_init_2 (dev);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_error, "authentec_initi_2, ERROR: not enough memory\n");
      return SANE_STATUS_NO_MEM;
    }

  authentec_close (dev);

  DBG (DBG_info, "attach_fingerprint: opening USB device %s\n", devicename);

  /* Build list of fingerprint supported resolutions. */
  DBG (DBG_proc, "attach_fingerprint: build resolution list\n");

  if (dev->hw->color_adjust[0].resolution_x != 0)
    {
      int num_entries;
      int i;
      num_entries = 0;

      while (dev->hw->color_adjust[num_entries].resolution_x != 0)
	num_entries++;

      dev->resolutions_list = malloc (sizeof (SANE_Word) * (num_entries + 1));

      if (dev->resolutions_list == NULL)
	{
	  DBG (DBG_error,
	       "ERROR: attach_fingerprint: fingerprint resolution list failed\n");
	  authentec_free (dev);
	  return SANE_STATUS_NO_MEM;
	}
      dev->resolutions_list[0] = num_entries;
      DBG (DBG_proc, "attach_fingerprint: make color resolution table \n");
      for (i = 0; i < num_entries; i++)
	{
	  dev->resolutions_list[i + 1] =
	    dev->hw->color_adjust[i].resolution_x;
	}
    }
  else
    {
      dev->resolutions_list = NULL;
    }

  /* Set the default options for that fingerprint. */
  dev->sane.name = dev->devicename;
  dev->sane.vendor = dev->hw->vendor_name;
  dev->sane.model = dev->hw->product_name;
  dev->sane.type = SANE_I18N ("fingerprint sensor");

  /* Link the fingerprint with the others. */
  dev->next = first_dev;
  first_dev = dev;

  if (devp)
    {
      *devp = dev;
    }

  num_devices++;

  DBG (DBG_proc, "attach_fingerprint: exit\n");

  return SANE_STATUS_GOOD;
}

static SANE_Status
attach_one (const char *dev)
{
  DBG (DBG_proc, "attach_one: open \n");
  attach_fingerprint (dev, NULL);
  DBG (DBG_proc, "attach_one: exit \n");
  return SANE_STATUS_GOOD;
}

/* Reset the options for that fingerprint. */
static void
authentec_init_options (Authentec_Fingerprint * dev)
{
  int i;

  DBG (DBG_proc, "authentec_init_options: open\n");

  /* Pre-initialize the options. */
  memset (dev->opt, 0, sizeof (dev->opt));
  memset (dev->val, 0, sizeof (dev->val));

  for (i = 0; i < OPT_NUM_OPTIONS; ++i)
    {
      dev->opt[i].size = sizeof (SANE_Word);
      dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
    }
  DBG (DBG_proc,
       "authentec_init_options: done loop opt_num_options=%d, i=%d \n",
       OPT_NUM_OPTIONS, i);
  /* Number of options. */
  dev->opt[OPT_NUM_OPTS].name = "";
  dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
  dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
  dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
  dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
  dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;

  /* Mode group */
  dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
  dev->opt[OPT_MODE_GROUP].desc = "";	/* not valid for a group */
  dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
  dev->opt[OPT_MODE_GROUP].cap = 0;
  dev->opt[OPT_MODE_GROUP].size = 0;
  dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

  /* Fingerprint supported modes */
  dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
  dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
  dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
  dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
  dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
  dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
  dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
  dev->val[OPT_MODE].s = (SANE_Char *) strdup ("");	/* will be set later */

  /* X and Y resolution */
  dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
  dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
  dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
  dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
  dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
  dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
  dev->val[OPT_RESOLUTION].w = dev->resolutions_list[2];

  /* brightness   */
  dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
  dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
  dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
  dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
  dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
  dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
  dev->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
  dev->val[OPT_BRIGHTNESS].w = 0;	/* to get middle value */

  /* Enhancement group */
  dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
  dev->opt[OPT_ENHANCEMENT_GROUP].desc = "";	/* not valid for a group */
  dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
  dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
  dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
  dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

  /* red level calibration manual correction */
  dev->opt[OPT_WHITE_LEVEL_R].name = SANE_NAME_WHITE_LEVEL_R;
  dev->opt[OPT_WHITE_LEVEL_R].title = SANE_TITLE_WHITE_LEVEL_R;
  dev->opt[OPT_WHITE_LEVEL_R].desc = SANE_DESC_WHITE_LEVEL_R;
  dev->opt[OPT_WHITE_LEVEL_R].type = SANE_TYPE_INT;
  dev->opt[OPT_WHITE_LEVEL_R].unit = SANE_UNIT_NONE;
  dev->opt[OPT_WHITE_LEVEL_R].constraint_type = SANE_CONSTRAINT_RANGE;
  dev->opt[OPT_WHITE_LEVEL_R].constraint.range = &red_level_range;
  dev->val[OPT_WHITE_LEVEL_R].w = 00;	/* to get middle value */

  /* white level calibration manual correction */
  dev->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
  dev->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
  dev->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL;
  dev->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
  dev->opt[OPT_WHITE_LEVEL].unit = SANE_UNIT_NONE;
  dev->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
  dev->opt[OPT_WHITE_LEVEL].constraint.range = &white_level_range;
  dev->val[OPT_WHITE_LEVEL].w = 00;	/* to get middle value */

  DBG (DBG_proc, "authentec_init_options: after white level\n");

  /* Lastly, set the default scan mode. This might change some
   * values previously set here. */

  sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
		       (SANE_String_Const *) scan_mode_list[0], NULL);
  DBG (DBG_proc, "authentec_init_options: exit\n");
}

/* Read the image from the fingerprint and fill the temporary buffer with it. */
static SANE_Status
authentec_fill_image (Authentec_Fingerprint * dev)
{
  SANE_Status status;
  size_t size;
  size_t bulk_size_read;
  size_t sizew;
  int i = 0;
  size_t f;
  size_t size_short;
  int sum;

  assert (dev->image_begin == dev->image_end);
  assert (dev->real_bytes_left > 0);

  DBG (DBG_proc, "authentec_fill_image: enter\n");

  DBG (DBG_proc, "authentec_fill_image: real dev bytes left=0x%lx \n",
       (unsigned long) (size_t) dev->real_bytes_left);
  bulk_size_read = dev->real_bytes_left;

  switch (dev->hw->authentecref)
    {
    case Authentec_AES3500_X50:
    case Authentec_AES3500:

      switch (dev->val[OPT_RESOLUTION].w)
	{
	case 128:
/* urb biopod  string  aes3500 */
	  sizew = sizeof (urb_init_biopod);
	  memcpy (dev->windoww, urb_init_biopod, sizew);
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc,
		   "authentec_fingerprint_init: urb biopod error\n");
	      /* return SANE_FALSE; */
	    }
	  hexdump (DBG_info2, "urb biopod write 0x7c bytes", dev->windoww,
		   sizew);
	  break;

	case 96:
/* urb udrv string  aes4000*/
	  sizew = sizeof (urb_init_udrv);
	  memcpy (dev->windoww, urb_init_udrv, sizew);
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc,
		   "authentec_fingerprint_init: urb biopod error\n");
	      /* return SANE_FALSE; */
	    }
	  hexdump (DBG_info2, "urb biopod write 0x7c bytes", dev->windoww,
		   sizew);

	  break;
	}

    case Authentec_AES2501:
	       /*-------------start scan part --------------------*/

      DBG (DBG_proc, "authentec_fingerprint_init: start scan\n");
/*	SEND(2), 0xb0,0x27,
 		SEND(2), 0xff,0x00,  */
      sizew = 0x02;
      dev->windoww[0] = 0xb0;
      dev->windoww[1] = 0x27;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb 1x\n");
      usleep (200);

/*	SEND(4), 0x80,0x01,0x82,0x40,  urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
      usleep (200);

      usleep (200);

/*	SEND(2), 0xff,0x00,  */
      sizew = 0x02;
      dev->windoww[0] = 0xff;
      dev->windoww[1] = 0x00;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb 1x\n");
      usleep (200);

      /*      SEND(4), 0x80,0x01,0x82,0x40,  urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
      usleep (200);

/*	SEND(4), 0x80,0x01,0x82,0x40,  urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
      usleep (200);

/*	SEND(4), 0x80,0x01,0x82,0x40,  urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
      usleep (200);

/*	SEND(4), 0x80,0x01,0x82,0x40, urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
      usleep (200);


/*	SEND(4), 0x80,0x01,0x82,0x40,  urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  return SANE_FALSE;
	}
      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);

      usleep (200);

/*	SEND(2), 0x81,0x02,  */
      sizew = 0x02;
      dev->windoww[0] = 0x81;
      dev->windoww[1] = 0x02;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb 1x\n");
      usleep (200);


/*	SEND(2), 0x81,0x02,  */
      sizew = 0x02;
      dev->windoww[0] = 0x81;
      dev->windoww[1] = 0x02;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb 1x\n");
      usleep (200);


/*	SEND(2), 0x81,0x02,  */
      sizew = 0x02;
      dev->windoww[0] = 0x81;
      dev->windoww[1] = 0x02;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb 1x\n");
      usleep (200);

/*	RECV(126),  urb xx  read bulk, register values? */
      size = 126;
      status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);
      if (status != SANE_STATUS_GOOD)
	{
	  return status;
	}
      hexdump (DBG_info2, "urb read register values???", dev->buffer, size);
/*-----------------------------------------------*/
while (1) {
	    printf("READY (touch the sensor to stop)\n");
	  /*  while (!aesDetectFinger(aes))
	   *
	   *
	   *{} */

	   usleep (2000000);
while (1) {
/*	SEND(4), 0x80,0x01,0x82,0x40  */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb 1x error\n");
	  return SANE_FALSE;
	}
      DBG (DBG_proc, "authentec_fingerprint_init: urb x\n");
      usleep (30000);


sizew = sizeof (urb_outbufwaitfordata2_aes2501);
  memcpy (dev->windoww,urb_outbufwaitfordata2_aes2501, sizew);
  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (DBG_proc, "authentec_fingerprint_init:urb_outbufwaitfordata2_aes2501[0x2a] error\n");
      return SANE_FALSE;
    }
  hexdump (DBG_info2, "urb urb_outbufwaitfordata2_aes2501[0x2a]", dev->windoww, sizew);
 



/*	RECV(20),  urb xx  read bulk, register values? */
      size = 20;
      status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);
      if (status != SANE_STATUS_GOOD)
	{
	  return status;
	}
      hexdump (DBG_info2, "urb read register values 20", dev->buffer, size);

/* One column is returned here but I don't know which one
      Maybe an average over the whole sensor area. */
    sum = 0;
    for (i = 1; i != 9; i++) {
	sum += (dev->buffer[i] & 0xf) + (dev->buffer[i] >> 4);
    }
    DBG(DBG_info2, "sum = %u\n", sum);
    /* return (sum > aes->fingerTh1); */
    if (sum > 50) {
    status = SANE_STATUS_GOOD;
    /* return status; */
    return status;
    }
   }

/*-----------------------------------------------*/





  status = SANE_STATUS_GOOD;


	    printf("Scanning...\n");
	   /* nstrips = aesReadFingerprint(aes, raw, maxstrip); */

	    printf("Assembling...\n");
	   /* height = assemble(raw, cooked, nstrips, 1); */
	  /*  if (height < 100) { */
                /* It was a "touch", not a finger scan. */
            /*    break; */
          /*  }
	    toPNM(cooked, swidth, height, fp_pnm);
	    height = assemble(raw, cooked, nstrips, 0);
	    toPNM(cooked, swidth, height, fporig_pnm);

	    system(displayCmd); */
	    printf("\n");



}


/*-----------------------------------------------------------------*/
      break;
    default:
      usleep (100000);
      /* urb xx  80 01 82  40  ,reset, start */
      sizew = 0x04;
      dev->windoww[0] = 0x80;
      dev->windoww[1] = 0x01;
      dev->windoww[2] = 0x82;
      dev->windoww[3] = 0x40;
      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	  /* return SANE_FALSE; */
	}

      hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
    }

  usleep (100000);

  while (dev->real_bytes_left)
    {
      /* Try to read the maximum number of bytes. */
      DBG (DBG_proc,
	   "authentec_fill_image: real dev bytes left, while loop=0x%lx \n",
	   (unsigned long) (size_t)dev->real_bytes_left);
      switch (dev->hw->authentecref)
	{
	case Authentec_AES3400:
	  /* urb biopod  string  */
	  sizew = sizeof (urb_init_biopod);
	  memcpy (dev->windoww, urb_init_biopod, sizew);
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc,
		   "authentec_fingerprint_init: urb biopod error\n");
	      /* return SANE_FALSE; */
	    }
	  hexdump (DBG_info2, "urb biopod write 0x7c bytes", dev->windoww,
		   sizew);

	  break;

	case Authentec_AES3500_X50:
	case Authentec_AES3500:

	  switch (dev->val[OPT_RESOLUTION].w)
	    {
	    case 128:
/* urb biopod  string  aes3500 */
	      sizew = sizeof (urb_init_biopod);
	      memcpy (dev->windoww, urb_init_biopod, sizew);
	      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	      if (status != SANE_STATUS_GOOD)
		{
		  DBG (DBG_proc,
		       "authentec_fingerprint_init: urb biopod error\n");
		  /* return SANE_FALSE; */
		}
	      hexdump (DBG_info2, "urb biopod write 0x7c bytes",
		       dev->windoww, sizew);
	      break;

	    case 96:
/* urb udrv string  aes4000*/
	      f = 0x0c;
	      sizew = sizeof (urb_init_write_udrv);
	      memcpy (dev->windoww, urb_init_write_udrv, sizew);
	      dev->windoww[0x05] = f;
	      if (f == 0x0c)
		f = 0x04;
	      else
		f = 0x0c;

	      status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	      if (status != SANE_STATUS_GOOD)
		{
		  DBG (DBG_proc,
		       "authentec_fingerprint_init: urb init udrv error\n");
		  /* return SANE_FALSE; */
		}
	      hexdump (DBG_info2, "urb init udrv", dev->windoww, sizew);

	      break;
	    }
	  break;

	case Authentec_AES2501:
	  if (size > 0x226c0)
	    {
	      /* dpi size of device,
	       * data is read with each bulk read to get a full size image? */
	      size = 0x226c0;
	    }
	  break;
	default:

	  /* urb xx  80 01 82  40  ,reset, start urb 1811 */
	  sizew = 0x04;
	  dev->windoww[0] = 0x80;
	  dev->windoww[1] = 0x01;
	  dev->windoww[2] = 0x82;
	  dev->windoww[3] = 0x40;
	  status = sanei_usb_write_bulk (dev->fd, dev->windoww, &sizew);
	  if (status != SANE_STATUS_GOOD)
	    {
	      DBG (DBG_proc, "authentec_fingerprint_init: urb xx error\n");
	      /* return SANE_FALSE; */
	    }
	  hexdump (DBG_info2, "urb xx write 4 bytes", dev->windoww, sizew);
	  if (size > (192 * 16))
	    {
	      size = (192 * 16);	/* dpi size of device, so data is read with each bulk read to get a full size image? */
	    }

	}

      size = dev->real_bytes_left;
      if (size < bulk_size_read)
	{
	  size = bulk_size_read;	/* it seems size can not be smaller then read by bulk */
	}
      if (size == 0)
	{
	  /* Probably reached the end of the buffer. Check, just in case. */
	  assert (dev->image_end != 0);
	  return (SANE_STATUS_GOOD);
	}

      /* Do the transfer */

      DBG (DBG_proc,
	   "authentec_fill_image: dev->real_bytes_left: 0x%lx size: 0x%lx\n",
	   (unsigned long) (size_t) dev->real_bytes_left, (unsigned long) (size_t) size);
      usleep (100000);

      /* urb xx first read bulk  urb 1812 */
      status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);

      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_info,
	       "authentec_fill_image: error size (read) = 0x%lx bytes (bpl=0x%lx)\n",
	       (long) size, (long) dev->params.bytes_per_line);

	  return status;
	}
      size_short = 0x40;
      hexdump (DBG_info2, "urb read bulk data", dev->buffer, size_short);
      DBG (DBG_info,
	   "authentec_fill_image: size (read) = 0x%lx bytes (bpl=0x%lx)\n",
	   (long) size, (long) dev->params.bytes_per_line);

      memcpy (dev->image + dev->image_end, dev->buffer, size);

      dev->image_end += size;
      bulk_size_read = size;
      if (dev->real_bytes_left > size)
	dev->real_bytes_left -= size;
      else if (dev->real_bytes_left <= size)	/* last loop */
	dev->real_bytes_left = 0;
      DBG (DBG_info, "authentec_fill_image: real bytes left = 0x%lx\n",
	   (unsigned long) (size_t) dev->real_bytes_left);
    }

  DBG (DBG_proc, "authentec_fill_image: exit\n");
  return (SANE_STATUS_GOOD);	/* unreachable */
}

/**********************************************************************
*
* The add_text routine and font_6x11.h file are taken from the (GPLed) 
* webcam.c file, part of xawtv,   (c) 1998-2002 Gerd Knorr.
* add_text was slightly modified for the pencam2 program.
* authentec_add_text was taken from the pencam2 program and changed on
* some points
*
*********************************************************************/

#define MSG_MAXLEN   45
#define CHAR_HEIGHT  11
#define CHAR_WIDTH   6
#define CHAR_START   4

static SANE_Status
authentec_add_text (SANE_Byte * image, int width, int height, char *txt)
{
  SANE_Status status;
  time_t t;
  struct tm *tm;
  char line[MSG_MAXLEN + 1];
  SANE_Byte *ptr;
  int i, x, y, f, len;
  char fmtstring[25] = " %Y-%m-%d %H:%M:%S";
  char fmttxt[46];

  DBG (DBG_proc, "authentec_add_text: enter\n");
  time (&t);
  tm = localtime (&t);
  if (strlen (txt) > (MSG_MAXLEN - 23))
    strncpy (fmttxt, txt, (MSG_MAXLEN - 23));
  else
    strcpy (fmttxt, txt);
  strcat (fmttxt, fmtstring);

  len = strftime (line, MSG_MAXLEN, fmttxt, tm);

  for (y = 0; y < CHAR_HEIGHT; y++)
    {
      ptr = image + width * (height - CHAR_HEIGHT - 2 + y) + 12;

      for (x = 0; x < len; x++)
	{
	  f = fontdata[line[x] * CHAR_HEIGHT + y];
	  for (i = CHAR_WIDTH - 1; i >= 0; i--)
	    {
	      if (f & (CHAR_START << i))
		{
		  ptr[0] = 255;
		  /* ptr[1] = 255;
		   * ptr[2] = 255; */
		}
	      ptr += 1;
	    }			/* for i */
	}			/* for x */
    }				/* for y */

  DBG (DBG_proc, "authentec_add_text: exit vw=%d, vh=%d\n", width, height);
  status = (SANE_STATUS_GOOD);
  return status;

}				/*  end of add_text  */

/* **************************  Video Decoding  *********************  */

static SANE_Status
authentec_unshuffle (Authentec_Fingerprint * dev, SANE_Byte * buf,
		     size_t * size)
{
  SANE_Status status;
  int x;
  int i = 0;
  int vw = dev->x_resolution;
  int vh = dev->y_resolution;
  int bright_red;
  int count;

  DBG (DBG_proc, "authentec_unshuffle: enter\n");
/* subroutine for conversion 2x 4bit to 2x 8bit data */
  count = (vw * vh) / 2;

  for (i = 0; i < count; i++)
    {
      x = i * 2;
      dev->output[x] = (dev->image[i] & 0x0f) << 2;
      x++;
      dev->output[x] = (dev->image[i] & 0xf0) >> 2;
    }

  /* memcpy (dev->output, dev->image, size); */

  /* brightness adjustment */

  count = vw * vh;

  bright_red = (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_R].w);

  for (x = 0; x < count; x++)
    {
      if ((*(dev->output + x) + bright_red) >= 255)
	*(buf + x) = 255;

      else if ((*(dev->output + x) + bright_red) <= 0)
	*(buf + x) = 0;
      else
	*(buf + x) = (*(dev->output + x) + bright_red);
    }

  if (dev->scan_mode == AUTHENTEC_GRAYSCALE_TEXT)
    {
/* insert add text routine                 */
/**********************************************************************
*
* The add_text routine and font_6x11.h file are taken from the (GPLed) 
* webcam.c file, part of xawtv,   (c) 1998-2002 Gerd Knorr.
* add_text was slightly modified for this program.
*
*********************************************************************/
      strcpy (dev->picmsg_ps, "Authentec");

      status = authentec_add_text (buf, vw, vh, dev->picmsg_ps);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_info, "authentec_unshuffle status NOK\n");
	  return (status);
	}
    }

  /* end of add text routine                 */
  DBG (DBG_proc, "authentec_unshuffle: exit vw=%d, vh=%d\n", vw, vh);
  status = (SANE_STATUS_GOOD);
  return status;
}

/* Sane entry points */

SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
  FILE *fp;
  char line[PATH_MAX];
  size_t len;

  num_devices = 0;
  devlist = NULL;
  first_dev = NULL;

  DBG_INIT ();

  DBG (DBG_sane_init, "sane_init\n");

  authorize = authorize;	/* silence gcc */

  DBG (DBG_error, "This is sane-authentec version %d.%d-%d\n", V_MAJOR,
       V_MINOR, BUILD);
  DBG (DBG_error, "(C) 2006-2006 by Gerard Klaver\n");

  if (version_code)
    {
      *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
    }

  DBG (DBG_proc, "sane_init: authorize %s null\n", authorize ? "!=" : "==");

  sanei_usb_init ();

  fp = sanei_config_open (AUTHENTEC_CONFIG_FILE);
  if (!fp)
    {
      /* No default fingerprint? */
      DBG (DBG_warning, "configuration file not found (%s)\n",
	   AUTHENTEC_CONFIG_FILE);

      return SANE_STATUS_GOOD;
    }

  while (sanei_config_read (line, sizeof (line), fp))
    {
      SANE_Word vendor;
      SANE_Word product;

      if (line[0] == '#')	/* ignore line comments */
	continue;
      len = strlen (line);

      if (!len)
	continue;		/* ignore empty lines */
      if (sscanf (line, "usb %i %i", &vendor, &product) == 2)
	{

	  sanei_usb_attach_matching_devices (line, attach_one);
	}
      else
	{
	  /* Garbage. Ignore. */
	  DBG (DBG_warning, "bad configuration line: \"%s\" - ignoring.\n",
	       line);
	}
    }

  fclose (fp);

  DBG (DBG_proc, "sane_init: leave\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
  Authentec_Fingerprint *dev;
  int i;

  DBG (DBG_proc, "sane_get_devices: enter\n");

  local_only = local_only;	/* silence gcc */

  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;

  DBG (DBG_proc, "sane_get_devices: exit\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
{
  Authentec_Fingerprint *dev;
  SANE_Status status;

  DBG (DBG_proc, "sane_open: enter\n");

  /* search for devicename */
  if (devicename[0])
    {
      DBG (DBG_info, "sane_open: devicename=%s\n", devicename);

      for (dev = first_dev; dev; dev = dev->next)
	{
	  if (strcmp (dev->sane.name, devicename) == 0)
	    {
	      break;
	    }
	}

      if (!dev)
	{
	  status = attach_fingerprint (devicename, &dev);
	  if (status != SANE_STATUS_GOOD)
	    {
	      return status;
	    }
	}
    }
  else
    {
      DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
      dev = first_dev;		/* empty devicename -> use first device */
    }

  if (!dev)
    {
      DBG (DBG_error, "No fingerprint found\n");

      return SANE_STATUS_INVAL;
    }

  authentec_init_options (dev);

  *handle = dev;

  DBG (DBG_proc, "sane_open: exit\n");

  return SANE_STATUS_GOOD;
}

const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
{
  Authentec_Fingerprint *dev = handle;

  DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);

  if ((unsigned) option >= OPT_NUM_OPTIONS)
    {
      return NULL;
    }

  DBG (DBG_proc, "sane_get_option_descriptor: exit\n");

  return dev->opt + option;
}

SANE_Status
sane_control_option (SANE_Handle handle, SANE_Int option,
		     SANE_Action action, void *val, SANE_Int * info)
{
  Authentec_Fingerprint *dev = handle;
  SANE_Status status;
  SANE_Word cap;

  DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
       option, action);

  if (info)
    {
      *info = 0;
    }

  if (dev->scanning)
    {
      return SANE_STATUS_DEVICE_BUSY;
    }

  if (option < 0 || option >= OPT_NUM_OPTIONS)
    {
      return SANE_STATUS_INVAL;
    }

  cap = dev->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_NUM_OPTS:
	case OPT_RESOLUTION:
	case OPT_BRIGHTNESS:
	case OPT_WHITE_LEVEL_R:
	case OPT_WHITE_LEVEL:
	  *(SANE_Word *) val = dev->val[option].w;
	  return SANE_STATUS_GOOD;
	case OPT_MODE:
	  strcpy (val, dev->val[option].s);
	  return SANE_STATUS_GOOD;
	default:
	  return SANE_STATUS_INVAL;
	}
    }
  else if (action == SANE_ACTION_SET_VALUE)
    {

      if (!SANE_OPTION_IS_SETTABLE (cap))
	{
	  DBG (DBG_error, "could not set option, not settable\n");
	  return SANE_STATUS_INVAL;
	}

      status = sanei_constrain_value (dev->opt + option, val, info);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_error, "could not set option, invalid value\n");
	  return status;
	}

      switch (option)
	{

	  /* Numeric side-effect options */
	case OPT_RESOLUTION:
	case OPT_BRIGHTNESS:
	case OPT_WHITE_LEVEL_R:
	case OPT_WHITE_LEVEL:
	  if (info)
	    {
	      *info |= SANE_INFO_RELOAD_PARAMS;
	    }
	  dev->val[option].w = *(SANE_Word *) val;
	  return SANE_STATUS_GOOD;

	  /* String side-effect options */
	case OPT_MODE:
	  if (strcmp (dev->val[option].s, val) == 0)
	    return SANE_STATUS_GOOD;

	  free (dev->val[OPT_MODE].s);
	  dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);

	  dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
	  dev->opt[OPT_WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE;

	  if (strcmp (dev->val[OPT_MODE].s, GRAYSCALE_RAW_STR) == 0)
	    {
	      dev->scan_mode = AUTHENTEC_GRAYSCALE_RAW;
	    }
	  else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)
		   == 0)
	    {
	      dev->scan_mode = AUTHENTEC_GRAYSCALE;

	    }
	  else if (strcmp (dev->val[OPT_MODE].s, GRAYSCALE_TEXT_STR) == 0)
	    {
	      dev->scan_mode = AUTHENTEC_GRAYSCALE_TEXT;

	    }

	  /* The AUTHENTEC supports only a handful of resolution. */
	  /* This the default resolution range for the AUTHENTEC */

	  dev->depth = 8;
	  if (dev->resolutions_list != NULL)
	    {
	      int i;

	      dev->opt[OPT_RESOLUTION].constraint_type =
		SANE_CONSTRAINT_WORD_LIST;
	      dev->opt[OPT_RESOLUTION].constraint.word_list =
		dev->resolutions_list;

	      /* If the resolution isn't in the list, set a default. */
	      for (i = 1; i <= dev->resolutions_list[0]; i++)
		{
		  if (dev->resolutions_list[i] >= dev->val[OPT_RESOLUTION].w)
		    break;
		}
	      if (i > dev->resolutions_list[0])
		{
		  /* Too big. Take lowest. */
		  dev->val[OPT_RESOLUTION].w = dev->resolutions_list[1];
		}
	      else
		{
		  /* Take immediate superioir value. */
		  dev->val[OPT_RESOLUTION].w = dev->resolutions_list[i];
		}
	    }

	  /* String side-effect options */

	  if (info)
	    {
	      *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
	    }
	  return SANE_STATUS_GOOD;
	default:
	  return SANE_STATUS_INVAL;
	}
    }

  DBG (DBG_proc, "sane_control_option: exit, bad\n");

  return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
{
  Authentec_Fingerprint *dev = handle;
  int i;

  DBG (DBG_proc, "sane_get_parameters: enter\n");

  if (!(dev->scanning))
    {
      dev->x_resolution = dev->val[OPT_RESOLUTION].w;
      /* Prepare the parameters for the caller. */
      memset (&dev->params, 0, sizeof (SANE_Parameters));

      dev->params.last_frame = SANE_TRUE;

      switch (dev->scan_mode)
	{
	case AUTHENTEC_GRAYSCALE_RAW:
	case AUTHENTEC_GRAYSCALE:
	case AUTHENTEC_GRAYSCALE_TEXT:
	  dev->bytes_pixel = 1;
	  break;
	}
      if (dev->resolutions_list != NULL)
	{
	  /* This fingerprint has a fixed number of supported
	   * resolutions. Find the color sequence for that
	   * resolution. */

	  for (i = 0;
	       dev->hw->color_adjust[i].resolution_x != dev->x_resolution;
	       i++);

	  dev->red_s = dev->hw->color_adjust[i].z1_color_0;
	  dev->green_s = dev->hw->color_adjust[i].z1_color_1;
	  dev->blue_s = dev->hw->color_adjust[i].z1_color_2;
	  dev->y_resolution = dev->hw->color_adjust[i].resolution_y;
	}
      dev->params.format = SANE_FRAME_GRAY;
      dev->params.pixels_per_line = dev->x_resolution;
      dev->params.lines = dev->y_resolution;
      dev->params.bytes_per_line =
	dev->params.pixels_per_line * dev->bytes_pixel;
      dev->params.depth = 8;
      DBG (DBG_info, "sane_get_parameters: x=%d, y=%d\n", dev->x_resolution,
	   dev->y_resolution);
    }

  /* Return the current values. */
  if (params)
    {
      *params = (dev->params);
    }

  DBG (DBG_proc, "sane_get_parameters: exit\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_start (SANE_Handle handle)
{
  Authentec_Fingerprint *dev = handle;
  SANE_Status status;

  DBG (DBG_proc, "sane_start: enter\n");

  if (!(dev->scanning))
    {
      sane_get_parameters (dev, NULL);

      /* Open again the fingerprint  */
      if (sanei_usb_open (dev->devicename, &(dev->fd)) != 0)
	{
	  DBG (DBG_error, "ERROR: sane_start: open failed\n");
	  return SANE_STATUS_INVAL;
	}

      /* Initialize the fingerprint. */
      status = authentec_fingerprint_init (dev);
      if (status)
	{
	  DBG (DBG_error, "ERROR: failed to init the fingerprint\n");
	  authentec_close (dev);
	  return status;
	}

    }

  dev->image_end = 0;
  dev->image_begin = 0;
  /* real_byte_left is bulk read bytes, bytes_left is frontend buffer bytes */
  dev->real_bytes_left = (dev->params.bytes_per_line * dev->params.lines) / 2;
  dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;

  dev->scanning = SANE_TRUE;

  DBG (DBG_proc, "sane_start: exit\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
	   SANE_Int * len)
{
  SANE_Status status;
  Authentec_Fingerprint *dev = handle;
  size_t size;

  DBG (DBG_proc, "sane_read: enter\n");

  *len = 0;
  if (dev->deliver_eof)
    {
      dev->deliver_eof = 0;
      return SANE_STATUS_EOF;
    }

  if (!(dev->scanning))
    {
      /* OOPS, not scanning, stop a scan. */
      authentec_reset_fingerprint (dev);
      authentec_close (dev);
      dev->scanning = SANE_FALSE;
      return SANE_STATUS_CANCELLED;
    }

  if (dev->bytes_left <= 0)
    {
      return (SANE_STATUS_EOF);
    }

  if (dev->image_begin == dev->image_end)
    {
      /* Fill image */
      status = authentec_fill_image (dev);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_info, "sane_read: authentec_fill_image status NOK\n");
	  return (status);
	}
    }

  /* Something must have been read */
  if (dev->image_begin == dev->image_end)
    {
      DBG (DBG_info, "sane_read: nothing read\n");
      return SANE_STATUS_IO_ERROR;
    }

  size = dev->bytes_left;
  if (((unsigned int) max_len) < size)
    {
      DBG (DBG_error, "sane_read: max_len < size\n");
      return (SANE_FALSE);
    }
  if ((dev->image_end - dev->image_begin) > size)
    {
      size = dev->image_end - dev->image_begin;
      DBG (DBG_proc, "sane_read: size < dev->image_end - dev->image_begin\n");
    }
  /* diff between size an dev->bytes_left because of 356/352 and 292/288 */
  DBG (DBG_info, "sane_read: size =0x%lx bytes, max_len=0x%lx bytes\n",
       (unsigned long) (size_t) size, (unsigned long) (size_t) max_len);

  *len = dev->bytes_left;	/* needed */
  size = dev->bytes_left;
  dev->bytes_left = 0;		/* needed for frontend or ? */

  if (dev->scan_mode != AUTHENTEC_GRAYSCALE_RAW)
    {
      /* do unshuffle  after complete frame is read */
      status = authentec_unshuffle (dev, buf, &size);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (DBG_info, "sane_read: authentec_unshuffle status NOK\n");
	  return (status);
	}
    }
  else
    {
      /* Copy the raw data to the frontend buffer. */
      memcpy (buf, dev->image, size);
      DBG (DBG_info, "sane_read: raw mode\n");
    }
  DBG (DBG_info, "sane_read: exit\n");
  status = SANE_STATUS_GOOD;
  return status;
}

SANE_Status
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
{

  DBG (DBG_proc, "sane_set_io_mode: enter\n");

  handle = handle;		/* silence gcc */
  non_blocking = non_blocking;	/* silence gcc */


  DBG (DBG_proc, "sane_set_io_mode: exit\n");

  return SANE_STATUS_UNSUPPORTED;
}

SANE_Status
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
{
  DBG (DBG_proc, "sane_get_select_fd: enter\n");

  handle = handle;		/* silence gcc */
  fd = fd;			/* silence gcc */

  DBG (DBG_proc, "sane_get_select_fd: exit\n");

  return SANE_STATUS_UNSUPPORTED;
}

void
sane_cancel (SANE_Handle handle)
{
  Authentec_Fingerprint *dev = handle;

  DBG (DBG_proc, "sane_cancel: enter\n");

  /* Stop a scan. */
  if (dev->scanning == SANE_TRUE)
    {
      /* Reset the fingerprint */
      authentec_reset_fingerprint (dev);
      authentec_close (dev);
    }
  dev->scanning = SANE_FALSE;
  dev->deliver_eof = 0;

  /* return SANE_STATUS_CANCELLED; */
  DBG (DBG_proc, "sane_cancel: exit\n");
}

void
sane_close (SANE_Handle handle)
{
  Authentec_Fingerprint *dev = handle;
  Authentec_Fingerprint *dev_tmp;

  DBG (DBG_proc, "sane_close: enter\n");

/* Stop a scan. */

  if (dev->scanning == SANE_TRUE)
    {
      authentec_reset_fingerprint (dev);
      authentec_close (dev);
    }
  dev->scanning = SANE_FALSE;

  /* Unlink dev. */
  if (first_dev == dev)
    {
      first_dev = dev->next;
    }
  else
    {
      dev_tmp = first_dev;
      while (dev_tmp->next && dev_tmp->next != dev)
	{
	  dev_tmp = dev_tmp->next;
	}
      if (dev_tmp->next != NULL)
	{
	  dev_tmp->next = dev_tmp->next->next;
	}
    }

  authentec_free (dev);
  num_devices--;

  DBG (DBG_proc, "sane_close: exit\n");
}

void
sane_exit (void)
{
  DBG (DBG_proc, "sane_exit: enter\n");

  while (first_dev)
    {
      sane_close (first_dev);
    }

  if (devlist)
    {
      free (devlist);
      devlist = NULL;
    }

  DBG (DBG_proc, "sane_exit: exit\n");
}
