kopia lustrzana https://gitlab.com/sane-project/backends
4184 wiersze
138 KiB
C
4184 wiersze
138 KiB
C
/***************************************************************************
|
|
* 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, i;
|
|
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++) {
|
|
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;
|
|
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) {
|
|
unsigned 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) {
|
|
unsigned 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, 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, SANE_Int dest_length, ring_buffer *rb)
|
|
{
|
|
SANE_Int 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;
|
|
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;
|
|
}
|