kopia lustrzana https://gitlab.com/sane-project/backends
3751 wiersze
100 KiB
C
3751 wiersze
100 KiB
C
/* 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 "sane/sane.h"
|
|
#include "sane/saneopts.h"
|
|
#include "sane/sanei_scsi.h"
|
|
#include "sane/sanei_backend.h"
|
|
#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"
|
|
|
|
#define MM_PER_INCH 25.4
|
|
|
|
#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[] =
|
|
{
|
|
"Lineart", "Halftone", "Gray", "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 u_int8_t inquiry[] =
|
|
{
|
|
0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00
|
|
};
|
|
|
|
static const u_int8_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;
|
|
u_int8_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;
|
|
u_int8_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, "Lineart") == 0)
|
|
{
|
|
data[33] = ARTEC_COMP_LINEART;
|
|
data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80;
|
|
}
|
|
else if (strcmp (s->mode, "Halftone") == 0)
|
|
{
|
|
data[33] = ARTEC_COMP_HALFTONE;
|
|
data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80;
|
|
}
|
|
else if (strcmp (s->mode, "Gray") == 0)
|
|
{
|
|
data[33] = ARTEC_COMP_GRAY;
|
|
data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0;
|
|
}
|
|
else if (strcmp (s->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
|
|
u_int8_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 (V_MAJOR, V_MINOR, 0);
|
|
|
|
if (authorize)
|
|
DBG (7, "sane_init(), authorize callback specified as %p\n", (void *) 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, "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, "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, "Gray") == 0)
|
|
{
|
|
/* Grayscale mode */
|
|
if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE))
|
|
{
|
|
s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
}
|
|
else if (strcmp (val, "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, "Lineart") == 0) ||
|
|
(strcmp (mode, "Halftone") == 0) ||
|
|
(strcmp (mode, "Gray") == 0))
|
|
{
|
|
s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
|
|
}
|
|
else if (strcmp (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 = "Gray";
|
|
}
|
|
else
|
|
{
|
|
s->mode = s->val[OPT_MODE].s;
|
|
}
|
|
|
|
if ((strcmp (s->mode, "Lineart") == 0) ||
|
|
(strcmp (s->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, "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, "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, "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, "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, "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, "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, "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, "Color") == 0) &&
|
|
(s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)))
|
|
{
|
|
for (line = 0; line < lread; line++)
|
|
{
|
|
if ((strcmp (s->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, "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);
|
|
}
|