kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1363 wiersze
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1363 wiersze
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
  Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
 | 
						|
 | 
						|
  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$
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
    Core NIASH chip functions.
 | 
						|
*/
 | 
						|
 | 
						|
#include <stdio.h>		/* fopen, fread, fwrite, fclose etc */
 | 
						|
#include <stdarg.h>		/* va_list for vfprintf */
 | 
						|
#include <string.h>		/* memcpy, memset */
 | 
						|
#include <unistd.h>		/* unlink */
 | 
						|
#include <stdlib.h>		/* malloc, free */
 | 
						|
#include <math.h>		/* exp, pow */
 | 
						|
 | 
						|
#include "niash_xfer.h"
 | 
						|
#include "niash_core.h"
 | 
						|
 | 
						|
 | 
						|
#ifndef MIN
 | 
						|
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef MAX
 | 
						|
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#define XFER_BUF_SIZE  0xF000
 | 
						|
 | 
						|
 | 
						|
/* HP3400 firmware data */
 | 
						|
static unsigned char abData0000[] = {
 | 
						|
  0xfe, 0x9f, 0x58, 0x1b, 0x00, 0x03, 0xa4, 0x02, 0x63, 0x02, 0x33, 0x02,
 | 
						|
  0x0d, 0x02, 0xf0, 0x01,
 | 
						|
  0xd8, 0x01, 0xc5, 0x01, 0xb5, 0x01, 0xa8, 0x01, 0x9d, 0x01, 0x93, 0x01,
 | 
						|
  0x8b, 0x01, 0x84, 0x01,
 | 
						|
  0x7e, 0x01, 0x79, 0x01, 0x74, 0x01, 0x70, 0x01, 0x6d, 0x01, 0x69, 0x01,
 | 
						|
  0x67, 0x01, 0x64, 0x01,
 | 
						|
  0x62, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x5d, 0x01, 0x5c, 0x01, 0x5b, 0x01,
 | 
						|
  0x5a, 0x01, 0x59, 0x01,
 | 
						|
  0x58, 0x01, 0x57, 0x01, 0x57, 0x01, 0x56, 0x01, 0x56, 0x01, 0x55, 0x01,
 | 
						|
  0x55, 0x01, 0x54, 0x01,
 | 
						|
  0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x53, 0x01, 0x53, 0x01, 0x53, 0x01,
 | 
						|
  0x53, 0x01, 0x52, 0x81
 | 
						|
};
 | 
						|
 | 
						|
/*  1st word : 0x9ffe = 40958, strip 15th bit: 0x1ffe = 8190
 | 
						|
    2nd word : 0x1b58 = 7000 -> coincidence ?
 | 
						|
    other words: formula: y = 676 / (2 - exp(0.113 * (1-x)) ), where x = 0 for first entry
 | 
						|
*/
 | 
						|
 | 
						|
/* more HP3400 firmware data */
 | 
						|
static unsigned char abData0400[] = {
 | 
						|
  0xa4, 0x82, 0x00, 0x80, 0xa4, 0x82, 0xaa, 0x02, 0xc0, 0x02, 0xe8, 0x02,
 | 
						|
  0x3e, 0x03, 0xc8, 0x03,
 | 
						|
  0x58, 0x1b, 0xfe, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
  0x00, 0x00, 0x00, 0x00,
 | 
						|
  0x00, 0x00, 0x00, 0x00
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
_ConvertMotorTable (unsigned char *pabOld, unsigned char *pabNew, int iSize,
 | 
						|
		    int iLpi)
 | 
						|
{
 | 
						|
  int iData, i, iBit15;
 | 
						|
 | 
						|
  for (i = 0; i < (iSize / 2); i++)
 | 
						|
    {
 | 
						|
      iData = pabOld[2 * i + 0] + (pabOld[2 * i + 1] << 8);
 | 
						|
      iBit15 = (iData & 0x8000);
 | 
						|
      iData = (iData & 0x7FFF);
 | 
						|
      if (iData <= 0x400)
 | 
						|
	{
 | 
						|
	  iData = iData * iLpi / 300;
 | 
						|
	}
 | 
						|
      if (iBit15 != 0)
 | 
						|
	{
 | 
						|
	  iData |= 0x8000;
 | 
						|
	}
 | 
						|
      pabNew[2 * i + 0] = iData & 255;
 | 
						|
      pabNew[2 * i + 1] = (iData >> 8) & 255;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*************************************************************************
 | 
						|
  _ProbeRegisters
 | 
						|
  ===============
 | 
						|
    Tries to determine certain hardware properties.
 | 
						|
 | 
						|
  This is done by checking the writeability of some scanner registers.
 | 
						|
  We cannot rely simply on the scanner model to contain a specific
 | 
						|
  chip. The HP3300c for example uses one of at least three slightly
 | 
						|
  different scanner ASICs (NIASH00012, NIASH00013 and NIASH00014).
 | 
						|
 | 
						|
  OUT pHWParams     Hardware parameters, updated fields:
 | 
						|
        fGamma16    TRUE if 16 bit gamma tables can be used
 | 
						|
        fReg07      TRUE if reg07 is writeable
 | 
						|
        iBufferSize Size of scanner's internal buffer
 | 
						|
 | 
						|
  Returns TRUE if a NIASH chipset was found.
 | 
						|
*************************************************************************/
 | 
						|
static SANE_Bool
 | 
						|
_ProbeRegisters (THWParams * pHWParams)
 | 
						|
{
 | 
						|
  unsigned char bData1, bData2;
 | 
						|
  int iHandle;
 | 
						|
 | 
						|
  iHandle = pHWParams->iXferHandle;
 | 
						|
 | 
						|
  DBG (DBG_MSG, "Probing scanner...\n");
 | 
						|
 | 
						|
  /* check register 0x04 */
 | 
						|
  NiashWriteReg (iHandle, 0x04, 0x55);
 | 
						|
  NiashReadReg (iHandle, 0x04, &bData1);
 | 
						|
  NiashWriteReg (iHandle, 0x04, 0xAA);
 | 
						|
  NiashReadReg (iHandle, 0x04, &bData2);
 | 
						|
  NiashWriteReg (iHandle, 0x04, 0x07);
 | 
						|
  if ((bData1 != 0x55) || (bData2 != 0xAA))
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "  No NIASH chipset found!\n");
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* check writeability of register 3 bit 1 */
 | 
						|
  NiashReadReg (iHandle, 0x03, &bData1);
 | 
						|
  NiashWriteReg (iHandle, 0x03, bData1 | 0x02);
 | 
						|
  NiashReadReg (iHandle, 0x03, &bData2);
 | 
						|
  NiashWriteReg (iHandle, 0x03, bData1);
 | 
						|
  pHWParams->fGamma16 = ((bData2 & 0x02) != 0);
 | 
						|
  DBG (DBG_MSG, "  Gamma table entries are %d bit\n",
 | 
						|
       pHWParams->fGamma16 ? 16 : 8);
 | 
						|
 | 
						|
  /* check register 0x07 */
 | 
						|
  NiashReadReg (iHandle, 0x07, &bData1);
 | 
						|
  NiashWriteReg (iHandle, 0x07, 0x1C);
 | 
						|
  NiashReadReg (iHandle, 0x07, &bData2);
 | 
						|
  NiashWriteReg (iHandle, 0x07, bData1);
 | 
						|
  pHWParams->fReg07 = (bData2 == 0x1C);
 | 
						|
 | 
						|
  if (!pHWParams->fGamma16)
 | 
						|
    {
 | 
						|
      /* internal scan buffer size is an educated guess, but seems to correlate
 | 
						|
         well with the size calculated from several windows driver log files
 | 
						|
         size = 128kB - 44088 unsigned chars (space required for gamma/calibration table)
 | 
						|
       */
 | 
						|
      pHWParams->iBufferSize = 86984L;
 | 
						|
      DBG (DBG_MSG, "  NIASH version < 00014\n");
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      pHWParams->iBufferSize = 0x60000L;
 | 
						|
      if (!pHWParams->fReg07)
 | 
						|
	{
 | 
						|
	  DBG (DBG_MSG, "  NIASH version = 00014\n");
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (DBG_MSG, "  NIASH version > 00014\n");
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* returns 0 on success, < 0 otherwise */
 | 
						|
STATIC int
 | 
						|
NiashOpen (THWParams * pHWParams, const char *pszName)
 | 
						|
{
 | 
						|
  int iXferHandle;
 | 
						|
 | 
						|
  iXferHandle = NiashXferOpen (pszName, &pHWParams->eModel);
 | 
						|
  if (iXferHandle < 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "NiashXferOpen failed for '%s'\n", pszName);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  pHWParams->iXferHandle = iXferHandle;
 | 
						|
 | 
						|
  NiashWakeup (pHWParams->iXferHandle);
 | 
						|
 | 
						|
  /* default HW params */
 | 
						|
  pHWParams->iSensorSkew = 8;
 | 
						|
  pHWParams->iTopLeftX = 0;
 | 
						|
  pHWParams->iTopLeftY = 3;
 | 
						|
  pHWParams->fReg07 = SANE_FALSE;
 | 
						|
  pHWParams->iSkipLines = 0;
 | 
						|
  pHWParams->iExpTime = 5408;
 | 
						|
  pHWParams->iReversedHead = SANE_TRUE;
 | 
						|
 | 
						|
  switch (pHWParams->eModel)
 | 
						|
    {
 | 
						|
 | 
						|
    case eHp3300c:
 | 
						|
      DBG (DBG_MSG, "Setting params for Hp3300\n");
 | 
						|
      pHWParams->iTopLeftX = 4;
 | 
						|
      pHWParams->iTopLeftY = 11;
 | 
						|
      pHWParams->iSkipLines = 14;
 | 
						|
      break;
 | 
						|
 | 
						|
    case eHp3400c:
 | 
						|
    case eHp4300c:
 | 
						|
      DBG (DBG_MSG, "Setting params for Hp3400c/Hp4300c\n");
 | 
						|
      pHWParams->iTopLeftX = 3;
 | 
						|
      pHWParams->iTopLeftY = 14;
 | 
						|
      pHWParams->fReg07 = SANE_TRUE;
 | 
						|
      break;
 | 
						|
 | 
						|
    case eAgfaTouch:
 | 
						|
      DBG (DBG_MSG, "Setting params for AgfaTouch\n");
 | 
						|
      pHWParams->iReversedHead = SANE_FALSE;	/* head not reversed on Agfa Touch */
 | 
						|
      pHWParams->iTopLeftX = 3;
 | 
						|
      pHWParams->iTopLeftY = 10;
 | 
						|
      pHWParams->iSkipLines = 7;
 | 
						|
      break;
 | 
						|
 | 
						|
    case eUnknownModel:
 | 
						|
      DBG (DBG_MSG, "Setting params for UnknownModel\n");
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      DBG (DBG_ERR, "ERROR: internal error! (%d)\n", (int) pHWParams->eModel);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* autodetect some hardware properties */
 | 
						|
  if (!_ProbeRegisters (pHWParams))
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "_ProbeRegisters failed!\n");
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
STATIC void
 | 
						|
NiashClose (THWParams * pHWPar)
 | 
						|
{
 | 
						|
  NiashXferClose (pHWPar->iXferHandle);
 | 
						|
  pHWPar->iXferHandle = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
WriteRegWord (int iHandle, unsigned char bReg, SANE_Word wData)
 | 
						|
{
 | 
						|
  NiashWriteReg (iHandle, bReg, wData & 0xFF);
 | 
						|
  NiashWriteReg (iHandle, bReg + 1, (wData >> 8) & 0xFF);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* calculate a 4096 unsigned char gamma table */
 | 
						|
STATIC void
 | 
						|
CalcGamma (unsigned char *pabTable, double Gamma)
 | 
						|
{
 | 
						|
  int i, iData;
 | 
						|
 | 
						|
  /* fill gamma table */
 | 
						|
  for (i = 0; i < 4096; i++)
 | 
						|
    {
 | 
						|
      iData = floor (256.0 * pow (((double) i / 4096.0), 1.0 / Gamma));
 | 
						|
      pabTable[i] = iData;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Hp3400WriteFw
 | 
						|
  =============
 | 
						|
    Writes data to scanners with a NIASH00019 chipset, e.g.
 | 
						|
    gamma, calibration and motor control data.
 | 
						|
 | 
						|
  IN  pabData   pointer to firmware data
 | 
						|
      iLen      Size of firmware date (unsigned chars)
 | 
						|
      iAddr     Scanner address to write to
 | 
						|
*/
 | 
						|
static void
 | 
						|
Hp3400cWriteFW (int iXferHandle, unsigned char *pabData, int iLen, int iAddr)
 | 
						|
{
 | 
						|
  iAddr--;
 | 
						|
  NiashWriteReg (iXferHandle, 0x21, iAddr & 0xFF);
 | 
						|
  NiashWriteReg (iXferHandle, 0x22, (iAddr >> 8) & 0xFF);
 | 
						|
  NiashWriteReg (iXferHandle, 0x23, (iAddr >> 16) & 0xFF);
 | 
						|
  NiashWriteBulk (iXferHandle, pabData, iLen);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Writes the gamma and offset/gain tables to the scanner.
 | 
						|
   In case a calibration file exist, it will be used for offset/gain */
 | 
						|
STATIC void
 | 
						|
WriteGammaCalibTable (unsigned char *pabGammaR, unsigned char *pabGammaG,
 | 
						|
		      unsigned char *pabGammaB, unsigned char *pabCalibTable,
 | 
						|
		      int iGain, int iOffset, THWParams * pHWPar)
 | 
						|
{
 | 
						|
  int i, j, k;
 | 
						|
  static unsigned char abGamma[60000];
 | 
						|
  int iData;
 | 
						|
  int iHandle;
 | 
						|
 | 
						|
  iHandle = pHWPar->iXferHandle;
 | 
						|
 | 
						|
  j = 0;
 | 
						|
  /* fill gamma table for red component */
 | 
						|
  /* pad entries with 0 for 16-bit gamma table */
 | 
						|
  for (i = 0; i < 4096; i++)
 | 
						|
    {
 | 
						|
      if (pHWPar->fGamma16)
 | 
						|
	{
 | 
						|
	  abGamma[j++] = 0;
 | 
						|
	}
 | 
						|
      abGamma[j++] = pabGammaR[i];
 | 
						|
    }
 | 
						|
  /* fill gamma table for green component */
 | 
						|
  for (i = 0; i < 4096; i++)
 | 
						|
    {
 | 
						|
      if (pHWPar->fGamma16)
 | 
						|
	{
 | 
						|
	  abGamma[j++] = 0;
 | 
						|
	}
 | 
						|
      abGamma[j++] = pabGammaG[i];
 | 
						|
    }
 | 
						|
  /* fill gamma table for blue component */
 | 
						|
  for (i = 0; i < 4096; i++)
 | 
						|
    {
 | 
						|
      if (pHWPar->fGamma16)
 | 
						|
	{
 | 
						|
	  abGamma[j++] = 0;
 | 
						|
	}
 | 
						|
      abGamma[j++] = pabGammaB[i];
 | 
						|
    }
 | 
						|
 | 
						|
  if (pabCalibTable == NULL)
 | 
						|
    {
 | 
						|
      iData = (iGain << 6) + iOffset;
 | 
						|
      for (i = 0; i < HW_PIXELS; i++)
 | 
						|
	{
 | 
						|
	  for (k = 0; k < 3; k++)
 | 
						|
	    {
 | 
						|
	      abGamma[j++] = (iData) & 255;
 | 
						|
	      abGamma[j++] = (iData >> 8) & 255;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      memcpy (&abGamma[j], pabCalibTable, HW_PIXELS * 6);
 | 
						|
      j += HW_PIXELS * 6;
 | 
						|
    }
 | 
						|
 | 
						|
  NiashWriteReg (iHandle, 0x02, 0x80);
 | 
						|
  NiashWriteReg (iHandle, 0x03, 0x01);
 | 
						|
  NiashWriteReg (iHandle, 0x03, 0x11);
 | 
						|
  NiashWriteReg (iHandle, 0x02, 0x84);
 | 
						|
 | 
						|
  if (pHWPar->fReg07)
 | 
						|
    {
 | 
						|
      Hp3400cWriteFW (iHandle, abGamma, j, 0x2000);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      NiashWriteBulk (iHandle, abGamma, j);
 | 
						|
    }
 | 
						|
 | 
						|
  NiashWriteReg (iHandle, 0x02, 0x80);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
WriteAFEReg (int iHandle, int iReg, int iData)
 | 
						|
{
 | 
						|
  NiashWriteReg (iHandle, 0x25, iReg);
 | 
						|
  NiashWriteReg (iHandle, 0x26, iData);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* setup the analog front-end -> coarse calibration */
 | 
						|
static void
 | 
						|
WriteAFE (int iHandle)
 | 
						|
{
 | 
						|
  /* see WM8143 datasheet */
 | 
						|
 | 
						|
  WriteAFEReg (iHandle, 0x04, 0x00);
 | 
						|
  WriteAFEReg (iHandle, 0x03, 0x12);
 | 
						|
  WriteAFEReg (iHandle, 0x02, 0x04);
 | 
						|
  WriteAFEReg (iHandle, 0x05, 0x10);
 | 
						|
  WriteAFEReg (iHandle, 0x01, 0x03);
 | 
						|
 | 
						|
  WriteAFEReg (iHandle, 0x20, 0xc0);	/*c8 *//* red offset */
 | 
						|
  WriteAFEReg (iHandle, 0x21, 0xc0);	/*c8 *//* green offset */
 | 
						|
  WriteAFEReg (iHandle, 0x22, 0xc0);	/*d0 *//* blue offset */
 | 
						|
 | 
						|
  WriteAFEReg (iHandle, 0x28, 0x05);	/*5 *//* red gain */
 | 
						|
  WriteAFEReg (iHandle, 0x29, 0x03);	/*3 *//* green gain */
 | 
						|
  WriteAFEReg (iHandle, 0x2A, 0x04);	/*4 *//* blue gain */
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* wait for the carriage to return */
 | 
						|
static void
 | 
						|
WaitReadyBit (int iHandle)
 | 
						|
{
 | 
						|
  unsigned char bData;
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      NiashReadReg (iHandle, 0x03, &bData);
 | 
						|
    }
 | 
						|
  while ((bData & 8) == 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Initialisation specific for NIASH00014 and lower chips
 | 
						|
*/
 | 
						|
static void
 | 
						|
InitNiash00014 (TScanParams * pParams, THWParams * pHWParams)
 | 
						|
{
 | 
						|
  int iHandle, iLpiCode;
 | 
						|
 | 
						|
  iHandle = pHWParams->iXferHandle;
 | 
						|
 | 
						|
  /* exposure time (in units 24/Fcrystal)? */
 | 
						|
  WriteRegWord (iHandle, 0x08, pHWParams->iExpTime - 1);
 | 
						|
 | 
						|
  /* width in pixels */
 | 
						|
  WriteRegWord (iHandle, 0x12, pParams->iWidth - 1);
 | 
						|
 | 
						|
  /* top */
 | 
						|
  WriteRegWord (iHandle, 0x17, pParams->iTop);
 | 
						|
  WriteRegWord (iHandle, 0x19, pParams->iTop);
 | 
						|
 | 
						|
  /* time between stepper motor steps (in units of 24/Fcrystal)? */
 | 
						|
  iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L;
 | 
						|
 | 
						|
  if (!pHWParams->fGamma16)
 | 
						|
    {
 | 
						|
      /* NIASH 00012 / 00013 init */
 | 
						|
 | 
						|
      /* LPI specific settings */
 | 
						|
      if (pParams->iLpi < 600)
 | 
						|
	{
 | 
						|
	  /* set halfres bit */
 | 
						|
	  NiashWriteReg (iHandle, 0x06, 0x01);
 | 
						|
	  /* double lpi code because of halfres bit */
 | 
						|
	  iLpiCode *= 2;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* clear halfres bit */
 | 
						|
	  NiashWriteReg (iHandle, 0x06, 0x00);
 | 
						|
	  /* add exptime to make it scan slower */
 | 
						|
	  iLpiCode += pHWParams->iExpTime;
 | 
						|
	}
 | 
						|
 | 
						|
      /* unknown setting */
 | 
						|
      WriteRegWord (iHandle, 0x27, 0x7FD2);
 | 
						|
      WriteRegWord (iHandle, 0x29, 0x6421);
 | 
						|
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* NIASH 00014 init */
 | 
						|
 | 
						|
      /* halfres bit always cleared */
 | 
						|
      NiashWriteReg (iHandle, 0x06, 0x00);
 | 
						|
 | 
						|
      /* LPI specific settings */
 | 
						|
      if (pParams->iLpi >= 600)
 | 
						|
	{
 | 
						|
	  /* add exptime to make it scan slower */
 | 
						|
	  iLpiCode += pHWParams->iExpTime;
 | 
						|
	}
 | 
						|
 | 
						|
      /* unknown setting */
 | 
						|
      WriteRegWord (iHandle, 0x27, 0xc862);	/*c862 */
 | 
						|
      WriteRegWord (iHandle, 0x29, 0xb853);	/*b853 */
 | 
						|
    }
 | 
						|
 | 
						|
  /* LPI code */
 | 
						|
  WriteRegWord (iHandle, 0x0A, iLpiCode - 1);
 | 
						|
 | 
						|
  /* backtrack reversing speed */
 | 
						|
  NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Initialisation specific for NIASH00019 chips
 | 
						|
*/
 | 
						|
static void
 | 
						|
InitNiash00019 (TScanParams * pParams, THWParams * pHWParams)
 | 
						|
{
 | 
						|
  int iHandle, iLpiCode;
 | 
						|
  static unsigned char abMotor[512];
 | 
						|
 | 
						|
 | 
						|
  iHandle = pHWParams->iXferHandle;
 | 
						|
 | 
						|
  /* exposure time (in units 24/Fcrystal)? */
 | 
						|
  WriteRegWord (iHandle, 0x08, pHWParams->iExpTime);
 | 
						|
 | 
						|
  /* width in pixels */
 | 
						|
  WriteRegWord (iHandle, 0x12, pParams->iWidth);
 | 
						|
 | 
						|
  /* ? */
 | 
						|
  WriteRegWord (iHandle, 0x27, 0xc862);	/*c862 */
 | 
						|
  WriteRegWord (iHandle, 0x29, 0xb853);	/*b853 */
 | 
						|
 | 
						|
  /* specific handling of 150 dpi resolution */
 | 
						|
  if (pParams->iLpi == 150)
 | 
						|
    {
 | 
						|
      /* use 300 LPI but skip every other line */
 | 
						|
      pParams->iLpi = 300;
 | 
						|
      NiashWriteReg (iHandle, 0x06, 0x01);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      NiashWriteReg (iHandle, 0x06, 0x00);
 | 
						|
    }
 | 
						|
 | 
						|
  /* DPI and position table */
 | 
						|
  NiashWriteReg (iHandle, 0x07, 0x02);
 | 
						|
  _ConvertMotorTable (abData0000, abMotor, sizeof (abData0000),
 | 
						|
		      pParams->iLpi);
 | 
						|
  Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0000), 0x000);
 | 
						|
  _ConvertMotorTable (abData0400, abMotor, sizeof (abData0400),
 | 
						|
		      pParams->iLpi);
 | 
						|
  Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0400), 0x400);
 | 
						|
 | 
						|
  /* backtrack reversing speed */
 | 
						|
  iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L;
 | 
						|
  NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Scanner initialisation common to all NIASH chips
 | 
						|
*/
 | 
						|
static void
 | 
						|
InitNiashCommon (TScanParams * pParams, THWParams * pHWParams)
 | 
						|
{
 | 
						|
  int iWidthHW, iHandle, iMaxLevel;
 | 
						|
 | 
						|
 | 
						|
  iHandle = pHWParams->iXferHandle;
 | 
						|
 | 
						|
  NiashWriteReg (iHandle, 0x02, 0x80);
 | 
						|
  NiashWriteReg (iHandle, 0x03, 0x11);
 | 
						|
  NiashWriteReg (iHandle, 0x01, 0x8B);
 | 
						|
  NiashWriteReg (iHandle, 0x05, 0x01);
 | 
						|
 | 
						|
  /* dpi */
 | 
						|
  WriteRegWord (iHandle, 0x0C, pParams->iDpi);
 | 
						|
 | 
						|
  /* calculate width in units of HW resolution */
 | 
						|
  iWidthHW = pParams->iWidth * (HW_DPI / pParams->iDpi);
 | 
						|
 | 
						|
  /* set left and right limits */
 | 
						|
  if (pHWParams->iReversedHead)
 | 
						|
    {
 | 
						|
      /* head is reversed */
 | 
						|
      /* right */
 | 
						|
      WriteRegWord (iHandle, 0x0E,
 | 
						|
		    3 * (HW_PIXELS - (pParams->iLeft + iWidthHW)));
 | 
						|
 | 
						|
      /* left */
 | 
						|
      WriteRegWord (iHandle, 0x10, 3 * (HW_PIXELS - pParams->iLeft) - 1);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* head is not reversed */
 | 
						|
      /*left  */
 | 
						|
      WriteRegWord (iHandle, 0x0E, 3 * pParams->iLeft);
 | 
						|
 | 
						|
      /* right */
 | 
						|
      WriteRegWord (iHandle, 0x10, 3 * (pParams->iLeft + iWidthHW) - 1);
 | 
						|
    }
 | 
						|
 | 
						|
  /* bottom */
 | 
						|
  WriteRegWord (iHandle, 0x1B, pParams->iBottom);	/* 0x393C); */
 | 
						|
 | 
						|
  /* forward jogging speed */
 | 
						|
  NiashWriteReg (iHandle, 0x1D, 0x60);
 | 
						|
 | 
						|
  /* backtrack reversing speed? */
 | 
						|
  NiashWriteReg (iHandle, 0x2B, 0x15);
 | 
						|
 | 
						|
  /* backtrack distance */
 | 
						|
  if (pParams->iLpi < 600)
 | 
						|
    {
 | 
						|
      NiashWriteReg (iHandle, 0x1F, 0x30);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      NiashWriteReg (iHandle, 0x1F, 0x18);
 | 
						|
    }
 | 
						|
 | 
						|
  /* max buffer level before backtrace */
 | 
						|
  iMaxLevel = MIN (pHWParams->iBufferSize / pParams->iWidth, 250);
 | 
						|
  NiashWriteReg (iHandle, 0x14, iMaxLevel - 1);
 | 
						|
 | 
						|
  /* lamp PWM, max = 0x1ff? */
 | 
						|
  WriteRegWord (iHandle, 0x2C, 0x01FF);
 | 
						|
 | 
						|
  /* not needed? */
 | 
						|
  NiashWriteReg (iHandle, 0x15, 0x90);	/* 90 */
 | 
						|
  NiashWriteReg (iHandle, 0x16, 0x70);	/* 70 */
 | 
						|
 | 
						|
  WriteAFE (iHandle);
 | 
						|
 | 
						|
  WaitReadyBit (iHandle);
 | 
						|
 | 
						|
  NiashWriteReg (iHandle, 0x03, 0x05);
 | 
						|
 | 
						|
  NiashWriteReg (iHandle, 0x02, pParams->fCalib ? 0x88 : 0xA8);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* write registers */
 | 
						|
STATIC SANE_Bool
 | 
						|
InitScan (TScanParams * pParams, THWParams * pHWParams)
 | 
						|
{
 | 
						|
  int iHeight;
 | 
						|
  int iExpTime;
 | 
						|
  TScanParams Params;
 | 
						|
  int iHandle;
 | 
						|
 | 
						|
  iHandle = pHWParams->iXferHandle;
 | 
						|
 | 
						|
  /* check validity of scanparameters */
 | 
						|
  switch (pParams->iDpi)
 | 
						|
    {
 | 
						|
    case 150:
 | 
						|
    case 300:
 | 
						|
    case 600:
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      DBG (DBG_ERR, "Invalid dpi (%d)\n", pParams->iDpi);
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  iHeight = (pParams->iBottom - pParams->iTop + 1);
 | 
						|
  if (iHeight <= 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "Invalid height (%d)\n", iHeight);
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (pParams->iWidth <= 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "Invalid width (%d)\n", pParams->iWidth);
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  switch (pParams->iLpi)
 | 
						|
    {
 | 
						|
    case 150:
 | 
						|
    case 300:
 | 
						|
    case 600:
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      DBG (DBG_ERR, "Invalid lpi (%d)\n", pParams->iLpi);
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* exposure time (in units of 24/Fcrystal?), must be divisible by 8 !!! */
 | 
						|
  iExpTime = 5408;
 | 
						|
  if ((iExpTime % 8) != 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "Invalid exposure time (%d)\n", iExpTime);
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  /*
 | 
						|
   *** Done checking scan parameters validity ***
 | 
						|
   */
 | 
						|
 | 
						|
  /*
 | 
						|
     copy the parameters locally and make pParams point to the local copy
 | 
						|
   */
 | 
						|
  memcpy (&Params, pParams, sizeof (Params));
 | 
						|
  pParams = &Params;
 | 
						|
 | 
						|
  if (!pHWParams->fReg07)
 | 
						|
    {
 | 
						|
      /* init NIASH00014 and lower */
 | 
						|
      InitNiash00014 (pParams, pHWParams);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* init NIASH00019 */
 | 
						|
      InitNiash00019 (pParams, pHWParams);
 | 
						|
    }
 | 
						|
 | 
						|
  /* common NIASH init */
 | 
						|
  InitNiashCommon (pParams, pHWParams);
 | 
						|
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/************************************************************************/
 | 
						|
 | 
						|
static SANE_Bool
 | 
						|
XferBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine,
 | 
						|
		   SANE_Bool fReturn)
 | 
						|
{
 | 
						|
  unsigned char bData, bData2;
 | 
						|
  SANE_Bool fJustDone = SANE_FALSE;
 | 
						|
  /* all calculated transfers done ? */
 | 
						|
  if (p->iLinesLeft == 0)
 | 
						|
    return SANE_FALSE;
 | 
						|
 | 
						|
  /* time for a fresh read? */
 | 
						|
  if (p->iCurLine == 0)
 | 
						|
    {
 | 
						|
      int iLines;
 | 
						|
      iLines = p->iLinesPerXferBuf;
 | 
						|
      /* read only as many lines as needed */
 | 
						|
      if (p->iLinesLeft > 0 && p->iLinesLeft <= iLines)
 | 
						|
	{
 | 
						|
	  iLines = p->iLinesLeft;
 | 
						|
	  DBG (DBG_MSG, "\n");
 | 
						|
	  DBG (DBG_MSG, "last bulk read\n");
 | 
						|
	  if (iLines < p->iLinesPerXferBuf)
 | 
						|
	    {
 | 
						|
	      DBG (DBG_MSG,
 | 
						|
		   "reading reduced number of lines: %d instead of %d\n",
 | 
						|
		   iLines, p->iLinesPerXferBuf);
 | 
						|
	    }
 | 
						|
	  fJustDone = SANE_TRUE;
 | 
						|
	}
 | 
						|
      /* reading old buffer level */
 | 
						|
      NiashReadReg (iHandle, 0x20, &bData);
 | 
						|
      NiashReadBulk (iHandle, p->pabXferBuf, iLines * p->iBytesPerLine);
 | 
						|
      /* reding new buffer level */
 | 
						|
      NiashReadReg (iHandle, 0x20, &bData2);
 | 
						|
      if (fJustDone && fReturn)
 | 
						|
	{
 | 
						|
	  NiashWriteReg (iHandle, 0x02, 0x80);
 | 
						|
	  DBG (DBG_MSG, "returning scanner head\n");
 | 
						|
	}
 | 
						|
      DBG (DBG_MSG,
 | 
						|
	   "buffer level = %3d, <reading %5d unsigned chars>, buffer level = %3d\r",
 | 
						|
	   (int) bData, iLines * p->iBytesPerLine, (int) bData2);
 | 
						|
      fflush (stdout);
 | 
						|
    }
 | 
						|
  /* copy one line */
 | 
						|
  if (pabLine != NULL)
 | 
						|
    {
 | 
						|
      memcpy (pabLine, &p->pabXferBuf[p->iCurLine * p->iBytesPerLine],
 | 
						|
	      p->iBytesPerLine);
 | 
						|
    }
 | 
						|
  /* advance pointer */
 | 
						|
  p->iCurLine = (p->iCurLine + 1) % p->iLinesPerXferBuf;
 | 
						|
 | 
						|
  /* one transfer line less to the XFerBuffer */
 | 
						|
  if (p->iLinesLeft > 0)
 | 
						|
    --(p->iLinesLeft);
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
XferBufferInit (int iHandle, TDataPipe * p)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  p->pabXferBuf = (unsigned char *) malloc (XFER_BUF_SIZE);
 | 
						|
  p->iCurLine = 0;
 | 
						|
 | 
						|
  /* skip garbage lines */
 | 
						|
  for (i = 0; i < p->iSkipLines; i++)
 | 
						|
    {
 | 
						|
      XferBufferGetLine (iHandle, p, NULL, SANE_FALSE);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* static procedure that fills the circular buffer in advance to any
 | 
						|
   circular buffer data retrieval */
 | 
						|
static void
 | 
						|
CircBufferFill (int iHandle, TDataPipe * p, SANE_Bool iReversedHead)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  for (i = 0; i < p->iLinesPerCircBuf; i++)
 | 
						|
    {
 | 
						|
      if (iReversedHead)
 | 
						|
	{
 | 
						|
	  XferBufferGetLine (iHandle, p,
 | 
						|
			     &p->pabCircBuf[p->iRedLine * p->iBytesPerLine],
 | 
						|
			     SANE_FALSE);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  XferBufferGetLine (iHandle, p,
 | 
						|
			     &p->pabCircBuf[p->iBluLine * p->iBytesPerLine],
 | 
						|
			     SANE_FALSE);
 | 
						|
	}
 | 
						|
      /* advance pointers */
 | 
						|
      p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf;
 | 
						|
      p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf;
 | 
						|
      p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
XferBufferExit (TDataPipe * p)
 | 
						|
{
 | 
						|
  if (p->pabXferBuf != NULL)
 | 
						|
    {
 | 
						|
      free (p->pabXferBuf);
 | 
						|
      p->pabXferBuf = NULL;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "XferBufExit: Xfer buffer not initialised!\n");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* unscrambles a line:
 | 
						|
   - combining the proper R, G and B lines and converting them to interpixel RGB
 | 
						|
   - mirroring left to right
 | 
						|
*/
 | 
						|
static void
 | 
						|
_UnscrambleLine (unsigned char *pabLine,
 | 
						|
		 unsigned char *pabRed, unsigned char *pabGrn,
 | 
						|
		 unsigned char *pabBlu, int iWidth, SANE_Bool iReversedHead,
 | 
						|
		 int iScaleDownDpi, int iBufWeight)
 | 
						|
{
 | 
						|
  /* never change an approved algorithm ...
 | 
						|
     so take Bertriks original source for this special case */
 | 
						|
  if (iScaleDownDpi == 1 && iBufWeight == 0)
 | 
						|
    {
 | 
						|
      int i, j;
 | 
						|
      if (iReversedHead)
 | 
						|
	{
 | 
						|
	  /* reversed */
 | 
						|
	  for (i = 0; i < iWidth; i++)
 | 
						|
	    {
 | 
						|
	      j = (iWidth - i) * 3;
 | 
						|
	      pabLine[j - 3] = pabRed[i];
 | 
						|
	      pabLine[j - 2] = pabGrn[i + iWidth];
 | 
						|
	      pabLine[j - 1] = pabBlu[i + iWidth * 2];
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* not reversed */
 | 
						|
	  for (i = 0; i < iWidth; i++)
 | 
						|
	    {
 | 
						|
	      pabLine[3 * i] = pabRed[i];
 | 
						|
	      pabLine[3 * i + 1] = pabGrn[i + iWidth];
 | 
						|
	      pabLine[3 * i + 2] = pabBlu[i + iWidth * 2];
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      int i, j;			/* loop variables */
 | 
						|
      int c;			/* color buffer accumulator for horizontal avarage */
 | 
						|
 | 
						|
      /* initialize for incremental color buffer access */
 | 
						|
      int iInc = 1;
 | 
						|
      int iStart = 0;
 | 
						|
 | 
						|
      /* set for "from the end to the front" of the circular color buffers */
 | 
						|
      if (iReversedHead)
 | 
						|
	{
 | 
						|
	  iStart = iWidth - iScaleDownDpi;
 | 
						|
	  iInc = -1;
 | 
						|
	}
 | 
						|
 | 
						|
      /* each pixel is the mean of iScaleDownDpi
 | 
						|
         so set the skip width accordingly */
 | 
						|
      iInc *= iScaleDownDpi;
 | 
						|
 | 
						|
      for (i = iStart; i >= 0 && i < iWidth; i += iInc)
 | 
						|
	{
 | 
						|
	  /* collect the red pixels */
 | 
						|
	  for (c = j = 0; j < iScaleDownDpi; ++j)
 | 
						|
	    c += pabRed[i + j];
 | 
						|
	  *pabLine =
 | 
						|
	    (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
 | 
						|
	  pabLine++;
 | 
						|
 | 
						|
	  /* collect the green pixels */
 | 
						|
	  for (c = j = 0; j < iScaleDownDpi; ++j)
 | 
						|
	    c += pabGrn[i + iWidth + j];
 | 
						|
	  *pabLine =
 | 
						|
	    (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
 | 
						|
	  pabLine++;
 | 
						|
 | 
						|
	  /* collect the blue pixels */
 | 
						|
	  for (c = j = 0; j < iScaleDownDpi; ++j)
 | 
						|
	    c += pabBlu[i + 2 * iWidth + j];
 | 
						|
	  *pabLine =
 | 
						|
	    (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1);
 | 
						|
	  pabLine++;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage,
 | 
						|
   if fReturn==SANE_TRUE, the head will return automatically on an end of scan */
 | 
						|
STATIC SANE_Bool
 | 
						|
CircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine,
 | 
						|
		     SANE_Bool iReversedHead, SANE_Bool fReturn)
 | 
						|
{
 | 
						|
  int iLineCount;
 | 
						|
  for (iLineCount = 0; iLineCount < p->iScaleDownLpi; ++iLineCount)
 | 
						|
    {
 | 
						|
      if (iReversedHead)
 | 
						|
	{
 | 
						|
	  if (!XferBufferGetLine (iHandle, p,
 | 
						|
				  &p->pabCircBuf[p->iRedLine *
 | 
						|
						 p->iBytesPerLine], fReturn))
 | 
						|
	    return SANE_FALSE;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (!XferBufferGetLine (iHandle, p,
 | 
						|
				  &p->pabCircBuf[p->iBluLine *
 | 
						|
						 p->iBytesPerLine], fReturn))
 | 
						|
	    return SANE_FALSE;
 | 
						|
	}
 | 
						|
      if (pabLine != NULL)
 | 
						|
	{
 | 
						|
	  _UnscrambleLine (pabLine,
 | 
						|
			   &p->pabCircBuf[p->iRedLine * p->iBytesPerLine],
 | 
						|
			   &p->pabCircBuf[p->iGrnLine * p->iBytesPerLine],
 | 
						|
			   &p->pabCircBuf[p->iBluLine * p->iBytesPerLine],
 | 
						|
			   p->iWidth * p->iScaleDownDpi, iReversedHead,
 | 
						|
			   p->iScaleDownDpi, iLineCount);
 | 
						|
	}
 | 
						|
 | 
						|
      /* advance pointers */
 | 
						|
      p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf;
 | 
						|
      p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf;
 | 
						|
      p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf;
 | 
						|
    }
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage */
 | 
						|
STATIC SANE_Bool
 | 
						|
CircBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine,
 | 
						|
		   SANE_Bool iReversedHead)
 | 
						|
{
 | 
						|
  return CircBufferGetLineEx (iHandle, p, pabLine, iReversedHead, SANE_FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* try to keep the number of transfers the same, but make them all 
 | 
						|
   as good as possible the same size to avoid cranking in critical
 | 
						|
   situations
 | 
						|
*/
 | 
						|
static int
 | 
						|
_OptimizeXferSize (int iLines, int iLinesPerXfer)
 | 
						|
{
 | 
						|
  int iXfers;
 | 
						|
  iXfers = (iLines + iLinesPerXfer - 1) / iLinesPerXfer;
 | 
						|
  while (--iLinesPerXfer > 0
 | 
						|
	 && (iLines + iLinesPerXfer - 1) / iLinesPerXfer == iXfers);
 | 
						|
  return iLinesPerXfer + 1;
 | 
						|
}
 | 
						|
 | 
						|
STATIC void
 | 
						|
CircBufferInit (int iHandle, TDataPipe * p,
 | 
						|
		int iWidth, int iHeight,
 | 
						|
		int iMisAlignment, SANE_Bool iReversedHead,
 | 
						|
		int iScaleDownDpi, int iScaleDownLpi)
 | 
						|
{
 | 
						|
 | 
						|
  /* relevant for internal read and write functions */
 | 
						|
  p->iScaleDownLpi = iScaleDownLpi;
 | 
						|
  p->iScaleDownDpi = iScaleDownDpi;
 | 
						|
  p->iWidth = iWidth;
 | 
						|
  p->iBytesPerLine = iWidth * iScaleDownDpi * BYTES_PER_PIXEL;
 | 
						|
  p->iSaneBytesPerLine = iWidth * BYTES_PER_PIXEL;
 | 
						|
  if (iMisAlignment == 0)
 | 
						|
    {
 | 
						|
      p->iLinesPerCircBuf = 1;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      p->iLinesPerCircBuf = 3 * iMisAlignment;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (DBG_MSG, "_iScaleDown (Dpi,Lpi) = (%d,%d)\n", p->iScaleDownDpi,
 | 
						|
       p->iScaleDownLpi);
 | 
						|
  DBG (DBG_MSG, "_iBytesPerLine = %d\n", p->iBytesPerLine);
 | 
						|
  DBG (DBG_MSG, "_iLinesPerCircBuf = %d\n", p->iLinesPerCircBuf);
 | 
						|
  p->pabCircBuf =
 | 
						|
    (unsigned char *) malloc (p->iBytesPerLine * p->iLinesPerCircBuf);
 | 
						|
  if (p->pabCircBuf == NULL)
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR,
 | 
						|
	   "Unable to allocate %d unsigned chars for circular buffer\n",
 | 
						|
	   (int) (p->iBytesPerLine * p->iLinesPerCircBuf));
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  DBG (DBG_MSG, "Allocated %d unsigned chars for circular buffer\n",
 | 
						|
       p->iBytesPerLine * p->iLinesPerCircBuf);
 | 
						|
 | 
						|
  if (iReversedHead)
 | 
						|
    {
 | 
						|
      p->iBluLine = 0;
 | 
						|
      p->iGrnLine = iMisAlignment;
 | 
						|
      p->iRedLine = iMisAlignment * 2;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      p->iRedLine = 0;
 | 
						|
      p->iGrnLine = iMisAlignment;
 | 
						|
      p->iBluLine = iMisAlignment * 2;
 | 
						|
    }
 | 
						|
 | 
						|
  /* negative height is an indication for "no Check" */
 | 
						|
  if (iHeight < 0)
 | 
						|
    {
 | 
						|
      p->iLinesLeft = -1;
 | 
						|
      p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine;
 | 
						|
      DBG (DBG_MSG, "using unchecked XFER_BUF_SIZE\n");
 | 
						|
      DBG (DBG_MSG, "_iXFerSize = %d\n",
 | 
						|
	   p->iBytesPerLine * p->iLinesPerXferBuf);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
#define SAFETY_LINES 0
 | 
						|
#define MAX_LINES_PER_XFERBUF 800
 | 
						|
      /* estimate of number of unsigned chars to transfer at all via the USB */
 | 
						|
      /* add some lines for securtiy */
 | 
						|
 | 
						|
      p->iLinesLeft =
 | 
						|
	iHeight + p->iSkipLines + p->iLinesPerCircBuf + SAFETY_LINES;
 | 
						|
      p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine;
 | 
						|
      /* with more than 800 lines the timing is spoiled */
 | 
						|
      if (p->iLinesPerXferBuf > MAX_LINES_PER_XFERBUF)
 | 
						|
	{
 | 
						|
	  p->iLinesPerXferBuf = MAX_LINES_PER_XFERBUF;
 | 
						|
	}
 | 
						|
      /* final optimization to keep critical scans smooth */
 | 
						|
      p->iLinesPerXferBuf =
 | 
						|
	_OptimizeXferSize (p->iLinesLeft, p->iLinesPerXferBuf);
 | 
						|
 | 
						|
      DBG (DBG_MSG, "_iXFerSize = %d for %d transfer(s)\n",
 | 
						|
	   (int) p->iLinesPerXferBuf * p->iBytesPerLine,
 | 
						|
	   (p->iLinesLeft + p->iLinesPerXferBuf - 1) / p->iLinesPerXferBuf);
 | 
						|
    }
 | 
						|
  DBG (DBG_MSG, "_iLinesPerXferBuf = %d\n", p->iLinesPerXferBuf);
 | 
						|
 | 
						|
  /* init transfer buffer */
 | 
						|
  XferBufferInit (iHandle, p);
 | 
						|
 | 
						|
  /* fill circular buffer */
 | 
						|
  CircBufferFill (iHandle, p, iReversedHead);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
STATIC void
 | 
						|
CircBufferExit (TDataPipe * p)
 | 
						|
{
 | 
						|
  XferBufferExit (p);
 | 
						|
  if (p->pabCircBuf != NULL)
 | 
						|
    {
 | 
						|
      DBG (DBG_MSG, "\n");
 | 
						|
      free (p->pabCircBuf);
 | 
						|
      p->pabCircBuf = NULL;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (DBG_ERR, "CircBufferExit: Circular buffer not initialised!\n");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/************************************************************************/
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
_CalcAvg (unsigned char *pabBuf, int n, int iStep)
 | 
						|
{
 | 
						|
  int i, j, x;
 | 
						|
 | 
						|
  for (i = j = x = 0; i < n; i++)
 | 
						|
    {
 | 
						|
      x += pabBuf[j];
 | 
						|
      j += iStep;
 | 
						|
    }
 | 
						|
  return (x / n);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* converts white line data and black point data into a calibration table */
 | 
						|
static void
 | 
						|
CreateCalibTable (unsigned char *abWhite, unsigned char bBlackR,
 | 
						|
		  unsigned char bBlackG, unsigned char bBlackB,
 | 
						|
		  int iReversedHead, unsigned char *pabCalibTable)
 | 
						|
{
 | 
						|
  int i, j, iGain, iOffset, iData;
 | 
						|
  unsigned char *pabPixel;
 | 
						|
 | 
						|
  j = 0;
 | 
						|
  for (i = 0; i < HW_PIXELS; i++)
 | 
						|
    {
 | 
						|
      if (iReversedHead)
 | 
						|
	{
 | 
						|
	  pabPixel = &abWhite[(HW_PIXELS - i - 1) * 3];
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  pabPixel = &abWhite[i * 3];
 | 
						|
	}
 | 
						|
      /* red */
 | 
						|
      if (bBlackR > 16)
 | 
						|
	bBlackR = 16;
 | 
						|
      iGain = 65536 / MAX (1, pabPixel[0] - bBlackR);
 | 
						|
      iOffset = bBlackR * 4;
 | 
						|
      if (iOffset > 63)
 | 
						|
	iOffset = 63;
 | 
						|
      iData = (iGain << 6) + iOffset;
 | 
						|
      pabCalibTable[j++] = (iData) & 255;
 | 
						|
      pabCalibTable[j++] = (iData >> 8) & 255;
 | 
						|
      /* green */
 | 
						|
      if (bBlackG > 16)
 | 
						|
	bBlackG = 16;
 | 
						|
      iGain = 65536 / MAX (1, pabPixel[1] - bBlackG);
 | 
						|
      iOffset = bBlackG * 4;
 | 
						|
      if (iOffset > 63)
 | 
						|
	iOffset = 63;
 | 
						|
      iData = (iGain << 6) + iOffset;
 | 
						|
      pabCalibTable[j++] = (iData) & 255;
 | 
						|
      pabCalibTable[j++] = (iData >> 8) & 255;
 | 
						|
      /* blue */
 | 
						|
      if (bBlackB > 16)
 | 
						|
	bBlackB = 16;
 | 
						|
      iGain = 65536 / MAX (1, pabPixel[2] - bBlackB);
 | 
						|
      iOffset = bBlackB * 4;
 | 
						|
      if (iOffset > 63)
 | 
						|
	iOffset = 63;
 | 
						|
      iData = (iGain << 6) + iOffset;
 | 
						|
      pabCalibTable[j++] = (iData) & 255;
 | 
						|
      pabCalibTable[j++] = (iData >> 8) & 255;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*************************************************************************
 | 
						|
  Lamp control functions
 | 
						|
*************************************************************************/
 | 
						|
STATIC SANE_Bool
 | 
						|
GetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn)
 | 
						|
{
 | 
						|
  unsigned char bData;
 | 
						|
 | 
						|
  NiashReadReg (pHWParams->iXferHandle, 0x03, &bData);
 | 
						|
  *pfLampIsOn = ((bData & 0x01) != 0);
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
STATIC SANE_Bool
 | 
						|
SetLamp (THWParams * pHWParams, SANE_Bool fLampOn)
 | 
						|
{
 | 
						|
  unsigned char bData;
 | 
						|
  int iHandle;
 | 
						|
 | 
						|
  iHandle = pHWParams->iXferHandle;
 | 
						|
 | 
						|
  NiashReadReg (iHandle, 0x03, &bData);
 | 
						|
  if (fLampOn)
 | 
						|
    {
 | 
						|
      NiashWriteReg (iHandle, 0x03, bData | 0x01);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      NiashWriteReg (iHandle, 0x03, bData & ~0x01);
 | 
						|
    }
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*************************************************************************
 | 
						|
  Experimental simple calibration, but also returning the white levels
 | 
						|
*************************************************************************/
 | 
						|
STATIC SANE_Bool
 | 
						|
SimpleCalibExt (THWParams * pHWPar, unsigned char *pabCalibTable,
 | 
						|
		unsigned char *pabCalWhite)
 | 
						|
{
 | 
						|
  unsigned char bMinR, bMinG, bMinB;
 | 
						|
  TDataPipe DataPipe;
 | 
						|
  TScanParams Params;
 | 
						|
  unsigned char abGamma[4096];
 | 
						|
  int i, j;
 | 
						|
  static unsigned char abBuf[HW_PIXELS * 3 * 71];	/* Carefull : see startWhite and endWhite below */
 | 
						|
  static unsigned char abLine[HW_PIXELS * 3];
 | 
						|
  static unsigned char abWhite[HW_PIXELS * 3];
 | 
						|
  unsigned char *pabWhite;
 | 
						|
  int iWhiteR, iWhiteG, iWhiteB;
 | 
						|
  int iHandle;
 | 
						|
  SANE_Bool iReversedHead;
 | 
						|
  int startWhiteY, endWhiteY;
 | 
						|
  int startBlackY, endBlackY;
 | 
						|
  int startBlackX, endBlackX;
 | 
						|
 | 
						|
  iHandle = pHWPar->iXferHandle;
 | 
						|
  iReversedHead = pHWPar->iReversedHead;
 | 
						|
 | 
						|
  DataPipe.iSkipLines = pHWPar->iSkipLines;
 | 
						|
 | 
						|
  Params.iDpi = HW_DPI;
 | 
						|
  Params.iLpi = HW_DPI;
 | 
						|
  if (iReversedHead)		/* hp scanners */
 | 
						|
    Params.iTop = 60;
 | 
						|
  else				/* agfa scanners */
 | 
						|
    Params.iTop = 30;
 | 
						|
  Params.iBottom = HP3300C_BOTTOM;
 | 
						|
  Params.iLeft = 0;
 | 
						|
  Params.iWidth = HW_PIXELS;
 | 
						|
  Params.iHeight = 54;
 | 
						|
  Params.fCalib = SANE_TRUE;
 | 
						|
 | 
						|
  /* write gamma table with neutral gain / offset */
 | 
						|
  CalcGamma (abGamma, 1.0);
 | 
						|
  WriteGammaCalibTable (abGamma, abGamma, abGamma, NULL, 256, 0, pHWPar);
 | 
						|
 | 
						|
  if (!InitScan (&Params, pHWPar))
 | 
						|
    {
 | 
						|
      if (pabCalWhite)
 | 
						|
	pabCalWhite[0] = pabCalWhite[1] = pabCalWhite[2] = 0;
 | 
						|
      return SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Definition of white and black areas */
 | 
						|
  if (iReversedHead)
 | 
						|
    {				/* hp scanners */
 | 
						|
      startWhiteY = 0;
 | 
						|
      endWhiteY = 15;
 | 
						|
      startBlackY = 16;
 | 
						|
      endBlackY = 135;
 | 
						|
      startBlackX = 0;
 | 
						|
      endBlackX = HW_PIXELS;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {				/* agfa scanners */
 | 
						|
      startWhiteY = 0;
 | 
						|
      endWhiteY = 70;
 | 
						|
      startBlackY = 86;
 | 
						|
      endBlackY = 135;
 | 
						|
      startBlackX = 1666;
 | 
						|
      endBlackX = 3374;
 | 
						|
    }
 | 
						|
 | 
						|
  CircBufferInit (iHandle, &DataPipe, HW_PIXELS, -1, Params.iLpi / 150,
 | 
						|
		  iReversedHead, 1, 1);
 | 
						|
  /* white level */
 | 
						|
  /* skip some lines */
 | 
						|
  for (i = 0; i < startWhiteY; i++)
 | 
						|
    {
 | 
						|
      CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
 | 
						|
    }
 | 
						|
  /* Get white lines */
 | 
						|
  for (i = 0; i < endWhiteY - startWhiteY + 1; i++)
 | 
						|
    {
 | 
						|
      CircBufferGetLine (iHandle, &DataPipe, &abBuf[i * HW_PIXELS * 3],
 | 
						|
			 iReversedHead);
 | 
						|
    }
 | 
						|
  /* black level */
 | 
						|
  bMinR = 255;
 | 
						|
  bMinG = 255;
 | 
						|
  bMinB = 255;
 | 
						|
  /* Skip some lines */
 | 
						|
  for (i = 0; i < startBlackY; i++)
 | 
						|
    {
 | 
						|
      CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
 | 
						|
    }
 | 
						|
  for (i = 0; i < endBlackY - startBlackY + 1; i++)
 | 
						|
    {
 | 
						|
      CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead);
 | 
						|
      for (j = 0; j < endBlackX; j++)
 | 
						|
	{
 | 
						|
	  bMinR = MIN (abLine[j * 3 + 0], bMinR);
 | 
						|
	  bMinG = MIN (abLine[j * 3 + 1], bMinG);
 | 
						|
	  bMinB = MIN (abLine[j * 3 + 2], bMinB);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  CircBufferExit (&DataPipe);
 | 
						|
  FinishScan (pHWPar);
 | 
						|
 | 
						|
  /* calc average white level */
 | 
						|
  pabWhite = abBuf;
 | 
						|
  for (i = 0; i < HW_PIXELS; i++)
 | 
						|
    {
 | 
						|
      abWhite[i * 3 + 0] =
 | 
						|
	_CalcAvg (&pabWhite[i * 3 + 0], endWhiteY - startWhiteY + 1,
 | 
						|
		  HW_PIXELS * 3);
 | 
						|
      abWhite[i * 3 + 1] =
 | 
						|
	_CalcAvg (&pabWhite[i * 3 + 1], endWhiteY - startWhiteY + 1,
 | 
						|
		  HW_PIXELS * 3);
 | 
						|
      abWhite[i * 3 + 2] =
 | 
						|
	_CalcAvg (&pabWhite[i * 3 + 2], endWhiteY - startWhiteY + 1,
 | 
						|
		  HW_PIXELS * 3);
 | 
						|
    }
 | 
						|
  iWhiteR = _CalcAvg (&abWhite[0], HW_PIXELS, 3);
 | 
						|
  iWhiteG = _CalcAvg (&abWhite[1], HW_PIXELS, 3);
 | 
						|
  iWhiteB = _CalcAvg (&abWhite[2], HW_PIXELS, 3);
 | 
						|
 | 
						|
  DBG (DBG_MSG, "Black level (%d,%d,%d), White level (%d,%d,%d)\n",
 | 
						|
       (int) bMinR, (int) bMinG, (int) bMinB, iWhiteR, iWhiteG, iWhiteB);
 | 
						|
 | 
						|
  /* convert the white line and black point into a calibration table */
 | 
						|
  CreateCalibTable (abWhite, bMinR, bMinG, bMinB, iReversedHead,
 | 
						|
		    pabCalibTable);
 | 
						|
  /* assign the White Levels */
 | 
						|
  if (pabCalWhite)
 | 
						|
    {
 | 
						|
      pabCalWhite[0] = iWhiteR;
 | 
						|
      pabCalWhite[1] = iWhiteG;
 | 
						|
      pabCalWhite[2] = iWhiteB;
 | 
						|
    }
 | 
						|
  return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*************************************************************************
 | 
						|
  FinishScan
 | 
						|
  ==========
 | 
						|
    Finishes the scan. Makes the scanner head move back to the home position.
 | 
						|
 | 
						|
*************************************************************************/
 | 
						|
STATIC void
 | 
						|
FinishScan (THWParams * pHWParams)
 | 
						|
{
 | 
						|
  NiashWriteReg (pHWParams->iXferHandle, 0x02, 0x80);
 | 
						|
}
 |