mirror of
https://github.com/openharmony/third_party_backends.git
synced 2026-08-26 21:11:48 -04:00
330b2f0c57
These were intended to suppress GCC warnings about unused variables. However, this leads to different warnings from Clang instead. Use another approach that suppresses warnings from both compilers.
4194 lines
115 KiB
C
4194 lines
115 KiB
C
/* ------------------------------------------------------------------------- */
|
|
/* sane - Scanner Access Now Easy.
|
|
coolscan.c , version 0.4.4
|
|
|
|
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, see <https://www.gnu.org/licenses/>.
|
|
|
|
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 COOLSCAN flatbed scanners. */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
/* SANE-FLOW-DIAGRAMM
|
|
|
|
- sane_init() : initialize backend, attach scanners
|
|
. - sane_get_devices() : query list of scanner-devices
|
|
. - sane_open() : open a particular scanner-device
|
|
. . - sane_set_io_mode : set blocking-mode
|
|
. . - sane_get_select_fd : get scanner-fd
|
|
. . - sane_get_option_descriptor() : get option information
|
|
. . - sane_control_option() : change option values
|
|
. .
|
|
. . - sane_start() : start image acquisition
|
|
. . - sane_get_parameters() : returns actual scan-parameters
|
|
. . - sane_read() : read image-data (from pipe)
|
|
. .
|
|
. . - sane_cancel() : cancel operation
|
|
. - sane_close() : close opened scanner-device
|
|
- sane_exit() : terminate use of backend
|
|
*/
|
|
|
|
#ifdef _AIX
|
|
# include "lalloca.h" /* MUST come first for AIX! */
|
|
#endif
|
|
|
|
#include "../include/sane/config.h"
|
|
#include "lalloca.h"
|
|
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../include/sane/sane.h"
|
|
#include "../include/sane/sanei.h"
|
|
#include "../include/sane/saneopts.h"
|
|
#include "../include/sane/sanei_scsi.h"
|
|
#include "../include/sane/sanei_debug.h"
|
|
#include "../include/sane/sanei_thread.h"
|
|
|
|
#include "../include/sane/sanei_config.h"
|
|
#define COOLSCAN_CONFIG_FILE "coolscan.conf"
|
|
#include "../include/sane/sanei_backend.h"
|
|
|
|
#include "coolscan.h"
|
|
#include "coolscan-scsidef.h"
|
|
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX 1024
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
static const SANE_Int resolution_list[] =
|
|
{
|
|
25,
|
|
2700, 1350, 900, 675, 540, 450, 385, 337, 300, 270, 245, 225, 207,
|
|
192, 180, 168, 158, 150, 142, 135, 128, 122, 117, 112, 108
|
|
};
|
|
|
|
|
|
#define coolscan_do_scsi_open(dev, fd, handler) sanei_scsi_open(dev, fd, handler)
|
|
#define coolscan_do_scsi_close(fd) sanei_scsi_close(fd)
|
|
|
|
#define COOLSCAN_MAX_RETRY 25
|
|
|
|
|
|
static SANE_Status sense_handler (int scsi_fd, unsigned char * result, void *arg);
|
|
static int coolscan_check_values (Coolscan_t * s);
|
|
static int get_internal_info (Coolscan_t *);
|
|
static void coolscan_get_inquiry_values (Coolscan_t *);
|
|
static void hexdump (int level, char *comment, unsigned char *p, int l);
|
|
static int swap_res (Coolscan_t * s);
|
|
/* --------------------------- COOLSCAN_DO_SCSI_CMD ----------------------- */
|
|
static int
|
|
do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len)
|
|
{
|
|
int ret;
|
|
size_t ol = out_len;
|
|
|
|
hexdump (20, "", cmd, cmd_len);
|
|
|
|
ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol);
|
|
if ((out_len != 0) && (out_len != ol))
|
|
{
|
|
DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n",
|
|
(u_long) out_len, (u_long) ol);
|
|
}
|
|
if (ret)
|
|
{
|
|
DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret);
|
|
}
|
|
DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol);
|
|
if (out != NULL && out_len != 0)
|
|
hexdump (15, "", out, (out_len > 0x60) ? 0x60 : out_len);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
request_sense_parse (unsigned char *sensed_data)
|
|
{
|
|
int ret, sense, asc, ascq;
|
|
sense = get_RS_sense_key (sensed_data);
|
|
asc = get_RS_ASC (sensed_data);
|
|
ascq = get_RS_ASCQ (sensed_data);
|
|
|
|
ret = SANE_STATUS_IO_ERROR;
|
|
|
|
switch (sense)
|
|
{
|
|
case 0x0:
|
|
DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case 0x1:
|
|
if ((0x37 == asc) && (0x00 == ascq)) {
|
|
DBG (1, "\t%d/%d/%d: Rounded Parameter\n", sense, asc, ascq);
|
|
ret = SANE_STATUS_GOOD;
|
|
}
|
|
else if ((0x61 == asc) && (0x02 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Out Of Focus\n", sense, asc, ascq);
|
|
else
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
|
|
case 0x2:
|
|
if ((0x4 == asc) && (0x1 == ascq)) {
|
|
DBG (10, "\t%d/%d/%d: Logical unit is in process of becoming ready\n",
|
|
sense, asc, ascq);
|
|
ret = SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
else if ((0x3A == asc) && (0x00 == ascq))
|
|
{
|
|
DBG (1, "\t%d/%d/%d: No Diapo inserted\n", sense, asc, ascq);
|
|
ret = SANE_STATUS_GOOD;
|
|
}
|
|
else if ((0x60 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Lamp Failure\n", sense, asc, ascq);
|
|
else
|
|
{
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
ret = SANE_STATUS_GOOD;
|
|
}
|
|
break;
|
|
|
|
case 0x3:
|
|
if ((0x3b == asc) && (0xe == ascq))
|
|
DBG (1, "\t%d/%d/%d: Medium source element empty\n", sense, asc, ascq);
|
|
else if ((0x53 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Media Load of Eject Failed\n", sense, asc, ascq);
|
|
else
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
|
|
case 0x4:
|
|
if ((0x15 == asc) && (0x1 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Mechanical Positioning Error\n", sense, asc, ascq);
|
|
else
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
|
|
case 0x5:
|
|
if ((0x00 == asc) && (0x5 == ascq))
|
|
DBG (1, "\t%d/%d/%d: End-Of-Data Detected\n", sense, asc, ascq);
|
|
else if ((0x1a == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Parameter List Length Error\n", sense, asc, ascq);
|
|
else if ((0x20 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Invalid Command Operation Code\n", sense, asc, ascq);
|
|
else if ((0x24 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Invalid Field In CDB\n", sense, asc, ascq);
|
|
else if ((0x25 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Logical Unit Not Supported\n", sense, asc, ascq);
|
|
else if ((0x26 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Invalid Field in Parameter List\n", sense, asc, ascq);
|
|
else if ((0x2c == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Command Sequence Error\n", sense, asc, ascq);
|
|
else if ((0x39 == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Saving Parameters Not Supported\n", sense, asc, ascq);
|
|
else if ((0x3d == asc) && (0x00 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Invalid Bits In Identify Message\n", sense, asc, ascq);
|
|
else
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
|
|
case 0x6:
|
|
if ((0x29 == asc) && (0x0 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Power On, Reset, or Bus Device Reset Occurred\n", sense, asc, ascq);
|
|
else if ((0x2a == asc) && (0x1 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Mode Parameters Changed\n", sense, asc, ascq);
|
|
else
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
|
|
case 0xb:
|
|
if ((0x43 == asc) && (0x0 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Message Error\n", sense, asc, ascq);
|
|
else if ((0x47 == asc) && (0x0 == ascq))
|
|
DBG (1, "\t%d/%d/%d: SCSI Parity Error\n", sense, asc, ascq);
|
|
else if ((0x48 == asc) && (0x0 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Initiator Detected Error Message Received\n", sense, asc, ascq);
|
|
else if ((0x49 == asc) && (0x0 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Invalid Message Error\n", sense, asc, ascq);
|
|
else if ((0x4e == asc) && (0x0 == ascq))
|
|
DBG (1, "\t%d/%d/%d: Overlapped Commands Attempted\n", sense, asc, ascq);
|
|
else
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
|
|
default:
|
|
DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
|
|
break;
|
|
} /* switch */
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* wait_scanner should spin until TEST_UNIT_READY returns 0 (GOOD)
|
|
* returns 0 on success,
|
|
* returns -1 on error.
|
|
*/
|
|
static int
|
|
wait_scanner (Coolscan_t * s)
|
|
{
|
|
int ret = -1;
|
|
int cnt = 0;
|
|
DBG (10, "wait_scanner: Testing if scanner is ready\n");
|
|
|
|
while (ret != 0)
|
|
{
|
|
ret = do_scsi_cmd (s->sfd, test_unit_ready.cmd,
|
|
test_unit_ready.size, 0, 0);
|
|
|
|
if (ret == SANE_STATUS_DEVICE_BUSY)
|
|
{
|
|
usleep (500000); /* wait 0.5 seconds */
|
|
if (cnt++ > 40)
|
|
{ /* 20 sec. max (prescan takes up to 15 sec. */
|
|
DBG (1, "wait_scanner: scanner does NOT get ready\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (ret == SANE_STATUS_GOOD)
|
|
{
|
|
DBG (10, "wait_scanner: scanner is ready\n");
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
DBG (1, "wait_scanner: test unit ready failed (%s)\n",
|
|
sane_strstatus (ret));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------- COOLSCAN GRAB SCANNER ----------------------------- */
|
|
|
|
|
|
/* coolscan_grab_scanner should go through the following command sequence:
|
|
* TEST UNIT READY
|
|
* CHECK CONDITION \
|
|
* REQUEST SENSE > These should be handled automagically by
|
|
* UNIT ATTENTION / the kernel if they happen (powerup/reset)
|
|
* TEST UNIT READY
|
|
* GOOD
|
|
* RESERVE UNIT
|
|
* GOOD
|
|
*
|
|
* It is then responsible for installing appropriate signal handlers
|
|
* to call emergency_give_scanner() if user aborts.
|
|
*/
|
|
|
|
static int
|
|
coolscan_grab_scanner (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "grabbing scanner\n");
|
|
|
|
wait_scanner (s); /* wait for scanner ready, if not print
|
|
sense and return 1 */
|
|
ret = do_scsi_cmd (s->sfd, reserve_unit.cmd, reserve_unit.size, NULL, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
DBG (10, "scanner reserved\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Convert a size in ilu to the units expected by the scanner
|
|
*/
|
|
|
|
static int
|
|
resDivToVal (int res_div)
|
|
{
|
|
if (res_div < 1 || res_div > resolution_list[0])
|
|
{
|
|
DBG (1, "Invalid resolution divisor %d \n", res_div);
|
|
return 2700;
|
|
}
|
|
else
|
|
{
|
|
return resolution_list[res_div];
|
|
}
|
|
}
|
|
|
|
static int
|
|
resValToDiv (int res_val)
|
|
{
|
|
int res_div;
|
|
int max_res = resolution_list[0];
|
|
for (res_div = 1; res_div <= max_res; res_div++)
|
|
{
|
|
if (resolution_list[res_div] == res_val)
|
|
break;
|
|
}
|
|
if (res_div > max_res)
|
|
{
|
|
DBG (1, "Invalid resolution value\n");
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return res_div;
|
|
}
|
|
}
|
|
/*
|
|
* use mode select to force a measurement divisor of 2700
|
|
*/
|
|
static unsigned char mode_select[] =
|
|
{
|
|
MODE_SELECT, 0x10, 0, 0, 20, 0,
|
|
0, 0, 0, 8,
|
|
0, 0, 0, 0, 0, 0, 0, 1,
|
|
3, 6, 0, 0, 0xA, 0x8C, 0, 0};
|
|
|
|
static int
|
|
select_MUD (Coolscan_t * s)
|
|
{
|
|
return do_scsi_cmd (s->sfd, mode_select, 26, NULL, 0);
|
|
}
|
|
|
|
static int
|
|
coolscan_autofocus_LS30 (Coolscan_t * s)
|
|
{
|
|
int x, y;
|
|
|
|
wait_scanner(s);
|
|
memcpy(s->buffer, autofocusLS30.cmd, autofocusLS30.size);
|
|
memcpy(s->buffer+ autofocusLS30.size, autofocuspos, 9);
|
|
|
|
x = s->xmaxpix - (s->brx + s->tlx) / 2;
|
|
y = (s->bry + s->tly) / 2;
|
|
|
|
DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
|
|
|
|
do_scsi_cmd (s->sfd, s->buffer,
|
|
autofocusLS30.size + 9, NULL, 0);
|
|
/* Trashes when used in combination with scsi-driver AM53C974.o */
|
|
do_scsi_cmd (s->sfd, command_c1.cmd,
|
|
command_c1.size, NULL, 0);
|
|
|
|
DBG (10, "\tWaiting end of Autofocus\n");
|
|
wait_scanner (s);
|
|
DBG (10, "AutoFocused.\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
coolscan_autofocus (Coolscan_t * s)
|
|
{
|
|
int x, y;
|
|
|
|
if(s->LS>=2)
|
|
{ return coolscan_autofocus_LS30(s);
|
|
}
|
|
|
|
wait_scanner(s);
|
|
memcpy(s->buffer, autofocus.cmd, autofocus.size);
|
|
|
|
x = s->xmaxpix - (s->brx + s->tlx) / 2;
|
|
y = (s->bry + s->tly) / 2;
|
|
|
|
DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
|
|
|
|
set_AF_XPoint (s->buffer, x);
|
|
set_AF_YPoint (s->buffer, y);
|
|
|
|
set_AF_transferlength (s->buffer, 0); /* should be 8 !*/
|
|
do_scsi_cmd (s->sfd, s->buffer,
|
|
autofocus.size + AF_Point_length, NULL, 0);
|
|
|
|
sleep(5); /* autofocus takes a minimum of 5 sec. */
|
|
|
|
DBG (10, "\tWaiting end of Autofocus\n");
|
|
wait_scanner (s);
|
|
DBG (10, "AutoFocused.\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
static int
|
|
coolscan_abort_scan (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (5, "Aborting scan...\n");
|
|
ret = do_scsi_cmd (s->sfd, sabort.cmd, sabort.size, NULL, 0);
|
|
if (ret)
|
|
DBG (5, "Scan Aborted\n");
|
|
else
|
|
DBG (5, "Not scanning\n");
|
|
return 0;
|
|
}
|
|
*/
|
|
static int
|
|
coolscan_mode_sense (Coolscan_t * s)
|
|
{
|
|
int ret, len;
|
|
|
|
DBG (10, "Mode Sense...\n");
|
|
len = 12;
|
|
set_MS_DBD (mode_sense.cmd, 1);
|
|
set_MS_len (mode_sense.cmd, len);
|
|
ret = do_scsi_cmd (s->sfd, mode_sense.cmd, mode_sense.size,
|
|
s->buffer, len);
|
|
|
|
if (ret == 0)
|
|
{
|
|
s->MUD = get_MS_MUD (s->buffer);
|
|
DBG (10, "\tMode Sensed (MUD is %d)\n", s->MUD);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
coolscan_object_discharge (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "Trying to discharge object...\n");
|
|
|
|
memcpy (s->buffer, object_position.cmd, object_position.size);
|
|
set_OP_autofeed (s->buffer, OP_Discharge);
|
|
ret = do_scsi_cmd (s->sfd, s->buffer,
|
|
object_position.size, NULL, 0);
|
|
wait_scanner (s);
|
|
DBG (10, "Object discharged.\n");
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
coolscan_object_feed (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
DBG (10, "Trying to feed object...\n");
|
|
if (!s->asf)
|
|
{
|
|
DBG (10, "\tAutofeeder not present.\n");
|
|
return 0;
|
|
}
|
|
memcpy (s->buffer, object_position.cmd, object_position.size);
|
|
set_OP_autofeed (s->buffer, OP_Feed);
|
|
ret = do_scsi_cmd (s->sfd, s->buffer,
|
|
object_position.size, NULL, 0);
|
|
wait_scanner (s);
|
|
DBG (10, "Object fed.\n");
|
|
return ret;
|
|
}
|
|
|
|
/* coolscan_give_scanner should go through the following sequence:
|
|
* OBJECT POSITION DISCHARGE
|
|
* GOOD
|
|
* RELEASE UNIT
|
|
* GOOD
|
|
*/
|
|
static int
|
|
coolscan_give_scanner (Coolscan_t * s)
|
|
{
|
|
DBG (10, "trying to release scanner ...\n");
|
|
coolscan_object_discharge (s);
|
|
wait_scanner (s);
|
|
do_scsi_cmd (s->sfd, release_unit.cmd, release_unit.size, NULL, 0);
|
|
DBG (10, "scanner released\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
coolscan_set_window_param_LS20 (Coolscan_t * s, int prescan)
|
|
{
|
|
unsigned char buffer_r[max_WDB_size];
|
|
int ret;
|
|
|
|
wait_scanner (s);
|
|
memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
|
|
memcpy (buffer_r, window_descriptor_block.cmd,
|
|
window_descriptor_block.size); /* copy preset data */
|
|
|
|
set_WD_wid (buffer_r, WD_wid_all); /* window identifier */
|
|
set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */
|
|
|
|
set_WD_negative (buffer_r, s->negative); /* Negative/positive slide */
|
|
|
|
if (prescan)
|
|
{
|
|
set_WD_scanmode (buffer_r, WD_Prescan);
|
|
}
|
|
else
|
|
{
|
|
set_WD_scanmode (buffer_r, WD_Scan);
|
|
|
|
/* geometry */
|
|
set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */
|
|
set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */
|
|
|
|
/* the coolscan uses the upper right corner as the origin of coordinates */
|
|
/* xmax and ymax are given in 1200 dpi */
|
|
set_WD_ULX (buffer_r, (s->xmaxpix - s->brx));
|
|
set_WD_ULY (buffer_r, s->tly); /* upper_edge y */
|
|
set_WD_width (buffer_r, (s->brx - s->tlx + 1));
|
|
set_WD_length (buffer_r, (s->bry - s->tly + 1));
|
|
|
|
/* BTC */
|
|
if (s->brightness == 128)
|
|
{
|
|
set_WD_brightness (buffer_r, 0);
|
|
}
|
|
else
|
|
{
|
|
set_WD_brightness (buffer_r, s->brightness); /* brightness */
|
|
}
|
|
|
|
if (s->contrast == 128)
|
|
{
|
|
set_WD_contrast (buffer_r, 0);
|
|
}
|
|
else
|
|
{
|
|
set_WD_contrast (buffer_r, s->contrast); /* contrast */
|
|
}
|
|
|
|
/* scanmode */
|
|
if (s->colormode == GREYSCALE)
|
|
set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */
|
|
else
|
|
set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */
|
|
|
|
set_WD_dropoutcolor (buffer_r, s->dropoutcolor); /* Which color to scan with when grayscale scan */
|
|
set_WD_transfermode (buffer_r, WD_LineSequence);
|
|
set_WD_gammaselection (buffer_r, s->gammaselection); /* monitor/linear */
|
|
|
|
set_WD_shading (buffer_r, WD_Shading_ON); /* default for non-manufacturing */
|
|
|
|
if (1 == s->LS)
|
|
{ /* Analog gamma reserved on LS-1000 */
|
|
set_WD_analog_gamma_R (buffer_r, 0);
|
|
set_WD_analog_gamma_G (buffer_r, 0);
|
|
set_WD_analog_gamma_R (buffer_r, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Quote spec: "It is recommended that analog gamma bits 5, 4 and 3 be
|
|
* set to 1 (OFF) when the object type of byte 48 is positive and the
|
|
* gamma specification of byte 51 is linear, and to 0 (ON) in all
|
|
* other cases." */
|
|
/*
|
|
int foo;
|
|
if ((buffer_r[48] == WD_Positive) && (buffer_r[51] == WD_Linear))
|
|
foo = WD_Analog_Gamma_OFF;
|
|
else
|
|
foo = WD_Analog_Gamma_ON;
|
|
set_WD_analog_gamma_R (buffer_r, foo);
|
|
set_WD_analog_gamma_G (buffer_r, foo);
|
|
set_WD_analog_gamma_B (buffer_r, foo);
|
|
*/
|
|
set_WD_analog_gamma_R (buffer_r, s->analog_gamma_r);
|
|
set_WD_analog_gamma_G (buffer_r, s->analog_gamma_g);
|
|
set_WD_analog_gamma_B (buffer_r, s->analog_gamma_b);
|
|
if (s->gamma_bind)
|
|
{
|
|
set_WD_LUT_R (buffer_r, 1);
|
|
set_WD_LUT_G (buffer_r, 1);
|
|
set_WD_LUT_B (buffer_r, 1);
|
|
}
|
|
else
|
|
{
|
|
set_WD_LUT_R (buffer_r, 1);
|
|
set_WD_LUT_G (buffer_r, 2);
|
|
set_WD_LUT_B (buffer_r, 3);
|
|
}
|
|
}
|
|
set_WD_averaging (buffer_r, s->averaging);
|
|
|
|
set_WD_brightness_R (buffer_r, s->brightness_R);
|
|
set_WD_brightness_G (buffer_r, s->brightness_G);
|
|
set_WD_brightness_B (buffer_r, s->brightness_B);
|
|
|
|
set_WD_contrast_R (buffer_r, s->contrast_R);
|
|
set_WD_contrast_G (buffer_r, s->contrast_G);
|
|
set_WD_contrast_B (buffer_r, s->contrast_B);
|
|
|
|
set_WD_exposure_R (buffer_r, s->exposure_R);
|
|
set_WD_exposure_G (buffer_r, s->exposure_G);
|
|
set_WD_exposure_B (buffer_r, s->exposure_B);
|
|
set_WD_shift_R (buffer_r, s->shift_R);
|
|
set_WD_shift_G (buffer_r, s->shift_G);
|
|
set_WD_shift_B (buffer_r, s->shift_B);
|
|
|
|
|
|
/* FIXME: LUT-[RGB] */
|
|
/* FIXME: stop on/off */
|
|
}
|
|
|
|
DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
|
|
s->x_nres, s->y_nres, s->tlx, s->tly);
|
|
DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
|
|
s->brx - s->tlx, s->MUD, s->brx);
|
|
DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
|
|
s->colormode, s->bits_per_color);
|
|
DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
|
|
s->negative, s->dropoutcolor, s->preview, s->transfermode,
|
|
s->gammaselection);
|
|
|
|
/* prepare SCSI-BUFFER */
|
|
memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */
|
|
memcpy ((s->buffer + set_window.size), /* add WPDB */
|
|
window_parameter_data_block.cmd,
|
|
window_parameter_data_block.size);
|
|
set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size); /* set WD_len */
|
|
memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
|
|
buffer_r, window_descriptor_block.size);
|
|
|
|
hexdump (15, "Window set", buffer_r, s->wdb_len);
|
|
|
|
set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
|
|
window_descriptor_block.size));
|
|
|
|
ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
|
|
window_parameter_data_block.size +
|
|
window_descriptor_block.size,
|
|
NULL, 0);
|
|
DBG (10, "window set.\n");
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
coolscan_set_window_param_LS30 (Coolscan_t * s, int wid, int prescan)
|
|
{
|
|
unsigned char buffer_r[max_WDB_size];
|
|
int ret;
|
|
|
|
wait_scanner (s);
|
|
memset (buffer_r, '\0', max_WDB_size); /* clear buffer */
|
|
memcpy (buffer_r, window_descriptor_block_LS30.cmd,
|
|
window_descriptor_block_LS30.size); /* copy preset data */
|
|
|
|
set_WD_wid (buffer_r, wid); /* window identifier */
|
|
set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */
|
|
|
|
/* geometry */
|
|
set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */
|
|
set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */
|
|
|
|
if (prescan)
|
|
{
|
|
set_WD_scanmode_LS30 (buffer_r, WD_Prescan);
|
|
set_WD_Xres (buffer_r, resDivToVal (1)); /* x res. in dpi */
|
|
set_WD_Yres (buffer_r, resDivToVal (1)); /* y res. in dpi */
|
|
buffer_r[0x29]=0x81;
|
|
buffer_r[0x2a]=0x04;
|
|
buffer_r[0x2b]=0x02;
|
|
buffer_r[0x2c]=0x01;
|
|
buffer_r[0x2d]=0xff;
|
|
buffer_r[0x30]=0x00;
|
|
buffer_r[0x31]=0x00;
|
|
buffer_r[0x32]=0x00;
|
|
buffer_r[0x33]=0x00;
|
|
set_WD_width (buffer_r,(2592));
|
|
set_WD_length (buffer_r,(3894));
|
|
}
|
|
else
|
|
{
|
|
set_WD_scanmode_LS30 (buffer_r, WD_Scan);
|
|
|
|
/* the coolscan LS-30 uses the upper left corner
|
|
as the origin of coordinates */
|
|
/* xmax and ymax are given in 1200 dpi */
|
|
set_WD_ULX (buffer_r, s->tlx);
|
|
set_WD_ULY (buffer_r, s->tly); /* upper_edge y */
|
|
set_WD_width (buffer_r, (s->brx - s->tlx+1));
|
|
set_WD_length (buffer_r, (s->bry - s->tly+1));
|
|
|
|
/* BTC */
|
|
if (s->brightness == 128)
|
|
{
|
|
buffer_r[0x32]=0x00;
|
|
}
|
|
else
|
|
{
|
|
buffer_r[0x32]=s->brightness; /* brightness */
|
|
}
|
|
|
|
if (s->contrast == 128)
|
|
{
|
|
buffer_r[0x33]=0x00;
|
|
}
|
|
else
|
|
{
|
|
buffer_r[0x33]=s->contrast; /* contrast */
|
|
}
|
|
|
|
/* scanmode */
|
|
if (s->colormode == GREYSCALE)
|
|
set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */
|
|
else
|
|
set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */
|
|
|
|
set_WD_composition (buffer_r, WD_comp_rgb_full); /* always RGB composition */
|
|
|
|
/* Bits per pixel */
|
|
set_WD_bitsperpixel(buffer_r, s->bits_per_color);
|
|
|
|
buffer_r[0x29]=0x81;
|
|
buffer_r[0x2a]=0x01;
|
|
buffer_r[0x2b]=0x02;
|
|
buffer_r[0x2c]=0x01;
|
|
buffer_r[0x2d]=0xff;
|
|
buffer_r[0x30]=0x00;
|
|
|
|
}
|
|
set_WD_negative_LS30(buffer_r, s->negative); /* Negative/positive slide */
|
|
|
|
switch(wid)
|
|
{ case 1: set_gain_LS30(buffer_r,(s->exposure_R*s->pretv_r)/50);
|
|
break;
|
|
case 2: set_gain_LS30(buffer_r,(s->exposure_G*s->pretv_g)/50);
|
|
break;
|
|
case 3: set_gain_LS30(buffer_r,(s->exposure_B*s->pretv_b)/50);
|
|
break;
|
|
}
|
|
|
|
DBG (10, "\texpo_r=%d, expo_g=%d, expob=%d\n",
|
|
s->exposure_R, s->exposure_G, s->exposure_B);
|
|
DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
|
|
s->pretv_r, s->pretv_g, s->pretv_b);
|
|
DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
|
|
s->x_nres, s->y_nres, s->tlx, s->tly);
|
|
DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
|
|
s->brx - s->tlx, s->MUD, s->brx);
|
|
DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
|
|
s->colormode, s->bits_per_color);
|
|
DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
|
|
s->negative, s->dropoutcolor, s->preview, s->transfermode,
|
|
s->gammaselection);
|
|
|
|
/* prepare SCSI-BUFFER */
|
|
memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */
|
|
memcpy ((s->buffer + set_window.size), /* add WPDB */
|
|
window_parameter_data_block.cmd,
|
|
window_parameter_data_block.size);
|
|
set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size_LS30); /* set WD_len */
|
|
memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
|
|
buffer_r, window_descriptor_block_LS30.size);
|
|
|
|
hexdump (15, "Window set", buffer_r, s->wdb_len);
|
|
|
|
set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
|
|
window_descriptor_block_LS30.size));
|
|
|
|
ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
|
|
window_parameter_data_block.size +
|
|
window_descriptor_block_LS30.size,
|
|
NULL, 0);
|
|
DBG (10, "window set.\n");
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
coolscan_set_window_param (Coolscan_t * s, int prescan)
|
|
{
|
|
int ret;
|
|
ret=0;
|
|
DBG (10, "set_window_param\n");
|
|
|
|
if(s->LS<2) /* distinguish between old and new scanners */
|
|
{ ret=coolscan_set_window_param_LS20 (s,prescan);
|
|
}
|
|
else
|
|
{ do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
|
|
wait_scanner (s);
|
|
wait_scanner (s);
|
|
coolscan_set_window_param_LS30(s,1,prescan);
|
|
ret=coolscan_set_window_param_LS30(s,2,prescan);
|
|
ret=coolscan_set_window_param_LS30(s,3,prescan);
|
|
if(s->colormode&0x08)
|
|
{ ret=coolscan_set_window_param_LS30(s,9,prescan);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* The only purpose of get_window is debugging. None of the return parameters
|
|
* is currently used.
|
|
*/
|
|
static int
|
|
coolscan_get_window_param_LS30 (Coolscan_t * s, int wid,int prescanok)
|
|
{
|
|
int translen;
|
|
unsigned char *buf;
|
|
|
|
DBG (10, "GET_WINDOW_PARAM\n");
|
|
/* wait_scanner (s); */
|
|
|
|
translen = window_parameter_data_block.size + window_descriptor_block_LS30.size;
|
|
|
|
/* prepare SCSI-BUFFER */
|
|
memset (s->buffer, '\0', max_WDB_size); /* clear buffer */
|
|
|
|
set_SW_xferlen (get_window.cmd, translen); /* Transfer length */
|
|
get_window.cmd[5]= wid; /* window identifier */
|
|
|
|
hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
|
|
do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
|
|
s->buffer, translen);
|
|
|
|
buf = s->buffer + window_parameter_data_block.size;
|
|
hexdump (10, "Window get", buf, 117);
|
|
|
|
s->brightness = buf[0x32]; /* brightness */
|
|
s->contrast = buf[0x33]; /* contrast */
|
|
DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
|
|
|
|
/* Useful? */
|
|
s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */
|
|
|
|
DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
|
|
s->colormode, s->bits_per_color);
|
|
|
|
if(prescanok)
|
|
{ switch(wid)
|
|
{ case 1: s->pretv_r = get_gain_LS30(buf);
|
|
break;
|
|
case 2: s->pretv_g = get_gain_LS30(buf);
|
|
break;
|
|
case 3: s->pretv_b = get_gain_LS30(buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Should this one be set at all, here? */
|
|
s->transfermode = get_WD_transfermode (buf);
|
|
|
|
s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */
|
|
DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
|
|
s->pretv_r, s->pretv_g, s->pretv_b);
|
|
|
|
DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
|
|
s->negative, s->dropoutcolor, s->preview, s->transfermode,
|
|
s->gammaselection);
|
|
|
|
DBG (10, "get_window_param - return\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The only purpose of get_window is debugging. None of the return parameters
|
|
* is currently used.
|
|
*/
|
|
static int
|
|
coolscan_get_window_param_LS20 (Coolscan_t * s)
|
|
{
|
|
int translen;
|
|
unsigned char *buf;
|
|
|
|
DBG (10, "GET_WINDOW_PARAM\n");
|
|
wait_scanner (s);
|
|
|
|
translen = window_parameter_data_block.size + window_descriptor_block.size;
|
|
|
|
/* prepare SCSI-BUFFER */
|
|
memset (s->buffer, '\0', max_WDB_size); /* clear buffer */
|
|
|
|
set_SW_xferlen (get_window.cmd, translen); /* Transfer length */
|
|
|
|
hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
|
|
do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
|
|
s->buffer, translen);
|
|
|
|
buf = s->buffer + window_parameter_data_block.size;
|
|
hexdump (10, "Window get", buf, 117);
|
|
|
|
/* BTC */
|
|
s->brightness = get_WD_brightness (buf); /* brightness */
|
|
s->contrast = get_WD_contrast (buf); /* contrast */
|
|
DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
|
|
|
|
if (WD_comp_gray == get_WD_composition (buf))
|
|
s->colormode = GREYSCALE;
|
|
else
|
|
s->colormode = RGB;
|
|
|
|
/* Useful? */
|
|
s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */
|
|
|
|
DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
|
|
s->colormode, s->bits_per_color);
|
|
|
|
|
|
s->dropoutcolor = get_WD_dropoutcolor (buf); /* Which color to scan with when grayscale scan */
|
|
|
|
/* Should this one be set at all, here? */
|
|
s->transfermode = get_WD_transfermode (buf);
|
|
|
|
s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */
|
|
|
|
DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
|
|
s->negative, s->dropoutcolor, s->preview, s->transfermode,
|
|
s->gammaselection);
|
|
|
|
/* Should this one be set at all? */
|
|
s->shading = get_WD_shading (buf);
|
|
s->averaging = get_WD_averaging (buf);
|
|
DBG (10, "get_window_param - return\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The only purpose of get_window is debugging. None of the return parameters
|
|
* is currently used.
|
|
*/
|
|
static int
|
|
coolscan_get_window_param (Coolscan_t * s, int prescanok)
|
|
{
|
|
int ret;
|
|
DBG (10, "get_window_param\n");
|
|
|
|
ret=0;
|
|
if(s->LS<2) /* distinguish between old and new scanners */
|
|
{ ret=coolscan_get_window_param_LS20 (s);
|
|
}
|
|
else
|
|
{
|
|
ret=coolscan_get_window_param_LS30(s,1,prescanok);
|
|
ret=coolscan_get_window_param_LS30(s,2,prescanok);
|
|
ret=coolscan_get_window_param_LS30(s,3,prescanok);
|
|
if(s->colormode&0x08)
|
|
{ ret=coolscan_get_window_param_LS30(s,9,prescanok);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
coolscan_start_scanLS30 (Coolscan_t * s)
|
|
{ int channels;
|
|
DBG (10, "starting scan\n");
|
|
|
|
channels=1;
|
|
memcpy (s->buffer, scan.cmd, scan.size);
|
|
switch(s->colormode)
|
|
{ case RGB:
|
|
case GREYSCALE:
|
|
channels=s->buffer[4]=0x03; /* window 1 */
|
|
s->buffer[6]=0x01; /* window 1 */
|
|
s->buffer[7]=0x02; /* window 2 */
|
|
s->buffer[8]=0x03; /* window 3 */
|
|
|
|
break;
|
|
case RGBI:
|
|
channels=s->buffer[4]=0x04; /* window 1 */
|
|
s->buffer[6]=0x01; /* window 1 */
|
|
s->buffer[7]=0x02; /* window 2 */
|
|
s->buffer[8]=0x03; /* window 3 */
|
|
s->buffer[9]=0x09; /* window 3 */
|
|
break;
|
|
case IRED:
|
|
channels=s->buffer[4]=0x01; /* window 1 */
|
|
s->buffer[8]=0x09; /* window 3 */
|
|
break;
|
|
}
|
|
|
|
return do_scsi_cmd (s->sfd, s->buffer, scan.size+channels, NULL, 0);
|
|
}
|
|
|
|
static int
|
|
coolscan_start_scan (Coolscan_t * s)
|
|
{
|
|
DBG (10, "starting scan\n");
|
|
if(s->LS>=2)
|
|
{ return coolscan_start_scanLS30(s);
|
|
}
|
|
return do_scsi_cmd (s->sfd, scan.cmd, scan.size, NULL, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
prescan (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "Starting prescan...\n");
|
|
if(s->LS<2)
|
|
{ coolscan_set_window_param (s, 1);
|
|
}
|
|
else
|
|
{
|
|
do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
|
|
wait_scanner (s);
|
|
wait_scanner (s);
|
|
coolscan_set_window_param_LS30 (s,1,1);
|
|
coolscan_set_window_param_LS30 (s,2,1);
|
|
coolscan_set_window_param_LS30 (s,3,1);
|
|
|
|
}
|
|
ret = coolscan_start_scan(s);
|
|
|
|
sleep(8); /* prescan takes a minimum of 10 sec. */
|
|
wait_scanner (s);
|
|
DBG (10, "Prescan done\n");
|
|
return ret;
|
|
}
|
|
|
|
static SANE_Status
|
|
do_prescan_now (Coolscan_t * scanner)
|
|
{
|
|
|
|
DBG (10, "do_prescan_now \n");
|
|
if (scanner->scanning == SANE_TRUE)
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
if (scanner->sfd < 0)
|
|
{ /* first call */
|
|
if (sanei_scsi_open (scanner->sane.name,
|
|
&(scanner->sfd),
|
|
sense_handler, 0) != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "do_prescan_now: open of %s failed:\n",
|
|
scanner->sane.name);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
scanner->scanning = SANE_TRUE;
|
|
|
|
|
|
if (coolscan_check_values (scanner) != 0)
|
|
{ /* Verify values */
|
|
DBG (1, "ERROR: invalid scan-values\n");
|
|
scanner->scanning = SANE_FALSE;
|
|
coolscan_give_scanner (scanner);
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (coolscan_grab_scanner (scanner))
|
|
{
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
DBG (5, "WARNING: unable to reserve scanner: device busy\n");
|
|
scanner->scanning = SANE_FALSE;
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
prescan (scanner);
|
|
if(scanner->LS<2)
|
|
{ get_internal_info(scanner);
|
|
}
|
|
coolscan_get_window_param (scanner,1);
|
|
scanner->scanning = SANE_FALSE;
|
|
coolscan_give_scanner (scanner);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
static int
|
|
send_one_LUT (Coolscan_t * s, SANE_Word * LUT, int reg)
|
|
{
|
|
int i;
|
|
short lutval;
|
|
short bytesperval;
|
|
unsigned char *gamma, *gamma_p;
|
|
unsigned short *gamma_s;
|
|
|
|
DBG (10, "send LUT\n");
|
|
|
|
if(s->LS<2)
|
|
{ set_S_datatype_code (send.cmd, R_user_reg_gamma);
|
|
bytesperval=1;
|
|
}
|
|
else
|
|
{
|
|
send.cmd[0x02]=3;
|
|
send.cmd[0x05]=1;
|
|
bytesperval=2;
|
|
}
|
|
|
|
set_S_xfer_length (send.cmd, s->lutlength*bytesperval);
|
|
set_S_datatype_qual_upper (send.cmd, reg);
|
|
|
|
gamma = alloca (send.size + s->lutlength*2);
|
|
memcpy (gamma, send.cmd, send.size);
|
|
if(s->LS<2)
|
|
{ gamma_p = &gamma[send.size];
|
|
for (i = 0; i < s->lutlength; i++)
|
|
{
|
|
if (LUT[i] > 255)
|
|
LUT[i] = 255; /* broken gtk */
|
|
*gamma_p++ = (unsigned char) LUT[i];
|
|
}
|
|
}
|
|
else if(s->LS==2)
|
|
{ gamma_s = (unsigned short*)( &gamma[send.size]);
|
|
for (i = 0; i < s->lutlength; i++)
|
|
{
|
|
if(s->negative)
|
|
{
|
|
lutval=(unsigned short)(LUT[(s->lutlength-i)]);
|
|
}
|
|
else
|
|
{
|
|
lutval=(unsigned short)(LUT[i]);
|
|
}
|
|
if (LUT[i] >= s->max_lut_val)
|
|
LUT[i] = s->max_lut_val-1; /* broken gtk */
|
|
if(s->low_byte_first) /* if on little endian machine: */
|
|
{
|
|
lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */
|
|
}
|
|
*gamma_s++ = lutval;
|
|
}
|
|
}
|
|
else if(s->LS==3)
|
|
{ gamma_s = (unsigned short*)( &gamma[send.size]);
|
|
for (i = 0; i < s->lutlength; i++)
|
|
{
|
|
if(s->negative)
|
|
{
|
|
lutval=(unsigned short)(LUT[s->lutlength-i]);
|
|
}
|
|
else
|
|
{
|
|
lutval=(unsigned short)(LUT[i]);
|
|
}
|
|
if (LUT[i] >= s->max_lut_val)
|
|
LUT[i] = s->max_lut_val-1; /* broken gtk */
|
|
if(s->low_byte_first) /* if on little endian machine: */
|
|
{ lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */
|
|
}
|
|
*gamma_s++ = lutval;
|
|
}
|
|
}
|
|
return do_scsi_cmd (s->sfd, gamma, send.size + s->lutlength*bytesperval, NULL, 0);
|
|
}
|
|
|
|
|
|
static int
|
|
send_LUT (Coolscan_t * s)
|
|
{
|
|
wait_scanner (s);
|
|
if (s->gamma_bind)
|
|
{
|
|
send_one_LUT (s, s->gamma, S_DQ_Reg1);
|
|
if(s->LS>=2)
|
|
{ send_one_LUT (s, s->gamma, S_DQ_Reg2);
|
|
send_one_LUT (s, s->gamma, S_DQ_Reg3);
|
|
if(s->colormode&0x08)
|
|
{ send_one_LUT (s, s->gamma, S_DQ_Reg9);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
send_one_LUT (s, s->gamma_r, S_DQ_Reg1);
|
|
send_one_LUT (s, s->gamma_g, S_DQ_Reg2);
|
|
send_one_LUT (s, s->gamma_b, S_DQ_Reg3);
|
|
if(s->colormode&0x08)
|
|
{ send_one_LUT (s, s->gamma_r, S_DQ_Reg9);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
coolscan_read_data_block (Coolscan_t * s, unsigned int datatype, unsigned int length)
|
|
{
|
|
int r;
|
|
|
|
DBG (10, "read_data_block (type= %x length = %d)\n",datatype,length);
|
|
/*wait_scanner(s); */
|
|
|
|
set_R_datatype_code (sread.cmd, datatype);
|
|
sread.cmd[4]=00;
|
|
sread.cmd[5]=00;
|
|
set_R_xfer_length (sread.cmd, length);
|
|
|
|
r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, length);
|
|
return ((r != 0) ? -1 : (int) length);
|
|
}
|
|
|
|
|
|
static void
|
|
coolscan_do_inquiry (Coolscan_t * s)
|
|
{
|
|
int size;
|
|
|
|
DBG (10, "do_inquiry\n");
|
|
memset (s->buffer, '\0', 256); /* clear buffer */
|
|
size = 36; /* Hardcoded, and as specified by Nikon */
|
|
/* then get inquiry with actual size */
|
|
set_inquiry_return_size (inquiry.cmd, size);
|
|
do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size);
|
|
}
|
|
|
|
static int
|
|
coolscan_identify_scanner (Coolscan_t * s)
|
|
{
|
|
unsigned char vendor[9];
|
|
unsigned char product[0x11];
|
|
unsigned char version[5];
|
|
unsigned char *pp;
|
|
int i;
|
|
|
|
vendor[8] = product[0x10] = version[4] = 0;
|
|
DBG (10, "identify_scanner\n");
|
|
coolscan_do_inquiry (s); /* get inquiry */
|
|
if (get_inquiry_periph_devtype (s->buffer) != IN_periph_devtype_scanner)
|
|
{
|
|
DBG (5, "identify_scanner: not a scanner\n");
|
|
return 1;
|
|
} /* no, continue searching */
|
|
|
|
coolscan_get_inquiry_values (s);
|
|
|
|
get_inquiry_vendor ((char *)s->buffer, (char *)vendor);
|
|
get_inquiry_product ((char *)s->buffer, (char *)product);
|
|
get_inquiry_version ((char *)s->buffer, (char *)version);
|
|
|
|
if (strncmp ("Nikon ", (char *)vendor, 8))
|
|
{
|
|
DBG (5, "identify_scanner: \"%s\" isn't a Nikon product\n", vendor);
|
|
return 1;
|
|
} /* Not a Nikon product */
|
|
|
|
pp = &vendor[8];
|
|
vendor[8] = ' ';
|
|
while (*pp == ' ')
|
|
{
|
|
*pp-- = '\0';
|
|
}
|
|
|
|
pp = &product[0x10];
|
|
product[0x10] = ' ';
|
|
while (*(pp - 1) == ' ')
|
|
{
|
|
*pp-- = '\0';
|
|
} /* leave one blank at the end! */
|
|
|
|
pp = &version[4];
|
|
version[4] = ' ';
|
|
while (*pp == ' ')
|
|
{
|
|
*pp-- = '\0';
|
|
}
|
|
|
|
DBG (10, "Found Nikon scanner %sversion %s on device %s\n",
|
|
product, version, s->devicename);
|
|
|
|
/* look for scanners that do not give all inquiry-informations */
|
|
/* and if possible use driver-known inquiry-data */
|
|
if (get_inquiry_additional_length (s->buffer) >= 0x1f)
|
|
{
|
|
/* Now identify full supported scanners */
|
|
for (i = 0; i < known_scanners; i++)
|
|
{
|
|
if (!strncmp ((char *)product, scanner_str[i], strlen (scanner_str[i])))
|
|
{
|
|
s->LS = i;
|
|
return 0;
|
|
}
|
|
}
|
|
if (s->cont)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
pixels_per_line (Coolscan_t * s)
|
|
{
|
|
int pic_dot;
|
|
if(s->LS<2)
|
|
{ pic_dot = (s->brx - s->tlx + s->x_nres) / s->x_nres;
|
|
}
|
|
else
|
|
{ pic_dot = (s->brx - s->tlx + 1) / s->x_nres;
|
|
}
|
|
DBG (10, "pic_dot=%d\n", pic_dot);
|
|
return pic_dot;
|
|
}
|
|
|
|
static int
|
|
lines_per_scan (Coolscan_t * s)
|
|
{
|
|
int pic_line;
|
|
if(s->LS<2)
|
|
{ pic_line = (s->bry - s->tly + s->y_nres) / s->y_nres;
|
|
}
|
|
else
|
|
{ pic_line = (( s->bry - s->tly + 1.0 ) / s->y_nres);
|
|
}
|
|
DBG (10, "pic_line=%d\n", pic_line);
|
|
return pic_line;
|
|
}
|
|
|
|
static int
|
|
scan_bytes_per_line (Coolscan_t * s)
|
|
{ int bpl;
|
|
switch(s->colormode)
|
|
{ case RGB:
|
|
case GREYSCALE:
|
|
bpl=pixels_per_line (s) * 3;
|
|
if(s->bits_per_color>8) bpl=bpl*2;
|
|
return bpl;
|
|
break;
|
|
case RGBI:
|
|
case IRED:
|
|
bpl=pixels_per_line (s) * 4;
|
|
if(s->bits_per_color>8) bpl=bpl*2;
|
|
return bpl;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
write_bytes_per_line (Coolscan_t * s)
|
|
{ int bpl;
|
|
switch(s->colormode)
|
|
{ case RGB:
|
|
bpl=pixels_per_line (s) * 3;
|
|
if(s->bits_per_color>8) bpl=bpl*2;
|
|
return bpl;
|
|
break;
|
|
case RGBI:
|
|
bpl=pixels_per_line (s) * 4;
|
|
if(s->bits_per_color>8) bpl=bpl*2;
|
|
return bpl;
|
|
break;
|
|
case IRED:
|
|
case GREYSCALE:
|
|
bpl= pixels_per_line (s) ;
|
|
if(s->bits_per_color>8) bpl=bpl*2;
|
|
return bpl;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
coolscan_trim_rowbufsize (Coolscan_t * s)
|
|
{
|
|
unsigned int row_len;
|
|
row_len = scan_bytes_per_line (s);
|
|
s->row_bufsize = (s->row_bufsize < row_len) ? s->row_bufsize
|
|
: s->row_bufsize - (s->row_bufsize % row_len);
|
|
DBG (10, "trim_bufsize to %d\n", s->row_bufsize);
|
|
}
|
|
|
|
static int
|
|
coolscan_check_values (Coolscan_t * s)
|
|
{
|
|
DBG (10, "check_values\n");
|
|
/* -------------------------- asf --------------------------------- */
|
|
if (s->asf != 0)
|
|
{
|
|
if (s->autofeeder == 0)
|
|
{
|
|
DBG (1, "ERROR: ASF-MODE NOT SUPPORTED BY SCANNER, ABORTING\n");
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* test_little_endian */
|
|
|
|
static SANE_Bool
|
|
coolscan_test_little_endian(void)
|
|
{
|
|
SANE_Int testvalue = 255;
|
|
unsigned char *firstbyte = (unsigned char *) &testvalue;
|
|
|
|
if (*firstbyte == 255)
|
|
{ return SANE_TRUE;
|
|
}
|
|
return SANE_FALSE;
|
|
}
|
|
|
|
static int
|
|
get_inquiery_part_LS30 (Coolscan_t * s, unsigned char part)
|
|
{
|
|
int size;
|
|
|
|
/* Get length of response */
|
|
inquiry.cmd[1]=0x01;
|
|
inquiry.cmd[2]=part;
|
|
size=4;
|
|
set_inquiry_return_size (inquiry.cmd, size);
|
|
do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
|
|
s->buffer, size);
|
|
size=get_inquiry_length(s->buffer);
|
|
size+=4;
|
|
/* then get inquiry with actual size */
|
|
set_inquiry_return_size (inquiry.cmd, size);
|
|
do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
|
|
s->buffer, size);
|
|
return size;
|
|
}
|
|
|
|
static int
|
|
coolscan_read_var_data_block (Coolscan_t * s,int datatype)
|
|
{
|
|
int r;
|
|
int size;
|
|
|
|
DBG (10, "read_data_block (type= %x)\n",datatype);
|
|
/*wait_scanner(s); */
|
|
|
|
sread.cmd[2]=datatype;
|
|
sread.cmd[4]=00;
|
|
sread.cmd[5]=03;
|
|
size=6;
|
|
set_R_xfer_length (sread.cmd, size);
|
|
r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
|
|
s->buffer, size);
|
|
size=s->buffer[5];
|
|
set_R_xfer_length (sread.cmd, size);
|
|
r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
|
|
s->buffer, size);
|
|
return ((r != 0) ? -1 : size);
|
|
}
|
|
|
|
static int
|
|
get_inquiery_LS30 (Coolscan_t * s)
|
|
{
|
|
unsigned char part;
|
|
unsigned char parts[5];
|
|
int i;
|
|
|
|
/* Get vector of inquiery parts */
|
|
get_inquiery_part_LS30(s, (unsigned char) 0);
|
|
/* Get the parts of inquiery */
|
|
for(i=0;i<5;i++)
|
|
{ parts[i]=((unsigned char *)s->buffer)[4+11+i];
|
|
}
|
|
for(i=0;i<5;i++)
|
|
{ part=parts[i];
|
|
get_inquiery_part_LS30 (s, part);
|
|
switch(part)
|
|
{ case 0x0c1:/* max size and resolution */
|
|
s->adbits = 8;
|
|
s->outputbits = 8;
|
|
s->maxres = getnbyte(s->buffer+0x12,2)-1;
|
|
s->xmaxpix = getnbyte(s->buffer+0x53,2)-1;
|
|
s->ymaxpix = getnbyte(s->buffer+0x3c,2)-1;
|
|
break;
|
|
case 0x0d1:
|
|
break;
|
|
case 0x0e1:
|
|
break;
|
|
case 0x0f0:
|
|
break;
|
|
case 0x0f8:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* get windows */
|
|
coolscan_get_window_param_LS30 (s,0,0);
|
|
s->xmax = get_WD_width(s->buffer);
|
|
s->ymax = get_WD_length(s->buffer);
|
|
coolscan_get_window_param_LS30 (s,1,0);
|
|
coolscan_get_window_param_LS30 (s,2,0);
|
|
coolscan_get_window_param_LS30 (s,3,0);
|
|
coolscan_get_window_param_LS30 (s,4,0);
|
|
coolscan_get_window_param_LS30 (s,9,0);
|
|
|
|
s->analoggamma = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
get_feeder_type_LS30 (Coolscan_t * s)
|
|
{
|
|
int size;
|
|
unsigned char *ptr;
|
|
int ima;
|
|
|
|
/* find out about Film-strip-feeder or Mount-Feeder */
|
|
size=get_inquiery_part_LS30(s, (unsigned char) 1);
|
|
if(strncmp((char *)s->buffer+5,"Strip",5)==0)
|
|
{ s->feeder=STRIP_FEEDER;
|
|
s->autofeeder = 1;
|
|
}
|
|
if(strncmp((char *)s->buffer+5,"Mount",5)==0)
|
|
{ s->feeder=MOUNT_FEEDER;
|
|
}
|
|
/* find out about Film-strip-feeder positions*/
|
|
if(s->feeder==STRIP_FEEDER)
|
|
{ size=coolscan_read_var_data_block (s,(int)0x88);
|
|
if(size>=4)
|
|
{ s->numima=s->buffer[3];
|
|
if(s->numima>6) s->numima=6; /* limit to 6 images for now */
|
|
if(s->numima>(size-4)/16) s->numima=(size-4)/16;
|
|
ptr=s->buffer+4;
|
|
for(ima=0;ima<s->numima;ima++)
|
|
{ s->ipos[ima].start=getnbyte(ptr,4);
|
|
s->ipos[ima].offset=getnbyte(ptr+4,4);
|
|
s->ipos[ima].end=getnbyte(ptr+8,4);
|
|
s->ipos[ima].height=getnbyte(ptr+12,4);
|
|
ptr+=16;
|
|
}
|
|
}
|
|
s->posima=0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
get_internal_info_LS20 (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "get_internal_info\n");
|
|
wait_scanner (s);
|
|
memset (s->buffer, '\0', DI_length); /* clear buffer */
|
|
|
|
set_R_datatype_code (sread.cmd, R_device_internal_info);
|
|
set_R_datatype_qual_upper (sread.cmd, R_DQ_none);
|
|
set_R_xfer_length (sread.cmd, DI_length);
|
|
/* then get inquiry with actual size */
|
|
ret = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
|
|
s->buffer, DI_length);
|
|
|
|
s->adbits = get_DI_ADbits (s->buffer);
|
|
s->outputbits = get_DI_Outputbits (s->buffer);
|
|
s->maxres = get_DI_MaxResolution (s->buffer);
|
|
s->xmax = get_DI_Xmax (s->buffer);
|
|
s->ymax = get_DI_Ymax (s->buffer);
|
|
s->xmaxpix = get_DI_Xmaxpixel (s->buffer);
|
|
s->ymaxpix = get_DI_Ymaxpixel (s->buffer);
|
|
s->ycurrent = get_DI_currentY (s->buffer);
|
|
s->currentfocus = get_DI_currentFocus (s->buffer);
|
|
s->currentscanpitch = get_DI_currentscanpitch (s->buffer);
|
|
s->autofeeder = get_DI_autofeeder (s->buffer);
|
|
s->analoggamma = get_DI_analoggamma (s->buffer);
|
|
s->derr[0] = get_DI_deviceerror0 (s->buffer);
|
|
s->derr[1] = get_DI_deviceerror1 (s->buffer);
|
|
s->derr[2] = get_DI_deviceerror2 (s->buffer);
|
|
s->derr[3] = get_DI_deviceerror3 (s->buffer);
|
|
s->derr[4] = get_DI_deviceerror4 (s->buffer);
|
|
s->derr[5] = get_DI_deviceerror5 (s->buffer);
|
|
s->derr[6] = get_DI_deviceerror6 (s->buffer);
|
|
s->derr[7] = get_DI_deviceerror7 (s->buffer);
|
|
s->wbetr_r = get_DI_WBETR_R (s->buffer);
|
|
s->webtr_g = get_DI_WBETR_G (s->buffer);
|
|
s->webtr_b = get_DI_WBETR_B (s->buffer);
|
|
s->pretv_r = get_DI_PRETV_R (s->buffer);
|
|
s->pretv_g = get_DI_PRETV_G (s->buffer);
|
|
s->pretv_r = get_DI_PRETV_R (s->buffer);
|
|
s->cetv_r = get_DI_CETV_R (s->buffer);
|
|
s->cetv_g = get_DI_CETV_G (s->buffer);
|
|
s->cetv_b = get_DI_CETV_B (s->buffer);
|
|
s->ietu_r = get_DI_IETU_R (s->buffer);
|
|
s->ietu_g = get_DI_IETU_G (s->buffer);
|
|
s->ietu_b = get_DI_IETU_B (s->buffer);
|
|
s->limitcondition = get_DI_limitcondition (s->buffer);
|
|
s->offsetdata_r = get_DI_offsetdata_R (s->buffer);
|
|
s->offsetdata_g = get_DI_offsetdata_G (s->buffer);
|
|
s->offsetdata_b = get_DI_offsetdata_B (s->buffer);
|
|
get_DI_poweron_errors (s->buffer, s->power_on_errors);
|
|
|
|
DBG (10,
|
|
"\tadbits=%d\toutputbits=%d\tmaxres=%d\txmax=%d\tymax=%d\n"
|
|
"\txmaxpix=%d\tymaxpix=%d\tycurrent=%d\tcurrentfocus=%d\n"
|
|
"\tautofeeder=%s\tanaloggamma=%s\tcurrentscanpitch=%d\n",
|
|
s->adbits, s->outputbits, s->maxres, s->xmax, s->ymax,
|
|
s->xmaxpix, s->ymaxpix, s->ycurrent, s->currentfocus,
|
|
s->autofeeder ? "Yes" : "No", s->analoggamma ? "Yes" : "No",
|
|
s->currentscanpitch);
|
|
DBG (10,
|
|
"\tWhite balance exposure time var [RGB]=\t%d %d %d\n"
|
|
"\tPrescan result exposure time var [RGB]=\t%d %d %d\n"
|
|
"\tCurrent exposure time var.[RGB]=\t%d %d %d\n"
|
|
"\tInternal exposure time unit[RGB]=\t%d %d %d\n",
|
|
s->wbetr_r, s->webtr_g, s->webtr_b, s->pretv_r, s->pretv_g,
|
|
s->pretv_r, s->cetv_r, s->cetv_g, s->cetv_b, s->ietu_r,
|
|
s->ietu_g, s->ietu_b);
|
|
DBG (10,
|
|
"\toffsetdata_[rgb]=\t0x%x 0x%x 0x%x\n"
|
|
"\tlimitcondition=0x%x\n"
|
|
"\tdevice error code = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n"
|
|
"\tpower-on errors = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
|
s->offsetdata_r, s->offsetdata_g, s->offsetdata_b,
|
|
s->limitcondition,
|
|
s->derr[0], s->derr[1], s->derr[2], s->derr[3], s->derr[4],
|
|
s->derr[5], s->derr[6], s->derr[7],
|
|
s->power_on_errors[0], s->power_on_errors[1],
|
|
s->power_on_errors[2], s->power_on_errors[3],
|
|
s->power_on_errors[4], s->power_on_errors[5],
|
|
s->power_on_errors[6], s->power_on_errors[7]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
get_internal_info (Coolscan_t * s)
|
|
{
|
|
int ret;
|
|
|
|
DBG (10, "get_internal_info\n");
|
|
|
|
if(s->LS<2) /* distinguish between old and new scanners */
|
|
{ ret=get_internal_info_LS20 (s);
|
|
}
|
|
else
|
|
{ ret=get_inquiery_LS30 (s);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
coolscan_get_inquiry_values (Coolscan_t * s)
|
|
{
|
|
unsigned char *inquiry_block;
|
|
|
|
DBG (10, "get_inquiry_values\n");
|
|
|
|
inquiry_block = (unsigned char *) s->buffer;
|
|
s->inquiry_len = 36;
|
|
|
|
get_inquiry_vendor ((char *)inquiry_block, (char *)s->vendor);
|
|
s->vendor[8] = '\0';
|
|
get_inquiry_product ((char *)inquiry_block, (char *)s->product);
|
|
s->product[16] = '\0';
|
|
get_inquiry_version ((char *)inquiry_block, (char *)s->version);
|
|
s->version[4] = '\0';
|
|
|
|
if (s->inquiry_len < 36)
|
|
{
|
|
DBG (1, "WARNING: inquiry return block is unexpected short (%d instead of 36).\n", s->inquiry_len);
|
|
}
|
|
s->inquiry_wdb_len = 117;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
coolscan_initialize_values (Coolscan_t * s)
|
|
{
|
|
int i;
|
|
DBG (10, "initialize_values\n");
|
|
/* Initialize us structure */
|
|
if(s->LS<2) /* LS-20 or LS-10000 */
|
|
{ select_MUD (s); /* must be before mode_sense - not for LS-30*/
|
|
coolscan_mode_sense (s); /* Obtain MUD (Measurement Unit Divisor) */
|
|
get_internal_info (s); /* MUST be called first. */
|
|
s->wdb_len = 117;
|
|
}
|
|
if(s->LS>=2) /* LS-30 */
|
|
{
|
|
get_inquiery_LS30(s); /* Info about scanner*/
|
|
select_MUD (s); /* must be before mode_sense */
|
|
get_feeder_type_LS30(s);
|
|
s->wdb_len = 117;
|
|
}
|
|
|
|
s->cont = 0; /* do not continue if scanner is unknown */
|
|
s->verbose = 2; /* 1=verbose,2=very verbose */
|
|
|
|
|
|
s->x_nres = s->y_nres = 2; /* 2 => 1350 dpi */
|
|
s->x_p_nres = s->y_p_nres = 9; /* 9 => 300 dpi */
|
|
s->tlx = 0;
|
|
s->tly = 0;
|
|
s->brx = s->xmaxpix; /* 2700 / 1200; */
|
|
s->bry = s->ymaxpix; /* 2700 / 1200; */
|
|
|
|
|
|
s->set_auto = 0; /* Always 0 on Nikon LS-{100|2}0 */
|
|
s->preview = 0; /* 1 for preview */
|
|
s->colormode = RGB; /* GREYSCALE or RGB */
|
|
s->colormode_p = RGB; /* GREYSCALE or RGB for preview*/
|
|
s->asf = 0; /* 1 if asf shall be used */
|
|
s->gammaselection = WD_Linear;
|
|
|
|
s->brightness = 128;
|
|
s->brightness_R = 128;
|
|
s->brightness_G = 128;
|
|
s->brightness_B = 128;
|
|
s->contrast = 128;
|
|
s->contrast_R = 128;
|
|
s->contrast_G = 128;
|
|
s->contrast_B = 128;
|
|
|
|
s->exposure_R = 50;
|
|
s->exposure_G = 50;
|
|
s->exposure_B = 50;
|
|
|
|
s->pretv_r=40000;
|
|
s->pretv_g=40000;
|
|
s->pretv_b=40000;
|
|
|
|
s->shift_R = 128;
|
|
s->shift_G = 128;
|
|
s->shift_B = 128;
|
|
|
|
s->ired_red=60;
|
|
s->ired_green=1;
|
|
s->ired_blue=1;
|
|
|
|
s->prescan = 1;
|
|
s->bits_per_color = 8;
|
|
s->rgb_control = 0;
|
|
s->gamma_bind = 1;
|
|
switch(s->LS)
|
|
{ case 0:s->lutlength=2048;
|
|
s->max_lut_val=256;
|
|
break;
|
|
case 1:s->lutlength=512;
|
|
s->max_lut_val=512;
|
|
break;
|
|
case 2:s->lutlength=1024;
|
|
s->max_lut_val=1024;
|
|
break;
|
|
case 3:s->lutlength=4096;
|
|
s->max_lut_val=4096;
|
|
break;
|
|
}
|
|
for (i = 0; i < s->lutlength; i++)
|
|
{
|
|
s->gamma[i] =((short)((((double)i)/s->lutlength)*s->max_lut_val));
|
|
s->gamma_r[i] = s->gamma[i];
|
|
s->gamma_g[i] = s->gamma[i];
|
|
s->gamma_b[i] = s->gamma[i];
|
|
}
|
|
|
|
if (coolscan_test_little_endian() == SANE_TRUE)
|
|
{
|
|
s->low_byte_first = 1; /* in 2 byte mode send lowbyte first */
|
|
DBG(10,"backend runs on little endian machine\n");
|
|
}
|
|
else
|
|
{
|
|
s->low_byte_first = 0; /* in 2 byte mode send highbyte first */
|
|
DBG(10,"backend runs on big endian machine\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
hexdump (int level, char *comment, unsigned char *p, int l)
|
|
{
|
|
int i;
|
|
char line[128];
|
|
char *ptr;
|
|
|
|
DBG (level, "%s\n", comment);
|
|
ptr = line;
|
|
for (i = 0; i < l; i++, p++)
|
|
{
|
|
if ((i % 16) == 0)
|
|
{
|
|
if (ptr != line)
|
|
{
|
|
*ptr = '\0';
|
|
DBG (level, "%s\n", line);
|
|
ptr = line;
|
|
}
|
|
sprintf (ptr, "%3.3d:", i);
|
|
ptr += 4;
|
|
}
|
|
sprintf (ptr, " %2.2x", *p);
|
|
ptr += 3;
|
|
}
|
|
*ptr = '\0';
|
|
DBG (level, "%s\n", line);
|
|
}
|
|
|
|
|
|
static SANE_Status
|
|
sense_handler (int scsi_fd, unsigned char * result, void *arg)
|
|
{
|
|
(void) scsi_fd;
|
|
(void) arg;
|
|
|
|
if (result[0] != 0x70)
|
|
{
|
|
return SANE_STATUS_IO_ERROR; /* we only know about this one */
|
|
}
|
|
return request_sense_parse(result);
|
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
/* ilu per mm */
|
|
|
|
#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 2700.0))
|
|
#define mmToIlu(mm) ((mm) / length_quant)
|
|
#define iluToMm(ilu) ((ilu) * length_quant)
|
|
|
|
#define P_200_TO_255(per) SANE_UNFIX((per + 100) * 255/200 )
|
|
#define P_100_TO_255(per) SANE_UNFIX(per * 255/100 )
|
|
|
|
static const char negativeStr[] = "Negative";
|
|
static const char positiveStr[] = "Positive";
|
|
static SANE_String_Const type_list[] =
|
|
{
|
|
positiveStr,
|
|
negativeStr,
|
|
0
|
|
};
|
|
|
|
static const char colorStr[] = SANE_VALUE_SCAN_MODE_COLOR;
|
|
static const char grayStr[] = SANE_VALUE_SCAN_MODE_GRAY;
|
|
static const char rgbiStr[] = "RGBI";
|
|
static const char iredStr[] = "Infrared";
|
|
|
|
static SANE_String_Const scan_mode_list_LS20[] =
|
|
{
|
|
colorStr,
|
|
grayStr,
|
|
NULL
|
|
};
|
|
|
|
static SANE_String_Const scan_mode_list_LS30[] =
|
|
{
|
|
colorStr,
|
|
grayStr,
|
|
#ifdef HAS_IRED
|
|
rgbiStr,
|
|
#endif /* HAS_IRED */
|
|
NULL
|
|
};
|
|
|
|
static SANE_Int bit_depth_list[9];
|
|
|
|
static const char neverStr[] = "never";
|
|
static const char previewStr[] = "before preview";
|
|
static const char scanStr[] = "before scan";
|
|
static const char preandscanStr[] = "before preview and scan";
|
|
static SANE_String_Const autofocus_mode_list[] =
|
|
{
|
|
neverStr,
|
|
previewStr,
|
|
scanStr,
|
|
preandscanStr,
|
|
NULL
|
|
};
|
|
|
|
static SANE_String_Const source_list[4] =
|
|
{NULL, NULL, NULL, NULL};
|
|
|
|
static const SANE_Range gamma_range_8 =
|
|
{
|
|
0, /* minimum */
|
|
255, /* maximum */
|
|
1 /* quantization */
|
|
};
|
|
|
|
|
|
static const SANE_Range gamma_range_9 =
|
|
{
|
|
0, /* minimum */
|
|
511, /* maximum */
|
|
1 /* quantization */
|
|
};
|
|
|
|
static const SANE_Range gamma_range_10 =
|
|
{
|
|
0, /* minimum */
|
|
1023, /* maximum */
|
|
1 /* quantization */
|
|
};
|
|
|
|
static const SANE_Range gamma_range_12 =
|
|
{
|
|
0, /* minimum */
|
|
4096, /* maximum */
|
|
1 /* quantization */
|
|
};
|
|
|
|
static const SANE_Range brightness_range =
|
|
{
|
|
-5,
|
|
+5,
|
|
1
|
|
};
|
|
|
|
static const SANE_Range contrast_range =
|
|
{
|
|
-5,
|
|
+5,
|
|
0
|
|
};
|
|
|
|
static const SANE_Range exposure_range =
|
|
{
|
|
24,
|
|
400,
|
|
2
|
|
};
|
|
|
|
static const SANE_Range shift_range =
|
|
{
|
|
-15,
|
|
+15,
|
|
0
|
|
};
|
|
|
|
static const SANE_Device **devlist = 0;
|
|
static int num_devices;
|
|
static Coolscan_t *first_dev;
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
static SANE_Status
|
|
do_eof (Coolscan_t * scanner)
|
|
{
|
|
DBG (10, "do_eof\n");
|
|
|
|
if (scanner->pipe >= 0)
|
|
{
|
|
close (scanner->pipe);
|
|
scanner->pipe = -1;
|
|
}
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
|
|
static SANE_Status
|
|
do_cancel (Coolscan_t * scanner)
|
|
{
|
|
DBG (10, "do_cancel\n");
|
|
swap_res (scanner);
|
|
scanner->scanning = SANE_FALSE;
|
|
|
|
do_eof (scanner); /* close pipe and reposition scanner */
|
|
|
|
if (sanei_thread_is_valid (scanner->reader_pid))
|
|
{
|
|
int exit_status;
|
|
|
|
DBG (10, "do_cancel: kill reader_process\n");
|
|
|
|
/* ensure child knows it's time to stop: */
|
|
sanei_thread_kill (scanner->reader_pid);
|
|
while (sanei_thread_waitpid(scanner->reader_pid, &exit_status) !=
|
|
scanner->reader_pid );
|
|
sanei_thread_invalidate (scanner->reader_pid);
|
|
}
|
|
|
|
if (scanner->sfd >= 0)
|
|
{
|
|
coolscan_give_scanner (scanner);
|
|
DBG (10, "do_cancel: close filedescriptor\n");
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
}
|
|
|
|
return SANE_STATUS_CANCELLED;
|
|
}
|
|
|
|
static SANE_Status
|
|
attach_scanner (const char *devicename, Coolscan_t ** devp)
|
|
{
|
|
Coolscan_t *dev;
|
|
int sfd;
|
|
|
|
DBG (10, "attach_scanner: %s\n", devicename);
|
|
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
{
|
|
if (strcmp (dev->sane.name, devicename) == 0)
|
|
{
|
|
if (devp)
|
|
{
|
|
*devp = dev;
|
|
}
|
|
DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
DBG (10, "attach_scanner: opening %s\n", devicename);
|
|
if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0)
|
|
{
|
|
DBG (1, "attach_scanner: open failed\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (NULL == (dev = malloc (sizeof (*dev))))
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
|
|
dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024)) ?
|
|
sanei_scsi_max_request_size : 64 * 1024;
|
|
|
|
if ((dev->buffer = malloc (dev->row_bufsize)) == NULL)
|
|
/* if ((dev->buffer = malloc (sanei_scsi_max_request_size)) == NULL)*/
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
if ((dev->obuffer = malloc (dev->row_bufsize)) == NULL)
|
|
return SANE_STATUS_NO_MEM;
|
|
|
|
dev->devicename = strdup (devicename);
|
|
dev->sfd = sfd;
|
|
|
|
/* Nikon manual: Step 1 */
|
|
if (coolscan_identify_scanner (dev) != 0)
|
|
{
|
|
DBG (1, "attach_scanner: scanner-identification failed\n");
|
|
sanei_scsi_close (dev->sfd);
|
|
free (dev->buffer);
|
|
free (dev);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* Get MUD (via mode_sense), internal info (via get_internal_info), and
|
|
* initialize values */
|
|
coolscan_initialize_values (dev);
|
|
|
|
/* Why? */
|
|
sanei_scsi_close (dev->sfd);
|
|
dev->sfd = -1;
|
|
|
|
dev->sane.name = dev->devicename;
|
|
dev->sane.vendor = dev->vendor;
|
|
dev->sane.model = dev->product;
|
|
dev->sane.type = "slide scanner";
|
|
|
|
dev->x_range.min = SANE_FIX (0);
|
|
dev->x_range.quant = SANE_FIX (length_quant);
|
|
dev->x_range.max = SANE_FIX ((double) ((dev->xmaxpix) * length_quant));
|
|
|
|
dev->y_range.min = SANE_FIX (0.0);
|
|
dev->y_range.quant = SANE_FIX (length_quant);
|
|
dev->y_range.max = SANE_FIX ((double) ((dev->ymaxpix) * length_quant));
|
|
|
|
/* ...and this?? */
|
|
dev->dpi_range.min = SANE_FIX (108);
|
|
dev->dpi_range.quant = SANE_FIX (0);
|
|
dev->dpi_range.max = SANE_FIX (dev->maxres);
|
|
DBG (10, "attach: dev->dpi_range.max = %f\n",
|
|
SANE_UNFIX (dev->dpi_range.max));
|
|
|
|
++num_devices;
|
|
dev->next = first_dev;
|
|
first_dev = dev;
|
|
|
|
if (devp)
|
|
{
|
|
*devp = dev;
|
|
}
|
|
|
|
DBG (10, "attach_scanner done\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
static SANE_Status
|
|
attach_one (const char *devName)
|
|
{
|
|
return attach_scanner(devName, 0);
|
|
}
|
|
|
|
static void
|
|
sigterm_handler (int signal)
|
|
{
|
|
(void) signal;
|
|
sanei_scsi_req_flush_all (); /* flush SCSI queue */
|
|
_exit (SANE_STATUS_GOOD);
|
|
}
|
|
|
|
|
|
typedef struct Color_correct_s
|
|
{ int sum; /* number of pixels summed so far */
|
|
double sumr; /* sum of red pixel values*/
|
|
double sumi; /* sum of infrared pixel values*/
|
|
double sumri; /* sum of red*ired pixel values*/
|
|
double sumii; /* sum of ired*ired pixel values*/
|
|
double sumrr; /* sum of ired*ired pixel values*/
|
|
int mr; /* factor between red and ired values (*256) */
|
|
int br; /* offset of ired values */
|
|
} ColorCorrect;
|
|
|
|
/* ---------------------------------------------------------------
|
|
|
|
function: RGBIfix
|
|
|
|
task: Correct the infrared channel
|
|
|
|
import: unsigned char * rgbimat - RGBI - matrix from scanner
|
|
int size - number of pixels to correct
|
|
int *lutr - lookup table for red correction
|
|
int *lutg - lookup table for red correction
|
|
int *lutb - lookup table for red correction
|
|
int *lutr - lookup table for red correction
|
|
|
|
export: unsigned char * orgbimat - RGBI - corrected matrix
|
|
|
|
written by: Andreas RICK 19.6.1999
|
|
|
|
----------------------------------------------------------------*/
|
|
|
|
static int Calc_fix_LUT(Coolscan_t * s)
|
|
{ int uselutr,uselutg,uselutb,useluti;
|
|
/* static int irmulr= -34*25; */
|
|
int irmulr= -64*25;
|
|
int irmulg= -1*25;
|
|
int irmulb= -0*25;
|
|
int irmuli= 256*25;
|
|
int div;
|
|
int i;
|
|
|
|
irmulr=s->ired_red*(25);
|
|
irmulg=s->ired_green*(25);
|
|
irmulb=s->ired_blue*(25);
|
|
irmuli=25*256;
|
|
|
|
if(s->LS==2) /* TODO: right conversion factors for 10 and 12 bit */
|
|
{ div=4;
|
|
}
|
|
else if(s->LS==3)
|
|
{ div=16;
|
|
}
|
|
else
|
|
{ return 0;
|
|
}
|
|
|
|
memset(s->lutr, 0,256*4);
|
|
memset(s->lutg, 0,256*4);
|
|
memset(s->lutb, 0,256*4);
|
|
memset(s->luti, 0,256*4);
|
|
|
|
for(i=0;i<s->lutlength;i++)
|
|
{ if(s->gamma_bind)
|
|
{ uselutr=uselutg=uselutb=useluti=s->gamma[i]/div;
|
|
}
|
|
else
|
|
{ uselutr=s->gamma_r[i]/div;
|
|
uselutg=s->gamma_g[i]/div;
|
|
uselutb=s->gamma_b[i]/div;
|
|
useluti=s->gamma_r[i]/div;
|
|
}
|
|
s->lutr[uselutr]=(int)(irmulr*pow((double)i,(double)0.333333));
|
|
s->lutg[uselutg]=(int)(irmulg*pow((double)i,(double)0.333333));
|
|
s->lutb[uselutb]=(int)(irmulb*pow((double)i,(double)0.333333));
|
|
s->luti[useluti]=(int)(irmuli*pow((double)i,(double)0.333333));
|
|
if(uselutr<255)
|
|
{ if(s->lutr[uselutr+1]==0) s->lutr[uselutr+1]=s->lutr[uselutr];
|
|
}
|
|
if(uselutg<255)
|
|
{ if(s->lutg[uselutg+1]==0) s->lutg[uselutg+1]=s->lutg[uselutg];
|
|
}
|
|
if(uselutb<255)
|
|
{ if(s->lutb[uselutb+1]==0) s->lutb[uselutb+1]=s->lutb[uselutb];
|
|
}
|
|
if(useluti<255)
|
|
{ if(s->luti[useluti+1]==0) s->luti[useluti+1]=s->luti[useluti];
|
|
}
|
|
}
|
|
/* DEBUG
|
|
for(i=0;i<255;i++)
|
|
{ fprintf(stderr,"%d %d %d %d\n"
|
|
,s->lutr[i],s->lutg[i],s->lutb[i],s->luti[i]);
|
|
}
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------
|
|
|
|
function: RGBIfix
|
|
|
|
task: Correct the infrared channel
|
|
|
|
import: unsigned char * rgbimat - RGBI - matrix from scanner
|
|
int size - number of pixels to correct
|
|
int *lutr - lookup table for red correction
|
|
int *lutg - lookup table for red correction
|
|
int *lutb - lookup table for red correction
|
|
int *lutr - lookup table for red correction
|
|
|
|
export: unsigned char * orgbimat - RGBI - corrected matrix
|
|
|
|
written by: Andreas RICK 19.6.1999
|
|
|
|
----------------------------------------------------------------*/
|
|
|
|
static int RGBIfix(Coolscan_t * scanner,
|
|
unsigned char* rgbimat,
|
|
unsigned char* orgbimat,
|
|
int size,
|
|
int *lutr,
|
|
int *lutg,
|
|
int *lutb,
|
|
int *luti)
|
|
|
|
{
|
|
unsigned char *pr,*pg,*pb,*pi;
|
|
unsigned char *opr,*opg,*opb,*opi;
|
|
|
|
int r,g,b,i;
|
|
int ii;
|
|
int x;
|
|
for(x=0;x<size;x++)
|
|
{
|
|
pr=rgbimat+x*4;
|
|
pg=pr+1;
|
|
pb=pg+1;
|
|
pi=pb+1;
|
|
opr=orgbimat+x*4;
|
|
opg=opr+1;
|
|
opb=opg+1;
|
|
opi=opb+1;
|
|
r=lutr[(*pr)];
|
|
g=lutg[(*pg)];
|
|
b=lutb[(*pb)];
|
|
i=luti[(*pi)];
|
|
ii= i-r-g-b;
|
|
(*opr)=(*pr);
|
|
(*opg)=(*pg);
|
|
(*opb)=(*pb);
|
|
if(ii<0)ii=0;
|
|
if(ii>255*256)ii=255*256;
|
|
if(scanner->negative)
|
|
{
|
|
(*opi)=(unsigned char)(255-(ii>>8));
|
|
}
|
|
else
|
|
{
|
|
(*opi)=(unsigned char)(ii>>8);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------
|
|
|
|
function: RGBIfix16
|
|
|
|
task: Correct the infrared channel for 16 bit images
|
|
(doesn't do anything for now)
|
|
|
|
import: unsigned char * rgbimat - RGBI - matrix from scanner
|
|
int size - number of pixels to correct
|
|
int *lutr - lookup table for red correction
|
|
int *lutg - lookup table for red correction
|
|
int *lutb - lookup table for red correction
|
|
int *lutr - lookup table for red correction
|
|
|
|
export: unsigned char * orgbimat - RGBI - corrected matrix
|
|
|
|
written by: Andreas RICK 19.6.1999
|
|
|
|
----------------------------------------------------------------*/
|
|
|
|
static int RGBIfix16(Coolscan_t * scanner,
|
|
unsigned short* rgbimat,
|
|
unsigned short* orgbimat,
|
|
int size,
|
|
int *lutr,
|
|
int *lutg,
|
|
int *lutb,
|
|
int *luti)
|
|
|
|
{
|
|
unsigned short *pr,*pg,*pb,*pi;
|
|
unsigned short *opr,*opg,*opb,*opi;
|
|
int x;
|
|
|
|
(void) scanner; (void) lutr; (void) lutg; (void) lutb; (void) luti;
|
|
|
|
for(x=0;x<size;x++)
|
|
{
|
|
pr=rgbimat+x*4;
|
|
pg=pr+1;
|
|
pb=pg+1;
|
|
pi=pb+1;
|
|
opr=orgbimat+x*4;
|
|
opg=opr+1;
|
|
opb=opg+1;
|
|
opi=opb+1;
|
|
(*opr)=(((*pr)&0x00ff)<<8)+(((*pr)&0xff00)>>8);
|
|
(*opg)=(((*pg)&0x00ff)<<8)+(((*pg)&0xff00)>>8);
|
|
(*opb)=(((*pb)&0x00ff)<<8)+(((*pb)&0xff00)>>8);
|
|
(*opi)=(((*pi)&0x00ff)<<8)+(((*pi)&0xff00)>>8);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------
|
|
|
|
function: rgb2g
|
|
|
|
task: Convert RGB data to grey
|
|
|
|
import: unsigned char * rgbimat - RGB - matrix from scanner
|
|
int size - size of input data (num pixel)
|
|
|
|
export: unsigned char * gomat - Grey matrix
|
|
|
|
written by: Andreas RICK 13.7.1999
|
|
|
|
----------------------------------------------------------------*/
|
|
#define RtoG ((int)(0.27*256))
|
|
#define GtoG ((int)(0.54*256))
|
|
#define BtoG ((int)(0.19*256))
|
|
|
|
static int rgb2g(unsigned char* rgbimat,unsigned char* gomat,
|
|
int size)
|
|
|
|
{ unsigned char *pr,*pg,*pb;
|
|
unsigned char *opg;
|
|
|
|
int g;
|
|
int x;
|
|
for(x=0;x<size;x++)
|
|
{
|
|
pr=rgbimat+x*3;
|
|
pg=pr+1;
|
|
pb=pg+1;
|
|
opg=gomat+x;
|
|
g= RtoG*(*pr) + GtoG*(*pg) + BtoG*(*pb);
|
|
(*opg)=(unsigned char)(g>>8);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------
|
|
|
|
function: RGBIfix1
|
|
|
|
task: Correct the infrared channel.
|
|
The input image data is the output of scanning
|
|
with LUT. To calculate the original values
|
|
the lutr and luti is applied.
|
|
The infrared values is corrected by:
|
|
|
|
Ir=mr*lutr(r)+luti(i)
|
|
|
|
import: unsigned char * rgbimat - RGBI - matrix from scanner
|
|
int size - number of pixels to correct
|
|
ColorCorrect *cc,
|
|
int *lutr - lookup table for red correction
|
|
int *luti - lookup table for ired correction
|
|
|
|
export: unsigned char * orgbimat - RGBI - corrected matrix
|
|
|
|
written by: Andreas RICK 3.7.1999
|
|
|
|
----------------------------------------------------------------*/
|
|
#if 0
|
|
static int RGBIfix1(unsigned char* rgbimat,unsigned char* orgbimat,
|
|
int size,
|
|
int *lutr,
|
|
int *lutg,
|
|
int *lutb,
|
|
int *luti)
|
|
|
|
{ unsigned char *pr,*pg,*pb,*pi;
|
|
unsigned char *opr,*opg,*opb,*opi;
|
|
ColorCorrect cc;
|
|
int r,i;
|
|
static int thresi=100;
|
|
int ii;
|
|
int x;
|
|
|
|
(void) lutg; (void) lutb;
|
|
|
|
/* calculate regression between r and ir */
|
|
cc.sum=0;
|
|
cc.sumr=cc.sumii=cc.sumrr=cc.sumi=cc.sumri=0.0;
|
|
for(x=0;x<size;x++)
|
|
{ pr=rgbimat+x*4;
|
|
pi=pr+3;
|
|
r=lutr[(*pr)];
|
|
i=luti[(*pi)];
|
|
/* r=(*pr);
|
|
i=(*pi); */
|
|
if((*pi)>thresi)
|
|
{ cc.sum++;
|
|
cc.sumr+=r;
|
|
cc.sumii+=(i*i);
|
|
cc.sumrr+=(r*r);
|
|
cc.sumi+=i;
|
|
cc.sumri+=(i*r);
|
|
}
|
|
}
|
|
if((cc.sumii!=0)&&(cc.sum!=0))
|
|
{ double dn,dz,dm;
|
|
dz=(cc.sumri-cc.sumr*cc.sumi/cc.sum);
|
|
dn=(cc.sumrr-cc.sumr*cc.sumr/cc.sum);
|
|
DBG (2, "Reg:dz:%e dn:%e\n",dz,dn);
|
|
if(dn!=0)
|
|
{ dm=(dz/dn);
|
|
cc.mr=(int)(dm*1024);
|
|
}
|
|
else
|
|
{ cc.mr=0;
|
|
dm=0;
|
|
}
|
|
cc.br=(int)((cc.sumi-dm*cc.sumr)/cc.sum);
|
|
}
|
|
else
|
|
{ cc.mr=0;
|
|
}
|
|
DBG (2, "Regression: size:%d I=%d/1024*R b:%d s:%d sr:%e si:%e sii:%e sri:%e srr:%e\n",
|
|
size,cc.mr,cc.br,cc.sum,cc.sumr,cc.sumi,cc.sumii,cc.sumri,cc.sumrr);
|
|
for(x=0;x<size;x++)
|
|
{
|
|
|
|
pr=rgbimat+x*4;
|
|
pg=pr+1;
|
|
pb=pg+1;
|
|
pi=pb+1;
|
|
opr=orgbimat+x*4;
|
|
opg=opr+1;
|
|
opb=opg+1;
|
|
opi=opb+1;
|
|
r=lutr[(*pr)];
|
|
i=luti[(*pi)];
|
|
/* r=(*pr);
|
|
i=(*pi); */
|
|
ii= ((i-((r*cc.mr)>>10)-cc.br)>>2) +128;
|
|
(*opr)=(*pr);
|
|
(*opg)=(*pg);
|
|
(*opb)=(*pb);
|
|
if(ii<0) ii=0;
|
|
if(ii>255) ii=255;
|
|
(*opi)=(unsigned char)(ii);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
/* This function is executed as a child process. */
|
|
static int
|
|
reader_process (void *data )
|
|
{
|
|
int status;
|
|
unsigned int i;
|
|
unsigned char h;
|
|
unsigned int data_left;
|
|
unsigned int data_to_read;
|
|
unsigned int data_to_write;
|
|
FILE *fp;
|
|
sigset_t sigterm_set, ignore_set;
|
|
struct SIGACTION act;
|
|
unsigned int bpl, linesPerBuf, lineOffset;
|
|
unsigned char r_data, g_data, b_data;
|
|
unsigned int j, line;
|
|
Coolscan_t * scanner = (Coolscan_t*)data;
|
|
|
|
if (sanei_thread_is_forked ())
|
|
{
|
|
DBG (10, "reader_process started (forked)\n");
|
|
close (scanner->pipe);
|
|
scanner->pipe = -1;
|
|
|
|
sigfillset ( &ignore_set );
|
|
sigdelset ( &ignore_set, SIGTERM );
|
|
#if defined (__APPLE__) && defined (__MACH__)
|
|
sigdelset ( &ignore_set, SIGUSR2 );
|
|
#endif
|
|
sigprocmask( SIG_SETMASK, &ignore_set, 0 );
|
|
|
|
memset (&act, 0, sizeof (act));
|
|
sigaction (SIGTERM, &act, 0);
|
|
}
|
|
else
|
|
{
|
|
DBG (10, "reader_process started (as thread)\n");
|
|
}
|
|
|
|
sigemptyset (&sigterm_set);
|
|
sigaddset (&sigterm_set, SIGTERM);
|
|
|
|
fp = fdopen ( scanner->reader_fds, "w");
|
|
if (!fp)
|
|
{
|
|
DBG (1, "reader_process: couldn't open pipe!\n");
|
|
return 1;
|
|
}
|
|
|
|
DBG (10, "reader_process: starting to READ data\n");
|
|
|
|
data_left = scan_bytes_per_line (scanner) *
|
|
lines_per_scan (scanner);
|
|
|
|
/*scanner->row_bufsize = sanei_scsi_max_request_size;*/
|
|
coolscan_trim_rowbufsize (scanner); /* trim bufsize */
|
|
|
|
DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n",
|
|
data_left, scanner->row_bufsize);
|
|
|
|
memset (&act, 0, sizeof (act));
|
|
act.sa_handler = sigterm_handler;
|
|
sigaction (SIGTERM, &act, 0);
|
|
/* wait_scanner(scanner); */
|
|
do
|
|
{
|
|
data_to_read = (data_left < scanner->row_bufsize) ?
|
|
data_left : scanner->row_bufsize;
|
|
|
|
data_to_write=data_to_read;
|
|
|
|
status = coolscan_read_data_block (scanner
|
|
,R_datatype_imagedata,data_to_read);
|
|
if (status == 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (status == -1)
|
|
{
|
|
DBG (1, "reader_process: unable to get image data from scanner!\n");
|
|
fclose (fp);
|
|
return (-1);
|
|
}
|
|
|
|
if (scanner->LS == 1) { /* mirror image for LS-1000 */
|
|
bpl = scan_bytes_per_line(scanner);
|
|
linesPerBuf = data_to_read / bpl;
|
|
|
|
for (line = 0, lineOffset = 0; line < linesPerBuf;
|
|
line++, lineOffset += bpl ) {
|
|
|
|
if (scanner->colormode == RGB) {
|
|
for (j = 0; j < bpl/2 ; j += 3) {
|
|
r_data=scanner->buffer[lineOffset + j];
|
|
g_data=scanner->buffer[lineOffset + j + 1];
|
|
b_data=scanner->buffer[lineOffset + j + 2];
|
|
|
|
scanner->buffer[lineOffset + j] =
|
|
scanner->buffer[lineOffset + bpl -1 - j - 2 ];
|
|
scanner->buffer[lineOffset + j + 1] =
|
|
scanner->buffer[lineOffset + bpl -1 - j - 1 ];
|
|
scanner->buffer[lineOffset + j + 2] =
|
|
scanner->buffer[lineOffset + bpl -1 - j ];
|
|
|
|
scanner->buffer[lineOffset + bpl -1 - j - 2 ] = r_data;
|
|
scanner->buffer[lineOffset + bpl -1 - j - 1] = g_data;
|
|
scanner->buffer[lineOffset + bpl -1 - j] = b_data;
|
|
}
|
|
}
|
|
else {
|
|
for (j = 0; j < bpl/2; j++) {
|
|
r_data=scanner->buffer[lineOffset + j];
|
|
scanner->buffer[lineOffset + j] =
|
|
scanner->buffer[lineOffset + bpl - 1 - j];
|
|
scanner->buffer[lineOffset + bpl - 1 - j] = r_data;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(scanner->colormode==RGBI)
|
|
{ /* Correct Infrared Channel */
|
|
if(scanner->bits_per_color>8)
|
|
{
|
|
RGBIfix16(scanner, (unsigned short * ) scanner->buffer,
|
|
(unsigned short * )scanner->obuffer,
|
|
data_to_read/8,scanner->lutr,
|
|
scanner->lutg,scanner->lutb,scanner->luti);
|
|
}
|
|
else
|
|
{
|
|
RGBIfix(scanner,scanner->buffer,scanner->obuffer,
|
|
data_to_read/4,scanner->lutr,
|
|
scanner->lutg,scanner->lutb,scanner->luti);
|
|
}
|
|
}
|
|
else if((scanner->colormode==GREYSCALE)&&(scanner->LS>=2))
|
|
{ /* Convert to Grey */
|
|
data_to_write/=3;
|
|
rgb2g(scanner->buffer,scanner->obuffer,data_to_write);
|
|
}
|
|
else
|
|
{ /* or just copy */
|
|
memcpy (scanner->obuffer, scanner->buffer,data_to_read);
|
|
}
|
|
if((!scanner->low_byte_first)&&(scanner->bits_per_color>8))
|
|
{ for(i=0;i<data_to_write;i++) /* inverse byteorder */
|
|
{ h=scanner->obuffer[i];
|
|
scanner->obuffer[i]=scanner->obuffer[i+1];
|
|
i++;
|
|
scanner->obuffer[i]=h;
|
|
}
|
|
}
|
|
fwrite (scanner->obuffer, 1, data_to_write, fp);
|
|
fflush (fp);
|
|
data_left -= data_to_read;
|
|
DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n",
|
|
data_to_read, data_left);
|
|
}
|
|
while (data_left);
|
|
|
|
fclose (fp);
|
|
|
|
DBG (10, "reader_process: finished reading data\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SANE_Status
|
|
init_options (Coolscan_t * scanner)
|
|
{
|
|
int i;
|
|
int bit_depths;
|
|
|
|
DBG (10, "init_options\n");
|
|
|
|
memset (scanner->opt, 0, sizeof (scanner->opt));
|
|
|
|
for (i = 0; i < NUM_OPTIONS; ++i)
|
|
{
|
|
scanner->opt[i].size = sizeof (SANE_Word);
|
|
scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
}
|
|
|
|
scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
|
|
scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
|
|
scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
|
|
|
|
/* "Mode" group: */
|
|
scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
|
|
scanner->opt[OPT_MODE_GROUP].desc = "";
|
|
scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_MODE_GROUP].cap = 0;
|
|
scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* scan mode */
|
|
scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
|
|
scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
|
|
scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
|
|
scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
|
|
if(scanner->LS<2)
|
|
{ scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS20);
|
|
scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS20;
|
|
}
|
|
else
|
|
{ scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS30);
|
|
scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS30;
|
|
}
|
|
|
|
/* source */
|
|
source_list[0] = "Slide";
|
|
source_list[1] = "Automatic Slide Feeder";
|
|
source_list[2] = NULL;
|
|
if (!scanner->autofeeder)
|
|
{
|
|
scanner->opt[OPT_SOURCE].cap = SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
|
|
scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
|
|
scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
|
|
scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_SOURCE].size = max_string_size (source_list);
|
|
scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
|
|
|
|
/* negative */
|
|
scanner->opt[OPT_TYPE].name = "type";
|
|
scanner->opt[OPT_TYPE].title = "Film type";
|
|
scanner->opt[OPT_TYPE].desc =
|
|
"Select the film type (positive (slide) or negative)";
|
|
scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_TYPE].size = max_string_size (type_list);
|
|
scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_TYPE].constraint.string_list = type_list;
|
|
|
|
scanner->opt[OPT_PRESCAN].name = "prescan";
|
|
scanner->opt[OPT_PRESCAN].title = "Prescan";
|
|
scanner->opt[OPT_PRESCAN].desc =
|
|
"Perform a prescan during preview";
|
|
scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE;
|
|
|
|
scanner->opt[OPT_PRESCAN_NOW].name = "prescan now";
|
|
scanner->opt[OPT_PRESCAN_NOW].title = "Prescan now";
|
|
scanner->opt[OPT_PRESCAN_NOW].desc =
|
|
"Perform a prescan now";
|
|
scanner->opt[OPT_PRESCAN_NOW].type = SANE_TYPE_BUTTON;
|
|
scanner->opt[OPT_PRESCAN_NOW].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_PRESCAN_NOW].size = 0;
|
|
scanner->opt[OPT_PRESCAN_NOW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
scanner->opt[OPT_PRESCAN_NOW].constraint_type = SANE_CONSTRAINT_NONE;
|
|
scanner->opt[OPT_PRESCAN_NOW].constraint.string_list = 0;
|
|
|
|
/* bit depth */
|
|
|
|
bit_depths=0;
|
|
bit_depth_list[++bit_depths] = 8;
|
|
if (scanner->LS==2)
|
|
{
|
|
bit_depth_list[++bit_depths] = 10;
|
|
}
|
|
if (scanner->LS==3)
|
|
{
|
|
bit_depth_list[++bit_depths] = 12;
|
|
}
|
|
|
|
bit_depth_list[0] = bit_depths;
|
|
|
|
scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
|
|
scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
|
|
scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
|
|
scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT;
|
|
scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
|
|
|
|
/* resolution */
|
|
scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
|
|
scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
|
|
scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
|
|
scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
|
|
scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_RESOLUTION].constraint.word_list = resolution_list;
|
|
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].name = "preview-resolution";
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].title = "Preview resolution";
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].unit = SANE_UNIT_DPI;
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
scanner->opt[OPT_PREVIEW_RESOLUTION].constraint.word_list = resolution_list;
|
|
|
|
/* "Geometry" group: */
|
|
scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
|
|
scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
|
|
scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
|
|
scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* top-left x */
|
|
scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
|
|
scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
|
|
scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
|
|
scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_TL_X].constraint.range = &(scanner->x_range);
|
|
|
|
/* top-left y */
|
|
scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
|
|
scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
|
|
scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
|
|
scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_TL_Y].constraint.range = &(scanner->y_range);
|
|
|
|
/* bottom-right x */
|
|
scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
|
|
scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
|
|
scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
|
|
scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_BR_X].constraint.range = &(scanner->x_range);
|
|
|
|
/* bottom-right y */
|
|
scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
|
|
scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
|
|
scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
|
|
scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
|
|
scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
|
|
scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_BR_Y].constraint.range = &(scanner->y_range);
|
|
|
|
|
|
/* ------------------------------ */
|
|
|
|
/* "Enhancement" group: */
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
|
|
scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
|
|
scanner->opt[OPT_GAMMA_BIND].name = "gamma-bind";
|
|
scanner->opt[OPT_GAMMA_BIND].title = "Gamma bind";
|
|
scanner->opt[OPT_GAMMA_BIND].desc =
|
|
"Use same gamma correction for all colours";
|
|
scanner->opt[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_GAMMA_BIND].unit = SANE_UNIT_NONE;
|
|
|
|
|
|
scanner->opt[OPT_ANALOG_GAMMA].name = "analog_gamma";
|
|
scanner->opt[OPT_ANALOG_GAMMA].title = "Analog Gamma";
|
|
scanner->opt[OPT_ANALOG_GAMMA].desc = "Analog Gamma";
|
|
scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
|
|
if (!scanner->analoggamma)
|
|
{
|
|
scanner->opt[OPT_ANALOG_GAMMA].cap = SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
scanner->opt[OPT_AVERAGING].name = "averaging";
|
|
scanner->opt[OPT_AVERAGING].title = "Averaging";
|
|
scanner->opt[OPT_AVERAGING].desc = "Averaging";
|
|
scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE;
|
|
|
|
|
|
scanner->opt[OPT_RGB_CONTROL].name = "rgb-control";
|
|
scanner->opt[OPT_RGB_CONTROL].title = "RGB control";
|
|
scanner->opt[OPT_RGB_CONTROL].desc =
|
|
"toggles brightness/contrast control over individual colours";
|
|
scanner->opt[OPT_RGB_CONTROL].type = SANE_TYPE_BOOL;
|
|
scanner->opt[OPT_RGB_CONTROL].unit = SANE_UNIT_NONE;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_RGB_CONTROL].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
|
|
/* brightness */
|
|
scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
|
|
scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
|
|
scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
|
|
scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
scanner->opt[OPT_R_BRIGHTNESS].name = "red-brightness";
|
|
scanner->opt[OPT_R_BRIGHTNESS].title = "Red brightness";
|
|
scanner->opt[OPT_R_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
|
|
scanner->opt[OPT_R_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_R_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_R_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_R_BRIGHTNESS].constraint.range = &brightness_range;
|
|
scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_G_BRIGHTNESS].name = "green-brightness";
|
|
scanner->opt[OPT_G_BRIGHTNESS].title = "Green brightness";
|
|
scanner->opt[OPT_G_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
|
|
scanner->opt[OPT_G_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_G_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_G_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_G_BRIGHTNESS].constraint.range = &brightness_range;
|
|
scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_B_BRIGHTNESS].name = "blue-brightness";
|
|
scanner->opt[OPT_B_BRIGHTNESS].title = "Blue brightness";
|
|
scanner->opt[OPT_B_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
|
|
scanner->opt[OPT_B_BRIGHTNESS].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_B_BRIGHTNESS].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_B_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_B_BRIGHTNESS].constraint.range = &brightness_range;
|
|
scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
/* contrast */
|
|
scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
|
|
scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
|
|
scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
|
|
scanner->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_CONTRAST].constraint.range = &contrast_range;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
scanner->opt[OPT_R_CONTRAST].name = "red-contrast";
|
|
scanner->opt[OPT_R_CONTRAST].title = "Red contrast";
|
|
scanner->opt[OPT_R_CONTRAST].desc = SANE_DESC_CONTRAST;
|
|
scanner->opt[OPT_R_CONTRAST].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_R_CONTRAST].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_R_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_R_CONTRAST].constraint.range = &contrast_range;
|
|
scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_G_CONTRAST].name = "green-contrast";
|
|
scanner->opt[OPT_G_CONTRAST].title = "Green contrast";
|
|
scanner->opt[OPT_G_CONTRAST].desc = SANE_DESC_CONTRAST;
|
|
scanner->opt[OPT_G_CONTRAST].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_G_CONTRAST].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_G_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_G_CONTRAST].constraint.range = &contrast_range;
|
|
scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_B_CONTRAST].name = "blue-contrast";
|
|
scanner->opt[OPT_B_CONTRAST].title = "Blue contrast";
|
|
scanner->opt[OPT_B_CONTRAST].desc = SANE_DESC_CONTRAST;
|
|
scanner->opt[OPT_B_CONTRAST].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_B_CONTRAST].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_B_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_B_CONTRAST].constraint.range = &contrast_range;
|
|
scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_EXPOSURE].name = "exposure";
|
|
scanner->opt[OPT_EXPOSURE].title = "Exposure";
|
|
scanner->opt[OPT_EXPOSURE].desc = "";
|
|
scanner->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
|
|
scanner->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_EXPOSURE].constraint.range = &exposure_range;
|
|
|
|
scanner->opt[OPT_R_EXPOSURE].name = "red-exposure";
|
|
scanner->opt[OPT_R_EXPOSURE].title = "Red exposure";
|
|
scanner->opt[OPT_R_EXPOSURE].desc = "";
|
|
scanner->opt[OPT_R_EXPOSURE].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_R_EXPOSURE].unit = SANE_UNIT_PERCENT;
|
|
scanner->opt[OPT_R_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_R_EXPOSURE].constraint.range = &exposure_range;
|
|
|
|
scanner->opt[OPT_G_EXPOSURE].name = "green-exposure";
|
|
scanner->opt[OPT_G_EXPOSURE].title = "Green exposure";
|
|
scanner->opt[OPT_G_EXPOSURE].desc = "";
|
|
scanner->opt[OPT_G_EXPOSURE].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_G_EXPOSURE].unit = SANE_UNIT_PERCENT;
|
|
scanner->opt[OPT_G_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_G_EXPOSURE].constraint.range = &exposure_range;
|
|
scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_B_EXPOSURE].name = "blue-exposure";
|
|
scanner->opt[OPT_B_EXPOSURE].title = "Blue exposre";
|
|
scanner->opt[OPT_B_EXPOSURE].desc = "";
|
|
scanner->opt[OPT_B_EXPOSURE].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_B_EXPOSURE].unit = SANE_UNIT_PERCENT;
|
|
scanner->opt[OPT_B_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_B_EXPOSURE].constraint.range = &exposure_range;
|
|
scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
scanner->opt[OPT_R_SHIFT].name = "red-shift";
|
|
scanner->opt[OPT_R_SHIFT].title = "Red shift";
|
|
scanner->opt[OPT_R_SHIFT].desc = "";
|
|
scanner->opt[OPT_R_SHIFT].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_R_SHIFT].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_R_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_R_SHIFT].constraint.range = &shift_range;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_R_SHIFT].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
scanner->opt[OPT_G_SHIFT].name = "green-shift";
|
|
scanner->opt[OPT_G_SHIFT].title = "Green shift";
|
|
scanner->opt[OPT_G_SHIFT].desc = "";
|
|
scanner->opt[OPT_G_SHIFT].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_G_SHIFT].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_G_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_G_SHIFT].constraint.range = &shift_range;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_G_SHIFT].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
scanner->opt[OPT_B_SHIFT].name = "blue-shift";
|
|
scanner->opt[OPT_B_SHIFT].title = "Blue shift";
|
|
scanner->opt[OPT_B_SHIFT].desc = "";
|
|
scanner->opt[OPT_B_SHIFT].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_B_SHIFT].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_B_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_B_SHIFT].constraint.range = &shift_range;
|
|
if(scanner->LS>=2)
|
|
{ scanner->opt[OPT_B_SHIFT].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
/* R+G+B gamma vector */
|
|
scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
|
|
scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
|
|
scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
|
|
scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
|
|
if (scanner->LS == 1)
|
|
{
|
|
scanner->opt[OPT_GAMMA_VECTOR].cap = SANE_CAP_INACTIVE;
|
|
}
|
|
scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
|
|
switch(scanner->LS)
|
|
{ case 0:
|
|
scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_8;
|
|
scanner->lutlength=2048;
|
|
break;
|
|
case 1:
|
|
scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_9;
|
|
scanner->lutlength=512;
|
|
break;
|
|
case 2:
|
|
scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_10;
|
|
scanner->lutlength=1024;
|
|
break;
|
|
case 3:
|
|
scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_12;
|
|
scanner->lutlength=4096;
|
|
break;
|
|
}
|
|
scanner->opt[OPT_GAMMA_VECTOR].size = scanner->lutlength * sizeof (SANE_Word);
|
|
scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
/* red gamma vector */
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
|
|
switch(scanner->LS)
|
|
{ case 0:
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_8;
|
|
scanner->lutlength=2048;
|
|
break;
|
|
case 1:
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_9;
|
|
scanner->lutlength=512;
|
|
break;
|
|
case 2:
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_10;
|
|
scanner->lutlength=1024;
|
|
break;
|
|
case 3:
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_12;
|
|
scanner->lutlength=4096;
|
|
break;
|
|
}
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->lutlength * sizeof (SANE_Word);
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
/* green gamma vector */
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
|
|
switch(scanner->LS)
|
|
{ case 0:
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_8;
|
|
scanner->lutlength=2048;
|
|
break;
|
|
case 1:
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_9;
|
|
scanner->lutlength=512;
|
|
break;
|
|
case 2:
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_10;
|
|
scanner->lutlength=1024;
|
|
break;
|
|
case 3:
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_12;
|
|
scanner->lutlength=4096;
|
|
break;
|
|
}
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->lutlength * sizeof (SANE_Word);
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
/* blue gamma vector */
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
|
|
switch(scanner->LS)
|
|
{ case 0:
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_8;
|
|
scanner->lutlength=2048;
|
|
break;
|
|
case 1:
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_9;
|
|
scanner->lutlength=512;
|
|
break;
|
|
case 2:
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_10;
|
|
scanner->lutlength=1024;
|
|
break;
|
|
case 3:
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_12;
|
|
scanner->lutlength=4096;
|
|
break;
|
|
}
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->lutlength * sizeof (SANE_Word);
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
|
|
|
|
/* ------------------------------ */
|
|
|
|
/* "Advanced" group: */
|
|
scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
|
|
scanner->opt[OPT_ADVANCED_GROUP].desc = "";
|
|
scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
|
|
scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
|
|
scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* preview */
|
|
scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
|
|
scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
|
|
scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
|
|
scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
|
|
|
|
/* Autofocus */
|
|
scanner->opt[OPT_AUTOFOCUS].name = "Autofocus";
|
|
scanner->opt[OPT_AUTOFOCUS].title ="Autofocus";
|
|
scanner->opt[OPT_AUTOFOCUS].desc = "When to do autofocussing";
|
|
scanner->opt[OPT_AUTOFOCUS].type = SANE_TYPE_STRING;
|
|
scanner->opt[OPT_AUTOFOCUS].size = max_string_size (autofocus_mode_list);
|
|
scanner->opt[OPT_AUTOFOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
scanner->opt[OPT_AUTOFOCUS].constraint.string_list = autofocus_mode_list;
|
|
|
|
scanner->opt[OPT_IRED_RED].name = "IRED cor. red";
|
|
scanner->opt[OPT_IRED_RED].title ="IRED cor. red";
|
|
scanner->opt[OPT_IRED_RED].desc = "Correction of infrared from red";
|
|
scanner->opt[OPT_IRED_RED].type = SANE_TYPE_INT;
|
|
scanner->opt[OPT_IRED_RED].unit = SANE_UNIT_NONE;
|
|
scanner->opt[OPT_IRED_RED].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
scanner->opt[OPT_IRED_RED].constraint.range = &gamma_range_8;
|
|
scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_ADVANCED;
|
|
if(scanner->LS<2)
|
|
{ scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
|
|
|
|
|
|
/* scanner->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; */
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
|
|
{
|
|
char dev_name[PATH_MAX];
|
|
size_t len;
|
|
FILE *fp;
|
|
|
|
(void) authorize;
|
|
|
|
DBG_INIT ();
|
|
sanei_thread_init ();
|
|
|
|
DBG (10, "sane_init\n");
|
|
if (version_code)
|
|
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
|
|
|
|
fp = sanei_config_open (COOLSCAN_CONFIG_FILE);
|
|
if (!fp)
|
|
{
|
|
attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
while (sanei_config_read (dev_name, sizeof (dev_name), fp))
|
|
{
|
|
if (dev_name[0] == '#')
|
|
continue; /* ignore line comments */
|
|
len = strlen (dev_name);
|
|
|
|
if (!len)
|
|
continue; /* ignore empty lines */
|
|
|
|
sanei_config_attach_matching_devices (dev_name, attach_one);
|
|
/*attach_scanner (dev_name, 0);*/
|
|
}
|
|
fclose (fp);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
void
|
|
sane_exit (void)
|
|
{
|
|
Coolscan_t *dev, *next;
|
|
|
|
DBG (10, "sane_exit\n");
|
|
|
|
for (dev = first_dev; dev; dev = next)
|
|
{
|
|
next = dev->next;
|
|
free (dev->devicename);
|
|
free (dev->buffer);
|
|
free (dev->obuffer);
|
|
free (dev);
|
|
}
|
|
|
|
if (devlist)
|
|
free (devlist);
|
|
}
|
|
|
|
/* ----------------------------- SANE GET DEVICES -------------------------- */
|
|
SANE_Status
|
|
sane_get_devices (const SANE_Device *** device_list,
|
|
SANE_Bool local_only)
|
|
{
|
|
Coolscan_t *dev;
|
|
int i;
|
|
|
|
(void) local_only;
|
|
|
|
DBG (10, "sane_get_devices\n");
|
|
|
|
if (devlist)
|
|
free (devlist);
|
|
|
|
devlist = calloc (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)
|
|
{
|
|
Coolscan_t *dev;
|
|
SANE_Status status;
|
|
|
|
DBG (10, "sane_open\n");
|
|
|
|
if (devicename[0])
|
|
{ /* search for devicename */
|
|
for (dev = first_dev; dev; dev = dev->next)
|
|
{
|
|
if (strcmp (dev->sane.name, devicename) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!dev)
|
|
{
|
|
status = attach_scanner (devicename, &dev);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dev = first_dev; /* empty devicname -> use first device */
|
|
}
|
|
|
|
if (!dev)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
dev->sfd = -1;
|
|
dev->pipe = -1;
|
|
dev->scanning = SANE_FALSE;
|
|
|
|
init_options (dev);
|
|
*handle = dev;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
void
|
|
sane_close (SANE_Handle handle)
|
|
{
|
|
DBG (10, "sane_close\n");
|
|
if (((Coolscan_t *) handle)->scanning)
|
|
do_cancel (handle);
|
|
}
|
|
|
|
const SANE_Option_Descriptor *
|
|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
|
|
DBG (10, "sane_get_option_descriptor %d\n", option);
|
|
|
|
if ((unsigned) option >= NUM_OPTIONS)
|
|
return 0;
|
|
return &scanner->opt[option];
|
|
}
|
|
|
|
/*
|
|
static void
|
|
worddump(char *comment, SANE_Word * p, int l)
|
|
{
|
|
int i;
|
|
char line[128];
|
|
char *ptr;
|
|
|
|
DBG (5, "%s\n", comment);
|
|
ptr = line;
|
|
for (i = 0; i < l; i++, p++)
|
|
{
|
|
if ((i % 8) == 0)
|
|
{
|
|
if (ptr != line)
|
|
{
|
|
*ptr = '\0';
|
|
DBG (5, "%s\n", line);
|
|
ptr = line;
|
|
}
|
|
sprintf (ptr, "%3.3d:", i);
|
|
ptr += 4;
|
|
}
|
|
sprintf (ptr, " %4.4d", *p);
|
|
ptr += 5;
|
|
}
|
|
*ptr = '\0';
|
|
DBG (5, "%s\n", line);
|
|
}
|
|
*/
|
|
SANE_Status
|
|
sane_control_option (SANE_Handle handle, SANE_Int option,
|
|
SANE_Action action, void *val,
|
|
SANE_Int * info)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
SANE_Status status;
|
|
SANE_Word cap;
|
|
|
|
if (info)
|
|
*info = 0;
|
|
|
|
if (scanner->scanning)
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
if (option >= NUM_OPTIONS)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
cap = scanner->opt[option].cap;
|
|
|
|
|
|
if (action == SANE_ACTION_GET_VALUE)
|
|
{
|
|
DBG (10, "sane_control_option %d, get value\n", option);
|
|
switch (option)
|
|
{
|
|
/* word options: */
|
|
case OPT_TL_X:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tlx));
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TL_Y:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tly));
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_X:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->brx));
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_Y:
|
|
*(SANE_Word *) val = SANE_FIX (iluToMm (scanner->bry));
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PREVIEW:
|
|
*(SANE_Word *) val = scanner->preview;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AUTOFOCUS:
|
|
switch(scanner->autofocus)
|
|
{ case AF_NEVER: strcpy (val,neverStr);
|
|
break;
|
|
case AF_PREVIEW:strcpy (val,previewStr);
|
|
break;
|
|
case AF_SCAN:if(scanner->LS>=2) strcpy (val,scanStr);
|
|
break;
|
|
case AF_PREANDSCAN:if(scanner->LS>=2) strcpy (val,preandscanStr);
|
|
break;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_NUM_OPTS:
|
|
*(SANE_Word *) val = NUM_OPTIONS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_RESOLUTION:
|
|
*(SANE_Word *) val = resDivToVal (scanner->x_nres);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PREVIEW_RESOLUTION:
|
|
*(SANE_Word *) val = resDivToVal (scanner->x_p_nres);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BIT_DEPTH:
|
|
*(SANE_Word *) val = scanner->bits_per_color;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_CONTRAST:
|
|
*(SANE_Word *) val = scanner->contrast - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_R_CONTRAST:
|
|
*(SANE_Word *) val = scanner->contrast_R - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_G_CONTRAST:
|
|
*(SANE_Word *) val = scanner->contrast_G - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_B_CONTRAST:
|
|
*(SANE_Word *) val = scanner->contrast_B - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BRIGHTNESS:
|
|
*(SANE_Word *) val = scanner->brightness - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_R_BRIGHTNESS:
|
|
*(SANE_Word *) val = scanner->brightness_R - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_G_BRIGHTNESS:
|
|
*(SANE_Word *) val = scanner->brightness_G - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_B_BRIGHTNESS:
|
|
*(SANE_Word *) val = scanner->brightness_B - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_EXPOSURE:
|
|
*(SANE_Word *) val = scanner->exposure_R * 2;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_R_EXPOSURE:
|
|
*(SANE_Word *) val = scanner->exposure_R * 2;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_G_EXPOSURE:
|
|
*(SANE_Word *) val = scanner->exposure_G * 2;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_B_EXPOSURE:
|
|
*(SANE_Word *) val = scanner->exposure_B * 2;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_R_SHIFT:
|
|
*(SANE_Word *) val = scanner->shift_R - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_G_SHIFT:
|
|
*(SANE_Word *) val = scanner->shift_G - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_B_SHIFT:
|
|
*(SANE_Word *) val = scanner->shift_B - 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
case OPT_IRED_RED:
|
|
*(SANE_Word *) val = scanner->ired_red;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
/* string options: */
|
|
case OPT_TYPE:
|
|
strcpy (val, ((scanner->negative) ? negativeStr : positiveStr));
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_MODE:
|
|
switch(scanner->colormode)
|
|
{ case RGB: strcpy (val,colorStr);
|
|
break;
|
|
case GREYSCALE:strcpy (val,grayStr);
|
|
break;
|
|
case RGBI:if(scanner->LS>=2) strcpy (val,rgbiStr);
|
|
else strcpy (val,colorStr);
|
|
break;
|
|
case IRED:if(scanner->LS>=2) strcpy (val,iredStr);
|
|
else strcpy (val,grayStr);
|
|
break;
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PRESCAN:
|
|
*(SANE_Word *) val = (scanner->prescan) ? SANE_TRUE : SANE_FALSE;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PRESCAN_NOW:
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_RGB_CONTROL:
|
|
*(SANE_Word *) val = (scanner->rgb_control) ? SANE_TRUE : SANE_FALSE;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA_BIND:
|
|
*(SANE_Word *) val = (scanner->gamma_bind) ? SANE_TRUE : SANE_FALSE;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_ANALOG_GAMMA:
|
|
*(SANE_Word *) val =
|
|
(scanner->analog_gamma_r) ? SANE_TRUE : SANE_FALSE;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AVERAGING:
|
|
*(SANE_Word *) val = (scanner->averaging) ? SANE_TRUE : SANE_FALSE;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA_VECTOR:
|
|
memcpy (val, scanner->gamma, scanner->opt[option].size);
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_GAMMA_VECTOR_R:
|
|
memcpy (val, scanner->gamma_r, scanner->opt[option].size);
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_GAMMA_VECTOR_G:
|
|
memcpy (val, scanner->gamma_g, scanner->opt[option].size);
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_GAMMA_VECTOR_B:
|
|
memcpy (val, scanner->gamma_b, scanner->opt[option].size);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_SOURCE:
|
|
if (strcmp (val, "Automatic Slide Feeder") == 0)
|
|
{
|
|
/* Feed/Discharge/update filename/etc */
|
|
}
|
|
else
|
|
{
|
|
/* Reset above */
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (action == SANE_ACTION_SET_VALUE)
|
|
{
|
|
DBG (10, "sane_control_option %d, set value\n", option);
|
|
|
|
if (!SANE_OPTION_IS_ACTIVE (cap))
|
|
return SANE_STATUS_INVAL;
|
|
|
|
if (!SANE_OPTION_IS_SETTABLE (cap))
|
|
return SANE_STATUS_INVAL;
|
|
|
|
status = sanei_constrain_value (scanner->opt + option, val, info);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
switch (option)
|
|
{
|
|
case OPT_GAMMA_BIND:
|
|
scanner->gamma_bind = (*(SANE_Word *) val == SANE_TRUE);
|
|
if (scanner->LS != 1)
|
|
{
|
|
if (scanner->gamma_bind)
|
|
{
|
|
scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
else
|
|
{
|
|
scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
}
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_ANALOG_GAMMA:
|
|
scanner->analog_gamma_r = scanner->analog_gamma_g =
|
|
scanner->analog_gamma_b = (*(SANE_Word *) val == SANE_TRUE);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AVERAGING:
|
|
scanner->averaging = (*(SANE_Word *) val == SANE_TRUE);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PRESCAN:
|
|
scanner->prescan = (*(SANE_Word *) val == SANE_TRUE);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PRESCAN_NOW:
|
|
do_prescan_now(scanner);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BIT_DEPTH:
|
|
scanner->bits_per_color=(*(SANE_Word *)val);
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
|
|
case OPT_RGB_CONTROL:
|
|
scanner->rgb_control = (*(SANE_Word *) val == SANE_TRUE);
|
|
if (scanner->rgb_control)
|
|
{
|
|
scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_R_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_R_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
scanner->contrast_R = 128;
|
|
scanner->contrast_G = 128;
|
|
scanner->contrast_B = 128;
|
|
scanner->brightness_R = 128;
|
|
scanner->brightness_G = 128;
|
|
scanner->brightness_B = 128;
|
|
scanner->exposure_R = 50;
|
|
scanner->exposure_G = 50;
|
|
scanner->exposure_B = 50;
|
|
}
|
|
else
|
|
{
|
|
scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
scanner->contrast = 128;
|
|
scanner->brightness = 128;
|
|
scanner->exposure_R = 50;
|
|
scanner->exposure_G = 50;
|
|
scanner->exposure_B = 50;
|
|
|
|
scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
|
|
|
|
scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_RESOLUTION:
|
|
scanner->y_nres = scanner->x_nres =
|
|
resValToDiv (*(SANE_Word *) val);
|
|
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PREVIEW_RESOLUTION:
|
|
scanner->y_p_nres = scanner->x_p_nres =
|
|
resValToDiv (*(SANE_Word *) val);
|
|
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TL_X:
|
|
scanner->tlx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TL_Y:
|
|
scanner->tly = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_X:
|
|
scanner->brx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BR_Y:
|
|
scanner->bry = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
|
|
if (info)
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_NUM_OPTS:
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_PREVIEW:
|
|
scanner->preview = *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_AUTOFOCUS:
|
|
if(strcmp(val,neverStr)==0)
|
|
{ scanner->autofocus=AF_NEVER;
|
|
}
|
|
if(strcmp(val,previewStr)==0)
|
|
{ scanner->autofocus=AF_PREVIEW;
|
|
}
|
|
if(strcmp(val,scanStr)==0)
|
|
{ scanner->autofocus=AF_SCAN;
|
|
}
|
|
if(strcmp(val,preandscanStr)==0)
|
|
{ scanner->autofocus=AF_PREANDSCAN;;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_CONTRAST:
|
|
scanner->contrast = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_R_CONTRAST:
|
|
scanner->contrast_R = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_G_CONTRAST:
|
|
scanner->contrast_G = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_B_CONTRAST:
|
|
scanner->contrast_B = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_BRIGHTNESS:
|
|
scanner->brightness = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_R_BRIGHTNESS:
|
|
scanner->brightness_R = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_G_BRIGHTNESS:
|
|
scanner->brightness_G = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_B_BRIGHTNESS:
|
|
scanner->brightness_B = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_EXPOSURE:
|
|
scanner->exposure_R = *(SANE_Word *) val / 2;
|
|
scanner->exposure_G = *(SANE_Word *) val / 2;
|
|
scanner->exposure_B = *(SANE_Word *) val / 2;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_R_EXPOSURE:
|
|
scanner->exposure_R = *(SANE_Word *) val / 2;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_G_EXPOSURE:
|
|
scanner->exposure_G = *(SANE_Word *) val / 2;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_B_EXPOSURE:
|
|
scanner->exposure_B = *(SANE_Word *) val / 2;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_R_SHIFT:
|
|
scanner->shift_R = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_G_SHIFT:
|
|
scanner->shift_G = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_B_SHIFT:
|
|
scanner->shift_B = *(SANE_Word *) val + 128;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_IRED_RED:
|
|
scanner->ired_red= *(SANE_Word *) val;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_SOURCE:
|
|
scanner->asf = (strcmp (val, "Automatic...") == 0);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_TYPE:
|
|
scanner->negative = (strcmp (val, negativeStr) == 0);
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
case OPT_MODE:
|
|
if(strcmp(val,colorStr)==0)
|
|
{ scanner->colormode=RGB;
|
|
scanner->colormode_p=RGB;
|
|
}
|
|
if(strcmp(val,grayStr)==0)
|
|
{ scanner->colormode=GREYSCALE;
|
|
scanner->colormode_p=GREYSCALE;
|
|
}
|
|
if(strcmp(val,rgbiStr)==0)
|
|
{ scanner->colormode=RGBI;
|
|
scanner->colormode_p=RGB;
|
|
}
|
|
if(strcmp(val,iredStr)==0)
|
|
{ scanner->colormode=IRED;
|
|
scanner->colormode_p=GREYSCALE;
|
|
}
|
|
if (info)
|
|
{
|
|
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA_VECTOR:
|
|
memcpy (scanner->gamma, val, scanner->opt[option].size);
|
|
if(scanner->LS>2) Calc_fix_LUT(scanner);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA_VECTOR_R:
|
|
memcpy (scanner->gamma_r, val, scanner->opt[option].size);
|
|
if(scanner->LS>2) Calc_fix_LUT(scanner);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA_VECTOR_G:
|
|
memcpy (scanner->gamma_g, val, scanner->opt[option].size);
|
|
if(scanner->LS>2) Calc_fix_LUT(scanner);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case OPT_GAMMA_VECTOR_B:
|
|
memcpy (scanner->gamma_b, val, scanner->opt[option].size);
|
|
if(scanner->LS>2) Calc_fix_LUT(scanner);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
} /* switch */
|
|
} /* else */
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
|
|
DBG (10, "sane_get_parameters");
|
|
switch(scanner->colormode)
|
|
{ case RGB:
|
|
params->format = SANE_FRAME_RGB;
|
|
break;
|
|
#ifdef HAS_IRED
|
|
case RGBI:
|
|
params->format = SANE_FRAME_RGBA;
|
|
break;
|
|
#endif /* HAS_RGBI */
|
|
case GREYSCALE:
|
|
params->format = SANE_FRAME_GRAY;
|
|
break;
|
|
}
|
|
|
|
params->depth = scanner->bits_per_color>8?16:8;
|
|
params->pixels_per_line = pixels_per_line (scanner);
|
|
params->lines = lines_per_scan (scanner);
|
|
params->bytes_per_line = write_bytes_per_line (scanner);
|
|
params->last_frame = 1;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
static int
|
|
swap_res (Coolscan_t * s)
|
|
{
|
|
if (s->preview)
|
|
{
|
|
/* swap preview/scan resolutions */
|
|
int xres, yres, cmode;
|
|
xres = s->x_nres;
|
|
yres = s->y_nres;
|
|
s->x_nres = s->x_p_nres;
|
|
s->y_nres = s->y_p_nres;
|
|
|
|
s->x_p_nres = xres;
|
|
s->y_p_nres = yres;
|
|
cmode=s->colormode;
|
|
s->colormode=s->colormode_p;
|
|
s->colormode_p=cmode;
|
|
}
|
|
return 0;
|
|
}
|
|
SANE_Status
|
|
sane_start (SANE_Handle handle)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
int fds[2];
|
|
|
|
DBG (10, "sane_start\n");
|
|
if (scanner->scanning == SANE_TRUE)
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
if (scanner->sfd < 0)
|
|
{ /* first call */
|
|
if (sanei_scsi_open (scanner->sane.name,
|
|
&(scanner->sfd),
|
|
sense_handler, 0) != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "sane_start: open of %s failed:\n",
|
|
scanner->sane.name);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
scanner->scanning = SANE_TRUE;
|
|
|
|
|
|
if (coolscan_check_values (scanner) != 0)
|
|
{ /* Verify values */
|
|
DBG (1, "ERROR: invalid scan-values\n");
|
|
scanner->scanning = SANE_FALSE;
|
|
coolscan_give_scanner (scanner);
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (coolscan_grab_scanner (scanner))
|
|
{
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
DBG (5, "WARNING: unable to reserve scanner: device busy\n");
|
|
scanner->scanning = SANE_FALSE;
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
/* hoho, step 2c, -perm */
|
|
coolscan_object_feed (scanner);
|
|
|
|
swap_res (scanner);
|
|
if (!scanner->preview)
|
|
{ if(scanner->autofocus & 0x02)
|
|
{ coolscan_autofocus (scanner);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(scanner->autofocus & 0x01)
|
|
{ coolscan_autofocus (scanner);
|
|
}
|
|
if (scanner->prescan) {
|
|
prescan (scanner);
|
|
if(scanner->LS<2)
|
|
{ get_internal_info(scanner);
|
|
}
|
|
coolscan_get_window_param (scanner,1);
|
|
}
|
|
}
|
|
/*read_LUT(scanner); */
|
|
if(scanner->LS<2)
|
|
{ send_LUT (scanner);
|
|
coolscan_set_window_param (scanner, 0);
|
|
coolscan_get_window_param (scanner,0);
|
|
coolscan_start_scan (scanner);
|
|
}
|
|
else
|
|
{ coolscan_set_window_param (scanner, 0);
|
|
send_LUT (scanner);
|
|
Calc_fix_LUT(scanner);
|
|
coolscan_start_scan (scanner);
|
|
wait_scanner (scanner);
|
|
coolscan_get_window_param (scanner,0);
|
|
}
|
|
|
|
DBG (10, "bytes per line = %d\n", scan_bytes_per_line (scanner));
|
|
DBG (10, "pixels_per_line = %d\n", pixels_per_line (scanner));
|
|
DBG (10, "lines = %d\n", lines_per_scan (scanner));
|
|
DBG (10, "negative = %d\n", scanner->negative);
|
|
DBG (10, "brightness (halftone) = %d\n", scanner->brightness);
|
|
DBG (10, "contrast (halftone) = %d\n", scanner->contrast);
|
|
DBG (10, "fast preview function = %d\n", scanner->preview);
|
|
|
|
/* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
|
|
if (pipe (fds) < 0)
|
|
{
|
|
DBG (1, "ERROR: could not create pipe\n");
|
|
|
|
swap_res (scanner);
|
|
scanner->scanning = SANE_FALSE;
|
|
coolscan_give_scanner (scanner);
|
|
sanei_scsi_close (scanner->sfd);
|
|
scanner->sfd = -1;
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
scanner->pipe = fds[0];
|
|
scanner->reader_fds = fds[1];
|
|
scanner->reader_pid = sanei_thread_begin( reader_process, (void*)scanner );
|
|
if (!sanei_thread_is_valid (scanner->reader_pid))
|
|
{
|
|
DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
|
|
strerror (errno));
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
if (sanei_thread_is_forked ())
|
|
{
|
|
close (scanner->reader_fds);
|
|
scanner->reader_fds = -1;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_read (SANE_Handle handle, SANE_Byte * buf,
|
|
SANE_Int max_len, SANE_Int * len)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
ssize_t nread;
|
|
|
|
*len = 0;
|
|
|
|
nread = read (scanner->pipe, buf, max_len);
|
|
DBG (10, "sane_read: read %ld bytes\n", (long) nread);
|
|
|
|
if (!(scanner->scanning))
|
|
{
|
|
return do_cancel (scanner);
|
|
}
|
|
|
|
if (nread < 0)
|
|
{
|
|
if (errno == EAGAIN)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
else
|
|
{
|
|
do_cancel (scanner);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
}
|
|
|
|
*len = nread;
|
|
|
|
if (nread == 0)
|
|
return do_eof (scanner); /* close pipe */
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
void
|
|
sane_cancel (SANE_Handle handle)
|
|
{
|
|
Coolscan_t *s = handle;
|
|
|
|
if (sanei_thread_is_valid (s->reader_pid))
|
|
{
|
|
sanei_thread_kill ( s->reader_pid );
|
|
sanei_thread_waitpid( s->reader_pid, NULL );
|
|
sanei_thread_invalidate (s->reader_pid);
|
|
}
|
|
swap_res (s);
|
|
s->scanning = SANE_FALSE;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
|
|
DBG (10, "sane_set_io_mode: non_blocking=%d\n", non_blocking);
|
|
|
|
if (!scanner->scanning)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
if (fcntl (scanner->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
|
|
return SANE_STATUS_IO_ERROR;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
|
|
{
|
|
Coolscan_t *scanner = handle;
|
|
|
|
DBG (10, "sane_get_select_fd\n");
|
|
|
|
if (!scanner->scanning)
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
*fd = scanner->pipe;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|