From 8e1194f427f89f73ea71e58bf95d24cd7606c47b Mon Sep 17 00:00:00 2001 From: Henning Geinitz Date: Sat, 4 Oct 2003 10:40:00 +0000 Subject: [PATCH] Added back old mustek_pp backend. That backend supports Mustek CCD scanners. The backend was renamed to mustek_pp_ccd to avoid conflicts with the new mustek_pp backend for CIS scanners. The code was taken from sane-backends 1.0.9. This is only a temporary solution. The goal is to incooperate CCD and CIS support into mustek_pp. --- AUTHORS | 1 + backend/Makefile.in | 3 + backend/dll.conf | 1 + backend/mustek_pp_ccd.c | 4340 +++++++++++++++++++++++++++ backend/mustek_pp_ccd.conf | 99 + backend/mustek_pp_ccd.h | 296 ++ configure | 4 +- configure.in | 4 +- doc/Makefile.in | 6 +- doc/descriptions/mustek_pp_ccd.desc | 77 + doc/sane-mustek_pp_ccd.man | 474 +++ doc/sane.man | 11 +- 12 files changed, 5308 insertions(+), 8 deletions(-) create mode 100644 backend/mustek_pp_ccd.c create mode 100644 backend/mustek_pp_ccd.conf create mode 100644 backend/mustek_pp_ccd.h create mode 100644 doc/descriptions/mustek_pp_ccd.desc create mode 100644 doc/sane-mustek_pp_ccd.man diff --git a/AUTHORS b/AUTHORS index 5db5695ff..78c6a812a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,6 +46,7 @@ Backends: mustek: Andreas Bolsch, David Mosberger, Andreas Czechanowski and Henning Meier-Geinitz (*) mustek_pp: Jochen Eisinger (*), Eddy De Greef (*) + mustek_pp_ccd: Jochen Eisinger (*) mustek_usb: Tom Wang and Henning Meier-Geinitz (*) nec: Kazuya Fukuda (*) net: Andreas Beck and David Mosberger diff --git a/backend/Makefile.in b/backend/Makefile.in index 071580665..046993a70 100644 --- a/backend/Makefile.in +++ b/backend/Makefile.in @@ -113,6 +113,7 @@ DISTFILES = abaton.c abaton.conf abaton.h agfafocus.c agfafocus.conf \ Makefile.in matsushita.c \ matsushita.conf matsushita.h microtek2.c microtek2.conf microtek2.h \ microtek.c microtek.conf microtek.h mustek.c mustek.conf mustek.h \ + mustek_pp_ccd.c mustek_pp_ccd.conf mustek_pp_ccd.h \ mustek_pp.c mustek_pp.conf mustek_pp.h mustek_pp_cis.c mustek_pp_cis.h \ mustek_pp_decl.h mustek_pp_drivers.h mustek_pp_null.c mustek_pp_ccd300.c \ mustek_pp_ccd300.h mustek_usb.c mustek_usb.conf \ @@ -345,6 +346,8 @@ libsane-mustek.la: ../sanei/sanei_scsi.lo libsane-mustek.la: ../sanei/sanei_ab306.lo libsane-mustek_pp.la: ../sanei/sanei_constrain_value.lo libsane-mustek_pp.la: ../sanei/sanei_pa4s2.lo +libsane-mustek_pp_ccd.la: ../sanei/sanei_constrain_value.lo +libsane-mustek_pp_ccd.la: ../sanei/sanei_pa4s2.lo libsane-mustek_usb.la: ../sanei/sanei_constrain_value.lo libsane-mustek_usb.la: ../sanei/sanei_usb.lo libsane-nec.la: ../sanei/sanei_config2.lo diff --git a/backend/dll.conf b/backend/dll.conf index 9e8a746d0..2635804a7 100644 --- a/backend/dll.conf +++ b/backend/dll.conf @@ -32,6 +32,7 @@ microtek microtek2 mustek #mustek_pp +#mustek_pp_ccd mustek_usb nec pie diff --git a/backend/mustek_pp_ccd.c b/backend/mustek_pp_ccd.c new file mode 100644 index 000000000..33fe504a9 --- /dev/null +++ b/backend/mustek_pp_ccd.c @@ -0,0 +1,4340 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2000 Jochen Eisinger + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. + + This file implements a SANE backend for Mustek PP flatbed scanners. */ + +#include "../include/sane/config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_pa4s2.h" + +#define BACKEND_NAME mustek_pp_ccd +#include "../include/sane/sanei_backend.h" + +#include "../include/sane/sanei_config.h" +#define MUSTEK_PP_CONFIG_FILE "mustek_pp_ccd.conf" + +#include "mustek_pp_ccd.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +/* #define if you want to patch for ASIC 1505 */ +/* #undef PATCH_MUSTEK_PP_1505 */ + +/* #define if you want authorization support */ +/* please note: + * to use user authorization, you need to modify + * a) SANED: the protocoll doesn't realy support auth + * b) NET: ...nor does the backend + * c) your frontend: at least (x)scanimage doesn't support it + * d) the makefiles: libcrypt is needed for crypt() + * + * -> so, don't do it... + * + * the format of the file .auth is + * ressource:user:password + * ressource:user:password + * ... + * + * where password is encoded using crypt() + * + * please note, that the password is passed to the backend in plain text + * + */ +/* #undef HAVE_AUTHORIZATION */ + +/* #define for invert option + * inversion doesn't really work */ +/* #undef HAVE_INVERSION */ + +/* TODO: + * - perhaps option bw should be set at runtime (i.e. SANE_OPT_TRESHOLD) + * - remove line ending handling code + * - test with 1015/ CCD 04&05 scanners + * - add 1505 code + * - sane_read should operate on a fd + * - there is an transparency adapter -> support it + * - make it possible to turn lamp off during scan + * - add API docu (what's this?) + * - make the user authentification work + * - make use of ppdev + * + * The primary goal at the moment is to support ASIC 1015 - CCD 01 + */ + +/* DEBUG + * for debug output, set SANE_DEBUG_MUSTEK_PP to + * 0 for nothing + * 1 for errors + * 2 for warnings + * 3 for additional information + * 4 for debug information + * 5 for code flow protocoll (there isn't any) + * 6 old debug info + * 129 if you want to know which parameters are unused + */ + +/* history: + * 1.0.0-devel SANE backend structure taken from mustek backend + * 1.0.1-devel hardware functions taken from various sources + * 1.0.2-devel ld correction works now for ASIC 1015 + * 1.0.3-alpha code tested for ASIC 1015 CCD-Type 0 + * 1.0.4-devel added lots of options + * 1.0.5-devel removed AUTO options, added option niceload & auth + * fixed bug in set_ccd_channel_1013 + * 1.0.6-alpha fixed bug in sane_cancel, changed return_home + * fixed bug in return_home_1013, added option wait-lamp + * added motor control codes for ASIC 1015 CCD 01 + * fixed bug in config_ccd_1013 & config_ccd_1015 + * added resolution code to attach & sane_get_parameters + * 1.0.7-devel added functions common the both 101x chipsets + * added option bw, fixed bug in sane_open + * fixed bug in sane_exit + * 1.0.8-devel added some code to config_ccd_1015 & set_dpi_value + * replaced fgets by sanei_config_read + * replaced #include by #include "sane/..." + * added option use600 + * 1.0.9-devel added tons off 600 dpi code: kind of works for + * ASIC 1015 CCD 01 scanners (grayscale) + * option preview defaults to color mode + * replaced #include "sane/..." by + * #include "../include/sane/..." + */ + +/* if you change the source, please set MUSTEK_PP_STATE to "devel". Do *not* + * change the MUSTEK_PP_BUILD. */ +#define MUSTEK_PP_BUILD 9 +#define MUSTEK_PP_STATE "devel" + +static int num_devices = 0; + +static Mustek_PP_Descriptor *devlist = NULL; +static SANE_Device **devarray = NULL; + +static Mustek_PP_Device *first_dev = NULL; + +static int strip_height = 0; +static unsigned int wait_bank = 700; + +/* 1 Meg scan buffer */ +static long int buf_size = 1024 * 1024; + +static int niceload = 0; + +static int wait_lamp = 5; + +static int bw = 127; + +#ifdef HAVE_AUTHORIZATION +static SANE_Auth_Callback auth_callback; +#endif + +/* use600 test vars */ +/* static char motor_command=0x43; */ +/* static int first_time=1; */ +/* static char unk1= 0xAA; */ +/* static char expose_time = 0x64; */ +/* static char volt[3]; */ +/*static int do_calib=0; */ +static void config_ccd_101x (Mustek_PP_Device * dev); +static void config_ccd (Mustek_PP_Device * dev); +static void lamp (Mustek_PP_Device * dev, int lamp_on); +/* end of test vars */ + +static const SANE_String_Const mode_list[] = { + "Lineart", "Gray", "Color", 0 +}; + +static const SANE_Range u8_range = { + 0, /* minimum */ + 255, /* maximum */ + 0 /* quantization */ +}; + + +#define MUSTEK_PP_CHANNEL_RED 0 +#define MUSTEK_PP_CHANNEL_GREEN 1 +#define MUSTEK_PP_CHANNEL_BLUE 2 +#define MUSTEK_PP_CHANNEL_GRAY 1 + +#define MUSTEK_PP_STATE_SCANNING 2 +#define MUSTEK_PP_STATE_CANCELLED 1 +#define MUSTEK_PP_STATE_IDLE 0 + +#define MUSTEK_PP_MODE_LINEART 0 +#define MUSTEK_PP_MODE_GRAYSCALE 1 +#define MUSTEK_PP_MODE_COLOR 2 + +#define MM_PER_INCH 25.4 +#define MM_TO_PIXEL(mm, res) (SANE_UNFIX(mm) * (float )res / MM_PER_INCH) +#define PIXEL_TO_MM(px, res) (SANE_FIX((float )(px * MM_PER_INCH / (res / 10)) / 10.0)) + +/* scan area is mixture of 8.5" x 11" (letter) and 8.2" x 11.6" (a4) */ +/* actually it is 8.7" x 11.6" ;-) */ +#define MUSTEK_PP_101x_MAX_H_PIXEL 2600 +#define MUSTEK_PP_101x_MAX_V_PIXEL 3500 + +#define MUSTEK_PP_DEFAULT_PORT 0x378 + + +#define MUSTEK_PP_ASIC_1013 0xA8 +#define MUSTEK_PP_ASIC_1015 0xA5 +#define MUSTEK_PP_ASIC_1505 0xA2 + + +/* + * Here starts the driver code for the different chipsets + * + * The 1013 & 1015 chipsets share large portions of the code. This + * shared functions end with _101x. + */ + +static const u_char chan_codes_1013[] = { 0x82, 0x42, 0xC2 }; +static const u_char chan_codes_1015[] = { 0x80, 0x40, 0xC0 }; +static const u_char fullstep[] = { 0x09, 0x0C, 0x06, 0x03 }; +static const u_char halfstep[] = { 0x02, 0x03, 0x01, 0x09, + 0x08, 0x0C, 0x04, 0x06 +}; +static const u_char voltages[4][3] = { {0x5C, 0x5A, 0x63}, +{0xE6, 0xB4, 0xBE}, +{0xB4, 0xB4, 0xB4}, +{0x64, 0x50, 0x64} +}; + +/* Forward declarations of 1013/1015 functions */ +static void set_ccd_channel_1013 (Mustek_PP_Device * dev, int channel); +static void motor_backward_1013 (Mustek_PP_Device * dev); +static void return_home_1013 (Mustek_PP_Device * dev, SANE_Bool nowait); +static void motor_forward_1013 (Mustek_PP_Device * dev); +static void config_ccd_1013 (Mustek_PP_Device * dev); + +static void set_ccd_channel_1015 (Mustek_PP_Device * dev, int channel); +/* static void motor_backward_1015 (Mustek_PP_Device * dev); */ +static void return_home_1015 (Mustek_PP_Device * dev, SANE_Bool nowait); +static void motor_forward_1015 (Mustek_PP_Device * dev); +static void config_ccd_1015 (Mustek_PP_Device * dev); + + +/* These functions are common to all 1013/1015 chipsets */ + +/* +static void +set_led (Mustek_PP_Device * dev) +{ + + sanei_pa4s2_writebyte (dev->fd, 6, + (dev->motor_step % 5 == 0 ? 0x03 : 0x13)); + +}*/ + +#define set_led(dev) sanei_pa4s2_writebyte (dev->fd, 6, \ + (dev->motor_step % 5 == 0 ? 0x03 : 0x13)) + +/* +static void +set_sti (Mustek_PP_Device * dev) +{ + + sanei_pa4s2_writebyte (dev->fd, 3, 0); + dev->bank_count++; + dev->bank_count &= 7; + +}*/ + +#define set_sti(dev) do { \ + if (dev->desc->use600 == SANE_TRUE) \ + sanei_pa4s2_writebyte(dev->fd, 3, 0xFF); \ + else \ + sanei_pa4s2_writebyte (dev->fd, 3, 0); \ + dev->bank_count++; \ + dev->bank_count &= 7; \ + } while (0) +/* +static void +get_bank_count (Mustek_PP_Device * dev) +{ + + u_char val; + + sanei_pa4s2_readbegin (dev->fd, 3); + sanei_pa4s2_readbyte (dev->fd, &val); + sanei_pa4s2_readend (dev->fd); + + dev->bank_count = (val & 0x07); + +}*/ +#define get_bank_count(dev) do { \ + sanei_pa4s2_readbegin (dev->fd, 3); \ + sanei_pa4s2_readbyte \ + (dev->fd, \ + (u_char *)&dev->bank_count); \ + sanei_pa4s2_readend (dev->fd); \ + dev->bank_count &= 0x07; \ + } while (0) + +/* +static void +reset_bank_count (Mustek_PP_Device * dev) +{ + + sanei_pa4s2_writebyte (dev->fd, 6, 7); + +}*/ +#define reset_bank_count(dev) sanei_pa4s2_writebyte (dev->fd, 6, 7) + + +static void +wait_bank_change (Mustek_PP_Device * dev, int bankcount) +{ + struct timeval start, end; + unsigned long diff; + int firsttime = 1; + + if (dev->desc->use600) + { + + if (niceload) + usleep (2); + + return; + + } + + gettimeofday (&start, NULL); + + do + { + if (niceload) + { + if (firsttime) + firsttime = 0; + else + usleep (10); /* for a little nicer load */ + } + get_bank_count (dev); + + gettimeofday (&end, NULL); + diff = (end.tv_sec * 1000 + end.tv_usec / 1000) - + (start.tv_sec * 1000 + start.tv_usec / 1000); + + } + while ((dev->bank_count != bankcount) && (diff < dev->desc->wait_bank)); + +} + +static void +set_dpi_value (Mustek_PP_Device * dev) +{ + u_char val = 0; + + sanei_pa4s2_writebyte (dev->fd, 6, 0x80); + + switch (dev->CCD.hwres * (600 / dev->desc->max_res)) + { + case 100: + val = 0x08; + break; + case 200: + val = 0x00; + break; + case 300: + val = 0x50; + break; + case 400: + val = 0x10; + break; + case 600: + val = 0x20; + break; + /* + case 100: val=0x00; break; + case 200: val=0x10; break; + case 300: val=0x20; break; */ + } + + + if (dev->ccd_type == 1) + val |= 0x01; + + sanei_pa4s2_writebyte (dev->fd, 5, val); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + DBG (4, "set_dpi_value: value 0x%02x\n", val); + +} + +static void +set_line_adjust (Mustek_PP_Device * dev) +{ + int adjustline; + + + + if (dev->desc->use600 == SANE_FALSE) + { + adjustline = + (dev->BottomX - dev->TopX) * dev->CCD.hwres / dev->desc->max_res; + dev->CCD.adjustskip = + dev->CCD.adjustskip * dev->CCD.hwres / dev->desc->max_res; + } + else + { + + adjustline = dev->params.pixels_per_line; + + adjustline = adjustline * dev->CCD.hwres / dev->CCD.res; + + if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + adjustline <<= 3; + +/* adjustline = adjustline * dev->CCD.hwres / dev->CCD.res; */ + adjustline += 2; + + dev->CCD.adjustskip = + dev->CCD.adjustskip * dev->CCD.hwres / dev->CCD.res; + + DBG (4, "set_line_adjust: value = %u (0x%04x)\n", adjustline, + adjustline); + + } + + DBG (4, "set_line_adjust: ppl %u (%u), adjust %u, skip %u\n", + dev->params.pixels_per_line, (dev->BottomX - dev->TopX), adjustline, + dev->CCD.adjustskip); + + + sanei_pa4s2_writebyte (dev->fd, 6, 0x11); + sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + dev->CCD.adjustskip) >> 8); + sanei_pa4s2_writebyte (dev->fd, 6, 0x21); + sanei_pa4s2_writebyte (dev->fd, 5, + (adjustline + dev->CCD.adjustskip) & 0xFF); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x01); + +} + +static void +set_lamp (Mustek_PP_Device * dev, int lamp_on) +{ + + int ctr; + + if (dev->desc->use600 == SANE_FALSE) + sanei_pa4s2_writebyte (dev->fd, 6, 0xC3); + + for (ctr = 0; ctr < 3; ctr++) + { + sanei_pa4s2_writebyte (dev->fd, 6, (lamp_on ? 0x47 : 0x57)); + if (dev->desc->use600 == SANE_FALSE) + sanei_pa4s2_writebyte (dev->fd, 6, 0x77); + } + + dev->motor_step = lamp_on; + + if (dev->desc->use600 == SANE_FALSE) + set_led (dev); + +} + +static void +send_voltages (Mustek_PP_Device * dev) +{ + + int voltage, sel = 8, ctr; + + switch (dev->ccd_type) + { + case 0: + voltage = 0; + break; + case 1: + voltage = 1; +/* if (dev->asic_id == MUSTEK_PP_ASIC_1015) + voltage = 3; */ + break; + default: + voltage = 2; + break; + } + + for (ctr = 0; ctr < 3; ctr++) + { + + sel <<= 1; + sanei_pa4s2_writebyte (dev->fd, 6, sel); + sanei_pa4s2_writebyte (dev->fd, 5, voltages[voltage][ctr]); + + } + + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + +} + +static int +compar (const void *a, const void *b) +{ + return (signed int) (*(const SANE_Byte *) a) - + (signed int) (*(const SANE_Byte *) b); +} + +static void +set_ccd_channel_101x (Mustek_PP_Device * dev, int channel) +{ + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + set_ccd_channel_1013 (dev, channel); + break; + + case MUSTEK_PP_ASIC_1015: + set_ccd_channel_1015 (dev, channel); + break; + } +} + +static void +motor_forward_101x (Mustek_PP_Device * dev) +{ + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + motor_forward_1013 (dev); + break; + + case MUSTEK_PP_ASIC_1015: + motor_forward_1015 (dev); + break; + } +} + +static void +motor_backward_101x (Mustek_PP_Device * dev) +{ + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + motor_backward_1013 (dev); + break; + + case MUSTEK_PP_ASIC_1015: +/* motor_backward_1015 (dev); */ + break; + } +} + +static void +move_motor_101x (Mustek_PP_Device * dev, int forward) +{ + if (forward == SANE_TRUE) + motor_forward_101x (dev); + else + motor_backward_101x (dev); + + wait_bank_change (dev, dev->bank_count); + reset_bank_count (dev); +} + + +static void +config_ccd_101x (Mustek_PP_Device * dev) +{ + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + config_ccd_1013 (dev); + break; + + case MUSTEK_PP_ASIC_1015: + config_ccd_1015 (dev); + break; + } +} + + + +static void +read_line_101x (Mustek_PP_Device * dev, SANE_Byte * buf, SANE_Int pixel, + SANE_Int RefBlack, SANE_Byte * calib, SANE_Int * gamma) +{ + + SANE_Byte *cal = calib; + u_char color; + int ctr, skips = dev->CCD.adjustskip + 1, cval; + + if (pixel <= 0) + return; + + sanei_pa4s2_readbegin (dev->fd, 1); + + + if (dev->desc->use600 == SANE_TRUE) + { + + + if (dev->CCD.hwres == dev->CCD.res) + { + + while (skips--) + sanei_pa4s2_readbyte (dev->fd, &color); + + for (ctr = 0; ctr < pixel; ctr++) + sanei_pa4s2_readbyte (dev->fd, &buf[ctr]); + + } + else + { + + int pos = 0, bpos = 0; + + while (skips--) + sanei_pa4s2_readbyte (dev->fd, &color); + + ctr = 0; + + do + { + + sanei_pa4s2_readbyte (dev->fd, &color); + + if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT)) + { + ctr++; + continue; + } + + ctr++; + pos += dev->CCD.res_step; + + + buf[bpos++] = color; + + } + while (bpos < pixel); + + } + + sanei_pa4s2_readend (dev->fd); + + return; + + } + + if (dev->CCD.hwres == dev->CCD.res) + { + + while (skips--) + sanei_pa4s2_readbyte (dev->fd, &color); + + for (ctr = 0; ctr < pixel; ctr++) + { + + sanei_pa4s2_readbyte (dev->fd, &color); + + cval = color; + + if (cval < RefBlack) + cval = 0; + else + cval -= RefBlack; + + if (cal) + { + if (cval >= cal[ctr]) + cval = 0xFF; + else + { + cval <<= 8; + cval /= (int) cal[ctr]; + } + } + + if (gamma) + cval = gamma[cval]; + + buf[ctr] = cval; + + } + + } + else + { + + int pos = 0, bpos = 0; + + while (skips--) + sanei_pa4s2_readbyte (dev->fd, &color); + + ctr = 0; + + do + { + + sanei_pa4s2_readbyte (dev->fd, &color); + + cval = color; + + if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT)) + { + ctr++; + continue; + } + + ctr++; + pos += dev->CCD.res_step; + + + if (cval < RefBlack) + cval = 0; + else + cval -= RefBlack; + + if (cal) + { + if (cval >= cal[bpos]) + cval = 0xFF; + else + { + cval <<= 8; + cval /= (int) cal[bpos]; + } + } + + if (gamma) + cval = gamma[cval]; + + buf[bpos++] = cval; + + } + while (bpos < pixel); + + } + + sanei_pa4s2_readend (dev->fd); + +} + +static void +read_average_line_101x (Mustek_PP_Device * dev, SANE_Byte * buf, int pixel, + int RefBlack) +{ + + SANE_Byte lbuf[4][MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + int ctr, sum; + + for (ctr = 0; ctr < 4; ctr++) + { + + wait_bank_change (dev, dev->bank_count); + read_line_101x (dev, lbuf[ctr], pixel, RefBlack, NULL, NULL); + reset_bank_count (dev); + if (ctr < 3) + set_sti (dev); + + } + + for (ctr = 0; ctr < pixel; ctr++) + { + + sum = lbuf[0][ctr] + lbuf[1][ctr] + lbuf[2][ctr] + lbuf[3][ctr]; + + buf[ctr] = (sum / 4); + + } + +} + +static void +find_black_side_edge_101x (Mustek_PP_Device * dev) +{ + SANE_Byte buf[MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + SANE_Byte blackposition[5]; + int pos = 0, ctr, blackpos; + + for (ctr = 0; ctr < 20; ctr++) + { + + motor_forward_101x (dev); + wait_bank_change (dev, dev->bank_count); + read_line_101x (dev, buf, dev->desc->max_h_size, 0, NULL, NULL); + reset_bank_count (dev); + + dev->ref_black = dev->ref_red = dev->ref_green = dev->ref_blue = buf[0]; + + blackpos = dev->desc->max_h_size / 4; + + while ((abs (buf[blackpos] - buf[0]) >= 15) && (blackpos > 0)) + blackpos--; + + if (blackpos > 1) + blackposition[pos++] = blackpos; + + if (pos == 5) + break; + + } + + blackpos = 0; + + for (ctr = 0; ctr < pos; ctr++) + if (blackposition[ctr] > blackpos) + blackpos = blackposition[ctr]; + + if (blackpos < 0x66) + blackpos = 0x6A; + + dev->blackpos = blackpos; + dev->Saved_CCD.skipcount = (blackpos + 12) & 0xFF; + +} + +static void +min_color_levels_101x (Mustek_PP_Device * dev) +{ + + SANE_Byte buf[MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + int ctr, sum = 0; + + for (ctr = 0; ctr < 8; ctr++) + { + + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_RED); + set_sti (dev); + wait_bank_change (dev, dev->bank_count); + + read_line_101x (dev, buf, dev->desc->max_h_size, 0, NULL, NULL); + + reset_bank_count (dev); + + sum += buf[3]; + + } + + dev->ref_red = sum / 8; + + sum = 0; + + for (ctr = 0; ctr < 8; ctr++) + { + + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_GREEN); + set_sti (dev); + wait_bank_change (dev, dev->bank_count); + + read_line_101x (dev, buf, dev->desc->max_h_size, 0, NULL, NULL); + + reset_bank_count (dev); + + sum += buf[3]; + + } + + dev->ref_green = sum / 8; + + sum = 0; + + for (ctr = 0; ctr < 8; ctr++) + { + + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_BLUE); + set_sti (dev); + wait_bank_change (dev, dev->bank_count); + + read_line_101x (dev, buf, dev->desc->max_h_size, 0, NULL, NULL); + + reset_bank_count (dev); + + sum += buf[3]; + + } + + dev->ref_blue = sum / 8; + +} + + +static void +max_color_levels_101x (Mustek_PP_Device * dev) +{ + + int ctr, line, sum; + SANE_Byte rbuf[32][MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + SANE_Byte gbuf[32][MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + SANE_Byte bbuf[32][MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + + SANE_Byte maxbuf[32]; + + for (ctr = 0; ctr < 32; ctr++) + { + + if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + { + + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_RED); + motor_forward_101x (dev); + + read_average_line_101x (dev, rbuf[ctr], dev->params.pixels_per_line, + dev->ref_red); + + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_GREEN); + set_sti (dev); + + read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line, + dev->ref_green); + + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_BLUE); + set_sti (dev); + + read_average_line_101x (dev, bbuf[ctr], dev->params.pixels_per_line, + dev->ref_blue); + + } + else + { + + dev->CCD.channel = MUSTEK_PP_CHANNEL_GRAY; + + motor_forward_101x (dev); + + read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line, + dev->ref_black); + + } + + } + + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + { + for (line = 0; line < 32; line++) + maxbuf[line] = gbuf[line][ctr]; + + qsort (maxbuf, 32, sizeof (maxbuf[0]), compar); + + sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7]; + + dev->calib_g[ctr] = sum / 4; + + } + + if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + { + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + { + for (line = 0; line < 32; line++) + maxbuf[line] = rbuf[line][ctr]; + + qsort (maxbuf, 32, sizeof (maxbuf[0]), compar); + + sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7]; + + dev->calib_r[ctr] = sum / 4; + + } + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + { + for (line = 0; line < 32; line++) + maxbuf[line] = bbuf[line][ctr]; + + qsort (maxbuf, 32, sizeof (maxbuf[0]), compar); + + sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7]; + + dev->calib_b[ctr] = sum / 4; + + } + + } + +} + +static void +find_black_top_edge_101x (Mustek_PP_Device * dev) +{ + + int lines = 0, ctr, pos; + SANE_Byte buf[MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + + dev->CCD.channel = MUSTEK_PP_CHANNEL_GRAY; + + do + { + + motor_forward_101x (dev); + wait_bank_change (dev, dev->bank_count); + + read_line_101x + (dev, buf, dev->desc->max_h_size, dev->ref_black, NULL, NULL); + + reset_bank_count (dev); + + pos = 0; + + for (ctr = dev->blackpos; ctr > dev->blackpos - 10; ctr--) + if (buf[ctr] <= 15) + pos++; + + } + while ((pos >= 8) && (lines++ < 67)); + +} + +static void +calibrate_device_101x (Mustek_PP_Device * dev) +{ + + int saved_ppl = dev->params.pixels_per_line, ctr; + + DBG (1, "calibrate_device_101x: use600 = %s\n", + (dev->desc->use600 ? "yes" : "no")); + + if (dev->desc->use600) + { + + int i, j; +/* FILE *fptr; */ + SANE_Byte buf[MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + + dev->Saved_CCD = dev->CCD; + dev->CCD.mode = MUSTEK_PP_MODE_COLOR; + dev->CCD.hwres = dev->CCD.res = 600; + dev->unknown_value = 0xAA; + dev->expose_time = 0x64; + dev->CCD.skipcount = dev->CCD.skipimagebytes = 0; + dev->params.pixels_per_line = 10; + + config_ccd (dev); + + + + sanei_pa4s2_writebyte (dev->fd, 6, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x67); + sanei_pa4s2_writebyte (dev->fd, 6, 0x17); + sanei_pa4s2_writebyte (dev->fd, 6, 0x07); + sanei_pa4s2_writebyte (dev->fd, 6, 0x27); + sanei_pa4s2_writebyte (dev->fd, 6, 0x12); + + for (i = 0; i < 8; i++) + for (j = 0; j < 255; j++) + sanei_pa4s2_writebyte (dev->fd, 5, j); + + for (j = 0; j < 8; j++) + sanei_pa4s2_writebyte (dev->fd, 5, j); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x02); + +/* fptr = fopen("/tmp/calib.1", "w"); */ + + + for (ctr = 0; ctr < 100; ctr++) + { + + reset_bank_count (dev); + sanei_pa4s2_writebyte (dev->fd, 6, 0x27); + read_line_101x (dev, buf, 2048, 0, NULL, NULL); + +/* fwrite(buf,2048,1,fptr); */ + + } + +/* fclose (fptr); */ + + dev->CCD.hwres = dev->CCD.res = 300; + dev->unknown_value = 0xFE; + dev->expose_time = 0xFF; + dev->params.pixels_per_line = 150; + dev->voltages[0] = 0x6b; + dev->voltages[1] = 0x4f; + dev->voltages[2] = 0x65; + + dev->send_voltages = 1; + + config_ccd (dev); + get_bank_count (dev); + + dev->motor_ctrl = 0x43; + for (ctr = 0; ctr < 3; ctr++) + move_motor_101x (dev, SANE_TRUE); + +/* fptr = fopen("/tmp/calib.2","w"); */ + + read_line_101x (dev, buf, 150, 0, NULL, NULL); + +/* fwrite(buf,150,1,fptr); + fclose(fptr); */ + + + move_motor_101x (dev, SANE_TRUE); + + dev->CCD = dev->Saved_CCD; + + dev->CCD.mode = MUSTEK_PP_MODE_GRAYSCALE; + + dev->voltages[0] = 0x32; + dev->voltages[1] = 0x32; + dev->voltages[2] = 0x32; + dev->params.pixels_per_line = saved_ppl; + + config_ccd (dev); + + /* + if ((dev->CCD.hwres >= 300) && (dev->CCD.mode != MUSTEK_PP_MODE_COLOR)) + motor_command = 0x63; */ + + for (ctr = 0; ctr < 4; ctr++) + { + get_bank_count (dev); + set_sti (dev); + } + + dev->CCD.adjustskip = 3; + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x7f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x7f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x7f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + +/* fptr = fopen("/tmp/calib.3","w"); */ + + j = 0; + + + for (ctr = 0; ctr < 4; ctr++) + { + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); + + + + +/* fwrite(buf,saved_ppl, 1,fptr); */ + reset_bank_count (dev); + get_bank_count (dev); + set_sti (dev); + + } + + + + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x3f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x3f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x3f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + + for (ctr = 0; ctr < 4; ctr++) + { + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); +/* fwrite(buf,saved_ppl, 1,fptr); */ + reset_bank_count (dev); + get_bank_count (dev); + set_sti (dev); + + } + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x1f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x1f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x1f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + + for (ctr = 0; ctr < 4; ctr++) + { + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); +/* fwrite(buf,saved_ppl, 1,fptr); */ + reset_bank_count (dev); + get_bank_count (dev); + set_sti (dev); + + } + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x2f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x2f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x2f); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + + for (ctr = 0; ctr < 4; ctr++) + { + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); +/* fwrite(buf,saved_ppl, 1,fptr);*/ + reset_bank_count (dev); + get_bank_count (dev); + set_sti (dev); + + } + + for (i = 0; i < 4; i++) + { + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + + for (ctr = 0; ctr < 4; ctr++) + { + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); +/* fwrite(buf,saved_ppl, 1,fptr); */ + reset_bank_count (dev); + get_bank_count (dev); + set_sti (dev); + + } + } + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + for (ctr = 0; ctr < 43; ctr++) + { + + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); +/* fwrite(buf,saved_ppl, 1,fptr); */ + reset_bank_count (dev); + get_bank_count (dev); + motor_forward_1015 (dev); + + } + + lamp (dev, SANE_FALSE); + + for (ctr = 0; ctr < 13; ctr++) + { + + read_line_101x (dev, buf, saved_ppl, 0, NULL, NULL); + + i = saved_ppl; + while ((abs (buf[i] - buf[0]) >= 15) && (i > 0)) + i--; + + if (i > j) + j = i; + + + /* + + for (i=0 ; i= 15) { + + + if (i > j) + j=i; + + break; + + + } */ +/* fwrite(buf,saved_ppl, 1,fptr); */ + reset_bank_count (dev); + get_bank_count (dev); + set_sti (dev); + } + lamp (dev, SANE_TRUE); + +/* fclose(fptr); */ + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, 0x37); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + + + + dev->blackpos = 0x6a; + dev->Saved_CCD.skipcount = 0x6A; /*(120 + 12) & 0xFF; */ + + dev->CCD = dev->Saved_CCD; + + dev->send_voltages = 0; + config_ccd (dev); + + for (ctr = 0; ctr < 3; ctr++) + { + + motor_forward_1015 (dev); + + + } + + for (ctr = 0; ctr < 100; ctr++) + { + read_line_101x (dev, buf, 150, 0, NULL, NULL); + motor_forward_1015 (dev); + } + + + + + + dev->CCD = dev->Saved_CCD; + + dev->send_voltages = 0; + config_ccd (dev); + + + + + return; + } + + dev->Saved_CCD = dev->CCD; + dev->params.pixels_per_line = dev->desc->max_h_size; + dev->CCD.hwres = dev->CCD.res = dev->desc->max_res; + dev->CCD.mode = MUSTEK_PP_MODE_GRAYSCALE; + dev->CCD.skipcount = dev->CCD.skipimagebytes = 0; + dev->CCD.invert = SANE_FALSE; + dev->CCD.channel = MUSTEK_PP_CHANNEL_GRAY; + + config_ccd_101x (dev); + get_bank_count (dev); + + find_black_side_edge_101x (dev); + + for (ctr = 0; ctr < 4; ctr++) + move_motor_101x (dev, SANE_TRUE); + + dev->CCD = dev->Saved_CCD; + + dev->CCD.hwres = dev->CCD.res = dev->desc->max_res; + dev->CCD.skipcount = dev->CCD.skipimagebytes = 0; + dev->CCD.invert = SANE_FALSE; + + config_ccd_101x (dev); + get_bank_count (dev); + + if ((dev->CCD.mode == MUSTEK_PP_MODE_COLOR) && (dev->ccd_type != 0)) + min_color_levels_101x (dev); + + dev->CCD = dev->Saved_CCD; + dev->params.pixels_per_line = saved_ppl; + dev->CCD.invert = SANE_FALSE; + + config_ccd_101x (dev); + get_bank_count (dev); + + max_color_levels_101x (dev); + + dev->params.pixels_per_line = dev->desc->max_h_size; + dev->CCD.mode = MUSTEK_PP_MODE_GRAYSCALE; + dev->CCD.hwres = dev->CCD.res = dev->desc->max_res; + dev->CCD.skipcount = dev->CCD.skipimagebytes = 0; + dev->CCD.invert = SANE_FALSE; + + config_ccd_101x (dev); + get_bank_count (dev); + + find_black_top_edge_101x (dev); + + dev->CCD = dev->Saved_CCD; + + dev->params.pixels_per_line = saved_ppl; + + config_ccd_101x (dev); + get_bank_count (dev); + +} + +static void +get_grayscale_line_101x (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + + int skips; + + dev->line_diff += + SANE_FIX ((float) dev->desc->max_res / (float) dev->CCD.res); + + skips = (dev->line_diff >> SANE_FIXED_SCALE_SHIFT); + + while (--skips) + { + motor_forward_101x (dev); + wait_bank_change (dev, dev->bank_count); + reset_bank_count (dev); + } + + dev->line_diff &= 0xFFFF; + + motor_forward_101x (dev); + wait_bank_change (dev, dev->bank_count); + + read_line_101x (dev, buf, dev->params.pixels_per_line, dev->ref_black, + dev->calib_g, + (dev->val[OPT_CUSTOM_GAMMA].w ? + dev->gamma_table[0] : NULL)); + + reset_bank_count (dev); + +} + +static void +get_lineart_line_101x (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + + int ctr; + SANE_Byte gbuf[MUSTEK_PP_101x_MAX_H_PIXEL * 2]; + + get_grayscale_line_101x (dev, gbuf); + + memset (buf, 0xFF, dev->params.bytes_per_line); + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + buf[ctr >> 3] ^= ((gbuf[ctr] > dev->desc->bw) ? (1 << (7 - ctr % 8)) : 0); + +} + +static void +get_color_line_101x (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + + SANE_Byte *red, *blue, *src, *dest; + int gotline = 0, ctr; + int gored, goblue, gogreen; + int step = dev->CCD.line_step; + + do + { + + red = dev->red[dev->redline]; + blue = dev->blue[dev->blueline]; + + dev->ccd_line++; + + if ((dev->rdiff >> SANE_FIXED_SCALE_SHIFT) == dev->ccd_line) + { + gored = 1; + dev->rdiff += step; + } + else + gored = 0; + + if ((dev->bdiff >> SANE_FIXED_SCALE_SHIFT) == dev->ccd_line) + { + goblue = 1; + dev->bdiff += step; + } + else + goblue = 0; + + if ((dev->gdiff >> SANE_FIXED_SCALE_SHIFT) == dev->ccd_line) + { + gogreen = 1; + dev->gdiff += step; + } + else + gogreen = 0; + + if (!gored && !goblue && !gogreen) + { + motor_forward_101x (dev); + wait_bank_change (dev, dev->bank_count); + reset_bank_count (dev); + if (dev->ccd_line >= (dev->CCD.line_step >> SANE_FIXED_SCALE_SHIFT)) + dev->redline = ++dev->redline % dev->green_offs; + if (dev->ccd_line >= + dev->blue_offs + (dev->CCD.line_step >> SANE_FIXED_SCALE_SHIFT)) + dev->blueline = ++dev->blueline % dev->blue_offs; + continue; + } + + if (gored) + dev->CCD.channel = MUSTEK_PP_CHANNEL_RED; + else if (goblue) + dev->CCD.channel = MUSTEK_PP_CHANNEL_BLUE; + else + dev->CCD.channel = MUSTEK_PP_CHANNEL_GREEN; + + motor_forward_101x (dev); + wait_bank_change (dev, dev->bank_count); + + if (dev->ccd_line >= dev->green_offs && gogreen) + { + src = red; + dest = buf; + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + { + *dest = *src++; + dest += 3; + } + } + + if (gored) + { + + read_line_101x (dev, red, dev->params.pixels_per_line, dev->ref_red, + dev->calib_r, + (dev->val[OPT_CUSTOM_GAMMA].w ? + dev->gamma_table[1] : NULL)); + + reset_bank_count (dev); + + } + + dev->redline = ++dev->redline % dev->green_offs; + + if (dev->ccd_line >= dev->green_offs && gogreen) + { + src = blue; + dest = buf + 2; + + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + { + *dest = *src++; + dest += 3; + } + + } + + if (goblue) + { + if (gored) + { + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_BLUE); + set_sti (dev); + wait_bank_change (dev, dev->bank_count); + } + + read_line_101x (dev, blue, dev->params.pixels_per_line, + dev->ref_blue, dev->calib_b, + (dev->val[OPT_CUSTOM_GAMMA].w ? dev-> + gamma_table[3] : NULL)); + + reset_bank_count (dev); + + } + + if (dev->ccd_line >= + dev->blue_offs + (dev->CCD.line_step >> SANE_FIXED_SCALE_SHIFT)) + dev->blueline = ++dev->blueline % dev->blue_offs; + + if (gogreen) + { + + if (gored || goblue) + { + set_ccd_channel_101x (dev, MUSTEK_PP_CHANNEL_GREEN); + set_sti (dev); + wait_bank_change (dev, dev->bank_count); + } + + read_line_101x (dev, dev->green, dev->params.pixels_per_line, + dev->ref_green, dev->calib_g, + (dev->val[OPT_CUSTOM_GAMMA].w ? + dev->gamma_table[2] : NULL)); + + reset_bank_count (dev); + + src = dev->green; + dest = buf + 1; + + for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) + { + *dest = *src++; + dest += 3; + } + + gotline = 1; + } + + } + while (!gotline); + +} + + + +/* these functions are for the 1013 chipset */ + +static void +set_ccd_channel_1013 (Mustek_PP_Device * dev, int channel) +{ + dev->CCD.channel = channel; + sanei_pa4s2_writebyte (dev->fd, 6, chan_codes_1013[channel]); +} + +static void +motor_backward_1013 (Mustek_PP_Device * dev) +{ + + dev->motor_step++; + set_led (dev); + + if (dev->motor_phase > 3) + dev->motor_phase = 3; + + sanei_pa4s2_writebyte (dev->fd, 6, 0x62); + sanei_pa4s2_writebyte (dev->fd, 5, fullstep[dev->motor_phase]); + + dev->motor_phase = (dev->motor_phase == 0 ? 3 : dev->motor_phase - 1); + + set_ccd_channel_1013 (dev, dev->CCD.channel); + set_sti (dev); + +} + +static void +return_home_1013 (Mustek_PP_Device * dev, SANE_Bool nowait) +{ + u_char ishome; + int ctr, saved_niceload; + + /* 1013 can't return home all alone, nowait ignored */ + + saved_niceload = niceload; + + niceload = 0; + + for (ctr = 0; ctr < 4500; ctr++) + { + + /* check_is_home_1013 */ + sanei_pa4s2_readbegin (dev->fd, 2); + sanei_pa4s2_readbyte (dev->fd, &ishome); + sanei_pa4s2_readend (dev->fd); + + /* yes, it should be is_not_home */ + if ((ishome & 1) == 0) + break; + + motor_backward_1013 (dev); + wait_bank_change (dev, dev->bank_count); + reset_bank_count (dev); + + } + + niceload = saved_niceload; + +} + +static void +motor_forward_1013 (Mustek_PP_Device * dev) +{ + + int ctr; + + dev->motor_step++; + set_led (dev); + + for (ctr = 0; ctr < 2; ctr++) + { + + sanei_pa4s2_writebyte (dev->fd, 6, 0x62); + sanei_pa4s2_writebyte (dev->fd, 5, halfstep[dev->motor_phase]); + + dev->motor_phase = (dev->motor_phase == 7 ? 0 : dev->motor_phase + 1); + + } + + set_ccd_channel_1013 (dev, dev->CCD.channel); + set_sti (dev); +} + + + +static void +config_ccd_1013 (Mustek_PP_Device * dev) +{ + + if (dev->CCD.res != 0) + dev->CCD.res_step = + SANE_FIX ((float) dev->CCD.hwres / (float) dev->CCD.res); + + set_dpi_value (dev); + + /* set_start_channel_1013 (dev); */ + + sanei_pa4s2_writebyte (dev->fd, 6, 0x05); + + switch (dev->CCD.mode) + { + case MUSTEK_PP_MODE_LINEART: + case MUSTEK_PP_MODE_GRAYSCALE: + dev->CCD.channel = MUSTEK_PP_CHANNEL_GRAY; + break; + + case MUSTEK_PP_MODE_COLOR: + dev->CCD.channel = MUSTEK_PP_CHANNEL_RED; + break; + + } + + set_ccd_channel_1013 (dev, dev->CCD.channel); + + /* set_invert_1013 (dev); */ + + sanei_pa4s2_writebyte (dev->fd, 6, + (dev->CCD.invert == SANE_TRUE ? 0x04 : 0x14)); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x37); + reset_bank_count (dev); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x27); + sanei_pa4s2_writebyte (dev->fd, 6, 0x67); + sanei_pa4s2_writebyte (dev->fd, 6, 0x17); + sanei_pa4s2_writebyte (dev->fd, 6, 0x77); + + /* set_initial_skip_1013 (dev); */ + + sanei_pa4s2_writebyte (dev->fd, 6, 0x41); + + dev->CCD.adjustskip = dev->CCD.skipcount + dev->CCD.skipimagebytes; + + DBG (4, "config_ccd_1013: adjustskip %u\n", dev->CCD.adjustskip); + + sanei_pa4s2_writebyte (dev->fd, 5, dev->CCD.adjustskip / 16 + 2); + + dev->CCD.adjustskip %= 16; + + sanei_pa4s2_writebyte (dev->fd, 6, 0x81); + sanei_pa4s2_writebyte (dev->fd, 5, 0x70); + sanei_pa4s2_writebyte (dev->fd, 6, 0x01); + + + set_line_adjust (dev); + + get_bank_count (dev); + +} + +/* these functions are for the 1015 chipset */ + + +static void +motor_control_1015 (Mustek_PP_Device * dev, u_char control) +{ + + + u_char val; + + DBG (4, "motor_controll_1015: control code 0x%02x\n", + (unsigned int) control); + + if (dev->desc->use600 == SANE_FALSE) + sanei_pa4s2_writebyte (dev->fd, 6, 0xF6); + sanei_pa4s2_writebyte (dev->fd, 6, 0x22); + sanei_pa4s2_writebyte (dev->fd, 5, control); + sanei_pa4s2_writebyte (dev->fd, 6, 0x02); + + do + { + + sanei_pa4s2_readbegin (dev->fd, 2); + sanei_pa4s2_readbyte (dev->fd, &val); + sanei_pa4s2_readend (dev->fd); + + } + while ((val & 0x08) != 0); + +} + +static void +return_home_1015 (Mustek_PP_Device * dev, SANE_Bool nowait) +{ + + u_char ishome, control = 0xC3; + + if (dev->desc->use600 == SANE_TRUE) + { + + switch (dev->ccd_type) + { + case 1: + control = 0xD3; + break; + + default: + control = 0xC3; + break; + } + } + + motor_control_1015 (dev, control); + + do + { + + /* check_is_home_1015 */ + sanei_pa4s2_readbegin (dev->fd, 2); + sanei_pa4s2_readbyte (dev->fd, &ishome); + sanei_pa4s2_readend (dev->fd); + + if (nowait) + break; + + usleep (1000); /* much nicer load */ + + } + while ((ishome & 2) == 0); + +} + +static void +motor_forward_1015 (Mustek_PP_Device * dev) +{ + u_char control = 0x1B; + + dev->motor_step++; + if (dev->desc->use600 == SANE_FALSE) + set_led (dev); + + if (dev->desc->use600 == SANE_TRUE) + { + + switch (dev->ccd_type) + { + case 1: + control = dev->motor_ctrl; + break; + + default: + control = 0x1B; + break; + } + } + + motor_control_1015 (dev, control); + /* + + if ((dev->desc->use600 == SANE_TRUE) && (dev->CCD.mode != MUSTEK_PP_MODE_COLOR) && (dev->CCD.hwres < 300)) + motor_control_1015 (dev, control); */ + + if (dev->desc->use600 == SANE_FALSE) + set_ccd_channel_1015 (dev, dev->CCD.channel); + set_sti (dev); + +} + +/* +static void +motor_backward_1015 (Mustek_PP_Device * dev) +{ + u_char control = 0x43; + + + dev->motor_step++; + + set_led (dev); + + switch (dev->ccd_type) + { + case 1: + control = 0x1B; + break; + + default: + control = 0x43; + break; + } + + motor_control_1015 (dev, control); + + set_ccd_channel_1015 (dev, dev->CCD.channel); + set_sti (dev); + +} +*/ + + +static void +set_ccd_channel_1015 (Mustek_PP_Device * dev, int channel) +{ + + u_char chancode = chan_codes_1015[channel]; + + dev->CCD.channel = channel; + if (dev->desc->use600 == SANE_TRUE) + { + chancode |= 0x14; + } + else + { + + dev->image_control &= 0x34; + chancode |= dev->image_control; + + } + + dev->image_control = chancode; + + sanei_pa4s2_writebyte (dev->fd, 6, chancode); + +} + + +static void +config_ccd_1015 (Mustek_PP_Device * dev) +{ + + u_char val; + + if (dev->CCD.res != 0) + dev->CCD.res_step = + SANE_FIX ((float) dev->CCD.hwres / (float) dev->CCD.res); + + if (dev->desc->use600 == SANE_TRUE) + { + + if (dev->first_time) + sanei_pa4s2_writebyte (dev->fd, 6, 0x86); + else + sanei_pa4s2_writebyte (dev->fd, 6, 0xC6); + + dev->first_time = 0; + } + + set_dpi_value (dev); + + dev->image_control = 4; + + /* set_start_channel_1015 (dev); */ + + switch (dev->CCD.mode) + { + case MUSTEK_PP_MODE_LINEART: + case MUSTEK_PP_MODE_GRAYSCALE: + dev->CCD.channel = MUSTEK_PP_CHANNEL_GRAY; + break; + + case MUSTEK_PP_MODE_COLOR: + dev->CCD.channel = MUSTEK_PP_CHANNEL_RED; + break; + + } + + set_ccd_channel_1015 (dev, dev->CCD.channel); + + + /* set_invert_1015 (dev); */ + + dev->image_control &= 0xE4; + + if (dev->CCD.invert == SANE_FALSE) + dev->image_control |= 0x10; + + + if (dev->desc->use600 == SANE_TRUE) + { + + sanei_pa4s2_writebyte (dev->fd, 6, 0x13); + sanei_pa4s2_writebyte (dev->fd, 5, dev->unknown_value); /* FIXME */ + sanei_pa4s2_writebyte (dev->fd, 6, 0x03); + + } + else + { + sanei_pa4s2_writebyte (dev->fd, 6, dev->image_control); + } + + sanei_pa4s2_writebyte (dev->fd, 6, 0x23); + sanei_pa4s2_writebyte (dev->fd, 5, 0x00); + + if (dev->desc->use600 == SANE_TRUE) + sanei_pa4s2_writebyte (dev->fd, 6, 0x03); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x43); + + switch (dev->ccd_type) + { + case 1: + if (dev->desc->use600 == SANE_TRUE) + val = 0x0F; + else + val = 0x6B; + break; + case 4: + val = 0x9F; + break; + default: + val = 0x92; + break; + } + + sanei_pa4s2_writebyte (dev->fd, 5, val); + sanei_pa4s2_writebyte (dev->fd, 6, 0x03); + + if (dev->desc->use600 == SANE_TRUE) + sanei_pa4s2_writebyte (dev->fd, 6, 0x45); /* or 0x05 for no 8kbank */ + + sanei_pa4s2_writebyte (dev->fd, 6, 0x37); + reset_bank_count (dev); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x27); + sanei_pa4s2_writebyte (dev->fd, 6, 0x67); + sanei_pa4s2_writebyte (dev->fd, 6, 0x17); + sanei_pa4s2_writebyte (dev->fd, 6, 0x77); + + /* set_initial_skip_1015 (dev); */ + + sanei_pa4s2_writebyte (dev->fd, 6, 0x41); + + dev->CCD.adjustskip = dev->CCD.skipcount + dev->CCD.skipimagebytes; + + /* if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + dev->CCD.adjustskip <<= 3; */ + + + if ((dev->desc->use600 == SANE_TRUE) && (dev->CCD.adjustskip > 32)) + dev->CCD.adjustskip += 26; + + sanei_pa4s2_writebyte (dev->fd, 5, + dev->CCD.adjustskip / 32 + (dev->desc->use600 == + SANE_TRUE ? 0 : 1)); + +/* if (dev->desc->use600 == SANE_FALSE) */ + dev->CCD.adjustskip %= 32; + + + if (dev->desc->use600 == SANE_TRUE) + sanei_pa4s2_writebyte (dev->fd, 6, 0x01); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x81); + + /* expose time */ + switch (dev->ccd_type) + { + case 1: + + if (dev->desc->use600 == SANE_TRUE) + val = dev->expose_time; + else + val = 0xA8; + break; + case 0: + val = 0x8A; + break; + default: + val = 0xA8; + break; + } + + sanei_pa4s2_writebyte (dev->fd, 5, val); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x01); + + if ((dev->desc->use600 == SANE_TRUE) && (dev->send_voltages == 1)) + { + + sanei_pa4s2_writebyte (dev->fd, 6, 0x81); + sanei_pa4s2_writebyte (dev->fd, 5, 0xFD); + sanei_pa4s2_writebyte (dev->fd, 6, 0x01); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x13); + sanei_pa4s2_writebyte (dev->fd, 5, 0xFE); + sanei_pa4s2_writebyte (dev->fd, 6, 0x03); + + sanei_pa4s2_writebyte (dev->fd, 6, 0x10); + sanei_pa4s2_writebyte (dev->fd, 5, dev->voltages[0]); + sanei_pa4s2_writebyte (dev->fd, 6, 0x20); + sanei_pa4s2_writebyte (dev->fd, 5, dev->voltages[1]); + sanei_pa4s2_writebyte (dev->fd, 6, 0x40); + sanei_pa4s2_writebyte (dev->fd, 5, dev->voltages[2]); + sanei_pa4s2_writebyte (dev->fd, 6, 0x00); + + + } + + set_line_adjust (dev); + + get_bank_count (dev); + +} + +/* these functions are for the 1505 chipset */ +static void +return_home_1505 (Mustek_PP_Device * dev, SANE_Bool nowait) +{ +} + +static void +config_ccd_1505 (Mustek_PP_Device * dev) +{ +} + +static void +lamp_1505 (Mustek_PP_Device * dev, int lamp_on) +{ +} + +static void +send_voltages_1505 (Mustek_PP_Device * dev) +{ +} + +static void +move_motor_1505 (Mustek_PP_Device * dev, int forward) +{ +} + +static void +calibrate_device_1505 (Mustek_PP_Device * dev) +{ +} + +static void +get_grayscale_line_1505 (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + +} + +static void +get_lineart_line_1505 (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + +} + +static void +get_color_line_1505 (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + +} + +/* these functions are interfaces only */ +static void +config_ccd (Mustek_PP_Device * dev) +{ + DBG (6, "config_ccd: %d dpi, mode %d, invert %d, size %d\n", + dev->CCD.hwres, dev->CCD.mode, dev->CCD.invert, + dev->params.pixels_per_line); + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + config_ccd_1013 (dev); + break; + + case MUSTEK_PP_ASIC_1015: + config_ccd_1015 (dev); + break; + + case MUSTEK_PP_ASIC_1505: + config_ccd_1505 (dev); + break; + } + +} + +static void +return_home (Mustek_PP_Device * dev, SANE_Bool nowait) +{ + + CCD_Info CCD = dev->CCD; + + + if (dev->desc->use600) + { + dev->CCD.mode = MUSTEK_PP_MODE_COLOR; + dev->CCD.hwres = dev->CCD.res = 600; + } + else + { + dev->CCD.hwres = dev->CCD.res = 100; + dev->CCD.mode = MUSTEK_PP_MODE_GRAYSCALE; + } + + dev->CCD.skipcount = dev->CCD.skipimagebytes = 0; + + config_ccd (dev); + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + return_home_1013 (dev, nowait); + break; + + case MUSTEK_PP_ASIC_1015: + return_home_1015 (dev, nowait); + break; + + case MUSTEK_PP_ASIC_1505: + return_home_1505 (dev, nowait); + break; + } + + dev->CCD = CCD; + + dev->motor_step = 0; + + config_ccd (dev); +} + +static void +lamp (Mustek_PP_Device * dev, int lamp_on) +{ + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + case MUSTEK_PP_ASIC_1015: + set_lamp (dev, lamp_on); + break; + + case MUSTEK_PP_ASIC_1505: + lamp_1505 (dev, lamp_on); + break; + } + +} + +static void +set_voltages (Mustek_PP_Device * dev) +{ + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + case MUSTEK_PP_ASIC_1015: + send_voltages (dev); + break; + + case MUSTEK_PP_ASIC_1505: + send_voltages_1505 (dev); + break; + + } +} + +static void +move_motor (Mustek_PP_Device * dev, int count, int forward) +{ + + int ctr; + int saved_niceload = niceload; + + DBG (4, "move_motor: %u steps (%s)\n", count, + (forward == SANE_TRUE ? "forward" : "backward")); + + niceload = 0; + + for (ctr = 0; ctr < count; ctr++) + { + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1013: + case MUSTEK_PP_ASIC_1015: + move_motor_101x (dev, forward); + break; + + case MUSTEK_PP_ASIC_1505: + move_motor_1505 (dev, forward); + break; + } + + } + + niceload = saved_niceload; + +} + +static void +calibrate (Mustek_PP_Device * dev) +{ + + DBG (1, "calibrate entered (asic = 0x%02x)\n", dev->asic_id); + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1015: + case MUSTEK_PP_ASIC_1013: + calibrate_device_101x (dev); + break; + + case MUSTEK_PP_ASIC_1505: + calibrate_device_1505 (dev); + break; + } + + DBG (3, "calibrate: ref_black %d, blackpos %d\n", + dev->ref_black, dev->blackpos); + +} + + +static void +get_lineart_line (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1015: + case MUSTEK_PP_ASIC_1013: + get_lineart_line_101x (dev, buf); + break; + + case MUSTEK_PP_ASIC_1505: + get_lineart_line_1505 (dev, buf); + break; + + } +} + +static void +get_grayscale_line (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1015: + case MUSTEK_PP_ASIC_1013: + get_grayscale_line_101x (dev, buf); + break; + + case MUSTEK_PP_ASIC_1505: + get_grayscale_line_1505 (dev, buf); + break; + + } + +} + +static void +get_color_line (Mustek_PP_Device * dev, SANE_Byte * buf) +{ + + switch (dev->asic_id) + { + case MUSTEK_PP_ASIC_1015: + case MUSTEK_PP_ASIC_1013: + get_color_line_101x (dev, buf); + break; + + case MUSTEK_PP_ASIC_1505: + get_color_line_1505 (dev, buf); + break; + + } + +} + + +#ifdef HAVE_AUTHORIZATION + +static SANE_Status +do_authorization (SANE_String_Const ressource) +{ + /* This function implements a simple authorization function. It looks */ + /* up an entry in the file SANE_PATH_CONFIG_DIR/.auth. Such an entry */ + /* must be of the form device:user:password where password is a crypt() */ + /* encrypted password. If several users are allowed to access a device */ + /* an entry must be created for each user. If no entry exists for device */ + /* or the file does not exist the authentication failes. If the */ + /* file exists, but can't be opened the authentication fails */ + + SANE_Status status; + FILE *fp; + int device_found; + char username[SANE_MAX_USERNAME_LEN]; + char password[SANE_MAX_PASSWORD_LEN]; + char line[MAX_LINE_LEN]; + char *linep; + char *device; + char *user; + char *passwd; + char *p; + + if (auth_callback == NULL) + { + DBG (2, "auth: no auth callback\n"); + DEBUG (); + return SANE_STATUS_ACCESS_DENIED; + } + + /* first check if an entry exists in for this device */ + + fp = sanei_config_open (PASSWD_FILE); + + if (fp == NULL) + { + DBG (2, "auth: password file is missing\n"); + DEBUG (); + return SANE_STATUS_ACCESS_DENIED; + } + + linep = &line[0]; + device_found = SANE_FALSE; + + while (sanei_config_read (line, MAX_LINE_LEN, fp)) + { + p = index (linep, SEPARATOR); + if (p) + { + *p = '\0'; + device = linep; + if (strcmp (device, ressource) == 0) + { + device_found = SANE_TRUE; + break; + } + } + } + + if (device_found == SANE_FALSE) + { + DBG (2, "auth: requested resource (%s) not in file\n", ressource); + DEBUG (); + return SANE_STATUS_ACCESS_DENIED; + } + + fseek (fp, 0L, SEEK_SET); + + (*auth_callback) (ressource, username, password); + + status = SANE_STATUS_ACCESS_DENIED; + + do + { + sanei_config_read (line, MAX_LINE_LEN, fp); + if (!ferror (fp) && !feof (fp)) + { + /* neither strsep(3) nor strtok(3) seem to work on my system */ + p = index (linep, SEPARATOR); + if (p == NULL) + continue; + *p = '\0'; + device = linep; + if (strcmp (device, ressource) != 0) /* not a matching entry */ + continue; + + linep = ++p; + p = index (linep, SEPARATOR); + if (p == NULL) + continue; + + *p = '\0'; + user = linep; + if (strncmp (user, username, SANE_MAX_USERNAME_LEN) != 0) + continue; /* username doesn´t match */ + + linep = ++p; + /* rest of the line is considered to be the password */ + passwd = linep; + /* remove newline */ + *(passwd + strlen (passwd) - 1) = '\0'; + p = crypt (password, SALT); + if (strcmp (p, passwd) == 0) + { + /* authentication ok */ + status = SANE_STATUS_GOOD; + break; + } + else + continue; + } + } + while (!ferror (fp) && !feof (fp)); + fclose (fp); + + if (status != SANE_STATUS_GOOD) + DBG (1, "auth: access denied\n"); + + return status; +} +#endif + +static SANE_Status +attach (const char *devname) +{ + Mustek_PP_Descriptor *dev; + int i, fd; + SANE_Status status; + u_char val; + + + for (i = 0; i < num_devices; i++) + if (strcmp (devlist[i].port, devname) == 0) + return SANE_STATUS_GOOD; + + status = sanei_pa4s2_open (devname, &fd); + + if (status != SANE_STATUS_GOOD) + { + DBG (2, "attach: couldn't attach to `%s' (%s)\n", devname, + sane_strstatus (status)); + DEBUG (); + return status; + } + + + sanei_pa4s2_enable (fd, SANE_TRUE); + sanei_pa4s2_readbegin (fd, 0); + sanei_pa4s2_readbyte (fd, &val); + sanei_pa4s2_readend (fd); + sanei_pa4s2_enable (fd, SANE_FALSE); + + +#ifndef PATCH_MUSTEK_PP_1505 + + if (val == MUSTEK_PP_ASIC_1505) + { + + DBG (2, "attach: this scanner reports ASIC 0x%02x\n", (int) val); + DBG (2, "attach: you'll have to enable this in the source\n"); + sanei_pa4s2_close (fd); + return SANE_STATUS_INVAL; + + } + +#endif + + + dev = malloc (sizeof (Mustek_PP_Descriptor) * (num_devices + 1)); + + if (dev == NULL) + { + DBG (2, "attach: not enough memory for device descriptor\n"); + DEBUG (); + sanei_pa4s2_close (fd); + return SANE_STATUS_NO_MEM; + } + + memset (dev, 0, sizeof (Mustek_PP_Descriptor) * (num_devices + 1)); + + if (num_devices > 0) + { + memcpy (dev + 1, devlist, + sizeof (Mustek_PP_Descriptor) * (num_devices)); + free (devlist); + } + + devlist = dev; + num_devices++; + + dev->sane.name = strdup (devname); + dev->sane.vendor = strdup ("Mustek"); + dev->sane.type = "flatbed scanner"; + + dev->port = strdup (devname); + + dev->buf_size = buf_size; + + dev->requires_auth = SANE_FALSE; + + dev->wait_lamp = wait_lamp; + + dev->bw = bw; + + dev->asic = val; + + sanei_pa4s2_enable (fd, SANE_TRUE); + sanei_pa4s2_readbegin (fd, 2); + sanei_pa4s2_readbyte (fd, &val); + sanei_pa4s2_readend (fd); + sanei_pa4s2_enable (fd, SANE_FALSE); + + dev->ccd = (val & (dev->asic == MUSTEK_PP_ASIC_1013 ? 0x04 : 0x05)); + /* FIXME: ASIC 1505 scanners do *not* report their CCD type ... + at least not this way :-( */ + + sanei_pa4s2_close (fd); + + + switch (dev->asic) + { + case MUSTEK_PP_ASIC_1013: + dev->max_res = 300; + dev->max_h_size = MUSTEK_PP_101x_MAX_H_PIXEL; + dev->max_v_size = MUSTEK_PP_101x_MAX_V_PIXEL; + dev->wait_bank = wait_bank; + dev->strip_height = strip_height; + dev->sane.model = strdup ("MFS-600IIIP"); + + dev->use600 = SANE_FALSE; + break; + + case MUSTEK_PP_ASIC_1015: + +/* if (dev->ccd == 1) + { + dev->max_res = 600; + dev->max_h_size = MUSTEK_PP_101x_MAX_H_PIXEL * 2; + dev->max_v_size = MUSTEK_PP_101x_MAX_V_PIXEL * 2; + } + else + {*/ + dev->max_res = 300; + dev->max_h_size = MUSTEK_PP_101x_MAX_H_PIXEL; + dev->max_v_size = MUSTEK_PP_101x_MAX_V_PIXEL; +/* }*/ + + dev->wait_bank = wait_bank; + dev->strip_height = strip_height; + dev->sane.model = strdup ("MFS-600IIIP"); + + dev->use600 = SANE_FALSE; + break; + + case MUSTEK_PP_ASIC_1505: + + DBG (2, "attach: don't know parameters for ASIC 1505 scanner\n"); + DEBUG (); + + dev->max_res = 600; + dev->max_h_size = 5100; + dev->max_v_size = 7000; + dev->wait_bank = wait_bank; + dev->strip_height = strip_height; + dev->sane.model = strdup ("MFS-600IIIP"); + dev->use600 = SANE_TRUE; + break; + } + + + + DBG (3, "attach: device %s attached\n", devname); + + DBG (3, "attach: asic 0x%02x, ccd %02d\n", dev->asic, dev->ccd); + + DBG (4, "attach: use600 is `%s'\n", + (dev->use600 == SANE_TRUE ? "yes" : "no")); + + return SANE_STATUS_GOOD; +} + +static size_t +max_string_size (const SANE_String_Const strings[]) +{ + size_t size, max_size = 0; + int i; + + for (i = 0; strings[i]; ++i) + { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } + + return max_size; +} + + + +static SANE_Status +init_options (Mustek_PP_Device * dev) +{ + int i; + + /* only this code is tested so far */ + ASSERT ((dev->asic_id == MUSTEK_PP_ASIC_1013) || + ((dev->asic_id == MUSTEK_PP_ASIC_1015) && (dev->ccd_type == 0))); + + memset (dev->opt, 0, sizeof (dev->opt)); + memset (dev->val, 0, sizeof (dev->val)); + + for (i = 0; i < NUM_OPTIONS; ++i) + { + dev->opt[i].size = sizeof (SANE_Word); + dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS; + + /* "Mode" group: */ + + dev->opt[OPT_MODE_GROUP].title = "Scan Mode"; + dev->opt[OPT_MODE_GROUP].desc = ""; + dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_MODE_GROUP].cap = 0; + dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* scan mode */ + dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + dev->opt[OPT_MODE].type = SANE_TYPE_STRING; + dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + dev->opt[OPT_MODE].size = max_string_size (mode_list); + dev->opt[OPT_MODE].constraint.string_list = mode_list; + dev->val[OPT_MODE].s = strdup (mode_list[2]); + + /* resolution */ + dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + dev->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED; + dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_RESOLUTION].constraint.range = &dev->dpi_range; + dev->val[OPT_RESOLUTION].w = dev->dpi_range.max; + + /* preview */ + dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + dev->val[OPT_PREVIEW].w = SANE_FALSE; + + /* gray preview */ + dev->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; + dev->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; + dev->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; + dev->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; + dev->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; + + /* "Geometry" group: */ + + dev->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; + dev->opt[OPT_GEOMETRY_GROUP].desc = ""; + dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_GEOMETRY_GROUP].cap = 0; + dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + + /* top-left x */ + dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED; + dev->opt[OPT_TL_X].unit = SANE_UNIT_MM; + dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_X].constraint.range = &dev->x_range; + dev->val[OPT_TL_X].w = 0; + + /* top-left y */ + dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; + dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM; + dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_TL_Y].constraint.range = &dev->y_range; + dev->val[OPT_TL_Y].w = 0; + + /* bottom-right x */ + dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED; + dev->opt[OPT_BR_X].unit = SANE_UNIT_MM; + dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_X].constraint.range = &dev->x_range; + dev->val[OPT_BR_X].w = dev->x_range.max; + + /* bottom-right y */ + dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; + dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM; + dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_BR_Y].constraint.range = &dev->y_range; + dev->val[OPT_BR_Y].w = dev->y_range.max; + + /* "Enhancement" group: */ + + dev->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; + dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; + dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; + dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; + dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + +#ifdef HAVE_INVERSION + + /* invert */ + dev->oPt[OPT_INVERT].name = SANE_NAME_NEGATIVE; + dev->opt[OPT_INVERT].title = SANE_TITLE_NEGATIVE; + dev->opt[OPT_INVERT].desc = "Invert colors colors/swap black and white"; + dev->opt[OPT_INVERT].type = SANE_TYPE_BOOL; + dev->opt[OPT_INVERT].cap |= SANE_CAP_ADVANCED; + dev->val[OPT_INVERT].w = SANE_FALSE; + +#endif + + /* custom-gamma table */ + dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; + dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED; + dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; + + /* grayscale gamma vector */ + dev->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; + dev->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR].wa = &dev->gamma_table[0][0]; + + /* red gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; + dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR_R].wa = &dev->gamma_table[1][0]; + + /* green gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; + dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR_G].wa = &dev->gamma_table[2][0]; + + /* blue gamma vector */ + dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; + dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; + dev->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); + dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; + dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; + dev->val[OPT_GAMMA_VECTOR_B].wa = &dev->gamma_table[3][0]; + + 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; + u_int io_mode = SANEI_PA4S2_OPT_DEFAULT; + + DBG_INIT (); + + if (version_code != NULL) + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, MUSTEK_PP_BUILD); + + DBG (3, "init: SANE v%s, backend v%d.%d.%d-%s\n", VERSION, V_MAJOR, V_MINOR, + MUSTEK_PP_BUILD, MUSTEK_PP_STATE); + +#ifdef PATCH_MUSTEK_PP_1505 + + DBG (3, "init: patches for ASIC 1505 (0xA2) enabled\n"); + +#endif + +#ifdef HAVE_AUTHORIZATION + + auth_callback = authorize; + + ASSERT (authorize != NULL); + +#else + + DBG (1, "I wouldn't let myself be root if I were you...\n"); + +#endif + + fp = sanei_config_open (MUSTEK_PP_CONFIG_FILE); + + + if (fp == NULL) + { + DBG (2, "init: no configuration file, using default `port 0x%03x'\n", + MUSTEK_PP_DEFAULT_PORT); + + attach (STRINGIFY (MUSTEK_PP_DEFAULT_PORT)); + return SANE_STATUS_GOOD; + } + + while (sanei_config_read (dev_name, sizeof (dev_name), 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'; + + while (len && isspace (cp[len - 1])) + cp[--len] = '\0'; + + if (!len) + continue;*//* ignore empty lines */ + + + if (strncmp (cp, "option", 6) == 0 && isspace (cp[6])) + { + + cp += 7; + cp = (char *) sanei_config_skip_whitespace (cp); + + if (strncmp (cp, "strip-height", 12) == 0 && isspace (cp[12])) + { + char *end; + int val; + + errno = 0; + cp += 13; + val = strtol (cp, &end, 0); + if ((end == cp || errno) || (val < 0)) + { + DBG (2, "init: invalid value `%s`, falling back to %d\n", + cp, strip_height); + val = strip_height; /* safe fallback */ + } + + DBG (3, "init: option strip-height %d\n", val); + + if (num_devices == 0) + { + DBG (3, "init: setting global option strip-height to %d\n", + val); + strip_height = val; + } + else + { + DBG (3, "init: setting strip-height to %d for device %s\n", + val, devlist[0].sane.name); + devlist[0].strip_height = val; + } + } + else if (strncmp (cp, "io-mode", 7) == 0 && isspace (cp[7])) + { + + cp += 8; + cp = (char *) sanei_config_skip_whitespace (cp); + + if (strncmp (cp, "try_mode_uni", 12) == 0) + io_mode |= SANEI_PA4S2_OPT_TRY_MODE_UNI; + else if (strncmp (cp, "alt_lock", 8) == 0) + io_mode |= SANEI_PA4S2_OPT_ALT_LOCK; + else + DBG (2, "init: unknown io-mode `%s'\n", cp); + + DBG (3, "init: option io-mode %d\n", io_mode); + + } + else if (strncmp (cp, "wait-bank", 9) == 0 && isspace (cp[9])) + { + char *end; + int val; + + cp += 10; + + errno = 0; + val = strtol (cp, &end, 0); + + if ((end == cp || errno) || (val < 0)) + { + DBG (2, "init: invalid value `%s`, falling back to %d\n", + cp, wait_bank); + val = wait_bank; /* safe fallback */ + } + + DBG (3, "init: option wait-bank %d\n", val); + + if (num_devices == 0) + { + DBG (3, "init: setting global option wait-bank to %d\n", + val); + wait_bank = val; + } + else + { + DBG (3, "init: setting wait-bank to %d for device %s\n", + val, devlist[0].sane.name); + devlist[0].wait_bank = val; + } + } + else if (strncmp (cp, "bw", 2) == 0 && isspace (cp[2])) + { + char *end; + int val; + + cp += 3; + + errno = 0; + val = strtol (cp, &end, 0); + + if ((end == cp || errno) || (val < 0) || (val > 255)) + { + DBG (2, "init: invalid value `%s`, falling back to %d\n", + cp, bw); + val = bw; /* safe fallback */ + } + + DBG (3, "init: option bw %d\n", val); + + if (num_devices == 0) + { + DBG (3, "init: setting global option bw to %d\n", val); + bw = val; + } + else + { + DBG (3, "init: setting bw to %d for device %s\n", + val, devlist[0].sane.name); + devlist[0].bw = val; + } + } + else if (strncmp (cp, "wait-lamp", 9) == 0 && isspace (cp[9])) + { + char *end; + int val; + + cp += 10; + + errno = 0; + val = strtol (cp, &end, 0); + + if ((end == cp || errno) || (val < 0)) + { + DBG (2, "init: invalid value `%s`, falling back to %d\n", + cp, wait_lamp); + val = wait_lamp; /* safe fallback */ + } + + DBG (3, "init: option wait-lamp %d\n", val); + + if (num_devices == 0) + { + DBG (3, "init: setting global option wait-lamp to %d\n", + val); + wait_lamp = val; + } + else + { + DBG (3, "init: setting wait-lamp to %d for device %s\n", + val, devlist[0].sane.name); + devlist[0].wait_lamp = val; + } + } + else if (strncmp (cp, "buffer", 6) == 0 && isspace (cp[6])) + { + char *end; + long int val; + + cp += 7; + + errno = 0; + val = strtol (cp, &end, 0); + + if ((end == cp || errno) || (val < 0)) + { + DBG (2, "init: invalid value `%s`, falling back to %ld\n", + cp, buf_size); + val = buf_size; /* safe fallback */ + } + + DBG (3, "init: option buffer %ld\n", val); + + if (num_devices == 0) + { + DBG (3, "init: setting global option buffer to %ld\n", val); + buf_size = val; + } + else + { + DBG (3, "init: setting buffer to %ld for device %s\n", + val, devlist[0].sane.name); + devlist[0].buf_size = val; + } + } + else if (strncmp (cp, "niceload", 8) == 0) + { + DBG (3, "init: option niceload\n"); + niceload = 1; + } + else if (strncmp (cp, "use600", 6) == 0) + { + if (num_devices == 0) + DBG (2, "init: option use600 isn't a global option\n"); + else + { + DBG (3, "init: device %s is a 600 dpi scanner\n", + devlist[0].sane.name); + if (devlist[0].use600 == SANE_FALSE) + { + devlist[0].use600 = SANE_TRUE; + devlist[0].max_res = 600; + devlist[0].max_h_size = MUSTEK_PP_101x_MAX_H_PIXEL * 2; + devlist[0].max_v_size = MUSTEK_PP_101x_MAX_V_PIXEL * 2; + } + else + { + DBG (2, "init: option use600 alreay present...\n"); + } + } + } + else if (strncmp (cp, "auth", 4) == 0) + { +#ifndef HAVE_AUTHORIZATION + DBG (1, "init: user authentification support not compiled\n"); + DEBUG (); +#endif + if (num_devices == 0) + DBG (2, "init: option auth isn't a global option\n"); + else + { + DBG (3, "init: device %s requires user authentification\n", + devlist[0].sane.name); + devlist[0].requires_auth = SANE_TRUE; + } + } + else + DBG (2, "init: unknown option `%s'\n", cp); + continue; + } + else if ((strncmp (cp, "port", 4) == 0) && isspace (cp[4])) + { + + cp += 5; + cp = (char *) sanei_config_skip_whitespace (cp); + + DBG (3, "init: trying port `%s'\n", cp); + + if (*cp) + if (attach (cp) != SANE_STATUS_GOOD) + DBG (2, "init: couldn't attach to port `%s'\n", cp); + } + else if ((strncmp (cp, "name", 4) == 0) && isspace (cp[4])) + { + cp += 5; + cp = (char *) sanei_config_skip_whitespace (cp); + + if (num_devices == 0) + DBG (2, "init: 'name' only allowed after 'port'\n"); + else + { + DBG (3, "init: naming device %s '%s'\n", devlist[0].port, cp); + free (devlist[0].sane.name); + devlist[0].sane.name = strdup (cp); + } + } + else if ((strncmp (cp, "model", 5) == 0) && isspace (cp[5])) + { + cp += 6; + cp = (char *) sanei_config_skip_whitespace (cp); + + if (num_devices == 0) + DBG (2, "init: 'model' only allowed after 'port'\n"); + else + { + DBG (3, "init: device %s is a '%s'\n", devlist[0].port, cp); + free (devlist[0].sane.model); + devlist[0].sane.model = strdup (cp); + } + } + else if ((strncmp (cp, "vendor", 6) == 0) && isspace (cp[6])) + { + cp += 7; + cp = (char *) sanei_config_skip_whitespace (cp); + + if (num_devices == 0) + DBG (2, "init: 'vendor' only allowed after 'port'\n"); + else + { + DBG (3, "init: device %s is from '%s'\n", devlist[0].port, cp); + free (devlist[0].sane.vendor); + devlist[0].sane.vendor = strdup (cp); + } + } + else + DBG (2, "init: don't know what to do with `%s'\n", cp); + } + + fclose (fp); + + sanei_pa4s2_options (&io_mode, SANE_TRUE); + + return SANE_STATUS_GOOD; +} + +void +sane_exit (void) +{ + int i; + Mustek_PP_Device *dev; + + if (first_dev) + DBG (3, "exit: closing open devices\n"); + + while (first_dev) + { + dev = first_dev; + sane_close (dev); + } + + for (i = 0; i < num_devices; i++) + { + free (devlist[i].port); + free (devlist[i].sane.name); + free (devlist[i].sane.model); + free (devlist[i].sane.vendor); + } + + if (devlist != NULL) + free (devlist); + + if (devarray != NULL) + free (devarray); + + DBG (3, "exit: (...)\n"); + + num_devices = 0; +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + int i; + + DBG (129, "unused arg: local_only = %d\n", (int) local_only); + + if (devarray != NULL) + free (devarray); + + devarray = malloc ((num_devices + 1) * sizeof (devarray[0])); + + if (devarray == NULL) + { + DBG (2, "get_devices: not enough memory for device list\n"); + DEBUG (); + return SANE_STATUS_NO_MEM; + } + + for (i = 0; i < num_devices; i++) + devarray[i] = &devlist[i].sane; + + devarray[num_devices] = NULL; + *device_list = devarray; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Mustek_PP_Device *dev; + Mustek_PP_Descriptor *desc; + SANE_Status status; + int i, j, fd; + + DBG (3, "open: device `%s'\n", devicename); + + if (devicename[0]) + { + for (i = 0; i < num_devices; i++) + if (strcmp (devlist[i].sane.name, devicename) == 0) + break; + + if (i >= num_devices) + for (i = 0; i < num_devices; i++) + if (strcmp (devlist[i].port, devicename) == 0) + break; + + if (i >= num_devices) + { + DBG (2, "open: device doesn't exist\n"); + DEBUG (); + return SANE_STATUS_INVAL; + } + + desc = &devlist[i]; + +#ifdef HAVE_AUTHORIZATION + + if (desc->requires_auth == SANE_TRUE) + if (do_authorization (devlist[i].port) != SANE_STATUS_GOOD) + { + DBG (2, "open: access denied\n"); + DEBUG (); + return SANE_STATUS_ACCESS_DENIED; + } + +#else + + /* authorization should realy be compiled */ +/* DBG (1, "I wouldn't let myself be root if I were you\n"); */ + + if (desc->requires_auth == SANE_TRUE) + { + DBG (1, "open: ressource %s requires user authentification\n", + desc->port); + DBG (3, "open: ... which isn't compiled :-(\n"); + DBG (2, "open: access denied\n"); + return SANE_STATUS_ACCESS_DENIED; + } + +#endif + + status = sanei_pa4s2_open (devlist[i].port, &fd); + } + else + { + + if (num_devices == 0) + { + DBG (1, "open: no devices present\n"); + return SANE_STATUS_INVAL; + } + + DBG (3, "open: trying default device %s\n", devlist[0].sane.name); + +#ifdef HAVE_AUTHORIZATION + + if (devlist[0].requires_auth == SANE_TRUE) + if (do_authorization (devlist[0].port) != SANE_STATUS_GOOD) + { + DBG (2, "open: access denied\n"); + DEBUG (); + return SANE_STATUS_ACCESS_DENIED; + } + +#else + + /* authorization should realy be compiled */ +/* DBG (1, "I wouldn't let myself be root if I were you\n"); */ + if (devlist[0].requires_auth == SANE_TRUE) + { + DBG (1, "open: ressource %s requires user authentification\n", + devlist[0].port); + DBG (3, "open: ... which isn't compiled :-(\n"); + DBG (2, "open: access denied\n"); + return SANE_STATUS_ACCESS_DENIED; + } + +#endif + + + status = sanei_pa4s2_open (devlist[0].port, &fd); + + desc = &devlist[0]; + } + + if (status != SANE_STATUS_GOOD) + { + DBG (2, "open: device not found (%s)\n", sane_strstatus (status)); + DEBUG (); + return status; + } + + dev = (Mustek_PP_Device *) malloc (sizeof (*dev)); + + if (dev == NULL) + { + DBG (2, "open: not enough memory for device descriptor\n"); + DEBUG (); + sanei_pa4s2_close (fd); + return SANE_STATUS_NO_MEM; + } + + memset (dev, 0, sizeof (*dev)); + + dev->fd = fd; + + dev->desc = desc; + + dev->asic_id = dev->desc->asic; + dev->ccd_type = dev->desc->ccd; + + dev->motor_ctrl = 0x43; + dev->first_time = 1; + dev->unknown_value = 0xAA; + dev->expose_time = 0x64; + dev->send_voltages = 0; + + for (i = 0; i < 4; ++i) + for (j = 0; j < 256; ++j) + dev->gamma_table[i][j] = j; + + if (dev->desc->buf_size < 3 * dev->desc->max_h_size) + { + + DBG (2, "open: scan buffer to small, falling back to %d bytes\n", + dev->desc->max_h_size * 3); + dev->desc->buf_size = dev->desc->max_h_size * 3; + } + + dev->buf = malloc (dev->desc->buf_size); + dev->bufsize = dev->desc->buf_size; + + dev->dpi_range.min = SANE_FIX (50); + dev->dpi_range.max = SANE_FIX (dev->desc->max_res); + dev->dpi_range.quant = SANE_FIX (1); + + dev->x_range.min = 0; + dev->x_range.max = + PIXEL_TO_MM (dev->desc->max_h_size, (float) dev->desc->max_res); + dev->x_range.quant = 0; + + dev->y_range.min = 0; + dev->y_range.max = + PIXEL_TO_MM (dev->desc->max_v_size, (float) dev->desc->max_res); + dev->y_range.quant = 0; + + DBG (6, "open: range (0.0,0.0)-(%f,%f)\n", SANE_UNFIX (dev->x_range.max), + SANE_UNFIX (dev->y_range.max)); + + + if (dev->buf == NULL) + { + DBG (2, "open: not enough memory for scan buffer (%lu bytes)\n", + (long int) dev->desc->buf_size); + DEBUG (); + sanei_pa4s2_close (fd); + free (dev); + return SANE_STATUS_NO_MEM; + } + + init_options (dev); + + dev->next = first_dev; + first_dev = dev; + + sanei_pa4s2_enable (dev->fd, SANE_TRUE); + + lamp (dev, SANE_TRUE); + + if (dev->desc->use600) + dev->params.pixels_per_line = 10; + + dev->CCD.hwres = dev->CCD.res = dev->desc->max_res; + dev->CCD.mode = MUSTEK_PP_MODE_COLOR; + + return_home (dev, SANE_TRUE); + + + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + + dev->lamp_on = time (NULL); + dev->max_lines = dev->desc->strip_height; + + *handle = dev; + + DBG (3, "open: success\n"); + + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + Mustek_PP_Device *prev, *dev; + + /* remove handle from list of open handles: */ + prev = NULL; + + for (dev = first_dev; dev; dev = dev->next) + { + if (dev == handle) + break; + prev = dev; + } + + if (dev == NULL) + { + DBG (2, "close: unknown device\n"); + DEBUG (); + return; /* oops, not a handle we know about */ + } + + if (dev->state == MUSTEK_PP_STATE_SCANNING) + sane_cancel (handle); /* remember: sane_cancel is a macro and + expands to sane_mustek_pp_cancel ()... */ + + if (prev != NULL) + prev->next = dev->next; + else + first_dev = dev->next; /* mustek.c had a bug at this point :-) */ + + DBG (3, "close: maybe waiting for lamp...\n"); + while (time (NULL) - dev->lamp_on < 2) + sleep (1); + + sanei_pa4s2_enable (dev->fd, SANE_TRUE); + lamp (dev, SANE_FALSE); + return_home (dev, SANE_FALSE); + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + + sanei_pa4s2_close (dev->fd); + free (dev->buf); + + DBG (3, "close: device closed\n"); + + free (handle); + +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Mustek_PP_Device *dev = handle; + + if ((unsigned) option >= NUM_OPTIONS) + { + DBG (2, "get_option_descriptor: option %d doesn't exist\n", option); + DEBUG (); + return NULL; + } + + DBG (6, "get_option_descriptor: requested option %d (%s)\n", + option, dev->opt[option].name); + + return dev->opt + option; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + Mustek_PP_Device *dev = handle; + SANE_Status status; + SANE_Word w, cap; + + DBG (6, "control_option: option %d, action %d\n", option, action); + + if (info) + *info = 0; + + if (dev->state == MUSTEK_PP_STATE_SCANNING) + { + DBG (2, "control_option: device is scanning\n"); + DEBUG (); + return SANE_STATUS_DEVICE_BUSY; + } + + if ((unsigned int) option >= NUM_OPTIONS) + { + DBG (2, "control_option: option doesn't exist\n"); + DEBUG (); + return SANE_STATUS_INVAL; + } + + cap = dev->opt[option].cap; + + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + DBG (2, "control_option: option isn't active\n"); + DEBUG (); + return SANE_STATUS_INVAL; + } + + if (action == SANE_ACTION_GET_VALUE) + { + + switch (option) + { + /* word options: */ + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + case OPT_RESOLUTION: + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_NUM_OPTS: + case OPT_CUSTOM_GAMMA: +#ifdef HAVE_INVERSION + case OPT_INVERT: +#endif + + *(SANE_Word *) val = dev->val[option].w; + return SANE_STATUS_GOOD; + + /* word-array options: */ + case OPT_GAMMA_VECTOR: + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + + memcpy (val, dev->val[option].wa, dev->opt[option].size); + return SANE_STATUS_GOOD; + + /* string options: */ + case OPT_MODE: + + strcpy (val, dev->val[option].s); + return SANE_STATUS_GOOD; + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + + if (!SANE_OPTION_IS_SETTABLE (cap)) + { + DBG (2, "control_option: option can't be set\n"); + DEBUG (); + return SANE_STATUS_INVAL; + } + + status = sanei_constrain_value (dev->opt + option, val, info); + + if (status != SANE_STATUS_GOOD) + { + DBG (2, "control_option: constrain_value failed (%s)\n", + sane_strstatus (status)); + DEBUG (); + return status; + } + + switch (option) + { + /* (mostly) side-effect-free word options: */ + case OPT_RESOLUTION: + case OPT_TL_X: + case OPT_BR_X: + case OPT_TL_Y: + case OPT_BR_Y: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: +#ifdef HAVE_INVERSION + case OPT_INVERT: +#endif + + if (info) + *info |= SANE_INFO_RELOAD_PARAMS; + + dev->val[option].w = *(SANE_Word *) val; + return SANE_STATUS_GOOD; + + /* side-effect-free word-array options: */ + case OPT_GAMMA_VECTOR: + case OPT_GAMMA_VECTOR_R: + case OPT_GAMMA_VECTOR_G: + case OPT_GAMMA_VECTOR_B: + + memcpy (dev->val[option].wa, val, dev->opt[option].size); + return SANE_STATUS_GOOD; + + + /* options with side-effects: */ + + case OPT_CUSTOM_GAMMA: + w = *(SANE_Word *) val; + + if (w == dev->val[OPT_CUSTOM_GAMMA].w) + return SANE_STATUS_GOOD; /* no change */ + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS; + + dev->val[OPT_CUSTOM_GAMMA].w = w; + + if (w == SANE_TRUE) + { + const char *mode = dev->val[OPT_MODE].s; + + if (strcmp (mode, "Gray") == 0) + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (mode, "Color") == 0) + { + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + else + { + dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + } + + return SANE_STATUS_GOOD; + + case OPT_MODE: + { + char *old_val = dev->val[option].s; + + if (old_val) + { + if (strcmp (old_val, val) == 0) + return SANE_STATUS_GOOD; /* no change */ + + free (old_val); + } + + if (info) + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + + dev->val[option].s = strdup (val); + + dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + + if (strcmp (val, "Lineart") != 0) + dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; + + if (dev->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) + { + if (strcmp (val, "Gray") == 0) + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + else if (strcmp (val, "Color") == 0) + { + dev->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; + dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; + } + } + + return SANE_STATUS_GOOD; + } + } + } + + DBG (2, "control_option: unknown action\n"); + DEBUG (); + return SANE_STATUS_INVAL; +} + + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Mustek_PP_Device *dev = handle; + char *mode; + + if (dev->state != MUSTEK_PP_STATE_SCANNING) + { + + int dpi; + + memset (&dev->params, 0, sizeof (dev->params)); + + mode = dev->val[OPT_MODE].s; + + if (strcmp (mode, "Lineart") == 0) + dev->CCD.mode = MUSTEK_PP_MODE_LINEART; + else if (strcmp (mode, "Gray") == 0) + dev->CCD.mode = MUSTEK_PP_MODE_GRAYSCALE; + else + dev->CCD.mode = MUSTEK_PP_MODE_COLOR; + + + if (dev->val[OPT_PREVIEW].w == SANE_TRUE) + { + + if (dev->val[OPT_GRAY_PREVIEW].w == SANE_TRUE) + dev->CCD.mode = MUSTEK_PP_MODE_GRAYSCALE; + else + dev->CCD.mode = MUSTEK_PP_MODE_COLOR; + + } + + DBG (3, "get_parameters: mode `%s'\n", mode); + + dpi = (int) (SANE_UNFIX (dev->val[OPT_RESOLUTION].w) + 0.5); + dev->CCD.res = dpi; + + DBG (3, "get_parameters: %d dpi\n", dpi); + + if (dpi <= 100) + dev->CCD.hwres = 100; + else if (dpi <= 200) + dev->CCD.hwres = 200; + else if (dpi <= 300) + dev->CCD.hwres = 300; + else if (dpi <= 400) + dev->CCD.hwres = 400; + else if (dpi <= 600) + dev->CCD.hwres = 600; + + /* TODO: add 1505 code for hw resolution here */ + + DBG (6, "get_parameters: resolution %d dpi -> hardware %d dpi\n", + dev->CCD.res, dev->CCD.hwres); + +#ifdef HAVE_INVERSION + dev->CCD.invert = dev->val[OPT_INVERT].w; +#else + dev->CCD.invert = SANE_FALSE; +#endif + + dev->TopX = + MIN ((int) + (MM_TO_PIXEL (dev->val[OPT_TL_X].w, (float) dev->desc->max_res) + + 0.5), dev->desc->max_h_size); + dev->TopY = + MIN ((int) + (MM_TO_PIXEL (dev->val[OPT_TL_Y].w, (float) dev->desc->max_res) + + 0.5), dev->desc->max_v_size); + + dev->BottomX = + MIN ((int) + (MM_TO_PIXEL (dev->val[OPT_BR_X].w, (float) dev->desc->max_res) + + 0.5), dev->desc->max_h_size); + dev->BottomY = + MIN ((int) + (MM_TO_PIXEL (dev->val[OPT_BR_Y].w, (float) dev->desc->max_res) + + 0.5), dev->desc->max_v_size); + + dev->params.pixels_per_line = (dev->BottomX - dev->TopX) * dev->CCD.res + / dev->desc->max_res; + + dev->params.bytes_per_line = dev->params.pixels_per_line; + + switch (dev->CCD.mode) + { + + case MUSTEK_PP_MODE_LINEART: + dev->params.bytes_per_line /= 8; + + if ((dev->params.pixels_per_line % 8) != 0) + dev->params.bytes_per_line++; + + dev->params.depth = 1; + break; + + case MUSTEK_PP_MODE_GRAYSCALE: + dev->params.depth = 8; + dev->params.format = SANE_FRAME_GRAY; + break; + + case MUSTEK_PP_MODE_COLOR: + dev->params.depth = 8; + dev->params.bytes_per_line *= 3; + dev->params.format = SANE_FRAME_RGB; + break; + + } + + dev->params.last_frame = SANE_TRUE; + + dev->params.lines = (dev->BottomY - dev->TopY) * dev->CCD.res / + dev->desc->max_res; + + DBG (3, "get_parameters: %dx%d pixels\n", dev->params.pixels_per_line, + dev->params.lines); + + dev->CCD.skipimagebytes = dev->TopX; + + ASSERT (dev->params.lines > 0); + ASSERT (dev->params.pixels_per_line > 0); + + } + else + { + DBG (2, "get_parameters: can't set parameters while scanning\n"); + DEBUG (); + } + + if (params != NULL) + *params = dev->params; + + return SANE_STATUS_GOOD; + +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Mustek_PP_Device *dev = handle; + + if (dev->state == MUSTEK_PP_STATE_SCANNING) + { + DBG (2, "start: device is already scanning\n"); + DEBUG (); + + return SANE_STATUS_GOOD; + } + + sane_get_parameters (handle, NULL); + + DBG (3, "start: maybe waiting for lamp...\n"); + /* image quality increases when lamp isn't to cold */ + while ((time (NULL) - dev->lamp_on) < dev->desc->wait_lamp) + sleep (1); + + sanei_pa4s2_enable (dev->fd, SANE_TRUE); + + if (dev->desc->use600 == SANE_FALSE) + { + + config_ccd (dev); + set_voltages (dev); + + get_bank_count (dev); + ASSERT (dev->bank_count == 0); + + } + + return_home (dev, SANE_FALSE); + + dev->motor_step = 0; + + /* allocating memory for calibration */ + + dev->calib_g = malloc (dev->params.pixels_per_line); + + if (dev->calib_g == NULL) + { + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + DBG (2, "start: not enough memory for calibration buffer\n"); + DEBUG (); + + return SANE_STATUS_NO_MEM; + } + + if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + { + + dev->calib_r = malloc (dev->params.pixels_per_line); + dev->calib_b = malloc (dev->params.pixels_per_line); + + if ((dev->calib_r == NULL) || (dev->calib_b == NULL)) + { + free (dev->calib_g); + if (dev->calib_r) + free (dev->calib_r); + if (dev->calib_b) + free (dev->calib_b); + + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + + DBG (2, "start: not enough memory for color calib buffer\n"); + DEBUG (); + + return SANE_STATUS_NO_MEM; + } + } + + DBG (3, "start: executing calibration\n"); + + calibrate (dev); + + /* line distance correction for color mode */ + if (dev->ccd_type == 1) + { + + dev->blue_offs = 4; + dev->green_offs = 8; + + } + else + { + + dev->blue_offs = 8; + dev->green_offs = 16; + + } + + if (dev->desc->use600) + { + + dev->blue_offs = 2; + dev->green_offs = 4; + + if (dev->CCD.hwres > 300) + { + + dev->blue_offs = 4; + dev->green_offs = 8; + + } + + + } + + + if (dev->desc->use600) + { + + dev->motor_ctrl = 0x63; + + move_motor (dev, /* (dev->CCD.mode == MUSTEK_PP_MODE_COLOR ? */ 136 /* : 95) */ + + dev->TopY - + (dev->CCD.mode == + MUSTEK_PP_MODE_COLOR ? dev->green_offs : 0), SANE_TRUE); + + dev->motor_ctrl = 0x43; + } + else + /* musteka4s2 says 56 lines instead of 47 */ + move_motor (dev, 47 + dev->TopY - + (dev->CCD.mode == MUSTEK_PP_MODE_COLOR ? dev->green_offs : 0), + SANE_TRUE); + + if ((dev->ccd_type == 1) && (dev->desc->use600 == SANE_FALSE)) + sanei_pa4s2_writebyte (dev->fd, 6, 0x15); + + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + + if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + { + + int failed = SANE_FALSE, cnt; + + dev->CCD.line_step = + SANE_FIX ((float) dev->desc->max_res / (float) dev->CCD.res); + dev->rdiff = dev->CCD.line_step; + dev->bdiff = dev->rdiff + (dev->blue_offs << SANE_FIXED_SCALE_SHIFT); + dev->gdiff = dev->rdiff + (dev->green_offs << SANE_FIXED_SCALE_SHIFT); + + + + dev->red = malloc (sizeof (SANE_Byte *) * dev->green_offs); + dev->blue = malloc (sizeof (SANE_Byte *) * dev->blue_offs); + dev->green = malloc (dev->params.pixels_per_line); + + if ((dev->red == NULL) || (dev->blue == NULL) || (dev->green == NULL)) + { + + free (dev->calib_r); + free (dev->calib_b); + free (dev->calib_g); + + if (dev->red != NULL) + free (dev->red); + if (dev->green != NULL) + free (dev->green); + if (dev->blue != NULL) + free (dev->blue); + + DBG (2, "start: not enough memory for ld correction buffers\n"); + DEBUG (); + + return SANE_STATUS_NO_MEM; + + } + + for (cnt = 0; cnt < dev->green_offs; cnt++) + if ((dev->red[cnt] = malloc (dev->params.pixels_per_line)) == NULL) + failed = SANE_TRUE; + + for (cnt = 0; cnt < dev->blue_offs; cnt++) + if ((dev->blue[cnt] = malloc (dev->params.pixels_per_line)) == NULL) + failed = SANE_TRUE; + + if (failed == SANE_TRUE) + { + + free (dev->calib_r); + free (dev->calib_b); + free (dev->calib_g); + + + for (cnt = 0; cnt < dev->green_offs; cnt++) + if (dev->red[cnt] != NULL) + free (dev->red[cnt]); + for (cnt = 0; cnt < dev->blue_offs; cnt++) + if (dev->blue[cnt] != NULL) + free (dev->blue[cnt]); + + free (dev->red); + free (dev->green); + free (dev->blue); + + DBG (2, "start: not enough memory for ld correction buffers\n"); + DEBUG (); + + return SANE_STATUS_NO_MEM; + + } + + dev->redline = 0; + dev->blueline = 0; + dev->ccd_line = 0; + + } + + dev->line = 0; + dev->lines_left = dev->params.lines; + + dev->state = MUSTEK_PP_STATE_SCANNING; + + DBG (3, "start: device ready for scanning\n"); + + return SANE_STATUS_GOOD; + +} + + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, + SANE_Int * len) +{ + Mustek_PP_Device *dev = handle; + long int n, cnt; + + *len = 0; + + if ((dev->state != MUSTEK_PP_STATE_SCANNING) || + ((dev->lines_left == 0) && (dev->buflen == 0))) + { + /* device isn't scanning */ + + if (dev->state != MUSTEK_PP_STATE_SCANNING) + { + DBG (2, "read: device isn't scanning\n"); + DEBUG (); + dev->state = MUSTEK_PP_STATE_IDLE; + return SANE_STATUS_CANCELLED; + } + else + { + DBG (2, "read: no more image data available\n"); + DEBUG (); + + DBG (3, "read: finishing scan request using sane_cancel\n"); + + sane_cancel (handle); + + } + + return SANE_STATUS_EOF; + } + + + if (dev->buflen == 0) + { + + /* no image data in scan buffer -> get new */ + + if (dev->max_lines > 0) + cnt = MIN (dev->bufsize / dev->params.bytes_per_line, dev->max_lines); + else + cnt = dev->bufsize / dev->params.bytes_per_line; + + cnt = MIN (cnt, dev->lines_left); + + ASSERT (cnt > 0); + + DBG (3, "read: scanning next %ld lines\n", cnt); + + sanei_pa4s2_enable (dev->fd, SANE_TRUE); + + for (n = 0; n < cnt; n++) + { + + switch (dev->CCD.mode) + { + + case MUSTEK_PP_MODE_LINEART: + + get_lineart_line (dev, + &dev->buf[dev->params.bytes_per_line * n]); + break; + + case MUSTEK_PP_MODE_GRAYSCALE: + + get_grayscale_line (dev, + &dev->buf[dev->params.bytes_per_line * n]); + break; + + case MUSTEK_PP_MODE_COLOR: + + get_color_line (dev, &dev->buf[dev->params.bytes_per_line * n]); + break; + + } + + dev->lines_left--; + dev->line++; + + } + + dev->buflen = cnt * dev->params.bytes_per_line; + + if (dev->lines_left == 0) + { + DBG (3, "read: scan finished\n"); + + return_home (dev, SANE_TRUE); + + } + + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + + } + + /* return requested amount of data from buffer */ + + ASSERT (dev->buflen > 0); + + memcpy (buf, dev->buf, MIN (max_len, dev->buflen)); + + *len = MIN (max_len, dev->buflen); + + ASSERT (*len > 0) DBG (3, "read: delivering %d bytes\n", *len); + + if (*len == dev->buflen) + dev->buflen = 0; + else + { + dev->buflen -= *len; + memmove (dev->buf, &dev->buf[*len], dev->buflen); + } + + return SANE_STATUS_GOOD; + +} + +void +sane_cancel (SANE_Handle handle) +{ + Mustek_PP_Device *dev = handle; + + if (dev->state == MUSTEK_PP_STATE_SCANNING) + { + + /* device is scanning: return lamp and free line distance correction + and calibration buffers + */ + + DBG (3, "cancel: stopping current scan\n"); + + sanei_pa4s2_enable (dev->fd, SANE_TRUE); + return_home (dev, SANE_TRUE); + sanei_pa4s2_enable (dev->fd, SANE_FALSE); + + free (dev->calib_g); + + if (dev->CCD.mode == MUSTEK_PP_MODE_COLOR) + { + + int cnt; + + free (dev->calib_r); + free (dev->calib_b); + + for (cnt = 0; cnt < dev->green_offs; cnt++) + free (dev->red[cnt]); + for (cnt = 0; cnt < dev->blue_offs; cnt++) + free (dev->blue[cnt]); + + free (dev->red); + free (dev->blue); + free (dev->green); + + } + + dev->buflen = 0; + + dev->state = MUSTEK_PP_STATE_CANCELLED; + + } + else + { + + DBG (2, "cancel: device isn't scanning\n"); + DEBUG (); + } +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + DBG (129, "unused arg: handle = %p, non_blocking = %d\n", + handle, (int) non_blocking); + + DBG (2, "set_io_mode: not supported\n"); + + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + + DBG (129, "unused arg: handle = %p, fd = %p\n", handle, fd); + + DBG (2, "get_select_fd: not supported\n"); + + return SANE_STATUS_UNSUPPORTED; +} diff --git a/backend/mustek_pp_ccd.conf b/backend/mustek_pp_ccd.conf new file mode 100644 index 000000000..e6ccf6946 --- /dev/null +++ b/backend/mustek_pp_ccd.conf @@ -0,0 +1,99 @@ +# For documentation see sane-mustek_pp_ccd(5) + +# GLOBAL # + +# option io-mode [mode] must come before all port definitions, or it won't +# have the effect you'd expect + +# enable this option, if you think your scanner supports the UNI protocol +# note however that this might disable the better EPP protocol +#option io-mode try_mode_uni + +# choose between two different ways to lock to port +option io-mode alt_lock + +# set the maximal height (in lines) of a strip scanned (default: no limit) +#option strip-height 0 + +# wait n msecs for bank to change (default: 700 msecs) +# if this value is to low, stripes my appear in the scanned image +#option wait-bank 700 + +# size (in bytes) of scan buffer (default: 1 megabyte) +#option buffer 1048576 + +# try to avoid to heavy load. Note that this reduces scan speed +option niceload + +# Define the time the lamp has to be on before scan starts (default 5 secs) +#option wait-lamp 5 + + +# DEVICES # + +# specify the port your scanner is connected to. Possible are 0x378 (lp1) +# 0x278 (lp2) and 0x3bc (lp0) +# for 2.4.x+ kernels, this isn't true anymore. Here lp0 is assigned to the +# first available port, normally 0x378 +port 0x378 + + # the following options are local to this scanner + +# WELL KNOWN OPTIONS # + + # most scanners only need 200 - 250 msecs to change bank -> try it out + + # Mustek ScanExpress 6000 P + # name SE-6000P + # vendor Mustek + # option wait-lamp 15 + + # Mustek ScanExpress 600 SEP + # name SE-600SEP + # vendor Mustek + # option wait-lamp 15 + + # Mustek ScanMagic 4800 P + # name SM-4800P + # vendor Mustek + # option wait-lamp 15 + + # Mustek 600 III EP Plus + # name 600IIIEPP + # vendor Mustek + # option wait-lamp 15 # some models only need 5 secs... + + # Mustek ScanMagic/Express 1200 ED Plus (this scanner isn't yet supported!!!) + # name SM-1200EDP + # name SE-1200EDP + # vendor Mustek + # this scanner has an optical resolution of 600 dpi + # option use600 + # this scanner *must* use option niceload + # option niceload + + # Fidelity Imaging Solutions Inc. Gallery 4800 + # name Gallery-4800 + # vendor Fidelity-Imaging-Solutions + + # Viviscan Compact II + # name Compact-II + # vendor Viviscan + + # Medion MD9848 (aka Aldi-Scanner) + name MD9848 + vendor Medion + option wait-bank 250 + + # scan maximal 16 lines for one sane_read() call + option strip-height 16 + + # we just need 16 lines * 3 (rgb) colors * 300 dpi * 8.5 inch bytes + option buffer 122400 + + # Enable this option, if you want user authentification *and* if it's + # enabled at compile time + #option auth + + # use this option to define the maximal black value for lineart scans + #option bw 127 diff --git a/backend/mustek_pp_ccd.h b/backend/mustek_pp_ccd.h new file mode 100644 index 000000000..3dc4fac2d --- /dev/null +++ b/backend/mustek_pp_ccd.h @@ -0,0 +1,296 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2000 Jochen Eisinger + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. */ + +#ifndef mustek_pp_ccd_h +#define mustek_pp_ccd_h + +#include +#include + +#include "../include/sane/sanei_debug.h" + +/* #define for user authentification */ +/* #undef HAVE_AUTHORIZATION */ + +/* #define for invert option */ +/* #undef HAVE_INVERSION */ + +/* #define if you want to patch for ASIC 1505 */ +/* #undef PATCH_MUSTEK_PP_1505 */ + +#ifdef HAVE_AUTHORIZATION + +#define MAX_LINE_LEN 512 + + /* Here are the usernames and passwords stored + * the file name is relative to SANE_CONFIG_DIR */ + +#define PASSWD_FILE ".auth" +#define SEPARATOR ':' + +#define SALT "je" + +#endif + +enum Mustek_PP_Option +{ + OPT_NUM_OPTS = 0, + + OPT_MODE_GROUP, + OPT_MODE, + OPT_RESOLUTION, + OPT_PREVIEW, + OPT_GRAY_PREVIEW, + + OPT_GEOMETRY_GROUP, + OPT_TL_X, /* top-left x */ + OPT_TL_Y, /* top-left y */ + OPT_BR_X, /* bottom-right x */ + OPT_BR_Y, /* bottom-right y */ + + OPT_ENHANCEMENT_GROUP, + +#ifdef HAVE_INVERSION + + OPT_INVERT, + +#endif + OPT_CUSTOM_GAMMA, /* use custom gamma tables? */ + /* The gamma vectors MUST appear in the order gray, red, green, + blue. */ + OPT_GAMMA_VECTOR, + OPT_GAMMA_VECTOR_R, + OPT_GAMMA_VECTOR_G, + OPT_GAMMA_VECTOR_B, + + /* must come last: */ + NUM_OPTIONS +}; + +typedef struct CCD_info +{ + /* CCD mode (color/grayscale/lineart) */ + int mode; + /* inversion (true/false) */ + int invert; + /* how many positions to skip until scanable area starts */ + int skipcount; + /* how many positions to skip until scan area starts */ + int skipimagebytes; + /* total skip, adjusted to resolution */ + int adjustskip; + /* current resolution */ + int res; + /* current hardware resolution */ + int hwres; + /* how many positions to scan for one pixel */ + int res_step; + /* how many lines to scan for one scanline */ + int line_step; + /* current CCD channel (red/green or gray/blue) */ + int channel; +} +CCD_Info; + +typedef struct Mustek_PP_Descriptor +{ + /* SANE device */ + SANE_Device sane; + /* port no (0x378,0x278,0x3bc) */ + SANE_String port; + /* maximal resolution (300/600) */ + SANE_Int max_res; + /* max horiz */ + SANE_Int max_h_size; + /* max vert */ + SANE_Int max_v_size; + /* msecs to wait for bank change */ + unsigned int wait_bank; + /* lines to scan in one read */ + SANE_Int strip_height; + /* bytesize of scan buffer */ + long int buf_size; + /* ASIC id (0xa8 0xa5, 0xa2) */ + u_char asic; + /* CCD type (0,1,4,5) */ + u_char ccd; + /* devices requires authentification (yes/no) */ + int requires_auth; + /* color index to divide black from white (0-255) */ + int bw; + /* seconds to wait for the lamp */ + int wait_lamp; + /* use 600 dpi code (yes/no) */ + int use600; +} +Mustek_PP_Descriptor; + +typedef struct Mustek_PP_Device +{ + /* next device */ + struct Mustek_PP_Device *next; + + /* device descriptor */ + Mustek_PP_Descriptor *desc; + + /* fd for sanei_pa4s2 */ + int fd; + + /* CCD status */ + CCD_Info CCD; + /* used during calibration & return_home */ + CCD_Info Saved_CCD; + + /* options and values */ + SANE_Option_Descriptor opt[NUM_OPTIONS]; + Option_Value val[NUM_OPTIONS]; + + /* gamma tables (all, red, green, blue) */ + SANE_Int gamma_table[4][256]; + + /* scanning, idle, cancelled */ + int state; + + /* scan area */ + int TopX; + int TopY; + int BottomX; + int BottomY; + + /* ASIC & CCD (see mustek_pp_descritpor) */ + int asic_id; + int ccd_type; + + /* when did we turn the lamp on? */ + time_t lamp_on; + + /* image control for ASIC 0xA5 */ + int image_control; + + /* bank count (for all ASICs) */ + int bank_count; + + /* motor state for ASIC 0xA8 */ + int motor_phase; + int motor_step; + + /* those are used to count the hardware line the scanner is at, the + line the current bank is at and the lines we've scanned */ + int line; + int line_diff; + int ccd_line; + int lines_left; + int max_lines; + int redline; + int blueline; + + /* result from calibration */ + int blackpos; + + /* line distances during scan */ + int rdiff, gdiff, bdiff; + + /* calibration buffers (high cut) */ + SANE_Byte *calib_r; + SANE_Byte *calib_g; + SANE_Byte *calib_b; + + /* calibration values (low cut) */ + SANE_Byte ref_black, ref_red, ref_green, ref_blue; + + /* line distance buffers */ + SANE_Byte *green; + SANE_Byte **red; + SANE_Byte **blue; + + /* line distances */ + int blue_offs; + int green_offs; + + /* scan buffer */ + SANE_Byte *buf; + long int bufsize, buflen; + + /* current parameters */ + SANE_Parameters params; + SANE_Range dpi_range; + SANE_Range x_range; + SANE_Range y_range; + + + /* these are additional parameters for 600 dpi scanners */ + + SANE_Byte motor_ctrl; + SANE_Bool first_time; + SANE_Byte unknown_value; + SANE_Byte expose_time; + SANE_Byte voltages[3]; + SANE_Bool send_voltages; + + +} +Mustek_PP_Device; + + +#if (!defined __GNUC__ || __GNUC__ < 2 || \ + __GNUC_MINOR__ < (defined __cplusplus ? 6 : 4)) + +#define __PRETTY_FUNCTION__ "mustek_pp" + +#endif + +#define DEBUG() DBG(4, "%s(v%d.%d.%d-%s): line %d: debug exception\n", \ + __PRETTY_FUNCTION__, V_MAJOR, V_MINOR, \ + MUSTEK_PP_BUILD, MUSTEK_PP_STATE, __LINE__) + +#define ASSERT(cond) if (!(cond)) \ + { \ + DEBUG(); \ + DBG(1, "ASSERT(%s) failed\n", STRINGIFY(cond)); \ + DBG(1, "expect disaster...\n");\ + } + +/* Please note: ASSERT won't go away if you define NDEBUG, it just won't + * output a message when ASSERT failes. So if "cond" does anything, it will + * be executed, even if NDEBUG is defined... + */ + + +#endif /* mustek_pp_ccd_h */ diff --git a/configure b/configure index 743eb0f2c..0ecb7ea56 100755 --- a/configure +++ b/configure @@ -23641,8 +23641,8 @@ echo "$as_me: Manually selected backends: ${BACKENDS}" >&6;} BACKENDS="abaton agfafocus apple artec as6e avision bh canon \ canon630u coolscan coolscan2 dc25 dmc \ epson fujitsu gt68xx hp leo matsushita microtek \ - microtek2 mustek mustek_pp mustek_usb nec pie plustek plustek_pp \ - ricoh s9036 sceptre sharp \ + microtek2 mustek mustek_pp mustek_pp_ccd mustek_usb nec pie plustek \ + plustek_pp ricoh s9036 sceptre sharp \ sp15c st400 tamarack test teco1 teco2 teco3 umax umax_pp umax1220u \ artec_eplus48u ma1509 ibm hp5400" diff --git a/configure.in b/configure.in index 3f56d8199..e7cc0eeec 100644 --- a/configure.in +++ b/configure.in @@ -276,8 +276,8 @@ else BACKENDS="abaton agfafocus apple artec as6e avision bh canon \ canon630u coolscan coolscan2 dc25 dmc \ epson fujitsu gt68xx hp leo matsushita microtek \ - microtek2 mustek mustek_pp mustek_usb nec pie plustek plustek_pp \ - ricoh s9036 sceptre sharp \ + microtek2 mustek mustek_pp mustek_pp_ccd mustek_usb nec pie plustek \ + plustek_pp ricoh s9036 sceptre sharp \ sp15c st400 tamarack test teco1 teco2 teco3 umax umax_pp umax1220u \ artec_eplus48u ma1509 ibm hp5400" diff --git a/doc/Makefile.in b/doc/Makefile.in index 85351b88b..e150bd4dc 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -51,7 +51,8 @@ SECT5 = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 sane-dll.5 \ sane-mustek_usb.5 sane-sceptre.5 sane-canon_pp.5 sane-canon630u.5 \ sane-teco1.5 sane-teco2.5 sane-teco3.5 sane-test.5 sane-sp15c.5 \ sane-coolscan2.5 sane-hpsj5s.5 sane-gt68xx.5 sane-artec_eplus48u.5 \ - sane-ma1509.5 sane-ibm.5 sane-hp5400.5 sane-plustek_pp.5 + sane-ma1509.5 sane-ibm.5 sane-hp5400.5 sane-plustek_pp.5 \ + sane-mustek_pp_ccd.5 SECT7 = sane.7 MANPAGES = $(SECT1) $(SECT5) $(SECT7) READMES = README AUTHORS COPYING ChangeLog LICENSE NEWS PROBLEMS \ @@ -100,7 +101,8 @@ DISTFILES = Makefile.in backend-writing.txt descriptions.txt \ sane.tex saned.man scanimage.man sane-sceptre.man sane-canon_pp.man \ sane-teco1.man sane-teco2.man sane-teco3.man sane-test.man sane-sp15c.man \ sane-hpsj5s.man gamma4scanimage.man sane-gt68xx.man sane-artec_eplus48u.man \ - sane-ma1509.man sane-ibm.man sane-hp5400.man sane-plustek_pp.man + sane-ma1509.man sane-ibm.man sane-hp5400.man sane-plustek_pp.man \ + sane-mustek_pp_ccd.man .PHONY: all clean depend dist distclean html html-man install \ sane-html uninstall diff --git a/doc/descriptions/mustek_pp_ccd.desc b/doc/descriptions/mustek_pp_ccd.desc new file mode 100644 index 000000000..6f8473bd2 --- /dev/null +++ b/doc/descriptions/mustek_pp_ccd.desc @@ -0,0 +1,77 @@ +; +; SANE Backend specification file +; +; It's basically emacs-lisp --- so ";" indicates comment to end of line. +; All syntactic elements are keyword tokens, followed by a string or +; keyword argument, as specified. +; +; ":backend" *must* be specified. +; All other information is optional (but what good is the file without it?). +; + +:backend "mustek_pp_ccd" ; name of backend +:version "9" ; version of backend +;:status :alpha ; :alpha, :beta, :stable, :new +:manpage "sane-mustek_pp_ccd" ; name of manpage (if it exists) + ; backend's web page +:url "http://www.penguin-breeder.org/?page=mustek_pp" +:comment "Supports CCD type parallel port scanners from Mustek. A lot of Mustek CIS scanners are supported by the mustek_pp backend." + +:devicetype :scanner ; start of a list of devices.... + ; other types: :stillcam, :vidcam, + ; :meta, :api + +:mfg "Mustek" ; name a manufacturer +:url "http://www.mustek.com/" + +; These scanners are known to have an A4S2 chipset. What scanners work +; isn't actually known. Also the interfaces are only guesed + +:model "ScanExpress 6000 P" +:interface "Parport (EPP)" +:comment "works" +:status :good + +:model "ScanMagic 4800 P" +:interface "Parport (EPP)" +:comment "works" +:status :good + +:model "ScanExpress 1200 ED Plus" +:interface "Parport (EPP)" +:comment "works partly" +:status :minimal + + +:model "ScanMagic 1200 ED Plus" +:interface "Parport (EPP)" +:comment "works partly" +:status :minimal + +:model "600 III EP Plus" +:interface "Parport (SPP,EPP)" +:comment "works" +:status :good + +:model "ScanExpress 600 SEP" +:interface "Parport (EPP)" +:comment "works" +:status :good + +:mfg "Medion/Lifetec/Tevion/Cytron" +:url "http://www.medion.de/" + +:model "MD9848" +:interface "Parport (SPP)" +:comment "aka. ``Aldi Scanner'' - works" +:status :good + +:mfg "Fidelity Imaging Solutions Inc." + +:model "Gallery 4800" +:interface "Parport (EPP)" +:comment "works" +:status :good + +; :comment and :url specifiers are optional after :mfg, :model, :desc, +; and at the top-level. diff --git a/doc/sane-mustek_pp_ccd.man b/doc/sane-mustek_pp_ccd.man new file mode 100644 index 000000000..54bdb22db --- /dev/null +++ b/doc/sane-mustek_pp_ccd.man @@ -0,0 +1,474 @@ +.TH sane-mustek_pp_ccd 5 "3 Oct 2003" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" +.IX sane-mustek_pp_ccd +.SH NAME +sane-mustek_pp_ccd \- SANE backend for Mustek CCD parallel port flatbed scanners +.SH DESCRIPTION +The +.B sane-mustek_pp_ccd +library implements a SANE (Scanner Access Now Easy) backend that +provides access to Mustek CCD parallel port flatbed scanners. The +following scanners might work with this backend: +.PP +.RS +Model: ASIC ID: CCD Type: works: +.br +-------------------- --------- ---------- ------- +.br +SE 6000 P 1013 00 yes +.br +SM 4800 P 1013/1015 04/01 yes +.br +SE 1200 ED Plus 1015 01 partly +.br +SM 1200 ED Plus 1015 01 partly +.br +600 III EP Plus 1013/1015 00/01 yes +.br +SE 600 SEP 1013 ?? yes +.br +MD9848 1015 00 yes +.br +Gallery 4800 ???? ?? yes +.br +Viviscan Compact II 1013 00 yes +.RE +.PP +Other scanners, especially scanners using a CIS type sensor are not supported by this +backend. Have a look at the +.BR sane-mustek_pp (5) +backend for CIS scanners. +.PP +Please note that this backend is still under construction. I don't know +which scanners work or which will work in future releases. +.PP +You have to enable the backend explicitly in your dll.conf. Just remove the hash mark in the +line "mustek_pp_ccd". +.PP +Some scanners work faster when +.B EPP/ECP +is enabled in the BIOS. +.PP +Note that the backend needs to run as root. To allow user access to the scanner run the backend +through the network interface (See saned(1) and sane\-net(5)). Note also that the backend +.I does not +support +.I parport sharing , +i.e. if you try printing while scanning, your computer may crash. + +.SH "DEVICE NAMES" +This backend expects device names of the form: +.PP +.RS +.I port addr +.RE +.PP +Where +.B addr +is the base address of the port your scanner is attached to. Known ports are +.B 0x378 +(lp1) +.B 0x278 +(lp2) and +.B 0x3BC +(lp0). Note that if you are using a Kernel 2.2.x or better and you have only one +parallel port this port is named lp0 regardless of the base address. However, +this backend requires the base address of your port. If you are not sure which +port your scanner is connected to, have a look at your /etc/modules.conf. + +You can rename any device using the +.PP +.RS +.I name devname +.br +.I model model +.br +.I vendor vendor +.RE +.PP +options. These options aply to the last port option. + +.SH CONFIGURATION +Please make sure to edit mustek_pp_ccd.conf +.B before +you use the backend. +.PP +The contents of the +.B mustek_pp_ccd.conf +file is a list of options and device names that correspond to Mustek +scanners. Empty lines and lines starting with a hash mark (#) are +ignored. +.PP +The nine options supported are +.BR io\-mode , +.BR wait\-bank , +.BR strip\-height , +.BR niceload , +.BR auth , +.BR wait-lamp , +.BR bw , +.BR use600 , +and +.BR buffer . + +Option +.B io-mode +defines the mode of the sanei_pa4s2 interface. Possible values are +.BR try_mode_uni +and +.BR alt_lock . +This option may appear for each possible value. try_mode_uni allows +the UNI port mode, however this may disable the (better) EPP mode. alt_lock +toggles between two different ways to lock the port for scanner access. +This option must come before any port definition or it won't have the effect +you'd expect. + +Option +.B wait-bank +defines the maximal time in msecs the backend waits for the bank to change. The +default value is 700. If this option is given after the port option, only this +device is affected. If this value is to low, you will get wrong colors and +stripes in the scanned image. + +Option +.B strip-height +is a option that limits the maximum height of the strip scanned with +a single read command. The height is specified in lines. If this option is +given after the port option, only this device is affected. A strip\-height of +zero allows to scan as much as fits into the scan buffer. + +Option +.B niceload +tries to avoid to heavy load. Note that this reduces scan speed. This is a +global option. + +Option +.B auth +turns on user authentification for this scanner. Use this if you want access +control and if you have enabled this at compile time. This option must come +after a port option. At the moment, this option isn't really supported. + +Option +.B wait-lamp +allows you to control the time the backend waits for the lamp to warm up. The +time is specified in secs. The default value is 5 secs. Some scanners need +longer to warm up. A value of 0 makes the backend start without waiting. +However the backend will wait at least 2 secs before turning the lamp off +again. If this option is given after a port option it affects only this +device. + +Option +.B bw +defines the maximal color value that is considered black in lineart scans. +The default value is 127. Possible values are between 0 and 255. If this +option is given after a port option it affects only this device. + +Option +.B use600 +enables the 600 dpi handling code. It's not possible to detect wether a scanner +has an optical resolution of 300x600 dpi or 600x1200 dpi. The default is to +use the 300x600 dpi code. Use this option only if your scanner has an optical +resolution of 600x1200 dpi. This is a local only option. + +Option +.B buffer +allows you to change the size of the scan buffer. The size must be specified in +bytes. The default value is 1 megabyte. If this opttion is given after the port +option, only this device is affected. If you have limited the strip\-height, +you only need a scan buffer of +.PP +.RS +.B 8.5 * dpi * 3 * strip\-height bytes. +.RE +.PP + +.PP +A sample configuration file is shown below: +.PP +.RS +# GLOBAL # +.br + +.br +# enable this option, if you think your scanner +.br +# supports the UNI protocol +.br +# note however that this might disable the better +.br +# EPP protocol +.br +#option io-mode try_mode_uni +.br + +.br +# choose between two different ways to lock to port +.br +option io-mode alt_lock +.br + +.br +# set the maximal height (in lines) of a strip +.br +# scanned (default: no limit) +.br +#option strip-height 0 +.br + +.br +# wait n msecs for bank to change (default: 700 +.br +# msecs) if this value is to low, stripes my appear +.br +# in the scanned image +.br +#option wait-bank 700 +.br + +.br +# size (in bytes) of scan buffer (default: +.br +# 1 megabyte) +.br +#option buffer 1048576 +.br + +.br +# try to avoid to heavy load. Note that this +.br +# reduces scan speed +.br +option niceload +.br + +.br +# Define the time the lamp has to be on before +.br +# scan starts (default 5 secs) +.br +#option wait-lamp 5 +.br + +.br + +.br +# DEVICES # +.br + +.br +# specify the port your scanner is connected to. +.br +# Possible are 0x378 (lp1) 0x278 (lp2) and +.br +# 0x3bc (lp0) +.br +port 0x378 +.br + +.br +# the following options are local to this scanner +.br + +.br + # scan maximal 16 lines for one sane_read() call +.br + option strip-height 16 +.br + +.br + # we just need 16 * 3 * 300 * 8.5 bytes +.br + option buffer 122400 +.br + +.br + # this scanner needs max 250 msecs to change +.br + # the bank +.br + option wait-bank 250 +.br + +.br + # My scanner is a MD9848 from Medion using the +.br + # Mustek chipset +.br + name MD9848 +.br + vendor Medion +.br + +.br + # Enable this option, if you want user +.br + # authentification *and* if it's enabled at +.br + # compile time +.br + #option auth +.br + +.br + # Some scanners (especially ASIC 1013) need +.br + # longer to warm up. This option specifies +.br + # the time to wait for the lamp to get hot +.br + #option wait-lamp 15 +.br + +.br + # Use this option to define the maximal +.br + # black value in lineart scans +.br + #option bw 127 +.br + +.br + # Use this option for 600 dpi scanners +.br + # for example ScanExpress 1200 ED Plus +.br + #option use600 +.br + +.RE + + +.SH FILES +.TP +.I @CONFIGDIR@/mustek_pp_ccd.conf +The backend configuration file (see also description of +.B SANE_CONFIG_DIR +below). +.TP +.I @LIBDIR@/libsane\-mustek_pp_ccd.a +The static library implementing this backend. +.TP +.I @LIBDIR@/libsane\-mustek_pp_ccd.so +The shared library implementing this backend (present on systems that +support dynamic loading). + +.SH ENVIRONMENT +.TP +.B SANE_CONFIG_DIR +This environment variable specifies the list of directories that may +contain the configuration file. Under UNIX, the directories are +separated by a colon (`:'), under OS/2, they are separated by a +semi-colon (`;'). If this variable is not set, the configuration file +is searched in two default directories: first, the current working +directory (".") and then in @CONFIGDIR@. If the value of the +environment variable ends with the directory separator character, then +the default directories are searched after the explicitly specified +directories. For example, setting +.B SANE_CONFIG_DIR +to "/tmp/config:" would result in directories "tmp/config", ".", and +"@CONFIGDIR@" being searched (in this order). +.TP +.B SANE_DEBUG_MUSTEK_PP_CCD +If the library was compiled with debug support enabled, this +environment variable controls the debug level for this backend. E.g., +a value of 128 requests all debug output to be printed. Smaller +levels reduce verbosity. + +.PP +.RS +level debug output +.br +------- ------------------------------ +.br + 0 nothing +.br + 1 errors +.br + 2 warnings & minor errors +.br + 3 additional information +.br + 4 debug information +.br + 5 code flow (not supported yet) +.br + 6 special debug information +.RE +.PP +.TP +.B SANE_DEBUG_SANEI_PA4S2 +This variable sets the debug level for the SANE interface for the Mustek +chipset A4S2. Note that enabling this will spam your terminal with some +million lines of debug output. + +.PP +.RS +level debug output +.br +------- ------------------------------- +.br + 0 nothing +.br + 1 errors +.br + 2 warnings +.br + 3 things nice to know +.br + 4 code flow +.br + 5 detailed code flow +.br + 6 everything +.RE +.PP + +.PP +.SH "SEE ALSO" +sane(7), sane\-mustek_pp(5), sane\-mustek_usb, sane\-mustek(5), sane\-net(5), saned(1) + +.TP +For latest bug fixes and information see +.I http://www.penguin-breeder.org/?page=mustek_pp + +.SH AUTHOR +Jochen Eisinger + +.SH BUGS +Too many... please send bug reports to +.I sane\-devel@lists.alioth.debian.org +(note that you have to subscribe first to the list before you can send +emails... see http://www.sane-project.org/mailing-lists.html) +.PP +.RS +* 1013 support isn't bullet proofed +.br +* 1505 support isn't even present +.br +* 1015 only works for CCD type 00 & 01 (01 only bw/grayscale) +.RE + + +.SH BUG REPORTS +If something doesn't work, please contact me. But I need some information about +your scanner to be able to help you... + +.TP +.I SANE version +run "scanimage -V" to determine this +.TP +.I the backend version and your scanner hardware +run "SANE_DEBUG_MUSTEK_PP_CCD=128 scanimage -L" as root. If you don't get any output +from the mustek_pp_ccd backend, make sure a line "mustek_pp_ccd" is included into +your @CONFIGDIR@/dll.conf. +If your scanner isn't detected, make sure you've defined the right port address +in your mustek_pp_ccd.conf. +.TP +.I the name of your scanner/vendor +also a worthy information. Please also include the optical resolution and lamp type of your scanner, both can be found in the manual of your scanner. +.TP +.I any further comments +if you have comments about the documentation (what could be done better), or you +think I should know something, please include it. +.TP +.I some nice greetings +. diff --git a/doc/sane.man b/doc/sane.man index 6ad3ed36f..aa8d7ca99 100644 --- a/doc/sane.man +++ b/doc/sane.man @@ -1,4 +1,4 @@ -.TH sane 7 "30 Sept 2003" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" +.TH sane 7 "3 Oct 2003" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" .IX sane .SH NAME @@ -308,10 +308,16 @@ for details. .TP .B mustek_pp The mustek_pp backend provides access to Mustek parallel port flatbed -scanners. See +scanners with a CIS sensor. See .BR sane\-mustek_pp (5) for details. .TP +.B mustek_pp_ccd +The mustek_pp_ccd backend provides access to Mustek parallel port flatbed +scanners with a CCD sensor. See +.BR sane\-mustek_pp_ccd (5) +for details. +.TP .B mustek_usb The mustek_usb backend provides access to some Mustek ScanExpress USB flatbed scanners. See @@ -761,6 +767,7 @@ for details). .BR sane\-microtek (5), .BR sane\-mustek (5), .BR sane\-mustek_pp (5), +.BR sane\-mustek_pp_ccd (5), .BR sane\-mustek_usb (5), .BR sane\-nec (5), .BR sane\-net (5),