diff options
Diffstat (limited to 'backend/artec.c')
-rw-r--r-- | backend/artec.c | 3754 |
1 files changed, 3754 insertions, 0 deletions
diff --git a/backend/artec.c b/backend/artec.c new file mode 100644 index 0000000..212de34 --- /dev/null +++ b/backend/artec.c @@ -0,0 +1,3754 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 1997 David Mosberger-Tang + 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. + + This file implements a SANE backend for the Artec/Ultima scanners. + + Copyright (C) 1998-2000 Chris Pinkham + Released under the terms of the GPL. + *NO WARRANTY* + + Portions contributed by: + David Leadbetter - A6000C (3-pass) + Dick Bruijn - AT12 + + ********************************************************************* + For feedback/information: + + cpinkham@corp.infi.net + http://www4.infi.net/~cpinkham/sane/sane-artec-doc.html + ********************************************************************* + */ + +#include "../include/sane/config.h" + +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "../include/_stdint.h" + +#include "../include/sane/sane.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_scsi.h" +#include "../include/sane/sanei_backend.h" +#include "../include/sane/sanei_config.h" + +#include <artec.h> + +#define BACKEND_NAME artec + +#define ARTEC_MAJOR 0 +#define ARTEC_MINOR 5 +#define ARTEC_SUB 16 +#define ARTEC_LAST_MOD "05/26/2001 17:28 EST" + + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#define ARTEC_CONFIG_FILE "artec.conf" +#define ARTEC_MAX_READ_SIZE 32768 + +static int num_devices; +static const SANE_Device **devlist = 0; +static ARTEC_Device *first_dev; +static ARTEC_Scanner *first_handle; + +static const SANE_String_Const mode_list[] = +{ + SANE_VALUE_SCAN_MODE_LINEART, + SANE_VALUE_SCAN_MODE_HALFTONE, + SANE_VALUE_SCAN_MODE_GRAY, + SANE_VALUE_SCAN_MODE_COLOR, + 0 +}; + +static const SANE_String_Const filter_type_list[] = +{ + "Mono", "Red", "Green", "Blue", + 0 +}; + +static const SANE_String_Const halftone_pattern_list[] = +{ + "User defined (unsupported)", "4x4 Spiral", "4x4 Bayer", "8x8 Spiral", + "8x8 Bayer", + 0 +}; + +static const SANE_Range u8_range = +{ + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + +#define INQ_LEN 0x60 +static const uint8_t inquiry[] = +{ + 0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00 +}; + +static const uint8_t test_unit_ready[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static struct + { + SANE_String model; /* product model */ + SANE_String type; /* type of scanner */ + double width; /* width in inches */ + double height; /* height in inches */ + SANE_Word adc_bits; /* Analog-to-Digital Converter Bits */ + SANE_Word setwindow_cmd_size; /* Set-Window command size */ + SANE_Word max_read_size; /* Max Read size in bytes */ + long flags; /* flags */ + SANE_String horz_resolution_str; /* Horizontal resolution list */ + SANE_String vert_resolution_str; /* Vertical resolution list */ + } +cap_data[] = +{ + { + "AT3", "flatbed", + 8.3, 11, 8, 55, 32768, + ARTEC_FLAG_CALIBRATE_RGB | + ARTEC_FLAG_RGB_LINE_OFFSET | + ARTEC_FLAG_RGB_CHAR_SHIFT | + ARTEC_FLAG_OPT_CONTRAST | + ARTEC_FLAG_GAMMA_SINGLE | + ARTEC_FLAG_SEPARATE_RES | + ARTEC_FLAG_SENSE_HANDLER | + ARTEC_FLAG_SENSE_BYTE_19 | + ARTEC_FLAG_ADF | + ARTEC_FLAG_HALFTONE_PATTERN | + ARTEC_FLAG_MBPP_NEGATIVE | + ARTEC_FLAG_ONE_PASS_SCANNER, + "50,100,200,300", "50,100,200,300,600" + } + , + { + "A6000C", "flatbed", + 8.3, 14, 8, 55, 8192, +/* some have reported that Calibration does not work the same as AT3 & A6000C+ + ARTEC_FLAG_CALIBRATE_RGB | + */ + ARTEC_FLAG_OPT_CONTRAST | + ARTEC_FLAG_OPT_BRIGHTNESS | + ARTEC_FLAG_SEPARATE_RES | + ARTEC_FLAG_SENSE_HANDLER | + ARTEC_FLAG_ADF | + ARTEC_FLAG_HALFTONE_PATTERN, + "50,100,200,300", "50,100,200,300,600" + } + , + { + "A6000C PLUS", "flatbed", + 8.3, 14, 8, 55, 8192, + ARTEC_FLAG_CALIBRATE_RGB | + ARTEC_FLAG_RGB_LINE_OFFSET | + ARTEC_FLAG_RGB_CHAR_SHIFT | + ARTEC_FLAG_OPT_CONTRAST | + ARTEC_FLAG_GAMMA_SINGLE | + ARTEC_FLAG_SEPARATE_RES | + ARTEC_FLAG_SENSE_HANDLER | + ARTEC_FLAG_SENSE_BYTE_19 | + ARTEC_FLAG_ADF | + ARTEC_FLAG_HALFTONE_PATTERN | + ARTEC_FLAG_MBPP_NEGATIVE | + ARTEC_FLAG_ONE_PASS_SCANNER, + "50,100,200,300", "50,100,200,300,600" + } + , + { + "AT6", "flatbed", + 8.3, 11, 10, 55, 32768, + ARTEC_FLAG_CALIBRATE_RGB | + ARTEC_FLAG_RGB_LINE_OFFSET | + ARTEC_FLAG_RGB_CHAR_SHIFT | + ARTEC_FLAG_OPT_CONTRAST | +/* gamma not working totally correct yet. + ARTEC_FLAG_GAMMA_SINGLE | + */ + ARTEC_FLAG_SEPARATE_RES | + ARTEC_FLAG_SENSE_HANDLER | + ARTEC_FLAG_ADF | + ARTEC_FLAG_HALFTONE_PATTERN | + ARTEC_FLAG_MBPP_NEGATIVE | + ARTEC_FLAG_ONE_PASS_SCANNER, + "50,100,200,300", "50,100,200,300,600" + } + , + { + "AT12", "flatbed", + 8.5, 11, 12, 67, 32768, +/* calibration works slower so disabled + ARTEC_CALIBRATE_DARK_WHITE | + */ +/* gamma not working totally correct yet. + ARTEC_FLAG_GAMMA | + */ + ARTEC_FLAG_OPT_CONTRAST | + ARTEC_FLAG_SEPARATE_RES | + ARTEC_FLAG_SENSE_HANDLER | + ARTEC_FLAG_SENSE_ENH_18 | + ARTEC_FLAG_SENSE_BYTE_22 | + ARTEC_FLAG_SC_BUFFERS_LINES | + ARTEC_FLAG_SC_HANDLES_OFFSET | + ARTEC_FLAG_PIXEL_AVERAGING | + ARTEC_FLAG_ENHANCE_LINE_EDGE | + ARTEC_FLAG_ADF | + ARTEC_FLAG_HALFTONE_PATTERN | + ARTEC_FLAG_MBPP_NEGATIVE | + ARTEC_FLAG_ONE_PASS_SCANNER, + "25,50,100,200,300,400,500,600", + "25,50,100,200,300,400,500,600,700,800,900,1000,1100,1200" + } + , + { + "AM12S", "flatbed", + 8.26, 11.7, 12, 67, ARTEC_MAX_READ_SIZE, +/* calibration works slower so disabled + ARTEC_CALIBRATE_DARK_WHITE | + */ +/* gamma not working totally correct yet. + ARTEC_FLAG_GAMMA | + */ + ARTEC_FLAG_RGB_LINE_OFFSET | + ARTEC_FLAG_SEPARATE_RES | + ARTEC_FLAG_IMAGE_REV_LR | + ARTEC_FLAG_REVERSE_WINDOW | + ARTEC_FLAG_SENSE_HANDLER | + ARTEC_FLAG_SENSE_ENH_18 | + ARTEC_FLAG_MBPP_NEGATIVE | + ARTEC_FLAG_ONE_PASS_SCANNER, + "50,100,300,600", + "50,100,300,600,1200" + } + , +}; + +/* store vendor and model if hardcoded in artec.conf */ +static char artec_vendor[9] = ""; +static char artec_model[17] = ""; + +/* file descriptor for debug data output */ +static int debug_fd = -1; + +static char *artec_skip_whitespace (char *str) +{ + while (isspace (*str)) + ++str; + return str; +} + +static SANE_Status +artec_str_list_to_word_list (SANE_Word ** word_list_ptr, SANE_String str) +{ + SANE_Word *word_list; + char *start; + char *end; + char temp_str[1024]; + int comma_count = 1; + + if ((str == NULL) || + (strlen (str) == 0)) + { + /* alloc space for word which stores length (0 in this case) */ + word_list = (SANE_Word *) malloc (sizeof (SANE_Word)); + if (word_list == NULL) + return (SANE_STATUS_NO_MEM); + + word_list[0] = 0; + *word_list_ptr = word_list; + return (SANE_STATUS_GOOD); + } + + /* make temp copy of input string (only hold 1024 for now) */ + strncpy (temp_str, str, 1023); + temp_str[1023] = '\0'; + + end = strchr (temp_str, ','); + while (end != NULL) + { + comma_count++; + start = end + 1; + end = strchr (start, ','); + } + + word_list = (SANE_Word *) calloc (comma_count + 1, + sizeof (SANE_Word)); + + if (word_list == NULL) + return (SANE_STATUS_NO_MEM); + + word_list[0] = comma_count; + + comma_count = 1; + start = temp_str; + end = strchr (temp_str, ','); + while (end != NULL) + { + *end = '\0'; + word_list[comma_count] = atol (start); + + start = end + 1; + comma_count++; + end = strchr (start, ','); + } + + word_list[comma_count] = atol (start); + + *word_list_ptr = word_list; + return (SANE_STATUS_GOOD); +} + +static size_t +artec_get_str_index (const SANE_String_Const strings[], char *str) +{ + size_t index; + + index = 0; + while ((strings[index]) && strcmp (strings[index], str)) + { + index++; + } + + if (!strings[index]) + { + index = 0; + } + + return (index); +} + +static size_t +max_string_size (const SANE_String_Const strings[]) +{ + size_t size, max_size = 0; + int i; + + for (i = 0; strings[i]; ++i) + { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } + + return (max_size); +} + +/* DB added a sense handler */ +/* last argument is expected to be a pointer to a Artec_Scanner structure */ +static SANE_Status +sense_handler (int fd, u_char * sense, void *arg) +{ + ARTEC_Scanner *s = (ARTEC_Scanner *)arg; + int err; + + err = 0; + + DBG(2, "sense fd: %d, data: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", fd, + sense[0], sense[1], sense[2], sense[3], + sense[4], sense[5], sense[6], sense[7], + sense[8], sense[9], sense[10], sense[11], + sense[12], sense[13], sense[14], sense[15]); + + /* byte 18 info pertaining to ADF */ + if ((s) && (s->hw->flags & ARTEC_FLAG_ADF)) + { + if (sense[18] & 0x01) + { + DBG (2, "sense: ADF PAPER JAM\n"); + err++; + } + if (sense[18] & 0x02) + { + DBG (2, "sense: ADF NO DOCUMENT IN BIN\n"); + err++; + } + if (sense[18] & 0x04) + { + DBG (2, "sense: ADF SWITCH COVER OPEN\n"); + err++; + } + /* DB : next is, i think no failure, so no incrementing s */ + if (sense[18] & 0x08) + { + DBG (2, "sense: ADF SET CORRECTLY ON TARGET\n"); + } + /* The following only for AT12, its reserved (zero?) on other models, */ + if (sense[18] & 0x10) + { + DBG (2, "sense: ADF LENGTH TOO SHORT\n"); + err++; + } + } + + /* enhanced byte 18 sense data */ + if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_ENH_18)) + { + if (sense[18] & 0x20) + { + DBG (2, "sense: LAMP FAIL : NOT WARM \n"); + err++; + } + if (sense[18] & 0x40) + { + DBG (2, "sense: NOT READY STATE\n"); + err++; + } + } + + if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_BYTE_19)) + { + if (sense[19] & 0x01) + { + DBG (2, "sense: 8031 program ROM checksum Error\n"); + err++; + } + if (sense[19] & 0x02) + { + DBG (2, "sense: 8031 data RAM R/W Error\n"); + err++; + } + if (sense[19] & 0x04) + { + DBG (2, "sense: Shadow Correction RAM R/W Error\n"); + err++; + } + if (sense[19] & 0x08) + { + DBG (2, "sense: Line RAM R/W Error\n"); + err++; + } + if (sense[19] & 0x10) + { + /* docs say "reserved to '0'" */ + DBG (2, "sense: CCD control circuit Error\n"); + err++; + } + if (sense[19] & 0x20) + { + DBG (2, "sense: Motor End Switch Error\n"); + err++; + } + if (sense[19] & 0x40) + { + /* docs say "reserved to '0'" */ + DBG (2, "sense: Lamp Error\n"); + err++; + } + if (sense[19] & 0x80) + { + DBG (2, "sense: Optical Calibration/Shading Error\n"); + err++; + } + } + + /* These are the self test results for tests 0-15 */ + if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_BYTE_22)) + { + if (sense[22] & 0x01) + { + DBG (2, "sense: 8031 Internal Memory R/W Error\n"); + err++; + } + if (sense[22] & 0x02) + { + DBG (2, "sense: EEPROM test pattern R/W Error\n"); + err++; + } + if (sense[22] & 0x04) + { + DBG (2, "sense: ASIC Test Error\n"); + err++; + } + if (sense[22] & 0x08) + { + DBG (2, "sense: Line RAM R/W Error\n"); + err++; + } + if (sense[22] & 0x10) + { + DBG (2, "sense: PSRAM R/W Test Error\n"); + err++; + } + if (sense[22] & 0x20) + { + DBG (2, "sense: Positioning Error\n"); + err++; + } + if (sense[22] & 0x40) + { + DBG (2, "sense: Test 6 Error\n"); + err++; + } + if (sense[22] & 0x80) + { + DBG (2, "sense: Test 7 Error\n"); + err++; + } + if (sense[23] & 0x01) + { + DBG (2, "sense: Test 8 Error\n"); + err++; + } + if (sense[23] & 0x02) + { + DBG (2, "sense: Test 9 Error\n"); + err++; + } + if (sense[23] & 0x04) + { + DBG (2, "sense: Test 10 Error\n"); + err++; + } + if (sense[23] & 0x08) + { + DBG (2, "sense: Test 11 Error\n"); + err++; + } + if (sense[23] & 0x10) + { + DBG (2, "sense: Test 12 Error\n"); + err++; + } + if (sense[23] & 0x20) + { + DBG (2, "sense: Test 13 Error\n"); + err++; + } + if (sense[23] & 0x40) + { + DBG (2, "sense: Test 14 Error\n"); + err++; + } + if (sense[23] & 0x80) + { + DBG (2, "sense: Test 15 Error\n"); + err++; + } + } + + if (err) + return SANE_STATUS_IO_ERROR; + + switch (sense[0]) + { + case 0x70: /* ALWAYS */ + switch (sense[2]) + { + case 0x00: + DBG (2, "sense: Successful command\n"); + return SANE_STATUS_GOOD; + case 0x02: + DBG (2, "sense: Not Ready, target can not be accessed\n"); + return SANE_STATUS_IO_ERROR; + case 0x03: + DBG (2, "sense: Medium Error, paper jam or misfeed during ADF\n"); + return SANE_STATUS_IO_ERROR; + case 0x04: + DBG (2, "sense: Hardware Error, non-recoverable\n"); + return SANE_STATUS_IO_ERROR; + case 0x05: + DBG (2, "sense: Illegal Request, bad parameter in command block\n"); + return SANE_STATUS_IO_ERROR; + case 0x06: + DBG (2, "sense: Unit Attention\n"); + return SANE_STATUS_GOOD; + default: + DBG (2, "sense: SENSE KEY UNKNOWN (%02x)\n", sense[2]); + return SANE_STATUS_IO_ERROR; + } + default: + DBG (2, "sense: Unkown Error Code Qualifier (%02x)\n", sense[0]); + return SANE_STATUS_IO_ERROR; + } + + DBG (2, "sense: Should not come here!\n"); + return SANE_STATUS_IO_ERROR; +} + + +/* DB added a wait routine for the scanner to come ready */ +static SANE_Status +wait_ready (int fd) +{ + SANE_Status status; + int retry = 30; /* make this tuneable? */ + + DBG (7, "wait_ready()\n"); + while (retry-- > 0) + { + status = sanei_scsi_cmd (fd, test_unit_ready, + sizeof (test_unit_ready), 0, 0); + if (status == SANE_STATUS_GOOD) + return status; + + if (status == SANE_STATUS_DEVICE_BUSY) + { + sleep (1); + continue; + } + + /* status != GOOD && != BUSY */ + DBG (9, "wait_ready: '%s'\n", sane_strstatus (status)); + return status; + } + + /* BUSY after n retries */ + DBG (9, "wait_ready: '%s'\n", sane_strstatus (status)); + return status; +} + +/* DB added a abort routine, executed via mode select */ +static SANE_Status +abort_scan (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + uint8_t *data, comm[22]; + + DBG (7, "abort_scan()\n"); + memset (comm, 0, sizeof (comm)); + + comm[0] = 0x15; + comm[1] = 0x10; + comm[2] = 0x00; + comm[3] = 0x00; + comm[4] = 0x10; + comm[5] = 0x00; + + data = comm + 6; + data[0] = 0x00; /* mode data length */ + data[1] = 0x00; /* medium type */ + data[2] = 0x00; /* device specific parameter */ + data[3] = 0x00; /* block descriptor length */ + + data = comm + 10; + data[0] = 0x00; /* control page parameters */ + data[1] = 0x0a; /* parameter length */ + data[2] = 0x02 | ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) | + ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01); + data[3] = 0x00; /* reserved */ + data[4] = 0x00; /* reserved */ + + DBG (9, "abort: sending abort command\n"); + sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0); + + DBG (9, "abort: wait for scanner to come ready...\n"); + wait_ready (s->fd); + + DBG (9, "abort: resetting abort status\n"); + data[2] = ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) | + ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01); + sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0); + + DBG (9, "abort: wait for scanner to come ready...\n"); + return wait_ready (s->fd); +} + +/* DAL - mode_select: used for transparency and ADF scanning */ +/* Based on abort_scan */ +static SANE_Status +artec_mode_select (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + uint8_t *data, comm[22]; + + DBG (7, "artec_mode_select()\n"); + memset (comm, 0, sizeof (comm)); + + comm[0] = 0x15; + comm[1] = 0x10; + comm[2] = 0x00; + comm[3] = 0x00; + comm[4] = 0x10; + comm[5] = 0x00; + + data = comm + 6; + data[0] = 0x00; /* mode data length */ + data[1] = 0x00; /* medium type */ + data[2] = 0x00; /* device specific parameter */ + data[3] = 0x00; /* block descriptor length */ + + data = comm + 10; + data[0] = 0x00; /* control page parameters */ + data[1] = 0x0a; /* parameter length */ + data[2] = ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) | + ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01); + data[3] = 0x00; /* reserved */ + data[4] = 0x00; /* reserved */ + + DBG (9, "artec_mode_select: mode %d\n", data[2]); + DBG (9, "artec_mode_select: sending mode command\n"); + sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0); + + DBG (9, "artec_mode_select: wait for scanner to come ready...\n"); + return wait_ready (s->fd); +} + + +static SANE_Status +read_data (int fd, int data_type_code, u_char * dest, size_t * len) +{ + static u_char read_6[10]; + + DBG (7, "read_data()\n"); + + memset (read_6, 0, sizeof (read_6)); + read_6[0] = 0x28; + read_6[2] = data_type_code; + read_6[6] = *len >> 16; + read_6[7] = *len >> 8; + read_6[8] = *len; + + return (sanei_scsi_cmd (fd, read_6, sizeof (read_6), dest, len)); +} + +static int +artec_get_status (int fd) +{ + u_char write_10[10]; + u_char read_12[12]; + size_t nread; + + DBG (7, "artec_get_status()\n"); + + nread = 12; + + memset (write_10, 0, 10); + write_10[0] = 0x34; + write_10[8] = 0x0c; + + sanei_scsi_cmd (fd, write_10, 10, read_12, &nread); + + nread = (read_12[9] << 16) + (read_12[10] << 8) + read_12[11]; + DBG (9, "artec_status: %lu\n", (u_long) nread); + + return (nread); +} + +static SANE_Status +artec_reverse_line (SANE_Handle handle, SANE_Byte * data) +{ + ARTEC_Scanner *s = handle; + SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */ + SANE_Byte *to, *from; + int len; + + DBG (8, "artec_reverse_line()\n"); + + len = s->params.bytes_per_line; + memcpy (tmp_buf, data, len); + + if (s->params.format == SANE_FRAME_RGB) /* RGB format */ + { + for (from = tmp_buf, to = data + len - 3; + to >= data; + to -= 3, from += 3) + { + *(to + 0) = *(from + 0); /* copy the R byte */ + *(to + 1) = *(from + 1); /* copy the G byte */ + *(to + 2) = *(from + 2); /* copy the B byte */ + } + } + else if (s->params.format == SANE_FRAME_GRAY) + { + if (s->params.depth == 8) /* 256 color gray-scale */ + { + for (from = tmp_buf, to = data + len; to >= data; to--, from++) + { + *to = *from; + } + } + else if (s->params.depth == 1) /* line art or halftone */ + { + for (from = tmp_buf, to = data + len; to >= data; to--, from++) + { + *to = (((*from & 0x01) << 7) | + ((*from & 0x02) << 5) | + ((*from & 0x04) << 3) | + ((*from & 0x08) << 1) | + ((*from & 0x10) >> 1) | + ((*from & 0x20) >> 3) | + ((*from & 0x40) >> 5) | + ((*from & 0x80) >> 7)); + } + } + } + + return (SANE_STATUS_GOOD); +} + + +#if 0 +static SANE_Status +artec_byte_rgb_to_line_rgb (SANE_Byte * data, SANE_Int len) +{ + SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */ + int count, from; + + DBG (8, "artec_byte_rgb_to_line_rgb()\n"); + + /* copy the RGBRGBRGBRGBRGB... formated data to our temp buffer */ + memcpy (tmp_buf, data, len * 3); + + /* now copy back to *data in RRRRRRRGGGGGGGBBBBBBB format */ + for (count = 0, from = 0; count < len; count++, from += 3) + { + data[count] = tmp_buf[from]; /* R byte */ + data[count + len] = tmp_buf[from + 1]; /* G byte */ + data[count + (len * 2)] = tmp_buf[from + 2]; /* B byte */ + } + + return (SANE_STATUS_GOOD); +} +#endif + +static SANE_Status +artec_line_rgb_to_byte_rgb (SANE_Byte * data, SANE_Int len) +{ + SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */ + int count, to; + + DBG (8, "artec_line_rgb_to_byte_rgb()\n"); + + /* copy the rgb data to our temp buffer */ + memcpy (tmp_buf, data, len * 3); + + /* now copy back to *data in RGB format */ + for (count = 0, to = 0; count < len; count++, to += 3) + { + data[to] = tmp_buf[count]; /* R byte */ + data[to + 1] = tmp_buf[count + len]; /* G byte */ + data[to + 2] = tmp_buf[count + (len * 2)]; /* B byte */ + } + + return (SANE_STATUS_GOOD); +} + +static SANE_Byte **line_buffer = NULL; +static SANE_Byte *tmp_line_buf = NULL; +static SANE_Int r_buf_lines; +static SANE_Int g_buf_lines; + +static SANE_Status +artec_buffer_line_offset (SANE_Handle handle, SANE_Int line_offset, + SANE_Byte * data, size_t * len) +{ + ARTEC_Scanner *s = handle; + static SANE_Int width; + static SANE_Int cur_line; + SANE_Byte *tmp_buf_ptr; + SANE_Byte *grn_ptr; + SANE_Byte *blu_ptr; + SANE_Byte *out_ptr; + int count; + + DBG (8, "artec_buffer_line_offset()\n"); + + if (*len == 0) + return (SANE_STATUS_GOOD); + + if (tmp_line_buf == NULL) + { + width = *len / 3; + cur_line = 0; + + DBG (9, "buffer_line_offset: offset = %d, len = %lu\n", + line_offset, (u_long) * len); + + tmp_line_buf = malloc (*len); + if (tmp_line_buf == NULL) + { + DBG (1, "couldn't allocate memory for temp line buffer\n"); + return (SANE_STATUS_NO_MEM); + } + + r_buf_lines = line_offset * 2; + g_buf_lines = line_offset; + + line_buffer = malloc (r_buf_lines * sizeof (SANE_Byte *)); + if (line_buffer == NULL) + { + DBG (1, "couldn't allocate memory for line buffer pointers\n"); + return (SANE_STATUS_NO_MEM); + } + + for (count = 0; count < r_buf_lines; count++) + { + line_buffer[count] = malloc ((*len) * sizeof (SANE_Byte)); + if (line_buffer[count] == NULL) + { + DBG (1, "couldn't allocate memory for line buffer %d\n", + count); + return (SANE_STATUS_NO_MEM); + } + } + + DBG (9, "buffer_line_offset: r lines = %d, g lines = %d\n", + r_buf_lines, g_buf_lines); + } + + cur_line++; + + if (r_buf_lines > 0) + { + if (cur_line > r_buf_lines) + { + /* copy the Red and Green portions out of the buffer */ + /* if scanner returns RRRRRRRRGGGGGGGGGBBBBBBBB format it's easier */ + if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT) + { + /* get the red line info from r_buf_lines ago */ + memcpy (tmp_line_buf, line_buffer[0], width); + + /* get the green line info from g_buf_lines ago */ + memcpy (tmp_line_buf + width, &line_buffer[line_offset][width], + width); + } + else + { + /* get the red line info from r_buf_lines ago as a whole line */ + memcpy (tmp_line_buf, line_buffer[0], *len); + + /* scanner returns RGBRGBRGB format so we do a loop for green */ + grn_ptr = &line_buffer[line_offset][1]; + out_ptr = tmp_line_buf + 1; + for (count = 0; count < width; count++) + { + *out_ptr = *grn_ptr; /* copy green pixel */ + + grn_ptr += 3; + out_ptr += 3; + } + } + } + + /* move all the buffered lines down (just move the ptrs for speed) */ + tmp_buf_ptr = line_buffer[0]; + for (count = 0; count < (r_buf_lines - 1); count++) + { + line_buffer[count] = line_buffer[count + 1]; + } + line_buffer[r_buf_lines - 1] = tmp_buf_ptr; + + /* insert the new line data at the end of our FIFO */ + memcpy (line_buffer[r_buf_lines - 1], data, *len); + + if (cur_line > r_buf_lines) + { + /* copy the Red and Green portions out of the buffer */ + /* if scanner returns RRRRRRRRGGGGGGGGGBBBBBBBB format it's easier */ + if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT) + { + /* copy the red and green data in with the original blue */ + memcpy (data, tmp_line_buf, width * 2); + } + else + { + /* scanner returns RGBRGBRGB format so we have to do a loop */ + /* copy the blue data into our temp buffer then copy full */ + /* temp buffer overtop of input data */ + if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) + { + blu_ptr = data; + out_ptr = tmp_line_buf; + } + else + { + blu_ptr = data + 2; + out_ptr = tmp_line_buf + 2; + } + + for (count = 0; count < width; count++) + { + *out_ptr = *blu_ptr; /* copy blue pixel */ + + blu_ptr += 3; + out_ptr += 3; + } + + /* now just copy tmp_line_buf back over original data */ + memcpy (data, tmp_line_buf, *len); + } + } + else + { + /* if in the first r_buf_lines, then don't return anything */ + *len = 0; + } + } + + return (SANE_STATUS_GOOD); +} + +static SANE_Status +artec_buffer_line_offset_free (void) +{ + int count; + + DBG (7, "artec_buffer_line_offset_free()\n"); + + free (tmp_line_buf); + tmp_line_buf = NULL; + + for (count = 0; count < r_buf_lines; count++) + { + free (line_buffer[count]); + } + free (line_buffer); + line_buffer = NULL; + + return (SANE_STATUS_GOOD); +} + + +#if 0 +static SANE_Status +artec_read_gamma_table (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + char write_6[4096 + 20]; /* max gamma table is 4096 + 20 for command data */ + char *data; + char prt_buf[128]; + char tmp_buf[128]; + int i; + + DBG (7, "artec_read_gamma_table()\n"); + + memset (write_6, 0, sizeof (*write_6)); + + write_6[0] = 0x28; /* read data code */ + + /* FIXME: AT12 and AM12S use 0x0E for reading all channels of data */ + write_6[2] = 0x03; /* data type code "gamma data" */ + + write_6[6] = (s->gamma_length + 9) >> 16; + write_6[7] = (s->gamma_length + 9) >> 8; + write_6[8] = (s->gamma_length + 9); + + /* FIXME: AT12 and AM12S have one less byte so use 18 */ + if ((!strcmp (s->hw->sane.model, "AT12")) || + (!strcmp (s->hw->sane.model, "AM12S"))) + { + data = write_6 + 18; + } + else + { + data = write_6 + 19; + } + + /* FIXME: AT12 & AM12S ignore this, it's a reserved field */ + write_6[10] = 0x08; /* bitmask, bit 3 means mono type */ + + if (!s->val[OPT_CUSTOM_GAMMA].w) + { + write_6[11] = 1; /* internal gamma table #1 (hope this is default) */ + } + + DBG( 9, "Gamma Table\n" ); + DBG( 9, "==================================\n" ); + + prt_buf[0] = '\0'; + for (i = 0; i < s->gamma_length; i++) + { + if (DBG_LEVEL >= 9) + { + if (!(i % 16)) + { + if ( prt_buf[0] ) + { + strcat( prt_buf, "\n" ); + DBG( 9, "%s", prt_buf ); + } + sprintf (prt_buf, "%02x: ", i); + } + sprintf (tmp_buf, "%02x ", (int) s->gamma_table[0][i]); + strcat (prt_buf, tmp_buf ); + } + + data[i] = s->gamma_table[0][i]; + } + + if ( prt_buf[0] ) + { + strcat( prt_buf, "\n" ); + DBG( 9, "%s", prt_buf ); + } + + if ((!strcmp (s->hw->sane.model, "AT12")) || + (!strcmp (s->hw->sane.model, "AM12S"))) + { + return (sanei_scsi_cmd (s->fd, write_6, 10 + 8 + s->gamma_length, 0, 0)); + } + else + { + return (sanei_scsi_cmd (s->fd, write_6, 10 + 9 + s->gamma_length, 0, 0)); + } +} +#endif + +static SANE_Status +artec_send_gamma_table (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + char write_6[4096 + 20]; /* max gamma table is 4096 + 20 for command data */ + char *data; + char prt_buf[128]; + char tmp_buf[128]; + int i; + + DBG (7, "artec_send_gamma_table()\n"); + + memset (write_6, 0, sizeof (*write_6)); + + write_6[0] = 0x2a; /* send data code */ + + if (s->hw->setwindow_cmd_size > 55) + { + /* newer scanners support sending 3 channels of gamma, or populating all */ + /* 3 channels with same data by using code 0x0e */ + write_6[2] = 0x0e; + } + else + { + /* older scanners only support 1 channel of gamma data using code 0x3 */ + write_6[2] = 0x03; + } + + /* FIXME: AT12 & AM!2S ignore this, it's a reserved field */ + write_6[10] = 0x08; /* bitmask, bit 3 means mono type */ + + if (!s->val[OPT_CUSTOM_GAMMA].w) + { + write_6[6] = 9 >> 16; + write_6[7] = 9 >> 8; + write_6[8] = 9; + write_6[11] = 1; /* internal gamma table #1 (hope this is default) */ + + return (sanei_scsi_cmd (s->fd, write_6, 10 + 9, 0, 0)); + } + else + { + write_6[6] = (s->gamma_length + 9) >> 16; + write_6[7] = (s->gamma_length + 9) >> 8; + write_6[8] = (s->gamma_length + 9); + + DBG( 9, "Gamma Table\n" ); + DBG( 9, "==================================\n" ); + + /* FIXME: AT12 and AM12S have one less byte so use 18 */ + if ((!strcmp (s->hw->sane.model, "AT12")) || + (!strcmp (s->hw->sane.model, "AM12S"))) + { + data = write_6 + 18; + } + else + { + data = write_6 + 19; + } + + prt_buf[0] = '\0'; + for (i = 0; i < s->gamma_length; i++) + { + if (DBG_LEVEL >= 9) + { + if (!(i % 16)) + { + if ( prt_buf[0] ) + { + strcat( prt_buf, "\n" ); + DBG( 9, "%s", prt_buf ); + } + sprintf (prt_buf, "%02x: ", i); + } + sprintf (tmp_buf, "%02x ", (int) s->gamma_table[0][i]); + strcat (prt_buf, tmp_buf ); + } + + data[i] = s->gamma_table[0][i]; + } + + data[s->gamma_length - 1] = 0; + + if ( prt_buf[0] ) + { + strcat( prt_buf, "\n" ); + DBG( 9, "%s", prt_buf ); + } + + /* FIXME: AT12 and AM12S have one less byte so use 18 */ + if ((!strcmp (s->hw->sane.model, "AT12")) || + (!strcmp (s->hw->sane.model, "AM12S"))) + { + return (sanei_scsi_cmd (s->fd, write_6, 10 + 8 + s->gamma_length, 0, 0)); + } + else + { + return (sanei_scsi_cmd (s->fd, write_6, 10 + 9 + s->gamma_length, 0, 0)); + } + } +} + +static SANE_Status +artec_set_scan_window (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + char write_6[4096]; + unsigned char *data; + int counter; + int reversed_x; + int max_x; + + DBG (7, "artec_set_scan_window()\n"); + + /* + * if we can, start before the desired window since we have to throw away + * s->line_offset number of rows because of the RGB fixup. + */ + if ((s->line_offset) && + (s->tl_y) && + (s->tl_y >= (s->line_offset * 2))) + { + s->tl_y -= (s->line_offset * 2); + } + + data = (unsigned char *)write_6 + 10; + + DBG (5, "Scan window info:\n"); + DBG (5, " X resolution: %5d (%d-%d)\n", + s->x_resolution, ARTEC_MIN_X (s->hw), ARTEC_MAX_X (s->hw)); + DBG (5, " Y resolution: %5d (%d-%d)\n", + s->y_resolution, ARTEC_MIN_Y (s->hw), ARTEC_MAX_Y (s->hw)); + DBG (5, " TL_X (pixel): %5d\n", + s->tl_x); + DBG (5, " TL_Y (pixel): %5d\n", + s->tl_y); + DBG (5, " Width : %5d (%d-%d)\n", + s->params.pixels_per_line, + s->hw->x_range.min, + (int) ((SANE_UNFIX (s->hw->x_range.max) / MM_PER_INCH) * + s->x_resolution)); + DBG (5, " Height : %5d (%d-%d)\n", + s->params.lines, + s->hw->y_range.min, + (int) ((SANE_UNFIX (s->hw->y_range.max) / MM_PER_INCH) * + s->y_resolution)); + + DBG (5, " Image Comp. : %s\n", s->mode); + DBG (5, " Line Offset : %lu\n", (u_long) s->line_offset); + + memset (write_6, 0, 4096); + write_6[0] = 0x24; + write_6[8] = s->hw->setwindow_cmd_size; /* total size of command */ + + /* beginning of set window data header */ + /* actual SCSI command data byte count */ + data[7] = s->hw->setwindow_cmd_size - 8; + + /* x resolution */ + data[10] = s->x_resolution >> 8; + data[11] = s->x_resolution; + + /* y resolution */ + data[12] = s->y_resolution >> 8; + data[13] = s->y_resolution; + + if ( s->hw->flags & ARTEC_FLAG_REVERSE_WINDOW ) + { + /* top left X value */ + /* the select area is flipped across the page, so we have to do some */ + /* calculation here to get the the real starting X value */ + max_x = (int) ((SANE_UNFIX (s->hw->x_range.max) / MM_PER_INCH) * + s->x_resolution); + reversed_x = max_x - s->tl_x - s->params.pixels_per_line; + + data[14] = reversed_x >> 24; + data[15] = reversed_x >> 16; + data[16] = reversed_x >> 8; + data[17] = reversed_x; + } + else + { + /* top left X value */ + data[14] = s->tl_x >> 24; + data[15] = s->tl_x >> 16; + data[16] = s->tl_x >> 8; + data[17] = s->tl_x; + } + + /* top left Y value */ + data[18] = s->tl_y >> 24; + data[19] = s->tl_y >> 16; + data[20] = s->tl_y >> 8; + data[21] = s->tl_y; + + + /* width */ + data[22] = s->params.pixels_per_line >> 24; + data[23] = s->params.pixels_per_line >> 16; + data[24] = s->params.pixels_per_line >> 8; + data[25] = s->params.pixels_per_line; + + /* height */ + data[26] = (s->params.lines + (s->line_offset * 2)) >> 24; + data[27] = (s->params.lines + (s->line_offset * 2)) >> 16; + data[28] = (s->params.lines + (s->line_offset * 2)) >> 8; + data[29] = (s->params.lines + (s->line_offset * 2)); + + /* misc. single-byte settings */ + /* brightness */ + if (s->hw->flags & ARTEC_FLAG_OPT_BRIGHTNESS) + data[30] = s->val[OPT_BRIGHTNESS].w; + + data[31] = s->val[OPT_THRESHOLD].w; /* threshold */ + + /* contrast */ + if (s->hw->flags & ARTEC_FLAG_OPT_CONTRAST) + data[32] = s->val[OPT_CONTRAST].w; + + /* + * byte 33 is mode + * byte 37 bit 7 is "negative" setting + */ + if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) + { + data[33] = ARTEC_COMP_LINEART; + data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80; + } + else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) + { + data[33] = ARTEC_COMP_HALFTONE; + data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80; + } + else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + data[33] = ARTEC_COMP_GRAY; + data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0; + } + else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + data[33] = ARTEC_COMP_COLOR; + data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0; + } + + data[34] = s->params.depth; /* bits per pixel */ + + if (s->hw->flags & ARTEC_FLAG_HALFTONE_PATTERN) + { + data[35] = artec_get_str_index (halftone_pattern_list, + s->val[OPT_HALFTONE_PATTERN].s); /* halftone pattern */ + } + + /* user supplied halftone pattern not supported for now so override with */ + /* 8x8 Bayer */ + if (data[35] == 0) + { + data[35] = 4; + } + + /* NOTE: AT12 doesn't support mono according to docs. */ + data[48] = artec_get_str_index (filter_type_list, + s->val[OPT_FILTER_TYPE].s); /* filter mode */ + + if (s->hw->setwindow_cmd_size > 55) + { + data[48] = 0x2; /* DB filter type green for AT12,see above */ + + if (s->hw->flags & ARTEC_FLAG_SC_BUFFERS_LINES) + { + /* FIXME: guessing at this value, use formula instead */ + data[55] = 0x00; /* buffer full line count */ + data[56] = 0x00; /* buffer full line count */ + data[57] = 0x00; /* buffer full line count */ + data[58] = 0x0a; /* buffer full line count */ + + /* FIXME: guessing at this value, use formula instead */ + data[59] = 0x00; /* access line count */ + data[60] = 0x00; /* access line count */ + data[61] = 0x00; /* access line count */ + data[62] = 0x0a; /* access line count */ + } + + if (s->hw->flags & ARTEC_FLAG_SC_HANDLES_OFFSET) + { + /* DB : following fields : high order bit (0x80) is enable */ + /* scanner handles line offset fixup, 0 = driver handles */ + data[63] = 0x80; + } + + if ((s->hw->flags & ARTEC_FLAG_PIXEL_AVERAGING) && + (s->val[OPT_PIXEL_AVG].w)) + { + /* enable pixel average function */ + data[64] = 0x80; + } + else + { + /* disable pixel average function */ + data[64] = 0; + } + + if ((s->hw->flags & ARTEC_FLAG_ENHANCE_LINE_EDGE) && + (s->val[OPT_EDGE_ENH].w)) + { + /* enable lineart edge enhancement function */ + data[65] = 0x80; + } + else + { + /* disable lineart edge enhancement function */ + data[65] = 0; + } + + /* data is R-G-B format, 0x80 = G-B-R format (reversed) */ + data[66] = 0; + } + + DBG (50, "Set Window data : \n"); + for (counter = 0; counter < s->hw->setwindow_cmd_size; counter++) + { + DBG (50, " byte %2d = %02x \n", counter, data[counter] & 0xff); /* DB */ + } + DBG (50, "\n"); + + /* set the scan window */ + return (sanei_scsi_cmd (s->fd, write_6, 10 + + s->hw->setwindow_cmd_size, 0, 0)); +} + +static SANE_Status +artec_start_scan (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + char write_7[7]; + + DBG (7, "artec_start_scan()\n"); + + /* setup cmd to start scanning */ + memset (write_7, 0, 7); + write_7[0] = 0x1b; /* code to start scan */ + + /* FIXME: need to make this a flag */ + if (!strcmp (s->hw->sane.model, "AM12S")) + { + /* start the scan */ + return (sanei_scsi_cmd (s->fd, write_7, 6, 0, 0)); + } + else + { + write_7[4] = 0x01; /* need to send 1 data byte */ + + /* start the scan */ + return (sanei_scsi_cmd (s->fd, write_7, 7, 0, 0)); + } +} + +static SANE_Status +artec_software_rgb_calibrate (SANE_Handle handle, SANE_Byte * buf, int lines) +{ + ARTEC_Scanner *s = handle; + int line, i, loop, offset; + + DBG (7, "artec_software_rgb_calibrate()\n"); + + for (line = 0; line < lines; line++) + { + i = 0; + offset = 0; + + if (s->x_resolution == 200) + { + /* skip ever 3rd byte, -= causes us to go down in count */ + if ((s->tl_x % 3) == 0) + offset -= 1; + } + else + { + /* round down to the previous pixel */ + offset += ((s->tl_x / (300 / s->x_resolution)) * + (300 / s->x_resolution)); + } + + for (loop = 0; loop < s->params.pixels_per_line; loop++) + { + if ((DBG_LEVEL == 100) && + (loop < 100)) + { + DBG (100, " %2d-%4d R (%4d,%4d): %d * %5.2f = %d\n", + line, loop, i, offset, buf[i], + s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset], + (int) (buf[i] * + s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset])); + } + buf[i] = buf[i] * + s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset]; + i++; + + if ((DBG_LEVEL == 100) && + (loop < 100)) + { + DBG (100, " G (%4d,%4d): %d * %5.2f = %d\n", + i, offset, buf[i], + s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset], + (int) (buf[i] * + s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset])); + } + buf[i] = buf[i] * + s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset]; + i++; + + if ((DBG_LEVEL == 100) && + (loop < 100)) + { + DBG (100, " B (%4d,%4d): %d * %5.2f = %d\n", + i, offset, buf[i], + s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset], + (int) (buf[i] * + s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset])); + } + buf[i] = buf[i] * + s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset]; + i++; + + if (s->x_resolution == 200) + { + offset += 1; + + /* skip every 3rd byte */ + if (((offset + 1) % 3) == 0) + offset += 1; + } + else + { + offset += (300 / s->x_resolution); + } + } + } + + return (SANE_STATUS_GOOD); +} + +static SANE_Status +artec_calibrate_shading (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + SANE_Status status; /* DB added */ + u_char buf[76800]; /* should be big enough */ + size_t len; + SANE_Word save_x_resolution; + SANE_Word save_pixels_per_line; + int i; + + DBG (7, "artec_calibrate_shading()\n"); + + if (s->hw->flags & ARTEC_FLAG_CALIBRATE_RGB) + { + /* this method scans in 4 lines each of Red, Green, and Blue */ + /* after reading line of shading data, generate data for software */ + /* calibration so we have it if user requests */ + len = 4 * 2592; /* 4 lines of data, 2592 pixels wide */ + + if ( DBG_LEVEL == 100 ) + DBG (100, "RED Software Calibration data\n"); + + read_data (s->fd, ARTEC_DATA_RED_SHADING, buf, &len); + for (i = 0; i < 2592; i++) + { + s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][i] = + 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4); + if (DBG_LEVEL == 100) + { + DBG (100, + " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n", + i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776], + s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][i]); + } + } + + if (DBG_LEVEL == 100) + { + DBG (100, "GREEN Software Calibration data\n"); + } + + read_data (s->fd, ARTEC_DATA_GREEN_SHADING, buf, &len); + for (i = 0; i < 2592; i++) + { + s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][i] = + 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4); + if (DBG_LEVEL == 100) + { + DBG (100, + " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n", + i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776], + s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][i]); + } + } + + if (DBG_LEVEL == 100) + { + DBG (100, "BLUE Software Calibration data\n"); + } + + read_data (s->fd, ARTEC_DATA_BLUE_SHADING, buf, &len); + for (i = 0; i < 2592; i++) + { + s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][i] = + 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4); + if (DBG_LEVEL == 100) + { + DBG (100, + " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n", + i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776], + s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][i]); + } + } + } + else if (s->hw->flags & ARTEC_FLAG_CALIBRATE_DARK_WHITE) + { + /* this method scans black, then white data */ + len = 3 * 5100; /* 1 line of data, 5100 pixels wide, RGB data */ + read_data (s->fd, ARTEC_DATA_DARK_SHADING, buf, &len); + save_x_resolution = s->x_resolution; + s->x_resolution = 600; + save_pixels_per_line = s->params.pixels_per_line; + s->params.pixels_per_line = ARTEC_MAX_X (s->hw); + s->params.pixels_per_line = 600 * 8.5; /* ?this? or ?above line? */ + /* DB added wait_ready */ + status = wait_ready (s->fd); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "wait for scanner ready failed: %s\n", sane_strstatus (status)); + return status; + } + /* next line should use ARTEC_DATA_WHITE_SHADING_TRANS if using ADF */ + read_data (s->fd, ARTEC_DATA_WHITE_SHADING_OPT, buf, &len); + s->x_resolution = save_x_resolution; + s->params.pixels_per_line = save_pixels_per_line; + } + + return (SANE_STATUS_GOOD); +} + + +static SANE_Status +end_scan (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + /* DB + uint8_t write_6[6] = + {0x1B, 0, 0, 0, 0, 0}; + */ + + DBG (7, "end_scan()\n"); + + s->scanning = SANE_FALSE; + +/* if (s->this_pass == 3) */ + s->this_pass = 0; + + if ((s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) && + (tmp_line_buf != NULL)) + { + artec_buffer_line_offset_free (); + } + + /* DB + return (sanei_scsi_cmd (s->fd, write_6, 6, 0, 0)); + */ + return abort_scan (s); +} + + +static SANE_Status +artec_get_cap_data (ARTEC_Device * dev, int fd) +{ + int cap_model, loop; + SANE_Status status; + u_char cap_buf[256]; /* buffer for cap data */ + + DBG (7, "artec_get_cap_data()\n"); + + /* DB always use the hard-coded capability info first + * if we get cap data from the scanner, we override */ + cap_model = -1; + for (loop = 0; loop < NELEMS (cap_data); loop++) + { + if (strcmp (cap_data[loop].model, dev->sane.model) == 0) + { + cap_model = loop; + } + } + + if (cap_model == -1) + { + DBG (1, "unable to identify Artec model '%s', check artec.c\n", + dev->sane.model); + return (SANE_STATUS_UNSUPPORTED); + } + + dev->x_range.min = 0; + dev->x_range.max = SANE_FIX (cap_data[cap_model].width) * MM_PER_INCH; + dev->x_range.quant = 1; + + dev->width = cap_data[cap_model].width; + + dev->y_range.min = 0; + dev->y_range.max = SANE_FIX (cap_data[cap_model].height) * MM_PER_INCH; + dev->y_range.quant = 1; + + dev->height = cap_data[cap_model].height; + + status = artec_str_list_to_word_list (&dev->horz_resolution_list, + cap_data[cap_model].horz_resolution_str); + + status = artec_str_list_to_word_list (&dev->vert_resolution_list, + cap_data[cap_model].vert_resolution_str); + + dev->contrast_range.min = 0; + dev->contrast_range.max = 255; + dev->contrast_range.quant = 1; + + dev->brightness_range.min = 0; + dev->brightness_range.max = 255; + dev->brightness_range.quant = 1; + + dev->threshold_range.min = 0; + dev->threshold_range.max = 255; + dev->threshold_range.quant = 1; + + dev->sane.type = cap_data[cap_model].type; + + dev->max_read_size = cap_data[cap_model].max_read_size; + + dev->flags = cap_data[cap_model].flags; + + switch (cap_data[cap_model].adc_bits) + { + case 8: + dev->gamma_length = 256; + break; + + case 10: + dev->gamma_length = 1024; + break; + + case 12: + dev->gamma_length = 4096; + break; + } + + dev->setwindow_cmd_size = cap_data[cap_model].setwindow_cmd_size; + + if (dev->support_cap_data_retrieve) /* DB */ + { + /* DB added reading capability data from scanner */ + char info[80]; /* for printing debugging info */ + size_t len = sizeof (cap_buf); + + /* read the capability data from the scanner */ + DBG (9, "reading capability data from scanner...\n"); + + wait_ready (fd); + + read_data (fd, ARTEC_DATA_CAPABILITY_DATA, cap_buf, &len); + + DBG (50, "scanner capability data : \n"); + strncpy (info, (const char *) &cap_buf[0], 8); + info[8] = '\0'; + DBG (50, " Vendor : %s\n", info); + strncpy (info, (const char *) &cap_buf[8], 16); + info[16] = '\0'; + DBG (50, " Device Name : %s\n", info); + strncpy (info, (const char *) &cap_buf[24], 4); + info[4] = '\0'; + DBG (50, " Version Number : %s\n", info); + sprintf (info, "%d ", cap_buf[29]); + DBG (50, " CCD Type : %s\n", info); + sprintf (info, "%d ", cap_buf[30]); + DBG (50, " AD Converter Type : %s\n", info); + sprintf (info, "%d ", (cap_buf[31] << 8) | cap_buf[32]); + DBG (50, " Buffer size : %s\n", info); + sprintf (info, "%d ", cap_buf[33]); + DBG (50, " Channels of RGB Gamma : %s\n", info); + sprintf (info, "%d ", (cap_buf[34] << 8) | cap_buf[35]); + DBG (50, " Opt. res. of R channel : %s\n", info); + sprintf (info, "%d ", (cap_buf[36] << 8) | cap_buf[37]); + DBG (50, " Opt. res. of G channel : %s\n", info); + sprintf (info, "%d ", (cap_buf[38] << 8) | cap_buf[39]); + DBG (50, " Opt. res. of B channel : %s\n", info); + sprintf (info, "%d ", (cap_buf[40] << 8) | cap_buf[41]); + DBG (50, " Min. Hor. Resolution : %s\n", info); + sprintf (info, "%d ", (cap_buf[42] << 8) | cap_buf[43]); + DBG (50, " Max. Vert. Resolution : %s\n", info); + sprintf (info, "%d ", (cap_buf[44] << 8) | cap_buf[45]); + DBG (50, " Min. Vert. Resolution : %s\n", info); + sprintf (info, "%s ", cap_buf[46] == 0x80 ? "yes" : "no"); + DBG (50, " Chunky Data Format : %s\n", info); + sprintf (info, "%s ", cap_buf[47] == 0x80 ? "yes" : "no"); + DBG (50, " RGB Data Format : %s\n", info); + sprintf (info, "%s ", cap_buf[48] == 0x80 ? "yes" : "no"); + DBG (50, " BGR Data Format : %s\n", info); + sprintf (info, "%d ", cap_buf[49]); + DBG (50, " Line Offset : %s\n", info); + sprintf (info, "%s ", cap_buf[50] == 0x80 ? "yes" : "no"); + DBG (50, " Channel Valid Sequence : %s\n", info); + sprintf (info, "%s ", cap_buf[51] == 0x80 ? "yes" : "no"); + DBG (50, " True Gray : %s\n", info); + sprintf (info, "%s ", cap_buf[52] == 0x80 ? "yes" : "no"); + DBG (50, " Force Host Not Do Shading : %s\n", info); + sprintf (info, "%s ", cap_buf[53] == 0x00 ? "AT006" : "AT010"); + DBG (50, " ASIC : %s\n", info); + sprintf (info, "%s ", cap_buf[54] == 0x82 ? "SCSI2" : + cap_buf[54] == 0x81 ? "SCSI1" : "Parallel"); + DBG (50, " Interface : %s\n", info); + sprintf (info, "%d ", (cap_buf[55] << 8) | cap_buf[56]); + DBG (50, " Phys. Area Width : %s\n", info); + sprintf (info, "%d ", (cap_buf[57] << 8) | cap_buf[58]); + DBG (50, " Phys. Area Length : %s\n", info); + + /* fill in the information we've got from the scanner */ + + dev->width = ((float) ((cap_buf[55] << 8) | cap_buf[56])) / 1000; + dev->height = ((float) ((cap_buf[57] << 8) | cap_buf[58])) / 1000; + + /* DB ----- */ + } + + DBG (9, "Scanner capability info.\n"); + DBG (9, " Vendor : %s\n", dev->sane.vendor); + DBG (9, " Model : %s\n", dev->sane.model); + DBG (9, " Type : %s\n", dev->sane.type); + DBG (5, " Width : %.2f inches\n", dev->width); + DBG (9, " Height : %.2f inches\n", dev->height); + DBG (9, " X Range(mm) : %d-%d\n", + dev->x_range.min, + (int) (SANE_UNFIX (dev->x_range.max))); + DBG (9, " Y Range(mm) : %d-%d\n", + dev->y_range.min, + (int) (SANE_UNFIX (dev->y_range.max))); + + DBG (9, " Horz. DPI : %d-%d\n", ARTEC_MIN_X (dev), ARTEC_MAX_X (dev)); + DBG (9, " Vert. DPI : %d-%d\n", ARTEC_MIN_Y (dev), ARTEC_MAX_Y (dev)); + DBG (9, " Contrast : %d-%d\n", + dev->contrast_range.min, dev->contrast_range.max); + DBG (9, " REQ Sh. Cal.: %d\n", + dev->flags & ARTEC_FLAG_CALIBRATE ? 1 : 0); + DBG (9, " REQ Ln. Offs: %d\n", + dev->flags & ARTEC_FLAG_RGB_LINE_OFFSET ? 1 : 0); + DBG (9, " REQ Ch. Shft: %d\n", + dev->flags & ARTEC_FLAG_RGB_CHAR_SHIFT ? 1 : 0); + DBG (9, " SetWind Size: %d\n", + dev->setwindow_cmd_size); + DBG (9, " Calib Method: %s\n", + dev->flags & ARTEC_FLAG_CALIBRATE_RGB ? "RGB" : + dev->flags & ARTEC_FLAG_CALIBRATE_DARK_WHITE ? "white/black" : "N/A"); + + return (SANE_STATUS_GOOD); +} + +static SANE_Status +dump_inquiry (unsigned char *result) +{ + int i; + int j; + char prt_buf[129] = ""; + char tmp_buf[129]; + + DBG (4, "dump_inquiry()\n"); + + DBG (4, " === SANE/Artec backend v%d.%d.%d ===\n", + ARTEC_MAJOR, ARTEC_MINOR, ARTEC_SUB); + DBG (4, " ===== Scanner Inquiry Block =====\n"); + for (i = 0; i < 96; i += 16) + { + sprintf (prt_buf, "0x%02x: ", i); + for (j = 0; j < 16; j++) + { + sprintf (tmp_buf, "%02x ", (int) result[i + j]); + strcat( prt_buf, tmp_buf ); + } + strcat( prt_buf, " "); + for (j = 0; j < 16; j++) + { + sprintf (tmp_buf, "%c", + isprint (result[i + j]) ? result[i + j] : '.'); + strcat( prt_buf, tmp_buf ); + } + strcat( prt_buf, "\n" ); + DBG(4, "%s", prt_buf ); + } + + return (SANE_STATUS_GOOD); +} + +static SANE_Status +attach (const char *devname, ARTEC_Device ** devp) +{ + char result[INQ_LEN]; + char product_revision[5]; + char temp_result[33]; + char *str, *t; + int fd; + SANE_Status status; + ARTEC_Device *dev; + size_t size; + + DBG (7, "attach()\n"); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devname) == 0) + { + if (devp) + *devp = dev; + return (SANE_STATUS_GOOD); + } + } + + DBG (6, "attach: opening %s\n", devname); + + status = sanei_scsi_open (devname, &fd, sense_handler, NULL); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "attach: open failed (%s)\n", sane_strstatus (status)); + return (SANE_STATUS_INVAL); + } + + DBG (6, "attach: sending INQUIRY\n"); + size = sizeof (result); + status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size); + if (status != SANE_STATUS_GOOD || size < 16) + { + DBG (1, "attach: inquiry failed (%s)\n", sane_strstatus (status)); + sanei_scsi_close (fd); + return (status); + } + + /* + * Check to see if this device is a scanner. + */ + if (result[0] != 0x6) + { + DBG (1, "attach: device doesn't look like a scanner at all.\n"); + sanei_scsi_close (fd); + return (SANE_STATUS_INVAL); + } + + /* + * The BlackWidow BW4800SP is actually a rebadged AT3, with the vendor + * string set to 8 spaces and the product to "Flatbed Scanner ". So, + * if we have one of these, we'll make it look like an AT3. + * + * For now, to be on the safe side, we'll also check the version number + * since BlackWidow seems to have left that intact as "1.90". + * + * Check that result[36] == 0x00 so we don't mistake a microtek scanner. + */ + if ((result[36] == 0x00) && + (strncmp (result + 32, "1.90", 4) == 0) && + (strncmp (result + 8, " ", 8) == 0) && + (strncmp (result + 16, "Flatbed Scanner ", 16) == 0)) + { + DBG (6, "Found BlackWidow BW4800SP scanner, setting up like AT3\n"); + + /* setup the vendor and product to mimic the Artec/Ultima AT3 */ + strncpy (result + 8, "ULTIMA", 6); + strncpy (result + 16, "AT3 ", 16); + } + + /* + * The Plustek 19200S is actually a rebadged AM12S, with the vendor string + * set to 8 spaces. + */ + if ((strncmp (result + 8, " ", 8) == 0) && + (strncmp (result + 16, "SCAN19200 ", 16) == 0)) + { + DBG (6, "Found Plustek 19200S scanner, setting up like AM12S\n"); + + /* setup the vendor and product to mimic the Artec/Ultima AM12S */ + strncpy (result + 8, "ULTIMA", 6); + strncpy (result + 16, "AM12S ", 16); + } + + /* + * Check to see if they have forced a vendor and/or model string and + * if so, fudge the inquiry results with that info. We do this right + * before we check the inquiry results, otherwise we might not be forcing + * anything. + */ + if (artec_vendor[0] != 0x0) + { + /* + * 1) copy the vendor string to our temp variable + * 2) append 8 spaces to make sure we have at least 8 characters + * 3) copy our fudged vendor string into the inquiry result. + */ + strcpy (temp_result, artec_vendor); + strcat (temp_result, " "); + strncpy (result + 8, temp_result, 8); + } + + if (artec_model[0] != 0x0) + { + /* + * 1) copy the model string to our temp variable + * 2) append 16 spaces to make sure we have at least 16 characters + * 3) copy our fudged model string into the inquiry result. + */ + strcpy (temp_result, artec_model); + strcat (temp_result, " "); + strncpy (result + 16, temp_result, 16); + } + + /* are we really dealing with a scanner by ULTIMA/ARTEC? */ + if ((strncmp (result + 8, "ULTIMA", 6) != 0) && + (strncmp (result + 8, "ARTEC", 5) != 0)) + { + DBG (1, "attach: device doesn't look like a Artec/ULTIMA scanner\n"); + + strncpy (temp_result, result + 8, 8); + temp_result[8] = 0x0; + DBG (1, "attach: FOUND vendor = '%s'\n", temp_result); + strncpy (temp_result, result + 16, 16); + temp_result[16] = 0x0; + DBG (1, "attach: FOUND model = '%s'\n", temp_result); + + sanei_scsi_close (fd); + return (SANE_STATUS_INVAL); + } + + /* turn this wait OFF for now since it appears to cause problems with */ + /* AT12 models */ + /* turned off by creating an "if" that can never be true */ + if ( 1 == 2 ) { + DBG (6, "attach: wait for scanner to come ready\n"); + status = wait_ready (fd); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "attach: test unit ready failed (%s)\n", + sane_strstatus (status)); + sanei_scsi_close (fd); + return (status); + } + /* This is the end of the "if" that can never be true that in effect */ + /* comments out this wait_ready() call */ + } + /* end of "if( 1 == 2 )" */ + + dev = malloc (sizeof (*dev)); + if (!dev) + return (SANE_STATUS_NO_MEM); + + memset (dev, 0, sizeof (*dev)); + + if (DBG_LEVEL >= 4) + dump_inquiry ((unsigned char *) result); + + dev->sane.name = strdup (devname); + + /* get the model info */ + str = malloc (17); + memcpy (str, result + 16, 16); + str[16] = ' '; + t = str + 16; + while ((*t == ' ') && (t > str)) + { + *t = '\0'; + t--; + } + dev->sane.model = str; + + /* for some reason, the firmware revision is in the model info string on */ + /* the A6000C PLUS scanners instead of in it's proper place */ + if (strstr (str, "A6000C PLUS") == str) + { + str[11] = '\0'; + strncpy (product_revision, str + 12, 4); + } + else if (strstr (str, "AT3") == str) + { + str[3] = '\0'; + strncpy (product_revision, str + 8, 4); + } + else + { + /* get the product revision from it's normal place */ + strncpy (product_revision, result + 32, 4); + } + product_revision[4] = ' '; + t = strchr (product_revision, ' '); + if (t) + *t = '\0'; + else + t = "unknown revision"; + + /* get the vendor info */ + str = malloc (9); + memcpy (str, result + 8, 8); + str[8] = ' '; + t = strchr (str, ' '); + *t = '\0'; + dev->sane.vendor = str; + + DBG (5, "scanner vendor: '%s', model: '%s', revision: '%s'\n", + dev->sane.vendor, dev->sane.model, product_revision); + + /* Artec docs say if bytes 36-43 = "ULTIMA ", then supports read cap. data */ + if (strncmp (result + 36, "ULTIMA ", 8) == 0) + { + DBG (5, "scanner supports read capability data function\n"); + dev->support_cap_data_retrieve = SANE_TRUE; + } + else + { + DBG (5, "scanner does NOT support read capability data function\n"); + dev->support_cap_data_retrieve = SANE_FALSE; + } + + DBG (6, "attach: getting scanner capability data\n"); + status = artec_get_cap_data (dev, fd); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "attach: artec_get_cap_data failed (%s)\n", + sane_strstatus (status)); + sanei_scsi_close (fd); + return (status); + } + + sanei_scsi_close (fd); + + ++num_devices; + dev->next = first_dev; + first_dev = dev; + + if (devp) + *devp = dev; + + return (SANE_STATUS_GOOD); +} + +static SANE_Status +init_options (ARTEC_Scanner * s) +{ + int i; + + DBG (7, "init_options()\n"); + + memset (s->opt, 0, sizeof (s->opt)); + memset (s->val, 0, sizeof (s->val)); + + for (i = 0; i < NUM_OPTIONS; ++i) + { + s->opt[i].size = sizeof (SANE_Word); + s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; + + /* "Mode" group: */ + s->opt[OPT_MODE_GROUP].title = "Scan Mode"; + s->opt[OPT_MODE_GROUP].desc = ""; + s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + s->opt[OPT_MODE_GROUP].cap = 0; + s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* scan mode */ + s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + s->opt[OPT_MODE].type = SANE_TYPE_STRING; + s->opt[OPT_MODE].size = max_string_size (mode_list); + s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_MODE].constraint.string_list = mode_list; + s->val[OPT_MODE].s = strdup (mode_list[3]); + + /* horizontal resolution */ + s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; + s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; + s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; + s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->horz_resolution_list; + s->val[OPT_X_RESOLUTION].w = 100; + + /* vertical resolution */ + s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; + s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; + s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION; + s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; + s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; + s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; + s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->vert_resolution_list; + s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; + s->val[OPT_Y_RESOLUTION].w = 100; + + /* bind resolution */ + s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND; + s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND; + s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND; + s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL; + s->val[OPT_RESOLUTION_BIND].w = SANE_TRUE; + + if (!(s->hw->flags & ARTEC_FLAG_SEPARATE_RES)) + s->opt[OPT_RESOLUTION_BIND].cap |= SANE_CAP_INACTIVE; + + /* Preview Mode */ + s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE; + s->opt[OPT_PREVIEW].size = sizeof (SANE_Word); + s->val[OPT_PREVIEW].w = SANE_FALSE; + + /* Grayscale Preview Mode */ + s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; + s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; + s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; + s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; + s->opt[OPT_GRAY_PREVIEW].unit = SANE_UNIT_NONE; + s->opt[OPT_GRAY_PREVIEW].size = sizeof (SANE_Word); + s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; + + /* negative */ + s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE; + s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE; + s->opt[OPT_NEGATIVE].desc = "Negative Image"; + s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL; + s->val[OPT_NEGATIVE].w = SANE_FALSE; + + if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE)) + { + s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE; + } + + /* "Geometry" group: */ + s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; + s->opt[OPT_GEOMETRY_GROUP].desc = ""; + s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; + s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* top-left x */ + s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; + s->opt[OPT_TL_X].unit = SANE_UNIT_MM; + + s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; + s->val[OPT_TL_X].w = s->hw->x_range.min; + + /* top-left y */ + s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; + s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; + + s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; + s->val[OPT_TL_Y].w = s->hw->y_range.min; + + /* bottom-right x */ + s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; + s->opt[OPT_BR_X].unit = SANE_UNIT_MM; + + s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; + s->val[OPT_BR_X].w = s->hw->x_range.max; + + /* bottom-right y */ + s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; + s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; + + s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; + s->val[OPT_BR_Y].w = s->hw->y_range.max; + + /* Enhancement group: */ + s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; + s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; + s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; + s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; + s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* filter mode */ + s->opt[OPT_FILTER_TYPE].name = "filter-type"; + s->opt[OPT_FILTER_TYPE].title = "Filter Type"; + s->opt[OPT_FILTER_TYPE].desc = "Filter Type for mono scans"; + s->opt[OPT_FILTER_TYPE].type = SANE_TYPE_STRING; + s->opt[OPT_FILTER_TYPE].size = max_string_size (filter_type_list); + s->opt[OPT_FILTER_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_FILTER_TYPE].constraint.string_list = filter_type_list; + s->val[OPT_FILTER_TYPE].s = strdup (filter_type_list[0]); + s->opt[OPT_FILTER_TYPE].cap |= SANE_CAP_INACTIVE; + + /* contrast */ + s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; + s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; + s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; + s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; + s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; + s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_CONTRAST].constraint.range = &s->hw->brightness_range; + s->val[OPT_CONTRAST].w = 0x80; + + if (!(s->hw->flags & ARTEC_FLAG_OPT_CONTRAST)) + { + s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; + } + + /* brightness */ + s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; + s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; + s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; + s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; + s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; + s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->contrast_range; + s->val[OPT_BRIGHTNESS].w = 0x80; + + if (!(s->hw->flags & ARTEC_FLAG_OPT_BRIGHTNESS)) + { + s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; + } + + /* threshold */ + s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; + s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; + s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; + s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT; + s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; + s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_THRESHOLD].constraint.range = &s->hw->threshold_range; + s->val[OPT_THRESHOLD].w = 0x80; + s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + + /* halftone pattern */ + s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; + s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; + s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; + s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; + s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list); + s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list; + s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[1]); + s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; + + /* pixel averaging */ + s->opt[OPT_PIXEL_AVG].name = "pixel-avg"; + s->opt[OPT_PIXEL_AVG].title = "Pixel Averaging"; + s->opt[OPT_PIXEL_AVG].desc = "Enable HardWare Pixel Averaging function"; + s->opt[OPT_PIXEL_AVG].type = SANE_TYPE_BOOL; + s->val[OPT_PIXEL_AVG].w = SANE_FALSE; + + if (!(s->hw->flags & ARTEC_FLAG_PIXEL_AVERAGING)) + { + s->opt[OPT_PIXEL_AVG].cap |= SANE_CAP_INACTIVE; + } + + /* lineart line edge enhancement */ + s->opt[OPT_EDGE_ENH].name = "edge-enh"; + s->opt[OPT_EDGE_ENH].title = "Line Edge Enhancement"; + s->opt[OPT_EDGE_ENH].desc = "Enable HardWare Lineart Line Edge Enhancement"; + s->opt[OPT_EDGE_ENH].type = SANE_TYPE_BOOL; + s->val[OPT_EDGE_ENH].w = SANE_FALSE; + s->opt[OPT_EDGE_ENH].cap |= SANE_CAP_INACTIVE; + + /* custom-gamma table */ + s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; + s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; + s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; + s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; + s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + /* grayscale gamma vector */ + s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; + s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; + s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; + s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; + s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; + s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; + s->val[OPT_GAMMA_VECTOR].wa = &(s->gamma_table[0][0]); + s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; + s->opt[OPT_GAMMA_VECTOR].size = s->gamma_length * sizeof (SANE_Word); + + /* red gamma vector */ + s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; + s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; + s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; + s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; + s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; + s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; + s->val[OPT_GAMMA_VECTOR_R].wa = &(s->gamma_table[1][0]); + s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->gamma_range); + s->opt[OPT_GAMMA_VECTOR_R].size = s->gamma_length * sizeof (SANE_Word); + + /* green gamma vector */ + s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; + s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; + s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; + s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; + s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; + s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; + s->val[OPT_GAMMA_VECTOR_G].wa = &(s->gamma_table[2][0]); + s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->gamma_range); + s->opt[OPT_GAMMA_VECTOR_G].size = s->gamma_length * sizeof (SANE_Word); + + /* blue gamma vector */ + s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; + s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; + s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; + s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; + s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; + s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; + s->val[OPT_GAMMA_VECTOR_B].wa = &(s->gamma_table[3][0]); + s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->gamma_range); + s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof (SANE_Word); + + if (s->hw->flags & ARTEC_FLAG_GAMMA_SINGLE) + { + s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + } + + if (!(s->hw->flags & ARTEC_FLAG_GAMMA)) + { + s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + } + + /* transparency */ + s->opt[OPT_TRANSPARENCY].name = "transparency"; + s->opt[OPT_TRANSPARENCY].title = "Transparency"; + s->opt[OPT_TRANSPARENCY].desc = "Use transparency adaptor"; + s->opt[OPT_TRANSPARENCY].type = SANE_TYPE_BOOL; + s->val[OPT_TRANSPARENCY].w = SANE_FALSE; + + /* ADF */ + s->opt[OPT_ADF].name = "adf"; + s->opt[OPT_ADF].title = "ADF"; + s->opt[OPT_ADF].desc = "Use ADF"; + s->opt[OPT_ADF].type = SANE_TYPE_BOOL; + s->val[OPT_ADF].w = SANE_FALSE; + + /* Calibration group: */ + s->opt[OPT_CALIBRATION_GROUP].title = "Calibration"; + s->opt[OPT_CALIBRATION_GROUP].desc = ""; + s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP; + s->opt[OPT_CALIBRATION_GROUP].cap = SANE_CAP_ADVANCED; + s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* Calibrate Every Scan? */ + s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL; + s->opt[OPT_QUALITY_CAL].title = "Hardware Calibrate Every Scan"; + s->opt[OPT_QUALITY_CAL].desc = "Perform hardware calibration on every scan"; + s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL; + s->val[OPT_QUALITY_CAL].w = SANE_FALSE; + + if (!(s->hw->flags & ARTEC_FLAG_CALIBRATE)) + { + s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE; + } + + /* Perform Software Quality Calibration */ + s->opt[OPT_SOFTWARE_CAL].name = "software-cal"; + s->opt[OPT_SOFTWARE_CAL].title = "Software Color Calibration"; + s->opt[OPT_SOFTWARE_CAL].desc = "Perform software quality calibration in " + "addition to hardware calibration"; + s->opt[OPT_SOFTWARE_CAL].type = SANE_TYPE_BOOL; + s->val[OPT_SOFTWARE_CAL].w = SANE_FALSE; + + /* check for RGB calibration now because we have only implemented software */ + /* calibration in conjunction with hardware RGB calibration */ + if ((!(s->hw->flags & ARTEC_FLAG_CALIBRATE)) || + (!(s->hw->flags & ARTEC_FLAG_CALIBRATE_RGB))) + { + s->opt[OPT_SOFTWARE_CAL].cap |= SANE_CAP_INACTIVE; + } + + return (SANE_STATUS_GOOD); +} + +static SANE_Status +do_cancel (ARTEC_Scanner * s) +{ + DBG (7, "do_cancel()\n"); + + s->scanning = SANE_FALSE; + + /* DAL: Terminate a three pass scan properly */ +/* if (s->this_pass == 3) */ + s->this_pass = 0; + + if ((s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) && + (tmp_line_buf != NULL)) + { + artec_buffer_line_offset_free (); + } + + if (s->fd >= 0) + { + sanei_scsi_close (s->fd); + s->fd = -1; + } + + return (SANE_STATUS_CANCELLED); +} + + +static SANE_Status +attach_one (const char *dev) +{ + DBG (7, "attach_one()\n"); + + attach (dev, 0); + return (SANE_STATUS_GOOD); +} + + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + char dev_name[PATH_MAX], *cp; + size_t len; + FILE *fp; + + DBG_INIT (); + + DBG (1, "Artec/Ultima backend version %d.%d.%d, last mod: %s\n", + ARTEC_MAJOR, ARTEC_MINOR, ARTEC_SUB, ARTEC_LAST_MOD); + DBG (1, "http://www4.infi.net/~cpinkham/sane-artec-doc.html\n"); + + DBG (7, "sane_init()\n" ); + + devlist = 0; + /* make sure these 2 are empty */ + strcpy (artec_vendor, ""); + strcpy (artec_model, ""); + + if (version_code) + *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0); + + if (authorize) + DBG (7, "sane_init(), authorize %s null\n", (authorize) ? "!=" : "=="); + + fp = sanei_config_open (ARTEC_CONFIG_FILE); + if (!fp) + { + /* default to /dev/scanner instead of insisting on config file */ + attach ("/dev/scanner", 0); + return (SANE_STATUS_GOOD); + } + + while (sanei_config_read (dev_name, sizeof (dev_name), fp)) + { + cp = artec_skip_whitespace (dev_name); + + /* ignore line comments and blank lines */ + if ((!*cp) || (*cp == '#')) + continue; + + len = strlen (cp); + + /* ignore empty lines */ + if (!len) + continue; + + DBG (50, "%s line: '%s', len = %lu\n", ARTEC_CONFIG_FILE, cp, + (u_long) len); + + /* check to see if they forced a vendor string in artec.conf */ + if ((strncmp (cp, "vendor", 6) == 0) && isspace (cp[6])) + { + cp += 7; + cp = artec_skip_whitespace (cp); + + strcpy (artec_vendor, cp); + DBG (5, "sane_init: Forced vendor string '%s' in %s.\n", + cp, ARTEC_CONFIG_FILE); + } + /* OK, maybe they forced the model string in artec.conf */ + else if ((strncmp (cp, "model", 5) == 0) && isspace (cp[5])) + { + cp += 6; + cp = artec_skip_whitespace (cp); + + strcpy (artec_model, cp); + DBG (5, "sane_init: Forced model string '%s' in %s.\n", + cp, ARTEC_CONFIG_FILE); + } + /* well, nothing else to do but attempt the attach */ + else + { + sanei_config_attach_matching_devices (dev_name, attach_one); + strcpy (artec_vendor, ""); + strcpy (artec_model, ""); + } + } + fclose (fp); + + return (SANE_STATUS_GOOD); +} + +void +sane_exit (void) +{ + ARTEC_Device *dev, *next; + + DBG (7, "sane_exit()\n"); + + for (dev = first_dev; dev; dev = next) + { + next = dev->next; + free ((void *) dev->sane.name); + free ((void *) dev->sane.model); + free (dev); + } + + if (devlist) + free (devlist); +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + ARTEC_Device *dev; + int i; + + DBG (7, "sane_get_devices( device_list, local_only = %d )\n", local_only ); + + if (devlist) + free (devlist); + + devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); + if (!devlist) + return SANE_STATUS_NO_MEM; + + i = 0; + for (dev = first_dev; i < num_devices; dev = dev->next) + devlist[i++] = &dev->sane; + devlist[i++] = 0; + + *device_list = devlist; + + return (SANE_STATUS_GOOD); +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + SANE_Status status; + ARTEC_Device *dev; + ARTEC_Scanner *s; + int i, j; + + DBG (7, "sane_open()\n"); + + if (devicename[0]) + { + for (dev = first_dev; dev; dev = dev->next) + if (strcmp (dev->sane.name, devicename) == 0) + break; + + if (!dev) + { + status = attach (devicename, &dev); + if (status != SANE_STATUS_GOOD) + return (status); + } + } + else + { + /* empty devicname -> use first device */ + dev = first_dev; + } + + if (!dev) + return SANE_STATUS_INVAL; + + s = malloc (sizeof (*s)); + if (!s) + return SANE_STATUS_NO_MEM; + memset (s, 0, sizeof (*s)); + s->fd = -1; + s->hw = dev; + s->this_pass = 0; + + s->gamma_length = s->hw->gamma_length; + s->gamma_range.min = 0; + s->gamma_range.max = s->gamma_length - 1; + s->gamma_range.quant = 0; + + /* not sure if I need this or not, it was in the umax backend though. :-) */ + for (j = 0; j < s->gamma_length; ++j) + { + s->gamma_table[0][j] = j * (s->gamma_length - 1) / s->gamma_length; + } + + for (i = 1; i < 4; i++) + { + for (j = 0; j < s->gamma_length; ++j) + { + s->gamma_table[i][j] = j; + } + } + + init_options (s); + + /* insert newly opened handle into list of open handles: */ + s->next = first_handle; + first_handle = s; + + *handle = s; + + if (s->hw->flags & ARTEC_FLAG_CALIBRATE) + { + status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "error opening scanner for initial calibration: %s\n", + sane_strstatus (status)); + s->fd = -1; + return status; + } + + status = artec_calibrate_shading (s); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "initial shading calibration failed: %s\n", + sane_strstatus (status)); + sanei_scsi_close (s->fd); + s->fd = -1; + return status; + } + + sanei_scsi_close (s->fd); + } + + return (SANE_STATUS_GOOD); +} + +void +sane_close (SANE_Handle handle) +{ + ARTEC_Scanner *prev, *s; + + DBG (7, "sane_close()\n"); + + if ((DBG_LEVEL == 101) && + (debug_fd > -1)) + { + close (debug_fd); + DBG (101, "closed artec.data.raw output file\n"); + } + + /* remove handle from list of open handles: */ + prev = 0; + for (s = first_handle; s; s = s->next) + { + if (s == handle) + break; + prev = s; + } + if (!s) + { + DBG (1, "close: invalid handle %p\n", handle); + return; /* oops, not a handle we know about */ + } + + if (s->scanning) + do_cancel (handle); + + + if (prev) + prev->next = s->next; + else + first_handle = s->next; + + free (handle); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + ARTEC_Scanner *s = handle; + + DBG (7, "sane_get_option_descriptor()\n"); + + if (((unsigned) option >= NUM_OPTIONS) || + (option < 0 )) + return (0); + + return (s->opt + option); +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + ARTEC_Scanner *s = handle; + SANE_Status status; + SANE_Word w, cap; + + DBG (7, "sane_control_option()\n"); + + if (info) + *info = 0; + + if (s->scanning) + return SANE_STATUS_DEVICE_BUSY; + + if (s->this_pass) + return SANE_STATUS_DEVICE_BUSY; + + if (option >= NUM_OPTIONS) + return SANE_STATUS_INVAL; + + cap = s->opt[option].cap; + + if (!SANE_OPTION_IS_ACTIVE (cap)) + return SANE_STATUS_INVAL; + + if (action == SANE_ACTION_GET_VALUE) + { + DBG (13, "sane_control_option %d, get value\n", option); + + switch (option) + { + /* word options: */ + case OPT_X_RESOLUTION: + case OPT_Y_RESOLUTION: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + case OPT_RESOLUTION_BIND: + case OPT_NEGATIVE: + case OPT_TRANSPARENCY: + case OPT_ADF: + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_NUM_OPTS: + case OPT_QUALITY_CAL: + case OPT_SOFTWARE_CAL: + case OPT_CONTRAST: + case OPT_BRIGHTNESS: + case OPT_THRESHOLD: + case OPT_CUSTOM_GAMMA: + case OPT_PIXEL_AVG: + case OPT_EDGE_ENH: + *(SANE_Word *) val = s->val[option].w; + return (SANE_STATUS_GOOD); + + /* string options: */ + case OPT_MODE: + case OPT_FILTER_TYPE: + case OPT_HALFTONE_PATTERN: + strcpy (val, s->val[option].s); + return (SANE_STATUS_GOOD); + + /* word array options: */ + case OPT_GAMMA_VECTOR: + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + memcpy (val, s->val[option].wa, s->opt[option].size); + return (SANE_STATUS_GOOD); + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + DBG (13, "sane_control_option %d, set value\n", option); + + if (!SANE_OPTION_IS_SETTABLE (cap)) + return (SANE_STATUS_INVAL); + + status = sanei_constrain_value (s->opt + option, val, info); + if (status != SANE_STATUS_GOOD) + return (status); + + switch (option) + { + /* (mostly) side-effect-free word options: */ + case OPT_X_RESOLUTION: + case OPT_Y_RESOLUTION: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_TL_X: + case OPT_TL_Y: + if (info && s->val[option].w != *(SANE_Word *) val) + *info |= SANE_INFO_RELOAD_PARAMS; + + /* fall through */ + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + case OPT_QUALITY_CAL: + case OPT_SOFTWARE_CAL: + case OPT_NUM_OPTS: + case OPT_NEGATIVE: + case OPT_TRANSPARENCY: + case OPT_ADF: + case OPT_CONTRAST: + case OPT_BRIGHTNESS: + case OPT_THRESHOLD: + case OPT_PIXEL_AVG: + case OPT_EDGE_ENH: + s->val[option].w = *(SANE_Word *) val; + return (SANE_STATUS_GOOD); + + case OPT_MODE: + { + if (s->val[option].s) + free (s->val[option].s); + + s->val[option].s = (SANE_Char *) strdup (val); + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + + s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + /* options INvisible by default */ + s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_SOFTWARE_CAL].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_EDGE_ENH].cap |= SANE_CAP_INACTIVE; + + /* options VISIBLE by default */ + s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_FILTER_TYPE].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE; + + if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0) + { + /* Lineart mode */ + s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* OFF */ + s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; + + if (s->hw->flags & ARTEC_FLAG_ENHANCE_LINE_EDGE) + s->opt[OPT_EDGE_ENH].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) + { + /* Halftone mode */ + if (s->hw->flags & ARTEC_FLAG_HALFTONE_PATTERN) + s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + /* Grayscale mode */ + if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE)) + { + s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE; + } + } + else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + /* Color mode */ + s->opt[OPT_FILTER_TYPE].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_SOFTWARE_CAL].cap &= ~SANE_CAP_INACTIVE; + if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE)) + { + s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE; + } + } + } + return (SANE_STATUS_GOOD); + + case OPT_FILTER_TYPE: + case OPT_HALFTONE_PATTERN: + if (s->val[option].s) + free (s->val[option].s); + s->val[option].s = strdup (val); + return (SANE_STATUS_GOOD); + + case OPT_RESOLUTION_BIND: + if (s->val[option].w != *(SANE_Word *) val) + { + s->val[option].w = *(SANE_Word *) val; + + if (info) + { + *info |= SANE_INFO_RELOAD_OPTIONS; + } + + if (s->val[option].w == SANE_FALSE) + { /* don't bind */ + s->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_X_RESOLUTION].title = + SANE_TITLE_SCAN_X_RESOLUTION; + s->opt[OPT_X_RESOLUTION].name = + SANE_NAME_SCAN_RESOLUTION; + s->opt[OPT_X_RESOLUTION].desc = + SANE_DESC_SCAN_X_RESOLUTION; + } + else + { /* bind */ + s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_X_RESOLUTION].title = + SANE_TITLE_SCAN_RESOLUTION; + s->opt[OPT_X_RESOLUTION].name = + SANE_NAME_SCAN_RESOLUTION; + s->opt[OPT_X_RESOLUTION].desc = + SANE_DESC_SCAN_RESOLUTION; + } + } + return (SANE_STATUS_GOOD); + + /* side-effect-free word-array options: */ + case OPT_GAMMA_VECTOR: + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + memcpy (s->val[option].wa, val, s->opt[option].size); + return (SANE_STATUS_GOOD); + + /* options with side effects: */ + case OPT_CUSTOM_GAMMA: + w = *(SANE_Word *) val; + if (w == s->val[OPT_CUSTOM_GAMMA].w) + return (SANE_STATUS_GOOD); + + s->val[OPT_CUSTOM_GAMMA].w = w; + if (w) /* use custom_gamma_table */ + { + const char *mode = s->val[OPT_MODE].s; + + if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || + (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) || + (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)) + { + s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + } + else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + + if (!(s->hw->flags & ARTEC_FLAG_GAMMA_SINGLE)) + { + s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + } + else + /* don't use custom_gamma_table */ + { + s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + } + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS; + + return (SANE_STATUS_GOOD); + } + } + + return (SANE_STATUS_INVAL); +} + +static void +set_pass_parameters (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + + DBG (7, "set_pass_parameters()\n"); + + if (s->threepasscolor) + { + s->this_pass += 1; + DBG (9, "set_pass_parameters: three-pass, on %d\n", s->this_pass); + switch (s->this_pass) + { + case 1: + s->params.format = SANE_FRAME_RED; + s->params.last_frame = SANE_FALSE; + break; + case 2: + s->params.format = SANE_FRAME_GREEN; + s->params.last_frame = SANE_FALSE; + break; + case 3: + s->params.format = SANE_FRAME_BLUE; + s->params.last_frame = SANE_TRUE; + break; + default: + DBG (9, "set_pass_parameters: What?!? pass %d = filter?\n", + s->this_pass); + break; + } + } + else + s->this_pass = 0; +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + ARTEC_Scanner *s = handle; + + DBG (7, "sane_get_parameters()\n"); + + if (!s->scanning) + { + double width, height; + + memset (&s->params, 0, sizeof (s->params)); + + s->x_resolution = s->val[OPT_X_RESOLUTION].w; + s->y_resolution = s->val[OPT_Y_RESOLUTION].w; + + if ((s->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) || + (s->val[OPT_PREVIEW].w == SANE_TRUE)) + { + s->y_resolution = s->x_resolution; + } + + s->tl_x = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH + * s->x_resolution; + s->tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH + * s->y_resolution; + width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); + height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); + + if ((s->x_resolution > 0.0) && + (s->y_resolution > 0.0) && + (width > 0.0) && + (height > 0.0)) + { + s->params.pixels_per_line = width * s->x_resolution / MM_PER_INCH + 1; + s->params.lines = height * s->y_resolution / MM_PER_INCH + 1; + } + + s->onepasscolor = SANE_FALSE; + s->threepasscolor = SANE_FALSE; + s->params.last_frame = SANE_TRUE; + + if ((s->val[OPT_PREVIEW].w == SANE_TRUE) && + (s->val[OPT_GRAY_PREVIEW].w == SANE_TRUE)) + { + s->mode = SANE_VALUE_SCAN_MODE_GRAY; + } + else + { + s->mode = s->val[OPT_MODE].s; + } + + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || + (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)) + { + s->params.format = SANE_FRAME_GRAY; + s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8; + s->params.depth = 1; + s->line_offset = 0; + + /* round pixels_per_line up to the next full byte of pixels */ + /* this way we don't have to do bit buffering, pixels_per_line is */ + /* what is used in the set window command. */ + /* SANE expects the last byte in a line to be padded if it's not */ + /* full, so this should not affect scans in a negative way */ + s->params.pixels_per_line = s->params.bytes_per_line * 8; + } + else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) + { + s->params.format = SANE_FRAME_GRAY; + s->params.bytes_per_line = s->params.pixels_per_line; + s->params.depth = 8; + s->line_offset = 0; + } + else + { + s->params.bytes_per_line = s->params.pixels_per_line; + s->params.depth = 8; + + if (s->hw->flags & ARTEC_FLAG_ONE_PASS_SCANNER) + { + s->onepasscolor = SANE_TRUE; + s->params.format = SANE_FRAME_RGB; + s->params.bytes_per_line *= 3; + + /* + * line offsets from documentation. + * (I don't yet see a common formula I can easily use) + */ + /* FIXME: figure out a cleaner way to do this... */ + s->line_offset = 0; /* default */ + if ((!strcmp (s->hw->sane.model, "AT3")) || + (!strcmp (s->hw->sane.model, "A6000C")) || + (!strcmp (s->hw->sane.model, "A6000C PLUS")) || + (!strcmp (s->hw->sane.model, "AT6"))) + { + /* formula #1 */ + /* ranges from 1 at 50dpi to 16 at 600dpi */ + s->line_offset = 8 * (s->y_resolution / 300.0); + } + else if (!strcmp (s->hw->sane.model, "AT12")) + { + /* formula #2 */ + /* ranges from 0 at 25dpi to 16 at 1200dpi */ + /***********************************************************/ + /* this should be handled in hardware for now, so leave it */ + /* sitting at zero for now. */ + /***********************************************************/ + /* + s->line_offset = 16 * ( s->y_resolution / 1200.0 ); + */ + } + else if (!strcmp (s->hw->sane.model, "AM12S")) + { + /* formula #3 */ + /* ranges from 0 at 50dpi to 8 at 1200dpi */ + s->line_offset = 8 * (s->y_resolution / 1200.0); + } + } + else + { + s->params.last_frame = SANE_FALSE; + s->threepasscolor = SANE_TRUE; + s->line_offset = 0; + } + } + } + + if (params) + *params = s->params; + + return (SANE_STATUS_GOOD); +} + + +SANE_Status +sane_start (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + SANE_Status status; + + DBG (7, "sane_start()\n"); + + if (debug_fd != -1) + { + close (debug_fd); + debug_fd = -1; + } + + if (DBG_LEVEL == 101) + { + debug_fd = open ("artec.data.raw", + O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (debug_fd > -1) + DBG (101, "opened artec.data.raw output file\n"); + } + + /* First make sure we have a current parameter set. Some of the */ + /* parameters will be overwritten below, but that's OK. */ + status = sane_get_parameters (s, 0); + if (status != SANE_STATUS_GOOD) + return status; + + /* DAL: For 3 pass colour set the current pass parameters */ + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && s->threepasscolor) + set_pass_parameters (s); + + /* DAL: For single pass scans and the first pass of a 3 pass scan */ + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || + (!s->threepasscolor) || + ((s->threepasscolor) && + (s->this_pass == 1))) + { + + if (s->hw->flags & ARTEC_FLAG_SENSE_HANDLER) + { + status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, + (void *)s); + } + else + { + status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0); + } + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "open of %s failed: %s\n", + s->hw->sane.name, sane_strstatus (status)); + return status; + } + + /* DB added wait_ready */ + status = wait_ready (s->fd); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "wait for scanner ready failed: %s\n", + sane_strstatus (status)); + return status; + } + } + + s->bytes_to_read = s->params.bytes_per_line * s->params.lines; + + DBG (9, "%d pixels per line, %d bytes, %d lines high, xdpi = %d, " + "ydpi = %d, btr = %lu\n", + s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, + s->x_resolution, s->y_resolution, (u_long) s->bytes_to_read); + + /* DAL: For single pass scans and the first pass of a 3 pass scan */ + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || !s->threepasscolor || + (s->threepasscolor && s->this_pass == 1)) + { + + /* do a calibrate if scanner requires/recommends it */ + if ((s->hw->flags & ARTEC_FLAG_CALIBRATE) && + (s->val[OPT_QUALITY_CAL].w == SANE_TRUE)) + { + status = artec_calibrate_shading (s); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "shading calibration failed: %s\n", + sane_strstatus (status)); + return status; + } + } + + /* DB added wait_ready */ + status = wait_ready (s->fd); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "wait for scanner ready failed: %s\n", + sane_strstatus (status)); + return status; + } + + /* send the custom gamma table if we have one */ + if (s->hw->flags & ARTEC_FLAG_GAMMA) + artec_send_gamma_table (s); + + /* now set our scan window */ + status = artec_set_scan_window (s); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "set scan window failed: %s\n", + sane_strstatus (status)); + return status; + } + + /* DB added wait_ready */ + status = wait_ready (s->fd); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "wait for scanner ready failed: %s\n", + sane_strstatus (status)); + return status; + } + } + + /* now we can start the actual scan */ + /* DAL: For single pass scans and the first pass of a 3 pass scan */ + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || + (!s->threepasscolor) || + (s->this_pass == 1)) + { + /* DAL - do mode select before each scan */ + /* The mode is NOT turned off at the end of the scan */ + artec_mode_select (s); + + status = artec_start_scan (s); + + if (status != SANE_STATUS_GOOD) + { + DBG (1, "start scan: %s\n", sane_strstatus (status)); + return status; + } + } + + s->scanning = SANE_TRUE; + + return (SANE_STATUS_GOOD); +} + + +#if 0 +static void +binout (SANE_Byte byte) +{ + SANE_Byte b = byte; + int bit; + + for (bit = 0; bit < 8; bit++) + { + DBG (9, "%d", b & 128 ? 1 : 0); + b = b << 1; + } +} +#endif + +static SANE_Status +artec_sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) +{ + ARTEC_Scanner *s = handle; + SANE_Status status; + size_t nread; + size_t lread; + size_t bytes_read; + size_t rows_read; + size_t max_read_rows; + size_t max_ret_rows; + size_t remaining_rows; + size_t rows_available; + size_t line; + SANE_Byte temp_buf[ARTEC_MAX_READ_SIZE]; + SANE_Byte line_buf[ARTEC_MAX_READ_SIZE]; + + + DBG (7, "artec_sane_read( %p, %p, %d, %d )\n", handle, buf, max_len, *len); + + *len = 0; + + if (s->bytes_to_read == 0) + { + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || !s->threepasscolor || + (s->threepasscolor && s->this_pass == 3)) + { + do_cancel (s); + /* without this a 4th pass is attempted, yet do_cancel does this */ + s->scanning = SANE_FALSE; + } + return (SANE_STATUS_EOF); + } + + if (!s->scanning) + return do_cancel (s); + + remaining_rows = (s->bytes_to_read + s->params.bytes_per_line - 1) / s->params.bytes_per_line; + max_read_rows = s->hw->max_read_size / s->params.bytes_per_line; + max_ret_rows = max_len / s->params.bytes_per_line; + + while (artec_get_status (s->fd) == 0) + { + DBG (120, "hokey loop till data available\n"); + usleep (50000); /* sleep for .05 second */ + } + + rows_read = 0; + bytes_read = 0; + while ((rows_read < max_ret_rows) && (rows_read < remaining_rows)) + { + DBG (50, "top of while loop, rr = %lu, mrr = %lu, rem = %lu\n", + (u_long) rows_read, (u_long) max_ret_rows, (u_long) remaining_rows); + + if (s->bytes_to_read - bytes_read <= s->params.bytes_per_line * max_read_rows) + { + nread = s->bytes_to_read - bytes_read; + } + else + { + nread = s->params.bytes_per_line * max_read_rows; + } + lread = nread / s->params.bytes_per_line; + + if ((max_read_rows - rows_read) < lread) + { + lread = max_read_rows - rows_read; + nread = lread * s->params.bytes_per_line; + } + + if ((max_ret_rows - rows_read) < lread) + { + lread = max_ret_rows - rows_read; + nread = lread * s->params.bytes_per_line; + } + + while ((rows_available = artec_get_status (s->fd)) == 0) + { + DBG (120, "hokey loop till data available\n"); + usleep (50000); /* sleep for .05 second */ + } + + if (rows_available < lread) + { + lread = rows_available; + nread = lread * s->params.bytes_per_line; + } + + /* This should never happen, but just in case... */ + if (nread > (s->bytes_to_read - bytes_read)) + { + nread = s->bytes_to_read - bytes_read; + lread = 1; + } + + DBG (50, "rows_available = %lu, params.lines = %d, bytes_per_line = %d\n", + (u_long) rows_available, s->params.lines, s->params.bytes_per_line); + DBG (50, "bytes_to_read = %lu, max_len = %d, max_rows = %lu\n", + (u_long) s->bytes_to_read, max_len, (u_long) max_ret_rows); + DBG (50, "nread = %lu, lread = %lu, bytes_read = %lu, rows_read = %lu\n", + (u_long) nread, (u_long) lread, (u_long) bytes_read, (u_long) rows_read); + + status = read_data (s->fd, ARTEC_DATA_IMAGE, temp_buf, &nread); + + if (status != SANE_STATUS_GOOD) + { + end_scan (s); + do_cancel (s); + return (SANE_STATUS_IO_ERROR); + } + + if ((DBG_LEVEL == 101) && + (debug_fd > -1)) + { + write (debug_fd, temp_buf, nread); + } + + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && + (s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET)) + { + for (line = 0; line < lread; line++) + { + memcpy (line_buf, + temp_buf + (line * s->params.bytes_per_line), + s->params.bytes_per_line); + + nread = s->params.bytes_per_line; + + artec_buffer_line_offset (s, s->line_offset, line_buf, &nread); + + if (nread > 0) + { + if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT) + { + artec_line_rgb_to_byte_rgb (line_buf, + s->params.pixels_per_line); + } + if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) + { + artec_reverse_line (s, line_buf); + } + + /* do software calibration if necessary */ + if (s->val[OPT_SOFTWARE_CAL].w) + { + artec_software_rgb_calibrate (s, line_buf, 1); + } + + memcpy (buf + bytes_read, line_buf, + s->params.bytes_per_line); + bytes_read += nread; + rows_read++; + } + } + } + else + { + if ((s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) || + ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && + (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT))) + { + for (line = 0; line < lread; line++) + { + if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && + (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)) + { + artec_line_rgb_to_byte_rgb (temp_buf + + (line * s->params.bytes_per_line), + s->params.pixels_per_line); + } + if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) + { + artec_reverse_line (s, temp_buf + + (line * s->params.bytes_per_line)); + } + } + } + + /* do software calibration if necessary */ + if ((s->val[OPT_SOFTWARE_CAL].w) && + (strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)) + { + artec_software_rgb_calibrate (s, temp_buf, lread); + } + + memcpy (buf + bytes_read, temp_buf, nread); + bytes_read += nread; + rows_read += lread; + } + } + + *len = bytes_read; + s->bytes_to_read -= bytes_read; + + DBG (9, "artec_sane_read() returning, we read %lu bytes, %lu left\n", + (u_long) * len, (u_long) s->bytes_to_read); + + if ((s->bytes_to_read == 0) && + (s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) && + (tmp_line_buf != NULL)) + { + artec_buffer_line_offset_free (); + } + + return (SANE_STATUS_GOOD); +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) +{ + ARTEC_Scanner *s = handle; + SANE_Status status; + int bytes_to_copy; + int loop; + + static SANE_Byte temp_buf[ARTEC_MAX_READ_SIZE]; + static int bytes_in_buf = 0; + + DBG (7, "sane_read( %p, %p, %d, %d )\n", handle, buf, max_len, *len); + DBG (9, "sane_read: bib = %d, ml = %d\n", bytes_in_buf, max_len); + + if (bytes_in_buf != 0) + { + bytes_to_copy = max_len < bytes_in_buf ? max_len : bytes_in_buf; + } + else + { + status = artec_sane_read (s, temp_buf, s->hw->max_read_size, len); + + if (status != SANE_STATUS_GOOD) + { + return (status); + } + + bytes_in_buf = *len; + + if (*len == 0) + { + return (SANE_STATUS_GOOD); + } + + bytes_to_copy = max_len < s->hw->max_read_size ? + max_len : s->hw->max_read_size; + bytes_to_copy = *len < bytes_to_copy ? *len : bytes_to_copy; + } + + memcpy (buf, temp_buf, bytes_to_copy); + bytes_in_buf -= bytes_to_copy; + *len = bytes_to_copy; + + DBG (9, "sane_read: btc = %d, bib now = %d\n", + bytes_to_copy, bytes_in_buf); + + for (loop = 0; loop < bytes_in_buf; loop++) + { + temp_buf[loop] = temp_buf[loop + bytes_to_copy]; + } + + return (SANE_STATUS_GOOD); +} + +void +sane_cancel (SANE_Handle handle) +{ + ARTEC_Scanner *s = handle; + + DBG (7, "sane_cancel()\n"); + + if (s->scanning) + { + s->scanning = SANE_FALSE; + + abort_scan (s); + + do_cancel (s); + } +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + DBG (7, "sane_set_io_mode( %p, %d )\n", handle, non_blocking); + + return (SANE_STATUS_UNSUPPORTED); +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + DBG (7, "sane_get_select_fd( %p, %d )\n", handle, *fd ); + + return (SANE_STATUS_UNSUPPORTED); +} |