diff options
Diffstat (limited to 'backend/lexmark_low.c')
-rw-r--r-- | backend/lexmark_low.c | 6106 |
1 files changed, 6106 insertions, 0 deletions
diff --git a/backend/lexmark_low.c b/backend/lexmark_low.c new file mode 100644 index 0000000..ae0db2c --- /dev/null +++ b/backend/lexmark_low.c @@ -0,0 +1,6106 @@ +/* lexmark-low.c: scanner-interface file for low Lexmark scanners. + + (C) 2005 Fred Odendaal + (C) 2006-2013 Stéphane Voltz <stef.dev@free.fr> + (C) 2010 "Torsten Houwaart" <ToHo@gmx.de> X74 support + + 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. + + **************************************************************************/ + +#undef BACKEND_NAME +#define BACKEND_NAME lexmark_low + +#include "lexmark.h" + +#include "lexmark_sensors.c" +#include "lexmark_models.c" + +/* numbre of ranges for offset */ +#define OFFSET_RANGES 5 + +typedef enum +{ + black = 0, + white +} +region_type; + +#define HomeTolerance 32 + + +#define LOBYTE(x) ((uint8_t)((x) & 0xFF)) +#define HIBYTE(x) ((uint8_t)((x) >> 8)) + +/* Static low function proto-types */ +static SANE_Status low_usb_bulk_write (SANE_Int devnum, + SANE_Byte * cmd, size_t * size); +static SANE_Status low_usb_bulk_read (SANE_Int devnum, + SANE_Byte * buf, size_t * size); +static SANE_Status low_write_all_regs (SANE_Int devnum, SANE_Byte * regs); +static SANE_Bool low_is_home_line (Lexmark_Device * dev, + unsigned char *buffer); +static SANE_Status low_get_start_loc (SANE_Int resolution, + SANE_Int * vert_start, + SANE_Int * hor_start, SANE_Int offset, + Lexmark_Device * dev); +static void low_rewind (Lexmark_Device * dev, SANE_Byte * regs); +static SANE_Status low_start_mvmt (SANE_Int devnum); +static SANE_Status low_stop_mvmt (SANE_Int devnum); +static SANE_Status low_clr_c6 (SANE_Int devnum); +static SANE_Status low_simple_scan (Lexmark_Device * dev, + SANE_Byte * regs, + int xoffset, + int pixels, + int yoffset, + int lines, SANE_Byte ** data); +static void low_set_scan_area (SANE_Int res, + SANE_Int tlx, + SANE_Int tly, + SANE_Int brx, + SANE_Int bry, + SANE_Int offset, + SANE_Bool half_step, + SANE_Byte * regs, Lexmark_Device * dev); + +/* Static Read Buffer Proto-types */ +static SANE_Status read_buffer_init (Lexmark_Device * dev, int bytesperline); +static SANE_Status read_buffer_free (Read_Buffer * rb); +static size_t read_buffer_bytes_available (Read_Buffer * rb); +static SANE_Status read_buffer_add_byte (Read_Buffer * rb, + SANE_Byte * byte_pointer); +static SANE_Status read_buffer_add_byte_gray (Read_Buffer * rb, + SANE_Byte * byte_pointer); +static SANE_Status read_buffer_add_bit_lineart (Read_Buffer * rb, + SANE_Byte * byte_pointer, + SANE_Byte threshold); +static size_t read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer, + size_t rqst_size); +static SANE_Bool read_buffer_is_empty (Read_Buffer * rb); + + +/* + * RTS88XX START + * + * these rts88xx functions will be spin off in a separate lib + * so that they can be reused. + */ + +/* + * registers helpers to avoid direct access + */ +static SANE_Bool +rts88xx_is_color (SANE_Byte * regs) +{ + if ((regs[0x2f] & 0x11) == 0x11) + return SANE_TRUE; + return SANE_FALSE; +} + +static void +rts88xx_set_gray_scan (SANE_Byte * regs) +{ + regs[0x2f] = (regs[0x2f] & 0x0f) | 0x20; +} + +#if 0 +static void +rts88xx_set_color_scan (SANE_Byte * regs) +{ + regs[0x2f] = (regs[0x2f] & 0x0f) | 0x10; +} +#endif + +static void +rts88xx_set_offset (SANE_Byte * regs, SANE_Byte red, SANE_Byte green, + SANE_Byte blue) +{ + /* offset for odd pixels */ + regs[0x02] = red; + regs[0x03] = green; + regs[0x04] = blue; + + /* offset for even pixels */ + regs[0x05] = red; + regs[0x06] = green; + regs[0x07] = blue; +} + +static void +rts88xx_set_gain (SANE_Byte * regs, SANE_Byte red, SANE_Byte green, + SANE_Byte blue) +{ + regs[0x08] = red; + regs[0x09] = green; + regs[0x0a] = blue; +} + +/* set # of head moves per CIS read */ +static int +rts88xx_set_scan_frequency (SANE_Byte * regs, int frequency) +{ + regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f); + return 0; +} + +/* + * read one register at given index + */ +static SANE_Status +rts88xx_read_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg) +{ + SANE_Status status = SANE_STATUS_GOOD; + unsigned char cmd[] = { 0x80, 0x00, 0x00, 0x01 }; + size_t size; + + cmd[1] = index; + + size = 4; +#ifdef FAKE_USB + status = SANE_STATUS_GOOD; +#else + status = sanei_usb_write_bulk (devnum, cmd, &size); +#endif + if (status != SANE_STATUS_GOOD) + { + DBG (5, "rts88xx_read_reg: bulk write failed\n"); + return status; + } + size = 1; +#ifdef FAKE_USB + status = SANE_STATUS_GOOD; +#else + status = sanei_usb_read_bulk (devnum, reg, &size); +#endif + if (status != SANE_STATUS_GOOD) + { + DBG (5, "rts88xx_read_reg: bulk read failed\n"); + return status; + } + DBG (15, "rts88xx_read_reg: reg[0x%02x]=0x%02x\n", index, *reg); + return status; +} + +/* + * write one register at given index + */ +static SANE_Status +rts88xx_write_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg) +{ + SANE_Status status = SANE_STATUS_GOOD; + unsigned char cmd[] = { 0x88, 0x00, 0x00, 0x01 }; + size_t size; + + cmd[1] = index; + + size = 4; +#ifdef FAKE_USB + status = SANE_STATUS_GOOD; +#else + status = sanei_usb_write_bulk (devnum, cmd, &size); +#endif + if (status != SANE_STATUS_GOOD) + { + DBG (5, "rts88xx_write_reg: bulk write failed\n"); + return status; + } + size = 1; +#ifdef FAKE_USB + status = SANE_STATUS_GOOD; +#else + status = sanei_usb_write_bulk (devnum, reg, &size); +#endif + if (status != SANE_STATUS_GOOD) + { + DBG (5, "rts88xx_write_reg: bulk write failed\n"); + return status; + } + DBG (15, "rts88xx_write_reg: reg[0x%02x]=0x%02x\n", index, *reg); + return status; +} + +/* + * write length consecutive registers, starting at index + * register 0xb3 is never wrote in bulk register write, so we split + * write if it belongs to the register set sent + */ +static SANE_Status +rts88xx_write_regs (SANE_Int devnum, SANE_Int start, SANE_Byte * source, + SANE_Int length) +{ + size_t size = 0; + + /* when writing several registers at a time, we avoid writing 0xb3 + register */ + if ((start + length > 0xb3) && (length > 1)) + { + size = 0xb3 - start; + if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD) + { + DBG (5, "rts88xx_write_regs : write registers part 1 failed ...\n"); + return SANE_STATUS_IO_ERROR; + } + + /* skip 0xB3 register */ + size++; + start = 0xb4; + source = source + size; + } + size = length - size; + if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD) + { + DBG (5, "rts88xx_write_regs : write registers part 2 failed ...\n"); + return SANE_STATUS_IO_ERROR; + } + + return SANE_STATUS_GOOD; + +} + +/* + * reads 'needed' bytes of scanned data into 'data'. Actual number of bytes get + * is retruned in 'size' + */ +static SANE_Status +rts88xx_read_data (SANE_Int devnum, size_t needed, SANE_Byte * data, + size_t * size) +{ + SANE_Byte read_cmd[] = { 0x91, 0x00, 0x00, 0x00 }; + size_t cmd_size; + SANE_Status status = SANE_STATUS_GOOD; + + /* this block would deserve to be a function */ + if (needed > MAX_XFER_SIZE) + *size = MAX_XFER_SIZE; + else + *size = needed; + read_cmd[3] = (*size) & 0xff; + read_cmd[2] = (*size >> 8) & 0xff; + read_cmd[1] = (*size >> 16) & 0xff; + + /* send header for 'get scanned data' */ + cmd_size = 4; + status = low_usb_bulk_write (devnum, read_cmd, &cmd_size); + if (status != SANE_STATUS_GOOD) + { + *size = 0; + DBG (5, "rts88xx_read_data : header sending failed ...\n"); + return status; + } + /* get actual scanned data */ + status = low_usb_bulk_read (devnum, data, size); + if (status != SANE_STATUS_GOOD) + { + *size = 0; + DBG (5, "rts88xx_read_data : data reading failed ...\n"); + } + return status; +} + +/* starts scan by sending color depth, stopping head, the starting it */ +static SANE_Status +rts88xx_commit (SANE_Int devnum, SANE_Byte depth) +{ + SANE_Status status; + SANE_Byte reg; + + DBG (2, "rts88xx_commit: start\n"); + + /* send color depth depth ?? + * X1100 -> 0x0f + * X1100/B2 -> 0x0d + * X1200 -> 0x01 */ + reg = depth; + rts88xx_write_reg (devnum, 0x2c, ®); + + /* stop before starting */ + low_stop_mvmt (devnum); + + /* effective start */ + status = low_start_mvmt (devnum); + + DBG (2, "rts88xx_commit: end\n"); + + return status; +} + +/* + * RTS88XX END + */ + + + +/* + * sets the scanner idle + */ +static SANE_Status +lexmark_low_set_idle (SANE_Int devnum) +{ + SANE_Byte regs[14] = + { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60 + }; + if (rts88xx_write_regs (devnum, 16, regs, 14) != SANE_STATUS_GOOD) + { + DBG (5, "lexmark_low_set_idle : register write failed ...\n"); + return SANE_STATUS_IO_ERROR; + } + return SANE_STATUS_GOOD; +} + + +/* wake up scanner */ +#if 0 +static SANE_Status +lexmark_low_wake_up (Lexmark_Device * dev) +{ + SANE_Byte regs[5] = { 0x12, 0x14, 0x16, 0x18, 0x1a }; + SANE_Byte values[5] = { 0x0f, 0x00, 0x07, 0x00, 0x00 }; + int i; + + /* send the wake-up sequence, one reg at at time */ + for (i = 0; i < 10; i++) + { + if (rts88xx_write_reg (dev->devnum, regs[i], values + i) != + SANE_STATUS_GOOD) + { + DBG (5, + "lexmark_low_wake_up : register write pass %d failed ...\n", + i); + return SANE_STATUS_IO_ERROR; + } + } + return SANE_STATUS_GOOD; +} +#endif + + +/** + * + */ +#ifdef DEEP_DEBUG +static void +write_pnm_file (char *title, int pixels, int lines, int color, + unsigned char *data) +{ + FILE *fdbg; + int x, y; + + fdbg = fopen (title, "wb"); + if (fdbg == NULL) + return; + + if (color) + { + fprintf (fdbg, "P6\n%d %d\n255\n", pixels, lines); + for (y = 0; y < lines; y++) + { + for (x = 0; x < pixels; x += 2) + { + fputc (data[y * pixels * 3 + x + 1], fdbg); + fputc (data[y * pixels * 3 + x + 1 + pixels], fdbg); + fputc (data[y * pixels * 3 + x + 1 + pixels * 2], fdbg); + fputc (data[y * pixels * 3 + x], fdbg); + fputc (data[y * pixels * 3 + x + pixels], fdbg); + fputc (data[y * pixels * 3 + x + pixels * 2], fdbg); + } + } + } + else + { + fprintf (fdbg, "P5\n%d %d\n255\n", pixels, lines); + fwrite (data, pixels, lines, fdbg); + } + fclose (fdbg); +} +#endif + +/* + * mid level hardware functions + */ +/* + * model init + */ +SANE_Status +sanei_lexmark_low_init (Lexmark_Device * dev) +{ + int i; + SANE_Status status; + + DBG_INIT (); + + status = SANE_STATUS_UNSUPPORTED; + DBG (2, "low_init: start\n"); + + /* clear all registers first */ + for (i = 0; i < 255; i++) + { + dev->shadow_regs[i] = 0; + } + + /* set up per model constant values */ + dev->shadow_regs[0xf3] = 0xf8; + dev->shadow_regs[0xf4] = 0x7f; + + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x00] = 0x04; + dev->shadow_regs[0x01] = 0x43; + dev->shadow_regs[0x0b] = 0x70; + dev->shadow_regs[0x12] = 0x0f; + dev->shadow_regs[0x16] = 0x07; + dev->shadow_regs[0x1d] = 0x20; + dev->shadow_regs[0x28] = 0xe0; + dev->shadow_regs[0x29] = 0xe3; + dev->shadow_regs[0x2a] = 0xeb; + dev->shadow_regs[0x2b] = 0x0d; + dev->shadow_regs[0x2e] = 0x40; + dev->shadow_regs[0x2e] = 0x86; + dev->shadow_regs[0x2f] = 0x01; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x31] = 0x06; + dev->shadow_regs[0x33] = 0x01; + dev->shadow_regs[0x34] = 0x50; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x50; + dev->shadow_regs[0x37] = 0x01; + dev->shadow_regs[0x38] = 0x50; + dev->shadow_regs[0x3a] = 0x20; + dev->shadow_regs[0x3c] = 0x88; + dev->shadow_regs[0x3d] = 0x08; + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x66] = 0x64; + dev->shadow_regs[0x6c] = 0xc8; + dev->shadow_regs[0x72] = 0x1a; + dev->shadow_regs[0x74] = 0x23; + dev->shadow_regs[0x75] = 0x03; + dev->shadow_regs[0x79] = 0x40; + dev->shadow_regs[0x7A] = 0x01; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + dev->shadow_regs[0x93] = 0x02; + dev->shadow_regs[0x94] = 0x0e; + dev->shadow_regs[0xa3] = 0xcc; + dev->shadow_regs[0xa4] = 0x27; + dev->shadow_regs[0xa5] = 0x24; + dev->shadow_regs[0xc2] = 0x80; + dev->shadow_regs[0xc3] = 0x01; + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x0a; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x0a; + dev->shadow_regs[0xe2] = 0x70; + dev->shadow_regs[0xe3] = 0x17; + dev->shadow_regs[0xf3] = 0xe0; + dev->shadow_regs[0xf4] = 0xff; + dev->shadow_regs[0xf5] = 0x01; + status = SANE_STATUS_GOOD; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x01] = 0x43; + dev->shadow_regs[0x0b] = 0x70; + dev->shadow_regs[0x11] = 0x01; + dev->shadow_regs[0x12] = 0x0f; + dev->shadow_regs[0x13] = 0x01; + dev->shadow_regs[0x15] = 0x01; + dev->shadow_regs[0x16] = 0x0f; + dev->shadow_regs[0x1d] = 0x20; + dev->shadow_regs[0x28] = 0xeb; + dev->shadow_regs[0x29] = 0xee; + dev->shadow_regs[0x2a] = 0xf7; + dev->shadow_regs[0x2b] = 0x01; + dev->shadow_regs[0x2e] = 0x86; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x33] = 0x01; + dev->shadow_regs[0x3a] = 0x20; + dev->shadow_regs[0x3b] = 0x37; + dev->shadow_regs[0x3c] = 0x88; + dev->shadow_regs[0x3d] = 0x08; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x72] = 0x05; + dev->shadow_regs[0x74] = 0x0e; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x8c] = 0x02; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + dev->shadow_regs[0x94] = 0x0e; + dev->shadow_regs[0xa3] = 0xcc; + dev->shadow_regs[0xa4] = 0x27; + dev->shadow_regs[0xa5] = 0x24; + dev->shadow_regs[0xb0] = 0xb2; + dev->shadow_regs[0xb2] = 0x04; + dev->shadow_regs[0xc2] = 0x80; + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xed] = 0xc2; + dev->shadow_regs[0xee] = 0x02; + status = SANE_STATUS_GOOD; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x00] = 0x00; + dev->shadow_regs[0x01] = 0x43; + dev->shadow_regs[0x0b] = 0x70; + dev->shadow_regs[0x0c] = 0x28; + dev->shadow_regs[0x0d] = 0xa4; + dev->shadow_regs[0x11] = 0x01; + dev->shadow_regs[0x12] = 0x0f; + dev->shadow_regs[0x13] = 0x01; + dev->shadow_regs[0x15] = 0x01; + dev->shadow_regs[0x16] = 0x0f; + dev->shadow_regs[0x17] = 0x00; + dev->shadow_regs[0x1d] = 0x20; + dev->shadow_regs[0x28] = 0xf5; + dev->shadow_regs[0x29] = 0xf7; + dev->shadow_regs[0x2a] = 0xf5; + dev->shadow_regs[0x2b] = 0x17; + dev->shadow_regs[0x2d] = 0x41; + dev->shadow_regs[0x2e] = 0x86; + dev->shadow_regs[0x2f] = 0x11; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x31] = 0x01; + dev->shadow_regs[0x33] = 0x01; + dev->shadow_regs[0x34] = 0x50; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x50; + dev->shadow_regs[0x37] = 0x01; + dev->shadow_regs[0x38] = 0x50; + dev->shadow_regs[0x3a] = 0x20; + dev->shadow_regs[0x3b] = 0x37; + dev->shadow_regs[0x3c] = 0x88; + dev->shadow_regs[0x3d] = 0x08; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x47] = 0x01; + dev->shadow_regs[0x48] = 0x1a; + dev->shadow_regs[0x49] = 0x5b; + dev->shadow_regs[0x4a] = 0x1b; + dev->shadow_regs[0x4b] = 0x5b; + dev->shadow_regs[0x4c] = 0x05; + dev->shadow_regs[0x4d] = 0x3f; + dev->shadow_regs[0x60] = 0x2f; + dev->shadow_regs[0x61] = 0x36; + dev->shadow_regs[0x62] = 0x30; + dev->shadow_regs[0x63] = 0x36; + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x66] = 0x64; + dev->shadow_regs[0x6c] = 0xc8; + dev->shadow_regs[0x6d] = 0x00; + dev->shadow_regs[0x72] = 0x35; + dev->shadow_regs[0x74] = 0x4e; + dev->shadow_regs[0x75] = 0x03; + dev->shadow_regs[0x79] = 0x40; + dev->shadow_regs[0x7a] = 0x01; + dev->shadow_regs[0x85] = 0x02; + dev->shadow_regs[0x86] = 0x33; + dev->shadow_regs[0x87] = 0x0f; + dev->shadow_regs[0x88] = 0x24; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x8c] = 0x02; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + dev->shadow_regs[0x91] = 0x19; + dev->shadow_regs[0x92] = 0x20; + dev->shadow_regs[0x93] = 0x02; + dev->shadow_regs[0x94] = 0x0e; + dev->shadow_regs[0xa3] = 0x0d; + dev->shadow_regs[0xa4] = 0x5e; + dev->shadow_regs[0xa5] = 0x23; + dev->shadow_regs[0xb0] = 0x2c; + dev->shadow_regs[0xb1] = 0x07; + dev->shadow_regs[0xb2] = 0x04; + dev->shadow_regs[0xc2] = 0x80; + dev->shadow_regs[0xc3] = 0x01; + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x0a; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xca] = 0x0a; + dev->shadow_regs[0xe2] = 0xf8; + dev->shadow_regs[0xe3] = 0x2a; + status = SANE_STATUS_GOOD; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x01] = 0x43; + dev->shadow_regs[0x11] = 0x01; + dev->shadow_regs[0x12] = 0x0f; + dev->shadow_regs[0x13] = 0x01; + dev->shadow_regs[0x15] = 0x01; + dev->shadow_regs[0x16] = 0x0f; + dev->shadow_regs[0x17] = 0x00; + dev->shadow_regs[0x1d] = 0x20; + dev->shadow_regs[0x28] = 0xf5; + dev->shadow_regs[0x29] = 0xf7; + dev->shadow_regs[0x2a] = 0xf5; + dev->shadow_regs[0x2b] = 0x17; + dev->shadow_regs[0x2d] = 0x41; + dev->shadow_regs[0x2e] = 0x86; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x31] = 0x01; + dev->shadow_regs[0x33] = 0x01; + dev->shadow_regs[0x34] = 0x50; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x50; + dev->shadow_regs[0x37] = 0x01; + dev->shadow_regs[0x38] = 0x50; + dev->shadow_regs[0x3c] = 0x88; + dev->shadow_regs[0x3d] = 0x08; + dev->shadow_regs[0x66] = 0x64; + dev->shadow_regs[0x67] = 0x00; + dev->shadow_regs[0x6c] = 0xc8; + dev->shadow_regs[0x6d] = 0x00; + dev->shadow_regs[0x72] = 0x35; + dev->shadow_regs[0x74] = 0x4e; + dev->shadow_regs[0x75] = 0x03; + dev->shadow_regs[0x7a] = 0x01; + dev->shadow_regs[0x93] = 0x0a; + dev->shadow_regs[0x94] = 0x0e; + + dev->shadow_regs[0xc3] = 0x01; + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x0a; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xca] = 0x0a; + dev->shadow_regs[0xe2] = 0xf8; + dev->shadow_regs[0xe3] = 0x2a; + status = SANE_STATUS_GOOD; + break; + case A920_SENSOR: + dev->shadow_regs[0x01] = 0x43; + dev->shadow_regs[0x0b] = 0x70; + dev->shadow_regs[0x0c] = 0x28; + dev->shadow_regs[0x0d] = 0xa4; + dev->shadow_regs[0x11] = 0x01; + dev->shadow_regs[0x12] = 0x0f; + dev->shadow_regs[0x13] = 0x01; + dev->shadow_regs[0x15] = 0x01; + dev->shadow_regs[0x16] = 0x07; + dev->shadow_regs[0x1d] = 0x20; + dev->shadow_regs[0x28] = 0xf5; + dev->shadow_regs[0x29] = 0xf7; + dev->shadow_regs[0x2a] = 0xf5; + dev->shadow_regs[0x2b] = 0x17; + dev->shadow_regs[0x2e] = 0x86; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x31] = 0x01; + dev->shadow_regs[0x33] = 0x01; + dev->shadow_regs[0x3a] = 0x20; + dev->shadow_regs[0x3b] = 0x37; + dev->shadow_regs[0x3c] = 0x88; + dev->shadow_regs[0x3d] = 0x08; + dev->shadow_regs[0x47] = 0x21; + dev->shadow_regs[0x48] = 0x1a; + dev->shadow_regs[0x49] = 0x5b; + dev->shadow_regs[0x4a] = 0x1b; + dev->shadow_regs[0x4b] = 0x5b; + dev->shadow_regs[0x4c] = 0x05; + dev->shadow_regs[0x4d] = 0x3f; + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x89] = 0xf5; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + dev->shadow_regs[0x94] = 0x0e; + dev->shadow_regs[0xa3] = 0x0d; + dev->shadow_regs[0xa4] = 0x5e; + dev->shadow_regs[0xa5] = 0x23; + dev->shadow_regs[0xb0] = 0x2c; + dev->shadow_regs[0xb1] = 0x0f; + dev->shadow_regs[0xc2] = 0x80; + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc8] = 0x04; + status = SANE_STATUS_GOOD; + break; + case X1200_SENSOR: + dev->shadow_regs[0x01] = 0x43; + dev->shadow_regs[0x0b] = 0x70; + dev->shadow_regs[0x0c] = 0x28; + dev->shadow_regs[0x0d] = 0xa4; + dev->shadow_regs[0x11] = 0x01; + dev->shadow_regs[0x12] = 0x0f; + dev->shadow_regs[0x13] = 0x01; + dev->shadow_regs[0x15] = 0x01; + dev->shadow_regs[0x16] = 0x0f; + dev->shadow_regs[0x1d] = 0x20; + dev->shadow_regs[0x28] = 0xe9; + dev->shadow_regs[0x29] = 0xeb; + dev->shadow_regs[0x2a] = 0xe9; + dev->shadow_regs[0x2b] = 0x0b; + dev->shadow_regs[0x2d] = 0x01; + dev->shadow_regs[0x2e] = 0x86; + dev->shadow_regs[0x2f] = 0x11; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x33] = 0x01; + dev->shadow_regs[0x34] = 0x50; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x50; + dev->shadow_regs[0x37] = 0x01; + dev->shadow_regs[0x38] = 0x50; + dev->shadow_regs[0x3a] = 0x20; + dev->shadow_regs[0x3b] = 0x37; + dev->shadow_regs[0x3c] = 0x88; + dev->shadow_regs[0x3d] = 0x08; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x47] = 0x01; + dev->shadow_regs[0x48] = 0x1a; + dev->shadow_regs[0x49] = 0x5b; + dev->shadow_regs[0x4a] = 0x1b; + dev->shadow_regs[0x4b] = 0x5b; + dev->shadow_regs[0x4c] = 0x05; + dev->shadow_regs[0x4d] = 0x3f; + dev->shadow_regs[0x60] = 0x12; + dev->shadow_regs[0x62] = 0x81; + dev->shadow_regs[0x63] = 0x03; + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x66] = 0x64; + dev->shadow_regs[0x6c] = 0xc8; + dev->shadow_regs[0x72] = 0x1e; + dev->shadow_regs[0x74] = 0x3c; + dev->shadow_regs[0x75] = 0x03; + dev->shadow_regs[0x79] = 0x40; + dev->shadow_regs[0x7a] = 0x01; + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x1e; + dev->shadow_regs[0x87] = 0x39; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x8c] = 0x02; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + dev->shadow_regs[0x92] = 0x92; + dev->shadow_regs[0x93] = 0x02; + dev->shadow_regs[0x94] = 0x0e; + dev->shadow_regs[0xa3] = 0x0d; + dev->shadow_regs[0xa4] = 0x5e; + dev->shadow_regs[0xa5] = 0x23; + dev->shadow_regs[0xb0] = 0x2c; + dev->shadow_regs[0xb1] = 0x07; + dev->shadow_regs[0xb2] = 0x04; + dev->shadow_regs[0xc2] = 0x80; + dev->shadow_regs[0xc3] = 0x01; + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x0a; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xca] = 0x0a; + dev->shadow_regs[0xe2] = 0xf8; + dev->shadow_regs[0xe3] = 0x2a; + dev->shadow_regs[0xf3] = 0xff; + dev->shadow_regs[0xf4] = 0x0f; + break; + } + DBG (5, "sanei_lexmark_low_init: init done for model %s/%s\n", + dev->model.model, dev->model.name); + DBG (2, "low_init: done\n"); + return status; +} + +void +sanei_lexmark_low_destroy (Lexmark_Device * dev) +{ + /* free the read buffer */ + if (dev->read_buffer != NULL) + read_buffer_free (dev->read_buffer); +} + + +SANE_Status +low_usb_bulk_write (SANE_Int devnum, SANE_Byte * cmd, size_t * size) +{ + SANE_Status status; + size_t cmd_size; + + cmd_size = *size; +#ifdef FAKE_USB + status = SANE_STATUS_GOOD; +#else + status = sanei_usb_write_bulk (devnum, cmd, size); +#endif + if (status != SANE_STATUS_GOOD) + { + DBG (5, + "low_usb_bulk_write: returned %s (size = %lu, expected %lu)\n", + sane_strstatus (status), (u_long) * size, (u_long) cmd_size); + /* F.O. should reset the pipe here... */ + } + return status; +} + +SANE_Status +low_usb_bulk_read (SANE_Int devnum, SANE_Byte * buf, size_t * size) +{ + SANE_Status status; + size_t exp_size; + + exp_size = *size; +#ifdef FAKE_USB + status = SANE_STATUS_GOOD; +#else + status = sanei_usb_read_bulk (devnum, buf, size); +#endif + if (status != SANE_STATUS_GOOD) + { + DBG (5, + "low_usb_bulk_read: returned %s (size = %lu, expected %lu)\n", + sane_strstatus (status), (u_long) * size, (u_long) exp_size); + /* F.O. should reset the pipe here... */ + } + DBG (7, "low_usb_bulk_read: returned size = %lu (required %lu)\n", + (u_long) * size, (u_long) exp_size); + return status; +} + + +SANE_Status +low_start_mvmt (SANE_Int devnum) +{ + SANE_Status status; + SANE_Byte reg; + + reg = 0x68; + rts88xx_write_reg (devnum, 0xb3, ®); + status = rts88xx_write_reg (devnum, 0xb3, ®); + return status; +} + +SANE_Status +low_stop_mvmt (SANE_Int devnum) +{ + SANE_Status status; + SANE_Byte reg; + + /* Stop scanner - clear reg 0xb3: */ + reg = 0x02; + rts88xx_write_reg (devnum, 0xb3, ®); + rts88xx_write_reg (devnum, 0xb3, ®); + reg = 0x00; + rts88xx_write_reg (devnum, 0xb3, ®); + status = rts88xx_write_reg (devnum, 0xb3, ®); + return status; +} + +SANE_Status +low_clr_c6 (SANE_Int devnum) +{ + SANE_Status status; + SANE_Byte reg; + + /* Clear register 0xC6 */ + /* cmd_size = 0x05; + return low_usb_bulk_write (devnum, clearC6_command_block, &cmd_size); */ + + reg = 0x00; + status = rts88xx_write_reg (devnum, 0xc6, ®); + return status; +} + +/* stops current scan */ +static SANE_Status +low_cancel (SANE_Int devnum) +{ + SANE_Status status; + + DBG (2, "low_cancel: start\n"); + status = low_stop_mvmt (devnum); + if (status != SANE_STATUS_GOOD) + return status; + status = low_clr_c6 (devnum); + if (status != SANE_STATUS_GOOD) + return status; + DBG (2, "low_cancel: end.\n"); + return status; +} + +static SANE_Status +low_start_scan (SANE_Int devnum, SANE_Byte * regs) +{ + SANE_Status status; + + DBG (2, "low_start_scan: start\n"); + + /* writes registers to scanner */ + regs[0x32] = 0x00; + status = low_write_all_regs (devnum, regs); + if (status != SANE_STATUS_GOOD) + return status; + regs[0x32] = 0x40; + status = low_write_all_regs (devnum, regs); + if (status != SANE_STATUS_GOOD) + return status; + + /* Stop scanner - clear reg 0xb3: */ + /* status = low_stop_mvmt (devnum); + if (status != SANE_STATUS_GOOD) + return status; */ + + /* then start */ + status = rts88xx_commit (devnum, regs[0x2c]); + DBG (2, "low_start_scan: end.\n"); + return status; +} + +/* wait for scan data being available */ +static SANE_Status +low_poll_data (SANE_Int devnum) +{ + SANE_Status status; + int loops = 0; + size_t size; + static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; + SANE_Byte result[3]; + SANE_Word count; + + /* Poll the available byte count until not 0 */ + while (loops < 1000) + { + /* 10 ms sleep */ + usleep (10000); + + /* as stated in sanei_lexmark_low_search_home_bwd, we read + * available data count twice */ + size = 4; + status = low_usb_bulk_write (devnum, command4_block, &size); + if (status != SANE_STATUS_GOOD) + return status; + size = 0x3; + status = low_usb_bulk_read (devnum, result, &size); + if (status != SANE_STATUS_GOOD) + return status; + size = 4; + /* read availbale data size again */ + status = low_usb_bulk_write (devnum, command4_block, &size); + if (status != SANE_STATUS_GOOD) + return status; + size = 0x3; + status = low_usb_bulk_read (devnum, result, &size); + if (status != SANE_STATUS_GOOD) + return status; + count = result[0] + (result[1] << 8) + (result[2] << 16); + if (count != 0) + { + DBG (15, "low_poll_data: %d bytes available\n", count); + return SANE_STATUS_GOOD; + } + loops++; + } + return SANE_STATUS_IO_ERROR; +} + +/** + * do a simple scan with the given registers. data buffer is allocated within + * the function + */ +static SANE_Status +low_simple_scan (Lexmark_Device * dev, SANE_Byte * regs, int xoffset, + int pixels, int yoffset, int lines, SANE_Byte ** data) +{ + SANE_Status status = SANE_STATUS_GOOD; + static SANE_Byte reg; + size_t size, read, needed; + int i, bpl, yend; + + DBG (2, "low_simple_scan: start\n"); + DBG (15, "low_simple_scan: x=%d, pixels=%d (ex=%d), y=%d, lines=%d\n", + xoffset, pixels, xoffset + pixels * regs[0x7a], yoffset, lines); + + /* set up registers */ + regs[0x60] = LOBYTE (yoffset); + regs[0x61] = HIBYTE (yoffset); + yend = yoffset + lines; + if ((dev->model.motor_type == A920_MOTOR + || dev->model.motor_type == X74_MOTOR) && rts88xx_is_color (regs) + && dev->val[OPT_RESOLUTION].w == 600) + yend *= 2; + regs[0x62] = LOBYTE (yend); + regs[0x63] = HIBYTE (yend); + + regs[0x66] = LOBYTE (xoffset); + regs[0x67] = HIBYTE (xoffset); + + regs[0x6c] = LOBYTE (xoffset + pixels * regs[0x7a]); + regs[0x6d] = HIBYTE (xoffset + pixels * regs[0x7a]); + + /* allocate memory */ + if (rts88xx_is_color (regs)) + bpl = 3 * pixels; + else + bpl = pixels; + *data = (SANE_Byte *) malloc (bpl * lines); + if (*data == NULL) + { + DBG (2, + "low_simple_scan: failed to allocate %d bytes !\n", bpl * lines); + return SANE_STATUS_NO_MEM; + } + + /* start scan */ + status = low_cancel (dev->devnum); + if (status != SANE_STATUS_GOOD) + return status; + + + status = low_start_scan (dev->devnum, regs); + if (status != SANE_STATUS_GOOD) + return status; + + /* wait for data */ + status = low_poll_data (dev->devnum); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "low_simple_scan: time-out while waiting for data.\n"); + return status; + } + + /* data reading loop */ + needed = bpl * lines; + DBG (1, "low_simple_scan: bpl=%d, lines=%d, needed=%lu.\n", bpl, lines, + (u_long) needed); + read = 0; + do + { + /* this block would deserve to be a function */ + status = + rts88xx_read_data (dev->devnum, needed - read, (*data) + read, &size); + if (status != SANE_STATUS_GOOD) + return status; + read += size; + } + while (read < needed); + + /* if needed, wait for motor to stop */ + if (regs[0xc3] & 0x80) + { + i = 0; + do + { + if (rts88xx_read_reg (dev->devnum, 0xb3, ®) != SANE_STATUS_GOOD) + { + DBG (5, "low_simple_scan: register read failed ...\n"); + return SANE_STATUS_IO_ERROR; + } + usleep (100000); + i++; + } + while ((reg & 0x08) && (i < 100)); + if (reg & 0x08) + { + DBG (5, + "low_simple_scan : timeout waiting for motor to stop ...\n"); + return SANE_STATUS_IO_ERROR; + } + } + + /* stop scan */ + status = low_cancel (dev->devnum); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "low_simple_scan: cancel failed.\n"); + return status; + } + + DBG (2, "low_simple_scan: end.\n"); + return status; +} + +/* + * open USB device ,read initial registers values and probe sensor + */ +SANE_Status +sanei_lexmark_low_open_device (Lexmark_Device * dev) +{ + /* This function calls the Sane Interface to open this usb device. + It also needlessly does what the Windows driver does and reads + the entire register set - this may be removed. */ + + SANE_Status result; + static SANE_Byte command_block[] = { 0x80, 0, 0x00, 0xFF }; + size_t size; + SANE_Byte variant = 0; + SANE_Byte shadow_regs[255]; + int sx, ex; + int sy, ey; + int i; + char msg[2048]; + + +#ifdef FAKE_USB + result = SANE_STATUS_GOOD; + shadow_regs[0x00] = 0x91; + shadow_regs[0xb0] = 0x2c; + shadow_regs[0x10] = 0x97; + shadow_regs[0x10] = 0x87; + shadow_regs[0xf3] = 0xf8; + shadow_regs[0xf4] = 0x7f; +#else + result = sanei_usb_open (dev->sane.name, &(dev->devnum)); +#endif + DBG (2, "sanei_lexmark_low_open_device: devnum=%d\n", dev->devnum); + + size = 4; + low_usb_bulk_write (dev->devnum, command_block, &size); + size = 0xFF; + memset (shadow_regs, 0, sizeof (shadow_regs)); + low_usb_bulk_read (dev->devnum, shadow_regs, &size); + + if (DBG_LEVEL > 2) + { + DBG (2, "sanei_lexmark_low_open_device: initial registers values\n"); + for (i = 0; i < 255; i++) + { + sprintf (msg + i * 5, "0x%02x ", shadow_regs[i]); + } + DBG (3, "%s\n", msg); + } + + /* it seems that at first read after reset, registers hold information + * about the scanner. Register 0x00 is overwritten with 0, so only first read + * after USB plug-in gives this value */ + if (shadow_regs[0] == 0x91) + { + sx = shadow_regs[0x67] * 256 + shadow_regs[0x66]; + ex = shadow_regs[0x6d] * 256 + shadow_regs[0x6c]; + DBG (7, "startx=%d, endx=%d, pixels=%d, coef=%d, r2f=0x%02x\n", sx, ex, + ex - sx, dev->shadow_regs[0x7a], shadow_regs[0x2f]); + sy = shadow_regs[0x61] * 256 + shadow_regs[0x60]; + ey = shadow_regs[0x63] * 256 + shadow_regs[0x62]; + DBG (7, "starty=%d, endy=%d, lines=%d\n", sy, ey, ey - sy); + } + + /* we use register 0xb0 to identify details about models */ + /* this register isn't overwritten during normal operation */ + if (shadow_regs[0xb0] == 0x2c && dev->model.sensor_type == X1100_B2_SENSOR) + { + variant = shadow_regs[0xb0]; + } + /* now the same with register 0x10 */ + /* which most likely signals USB2.0/USB1.1 */ + if ((dev->model.sensor_type == X1200_SENSOR) && (shadow_regs[0x10] == 0x97)) + { + variant = shadow_regs[0x10]; + } + + /* if find a case where default model given is inappropriate, reassign it + * since we have now the informations to get the real one. + * We could avoid this if attach() did open and read registers, not init */ + if (variant != 0) + { + DBG (3, + "sanei_lexmark_low_open_device: reassign model/sensor for variant 0x%02x\n", + variant); + sanei_lexmark_low_assign_model (dev, dev->sane.name, + dev->model.vendor_id, + dev->model.product_id, variant); + /* since model has changed, run init again */ + sanei_lexmark_low_init (dev); + } + DBG (2, "sanei_lexmark_low_open_device: end\n"); + return result; +} + +void +sanei_lexmark_low_close_device (Lexmark_Device * dev) +{ + /* put scanner in idle state */ + lexmark_low_set_idle (dev->devnum); + + /* This function calls the Sane USB library to close this usb device */ +#ifndef FAKE_USB + sanei_usb_close (dev->devnum); +#endif + return; +} + + +/* This function writes the contents of the given registers to the + scanner. */ +SANE_Status +low_write_all_regs (SANE_Int devnum, SANE_Byte * regs) +{ + int i; + SANE_Status status; + size_t size; + static SANE_Byte command_block1[0xb7]; + static SANE_Byte command_block2[0x4f]; + command_block1[0] = 0x88; + command_block1[1] = 0x00; + command_block1[2] = 0x00; + command_block1[3] = 0xb3; + for (i = 0; i < 0xb3; i++) + { + command_block1[i + 4] = regs[i]; + } + command_block2[0] = 0x88; + command_block2[1] = 0xb4; + command_block2[2] = 0x00; + command_block2[3] = 0x4b; + for (i = 0; i < 0x4b; i++) + { + command_block2[i + 4] = regs[i + 0xb4]; + } + size = 0xb7; + +#ifdef DEEP_DEBUG + fprintf (stderr, "write_all(0x00,255)="); + for (i = 0; i < 255; i++) + { + fprintf (stderr, "0x%02x ", regs[i]); + } + fprintf (stderr, "\n"); +#endif + + status = low_usb_bulk_write (devnum, command_block1, &size); + if (status != SANE_STATUS_GOOD) + return status; + size = 0x4f; + status = low_usb_bulk_write (devnum, command_block2, &size); + if (status != SANE_STATUS_GOOD) + return status; + return SANE_STATUS_GOOD; +} + + +SANE_Bool +low_is_home_line (Lexmark_Device * dev, unsigned char *buffer) +{ + /* + This function assumes the buffer has a size of 2500 bytes.It is + destructive to the buffer. + + Here is what it does: + + Go through the buffer finding low and high values, which are computed by + comparing to the average: + average = (lowest value + highest value)/2 + High bytes are changed to 0xFF (white), lower or equal bytes are changed + to 0x00 (black),so that the buffer only contains white (0xFF) or black + (0x00) values. + + Next, we go through the buffer. We use a tolerance of 5 bytes on each end + of the buffer and check a region from bytes 5 to 2495. We start assuming + we are in a white region and look for the start of a black region. We save + this index as the transition from white to black. We also save where we + change from black back to white. We continue checking for transitions + until the end of the check region. If we don't have exactly two + transitions when we reach the end we return SANE_FALSE. + + The final check compares the transition indices to the nominal values + plus or minus the tolerence. For the first transition (white to black + index) the value must lie in the range 1235-30 (1205) to 1235+30 (1265). + For the second transition (black to white) the value must lie in the range + 1258-30 (1228) to 1258+30 (1288). If the indices are out of range we + return SANE_FALSE. Otherwise, we return SANE_TRUE. + */ + + + unsigned char max_byte = 0; + unsigned char min_byte = 0xFF; + unsigned char average; + int i; + int home_point1; + int home_point2; + region_type region; + int transition_counter; + int index1 = 0; + int index2 = 0; + int low_range, high_range; + +#ifdef DEEP_DEBUG + static int numero = 0; + char titre[80]; + FILE *trace = NULL; + sprintf (titre, "lgn%03d.pnm", numero); + trace = fopen (titre, "wb"); + if (trace) + { + fprintf (trace, "P5\n2500 1\n255\n"); + fwrite (buffer, 2500, 1, trace); + fclose (trace); + } + numero++; +#endif + + DBG (15, "low_is_home_line: start\n"); + /* Find the max and the min */ + for (i = 0; i < 2500; i++) + { + if (*(buffer + i) > max_byte) + max_byte = *(buffer + i); + if (*(buffer + i) < min_byte) + min_byte = *(buffer + i); + } + + /* The average */ + average = ((max_byte + min_byte) / 2); + + /* Set bytes as white (0xFF) or black (0x00) */ + for (i = 0; i < 2500; i++) + { + if (*(buffer + i) > average) + *(buffer + i) = 0xFF; + else + *(buffer + i) = 0x00; + } + + region = white; + transition_counter = 0; + + /* Go through the check region - bytes 5 to 2495 */ + /* XXX STEF XXX shrink the area to where the dot should be + * +-100 around the 1250 expected location */ + for (i = 1150; i <= 1350; i++) + { + /* Check for transition to black */ + if ((region == white) && (*(buffer + i) == 0)) + { + if (transition_counter < 2) + { + region = black; + index1 = i; + transition_counter++; + } + else + { + DBG (15, "low_is_home_line: no transition to black \n"); + return SANE_FALSE; + } + } + /* Check for transition to white */ + else if ((region == black) && (*(buffer + i) == 0xFF)) + { + if (transition_counter < 2) + { + region = white; + index2 = i; + transition_counter++; + } + else + { + DBG (15, "low_is_home_line: no transition to white \n"); + return SANE_FALSE; + } + } + } + + /* Check that the number of transitions is 2 */ + if (transition_counter != 2) + { + DBG (15, "low_is_home_line: transitions!=2 (%d)\n", transition_counter); + return SANE_FALSE; + } + + + + + /* Check that the 1st index is in range */ + home_point1 = dev->model.HomeEdgePoint1; + low_range = home_point1 - HomeTolerance; + high_range = home_point1 + HomeTolerance; + + if ((index1 < low_range) || (index1 > high_range)) + { + DBG (15, "low_is_home_line: index1=%d out of range\n", index1); + return SANE_FALSE; + } + + + /* Check that the 2nd index is in range */ + home_point2 = dev->model.HomeEdgePoint2; + low_range = home_point2 - HomeTolerance; + high_range = home_point2 + HomeTolerance; + + if ((index2 < low_range) || (index2 > high_range)) + { + DBG (15, "low_is_home_line: index2=%d out of range.\n", index2); + return SANE_FALSE; + } + + /* We made it this far, so its a good home line. Return True */ + DBG (15, "low_is_home_line: success\n"); + return SANE_TRUE; +} + +void +sanei_lexmark_low_move_fwd (SANE_Int distance, Lexmark_Device * dev, + SANE_Byte * regs) +{ + /* + This function moves the scan head forward with the highest vertical + resolution of 1200dpi. The distance moved is given by the distance + parameter. + + As an example, given a distance parameter of 600, the scan head will + move 600/1200", or 1/2" forward. + */ + + static SANE_Byte pollstopmoving_command_block[] = + { 0x80, 0xb3, 0x00, 0x01 }; + + + size_t cmd_size; + SANE_Int devnum; + SANE_Bool scan_head_moving; + SANE_Byte read_result; + + DBG (2, "sanei_lexmark_low_move_fwd: \n"); + devnum = dev->devnum; + + + /* registers set-up */ + regs[0x2c] = 0x00; + regs[0x2d] = 0x41; + regs[0x65] = 0x80; + switch (dev->model.sensor_type) + { + case X74_SENSOR: + rts88xx_set_scan_frequency (regs, 0); + regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + regs[0x8b] = 0x00; + regs[0x8c] = 0x00; + regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + rts88xx_set_scan_frequency (regs, 0); + regs[0x93] = 0x06; + break; + case A920_SENSOR: + rts88xx_set_scan_frequency (regs, 0); + regs[0x8b] = 0xff; + regs[0x8c] = 0x02; + regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x2d] = 0x01; + rts88xx_set_scan_frequency (regs, 0); + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x2d] = 0x4f; + rts88xx_set_scan_frequency (regs, 0); + break; + } + + /* set grayscale scan + nodata/nochannel? */ + regs[0x2f] = 0xa1; + + /* set ? */ + regs[0x34] = 0x50; + regs[0x35] = 0x01; + regs[0x36] = 0x50; + regs[0x37] = 0x01; + regs[0x38] = 0x50; + /* set motor resolution divisor */ + regs[0x39] = 0x00; + /* set vertical start/end positions */ + regs[0x60] = LOBYTE (distance - 1); + regs[0x61] = HIBYTE (distance - 1); + regs[0x62] = LOBYTE (distance); + regs[0x63] = HIBYTE (distance); + /* set horizontal start position */ + regs[0x66] = 0x64; + regs[0x67] = 0x00; + /* set horizontal end position */ + regs[0x6c] = 0xc8; + regs[0x6d] = 0x00; + /* set horizontal resolution */ + regs[0x79] = 0x40; + regs[0x7a] = 0x01; + /* don't buffer data for this scan */ + regs[0xb2] = 0x04; + /* Motor enable & Coordinate space denominator */ + regs[0xc3] = 0x81; + /* Movement direction & step size */ + regs[0xc6] = 0x09; + /* ? */ + regs[0x80] = 0x00; + regs[0x81] = 0x00; + regs[0x82] = 0x00; + regs[0xc5] = 0x0a; + + + switch (dev->model.motor_type) + { + case X1100_MOTOR: + case A920_MOTOR: + /* step size range2 */ + regs[0xc9] = 0x3b; + /* ? */ + regs[0xca] = 0x0a; + /* motor curve stuff */ + regs[0xe0] = 0x00; + regs[0xe1] = 0x00; + regs[0xe4] = 0x00; + regs[0xe5] = 0x00; + regs[0xe7] = 0x00; + regs[0xe8] = 0x00; + regs[0xe2] = 0x09; + regs[0xe3] = 0x1a; + regs[0xe6] = 0xdc; + regs[0xe9] = 0x1b; + regs[0xec] = 0x07; + regs[0xef] = 0x03; + break; + case X74_MOTOR: + regs[0xc5] = 0x41; + /* step size range2 */ + regs[0xc9] = 0x39; + /* ? */ + regs[0xca] = 0x40; + /* motor curve stuff */ + regs[0xe0] = 0x00; + regs[0xe1] = 0x00; + regs[0xe2] = 0x09; + regs[0xe3] = 0x1a; + regs[0xe4] = 0x00; + regs[0xe5] = 0x00; + regs[0xe6] = 0x64; + regs[0xe7] = 0x00; + regs[0xe8] = 0x00; + regs[0xe9] = 0x32; + regs[0xec] = 0x0c; + regs[0xef] = 0x08; + break; + } + + + /* prepare for register write */ + low_clr_c6 (devnum); + low_stop_mvmt (devnum); + +/* Move Forward without scanning: */ + regs[0x32] = 0x00; + low_write_all_regs (devnum, regs); + regs[0x32] = 0x40; + low_write_all_regs (devnum, regs); + + /* Stop scanner - clear reg 0xb3: */ + /* low_stop_mvmt (devnum); */ + + rts88xx_commit (devnum, regs[0x2c]); + + /* Poll for scanner stopped - return value(3:0) = 0: */ + scan_head_moving = SANE_TRUE; + while (scan_head_moving) + { +#ifdef FAKE_USB + scan_head_moving = SANE_FALSE; +#else + cmd_size = 0x04; + low_usb_bulk_write (devnum, pollstopmoving_command_block, &cmd_size); + cmd_size = 0x1; + low_usb_bulk_read (devnum, &read_result, &cmd_size); + if ((read_result & 0xF) == 0x0) + { + scan_head_moving = SANE_FALSE; + } +#endif + } + + /* this is needed to find the start line properly */ + if (dev->model.sensor_type == X74_SENSOR) + low_stop_mvmt (devnum); + + DBG (2, "sanei_lexmark_low_move_fwd: end.\n"); +} + +SANE_Bool +sanei_lexmark_low_search_home_fwd (Lexmark_Device * dev) +{ + /* This function actually searches backwards one line looking for home */ + + SANE_Int devnum; + int i; + SANE_Byte poll_result[3]; + SANE_Byte *buffer; + SANE_Byte temp_byte; + + static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; + + static SANE_Byte command5_block[] = { 0x91, 0x00, 0x09, 0xc4 }; + + size_t cmd_size; + SANE_Bool got_line; + SANE_Bool ret_val; + + devnum = dev->devnum; + + DBG (2, "sanei_lexmark_low_search_home_fwd:\n"); + + /* set up registers according to the sensor type */ + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x2c] = 0x03; + dev->shadow_regs[0x2d] = 0x45; + dev->shadow_regs[0x2f] = 0x21; + dev->shadow_regs[0x30] = 0x48; + dev->shadow_regs[0x31] = 0x06; + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x09; + dev->shadow_regs[0x37] = 0x09; + dev->shadow_regs[0x38] = 0x0d; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x75] = 0x00; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x2c] = 0x0f; + dev->shadow_regs[0x2d] = 0x51; + dev->shadow_regs[0x2f] = 0x21; + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x35] = 0x04; + dev->shadow_regs[0x36] = 0x08; + dev->shadow_regs[0x37] = 0x08; + dev->shadow_regs[0x38] = 0x0b; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x09; + dev->shadow_regs[0x37] = 0x09; + dev->shadow_regs[0x38] = 0x0d; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x72] = 0x35; + dev->shadow_regs[0x74] = 0x4e; + + dev->shadow_regs[0x85] = 0x20; /* 05 */ + dev->shadow_regs[0x86] = 0x00; /* 05 */ + dev->shadow_regs[0x87] = 0x00; /* 05 */ + dev->shadow_regs[0x88] = 0x00; /* 45 */ + dev->shadow_regs[0x89] = 0x00; + dev->shadow_regs[0x8b] = 0xff; + + dev->shadow_regs[0x93] = 0x06; /* 0e */ + + dev->shadow_regs[0x75] = 0x00; /* */ + dev->shadow_regs[0x91] = 0x00; /* 60 */ + dev->shadow_regs[0x92] = 0x00; /* 8d */ + dev->shadow_regs[0xb1] = 0x00; /* */ + dev->shadow_regs[0xc5] = 0x00; /* */ + dev->shadow_regs[0xca] = 0x00; /* */ + dev->shadow_regs[0xc3] = 0x01; /* */ + break; + case A920_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x09; + dev->shadow_regs[0x37] = 0x09; + dev->shadow_regs[0x38] = 0x0d; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x72] = 0x35; + dev->shadow_regs[0x74] = 0x4e; + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x88] = 0x45; + dev->shadow_regs[0x89] = 0x00; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x91] = 0x60; + dev->shadow_regs[0x92] = 0x8d; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x2c] = 0x01; + dev->shadow_regs[0x2d] = 0x03; + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x35] = 0x04; + dev->shadow_regs[0x36] = 0x08; + dev->shadow_regs[0x37] = 0x08; + dev->shadow_regs[0x38] = 0x0b; + dev->shadow_regs[0x66] = 0x88; + dev->shadow_regs[0x6c] = 0x10; + dev->shadow_regs[0x6d] = 0x14; + dev->shadow_regs[0x75] = 0x00; + dev->shadow_regs[0x93] = 0x06; + dev->shadow_regs[0xc5] = 0x00; + dev->shadow_regs[0xca] = 0x00; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x0b] = 0x70; + dev->shadow_regs[0x0c] = 0x28; + dev->shadow_regs[0x0d] = 0xa4; + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x2f] = 0x21; + dev->shadow_regs[0x32] = 0x40; + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x09; + dev->shadow_regs[0x37] = 0x09; + dev->shadow_regs[0x38] = 0x0d; + dev->shadow_regs[0x3a] = 0x20; + dev->shadow_regs[0x3b] = 0x37; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x47] = 0x01; + dev->shadow_regs[0x48] = 0x1a; + dev->shadow_regs[0x49] = 0x5b; + dev->shadow_regs[0x4a] = 0x1b; + dev->shadow_regs[0x4b] = 0x5b; + dev->shadow_regs[0x4c] = 0x05; + dev->shadow_regs[0x4d] = 0x3f; + dev->shadow_regs[0x75] = 0x00; + dev->shadow_regs[0x85] = 0x03; + dev->shadow_regs[0x86] = 0x33; + dev->shadow_regs[0x87] = 0x8f; + dev->shadow_regs[0x88] = 0x34; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + dev->shadow_regs[0x91] = 0x59; + dev->shadow_regs[0x92] = 0x10; + dev->shadow_regs[0x93] = 0x06; + dev->shadow_regs[0xa3] = 0x0d; + dev->shadow_regs[0xa4] = 0x5e; + dev->shadow_regs[0xa5] = 0x23; + dev->shadow_regs[0xb1] = 0x07; + dev->shadow_regs[0xc2] = 0x80; + dev->shadow_regs[0xc5] = 0x00; + dev->shadow_regs[0xca] = 0x00; + break; + } + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x8c] = 0x02; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0xb2] = 0x00; + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + + rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain, + dev->sensor->default_gain, dev->sensor->default_gain); + rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80); + + /* set grayscale scan */ + rts88xx_set_gray_scan (dev->shadow_regs); + + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x07; + + /* set vertical start/end positions */ + dev->shadow_regs[0x60] = 0x01; + dev->shadow_regs[0x61] = 0x00; + dev->shadow_regs[0x62] = 0x02; + dev->shadow_regs[0x63] = 0x00; + + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + + /* set horizontal start position */ + dev->shadow_regs[0x66] = 0x6a; /* 0x88 for X1200 */ + dev->shadow_regs[0x67] = 0x00; + /* set horizontal end position */ + dev->shadow_regs[0x6c] = 0xf2; /* 0x1410 for X1200 */ + dev->shadow_regs[0x6d] = 0x13; + /* set horizontal resolution */ + dev->shadow_regs[0x79] = 0x40; + dev->shadow_regs[0x7a] = 0x02; + /* Motor disable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x01; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x01; + + switch (dev->model.motor_type) + { + case A920_MOTOR: + case X1100_MOTOR: + /* step size range2 */ + dev->shadow_regs[0xc9] = 0x3b; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x01; + /* ? */ + dev->shadow_regs[0xe3] = 0x03; + break; + case X74_MOTOR: + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x00; + dev->shadow_regs[0xc8] = 0x04; + /* step size range2 */ + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x00; + /* motor curve stuff */ + dev->shadow_regs[0xe0] = 0x29; + dev->shadow_regs[0xe1] = 0x17; + dev->shadow_regs[0xe2] = 0x8f; + dev->shadow_regs[0xe3] = 0x06; + dev->shadow_regs[0xe4] = 0x61; + dev->shadow_regs[0xe5] = 0x16; + dev->shadow_regs[0xe6] = 0x64; + dev->shadow_regs[0xe7] = 0xb5; + dev->shadow_regs[0xe8] = 0x08; + dev->shadow_regs[0xe9] = 0x32; + dev->shadow_regs[0xec] = 0x0c; + dev->shadow_regs[0xef] = 0x08; + break; + } + + /* Stop the scanner */ + low_stop_mvmt (devnum); + + /* write regs out twice */ + dev->shadow_regs[0x32] = 0x00; + low_write_all_regs (devnum, dev->shadow_regs); + dev->shadow_regs[0x32] = 0x40; + low_write_all_regs (devnum, dev->shadow_regs); + + /* Start Scan */ + rts88xx_commit (devnum, dev->shadow_regs[0x2c]); + + /* Poll the available byte count until not 0 */ + got_line = SANE_FALSE; + while (!got_line) + { + cmd_size = 4; + low_usb_bulk_write (devnum, command4_block, &cmd_size); + cmd_size = 0x3; + low_usb_bulk_read (devnum, poll_result, &cmd_size); + if (! + (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) + { + /* if result != 00 00 00 we got data */ + got_line = SANE_TRUE; + } + } + + /* create buffer for scan data */ + buffer = calloc (2500, sizeof (char)); + if (buffer == NULL) + { + return SANE_FALSE; + } + + /* Tell the scanner to send the data */ + /* Write: 91 00 09 c4 */ + cmd_size = 4; + low_usb_bulk_write (devnum, command5_block, &cmd_size); + /* Read it */ + cmd_size = 0x09c4; + low_usb_bulk_read (devnum, buffer, &cmd_size); + + /* Reverse order of bytes in words of buffer */ + for (i = 0; i < 2500; i = i + 2) + { + temp_byte = *(buffer + i); + *(buffer + i) = *(buffer + i + 1); + *(buffer + i + 1) = temp_byte; + } + + /* check for home position */ + ret_val = low_is_home_line (dev, buffer); + + if (ret_val) + DBG (2, "sanei_lexmark_low_search_home_fwd: !!!HOME POSITION!!!\n"); + + /* free the buffer */ + free (buffer); + DBG (2, "sanei_lexmark_low_search_home_fwd: end.\n"); + + return ret_val; +} + +SANE_Bool +sanei_lexmark_low_search_home_bwd (Lexmark_Device * dev) +{ +/* This function must only be called if the scan head is past the home dot. + It could damage the scanner if not. + + This function tells the scanner to do a grayscale scan backwards with a + 300dpi resolution. It reads 2500 bytes of data between horizontal + co-ordinates 0x6a and 0x13f2. + + The scan is set to read between vertical co-ordinates from 0x0a to 0x0f46, + or 3900 lines. This equates to 13" at 300dpi, so we must stop the scan + before it bangs against the end. A line limit is set so that a maximum of + 0x0F3C (13"*300dpi) lines can be read. + + To read the scan data we create a buffer space large enough to hold 10 + lines of data. For each read we poll twice, ignoring the first poll. This + is required for timing. We repeat the double poll until there is data + available. The number of lines (or number of buffers in our buffer space) + is calculated from the size of the data available from the scanner. The + number of buffers is calculated as the space required to hold 1.5 times + the the size of the data available from the scanner. + + After data is read from the scanner each line is checked if it is on the + home dot. Lines are continued to be read until we are no longer on the home + dot. */ + + + SANE_Int devnum; + SANE_Status status; + int i, j; + SANE_Byte poll_result[3]; + SANE_Byte *buffer; + SANE_Byte *buffer_start; + SANE_Byte temp_byte; + + SANE_Int buffer_count = 0; + SANE_Int size_requested; + SANE_Int size_returned; + SANE_Int no_of_buffers; + SANE_Int buffer_limit = 0xF3C; + + SANE_Int high_byte, mid_byte, low_byte; + SANE_Int home_line_count; + SANE_Bool in_home_region; + + static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; + + static SANE_Byte command5_block[] = { 0x91, 0x00, 0xff, 0xc0 }; +#ifdef DEEP_DEBUG + FILE *img = NULL; +#endif + + size_t cmd_size; + SANE_Bool got_line; + + devnum = dev->devnum; + + DBG (2, "sanei_lexmark_low_search_home_bwd:\n"); + + /* set up registers */ + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x2c] = 0x03; + dev->shadow_regs[0x2d] = 0x45; + dev->shadow_regs[0x34] = 0x09; + dev->shadow_regs[0x35] = 0x09; + dev->shadow_regs[0x36] = 0x11; + dev->shadow_regs[0x37] = 0x11; + dev->shadow_regs[0x38] = 0x19; + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x93] = 0x06; + dev->shadow_regs[0x40] = 0x80; + /* important for detection of b/w transitions */ + dev->shadow_regs[0x75] = 0x00; + dev->shadow_regs[0x8b] = 0xff; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x2c] = 0x0f; + dev->shadow_regs[0x2d] = 0x51; + dev->shadow_regs[0x34] = 0x07; + dev->shadow_regs[0x35] = 0x07; + dev->shadow_regs[0x36] = 0x0f; + dev->shadow_regs[0x37] = 0x0f; + dev->shadow_regs[0x38] = 0x15; + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x34] = 0x09; + dev->shadow_regs[0x35] = 0x09; + dev->shadow_regs[0x36] = 0x11; + dev->shadow_regs[0x37] = 0x11; + dev->shadow_regs[0x38] = 0x19; + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x34] = 0x09; + dev->shadow_regs[0x35] = 0x09; + dev->shadow_regs[0x36] = 0x11; + dev->shadow_regs[0x37] = 0x11; + dev->shadow_regs[0x38] = 0x19; + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x2c] = 0x01; + dev->shadow_regs[0x2d] = 0x03; + dev->shadow_regs[0x34] = 0x07; + dev->shadow_regs[0x35] = 0x07; + dev->shadow_regs[0x36] = 0x0f; + dev->shadow_regs[0x37] = 0x0f; + dev->shadow_regs[0x38] = 0x15; + break; + + case X1200_USB2_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x34] = 0x09; + dev->shadow_regs[0x35] = 0x09; + dev->shadow_regs[0x36] = 0x11; + dev->shadow_regs[0x37] = 0x11; + dev->shadow_regs[0x38] = 0x19; + dev->shadow_regs[0x85] = 0x03; + dev->shadow_regs[0x93] = 0x06; + break; + } + rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain, + dev->sensor->default_gain, dev->sensor->default_gain); + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x8b] = 0xff; + dev->shadow_regs[0x8c] = 0x02; + dev->shadow_regs[0xb2] = 0x00; + + /* set calibration */ + rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80); + + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + /* set vertical start/end positions */ + dev->shadow_regs[0x60] = 0x0a; + dev->shadow_regs[0x61] = 0x00; + dev->shadow_regs[0x62] = 0x46; + dev->shadow_regs[0x63] = 0x0f; + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 2); + /* set horizontal start position */ + dev->shadow_regs[0x66] = 0x6a; /* 0x88 for X1200 */ + dev->shadow_regs[0x67] = 0x00; + /* set horizontal end position */ + dev->shadow_regs[0x6c] = 0xf2; /* 0x1410 for X1200, 13f2 for X1200/rev. 97 */ + dev->shadow_regs[0x6d] = 0x13; + /* set horizontal resolution */ + dev->shadow_regs[0x79] = 0x40; + dev->shadow_regs[0x7a] = 0x02; + + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x01; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + + switch (dev->model.motor_type) + { + case X74_MOTOR: + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x00; + dev->shadow_regs[0xc8] = 0x04; + /* step size range2 */ + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x00; + /* motor curve stuff */ + dev->shadow_regs[0xe0] = 0x29; + dev->shadow_regs[0xe1] = 0x17; + dev->shadow_regs[0xe2] = 0x8f; + dev->shadow_regs[0xe3] = 0x06; + dev->shadow_regs[0xe4] = 0x61; + dev->shadow_regs[0xe5] = 0x16; + dev->shadow_regs[0xe6] = 0x64; + dev->shadow_regs[0xe7] = 0xb5; + dev->shadow_regs[0xe8] = 0x08; + dev->shadow_regs[0xe9] = 0x32; + dev->shadow_regs[0xec] = 0x0c; + dev->shadow_regs[0xef] = 0x08; + break; + case A920_MOTOR: + case X1100_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x19; + /* step size range2 */ + dev->shadow_regs[0xc9] = 0x3a; + /* ? */ + dev->shadow_regs[0xca] = 0x08; + /* motor curve stuff */ + dev->shadow_regs[0xe0] = 0xe3; + dev->shadow_regs[0xe1] = 0x18; + dev->shadow_regs[0xe2] = 0x03; + dev->shadow_regs[0xe3] = 0x06; + dev->shadow_regs[0xe4] = 0x2b; + dev->shadow_regs[0xe5] = 0x17; + dev->shadow_regs[0xe6] = 0xdc; + dev->shadow_regs[0xe7] = 0xb3; + dev->shadow_regs[0xe8] = 0x07; + dev->shadow_regs[0xe9] = 0x1b; + dev->shadow_regs[0xec] = 0x07; + dev->shadow_regs[0xef] = 0x03; + break; + } + + /* Stop the scanner */ + low_stop_mvmt (devnum); + + /* write regs out twice */ + dev->shadow_regs[0x32] = 0x00; + low_write_all_regs (devnum, dev->shadow_regs); + dev->shadow_regs[0x32] = 0x40; + low_write_all_regs (devnum, dev->shadow_regs); + + /* Start Scan */ + status = rts88xx_commit (devnum, dev->shadow_regs[0x2c]); + + /* create buffer to hold up to 10 lines of scan data */ + buffer = calloc (10 * 2500, sizeof (char)); + if (buffer == NULL) + { + return SANE_FALSE; + } + + home_line_count = 0; + in_home_region = SANE_FALSE; + +#ifdef DEEP_DEBUG + img = fopen ("find_bwd.pnm", "wb"); + fprintf (img, "P5\n2500 100\n255\n"); +#endif + while (buffer_count < buffer_limit) + { + size_returned = 0; + got_line = SANE_FALSE; + while (!got_line) + { + /* always poll twice (needed for timing) - disregard 1st poll */ + cmd_size = 4; + status = low_usb_bulk_write (devnum, command4_block, &cmd_size); + if (status != SANE_STATUS_GOOD) + return SANE_FALSE; + cmd_size = 0x3; + status = low_usb_bulk_read (devnum, poll_result, &cmd_size); + if (status != SANE_STATUS_GOOD) + return SANE_FALSE; + cmd_size = 4; + status = low_usb_bulk_write (devnum, command4_block, &cmd_size); + if (status != SANE_STATUS_GOOD) + return SANE_FALSE; + cmd_size = 0x3; + status = low_usb_bulk_read (devnum, poll_result, &cmd_size); + if (status != SANE_STATUS_GOOD) + return SANE_FALSE; + if (! + (poll_result[0] == 0 && poll_result[1] == 0 + && poll_result[2] == 0)) + { + /* if result != 00 00 00 we got data */ + got_line = SANE_TRUE; + high_byte = poll_result[2] << 16; + mid_byte = poll_result[1] << 8; + low_byte = poll_result[0]; + size_returned = high_byte + mid_byte + low_byte; + } + } + + /*size_requested = size_returned;*/ + size_requested = 2500; + no_of_buffers = size_returned * 3; + no_of_buffers = no_of_buffers / 2500; + no_of_buffers = no_of_buffers >> 1; + /* force 1 buffer at a time to improve accuray, which slow downs search */ + no_of_buffers = 1; + + if (no_of_buffers < 1) + no_of_buffers = 1; + else if (no_of_buffers > 10) + no_of_buffers = 10; + buffer_count = buffer_count + no_of_buffers; + + size_requested = no_of_buffers * 2500; + + /* Tell the scanner to send the data */ + /* Write: 91 <size_requested> */ + command5_block[1] = (SANE_Byte) (size_requested >> 16); + command5_block[2] = (SANE_Byte) (size_requested >> 8); + command5_block[3] = (SANE_Byte) (size_requested & 0xFF); + + cmd_size = 4; + status = low_usb_bulk_write (devnum, command5_block, &cmd_size); + if (status != SANE_STATUS_GOOD) + return SANE_FALSE; + /* Read it */ + cmd_size = size_requested; + status = low_usb_bulk_read (devnum, buffer, &cmd_size); + if (status != SANE_STATUS_GOOD) + return SANE_FALSE; + for (i = 0; i < no_of_buffers; i++) + { + buffer_start = buffer + (i * 2500); + /* Reverse order of bytes in words of buffer */ + for (j = 0; j < 2500; j = j + 2) + { + temp_byte = *(buffer_start + j); + *(buffer_start + j) = *(buffer_start + j + 1); + *(buffer_start + j + 1) = temp_byte; + } +#ifdef DEEP_DEBUG + fwrite (buffer + (i * 2500), 2500, 1, img); +#endif + if (low_is_home_line (dev, buffer_start)) + { + home_line_count++; + if (home_line_count > 7) + in_home_region = SANE_TRUE; + } + if (in_home_region) + { + /* slow down scanning : on purpose backtracking */ + if (home_line_count) + sleep (1); + free (buffer); +#ifdef DEEP_DEBUG + fflush (img); + i = ftell (img) / 2500; + rewind (img); + DBG (2, "sanei_lexmark_low_search_home_bwd: offset=%d\n", i); + fprintf (img, "P5\n2500 %03d\n", i); + fclose (img); +#endif + low_stop_mvmt (devnum); + DBG (2, + "sanei_lexmark_low_search_home_bwd: in home region, end.\n"); + return SANE_TRUE; + } + } + } /* end while (buffer_count > buffer_limit); */ + free (buffer); +#ifdef DEEP_DEBUG + fflush (img); + i = ftell (img) / 2500; + rewind (img); + fprintf (img, "P5\n2500 %03d\n", i); + fclose (img); +#endif + low_stop_mvmt (devnum); + + DBG (2, "sanei_lexmark_low_search_home_bwd: end.\n"); + + return SANE_FALSE; +} + +SANE_Status +low_get_start_loc (SANE_Int resolution, SANE_Int * vert_start, + SANE_Int * hor_start, SANE_Int offset, + Lexmark_Device * dev) +{ + SANE_Int start_600; + + switch (dev->model.sensor_type) + { + case X1100_2C_SENSOR: + case X1200_USB2_SENSOR: + case A920_SENSOR: + case X1200_SENSOR: + start_600 = 195 - offset; + *hor_start = 0x68; + break; + case X1100_B2_SENSOR: + start_600 = 195 - offset; + switch (resolution) + { + case 75: + *hor_start = 0x68; + break; + case 150: + *hor_start = 0x68; + break; + case 300: + *hor_start = 0x6a; + break; + case 600: + *hor_start = 0x6b; + break; + case 1200: + *hor_start = 0x6b; + break; + default: + /* If we're here we have an invalid resolution */ + return SANE_STATUS_INVAL; + } + break; + case X74_SENSOR: + start_600 = 268 - offset; + switch (resolution) + { + case 75: + *hor_start = 0x48; + break; + case 150: + *hor_start = 0x48; + break; + case 300: + *hor_start = 0x4a; + break; + case 600: + *hor_start = 0x4b; + break; + default: + /* If we're here we have an invalid resolution */ + return SANE_STATUS_INVAL; + } + break; + } + /* Calculate vertical start distance at 600dpi */ + switch (resolution) + { + case 75: + *vert_start = start_600 / 8; + break; + case 150: + *vert_start = start_600 / 4; + break; + case 300: + *vert_start = start_600 / 2; + break; + case 600: + *vert_start = start_600; + break; + case 1200: + *vert_start = start_600 * 2; + break; + default: + /* If we're here we have an invalid resolution */ + return SANE_STATUS_INVAL; + } + + return SANE_STATUS_GOOD; +} + +void +low_set_scan_area (SANE_Int res, + SANE_Int tlx, + SANE_Int tly, + SANE_Int brx, + SANE_Int bry, + SANE_Int offset, + SANE_Bool half_step, + SANE_Byte * regs, Lexmark_Device * dev) +{ + + SANE_Int vert_start = 0; + SANE_Int hor_start = 0; + SANE_Int vert_end; + SANE_Int hor_end; + + low_get_start_loc (res, &vert_start, &hor_start, offset, dev); + + /* convert pixel height to vertical location coordinates */ + vert_end = vert_start + (bry * res) / 600; + vert_start += (tly * res) / 600; + + /* scan area size : for A920, 600 color scans are done at 1200 y dpi */ + /* this follow what was found in usb logs */ + if (half_step) + { + vert_end = vert_end * 2; + vert_start = vert_start * 2; + } + + /* set vertical start position registers */ + regs[0x60] = LOBYTE (vert_start); + regs[0x61] = HIBYTE (vert_start); + /* set vertical end position registers */ + regs[0x62] = LOBYTE (vert_end); + regs[0x63] = HIBYTE (vert_end); + + /* convert pixel width to horizontal location coordinates */ + + hor_end = hor_start + brx; + hor_start += tlx; + + regs[0x66] = LOBYTE (hor_start); + regs[0x67] = HIBYTE (hor_start); + /* set horizontal end position registers */ + regs[0x6c] = LOBYTE (hor_end); + regs[0x6d] = HIBYTE (hor_end); + + /* Debug */ + DBG (2, "low_set_scan_area: vert_start: %d (tly=%d)\n", vert_start, tly); + DBG (2, "low_set_scan_area: vert_end: %d\n", vert_end); + DBG (2, "low_set_scan_area: hor_start: %d\n", hor_start); + DBG (2, "low_set_scan_area: hor_end: %d\n", hor_end); + +} + +SANE_Int +sanei_lexmark_low_find_start_line (Lexmark_Device * dev) +{ + /* + This function scans forward 59 lines, reading 88 bytes per line from the + middle of the horizontal line: pixel 0xa84 to pixel 0x9d4. It scans with + the following parameters: + dir=fwd + mode=grayscale + h.res=300 dpi + v.res=600 dpi + hor. pixels = (0xa84 - 0x9d4)/2 = 0x58 = 88 + vert. pixels = 0x3e - 0x03 = 0x3b = 59 + data = 88x59=5192=0x1448 + + It assumes we are in the start dot, or just before it. We are reading + enough lines at 600dpi to read past the dot. We return the number of + entirely white lines read consecutively, so we know how far past the + dot we are. + + To find the number of consecutive white lines we do the following: + + Byte swap the order of the bytes in the buffer. + + Go through the buffer finding low and high values, which are computed by + comparing to the weighted average: + weighted_average = (lowest value + (highest value - lowest value)/4) + Low bytes are changed to 0xFF (white), higher of equal bytes are changed + to 0x00 (black),so that the buffer only contains white (0xFF) or black + (0x00) values. + + Next, we go through the buffer a line (88 bytes) at a time for 59 lines + to read the entire buffer. For each byte in a line we check if the + byte is black. If it is we increment the black byte counter. + + After each line we check the black byte counter. If it is greater than 0 + we increment the black line count and set the white line count to 0. If + there were no black bytes in the line we set the black line count to 0 + and increment the white line count. + + When all lines have been processed we return the white line count. + */ + + + int blackLineCount = 0; + int whiteLineCount = 0; + int blackByteCounter = 0; + unsigned char max_byte = 0; + unsigned char min_byte = 0xFF; + unsigned char weighted_average; + int i, j; +#ifdef DEEP_DEBUG + FILE *fdbg = NULL; +#endif + + SANE_Byte poll_result[3]; + SANE_Byte *buffer; + SANE_Byte temp_byte; + + static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; + + static SANE_Byte command5_block[] = { 0x91, 0x00, 0x14, 0x48 }; + + size_t cmd_size; + SANE_Bool got_line; + + DBG (2, "sanei_lexmark_low_find_start_line:\n"); + + + /* set up registers */ + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x2c] = 0x04; + dev->shadow_regs[0x2d] = 0x46; + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x0b; + dev->shadow_regs[0x37] = 0x0b; + dev->shadow_regs[0x38] = 0x11; + dev->shadow_regs[0x40] = 0x40; + rts88xx_set_gain (dev->shadow_regs, 6, 6, 6); + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x2c] = 0x0f; + dev->shadow_regs[0x2d] = 0x51; + dev->shadow_regs[0x34] = 0x0d; + dev->shadow_regs[0x35] = 0x0d; + dev->shadow_regs[0x36] = 0x1d; + dev->shadow_regs[0x37] = 0x1d; + dev->shadow_regs[0x38] = 0x29; + dev->shadow_regs[0x65] = 0x80; + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x93] = 0x06; + rts88xx_set_gain (dev->shadow_regs, 6, 6, 6); + break; + case X1100_2C_SENSOR: + rts88xx_set_gain (dev->shadow_regs, 10, 10, 10); + dev->shadow_regs[0x28] = 0xf5; + dev->shadow_regs[0x29] = 0xf7; + dev->shadow_regs[0x2a] = 0xf5; + dev->shadow_regs[0x2b] = 0x17; + + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x31] = 0x01; + dev->shadow_regs[0x34] = 0x11; + dev->shadow_regs[0x35] = 0x11; + dev->shadow_regs[0x36] = 0x21; + dev->shadow_regs[0x37] = 0x21; + dev->shadow_regs[0x38] = 0x31; + dev->shadow_regs[0x72] = 0x35; + dev->shadow_regs[0x74] = 0x4e; + dev->shadow_regs[0x85] = 0x02; + dev->shadow_regs[0x86] = 0x33; + dev->shadow_regs[0x87] = 0x0f; + dev->shadow_regs[0x88] = 0x24; + dev->shadow_regs[0x91] = 0x19; + dev->shadow_regs[0x92] = 0x20; + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + break; + case A920_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + dev->shadow_regs[0x34] = 0x11; + dev->shadow_regs[0x35] = 0x11; + dev->shadow_regs[0x36] = 0x21; + dev->shadow_regs[0x37] = 0x21; + dev->shadow_regs[0x38] = 0x31; + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x88] = 0x44; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + rts88xx_set_gain (dev->shadow_regs, 6, 6, 6); + break; + case X1200_SENSOR: + dev->shadow_regs[0x2c] = 0x01; + dev->shadow_regs[0x2d] = 0x03; + dev->shadow_regs[0x34] = 0x0d; + dev->shadow_regs[0x35] = 0x0d; + dev->shadow_regs[0x36] = 0x1d; + dev->shadow_regs[0x37] = 0x1d; + dev->shadow_regs[0x38] = 0x29; + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x00; + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + dev->shadow_regs[0x92] = 0x00; + rts88xx_set_gain (dev->shadow_regs, 10, 10, 10); + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x2c] = 0x01; + dev->shadow_regs[0x2d] = 0x03; + dev->shadow_regs[0x34] = 0x0d; + dev->shadow_regs[0x35] = 0x0d; + dev->shadow_regs[0x36] = 0x1d; + dev->shadow_regs[0x37] = 0x1d; + dev->shadow_regs[0x38] = 0x29; + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + rts88xx_set_gain (dev->shadow_regs, 10, 10, 10); + break; + } + + /* set offset to a safe value */ + rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80); + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x01; + /* set vertical start/end positions */ + dev->shadow_regs[0x60] = 0x03; + dev->shadow_regs[0x61] = 0x00; + dev->shadow_regs[0x62] = 0x3e; + dev->shadow_regs[0x63] = 0x00; + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + /* set horizontal start position */ + dev->shadow_regs[0x66] = 0xd4; /* 0xf2 for X1200 */ + dev->shadow_regs[0x67] = 0x09; + /* set horizontal end position */ + dev->shadow_regs[0x6c] = 0x84; /* 0xa2 for X1200 */ + dev->shadow_regs[0x6d] = 0x0a; + /* set horizontal resolution */ + dev->shadow_regs[0x79] = 0x40; + dev->shadow_regs[0x7a] = 0x02; + /* set for ? */ + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + + + + + + switch (dev->model.motor_type) + { + case X1100_MOTOR: + case A920_MOTOR: + /* set for ? */ + dev->shadow_regs[0xc5] = 0x22; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + /* step size range2 */ + dev->shadow_regs[0xc9] = 0x3b; + /* set for ? */ + dev->shadow_regs[0xca] = 0x1f; + dev->shadow_regs[0xe0] = 0xf7; + dev->shadow_regs[0xe1] = 0x16; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x87; + /* ? */ + dev->shadow_regs[0xe3] = 0x13; + dev->shadow_regs[0xe4] = 0x1b; + dev->shadow_regs[0xe5] = 0x16; + dev->shadow_regs[0xe6] = 0xdc; + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + dev->shadow_regs[0xe9] = 0x1b; + dev->shadow_regs[0xec] = 0x07; + dev->shadow_regs[0xef] = 0x03; + break; + case X74_MOTOR: + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x22; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0b; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x1f; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x2f; + dev->shadow_regs[0xe1] = 0x11; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x9f; + /* ? */ + dev->shadow_regs[0xe3] = 0x0f; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0xcb; + + dev->shadow_regs[0xe5] = 0x10; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x64; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x32; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x0c; + /* bounds of movement range4 -only for 75dpi grayscale */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x08; + break; + } + + + /* Stop the scanner */ + low_stop_mvmt (dev->devnum); + + /* write regs out twice */ + dev->shadow_regs[0x32] = 0x00; + low_write_all_regs (dev->devnum, dev->shadow_regs); + dev->shadow_regs[0x32] = 0x40; + low_write_all_regs (dev->devnum, dev->shadow_regs); + + /* Start Scan */ + rts88xx_commit (dev->devnum, dev->shadow_regs[0x2c]); + + /* Poll the available byte count until not 0 */ + got_line = SANE_FALSE; + while (!got_line) + { + cmd_size = 4; + low_usb_bulk_write (dev->devnum, command4_block, &cmd_size); + cmd_size = 0x3; + low_usb_bulk_read (dev->devnum, poll_result, &cmd_size); + if (! + (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) + { + /* if result != 00 00 00 we got data */ + got_line = SANE_TRUE; + } +#ifdef FAKE_USB + got_line = SANE_TRUE; +#endif + } + + + /* create buffer for scan data */ + buffer = calloc (5192, sizeof (char)); + if (buffer == NULL) + { + return -1; + } + + /* Tell the scanner to send the data */ + /* Write: 91 00 14 48 */ + cmd_size = 4; + low_usb_bulk_write (dev->devnum, command5_block, &cmd_size); + /* Read it */ + cmd_size = 0x1448; + low_usb_bulk_read (dev->devnum, buffer, &cmd_size); + + /* Stop the scanner */ + low_stop_mvmt (dev->devnum); + + + /* Reverse order of bytes in words of buffer */ + for (i = 0; i < 5192; i = i + 2) + { + temp_byte = *(buffer + i); + *(buffer + i) = *(buffer + i + 1); + *(buffer + i + 1) = temp_byte; + } + +#ifdef DEEP_DEBUG + fdbg = fopen ("find_start.pnm", "wb"); + if (fdbg != NULL) + { + fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59); + fwrite (buffer, 5192, 1, fdbg); + fclose (fdbg); + } +#endif + + /* Find the max and the min */ + for (i = 0; i < 5192; i++) + { + if (*(buffer + i) > max_byte) + max_byte = *(buffer + i); + if (*(buffer + i) < min_byte) + min_byte = *(buffer + i); + } + + weighted_average = min_byte + ((max_byte - min_byte) / 4); + + /* Set bytes as black (0x00) or white (0xFF) */ + for (i = 0; i < 5192; i++) + { + if (*(buffer + i) > weighted_average) + *(buffer + i) = 0xFF; + else + *(buffer + i) = 0x00; + } + +#ifdef DEEP_DEBUG + fdbg = fopen ("find_start_after.pnm", "wb"); + if (fdbg != NULL) + { + fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59); + fwrite (buffer, 5192, 1, fdbg); + fclose (fdbg); + } +#endif + + /* Go through 59 lines */ + for (j = 0; j < 59; j++) + { + blackByteCounter = 0; + /* Go through 88 bytes per line */ + for (i = 0; i < 88; i++) + { + /* Is byte black? */ + if (*(buffer + (j * 88) + i) == 0) + { + blackByteCounter++; + } + } /* end for line */ + if (blackByteCounter > 0) + { + /* This was a black line */ + blackLineCount++; + whiteLineCount = 0; + } + else + { + /* This is a white line */ + whiteLineCount++; + blackLineCount = 0; + } + } /* end for buffer */ + + free (buffer); + /* Stop the scanner. + This is needed to get the right distance to the scanning area */ + if (dev->model.sensor_type == X74_SENSOR) + low_stop_mvmt (dev->devnum); + + DBG (2, "sanei_lexmark_low_find_start_line: end.\n"); + return whiteLineCount; +} + + +SANE_Status +sanei_lexmark_low_set_scan_regs (Lexmark_Device * dev, SANE_Int resolution, + SANE_Int offset, SANE_Bool calibrated) +{ + SANE_Bool isColourScan; + + DBG (2, "sanei_lexmark_low_set_scan_regs:\n"); + + DBG (7, "sanei_lexmark_low_set_scan_regs: resolution=%d DPI\n", resolution); + + /* colour mode */ + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + isColourScan = SANE_TRUE; + else + isColourScan = SANE_FALSE; + + /* set up registers */ + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x2c] = 0x03; + dev->shadow_regs[0x2d] = 0x45; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x2c] = 0x0f; + dev->shadow_regs[0x2d] = 0x51; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + break; + case A920_SENSOR: + dev->shadow_regs[0x2c] = 0x0d; + dev->shadow_regs[0x2d] = 0x4f; + break; + case X1200_SENSOR: + dev->shadow_regs[0x2c] = 0x01; + dev->shadow_regs[0x2d] = 0x03; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x2c] = 0x01; + dev->shadow_regs[0x2d] = 0x03; + break; + } + + low_set_scan_area (resolution, + dev->val[OPT_TL_X].w, + dev->val[OPT_TL_Y].w, + dev->val[OPT_BR_X].w, + dev->val[OPT_BR_Y].w, + offset, + (dev->model.motor_type == A920_MOTOR + || dev->model.motor_type == X74_MOTOR) && isColourScan + && (resolution == 600), dev->shadow_regs, dev); + + /* may be we could use a sensor descriptor that would held the max horiz dpi */ + if (dev->val[OPT_RESOLUTION].w < 600) + dev->shadow_regs[0x7a] = 600 / dev->val[OPT_RESOLUTION].w; + else + dev->shadow_regs[0x7a] = 1; + + /* 75dpi x 75dpi */ + if (resolution == 75) + { + DBG (5, "sanei_lexmark_low_set_scan_regs(): 75 DPI resolution\n"); + + if (isColourScan) + { + switch (dev->model.sensor_type) + { + case X74_SENSOR: + + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x36] = 0x03; + dev->shadow_regs[0x38] = 0x03; + + dev->shadow_regs[0x79] = 0x08; + + dev->shadow_regs[0x80] = 0x0d; + dev->shadow_regs[0x81] = 0x0e; + dev->shadow_regs[0x82] = 0x02; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06;; + + break; + + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x36] = 0x05; + dev->shadow_regs[0x38] = 0x05; + + dev->shadow_regs[0x80] = 0x0c; + dev->shadow_regs[0x81] = 0x0c; + dev->shadow_regs[0x82] = 0x09; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x8c; + dev->shadow_regs[0x92] = 0x40; + dev->shadow_regs[0x93] = 0x06; + break; + + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x03; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x38] = 0x03; + + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x02; + dev->shadow_regs[0x82] = 0x03; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x38] = 0x03; + + dev->shadow_regs[0x80] = 0x07; + dev->shadow_regs[0x81] = 0x0f; + dev->shadow_regs[0x82] = 0x03; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x44; + + dev->shadow_regs[0x91] = 0x60; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + break; + + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x36] = 0x03; + dev->shadow_regs[0x38] = 0x01; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x80] = 0x08; + dev->shadow_regs[0x81] = 0x02; + dev->shadow_regs[0x82] = 0x0d; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x1e; + dev->shadow_regs[0x87] = 0x39; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + /* dev->shadow_regs[0x92] = 0x92; */ + dev->shadow_regs[0x93] = 0x06; + break; + + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x36] = 0x05; + dev->shadow_regs[0x38] = 0x04; + + dev->shadow_regs[0x80] = 0x01; + dev->shadow_regs[0x81] = 0x0a; + dev->shadow_regs[0x82] = 0x0b; + break; + } + + switch (dev->model.motor_type) + { + case X74_MOTOR: + dev->shadow_regs[0xc2] = 0x80; + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x0c; + dev->shadow_regs[0xc6] = 0x0b; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x01; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x1b; + dev->shadow_regs[0xe1] = 0x0a; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x4f; + /* ? */ + dev->shadow_regs[0xe3] = 0x01; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0xb3; + + dev->shadow_regs[0xe5] = 0x09; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0d; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xe5; + dev->shadow_regs[0xe8] = 0x02; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0xa0; + dev->shadow_regs[0xeb] = 0x01; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* bounds of movement range4 */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + case A920_MOTOR: + case X1100_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x0a; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x2b; + dev->shadow_regs[0xe1] = 0x0a; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x7f; + /* ? */ + dev->shadow_regs[0xe3] = 0x01; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0xbb; + dev->shadow_regs[0xe5] = 0x09; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0e; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x2b; + dev->shadow_regs[0xe8] = 0x03; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0xa0; + dev->shadow_regs[0xeb] = 0x01; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + } + + /* set colour scan */ + dev->shadow_regs[0x2f] = 0x11; + + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x37] = 0x01; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x83; + } + else /* 75 dpi gray */ + { + switch (dev->model.sensor_type) + { + case X74_SENSOR: + + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x03; + dev->shadow_regs[0x39] = 0x0f; + + dev->shadow_regs[0x40] = 0x80; + + dev->shadow_regs[0x79] = 0x08; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x8c] = 0x02; + dev->shadow_regs[0x8d] = 0x01; + dev->shadow_regs[0x8e] = 0x60; + dev->shadow_regs[0x8f] = 0x80; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + + break; + + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x35] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x37] = 0x04; + dev->shadow_regs[0x38] = 0x06; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x03; /* these are half of B2 sensor */ + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x03; + + dev->shadow_regs[0x85] = 0x0d; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x45; + + dev->shadow_regs[0x91] = 0x60; + dev->shadow_regs[0x92] = 0x8d; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x02; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + + dev->shadow_regs[0x92] = 0x00; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x02; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + + dev->shadow_regs[0x92] = 0x00; + break; + } + switch (dev->model.motor_type) + { + case X74_MOTOR: + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x0a; + dev->shadow_regs[0xc6] = 0x0b; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x01; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x07; + dev->shadow_regs[0xe1] = 0x18; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xe7; + /* ? */ + dev->shadow_regs[0xe3] = 0x03; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0xe7; + dev->shadow_regs[0xe5] = 0x14; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x64; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xcb; + dev->shadow_regs[0xe8] = 0x08; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x32; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0xe3; + dev->shadow_regs[0xeb] = 0x04; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x0c; + /* bounds of movement range4 */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x08; + break; + case A920_MOTOR: + case X1100_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x10; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xca] = 0x01; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x4d; + dev->shadow_regs[0xe1] = 0x1c; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x71; + /* ? */ + dev->shadow_regs[0xe3] = 0x02; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x6d; + dev->shadow_regs[0xe5] = 0x15; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0xdc; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xad; + dev->shadow_regs[0xe8] = 0x07; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x1b; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0xe1; + dev->shadow_regs[0xeb] = 0x03; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x07; + /* bounds of movement range4 -only for 75dpi grayscale */ + dev->shadow_regs[0xed] = 0xc2; + dev->shadow_regs[0xee] = 0x02; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x03; + break; + } + + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + + /* set ? only for colour? */ + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x00; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + + } + + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x0f; + + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + + /* set horizontal resolution */ + if (dev->model.sensor_type != X1200_SENSOR) + dev->shadow_regs[0x79] = 0x08; + + } + + /* 150dpi x 150dpi */ + if (resolution == 150) + { + DBG (5, "sanei_lexmark_low_set_scan_regs(): 150 DPI resolution\n"); + + if (isColourScan) + { + + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x34] = 0x08; + dev->shadow_regs[0x36] = 0x06; + dev->shadow_regs[0x38] = 0x05; + dev->shadow_regs[0x39] = 0x07; + + /* resolution divisor */ + dev->shadow_regs[0x79] = 0x08; + + dev->shadow_regs[0x80] = 0x0a; + dev->shadow_regs[0x81] = 0x0c; + dev->shadow_regs[0x82] = 0x04; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x0b; + dev->shadow_regs[0x36] = 0x0b; + dev->shadow_regs[0x38] = 0x0a; + + dev->shadow_regs[0x80] = 0x05; + dev->shadow_regs[0x81] = 0x05; + dev->shadow_regs[0x82] = 0x0a; + + dev->shadow_regs[0x85] = 0x83; + dev->shadow_regs[0x86] = 0x7e; + dev->shadow_regs[0x87] = 0xad; + dev->shadow_regs[0x88] = 0x35; + + dev->shadow_regs[0x91] = 0xfe; + dev->shadow_regs[0x92] = 0xdf; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x36] = 0x07; + dev->shadow_regs[0x38] = 0x05; + + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x02; + dev->shadow_regs[0x82] = 0x06; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x03; + dev->shadow_regs[0x36] = 0x08; + dev->shadow_regs[0x38] = 0x05; + + dev->shadow_regs[0x80] = 0x0e; + dev->shadow_regs[0x81] = 0x07; + dev->shadow_regs[0x82] = 0x02; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x04; + + dev->shadow_regs[0x91] = 0xe0; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x36] = 0x05; + dev->shadow_regs[0x38] = 0x02; + /* data compression + dev->shadow_regs[0x40] = 0x90; + dev->shadow_regs[0x50] = 0x20; */ + /* no data compression */ + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x07; + dev->shadow_regs[0x82] = 0x0b; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x1e; + dev->shadow_regs[0x87] = 0x39; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x92] = 0x92; + + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x36] = 0x05; + dev->shadow_regs[0x38] = 0x02; + + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x07; + dev->shadow_regs[0x82] = 0x0b; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x1e; + dev->shadow_regs[0x87] = 0x39; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x92] = 0x92; + break; + } /* switch */ + switch (dev->model.motor_type) + { + case X74_MOTOR: + dev->shadow_regs[0xc2] = 0x80; + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + + dev->shadow_regs[0xc5] = 0x0e; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0b; + dev->shadow_regs[0xc7] = 0x00; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x03; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x41; + dev->shadow_regs[0xe1] = 0x09; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x89; + /* ? */ + dev->shadow_regs[0xe3] = 0x02; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x0d; + + dev->shadow_regs[0xe5] = 0x09; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0d; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xe8; + dev->shadow_regs[0xe8] = 0x02; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* bounds of movement range4 */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + case X1100_MOTOR: + case A920_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x0e; + /* ? */ + dev->shadow_regs[0xc9] = 0x3a; + dev->shadow_regs[0xca] = 0x03; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x61; + dev->shadow_regs[0xe1] = 0x0a; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xed; + /* ? */ + dev->shadow_regs[0xe3] = 0x02; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x29; + dev->shadow_regs[0xe5] = 0x0a; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0e; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x29; + dev->shadow_regs[0xe8] = 0x03; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + } + /* set colour scan */ + dev->shadow_regs[0x2f] = 0x11; + + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x37] = 0x01; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x83; + + } /* if (isColourScan) */ + else + { + switch (dev->model.sensor_type) + { + case X74_SENSOR: + + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x35] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x37] = 0x04; + dev->shadow_regs[0x38] = 0x06; + dev->shadow_regs[0x39] = 0x07; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0x40] = 0x40; + + /* resolution divisor */ + dev->shadow_regs[0x79] = 0x08; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x35] = 0x04; + dev->shadow_regs[0x36] = 0x07; + dev->shadow_regs[0x37] = 0x07; + dev->shadow_regs[0x38] = 0x0a; + + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x35] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x37] = 0x04; + dev->shadow_regs[0x38] = 0x05; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x35] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x37] = 0x04; + dev->shadow_regs[0x38] = 0x05; + + dev->shadow_regs[0x85] = 0x0d; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x45; + + dev->shadow_regs[0x91] = 0x60; + dev->shadow_regs[0x92] = 0x8d; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x03; + + /* dev->shadow_regs[0x40] = 0x90; + dev->shadow_regs[0x50] = 0x20; */ + /* no data compression */ + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + + dev->shadow_regs[0x92] = 0x92; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x01; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x02; + dev->shadow_regs[0x37] = 0x02; + dev->shadow_regs[0x38] = 0x03; + + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x79] = 0x20; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + + dev->shadow_regs[0x92] = 0x92; + break; + } /* switch */ + switch (dev->model.motor_type) + { + case X74_MOTOR: + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x14; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0b; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x01; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x09; + dev->shadow_regs[0xe1] = 0x18; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xe9; + /* ? */ + dev->shadow_regs[0xe3] = 0x03; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x79; + + dev->shadow_regs[0xe5] = 0x16; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x64; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xcd; + dev->shadow_regs[0xe8] = 0x08; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x32; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0xe5; + dev->shadow_regs[0xeb] = 0x04; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x0c; + /* bounds of movement range4 */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x08; + break; + case X1100_MOTOR: + case A920_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x16; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + /* ? */ + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xca] = 0x01; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0xdd; + dev->shadow_regs[0xe1] = 0x18; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x01; + /* ? */ + dev->shadow_regs[0xe3] = 0x03; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x6d; + dev->shadow_regs[0xe5] = 0x15; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0xdc; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xad; + dev->shadow_regs[0xe8] = 0x07; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x1b; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0xe1; + dev->shadow_regs[0xeb] = 0x03; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x07; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x03; + break; + } + + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x07; + /* set ? only for colour? */ + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x00; + + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + } /* else (greyscale) */ + + + + + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + + /* hum, horizontal resolution different for X1200 ? */ + /* if (dev->model.sensor_type != X1200_SENSOR) + dev->shadow_regs[0x79] = 0x20; */ + + } + + /*300dpi x 300dpi */ + if (resolution == 300) + { + DBG (5, "sanei_lexmark_low_set_scan_regs(): 300 DPI resolution\n"); + + if (isColourScan) + { + + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x34] = 0x08; + dev->shadow_regs[0x36] = 0x06; + dev->shadow_regs[0x38] = 0x05; + dev->shadow_regs[0x39] = 0x07; + + dev->shadow_regs[0x80] = 0x08; + dev->shadow_regs[0x81] = 0x0a; + dev->shadow_regs[0x82] = 0x03; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x15; + dev->shadow_regs[0x36] = 0x15; + dev->shadow_regs[0x38] = 0x14; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + + dev->shadow_regs[0x80] = 0x0a; + dev->shadow_regs[0x81] = 0x0a; + dev->shadow_regs[0x82] = 0x06; + + dev->shadow_regs[0x85] = 0x83; + dev->shadow_regs[0x86] = 0x7e; + dev->shadow_regs[0x87] = 0xad; + dev->shadow_regs[0x88] = 0x35; + + dev->shadow_regs[0x91] = 0xfe; + dev->shadow_regs[0x92] = 0xdf; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x08; + dev->shadow_regs[0x36] = 0x0d; + dev->shadow_regs[0x38] = 0x09; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + + dev->shadow_regs[0x80] = 0x0e; + dev->shadow_regs[0x81] = 0x04; + dev->shadow_regs[0x82] = 0x0a; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x06; + dev->shadow_regs[0x36] = 0x10; + dev->shadow_regs[0x38] = 0x09; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + + dev->shadow_regs[0x80] = 0x0c; + dev->shadow_regs[0x81] = 0x02; + dev->shadow_regs[0x82] = 0x04; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x04; + + dev->shadow_regs[0x91] = 0xe0; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x07; + dev->shadow_regs[0x36] = 0x09; + dev->shadow_regs[0x38] = 0x04; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + + /* data compression + dev->shadow_regs[0x40] = 0x90; + dev->shadow_regs[0x50] = 0x20; */ + /* no data compression */ + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x0e; + dev->shadow_regs[0x82] = 0x06; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x07; + dev->shadow_regs[0x36] = 0x09; + dev->shadow_regs[0x38] = 0x04; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x0e; + dev->shadow_regs[0x82] = 0x06; + break; + } + switch (dev->model.motor_type) + { + case X74_MOTOR: + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x12; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x0f; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x5d; + dev->shadow_regs[0xe1] = 0x05; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xed; + /* ? */ + dev->shadow_regs[0xe3] = 0x02; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x29; + dev->shadow_regs[0xe5] = 0x05; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0d; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* bounds of movement range4 -only for 75dpi grayscale */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + case A920_MOTOR: + case X1100_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x17; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + /* ? */ + dev->shadow_regs[0xc9] = 0x3a; + dev->shadow_regs[0xca] = 0x0a; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x75; + dev->shadow_regs[0xe1] = 0x0a; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xdd; + /* ? */ + dev->shadow_regs[0xe3] = 0x05; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x59; + dev->shadow_regs[0xe5] = 0x0a; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0e; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + } + + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x37] = 0x01; + + /* set colour scan */ + dev->shadow_regs[0x2f] = 0x11; + + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x83; + + } + else /* greyscale */ + { + + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x35] = 0x04; + dev->shadow_regs[0x36] = 0x08; + dev->shadow_regs[0x37] = 0x08; + dev->shadow_regs[0x38] = 0x0c; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x08; + dev->shadow_regs[0x35] = 0x08; + dev->shadow_regs[0x36] = 0x0f; + dev->shadow_regs[0x37] = 0x0f; + dev->shadow_regs[0x38] = 0x16; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x04; + dev->shadow_regs[0x35] = 0x04; + dev->shadow_regs[0x36] = 0x07; + dev->shadow_regs[0x37] = 0x07; + dev->shadow_regs[0x38] = 0x0a; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x03; + dev->shadow_regs[0x35] = 0x03; + dev->shadow_regs[0x36] = 0x06; + dev->shadow_regs[0x37] = 0x06; + dev->shadow_regs[0x38] = 0x09; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x04; + + dev->shadow_regs[0x91] = 0xe0; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x35] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x37] = 0x04; + dev->shadow_regs[0x38] = 0x06; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x02; + dev->shadow_regs[0x35] = 0x02; + dev->shadow_regs[0x36] = 0x04; + dev->shadow_regs[0x37] = 0x04; + dev->shadow_regs[0x38] = 0x06; + break; + } + switch (dev->model.motor_type) + { + case X74_MOTOR: + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x1c; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0b; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x05; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x29; + dev->shadow_regs[0xe1] = 0x17; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x8f; + /* ? */ + dev->shadow_regs[0xe3] = 0x06; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x61; + + dev->shadow_regs[0xe5] = 0x16; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x64; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xb5; + dev->shadow_regs[0xe8] = 0x08; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x32; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x0c; + /* bounds of movement range4 -only for 75dpi grayscale */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x08; + break; + case A920_MOTOR: + case X1100_MOTOR: + /* ? */ + dev->shadow_regs[0xc5] = 0x19; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + /* ? */ + dev->shadow_regs[0xc9] = 0x3a; + dev->shadow_regs[0xca] = 0x08; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0xe3; + dev->shadow_regs[0xe1] = 0x18; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x03; + /* ? */ + dev->shadow_regs[0xe3] = 0x06; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x2b; + dev->shadow_regs[0xe5] = 0x17; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0xdc; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0xb3; + dev->shadow_regs[0xe8] = 0x07; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x1b; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x07; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x03; + break; + } /* switch motortype */ + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + + /* set ? only for colour? */ + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x00; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + + + } /* else (gray) */ + + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + /* set horizontal resolution */ + dev->shadow_regs[0x79] = 0x20; + } + + /* 600dpi x 600dpi */ + if (resolution == 600) + { + DBG (5, "sanei_lexmark_low_set_scan_regs(): 600 DPI resolution\n"); + + + + if (isColourScan) + { + /* 600 dpi color doesn't work for X74 yet */ + if (dev->model.sensor_type == X74_SENSOR) + return SANE_STATUS_INVAL; + + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x34] = 0x10; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x0c; + dev->shadow_regs[0x37] = 0x01; + dev->shadow_regs[0x38] = 0x09; + + dev->shadow_regs[0x80] = 0x02; + dev->shadow_regs[0x81] = 0x08; + dev->shadow_regs[0x82] = 0x08; + + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + + /*dev->shadow_regs[0x34] = 0x08; + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x36] = 0x06; + dev->shadow_regs[0x37] = 0x01; + dev->shadow_regs[0x38] = 0x05; + + + dev->shadow_regs[0x80] = 0x09; + dev->shadow_regs[0x81] = 0x0c; + dev->shadow_regs[0x82] = 0x04; + + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; */ + + + + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x15; + dev->shadow_regs[0x36] = 0x15; + dev->shadow_regs[0x38] = 0x14; + + dev->shadow_regs[0x80] = 0x02; + dev->shadow_regs[0x81] = 0x02; + dev->shadow_regs[0x82] = 0x08; + + dev->shadow_regs[0x85] = 0x83; + dev->shadow_regs[0x86] = 0x7e; + dev->shadow_regs[0x87] = 0xad; + dev->shadow_regs[0x88] = 0x35; + + dev->shadow_regs[0x91] = 0xfe; + dev->shadow_regs[0x92] = 0xdf; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x08; + dev->shadow_regs[0x36] = 0x0d; + dev->shadow_regs[0x38] = 0x09; + + dev->shadow_regs[0x80] = 0x0e; + dev->shadow_regs[0x81] = 0x02; + dev->shadow_regs[0x82] = 0x0a; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x06; + dev->shadow_regs[0x36] = 0x0f; + dev->shadow_regs[0x38] = 0x09; + + dev->shadow_regs[0x79] = 0x40; + + dev->shadow_regs[0x80] = 0x0e; + dev->shadow_regs[0x81] = 0x0e; + dev->shadow_regs[0x82] = 0x00; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x04; + + dev->shadow_regs[0x91] = 0x60; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x07; + dev->shadow_regs[0x36] = 0x0a; + dev->shadow_regs[0x38] = 0x04; + + /* data compression + dev->shadow_regs[0x40] = 0x90; + dev->shadow_regs[0x50] = 0x20; */ + + /* no data compression */ + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x80] = 0x02; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x06; + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x0d; + dev->shadow_regs[0x36] = 0x13; + dev->shadow_regs[0x38] = 0x10; + + dev->shadow_regs[0x80] = 0x04; + dev->shadow_regs[0x81] = 0x0e; + dev->shadow_regs[0x82] = 0x08; + + dev->shadow_regs[0x85] = 0x02; + dev->shadow_regs[0x86] = 0x3b; + dev->shadow_regs[0x87] = 0x0f; + dev->shadow_regs[0x88] = 0x24; + + dev->shadow_regs[0x91] = 0x19; + dev->shadow_regs[0x92] = 0x30; + dev->shadow_regs[0x93] = 0x0e; + dev->shadow_regs[0xc5] = 0x17; + dev->shadow_regs[0xc6] = 0x09; + dev->shadow_regs[0xca] = 0x0a; + break; + } + switch (dev->model.motor_type) + { + case X74_MOTOR: + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x21; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x20; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x00; + dev->shadow_regs[0xe1] = 0x00; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xbf; + /* ? */ + dev->shadow_regs[0xe3] = 0x05; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x00; + dev->shadow_regs[0xe5] = 0x00; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0d; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* bounds of movement range4 -only for 75dpi grayscale */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + case A920_MOTOR: + case X1100_MOTOR: + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x86; + /* ? */ + dev->shadow_regs[0xc5] = 0x27; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0c; + /* ? */ + dev->shadow_regs[0xc9] = 0x3a; + dev->shadow_regs[0xca] = 0x1a; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x57; + dev->shadow_regs[0xe1] = 0x0a; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xbf; + /* ? */ + dev->shadow_regs[0xe3] = 0x05; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x3b; + dev->shadow_regs[0xe5] = 0x0a; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0e; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + break; + } + /* set colour scan */ + dev->shadow_regs[0x2f] = 0x11; + + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x37] = 0x01; + + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x03; + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 2); + + + } + else + { + switch (dev->model.sensor_type) + { + case X74_SENSOR: + dev->shadow_regs[0x2c] = 0x04; + dev->shadow_regs[0x2d] = 0x46; + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x0b; + dev->shadow_regs[0x37] = 0x0b; + dev->shadow_regs[0x38] = 0x11; + dev->shadow_regs[0x40] = 0x40; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_B2_SENSOR: + dev->shadow_regs[0x34] = 0x11; + dev->shadow_regs[0x35] = 0x11; + dev->shadow_regs[0x36] = 0x21; + dev->shadow_regs[0x37] = 0x21; + dev->shadow_regs[0x38] = 0x31; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case X1100_2C_SENSOR: + dev->shadow_regs[0x34] = 0x07; + dev->shadow_regs[0x35] = 0x07; + dev->shadow_regs[0x36] = 0x0d; + dev->shadow_regs[0x37] = 0x0d; + dev->shadow_regs[0x38] = 0x13; + + dev->shadow_regs[0x85] = 0x20; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + break; + case A920_SENSOR: + dev->shadow_regs[0x34] = 0x05; + dev->shadow_regs[0x35] = 0x05; + dev->shadow_regs[0x36] = 0x0b; + dev->shadow_regs[0x37] = 0x0b; + dev->shadow_regs[0x38] = 0x11; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x04; + + dev->shadow_regs[0x91] = 0xe0; + dev->shadow_regs[0x92] = 0x85; + dev->shadow_regs[0x93] = 0x0e; + break; + case X1200_SENSOR: + dev->shadow_regs[0x34] = 0x03; + dev->shadow_regs[0x35] = 0x03; + dev->shadow_regs[0x36] = 0x07; + dev->shadow_regs[0x37] = 0x07; + dev->shadow_regs[0x38] = 0x0b; + + /* data compression + dev->shadow_regs[0x40] = 0x90; + dev->shadow_regs[0x50] = 0x20; */ + /* no data compression */ + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + + dev->shadow_regs[0x92] = 0x00; + + break; + case X1200_USB2_SENSOR: + dev->shadow_regs[0x34] = 0x03; + dev->shadow_regs[0x35] = 0x03; + dev->shadow_regs[0x36] = 0x07; + dev->shadow_regs[0x37] = 0x07; + dev->shadow_regs[0x38] = 0x0b; + + dev->shadow_regs[0x40] = 0x80; + dev->shadow_regs[0x50] = 0x00; + + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0xff; + dev->shadow_regs[0x88] = 0x02; + + dev->shadow_regs[0x92] = 0x00; + break; + } + switch (dev->model.motor_type) + { + case X74_MOTOR: + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + /* ? */ + dev->shadow_regs[0xc4] = 0x20; + dev->shadow_regs[0xc5] = 0x22; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0b; + + dev->shadow_regs[0xc8] = 0x04; + dev->shadow_regs[0xc9] = 0x39; + dev->shadow_regs[0xca] = 0x1f; + + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x2f; + dev->shadow_regs[0xe1] = 0x11; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x9f; + /* ? */ + dev->shadow_regs[0xe3] = 0x0f; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0xcb; + + dev->shadow_regs[0xe5] = 0x10; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x64; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x32; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x0c; + /* bounds of movement range4 -only for 75dpi grayscale */ + dev->shadow_regs[0xed] = 0x00; + dev->shadow_regs[0xee] = 0x00; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x08; + break; + case X1100_MOTOR: + case A920_MOTOR: + /* set ? only for colour? */ + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x00; + /* ? */ + dev->shadow_regs[0xc5] = 0x22; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + /* ? */ + dev->shadow_regs[0xc9] = 0x3b; + dev->shadow_regs[0xca] = 0x1f; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0xf7; + dev->shadow_regs[0xe1] = 0x16; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x87; + /* ? */ + dev->shadow_regs[0xe3] = 0x13; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x1b; + dev->shadow_regs[0xe5] = 0x16; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0xdc; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x1b; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x07; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x03; + break; + } + + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x01; + + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 1); + + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + } /* else (grayscale) */ + + /* set horizontal resolution */ + dev->shadow_regs[0x79] = 0x40; + + } + /*600dpi x 1200dpi */ + if (resolution == 1200) + { + DBG (5, "sanei_lexmark_low_set_scan_regs(): 1200 DPI resolution\n"); + + /* 1200 dpi doesn't work for X74 yet */ + if (dev->model.sensor_type == X74_SENSOR) + return SANE_STATUS_INVAL; + + if (isColourScan) + { + /* set colour scan */ + dev->shadow_regs[0x2f] = 0x11; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x01; + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 2); + + if (dev->model.sensor_type == X1100_B2_SENSOR) + { + /* set ? */ + dev->shadow_regs[0x34] = 0x29; + dev->shadow_regs[0x36] = 0x29; + dev->shadow_regs[0x38] = 0x28; + /* set ? */ + dev->shadow_regs[0x80] = 0x04; + dev->shadow_regs[0x81] = 0x04; + dev->shadow_regs[0x82] = 0x08; + dev->shadow_regs[0x85] = 0x83; + dev->shadow_regs[0x86] = 0x7e; + dev->shadow_regs[0x87] = 0xad; + dev->shadow_regs[0x88] = 0x35; + dev->shadow_regs[0x91] = 0xfe; + dev->shadow_regs[0x92] = 0xdf; + } + else + { /* A920 case */ + dev->shadow_regs[0x34] = 0x0c; + dev->shadow_regs[0x36] = 0x1e; + dev->shadow_regs[0x38] = 0x10; + + dev->shadow_regs[0x80] = 0x0c; + dev->shadow_regs[0x81] = 0x08; + dev->shadow_regs[0x82] = 0x0c; + + dev->shadow_regs[0x85] = 0x05; + dev->shadow_regs[0x86] = 0x14; + dev->shadow_regs[0x87] = 0x06; + dev->shadow_regs[0x88] = 0x04; + + dev->shadow_regs[0x91] = 0x60; + dev->shadow_regs[0x92] = 0x85; + } + + dev->shadow_regs[0x35] = 0x01; + dev->shadow_regs[0x37] = 0x01; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x01; + dev->shadow_regs[0x93] = 0x0e; + + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x86; + /* ? */ + dev->shadow_regs[0xc5] = 0x41; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x0c; + /* ? */ + dev->shadow_regs[0xc9] = 0x3a; + dev->shadow_regs[0xca] = 0x40; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x00; + dev->shadow_regs[0xe1] = 0x00; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0x85; + /* ? */ + dev->shadow_regs[0xe3] = 0x0b; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x00; + dev->shadow_regs[0xe5] = 0x00; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0x0e; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x05; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x01; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x01; + } + else + { + /* set grayscale scan */ + dev->shadow_regs[0x2f] = 0x21; + /* set ? */ + dev->shadow_regs[0x34] = 0x22; + dev->shadow_regs[0x35] = 0x22; + dev->shadow_regs[0x36] = 0x42; + dev->shadow_regs[0x37] = 0x42; + dev->shadow_regs[0x38] = 0x62; + /* set motor resolution divisor */ + dev->shadow_regs[0x39] = 0x01; + /* set # of head moves per CIS read */ + rts88xx_set_scan_frequency (dev->shadow_regs, 0); + + /* set ? only for colour? */ + dev->shadow_regs[0x80] = 0x00; + dev->shadow_regs[0x81] = 0x00; + dev->shadow_regs[0x82] = 0x00; + dev->shadow_regs[0x85] = 0x00; + dev->shadow_regs[0x86] = 0x00; + dev->shadow_regs[0x87] = 0x00; + dev->shadow_regs[0x88] = 0x00; + dev->shadow_regs[0x91] = 0x00; + dev->shadow_regs[0x92] = 0x00; + dev->shadow_regs[0x93] = 0x06; + /* Motor enable & Coordinate space denominator */ + dev->shadow_regs[0xc3] = 0x81; + /* ? */ + dev->shadow_regs[0xc5] = 0x41; + /* Movement direction & step size */ + dev->shadow_regs[0xc6] = 0x09; + /* ? */ + dev->shadow_regs[0xc9] = 0x3a; + dev->shadow_regs[0xca] = 0x40; + /* bounds of movement range0 */ + dev->shadow_regs[0xe0] = 0x00; + dev->shadow_regs[0xe1] = 0x00; + /* step size range0 */ + dev->shadow_regs[0xe2] = 0xc7; + /* ? */ + dev->shadow_regs[0xe3] = 0x29; + /* bounds of movement range1 */ + dev->shadow_regs[0xe4] = 0x00; + dev->shadow_regs[0xe5] = 0x00; + /* step size range1 */ + dev->shadow_regs[0xe6] = 0xdc; + /* bounds of movement range2 */ + dev->shadow_regs[0xe7] = 0x00; + dev->shadow_regs[0xe8] = 0x00; + /* step size range2 */ + dev->shadow_regs[0xe9] = 0x1b; + /* bounds of movement range3 */ + dev->shadow_regs[0xea] = 0x00; + dev->shadow_regs[0xeb] = 0x00; + /* step size range3 */ + dev->shadow_regs[0xec] = 0x07; + /* step size range4 */ + dev->shadow_regs[0xef] = 0x03; + } + + /* set horizontal resolution */ + dev->shadow_regs[0x79] = 0x40; + } + + /* is calibration has been done, we override fixed settings with detected ones */ + if (calibrated) + { + /* override fixed values with ones from calibration */ + if (rts88xx_is_color (dev->shadow_regs)) + { + rts88xx_set_offset (dev->shadow_regs, + dev->offset.red, + dev->offset.green, dev->offset.blue); + rts88xx_set_gain (dev->shadow_regs, + dev->gain.red, dev->gain.green, dev->gain.blue); + } + else + { + rts88xx_set_offset (dev->shadow_regs, + dev->offset.gray, + dev->offset.gray, dev->offset.gray); + rts88xx_set_gain (dev->shadow_regs, + dev->gain.gray, dev->gain.gray, dev->gain.gray); + } + } + DBG (2, "sanei_lexmark_low_set_scan_regs: end.\n"); + return SANE_STATUS_GOOD; +} + +SANE_Status +sanei_lexmark_low_start_scan (Lexmark_Device * dev) +{ + SANE_Int devnum; + + static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; + + static SANE_Byte command5_block[] = { 0x80, 0xb3, 0x00, 0x01 }; + + SANE_Byte poll_result[3]; + SANE_Byte read_result; + SANE_Bool scan_head_moving; + size_t size; + + devnum = dev->devnum; + + dev->transfer_buffer = NULL; /* No data xferred yet */ + DBG (2, "sanei_lexmark_low_start_scan:\n"); + + + /* 80 b3 00 01 - poll for scanner not moving */ + scan_head_moving = SANE_TRUE; + while (scan_head_moving) + { + size = 4; + low_usb_bulk_write (devnum, command5_block, &size); + size = 0x1; + low_usb_bulk_read (devnum, &read_result, &size); + if ((read_result & 0xF) == 0x0) + { + scan_head_moving = SANE_FALSE; + } + /* F.O. Should be a timeout here so we don't hang if something breaks */ +#ifdef FAKE_USB + scan_head_moving = SANE_FALSE; +#endif + } + + /* Clear C6 */ + low_clr_c6 (devnum); + /* Stop the scanner */ + low_stop_mvmt (devnum); + + /*Set regs x2 */ + dev->shadow_regs[0x32] = 0x00; + low_write_all_regs (devnum, dev->shadow_regs); + dev->shadow_regs[0x32] = 0x40; + low_write_all_regs (devnum, dev->shadow_regs); + + /* Start Scan */ + rts88xx_commit (devnum, dev->shadow_regs[0x2c]); + + /* We start with 0 bytes remaining to be read */ + dev->bytes_remaining = 0; + /* and 0 bytes in the transfer buffer */ + dev->bytes_in_buffer = 0; + dev->bytes_read = 0; + + /* Poll the available byte count until not 0 */ + while (1) + { + size = 4; + low_usb_bulk_write (devnum, command4_block, &size); + size = 0x3; + low_usb_bulk_read (devnum, poll_result, &size); + if (! + (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) + { + /* if result != 00 00 00 we got data */ + + /* data_size should be used to set bytes_remaining */ + /* data_size is set from sane_get_parameters () */ + dev->bytes_remaining = dev->data_size; + /* Initialize the read buffer */ + read_buffer_init (dev, dev->params.bytes_per_line); + return SANE_STATUS_GOOD; + + } + size = 4; + /* I'm not sure why the Windows driver does this - probably a timeout? */ + low_usb_bulk_write (devnum, command5_block, &size); + size = 0x1; + low_usb_bulk_read (devnum, &read_result, &size); + if (read_result != 0x68) + { + dev->bytes_remaining = 0; + return SANE_STATUS_IO_ERROR; + } + } + + DBG (2, "sanei_lexmark_low_start_scan: end.\n"); + return SANE_STATUS_GOOD; +} + +long +sanei_lexmark_low_read_scan_data (SANE_Byte * data, SANE_Int size, + Lexmark_Device * dev) +{ + SANE_Bool isColourScan, isGrayScan; + static SANE_Byte command1_block[] = { 0x91, 0x00, 0xff, 0xc0 }; + size_t cmd_size, xfer_request; + long bytes_read; + SANE_Bool even_byte; + SANE_Status status; + int i, k, val; + + DBG (2, "sanei_lexmark_low_read_scan_data:\n"); + + /* colour mode */ + isGrayScan = SANE_FALSE; + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + isColourScan = SANE_TRUE; + else + { + isColourScan = SANE_FALSE; + /* grayscale mode */ + if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) + isGrayScan = SANE_TRUE; + } + + /* Check if we have a transfer buffer. Create one and fill it if we don't */ + if (dev->transfer_buffer == NULL) + { + if (dev->bytes_remaining > 0) + { + if (dev->bytes_remaining > MAX_XFER_SIZE) + xfer_request = MAX_XFER_SIZE; + else + xfer_request = dev->bytes_remaining; + + command1_block[2] = (SANE_Byte) (xfer_request >> 8); + command1_block[3] = (SANE_Byte) (xfer_request & 0xFF); + + /* wait for data */ + status = low_poll_data (dev->devnum); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_lexmark_low_read_scan_data: time-out while waiting for data.\n"); + return status; + } + + /* Create buffer to hold the amount we will request */ + dev->transfer_buffer = (SANE_Byte *) malloc (MAX_XFER_SIZE); + if (dev->transfer_buffer == NULL) + return SANE_STATUS_NO_MEM; + + /* Fill it */ + /* Write: 91 00 (xfer_size) */ + cmd_size = 4; + low_usb_bulk_write (dev->devnum, command1_block, &cmd_size); + + /* Read: xfer_size bytes */ + cmd_size = xfer_request; + low_usb_bulk_read (dev->devnum, dev->transfer_buffer, &cmd_size); + + /* apply shading coefficients */ + k = dev->bytes_read % dev->read_buffer->linesize; + for (i = 0; i < (int) cmd_size; i++) + { + val = dev->transfer_buffer[i]; + val = (int) ((float) val * dev->shading_coeff[k] + 0.5); + if (val > 255) + val = 255; + dev->transfer_buffer[i] = val; + k++; + if ((size_t) k == dev->read_buffer->linesize) + k = 0; + } + + /* advance by the amount actually read from device */ + dev->bytes_read += cmd_size; + dev->bytes_remaining -= cmd_size; + dev->bytes_in_buffer = cmd_size; + dev->read_pointer = dev->transfer_buffer; + DBG (2, "sanei_lexmark_low_read_scan_data:\n"); + DBG (2, " Filled a buffer from the scanner\n"); + DBG (2, " bytes_remaining: %lu\n", (u_long) dev->bytes_remaining); + DBG (2, " bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer); + DBG (2, " read_pointer: %p\n", dev->read_pointer); + } + } + + DBG (5, "READ BUFFER INFO: \n"); + DBG (5, " write ptr: %p\n", dev->read_buffer->writeptr); + DBG (5, " read ptr: %p\n", dev->read_buffer->readptr); + DBG (5, " max write ptr: %p\n", dev->read_buffer->max_writeptr); + DBG (5, " buffer size: %lu\n", (u_long) dev->read_buffer->size); + DBG (5, " line size: %lu\n", (u_long) dev->read_buffer->linesize); + DBG (5, " empty: %d\n", dev->read_buffer->empty); + DBG (5, " line no: %d\n", dev->read_buffer->image_line_no); + + + /* If there is space in the read buffer, copy the transfer buffer over */ + if (read_buffer_bytes_available (dev->read_buffer) >= dev->bytes_in_buffer) + { + even_byte = SANE_TRUE; + while (dev->bytes_in_buffer) + { + + /* Colour Scan */ + if (isColourScan) + { + if (even_byte) + read_buffer_add_byte (dev->read_buffer, + dev->read_pointer + 1); + else + read_buffer_add_byte (dev->read_buffer, + dev->read_pointer - 1); + even_byte = !even_byte; + } + /* Gray Scan */ + else if (isGrayScan) + { + if (even_byte) + read_buffer_add_byte_gray (dev->read_buffer, + dev->read_pointer + 1); + else + read_buffer_add_byte_gray (dev->read_buffer, + dev->read_pointer - 1); + even_byte = !even_byte; + } + /* Lineart Scan */ + else + { + if (even_byte) + read_buffer_add_bit_lineart (dev->read_buffer, + dev->read_pointer + 1, + dev->threshold); + else + read_buffer_add_bit_lineart (dev->read_buffer, + dev->read_pointer - 1, + dev->threshold); + even_byte = !even_byte; + } + dev->read_pointer = dev->read_pointer + sizeof (SANE_Byte); + dev->bytes_in_buffer--; + } + /* free the transfer buffer */ + free (dev->transfer_buffer); + dev->transfer_buffer = NULL; + } + + DBG (5, "READ BUFFER INFO: \n"); + DBG (5, " write ptr: %p\n", dev->read_buffer->writeptr); + DBG (5, " read ptr: %p\n", dev->read_buffer->readptr); + DBG (5, " max write ptr: %p\n", dev->read_buffer->max_writeptr); + DBG (5, " buffer size: %lu\n", (u_long) dev->read_buffer->size); + DBG (5, " line size: %lu\n", (u_long) dev->read_buffer->linesize); + DBG (5, " empty: %d\n", dev->read_buffer->empty); + DBG (5, " line no: %d\n", dev->read_buffer->image_line_no); + + /* Read blocks out of read buffer */ + bytes_read = read_buffer_get_bytes (dev->read_buffer, data, size); + + DBG (2, "sanei_lexmark_low_read_scan_data:\n"); + DBG (2, " Copying lines from buffer to data\n"); + DBG (2, " bytes_remaining: %lu\n", (u_long) dev->bytes_remaining); + DBG (2, " bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer); + DBG (2, " read_pointer: %p\n", dev->read_buffer->readptr); + DBG (2, " bytes_read %lu\n", (u_long) bytes_read); + + /* if no more bytes to xfer and read buffer empty we're at the end */ + if ((dev->bytes_remaining == 0) && read_buffer_is_empty (dev->read_buffer)) + { + if (!dev->eof) + { + DBG (2, + "sanei_lexmark_low_read_scan_data: EOF- parking the scanner\n"); + dev->eof = SANE_TRUE; + low_rewind (dev, dev->shadow_regs); + } + else + { + DBG (2, "ERROR: Why are we trying to set eof more than once?\n"); + } + } + + DBG (2, "sanei_lexmark_low_read_scan_data: end.\n"); + return bytes_read; +} + +void +low_rewind (Lexmark_Device * dev, SANE_Byte * regs) +{ + SANE_Int new_location; + SANE_Int location; + SANE_Int scale; + + DBG (2, "low_rewind: \n"); + + /* We rewind at 1200dpi resolution. We rely on content of shadow registers + to compute the number of lines at 1200 dpi to go back */ + + /* first move to start of scanning area */ + scale = 600 / dev->val[OPT_RESOLUTION].w; + new_location = ((dev->val[OPT_BR_Y].w / scale) * scale) * 2; + + /* then add distance to go to the "origin dot" */ + if (rts88xx_is_color (regs)) + new_location += 400; + else + new_location += 420; + + if (dev->model.sensor_type == X74_SENSOR) + new_location += 150; + + + location = new_location - 1; + DBG (2, "low_rewind: %d=>new_location=%d\n", dev->val[OPT_BR_Y].w, + new_location); + + /* stops any pending scan */ + low_clr_c6 (dev->devnum); + low_cancel (dev->devnum); + + /* set regs for rewind */ + regs[0x2f] = 0xa1; + regs[0x32] = 0x00; + regs[0x39] = 0x00; + + /* all other regs are always the same. these ones change with parameters */ + /* the following 4 regs are the location 61,60 and the location+1 63,62 */ + + regs[0x60] = LOBYTE (location); + regs[0x61] = HIBYTE (location); + regs[0x62] = LOBYTE (new_location); + regs[0x63] = HIBYTE (new_location); + + switch (dev->model.motor_type) + { + case X74_MOTOR: + regs[0xc3] = 0x81; + regs[0xc6] = 0x03; + regs[0xc9] = 0x39; + regs[0xe0] = 0x81; + regs[0xe1] = 0x16; + regs[0xe2] = 0xe1; + regs[0xe3] = 0x04; + regs[0xe4] = 0xe7; + regs[0xe5] = 0x14; + regs[0xe6] = 0x64; + regs[0xe7] = 0xd5; + regs[0xe8] = 0x08; + regs[0xe9] = 0x32; + regs[0xea] = 0xed; + regs[0xeb] = 0x04; + regs[0xec] = 0x0c; + regs[0xef] = 0x08; + break; + case X1100_MOTOR: + case A920_MOTOR: + /* set regs for rewind */ + regs[0x79] = 0x40; + regs[0xb2] = 0x04; + regs[0xc3] = 0x81; + regs[0xc6] = 0x01; + regs[0xc9] = 0x3b; + regs[0xe0] = 0x2b; + regs[0xe1] = 0x17; + regs[0xe2] = 0xe7; + regs[0xe3] = 0x03; + regs[0xe6] = 0xdc; + regs[0xe7] = 0xb3; + regs[0xe8] = 0x07; + regs[0xe9] = 0x1b; + regs[0xea] = 0x00; + regs[0xeb] = 0x00; + regs[0xec] = 0x07; + regs[0xef] = 0x03; + break; + } + + + /* starts scan */ + low_start_scan (dev->devnum, regs); + DBG (2, "low_rewind: end.\n"); +} + + +SANE_Status +read_buffer_init (Lexmark_Device * dev, int bytesperline) +{ + size_t no_lines_in_buffer; + + DBG (2, "read_buffer_init: Start\n"); + + dev->read_buffer = (Read_Buffer *) malloc (sizeof (Read_Buffer)); + if (dev->read_buffer == NULL) + return SANE_STATUS_NO_MEM; + dev->read_buffer->linesize = bytesperline; + dev->read_buffer->gray_offset = 0; + dev->read_buffer->max_gray_offset = bytesperline - 1; + dev->read_buffer->region = RED; + dev->read_buffer->red_offset = 0; + dev->read_buffer->green_offset = 1; + dev->read_buffer->blue_offset = 2; + dev->read_buffer->max_red_offset = bytesperline - 3; + dev->read_buffer->max_green_offset = bytesperline - 2; + dev->read_buffer->max_blue_offset = bytesperline - 1; + no_lines_in_buffer = 3 * MAX_XFER_SIZE / bytesperline; + dev->read_buffer->size = bytesperline * no_lines_in_buffer; + dev->read_buffer->data = (SANE_Byte *) malloc (dev->read_buffer->size); + if (dev->read_buffer->data == NULL) + return SANE_STATUS_NO_MEM; + dev->read_buffer->readptr = dev->read_buffer->data; + dev->read_buffer->writeptr = dev->read_buffer->data; + dev->read_buffer->max_writeptr = dev->read_buffer->data + + (no_lines_in_buffer - 1) * bytesperline; + dev->read_buffer->empty = SANE_TRUE; + dev->read_buffer->image_line_no = 0; + dev->read_buffer->bit_counter = 0; + dev->read_buffer->max_lineart_offset = dev->params.pixels_per_line - 1; + return SANE_STATUS_GOOD; +} + +SANE_Status +read_buffer_free (Read_Buffer * read_buffer) +{ + DBG (2, "read_buffer_free:\n"); + if (read_buffer) + { + free (read_buffer->data); + free (read_buffer); + read_buffer = NULL; + } + return SANE_STATUS_GOOD; +} + +size_t +read_buffer_bytes_available (Read_Buffer * rb) +{ + + DBG (2, "read_buffer_bytes_available:\n"); + + if (rb->empty) + return rb->size; + else if ((size_t) abs (rb->writeptr - rb->readptr) < rb->linesize) + return 0; /* ptrs are less than one line apart */ + else if (rb->writeptr < rb->readptr) + return (rb->readptr - rb->writeptr - rb->linesize); + else + return (rb->size + rb->readptr - rb->writeptr - rb->linesize); +} + +SANE_Status +read_buffer_add_byte (Read_Buffer * rb, SANE_Byte * byte_pointer) +{ + + /* DBG(2, "read_buffer_add_byte:\n"); */ + /* F.O. Need to fix the endian byte ordering here */ + + switch (rb->region) + { + case RED: + *(rb->writeptr + rb->red_offset) = *byte_pointer; + if (rb->red_offset == rb->max_red_offset) + { + rb->red_offset = 0; + rb->region = GREEN; + } + else + rb->red_offset = rb->red_offset + (3 * sizeof (SANE_Byte)); + return SANE_STATUS_GOOD; + case GREEN: + *(rb->writeptr + rb->green_offset) = *byte_pointer; + if (rb->green_offset == rb->max_green_offset) + { + rb->green_offset = 1; + rb->region = BLUE; + } + else + rb->green_offset = rb->green_offset + (3 * sizeof (SANE_Byte)); + return SANE_STATUS_GOOD; + case BLUE: + *(rb->writeptr + rb->blue_offset) = *byte_pointer; + if (rb->blue_offset == rb->max_blue_offset) + { + rb->image_line_no++; + /* finished a line. read_buffer no longer empty */ + rb->empty = SANE_FALSE; + rb->blue_offset = 2; + rb->region = RED; + if (rb->writeptr == rb->max_writeptr) + rb->writeptr = rb->data; /* back to beginning of buffer */ + else + rb->writeptr = rb->writeptr + rb->linesize; /* next line */ + } + else + rb->blue_offset = rb->blue_offset + (3 * sizeof (SANE_Byte)); + return SANE_STATUS_GOOD; + } + return SANE_STATUS_GOOD; +} + +SANE_Status +read_buffer_add_byte_gray (Read_Buffer * rb, SANE_Byte * byte_pointer) +{ + + /* DBG(2, "read_buffer_add_byte_gray:\n"); */ + + *(rb->writeptr + rb->gray_offset) = *byte_pointer; + + if (rb->gray_offset == rb->max_gray_offset) + { + rb->image_line_no++; + /* finished a line. read_buffer no longer empty */ + rb->empty = SANE_FALSE; + rb->gray_offset = 0; + + if (rb->writeptr == rb->max_writeptr) + rb->writeptr = rb->data; /* back to beginning of buffer */ + else + rb->writeptr = rb->writeptr + rb->linesize; /* next line */ + } + else + rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte)); + return SANE_STATUS_GOOD; +} + +SANE_Status +read_buffer_add_bit_lineart (Read_Buffer * rb, SANE_Byte * byte_pointer, + SANE_Byte threshold) +{ + SANE_Byte tmpByte; + SANE_Byte *currentBytePtr; + SANE_Int bitIndex; + + /* DBG(2, "read_buffer_add_bit_lineart:\n"); */ + + /* threshold = 0x80; */ + tmpByte = 0; + /* Create a bit by comparing incoming byte to threshold */ + if (*byte_pointer <= threshold) + { + tmpByte = 128; + } + + /* Calculate the bit index in the current byte */ + bitIndex = rb->bit_counter % 8; + /* Move the bit to its correct position in the temporary byte */ + tmpByte = tmpByte >> bitIndex; + /* Get the pointer to the current byte */ + currentBytePtr = rb->writeptr + rb->gray_offset; + + /* If this is the first write to this byte, clear the byte */ + if (bitIndex == 0) + *currentBytePtr = 0; + /* Set the value of the bit in the current byte */ + *currentBytePtr = *currentBytePtr | tmpByte; + + /* last bit in the line? */ + if (rb->bit_counter == rb->max_lineart_offset) + { + /* Check if we're at the last byte of the line - error if not */ + if (rb->gray_offset != rb->max_gray_offset) + { + DBG (5, "read_buffer_add_bit_lineart:\n"); + DBG (5, " Last bit of line is not last byte.\n"); + DBG (5, " Bit Index: %d, Byte Index: %d. \n", rb->bit_counter, + rb->max_gray_offset); + return SANE_STATUS_INVAL; + } + rb->image_line_no++; + /* line finished read_buffer no longer empty */ + rb->empty = SANE_FALSE; + rb->gray_offset = 0; + /* are we at the last line in the read buffer ? */ + if (rb->writeptr == rb->max_writeptr) + rb->writeptr = rb->data; /* back to beginning of buffer */ + else + rb->writeptr = rb->writeptr + rb->linesize; /* next line */ + /* clear the bit counter */ + rb->bit_counter = 0; + } + /* last bit in the byte? */ + else if (bitIndex == 7) + { + /* Not at the end of the line, but byte done. Increment byte offset */ + rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte)); + /* increment bit counter */ + rb->bit_counter++; + } + else + { + /* else increment bit counter */ + rb->bit_counter++; + } + + return SANE_STATUS_GOOD; +} + + +size_t +read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer, size_t rqst_size) +{ + /* Read_Buffer *rb; */ + size_t available_bytes; + + /* rb = read_buffer; */ + if (rb->empty) + return 0; + else if (rb->writeptr > rb->readptr) + { + available_bytes = rb->writeptr - rb->readptr; + if (available_bytes <= rqst_size) + { + /* We can read from the read pointer up to the write pointer */ + memcpy (buffer, rb->readptr, available_bytes); + rb->readptr = rb->writeptr; + rb->empty = SANE_TRUE; + return available_bytes; + } + else + { + /* We can read from the full request size */ + memcpy (buffer, rb->readptr, rqst_size); + rb->readptr = rb->readptr + rqst_size; + return rqst_size; + } + } + else + { + /* The read pointer is ahead of the write pointer. Its wrapped around. */ + /* We can read to the end of the buffer and make a recursive call to */ + /* read any available lines at the beginning of the buffer */ + available_bytes = rb->data + rb->size - rb->readptr; + if (available_bytes <= rqst_size) + { + /* We can read from the read pointer up to the end of the buffer */ + memcpy (buffer, rb->readptr, available_bytes); + rb->readptr = rb->data; + if (rb->writeptr == rb->readptr) + rb->empty = SANE_TRUE; + return available_bytes + + read_buffer_get_bytes (rb, buffer + available_bytes, + rqst_size - available_bytes); + } + else + { + /* We can read from the full request size */ + memcpy (buffer, rb->readptr, rqst_size); + rb->readptr = rb->readptr + rqst_size; + return rqst_size; + } + } +} + +SANE_Bool +read_buffer_is_empty (Read_Buffer * read_buffer) +{ + return read_buffer->empty; +} + +/* + * average a width*height rgb/monochrome area + * return values in given pointers + */ +static int +average_area (SANE_Byte * regs, SANE_Byte * data, int width, int height, + int *ra, int *ga, int *ba) +{ + int x, y; + int global = 0; + int rc, gc, bc; + + *ra = 0; + *ga = 0; + *ba = 0; + rc = 0; + gc = 0; + bc = 0; + if (rts88xx_is_color (regs)) + { + for (x = 0; x < width; x++) + for (y = 0; y < height; y++) + { + rc += data[3 * width * y + x]; + gc += data[3 * width * y + width + x]; + bc += data[3 * width * y + 2 * width + x]; + } + global = (rc + gc + bc) / (3 * width * height); + *ra = rc / (width * height); + *ga = gc / (width * height); + *ba = bc / (width * height); + } + else + { + for (x = 0; x < width; x++) + for (y = 0; y < height; y++) + { + gc += data[width * y + x]; + } + global = gc / (width * height); + *ga = gc / (width * height); + } + DBG (7, "average_area: global=%d, red=%d, green=%d, blue=%d\n", global, *ra, + *ga, *ba); + return global; +} + +/** + * we scan a dark area with gain minimum to detect offset + */ +SANE_Status +sanei_lexmark_low_offset_calibration (Lexmark_Device * dev) +{ + SANE_Byte regs[255]; /* we have our own copy of shadow registers */ + SANE_Status status = SANE_STATUS_GOOD; + int i, lines = 8, yoffset = 2; + int pixels; + int failed = 0; + /* offsets */ + int ro = 0, go = 0, bo = 0; + /* averages */ + int ra, ga, ba, average; + SANE_Byte *data = NULL; + SANE_Byte top[OFFSET_RANGES] = { 0, 0x7f, 0x9f, 0xbf, 0xff }; +#ifdef DEEP_DEBUG + char title[20]; +#endif + + DBG (2, "sanei_lexmark_low_offset_calibration: start\n"); + /* copy registers */ + for (i = 0; i < 255; i++) + regs[i] = dev->shadow_regs[i]; + + /* we clear movement bit */ + regs[0xc3] = regs[0xc3] & 0x7f; + + pixels = + (dev->sensor->offset_endx - dev->sensor->offset_startx) / regs[0x7a]; + + /* there are 4 ranges of offset: + 00-7F : almost no offset + 80-9F : high noise + A0-BF : high noise + C0-FF : high noise + we start with the highest one and decrease until + overall offset is ok + First loop may have such an high offset that scanned data overflow + and gives a low average. So we allways skip its results + */ + + /* minimal gains */ + DBG (3, + "sanei_lexmark_low_offset_calibration: setting gains to (1,1,1).\n"); + rts88xx_set_gain (regs, 1, 1, 1); + + i = OFFSET_RANGES; + average = 255; + + /* loop on ranges until one fits. Then adjust offset, first loop is + * always done. TODO detect overflow by 'noise looking' data pattern */ + while (((i > 0) && (average > dev->sensor->offset_threshold)) + || (i == OFFSET_RANGES)) + { + /* next range */ + i--; + + /* sets to top of range */ + ro = top[i]; + go = top[i]; + bo = top[i]; + rts88xx_set_offset (regs, ro, ro, ro); + DBG (3, + "sanei_lexmark_low_offset_calibration: setting offsets to (%d,%d,%d).\n", + ro, ro, ro); + + status = + low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels, + yoffset, lines, &data); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n"); + if (data != NULL) + free (data); + return status; + } +#ifdef DEEP_DEBUG + sprintf (title, "offset%02x.pnm", ro); + write_pnm_file (title, pixels, lines, rts88xx_is_color (regs), data); +#endif + average = average_area (regs, data, pixels, lines, &ra, &ga, &ba); + free (data); + } + if (i == 0) + { + DBG (2, "sanei_lexmark_low_offset_calibration: failed !\n"); + failed = 1; + } + + /* increase gain and scan again */ + /* increase gain for fine offset tuning */ + rts88xx_set_gain (regs, 6, 6, 6); + status = + low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels, yoffset, + lines, &data); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n"); + if (data != NULL) + free (data); + return status; + } + average_area (regs, data, pixels, lines, &ra, &ga, &ba); +#ifdef DEEP_DEBUG + write_pnm_file ("offset-final.pnm", pixels, lines, rts88xx_is_color (regs), + data); +#endif + + /* this "law" is a guess, may (should?) be changed ... */ + if (!failed) + { + if (ro > ra) + dev->offset.red = ro - ra; + if (go > ga) + { + dev->offset.green = go - ga; + dev->offset.gray = go - ga; + } + if (bo > ba) + dev->offset.blue = bo - ba; + } + else + { + dev->offset.red = dev->sensor->offset_fallback; + dev->offset.green = dev->sensor->offset_fallback; + dev->offset.blue = dev->sensor->offset_fallback; + } + DBG (7, + "sanei_lexmark_low_offset_calibration: offset=(0x%02x,0x%02x,0x%02x).\n", + dev->offset.red, dev->offset.green, dev->offset.blue); + + DBG (2, "sanei_lexmark_low_offset_calibration: end.\n"); + free (data); + return status; +} + +/* + * we scan a white area until average is good enough + * level is good enough when it maximize the range value of output: + * ie max-min is maximum + */ +SANE_Status +sanei_lexmark_low_gain_calibration (Lexmark_Device * dev) +{ + SANE_Byte regs[255]; /* we have our own copy of shadow registers */ + SANE_Status status = SANE_STATUS_GOOD; + int i, lines = 4, yoffset = 1; + int sx, ex; + int pixels; + /* averages */ + int ra, ga, ba; + SANE_Byte *data = NULL; + int red, green, blue; +#ifdef DEEP_DEBUG + char title[20]; +#endif + + DBG (2, "sanei_lexmark_low_gain_calibration: start\n"); + /* copy registers */ + for (i = 0; i < 255; i++) + regs[i] = dev->shadow_regs[i]; + + /* we clear movement bit */ + regs[0xc3] = regs[0xc3] & 0x7f; + sx = regs[0x67] * 256 + regs[0x66]; + ex = regs[0x6d] * 256 + regs[0x6c]; + pixels = (ex - sx) / regs[0x7a]; + + + /* set up inital gains */ + red = 6; + green = 6; + blue = 6; + rts88xx_set_gain (regs, red, green, blue); + + /* init loop */ + i = 0; + ra = 0; + ba = 0; + ga = 0; + + status = low_cancel (dev->devnum); + if (status != SANE_STATUS_GOOD) + return status; + + /* we do a simple scan all 3 averages give the choosen level */ + while (((rts88xx_is_color (regs) + && ((ra < dev->sensor->red_gain_target) + || (ga < dev->sensor->green_gain_target) + || (ba < dev->sensor->blue_gain_target))) + || (!rts88xx_is_color (regs) + && (ga < dev->sensor->gray_gain_target))) && (i < 25)) + { + status = low_simple_scan (dev, regs, sx, pixels, yoffset, lines, &data); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_lexmark_low_gain_calibration: low_simple_scan failed!\n"); + if (data != NULL) + free (data); + return status; + } +#ifdef DEEP_DEBUG + sprintf (title, "gain%02d.pnm", i); + write_pnm_file (title, pixels, lines, rts88xx_is_color (regs), data); +#endif + average_area (regs, data, pixels, lines, &ra, &ga, &ba); + free (data); + if (ra < dev->sensor->red_gain_target) + red++; + if (ga < dev->sensor->green_gain_target + || (dev->sensor->gray_gain_target && !rts88xx_is_color (regs))) + green++; + if (ba < dev->sensor->blue_gain_target) + blue++; + rts88xx_set_gain (regs, red, green, blue); + i++; + } + dev->gain.red = red; + dev->gain.green = green; + dev->gain.blue = blue; + dev->gain.gray = green; + DBG (7, + "sanei_lexmark_low_gain_calibration: gain=(0x%02x,0x%02x,0x%02x).\n", + dev->gain.red, dev->gain.green, dev->gain.blue); + DBG (2, "sanei_lexmark_low_gain_calibration: end.\n"); + return status; +} + +/** + * there is no hardware shading correction. So we have to do it in software. + * We do it by scanning a pure white area which is before scanning area. Then + * we compute per pixel coefficient to move the scanned value to the target + * value. These coefficients are used later to correct scanned data. + * The scan is done with all the final scan settings but the heigth and vertical + * start position. + */ +SANE_Status +sanei_lexmark_low_shading_calibration (Lexmark_Device * dev) +{ + SANE_Byte regs[255]; /* we have our own copy of shadow registers */ + int i, j, pixels, bpl; + int sx, ex; + SANE_Byte *data = NULL; + SANE_Status status; + /* enough 75 dpi lines to "go off" home position dot, + and include shading area */ + int lines = 4 + 4; + int lineoffset = 1; + int linetotal = lines + lineoffset; + int yoffset; + int x, y; + float rtarget, btarget, gtarget; + + DBG (2, "sanei_lexmark_low_shading_calibration: start\n"); + /* copy registers */ + for (i = 0; i < 255; i++) + regs[i] = dev->shadow_regs[i]; + + /* allocate memory for scan */ + sx = regs[0x67] * 256 + regs[0x66]; + ex = regs[0x6d] * 256 + regs[0x6c]; + + + DBG (7, "startx=%d, endx=%d, coef=%d, r2f=0x%02x\n", + sx, ex, regs[0x7a], regs[0x2f]); + + pixels = (ex - sx) / regs[0x7a]; + if (rts88xx_is_color (regs)) + bpl = 3 * pixels; + else + bpl = pixels; + + /* adjust the target area to the scanning resolution */ + lines = (8 * lines) / regs[0x7a]; + lineoffset = (8 * lineoffset) / regs[0x7a]; + linetotal = (8 * linetotal) / regs[0x7a]; + + data = (SANE_Byte *) malloc (bpl * lines); + DBG (7, "pixels=%d, lines=%d, size=%d\n", pixels, lines, bpl * lines); + if (data == NULL) + { + DBG (2, + "sanei_lexmark_low_shading_calibration: failed to allocate %d bytes !\n", + bpl * lines); + return SANE_STATUS_NO_MEM; + } + if (dev->shading_coeff != NULL) + free (dev->shading_coeff); + dev->shading_coeff = (float *) malloc (bpl * sizeof (float)); + if (dev->shading_coeff == NULL) + { + DBG (2, + "sanei_lexmark_low_shading_calibration: failed to allocate %d floats !\n", + bpl); + free (data); + return SANE_STATUS_NO_MEM; + } + + /* we set movement bit this time */ + regs[0xc3] = regs[0xc3] | 0x80; + + /* execute scan */ + status = low_simple_scan (dev, regs, sx, pixels, lineoffset, lines, &data); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n"); + if (data != NULL) + free (data); + return status; + } + + yoffset = -1; + /* the very first lines of the scan may include the dark dot used + * locate park position. We find the first line free of it in the scan. + * We can't use is_home_line since it modifies data. + */ + for (y = 0; (y < lines) && (yoffset == y - 1); y++) + { + if (rts88xx_is_color (regs)) + { + for (x = 0; x < 3 * pixels; x++) + { + if (data[x + y * 3 * pixels] < 30) + yoffset = y; + } + } + else + { + for (x = 0; x < pixels; x++) + { + if (data[x + y * pixels] < 30) + yoffset = y; + } + } + } + /* make sure we are really out of the dot */ + yoffset++; + + /* yoffset is index of last dot line, go to first white line */ + if (yoffset >= lines - 1) + { + DBG (7, + "sanei_lexmark_low_shading_calibration: failed to detect yoffset.\n"); + /* fail safe fallback, picture will be altered at dot position, + but scanner is safe */ + yoffset = lines - 2; + } + else + yoffset++; + DBG (7, "sanei_lexmark_low_shading_calibration: yoffset=%d.\n", yoffset); + +#ifdef DEEP_DEBUG + write_pnm_file ("shading.pnm", pixels, lines, rts88xx_is_color (regs), + data); +#endif + + /* computes coefficients */ + /* there are 8 lines usable for shading calibration at 150 dpi, between + bottom of "home position" dot and the start of the scanner's window + assembly, we only use 7 of them */ + if (yoffset + (8 * 4) / regs[0x7a] < lines) + lines = yoffset + (8 * 4) / regs[0x7a]; + rtarget = dev->sensor->red_shading_target; + gtarget = dev->sensor->green_shading_target; + btarget = dev->sensor->blue_shading_target; + for (i = 0; i < pixels; i++) + { + /* we computes the coefficient needed to move the scanned value to + the target value */ + if (rts88xx_is_color (dev->shadow_regs)) + { + /* RED */ + dev->shading_coeff[i] = 0; + for (j = yoffset; j < lines; j++) + dev->shading_coeff[i] += data[i + j * bpl]; + dev->shading_coeff[i] = + (rtarget / (dev->shading_coeff[i] / (lines - yoffset))); + + /* GREEN */ + dev->shading_coeff[i + pixels] = 0; + for (j = yoffset; j < lines; j++) + dev->shading_coeff[i + pixels] += data[i + j * bpl + pixels]; + dev->shading_coeff[i + pixels] = + ((gtarget / dev->shading_coeff[i + pixels]) * (lines - yoffset)); + + /* BLUE */ + dev->shading_coeff[i + 2 * pixels] = 0; + for (j = yoffset; j < lines; j++) + dev->shading_coeff[i + 2 * pixels] += + data[i + j * bpl + 2 * pixels]; + dev->shading_coeff[i + 2 * pixels] = + ((btarget / dev->shading_coeff[i + 2 * pixels]) * + (lines - yoffset)); + } + else + { + dev->shading_coeff[i] = 0; + for (j = yoffset; j < lines; j++) + { + dev->shading_coeff[i] += data[i + j * bpl]; + } + dev->shading_coeff[i] = + (rtarget / dev->shading_coeff[i]) * (lines - yoffset); + } + } + free(data); + + /* do the scan backward to go back to start position */ + regs[0xc6] &= 0xF7; + lines = (8 * 8) / regs[0x7a]; + /* it shoud use linetotal to account for the lineoffset */ + if (dev->model.sensor_type == X74_SENSOR) + lines = linetotal; + + /* execute scan */ + status = low_simple_scan (dev, regs, sx, pixels, 1, lines, &data); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n"); + if(data!=NULL) + free(data); + return status; + } + +#ifdef DEEP_DEBUG + write_pnm_file ("shading_bwd.pnm", pixels, lines, rts88xx_is_color (regs), + data); +#endif + free (data); + + DBG (2, "sanei_lexmark_low_shading_calibration: end.\n"); + return status; +} + + +SANE_Status +sanei_lexmark_low_calibration (Lexmark_Device * dev) +{ + SANE_Status status; + + DBG (2, "sanei_lexmark_low_calibration: start.\n"); + status = sanei_lexmark_low_offset_calibration (dev); + if (status != SANE_STATUS_GOOD) + return status; + + /* we put the offset just computed in scanning regs */ + if (rts88xx_is_color (dev->shadow_regs)) + { + rts88xx_set_offset (dev->shadow_regs, + dev->offset.red, + dev->offset.green, dev->offset.blue); + } + else + { + rts88xx_set_offset (dev->shadow_regs, + dev->offset.gray, + dev->offset.gray, dev->offset.gray); + } + + /* if manual gain settings, no gain calibration */ + if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE) + { + if (rts88xx_is_color (dev->shadow_regs)) + { + dev->gain.red = dev->val[OPT_RED_GAIN].w; + dev->gain.green = dev->val[OPT_GREEN_GAIN].w; + dev->gain.blue = dev->val[OPT_BLUE_GAIN].w; + } + else + dev->gain.gray = dev->val[OPT_GRAY_GAIN].w; + } + else + { + status = sanei_lexmark_low_gain_calibration (dev); + if (status != SANE_STATUS_GOOD) + return status; + } + + /* put the calibrated or manual settings before shading calibration + which must be done with final setting values */ + if (rts88xx_is_color (dev->shadow_regs)) + { + rts88xx_set_gain (dev->shadow_regs, dev->gain.red, dev->gain.green, + dev->gain.blue); + } + else + { + rts88xx_set_gain (dev->shadow_regs, dev->gain.gray, dev->gain.gray, + dev->gain.gray); + } + + status = sanei_lexmark_low_shading_calibration (dev); + + if (status != SANE_STATUS_GOOD) + return status; + + DBG (2, "sanei_lexmark_low_calibration: end.\n"); + return SANE_STATUS_GOOD; +} + +/* assign sensor data */ +static SANE_Status +sanei_lexmark_low_assign_sensor (Lexmark_Device * dev) +{ + int dn; + + /* init sensor data */ + dn = 0; + while (sensor_list[dn].id != 0 + && sensor_list[dn].id != dev->model.sensor_type) + dn++; + if (sensor_list[dn].id == 0) + { + DBG (1, + "sanei_lexmark_low_assign_sensor: unknown sensor %d\n", + dev->model.sensor_type); + return SANE_STATUS_UNSUPPORTED; + } + dev->sensor = &(sensor_list[dn]); + DBG (1, "sanei_lexmark_low_assign_sensor: assigned sensor number %d\n", + dev->model.sensor_type); + return SANE_STATUS_GOOD; +} + +/* assign model description, based on USB id, and register content when + * available */ +SANE_Status +sanei_lexmark_low_assign_model (Lexmark_Device * dev, + SANE_String_Const devname, SANE_Int vendor, + SANE_Int product, SANE_Byte mainboard) +{ + int dn; + SANE_Bool found = SANE_FALSE; + + DBG_INIT (); + + DBG (2, "sanei_lexmark_low_assign_model: start\n"); + DBG (3, + "sanei_lexmark_low_assign_model: assigning %04x:%04x, variant %d\n", + vendor, product, mainboard); + + dn = 0; + /* walk the list of known devices */ + while (!found && model_list[dn].vendor_id != 0) + { + /* no mainboard id given (at attach time) */ + if (mainboard == 0 + && vendor == model_list[dn].vendor_id + && product == model_list[dn].product_id) + found = SANE_TRUE; + /* mainboard given (init time) */ + if (mainboard != 0 + && mainboard == model_list[dn].mainboard_id + && vendor == model_list[dn].vendor_id + && product == model_list[dn].product_id) + found = SANE_TRUE; + + if (!found) + dn++; + } + + /* we hit the end of list, so we don't know about the current model */ + if (!found) + { + DBG (1, + "sanei_lexmark_low_assign_model: unknown device 0x%04x:0x%04x\n", + vendor, product); + return SANE_STATUS_UNSUPPORTED; + } + + dev->sane.name = strdup (devname); + dev->sane.vendor = model_list[dn].vendor; + dev->sane.model = model_list[dn].model; + dev->model = model_list[dn]; + dev->sane.type = "flatbed scanner"; + + DBG (3, "sanei_lexmark_low_assign_model: assigned %s\n", dev->model.model); + + /* init sensor data */ + DBG (2, "sanei_lexmark_low_assign_model: end.\n"); + return sanei_lexmark_low_assign_sensor (dev); +} |