summaryrefslogtreecommitdiff
path: root/backend/microtek.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/microtek.c')
-rw-r--r--backend/microtek.c4187
1 files changed, 4187 insertions, 0 deletions
diff --git a/backend/microtek.c b/backend/microtek.c
new file mode 100644
index 0000000..ff82df4
--- /dev/null
+++ b/backend/microtek.c
@@ -0,0 +1,4187 @@
+/***************************************************************************
+ * SANE - Scanner Access Now Easy.
+
+ microtek.c
+
+ This file Copyright 2002 Matthew Marjanovic
+
+ 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.
+
+ (feedback to: mtek-bugs@mir.com)
+ (for latest info: http://www.mir.com/mtek/)
+
+ ***************************************************************************/
+
+
+#define MICROTEK_MAJOR 0
+#define MICROTEK_MINOR 13
+#define MICROTEK_PATCH 1
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "../include/_stdint.h"
+
+#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"
+
+#define BACKEND_NAME microtek
+#include "../include/sane/sanei_backend.h"
+
+#include "microtek.h"
+
+
+#define MICROTEK_CONFIG_FILE "microtek.conf"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+
+#define SCSI_BUFF_SIZE sanei_scsi_max_request_size
+
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+static int num_devices = 0;
+static Microtek_Device *first_dev = NULL; /* list of known devices */
+static Microtek_Scanner *first_handle = NULL; /* list of open scanners */
+static const SANE_Device **devlist = NULL; /* sane_get_devices() */
+
+
+static SANE_Bool inhibit_clever_precal = SANE_FALSE;
+static SANE_Bool inhibit_real_calib = SANE_FALSE;
+
+
+#define M_GSS_WAIT 5 /* seconds */
+
+#define M_LINEART SANE_VALUE_SCAN_MODE_LINEART
+#define M_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE
+#define M_GRAY SANE_VALUE_SCAN_MODE_GRAY
+#define M_COLOR SANE_VALUE_SCAN_MODE_COLOR
+
+#define M_OPAQUE "Opaque/Normal"
+#define M_TRANS "Transparency"
+#define M_AUTOFEED "AutoFeeder"
+
+#define M_NONE "None"
+#define M_SCALAR "Scalar"
+#define M_TABLE "Table"
+
+static SANE_String_Const gamma_mode_list[4] = {
+ M_NONE,
+ M_SCALAR,
+ M_TABLE,
+ NULL
+};
+
+
+/* These are for the E6. Does this hold for other models? */
+static SANE_String_Const halftone_mode_list[13] = {
+ " 1 53-dot screen (53 gray levels)",
+ " 2 Horiz. screen (65 gray levels)",
+ " 3 Vert. screen (65 gray levels)",
+ " 4 Mixed page (33 gray levels)",
+ " 5 71-dot screen (29 gray levels)",
+ " 6 60-dot #1 (26 gray levels)",
+ " 7 60-dot #2 (26 gray levels)",
+ " 8 Fine detail #1 (17 gray levels)",
+ " 9 Fine detail #2 (17 gray levels)",
+ "10 Slant line (17 gray levels)",
+ "11 Posterizing (10 gray levels)",
+ "12 High Contrast (5 gray levels)",
+ NULL
+};
+
+
+
+static SANE_Range speed_range = {1, 7, 1};
+
+static SANE_Range brightness_range = {-100, 100, 1};
+/*static SANE_Range brightness_range = {0, 255, 1};*/
+/*static SANE_Range exposure_range = {-18, 21, 3};*/
+/*static SANE_Range contrast_range = {-42, 49, 7};*/
+static SANE_Range u8_range = {0, 255, 1};
+static SANE_Range analog_gamma_range =
+{ SANE_FIX(0.1), SANE_FIX(4.0), SANE_FIX(0) };
+
+
+
+
+#define MAX_MDBG_LENGTH 1024
+static char _mdebug_string[MAX_MDBG_LENGTH];
+
+static void MDBG_INIT(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(_mdebug_string, MAX_MDBG_LENGTH, format, ap);
+ va_end(ap);
+}
+
+static void MDBG_ADD(const char *format, ...)
+{
+ int len = strlen(_mdebug_string);
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(_mdebug_string+len, MAX_MDBG_LENGTH-len, format, ap);
+ va_end(ap);
+}
+
+static void MDBG_FINISH(int dbglvl)
+{
+ DBG(dbglvl, "%s\n", _mdebug_string);
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/*** Utility Functions **********************************************/
+/********************************************************************/
+/********************************************************************/
+
+static size_t max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i) {
+ size = strlen(strings[i]) + 1;
+ if (size > max_size) max_size = size;
+ }
+ return max_size;
+}
+
+
+
+/********************************************************************/
+/* Allocate/create a new ring buffer */
+/********************************************************************/
+static ring_buffer *
+ring_alloc (size_t initial_size, size_t bpl, size_t ppl)
+{
+ ring_buffer *rb;
+ uint8_t *buff;
+
+ if ((rb = (ring_buffer *)malloc(sizeof(*rb))) == NULL)
+ return NULL;
+ if ((buff = (uint8_t *)malloc(initial_size * sizeof(*buff))) == NULL) {
+ free(rb);
+ return NULL;
+ }
+ rb->base = buff;
+ rb->size = initial_size;
+ rb->initial_size = initial_size;
+
+ rb->bpl = bpl;
+ rb->ppl = ppl;
+
+ rb->tail_red = 0;
+ rb->tail_green = 1;
+ rb->tail_blue = 2;
+ rb->head_complete = 0;
+
+ rb->red_extra = 0;
+ rb->green_extra = 0;
+ rb->blue_extra = 0;
+ rb->complete_count = 0;
+
+ return rb;
+}
+
+
+/********************************************************************/
+/* Enlarge an existing ring buffer */
+/********************************************************************/
+static SANE_Status
+ring_expand (ring_buffer *rb, size_t amount)
+{
+ uint8_t *buff;
+ size_t oldsize;
+
+ if (rb == NULL) return SANE_STATUS_INVAL;
+ buff = (uint8_t *)realloc(rb->base, (rb->size + amount) * sizeof(*buff));
+ if (buff == NULL) return SANE_STATUS_NO_MEM;
+
+ rb->base = buff;
+ oldsize = rb->size;
+ rb->size += amount;
+
+ DBG(23, "ring_expand: old, new, inc size: %lu, %lu, %lu\n",
+ (u_long)oldsize, (u_long)rb->size, (u_long)amount);
+ DBG(23, "ring_expand: old tr: %lu tg: %lu tb: %lu hc: %lu\n",
+ (u_long)rb->tail_red, (u_long)rb->tail_green,
+ (u_long)rb->tail_blue, (u_long)rb->head_complete);
+ /* if necessary, move data and fix up them pointers */
+ /* (will break subtly if ring is filled with G or B bytes,
+ and tail_g or tail_b have rolled over...) */
+ if (((rb->complete_count) ||
+ (rb->red_extra) ||
+ (rb->green_extra) ||
+ (rb->blue_extra)) && ((rb->tail_red <= rb->head_complete) ||
+ (rb->tail_green <= rb->head_complete) ||
+ (rb->tail_blue <= rb->head_complete))) {
+ memmove(rb->base + rb->head_complete + amount,
+ rb->base + rb->head_complete,
+ oldsize - rb->head_complete);
+ if ((rb->tail_red > rb->head_complete) ||
+ ((rb->tail_red == rb->head_complete) &&
+ !(rb->complete_count) && !(rb->red_extra)))
+ rb->tail_red += amount;
+ if ((rb->tail_green > rb->head_complete) ||
+ ((rb->tail_green == rb->head_complete) &&
+ !(rb->complete_count) && !(rb->green_extra)))
+ rb->tail_green += amount;
+ if ((rb->tail_blue > rb->head_complete) ||
+ ((rb->tail_blue == rb->head_complete) &&
+ !(rb->complete_count) && !(rb->blue_extra)))
+ rb->tail_blue += amount;
+ rb->head_complete += amount;
+ }
+ DBG(23, "ring_expand: new tr: %lu tg: %lu tb: %lu hc: %lu\n",
+ (u_long)rb->tail_red, (u_long)rb->tail_green,
+ (u_long)rb->tail_blue, (u_long)rb->head_complete);
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Deallocate a ring buffer */
+/********************************************************************/
+static void
+ring_free (ring_buffer *rb)
+{
+ free(rb->base);
+ free(rb);
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/*** Basic SCSI Commands ********************************************/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* parse sense from scsi error */
+/* (even though microtek sense codes are non-standard and */
+/* typically misinterpreted/munged by the low-level scsi driver) */
+/********************************************************************/
+static SANE_Status
+sense_handler (int scsi_fd, u_char *sense, void *arg)
+{
+ int *sense_flags = (int *)arg;
+ SANE_Status stat;
+
+ DBG(10, "SENSE! fd = %d\n", scsi_fd);
+ DBG(10, "sense = %02x %02x %02x %02x.\n",
+ sense[0], sense[1], sense[2], sense[3]);
+ switch(sense[0]) {
+ case 0x00:
+ return SANE_STATUS_GOOD;
+ case 0x81: /* COMMAND/DATA ERROR */
+ stat = SANE_STATUS_GOOD;
+ if (sense[1] & 0x01) {
+ if ((sense_flags != NULL) && (*sense_flags & MS_SENSE_IGNORE))
+ DBG(10, "sense: ERR_SCSICMD -- ignored\n");
+ else {
+ DBG(10, "sense: ERR_SCSICMD\n");
+ stat = SANE_STATUS_IO_ERROR;
+ }
+ }
+ if (sense[1] & 0x02) {
+ DBG(10, "sense: ERR_TOOMANY\n");
+ stat = SANE_STATUS_IO_ERROR;
+ }
+ return stat;
+ case 0x82 : /* SCANNER HARDWARE ERROR */
+ if (sense[1] & 0x01) DBG(10, "sense: ERR_CPURAMFAIL\n");
+ if (sense[1] & 0x02) DBG(10, "sense: ERR_SYSRAMFAIL\n");
+ if (sense[1] & 0x04) DBG(10, "sense: ERR_IMGRAMFAIL\n");
+ if (sense[1] & 0x10) DBG(10, "sense: ERR_CALIBRATE\n");
+ if (sense[1] & 0x20) DBG(10, "sense: ERR_LAMPFAIL\n");
+ if (sense[1] & 0x40) DBG(10, "sense: ERR_MOTORFAIL\n");
+ if (sense[1] & 0x80) DBG(10, "sense: ERR_FEEDERFAIL\n");
+ if (sense[2] & 0x01) DBG(10, "sense: ERR_POWERFAIL\n");
+ if (sense[2] & 0x02) DBG(10, "sense: ERR_ILAMPFAIL\n");
+ if (sense[2] & 0x04) DBG(10, "sense: ERR_IMOTORFAIL\n");
+ if (sense[2] & 0x08) DBG(10, "sense: ERR_PAPERFAIL\n");
+ if (sense[2] & 0x10) DBG(10, "sense: ERR_FILTERFAIL\n");
+ return SANE_STATUS_IO_ERROR;
+ case 0x83 : /* OPERATION ERROR */
+ if (sense[1] & 0x01) DBG(10, "sense: ERR_ILLGRAIN\n");
+ if (sense[1] & 0x02) DBG(10, "sense: ERR_ILLRES\n");
+ if (sense[1] & 0x04) DBG(10, "sense: ERR_ILLCOORD\n");
+ if (sense[1] & 0x10) DBG(10, "sense: ERR_ILLCNTR\n");
+ if (sense[1] & 0x20) DBG(10, "sense: ERR_ILLLENGTH\n");
+ if (sense[1] & 0x40) DBG(10, "sense: ERR_ILLADJUST\n");
+ if (sense[1] & 0x80) DBG(10, "sense: ERR_ILLEXPOSE\n");
+ if (sense[2] & 0x01) DBG(10, "sense: ERR_ILLFILTER\n");
+ if (sense[2] & 0x02) DBG(10, "sense: ERR_NOPAPER\n");
+ if (sense[2] & 0x04) DBG(10, "sense: ERR_ILLTABLE\n");
+ if (sense[2] & 0x08) DBG(10, "sense: ERR_ILLOFFSET\n");
+ if (sense[2] & 0x10) DBG(10, "sense: ERR_ILLBPP\n");
+ return SANE_STATUS_IO_ERROR;
+ default :
+ DBG(10, "sense: unknown error\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* wait (via polling) until scanner seems "ready" */
+/********************************************************************/
+static SANE_Status
+wait_ready(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0, 0, 0, 0, 0, 0 };
+ SANE_Status status;
+ int retry = 0;
+
+ DBG(23, ".wait_ready %d...\n", ms->sfd);
+ while ((status = sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "wait_ready failed (%d)\n", retry);
+ if (retry > 5) return SANE_STATUS_IO_ERROR; /* XXXXXXXX */
+ retry++;
+ sleep(3);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* send scan region coordinates */
+/********************************************************************/
+static SANE_Status
+scanning_frame(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[15] = { 0x04, 0, 0, 0, 0x09, 0 };
+ int x1, y1, x2, y2;
+
+ DBG(23, ".scanning_frame...\n");
+
+ x1 = ms->x1;
+ x2 = ms->x2;
+ y1 = ms->y1;
+ y2 = ms->y2;
+ /* E6 weirdness (other models too?) */
+ if (ms->unit_type == MS_UNIT_18INCH) {
+ x1 /= 2;
+ x2 /= 2;
+ y1 /= 2;
+ y2 /= 2;
+ }
+
+ DBG(23, ".scanning_frame: in- %d,%d %d,%d\n",
+ ms->x1, ms->y1, ms->x2, ms->y2);
+ DBG(23, ".scanning_frame: out- %d,%d %d,%d\n", x1, y1, x2, y2);
+ data = comm + 6;
+ data[0] =
+ ((ms->unit_type == MS_UNIT_PIXELS) ? 0x08 : 0 ) |
+ ((ms->mode == MS_MODE_HALFTONE) ? 0x01 : 0 );
+ data[1] = x1 & 0xFF;
+ data[2] = (x1 >> 8) & 0xFF;
+ data[3] = y1 & 0xFF;
+ data[4] = (y1 >> 8) & 0xFF;
+ data[5] = x2 & 0xFF;
+ data[6] = (x2 >> 8) & 0xFF;
+ data[7] = y2 & 0xFF;
+ data[8] = (y2 >> 8) & 0xFF;
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "SF: ");
+ for (i=0;i<6+0x09;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SF: ");
+ for (i=0;i<6+0x09;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x09, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* send "mode_select" */
+/********************************************************************/
+static SANE_Status
+mode_select(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[19] = { 0x15, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".mode_select %d...\n", ms->sfd);
+ data = comm + 6;
+ data[0] =
+ 0x81 |
+ ((ms->unit_type == MS_UNIT_18INCH) ? 0 : 0x08) |
+ ((ms->res_type == MS_RES_5PER) ? 0 : 0x02);
+ data[1] = ms->resolution_code;
+ data[2] = ms->exposure;
+ data[3] = ms->contrast;
+ data[4] = ms->pattern;
+ data[5] = ms->velocity;
+ data[6] = ms->shadow;
+ data[7] = ms->highlight;
+ DBG(23, ".mode_select: pap_len: %d\n", ms->paper_length);
+ data[8] = ms->paper_length & 0xFF;
+ data[9] = (ms->paper_length >> 8) & 0xFF;
+ data[10] = ms->midtone;
+ /* set command/data length */
+ comm[4] = (ms->midtone_support) ? 0x0B : 0x0A;
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "MSL: ");
+ for (i=0;i<6+comm[4];i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("MSL: ");
+ for (i=0;i<6+comm[4];i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + comm[4], 0, 0);
+}
+
+
+
+/********************************************************************/
+/* send "mode_select_1" */
+/********************************************************************/
+static SANE_Status
+mode_select_1(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[16] = { 0x16, 0, 0, 0, 0x0A, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ DBG(23, ".mode_select_1 %d...\n", ms->sfd);
+ data = comm + 6;
+ data[1] = ms->bright_r;
+ data[3] = ((ms->allow_calibrate) ? 0 : 0x02); /* | 0x01; */
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "MSL1: ");
+ for (i=0;i<6+0x0A;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("MSL1: ");
+ for (i=0;i<6+0x0A;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x0A, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* record mode_sense results in the mode_sense buffer */
+/* (this is to tell if something catastrophic has happened */
+/* to the scanner in-between scans) */
+/********************************************************************/
+static SANE_Status
+save_mode_sense(Microtek_Scanner *ms)
+{
+ uint8_t data[20], comm[6] = { 0x1A, 0, 0, 0, 0, 0};
+ size_t lenp;
+ SANE_Status status;
+ int i;
+
+ DBG(23, ".save_mode_sense %d...\n", ms->sfd);
+ if (ms->onepass) comm[4] = 0x13;
+ else if (ms->midtone_support) comm[4] = 0x0B;
+ else comm[4] = 0x0A;
+ lenp = comm[4];
+
+ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp);
+ for (i=0; i<10; i++) ms->mode_sense_cache[i] = data[i];
+
+ if (DBG_LEVEL >= 192) {
+ unsigned int i;
+#if 0
+ fprintf(stderr, "SMS: ");
+ for (i=0;i<lenp;i++) fprintf(stderr, "%2x ", data[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SMS: ");
+ for (i=0;i<lenp;i++) MDBG_ADD("%2x ", data[i]);
+ MDBG_FINISH(192);
+ }
+
+ return status;
+}
+
+
+/********************************************************************/
+/* read mode_sense and compare to what we saved before */
+/********************************************************************/
+static SANE_Status
+compare_mode_sense(Microtek_Scanner *ms, int *match)
+{
+ uint8_t data[20], comm[6] = { 0x1A, 0, 0, 0, 0, 0};
+ size_t lenp;
+ SANE_Status status;
+ int i;
+
+ DBG(23, ".compare_mode_sense %d...\n", ms->sfd);
+ if (ms->onepass) comm[4] = 0x13;
+ else if (ms->midtone_support) comm[4] = 0x0B;
+ else comm[4] = 0x0A;
+ lenp = comm[4];
+
+ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp);
+ *match = 1;
+ for (i=0; i<10; i++)
+ *match = *match && (ms->mode_sense_cache[i] == data[i]);
+
+ if (DBG_LEVEL >= 192) {
+ unsigned int i;
+#if 0
+ fprintf(stderr, "CMS: ");
+ for (i=0;i<lenp;i++) fprintf(stderr, "%2x(%2x) ",
+ data[i],
+ ms->mode_sense_cache[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("CMS: ");
+ for (i=0;i<lenp;i++) MDBG_ADD("%2x(%2x) ",
+ data[i],
+ ms->mode_sense_cache[i]);
+ MDBG_FINISH(192);
+ }
+
+ return status;
+}
+
+/********************************************************************/
+/* send mode_sense_1, and upset every scsi driver known to mankind */
+/********************************************************************/
+#if 0
+static SANE_Status
+mode_sense_1(Microtek_Scanner *ms)
+{
+ uint8_t *data, comm[36] = { 0x19, 0, 0, 0, 0x1E, 0 };
+
+ DBG(23, ".mode_sense_1...\n");
+ data = comm + 6;
+ memset(data, 0, 30);
+ data[1] = ms->bright_r;
+ data[2] = ms->bright_g;
+ data[3] = ms->bright_b;
+ if (DBG_LEVEL >= 192) {
+ int i;
+ fprintf(stderr, "MS1: ");
+ for (i=0;i<6+0x1E;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x1E, 0, 0);
+}
+#endif
+
+
+
+/********************************************************************/
+/* send "accessory" command */
+/********************************************************************/
+static SANE_Status
+accessory(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0x10, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".accessory...\n");
+ comm[4] =
+ ((ms->useADF) ? 0x41 : 0x40) |
+ ((ms->prescan) ? 0x18 : 0x10) |
+ ((ms->transparency) ? 0x24 : 0x20) |
+ ((ms->allowbacktrack) ? 0x82 : 0x80);
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "AC: ");
+ for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("AC: ");
+ for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* start the scanner a-scannin' */
+/********************************************************************/
+static SANE_Status
+start_scan(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0x1B, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".start_scan...\n");
+ comm[4] =
+ 0x01 | /* "start" */
+ ((ms->expandedresolution) ? 0x80 : 0) |
+ ((ms->multibit) ? 0x40 : 0) |
+ ((ms->onepasscolor) ? 0x20 : 0) |
+ ((ms->reversecolors) ? 0x04 : 0) |
+ ((ms->fastprescan) ? 0x02 : 0) |
+ ((ms->filter == MS_FILT_RED) ? 0x08 : 0) |
+ ((ms->filter == MS_FILT_GREEN) ? 0x10 : 0) |
+ ((ms->filter == MS_FILT_BLUE) ? 0x18 : 0) ;
+
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "SS: ");
+ for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SS: ");
+ for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* stop the scanner a-scannin' */
+/********************************************************************/
+static SANE_Status
+stop_scan(Microtek_Scanner *ms)
+{
+ uint8_t comm[6] = { 0x1B, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".stop_scan...\n");
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "SPS:");
+ for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("SPS:");
+ for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* get scan status */
+/********************************************************************/
+static SANE_Status
+get_scan_status(Microtek_Scanner *ms,
+ SANE_Int *busy,
+ SANE_Int *bytes_per_line,
+ SANE_Int *lines)
+{
+ uint8_t data[6], comm[6] = { 0x0F, 0, 0, 0, 0x06, 0 };
+ SANE_Status status;
+ size_t lenp;
+ int retry = 0;
+
+ DBG(23, ".get_scan_status %d...\n", ms->sfd);
+
+ do {
+ lenp = 6;
+ /* do some retry stuff in here, too */
+ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(20, "get_scan_status: scsi error\n");
+ return status;
+ }
+ *busy = data[0];
+ *bytes_per_line = (data[1]) + (data[2] << 8);
+ *lines = (data[3]) + (data[4] << 8) + (data[5] << 16);
+
+ DBG(20, "get_scan_status(%lu): %d, %d, %d -> #%d\n",
+ (u_long) lenp, *busy, *bytes_per_line, *lines, retry);
+ DBG(20, "> %2x %2x %2x %2x %2x %2x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+ if (*busy != 0) {
+ retry++;
+ DBG(23, "get_scan_status: busy, retry in %d...\n",
+ M_GSS_WAIT * retry);
+ sleep(M_GSS_WAIT * retry);
+ }
+ } while ((*busy != 0) && (retry < 4));
+
+ if (*busy == 0) return status;
+ else return SANE_STATUS_IO_ERROR;
+}
+
+
+
+/********************************************************************/
+/* get scanlines from scanner */
+/********************************************************************/
+static SANE_Status
+read_scan_data(Microtek_Scanner *ms,
+ int lines,
+ uint8_t *buffer,
+ size_t *bufsize)
+{
+ uint8_t comm[6] = { 0x08, 0, 0, 0, 0, 0 };
+
+ DBG(23, ".read_scan_data...\n");
+ comm[2] = (lines >> 16) & 0xFF;
+ comm[3] = (lines >> 8) & 0xFF;
+ comm[4] = (lines) & 0xFF;
+
+ return sanei_scsi_cmd(ms->sfd, comm, 6, buffer, bufsize);
+}
+
+
+
+/********************************************************************/
+/* download color LUT to scanner (if it takes one) */
+/********************************************************************/
+static SANE_Status
+download_gamma(Microtek_Scanner *ms)
+{
+ uint8_t *data, *comm; /* commbytes[10] = { 0x55, 0, 0x27, 0, 0,
+ 0, 0, 0, 0, 0 };*/
+ int i, pl;
+ int commsize;
+ int bit_depth = 8; /* hard-code for now, should match bpp XXXXXXX */
+ int max_entry;
+ SANE_Status status;
+
+ DBG(23, ".download_gamma...\n");
+ /* skip if scanner doesn't take 'em */
+ if (!(ms->gamma_entries)) {
+ DBG(23, ".download_gamma: no entries; skipping\n");
+ return SANE_STATUS_GOOD;
+ }
+ if ((ms->gamma_entry_size != 1) && (ms->gamma_entry_size != 2)) {
+ DBG(23, ".download_gamma: entry size %d?!?!?\n", ms->gamma_entry_size);
+ return SANE_STATUS_INVAL; /* XXXXXXXxx */
+ }
+
+ max_entry = (1 << bit_depth) - 1;
+
+ DBG(23, ".download_gamma: %d entries of %d bytes, max %d\n",
+ ms->gamma_entries, ms->gamma_entry_size, max_entry);
+ commsize = 10 + (ms->gamma_entries * ms->gamma_entry_size);
+ comm = calloc(commsize, sizeof(uint8_t));
+ if (comm == NULL) {
+ DBG(23, ".download_gamma: couldn't allocate %d bytes for comm buffer!\n",
+ commsize);
+ return SANE_STATUS_NO_MEM;
+ }
+ data = comm + 10;
+
+ comm[0] = 0x55;
+ comm[1] = 0;
+ comm[2] = 0x27;
+ comm[3] = 0;
+ comm[4] = 0;
+ comm[5] = 0;
+ comm[6] = 0;
+ comm[7] = ((ms->gamma_entries * ms->gamma_entry_size) >> 8) & 0xFF;
+ comm[8] = (ms->gamma_entries * ms->gamma_entry_size) & 0xFF;
+ comm[9] = (ms->gamma_entry_size == 2) ? 1 : 0;
+
+ if (!(strcmp(ms->val[OPT_CUSTOM_GAMMA].s, M_TABLE))) {
+ /***** Gamma by TABLE *****/
+ int table_shift = (ms->gamma_bit_depth - bit_depth);
+
+ DBG(23, ".download_gamma: by table (%d bpe, %d shift)\n",
+ ms->gamma_bit_depth, table_shift);
+
+ if (ms->val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = ms->gray_lut[i] >> table_shift;
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ } else {
+ pl = 1;
+ do {
+ SANE_Int *pl_lut;
+ switch (pl) {
+ case 1: pl_lut = ms->red_lut; break;
+ case 2: pl_lut = ms->green_lut; break;
+ case 3: pl_lut = ms->blue_lut; break;
+ default:
+ DBG(23, ".download_gamma: uh, exceeded pl bound!\n");
+ free(comm);
+ return SANE_STATUS_INVAL; /* XXXXXXXxx */
+ break;
+ }
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = pl_lut[i] >> table_shift;
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ /* XXXXXXX */
+ comm[9] = (comm[9] & 0x3F) | (pl << 6);
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ pl++;
+ } while ((pl < 4) && (status == SANE_STATUS_GOOD));
+ }
+ } else if (!(strcmp(ms->val[OPT_CUSTOM_GAMMA].s, M_SCALAR))) {
+ /***** Gamma by SCALAR *****/
+ DBG(23, ".download_gamma: by scalar\n");
+ if (ms->val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ double gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA].w);
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = (max_entry *
+ pow((double) i / ((double) ms->gamma_entries - 1.0),
+ 1.0 / gamma));
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ } else {
+ double gamma;
+ pl = 1;
+ do {
+ switch (pl) {
+ case 1: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_R].w); break;
+ case 2: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_G].w); break;
+ case 3: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_B].w); break;
+ default: gamma = 1.0; break; /* should never happen */
+ }
+ for (i=0; i<ms->gamma_entries; i++) {
+ int val = (max_entry *
+ pow((double) i / ((double) ms->gamma_entries - 1.0),
+ 1.0 / gamma));
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val>>8) & 0xFF;
+ break;
+ }
+ }
+ comm[9] = (comm[9] & 0x3F) | (pl << 6);
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ pl++;
+ } while ((pl < 4) && (status == SANE_STATUS_GOOD));
+ }
+ } else {
+ /***** No custom Gamma *****/
+ DBG(23, ".download_gamma: by default\n");
+ for (i=0; i<ms->gamma_entries; i++) {
+ /* int val = (((double) max_entry * (double) i /
+ ((double) ms->gamma_entries - 1.0)) + 0.5); ROUNDING????*/
+ int val =
+ (double) max_entry * (double) i /
+ ((double) ms->gamma_entries - 1.0);
+ switch (ms->gamma_entry_size) {
+ case 1:
+ data[i] = (uint8_t) val;
+ break;
+ case 2:
+ data[i*2] = val & 0xFF;
+ data[(i*2)+1] = (val >> 8) & 0xFF;
+ break;
+ }
+ }
+ status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0);
+ }
+ free(comm);
+ return status;
+}
+
+
+/********************************************************************/
+/* magic command to start calibration */
+/********************************************************************/
+static SANE_Status
+start_calibration(Microtek_Scanner *ms)
+{
+ uint8_t comm[8] = { 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0a };
+
+ DBG(23, ".start_calibrate...\n");
+ if (DBG_LEVEL >= 192) {
+ int i;
+#if 0
+ fprintf(stderr, "STCal:");
+ for (i=0;i<8;i++) fprintf(stderr, "%2x ", comm[i]);
+ fprintf(stderr, "\n");
+#endif
+ MDBG_INIT("STCal:");
+ for (i=0;i<8;i++) MDBG_ADD("%2x ", comm[i]);
+ MDBG_FINISH(192);
+ }
+ return sanei_scsi_cmd(ms->sfd, comm, 8, 0, 0);
+}
+
+
+
+/********************************************************************/
+/* magic command to download calibration values */
+/********************************************************************/
+static SANE_Status
+download_calibration(Microtek_Scanner *ms, uint8_t *comm,
+ uint8_t letter, int linewidth)
+{
+ DBG(23, ".download_calibration... %c %d\n", letter, linewidth);
+
+ comm[0] = 0x0c;
+ comm[1] = 0x00;
+ comm[2] = 0x00;
+ comm[3] = (linewidth >> 8) & 0xFF;
+ comm[4] = linewidth & 0xFF;
+ comm[5] = 0x00;
+
+ comm[6] = 0x00;
+ switch (letter) {
+ case 'R': comm[7] = 0x40; break;
+ case 'G': comm[7] = 0x80; break;
+ case 'B': comm[7] = 0xc0; break;
+ default: /* XXXXXXX */ break;
+ }
+
+ return sanei_scsi_cmd(ms->sfd, comm, 6 + linewidth, 0, 0);
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/* */
+/* Myriad of internal functions */
+/* */
+/********************************************************************/
+/********************************************************************/
+
+
+
+/********************************************************************/
+/* Initialize the options registry */
+/********************************************************************/
+static SANE_Status
+init_options(Microtek_Scanner *ms)
+{
+ int i;
+ SANE_Option_Descriptor *sod = ms->sod;
+ Option_Value *val = ms->val;
+
+ DBG(15, "init_options...\n");
+
+ memset(ms->sod, 0, sizeof(ms->sod));
+ memset(ms->val, 0, sizeof(ms->val));
+ /* default: software selectable word options... */
+ for (i=0; i<NUM_OPTIONS; i++) {
+ sod[i].size = sizeof(SANE_Word);
+ sod[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+
+ 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].unit = SANE_UNIT_NONE;
+ sod[OPT_NUM_OPTS].size = sizeof (SANE_Word);
+ sod[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ sod[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* The Scan Mode Group */
+ sod[OPT_MODE_GROUP].name = "";
+ sod[OPT_MODE_GROUP].title = "Scan Mode";
+ sod[OPT_MODE_GROUP].desc = "";
+ sod[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_MODE_GROUP].cap = 0;
+ sod[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ 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].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ {
+ SANE_String_Const *mode_list;
+ mode_list = (SANE_String_Const *) malloc(5 * sizeof(SANE_String_Const));
+ if (mode_list == NULL) return SANE_STATUS_NO_MEM;
+ i = 0;
+ if (ms->dev->info.modes & MI_MODES_COLOR) mode_list[i++] = M_COLOR;
+ if (ms->dev->info.modes & MI_MODES_GRAY) mode_list[i++] = M_GRAY;
+ if (ms->dev->info.modes & MI_MODES_HALFTONE) mode_list[i++] = M_HALFTONE;
+ if (ms->dev->info.modes & MI_MODES_LINEART) mode_list[i++] = M_LINEART;
+ mode_list[i] = NULL;
+ sod[OPT_MODE].constraint.string_list = mode_list;
+ sod[OPT_MODE].size = max_string_size(mode_list);
+ val[OPT_MODE].s = strdup(mode_list[0]);
+ }
+
+ sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ sod[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
+ sod[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ sod[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ {
+ SANE_Int maxres = ms->dev->info.base_resolution;
+
+ ms->res_range.max = SANE_FIX(maxres);
+ ms->exp_res_range.max = SANE_FIX(2 * maxres);
+ if (ms->dev->info.res_step & MI_RESSTEP_1PER) {
+ DBG(23, "init_options: quant yes\n");
+ ms->res_range.min = SANE_FIX( maxres / 100 );
+ ms->res_range.quant = ms->res_range.min;
+ ms->exp_res_range.min = SANE_FIX(2 * maxres / 100);
+ ms->exp_res_range.quant = ms->exp_res_range.min;
+ } else {
+ /* XXXXXXXXXXX */
+ DBG(23, "init_options: quant no\n");
+ ms->res_range.quant = SANE_FIX( 0 );
+ }
+ sod[OPT_RESOLUTION].constraint.range = &(ms->res_range);
+ }
+ val[OPT_RESOLUTION].w = SANE_FIX(100);
+
+ sod[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
+ sod[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
+ sod[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
+ sod[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
+ sod[OPT_HALFTONE_PATTERN].size = max_string_size(halftone_mode_list);
+ sod[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_mode_list;
+ val[OPT_HALFTONE_PATTERN].s = strdup(halftone_mode_list[0]);
+
+ sod[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
+ sod[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE;
+ sod[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE;
+ sod[OPT_NEGATIVE].type = SANE_TYPE_BOOL;
+ sod[OPT_NEGATIVE].cap |=
+ (ms->dev->info.modes & MI_MODES_NEGATIVE) ? 0 : SANE_CAP_INACTIVE;
+ val[OPT_NEGATIVE].w = SANE_FALSE;
+
+ sod[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
+ sod[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
+ /* sod[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;*/
+ sod[OPT_SPEED].desc = "Scan speed throttle -- higher values are *slower*.";
+ sod[OPT_SPEED].type = SANE_TYPE_INT;
+ sod[OPT_SPEED].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_SPEED].unit = SANE_UNIT_NONE;
+ sod[OPT_SPEED].size = sizeof(SANE_Word);
+ sod[OPT_SPEED].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_SPEED].constraint.range = &speed_range;
+ val[OPT_SPEED].w = 1;
+
+ 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].unit = SANE_UNIT_NONE;
+ sod[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ {
+ SANE_String_Const *source_list;
+ source_list = (SANE_String_Const *) malloc(4 * sizeof(SANE_String_Const));
+ if (source_list == NULL) return SANE_STATUS_NO_MEM;
+ i = 0;
+ source_list[i++] = M_OPAQUE;
+ if (ms->dev->info.source_options & MI_SRC_HAS_TRANS)
+ source_list[i++] = M_TRANS;
+ if (ms->dev->info.source_options & MI_SRC_HAS_FEED)
+ source_list[i++] = M_AUTOFEED;
+ source_list[i] = NULL;
+ sod[OPT_SOURCE].constraint.string_list = source_list;
+ sod[OPT_SOURCE].size = max_string_size(source_list);
+ if (i < 2)
+ sod[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
+ val[OPT_SOURCE].s = strdup(source_list[0]);
+ }
+
+ 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].unit = SANE_UNIT_NONE;
+ sod[OPT_PREVIEW].size = sizeof(SANE_Word);
+ val[OPT_PREVIEW].w = SANE_FALSE;
+
+
+ sod[OPT_GEOMETRY_GROUP].name = "";
+ sod[OPT_GEOMETRY_GROUP].title = "Geometry";
+ sod[OPT_GEOMETRY_GROUP].desc = "";
+ sod[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ 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].type = SANE_TYPE_FIXED;
+ sod[OPT_TL_X].unit = SANE_UNIT_MM;
+ sod[OPT_TL_X].size = sizeof(SANE_Word);
+ sod[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ 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].type = SANE_TYPE_FIXED;
+ sod[OPT_TL_Y].unit = SANE_UNIT_MM;
+ sod[OPT_TL_Y].size = sizeof(SANE_Word);
+ sod[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ 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].type = SANE_TYPE_FIXED;
+ sod[OPT_BR_X].unit = SANE_UNIT_MM;
+ sod[OPT_BR_X].size = sizeof(SANE_Word);
+ sod[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ 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].type = SANE_TYPE_FIXED;
+ sod[OPT_BR_Y].unit = SANE_UNIT_MM;
+ sod[OPT_BR_Y].size = sizeof(SANE_Word);
+ sod[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+
+ sod[OPT_TL_X].constraint.range =
+ sod[OPT_BR_X].constraint.range = &(ms->dev->info.doc_x_range);
+ sod[OPT_TL_Y].constraint.range =
+ sod[OPT_BR_Y].constraint.range = &(ms->dev->info.doc_y_range);
+
+ /* set default scan region to be maximum size */
+ val[OPT_TL_X].w = sod[OPT_TL_X].constraint.range->min;
+ val[OPT_TL_Y].w = sod[OPT_TL_Y].constraint.range->min;
+ val[OPT_BR_X].w = sod[OPT_BR_X].constraint.range->max;
+ val[OPT_BR_Y].w = sod[OPT_BR_Y].constraint.range->max;
+
+ sod[OPT_ENHANCEMENT_GROUP].name = "";
+ sod[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
+ sod[OPT_ENHANCEMENT_GROUP].desc = "";
+ sod[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ sod[OPT_ENHANCEMENT_GROUP].cap = 0;
+ sod[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_EXPOSURE].name = "exposure";
+ sod[OPT_EXPOSURE].title = "Exposure";
+ sod[OPT_EXPOSURE].desc = "Analog exposure control";
+ sod[OPT_EXPOSURE].type = SANE_TYPE_INT;
+ sod[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
+ sod[OPT_EXPOSURE].size = sizeof(SANE_Word);
+ sod[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
+ ms->exposure_range.min = ms->dev->info.min_exposure;
+ ms->exposure_range.max = ms->dev->info.max_exposure;
+ ms->exposure_range.quant = 3;
+ sod[OPT_EXPOSURE].constraint.range = &(ms->exposure_range);
+ val[OPT_EXPOSURE].w = 0;
+
+ sod[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ sod[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ sod[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
+ sod[OPT_BRIGHTNESS].size = sizeof(SANE_Word);
+ sod[OPT_BRIGHTNESS].cap |=
+ ((ms->dev->info.extra_cap & MI_EXCAP_OFF_CTL) ? 0: SANE_CAP_INACTIVE);
+ sod[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ val[OPT_BRIGHTNESS].w = 0;
+
+ sod[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
+ sod[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
+ sod[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
+ sod[OPT_CONTRAST].type = SANE_TYPE_INT;
+ sod[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
+ sod[OPT_CONTRAST].size = sizeof(SANE_Word);
+ sod[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
+ ms->contrast_range.min = ms->dev->info.min_contrast;
+ ms->contrast_range.max = ms->dev->info.max_contrast;
+ ms->contrast_range.quant = 7;
+ sod[OPT_CONTRAST].constraint.range = &(ms->contrast_range);
+ val[OPT_CONTRAST].w = 0;
+
+
+ sod[OPT_HIGHLIGHT].name = SANE_NAME_WHITE_LEVEL;
+ sod[OPT_HIGHLIGHT].title = SANE_TITLE_WHITE_LEVEL;
+ sod[OPT_HIGHLIGHT].desc = SANE_DESC_WHITE_LEVEL;
+ sod[OPT_HIGHLIGHT].type = SANE_TYPE_INT;
+ sod[OPT_HIGHLIGHT].unit = SANE_UNIT_NONE;
+ sod[OPT_HIGHLIGHT].size = sizeof(SANE_Word);
+ sod[OPT_HIGHLIGHT].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_HIGHLIGHT].constraint.range = &u8_range;
+ val[OPT_HIGHLIGHT].w = 255;
+
+ sod[OPT_SHADOW].name = SANE_NAME_BLACK_LEVEL;
+ sod[OPT_SHADOW].title = SANE_TITLE_BLACK_LEVEL;
+ sod[OPT_SHADOW].desc = SANE_DESC_BLACK_LEVEL;
+ sod[OPT_SHADOW].type = SANE_TYPE_INT;
+ sod[OPT_SHADOW].unit = SANE_UNIT_NONE;
+ sod[OPT_SHADOW].size = sizeof(SANE_Word);
+ sod[OPT_SHADOW].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_SHADOW].constraint.range = &u8_range;
+ val[OPT_SHADOW].w = 0;
+
+ if (ms->dev->info.enhance_cap & MI_ENH_CAP_SHADOW) {
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_ADVANCED;
+ sod[OPT_SHADOW].cap |= SANE_CAP_ADVANCED;
+ } else {
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ }
+
+ sod[OPT_MIDTONE].name = "midtone";
+ sod[OPT_MIDTONE].title = "Midtone Level";
+ sod[OPT_MIDTONE].desc = "Midtone Level";
+ sod[OPT_MIDTONE].type = SANE_TYPE_INT;
+ sod[OPT_MIDTONE].unit = SANE_UNIT_NONE;
+ sod[OPT_MIDTONE].size = sizeof(SANE_Word);
+ sod[OPT_MIDTONE].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_MIDTONE].constraint.range = &u8_range;
+ val[OPT_MIDTONE].w = 128;
+ if (ms->midtone_support) {
+ sod[OPT_MIDTONE].cap |= SANE_CAP_ADVANCED;
+ } else {
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ }
+ /* XXXXXXXX is this supported by all scanners??
+ if ((strcmp(M_COLOR, val[OPT_MODE].s)) &&
+ (strcmp(M_GRAY, val[OPT_MODE].s))) {
+ sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE;
+ }
+ */
+
+ sod[OPT_GAMMA_GROUP].name = "";
+ sod[OPT_GAMMA_GROUP].title = "Gamma Control";
+ sod[OPT_GAMMA_GROUP].desc = "";
+ sod[OPT_GAMMA_GROUP].type = SANE_TYPE_GROUP;
+ if (!(ms->gamma_entries))
+ sod[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ sod[OPT_CUSTOM_GAMMA].name = "gamma-mode";
+ sod[OPT_CUSTOM_GAMMA].title = "Gamma Control Mode";
+ sod[OPT_CUSTOM_GAMMA].desc = "How to specify gamma correction, if at all";
+ sod[OPT_CUSTOM_GAMMA].type = SANE_TYPE_STRING;
+ sod[OPT_CUSTOM_GAMMA].size = max_string_size(gamma_mode_list);
+ sod[OPT_CUSTOM_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ sod[OPT_CUSTOM_GAMMA].constraint.string_list = gamma_mode_list;
+ if (!(ms->gamma_entries))
+ sod[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
+ val[OPT_CUSTOM_GAMMA].s = strdup(gamma_mode_list[0]);
+
+ sod[OPT_GAMMA_BIND].name = SANE_NAME_ANALOG_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].title = SANE_TITLE_ANALOG_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].desc = SANE_DESC_ANALOG_GAMMA_BIND;
+ sod[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ val[OPT_GAMMA_BIND].w = SANE_TRUE;
+
+ sod[OPT_ANALOG_GAMMA].name = SANE_NAME_ANALOG_GAMMA;
+ sod[OPT_ANALOG_GAMMA].title = SANE_TITLE_ANALOG_GAMMA;
+ sod[OPT_ANALOG_GAMMA].desc = SANE_DESC_ANALOG_GAMMA;
+ sod[OPT_ANALOG_GAMMA].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA].w = SANE_FIX(1.0);
+
+ sod[OPT_ANALOG_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R;
+ sod[OPT_ANALOG_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R;
+ sod[OPT_ANALOG_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R;
+ sod[OPT_ANALOG_GAMMA_R].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA_R].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA_R].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA_R].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA_R].w = SANE_FIX(1.0);
+
+ sod[OPT_ANALOG_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G;
+ sod[OPT_ANALOG_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G;
+ sod[OPT_ANALOG_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G;
+ sod[OPT_ANALOG_GAMMA_G].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA_G].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA_G].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA_G].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA_G].w = SANE_FIX(1.0);
+
+ sod[OPT_ANALOG_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B;
+ sod[OPT_ANALOG_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B;
+ sod[OPT_ANALOG_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B;
+ sod[OPT_ANALOG_GAMMA_B].type = SANE_TYPE_FIXED;
+ sod[OPT_ANALOG_GAMMA_B].unit = SANE_UNIT_NONE;
+ sod[OPT_ANALOG_GAMMA_B].size = sizeof(SANE_Word);
+ sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_ANALOG_GAMMA_B].constraint.range = &analog_gamma_range;
+ val[OPT_ANALOG_GAMMA_B].w = SANE_FIX(1.0);
+
+ sod[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
+ sod[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
+ sod[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
+ sod[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR].wa = ms->gray_lut;
+
+ sod[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
+ sod[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR_R].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR_R].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR_R].wa = ms->red_lut;
+
+ sod[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
+ sod[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR_G].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR_G].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR_G].wa = ms->green_lut;
+
+ sod[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
+ sod[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
+ sod[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
+ sod[OPT_GAMMA_VECTOR_B].size = ms->gamma_entries * sizeof(SANE_Word);
+ sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ sod[OPT_GAMMA_VECTOR_B].constraint.range = &(ms->gamma_entry_range);
+ val[OPT_GAMMA_VECTOR_B].wa = ms->blue_lut;
+
+ sod[OPT_EXP_RES].name = "exp_res";
+ sod[OPT_EXP_RES].title = "Expanded Resolution";
+ sod[OPT_EXP_RES].desc = "Enable double-resolution scans";
+ sod[OPT_EXP_RES].type = SANE_TYPE_BOOL;
+ sod[OPT_EXP_RES].cap |= SANE_CAP_ADVANCED;
+ if (!(ms->dev->info.expanded_resolution))
+ sod[OPT_EXP_RES].cap |= SANE_CAP_INACTIVE;
+ val[OPT_EXP_RES].w = SANE_FALSE;
+
+ sod[OPT_CALIB_ONCE].name = "calib_once";
+ sod[OPT_CALIB_ONCE].title = "Calibrate Only Once";
+ sod[OPT_CALIB_ONCE].desc = "Avoid CCD calibration on every scan" \
+ "(toggle off/on to cause calibration on next scan)";
+ sod[OPT_CALIB_ONCE].type = SANE_TYPE_BOOL;
+ sod[OPT_CALIB_ONCE].cap |= SANE_CAP_ADVANCED;
+ if (!(ms->do_real_calib)) {
+ sod[OPT_CALIB_ONCE].cap |= SANE_CAP_INACTIVE;
+ val[OPT_CALIB_ONCE].w = SANE_FALSE;
+ } else
+ val[OPT_CALIB_ONCE].w = SANE_TRUE;
+
+ /*
+ sod[OPT_].name = SANE_NAME_;
+ sod[OPT_].title = SANE_TITLE_;
+ sod[OPT_].desc = SANE_DESC_;
+ sod[OPT_].type = SANE_TYPE_;
+ sod[OPT_].unit = SANE_UNIT_NONE;
+ sod[OPT_].size = sizeof(SANE_Word);
+ sod[OPT_].cap = 0;
+ sod[OPT_].constraint_type = SANE_CONSTRAINT_NONE;
+ sod[OPT_].constraint = ;
+ val[OPT_].w = ;
+ */
+
+ DBG(15, "init_options: done.\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Parse an INQUIRY information block, as returned by scanner */
+/********************************************************************/
+static SANE_Status
+parse_inquiry(Microtek_Info *mi, unsigned char *result)
+{
+#if 0
+ unsigned char result[0x60] = {
+ 0x06,0x31,0x23,0x01,0x5b,0x00,0x00,0x00,0x41,0x47,0x46,0x41,0x20,0x20,0x20,0x20,
+ 0x53,0x74,0x75,0x64,0x69,0x6f,0x53,0x63,0x61,0x6e,0x20,0x49,0x49,0x20,0x20,0x20,
+ 0x32,0x2e,0x33,0x30,0x53,0x43,0x53,0x49,0x20,0x46,0x2f,0x57,0x56,0x33,0x2e,0x31,
+ 0x20,0x43,0x54,0x4c,0x35,0x33,0x38,0x30,0x03,0x4f,0x8c,0xc5,0x00,0xee,0x5b,0x43,
+ 0x01,0x01,0x02,0x00,0x00,0x03,0x00,0x01,0x00,0x4a,0x01,0x04,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff
+ };
+#endif
+ DBG(15, "parse_inquiry...\n");
+ strncpy(mi->vendor_id, (char *)&result[8], 8);
+ strncpy(mi->model_name, (char *)&result[16], 16);
+ strncpy(mi->revision_num, (char *)&result[32], 4);
+ strncpy(mi->vendor_string, (char *)&result[36], 20);
+ mi->vendor_id[8] = 0;
+ mi->model_name[16] = 0;
+ mi->revision_num[4] = 0;
+ mi->vendor_string[20] = 0;
+
+ mi->device_type = (SANE_Byte)(result[0] & 0x1f);
+ mi->SCSI_firmware_ver_major = (SANE_Byte)((result[1] & 0xf0) >> 4);
+ mi->SCSI_firmware_ver_minor = (SANE_Byte)(result[1] & 0x0f);
+ mi->scanner_firmware_ver_major = (SANE_Byte)((result[2] & 0xf0) >> 4);
+ mi->scanner_firmware_ver_minor = (SANE_Byte)(result[2] & 0x0f);
+ mi->response_data_format = (SANE_Byte)(result[3]);
+
+ mi->res_step = (SANE_Byte)(result[56] & 0x03);
+ mi->modes = (SANE_Byte)(result[57]);
+ mi->pattern_count = (SANE_Int)(result[58] & 0x7f);
+ mi->pattern_dwnld = (SANE_Byte)(result[58] & 0x80) > 0;
+ mi->feed_type = (SANE_Byte)(result[59] & 0x0F);
+ mi->compress_type = (SANE_Byte)(result[59] & 0x30);
+ mi->unit_type = (SANE_Byte)(result[59] & 0xC0);
+
+ mi->doc_size_code = (SANE_Byte)result[60];
+ /* we'll compute the max sizes after we know base resolution... */
+
+ /* why are these things set in two places (and probably wrong anyway)? */
+ mi->cont_settings = (SANE_Int)((result[61] & 0xf0) >> 4);
+ if ((SANE_Int)(result[72]))
+ mi->cont_settings = (SANE_Int)(result[72]);
+ mi->min_contrast = -42;
+ mi->max_contrast = (mi->cont_settings * 7) - 49;
+
+ mi->exp_settings = (SANE_Int)(result[61] & 0x0f);
+ if ((SANE_Int)(result[73]))
+ mi->exp_settings = (SANE_Int)(result[73]);
+ mi->min_exposure = -18;
+ mi->max_exposure = (mi->exp_settings * 3) - 21;
+#if 0
+ mi->contrast_vals = (SANE_Int)(result[72]);
+ mi->min_contrast = -42;
+ mi->max_contrast = 49;
+ if (mi->contrast_vals)
+ mi->max_contrast = (mi->contrast_vals * 7) - 49;
+
+ mi->exposure_vals = (SANE_Int)(result[73]);
+ mi->min_exposure = -18;
+ mi->max_exposure = 21;
+ if (mi->exposure_vals)
+ mi->max_exposure = (mi->exposure_vals * 3) - 21;
+#endif
+
+ mi->model_code = (SANE_Byte)(result[62]);
+ switch (mi->model_code) {
+ case 0x16: /* the other ScanMaker 600ZS */
+ case 0x50: /* ScanMaker II/IIXE */
+ case 0x54: /* ScanMaker IISP */
+ case 0x55: /* ScanMaker IIER */
+ case 0x58: /* ScanMaker IIG */
+ case 0x5a: /* Agfa StudioScan (untested!) */
+ case 0x5f: /* ScanMaker E3 */
+ case 0x56: /* ScanMaker A3t */
+ case 0x64: /* ScanMaker E2 (,Vobis RealScan) */
+ case 0x65: /* Color PageWiz */
+ case 0xC8: /* ScanMaker 600ZS */
+ mi->base_resolution = 300;
+ break;
+ case 0x5b: /* Agfa StudioScan II/IIsi (untested!) */
+ mi->base_resolution = 400;
+ break;
+ case 0x57: /* ScanMaker IIHR */
+ case 0x59: /* ScanMaker III */
+ case 0x5c: /* Agfa Arcus II */
+ case 0x5e: /* Agfa StudioStar */
+ case 0x63: /* ScanMaker E6 */
+ case 0x66: /* ScanMaker E6 (new)*/
+ mi->base_resolution = 600;
+ break;
+ case 0x51: /* ScanMaker 45t */
+ case 0x5d: /* Agfa DuoScan */
+ mi->base_resolution = 1000;
+ break;
+ case 0x52: /* ScanMaker 35t */
+ mi->base_resolution = 1828;
+ break;
+ case 0x62: /* ScanMaker 35t+, Polaroid 35/LE */
+ mi->base_resolution = 1950;
+ break;
+ default:
+ mi->base_resolution = 300;
+ DBG(15, "parse_inquiry: Unknown base resolution for 0x%x!\n",
+ mi->model_code);
+ break;
+ }
+
+ /* Our max_x,y is in pixels. `Some scanners think in 1/8", though.' */
+ /* max pixel is, of course, total - 1 */
+ switch (mi->doc_size_code) {
+ case 0x00:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 14.0 * mi->base_resolution - 1;
+ break;
+ case 0x01:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 11.0 * mi->base_resolution - 1;
+ break;
+ case 0x02:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 11.69 * mi->base_resolution - 1;
+ break;
+ case 0x03:
+ mi->max_x = 8.5 * mi->base_resolution - 1;
+ mi->max_y = 13.0 * mi->base_resolution - 1;
+ break;
+ case 0x04:
+ mi->max_x = 8.0 * mi->base_resolution - 1;
+ mi->max_y = 10.0 * mi->base_resolution - 1;
+ break;
+ case 0x05:
+ mi->max_x = 8.3 * mi->base_resolution - 1;
+ mi->max_y = 14.0 * mi->base_resolution - 1;
+ break;
+ case 0x06:
+ mi->max_x = 8.3 * mi->base_resolution - 1;
+ mi->max_y = 13.5 * mi->base_resolution - 1;
+ break;
+ case 0x07:
+ mi->max_x = 8.0 * mi->base_resolution - 1;
+ mi->max_y = 14.0 * mi->base_resolution - 1;
+ break;
+ case 0x80:
+ /* Slide format, size is mm */
+ mi->max_x = (35.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ mi->max_y = (35.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ break;
+ case 0x81:
+ mi->max_x = 5.0 * mi->base_resolution - 1;
+ mi->max_y = 5.0 * mi->base_resolution - 1;
+ break;
+ case 0x82:
+ /* Slide format, size is mm */
+ mi->max_x = (36.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ mi->max_y = (36.0 / MM_PER_INCH) * mi->base_resolution - 1;
+ break;
+ default:
+ /* Undefined document format code */
+ mi->max_x = mi->max_y = 0;
+ DBG(15, "parse_inquiry: Unknown doc_size_code! 0x%x\n",
+ mi->doc_size_code);
+ }
+
+ /* create the proper range constraints, given the doc size */
+ {
+ /* we need base resolution in dots-per-millimeter... */
+ float base_res_dpmm = (float) mi->base_resolution / MM_PER_INCH;
+ mi->doc_x_range.min = SANE_FIX(0);
+ mi->doc_x_range.max = SANE_FIX((float)mi->max_x / base_res_dpmm);
+ mi->doc_x_range.quant = SANE_FIX(0);
+ mi->doc_y_range.min = SANE_FIX(0);
+ mi->doc_y_range.max = SANE_FIX((float)mi->max_y / base_res_dpmm);
+ mi->doc_y_range.quant = SANE_FIX(0);
+ }
+
+ mi->source_options = (SANE_Byte)(result[63]);
+
+ mi->expanded_resolution = (result[64] & 0x01);
+ /* my E6 reports exp-res capability incorrectly */
+ if ((mi->model_code == 0x66) || (mi->model_code == 0x63)) {
+ mi->expanded_resolution = 0xFF;
+ DBG(4, "parse_inquiry: E6 falsely denies expanded resolution.\n");
+ }
+ /* the StudioScan II(si) does the expanded-mode aspect correction
+ within the scanner... (do others too?) */
+ if (mi->model_code == 0x5b) {
+ DBG(4, "parse_inquiry: does expanded-mode expansion internally.\n");
+ mi->does_expansion = 1;
+ } else
+ mi->does_expansion = 0;
+
+ mi->enhance_cap = (result[65] & 0x03);
+
+ /*
+ switch (result[66] & 0x0F) {
+ case 0x00: mi->max_lookup_size = 0; break;
+ case 0x01: mi->max_lookup_size = 256; break;
+ case 0x03: mi->max_lookup_size = 1024; break;
+ case 0x05: mi->max_lookup_size = 4096; break;
+ case 0x09: mi->max_lookup_size = 65536; break;
+ default:
+ mi->max_lookup_size = 0;
+ DBG(15, "parse_inquiry: Unknown gamma LUT size! 0x%x\n",
+ result[66]);
+ }
+ */
+
+ /* This is not how the vague documentation specifies this register.
+ We're going to take it literally here -- i.e. if the bit is
+ set, the scanner supports the value, otherwise it doesn't.
+ (The docs say all lower values are always supported. This is
+ not the case for the StudioScan IIsi, at least, which only
+ specifies 0x02==1024-byte table, and only supports that, too.)
+
+ All-in-all, it doesn't matter, since we take the largest
+ allowed LUT size anyway.
+ */
+ if (result[66] & 0x08)
+ mi->max_lookup_size = 65536;
+ else if (result[66] & 0x04)
+ mi->max_lookup_size = 4096;
+ else if (result[66] & 0x02)
+ mi->max_lookup_size = 1024;
+ else if (result[66] & 0x01)
+ mi->max_lookup_size = 256;
+ else
+ mi->max_lookup_size = 0;
+
+ /* my E6 reports incorrectly */
+ if ((mi->model_code == 0x66) || (mi->model_code == 0x63)) {
+ mi->max_lookup_size = 1024;
+ DBG(4, "parse_inquiry: E6 falsely denies 1024-byte LUT.\n");
+ }
+
+ /*
+ switch (result[66] >> 5) {
+ case 0x00: mi->max_gamma_val = 255; mi->gamma_size = 1; break;
+ case 0x01: mi->max_gamma_val = 1023; mi->gamma_size = 2; break;
+ case 0x02: mi->max_gamma_val = 4095; mi->gamma_size = 2; break;
+ case 0x03: mi->max_gamma_val = 65535; mi->gamma_size = 2; break;
+ default:
+ mi->max_gamma_val = 0; mi->gamma_size = 0;
+ DBG(15, "parse_inquiry: Unknown gamma max val! 0x%x\n",
+ result[66]);
+ }
+ */
+ switch (result[66] >> 5) {
+ case 0x00: mi->max_gamma_bit_depth = 8; mi->gamma_size = 1; break;
+ case 0x01: mi->max_gamma_bit_depth = 10; mi->gamma_size = 2; break;
+ case 0x02: mi->max_gamma_bit_depth = 12; mi->gamma_size = 2; break;
+ case 0x03: mi->max_gamma_bit_depth = 16; mi->gamma_size = 2; break;
+ default:
+ mi->max_gamma_bit_depth = 0; mi->gamma_size = 0;
+ DBG(15, "parse_inquiry: Unknown gamma max val! 0x%x\n",
+ result[66]);
+ }
+
+ mi->fast_color_preview = (SANE_Byte)(result[67] & 0x01);
+ mi->xfer_format_select = (SANE_Byte)(result[68] & 0x01);
+ mi->color_sequence = (SANE_Byte)(result[69] & 0x7f);
+ mi->does_3pass = (SANE_Byte)(!(result[69] & 0x80));
+ mi->does_mode1 = (SANE_Byte)(result[71] & 0x01);
+
+ mi->bit_formats = (SANE_Byte)(result[74] & 0x0F);
+ mi->extra_cap = (SANE_Byte)(result[75] & 0x07);
+
+ /* XXXXXX a quick hack to disable any [pre/real]cal stuff for
+ anything but an E6... */
+ if (!((mi->model_code == 0x66) || (mi->model_code == 0x63))) {
+ mi->extra_cap &= ~MI_EXCAP_DIS_RECAL;
+ DBG(4, "parse_inquiry: Not an E6 -- pretend recal cannot be disabled.\n");
+ }
+
+ /* The E2 lies... */
+ if (mi->model_code == 0x64) {
+ DBG(4, "parse_inquiry: The E2 lies about it's 3-pass heritage.\n");
+ mi->does_3pass = 1;
+ mi->modes &= ~MI_MODES_ONEPASS;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Dump all we know about scanner to stderr */
+/********************************************************************/
+static SANE_Status
+dump_inquiry(Microtek_Info *mi, unsigned char *result)
+{
+ int i;
+
+ DBG(15, "dump_inquiry...\n");
+ DBG(1, " === SANE/Microtek backend v%d.%d.%d ===\n",
+ MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH);
+ DBG(1, "========== Scanner Inquiry Block ========mm\n");
+ for (i=0; i<96; ) {
+ if (!(i % 16)) MDBG_INIT("");
+ MDBG_ADD("%02x ", (int)result[i++]);
+ if (!(i % 16)) MDBG_FINISH(1);
+ }
+ DBG(1, "========== Scanner Inquiry Report ==========\n");
+ DBG(1, "===== Scanner ID...\n");
+ DBG(1, "Device Type Code: 0x%02x\n", mi->device_type);
+ DBG(1, "Model Code: 0x%02x\n", mi->model_code);
+ DBG(1, "Vendor Name: '%s' Model Name: '%s'\n",
+ mi->vendor_id, mi->model_name);
+ DBG(1, "Vendor Specific String: '%s'\n", mi->vendor_string);
+ DBG(1, "Firmware Rev: '%s'\n", mi->revision_num);
+ DBG(1,
+ "SCSI F/W version: %1d.%1d Scanner F/W version: %1d.%1d\n",
+ mi->SCSI_firmware_ver_major, mi->SCSI_firmware_ver_minor,
+ mi->scanner_firmware_ver_major, mi->scanner_firmware_ver_minor);
+ DBG(1, "Response data format: 0x%02x\n", mi->response_data_format);
+
+ DBG(1, "===== Imaging Capabilities...\n");
+ DBG(1, "Modes: %s%s%s%s%s%s%s\n",
+ (mi->modes & MI_MODES_LINEART) ? "Lineart " : "",
+ (mi->modes & MI_MODES_HALFTONE) ? "Halftone " : "",
+ (mi->modes & MI_MODES_GRAY) ? "Gray " : "",
+ (mi->modes & MI_MODES_COLOR) ? "Color " : "",
+ (mi->modes & MI_MODES_TRANSMSV) ? "(X-msv) " : "",
+ (mi->modes & MI_MODES_ONEPASS) ? "(OnePass) " : "",
+ (mi->modes & MI_MODES_NEGATIVE) ? "(Negative) " : "");
+ DBG(1,
+ "Resolution Step Sizes: %s%s Expanded Resolution Support? %s%s\n",
+ (mi->res_step & MI_RESSTEP_1PER) ? "1% " : "",
+ (mi->res_step & MI_RESSTEP_5PER) ? "5%" : "",
+ (mi->expanded_resolution) ? "yes" : "no",
+ (mi->expanded_resolution == 0xFF) ? "(but says no)" : "");
+ DBG(1, "Supported Bits Per Sample: %s8 %s%s%s\n",
+ (mi->bit_formats & MI_FMT_CAP_4BPP) ? "4 " : "",
+ (mi->bit_formats & MI_FMT_CAP_10BPP) ? "10 " : "",
+ (mi->bit_formats & MI_FMT_CAP_12BPP) ? "12 " : "",
+ (mi->bit_formats & MI_FMT_CAP_16BPP) ? "16 " : "");
+ DBG(1, "Max. document size code: 0x%02x\n",
+ mi->doc_size_code);
+ DBG(1, "Max. document size: %d x %d pixels\n",
+ mi->max_x, mi->max_y);
+ DBG(1, "Frame units: %s%s\n",
+ (mi->unit_type & MI_UNIT_PIXELS) ? "pixels " : "",
+ (mi->unit_type & MI_UNIT_8TH_INCH) ? "1/8\"'s " : "");
+ DBG(1, "# of built-in halftones: %d Downloadable patterns? %s\n",
+ mi->pattern_count, (mi->pattern_dwnld) ? "Yes" : "No");
+
+ DBG(1, "Data Compression: %s%s\n",
+ (mi->compress_type & MI_COMPRSS_HUFF) ? "huffman " : "",
+ (mi->compress_type & MI_COMPRSS_RD) ? "read-data " : "");
+ DBG(1, "Contrast Settings: %d Exposure Settings: %d\n",
+ mi->cont_settings, mi->exp_settings);
+ DBG(1, "Adjustable Shadow/Highlight? %s Adjustable Midtone? %s\n",
+ (mi->enhance_cap & MI_ENH_CAP_SHADOW) ? "yes" : "no ",
+ (mi->enhance_cap & MI_ENH_CAP_MIDTONE) ? "yes" : "no ");
+ DBG(1, "Digital brightness/offset? %s\n",
+ (mi->extra_cap & MI_EXCAP_OFF_CTL) ? "yes" : "no");
+ /*
+ fprintf(stderr,
+ "Gamma Table Size: %d entries of %d bytes (max. value: %d)\n",
+ mi->max_lookup_size, mi->gamma_size, mi->max_gamma_val);
+ */
+ DBG(1,
+ "Gamma Table Size: %d entries of %d bytes (max. depth: %d)\n",
+ mi->max_lookup_size, mi->gamma_size, mi->max_gamma_bit_depth);
+
+ DBG(1, "===== Source Options...\n");
+ DBG(1, "Feed type: %s%s ADF support? %s\n",
+ (mi->feed_type & MI_FEED_FLATBED) ? "flatbed " : "",
+ (mi->feed_type & MI_FEED_EDGEFEED) ? "edge-feed " : "",
+ (mi->feed_type & MI_FEED_AUTOSUPP) ? "yes" : "no");
+ DBG(1, "Document Feeder Support? %s Feeder Backtracking? %s\n",
+ (mi->source_options & MI_SRC_FEED_SUPP) ? "yes" : "no ",
+ (mi->source_options & MI_SRC_FEED_BT) ? "yes" : "no ");
+ DBG(1, "Feeder Installed? %s Feeder Ready? %s\n",
+ (mi->source_options & MI_SRC_HAS_FEED) ? "yes" : "no ",
+ (mi->source_options & MI_SRC_FEED_RDY) ? "yes" : "no ");
+ DBG(1, "Transparency Adapter Installed? %s\n",
+ (mi->source_options & MI_SRC_HAS_TRANS) ? "yes" : "no ");
+ /* GET_TRANS GET_FEED XXXXXXXXX */
+ /* mt_SWslct ???? XXXXXXXXXXX */
+ /*#define DOC_ON_FLATBED 0x00
+ #define DOC_IN_FEEDER 0x01
+ #define TRANSPARENCY 0x10
+ */
+ DBG(1, "Fast Color Prescan? %s\n",
+ (mi->fast_color_preview) ? "yes" : "no");
+ DBG(1, "Selectable Transfer Format? %s\n",
+ (mi->xfer_format_select) ? "yes" : "no");
+ MDBG_INIT("Color Transfer Sequence: ");
+ switch (mi->color_sequence) {
+ case MI_COLSEQ_PLANE:
+ MDBG_ADD("plane-by-plane (3-pass)"); break;
+ case MI_COLSEQ_PIXEL:
+ MDBG_ADD("pixel-by-pixel RGB"); break;
+ case MI_COLSEQ_RGB:
+ MDBG_ADD("line-by-line, R-G-B sequence"); break;
+ case MI_COLSEQ_NONRGB:
+ MDBG_ADD("line-by-line, non-sequential with headers"); break;
+ case MI_COLSEQ_2PIXEL:
+ MDBG_ADD("2pixel-by-2pixel RRGGBB"); break;
+ default:
+ MDBG_ADD("UNKNOWN CODE (0x%02x)", mi->color_sequence);
+ }
+ MDBG_FINISH(1);
+ /* if (mi->modes & MI_MODES_ONEPASS) XXXXXXXXXXX */
+ DBG(1, "Three pass scan support? %s\n",
+ (mi->does_3pass ? "yes" : "no"));
+ DBG(1, "ModeSelect-1 and ModeSense-1 Support? %s\n",
+ (mi->does_mode1) ? "yes" : "no");
+ DBG(1, "Can Disable Linearization Table? %s\n",
+ (mi->extra_cap & MI_EXCAP_DIS_LNTBL) ? "yes" : "no");
+ DBG(1, "Can Disable Start-of-Scan Recalibration? %s\n",
+ (mi->extra_cap & MI_EXCAP_DIS_RECAL) ? "yes" : "no");
+
+ DBG(1, "Internal expanded expansion? %s\n",
+ mi->does_expansion ? "yes" : "no");
+ /*
+ fprintf(stderr, "cntr_vals = %d, min_cntr = %d, max_cntr = %d\n",
+ cntr_vals, min_cntr, max_cntr);
+ fprintf(stderr, "exp_vals = %d, min_exp = %d, max_exp = %d\n",
+ exp_vals, min_exp, max_exp);
+ */
+ DBG(1, "====== End of Scanner Inquiry Report =======\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Dump all we know about some unknown scanner to stderr */
+/********************************************************************/
+static SANE_Status
+dump_suspect_inquiry(unsigned char *result)
+{
+ int i;
+ char vendor_id[64], model_name[64], revision_num[16];
+ SANE_Byte device_type, model_code;
+ SANE_Byte SCSI_firmware_ver_major, SCSI_firmware_ver_minor;
+ SANE_Byte scanner_firmware_ver_major, scanner_firmware_ver_minor;
+ SANE_Byte response_data_format;
+
+ DBG(15, "dump_suspect_inquiry...\n");
+ DBG(1, " === SANE/Microtek backend v%d.%d.%d ===\n",
+ MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH);
+ DBG(1, "========== Scanner Inquiry Block ========mm\n");
+ for (i=0; i<96; ) {
+ if (!(i % 16)) MDBG_INIT("");
+ MDBG_ADD("%02x ", (int)result[i++]);
+ if (!(i % 16)) MDBG_FINISH(1);
+ }
+#if 0
+ for (i=0; i<96; i++) {
+ if (!(i % 16) && (i)) fprintf(stderr, "\n");
+ fprintf(stderr, "%02x ", (int)result[i]);
+ }
+ fprintf(stderr, "\n\n");
+#endif
+ strncpy(vendor_id, (char *)&result[8], 8);
+ strncpy(model_name, (char *)&result[16], 16);
+ strncpy(revision_num, (char *)&result[32], 4);
+ vendor_id[8] = 0;
+ model_name[16] = 0;
+ revision_num[5] = 0;
+ device_type = (SANE_Byte)(result[0] & 0x1f);
+ SCSI_firmware_ver_major = (SANE_Byte)((result[1] & 0xf0) >> 4);
+ SCSI_firmware_ver_minor = (SANE_Byte)(result[1] & 0x0f);
+ scanner_firmware_ver_major = (SANE_Byte)((result[2] & 0xf0) >> 4);
+ scanner_firmware_ver_minor = (SANE_Byte)(result[2] & 0x0f);
+ response_data_format = (SANE_Byte)(result[3]);
+ model_code = (SANE_Byte)(result[62]);
+
+ DBG(1, "========== Scanner Inquiry Report ==========\n");
+ DBG(1, "===== Scanner ID...\n");
+ DBG(1, "Device Type Code: 0x%02x\n", device_type);
+ DBG(1, "Model Code: 0x%02x\n", model_code);
+ DBG(1, "Vendor Name: '%s' Model Name: '%s'\n",
+ vendor_id, model_name);
+ DBG(1, "Firmware Rev: '%s'\n", revision_num);
+ DBG(1,
+ "SCSI F/W version: %1d.%1d Scanner F/W version: %1d.%1d\n",
+ SCSI_firmware_ver_major, SCSI_firmware_ver_minor,
+ scanner_firmware_ver_major, scanner_firmware_ver_minor);
+ DBG(1, "Response data format: 0x%02x\n", response_data_format);
+ DBG(1, "====== End of Scanner Inquiry Report =======\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Determine if device is a Microtek Scanner (from INQUIRY info) */
+/********************************************************************/
+static SANE_Status
+id_microtek(uint8_t *result, char **model_string)
+{
+ SANE_Byte device_type, response_data_format;
+ int forcewarn = 0;
+
+ DBG(15, "id_microtek...\n");
+ /* check device type first... */
+ device_type = (SANE_Byte)(result[0] & 0x1f);
+ if (device_type != 0x06) {
+ DBG(15, "id_microtek: not even a scanner: dev_type = %d\n",
+ device_type);
+ return SANE_STATUS_INVAL;
+ }
+ if (!(strncmp("MICROTEK", (char *)&(result[8]), 8)) ||
+ !(strncmp("MII SC31", (char *)&(result[8]), 8)) || /* the IISP */
+ !(strncmp("MII SC21", (char *)&(result[8]), 8)) || /* the 600ZS */
+ !(strncmp("MII SC23", (char *)&(result[8]), 8)) || /* the other 600ZS */
+ !(strncmp("MII SC25", (char *)&(result[8]), 8)) || /* some other 600GS */
+ !(strncmp("AGFA ", (char *)&(result[8]), 8)) || /* Arcus II */
+ !(strncmp("Microtek", (char *)&(result[8]), 8)) || /* some 35t+'s */
+ !(strncmp("Polaroid", (char *)&(result[8]), 8)) || /* SprintScan 35LE */
+ !(strncmp(" ", (char *)&(result[8]), 8)) ) {
+ switch (result[62]) {
+ case 0x16 :
+ *model_string = "ScanMaker 600ZS"; break;
+ case 0x50 :
+ *model_string = "ScanMaker II/IIXE"; break;
+ case 0x51 :
+ *model_string = "ScanMaker 45t"; break;
+ case 0x52 :
+ *model_string = "ScanMaker 35t"; break;
+ case 0x54 :
+ *model_string = "ScanMaker IISP"; break;
+ case 0x55 :
+ *model_string = "ScanMaker IIER"; break;
+ case 0x56 :
+ *model_string = "ScanMaker A3t"; break;
+ case 0x57 :
+ *model_string = "ScanMaker IIHR"; break;
+ case 0x58 :
+ *model_string = "ScanMaker IIG"; break;
+ case 0x59 :
+ *model_string = "ScanMaker III"; break;
+ case 0x5A :
+ *model_string = "Agfa StudioScan"; break;
+ case 0x5B :
+ *model_string = "Agfa StudioScan II"; break;
+ case 0x5C :
+ *model_string = "Agfa Arcus II"; break;
+ case 0x5f :
+ *model_string = "ScanMaker E3"; break;
+ case 0x62 :
+ if (!(strncmp("Polaroid", (char *)&(result[8]), 8)))
+ *model_string = "Polaroid SprintScan 35/LE";
+ else
+ *model_string = "ScanMaker 35t+";
+ break;
+ case 0x63 :
+ case 0x66 :
+ *model_string = "ScanMaker E6"; break;
+ case 0x64 : /* and "Vobis RealScan" */
+ *model_string = "ScanMaker E2"; break;
+ case 0x65:
+ *model_string = "Color PageWiz"; break;
+ case 0xC8:
+ *model_string = "ScanMaker 600ZS"; break;
+ /* the follow are listed in the docs, but are otherwise a mystery... */
+ case 0x5D:
+ *model_string = "Agfa DuoScan"; forcewarn = 1; break;
+ case 0x5E:
+ *model_string = "SS3"; forcewarn = 1; break;
+ case 0x60:
+ *model_string = "HR1"; forcewarn = 1; break;
+ case 0x61:
+ *model_string = "45T+"; forcewarn = 1; break;
+ case 0x67:
+ *model_string = "TR3"; forcewarn = 1; break;
+ default :
+ /* this might be a newer scanner, which uses the SCSI II command set. */
+ /* that's unfortunate, but we'll warn the user anyway.... */
+ response_data_format = (SANE_Byte)(result[3]);
+ if (response_data_format == 0x02) {
+ DBG(15, "id_microtek: (uses new SCSI II command set)\n");
+ if (DBG_LEVEL >= 15) {
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "========== Congratulations! ==========\n");
+ DBG(1, "You appear to be the proud owner of a \n");
+ DBG(1, "brand-new Microtek scanner, which uses\n");
+ DBG(1, "a new SCSI II command set. \n");
+ DBG(1, "\n");
+ DBG(1, "Try the `microtek2' backend instead. \n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ }
+ }
+ return SANE_STATUS_INVAL;
+ }
+ if (forcewarn) {
+ /* force debugging on, to encourage user to send in a report */
+#ifndef NDEBUG
+ DBG_LEVEL = 1;
+#endif
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "========== Congratulations! ==========\n");
+ DBG(1, "Your scanner appears to be supported \n");
+ DBG(1, "by the microtek backend. However, it \n");
+ DBG(1, "has never been tried before, and some \n");
+ DBG(1, "parameters are bound to be wrong. \n");
+ DBG(1, "\n");
+ DBG(1, "Please send the scanner inquiry log in\n");
+ DBG(1, "its entirety to mtek-bugs@mir.com and \n");
+ DBG(1, "include a description of the scanner, \n");
+ DBG(1, "including the base optical resolution.\n");
+ DBG(1, "\n");
+ DBG(1, "You'll find complete instructions for \n");
+ DBG(1, "submitting an error/debug log in the \n");
+ DBG(1, "'sane-microtek' man-page. \n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ DBG(1, "\n");
+ }
+ return SANE_STATUS_GOOD;
+ }
+ DBG(15, "id_microtek: not microtek: %d, %d, %d\n",
+ strncmp("MICROTEK", (char *)&(result[8]), 8),
+ strncmp(" ", (char *)&(result[8]), 8),
+ result[62]);
+ return SANE_STATUS_INVAL;
+}
+
+
+
+/********************************************************************/
+/* Try to attach a device as a Microtek scanner */
+/********************************************************************/
+static SANE_Status
+attach_scanner(const char *devicename, Microtek_Device **devp)
+{
+ Microtek_Device *dev;
+ int sfd;
+ size_t size;
+ unsigned char result[0x60];
+ SANE_Status status;
+ char *model_string;
+ uint8_t inquiry[] = { 0x12, 0, 0, 0, 0x60, 0 };
+
+ DBG(15,"attach_scanner: %s\n", devicename);
+ /* check if device is already known... */
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, devicename) == 0) {
+ if (devp) *devp = dev;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* open scsi device... */
+ DBG(20, "attach_scanner: opening %s\n", devicename);
+ if (sanei_scsi_open(devicename, &sfd, sense_handler, NULL) != 0) {
+ DBG(20, "attach_scanner: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* say hello... */
+ DBG(20, "attach_scanner: sending INQUIRY\n");
+ size = sizeof(result);
+ status = sanei_scsi_cmd(sfd, inquiry, sizeof(inquiry), result, &size);
+ sanei_scsi_close (sfd);
+ if (status != SANE_STATUS_GOOD || size != 0x60) {
+ DBG(20, "attach_scanner: inquiry failed (%s)\n", sane_strstatus (status));
+ return status;
+ }
+
+ if (id_microtek(result, &model_string) != SANE_STATUS_GOOD) {
+ DBG(15, "attach_scanner: device doesn't look like a Microtek scanner.");
+ if (DBG_LEVEL >= 5) dump_suspect_inquiry(result);
+ return SANE_STATUS_INVAL;
+ }
+
+ dev=malloc(sizeof(*dev));
+ if (!dev) return SANE_STATUS_NO_MEM;
+ memset(dev, 0, sizeof(*dev));
+
+ parse_inquiry(&(dev->info), result);
+ if (DBG_LEVEL > 0) dump_inquiry(&(dev->info), result);
+
+ /* initialize dev structure */
+ dev->sane.name = strdup(devicename);
+ dev->sane.vendor = "Microtek";
+ dev->sane.model = strdup(model_string);
+ dev->sane.type = "flatbed scanner";
+
+ /* link into device list... */
+ ++num_devices;
+ dev->next = first_dev;
+ first_dev = dev;
+ if (devp) *devp = dev;
+
+ DBG(15, "attach_scanner: happy.\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Attach a scanner (convenience wrapper for find_scanners...) */
+/********************************************************************/
+static SANE_Status
+attach_one (const char *dev)
+{
+ attach_scanner (dev, 0);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/********************************************************************/
+/* End a scan, and clean up afterwards */
+/********************************************************************/
+static SANE_Status end_scan(Microtek_Scanner *s, SANE_Status ostat)
+{
+ SANE_Status status;
+
+ DBG(15, "end_scan...\n");
+ if (s->scanning) {
+ s->scanning = SANE_FALSE;
+ /* stop the scanner */
+ if (s->scan_started) {
+ status = stop_scan(s);
+ if (status != SANE_STATUS_GOOD)
+ DBG(23, "end_scan: OY! on stop_scan\n");
+ s->scan_started = SANE_FALSE;
+ }
+ /* close the SCSI device */
+ if (s->sfd != -1) {
+ sanei_scsi_close(s->sfd);
+ s->sfd = -1;
+ }
+ /* free the buffers we malloc'ed */
+ if (s->scsi_buffer != NULL) {
+ free(s->scsi_buffer);
+ s->scsi_buffer = NULL;
+ }
+ if (s->rb != NULL) {
+ ring_free(s->rb);
+ s->rb = NULL;
+ }
+ }
+ /* if this -was- pass 3, or cancel, then we must be done */
+ if ((s->this_pass == 3) || (s->cancel))
+ s->this_pass = 0;
+ return ostat;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/***** Scan-time operations *****/
+/********************************************************************/
+/********************************************************************/
+
+
+/* number of lines of calibration data returned by scanner */
+#define STRIPS 12 /* well, that's what it seems to be for the E6 */
+
+
+/* simple comparison for the qsort below */
+
+static int comparo(const void *a, const void *b)
+{
+ return (*(const int *)a - *(const int *)b);
+}
+
+
+/* extract values from scanlines and sort */
+
+static void sort_values(int *result, uint8_t *scanline[], int pix)
+{
+ int i;
+ for (i=0; i<STRIPS; i++) result[i] = (scanline[i])[pix];
+ qsort(result, STRIPS, sizeof(result[0]), comparo);
+}
+
+
+/********************************************************************/
+/* Calculate the calibration data. */
+/* This seems to be, for each pixel of each R/G/B ccd, the average */
+/* of the STRIPS# values read by the scanner, presumably off some */
+/* blank spot under the cover. */
+/* The raw scanner data does indeed resemble the intensity profile */
+/* of a lamp. */
+/* The sort is used to calc the median, which is used to remove */
+/* outliers in the data; maybe from dust under the cover? */
+/********************************************************************/
+
+
+static void calc_calibration(uint8_t *caldata, uint8_t *scanline[],
+ int pixels)
+{
+ int i,j;
+ int sorted[STRIPS];
+
+ DBG(23, ".calc_calibration...\n");
+ for (i=0; i<pixels; i++) {
+ int q1, q3;
+ int bot, top;
+ int sum = 0;
+ int count = 0;
+
+ sort_values(sorted, scanline, i);
+ q1 = sorted[STRIPS / 4]; /* first quartile */
+ q3 = sorted[STRIPS * 3 / 4]; /* third quartile */
+ bot = q1 - 3 * (q3 - q1) / 2; /* quick'n'easy bounds */
+ top = q3 + 3 * (q3 - q1) / 2;
+
+ for (j=0; j<STRIPS; j++) {
+ if ((sorted[j] >= bot) && (sorted[j] <= top)) {
+ sum += sorted[j];
+ count++;
+ }
+ }
+ if (count)
+ caldata[i] = (sum + (count / 2)) / count;
+ else {
+ DBG(23, "zero: i=%d b/t=%d/%d ", i, bot, top);
+ if (DBG_LEVEL >= 23) {
+ MDBG_INIT("");
+ for (j=0; j<STRIPS; j++) MDBG_ADD(" %3d", sorted[j]);
+ MDBG_FINISH(23);
+ }
+ caldata[i] = 0;
+ }
+ }
+}
+
+
+
+/********************************************************************/
+/* Calibrate scanner CCD, the "real" way. */
+/* This stuff is not documented in the command set, but this is */
+/* what Microtek's TWAIN driver seems to do, more or less, on an */
+/* E6 at least. What other scanners will do this??? */
+/********************************************************************/
+
+
+static SANE_Status do_real_calibrate(Microtek_Scanner *s)
+{
+ SANE_Status status, statusA;
+ SANE_Int busy, linewidth, lines;
+ size_t buffsize;
+ uint8_t *input, *scanline[STRIPS], *combuff;
+ uint8_t letter;
+ int i, spot;
+ int nmax, ntoget, nleft;
+
+ DBG(10, "do_real_calibrate...\n");
+
+ /* tell scanner to read it's little chart */
+ if ((status = start_calibration(s)) != SANE_STATUS_GOOD) return status;
+ if ((status = get_scan_status(s, &busy, &linewidth, &lines))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "do_real_cal: get_scan_status failed!\n");
+ return status;
+ }
+ /* make room for data in and data out */
+ input = calloc(STRIPS * 3 * linewidth, sizeof(input[0]));
+ combuff = calloc(linewidth + 6, sizeof(combuff[0]));
+ if ((input == NULL) || (combuff == NULL)) {
+ DBG(23, "do_real_cal: bad calloc %p %p\n", input, combuff);
+ free(input);
+ free(combuff);
+ return SANE_STATUS_NO_MEM;
+ }
+ /* read STRIPS lines of R, G, B ccd data */
+ nmax = SCSI_BUFF_SIZE / (3 * linewidth);
+ DBG(23, "do_real_cal: getting data (max=%d)\n", nmax);
+ for (nleft = STRIPS, spot=0;
+ nleft > 0;
+ nleft -= ntoget, spot += buffsize) {
+ ntoget = (nleft > nmax) ? nmax : nleft;
+ buffsize = ntoget * 3 * linewidth;
+ DBG(23, "...nleft %d toget %d size %lu spot %d input+spot %p\n",
+ nleft, ntoget, (u_long) buffsize, spot, input+spot);
+ if ((statusA = read_scan_data(s, ntoget, input+spot, &buffsize))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "...read scan failed\n");
+ break;
+ }
+ }
+ status = stop_scan(s);
+ if ((statusA != SANE_STATUS_GOOD) || (status != SANE_STATUS_GOOD)) {
+ free(input);
+ free(combuff);
+ return ((statusA != SANE_STATUS_GOOD) ? statusA : status);
+ }
+ /* calculate calibration data for each element and download */
+ for (letter = 'R'; letter != 'X'; ) {
+ DBG(23, "do_real_calibrate: working on %c\n", letter);
+ for (spot=0, i=0; spot < linewidth * STRIPS * 3; spot += linewidth) {
+ if (input[spot+1] == letter) {
+ DBG(23, " found %d (at %d)\n", i, spot);
+ if (i >= STRIPS) {
+ DBG(23, "WHOA!!! %i have already been found!\n", i);
+ break;
+ }
+ scanline[i] = &(input[spot+2]);
+ i++;
+ }
+ }
+ calc_calibration(combuff + 8, scanline, linewidth - 2);
+ if ((status = download_calibration(s, combuff, letter, linewidth))
+ != SANE_STATUS_GOOD) {
+ DBG(23, "...download_calibration failed\n");
+ free(input);
+ free(combuff);
+ return status;
+ }
+ switch (letter) {
+ case 'R': letter = 'G'; break;
+ case 'G': letter = 'B'; break;
+ case 'B':
+ default: letter = 'X'; break;
+ }
+ }
+ /* clean up */
+ free(input);
+ free(combuff);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+
+/********************************************************************/
+/* Cause scanner to calibrate, but don't really scan anything */
+/* (i.e. do everything but read data) */
+/********************************************************************/
+static SANE_Status do_precalibrate(SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status, statusA;
+ SANE_Int busy, linewidth, lines;
+
+ DBG(10, "do_precalibrate...\n");
+
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status;
+ {
+ SANE_Int y1 = s->y1;
+ SANE_Int y2 = s->y2;
+ /* some small range, but large enough to cause the scanner
+ to think it'll scan *something*... */
+ s->y1 = 0;
+ s->y2 =
+ (s->resolution > s->dev->info.base_resolution) ?
+ 4 : 4 * s->dev->info.base_resolution / s->resolution;
+ status = scanning_frame(s);
+ s->y1 = y1;
+ s->y2 = y2;
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ if (s->dev->info.source_options &
+ (MI_SRC_FEED_BT | MI_SRC_HAS_TRANS |
+ MI_SRC_FEED_SUPP | MI_SRC_HAS_FEED)) { /* ZZZZZZZZZZZ */
+ if ((status = accessory(s)) != SANE_STATUS_GOOD) return status;
+ }
+ if ((status = mode_select(s)) != SANE_STATUS_GOOD) return status;
+ /* why would we even try if this were not true?... */
+ /*if (s->dev->info.extra_cap & MI_EXCAP_DIS_RECAL) */
+ {
+ SANE_Bool allow_calibrate = s->allow_calibrate;
+ s->allow_calibrate = SANE_TRUE;
+ status = mode_select_1(s);
+ s->allow_calibrate = allow_calibrate;
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status;
+ if ((status = start_scan(s)) != SANE_STATUS_GOOD) return status;
+ if ((statusA = get_scan_status(s, &busy,
+ &linewidth, &lines)) != SANE_STATUS_GOOD) {
+ DBG(10, "do_precalibrate: get_scan_status fails\n");
+ }
+ if ((status = stop_scan(s)) != SANE_STATUS_GOOD) return status;
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status;
+ DBG(10, "do_precalibrate done.\n");
+ if (statusA != SANE_STATUS_GOOD)
+ return statusA;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Calibrate scanner, if necessary; record results */
+/********************************************************************/
+static SANE_Status finagle_precal(SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+ int match;
+
+ /* try to check if scanner has been reset */
+ /* if so, calibrate it
+ (either for real, or via a fake scan, with calibration */
+ /* (but only bother if you *could* disable calibration) */
+ DBG(23, "finagle_precal...\n");
+ if ((s->do_clever_precal) || (s->do_real_calib)) {
+ if ((status = compare_mode_sense(s, &match)) != SANE_STATUS_GOOD)
+ return status;
+ if (((s->do_real_calib) && (!s->calib_once)) || /* user want recal */
+ (!match) || /* or, possible reset */
+ ((s->mode == MS_MODE_COLOR) && /* or, other weirdness */
+ (s->precal_record < MS_PRECAL_COLOR)) ||
+ ((s->mode == MS_MODE_COLOR) &&
+ (s->expandedresolution) &&
+ (s->precal_record < MS_PRECAL_EXP_COLOR))) {
+ DBG(23, "finagle_precal: must precalibrate!\n");
+ s->precal_record = MS_PRECAL_NONE;
+ if (s->do_real_calib) { /* do a real calibration if allowed */
+ if ((status = do_real_calibrate(s)) != SANE_STATUS_GOOD)
+ return status;
+ } else if (s->do_clever_precal) {/* otherwise do the fake-scan version */
+ if ((status = do_precalibrate(s)) != SANE_STATUS_GOOD)
+ return status;
+ }
+ if (s->mode == MS_MODE_COLOR) {
+ if (s->expandedresolution)
+ s->precal_record = MS_PRECAL_EXP_COLOR;
+ else
+ s->precal_record = MS_PRECAL_COLOR;
+ } else
+ s->precal_record = MS_PRECAL_GRAY;
+ } else
+ DBG(23, "finagle_precal: no precalibrate necessary.\n");
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Set pass-dependent parameters (for 3-pass color scans) */
+/********************************************************************/
+static void
+set_pass_parameters (SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+
+ if (s->threepasscolor) {
+ s->this_pass += 1;
+ DBG(23, "set_pass_parameters: three-pass, on %d\n", s->this_pass);
+ switch (s->this_pass) {
+ case 1:
+ s->filter = MS_FILT_RED;
+ s->params.format = SANE_FRAME_RED;
+ s->params.last_frame = SANE_FALSE;
+ break;
+ case 2:
+ s->filter = MS_FILT_GREEN;
+ s->params.format = SANE_FRAME_GREEN;
+ s->params.last_frame = SANE_FALSE;
+ break;
+ case 3:
+ s->filter = MS_FILT_BLUE;
+ s->params.format = SANE_FRAME_BLUE;
+ s->params.last_frame = SANE_TRUE;
+ break;
+ default:
+ s->filter = MS_FILT_CLEAR;
+ DBG(23, "set_pass_parameters: What?!? pass %d = filter?\n",
+ s->this_pass);
+ break;
+ }
+ } else
+ s->this_pass = 0;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/***** Packing functions *****/
+/***** ...process raw scanner bytes, and shove into *****/
+/***** the ring buffer *****/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* Process flat (byte-by-byte) data */
+/********************************************************************/
+static SANE_Status pack_flat_data(Microtek_Scanner *s, size_t nlines)
+{
+ SANE_Status status;
+ ring_buffer *rb = s->rb;
+ size_t nbytes = nlines * rb->bpl;
+
+ size_t start = (rb->head_complete + rb->complete_count) % rb->size;
+ size_t max_xfer =
+ (start < rb->head_complete) ?
+ (rb->head_complete - start) :
+ (rb->size - start + rb->head_complete);
+ size_t length = MIN(nbytes, max_xfer);
+
+ if (nbytes > max_xfer) {
+ DBG(23, "pack_flat: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)(nbytes - max_xfer));
+ status = ring_expand(rb, (nbytes - max_xfer));
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+
+ if (s->doexpansion) {
+ unsigned int line, bit;
+ SANE_Byte *sb, *db, byte;
+
+ size_t pos;
+
+ sb = s->scsi_buffer;
+ db = rb->base;
+ pos = start;
+
+ if (!(s->multibit)) {
+ for (line=0; line<nlines; line++) {
+ size_t i;
+ double x1, x2, n1, n2;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < rb->bpl;
+ i++) {
+ byte = 0;
+ for (bit=0;
+ bit < 8;
+ bit++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ /* #define getbit(byte, index) (((byte)>>(index))&1) */
+ byte |=
+ ((
+ (x2 == n2) ?
+ (((sb[(int)n1 / 8])>>(7 - ((int)n1) % 8))&1) :
+ (((double)(((sb[(int)n1/8])>>(7-(int)n1%8))&1) * (n2 - x1) +
+ (double)(((sb[(int)n2/8])>>(7-(int)n2%8))&1) * (x2 - n2)
+ ) / s->exp_aspect)
+ ) > 0.5) << (7 - bit);
+ }
+ db[pos] = byte;
+ if (++pos >= rb->size) pos = 0;
+ }
+ sb += s->pixel_bpl;
+ }
+ } else { /* multibit scan (8 is assumed!) */
+ for (line=0; line<nlines; line++) {
+ double x1, x2, n1, n2;
+ int i;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < s->dest_ppl;
+ i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ db[pos] =
+ (x2 == n2) ?
+ sb[(int)n1] :
+ (int)(((double)sb[(int)n1] * (n2 - x1) +
+ (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect);
+ if (++pos >= rb->size) pos = 0;
+ }
+ sb += s->pixel_bpl;
+ }
+ }
+ } else {
+ /* adjust for rollover!!! */
+ if ((start + length) < rb->size) {
+ memcpy(rb->base + start, s->scsi_buffer, length);
+ } else {
+ size_t chunk1 = rb->size - start;
+ size_t chunk2 = length - chunk1;
+ memcpy(rb->base + start, s->scsi_buffer, chunk1);
+ memcpy(rb->base, s->scsi_buffer + chunk1, chunk2);
+ }
+ }
+ rb->complete_count += length;
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Process sequential R-G-B scan lines (who uses this??? ) */
+/********************************************************************/
+static SANE_Status
+pack_seqrgb_data (Microtek_Scanner *s, size_t nlines)
+{
+ ring_buffer *rb = s->rb;
+ unsigned int seg;
+ SANE_Byte *db = rb->base;
+ SANE_Byte *sb = s->scsi_buffer;
+ size_t completed;
+ size_t spot;
+ SANE_Byte id;
+
+ {
+ size_t ar, ag, ab; /* allowed additions */
+ size_t dr, dg, db; /* additions which will occur */
+ SANE_Status status;
+
+ dr = dg = db = nlines * rb->bpl;
+ ar = rb->size - (rb->complete_count + rb->red_extra * 3);
+ ag = rb->size - (rb->complete_count + rb->green_extra * 3);
+ ab = rb->size - (rb->complete_count + rb->blue_extra * 3);
+ DBG(23, "pack_seq: dr/ar: %lu/%lu dg/ag: %lu/%lu db/ab: %lu/%lu\n",
+ (u_long)dr, (u_long)ar,
+ (u_long)dg, (u_long)ag,
+ (u_long)db, (u_long)ab);
+ if ((dr > ar) ||
+ (dg > ag) ||
+ (db > ab)) {
+ size_t increase = 0;
+ if (dr > ar) increase = (dr - ar);
+ if (dg > ag) increase = MAX(increase, (dg - ag));
+ if (db > ab) increase = MAX(increase, (db - ab));
+ DBG(23, "pack_seq: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)increase);
+ status = ring_expand(rb, increase);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ }
+
+ for (seg = 0, id = 0; seg < nlines * 3; seg++, id = (id+1)%3) {
+ switch (id) {
+ case 0: spot = rb->tail_red; break;
+ case 1: spot = rb->tail_green; break;
+ case 2: spot = rb->tail_blue; break;
+ default:
+ DBG(18, "pack_seq: missing scanline RGB header!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ if (s->doexpansion) {
+ int i;
+ double x1, x2, n1, n2;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < s->dest_ppl;
+ i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ db[spot] =
+ (x2 == n2) ?
+ sb[(int)n1] :
+ (int)(((double)sb[(int)n1] * (n2 - x1) +
+ (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect);
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ sb += s->ppl;
+ } else {
+ size_t i;
+ for (i=0; i < rb->ppl; i++) {
+ db[spot] = *sb;
+ sb++;
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ }
+
+ switch (id) {
+ case 0: rb->tail_red = spot; rb->red_extra += rb->ppl; break;
+ case 1: rb->tail_green = spot; rb->green_extra += rb->ppl; break;
+ case 2: rb->tail_blue = spot; rb->blue_extra += rb->ppl; break;
+ }
+ }
+
+ completed = MIN(rb->red_extra, MIN(rb->green_extra, rb->blue_extra));
+ rb->complete_count += completed * 3; /* 3 complete bytes per pixel! */
+ rb->red_extra -= completed;
+ rb->green_extra -= completed;
+ rb->blue_extra -= completed;
+
+ DBG(18, "pack_seq: extra r: %lu g: %lu b: %lu\n",
+ (u_long)rb->red_extra,
+ (u_long)rb->green_extra,
+ (u_long)rb->blue_extra);
+ DBG(18, "pack_seq: completed: %lu complete: %lu\n",
+ (u_long)completed, (u_long)rb->complete_count);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+/********************************************************************/
+/* Process non-sequential R,G, and B scan-lines */
+/********************************************************************/
+static SANE_Status
+pack_goofyrgb_data(Microtek_Scanner *s, size_t nlines)
+{
+ ring_buffer *rb = s->rb;
+ unsigned int seg; /* , i;*/
+ SANE_Byte *db;
+ SANE_Byte *sb = s->scsi_buffer;
+ size_t completed;
+ size_t spot;
+ SANE_Byte id;
+
+ /* prescan to decide if ring should be expanded */
+ {
+ size_t ar, ag, ab; /* allowed additions */
+ size_t dr, dg, db; /* additions which will occur */
+ SANE_Status status;
+ SANE_Byte *pt;
+
+ for (dr = dg = db = 0, seg = 0, pt = s->scsi_buffer + 1;
+ seg < nlines * 3;
+ seg++, pt += s->ppl + 2) {
+ switch (*pt) {
+ case 'R': dr += rb->bpl; break;
+ case 'G': dg += rb->bpl; break;
+ case 'B': db += rb->bpl; break;
+ }
+ }
+ ar = rb->size - (rb->complete_count + rb->red_extra * 3);
+ ag = rb->size - (rb->complete_count + rb->green_extra * 3);
+ ab = rb->size - (rb->complete_count + rb->blue_extra * 3);
+ DBG(23, "pack_goofy: dr/ar: %lu/%lu dg/ag: %lu/%lu db/ab: %lu/%lu\n",
+ (u_long)dr, (u_long)ar,
+ (u_long)dg, (u_long)ag,
+ (u_long)db, (u_long)ab);
+ /* >, or >= ???????? */
+ if ((dr > ar) ||
+ (dg > ag) ||
+ (db > ab)) {
+ size_t increase = 0;
+ if (dr > ar) increase = (dr - ar);
+ if (dg > ag) increase = MAX(increase, (dg - ag));
+ if (db > ab) increase = MAX(increase, (db - ab));
+ DBG(23, "pack_goofy: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)increase);
+ status = ring_expand(rb, increase);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ }
+
+ db = rb->base;
+ for (seg = 0; seg < nlines * 3; seg++) {
+ sb++; /* skip first byte in line (two byte header) */
+ id = *sb;
+ switch (id) {
+ case 'R': spot = rb->tail_red; break;
+ case 'G': spot = rb->tail_green; break;
+ case 'B': spot = rb->tail_blue; break;
+ default:
+ DBG(18, "pack_goofy: missing scanline RGB header!\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+ sb++; /* skip the other header byte */
+
+ if (s->doexpansion) {
+ int i;
+ double x1, x2, n1, n2;
+ for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2);
+ i < s->dest_ppl;
+ i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) {
+ db[spot] =
+ (x2 == n2) ?
+ sb[(int)n1] :
+ (int)(((double)sb[(int)n1] * (n2 - x1) +
+ (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect);
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ sb += s->ppl;
+ } else {
+ unsigned int i;
+ for (i=0; i < rb->ppl; i++) {
+ db[spot] = *sb;
+ sb++;
+ if ((spot += 3) >= rb->size) spot -= rb->size;
+ }
+ }
+ switch (id) {
+ case 'R': rb->tail_red = spot; rb->red_extra += rb->ppl; break;
+ case 'G': rb->tail_green = spot; rb->green_extra += rb->ppl; break;
+ case 'B': rb->tail_blue = spot; rb->blue_extra += rb->ppl; break;
+ }
+ }
+
+ completed = MIN(rb->red_extra, MIN(rb->green_extra, rb->blue_extra));
+ rb->complete_count += completed * 3; /* 3 complete bytes per pixel! */
+ rb->red_extra -= completed;
+ rb->green_extra -= completed;
+ rb->blue_extra -= completed;
+
+ DBG(18, "pack_goofy: extra r: %lu g: %lu b: %lu\n",
+ (u_long)rb->red_extra,
+ (u_long)rb->green_extra,
+ (u_long)rb->blue_extra);
+ DBG(18, "pack_goofy: completed: %lu complete: %lu\n",
+ (u_long)completed, (u_long)rb->complete_count);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Process R1R2-G1G2-B1B2 double pixels (AGFA StudioStar) */
+/********************************************************************/
+
+static SANE_Status
+pack_seq2r2g2b_data(Microtek_Scanner *s, size_t nlines)
+{
+ SANE_Status status;
+ ring_buffer *rb = s->rb;
+ size_t nbytes = nlines * rb->bpl;
+
+ size_t start = (rb->head_complete + rb->complete_count) % rb->size;
+ size_t max_xfer =
+ (start < rb->head_complete) ?
+ (rb->head_complete - start) :
+ (rb->size - start + rb->head_complete);
+ size_t length = MIN(nbytes, max_xfer);
+
+ if (nbytes > max_xfer) {
+ DBG(23, "pack_2r2g2b: must expand ring, %lu + %lu\n",
+ (u_long)rb->size, (u_long)(nbytes - max_xfer));
+ status = ring_expand(rb, (nbytes - max_xfer));
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ {
+ unsigned int line;
+ int p;
+ size_t pos = start;
+ SANE_Byte *sb = s->scsi_buffer;
+ SANE_Byte *db = rb->base;
+
+ for (line = 0; line < nlines; line++) {
+ for (p = 0; p < s->dest_ppl; p += 2){
+ /* first pixel */
+ db[pos] = sb[0];
+ if (++pos >= rb->size) pos = 0; /* watch out for ringbuff end? */
+ db[pos] = sb[2];
+ if (++pos >= rb->size) pos = 0;
+ db[pos] = sb[4];
+ if (++pos >= rb->size) pos = 0;
+ /* second pixel */
+ db[pos] = sb[1];
+ if (++pos >= rb->size) pos = 0;
+ db[pos] = sb[3];
+ if (++pos >= rb->size) pos = 0;
+ db[pos] = sb[5];
+ if (++pos >= rb->size) pos = 0;
+ sb += 6;
+ }
+ }
+ }
+ rb->complete_count += length;
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/***** the basic scanning chunks for sane_read() *****/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* Request bytes from scanner (and put in scsi_buffer) */
+/********************************************************************/
+static SANE_Status
+read_from_scanner (Microtek_Scanner *s, int *nlines)
+{
+ SANE_Status status;
+ SANE_Int busy, linewidth, remaining;
+ size_t buffsize;
+
+ DBG(23, "read_from_scanner...\n");
+ if (s->unscanned_lines > 0) {
+ status = get_scan_status(s, &busy, &linewidth, &remaining);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(18, "read_from_scanner: bad get_scan_status!\n");
+ return status;
+ }
+ DBG(18, "read_from_scanner: gss busy, linewidth, remaining: %d, %d, %d\n",
+ busy, linewidth, remaining);
+ } else {
+ DBG(18, "read_from_scanner: no gss/no unscanned\n");
+ remaining = 0;
+ }
+
+ *nlines = MIN(remaining, s->max_scsi_lines);
+ DBG(18, "sane_read: max_scsi: %d, rem: %d, nlines: %d\n",
+ s->max_scsi_lines, remaining, *nlines);
+
+ /* grab them bytes! (only if the scanner still has bytes to give...) */
+ if (*nlines > 0) {
+ buffsize = *nlines * (s->pixel_bpl + s->header_bpl);/* == "* linewidth" */
+ status = read_scan_data(s, *nlines, s->scsi_buffer, &buffsize);
+ if (status != SANE_STATUS_GOOD) {
+ DBG(18, "sane_read: bad read_scan_data!\n");
+ return status;
+ }
+ s->unscanned_lines -= *nlines;
+ DBG(18, "sane_read: buffsize: %lu, unscanned: %d\n",
+ (u_long) buffsize, s->unscanned_lines);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* Process scanner bytes, and shove in ring_buffer */
+/********************************************************************/
+static SANE_Status
+pack_into_ring(Microtek_Scanner *s, int nlines)
+{
+ SANE_Status status;
+
+ DBG(23, "pack_into_ring...\n");
+ switch (s->line_format) {
+ case MS_LNFMT_FLAT:
+ status = pack_flat_data(s, nlines); break;
+ case MS_LNFMT_SEQ_RGB:
+ status = pack_seqrgb_data(s, nlines); break;
+ case MS_LNFMT_GOOFY_RGB:
+ status = pack_goofyrgb_data(s, nlines); break;
+ case MS_LNFMT_SEQ_2R2G2B:
+ status = pack_seq2r2g2b_data(s, nlines); break;
+ default:
+ status = SANE_STATUS_JAMMED;
+ }
+ return status;
+}
+
+
+
+/********************************************************************/
+/* Pack processed image bytes into frontend destination buffer */
+/********************************************************************/
+static SANE_Int
+pack_into_dest(SANE_Byte *dest_buffer, size_t dest_length, ring_buffer *rb)
+{
+ size_t ret_length = MIN(rb->complete_count, dest_length);
+
+ DBG(23, "pack_into_dest...\n");
+ DBG(23, "pack_into_dest: rl: %lu sz: %lu hc: %lu\n",
+ (u_long)ret_length, (u_long)rb->size, (u_long)rb->head_complete);
+ /* adjust for rollover!!! */
+ if ((rb->head_complete + ret_length) < rb->size) {
+ memcpy(dest_buffer, rb->base + rb->head_complete, ret_length);
+ rb->head_complete += ret_length;
+ } else {
+ size_t chunk1 = rb->size - rb->head_complete;
+ size_t chunk2 = ret_length - chunk1;
+ memcpy(dest_buffer, rb->base + rb->head_complete, chunk1);
+ memcpy(dest_buffer + chunk1, rb->base, chunk2);
+ rb->head_complete = chunk2;
+ }
+ rb->complete_count -= ret_length;
+ return ret_length;
+}
+
+
+
+/********************************************************************/
+/********************************************************************/
+/****** "Registered" SANE API Functions *****************************/
+/********************************************************************/
+/********************************************************************/
+
+
+/********************************************************************/
+/* sane_init() */
+/********************************************************************/
+SANE_Status
+sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ char dev_name[PATH_MAX];
+ size_t len;
+ FILE *fp;
+
+ authorize = authorize;
+ DBG_INIT();
+ DBG(1, "sane_init: MICROTEK says hello! (v%d.%d.%d)\n",
+ MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH);
+ /* return the SANE version we got compiled under */
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ /* parse config file */
+ fp = sanei_config_open (MICROTEK_CONFIG_FILE);
+ if (!fp) {
+ /* default to /dev/scanner instead of insisting on config file */
+ DBG(1, "sane_init: missing config file '%s'\n", MICROTEK_CONFIG_FILE);
+ attach_scanner("/dev/scanner", 0);
+ return SANE_STATUS_GOOD;
+ }
+ while (sanei_config_read(dev_name, sizeof (dev_name), fp)) {
+ DBG(23, "sane_init: config-> %s\n", dev_name);
+ if (dev_name[0] == '#') continue; /* ignore comments */
+ if (!(strncmp("noprecal", dev_name, 8))) {
+ DBG(23,
+ "sane_init: Clever Precalibration will be forcibly disabled...\n");
+ inhibit_clever_precal = SANE_TRUE;
+ continue;
+ }
+ if (!(strncmp("norealcal", dev_name, 9))) {
+ DBG(23,
+ "sane_init: Real calibration will be forcibly disabled...\n");
+ inhibit_real_calib = SANE_TRUE;
+ continue;
+ }
+ len = strlen (dev_name);
+ if (!len) continue; /* ignore empty lines */
+ sanei_config_attach_matching_devices (dev_name, attach_one);
+ }
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_get_devices */
+/********************************************************************/
+SANE_Status
+sane_get_devices(const SANE_Device ***device_list,
+ SANE_Bool local_only)
+{
+ Microtek_Device *dev;
+ int i;
+
+ local_only = local_only;
+ DBG(10, "sane_get_devices\n");
+ /* we keep an internal copy */
+ if (devlist)
+ free(devlist); /* hmm, free it if we want a new one, I guess. YYYYY*/
+
+ devlist = malloc((num_devices + 1) * sizeof(devlist[0]));
+ if (!devlist) return SANE_STATUS_NO_MEM;
+
+ for (i=0, dev=first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_open */
+/********************************************************************/
+SANE_Status
+sane_open(SANE_String_Const devicename,
+ SANE_Handle *handle)
+{
+ Microtek_Scanner *scanner;
+ Microtek_Device *dev;
+ SANE_Status status;
+
+ DBG(10, "sane_open\n");
+ /* find device... */
+ DBG(23, "sane_open: find device...\n");
+ if (devicename[0]) {
+ for (dev = first_dev; dev; dev = dev->next) {
+ if (strcmp(dev->sane.name, devicename) == 0)
+ break;
+ }
+ if (!dev) { /* not in list, try manually... */
+ status = attach_scanner(devicename, &dev);
+ if (status != SANE_STATUS_GOOD) return status;
+ }
+ } else { /* no device specified, so use first */
+ dev = first_dev;
+ }
+ if (!dev) return SANE_STATUS_INVAL;
+
+ /* create a scanner... */
+ DBG(23, "sane_open: create scanner...\n");
+ scanner = malloc(sizeof(*scanner));
+ if (!scanner) return SANE_STATUS_NO_MEM;
+ memset(scanner, 0, sizeof(*scanner));
+
+ /* initialize scanner dependent stuff */
+ DBG(23, "sane_open: initialize scanner dependent stuff...\n");
+ /* ZZZZZZZZZZZZZZ */
+ scanner->unit_type =
+ (dev->info.unit_type & MI_UNIT_PIXELS) ? MS_UNIT_PIXELS : MS_UNIT_18INCH;
+ scanner->res_type =
+ (dev->info.res_step & MI_RESSTEP_1PER) ? MS_RES_1PER : MS_RES_5PER;
+ scanner->midtone_support =
+ (dev->info.enhance_cap & MI_ENH_CAP_MIDTONE) ? SANE_TRUE : SANE_FALSE;
+ scanner->paper_length =
+ (scanner->unit_type == MS_UNIT_PIXELS) ?
+ dev->info.max_y :
+ (SANE_Int)((double)dev->info.max_y * 8.0 /
+ (double)dev->info.base_resolution);
+ /*
+ (SANE_Int)(SANE_UNFIX(dev->info.max_y) * dev->info.base_resolution) :
+ (SANE_Int)(SANE_UNFIX(dev->info.max_y) * 8);
+ ZZZZZZZ */
+ scanner->bright_r = 0;
+ scanner->bright_g = 0;
+ scanner->bright_b = 0;
+
+ /* calibration shenanigans */
+ if ((dev->info.extra_cap & MI_EXCAP_DIS_RECAL) &&
+ (!(inhibit_real_calib))) {
+ DBG(23, "sane_open: Real calibration enabled.\n");
+ scanner->allow_calibrate = SANE_FALSE;
+ scanner->do_real_calib = SANE_TRUE;
+ scanner->do_clever_precal = SANE_FALSE;
+ } else if ((dev->info.extra_cap & MI_EXCAP_DIS_RECAL) &&
+ (!(inhibit_clever_precal))) {
+ DBG(23, "sane_open: Clever precalibration enabled.\n");
+ scanner->allow_calibrate = SANE_FALSE;
+ scanner->do_real_calib = SANE_FALSE;
+ scanner->do_clever_precal = SANE_TRUE;
+ } else {
+ DBG(23, "sane_open: All calibration routines disabled.\n");
+ scanner->allow_calibrate = SANE_TRUE;
+ scanner->do_real_calib = SANE_FALSE;
+ scanner->do_clever_precal = SANE_FALSE;
+ }
+
+ scanner->onepass = (dev->info.modes & MI_MODES_ONEPASS);
+ scanner->allowbacktrack = SANE_TRUE; /* ??? XXXXXXX */
+ scanner->reversecolors = SANE_FALSE;
+ scanner->fastprescan = SANE_FALSE;
+ scanner->bits_per_color = 8;
+
+ /* init gamma tables */
+ if (dev->info.max_lookup_size) {
+ int j, v, max_entry;
+ DBG(23, "sane_open: init gamma tables...\n");
+ scanner->gamma_entries = dev->info.max_lookup_size;
+ scanner->gamma_entry_size = dev->info.gamma_size;
+ scanner->gamma_bit_depth = dev->info.max_gamma_bit_depth;
+ max_entry = (1 << scanner->gamma_bit_depth) - 1;
+ scanner->gamma_entry_range.min = 0;
+ scanner->gamma_entry_range.max = max_entry;
+ scanner->gamma_entry_range.quant = 1;
+
+ scanner->gray_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->gray_lut[0]));
+ scanner->red_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->red_lut[0]));
+ scanner->green_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->green_lut[0]));
+ scanner->blue_lut = calloc(scanner->gamma_entries,
+ sizeof(scanner->blue_lut[0]));
+ if ((scanner->gray_lut == NULL) ||
+ (scanner->red_lut == NULL) ||
+ (scanner->green_lut == NULL) ||
+ (scanner->blue_lut == NULL)) {
+ DBG(23, "sane_open: unable to allocate space for %d-entry LUT's;\n",
+ scanner->gamma_entries);
+ DBG(23, " so, gamma tables now DISABLED.\n");
+ free(scanner->gray_lut);
+ free(scanner->red_lut);
+ free(scanner->green_lut);
+ free(scanner->blue_lut);
+ }
+ for (j=0; j<scanner->gamma_entries; j += scanner->gamma_entry_size) {
+ v = (SANE_Int)
+ ((double) j * (double) max_entry /
+ ((double) scanner->gamma_entries - 1.0) + 0.5);
+ scanner->gray_lut[j] = v;
+ scanner->red_lut[j] = v;
+ scanner->green_lut[j] = v;
+ scanner->blue_lut[j] = v;
+ }
+ } else {
+ DBG(23, "sane_open: NO gamma tables. (max size = %lu)\n",
+ (u_long)dev->info.max_lookup_size);
+ scanner->gamma_entries = 0;
+ scanner->gray_lut = NULL;
+ scanner->red_lut = NULL;
+ scanner->green_lut = NULL;
+ scanner->blue_lut = NULL;
+ }
+
+ DBG(23, "sane_open: init pass-time variables...\n");
+ scanner->scanning = SANE_FALSE;
+ scanner->this_pass = 0;
+ scanner->sfd = -1;
+ scanner->dev = dev;
+ scanner->sense_flags = 0;
+ scanner->scan_started = SANE_FALSE;
+ scanner->woe = SANE_FALSE;
+ scanner->cancel = SANE_FALSE;
+
+ DBG(23, "sane_open: init clever cache...\n");
+ /* clear out that clever cache, so it doesn't match anything */
+ {
+ int j;
+ for (j=0; j<10; j++)
+ scanner->mode_sense_cache[j] = 0;
+ scanner->precal_record = MS_PRECAL_NONE;
+ }
+
+ DBG(23, "sane_open: initialize options: \n");
+ if ((status = init_options(scanner)) != SANE_STATUS_GOOD) return status;
+
+ scanner->next = first_handle;
+ first_handle = scanner;
+ *handle = scanner;
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_close */
+/********************************************************************/
+void
+sane_close (SANE_Handle handle)
+{
+ Microtek_Scanner *ms = handle;
+
+ DBG(10, "sane_close...\n");
+ /* free malloc'ed stuff (strdup counts too!) */
+ free((void *) ms->sod[OPT_MODE].constraint.string_list);
+ free((void *) ms->sod[OPT_SOURCE].constraint.string_list);
+ free(ms->val[OPT_MODE].s);
+ free(ms->val[OPT_HALFTONE_PATTERN].s);
+ free(ms->val[OPT_SOURCE].s);
+ free(ms->val[OPT_CUSTOM_GAMMA].s);
+ free(ms->gray_lut);
+ free(ms->red_lut);
+ free(ms->green_lut);
+ free(ms->blue_lut);
+ /* remove Scanner from linked list */
+ if (first_handle == ms)
+ first_handle = ms->next;
+ else {
+ Microtek_Scanner *ts = first_handle;
+ while ((ts != NULL) && (ts->next != ms)) ts = ts->next;
+ ts->next = ts->next->next; /* == ms->next */
+ }
+ /* finally, say goodbye to the Scanner */
+ free(ms);
+}
+
+
+
+/********************************************************************/
+/* sane_get_option_descriptor */
+/********************************************************************/
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle,
+ SANE_Int option)
+{
+ Microtek_Scanner *scanner = handle;
+
+ DBG(96, "sane_get_option_descriptor (%d)...\n", option);
+ if ((unsigned)option >= NUM_OPTIONS) return NULL;
+ return &(scanner->sod[option]);
+}
+
+
+
+/********************************************************************/
+/* sane_control_option */
+/********************************************************************/
+SANE_Status
+sane_control_option (SANE_Handle handle,
+ SANE_Int option,
+ SANE_Action action,
+ void *value,
+ SANE_Int *info)
+{
+ Microtek_Scanner *scanner = handle;
+ SANE_Option_Descriptor *sod;
+ Option_Value *val;
+ SANE_Status status;
+
+ DBG(96, "sane_control_option (opt=%d,act=%d,val=%p,info=%p)\n",
+ option, action, value, (void*) info);
+
+ sod = scanner->sod;
+ val = scanner->val;
+
+ /* no changes while in mid-pass! */
+ if (scanner->scanning) return SANE_STATUS_DEVICE_BUSY;
+ /* and... no changes while in middle of three-pass series! */
+ if (scanner->this_pass != 0) return SANE_STATUS_DEVICE_BUSY;
+
+ if ( ((option >= NUM_OPTIONS) || (option < 0)) ||
+ (!SANE_OPTION_IS_ACTIVE(scanner->sod[option].cap)) )
+ return SANE_STATUS_INVAL;
+
+ if (info) *info = 0;
+
+ /* choose by action */
+ switch (action) {
+
+ case SANE_ACTION_GET_VALUE:
+ switch (option) {
+ /* word options... */
+ case OPT_RESOLUTION:
+ case OPT_SPEED:
+ case OPT_BACKTRACK:
+ case OPT_NEGATIVE:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_EXPOSURE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_HIGHLIGHT:
+ case OPT_SHADOW:
+ case OPT_MIDTONE:
+ case OPT_GAMMA_BIND:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ case OPT_EXP_RES:
+ case OPT_CALIB_ONCE:
+ *(SANE_Word *)value = val[option].w;
+ return SANE_STATUS_GOOD;
+ /* word-array options... */
+ /* case OPT_HALFTONE_PATTERN:*/
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy(value, val[option].wa, sod[option].size);
+ return SANE_STATUS_GOOD;
+ /* string options... */
+ case OPT_MODE:
+ case OPT_HALFTONE_PATTERN:
+ case OPT_CUSTOM_GAMMA:
+ case OPT_SOURCE:
+ strcpy(value, val[option].s);
+ return SANE_STATUS_GOOD;
+ /* others.... */
+ case OPT_NUM_OPTS:
+ *(SANE_Word *) value = NUM_OPTIONS;
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ break;
+
+ case SANE_ACTION_SET_VALUE: {
+ status = sanei_constrain_value(sod + option, value, info);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ switch (option) {
+ /* set word options... */
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_RESOLUTION:
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ case OPT_SPEED:
+ case OPT_PREVIEW:
+ case OPT_BACKTRACK:
+ case OPT_NEGATIVE:
+ case OPT_EXPOSURE:
+ case OPT_BRIGHTNESS:
+ case OPT_CONTRAST:
+ case OPT_ANALOG_GAMMA:
+ case OPT_ANALOG_GAMMA_R:
+ case OPT_ANALOG_GAMMA_G:
+ case OPT_ANALOG_GAMMA_B:
+ val[option].w = *(SANE_Word *)value;
+ return SANE_STATUS_GOOD;
+
+ case OPT_HIGHLIGHT:
+ case OPT_SHADOW:
+ case OPT_MIDTONE:
+ val[option].w = *(SANE_Word *)value;
+ /* we need to (silently) make sure shadow <= midtone <= highlight */
+ if (scanner->midtone_support) {
+ if (val[OPT_SHADOW].w > val[OPT_MIDTONE].w) {
+ if (option == OPT_SHADOW)
+ val[OPT_SHADOW].w = val[OPT_MIDTONE].w;
+ else
+ val[OPT_MIDTONE].w = val[OPT_SHADOW].w;
+ }
+ if (val[OPT_HIGHLIGHT].w < val[OPT_MIDTONE].w) {
+ if (option == OPT_HIGHLIGHT)
+ val[OPT_HIGHLIGHT].w = val[OPT_MIDTONE].w;
+ else
+ val[OPT_MIDTONE].w = val[OPT_HIGHLIGHT].w;
+ }
+ } else {
+ if (val[OPT_SHADOW].w > val[OPT_HIGHLIGHT].w) {
+ if (option == OPT_SHADOW)
+ val[OPT_SHADOW].w = val[OPT_HIGHLIGHT].w;
+ else
+ val[OPT_HIGHLIGHT].w = val[OPT_SHADOW].w;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_EXP_RES:
+ if (val[option].w != *(SANE_Word *) value) {
+ val[option].w = *(SANE_Word *)value;
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ if (val[OPT_EXP_RES].w) {
+ sod[OPT_RESOLUTION].constraint.range = &(scanner->exp_res_range);
+ val[OPT_RESOLUTION].w *= 2;
+ } else {
+ sod[OPT_RESOLUTION].constraint.range = &(scanner->res_range);
+ val[OPT_RESOLUTION].w /= 2;
+ }
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_CALIB_ONCE:
+ val[option].w = *(SANE_Word *)value;
+ /* toggling off and on should force a recalibration... */
+ if (!(val[option].w)) scanner->precal_record = MS_PRECAL_NONE;
+ return SANE_STATUS_GOOD;
+
+ case OPT_GAMMA_BIND:
+ case OPT_CUSTOM_GAMMA:
+ if (option == OPT_GAMMA_BIND) {
+ if (val[option].w != *(SANE_Word *) value)
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+ val[option].w = *(SANE_Word *) value;
+ } else if (option == OPT_CUSTOM_GAMMA) {
+ if (val[option].s) {
+ if (strcmp(value, val[option].s))
+ if (info) *info |= SANE_INFO_RELOAD_OPTIONS;
+ free(val[option].s);
+ }
+ val[option].s = strdup(value);
+ }
+ if ( !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)) ||
+ !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_SCALAR)) ) {
+ sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if ( !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)) ||
+ !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_TABLE)) ) {
+ sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ }
+ if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_SCALAR))) {
+ if (val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ sod[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE;
+ } else {
+ sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_ANALOG_GAMMA_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_TABLE))) {
+ if (val[OPT_GAMMA_BIND].w == SANE_TRUE) {
+ sod[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
+ } else {
+ sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
+ sod[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
+ }
+ }
+ if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)))
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ else if (!(strcmp(val[OPT_MODE].s, M_COLOR)))
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ return SANE_STATUS_GOOD;
+
+
+ case OPT_MODE:
+ if (val[option].s) {
+ if (strcmp(val[option].s, value))
+ if (info)
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ free(val[option].s);
+ }
+ val[option].s = strdup(value);
+ if (strcmp(val[option].s, M_HALFTONE)) {
+ sod[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
+ } else {
+ sod[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
+ }
+ if (strcmp(val[option].s, M_COLOR)) { /* not color */
+ /*val[OPT_GAMMA_BIND].w = SANE_TRUE;*/
+ DBG(23, "FLIP ma LID! bind is %d\n", val[OPT_GAMMA_BIND].w);
+ {
+ SANE_Bool Trueness = SANE_TRUE;
+ SANE_Status status;
+ status = sane_control_option(handle,
+ OPT_GAMMA_BIND,
+ SANE_ACTION_SET_VALUE,
+ &Trueness,
+ NULL);
+ DBG(23, "stat is: %d\n", status);
+ }
+ DBG(23, "LID be FLIPPED! bind is %d\n", val[OPT_GAMMA_BIND].w);
+ sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
+ /* sod[OPT_FORCE_3PASS].cap |= SANE_CAP_INACTIVE;*/
+ } else {
+ sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE;
+ /* if (scanner->dev->info.modes & MI_MODES_ONEPASS)
+ sod[OPT_FORCE_3PASS].cap &= ~SANE_CAP_INACTIVE;*/
+ }
+ return SANE_STATUS_GOOD;
+
+ case OPT_HALFTONE_PATTERN:
+ case OPT_SOURCE:
+ if (val[option].s) free(val[option].s);
+ val[option].s = strdup(value);
+ return SANE_STATUS_GOOD;
+ case OPT_GAMMA_VECTOR:
+ case OPT_GAMMA_VECTOR_R:
+ case OPT_GAMMA_VECTOR_G:
+ case OPT_GAMMA_VECTOR_B:
+ memcpy(val[option].wa, value, sod[option].size);
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ return SANE_STATUS_UNSUPPORTED; /* We are DUMB. */
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_get_parameters */
+/********************************************************************/
+SANE_Status
+sane_get_parameters (SANE_Handle handle,
+ SANE_Parameters *params)
+{
+ Microtek_Scanner *s = handle;
+
+ DBG(23, "sane_get_parameters...\n");
+
+ if (!s->scanning) {
+ /* decipher scan mode */
+ if (!(strcmp(s->val[OPT_MODE].s, M_LINEART)))
+ s->mode = MS_MODE_LINEART;
+ else if (!(strcmp(s->val[OPT_MODE].s, M_HALFTONE)))
+ s->mode = MS_MODE_HALFTONE;
+ else if (!(strcmp(s->val[OPT_MODE].s, M_GRAY)))
+ s->mode = MS_MODE_GRAY;
+ else if (!(strcmp(s->val[OPT_MODE].s, M_COLOR)))
+ s->mode = MS_MODE_COLOR;
+
+ if (s->mode == MS_MODE_COLOR) {
+ if (s->onepass) {
+ /* regular one-pass */
+ DBG(23, "sane_get_parameters: regular 1-pass color\n");
+ s->threepasscolor = SANE_FALSE;
+ s->onepasscolor = SANE_TRUE;
+ s->color_seq = s->dev->info.color_sequence;
+ } else { /* 3-pass scanner */
+ DBG(23, "sane_get_parameters: regular 3-pass color\n");
+ s->threepasscolor = SANE_TRUE;
+ s->onepasscolor = SANE_FALSE;
+ s->color_seq = s->dev->info.color_sequence;
+ }
+ } else { /* not color! */
+ DBG(23, "sane_get_parameters: non-color\n");
+ s->threepasscolor = SANE_FALSE;
+ s->onepasscolor = SANE_FALSE;
+ s->color_seq = s->dev->info.color_sequence;
+ }
+
+ s->transparency = !(strcmp(s->val[OPT_SOURCE].s, M_TRANS));
+ s->useADF = !(strcmp(s->val[OPT_SOURCE].s, M_AUTOFEED));
+ /* disallow exp. res. during preview scan XXXXXXXXXXX */
+ /*s->expandedresolution =
+ (s->val[OPT_EXP_RES].w) && !(s->val[OPT_PREVIEW].w);*/
+ s->expandedresolution = (s->val[OPT_EXP_RES].w);
+ s->doexpansion = (s->expandedresolution && !(s->dev->info.does_expansion));
+
+ if (s->res_type == MS_RES_1PER) {
+ s->resolution = (SANE_Int)(SANE_UNFIX(s->val[OPT_RESOLUTION].w));
+ s->resolution_code =
+ 0xFF & ((s->resolution * 100) /
+ s->dev->info.base_resolution /
+ (s->expandedresolution ? 2 : 1));
+ DBG(23, "sane_get_parameters: res_code = %d (%2x)\n",
+ s->resolution_code, s->resolution_code);
+ } else {
+ DBG(23, "sane_get_parameters: 5 percent!!!\n");
+ /* XXXXXXXXXXXXX */
+ }
+
+ s->calib_once = s->val[OPT_CALIB_ONCE].w;
+
+ s->reversecolors = s->val[OPT_NEGATIVE].w;
+ s->prescan = s->val[OPT_PREVIEW].w;
+ s->exposure = (s->val[OPT_EXPOSURE].w / 3) + 7;
+ s->contrast = (s->val[OPT_CONTRAST].w / 7) + 7;
+ s->velocity = s->val[OPT_SPEED].w;
+ s->shadow = s->val[OPT_SHADOW].w;
+ s->highlight = s->val[OPT_HIGHLIGHT].w;
+ s->midtone = s->val[OPT_MIDTONE].w;
+ if (SANE_OPTION_IS_ACTIVE(s->sod[OPT_BRIGHTNESS].cap)) {
+#if 1 /* this is _not_ what the docs specify! */
+ if (s->val[OPT_BRIGHTNESS].w >= 0)
+ s->bright_r = (SANE_Byte) (s->val[OPT_BRIGHTNESS].w);
+ else
+ s->bright_r = (SANE_Byte) (0x80 | (- s->val[OPT_BRIGHTNESS].w));
+#else
+ s->bright_r = (SANE_Byte) (s->val[OPT_BRIGHTNESS].w);
+#endif
+ s->bright_g = s->bright_b = s->bright_r;
+ DBG(23, "bright_r of %d set to 0x%0x\n",
+ s->val[OPT_BRIGHTNESS].w, s->bright_r);
+ } else {
+ s->bright_r = s->bright_g = s->bright_b = 0;
+ }
+ /* figure out halftone pattern selection... */
+ if (s->mode == MS_MODE_HALFTONE) {
+ int i = 0;
+ while ((halftone_mode_list[i] != NULL) &&
+ (strcmp(halftone_mode_list[i], s->val[OPT_HALFTONE_PATTERN].s)))
+ i++;
+ s->pattern = ((i < s->dev->info.pattern_count) ? i : 0);
+ } else
+ s->pattern = 0;
+
+
+
+ {
+ /* need to 'round' things properly! XXXXXXXX */
+ SANE_Int widthpix;
+ double dots_per_mm = s->resolution / MM_PER_INCH;
+ double units_per_mm =
+ (s->unit_type == MS_UNIT_18INCH) ?
+ (8.0 / MM_PER_INCH) : /* 1/8 inches */
+ (s->dev->info.base_resolution / MM_PER_INCH); /* pixels */
+
+ DBG(23, "sane_get_parameters: dots_per_mm: %f\n", dots_per_mm);
+ DBG(23, "sane_get_parameters: units_per_mm: %f\n", units_per_mm);
+
+ /* calculate frame coordinates...
+ * scanner coords are in 'units' -- pixels or 1/8"
+ * option coords are MM
+ */
+ s->x1 = (SANE_Int)(SANE_UNFIX(s->val[OPT_TL_X].w) * units_per_mm + 0.5);
+ s->y1 = (SANE_Int)(SANE_UNFIX(s->val[OPT_TL_Y].w) * units_per_mm + 0.5);
+ s->x2 = (SANE_Int)(SANE_UNFIX(s->val[OPT_BR_X].w) * units_per_mm + 0.5);
+ s->y2 = (SANE_Int)(SANE_UNFIX(s->val[OPT_BR_Y].w) * units_per_mm + 0.5);
+ /* bug out if length or width is <= zero... */
+ if ((s->x1 >= s->x2) || (s->y1 >= s->y2))
+ return SANE_STATUS_INVAL;
+
+ /* these are just an estimate... (but *should* be completely accurate)
+ * real values come from scanner after sane_start.
+ */
+ if (s->unit_type == MS_UNIT_18INCH) {
+ /* who *knows* what happens */
+ widthpix =
+ (SANE_Int)((double)(s->x2 - s->x1 + 1) / 8.0 *
+ (double)s->resolution);
+ s->params.lines =
+ (SANE_Int)((double)(s->y2 - s->y1 + 1) / 8.0 *
+ (double)s->resolution);
+ } else {
+ /* calculate pixels per scanline returned by scanner... */
+ /* scanner (E6 at least) always seems to return
+ an -even- number of -bytes- */
+ if (s->resolution <= s->dev->info.base_resolution)
+ widthpix =
+ (SANE_Int)((double)(s->x2 - s->x1 + 1) *
+ (double)(s->resolution) /
+ (double)(s->dev->info.base_resolution));
+ else
+ widthpix = (s->x2 - s->x1 + 1);
+ if ((s->mode == MS_MODE_LINEART) ||
+ (s->mode == MS_MODE_HALFTONE)) {
+ DBG(23, "WIDTHPIX: before: %d", widthpix);
+ widthpix = ((widthpix / 8) & ~0x1) * 8;
+ DBG(23, "after: %d", widthpix);
+ } else {
+ widthpix = widthpix & ~0x1;
+ }
+ DBG(23, "WIDTHPIX: before exp: %d\n", widthpix);
+ /* ok, now fix up expanded-mode conversions */
+ if (s->resolution > s->dev->info.base_resolution)
+ widthpix = (SANE_Int) ((double)widthpix *
+ (double)s->resolution /
+ (double)s->dev->info.base_resolution);
+ s->params.pixels_per_line = widthpix;
+ s->params.lines =
+ (SANE_Int)((double)(s->y2 - s->y1 + 1) *
+ (double)(s->resolution) /
+ (double)(s->dev->info.base_resolution));
+ }
+ }
+
+ switch (s->mode) {
+ case MS_MODE_LINEART:
+ case MS_MODE_HALFTONE:
+ s->multibit = SANE_FALSE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->filter = MS_FILT_CLEAR;
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ break;
+ case MS_MODE_GRAY:
+ s->multibit = SANE_TRUE;
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = s->bits_per_color;
+ s->filter = MS_FILT_CLEAR;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ break;
+ case MS_MODE_COLOR:
+ s->multibit = SANE_TRUE;
+ if (s->onepasscolor) { /* a single-pass color scan */
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = s->bits_per_color;
+ s->filter = MS_FILT_CLEAR;
+ s->params.bytes_per_line = s->params.pixels_per_line * 3;
+ } else { /* a three-pass color scan */
+ s->params.depth = s->bits_per_color;
+ /* this will be correctly set in sane_start */
+ s->params.format = SANE_FRAME_RED;
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ }
+ break;
+ }
+
+ DBG(23, "sane_get_parameters: lines: %d ppl: %d bpl: %d\n",
+ s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line);
+
+ /* also fixed in sane_start for multi-pass scans */
+ s->params.last_frame = SANE_TRUE; /* ?? XXXXXXXX */
+ }
+
+ if (params)
+ *params = s->params;
+
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_start */
+/********************************************************************/
+static SANE_Status
+sane_start_guts (SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Int busy, linewidth;
+
+ DBG(10, "sane_start...\n");
+
+ if (s->sfd != -1) {
+ DBG(23, "sane_start: sfd already set!\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if ((status = sane_get_parameters(s, 0)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ set_pass_parameters(s);
+
+ s->scanning = SANE_TRUE;
+ s->cancel = SANE_FALSE;
+
+ status = sanei_scsi_open(s->dev->sane.name,
+ &(s->sfd),
+ sense_handler,
+ &(s->sense_flags));
+ if (status != SANE_STATUS_GOOD) {
+ DBG(10, "sane_start: open of %s failed: %s\n",
+ s->dev->sane.name, sane_strstatus (status));
+ s->sfd = -1;
+ return end_scan(s, status);
+ }
+
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+
+ if ((status = finagle_precal(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+
+ if ((status = scanning_frame(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ if (s->dev->info.source_options &
+ (MI_SRC_FEED_BT | MI_SRC_HAS_TRANS |
+ MI_SRC_FEED_SUPP | MI_SRC_HAS_FEED)) { /* ZZZZZZZZZZZ */
+ if ((status = accessory(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ /* if SWslct ???? XXXXXXXXXXXXXXX */
+ }
+ if ((status = download_gamma(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ if ((status = mode_select(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ if (s->dev->info.does_mode1) {
+ if ((status = mode_select_1(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ }
+ if ((s->do_clever_precal) || (s->do_real_calib)) {
+ if ((status = save_mode_sense(s)) != SANE_STATUS_GOOD)
+ return end_scan(s, status);
+ }
+ if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ s->scan_started = SANE_TRUE;
+ if ((status = start_scan(s)) != SANE_STATUS_GOOD) return end_scan(s, status);
+ if ((status = get_scan_status(s, &busy,
+ &linewidth, &(s->unscanned_lines))) !=
+ SANE_STATUS_GOOD) {
+ DBG(10, "sane_start: get_scan_status fails\n");
+ return end_scan(s, status);
+ }
+ /* check for a bizarre linecount */
+ if ((s->unscanned_lines < 0) ||
+ (s->unscanned_lines >
+ (s->params.lines * 2 * (s->expandedresolution ? 2 : 1)))) {
+ DBG(10, "sane_start: get_scan_status returns weird line count %d\n",
+ s->unscanned_lines);
+ return end_scan(s, SANE_STATUS_DEVICE_BUSY);
+ }
+
+
+ /* figure out image format parameters */
+ switch (s->mode) {
+ case MS_MODE_LINEART:
+ case MS_MODE_HALFTONE:
+ s->pixel_bpl = linewidth;
+ s->header_bpl = 0;
+ s->ppl = linewidth * 8;
+ s->planes = 1;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MS_MODE_GRAY:
+ if (s->bits_per_color < 8) {
+ s->pixel_bpl = linewidth;
+ s->ppl = linewidth * (8 / s->bits_per_color);
+ } else {
+ s->pixel_bpl = linewidth * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ }
+ s->header_bpl = 0;
+ s->planes = 1;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MS_MODE_COLOR:
+ switch (s->color_seq) {
+ case MI_COLSEQ_PLANE:
+ s->pixel_bpl = linewidth * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 1;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MI_COLSEQ_NONRGB:
+ s->pixel_bpl = (linewidth - 2) * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth - 2;
+ s->header_bpl = 2 * 3;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_GOOFY_RGB;
+ break;
+ case MI_COLSEQ_PIXEL:
+ s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_FLAT;
+ break;
+ case MI_COLSEQ_2PIXEL:
+ s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_SEQ_2R2G2B;
+ break;
+ case MI_COLSEQ_RGB:
+ s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8);
+ s->ppl = linewidth;
+ s->header_bpl = 0;
+ s->planes = 3;
+ s->line_format = MS_LNFMT_SEQ_RGB;
+ break;
+ default:
+ DBG(10, "sane_start: Unknown color_sequence: %d\n",
+ s->dev->info.color_sequence);
+ return end_scan(s, SANE_STATUS_INVAL);
+ }
+ break;
+ default:
+ DBG(10, "sane_start: Unknown scan mode: %d\n", s->mode);
+ return end_scan(s, SANE_STATUS_INVAL);
+ }
+
+ if ((s->doexpansion) &&
+ (s->resolution > s->dev->info.base_resolution)) {
+ s->dest_ppl = (int) ((double)s->ppl *
+ (double)s->resolution /
+ (double)s->dev->info.base_resolution);
+ /*+ 0.5 XXXXXX */
+ s->exp_aspect = (double)s->ppl / (double)s->dest_ppl;
+ s->dest_pixel_bpl = (int) ceil((double)s->pixel_bpl / s->exp_aspect);
+ /*s->exp_aspect =
+ (double) s->dev->info.base_resolution / (double) s->resolution;*/
+ /* s->dest_pixel_bpl = s->pixel_bpl / s->exp_aspect;
+ s->dest_ppl = s->ppl / s->exp_aspect;*/
+ /*s->dest_ppl = s->ppl / s->exp_aspect;
+ s->dest_pixel_bpl = (int) ceil((double)s->dest_ppl *
+ (double)s->pixel_bpl /
+ (double)s->ppl);*/
+ } else {
+ s->exp_aspect = 1.0;
+ s->dest_pixel_bpl = s->pixel_bpl;
+ s->dest_ppl = s->ppl;
+ }
+
+ s->params.lines = s->unscanned_lines;
+ s->params.pixels_per_line = s->dest_ppl;
+ s->params.bytes_per_line = s->dest_pixel_bpl;
+
+ /* calculate maximum line capacity of SCSI buffer */
+ s->max_scsi_lines = SCSI_BUFF_SIZE / (s->pixel_bpl + s->header_bpl);
+ if (s->max_scsi_lines < 1) {
+ DBG(10, "sane_start: SCSI buffer smaller that one scan line!\n");
+ return end_scan(s, SANE_STATUS_NO_MEM);
+ }
+
+ s->scsi_buffer = (uint8_t *) malloc(SCSI_BUFF_SIZE * sizeof(uint8_t));
+ if (s->scsi_buffer == NULL) return SANE_STATUS_NO_MEM;
+
+ /* what's a good initial size for this? */
+ s->rb = ring_alloc(s->max_scsi_lines * s->dest_pixel_bpl,
+ s->dest_pixel_bpl, s->dest_ppl);
+
+ s->undelivered_bytes = s->unscanned_lines * s->dest_pixel_bpl;
+
+ DBG(23, "Scan Param:\n");
+ DBG(23, "pix bpl: %d hdr bpl: %d ppl: %d\n",
+ s->pixel_bpl, s->header_bpl, s->ppl);
+ DBG(23, "undel bytes: %d unscan lines: %d planes: %d\n",
+ s->undelivered_bytes, s->unscanned_lines, s->planes);
+ DBG(23, "dest bpl: %d dest ppl: %d aspect: %f\n",
+ s->dest_pixel_bpl, s->dest_ppl, s->exp_aspect);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+
+ s->woe = SANE_TRUE;
+ status = sane_start_guts(handle);
+ s->woe = SANE_FALSE;
+ return status;
+}
+
+
+
+/********************************************************************/
+/* sane_read */
+/********************************************************************/
+static SANE_Status
+sane_read_guts (SANE_Handle handle, SANE_Byte *dest_buffer,
+ SANE_Int dest_length, SANE_Int *ret_length)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+ int nlines;
+ ring_buffer *rb = s->rb;
+
+ DBG(10, "sane_read...\n");
+
+ *ret_length = 0; /* default: no data */
+ /* we have been cancelled... */
+ if (s->cancel) return end_scan(s, SANE_STATUS_CANCELLED);
+ /* we're not really scanning!... */
+ if (!(s->scanning)) return SANE_STATUS_INVAL;
+ /* we are done scanning... */
+ if (s->undelivered_bytes <= 0) return end_scan(s, SANE_STATUS_EOF);
+
+ /* get more bytes if our ring is empty... */
+ while (rb->complete_count == 0) {
+ if ((status = read_from_scanner(s, &nlines)) != SANE_STATUS_GOOD) {
+ DBG(18, "sane_read: read_from_scanner failed.\n");
+ return end_scan(s, status);
+ }
+ if ((status = pack_into_ring(s, nlines)) != SANE_STATUS_GOOD) {
+ DBG(18, "sane_read: pack_into_ring failed.\n");
+ return end_scan(s, status);
+ }
+ }
+ /* return some data to caller */
+ *ret_length = pack_into_dest(dest_buffer, dest_length, rb);
+ s->undelivered_bytes -= *ret_length;
+
+ if (s->cancel) return end_scan(s, SANE_STATUS_CANCELLED);
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte *dest_buffer,
+ SANE_Int dest_length, SANE_Int *ret_length)
+{
+ Microtek_Scanner *s = handle;
+ SANE_Status status;
+
+ s->woe = SANE_TRUE;
+ status = sane_read_guts(handle, dest_buffer, dest_length, ret_length);
+ s->woe = SANE_FALSE;
+ return status;
+}
+
+
+
+/********************************************************************/
+/* sane_exit */
+/********************************************************************/
+void
+sane_exit (void)
+{
+ Microtek_Device *next;
+
+ DBG(10, "sane_exit...\n");
+ /* close all leftover Scanners */
+ /*(beware of how sane_close interacts with linked list) */
+ while (first_handle != NULL)
+ sane_close(first_handle);
+ /* free up device list */
+ while (first_dev != NULL) {
+ next = first_dev->next;
+ free((void *) first_dev->sane.name);
+ free((void *) first_dev->sane.model);
+ free(first_dev);
+ first_dev = next;
+ }
+ /* the devlist allocated by sane_get_devices */
+ free(devlist);
+ DBG(10, "sane_exit: MICROTEK says goodbye.\n");
+}
+
+
+
+/********************************************************************/
+/* sane_cancel */
+/********************************************************************/
+void
+sane_cancel (SANE_Handle handle)
+{
+ Microtek_Scanner *ms = handle;
+ DBG(10, "sane_cancel...\n");
+ ms->cancel = SANE_TRUE;
+ if (!(ms->woe)) end_scan(ms, SANE_STATUS_CANCELLED);
+}
+
+
+
+/********************************************************************/
+/* sane_set_io_mode */
+/********************************************************************/
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG(10, "sane_set_io_mode...\n");
+ handle = handle;
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+
+
+/********************************************************************/
+/* sane_get_select_fd */
+/********************************************************************/
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG(10, "sane_get_select_fd...\n");
+ handle = handle, fd = fd;
+ return SANE_STATUS_UNSUPPORTED;
+}