kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1657 wiersze
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1657 wiersze
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
   (c) 2001,2002 Nathan Rutman  nathan@gordian.com  10/17/01
 | 
						|
 | 
						|
   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.  
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
   Communication, calibration, and scanning with the Canon CanoScan FB630U
 | 
						|
   flatbed scanner under linux.
 | 
						|
   
 | 
						|
   Reworked into SANE-compatible format.
 | 
						|
   
 | 
						|
   The usb-parallel port interface chip is GL640usb, on the far side of
 | 
						|
   which is an LM9830 parallel-port scanner-on-a-chip.
 | 
						|
 | 
						|
   This code has not been tested on anything other than Linux/i386.
 | 
						|
*/
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>		/* open */
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>		/* usleep */
 | 
						|
#include <time.h>
 | 
						|
#include <math.h>               /* exp() */
 | 
						|
#ifdef HAVE_OS2_H
 | 
						|
#include <sys/types.h> 		/* mode_t */
 | 
						|
#endif
 | 
						|
#include <sys/stat.h>
 | 
						|
#include "lm9830.h"
 | 
						|
 | 
						|
#define USB_TYPE_VENDOR                 (0x02 << 5)
 | 
						|
#define USB_RECIP_DEVICE                0x00
 | 
						|
#define USB_DIR_OUT                     0
 | 
						|
#define USB_DIR_IN                      0x80
 | 
						|
 | 
						|
/* Assign status and verify a good return code */
 | 
						|
#define CHK(A) {if( (status = A) != SANE_STATUS_GOOD ) { \
 | 
						|
                 DBG( 1, "Failure on line of %s: %d\n", __FILE__, \
 | 
						|
                      __LINE__ ); return A; }}
 | 
						|
 | 
						|
 | 
						|
typedef SANE_Byte byte;
 | 
						|
 | 
						|
 | 
						|
/*****************************************************
 | 
						|
            GL640 communication primitives
 | 
						|
   Provides I/O routines to Genesys Logic GL640USB USB-IEEE1284 parallel
 | 
						|
   port bridge.  Used in HP3300c, Canon FB630u.
 | 
						|
******************************************************/
 | 
						|
 | 
						|
/* Register codes for the bridge.  These are NOT the registers for the
 | 
						|
   scanner chip on the other side of the bridge. */
 | 
						|
typedef enum
 | 
						|
{
 | 
						|
  GL640_BULK_SETUP = 0x82,
 | 
						|
  GL640_EPP_ADDR = 0x83,
 | 
						|
  GL640_EPP_DATA_READ = 0x84,
 | 
						|
  GL640_EPP_DATA_WRITE = 0x85,
 | 
						|
  GL640_SPP_STATUS = 0x86,
 | 
						|
  GL640_SPP_CONTROL = 0x87,
 | 
						|
  GL640_SPP_DATA = 0x88,
 | 
						|
  GL640_GPIO_OE = 0x89,
 | 
						|
  GL640_GPIO_READ = 0x8a,
 | 
						|
  GL640_GPIO_WRITE = 0x8b
 | 
						|
}
 | 
						|
GL640_Request;
 | 
						|
 | 
						|
/* Write to the usb-parallel port bridge. */
 | 
						|
static SANE_Status
 | 
						|
gl640WriteControl (int fd, GL640_Request req, byte * data, unsigned int size)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  status = sanei_usb_control_msg (fd,
 | 
						|
				  /* rqttype */ USB_TYPE_VENDOR |
 | 
						|
				  USB_RECIP_DEVICE | USB_DIR_OUT /*0x40? */ ,
 | 
						|
				  /* rqt */ (size > 1) ? 0x04 : 0x0C,
 | 
						|
				  /* val */ (SANE_Int) req,
 | 
						|
				  /* ind */ 0,
 | 
						|
				  /* len */ size,
 | 
						|
				  /* dat */ data);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    DBG (1, "gl640WriteControl error\n");
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Read from the usb-parallel port bridge. */
 | 
						|
static SANE_Status
 | 
						|
gl640ReadControl (int fd, GL640_Request req, byte * data, unsigned int size)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  status = sanei_usb_control_msg (fd,
 | 
						|
				  /* rqttype */ USB_TYPE_VENDOR |
 | 
						|
				  USB_RECIP_DEVICE | USB_DIR_IN /*0xc0? */ ,
 | 
						|
				  /* rqt */ (size > 1) ? 0x04 : 0x0C,
 | 
						|
				  /* val */ (SANE_Int) req,
 | 
						|
				  /* ind */ 0,
 | 
						|
				  /* len */ size,
 | 
						|
				  /* dat */ data);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    DBG (1, "gl640ReadControl error\n");
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Wrappers to read or write a single byte to the bridge */
 | 
						|
static inline SANE_Status
 | 
						|
gl640WriteReq (int fd, GL640_Request req, byte data)
 | 
						|
{
 | 
						|
  return gl640WriteControl (fd, req, &data, 1);
 | 
						|
}
 | 
						|
 | 
						|
static inline SANE_Status
 | 
						|
gl640ReadReq (int fd, GL640_Request req, byte * data)
 | 
						|
{
 | 
						|
  return gl640ReadControl (fd, req, data, 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Write USB bulk data 
 | 
						|
   setup is an apparently scanner-specific sequence:
 | 
						|
   {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00}
 | 
						|
   hp3400: setup[1] = 0x01
 | 
						|
   fb630u: setup[2] = 0x80
 | 
						|
*/
 | 
						|
static SANE_Status
 | 
						|
gl640WriteBulk (int fd, byte * setup, byte * data, size_t size)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  setup[0] = 1;
 | 
						|
  setup[4] = (size) & 0xFF;
 | 
						|
  setup[5] = (size >> 8) & 0xFF;
 | 
						|
 | 
						|
  CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8));
 | 
						|
 | 
						|
  status = sanei_usb_write_bulk (fd, data, &size);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    DBG (1, "gl640WriteBulk error\n");
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Read USB bulk data 
 | 
						|
   setup is an apparently scanner-specific sequence:
 | 
						|
   {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00}
 | 
						|
   fb630u: setup[2] = 0x80
 | 
						|
*/
 | 
						|
static SANE_Status
 | 
						|
gl640ReadBulk (int fd, byte * setup, byte * data, size_t size)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  setup[0] = 0;
 | 
						|
  setup[4] = (size) & 0xFF;
 | 
						|
  setup[5] = (size >> 8) & 0xFF;
 | 
						|
 | 
						|
  CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8));
 | 
						|
 | 
						|
  status = sanei_usb_read_bulk (fd, data, &size);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    DBG (1, "gl640ReadBulk error\n");
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*****************************************************
 | 
						|
            LM9830 communication primitives
 | 
						|
	    parallel-port scanner-on-a-chip.
 | 
						|
******************************************************/
 | 
						|
 | 
						|
/* write 1 byte to a LM9830 register address */
 | 
						|
static SANE_Status
 | 
						|
write_byte (int fd, byte addr, byte val)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  DBG (14, "write_byte(fd, 0x%02x, 0x%02x);\n", addr, val);
 | 
						|
  CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
 | 
						|
  CHK (gl640WriteReq (fd, GL640_EPP_DATA_WRITE, val));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* read 1 byte from a LM9830 register address */
 | 
						|
static SANE_Status
 | 
						|
read_byte (int fd, byte addr, byte * val)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
 | 
						|
  CHK (gl640ReadReq (fd, GL640_EPP_DATA_READ, val));
 | 
						|
  DBG (14, "read_byte(fd, 0x%02x, &result); /* got %02x */\n", addr, *val);
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static byte bulk_setup_data[] = { 0, 0, 0x80, 0, 0, 0, 0, 0 };
 | 
						|
 | 
						|
/* Bulk write */
 | 
						|
static SANE_Status
 | 
						|
write_bulk (int fd, unsigned int addr, void *src, size_t count)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (13, "write_bulk(fd, 0x%02x, buf, 0x%04x);\n", addr, count);
 | 
						|
 | 
						|
  if (!src)
 | 
						|
    {
 | 
						|
      DBG (1, "write_bulk: bad src\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* destination address */
 | 
						|
  CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
 | 
						|
  /* write */
 | 
						|
  CHK (gl640WriteBulk (fd, bulk_setup_data, src, count));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Bulk read */
 | 
						|
static SANE_Status
 | 
						|
read_bulk (int fd, unsigned int addr, void *dst, size_t count)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (13, "read_bulk(fd, 0x%02x, buf, 0x%04x);\n", addr, count);
 | 
						|
 | 
						|
  if (!dst)
 | 
						|
    {
 | 
						|
      DBG (1, "read_bulk: bad dest\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* destination address */
 | 
						|
  CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr));
 | 
						|
  /* read */
 | 
						|
  CHK (gl640ReadBulk (fd, bulk_setup_data, dst, count));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*****************************************************
 | 
						|
            useful macro routines
 | 
						|
******************************************************/
 | 
						|
 | 
						|
/* write a 16-bit int to two sequential registers */
 | 
						|
static SANE_Status
 | 
						|
write_word (int fd, unsigned int addr, unsigned int data)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  /* MSB */
 | 
						|
  CHK (write_byte (fd, addr, (data >> 8) & 0xff));
 | 
						|
  /* LSB */
 | 
						|
  CHK (write_byte (fd, addr + 1, data & 0xff));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* write multiple bytes, one at a time (non-bulk) */
 | 
						|
static SANE_Status
 | 
						|
write_many (int fd, unsigned int addr, void *src, size_t count)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  size_t i;
 | 
						|
 | 
						|
  DBG (14, "multi write %d\n", count);
 | 
						|
  for (i = 0; i < count; i++)
 | 
						|
    {
 | 
						|
      DBG (15, " %04x:%02x", addr + i, ((byte *) src)[i]);
 | 
						|
      status = write_byte (fd, addr + i, ((byte *) src)[i]);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (15, "\n");
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (15, "\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* read multiple bytes, one at a time (non-bulk) */
 | 
						|
static SANE_Status
 | 
						|
read_many (int fd, unsigned int addr, void *dst, size_t count)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  size_t i;
 | 
						|
  byte val;
 | 
						|
 | 
						|
  DBG (14, "multi read %d\n", count);
 | 
						|
  for (i = 0; i < count; i++)
 | 
						|
    {
 | 
						|
      status = read_byte (fd, addr + i, &val);
 | 
						|
      ((byte *) dst)[i] = val;
 | 
						|
      DBG (15, " %04x:%02x", addr + i, ((byte *) dst)[i]);
 | 
						|
      /* on err, return number of success */
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (15, "\n");
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (15, "\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Poll addr until result & mask = val */
 | 
						|
static int
 | 
						|
read_poll_flag (int fd,
 | 
						|
		unsigned int addr, unsigned int mask, unsigned int val)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  byte result = 0;
 | 
						|
  time_t start_time = time (NULL);
 | 
						|
 | 
						|
  DBG (12, "read_poll_flag...\n");
 | 
						|
  do
 | 
						|
    {
 | 
						|
      status = read_byte (fd, addr, &result);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return -1;
 | 
						|
      /* Give it a minute */
 | 
						|
      if ((time (NULL) - start_time) > 60)
 | 
						|
	{
 | 
						|
	  DBG (1, "read_poll_flag: timed out (%d)\n", result);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
      usleep (100000);
 | 
						|
    }
 | 
						|
  while ((result & mask) != val);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Keep reading addr until results >= min */
 | 
						|
static int
 | 
						|
read_poll_min (int fd, unsigned int addr, unsigned int min)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  byte result;
 | 
						|
  time_t start_time = time (NULL);
 | 
						|
 | 
						|
  DBG (12, "waiting...\n");
 | 
						|
  do
 | 
						|
    {
 | 
						|
      status = read_byte (fd, addr, &result);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return -1;
 | 
						|
      /* Give it a minute */
 | 
						|
      if ((time (NULL) - start_time) > 60)
 | 
						|
	{
 | 
						|
	  DBG (1, "read_poll_min: timed out (%d < %d)\n", result, min);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
      /* no sleep here, or calibration gets unhappy. */
 | 
						|
    }
 | 
						|
  while (result < min);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Bulk read "ks" kilobytes + "remainder" bytes of data, to a buffer if the
 | 
						|
   buffer is valid. */
 | 
						|
static int
 | 
						|
read_bulk_size (int fd, int ks, int remainder, byte * dest, int destsize)
 | 
						|
{
 | 
						|
  byte *buf;
 | 
						|
  int bytes = (ks - 1) * 1024 + remainder;
 | 
						|
  int dropdata = ((dest == 0) || (destsize < bytes));
 | 
						|
 | 
						|
  if (bytes < 0)
 | 
						|
    {
 | 
						|
      DBG (1, "read_bulk_size: invalid size %02x (%d)\n", ks, bytes);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  if (destsize && (destsize < bytes))
 | 
						|
    {
 | 
						|
      DBG (3, "read_bulk_size: more data than buffer (%d/%d)\n",
 | 
						|
	   destsize, bytes);
 | 
						|
      bytes = destsize;
 | 
						|
    }
 | 
						|
 | 
						|
  if (bytes == 0)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if (dropdata)
 | 
						|
    {
 | 
						|
      buf = malloc (bytes);
 | 
						|
      DBG (3, " ignoring data ");
 | 
						|
    }
 | 
						|
  else
 | 
						|
    buf = dest;
 | 
						|
 | 
						|
  read_bulk (fd, 0x00, buf, bytes);
 | 
						|
 | 
						|
  if (dropdata)
 | 
						|
    free (buf);
 | 
						|
  return bytes;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*****************************************************
 | 
						|
 | 
						|
            fb630u calibration and scan
 | 
						|
 | 
						|
******************************************************/
 | 
						|
 | 
						|
/* data structures and constants */
 | 
						|
 | 
						|
typedef struct CANON_Handle
 | 
						|
{
 | 
						|
  int fd;			/* scanner fd */
 | 
						|
  int x1, x2, y1, y2;		/* in pixels, 600 dpi */
 | 
						|
  int width, height;		/* at scan resolution */
 | 
						|
  int resolution;		/* dpi */
 | 
						|
  char *fname;			/* output file name */
 | 
						|
  FILE *fp;			/* output file pointer (for reading) */
 | 
						|
  char *buf, *ptr;		/* data buffer */
 | 
						|
  unsigned char gain;		/* static analog gain, 0 - 31 */
 | 
						|
  double gamma;		        /* gamma correction */
 | 
						|
  int flags;
 | 
						|
#define FLG_GRAY	0x01	/* grayscale */
 | 
						|
#define FLG_FORCE_CAL	0x02	/* force calibration */
 | 
						|
#define FLG_BUF		0x04	/* save scan to buffer instead of file */
 | 
						|
#define FLG_NO_INTERLEAVE 0x08	/* don't interleave r,g,b pixels; leave them 
 | 
						|
				   in row format */
 | 
						|
#define FLG_PPM_HEADER	0x10	/* include PPM header in scan file */
 | 
						|
}
 | 
						|
CANON_Handle;
 | 
						|
 | 
						|
 | 
						|
/* offset/gain calibration file name */
 | 
						|
#define CAL_FILE_OGN "/tmp/canon.cal"
 | 
						|
 | 
						|
/* at 600 dpi */
 | 
						|
#define CANON_MAX_WIDTH    5100	/* 8.5in */
 | 
						|
/* this may not be right */
 | 
						|
#define CANON_MAX_HEIGHT   7000	/* 11.66in */
 | 
						|
 | 
						|
/* scanline end-of-line data byte, returned after each r,g,b segment,
 | 
						|
   specific to the FB630u */
 | 
						|
#define SCANLINE_END	0x0c
 | 
						|
 | 
						|
 | 
						|
static const byte seq002[] =
 | 
						|
  { /*r08 */ 0x04, /*300 dpi */ 0x1a, 0x00, 0x0d, 0x4c, 0x2f, 0x00, 0x01,
 | 
						|
/*r10 */ 0x07, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x25, 0x00,
 | 
						|
0x4b, /*r20 */ 0x15, 0xe0, /*data px start */ 0x00, 0x4b, /*data px end */ 0x14, 0x37, 0x15, 0x00 };
 | 
						|
 | 
						|
static const byte seq003[] =
 | 
						|
  { 0x02, 0x00, 0x00, /*lights out */ 0x03, 0xff, 0x00, 0x01, 0x03, 0xff,
 | 
						|
0x00, 0x01, 0x03, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06,
 | 
						|
0x1d, 0x00, 0x13, 0x04, 0x1a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x3c, 0x35, 0x94,
 | 
						|
0x00, 0x10, 0x08, 0x3f, 0x2b, 0x91, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00 };
 | 
						|
 | 
						|
 | 
						|
/* Scanner init, called at calibration and scan time.  Returns 1 if this
 | 
						|
   was the first time the scanner was plugged in, 0 afterward, and
 | 
						|
   -1 on error. */
 | 
						|
static int
 | 
						|
init (int fd)
 | 
						|
{
 | 
						|
  byte result, rv;
 | 
						|
 | 
						|
  if (gl640WriteReq (fd, GL640_GPIO_OE, 0x71) != SANE_STATUS_GOOD) { 
 | 
						|
      DBG(1, "Initial write request failed.\n");
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
  /* Gets 0x04 or 0x05 first run, gets 0x64 subsequent runs. */
 | 
						|
  if (gl640ReadReq (fd, GL640_GPIO_READ, &rv) != SANE_STATUS_GOOD) {
 | 
						|
      DBG(1, "Initial read request failed.\n");
 | 
						|
      return -1;
 | 
						|
  }
 | 
						|
  gl640WriteReq (fd, GL640_GPIO_OE, 0x70);
 | 
						|
 | 
						|
  DBG (2, "init query: %x\n", rv);
 | 
						|
  if (rv != 0x64)
 | 
						|
    {
 | 
						|
      gl640WriteReq (fd, GL640_GPIO_WRITE, 0x00);
 | 
						|
      gl640WriteReq (fd, GL640_GPIO_WRITE, 0x40);
 | 
						|
    }
 | 
						|
 | 
						|
  gl640WriteReq (fd, GL640_SPP_DATA, 0x99);
 | 
						|
  gl640WriteReq (fd, GL640_SPP_DATA, 0x66);
 | 
						|
  gl640WriteReq (fd, GL640_SPP_DATA, 0xcc);
 | 
						|
  gl640WriteReq (fd, GL640_SPP_DATA, 0x33);
 | 
						|
  /* parallel port setting */
 | 
						|
  write_byte (fd, PARALLEL_PORT, 0x06);
 | 
						|
  /* sensor control settings */
 | 
						|
  write_byte (fd, 0x0b, 0x0d);
 | 
						|
  write_byte (fd, 0x0c, 0x4c);
 | 
						|
  write_byte (fd, 0x0d, 0x2f);
 | 
						|
  read_byte (fd, 0x0b, &result);	/* wants 0d */
 | 
						|
  read_byte (fd, 0x0c, &result);	/* wants 4c */
 | 
						|
  read_byte (fd, 0x0d, &result);	/* wants 2f */
 | 
						|
  /* parallel port noise filter */
 | 
						|
  write_byte (fd, 0x70, 0x73);
 | 
						|
 | 
						|
  DBG (2, "init post-reset: %x\n", rv);
 | 
						|
  /* Returns 1 if this was the first time the scanner was plugged in. */
 | 
						|
  return (rv != 0x64);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Turn off the lamps */
 | 
						|
static void
 | 
						|
lights_out (int fd)
 | 
						|
{
 | 
						|
  write_word (fd, LAMP_R_ON, 0x3fff);
 | 
						|
  write_word (fd, LAMP_R_OFF, 0x0001);
 | 
						|
  write_word (fd, LAMP_G_ON, 0x3fff);
 | 
						|
  write_word (fd, LAMP_G_OFF, 0x0001);
 | 
						|
  write_word (fd, LAMP_B_ON, 0x3fff);
 | 
						|
  write_word (fd, LAMP_B_OFF, 0x0001);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Do the scan and save the resulting image as r,g,b interleaved PPM
 | 
						|
   file.  */
 | 
						|
static SANE_Status
 | 
						|
do_scan (CANON_Handle * s)
 | 
						|
{
 | 
						|
  SANE_Status status = SANE_STATUS_GOOD;
 | 
						|
  int numbytes, datasize, level = 0, line = 0, pixel = 0;
 | 
						|
  byte *buf, *ptr, *redptr;
 | 
						|
  FILE *fp;
 | 
						|
 | 
						|
#define BUFSIZE 0xf000
 | 
						|
  buf = malloc (BUFSIZE);
 | 
						|
  if (!buf)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  if (s->flags & FLG_BUF)
 | 
						|
    {
 | 
						|
      /* read the whole thing into buf */
 | 
						|
      if (!s->buf)
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
      s->ptr = s->buf;
 | 
						|
      fp = NULL;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      fp = fopen (s->fname, "w");
 | 
						|
      if (!fp)
 | 
						|
	{
 | 
						|
	  free (buf);
 | 
						|
	  DBG (1, "err:%s when opening %s\n", strerror (errno), s->fname);
 | 
						|
	  return SANE_STATUS_IO_ERROR;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (fp && (s->flags & FLG_PPM_HEADER))
 | 
						|
    /* PPM format header */
 | 
						|
    fprintf (fp, "P6\n%d %d\n255\n", s->width, s->height);
 | 
						|
 | 
						|
  /* lights off */
 | 
						|
  write_byte (s->fd, COMMAND, 0x08);
 | 
						|
  /* lights on */
 | 
						|
  write_byte (s->fd, COMMAND, 0x00);
 | 
						|
  /* begin scan */
 | 
						|
  write_byte (s->fd, COMMAND, 0x03);
 | 
						|
 | 
						|
  ptr = redptr = buf;
 | 
						|
  while (line < s->height)
 | 
						|
    {
 | 
						|
      datasize = read_poll_min (s->fd, IMAGE_DATA_AVAIL, 2);
 | 
						|
      if (datasize < 0)
 | 
						|
	{
 | 
						|
	  DBG (1, "no data\n");
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      DBG (12, "scan line %d %dk\n", line, datasize - 1);
 | 
						|
      /* Read may cause scan head to move */
 | 
						|
      numbytes = read_bulk_size (s->fd, datasize, 0, ptr, BUFSIZE - level);
 | 
						|
      if (numbytes < 0)
 | 
						|
	{
 | 
						|
	  status = SANE_STATUS_INVAL;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      /* Data coming back is "width" bytes Red data followed by 0x0c,
 | 
						|
         width bytes Green, 0x0c, width bytes Blue, 0x0c, repeat for
 | 
						|
         "height" lines. */
 | 
						|
      if (s->flags & FLG_NO_INTERLEAVE)
 | 
						|
	{
 | 
						|
	  /* number of full lines */
 | 
						|
	  line += (numbytes + level) / (s->width * 3);
 | 
						|
	  /* remainder (partial line) */
 | 
						|
	  level = (numbytes + level) % (s->width * 3);
 | 
						|
	  /* but if last line, don't store extra */
 | 
						|
	  if (line >= s->height)
 | 
						|
	    numbytes -= (line - s->height) * s->width * 3 + level;
 | 
						|
	  if (fp)
 | 
						|
	    fwrite (buf, 1, numbytes, fp);
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      memcpy (s->ptr, buf, numbytes);
 | 
						|
	      s->ptr += numbytes;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Contorsions to convert data from line-by-line RGB to
 | 
						|
	     byte-by-byte RGB, without reading in the whole buffer first.
 | 
						|
	     We use the sliding window redptr with the temp buffer buf. */
 | 
						|
	  ptr += numbytes;	/* point to the end of data */
 | 
						|
	  /* while we have RGB triple data */
 | 
						|
	  while (redptr + s->width + s->width <= ptr)
 | 
						|
	    {
 | 
						|
	      if (*redptr == SCANLINE_END)
 | 
						|
		DBG (13, "-%d- ", pixel);
 | 
						|
	      if (fp)
 | 
						|
		{
 | 
						|
		  /* for PPM binary (P6), 3-byte RGB pixel */
 | 
						|
		  fwrite (redptr, 1, 1, fp);	/* Red */
 | 
						|
		  fwrite (redptr + s->width, 1, 1, fp);	/* Green */
 | 
						|
		  fwrite (redptr + s->width + s->width, 1, 1, fp);	/* Blue */
 | 
						|
		  /* for PPM ascii (P3)
 | 
						|
		     fprintf(fp, "%3d %3d %3d\n",  *redptr, 
 | 
						|
		     *(redptr + s->width),
 | 
						|
		     *(redptr + s->width + s->width));
 | 
						|
		   */
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  /* R */ *s->ptr = *redptr;
 | 
						|
		  s->ptr++;
 | 
						|
		  /* G */ *s->ptr = *(redptr + s->width);
 | 
						|
		  s->ptr++;
 | 
						|
		  /* B */ *s->ptr = *(redptr + s->width + s->width);
 | 
						|
		  s->ptr++;
 | 
						|
		}
 | 
						|
	      redptr++;
 | 
						|
	      pixel++;
 | 
						|
	      if (pixel && !(pixel % s->width))
 | 
						|
		{
 | 
						|
		  /* end of a line, move redptr to the next Red section */
 | 
						|
		  line++;
 | 
						|
		  redptr += s->width + s->width;
 | 
						|
#if 0
 | 
						|
		  /* progress */
 | 
						|
		  printf ("%2d%%\r", line * 100 / s->height);
 | 
						|
		  fflush (stdout);
 | 
						|
#endif
 | 
						|
		  /* don't record any extra */
 | 
						|
		  if (line >= s->height)
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  /* keep the extra around for next time */
 | 
						|
	  level = ptr - redptr;
 | 
						|
	  if (level < 0)
 | 
						|
	    level = 0;
 | 
						|
	  memmove (buf, redptr, level);
 | 
						|
	  ptr = buf + level;
 | 
						|
	  redptr = buf;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (fp)
 | 
						|
    {
 | 
						|
      fclose (fp);
 | 
						|
      DBG (6, "created scan file %s\n", s->fname);
 | 
						|
    }
 | 
						|
  free (buf);
 | 
						|
  DBG (6, "%d lines, %d pixels, %d extra bytes\n", line, pixel, level);
 | 
						|
 | 
						|
  /* motor off */
 | 
						|
  write_byte (s->fd, COMMAND, 0x00);
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
wait_for_return (int fd)
 | 
						|
{
 | 
						|
  return read_poll_flag (fd, STATUS, STATUS_HOME, STATUS_HOME);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status compute_ogn (char *calfilename);
 | 
						|
 | 
						|
 | 
						|
/* This is the calibration rountine Win2k goes through when the scanner is
 | 
						|
   first plugged in.
 | 
						|
   Original usb trace from Win2k with USBSnoopy ("usb sniffer for w2k"
 | 
						|
   http://benoit.papillault.free.fr/speedtouch/sniff-2000.en.php3)
 | 
						|
 */
 | 
						|
static int
 | 
						|
plugin_cal (CANON_Handle * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  unsigned int temp;
 | 
						|
  byte result;
 | 
						|
  byte *buf;
 | 
						|
  int fd = s->fd;
 | 
						|
 | 
						|
  DBG (6, "Calibrating\n");
 | 
						|
 | 
						|
  /* reserved? */
 | 
						|
  read_byte (fd, 0x69, &result);	/* wants 02 */
 | 
						|
 | 
						|
  /* parallel port setting */
 | 
						|
  write_byte (fd, PARALLEL_PORT, 0x06);
 | 
						|
 | 
						|
  write_many (fd, 0x08, (byte *) seq002, sizeof (seq002));
 | 
						|
  /* addr 0x28 isn't written */
 | 
						|
  write_many (fd, 0x29, (byte *) seq003, sizeof (seq003));
 | 
						|
  /* Verification */
 | 
						|
  buf = malloc (0x400);
 | 
						|
  read_many (fd, 0x08, buf, sizeof (seq002));
 | 
						|
  if (memcmp (seq002, buf, sizeof (seq002)))
 | 
						|
    DBG (1, "seq002 verification error\n");
 | 
						|
  /* addr 0x28 isn't read */
 | 
						|
  read_many (fd, 0x29, buf, sizeof (seq003));
 | 
						|
  if (memcmp (seq003, buf, sizeof (seq003)))
 | 
						|
    DBG (1, "seq003 verification error\n");
 | 
						|
 | 
						|
  /* parallel port noise filter */
 | 
						|
  write_byte (fd, 0x70, 0x73);
 | 
						|
 | 
						|
  lights_out (fd);
 | 
						|
 | 
						|
  /* Home motor */
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 2f or 2d */
 | 
						|
  if (!(result & STATUS_HOME) /*0x2d */ )
 | 
						|
    write_byte (fd, COMMAND, 0x02);
 | 
						|
 | 
						|
  wait_for_return (fd);
 | 
						|
 | 
						|
  /* Motor forward */
 | 
						|
  write_byte (fd, COMMAND, 0x01);
 | 
						|
  usleep (600000);
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0c or 2c */
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0c */
 | 
						|
  /* Return home */
 | 
						|
  write_byte (fd, COMMAND, 0x02);
 | 
						|
 | 
						|
  /* Gamma tables */
 | 
						|
  /* Linear gamma */
 | 
						|
  for (temp = 0; temp < 0x0400; temp++)
 | 
						|
    buf[temp] = temp / 4;
 | 
						|
  /* Gamma Red */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  /* Gamma Green */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  /* Gamma Blue */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
 | 
						|
  /* Read back gamma tables.  I suppose I should check results... */
 | 
						|
  /* Gamma Red */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_READ);
 | 
						|
  read_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  /* Gamma Green */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_READ);
 | 
						|
  read_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  /* Gamma Blue */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_READ);
 | 
						|
  read_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  free (buf);
 | 
						|
 | 
						|
  /* Make sure STATUS_HOME */
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0e */
 | 
						|
  /* stepper forward */
 | 
						|
  write_byte (fd, COMMAND, 0x01);
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0c */
 | 
						|
  /* not sure if this rigid read/write pattern is required */
 | 
						|
  read_byte (fd, CLOCK_DIV, &result);	/* wants 04 */
 | 
						|
  write_byte (fd, CLOCK_DIV, 0x04);
 | 
						|
  read_byte (fd, STEP_SIZE, &result);	/* wants 04 */
 | 
						|
  write_byte (fd, STEP_SIZE, 0x3f);
 | 
						|
  read_byte (fd, 0x47, &result);	/* wants 1a */
 | 
						|
  write_byte (fd, 0x47, 0xff);
 | 
						|
  read_byte (fd, FAST_STEP, &result);	/* wants 01 */
 | 
						|
  write_byte (fd, FAST_STEP, 0x01);
 | 
						|
  read_byte (fd, 0x49, &result);	/* wants 04 */
 | 
						|
  write_byte (fd, 0x49, 0x04);
 | 
						|
  read_byte (fd, SKIP_STEPS, &result);	/* wants 00 */
 | 
						|
  write_byte (fd, SKIP_STEPS, 0x00);
 | 
						|
  read_byte (fd, 0x4b, &result);	/* wants 00 */
 | 
						|
  write_byte (fd, 0x4b, 0xc8);
 | 
						|
  read_byte (fd, BUFFER_LIMIT, &result);	/* wants 57 */
 | 
						|
  write_byte (fd, BUFFER_LIMIT, 0x04);
 | 
						|
  read_byte (fd, BUFFER_RESUME, &result);	/* wants 02 */
 | 
						|
  write_byte (fd, BUFFER_RESUME, 0x02);
 | 
						|
  read_byte (fd, REVERSE_STEPS, &result);	/* wants 00 */
 | 
						|
  write_byte (fd, REVERSE_STEPS, 0x00);
 | 
						|
  write_byte (fd, STEP_PWM, 0x1f);
 | 
						|
 | 
						|
  /* Reset motor */
 | 
						|
  write_byte (fd, COMMAND, 0x08);
 | 
						|
  write_byte (fd, COMMAND, 0x00);
 | 
						|
  /* Scan */
 | 
						|
  write_byte (fd, COMMAND, 0x03);
 | 
						|
  /* Wants 02 or 03, gets a bunch of 0's first */
 | 
						|
  read_poll_min (fd, IMAGE_DATA_AVAIL, 2);
 | 
						|
  write_byte (fd, COMMAND, 0x00);
 | 
						|
 | 
						|
  write_byte (fd, STEP_PWM, 0x3f);
 | 
						|
  write_byte (fd, CLOCK_DIV, 0x04);
 | 
						|
  /* 300 dpi */
 | 
						|
  write_word (fd, STEP_SIZE, 0x041a);
 | 
						|
  write_word (fd, FAST_STEP, 0x0104);
 | 
						|
  /* Don't skip the black/white calibration area at the bottom of the
 | 
						|
     scanner. */
 | 
						|
  write_word (fd, SKIP_STEPS, 0x0000);
 | 
						|
  write_byte (fd, BUFFER_LIMIT, 0x57);
 | 
						|
  write_byte (fd, BUFFER_RESUME, 0x02);
 | 
						|
  write_byte (fd, REVERSE_STEPS, 0x00);
 | 
						|
  write_byte (fd, BUFFER_LIMIT, 0x09);
 | 
						|
  write_byte (fd, STEP_PWM, 0x1f);
 | 
						|
  read_byte (fd, MICROSTEP, &result);	/* wants 13, active */
 | 
						|
  write_byte (fd, MICROSTEP, 0x03 /* tristate */ );
 | 
						|
 | 
						|
  /* Calibration data taken under 3 different lighting conditions */
 | 
						|
  /* dark */
 | 
						|
  write_word (fd, LAMP_R_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_R_OFF, 0x0100);
 | 
						|
  write_word (fd, LAMP_G_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_G_OFF, 0x0100);
 | 
						|
  write_word (fd, LAMP_B_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_B_OFF, 0x0100);
 | 
						|
  /* coming in, we've got 300dpi,
 | 
						|
     data px start : 0x004b
 | 
						|
     data px end  : 0x1437 for a total of 5100(13ec) 600-dpi pixels, 
 | 
						|
     (8.5 inches) or 2550 300-dpi pixels (7653 bytes). 
 | 
						|
     Interestingly, the scan head never moves, no matter how many rows
 | 
						|
     are read. */
 | 
						|
  s->width = 2551;
 | 
						|
  s->height = 1;
 | 
						|
  s->flags = FLG_BUF | FLG_NO_INTERLEAVE;
 | 
						|
  s->buf = malloc (s->width * s->height * 3);
 | 
						|
  /* FIXME do something with this data */
 | 
						|
  CHK (do_scan (s));
 | 
						|
 | 
						|
  /* Lighting */
 | 
						|
  /* medium */
 | 
						|
  write_word (fd, LAMP_R_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_R_OFF, 0x0200);
 | 
						|
  write_word (fd, LAMP_G_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_G_OFF, 0x01d7 /* also 01db */ );
 | 
						|
  write_word (fd, LAMP_B_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_B_OFF, 0x01af /* also 01b2 */ );
 | 
						|
  /* FIXME do something with this data */
 | 
						|
  CHK (do_scan (s));
 | 
						|
 | 
						|
  /* Lighting */
 | 
						|
  /* bright */
 | 
						|
  write_word (fd, LAMP_R_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_R_OFF, 0x0e8e /* also 1040 */ );
 | 
						|
  write_word (fd, LAMP_G_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_G_OFF, 0x0753 /* also 0718 */ );
 | 
						|
  write_word (fd, LAMP_B_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_B_OFF, 0x03f8 /* also 040d */ );
 | 
						|
  /* FIXME do something with this data */
 | 
						|
  CHK (do_scan (s));
 | 
						|
  free (s->buf);
 | 
						|
  s->buf = NULL;
 | 
						|
 | 
						|
  /* The trace gets a little iffy from here on out since the log files
 | 
						|
     are missing different urb's.  This is kind of a puzzled-out
 | 
						|
     compilation. */
 | 
						|
 | 
						|
  write_byte (fd, MICROSTEP, 0x13 /* pins active */ );
 | 
						|
  write_byte (fd, STEP_PWM, 0x3f);
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0c */
 | 
						|
 | 
						|
  /* Stepper home */
 | 
						|
  write_byte (fd, COMMAND, 0x02);
 | 
						|
  /* Step size */
 | 
						|
  write_word (fd, STEP_SIZE, 0x041a /* 300 dpi */ );
 | 
						|
  /* Skip steps */
 | 
						|
  write_word (fd, SKIP_STEPS, 0x0000);
 | 
						|
  /* Pause buffer levels */
 | 
						|
  write_byte (fd, BUFFER_LIMIT, 0x57);
 | 
						|
  /* Resume buffer levels */
 | 
						|
  write_byte (fd, BUFFER_RESUME, 0x02);
 | 
						|
 | 
						|
  wait_for_return (fd);
 | 
						|
  /* stepper forward small */
 | 
						|
  write_byte (fd, COMMAND, 0x01);
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0c */
 | 
						|
  usleep (200000);
 | 
						|
  write_byte (fd, STEP_PWM, 0x1f);
 | 
						|
 | 
						|
  /* Read in cal strip at bottom of scanner (to adjust gain/offset
 | 
						|
     tables.  Note that this isn't the brightest lighting condition.)
 | 
						|
     At 300 dpi: black rows 0-25; white rows 30-75; beginning
 | 
						|
     of glass 90.
 | 
						|
     This produces 574k of data, so save it to a temp file. */
 | 
						|
  if (!s->fname)
 | 
						|
    {
 | 
						|
      DBG (1, "No temp filename!\n");
 | 
						|
      s->fname = strdup ("/tmp/cal.XXXXXX");
 | 
						|
      mktemp (s->fname);
 | 
						|
    }
 | 
						|
  s->width = 2551;
 | 
						|
  s->height = 75;
 | 
						|
  s->flags = FLG_PPM_HEADER | FLG_NO_INTERLEAVE;
 | 
						|
  CHK (do_scan (s));
 | 
						|
  compute_ogn (s->fname);
 | 
						|
  unlink (s->fname);
 | 
						|
 | 
						|
  write_byte (fd, STEP_PWM, 0x3f);
 | 
						|
  /* stepper home */
 | 
						|
  write_byte (fd, COMMAND, 0x02);
 | 
						|
 | 
						|
  /* discard the remaining data */
 | 
						|
  read_byte (fd, IMAGE_DATA_AVAIL, &result);	/* wants 42,4c */
 | 
						|
  if (result > 1)
 | 
						|
    {
 | 
						|
      read_bulk_size (fd, result, 0, 0, 0);
 | 
						|
      DBG (11, "read %dk extra\n", result);
 | 
						|
    }
 | 
						|
  read_byte (fd, 0x69, &result);	/* wants 02 */
 | 
						|
  write_byte (fd, 0x69, 0x0a);
 | 
						|
 | 
						|
  lights_out (fd);
 | 
						|
  init (fd);
 | 
						|
 | 
						|
#if 0
 | 
						|
  /* Repeatedly send this every 1 second.  Button scan?  FIXME */
 | 
						|
  gl640ReadReq (fd, GL640_GPIO_READ, &result);	/* wants 00 */
 | 
						|
#endif
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* The number of regions in the calibration strip (black & white). */
 | 
						|
#define NREGIONS 2
 | 
						|
 | 
						|
/* Compute the offset/gain table from the calibration strip.  This is
 | 
						|
   somewhat more complicated than necessary because I don't hard-code the
 | 
						|
   strip widths; I try to figure out the regions based on the scan data.
 | 
						|
   Theoretically, the region-finder should work for any number of distinct
 | 
						|
   regions (but there are only 2 on this scanner.) 
 | 
						|
   This produces the CAL_FILE_OGN file, the final offset/gain table. */
 | 
						|
static SANE_Status
 | 
						|
compute_ogn (char *calfilename)
 | 
						|
{
 | 
						|
  byte *linebuf, *oldline, *newline;
 | 
						|
  mode_t oldmask;
 | 
						|
  FILE *fp;
 | 
						|
  int width, height, nlines = 0, region = -1, i, transition = 1, badcnt;
 | 
						|
  int pct;
 | 
						|
  int reglines[NREGIONS];
 | 
						|
  float *avg;
 | 
						|
  float max_range[3], tmp1, tmp2;
 | 
						|
 | 
						|
  fp = fopen (calfilename, "r");
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      DBG (1, "open %s\n", calfilename);
 | 
						|
      return SANE_STATUS_EOF;
 | 
						|
    }
 | 
						|
  fscanf (fp, "P6 %d %d %*d ", &width, &height);
 | 
						|
  DBG (12, "cal file %s %dx%d\n", calfilename, width, height);
 | 
						|
  width = width * 3;		/* 1 byte each of r, g, b */
 | 
						|
  /* make a buffer holding 2 lines of data */
 | 
						|
  linebuf = calloc (width * 2, sizeof (linebuf[0]));
 | 
						|
  /* first line is data read buffer */
 | 
						|
  newline = linebuf;
 | 
						|
  /* second line is a temporary holding spot in case the next line read
 | 
						|
     is the black/white transition, in which case we'll disregard this
 | 
						|
     one. */
 | 
						|
  oldline = linebuf + width;
 | 
						|
  /* column averages per region */
 | 
						|
  avg = calloc (width * NREGIONS, sizeof (avg[0]));
 | 
						|
 | 
						|
  while (nlines < height)
 | 
						|
    {
 | 
						|
      if (fread (newline, 1, width, fp) != (size_t) width)
 | 
						|
	break;
 | 
						|
      nlines++;
 | 
						|
      /* Check if new line is majorly different than old.
 | 
						|
         Criteria is 10 pixels differing by more than 10%. */
 | 
						|
      badcnt = 0;
 | 
						|
      for (i = 0; i < width; i++)
 | 
						|
	{
 | 
						|
	  pct = newline[i] - oldline[i];
 | 
						|
	  /* Fix by M.Reinelt <reinelt@eunet.at>
 | 
						|
	   * do NOT use 10% (think of a dark area with
 | 
						|
	   * oldline=4 and newline=5, which is a change of 20% !!
 | 
						|
	   * Use an absolute difference of 10 as criteria
 | 
						|
	   */
 | 
						|
	  if (pct < -10 || pct > 10)
 | 
						|
	    {
 | 
						|
	      badcnt++;
 | 
						|
	      DBG (16, "pix%d[%d/%d] ", i, newline[i], oldline[i]);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      DBG (13, "line %d changed %d\n", nlines, badcnt);
 | 
						|
      if ((badcnt > 10) || (nlines == height))
 | 
						|
	{
 | 
						|
	  /* End of region.  Lines are different or end of data. */
 | 
						|
	  transition++;
 | 
						|
	  if (transition == 1)
 | 
						|
	    DBG (12, "Region %d lines %d-%d\n",
 | 
						|
		 region, nlines - reglines[region], nlines - 1);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Lines are similar, so still in region.  */
 | 
						|
	  if (transition)
 | 
						|
	    {
 | 
						|
	      /* There was just a transition, so this is the start of a
 | 
						|
	         new region. */
 | 
						|
	      region++;
 | 
						|
	      if (region >= NREGIONS)
 | 
						|
		/* Too many regions detected.  Err below. */
 | 
						|
		break;
 | 
						|
	      transition = 0;
 | 
						|
	      reglines[region] = 0;
 | 
						|
	    }
 | 
						|
	  /* Add oldline to the current region's average */
 | 
						|
	  for (i = 0; i < width; i++)
 | 
						|
	    avg[i + region * width] += oldline[i];
 | 
						|
	  reglines[region]++;
 | 
						|
	}
 | 
						|
      /* And newline becomes old */
 | 
						|
      memcpy (oldline, newline, width);
 | 
						|
    }
 | 
						|
  fclose (fp);
 | 
						|
  free (linebuf);
 | 
						|
  region++;			/* now call it number of regions instead of index */
 | 
						|
  DBG (11, "read %d lines as %d regions\n", nlines, region);
 | 
						|
 | 
						|
  /* Check to see if we screwed up */
 | 
						|
  if (region != NREGIONS)
 | 
						|
    {
 | 
						|
      DBG (1, "Warning: gain/offset compute failed.\n"
 | 
						|
	   "Found %d regions instead of %d.\n", region, NREGIONS);
 | 
						|
      for (i = 0; i < region; i++)
 | 
						|
	DBG (1, "   Region %d: %d lines\n",
 | 
						|
	     i, (i >= NREGIONS) ? -1 : reglines[i]);
 | 
						|
      free (avg);
 | 
						|
      return SANE_STATUS_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Now we've got regions and sums.  Find averages and range. */
 | 
						|
  max_range[0] = max_range[1] = max_range[2] = 0.0;
 | 
						|
  for (i = 0; i < width; i++)
 | 
						|
    {
 | 
						|
      /* Convert sums to averages */
 | 
						|
      /* black region */
 | 
						|
      tmp1 = avg[i] /= reglines[0];
 | 
						|
      /* white region */
 | 
						|
      tmp2 = avg[i + width] /= reglines[1];
 | 
						|
      /* Track largest range for each color.
 | 
						|
         If image is interleaved, use 'i%3', if not, 'i/(width/3)' */
 | 
						|
      if ((tmp2 - tmp1) > max_range[i / (width / 3)])
 | 
						|
	{
 | 
						|
	  max_range[i / (width / 3)] = tmp2 - tmp1;
 | 
						|
	  DBG (14, "max %d@%d %f-%f=%f\n",
 | 
						|
	       i / (width / 3), i, tmp2, tmp1, tmp2 - tmp1);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (13, "max range r %f\n", max_range[0]);
 | 
						|
  DBG (13, "max range g %f\n", max_range[1]);
 | 
						|
  DBG (13, "max range b %f\n", max_range[2]);
 | 
						|
 | 
						|
  /* Set umask to world r/w so other users can overwrite common cal... */
 | 
						|
  oldmask = umask (0);
 | 
						|
  fp = fopen (CAL_FILE_OGN, "w");
 | 
						|
  /* ... and set it back. */
 | 
						|
  umask (oldmask);
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      DBG (1, "open " CAL_FILE_OGN);
 | 
						|
      free (avg);
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Finally, compute offset and gain */
 | 
						|
  for (i = 0; i < width; i++)
 | 
						|
    {
 | 
						|
      int gain, offset;
 | 
						|
      byte ogn[2];
 | 
						|
 | 
						|
      /* skip line termination flags */
 | 
						|
      if (!((i + 1) % (width / 3)))
 | 
						|
	{
 | 
						|
	  DBG (13, "skip scanline EOL %d/%d\n", i, width);
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Gain multiplier: 
 | 
						|
         255 : 1.5 times brighter
 | 
						|
         511 : 2 times brighter
 | 
						|
         1023: 3 times brighter */
 | 
						|
#if 1
 | 
						|
      /* Original gain/offset */
 | 
						|
      gain = 512 * ((max_range[i / (width / 3)] /
 | 
						|
		     (avg[i + width] - avg[i])) - 1);
 | 
						|
      offset = avg[i];
 | 
						|
#else
 | 
						|
      /* This doesn't work for some people.  For instance, a negative
 | 
						|
         offset would be bad.  */
 | 
						|
 | 
						|
      /* Enhanced offset and gain calculation by M.Reinelt <reinelt@eunet.at>
 | 
						|
       * These expressions were found by an iterative calibration process, 
 | 
						|
       * by changing gain and offset values for every pixel until the desired
 | 
						|
       * values for black and white were reached, and finding an approximation 
 | 
						|
       * formula.
 | 
						|
       * Note that offset is linear, but gain isn't!
 | 
						|
       */
 | 
						|
      offset = (double)3.53 * avg[i] - 125;
 | 
						|
      gain = (double)3861.0 * exp(-0.0168 * (avg[i + width] - avg[i]));
 | 
						|
#endif
 | 
						|
 | 
						|
      DBG (14, "%d wht=%f blk=%f diff=%f gain=%d offset=%d\n", i,
 | 
						|
	   avg[i + width], avg[i], avg[i + width] - avg[i], gain, offset);
 | 
						|
      /* 10-bit gain, 6-bit offset (subtractor) in two bytes */
 | 
						|
      ogn[0] = (byte) (((offset << 2) + (gain >> 8)) & 0xFF);
 | 
						|
      ogn[1] = (byte) (gain & 0xFF);
 | 
						|
      fwrite (ogn, sizeof (byte), 2, fp);
 | 
						|
      /* Annoyingly, we seem to use ogn data at 600dpi, while we scanned
 | 
						|
         at 300, so double our file.  Much easier than doubling at the
 | 
						|
         read. */
 | 
						|
      fwrite (ogn, sizeof (byte), 2, fp);
 | 
						|
    }
 | 
						|
 | 
						|
  fclose (fp);
 | 
						|
  free (avg);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
check_ogn_file (void)
 | 
						|
{
 | 
						|
  FILE *fp;
 | 
						|
  fp = fopen (CAL_FILE_OGN, "r");
 | 
						|
  if (fp)
 | 
						|
    {
 | 
						|
      fclose (fp);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Load or fake the offset/gain table */
 | 
						|
static void
 | 
						|
install_ogn (int fd)
 | 
						|
{
 | 
						|
  int temp;
 | 
						|
  byte *buf;
 | 
						|
  FILE *fp;
 | 
						|
 | 
						|
  /* 8.5in at 600dpi = 5104 pixels in scan head
 | 
						|
     10-bit gain + 6-bit offset = 2 bytes per pixel, so 10208 bytes */
 | 
						|
  buf = malloc (10208);
 | 
						|
 | 
						|
  fp = fopen (CAL_FILE_OGN, "r");
 | 
						|
  if (fp)
 | 
						|
    {
 | 
						|
      fread (buf, 2, 5100, fp);
 | 
						|
      /* screw the last 4 pixels */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* Make up the gain/offset data. */
 | 
						|
#define GAIN 256		/* 1.5x */
 | 
						|
#define OFFSET 0
 | 
						|
      for (temp = 0; temp < 10208; temp += 2)
 | 
						|
	{
 | 
						|
	  buf[temp] = (byte) ((OFFSET << 2) + (GAIN >> 8));
 | 
						|
	  buf[temp + 1] = (byte) (GAIN & 0xFF);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  /* Gain/offset table (r,g,b) */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_R | DP_OFFSET);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 10208);
 | 
						|
  if (fp)
 | 
						|
    fread (buf, 2, 5100, fp);
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_G | DP_OFFSET);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 10208);
 | 
						|
  if (fp)
 | 
						|
    {
 | 
						|
      fread (buf, 2, 5100, fp);
 | 
						|
      fclose (fp);
 | 
						|
    }
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_B | DP_OFFSET);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 10208);
 | 
						|
 | 
						|
  free (buf);
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Scan sequence */
 | 
						|
/* resolution is 75,150,300,600,1200
 | 
						|
   scan coordinates in 600-dpi pixels */
 | 
						|
static SANE_Status
 | 
						|
scan (CANON_Handle * opt)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  const int left_edge = 0x004b;	/* Just for my scanner, or is this
 | 
						|
				   universal?  Calibrate? */
 | 
						|
  int temp;
 | 
						|
  int fd = opt->fd;
 | 
						|
  byte result;
 | 
						|
  byte *buf;
 | 
						|
 | 
						|
  /* Check status. (not in w2k driver) */
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 2f or 2d */
 | 
						|
  if (!(result & STATUS_HOME) /*0x2d */ )
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
  /* or force it to return? 
 | 
						|
     write_byte(fd, COMMAND, 0x02); 
 | 
						|
     wait_for_return(fd);
 | 
						|
   */
 | 
						|
 | 
						|
  /* reserved? */
 | 
						|
  read_byte (fd, 0x69, &result);	/* wants 0a */
 | 
						|
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0e */
 | 
						|
  read_byte (fd, PAPER_SENSOR, &result);	/* wants 2b */
 | 
						|
  write_byte (fd, PAPER_SENSOR, 0x2b);
 | 
						|
  /* Color mode:
 | 
						|
     1-Channel Line Rate Color 0x15.
 | 
						|
     1-Channel Grayscale 0x14 (and we skip some of these tables) */
 | 
						|
  write_byte (fd, COLOR_MODE, 0x15);
 | 
						|
 | 
						|
  /* install the offset/gain table */
 | 
						|
  install_ogn (fd);
 | 
						|
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0e */
 | 
						|
  /* move forward to "glass 0" */
 | 
						|
  write_byte (fd, COMMAND, 0x01);
 | 
						|
  read_byte (fd, STATUS, &result);	/* wants 0c */
 | 
						|
 | 
						|
  /* create gamma table */
 | 
						|
  buf = malloc (0x400);
 | 
						|
  for (temp = 0; temp < 0x0400; temp++)
 | 
						|
    /* gamma calculation by M.Reinelt <reinelt@eunet.at> */
 | 
						|
    buf[temp] = (double) 255.0 * exp(log((temp + 0.5) / 1023.0) / opt->gamma)
 | 
						|
	+ 0.5;
 | 
						|
 | 
						|
  /* Gamma R, write and verify */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_READ);
 | 
						|
  read_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  /* Gamma G */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_READ);
 | 
						|
  read_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  /* Gamma B */
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_WRITE);
 | 
						|
  write_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA);
 | 
						|
  write_word (fd, DATAPORT_ADDR, DP_READ);
 | 
						|
  read_bulk (fd, DATAPORT, buf, 0x0400);
 | 
						|
  free (buf);
 | 
						|
 | 
						|
  write_byte (fd, CLOCK_DIV, 0x04);
 | 
						|
  /* Resolution: dpi 75(ie) 100,150(1c) 200,300(1a) 600,1200(18) */
 | 
						|
  switch (opt->resolution)
 | 
						|
    {
 | 
						|
    case 150:
 | 
						|
      write_byte (fd, 0x09, 0x1c);
 | 
						|
      break;
 | 
						|
    case 300:
 | 
						|
      write_byte (fd, 0x09, 0x1a);
 | 
						|
      break;
 | 
						|
    case 600:
 | 
						|
    case 1200:
 | 
						|
      /* actually 600 dpi horiz max */
 | 
						|
      write_byte (fd, 0x09, 0x18);
 | 
						|
      break;
 | 
						|
    default:			/* 75 */
 | 
						|
      write_byte (fd, 0x09, 0x1e);
 | 
						|
      opt->resolution = 75;
 | 
						|
    }
 | 
						|
 | 
						|
  write_word (fd, ACTIVE_PX_START, left_edge);
 | 
						|
  /* Data pixel start.  Measured at 600dpi regardless of
 | 
						|
     scan resolution.  0-position is 0x004b */
 | 
						|
  write_word (fd, DATA_PX_START, left_edge + opt->x1);
 | 
						|
  /* Data pixel end.  Measured at 600dpi regardless of scan
 | 
						|
     resolution. */
 | 
						|
  write_word (fd, DATA_PX_END, left_edge + opt->x2);
 | 
						|
  /* greyscale has 14,03, different lights */
 | 
						|
  write_byte (fd, COLOR_MODE, 0x15);
 | 
						|
  write_byte (fd, 0x29, 0x02);
 | 
						|
  /* Lights */
 | 
						|
  write_word (fd, LAMP_R_ON, 0x0017);
 | 
						|
  /* "Hi-low color" selection from windows driver.  low(1437) hi(1481) */
 | 
						|
  write_word (fd, LAMP_R_OFF, 0x1437);
 | 
						|
  write_word (fd, LAMP_G_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_G_OFF, 0x094e);
 | 
						|
  write_word (fd, LAMP_B_ON, 0x0017);
 | 
						|
  write_word (fd, LAMP_B_OFF, 0x0543);
 | 
						|
 | 
						|
  /* Analog static offset R,G,B.  Greyscale has 0,0,0 */
 | 
						|
  write_byte (fd, 0x38, 0x3f);
 | 
						|
  write_byte (fd, 0x39, 0x3f);
 | 
						|
  write_byte (fd, 0x3a, 0x3f);
 | 
						|
  /* Analog static gain R,G,B (normally 0x01) */
 | 
						|
  write_byte (fd, 0x3b, opt->gain);
 | 
						|
  write_byte (fd, 0x3c, opt->gain);
 | 
						|
  write_byte (fd, 0x3d, opt->gain);
 | 
						|
  /* Digital gain/offset settings.  Greyscale has 0 */
 | 
						|
  write_byte (fd, 0x3e, 0x1a);
 | 
						|
 | 
						|
  {
 | 
						|
    /* Stepper motion setup. */
 | 
						|
    int stepsize, faststep = 0x0104, reverse = 0x28, phase, pwm = 0x1f;
 | 
						|
    switch (opt->resolution)
 | 
						|
      {
 | 
						|
      case 75:
 | 
						|
	stepsize = 0x0106;
 | 
						|
	faststep = 0x0106;
 | 
						|
	reverse = 0;
 | 
						|
	phase = 0x39a8;
 | 
						|
	pwm = 0x3f;
 | 
						|
	break;
 | 
						|
      case 150:
 | 
						|
	stepsize = 0x020d;
 | 
						|
	phase = 0x3198;
 | 
						|
	break;
 | 
						|
      case 300:
 | 
						|
	stepsize = 0x041a;
 | 
						|
	phase = 0x2184;
 | 
						|
	break;
 | 
						|
      case 600:
 | 
						|
	stepsize = 0x0835;
 | 
						|
	phase = 0x0074;
 | 
						|
	break;
 | 
						|
      case 1200:
 | 
						|
	/* 1200 dpi y only, x is 600 dpi */
 | 
						|
	stepsize = 0x106b;
 | 
						|
	phase = 0x41ac;
 | 
						|
	break;
 | 
						|
      default:
 | 
						|
	DBG (1, "BAD RESOLUTION");
 | 
						|
	return SANE_STATUS_UNSUPPORTED;
 | 
						|
      }
 | 
						|
 | 
						|
    write_word (fd, STEP_SIZE, stepsize);
 | 
						|
    write_word (fd, FAST_STEP, faststep);
 | 
						|
    /* There sounds like a weird step disjoint at the end of skipsteps
 | 
						|
       at 75dpi, so I think that's why skipsteps=0 at 75dpi in the
 | 
						|
       Windows driver.  It still works at the normal 0x017a though. */
 | 
						|
    /* cal strip is 0x17a steps, plus 2 300dpi microsteps per pixel */
 | 
						|
    write_word (fd, SKIP_STEPS, 0x017a /* cal strip */  + opt->y1 * 2);
 | 
						|
    /* FIXME could be 0x57, why not? */
 | 
						|
    write_byte (fd, BUFFER_LIMIT, 0x20);
 | 
						|
    write_byte (fd, BUFFER_RESUME, 0x02);
 | 
						|
    write_byte (fd, REVERSE_STEPS, reverse);
 | 
						|
    /* motor resume phasing */
 | 
						|
    write_word (fd, 0x52, phase);
 | 
						|
    write_byte (fd, STEP_PWM, pwm);
 | 
						|
  }
 | 
						|
 | 
						|
  read_byte (fd, PAPER_SENSOR, &result);	/* wants 2b */
 | 
						|
  write_byte (fd, PAPER_SENSOR, 0x0b);
 | 
						|
 | 
						|
  opt->width = (opt->x2 - opt->x1) * opt->resolution / 600 + 1;
 | 
						|
  opt->height = (opt->y2 - opt->y1) * opt->resolution / 600;
 | 
						|
  opt->flags = 0;
 | 
						|
  DBG (1, "width=%d height=%d dpi=%d\n", opt->width, opt->height,
 | 
						|
       opt->resolution);
 | 
						|
  CHK (do_scan (opt));
 | 
						|
 | 
						|
  read_byte (fd, PAPER_SENSOR, &result);	/* wants 0b */
 | 
						|
  write_byte (fd, PAPER_SENSOR, 0x2b);
 | 
						|
  write_byte (fd, STEP_PWM, 0x3f);
 | 
						|
 | 
						|
  lights_out (fd);
 | 
						|
  /* home */
 | 
						|
  write_byte (fd, COMMAND, 0x02);
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
CANON_set_scan_parameters (CANON_Handle * scan,
 | 
						|
			   const int forceCal,
 | 
						|
			   const int gray,
 | 
						|
			   const int left,
 | 
						|
			   const int top,
 | 
						|
			   const int right,
 | 
						|
			   const int bottom, 
 | 
						|
			   const int res, 
 | 
						|
			   const int gain, 
 | 
						|
			   const double gamma)
 | 
						|
{
 | 
						|
  DBG (2, "CANON_set_scan_parameters:\n");
 | 
						|
  DBG (2, "cal   = %d\n", forceCal);
 | 
						|
  DBG (2, "gray  = %d (ignored)\n", gray);
 | 
						|
  DBG (2, "res   = %d\n", res);
 | 
						|
  DBG (2, "gain  = %d\n", gain);
 | 
						|
  DBG (2, "gamma = %f\n", gamma);
 | 
						|
  DBG (2, "in 600dpi pixels:\n");
 | 
						|
  DBG (2, "left  = %d, top    = %d\n", left, top);
 | 
						|
  DBG (2, "right = %d, bottom = %d\n", right, bottom);
 | 
						|
 | 
						|
  /* Validate the input parameters */
 | 
						|
  if ((left < 0) || (right > CANON_MAX_WIDTH))
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if ((top < 0) || (bottom > CANON_MAX_HEIGHT))
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (((right - left) < 10) || ((bottom - top) < 10))
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if ((res != 75) && (res != 150) && (res != 300)
 | 
						|
      && (res != 600) && (res != 1200))
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if ((gain < 0) || (gain > 64))
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (gamma <= 0.0)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  /* Store params */
 | 
						|
  scan->resolution = res;
 | 
						|
  scan->x1 = left;
 | 
						|
  scan->x2 = right - /* subtract 1 pixel */ 600 / scan->resolution;
 | 
						|
  scan->y1 = top;
 | 
						|
  scan->y2 = bottom;
 | 
						|
  scan->gain = gain;
 | 
						|
  scan->gamma = gamma;
 | 
						|
  scan->flags = forceCal ? FLG_FORCE_CAL : 0;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
CANON_close_device (CANON_Handle * scan)
 | 
						|
{
 | 
						|
  DBG (3, "CANON_close_device:\n");
 | 
						|
  sanei_usb_close (scan->fd);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
CANON_open_device (CANON_Handle * scan, const char *dev)
 | 
						|
{
 | 
						|
  SANE_Word vendor;
 | 
						|
  SANE_Word product;
 | 
						|
  SANE_Status res;
 | 
						|
 | 
						|
  DBG (3, "CANON_open_device: `%s'\n", dev);
 | 
						|
 | 
						|
  scan->fname = NULL;
 | 
						|
  scan->fp = NULL;
 | 
						|
  scan->flags = 0;
 | 
						|
 | 
						|
  res = sanei_usb_open (dev, &scan->fd);
 | 
						|
  if (res != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "CANON_open_device: couldn't open device `%s': %s\n", dev,
 | 
						|
	   sane_strstatus (res));
 | 
						|
      return res;
 | 
						|
    }
 | 
						|
 | 
						|
#ifndef NO_AUTODETECT
 | 
						|
  /* We have opened the device. Check that it is a USB scanner. */
 | 
						|
  if (sanei_usb_get_vendor_product (scan->fd, &vendor, &product) !=
 | 
						|
      SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "CANON_open_device: sanei_usb_get_vendor_product failed\n");
 | 
						|
      /* This is not a USB scanner, or SANE or the OS doesn't support it. */
 | 
						|
      sanei_usb_close (scan->fd);
 | 
						|
      scan->fd = -1;
 | 
						|
      return SANE_STATUS_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Make sure we have a CANON scanner */
 | 
						|
  if ((vendor != 0x04a9) || (product != 0x2204))
 | 
						|
    {
 | 
						|
      DBG (1, "CANON_open_device: incorrect vendor/product (0x%x/0x%x)\n",
 | 
						|
	   vendor, product);
 | 
						|
      sanei_usb_close (scan->fd);
 | 
						|
      scan->fd = -1;
 | 
						|
      return SANE_STATUS_UNSUPPORTED;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static const char *
 | 
						|
CANON_get_device_name (CANON_Handle * scanner)
 | 
						|
{
 | 
						|
  scanner = scanner;		/* Eliminate warning about unused parameters */
 | 
						|
  return "Canoscan FB630U";
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
CANON_finish_scan (CANON_Handle * scanner)
 | 
						|
{
 | 
						|
  DBG (3, "CANON_finish_scan:\n");
 | 
						|
  if (scanner->fp)
 | 
						|
    fclose (scanner->fp);
 | 
						|
  scanner->fp = NULL;
 | 
						|
 | 
						|
  /* remove temp file */
 | 
						|
  if (scanner->fname)
 | 
						|
    {
 | 
						|
      DBG (4, "removing temp file %s\n", scanner->fname);
 | 
						|
      unlink (scanner->fname);
 | 
						|
      free (scanner->fname);
 | 
						|
    }
 | 
						|
  scanner->fname = NULL;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
CANON_start_scan (CANON_Handle * scanner)
 | 
						|
{
 | 
						|
  int rv;
 | 
						|
  SANE_Status status;
 | 
						|
  DBG (3, "CANON_start_scan called\n");
 | 
						|
 | 
						|
  /* choose a temp file name for scan data */
 | 
						|
  scanner->fname = strdup ("/tmp/scan.XXXXXX");
 | 
						|
  if (!mktemp (scanner->fname))
 | 
						|
    return SANE_STATUS_IO_ERROR;
 | 
						|
 | 
						|
  /* calibrate if needed */
 | 
						|
  rv = init (scanner->fd);
 | 
						|
  if (rv < 0) {
 | 
						|
      DBG(1, "Can't talk on USB.\n");
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
  }
 | 
						|
  if ((rv == 1)
 | 
						|
      || !check_ogn_file () 
 | 
						|
      || (scanner->flags & FLG_FORCE_CAL)) {
 | 
						|
      plugin_cal (scanner);
 | 
						|
      wait_for_return (scanner->fd);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /* scan */
 | 
						|
  if ((status = scan (scanner)) != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      CANON_finish_scan (scanner);
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
 | 
						|
  /* read the temp file back out */
 | 
						|
  scanner->fp = fopen (scanner->fname, "r");
 | 
						|
  DBG (4, "reading %s\n", scanner->fname);
 | 
						|
  if (!scanner->fp)
 | 
						|
    {
 | 
						|
      DBG (1, "open %s", scanner->fname);
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
CANON_read (CANON_Handle * scanner, SANE_Byte * data,
 | 
						|
	    SANE_Int max_length, SANE_Int * length)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  int red_len;
 | 
						|
 | 
						|
  DBG (5, "CANON_read called\n");
 | 
						|
  if (!scanner->fp)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
  red_len = fread (data, 1, max_length, scanner->fp);
 | 
						|
  /* return some data */
 | 
						|
  if (red_len > 0)
 | 
						|
    {
 | 
						|
      *length = red_len;
 | 
						|
      DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length);
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
 | 
						|
  /* EOF or file err */
 | 
						|
  *length = 0;
 | 
						|
  if (feof (scanner->fp))
 | 
						|
    {
 | 
						|
      DBG (4, "EOF\n");
 | 
						|
      status = SANE_STATUS_EOF;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (4, "IO ERR\n");
 | 
						|
      status = SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  CANON_finish_scan (scanner);
 | 
						|
  DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length);
 | 
						|
  return status;
 | 
						|
}
 |