summaryrefslogtreecommitdiff
path: root/backend/umax1220u-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/umax1220u-common.c')
-rw-r--r--backend/umax1220u-common.c2386
1 files changed, 2386 insertions, 0 deletions
diff --git a/backend/umax1220u-common.c b/backend/umax1220u-common.c
new file mode 100644
index 0000000..43a4a6f
--- /dev/null
+++ b/backend/umax1220u-common.c
@@ -0,0 +1,2386 @@
+/* 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
+ Parts copyright (C) 2006 Patrick Lessard
+
+ 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>
+#include <math.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;
+
+
+#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
+
+#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
+
+
+/* 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;
+ }
+ return q - buf;
+}
+
+
+
+/* Write */
+
+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 = %lu\n", cmd, (u_long) 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;
+}
+
+/* Read */
+
+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 = %lu\n", cmd, (u_long) 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 %lu, only got %lu\n", (u_long) req, (u_long) 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;
+}
+
+
+/* Send command */
+
+static SANE_Status
+csend (UMAX_Handle * scan, UMAX_Cmd cmd)
+{
+ DBG (80, "csend: cmd = %d\n", cmd);
+
+ return usync (scan, cmd, 0);
+}
+
+/* Lamp control */
+
+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);
+}
+
+
+/* Restore Head 1220U */
+
+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;
+}
+
+
+/* Restore Head 2100U */
+
+static SANE_Status
+cwritev_opb3_restore_2100U (UMAX_Handle * scan)
+{
+ unsigned char opb3[36] = {
+ 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, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x0b, 0x1a, 0x00
+ };
+
+ SANE_Status res;
+
+ DBG (9, "cwritev_opb3_restore:\n");
+ CHK (cwritev (scan, CMD_8, 36, opb3, NULL));
+ CHK (csend (scan, CMD_40));
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Initialize and turn lamp on 1220U */
+
+/*
+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;
+}
+
+/* Initialize and turn lamp on 2100U */
+
+static SANE_Status
+umaxinit_2100U (UMAX_Handle * scan)
+{
+
+ unsigned char opx[36];
+ unsigned char opy[16];
+
+
+ SANE_Status res;
+ UMAX_Status_Byte s;
+
+ DBG (3, "umaxinit called\n");
+
+ CHK (xxxops (scan));
+ CHK (csend (scan, CMD_0));
+
+ /* Turn lamp on */
+
+ cwritev_opc1_lamp_ctrl (scan, UMAX_LAMP_ON);
+
+ CHK (cread (scan, CMD_8, 36, opx, &s));
+ CHK (cread (scan, CMD_2, 16, opy, &s));
+ CHK (csend (scan, CMD_0));
+ CHK (cread (scan, CMD_2, 0, NULL, &s));
+ CHK (csend (scan, CMD_0));
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Move head 1220U */
+
+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 ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+
+ 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;
+}
+
+
+
+/* Move head 2100U */
+
+static SANE_Status
+move_2100U (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[36] = {
+ 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, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x03, 0x1a, 0x00
+ };
+ unsigned char opb7[36] = {
+ 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, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x03, 0x1a, 0x00
+ };
+ unsigned char ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+ unsigned char ope2[3] = {
+ 0x00, 0xff, 0xff
+ };
+ unsigned char buf[512];
+
+
+ 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) & 0x0f);
+ opc4[9] = rev ? 0x01 : 0x05;
+
+ if (fine == UMAX_FINE)
+ {
+ opc4[8] = 0x2b;
+ opc4[14] = 0xa4;
+ }
+ else
+ {
+ opc4[8] = 0x15;
+ 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, 36, 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;
+}
+
+
+/* Get pixel image 1220U */
+
+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;
+}
+
+
+/* Get pixel image 2100U */
+
+static SANE_Status
+get_pixels_2100U (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);
+
+ CHK (cwrite (scan, CMD_2, 16, op2, &s));
+ CHK (cwrite (scan, CMD_8, 36, op8, &s));
+
+ if (zpos == 1)
+ CHK (cwritev (scan, CMD_1, 8, op1, &s))
+ else
+ 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;
+}
+
+
+/* This function locates the black stripe under scanner lid */
+
+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;
+}
+
+
+/* To find the lowest head position 1220U */
+
+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;
+}
+
+
+/* To find the lowest head position 2100U */
+
+static SANE_Status
+find_zero_2100U (UMAX_Handle * scan)
+{
+ unsigned char opc3[16] = {
+ 0xb4, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x2f, 0x2b, 0x05,
+ 0x00, 0x00, 0x00, 0x80, 0xa4, 0x00
+ };
+ unsigned char ope1[8] = {
+ 0x00, 0x00, 0x00, 0xaa, 0xcc, 0xee, 0x80, 0xff
+ };
+ unsigned char opb6[36] = {
+ 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, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x03, 0x1a, 0x00
+ };
+ unsigned char opd1[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x1b, 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_2100U (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;
+}
+
+
+/* Calibration 1220U */
+
+/*
+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 ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+
+ 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;
+}
+
+/* Calibration 2100U */
+
+/*
+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_2100U (UMAX_Handle * scan, int color)
+{
+ unsigned char opc9[16] = {
+ XXXX, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x15, 0x05,
+ XXXX, XXXX, XXXX, XXXX, 0xac, 0x00
+ };
+ unsigned char opb11[36] = {
+ 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x04,
+ 0x40, 0x01, 0x00, 0x00, 0x04, 0x00, 0x6e, XXXX, XXXX, 0x46,
+ 0x06, 0x00, 0x00, XXXX, XXXX, 0xa0, 0x00, 0x8b, 0x49, 0x2a,
+ 0xe9, 0x68, 0xdf, 0x83, XXXX, 0x00
+ };
+ unsigned char opd4[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x1b, 0x00, XXXX, XXXX
+ };
+ unsigned char ope[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
+ };
+
+
+/* default gamma translation table */
+ unsigned char ggamma[256] = {
+ 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x12, 0x14, 0x17, 0x19, 0x1B, 0x1D,
+ 0x1F, 0x21, 0x23, 0x24, 0x26, 0x28, 0x2A, 0x2B, 0x2D, 0x2E, 0x30,
+ 0x31, 0x33, 0x34, 0x36, 0x37, 0x39, 0x3A, 0x3B, 0x3D, 0x3E, 0x40,
+ 0x41, 0x42, 0x43, 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4F, 0x50, 0x51, 0x52, 0x53, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
+ 0x5B, 0x5C, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
+ 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86,
+ 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x90,
+ 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A,
+ 0x9B, 0x9C, 0x9D, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA2, 0xA3,
+ 0xA4, 0xA5, 0xA6, 0xA7, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAC,
+ 0xAD, 0xAE, 0xAF, 0xB0, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5,
+ 0xB6, 0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBC, 0xBD, 0xBE,
+ 0xBF, 0xBF, 0xC0, 0xC1, 0xC2, 0xC2, 0xC3, 0xC4, 0xC5, 0xC5, 0xC6,
+ 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCB, 0xCC, 0xCD, 0xCE, 0xCE,
+ 0xCF, 0xD0, 0xD1, 0xD1, 0xD2, 0xD3, 0xD4, 0xD4, 0xD5, 0xD6, 0xD6,
+ 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC, 0xDC, 0xDD, 0xDE, 0xDE,
+ 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE3, 0xE4, 0xE5, 0xE6, 0xE6,
+ 0xE7, 0xE8, 0xE8, 0xE9, 0xEA, 0xEA, 0xEB, 0xEC, 0xEC, 0xED, 0xEE,
+ 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3, 0xF4, 0xF5, 0xF5,
+ 0xF6, 0xF7, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD,
+ 0xFE, 0xFE, 0xFF
+ };
+
+
+ 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;
+ int t, gn;
+ double av, pct;
+
+ 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));
+ CHK (csend (scan, CMD_0));
+
+ opc9[0] = h + 4;
+
+ if (color)
+ {
+ opc9[10] = 0xb6;
+ opc9[11] = 0x3b;
+ opc9[12] = 0x0c;
+ opc9[13] = 0x03;
+ opb11[17] = 0x7e;
+ opb11[18] = 0xb0;
+ opb11[23] = 0xc4;
+ opb11[24] = 0x5c;
+ opb11[34] = 0x1b;
+ opd4[6] = 0x0f;
+ opd4[7] = 0x40;
+ }
+ else
+ {
+ opc9[10] = 0xa6;
+ opc9[11] = 0x2a;
+ opc9[12] = 0x08;
+ opc9[13] = 0xc2;
+ opb11[17] = 0x7f;
+ opb11[18] = 0xc0;
+ opb11[23] = 0xec;
+ opb11[24] = 0x54;
+ opb11[34] = 0x1a;
+ opd4[6] = 0x06;
+ opd4[7] = 0x20;
+ }
+
+ /* Do a test scan of the calibration strip (which is located
+ * under the scanner's lid */
+ CHK (get_pixels_2100U (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++)
+ {
+ t = 0;
+ for (y = 0; y < h; y++)
+ t += p[x + y * w];
+ av = (double) t / h;
+ pct = 100.0 - (av * 100.0) / 250;
+ gn = (int) (pct / 0.57);
+
+ pct = gn;
+ av = exp((-pct)/50)*2.5+0.9;
+ gn = gn * av;
+
+
+ if (gn < 0)
+ gn = 0;
+ else if (gn > 127)
+ gn = 127;
+ scan->caldata[x + x0] = gn;
+ }
+
+ /* Gamma table for blue */
+
+ for (i = 0; i < 256; i++)
+ scan->caldata[i + 3 * 5100 + 0] = ggamma[i];
+
+ /* Gamma table for green */
+
+ for (i = 0; i < 256; i++)
+ scan->caldata[i + 3 * 5100 + 256] = ggamma[i];
+
+ /* Gamma table for red */
+
+ for (i = 0; i < 256; i++)
+ scan->caldata[i + 3 * 5100 + 512] = ggamma[i];
+
+ free (p);
+
+ CHK_ARRAY (scan->caldata, 16070);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/* Sends scan user parameters from frontend 1220U */
+
+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 % 256;
+ 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;
+}
+
+/* Sends scan user parameters from frontend 2100U */
+
+static SANE_Status
+send_scan_parameters_2100U (UMAX_Handle * scan)
+{
+ SANE_Status res;
+ UMAX_Status_Byte s;
+ int bpl;
+
+ /* Appears to correspond to opscan in umax_pp_low.c */
+ unsigned char opbgo[36] = {
+ 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, 0x2a,
+ 0xe9, 0x68, 0xdf, XXXX, 0x1a, 0x00
+ };
+
+ /* Appears to correspond to opsc53 in umax_pp_low.c */
+ unsigned char opcgo[16] = {
+ XXXX, XXXX, XXXX, XXXX, 0xec, 0x03, 0x60, XXXX, XXXX, XXXX,
+ XXXX, XXXX, XXXX, XXXX, XXXX, 0x00
+ };
+
+ /* Appears to correspond to opsc04 in umax_pp_low.c */
+ unsigned char opdgo[8] = {
+ 0x06, 0xf4, 0xff, 0x81, 0x1b, 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 % 256;
+ opbgo[18] = ((scan->xskip >> 8) & 0x0f) + (xend << 4);
+ opbgo[19] = xend >> 4;
+ opbgo[33] = 0x23 + ((xend & 0x1000) >> 5) + ((scan->xskip & 0x1000) >> 6);
+
+ /* bytes per line */
+
+ bpl = (scan->color ? 3 : 1) * scan->w * scan->xdpi;
+ opbgo[23] = bpl % 256;
+ opbgo[24] = 0x41 + ((bpl / 256) & 0x1f);
+
+ /* Scan height */
+
+ opcgo[0] = ytot;
+ opcgo[1] = ((ytot >> 8) & 0x3f) + (scan->yskip << 6);
+ opcgo[2] = (scan->yskip >> 2);
+ opcgo[3] = 0x50 + ((scan->yskip >> 10) & 0x0f);
+
+
+ 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[10] = scan->color ? 0xb6 : 0xa6;
+ opcgo[11] = scan->color ? 0x3b : 0x2a;
+ opcgo[12] = scan->color ? 0x0c : 0x08;
+ opcgo[13] = scan->color ? 0x03 : 0xc2;
+
+ opdgo[6] = scan->color ? 0x8f : 0x86;
+ opdgo[7] = scan->color ? 0x40 : 0x20;
+
+ 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, 36, 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;
+}
+
+/* Read raw data */
+
+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;
+}
+
+/* Read raw strip color */
+
+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;
+}
+
+/* Read raw strip grey */
+
+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;
+}
+
+
+/* Read raw strip */
+
+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);
+}
+
+/* Set scan user pamaters Frontend */
+
+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;
+}
+
+/* Start actual scan 1220U */
+
+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;
+}
+
+
+/* Start actual scan 2100U */
+
+static SANE_Status
+UMAX_start_scan_2100U (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_2100U (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_2100U (scan, 196, UMAX_NOT_FINE));
+ CHK (find_zero_2100U (scan));
+ CHK (move_2100U (scan, scan->scanner_yorg - 232 - scan->scanner_ypos, UMAX_FINE));
+ CHK (get_caldata_2100U (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_2100U (scan, yd, UMAX_FINE));
+ if (yd > 300)
+ CHK (move_2100U (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_2100U (scan));
+ CHK (read_raw_strip (scan));
+
+ DBG (4, "UMAX_start_scan successful\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Set lamp state */
+
+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;
+}
+
+/* Park head 1220U */
+
+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;
+}
+
+
+/* Park head 2100U */
+
+static SANE_Status
+UMAX_park_head_2100U (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_2100U (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);
+ }
+
+ /* CHK (csend (scan, CMD_0));
+ CHK (csend (scan, CMD_0)); */
+
+ scan->scanner_ypos = 0;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/* Finish scan */
+
+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;
+}
+
+
+/* RGB decoding for a color scan */
+
+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;
+
+ 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);
+
+ 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);
+}
+
+/* Close device */
+
+static SANE_Status
+UMAX_close_device (UMAX_Handle * scan)
+{
+ DBG (3, "UMAX_close_device:\n");
+ sanei_usb_close (scan->fd);
+ return SANE_STATUS_GOOD;
+}
+
+/* Open device */
+
+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:
+ 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;
+}
+
+/* Get scanner model name */
+
+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";
+}
+
+/* End */