kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1704 wiersze
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1704 wiersze
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
| 
 | |
|    Copyright (C) 1999 Paul Mackerras
 | |
|    Copyright (C) 2000 Adrian Perez Jorge
 | |
|    Copyright (C) 2001 Frank Zago
 | |
|    Copyright (C) 2001 Marcio Teixeira
 | |
| 
 | |
|    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.
 | |
| 
 | |
|    Defines a driver and API for accessing the UMAX Astra 1220U
 | |
|    USB scanner. Based on the original command line tool by Paul
 | |
|    Mackerras.
 | |
| 
 | |
|    The UMAX Astra 1220U scanner uses the PowerVision PV8630
 | |
|    Parallel Port to USB bridge. This chip is also used
 | |
|    by the HP4200C flatbed scanner. Adrian Perez Jorge wrote
 | |
|    a nice interface file for that chip and Frank Zago adapted
 | |
|    it to use the sanei_usb interface. Thanks, guys, for making
 | |
|    my life easier! :)
 | |
| 
 | |
|  */
 | |
| 
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/ioctl.h>
 | |
| 
 | |
| /* 
 | |
|  * The backend performs test scans in order to calibrate
 | |
|  * the CCD and to find the zero location. If you would like
 | |
|  * to look at those scans, define DEBUG_CALIBRATION to have
 | |
|  * the backend save "find_zero.pgm" and "calibration.pgm" to
 | |
|  * disk.
 | |
|  */
 | |
| /*#define DEBUG_CALIBRATION*/
 | |
| 
 | |
| /*
 | |
|  * Define DEBUG_BOUNDS to insert paranoid array bounds
 | |
|  * overrun detection into the code.
 | |
|  */
 | |
| /* #define DEBUG_BOUNDS */
 | |
| 
 | |
| /* These values are empirically determined and are given
 | |
|  * in 1/600 inch units. If UMAX_MAX_HEIGHT is too large,
 | |
|  * the scanner may grind its gears. I assume there is a
 | |
|  * physical limit to UMAX_MAX_WIDTH as well (based on the
 | |
|  * sensor size) but I do not know what it is. The current
 | |
|  * value can be increased beyond what it is now, but you
 | |
|  * gain nothing in usuable scan area (you only scan more
 | |
|  * of the underside of the scanner's plastic lid).
 | |
|  */
 | |
| 
 | |
| #define UMAX_MAX_WIDTH    5400
 | |
| #define UMAX_MAX_HEIGHT   7040
 | |
| 
 | |
| /* Buffer size. Specifies the size of the buffer that is
 | |
|  * used to copy data from the scanner. The old command
 | |
|  * line driver had this set at 0x80000 which is likely
 | |
|  * the largest possible chunck of data that can be.
 | |
|  * at once. This is probably most efficient, but using
 | |
|  * a lower value for the SANE driver makes the driver
 | |
|  * more responsive to interaction.
 | |
|  */
 | |
| #define BUFFER_SIZE 0x80000
 | |
| 
 | |
| /* Constants that can be used with set_lamp_state to
 | |
|  * control the state of the scanner's lamp
 | |
|  */
 | |
| typedef enum
 | |
| {
 | |
|   UMAX_LAMP_OFF = 0,
 | |
|   UMAX_LAMP_ON = 1
 | |
| }
 | |
| UMAX_Lamp_State;
 | |
| 
 | |
| /* Constants that can be used with move to control
 | |
|  * the rate of scanner head movement
 | |
|  */
 | |
| typedef enum
 | |
| {
 | |
|   UMAX_NOT_FINE = 0,
 | |
|   UMAX_FINE = 1
 | |
| }
 | |
| UMAX_Speed;
 | |
| 
 | |
| /* If anyone knows some descriptive names for these,
 | |
|  * please update
 | |
|  */
 | |
| typedef enum
 | |
| {
 | |
|   CMD_0 = 0x00,
 | |
|   CMD_1 = 0x01,
 | |
|   CMD_2 = 0x02,
 | |
|   CMD_4 = 0x04,
 | |
|   CMD_8 = 0x08,
 | |
|   CMD_40 = 0x40,
 | |
|   CMD_WRITE = 0x80,
 | |
|   CMD_READ = 0xC0
 | |
| }
 | |
| UMAX_Cmd;
 | |
| 
 | |
| /* Product IDs for Astra scanners
 | |
|  */
 | |
| typedef enum
 | |
| {
 | |
|   ASTRA_1220U = 0x0010,
 | |
|   ASTRA_2000U = 0x0030,
 | |
|   ASTRA_2100U = 0x0130
 | |
| }
 | |
| UMAX_Model;
 | |
| 
 | |
| /* The bytes UMAX_SYNC1 and UMAX_SYNC2 serve as a
 | |
|  * synchronization signal. Unintentional sync bytes
 | |
|  * in the data stream are escaped with UMAX_ESCAPE
 | |
|  * character
 | |
|  */
 | |
| 
 | |
| #define UMAX_SYNC1  0x55
 | |
| #define UMAX_SYNC2  0xAA
 | |
| #define UMAX_ESCAPE 0x1B
 | |
| 
 | |
| /* Status bits. These bits are active low.
 | |
|  * In umax_pp, UMAX_REVERSE_BIT is called
 | |
|  * MOTOR_BIT.
 | |
|  */
 | |
| 
 | |
| #define UMAX_FORWARD_BIT       0x40
 | |
| #define UMAX_ERROR_BIT         0x20
 | |
| #define UMAX_MOTOR_OFF_BIT     0x08
 | |
| 
 | |
| #define UMAX_OK                0x48        /* Used to be 0xC8 */
 | |
| #define UMAX_OK_WITH_MOTOR     0x40        /* Used to be 0xD0 */
 | |
| 
 | |
| #define UMAX_STATUS_MASK       0x68
 | |
| 
 | |
| /* This byte is used as a placeholder for bytes that are parameterized
 | |
|  * in the opcode strings */
 | |
| 
 | |
| #define XXXX 0x00
 | |
| 
 | |
| /* This macro is used to check the return code of
 | |
|  * functions
 | |
|  */
 | |
| #define CHK(A) {if( (res = A) != SANE_STATUS_GOOD ) { \
 | |
|                  DBG( 1, "Failure on line of %s: %d\n", __FILE__, \
 | |
|                       __LINE__ ); return A; }}
 | |
| 
 | |
| /* Macros that are used for array overrun detection
 | |
|  * (when DEBUG_BOUNDS is defined)
 | |
|  */
 | |
| #ifdef DEBUG_BOUNDS
 | |
| #define PAD 10
 | |
| #define PAD_ARRAY( A, len ) {int i; \
 | |
|                  for( i = 0; i < PAD; i++ ) {A[len+i]=0x55;}}
 | |
| 
 | |
| #define CHK_ARRAY( A, len ) {int i;for( i = 0; i < PAD; i++ ) {\
 | |
|    if(A[len+i]!=0x55) { \
 | |
|      DBG( 1, "Array overrun detected on line %d\n", __LINE__ ); \
 | |
|    }}}
 | |
| #else
 | |
| #define PAD 0
 | |
| #define PAD_ARRAY( A, len )
 | |
| #define CHK_ARRAY( A, len )
 | |
| #endif
 | |
| 
 | |
| /* This data structure contains data related
 | |
|  * to the scanning process.
 | |
|  */
 | |
| typedef struct
 | |
| {
 | |
|   /* Constant data */
 | |
| 
 | |
|   int color;
 | |
|   int w;
 | |
|   int h;
 | |
|   int xo;
 | |
|   int yo;
 | |
|   int xdpi;                      /* Physical x dpi */
 | |
|   int ydpi;                      /* Physical y dpi */
 | |
|   int xsamp;
 | |
|   int ysamp;
 | |
| 
 | |
|   int xskip;
 | |
|   int yskip;
 | |
| 
 | |
|   int fd;                        /* Device file handle */
 | |
|   UMAX_Model model;
 | |
| 
 | |
|   /* Raw scan data buffer */
 | |
| 
 | |
|   unsigned char *p;
 | |
|   int bh;                        /* Size of buffer in lines    */
 | |
|   int hexp;                      /* Scan lines yet to be read  */
 | |
| 
 | |
|   /* Decoding logic */
 | |
| 
 | |
|   int x, y, maxh;
 | |
|   int done;                      /* Boolean, all lines decoded */
 | |
| 
 | |
|   /* Calibration data */
 | |
| 
 | |
|   unsigned char caldata[16070 + PAD];
 | |
| 
 | |
|   /* Scan head position */
 | |
| 
 | |
|   int scanner_ypos;
 | |
|   int scanner_yorg;
 | |
| }
 | |
| UMAX_Handle;
 | |
| 
 | |
| typedef unsigned char UMAX_Status_Byte;
 | |
| 
 | |
| /* This seems to configure the pv8630 chip somehow. I wish
 | |
|  * all the magic numbers were defined as self-descriptive
 | |
|  * constants somewhere. I made some guesses based on what
 | |
|  * I found in "pv8630.c", but alas there wasn't enough in
 | |
|  * there. If you know what this does, please let me know!
 | |
|  */
 | |
| static SANE_Status
 | |
| xxxops (UMAX_Handle * scan)
 | |
| {
 | |
|   SANE_Status res;
 | |
| 
 | |
|   DBG (9, "doing xxxops\n");
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_RMODE, 0x02));
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x0E));
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_RDATA, 0x40));
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x06));
 | |
|   CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_RSTATUS, 0x38, 0xFF));
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x07));
 | |
|   CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_RSTATUS, 0x38, 0xFF));
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x04));
 | |
|   CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_RSTATUS, 0xF8, 0xFF));
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x05));
 | |
|   CHK (sanei_pv8630_xpect_byte (scan->fd, PV8630_UNKNOWN, 0x05, 0xFF));
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_UNKNOWN, 0x04));
 | |
| 
 | |
|   CHK (sanei_pv8630_write_byte (scan->fd, PV8630_RMODE, 0x1E));
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Apparently sends the two syncronization characters followed
 | |
| by the command length, followed by the command number
 | |
| */
 | |
| static SANE_Status
 | |
| usync (UMAX_Handle * scan, UMAX_Cmd cmd, int len)
 | |
| {
 | |
|   UMAX_Status_Byte s0, s4;
 | |
|   SANE_Status res;
 | |
|   unsigned char buf[4];
 | |
|   size_t nb;
 | |
| 
 | |
|   DBG (80, "usync: len = %d, cmd = %d\n", len, cmd);
 | |
| 
 | |
|   buf[0] = UMAX_SYNC1;
 | |
|   buf[1] = UMAX_SYNC2;
 | |
| 
 | |
|   nb = 2;
 | |
|   CHK (sanei_pv8630_flush_buffer (scan->fd));
 | |
|   CHK (sanei_pv8630_prep_bulkwrite (scan->fd, nb));
 | |
|   CHK (sanei_pv8630_bulkwrite (scan->fd, buf, &nb));
 | |
| 
 | |
|   CHK (sanei_pv8630_wait_byte
 | |
|        (scan->fd, PV8630_RSTATUS, UMAX_OK, UMAX_STATUS_MASK, 20));
 | |
| 
 | |
|   buf[0] = len >> 16;
 | |
|   buf[1] = len >> 8;
 | |
|   buf[2] = len;
 | |
|   buf[3] = cmd;
 | |
| 
 | |
|   nb = 4;
 | |
|   CHK (sanei_pv8630_flush_buffer (scan->fd));
 | |
|   CHK (sanei_pv8630_prep_bulkwrite (scan->fd, nb));
 | |
|   CHK (sanei_pv8630_bulkwrite (scan->fd, buf, &nb));
 | |
| 
 | |
|   CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RDATA, &s0));
 | |
|   CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RSTATUS, &s4));
 | |
| 
 | |
|   DBG (90, "usync: s0 = %#x s4 = %#x\n", s0, s4);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /*
 | |
| This function escapes any syncronization sequence that may be
 | |
| in data, storing the result in buf. In the worst case where
 | |
| every character gets escaped buf must be at least twice as
 | |
| large as dlen.
 | |
| */
 | |
| static int
 | |
| bescape (const unsigned char *data, int dlen, unsigned char *buf, int blen)
 | |
| {
 | |
|   const unsigned char *p;
 | |
|   unsigned char *q;
 | |
|   int i, c;
 | |
|   i = blen;   /* Eliminate compiler warning about unused param */
 | |
| 
 | |
|   p = data;
 | |
|   q = buf;
 | |
|   for (i = 0; i < dlen; ++i)
 | |
|     {
 | |
|       c = *p++;
 | |
|       if (c == UMAX_ESCAPE
 | |
|           || (c == UMAX_SYNC2 && i > 0 && p[-2] == UMAX_SYNC1))
 | |
|         *q++ = UMAX_ESCAPE;
 | |
|       *q++ = c;
 | |
| #ifdef DEBUG_BOUNDS
 | |
|       if (q > (buf + blen))
 | |
|         {
 | |
|           DBG (2, "Overrun error in bescape!\n");
 | |
|         }
 | |
| #endif
 | |
|     }
 | |
|   return q - buf;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| cwrite (UMAX_Handle * scan, UMAX_Cmd cmd, size_t len, const unsigned char *data,
 | |
|         UMAX_Status_Byte * s)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s0, s4;
 | |
| 
 | |
|   static unsigned char *escaped = NULL;
 | |
|   static size_t escaped_size = 0;
 | |
| 
 | |
|   DBG (80, "cwrite: cmd = %d, len = %d\n", cmd, len);
 | |
| 
 | |
|   CHK (usync (scan, cmd | CMD_WRITE, len));
 | |
| 
 | |
|   if (len <= 0)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   if (escaped_size < len * 2)
 | |
|     {
 | |
|       escaped_size = len * 2;
 | |
|       if (escaped)
 | |
|         free (escaped);
 | |
|       escaped = malloc (escaped_size);
 | |
|       if (escaped == NULL)
 | |
|         return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   len = bescape (data, len, escaped, len * 2);
 | |
| 
 | |
|   CHK (sanei_pv8630_wait_byte
 | |
|        (scan->fd, PV8630_RSTATUS, UMAX_OK, UMAX_STATUS_MASK, 20));
 | |
| 
 | |
|   CHK (sanei_pv8630_flush_buffer (scan->fd));
 | |
|   CHK (sanei_pv8630_prep_bulkwrite (scan->fd, len));
 | |
|   CHK (sanei_pv8630_bulkwrite (scan->fd, escaped, &len));
 | |
| 
 | |
|   CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RSTATUS, &s4));
 | |
|   CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RDATA, &s0));
 | |
| 
 | |
|   DBG (90, "cwrite: s0 = %#x s4 = %#x\n", s0, s4);
 | |
| 
 | |
|   if (s)
 | |
|     *s = s0;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| cread (UMAX_Handle * scan, UMAX_Cmd cmd, size_t len, unsigned char *data,
 | |
|        UMAX_Status_Byte * s)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s0, s4;
 | |
| 
 | |
|   DBG (80, "cread: cmd = %d, len = %d\n", cmd, len);
 | |
| 
 | |
|   CHK (usync (scan, cmd | CMD_READ, len));
 | |
| 
 | |
|   if (len > 0)
 | |
|     {
 | |
|       CHK (sanei_pv8630_wait_byte
 | |
|            (scan->fd, PV8630_RSTATUS, UMAX_OK_WITH_MOTOR, UMAX_STATUS_MASK,
 | |
|             2000));
 | |
| 
 | |
|       while (len > 0)
 | |
|         {
 | |
|           size_t req, n;
 | |
| 
 | |
|           req = n = (len > 0xf000) ? 0xf000 : len;
 | |
|           CHK (sanei_pv8630_prep_bulkread (scan->fd, n));
 | |
|           CHK (sanei_pv8630_bulkread (scan->fd, data, &n));
 | |
|           if (n < req)
 | |
|             {
 | |
|               DBG (1, "qread: Expecting to read %d, only got %d\n", req, n);
 | |
|               return SANE_STATUS_IO_ERROR;
 | |
|             }
 | |
|           data += n;
 | |
|           len -= n;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RSTATUS, &s4));
 | |
|   CHK (sanei_pv8630_read_byte (scan->fd, PV8630_RDATA, &s0));
 | |
| 
 | |
|   DBG (90, "cwrite: s0 = %#x s4 = %#x\n", s0, s4);
 | |
| 
 | |
|   if (s)
 | |
|     *s = s0;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* Seems to be like cwrite, with a verification option? */
 | |
| static SANE_Status
 | |
| cwritev (UMAX_Handle * scan, UMAX_Cmd cmd, size_t len, const unsigned char *data,
 | |
|          UMAX_Status_Byte * s)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   unsigned char buf[16384];
 | |
| 
 | |
|   /* Write out the opcode */
 | |
| 
 | |
|   CHK (cwrite (scan, cmd, len, data, s));
 | |
|   if (len <= 0)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   /* Read the opcode back */
 | |
| 
 | |
|   CHK (cread (scan, cmd, len, buf, NULL));
 | |
|   if (bcmp (buf, data, len))
 | |
|     {
 | |
|       DBG (1, "cwritev: verification failed\n");
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| csend (UMAX_Handle * scan, UMAX_Cmd cmd)
 | |
| {
 | |
|   DBG (80, "csend: cmd = %d\n", cmd);
 | |
| 
 | |
|   return usync (scan, cmd, 0);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static void
 | |
| unused_operations ()
 | |
| {
 | |
|   /* These operations are unused anywhere in the driver */
 | |
| 
 | |
|   unsigned char opb8[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
 | |
|     0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0x18, 0x10, 0x03,
 | |
|     0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x13, 0x1a
 | |
|   };
 | |
| 
 | |
|   unsigned char opb9[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
 | |
|     0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0x41, 0x20, 0x24,
 | |
|     0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x13, 0x1a
 | |
|   };
 | |
| 
 | |
|   unsigned char opb10[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
 | |
|     0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0x41, 0x60, 0x4f,
 | |
|     0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x93, 0x1a
 | |
|   };
 | |
| 
 | |
|   unsigned char opc5[16] = {
 | |
|     0x05, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x00,
 | |
|     0x00, 0x30, 0x0c, 0xc3, 0xa4, 0x00
 | |
|   };
 | |
| 
 | |
|   unsigned char opc6[16] = {
 | |
|     0x01, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x00,
 | |
|     0x88, 0x48, 0x0c, 0x83, 0xa4, 0x00
 | |
|   };
 | |
| 
 | |
|   unsigned char opc7[16] = {
 | |
|     0x05, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x00,
 | |
|     0xec, 0x4e, 0x0c, 0xc3, 0xa4, 0x00
 | |
|   };
 | |
| 
 | |
|   unsigned char opd2[8] = {
 | |
|     0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, 0x00, 0x30
 | |
|   };
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static SANE_Status
 | |
| cwritev_opc1_lamp_ctrl (UMAX_Handle * scan, UMAX_Lamp_State state)
 | |
| {
 | |
|   unsigned char opc1[16] = {
 | |
|     0x01, 0x00, 0x01, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x13, 0x05,
 | |
|     0x00, 0x00, 0x00, 0x80, 0xf0, 0x00
 | |
|   };
 | |
| 
 | |
|   DBG (9, "cwritev_opc1: set lamp state = %s\n",
 | |
|        (state == UMAX_LAMP_OFF) ? "off" : "on");
 | |
|   opc1[14] = (state == UMAX_LAMP_OFF) ? 0x90 : 0xf0;
 | |
|   return cwritev (scan, CMD_2, 16, opc1, NULL);
 | |
| }
 | |
| 
 | |
| /* Used by umaxinit and finish_scan */
 | |
| 
 | |
| static SANE_Status
 | |
| cwritev_opb3_restore (UMAX_Handle * scan)
 | |
| {
 | |
|   unsigned char opb3[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
 | |
|     0xc1, 0x80, 0x00, 0x00, 0x04, 0x00, 0x16, 0x80, 0x15, 0x78,
 | |
|     0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x1b, 0x1a,
 | |
|   };
 | |
| 
 | |
|   SANE_Status res;
 | |
| 
 | |
|   DBG (9, "cwritev_opb3_restore:\n");
 | |
|   CHK (cwritev (scan, CMD_8, 35, opb3, NULL));
 | |
|   CHK (csend (scan, CMD_40));
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* Used in move and get_caldata */
 | |
| static unsigned char ope[8] = {
 | |
|   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
 | |
| };
 | |
| 
 | |
| /*
 | |
| This function seems to perform various things. First, it loads a default
 | |
| gamma information (which is used for the calibration scan), returns the
 | |
| head to the park position, and turns the lamp on. This function used to
 | |
| be split up into two parts, umaxinit and umaxinit2.
 | |
| */
 | |
| 
 | |
| static SANE_Status
 | |
| umaxinit (UMAX_Handle * scan)
 | |
| {
 | |
|   unsigned char opb[34] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
 | |
|     0xc1, 0x80, 0x60, 0x20, 0x00, 0x00, 0x16, 0x41, 0xe0, 0xac,
 | |
|     0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x00, 0x00, 0x00,
 | |
|     0x00, 0x00, 0x00, 0xf0,
 | |
|   };
 | |
|   unsigned char opb1[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
 | |
|     0xc1, 0x80, 0x00, 0x20, 0x02, 0x00, 0x16, 0x41, 0xe0, 0xac,
 | |
|     0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x00, 0x00, 0x00,
 | |
|     0x00, 0x00, 0x00, 0x10, 0x1a
 | |
|   };
 | |
|   unsigned char opb2[35] = {
 | |
|     0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
 | |
|     0xc1, 0x80, 0x00, 0x20, 0x02, 0x00, 0x16, 0x41, 0xe0, 0xac,
 | |
|     0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x00, 0x00, 0x00,
 | |
|     0x00, 0x00, 0x00, 0x10, 0x1a,
 | |
|   };
 | |
|   unsigned char opb4[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x03,
 | |
|     0xc1, 0x80, 0x60, 0x20, 0x00, 0x00, 0x16, 0x41, 0xe0, 0xac,
 | |
|     0x03, 0x03, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0xf3, 0x1b
 | |
|   };
 | |
|   unsigned char opbx[35];
 | |
|   unsigned char opc[16] = {
 | |
|     0x02, 0x80, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x07,
 | |
|     0x00, 0x00, 0x00, 0x80, 0xf0, 0x00
 | |
|   };
 | |
|   unsigned char opcx[16];
 | |
|   unsigned char opd[8] = {
 | |
|     0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa
 | |
|   };
 | |
| 
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s;
 | |
|   unsigned char ramp[800];
 | |
|   int i;
 | |
|   unsigned char *p;
 | |
| 
 | |
|   DBG (3, "umaxinit called\n");
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (xxxops (scan));
 | |
| 
 | |
|   CHK (cwritev (scan, CMD_8, 34, opb, &s));
 | |
|   CHK (cread (scan, CMD_8, 35, opbx, &s));
 | |
| 
 | |
|   CHK (cwritev (scan, CMD_8, 35, opb1, &s));
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 2:\n");
 | |
| 
 | |
|   /* The following code appears to send three 256 entry, 8-bit gamma tables
 | |
|    * to the scanner
 | |
|    */
 | |
|   p = ramp;
 | |
|   *p++ = 0;
 | |
|   *p++ = 0;
 | |
|   *p++ = 0;
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     *p++ = i;
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     *p++ = i;
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     *p++ = i;
 | |
|   *p++ = 0xaa;
 | |
|   *p++ = 0xaa;
 | |
| 
 | |
|   res = cwritev (scan, CMD_4, p - ramp, ramp, &s);
 | |
|   if (res != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (4, "umaxinit: Writing ramp 1 failed (is this a 2000U?)\n");
 | |
|     }
 | |
|   CHK (cwritev (scan, CMD_8, 35, opb1, &s));
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 3:\n");
 | |
| 
 | |
|   /* The following code appears to send a 256 entry, 16-bit gamma table
 | |
|    * to the scanner
 | |
|    */
 | |
|   p = ramp;
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     {
 | |
|       *p++ = i;
 | |
|       *p++ = 0;
 | |
|     }
 | |
| 
 | |
|   res = cwrite (scan, CMD_4, p - ramp, ramp, &s);
 | |
|   if (res != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (4, "umaxinit: Writing ramp 2 failed (is this a 2000U?)\n");
 | |
|     }
 | |
|   CHK (cwritev (scan, CMD_8, 35, opb2, &s));
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 4:\n");
 | |
| 
 | |
|   /* The following code appears to send a 256 entry, 16-bit gamma table
 | |
|    * to the scanner.
 | |
|    */
 | |
|   p = ramp;
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     {
 | |
|       *p++ = i;
 | |
|       *p++ = 4;
 | |
|     }
 | |
| 
 | |
|   res = cwritev (scan, CMD_4, p - ramp, ramp, &s);
 | |
|   if (res != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (4, "umaxinit: Writing ramp 3 failed (is this a 2000U?)\n");
 | |
|     }
 | |
|   CHK (cwritev (scan, CMD_8, 35, opb1, &s));
 | |
| 
 | |
|   CHK (cwritev (scan, CMD_2, 16, opc, NULL));
 | |
|   CHK (cwritev (scan, CMD_1, 8, opd, NULL));
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 5: s = %#x\n", s);
 | |
| 
 | |
|   if ((s & 0x40) == 0)
 | |
|     {
 | |
|       DBG (4, "umaxinit: turning on lamp and restoring\n");
 | |
|       CHK (cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON));
 | |
|       CHK (cwritev_opb3_restore (scan));
 | |
| 
 | |
|       for (i = 0; i < 60; ++i)
 | |
|         {
 | |
|           CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
|           DBG (4, "umaxinit: s = %#x\n", s);
 | |
|           if ((s & 0x40) != 0)
 | |
|             break;
 | |
|           DBG (4, "umaxinit: sleeping\n");
 | |
|           usleep (500000);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 6\n");
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
| 
 | |
|   /* The following stuff used to be in umaxinit2() */
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 7\n");
 | |
| 
 | |
|   CHK (xxxops (scan));
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (xxxops (scan));
 | |
| 
 | |
|   CHK (cwritev (scan, CMD_8, 34, opb4, &s));
 | |
|   CHK (cread (scan, CMD_8, 35, opbx, &s));
 | |
|   CHK (cread (scan, CMD_2, 16, opcx, &s));
 | |
| 
 | |
|   CHK (cwritev (scan, CMD_2, 16, opc, NULL));
 | |
|   CHK (cwritev (scan, CMD_1, 8, opd, NULL));
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (4, "umaxinit: checkpoint 8: s = %d\n", s);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| move (UMAX_Handle * scan, int distance, UMAX_Speed fine)
 | |
| {
 | |
|   unsigned char opc4[16] = {
 | |
|     0x01, XXXX, XXXX, XXXX, 0x00, 0x00, 0x60, 0x2f, XXXX, XXXX,
 | |
|     0x00, 0x00, 0x00, 0x80, XXXX, 0x00
 | |
|   };
 | |
|   unsigned char opb5[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
 | |
|     0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xf6, 0x79, 0xbf,
 | |
|     0x06, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x13, 0x1a
 | |
|   };
 | |
|   unsigned char opb7[35] = {
 | |
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | |
|     0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xf6, 0x79, 0xbf,
 | |
|     0x01, 0x00, 0x00, 0x00, 0x46, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x13, 0x1a
 | |
|   };
 | |
|   unsigned char ope2[3] = {
 | |
|     0x00, 0xff, 0x8f
 | |
|   };
 | |
|   unsigned char buf[512 + PAD];
 | |
| 
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s;
 | |
| 
 | |
|   SANE_Bool rev = distance < 0;
 | |
|   int skip = (rev ? -distance : distance) - 1;
 | |
| 
 | |
|   DBG (9, "move: distance = %d, scanner_ypos = %d\n", distance,
 | |
|        scan->scanner_ypos);
 | |
| 
 | |
|   PAD_ARRAY (buf, 512);
 | |
| 
 | |
|   if (distance == 0)
 | |
|     return SANE_STATUS_GOOD;
 | |
| 
 | |
|   opc4[1] = skip << 6;
 | |
|   opc4[2] = skip >> 2;
 | |
|   opc4[3] = (rev ? 0x20 : 0x70) + ((skip >> 10) & 0xf);
 | |
|   opc4[9] = rev ? 0x01 : 0x05;
 | |
| 
 | |
|   if (fine == UMAX_FINE)
 | |
|     {
 | |
|       opc4[8]  = 0x2f;
 | |
|       opc4[14] = 0xa4;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       opc4[8]  = 0x17;
 | |
|       opc4[14] = 0xac;
 | |
|     }
 | |
| 
 | |
|   scan->scanner_ypos +=
 | |
|     (fine == UMAX_FINE ? distance : 2 * distance + (rev ? -1 : 1));
 | |
|   scan->scanner_ypos = (scan->scanner_ypos + (rev ? 0 : 3)) & ~3;
 | |
| 
 | |
|   CHK (cwrite (scan, CMD_2, 16, opc4, &s));
 | |
|   CHK (cwrite (scan, CMD_8, 35, rev ? opb7 : opb5, &s));
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
|   DBG (10, "move: checkpoint 1: s = %d\n", s);
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   if (rev)
 | |
|     {
 | |
|       CHK (cwrite (scan, CMD_4, 3, ope2, &s));
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       CHK (cwrite (scan, CMD_4, 8, ope, &s));
 | |
|     }
 | |
| 
 | |
|   CHK (csend (scan, CMD_40));
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (10, "move: checkpoint 2: s = %d\n", s);
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
|   DBG (10, "move: checkpoint 3: s = %d\n", s);
 | |
| 
 | |
|   CHK (cread (scan, CMD_4, 512, buf, &s));
 | |
| 
 | |
|   CHK_ARRAY (buf, 512);
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| get_pixels (UMAX_Handle * scan, unsigned char *op2, unsigned char *op8,
 | |
|             unsigned char *op1, unsigned char *op4, int len, int zpos,
 | |
|             unsigned char *buf)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s;
 | |
| 
 | |
|   DBG (9, "get_pixels: len = %d, zpos = %d\n", len, zpos);
 | |
| 
 | |
|   if (zpos == 0)
 | |
|     CHK (csend (scan, CMD_0));
 | |
| 
 | |
|   CHK (cwrite (scan, CMD_2, 16, op2, &s));
 | |
|   CHK (cwrite (scan, CMD_8, 35, op8, &s));
 | |
|   CHK (cwrite (scan, CMD_1, 8, op1, &s));
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   if (zpos == 1)
 | |
|     CHK (csend (scan, CMD_0));
 | |
| 
 | |
|   CHK (cwrite (scan, CMD_4, 8, op4, &s));
 | |
|   CHK (csend (scan, CMD_40));
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   CHK (cread (scan, CMD_4, len, buf, &s));
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static int
 | |
| locate_black_stripe (unsigned char *img, int w, int h)
 | |
| {
 | |
|   int epos, ecnt, x, y;
 | |
|   unsigned char *p;
 | |
| 
 | |
|   epos = 0;
 | |
|   ecnt = 0;
 | |
|   p = img;
 | |
|   for (x = 0; x < w; ++x, ++p)
 | |
|     {
 | |
|       int d, dmax = 0, dpos = 0;
 | |
|       unsigned char *q = img + x;
 | |
|       for (y = 1; y < h; ++y, q += w)
 | |
|         {
 | |
|           d = q[0] - q[w];
 | |
|           if (d > dmax)
 | |
|             {
 | |
|               dmax = d;
 | |
|               dpos = y;
 | |
|             }
 | |
|         }
 | |
|       if (dmax > 0)
 | |
|         {
 | |
|           epos += dpos;
 | |
|           ++ecnt;
 | |
|         }
 | |
|     }
 | |
|   if (ecnt == 0)
 | |
|     epos = 70;
 | |
|   else
 | |
|     epos = (epos + ecnt / 2) / ecnt;
 | |
|   return epos;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| find_zero (UMAX_Handle * scan)
 | |
| {
 | |
|   unsigned char opc3[16] = {
 | |
|     0xb4, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2f, 0x05,
 | |
|     0x00, 0x00, 0x00, 0x80, 0xa4, 0x00
 | |
|   };
 | |
|   unsigned char ope1[8] = {
 | |
|     0x00, 0x00, 0x00, 0xaa, 0xcc, 0xee, 0x80, 0xff
 | |
|   };
 | |
|   unsigned char opb6[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
 | |
|     0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xfb, 0xc4, 0xe5,
 | |
|     0x06, 0x00, 0x00, 0x60, 0x4d, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x13, 0x1a
 | |
|   };
 | |
|   unsigned char opd1[8] = {
 | |
|     0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, 0x08, 0x00
 | |
|   };
 | |
| 
 | |
|   SANE_Status res;
 | |
|   int s;
 | |
|   unsigned char *img;
 | |
| 
 | |
|   DBG (9, "find_zero:\n");
 | |
| 
 | |
|   img = malloc (54000);
 | |
|   if (img == 0)
 | |
|     {
 | |
|       DBG (1, "out of memory (need 54000)\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (get_pixels (scan, opc3, opb6, opd1, ope1, 54000, 1, img));
 | |
| 
 | |
| #ifdef DEBUG_CALIBRATION
 | |
|   {
 | |
|     int w = 300, h = 180;
 | |
|     FILE *f2 = fopen ("find_zero.pgm", "wb");
 | |
|     fprintf (f2, "P5\n%d %d\n255\n", w, h);
 | |
|     fwrite (img, 1, w * h, f2);
 | |
|     fclose (f2);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   s = locate_black_stripe (img, 300, 180);
 | |
|   scan->scanner_yorg = scan->scanner_ypos + s + 64;
 | |
|   scan->scanner_ypos += 180 + 3;
 | |
|   scan->scanner_ypos &= ~3;
 | |
| 
 | |
|   free (img);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static SANE_Status
 | |
| calib (UMAX_Handle * scan)
 | |
| {
 | |
|   unsigned char buf[65536];
 | |
|   opc5[11] = 0x30;
 | |
|   opd2[7]  = 0x30;
 | |
|   CHK (get_pixels (scan, opc5, opb8, opd2, ope, 24, 0, buff));
 | |
| 
 | |
|   opc5[11] = 0x40;
 | |
|   opd2[7]  = 0x40;
 | |
|   CHK (get_pixels (scan, opc5, opb8, opd2, ope, 24, 0, buff));
 | |
| 
 | |
|   opd2[6] = 8;
 | |
|   opd2[7] = 0x30;
 | |
|   CHK (get_pixels (scan, opc6, opb9, opd2, ope, 0x200, 1, buff));
 | |
| 
 | |
|   opc7[10] = 0xec;
 | |
|   opd2[6]  = 0xc;
 | |
|   opd2[7]  = 0x40;
 | |
|   CHK (get_pixels (scan, opc7, opb10, opd2, ope, 5300, 1, buff));
 | |
| 
 | |
|   opc7[10] = 0xed;
 | |
|   opd2[6]  = 0xd;
 | |
|   CHK (get_pixels (scan, opc7, opb10, opd2, ope, 5300, 0, buff));
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
| Format of caldata:
 | |
| 
 | |
|    5100 bytes of CCD calibration values
 | |
|    5100 bytes of CCD calibration values
 | |
|    5100 bytes of CCD calibration values
 | |
|    256 bytes of gamma data for blue
 | |
|    256 bytes of gamma data for green
 | |
|    256 bytes of gamma data for red
 | |
|    2 bytes of extra information
 | |
| 
 | |
| */
 | |
| static SANE_Status
 | |
| get_caldata (UMAX_Handle * scan, int color)
 | |
| {
 | |
|   unsigned char opc9[16] = {
 | |
|     XXXX, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x17, 0x05,
 | |
|     0xec, 0x4e, 0x0c, XXXX, 0xac
 | |
|   };
 | |
|   unsigned char opb11[35] = {
 | |
|     0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
 | |
|     0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, 0xad, 0xa0, 0x49,
 | |
|     0x06, 0x00, 0x00, XXXX, XXXX, 0xa0, 0x00, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, 0x93, 0x1b
 | |
|   };
 | |
|   unsigned char opd4[8] = {
 | |
|     0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, XXXX, XXXX
 | |
|   };
 | |
|   SANE_Status res;
 | |
| 
 | |
|   unsigned char *p;
 | |
|   int h  = 66;
 | |
|   int w  = color ? 3 * 5100 : 5100;
 | |
|   int x0 = color ? 0 : 5100;
 | |
|   int l  = w * h;
 | |
|   int i, x, y;
 | |
| 
 | |
|   PAD_ARRAY (scan->caldata, 16070);
 | |
| 
 | |
|   DBG (9, "get_caldata: color = %d\n", color);
 | |
| 
 | |
|   p = malloc (l);
 | |
|   if (p == 0)
 | |
|     {
 | |
|       DBG (1, "out of memory (need %d)\n", l);
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
|   memset (scan->caldata, 0, 3 * 5100);
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   opc9[0] = h + 4;
 | |
|   if (color)
 | |
|     {
 | |
|       opc9[13]  = 0x03;
 | |
|       opb11[23] = 0xc4;
 | |
|       opb11[24] = 0x5c;
 | |
|       opd4[6]   = 0x08;
 | |
|       opd4[7]   = 0x00;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       opc9[13]  = 0xc3;
 | |
|       opb11[23] = 0xec;
 | |
|       opb11[24] = 0x54;
 | |
|       opd4[6]   = 0x0c;
 | |
|       opd4[7]   = 0x40;
 | |
|     }
 | |
| 
 | |
|   /* Do a test scan of the calibration strip (which is located
 | |
|    * under the scanner's lid */
 | |
| 
 | |
|   CHK (get_pixels (scan, opc9, opb11, opd4, ope, l, 0, p));
 | |
| 
 | |
| #ifdef DEBUG_CALIBRATION
 | |
|   {
 | |
|     FILE *f2 = fopen ("calibration.pgm", "wb");
 | |
|     fprintf (f2, "P5\n%d %d\n255\n", w, h);
 | |
|     fwrite (p, 1, w * h, f2);
 | |
|     fclose (f2);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   scan->scanner_ypos += (h + 4) * 2 + 3;
 | |
|   scan->scanner_ypos &= ~3;
 | |
| 
 | |
|   /* The following loop computes the gain for each of the CCD pixel
 | |
|    * elements.
 | |
|    */
 | |
|   for (x = 0; x < w; ++x)
 | |
|     {
 | |
|       int t = 0, gn;
 | |
|       double av, gain;
 | |
| 
 | |
|       for (y = 0; y < h; ++y)
 | |
|         t += p[x + y * w];
 | |
|       av = (double) t / h;
 | |
|       gain = 250 / av;
 | |
|       gn = (int) ((gain - 0.984) * 102.547 + 0.5);
 | |
|       if (gn < 0)
 | |
|         gn = 0;
 | |
|       else if (gn > 255)
 | |
|         gn = 255;
 | |
|       scan->caldata[x + x0] = gn;
 | |
|     }
 | |
| 
 | |
|   /* Gamma table for blue */
 | |
| 
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     scan->caldata[i + 3 * 5100 + 0] = i;
 | |
| 
 | |
|   /* Gamma table for green */
 | |
| 
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     scan->caldata[i + 3 * 5100 + 256] = i;
 | |
| 
 | |
|   /* Gamma table for red */
 | |
| 
 | |
|   for (i = 0; i < 256; ++i)
 | |
|     scan->caldata[i + 3 * 5100 + 512] = i;
 | |
| 
 | |
|   free (p);
 | |
| 
 | |
|   CHK_ARRAY (scan->caldata, 16070);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| send_scan_parameters (UMAX_Handle * scan)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s;
 | |
| 
 | |
|   /* Appears to correspond to opscan in umax_pp_low.c */
 | |
|   unsigned char opbgo[35] = {
 | |
|     0x00, 0x00, 0xb0, 0x4f, 0xd8, 0xe7, 0xfa, 0x10, 0xef, 0xc4,
 | |
|     0x3c, 0x71, 0x0f, 0x00, 0x04, 0x00, 0x6e, XXXX, XXXX, XXXX,
 | |
|     0xc4, 0x7e, 0x00, XXXX, XXXX, 0xa0, 0x0a, 0x8b, 0x49, 0x4a,
 | |
|     0xd0, 0x68, 0xdf, XXXX, 0x1a
 | |
|   };
 | |
| 
 | |
|   /* Appears to correspond to opsc53 in umax_pp_low.c */
 | |
|   unsigned char opcgo[16] = {
 | |
|     XXXX, XXXX, XXXX, XXXX, 0xec, 0x03, XXXX, XXXX, XXXX, XXXX,
 | |
|     0xec, 0x4e, XXXX, XXXX, XXXX
 | |
|   };
 | |
| 
 | |
|   /* Appears to correspond to opsc04 in umax_pp_low.c */
 | |
|   unsigned char opdgo[8] = {
 | |
|     0x06, 0xf4, 0xff, 0x81, 0x3d, 0x00, XXXX, XXXX
 | |
|   };
 | |
| 
 | |
|   unsigned char subsamp[9] = {
 | |
|     0xff, 0xff, 0xaa, 0xaa, 0x88, 0x88, 0x88, 0x88, 0x80
 | |
|   };
 | |
| 
 | |
|   const int xend =
 | |
|     scan->xskip + scan->w * scan->xsamp + (scan->xsamp + 1) / 2;
 | |
|   const int ytot = scan->hexp * scan->ysamp + 12;
 | |
| 
 | |
|   opbgo[17] = scan->xskip;
 | |
|   opbgo[18] = ((scan->xskip >> 8) & 0xf) + (xend << 4);
 | |
|   opbgo[19] = xend >> 4;
 | |
|   opbgo[33] = 0x33 + ((xend & 0x1000) >> 5) + ((scan->xskip & 0x1000) >> 6);
 | |
| 
 | |
|   /* bytes per line */
 | |
| 
 | |
|   opbgo[23] = scan->color ? 0xc6 : 0x77;
 | |
|   opbgo[24] = scan->color ? 0x5b : 0x4a;
 | |
| 
 | |
|   /* Scan height */
 | |
| 
 | |
|   opcgo[0] = ytot;
 | |
|   opcgo[1] = ((ytot >> 8) & 0x3f) + (scan->yskip << 6);
 | |
|   opcgo[2] = scan->yskip >> 2;
 | |
|   opcgo[3] = 0x50 + ((scan->yskip >> 10) & 0xf);
 | |
| 
 | |
|   /* This is what used to be here:
 | |
| 
 | |
|      opcgo[6] = bh == h? 0: 0x60;       // a guess
 | |
| 
 | |
|      I replaced it with what umax_pp_low.c uses, since it
 | |
|      made more sense
 | |
|    */
 | |
|   opcgo[6]  = (scan->ydpi <= 300) ? 0x00 : 0x60;
 | |
|   opcgo[8]  = (scan->ydpi <= 300) ? 0x17 : 0x2F;
 | |
|   opcgo[9]  = (scan->ydpi >= 300) ? 0x05 : 0x07;
 | |
|   opcgo[14] = (scan->ydpi == 600) ? 0xa4 : 0xac;
 | |
| 
 | |
|   opcgo[7]  = scan->color ? 0x2F : 0x40;
 | |
|   opcgo[12] = scan->color ? 0x10 : 0x0C;
 | |
|   opcgo[13] = scan->color ? 0x04 : 0xc3;
 | |
| 
 | |
|   opdgo[6]  = scan->color ? 0x88 : 0x8c;
 | |
|   opdgo[7]  = scan->color ? 0x00 : 0x40;
 | |
| 
 | |
|   DBG (3, "send_scan_parameters: xskip = %d, yskip = %d\n", scan->xskip,
 | |
|        scan->yskip);
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (cwritev (scan, CMD_2, 16, opcgo, &s));
 | |
|   CHK (cwritev (scan, CMD_8, 35, opbgo, &s));
 | |
|   CHK (cwritev (scan, CMD_1, 8, opdgo, &s));
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
|   DBG (4, "send_scan_parameters: checkpoint 1: s = %d\n", s);
 | |
| 
 | |
|   /* Loads the new calibration data (that was computed by get_caldata) into the
 | |
|      scanner */
 | |
| 
 | |
|   scan->caldata[16068] = subsamp[scan->xsamp];
 | |
|   scan->caldata[16069] = subsamp[scan->ysamp];
 | |
|   CHK (cwrite (scan, CMD_4, 16070, scan->caldata, &s));
 | |
| 
 | |
|   CHK (csend (scan, CMD_40));
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
| 
 | |
|   DBG (4, "send_scan_parameters: checkpoint 2: s = %d\n", s);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_raw_data (UMAX_Handle * scan, unsigned char *data, int len)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s;
 | |
| 
 | |
|   CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
|   CHK (cread (scan, CMD_4, len, data, &s));
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_raw_strip_color (UMAX_Handle * scan)
 | |
| {
 | |
|   /**
 | |
|      yres = 75   => ydpi = 150 => ysamp = 2 => yoff_scale = 8
 | |
|      yres = 150  => ydpi = 150 => ysamp = 1 => yoff_scale = 4
 | |
|      yres = 300  => ydpi = 300 => ysamp = 1 => yoff_scale = 2
 | |
|      yres = 600  => ydpi = 600 => ysamp = 1 => yoff_scale = 1
 | |
|   */
 | |
| 
 | |
|   const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
 | |
|   const int linelen = 3 * scan->w;
 | |
| 
 | |
|   /*
 | |
|      yoff_scale = 8  =>  roff = 5  * w,  goff = 1  * w,  boff = 0 * w, hextra = 1
 | |
|      yoff_scale = 4  =>  roff = 8  * w,  goff = 4  * w,  boff = 0 * w, hextra = 2
 | |
|      yoff_scale = 2  =>  roff = 14 * w,  goff = 7  * w,  boff = 0 * w, hextra = 4
 | |
|      yoff_scale = 1  =>  roff = 26 * w,  goff = 13 * w,  boff = 0 * w, hextra = 8
 | |
|    */
 | |
| 
 | |
|   const int hextra = 8 / yoff_scale;
 | |
| 
 | |
|   SANE_Status res;
 | |
|   int lines_to_read = scan->hexp;
 | |
| 
 | |
|   DBG (9, "read_raw_strip_color: hexp = %d, bh = %d\n", scan->hexp, scan->bh);
 | |
| 
 | |
|   if (scan->maxh == -1)
 | |
|     {
 | |
|       DBG (10, "read_raw_strip_color: filling buffer for the first time\n");
 | |
|       if (lines_to_read > scan->bh)
 | |
|         lines_to_read = scan->bh;
 | |
| 
 | |
|       CHK (read_raw_data (scan, scan->p, lines_to_read * linelen));
 | |
|       scan->maxh = lines_to_read - hextra;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       DBG (10, "read_raw_strip_color: reading new rows into buffer\n");
 | |
|       memmove (scan->p, scan->p + (scan->bh - hextra) * linelen,
 | |
|                hextra * linelen);
 | |
| 
 | |
|       if (lines_to_read > (scan->bh - hextra))
 | |
|         lines_to_read = scan->bh - hextra;
 | |
| 
 | |
|       CHK (read_raw_data
 | |
|            (scan, scan->p + hextra * linelen, lines_to_read * linelen));
 | |
|       scan->maxh = lines_to_read;
 | |
|     }
 | |
| 
 | |
|   scan->hexp -= lines_to_read;
 | |
|   scan->x = 0;
 | |
|   scan->y = 0;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_raw_strip_gray (UMAX_Handle * scan)
 | |
| {
 | |
|   const int linelen = scan->w;
 | |
| 
 | |
|   SANE_Status res;
 | |
| 
 | |
|   int lines_to_read = scan->bh;
 | |
| 
 | |
|   DBG (9, "read_raw_strip_gray: hexp = %d\n", scan->hexp);
 | |
| 
 | |
|   if (lines_to_read > scan->hexp)
 | |
|     lines_to_read = scan->hexp;
 | |
|   scan->hexp -= lines_to_read;
 | |
| 
 | |
|   CHK (read_raw_data (scan, scan->p, lines_to_read * linelen));
 | |
| 
 | |
|   scan->maxh = lines_to_read;
 | |
|   scan->x = 0;
 | |
|   scan->y = 0;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| read_raw_strip (UMAX_Handle * scan)
 | |
| {
 | |
|   if (scan->color)
 | |
|       return read_raw_strip_color (scan);
 | |
|   else
 | |
|       return read_raw_strip_gray (scan);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_set_scan_parameters (UMAX_Handle * scan,
 | |
|                           const int color,
 | |
|                           const int xo,
 | |
|                           const int yo,
 | |
|                           const int w,
 | |
|                           const int h, const int xres, const int yres)
 | |
| {
 | |
| 
 | |
|   /* Validate the input parameters */
 | |
| 
 | |
|   int left = xo;
 | |
|   int top = yo;
 | |
|   int right = xo + w * 600 / xres;
 | |
|   int bottom = yo + h * 600 / yres;
 | |
| 
 | |
|   DBG (2, "UMAX_set_scan_parameters:\n");
 | |
|   DBG (2, "color = %d             \n", color);
 | |
|   DBG (2, "xo    = %d, yo     = %d\n", xo, yo);
 | |
|   DBG (2, "w     = %d, h      = %d\n", w, h);
 | |
|   DBG (2, "xres  = %d, yres   = %d\n", xres, yres);
 | |
|   DBG (2, "left  = %d, top    = %d\n", left, top);
 | |
|   DBG (2, "right = %d, bottom = %d\n", right, bottom);
 | |
| 
 | |
|   if ((left < 0) || (right > UMAX_MAX_WIDTH))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if ((top < 0) || (bottom > UMAX_MAX_HEIGHT))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if (((right - left) < 10) || ((bottom - top) < 10))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if ((xres != 75) && (xres != 150) && (xres != 300) && (xres != 600))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if ((yres != 75) && (yres != 150) && (yres != 300) && (yres != 600))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   /* If we get this far, begin initializing the data
 | |
|      structure
 | |
|    */
 | |
| 
 | |
|   scan->color = color;
 | |
|   scan->w = w;
 | |
|   scan->h = h;
 | |
|   scan->xo = xo;
 | |
|   scan->yo = yo;
 | |
| 
 | |
|   /*
 | |
|      The scanner has a fixed X resolution of 600 dpi, but
 | |
|      supports three choices for the Y resolution. We must
 | |
|      choose an appropriate physical resolution and the
 | |
|      corresponding sampling value.
 | |
| 
 | |
|      It is not clear to me why the choice depends on
 | |
|      whether we are scanning in color or not, but the
 | |
|      original code did this and I didn't want to mess
 | |
|      with it.
 | |
| 
 | |
|      Physical X resolution choice:
 | |
|      xres = 75   =>  xdpi = 600  (xsamp = 8)
 | |
|      xres = 150  =>  xdpi = 600  (xsamp = 4)
 | |
|      xres = 300  =>  xdpi = 600  (xsamp = 2)
 | |
|      xres = 600  =>  xdpi = 600  (xsamp = 1)
 | |
| 
 | |
|      Physical Y resolution choice (if color):
 | |
|      yres = 75    =>  ydpi = 150  (ysamp = 2)
 | |
|      yres = 150   =>  ydpi = 150  (ysamp = 1)
 | |
|      yres = 300   =>  ydpi = 300  (ysamp = 1)
 | |
|      yres = 600   =>  ydpi = 600  (ysamp = 1)
 | |
| 
 | |
|      Physical Y resolution choice (if not color):
 | |
|      yres = 75    =>  ydpi = 300  (ysamp = 4)
 | |
|      yres = 150   =>  ydpi = 300  (ysamp = 2)
 | |
|      yres = 300   =>  ydpi = 300  (ysamp = 1)
 | |
|      yres = 600   =>  ydpi = 600  (ysamp = 1)
 | |
|    */
 | |
| 
 | |
|   scan->xdpi = 600;
 | |
|   if (yres <= 150 && color)
 | |
|     scan->ydpi = 150;
 | |
|   else if (yres > 300)
 | |
|     scan->ydpi = 600;
 | |
|   else
 | |
|     scan->ydpi = 300;
 | |
| 
 | |
|   scan->xsamp = scan->xdpi / xres;
 | |
|   scan->ysamp = scan->ydpi / yres;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_start_scan (UMAX_Handle * scan)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   int linelen;
 | |
|   int yd;
 | |
| 
 | |
|   DBG (3, "UMAX_start_scan called\n");
 | |
| 
 | |
|   if (scan->color)
 | |
|     {
 | |
|       const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
 | |
|       const int hextra = 8 / yoff_scale;
 | |
| 
 | |
|       linelen = 3 * scan->w;
 | |
|       scan->hexp = scan->h + hextra;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       linelen = scan->w;
 | |
|       scan->hexp = scan->h;
 | |
|     }
 | |
| 
 | |
|   scan->bh = BUFFER_SIZE / linelen;
 | |
| 
 | |
|   scan->p = malloc (scan->bh * linelen);
 | |
|   if (scan->p == 0)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   DBG (4, "UMAX_start_scan: bh = %d, linelen = %d\n", scan->bh, linelen);
 | |
| 
 | |
|   scan->maxh = -1;
 | |
|   scan->done = 0;
 | |
| 
 | |
|   /* Initialize the scanner and position the scan head */
 | |
| 
 | |
|   CHK (umaxinit (scan));
 | |
| 
 | |
|   /* This scans in the black and white calibration strip that
 | |
|    * is located under the scanner's lid. The scan of that strip
 | |
|    * is used to pick correct values for the CCD calibration
 | |
|    * values
 | |
|    */
 | |
| 
 | |
|   scan->scanner_ypos = 0;
 | |
|   CHK (move (scan, 196, UMAX_NOT_FINE));
 | |
|   CHK (find_zero (scan));
 | |
|   CHK (move (scan, scan->scanner_yorg - 232 - scan->scanner_ypos, UMAX_FINE));
 | |
|   CHK (get_caldata (scan, scan->color));
 | |
| 
 | |
|   /* This moves the head back to the starting position */
 | |
| 
 | |
|   yd = scan->scanner_yorg + scan->yo - scan->scanner_ypos;
 | |
|   if (yd < 0)
 | |
|     CHK (move (scan, yd, UMAX_FINE));
 | |
|   if (yd > 300)
 | |
|     CHK (move (scan, (yd - 20) / 2, UMAX_NOT_FINE));
 | |
|   yd = scan->scanner_yorg + scan->yo - scan->scanner_ypos;
 | |
| 
 | |
|   scan->yskip = yd / (600 / scan->ydpi);
 | |
|   scan->xskip = scan->xo / (600 / scan->xdpi);
 | |
| 
 | |
|   /* Read in the first chunk of raw data */
 | |
| 
 | |
|   CHK (send_scan_parameters (scan));
 | |
|   CHK (read_raw_strip (scan));
 | |
| 
 | |
|   DBG (4, "UMAX_start_scan successful\n");
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_set_lamp_state (UMAX_Handle * scan, UMAX_Lamp_State state)
 | |
| {
 | |
|   SANE_Status res;
 | |
| 
 | |
|   DBG (3, "UMAX_set_lamp_state: state = %d\n", (int) state);
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   CHK (cwritev_opc1_lamp_ctrl (scan, state));
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_park_head (UMAX_Handle * scan)
 | |
| {
 | |
|   SANE_Status res;
 | |
|   UMAX_Status_Byte s;
 | |
|   int i;
 | |
| 
 | |
|   DBG (3, "UMAX_park_head called\n");
 | |
| 
 | |
|   CHK (csend (scan, CMD_0));
 | |
|   /* WARNING: Must call cwritev_opc1_lamp_ctrl before cwritev_opb3_restore,
 | |
|    * otherwise the head moves the wrong way and makes ugly grinding noises. */
 | |
|   CHK (cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON));
 | |
|   CHK (cwritev_opb3_restore (scan));
 | |
| 
 | |
|   for (i = 0; i < 60; ++i)
 | |
|     {
 | |
|       CHK (cread (scan, CMD_2, 0, NULL, &s));
 | |
|       DBG (4, "UMAX_park_head: s = %#x\n", s);
 | |
|       if ((s & 0x40) != 0)
 | |
|         break;
 | |
|       DBG (4, "UMAX_park_head: sleeping\n");
 | |
|       usleep (500000);
 | |
|     }
 | |
| 
 | |
|   scan->scanner_ypos = 0;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_finish_scan (UMAX_Handle * scan)
 | |
| {
 | |
|   DBG (3, "UMAX_finish_scan:\n");
 | |
| 
 | |
|   if (scan->p)
 | |
|     free (scan->p);
 | |
|   scan->p = NULL;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_get_rgb (UMAX_Handle * scan, unsigned char *rgb)
 | |
| {
 | |
| 
 | |
|   if (scan->color)
 | |
|     {
 | |
|       const int linelen = 3 * scan->w;
 | |
|       const int yoff_scale = 600 * scan->ysamp / scan->ydpi;
 | |
|       const int roff = (8 / yoff_scale * 3 + 2) * scan->w;
 | |
|       const int goff = (4 / yoff_scale * 3 + 1) * scan->w;
 | |
|       const int boff = 0;
 | |
| 
 | |
|       unsigned char *base = scan->p + (scan->y * linelen) + scan->x;
 | |
| 
 | |
| #ifdef DEBUG_BOUNDS
 | |
|       if ((base + roff) >= (scan->p + (scan->bh * linelen)))
 | |
|         DBG (1, "UMAX_get_rgb: Overrun in get_rgb (roff)!\n");
 | |
| 
 | |
|       if ((base + goff) >= (scan->p + (scan->bh * linelen)))
 | |
|         DBG (1, "UMAX_get_rgb: Overrun in get_rgb (goff)!\n");
 | |
| 
 | |
|       if ((base + boff) >= (scan->p + (scan->bh * linelen)))
 | |
|         DBG (1, "UMAX_get_rgb: Overrun in get_rgb (boff)!\n");
 | |
| #endif
 | |
| 
 | |
|       rgb[0] = base[roff];
 | |
|       rgb[1] = base[goff];
 | |
|       rgb[2] = base[boff];
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       const int linelen = scan->w;
 | |
|       unsigned char *base = scan->p + (scan->y * linelen) + scan->x;
 | |
| 
 | |
| #ifdef DEBUG_BOUNDS
 | |
|       if (base >= (scan->p + (scan->bh * linelen)))
 | |
|         DBG (1, "UMAX_get_rgb: Overrun in get_rgb (gray)!\n");
 | |
| #endif
 | |
| 
 | |
|       rgb[0] = base[0];
 | |
|       rgb[1] = base[0];
 | |
|       rgb[2] = base[0];
 | |
|     }
 | |
| 
 | |
|   if (!(((scan->x + 1) == scan->w) && ((scan->y + 1) == scan->maxh)))
 | |
|     {
 | |
|       ++scan->x;
 | |
|       if (scan->x == scan->w)
 | |
|         {
 | |
|           ++scan->y;
 | |
|           scan->x = 0;
 | |
|         }
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   if (scan->hexp <= 0)
 | |
|     {
 | |
|       DBG (4, "UMAX_get_rgb: setting done flag\n");
 | |
|       scan->done = 1;
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   return read_raw_strip (scan);
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_close_device (UMAX_Handle * scan)
 | |
| {
 | |
|   DBG (3, "UMAX_close_device:\n");
 | |
|   sanei_usb_close (scan->fd);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| UMAX_open_device (UMAX_Handle * scan, const char *dev)
 | |
| {
 | |
|   SANE_Word vendor;
 | |
|   SANE_Word product;
 | |
|   SANE_Status res;
 | |
| 
 | |
|   DBG (3, "UMAX_open_device: `%s'\n", dev);
 | |
| 
 | |
|   res = sanei_usb_open (dev, &scan->fd);
 | |
|   if (res != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (1, "UMAX_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, "UMAX_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 UMAX scanner */
 | |
|   if (vendor != 0x1606)
 | |
|     {
 | |
|       DBG (1, "UMAX_open_device: incorrect vendor\n");
 | |
|       sanei_usb_close (scan->fd);
 | |
|       scan->fd = -1;
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|   /* Now check whether it is a scanner we know about */
 | |
|   switch (product)
 | |
|     {
 | |
|     case ASTRA_2000U:
 | |
|       /* The UMAX Astra 2000U is only partially supported by
 | |
|          this driver. Expect severe color problems! :)
 | |
|        */
 | |
|       DBG (1,
 | |
|            "UMAX_open_device: Scanner is a 2000U. Expect color problems :)\n");
 | |
|       scan->model = ASTRA_2000U;
 | |
|       break;
 | |
|      case ASTRA_2100U:
 | |
|       /* The UMAX Astra 2100U is only partially supported by
 | |
|          this driver. Expect severe color problems! :) [???]
 | |
|        */
 | |
|       DBG (1,
 | |
|            "UMAX_open_device: Scanner is a 2100U. Expect color problems :)\n");
 | |
|       scan->model = ASTRA_2100U;
 | |
|       break;
 | |
|     case ASTRA_1220U:
 | |
|       scan->model = ASTRA_1220U;
 | |
|       break;
 | |
|     default:
 | |
|       DBG (1, "UMAX_open_device: unknown product number\n");
 | |
|       sanei_usb_close (scan->fd);
 | |
|       scan->fd = -1;
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   res = csend (scan, CMD_0);
 | |
|   if (res != SANE_STATUS_GOOD)
 | |
|     UMAX_close_device (scan);
 | |
|   CHK (res);
 | |
| 
 | |
|   res = xxxops (scan);
 | |
|   if (res != SANE_STATUS_GOOD)
 | |
|     UMAX_close_device (scan);
 | |
|   CHK (res);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static const char *
 | |
| UMAX_get_device_name (UMAX_Handle * scan)
 | |
| {
 | |
|   switch (scan->model)
 | |
|     {
 | |
|     case ASTRA_1220U:
 | |
|       return "Astra 1220U";
 | |
|     case ASTRA_2000U:
 | |
|       return "Astra 2000U";
 | |
|     case ASTRA_2100U:
 | |
|       return "Astra 2100U";
 | |
|     }
 | |
|   return "Unknown";
 | |
| }
 |