kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1952 wiersze
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1952 wiersze
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
| /*
 | |
|   sane - Scanner Access Now Easy.
 | |
|   Copyright (C) 2006 Jon Chambers <jon@jon.demon.co.uk>
 | |
|  
 | |
|   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.
 | |
|  
 | |
|   Dell 1600n network scan driver for SANE.
 | |
|  
 | |
|   NOTE
 | |
|  
 | |
|   - need to add support for multiple network interfaces
 | |
|   
 | |
|   To debug:
 | |
|   SANE_DEBUG_DELL1600N_NET=255 scanimage --verbose 2>scan.errs 1>scan.png
 | |
| */
 | |
| 
 | |
| /***********************************************************
 | |
|  * INCLUDES
 | |
|  ***********************************************************/
 | |
| 
 | |
| #include "../include/sane/config.h"
 | |
| #include "../include/sane/sane.h"
 | |
| #include "../include/sane/sanei.h"
 | |
| 
 | |
| #define BACKEND_NAME    dell1600n_net
 | |
| #include "../include/sane/sanei_backend.h"
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| #include <math.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| /* :NOTE: these are likely to be platform-specific! */
 | |
| #include <sys/types.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <netdb.h>
 | |
| 
 | |
| #include <jpeglib.h>
 | |
| #include <tiffio.h>
 | |
| 
 | |
| 
 | |
| /***********************************************************
 | |
|  * DEFINITIONS
 | |
|  ***********************************************************/
 | |
| 
 | |
| /* Maximum number of scanners */
 | |
| #define MAX_SCANNERS 32
 | |
| 
 | |
| /* version number */
 | |
| #define DRIVER_VERSION SANE_VERSION_CODE( V_MAJOR, V_MINOR, 0 )
 | |
| 
 | |
| /* size of buffer for socket communication */
 | |
| #define SOCK_BUF_SIZE 2048
 | |
| 
 | |
| /* size of registation name */
 | |
| #define REG_NAME_SIZE 64
 | |
| 
 | |
| /* a buffer struct to store "stuff" */
 | |
| struct ComBuf
 | |
| {
 | |
|   size_t m_capacity;		/* current allocated size in bytes */
 | |
|   size_t m_used;		/* current used size in bytes */
 | |
|   unsigned char *m_pBuf;	/* storage (or NULL if none allocated) */
 | |
| };
 | |
| 
 | |
| /* state data for a single scanner connection */
 | |
| struct ScannerState
 | |
| {
 | |
|   int m_udpFd;			/* file descriptor to UDP socket */
 | |
|   int m_tcpFd;			/* file descriptor to TCP socket */
 | |
|   struct sockaddr_in m_sockAddr;	/* printer address */
 | |
|   struct ComBuf m_buf;		/* buffer for network data */
 | |
|   struct ComBuf m_imageData;	/* storage for decoded image data */
 | |
|   int m_bFinish;		/* set non-0 to signal that we are finished */
 | |
|   char m_regName[REG_NAME_SIZE];	/* name with which to register */
 | |
|   unsigned short m_xres;	/* x resolution (network byte order) */
 | |
|   unsigned short m_yres;	/* y resolution (network byte order) */
 | |
|   unsigned int m_composition;	/* composition (0x01=>TIFF/PDF,0x40=>JPEG) (network byte order) */
 | |
|   unsigned char m_brightness;	/* brightness */
 | |
|   unsigned int m_compression;	/* compression (0x08=>CCIT Group 4,0x20=>JPEG) (network byte order) */
 | |
|   unsigned int m_fileType;	/* file type (2=>TIFF,4=>PDF,8=>JPEG)(network byte order) */
 | |
|   unsigned int m_pixelWidth;	/* width in pixels (network byte order) */
 | |
|   unsigned int m_pixelHeight;	/* height in pixels (network byte order) */
 | |
|   unsigned int m_bytesRead;	/* bytes read by SANE (host byte order) */
 | |
| };
 | |
| 
 | |
| /* struct for in-memory jpeg decompression */
 | |
| struct JpegDataDecompState
 | |
| {
 | |
|   struct jpeg_decompress_struct m_cinfo;	/* base struct */
 | |
|   unsigned char *m_pData;	/* data pointer */
 | |
|   unsigned int m_bytesRemaining;	/* amount of unprocessed data */
 | |
| };
 | |
| 
 | |
| /* initial ComBuf allocation */
 | |
| #define INITIAL_COM_BUF_SIZE 1024
 | |
| 
 | |
| /***********************************************************
 | |
|  * FUNCTION PROTOTYPES
 | |
|  ***********************************************************/
 | |
| 
 | |
| /* print hex buffer to stdout */
 | |
| static void HexDump (int debugLevel, const unsigned char *buf,
 | |
| 		     size_t bufSize);
 | |
| 
 | |
| /* clears gKnownDevices array */
 | |
| static void ClearKnownDevices (void);
 | |
| 
 | |
| /* initialise a ComBuf struct */
 | |
| static int InitComBuf (struct ComBuf *pBuf);
 | |
| 
 | |
| /* free a ComBuf struct */
 | |
| static void FreeComBuf (struct ComBuf *pBuf);
 | |
| 
 | |
| /* add data to a ComBuf struct */
 | |
| static int AppendToComBuf (struct ComBuf *pBuf, const unsigned char *pData,
 | |
| 			   size_t datSize);
 | |
| 
 | |
| /* remove data from the front of a ComBuf struct */
 | |
| static int PopFromComBuf (struct ComBuf *pBuf, size_t datSize);
 | |
| 
 | |
| /* Initialise a packet */
 | |
| static int InitPacket (struct ComBuf *pBuf, char type);
 | |
| 
 | |
| /* append message to a packet */
 | |
| static int AppendMessageToPacket (struct ComBuf *pBuf,
 | |
| 				  char messageType,
 | |
| 				  char *messageName,
 | |
| 				  char valueType,
 | |
| 				  void *pValue, size_t valueLen);
 | |
| 
 | |
| /* write length data to packet header */
 | |
| static void FinalisePacket (struct ComBuf *pBuf);
 | |
| 
 | |
| /* \return 1 if message is complete, 0 otherwise */
 | |
| static int MessageIsComplete (unsigned char *pData, size_t size);
 | |
| 
 | |
| /* process a registration broadcast response
 | |
|    \return SANE_Device pointer on success (caller frees), NULL on failure 
 | |
| */
 | |
| static SANE_Device *ProcessFindResponse (unsigned char *pData, size_t size);
 | |
| 
 | |
| /* frees a scanner state struct stored in gOpenScanners */
 | |
| static void FreeScannerState (int iScanner);
 | |
| 
 | |
| /* \return 1 if iScanner is a valid member of gOpenScanners, 0 otherwise */
 | |
| static int ValidScannerNumber (int iScanner);
 | |
| 
 | |
| /* process UDP responses, \return 0 in success, >0 otherwise */
 | |
| static int ProcessUdpResponse (unsigned char *pData, size_t size,
 | |
| 			       struct ScannerState *pState);
 | |
| 
 | |
| /* process TCP responses, \return 0 in success, >0 otherwise */
 | |
| static int ProcessTcpResponse (struct ScannerState *pState,
 | |
| 			       struct ComBuf *pTcpBufBuf);
 | |
| 
 | |
| /* Process the data from a single scanned page, \return 0 in success, >0 otherwise */
 | |
| static int ProcessPageData (struct ScannerState *pState);
 | |
| 
 | |
| /* Libjpeg decompression interface */
 | |
| static void JpegDecompInitSource (j_decompress_ptr cinfo);
 | |
| static boolean JpegDecompFillInputBuffer (j_decompress_ptr cinfo);
 | |
| static void JpegDecompSkipInputData (j_decompress_ptr cinfo, long numBytes);
 | |
| static void JpegDecompTermSource (j_decompress_ptr cinfo);
 | |
| 
 | |
| /***********************************************************
 | |
|  * GLOBALS
 | |
|  ***********************************************************/
 | |
| 
 | |
| /* Results of last call to sane_get_devices */
 | |
| static SANE_Device *gKnownDevices[MAX_SCANNERS];
 | |
| 
 | |
| /* Array of open scanner device states.
 | |
|    :NOTE: (int)SANE_Handle is an offset into this array */
 | |
| static struct ScannerState *gOpenScanners[MAX_SCANNERS];
 | |
| 
 | |
| /* scanner port */
 | |
| static unsigned short gScannerPort = 1124;
 | |
| 
 | |
| /* ms to wait for registration replies */
 | |
| static unsigned short gRegReplyWaitMs = 300;
 | |
| 
 | |
| /***********************************************************
 | |
|  * FUNCTION IMPLEMENTATIONS
 | |
|  ***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_init (SANE_Int * version_code,
 | |
| 	   SANE_Auth_Callback __sane_unused__ authorize)
 | |
| {
 | |
| 
 | |
|   /* init globals */
 | |
|   memset (gKnownDevices, 0, sizeof (gKnownDevices));
 | |
|   memset (gOpenScanners, 0, sizeof (gOpenScanners));
 | |
| 
 | |
|   /* report version */
 | |
|   *version_code = DRIVER_VERSION;
 | |
| 
 | |
|   /* init debug */
 | |
|   DBG_INIT ();
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| 
 | |
| }				/* sane_init */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| void
 | |
| sane_exit (void)
 | |
| {
 | |
| 
 | |
|   int iScanner;
 | |
| 
 | |
|   /* clean up */
 | |
|   ClearKnownDevices ();
 | |
| 
 | |
|   for (iScanner = 0; iScanner < MAX_SCANNERS; ++iScanner)
 | |
|     {
 | |
|       if (gOpenScanners[iScanner])
 | |
| 	FreeScannerState (iScanner);
 | |
|     }
 | |
| 
 | |
| }				/* sane_exit */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_devices (const SANE_Device *** device_list,
 | |
| 		  SANE_Bool __sane_unused__ local_only)
 | |
| {
 | |
| 
 | |
|   int ret;
 | |
|   unsigned char sockBuf[SOCK_BUF_SIZE];
 | |
|   int sock, optYes;
 | |
|   SANE_Device *pDevice;
 | |
|   struct ComBuf queryPacket;
 | |
|   struct sockaddr_in remoteAddr;
 | |
|   unsigned char ucVal;
 | |
|   fd_set readFds;
 | |
|   struct timeval selTimeVal;
 | |
|   int nread, iNextDevice;
 | |
| 
 | |
|   /* init variables */
 | |
|   ret = SANE_STATUS_GOOD;
 | |
|   sock = 0;
 | |
|   pDevice = NULL;
 | |
|   optYes = 1;
 | |
|   InitComBuf (&queryPacket);
 | |
| 
 | |
|   /* clear previous results */
 | |
|   ClearKnownDevices ();
 | |
| 
 | |
|   /* open UDP socket */
 | |
|   sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 | |
|   if (sock == -1)
 | |
|     {
 | |
|       DBG (1, "Error creating socket\n");
 | |
|       ret = SANE_STATUS_NO_MEM;
 | |
|       goto cleanup;
 | |
|     }
 | |
|   setsockopt (sock, SOL_SOCKET, SO_BROADCAST, &optYes, sizeof (optYes));
 | |
| 
 | |
|   /* prepare select mask */
 | |
|   FD_ZERO (&readFds);
 | |
|   FD_SET (sock, &readFds);
 | |
|   selTimeVal.tv_sec = 0;
 | |
|   selTimeVal.tv_usec = gRegReplyWaitMs * 1000;
 | |
| 
 | |
|   /* init a packet */
 | |
|   InitPacket (&queryPacket, 0x01);
 | |
| 
 | |
|   /* add query */
 | |
|   ucVal = 0;
 | |
|   AppendMessageToPacket (&queryPacket, 0x25, "std-scan-discovery-all",
 | |
| 			 0x02, &ucVal, sizeof (ucVal));
 | |
| 
 | |
|   FinalisePacket (&queryPacket);
 | |
| 
 | |
|   DBG (10, "Sending:\n");
 | |
|   HexDump (10, queryPacket.m_pBuf, queryPacket.m_used);
 | |
| 
 | |
| 
 | |
|   remoteAddr.sin_family = AF_INET;
 | |
|   remoteAddr.sin_port = htons (gScannerPort);
 | |
|   remoteAddr.sin_addr.s_addr = 0xFFFFFFFF;	/* broadcast */
 | |
| 
 | |
|   if (sendto (sock, queryPacket.m_pBuf, queryPacket.m_used, 0,
 | |
| 	      &remoteAddr, sizeof (remoteAddr)) == -1)
 | |
|     {
 | |
|       DBG (1, "Error sending broadcast packet\n");
 | |
|       ret = SANE_STATUS_NO_MEM;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* process replies */
 | |
|   iNextDevice = 0;
 | |
|   while (select (sock + 1, &readFds, NULL, NULL, &selTimeVal))
 | |
|     {
 | |
| 
 | |
|       /* break if we've got no more storage space in array */
 | |
|       if (iNextDevice >= MAX_SCANNERS)
 | |
| 	{
 | |
| 	  DBG (1, "sane_get_devices: more than %d devices, ignoring\n",
 | |
| 	       MAX_SCANNERS);
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       nread = read (sock, sockBuf, sizeof (sockBuf));
 | |
|       DBG (5, "Got a broadcast response, (%d bytes)\n", nread);
 | |
| 
 | |
|       if (nread <= 0)
 | |
| 	break;
 | |
| 
 | |
|       HexDump (10, sockBuf, nread);
 | |
| 
 | |
|       /* process response (skipping bad ones) */
 | |
|       if (!(pDevice = ProcessFindResponse (sockBuf, nread)))
 | |
| 	continue;
 | |
| 
 | |
|       /* add to list */
 | |
|       gKnownDevices[iNextDevice++] = pDevice;
 | |
| 
 | |
|     }				/* while */
 | |
| 
 | |
|   /* report our finds */
 | |
|   *device_list = (const SANE_Device **) gKnownDevices;
 | |
| 
 | |
| cleanup:
 | |
| 
 | |
|   if (sock)
 | |
|     close (sock);
 | |
|   FreeComBuf (&queryPacket);
 | |
| 
 | |
|   return ret;
 | |
| 
 | |
| }				/* sane_get_devices */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_open (SANE_String_Const devicename, SANE_Handle * handle)
 | |
| {
 | |
| 
 | |
|   int iScanner = -1, i;
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   struct hostent *pHostent;
 | |
|   char *pDot;
 | |
| 
 | |
| 
 | |
|   /* find the next available scanner pointer in gOpenScanners */
 | |
|   for (i = 0; i < MAX_SCANNERS; ++i)
 | |
|     {
 | |
| 
 | |
|       if (gOpenScanners[i])
 | |
| 	continue;
 | |
| 
 | |
|       iScanner = i;
 | |
|       break;
 | |
| 
 | |
|     }				/* for */
 | |
|   if (iScanner == -1)
 | |
|     {
 | |
|       DBG (1, "sane_open: no space left in gOpenScanners array\n");
 | |
|       status = SANE_STATUS_NO_MEM;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* allocate some space */
 | |
|   if (!(gOpenScanners[iScanner] = malloc (sizeof (struct ScannerState))))
 | |
|     {
 | |
|       status = SANE_STATUS_NO_MEM;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* init data */
 | |
|   memset (gOpenScanners[iScanner], 0, sizeof (struct ScannerState));
 | |
|   InitComBuf (&gOpenScanners[iScanner]->m_buf);
 | |
|   InitComBuf (&gOpenScanners[iScanner]->m_imageData);
 | |
|   gOpenScanners[iScanner]->m_xres = ntohs (200);
 | |
|   gOpenScanners[iScanner]->m_yres = ntohs (200);
 | |
|   gOpenScanners[iScanner]->m_composition = ntohl (0x01);
 | |
|   gOpenScanners[iScanner]->m_brightness = 0x80;
 | |
|   gOpenScanners[iScanner]->m_compression = ntohl (0x08);
 | |
|   gOpenScanners[iScanner]->m_fileType = ntohl (0x02);
 | |
| 
 | |
| 
 | |
|   /* look up scanner name */
 | |
|   pHostent = gethostbyname (devicename);
 | |
|   if ((!pHostent) || (!pHostent->h_addr_list))
 | |
|     {
 | |
|       DBG (1, "sane_open: error looking up scanner name %s\n", devicename);
 | |
|       status = SANE_STATUS_INVAL;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* open a UDP socket */
 | |
|   if (!(gOpenScanners[iScanner]->m_udpFd =
 | |
| 	socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)))
 | |
|     {
 | |
|       DBG (1, "sane_open: error opening socket\n");
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* connect to the scanner */
 | |
|   memset (&gOpenScanners[iScanner]->m_sockAddr, 0,
 | |
| 	  sizeof (gOpenScanners[iScanner]->m_sockAddr));
 | |
|   gOpenScanners[iScanner]->m_sockAddr.sin_family = AF_INET;
 | |
|   gOpenScanners[iScanner]->m_sockAddr.sin_port = htons (gScannerPort);
 | |
|   memcpy (&gOpenScanners[iScanner]->m_sockAddr.sin_addr,
 | |
| 	  pHostent->h_addr_list[0], pHostent->h_length);
 | |
|   if (connect (gOpenScanners[iScanner]->m_udpFd,
 | |
| 	       (struct sockaddr *) &gOpenScanners[iScanner]->m_sockAddr,
 | |
| 	       sizeof (gOpenScanners[iScanner]->m_sockAddr)))
 | |
|     {
 | |
|       DBG (1, "sane_open: error connecting to %s:%d\n", devicename,
 | |
| 	   gScannerPort);
 | |
|       status = SANE_STATUS_IO_ERROR;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* set fallback registration name */
 | |
|   sprintf (gOpenScanners[iScanner]->m_regName, "Sane");
 | |
| 
 | |
|   /* try to fill in hostname */
 | |
|   gethostname (gOpenScanners[iScanner]->m_regName, REG_NAME_SIZE);
 | |
| 
 | |
|   /* just in case... */
 | |
|   gOpenScanners[iScanner]->m_regName[REG_NAME_SIZE - 1] = 0;
 | |
| 
 | |
|   /* chop off any domain (if any) */
 | |
|   if ((pDot = strchr (gOpenScanners[iScanner]->m_regName, '.')))
 | |
|     *pDot = 0;
 | |
| 
 | |
|   DBG (5, "sane_open: connected to %s:%d as %s\n", devicename, gScannerPort,
 | |
|        gOpenScanners[iScanner]->m_regName);
 | |
| 
 | |
| 
 | |
|   /* set the handle */
 | |
|   *handle = (SANE_Handle) iScanner;
 | |
| 
 | |
|   return status;
 | |
| 
 | |
| cleanup:
 | |
| 
 | |
|   if (iScanner != -1)
 | |
|     FreeScannerState (iScanner);
 | |
| 
 | |
|   return status;
 | |
| 
 | |
| }				/* sane_open */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| void
 | |
| sane_close (SANE_Handle handle)
 | |
| {
 | |
| 
 | |
|   FreeScannerState ((int) handle);
 | |
| 
 | |
| }				/* sane_close */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| const SANE_Option_Descriptor *
 | |
| sane_get_option_descriptor (SANE_Handle __sane_unused__ handle,
 | |
| 			    SANE_Int option)
 | |
| {
 | |
| 
 | |
|   static SANE_Option_Descriptor numOptions = {
 | |
|     "num_options",
 | |
|     "Number of options",
 | |
|     "Number of options",
 | |
|     SANE_TYPE_INT,
 | |
|     SANE_UNIT_NONE,
 | |
|     1,
 | |
|     0,
 | |
|     0,
 | |
|     {0}
 | |
|   };
 | |
| 
 | |
|   if (option == 0)
 | |
|     return &numOptions;
 | |
|   else
 | |
|     return NULL;
 | |
| 
 | |
| }				/* sane_get_option_descriptor */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_control_option (SANE_Handle __sane_unused__ handle, SANE_Int option,
 | |
| 		     SANE_Action action, void *value,
 | |
| 		     SANE_Int __sane_unused__ * info)
 | |
| {
 | |
| 
 | |
|   static int numOptions = 1;
 | |
| 
 | |
|   if (action == SANE_ACTION_GET_VALUE && option == 0)
 | |
|     *(int *) value = numOptions;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| 
 | |
| }				/* sane_control_option  */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
 | |
| {
 | |
|   int iHandle = (int) handle;
 | |
|   unsigned int width, height, imageSize;
 | |
| 
 | |
|   if (!gOpenScanners[iHandle])
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   width = ntohl (gOpenScanners[iHandle]->m_pixelWidth);
 | |
|   height = ntohl (gOpenScanners[iHandle]->m_pixelHeight);
 | |
|   imageSize = width * height * 3;
 | |
| 
 | |
|   DBG (10,
 | |
|        "sane_get_parameters: handle %d: bytes outstanding: %d, image size: %d\n",
 | |
|        iHandle, gOpenScanners[iHandle]->m_imageData.m_used, imageSize);
 | |
| 
 | |
|   /* check for enough data */
 | |
|   if (gOpenScanners[iHandle]->m_imageData.m_used < imageSize)
 | |
|     {
 | |
|       DBG (1, "sane_get_parameters: handle %d: not enough data: %d < %d\n",
 | |
| 	   iHandle, gOpenScanners[iHandle]->m_imageData.m_used, imageSize);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   params->format = SANE_FRAME_RGB;
 | |
|   /*
 | |
|      params->last_frame = ( gOpenScanners[ iHandle ]->m_imageData.m_used == imageSize )
 | |
|      ? SANE_TRUE : SANE_FALSE;
 | |
|    */
 | |
|   params->last_frame = SANE_TRUE;
 | |
|   params->lines = height;
 | |
|   params->depth = 8;
 | |
|   params->pixels_per_line = width;
 | |
|   params->bytes_per_line = width * 3;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| 
 | |
| }				/* sane_get_parameters  */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_start (SANE_Handle handle)
 | |
| {
 | |
| 
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   struct ComBuf buf;
 | |
|   unsigned char sockBuf[SOCK_BUF_SIZE];
 | |
|   int iScanner, nread;
 | |
|   int errorCheck = 0;
 | |
|   struct sockaddr_in myAddr;
 | |
|   socklen_t addrSize;
 | |
|   fd_set readFds;
 | |
|   struct timeval selTimeVal;
 | |
| 
 | |
|   /* fetch and check scanner index */
 | |
|   iScanner = (int) handle;
 | |
|   if (!ValidScannerNumber (iScanner))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   /* check if we still have oustanding pages of data on this handle */
 | |
|   if (gOpenScanners[iScanner]->m_imageData.m_used)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   /* determine local IP address */
 | |
|   addrSize = sizeof (myAddr);
 | |
|   if (getsockname (gOpenScanners[iScanner]->m_udpFd, &myAddr, &addrSize))
 | |
|     {
 | |
|       DBG (1, "sane_start: Error getting own IP address\n");
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   /* init a buffer for our registration message */
 | |
|   errorCheck |= InitComBuf (&buf);
 | |
| 
 | |
|   /* build packet */
 | |
|   errorCheck |= InitPacket (&buf, 1);
 | |
|   errorCheck |=
 | |
|     AppendMessageToPacket (&buf, 0x22, "std-scan-subscribe-user-name", 0x0b,
 | |
| 			   gOpenScanners[iScanner]->m_regName,
 | |
| 			   strlen (gOpenScanners[iScanner]->m_regName));
 | |
|   errorCheck |=
 | |
|     AppendMessageToPacket (&buf, 0x22, "std-scan-subscribe-ip-address", 0x0a,
 | |
| 			   &myAddr.sin_addr, 4);
 | |
|   FinalisePacket (&buf);
 | |
| 
 | |
|   /* check nothing went wrong along the way */
 | |
|   if (errorCheck)
 | |
|     {
 | |
|       status = SANE_STATUS_NO_MEM;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* send the packet */
 | |
|   send (gOpenScanners[iScanner]->m_udpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 
 | |
|   /* loop until done */
 | |
|   gOpenScanners[iScanner]->m_bFinish = 0;
 | |
|   while (!gOpenScanners[iScanner]->m_bFinish)
 | |
|     {
 | |
| 
 | |
|       /* prepare select mask */
 | |
|       FD_ZERO (&readFds);
 | |
|       FD_SET (gOpenScanners[iScanner]->m_udpFd, &readFds);
 | |
|       selTimeVal.tv_sec = 1;
 | |
|       selTimeVal.tv_usec = 0;
 | |
| 
 | |
|       DBG (20, "sane_start: waiting for scan signal\n");
 | |
| 
 | |
|       /* wait again if nothing received */
 | |
|       if (!select (gOpenScanners[iScanner]->m_udpFd + 1,
 | |
| 		   &readFds, NULL, NULL, &selTimeVal))
 | |
| 	continue;
 | |
| 
 | |
|       /* read from socket */
 | |
|       nread =
 | |
| 	read (gOpenScanners[iScanner]->m_udpFd, sockBuf, sizeof (sockBuf));
 | |
| 
 | |
|       if (nread <= 0)
 | |
| 	{
 | |
| 	  DBG (1, "sane_start: read returned %d\n", nread);
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       /* process the response */
 | |
|       if (ProcessUdpResponse (sockBuf, nread, gOpenScanners[iScanner]))
 | |
| 	{
 | |
| 	  status = SANE_STATUS_IO_ERROR;
 | |
| 	  goto cleanup;
 | |
| 	}
 | |
| 
 | |
|     }				/* while */
 | |
| 
 | |
| 
 | |
| cleanup:
 | |
| 
 | |
|   FreeComBuf (&buf);
 | |
| 
 | |
| 
 | |
|   return status;
 | |
| 
 | |
| }				/* sane_start */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_read (SANE_Handle handle, SANE_Byte * data,
 | |
| 	   SANE_Int max_length, SANE_Int * length)
 | |
| {
 | |
| 
 | |
|   int iHandle = (int) handle;
 | |
|   int dataSize, imageSize;
 | |
| 
 | |
|   *length = 0;
 | |
| 
 | |
|   if (!gOpenScanners[iHandle])
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   /* check for end of data */
 | |
|   if (!gOpenScanners[iHandle]->m_imageData.m_used)
 | |
|     return SANE_STATUS_EOF;
 | |
| 
 | |
|   /* calculate the size of a single RGB frame */
 | |
|   imageSize = ntohl (gOpenScanners[iHandle]->m_pixelWidth)
 | |
|     * ntohl (gOpenScanners[iHandle]->m_pixelHeight) * 3;
 | |
| 
 | |
|   /* check for end of page */
 | |
|   if (gOpenScanners[iHandle]->m_bytesRead
 | |
|       && !(gOpenScanners[iHandle]->m_bytesRead % imageSize))
 | |
|     {
 | |
|       *length = 0;
 | |
|       gOpenScanners[iHandle]->m_bytesRead = 0;
 | |
|       return SANE_STATUS_EOF;
 | |
|     }
 | |
| 
 | |
|   /* how much to send? */
 | |
| 
 | |
|   /* calculate the size of the remainder of the current image */
 | |
|   dataSize = imageSize - (gOpenScanners[iHandle]->m_bytesRead % imageSize);
 | |
| 
 | |
|   /* ...unless we don't have that much data to send */
 | |
|   if ((unsigned int) dataSize > gOpenScanners[iHandle]->m_imageData.m_used)
 | |
|     dataSize = gOpenScanners[iHandle]->m_imageData.m_used;
 | |
| 
 | |
|   /* ...or there's not enough room in the output buffer */
 | |
|   if (dataSize > max_length)
 | |
|     dataSize = max_length;
 | |
| 
 | |
|   /* update the data sent counter */
 | |
|   gOpenScanners[iHandle]->m_bytesRead += dataSize;
 | |
| 
 | |
|   DBG (10,
 | |
|        "sane_read: sending %d bytes, image total %d of %d, %d remaining in buffer\n",
 | |
|        dataSize, gOpenScanners[iHandle]->m_bytesRead, imageSize,
 | |
|        gOpenScanners[iHandle]->m_imageData.m_used - dataSize);
 | |
| 
 | |
|   /* copy the data */
 | |
|   memcpy (data, gOpenScanners[iHandle]->m_imageData.m_pBuf, dataSize);
 | |
|   if (PopFromComBuf (&gOpenScanners[iHandle]->m_imageData, dataSize))
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   *length = dataSize;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| 
 | |
| }				/* sane_read */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| void
 | |
| sane_cancel (SANE_Handle __sane_unused__ handle)
 | |
| {
 | |
| }				/* sane_cancel */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_set_io_mode (SANE_Handle __sane_unused__ handle,
 | |
| 		  SANE_Bool __sane_unused__ non_blocking)
 | |
| {
 | |
| 
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| 
 | |
| }				/* sane_set_io_mode */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_select_fd (SANE_Handle __sane_unused__ handle,
 | |
| 		    SANE_Int __sane_unused__ * fd)
 | |
| {
 | |
| 
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| 
 | |
| }				/* sane_get_select_fd */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* Clears the contents of gKnownDevices and zeros it */
 | |
| void
 | |
| ClearKnownDevices ()
 | |
| {
 | |
| 
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < MAX_SCANNERS; ++i)
 | |
|     {
 | |
| 
 | |
|       if (gKnownDevices[i])
 | |
| 	{
 | |
| 	  if (gKnownDevices[i]->name)
 | |
| 	    free (gKnownDevices[i]->name);
 | |
| 	  if (gKnownDevices[i]->model)
 | |
| 	    free (gKnownDevices[i]->model);
 | |
| 	  free (gKnownDevices[i]);
 | |
| 	}
 | |
|       gKnownDevices[i] = NULL;
 | |
| 
 | |
|     }
 | |
| 
 | |
| }				/* ClearKnownDevices */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* print hex buffer to debug output */
 | |
| void
 | |
| HexDump (int debugLevel, const unsigned char *buf, size_t bufSize)
 | |
| {
 | |
| 
 | |
|   unsigned int i, j;
 | |
| 
 | |
|   char itemBuf[16] = { 0 }, lineBuf[256] =
 | |
|   {
 | |
|   0};
 | |
| 
 | |
|   if (DBG_LEVEL < debugLevel)
 | |
|     return;
 | |
| 
 | |
|   for (i = 0; i < bufSize; ++i)
 | |
|     {
 | |
| 
 | |
|       if (!(i % 16))
 | |
| 	sprintf (lineBuf, "%04x: ", (unsigned int) (buf + i));
 | |
| 
 | |
|       sprintf (itemBuf, "%02x ", (const unsigned int) buf[i]);
 | |
| 
 | |
|       strncat (lineBuf, itemBuf, sizeof (lineBuf));
 | |
| 
 | |
|       if ((i + 1) % 16)
 | |
| 	continue;
 | |
| 
 | |
|       /* print string equivalent */
 | |
|       for (j = i - 15; j <= i; ++j)
 | |
| 	{
 | |
| 
 | |
| 	  if ((buf[j] >= 0x20) && (!(buf[j] & 0x80)))
 | |
| 	    {
 | |
| 	      sprintf (itemBuf, "%c", buf[j]);
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      sprintf (itemBuf, ".");
 | |
| 	    }
 | |
| 	  strncat (lineBuf, itemBuf, sizeof (lineBuf));
 | |
| 
 | |
| 	}			/* for j */
 | |
| 
 | |
|       DBG (debugLevel, "%s\n", lineBuf);
 | |
|       lineBuf[0] = 0;
 | |
| 
 | |
|     }				/* for i */
 | |
| 
 | |
|   if (i % 16)
 | |
|     {
 | |
| 
 | |
|       for (j = (i % 16); j < 16; ++j)
 | |
| 	{
 | |
| 	  strncat (lineBuf, "   ", sizeof (lineBuf));
 | |
| 	}
 | |
|       for (j = 1 + i - ((i + 1) % 16); j < i; ++j)
 | |
| 	{
 | |
| 	  if ((buf[j] >= 0x20) && (!(buf[j] & 0x80)))
 | |
| 	    {
 | |
| 	      sprintf (itemBuf, "%c", buf[j]);
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      strcpy (itemBuf, ".");
 | |
| 	    }
 | |
| 	  strncat (lineBuf, itemBuf, sizeof (lineBuf));
 | |
| 	}
 | |
| 
 | |
|       DBG (debugLevel, "%s\n", lineBuf);
 | |
| 
 | |
|     }
 | |
| 
 | |
| }				/* HexDump */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* initialise a ComBuf struct
 | |
|    \return 0 on success, >0 on failure
 | |
| */
 | |
| int
 | |
| InitComBuf (struct ComBuf *pBuf)
 | |
| {
 | |
| 
 | |
|   memset (pBuf, 0, sizeof (struct ComBuf));
 | |
| 
 | |
|   pBuf->m_pBuf = malloc (INITIAL_COM_BUF_SIZE);
 | |
|   if (!pBuf->m_pBuf)
 | |
|     return 1;
 | |
| 
 | |
|   pBuf->m_capacity = INITIAL_COM_BUF_SIZE;
 | |
|   pBuf->m_used = 0;
 | |
| 
 | |
|   return 0;
 | |
| 
 | |
| }				/* InitComBuf */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* free a ComBuf struct */
 | |
| void
 | |
| FreeComBuf (struct ComBuf *pBuf)
 | |
| {
 | |
| 
 | |
|   if (pBuf->m_pBuf)
 | |
|     free (pBuf->m_pBuf);
 | |
|   memset (pBuf, 0, sizeof (struct ComBuf));
 | |
| 
 | |
| }				/* FreeComBuf */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* add data to a ComBuf struct
 | |
|    \return 0 on success, >0 on failure
 | |
|    \note If pData is NULL then buffer size will be increased but no copying will take place
 | |
|    \note In case of failure pBuf will be released using FreeComBuf 
 | |
| */
 | |
| int
 | |
| AppendToComBuf (struct ComBuf *pBuf, const unsigned char *pData,
 | |
| 		size_t datSize)
 | |
| {
 | |
| 
 | |
|   size_t newSize;
 | |
| 
 | |
|   /* check we have enough space */
 | |
|   if (pBuf->m_used + datSize > pBuf->m_capacity)
 | |
|     {
 | |
| 
 | |
|       /* nope - allocate some more */
 | |
|       newSize = pBuf->m_used + datSize + INITIAL_COM_BUF_SIZE;
 | |
|       pBuf->m_pBuf = realloc (pBuf->m_pBuf, newSize);
 | |
|       if (!pBuf->m_pBuf)
 | |
| 	{
 | |
| 	  DBG (1, "AppendToComBuf: memory allocation error");
 | |
| 	  FreeComBuf (pBuf);
 | |
| 	  return (1);
 | |
| 	}
 | |
|       pBuf->m_capacity = newSize;
 | |
|     }				/* if */
 | |
| 
 | |
|   /* add data */
 | |
|   if (pData)
 | |
|     memcpy (pBuf->m_pBuf + pBuf->m_used, pData, datSize);
 | |
|   pBuf->m_used += datSize;
 | |
| 
 | |
|   return 0;
 | |
| 
 | |
| }				/* AppendToComBuf */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* append message to a packet
 | |
|    \return 0 if  ok, 1 if bad */
 | |
| int
 | |
| AppendMessageToPacket (struct ComBuf *pBuf,	/* packet to which to append */
 | |
| 		       char messageType,	/* type of message */
 | |
| 		       char *messageName,	/* name of message */
 | |
| 		       char valueType,	/* type of value */
 | |
| 		       void *pValue,	/* pointer to value */
 | |
| 		       size_t valueLen	/* length of value (bytes) */
 | |
|   )
 | |
| {
 | |
| 
 | |
|   unsigned short slen;
 | |
| 
 | |
|   /* message type */
 | |
|   AppendToComBuf (pBuf, (void *) &messageType, 1);
 | |
| 
 | |
|   /* message length */
 | |
|   slen = htons (strlen (messageName));
 | |
|   AppendToComBuf (pBuf, (void *) &slen, 2);
 | |
| 
 | |
|   /* and name */
 | |
|   AppendToComBuf (pBuf, (void *) messageName, strlen (messageName));
 | |
| 
 | |
|   /* and value type */
 | |
|   AppendToComBuf (pBuf, (void *) &valueType, 1);
 | |
| 
 | |
|   /* value length */
 | |
|   slen = htons (valueLen);
 | |
|   AppendToComBuf (pBuf, (void *) &slen, 2);
 | |
| 
 | |
|   /* and value */
 | |
|   return (AppendToComBuf (pBuf, (void *) pValue, valueLen));
 | |
| 
 | |
| }				/* AppendMessageToPacket */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* Initialise a packet
 | |
|    \param pBuf : An initialise ComBuf
 | |
|    \param type : either 0x01 ("normal" ) or 0x02 ("reply" )
 | |
|    \return 0 on success, >0 otherwise
 | |
| */
 | |
| int
 | |
| InitPacket (struct ComBuf *pBuf, char type)
 | |
| {
 | |
| 
 | |
|   char header[8] = { 2, 0, 0, 2, 0, 0, 0, 0 };
 | |
| 
 | |
|   header[2] = type;
 | |
| 
 | |
|   /* reset size */
 | |
|   pBuf->m_used = 0;
 | |
| 
 | |
|   /* add header */
 | |
|   return (AppendToComBuf (pBuf, (void *) &header, 8));
 | |
| 
 | |
| }				/* InitPacket */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* write length data to packet header
 | |
| */
 | |
| void
 | |
| FinalisePacket (struct ComBuf *pBuf)
 | |
| {
 | |
| 
 | |
|   /* sanity check */
 | |
|   if (pBuf->m_used < 8)
 | |
|     return;
 | |
| 
 | |
|   /* set the size */
 | |
|   *((unsigned short *) (pBuf->m_pBuf + 6)) = htons (pBuf->m_used - 8);
 | |
| 
 | |
|   DBG (20, "FinalisePacket: outgoing packet:\n");
 | |
|   HexDump (20, pBuf->m_pBuf, pBuf->m_used);
 | |
| 
 | |
| }				/* FinalisePacket */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* \return 1 if message is complete, 0 otherwise */
 | |
| int
 | |
| MessageIsComplete (unsigned char *pData, size_t size)
 | |
| {
 | |
|   unsigned short dataSize;
 | |
| 
 | |
|   /* sanity check */
 | |
|   if (size < 8)
 | |
|     return 0;
 | |
| 
 | |
|   /* :NOTE: we can't just cast to a short as data may not be aligned */
 | |
|   dataSize = (((unsigned short) pData[6]) << 8) | pData[7];
 | |
| 
 | |
|   DBG (20, "MessageIsComplete: data size = %d\n", dataSize);
 | |
| 
 | |
|   if (size >= (size_t) (dataSize + 8))
 | |
|     return 1;
 | |
|   else
 | |
|     return 0;
 | |
| 
 | |
| }				/* MessageIsComplete */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* process a registration broadcast response
 | |
|    \return SANE_Device pointer on success (caller frees), NULL on failure 
 | |
| */
 | |
| SANE_Device *
 | |
| ProcessFindResponse (unsigned char *pData, size_t size)
 | |
| {
 | |
| 
 | |
|   SANE_Device *pDevice = NULL;
 | |
|   unsigned short messageSize, nameSize, valueSize;
 | |
|   unsigned char *pItem, *pEnd, *pValue;
 | |
|   char printerName[256] = { 0 };
 | |
|   char printerModel[256] = "1600n";
 | |
|   char *pModel, *pName;
 | |
| 
 | |
| 
 | |
|   DBG (10, "ProcessFindResponse: processing %d bytes, pData=%p\n", size,
 | |
|        pData);
 | |
| 
 | |
|   /* check we have a complete packet */
 | |
|   if (!MessageIsComplete (pData, size))
 | |
|     {
 | |
|       DBG (1, "ProcessFindResponse: Ignoring incomplete packet\n");
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* extract data size */
 | |
|   messageSize = (((unsigned short) (pData[6])) << 8) | pData[7];
 | |
| 
 | |
|   /* loop through items in message */
 | |
|   pItem = pData + 8;
 | |
|   pEnd = pItem + messageSize;
 | |
|   while (pItem < pEnd)
 | |
|     {
 | |
| 
 | |
|       pItem++;
 | |
|       nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
 | |
|       pItem += 2;
 | |
|       pName = (char *) pItem;
 | |
| 
 | |
|       pItem += nameSize;
 | |
| 
 | |
|       pItem++;
 | |
|       valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
 | |
|       pItem += 2;
 | |
| 
 | |
|       pValue = pItem;
 | |
| 
 | |
|       pItem += valueSize;
 | |
| 
 | |
|       /* process the item */
 | |
|       if (!strncmp ("std-scan-discovery-ip", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  snprintf (printerName, sizeof (printerName), "%d.%d.%d.%d",
 | |
| 		    (int) pValue[0],
 | |
| 		    (int) pValue[1], (int) pValue[2], (int) pValue[3]);
 | |
| 	  DBG (2, "%s\n", printerName);
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-discovery-model-name", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  memset (printerModel, 0, sizeof (printerModel));
 | |
| 	  if (valueSize > (sizeof (printerModel) - 1))
 | |
| 	    valueSize = sizeof (printerModel) - 1;
 | |
| 	  memcpy (printerModel, pValue, valueSize);
 | |
| 	  DBG (2, "std-scan-discovery-model-name: %s\n", printerModel);
 | |
| 
 | |
| 	}
 | |
| 
 | |
|     }				/* while pItem */
 | |
| 
 | |
|   /* just in case nothing sensible was found */
 | |
|   if (!strlen (printerName))
 | |
|     return NULL;
 | |
| 
 | |
|   pDevice = malloc (sizeof (SANE_Device));
 | |
|   if (!pDevice)
 | |
|     {
 | |
|       DBG (1, "ProcessFindResponse: memory allocation failure\n");
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* knock off "Dell " from start of model name */
 | |
|   pModel = printerModel;
 | |
|   if (!strncmp (pModel, "Dell ", 5))
 | |
|     pModel += 5;
 | |
| 
 | |
|   pDevice->name = strdup (printerName);
 | |
|   pDevice->vendor = "Dell";
 | |
|   pDevice->model = strdup (pModel);
 | |
|   pDevice->type = "multi-function peripheral";
 | |
| 
 | |
|   return pDevice;
 | |
| 
 | |
| }				/* ProcessFindResponse */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* frees a scanner state struct stored in gOpenScanners */
 | |
| void
 | |
| FreeScannerState (int iScanner)
 | |
| {
 | |
| 
 | |
|   /* check range etc */
 | |
|   if (!ValidScannerNumber (iScanner))
 | |
|     return;
 | |
| 
 | |
|   /* close UDP handle */
 | |
|   if (gOpenScanners[iScanner]->m_udpFd)
 | |
|     close (gOpenScanners[iScanner]->m_udpFd);
 | |
| 
 | |
|   /* free m_buf */
 | |
|   FreeComBuf (&gOpenScanners[iScanner]->m_buf);
 | |
| 
 | |
|   /* free m_imageData */
 | |
|   FreeComBuf (&gOpenScanners[iScanner]->m_imageData);
 | |
| 
 | |
|   /* free the struct */
 | |
|   free (gOpenScanners[iScanner]);
 | |
| 
 | |
|   /* set pointer to NULL */
 | |
|   gOpenScanners[iScanner] = NULL;
 | |
| 
 | |
| }				/* FreeScannerState */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* \return 1 if iScanner is a valid member of gOpenScanners, 0 otherwise */
 | |
| int
 | |
| ValidScannerNumber (int iScanner)
 | |
| {
 | |
|   /* check range */
 | |
|   if ((iScanner < 0) || (iScanner >= MAX_SCANNERS))
 | |
|     {
 | |
|       DBG (1, "ValidScannerNumber: invalid scanner index %d", iScanner);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   /* check non-NULL pointer */
 | |
|   if (!gOpenScanners[iScanner])
 | |
|     {
 | |
|       DBG (1, "ValidScannerNumber: NULL scanner struct %d", iScanner);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   /* OK */
 | |
|   return 1;
 | |
| 
 | |
| }				/* ValidScannerNumber */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* process UDP responses
 | |
|    \return 0 in success, >0 otherwise */
 | |
| static int
 | |
| ProcessUdpResponse (unsigned char *pData, size_t size,
 | |
| 		    struct ScannerState *pState)
 | |
| {
 | |
| 
 | |
|   unsigned short messageSize, nameSize, valueSize;
 | |
|   unsigned char *pItem, *pEnd, *pValue;
 | |
|   char sockBuf[SOCK_BUF_SIZE], *pName;
 | |
|   struct ComBuf tcpBuf;
 | |
|   int nread;
 | |
|   unsigned int numUsed;
 | |
| 
 | |
|   HexDump (15, pData, size);
 | |
| 
 | |
|   DBG (10, "ProcessUdpResponse: processing %d bytes, pData=%p\n", size,
 | |
|        pData);
 | |
| 
 | |
|   /* check we have a complete packet */
 | |
|   if (!MessageIsComplete (pData, size))
 | |
|     {
 | |
|       DBG (1, "ProcessUdpResponse: Ignoring incomplete packet\n");
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   /* init a com buf for use in tcp communication */
 | |
|   InitComBuf (&tcpBuf);
 | |
| 
 | |
|   /* extract data size */
 | |
|   messageSize = (((unsigned short) (pData[6])) << 8) | pData[7];
 | |
| 
 | |
|   /* loop through items in message */
 | |
|   pItem = pData + 8;
 | |
|   pEnd = pItem + messageSize;
 | |
|   while (pItem < pEnd)
 | |
|     {
 | |
| 
 | |
|       pItem++;
 | |
|       nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
 | |
|       pItem += 2;
 | |
|       pName = (char *) pItem;
 | |
| 
 | |
|       pItem += nameSize;
 | |
| 
 | |
|       pItem++;
 | |
|       valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
 | |
|       pItem += 2;
 | |
| 
 | |
|       pValue = pItem;
 | |
| 
 | |
|       pItem += valueSize;
 | |
| 
 | |
|       /* process the item */
 | |
|       if (!strncmp ("std-scan-request-tcp-connection", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  /* open TCP socket to scanner */
 | |
| 	  if (!(pState->m_tcpFd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)))
 | |
| 	    {
 | |
| 	      DBG (1, "ProcessUdpResponse: error opening TCP socket\n");
 | |
| 	      return 2;
 | |
| 	    }
 | |
| 	  if (connect (pState->m_tcpFd,
 | |
| 		       (struct sockaddr *) &pState->m_sockAddr,
 | |
| 		       sizeof (pState->m_sockAddr)))
 | |
| 	    {
 | |
| 	      DBG (1,
 | |
| 		   "ProcessUdpResponse: error connecting to scanner TCP port\n");
 | |
| 	      goto cleanup;
 | |
| 	    }
 | |
| 
 | |
| 	  DBG (1, "ProcessUdpResponse: opened TCP connection to scanner\n");
 | |
| 
 | |
| 	  /* clear read buf */
 | |
| 	  tcpBuf.m_used = 0;
 | |
| 
 | |
| 	  /* TCP read loop */
 | |
| 	  while (1)
 | |
| 	    {
 | |
| 
 | |
| 	      nread = read (pState->m_tcpFd, sockBuf, sizeof (sockBuf));
 | |
| 
 | |
| 	      if (nread <= 0)
 | |
| 		{
 | |
| 		  DBG (1, "ProcessUdpResponse: TCP read returned %d\n",
 | |
| 		       nread);
 | |
| 		  break;
 | |
| 		}
 | |
| 
 | |
| 	      /* append message to buffer */
 | |
| 	      if (AppendToComBuf (&tcpBuf, (unsigned char *) sockBuf, nread))
 | |
| 		goto cleanup;
 | |
| 
 | |
| 	      /* process all available responses */
 | |
| 	      while (tcpBuf.m_used)
 | |
| 		{
 | |
| 
 | |
| 		  /* note the buffer size before the call */
 | |
| 		  numUsed = tcpBuf.m_used;
 | |
| 
 | |
| 		  /* process the response */
 | |
| 		  if (ProcessTcpResponse (pState, &tcpBuf))
 | |
| 		    goto cleanup;
 | |
| 
 | |
| 		  /* if the buffer size has not changed then assume no more processing is possible */
 | |
| 		  if (numUsed == tcpBuf.m_used)
 | |
| 		    break;
 | |
| 
 | |
| 		}		/* while */
 | |
| 
 | |
| 	    }			/* while */
 | |
| 
 | |
| 	  close (pState->m_tcpFd);
 | |
| 	  DBG (1, "ProcessUdpResponse: closed TCP connection to scanner\n");
 | |
| 
 | |
| 	  /* signal end of session */
 | |
| 	  pState->m_bFinish = 1;
 | |
| 
 | |
| 	}			/* if */
 | |
| 
 | |
|     }				/* while pItem */
 | |
| 
 | |
|   return 0;
 | |
| 
 | |
| cleanup:
 | |
| 
 | |
|   FreeComBuf (&tcpBuf);
 | |
|   close (pState->m_tcpFd);
 | |
|   return 3;
 | |
| 
 | |
| }				/* ProcessUdpResponse */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* process TCP responses, \return 0 in success, >0 otherwise */
 | |
| int
 | |
| ProcessTcpResponse (struct ScannerState *pState, struct ComBuf *pTcpBuf)
 | |
| {
 | |
| 
 | |
|   struct ComBuf buf;
 | |
|   unsigned short messageSize = 0, nameSize, valueSize, dataChunkSize;
 | |
|   unsigned char *pItem, *pEnd, *pValue;
 | |
|   unsigned char *pData = pTcpBuf->m_pBuf;
 | |
|   char *pName;
 | |
|   unsigned int uiVal;
 | |
|   int errorCheck = 0;
 | |
| 
 | |
|   DBG (10, "ProcessTcpResponse: processing %d bytes, pData=%p\n",
 | |
|        pTcpBuf->m_used, pData);
 | |
|   HexDump (15, pData, pTcpBuf->m_used);
 | |
| 
 | |
|   /* if message not complete then wait for more to arrive */
 | |
|   if (!MessageIsComplete (pData, pTcpBuf->m_used))
 | |
|     {
 | |
|       DBG (10, "ProcessTcpResponse: incomplete message, returning\n");
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   /* init a buffer for our outbound messages */
 | |
|   if (InitComBuf (&buf))
 | |
|     {
 | |
|       errorCheck |= 1;
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* extract data size */
 | |
|   messageSize = (((unsigned short) (pData[6])) << 8) | pData[7];
 | |
| 
 | |
|   /* loop through items in message */
 | |
|   pItem = pData + 8;
 | |
|   pEnd = pItem + messageSize;
 | |
|   while (pItem < pEnd)
 | |
|     {
 | |
| 
 | |
|       pItem++;
 | |
|       nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
 | |
|       pItem += 2;
 | |
|       pName = (char *) pItem;
 | |
| 
 | |
|       pItem += nameSize;
 | |
| 
 | |
|       pItem++;
 | |
|       valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1];
 | |
|       pItem += 2;
 | |
| 
 | |
|       pValue = pItem;
 | |
| 
 | |
|       pItem += valueSize;
 | |
| 
 | |
|       /* process the item */
 | |
|       if (!strncmp ("std-scan-session-open", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-session-open-response", 0x05,
 | |
| 				   &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-getclientpref", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-x1",
 | |
| 				   0x05, &uiVal, sizeof (uiVal));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-x2",
 | |
| 				   0x05, &uiVal, sizeof (uiVal));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-y1",
 | |
| 				   0x05, &uiVal, sizeof (uiVal));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-y2",
 | |
| 				   0x05, &uiVal, sizeof (uiVal));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-xresolution", 0x04,
 | |
| 				   &pState->m_xres, sizeof (pState->m_xres));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-yresolution", 0x04,
 | |
| 				   &pState->m_yres, sizeof (pState->m_yres));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-image-composition",
 | |
| 				   0x06, &pState->m_composition,
 | |
| 				   sizeof (pState->m_composition));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-brightness", 0x02,
 | |
| 				   &pState->m_brightness,
 | |
| 				   sizeof (pState->m_brightness));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-image-compression",
 | |
| 				   0x06, &pState->m_compression,
 | |
| 				   sizeof (pState->m_compression));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-file-type", 0x06,
 | |
| 				   &pState->m_fileType,
 | |
| 				   sizeof (pState->m_fileType));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-paper-size-detect",
 | |
| 				   0x06, &uiVal, sizeof (uiVal));
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-getclientpref-paper-scanner-type",
 | |
| 				   0x06, &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-document-start", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-document-start-response", 0x05,
 | |
| 				   &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-document-file-type", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  memcpy (&pState->m_fileType, pValue, sizeof (pState->m_fileType));
 | |
| 	  DBG (10, "File type: %x\n", ntohl (pState->m_fileType));
 | |
| 
 | |
| 	}
 | |
|       else
 | |
| 	if (!strncmp ("std-scan-document-image-compression", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  memcpy (&pState->m_compression, pValue,
 | |
| 		  sizeof (pState->m_compression));
 | |
| 	  DBG (10, "Compression: %x\n", ntohl (pState->m_compression));
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-document-xresolution", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  memcpy (&pState->m_xres, pValue, sizeof (pState->m_xres));
 | |
| 	  DBG (10, "X resolution: %d\n", ntohs (pState->m_xres));
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-document-yresolution", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  memcpy (&pState->m_yres, pValue, sizeof (pState->m_yres));
 | |
| 	  DBG (10, "Y resolution: %d\n", ntohs (pState->m_yres));
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-page-widthpixel", pName, nameSize))
 | |
| 	{
 | |
| 	  if (!pState->m_pixelWidth)
 | |
| 	    {
 | |
| 	      memcpy (&pState->m_pixelWidth, pValue,
 | |
| 		      sizeof (pState->m_pixelWidth));
 | |
| 	      DBG (10, "Width: %d\n", ntohl (pState->m_pixelWidth));
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      DBG (10, "Ignoring width (already have a value)\n");
 | |
| 	    }
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-page-heightpixel", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  if (!pState->m_pixelHeight)
 | |
| 	    {
 | |
| 	      memcpy (&pState->m_pixelHeight, pValue,
 | |
| 		      sizeof (pState->m_pixelHeight));
 | |
| 	      DBG (10, "Height: %d\n", ntohl (pState->m_pixelHeight));
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      DBG (10, "Ignoring height (already have a value)\n");
 | |
| 	    }
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-page-start", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22, "std-scan-page-start-response",
 | |
| 				   0x05, &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	  /* write out any pre-existing page data */
 | |
| 	  errorCheck |= ProcessPageData (pState);
 | |
| 
 | |
| 	  /* reset the data buffer ready to store a new page */
 | |
| 	  pState->m_buf.m_used = 0;
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-page-end", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22, "std-scan-page-end-response",
 | |
| 				   0x05, &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-document-end", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-document-end-response", 0x05,
 | |
| 				   &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	  /* write out any pre-existing page data */
 | |
| 	  errorCheck |= ProcessPageData (pState);
 | |
| 
 | |
| 	  /* reset the data buffer ready to store a new page */
 | |
| 	  pState->m_buf.m_used = 0;
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-session-end", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  errorCheck |= InitPacket (&buf, 0x02);
 | |
| 	  uiVal = 0;
 | |
| 	  errorCheck |=
 | |
| 	    AppendMessageToPacket (&buf, 0x22,
 | |
| 				   "std-scan-session-end-response", 0x05,
 | |
| 				   &uiVal, sizeof (uiVal));
 | |
| 	  FinalisePacket (&buf);
 | |
| 	  send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0);
 | |
| 
 | |
| 	  /* initialise a shutodwn of the socket */
 | |
| 	  shutdown (pState->m_tcpFd, SHUT_RDWR);
 | |
| 
 | |
| 	}
 | |
|       else if (!strncmp ("std-scan-scandata-error", pName, nameSize))
 | |
| 	{
 | |
| 
 | |
| 	  /* determine the size of data in this chunk */
 | |
| 	  dataChunkSize = (pItem[6] << 8) + pItem[7];
 | |
| 
 | |
| 	  pItem += 8;
 | |
| 
 | |
| 	  DBG (10, "Reading %d bytes of scan data\n", dataChunkSize);
 | |
| 
 | |
| 	  /* append message to buffer */
 | |
| 	  errorCheck |= AppendToComBuf (&pState->m_buf, pItem, dataChunkSize);
 | |
| 
 | |
| 	  pItem += dataChunkSize;
 | |
| 
 | |
| 	  DBG (10, "Accumulated %d bytes of scan data so far\n",
 | |
| 	       pState->m_buf.m_used);
 | |
| 
 | |
| 	}			/* if */
 | |
| 
 | |
|     }				/* while */
 | |
| 
 | |
| 
 | |
| cleanup:
 | |
| 
 | |
|   /* remove processed data (including 8 byte header) from start of tcp buffer */
 | |
|   PopFromComBuf (pTcpBuf, messageSize + 8);
 | |
| 
 | |
|   /* free com buf */
 | |
|   FreeComBuf (&buf);
 | |
| 
 | |
|   return errorCheck;
 | |
| 
 | |
| }				/* ProcessTcpResponse */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* remove data from the front of a ComBuf struct
 | |
|    \return 0 if sucessful, >0 otherwise
 | |
| */
 | |
| int
 | |
| PopFromComBuf (struct ComBuf *pBuf, size_t datSize)
 | |
| {
 | |
| 
 | |
|   /* check if we're trying to remove more data than is present */
 | |
|   if (datSize > pBuf->m_used)
 | |
|     {
 | |
|       pBuf->m_used = 0;
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   /* check easy cases */
 | |
|   if ((!datSize) || (datSize == pBuf->m_used))
 | |
|     {
 | |
|       pBuf->m_used -= datSize;
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   /* move remaining memory contents to start */
 | |
|   memmove (pBuf->m_pBuf, pBuf->m_pBuf + datSize, pBuf->m_used - datSize);
 | |
| 
 | |
|   pBuf->m_used -= datSize;
 | |
|   return 0;
 | |
| 
 | |
| }				/* PopFromComBuf */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| /* Process the data from a single scanned page, \return 0 in success, >0 otherwise */
 | |
| int
 | |
| ProcessPageData (struct ScannerState *pState)
 | |
| {
 | |
| 
 | |
|   char tempFilename[TMP_MAX] = "scan.dat";
 | |
|   FILE *fTmp;
 | |
|   int fdTmp;
 | |
|   struct jpeg_source_mgr jpegSrcMgr;
 | |
|   struct JpegDataDecompState jpegCinfo;
 | |
|   struct jpeg_error_mgr jpegErr;
 | |
|   int numPixels, iPixel, width, height, scanLineSize, imageBytes;
 | |
|   int ret = 0;
 | |
| 
 | |
|   JSAMPLE *pJpegLine = NULL;
 | |
|   uint32 *pTiffRgba = NULL;
 | |
|   unsigned char *pOut;
 | |
|   char tiffErrBuf[1024];
 | |
| 
 | |
|   TIFF *pTiff = NULL;
 | |
| 
 | |
|   /* If there's no data then there's nothing to write */
 | |
|   if (!pState->m_buf.m_used)
 | |
|     return 0;
 | |
| 
 | |
|   DBG (1, "ProcessPageData: Got compression %x\n",
 | |
|        ntohl (pState->m_compression));
 | |
| 
 | |
|   switch (ntohl (pState->m_compression))
 | |
|     {
 | |
| 
 | |
|     case 0x20:
 | |
|       /* decode as JPEG if appropriate */
 | |
|       {
 | |
| 
 | |
| 	jpegSrcMgr.resync_to_restart = jpeg_resync_to_restart;
 | |
| 	jpegSrcMgr.init_source = JpegDecompInitSource;
 | |
| 	jpegSrcMgr.fill_input_buffer = JpegDecompFillInputBuffer;
 | |
| 	jpegSrcMgr.skip_input_data = JpegDecompSkipInputData;
 | |
| 	jpegSrcMgr.term_source = JpegDecompTermSource;
 | |
| 
 | |
| 	jpegCinfo.m_cinfo.err = jpeg_std_error (&jpegErr);
 | |
| 	jpeg_create_decompress (&jpegCinfo.m_cinfo);
 | |
| 	jpegCinfo.m_cinfo.src = &jpegSrcMgr;
 | |
| 	jpegCinfo.m_bytesRemaining = pState->m_buf.m_used;
 | |
| 	jpegCinfo.m_pData = pState->m_buf.m_pBuf;
 | |
| 
 | |
| 	jpeg_read_header (&jpegCinfo.m_cinfo, TRUE);
 | |
| 	jpeg_start_decompress (&jpegCinfo.m_cinfo);
 | |
| 
 | |
| 	/* allocate space for a single scanline */
 | |
| 	scanLineSize = jpegCinfo.m_cinfo.output_width
 | |
| 	  * jpegCinfo.m_cinfo.output_components;
 | |
| 	DBG (1, "ProcessPageData: image dimensions: %d x %d, line size: %d\n",
 | |
| 	     jpegCinfo.m_cinfo.output_width,
 | |
| 	     jpegCinfo.m_cinfo.output_height, scanLineSize);
 | |
| 
 | |
| 	pJpegLine = calloc (scanLineSize, sizeof (JSAMPLE));
 | |
| 	if (!pJpegLine)
 | |
| 	  {
 | |
| 	    DBG (1, "ProcessPageData: memory allocation error\n");
 | |
| 	    ret = 1;
 | |
| 	    goto JPEG_CLEANUP;
 | |
| 	  }			/* if */
 | |
| 
 | |
| 	/* note dimensions - may be different from those previously reported */
 | |
| 	pState->m_pixelWidth = htonl (jpegCinfo.m_cinfo.output_width);
 | |
| 	pState->m_pixelHeight = htonl (jpegCinfo.m_cinfo.output_height);
 | |
| 
 | |
| 	/* decode scanlines */
 | |
| 	while (jpegCinfo.m_cinfo.output_scanline
 | |
| 	       < jpegCinfo.m_cinfo.output_height)
 | |
| 	  {
 | |
| 	    DBG (20, "Reading scanline %d of %d\n",
 | |
| 		 jpegCinfo.m_cinfo.output_scanline,
 | |
| 		 jpegCinfo.m_cinfo.output_height);
 | |
| 
 | |
| 	    /* read scanline */
 | |
| 	    jpeg_read_scanlines (&jpegCinfo.m_cinfo, &pJpegLine, 1);
 | |
| 
 | |
| 	    /* append to output buffer */
 | |
| 	    ret |= AppendToComBuf (&pState->m_imageData,
 | |
| 				   pJpegLine, scanLineSize);
 | |
| 
 | |
| 	  }			/* while */
 | |
| 
 | |
|       JPEG_CLEANUP:
 | |
| 	jpeg_finish_decompress (&jpegCinfo.m_cinfo);
 | |
| 	jpeg_destroy_decompress (&jpegCinfo.m_cinfo);
 | |
| 
 | |
| 	if (pJpegLine)
 | |
| 	  free (pJpegLine);
 | |
| 
 | |
| 	return ret;
 | |
|       }				/* case JPEG */
 | |
| 
 | |
|     case 0x08:
 | |
|       /* CCITT Group 4 Fax data */
 | |
|       {
 | |
| 	/* get a temp file
 | |
| 	   :TODO: 2006-04-18: Use TIFFClientOpen and do everything in RAM
 | |
| 	 */
 | |
| 	fTmp = tmpfile ();
 | |
| 	fdTmp = fileno (fTmp);
 | |
| 
 | |
| 	pTiff = TIFFFdOpen (fdTmp, "tempfile", "w");
 | |
| 	if (!pTiff)
 | |
| 	  {
 | |
| 	    DBG (1, "ProcessPageData: Error opening temp TIFF file");
 | |
| 	    ret = SANE_STATUS_IO_ERROR;
 | |
| 	    goto TIFF_CLEANUP;
 | |
| 	  }
 | |
| 
 | |
| 	/* create a TIFF file */
 | |
| 	width = ntohl (pState->m_pixelWidth);
 | |
| 	height = ntohl (pState->m_pixelHeight);
 | |
| 	TIFFSetField (pTiff, TIFFTAG_IMAGEWIDTH, width);
 | |
| 	TIFFSetField (pTiff, TIFFTAG_IMAGELENGTH, height);
 | |
| 	TIFFSetField (pTiff, TIFFTAG_BITSPERSAMPLE, 1);
 | |
| 	TIFFSetField (pTiff, TIFFTAG_PHOTOMETRIC, 0);	/* 0 is white */
 | |
| 	TIFFSetField (pTiff, TIFFTAG_COMPRESSION, 4);	/* CCITT Group 4 */
 | |
| 
 | |
| 	TIFFWriteRawStrip (pTiff, 0, pState->m_buf.m_pBuf,
 | |
| 			   pState->m_buf.m_used);
 | |
| 
 | |
| 	if (0 > TIFFRGBAImageOK (pTiff, tiffErrBuf))
 | |
| 	  {
 | |
| 	    DBG (1, "ProcessPageData: %s\n", tiffErrBuf);
 | |
| 	    ret = SANE_STATUS_IO_ERROR;
 | |
| 	    goto TIFF_CLEANUP;
 | |
| 	  }
 | |
| 
 | |
| 	/* allocate space for RGBA representation of image */
 | |
| 	numPixels = height * width;
 | |
| 	DBG (20, "ProcessPageData: num TIFF RGBA pixels: %d\n", numPixels);
 | |
| 	if (!(pTiffRgba = calloc (numPixels, sizeof (u_long))))
 | |
| 	  {
 | |
| 	    ret = SANE_STATUS_NO_MEM;
 | |
| 	    goto TIFF_CLEANUP;
 | |
| 	  }
 | |
| 
 | |
| 	/* make space in image buffer to store the results */
 | |
| 	imageBytes = width * height * 3;
 | |
| 	ret |= AppendToComBuf (&pState->m_imageData, NULL, imageBytes);
 | |
| 	if (ret)
 | |
| 	  goto TIFF_CLEANUP;
 | |
| 
 | |
| 	/* get a pointer to the start of the output data */
 | |
| 	pOut = pState->m_imageData.m_pBuf
 | |
| 	  + pState->m_imageData.m_used - imageBytes;
 | |
| 
 | |
| 	/* read RGBA image */
 | |
| 	DBG (20, "ProcessPageData: setting up read buffer\n");
 | |
| 	TIFFReadBufferSetup (pTiff, NULL, width * height * sizeof (u_long));
 | |
| 	DBG (20, "ProcessPageData: reading RGBA data\n");
 | |
| 	TIFFReadRGBAImageOriented (pTiff, width, height, pTiffRgba,
 | |
| 				   ORIENTATION_TOPLEFT, 0);
 | |
| 
 | |
| 	/* loop over pixels */
 | |
| 	for (iPixel = 0; iPixel < numPixels; ++iPixel)
 | |
| 	  {
 | |
| 
 | |
| 	    *(pOut++) = TIFFGetR (pTiffRgba[iPixel]);
 | |
| 	    *(pOut++) = TIFFGetG (pTiffRgba[iPixel]);
 | |
| 	    *(pOut++) = TIFFGetB (pTiffRgba[iPixel]);
 | |
| 
 | |
| 	  }			/* for iRow */
 | |
| 
 | |
| 
 | |
|       TIFF_CLEANUP:
 | |
| 	if (pTiff)
 | |
| 	  TIFFClose (pTiff);
 | |
| 	if (fTmp)
 | |
| 	  fclose (fTmp);
 | |
| 	if (pTiffRgba)
 | |
| 	  free (pTiffRgba);
 | |
| 	return ret;
 | |
| 
 | |
|       }				/* case CCITT */
 | |
|     default:
 | |
|       /* this is not expected or very useful */
 | |
|       {
 | |
| 	tmpnam (tempFilename);
 | |
| 	DBG (1, "ProcessPageData: Writing scan data to %s\n", tempFilename);
 | |
| 	fTmp = fopen (tempFilename, "w");
 | |
| 	fwrite (pState->m_buf.m_pBuf, pState->m_buf.m_used, 1, fTmp);
 | |
| 	fclose (fTmp);
 | |
| 	ret = SANE_STATUS_IO_ERROR;
 | |
|       }
 | |
|     }				/* switch */
 | |
| 
 | |
|   return ret;
 | |
| 
 | |
| }				/* ProcessPageData */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| void
 | |
| JpegDecompInitSource (j_decompress_ptr cinfo)
 | |
| /* Libjpeg decompression interface */
 | |
| {
 | |
|   cinfo->src->bytes_in_buffer = 0;
 | |
| 
 | |
| }				/* JpegDecompInitSource */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| boolean
 | |
| JpegDecompFillInputBuffer (j_decompress_ptr cinfo)
 | |
| /* Libjpeg decompression interface */
 | |
| {
 | |
|   struct JpegDataDecompState *pState = (struct JpegDataDecompState *) cinfo;
 | |
|   static const unsigned char eoiByte[] = {
 | |
|     0xFF, JPEG_EOI
 | |
|   };
 | |
| 
 | |
|   DBG (10, "JpegDecompFillInputBuffer: bytes remaining: %d\n",
 | |
|        pState->m_bytesRemaining);
 | |
| 
 | |
|   if (!pState->m_bytesRemaining)
 | |
|     {
 | |
| 
 | |
|       /* no input data available so return dummy data */
 | |
|       cinfo->src->bytes_in_buffer = 2;
 | |
|       cinfo->src->next_input_byte = (const JOCTET *) eoiByte;
 | |
| 
 | |
|     }
 | |
|   else
 | |
|     {
 | |
| 
 | |
|       /* point to data */
 | |
|       cinfo->src->bytes_in_buffer = pState->m_bytesRemaining;
 | |
|       cinfo->src->next_input_byte = (const JOCTET *) pState->m_pData;
 | |
| 
 | |
|       /* note that data is now gone */
 | |
|       pState->m_bytesRemaining = 0;
 | |
| 
 | |
|     }				/* if */
 | |
| 
 | |
|   return TRUE;
 | |
| 
 | |
| }				/* JpegDecompFillInputBuffer */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| void
 | |
| JpegDecompSkipInputData (j_decompress_ptr cinfo, long numBytes)
 | |
| /* Libjpeg decompression interface */
 | |
| {
 | |
|   DBG (10, "JpegDecompSkipInputData: skipping %ld bytes\n", numBytes);
 | |
| 
 | |
|   cinfo->src->bytes_in_buffer -= numBytes;
 | |
|   cinfo->src->next_input_byte += numBytes;
 | |
| 
 | |
| }				/* JpegDecompSkipInputData */
 | |
| 
 | |
| /***********************************************************/
 | |
| 
 | |
| void
 | |
| JpegDecompTermSource (j_decompress_ptr __sane_unused__ cinfo)
 | |
| /* Libjpeg decompression interface */
 | |
| {
 | |
|   /* nothing to do */
 | |
| 
 | |
| }				/* JpegDecompTermSource */
 | |
| 
 | |
| /***********************************************************/
 |