diff --git a/ChangeLog b/ChangeLog index 9bd28068d..ba4dfa4e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,10 @@ backend options. Updated manpage and .desc file concerning scanner status. Added untested flag for Mustek ScanEpress A3 USB and Lexmark X73. + * backend/mustek.c doc/sane-mustek.man doc/descriptions/mustek.desc + doc/mustek/mustek.CHANGES: Upload linear gamma table for Pro models + if custom gamma is off instead of uploading the composed gamma table. + That avoids applying gamm twice. Minor man page update. 2002-11-05 Stéphane Voltz diff --git a/backend/mustek.c b/backend/mustek.c index fc61a5cff..e210417e9 100644 --- a/backend/mustek.c +++ b/backend/mustek.c @@ -1,6 +1,7 @@ /* sane - Scanner Access Now Easy. Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Czechanowski, - 1998 Andreas Bolsch for extension to ScanExpress models version 0.6 + 1998 Andreas Bolsch for extension to ScanExpress models version 0.6, + 2000-2002 Henning Meier-Geinitz. This file is part of the SANE package. This program is free software; you can redistribute it and/or @@ -39,9 +40,16 @@ 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 Mustek flatbed scanners. */ + This file implements a SANE backend for Mustek and some Trust flatbed + scanners with SCSI or proprietary interface. */ -#include + +/**************************************************************************/ +/* Mustek backend version */ +#define BUILD 129 +/**************************************************************************/ + +#include "../include/sane/config.h" #include #include @@ -57,260 +65,266 @@ #include #include -#include -#include -#include -#include -#include -#include - -#define BACKEND_NAME mustek -#include - -#ifndef PATH_MAX -# define PATH_MAX 1024 +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_scsi.h" +#include "../include/sane/sanei_ab306.h" +#ifdef HAVE_OS2_H +#include "../include/sane/sanei_thread.h" #endif -#include -#define MUSTEK_CONFIG_FILE "mustek.conf" +#include "mustek.h" -#define MM_PER_INCH 25.4 -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define BACKEND_NAME mustek +#include "../include/sane/sanei_backend.h" +#include "../include/sane/sanei_config.h" -/* Maximum time to wait for scanner to become ready. */ -#define MAX_WAITING_TIME 60 -/* Number of queued read requests/buffers */ -#define REQUESTS 1 -/* Max. allocated buffers for read requests */ -#define BUFFERS 64 -/* The 600 II N and ScanExpress require this for line-distance correction */ -#define MAX_LINE_DIST 32 +#ifndef SANE_I18N +#define SANE_I18N(text) text +#endif + +/* Debug level from sanei_init_debug */ +static SANE_Int debug_level; /* Maximum # of inches to scan in one swoop. 0 means "unlimited." This is here to be nice on the SCSI bus---Mustek scanners don't disconnect while scanning is in progress, which confuses some drivers that expect no reasonable SCSI request would take more than 10 seconds. That's not really true for Mustek scanners operating - in certain modes, hence this limit. */ + in certain modes, hence this limit. Usually you don't need to set it. */ static double strip_height; -static int num_devices; +/* Should we wait for the scan slider to return after each scan? */ +static SANE_Bool force_wait; + +/* Should we disable double buffering when reading data from the scanner? */ +static SANE_Bool disable_double_buffering; + +static SANE_Int num_devices; static Mustek_Device *first_dev; static Mustek_Scanner *first_handle; +static const SANE_Device **devlist = 0; -static Mustek_Device **new_dev; /* array of newly attached devices */ -static int new_dev_len; /* length of new_dev array */ -static int new_dev_alloced; /* number of entries alloced for new_dev */ +/* Array of newly attached devices */ +static Mustek_Device **new_dev; + +/* Length of new_dev array */ +static SANE_Int new_dev_len; + +/* Number of entries alloced for new_dev */ +static SANE_Int new_dev_alloced; /* Used for line-distance correction: */ -static const int color_seq[] = - { - 1, 2, 0 /* green, blue, red */ - }; - -static const SANE_String_Const mode_list[] = - { - "Lineart", "Halftone", "Gray", - "Color Lineart", "Color Halftone", "Color", - 0 - }; +static const SANE_Int color_seq[] = { + 1, 2, 0 /* green, blue, red */ +}; -static const SANE_String_Const pp_mode_list[] = - { - "Lineart", "Halftone", "Gray", "Color", - 0 - }; +/* Which modes are supported? */ +static SANE_String_Const mode_list_paragon[] = { + SANE_I18N ("Lineart"), SANE_I18N ("Halftone"), SANE_I18N ("Gray"), + SANE_I18N ("Color"), + 0 +}; +static SANE_String_Const mode_list_se[] = { + SANE_I18N ("Lineart"), SANE_I18N ("Gray"), SANE_I18N ("Color"), + 0 +}; -static const SANE_String_Const se_mode_list[] = - { - "Lineart", "Gray", "Color", - 0 - }; +static SANE_String_Const bit_depth_list_pro[] = { + "8", "12", + 0 +}; -static const SANE_String_Const speed_list[] = - { - "Slowest", "Slower", "Normal", "Faster", "Fastest", - 0 - }; +/* Some scanners support setting speed manually */ +static SANE_String_Const speed_list[] = { + SANE_I18N ("Slowest"), SANE_I18N ("Slower"), SANE_I18N ("Normal"), + SANE_I18N ("Faster"), SANE_I18N ("Fastest"), + 0 +}; -enum Scan_Source - { - FLATBED, ADF, TA - }; +/* Which scan-sources are supported? */ +static const SANE_String_Const source_list[] = { + SANE_I18N ("Flatbed"), + 0 +}; +static SANE_String_Const adf_source_list[] = { + SANE_I18N ("Flatbed"), SANE_I18N ("Automatic Document Feeder"), + 0 +}; +static SANE_String_Const ta_source_list[] = { + SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"), + 0 +}; -static const SANE_String_Const source_list[] = - { - "Flatbed", "Automatic Document Feeder", "Transparency Adapter", - 0 - }; +/* Range used for gamma and halftone pattern */ +static const SANE_Range u8_range = { + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; -static const SANE_Int grain_list[] = - { - 6, /* # of elements */ - 2, 3, 4, 5, 6, 8 - }; +/* Which kind of halftone patterns are available? */ +static SANE_String_Const halftone_list[] = { + SANE_I18N ("8x8 coarse"), SANE_I18N ("8x8 normal"), SANE_I18N ("8x8 fine"), + SANE_I18N ("8x8 very fine"), SANE_I18N ("6x6 normal"), + SANE_I18N ("5x5 coarse"), SANE_I18N ("5x5 fine"), SANE_I18N ("4x4 coarse"), + SANE_I18N ("4x4 normal"), SANE_I18N ("4x4 fine"), SANE_I18N ("3x3 normal"), + SANE_I18N ("2x2 normal"), SANE_I18N ("8x8 custom"), + SANE_I18N ("6x6 custom"), + SANE_I18N ("5x5 custom"), SANE_I18N ("4x4 custom"), + SANE_I18N ("3x3 custom"), + SANE_I18N ("2x2 custom"), + 0 +}; -static const SANE_Range u8_range = - { - 0, /* minimum */ - 255, /* maximum */ - 0 /* quantization */ - }; +/* Range used for brightness and contrast */ +static const SANE_Range percentage_range = { + -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */ + 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */ + 1 << SANE_FIXED_SCALE_SHIFT /* quantization */ +}; -static const SANE_Int pattern_dimension_list[] = - { - 8, /* # of elements */ - 0, 2, 3, 4, 5, 6, 7, 8 - }; - -static const SANE_Range percentage_range = - { - -100 << SANE_FIXED_SCALE_SHIFT, /* minimum */ - 100 << SANE_FIXED_SCALE_SHIFT, /* maximum */ - 1 << SANE_FIXED_SCALE_SHIFT /* quantization */ - }; - -/* for three-pass (ax) scanners: */ - -static const SANE_Range ax_brightness_range = - { - -36 << SANE_FIXED_SCALE_SHIFT, /* minimum */ - 36 << SANE_FIXED_SCALE_SHIFT, /* maximum */ - 3 << SANE_FIXED_SCALE_SHIFT /* quantization */ - }; - -static const SANE_Range ax_contrast_range = - { - -84 << SANE_FIXED_SCALE_SHIFT, /* minimum */ - 84 << SANE_FIXED_SCALE_SHIFT, /* maximum */ - 7 << SANE_FIXED_SCALE_SHIFT /* quantization */ - }; - -/* Color band codes: */ -#define MUSTEK_CODE_GRAY 0 -#define MUSTEK_CODE_RED 1 -#define MUSTEK_CODE_GREEN 2 -#define MUSTEK_CODE_BLUE 3 - -/* SCSI commands that the Mustek scanners understand (or not): */ -#define MUSTEK_SCSI_TEST_UNIT_READY 0x00 -#define MUSTEK_SCSI_AREA_AND_WINDOWS 0x04 -#define MUSTEK_SCSI_READ_SCANNED_DATA 0x08 -#define MUSTEK_SCSI_GET_IMAGE_STATUS 0x0f -#define MUSTEK_SCSI_ADF_AND_BACKTRACK 0x10 -#define MUSTEK_SCSI_CCD_DISTANCE 0x11 -#define MUSTEK_SCSI_INQUIRY 0x12 -#define MUSTEK_SCSI_MODE_SELECT 0x15 -#define MUSTEK_SCSI_START_STOP 0x1b -#define MUSTEK_SCSI_LOOKUP_TABLE 0x55 -#define MUSTEK_SCSI_SET_WINDOW 0x24 -#define MUSTEK_SCSI_GET_WINDOW 0x25 -#define MUSTEK_SCSI_READ_DATA 0x28 -#define MUSTEK_SCSI_SEND_DATA 0x2a - -#define INQ_LEN 0x60 -static const u_int8_t inquiry[] = -{ +/* SCSI command buffers used by the backend */ +static const SANE_Byte scsi_inquiry[] = { MUSTEK_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; - -static const u_int8_t test_unit_ready[] = -{ +static const SANE_Byte scsi_test_unit_ready[] = { MUSTEK_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static const u_int8_t stop[] = -{ +static const SANE_Byte scsi_area_and_windows[] = { + MUSTEK_SCSI_AREA_AND_WINDOWS, 0x00, 0x00, 0x00, 0x09, 0x00 +}; +static const SANE_Byte scsi_request_sense[] = { + MUSTEK_SCSI_REQUEST_SENSE, 0x00, 0x00, 0x00, 0x04, 0x00 +}; +static const SANE_Byte scsi_start_stop[] = { MUSTEK_SCSI_START_STOP, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static const u_int8_t distance[] = -{ +static const SANE_Byte scsi_ccd_distance[] = { MUSTEK_SCSI_CCD_DISTANCE, 0x00, 0x00, 0x00, 0x05, 0x00 }; - -static const u_int8_t get_status[] = -{ +static const SANE_Byte scsi_get_image_status[] = { MUSTEK_SCSI_GET_IMAGE_STATUS, 0x00, 0x00, 0x00, 0x06, 0x00 }; - -static const u_int8_t set_window[] = -{ +static const SANE_Byte scsi_set_window[] = { MUSTEK_SCSI_SET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00 }; - -static const u_int8_t get_window[] = -{ +static const SANE_Byte scsi_get_window[] = { MUSTEK_SCSI_GET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00 }; - -static const u_int8_t read_data[] = -{ +static const SANE_Byte scsi_read_data[] = { MUSTEK_SCSI_READ_DATA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static const u_int8_t send_data[] = -{ +static const SANE_Byte scsi_send_data[] = { MUSTEK_SCSI_SEND_DATA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const SANE_Byte scsi_lookup_table[] = { + MUSTEK_SCSI_LOOKUP_TABLE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; -/* For the next macros suffix 'L' means little endian, 'B' big endian */ -#define STORE16L(p,v) \ -do { \ - int value = (v); \ - \ - *(cp)++ = (value >> 0) & 0xff; \ - *(cp)++ = (value >> 8) & 0xff; \ -} while (0) +/* prototypes */ +static SANE_Status area_and_windows (Mustek_Scanner * s); +static SANE_Status inquiry (Mustek_Scanner * s); -#define STORE16B(p,v) \ -do { \ - int value = (v); \ - \ - *(cp)++ = (value >> 8) & 0xff; \ - *(cp)++ = (value >> 0) & 0xff; \ -} while (0) +/* Test if this machine is little endian (from coolscan.c) */ +static SANE_Bool +little_endian (void) +{ + SANE_Int testvalue = 255; + u_int8_t *firstbyte = (u_int8_t *) & testvalue; -#define STORE32B(p,v) \ -do { \ - long int value = (v); \ - \ - *(cp)++ = (value >> 24) & 0xff; \ - *(cp)++ = (value >> 16) & 0xff; \ - *(cp)++ = (value >> 8) & 0xff; \ - *(cp)++ = (value >> 0) & 0xff; \ -} while (0) + if (*firstbyte == 255) + return SANE_TRUE; + return SANE_FALSE; +} +/* Used for Pro series. First value seems to be always 85, second one varies. + First bit of second value clear == device ready (?) */ static SANE_Status -scsi_wait_ready (int fd) +scsi_sense_wait_ready (Mustek_Scanner * s) +{ + struct timeval now, start; + SANE_Status status; + size_t len; + SANE_Byte sense_buffer[4]; + SANE_Byte bytetxt[300], dbgtxt[300], *pp; + + gettimeofday (&start, 0); + + while (1) + { + len = sizeof (sense_buffer); + + DBG (5, "scsi_sense_wait_ready: command size = %ld, sense size = %ld\n", + (long int) sizeof (scsi_request_sense), (long int) len); + status = sanei_scsi_cmd (s->fd, scsi_request_sense, + sizeof (scsi_request_sense), sense_buffer, + &len); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "scsi_sense_wait_ready: failed: %s\n", + sane_strstatus (status)); + return status; + } + + dbgtxt[0] = '\0'; + for (pp = sense_buffer; pp < (sense_buffer + 4); pp++) + { + sprintf ((SANE_String) bytetxt, " %02x", *pp); + strcat ((SANE_String) dbgtxt, (SANE_String) bytetxt); + } + DBG (5, "scsi_sense_wait_ready: sensebuffer: %s\n", dbgtxt); + + if (!(sense_buffer[1] & 0x01)) + { + DBG (4, "scsi_sense_wait_ready: ok\n"); + return SANE_STATUS_GOOD; + } + else + { + gettimeofday (&now, 0); + if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) + { + DBG (1, "scsi_sense_wait_ready: timed out after %lu seconds\n", + (u_long) (now.tv_sec - start.tv_sec)); + return SANE_STATUS_INVAL; + } + usleep (100000); /* retry after 100ms */ + } + } + return SANE_STATUS_INVAL; +} + +/* Used for 3pass series */ +static SANE_Status +scsi_area_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); + DBG (5, "scsi_area_wait_ready\n"); while (1) { - DBG(3, "scsi_wait_ready: sending TEST_UNIT_READY\n"); - - status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), - 0, 0); + status = area_and_windows (s); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ - DBG(1, "scsi_wait_ready: test unit ready failed (%s)\n", - sane_strstatus (status)); + DBG (3, "scsi_area_wait_ready: failed (%s)\n", + sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { - DBG(1, "scsi_wait_ready: timed out after %lu seconds\n", - (u_long) (now.tv_sec - start.tv_sec)); + DBG (1, "scsi_area_wait_ready: timed out after %lu seconds\n", + (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ @@ -324,7 +338,7 @@ scsi_wait_ready (int fd) } static SANE_Status -pp_wait_ready (int fd) +scsi_unit_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; @@ -333,62 +347,234 @@ pp_wait_ready (int fd) while (1) { - status = sanei_ab306_test_ready (fd); + DBG (5, "scsi_unit_wait_ready: sending TEST_UNIT_READY\n"); + status = sanei_scsi_cmd (s->fd, scsi_test_unit_ready, + sizeof (scsi_test_unit_ready), 0, 0); + DBG (5, "scsi_unit_wait_ready: TEST_UNIT_READY finished\n"); + switch (status) + { + default: + /* Ignore errors while waiting for scanner to become ready. + Some SCSI drivers return EIO while the scanner is + returning to the home position. */ + DBG (3, "scsi_unit_wait_ready: test unit ready failed (%s)\n", + sane_strstatus (status)); + /* fall through */ + case SANE_STATUS_DEVICE_BUSY: + gettimeofday (&now, 0); + if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) + { + DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n", + (u_long) (now.tv_sec - start.tv_sec)); + return SANE_STATUS_INVAL; + } + usleep (100000); /* retry after 100ms */ + break; + + case SANE_STATUS_GOOD: + return status; + } + } + return SANE_STATUS_INVAL; +} + +static SANE_Status +scsi_inquiry_wait_ready (Mustek_Scanner * s) +{ + struct timeval now, start; + SANE_Status status; + + gettimeofday (&start, 0); + + while (1) + { + DBG (5, "scsi_inquiry_wait_ready: sending INQUIRY\n"); + status = inquiry (s); + DBG (5, "scsi_inquiry_wait_ready: INQUIRY finished\n"); + switch (status) + { + default: + /* Ignore errors while waiting for scanner to become ready. + Some SCSI drivers return EIO while the scanner is + returning to the home position. */ + DBG (3, "scsi_unit_wait_ready: inquiry failed (%s)\n", + sane_strstatus (status)); + /* fall through */ + case SANE_STATUS_DEVICE_BUSY: + gettimeofday (&now, 0); + if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) + { + DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n", + (u_long) (now.tv_sec - start.tv_sec)); + return SANE_STATUS_INVAL; + } + usleep (500000); /* retry after 500ms */ + break; + + case SANE_STATUS_GOOD: + return status; + } + } + return SANE_STATUS_INVAL; +} + +static SANE_Status +n_wait_ready (Mustek_Scanner * s) +{ + struct timeval now, start; + SANE_Status status; + + gettimeofday (&start, 0); + + DBG (5, "n_wait_ready\n"); + while (1) + { + status = sanei_ab306_test_ready (s->fd); if (status == SANE_STATUS_GOOD) return SANE_STATUS_GOOD; gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { - DBG(1, "pp_wait_ready: timed out after %lu seconds\n", - (u_long) (now.tv_sec - start.tv_sec)); + DBG (1, "n_wait_ready: timed out after %lu seconds\n", + (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } - usleep (100000); /* retry after 100ms */ + usleep (100000); /* retry after 100ms */ } } static SANE_Status -dev_wait_ready (Mustek_Scanner *s) +dev_wait_ready (Mustek_Scanner * s) { - if (s->hw->flags & MUSTEK_FLAG_PP) - return pp_wait_ready (s->fd); + if (s->hw->flags & MUSTEK_FLAG_N) + return n_wait_ready (s); + else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + { + SANE_Status status; + + /* some 3-pass scanners seem to need the inquiry wait, too */ + status = scsi_area_wait_ready (s); + if (status != SANE_STATUS_GOOD) + return status; + return scsi_inquiry_wait_ready (s); + } + else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1) + || (s->hw->flags & MUSTEK_FLAG_PARAGON_2)) + return scsi_inquiry_wait_ready (s); + else if (s->hw->flags & MUSTEK_FLAG_PRO) + return scsi_sense_wait_ready (s); else - return scsi_wait_ready (s->fd); + return scsi_unit_wait_ready (s); } static SANE_Status -dev_open (const char * devname, Mustek_Scanner * s, +dev_open (SANE_String_Const devname, Mustek_Scanner * s, SANEI_SCSI_Sense_Handler handler) { SANE_Status status; + DBG (5, "dev_open %s\n", devname); + +#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED + s->hw->buffer_size = s->hw->max_buffer_size; + status = sanei_scsi_open_extended (devname, &s->fd, handler, 0, + &s->hw->buffer_size); +#else + s->hw->buffer_size = MIN (sanei_scsi_max_request_size, + s->hw->max_buffer_size); status = sanei_scsi_open (devname, &s->fd, handler, 0); - if (status != SANE_STATUS_GOOD) +#endif + + if (status == SANE_STATUS_GOOD) { - DBG(2, "dev_open: %s: can't open %s as a SCSI device\n", - sane_strstatus (status), devname); + DBG (3, "dev_open: %s is a SCSI device\n", devname); + DBG (4, "dev_open: wanted %d kbytes, got %d kbytes buffer\n", + s->hw->max_buffer_size / 1024, s->hw->buffer_size / 1024); + if (s->hw->buffer_size < 4096) + { + DBG (1, "dev_open: sanei_scsi_open buffer too small\n"); + sanei_scsi_close (s->fd); + return SANE_STATUS_NO_MEM; + } + } + else + { + DBG (3, "dev_open: %s: can't open %s as a SCSI device\n", + sane_strstatus (status), devname); status = sanei_ab306_open (devname, &s->fd); - if (status != SANE_STATUS_GOOD) + if (status == SANE_STATUS_GOOD) { - DBG(1, "dev_open: %s: can't open %s as a parallel-port device\n", - sane_strstatus (status), devname); + s->hw->flags |= MUSTEK_FLAG_N; + DBG (3, "dev_open: %s is an AB306N device\n", devname); + } + else + { + DBG (3, "dev_open: %s: can't open %s as an AB306N device\n", + sane_strstatus (status), devname); + DBG (1, "dev_open: can't open %s\n", devname); return SANE_STATUS_INVAL; } - s->hw->flags |= MUSTEK_FLAG_PP; } return SANE_STATUS_GOOD; } static SANE_Status -dev_cmd (Mustek_Scanner * s, const void * src, size_t src_size, - void * dst, size_t * dst_size) +dev_cmd (Mustek_Scanner * s, const void *src, size_t src_size, + void *dst, size_t * dst_size) { - if (s->hw->flags & MUSTEK_FLAG_PP) - return sanei_ab306_cmd (s->fd, src, src_size, dst, dst_size); + SANE_Status status; + SANE_Byte cmd_byte_list[50]; + SANE_Byte cmd_byte[5]; + const SANE_Byte *pp; + + DBG (5, "dev_cmd: fd=%d, src=%p, src_size=%ld, dst=%p, dst_size=%ld\n", + s->fd, src, (long int) src_size, dst, + (long int) (dst_size ? *dst_size : 0)); + + if (src && (debug_level >= 5)) /* output data sent to SCSI device */ + { + cmd_byte_list[0] = '\0'; + for (pp = (const SANE_Byte *) src; + pp < (((const SANE_Byte *) src) + src_size); pp++) + { + sprintf ((SANE_String) cmd_byte, " %02x", *pp); + strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte); + if (((pp - (const SANE_Byte *) src) % 0x10 == 0x0f) + || (pp >= (((const SANE_Byte *) src) + src_size - 1))) + { + DBG (5, "dev_cmd: sending: %s\n", cmd_byte_list); + cmd_byte_list[0] = '\0'; + } + } + } + if (s->hw->flags & MUSTEK_FLAG_N) + status = sanei_ab306_cmd (s->fd, src, src_size, dst, dst_size); else - return sanei_scsi_cmd (s->fd, src, src_size, dst, dst_size); + status = sanei_scsi_cmd (s->fd, src, src_size, dst, dst_size); + + if (dst && dst_size && (debug_level >= 5)) + /* output data received from SCSI device */ + { + cmd_byte_list[0] = '\0'; + for (pp = (const SANE_Byte *) dst; + pp < (((const SANE_Byte *) dst) + *dst_size); pp++) + { + sprintf ((SANE_String) cmd_byte, " %02x", *pp); + strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte); + if (((pp - (const SANE_Byte *) dst) % 0x10 == 0x0f) + || (pp >= (((const SANE_Byte *) dst) + *dst_size - 1))) + { + DBG (5, "dev_cmd: receiving: %s\n", cmd_byte_list); + cmd_byte_list[0] = '\0'; + } + } + } + + DBG (5, "dev_cmd: finished: dst_size=%ld, status=%s\n", + (long int) (dst_size ? *dst_size : 0), sane_strstatus (status)); + return status; } static SANE_Status @@ -401,90 +587,106 @@ dev_req_wait (void *id) } static SANE_Status -dev_read_start (Mustek_Scanner *s) +dev_block_read_start (Mustek_Scanner * s, SANE_Int lines) { - int lines = s->hw->lines; - - if (s->hw->flags & MUSTEK_FLAG_PP) + DBG (4, "dev_block_read_start: entering block for %d lines\n", lines); + if (s->hw->flags & MUSTEK_FLAG_N) { - if (s->hw->flags & MUSTEK_FLAG_SE) + SANE_Byte readlines[6]; + + memset (readlines, 0, sizeof (readlines)); + readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA; + readlines[2] = (lines >> 16) & 0xff; + readlines[3] = (lines >> 8) & 0xff; + readlines[4] = (lines >> 0) & 0xff; + return sanei_ab306_cmd (s->fd, readlines, sizeof (readlines), 0, 0); + } + else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2) + { + SANE_Byte buffer[6]; + size_t len; + SANE_Int color; + SANE_Status status; + + /* reset line-distance values */ + if (s->mode & MUSTEK_MODE_COLOR) { - u_int8_t readlines[10]; - - if (s->mode & MUSTEK_MODE_COLOR) - lines *= 3; - - memset (readlines, 0, sizeof (readlines)); - readlines[0] = MUSTEK_SCSI_READ_DATA; - readlines[7] = (lines >> 8) & 0xff; - readlines[8] = (lines >> 0) & 0xff; - return sanei_ab306_cmd (s->fd, readlines, sizeof (readlines), 0, 0); + for (color = 0; color < 3; ++color) + { + s->ld.index[color] = -s->ld.dist[color]; + } + s->ld.lmod3 = -1; + s->ld.ld_line = 0; } - else - { - u_int8_t readlines[6]; - memset (readlines, 0, sizeof (readlines)); - readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA; - readlines[2] = (lines >> 16) & 0xff; - readlines[3] = (lines >> 8) & 0xff; - readlines[4] = (lines >> 0) & 0xff; - return sanei_ab306_cmd (s->fd, readlines, sizeof (readlines), 0, 0); - } + /* Get image status (necessary to start new block) */ + len = sizeof (buffer); + status = dev_cmd (s, scsi_get_image_status, + sizeof (scsi_get_image_status), buffer, &len); + if (status != SANE_STATUS_GOOD) + return status; + + /* tell scanner how many lines to scan in one block */ + memset (buffer, 0, sizeof (buffer)); + buffer[0] = MUSTEK_SCSI_READ_SCANNED_DATA; + buffer[2] = (lines >> 16) & 0xff; + buffer[3] = (lines >> 8) & 0xff; + buffer[4] = (lines >> 0) & 0xff; + buffer[5] = 0x04; + return sanei_scsi_cmd (s->fd, buffer, sizeof (buffer), 0, 0); } else return SANE_STATUS_GOOD; } static SANE_Status -dev_read_req_enter (Mustek_Scanner *s, SANE_Byte *buf, int lines, int bpl, - size_t *lenp, void **idp, int bank) +dev_read_req_enter (Mustek_Scanner * s, SANE_Byte * buf, SANE_Int lines, + SANE_Int bpl, size_t * lenp, void **idp, SANE_Int bank, + SANE_Byte * command) { *lenp = lines * bpl; - DBG(1, "reader: about to read %lu bytes\n", (unsigned long) *lenp); - if (s->hw->flags & MUSTEK_FLAG_PP) + if (s->hw->flags & MUSTEK_FLAG_N) { - int planes; + SANE_Int planes; *idp = 0; - planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1; - /* for color lineart/halftone modes, try this : */ - if ((s->mode & MUSTEK_MODE_COLOR) && !(s->mode & MUSTEK_MODE_MULTIBIT)) - lines /= 3; - return sanei_ab306_rdata (s->fd, planes, buf, lines, bpl); } else { if (s->hw->flags & MUSTEK_FLAG_SE) { - u_int8_t readlines[10]; - DBG(1, "buffer_bank: %d\n", bank); if (s->mode & MUSTEK_MODE_COLOR) lines *= 3; - - memset (readlines, 0, sizeof (readlines)); - readlines[0] = MUSTEK_SCSI_READ_DATA; - readlines[6] = bank; /* buffer bank not used ??? */ - readlines[7] = (lines >> 8) & 0xff; - readlines[8] = (lines >> 0) & 0xff; - return sanei_scsi_req_enter (s->fd, readlines, sizeof (readlines), - buf, lenp, idp); - } - else - { - u_int8_t readlines[6]; - memset (readlines, 0, sizeof (readlines)); - readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA; - readlines[2] = (lines >> 16) & 0xff; - readlines[3] = (lines >> 8) & 0xff; - readlines[4] = (lines >> 0) & 0xff; - return sanei_scsi_req_enter (s->fd, readlines, sizeof (readlines), - buf, lenp, idp); + memset (command, 0, 10); + command[0] = MUSTEK_SCSI_READ_DATA; + command[6] = bank; /* buffer bank not used ??? */ + command[7] = (lines >> 8) & 0xff; + command[8] = (lines >> 0) & 0xff; + return sanei_scsi_req_enter (s->fd, command, 10, buf, lenp, idp); + } + else if (s->hw->flags & MUSTEK_FLAG_PRO) + { + memset (command, 0, 6); + command[0] = MUSTEK_SCSI_READ_SCANNED_DATA; + command[2] = ((lines * bpl) >> 16) & 0xff; + command[3] = ((lines * bpl) >> 8) & 0xff; + command[4] = ((lines * bpl) >> 0) & 0xff; + + return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp); + } + else /* Paragon series */ + { + memset (command, 0, 6); + command[0] = MUSTEK_SCSI_READ_SCANNED_DATA; + command[2] = (lines >> 16) & 0xff; + command[3] = (lines >> 8) & 0xff; + command[4] = (lines >> 0) & 0xff; + return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp); } } } @@ -492,67 +694,151 @@ dev_read_req_enter (Mustek_Scanner *s, SANE_Byte *buf, int lines, int bpl, static void dev_close (Mustek_Scanner * s) { - if (s->hw->flags & MUSTEK_FLAG_PP) + if (s->hw->flags & MUSTEK_FLAG_N) sanei_ab306_close (s->fd); else sanei_scsi_close (s->fd); } static SANE_Status -sense_handler (int scsi_fd, u_char *result, void *arg) +sense_handler (SANE_Int scsi_fd, SANE_Byte * result, void *arg) { + if (!result) + { + DBG (5, "sense_handler: no sense buffer\n"); + return SANE_STATUS_IO_ERROR; + } + if (!arg) + DBG (5, "sense_handler: got sense code %02x for fd %d (arg = null)\n", + result[0], scsi_fd); + else + DBG (5, "sense_handler: got sense code %02x for fd %d (arg = %uc)\n", + result[0], scsi_fd, *(SANE_Byte *) arg); switch (result[0]) { case 0x00: - break; + break; case 0x82: if (result[1] & 0x80) - return SANE_STATUS_JAMMED; /* ADF is jammed */ + { + DBG (3, "sense_handler: ADF is jammed\n"); + return SANE_STATUS_JAMMED; /* ADF is jammed */ + } break; case 0x83: if (result[2] & 0x02) - return SANE_STATUS_NO_DOCS; /* ADF out of documents */ + { + DBG (3, "sense_handler: ADF is out of documents\n"); + return SANE_STATUS_NO_DOCS; /* ADF out of documents */ + } break; case 0x84: if (result[1] & 0x10) - return SANE_STATUS_COVER_OPEN; /* open transparency adapter cover */ + { + DBG (3, "sense_handler: transparency adapter cover open\n"); + return SANE_STATUS_COVER_OPEN; /* open transparency adapter cover */ + } break; default: - DBG(1, "sense_handler: got unknown sense code %02x\n", result[0]); + DBG (1, "sense_handler: got unknown sense code %02x for fd %d\n", + result[0], scsi_fd); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } static SANE_Status -do_inquiry (Mustek_Scanner *s) +inquiry (Mustek_Scanner * s) { - char result[INQ_LEN]; + SANE_Byte result[INQ_LEN]; size_t size; + SANE_Status status; - DBG(3, "do_inquiry: sending INQUIRY\n"); + DBG (5, "inquiry: sending INQUIRY\n"); size = sizeof (result); - return dev_cmd (s, inquiry, sizeof (inquiry), result, &size); + + memset (result, 0, size); + + status = dev_cmd (s, scsi_inquiry, sizeof (scsi_inquiry), result, &size); + if (status != SANE_STATUS_GOOD) + return status; + + /* checking ADF status */ + if (s->hw->flags & MUSTEK_FLAG_ADF) + { + if (result[63] & (1 << 3)) + { + s->hw->flags |= MUSTEK_FLAG_ADF_READY; + DBG (4, "inquiry: ADF ready\n"); + } + else + { + s->hw->flags &= ~MUSTEK_FLAG_ADF_READY; + DBG (4, "inquiry: ADF not ready (out of paper)\n"); + } + } + if (!result[0]) + return SANE_STATUS_DEVICE_BUSY; + return SANE_STATUS_GOOD; +} + +static SANE_Bool +ta_available_pro (Mustek_Scanner * s) +{ + SANE_Status status; + size_t len; + SANE_Byte sense_buffer[4]; + + len = sizeof (sense_buffer); + + status = sanei_scsi_cmd (s->fd, scsi_request_sense, + sizeof (scsi_request_sense), sense_buffer, &len); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "ta_available_pro: failed: %s\n", sane_strstatus (status)); + return status; + } + DBG (5, "ta_available_pro: sense_buffer[2] = %x\n", sense_buffer[2]); + + scsi_unit_wait_ready (s); + if (sense_buffer[2] == 0x40) + return SANE_TRUE; + + return SANE_FALSE; } static SANE_Status -attach (const char *devname, Mustek_Device **devp, int may_wait) +attach (SANE_String_Const devname, Mustek_Device ** devp, SANE_Bool may_wait) { - int mustek_scanner, fw_revision; - char result[INQ_LEN]; - const char *model_name = result + 44; + SANE_Int mustek_scanner, fw_revision; + SANE_Byte result[INQ_LEN]; + SANE_Byte inquiry_byte_list[50], inquiry_text_list[17]; + SANE_Byte inquiry_byte[5], inquiry_text[5]; + SANE_Byte *model_name = result + 44; Mustek_Scanner s; Mustek_Device *dev, new_dev; SANE_Status status; size_t size; + SANE_String scsi_device_type[] = { + "Direct-Access", "Sequential-Access", "Printer", "Processor", + "Write-Once", "CD-ROM", "Scanner", "Optical Memory", "Medium Changer", + "Communications" + }; + SANE_Byte scsi_vendor[9]; + SANE_Byte scsi_product[17]; + SANE_Byte scsi_revision[5]; + SANE_Byte *pp; + SANE_Bool warning = SANE_FALSE; + SANE_Int firmware_format = 0; + SANE_Int firmware_revision_system = 0; if (devp) *devp = 0; - + for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { @@ -561,57 +847,169 @@ attach (const char *devname, Mustek_Device **devp, int may_wait) return SANE_STATUS_GOOD; } - DBG(3, "attach: opening %s as scsi device\n", devname); - memset (&new_dev, 0, sizeof (new_dev)); memset (&s, 0, sizeof (s)); s.hw = &new_dev; + s.hw->max_buffer_size = 8 * 1024; + + DBG (3, "attach: trying device %s\n", devname); status = dev_open (devname, &s, sense_handler); if (status != SANE_STATUS_GOOD) return status; - if (may_wait) + if (may_wait || force_wait) dev_wait_ready (&s); - DBG(3, "attach: sending INQUIRY\n"); + DBG (5, "attach: sending INQUIRY\n"); size = sizeof (result); - status = dev_cmd (&s, inquiry, sizeof (inquiry), result, &size); + memset (result, 0, sizeof (result)); + status = dev_cmd (&s, scsi_inquiry, sizeof (scsi_inquiry), result, &size); if (status != SANE_STATUS_GOOD || size != INQ_LEN) { - DBG(1, "attach: inquiry failed (%s)\n", sane_strstatus (status)); - + DBG (1, "attach: inquiry for device %s failed (%s)\n", devname, + sane_strstatus (status)); dev_close (&s); return status; } status = dev_wait_ready (&s); dev_close (&s); + if (status != SANE_STATUS_GOOD) return status; - /* first check for new firmware format: */ - mustek_scanner = (strncmp (result + 36, "MUSTEK", 6) == 0); - if (!mustek_scanner) + if ((result[0] & 0x1f) != 0x06) { - /* check for old format: */ - mustek_scanner = (strncmp (result + 8, "MUSTEK", 6) == 0); - model_name = result + 16; - } - mustek_scanner = mustek_scanner && (result[0] == 0x06); - - if (!mustek_scanner) - { - DBG(1, "attach: device doesn't look like a Mustek scanner " - "(result[0]=%#02x)\n", result[0]); + DBG (1, "attach: device %s doesn't look like a scanner at all (%d)\n", + devname, result[0] & 0x1f); return SANE_STATUS_INVAL; } - /* get firmware revision as BCD number: */ - fw_revision = - (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - '0'); - DBG(1, "attach: firmware revision %d.%02x\n", - fw_revision >> 8, fw_revision & 0xff); + if (debug_level >= 3) + { + /* clear spaces and special chars */ + strncpy ((SANE_String) scsi_vendor, (SANE_String) result + 8, 8); + scsi_vendor[8] = '\0'; + pp = scsi_vendor + 7; + while (pp >= scsi_vendor && (*pp == ' ' || *pp >= 127)) + *pp-- = '\0'; + strncpy ((SANE_String) scsi_product, (SANE_String) result + 16, 16); + scsi_product[16] = '\0'; + pp = scsi_product + 15; + while (pp >= scsi_product && (*pp == ' ' || *pp >= 127)) + *pp-- = '\0'; + strncpy ((SANE_String) scsi_revision, (SANE_String) result + 32, 4); + scsi_revision[4] = '\0'; + pp = scsi_revision + 3; + while (pp >= scsi_revision && (*pp == ' ' || *pp >= 127)) + *pp-- = '\0'; + DBG (3, "attach: SCSI Vendor: `%-8s' Model: `%-16s' Rev.: `%-4s'\n", + scsi_vendor, scsi_product, scsi_revision); + DBG (3, "attach: SCSI Type: %s; ANSI rev.: %d\n", + ((result[0] & 0x1f) < 0x10) ? + scsi_device_type[result[0] & 0x1f] : "Unknown", result[2] & 0x03); + DBG (3, "attach: SCSI flags: %s%s%s%s%s%s%s\n", + (result[7] & 0x80) ? "RelAdr " : "", + (result[7] & 0x40) ? "WBus32 " : "", + (result[7] & 0x20) ? "WBus16 " : "", + (result[7] & 0x10) ? "Sync " : "", + (result[7] & 0x08) ? "Linked " : "", + (result[7] & 0x02) ? "CmdQue " : "", + (result[7] & 0x01) ? "SftRe " : ""); + } + + if (debug_level >= 4) + { + /* print out inquiry */ + DBG (4, "attach: inquiry output:\n"); + inquiry_byte_list[0] = '\0'; + inquiry_text_list[0] = '\0'; + for (pp = result; pp < (result + INQ_LEN); pp++) + { + sprintf ((SANE_String) inquiry_text, "%c", + (*pp < 127) && (*pp > 31) ? *pp : '.'); + strcat ((SANE_String) inquiry_text_list, + (SANE_String) inquiry_text); + sprintf ((SANE_String) inquiry_byte, " %02x", *pp); + strcat ((SANE_String) inquiry_byte_list, + (SANE_String) inquiry_byte); + if ((pp - result) % 0x10 == 0x0f) + { + DBG (4, "%s %s\n", inquiry_byte_list, inquiry_text_list); + inquiry_byte_list[0] = '\0'; + inquiry_text_list[0] = '\0'; + } + } + } + + /* first check for new firmware format: */ + mustek_scanner = (strncmp ((SANE_String) result + 36, "MUSTEK", 6) == 0); + if (mustek_scanner) + { + if (result[43] == 'M') + { + DBG (3, "attach: found Mustek scanner (pro series firmware " + "format)\n"); + firmware_format = 2; + model_name = result + 43; + } + else + { + DBG (3, "attach: found Mustek scanner (new firmware format)\n"); + firmware_format = 1; + } + } + else + { + /* check for old format: */ + mustek_scanner = (strncmp ((SANE_String) result + 8, "MUSTEK", 6) == 0); + if (mustek_scanner) + { + model_name = result + 16; + DBG (3, "attach: found Mustek scanner (old firmware format)\n"); + firmware_format = 0; + } + else + { + /* Check for some non-Mustek scanners an print warning */ + if (strncmp ((SANE_String) result + 8, "Trust", 5) == 0) + DBG (1, "attach: this is a real Trust scanner. It is not " + " supported by this backend.\n"); + if (strncmp ((SANE_String) result + 8, "Aashima", 7) == 0) + DBG (1, "attach: this is an Aashima/Teco scanner. It is not " + " supported by this backend.\n"); + if (strncmp ((SANE_String) result + 16, "Flatbed Scanner", 15) == 0 + && strncmp ((SANE_String) result + 42, "TECO", 4) == 0) + DBG (1, "attach: this is a Relysis/Teco scanner. It is not " + " supported by this backend.\n"); + DBG (1, "attach: device %s doesn't look like a Mustek scanner\n", + devname); + return SANE_STATUS_INVAL; + } + } + + /* get firmware revision as BCD number: */ + /* General format: x.yz */ + /* Newer ScanExpress scanners (ID XC06): Vxyz */ + if (result[33] == '.') + { + fw_revision = + (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - + '0'); + firmware_revision_system = 0; + DBG (4, "attach: old firmware revision system\n"); + } + else + { + fw_revision = + (result[33] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - + '0'); + firmware_revision_system = 1; + DBG (4, "attach: new firmware revision system\n"); + } + DBG (3, "attach: firmware revision %d.%02x\n", + fw_revision >> 8, fw_revision & 0xff); dev = malloc (sizeof (*dev)); if (!dev) @@ -619,10 +1017,12 @@ attach (const char *devname, Mustek_Device **devp, int may_wait) memcpy (dev, &new_dev, sizeof (*dev)); - dev->sane.name = strdup (devname); + dev->name = strdup (devname); + if (!dev->name) + return SANE_STATUS_NO_MEM; + dev->sane.name = (SANE_String_Const) dev->name; dev->sane.vendor = "Mustek"; - dev->sane.model = strndup (model_name, 11); - dev->sane.type = "flatbed scanner"; + dev->sane.type = "flatbed scanner"; dev->x_range.min = 0; dev->y_range.min = 0; @@ -635,192 +1035,518 @@ attach (const char *devname, Mustek_Device **devp, int may_wait) dev->y_trans_range.max = SANE_FIX (5.0 * MM_PER_INCH); dev->x_trans_range.quant = 0; dev->y_trans_range.quant = 0; - if (dev->flags & MUSTEK_FLAG_PP) - /* parallel-port scanners make unhealthy noises below 51 dpi... */ - dev->dpi_range.min = SANE_FIX (51); - else - dev->dpi_range.min = SANE_FIX (1); + dev->dpi_range.min = SANE_FIX (72); /* some scanners don't like low dpi */ dev->dpi_range.quant = SANE_FIX (1); + /* default to 128 kB */ + dev->max_buffer_size = 128 * 1024; /* SCSI buffer -> use 64 k per buffer */ + dev->max_block_buffer_size = 1024 * 1024 * 1024; + dev->firmware_format = firmware_format; + dev->firmware_revision_system = firmware_revision_system; - if (strncmp (model_name, "MFS-12000CX", 11) == 0) + DBG (3, "attach: scanner id: %.11s\n", model_name); + if (strncmp ((SANE_String) model_name + 10, "PRO", 3) == 0) + DBG (3, "attach: this is probably a Paragon Pro series scanner\n"); + else if (strncmp ((SANE_String) model_name, "MFC", 3) == 0) + DBG (3, "attach: this is probably a Paragon series II scanner\n"); + else if (strncmp ((SANE_String) model_name, "M", 1) == 0) + DBG (3, + "attach: this is probably a Paragon series I or 3-pass scanner\n"); + else if (strncmp ((SANE_String) model_name, " C", 2) == 0) + DBG (3, "attach: this is probably a ScanExpress series A4 scanner\n"); + else if (strncmp ((SANE_String) model_name, " L", 2) == 0) + DBG (3, "attach: this is probably a ScanExpress series A3 scanner\n"); + else if (strncmp ((SANE_String) model_name, "XC", 2) == 0) + DBG (3, + "attach: this is probably a ScanExpress Plus series A4 scanner\n"); + else + DBG (3, "attach: I am not sure what type of scanner this is\n"); + + /* Paragon 3-pass series */ + if (strncmp ((SANE_String) model_name, "MFS-12000CX", 11) == 0) { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon MFS-12000CX v4.00 */ + dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); - dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); + dev->y_range.min = SANE_FIX (0.0); + dev->y_range.max = SANE_FIX (14.00 * MM_PER_INCH); + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (1.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (1200); - dev->flags |= MUSTEK_FLAG_USE_EIGHTS; + dev->sane.model = "MFS-12000CX"; } - else if (strncmp (model_name, "MFS-06000CX", 11) == 0) + /* There are two different versions of the MFS-6000CX, one has the model + name "MFS-06000CX", the other one is "MSF-06000CZ" */ + else if (strncmp ((SANE_String) model_name, "MFS-06000CX", 11) == 0) { + /* These values were measured and tested with a Paragon MFS-6000CX + v4.06 */ + dev->x_range.min = SANE_FIX (0.0); + dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); + dev->y_range.min = SANE_FIX (0.0); + dev->y_range.max = SANE_FIX (13.86 * MM_PER_INCH); + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (2.0); + dev->x_trans_range.max = SANE_FIX (203.0); + dev->y_trans_range.max = SANE_FIX (255.0); + + dev->dpi_range.max = SANE_FIX (600); + dev->sane.model = "MFS-6000CX"; + } + else if (strncmp ((SANE_String) model_name, "MSF-06000CZ", 11) == 0) + { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon MFS-6000CX v4.00 */ + dev->x_range.min = SANE_FIX (0.0); + dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); + dev->y_range.min = SANE_FIX (0.0); + dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (2.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (255.0); + + dev->dpi_range.max = SANE_FIX (600); + dev->sane.model = "MFS-6000CX"; + } + + /* Paragon 1-pass 14" series I */ + + /* I haven't seen a single report for this, but it is mentioned in + the old man page. All reported Paragon 1200SP had a model name + "MFS-12000SP" */ + else if (strncmp ((SANE_String) model_name, "MSF-12000SP", 11) == 0) + { + /* These values are not tested and mostly guessed. */ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); - dev->dpi_range.max = SANE_FIX (600); - dev->flags |= MUSTEK_FLAG_USE_EIGHTS; - } - else if (strncmp (model_name, "MSF-12000SP", 11) == 0) - { - dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); /* is this correct? */ - dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH);/* is this correct? */ + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (1.0); + dev->x_trans_range.max = SANE_FIX (200.0); + dev->y_trans_range.max = SANE_FIX (250.0); dev->dpi_range.max = SANE_FIX (1200); - /* This is a bit of a guess. The reports by Andreas Gaumann - indicate that at least the MSF-06000SP with firmware revision - 3.12 does not require any line-distance correction. I'm - guessing this is true for all firmware revisions and for - MSF-12000SP and MSF-06000SP. */ dev->flags |= MUSTEK_FLAG_LD_NONE; + dev->flags |= MUSTEK_FLAG_PARAGON_1; + dev->flags |= MUSTEK_FLAG_USE_BLOCK; + dev->sane.model = "MFS-12000SP"; + warning = SANE_TRUE; } - else if (strncmp (model_name, "MSF-08000SP", 11) == 0) - { - dev->x_range.max = SANE_FIX (8.50 * MM_PER_INCH); - dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); - dev->dpi_range.max = SANE_FIX (800); - /* This is a bit of a guess. The reports by Andreas Gaumann - indicate that at least the MSF-06000SP with firmware revision - 3.12 does not require any line-distance correction. I'm - guessing this is true for all firmware revisions and for - MSF-12000SP and MSF-06000SP. */ - dev->flags |= MUSTEK_FLAG_LD_NONE; - } - else if (strncmp (model_name, "MSF-06000SP", 11) == 0) - { - dev->x_range.max = SANE_FIX (220); /* measured */ - dev->y_range.max = SANE_FIX (360); /* measured */ - dev->dpi_range.max = SANE_FIX (600); - /* This is a bit of a guess. The reports by Andreas Gaumann - indicate that at least the MSF-06000SP with firmware revision - 3.12 does not require any line-distance correction. I'm - guessing this is true for all firmware revisions and for - MSF-12000SP and MSF-06000SP. */ - dev->flags |= MUSTEK_FLAG_LD_NONE; - } - else if (strncmp (model_name, "MFC-08000CZ", 11) == 0) - { - dev->x_range.max = SANE_FIX (220.0); /* measured */ - dev->y_range.max = SANE_FIX (292.0); /* measured */ - dev->dpi_range.max = SANE_FIX (800); - } - else if (strncmp (model_name, "MFC-06000CZ", 11) == 0) - { - dev->x_range.max = SANE_FIX (220.0); /* measured */ - dev->y_range.max = SANE_FIX (292.0); /* measured */ - dev->dpi_range.max = SANE_FIX (600); - } - /* No documentation for these, but they do exist. Duh... */ - else if (strncmp (model_name, "MFS-12000SP", 11) == 0) + /* MFS-8000 SP v 1.x */ + else if (strncmp ((SANE_String) model_name, "MSF-08000SP", 11) == 0) { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon MFS-8000SP v1.20 */ + dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); - dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); - /* This is a conservative setting. It is 3mm less than what the - windows driver uses, but this way the scanner doesn't make - ugly noises. */ - dev->x_trans_range.max = SANE_FIX (8.0 * MM_PER_INCH); - dev->y_trans_range.max = SANE_FIX (9.7 * MM_PER_INCH); + dev->y_range.min = SANE_FIX (0); + dev->y_range.max = SANE_FIX (355.6); + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (1.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (255.0); + + dev->dpi_range.max = SANE_FIX (800); + /* At least scanners with firmware 1.20 need a gamma table upload + in color mode, otherwise the image is red */ + if (fw_revision == 0x120) + dev->flags |= MUSTEK_FLAG_FORCE_GAMMA; + dev->flags |= MUSTEK_FLAG_PARAGON_1; + dev->sane.model = "MFS-8000SP"; + } + /* This model name exists */ + else if (strncmp ((SANE_String) model_name, "MSF-06000SP", 11) == 0) + { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon MFS-6000SP v3.12 */ + dev->x_range.min = SANE_FIX (0.0); + dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); + dev->y_range.min = SANE_FIX (0.0); + dev->y_range.max = SANE_FIX (355.6); + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (1.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (255.0); + dev->dpi_range.max = SANE_FIX (600); + /* Looks like at least some versions of this scanner produce black images + in gray and color mode if MUSTEK_FORCE_GAMMA is not set. At least + versions 2.01, 2.02 and 2.10 are reported as having this bug. 3.12 + doesn't need this workaround but it doesn't harm either. */ + dev->flags |= MUSTEK_FLAG_FORCE_GAMMA; + dev->flags |= MUSTEK_FLAG_PARAGON_1; + dev->sane.model = "MFS-6000SP"; + } + + /* This one was reported multiple times */ + else if (strncmp ((SANE_String) model_name, "MFS-12000SP", 11) == 0) + { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon MFS-12000SP v1.02 and v1.00 */ + dev->x_range.min = SANE_FIX (0.0); + dev->x_range.max = SANE_FIX (217.0); + dev->y_range.min = SANE_FIX (2.0); + dev->y_range.max = SANE_FIX (352.0); + dev->x_trans_range.min = SANE_FIX (0.0); + dev->y_trans_range.min = SANE_FIX (0.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (250.0); dev->dpi_range.max = SANE_FIX (1200); - /* Revision 1.00 of the MFS-12000SP firmware is buggy and needs - this workaround. Maybe others need it too, but this one is - for sure. We know for certain that revision 1.02 or newer - has this bug fixed. */ - if (fw_revision < 0x102) - dev->flags |= MUSTEK_FLAG_LD_MFS; + /* Earlier versions of this source code used MUSTEK_FLAG_LD_MFS + for firmware versions < 1.02 and LD_NONE for the rest. This + didn't work for my scanners. 1.00 doesn't need any LD + correction, 1.02, 1.07 and 1.11 do need normal LD + corrections. Maybe all != 1.00 need normal LD */ + dev->flags |= MUSTEK_FLAG_PARAGON_1; + dev->flags |= MUSTEK_FLAG_LD_BLOCK; + dev->flags |= MUSTEK_FLAG_USE_BLOCK; + dev->sane.model = "MFS-12000SP"; + } + /* MFS-8000 SP v2.x */ + else if (strncmp ((SANE_String) model_name, "MFS-08000SP", 11) == 0) + { + /* These values are tested with a MFS-08000SP v 2.04 */ + dev->x_range.min = SANE_FIX (0.0); + dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); + dev->y_range.min = SANE_FIX (0); + dev->y_range.max = SANE_FIX (355.6); + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (1.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (255.0); + + dev->dpi_range.max = SANE_FIX (800); + /* At least scanners with firmware 1.20 need a gamma table upload + in color mode, otherwise the image is red */ + if (fw_revision == 0x120) + dev->flags |= MUSTEK_FLAG_FORCE_GAMMA; + dev->flags |= MUSTEK_FLAG_PARAGON_1; + dev->sane.model = "MFS-8000SP"; + } + /* I have never seen one of those */ + else if (strncmp ((SANE_String) model_name, "MFS-06000SP", 11) == 0) + { + /* These values are not tested. */ + dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); + dev->y_range.max = SANE_FIX (13.84 * MM_PER_INCH); + /* copied from MSF-06000SP */ + dev->x_trans_range.min = SANE_FIX (1.0); + dev->y_trans_range.min = SANE_FIX (1.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (255.0); + dev->dpi_range.max = SANE_FIX (600); + dev->flags |= MUSTEK_FLAG_PARAGON_1; + dev->sane.model = "MFS-6000SP"; + warning = SANE_TRUE; + } + + /* Paragon 1-pass A4 series II */ + else if (strncmp ((SANE_String) model_name, "MFC-08000CZ", 11) == 0) + { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon 800 II SP v1.06. */ + dev->x_range.min = SANE_FIX (1.5); + dev->x_range.max = SANE_FIX (218.0); + dev->y_range.min = SANE_FIX (0.0); + dev->y_range.max = SANE_FIX (293.0); + dev->x_trans_range.min = SANE_FIX (0.0); + dev->y_trans_range.min = SANE_FIX (0.0); + dev->x_trans_range.max = SANE_FIX (205.0); + dev->y_trans_range.max = SANE_FIX (254.0); + + dev->dpi_range.max = SANE_FIX (800); + dev->max_block_buffer_size = 2 * 1024 * 1024; + + dev->flags |= MUSTEK_FLAG_PARAGON_2; + dev->flags |= MUSTEK_FLAG_LD_BLOCK; + dev->flags |= MUSTEK_FLAG_USE_BLOCK; + dev->sane.model = "800S/800 II SP"; + } + else if (strncmp ((SANE_String) model_name, "MFC-06000CZ", 11) == 0) + { + /* These values were measured and compared to those from the + Windows driver. Tested with a Paragon 600 II CD, a Paragon + MFC-600S and a Paragon 600 II N. */ + dev->x_range.min = SANE_FIX (0.0); + dev->x_range.max = SANE_FIX (218.0); + dev->y_range.min = SANE_FIX (0.0); + dev->y_range.max = SANE_FIX (293.0); + dev->x_trans_range.min = SANE_FIX (0.0); + dev->y_trans_range.min = SANE_FIX (0.0); + dev->x_trans_range.max = SANE_FIX (201.0); + dev->y_trans_range.max = SANE_FIX (257.0); + + dev->dpi_range.max = SANE_FIX (600); + /* This model comes in a non-scsi version, too. It is supplied + with its own parallel-port like adapter, an AB306N. Two + firmware revisions are known: 1.01 and 2.00. Each needs its + own line-distance correction code. */ + if (dev->flags & MUSTEK_FLAG_N) + { + if (fw_revision < 0x200) + dev->flags |= MUSTEK_FLAG_LD_N1; + else + dev->flags |= MUSTEK_FLAG_LD_N2; + dev->x_trans_range.min = SANE_FIX (33.0); + dev->y_trans_range.min = SANE_FIX (62.0); + dev->x_trans_range.max = SANE_FIX (183.0); + dev->y_trans_range.max = SANE_FIX (238.0); + dev->max_block_buffer_size = 1024 * 1024 * 1024; + dev->sane.model = "600 II N"; + } else - dev->flags |= MUSTEK_FLAG_LD_NONE; + { + dev->sane.model = "600S/600 II CD"; + dev->flags |= MUSTEK_FLAG_PARAGON_2; + dev->flags |= MUSTEK_FLAG_LD_BLOCK; + dev->flags |= MUSTEK_FLAG_USE_BLOCK; + dev->max_block_buffer_size = 2 * 1024 * 1024; + } } - else if (strncmp (model_name, "MFS-08000SP", 11) == 0) + + /* ScanExpress and ScanMagic series */ + else if (strncmp ((SANE_String) model_name, " C03", 4) == 0) { - dev->x_range.max = SANE_FIX (8.50 * MM_PER_INCH); - dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); - dev->dpi_range.max = SANE_FIX (800); - } - else if (strncmp (model_name, "MFS-06000SP", 11) == 0) - { - dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); /* is this correct? */ - dev->y_range.max = SANE_FIX (13.84 * MM_PER_INCH);/* is this correct? */ + /* These values were measured and compared to those from the Windows + driver. Tested with a ScannExpress 6000SP 1.00 */ + dev->x_range.max = SANE_FIX (215); + dev->y_range.min = SANE_FIX (0); + dev->y_range.max = SANE_FIX (293); + + dev->x_trans_range.min = SANE_FIX (0); + dev->y_trans_range.min = SANE_FIX (0); + dev->x_trans_range.max = SANE_FIX (150.0); + dev->y_trans_range.max = SANE_FIX (175.0); + dev->dpi_range.max = SANE_FIX (600); - } - else if (strncmp(model_name, "MSF-06000CZ", 11) == 0) - { - dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); - dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); - dev->dpi_range.max = SANE_FIX (600); - dev->flags |= MUSTEK_FLAG_USE_EIGHTS | MUSTEK_FLAG_DOUBLE_RES; - } - else if (strncmp(model_name, " C03", 4) == 0) - { - dev->x_range.max = SANE_FIX (216); - dev->y_range.min = SANE_FIX (2.5); - dev->y_range.max = SANE_FIX (294.5); - dev->dpi_range.max = SANE_FIX (600); - dev->dpi_range.min = SANE_FIX (75); + dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; + /* At least the SE 6000SP with firmware 1.00 limits its + x-resolution to 300 dpi and does *no* interpolation at higher + resolutions. So this has to be done in software. */ + dev->flags |= MUSTEK_FLAG_ENLARGE_X; + dev->sane.model = "ScanExpress 6000SP"; } - else if (strncmp(model_name, " C04", 4) == 0) + /* There are two different versions of the ScanExpress 12000SP, one + has the model name " C06", the other one is "XC06". The latter + seems to be used in the newer "Plus" models */ + else if (strncmp ((SANE_String) model_name, " C06", 4) == 0) { - dev->x_range.max = SANE_FIX (216); - dev->y_range.min = SANE_FIX (2.5); - dev->y_range.max = SANE_FIX (294.5); - dev->dpi_range.max = SANE_FIX (800); - dev->dpi_range.min = SANE_FIX (75); - dev->flags |= MUSTEK_FLAG_SE; - } - else if (strncmp(model_name, " C06", 4) == 0) - { - dev->x_range.max = SANE_FIX (216); /* measured */ - dev->y_range.min = SANE_FIX (2.5); - dev->y_range.max = SANE_FIX (294.5); /* measured */ + /* These values were measured and compared to those from the Windows + driver. Tested with a ScaneExpress 12000SP 2.02 and a ScanMagic + 9636S v 1.01 */ + dev->x_range.min = SANE_FIX (0); + dev->y_range.min = SANE_FIX (0); + dev->x_range.max = SANE_FIX (215.9); + dev->y_range.max = SANE_FIX (291.2); + + dev->x_trans_range.min = SANE_FIX (0); + dev->y_trans_range.min = SANE_FIX (0); + dev->x_trans_range.max = SANE_FIX (150.0); + dev->y_trans_range.max = SANE_FIX (175.0); + dev->dpi_range.max = SANE_FIX (1200); - dev->dpi_range.min = SANE_FIX (75); + dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; + /* The ScanExpress models limit their x-resolution to 600 dpi + and do *no* interpolation at higher resolutions. So this has + to be done in software. */ + dev->flags |= MUSTEK_FLAG_ENLARGE_X; + dev->flags |= MUSTEK_FLAG_COVER_SENSOR; + dev->sane.model = "ScanExpress 12000SP"; } - else if (strncmp(model_name, " C12", 4) == 0) + else if (strncmp ((SANE_String) model_name, "XC06", 4) == 0) { + /* These values are tested with a SE 12000 SP Plus v 1.01 */ dev->x_range.max = SANE_FIX (216); + dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (294.5); + + dev->x_trans_range.min = SANE_FIX (0); + dev->y_trans_range.min = SANE_FIX (0); + dev->x_trans_range.max = SANE_FIX (152.0); + dev->y_trans_range.max = SANE_FIX (177.0); + dev->dpi_range.max = SANE_FIX (1200); - dev->dpi_range.min = SANE_FIX (75); + dev->dpi_range.min = SANE_FIX (60); + dev->flags |= MUSTEK_FLAG_SE; + dev->flags |= MUSTEK_FLAG_SE_PLUS; + /* The ScanExpress models limit their x-resolution to 600 dpi + and do *no* interpolation at higher resolutions. So this has + to be done in software. */ + dev->flags |= MUSTEK_FLAG_ENLARGE_X; + dev->flags |= MUSTEK_FLAG_COVER_SENSOR; + dev->sane.model = "ScanExpress 12000SP Plus"; + } + /* ScanExpress A3 SP */ + else if (strncmp ((SANE_String) model_name, " L03", 4) == 0) + { + /* These values were measured with a ScannExpress A3 SP 2.00 */ + dev->x_range.max = SANE_FIX (297); + dev->y_range.min = SANE_FIX (0); + dev->y_range.max = SANE_FIX (430); + + /* TA couldn't be tested due to lack of equipment. So At least + the TA IV (A4 size) is supported */ + dev->x_trans_range.min = SANE_FIX (0); + dev->y_trans_range.min = SANE_FIX (0); + dev->x_trans_range.max = SANE_FIX (150.0); + dev->y_trans_range.max = SANE_FIX (175.0); + + dev->dpi_range.max = SANE_FIX (600); + dev->dpi_range.min = SANE_FIX (60); + dev->flags |= MUSTEK_FLAG_SE; + /* The ScanExpress models limit their x-resolution to 300 dpi + and do *no* interpolation at higher resolutions. So this has + to be done in software. */ + dev->flags |= MUSTEK_FLAG_ENLARGE_X; + dev->flags |= MUSTEK_FLAG_COVER_SENSOR; + dev->sane.model = "ScanExpress A3 SP"; + } + /* Paragon 1200 SP Pro */ + else if (strncmp ((SANE_String) model_name, "MFS-1200SPPRO", 13) == 0) + { + /* These values were measured with a Paragon 1200 SP Pro v2.01 */ + dev->x_range.max = SANE_FIX (8.6 * MM_PER_INCH); + dev->y_range.max = SANE_FIX (13.70 * MM_PER_INCH); + dev->dpi_range.max = SANE_FIX (1200); + dev->sane.model = "1200 SP PRO"; + dev->flags |= MUSTEK_FLAG_LD_NONE; + dev->flags |= MUSTEK_FLAG_ENLARGE_X; + } + /* No documentation, but it works: Paragon 1200 A3 PRO */ + else if (strncmp ((SANE_String) model_name, "MFS-1200A3PRO", 13) == 0) + { + /* These values were measured and compared to those from the Windows + driver. Tested with a Paragon 1200 A3 Pro v1.10 */ + dev->x_range.max = SANE_FIX (11.7 * MM_PER_INCH); + dev->y_range.max = SANE_FIX (17 * MM_PER_INCH); + dev->dpi_range.max = SANE_FIX (1200); + dev->sane.model = "1200 A3 PRO"; + dev->flags |= MUSTEK_FLAG_LD_NONE; + dev->flags |= MUSTEK_FLAG_ENLARGE_X; + warning = SANE_TRUE; } else { - DBG(1, "attach: unknown model `%s'\n", model_name); + DBG (0, "attach: this Mustek scanner (ID: %s) is not supported yet\n", + model_name); + DBG (0, "attach: please set the debug level to 5 and send a debug " + "report\n"); + DBG (0, "attach: to henning@meier-geinitz.de (export " + "SANE_DEBUG_MUSTEK=5\n"); + DBG (0, "attach: scanimage -L 2>debug.txt). Thank you.\n"); free (dev); return SANE_STATUS_INVAL; } - if (dev->flags & MUSTEK_FLAG_LD_MFS) - DBG(1, "attach: using special line-distance algorithm\n"); - if (dev->flags & MUSTEK_FLAG_LD_NONE) - DBG(1, "attach: scanner has automatic line-distance correction\n"); if (dev->flags & MUSTEK_FLAG_SE) - dev->flags |= MUSTEK_FLAG_SINGLE_PASS; - else + { + DBG (3, "attach: this is a single-pass scanner\n"); + if (result[63] & (1 << 6)) + { + dev->flags |= MUSTEK_FLAG_TA; + DBG (3, "attach: scanner supports transparency adapter (TA)\n"); + } + } + else { if (result[57] & (1 << 6)) - dev->flags |= MUSTEK_FLAG_SINGLE_PASS; + { + DBG (3, "attach: this is a single-pass scanner\n"); + if (dev->flags & MUSTEK_FLAG_LD_NONE) + DBG (4, + "attach: scanner doesn't need line-distance correction\n"); + else if (dev->flags & MUSTEK_FLAG_LD_N1) + DBG (4, "attach: scanner has N1 line-distance correction\n"); + else if (dev->flags & MUSTEK_FLAG_LD_N2) + DBG (4, "attach: scanner has N2 line-distance correction\n"); + else if (dev->flags & MUSTEK_FLAG_LD_BLOCK) + DBG (4, "attach: scanner has block line-distance correction\n"); + else + DBG (4, "attach: scanner has normal line-distance correction\n"); + } else - { - /* three-pass scanners quantize to 1% of the maximum resolution/2: */ - dev->dpi_range.quant = dev->dpi_range.max / 2 / 100; - dev->dpi_range.min = dev->dpi_range.quant; - } + { + dev->flags |= MUSTEK_FLAG_THREE_PASS; + /* three-pass scanners quantize to 0.5% of the maximum resolution: */ + dev->dpi_range.quant = dev->dpi_range.max / 200; + dev->dpi_range.min = dev->dpi_range.quant; + DBG (3, "attach: this is a three-pass scanner\n"); + } + if (result[57] & (1 << 5)) + { + DBG (3, "attach: this is a professional series scanner\n"); + dev->flags |= MUSTEK_FLAG_PRO; + status = dev_open (devname, &s, sense_handler); + if (status == SANE_STATUS_GOOD) + { + if (ta_available_pro (&s)) + { + dev->flags |= MUSTEK_FLAG_TA; + DBG (3, "attach: found transparency adapter (TA)\n"); + } + dev_close (&s); + } + else + { + DBG (1, "attach: couldn't open device: %s\n", + sane_strstatus (status)); + return status; + } + } if (result[63] & (1 << 2)) - dev->flags |= MUSTEK_FLAG_ADF; - if (result[63] & (1 << 3)) - dev->flags |= MUSTEK_FLAG_ADF_READY; + { + dev->flags |= MUSTEK_FLAG_ADF; + DBG (3, "attach: found automatic document feeder (ADF)\n"); + if (result[63] & (1 << 3)) + { + dev->flags |= MUSTEK_FLAG_ADF_READY; + DBG (4, "attach: automatic document feeder is ready\n"); + } + else + { + DBG (4, "attach: automatic document feeder is out of " + "documents\n"); + } + } + if (result[63] & (1 << 6)) - dev->flags |= MUSTEK_FLAG_TA; + { + dev->flags |= MUSTEK_FLAG_TA; + DBG (3, "attach: found transparency adapter (TA)\n"); + } } - if (! (result[62] & (1 << 0))) + if (dev->flags & MUSTEK_FLAG_COVER_SENSOR) { - DBG(1, "attach: cover open\n"); + if (result[62] & (1 << 0)) + DBG (4, "attach: scanner cover is closed\n"); + else + DBG (4, "attach: scanner cover is open\n"); } - - DBG(2, "attach: found Mustek scanner model %s (%s), %s%s%s%s\n", - dev->sane.model, dev->sane.type, - (dev->flags & MUSTEK_FLAG_SINGLE_PASS) ? "1-pass" : "3-pass", - (dev->flags & MUSTEK_FLAG_ADF) ? ", ADF" : "", - (dev->flags & MUSTEK_FLAG_TA) ? ", TA" : "", - (dev->flags & MUSTEK_FLAG_SE) ? ", SE" : ""); + + if (warning == SANE_TRUE) + { + DBG (0, + "WARNING: Your scanner was detected by the SANE Mustek backend, " + "but\n it is not fully tested. It may or may not work. Be " + "carefull and read\n the PROBLEMS file in the sane directory. " + "Please set the debug level of this\n backend to maximum " + "(export SANE_DEBUG_MUSTEK=255) and send the output of\n " + "scanimage -L to the SANE mailing list sane-devel@mostang.com. " + "Please include\n the exact model name of your scanner and to " + "which extend it works.\n"); + } + + DBG (2, "attach: found Mustek %s %s, %s%s%s%s\n", + dev->sane.model, dev->sane.type, + (dev->flags & MUSTEK_FLAG_THREE_PASS) ? "3-pass" : "1-pass", + (dev->flags & MUSTEK_FLAG_ADF) ? ", ADF" : "", + (dev->flags & MUSTEK_FLAG_TA) ? ", TA" : "", + (dev->flags & MUSTEK_FLAG_SE) ? ", SE" : ""); ++num_devices; dev->next = first_dev; @@ -835,7 +1561,7 @@ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; - int i; + SANE_Int i; for (i = 0; strings[i]; ++i) { @@ -847,55 +1573,75 @@ max_string_size (const SANE_String_Const strings[]) } static SANE_Status -constrain_value (Mustek_Scanner *s, SANE_Int option, void *value, - SANE_Int *info) +constrain_value (Mustek_Scanner * s, SANE_Int option, void *value, + SANE_Int * info) { SANE_Fixed w, dpi; + SANE_Status status; - if (option == OPT_RESOLUTION && !(s->hw->flags & MUSTEK_FLAG_SINGLE_PASS)) + if (value) + w = *(SANE_Fixed *) value; + else + w = 0; + + if (option == OPT_RESOLUTION) { - /* The three pass scanners use a 1% of the maximum resolution - increment for resolutions less than or equal to half of the - maximum resolution and a 10% of the maximum resolution - increment for larger resolutions. We can't represent this - easily in SANE, so the constraint is simply for 1dpi and then - we round to the 10% increments if necessary. */ - SANE_Fixed max_dpi, quant, half_res; - - w = *(SANE_Word *) value; - max_dpi = s->hw->dpi_range.max; - half_res = max_dpi / 2; - - if (w > half_res) + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { - /* round to 10% step: */ - quant = (half_res / 10); - dpi = (w + quant / 2) / quant; - dpi *= quant; - if (dpi != w) + /* The three pass scanners use a 0.5% of the maximum resolution + increment for resolutions less than or equal to half of the + maximum resolution. The MFS-06000CX uses a 5% of the maximum + resolution increment for larger resolutions. The models + MFS-12000CX and MSF-06000CZ use 1% of the maximum resolution. + We can't represent this easily in SANE, so the constraint is + simply for 0.5% and then we round to the 5% or 1% increments + if necessary. */ + SANE_Fixed max_dpi, quant, half_res; + + /*w = *(SANE_Word *) value; */ + max_dpi = s->hw->dpi_range.max; + half_res = max_dpi / 2; + + if (w > half_res) { - *(SANE_Word *) value = dpi; - if (info) - *info |= SANE_INFO_INEXACT; + /* quantizize to 1% step */ + quant = max_dpi / 100; + + dpi = (w + quant / 2) / quant; + dpi *= quant; + if (dpi != w) + { + *(SANE_Word *) value = dpi; + if (info) + *info |= SANE_INFO_INEXACT; + } } + } } - return sanei_constrain_value (s->opt + option, value, info); + + status = sanei_constrain_value (s->opt + option, value, info); + if (s->opt[option].type == SANE_TYPE_FIXED) + DBG (5, "constrain_value: %s = %.2f (was %.2f)\n", s->opt[option].name, + SANE_UNFIX (*(SANE_Word *) value), SANE_UNFIX (w)); + return status; } -/* Quantize s->req.resolution and return the resolution code for the +/* Quantize s->val[OPT_RESOLUTION].w and return the resolution code for the quantized resolution. Quantization depends on scanner type (single - pass vs. three-pass). */ -static int -encode_resolution (Mustek_Scanner *s) + pass vs. three-pass) and resolution */ +static SANE_Int +encode_resolution (Mustek_Scanner * s) { SANE_Fixed max_dpi, dpi; - int code, mode = 0; + SANE_Int code, mode = 0; dpi = s->val[OPT_RESOLUTION].w; - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - code = dpi >> SANE_FIXED_SCALE_SHIFT; + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) + { + code = dpi >> SANE_FIXED_SCALE_SHIFT; + } else { SANE_Fixed quant, half_res; @@ -903,36 +1649,39 @@ encode_resolution (Mustek_Scanner *s) max_dpi = s->hw->dpi_range.max; half_res = max_dpi / 2; - if (s->hw->flags & MUSTEK_FLAG_DOUBLE_RES) - { - dpi /= 2; - mode = 0x100; /* indicate double resultion */ - } - if (dpi <= half_res) { - quant = half_res / 100; - code = (dpi + quant / 2) / quant; - if (code < 1) - code = 1; + /* quantizize to 0.5% step */ + quant = max_dpi / 200; } else { - /* quantize to 10% step: */ - quant = (half_res / 10); - code = (dpi + quant / 2) / quant; - mode = 0x100; /* indicate 10% quantization */ + /* quantizize to 1% step */ + quant = max_dpi / 100; + mode = 0x100; /* indicate 5% or 1% quantization */ } + + code = (dpi + quant / 2) / quant; + if (code < 1) + code = 1; + } + DBG (5, "encode_resolution: code = 0x%x (%d); mode = %x\n", code, code, + mode); return code | mode; } -static int -encode_percentage (Mustek_Scanner *s, double value, double quant) +static SANE_Int +encode_percentage (Mustek_Scanner * s, double value) { - int max, code, sign = 0; + SANE_Int max, code, sign = 0; - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + { + code = (int) ((value / 100.0 * 12) + 12.5); + max = 0x18; + } + else { if (value < 0.0) { @@ -943,11 +1692,6 @@ encode_percentage (Mustek_Scanner *s, double value, double quant) code |= sign; max = 0xff; } - else - { - code = (int) ((value / quant) + 12.5); - max = 0x18; - } if (code > max) code = max; if (code < 0) @@ -955,11 +1699,48 @@ encode_percentage (Mustek_Scanner *s, double value, double quant) return code; } +/* encode halftone pattern type and size */ static SANE_Status -scan_area_and_windows (Mustek_Scanner *s) +encode_halftone (Mustek_Scanner * s) { - u_int8_t cmd[117], *cp; - int i, dim; + SANE_String selection = s->val[OPT_HALFTONE_DIMENSION].s; + SANE_Int i = 0; + + while ((halftone_list != 0) && (strcmp (selection, halftone_list[i]) != 0)) + { + i++; + } + if (halftone_list[i] == 0) + return SANE_STATUS_INVAL; + + if (i < 0x0c) /* standard pattern */ + { + s->custom_halftone_pattern = SANE_FALSE; + s->halftone_pattern_type = i; + } + else /* custom pattern */ + { + s->custom_halftone_pattern = SANE_TRUE; + i -= 0x0c; + i = 8 - i; + if (i < 8) + i--; + i = i + (i << 4); + s->halftone_pattern_type = i; + } + + DBG (5, "encode_halftone: %s pattern type %x\n", + s->custom_halftone_pattern ? "custom" : "standard", + s->halftone_pattern_type); + return SANE_STATUS_GOOD; +} + +/* Paragon series */ +static SANE_Status +area_and_windows (Mustek_Scanner * s) +{ + SANE_Byte cmd[117], *cp; + SANE_Int i, offset; /* setup SCSI command (except length): */ memset (cmd, 0, sizeof (cmd)); @@ -967,199 +1748,589 @@ scan_area_and_windows (Mustek_Scanner *s) cp = cmd + 6; + /* Some scanners need a larger scanarea for line-distance correction */ + offset = 0; + if (((s->hw->flags & MUSTEK_FLAG_LD_N1) + || ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK) + && (s->hw->flags & MUSTEK_FLAG_PARAGON_1))) + && (s->mode & MUSTEK_MODE_COLOR)) + offset = MAX_LINE_DIST; + /* fill in frame header: */ if (s->hw->flags & MUSTEK_FLAG_USE_EIGHTS) { double eights_per_mm = 8 / MM_PER_INCH; - + SANE_Int tlx, tly, brx, bry; /* * The MSF-06000CZ seems to lock-up if the pixel-unit is used. * Using 1/8" works. + * This doesn't seem to be true with the current scheme. + * This code isn't used at the moment. */ - *cp++ = ((s->mode & MUSTEK_MODE_HALFTONE) ? 0x01 : 0x00); - STORE16L(cp, SANE_UNFIX(s->val[OPT_TL_X].w) * eights_per_mm + 0.5); - STORE16L(cp, SANE_UNFIX(s->val[OPT_TL_Y].w) * eights_per_mm + 0.5); - STORE16L(cp, SANE_UNFIX(s->val[OPT_BR_X].w) * eights_per_mm + 0.5); - STORE16L(cp, SANE_UNFIX(s->val[OPT_BR_Y].w) * eights_per_mm + 0.5); + *cp++ = ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01); + + tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * eights_per_mm + 0.5; + tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * eights_per_mm + 0.5; + brx = SANE_UNFIX (s->val[OPT_BR_X].w) * eights_per_mm + 0.5; + bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * eights_per_mm + 0.5; + STORE16L (cp, tlx); + STORE16L (cp, tly); + STORE16L (cp, brx); + STORE16L (cp, bry); + DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); " + "brx=%d (%d mm); bry=%d (%d mm)\n", tlx, + (int) (tlx / eights_per_mm), tly, (int) (tly / eights_per_mm), brx, + (int) (brx / eights_per_mm), bry, (int) (bry / eights_per_mm)); } else { double pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH; + SANE_Int tlx, tly, brx, bry; - /* pixel unit and halftoning: */ - *cp++ = 0x8 | ((s->mode & MUSTEK_MODE_HALFTONE) ? 0x01 : 0x00); + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + /* 3pass scanners use 1/2 of the max resolution as base */ + pixels_per_mm /= 2; + + /* pixel unit and halftoning: */ + *cp++ = 0x8 | ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01); /* fill in scanning area: */ - STORE16L(cp, SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5); - STORE16L(cp, SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5); - STORE16L(cp, SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5); - STORE16L(cp, SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5); + if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) + { + /* must mirror the x coordinates */ + brx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_TL_X].w) + * pixels_per_mm + 0.5; + tlx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_BR_X].w) + * pixels_per_mm + 0.5; + } + else + { + tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5; + brx = SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5; + + } + tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5; + bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5 + offset; + STORE16L (cp, tlx); + STORE16L (cp, tly); + STORE16L (cp, brx); + STORE16L (cp, bry); + DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); " + "brx=%d (%d mm); bry=%d (%d mm)\n", tlx, + (int) (tlx / pixels_per_mm), tly, (int) (tly / pixels_per_mm), brx, + (int) (brx / pixels_per_mm), bry, (int) (bry / pixels_per_mm)); } - - dim = s->val[OPT_HALFTONE_DIMENSION].w; - if (dim) + + if (s->custom_halftone_pattern) { - *cp++ = 0x40; /* mark presence of user pattern */ - *cp++ = (dim << 4) | dim; /* set pattern length */ - for (i = 0; i < dim * dim; ++i) + *cp++ = 0x40; /* mark presence of user pattern */ + *cp++ = s->halftone_pattern_type; /* set pattern length */ + for (i = 0; i < (s->halftone_pattern_type & 0x0f) * + ((s->halftone_pattern_type >> 4) & 0x0f); ++i) *cp++ = s->val[OPT_HALFTONE_PATTERN].wa[i]; } cmd[4] = (cp - cmd) - 6; + return dev_cmd (s, cmd, (cp - cmd), 0, 0); } +/* ScanExpress */ static SANE_Status -do_set_window (Mustek_Scanner *s, int lamp) +set_window_se (Mustek_Scanner * s, SANE_Int lamp) { - u_int8_t cmd[58], *cp; + SANE_Byte cmd[58], *cp; double pixels_per_mm; - int offset; + SANE_Int offset; + SANE_Int tlx, tly, width, height; /* setup SCSI command (except length): */ memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_SET_WINDOW; - cp = cmd + sizeof (set_window); /* skip command block */ + cp = cmd + sizeof (scsi_set_window); /* skip command block */ if (s->mode & MUSTEK_MODE_COLOR) { - /* We have to increase the specified resolution to the next */ - /* "standard" resolution due to a firmware bug(?) in color mode */ + /* We have to increase the specified resolution to the next */ + /* "standard" resolution due to a firmware bug(?) in color mode */ + /* It's possible to scan in 36, 75, 100, 150, 200, 250, 300, */ + /* 400, 500, 600, 900, 1200 dpi but the speed is only different */ + /* with 36, 150, 300, 600, 1200 dpi. */ /* Additionally we must increase the window length slightly to */ - /* compensate for different line counts for r/g/b */ - s->ld.peak_res = SANE_UNFIX (s->hw->dpi_range.max); - while (((s->ld.peak_res / 2) >= SANE_UNFIX (s->val[OPT_RESOLUTION].w)) & - ((s->ld.peak_res / 2) >= 150)) - s->ld.peak_res /= 2; - offset = MAX_LINE_DIST; /* distance r/b lines */ + /* compensate for different line counts for r/g/b */ + const SANE_Int resolution_list[] = { 36, 150, 300, 600, 1200, 0 }; + SANE_Int entry = 0; + + while (resolution_list[entry] < s->resolution_code) + entry++; + s->ld.peak_res = resolution_list[entry]; + + offset = MAX_LINE_DIST; /* distance r/b lines */ } - else + else { - s->ld.peak_res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); + /* In gray and lineart modes all resolutions are possible */ + s->ld.peak_res = s->resolution_code; offset = 0; } - - STORE16B(cp, 0); /* window identifier */ - STORE16B(cp, s->ld.peak_res); /* x and y resolution */ - STORE16B(cp, 0); /* not used acc. to specs */ + DBG (5, "set_window_se: hardware resolution is %d dpi; offset is %d\n", + s->ld.peak_res, offset); + + STORE16B (cp, 0); /* window identifier */ + STORE16B (cp, s->ld.peak_res); + /* x and y resolution */ + STORE16B (cp, 0); /* not used acc. to specs */ pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH; + /* fill in scanning area, begin and length(!) */ - STORE32B(cp, (SANE_UNFIX (s->val[OPT_TL_X].w) - \ - SANE_UNFIX (s->hw->x_range.min)) * pixels_per_mm + 0.5); - STORE32B(cp, (SANE_UNFIX (s->val[OPT_TL_Y].w) - \ - SANE_UNFIX (s->hw->y_range.min)) * pixels_per_mm + 0.5); - STORE32B(cp, SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) \ - * pixels_per_mm + 0.5); - STORE32B(cp, (SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)) \ - * pixels_per_mm + 0.5 + offset); + if ((strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) && + !(s->hw->flags & MUSTEK_FLAG_TA)) + { + /* need to add the start values of the transparency adapter */ + tlx = (SANE_UNFIX (s->val[OPT_TL_X].w) + 33.0) * pixels_per_mm + 0.5; + tly = (SANE_UNFIX (s->val[OPT_TL_Y].w) + 60.0) * pixels_per_mm + 0.5; + DBG (5, "set_window_se: added offset for transparency adapter\n"); + } + else + { + /* no transparency adapter selected or calculation done in firmware */ + tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5; + tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5; + } + width = (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w)) + * pixels_per_mm + 0.5; + height = (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w)) + * pixels_per_mm + 0.5 + offset; - *cp++ = 0x00; /* brightness, not impl. */ - *cp++ = 0x80; /* threshold, not impl. */ - *cp++ = 0x00; /* contrast, not impl. */ + DBG (5, "set_window_se: tlx=%d (%d mm); tly=%d (%d mm); width=%d (%d mm); " + "height=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly, + (int) (tly / pixels_per_mm), width, (int) (width / pixels_per_mm), + height, (int) (height / pixels_per_mm)); - /* Note that 'image composition' has no meaning for the SE series */ - /* Mode selection is accomplished solely by bits/pixel (1, 8, 24) */ + + STORE32B (cp, tlx); + STORE32B (cp, tly); + STORE32B (cp, width); + STORE32B (cp, height); + + *cp++ = 0x00; /* brightness, not impl. */ + *cp++ = 0x80; /* threshold, not impl. */ + *cp++ = 0x00; /* contrast, not impl. */ + + /* Note that 'image composition' has no meaning for the SE series */ + /* Mode selection is accomplished solely by bits/pixel (1, 8, 24) */ if (s->mode & MUSTEK_MODE_COLOR) { - *cp++ = 0x05; /* actually not used ! */ - *cp++ = 24; /* 24 bits/pixel in color mode */ + *cp++ = 0x05; /* actually not used! */ + *cp++ = 24; /* 24 bits/pixel in color mode */ } - else if (s->mode & MUSTEK_MODE_MULTIBIT) + else if (s->mode & MUSTEK_MODE_GRAY) { - *cp++ = 0x02; /* actually not used ! */ - *cp++ = 8; /* 8 bits/pixel in gray mode */ + *cp++ = 0x02; /* actually not used! */ + *cp++ = 8; /* 8 bits/pixel in gray mode */ } - else + else { - *cp++ = 0x00; /* actually not used ! */ - *cp++ = 1; /* 1 bit/pixel in lineart mode */ + *cp++ = 0x00; /* actually not used! */ + *cp++ = 1; /* 1 bit/pixel in lineart mode */ } - - cp += 13; /* skip reserved bytes */ - *cp++ = lamp; /* 0 = normal, 1 = on, 2 = off */ - cp += 7; /* skip reserved bytes */ - - cmd[8] = cp - cmd - sizeof (set_window); + + cp += 14; /* skip reserved bytes */ + *cp++ = lamp; /* 0 = normal, 1 = on, 2 = off */ + + if ((s->hw->flags & MUSTEK_FLAG_TA) + && (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0)) + *cp++ = 1; + else + *cp++ = 0; + cp += 5; /* skip reserved bytes */ + + cmd[8] = cp - cmd - sizeof (scsi_set_window); return dev_cmd (s, cmd, (cp - cmd), 0, 0); } -#if 0 +/* Pro series */ static SANE_Status -calibration (Mustek_Scanner *s) +set_window_pro (Mustek_Scanner * s) +{ + SANE_Byte cmd[20], *cp; + double pixels_per_mm; + + memset (cmd, 0, sizeof (cmd)); + cmd[0] = MUSTEK_SCSI_SET_WINDOW; + if (strcmp (s->hw->sane.model, "1200 SP PRO") == 0) + cmd[8] = 0x09; + else + cmd[8] = 0x0a; + + cp = cmd + sizeof (scsi_set_window); /* skip command block */ + + *cp++ = 0; /* what's this? */ + pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH; + + /* The next for 16 bit values are x0, y0, x1, y1 in pixels at max res */ + STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5); + STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5); + STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5); + STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5); + + if (strcmp (s->hw->sane.model, "1200 SP PRO") != 0) + *cp++ = 0x3c; /* Only needed for A3 Pro, 60 minutes until lamp-off */ + DBG (5, "set_window_pro\n"); + + return dev_cmd (s, cmd, (cp - cmd), 0, 0); +} + +/* Pro series calibration */ +static SANE_Status +get_calibration_size_pro (Mustek_Scanner * s) { SANE_Status status; - u_int8_t cmd[10 + 100000]; - size_t num; + SANE_Byte cmd[6]; + SANE_Byte result[6]; + size_t len; - num = s->hw->cal.bytes * s->hw->cal.lines; memset (cmd, 0, sizeof (cmd)); + memset (result, 0, sizeof (result)); + cmd[0] = MUSTEK_SCSI_GET_IMAGE_STATUS; + cmd[4] = 0x06; /* size of result */ + cmd[5] = 0x80; /* get back buffer size and number of buffers */ + len = sizeof (result); + status = dev_cmd (s, cmd, sizeof (cmd), result, &len); + if (status != SANE_STATUS_GOOD) + return status; + s->hw->cal.bytes = result[1] | (result[2] << 8); + s->hw->cal.lines = result[3] | (result[4] << 8); + + DBG (4, "get_calibration_size_pro: bytes=%d, lines=%d\n", s->hw->cal.bytes, + s->hw->cal.lines); + return SANE_STATUS_GOOD; +} + +static SANE_Status +get_calibration_lines_pro (Mustek_Scanner * s) +{ + SANE_Status status; + SANE_Byte cmd[10]; + size_t len; + SANE_Int line; + + DBG (2, "get_calibration_lines_pro: please wait for warmup\n"); + memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_READ_DATA; - cmd[2] = 0x01; - cmd[6] = (num >> 16) & 0xff; - cmd[7] = (num >> 8) & 0xff; - cmd[8] = (num >> 0) & 0xff; + len = s->hw->cal.bytes; + cmd[6] = (len >> 16) & 0xff; + cmd[7] = (len >> 8) & 0xff; + cmd[8] = (len >> 0) & 0xff; - status = dev_cmd (s, cmd, sizeof (read_data), - cmd + sizeof (read_data), &num); + for (line = 0; line < s->hw->cal.lines; line++) + { + status = dev_cmd (s, cmd, sizeof (scsi_read_data), + s->hw->cal.buffer + line * len, &len); + if ((status != SANE_STATUS_GOOD) + || (len != (unsigned int) s->hw->cal.bytes)) + { + DBG (1, "get_calibration_lines_pro: read failed\n"); + return status; + } + } + DBG (5, "get_calibration_lines_pro finished. Assuming 12 bits per color\n"); + return SANE_STATUS_GOOD; +} + +static SANE_Status +send_calibration_lines_pro (Mustek_Scanner * s) +{ + SANE_Status status; + SANE_Byte *cmd1, *cmd2; + size_t buf_size; + SANE_Word column, line, color; + + DBG (5, "send_calibration_lines_pro\n"); + + buf_size = s->hw->cal.bytes / 2; + cmd1 = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data)); + cmd2 = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data)); + if (!cmd1 || !cmd2) + { + DBG (1, "send_calibration_lines_pro: failed to malloc %ld bytes for " + "sending lines\n", (long int) (buf_size + sizeof (scsi_send_data))); + return SANE_STATUS_NO_MEM; + } + memset (cmd1, 0, sizeof (scsi_send_data)); + memset (cmd2, 0, sizeof (scsi_send_data)); + + cmd1[0] = cmd2[0] = MUSTEK_SCSI_SEND_DATA; + cmd1[6] = cmd2[6] = (buf_size >> 16) & 0xff; + cmd1[7] = cmd2[7] = (buf_size >> 8) & 0xff; + cmd1[8] = cmd2[8] = (buf_size >> 0) & 0xff; + cmd1[9] = 0; /* Least significant 8 bits */ + cmd2[9] = 0x80; /* Most significant 2 bits */ + + for (color = 0; color < 3; color++) + { + for (column = 0; column < s->hw->cal.bytes / 6; column++) + { + SANE_Word calibration_word = 0; + for (line = 0; line < s->hw->cal.lines; line++) + { + calibration_word += + *(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 0) + + + (*(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 1) + << 8); + } + if (!calibration_word) + calibration_word = 1; + calibration_word = (1024 * 65536 / calibration_word) - 1024; + if (calibration_word > 1023) + calibration_word = 1023; + *(cmd1 + sizeof (scsi_send_data) + (buf_size / 3) * color + column) + = calibration_word & 0xff; + *(cmd2 + sizeof (scsi_send_data) + (buf_size / 3) * color + column) + = (calibration_word >> 8) & 0xff; + } + } + + status = dev_cmd (s, cmd1, buf_size + sizeof (scsi_send_data), 0, 0); if (status != SANE_STATUS_GOOD) { - DBG(1, "Calibration: read failed\n"); + DBG (1, "send_calibration_lines_pro: send failed\n"); return status; } - num = s->hw->cal.bytes * s->hw->cal.lines; - - cmd[0] = MUSTEK_SCSI_SEND_DATA; - cmd[2] = 0x01; - cmd[6] = (num >> 16) & 0xff; - cmd[7] = (num >> 8) & 0xff; - cmd[8] = (num >> 0) & 0xff; - - status = dev_cmd (s, cmd, sizeof (send_data) + num, 0, 0); - + status = dev_cmd (s, cmd2, buf_size + sizeof (scsi_send_data), 0, 0); if (status != SANE_STATUS_GOOD) { - DBG(1, "Calibration: send failed\n"); + DBG (1, "send_calibration_lines_pro: send failed\n"); return status; - } - + } + free (cmd1); + free (cmd2); return SANE_STATUS_GOOD; -} -#endif +} static SANE_Status -send_gamma_se (Mustek_Scanner *s) +calibration_pro (Mustek_Scanner * s) { SANE_Status status; - u_int8_t gamma[10 + 4096], *cp; - int color, factor, val_a, val_b; - int i, j; + + if (s->val[OPT_QUALITY_CAL].w) + DBG (4, "calibration_pro: doing calibration\n"); + else + { + DBG (4, "calibration_pro: calibration not necessary\n"); + return SANE_STATUS_GOOD; + } + + status = get_calibration_size_pro (s); + if (status != SANE_STATUS_GOOD) + return status; + + s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes * + s->hw->cal.lines); + if (!s->hw->cal.buffer) + { + DBG (1, "calibration_pro: failed to malloc %d bytes for buffer\n", + s->hw->cal.bytes * s->hw->cal.lines); + return SANE_STATUS_NO_MEM; + } + + status = get_calibration_lines_pro (s); + if (status != SANE_STATUS_GOOD) + return status; + + status = send_calibration_lines_pro (s); + if (status != SANE_STATUS_GOOD) + return status; + + free (s->hw->cal.buffer); + return SANE_STATUS_GOOD; +} + + +/* ScanExpress series calibration */ +static SANE_Status +get_calibration_lines_se (Mustek_Scanner * s) +{ + SANE_Status status; + SANE_Byte cmd[10]; + size_t len; + SANE_Word lines, bytes_per_color; + + if (s->mode == MUSTEK_MODE_COLOR) + { + lines = s->hw->cal.lines * 3; + bytes_per_color = s->hw->cal.bytes / 3; + } + else + { + lines = s->hw->cal.lines; + bytes_per_color = s->hw->cal.bytes; + } + + DBG (4, "get_calibration_lines_se: reading %d lines (%d bytes per color)\n", + lines, bytes_per_color); + memset (cmd, 0, sizeof (cmd)); + cmd[0] = MUSTEK_SCSI_READ_DATA; + cmd[2] = 1; + cmd[7] = (lines >> 8) & 0xff; + cmd[8] = (lines >> 0) & 0xff; + len = lines * bytes_per_color; + status = dev_cmd (s, cmd, sizeof (scsi_read_data), s->hw->cal.buffer, &len); + if ((status != SANE_STATUS_GOOD) + || (len != (unsigned int) (lines * bytes_per_color))) + { + DBG (1, "get_calibration_lines_se: read failed\n"); + return status; + } + return SANE_STATUS_GOOD; +} + +static SANE_Status +send_calibration_lines_se (Mustek_Scanner * s, SANE_Word color) +{ + SANE_Status status; + SANE_Byte *cmd; + size_t buf_size; + SANE_Word column; + SANE_Word lines, bytes_per_color; + + if (s->mode == MUSTEK_MODE_COLOR) + { + lines = s->hw->cal.lines * 3; + bytes_per_color = s->hw->cal.bytes / 3; + } + else + { + lines = s->hw->cal.lines; + bytes_per_color = s->hw->cal.bytes; + } + + buf_size = bytes_per_color; + + DBG (5, "send_calibration_lines_se: %d bytes, color: %d\n", + bytes_per_color, color + 1); + + cmd = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data)); + if (!cmd) + { + DBG (1, "send_calibration_lines_se: failed to malloc %ld bytes for " + "sending lines\n", (long int) (buf_size + sizeof (scsi_send_data))); + return SANE_STATUS_NO_MEM; + } + memset (cmd, 0, sizeof (scsi_send_data)); + + for (column = 0; column < bytes_per_color; column++) + { + SANE_Word line; + SANE_Word cali_word = 0; + SANE_Int color_seq[] = { 2, 0, 1 }; + + for (line = 0; line < s->hw->cal.lines; line++) + cali_word += *(s->hw->cal.buffer + + line * bytes_per_color + + bytes_per_color * color_seq[color] + column); + if (!cali_word) + cali_word = 1; + cali_word = 256 * s->hw->cal.lines * 255 / cali_word - 256; + if (cali_word > 255) + cali_word = 255; + *(cmd + sizeof (scsi_send_data) + column) = cali_word; + } + + cmd[0] = MUSTEK_SCSI_SEND_DATA; + cmd[2] = 1; + cmd[6] = color + 1; + cmd[7] = (buf_size >> 8) & 0xff; + cmd[8] = (buf_size >> 0) & 0xff; + + status = dev_cmd (s, cmd, buf_size + sizeof (scsi_send_data), 0, 0); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "send_calibration_lines_se: send failed\n"); + return status; + } + free (cmd); + return SANE_STATUS_GOOD; +} + +static SANE_Status +calibration_se (Mustek_Scanner * s) +{ + SANE_Status status; + + if (!s->val[OPT_QUALITY_CAL].w || s->val[OPT_PREVIEW].w + || s->mode == MUSTEK_MODE_LINEART) + return SANE_STATUS_GOOD; + + DBG (4, "calibration_se: doing calibration\n"); + + s->hw->cal.lines = MIN (s->hw->cal.lines, + s->hw->buffer_size / s->hw->cal.bytes); + + s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes + * s->hw->cal.lines); + if (!s->hw->cal.buffer) + { + DBG (1, "calibration_se: failed to malloc %d bytes for buffer\n", + s->hw->cal.bytes * s->hw->cal.lines); + return SANE_STATUS_NO_MEM; + } + + status = get_calibration_lines_se (s); + if (status != SANE_STATUS_GOOD) + return status; + + if (s->mode == MUSTEK_MODE_GRAY) + status = send_calibration_lines_se (s, 0); + else + { + status = send_calibration_lines_se (s, 0); + status = send_calibration_lines_se (s, 1); + status = send_calibration_lines_se (s, 2); + } + if (status != SANE_STATUS_GOOD) + return status; + + free (s->hw->cal.buffer); + return SANE_STATUS_GOOD; +} + +/* ScanExpress series */ +static SANE_Status +send_gamma_table_se (Mustek_Scanner * s) +{ + SANE_Status status; + SANE_Byte gamma[10 + 4096], *cp; + SANE_Int color, factor, val_a, val_b; + SANE_Int i, j; # define CLIP(x) ((x) < 0 ? 0 : ((x) > 255 ? 255 : (x))) - memset (gamma, 0, sizeof (send_data)); + memset (gamma, 0, sizeof (scsi_send_data)); gamma[0] = MUSTEK_SCSI_SEND_DATA; - gamma[2] = 0x03; /* indicates gamma table */ + gamma[2] = 0x03; /* indicates gamma table */ - if (s->mode & MUSTEK_MODE_MULTIBIT) + if ((s->mode & MUSTEK_MODE_GRAY) || (s->mode & MUSTEK_MODE_COLOR)) { - if (s->hw->gamma_length + sizeof (send_data) > sizeof (gamma)) - return SANE_STATUS_NO_MEM; + if (s->hw->gamma_length + sizeof (scsi_send_data) > sizeof (gamma)) + return SANE_STATUS_NO_MEM; gamma[7] = (s->hw->gamma_length >> 8) & 0xff; gamma[8] = (s->hw->gamma_length >> 0) & 0xff; factor = s->hw->gamma_length / 256; color = (s->mode & MUSTEK_MODE_COLOR) ? 1 : 0; - do - { - gamma[6] = color; - - if (color == 0) + do + { + gamma[6] = color; + + if (color == 0) { val_a = s->gamma_table[0][1]; val_b = s->gamma_table[0][0]; @@ -1170,69 +2341,66 @@ send_gamma_se (Mustek_Scanner *s) val_a = s->gamma_table[0][s->gamma_table[color][1]]; val_b = s->gamma_table[0][s->gamma_table[color][0]]; } - /* Now val_a is extrapolated from [0] and [1] */ - val_a = MAX( 2 * val_b - val_a, 0); + /* Now val_a is extrapolated from [0] and [1] */ + val_a = MAX (2 * val_b - val_a, 0); /* Interpolate first entries from 256 entry table */ - cp = gamma + sizeof (send_data); + cp = gamma + sizeof (scsi_send_data); for (j = 0; j < factor; j++) - *cp++ = CLIP(((factor - j) * val_a + j * val_b - + factor / 2) / factor); + *cp++ = CLIP (((factor - j) * val_a + j * val_b + + factor / 2) / factor); for (i = 1; i < 256; i++) { if (color == 0) { - val_a = s->gamma_table[0][i-1]; + val_a = s->gamma_table[0][i - 1]; val_b = s->gamma_table[0][i]; } else { /* compose intensity gamma and color channel gamma: */ - val_a = s->gamma_table[0][s->gamma_table[color][i-1]]; + val_a = s->gamma_table[0][s->gamma_table[color][i - 1]]; val_b = s->gamma_table[0][s->gamma_table[color][i]]; } - + /* Interpolate next entries from the 256 entry table */ for (j = 0; j < factor; j++) - *cp++ = CLIP(((factor - j) * val_a + j * val_b - + factor / 2 ) / factor); - } + *cp++ = CLIP (((factor - j) * val_a + j * val_b + + factor / 2) / factor); + } - DBG(3, "send_gamma_se: sending table for color %d\n", gamma[6]); - status = dev_cmd (s, gamma, sizeof (send_data) - + s->hw->gamma_length, 0, 0); + DBG (5, "send_gamma_table_se: sending table for color %d\n", + gamma[6]); + status = dev_cmd (s, gamma, sizeof (scsi_send_data) + + s->hw->gamma_length, 0, 0); ++color; } - while ((color !=1) & (color < 4) & (status == SANE_STATUS_GOOD)); - + while ((color != 1) & (color < 4) & (status == SANE_STATUS_GOOD)); + return status; } else { - /* In lineart mode the threshold is encoded in byte 8 as follows */ - /* brightest -> 00 01 02 ... 7F 80 81 82 ... FF <- darkest image */ + /* In lineart mode the threshold is encoded in byte 8 as follows */ + /* brightest -> 00 01 02 ... 7F 80 81 82 ... FF <- darkest image */ gamma[6] = 0x04; - gamma[8] = 128 - 127 * SANE_UNFIX(s->val[OPT_BRIGHTNESS].w) / 100.0; - - DBG(3, "send_gamma_se: sending lineart threshold %2X\n", gamma[8]); - return dev_cmd (s, gamma, sizeof (send_data), 0, 0); + gamma[8] = 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0; + + DBG (5, "send_gamma_table_se: sending lineart threshold %2X\n", + gamma[8]); + return dev_cmd (s, gamma, sizeof (scsi_send_data), 0, 0); } -} +} +/* Paragon series */ static SANE_Status -mode_select (Mustek_Scanner *s, int color_code) +mode_select_paragon (Mustek_Scanner * s, SANE_Int color_code) { - int grain_code, speed_code; - u_int8_t mode[19], *cp; + SANE_Int speed_code; + SANE_Byte mode[19], *cp; - /* the scanners use a funky code for the grain size, let's compute it: */ - grain_code = s->val[OPT_GRAIN_SIZE].w; - if (grain_code > 7) - grain_code = 7; /* code 0 is 8x8, not 7x7 */ - grain_code = 7 - grain_code; - - /* same goes for speed: */ + /* calculate funky speed code: */ for (speed_code = 0; speed_list[speed_code]; ++speed_code) { if (strcmp (speed_list[speed_code], s->val[OPT_SPEED].s) == 0) @@ -1242,105 +2410,303 @@ mode_select (Mustek_Scanner *s, int color_code) speed_code = 4; else if (speed_code < 0) speed_code = 0; - speed_code = 4 - speed_code; /* 0 is fast, 4 is slow */ - + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + { + speed_code = 5 - speed_code; /* 1 is fast, 5 is slow */ + } + else + { + speed_code = 4 - speed_code; /* 0 is fast, 4 is slow */ + } memset (mode, 0, sizeof (mode)); mode[0] = MUSTEK_SCSI_MODE_SELECT; /* set command length and resolution code: */ - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - { - mode[4] = 0x0d; - cp = mode + 17; - STORE16L(cp, s->resolution_code); - } - else + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { mode[4] = 0x0b; mode[7] = s->resolution_code; } + else + { + mode[4] = 0x0d; + cp = mode + 17; + STORE16L (cp, s->resolution_code); + } /* set mode byte: */ mode[6] = 0x83 | (color_code << 5); if (!(s->hw->flags & MUSTEK_FLAG_USE_EIGHTS)) mode[6] |= 0x08; - if (s->val[OPT_HALFTONE_DIMENSION].w) + if (s->custom_halftone_pattern) mode[6] |= 0x10; - mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w), 3.0); - mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w), 7.0); - mode[10] = grain_code; - mode[11] = speed_code; /* lamp setting not supported yet */ - mode[12] = 0; /* shadow param not used by Mustek */ - mode[13] = 0; /* highlist param not used by Mustek */ - mode[14] = mode[15] = 0; /* paperlength not used by Mustek */ - mode[16] = 0; /* midtone param not used by Mustek */ + if (s->hw->flags & MUSTEK_FLAG_PARAGON_1) + { + if ((s->mode == MUSTEK_MODE_LINEART) + || (s->mode == MUSTEK_MODE_HALFTONE)) + { + mode[8] = + encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)); + mode[9] = + encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w)); + } + else + { + mode[8] = 0x0c; + mode[9] = 0x0c; + } + mode[10] = 2; /* grain */ + if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w) + mode[11] = 0x01; + else if ((s->mode == MUSTEK_MODE_COLOR) + || (s->mode == MUSTEK_MODE_HALFTONE)) + mode[11] = 0x00; /* speed */ + else + mode[11] = 0x02; /* speed */ + mode[12] = 0x00; /* shadow param not used by Mustek */ + mode[13] = 0xff; /* highlight only used by some scanners */ + mode[14] = 0x70; /* paper- */ + mode[15] = 0x00; /* length */ + mode[16] = 0x53; /* midtone param not used by Mustek */ + } + else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2) + { + mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)); + mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w)); + mode[10] = 2; /* grain */ + if ((s->mode == MUSTEK_MODE_COLOR) || (s->mode == MUSTEK_MODE_HALFTONE)) + mode[11] = 0x00; /* speed */ + else + mode[11] = 0x02; /* speed */ + mode[12] = 0x00; /* shadow param not used by Mustek */ + mode[13] = 0x00; /* highlight param not used by Mustek */ + mode[14] = 0x5c; /* paper- */ + mode[15] = 0x00; /* length */ + mode[16] = 0x41; /* midtone param not used by Mustek */ + } + else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + { + if (s->mode & MUSTEK_MODE_COLOR) + { + mode[8] = encode_percentage + (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS + s->pass + 1].w - 1)); + mode[9] = encode_percentage + (s, SANE_UNFIX (s->val[OPT_CONTRAST + s->pass + 1].w - 1)); + } + else + { + mode[8] = encode_percentage + (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w - 1)); + mode[9] = encode_percentage + (s, SANE_UNFIX (s->val[OPT_CONTRAST].w - 1)); + } + mode[10] = s->halftone_pattern_type; + mode[11] = speed_code; /* lamp setting not supported yet */ + mode[12] = 0; /* shadow param not used by Mustek */ + mode[13] = 0; /* highlight param not used by Mustek */ + mode[14] = mode[15] = 0; /* paperlength not used by Mustek */ + mode[16] = 0; /* midtone param not used by Mustek */ + } + else + { + mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)); + mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w)); + mode[10] = s->halftone_pattern_type; + mode[11] = speed_code; /* lamp setting not supported yet */ + mode[12] = 0; /* shadow param not used by Mustek */ + mode[13] = 0; /* highlight param not used by Mustek */ + mode[14] = mode[15] = 0; /* paperlength not used by Mustek */ + mode[16] = 0; /* midtone param not used by Mustek */ + } + DBG (5, "mode_select: resolution_code=%d (0x%x)\n", s->resolution_code, + s->resolution_code); return dev_cmd (s, mode, 6 + mode[4], 0, 0); } -/* According to Mustek, the only builtin gamma table is a linear - table, so all we support here is user-defined gamma tables. */ +/* Pro series */ static SANE_Status -gamma_correction (Mustek_Scanner *s, int color_code) +mode_select_pro (Mustek_Scanner * s) { - int i, j, table = 0, len, num_channels = 1; - u_int8_t gamma[3*256+10], val, *cp; + SANE_Byte mode[19], *cp; - if ((s->hw->flags & MUSTEK_FLAG_PP) - && !(s->mode & MUSTEK_MODE_MULTIBIT)) + memset (mode, 0, sizeof (mode)); + + mode[0] = MUSTEK_SCSI_MODE_SELECT; + mode[4] = 0x0d; + + if (s->mode & MUSTEK_MODE_COLOR) + { + if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) + mode[6] = 0xE0; + else + mode[6] = 0x60; + } + else if (s->mode & MUSTEK_MODE_GRAY) + { + if (s->val[OPT_FAST_GRAY_MODE].w) + mode[6] = 0x20; + else + mode[6] = 0x40; + } + else + mode[6] = 0x00; /* lineart */ + + mode[7] = 0; + mode[8] = 0; + mode[9] = 0; + mode[10] = 0; + mode[11] = 0x00; + mode[12] = 0x27; + mode[13] = 0xb0; + mode[14] = 0x04; + mode[15] = 0x43; + mode[16] = 0x41; + + cp = mode + 17; + STORE16L (cp, s->resolution_code); + + DBG (5, "mode_select_pro: resolution_code=%d (0x%x), mode=0x%x\n", + s->resolution_code, s->resolution_code, mode[6]); + return dev_cmd (s, mode, 6 + mode[4], 0, 0); +} + +/* Paragon and Pro series. According to Mustek, the only builtin gamma + table is a linear table, so all we support here is user-defined + gamma tables. */ +static SANE_Status +gamma_correction (Mustek_Scanner * s, SANE_Int color_code) +{ + SANE_Int i, j, table = 0, len = 0, bytes_per_channel, num_channels = 1; + SANE_Byte gamma[4096 + 10], val, *cp; /* for Paragon models 3 x 256 is the + maximum. Pro needs 4096 bytes */ + + if ((s->hw->flags & MUSTEK_FLAG_N) + && ((s->mode & MUSTEK_MODE_LINEART) + || (s->mode & MUSTEK_MODE_HALFTONE))) { /* sigh! - the 600 II N needs a (dummy) table download even for - lineart and halftone mode, else it produces a completely - white image. Thank Mustek for their buggy firmware ! */ + lineart and halftone mode, else it produces a completely + white image. Thank Mustek for their buggy firmware ! */ memset (gamma, 0, sizeof (gamma)); gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE; gamma[2] = 0x0; /* indicate any preloaded gamma table */ - DBG(3, "gamma_correction: sending dummy gamma table\n"); + DBG (5, "gamma_correction: sending dummy gamma table\n"); return dev_cmd (s, gamma, 6, 0, 0); } - if (!s->val[OPT_CUSTOM_GAMMA].w - || !(s->mode & MUSTEK_MODE_MULTIBIT)) - return SANE_STATUS_GOOD; + if (((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE)) + && !(s->hw->flags & MUSTEK_FLAG_PRO)) + { + DBG (5, "gamma_correction: nothing to do in lineart mode -- exiting\n"); + return SANE_STATUS_GOOD; + } + + if ((!s->val[OPT_CUSTOM_GAMMA].w) && (!(s->hw->flags & MUSTEK_FLAG_PRO))) + { + /* Do we need to upload a gamma table even if the user didn't select + this option? Some scanners need this work around. */ + if (!(s->hw->flags & MUSTEK_FLAG_FORCE_GAMMA) || + !((s->mode & MUSTEK_MODE_COLOR) || (s->mode & MUSTEK_MODE_GRAY))) + { + DBG (5, + "gamma_correction: no custom table selected -- exititing\n"); + return SANE_STATUS_GOOD; + } + } if (s->mode & MUSTEK_MODE_COLOR) { table = 1; - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + table += s->pass; + else { - if (color_code == MUSTEK_CODE_GRAY) + if ((color_code == MUSTEK_CODE_GRAY) + && !(s->hw->flags & MUSTEK_FLAG_PRO)) num_channels = 3; else table = color_code; } - else - table += s->pass; } - - if (s->mode & MUSTEK_MODE_MULTIBIT) - len = num_channels*256; - else - len = 0; + else if (s->hw->flags & MUSTEK_FLAG_N) + { + /* it seems 600 II N (firmware 1.x at least) wants 768 bytes in + * gray mode too */ + num_channels = 3; + } memset (gamma, 0, sizeof (gamma)); gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE; - gamma[2] = 0x27; /* indicate user-selected gamma table */ - gamma[7] = (len >> 8) & 0xff; /* big endian! */ - gamma[8] = (len >> 0) & 0xff; - gamma[9] = (color_code << 6); + + if (s->hw->flags & MUSTEK_FLAG_PRO) + { + bytes_per_channel = 4096; + len = bytes_per_channel; + if (s->mode == MUSTEK_MODE_COLOR) + { + gamma[9] = (color_code << 6); /* color */ + if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) + gamma[2] = 0x7f; /* medium brightness */ + } + else if (s->mode == MUSTEK_MODE_GRAY) + { + gamma[9] = 0x80; /* grayscale */ + if (s->val[OPT_FAST_GRAY_MODE].w) + gamma[2] = 0x7f; /* medium brightness */ + } + else /* lineart */ + { + gamma[2] = + 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0; + gamma[9] = 0x80; /* grayscale/lineart */ + DBG (5, "gamma_correction: sending brightness information\n"); + } + gamma[7] = (len >> 8) & 0xff; /* big endian! */ + gamma[8] = (len >> 0) & 0xff; + } + else + { + bytes_per_channel = 256; + len = num_channels * bytes_per_channel; + gamma[2] = 0x27; /* indicate user-selected gamma table */ + if (s->hw->flags & MUSTEK_FLAG_N) + { + /* 600 II N always uses 6-byte cdb */ + gamma[3] = (len >> 8) & 0xff; /* big endian! */ + gamma[4] = (len >> 0) & 0xff; + /* no way to pass color_code (?) */ + } + else + { + gamma[7] = (len >> 8) & 0xff; /* big endian! */ + gamma[8] = (len >> 0) & 0xff; + gamma[9] = (color_code << 6); + } + } + if (len > 0) { cp = gamma + 10; - for (j = 0; j < num_channels; ++j, ++table) - for (i = 0; i < 256; ++i) - { - val = s->gamma_table[table][i]; - if (s->mode & MUSTEK_MODE_COLOR) - /* compose intensity gamma and color channel gamma: */ - val = s->gamma_table[0][val]; - *cp++ = val; - } + for (j = 0; j < num_channels; ++j) + { + for (i = 0; i < bytes_per_channel; ++i) + { + if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) + val = s->gamma_table[table][i * 256 / bytes_per_channel]; + else + val = i * 256 / bytes_per_channel; + if ((s->mode & MUSTEK_MODE_COLOR) + && (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)) + /* compose intensity gamma and color channel gamma: */ + val = s->gamma_table[0][val]; + *cp++ = val; + } + if (!(s->hw->flags & MUSTEK_FLAG_N) + || !(s->mode & MUSTEK_MODE_GRAY)) + table++; + } } - DBG(3, "gamma_correction: sending gamma table of %d bytes\n", len); + DBG (5, "gamma_correction: sending gamma table of %d bytes\n", len); return dev_cmd (s, gamma, 10 + len, 0, 0); } @@ -1351,9 +2717,9 @@ send_gamma_table (Mustek_Scanner * s) if (s->one_pass_color_scan) { - if (s->hw->flags & MUSTEK_FLAG_PP) + if (s->hw->flags & MUSTEK_FLAG_N) /* This _should_ work for all one-pass scanners (not just - parallel-port scanners), but it doesn't work for my Paragon + AB306N scanners), but it doesn't work for my Paragon 600 II SP with firmware rev 1.01. Too bad, since it would simplify the gamma correction code quite a bit. */ status = gamma_correction (s, MUSTEK_CODE_GRAY); @@ -1375,91 +2741,147 @@ send_gamma_table (Mustek_Scanner * s) return status; } + +/* ScanExpress and Paragon series */ static SANE_Status -start_scan (Mustek_Scanner *s) +start_scan (Mustek_Scanner * s) { - u_int8_t start[6]; + SANE_Byte start[6]; + SANE_Status status; memset (start, 0, sizeof (start)); start[0] = MUSTEK_SCSI_START_STOP; start[4] = 0x01; - /* ScanExpress models don't have any variants */ - if (! (s->hw->flags & MUSTEK_FLAG_SE) ) + DBG (4, "start_scan\n"); + /* ScanExpress and Pro models don't have any variants */ + if (!(s->hw->flags & MUSTEK_FLAG_SE) && !(s->hw->flags & MUSTEK_FLAG_PRO)) { if (s->mode & MUSTEK_MODE_COLOR) - { - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - start[4] |= 0x20; - else + { + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) start[4] |= ((s->pass + 1) << 3); - } + else + start[4] |= 0x20; + } /* or in single/multi bit: */ - start[4] |= (s->mode & MUSTEK_MODE_MULTIBIT) ? (1 << 6) : 0; + start[4] |= ((s->mode & MUSTEK_MODE_LINEART) + || (s->mode & MUSTEK_MODE_HALFTONE)) ? 0 : (1 << 6); - if (!(s->hw->flags & MUSTEK_FLAG_SINGLE_PASS)) - /* or in expanded resolution bit: */ - start[4] |= (s->resolution_code & 0x100) >> 1; + /* or in expanded resolution bit: */ + if (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2) + && ((s->hw->flags & MUSTEK_FLAG_THREE_PASS) + || (s->hw->flags & MUSTEK_FLAG_PARAGON_1) + || (s->hw->flags & MUSTEK_FLAG_PARAGON_2))) + start[4] |= 1 << 7; + + /* block mode (or whatever) */ + if (s->hw->flags & MUSTEK_FLAG_USE_BLOCK) + { + start[5] = 0x08; + DBG (4, "start_scan: using block mode\n"); + } } - - return dev_cmd (s, start, sizeof (start), 0, 0); + + status = dev_cmd (s, start, sizeof (start), 0, 0); + if (status != SANE_STATUS_GOOD) + DBG (1, "start_scan returned status %s\n", sane_strstatus (status)); + return status; } static SANE_Status -stop_scan (Mustek_Scanner *s) -{ - return dev_cmd (s, stop, sizeof (stop), 0, 0); -} - -static SANE_Status -do_eof (Mustek_Scanner *s) +do_eof (Mustek_Scanner * s) { if (s->pipe >= 0) { close (s->pipe); s->pipe = -1; + DBG (5, "do_eof: closing pipe\n"); } return SANE_STATUS_EOF; } static SANE_Status -do_stop (Mustek_Scanner *s) +do_stop (Mustek_Scanner * s) { SANE_Status status = SANE_STATUS_GOOD; - if (!s->scanning) + DBG (5, "do_stop\n"); + + if (s->cancelled) status = SANE_STATUS_CANCELLED; s->scanning = SANE_FALSE; s->pass = 0; - do_eof (s); - if (s->reader_pid > 0) { - int exit_status; + SANE_Int exit_status; + struct timeval now; + long int scan_time; + long int scan_size; + pid_t pid; + + /* print scanning time */ + gettimeofday (&now, 0); + scan_time = now.tv_sec - s->start_time; + if (scan_time < 1) + scan_time = 1; + scan_size = s->hw->bpl * s->hw->lines / 1024; + DBG (2, "Scanning time was %ld seconds, %ld kB/s\n", scan_time, + scan_size / scan_time); + + if (s->total_bytes == s->params.lines * s->params.bytes_per_line) + DBG (3, "Scanned %d bytes as expected\n", s->total_bytes); + else if (s->total_bytes < s->params.lines * s->params.bytes_per_line) + DBG (3, "Scanned %d bytes, expected %d bytes\n", s->total_bytes, + s->params.lines * s->params.bytes_per_line); + else + DBG (1, "Warning: Scanned %d bytes, but expected only %d bytes\n", + s->total_bytes, s->params.lines * s->params.bytes_per_line); /* ensure child knows it's time to stop: */ - DBG(4, "do_stop: terminating reader process\n"); + DBG (5, "do_stop: terminating reader process\n"); kill (s->reader_pid, SIGTERM); - while (wait (&exit_status) != s->reader_pid); - DBG(4, "do_stop: reader process terminated with status 0x%x\n", - exit_status); - if (status != SANE_STATUS_CANCELLED && WIFEXITED(exit_status)) - status = WEXITSTATUS(exit_status); + pid = waitpid (s->reader_pid, &exit_status, 0); + DBG (5, "do_stop: reader process terminated: %s\n", + sane_strstatus (status)); + if (pid <= 0) + DBG (5, "do_stop: reader process already terminated (%s)\n", + strerror (errno)); + if (status != SANE_STATUS_CANCELLED && pid > 0 + && WIFEXITED (exit_status)) + status = WEXITSTATUS (exit_status); s->reader_pid = 0; } if (s->fd >= 0) { - if (status == SANE_STATUS_CANCELLED) + if (s->hw->flags & MUSTEK_FLAG_PRO) { - DBG(4, "do_stop: waiting for scanner to become ready\n"); + if (s->total_bytes < s->params.lines * s->params.bytes_per_line) + status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), + 0, 0); dev_wait_ready (s); } - DBG(4, "do_stop: sending STOP command\n"); - stop_scan (s); - DBG(4, "do_stop: closing scanner\n"); + else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1) + || (s->hw->flags & MUSTEK_FLAG_PARAGON_2) + || (s->hw->flags & MUSTEK_FLAG_THREE_PASS)) + { + if (s->cancelled && + (s->total_bytes < s->params.lines * s->params.bytes_per_line)) + status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), + 0, 0); + } + else + status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), 0, 0); + + if (force_wait) + { + DBG (5, "do_stop: waiting for scanner to be ready\n"); + dev_wait_ready (s); + } + DBG (5, "do_stop: closing scanner\n"); dev_close (s); s->fd = -1; } @@ -1467,36 +2889,46 @@ do_stop (Mustek_Scanner *s) return status; } -/* Determine the CCD's distance between the primary color lines. */ -static SANE_Status -line_distance (Mustek_Scanner *s) +#ifdef HAVE_OS2_H +/* + * reader thread for OS/2: need a wrapper, because threads can have + * only one parameter. +*/ +static void +os2_reader_process (void *data) { - int factor, color, res, peak_res; + struct Mustek_Scanner *scanner = (struct Mustek_Scanner *) data; + + DBG (1, "reader_process thread started\n"); + reader_process (scanner, scanner->reader_fds); +} +#endif + +/* Paragon I + II: Determine the CCD's distance between the primary color + lines. */ +static SANE_Status +line_distance (Mustek_Scanner * s) +{ + SANE_Int factor, color, res, peak_res; SANE_Status status; - u_int8_t result[5]; + SANE_Byte result[5]; size_t len; + memset (result, 0, 5); + res = SANE_UNFIX (s->val[OPT_RESOLUTION].w) + 0.5; peak_res = SANE_UNFIX (s->hw->dpi_range.max) + 0.5; s->ld.buf[0] = NULL; - - if (s->hw->flags & MUSTEK_FLAG_LD_MFS) - { - /* At least the MFS12000SP scanner needs a special form of - line-distance correction and goes wild if they receive an LD - command. */ - s->ld.peak_res = res; - return SANE_STATUS_GOOD; - } len = sizeof (result); - status = dev_cmd (s, distance, sizeof (distance), result, &len); + status = dev_cmd (s, scsi_ccd_distance, sizeof (scsi_ccd_distance), + result, &len); if (status != SANE_STATUS_GOOD) return status; - DBG(1, "line_distance: got factor=%d, (r/g/b)=(%d/%d/%d)\n", - result[0] | (result[1] << 8), result[2], result[3], result[4]); + DBG (3, "line_distance: got factor=%d, (r/g/b)=(%d/%d/%d)\n", + result[0] | (result[1] << 8), result[2], result[3], result[4]); if (s->hw->flags & MUSTEK_FLAG_LD_FIX) { @@ -1504,41 +2936,55 @@ line_distance (Mustek_Scanner *s) result[1] = 0xff; if (s->mode & MUSTEK_MODE_COLOR) { - if (s->hw->flags & MUSTEK_FLAG_PP) + if (s->hw->flags & MUSTEK_FLAG_N) { - /* According to Andreas Czechanowski, the line-distance - values returned for the parallel-port scanners are - garbage, so we have to fix things up manually. Not - good. */ + /* According to Andreas Czechanowski, the line-distance values + returned for the AB306N scanners are garbage, so we have to + fix things up manually. Not good. + This seems to be true only for firmware 2.00 which is + extremely seldom.. AB306N scanners with firmware 1.01 don't + need this fix. */ if (peak_res == 600) { if (res < 51) { - result[0] = 8; result[1] = 0; - result[2] = 0; result[3] = 2; result[4] = 3; + result[0] = 8; + result[1] = 0; + result[2] = 0; + result[3] = 2; + result[4] = 3; } else if (res < 75 || (res > 90 && res < 150)) { /* 51-74 and 91-149 dpi: */ - result[0] = 4; result[1] = 0; - result[2] = 0; result[3] = 3; result[4] = 5; + result[0] = 4; + result[1] = 0; + result[2] = 0; + result[3] = 3; + result[4] = 5; } else if (res <= 90 || (res >= 150 && res <= 300)) { /* 75-90 and 150-300 dpi: */ - result[0] = 2; result[1] = 0; - result[2] = 0; result[3] = 5; result[4] = 9; + result[0] = 2; + result[1] = 0; + result[2] = 0; + result[3] = 5; + result[4] = 9; } else { /* 301-600 dpi: */ - result[0] = 1; result[1] = 0; - result[2] = 0; result[3] = 9; result[4] = 23; + result[0] = 1; + result[1] = 0; + result[2] = 0; + result[3] = 9; + result[4] = 23; } } else - DBG(1, "don't know how to fix up line-distance for %d dpi\n", - peak_res); + DBG (1, "don't know how to fix up line-distance for %d dpi\n", + peak_res); } else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE)) { @@ -1547,20 +2993,29 @@ line_distance (Mustek_Scanner *s) if (res < 51) { /* 1-50 dpi: */ - result[0] = 4; result[1] = 0; - result[2] = 0; result[3] = 3; result[4] = 5; + result[0] = 4; + result[1] = 0; + result[2] = 0; + result[3] = 3; + result[4] = 5; } else if (res <= 300) { /* 51-300 dpi: */ - result[0] = 2; result[1] = 0; - result[2] = 0; result[3] = 5; result[4] = 9; + result[0] = 2; + result[1] = 0; + result[2] = 0; + result[3] = 5; + result[4] = 9; } else { /*301-600 dpi: */ - result[0] = 1; result[1] = 0; - result[2] = 0; result[3] = 9; result[4] = 17; + result[0] = 1; + result[1] = 0; + result[2] = 0; + result[3] = 9; + result[4] = 17; } } else if (peak_res == 800) @@ -1568,26 +3023,35 @@ line_distance (Mustek_Scanner *s) if (res < 72) { /* 1-71 dpi: */ - result[0] = 4; result[1] = 0; - result[2] = 0; result[3] = 3; result[4] = 5; + result[0] = 4; + result[1] = 0; + result[2] = 0; + result[3] = 3; + result[4] = 5; } else if (res <= 400) { /* 72-400 dpi: */ - result[0] = 2; result[1] = 0; - result[2] = 0; result[3] = 9; result[4] = 17; + result[0] = 2; + result[1] = 0; + result[2] = 0; + result[3] = 9; + result[4] = 17; } else { /*401-800 dpi: */ - result[0] = 1; result[1] = 0; - result[2] = 0; result[3] = 16; result[4] = 32; + result[0] = 1; + result[1] = 0; + result[2] = 0; + result[3] = 16; + result[4] = 32; } } } } - DBG(1, "line_distance: fixed up to factor=%d, (r/g/b)=(%d/%d/%d)\n", - result[0] | (result[1] << 8), result[2], result[3], result[4]); + DBG (4, "line_distance: fixed up to factor=%d, (r/g/b)=(%d/%d/%d)\n", + result[0] | (result[1] << 8), result[2], result[3], result[4]); } factor = result[0] | (result[1] << 8); @@ -1609,30 +3073,48 @@ line_distance (Mustek_Scanner *s) { s->ld.dist[color] = result[2 + color]; s->ld.quant[color] = s->ld.max_value; + s->ld.index[color] = -s->ld.dist[color]; } + s->ld.lmod3 = -1; - if (s->hw->flags & MUSTEK_FLAG_PP) - { - for (color = 0; color < 3; ++color) - s->ld.index[color] = -s->ld.dist[color]; - s->ld.lmod3 = -1; - } + DBG (4, "line_distance: max_value = %d, peak_res = %d, ld.quant = " + "(%d, %d, %d)\n", s->ld.max_value, s->ld.peak_res, s->ld.quant[0], + s->ld.quant[1], s->ld.quant[2]); } + else + s->ld.max_value = 0; + return SANE_STATUS_GOOD; } +/* Paragon + Pro series */ static SANE_Status -get_image_status (Mustek_Scanner *s, SANE_Int *bpl, SANE_Int *lines) +get_image_status (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines) { - u_int8_t result[6]; + SANE_Byte result[6]; SANE_Status status; size_t len; - int busy; + SANE_Int busy, offset; + long res, half_res; + + memset (result, 0, 6); + + /* The 600 II N v1.01 and Paragon 12000SP need a larger scan-area for + line-distance correction in color mode */ + offset = 0; + if ((s->hw->flags & MUSTEK_FLAG_LD_N1) && (s->mode & MUSTEK_MODE_COLOR)) + offset = s->ld.dist[1]; + else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK) + && (s->hw->flags & MUSTEK_FLAG_PARAGON_1) + && (s->mode & MUSTEK_MODE_COLOR)) + offset = MAX_LINE_DIST * SANE_UNFIX (s->val[OPT_RESOLUTION].w) + / SANE_UNFIX (s->hw->dpi_range.max); do { len = sizeof (result); - status = dev_cmd (s, get_status, sizeof (get_status), result, &len); + status = dev_cmd (s, scsi_get_image_status, + sizeof (scsi_get_image_status), result, &len); if (status != SANE_STATUS_GOOD) return status; @@ -1640,32 +3122,55 @@ get_image_status (Mustek_Scanner *s, SANE_Int *bpl, SANE_Int *lines) if (busy) usleep (100000); - if (!s->scanning) - return do_stop (s); + if (!s->scanning) /* ? */ + if (!(s->hw->flags & MUSTEK_FLAG_PRO)) + return do_stop (s); } while (busy); s->hw->bpl = result[1] | (result[2] << 8); s->hw->lines = result[3] | (result[4] << 8) | (result[5] << 16); - *bpl = s->hw->bpl; - *lines = s->hw->lines; - DBG(2, "get_image_status: bytes_per_line=%d, lines=%d\n", - *bpl, *lines); + res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); + half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; + /* Need to interpolate resolutions > max x-resolution? */ + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) + { + *bpl = (s->hw->bpl * res) / half_res / 3; + *bpl *= 3; + DBG (4, "get_image_status: resolution > x-max; enlarge %d bpl to " + "%d bpl\n", s->hw->bpl, *bpl); + } + else + *bpl = s->hw->bpl; + + *lines = s->hw->lines - offset; + + DBG (3, "get_image_status: bytes_per_line=%d, lines=%d (offset = %d)\n", + *bpl, *lines, offset); return SANE_STATUS_GOOD; } +/* ScanExpress models */ static SANE_Status -do_get_window (Mustek_Scanner *s, SANE_Int *bpl, SANE_Int *lines, - SANE_Int *pixels) +get_window (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines, + SANE_Int * pixels) { - u_int8_t result[48]; + SANE_Byte result[48]; SANE_Status status; size_t len; - int color; + SANE_Int color; + long res, half_res; + + res = s->resolution_code; + half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; + + DBG (5, "get_window: resolution: %ld dpi (hardware: %d dpi)\n", + res, s->ld.peak_res); len = sizeof (result); - status = dev_cmd (s, get_window, sizeof (get_window), result, &len); + status = dev_cmd (s, scsi_get_window, sizeof (scsi_get_window), result, + &len); if (status != SANE_STATUS_GOOD) return status; @@ -1673,202 +3178,118 @@ do_get_window (Mustek_Scanner *s, SANE_Int *bpl, SANE_Int *lines, return do_stop (s); s->hw->cal.bytes = (result[6] << 24) | (result[7] << 16) | - (result[8] << 8) | (result[9] << 0); + (result[8] << 8) | (result[9] << 0); s->hw->cal.lines = (result[10] << 24) | (result[11] << 16) | - (result[12] << 8) | (result[13] << 0); + (result[12] << 8) | (result[13] << 0); - DBG(3, "get_window: calib_bytes=%d, calib_lines=%d\n", - s->hw->cal.bytes, s->hw->cal.lines); + DBG (4, "get_window: calibration bpl=%d, lines=%d\n", + s->hw->cal.bytes, s->hw->cal.lines); s->hw->bpl = (result[14] << 24) | (result[15] << 16) | - (result[16] << 8) | result[17]; - s->hw->lines = (result[18] << 24) | (result[19] << 16) | - (result[20] << 8) | result[21]; + (result[16] << 8) | result[17]; - DBG(2, "get_window: bytes_per_line=%d, lines=%d\n", - s->hw->bpl, s->hw->lines); - - s->hw->gamma_length = 1 << result[26]; - DBG(2, "get_window: gamma length=%d\n", s->hw->gamma_length); + s->hw->lines = (result[18] << 24) | (result[19] << 16) | + (result[20] << 8) | result[21]; - if (s->mode & MUSTEK_MODE_COLOR) + DBG (4, "get_window: scan bpl=%d, lines=%d\n", s->hw->bpl, s->hw->lines); + + if ((s->hw->cal.bytes == 0) || (s->hw->cal.lines == 0) + || (s->hw->bpl == 0) || (s->hw->lines == 0)) { - long res; + DBG (1, "get_window: oops, none of these values should be 0 " + "-- exiting\n"); + return SANE_STATUS_INVAL; + } + s->hw->gamma_length = 1 << result[26]; + DBG (4, "get_window: gamma length=%d\n", s->hw->gamma_length); + + if (s->mode & MUSTEK_MODE_COLOR) + { s->ld.buf[0] = NULL; for (color = 0; color < 3; ++color) { s->ld.dist[color] = result[42 + color]; } - - DBG(1, "line_distance: got res=%d, (r/g/b)=(%d/%d/%d)\n", - (result[40] << 8) | result[41], s->ld.dist[0], - s->ld.dist[1], s->ld.dist[2]); - /* In color mode scale the image according to desired resolution */ - - res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); - *bpl = *pixels = (((s->hw->bpl / 3 ) * res) / s->ld.peak_res) * 3; - *lines = ((s->hw->lines - s->ld.dist[2]) * res) / s->ld.peak_res; + DBG (4, "get_window: LD res=%d, (r/g/b)=(%d/%d/%d)\n", + (result[40] << 8) | result[41], s->ld.dist[0], + s->ld.dist[1], s->ld.dist[2]); + s->ld.max_value = (result[40] << 8) | result[41]; + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) + { + /* We must interpolate resolutions > max x-resolution */ + *bpl = *pixels = (((s->hw->bpl / 3) * res) / half_res) * 3; + } + else + { + /* Scale down the image according to desired resolution */ + *bpl = *pixels = (((s->hw->bpl / 3) * res) / s->ld.peak_res) * 3; + } + *lines = (s->hw->lines - s->ld.dist[2]) * res / s->ld.peak_res; } else { - /* Linart and gray seems to work with arbitrary resolution */ - *bpl = s->hw->bpl; + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) + { + /* We must interpolate resolutions > max x-resolution */ + *bpl = s->hw->bpl * res / half_res; + } + else + { + *bpl = s->hw->bpl; + } *lines = s->hw->lines; - } - - DBG(2, "get_window: bytes_per_line=%d, lines=%d, pixels=%d\n", - *bpl, *lines, *pixels); - + } + DBG (4, "get_window: bpl = %d (hardware: %d), lines = %d (hardware: %d)\n", + *bpl, s->hw->bpl, *lines, s->hw->lines); return SANE_STATUS_GOOD; } static SANE_Status -backtrack_and_adf (Mustek_Scanner *s) +adf_and_backtrack (Mustek_Scanner * s) { - u_int8_t backtrack[6]; - int code = 0x80; + SANE_Byte backtrack[6]; + SANE_Int code = 0x80; - if (s->val[OPT_BACKTRACK].w) - code |= 0x02; + if (!(s->hw->flags & MUSTEK_FLAG_NO_BACKTRACK)) + code |= 0x02; /* enable backtracking */ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) code |= 0x01; else if (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) code |= 0x04; - memset (backtrack, 0, sizeof (backtrack)); backtrack[0] = MUSTEK_SCSI_ADF_AND_BACKTRACK; backtrack[4] = code; + DBG (4, "adf_and_backtrack: backtrack: %s; ADF: %s; TA: %s\n", + code & 0x02 ? "yes" : "no", code & 0x01 ? "yes" : "no", + code & 0x04 ? "yes" : "no"); return dev_cmd (s, backtrack, sizeof (backtrack), 0, 0); } -/* MFS scanner have three separate sensor bars (one per primary color) - and these sensor bars are vertically 1/72" apart from each other. - So when scanning at a resolution of RES dots/inch, then the first - red strip goes with the green strip that is dy=round(RES/72) - further down and the blue strip that is 2*dy further down. */ -static void -fix_line_distance_mfs (Mustek_Scanner * s, int num_lines, int bpl, - u_int8_t *raw, u_int8_t *out) +/* 600 II N firmware 2.x */ +static SANE_Int +fix_line_distance_n_2 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, + SANE_Byte * raw, SANE_Byte * out) { - u_int8_t *out_ptr, *ptr, *ptr_end, *src; - u_int y, dy; - int bpc; -# define RED 0 -# define GRN 1 /* green, spelled funny */ - - bpc = bpl / 3; - dy = (s->ld.peak_res + 36) / 72; - - if (!s->ld.buf[RED]) - { - /* The red buffer must be able to hold up to 2*dy lines whereas - the green buffer must be able to hold up to 1*dy lines. */ - s->ld.buf[RED] = malloc (3 * dy * (long) bpc); - s->ld.buf[GRN] = s->ld.buf[RED] + 2 * dy * bpc; - } - - /* restore the red and green lines from the previous buffer: */ - for (y = 0; y < 2*dy && y < num_lines; ++y) - { - ptr = s->ld.buf[RED] + y*bpc; - ptr_end = ptr + bpc; - out_ptr = out + y*bpl + 0; - while (ptr != ptr_end) - { - *out_ptr = *ptr++; - out_ptr += 3; - } - } - for (y = 0; y < dy && y < num_lines; ++y) - { - ptr = s->ld.buf[GRN] + y*bpc; - ptr_end = ptr + bpc; - out_ptr = out + y*bpl + 1; - while (ptr != ptr_end) - { - *out_ptr = *ptr++; - out_ptr += 3; - } - } - - for (y = 0; y < num_lines; ++y) - { - if (y >= 2*dy) - { - ptr = raw + (y - 2*dy)*bpl + 0*bpc; - ptr_end = ptr + bpc; - out_ptr = out + y*bpl + 0; - while (ptr != ptr_end) - { - *out_ptr = *ptr++; - out_ptr += 3; - } - } - if (y >= 1*dy) - { - ptr = raw + (y - 1*dy)*bpl + 1*bpc; - ptr_end = ptr + bpc; - out_ptr = out + y*bpl + 1; - while (ptr != ptr_end) - { - *out_ptr = *ptr++; - out_ptr += 3; - } - } - if (y >= 0*dy) - { - ptr = raw + (y - 0*dy)*bpl + 2*bpc; - ptr_end = ptr + bpc; - out_ptr = out + y*bpl + 2; - while (ptr != ptr_end) - { - *out_ptr = *ptr++; - out_ptr += 3; - } - } - } - - /* save red and green lines: */ - for (y = 0; y < 2*dy; ++y) - { - if (num_lines - 2*dy + y >= 0) - src = raw + (num_lines - 2*dy + y)*bpl; - else - src = s->ld.buf[RED] + (y + num_lines)*bpc; - memcpy (s->ld.buf[RED] + y*bpc, src, bpc); - } - for (y = 0; y < 1*dy; ++y) - { - if (num_lines - 1*dy + y >= 0) - src = raw + (num_lines - 1*dy + y)*bpl + bpc; - else - src = s->ld.buf[GRN] + (y + num_lines)*bpc; - memcpy (s->ld.buf[GRN] + y*bpc, src, bpc); - } -} - -static int -fix_line_distance_pp (Mustek_Scanner *s, int num_lines, int bpl, - u_int8_t *raw, u_int8_t *out) -{ - u_int8_t *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; - int c, num_saved_lines, line; + SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; + SANE_Int c, num_saved_lines, line; if (!s->ld.buf[0]) { /* This buffer must be big enough to hold maximum line distance - times max_bpl bytes. The maximum line distance for 600 dpi - parallel-port scanners is 23. We use 32 to play it safe... */ - DBG(2, "fix_line_distance_pp: allocating temp buffer of %d*%d bytes\n", - 32, bpl); - s->ld.buf[0] = malloc (32 * (long) bpl); + times max_bpl bytes. The maximum line distance for the + Paragon 600 II N scanner is 23, so 40 should be safe. */ + DBG (5, + "fix_line_distance_n_2: allocating temp buffer of %d*%d bytes\n", + MAX_LINE_DIST, bpl); + s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl); if (!s->ld.buf[0]) { - DBG(1, "fix_line_distance_pp: failed to malloc temporary buffer\n"); + DBG (1, + "fix_line_distance_n_2: failed to malloc temporary buffer\n"); return 0; } } @@ -1899,12 +3320,12 @@ fix_line_distance_pp (Mustek_Scanner *s, int num_lines, int bpl, { *out_ptr = *raw++; out_ptr += 3; - } + } if (raw >= raw_end) { - DBG (1, "fix_line_distance_pp: lmod3=%d, index=(%d,%d,%d)\n", - s->ld.lmod3, + DBG (3, "fix_line_distance_n_2: lmod3=%d, " + "index=(%d,%d,%d)\n", s->ld.lmod3, s->ld.index[0], s->ld.index[1], s->ld.index[2]); num_lines = s->ld.index[2] - s->ld.ld_line; @@ -1925,64 +3346,176 @@ fix_line_distance_pp (Mustek_Scanner *s, int num_lines, int bpl, } } -static int -fix_line_distance_se (Mustek_Scanner *s, int num_lines, int bpl, - u_int8_t *raw, u_int8_t *out) +/* 600 II N firmware 1.x */ +static SANE_Int +fix_line_distance_n_1 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, + SANE_Byte * raw, SANE_Byte * out) { - u_int8_t *raw_end = raw + num_lines * bpl; - u_int8_t *out_ptr[3], *ptr; - int index[3], lines[3], quant[3]; - int color, pixel, res, scale; - int bpc = bpl / 3; /* bytes per color (per line) */ + SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; + SANE_Int c, num_saved_lines, line; + + /* For firmware 1.x the scanarea must be soemwhat bigger than needed + because of the linedistance correction */ + + if (!s->ld.buf[0]) + { + /* This buffer must be big enough to hold maximum line distance + times max_bpl bytes. The maximum line distance for the 600 II N + is 23, so 40 is safe. */ + DBG (5, + "fix_line_distance_n_1: allocating temp buffer of %d*%d bytes\n", + MAX_LINE_DIST, bpl); + s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl); + if (!s->ld.buf[0]) + { + DBG (1, + "fix_line_distance_n_1: failed to malloc temporary buffer\n"); + return 0; + } + } + num_saved_lines = s->ld.index[0] - s->ld.index[1]; + DBG (5, "fix_line_distance_n_1: got %d lines, %d bpl\n", num_lines, bpl); + DBG (5, "fix_line_distance_n_1: num_saved_lines = %d; peak_res = %d; " + "max_value = %d\n", num_saved_lines, s->ld.peak_res, s->ld.max_value); + if (num_saved_lines > 0) + /* restore the previously saved lines: */ + memcpy (out, s->ld.buf[0], num_saved_lines * bpl); + + while (1) + { + if (++s->ld.lmod3 >= 3) + s->ld.lmod3 = 0; + c = s->ld.lmod3; + if (s->ld.index[c] < 0) + ++s->ld.index[c]; + else + { + s->ld.quant[c] += s->ld.peak_res; + if (s->ld.quant[c] > s->ld.max_value) + { + s->ld.quant[c] -= s->ld.max_value; + line = s->ld.index[c]++ - s->ld.ld_line; + out_ptr = out + line * bpl + c; + out_end = out_ptr + bpl; + while (out_ptr != out_end) + { + *out_ptr = *raw++; + out_ptr += 3; + } + DBG (5, "fix_line_distance_n_1: copied line %d (color %d)\n", + line, c); + } + } + if ((raw >= raw_end) || ((s->ld.index[0] >= s->params.lines) && + (s->ld.index[1] >= s->params.lines) && + (s->ld.index[2] >= s->params.lines))) + { + DBG (3, "fix_line_distance_n_1: lmod3=%d, index=(%d,%d,%d)%s\n", + s->ld.lmod3, + s->ld.index[0], s->ld.index[1], s->ld.index[2], + raw >= raw_end ? " raw >= raw_end" : ""); + num_lines = s->ld.index[1] - s->ld.ld_line; + if (num_lines < 0) + num_lines = 0; + DBG (4, "fix_line_distance_n_1: lines ready: %d\n", num_lines); + + /* copy away the lines with at least one missing + color component, so that we can interleave them + with new scan data on the next call */ + num_saved_lines = s->ld.index[0] - s->ld.index[1]; + DBG (4, "fix_line_distance_n_1: copied %d lines to " + "ld.buf\n", num_saved_lines); + memcpy (s->ld.buf[0], out + num_lines * bpl, num_saved_lines * bpl); + /* notice the number of lines we processed */ + s->ld.ld_line = s->ld.index[1]; + if (s->ld.ld_line < 0) + s->ld.ld_line = 0; + /* return number of complete (r+g+b) lines */ + return num_lines; + } + + } +} + +/* For ScanExpress models */ +static SANE_Int +fix_line_distance_se (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, + SANE_Byte * raw, SANE_Byte * out) +{ + SANE_Byte *raw_end = raw + num_lines * bpl; + SANE_Byte *out_ptr[3], *ptr; + SANE_Int index[3], lines[3], quant[3], dist[3]; + SANE_Int max_value; + SANE_Int color, pixel, res, half_res, scale; + SANE_Int bpc = bpl / 3; /* bytes per color (per line) */ + SANE_Bool preview = SANE_FALSE; + + res = s->resolution_code; + half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; + max_value = s->ld.max_value; + if ((s->val[OPT_PREVIEW].w == SANE_TRUE) + && (s->val[OPT_FAST_PREVIEW].w == SANE_TRUE)) + { + preview = SANE_TRUE; + /*max_value = 75; */ + dist[0] = s->ld.dist[0]; + dist[1] = s->ld.dist[1]; + dist[2] = s->ld.dist[2]; + } + else + { + dist[0] = s->ld.dist[0]; + dist[1] = s->ld.dist[1]; + dist[2] = s->ld.dist[2]; + } - res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); - if (!s->ld.buf[0]) { /* This buffer must be big enough to hold maximum line distance times 3*bpl bytes. The maximum line distance for 1200 dpi is 32 */ - DBG(2, "fix_line_distance_se: allocating temp buffer of %d*%d bytes\n", - 3 * MAX_LINE_DIST, bpc); + DBG (5, "fix_line_distance_se: allocating temp buffer of %d*%d bytes\n", + 3 * MAX_LINE_DIST, bpc); s->ld.buf[0] = malloc (3 * MAX_LINE_DIST * (long) bpc); if (!s->ld.buf[0]) { - DBG(1, "fix_line_distance_se: failed to malloc temporary buffer\n"); + DBG (1, + "fix_line_distance_se: failed to malloc temporary buffer\n"); return 0; } - /* Note that either s->ld.buf[1] or s->ld.buf[2] is never user. */ - s->ld.buf[1] = s->ld.buf[2] = - s->ld.buf[0] + 2 * MAX_LINE_DIST * (long) bpc; + /* Note that either s->ld.buf[1] or s->ld.buf[2] is never used. */ + s->ld.buf[1] = s->ld.buf[2] = + s->ld.buf[0] + 2 * MAX_LINE_DIST * (long) bpc; /* Since the blocks don't start necessarily with red note color. */ s->ld.color = 0; - + /* The scan area must be longer than desired because of the line distance. So me must count complete (r+g+b) lines already submitted to the fronted. */ s->ld.ld_line = s->params.lines; - + for (color = 0; color < 3; ++color) { - s->ld.index[color] = - s->ld.dist[color]; + s->ld.index[color] = -dist[color]; s->ld.quant[color] = 0; s->ld.saved[color] = 0; } } - + num_lines *= 3; - DBG(4, "start color: %d\n", s->ld.color); - DBG(4, "read lines: %d\n", num_lines); + DBG (5, "fix_line_distance_se: start color: %d; %d lines \n", + s->ld.color, num_lines); /* First scan the lines read and count red, green and blue ones. - Since we will step thru the lines a second time we must not + Since we will step through the lines a second time we must not alter any global variables here! */ for (color = 0; color < 3; ++color) { index[color] = s->ld.index[color]; lines[color] = s->ld.saved[color]; - quant[color] = s->ld.quant[color]; + quant[color] = s->ld.quant[color]; } color = s->ld.color; @@ -1993,56 +3526,97 @@ fix_line_distance_se (Mustek_Scanner *s, int num_lines, int bpl, else { quant[color] += res; - if (quant[color] >= s->ld.peak_res) + if (quant[color] >= max_value) { /* This line must be processed, not dropped. */ - quant[color] -= s->ld.peak_res; + quant[color] -= max_value; ++lines[color]; - } - --num_lines; + --num_lines; + } + else if (!preview) + --num_lines; + } - if (++color > 2) color = 0; - } - + if (++color > 2) + color = 0; + } + /* Calculate how many triples of color lines we can output now. Because the number of available red lines is always greater than for the other colors we may ignore the red ones here. */ - num_lines = MIN( lines[1], lines[2]); + num_lines = MIN (lines[1], lines[2]); - DBG(4, "saved lines: %d/%d/%d\n", s->ld.saved[0], s->ld.saved[1], - s->ld.saved[2]); - DBG(4, "available: %d/%d/%d\n", lines[0], lines[1], lines[2]); - DBG(4, "triples: %d\n", num_lines); + DBG (5, "fix_line_distance_se: saved lines: %d/%d/%d\n", s->ld.saved[0], + s->ld.saved[1], s->ld.saved[2]); + DBG (5, "fix_line_distance_se: available: %d/%d/%d --> triples: %d\n", + lines[0], lines[1], lines[2], num_lines); lines[0] = lines[1] = lines[2] = num_lines; - + /* Output the color lines saved in previous call first. Note that data is converted in r/g/b interleave on the fly. */ - for (color = 0; color < 3; ++color) + for (color = 0; color < 3; ++color) { out_ptr[color] = out + color; ptr = s->ld.buf[color]; - while ((s->ld.saved[color] > 0) && (lines[color] > 0)) + while ((s->ld.saved[color] > 0) && (lines[color] > 0)) { scale = 0; - for (pixel = 0; pixel < bpc; ++pixel) + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { - scale += res; - if (scale >= s->ld.peak_res) + /* Need to enlarge x-resolution */ + SANE_Byte *ptr_start = ptr; + for (pixel = 0; pixel < s->params.pixels_per_line; ++pixel) { - scale -= s->ld.peak_res; *out_ptr[color] = *ptr; out_ptr[color] += 3; + scale += half_res; + if (scale >= half_res) + { + scale -= res; + ++ptr; + } } - ++ptr; + DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; " + "color: %d; raw bytes: %d; out bytes: %d\n", + s->ld.saved[color], lines[color], color, ptr - ptr_start, + s->params.pixels_per_line); + ptr = ptr_start + bpc; + } + else + { + if (preview) + { + for (pixel = 0; pixel < bpc; ++pixel) + { + *out_ptr[color] = *ptr++; + out_ptr[color] += 3; + } + } + else + { + for (pixel = 0; pixel < bpc; ++pixel) + { + scale += res; + if (scale >= max_value) + { + scale -= max_value; + *out_ptr[color] = *ptr; + out_ptr[color] += 3; + } + ++ptr; + } + } + DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; " + "color: %d\n", s->ld.saved[color], lines[color], color); } --(s->ld.saved[color]); --lines[color]; - } - if (s->ld.saved[color] > 0) - memmove(s->ld.buf[color], ptr, s->ld.saved[color] * bpc); - } - + } + if (s->ld.saved[color] > 0) + memmove (s->ld.buf[color], ptr, s->ld.saved[color] * bpc); + } + while (1) { if (s->ld.index[s->ld.color] < 0) @@ -2050,68 +3624,235 @@ fix_line_distance_se (Mustek_Scanner *s, int num_lines, int bpl, else { s->ld.quant[s->ld.color] += res; - if (s->ld.quant[s->ld.color] >= s->ld.peak_res) + if (s->ld.quant[s->ld.color] >= max_value) { /* This line must be processed, not dropped. */ - s->ld.quant[s->ld.color] -= s->ld.peak_res; + s->ld.quant[s->ld.color] -= max_value; if (lines[s->ld.color] > 0) { - /* There's still a line to be output for current color. + /* There's still a line to be output for current color. Then shuffle current color line to output buffer. */ scale = 0; - for (pixel = 0; pixel < bpc; ++pixel) - { - scale += res; - if (scale >= s->ld.peak_res) - { - scale -= s->ld.peak_res; - *out_ptr[s->ld.color] = *raw; - out_ptr[s->ld.color] += 3; - } - ++raw; - } + /* need to enlarge x-resolution? */ + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) + && (res > half_res)) + { + SANE_Byte *raw_start = raw; + for (pixel = 0; pixel < s->params.pixels_per_line; + ++pixel) + { + *out_ptr[s->ld.color] = *raw; + out_ptr[s->ld.color] += 3; + scale += half_res; + if (scale >= half_res) + { + scale -= res; + ++raw; + } + + } + DBG (5, + "fix_line_distance_se: got line: %d; color: %d; " + "raw bytes: %d; out bytes: %d\n", + lines[s->ld.color], s->ld.color, raw - raw_start, + s->params.pixels_per_line); + raw = raw_start + bpc; + } + else + { + if (preview) + { + for (pixel = 0; pixel < bpc; ++pixel) + { + *out_ptr[s->ld.color] = *raw++; + out_ptr[s->ld.color] += 3; + } + } + else + { + for (pixel = 0; pixel < bpc; ++pixel) + { + scale += res; + if (scale >= max_value) + { + scale -= max_value; + *out_ptr[s->ld.color] = *raw; + out_ptr[s->ld.color] += 3; + } + ++raw; + } + } + + DBG (5, "fix_line_distance_se: got line: %d; color: " + "%d\n", lines[s->ld.color], s->ld.color); + } --lines[s->ld.color]; } else - { + { /* At least one component missing, so save this line. */ - memcpy(s->ld.buf[s->ld.color] + s->ld.saved[s->ld.color] - * bpc, raw, bpc); + memcpy (s->ld.buf[s->ld.color] + s->ld.saved[s->ld.color] + * bpc, raw, bpc); + DBG (5, "fix_line_distance_se: saved line %d; color %d\n", + s->ld.saved[s->ld.color], s->ld.color); ++(s->ld.saved[s->ld.color]); raw += bpc; } } - else - raw += bpc; + else + { + if (!preview) + raw += bpc; + DBG (5, "fix_line_distance_se: ignored line; color: %d\n", + s->ld.color); + } - if (raw >= raw_end) + if (raw >= raw_end) { /* Reduce num_lines if we encounter excess lines. */ if (num_lines > s->ld.ld_line) - num_lines = s->ld.ld_line; + num_lines = s->ld.ld_line; s->ld.ld_line -= num_lines; - if (++s->ld.color > 2) s->ld.color = 0; - return num_lines; + if (++s->ld.color > 2) + s->ld.color = 0; + return num_lines; } } - if (++s->ld.color > 2) s->ld.color = 0; + if (++s->ld.color > 2) + s->ld.color = 0; } } + +/* For Pro models. Not really a linedistance correction (they don't need one) + only enlarging x-res here */ static void -fix_line_distance_normal (Mustek_Scanner *s, int num_lines, int bpl, - u_int8_t *raw, u_int8_t *out) +fix_line_distance_pro (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, + SANE_Byte * raw, SANE_Byte * out) { - u_int8_t *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; - int index[3]; /* index of the next output line for color C */ - int i, color; + SANE_Byte *out_addr, *in_addr; + SANE_Int res, half_res, y, x_out, x_in; + + + res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); + half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; + + DBG (5, "fix_line_distance_pro: res=%d; halfres=%d; num_lines=%d; bpl=%d\n", + res, half_res, num_lines, bpl); + + if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) + { + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) + { + /*12 bit, need to enlarge x-resolution */ + DBG (5, "fix_line_distance_pro: res > half_res --> need to " + "enlarge x\n"); + if (little_endian ()) + for (y = 0; y < num_lines; y++) + { + for (x_out = 0; x_out < s->params.pixels_per_line; x_out++) + { + x_in = x_out * bpl / s->params.bytes_per_line / 2; + x_in *= 2; + out_addr = out + y * s->params.bytes_per_line + x_out * 6; + in_addr = raw + y * bpl + x_in * 6; + *(out_addr) = *(in_addr) << 4; + *(out_addr + 1) = (*(in_addr) >> 4) + + (*(in_addr + 1) << 4); + *(out_addr + 2) = *(in_addr + 2) << 4; + *(out_addr + 3) = (*(in_addr + 2) >> 4) + + (*(in_addr + 3) << 4); + *(out_addr + 4) = *(in_addr + 4) << 4; + *(out_addr + 5) = (*(in_addr + 4) >> 4) + + (*(in_addr + 5) << 4); + } + } + else /* big endian */ + for (y = 0; y < num_lines; y++) + { + for (x_out = 0; x_out < s->params.pixels_per_line; x_out++) + { + x_in = x_out * bpl / s->params.bytes_per_line / 2; + out_addr = out + y * s->params.bytes_per_line + x_out * 6; + in_addr = raw + y * bpl + x_in * 6; + *(out_addr) = (*(in_addr) >> 4) + (*(in_addr + 1) << 4); + *(out_addr + 1) = *(in_addr) << 4; + *(out_addr + 2) = (*(in_addr + 2) >> 4) + + (*(in_addr + 3) << 4); + *(out_addr + 3) = *(in_addr + 2) << 4; + *(out_addr + 4) = (*(in_addr + 4) >> 4) + + (*(in_addr + 5) << 4); + *(out_addr + 5) = *(in_addr + 4) << 4; + } + } + } + else /* 12 bit, no need to enlarge x */ + { + SANE_Word pixel; + + if (little_endian ()) + for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++) + { + *(out + pixel * 2) = *(raw + pixel * 2) << 4; + *(out + pixel * 2 + 1) = (*(raw + pixel * 2) >> 4) + + (*(raw + pixel * 2 + 1) << 4); + } + else /* big endian */ + for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++) + { + *(out + pixel * 2) = (*(raw + pixel * 2) >> 4) + + (*(raw + pixel * 2 + 1) << 4); + *(out + pixel * 2 + 1) = *(raw + pixel * 2) << 4; + } + + } + } + else /* 8 bit */ + { + /* need to enlarge x-resolution? */ + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) + { + DBG (5, "fix_line_distance_pro: res > half_res --> need to " + "enlarge x\n"); + + for (y = 0; y < num_lines; y++) + { + for (x_out = 0; x_out < s->params.pixels_per_line; x_out++) + { + x_in = x_out * bpl / s->params.bytes_per_line; + out_addr = out + y * s->params.bytes_per_line + x_out * 3; + in_addr = raw + y * bpl + x_in * 3; + *(out_addr) = *(in_addr); + *(out_addr + 1) = *(in_addr + 1); + *(out_addr + 2) = *(in_addr + 2); + } + } + } + else + memcpy (out, raw, num_lines * bpl); + } + return; +} + +/* For MFS-08000SP, MFS-06000SP. MFC-08000CZ, MFC-06000CZ */ +static void +fix_line_distance_normal (Mustek_Scanner * s, SANE_Int num_lines, + SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) +{ + SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; + SANE_Int index[3]; /* index of the next output line for color C */ + SANE_Int i, color; /* Initialize the indices with the line distances that were returned - by the CCD linedistance command. We want to skip data for the - first OFFSET rounds, so we initialize the indices to the negative - of this offset. */ + by the CCD linedistance command or set manually (option + linedistance-fix). We want to skip data for the first OFFSET + rounds, so we initialize the indices to the negative of this + offset. */ + + DBG (5, "fix_line_distance_normal: %d lines, %d bpl\n", num_lines, bpl); + for (color = 0; color < 3; ++color) index[color] = -s->ld.dist[color]; @@ -2134,7 +3875,7 @@ fix_line_distance_normal (Mustek_Scanner *s, int num_lines, int bpl, { *out_ptr = *raw++; out_ptr += 3; - } + } ++index[color]; if (raw >= raw_end) return; @@ -2144,10 +3885,168 @@ fix_line_distance_normal (Mustek_Scanner *s, int num_lines, int bpl, } } -static SANE_Status -init_options (Mustek_Scanner *s) +/* Paragon series I + II. */ +static SANE_Int +fix_line_distance_block (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, + SANE_Byte * raw, SANE_Byte * out, + SANE_Int num_lines_total) { - int i; + SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; + SANE_Int c, num_saved_lines, line, max_index, min_index; + + if (!s->ld.buf[0]) + { + DBG (5, "fix_line_distance_block: allocating temp buffer of %d*%d " + "bytes\n", MAX_LINE_DIST, bpl); + s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl); + if (!s->ld.buf[0]) + { + DBG (1, "fix_line_distance_block: failed to malloc temporary " + "buffer\n"); + return 0; + } + } + DBG (5, "fix_line_distance_block: s->ld.index = {%d, %d, %d}, " + "s->ld.lmod3 = %d\n", s->ld.index[0], s->ld.index[1], s->ld.index[2], + s->ld.lmod3); + DBG (5, "fix_line_distance_block: s->ld.quant = {%d, %d, %d}, " + "s->ld.max_value = %d\n", s->ld.quant[0], s->ld.quant[1], + s->ld.quant[2], s->ld.max_value); + DBG (5, + "fix_line_distance_block: s->ld.peak_res = %d, s->ld.ld_line = %d\n", + s->ld.peak_res, s->ld.ld_line); + + /* search maximum and minimum index */ + max_index = MAX (s->ld.index[0], MAX (s->ld.index[1], s->ld.index[2])); + min_index = MIN (s->ld.index[0], MIN (s->ld.index[1], s->ld.index[2])); + num_saved_lines = max_index - min_index; + if (s->ld.index[0] == 0) + num_saved_lines = 0; + /* restore the previously saved lines: */ + memcpy (out, s->ld.buf[0], num_saved_lines * bpl); + DBG (5, "fix_line_distance_block: copied %d lines from " + "ld.buf to buffer (max=%d, min=%d)\n", num_saved_lines, max_index, + min_index); + while (1) + { + if (++s->ld.lmod3 >= 3) + s->ld.lmod3 = 0; + + c = color_seq[s->ld.lmod3]; + if (s->ld.index[c] < 0) + ++s->ld.index[c]; + else if (s->ld.index[c] < num_lines_total) + { + s->ld.quant[c] += s->ld.peak_res; + if (s->ld.quant[c] > s->ld.max_value) + { + s->ld.quant[c] -= s->ld.max_value; + line = s->ld.index[c]++ - s->ld.ld_line; + out_ptr = out + line * bpl + c; + out_end = out_ptr + bpl; + while (out_ptr != out_end) + { + *out_ptr = *raw++; + out_ptr += 3; + } + DBG (5, "fix_line_distance_block: copied line %d (color %d)\n", + line + s->ld.ld_line, c); + + max_index = MAX (s->ld.index[0], + MAX (s->ld.index[1], s->ld.index[2])); + min_index = MIN (s->ld.index[0], + MIN (s->ld.index[1], s->ld.index[2])); + if ((raw >= raw_end) || ((min_index >= num_lines_total))) + { + DBG (5, "fix_line_distance_block: got num_lines: %d\n", + num_lines); + num_lines = min_index - s->ld.ld_line; + if (num_lines < 0) + num_lines = 0; + if ((s->total_lines + num_lines) > s->params.lines) + num_lines = s->params.lines - s->total_lines; + s->total_lines += num_lines; + + /* copy away the lines with at least one missing + color component, so that we can interleave them + with new scan data on the next call */ + num_saved_lines = max_index - min_index; + + DBG (5, "fix_line_distance_block: num_saved_lines = %d; " + "num_lines = %d; bpl = %d\n", num_saved_lines, + num_lines, bpl); + + memcpy (s->ld.buf[0], out + num_lines * bpl, + num_saved_lines * bpl); + + DBG (5, "fix_line_distance_block: copied %d lines to " + "ld.buf\n", num_saved_lines); + + /* notice the number of lines we processed */ + s->ld.ld_line = min_index; + if (s->ld.ld_line < 0) + s->ld.ld_line = 0; + + DBG (4, "fix_line_distance_block: lmod3=%d, " + "index=(%d,%d,%d), line = %d, lines = %d\n", + s->ld.lmod3, + s->ld.index[0], s->ld.index[1], s->ld.index[2], + s->ld.ld_line, num_lines); + /* return number of complete (r+g+b) lines */ + return num_lines; + } + } + } + } +} + +/* For MFS-1200SP 1.00 and others */ +/* No LD correction necessary, just shuffle around data */ +static SANE_Int +fix_line_distance_none (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, + SANE_Byte * raw, SANE_Byte * out) +{ + SANE_Byte *red_ptr, *grn_ptr, *blu_ptr, *ptr, *ptr_end; + SANE_Word y; + + ptr = out; + red_ptr = raw; + + DBG (5, "fix_line_distance_none: no ld correction necessary (%d lines)\n", + num_lines); + + s->ld.ld_line += num_lines; + + if (s->ld.ld_line > s->params.lines) + num_lines -= (s->ld.ld_line - s->params.lines); + if (num_lines < 0) + num_lines = 0; + + DBG (5, "fix_line_distance_none: using %d lines (ld_line = %d, " + "s->params.lines = %d)\n", num_lines, s->ld.ld_line, s->params.lines); + + for (y = 0; y < num_lines; ++y) + { + grn_ptr = red_ptr + bpl / 3; + blu_ptr = grn_ptr + bpl / 3; + ptr_end = red_ptr + bpl; + + while (blu_ptr != ptr_end) + { + *ptr++ = *red_ptr++; + *ptr++ = *grn_ptr++; + *ptr++ = *blu_ptr++; + } + red_ptr = ptr_end; + } + return num_lines; +} + + +static SANE_Status +init_options (Mustek_Scanner * s) +{ + SANE_Int i, j, gammasize; memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); @@ -2158,6 +4057,7 @@ init_options (Mustek_Scanner *s) s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } + s->opt[OPT_NUM_OPTS].name = ""; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; @@ -2165,11 +4065,11 @@ init_options (Mustek_Scanner *s) s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ - - s->opt[OPT_MODE_GROUP].title = "Scan Mode"; + s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; + s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ @@ -2178,23 +4078,35 @@ init_options (Mustek_Scanner *s) s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; s->opt[OPT_MODE].type = SANE_TYPE_STRING; s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - if (s->hw->flags & MUSTEK_FLAG_PP) + if (s->hw->flags & MUSTEK_FLAG_SE) { - s->opt[OPT_MODE].size = max_string_size (pp_mode_list); - s->opt[OPT_MODE].constraint.string_list = pp_mode_list; - s->val[OPT_MODE].s = strdup (pp_mode_list[2]); - } - else if (s->hw->flags & MUSTEK_FLAG_SE) - { - s->opt[OPT_MODE].size = max_string_size (se_mode_list); - s->opt[OPT_MODE].constraint.string_list = se_mode_list; - s->val[OPT_MODE].s = strdup (se_mode_list[1]); + s->opt[OPT_MODE].size = max_string_size (mode_list_se); + s->opt[OPT_MODE].constraint.string_list = mode_list_se; + s->val[OPT_MODE].s = strdup (mode_list_se[1]); + if (!s->val[OPT_MODE].s) + return SANE_STATUS_NO_MEM; } else { - s->opt[OPT_MODE].size = max_string_size (mode_list); - s->opt[OPT_MODE].constraint.string_list = mode_list; - s->val[OPT_MODE].s = strdup (mode_list[2]); + s->opt[OPT_MODE].size = max_string_size (mode_list_paragon); + s->opt[OPT_MODE].constraint.string_list = mode_list_paragon; + s->val[OPT_MODE].s = strdup (mode_list_paragon[2]); + if (!s->val[OPT_MODE].s) + return SANE_STATUS_NO_MEM; + } + + /* fast gray mode (pro models) */ + s->opt[OPT_FAST_GRAY_MODE].name = "fast-gray-mode"; + s->opt[OPT_FAST_GRAY_MODE].title = SANE_I18N ("Fast gray mode"); + s->opt[OPT_FAST_GRAY_MODE].desc = SANE_I18N ("Scan in fast gray mode " + "(lower quality)."); + s->opt[OPT_FAST_GRAY_MODE].type = SANE_TYPE_BOOL; + s->val[OPT_FAST_GRAY_MODE].w = SANE_FALSE; + s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE; + if (s->hw->flags & MUSTEK_FLAG_PRO) + { + /* Only Pro models support fast gray mode */ + s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE; } /* resolution */ @@ -2205,7 +4117,20 @@ init_options (Mustek_Scanner *s) s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range; - s->val[OPT_RESOLUTION].w = MAX(SANE_FIX(18), s->hw->dpi_range.min); + s->val[OPT_RESOLUTION].w = MAX (SANE_FIX (72), s->hw->dpi_range.min); + + /* bit depth */ + s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; + s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; + s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; + s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_STRING; + s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_BIT_DEPTH].size = max_string_size (bit_depth_list_pro); + s->opt[OPT_BIT_DEPTH].constraint.string_list = bit_depth_list_pro; + s->val[OPT_BIT_DEPTH].s = strdup (bit_depth_list_pro[0]); + if (!s->val[OPT_BIT_DEPTH].s) + return SANE_STATUS_NO_MEM; /* speed */ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; @@ -2215,25 +4140,51 @@ init_options (Mustek_Scanner *s) s->opt[OPT_SPEED].size = max_string_size (speed_list); s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SPEED].constraint.string_list = speed_list; - s->val[OPT_SPEED].s = strdup (speed_list[2]); + s->val[OPT_SPEED].s = strdup (speed_list[4]); + if (!s->val[OPT_SPEED].s) + return SANE_STATUS_NO_MEM; + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) + { + /* Speed only supported by 3-pass scanners */ + s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; + } /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; - s->opt[OPT_SOURCE].size = max_string_size (source_list); - s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - s->opt[OPT_SOURCE].constraint.string_list = source_list; - s->val[OPT_SOURCE].s = strdup (source_list[0]); - /* backtrack */ - s->opt[OPT_BACKTRACK].name = SANE_NAME_BACKTRACK; - s->opt[OPT_BACKTRACK].title = SANE_TITLE_BACKTRACK; - s->opt[OPT_BACKTRACK].desc = SANE_DESC_BACKTRACK; - s->opt[OPT_BACKTRACK].type = SANE_TYPE_BOOL; - s->val[OPT_BACKTRACK].w = SANE_FALSE; - + if ((s->hw->flags & MUSTEK_FLAG_SE) || (s->hw->flags & MUSTEK_FLAG_N) + || (s->hw->flags & MUSTEK_FLAG_TA)) + { + s->opt[OPT_SOURCE].size = max_string_size (ta_source_list); + s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_SOURCE].constraint.string_list = ta_source_list; + s->val[OPT_SOURCE].s = strdup (ta_source_list[0]); + if (!s->val[OPT_SOURCE].s) + return SANE_STATUS_NO_MEM; + } + else if (s->hw->flags & MUSTEK_FLAG_ADF) + { + s->opt[OPT_SOURCE].size = max_string_size (adf_source_list); + s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_SOURCE].constraint.string_list = adf_source_list; + s->val[OPT_SOURCE].s = strdup (adf_source_list[0]); + if (!s->val[OPT_SOURCE].s) + return SANE_STATUS_NO_MEM; + } + else + { + s->opt[OPT_SOURCE].size = max_string_size (source_list); + s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_SOURCE].constraint.string_list = source_list; + s->val[OPT_SOURCE].s = strdup (source_list[0]); + s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; + if (!s->val[OPT_SOURCE].s) + return SANE_STATUS_NO_MEM; + } + /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; @@ -2241,19 +4192,21 @@ init_options (Mustek_Scanner *s) s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = 0; - /* gray preview */ - s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; - s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; - s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; - s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; - s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; + /* fast preview */ + s->opt[OPT_FAST_PREVIEW].name = "fast-preview"; + s->opt[OPT_FAST_PREVIEW].title = SANE_I18N ("Fast preview"); + s->opt[OPT_FAST_PREVIEW].desc = SANE_I18N ("Request that all previews are " + "done in in the fastest (low-quality) mode. This may be a non-color " + "mode or a low resolution mode."); + s->opt[OPT_FAST_PREVIEW].type = SANE_TYPE_BOOL; + s->val[OPT_FAST_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ - - s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; + s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; + s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ @@ -2297,61 +4250,123 @@ init_options (Mustek_Scanner *s) s->val[OPT_BR_Y].w = s->hw->y_range.max; /* "Enhancement" group: */ - - s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; + s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; + s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - /* grain-size */ - s->opt[OPT_GRAIN_SIZE].name = SANE_NAME_GRAIN_SIZE; - s->opt[OPT_GRAIN_SIZE].title = SANE_TITLE_GRAIN_SIZE; - s->opt[OPT_GRAIN_SIZE].desc = SANE_DESC_GRAIN_SIZE; - s->opt[OPT_GRAIN_SIZE].type = SANE_TYPE_INT; - s->opt[OPT_GRAIN_SIZE].unit = SANE_UNIT_PIXEL; - s->opt[OPT_GRAIN_SIZE].constraint_type = SANE_CONSTRAINT_WORD_LIST; - s->opt[OPT_GRAIN_SIZE].constraint.word_list = grain_list; - s->val[OPT_GRAIN_SIZE].w = grain_list[1]; - /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; - s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS - " This option is active for lineart/halftone modes only. " - "For multibit modes (grey/color) use the gamma-table(s)."; + s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - { - /* 1-pass scanners don't support brightness in colormode: */ - s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range; - } - else - s->opt[OPT_BRIGHTNESS].constraint.range = &ax_brightness_range; + s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range; + if (!s->hw->flags & MUSTEK_FLAG_THREE_PASS) + /* 1-pass scanners don't support brightness in multibit mode */ + s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->val[OPT_BRIGHTNESS].w = 0; + /* brightness red */ + s->opt[OPT_BRIGHTNESS_R].name = "brightness-r"; + s->opt[OPT_BRIGHTNESS_R].title = SANE_I18N ("Brightness red channel"); + s->opt[OPT_BRIGHTNESS_R].desc = SANE_I18N ("Controls the brightness of " + "the red channel of the " + "acquired image."); + s->opt[OPT_BRIGHTNESS_R].type = SANE_TYPE_FIXED; + s->opt[OPT_BRIGHTNESS_R].unit = SANE_UNIT_PERCENT; + s->opt[OPT_BRIGHTNESS_R].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BRIGHTNESS_R].constraint.range = &percentage_range; + s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE; + s->val[OPT_BRIGHTNESS_R].w = 0; + + /* brightness green */ + s->opt[OPT_BRIGHTNESS_G].name = "brightness-g"; + s->opt[OPT_BRIGHTNESS_G].title = SANE_I18N ("Brightness green channel"); + s->opt[OPT_BRIGHTNESS_G].desc = SANE_I18N ("Controls the brightness of " + "the green channel of the " + "acquired image."); + s->opt[OPT_BRIGHTNESS_G].type = SANE_TYPE_FIXED; + s->opt[OPT_BRIGHTNESS_G].unit = SANE_UNIT_PERCENT; + s->opt[OPT_BRIGHTNESS_G].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BRIGHTNESS_G].constraint.range = &percentage_range; + s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE; + s->val[OPT_BRIGHTNESS_G].w = 0; + + /* brightness blue */ + s->opt[OPT_BRIGHTNESS_B].name = "brightness-b"; + s->opt[OPT_BRIGHTNESS_B].title = SANE_I18N ("Brightness blue channel"); + s->opt[OPT_BRIGHTNESS_B].desc = SANE_I18N ("Controls the brightness of " + "the blue channel of the " + "acquired image."); + s->opt[OPT_BRIGHTNESS_B].type = SANE_TYPE_FIXED; + s->opt[OPT_BRIGHTNESS_B].unit = SANE_UNIT_PERCENT; + s->opt[OPT_BRIGHTNESS_B].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BRIGHTNESS_B].constraint.range = &percentage_range; + s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE; + s->val[OPT_BRIGHTNESS_B].w = 0; + /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; - s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST - " This option is active for lineart/halftone modes only. " - "For multibit modes (grey/color) use the gamma-table(s)."; + s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED; s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - { - /* 1-pass scanners don't support contrast in colormode: */ - s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_CONTRAST].constraint.range = &percentage_range; - } - else - s->opt[OPT_CONTRAST].constraint.range = &ax_contrast_range; + s->opt[OPT_CONTRAST].constraint.range = &percentage_range; + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) + /* 1-pass scanners don't support contrast in multibit mode */ + s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->val[OPT_CONTRAST].w = 0; + /* contrast red */ + s->opt[OPT_CONTRAST_R].name = "contrast-r"; + s->opt[OPT_CONTRAST_R].title = SANE_I18N ("Contrast red channel"); + s->opt[OPT_CONTRAST_R].desc = SANE_I18N ("Controls the contrast of " + "the red channel of the " + "acquired image."); + s->opt[OPT_CONTRAST_R].type = SANE_TYPE_FIXED; + s->opt[OPT_CONTRAST_R].unit = SANE_UNIT_PERCENT; + s->opt[OPT_CONTRAST_R].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_CONTRAST_R].constraint.range = &percentage_range; + s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE; + s->val[OPT_CONTRAST_R].w = 0; + + /* contrast green */ + s->opt[OPT_CONTRAST_G].name = "contrast-g"; + s->opt[OPT_CONTRAST_G].title = SANE_I18N ("Contrast green channel"); + s->opt[OPT_CONTRAST_G].desc = SANE_I18N ("Controls the contrast of " + "the green channel of the " + "acquired image."); + s->opt[OPT_CONTRAST_G].type = SANE_TYPE_FIXED; + s->opt[OPT_CONTRAST_G].unit = SANE_UNIT_PERCENT; + s->opt[OPT_CONTRAST_G].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_CONTRAST_G].constraint.range = &percentage_range; + s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE; + s->val[OPT_CONTRAST_G].w = 0; + + /* contrast blue */ + s->opt[OPT_CONTRAST_B].name = "contrast-b"; + s->opt[OPT_CONTRAST_B].title = SANE_I18N ("Contrast blue channel"); + s->opt[OPT_CONTRAST_B].desc = SANE_I18N ("Controls the contrast of " + "the blue channel of the " + "acquired image."); + s->opt[OPT_CONTRAST_B].type = SANE_TYPE_FIXED; + s->opt[OPT_CONTRAST_B].unit = SANE_UNIT_PERCENT; + s->opt[OPT_CONTRAST_B].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_CONTRAST_B].constraint.range = &percentage_range; + s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE; + s->val[OPT_CONTRAST_B].w = 0; + + /* gamma */ + gammasize = 256; + for (i = 0; i < 4; ++i) + for (j = 0; j < gammasize; ++j) + s->gamma_table[i][j] = j; + /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; @@ -2367,9 +4382,9 @@ init_options (Mustek_Scanner *s) s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); + s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0]; s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; - s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; @@ -2379,9 +4394,9 @@ init_options (Mustek_Scanner *s) s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); + s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0]; s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; - s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; @@ -2391,9 +4406,9 @@ init_options (Mustek_Scanner *s) s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); + s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0]; s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; - s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0]; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; @@ -2403,41 +4418,52 @@ init_options (Mustek_Scanner *s) s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); + s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0]; s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; - s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0]; + + /* quality calibration */ + s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL; + s->opt[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL; + s->opt[OPT_QUALITY_CAL].desc = SANE_DESC_QUALITY_CAL; + s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL; + if (s->hw->flags & MUSTEK_FLAG_PRO) + s->val[OPT_QUALITY_CAL].w = SANE_TRUE; + else + s->val[OPT_QUALITY_CAL].w = SANE_FALSE; + s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE; + if ((s->hw->flags & MUSTEK_FLAG_PRO) + || (s->hw->flags & MUSTEK_FLAG_SE_PLUS)) + { + /* Only Pro and SE Plus models support calibration */ + s->opt[OPT_QUALITY_CAL].cap &= ~SANE_CAP_INACTIVE; + } /* halftone dimension */ s->opt[OPT_HALFTONE_DIMENSION].name = SANE_NAME_HALFTONE_DIMENSION; s->opt[OPT_HALFTONE_DIMENSION].title = SANE_TITLE_HALFTONE_DIMENSION; s->opt[OPT_HALFTONE_DIMENSION].desc = SANE_DESC_HALFTONE_DIMENSION; - s->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_INT; + s->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_STRING; + s->opt[OPT_HALFTONE_DIMENSION].size = max_string_size (halftone_list); + s->opt[OPT_HALFTONE_DIMENSION].constraint_type = + SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_HALFTONE_DIMENSION].constraint.string_list = halftone_list; + s->val[OPT_HALFTONE_DIMENSION].s = strdup (halftone_list[0]); + if (!s->val[OPT_HALFTONE_DIMENSION].s) + return SANE_STATUS_NO_MEM; s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_HALFTONE_DIMENSION].unit = SANE_UNIT_PIXEL; - s->opt[OPT_HALFTONE_DIMENSION].constraint_type = SANE_CONSTRAINT_WORD_LIST; - s->opt[OPT_HALFTONE_DIMENSION].constraint.word_list = pattern_dimension_list; - s->val[OPT_HALFTONE_DIMENSION].w = 0; /* halftone pattern */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT; - s->opt[OPT_HALFTONE_PATTERN].size = 0; + s->opt[OPT_HALFTONE_PATTERN].size = 4; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_HALFTONE_PATTERN].constraint.range = &u8_range; s->val[OPT_HALFTONE_PATTERN].wa = s->halftone_pattern; - if (s->hw->flags & MUSTEK_FLAG_SE) - { - /* SE models don't support speed, source, backtrack, grain size */ - s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_BACKTRACK].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_GRAIN_SIZE].cap |= SANE_CAP_INACTIVE; - } - return SANE_STATUS_GOOD; } @@ -2457,114 +4483,227 @@ init_options (Mustek_Scanner *s) descriptors. */ static void -output_data (Mustek_Scanner *s, FILE *fp, - SANE_Byte *data, int lines_per_buffer, int bpl, - SANE_Byte *extra) +output_data (Mustek_Scanner * s, FILE * fp, + SANE_Byte * data, SANE_Int lines_per_buffer, SANE_Int bpl, + SANE_Byte * extra) { SANE_Byte *ptr, *ptr_end; - int y, bit, num_lines; + SANE_Int y, num_lines; - DBG(4, "mustek.output_data: data=%p, lpb=%d, bpl=%d, extra=%p\n", - data, lines_per_buffer, bpl, extra); + DBG (5, "output_data: data=%p, lpb=%d, bpl=%d, extra=%p\n", + data, lines_per_buffer, bpl, extra); /* convert to pixel-interleaved format: */ if ((s->mode & MUSTEK_MODE_COLOR) - && (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS)) + && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { num_lines = lines_per_buffer; /* need to correct for distance between r/g/b sensors: */ - if (s->hw->flags & MUSTEK_FLAG_SE) - num_lines = fix_line_distance_se (s, num_lines, bpl, data, extra); - else if (s->hw->flags & MUSTEK_FLAG_LD_MFS) - fix_line_distance_mfs (s, num_lines, bpl, data, extra); - else if (s->ld.max_value) + if (s->hw->flags & MUSTEK_FLAG_PRO) + fix_line_distance_pro (s, num_lines, bpl, data, extra); + else if (s->hw->flags & MUSTEK_FLAG_SE) { - if (s->hw->flags & MUSTEK_FLAG_PP) - num_lines = fix_line_distance_pp (s, num_lines, bpl, data, extra); + num_lines = fix_line_distance_se (s, num_lines, bpl, data, extra); + } + else if (s->hw->flags & MUSTEK_FLAG_N) + { + if (s->hw->flags & MUSTEK_FLAG_LD_N2) + num_lines = fix_line_distance_n_2 (s, num_lines, bpl, data, + extra); else - fix_line_distance_normal (s, num_lines, bpl, data, extra); + num_lines = fix_line_distance_n_1 (s, num_lines, bpl, data, + extra); } - else - { - /* Just shuffle around while copying from *data to *extra */ - SANE_Byte *red_ptr, *grn_ptr, *blu_ptr; - - ptr = extra; - red_ptr = data; - for (y = 0; y < num_lines; ++y) - { - grn_ptr = red_ptr + bpl / 3; - blu_ptr = grn_ptr + bpl / 3; - ptr_end = red_ptr + bpl; - - while (blu_ptr != ptr_end) - { - *ptr++ = *red_ptr++; - *ptr++ = *grn_ptr++; - *ptr++ = *blu_ptr++; - } - red_ptr = ptr_end; - } - } - - if (s->mode & MUSTEK_MODE_MULTIBIT) + else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK) + && (s->ld.max_value != 0)) { - /* each r/g/b sample is 8 bits in line-interleaved format */ - fwrite (extra, num_lines, s->params.bytes_per_line, fp); + if (s->hw->flags & MUSTEK_FLAG_PARAGON_1) + num_lines = fix_line_distance_block (s, num_lines, bpl, data, + extra, s->hw->lines); + else + num_lines = fix_line_distance_block (s, num_lines, bpl, data, + extra, + s->hw->lines_per_block); } + else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE) + && (s->ld.max_value != 0)) + fix_line_distance_normal (s, num_lines, bpl, data, extra); else + num_lines = fix_line_distance_none (s, num_lines, bpl, data, extra); + + if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) { - /* each r/g/b/ sample is 1 bit in line-interleaved format */ - ptr = extra; - ptr_end = ptr + num_lines * bpl; - while (ptr != ptr_end) + /* need to revert line direction */ + SANE_Int line_number; + SANE_Int byte_number; + + DBG (5, "output_data: ADF found, mirroring lines\n"); + for (line_number = 0; line_number < num_lines; line_number++) { - for (bit = 7; bit >= 0; --bit) - fputc ( (*ptr & (1 << bit)) ? 0xff : 0x00, fp); - ++ptr; + for (byte_number = bpl - 3; byte_number >= 0; byte_number -= 3) + { + fputc (*(extra + line_number * bpl + byte_number), fp); + fputc (*(extra + line_number * bpl + byte_number + 1), fp); + fputc (*(extra + line_number * bpl + byte_number + 2), fp); + } } } + else + fwrite (extra, num_lines, s->params.bytes_per_line, fp); } else { - if (! (s->mode & MUSTEK_MODE_MULTIBIT)) + DBG (5, "output_data: write %d lpb; %d bpl\n", lines_per_buffer, bpl); + /* Scale x-resolution above 1/2 of the maximum resolution for + SE and Pro scanners */ + if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && + (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2))) { - /* in singlebit mode, the scanner returns 1 for black. ;-( */ - ptr = data; - ptr_end = ptr + lines_per_buffer * bpl; + SANE_Int x; + SANE_Int half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; + SANE_Int res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); + SANE_Int res_counter; + SANE_Int enlarged_x; - while (ptr != ptr_end) - *ptr++ = ~*ptr; - } - fwrite (data, lines_per_buffer, bpl, fp); + DBG (5, "output_data: enlarge lines from %d bpl to %d bpl\n", + s->hw->bpl, s->params.bytes_per_line); + + for (y = 0; y < lines_per_buffer; y++) + { + SANE_Byte byte = 0; + + x = 0; + res_counter = 0; + enlarged_x = 0; + + while (enlarged_x < s->params.pixels_per_line) + { + if (s->mode & MUSTEK_MODE_GRAY) + { + fputc (*(data + y * bpl + x), fp); + res_counter += half_res; + if (res_counter >= half_res) + { + res_counter -= res; + x++; + } + enlarged_x++; + } + else /* lineart */ + { + /* need to invert image because of funny SANE 1-bit image + polarity */ + if (*(data + x / 8 + y * bpl) & (1 << (7 - (x % 8)))) + byte |= 1 << (7 - (enlarged_x % 8)); + + if ((enlarged_x % 8) == 7) + { + fputc (~byte, fp); /* invert image */ + byte = 0; + } + res_counter += half_res; + if (res_counter >= half_res) + { + res_counter -= res; + x++; + } + enlarged_x++; + } + } + } + } + else /* lineart, gray or halftone (nothing to scale) */ + { + if ((s->mode & MUSTEK_MODE_LINEART) + || (s->mode & MUSTEK_MODE_HALFTONE)) + { + /* need to invert image because of funny SANE 1-bit image + polarity */ + ptr = data; + ptr_end = ptr + lines_per_buffer * bpl; + + if (strcmp (s->val[OPT_SOURCE].s, + "Automatic Document Feeder") == 0) + { + while (ptr != ptr_end) + { + (*ptr) = ~(*ptr); + ptr++; + /* need to revert bit direction */ + *ptr = ((*ptr & 0x80) >> 7) + ((*ptr & 0x40) >> 5) + + ((*ptr & 0x20) >> 3) + ((*ptr & 0x10) >> 1) + + ((*ptr & 0x08) << 1) + ((*ptr & 0x04) << 3) + + ((*ptr & 0x02) << 5) + ((*ptr & 0x01) << 7); + } + } + else + while (ptr != ptr_end) + { + (*ptr) = ~(*ptr); + ptr++; + } + } + if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) + { + /* need to revert line direction */ + SANE_Int line_number; + SANE_Int byte_number; + + DBG (5, "output_data: ADF found, mirroring lines\n"); + for (line_number = 0; line_number < lines_per_buffer; + line_number++) + { + for (byte_number = bpl - 1; byte_number >= 0; byte_number--) + { + fputc (*(data + line_number * bpl + byte_number), fp); + } + } + } + else + { + fwrite (data, lines_per_buffer, bpl, fp); + } + } } + DBG (5, "output_data: end\n"); } static RETSIGTYPE sigterm_handler (int signal) { - sanei_scsi_req_flush_all (); /* flush SCSI queue */ + sanei_scsi_req_flush_all (); /* flush SCSI queue */ + DBG (5, "sigterm_handler: signal %d\n", signal); _exit (SANE_STATUS_GOOD); } -static int -reader_process (Mustek_Scanner *s, int fd) +static SANE_Int +reader_process (Mustek_Scanner * s, SANE_Int fd) { - int index, last, lines_per_buffer, bpl, buffers, qu = 0, rd = 0; + SANE_Int lines_per_buffer, bpl; SANE_Byte *extra = 0, *ptr; sigset_t sigterm_set; struct SIGACTION act; SANE_Status status; FILE *fp; + SANE_Int buffernumber = 0; + SANE_Int buffer_count, max_buffers; struct - { - void *id; /* scsi queue id */ - SANE_Byte *data; /* data buffer */ - int lines; /* # lines in buffer */ - size_t num_read; /* # of bytes read (return value) */ - int bank; /* needed by SE models */ - } bstat[BUFFERS]; + { + void *id; /* scsi queue id */ + SANE_Byte *data; /* data buffer */ + SANE_Byte *command; /* command buffer */ + SANE_Int lines; /* # lines in buffer */ + size_t num_read; /* # of bytes read (return value) */ + SANE_Int bank; /* needed by SE models */ + SANE_Bool ready; /* ready to send to application? */ + SANE_Bool finished; /* block is finished */ + } + bstat[2]; + + if (disable_double_buffering) + DBG (3, "reader_process: disable_double_buffering is set, this may be " + "slow\n"); sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); @@ -2573,30 +4712,15 @@ reader_process (Mustek_Scanner *s, int fd) if (!fp) return SANE_STATUS_IO_ERROR; + s->total_lines = 0; bpl = s->hw->bpl; - if ((s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - && ((s->mode & (MUSTEK_MODE_COLOR | MUSTEK_MODE_MULTIBIT)) - == MUSTEK_MODE_COLOR)) - /* In single-bit, single-pass color mode we expand every bit of - information into a byte (0x00 or 0xff). */ - bpl /= 8; + /* buffer size is scanner dependant */ + lines_per_buffer = s->hw->buffer_size / bpl / 2; - /* Request size must be limited to 64 kByte for ScanExpress. */ - if (s->hw->flags & MUSTEK_FLAG_SE) + if (strip_height > 0.0) { - buffers = BUFFERS; - lines_per_buffer = MIN( sanei_scsi_max_request_size, 128 * 1024 / 2) / bpl; - } - else - { - buffers = 1; - lines_per_buffer = sanei_scsi_max_request_size / bpl; - } - - if (strip_height > 0.0) - { - int max_lines; + SANE_Int max_lines; double dpi; dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w); @@ -2604,167 +4728,239 @@ reader_process (Mustek_Scanner *s, int fd) if (lines_per_buffer > max_lines) { - DBG(2, "reader_process: limiting strip height to %g inches " - "(%d lines)\n", strip_height, max_lines); + DBG (2, "reader_process: limiting strip height to %g inches " + "(%d lines)\n", strip_height, max_lines); lines_per_buffer = max_lines; } } + if (!lines_per_buffer) { - DBG(1, "bpl (%d) > sanei_scsi_max_request_size (%d)\n", - bpl, sanei_scsi_max_request_size); - return SANE_STATUS_NO_MEM; /* resolution is too high */ + DBG (1, "reader_process: bpl (%d) > SCSI buffer size / 2 (%d)\n", + bpl, s->hw->buffer_size / 2); + return SANE_STATUS_NO_MEM; /* resolution is too high */ } - DBG(3, "lines_per_buffer=%d, bytes_per_line=%d\n", lines_per_buffer, bpl); - bstat[0].data = malloc (buffers * lines_per_buffer * (long) bpl); + DBG (4, "reader_process: %d lines per buffer, %d bytes per line, " + "%d bytes per buffer\n", lines_per_buffer, bpl, + lines_per_buffer * bpl); + bstat[0].data = malloc (2 * lines_per_buffer * (long) bpl); if (!bstat[0].data) { - DBG(1, "reader_process: failed to malloc %ld bytes\n", - buffers * lines_per_buffer * (long) bpl); + DBG (1, "reader_process: failed to malloc %ld bytes for data buffer\n", + lines_per_buffer * (long) bpl); return SANE_STATUS_NO_MEM; } + bstat[1].data = bstat[0].data + lines_per_buffer * (long) bpl; - for (index = 1; index < buffers; ++index) - bstat[index].data = bstat[index - 1].data + lines_per_buffer * (long) bpl; + bstat[0].command = malloc (2 * 10); + if (!bstat[0].command) + { + DBG (1, + "reader_process: failed to malloc %d bytes for command buffer\n", + 2 * 10); + return SANE_STATUS_NO_MEM; + } + bstat[1].command = bstat[0].command + 10; - /* Touch all pages of the buffer to fool the memory management. */ - ptr = bstat[0].data + buffers * lines_per_buffer * (long) bpl - 256; + /* Touch all pages of the buffer to fool the memory management. */ + ptr = bstat[0].data + 2 * lines_per_buffer * (long) bpl - 1; while (ptr >= bstat[0].data) { *ptr = 0x00; ptr -= 256; - } - - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) + } + + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { /* get temporary buffer for line-distance correction and/or bit - expansion. The 600 II N needs more space because the data must - be read in as single big block (cut up into pieces of - lines_per_buffer). This requires that the line distance correction - continues on every call exactly where it stopped if the image - shall be reconstructed without any stripes. The older SCSI scanners - allow the line distance correction to initialize for every - lines_per_buffer lines that are read on request. - The ScanExpress is similar to the 600 II N in this respect. */ - if (s->hw->flags & (MUSTEK_FLAG_PP || MUSTEK_FLAG_SE)) - extra = malloc ((lines_per_buffer + MAX_LINE_DIST) * (long) bpl); - else - extra = malloc (lines_per_buffer * (long) bpl); + expansion. For some scanners more space is needed because the + data must be read in as single big block (cut up into pieces + of lines_per_buffer). This requires that the line distance + correction continues on every call exactly where it stopped + if the image shall be reconstructed without any stripes. */ + extra = malloc ((lines_per_buffer + MAX_LINE_DIST) + * (long) s->params.bytes_per_line); if (!extra) - { - DBG(1, "reader_process: failed to malloc extra buffer\n"); + { + DBG (1, "reader_process: failed to malloc extra buffer\n"); return SANE_STATUS_NO_MEM; } } - + memset (&act, 0, sizeof (act)); act.sa_handler = sigterm_handler; sigaction (SIGTERM, &act, 0); - if (s->hw->flags & MUSTEK_FLAG_PP) + if (s->hw->flags & MUSTEK_FLAG_N) { /* reacquire port access rights (lost because of fork()): */ sanei_ab306_get_io_privilege (s->fd); + } + + if ((s->hw->flags & MUSTEK_FLAG_N) || (s->hw->flags & MUSTEK_FLAG_LD_BLOCK)) + { /* reset counter of line number for line-dictance correction */ s->ld.ld_line = 0; } - status = dev_read_start (s); - if (status != SANE_STATUS_GOOD) - return status; + max_buffers = s->hw->max_block_buffer_size / (lines_per_buffer * bpl); + if (max_buffers < 1) + { + DBG (1, "reader_process: buffersize > blocksize!\n"); + return SANE_STATUS_NO_MEM; + } + DBG (4, "reader_process: limiting block read to %d buffers (%d lines)\n", + max_buffers, MIN (s->hw->lines, (max_buffers * lines_per_buffer))); while (s->line < s->hw->lines) { - /* Enqueue read requests as long as there is more to scan and as - long as the queue is not full: */ + s->hw->lines_per_block = + MIN (s->hw->lines - s->line, (max_buffers * lines_per_buffer)); + status = dev_block_read_start (s, s->hw->lines_per_block); + if (status != SANE_STATUS_GOOD) + return status; - last = (s->line >= s->hw->lines || qu >= buffers); - - while (! last && (qu - rd) < REQUESTS) + for (buffernumber = 0; buffernumber < 2; buffernumber++) { - if (s->line + lines_per_buffer >= s->hw->lines) - { - /* do the last few lines: */ - bstat[qu].lines = s->hw->lines - s->line; - bstat[qu].bank = 0x01; - } - else - { - bstat[qu].lines = lines_per_buffer; - bstat[qu].bank = (qu < (buffers - 1)) ? 0x00 : 0x01; - } - s->line += bstat[qu].lines; - - sigprocmask (SIG_BLOCK, &sigterm_set, 0); - status = dev_read_req_enter (s, bstat[qu].data, bstat[qu].lines, bpl, - &bstat[qu].num_read, &bstat[qu].id, - bstat[qu].bank); - sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); - ++qu; - - if (status != SANE_STATUS_GOOD) - { - DBG(1, - "reader_process: failed to enqueue read req, status: %s\n", - sane_strstatus (status)); - return status; - } - else - { - DBG(4, "reader_process: line=%d (num_lines=%d), num_reqs=%d\n", - s->line, s->params.lines, (qu - rd)); - } - - last = (s->line >= s->hw->lines || qu >= buffers); + bstat[buffernumber].ready = SANE_FALSE; + bstat[buffernumber].finished = SANE_FALSE; } + buffer_count = 0; + buffernumber = 0; - /* wait for request(s) to complete */ - - while ((qu - rd) >= REQUESTS || (last && (rd < qu))) + while (1) { - status = dev_req_wait (bstat[rd].id); - - if (status != SANE_STATUS_GOOD) + /* omit reading first two buffers (not yet ready) */ + if (bstat[buffernumber].ready == SANE_TRUE) { - DBG(1, "reader_process: failed to read data, status: %s\n", - sane_strstatus (status)); - return status; + DBG (4, "reader_process: buffer %d: waiting for request to be " + "ready\n", buffernumber + 1); + status = dev_req_wait (bstat[buffernumber].id); + if (status == SANE_STATUS_GOOD) + { + DBG (4, "reader_process: buffer %d is ready, wanted %d, " + "got %ld bytes\n", buffernumber + 1, + bstat[buffernumber].lines * bpl, + (long int) bstat[buffernumber].num_read); + } + else + { + DBG (1, "reader_process: failed to read data, status: %s, " + "buffer: %d\n", sane_strstatus (status), + buffernumber + 1); + if (status == SANE_STATUS_NO_MEM) + { + DBG (1, + "Probably the size of the kernel SCSI buffer is " + "too small for the\n selected buffersize " + "in mustek.conf. Either decrease " + "buffersize in\n mustek.conf to e.g. 32, " + "increase SG_BIG_BUF in kernel to 130560, " + "or\n use SANE_SG_BUFFERSIZE variable. " + "See man sane-scsi and README for\n " + "details.\n"); + } + return status; + } + + DBG (4, "reader_process: buffer %d: sending %ld bytes to " + "output_data\n", buffernumber + 1, + (long int) bstat[buffernumber].num_read); + output_data (s, fp, bstat[buffernumber].data, + bstat[buffernumber].lines, bpl, extra); + if (bstat[buffernumber].finished) + break; /* everything written; exit loop */ } - else - DBG(4, "reader_process: %lu bytes read.\n", - (unsigned long) bstat[rd].num_read); - - ++rd; + if (disable_double_buffering) + { + /* Enter only one buffer at once */ + if (buffernumber == 1) + buffernumber = 0; + else + buffernumber = 1; + } + + /* enter read requests only if data left */ + if ((s->line < s->hw->lines) && (buffer_count < max_buffers)) + { + if (s->line + lines_per_buffer >= s->hw->lines) + { + /* do the last few lines: */ + bstat[buffernumber].lines = s->hw->lines - s->line; + bstat[buffernumber].bank = 0x01; + bstat[buffernumber].finished = SANE_TRUE; + } + else + { + bstat[buffernumber].lines = lines_per_buffer; + bstat[buffernumber].bank = 0x00; + } + + if ((buffer_count + 1) >= max_buffers) + bstat[buffernumber].finished = SANE_TRUE; + + s->line += bstat[buffernumber].lines; + bstat[buffernumber].ready = SANE_TRUE; + + buffer_count++; + + DBG (4, + "reader_process: buffer %d: entering read request for %d " + "bytes (buffer %d)\n", buffernumber + 1, + bstat[buffernumber].lines * bpl, buffer_count); + sigprocmask (SIG_BLOCK, &sigterm_set, 0); + status = dev_read_req_enter (s, bstat[buffernumber].data, + bstat[buffernumber].lines, bpl, + &bstat[buffernumber].num_read, + &bstat[buffernumber].id, + bstat[buffernumber].bank, + bstat[buffernumber].command); + sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); + + + if (status == SANE_STATUS_GOOD) + { + DBG (5, "reader_process: buffer %d: entered (line %d of %d," + " buffer %d)\n", buffernumber + 1, s->line, + s->hw->lines, buffer_count); + } + else + { + DBG (1, "reader_process: buffer %d: failed to enter read " + "request, status: %s\n", buffernumber + 1, + sane_strstatus (status)); + return status; + } + } + if (!disable_double_buffering) + { + if (buffernumber == 1) + buffernumber = 0; + else + buffernumber = 1; + } + /* This is said to fix the scanner hangs that reportedly show on + some MFS-12000SP scanners. */ + if (s->mode == 0 && (s->hw->flags & MUSTEK_FLAG_LINEART_FIX)) + usleep (200000); } - - if (last) - { - for (rd = 0; rd < qu; ++rd) - output_data (s, fp, bstat[rd].data, bstat[rd].lines, bpl, extra); - qu = 0; - rd = 0; - } - - /* This is said to fix the scanner hangs that reportedly show on - some MFS-12000SP scanners. */ - if (s->mode == 0 && (s->hw->flags & MUSTEK_FLAG_LINEART_FIX)) - usleep (200000); } fclose (fp); free (bstat[0].data); if (s->ld.buf[0]) free (s->ld.buf[0]); - if (extra) + if (extra) free (extra); + close (fd); return SANE_STATUS_GOOD; } static SANE_Status -attach_one_device (const char *devname) +attach_one_device (SANE_String_Const devname) { Mustek_Device *dev; @@ -2772,12 +4968,13 @@ attach_one_device (const char *devname) if (dev) { /* Keep track of newly attached devices so we can set options as - necessary. */ + necessary. */ if (new_dev_len >= new_dev_alloced) { new_dev_alloced += 4; if (new_dev) - new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); + new_dev = + realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); else new_dev = malloc (new_dev_alloced * sizeof (new_dev[0])); if (!new_dev) @@ -2791,101 +4988,348 @@ attach_one_device (const char *devname) return SANE_STATUS_GOOD; } -SANE_Status -sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize) -{ - char dev_name[PATH_MAX], *cp; - size_t len; - FILE *fp; - int i; +/**************************************************************************/ +/* SANE API calls */ +/**************************************************************************/ - DBG_INIT(); +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + SANE_Char line[PATH_MAX], *word, *end; + SANE_String_Const cp; + SANE_Int linenumber; + FILE *fp; + + DBG_INIT (); + +#ifdef DBG_LEVEL + debug_level = DBG_LEVEL; +#else + debug_level = 0; +#endif + + DBG (2, "SANE mustek backend version %d.%d build %d from %s\n", V_MAJOR, + V_MINOR, BUILD, PACKAGE_VERSION); if (version_code) - *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0); + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); + + DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); + +#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED + DBG (5, "sane_init: using sanei_scsi_open_extended\n"); +#else + DBG (5, "sane_init: using sanei_scsi_open with buffer size = %d bytes\n", + sanei_scsi_max_request_size); +#endif + + num_devices = 0; + force_wait = SANE_FALSE; + disable_double_buffering = SANE_FALSE; fp = sanei_config_open (MUSTEK_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ + DBG (3, "sane_init: couldn't find config file (%s), trying " + "/dev/scanner directly\n", MUSTEK_CONFIG_FILE); attach ("/dev/scanner", 0, SANE_FALSE); return SANE_STATUS_GOOD; } - - while (fgets (dev_name, sizeof (dev_name), fp)) + linenumber = 0; + DBG (4, "sane_init: reading config file `%s'\n", MUSTEK_CONFIG_FILE); + while (sanei_config_read (line, sizeof (line), fp)) { - cp = (char *) sanei_config_skip_whitespace (dev_name); - if (!*cp || *cp == '#') /* ignore line comments & empty lines */ - continue; - - len = strlen (cp); - if (cp[len - 1] == '\n') - cp[--len] = '\0'; + word = 0; + linenumber++; - if (!len) - continue; /* ignore empty lines */ - - if (strncmp (cp, "option", 6) == 0 && isspace (cp[6])) + cp = sanei_config_get_string (line, &word); + if (!word || cp == line) { - cp += 7; - cp = (char *) sanei_config_skip_whitespace (cp); + DBG (5, "sane_init: config file line %d: ignoring empty line\n", + linenumber); + if (word) + free (word); + continue; + } + if (word[0] == '#') + { + DBG (5, "sane_init: config file line %d: ignoring comment line\n", + linenumber); + free (word); + continue; + } - if (strncmp (cp, "strip-height", 12) == 0 && isspace (cp[12])) + if (strcmp (word, "option") == 0) + { + free (word); + word = 0; + cp = sanei_config_get_string (cp, &word); + + if (strcmp (word, "strip-height") == 0) { - char * end; - + free (word); + word = 0; + cp = sanei_config_get_string (cp, &word); errno = 0; - cp += 13; - strip_height = strtod (cp, &end); - if (end == cp || errno) + strip_height = strtod (word, &end); + if (end == word) { - DBG(1, "%s: strip-height `%s' is invalid\n", - MUSTEK_CONFIG_FILE, cp); - strip_height = 1.0; /* safe fallback */ + DBG (3, "sane-init: config file line %d: strip-height " + "must have a parameter; using 1 inch\n", linenumber); + strip_height = 1.0; + } + if (errno) + { + DBG (3, "sane-init: config file line %d: strip-height `%s' " + "is invalid (%s); using 1 inch\n", linenumber, + word, strerror (errno)); + strip_height = 1.0; } else { - if (strip_height < 0.0) - strip_height = 0.0; - DBG(2, "sane_init: strip-height set to %g inches\n", - strip_height); + if (strip_height < 0.1) + strip_height = 0.1; + DBG (3, "sane_init: config file line %d: strip-height set " + "to %g inches\n", linenumber, strip_height); } + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "force-wait") == 0) + { + DBG (3, "sane_init: config file line %d: enabling force-wait\n", + linenumber); + force_wait = SANE_TRUE; + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "disable-double-buffering") == 0) + { + DBG (3, "sane_init: config file line %d: disabling " + "double-buffering\n", linenumber); + disable_double_buffering = SANE_TRUE; + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "legal-size") == 0) + { + if (new_dev_len > 0) + { + /* Check for 12000 LS, no way to find out automatically */ + if (strcmp (new_dev[new_dev_len - 1]->sane.model, + "ScanExpress 12000SP") == 0) + { + new_dev[new_dev_len - 1]->x_range.max = + SANE_FIX (220.0); + new_dev[new_dev_len - 1]->y_range.max = + SANE_FIX (360.0); + new_dev[new_dev_len - 1]->sane.model = + "Paragon 1200 LS"; + DBG (3, + "sane_init: config file line %d: enabling " + "legal-size for %s\n", linenumber, + new_dev[new_dev_len - 1]->sane.name); + } + else + { + DBG (3, "sane_init: config file line %d: option " + "legal-size ignored, device %s is not a " + "Paragon 1200 LS\n", linenumber, + new_dev[new_dev_len - 1]->sane.name); + } + + } + else + { + DBG (3, "sane_init: config file line %d: option " + "legal-size ignored, was set before any device " + "name\n", linenumber); + } + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "linedistance-fix") == 0) + { + if (new_dev_len > 0) + { + new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LD_FIX; + DBG (3, "sane_init: config file line %d: enabling " + "linedistance-fix for %s\n", linenumber, + new_dev[new_dev_len - 1]->sane.name); + } + else + { + DBG (3, "sane_init: config file line %d: option " + "linedistance-fix ignored, was set before any device " + "name\n", linenumber); + } + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "disable-backtracking") == 0) + { + if (new_dev_len > 0) + { + new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_NO_BACKTRACK; + DBG (3, "sane_init: config file line %d: disabling " + "backtracking for %s\n", linenumber, + new_dev[new_dev_len - 1]->sane.name); + } + else + { + DBG (3, "sane_init: config file line %d: option " + "disable-backtracking ignored, was set before any " + "device name\n", linenumber); + } + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "lineart-fix") == 0) + { + if (new_dev_len > 0) + { + new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LINEART_FIX; + DBG (3, "sane_init: config file line %d: enabling " + "lineart-fix for %s\n", linenumber, + new_dev[new_dev_len - 1]->sane.name); + } + else + { + DBG (3, "sane_init: config file line %d: option " + "lineart-fix ignored, was set before any device name\n", + linenumber); + } + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "buffersize") == 0) + { + long buffer_size; + + free (word); + word = 0; + cp = sanei_config_get_string (cp, &word); + errno = 0; + buffer_size = strtol (word, &end, 0); + + if (end == word) + { + DBG (3, "sane-init: config file line %d:: buffersize must " + "have a parameter; using default (%d kb)\n", + linenumber, new_dev[new_dev_len - 1]->max_buffer_size); + } + if (errno) + { + DBG (3, "sane-init: config file line %d: buffersize `%s' " + "is invalid (%s); using default (%d kb)\n", linenumber, + word, strerror (errno), + new_dev[new_dev_len - 1]->max_buffer_size); + } + else + { + if (new_dev_len > 0) + { + if (buffer_size < 32.0) + buffer_size = 32.0; + new_dev[new_dev_len - 1]->max_buffer_size = + buffer_size * 1024; + DBG (3, + "sane_init: config file line %d: buffersize set " + "to %ld kb for %s\n", linenumber, buffer_size, + new_dev[new_dev_len - 1]->sane.name); + } + else + { + DBG (3, "sane_init: config file line %d: option " + "buffersize ignored, was set before any device " + "name\n", linenumber); + } + } + if (word) + free (word); + word = 0; + } + else if (strcmp (word, "blocksize") == 0) + { + long block_size; + + free (word); + word = 0; + cp = sanei_config_get_string (cp, &word); + errno = 0; + block_size = strtol (word, &end, 0); + + if (end == word) + { + DBG (3, "sane-init: config file line %d:: blocksize must " + "have a parameter; using default (1 GB)\n", + linenumber); + } + if (errno) + { + DBG (3, "sane-init: config file line %d: blocksize `%s' " + "is invalid (%s); using default (1 GB)\n", linenumber, + word, strerror (errno)); + } + else + { + if (new_dev_len > 0) + { + if (block_size < 256.0) + block_size = 256.0; + new_dev[new_dev_len - 1]->max_block_buffer_size = + block_size * 1024; + DBG (3, "sane_init: config file line %d: blocksize set " + "to %ld kb for %s\n", linenumber, block_size, + new_dev[new_dev_len - 1]->sane.name); + } + else + { + DBG (3, "sane_init: config file line %d: option " + "blocksize ignored, was set before any device " + "name\n", linenumber); + } + } + if (word) + free (word); + word = 0; } - else if (strncmp (cp, "linedistance-fix", 16) == 0 && - (isspace (cp[16]) || !cp[16])) - for (i = 0; i < new_dev_len; ++i) - { - new_dev[i]->flags |= MUSTEK_FLAG_LD_FIX; - DBG(2, "sane_init: enabling linedistance-fix for %s\n", - new_dev[i]->sane.name); - break; - } - else if (strncmp (cp, "lineart-fix", 11) == 0 && - (isspace (cp[11]) || !cp[11])) - for (i = 0; i < new_dev_len; ++i) - { - new_dev[i]->flags |= MUSTEK_FLAG_LINEART_FIX; - DBG(2, "sane_init: enabling lineart-fix for %s\n", - new_dev[i]->sane.name); - break; - } else - DBG(1, "%s: ignoring unknown option `%s'\n", - MUSTEK_CONFIG_FILE, cp); - continue; + { + DBG (3, "sane_init: config file line %d: ignoring unknown " + "option `%s'\n", linenumber, cp); + if (word) + free (word); + word = 0; + } } else - { + { new_dev_len = 0; - sanei_config_attach_matching_devices (cp, attach_one_device); + DBG (4, "sane_init: config file line %d: trying to attach `%s'\n", + linenumber, line); + sanei_config_attach_matching_devices (line, attach_one_device); + if (word) + free (word); + word = 0; } - } + } + if (new_dev_alloced > 0) - { + { new_dev_len = new_dev_alloced = 0; free (new_dev); } fclose (fp); + DBG (5, "sane_init: end\n"); return SANE_STATUS_GOOD; } @@ -2894,24 +5338,28 @@ sane_exit (void) { Mustek_Device *dev, *next; + DBG (4, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; - free ((void *) dev->sane.name); - free ((void *) dev->sane.model); + free (dev->name); free (dev); } - + if (devlist) + free (devlist); + devlist = 0; + first_dev = 0; sanei_ab306_exit (); /* may have to do some cleanup */ } SANE_Status -sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only) +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { - static const SANE_Device **devlist = 0; Mustek_Device *dev; - int i; + SANE_Int i; + DBG (4, "sane_get_devices: %d devices %s\n", num_devices, + local_only ? "(local only)" : ""); if (devlist) free (devlist); @@ -2925,16 +5373,28 @@ sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only) devlist[i++] = 0; *device_list = devlist; + DBG (5, "sane_get_devices: end\n"); return SANE_STATUS_GOOD; } SANE_Status -sane_open (SANE_String_Const devicename, SANE_Handle *handle) +sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Mustek_Device *dev; SANE_Status status; Mustek_Scanner *s; - int i, j; + + if (!devicename) + { + DBG (1, "sane_open: devicename is null!\n"); + return SANE_STATUS_INVAL; + } + if (!handle) + { + DBG (1, "sane_open: handle is null!\n"); + return SANE_STATUS_INVAL; + } + DBG (4, "sane_open: devicename=%s\n", devicename); if (devicename[0]) { @@ -2963,10 +5423,10 @@ sane_open (SANE_String_Const devicename, SANE_Handle *handle) s->fd = -1; s->pipe = -1; s->hw = dev; - for (i = 0; i < 4; ++i) - for (j = 0; j < 256; ++j) - s->gamma_table[i][j] = j; - + s->ld.ld_line = 0; + s->halftone_pattern = malloc (8 * 8 * sizeof (SANE_Int)); + if (!s->halftone_pattern) + return SANE_STATUS_NO_MEM; init_options (s); /* insert newly opened handle into list of open handles: */ @@ -2974,6 +5434,7 @@ sane_open (SANE_String_Const devicename, SANE_Handle *handle) first_handle = s; *handle = s; + DBG (4, "sane_open: finished (handle=%p)\n", (void *) s); return SANE_STATUS_GOOD; } @@ -2982,6 +5443,7 @@ sane_close (SANE_Handle handle) { Mustek_Scanner *prev, *s; + DBG (4, "sane_close: handle=%p\n", handle); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) @@ -2992,8 +5454,8 @@ sane_close (SANE_Handle handle) } if (!s) { - DBG(1, "close: invalid handle %p\n", handle); - return; /* oops, not a handle we know about */ + DBG (1, "sane_close: invalid handle %p\n", handle); + return; /* oops, not a handle we know about */ } if (s->scanning) @@ -3001,13 +5463,24 @@ sane_close (SANE_Handle handle) if (s->ld.buf[0]) free (s->ld.buf[0]); - + if (s->val[OPT_MODE].s) + free (s->val[OPT_MODE].s); + if (s->val[OPT_BIT_DEPTH].s) + free (s->val[OPT_BIT_DEPTH].s); + if (s->val[OPT_SPEED].s) + free (s->val[OPT_SPEED].s); + if (s->val[OPT_SOURCE].s) + free (s->val[OPT_SOURCE].s); + if (s->val[OPT_HALFTONE_DIMENSION].s) + free (s->val[OPT_HALFTONE_DIMENSION].s); + if (s->halftone_pattern) + free (s->halftone_pattern); if (prev) prev->next = s->next; else - first_handle = s; - + first_handle = s->next; free (handle); + handle = 0; } const SANE_Option_Descriptor * @@ -3015,32 +5488,84 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Mustek_Scanner *s = handle; - if ((unsigned) option >= NUM_OPTIONS) - return 0; + if (((unsigned) option >= NUM_OPTIONS) || (option < 0)) + { + DBG (4, "sane_get_option_descriptor: option %d >= NUM_OPTIONS or < 0\n", + option); + return 0; + } + if (!s) + { + DBG (1, "sane_get_option_descriptor: handle is null!\n"); + return 0; + } + if (s->opt[option].name && s->opt[option].name[0] != 0) + DBG (5, "sane_get_option_descriptor for option %s (%sactive%s)\n", + s->opt[option].name, + s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "", + s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : ""); + else + DBG (5, "sane_get_option_descriptor for option \"%s\" (%sactive%s)\n", + s->opt[option].title, + s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "", + s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : ""); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, - SANE_Action action, void *val, SANE_Int *info) + SANE_Action action, void *val, SANE_Int * info) { Mustek_Scanner *s = handle; SANE_Status status; SANE_Word w, cap; + if (((unsigned) option >= NUM_OPTIONS) || (option < 0)) + { + DBG (4, "sane_control_option: option %d < 0 or >= NUM_OPTIONS\n", + option); + return SANE_STATUS_INVAL; + } + if (!s) + { + DBG (1, "sane_control_option: handle is null!\n"); + return SANE_STATUS_INVAL; + } + if (!val) + { + DBG (1, "sane_control_option: val is null!\n"); + return SANE_STATUS_INVAL; + } + + if (s->opt[option].name && s->opt[option].name[0] != 0) + DBG (5, "sane_control_option (%s option %s)\n", + action == SANE_ACTION_GET_VALUE ? "get" : + (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"), + s->opt[option].name); + else + DBG (5, "sane_control_option (%s option \"%s\")\n", + action == SANE_ACTION_GET_VALUE ? "get" : + (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"), + s->opt[option].title); + if (info) *info = 0; if (s->scanning) - return SANE_STATUS_DEVICE_BUSY; - - if (option >= NUM_OPTIONS) - return SANE_STATUS_INVAL; + { + DBG (4, "sane_control_option: don't use while scanning (option %s)\n", + s->opt[option].name); + return SANE_STATUS_DEVICE_BUSY; + } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) - return SANE_STATUS_INVAL; + { + DBG (4, "sane_control_option: option %s is inactive\n", + s->opt[option].name); + return SANE_STATUS_INVAL; + } if (action == SANE_ACTION_GET_VALUE) { @@ -3048,19 +5573,24 @@ sane_control_option (SANE_Handle handle, SANE_Int option, { /* word options: */ case OPT_PREVIEW: - case OPT_GRAY_PREVIEW: + case OPT_FAST_PREVIEW: case OPT_RESOLUTION: - case OPT_BACKTRACK: + case OPT_FAST_GRAY_MODE: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: - case OPT_GRAIN_SIZE: case OPT_BRIGHTNESS: + case OPT_BRIGHTNESS_R: + case OPT_BRIGHTNESS_G: + case OPT_BRIGHTNESS_B: case OPT_CONTRAST: + case OPT_CONTRAST_R: + case OPT_CONTRAST_G: + case OPT_CONTRAST_B: case OPT_CUSTOM_GAMMA: - case OPT_HALFTONE_DIMENSION: + case OPT_QUALITY_CAL: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; @@ -3077,6 +5607,8 @@ sane_control_option (SANE_Handle handle, SANE_Int option, case OPT_SPEED: case OPT_SOURCE: case OPT_MODE: + case OPT_BIT_DEPTH: + case OPT_HALFTONE_DIMENSION: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; } @@ -3084,11 +5616,19 @@ sane_control_option (SANE_Handle handle, SANE_Int option, else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) - return SANE_STATUS_INVAL; + { + DBG (4, "sane_control_option: option %s is not setable\n", + s->opt[option].name); + return SANE_STATUS_INVAL; + } status = constrain_value (s, option, val, info); if (status != SANE_STATUS_GOOD) - return status; + { + DBG (4, "sane_control_option: constrain_value error (option %s)\n", + s->opt[option].name); + return status; + } switch (option) { @@ -3102,11 +5642,17 @@ sane_control_option (SANE_Handle handle, SANE_Int option, *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_PREVIEW: - case OPT_GRAY_PREVIEW: - case OPT_BACKTRACK: - case OPT_GRAIN_SIZE: + case OPT_FAST_PREVIEW: + case OPT_FAST_GRAY_MODE: case OPT_BRIGHTNESS: + case OPT_BRIGHTNESS_R: + case OPT_BRIGHTNESS_G: + case OPT_BRIGHTNESS_B: case OPT_CONTRAST: + case OPT_CONTRAST_R: + case OPT_CONTRAST_G: + case OPT_CONTRAST_B: + case OPT_QUALITY_CAL: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; @@ -3124,15 +5670,34 @@ sane_control_option (SANE_Handle handle, SANE_Int option, if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); + if (!s->val[option].s) + return SANE_STATUS_NO_MEM; + return SANE_STATUS_GOOD; - /* options with side-effects: */ + /* side-effect-free string list options: */ + case OPT_BIT_DEPTH: + { + SANE_Char *old_val = s->val[option].s; + if (old_val) + { + if (strcmp (old_val, val) == 0) + return SANE_STATUS_GOOD; /* no change */ + free (old_val); + } + s->val[option].s = strdup (val); + if (!s->val[option].s) + return SANE_STATUS_NO_MEM; + return SANE_STATUS_GOOD; + } + + /* options with side-effects: */ case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA].w) - return SANE_STATUS_GOOD; /* no change */ + return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; @@ -3140,21 +5705,26 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->val[OPT_CUSTOM_GAMMA].w = w; if (w) { - const char *mode = s->val[OPT_MODE].s; + SANE_String_Const mode = s->val[OPT_MODE].s; if (strcmp (mode, "Gray") == 0) s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (mode, "Color") == 0) { - s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } + else if ((strcmp (val, "Lineart") == 0) + && (s->hw->flags & MUSTEK_FLAG_PRO)) + { + s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + } } else { - s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; @@ -3163,21 +5733,30 @@ sane_control_option (SANE_Handle handle, SANE_Int option, case OPT_MODE: { - char *old_val = s->val[option].s; - int halftoning, binary; + SANE_Char *old_val = s->val[option].s; + SANE_Int halftoning, binary; if (old_val) { if (strcmp (old_val, val) == 0) - return SANE_STATUS_GOOD; /* no change */ + return SANE_STATUS_GOOD; /* no change */ free (old_val); } - *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; s->val[option].s = strdup (val); + if (!s->val[option].s) + return SANE_STATUS_NO_MEM; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; @@ -3186,31 +5765,74 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; - halftoning = (strcmp (val, "Halftone") == 0 - || strcmp (val, "Color Halftone") == 0); - binary = (halftoning - || strcmp (val, "Lineart") == 0 - || strcmp (val, "Color Lineart") == 0); + halftoning = strcmp (val, "Halftone") == 0; + binary = (halftoning || strcmp (val, "Lineart") == 0); - if (!(s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) || binary) + if (binary) { - /* enable brightness/contrast for 3-pass scanners or - for 1-pass scanners when in a binary mode */ + /* enable brightness/contrast for when in a binary mode */ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; - /* The SE models support only threshold in lineart */ - if (!(s->hw->flags & MUSTEK_FLAG_SE)) + /* The SE and paragon models support only threshold + in lineart */ + if (!(s->hw->flags & MUSTEK_FLAG_SE) + && !(s->hw->flags & MUSTEK_FLAG_PRO)) s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; if (halftoning) { s->opt[OPT_HALFTONE_DIMENSION].cap &= ~SANE_CAP_INACTIVE; - if (s->val[OPT_HALFTONE_DIMENSION].w) - s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; + encode_halftone (s); + if (s->custom_halftone_pattern) + { + s->opt[OPT_HALFTONE_PATTERN].cap + &= ~SANE_CAP_INACTIVE; + } + else + { + s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; + } } } + else + { + s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + } - if (!binary) - s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) + { + if (strcmp (s->val[OPT_MODE].s, "Color") == 0) + { + s->opt[OPT_BRIGHTNESS_R].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_BRIGHTNESS_G].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_BRIGHTNESS_B].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST_R].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST_G].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST_B].cap &= ~SANE_CAP_INACTIVE; + } + else + { + s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; + } + } + else if (s->hw->flags & MUSTEK_FLAG_PRO) + { + if (strcmp (s->val[OPT_MODE].s, "Gray") == 0) + s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE; + else + s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE; + if (strcmp (s->val[OPT_MODE].s, "Color") == 0) + s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; + else + s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; + } + else if (s->hw->flags & MUSTEK_FLAG_SE_PLUS) + { + if (strcmp (s->val[OPT_MODE].s, "Color") == 0) + s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; + else + s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; + } if (s->val[OPT_CUSTOM_GAMMA].w) { @@ -3218,7 +5840,7 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (val, "Color") == 0) { - s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; @@ -3230,20 +5852,22 @@ sane_control_option (SANE_Handle handle, SANE_Int option, case OPT_HALFTONE_DIMENSION: /* halftone pattern dimension affects halftone pattern option: */ { - unsigned dim = *(SANE_Word *) val; - - if (s->val[option].w == dim) - return SANE_STATUS_GOOD; /* no change */ + if (strcmp (s->val[option].s, (SANE_String) val) == 0) + return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; - s->val[option].w = dim; + s->val[option].s = strdup (val); + if (!s->val[option].s) + return SANE_STATUS_NO_MEM; + encode_halftone (s); s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; - if (dim > 0) + if (s->custom_halftone_pattern) { s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; - s->opt[OPT_HALFTONE_PATTERN].size = dim * sizeof (SANE_Word); + s->opt[OPT_HALFTONE_PATTERN].size = + (s->halftone_pattern_type & 0x0f) * sizeof (SANE_Word); } return SANE_STATUS_GOOD; } @@ -3254,28 +5878,42 @@ sane_control_option (SANE_Handle handle, SANE_Int option, if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); + if (!s->val[option].s) + return SANE_STATUS_NO_MEM; - if (strcmp (val, source_list[TA]) == 0) + if (strcmp (val, "Transparency Adapter") == 0) { + s->opt[OPT_TL_X].constraint.range = &s->hw->x_trans_range; + s->opt[OPT_TL_Y].constraint.range = &s->hw->y_trans_range; s->opt[OPT_BR_X].constraint.range = &s->hw->x_trans_range; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_trans_range; } else { + s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; + s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; } return SANE_STATUS_GOOD; } } + DBG (4, "sane_control_option: unknown action for option %s\n", + s->opt[option].name); return SANE_STATUS_INVAL; } SANE_Status -sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Mustek_Scanner *s = handle; - const char *mode; + SANE_String_Const mode; + + if (!s) + { + DBG (1, "sane_get_parameters: handle is null!\n"); + return SANE_STATUS_INVAL; + } if (!s->scanning) { @@ -3296,6 +5934,7 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) s->params.pixels_per_line = width * dots_per_mm; s->params.lines = height * dots_per_mm; } + encode_halftone (s); mode = s->val[OPT_MODE].s; if (strcmp (mode, "Lineart") == 0 || strcmp (mode, "Halftone") == 0) { @@ -3313,39 +5952,41 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) { /* it's one of the color modes... */ - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - { - /* all color modes are treated the same since lineart and - halftoning with 1 bit color pixels still results in 3 - bytes/pixel. */ - s->params.format = SANE_FRAME_RGB; - s->params.bytes_per_line = 3 * s->params.pixels_per_line; - s->params.depth = 8; - } - else + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { s->params.format = SANE_FRAME_RED + s->pass; - if (strcmp (mode, "Color") == 0) + s->params.bytes_per_line = s->params.pixels_per_line; + s->params.depth = 8; + } + else /* 1-pass */ + { + if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) { - s->params.bytes_per_line = s->params.pixels_per_line; - s->params.depth = 8; + s->params.bytes_per_line = 6 * s->params.pixels_per_line; + s->params.depth = 16; } else { - s->params.bytes_per_line - = (s->params.pixels_per_line + 7) / 8; - s->params.depth = 1; + s->params.bytes_per_line = 3 * s->params.pixels_per_line; + s->params.depth = 8; } + s->params.format = SANE_FRAME_RGB; } } } else if ((s->mode & MUSTEK_MODE_COLOR) - && !(s->hw->flags & MUSTEK_FLAG_SINGLE_PASS)) + && (s->hw->flags & MUSTEK_FLAG_THREE_PASS)) s->params.format = SANE_FRAME_RED + s->pass; s->params.last_frame = (s->params.format != SANE_FRAME_RED && s->params.format != SANE_FRAME_GREEN); if (params) *params = s->params; + DBG (4, "sane_get_parameters: frame = %d; last_frame = %s; depth = %d\n", + s->params.format, s->params.last_frame ? "true" : "false", + s->params.depth); + DBG (4, "sane_get_parameters: lines = %d; ppl = %d; bpl = %d\n", + s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line); + return SANE_STATUS_GOOD; } @@ -3356,134 +5997,258 @@ sane_start (SANE_Handle handle) SANE_Status status; int fds[2]; + if (!s) + { + DBG (1, "sane_start: handle is null!\n"); + return SANE_STATUS_INVAL; + } + + DBG (4, "sane_start\n"); /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) - return status; + return status; + + /* Check for inconsistencies */ + + if (s->val[OPT_TL_X].w > s->val[OPT_BR_X].w) + { + DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) " + "-- aborting\n", + s->opt[OPT_TL_X].title, SANE_UNFIX (s->val[OPT_TL_X].w), + s->opt[OPT_BR_X].title, SANE_UNFIX (s->val[OPT_BR_X].w)); + return SANE_STATUS_INVAL; + } + if (s->val[OPT_TL_Y].w > s->val[OPT_BR_Y].w) + { + DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) " + "-- aborting\n", + s->opt[OPT_TL_Y].title, SANE_UNFIX (s->val[OPT_TL_Y].w), + s->opt[OPT_BR_Y].title, SANE_UNFIX (s->val[OPT_BR_Y].w)); + return SANE_STATUS_INVAL; + } + + s->total_bytes = 0; if (s->fd < 0) { /* this is the first (and maybe only) pass... */ - const char *mode; + SANE_String_Const mode; + struct timeval start; + /* save start time */ + gettimeofday (&start, 0); + s->start_time = start.tv_sec; /* translate options into s->mode for convenient access: */ mode = s->val[OPT_MODE].s; if (strcmp (mode, "Lineart") == 0) - s->mode = 0; + s->mode = MUSTEK_MODE_LINEART; else if (strcmp (mode, "Halftone") == 0) s->mode = MUSTEK_MODE_HALFTONE; else if (strcmp (mode, "Gray") == 0) - s->mode = MUSTEK_MODE_MULTIBIT; - else if (strcmp (mode, "Color Lineart") == 0) - s->mode = MUSTEK_MODE_COLOR; - else if (strcmp (mode, "Color Halftone") == 0) - s->mode = MUSTEK_MODE_COLOR | MUSTEK_MODE_HALFTONE; + s->mode = MUSTEK_MODE_GRAY; else if (strcmp (mode, "Color") == 0) - s->mode = MUSTEK_MODE_COLOR | MUSTEK_MODE_MULTIBIT; + s->mode = MUSTEK_MODE_COLOR; - s->one_pass_color_scan = 0; - if (s->mode & MUSTEK_MODE_COLOR) + /* scanner dependant specials */ + s->one_pass_color_scan = SANE_FALSE; + if ((s->mode & MUSTEK_MODE_COLOR) + && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { - if (s->val[OPT_PREVIEW].w && s->val[OPT_GRAY_PREVIEW].w) + s->one_pass_color_scan = SANE_TRUE; + } + + s->resolution_code = encode_resolution (s); + + if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w) + { + if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { - /* Force gray-scale mode when previewing. */ - s->mode &= ~MUSTEK_MODE_COLOR; - s->params.format = SANE_FRAME_GRAY; - s->params.bytes_per_line = s->params.pixels_per_line; - s->params.last_frame = SANE_TRUE; - if (!(s->mode & MUSTEK_MODE_MULTIBIT)) + if (s->mode & MUSTEK_MODE_COLOR) { - s->params.bytes_per_line = (s->params.pixels_per_line + 7)/8; - s->params.depth = 1; + /* Force gray-scale mode when previewing. */ + s->mode = MUSTEK_MODE_GRAY; + s->params.format = SANE_FRAME_GRAY; + s->params.bytes_per_line = s->params.pixels_per_line; + s->params.last_frame = SANE_TRUE; } } - else if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - s->one_pass_color_scan = 1; + else if (s->hw->flags & MUSTEK_FLAG_SE) + { + /* use 36 dpi color in any case */ + s->mode = MUSTEK_MODE_COLOR; + s->params.format = SANE_FRAME_RGB; + s->params.depth = 8; + s->one_pass_color_scan = SANE_TRUE; + s->resolution_code = 36; + } + else if (s->hw->flags & MUSTEK_FLAG_PARAGON_1) + { + /* use 36 dpi */ + s->resolution_code = 36; + } + else if (s->hw->flags & MUSTEK_FLAG_PRO) + { + /* use 30 dpi color mode */ + s->mode = MUSTEK_MODE_COLOR; + s->params.format = SANE_FRAME_RGB; + s->params.depth = 8; + s->one_pass_color_scan = SANE_TRUE; + s->resolution_code = 30; + } + DBG (4, "sane_start: use fast preview (res=%d dpi)\n", + s->resolution_code); } - s->resolution_code = encode_resolution (s); status = dev_open (s->hw->sane.name, s, sense_handler); if (status != SANE_STATUS_GOOD) return status; - } + } status = dev_wait_ready (s); if (status != SANE_STATUS_GOOD) { - DBG(1, "open: wait_ready() failed: %s\n", sane_strstatus (status)); + DBG (1, "sane_start: wait_ready() failed: %s\n", + sane_strstatus (status)); goto stop_scanner_and_return; } - status = do_inquiry (s); + status = inquiry (s); if (status != SANE_STATUS_GOOD) { - DBG(1, "open: inquiry command failed: %s\n", sane_strstatus (status)); + DBG (1, "sane_start: inquiry command failed: %s\n", + sane_strstatus (status)); goto stop_scanner_and_return; } - + + if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) && + !(s->hw->flags & MUSTEK_FLAG_ADF_READY)) + { + DBG (2, "sane_start: automatic document feeder is out of documents\n"); + status = SANE_STATUS_NO_DOCS; + goto stop_scanner_and_return; + } + if (s->hw->flags & MUSTEK_FLAG_SE) - { - status = do_set_window (s, 0); + { + status = set_window_se (s, 0); if (status != SANE_STATUS_GOOD) { - DBG(1, "open: set window command failed: %s\n", - sane_strstatus (status)); + DBG (1, "sane_start: set window command failed: %s\n", + sane_strstatus (status)); goto stop_scanner_and_return; } s->scanning = SANE_TRUE; - status = do_get_window (s, &s->params.bytes_per_line, - &s->params.lines, &s->params.pixels_per_line); + s->cancelled = SANE_FALSE; + + dev_wait_ready (s); + + status = get_window (s, &s->params.bytes_per_line, + &s->params.lines, &s->params.pixels_per_line); if (status != SANE_STATUS_GOOD) { - DBG(1, "open: get window command failed: %s\n", - sane_strstatus (status)); + DBG (1, "sane_start: get window command failed: %s\n", + sane_strstatus (status)); goto stop_scanner_and_return; } + status = calibration_se (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + status = start_scan (s); - if (status != SANE_STATUS_GOOD) + if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; - status = send_gamma_se (s); - if (status != SANE_STATUS_GOOD) + status = send_gamma_table_se (s); + if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; -/* - status = calibration (s); - if (status != SANE_STATUS_GOOD) - goto stop_scanner_and_return; */ } - else + + else if (s->hw->flags & MUSTEK_FLAG_PRO) { - status = scan_area_and_windows (s); + + status = dev_wait_ready (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = set_window_pro (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + s->scanning = SANE_TRUE; + s->cancelled = SANE_FALSE; + + status = adf_and_backtrack (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = mode_select_pro (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = scsi_sense_wait_ready (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = calibration_pro (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = send_gamma_table (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = start_scan (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = get_image_status (s, &s->params.bytes_per_line, + &s->params.lines); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + + status = scsi_sense_wait_ready (s); + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; + } + + else /* Paragon series */ + { + status = area_and_windows (s); if (status != SANE_STATUS_GOOD) { - DBG(1, "open: set scan area command failed: %s\n", - sane_strstatus (status)); + DBG (1, "sane_start: set scan area command failed: %s\n", + sane_strstatus (status)); goto stop_scanner_and_return; } - - status = backtrack_and_adf (s); + + status = adf_and_backtrack (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; if (s->one_pass_color_scan) { - status = mode_select (s, MUSTEK_CODE_RED); + status = mode_select_paragon (s, MUSTEK_CODE_RED); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; - status = mode_select (s, MUSTEK_CODE_GREEN); + status = mode_select_paragon (s, MUSTEK_CODE_GREEN); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; - status = mode_select (s, MUSTEK_CODE_BLUE); + status = mode_select_paragon (s, MUSTEK_CODE_BLUE); } else - status = mode_select (s, MUSTEK_CODE_GRAY); + status = mode_select_paragon (s, MUSTEK_CODE_GRAY); + + if (status != SANE_STATUS_GOOD) + goto stop_scanner_and_return; s->scanning = SANE_TRUE; + s->cancelled = SANE_FALSE; status = send_gamma_table (s); if (status != SANE_STATUS_GOOD) @@ -3498,34 +6263,29 @@ sane_start (SANE_Handle handle) goto stop_scanner_and_return; s->ld.max_value = 0; - if (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { status = line_distance (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; } - status = backtrack_and_adf (s); - if (status != SANE_STATUS_GOOD) - goto stop_scanner_and_return; - - status = get_image_status (s, &s->params.bytes_per_line, + status = get_image_status (s, &s->params.bytes_per_line, &s->params.lines); if (status != SANE_STATUS_GOOD) - goto stop_scanner_and_return; + goto stop_scanner_and_return; } - if ((s->mode & (MUSTEK_MODE_COLOR | MUSTEK_MODE_MULTIBIT)) - == MUSTEK_MODE_COLOR - && (s->hw->flags & MUSTEK_FLAG_SINGLE_PASS)) - /* In single-bit, single-pass color mode we expand every bit of - information into a byte (0x00 or 0xff). */ - s->params.bytes_per_line *= 8; - s->params.pixels_per_line = s->params.bytes_per_line; if (s->one_pass_color_scan) - s->params.pixels_per_line /= 3; - else if (!(s->mode & MUSTEK_MODE_MULTIBIT)) + { + if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) + s->params.pixels_per_line /= 6; + else + s->params.pixels_per_line /= 3; + } + else if ((s->mode & MUSTEK_MODE_LINEART) + || (s->mode & MUSTEK_MODE_HALFTONE)) s->params.pixels_per_line *= 8; s->line = 0; @@ -3533,9 +6293,16 @@ sane_start (SANE_Handle handle) if (pipe (fds) < 0) return SANE_STATUS_IO_ERROR; +#ifndef HAVE_OS2_H s->reader_pid = fork (); +#else + /* create reader routine as new process or thread */ + s->reader_fds = fds[1]; + s->reader_pid = sanei_thread_begin (os2_reader_process, (void *) s); +#endif if (s->reader_pid == 0) { + SANE_Int status; sigset_t ignore_set; struct SIGACTION act; @@ -3544,14 +6311,17 @@ sane_start (SANE_Handle handle) sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); sigprocmask (SIG_SETMASK, &ignore_set, 0); - memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); + status = reader_process (s, fds[1]); + /* don't use exit() since that would run the atexit() handlers... */ - _exit (reader_process (s, fds[1])); + _exit (status); } +#ifndef HAVE_OS2_H close (fds[1]); +#endif s->pipe = fds[0]; return SANE_STATUS_GOOD; @@ -3562,44 +6332,115 @@ stop_scanner_and_return: } SANE_Status -sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, + SANE_Int * len) { Mustek_Scanner *s = handle; SANE_Status status; + ssize_t ntotal; ssize_t nread; - *len = 0; - nread = read (s->pipe, buf, max_len); - DBG(3, "read %ld bytes\n", (long) nread); + if (!s) + { + DBG (1, "sane_read: handle is null!\n"); + return SANE_STATUS_INVAL; + } + + if (!buf) + { + DBG (1, "sane_read: buf is null!\n"); + return SANE_STATUS_INVAL; + } + + if (!len) + { + DBG (1, "sane_read: len is null!\n"); + return SANE_STATUS_INVAL; + } + + DBG (5, "sane_read\n"); + *len = 0; + ntotal = 0; + + if (s->cancelled) + { + DBG (4, "sane_read: scan was cancelled\n"); + return SANE_STATUS_CANCELLED; + } if (!s->scanning) - return do_stop (s); - - if (nread < 0) { - if (errno == EAGAIN) - return SANE_STATUS_GOOD; - else - { - do_stop (s); - return SANE_STATUS_IO_ERROR; - } + DBG (3, "sane_read: must call sane_start before sane_read\n"); + return SANE_STATUS_INVAL; } - *len = nread; - - if (nread == 0) + while (*len < max_len) { - if ((s->hw->flags & MUSTEK_FLAG_SINGLE_PASS) - || !(s->mode & MUSTEK_MODE_COLOR) || ++s->pass >= 3) + nread = read (s->pipe, buf + *len, max_len - *len); + + if (s->cancelled) { - status = do_stop (s); - if (status != SANE_STATUS_CANCELLED && status != SANE_STATUS_GOOD) - return status; /* something went wrong */ + DBG (4, "sane_read: scan was cancelled\n"); + *len = 0; + return SANE_STATUS_CANCELLED; + } + + if (nread < 0) + { + if (errno == EAGAIN) + { + if (*len == 0) + DBG (5, "sane_read: no more data at the moment--try again\n"); + else + DBG (5, "sane_read: read buffer of %d bytes " + "(%d bytes total)\n", *len, s->total_bytes); + return SANE_STATUS_GOOD; + } + else + { + DBG (1, "sane_read: IO error\n"); + do_stop (s); + *len = 0; + return SANE_STATUS_IO_ERROR; + } + } + + *len += nread; + s->total_bytes += nread; + + if (nread == 0) + { + if (*len == 0) + { + if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS) + || !(s->mode & MUSTEK_MODE_COLOR) || ++s->pass >= 3) + { + DBG (5, "sane_read: pipe was closed ... calling do_stop\n"); + status = do_stop (s); + if (status != SANE_STATUS_CANCELLED + && status != SANE_STATUS_GOOD) + return status; /* something went wrong */ + } + else /* 3pass color first or second pass */ + { + DBG (5, + "sane_read: pipe was closed ... finishing pass %d\n", + s->pass); + } + + return do_eof (s); + } + else + { + DBG (5, "sane_read: read last buffer of %d bytes " + "(%d bytes total)\n", *len, s->total_bytes); + return SANE_STATUS_GOOD; + } } - return do_eof (s); } + DBG (5, "sane_read: read full buffer of %d bytes (%d total bytes)\n", + *len, s->total_bytes); return SANE_STATUS_GOOD; } @@ -3608,13 +6449,19 @@ sane_cancel (SANE_Handle handle) { Mustek_Scanner *s = handle; - if (s->reader_pid > 0) + if (!s) { - kill (s->reader_pid, SIGTERM); - waitpid (s->reader_pid, 0, 0); - s->reader_pid = 0; + DBG (1, "sane_cancel: handle is null!\n"); + return; } - s->scanning = SANE_FALSE; + + DBG (4, "sane_cancel\n"); + if (s->scanning) + { + s->cancelled = SANE_TRUE; + do_stop (handle); + } + DBG (4, "sane_cancel finished\n"); } SANE_Status @@ -3622,20 +6469,47 @@ sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Mustek_Scanner *s = handle; + if (!s) + { + DBG (1, "sane_set_io_mode: handle is null!\n"); + return SANE_STATUS_INVAL; + } + + DBG (4, "sane_set_io_mode: %s\n", + non_blocking ? "non-blocking" : "blocking"); + if (!s->scanning) - return SANE_STATUS_INVAL; + { + DBG (1, "sane_set_io_mode: call sane_start before sane_set_io_mode"); + return SANE_STATUS_INVAL; + } if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) - return SANE_STATUS_IO_ERROR; + { + DBG (1, "sane_set_io_mode: can't set io mode"); + return SANE_STATUS_IO_ERROR; + } return SANE_STATUS_GOOD; } SANE_Status -sane_get_select_fd (SANE_Handle handle, SANE_Int *fd) +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Mustek_Scanner *s = handle; + if (!s) + { + DBG (1, "sane_get_select_fd: handle is null!\n"); + return SANE_STATUS_INVAL; + } + if (!fd) + { + DBG (1, "sane_get_select_fd: fd is null!\n"); + return SANE_STATUS_INVAL; + } + + DBG (4, "sane_get_select_fd\n"); if (!s->scanning) return SANE_STATUS_INVAL; diff --git a/doc/descriptions/mustek.desc b/doc/descriptions/mustek.desc index 89e2b0ea2..ee517e73d 100644 --- a/doc/descriptions/mustek.desc +++ b/doc/descriptions/mustek.desc @@ -9,7 +9,7 @@ ; :backend "mustek" ; name of backend -:version "1.0-128" ; version of backend +:version "1.0-129" ; version of backend :status :stable ; :alpha, :beta, :stable, :new :manpage "sane-mustek" ; name of manpage (if it exists) :url "http://www.meier-geinitz.de/sane/" diff --git a/doc/mustek/mustek.CHANGES b/doc/mustek/mustek.CHANGES index 5c21fed0d..29a72ee07 100644 --- a/doc/mustek/mustek.CHANGES +++ b/doc/mustek/mustek.CHANGES @@ -1,5 +1,13 @@ CHANGES for the SANE Mustek backend +2002-11-07 + * Released Mustek backend 1.0-129 + +2002-11-05 + * Upload linear gamma table for Pro models if custom gamma is off instead + of uploading the composed gamma table. That avoids applying gamm twice. + * Minor man page update. + 2002-10-11 * Released Mustek backend 1.0-128 diff --git a/doc/sane-mustek.man b/doc/sane-mustek.man index 05b65b27c..773f85d9f 100644 --- a/doc/sane-mustek.man +++ b/doc/sane-mustek.man @@ -1,4 +1,4 @@ -.TH sane-mustek 5 "22 Dec 2001" +.TH sane-mustek 5 "5 Nov 2002" .IX sane-mustek .SH NAME sane-mustek - SANE backend for Mustek flatbed scanners @@ -366,6 +366,8 @@ high resolutions and wide scanareas. Some scanners (e.g. Paragon 1200 A3 + Pro, SE A3) need more testing. +The gamma table supports only 256 colors, even if some scanners can do more. + More detailed bug information is available at the Mustek backend homepage .IR http://www.meier-geinitz.de/sane/ .