diff --git a/backend/Makefile.in b/backend/Makefile.in index 0d05a1cb1..8d13c37d6 100644 --- a/backend/Makefile.in +++ b/backend/Makefile.in @@ -277,7 +277,6 @@ libsane-umax.la: ../sanei/sanei_scsi.lo libsane-v4l.la: ../sanei/sanei_constrain_value.lo libsane-umax_pp.la: $(addsuffix .lo,$(EXTRA_umax_pp)) libsane-umax_pp.la: ../sanei/sanei_constrain_value.lo -libsane-umax1220u.la: ../sanei/sanei_config2.lo libsane-umax1220u.la: ../sanei/sanei_constrain_value.lo libsane-umax1220u.la: ../sanei/sanei_usb.lo libsane-umax1220u.la: ../sanei/sanei_pv8630.lo diff --git a/backend/dll.conf b/backend/dll.conf index 15fda7305..821e4d0a2 100644 --- a/backend/dll.conf +++ b/backend/dll.conf @@ -37,4 +37,5 @@ sp15c tamarack umax #umax_pp +umax1220u v4l diff --git a/backend/umax1220u-common.c b/backend/umax1220u-common.c new file mode 100644 index 000000000..0bba2159d --- /dev/null +++ b/backend/umax1220u-common.c @@ -0,0 +1,1697 @@ +/* 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 +#include +#include +#include +#include +#include + +/* + * 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 +} +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, int len, const unsigned char *data, + UMAX_Status_Byte * s) +{ + SANE_Status res; + UMAX_Status_Byte s0, s4; + + static unsigned char *escaped = NULL; + static int 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, int 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) + { + int 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 < 0) + { + DBG (1, "qread: error reading\n"); + return SANE_STATUS_IO_ERROR; + } + 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, int 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", s); + 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", s); + + 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_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"; + } + return "Unknown"; +} diff --git a/backend/umax1220u.c b/backend/umax1220u.c new file mode 100644 index 000000000..2cf94f080 --- /dev/null +++ b/backend/umax1220u.c @@ -0,0 +1,950 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2001, Marcio Luis Teixeira + + Parts copyright (C) 1996, 1997 Andreas Beck + Parts copyright (C) 2000, 2001 Michael Herder + Parts copyright (C) 2001 Henning Meier-Geinitz + + 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. */ + +#define BUILD 1 +#define MM_IN_INCH 25.4 + +#include "../include/sane/config.h" + +#include +#include +#include +#include +#include +#include + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_usb.h" +#include "../include/sane/sanei_pv8630.h" + +#define BACKEND_NAME umax1220u +#define UMAX_CONFIG_FILE "umax1220u.conf" + +#include "../include/sane/sanei_backend.h" + +#include "umax1220u-common.c" + +typedef struct Umax_Device +{ + struct Umax_Device *next; + SANE_String name; + SANE_Device sane; +} +Umax_Device; + +typedef struct Umax_Scanner +{ + struct Umax_Scanner *next; + Umax_Device *device; + UMAX_Handle scan; +} +Umax_Scanner; + +static int num_devices = 0; +static const SANE_Device **devlist = NULL; +static Umax_Device *first_dev = NULL; +static Umax_Scanner *first_handle = NULL; + +static SANE_Parameters parms = { + SANE_FRAME_RGB, + 0, + 0, /* Number of bytes returned per scan line: */ + 0, /* Number of pixels per scan line. */ + 0, /* Number of lines for the current scan. */ + 8 /* Number of bits per sample. */ +}; + +struct _SANE_Option +{ + SANE_Option_Descriptor *descriptor; + SANE_Status (*callback) (struct _SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, + SANE_Int * info); +}; + +typedef struct _SANE_Option SANE_Option; + +static SANE_Word getNumberOfOptions (void); /* Forward declaration */ + +/* +This read-only option returns the number of options available for +the device. It should be the first option in the options array +declared below. +*/ + +static SANE_Option_Descriptor optionNumOptionsDescriptor = { + SANE_NAME_NUM_OPTIONS, + SANE_TITLE_NUM_OPTIONS, + SANE_DESC_NUM_OPTIONS, + SANE_TYPE_INT, + SANE_UNIT_NONE, + sizeof (SANE_Word), + SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_NONE, + {NULL} +}; + +static SANE_Status +optionNumOptionsCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + option = option; + handle = handle; + info = info; /* Eliminate warning about unused parameters */ + + if (action != SANE_ACTION_GET_VALUE) + return SANE_STATUS_INVAL; + *(SANE_Word *) value = getNumberOfOptions (); + return SANE_STATUS_GOOD; +} + +/* +This option lets the user select the scan resolution. The UMAX +scanner only supports the following resolutions: 75, 150, 300 and +600 +*/ + +static const SANE_Word optionResolutionList[] = { + 4, /* Number of elements */ + 75, 150, 300, 600 /* Resolution list */ +}; + +static SANE_Option_Descriptor optionResolutionDescriptor = { + SANE_NAME_SCAN_RESOLUTION, + SANE_TITLE_SCAN_RESOLUTION, + SANE_DESC_SCAN_RESOLUTION, + SANE_TYPE_INT, + SANE_UNIT_DPI, + sizeof (SANE_Word), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC, + SANE_CONSTRAINT_WORD_LIST, + {(SANE_String_Const *) optionResolutionList} +}; + +static SANE_Word optionResolutionValue = 75; + +static SANE_Status +optionResolutionCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + SANE_Status status; + SANE_Word autoValue = 75; + + handle = handle; /* Eliminate warning about unused parameters */ + + switch (action) + { + case SANE_ACTION_SET_AUTO: + status = + sanei_constrain_value (option->descriptor, (void *) &autoValue, info); + if (status != SANE_STATUS_GOOD) + return status; + optionResolutionValue = autoValue; + *info |= SANE_INFO_RELOAD_PARAMS; + break; + case SANE_ACTION_SET_VALUE: + *info |= SANE_INFO_RELOAD_PARAMS; + optionResolutionValue = *(SANE_Word *) value; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Word *) value = optionResolutionValue; + break; + } + return SANE_STATUS_GOOD; +} + +/* +This option lets the user select a gray scale scan +*/ +static SANE_Word optionGrayscaleValue = SANE_FALSE; + +static SANE_Option_Descriptor optionGrayscaleDescriptor = { + SANE_I18N ("gray"), + SANE_I18N ("Grayscale scan"), + SANE_I18N ("Do a grayscale rather than color scan"), + SANE_TYPE_BOOL, + SANE_UNIT_NONE, + sizeof (SANE_Word), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_NONE, + {NULL} +}; + +static SANE_Status +optionGrayscaleCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + handle = handle; + option = option; /* Eliminate warning about unused parameters */ + + switch (action) + { + case SANE_ACTION_SET_AUTO: + return SANE_STATUS_INVAL; + break; + case SANE_ACTION_SET_VALUE: + *info |= SANE_INFO_RELOAD_PARAMS; + optionGrayscaleValue = *(SANE_Bool *) value; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Word *) value = optionGrayscaleValue; + break; + } + return SANE_STATUS_GOOD; +} + +/* +This option is a button that allows the user to turn off the +lamp in the UMAX scanner +*/ + +static SANE_Option_Descriptor optionLampOffDescriptor = { + SANE_I18N ("lamp-off"), + SANE_I18N ("Lamp off"), + SANE_I18N ("Turn off scanner lamp"), + SANE_TYPE_BUTTON, + SANE_UNIT_NONE, + 0, + SANE_CAP_SOFT_SELECT, + SANE_CONSTRAINT_NONE, + {NULL} +}; + +static SANE_Status +optionLampOffCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + Umax_Scanner *scanner = handle; + SANE_Status res = SANE_STATUS_GOOD; + + /* Eliminate warnings about unused parameters */ + option = option; + handle = handle; + info = info; + value = value; + + if (action != SANE_ACTION_SET_VALUE) + return SANE_STATUS_INVAL; + + res = UMAX_set_lamp_state (&scanner->scan, UMAX_LAMP_OFF); + + return res; +} + +static const SANE_Range widthRange = { + 0, /* minimum */ + SANE_FIX (UMAX_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */ + 0 /* quantization */ +}; + +static const SANE_Range heightRange = { + 0, /* minimum */ + SANE_FIX (UMAX_MAX_HEIGHT * MM_IN_INCH / 600), /* maximum */ + 0 /* quantization */ +}; + +/* +This option controls the top-left-x corner of the scan +*/ + +static SANE_Fixed optionTopLeftXValue = 0; + +static SANE_Option_Descriptor optionTopLeftXDescriptor = { + SANE_NAME_SCAN_TL_X, + SANE_TITLE_SCAN_TL_X, + SANE_DESC_SCAN_TL_X, + SANE_TYPE_FIXED, + SANE_UNIT_MM, + sizeof (SANE_Fixed), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_RANGE, + {(SANE_String_Const *) & widthRange} +}; + +static SANE_Status +optionTopLeftXCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + option = option; + handle = handle; + value = value; /* Eliminate warning about unused parameters */ + + switch (action) + { + case SANE_ACTION_SET_AUTO: + return SANE_STATUS_INVAL; + break; + case SANE_ACTION_SET_VALUE: + optionTopLeftXValue = *(SANE_Fixed *) value; + *info |= SANE_INFO_RELOAD_PARAMS; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Fixed *) value = optionTopLeftXValue; + break; + } + return SANE_STATUS_GOOD; +} + +/* +This option controls the top-left-y corner of the scan +*/ + +static SANE_Fixed optionTopLeftYValue = 0; + +static SANE_Option_Descriptor optionTopLeftYDescriptor = { + SANE_NAME_SCAN_TL_Y, + SANE_TITLE_SCAN_TL_Y, + SANE_DESC_SCAN_TL_Y, + SANE_TYPE_FIXED, + SANE_UNIT_MM, + sizeof (SANE_Fixed), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_RANGE, + {(SANE_String_Const *) & heightRange} +}; + +static SANE_Status +optionTopLeftYCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + /* Eliminate warnings about unused parameters */ + option = option; + handle = handle; + + switch (action) + { + case SANE_ACTION_SET_AUTO: + return SANE_STATUS_INVAL; + break; + case SANE_ACTION_SET_VALUE: + optionTopLeftYValue = *(SANE_Fixed *) value; + *info |= SANE_INFO_RELOAD_PARAMS; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Fixed *) value = optionTopLeftYValue; + break; + } + return SANE_STATUS_GOOD; +} + +/* +This option controls the bot-right-x corner of the scan +*/ + +static SANE_Fixed optionBotRightXValue = 0; + +static SANE_Option_Descriptor optionBotRightXDescriptor = { + SANE_NAME_SCAN_BR_X, + SANE_TITLE_SCAN_BR_X, + SANE_DESC_SCAN_BR_X, + SANE_TYPE_FIXED, + SANE_UNIT_MM, + sizeof (SANE_Fixed), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_RANGE, + {(SANE_String_Const *) & widthRange} +}; + +static SANE_Status +optionBotRightXCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + /* Eliminate warnings about unused parameters */ + option = option; + handle = handle; + + switch (action) + { + case SANE_ACTION_SET_AUTO: + return SANE_STATUS_INVAL; + break; + case SANE_ACTION_SET_VALUE: + optionBotRightXValue = *(SANE_Fixed *) value; + *info |= SANE_INFO_RELOAD_PARAMS; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Fixed *) value = optionBotRightXValue; + break; + } + return SANE_STATUS_GOOD; +} + +/* +This option controls the bot-right-y corner of the scan +*/ + +static SANE_Fixed optionBotRightYValue = 0; + +static SANE_Option_Descriptor optionBotRightYDescriptor = { + SANE_NAME_SCAN_BR_Y, + SANE_TITLE_SCAN_BR_Y, + SANE_DESC_SCAN_BR_Y, + SANE_TYPE_FIXED, + SANE_UNIT_MM, + sizeof (SANE_Fixed), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_RANGE, + {(SANE_String_Const *) & heightRange} +}; + +static SANE_Status +optionBotRightYCallback (SANE_Option * option, SANE_Handle handle, + SANE_Action action, void *value, SANE_Int * info) +{ + /* Eliminate warnings about unused parameters */ + option = option; + handle = handle; + + switch (action) + { + case SANE_ACTION_SET_AUTO: + return SANE_STATUS_INVAL; + break; + case SANE_ACTION_SET_VALUE: + optionBotRightYValue = *(SANE_Fixed *) value; + *info |= SANE_INFO_RELOAD_PARAMS; + break; + case SANE_ACTION_GET_VALUE: + *(SANE_Fixed *) value = optionBotRightYValue; + break; + } + return SANE_STATUS_GOOD; +} + +/* +The following array binds the option descriptors to +their respective callback routines +*/ + +static SANE_Option so[] = { + {&optionNumOptionsDescriptor, optionNumOptionsCallback}, + {&optionResolutionDescriptor, optionResolutionCallback}, + {&optionGrayscaleDescriptor, optionGrayscaleCallback}, + {&optionTopLeftXDescriptor, optionTopLeftXCallback}, + {&optionTopLeftYDescriptor, optionTopLeftYCallback}, + {&optionBotRightXDescriptor, optionBotRightXCallback}, + {&optionBotRightYDescriptor, optionBotRightYCallback}, + {&optionLampOffDescriptor, optionLampOffCallback} +}; + +static SANE_Word +getNumberOfOptions (void) +{ + return NELEMS (so); +} + +/* +This routine dispatches the control message to the appropriate +callback routine, it outght to be called by sane_control_option +after any driver specific validation. +*/ +static SANE_Status +dispatch_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *value, SANE_Int * info) +{ + SANE_Option *op = so + option; + SANE_Int myinfo = 0; + SANE_Status status = SANE_STATUS_GOOD; + + if (option < 0 || option >= NELEMS (so)) + return SANE_STATUS_INVAL; /* Unknown option ... */ + + if ((action == SANE_ACTION_SET_VALUE) && + ((op->descriptor->cap & SANE_CAP_SOFT_SELECT) == 0)) + return SANE_STATUS_INVAL; + + if ((action == SANE_ACTION_GET_VALUE) && + ((op->descriptor->cap & SANE_CAP_SOFT_DETECT) == 0)) + return SANE_STATUS_INVAL; + + if ((action == SANE_ACTION_SET_AUTO) && + ((op->descriptor->cap & SANE_CAP_AUTOMATIC) == 0)) + return SANE_STATUS_INVAL; + + if (action == SANE_ACTION_SET_VALUE) + { + status = sanei_constrain_value (op->descriptor, value, &myinfo); + if (status != SANE_STATUS_GOOD) + return status; + } + + status = (op->callback) (op, handle, action, value, &myinfo); + + if (info) + *info = myinfo; + + return status; +} + +static SANE_Status +attach_scanner (const char *devicename, Umax_Device ** devp) +{ + UMAX_Handle scan; + Umax_Device *dev; + SANE_Status status; + + DBG (3, "attach_scanner: %s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + if (devp) + *devp = dev; + return SANE_STATUS_GOOD; + } + } + + dev = malloc (sizeof (*dev)); + if (!dev) + return SANE_STATUS_NO_MEM; + memset (dev, '\0', sizeof (Umax_Device)); /* clear structure */ + + DBG (4, "attach_scanner: opening %s\n", devicename); + + status = UMAX_open_device (&scan, devicename); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename); + free (dev); + return status; + } + dev->name = strdup (devicename); + dev->sane.name = dev->name; + dev->sane.vendor = "UMAX"; + dev->sane.model = UMAX_get_device_name (&scan); + dev->sane.type = "flatbed scanner"; + UMAX_close_device (&scan); + + ++num_devices; + dev->next = first_dev; + first_dev = dev; + + if (devp) + *devp = dev; + return SANE_STATUS_GOOD; +} + +/* callback function for sanei_usb_attach_matching_devices +*/ +static SANE_Status +attach_one (const char *name) +{ + attach_scanner (name, 0); + return SANE_STATUS_GOOD; +} + +/* This file implements a SANE backend for the UMAX Astra 1220U scanner. + */ +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + char config_line[PATH_MAX]; + size_t len; + FILE *fp; + + DBG_INIT (); + + DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", + version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); + DBG (1, "sane_init: SANE umax1220u backend version %d.%d.%d from %s\n", + V_MAJOR, V_MINOR, BUILD, PACKAGE_VERSION); + + if (version_code) + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); + + sanei_usb_init (); + + fp = sanei_config_open (UMAX_CONFIG_FILE); + if (!fp) + { + /* no config-file: try /dev/scanner and /dev/usbscanner. */ + attach_scanner ("/dev/scanner", 0); + attach_scanner ("/dev/usbscanner", 0); + return SANE_STATUS_GOOD; + } + + DBG (3, "reading configure file %s\n", UMAX_CONFIG_FILE); + + while (sanei_config_read (config_line, sizeof (config_line), fp)) + { + if (config_line[0] == '#') + continue; /* ignore line comments */ + + len = strlen (config_line); + + if (!len) + continue; /* ignore empty lines */ + + DBG (4, "attach_matching_devices(%s)\n", config_line); + sanei_usb_attach_matching_devices (config_line, attach_one); + } + + DBG (4, "finished reading configure file\n"); + + fclose (fp); + + return SANE_STATUS_GOOD; +} + +void +sane_exit (void) +{ + Umax_Device *dev, *next; + + DBG (3, "sane_exit\n"); + + for (dev = first_dev; dev; dev = next) + { + next = dev->next; + free (dev->name); + free (dev); + } + + if (devlist) + free (devlist); + return; +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + Umax_Device *dev; + int i; + + DBG (3, "sane_get_devices(local_only = %d)\n", local_only); + + if (devlist) + free (devlist); + + devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); + if (!devlist) + return SANE_STATUS_NO_MEM; + + i = 0; + + for (dev = first_dev; i < num_devices; dev = dev->next) + devlist[i++] = &dev->sane; + + devlist[i++] = 0; + + *device_list = devlist; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Umax_Device *dev; + SANE_Status status; + Umax_Scanner *scanner; + + DBG (3, "sane_open\n"); + + if (devicename[0]) /* search for devicename */ + { + DBG (4, "sane_open: devicename=%s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + if (strcmp (dev->sane.name, devicename) == 0) + break; + + if (!dev) + { + status = attach_scanner (devicename, &dev); + if (status != SANE_STATUS_GOOD) + return status; + } + } + else + { + DBG (2, "sane_open: no devicename, opening first device\n"); + dev = first_dev; + } + + if (!dev) + return SANE_STATUS_INVAL; + + scanner = malloc (sizeof (*scanner)); + if (!scanner) + return SANE_STATUS_NO_MEM; + + memset (scanner, 0, sizeof (*scanner)); + scanner->device = dev; + + status = UMAX_open_device (&scanner->scan, dev->sane.name); + if (status != SANE_STATUS_GOOD) + { + free (scanner); + return status; + } + + *handle = scanner; + + /* insert newly opened handle into list of open handles: */ + scanner->next = first_handle; + + first_handle = scanner; + + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + Umax_Scanner *prev, *scanner; + SANE_Status res; + + DBG (3, "sane_close\n"); + + if (!first_handle) + { + DBG (1, "ERROR: sane_close: no handles opened\n"); + return; + } + + /* remove handle from list of open handles: */ + + prev = NULL; + + for (scanner = first_handle; scanner; scanner = scanner->next) + { + if (scanner == handle) + break; + + prev = scanner; + } + + if (!scanner) + { + DBG (1, "ERROR: sane_close: invalid handle %p\n", handle); + return; /* oops, not a handle we know about */ + } + + if (prev) + prev->next = scanner->next; + else + first_handle = scanner->next; + + res = UMAX_set_lamp_state (&scanner->scan, UMAX_LAMP_OFF); + res = UMAX_close_device (&scanner->scan); + + free (scanner); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + handle = handle; /* Eliminate compiler warning */ + + DBG (3, "sane_get_option_descriptor: option = %d\n", option); + if (option < 0 || option >= NELEMS (so)) + return NULL; + return so[option].descriptor; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *value, SANE_Int * info) +{ + handle = handle; /* Eliminate compiler warning */ + + DBG (3, + "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", + handle, option, action, value, info); + + return dispatch_control_option (handle, option, action, value, info); +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + int rc = SANE_STATUS_GOOD; + int w = + SANE_UNFIX (optionBotRightXValue - + optionTopLeftXValue) / MM_IN_INCH * optionResolutionValue; + int h = + SANE_UNFIX (optionBotRightYValue - + optionTopLeftYValue) / MM_IN_INCH * optionResolutionValue; + + handle = handle; /* Eliminate compiler warning */ + + DBG (3, "sane_get_parameters\n"); + parms.depth = 8; + parms.last_frame = SANE_TRUE; + parms.pixels_per_line = w; + parms.lines = h; + + if (optionGrayscaleValue == SANE_TRUE) + { + parms.format = SANE_FRAME_GRAY; + parms.bytes_per_line = w; + } + else + { + parms.format = SANE_FRAME_RGB; + parms.bytes_per_line = w * 3; + } + *params = parms; + return rc; +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Umax_Scanner *scanner = handle; + SANE_Status res; + + DBG (3, "sane_start\n"); + + res = UMAX_set_scan_parameters (&scanner->scan, + optionGrayscaleValue == SANE_FALSE, + SANE_UNFIX (optionTopLeftXValue) / + MM_IN_INCH * 600, + SANE_UNFIX (optionTopLeftYValue) / + MM_IN_INCH * 600, + SANE_UNFIX (optionBotRightXValue - + optionTopLeftXValue) / + MM_IN_INCH * optionResolutionValue, + SANE_UNFIX (optionBotRightYValue - + optionTopLeftYValue) / + MM_IN_INCH * optionResolutionValue, + optionResolutionValue, + optionResolutionValue); + + if (res != SANE_STATUS_GOOD) + return res; + + return UMAX_start_scan (&scanner->scan); +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + Umax_Scanner *scanner = handle; + SANE_Status res; + int len; + unsigned char rgb[3]; + + len = *length = 0; + + DBG (3, "sane_read: max_length = %d\n", max_length); + + if (!data || !length) + return SANE_STATUS_INVAL; + + if (scanner->scan.done) + { + res = UMAX_finish_scan (&scanner->scan); + res = UMAX_park_head (&scanner->scan); + + return SANE_STATUS_EOF; + } + + if (optionGrayscaleValue == SANE_FALSE) + { + while (!scanner->scan.done && (max_length >= 3)) + { + res = UMAX_get_rgb (&scanner->scan, rgb); + if (res != SANE_STATUS_GOOD) + { + *length = 0; + return res; + } + *data++ = rgb[0]; + *data++ = rgb[1]; + *data++ = rgb[2]; + max_length -= 3; + len += 3; + } + } + else + { + while (!scanner->scan.done && max_length) + { + res = UMAX_get_rgb (&scanner->scan, rgb); + if (res != SANE_STATUS_GOOD) + { + *length = 0; + return res; + } + *data++ = rgb[0]; + max_length--; + len++; + } + } + + *length = len; + return SANE_STATUS_GOOD; +} + +void +sane_cancel (SANE_Handle handle) +{ + DBG (3, "sane_cancel: handle = %p\n", handle); + DBG (3, "sane_cancel: canceling is unsupported in this backend\n"); +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, + non_blocking); + if (non_blocking != SANE_FALSE) + return SANE_STATUS_UNSUPPORTED; + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + DBG (3, "sane_get_select_fd: handle = %p, fd %s 0\n", handle, + fd ? "!=" : "="); + return SANE_STATUS_UNSUPPORTED; +} diff --git a/backend/umax1220u.conf b/backend/umax1220u.conf new file mode 100644 index 000000000..58114340b --- /dev/null +++ b/backend/umax1220u.conf @@ -0,0 +1,13 @@ +# Options for the umax1220u backend + +# Autodetect the UMAX Astra 1220U +usb 0x1606 0x0010 + +# The following line enables autodetection for the +# Astra 2000U. However, this driver isn't entirely +# compatible, so expect color problems :) +usb 0x1606 0x0030 + +# device list for non-linux-systems (enable if autodetect fails): +#/dev/scanner +#/dev/usb/scanner0 diff --git a/backend/umax1220u.desc b/backend/umax1220u.desc index d6153092a..951fc9f61 100644 --- a/backend/umax1220u.desc +++ b/backend/umax1220u.desc @@ -10,9 +10,9 @@ ; :backend "umax1220u" ; name of backend -:version "(sep15)" ; version of backend -:status :beta ; :alpha, :beta, :stable, :new -;:manpage "sane-umax1220u" ; name of manpage (if it exists) +:version "1.0.1" ; version of backend +:status :new ; :alpha, :beta, :stable, :new +:manpage "sane-umax1220u" ; name of manpage (if it exists) :url "http://umax1220u-sane.sourceforge.net" ; backend's web page :devicetype :scanner ; start of a list of devices.... @@ -21,11 +21,6 @@ :mfg "UMAX" ; name a manufacturer :url "http://www.umax.com/" -:url "http://support.umax.com/" -:url "http://www.umax.de/" -:url "http://www.umax.com.tw/" -:url "http://www.umaxweb.com.tw/" -:url "http://support.umax.co.uk/" :model "Astra 1220U" ; name models for above-specified mfg. :interface "USB" :comment "Rewrite of command-line driver by Paul Mackerras"