kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1586 wiersze
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1586 wiersze
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
|    Copyright (C) 2020 Ralph Little <skelband@gmail.com>
 | |
|    Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
 | |
|    Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
 | |
|    Copyright (c) 2003 Henning Meier-Geinitz, <henning@meier-geinitz.de>
 | |
| 
 | |
|    Originally copied from HP3300 testtools. Original notice follows:
 | |
| 
 | |
|    Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
 | |
| 
 | |
|    This file is part of the SANE package.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU General Public License
 | |
|    as published by the Free Software Foundation; either version 2
 | |
|    of the License, or (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | |
| 
 | |
|    As a special exception, the authors of SANE give permission for
 | |
|    additional uses of the libraries contained in this release of SANE.
 | |
| 
 | |
|    The exception is that, if you link a SANE library with other files
 | |
|    to produce an executable, this does not by itself cause the
 | |
|    resulting executable to be covered by the GNU General Public
 | |
|    License.  Your use of that executable is in no way restricted on
 | |
|    account of linking the SANE library code into it.
 | |
| 
 | |
|    This exception does not, however, invalidate any other reasons why
 | |
|    the executable file might be covered by the GNU General Public
 | |
|    License.
 | |
| 
 | |
|    If you submit changes to SANE to the maintainers to be included in
 | |
|    a subsequent release, you agree by submitting the changes that
 | |
|    those changes may be distributed with this exception intact.
 | |
| 
 | |
|    If you write modifications of your own for SANE, it is your choice
 | |
|    whether to permit this exception to apply to your modifications.
 | |
|    If you do not wish that, delete this exception notice.
 | |
| 
 | |
|    HP5400/5470 Test util.
 | |
|    Currently is only able to read back the scanner version string,
 | |
|    but this basically demonstrates ability to communicate with the scanner.
 | |
| 
 | |
|    Massively expanded. Can do calibration scan, upload gamma and calibration
 | |
|    tables and stores the results of a scan. - 19/02/2003 Martijn
 | |
| */
 | |
| 
 | |
| #include <stdio.h>		/* for printf */
 | |
| #include <stdlib.h>		/* for exit */
 | |
| #include <assert.h>
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <math.h>
 | |
| #include <netinet/in.h>		/* for htons */
 | |
| #include <string.h>
 | |
| 
 | |
| 
 | |
| #include "hp5400.h"
 | |
| #include "hp5400_xfer.h"
 | |
| #include "hp5400_internal.h"
 | |
| #include "hp5400_debug.h" /* debug functions */
 | |
| 
 | |
| 
 | |
| #ifndef min
 | |
| #define min(A,B) (((A)<(B)) ? (A) : (B))
 | |
| #endif
 | |
| #ifndef max
 | |
| #define max(A,B) (((A)>(B)) ? (A) : (B))
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #ifndef STRING_VERSION_MATCH
 | |
| #define NO_STRING_VERSION_MATCH 1
 | |
| #endif
 | |
| 
 | |
| #ifdef __GNUC__
 | |
| #define PACKED __attribute__ ((packed))
 | |
| #else
 | |
| #define PACKED
 | |
| #endif
 | |
| 
 | |
| /* If this is enabled, a copy of the raw data from the scanner will be saved to
 | |
|    imagedebug.dat and the attempted conversion to imagedebug.ppm */
 | |
| /* #define IMAGE_DEBUG */
 | |
| 
 | |
| /* If this is defined you get extra info on the calibration */
 | |
| /* #define CALIB_DEBUG */
 | |
| 
 | |
| 
 | |
| typedef struct versionString {
 | |
| 	char strVersion[128];
 | |
| } versionString;
 | |
| 
 | |
| const int numVersions = 3;
 | |
| versionString *MatchVersions;
 | |
| 
 | |
| 
 | |
| static TScannerModel Model_HP54xx =
 | |
|   { "Hewlett-Packard", "HP54xx Flatbed Scanner" };
 | |
| 
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| InitHp5400_internal() {
 | |
| 
 | |
| 	MatchVersions = malloc( sizeof(versionString) * numVersions );
 | |
| 	strcpy( MatchVersions[0].strVersion, "SilitekIBlizd C3 ScannerV0.84");
 | |
| 	strcpy( MatchVersions[1].strVersion, "SilitekIBlizd C3 ScannerV0.86");
 | |
| 	strcpy( MatchVersions[2].strVersion, "SilitekIBlizd C3 ScannerV0.87");
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| FreeHp5400_internal() {
 | |
| 
 | |
| 	free(MatchVersions);
 | |
| 	MatchVersions = NULL;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| WriteByte (int iHandle, int cmd, char data)
 | |
| {
 | |
|   if (hp5400_command_write (iHandle, cmd, 1, &data) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to send byte (cmd=%04X)\n", cmd);
 | |
|       return -1;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| SetLamp (THWParams * pHWParams, int fLampOn)
 | |
| {
 | |
|   if (fLampOn)
 | |
|     {
 | |
|       if (WriteByte (pHWParams->iXferHandle, 0x0000, 0x01) == 0)
 | |
|         return 0;
 | |
|     }
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| GetSensors(THWParams * pHWParams, uint16_t *sensorMap)
 | |
| {
 | |
|   /*
 | |
|    * Read until we get 0.
 | |
|    * Max 10 iterations for safety.
 | |
|    *
 | |
|    */
 | |
|   *sensorMap = 0;
 | |
|   uint16_t thisSensorMap = 0;
 | |
|   size_t iterCount = 10;
 | |
|   do
 | |
|     {
 | |
|       if (hp5400_command_read
 | |
|           (pHWParams->iXferHandle, CMD_GETSENSORS, sizeof (uint16_t), &thisSensorMap) < 0)
 | |
|         {
 | |
|           HP5400_DBG (DBG_MSG, "failed to read sensors\n");
 | |
|           return -1;
 | |
|         }
 | |
|       *sensorMap |= thisSensorMap;
 | |
|     } while (iterCount-- && (thisSensorMap > 0));
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| GetPanelInfo (THWParams * pHWParams, TPanelInfo *panelInfo)
 | |
| {
 | |
|   struct PanelInfo info;
 | |
|   if (hp5400_command_read (pHWParams->iXferHandle, CMD_READPANEL,
 | |
|                            sizeof(info), &info) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to read panel info\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   panelInfo->copycount = (SANE_Word)info.copycount;
 | |
|   panelInfo->bwcolour = (SANE_Word)info.bwcolour;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| SetCopyCount(THWParams * pHWParams, SANE_Word copyCount)
 | |
| {
 | |
| 
 | |
|   /*
 | |
|    * I don't know what most of these things are but it is
 | |
|    * necessary to send something sane otherwise we get an error from the scanner.
 | |
|    * I got these settings from a USB trace.
 | |
|    * Hopefully, we will learn what it is all about at some point
 | |
|    * and hopefully it doesn't screw with other settings.
 | |
|    *
 | |
|    */
 | |
|   uint8_t packetImage[] = {0x02, 0x06, 0x32, 0x01,
 | |
|                           0xf2, 0x40, 0x16, 0x01,
 | |
|                           0x7b, 0x41, 0x16, 0x01,
 | |
|                           0xdc, 0x06, 0x32, 0x01,
 | |
|                           0xd7, 0x5b, 0x16, 0x01,
 | |
|                           0xac, 0x06, 0x32, 0x01,
 | |
|                           0xf8, 0xd7, 0x18, 0x01,
 | |
|                           0xd8, 0x06, 0x32, 0x01,
 | |
|                           0x2c, 0xf3, 0x12, 0x00,
 | |
|                           0x70, 0x8d, 0x18, 0x01,
 | |
|                           0x7b, 0x00, 0x00, 0x00};
 | |
| 
 | |
|   struct PanelInfo workingInfo;
 | |
|   (void)memcpy(&workingInfo, packetImage, sizeof(workingInfo));
 | |
| 
 | |
|   workingInfo.copycount = (uint8_t)copyCount;
 | |
| 
 | |
|   if (hp5400_command_write (pHWParams->iXferHandle, CMD_WRITEPANEL,
 | |
|                            sizeof(workingInfo), &workingInfo) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to write panel info\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| SetColourBW(THWParams * pHWParams, SANE_Word colourBW)
 | |
| {
 | |
| 
 | |
|   /*
 | |
|    * I don't know what most of these things are but it is
 | |
|    * necessary to send something sane otherwise we get an error from the scanner.
 | |
|    * I got these settings from a USB trace.
 | |
|    * Hopefully, we will learn what it is all about at some point
 | |
|    * and hopefully it doesn't screw with other settings.
 | |
|    *
 | |
|    */
 | |
|   uint8_t packetImage[] = {0x03, 0x06, 0x32, 0x01,
 | |
|                           0xf2, 0x40, 0x16, 0x01,
 | |
|                           0x7b, 0x41, 0x16, 0x01,
 | |
|                           0xdc, 0x06, 0x32, 0x01,
 | |
|                           0xd7, 0x5b, 0x16, 0x01,
 | |
|                           0xac, 0x06, 0x32, 0x01,
 | |
|                           0xf8, 0xd7, 0x18, 0x01,
 | |
|                           0xd8, 0x06, 0x32, 0x01,
 | |
|                           0x68, 0xf5, 0x12, 0x00,
 | |
|                           0x70, 0x8d, 0x18, 0x01,
 | |
|                           0x7b, 0x00, 0x00, 0x00};
 | |
| 
 | |
|   struct PanelInfo workingInfo;
 | |
|   (void)memcpy(&workingInfo, packetImage, sizeof(workingInfo));
 | |
| 
 | |
|   workingInfo.bwcolour = (uint8_t)colourBW;
 | |
| 
 | |
|   if (hp5400_command_write (pHWParams->iXferHandle, CMD_WRITEPANEL,
 | |
|                            sizeof(workingInfo), &workingInfo) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to write panel info\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| WarmupLamp (int iHandle)
 | |
| {
 | |
|   int i = 30;			/* Max 30 seconds, 15 is typical for cold start? */
 | |
|   int couldRead;
 | |
|   unsigned char dataVerify[0x02];
 | |
| 
 | |
|   /* Keep writing 01 to 0000 until no error... */
 | |
|   unsigned char data0000[] = {
 | |
|     0x01
 | |
|   };
 | |
|   unsigned char data0300[3];
 | |
| 
 | |
| 
 | |
|   hp5400_command_write_noverify (iHandle, 0x0000, data0000, 1);
 | |
| 
 | |
|   do
 | |
|     {
 | |
|       hp5400_command_read_noverify (iHandle, 0x0300, 3, data0300);
 | |
| 
 | |
|       hp5400_command_write_noverify (iHandle, 0x0000, data0000, 1);
 | |
| 
 | |
|       couldRead =
 | |
| 	hp5400_command_read_noverify (iHandle, 0xc500, 0x02, dataVerify);
 | |
|       if ((dataVerify[0] != 0) || (dataVerify[1] != 0))
 | |
| 	sleep (1);
 | |
|     }
 | |
|   while ((i-- > 0) && (couldRead >= 0)
 | |
| 	 && ((dataVerify[0] != 0) || (dataVerify[1] != 0)));
 | |
| 
 | |
|   if (i > 0)
 | |
|     return 0;
 | |
| 
 | |
|   /*
 | |
| 
 | |
|      while( i > 0 )
 | |
|      {
 | |
|      if( WriteByte( iHandle, 0x0000, 0x01 ) == 0 )
 | |
|      return 0;
 | |
|      sleep(1);
 | |
|      i--;
 | |
|      }
 | |
|    */
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "***WARNING*** Warmup lamp failed...\n");
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| #define CALPIXBYBLOCK  42
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| SetCalibration (int iHandle, int numPixels, unsigned int *low_vals[3],
 | |
| 		unsigned int *high_vals[3], int dpi)
 | |
| {
 | |
|   char cmd[8];
 | |
|   /* unsigned char cmd[8]; */ /* should fix the compilation warning
 | |
| 				 but I don't have a scanner right now
 | |
| 				 to check that the fix does not break
 | |
| 				 calibration */
 | |
| 
 | |
|   int i, j, k;
 | |
|   struct CalPixel
 | |
|   {
 | |
|     char highr[2], highg[2], highb[2];
 | |
|     char lowr[2], lowg[2], lowb[2];
 | |
|   };
 | |
|   struct CalBlock
 | |
|   {
 | |
|     struct CalPixel pixels[CALPIXBYBLOCK];
 | |
|     char pad[8];
 | |
|   }
 | |
|   PACKED;
 | |
| 
 | |
|   struct CalBlock *calinfo;
 | |
| 
 | |
|   /**
 | |
|    we did scan test at 300 dpi, so we don't have all the needed pixels.
 | |
|    To fill the gap, we loop
 | |
|    */
 | |
|   int numLoop = max (1, dpi / 300);
 | |
|   int calBlockSize = CALPIXBYBLOCK * (6 * sizeof (short)) + 8 * sizeof (char);	/* = sizeof(calBlock) */
 | |
|   int numCalBlock =
 | |
|     ((numPixels / CALPIXBYBLOCK) +
 | |
|      ((numPixels % CALPIXBYBLOCK != 0) ? 1 : 0));
 | |
|   int calSize = numLoop * calBlockSize * numCalBlock;
 | |
| 
 | |
|   calinfo = malloc (calSize);
 | |
|   memset (calinfo, 0, calSize);
 | |
| 
 | |
|   for (j = 0; j < numLoop * numCalBlock * CALPIXBYBLOCK; j++)
 | |
|     {
 | |
|       struct CalPixel *pixel =
 | |
| 	&calinfo[j / CALPIXBYBLOCK].pixels[j % CALPIXBYBLOCK];
 | |
| 
 | |
|       /*
 | |
|          i = ( j % (int)( 0.80 * numPixels ) ) + (int)(0.10 * numPixels );
 | |
|        */
 | |
|       /* better solution : stretch the actual scan size to the calibration size */
 | |
|       i = j / numLoop;
 | |
| 
 | |
|       /* This is obviously not quite right. The values on
 | |
|        * the right are approximately what windows sends */
 | |
|       k = (high_vals[0][i] > 0x4000) ? 1000000000 / high_vals[0][i] : 0;	/* 0x6700 */
 | |
|       pixel->highr[0] = k;
 | |
|       pixel->highr[1] = k >> 8;
 | |
|       k = (high_vals[1][i] > 0x4000) ? 1000000000 / high_vals[1][i] : 0;	/* 0x5e00 */
 | |
|       pixel->highg[0] = k;
 | |
|       pixel->highg[1] = k >> 8;
 | |
|       k = (high_vals[2][i] > 0x4000) ? 1000000000 / high_vals[2][i] : 0;	/* 0x6000 */
 | |
|       pixel->highb[0] = k;
 | |
|       pixel->highb[1] = k >> 8;
 | |
| 
 | |
|       pixel->lowr[0] = low_vals[0][i];		/* 0x0530 */
 | |
|       pixel->lowr[1] = low_vals[0][i] >> 8;
 | |
|       pixel->lowg[0] = low_vals[1][i];		/* 0x0530 */
 | |
|       pixel->lowg[1] = low_vals[1][i] >> 8;
 | |
|       pixel->lowb[0] = low_vals[2][i];		/* 0x0530 */
 | |
|       pixel->lowb[1] = low_vals[2][i] >> 8;
 | |
|     }
 | |
| 
 | |
|   cmd[0] = 0xff & (calSize >> 16);
 | |
|   cmd[1] = 0xff & (calSize >> 8);
 | |
|   cmd[2] = 0xff & (calSize >> 0);
 | |
|   cmd[3] = 0x00;
 | |
|   cmd[4] = 0x54;
 | |
|   cmd[5] = 0x02;
 | |
|   cmd[6] = -128;                // 0x80; fixes compiler warning (for
 | |
|                                 // signed char implementations), see
 | |
|                                 // also comment above
 | |
|   cmd[7] = 0x00;
 | |
| 
 | |
|   hp5400_bulk_command_write (iHandle, 0xE603, cmd, 8, calSize, calSize,
 | |
| 			     (void *) calinfo);
 | |
| 
 | |
|   free (calinfo);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Write a gamma table */
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| WriteGammaCalibTable (int iHandle, const int *pabGammaR, const int *pabGammaG,
 | |
| 		      const int *pabGammaB)
 | |
| {
 | |
|   char cmd[3];
 | |
|   char *buffer;
 | |
|   int i, j;
 | |
| 
 | |
|   /* Setup dummy gamma correction table */
 | |
|   buffer = malloc (2 * 65536);
 | |
| 
 | |
|   cmd[0] = 2;
 | |
|   cmd[1] = 0;
 | |
|   cmd[2] = 0;
 | |
| 
 | |
|   for (i = 0; i < 3; i++)
 | |
|     {
 | |
|       const int *ptr = (i == 0) ? pabGammaR :
 | |
| 	(i == 1) ? pabGammaG : pabGammaB;
 | |
| 
 | |
|       for (j = 0; j < 65536; j++) {
 | |
| 	buffer[2 * j] = ptr[j];
 | |
| 	buffer[2 * j + 1] = ptr[j] >> 8;
 | |
|       }
 | |
| 
 | |
|       hp5400_bulk_command_write (iHandle, 0x2A01 + i, cmd, 3, 2 * 65536,
 | |
| 				 65536, (void *) buffer);
 | |
|     }
 | |
|   free (buffer);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| #ifdef STANDALONE
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| SetDefaultGamma (int iHandle)
 | |
| {
 | |
|   int *buffer = malloc (sizeof (int) * 65536);
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < 65336; i++)
 | |
|     buffer[i] = i;
 | |
| 
 | |
|   WriteGammaCalibTable (iHandle, buffer, buffer, buffer);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #define BUFFER_SIZE (6*65536)
 | |
| 
 | |
| #ifdef IMAGE_DEBUG
 | |
| FILE *temp;
 | |
| #endif
 | |
| 
 | |
| /* Bytes per line is the number of pixels. The actual bytes is one more */
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| CircBufferInit (int iHandle, TDataPipe * p, int iBytesPerLine,
 | |
| 		int bpp, int iMisAlignment, int blksize, int iTransferSize)
 | |
| {
 | |
|   iHandle = iHandle; /* to avoid compilation warning */
 | |
|   p->buffersize = max (BUFFER_SIZE, 3 * blksize);
 | |
| 
 | |
|   if (p->buffer)
 | |
|     {
 | |
|       free (p->buffer);
 | |
|     }
 | |
| 
 | |
|   /* Allocate a large enough buffer for transfer */
 | |
|   p->buffer = malloc (p->buffersize);
 | |
| 
 | |
|   p->pixels = (iBytesPerLine / 3) / bpp;
 | |
| 
 | |
|   /* These three must always be positive */
 | |
|   p->roff = 0;
 | |
|   p->goff = p->pixels * bpp + 1;
 | |
|   p->boff = 2 * p->pixels * bpp + 2;;
 | |
| 
 | |
|   p->linelength = iBytesPerLine + 3;	/* NUL at end of each row */
 | |
|   p->bpp = bpp;
 | |
|   p->bufstart = p->bufend = 0;
 | |
| 
 | |
|   if (iMisAlignment > 0)
 | |
|     {
 | |
|       p->roff += 0;
 | |
|       p->goff += p->linelength * iMisAlignment;
 | |
|       p->boff += p->linelength * iMisAlignment * 2;
 | |
|     }
 | |
| 
 | |
|   if (iMisAlignment < 0)
 | |
|     {
 | |
|       p->roff -= p->linelength * iMisAlignment * 2;
 | |
|       p->goff -= p->linelength * iMisAlignment;
 | |
|       p->boff -= 0;
 | |
|     }
 | |
| 
 | |
|   p->blksize = blksize;
 | |
|   p->transfersize = iTransferSize;
 | |
| 
 | |
| #ifdef IMAGE_DEBUG
 | |
|   temp = fopen ("imagedebug.dat", "w+b");
 | |
| #endif
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG,
 | |
|        "Begin: line=%d (%X), pixels=%d (%X), r=%d (%X), g=%d (%X), b=%d (%X), bpp=%d, step=%d\n",
 | |
|        p->linelength, p->linelength, p->pixels, p->pixels, p->roff, p->roff,
 | |
|        p->goff, p->goff, p->boff, p->boff, bpp, iMisAlignment);
 | |
| }
 | |
| 
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| CircBufferGetLine (int iHandle, TDataPipe * p, void *pabLine)
 | |
| {
 | |
|   int i;
 | |
|   int maxoff = 0;
 | |
|   char* buftmp = (char*) (p->buffer);
 | |
| 
 | |
| /*  HP5400_DBG(DBG_MSG, "CircBufferGetLine:\n");   */
 | |
| 
 | |
|   if (p->roff > maxoff)
 | |
|     maxoff = p->roff;
 | |
|   if (p->goff > maxoff)
 | |
|     maxoff = p->goff;
 | |
|   if (p->boff > maxoff)
 | |
|     maxoff = p->boff;
 | |
| 
 | |
|   maxoff += p->pixels * p->bpp;
 | |
| 
 | |
|   if (maxoff < p->linelength)
 | |
|     maxoff = p->linelength;
 | |
| 
 | |
| 
 | |
|   /* resize buffer if needed */
 | |
|   if (p->bufstart + maxoff >= p->buffersize + p->blksize)
 | |
|     {
 | |
|       /* store actual buffer pointer */
 | |
|       void *tmpBuf = p->buffer;
 | |
|       /* calculate new size for buffer (oversize a bit) */
 | |
|       int newsize = p->bufstart + maxoff + 2 * p->blksize;
 | |
| 
 | |
|       p->buffer = malloc (newsize);
 | |
|       memcpy (p->buffer, tmpBuf, p->buffersize);
 | |
|       p->buffersize = newsize;
 | |
| 
 | |
|       /* free old buffer */
 | |
|       free (tmpBuf);
 | |
|       buftmp = (char*)(p->buffer);
 | |
|     }
 | |
| 
 | |
|   while (p->bufstart + maxoff >= p->bufend)	/* Not enough data in buffer */
 | |
|     {
 | |
|       int res;
 | |
|       unsigned char cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 | |
|       cmd[4] = p->blksize;
 | |
|       cmd[5] = p->blksize >> 8;
 | |
| 
 | |
|       assert ((p->bufend + p->blksize) <= p->buffersize);
 | |
| 
 | |
|       HP5400_DBG (DBG_MSG, "Reading block, %d bytes remain\n", p->transfersize);
 | |
|       p->transfersize -= p->blksize;
 | |
| 
 | |
|       res =
 | |
| 	hp5400_bulk_read_block (iHandle, CMD_INITBULK3, cmd, sizeof (cmd),
 | |
|     				buftmp + p->bufend, p->blksize);
 | |
|       if (res != p->blksize)
 | |
| 	{
 | |
| 	  HP5400_DBG (DBG_ERR, "*** ERROR: Read returned %d. FATAL.\n", res);
 | |
| 	  return -1;
 | |
| 	}
 | |
| #ifdef IMAGE_DEBUG
 | |
|       fwrite (
 | |
|     	buftmp + p->bufend
 | |
| 	,p->blksize
 | |
| 	,1
 | |
| 	,temp
 | |
| 	);
 | |
| #endif
 | |
|       p->bufend += p->blksize;
 | |
|     }
 | |
| 
 | |
|   assert (p->bufstart + maxoff < p->bufend);
 | |
| 
 | |
|   /* Copy a line into the result buffer */
 | |
|   if (p->bpp == 1)
 | |
|     {
 | |
|       char *itPix = (char *) pabLine;
 | |
|       char *itR = (char *) (buftmp + p->bufstart + p->roff);
 | |
|       char *itG = (char *) (buftmp + p->bufstart + p->goff);
 | |
|       char *itB = (char *) (buftmp + p->bufstart + p->boff);
 | |
|       for (i = 0; i < p->pixels; i++)
 | |
| 	{
 | |
| 	  /* pointer move goes a little bit faster than vector access */
 | |
| 	  /* Although I wouldn't be surprised if the compiler worked that out anyway.
 | |
| 	   * No matter, this is easier to read :) */
 | |
| 	  *(itPix++) = *(itR++);
 | |
| 	  *(itPix++) = *(itG++);
 | |
| 	  *(itPix++) = *(itB++);
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       short *itPix = (short *) pabLine;
 | |
|       short *itR = (short *) (buftmp + p->bufstart + p->roff);
 | |
|       short *itG = (short *) (buftmp + p->bufstart + p->goff);
 | |
|       short *itB = (short *) (buftmp + p->bufstart + p->boff);
 | |
|       for (i = 0; i < p->pixels; i++)
 | |
| 	{
 | |
| #if 0
 | |
| 	  /* This code, while correct for PBM is not correct for the host and
 | |
| 	   * since we need it correct for calibration, it has to go */
 | |
| 	  ((short *) pabLine)[3 * i + 0] =
 | |
| 	    *((short *) (p->buffer + p->bufstart + p->roff + 2 * i));
 | |
| 	  ((short *) pabLine)[3 * i + 1] =
 | |
| 	    *((short *) (p->buffer + p->bufstart + p->goff + 2 * i));
 | |
| 	  ((short *) pabLine)[3 * i + 2] =
 | |
| 	    *((short *) (p->buffer + p->bufstart + p->boff + 2 * i));
 | |
| #else
 | |
| 	  /* pointer move goes a little bit faster than vector access */
 | |
| 	  *(itPix++) = htons (*(itR++));
 | |
| 	  *(itPix++) = htons (*(itG++));
 | |
| 	  *(itPix++) = htons (*(itB++));
 | |
| #endif
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   p->bufstart += p->linelength;
 | |
| 
 | |
|   assert (p->bufstart <= p->bufend);
 | |
| 
 | |
|   /* If we've used a whole block at the beginning, move it */
 | |
|   if (p->bufstart > p->blksize)
 | |
|     {
 | |
|       memmove (
 | |
| 	buftmp
 | |
| 	,buftmp + p->bufstart
 | |
| 	,p->bufend - p->bufstart
 | |
| 	);
 | |
| 
 | |
|       p->bufend -= p->bufstart;
 | |
|       p->bufstart = 0;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| CircBufferExit (TDataPipe * p)
 | |
| {
 | |
|   free (p->buffer);
 | |
|   p->buffer = NULL;
 | |
| #ifdef IMAGE_DEBUG
 | |
|   fclose (temp);
 | |
|   temp = NULL;
 | |
| #endif
 | |
|   return;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef STANDALONE
 | |
| /* bpp is BYTES per pixel */
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| DecodeImage (FILE * file, int planes, int bpp, int xsize, int ysize,
 | |
| 	     const char *filename)
 | |
| {
 | |
|   char *in, *buf;
 | |
|   char *p[3];
 | |
|   FILE *output;
 | |
|   int i, j, k;
 | |
| 
 | |
|   /* xsize is byte width, not pixel width */
 | |
|   xsize /= planes * bpp;
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG,
 | |
|        "DecodeImage(planes=%d,bpp=%d,xsize=%d,ysize=%d) => %d (file=%s)\n",
 | |
|        planes, bpp, xsize, ysize, planes * bpp * xsize * ysize, filename);
 | |
| 
 | |
|   in = malloc (planes * (xsize * bpp + 1));
 | |
| 
 | |
|   for (i = 0; i < planes; i++)
 | |
|     p[i] = in + i * (xsize * bpp + 1);
 | |
| 
 | |
|   buf = malloc (3 * xsize * bpp);
 | |
| 
 | |
|   output = fopen (filename, "wb");
 | |
| 
 | |
|   fprintf (output, "P%d\n%d %d\n", (planes == 3) ? 6 : 5, xsize, ysize);
 | |
|   fprintf (output, "%d\n", (bpp == 1) ? 255 : 0xb000);
 | |
| 
 | |
|   for (i = 0; i < ysize; i++)
 | |
|     {
 | |
|       fread (in, planes * (xsize * bpp + 1), 1, file);
 | |
| 
 | |
|       for (j = 0; j < xsize; j++)
 | |
| 	{
 | |
| 	  for (k = 0; k < planes; k++)
 | |
| 	    {
 | |
| 	      if (bpp == 1)
 | |
| 		{
 | |
| 		  buf[j * planes + k] = p[k][j];
 | |
| 		}
 | |
| 	      else if (bpp == 2)
 | |
| 		{
 | |
| 		  buf[j * planes * 2 + k * 2 + 0] = p[k][2 * j + 0];
 | |
| 		  buf[j * planes * 2 + k * 2 + 1] = p[k][2 * j + 1];
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|       fwrite (buf, planes * xsize * bpp, 1, output);
 | |
|     }
 | |
| 
 | |
|   fclose (output);
 | |
| 
 | |
|   free (in);
 | |
|   free (buf);
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| hp5400_test_scan_response (struct ScanResponse *resp, struct ScanRequest *req)
 | |
| {
 | |
|   req = req; /* to avoid compilation warning */
 | |
|   HP5400_DBG (DBG_MSG, "Scan response:\n");
 | |
|   HP5400_DBG (DBG_MSG, "  transfersize=%d   htonl-> %d\n", resp->transfersize,
 | |
|        htonl (resp->transfersize));
 | |
|   HP5400_DBG (DBG_MSG, "  xsize=%d    htonl-> %d\n", resp->xsize,
 | |
|        htonl (resp->xsize));
 | |
|   HP5400_DBG (DBG_MSG, "  ysize=%d    htons-> %d\n", resp->ysize,
 | |
|        htons (resp->ysize));
 | |
|   return 1;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* This is a very specialised scanning function. It does the scan request as
 | |
|  * usual but instead of producing an image it returns three arrays of ints.
 | |
|  * These are averages of the R,G,B values for each column.
 | |
|  *
 | |
|  * Note the array argument should point to an array of three NULL. These
 | |
|  * will be overwritten with allocated pointers. */
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| DoAverageScan (int iHandle, struct ScanRequest *req, int code,
 | |
| 	       unsigned int **array)
 | |
| {
 | |
|   THWParams HWParams;
 | |
|   struct ScanResponse res;
 | |
|   unsigned short *buffer;
 | |
|   int i, j, k, length;
 | |
| 
 | |
|   memset (&HWParams, 0, sizeof (HWParams));
 | |
| 
 | |
|   HWParams.iXferHandle = iHandle;
 | |
| 
 | |
|   if (InitScan2 (SCAN_TYPE_CALIBRATION, req, &HWParams, &res, 0, code) != 0)
 | |
|     return -1;			/* No colour offsetting, we want raw */
 | |
| 
 | |
|   length = htonl (res.xsize) / 6;
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "Calibration scan: %d pixels wide\n", length);
 | |
| 
 | |
|   for (j = 0; j < 3; j++)
 | |
|     {
 | |
|       array[j] = malloc (sizeof (int) * length);
 | |
|       memset (array[j], 0, sizeof (int) * length);	/* Clear array */
 | |
|     }
 | |
| 
 | |
|   buffer = malloc (htonl (res.xsize) + 1);
 | |
| 
 | |
|   /* First we just sum them all */
 | |
|   for (i = 0; i < htons (res.ysize); i++)
 | |
|     {
 | |
|       CircBufferGetLine (iHandle, &HWParams.pipe, buffer);
 | |
| 
 | |
|       for (j = 0; j < length; j++)
 | |
| 	for (k = 0; k < 3; k++)
 | |
| 	  array[k][j] += buffer[3 * j + k];
 | |
|     }
 | |
| 
 | |
|   free (buffer);
 | |
|   FinishScan (&HWParams);
 | |
| 
 | |
|   /* Now divide by the height to get the average */
 | |
|   for (j = 0; j < length; j++)
 | |
|     for (k = 0; k < 3; k++)
 | |
|       array[k][j] /= htons (res.ysize);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #ifdef STANDALONE
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| DoScan (int iHandle, struct ScanRequest *req, const char *filename, int code,
 | |
| 	struct ScanResponse *res)
 | |
| {
 | |
|   FILE *file;
 | |
|   THWParams HWParams;
 | |
|   struct ScanResponse res_temp;
 | |
|   void *buffer;
 | |
| /*  int bpp, planes; */
 | |
|   int i;
 | |
| 
 | |
|   code = code; /*to avoid compilation warning*/
 | |
| 
 | |
|   if (res == NULL)
 | |
|     res = &res_temp;
 | |
| 
 | |
|   memset (&HWParams, 0, sizeof (HWParams));
 | |
| 
 | |
|   file = fopen (filename, "w+b");
 | |
|   if (!file)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "Couldn't open outputfile (%s)\n", strerror (errno));
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   HWParams.iXferHandle = iHandle;
 | |
| 
 | |
|   if (InitScan2 (SCAN_TYPE_NORMAL, req, &HWParams, res, 1, 0x40) != 0)
 | |
|     return -1;
 | |
| 
 | |
|   fprintf (file, "P%d\n%d %d\n", 6, htonl (res->xsize) / 3,
 | |
| 	   htons (res->ysize));
 | |
|   fprintf (file, "%d\n", 255);
 | |
| 
 | |
|   buffer = malloc (htonl (res->xsize) + 1);
 | |
|   for (i = 0; i < htons (res->ysize); i++)
 | |
|     {
 | |
|       CircBufferGetLine (iHandle, &HWParams.pipe, buffer);
 | |
| 
 | |
|       fwrite (buffer, htonl (res->xsize), 1, file);
 | |
|     }
 | |
|   free (buffer);
 | |
| 
 | |
|   FinishScan (&HWParams);
 | |
| 
 | |
|   fclose (file);
 | |
|   return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| Calibrate (int iHandle, int dpi)
 | |
| {
 | |
|   struct ScanRequest req;
 | |
| 
 | |
|   unsigned int *low_array[3];
 | |
|   unsigned int *high_array[3];
 | |
| #ifdef CALIB_DEBUG
 | |
|   char buffer[512];
 | |
|   int i, j;
 | |
| #endif
 | |
| 
 | |
| /* The first calibration scan. Finds maximum of each CCD */
 | |
|   memset(&req, 0, sizeof(req));
 | |
| 
 | |
|   req.x1 = 0x08;
 | |
|   req.dpix = htons (300);	/* = 300 dpi */
 | |
|   req.dpiy = htons (300);	/* = 300 dpi */
 | |
|   req.offx = htons (0);		/* = 0cm */
 | |
|   req.offy = htons (0);		/* = 0cm */
 | |
|   req.lenx = htons (2690);	/* = 22.78cm */
 | |
|   req.leny = htons (50);	/* =  0.42cm */
 | |
| 
 | |
|   req.flags1 = htons (0x0000);
 | |
|   req.flags2 = htons (0x0010);
 | |
|   req.flags3 = htons (0x3020);	/* First calibration scan, 48bpp */
 | |
| 
 | |
|   req.gamma[0] = htons (100);
 | |
|   req.gamma[1] = htons (100);
 | |
|   req.gamma[2] = htons (100);
 | |
| 
 | |
|   if (DoAverageScan (iHandle, &req, 0x40, high_array) != 0)
 | |
|     return -1;
 | |
| 
 | |
| #ifdef CALIB_DEBUG
 | |
|   for (i = 0; i < 3; i++)
 | |
|     {
 | |
|       int len;
 | |
|       buffer[0] = 0;
 | |
|       sprintf (buffer, "Average %d: \n", i);
 | |
|       len = strlen (buffer);
 | |
| 
 | |
|       for (j = 0; j < 24; j++)
 | |
| 	{
 | |
| 	  sprintf (buffer + len, "%04X ", high_array[i][j]);
 | |
| 	  len += 5;
 | |
| 	}
 | |
|       strcat (buffer, " ... \n");
 | |
|       len += 6;
 | |
| 
 | |
|       for (j = 1000; j < 1024; j++)
 | |
| 	{
 | |
| 	  sprintf (buffer + len, "%04X ", high_array[i][j]);
 | |
| 	  len += 5;
 | |
| 	}
 | |
|       strcat (buffer, " ... \n");
 | |
|       len += 6;
 | |
| 
 | |
|       for (j = 2000; j < 2024; j++)
 | |
| 	{
 | |
| 	  sprintf (buffer + len, "%04X ", high_array[i][j]);
 | |
| 	  len += 5;
 | |
| 	}
 | |
|       strcat (buffer, " ... \n");
 | |
|       len += 6;
 | |
| 
 | |
|       HP5400_DBG (DBG_MSG, buffer);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
| /* The second calibration scan. Finds minimum of each CCD */
 | |
|   memset(&req, 0, sizeof(req));
 | |
| 
 | |
|   req.x1 = 0x08;
 | |
|   req.dpix = htons (300);	/* = 300 dpi */
 | |
|   req.dpiy = htons (300);	/* = 300 dpi */
 | |
|   req.offx = htons (0);		/* = 0cm */
 | |
|   req.offy = htons (0);		/* = 0cm */
 | |
|   req.lenx = htons (2690);	/* = 22.78cm */
 | |
|   req.leny = htons (16);	/* =  0.14cm */
 | |
| 
 | |
|   req.flags1 = htons (0x0000);
 | |
|   req.flags2 = htons (0x0010);
 | |
|   req.flags3 = htons (0x3024);	/* Second calibration scan, 48bpp */
 | |
| 
 | |
|   req.gamma[0] = htons (100);
 | |
|   req.gamma[1] = htons (100);
 | |
|   req.gamma[2] = htons (100);
 | |
| 
 | |
|   if (DoAverageScan (iHandle, &req, 0x00, low_array) != 0)
 | |
|     return -1;
 | |
| 
 | |
| #ifdef CALIB_DEBUG
 | |
|   for (i = 0; i < 3; i++)
 | |
|     {
 | |
|       int len;
 | |
|       buffer[0] = 0;
 | |
|       sprintf (buffer, "Average %d: \n", i);
 | |
|       len = strlen (buffer);
 | |
| 
 | |
|       for (j = 0; j < 24; j++)
 | |
| 	{
 | |
| 	  sprintf (buffer + len, "%04X ", low_array[i][j]);
 | |
| 	  len += 5;
 | |
| 	}
 | |
|       strcat (buffer, " ... \n");
 | |
|       len += 6;
 | |
| 
 | |
|       for (j = 1000; j < 1024; j++)
 | |
| 	{
 | |
| 	  sprintf (buffer + len, "%04X ", low_array[i][j]);
 | |
| 	  len += 5;
 | |
| 	}
 | |
|       strcat (buffer, " ... \n");
 | |
|       len += 6;
 | |
| 
 | |
|       for (j = 2000; j < 2024; j++)
 | |
| 	{
 | |
| 	  sprintf (buffer + len, "%04X ", low_array[i][j]);
 | |
| 	  len += 5;
 | |
| 	}
 | |
|       strcat (buffer, " ... \n");
 | |
|       len += 6;
 | |
| 
 | |
|       HP5400_DBG (DBG_MSG, buffer);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   SetCalibration (iHandle, 2690, low_array, high_array, dpi);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #ifdef STANDALONE
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| hp5400_scan (int iHandle, TScanParams * params, THWParams * pHWParams,
 | |
| 	     const char *filename)
 | |
| {
 | |
|   struct ScanRequest req;
 | |
|   struct ScanResponse res;
 | |
|   int result;
 | |
| 
 | |
|   pHWParams = pHWParams; /*to avoid compilation warning*/
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "\n");
 | |
|   HP5400_DBG (DBG_MSG, "Scanning :\n");
 | |
|   HP5400_DBG (DBG_MSG, "   dpi(x) : %d\n", params->iDpi);
 | |
|   HP5400_DBG (DBG_MSG, "   dpi(y) : %d\n", params->iLpi);
 | |
|   HP5400_DBG (DBG_MSG, "   x0 : %d\n", params->iLeft);
 | |
|   HP5400_DBG (DBG_MSG, "   y0 : %d\n", params->iTop);
 | |
|   HP5400_DBG (DBG_MSG, "   width : %d\n", params->iWidth);
 | |
|   HP5400_DBG (DBG_MSG, "   height : %d\n", params->iHeight);
 | |
|   HP5400_DBG (DBG_MSG, "\n");
 | |
| 
 | |
|   memset(&req, 0, sizeof(req));
 | |
| 
 | |
|   req.x1 = 0x08;
 | |
|   req.dpix = htons (params->iDpi);
 | |
|   req.dpiy = htons (params->iLpi);
 | |
| 
 | |
|   /* These offsets and lengths should all be in the reference DPI which
 | |
|    * is set to HW_LPI */
 | |
|   req.offx = htons (params->iLeft);
 | |
|   req.offy = htons (params->iTop);
 | |
|   req.lenx = htons (params->iWidth);
 | |
|   req.leny = htons (params->iHeight);
 | |
| 
 | |
|   req.flags1 = htons (0x0080);
 | |
|   req.flags2 = htons (0x0000);
 | |
|   req.flags3 = htons ((params->iDpi < 2400) ? 0x18E8 : 0x18C0);	/* Try preview scan */
 | |
| 
 | |
|   req.gamma[0] = htons (100);
 | |
|   req.gamma[1] = htons (100);
 | |
|   req.gamma[2] = htons (100);
 | |
| 
 | |
|   if (Calibrate (iHandle, params->iDpi) != 0)
 | |
|     return -1;
 | |
|   SetDefaultGamma (iHandle);
 | |
| 
 | |
|   result = DoScan (iHandle, &req, filename, 0x40, &res);
 | |
| 
 | |
|   /* Pass the results back to the parent */
 | |
|   params->iBytesPerLine = htonl (res.xsize);
 | |
|   params->iLines = htons (res.ysize);
 | |
| 
 | |
| #if 0
 | |
|   imageFile = fopen ("output.dat", "r+b");
 | |
|   if (imageFile)
 | |
|     {
 | |
|       int planes = 3;
 | |
|       int bpp = 1;
 | |
|       fseek (imageFile, 0, SEEK_SET);
 | |
|       DecodeImage (imageFile, planes, bpp, planes * bpp * params->iWidth,
 | |
| 		   params->iHeight, filename);
 | |
|       fclose (imageFile);
 | |
|     }
 | |
| #endif
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| PreviewScan (int iHandle)
 | |
| {
 | |
|   TScanParams params;
 | |
|   THWParams pHWParams;
 | |
| 
 | |
|   /* Reference LPI is 300dpi, remember */
 | |
|   params.iDpi = 75;
 | |
|   params.iLpi = 75;
 | |
|   params.iLeft = 0;
 | |
|   params.iTop = 0;
 | |
|   params.iWidth = 2552;		/* = 21.61cm * 300dpi */
 | |
|   params.iHeight = 3510;	/* = 29.72cm * 300dpi */
 | |
|   params.iColourOffset = 1;
 | |
| 
 | |
|   return hp5400_scan (iHandle, ¶ms, &pHWParams, "output.ppm");
 | |
| }
 | |
| 
 | |
| 
 | |
| static char UISetup1[] = {
 | |
| /* Offset 40 */
 | |
|   0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x14, 0x00,
 | |
|     0x52, 0x65, 0x61, 0x64,
 | |
|   0x79, 0x00, 0x53, 0x63, 0x61, 0x6E, 0x6E, 0x65, 0x72, 0x20, 0x4C, 0x6F,
 | |
|     0x63, 0x6B, 0x65, 0x64,
 | |
|   0x00, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x00, 0x43, 0x61, 0x6E, 0x63, 0x65,
 | |
|     0x6C, 0x69, 0x6E, 0x67,
 | |
|   0x14, 0x00, 0x50, 0x6F, 0x77, 0x65, 0x72, 0x53, 0x61, 0x76, 0x65, 0x20,
 | |
|     0x4F, 0x6E, 0x00, 0x53,
 | |
| };
 | |
| 
 | |
| static char UISetup2[] = {
 | |
| 
 | |
|   0x63, 0x61, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x14, 0x00, 0x41, 0x44, 0x46,
 | |
|     0x20, 0x70, 0x61, 0x70,
 | |
|   0x65, 0x72, 0x20, 0x6A, 0x61, 0x6D, 0x00, 0x43, 0x6F, 0x70, 0x69, 0x65,
 | |
|     0x73, 0x00, 0x00,
 | |
| };
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| InitScanner (int iHandle)
 | |
| {
 | |
|   WarmupLamp (iHandle);
 | |
| 
 | |
|   if (WriteByte (iHandle, 0xF200, 0x40) < 0)
 | |
|     return -1;
 | |
| 
 | |
|   if (hp5400_command_write (iHandle, 0xF10B, sizeof (UISetup1), UISetup1) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to send UISetup1 (%lu)\n", (u_long) sizeof (UISetup1));
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (WriteByte (iHandle, 0xF200, 0x00) < 0)
 | |
|     return -1;
 | |
| 
 | |
|   if (hp5400_command_write (iHandle, 0xF10C, sizeof (UISetup2), UISetup2) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to send UISetup2\n");
 | |
|       return -1;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Warning! The caller must have configured the gamma tables at this stage */
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| InitScan (enum ScanType scantype, TScanParams * pParams,
 | |
| 	  THWParams * pHWParams)
 | |
| {
 | |
|   struct ScanRequest req;
 | |
|   struct ScanResponse res;
 | |
|   int ret;
 | |
| 
 | |
|   memset(&req, 0, sizeof(req));
 | |
| 
 | |
|   req.x1 = 0x08;
 | |
|   req.dpix = htons (pParams->iDpi);	/* = 300 dpi */
 | |
|   req.dpiy = htons (pParams->iLpi);	/* = 300 dpi */
 | |
|   req.offx = htons (pParams->iLeft);	/* = 0cm */
 | |
|   req.offy = htons (pParams->iTop);	/* = 0cm */
 | |
|   req.lenx = htons (pParams->iWidth);	/* = 22.78cm */
 | |
| 
 | |
|   /* Scan a few extra rows so we can colour offset properly. At 300dpi the
 | |
|    * difference is 4 lines */
 | |
| 
 | |
|   req.leny = htons (pParams->iHeight);	/* =  0.42cm */
 | |
| 
 | |
|   req.flags1 = htons ((scantype == SCAN_TYPE_CALIBRATION) ? 0x0000 : 0x0080);
 | |
|   req.flags2 = htons ((scantype == SCAN_TYPE_CALIBRATION) ? 0x0010 :
 | |
| 		      (scantype == SCAN_TYPE_PREVIEW) ? 0x0000 :
 | |
| 		      /* SCAN_TYPE_NORMAL  */ 0x0040);
 | |
|   req.flags3 = htons (0x18E8);
 | |
| 
 | |
|   req.gamma[0] = htons (100);
 | |
|   req.gamma[1] = htons (100);
 | |
|   req.gamma[2] = htons (100);
 | |
| 
 | |
|   if (Calibrate (pHWParams->iXferHandle, pParams->iDpi) != 0)
 | |
|     return -1;
 | |
| /*  SetDefaultGamma( pHWParams->iXferHandle ); ** Must be done by caller */
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "Calibration complete\n");
 | |
|   ret =
 | |
|     InitScan2 (scantype, &req, pHWParams, &res, pParams->iColourOffset, 0x40);
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "InitScan2 returned %d\n", ret);
 | |
| 
 | |
|   /* Pass the results back to the parent */
 | |
|   pParams->iBytesPerLine = htonl (res.xsize);
 | |
| 
 | |
|   /* Hide the extra lines we're scanning */
 | |
|   pParams->iLines = htons (res.ysize);
 | |
| 
 | |
|   return ret;			/* 0 is good, -1 is bad */
 | |
| }
 | |
| 
 | |
| /* For want of a better name ... */
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| InitScan2 (enum ScanType scantype, struct ScanRequest *req,
 | |
| 	   THWParams * pHWParams, struct ScanResponse *result,
 | |
| 	   int iColourOffset, int code)
 | |
| {
 | |
|   struct ScanResponse res;
 | |
|   int iHandle = pHWParams->iXferHandle;
 | |
| 
 | |
|   memset(&res, 0, sizeof(res));
 | |
| 
 | |
|   /* Protect scanner from damage. This stops stpuid errors.  It basically
 | |
|    * limits you to the scanner glass. Stuff like calibrations which need
 | |
|    * more access do it safely by fiddling other parameters. Note you can
 | |
|    * still break things by fiddling the ScanOffset, but that is not yet
 | |
|    * under user control */
 | |
| 
 | |
|   if (scantype != SCAN_TYPE_CALIBRATION)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "Off(%d,%d) : Len(%d,%d)\n", htons (req->offx),
 | |
| 	   htons (req->offy), htons (req->lenx), htons (req->leny));
 | |
|       /* Yes, all the htons() is silly but we want this check as late as possible */
 | |
|       if (htons (req->offx) > 0x09F8)
 | |
| 	req->offx = htons (0x09F8);
 | |
|       if (htons (req->offy) > 0x0DB6)
 | |
| 	req->offy = htons (0x0DB6);
 | |
|       /* These tests are meaningless as htons() returns unsigned
 | |
|          if( htons( req->offx ) < 0      ) req->offx = 0;
 | |
|          if( htons( req->offy ) < 0      ) req->offy = 0;
 | |
|        */
 | |
|       if (htons (req->offx) + htons (req->lenx) > 0x09F8)
 | |
| 	req->lenx = htons (0x09F8 - htons (req->offx));
 | |
|       if (htons (req->offy) + htons (req->leny) > 0x0DB6)
 | |
| 	req->leny = htons (0x0DB6 - htons (req->offy));
 | |
|       if (htons (req->lenx) <= 1)
 | |
| 	return -1;
 | |
|       if (htons (req->leny) <= 1)
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|   WarmupLamp (iHandle);
 | |
| 
 | |
|   {				/* Try to set it to make scan succeed, URB 53 *//*  0x1B01 => 0x40  */
 | |
|     /* I think this tries to cancel any existing scan */
 | |
|     char flag = 0x40;
 | |
|     if (hp5400_command_write (iHandle, CMD_STOPSCAN, sizeof (flag), &flag) <
 | |
| 	0)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "failed to cancel scan flag\n");
 | |
| 	return -1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   {				/* Try to set it to make scan succeed, URB 55  */
 | |
|     char data[4] = { 0x02, 0x03, 0x03, 0x3C };
 | |
|     if (hp5400_command_write (iHandle, CMD_UNKNOWN3, sizeof (data), data) < 0)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "failed to set unknown1\n");
 | |
| 	return -1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   {				/* Try to set it to make scan succeed, URB 59 */
 | |
|     char flag = 0x04;
 | |
|     if (hp5400_command_write (iHandle, CMD_UNKNOWN2, sizeof (flag), &flag) <
 | |
| 	0)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "failed to set unknown2\n");
 | |
| 	return -1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   {				/* URB 67 *//* Reference DPI = 300 currently */
 | |
|     short dpi = htons (HW_LPI);
 | |
|     if (hp5400_command_write (iHandle, CMD_SETDPI, sizeof (dpi), &dpi) < 0)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "failed to set dpi\n");
 | |
| 	return -1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   if (scantype != SCAN_TYPE_CALIBRATION)
 | |
|     {				/* Setup scan offsets - Should only apply to non-calibration scans */
 | |
|       short offsets[2];
 | |
| 
 | |
|       offsets [0] = htons (0x0054);
 | |
|       offsets [1] = htons (0x0282);
 | |
| 
 | |
|       if (hp5400_command_write
 | |
| 	  (iHandle, CMD_SETOFFSET, sizeof (offsets), offsets) < 0)
 | |
| 	{
 | |
| 	  HP5400_DBG (DBG_MSG, "failed to set offsets\n");
 | |
| 	  return -1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "Scan request: \n  ");
 | |
|   {
 | |
|     size_t i;
 | |
|     for (i = 0; i < sizeof (*req); i++)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) req)[i]);
 | |
|       }
 | |
|     HP5400_DBG (DBG_MSG, "\n");
 | |
|   }
 | |
| 
 | |
|   if (hp5400_command_write
 | |
|       (iHandle,
 | |
|        (scantype !=
 | |
| 	SCAN_TYPE_CALIBRATION) ? CMD_SCANREQUEST2 : CMD_SCANREQUEST,
 | |
|        sizeof (*req), req) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to send scan request\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   {				/* Try to set it to make scan succeed, URB 71 */
 | |
|     char flag = code;		/* Start scan with light on or off as requested */
 | |
|     if (hp5400_command_write (iHandle, CMD_STARTSCAN, sizeof (flag), &flag) <
 | |
| 	0)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "failed to set gamma flag\n");
 | |
| 	return -1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   if (hp5400_command_read (iHandle, CMD_SCANRESPONSE, sizeof (res), &res) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to read scan response\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "Scan response: \n  ");
 | |
|   {
 | |
|     size_t i;
 | |
|     for (i = 0; i < sizeof (res); i++)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) &res)[i]);
 | |
|       }
 | |
|     HP5400_DBG (DBG_MSG, "\n");
 | |
|   }
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "Bytes to transfer: %d\nBitmap resolution: %d x %d\n",
 | |
|        htonl (res.transfersize), htonl (res.xsize), htons (res.ysize));
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG, "Proceeding to scan\n");
 | |
| 
 | |
|   if (htonl (res.transfersize) == 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "Hmm, size is zero. Obviously a problem. Aborting...\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   {
 | |
|     char x1 = 0x14, x2 = 0x24;
 | |
| 
 | |
|     float pixels = ((float) htons (req->lenx) * (float) htons (req->leny)) *
 | |
|       ((float) htons (req->dpix) * (float) htons (req->dpiy)) /
 | |
|       ((float) HW_LPI * (float) HW_LPI);
 | |
|     int bpp = (int) ((float) htonl (res.transfersize) / pixels + 0.5);
 | |
|     int planes = (bpp == 1) ? 1 : 3;
 | |
|     bpp /= planes;
 | |
| 
 | |
|     HP5400_DBG (DBG_MSG, "bpp = %d / ( (%d * %d) * (%d * %d) / (%d * %d) ) = %d\n",
 | |
| 	 htonl (res.transfersize),
 | |
| 	 htons (req->lenx), htons (req->leny),
 | |
| 	 htons (req->dpix), htons (req->dpiy), HW_LPI, HW_LPI, bpp);
 | |
| 
 | |
|     hp5400_command_write_noverify (iHandle, CMD_INITBULK1, &x1, 1);
 | |
|     hp5400_command_write_noverify (iHandle, CMD_INITBULK2, &x2, 1);
 | |
| 
 | |
|     if (bpp > 2)		/* Bug!! */
 | |
|       bpp = 2;
 | |
| 
 | |
|     CircBufferInit (pHWParams->iXferHandle, &pHWParams->pipe,
 | |
| 		    htonl (res.xsize), bpp, iColourOffset, 0xF000,
 | |
| 		    htonl (res.transfersize) + 3 * htons (res.ysize));
 | |
|   }
 | |
| 
 | |
|   if (result)			/* copy ScanResult to parent if they asked for it */
 | |
|     memcpy (result, &res, sizeof (*result));
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| FinishScan (THWParams * pHWParams)
 | |
| {
 | |
|   int iHandle = pHWParams->iXferHandle;
 | |
| 
 | |
|   CircBufferExit (&pHWParams->pipe);
 | |
| 
 | |
|   {				/* Finish scan request */
 | |
|     char flag = 0x40;
 | |
|     if (hp5400_command_write (iHandle, CMD_STOPSCAN, sizeof (flag), &flag) <
 | |
| 	0)
 | |
|       {
 | |
| 	HP5400_DBG (DBG_MSG, "failed to set gamma flag\n");
 | |
| 	return;
 | |
|       }
 | |
|   }
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| HP5400Open (THWParams * params, const char *filename)
 | |
| {
 | |
|   int iHandle = hp5400_open (filename);
 | |
|   char szVersion[32];
 | |
|   int i;
 | |
| #ifndef NO_STRING_VERSION_MATCH
 | |
|   int versionMatched;
 | |
| #endif
 | |
| 
 | |
|   if (iHandle < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "hp5400_open failed\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   params->iXferHandle = 0;
 | |
| 
 | |
|   /* read version info */
 | |
|   if (hp5400_command_read
 | |
|       (iHandle, CMD_GETVERSION, sizeof (szVersion), szVersion) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to read version string\n");
 | |
|       goto hp5400_close_exit;
 | |
|     }
 | |
| 
 | |
| 	  HP5400_DBG (DBG_MSG, "version String :\n");
 | |
|     for (i=0; i < 32; i++) {
 | |
|       HP5400_DBG (DBG_MSG, "%c\n", szVersion[i]);
 | |
|     }
 | |
|     HP5400_DBG (DBG_MSG, "\n");
 | |
| 
 | |
| #ifndef NO_STRING_VERSION_MATCH
 | |
|   i = 0;
 | |
|   versionMatched = 0;
 | |
|   while ( !versionMatched && (i < numVersions) ) {
 | |
| 	if (!strncmp (szVersion + 1, MatchVersions[i] .strVersion, strlen(MatchVersions[i] .strVersion) - 4)) {
 | |
| 		versionMatched = 1;
 | |
| 	}
 | |
|   	i++;
 | |
|   }
 | |
|   if ( !versionMatched ) {
 | |
| 	HP5400_DBG (DBG_MSG,
 | |
| 	       "Sorry, unknown scanner version. Attempted match on :\n");
 | |
| 	i = 0;
 | |
| 	while ( i < numVersions ) {
 | |
| 		HP5400_DBG (DBG_MSG, "* '%s'\n", MatchVersions[i] .strVersion);
 | |
| 		i++;
 | |
| 	}
 | |
| 	HP5400_DBG (DBG_MSG, "Version is '%s'\n", szVersion);
 | |
| 	goto hp5400_close_exit;
 | |
|     }
 | |
| #else
 | |
|   HP5400_DBG (DBG_MSG, "Warning, Version match is disabled. Version is '%s'\n",
 | |
|        szVersion);
 | |
| #endif /* NO_STRING_VERSION_MATCH */
 | |
| 
 | |
|   params->iXferHandle = iHandle;
 | |
| 
 | |
|   /* Start the lamp warming up */
 | |
|   /* No point checking the return value, we don't care anyway */
 | |
|   WriteByte (iHandle, 0x0000, 0x01);
 | |
| 
 | |
|   /* Success */
 | |
|   return 0;
 | |
| 
 | |
| hp5400_close_exit:
 | |
|   hp5400_close (iHandle);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| void
 | |
| HP5400Close (THWParams * params)
 | |
| {
 | |
|   hp5400_close (params->iXferHandle);
 | |
| }
 | |
| 
 | |
| HP5400_SANE_STATIC
 | |
| int
 | |
| HP5400Detect (const char *filename,
 | |
| 	      int (*_ReportDevice) (TScannerModel * pModel,
 | |
| 				    const char *pszDeviceName))
 | |
| {
 | |
|   int iHandle = hp5400_open (filename);
 | |
| 
 | |
|   char szVersion[32];
 | |
|   int ret = 0;
 | |
| #ifndef NO_STRING_VERSION_MATCH
 | |
|   int versionMatched = 0;
 | |
|   int i = 0;
 | |
| #endif
 | |
| 
 | |
|   if (iHandle < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "hp5400_open failed\n");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* read version info */
 | |
|   if (hp5400_command_read
 | |
|       (iHandle, CMD_GETVERSION, sizeof (szVersion), szVersion) < 0)
 | |
|     {
 | |
|       HP5400_DBG (DBG_MSG, "failed to read version string\n");
 | |
|       ret = -1;
 | |
|       goto hp5400_close_exit;
 | |
|     }
 | |
| 
 | |
| #ifndef NO_STRING_VERSION_MATCH
 | |
|   i = 0;
 | |
|   versionMatched = 0;
 | |
|   while ( !versionMatched && (i < numVersions) ) {
 | |
| 	if (!memcmp (szVersion + 1, MatchVersions[i] .strVersion, strlen (MatchVersions[i] .strVersion) - 4)) {
 | |
| 		versionMatched = 1;
 | |
| 	}
 | |
|   	i++;
 | |
|   }
 | |
|   if ( !versionMatched ) {
 | |
| 	HP5400_DBG (DBG_MSG,
 | |
| 	       "Sorry, unknown scanner version. Attempted match on :\n");
 | |
| 	i = 0;
 | |
| 	while ( i < numVersions ) {
 | |
| 		HP5400_DBG (DBG_MSG, "* '%s'\n", MatchVersions[i] .strVersion);
 | |
| 		i++;
 | |
| 	}
 | |
| 	HP5400_DBG (DBG_MSG, "Version is '%s'\n", szVersion);
 | |
| 	goto hp5400_close_exit;
 | |
|     }
 | |
| #else
 | |
|   HP5400_DBG (DBG_MSG, "Warning, Version match is disabled. Version is '%s'\n",
 | |
|        szVersion);
 | |
| #endif /* NO_STRING_VERSION_MATCH */
 | |
| 
 | |
|   if (_ReportDevice)
 | |
|     _ReportDevice (&Model_HP54xx, filename);
 | |
| 
 | |
| hp5400_close_exit:
 | |
|   hp5400_close (iHandle);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| #ifdef STANDALONE
 | |
| int
 | |
| main (int argc, char *argv[])
 | |
| {
 | |
|   THWParams scanner;
 | |
| 
 | |
|   assert (sizeof (struct ScanRequest) == 32);
 | |
|   assert (sizeof (struct ScanResponse) == 16);
 | |
| 
 | |
|   hp5400_dbg_start();
 | |
| 
 | |
|   HP5400_DBG (DBG_MSG,
 | |
|        "HP5400/5470C sample scan utility, by Martijn van Oosterhout <kleptog@svana.org>\n");
 | |
|   HP5400_DBG (DBG_MSG,
 | |
|        "Based on the testutils by Bertrik Sikken (bertrik@zonnet.nl)\n");
 | |
| 
 | |
|   if ((argc == 6) && (!strcmp (argv[1], "-decode")))
 | |
|     {
 | |
|       int width = atoi (argv[3]);
 | |
|       int height = atoi (argv[4]);
 | |
|       FILE *temp = fopen (argv[2], "r+b");
 | |
|       if (temp)
 | |
| 	{
 | |
| 	  int planes = 3;
 | |
| 	  int bpp = 1;
 | |
| 	  fseek (temp, 0, SEEK_SET);
 | |
| 	  DecodeImage (temp, planes, bpp, planes * bpp * width, height,
 | |
| 		       argv[5]);
 | |
| 	  fclose (temp);
 | |
| 	}
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   if (HP5400Open (&scanner, NULL) < 0)
 | |
|     {
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   PreviewScan (scanner.iXferHandle);
 | |
| 
 | |
|   HP5400Close (&scanner);
 | |
|   fprintf (stderr, "Note: output is in output.ppm\n");
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| #endif
 |