summaryrefslogtreecommitdiff
path: root/backend/microtek2.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-10-06 14:00:40 +0200
commit6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch)
tree2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/microtek2.c
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/microtek2.c')
-rw-r--r--backend/microtek2.c8409
1 files changed, 8409 insertions, 0 deletions
diff --git a/backend/microtek2.c b/backend/microtek2.c
new file mode 100644
index 0000000..d56e568
--- /dev/null
+++ b/backend/microtek2.c
@@ -0,0 +1,8409 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ microtek2.c
+
+ This file (C) 1998, 1999 Bernd Schroeder
+ modifications 2000, 2001 Karsten Festag
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ ***************************************************************************
+
+ This file implements a SANE backend for Microtek scanners with
+ SCSI-2 command set.
+
+ (feedback to: bernd@aquila.muc.de)
+ ( karsten.festag@t-online.de)
+ ***************************************************************************/
+
+
+#ifdef _AIX
+# include <lalloca.h> /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <math.h>
+
+#include "../include/_stdint.h"
+
+#ifdef HAVE_AUTHORIZATION
+#include <sys/stat.h>
+#endif
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/sanei_scsi.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_thread.h"
+
+#ifndef TESTBACKEND
+#define BACKEND_NAME microtek2
+#else
+#define BACKEND_NAME microtek2_test
+#endif
+
+/* for testing*/
+/*#define NO_PHANTOMTYPE_SHADING*/
+
+#include "../include/sane/sanei_backend.h"
+
+#include "microtek2.h"
+
+#ifdef HAVE_AUTHORIZATION
+static SANE_Auth_Callback auth_callback;
+#endif
+
+static int md_num_devices = 0; /* number of devices from config file */
+static Microtek2_Device *md_first_dev = NULL; /* list of known devices */
+static Microtek2_Scanner *ms_first_handle = NULL; /* list of open scanners */
+
+/* options that can be configured in the config file */
+static Config_Options md_options =
+ { 1.0, "off", "off", "off", "off", "off", "off"};
+static Config_Temp *md_config_temp = NULL;
+static int md_dump = 0; /* from config file: */
+ /* 1: inquiry + scanner attributes */
+ /* 2: + all scsi commands and data */
+ /* 3: + all scan data */
+static int md_dump_clear = 1;
+
+
+/*---------- sane_cancel() ---------------------------------------------------*/
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Microtek2_Scanner *ms = handle;
+
+ DBG(30, "sane_cancel: handle=%p\n", handle);
+
+ if ( ms->scanning == SANE_TRUE )
+ cleanup_scanner(ms);
+ ms->cancelled = SANE_TRUE;
+ ms->fd[0] = ms->fd[1] = -1;
+}
+
+
+/*---------- sane_close() ----------------------------------------------------*/
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Microtek2_Scanner *ms = handle;
+
+ DBG(30, "sane_close: ms=%p\n", (void *) ms);
+
+ if ( ! ms )
+ return;
+
+ /* free malloc'ed stuff */
+ cleanup_scanner(ms);
+
+ /* remove Scanner from linked list */
+ if ( ms_first_handle == ms )
+ ms_first_handle = ms->next;
+ else
+ {
+ Microtek2_Scanner *ts = ms_first_handle;
+ while ( (ts != NULL) && (ts->next != ms) )
+ ts = ts->next;
+ ts->next = ts->next->next; /* == ms->next */
+ }
+ DBG(100, "free ms at %p\n", (void *) ms);
+ free((void *) ms);
+ ms = NULL;
+}
+
+
+/*---------- sane_exit() -----------------------------------------------------*/
+
+void
+sane_exit (void)
+{
+ Microtek2_Device *next;
+ int i;
+
+ DBG(30, "sane_exit:\n");
+
+ /* close all leftover Scanners */
+ while (ms_first_handle != NULL)
+ sane_close(ms_first_handle);
+ /* free up device list */
+ while (md_first_dev != NULL)
+ {
+ next = md_first_dev->next;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( md_first_dev->custom_gamma_table[i] )
+ {
+ DBG(100, "free md_first_dev->custom_gamma_table[%d] at %p\n",
+ i, (void *) md_first_dev->custom_gamma_table[i]);
+ free((void *) md_first_dev->custom_gamma_table[i]);
+ md_first_dev->custom_gamma_table[i] = NULL;
+ }
+ }
+
+ if ( md_first_dev->shading_table_w )
+ {
+ DBG(100, "free md_first_dev->shading_table_w at %p\n",
+ md_first_dev->shading_table_w);
+ free((void *) md_first_dev->shading_table_w);
+ md_first_dev->shading_table_w = NULL;
+ }
+
+ if ( md_first_dev->shading_table_d )
+ {
+ DBG(100, "free md_first_dev->shading_table_d at %p\n",
+ md_first_dev->shading_table_d);
+ free((void *) md_first_dev->shading_table_d);
+ md_first_dev->shading_table_d = NULL;
+ }
+
+ DBG(100, "free md_first_dev at %p\n", (void *) md_first_dev);
+ free((void *) md_first_dev);
+ md_first_dev = next;
+ }
+ sane_get_devices(NULL, SANE_FALSE); /* free list of SANE_Devices */
+
+ DBG(30, "sane_exit: MICROTEK2 says goodbye.\n");
+}
+
+
+/*---------- sane_get_devices()-----------------------------------------------*/
+
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ /* return a list of available devices; available here means that we get */
+ /* a positive response to an 'INQUIRY' and possibly to a */
+ /* 'READ SCANNER ATTRIBUTE' call */
+
+ static const SANE_Device **sd_list = NULL;
+ Microtek2_Device *md;
+ SANE_Status status;
+ int index;
+
+ DBG(30, "sane_get_devices: local_only=%d\n", local_only);
+
+ /* this is hack to get the list freed with a call from sane_exit() */
+ if ( device_list == NULL )
+ {
+ if ( sd_list )
+ {
+ DBG(100, "free sd_list at %p\n", (void *) sd_list);
+ free(sd_list);
+ sd_list=NULL;
+ }
+ DBG(30, "sane_get_devices: sd_list_freed\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ /* first free old list, if there is one; frontend wants a new list */
+ if ( sd_list )
+ {
+ DBG(100, "free sd_list at %p\n", (void *) sd_list);
+ free(sd_list); /* free array of pointers */
+ }
+
+ sd_list = (const SANE_Device **)
+ malloc( (md_num_devices + 1) * sizeof(SANE_Device **));
+ DBG(100, "sane_get_devices: sd_list=%p, malloc'd %lu bytes\n",
+ (void *) sd_list, (u_long) ((md_num_devices + 1) * sizeof(SANE_Device **)));
+
+ if ( ! sd_list )
+ {
+ DBG(1, "sane_get_devices: malloc() for sd_list failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ *device_list = sd_list;
+ index = 0;
+ md = md_first_dev;
+ while ( md )
+ {
+ status = attach(md);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(10, "sane_get_devices: attach status '%s'\n",
+ sane_strstatus(status));
+ md = md->next;
+ continue;
+ }
+
+ /* check whether unit is ready, if so add it to the list */
+ status = scsi_test_unit_ready(md);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(10, "sane_get_devices: test_unit_ready status '%s'\n",
+ sane_strstatus(status));
+ md = md->next;
+ continue;
+ }
+
+ sd_list[index] = &md->sane;
+
+ ++index;
+ md = md->next;
+ }
+
+ sd_list[index] = NULL;
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_get_parameters() -------------------------------------------*/
+
+SANE_Status
+sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
+{
+ Microtek2_Scanner *ms = handle;
+ Microtek2_Device *md;
+ Option_Value *val;
+ Microtek2_Info *mi;
+ int mode;
+ int depth;
+ int bits_pp_in; /* bits per pixel from scanner */
+ int bits_pp_out; /* bits_per_pixel transferred to frontend */
+ int bytes_per_line;
+ double x_pixel_per_mm;
+ double y_pixel_per_mm;
+ double x1_pixel;
+ double y1_pixel;
+ double width_pixel;
+ double height_pixel;
+
+
+ DBG(40, "sane_get_parameters: handle=%p, params=%p\n", handle,
+ (void *) params);
+
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ val= ms->val;
+
+ if ( ! ms->scanning ) /* get an estimate for the params */
+ {
+
+ get_scan_mode_and_depth(ms, &mode, &depth, &bits_pp_in, &bits_pp_out);
+
+ switch ( mode )
+ {
+ case MS_MODE_COLOR:
+ if ( mi->onepass )
+ {
+ ms->params.format = SANE_FRAME_RGB;
+ ms->params.last_frame = SANE_TRUE;
+ }
+ else
+ {
+ ms->params.format = SANE_FRAME_RED;
+ ms->params.last_frame = SANE_FALSE;
+ }
+ break;
+ case MS_MODE_GRAY:
+ case MS_MODE_HALFTONE:
+ case MS_MODE_LINEART:
+ case MS_MODE_LINEARTFAKE:
+ ms->params.format = SANE_FRAME_GRAY;
+ ms->params.last_frame = SANE_TRUE;
+ break;
+ default:
+ DBG(1, "sane_get_parameters: Unknown scan mode %d\n", mode);
+ break;
+ }
+
+ ms->params.depth = (SANE_Int) bits_pp_out;
+
+ /* calculate lines, pixels per line and bytes per line */
+ if ( val[OPT_RESOLUTION_BIND].w == SANE_TRUE )
+ {
+ x_pixel_per_mm = y_pixel_per_mm =
+ SANE_UNFIX(val[OPT_RESOLUTION].w) / MM_PER_INCH;
+ DBG(30, "sane_get_parameters: x_res=y_res=%f\n",
+ SANE_UNFIX(val[OPT_RESOLUTION].w));
+ }
+ else
+ {
+ x_pixel_per_mm = SANE_UNFIX(val[OPT_RESOLUTION].w) / MM_PER_INCH;
+ y_pixel_per_mm = SANE_UNFIX(val[OPT_Y_RESOLUTION].w) / MM_PER_INCH;
+ DBG(30, "sane_get_parameters: x_res=%f, y_res=%f\n",
+ SANE_UNFIX(val[OPT_RESOLUTION].w),
+ SANE_UNFIX(val[OPT_Y_RESOLUTION].w));
+ }
+
+ DBG(30, "sane_get_parameters: x_ppm=%f, y_ppm=%f\n",
+ x_pixel_per_mm, y_pixel_per_mm);
+
+ y1_pixel = SANE_UNFIX(ms->val[OPT_TL_Y].w) * y_pixel_per_mm;
+ height_pixel = fabs(SANE_UNFIX(ms->val[OPT_BR_Y].w) * y_pixel_per_mm
+ - y1_pixel) + 0.5;
+ ms->params.lines = (SANE_Int) height_pixel;
+
+ x1_pixel = SANE_UNFIX(ms->val[OPT_TL_X].w) * x_pixel_per_mm;
+ width_pixel = fabs(SANE_UNFIX(ms->val[OPT_BR_X].w) * x_pixel_per_mm
+ - x1_pixel) + 0.5;
+ ms->params.pixels_per_line = (SANE_Int) width_pixel;
+
+
+ if ( bits_pp_out == 1 )
+ bytes_per_line = (width_pixel + 7 ) / 8;
+ else
+ {
+ bytes_per_line = ( width_pixel * bits_pp_out ) / 8 ;
+ if ( mode == MS_MODE_COLOR && mi->onepass )
+ bytes_per_line *= 3;
+ }
+ ms->params.bytes_per_line = (SANE_Int) bytes_per_line;
+ } /* if ms->scanning */
+
+ if ( params )
+ *params = ms->params;
+
+ DBG(30,"sane_get_parameters: format=%d, last_frame=%d, lines=%d\n",
+ ms->params.format,ms->params.last_frame, ms->params.lines);
+ DBG(30,"sane_get_parameters: depth=%d, ppl=%d, bpl=%d\n",
+ ms->params.depth,ms->params.pixels_per_line, ms->params.bytes_per_line);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_get_select_fd() --------------------------------------------*/
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
+{
+ Microtek2_Scanner *ms = handle;
+
+
+ DBG(30, "sane_get_select_fd: ms=%p\n", (void *) ms);
+
+ if ( ! ms->scanning )
+ {
+ DBG(1, "sane_get_select_fd: Scanner not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ *fd = (SANE_Int) ms->fd[0];
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_init() -----------------------------------------------------*/
+
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ Microtek2_Device *md;
+ FILE *fp;
+ int match;
+ SANE_Auth_Callback trash;
+
+
+ DBG_INIT();
+ DBG(1, "sane_init: Microtek2 (v%d.%d build %s) says hello...\n",
+ MICROTEK2_MAJOR, MICROTEK2_MINOR, MICROTEK2_BUILD);
+
+ if ( version_code )
+ *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+#ifdef HAVE_AUTHORIZATION
+ auth_callback = authorize;
+#else
+ trash = authorize; /* prevents compiler warning "unused variable" */
+#endif
+
+ sanei_thread_init();
+
+ match = 0;
+ fp = sanei_config_open(MICROTEK2_CONFIG_FILE);
+ if ( fp == NULL )
+ DBG(10, "sane_init: file not opened: '%s'\n", MICROTEK2_CONFIG_FILE);
+ else
+ {
+ /* check config file for devices and associated options */
+ parse_config_file(fp, &md_config_temp);
+
+ while ( md_config_temp )
+ {
+ sanei_config_attach_matching_devices(md_config_temp->device,
+ attach_one);
+ if ( md_config_temp->next ) /* go to next device, if existent */
+ md_config_temp = md_config_temp->next;
+ else
+ break;
+ }
+
+ fclose(fp);
+ }
+
+ if ( md_first_dev == NULL )
+ {
+ /* config file not found or no valid entry; default to /dev/scanner */
+ /* instead of insisting on config file */
+ add_device_list("/dev/scanner", &md);
+ if ( md )
+ attach(md);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_open() -----------------------------------------------------*/
+
+SANE_Status
+sane_open(SANE_String_Const name, SANE_Handle *handle)
+{
+ SANE_Status status;
+ Microtek2_Scanner *ms;
+ Microtek2_Device *md;
+#ifdef HAVE_AUTHORIZATION
+ struct stat st;
+ int rc;
+#endif
+
+
+ DBG(30, "sane_open: device='%s'\n", name);
+
+ *handle = NULL;
+ md = md_first_dev;
+
+ if ( name )
+ {
+ /* add_device_list() returns a pointer to the device struct if */
+ /* the device is known or newly added, else it returns NULL */
+
+ status = add_device_list(name, &md);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ if ( ! md )
+ {
+ DBG(10, "sane_open: invalid device name '%s'\n", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* attach calls INQUIRY and READ SCANNER ATTRIBUTES */
+ status = attach(md);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms = malloc(sizeof(Microtek2_Scanner));
+ DBG(100, "sane_open: ms=%p, malloc'd %lu bytes\n",
+ (void *) ms, (u_long) sizeof(Microtek2_Scanner));
+ if ( ms == NULL )
+ {
+ DBG(1, "sane_open: malloc() for ms failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ memset(ms, 0, sizeof(Microtek2_Scanner));
+ ms->dev = md;
+ ms->scanning = SANE_FALSE;
+ ms->cancelled = SANE_FALSE;
+ ms->current_pass = 0;
+ ms->sfd = -1;
+ ms->pid = -1;
+ ms->fp = NULL;
+ ms->gamma_table = NULL;
+ ms->buf.src_buf = ms->buf.src_buffer[0] = ms->buf.src_buffer[1] = NULL;
+ ms->control_bytes = NULL;
+ ms->shading_image = NULL;
+ ms->condensed_shading_w = NULL;
+ ms->condensed_shading_d = NULL;
+ ms->current_color = MS_COLOR_ALL;
+ ms->current_read_color = MS_COLOR_RED;
+
+ init_options(ms, MD_SOURCE_FLATBED);
+
+ /* insert scanner into linked list */
+ ms->next = ms_first_handle;
+ ms_first_handle = ms;
+
+ *handle = ms;
+
+#ifdef HAVE_AUTHORIZATION
+ /* check whether the file with the passwords exists. If it doesnt */
+ /* exist, we dont use any authorization */
+
+ rc = stat(PASSWD_FILE, &st);
+ if ( rc == -1 && errno == ENOENT )
+ return SANE_STATUS_GOOD;
+ else
+ {
+ status = do_authorization(md->name);
+ return status;
+ }
+#else
+ return SANE_STATUS_GOOD;
+#endif
+}
+
+
+/*---------- sane_read() -----------------------------------------------------*/
+
+SANE_Status
+sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len )
+{
+ Microtek2_Scanner *ms = handle;
+ SANE_Status status;
+ ssize_t nread;
+
+
+ DBG(30, "sane_read: handle=%p, buf=%p, maxlen=%d\n", handle, buf, maxlen);
+
+ *len = 0;
+
+ if ( ! ms->scanning || ms->cancelled )
+ {
+ if ( ms->cancelled )
+ {
+ status = SANE_STATUS_CANCELLED;
+ }
+ else
+ {
+ DBG(15, "sane_read: Scanner %p not scanning\n", (void *) ms);
+ status = SANE_STATUS_IO_ERROR;
+ }
+ DBG(15, "sane_read: scan cancelled or scanner not scanning->cleanup\n");
+ cleanup_scanner(ms);
+ return status;
+ }
+
+
+ nread = read(ms->fd[0], (void *) buf, (int) maxlen);
+ if ( nread == -1 )
+ {
+ if ( errno == EAGAIN )
+ {
+ DBG(30, "sane_read: currently no data available\n");
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(1, "sane_read: read() failed, errno=%d\n", errno);
+ cleanup_scanner(ms);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if ( nread == 0 )
+ {
+ DBG(15, "sane_read: read 0 bytes -> EOF\n");
+ ms->scanning = SANE_FALSE;
+ cleanup_scanner(ms);
+ return SANE_STATUS_EOF;
+ }
+
+
+ *len = (SANE_Int) nread;
+ DBG(30, "sane_read: *len=%d\n", *len);
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- sane_set_io_mode() ---------------------------------------------*/
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Microtek2_Scanner *ms = handle;
+ int rc;
+
+
+ DBG(30, "sane_set_io_mode: handle=%p, nonblocking=%d\n",
+ handle, non_blocking);
+
+ if ( ! ms->scanning )
+ {
+ DBG(1, "sane_set_io_mode: Scanner not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ rc = fcntl(ms->fd[0], F_SETFL, non_blocking ? O_NONBLOCK : 0);
+ if ( rc == -1 )
+ {
+ DBG(1, "sane_set_io_mode: fcntl() failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- add_device_list() -----------------------------------------------*/
+
+static SANE_Status
+add_device_list(SANE_String_Const dev_name, Microtek2_Device **mdev)
+{
+ Microtek2_Device *md;
+ SANE_String hdev;
+ size_t len;
+
+
+ if ( (hdev = strdup(dev_name)) == NULL)
+ {
+ DBG(5, "add_device_list: malloc() for hdev failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ len = strlen(hdev);
+ if ( hdev[len - 1] == '\n' )
+ hdev[--len] = '\0';
+
+ DBG(30, "add_device_list: device='%s'\n", hdev);
+
+ /* check, if device is already known */
+ md = md_first_dev;
+ while ( md )
+ {
+ if ( strcmp(hdev, md->name) == 0 )
+ {
+ DBG(30, "add_device_list: device '%s' already in list\n", hdev);
+
+ *mdev = md;
+ return SANE_STATUS_GOOD;
+ }
+ md = md->next;
+ }
+
+ md = (Microtek2_Device *) malloc(sizeof(Microtek2_Device));
+ DBG(100, "add_device_list: md=%p, malloc'd %lu bytes\n",
+ (void *) md, (u_long) sizeof(Microtek2_Device));
+ if ( md == NULL )
+ {
+ DBG(1, "add_device_list: malloc() for md failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* initialize Device and add it at the beginning of the list */
+ memset(md, 0, sizeof(Microtek2_Device));
+ md->next = md_first_dev;
+ md_first_dev = md;
+ md->sane.name = NULL;
+ md->sane.vendor = NULL;
+ md->sane.model = NULL;
+ md->sane.type = NULL;
+ md->scan_source = MD_SOURCE_FLATBED;
+ md->shading_table_w = NULL;
+ md->shading_table_d = NULL;
+ strncpy(md->name, hdev, PATH_MAX - 1);
+ if ( md_config_temp )
+ md->opts = md_config_temp->opts;
+ else
+ md->opts = md_options;
+ ++md_num_devices;
+ *mdev = md;
+ DBG(100, "free hdev at %p\n", hdev);
+ free(hdev);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- attach() --------------------------------------------------------*/
+
+static SANE_Status
+attach(Microtek2_Device *md)
+{
+ /* This function is called from sane_init() to do the inquiry and to read */
+ /* the scanner attributes. If one of these calls fails, or if a new */
+ /* device is passed in sane_open() this function may also be called */
+ /* from sane_open() or sane_get_devices(). */
+
+ SANE_String model_string;
+ SANE_Status status;
+ SANE_Byte source_info;
+
+
+ DBG(30, "attach: device='%s'\n", md->name);
+
+ status = scsi_inquiry( &md->info[MD_SOURCE_FLATBED], md->name );
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "attach: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ /* We copy the inquiry info into the info structures for each scansource */
+ /* like ADF, TMA, STRIPE and SLIDE */
+
+ for ( source_info = 1; source_info < 5; ++source_info )
+ memcpy( &md->info[source_info],
+ &md->info[MD_SOURCE_FLATBED],
+ sizeof( Microtek2_Info ) );
+
+ /* Here we should insert a function, that stores all the relevant */
+ /* information in the info structure in a more conveniant format */
+ /* in the device structure, e.g. the model name with a trailing '\0'. */
+
+ status = check_inquiry(md, &model_string);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ md->sane.name = md->name;
+ md->sane.vendor = "Microtek";
+ md->sane.model = strdup(model_string);
+ if ( md->sane.model == NULL )
+ DBG(1, "attach: strdup for model string failed\n");
+ md->sane.type = "flatbed scanner";
+ md->revision = strtod(md->info[MD_SOURCE_FLATBED].revision, NULL);
+
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_FLATBED);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "attach: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( MI_LUTCAP_NONE( md->info[MD_SOURCE_FLATBED].lut_cap) )
+ /* no gamma tables */
+ md->model_flags |= MD_NO_GAMMA;
+
+ /* check whether the device supports transparency media adapters */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_TMA )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_TMA);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ /* check whether the device supports an ADF */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_ADF )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_ADF);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ /* check whether the device supports STRIPES */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_STRIPE )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_STRIPE);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ /* check whether the device supports SLIDES */
+ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_SLIDE )
+ {
+ /* The Phantom 636cx indicates in its attributes that it supports */
+ /* slides, but it doesn't. Thus this command would fail. */
+
+ if ( ! (md->model_flags & MD_NO_SLIDE_MODE) )
+ {
+ status = scsi_read_attributes(&md->info[0],
+ md->name, MD_SOURCE_SLIDE);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+ }
+
+ status = scsi_read_system_status(md, -1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- attach_one() ----------------------------------------------------*/
+
+static SANE_Status
+attach_one (const char *name)
+{
+ Microtek2_Device *md;
+ Microtek2_Device *md_tmp;
+
+
+ DBG(30, "attach_one: name='%s'\n", name);
+
+ md_tmp = md_first_dev;
+ /* if add_device_list() adds an entry it does this at the beginning */
+ /* of the list and thus changes md_first_dev */
+ add_device_list(name, &md);
+ if ( md_tmp != md_first_dev )
+ attach(md);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- cancel_scan() ---------------------------------------------------*/
+
+static SANE_Status
+cancel_scan(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+
+
+ DBG(30, "cancel_scan: ms=%p\n", (void *) ms);
+
+ /* READ IMAGE with a transferlength of 0 aborts a scan */
+ ms->transfer_length = 0;
+ status = scsi_read_image(ms, (uint8_t *) NULL, 1);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "cancel_scan: cancel failed: '%s'\n", sane_strstatus(status));
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ status = SANE_STATUS_CANCELLED;
+
+ close(ms->fd[1]);
+
+ /* if we are aborting a scan because, for example, we run out
+ of material on a feeder, then pid may be already -1 and
+ kill(-1, SIGTERM), i.e. killing all our processes, is not
+ likely what we really want - --mj, 2001/Nov/19 */
+ if (ms->pid != -1)
+ {
+ sanei_thread_kill(ms->pid);
+ sanei_thread_waitpid(ms->pid, NULL);
+ }
+
+ return status;
+}
+
+
+/*---------- check_option() --------------------------------------------------*/
+
+static void
+check_option(const char *cp, Config_Options *co)
+{
+ /* This function analyses options in the config file */
+
+ char *endptr;
+
+ /* When this function is called, it is already made sure that this */
+ /* is an option line, i.e. a line that starts with option */
+
+ cp = sanei_config_skip_whitespace(cp); /* skip blanks */
+ cp = sanei_config_skip_whitespace(cp + 6); /* skip "option" */
+ if ( strncmp(cp, "dump", 4) == 0 && isspace(cp[4]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 4);
+ if ( *cp )
+ {
+ md_dump = (int) strtol(cp, &endptr, 10);
+ if ( md_dump > 4 || md_dump < 0 )
+ {
+ md_dump = 1;
+ DBG(30, "check_option: setting dump to %d\n", md_dump);
+ }
+ cp = sanei_config_skip_whitespace(endptr);
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ md_dump = 1;
+ DBG(30, "check_option: option value wrong\n");
+ }
+ }
+ else
+ {
+ DBG(30, "check_option: missing option value\n");
+ /* reasonable fallback */
+ md_dump = 1;
+ }
+ }
+ else if ( strncmp(cp, "strip-height", 12) == 0 && isspace(cp[12]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 12);
+ if ( *cp )
+ {
+ co->strip_height = strtod(cp, &endptr);
+ DBG(30, "check_option: setting strip_height to %f\n",
+ co->strip_height);
+ if ( co->strip_height <= 0.0 )
+ co->strip_height = 14.0;
+ cp = sanei_config_skip_whitespace(endptr);
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->strip_height = 14.0;
+ DBG(30, "check_option: option value wrong: %f\n",
+ co->strip_height);
+ }
+ }
+ }
+ else if ( strncmp(cp, "no-backtrack-option", 19) == 0
+ && isspace(cp[19]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 19);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->no_backtracking = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->no_backtracking = "off";
+ }
+ else
+ co->no_backtracking = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->no_backtracking = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "lightlid-35", 11) == 0
+ && isspace(cp[11]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 11);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->lightlid35 = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->lightlid35 = "off";
+ }
+ else
+ co->lightlid35 = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->lightlid35 = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "toggle-lamp", 11) == 0
+ && isspace(cp[11]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 11);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->toggle_lamp = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->toggle_lamp = "off";
+ }
+ else
+ co->toggle_lamp = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->toggle_lamp = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "lineart-autoadjust", 18) == 0
+ && isspace(cp[18]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 18);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->auto_adjust = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->auto_adjust = "off";
+ }
+ else
+ co->auto_adjust = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->auto_adjust = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "backend-calibration", 19) == 0
+ && isspace(cp[19]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 19);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->backend_calibration = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->backend_calibration = "off";
+ }
+ else
+ co->backend_calibration = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->backend_calibration = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else if ( strncmp(cp, "colorbalance-adjust", 19) == 0
+ && isspace(cp[19]) )
+ {
+ cp = sanei_config_skip_whitespace(cp + 19);
+ if ( strncmp(cp, "on", 2) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 2);
+ co->colorbalance_adjust = "on";
+ }
+ else if ( strncmp(cp, "off", 3) == 0 )
+ {
+ cp = sanei_config_skip_whitespace(cp + 3);
+ co->colorbalance_adjust = "off";
+ }
+ else
+ co->colorbalance_adjust = "off";
+
+ if ( *cp )
+ {
+ /* something behind the option value or value wrong */
+ co->colorbalance_adjust = "off";
+ DBG(30, "check_option: option value wrong: %s\n", cp);
+ }
+ }
+ else
+ DBG(30, "check_option: invalid option in '%s'\n", cp);
+}
+
+
+/*---------- check_inquiry() -------------------------------------------------*/
+
+static SANE_Status
+check_inquiry(Microtek2_Device *md, SANE_String *model_string)
+{
+ Microtek2_Info *mi;
+
+ DBG(30, "check_inquiry: md=%p\n", (void *) md);
+
+ md->n_control_bytes = 0;
+ md->shading_length = 0;
+ md->shading_table_contents = 0;
+
+ mi = &md->info[MD_SOURCE_FLATBED];
+ if ( mi->scsi_version != MI_SCSI_II_VERSION )
+ {
+ DBG(1, "check_inquiry: Device is not a SCSI-II device, but 0x%02x\n",
+ mi->scsi_version);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( mi->device_type != MI_DEVTYPE_SCANNER )
+ {
+ DBG(1, "check_inquiry: Device is not a scanner, but 0x%02x\n",
+ mi->device_type);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( strncasecmp("MICROTEK", mi->vendor, INQ_VENDOR_L) != 0
+ && strncmp(" ", mi->vendor, INQ_VENDOR_L) != 0
+ && strncmp("AGFA ", mi->vendor, INQ_VENDOR_L) != 0 )
+ {
+ DBG(1, "check_inquiry: Device is not a Microtek, but '%.*s'\n",
+ INQ_VENDOR_L, mi->vendor);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ md->shading_depth = 16;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ md->shading_depth = 14;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ md->shading_depth = 12;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ md->shading_depth = 10;
+ else
+ md->shading_depth = 8;
+
+ switch (mi->model_code)
+ {
+ case 0x81:
+ case 0xab:
+ *model_string = "ScanMaker 4";
+ break;
+ case 0x85:
+ *model_string = "ScanMaker V300 / ColorPage-EP";
+ /* The ScanMaker V300 (FW < 2.70) returns some values for the */
+ /* "read image info" command in only two bytes */
+ /* and doesn't understand read_image_status */
+ md->model_flags |= MD_NO_RIS_COMMAND;
+ if ( md->revision < 2.70 )
+ md->model_flags |= MD_RII_TWO_BYTES;
+ break;
+ case 0x87:
+ *model_string = "ScanMaker 5";
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0x89:
+ *model_string = "ScanMaker 6400XL";
+ break;
+ case 0x8a:
+ *model_string = "ScanMaker 9600XL";
+ break;
+ case 0x8c:
+ *model_string = "ScanMaker 630 / ScanMaker V600";
+ break;
+ case 0x8d:
+ *model_string = "ScanMaker 336 / ScanMaker V310";
+ break;
+ case 0x90:
+ case 0x92:
+ *model_string = "E3+ / Vobis HighScan";
+ break;
+ case 0x91:
+ *model_string = "ScanMaker X6 / Phantom 636";
+ /* The X6 indicates a data format of segregated data in TMA mode */
+ /* but actually transfers as chunky data */
+ md->model_flags |= MD_DATA_FORMAT_WRONG;
+ if ( md->revision == 1.00 )
+ md->model_flags |= MD_OFFSET_2;
+ break;
+ case 0x93:
+ *model_string = "ScanMaker 336 / ScanMaker V310";
+ break;
+ case 0x70:
+ case 0x71:
+ case 0x94:
+ case 0xa0:
+ *model_string = "Phantom 330cx / Phantom 336cx / SlimScan C3";
+ /* These models do not accept gamma tables. Apparently they */
+ /* read the control bits and do not accept shading tables */
+ /* They also don't support enhancements (contrast, brightness...)*/
+ md->model_flags |= MD_NO_SLIDE_MODE
+ | MD_NO_GAMMA
+#ifndef NO_PHANTOMTYPE_SHADING
+ | MD_PHANTOM336CX_TYPE_SHADING
+#endif
+ | MD_READ_CONTROL_BIT
+ | MD_NO_ENHANCEMENTS;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ md->n_control_bytes = 320;
+ md->shading_length = 18;
+ md->shading_depth = 10;
+ md->controlbit_offset = 7;
+ break;
+ case 0x95:
+ *model_string = "ArtixScan 1010";
+ break;
+ case 0x97:
+ *model_string = "ScanMaker 636";
+ break;
+ case 0x98:
+ *model_string = "ScanMaker X6EL";
+ if ( md->revision == 1.00 )
+ md->model_flags |= MD_OFFSET_2;
+ break;
+ case 0x99:
+ *model_string = "ScanMaker X6USB";
+ if ( md->revision == 1.00 )
+ md->model_flags |= MD_OFFSET_2;
+ md->model_flags |= MD_X6_SHORT_TRANSFER;
+ break;
+ case 0x9a:
+ *model_string = "Phantom 636cx / C6";
+ /* The Phantom 636cx says it supports the SLIDE mode, but it */
+ /* doesn't. Thus inquring the attributes for slide mode would */
+ /* fail. Also it does not accept gamma tables. Apparently */
+ /* it reads the control bits and does not accept shading tables */
+ md->model_flags |= MD_NO_SLIDE_MODE
+ | MD_READ_CONTROL_BIT
+ | MD_NO_GAMMA
+ | MD_PHANTOM_C6;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ md->n_control_bytes = 647;
+ /* md->shading_length = 18; firmware values seem to work better */
+ md->shading_depth = 12;
+ md->controlbit_offset = 18;
+ break;
+ case 0x9d:
+ *model_string = "AGFA Duoscan T1200";
+ break;
+ case 0xa3:
+ *model_string = "ScanMaker V6USL";
+ /* The V6USL does not accept gamma tables */
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0xa5:
+ *model_string = "ArtixScan 4000t";
+ break;
+ case 0xac:
+ *model_string = "ScanMaker V6UL";
+ /* The V6USL does not accept gamma tables, perhaps the V6UL also */
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0xaf:
+ *model_string = "SlimScan C3";
+ md->model_flags |= MD_NO_SLIDE_MODE
+ | MD_NO_GAMMA
+ | MD_READ_CONTROL_BIT
+ | MD_NO_ENHANCEMENTS;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ md->n_control_bytes = 320;
+ md->controlbit_offset = 7;
+ break;
+ case 0xb0:
+ *model_string = "ScanMaker X12USL";
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->model_flags |= MD_16BIT_TRANSFER
+ | MD_CALIB_DIVISOR_600;
+ break;
+ case 0xb3:
+ *model_string = "ScanMaker 3600";
+ break;
+ case 0xb4:
+ *model_string = "ScanMaker 4700";
+ break;
+ case 0xb6:
+ *model_string = "ScanMaker V6UPL";
+ /* is like V6USL but with USB and Parport interface ?? */
+ md->model_flags |= MD_NO_GAMMA;
+ break;
+ case 0xb8:
+ *model_string = "ScanMaker 3700";
+ break;
+ case 0xde:
+ *model_string = "ScanMaker 9800XL";
+ md->model_flags |= MD_NO_GAMMA
+ | MD_16BIT_TRANSFER;
+ md->opt_backend_calib_default = SANE_TRUE;
+ md->opt_no_backtrack_default = SANE_TRUE;
+ break;
+ default:
+ DBG(1, "check_inquiry: Model 0x%02x not supported\n", mi->model_code);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- cleanup_scanner() -----------------------------------------------*/
+
+static void
+cleanup_scanner(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ md = ms->dev;
+
+ DBG(30, "cleanup_scanner: ms=%p, ms->sfd=%d\n", (void *) ms, ms->sfd);
+
+ if ( ms->scanning == SANE_TRUE )
+ status=cancel_scan(ms);
+
+ if ( ms->sfd != -1 )
+ sanei_scsi_close(ms->sfd);
+ ms->sfd = -1;
+ ms->pid = -1;
+ ms->fp = NULL;
+ ms->current_pass = 0;
+ ms->scanning = SANE_FALSE;
+ ms->cancelled = SANE_FALSE;
+
+ /* free buffers */
+ if ( ms->buf.src_buffer[0] )
+ {
+ DBG(100, "free ms->buf.src_buffer[0] at %p\n", ms->buf.src_buffer[0]);
+ free((void *) ms->buf.src_buffer[0]);
+ ms->buf.src_buffer[0] = NULL;
+ ms->buf.src_buf = NULL;
+ }
+ if ( ms->buf.src_buffer[1] )
+ {
+ DBG(100, "free ms->buf.src_buffer[1] at %p\n", ms->buf.src_buffer[1]);
+ free((void *) ms->buf.src_buffer[1]);
+ ms->buf.src_buffer[1] = NULL;
+ ms->buf.src_buf = NULL;
+ }
+ if ( ms->buf.src_buf )
+ {
+ DBG(100, "free ms->buf.src_buf at %p\n", ms->buf.src_buf);
+ free((void *) ms->buf.src_buf);
+ ms->buf.src_buf = NULL;
+ }
+ if ( ms->temporary_buffer )
+ {
+ DBG(100, "free ms->temporary_buffer at %p\n", ms->temporary_buffer);
+ free((void *) ms->temporary_buffer);
+ ms->temporary_buffer = NULL;
+ }
+ if ( ms->gamma_table )
+ {
+ DBG(100, "free ms->gamma_table at %p\n", ms->gamma_table);
+ free((void *) ms->gamma_table);
+ ms->gamma_table = NULL;
+ }
+ if ( ms->control_bytes )
+ {
+ DBG(100, "free ms->control_bytes at %p\n", ms->control_bytes);
+ free((void *) ms->control_bytes);
+ ms->control_bytes = NULL;
+ }
+ if ( ms->condensed_shading_w )
+ {
+ DBG(100, "free ms->condensed_shading_w at %p\n",
+ ms->condensed_shading_w);
+ free((void *) ms->condensed_shading_w);
+ ms->condensed_shading_w = NULL;
+ }
+ if ( ms->condensed_shading_d )
+ {
+ DBG(100, "free ms->condensed_shading_d at %p\n",
+ ms->condensed_shading_d);
+ free((void *) ms->condensed_shading_d);
+ ms->condensed_shading_d = NULL;
+ }
+
+ return;
+}
+
+#ifdef HAVE_AUTHORIZATION
+/*---------- do_authorization() ----------------------------------------------*/
+
+static SANE_Status
+do_authorization(char *ressource)
+{
+ /* This function implements a simple authorization function. It looks */
+ /* up an entry in the file SANE_PATH_CONFIG_DIR/auth. Such an entry */
+ /* must be of the form device:user:password where password is a crypt() */
+ /* encrypted password. If several users are allowed to access a device */
+ /* an entry must be created for each user. If no entry exists for device */
+ /* or the file does not exist no authentication is neccessary. If the */
+ /* file exists, but cant be opened the authentication fails */
+
+ SANE_Status status;
+ FILE *fp;
+ int device_found;
+ char username[SANE_MAX_USERNAME_LEN];
+ char password[SANE_MAX_PASSWORD_LEN];
+ char line[MAX_LINE_LEN];
+ char *linep;
+ char *device;
+ char *user;
+ char *passwd;
+ char *p;
+
+
+ DBG(30, "do_authorization: ressource=%s\n", ressource);
+
+ if ( auth_callback == NULL ) /* frontend does not require authorization */
+ return SANE_STATUS_GOOD;
+
+ /* first check if an entry exists in for this device. If not, we dont */
+ /* use authorization */
+
+ fp = fopen(PASSWD_FILE, "r");
+ if ( fp == NULL )
+ {
+ if ( errno == ENOENT )
+ {
+ DBG(1, "do_authorization: file not found: %s\n", PASSWD_FILE);
+ return SANE_STATUS_GOOD;
+ }
+ else
+ {
+ DBG(1, "do_authorization: fopen() failed, errno=%d\n", errno);
+ return SANE_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ linep = &line[0];
+ device_found = 0;
+ while ( fgets(line, MAX_LINE_LEN, fp) )
+ {
+ p = index(linep, SEPARATOR);
+ if ( p )
+ {
+ *p = '\0';
+ device = linep;
+ if ( strcmp(device, ressource) == 0 )
+ {
+ DBG(2, "equal\n");
+ device_found = 1;
+ break;
+ }
+ }
+ }
+
+ if ( ! device_found )
+ {
+ fclose(fp);
+ return SANE_STATUS_GOOD;
+ }
+
+ fseek(fp, 0L, SEEK_SET);
+
+ (*auth_callback) (ressource, username, password);
+
+ status = SANE_STATUS_ACCESS_DENIED;
+ do
+ {
+ fgets(line, MAX_LINE_LEN, fp);
+ if ( ! ferror(fp) && ! feof(fp) )
+ {
+ /* neither strsep(3) nor strtok(3) seem to work on my system */
+ p = index(linep, SEPARATOR);
+ if ( p == NULL )
+ continue;
+ *p = '\0';
+ device = linep;
+ if ( strcmp( device, ressource) != 0 ) /* not a matching entry */
+ continue;
+
+ linep = ++p;
+ p = index(linep, SEPARATOR);
+ if ( p == NULL )
+ continue;
+
+ *p = '\0';
+ user = linep;
+ if ( strncmp(user, username, SANE_MAX_USERNAME_LEN) != 0 )
+ continue; /* username doesnt match */
+
+ linep = ++p;
+ /* rest of the line is considered to be the password */
+ passwd = linep;
+ /* remove newline */
+ *(passwd + strlen(passwd) - 1) = '\0';
+ p = crypt(password, SALT);
+ if ( strcmp(p, passwd) == 0 )
+ {
+ /* authentication ok */
+ status = SANE_STATUS_GOOD;
+ break;
+ }
+ else
+ continue;
+ }
+ } while ( ! ferror(fp) && ! feof(fp) );
+ fclose(fp);
+
+ return status;
+}
+#endif
+
+/*---------- dump_area() -----------------------------------------------------*/
+
+static SANE_Status
+dump_area(uint8_t *area, int len, char *info)
+{
+ /* this function dumps control or information blocks */
+
+#define BPL 16 /* bytes per line to print */
+
+ int i;
+ int o;
+ int o_limit;
+ char outputline[100];
+ char *outbuf;
+
+ if ( ! info[0] )
+ info = "No additional info available";
+
+ DBG(30, "dump_area: %s\n", info);
+
+ outbuf = outputline;
+ o_limit = (len + BPL - 1) / BPL;
+ for ( o = 0; o < o_limit; o++)
+ {
+ sprintf(outbuf, " %4d: ", o * BPL);
+ outbuf += 8;
+ for ( i=0; i < BPL && (o * BPL + i ) < len; i++)
+ {
+ if ( i == BPL / 2 )
+ {
+ sprintf(outbuf, " ");
+ outbuf +=1;
+ }
+ sprintf(outbuf, "%02x", area[o * BPL + i]);
+ outbuf += 2;
+ }
+
+ sprintf(outbuf, "%*s", 2 * ( 2 + BPL - i), " " );
+ outbuf += (2 * ( 2 + BPL - i));
+ sprintf(outbuf, "%s", (i == BPL / 2) ? " " : "");
+ outbuf += ((i == BPL / 2) ? 1 : 0);
+
+ for ( i = 0; i < BPL && (o * BPL + i ) < len; i++)
+ {
+ if ( i == BPL / 2 )
+ {
+ sprintf(outbuf, " ");
+ outbuf += 1;
+ }
+ sprintf(outbuf, "%c", isprint(area[o * BPL + i])
+ ? area[o * BPL + i]
+ : '.');
+ outbuf += 1;
+ }
+ outbuf = outputline;
+ DBG(1, "%s\n", outbuf);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- dump_area2() ----------------------------------------------------*/
+
+static SANE_Status
+dump_area2(uint8_t *area, int len, char *info)
+{
+
+#define BPL 16 /* bytes per line to print */
+
+ int i, linelength;
+ char outputline[100];
+ char *outbuf;
+ linelength = BPL * 3;
+
+ if ( ! info[0] )
+ info = "No additional info available";
+
+ DBG(1, "[%s]\n", info);
+
+ outbuf = outputline;
+ for ( i = 0; i < len; i++)
+ {
+ sprintf(outbuf, "%02x,", *(area + i));
+ outbuf += 3;
+ if ( ((i+1)%BPL == 0) || (i == len-1) )
+ {
+ outbuf = outputline;
+ DBG(1, "%s\n", outbuf);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- dump_to_file() --------------------------------------------------*/
+/*--- only for debugging, currently not used -----*/
+#if 0
+static SANE_Status
+dump_to_file(uint8_t *area, int len, char *filename, char *mode)
+{
+FILE *out;
+int i;
+
+ out = fopen(filename, mode);
+
+ for ( i = 0; i < len; i++)
+ fputc( *(area + i ), out);
+
+ fclose(out);
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+/*---------- dump_attributes() -----------------------------------------------*/
+
+static SANE_Status
+dump_attributes(Microtek2_Info *mi)
+{
+ /* dump all we know about the scanner */
+
+ int i;
+
+ DBG(30, "dump_attributes: mi=%p\n", (void *) mi);
+ DBG(1, "\n");
+ DBG(1, "Scanner attributes from device structure\n");
+ DBG(1, "========================================\n");
+ DBG(1, "Scanner ID...\n");
+ DBG(1, "~~~~~~~~~~~~~\n");
+ DBG(1, " Vendor Name%15s: '%s'\n", " ", mi->vendor);
+ DBG(1, " Model Name%16s: '%s'\n", " ", mi->model);
+ DBG(1, " Revision%18s: '%s'\n", " ", mi->revision);
+ DBG(1, " Model Code%16s: 0x%02x\n"," ", mi->model_code);
+ switch(mi->model_code)
+ {
+ case 0x80: DBG(1, "Redondo 2000XL / ArtixScan 2020\n"); break;
+ case 0x81: DBG(1, "ScanMaker 4 / Aruba\n"); break;
+ case 0x82: DBG(1, "Bali\n"); break;
+ case 0x83: DBG(1, "Washington\n"); break;
+ case 0x84: DBG(1, "Manhattan\n"); break;
+ case 0x85: DBG(1, "ScanMaker V300 / Phantom parallel / TR3\n"); break;
+ case 0x86: DBG(1, "CCP\n"); break;
+ case 0x87: DBG(1, "Scanmaker V\n"); break;
+ case 0x88: DBG(1, "Scanmaker VI\n"); break;
+ case 0x89: DBG(1, "ScanMaker 6400XL / A3-400\n"); break;
+ case 0x8a: DBG(1, "ScanMaker 9600XL / A3-600\n"); break;
+ case 0x8b: DBG(1, "Watt\n"); break;
+ case 0x8c: DBG(1, "ScanMaker V600 / TR6\n"); break;
+ case 0x8d: DBG(1, "ScanMaker V310 / Tr3 10-bit\n"); break;
+ case 0x8e: DBG(1, "CCB\n"); break;
+ case 0x8f: DBG(1, "Sun Rise\n"); break;
+ case 0x90: DBG(1, "ScanMaker E3+ 10-bit\n"); break;
+ case 0x91: DBG(1, "ScanMaker X6 / Phantom 636\n"); break;
+ case 0x92: DBG(1, "ScanMaker E3+ / Vobis Highscan\n"); break;
+ case 0x93: DBG(1, "ScanMaker V310\n"); break;
+ case 0x94: DBG(1, "SlimScan C3 / Phantom 330cx / 336cx\n"); break;
+ case 0x95: DBG(1, "ArtixScan 1010\n"); break;
+ case 0x97: DBG(1, "ScanMaker V636\n"); break;
+ case 0x98: DBG(1, "ScanMaker X6EL\n"); break;
+ case 0x99: DBG(1, "ScanMaker X6 / X6USB\n"); break;
+ case 0x9a: DBG(1, "SlimScan C6 / Phantom 636cx\n"); break;
+ case 0x9d: DBG(1, "AGFA DuoScan T1200\n"); break;
+ case 0xa0: DBG(1, "SlimScan C3 / Phantom 336cx\n"); break;
+ case 0xac: DBG(1, "ScanMaker V6UL\n"); break;
+ case 0xa3: DBG(1, "ScanMaker V6USL\n"); break;
+ case 0xaf: DBG(1, "SlimScan C3 / Phantom 336cx\n"); break;
+ case 0xb0: DBG(1, "ScanMaker X12USL\n"); break;
+ case 0xb3: DBG(1, "ScanMaker 3600\n"); break;
+ case 0xb4: DBG(1, "ScanMaker 4700\n"); break;
+ case 0xb6: DBG(1, "ScanMaker V6UPL\n"); break;
+ case 0xb8: DBG(1, "ScanMaker 3700\n"); break;
+ case 0xde: DBG(1, "ScanMaker 9800XL\n"); break;
+ default: DBG(1, "Unknown\n"); break;
+ }
+ DBG(1, " Device Type Code%10s: 0x%02x (%s),\n", " ",
+ mi->device_type,
+ mi->device_type & MI_DEVTYPE_SCANNER ?
+ "Scanner" : "Unknown type");
+
+ switch (mi->scanner_type)
+ {
+ case MI_TYPE_FLATBED:
+ DBG(1, " Scanner type%14s:%s", " ", " Flatbed scanner\n");
+ break;
+ case MI_TYPE_TRANSPARENCY:
+ DBG(1, " Scanner type%14s:%s", " ", " Transparency scanner\n");
+ break;
+ case MI_TYPE_SHEEDFEED:
+ DBG(1, " Scanner type%14s:%s", " ", " Sheet feed scanner\n");
+ break;
+ default:
+ DBG(1, " Scanner type%14s:%s", " ", " Unknown\n");
+ break;
+ }
+
+ DBG(1, " Supported options%9s: Automatic document feeder: %s\n",
+ " ", mi->option_device & MI_OPTDEV_ADF ? "Yes" : "No");
+ DBG(1, "%30sTransparency media adapter: %s\n",
+ " ", mi->option_device & MI_OPTDEV_TMA ? "Yes" : "No");
+ DBG(1, "%30sAuto paper detecting: %s\n",
+ " ", mi->option_device & MI_OPTDEV_ADP ? "Yes" : "No");
+ DBG(1, "%30sAdvanced picture system: %s\n",
+ " ", mi->option_device & MI_OPTDEV_APS ? "Yes" : "No");
+ DBG(1, "%30sStripes: %s\n",
+ " ", mi->option_device & MI_OPTDEV_STRIPE ? "Yes" : "No");
+ DBG(1, "%30sSlides: %s\n",
+ " ", mi->option_device & MI_OPTDEV_SLIDE ? "Yes" : "No");
+ DBG(1, " Scan button%15s: %s\n", " ", mi->scnbuttn ? "Yes" : "No");
+
+ DBG(1, "\n");
+ DBG(1, " Imaging Capabilities...\n");
+ DBG(1, " ~~~~~~~~~~~~~~~~~~~~~~~\n");
+ DBG(1, " Color scanner%6s: %s\n", " ", (mi->color) ? "Yes" : "No");
+ DBG(1, " Number passes%6s: %d pass%s\n", " ",
+ (mi->onepass) ? 1 : 3,
+ (mi->onepass) ? "" : "es");
+ DBG(1, " Resolution%9s: X-max: %5d dpi\n%35sY-max: %5d dpi\n",
+ " ", mi->max_xresolution, " ",mi->max_yresolution);
+ DBG(1, " Geometry%11s: Geometric width: %5d pts (%2.2f'')\n", " ",
+ mi->geo_width, (float) mi->geo_width / (float) mi->opt_resolution);
+ DBG(1, "%23sGeometric height:%5d pts (%2.2f'')\n", " ",
+ mi->geo_height, (float) mi->geo_height / (float) mi->opt_resolution);
+ DBG(1, " Optical resolution%1s: %d\n", " ", mi->opt_resolution);
+
+ DBG(1, " Modes%14s: Lineart: %s\n%35sHalftone: %s\n", " ",
+ (mi->scanmode & MI_HASMODE_LINEART) ? " Yes" : " No", " ",
+ (mi->scanmode & MI_HASMODE_HALFTONE) ? "Yes" : "No");
+
+ DBG(1, "%23sGray: %s\n%35sColor: %s\n", " ",
+ (mi->scanmode & MI_HASMODE_GRAY) ? " Yes" : " No", " ",
+ (mi->scanmode & MI_HASMODE_COLOR) ? " Yes" : " No");
+
+ DBG(1, " Depths%14s: Nibble Gray: %s\n",
+ " ", (mi->depth & MI_HASDEPTH_NIBBLE) ? "Yes" : "No");
+ DBG(1, "%23s10-bit-color: %s\n",
+ " ", (mi->depth & MI_HASDEPTH_10) ? "Yes" : "No");
+ DBG(1, "%23s12-bit-color: %s\n", " ",
+ (mi->depth & MI_HASDEPTH_12) ? "Yes" : "No");
+ DBG(1, "%23s14-bit-color: %s\n", " ",
+ (mi->depth & MI_HASDEPTH_14) ? "Yes" : "No");
+ DBG(1, "%23s16-bit-color: %s\n", " ",
+ (mi->depth & MI_HASDEPTH_16) ? "Yes" : "No");
+ DBG(1, " d/l of HT pattern%2s: %s\n",
+ " ", (mi->has_dnldptrn) ? "Yes" : "No");
+ DBG(1, " Builtin HT pattern%1s: %d\n", " ", mi->grain_slct);
+
+ if ( MI_LUTCAP_NONE(mi->lut_cap) )
+ DBG(1, " LUT capabilities : None\n");
+ if ( mi->lut_cap & MI_LUTCAP_256B )
+ DBG(1, " LUT capabilities : 256 bytes\n");
+ if ( mi->lut_cap & MI_LUTCAP_1024B )
+ DBG(1, " LUT capabilities : 1024 bytes\n");
+ if ( mi->lut_cap & MI_LUTCAP_1024W )
+ DBG(1, " LUT capabilities : 1024 words\n");
+ if ( mi->lut_cap & MI_LUTCAP_4096B )
+ DBG(1, " LUT capabilities : 4096 bytes\n");
+ if ( mi->lut_cap & MI_LUTCAP_4096W )
+ DBG(1, " LUT capabilities : 4096 words\n");
+ if ( mi->lut_cap & MI_LUTCAP_64k_W )
+ DBG(1, " LUT capabilities : 64k words\n");
+ if ( mi->lut_cap & MI_LUTCAP_16k_W )
+ DBG(1, " LUT capabilities : 16k words\n");
+ DBG(1, "\n");
+ DBG(1, " Miscellaneous capabilities...\n");
+ DBG(1, " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+ if ( mi->onepass)
+ {
+ switch(mi->data_format)
+ {
+ case MI_DATAFMT_CHUNKY:
+ DBG(1, " Data format :%s",
+ " Chunky data, R, G & B in one pixel\n");
+ break;
+ case MI_DATAFMT_LPLCONCAT:
+ DBG(1, " Data format :%s",
+ " Line by line in concatenated sequence,\n");
+ DBG(1, "%23swithout color indicator\n", " ");
+ break;
+ case MI_DATAFMT_LPLSEGREG:
+ DBG(1, " Data format :%s",
+ " Line by line in segregated sequence,\n");
+ DBG(1, "%23swith color indicator\n", " ");
+ break;
+ case MI_DATAFMT_WORDCHUNKY:
+ DBG(1, " Data format : Word chunky data\n");
+ break;
+ default:
+ DBG(1, " Data format : Unknown\n");
+ break;
+ }
+ }
+ else
+ DBG(1, "No information with 3-pass scanners\n");
+
+ DBG(1, " Color Sequence%17s: \n", " ");
+ for ( i = 0; i < RSA_COLORSEQUENCE_L; i++)
+ {
+ switch(mi->color_sequence[i])
+ {
+ case MI_COLSEQ_RED: DBG(1,"%34s%s\n", " ","R"); break;
+ case MI_COLSEQ_GREEN: DBG(1,"%34s%s\n", " ","G"); break;
+ case MI_COLSEQ_BLUE: DBG(1,"%34s%s\n", " ","B"); break;
+ }
+ }
+ if ( mi->new_image_status == SANE_TRUE )
+ DBG(1, " Using new ReadImageStatus format\n");
+ else
+ DBG(1, " Using old ReadImageStatus format\n");
+ if ( mi->direction & MI_DATSEQ_RTOL )
+ DBG(1, " Scanning direction : right to left\n");
+ else
+ DBG(1, " Scanning direction : left to right\n");
+ DBG(1, " CCD gap%24s: %d lines\n", " ", mi->ccd_gap);
+ DBG(1, " CCD pixels%21s: %d\n", " ", mi->ccd_pixels);
+ DBG(1, " Calib white stripe location%4s: %d\n",
+ " ", mi->calib_white);
+ DBG(1, " Max calib space%16s: %d\n", " ", mi->calib_space);
+ DBG(1, " Number of lens%17s: %d\n", " ", mi->nlens);
+ DBG(1, " Max number of windows%10s: %d\n", " ", mi->nwindows);
+ DBG(1, " Shading transfer function%6s: 0x%02x\n", " ",mi->shtrnsferequ);
+ DBG(1, " Red balance%20s: %d\n", " ", mi->balance[0]);
+ DBG(1, " Green balance%18s: %d\n", " ", mi->balance[1]);
+ DBG(1, " Blue balance%19s: %d\n", " " , mi->balance[2]);
+ DBG(1, " Buffer type%20s: %s\n",
+ " ", mi->buftype ? "Ping-Pong" : "Ring");
+ DBG(1, " FEPROM%25s: %s\n", " ", mi->feprom ? "Yes" : "No");
+
+ md_dump_clear = 0;
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- max_string_size() -----------------------------------------------*/
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size;
+ size_t max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen(strings[i]) + 1; /* +1 because NUL counts as part of string */
+ if (size > max_size) max_size = size;
+ }
+ return max_size;
+}
+
+/*---------- parse_config_file() ---------------------------------------------*/
+
+static void
+parse_config_file(FILE *fp, Config_Temp **ct)
+{
+ /* builds a list of device names with associated options from the */
+ /* config file for later use, when building the list of devices. */
+ /* ct->device = NULL indicates global options (valid for all devices */
+
+ char s[PATH_MAX];
+ Config_Options global_opts;
+ Config_Temp *hct1;
+ Config_Temp *hct2;
+
+
+ DBG(30, "parse_config_file: fp=%p\n", (void *) fp);
+
+ *ct = hct1 = NULL;
+
+ /* first read global options and store them in global_opts */
+ /* initialize global_opts with default values */
+
+ global_opts = md_options;
+
+ while ( sanei_config_read(s, sizeof(s), fp) )
+ {
+ DBG(100, "parse_config_file: read line: %s\n", s);
+ if ( *s == '#' || *s == '\0' ) /* ignore empty lines and comments */
+ continue;
+
+ if ( strncmp( sanei_config_skip_whitespace(s), "option ", 7) == 0
+ || strncmp( sanei_config_skip_whitespace(s), "option\t", 7) == 0 )
+ {
+ DBG(100, "parse_config_file: found global option %s\n", s);
+ check_option(s, &global_opts);
+ }
+ else /* it is considered a new device */
+ break;
+ }
+
+ if ( ferror(fp) || feof(fp) )
+ {
+ if ( ferror(fp) )
+ DBG(1, "parse_config_file: fread failed: errno=%d\n", errno);
+
+ return;
+ }
+
+ while ( ! feof(fp) && ! ferror(fp) )
+ {
+ if ( *s == '#' || *s == '\0' ) /* ignore empty lines and comments */
+ {
+ sanei_config_read(s, sizeof(s), fp);
+ continue;
+ }
+
+ if ( strncmp( sanei_config_skip_whitespace(s), "option ", 7) == 0
+ || strncmp( sanei_config_skip_whitespace(s), "option\t", 7) == 0 )
+ {
+ /* when we enter this loop for the first time we allocate */
+ /* memory, because the line surely contains a device name, */
+ /* so hct1 is always != NULL at this point */
+ DBG(100, "parse_config_file: found device option %s\n", s);
+ check_option(s, &hct1->opts);
+ }
+
+
+ else /* it is considered a new device */
+ {
+ DBG(100, "parse_config_file: found device %s\n", s);
+ hct2 = (Config_Temp *) malloc(sizeof(Config_Temp));
+ if ( hct2 == NULL )
+ {
+ DBG(1, "parse_config_file: malloc() failed\n");
+ return;
+ }
+
+ if ( *ct == NULL ) /* first element */
+ *ct = hct1 = hct2;
+
+ hct1->next = hct2;
+ hct1 = hct2;
+
+ hct1->device = strdup(s);
+ hct1->opts = global_opts;
+ hct1->next = NULL;
+ }
+ sanei_config_read(s, sizeof(s), fp);
+ }
+ /* set filepointer to the beginning of the file */
+ fseek(fp, 0L, SEEK_SET);
+ return;
+}
+
+
+/*---------- signal_handler() ------------------------------------------------*/
+
+static RETSIGTYPE
+signal_handler (int signal)
+{
+ if ( signal == SIGTERM )
+ {
+ sanei_scsi_req_flush_all ();
+ _exit (SANE_STATUS_GOOD);
+ }
+}
+
+/*---------- init_options() --------------------------------------------------*/
+
+static SANE_Status
+init_options(Microtek2_Scanner *ms, uint8_t current_scan_source)
+{
+ /* This function is called every time, when the scan source changes. */
+ /* The option values, that possibly change, are then reinitialized, */
+ /* whereas the option descriptors and option values that never */
+ /* change are not */
+
+ SANE_Option_Descriptor *sod;
+ SANE_Status status;
+ Option_Value *val;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ int tablesize;
+ int option_size;
+ int max_gamma_value;
+ int color;
+ int i;
+ static int first_call = 1; /* indicates, whether option */
+ /* descriptors must be initialized */
+ /* cannot be used as after a sane_close the sod's must be initialized */
+
+ DBG(30, "init_options: handle=%p, source=%d\n", (void *) ms,
+ current_scan_source);
+
+ sod = ms->sod;
+ val = ms->val;
+ md = ms->dev;
+ mi = &md->info[current_scan_source];
+
+ /* needed for gamma calculation */
+ get_lut_size(mi, &md->max_lut_size, &md->lut_entry_size);
+
+ /* calculate new values, where possibly needed */
+
+ /* Scan source */
+ if ( val[OPT_SOURCE].s )
+ free((void *) val[OPT_SOURCE].s);
+ i = 0;
+ md->scansource_list[i] = (SANE_String) MD_SOURCESTRING_FLATBED;
+ if ( current_scan_source == MD_SOURCE_FLATBED )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ if ( md->status.adfcnt )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_ADF;
+ if ( current_scan_source == MD_SOURCE_ADF )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+ if ( md->status.tmacnt )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_TMA;
+ if ( current_scan_source == MD_SOURCE_TMA )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+ if ( mi->option_device & MI_OPTDEV_STRIPE )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_STRIPE;
+ if ( current_scan_source == MD_SOURCE_STRIPE )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+
+ /* Comment this out as long as I do not know in which bit */
+ /* it is indicated, whether a slide adapter is connected */
+#if 0
+ if ( mi->option_device & MI_OPTDEV_SLIDE )
+ {
+ md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_SLIDE;
+ if ( current_scan_source == MD_SOURCE_SLIDE )
+ val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]);
+ }
+#endif
+
+ md->scansource_list[++i] = NULL;
+
+ /* Scan mode */
+ if ( val[OPT_MODE].s )
+ free((void *) val[OPT_MODE].s);
+
+ i = 0;
+ if ( (mi->scanmode & MI_HASMODE_COLOR) )
+ {
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_COLOR;
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ }
+
+ if ( mi->scanmode & MI_HASMODE_GRAY )
+ {
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_GRAY;
+ if ( ! (mi->scanmode & MI_HASMODE_COLOR ) )
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ }
+
+ if ( mi->scanmode & MI_HASMODE_HALFTONE )
+ {
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_HALFTONE;
+ if ( ! (mi->scanmode & MI_HASMODE_COLOR )
+ && ! (mi->scanmode & MI_HASMODE_GRAY ) )
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ }
+
+ /* Always enable a lineart mode. Some models (X6, FW 1.40) say */
+ /* that they have no lineart mode. In this case we will do a grayscale */
+ /* scan and convert it to onebit data */
+ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_LINEART;
+ if ( ! (mi->scanmode & MI_HASMODE_COLOR )
+ && ! (mi->scanmode & MI_HASMODE_GRAY )
+ && ! (mi->scanmode & MI_HASMODE_HALFTONE ) )
+ val[OPT_MODE].s = strdup(md->scanmode_list[i]);
+ ++i;
+ md->scanmode_list[i] = NULL;
+
+ /* bitdepth */
+ i = 0;
+
+#if 0
+ if ( mi->depth & MI_HASDEPTH_NIBBLE )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_4;
+#endif
+
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_8;
+ if ( mi->depth & MI_HASDEPTH_10 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_10;
+ if ( mi->depth & MI_HASDEPTH_12 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_12;
+ if ( mi->depth & MI_HASDEPTH_14 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_14;
+ if ( mi->depth & MI_HASDEPTH_16 )
+ md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_16;
+
+ md->bitdepth_list[0] = i;
+ if ( md->bitdepth_list[1] == (SANE_Int) MD_DEPTHVAL_8 )
+ val[OPT_BITDEPTH].w = md->bitdepth_list[1];
+ else
+ val[OPT_BITDEPTH].w = md->bitdepth_list[2];
+
+ /* Halftone */
+ md->halftone_mode_list[0] = (SANE_String) MD_HALFTONE0;
+ md->halftone_mode_list[1] = (SANE_String) MD_HALFTONE1;
+ md->halftone_mode_list[2] = (SANE_String) MD_HALFTONE2;
+ md->halftone_mode_list[3] = (SANE_String) MD_HALFTONE3;
+ md->halftone_mode_list[4] = (SANE_String) MD_HALFTONE4;
+ md->halftone_mode_list[5] = (SANE_String) MD_HALFTONE5;
+ md->halftone_mode_list[6] = (SANE_String) MD_HALFTONE6;
+ md->halftone_mode_list[7] = (SANE_String) MD_HALFTONE7;
+ md->halftone_mode_list[8] = (SANE_String) MD_HALFTONE8;
+ md->halftone_mode_list[9] = (SANE_String) MD_HALFTONE9;
+ md->halftone_mode_list[10] = (SANE_String) MD_HALFTONE10;
+ md->halftone_mode_list[11] = (SANE_String) MD_HALFTONE11;
+ md->halftone_mode_list[12] = NULL;
+ if ( val[OPT_HALFTONE].s )
+ free((void *) val[OPT_HALFTONE].s);
+ val[OPT_HALFTONE].s = strdup(md->halftone_mode_list[0]);
+
+ /* Resolution */
+ md->x_res_range_dpi.min = SANE_FIX(10.0);
+ md->x_res_range_dpi.max = SANE_FIX(mi->max_xresolution);
+ md->x_res_range_dpi.quant = SANE_FIX(1.0);
+ val[OPT_RESOLUTION].w = MIN(MD_RESOLUTION_DEFAULT, md->x_res_range_dpi.max);
+
+ md->y_res_range_dpi.min = SANE_FIX(10.0);
+ md->y_res_range_dpi.max = SANE_FIX(mi->max_yresolution);
+ md->y_res_range_dpi.quant = SANE_FIX(1.0);
+ val[OPT_Y_RESOLUTION].w = val[OPT_RESOLUTION].w; /* bind is default */
+
+ /* Preview mode */
+ val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* Geometry */
+ md->x_range_mm.min = SANE_FIX(0.0);
+ md->x_range_mm.max = SANE_FIX((double) mi->geo_width
+ / (double) mi->opt_resolution
+ * MM_PER_INCH);
+ md->x_range_mm.quant = SANE_FIX(0.0);
+ md->y_range_mm.min = SANE_FIX(0.0);
+ md->y_range_mm.max = SANE_FIX((double) mi->geo_height
+ / (double) mi->opt_resolution
+ * MM_PER_INCH);
+ md->y_range_mm.quant = SANE_FIX(0.0);
+ val[OPT_TL_X].w = SANE_FIX(0.0);
+ val[OPT_TL_Y].w = SANE_FIX(0.0);
+ val[OPT_BR_X].w = md->x_range_mm.max;
+ val[OPT_BR_Y].w = md->y_range_mm.max;
+
+ /* Enhancement group */
+ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT;
+ val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT;
+ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT;
+
+ /* Gamma */
+ /* linear gamma must come first */
+ i = 0;
+ md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_LINEAR;
+ md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_SCALAR;
+ md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_CUSTOM;
+ if ( val[OPT_GAMMA_MODE].s )
+ free((void *) val[OPT_GAMMA_MODE].s);
+ val[OPT_GAMMA_MODE].s = strdup(md->gammamode_list[0]);
+
+ md->gammamode_list[i] = NULL;
+
+ /* bind gamma */
+ val[OPT_GAMMA_BIND].w = SANE_TRUE;
+ val[OPT_GAMMA_SCALAR].w = MD_GAMMA_DEFAULT;
+ val[OPT_GAMMA_SCALAR_R].w = MD_GAMMA_DEFAULT;
+ val[OPT_GAMMA_SCALAR_G].w = MD_GAMMA_DEFAULT;
+ val[OPT_GAMMA_SCALAR_B].w = MD_GAMMA_DEFAULT;
+
+ /* If the device supports gamma tables, we allocate memory according */
+ /* to lookup table capabilities, otherwise we allocate 4096 elements */
+ /* which is sufficient for a color depth of 12. If the device */
+ /* does not support gamma tables, we fill the table according to */
+ /* the actual bit depth, i.e. 256 entries with a range of 0..255 */
+ /* if the actual bit depth is 8, for example. This will hopefully*/
+ /* make no trouble if the bit depth is 1. */
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ tablesize = 4096;
+ option_size = (int) pow(2.0, (double) val[OPT_BITDEPTH].w );
+ max_gamma_value = option_size - 1;
+ }
+ else
+ {
+ tablesize = md->max_lut_size;
+ option_size = tablesize;
+ max_gamma_value = md->max_lut_size - 1;
+ }
+
+ for ( color = 0; color < 4; color++ )
+ {
+ /* index 0 is used if bind gamma == true, index 1 to 3 */
+ /* if bind gamma == false */
+ if ( md->custom_gamma_table[color] )
+ free((void *) md->custom_gamma_table[color]);
+ md->custom_gamma_table[color] =
+ (SANE_Int *) malloc(tablesize * sizeof(SANE_Int));
+ DBG(100, "init_options: md->custom_gamma_table[%d]=%p, malloc'd %lu bytes\n",
+ color, (void *) md->custom_gamma_table[color], (u_long) (tablesize * sizeof(SANE_Int)));
+ if ( md->custom_gamma_table[color] == NULL )
+ {
+ DBG(1, "init_options: malloc for custom gamma table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ for ( i = 0; i < max_gamma_value; i++ )
+ md->custom_gamma_table[color][i] = i;
+ }
+
+ md->custom_gamma_range.min = 0;
+ md->custom_gamma_range.max = max_gamma_value;
+ md->custom_gamma_range.quant = 1;
+
+ sod[OPT_GAMMA_CUSTOM].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_R].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_G].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_B].size = option_size * sizeof (SANE_Int);
+
+ val[OPT_GAMMA_CUSTOM].wa = &md->custom_gamma_table[0][0];
+ val[OPT_GAMMA_CUSTOM_R].wa = &md->custom_gamma_table[1][0];
+ val[OPT_GAMMA_CUSTOM_G].wa = &md->custom_gamma_table[2][0];
+ val[OPT_GAMMA_CUSTOM_B].wa = &md->custom_gamma_table[3][0];
+
+ /* Shadow, midtone, highlight, exposure time */
+ md->channel_list[0] = (SANE_String) MD_CHANNEL_MASTER;
+ md->channel_list[1] = (SANE_String) MD_CHANNEL_RED;
+ md->channel_list[2] = (SANE_String) MD_CHANNEL_GREEN;
+ md->channel_list[3] = (SANE_String) MD_CHANNEL_BLUE;
+ md->channel_list[4] = NULL;
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup(md->channel_list[0]);
+ val[OPT_SHADOW].w = MD_SHADOW_DEFAULT;
+ val[OPT_SHADOW_R].w = MD_SHADOW_DEFAULT;
+ val[OPT_SHADOW_G].w = MD_SHADOW_DEFAULT;
+ val[OPT_SHADOW_B].w = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT;
+ val[OPT_MIDTONE_R].w = MD_MIDTONE_DEFAULT;
+ val[OPT_MIDTONE_G].w = MD_MIDTONE_DEFAULT;
+ val[OPT_MIDTONE_B].w = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_HIGHLIGHT_R].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_HIGHLIGHT_G].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_HIGHLIGHT_B].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_EXPOSURE_R].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_EXPOSURE_G].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_EXPOSURE_B].w = MD_EXPOSURE_DEFAULT;
+
+ /* special options */
+ val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
+
+ /* enable/disable option for backtracking */
+ val[OPT_DISABLE_BACKTRACK].w = md->opt_no_backtrack_default;
+
+ /* enable/disable calibration by backend */
+ val[OPT_CALIB_BACKEND].w = md->opt_backend_calib_default;
+
+ /* turn off the lamp during a scan */
+ val[OPT_LIGHTLID35].w = SANE_FALSE;
+
+ /* auto adjustment of threshold during a lineart scan */
+ val[OPT_AUTOADJUST].w = SANE_FALSE;
+
+ /* color balance (100% means no correction) */
+ val[OPT_BALANCE_R].w = SANE_FIX(100);
+ val[OPT_BALANCE_G].w = SANE_FIX(100);
+ val[OPT_BALANCE_B].w = SANE_FIX(100);
+
+ if ( first_call )
+ {
+ /* initialize option descriptors and ranges */
+
+ /* Percentage range for brightness, contrast */
+ md->percentage_range.min = 0 << SANE_FIXED_SCALE_SHIFT;
+ md->percentage_range.max = 200 << SANE_FIXED_SCALE_SHIFT;
+ md->percentage_range.quant = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ md->threshold_range.min = 1;
+ md->threshold_range.max = 255;
+ md->threshold_range.quant = 1;
+
+ md->scalar_gamma_range.min = SANE_FIX(0.1);
+ md->scalar_gamma_range.max = SANE_FIX(4.0);
+ md->scalar_gamma_range.quant = SANE_FIX(0.1);
+
+ md->shadow_range.min = 0;
+ md->shadow_range.max = 253;
+ md->shadow_range.quant = 1;
+
+ md->midtone_range.min = 1;
+ md->midtone_range.max = 254;
+ md->midtone_range.quant = 1;
+
+ md->highlight_range.min = 2;
+ md->highlight_range.max = 255;
+ md->highlight_range.quant = 1;
+
+ md->exposure_range.min = 0;
+ md->exposure_range.max = 510;
+ md->exposure_range.quant = 2;
+
+ md->balance_range.min = 0;
+ md->balance_range.max = 200 << SANE_FIXED_SCALE_SHIFT;
+ md->balance_range.quant = 1 << SANE_FIXED_SCALE_SHIFT;
+
+ /* default for most options */
+ for ( i = 0; i < NUM_OPTIONS; i++ )
+ {
+ sod[i].type = SANE_TYPE_FIXED;
+ sod[i].unit = SANE_UNIT_NONE;
+ sod[i].size = sizeof(SANE_Fixed);
+ sod[i].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ sod[i].constraint_type = SANE_CONSTRAINT_RANGE;
+ }
+
+ sod[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ sod[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ sod[OPT_NUM_OPTS].size = sizeof (SANE_Int);
+ sod[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ sod[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+ val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* NUM_OPTIONS is no option */
+ DBG(255, "sod=%p\n", (void *) sod);
+ DBG(255, "OPT_NUM_OPTS=%d\n", OPT_NUM_OPTS);
+ DBG(255, "SANE_CAP_SOFT_DETECT=%d\n", SANE_CAP_SOFT_DETECT);
+ DBG(255, "OPT_NUM_OPTS.cap=%d\n", sod[0].cap);
+
+ /* The Scan Mode Group */
+ sod[OPT_MODE_GROUP].title = M_TITLE_SCANMODEGRP;
+ sod[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_MODE_GROUP].size = 0;
+ sod[OPT_MODE_GROUP].desc = "";
+ sod[OPT_MODE_GROUP].cap = 0;
+ sod[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Scan source */
+ sod[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ sod[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ sod[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ sod[OPT_SOURCE].type = SANE_TYPE_STRING;
+ sod[OPT_SOURCE].size = max_string_size(md->scansource_list);
+ /* if there is only one scan source, deactivate option */
+ if ( md->scansource_list[1] == NULL )
+ sod[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_SOURCE].constraint.string_list = md->scansource_list;
+
+ /* Scan mode */
+ sod[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ sod[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ sod[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ sod[OPT_MODE].type = SANE_TYPE_STRING;
+ sod[OPT_MODE].size = max_string_size(md->scanmode_list);
+ sod[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_MODE].constraint.string_list = md->scanmode_list;
+
+ /* Bit depth */
+ sod[OPT_BITDEPTH].name = SANE_NAME_BIT_DEPTH;
+ sod[OPT_BITDEPTH].title = SANE_TITLE_BIT_DEPTH;
+ sod[OPT_BITDEPTH].desc = SANE_DESC_BIT_DEPTH;
+ sod[OPT_BITDEPTH].type = SANE_TYPE_INT;
+ sod[OPT_BITDEPTH].unit = SANE_UNIT_BIT;
+ sod[OPT_BITDEPTH].size = sizeof(SANE_Int);
+ /* if we have only 8 bit color deactivate this option */
+ if ( md->bitdepth_list[0] == 1 )
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BITDEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ sod[OPT_BITDEPTH].constraint.word_list = md->bitdepth_list;
+
+ /* Halftone */
+ sod[OPT_HALFTONE].name = SANE_NAME_HALFTONE;
+ sod[OPT_HALFTONE].title = SANE_TITLE_HALFTONE;
+ sod[OPT_HALFTONE].desc = SANE_DESC_HALFTONE;
+ sod[OPT_HALFTONE].type = SANE_TYPE_STRING;
+ sod[OPT_HALFTONE].size = max_string_size(md->halftone_mode_list);
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_HALFTONE].constraint.string_list = md->halftone_mode_list;
+
+ /* Resolution */
+ sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION;
+ sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION;
+ sod[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ sod[OPT_RESOLUTION].constraint.range = &md->x_res_range_dpi;
+
+ sod[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
+ sod[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
+ sod[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
+ sod[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
+ sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_Y_RESOLUTION].constraint.range = &md->y_res_range_dpi;
+
+ /* Preview */
+ sod[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
+ sod[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
+ sod[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
+ sod[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ sod[OPT_PREVIEW].size = sizeof(SANE_Bool);
+ sod[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Geometry group, for scan area selection */
+ sod[OPT_GEOMETRY_GROUP].title = M_TITLE_GEOMGRP;
+ sod[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_GEOMETRY_GROUP].size = 0;
+ sod[OPT_GEOMETRY_GROUP].desc = "";
+ sod[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ sod[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ sod[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ sod[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ sod[OPT_TL_X].unit = SANE_UNIT_MM;
+ sod[OPT_TL_X].constraint.range = &md->x_range_mm;
+
+ sod[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ sod[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ sod[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ sod[OPT_TL_Y].unit = SANE_UNIT_MM;
+ sod[OPT_TL_Y].constraint.range = &md->y_range_mm;
+
+ sod[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ sod[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ sod[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ sod[OPT_BR_X].unit = SANE_UNIT_MM;
+ sod[OPT_BR_X].constraint.range = &md->x_range_mm;
+
+ sod[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ sod[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ sod[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ sod[OPT_BR_Y].unit = SANE_UNIT_MM;
+ sod[OPT_BR_Y].constraint.range = &md->y_range_mm;
+
+ /* Enhancement group */
+ sod[OPT_ENHANCEMENT_GROUP].title = M_TITLE_ENHANCEGRP;
+ sod[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_ENHANCEMENT_GROUP].desc = "";
+ sod[OPT_ENHANCEMENT_GROUP].size = 0;
+ sod[OPT_ENHANCEMENT_GROUP].cap = 0;
+ sod[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BRIGHTNESS].constraint.range = &md->percentage_range;
+
+ sod[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ sod[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ sod[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ sod[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ sod[OPT_CONTRAST].constraint.range = &md->percentage_range;
+
+ sod[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
+ sod[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
+ sod[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
+ sod[OPT_THRESHOLD].type = SANE_TYPE_INT;
+ sod[OPT_THRESHOLD].size = sizeof(SANE_Int);
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].constraint.range = &md->threshold_range;
+
+ /* automatically adjust threshold for a lineart scan */
+ sod[OPT_AUTOADJUST].name = M_NAME_AUTOADJUST;
+ sod[OPT_AUTOADJUST].title = M_TITLE_AUTOADJUST;
+ sod[OPT_AUTOADJUST].desc = M_DESC_AUTOADJUST;
+ sod[OPT_AUTOADJUST].type = SANE_TYPE_BOOL;
+ sod[OPT_AUTOADJUST].size = sizeof(SANE_Bool);
+ sod[OPT_AUTOADJUST].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.auto_adjust, "off", 3) == 0 )
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+
+ /* Gamma */
+ sod[OPT_GAMMA_GROUP].title = "Gamma";
+ sod[OPT_GAMMA_GROUP].desc = "";
+ sod[OPT_GAMMA_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_GAMMA_GROUP].size = 0;
+ sod[OPT_GAMMA_GROUP].cap = 0;
+ sod[OPT_GAMMA_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_GAMMA_MODE].name = M_NAME_GAMMA_MODE;
+ sod[OPT_GAMMA_MODE].title = M_TITLE_GAMMA_MODE;
+ sod[OPT_GAMMA_MODE].desc = M_DESC_GAMMA_MODE;
+ sod[OPT_GAMMA_MODE].type = SANE_TYPE_STRING;
+ sod[OPT_GAMMA_MODE].size = max_string_size(md->gammamode_list);
+ sod[OPT_GAMMA_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_GAMMA_MODE].constraint.string_list = md->gammamode_list;
+
+ sod[OPT_GAMMA_BIND].name = M_NAME_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].title = M_TITLE_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].desc = M_DESC_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ sod[OPT_GAMMA_BIND].size = sizeof(SANE_Bool);
+ sod[OPT_GAMMA_BIND].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* this is active if gamma_bind == true and gammamode == scalar */
+ sod[OPT_GAMMA_SCALAR].name = M_NAME_GAMMA_SCALAR;
+ sod[OPT_GAMMA_SCALAR].title = M_TITLE_GAMMA_SCALAR;
+ sod[OPT_GAMMA_SCALAR].desc = M_DESC_GAMMA_SCALAR;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_SCALAR_R].name = M_NAME_GAMMA_SCALAR_R;
+ sod[OPT_GAMMA_SCALAR_R].title = M_TITLE_GAMMA_SCALAR_R;
+ sod[OPT_GAMMA_SCALAR_R].desc = M_DESC_GAMMA_SCALAR_R;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_SCALAR_G].name = M_NAME_GAMMA_SCALAR_G;
+ sod[OPT_GAMMA_SCALAR_G].title = M_TITLE_GAMMA_SCALAR_G;
+ sod[OPT_GAMMA_SCALAR_G].desc = M_DESC_GAMMA_SCALAR_G;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_SCALAR_B].name = M_NAME_GAMMA_SCALAR_B;
+ sod[OPT_GAMMA_SCALAR_B].title = M_TITLE_GAMMA_SCALAR_B;
+ sod[OPT_GAMMA_SCALAR_B].desc = M_DESC_GAMMA_SCALAR_B;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].constraint.range = &md->scalar_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM].name = SANE_NAME_GAMMA_VECTOR;
+ sod[OPT_GAMMA_CUSTOM].title = SANE_TITLE_GAMMA_VECTOR;
+ sod[OPT_GAMMA_CUSTOM].desc = SANE_DESC_GAMMA_VECTOR;
+ sod[OPT_GAMMA_CUSTOM].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM].constraint.range = &md->custom_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_CUSTOM_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_CUSTOM_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_CUSTOM_R].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_R].constraint.range = &md->custom_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_CUSTOM_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_CUSTOM_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_CUSTOM_G].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_G].constraint.range = &md->custom_gamma_range;
+
+ sod[OPT_GAMMA_CUSTOM_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_CUSTOM_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_CUSTOM_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_CUSTOM_B].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].size = option_size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_B].constraint.range = &md->custom_gamma_range;
+
+ /* Shadow, midtone, highlight */
+ sod[OPT_SMH_GROUP].title = M_TITLE_SMHGRP;
+ sod[OPT_SMH_GROUP].desc = "";
+ sod[OPT_SMH_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_SMH_GROUP].size = 0;
+ sod[OPT_SMH_GROUP].cap = 0;
+ sod[OPT_SMH_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_CHANNEL].name = M_NAME_CHANNEL;
+ sod[OPT_CHANNEL].title = M_TITLE_CHANNEL;
+ sod[OPT_CHANNEL].desc = M_DESC_CHANNEL;
+ sod[OPT_CHANNEL].type = SANE_TYPE_STRING;
+ sod[OPT_CHANNEL].size = max_string_size(md->channel_list);
+ sod[OPT_CHANNEL].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_CHANNEL].constraint.string_list = md->channel_list;
+
+ sod[OPT_SHADOW].name = SANE_NAME_SHADOW;
+ sod[OPT_SHADOW].title = SANE_TITLE_SHADOW;
+ sod[OPT_SHADOW].desc = SANE_DESC_SHADOW;
+ sod[OPT_SHADOW].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW].constraint.range = &md->shadow_range;
+
+ sod[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R;
+ sod[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R;
+ sod[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R;
+ sod[OPT_SHADOW_R].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW_R].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW_R].constraint.range = &md->shadow_range;
+
+ sod[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G;
+ sod[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G;
+ sod[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G;
+ sod[OPT_SHADOW_G].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW_G].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW_G].constraint.range = &md->shadow_range;
+
+ sod[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B;
+ sod[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B;
+ sod[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B;
+ sod[OPT_SHADOW_B].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW_B].size = sizeof(SANE_Int);
+ sod[OPT_SHADOW_B].constraint.range = &md->shadow_range;
+
+ sod[OPT_MIDTONE].name = M_NAME_MIDTONE;
+ sod[OPT_MIDTONE].title = M_TITLE_MIDTONE;
+ sod[OPT_MIDTONE].desc = M_DESC_MIDTONE;
+ sod[OPT_MIDTONE].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE].constraint.range = &md->midtone_range;
+
+ sod[OPT_MIDTONE_R].name = M_NAME_MIDTONE_R;
+ sod[OPT_MIDTONE_R].title = M_TITLE_MIDTONE_R;
+ sod[OPT_MIDTONE_R].desc = M_DESC_MIDTONE_R;
+ sod[OPT_MIDTONE_R].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE_R].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE_R].constraint.range = &md->midtone_range;
+
+ sod[OPT_MIDTONE_G].name = M_NAME_MIDTONE_G;
+ sod[OPT_MIDTONE_G].title = M_TITLE_MIDTONE_G;
+ sod[OPT_MIDTONE_G].desc = M_DESC_MIDTONE_G;
+ sod[OPT_MIDTONE_G].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE_G].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE_G].constraint.range = &md->midtone_range;
+
+ sod[OPT_MIDTONE_B].name = M_NAME_MIDTONE_B;
+ sod[OPT_MIDTONE_B].title = M_TITLE_MIDTONE_B;
+ sod[OPT_MIDTONE_B].desc = M_DESC_MIDTONE_B;
+ sod[OPT_MIDTONE_B].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE_B].size = sizeof(SANE_Int);
+ sod[OPT_MIDTONE_B].constraint.range = &md->midtone_range;
+
+ sod[OPT_HIGHLIGHT].name = SANE_NAME_HIGHLIGHT;
+ sod[OPT_HIGHLIGHT].title = SANE_TITLE_HIGHLIGHT;
+ sod[OPT_HIGHLIGHT].desc = SANE_DESC_HIGHLIGHT;
+ sod[OPT_HIGHLIGHT].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT].constraint.range = &md->highlight_range;
+
+ sod[OPT_HIGHLIGHT_R].name = SANE_NAME_HIGHLIGHT_R;
+ sod[OPT_HIGHLIGHT_R].title = SANE_TITLE_HIGHLIGHT_R;
+ sod[OPT_HIGHLIGHT_R].desc = SANE_DESC_HIGHLIGHT_R;
+ sod[OPT_HIGHLIGHT_R].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT_R].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT_R].constraint.range = &md->highlight_range;
+
+ sod[OPT_HIGHLIGHT_G].name = SANE_NAME_HIGHLIGHT_G;
+ sod[OPT_HIGHLIGHT_G].title = SANE_TITLE_HIGHLIGHT_G;
+ sod[OPT_HIGHLIGHT_G].desc = SANE_DESC_HIGHLIGHT_G;
+ sod[OPT_HIGHLIGHT_G].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT_G].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT_G].constraint.range = &md->highlight_range;
+
+ sod[OPT_HIGHLIGHT_B].name = SANE_NAME_HIGHLIGHT_B;
+ sod[OPT_HIGHLIGHT_B].title = SANE_TITLE_HIGHLIGHT_B;
+ sod[OPT_HIGHLIGHT_B].desc = SANE_DESC_HIGHLIGHT_B;
+ sod[OPT_HIGHLIGHT_B].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT_B].size = sizeof(SANE_Int);
+ sod[OPT_HIGHLIGHT_B].constraint.range = &md->highlight_range;
+
+ sod[OPT_EXPOSURE].name = SANE_NAME_SCAN_EXPOS_TIME;
+ sod[OPT_EXPOSURE].title = SANE_TITLE_SCAN_EXPOS_TIME;
+ sod[OPT_EXPOSURE].desc = SANE_DESC_SCAN_EXPOS_TIME;
+ sod[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE].constraint.range = &md->exposure_range;
+
+ sod[OPT_EXPOSURE_R].name = SANE_NAME_SCAN_EXPOS_TIME_R;
+ sod[OPT_EXPOSURE_R].title = SANE_TITLE_SCAN_EXPOS_TIME_R;
+ sod[OPT_EXPOSURE_R].desc = SANE_DESC_SCAN_EXPOS_TIME_R;
+ sod[OPT_EXPOSURE_R].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE_R].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE_R].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE_R].constraint.range = &md->exposure_range;
+
+ sod[OPT_EXPOSURE_G].name = SANE_NAME_SCAN_EXPOS_TIME_G;
+ sod[OPT_EXPOSURE_G].title = SANE_TITLE_SCAN_EXPOS_TIME_G;
+ sod[OPT_EXPOSURE_G].desc = SANE_DESC_SCAN_EXPOS_TIME_G;
+ sod[OPT_EXPOSURE_G].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE_G].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE_G].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE_G].constraint.range = &md->exposure_range;
+
+ sod[OPT_EXPOSURE_B].name = SANE_NAME_SCAN_EXPOS_TIME_B;
+ sod[OPT_EXPOSURE_B].title = SANE_TITLE_SCAN_EXPOS_TIME_B;
+ sod[OPT_EXPOSURE_B].desc = SANE_DESC_SCAN_EXPOS_TIME_B;
+ sod[OPT_EXPOSURE_B].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE_B].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE_B].size = sizeof(SANE_Int);
+ sod[OPT_EXPOSURE_B].constraint.range = &md->exposure_range;
+
+ /* The Special Options Group */
+ sod[OPT_SPECIAL].title = M_TITLE_SPECIALGRP;
+ sod[OPT_SPECIAL].type = SANE_TYPE_GROUP;
+ sod[OPT_SPECIAL].size = 0;
+ sod[OPT_SPECIAL].desc = "";
+ sod[OPT_SPECIAL].cap = SANE_CAP_ADVANCED;
+ sod[OPT_SPECIAL].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
+ sod[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
+ sod[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
+ sod[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
+ sod[OPT_RESOLUTION_BIND].size = sizeof(SANE_Bool);
+ sod[OPT_RESOLUTION_BIND].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_RESOLUTION_BIND].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* enable/disable option for backtracking */
+ sod[OPT_DISABLE_BACKTRACK].name = M_NAME_NOBACKTRACK;
+ sod[OPT_DISABLE_BACKTRACK].title = M_TITLE_NOBACKTRACK;
+ sod[OPT_DISABLE_BACKTRACK].desc = M_DESC_NOBACKTRACK;
+ sod[OPT_DISABLE_BACKTRACK].type = SANE_TYPE_BOOL;
+ sod[OPT_DISABLE_BACKTRACK].size = sizeof(SANE_Bool);
+ sod[OPT_DISABLE_BACKTRACK].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_DISABLE_BACKTRACK].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.no_backtracking, "off", 3) == 0 )
+ sod[OPT_DISABLE_BACKTRACK].cap |= SANE_CAP_INACTIVE;
+
+ /* calibration by driver */
+ sod[OPT_CALIB_BACKEND].name = M_NAME_CALIBBACKEND;
+ sod[OPT_CALIB_BACKEND].title = M_TITLE_CALIBBACKEND;
+ sod[OPT_CALIB_BACKEND].desc = M_DESC_CALIBBACKEND;
+ sod[OPT_CALIB_BACKEND].type = SANE_TYPE_BOOL;
+ sod[OPT_CALIB_BACKEND].size = sizeof(SANE_Bool);
+ sod[OPT_CALIB_BACKEND].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_CALIB_BACKEND].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.backend_calibration, "off", 3) == 0 )
+ sod[OPT_CALIB_BACKEND].cap |= SANE_CAP_INACTIVE;
+
+ /* turn off the lamp of the flatbed during a scan */
+ sod[OPT_LIGHTLID35].name = M_NAME_LIGHTLID35;
+ sod[OPT_LIGHTLID35].title = M_TITLE_LIGHTLID35;
+ sod[OPT_LIGHTLID35].desc = M_DESC_LIGHTLID35;
+ sod[OPT_LIGHTLID35].type = SANE_TYPE_BOOL;
+ sod[OPT_LIGHTLID35].size = sizeof(SANE_Bool);
+ sod[OPT_LIGHTLID35].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_LIGHTLID35].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.lightlid35, "off", 3) == 0 )
+ sod[OPT_LIGHTLID35].cap |= SANE_CAP_INACTIVE;
+
+ /* toggle the lamp of the flatbed */
+ sod[OPT_TOGGLELAMP].name = M_NAME_TOGGLELAMP;
+ sod[OPT_TOGGLELAMP].title = M_TITLE_TOGGLELAMP;
+ sod[OPT_TOGGLELAMP].desc = M_DESC_TOGGLELAMP;
+ sod[OPT_TOGGLELAMP].type = SANE_TYPE_BUTTON;
+ sod[OPT_TOGGLELAMP].size = 0;
+ sod[OPT_TOGGLELAMP].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_TOGGLELAMP].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.toggle_lamp, "off", 3) == 0 )
+ sod[OPT_TOGGLELAMP].cap |= SANE_CAP_INACTIVE;
+
+ /* color balance */
+ sod[OPT_COLORBALANCE].title = M_TITLE_COLBALANCEGRP;
+ sod[OPT_COLORBALANCE].type = SANE_TYPE_GROUP;
+ sod[OPT_COLORBALANCE].size = 0;
+ sod[OPT_COLORBALANCE].desc = "";
+ sod[OPT_COLORBALANCE].cap = SANE_CAP_ADVANCED;
+ sod[OPT_COLORBALANCE].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_BALANCE_R].name = M_NAME_BALANCE_R;
+ sod[OPT_BALANCE_R].title = M_TITLE_BALANCE_R;
+ sod[OPT_BALANCE_R].desc = M_DESC_BALANCE_R;
+ sod[OPT_BALANCE_R].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_R].constraint.range = &md->balance_range;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+
+ sod[OPT_BALANCE_G].name = M_NAME_BALANCE_G;
+ sod[OPT_BALANCE_G].title = M_TITLE_BALANCE_G;
+ sod[OPT_BALANCE_G].desc = M_DESC_BALANCE_G;
+ sod[OPT_BALANCE_G].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_G].constraint.range = &md->balance_range;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+
+ sod[OPT_BALANCE_B].name = M_NAME_BALANCE_B;
+ sod[OPT_BALANCE_B].title = M_TITLE_BALANCE_B;
+ sod[OPT_BALANCE_B].desc = M_DESC_BALANCE_B;
+ sod[OPT_BALANCE_B].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_B].constraint.range = &md->balance_range;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+
+ sod[OPT_BALANCE_FW].name = M_NAME_BALANCE_FW;
+ sod[OPT_BALANCE_FW].title = M_TITLE_BALANCE_FW;
+ sod[OPT_BALANCE_FW].desc = M_DESC_BALANCE_FW;
+ sod[OPT_BALANCE_FW].type = SANE_TYPE_BUTTON;
+ sod[OPT_BALANCE_FW].size = 0;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_BALANCE_FW].constraint_type = SANE_CONSTRAINT_NONE;
+ if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 )
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+ }
+
+ status = set_option_dependencies(ms, sod, val);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- set_option_dependencies() ---------------------------------------*/
+
+static SANE_Status
+set_option_dependencies(Microtek2_Scanner *ms, SANE_Option_Descriptor *sod,
+ Option_Value *val)
+{
+
+ Microtek2_Device *md;
+ md = ms->dev;
+
+ DBG(40, "set_option_dependencies: val=%p, sod=%p, mode=%s\n",
+ (void *) val, (void *) sod, val[OPT_MODE].s);
+
+ if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 )
+ {
+ /* activate brightness,..., deactivate halftone pattern */
+ /* and threshold */
+ sod[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ if ( md->bitdepth_list[0] != 1 )
+ sod[OPT_BITDEPTH].cap &= ~SANE_CAP_INACTIVE;
+ else
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+ if ( ! ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) )
+ {
+ sod[OPT_BALANCE_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap &= ~SANE_CAP_INACTIVE;
+ }
+ /* reset options values that are inactive to their default */
+ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT;
+ }
+
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ {
+ sod[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ if ( md->bitdepth_list[0] != 1 )
+ sod[OPT_BITDEPTH].cap &= ~SANE_CAP_INACTIVE;
+ else
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER);
+ }
+
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 )
+ {
+ sod[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT;
+ val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT;
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER);
+ val[OPT_SHADOW].w = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT;
+ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT;
+ }
+
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 )
+ {
+ sod[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE;
+ if ( val[OPT_AUTOADJUST].w == SANE_FALSE )
+ sod[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ else
+ sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_AUTOADJUST].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT;
+ val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT;
+ if ( val[OPT_CHANNEL].s )
+ free((void *) val[OPT_CHANNEL].s);
+ val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER);
+ val[OPT_SHADOW].w = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT;
+ }
+
+ else
+ {
+ DBG(1, "set_option_dependencies: unknown mode '%s'\n",
+ val[OPT_MODE].s );
+ return SANE_STATUS_INVAL;
+ }
+
+ /* these ones are always inactive if the mode changes */
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+
+ /* reset options values that are inactive to their default */
+ val[OPT_SHADOW_R].w = val[OPT_SHADOW_G].w = val[OPT_SHADOW_B].w
+ = MD_SHADOW_DEFAULT;
+ val[OPT_MIDTONE_R].w = val[OPT_MIDTONE_G].w = val[OPT_MIDTONE_B].w
+ = MD_MIDTONE_DEFAULT;
+ val[OPT_HIGHLIGHT_R].w = val[OPT_HIGHLIGHT_G].w = val[OPT_HIGHLIGHT_B].w
+ = MD_HIGHLIGHT_DEFAULT;
+ val[OPT_EXPOSURE_R].w = val[OPT_EXPOSURE_G].w = val[OPT_EXPOSURE_B].w
+ = MD_EXPOSURE_DEFAULT;
+
+ if ( SANE_OPTION_IS_SETTABLE(sod[OPT_GAMMA_MODE].cap) )
+ {
+ restore_gamma_options(sod, val);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- sane_control_option() -------------------------------------------*/
+
+SANE_Status
+sane_control_option(SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *value, SANE_Int *info)
+{
+ Microtek2_Scanner *ms = handle;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ Option_Value *val;
+ SANE_Option_Descriptor *sod;
+ SANE_Status status;
+
+ md = ms->dev;
+ val = &ms->val[0];
+ sod = &ms->sod[0];
+ mi = &md->info[md->scan_source];
+
+ if ( ms->scanning )
+ return SANE_STATUS_DEVICE_BUSY;
+
+ if ( option < 0 || option >= NUM_OPTIONS )
+ {
+ DBG(100, "sane_control_option: option %d; action %d \n", option, action);
+ DBG(10, "sane_control_option: option %d invalid\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ( ! SANE_OPTION_IS_ACTIVE(ms->sod[option].cap) )
+ {
+ DBG(100, "sane_control_option: option %d; action %d \n", option, action);
+ DBG(10, "sane_control_option: option %d not active\n", option);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ( info )
+ *info = 0;
+
+ switch ( action )
+ {
+ case SANE_ACTION_GET_VALUE: /* read out option values */
+ switch ( option )
+ {
+ /* word options */
+ case OPT_BITDEPTH:
+ case OPT_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_THRESHOLD:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ case OPT_MIDTONE:
+ case OPT_MIDTONE_R:
+ case OPT_MIDTONE_G:
+ case OPT_MIDTONE_B:
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ case OPT_EXPOSURE:
+ case OPT_EXPOSURE_R:
+ case OPT_EXPOSURE_G:
+ case OPT_EXPOSURE_B:
+ case OPT_GAMMA_SCALAR:
+ case OPT_GAMMA_SCALAR_R:
+ case OPT_GAMMA_SCALAR_G:
+ case OPT_GAMMA_SCALAR_B:
+ case OPT_BALANCE_R:
+ case OPT_BALANCE_G:
+ case OPT_BALANCE_B:
+
+ *(SANE_Word *) value = val[option].w;
+
+ if (sod[option].type == SANE_TYPE_FIXED )
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%f\n",
+ option, action, SANE_UNFIX(val[option].w));
+ else
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%d\n",
+ option, action, val[option].w);
+
+ return SANE_STATUS_GOOD;
+
+ /* boolean options */
+ case OPT_RESOLUTION_BIND:
+ case OPT_DISABLE_BACKTRACK:
+ case OPT_CALIB_BACKEND:
+ case OPT_LIGHTLID35:
+ case OPT_GAMMA_BIND:
+ case OPT_AUTOADJUST:
+ *(SANE_Bool *) value = val[option].w;
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%d\n",
+ option, action, val[option].w);
+ return SANE_STATUS_GOOD;
+
+ /* string options */
+ case OPT_SOURCE:
+ case OPT_MODE:
+ case OPT_HALFTONE:
+ case OPT_CHANNEL:
+ case OPT_GAMMA_MODE:
+ strcpy(value, val[option].s);
+ DBG(50, "sane_control_option: opt=%d, act=%d, val=%s\n",
+ option, action, val[option].s);
+ return SANE_STATUS_GOOD;
+
+ /* word array options */
+ case OPT_GAMMA_CUSTOM:
+ case OPT_GAMMA_CUSTOM_R:
+ case OPT_GAMMA_CUSTOM_G:
+ case OPT_GAMMA_CUSTOM_B:
+ memcpy(value, val[option].wa, sod[option].size);
+ return SANE_STATUS_GOOD;
+
+ /* button options */
+ case OPT_TOGGLELAMP:
+ case OPT_BALANCE_FW:
+ return SANE_STATUS_GOOD;
+
+ /* others */
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) value = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ /* NOTREACHED */
+ /* break; */
+
+ case SANE_ACTION_SET_VALUE: /* set option values */
+ if ( ! SANE_OPTION_IS_SETTABLE(sod[option].cap) )
+ {
+ DBG(100, "sane_control_option: option %d; action %d \n",
+ option, action);
+ DBG(10, "sane_control_option: trying to set unsettable option\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* do not check OPT_BR_Y, xscanimage sometimes tries to set */
+ /* it to a too large value; bug in xscanimage ? */
+ /* if ( option != OPT_BR_Y )
+ { */
+ status = sanei_constrain_value(ms->sod + option, value, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG(10, "sane_control_option: invalid option value\n");
+ return status;
+ }
+ /* } */
+
+ switch ( sod[option].type )
+ {
+ case SANE_TYPE_BOOL:
+ DBG(50, "sane_control_option: option=%d, action=%d, value=%d\n",
+ option, action, *(SANE_Int *) value);
+ if ( ! ( ( *(SANE_Bool *) value == SANE_TRUE )
+ || ( *(SANE_Bool *) value == SANE_FALSE ) ) )
+ {
+ DBG(10, "sane_control_option: invalid BOOL option value\n");
+ return SANE_STATUS_INVAL;
+ }
+ if ( val[option].w == *(SANE_Bool *) value ) /* no change */
+ return SANE_STATUS_GOOD;
+ val[option].w = *(SANE_Bool *) value;
+ break;
+
+ case SANE_TYPE_INT:
+ if ( sod[option].size == sizeof(SANE_Int) )
+ {
+ /* word option */
+ DBG(50, "sane_control_option: option=%d, action=%d, "
+ "value=%d\n", option, action, *(SANE_Int *) value);
+ if ( val[option].w == *(SANE_Int *) value ) /* no change */
+ return SANE_STATUS_GOOD;
+ val[option].w = *(SANE_Int *) value;
+ }
+ else
+ {
+ /* word array option */
+ memcpy(val[option].wa, value, sod[option].size);
+ }
+ break;
+
+ case SANE_TYPE_FIXED:
+ DBG(50, "sane_control_option: option=%d, action=%d, value=%f\n",
+ option, action, SANE_UNFIX( *(SANE_Fixed *) value));
+ if ( val[option].w == *(SANE_Fixed *) value ) /* no change */
+ return SANE_STATUS_GOOD;
+ val[option].w = *(SANE_Fixed *) value;
+ break;
+
+ case SANE_TYPE_STRING:
+ DBG(50, "sane_control_option: option=%d, action=%d, value=%s\n",
+ option, action, (SANE_String) value);
+ if ( strcmp(val[option].s, (SANE_String) value) == 0 )
+ return SANE_STATUS_GOOD; /* no change */
+ if ( val[option].s )
+ free((void *) val[option].s);
+ val[option].s = strdup(value);
+ if ( val[option].s == NULL )
+ {
+ DBG(1, "sane_control_option: strdup failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ break;
+
+ case SANE_TYPE_BUTTON:
+ break;
+
+ default:
+ DBG(1, "sane_control_option: unknown type %d\n",
+ sod[option].type);
+ break;
+ }
+
+ switch ( option )
+ {
+ case OPT_RESOLUTION:
+ case OPT_Y_RESOLUTION:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+ case OPT_DISABLE_BACKTRACK:
+ case OPT_CALIB_BACKEND:
+ case OPT_LIGHTLID35:
+ case OPT_PREVIEW:
+ case OPT_BRIGHTNESS:
+ case OPT_THRESHOLD:
+ case OPT_CONTRAST:
+ case OPT_EXPOSURE:
+ case OPT_EXPOSURE_R:
+ case OPT_EXPOSURE_G:
+ case OPT_EXPOSURE_B:
+ case OPT_GAMMA_SCALAR:
+ case OPT_GAMMA_SCALAR_R:
+ case OPT_GAMMA_SCALAR_G:
+ case OPT_GAMMA_SCALAR_B:
+ case OPT_GAMMA_CUSTOM:
+ case OPT_GAMMA_CUSTOM_R:
+ case OPT_GAMMA_CUSTOM_G:
+ case OPT_GAMMA_CUSTOM_B:
+ case OPT_HALFTONE:
+ case OPT_BALANCE_R:
+ case OPT_BALANCE_G:
+ case OPT_BALANCE_B:
+ return SANE_STATUS_GOOD;
+
+ case OPT_BITDEPTH:
+ /* If the bitdepth has changed we must change the size of */
+ /* the gamma table if the device does not support gamma */
+ /* tables. This will hopefully cause no trouble if the */
+ /* mode is one bit */
+
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ int max_gamma_value;
+ int size;
+ int color;
+ int i;
+
+ size = (int) pow(2.0, (double) val[OPT_BITDEPTH].w) - 1;
+ max_gamma_value = size - 1;
+ for ( color = 0; color < 4; color++ )
+ {
+ for ( i = 0; i < max_gamma_value; i++ )
+ md->custom_gamma_table[color][i] = (SANE_Int) i;
+ }
+ md->custom_gamma_range.max = (SANE_Int) max_gamma_value;
+ sod[OPT_GAMMA_CUSTOM].size = size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_R].size = size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_G].size = size * sizeof (SANE_Int);
+ sod[OPT_GAMMA_CUSTOM_B].size = size * sizeof (SANE_Int);
+
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ }
+
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_SOURCE:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ if ( strcmp(val[option].s, MD_SOURCESTRING_FLATBED) == 0 )
+ md->scan_source = MD_SOURCE_FLATBED;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_TMA) == 0 )
+ md->scan_source = MD_SOURCE_TMA;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_ADF) == 0 )
+ md->scan_source = MD_SOURCE_ADF;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_STRIPE) == 0 )
+ md->scan_source = MD_SOURCE_STRIPE;
+ else if ( strcmp(val[option].s, MD_SOURCESTRING_SLIDE) == 0 )
+ md->scan_source = MD_SOURCE_SLIDE;
+ else
+ {
+ DBG(1, "sane_control_option: unsupported option %s\n",
+ val[option].s);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ init_options(ms, md->scan_source);
+ return SANE_STATUS_GOOD;
+
+ case OPT_MODE:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+
+ status = set_option_dependencies(ms, sod, val);
+
+ /* Options with side effects need special treatment. They are */
+ /* reset, even if they were set by set_option_dependencies(): */
+ /* if we have more than one color depth activate this option */
+
+ if ( md->bitdepth_list[0] == 1 )
+ sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE;
+ if ( strncmp(md->opts.auto_adjust, "off", 3) == 0 )
+ sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE;
+
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ return SANE_STATUS_GOOD;
+
+ case OPT_CHANNEL:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ if ( strcmp(val[option].s, MD_CHANNEL_MASTER) == 0 )
+ {
+ sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[option].s, MD_CHANNEL_RED) == 0 )
+ {
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[option].s, MD_CHANNEL_GREEN) == 0 )
+ {
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[option].s, MD_CHANNEL_BLUE) == 0 )
+ {
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_HIGHLIGHT_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_EXPOSURE_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_MODE:
+ restore_gamma_options(sod, val);
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_BIND:
+ restore_gamma_options(sod, val);
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_SHADOW:
+ case OPT_SHADOW_R:
+ case OPT_SHADOW_G:
+ case OPT_SHADOW_B:
+ if ( val[option].w >= val[option + 1].w )
+ {
+ val[option + 1].w = val[option].w + 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if ( val[option + 1].w >= val[option + 2].w )
+ val[option + 2].w = val[option + 1].w + 1;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_MIDTONE:
+ case OPT_MIDTONE_R:
+ case OPT_MIDTONE_G:
+ case OPT_MIDTONE_B:
+ if ( val[option].w <= val[option - 1].w )
+ {
+ val[option - 1].w = val[option].w - 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if ( val[option].w >= val[option + 1].w )
+ {
+ val[option + 1].w = val[option].w + 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_HIGHLIGHT:
+ case OPT_HIGHLIGHT_R:
+ case OPT_HIGHLIGHT_G:
+ case OPT_HIGHLIGHT_B:
+ if ( val[option].w <= val[option - 1].w )
+ {
+ val[option - 1].w = val[option].w - 1;
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ }
+ if ( val[option - 1].w <= val[option - 2].w )
+ val[option - 2].w = val[option - 1].w - 1;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_RESOLUTION_BIND:
+ if ( ms->val[option].w == SANE_FALSE )
+ {
+ ms->sod[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ ms->sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
+ }
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+
+ case OPT_TOGGLELAMP:
+ status = scsi_read_system_status(md, -1);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_IO_ERROR;
+
+ md->status.flamp ^= 1;
+ status = scsi_send_system_status(md, -1);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_IO_ERROR;
+ return SANE_STATUS_GOOD;
+
+ case OPT_AUTOADJUST:
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ if ( ms->val[option].w == SANE_FALSE )
+ ms->sod[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
+ else
+ ms->sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+
+ return SANE_STATUS_GOOD;
+
+ case OPT_BALANCE_FW:
+ val[OPT_BALANCE_R].w =
+ SANE_FIX((uint8_t)( (float)mi->balance[0] / 2.55 ) );
+ val[OPT_BALANCE_G].w =
+ SANE_FIX((uint8_t)( (float)mi->balance[1] / 2.55 ) );
+ val[OPT_BALANCE_B].w =
+ SANE_FIX((uint8_t)( (float)mi->balance[2] / 2.55 ) );
+ if ( info )
+ *info |= SANE_INFO_RELOAD_OPTIONS;
+
+ return SANE_STATUS_GOOD;
+
+
+ default:
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#if 0
+ break;
+#endif
+ default:
+ DBG(1, "sane_control_option: Unsupported action %d\n", action);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+/*---------- sane_get_option_descriptor() ------------------------------------*/
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor(SANE_Handle handle, SANE_Int n)
+{
+ Microtek2_Scanner *ms = handle;
+
+ DBG(255, "sane_get_option_descriptor: handle=%p, sod=%p, opt=%d\n",
+ (void *) handle, (void *) ms->sod, n);
+
+ if ( n < 0 || n >= NUM_OPTIONS )
+ {
+ DBG(30, "sane_get_option_descriptor: invalid option %d\n", n);
+ return NULL;
+ }
+
+ return &ms->sod[n];
+}
+
+/*---------- restore_gamma_options() -----------------------------------------*/
+
+static SANE_Status
+restore_gamma_options(SANE_Option_Descriptor *sod, Option_Value *val)
+{
+
+ DBG(40, "restore_gamma_options: val=%p, sod=%p\n", (void *) val, (void *) sod);
+ /* if we dont have a gamma table return immediately */
+ if ( ! val[OPT_GAMMA_MODE].s )
+ return SANE_STATUS_GOOD;
+
+ if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 )
+ {
+ sod[OPT_GAMMA_MODE].cap &= ~SANE_CAP_INACTIVE;
+ if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_LINEAR) == 0 )
+ {
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_SCALAR) == 0 )
+ {
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if ( val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ {
+ sod[OPT_GAMMA_SCALAR].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_CUSTOM) == 0 )
+ {
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ if ( val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ {
+ sod[OPT_GAMMA_CUSTOM].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ {
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ }
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ {
+ sod[OPT_GAMMA_MODE].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_LINEAR) == 0 )
+ {
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_SCALAR) == 0 )
+ {
+ sod[OPT_GAMMA_SCALAR].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ }
+ else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_CUSTOM) == 0 )
+ {
+ sod[OPT_GAMMA_CUSTOM].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ }
+ }
+ else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0
+ || strcmp(val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 )
+ {
+ /* reset gamma to default */
+ if ( val[OPT_GAMMA_MODE].s )
+ free((void *) val[OPT_GAMMA_MODE].s);
+ val[OPT_GAMMA_MODE].s = strdup(MD_GAMMAMODE_LINEAR);
+ sod[OPT_GAMMA_MODE].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE;
+ }
+ else
+ DBG(1, "restore_gamma_options: unknown mode %s\n", val[OPT_MODE].s);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- calculate_sane_params() -----------------------------------------*/
+
+static SANE_Status
+calculate_sane_params(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+
+ DBG(30, "calculate_sane_params: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( ! mi->onepass && ms->mode == MS_MODE_COLOR )
+ {
+ if ( ms->current_pass == 1 )
+ ms->params.format = SANE_FRAME_RED;
+ else if ( ms->current_pass == 2 )
+ ms->params.format = SANE_FRAME_GREEN;
+ else if ( ms->current_pass == 3 )
+ ms->params.format = SANE_FRAME_BLUE;
+ else
+ {
+ DBG(1, "calculate_sane_params: invalid pass number %d\n",
+ ms->current_pass);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ else if ( mi->onepass && ms->mode == MS_MODE_COLOR )
+ ms->params.format = SANE_FRAME_RGB;
+ else
+ ms->params.format = SANE_FRAME_GRAY;
+
+ if ( ! mi->onepass && ms->mode == MS_MODE_COLOR && ms->current_pass < 3 )
+ ms->params.last_frame = SANE_FALSE;
+ else
+ ms->params.last_frame = SANE_TRUE;
+ ms->params.lines = ms->src_remaining_lines;
+ ms->params.pixels_per_line = ms->ppl;
+ ms->params.bytes_per_line = ms->real_bpl;
+ ms->params.depth = ms->bits_per_pixel_out;
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/*---------- get_calib_params() ----------------------------------------------*/
+
+static void
+get_calib_params(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+
+ DBG(30, "get_calib_params: handle=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( md->model_flags & MD_CALIB_DIVISOR_600 )
+ {
+ if ( ms->x_resolution_dpi <= 600 )
+ mi->calib_divisor = 2;
+ else
+ mi->calib_divisor = 1;
+ }
+ DBG(30, "Calib Divisor: %d\n", mi->calib_divisor);
+
+
+ ms->x_resolution_dpi = mi->opt_resolution / mi->calib_divisor;
+ ms->y_resolution_dpi = mi->opt_resolution / 5; /* ignore dust particles */
+ ms->x1_dots = 0;
+ ms->y1_dots = mi->calib_white;
+ ms->width_dots = mi->geo_width;
+ if ( md->shading_length != 0 )
+ ms->height_dots = md->shading_length;
+ else
+ ms->height_dots = mi->calib_space;
+
+ ms->mode = MS_MODE_COLOR;
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ ms->depth = 16;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ ms->depth = 14;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ ms->depth = 12;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ ms->depth = 10;
+ else
+ ms->depth = 8;
+
+ ms->stay = 0;
+ if ( mi->calib_space < 10 )
+ ms->stay = 1;
+ ms->rawdat = 1;
+ ms->quality = 1;
+ ms->fastscan = 0;
+/* ms->scan_source = md->scan_source; */
+ ms->scan_source = 0;
+ ms->brightness_m = ms->brightness_r = ms->brightness_g =
+ ms->brightness_b = 128;
+ ms->exposure_m = ms->exposure_r = ms->exposure_g = ms->exposure_b = 0;
+ ms->contrast_m = ms->contrast_r = ms->contrast_g = ms->contrast_b = 128;
+ ms->shadow_m = ms->shadow_r = ms->shadow_g = ms->shadow_b = 0;
+ ms->midtone_m = ms->midtone_r = ms->midtone_g = ms->midtone_b = 128;
+ ms->highlight_m = ms->highlight_r = ms->highlight_g = ms->highlight_b = 255;
+
+ return;
+}
+
+
+/*---------- get_scan_parameters () ------------------------------------------*/
+
+static SANE_Status
+get_scan_parameters(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ double dpm; /* dots per millimeter */
+ int x2_dots;
+ int y2_dots;
+ int i;
+
+
+ DBG(30, "get_scan_parameters: handle=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ get_scan_mode_and_depth(ms, &ms->mode, &ms->depth,
+ &ms->bits_per_pixel_in, &ms->bits_per_pixel_out);
+
+ /* get the scan_source */
+ if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_FLATBED) == 0 )
+ ms->scan_source = MS_SOURCE_FLATBED;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_ADF) == 0 )
+ ms->scan_source = MS_SOURCE_ADF;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_TMA) == 0 )
+ ms->scan_source = MS_SOURCE_TMA;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_STRIPE) == 0 )
+ ms->scan_source = MS_SOURCE_STRIPE;
+ else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_SLIDE) == 0 )
+ ms->scan_source = MS_SOURCE_SLIDE;
+
+ /* enable/disable backtracking */
+ if ( ms->val[OPT_DISABLE_BACKTRACK].w == SANE_TRUE )
+ ms->no_backtracking = 1;
+ else
+ ms->no_backtracking = 0;
+
+ /* turn off the lamp during a scan */
+ if ( ms->val[OPT_LIGHTLID35].w == SANE_TRUE )
+ ms->lightlid35 = 1;
+ else
+ ms->lightlid35 = 0;
+
+ /* automatic adjustment of threshold */
+ if ( ms->val[OPT_AUTOADJUST].w == SANE_TRUE)
+ ms->auto_adjust = 1;
+ else
+ ms->auto_adjust = 0;
+
+ /* color calibration by backend */
+ if ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE )
+ ms->calib_backend = 1;
+ else
+ ms->calib_backend = 0;
+
+ /* if halftone mode select halftone pattern */
+ if ( ms->mode == MS_MODE_HALFTONE )
+ {
+ i = 0;
+ while ( strcmp(md->halftone_mode_list[i], ms->val[OPT_HALFTONE].s) )
+ ++i;
+ ms->internal_ht_index = i;
+ }
+
+ /* if lineart get the value for threshold */
+ if ( ms->mode == MS_MODE_LINEART || ms->mode == MS_MODE_LINEARTFAKE)
+ ms->threshold = (uint8_t) ms->val[OPT_THRESHOLD].w;
+ else
+ ms->threshold = (uint8_t) M_THRESHOLD_DEFAULT;
+
+ DBG(30, "get_scan_parameters: mode=%d, depth=%d, bpp_in=%d, bpp_out=%d\n",
+ ms->mode, ms->depth, ms->bits_per_pixel_in,
+ ms->bits_per_pixel_out);
+
+ /* calculate positions, width and height in dots */
+ /* check for impossible values */
+ /* ensure a minimum scan area of 10 x 10 pixels */
+ dpm = (double) mi->opt_resolution / MM_PER_INCH;
+ ms->x1_dots = (SANE_Int) ( SANE_UNFIX(ms->val[OPT_TL_X].w) * dpm + 0.5 );
+ if ( ms->x1_dots > ( mi->geo_width - 10 ) )
+ ms->x1_dots = ( mi->geo_width - 10 );
+ ms->y1_dots = (SANE_Int) ( SANE_UNFIX(ms->val[OPT_TL_Y].w) * dpm + 0.5 );
+ if ( ms->y1_dots > ( mi->geo_height - 10 ) )
+ ms->y1_dots = ( mi->geo_height - 10 );
+ x2_dots = (int) ( SANE_UNFIX(ms->val[OPT_BR_X].w) * dpm + 0.5 );
+ if ( x2_dots >= mi->geo_width )
+ x2_dots = mi->geo_width - 1;
+ y2_dots = (int) ( SANE_UNFIX(ms->val[OPT_BR_Y].w) * dpm + 0.5 );
+ if ( y2_dots >= mi->geo_height )
+ y2_dots = mi->geo_height - 1;
+ ms->width_dots = x2_dots - ms->x1_dots;
+ if ( md->model_flags && MD_OFFSET_2 ) /* this firmware has problems with */
+ if ( ( ms->width_dots % 2 ) == 1 ) /* odd pixel numbers */
+ ms->width_dots -= 1;
+ if ( ms->width_dots < 10 )
+ ms->width_dots = 10;
+ ms->height_dots = y2_dots - ms->y1_dots;
+ if ( ms->height_dots < 10 )
+ ms->height_dots = 10;
+
+/*test!!!*/
+/* ms->y1_dots -= 50;*/
+
+ /* take scanning direction into account */
+ if ((mi->direction & MI_DATSEQ_RTOL) == 1)
+ ms->x1_dots = mi->geo_width - ms->x1_dots - ms->width_dots;
+
+ if ( ms->val[OPT_RESOLUTION_BIND].w == SANE_TRUE )
+ {
+ ms->x_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5);
+ ms->y_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5);
+ }
+ else
+ {
+ ms->x_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5);
+ ms->y_resolution_dpi =
+ (SANE_Int) (SANE_UNFIX(ms->val[OPT_Y_RESOLUTION].w) + 0.5);
+ }
+
+ if ( ms->x_resolution_dpi < 10 )
+ ms->x_resolution_dpi = 10;
+ if ( ms->y_resolution_dpi < 10 )
+ ms->y_resolution_dpi = 10;
+
+ DBG(30, "get_scan_parameters: yres=%d, x1=%d, width=%d, y1=%d, height=%d\n",
+ ms->y_resolution_dpi, ms->x1_dots, ms->width_dots,
+ ms->y1_dots, ms->height_dots);
+
+ /* Preview mode */
+ if ( ms->val[OPT_PREVIEW].w == SANE_TRUE )
+ {
+ ms->fastscan = SANE_TRUE;
+ ms->quality = SANE_FALSE;
+ }
+ else
+ {
+ ms->fastscan = SANE_FALSE;
+ ms->quality = SANE_TRUE;
+ }
+
+ ms->rawdat = 0;
+
+ /* brightness, contrast, values 1,..,255 */
+ ms->brightness_m = (uint8_t) (SANE_UNFIX(ms->val[OPT_BRIGHTNESS].w)
+ / SANE_UNFIX(md->percentage_range.max) * 254.0) + 1;
+ ms->brightness_r = ms->brightness_g = ms->brightness_b = ms->brightness_m;
+
+ ms->contrast_m = (uint8_t) (SANE_UNFIX(ms->val[OPT_CONTRAST].w)
+ / SANE_UNFIX(md->percentage_range.max) * 254.0) + 1;
+ ms->contrast_r = ms->contrast_g = ms->contrast_b = ms->contrast_m;
+
+ /* shadow, midtone, highlight, exposure */
+ ms->shadow_m = (uint8_t) ms->val[OPT_SHADOW].w;
+ ms->shadow_r = (uint8_t) ms->val[OPT_SHADOW_R].w;
+ ms->shadow_g = (uint8_t) ms->val[OPT_SHADOW_G].w;
+ ms->shadow_b = (uint8_t) ms->val[OPT_SHADOW_B].w;
+ ms->midtone_m = (uint8_t) ms->val[OPT_MIDTONE].w;
+ ms->midtone_r = (uint8_t) ms->val[OPT_MIDTONE_R].w;
+ ms->midtone_g = (uint8_t) ms->val[OPT_MIDTONE_G].w;
+ ms->midtone_b = (uint8_t) ms->val[OPT_MIDTONE_B].w;
+ ms->highlight_m = (uint8_t) ms->val[OPT_HIGHLIGHT].w;
+ ms->highlight_r = (uint8_t) ms->val[OPT_HIGHLIGHT_R].w;
+ ms->highlight_g = (uint8_t) ms->val[OPT_HIGHLIGHT_G].w;
+ ms->highlight_b = (uint8_t) ms->val[OPT_HIGHLIGHT_B].w;
+ ms->exposure_m = (uint8_t) (ms->val[OPT_EXPOSURE].w / 2);
+ ms->exposure_r = (uint8_t) (ms->val[OPT_EXPOSURE_R].w / 2);
+ ms->exposure_g = (uint8_t) (ms->val[OPT_EXPOSURE_G].w / 2);
+ ms->exposure_b = (uint8_t) (ms->val[OPT_EXPOSURE_B].w / 2);
+
+ ms->gamma_mode = strdup( (char *) ms->val[OPT_GAMMA_MODE].s);
+
+ ms->balance[0] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_R].w));
+ ms->balance[1] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_G].w));
+ ms->balance[2] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_B].w));
+ DBG(255, "get_scan_parameters:ms->balance[0]=%d,[1]=%d,[2]=%d\n",
+ ms->balance[0], ms->balance[1], ms->balance[2]);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- get_scan_mode_and_depth() ---------------------------------------*/
+
+static SANE_Status
+get_scan_mode_and_depth(Microtek2_Scanner *ms,
+ int *mode,
+ int *depth,
+ int *bits_per_pixel_in,
+ int *bits_per_pixel_out)
+{
+ /* This function translates the strings for the possible modes and */
+ /* bitdepth into a more conveniant format as needed for SET WINDOW. */
+ /* bits_per_pixel is the number of bits per color one pixel needs */
+ /* when transferred from the the scanner, bits_perpixel_out is the */
+ /* number of bits per color one pixel uses when transferred to the */
+ /* frontend. These may be different. For example, with a depth of 4 */
+ /* two pixels per byte are transferred from the scanner, but only one */
+ /* pixel per byte is transferred to the frontend. */
+ /* If lineart_fake is set to !=0, we need the parameters for a */
+ /* grayscale scan, because the scanner has no lineart mode */
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+ DBG(30, "get_scan_mode_and_depth: handle=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 )
+ *mode = MS_MODE_COLOR;
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ *mode = MS_MODE_GRAY;
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0)
+ *mode = MS_MODE_HALFTONE;
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 )
+ {
+ if ( MI_LINEART_NONE(mi->scanmode)
+ || ms->val[OPT_AUTOADJUST].w == SANE_TRUE
+ || md->model_flags & MD_READ_CONTROL_BIT)
+ *mode = MS_MODE_LINEARTFAKE;
+ else
+ *mode = MS_MODE_LINEART;
+ }
+ else
+ {
+ DBG(1, "get_scan_mode_and_depth: Unknown mode %s\n",
+ ms->val[OPT_MODE].s);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0
+ || strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 )
+ {
+ if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_16 )
+ {
+ *depth = 16;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_14 )
+ {
+ *depth = 14;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_12 )
+ {
+ *depth = 12;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_10 )
+ {
+ *depth = 10;
+ *bits_per_pixel_in = *bits_per_pixel_out = 16;
+ }
+ else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_8 )
+ {
+ *depth = 8;
+ *bits_per_pixel_in = *bits_per_pixel_out = 8;
+ }
+ else if ( ms->val[OPT_MODE].w == MD_DEPTHVAL_4 )
+ {
+ *depth = 4;
+ *bits_per_pixel_in = 4;
+ *bits_per_pixel_out = 8;
+ }
+ }
+ else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 )
+ {
+ *depth = 1;
+ *bits_per_pixel_in = *bits_per_pixel_out = 1;
+ }
+ else /* lineart */
+ {
+ *bits_per_pixel_out = 1;
+ if ( *mode == MS_MODE_LINEARTFAKE )
+ {
+ *depth = 8;
+ *bits_per_pixel_in = 8;
+ }
+ else
+ {
+ *depth = 1;
+ *bits_per_pixel_in = 1;
+ }
+ }
+
+#if 0
+ if ( ms->val[OPT_PREVIEW].w == SANE_TRUE )
+ {
+ if ( *depth > 8 )
+ {
+ *depth = 8;
+ *bits_per_pixel_in = *bits_per_pixel_out = 8;
+ }
+ }
+#endif
+
+ DBG(30, "get_scan_mode_and_depth: mode=%d, depth=%d,"
+ " bits_pp_in=%d, bits_pp_out=%d, preview=%d\n",
+ *mode, *depth, *bits_per_pixel_in, *bits_per_pixel_out,
+ ms->val[OPT_PREVIEW].w);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_wait_for_image() -------------------------------------------*/
+
+static SANE_Status
+scsi_wait_for_image(Microtek2_Scanner *ms)
+{
+ int retry = 60;
+ SANE_Status status;
+
+
+ DBG(30, "scsi_wait_for_image: ms=%p\n", (void *) ms);
+
+ while ( retry-- > 0 )
+ {
+ status = scsi_read_image_status(ms);
+ if (status == SANE_STATUS_DEVICE_BUSY )
+ {
+ sleep(1);
+ continue;
+ }
+ if ( status == SANE_STATUS_GOOD )
+ return status;
+
+ /* status != GOOD && != BUSY */
+ DBG(1, "scsi_wait_for_image: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ /* BUSY after n retries */
+ DBG(1, "scsi_wait_for_image: '%s'\n", sane_strstatus(status));
+ return status;
+}
+
+
+/*---------- scsi_read_gamma() -----------------------------------------------*/
+
+/* currently not used */
+/*
+static SANE_Status
+scsi_read_gamma(Microtek2_Scanner *ms, int color)
+{
+ uint8_t readgamma[RG_CMD_L];
+ uint8_t result[3072];
+ size_t size;
+ SANE_Bool endiantype;
+ SANE_Status status;
+
+ RG_CMD(readgamma);
+ ENDIAN_TYPE(endiantype);
+ RG_PCORMAC(readgamma, endiantype);
+ RG_COLOR(readgamma, color);
+ RG_WORD(readgamma, ( ms->dev->lut_entry_size == 1 ) ? 0 : 1);
+ RG_TRANSFERLENGTH(readgamma, (color == 3 ) ? 3072 : 1024);
+
+ dump_area(readgamma, 10, "ReadGamma");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(ms->sfd, readgamma, sizeof(readgamma),
+ result, &size);
+ if ( status != SANE_STATUS_GOOD ) {
+ DBG(1, "scsi_read_gamma: (L,R) read_gamma failed: status '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ dump_area(result, 3072, "Result");
+
+ return SANE_STATUS_GOOD;
+}
+*/
+
+
+/*---------- scsi_send_gamma() -----------------------------------------------*/
+
+static SANE_Status
+scsi_send_gamma(Microtek2_Scanner *ms)
+{
+ SANE_Bool endiantype;
+ SANE_Status status;
+ size_t size;
+ uint8_t *cmd, color;
+
+
+ DBG(30, "scsi_send_gamma: pos=%p, size=%d, word=%d, color=%d\n",
+ ms->gamma_table, ms->lut_size_bytes, ms->word, ms->current_color);
+
+ if ( ( 3 * ms->lut_size_bytes ) <= 0xffff ) /*send Gamma with one command*/
+ {
+ cmd = (uint8_t *) alloca(SG_CMD_L + 3 * ms->lut_size_bytes);
+ if ( cmd == NULL )
+ {
+ DBG(1, "scsi_send_gamma: Couldn't get buffer for gamma table\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ SG_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ SG_SET_PCORMAC(cmd, endiantype);
+ SG_SET_COLOR(cmd, ms->current_color);
+ SG_SET_WORD(cmd, ms->word);
+ SG_SET_TRANSFERLENGTH(cmd, 3 * ms->lut_size_bytes);
+ memcpy(cmd + SG_CMD_L, ms->gamma_table, 3 * ms->lut_size_bytes);
+ size = 3 * ms->lut_size_bytes;
+ if ( md_dump >= 2 )
+ dump_area2(cmd, SG_CMD_L, "sendgammacmd");
+ if ( md_dump >= 3 )
+ dump_area2(cmd + SG_CMD_L, size, "sendgammadata");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, size + SG_CMD_L, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_gamma: '%s'\n", sane_strstatus(status));
+ }
+
+ else /* send gamma with 3 commands, one for each color */
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ cmd = (uint8_t *) alloca(SG_CMD_L + ms->lut_size_bytes);
+ if ( cmd == NULL )
+ {
+ DBG(1, "scsi_send_gamma: Couldn't get buffer for gamma table\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ SG_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ SG_SET_PCORMAC(cmd, endiantype);
+ SG_SET_COLOR(cmd, color);
+ SG_SET_WORD(cmd, ms->word);
+ SG_SET_TRANSFERLENGTH(cmd, ms->lut_size_bytes);
+ memcpy(cmd + SG_CMD_L,
+ ms->gamma_table + color * ms->lut_size_bytes,
+ ms->lut_size_bytes);
+ size = ms->lut_size_bytes;
+ if ( md_dump >= 2 )
+ dump_area2(cmd, SG_CMD_L, "sendgammacmd");
+ if ( md_dump >= 3 )
+ dump_area2(cmd + SG_CMD_L, size, "sendgammadata");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, size + SG_CMD_L, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_gamma: '%s'\n", sane_strstatus(status));
+ }
+
+ }
+
+ return status;
+}
+
+
+/*---------- scsi_inquiry() --------------------------------------------------*/
+
+static SANE_Status
+scsi_inquiry(Microtek2_Info *mi, char *device)
+{
+ SANE_Status status;
+ uint8_t cmd[INQ_CMD_L];
+ uint8_t *result;
+ uint8_t inqlen;
+ size_t size;
+ int sfd;
+
+
+ DBG(30, "scsi_inquiry: mi=%p, device='%s'\n", (void *) mi, device);
+
+ status = sanei_scsi_open(device, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_inquiry: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ INQ_CMD(cmd);
+ INQ_SET_ALLOC(cmd, INQ_ALLOC_L);
+ result = (uint8_t *) alloca(INQ_ALLOC_L);
+ if ( result == NULL )
+ {
+ DBG(1, "scsi_inquiry: malloc failed\n");
+ sanei_scsi_close(sfd);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ size = INQ_ALLOC_L;
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_inquiry: '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+
+ INQ_GET_INQLEN(inqlen, result);
+ INQ_SET_ALLOC(cmd, inqlen + INQ_ALLOC_L);
+ result = alloca(inqlen + INQ_ALLOC_L);
+ if ( result == NULL )
+ {
+ DBG(1, "scsi_inquiry: malloc failed\n");
+ sanei_scsi_close(sfd);
+ return SANE_STATUS_NO_MEM;
+ }
+ size = inqlen + INQ_ALLOC_L;
+ if (md_dump >= 2 )
+ dump_area2(cmd, sizeof(cmd), "inquiry");
+
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_inquiry: cmd '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+ sanei_scsi_close(sfd);
+
+ if (md_dump >= 2 )
+ {
+ dump_area2((uint8_t *) result, size, "inquiryresult");
+ dump_area((uint8_t *) result, size, "inquiryresult");
+ }
+
+ /* copy results */
+ INQ_GET_QUAL(mi->device_qualifier, result);
+ INQ_GET_DEVT(mi->device_type, result);
+ INQ_GET_VERSION(mi->scsi_version, result);
+ INQ_GET_VENDOR(mi->vendor, (char *)result);
+ INQ_GET_MODEL(mi->model, (char *)result);
+ INQ_GET_REV(mi->revision, (char *)result);
+ INQ_GET_MODELCODE(mi->model_code, result);
+
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_read_attributes() ------------------------------------------*/
+
+static SANE_Status
+scsi_read_attributes(Microtek2_Info *pmi, char *device, uint8_t scan_source)
+{
+ SANE_Status status;
+ Microtek2_Info *mi;
+ uint8_t readattributes[RSA_CMD_L];
+ uint8_t result[RSA_TRANSFERLENGTH];
+ size_t size;
+ int sfd;
+
+
+ mi = &pmi[scan_source];
+
+ DBG(30, "scsi_read_attributes: mi=%p, device='%s', source=%d\n",
+ (void *) mi, device, scan_source);
+
+ RSA_CMD(readattributes);
+ RSA_SETMEDIA(readattributes, scan_source);
+ status = sanei_scsi_open(device, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_attributes: open '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if (md_dump >= 2 )
+ dump_area2(readattributes, sizeof(readattributes), "scannerattributes");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(sfd, readattributes,
+ sizeof(readattributes), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_attributes: cmd '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+
+ sanei_scsi_close(sfd);
+
+ /* The X6 appears to lie about the data format for a TMA */
+ if ( (&pmi[0])->model_code == 0x91 )
+ result[0] &= 0xfd;
+ /* default value for calib_divisor ... bit49?? */
+ mi->calib_divisor = 1;
+ /* 9600XL */
+ if ( (&pmi[0])->model_code == 0xde )
+ mi->calib_divisor = 2;
+ /* 6400XL has problems in lineart mode*/
+ if ( (&pmi[0])->model_code == 0x89 )
+ result[13] &= 0xfe; /* simulate no lineart */
+#if 0
+ result[13] &= 0xfe; /* simulate no lineart */
+#endif
+
+ /* copy all the stuff into the info structure */
+ RSA_COLOR(mi->color, result);
+ RSA_ONEPASS(mi->onepass, result);
+ RSA_SCANNERTYPE(mi->scanner_type, result);
+ RSA_FEPROM(mi->feprom, result);
+ RSA_DATAFORMAT(mi->data_format, result);
+ RSA_COLORSEQUENCE(mi->color_sequence, result);
+ RSA_NIS(mi->new_image_status, result);
+ RSA_DATSEQ(mi->direction, result);
+ RSA_CCDGAP(mi->ccd_gap, result);
+ RSA_MAX_XRESOLUTION(mi->max_xresolution, result);
+ RSA_MAX_YRESOLUTION(mi->max_yresolution, result);
+ RSA_GEOWIDTH(mi->geo_width, result);
+ RSA_GEOHEIGHT(mi->geo_height, result);
+ RSA_OPTRESOLUTION(mi->opt_resolution, result);
+ RSA_DEPTH(mi->depth, result);
+ /* The X12USL doesn't say that it has 14bit */
+ if ( (&pmi[0])->model_code == 0xb0 )
+ mi->depth |= MI_HASDEPTH_14;
+ RSA_SCANMODE(mi->scanmode, result);
+ RSA_CCDPIXELS(mi->ccd_pixels, result);
+ RSA_LUTCAP(mi->lut_cap, result);
+ RSA_DNLDPTRN(mi->has_dnldptrn, result);
+ RSA_GRAINSLCT(mi->grain_slct, result);
+ RSA_SUPPOPT(mi->option_device, result);
+ RSA_CALIBWHITE(mi->calib_white, result);
+ RSA_CALIBSPACE(mi->calib_space, result);
+ RSA_NLENS(mi->nlens, result);
+ RSA_NWINDOWS(mi->nwindows, result);
+ RSA_SHTRNSFEREQU(mi->shtrnsferequ, result);
+ RSA_SCNBTTN(mi->scnbuttn, result);
+ RSA_BUFTYPE(mi->buftype, result);
+ RSA_REDBALANCE(mi->balance[0], result);
+ RSA_GREENBALANCE(mi->balance[1], result);
+ RSA_BLUEBALANCE(mi->balance[2], result);
+ RSA_APSMAXFRAMES(mi->aps_maxframes, result);
+
+ if (md_dump >= 2 )
+ dump_area2((uint8_t *) result, sizeof(result),
+ "scannerattributesresults");
+ if ( md_dump >= 1 && md_dump_clear )
+ dump_attributes(mi);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_read_control_bits() ----------------------------------------*/
+
+static SANE_Status
+scsi_read_control_bits(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ SANE_Status status;
+ uint8_t cmd[RCB_CMD_L];
+ uint32_t byte;
+ int bit;
+ int count_1s;
+
+ md = ms->dev;
+
+ DBG(30, "scsi_read_control_bits: ms=%p, fd=%d\n", (void *) ms, ms->sfd);
+ DBG(30, "ms->control_bytes = %p\n", ms->control_bytes);
+
+ RCB_SET_CMD(cmd);
+ RCB_SET_LENGTH(cmd, ms->n_control_bytes);
+
+ if ( md_dump >= 2)
+ dump_area2(cmd, RCB_CMD_L, "readcontrolbits");
+
+ status = sanei_scsi_cmd(ms->sfd,
+ cmd,
+ sizeof(cmd),
+ ms->control_bytes,
+ &ms->n_control_bytes);
+
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_control_bits: cmd '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2)
+ dump_area2(ms->control_bytes,
+ ms->n_control_bytes,
+ "readcontrolbitsresult");
+
+ count_1s = 0;
+ for ( byte = 0; byte < ms->n_control_bytes; byte++ )
+ {
+ for ( bit = 0; bit < 8; bit++ )
+ {
+ if ( (ms->control_bytes[byte] >> bit) & 0x01 )
+ ++count_1s;
+ }
+ }
+ DBG(20, "read_control_bits: number of 1's in controlbytes: %d\n", count_1s);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_set_window() -----------------------------------------------*/
+
+static SANE_Status
+scsi_set_window(Microtek2_Scanner *ms, int n) { /* n windows, not yet */
+ /* implemented */
+ SANE_Status status;
+ uint8_t *setwindow;
+ int size;
+
+
+ DBG(30, "scsi_set_window: ms=%p, wnd=%d\n", (void *) ms, n);
+
+ size = SW_CMD_L + SW_HEADER_L + n * SW_BODY_L;
+ setwindow = (uint8_t *) malloc(size);
+ DBG(100, "scsi_set_window: setwindow= %p, malloc'd %d Bytes\n",
+ setwindow, size);
+ if ( setwindow == NULL )
+ {
+ DBG(1, "scsi_set_window: malloc for setwindow failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(setwindow, 0, size);
+
+ SW_CMD(setwindow);
+ SW_PARAM_LENGTH(setwindow, SW_HEADER_L + n * SW_BODY_L);
+ SW_WNDDESCLEN(setwindow + SW_HEADER_P, SW_WNDDESCVAL);
+
+#define POS (setwindow + SW_BODY_P(n-1))
+
+ SW_WNDID(POS, n-1);
+ SW_XRESDPI(POS, ms->x_resolution_dpi);
+ SW_YRESDPI(POS, ms->y_resolution_dpi);
+ SW_XPOSTL(POS, ms->x1_dots);
+ SW_YPOSTL(POS, ms->y1_dots);
+ SW_WNDWIDTH(POS, ms->width_dots);
+ SW_WNDHEIGHT(POS, ms->height_dots);
+ SW_THRESHOLD(POS, ms->threshold);
+ SW_IMGCOMP(POS, ms->mode);
+ SW_BITSPERPIXEL(POS, ms->depth);
+ SW_EXTHT(POS, ms->use_external_ht);
+ SW_INTHTINDEX(POS, ms->internal_ht_index);
+ SW_RIF(POS, 1);
+ SW_LENS(POS, 0); /* ???? */
+ SW_INFINITE(POS, 0);
+ SW_STAY(POS, ms->stay);
+ SW_RAWDAT(POS, ms->rawdat);
+ SW_QUALITY(POS, ms->quality);
+ SW_FASTSCAN(POS, ms->fastscan);
+ SW_MEDIA(POS, ms->scan_source);
+ SW_BRIGHTNESS_M(POS, ms->brightness_m);
+ SW_CONTRAST_M(POS, ms->contrast_m);
+ SW_EXPOSURE_M(POS, ms->exposure_m);
+ SW_SHADOW_M(POS, ms->shadow_m);
+ SW_MIDTONE_M(POS, ms->midtone_m);
+ SW_HIGHLIGHT_M(POS, ms->highlight_m);
+ /* the following properties are only referenced if it's a color scan */
+ /* but I guess they don't matter at a gray scan */
+ SW_BRIGHTNESS_R(POS, ms->brightness_r);
+ SW_CONTRAST_R(POS, ms->contrast_r);
+ SW_EXPOSURE_R(POS, ms->exposure_r);
+ SW_SHADOW_R(POS, ms->shadow_r);
+ SW_MIDTONE_R(POS, ms->midtone_r);
+ SW_HIGHLIGHT_R(POS, ms->highlight_r);
+ SW_BRIGHTNESS_G(POS, ms->brightness_g);
+ SW_CONTRAST_G(POS, ms->contrast_g);
+ SW_EXPOSURE_G(POS, ms->exposure_g);
+ SW_SHADOW_G(POS, ms->shadow_g);
+ SW_MIDTONE_G(POS, ms->midtone_g);
+ SW_HIGHLIGHT_G(POS, ms->highlight_g);
+ SW_BRIGHTNESS_B(POS, ms->brightness_b);
+ SW_CONTRAST_B(POS, ms->contrast_b);
+ SW_EXPOSURE_B(POS, ms->exposure_b);
+ SW_SHADOW_B(POS, ms->shadow_b);
+ SW_MIDTONE_B(POS, ms->midtone_b);
+ SW_HIGHLIGHT_B(POS, ms->highlight_b);
+
+ if ( md_dump >= 2 )
+ {
+ dump_area2(setwindow, 10, "setwindowcmd");
+ dump_area2(setwindow + 10 ,8 , "setwindowheader");
+ dump_area2(setwindow + 18 ,61 , "setwindowbody");
+ }
+
+ status = sanei_scsi_cmd(ms->sfd, setwindow, size, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_set_window: '%s'\n", sane_strstatus(status));
+
+ DBG(100, "scsi_set_window: free setwindow at %p\n", setwindow);
+ free((void *) setwindow);
+ return status;
+}
+
+
+/*---------- scsi_read_image_info() ------------------------------------------*/
+
+static SANE_Status
+scsi_read_image_info(Microtek2_Scanner *ms)
+{
+ uint8_t cmd[RII_CMD_L];
+ uint8_t result[RII_RESULT_L];
+ size_t size;
+ SANE_Status status;
+ Microtek2_Device *md;
+
+ md = ms->dev;
+
+ DBG(30, "scsi_read_image_info: ms=%p\n", (void *) ms);
+
+ RII_SET_CMD(cmd);
+
+ if ( md_dump >= 2)
+ dump_area2(cmd, RII_CMD_L, "readimageinfo");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), result, &size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_image_info: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2)
+ dump_area2(result, size, "readimageinforesult");
+
+ /* The V300 returns some values in only two bytes */
+ if ( !(md->revision==2.70) && (md->model_flags & MD_RII_TWO_BYTES) )
+ {
+ RII_GET_V300_WIDTHPIXEL(ms->ppl, result);
+ RII_GET_V300_WIDTHBYTES(ms->bpl, result);
+ RII_GET_V300_HEIGHTLINES(ms->src_remaining_lines, result);
+ RII_GET_V300_REMAINBYTES(ms->remaining_bytes, result);
+ }
+ else
+ {
+ RII_GET_WIDTHPIXEL(ms->ppl, result);
+ RII_GET_WIDTHBYTES(ms->bpl, result);
+ RII_GET_HEIGHTLINES(ms->src_remaining_lines, result);
+ RII_GET_REMAINBYTES(ms->remaining_bytes, result);
+ }
+
+ DBG(30, "scsi_read_image_info: ppl=%d, bpl=%d, lines=%d, remain=%d\n",
+ ms->ppl, ms->bpl, ms->src_remaining_lines, ms->remaining_bytes);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_read_image() -----------------------------------------------*/
+
+static SANE_Status
+scsi_read_image(Microtek2_Scanner *ms, uint8_t *buffer, int bytes_per_pixel)
+{
+ uint8_t cmd[RI_CMD_L];
+ SANE_Bool endiantype;
+ SANE_Status status;
+ size_t size;
+ size_t i;
+ uint8_t tmp;
+
+
+ DBG(30, "scsi_read_image: ms=%p, buffer=%p\n", (void *) ms, buffer);
+
+ ENDIAN_TYPE(endiantype)
+ RI_SET_CMD(cmd);
+ RI_SET_PCORMAC(cmd, endiantype);
+ RI_SET_COLOR(cmd, ms->current_read_color);
+ RI_SET_TRANSFERLENGTH(cmd, ms->transfer_length);
+
+ DBG(30, "scsi_read_image: transferlength=%d\n", ms->transfer_length);
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, RI_CMD_L, "readimagecmd");
+
+ size = ms->transfer_length;
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), buffer, &size);
+
+ if ( buffer && ( ms->dev->model_flags & MD_PHANTOM_C6 ) && endiantype )
+ {
+ switch(bytes_per_pixel)
+ {
+ case 1: break;
+ case 2:
+ for ( i = 1; i < size; i += 2 )
+ {
+ tmp = buffer[i-1];
+ buffer[i-1] = buffer[i];
+ buffer[i] = tmp;
+ }
+ break;
+ default:
+ DBG(1, "scsi_read_image: Unexpected bytes_per_pixel=%d\n", bytes_per_pixel);
+ }
+ }
+
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_read_image: '%s'\n", sane_strstatus(status));
+
+ if ( md_dump > 3 )
+ dump_area2(buffer, ms->transfer_length, "readimageresult");
+
+ return status;
+}
+
+
+/*---------- scsi_read_image_status() ----------------------------------------*/
+
+static SANE_Status
+scsi_read_image_status(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint8_t cmd[RIS_CMD_L];
+ uint8_t dummy;
+ size_t dummy_length;
+ SANE_Status status;
+ SANE_Bool endian_type;
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ DBG(30, "scsi_read_image_status: ms=%p\n", (void *) ms);
+
+ ENDIAN_TYPE(endian_type)
+ RIS_SET_CMD(cmd);
+ RIS_SET_PCORMAC(cmd, endian_type);
+ RIS_SET_COLOR(cmd, ms->current_read_color);
+
+/* mi->new_image_status = SANE_TRUE; */ /* for testing*/
+
+ if ( mi->new_image_status == SANE_TRUE )
+ {
+ DBG(30, "scsi_read_image_status: use new image status \n");
+ dummy_length = 1;
+ cmd[8] = 1;
+ }
+ else
+ {
+ DBG(30, "scsi_read_image_status: use old image status \n");
+ dummy_length = 0;
+ cmd[8] = 0;
+ }
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, sizeof(cmd), "readimagestatus");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), &dummy, &dummy_length);
+
+ if ( mi->new_image_status == SANE_TRUE )
+ {
+ if ( dummy == 0 )
+ status = SANE_STATUS_GOOD;
+ else
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ /* For some (X6USB) scanner
+ We say we are going to try to read 1 byte of data (as recommended
+ in the Microtek SCSI command documentation under "New Image Status")
+ so that dubious SCSI host adapters (like the one in at least some
+ Microtek X6 USB scanners) don't get wedged trying to do a zero
+ length read. However, we do not actually try to read this byte of
+ data, as that wedges the USB scanner as well.
+ IOW the SCSI command says we are going to read 1 byte, but in fact
+ we don't: */
+ /*cmd[8] = 1;
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), &dummy, 0); */
+
+
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_read_image_status: '%s'\n", sane_strstatus(status));
+
+ return status;
+}
+
+/*---------- scsi_read_shading () --------------------------------------------*/
+
+static SANE_Status
+scsi_read_shading(Microtek2_Scanner *ms, uint8_t *buffer, uint32_t length)
+{
+ Microtek2_Device *md;
+ uint8_t cmd[RSI_CMD_L];
+ SANE_Bool endiantype;
+ SANE_Status status = SANE_STATUS_GOOD;
+ size_t size;
+
+ DBG(30, "scsi_read_shading: pos=%p, size=%d, word=%d, color=%d, dark=%d\n",
+ buffer, length, ms->word, ms->current_color, ms->dark);
+
+ md = ms->dev;
+
+ size = length;
+
+ RSI_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ RSI_SET_PCORMAC(cmd, endiantype);
+ RSI_SET_COLOR(cmd, ms->current_color);
+ RSI_SET_DARK(cmd, ms->dark);
+ RSI_SET_WORD(cmd, ms->word);
+ RSI_SET_TRANSFERLENGTH(cmd, size);
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, RSI_CMD_L, "readshading");
+
+ DBG(100, "scsi_read_shading: sfd=%d, cmd=%p, sizeofcmd=%lu,"
+ "dest=%p, destsize=%lu\n",
+ ms->sfd, cmd, (u_long) sizeof(cmd), buffer, (u_long) size);
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), buffer, &size);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_read_shading: '%s'\n", sane_strstatus(status));
+
+ if ( md_dump > 3)
+ dump_area2(buffer,
+ size,
+ "readshadingresult");
+
+ return status;
+}
+
+
+/*---------- scsi_send_shading () --------------------------------------------*/
+
+static SANE_Status
+scsi_send_shading(Microtek2_Scanner *ms,
+ uint8_t *shading_data,
+ uint32_t length,
+ uint8_t dark)
+{
+ SANE_Bool endiantype;
+ SANE_Status status;
+ size_t size;
+ uint8_t *cmd;
+
+
+ DBG(30, "scsi_send_shading: pos=%p, size=%d, word=%d, color=%d, dark=%d\n",
+ shading_data, length, ms->word, ms->current_color,
+ dark);
+
+ cmd = (uint8_t *) malloc(SSI_CMD_L + length);
+ DBG(100, "scsi_send_shading: cmd=%p, malloc'd %d bytes\n",
+ cmd, SSI_CMD_L + length);
+ if ( cmd == NULL )
+ {
+ DBG(1, "scsi_send_shading: Couldn't get buffer for shading table\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ SSI_SET_CMD(cmd);
+ ENDIAN_TYPE(endiantype)
+ SSI_SET_PCORMAC(cmd, endiantype);
+ SSI_SET_COLOR(cmd, ms->current_color);
+ SSI_SET_DARK(cmd, dark);
+ SSI_SET_WORD(cmd, ms->word);
+ SSI_SET_TRANSFERLENGTH(cmd, length);
+ memcpy(cmd + SSI_CMD_L, shading_data, length);
+ size = length;
+
+ if ( md_dump >= 2 )
+ dump_area2(cmd, SSI_CMD_L, "sendshading");
+ if ( md_dump >= 3 )
+ dump_area2(cmd + SSI_CMD_L, size, "sendshadingdata");
+
+ status = sanei_scsi_cmd(ms->sfd, cmd, size + SSI_CMD_L, NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_shading: '%s'\n", sane_strstatus(status));
+
+ DBG(100, "free cmd at %p\n", cmd);
+ free((void *) cmd);
+
+ return status;
+
+}
+
+
+/*---------- scsi_read_system_status() ---------------------------------------*/
+
+static SANE_Status
+scsi_read_system_status(Microtek2_Device *md, int fd)
+{
+ uint8_t cmd[RSS_CMD_L];
+ uint8_t result[RSS_RESULT_L];
+ int sfd;
+ size_t size;
+ SANE_Status status;
+
+ DBG(30, "scsi_read_system_status: md=%p, fd=%d\n", (void *) md, fd);
+
+ if ( fd == -1 )
+ {
+ status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_system_status: open '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+ }
+ else
+ sfd = fd;
+
+ RSS_CMD(cmd);
+
+ if ( md_dump >= 2)
+ dump_area2(cmd, RSS_CMD_L, "readsystemstatus");
+
+ size = sizeof(result);
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size);
+
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_read_system_status: cmd '%s'\n", sane_strstatus(status));
+ sanei_scsi_close(sfd);
+ return status;
+ }
+
+ if ( fd == -1 )
+ sanei_scsi_close(sfd);
+
+ if ( md_dump >= 2)
+ dump_area2(result, size, "readsystemstatusresult");
+
+ md->status.sskip = RSS_SSKIP(result);
+ md->status.ntrack = RSS_NTRACK(result);
+ md->status.ncalib = RSS_NCALIB(result);
+ md->status.tlamp = RSS_TLAMP(result);
+ md->status.flamp = RSS_FLAMP(result);
+ md->status.rdyman= RSS_RDYMAN(result);
+ md->status.trdy = RSS_TRDY(result);
+ md->status.frdy = RSS_FRDY(result);
+ md->status.adp = RSS_RDYMAN(result);
+ md->status.detect = RSS_DETECT(result);
+ md->status.adptime = RSS_ADPTIME(result);
+ md->status.lensstatus = RSS_LENSSTATUS(result);
+ md->status.aloff = RSS_ALOFF(result);
+ md->status.timeremain = RSS_TIMEREMAIN(result);
+ md->status.tmacnt = RSS_TMACNT(result);
+ md->status.paper = RSS_PAPER(result);
+ md->status.adfcnt = RSS_ADFCNT(result);
+ md->status.currentmode = RSS_CURRENTMODE(result);
+ md->status.buttoncount = RSS_BUTTONCOUNT(result);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- scsi_request_sense() --------------------------------------------*/
+
+/* currently not used */
+
+#if 0
+
+static SANE_Status
+scsi_request_sense(Microtek2_Scanner *ms)
+{
+ uint8_t requestsense[RQS_CMD_L];
+ uint8_t buffer[100];
+ SANE_Status status;
+ int size;
+ int asl;
+ int as_info_length;
+
+ DBG(30, "scsi_request_sense: ms=%p\n", (void *) ms);
+
+ RQS_CMD(requestsense);
+ RQS_ALLOCLENGTH(requestsense, 100);
+
+ size = sizeof(buffer);
+ status = sanei_scsi_cmd(ms->sfd, requestsense, sizeof(requestsense),
+ buffer, &size);
+
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_request_sense: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2 )
+ dump_area2(buffer, size, "requestsenseresult");
+
+ dump_area(buffer, RQS_LENGTH(buffer), "RequestSense");
+ asl = RQS_ASL(buffer);
+ if ( (as_info_length = RQS_ASINFOLENGTH(buffer)) > 0 )
+ DBG(25, "scsi_request_sense: info '%.*s'\n",
+ as_info_length, RQS_ASINFO(buffer));
+
+ return SANE_STATUS_GOOD;
+}
+#endif
+
+
+/*---------- scsi_send_system_status() ---------------------------------------*/
+
+static SANE_Status
+scsi_send_system_status(Microtek2_Device *md, int fd)
+{
+ uint8_t cmd[SSS_CMD_L + SSS_DATA_L];
+ uint8_t *pos;
+ int sfd;
+ SANE_Status status;
+
+
+ DBG(30, "scsi_send_system_status: md=%p, fd=%d\n", (void *) md, fd);
+
+ memset(cmd, 0, SSS_CMD_L + SSS_DATA_L);
+ if ( fd == -1 )
+ {
+ status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_send_system_status: open '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+ }
+ else
+ sfd = fd;
+
+ SSS_CMD(cmd);
+ pos = cmd + SSS_CMD_L;
+ SSS_STICK(pos, md->status.stick);
+ SSS_NTRACK(pos, md->status.ntrack);
+ SSS_NCALIB(pos, md->status.ncalib);
+ SSS_TLAMP(pos, md->status.tlamp);
+ SSS_FLAMP(pos, md->status.flamp);
+ SSS_RESERVED17(pos, md->status.reserved17);
+ SSS_RDYMAN(pos, md->status.rdyman);
+ SSS_TRDY(pos, md->status.trdy);
+ SSS_FRDY(pos, md->status.frdy);
+ SSS_ADP(pos, md->status.adp);
+ SSS_DETECT(pos, md->status.detect);
+ SSS_ADPTIME(pos, md->status.adptime);
+ SSS_LENSSTATUS(pos, md->status.lensstatus);
+ SSS_ALOFF(pos, md->status.aloff);
+ SSS_TIMEREMAIN(pos, md->status.timeremain);
+ SSS_TMACNT(pos, md->status.tmacnt);
+ SSS_PAPER(pos, md->status.paper);
+ SSS_ADFCNT(pos, md->status.adfcnt);
+ SSS_CURRENTMODE(pos, md->status.currentmode);
+ SSS_BUTTONCOUNT(pos, md->status.buttoncount);
+
+ if ( md_dump >= 2)
+ {
+ dump_area2(cmd, SSS_CMD_L, "sendsystemstatus");
+ dump_area2(cmd + SSS_CMD_L, SSS_DATA_L, "sendsystemstatusdata");
+ }
+
+ status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_send_system_status: '%s'\n", sane_strstatus(status));
+
+ if ( fd == -1 )
+ sanei_scsi_close(sfd);
+ return status;
+}
+
+
+/*---------- scsi_sense_handler() --------------------------------------------*/
+/* rewritten 19.12.2001 for better SANE_STATUS return codes */
+
+static SANE_Status
+scsi_sense_handler (int fd, u_char *sense, void *arg)
+{
+ int as_info_length;
+ uint8_t sense_key;
+ uint8_t asl;
+ uint8_t asc;
+ uint8_t ascq;
+
+
+ DBG(30, "scsi_sense_handler: fd=%d, sense=%p arg=%p\n",fd, sense, arg);
+
+ dump_area(sense, RQS_LENGTH(sense), "SenseBuffer");
+
+ sense_key = RQS_SENSEKEY(sense);
+ asl = RQS_ASL(sense);
+ asc = RQS_ASC(sense);
+ ascq = RQS_ASCQ(sense);
+
+ DBG(5, "scsi_sense_handler: SENSE KEY (0x%02x), "
+ "ASC (0x%02x), ASCQ (0x%02x)\n", sense_key, asc, ascq);
+
+ if ( (as_info_length = RQS_ASINFOLENGTH(sense)) > 0 )
+ DBG(5,"scsi_sense_handler: info: '%*s'\n",
+ as_info_length, RQS_ASINFO(sense));
+
+ switch ( sense_key )
+ {
+ case RQS_SENSEKEY_NOSENSE:
+ return SANE_STATUS_GOOD;
+
+ case RQS_SENSEKEY_HWERR:
+ case RQS_SENSEKEY_ILLEGAL:
+ case RQS_SENSEKEY_VENDOR:
+ if ( asc == 0x4a && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Command phase error\n");
+ else if ( asc == 0x2c && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Command sequence error\n");
+ else if ( asc == 0x4b && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Data phase error\n");
+ else if ( asc == 0x40 )
+ {
+ DBG(5, "scsi_sense_handler: Hardware diagnostic failure:\n");
+ switch ( ascq )
+ {
+ case RQS_ASCQ_CPUERR:
+ DBG(5, "scsi_sense_handler: CPU error\n");
+ break;
+ case RQS_ASCQ_SRAMERR:
+ DBG(5, "scsi_sense_handler: SRAM error\n");
+ break;
+ case RQS_ASCQ_DRAMERR:
+ DBG(5, "scsi_sense_handler: DRAM error\n");
+ break;
+ case RQS_ASCQ_DCOFF:
+ DBG(5, "scsi_sense_handler: DC Offset error\n");
+ break;
+ case RQS_ASCQ_GAIN:
+ DBG(5, "scsi_sense_handler: Gain error\n");
+ break;
+ case RQS_ASCQ_POS:
+ DBG(5, "scsi_sense_handler: Positoning error\n");
+ break;
+ default:
+ DBG(5, "scsi_sense_handler: Unknown combination of ASC"
+ " (0x%02x) and ASCQ (0x%02x)\n", asc, ascq);
+ break;
+ }
+ }
+ else if ( asc == 0x00 && ascq == 0x05)
+ {
+ DBG(5, "scsi_sense_handler: End of data detected\n");
+ return SANE_STATUS_EOF;
+ }
+ else if ( asc == 0x3d && ascq == 0x00)
+ DBG(5, "scsi_sense_handler: Invalid bit in IDENTIFY\n");
+ else if ( asc == 0x2c && ascq == 0x02 )
+/* Ok */ DBG(5, "scsi_sense_handler: Invalid comb. of windows specfied\n");
+ else if ( asc == 0x20 && ascq == 0x00 )
+/* Ok */ DBG(5, "scsi_sense_handler: Invalid command opcode\n");
+ else if ( asc == 0x24 && ascq == 0x00 )
+/* Ok */ DBG(5, "scsi_sense_handler: Invalid field in CDB\n");
+ else if ( asc == 0x26 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Invalid field in the param list\n");
+ else if ( asc == 0x49 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Invalid message error\n");
+ else if ( asc == 0x60 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Lamp failure\n");
+ else if ( asc == 0x25 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Unsupported logic. unit\n");
+ else if ( asc == 0x53 && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: ADF paper jam or no paper\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ else if ( asc == 0x54 && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: Media bumping\n");
+ return SANE_STATUS_JAMMED; /* Don't know if this is right! */
+ }
+ else if ( asc == 0x55 && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: Scan Job stopped or cancelled\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ else if ( asc == 0x3a && ascq == 0x00 )
+ {
+ DBG(5, "scsi_sense_handler: Media (ADF or TMA) not available\n");
+ return SANE_STATUS_NO_DOCS;
+ }
+ else if ( asc == 0x3a && ascq == 0x01 )
+ {
+ DBG(5, "scsi_sense_handler: Door is not closed\n");
+ return SANE_STATUS_COVER_OPEN;
+ }
+ else if ( asc == 0x3a && ascq == 0x02 )
+ DBG(5, "scsi_sense_handler: Door is not opened\n");
+ else if ( asc == 0x00 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: No additional sense information\n");
+/* Ok */ else if ( asc == 0x1a && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Parameter list length error\n");
+ else if ( asc == 0x26 && ascq == 0x02 )
+ DBG(5, "scsi_sense_handler: Parameter value invalid\n");
+ else if ( asc == 0x03 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Peripheral device write fault - "
+ "Firmware Download Error\n");
+ else if ( asc == 0x2c && ascq == 0x01 )
+ DBG(5, "scsi_sense_handler: Too many windows specified\n");
+ else if ( asc == 0x80 && ascq == 0x00 )
+ DBG(5, "scsi_sense_handler: Target abort scan\n");
+ else if ( asc == 0x96 && ascq == 0x08 )
+ {
+ DBG(5, "scsi_sense_handler: Firewire Device busy\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ else
+ DBG(5, "scsi_sense_handler: Unknown combination of SENSE KEY "
+ "(0x%02x), ASC (0x%02x) and ASCQ (0x%02x)\n",
+ sense_key, asc, ascq);
+
+ return SANE_STATUS_IO_ERROR;
+
+ default:
+ DBG(5, "scsi_sense_handler: Unknown sense key (0x%02x)\n",
+ sense_key);
+ return SANE_STATUS_IO_ERROR;
+ }
+}
+
+
+/*---------- scsi_test_unit_ready() ------------------------------------------*/
+
+static SANE_Status
+scsi_test_unit_ready(Microtek2_Device *md)
+{
+ SANE_Status status;
+ uint8_t tur[TUR_CMD_L];
+ int sfd;
+
+
+ DBG(30, "scsi_test_unit_ready: md=%s\n", md->name);
+
+ TUR_CMD(tur);
+ status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "scsi_test_unit_ready: open '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( md_dump >= 2 )
+ dump_area2(tur, sizeof(tur), "testunitready");
+
+ status = sanei_scsi_cmd(sfd, tur, sizeof(tur), NULL, 0);
+ if ( status != SANE_STATUS_GOOD )
+ DBG(1, "scsi_test_unit_ready: cmd '%s'\n", sane_strstatus(status));
+
+ sanei_scsi_close(sfd);
+ return status;
+}
+
+
+/*---------- sane_start() ----------------------------------------------------*/
+
+SANE_Status
+sane_start(SANE_Handle handle)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ Microtek2_Scanner *ms = handle;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint8_t *pos;
+ int color, rc, retry;
+
+ DBG(30, "sane_start: handle=0x%p\n", handle);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ ms->n_control_bytes = md->n_control_bytes;
+
+ if ( md->model_flags & MD_READ_CONTROL_BIT )
+ {
+ if (ms->control_bytes) free((void *)ms->control_bytes);
+ ms->control_bytes = (uint8_t *) malloc(ms->n_control_bytes);
+ DBG(100, "sane_start: ms->control_bytes=%p, malloc'd %lu bytes\n",
+ ms->control_bytes, (u_long) ms->n_control_bytes);
+ if ( ms->control_bytes == NULL )
+ {
+ DBG(1, "sane_start: malloc() for control bits failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+
+ if (ms->sfd < 0) /* first or only pass of this scan */
+ {
+ /* open device */
+ for ( retry = 0; retry < 10; retry++ )
+ {
+ status = sanei_scsi_open (md->sane.name, &ms->sfd,
+ scsi_sense_handler, 0);
+ if ( status != SANE_STATUS_DEVICE_BUSY )
+ break;
+ DBG(30, "sane_start: Scanner busy, trying again\n");
+ sleep(1);
+ }
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "sane_start: scsi_open: '%s'\n", sane_strstatus(status));
+ goto cleanup;
+ }
+
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ if ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE )
+ DBG(30, "sane_start: backend calibration on\n");
+ else
+ DBG(30, "sane_start: backend calibration off\n");
+
+ if ( ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE )
+ && !( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) )
+ {
+ /* Read shading only once - possible with CIS scanners */
+ /* assuming only CIS scanners use Controlbits */
+ if ( ( md->shading_table_w == NULL )
+ || !( md->model_flags & MD_READ_CONTROL_BIT ) )
+ {
+ status = get_scan_parameters(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = read_shading_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+ }
+
+ status = get_scan_parameters(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ md->status.aloff |= 128;
+ md->status.timeremain = 10;
+
+ if ( ms->scan_source == MS_SOURCE_FLATBED
+ || ms->scan_source == MS_SOURCE_ADF )
+ {
+ md->status.flamp |= MD_FLAMP_ON;
+ md->status.tlamp &= ~MD_TLAMP_ON;
+ }
+ else
+ {
+ md->status.flamp &= ~MD_FLAMP_ON;
+ md->status.tlamp |= MD_TLAMP_ON;
+ }
+
+ if ( ms->lightlid35 )
+ {
+ md->status.flamp &= ~MD_FLAMP_ON;
+/* md->status.tlamp |= MD_TLAMP_ON;*/
+/* with this line on some scanners (X6, 0x91) the Flamp goes on */
+ }
+
+ if ( ms->no_backtracking )
+ md->status.ntrack |= MD_NTRACK_ON;
+ else
+ md->status.ntrack &= ~MD_NTRACK_ON;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ /* calculate gamma: we assume, that the gamma values are transferred */
+ /* with one send gamma command, even if it is a 3 pass scanner */
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ ms->lut_size = (int) pow(2.0, (double) ms->depth);
+ ms->lut_entry_size = ms->depth > 8 ? 2 : 1;
+ }
+ else
+ {
+ get_lut_size(mi, &ms->lut_size, &ms->lut_entry_size);
+ }
+ ms->lut_size_bytes = ms->lut_size * ms->lut_entry_size;
+ ms->word = (ms->lut_entry_size == 2);
+
+ ms->gamma_table = (uint8_t *) malloc(3 * ms->lut_size_bytes );
+ DBG(100, "sane_start: ms->gamma_table=%p, malloc'd %d bytes\n",
+ ms->gamma_table, 3 * ms->lut_size_bytes);
+ if ( ms->gamma_table == NULL )
+ {
+ DBG(1, "sane_start: malloc for gammatable failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ pos = ms->gamma_table + color * ms->lut_size_bytes;
+ calculate_gamma(ms, pos, color, ms->gamma_mode);
+ }
+
+ /* Some models ignore the settings for the exposure time, */
+ /* so we must do it ourselves. Apparently this seems to be */
+ /* the case for all models that have the chunky data format */
+
+ if ( mi->data_format == MI_DATAFMT_CHUNKY )
+ set_exposure(ms);
+
+ if ( ! (md->model_flags & MD_NO_GAMMA) )
+ {
+ status = scsi_send_gamma(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ status = scsi_set_window(ms, 1);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ ms->scanning = SANE_TRUE;
+ ms->cancelled = SANE_FALSE;
+ }
+
+ ++ms->current_pass;
+
+ status = scsi_read_image_info(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = prepare_buffers(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ status = calculate_sane_params(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ if ( !( md->model_flags & MD_NO_RIS_COMMAND ) )
+ {
+ /* !!FIXME!! - hack for C6USB because RIS over USB doesn't wait until */
+ /* scanner ready */
+ if (mi->model_code == 0x9a)
+ sleep(2);
+
+ status = scsi_wait_for_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ if ( ms->calib_backend
+ && ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ && ( ( md->shading_table_w == NULL )
+ || ( ms->mode != md->shading_table_contents )
+ )
+ )
+ {
+ status = read_cx_shading(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ if ( ms->lightlid35 )
+ /* hopefully this leads to a switched off flatbed lamp with lightlid */
+ {
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ md->status.flamp &= ~MD_FLAMP_ON;
+ md->status.tlamp &= ~MD_TLAMP_ON;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+
+ if ( md->model_flags & MD_READ_CONTROL_BIT )
+ {
+ status = scsi_read_control_bits(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ if ( ms->calib_backend )
+ {
+ status = condense_shading(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+ }
+ }
+
+ /* open a pipe and fork a child process, that actually reads the data */
+ rc = pipe(ms->fd);
+ if ( rc == -1 )
+ {
+ DBG(1, "sane_start: pipe failed\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* create reader routine as new thread or process */
+ ms->pid = sanei_thread_begin( reader_process,(void*) ms);
+
+ if ( ms->pid == -1 )
+ {
+ DBG(1, "sane_start: fork failed\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ if (sanei_thread_is_forked()) close(ms->fd[1]);
+
+ return SANE_STATUS_GOOD;
+
+cleanup:
+ cleanup_scanner(ms);
+ return status;
+}
+
+/*---------- prepare_buffers -------------------------------------------------*/
+
+static SANE_Status
+prepare_buffers(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t strip_lines;
+ int i;
+
+ status = SANE_STATUS_GOOD;
+ DBG(30, "prepare_buffers: ms=0x%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ /* calculate maximum number of lines to read */
+ strip_lines = (int) ((double) ms->y_resolution_dpi * md->opts.strip_height);
+ if ( strip_lines == 0 )
+ strip_lines = 1;
+
+ /* calculate number of lines that fit into the source buffer */
+#ifdef TESTBACKEND
+ ms->src_max_lines = MIN( 5000000 / ms->bpl, strip_lines);
+#else
+ ms->src_max_lines = MIN( sanei_scsi_max_request_size / ms->bpl, strip_lines);
+#endif
+ if ( ms->src_max_lines == 0 )
+ {
+ DBG(1, "sane_start: Scan buffer too small\n");
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* allocate buffers */
+ ms->src_buffer_size = ms->src_max_lines * ms->bpl;
+
+ if ( ms->mode == MS_MODE_COLOR && mi->data_format == MI_DATAFMT_LPLSEGREG )
+ {
+ /* In this case the data is not neccessarily in the order RGB */
+ /* and there may be different numbers of read red, green and blue */
+ /* segments. We allocate a second buffer to read new lines in */
+ /* and hold undelivered pixels in the other buffer */
+ int extra_buf_size;
+
+ extra_buf_size = 2 * ms->bpl * mi->ccd_gap
+ * (int) ceil( (double) mi->max_yresolution
+ / (double) mi->opt_resolution);
+ for ( i = 0; i < 2; i++ )
+ {
+ if ( ms->buf.src_buffer[i] )
+ free((void *) ms->buf.src_buffer[i]);
+ ms->buf.src_buffer[i] = (uint8_t *) malloc(ms->src_buffer_size
+ + extra_buf_size);
+ DBG(100, "prepare_buffers: ms->buf.src_buffer[%d]=%p,"
+ "malloc'd %d bytes\n", i, ms->buf.src_buffer[i],
+ ms->src_buffer_size + extra_buf_size);
+ if ( ms->buf.src_buffer[i] == NULL )
+ {
+ DBG(1, "sane_start: malloc for scan buffer failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+ ms->buf.free_lines = ms->src_max_lines + extra_buf_size / ms->bpl;
+ ms->buf.free_max_lines = ms->buf.free_lines;
+ ms->buf.src_buf = ms->buf.src_buffer[0];
+ ms->buf.current_src = 0; /* index to current buffer */
+ }
+ else
+ {
+ if ( ms->buf.src_buf )
+ free((void *) ms->buf.src_buf);
+ ms->buf.src_buf = malloc(ms->src_buffer_size);
+ DBG(100, "sane_start: ms->buf.src_buf=%p, malloc'd %d bytes\n",
+ ms->buf.src_buf, ms->src_buffer_size);
+ if ( ms->buf.src_buf == NULL )
+ {
+ DBG(1, "sane_start: malloc for scan buffer failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+
+ for ( i = 0; i < 3; i++ )
+ {
+ ms->buf.current_pos[i] = ms->buf.src_buffer[0];
+ ms->buf.planes[0][i] = 0;
+ ms->buf.planes[1][i] = 0;
+ }
+
+ /* allocate a temporary buffer for the data, if auto_adjust threshold */
+ /* is selected. */
+
+ if ( ms->auto_adjust == 1 )
+ {
+ ms->temporary_buffer = (uint8_t *) malloc(ms->remaining_bytes);
+ DBG(100, "sane_start: ms->temporary_buffer=%p, malloc'd %d bytes\n",
+ ms->temporary_buffer, ms->remaining_bytes);
+ if ( ms->temporary_buffer == NULL )
+ {
+ DBG(1, "sane_start: malloc() for temporary buffer failed\n");
+ status = SANE_STATUS_NO_MEM;
+ goto cleanup;
+ }
+ }
+ else
+ ms->temporary_buffer = NULL;
+
+ /* some data formats have additional information in a scan line, which */
+ /* is not transferred to the frontend; real_bpl is the number of bytes */
+ /* per line, that is copied into the frontend's buffer */
+ ms->real_bpl = (uint32_t) ceil( ((double) ms->ppl *
+ (double) ms->bits_per_pixel_out) / 8.0 );
+ if ( mi->onepass && ms->mode == MS_MODE_COLOR )
+ ms->real_bpl *= 3;
+
+ ms->real_remaining_bytes = ms->real_bpl * ms->src_remaining_lines;
+
+ return SANE_STATUS_GOOD;
+
+cleanup:
+ cleanup_scanner(ms);
+ return status;
+
+}
+static void
+write_shading_buf_pnm(Microtek2_Scanner *ms, uint32_t lines)
+{
+ FILE *outfile;
+ uint16_t pixel, color, linenr, factor;
+ unsigned char img_val_out;
+ float img_val = 0;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ factor = 256;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ factor = 64;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ factor = 16;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ factor = 4;
+ else
+ factor = 1;
+ if ( md->model_flags & MD_16BIT_TRANSFER )
+ factor = 256;
+
+ outfile = fopen("shading_buf_w.pnm", "w");
+ fprintf(outfile, "P6\n#imagedata\n%d %d\n255\n",
+ mi->geo_width / mi->calib_divisor, lines);
+ for ( linenr=0; linenr < lines; linenr++ )
+ {
+ if (mi->data_format == MI_DATAFMT_LPLSEGREG)
+ {
+ DBG(1, "Output of shading buffer unsupported for"
+ "Segreg Data format\n");
+ break;
+ }
+
+ for ( pixel=0;
+ pixel < (uint16_t) (mi->geo_width / mi->calib_divisor);
+ pixel++)
+ {
+ for ( color=0; color < 3; color++ )
+ {
+ switch( mi->data_format )
+ {
+ case MI_DATAFMT_LPLCONCAT:
+ if ( md->shading_depth > 8)
+ img_val = *((uint16_t *) ms->shading_image
+ + linenr * ( ms->bpl / ms->lut_entry_size )
+ + mi->color_sequence[color]
+ * ( ms->bpl / ms->lut_entry_size / 3 )
+ + pixel);
+ else
+ img_val = *((uint8_t *) ms->shading_image
+ + linenr * ( ms->bpl / ms->lut_entry_size )
+ + mi->color_sequence[color]
+ * ( ms->bpl / ms->lut_entry_size / 3 )
+ + pixel);
+
+ break;
+ case MI_DATAFMT_CHUNKY:
+ case MI_DATAFMT_9800:
+ img_val = *((uint16_t *)ms->shading_image
+ + linenr * 3 * ( mi->geo_width
+ / mi->calib_divisor )
+ + 3 * pixel
+ + mi->color_sequence[color]);
+ break;
+ }
+ img_val /= factor;
+ img_val_out = (unsigned char)img_val;
+ fputc(img_val_out, outfile);
+ }
+ }
+ }
+ fclose(outfile);
+
+ return;
+}
+
+static void
+write_shading_pnm(Microtek2_Scanner *ms)
+{
+ FILE *outfile_w = NULL, *outfile_d = NULL;
+ int pixel, color, line, offset, num_shading_pixels, output_height;
+ uint16_t img_val, factor;
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+
+ output_height = 180;
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ DBG(30, "write_shading_pnm: ms=%p\n", (void *) ms);
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ factor = 256;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ factor = 64;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ factor = 16;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ factor = 4;
+ else
+ factor = 1;
+ if ( md->model_flags & MD_16BIT_TRANSFER )
+ factor = 256;
+
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ num_shading_pixels = ms->n_control_bytes * 8;
+ else
+ num_shading_pixels = mi->geo_width / mi->calib_divisor;
+ if ( md->shading_table_w != NULL )
+ {
+ outfile_w = fopen("microtek2_shading_w.pnm", "w");
+ fprintf(outfile_w, "P6\n#imagedata\n%d %d\n255\n",
+ num_shading_pixels, output_height);
+ }
+ if ( md->shading_table_d != NULL )
+ {
+ outfile_d = fopen("microtek2_shading_d.pnm", "w");
+ fprintf(outfile_d, "P6\n#imagedata\n%d %d\n255\n",
+ num_shading_pixels, output_height);
+ }
+ for ( line=0; line < output_height; ++line )
+ {
+ for ( pixel=0; pixel < num_shading_pixels ; ++pixel)
+ {
+ for ( color=0; color < 3; ++color )
+ {
+ offset = mi->color_sequence[color]
+ * num_shading_pixels
+ + pixel;
+ if ( md->shading_table_w != NULL )
+ {
+ if ( ms->lut_entry_size == 2 )
+ {
+ img_val = *((uint16_t *) md->shading_table_w + offset );
+ img_val /= factor;
+ }
+ else
+ img_val = *((uint8_t *) md->shading_table_w + offset );
+ fputc((unsigned char)img_val, outfile_w);
+ }
+
+ if ( md->shading_table_d != NULL )
+ {
+ if ( ms->lut_entry_size == 2 )
+ {
+ img_val = *((uint16_t *) md->shading_table_d + offset );
+ img_val /= factor;
+ }
+ else
+ img_val = *((uint8_t *) md->shading_table_d + offset );
+ fputc((unsigned char)img_val, outfile_d);
+ }
+ }
+ }
+ }
+ if ( md->shading_table_w != NULL )
+ fclose(outfile_w);
+ if ( md->shading_table_d != NULL )
+ fclose(outfile_d);
+
+ return;
+}
+
+static void
+write_cshading_pnm(Microtek2_Scanner *ms)
+{
+ FILE *outfile;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ int pixel, color, line, offset, img_val, img_height=30, factor;
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ factor = 256;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ factor = 64;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ factor = 16;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ factor = 4;
+ else
+ factor = 1;
+ if ( md->model_flags & MD_16BIT_TRANSFER )
+ factor = 256;
+
+ outfile = fopen("microtek2_cshading_w.pnm", "w");
+ if ( ms->mode == MS_MODE_COLOR )
+ fprintf(outfile, "P6\n#imagedata\n%d %d\n255\n", ms->ppl, img_height);
+ else
+ fprintf(outfile, "P5\n#imagedata\n%d %d\n255\n", ms->ppl, img_height);
+
+ for ( line=0; line < img_height; ++line )
+ {
+ for ( pixel=0; pixel < (int)ms->ppl; ++pixel)
+ {
+ for ( color=0; color < 3; ++color )
+ {
+ offset = color * (int)ms->ppl + pixel;
+ if ( ms->lut_entry_size == 1 )
+ img_val = (int) *((uint8_t *)ms->condensed_shading_w + offset);
+ else
+ {
+ img_val = (int) *((uint16_t *)ms->condensed_shading_w
+ + offset);
+ img_val /= factor;
+ }
+ fputc((unsigned char)img_val, outfile);
+ if ( ms->mode == MS_MODE_GRAY )
+ break;
+ }
+ }
+ }
+ fclose(outfile);
+
+ return;
+}
+
+
+
+/*---------- condense_shading() ----------------------------------------------*/
+
+static SANE_Status
+condense_shading(Microtek2_Scanner *ms)
+{
+ /* This function extracts the relevant shading pixels from */
+ /* the shading image according to the 1's in the result of */
+ /* 'read control bits', and stores them in a memory block. */
+ /* We will then have as many shading pixels as there are */
+ /* pixels per line. The order of the pixels in the condensed */
+ /* shading data block will always be left to right. The color */
+ /* sequence remains unchanged. */
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t byte;
+ uint32_t cond_length; /* bytes per condensed shading line */
+ int color, count, lfd_bit;
+ int shad_bplc, shad_pixels; /* bytes per line & color in shading image */
+ int bit, flag;
+ uint32_t sh_offset, csh_offset;
+ int gray_filter_color = 1; /* which color of the shading is taken for gray*/
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ DBG(30, "condense_shading: ms=%p, ppl=%d\n", (void *) ms, ms->ppl);
+ if ( md->shading_table_w == NULL )
+ {
+ DBG(1, "condense shading: no shading table found, skip shading\n");
+ return SANE_STATUS_GOOD;
+ }
+
+ get_lut_size( mi, &ms->lut_size, &ms->lut_entry_size );
+
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ {
+ shad_pixels = ms->n_control_bytes * 8;
+ gray_filter_color = 0; /* 336CX reads only one shading in gray mode*/
+ }
+ else
+ shad_pixels = mi->geo_width;
+
+ shad_bplc = shad_pixels * ms->lut_entry_size;
+
+ if ( md_dump >= 3 )
+ {
+ dump_area2(md->shading_table_w, shad_bplc * 3, "shading_table_w");
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ write_shading_pnm(ms);
+ }
+
+ cond_length = ms->bpl * ms->lut_entry_size;
+
+ if ( ms->condensed_shading_w )
+ {
+ free((void*) ms->condensed_shading_w );
+ ms->condensed_shading_w = NULL;
+ }
+ ms->condensed_shading_w = (uint8_t *)malloc(cond_length);
+ DBG(100, "condense_shading: ms->condensed_shading_w=%p,"
+ "malloc'd %d bytes\n", ms->condensed_shading_w, cond_length);
+ if ( ms->condensed_shading_w == NULL )
+ {
+ DBG(1, "condense_shading: malloc for white table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if ( md->shading_table_d != NULL )
+ {
+ if ( md_dump >= 3 )
+ dump_area2(md->shading_table_d, shad_bplc * 3,
+ "shading_table_d");
+
+ if ( ms->condensed_shading_d )
+ {
+ free((void*) ms->condensed_shading_d );
+ ms->condensed_shading_d = NULL;
+ }
+ ms->condensed_shading_d = (uint8_t *)malloc(cond_length);
+ DBG(100, "condense_shading: ms->condensed_shading_d=%p,"
+ " malloc'd %d bytes\n", ms->condensed_shading_d, cond_length);
+ if ( ms->condensed_shading_d == NULL )
+ {
+ DBG(1, "condense_shading: malloc for dark table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+ DBG(128, "controlbit offset=%d\n", md->controlbit_offset);
+
+ count = 0;
+
+ for (lfd_bit = 0; ( lfd_bit < mi->geo_width ) && ( count < (int)ms->ppl );
+ ++lfd_bit)
+ {
+ byte = ( lfd_bit + md->controlbit_offset ) / 8;
+ bit = ( lfd_bit + md->controlbit_offset ) % 8;
+
+ if ( mi->direction & MI_DATSEQ_RTOL )
+ flag = ((ms->control_bytes[byte] >> bit) & 0x01);
+ else
+ flag = ((ms->control_bytes[byte] >> (7 - bit)) & 0x01);
+
+ if ( flag == 1 ) /* flag==1 if byte's bit is set */
+ {
+ for ( color = 0; color < 3; ++color )
+ {
+ if ( ( ms->mode == MS_MODE_COLOR )
+ || ( ( ms->mode == MS_MODE_GRAY )
+ && ( color == gray_filter_color ) )
+ || ( ( ms->mode == MS_MODE_LINEARTFAKE )
+ && ( color == gray_filter_color ) )
+ )
+ {
+ sh_offset = color * shad_pixels + lfd_bit;
+ if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING )
+ sh_offset += md->controlbit_offset;
+ if ( ms->mode == MS_MODE_COLOR )
+ csh_offset = color * ms->ppl + count;
+ else
+ csh_offset = count;
+
+ if ( csh_offset > cond_length )
+ {
+ DBG(1, "condense_shading: wrong control bits data, " );
+ DBG(1, "csh_offset (%d) > cond_length(%d)\n",
+ csh_offset, cond_length );
+ csh_offset = cond_length;
+ }
+
+ if ( ms->lut_entry_size == 2 )
+ {
+ *((uint16_t *)ms->condensed_shading_w + csh_offset) =
+ *((uint16_t *)md->shading_table_w
+ + sh_offset);
+ if ( ms->condensed_shading_d != NULL )
+ *((uint16_t *)ms->condensed_shading_d + csh_offset) =
+ *((uint16_t *)md->shading_table_d
+ + sh_offset);
+ }
+ else
+ {
+ *((uint8_t *)ms->condensed_shading_w + csh_offset) =
+ *((uint8_t *)md->shading_table_w
+ + sh_offset);
+ if ( ms->condensed_shading_d != NULL )
+ *((uint8_t *)ms->condensed_shading_d + csh_offset) =
+ *((uint8_t *)md->shading_table_d
+ + sh_offset);
+ }
+ }
+ }
+ ++count;
+ }
+ }
+
+ if ( md_dump >= 3 )
+ {
+ dump_area2(ms->condensed_shading_w, cond_length, "condensed_shading_w");
+ if ( ms->condensed_shading_d != NULL )
+ dump_area2(ms->condensed_shading_d, cond_length,
+ "condensed_shading_d");
+
+ write_cshading_pnm(ms);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- read_shading_image() --------------------------------------------*/
+
+static SANE_Status
+read_shading_image(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t lines;
+ uint8_t *buf;
+ int max_lines;
+ int lines_to_read;
+
+ DBG(30, "read_shading_image: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+
+ if ( ! MI_WHITE_SHADING_ONLY(mi->shtrnsferequ)
+ || ( md->model_flags & MD_PHANTOM_C6 ) )
+
+ /* Dark shading correction */
+ /* ~~~~~~~~~~~~~~~~~~~~~~~ */
+ {
+ DBG(30, "read_shading_image: reading black data\n");
+ md->status.ntrack |= MD_NTRACK_ON;
+ md->status.ncalib &= ~MD_NCALIB_ON;
+ md->status.flamp |= MD_FLAMP_ON;
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ {
+ md->status.stick |= MD_STICK_ON;
+ md->status.reserved17 |= MD_RESERVED17_ON;
+ }
+
+ get_calib_params(ms);
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ ms->stay = 1;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_set_window(ms, 1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+#ifdef TESTBACKEND
+ status = scsi_read_sh_image_info(ms);
+#else
+ status = scsi_read_image_info(ms);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_wait_for_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ md->status.flamp &= ~MD_FLAMP_ON;
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->shading_image = malloc(ms->bpl * ms->src_remaining_lines);
+ DBG(100, "read shading image: ms->shading_image=%p,"
+ " malloc'd %d bytes\n",
+ ms->shading_image, ms->bpl * ms->src_remaining_lines);
+ if ( ms->shading_image == NULL )
+ {
+ DBG(1, "read_shading_image: malloc for buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+
+#ifdef TESTBACKEND
+ max_lines = 5000000 / ms->bpl;
+#else
+ max_lines = sanei_scsi_max_request_size / ms->bpl;
+#endif
+ if ( max_lines == 0 )
+ {
+ DBG(1, "read_shading_image: buffer too small\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ lines = ms->src_remaining_lines;
+ while ( ms->src_remaining_lines > 0 )
+ {
+ lines_to_read = MIN(max_lines, ms->src_remaining_lines);
+ ms->src_buffer_size = lines_to_read * ms->bpl;
+ ms->transfer_length = ms->src_buffer_size;
+#ifdef TESTBACKEND
+ status = scsi_read_sh_d_image(ms, buf);
+#else
+ status = scsi_read_image(ms, buf, md->shading_depth>8 ? 2 : 1);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "read_shading_image: read image failed: '%s'\n",
+ sane_strstatus(status));
+ return status;
+ }
+
+ ms->src_remaining_lines -= lines_to_read;
+ buf += ms->src_buffer_size;
+ }
+
+ status = prepare_shading_data(ms, lines, &md->shading_table_d);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ /* send shading data to the device */
+ /* Some models use "read_control bit", and the shading must be */
+ /* applied by the backend later */
+ if ( ! (md->model_flags & MD_READ_CONTROL_BIT) )
+ {
+ status = shading_function(ms, md->shading_table_d);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->word = ms->lut_entry_size == 2 ? 1 : 0;
+ ms->current_color = MS_COLOR_ALL;
+ status = scsi_send_shading(ms,
+ md->shading_table_d,
+ 3 * ms->lut_entry_size
+ * mi->geo_width / mi->calib_divisor,
+ 1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ DBG(100, "free memory for ms->shading_image at %p\n",
+ ms->shading_image);
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+ }
+
+ /* white shading correction */
+ /* ~~~~~~~~~~~~~~~~~~~~~~~~ */
+ DBG(30, "read_shading_image: reading white data\n");
+
+ /* According to the doc NCalib must be set for white shading data */
+ /* if we have a black and a white shading correction and must be */
+ /* cleared if we have only a white shading collection */
+ if ( ! MI_WHITE_SHADING_ONLY(mi->shtrnsferequ)
+ || ( md->model_flags & MD_PHANTOM_C6 ) )
+ md->status.ncalib |= MD_NCALIB_ON;
+ else
+ md->status.ncalib &= ~MD_NCALIB_ON;
+
+ md->status.flamp |= MD_FLAMP_ON;
+/* md->status.tlamp &= ~MD_TLAMP_ON; */
+ md->status.ntrack |= MD_NTRACK_ON;
+
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ {
+ md->status.stick &= ~MD_STICK_ON;
+ md->status.reserved17 |= MD_RESERVED17_ON;
+ }
+
+ get_calib_params(ms);
+
+#ifdef NO_PHANTOMTYPE_SHADING
+/* md->status.stick &= ~MD_STICK_ON; */
+/* md->status.ncalib &= ~MD_NCALIB_ON; */
+/* md->status.reserved17 &= ~MD_RESERVED17_ON; */
+ ms->rawdat = 0;
+#endif
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_set_window(ms, 1);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+#ifdef TESTBACKEND
+ status = scsi_read_sh_image_info(ms);
+#else
+ status = scsi_read_image_info(ms);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ status = scsi_wait_for_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+#ifdef NO_PHANTOMTYPE_SHADING
+ if ( !( md->model_flags & MD_READ_CONTROL_BIT ) )
+ {
+#endif
+ status = scsi_read_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+#ifdef NO_PHANTOMTYPE_SHADING
+ }
+#endif
+
+#ifdef NO_PHANTOMTYPE_SHADING
+ if ( mi->model_code == 0x94 )
+ status = scsi_read_control_bits(ms);
+#endif
+
+ ms->shading_image = malloc(ms->bpl * ms->src_remaining_lines);
+ DBG(100, "read shading image: ms->shading_image=%p, malloc'd %d bytes\n",
+ ms->shading_image, ms->bpl * ms->src_remaining_lines);
+ if ( ms->shading_image == NULL )
+ {
+ DBG(1, "read_shading_image: malloc for buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+#ifdef TESTBACKEND
+ max_lines = 5000000 / ms->bpl;
+#else
+ max_lines = sanei_scsi_max_request_size / ms->bpl;
+#endif
+ if ( max_lines == 0 )
+ {
+ DBG(1, "read_shading_image: buffer too small\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ lines = ms->src_remaining_lines;
+ while ( ms->src_remaining_lines > 0 )
+ {
+ lines_to_read = MIN(max_lines, ms->src_remaining_lines);
+ ms->src_buffer_size = lines_to_read * ms->bpl;
+ ms->transfer_length = ms->src_buffer_size;
+
+#ifdef TESTBACKEND
+ status = scsi_read_sh_w_image(ms, buf);
+#else
+ status = scsi_read_image(ms, buf, md->shading_depth>8 ? 2 : 1);
+#endif
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->src_remaining_lines -= lines_to_read;
+ buf += ms->src_buffer_size;
+ }
+
+ status = prepare_shading_data(ms, lines, &md->shading_table_w);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ if ( md_dump >= 3 )
+ {
+ write_shading_buf_pnm(ms, lines);
+ write_shading_pnm(ms);
+ }
+
+ /* send shading data to the device */
+ /* Some models use "read_control bit", and the shading must be */
+ /* applied by the backend later */
+ if ( ! (md->model_flags & MD_READ_CONTROL_BIT) )
+ {
+ status = shading_function(ms, md->shading_table_w);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ ms->word = ms->lut_entry_size == 2 ? 1 : 0;
+ ms->current_color = MS_COLOR_ALL;
+ status = scsi_send_shading(ms,
+ md->shading_table_w,
+ 3 * ms->lut_entry_size
+ * mi->geo_width / mi->calib_divisor,
+ 0);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ }
+
+ ms->rawdat = 0;
+ ms->stay = 0;
+ md->status.ncalib |= MD_NCALIB_ON;
+
+ if ( md->model_flags & MD_PHANTOM_C6 )
+ {
+ md->status.stick &= ~MD_STICK_ON;
+ md->status.reserved17 &= ~MD_RESERVED17_ON;
+ }
+
+#ifdef NO_PHANTOMTYPE_SHADING
+ if (mi->model_code == 0x94)
+ md->status.ncalib &= ~MD_NCALIB_ON;
+#endif
+
+ status = scsi_send_system_status(md, ms->sfd);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ DBG(100, "free memory for ms->shading_image at %p\n",
+ ms->shading_image);
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+
+ return SANE_STATUS_GOOD;
+
+}
+
+/*---------- prepare_shading_data() ------------------------------------------*/
+
+static SANE_Status
+prepare_shading_data(Microtek2_Scanner *ms, uint32_t lines, uint8_t **data)
+{
+ /* This function calculates one line of black or white shading data */
+ /* from the shading image. At the end we have one line. The */
+ /* color sequence is unchanged. */
+
+#define MICROTEK2_CALIB_USE_MEDIAN
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t length,line;
+ int color, i;
+ SANE_Status status;
+
+#ifdef MICROTEK2_CALIB_USE_MEDIAN
+ uint16_t *sortbuf, value;
+#else
+ uint32_t value;
+#endif
+
+ DBG(30, "prepare_shading_data: ms=%p, lines=%d, *data=%p\n",
+ (void *) ms, lines, *data);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ status = SANE_STATUS_GOOD;
+
+ get_lut_size(mi, &ms->lut_size, &ms->lut_entry_size);
+ length = 3 * ms->lut_entry_size * mi->geo_width / mi->calib_divisor;
+
+ if ( *data == NULL )
+ {
+ *data = (uint8_t *) malloc(length);
+ DBG(100, "prepare_shading_data: malloc'd %d bytes at %p\n",
+ length, *data);
+ if ( *data == NULL )
+ {
+ DBG(1, "prepare_shading_data: malloc for shading table failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ }
+
+#ifdef MICROTEK2_CALIB_USE_MEDIAN
+ sortbuf = malloc( lines * ms->lut_entry_size );
+ DBG(100, "prepare_shading_data: sortbuf= %p, malloc'd %d Bytes\n",
+ (void *) sortbuf, lines * ms->lut_entry_size);
+ if ( sortbuf == NULL )
+ {
+ DBG(1, "prepare_shading_data: malloc for sort buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+#endif
+
+ switch( mi->data_format )
+ {
+ case MI_DATAFMT_LPLCONCAT:
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "prepare_shading_data: wordsize == 1 unsupported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ )
+ {
+ value = 0;
+ for ( line = 0; line < lines; line++ )
+#ifndef MICROTEK2_CALIB_USE_MEDIAN
+/* average the shading lines to get the shading data */
+ value += *((uint16_t *) ms->shading_image
+ + line * ( ms->bpl / ms->lut_entry_size )
+ + color * ( ms->bpl / ms->lut_entry_size / 3 )
+ + i);
+ value /= lines;
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+#else
+/* use a median filter to get the shading data -- should be better */
+ *(sortbuf + line ) =
+ *((uint16_t *) ms->shading_image
+ + line * ( ms->bpl / ms->lut_entry_size )
+ + color * ( ms->bpl / ms->lut_entry_size / 3 )
+ + i);
+ qsort(sortbuf, lines, sizeof(uint16_t),
+ (qsortfunc)compare_func_16);
+ value = *(sortbuf + ( lines - 1 ) / 2 );
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) = value;
+#endif
+ }
+ }
+ break;
+
+ case MI_DATAFMT_CHUNKY:
+ case MI_DATAFMT_9800:
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "prepare_shading_data: wordsize == 1 unsupported\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ )
+ {
+ value = 0;
+ for ( line = 0; line < lines; line++ )
+#ifndef MICROTEK2_CALIB_USE_MEDIAN
+/* average the shading lines to get the shading data */
+ value += *((uint16_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+
+ value /= lines;
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+#else
+/* use a median filter to get the shading data -- should be better */
+ *(sortbuf + line ) =
+ *((uint16_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+ qsort(sortbuf, lines, sizeof(uint16_t),
+ (qsortfunc)compare_func_16);
+ value = *(sortbuf + ( lines - 1 ) / 2 );
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) = value;
+#endif
+ }
+ }
+ break;
+
+ case MI_DATAFMT_LPLSEGREG:
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ )
+ {
+ value = 0;
+ if ( ms->lut_entry_size == 1 )
+ {
+ for ( line = 0; line < lines; line++ )
+ value += *((uint8_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+
+ value /= lines;
+ *((uint8_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint8_t) MIN(0xff, value);
+
+ }
+ else
+ {
+ for ( line = 0; line < lines; line++ )
+ value += *((uint16_t *) ms->shading_image
+ + line * 3 * mi->geo_width / mi->calib_divisor
+ + 3 * i
+ + color);
+
+ value /= lines;
+#ifndef MICROTEK2_CALIB_USE_MEDIAN
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+#else
+ *((uint16_t *) *data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) = value;
+#endif
+ }
+
+ }
+ }
+ break;
+
+ default:
+ DBG(1, "prepare_shading_data: Unsupported data format 0x%02x\n",
+ mi->data_format);
+ status = SANE_STATUS_UNSUPPORTED;
+ }
+
+#ifdef MICROTEK2_CALIB_USE_MEDIAN
+ DBG(100, "prepare_shading_data: free sortbuf at %p\n", (void *) sortbuf);
+ free(sortbuf);
+ sortbuf = NULL;
+#endif
+ return status;
+}
+
+
+/*---------- read_cx_shading() -----------------------------------------------*/
+
+static SANE_Status
+read_cx_shading(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ md = ms->dev;
+
+ DBG(30, "read_cx_shading: ms=%p\n",(void *) ms);
+
+ md->shading_table_contents = ms->mode;
+
+ if ( ms->mode == MS_MODE_COLOR )
+ ms->current_color = MS_COLOR_ALL;
+ else
+ ms->current_color = MS_COLOR_GREEN; /* for grayscale */
+
+ ms->word = 1;
+ ms->dark = 0;
+
+ status = read_cx_shading_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ ms->word = 0; /* the Windows driver reads dark shading with word=0 */
+ ms->dark = 1;
+ status = read_cx_shading_image(ms);
+ if ( status != SANE_STATUS_GOOD )
+ goto cleanup;
+
+ return SANE_STATUS_GOOD;
+
+cleanup:
+ cleanup_scanner(ms);
+ return status;
+}
+
+
+/*---------- read_cx_shading_image() -----------------------------------------*/
+
+static SANE_Status
+read_cx_shading_image(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ uint32_t shading_bytes, linesize, buffer_size;
+ uint8_t *buf;
+ int max_lines, lines_to_read, remaining_lines;
+
+ md = ms->dev;
+
+ shading_bytes = ms->n_control_bytes * 8 * md->shading_length;
+ if ( ms->current_color == MS_COLOR_ALL )
+ shading_bytes *= 3;
+ if ( ms->word == 1 )
+ shading_bytes *= 2;
+
+ if ( ms->shading_image )
+ {
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+ }
+ ms->shading_image = malloc(shading_bytes);
+ DBG(100, "read_cx_shading: ms->shading_image=%p, malloc'd %d bytes\n",
+ ms->shading_image, shading_bytes);
+ if ( ms->shading_image == NULL )
+ {
+ DBG(1, "read_cx_shading: malloc for cx_shading buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+
+ DBG(30, "read_cx_shading_image: ms=%p, shading_bytes=%d\n",
+ (void *) ms, shading_bytes);
+
+ linesize = shading_bytes / md->shading_length;
+#ifdef TESTBACKEND
+ max_lines = 5000000 / linesize;
+#else
+ max_lines = sanei_scsi_max_request_size / linesize;
+#endif
+ /* the following part is like in "read_shading_image" */
+ remaining_lines = md->shading_length;
+ while ( remaining_lines > 0 )
+ {
+ lines_to_read = MIN(max_lines, remaining_lines);
+ buffer_size = lines_to_read * linesize;
+
+ status = scsi_read_shading(ms, buf, buffer_size);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "read_cx_shading: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+ remaining_lines -= lines_to_read;
+ buf += buffer_size;
+ }
+
+ status = calc_cx_shading_line(ms);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "read_cx_shading: '%s'\n", sane_strstatus(status));
+ return status;
+ }
+
+ if ( ms->shading_image )
+ {
+ DBG(100, "free memory for ms->shading_image at %p\n",
+ ms->shading_image);
+ free((void *) ms->shading_image);
+ ms->shading_image = NULL;
+ }
+
+ return status;
+}
+
+/*---------- calc_cx_shading_line() ------------------------------------------*/
+/* calculates the mean value of the shading lines and stores one line of */
+/* 8-bit shading data. Scanning direction + color sequence remain as they are */
+/* ToDo: more than 8-bit data */
+
+static SANE_Status
+calc_cx_shading_line(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ SANE_Status status;
+ uint8_t *current_byte, *buf, *shading_table_pointer;
+ uint8_t color, factor;
+ uint32_t shading_line_pixels, shading_line_bytes,
+ shading_data_bytes, line, i, accu, color_offset;
+ uint16_t *sortbuf, value;
+
+ md = ms->dev;
+ status = SANE_STATUS_GOOD;
+
+ sortbuf = malloc( md->shading_length * sizeof(float) );
+ DBG(100, "calc_cx_shading: sortbuf= %p, malloc'd %lu Bytes\n",
+ (void *) sortbuf, (u_long) (md->shading_length * sizeof(float)));
+ if ( sortbuf == NULL )
+ {
+ DBG(1, "calc_cx_shading: malloc for sort buffer failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ buf = ms->shading_image;
+ shading_line_pixels = ms->n_control_bytes * 8; /* = 2560 for 330CX */
+ shading_line_bytes = shading_line_pixels; /* grayscale */
+ if ( ms->mode == MS_MODE_COLOR ) /* color */
+ shading_line_bytes *= 3;
+ shading_data_bytes = shading_line_bytes; /* 8-bit color depth */
+ if (ms->word == 1) /* > 8-bit color depth */
+ shading_data_bytes *= 2;
+ factor = 4; /* shading bit depth = 10bit; shading line bit depth = 8bit */
+
+ if (ms->dark == 0) /* white shading data */
+ {
+ if ( md->shading_table_w )
+ free( (void *)md->shading_table_w );
+ md->shading_table_w = (uint8_t *) malloc(shading_line_bytes);
+ DBG(100, "calc_cx_shading: md->shading_table_w=%p, malloc'd %d bytes\n",
+ md->shading_table_w, shading_line_bytes);
+ if ( md->shading_table_w == NULL )
+ {
+ DBG(100, "calc_cx_shading: malloc for white shadingtable failed\n");
+ status = SANE_STATUS_NO_MEM;
+ cleanup_scanner(ms);
+ }
+
+ shading_table_pointer = md->shading_table_w;
+ }
+
+ else /* dark shading data */
+ {
+ if ( md->shading_table_d )
+ free( (void *)md->shading_table_d);
+ md->shading_table_d = (uint8_t *) malloc(shading_line_bytes);
+ DBG(100, "calc_cx_shading: md->shading_table_d=%p, malloc'd %d bytes\n",
+ md->shading_table_d, shading_line_bytes);
+
+ if ( md->shading_table_d == NULL )
+ {
+ DBG(1, "calc_cx_shading: malloc for dark shading table failed\n");
+ status = SANE_STATUS_NO_MEM;
+ cleanup_scanner(ms);
+ }
+
+ shading_table_pointer = md->shading_table_d;
+ }
+
+ DBG(30, "calc_cx_shading_line: ms=%p\n"
+ "md->shading_table_w=%p\n"
+ "md->shading_table_d=%p\n"
+ "shading_line_bytes=%d\n"
+ "shading_line_pixels=%d\n"
+ "shading_table_pointer=%p\n",
+ (void *) ms, md->shading_table_w, md->shading_table_d,
+ shading_line_bytes, shading_line_pixels, shading_table_pointer);
+
+ /* calculating the median pixel values over the shading lines */
+ /* and write them to the shading table */
+ for (color = 0; color < 3; color++)
+ {
+ color_offset = color * shading_line_pixels;
+ if ( ms->word == 1 )
+ color_offset *=2;
+
+ for (i = 0; i < shading_line_pixels; i++)
+ {
+ value = 0;
+ for (line = 0; line < md->shading_length; line++)
+ {
+ current_byte = buf + ( line * shading_data_bytes )
+ + color_offset + i;
+ accu = *current_byte;
+
+ /* word shading data: the lower bytes per line and color are */
+ /* transfered first in one block and then the high bytes */
+ /* in one block */
+ /* the dark shading data is also 10 bit, but only the */
+ /* low byte is transferred (ms->word = 0) */
+ if ( ms->word == 1 )
+ {
+ current_byte = buf + ( line * shading_data_bytes )
+ + color_offset + shading_line_pixels + i;
+ accu += ( *current_byte * 256 );
+ }
+ *( sortbuf + line ) = accu;
+ }
+/* this is the Median filter: sort the values ascending and take the middlest */
+ qsort(sortbuf, md->shading_length, sizeof(float),
+ (qsortfunc)compare_func_16);
+ value = *( sortbuf + ( md->shading_length - 1 ) / 2 );
+ *shading_table_pointer = (uint8_t) (value / factor);
+ shading_table_pointer++;
+ }
+ if ( ms->mode != MS_MODE_COLOR )
+ break;
+ }
+ return status;
+}
+
+
+
+/*---------- get_lut_size() --------------------------------------------------*/
+
+static SANE_Status
+get_lut_size(Microtek2_Info *mi, int *max_lut_size, int *lut_entry_size)
+{
+ /* returns the maximum lookup table size. A device might indicate */
+ /* several lookup table sizes. */
+
+ DBG(30, "get_lut_size: mi=%p\n", (void *) mi);
+
+ *max_lut_size = 0;
+ *lut_entry_size = 0;
+
+ /* Normally this function is used for both gamma and shading tables */
+ /* If, however, the device indicates, that it does not support */
+ /* lookup tables, we set these values as if the device has a maximum */
+ /* bitdepth of 12, and these values are only used to determine the */
+ /* size of the shading table */
+ if ( MI_LUTCAP_NONE(mi->lut_cap) )
+ {
+ *max_lut_size = 4096;
+ *lut_entry_size = 2;
+ }
+
+ if ( mi->lut_cap & MI_LUTCAP_256B )
+ {
+ *max_lut_size = 256;
+ *lut_entry_size = 1;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_1024B )
+ {
+ *max_lut_size = 1024;
+ *lut_entry_size = 1;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_1024W )
+ {
+ *max_lut_size = 1024;
+ *lut_entry_size = 2;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_4096B )
+ {
+ *max_lut_size = 4096;
+ *lut_entry_size = 1;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_4096W )
+ {
+ *max_lut_size = 4096;
+ *lut_entry_size = 2;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_64k_W )
+ {
+ *max_lut_size = 65536;
+ *lut_entry_size = 2;
+ }
+ if ( mi->lut_cap & MI_LUTCAP_16k_W )
+ {
+ *max_lut_size = 16384;
+ *lut_entry_size = 2;
+ }
+ DBG(30, "get_lut_size: mi=%p, lut_size=%d, lut_entry_size=%d\n",
+ (void *) mi, *max_lut_size, *lut_entry_size);
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- calculate_gamma() -----------------------------------------------*/
+
+static SANE_Status
+calculate_gamma(Microtek2_Scanner *ms, uint8_t *pos, int color, char *mode)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ double exp;
+ double mult;
+ double steps;
+ unsigned int val;
+ int i;
+ int factor; /* take into account the differences between the */
+ /* possible values for the color and the number */
+ /* of bits the scanner works with internally. */
+ /* If depth == 1 handle this as if the maximum */
+ /* depth was chosen */
+
+
+ DBG(30, "calculate_gamma: ms=%p, pos=%p, color=%d, mode=%s\n",
+ (void *) ms, pos, color, mode);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ /* does this work everywhere ? */
+ if ( md->model_flags & MD_NO_GAMMA )
+ {
+ factor = 1;
+ mult = (double) (ms->lut_size - 1);
+ }
+ else
+ {
+ if ( mi->depth & MI_HASDEPTH_16 )
+ {
+ factor = ms->lut_size / 65536;
+ mult = 65535.0;
+ }
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ {
+ factor = ms->lut_size / 16384;
+ mult = 16383.0;
+ }
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ {
+ factor = ms->lut_size / 4096;
+ mult = 4095.0;
+ }
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ {
+ factor = ms->lut_size / 1024;
+ mult = 1023.0;
+ }
+ else
+ {
+ factor = ms->lut_size / 256;
+ mult = 255.0;
+ }
+ }
+
+#if 0
+ factor = ms->lut_size / (int) pow(2.0, (double) ms->depth);
+ mult = pow(2.0, (double) ms->depth) - 1.0; /* depending on output size */
+#endif
+
+ steps = (double) (ms->lut_size - 1); /* depending on input size */
+
+ DBG(30, "calculate_gamma: factor=%d, mult =%f, steps=%f, mode=%s\n",
+ factor, mult, steps, ms->val[OPT_GAMMA_MODE].s);
+
+
+ if ( strcmp(mode, MD_GAMMAMODE_SCALAR) == 0 )
+ {
+ int option;
+
+ option = OPT_GAMMA_SCALAR;
+ /* OPT_GAMMA_SCALAR_R follows OPT_GAMMA_SCALAR directly */
+ if ( ms->val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ exp = 1.0 / SANE_UNFIX(ms->val[option].w);
+ else
+ exp = 1.0 / SANE_UNFIX(ms->val[option + color + 1].w);
+
+ for ( i = 0; i < ms->lut_size; i++ )
+ {
+ val = (unsigned int) (mult * pow((double) i / steps, exp) + .5);
+
+ if ( ms->lut_entry_size == 2 )
+ *((uint16_t *) pos + i) = (uint16_t) val;
+ else
+ *((uint8_t *) pos + i) = (uint8_t) val;
+ }
+ }
+ else if ( strcmp(mode, MD_GAMMAMODE_CUSTOM) == 0 )
+ {
+ int option;
+ SANE_Int *src;
+
+ option = OPT_GAMMA_CUSTOM;
+ if ( ms->val[OPT_GAMMA_BIND].w == SANE_TRUE )
+ src = ms->val[option].wa;
+ else
+ src = ms->val[option + color + 1].wa;
+
+ for ( i = 0; i < ms->lut_size; i++ )
+ {
+ if ( ms->lut_entry_size == 2 )
+ *((uint16_t *) pos + i) = (uint16_t) (src[i] / factor);
+ else
+ *((uint8_t *) pos + i) = (uint8_t) (src[i] / factor);
+ }
+ }
+ else if ( strcmp(mode, MD_GAMMAMODE_LINEAR) == 0 )
+ {
+ for ( i = 0; i < ms->lut_size; i++ )
+ {
+ if ( ms->lut_entry_size == 2 )
+ *((uint16_t *) pos + i) = (uint16_t) (i / factor);
+ else
+ *((uint8_t *) pos + i) = (uint8_t) (i / factor);
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- shading_function() ----------------------------------------------*/
+
+static SANE_Status
+shading_function(Microtek2_Scanner *ms, uint8_t *data)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t value;
+ int color;
+ int i;
+
+
+ DBG(40, "shading_function: ms=%p, data=%p\n", (void *) ms, data);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+/* mi = &md->info[MD_SOURCE_FLATBED]; */
+
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "shading_function: wordsize = 1 unsupported\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++)
+ {
+ value = *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i);
+ switch ( mi->shtrnsferequ )
+ {
+ case 0x00:
+ /* output == input */
+ break;
+
+ case 0x01:
+ value = (ms->lut_size * ms->lut_size) / value;
+ *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+ break;
+
+ case 0x11:
+ value = (ms->lut_size * ms->lut_size)
+ / (uint32_t) ( (double) value
+ * ((double) mi->balance[color]
+ / 255.0));
+ *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+ break;
+ case 0x15:
+ value = (uint32_t) ( ( 1073741824 / (double) value )
+ * ( (double) mi->balance[color]
+ / 256.0) );
+ value = MIN(value, (uint32_t)65535);
+ *((uint16_t *) data
+ + color * ( mi->geo_width / mi->calib_divisor ) + i) =
+ (uint16_t) MIN(0xffff, value);
+ break;
+
+ default:
+ DBG(1, "Unsupported shading transfer function 0x%02x\n",
+ mi->shtrnsferequ );
+ break;
+ }
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- set_exposure() --------------------------------------------------*/
+
+static void
+set_exposure(Microtek2_Scanner *ms)
+{
+ /* This function manipulates the colors according to the exposure time */
+ /* settings on models where they are ignored. Currently this seems to */
+ /* be the case for all models with the data format chunky data. They */
+ /* all have tables with two byte gamma output, so for now we ignore */
+ /* gamma tables with one byte output */
+
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ int color;
+ int size;
+ int depth;
+ int maxval;
+ int byte;
+ uint32_t val32;
+ uint8_t *from;
+ uint8_t exposure;
+ uint8_t exposure_rgb[3];
+
+
+ DBG(30, "set_exposure: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ( ms->lut_entry_size == 1 )
+ {
+ DBG(1, "set_exposure: 1 byte gamma output tables currently ignored\n");
+ return;
+ }
+
+ if ( mi->depth & MI_HASDEPTH_16 )
+ depth = 16;
+ else if ( mi->depth & MI_HASDEPTH_14 )
+ depth = 14;
+ else if ( mi->depth & MI_HASDEPTH_12 )
+ depth = 12;
+ else if ( mi->depth & MI_HASDEPTH_10 )
+ depth = 10;
+ else
+ depth = 8;
+
+ maxval = ( 1 << depth ) - 1;
+
+ from = ms->gamma_table;
+ size = ms->lut_size;
+
+ /* first master channel, apply transformation to all colors */
+ exposure = ms->exposure_m;
+ for ( byte = 0; byte < ms->lut_size; byte++ )
+ {
+ for ( color = 0; color < 3; color++)
+ {
+ val32 = (uint32_t) *((uint16_t *) from + color * size + byte);
+ val32 = MIN(val32 + val32
+ * (2 * (uint32_t) exposure / 100), (uint32_t) maxval);
+ *((uint16_t *) from + color * size + byte) = (uint16_t) val32;
+ }
+ }
+
+ /* and now apply transformation to each channel */
+
+ exposure_rgb[0] = ms->exposure_r;
+ exposure_rgb[1] = ms->exposure_g;
+ exposure_rgb[2] = ms->exposure_b;
+ for ( color = 0; color < 3; color++ )
+ {
+ for ( byte = 0; byte < size; byte++ )
+ {
+ val32 = (uint32_t) *((uint16_t *) from + color * size + byte);
+ val32 = MIN(val32 + val32
+ * (2 * (uint32_t) exposure_rgb[color] / 100),
+ (uint32_t) maxval);
+ *((uint16_t *) from + color * size + byte) = (uint16_t) val32;
+ }
+ }
+
+ return;
+}
+
+
+/*---------- reader_process() ------------------------------------------------*/
+
+static int
+reader_process(void *data)
+{
+ Microtek2_Scanner *ms = (Microtek2_Scanner *) data;
+
+ SANE_Status status;
+ Microtek2_Info *mi;
+ Microtek2_Device *md;
+ struct SIGACTION act;
+ sigset_t sigterm_set;
+ static uint8_t *temp_current = NULL;
+
+ DBG(30, "reader_process: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if (sanei_thread_is_forked()) close(ms->fd[0]);
+
+ sigemptyset (&sigterm_set);
+ sigaddset (&sigterm_set, SIGTERM);
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = signal_handler;
+ sigaction (SIGTERM, &act, 0);
+
+ ms->fp = fdopen(ms->fd[1], "w");
+ if ( ms->fp == NULL )
+ {
+ DBG(1, "reader_process: fdopen() failed, errno=%d\n", errno);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ( ms->auto_adjust == 1 )
+ {
+ if ( temp_current == NULL )
+ temp_current = ms->temporary_buffer;
+ }
+
+ while ( ms->src_remaining_lines > 0 )
+ {
+
+ ms->src_lines_to_read = MIN(ms->src_remaining_lines, ms->src_max_lines);
+ ms->transfer_length = ms->src_lines_to_read * ms->bpl;
+
+ DBG(30, "reader_process: transferlength=%d, lines=%d, linelength=%d, "
+ "real_bpl=%d, srcbuf=%p\n", ms->transfer_length,
+ ms->src_lines_to_read, ms->bpl, ms->real_bpl, ms->buf.src_buf);
+
+ sigprocmask (SIG_BLOCK, &sigterm_set, 0);
+ status = scsi_read_image(ms, ms->buf.src_buf, (ms->depth > 8) ? 2 : 1);
+ sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
+ if ( status != SANE_STATUS_GOOD )
+ return SANE_STATUS_IO_ERROR;
+
+ ms->src_remaining_lines -= ms->src_lines_to_read;
+
+ /* prepare data for frontend */
+ switch (ms->mode)
+ {
+ case MS_MODE_COLOR:
+ if ( ! mi->onepass )
+ /* TODO */
+ {
+ DBG(1, "reader_process: 3 pass not yet supported\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ switch ( mi->data_format )
+ {
+ case MI_DATAFMT_CHUNKY:
+ case MI_DATAFMT_9800:
+ status = chunky_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MI_DATAFMT_LPLCONCAT:
+ status = lplconcat_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MI_DATAFMT_LPLSEGREG:
+ status = segreg_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MI_DATAFMT_WORDCHUNKY:
+ status = wordchunky_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ default:
+ DBG(1, "reader_process: format %d\n", mi->data_format);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+ break;
+ case MS_MODE_GRAY:
+ status = gray_proc_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MS_MODE_HALFTONE:
+ case MS_MODE_LINEART:
+ status = proc_onebit_data(ms);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ case MS_MODE_LINEARTFAKE:
+ if ( ms->auto_adjust == 1 )
+ status = auto_adjust_proc_data(ms, &temp_current);
+ else
+ status = lineartfake_proc_data(ms);
+
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ break;
+ default:
+ DBG(1, "reader_process: Unknown scan mode %d\n", ms->mode);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ fclose(ms->fp);
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- chunky_proc_data() ----------------------------------------------*/
+
+static SANE_Status
+chunky_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t line;
+ uint8_t *from;
+ int pad;
+ int bpp; /* bytes per pixel */
+ int bits_pp_in; /* bits per pixel input */
+ int bits_pp_out; /* bits per pixel output */
+ int bpl_ppl_diff;
+
+
+ DBG(30, "chunky_proc_data: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ bits_pp_in = ms->bits_per_pixel_in;
+ bits_pp_out = ms->bits_per_pixel_out;
+ pad = (int) ceil( (double) (ms->ppl * bits_pp_in) / 8.0 ) % 2;
+ bpp = bits_pp_out / 8;
+
+ /* Some models have 3 * ppl + 6 bytes per line if the number of pixels */
+ /* per line is even and 3 * ppl + 3 bytes per line if the number of */
+ /* pixels per line is odd. According to the documentation it should be */
+ /* bpl = 3*ppl (even number of pixels) or bpl=3*ppl+1 (odd number of */
+ /* pixels. Even worse: On different models it is different at which */
+ /* position in a scanline the image data starts. bpl_ppl_diff tries */
+ /* to fix this. */
+
+ if ( (md->model_flags & MD_OFFSET_2) && pad == 1 )
+ bpl_ppl_diff = 2;
+ else
+ bpl_ppl_diff = 0;
+
+#if 0
+ if ( md->revision == 1.00 && mi->model_code != 0x81 )
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ) - pad;
+ else
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp );
+
+ if ( md->revision > 1.00 )
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp );
+ else
+ bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ) - pad;
+#endif
+
+ from = ms->buf.src_buf;
+ from += bpl_ppl_diff;
+
+ DBG(30, "chunky_proc_data: lines=%d, bpl=%d, ppl=%d, bpp=%d, depth=%d"
+ " junk=%d\n", ms->src_lines_to_read, ms->bpl, ms->ppl,
+ bpp, ms->depth, bpl_ppl_diff);
+
+ for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ )
+ {
+ status = chunky_copy_pixels(ms, from);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ from += ms->bpl;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- chunky_copy_pixels() --------------------------------------------*/
+
+static SANE_Status
+chunky_copy_pixels(Microtek2_Scanner *ms, uint8_t *from)
+{
+ Microtek2_Device *md;
+ uint32_t pixel;
+ int color;
+
+ DBG(30, "chunky_copy_pixels: from=%p, pixels=%d, fp=%p, depth=%d\n",
+ from, ms->ppl, (void *) ms->fp, ms->depth);
+
+ md = ms->dev;
+ if ( ms->depth > 8 )
+ {
+ if ( !( md->model_flags & MD_16BIT_TRANSFER ) )
+ {
+ int scale1;
+ int scale2;
+ uint16_t val16;
+
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ val16 = *( (uint16_t *) from + 3 * pixel + color );
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+ }
+ }
+ else
+ {
+ fwrite((void *) from, 2, 3 * ms->ppl, ms->fp);
+ }
+ }
+ else if ( ms->depth == 8 )
+ {
+ fwrite((void *) from, 1, 3 * ms->ppl, ms->fp);
+ }
+ else
+ {
+ DBG(1, "chunky_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- segreg_proc_data() ----------------------------------------------*/
+
+static SANE_Status
+segreg_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ char colormap[] = "RGB";
+ uint8_t *from;
+ uint32_t lines_to_deliver;
+ int bpp; /* bytes per pixel */
+ int bpf; /* bytes per frame including color indicator */
+ int pad;
+ int colseq2;
+ int color;
+ int save_current_src;
+ int frame;
+ int right_to_left;
+
+ DBG(30, "segreg_proc_data: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ /* take a trailing junk byte into account */
+ pad = (int) ceil( (double) (ms->ppl * ms->bits_per_pixel_in) / 8.0 ) % 2;
+ bpp = ms->bits_per_pixel_out / 8; /* bits_per_pixel_out is either 8 or 16 */
+ bpf = ms->bpl / 3;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ DBG(30, "segreg_proc_data: lines=%d, bpl=%d, ppl=%d, bpf=%d, bpp=%d,\n"
+ "depth=%d, pad=%d, freelines=%d, calib_backend=%d\n",
+ ms->src_lines_to_read, ms->bpl, ms->ppl, bpf, bpp,
+ ms->depth, pad, ms->buf.free_lines, ms->calib_backend);
+
+ /* determine how many planes of each color are in the source buffer */
+ from = ms->buf.src_buf;
+ for ( frame = 0; frame < 3 * ms->src_lines_to_read; frame++, from += bpf )
+ {
+ switch ( *from )
+ {
+ case 'R':
+ ++ms->buf.planes[0][MS_COLOR_RED];
+ break;
+ case 'G':
+ ++ms->buf.planes[0][MS_COLOR_GREEN];
+ break;
+ case 'B':
+ ++ms->buf.planes[0][MS_COLOR_BLUE];
+ break;
+ default:
+ DBG(1, "segreg_proc_data: unknown color indicator (1) "
+ "0x%02x\n", *from);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ ms->buf.free_lines -= ms->src_lines_to_read;
+ save_current_src = ms->buf.current_src;
+ if ( ms->buf.free_lines < ms->src_max_lines )
+ {
+ ms->buf.current_src = ++ms->buf.current_src % 2;
+ ms->buf.src_buf = ms->buf.src_buffer[ms->buf.current_src];
+ ms->buf.free_lines = ms->buf.free_max_lines;
+ }
+ else
+ ms->buf.src_buf += ms->src_lines_to_read * ms->bpl;
+
+ colseq2 = mi->color_sequence[2];
+ lines_to_deliver = ms->buf.planes[0][colseq2] + ms->buf.planes[1][colseq2];
+ if ( lines_to_deliver == 0 )
+ return SANE_STATUS_GOOD;
+
+ DBG(30, "segreg_proc_data: planes[0][0]=%d, planes[0][1]=%d, "
+ "planes[0][2]=%d\n", ms->buf.planes[0][0], ms->buf.planes[0][1],
+ ms->buf.planes[0][2] );
+ DBG(30, "segreg_proc_data: planes[1][0]=%d, planes[1][1]=%d, "
+ "planes[1][2]=%d\n", ms->buf.planes[1][0], ms->buf.planes[1][1],
+ ms->buf.planes[1][2] );
+
+ while ( lines_to_deliver > 0 )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ /* get the position of the next plane for each color */
+ do
+ {
+ if ( *ms->buf.current_pos[color] == colormap[color] )
+ break;
+ ms->buf.current_pos[color] += bpf;
+ } while ( 1 );
+
+ ms->buf.current_pos[color] += 2; /* skip color indicator */
+ }
+
+ status = segreg_copy_pixels(ms);
+ if ( status != SANE_STATUS_GOOD )
+ {
+ DBG(1, "segreg_copy_pixels:status %d\n", status);
+ return status;
+ }
+
+ for ( color = 0; color < 3; color++ )
+ {
+ /* skip a padding byte at the end, if present */
+ ms->buf.current_pos[color] += pad;
+
+ if ( ms->buf.planes[1][color] > 0 )
+ {
+ --ms->buf.planes[1][color];
+ if ( ms->buf.planes[1][color] == 0 )
+ /* we have copied from the prehold buffer and are */
+ /* done now, we continue with the source buffer */
+ ms->buf.current_pos[color] =
+ ms->buf.src_buffer[save_current_src];
+ }
+ else
+ {
+ --ms->buf.planes[0][color];
+ if ( ms->buf.planes[0][color] == 0
+ && ms->buf.current_src != save_current_src )
+
+ ms->buf.current_pos[color] =
+ ms->buf.src_buffer[ms->buf.current_src];
+ }
+ }
+ DBG(100, "planes_to_deliver=%d\n", lines_to_deliver);
+ --lines_to_deliver;
+ }
+
+ if ( ms->buf.current_src != save_current_src )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ ms->buf.planes[1][color] += ms->buf.planes[0][color];
+ ms->buf.planes[0][color] = 0;
+ }
+ }
+
+ DBG(30, "segreg_proc_data: src_buf=%p, free_lines=%d\n",
+ ms->buf.src_buf, ms->buf.free_lines);
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- segreg_copy_pixels() --------------------------------------------*/
+
+static SANE_Status
+segreg_copy_pixels(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t pixel;
+ int color, i, gamma_by_backend, right_to_left, scale1, scale2, bpp_in;
+ float s_w, s_d; /* shading byte from condensed_shading */
+ float val, maxval = 0, shading_factor = 0;
+ uint16_t val16 = 0;
+ uint8_t val8 = 0;
+ uint8_t *from_effective;
+ uint8_t *gamma[3];
+ float f[3]; /* color balance factor */
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+ bpp_in = ( ms->bits_per_pixel_in + 7 ) / 8; /*Bytes per pixel from scanner*/
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend)
+ {
+ maxval = (float) pow(2.0, (float) ms->depth) - 1.0;
+ s_w = maxval;
+ s_d = 0.0;
+ shading_factor = (float) pow(2.0, (double) (md->shading_depth
+ - ms->depth) );
+ }
+
+ if ( gamma_by_backend )
+ {
+ i = (ms->depth > 8) ? 2 : 1;
+ for ( color = 0; color < 3; color++)
+ gamma[color] = ms->gamma_table
+ + i * (int) pow(2.0, (double)ms->depth);
+ }
+
+ DBG(30, "segreg_copy_pixels: pixels=%d\n", ms->ppl);
+ DBG(100, "segreg_copy_pixels: buffer 0x%p, right_to_left=%d, depth=%d\n",
+ (void *) ms->buf.current_pos, right_to_left, ms->depth);
+
+ for (color = 0; color < 3; color++ )
+ f[color] = (float) ms->balance[color] / 100.0;
+
+ DBG(100, "segreg_copy_pixels: color balance:\n"
+ " ms->balance[R]=%d, ms->balance[G]=%d, ms->balance[B]=%d\n",
+ ms->balance[0], ms->balance[1], ms->balance[2]);
+
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ if ( right_to_left )
+ from_effective = ms->buf.current_pos[color]
+ + ( ms->ppl - 1 - pixel ) * bpp_in;
+ else
+ from_effective = ms->buf.current_pos[color] + pixel * bpp_in;
+
+ if ( ms->depth > 8 )
+ val = (float) *(uint16_t *)from_effective;
+ else if ( ms->depth == 8 )
+ val = (float) *from_effective;
+ else
+ {
+ DBG(1, "segreg_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ color,
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+
+
+ if ( s_w == s_d ) s_w = s_d + 1;
+ if ( val < s_d ) val = s_d;
+ val = maxval *( val - s_d ) / ( s_w - s_d );
+
+ val *= f[color];
+
+ /* if scanner doesn't support brightness, contrast */
+ if ( md->model_flags & MD_NO_ENHANCEMENTS )
+ {
+ val += ( ( ms->brightness_m - 128 ) * 2 );
+ val = ( val - 128 ) * ( ms->contrast_m / 128 ) + 128;
+ }
+
+ val = MAX( 0.0, val);
+ val = MIN( maxval, val );
+ }
+
+ val16 = (uint16_t) val;
+ val8 = (uint8_t) val;
+
+ /* apply gamma correction if needed */
+ if ( gamma_by_backend )
+ {
+ if ( ms->depth > 8 )
+ val16 = *((uint16_t *) gamma[color] + val16);
+ else
+ val8 = gamma[color][val8];
+ }
+
+ if ( ms->depth > 8 )
+ {
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+ else
+ {
+ fputc((unsigned char) val8, ms->fp);
+ }
+
+ }
+ }
+ for ( color = 0; color < 3; color++ )
+ {
+ ms->buf.current_pos[color] += ms->ppl;
+ if ( ms->depth > 8 )
+ ms->buf.current_pos[color] += ms->ppl;
+ }
+
+ return SANE_STATUS_GOOD;
+
+}
+
+
+/*---------- lplconcat_proc_data() -------------------------------------------*/
+static SANE_Status
+lplconcat_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t line;
+ uint8_t *from[3];
+ uint8_t *save_from[3];
+ int color;
+ int bpp;
+ int pad;
+ int gamma_by_backend;
+ int right_to_left; /* 0=left to right, 1=right to left */
+
+
+ DBG(30, "lplconcat_proc_data: ms=%p\n", (void *) ms);
+
+ /* This data format seems to honour the color sequence indicator */
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ bpp = ms->bits_per_pixel_out / 8; /* ms->bits_per_pixel_out is 8 or 16 */
+ pad = (ms->ppl * bpp) % 2;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+ gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0;
+
+ if ( right_to_left == 1 )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ from[color] = ms->buf.src_buf
+ + ( mi->color_sequence[color] + 1 ) * ( ms->bpl / 3 )
+ - bpp - (ms->bpl - 3 * ms->ppl * bpp) / 3;
+ }
+ }
+ else
+ for ( color = 0; color < 3; color++ )
+ from[color] = ms->buf.src_buf
+ + mi->color_sequence[color] * ( ms->bpl / 3 );
+
+ for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ )
+ {
+ for ( color = 0 ; color < 3; color++ )
+ save_from[color] = from[color];
+
+ status = lplconcat_copy_pixels(ms,
+ from,
+ right_to_left,
+ gamma_by_backend);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ for ( color = 0; color < 3; color++ )
+ from[color] = save_from[color] + ms->bpl;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- lplconcat_copy_pixels() -----------------------------------------*/
+
+static SANE_Status
+lplconcat_copy_pixels(Microtek2_Scanner *ms,
+ uint8_t **from,
+ int right_to_left,
+ int gamma_by_backend)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t pixel;
+ uint16_t val16 = 0;
+ uint8_t val8 = 0;
+ uint8_t *gamma[3];
+ float s_d; /* dark shading pixel */
+ float s_w; /* white shading pixel */
+ float shading_factor = 0;
+ float f[3]; /* color balance factor */
+ float val, maxval = 0;
+ int color;
+ int step, scale1, scale2;
+ int i;
+
+
+ DBG(30, "lplconcat_copy_pixels: ms=%p, righttoleft=%d, gamma=%d,\n",
+ (void *) ms, right_to_left, gamma_by_backend);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend)
+ {
+ shading_factor = (float) pow(2.0,(double)(md->shading_depth - ms->depth));
+ maxval = (float) pow(2.0, (double) ms->depth) - 1.0;
+ s_w = maxval;
+ s_d = 0.0;
+ }
+
+ step = ( right_to_left == 1 ) ? -1 : 1;
+ if ( ms->depth > 8 ) step *= 2;
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+
+ if ( gamma_by_backend )
+ {
+ i = ( ms->depth > 8 ) ? 2 : 1;
+ for ( color = 0; color < 3; color++ )
+ gamma[color] = ms->gamma_table + i * (int) pow(2.0,(double)ms->depth);
+ }
+
+ for (color = 0; color < 3; color++ )
+ f[color] = (float)ms->balance[color] / 100.0;
+
+ DBG(100, "lplconcat_copy_pixels: color balance:\n"
+ " ms->balance[R]=%d, ms->balance[G]=%d, ms->balance[B]=%d\n",
+ ms->balance[0], ms->balance[1], ms->balance[2]);
+
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ if ( ms->depth > 8 )
+ val = (float) *(uint16_t *) from[color];
+ else if ( ms->depth == 8 )
+ val = (float) *from[color];
+ else
+ {
+ DBG(1, "lplconcat_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ mi->color_sequence[color],
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+
+ if ( val < s_d ) val = s_d;
+ if ( s_w == s_d ) s_w = s_d + 1;
+ val = ( maxval * ( val - s_d ) ) / (s_w - s_d);
+
+ val *= f[color]; /* apply color balance */
+
+ /* if scanner doesn't support brightness, contrast ... */
+ if ( md->model_flags & MD_NO_ENHANCEMENTS )
+ {
+ val += ( ( ms->brightness_m - 128 ) * 2 );
+ val = ( val - 128 ) * ( ms->contrast_m / 128 ) + 128;
+ }
+
+ if ( val > maxval ) val = maxval;
+ if ( val < 0.0 ) val = 0.0;
+ }
+
+ val16 = (uint16_t) val;
+ val8 = (uint8_t) val;
+
+ /* apply gamma correction if needed */
+ if ( gamma_by_backend )
+ {
+ if ( ms->depth > 8 )
+ val16 = *((uint16_t *) gamma[color] + val16);
+ else
+ val8 = gamma[color][val8];
+ }
+
+ if ( ms->depth > 8 )
+ {
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+ else
+ {
+ fputc((unsigned char) val8, ms->fp);
+ }
+ from[color] += step;
+ }
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/*---------- wordchunky_proc_data() ------------------------------------------*/
+
+static SANE_Status
+wordchunky_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ uint8_t *from;
+ uint32_t line;
+
+
+ DBG(30, "wordchunky_proc_data: ms=%p\n", (void *) ms);
+
+ from = ms->buf.src_buf;
+ for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ )
+ {
+ status = wordchunky_copy_pixels(from, ms->ppl, ms->depth, ms->fp);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+ from += ms->bpl;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- wordchunky_copy_pixels() ----------------------------------------*/
+
+static SANE_Status
+wordchunky_copy_pixels(uint8_t *from, uint32_t pixels, int depth, FILE *fp)
+{
+ uint32_t pixel;
+ int color;
+
+ DBG(30, "wordchunky_copy_pixels: from=%p, pixels=%d, depth=%d\n",
+ from, pixels, depth);
+
+ if ( depth > 8 )
+ {
+ int scale1;
+ int scale2;
+ uint16_t val16;
+
+ scale1 = 16 - depth;
+ scale2 = 2 * depth - 16;
+ for ( pixel = 0; pixel < pixels; pixel++ )
+ {
+ for ( color = 0; color < 3; color++ )
+ {
+ val16 = *(uint16_t *) from;
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, fp);
+ from += 2;
+ }
+ }
+ }
+ else if ( depth == 8 )
+ {
+ pixel = 0;
+ do
+ {
+ fputc((char ) *from, fp);
+ fputc((char) *(from + 2), fp);
+ fputc((char) *(from + 4), fp);
+ ++pixel;
+ if ( pixel < pixels )
+ {
+ fputc((char) *(from + 1), fp);
+ fputc((char) *(from + 3), fp);
+ fputc((char) *(from + 5), fp);
+ ++pixel;
+ }
+ from += 6;
+ } while ( pixel < pixels );
+ }
+ else
+ {
+ DBG(1, "wordchunky_copy_pixels: Unknown depth %d\n", depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- gray_proc_data() ------------------------------------------------*/
+
+static SANE_Status
+gray_proc_data(Microtek2_Scanner *ms)
+{
+ SANE_Status status;
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint8_t *from;
+ int gamma_by_backend, bpp;
+ int right_to_left; /* for scanning direction */
+
+
+ DBG(30, "gray_proc_data: lines=%d, bpl=%d, ppl=%d, depth=%d\n",
+ ms->src_lines_to_read, ms->bpl, ms->ppl, ms->depth);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+
+ gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+ bpp = ( ms->bits_per_pixel_in + 7 ) / 8;
+
+ if ( right_to_left == 1 )
+ from = ms->buf.src_buf + ms->ppl * bpp - bpp;
+ else
+ from = ms->buf.src_buf;
+
+ do
+ {
+ status = gray_copy_pixels(ms,
+ from,
+ right_to_left,
+ gamma_by_backend);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ from += ms->bpl;
+ --ms->src_lines_to_read;
+ } while ( ms->src_lines_to_read > 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- gray_copy_pixels() ----------------------------------------------*/
+
+static SANE_Status
+gray_copy_pixels(Microtek2_Scanner *ms,
+ uint8_t *from,
+ int right_to_left,
+ int gamma_by_backend)
+{
+ Microtek2_Device *md;
+ uint32_t pixel;
+ uint16_t val16;
+ uint8_t val8;
+ int step, scale1, scale2;
+ float val, maxval = 0;
+ float s_w, s_d, shading_factor = 0;
+
+ DBG(30, "gray_copy_pixels: pixels=%d, from=%p, fp=%p, depth=%d\n",
+ ms->ppl, from, (void *) ms->fp, ms->depth);
+
+ md = ms->dev;
+ step = right_to_left == 1 ? -1 : 1;
+ if ( ms->depth > 8 ) step *= 2;
+ val = 0;
+ scale1 = 16 - ms->depth;
+ scale2 = 2 * ms->depth - 16;
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend)
+ {
+ maxval = (float) pow(2.0, (float) ms->depth) - 1.0;
+ s_w = maxval;
+ s_d = 0.0;
+ shading_factor = (float) pow(2.0, (double) (md->shading_depth - ms->depth) );
+ }
+
+ if ( ms->depth >= 8 )
+ {
+ for ( pixel = 0; pixel < ms->ppl; pixel++ )
+ {
+ if ( ms->depth > 8 )
+ val = (float) *(uint16_t *) from;
+ if ( ms->depth == 8 )
+ val = (float) *from;
+
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ 0,
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+
+ if ( val < s_d ) val = s_d;
+ val = ( val - s_d ) * maxval / (s_w - s_d );
+ val = MAX( 0.0, val );
+ val = MIN( maxval, val );
+ }
+
+ if ( ms->depth > 8 )
+ {
+ val16 = (uint16_t) val;
+ if ( gamma_by_backend )
+ val16 = *((uint16_t *) ms->gamma_table + val16);
+ if ( !( md->model_flags & MD_16BIT_TRANSFER ) )
+ val16 = ( val16 << scale1 ) | ( val16 >> scale2 );
+ fwrite((void *) &val16, 2, 1, ms->fp);
+ }
+
+ if ( ms->depth == 8 )
+ {
+ val8 = (uint8_t) val;
+ if ( gamma_by_backend )
+ val8 = ms->gamma_table[(int)val8];
+ fputc((char)val8, ms->fp);
+ }
+ from += step;
+ }
+ }
+ else if ( ms->depth == 4 )
+ {
+ pixel = 0;
+ while ( pixel < ms->ppl )
+ {
+ fputc((char) ( ((*from >> 4) & 0x0f) | (*from & 0xf0) ), ms->fp);
+ ++pixel;
+ if ( pixel < ms->ppl )
+ fputc((char) ((*from & 0x0f) | ((*from << 4) & 0xf0)), ms->fp);
+ from += step;
+ ++pixel;
+ }
+ }
+ else
+ {
+ DBG(1, "gray_copy_pixels: Unknown depth %d\n", ms->depth);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- proc_onebit_data() ----------------------------------------------*/
+
+static SANE_Status
+proc_onebit_data(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ uint32_t bytes_to_copy; /* bytes per line to copy */
+ uint32_t line;
+ uint32_t byte;
+ uint32_t ppl;
+ uint8_t *from;
+ uint8_t to;
+ int right_to_left;
+ int bit;
+ int toindex;
+
+
+ DBG(30, "proc_onebit_data: ms=%p\n", (void *) ms);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ from = ms->buf.src_buf;
+ bytes_to_copy = ( ms->ppl + 7 ) / 8 ;
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ DBG(30, "proc_onebit_data: bytes_to_copy=%d, lines=%d\n",
+ bytes_to_copy, ms->src_lines_to_read);
+
+ line = 0;
+ to = 0;
+ do
+ {
+ /* in onebit mode black and white colors are inverted */
+ if ( right_to_left )
+ {
+ /* If the direction is right_to_left, we must skip some */
+ /* trailing bits at the end of the scan line and invert the */
+ /* bit sequence. We copy 8 bits into a byte, but these bits */
+ /* are normally not byte aligned. */
+
+ /* Determine the position of the first bit to copy */
+ ppl = ms->ppl;
+ byte = ( ppl + 7 ) / 8 - 1;
+ bit = ppl % 8 - 1;
+ to = 0;
+ toindex = 8;
+
+ while ( ppl > 0 )
+ {
+ to |= ( ( from[byte] >> (7 - bit) ) & 0x01);
+ --toindex;
+ if ( toindex == 0 )
+ {
+ fputc( (char) ~to, ms->fp);
+ toindex = 8;
+ to = 0;
+ }
+ else
+ to <<= 1;
+
+ --bit;
+ if ( bit < 0 )
+ {
+ bit = 7;
+ --byte;
+ }
+ --ppl;
+ }
+ /* print the last byte of the line, if it was not */
+ /* completely filled */
+ bit = ms->ppl % 8;
+ if ( bit != 0 )
+ fputc( (char) ~(to << (7 - bit)), ms->fp);
+ }
+ else
+ for ( byte = 0; byte < bytes_to_copy; byte++ )
+ fputc( (char) ~from[byte], ms->fp);
+
+ from += ms->bpl;
+
+ } while ( ++line < (uint32_t) ms->src_lines_to_read );
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/*---------- lineartfake_proc_data() -----------------------------------------*/
+
+static SANE_Status
+lineartfake_proc_data(Microtek2_Scanner *ms)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ SANE_Status status;
+ uint8_t *from;
+ int right_to_left;
+
+
+ DBG(30, "lineartfake_proc_data: lines=%d, bpl=%d, ppl=%d, depth=%d\n",
+ ms->src_lines_to_read, ms->bpl, ms->ppl, ms->depth);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ if ( right_to_left == 1 )
+ from = ms->buf.src_buf + ms->ppl - 1;
+ else
+ from = ms->buf.src_buf;
+
+ do
+ {
+ status = lineartfake_copy_pixels(ms,
+ from,
+ ms->ppl,
+ ms->threshold,
+ right_to_left,
+ ms->fp);
+ if ( status != SANE_STATUS_GOOD )
+ return status;
+
+ from += ms->bpl;
+ --ms->src_lines_to_read;
+ } while ( ms->src_lines_to_read > 0 );
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- lineartfake_copy_pixels() ---------------------------------------*/
+
+static SANE_Status
+lineartfake_copy_pixels(Microtek2_Scanner *ms,
+ uint8_t *from,
+ uint32_t pixels,
+ uint8_t threshold,
+ int right_to_left,
+ FILE *fp)
+{
+ Microtek2_Device *md;
+ uint32_t pixel;
+ uint32_t bit;
+ uint8_t dest;
+ uint8_t val;
+ float s_d, s_w, maxval, shading_factor, grayval;
+ int step;
+
+
+ DBG(30, "lineartfake_copy_pixels: from=%p,pixels=%d,threshold=%d,file=%p\n",
+ from, pixels, threshold, (void *) fp);
+ md = ms->dev;
+ bit = 0;
+ dest = 0;
+ step = right_to_left == 1 ? -1 : 1;
+ maxval = 255.0;
+ s_w = maxval;
+ s_d = 0.0;
+ shading_factor = (float) pow(2.0, (double) (md->shading_depth - 8) );
+
+ for ( pixel = 0; pixel < pixels; pixel++ )
+ {
+ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend
+ && ( ms->condensed_shading_w != NULL ))
+ /* apply shading by backend */
+ {
+ get_cshading_values(ms,
+ 0,
+ pixel,
+ shading_factor,
+ right_to_left,
+ &s_d,
+ &s_w);
+ }
+ else /* no shading */
+ {
+ s_w = maxval;
+ s_d = 0.0;
+ }
+
+ grayval = (float) *from;
+
+ if ( grayval < s_d ) grayval = s_d;
+ grayval = ( grayval - s_d ) * maxval / (s_w - s_d );
+ grayval = MAX( 0.0, grayval );
+ grayval = MIN( maxval, grayval );
+
+ if ( (uint8_t)grayval < threshold ) val = 1; else val = 0;
+ dest = ( dest << 1 ) | val;
+ bit = ( bit + 1 ) % 8;
+ if ( bit == 0 ) /* 8 input bytes processed */
+ {
+ fputc((char) dest, fp);
+ dest = 0;
+ }
+ from += step;
+ }
+
+ if ( bit != 0 )
+ {
+ dest <<= 7 - bit;
+ fputc((char) dest, fp);
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+/*---------- auto_adjust_proc_data() -----------------------------------------*/
+
+static SANE_Status
+auto_adjust_proc_data(Microtek2_Scanner *ms, uint8_t **temp_current)
+{
+ Microtek2_Device *md;
+ Microtek2_Info *mi;
+ SANE_Status status;
+ uint8_t *from;
+ uint32_t line;
+ uint32_t lines;
+ uint32_t pixel;
+ uint32_t threshold;
+ int right_to_left;
+
+
+ DBG(30, "auto_adjust_proc_data: ms=%p, temp_current=%p\n",
+ (void *) ms, *temp_current);
+
+ md = ms->dev;
+ mi = &md->info[md->scan_source];
+ right_to_left = mi->direction & MI_DATSEQ_RTOL;
+
+ memcpy(*temp_current, ms->buf.src_buf, ms->transfer_length);
+ *temp_current += ms->transfer_length;
+ threshold = 0;
+ status = SANE_STATUS_GOOD;
+
+ if ( ms->src_remaining_lines == 0 ) /* we have read all the image data, */
+ { /* calculate threshold value */
+ for ( pixel = 0; pixel < ms->remaining_bytes; pixel++ )
+ threshold += *(ms->temporary_buffer + pixel);
+
+ threshold /= ms->remaining_bytes;
+ lines = ms->remaining_bytes / ms->bpl;
+ for ( line = 0; line < lines; line++ )
+ {
+ from = ms->temporary_buffer + line * ms->bpl;
+ if ( right_to_left == 1 )
+ from += ms->ppl - 1;
+ status = lineartfake_copy_pixels(ms,
+ from,
+ ms->ppl,
+ (uint8_t) threshold,
+ right_to_left,
+ ms->fp);
+ }
+ *temp_current = NULL;
+ }
+
+ return status;
+}
+
+/*-------------- get_cshading_values -----------------------------------------*/
+
+static SANE_Status
+get_cshading_values(Microtek2_Scanner *ms,
+ uint8_t color,
+ uint32_t pixel,
+ float shading_factor,
+ int right_to_left,
+ float *s_d,
+ float *s_w)
+{
+ Microtek2_Device *md;
+ uint32_t csh_offset;
+
+ md = ms->dev;
+
+ if ( right_to_left == 1 )
+ csh_offset = (color + 1) * ms->ppl - 1 - pixel;
+ else
+ csh_offset = color * ms->ppl + pixel;
+
+ if ( ( md->shading_depth > 8 ) && ( ms->lut_entry_size == 2) )
+ /* condensed shading is 2 byte color data */
+ {
+ if ( ms->condensed_shading_d != NULL )
+ *s_d = (float) *( (uint16_t *)ms->condensed_shading_d
+ + csh_offset );
+ else
+ *s_d = 0.0;
+
+ *s_w = (float) *( (uint16_t *)ms->condensed_shading_w
+ + csh_offset );
+ *s_w /= shading_factor;
+ *s_d /= shading_factor;
+ }
+
+ else
+ /* condensed shading is 8 bit data */
+ {
+ *s_w = (float) *( ms->condensed_shading_w + csh_offset );
+ if ( ms->condensed_shading_d != NULL )
+ *s_d = (float) *( ms->condensed_shading_d + csh_offset );
+ else
+ *s_d = 0.0;
+ }
+ return SANE_STATUS_GOOD;
+}