diff -uNr sane-backends-1.0.17/acinclude.m4 sane-backends-1.0.18/acinclude.m4 --- sane-backends-1.0.17/acinclude.m4 2005-10-29 20:34:06.000000000 +0200 +++ sane-backends-1.0.18/acinclude.m4 2006-05-26 21:38:37.000000000 +0200 @@ -255,8 +255,8 @@ ]) # -# Checks for jpeg library >= v6B (61), needed for DC210, DC240, and -# GPHOTO2 backends. +# Checks for jpeg library >= v6B (61), needed for DC210, DC240, +# GPHOTO2 and dell1600n_net backends. AC_DEFUN([SANE_CHECK_JPEG], [ AC_CHECK_LIB(jpeg,jpeg_start_decompress, @@ -276,6 +276,16 @@ ],) ]) +# Checks for tiff library dell1600n_net backend. +AC_DEFUN([SANE_CHECK_TIFF], +[ + AC_CHECK_LIB(tiff,TIFFFdOpen, + [ + AC_CHECK_HEADER(tiffio.h, + [sane_cv_use_libtiff="yes"; LIBS="${LIBS} -ltiff"],) + ],) +]) + # # Checks for pthread support AC_DEFUN([SANE_CHECK_LOCKING], diff -uNr sane-backends-1.0.17/aclocal.m4 sane-backends-1.0.18/aclocal.m4 --- sane-backends-1.0.17/aclocal.m4 2005-12-18 14:15:58.000000000 +0100 +++ sane-backends-1.0.18/aclocal.m4 2006-07-03 00:22:01.000000000 +0200 @@ -1,7 +1,7 @@ -# generated automatically by aclocal 1.8.5 -*- Autoconf -*- +# generated automatically by aclocal 1.9.6 -*- Autoconf -*- -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 -# Free Software Foundation, Inc. +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. diff -uNr sane-backends-1.0.17/AUTHORS sane-backends-1.0.18/AUTHORS --- sane-backends-1.0.17/AUTHORS 2005-12-06 13:03:05.000000000 +0100 +++ sane-backends-1.0.18/AUTHORS 2006-06-11 23:16:42.000000000 +0200 @@ -26,18 +26,21 @@ dc25: Peter Fales (*) dc210: Brian J. Murrell dc240: Peter Fales (*) + dell1600n_net: Jon Chambers (*) dll: David Mosberger dmc: David F. Skoll epson: Karl Heinz Kremer (*) fujitsu: Randolph Bentson, Frederik Ramm, Oliver Schirrmeister (*), m. allan noah (*) gphoto2: Peter Fales (*) - genesys: Henning Meier-Geinitz (*), Gerhard Jaeger (*), Stephane Voltz (*) + genesys: Henning Meier-Geinitz (*), Gerhard Jaeger (*), Stephane Voltz (*), + Pierre Willenbrock (*) gt68xx: Sergey Vlasov, Andreas Nowack, David Stevenson, and Henning Meier-Geinitz (*) hp: Peter Kirchgessner (*) Geoffrey Dairiki hpsj5s: Max Vorobiev + hp3500: Troy Rollo (*) hp4200: Adrian Perez Jorge, Andrew John Lewis, Arnar Mar Hrafnkelsson, Frank Zago, Henning Meier-Geinitz (*) hp5400: Martijn van Oosterhout, Thomas Soumarmon (*) @@ -58,6 +61,7 @@ niash: Ullrich Sigwanz (*), Bertrik Sikken pie: Simon Munton (*) pint: Gordon Matzigkeit + pixma: Wittawat Yamwong (*) plustek: Gerhard Jaeger (*) plustek_pp: Rick Bronson (former pp driver-code), Gerhard Jaeger (*) pnm: Andreas Beck, Gordon Matzigkeit, David Mosberger, Michael @@ -75,6 +79,7 @@ Sable, Henrik Johansson, Chris Bagwell, and Oliver Schwartz (*) sp15c: Randolph Bentson st400: Ingo Wilken + stv680: Gerard Klaver (*) tamarack: Rogier Wolff teco1: Frank Zago (*) teco2: Frank Zago, Gerard Klaver (*) @@ -83,7 +88,7 @@ u12: Gerhard Jaeger (*) umax: Oliver Rauch (*) and Michael K. Johnson umax_pp: Stéphane Voltz (*) - umax1220u: Marcio L. Teixeira + umax1220u: Marcio L. Teixeira, Patrick Lessard v4l: Juergen G. Schimmer, and Henning Meier-Geinitz (*) Frontends: @@ -138,7 +143,7 @@ David Stevenson Didier Carlier Earle F. Philhower III -Eddy De Greef +Eddy De Greef Eugene S. Weiss Feico W. Dillema Franck Schneider @@ -159,6 +164,7 @@ James Perry Jeff Freedman Jochen Eisinger +Jon Chambers Juergen G. Schimmer Julien Blache Karl Anders Øygard @@ -188,10 +194,12 @@ Oliver Rauch Oliver Schirrmeister Oliver Schwartz +Patrick Lessard Patrick Reynolds Peter Fales Peter Kirchgessner Petter Reinholdtsen +Pierre Willenbrock Randolph Bentson Rene Rebe Roger Wolff @@ -205,5 +213,7 @@ Tom Martone Tom Wang Tristan Tarrant +Troy Rollo Ullrich Sigwanz Ulrich Deiters +Wittawat Yamwong diff -uNr sane-backends-1.0.17/backend/abaton.conf sane-backends-1.0.18/backend/abaton.conf --- sane-backends-1.0.17/backend/abaton.conf 1999-08-09 20:05:55.000000000 +0200 +++ sane-backends-1.0.18/backend/abaton.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -scsi ABATON -/dev/scanner diff -uNr sane-backends-1.0.17/backend/abaton.conf.in sane-backends-1.0.18/backend/abaton.conf.in --- sane-backends-1.0.17/backend/abaton.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/abaton.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +scsi ABATON +/dev/scanner diff -uNr sane-backends-1.0.17/backend/agfafocus.conf sane-backends-1.0.18/backend/agfafocus.conf --- sane-backends-1.0.17/backend/agfafocus.conf 1999-08-09 20:05:59.000000000 +0200 +++ sane-backends-1.0.18/backend/agfafocus.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -/dev/scanner - diff -uNr sane-backends-1.0.17/backend/agfafocus.conf.in sane-backends-1.0.18/backend/agfafocus.conf.in --- sane-backends-1.0.17/backend/agfafocus.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/agfafocus.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +/dev/scanner + diff -uNr sane-backends-1.0.17/backend/apple.conf sane-backends-1.0.18/backend/apple.conf --- sane-backends-1.0.17/backend/apple.conf 1999-08-09 20:05:53.000000000 +0200 +++ sane-backends-1.0.18/backend/apple.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -scsi APPLE -/dev/scanner diff -uNr sane-backends-1.0.17/backend/apple.conf.in sane-backends-1.0.18/backend/apple.conf.in --- sane-backends-1.0.17/backend/apple.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/apple.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +scsi APPLE +/dev/scanner diff -uNr sane-backends-1.0.17/backend/artec.conf sane-backends-1.0.18/backend/artec.conf --- sane-backends-1.0.17/backend/artec.conf 1999-08-09 20:05:54.000000000 +0200 +++ sane-backends-1.0.18/backend/artec.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -scsi ULTIMA -/dev/scanner - diff -uNr sane-backends-1.0.17/backend/artec.conf.in sane-backends-1.0.18/backend/artec.conf.in --- sane-backends-1.0.17/backend/artec.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/artec.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,3 @@ +scsi ULTIMA +/dev/scanner + diff -uNr sane-backends-1.0.17/backend/artec_eplus48u.conf sane-backends-1.0.18/backend/artec_eplus48u.conf --- sane-backends-1.0.17/backend/artec_eplus48u.conf 2005-11-26 12:01:58.000000000 +0100 +++ sane-backends-1.0.18/backend/artec_eplus48u.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,416 +0,0 @@ -# artec_eplus48u - SANE Backend configuration file -# For use with the Artec E+ 48U scanner -# This scanner is also sold as -# Tevion MD 9693, Medion MD 9705, Medion MD 9693, -# Trust Easy Webscan 19200, Memorex Mem 48U or Umax AstraSlim SE. -# -# The USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -usb 0x05d8 0x4003 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/Artec48.usb -option ePlusPro 0 - -# -# for adjusting the default gamma values -# - -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. If you do not want to get your -# scanner reported as "Artec E+ 48U", then change the option accordingly. - -option vendorString "Artec" -option modelString "E+ 48U" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner - - -# Another USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -# Since the Trust Easy Webscan 19200 uses a different product id, we add -# another usb section here. - -usb 0x05d8 0x4006 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/Artec48.usb - -# -# for adjusting the default gamma values -# -option ePlusPro 1 -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. -#If calibrate device is set to 0, this values are used instead of the values -#which were calculated during calibration. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. The Trust Easy Webscan 19200 -# has a different product id, therefore we can overwrite the vendor and model -# string here. - -option vendorString "Trust" -option modelString "Easy Webscan 19200" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner - -# Another USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -# Since the Memorex Mem48U uses a different product id, we add -# another usb section here. - -usb 0x05d8 0x4005 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/Artec48.usb - -# -# for adjusting the default gamma values -# - -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. -#If calibrate device is set to 0, this values are used instead of the values -#which were calculated during calibration. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. The Umax AstraSlim SE -# has a different product id, therefore we can overwrite the vendor and model -# string here. - -option vendorString "Memorex" -option modelString "MEM 48U" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner - -# Another USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -# Since the Trust 240H Easy Webscan Gold uses a different product id, we add -# another usb section here. - -usb 0x05d8 0x4007 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/Artec48.usb - -# -# for adjusting the default gamma values -# -option ePlusPro 1 -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. -#If calibrate device is set to 0, this values are used instead of the values -#which were calculated during calibration. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. The Trust 240H Easy Webscan Gold -# has a different product id, therefore we can overwrite the vendor and model -# string here. - -option vendorString "Trust" -option modelString "240H Easy Webscan Gold" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner - -# Another USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -# Since the Umax AstraSlim SE uses a different product id, we add -# another usb section here. - -usb 0x05d8 0x4009 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/Artec48.usb - -# -# for adjusting the default gamma values -# - -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. -#If calibrate device is set to 0, this values are used instead of the values -#which were calculated during calibration. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. The Umax AstraSlim SE -# has a different product id, therefore we can overwrite the vendor and model -# string here. - -option vendorString "Umax" -option modelString "AstraSlim SE" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner - -# Another USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -# This section is for the Artec E+ Pro -# Note, that the name of the firmware file is called 1200.usb for -# this device - -usb 0x05d8 0x4004 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/1200.usb - -# -# Tell the backend that this is an Artec E+ Pro -# by setting the option ePlusPro to 1 -# - -option ePlusPro 1 - -# -# for adjusting the default gamma values -# - -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. -#If calibrate device is set to 0, this values are used instead of the values -#which were calculated during calibration. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. The Umax AstraSlim SE -# has a different product id, therefore we can overwrite the vendor and model -# string here. - -option vendorString "Artec" -option modelString "E+ Pro" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner - -# Another USB section: -# each device needs at least the following line: -# usb vendor-ID and product-ID -# Every device configuration in this file must begin with an usb entry. -# This section is for the UMAX AstraSlim 1200 SE -# Note, that the name of the firmware file is called 1200.usb for -# this device - -usb 0x05d8 0x4010 - -# -# Path to the firmware file -# This file comes with the Windows driver -# The scanner won't work without it -# - -option artecFirmwareFile /usr/share/sane/artec_eplus48u/1200.usb - -# -# for adjusting the default gamma values -# - -option redGamma 1.0 -option greenGamma 1.0 -option blueGamma 1.0 -option masterGamma 1.9 - -#Use this options to set the default offset and exposure time values. -#If calibrate device is set to 0, this values are used instead of the values -#which were calculated during calibration. - -option redOffset 0x28 -option greenOffset 0x2f -option blueOffset 0x2f -option redExposure 0xa7 -option greenExposure 0x116 -option blueExposure 0xdc - -# The vendor and model string -# This string is displayed by the frontends. The Umax AstraSlim 1200 SE -# has a different product id, therefore we can overwrite the vendor and model -# string here. - -option vendorString "UMAX" -option modelString "AstraSlim 1200 SE" - -# -# device-name -# -# If autodetection does not work, then you can specify the device here -# The device entry must be the last one in this config file (or the last one before a -# new usb entry). - -#If you are using libusb, a device looks like this: -#device libusb:001:002 - -#If you are using the scanner module (kernel driver), a device looks like this: -#device /dev/usbscanner diff -uNr sane-backends-1.0.17/backend/artec_eplus48u.conf.in sane-backends-1.0.18/backend/artec_eplus48u.conf.in --- sane-backends-1.0.17/backend/artec_eplus48u.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/artec_eplus48u.conf.in 2006-04-20 19:14:26.000000000 +0200 @@ -0,0 +1,119 @@ +# artec_eplus48u - SANE Backend configuration file + +# This section is for use with the Artec E+ 48U scanner +# This scanner is also sold as +# Tevion MD 9693, Medion MD 9705, Medion MD 9693, Medion MD4394 +# This sections contains verbose description of each option. +# For other scanners, see below. +# +# The USB section: +# each device needs at least the following line: +# usb vendor-ID and product-ID +# Every device configuration in this file must begin with an usb entry. +usb 0x05d8 0x4003 + +# Path to the firmware file +# This file comes with the Windows driver +# The scanner won't work without it +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb + +# Is the scanner an Artec E Plus Pro (or compatible)? 0 = no, 1 = yes +option ePlusPro 0 + +# for adjusting the default gamma values +option redGamma 1.0 +option greenGamma 1.0 +option blueGamma 1.0 +option masterGamma 1.9 + +#Use this options to set the default offset and exposure time values. +option redOffset 0x28 +option greenOffset 0x2f +option blueOffset 0x2f +option redExposure 0xa7 +option greenExposure 0x116 +option blueExposure 0xdc + +# The vendor and model string +# This string is displayed by the frontends. If you do not want to get your +# scanner reported as "Artec E+ 48U", then change the option accordingly. +option vendorString "Artec" +option modelString "E+ 48U" + +# device-name +# +# If autodetection does not work, then you can specify the device here +# The device entry must be the last one in this config file (or the last one before a +# new usb entry). +#If you are using libusb, a device looks like this: +#device libusb:001:002 +#If you are using the scanner module (kernel driver), a device looks like this: +#device /dev/usbscanner + +# ---------------------------------------------------------------------------- + +# Since the Trust Easy Webscan 19200 uses a different product id, we add +# another usb section here. +usb 0x05d8 0x4006 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb +option vendorString "Trust" +option modelString "Easy Webscan 19200" + +# ---------------------------------------------------------------------------- + +# Since the Memorex Mem48U uses a different product id, we add +# another usb section here. +usb 0x05d8 0x4005 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb +option vendorString "Memorex" +option modelString "MEM 48U" + +# ---------------------------------------------------------------------------- + +# Since the Trust 240H Easy Webscan Gold uses a different product id, we add +# another usb section here. +usb 0x05d8 0x4007 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb +option ePlusPro 1 +option vendorString "Trust" +option modelString "240H Easy Webscan Gold" + +# ---------------------------------------------------------------------------- + +# Since the UMAX AstraSlim SE uses a different product id, we add +# another usb section here. +usb 0x05d8 0x4009 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb +option vendorString "UMAX" +option modelString "AstraSlim SE" + +# ---------------------------------------------------------------------------- + +# This section is for the Artec E+ Pro +# Note, that the name of the firmware file is called 1200.usb for +# this device +usb 0x05d8 0x4004 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/1200.usb +option ePlusPro 1 +option vendorString "Artec" +option modelString "E+ Pro" + +# ---------------------------------------------------------------------------- + +# This section is for the UMAX AstraSlim 1200 SE +# Note, that the name of the firmware file is called 1200.usb for +# this device +usb 0x05d8 0x4010 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/1200.usb +option vendorString "UMAX" +option modelString "AstraSlim 1200 SE" + +# ---------------------------------------------------------------------------- + +# Since the Yakumo Scan50 uses a different product id, we add +# another usb section here. +usb 0x05d8 0x4011 +option artecFirmwareFile @DATADIR@/sane/artec_eplus48u/Artec48.usb +option vendorString "Yakumo" +option modelString "Scan50" + diff -uNr sane-backends-1.0.17/backend/avision.c sane-backends-1.0.18/backend/avision.c --- sane-backends-1.0.17/backend/avision.c 2005-12-06 11:06:11.000000000 +0100 +++ sane-backends-1.0.18/backend/avision.c 2006-06-06 18:32:02.000000000 +0200 @@ -767,7 +767,7 @@ #include #define BACKEND_NAME avision -#define BACKEND_BUILD 182 /* avision backend BUILD version */ +#define BACKEND_BUILD 201 /* avision backend BUILD version */ #include #include @@ -811,23 +811,23 @@ { "AVISION", "AV120", 0x0638, 0x0A27, "Avision", "AV120", - AV_INT_BUTTON}, + 0}, /* comment="sheetfed scanner" */ - /* status="basic" */ + /* status="complete" */ { "AVISION", "AV121", 0x0638, 0x0A3C, "Avision", "AV121", AV_INT_BUTTON | AV_NO_MATRIX}, /* comment="sheetfed scanner" */ - /* status="basic" */ + /* status="untested" */ { "AVISION", "AV122", - 0x0638, 0x0A3C, + 0x0638, 0x0A33, "Avision", "AV122", - AV_INT_BUTTON | AV_NO_MATRIX}, + AV_INT_BUTTON | AV_NO_MATRIX | AV_2ND_LINE_INTERLACED}, /* comment="sheetfed duplex scanner" */ - /* status="basic" */ + /* status="good" */ { "AVISION", "AV210", 0x0638, 0x0A24, @@ -846,9 +846,9 @@ { "AVISION", "AV210C2", 0x0638, 0x0A3A, "Avision", "AV210C2", - AV_INT_BUTTON | AV_ACCEL_TABLE}, + AV_INT_BUTTON}, /* comment="sheetfed scanner" */ - /* status="untested" */ + /* status="complete" */ { "AVISION", "AVISION AV220", 0x0638, 0x0A23, @@ -864,6 +864,13 @@ /* comment="duplex! sheetfed scanner" */ /* status="complete" */ + { "AVISION", "AV220C2", + 0x0638, 0x0A2A, + "Avision", "AV220C2", + AV_INT_BUTTON}, + /* comment="duplex! sheetfed scanner" */ + /* status="complete" */ + { "AVISION", "AV240SC", 0, 0, "Avision", "AV240SC", @@ -901,27 +908,20 @@ /* status="untested" */ { "AVISION", "AV610", - 0x638, 0xa19, + 0x0638, 0x0a19, "Avision", "AV610", AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE}, /* status="good" */ - - /* { "AVISION", "AVA6", - 0x638, 0xa22, - "Avision", "AVA6", - 0}, */ - /* comment="probably a CPU-less device" */ - /* status="untested" */ - /* { "AVISION", "AVA6", - 0x638, 0xa3b, - "Avision", "AVA6", - 0}, */ - /* comment="probably a CPU-less device" */ - /* status="untested" */ - + { "AVISION", "AM3000 Series", + 0x0638, 0x0a41, + "Avision", "AM3000 Series", + 0}, + /* comment="MFD" */ + /* status="basic" */ + { "AVision", "DS610CU", - 0x638, 0xa16, + 0x0638, 0x0a16, "Avision", "DS610CU Scancopier", 0}, /* comment="1 pass, 600 dpi, A4" */ @@ -965,7 +965,7 @@ { "AVISION", "AV600U", 0x0638, 0x0A13, "Avision", "AV600U", - AV_NO_MATRIX | AV_MULTI_CALIB_CMD}, + AV_NO_MATRIX | AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT}, /* comment="1 pass, 600 dpi" */ /* status="good" */ @@ -1023,14 +1023,14 @@ "Avision", "AV820C", 0}, /* comment="1 pass, ??? dpi" */ - /* status="good" */ + /* status="complete" */ { "AVISION", "AV820C Plus", 0, 0, "Avision", "AV820C Plus", 0}, /* comment="1 pass, ??? dpi" */ - /* status="untested" */ + /* status="complete" */ { "AVISION", "AV830C", 0, 0, @@ -1063,7 +1063,7 @@ { "AVISION", "AV3200C", 0, 0, "Avision", "AV3200C", - AV_NON_INTERLACED_DUPLEX_300}, + AV_NON_INTERLACED_DUPLEX_300 | AV_FASTER_WITH_FILTER}, /* comment="1 pass, ??? dpi" */ /* status="complete" */ @@ -1072,7 +1072,14 @@ "Avision", "AV3800C", AV_NON_INTERLACED_DUPLEX_300}, /* comment="1 pass, ??? dpi" */ - /* status="good" */ + /* status="complete" */ + + { "AVISION", "AV3850SU", + 0x0638, 0x0a66, + "Avision", "AV3850SU", + 0}, + /* comment="1 pass, ??? dpi" */ + /* status="complete" */ { "AVISION", "FB6000E", 0, 0, @@ -1084,7 +1091,7 @@ { "AVISION", "AV8000S", 0, 0, "Avision", "AV8000S", - 0}, + AV_DOES_NOT_KEEP_WINDOW}, /* comment="1 pass, 1200 dpi, A3 - regularly tested" */ /* status="complete" */ @@ -1107,7 +1114,7 @@ "Avision", "IT8300", AV_NON_INTERLACED_DUPLEX_300 | AV_ACCEL_TABLE}, /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */ - /* status="complete" */ + /* status="good" */ { "AVISION", "@V5100", 0x0638, 0x0A45, @@ -1135,7 +1142,7 @@ { "HP", "ScanJet 5370C", 0x03f0, 0x0701, "Hewlett-Packard", "ScanJet 5370C", - AV_MULTI_CALIB_CMD }, + AV_MULTI_CALIB_CMD | AV_NO_MATRIX}, /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */ /* status="good" */ @@ -1150,7 +1157,7 @@ { "hp", "scanjet 7450c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7450c", - AV_FIRMWARE}, + AV_FIRMWARE | AV_NO_MATRIX}, /* comment="1 pass, 2400 dpi - dual USB/SCSI interface - regularly tested" */ /* status="good" */ @@ -1165,7 +1172,7 @@ { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8200", - AV_FIRMWARE}, + AV_FIRMWARE | AV_NO_MATRIX}, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ @@ -1314,34 +1321,40 @@ 0}, /* status="untested" */ - { "Kodak", "i30", - 0, 0, - "Kodak", "i30", + { "FCPA", "fi-4010CU", + 0x04c5, 0x1029, + "Fujitsu", "fi-4010CU", 0}, /* status="untested" */ - { "Kodak", "i40", - 0, 0, - "Kodak", "i40", - 0}, + { "KODAK", "i30 Scanner", + 0x040a, 0x6001, + "Kodak", "i30", + AV_INT_BUTTON}, /* status="untested" */ + + { "KODAK", "i40 Scanner", + 0x040a, 0x6002, + "Kodak", "i40", + AV_LIGHT_CHECK_BOGUS | AV_NO_GAMMA | AV_NO_MATRIX | AV_INT_BUTTON}, + /* status="basic" */ - { "Kodak", "i50", - 0, 0, + { "KODAK", "i50 Scanner", + 0x040a, 0x6003, "Kodak", "i50", - 0}, + AV_INT_BUTTON}, /* status="untested" */ - { "Kodak", "i60", - 0, 0, + { "KODAK", "i60 Scanner", + 0x040a, 0x6004, "Kodak", "i60", - 0}, + AV_INT_BUTTON}, /* status="untested" */ - { "Kodak", "i80", - 0, 0, + { "KODAK", "i80 Scanner", + 0x040a, 0x6005, "Kodak", "i80", - 0}, + AV_INT_BUTTON}, /* status="untested" */ { "iVina", "1200U", @@ -1367,6 +1380,13 @@ "Xerox", "DocuMate272", AV_INT_BUTTON}, /* status="untested" */ + + { "B+H", "2000F", + 0, 0, + "Bell+Howell", "2000F", + 0}, + /* comment="1 pass, ??? dpi, A4" */ + /* status="basic" */ /* More IDs from the Avision dll: ArtiScan ProA3 @@ -1384,59 +1404,13 @@ 0} }; -/* This is just for reference and future review - this is NOT used */ - -/* static const char c7_flash_ram [] = { - 0x00, 0xff, 0xff, 0x08, 0x53, 0x63, 0x61, 0x6e, - 0x6e, 0x65, 0x6e, 0x14}; */ - -/* a set of NV-RAM data obtained from the Avision driver */ - -/* - Scannen - SCN2CSS0279LZ - Initialisierung - Bereit - Bitte entriegeln - Fehler - SCSI-Adresse: - Energiesparmodus - Scannen - ADF-Papierstau */ - -/* static const char c7_nvram [] = { - 0xff, 0xff, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x1c, 0x07, 0xd2, 0x00, 0x03, 0x00, 0x05, - 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x7f, - 0x53, 0x43, 0x4e, 0x32, 0x43, 0x53, 0x53, 0x30, - 0x32, 0x37, 0x39, 0x4c, 0x5a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x69, 0x73, 0x69, 0x65, 0x72, 0x75, 0x6e, - 0x67, 0x14, 0x00, 0x20, 0x20, 0x20, 0x42, 0x65, - 0x72, 0x65, 0x69, 0x74, 0x00, 0x42, 0x69, 0x74, - 0x74, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x69, - 0x65, 0x67, 0x65, 0x6c, 0x6e, 0x00, 0x46, 0x65, - 0x68, 0x6c, 0x65, 0x72, 0x00, 0x53, 0x43, 0x53, - 0x49, 0x2d, 0x41, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x3a, 0x00, 0x45, 0x6e, 0x65, 0x72, 0x67, - 0x69, 0x65, 0x73, 0x70, 0x61, 0x72, 0x6d, 0x6f, - 0x64, 0x75, 0x73, 0x00, 0x53, 0x63, 0x61, 0x6e, - 0x6e, 0x65, 0x6e, 0x14, 0x00, 0x41, 0x44, 0x46, - 0x2d, 0x50, 0x61, 0x70, 0x69, 0x65, 0x72, 0x73, - 0x74, 0x61, 0x75, 0x00, 0x00, 0x00, 0x5c, 0x5c, - 0x2e, 0x5c, 0x55, 0x73, 0x62, 0x73, 0x63, 0x61, - 0x6e, 0x30, 0x00, 0xf5, 0x64, 0x00, 0x28, 0xb8, - 0xf7, 0xbf, 0x34, 0x32, 0x8a, 0x81, 0xf7, 0x41, - 0xf7, 0xbf, 0x90, 0x94, 0xfc, 0xbf, 0x4e, 0xb8, - 0xf7, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0xb0, 0xf6, 0x57, 0xc1, 0x09, 0x00, - 0x00, 0x00, 0x68, 0x2c, 0xf9, 0xbf, 0xab, 0xf5, - 0x64, 0x00, 0x29, 0x37, 0x04, 0x02, 0x5c, 0x45, - 0x03, 0x02, 0x5c, 0x00, 0x00, 0x00, 0xf9, 0x83, - 0x03, 0x02, 0x2c, 0x37, 0x04, 0x02, 0x9f, 0x7d}; */ +#if 0 + struct timeval tv; + #define TIMING(txt) gettimeofday (&tv, NULL); \ + DBG (4, "%lu: " txt "\n", tv.tv_sec * 1000000 + tv.tv_usec) +#else + #define TIMING(txt) +#endif /* used when scanner returns invalid range fields ... */ #define A4_X_RANGE 8.5 /* or 8.25 ? */ @@ -1449,7 +1423,12 @@ #define AVISION_CONFIG_FILE "avision.conf" +#define STD_INQUIRY_SIZE 0x24 +#define AVISION_INQUIRY_SIZE 0x60 + #define MM_PER_INCH 25.4 + +#define AVISION_READER_STRIPE_SIZE 32 #define AVISION_BASE_RES 300 /* calibration (shading) defines */ @@ -1486,6 +1465,7 @@ /* disable the calibration */ static SANE_Bool disable_calibration = SANE_FALSE; +static SANE_Bool force_calibration = SANE_FALSE; /* force scanable areas to ISO(DIN) A4/A3 */ static SANE_Bool force_a4 = SANE_FALSE; @@ -1588,6 +1568,63 @@ } } +static void debug_print_nvram_data (int dbg_level, char* func, + nvram_data* nvram) +{ + DBG (dbg_level, "%s: pad scans: %d\n", + func, get_quad(nvram->pad_scans)); + DBG (dbg_level, "%s: ADF simplex scans: %d\n", + func, get_quad(nvram->adf_simplex_scans)); + DBG (dbg_level, "%s: ADF duplex scans: %d\n", + func, get_quad(nvram->adf_duplex_scans)); + DBG (dbg_level, "%s: flatbed scans: %d\n", + func, get_quad(nvram->flatbed_scans)); + + DBG (dbg_level, "%s: flatbed leading edge: %d\n", + func, get_double(nvram->flatbed_leading_edge)); + DBG (dbg_level, "%s: flatbed side edge: %d\n", + func, get_double(nvram->flatbed_side_edge)); + DBG (dbg_level, "%s: ADF leading edge: %d\n", + func, get_double(nvram->adf_leading_edge)); + DBG (dbg_level, "%s: ADF side edge: %d\n", + func, get_double(nvram->adf_side_edge)); + DBG (dbg_level, "%s: ADF rear leading edge: %d\n", + func, get_double(nvram->adf_rear_leading_edge)); + DBG (dbg_level, "%s: ADF rear side edge: %d\n", + func, get_double(nvram->adf_rear_side_edge)); + + DBG (dbg_level, "%s: born month: %d\n", + func, get_double(nvram->born_month)); + DBG (dbg_level, "%s: born day: %d\n", + func, get_double(nvram->born_day)); + DBG (dbg_level, "%s: born year: %d\n", + func, get_double(nvram->born_year)); + + DBG (dbg_level, "%s: first scan month: %d\n", + func, get_double(nvram->first_scan_month)); + DBG (dbg_level, "%s: first scan day: %d\n", + func, get_double(nvram->first_scan_day)); + DBG (dbg_level, "%s: first scan year: %d\n", + func, get_double(nvram->first_scan_year)); + + + DBG (dbg_level, "%s: vert. magnification: %d\n", + func, get_double(nvram->vertical_magnification)); + DBG (dbg_level, "%s: horiz. magnification: %d\n", + func, get_double(nvram->horizontal_magnification)); + + DBG (dbg_level, "%s: CCD type: %d\n", + func, nvram->ccd_type); + DBG (dbg_level, "%s: scan speed: %d\n", + func, nvram->scan_speed); + + DBG (dbg_level, "%s: serial: %.24s\n", /* 24 chars max */ + func, nvram->serial); + + DBG (dbg_level, "%s: power saving time: %d\n", + func, get_double(nvram->power_saving_time)); +} + static void debug_print_avdimen (int dbg_level, char* func, Avision_Dimensions* avdimen) { @@ -2096,8 +2133,9 @@ SANE_Status status = SANE_STATUS_GOOD; size_t i, count, out_count; -#define STD_TIMEOUT 30000 /* some commands on some devices need a rather long time to respond */ -#define STD_STATUS_TIMEOUT 2500 + /* some commands on some devices need a rather long time to respond */ +#define STD_TIMEOUT 30000 +#define STD_STATUS_TIMEOUT 10000 int retry = 4; int write_timeout = STD_TIMEOUT; int read_timeout = STD_TIMEOUT; @@ -2126,10 +2164,14 @@ switch (m_cmd[0]) { case AVISION_SCSI_INQUIRY: case AVISION_SCSI_TEST_UNIT_READY: - read_timeout = 1500; + read_timeout = 1500; /* quick timeout on initial detection */ + status_timeout = 1500; break; } + DBG (7, "Timeouts: write: %d, read: %d, status: %d\n", + write_timeout, read_timeout, status_timeout); + write_usb_cmd: if (--retry == 0) { DBG (1, "Max retry count reached: I/O error\n"); @@ -2138,8 +2180,8 @@ count = cmd_size; - DBG (8, "try to write cmd, count: %lu.\n", (u_long) count); sanei_usb_set_timeout (write_timeout); + DBG (8, "try to write cmd, count: %lu.\n", (u_long) count); status = sanei_usb_write_bulk (av_con->usb_dn, m_cmd, &count); DBG (8, "wrote %lu bytes\n", (u_long) count); @@ -2579,8 +2621,8 @@ s->params.lines = s->avdimen.bry - s->avdimen.tly - 2 * s->avdimen.line_difference; if (s->avdimen.interlaced_duplex) - s->params.lines -= s->params.lines % 8; - + s->params.lines -= s->params.lines % AVISION_READER_STRIPE_SIZE; + debug_print_avdimen (1, "sane_compute_parameters", &s->avdimen); switch (s->c_mode) @@ -2629,6 +2671,38 @@ } static SANE_Status +inquiry (Avision_Connection av_con, u_int8_t* data, size_t len) +{ + SANE_Status status; + size_t size; + command_header inquiry; + + DBG (3, "inquiry: %ld\n", (long)len); + + memset (&inquiry, 0, sizeof(inquiry)); + inquiry.opc = AVISION_SCSI_INQUIRY; + inquiry.len = len; + + DBG (3, "attach: sending INQUIRY\n"); + size = inquiry.len; + + { + int try = 2; + + do { + status = avision_cmd (&av_con, &inquiry, sizeof (inquiry), 0, 0, + data, &size); + if (status != SANE_STATUS_GOOD || size != inquiry.len) { + DBG (1, "inquiry: inquiry failed (%s)\n", sane_strstatus (status)); + --try; + } + }while (status != SANE_STATUS_GOOD && try > 0); + } + + return status; +} + +static SANE_Status wait_ready (Avision_Connection* av_con) { SANE_Status status; @@ -2739,6 +2813,34 @@ } static SANE_Status +set_power_save_time (Avision_Scanner* s, int time) +{ + struct { + struct command_send cmd; + u_int8_t time[2]; + } scmd; + + Avision_Device* dev = s->hw; + SANE_Status status; + + DBG (3, "set_power_save_time: time %d\n", time); + + memset (&scmd, 0, sizeof (scmd)); + scmd.cmd.opc = AVISION_SCSI_SEND; + scmd.cmd.datatypecode = 0xA2; /* power-saving timer */ + set_double (scmd.cmd.datatypequal, dev->data_dq); + set_triple (scmd.cmd.transferlen, sizeof (scmd.time) ); + + set_double (scmd.time, time); + + status = avision_cmd (&s->av_con, &scmd.cmd, sizeof (scmd.cmd), + &scmd.time, sizeof (scmd.time), 0, 0); + if (status != SANE_STATUS_GOOD) + DBG (1, "set_power_save_time: send_data (%s)\n", sane_strstatus (status)); + return status; +} + +static SANE_Status get_firmware_status (Avision_Connection* av_con) { /* read stuff */ @@ -2841,6 +2943,121 @@ return SANE_STATUS_GOOD; } +static SANE_Status +get_nvram_data (Avision_Scanner* s, nvram_data* nvram) +{ + /* read stuff */ + struct command_send rcmd; + + size_t size; + SANE_Status status; + + DBG (3, "get_nvram_data\n"); + + size = sizeof (*nvram); + + memset (&rcmd, 0, sizeof (rcmd)); + memset (nvram, 0, size); + + rcmd.opc = AVISION_SCSI_READ; + + rcmd.datatypecode = 0x69; /* Read NVM RAM data */ + set_double (rcmd.datatypequal, 0); /* dev->data_dq not available */ + set_triple (rcmd.transferlen, size); + + status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, + nvram, &size); + if (status != SANE_STATUS_GOOD) { + DBG (1, "read_nvram_data: read failed (%s)\n", + sane_strstatus (status)); + return (status); + } + + debug_print_nvram_data (5, "get_nvram_data", nvram); + + return SANE_STATUS_GOOD; +} + +static SANE_Status +get_and_parse_nvram (Avision_Scanner* s, char* str, int n) +{ + SANE_Status status; + int i = 0; + int x; + nvram_data nvram; + u_int8_t inquiry_result [AVISION_INQUIRY_SIZE]; + + status = inquiry (s->av_con, inquiry_result, sizeof(inquiry_result)); + if (status == SANE_STATUS_GOOD) { + i += snprintf (str+i, n-i, "Vendor: %.8s", + inquiry_result+8); + i += snprintf (str+i, n-i, "\nModel: %.16s", + inquiry_result+16); + i += snprintf (str+i, n-i, "\nFirmware: %.4s", + inquiry_result+32); + } + + if (!s->hw->inquiry_nvram_read) + return SANE_STATUS_GOOD; + + status = get_nvram_data (s, &nvram); + if (status == SANE_STATUS_GOOD) + { + if (nvram.serial[0]) + i += snprintf (str+i, n-i, "\nSerial: %.24s", + nvram.serial); + + if (nvram.born_year) + i += snprintf (str+i, n-i, "\nManufacturing date: %d-%d-%d", + get_double(nvram.born_year), + get_double(nvram.born_month), + get_double(nvram.born_day)); + if (nvram.first_scan_year) + i += snprintf (str+i, n-i, "\nFirst scan date: %d-%d-%d", + get_double(nvram.first_scan_year), + get_double(nvram.first_scan_month), + get_double(nvram.first_scan_day)); + + x = get_quad (nvram.flatbed_scans); + if (x) + i += snprintf (str+i, n-i, "\nFlatbed scans: %d", x); + x = get_quad (nvram.pad_scans); + if (x) + i += snprintf (str+i, n-i, "\nPad scans: %d", x); + x = get_quad (nvram.adf_simplex_scans); + if (x) + i += snprintf (str+i, n-i, "\nADF simplex scans: %d", x); + x = get_quad (nvram.adf_duplex_scans); + if (x) + i += snprintf (str+i, n-i, "\nADF duplex scans: %d", x); + } + + return status; +} + +static SANE_Status +get_power_save_time (Avision_Scanner* s, SANE_Word* time) +{ + SANE_Status status; + nvram_data nvram; + + DBG (3, "get_power_save_time\n"); + + if (!s->hw->inquiry_nvram_read) + return SANE_STATUS_INVAL; + + status = get_nvram_data (s, &nvram); + + if (status != SANE_STATUS_GOOD) { + DBG (1, "get_power_save_time: read nvram failed (%s)\n", sane_strstatus (status)); + return status; + } + + *time = get_double (nvram.power_saving_time); + + return SANE_STATUS_GOOD; +} + #ifdef NEEDED static SANE_Status @@ -3272,17 +3489,11 @@ attach (SANE_String_Const devname, Avision_ConnectionType con_type, Avision_Device** devp) { -#define STD_INQUIRY_SIZE 0x24 -#define AVISION_INQUIRY_SIZE 0x60 - - command_header inquiry; - u_int8_t result [AVISION_INQUIRY_SIZE]; int model_num; Avision_Device* dev; SANE_Status status; - size_t size; Avision_Connection av_con; @@ -3315,36 +3526,12 @@ } /* first: get the standard inquiry? */ - memset (&inquiry, 0, sizeof (inquiry) ); -#ifdef MAYBE_OPTIONAL - inquiry.opc = AVISION_SCSI_INQUIRY; - inquiry.len = STD_INQUIRY_SIZE; -#else - inquiry.opc = AVISION_SCSI_INQUIRY; - inquiry.len = AVISION_INQUIRY_SIZE; -#endif - DBG (3, "attach: sending standard INQUIRY\n"); - size = inquiry.len; - if (av_con.connection_type == AV_USB) { - sanei_usb_set_timeout (1500); + status = inquiry (av_con, result, AVISION_INQUIRY_SIZE); + if (status != SANE_STATUS_GOOD) { + DBG (1, "attach: 1st inquiry failed (%s)\n", sane_strstatus (status)); + return status; } - { - int try = 2; - - do { - status = avision_cmd (&av_con, &inquiry, sizeof (inquiry), 0, 0, - result, &size); - if (status != SANE_STATUS_GOOD || size != inquiry.len) { - DBG (1, "attach: standard inquiry failed (%s)\n", - sane_strstatus (status)); - --try; - } - }while (status != SANE_STATUS_GOOD && try > 0); - if (status != SANE_STATUS_GOOD) - goto close_scanner_and_return; - } - /* copy string information - and zero terminate them c-style */ memcpy (&mfg, result + 8, 8); mfg [8] = 0; @@ -3384,9 +3571,9 @@ } if (!found) { - DBG (1, "attach: model is not in the list of supported models!\n"); - DBG (1, "attach: You might want to report this output. To:\n"); - DBG (1, "attach: rene@exactcode.de (the Avision backend author)\n"); + DBG (0, "attach: model is not in the list of supported models!\n"); + DBG (0, "attach: You might want to report this output.\n"); + DBG (0, "attach: To: rene@exactcode.de (the Avision backend author)\n"); status = SANE_STATUS_INVAL; goto close_scanner_and_return; @@ -3420,14 +3607,9 @@ } /* third: get the extended Avision inquiry */ - inquiry.len = AVISION_INQUIRY_SIZE; - - DBG (3, "attach: sending Avision INQUIRY\n"); - size = inquiry.len; - status = avision_cmd (&av_con, &inquiry, sizeof (inquiry), 0, 0, - result, &size); - if (status != SANE_STATUS_GOOD || size != inquiry.len) { - DBG (1, "attach: Avision inquiry failed (%s)\n", sane_strstatus (status)); + status = inquiry (av_con, result, AVISION_INQUIRY_SIZE); + if (status != SANE_STATUS_GOOD) { + DBG (1, "attach: 2nd inquiry failed (%s)\n", sane_strstatus (status)); goto close_scanner_and_return; } @@ -3625,24 +3807,31 @@ dev->inquiry_new_protocol = BIT (result[39],2); dev->inquiry_asic_type = (int) result[91]; + dev->inquiry_nvram_read = BIT(result[52],0); + dev->inquiry_power_save_time = BIT(result[52],1); + dev->inquiry_adf = BIT (result[62], 5); dev->inquiry_duplex = BIT (result[62], 2) || BIT (result[94], 5); dev->inquiry_duplex_interlaced = BIT (result[94], 4); dev->inquiry_paper_length = BIT (result[95], 5); dev->inquiry_detect_accessories = BIT (result[93], 7); - + dev->inquiry_needs_calibration = BIT (result[50], 4); dev->inquiry_needs_gamma = BIT (result[50], 3); dev->inquiry_keeps_gamma = BIT (result[50], 2); dev->inquiry_keeps_window = BIT (result[50], 1); + if (Avision_Device_List [model_num].feature_type & AV_DOES_NOT_KEEP_WINDOW) + dev->inquiry_keeps_window = 0; dev->inquiry_3x3_matrix = BIT (result[51], 1); dev->inquiry_needs_software_colorpack = BIT (result[50],5); dev->inquiry_needs_line_pack = BIT (result[94], 6); dev->inquiry_adf_need_mirror = BIT (result[51], 0); dev->inquiry_adf_bgr_order = BIT (result[93], 6); + if (Avision_Device_List [model_num].feature_type & AV_ADF_BGR_ORDER_INVERT) + dev->inquiry_adf_bgr_order = ! dev->inquiry_adf_bgr_order; dev->inquiry_light_detect = BIT (result[93], 2); dev->inquiry_light_control = BIT (result[50], 7); @@ -4451,7 +4640,7 @@ int elements_per_line = calib_format.pixel_per_line * calib_format.channels; int i; - DBG (1, "normal_calibration: dark data present - decreasing white aerage data\n"); + DBG (1, "normal_calibration: dark data present - decreasing white average data\n"); for (i = 0; i < elements_per_line; ++ i) { s->white_avg_data[i] -= s->dark_avg_data[i]; @@ -4803,7 +4992,7 @@ if (accel_info.target_speed > accel_info.start_speed || accel_info.target_speed == 0 || accel_info.total_steps <= accel_info.stable_steps) { - DBG (1, "send_acceleration_table: tables does not look right.\n"); + DBG (1, "send_acceleration_table: table does not look right.\n"); return SANE_STATUS_INVAL; } @@ -5072,7 +5261,7 @@ double feeds - they may not work without the field set */ if (dev->inquiry_paper_length) set_double (cmd.window.descriptor.paper_length, (int)((double)30.0*1200)); - + if ( !(dev->hw->feature_type & AV_FUJITSU) ) { /* quality scan option switch */ @@ -5080,8 +5269,8 @@ SET_BIT (cmd.window.avision.type.normal.bitset2, 4); } - /* quality calibration option switch */ - if (s->val[OPT_QCALIB].w == SANE_TRUE) { + /* quality calibration option switch (inverted! if set == speed) */ + if (s->val[OPT_QCALIB].w == SANE_FALSE) { SET_BIT (cmd.window.avision.type.normal.bitset2, 3); } @@ -5109,62 +5298,64 @@ cmd.window.descriptor.contrast = 128; cmd.window.avision.highlight = 0xFF; cmd.window.avision.shadow = 0x00; - + /* mode dependant settings */ switch (s->c_mode) { case AV_THRESHOLDED: cmd.window.descriptor.bpc = 1; cmd.window.descriptor.image_comp = 0; - /* cmd.window.avision.bitset1 &= 0xC7;*/ /* no filter */ break; case AV_DITHERED: cmd.window.descriptor.bpc = 1; cmd.window.descriptor.image_comp = 1; - /* cmd.window.avision.bitset1 &= 0xC7;*/ /* no filter */ break; case AV_GRAYSCALE: cmd.window.descriptor.bpc = 8; cmd.window.descriptor.image_comp = 2; - /* cmd.window.avision.bitset1 &= 0xC7;*/ /* no filter */ break; case AV_GRAYSCALE12: cmd.window.descriptor.bpc = 12; cmd.window.descriptor.image_comp = 2; - /* cmd.window.avision.bitset1 &= 0xC7;*/ /* no filter */ break; case AV_GRAYSCALE16: cmd.window.descriptor.bpc = 16; cmd.window.descriptor.image_comp = 2; - /* cmd.window.avision.bitset1 &= 0xC7;*/ /* no filter */ break; case AV_TRUECOLOR: cmd.window.descriptor.bpc = 8; cmd.window.descriptor.image_comp = 5; - cmd.window.avision.bitset1 |= 0x20; /* rgb one-pass filter */ break; case AV_TRUECOLOR12: cmd.window.descriptor.bpc = 12; cmd.window.descriptor.image_comp = 5; - cmd.window.avision.bitset1 |= 0x20; /* rgb one-pass filter */ break; case AV_TRUECOLOR16: cmd.window.descriptor.bpc = 16; cmd.window.descriptor.image_comp = 5; - cmd.window.avision.bitset1 |= 0x20; /* rgb one-pass filter */ break; default: DBG (1, "Invalid mode. %d\n", s->c_mode); return SANE_STATUS_INVAL; } + + if (color_mode_is_color (s->c_mode)) { + cmd.window.avision.bitset1 |= AVISION_FILTER_RGB << 3; + } + else { + if (dev->hw->feature_type & AV_FASTER_WITH_FILTER) + cmd.window.avision.bitset1 |= AVISION_FILTER_GREEN << 3; + else + cmd.window.avision.bitset1 |= AVISION_FILTER_NONE << 3; + } debug_print_window_descriptor (5, "set_window", &(cmd.window)); @@ -5278,7 +5469,8 @@ DBG (3, "do_eof:\n"); - s->param_cksum = create_cksum (s); + s->prepared = s->scanning = SANE_FALSE; + /* we can now mark the rear data as valid */ if (s->avdimen.interlaced_duplex) { DBG (3, "do_eof: toggling duplex rear data valid\n"); @@ -5287,19 +5479,17 @@ s->duplex_rear_valid); } - if (s->pipe >= 0) { - close (s->pipe); - s->pipe = -1; + if (s->read_fds >= 0) { + close (s->read_fds); + s->read_fds = -1; } /* join our processes - without a wait() you will produce defunct childs */ sanei_thread_waitpid (s->reader_pid, &exit_status); - s->reader_pid = 0; DBG (3, "do_eof: returning %d\n", exit_status); - return (SANE_Status)exit_status; } @@ -5308,9 +5498,7 @@ { DBG (3, "do_cancel:\n"); - /* TODO: reuse do_eof? */ - - s->scanning = SANE_FALSE; + s->prepared = s->scanning = SANE_FALSE; s->duplex_rear_valid = SANE_FALSE; if (s->reader_pid > 0) { @@ -5375,8 +5563,8 @@ case AV_ASIC_C5: dev->dpi_range.min = 80; break; - case AV_ASIC_C6: /* TODO: AV610 in ADF mode does not scan less */ - dev->dpi_range.min = 175; + case AV_ASIC_C6: /* TODO: AV610 in ADF mode does not scan less than 180 or so */ + dev->dpi_range.min = 50; break; default: dev->dpi_range.min = 50; @@ -5657,6 +5845,31 @@ s->opt[OPT_FRAME].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_FRAME].constraint.range = &dev->frame_range; s->val[OPT_FRAME].w = dev->current_frame; + + /* power save time */ + if (!dev->inquiry_power_save_time) + s->opt[OPT_POWER_SAVE_TIME].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_POWER_SAVE_TIME].name = "power-save-time"; + s->opt[OPT_POWER_SAVE_TIME].title = "Power save timer control"; + s->opt[OPT_POWER_SAVE_TIME].desc = "Allows control of the scanner's power save timer, dimming or turning off the light."; + s->opt[OPT_POWER_SAVE_TIME].type = SANE_TYPE_INT; + s->opt[OPT_POWER_SAVE_TIME].unit = SANE_UNIT_NONE; + s->opt[OPT_POWER_SAVE_TIME].constraint_type = SANE_CONSTRAINT_NONE; + s->val[OPT_POWER_SAVE_TIME].w = 0; + + /* NVRAM */ + if (!dev->inquiry_nvram_read) + s->opt[OPT_NVRAM].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_NVRAM].cap |= SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + s->opt[OPT_NVRAM].name = "nvram-values"; + s->opt[OPT_NVRAM].title = "Obtain NVRAM values"; + s->opt[OPT_NVRAM].desc = "Allows access obtaining the scanner's NVRAM values as pretty printed text."; + s->opt[OPT_NVRAM].type = SANE_TYPE_STRING; + s->opt[OPT_NVRAM].unit = SANE_UNIT_NONE; + s->opt[OPT_NVRAM].size = 1024; + s->opt[OPT_NVRAM].constraint_type = SANE_CONSTRAINT_NONE; + s->val[OPT_NVRAM].s = malloc(s->opt[OPT_NVRAM].size); + s->val[OPT_NVRAM].s[0] = 0; return SANE_STATUS_GOOD; } @@ -5676,12 +5889,12 @@ reader_process (void *data) { struct Avision_Scanner *s = (struct Avision_Scanner *) data; - int fd = s->reader_fds; + int fd = s->write_fds; Avision_Device* dev = s->hw; SANE_Status status; - SANE_Status exit_status; + SANE_Status exit_status = SANE_STATUS_GOOD; sigset_t sigterm_set; sigset_t ignore_set; struct SIGACTION act; @@ -5690,25 +5903,28 @@ FILE* rear_fp = 0; /* used to store the deinterlaced rear data */ /* the complex params */ - size_t bytes_per_line; - size_t pixels_per_line; - size_t lines_per_stripe; - size_t lines_per_output; - size_t max_bytes_per_read; + unsigned int bytes_per_line; + unsigned int pixels_per_line; + unsigned int lines_per_stripe; + unsigned int lines_per_output; + unsigned int max_bytes_per_read; SANE_Bool gray_mode; /* the simple params for the data reader */ - size_t stripe_size; - size_t stripe_fill; - size_t out_size; + unsigned int stripe_size; + unsigned int stripe_fill; + unsigned int out_size; size_t total_size; size_t processed_bytes; SANE_Bool deinterlace; - SANE_Bool really_deinterlace; - size_t stripe = 0; + enum { + STRIPE, /* every 2nd stripe */ + HALF, /* the 2nd half */ + LINE /* every 2nd line */ + } deinterlace_type; /* the fat strip we currently puzzle together to perform software-colorpack and more */ @@ -5719,7 +5935,7 @@ DBG (3, "reader_process:\n"); if (sanei_thread_is_forked()) - close (s->pipe); + close (s->read_fds); sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); @@ -5735,13 +5951,14 @@ sigaddset (&sigterm_set, SIGTERM); gray_mode = color_mode_is_shaded (s->c_mode); - really_deinterlace = deinterlace = s->avdimen.interlaced_duplex; - + + deinterlace = s->avdimen.interlaced_duplex; + deinterlace_type = STRIPE; if ( (dev->hw->feature_type & AV_NON_INTERLACED_DUPLEX_300) && - (s->avdimen.xres < 300 && s->avdimen.yres < 300) ) - really_deinterlace = SANE_FALSE; - if (deinterlace && !really_deinterlace) - DBG (1, "reader_process: deinterlacing disabled due to device list and res.\n"); + (s->avdimen.xres <= 300 && s->avdimen.yres <= 300) ) + deinterlace_type = HALF; + if (dev->hw->feature_type & AV_2ND_LINE_INTERLACED) + deinterlace_type = LINE; fp = fdopen (fd, "w"); if (!fp) @@ -5781,8 +5998,8 @@ /* setup file i/o for deinterlacing scans */ if (deinterlace) { - DBG (3, "reader_process: opening duplex rear file for writing.\n"); if (!s->duplex_rear_valid) { /* create new file for writing */ + DBG (3, "reader_process: opening duplex rear file for writing.\n"); rear_fp = fopen (s->duplex_rear_fname, "w"); if (! rear_fp) { fclose (fp); @@ -5818,55 +6035,39 @@ ; } - /* lines_per_stripe is either the line-difference, some ADF - deinterlace constant or in the remaining cases just 16 for a - pleasuant preview */ - - if (!deinterlace) { - lines_per_stripe = s->avdimen.line_difference * 3 * 2; - } - else { - /* In theory the SPEC has a command to request the value from the scanner. - However in practice the AV220 does not respond to the command ... */ - lines_per_stripe = 8; - } - - if (lines_per_stripe == 0) { - lines_per_stripe = 16; - } + /* always quite some lines to saturate the (USB) bus */ + lines_per_stripe = AVISION_READER_STRIPE_SIZE; stripe_size = bytes_per_line * lines_per_stripe; lines_per_output = lines_per_stripe - s->avdimen.line_difference; - /* The "/2" might make scans faster - because it should leave some - space in the SCSI buffers (scanner, kernel, ...) empty to - read/send _ahead_ ... */ if (s->av_con.connection_type == AV_SCSI) + /* maybe better not /2 ... */ max_bytes_per_read = dev->scsi_buffer_size / 2; - else { - max_bytes_per_read = (0x10000 / bytes_per_line) * bytes_per_line; - } + else + /* 1MB, vast buffer size to saturate the bus */ + max_bytes_per_read = 0x100000; out_size = bytes_per_line * lines_per_output; DBG (3, "dev->scsi_buffer_size / 2: %d\n", dev->scsi_buffer_size / 2); - DBG (3, "bytes_per_line: %lu, pixels_per_line: %lu\n", - (u_long) bytes_per_line, (u_long) pixels_per_line); + DBG (3, "bytes_per_line: %d, pixels_per_line: %d\n", + bytes_per_line, pixels_per_line); - DBG (3, "lines_per_stripe: %lu, lines_per_output: %lu\n", - (u_long) lines_per_stripe, (u_long) lines_per_output); + DBG (3, "lines_per_stripe: %d, lines_per_output: %d\n", + lines_per_stripe, lines_per_output); - DBG (3, "max_bytes_per_read: %lu, stripe_size: %lu, out_size: %lu\n", - (u_long) max_bytes_per_read, (u_long) stripe_size, (u_long) out_size); + DBG (3, "max_bytes_per_read: %d, stripe_size: %d, out_size: %d\n", + max_bytes_per_read, stripe_size, out_size); stripe_data = malloc (stripe_size); out_data = malloc (out_size); s->line = 0; - /* calculate params for the simple reader */ + /* calculate params for the reading loop */ total_size = bytes_per_line * (s->params.lines + 2 * s->avdimen.line_difference); if (deinterlace && !s->duplex_rear_valid) @@ -5878,13 +6079,14 @@ /* Data read loop until all data has been processed. Might exit before all lines are transferred for ADF paper end. */ - while (processed_bytes < total_size) + while (exit_status == SANE_STATUS_GOOD && processed_bytes < total_size) { - int useful_bytes; + unsigned int useful_bytes; - /* fill the stripe buffer with real data*/ + /* fill the stripe buffer with real data */ while (!s->duplex_rear_valid && - processed_bytes < total_size && stripe_fill < stripe_size) + processed_bytes < total_size && stripe_fill < stripe_size && + exit_status == SANE_STATUS_GOOD) { size_t this_read = stripe_size - stripe_fill; @@ -5901,9 +6103,7 @@ DBG (5, "reader_process: processed_bytes: %lu, total_size: %lu\n", (u_long) processed_bytes, (u_long) total_size); - - DBG (5, "reader_process: this_read: %lu\n", - (u_long) this_read); + DBG (5, "reader_process: this_read: %lu\n", (u_long) this_read); sigprocmask (SIG_BLOCK, &sigterm_set, 0); status = read_data (s, stripe_data + stripe_fill, &this_read); @@ -5912,14 +6112,12 @@ if (status == SANE_STATUS_EOF || this_read == 0) { DBG (1, "reader_process: read_data failed due to EOF\n"); exit_status = SANE_STATUS_EOF; - goto cleanup; } if (status != SANE_STATUS_GOOD) { DBG (1, "reader_process: read_data failed with status: %d\n", status); exit_status = status; - goto cleanup; } stripe_fill += this_read; @@ -5941,47 +6139,57 @@ DBG (5, "reader_process: virtual processed_bytes: %lu, total_size: %lu\n", (u_long) processed_bytes, (u_long) total_size); - - DBG (5, "reader_process: virtual this_read: %lu\n", - (u_long) this_read); + DBG (5, "reader_process: virtual this_read: %lu\n", (u_long) this_read); got = fread (stripe_data + stripe_fill, 1, this_read, rear_fp); if (got != this_read) { exit_status = SANE_STATUS_EOF; - goto cleanup; } stripe_fill += this_read; processed_bytes += this_read; } - DBG (5, "reader_process: stripe filled\n"); + DBG (5, "reader_process: stripe filled: %d\n", stripe_fill); useful_bytes = stripe_fill; - if (s->c_mode == AV_TRUECOLOR || s->c_mode == AV_TRUECOLOR16) + if (color_mode_is_color (s->c_mode)) useful_bytes -= 2 * s->avdimen.line_difference * bytes_per_line; DBG (3, "reader_process: useful_bytes %i\n", useful_bytes); - /* Save the rear stripes to deinterlace. For some scanners + /* Deinterlace, save the rear stripes. For some scanners (AV220) that is every 2nd stripe - for others (AV83xx) this is the 2nd half of the transferred data. */ if (deinterlace && !s->duplex_rear_valid) { - /* for some scanners up to 300 dpi are not deinterlaced */ - if ( (really_deinterlace && stripe % 2) || - (!really_deinterlace && processed_bytes > total_size/2) ) + /* for all lines we have in the buffer: */ + unsigned int absline = (processed_bytes - stripe_fill) / bytes_per_line; + unsigned int abslines = absline + useful_bytes / bytes_per_line; + u_int8_t* ptr = stripe_data; + for ( ; absline < abslines; ++absline) { - DBG (5, "reader_process: rear stripe -> saving to temporary file.\n"); - fwrite (stripe_data, bytes_per_line, - useful_bytes / bytes_per_line, rear_fp); - - stripe_fill -= useful_bytes; - /* TODO: copy remaining data, if any, to the front for line - diff compensation */ - goto iteration_end; - } + DBG (9, "reader_process: deinterlacing line %d\n", absline); + /* interlaced? save the back data to the rear buffer */ + if ( (deinterlace_type == STRIPE && absline % (lines_per_stripe*2) >= lines_per_stripe) || + (deinterlace_type == HALF && absline >= total_size / bytes_per_line / 2) || + (deinterlace_type == LINE && absline & 0x1) ) /* last bit equals % 2 */ + { + DBG (9, "reader_process: saving rear line %d to temporary file.\n", absline); + fwrite (ptr, bytes_per_line, 1, rear_fp); + if (deinterlace_type == LINE) + memmove (ptr, ptr+bytes_per_line, stripe_data + stripe_fill - ptr - bytes_per_line); + else + ptr += bytes_per_line; + useful_bytes -= bytes_per_line; + stripe_fill -= bytes_per_line; + } + else + ptr += bytes_per_line; + } + DBG (9, "reader_process: after deinterlacing: useful_bytes: %d, stripe_fill: %d\n", + useful_bytes, stripe_fill); } /* @@ -5996,12 +6204,12 @@ } else /* AV_TRUECOLOR* */ { - /* WARNING: DO NOT MODIFY MY (HOPEFULLY WELL) OPTMIZED + /* WARNING: DO NOT MODIFY MY (HOPEFULLY WELL) OPTIMIZED ALGORITHMS BELOW, WITHOUT UNDERSTANDING THEM FULLY ! */ if (s->avdimen.line_difference > 0) /* color-pack */ { /* TODO: add 16bit per sample code? */ - int i; + unsigned int i; int c_offset = s->avdimen.line_difference * bytes_per_line; u_int8_t* r_ptr = stripe_data; @@ -6050,11 +6258,11 @@ { /* Mirroring with bgr -> rgb convertion: Just mirror the * whole line */ - + int l; int lines = useful_bytes / bytes_per_line; - - for (l = 0; l < lines; ++ l) + + for (l = 0; l < lines; ++l) { u_int8_t* begin_ptr = out_data + (l * bytes_per_line); u_int8_t* end_ptr = begin_ptr + bytes_per_line; @@ -6073,11 +6281,11 @@ int l; int lines = useful_bytes / bytes_per_line; - - for (l = 0; l < lines; ++ l) + + for (l = 0; l < lines; ++l) { u_int8_t* begin_ptr = out_data + (l * bytes_per_line); - u_int8_t* end_ptr = begin_ptr + bytes_per_line - 2; + u_int8_t* end_ptr = begin_ptr + bytes_per_line - 3; while (begin_ptr < end_ptr) { u_int8_t tmp; @@ -6153,7 +6361,7 @@ /* I know that the next lines are broken if not a multiple of bytes_per_line are in the buffer. Shouldn't happen ... */ fwrite (out_data, bytes_per_line, useful_bytes / bytes_per_line, fp); - + /* save image date in stripe buffer for next next stripe */ stripe_fill -= useful_bytes; if (stripe_fill > 0) @@ -6161,17 +6369,12 @@ s->line += useful_bytes / bytes_per_line; - iteration_end: DBG (3, "reader_process: end of iteration\n"); - ++stripe; } /* end while not all lines or inf. mode */ - DBG (3, "reader_process: regular i/o loop finished\n"); - exit_status = SANE_STATUS_EOF; - - cleanup: - - DBG (3, "reader_process: cleanup\n"); + DBG (3, "reader_process: i/o loop finished\n"); + if (exit_status == SANE_STATUS_GOOD) + exit_status = SANE_STATUS_EOF; /* maybe we need to fill in some white data */ if (exit_status == SANE_STATUS_EOF && s->line < s->params.lines) { @@ -6321,6 +6524,11 @@ linenumber); disable_calibration = SANE_TRUE; } + else if (strcmp (word, "force-calibration") == 0) { + DBG (3, "sane_init: config file line %d: force-calibration\n", + linenumber); + force_calibration = SANE_TRUE; + } else if (strcmp (word, "force-a4") == 0) { DBG (3, "sane_init: config file line %d: enabling force-a4\n", linenumber); @@ -6384,13 +6592,18 @@ free (word); } /* end if fp */ - /* search for all supported USB devices */ + /* search for all supported SCSI/USB devices */ while (Avision_Device_List [model_num].scsi_mfg != NULL) { - attaching_hw = 0; /* also potentionally accessed in the attach_* callbacks */ attaching_hw = &(Avision_Device_List [model_num]); - if (attaching_hw->usb_vendor != 0 && attaching_hw->usb_product != 0 ) + + sanei_scsi_find_devices (attaching_hw->scsi_mfg, + attaching_hw->scsi_model, NULL, + -1, -1, -1, -1, + attach_one_scsi); + + if (attaching_hw->usb_vendor != 0 && attaching_hw->usb_product != 0 ) { DBG (1, "sane_init: Trying to find USB device %x %x ...\n", attaching_hw->usb_vendor, @@ -6463,6 +6676,7 @@ SANE_Status status; Avision_Scanner* s; int i, j; + u_int8_t inquiry_result[AVISION_INQUIRY_SIZE]; DBG (3, "sane_open:\n"); @@ -6497,7 +6711,7 @@ s->av_con.scsi_fd = -1; s->av_con.usb_dn = -1; - s->pipe = -1; + s->read_fds = -1; s->hw = dev; /* We initilize the table to a gamma value of 2.22, since this is what @@ -6539,6 +6753,14 @@ DBG (1, "sane_open: got %d scsi_max_request_size\n", dev->scsi_buffer_size); } + /* first: re-awake the device with an inquiry, some devices are flunk while initializing + the usb connection and like a inquiry to come first ... (AV610 et.al.) */ + status = inquiry (s->av_con, inquiry_result, sizeof(inquiry_result)); + if (status != SANE_STATUS_GOOD) { + DBG (1, "sane_open: awakening inquiry failed: %s\n", sane_strstatus (status)); + return status; + } + /* maybe probe for additional information */ if (dev->additional_probe) { @@ -6582,7 +6804,7 @@ Avision_Scanner* s = handle; int i; - DBG (3, "sane_close:\n\n"); + DBG (3, "sane_close:\n"); /* close the device */ if (avision_is_open (&s->av_con) ) { @@ -6688,11 +6910,16 @@ case OPT_QSCAN: case OPT_QCALIB: case OPT_FRAME: - *(SANE_Word*) val = s->val[option].w; return SANE_STATUS_GOOD; /* specially treated word options */ + + case OPT_POWER_SAVE_TIME: + get_power_save_time (s, &(s->val[option].w)); + *(SANE_Word*) val = s->val[option].w; + return SANE_STATUS_GOOD; + case OPT_BUTTON_0: /* I would like to return _INVALID for buttons not present however xsane querries them all :-( */ @@ -6727,6 +6954,13 @@ case OPT_MESSAGE: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; + + case OPT_NVRAM: + get_and_parse_nvram (s, s->val[option].s, 1024); + + strcpy (val, s->val[option].s); + return SANE_STATUS_GOOD; + } /* end switch option */ } /* end if GET_ACTION_GET_VALUE */ else if (action == SANE_ACTION_SET_VALUE) @@ -6836,6 +7070,15 @@ } return status; } + case OPT_POWER_SAVE_TIME: + { + SANE_Word time = *((SANE_Word *) val); + + status = set_power_save_time (s, time); + if (status == SANE_STATUS_GOOD) + s->val[OPT_POWER_SAVE_TIME].w = time; + return status; + } } /* end switch option */ } /* end else SET_VALUE */ return SANE_STATUS_INVAL; @@ -6891,7 +7134,6 @@ DBG (1, "sane_start:\n"); /* Make sure there is no scan running!!! */ - if (s->scanning) return SANE_STATUS_DEVICE_BUSY; @@ -6920,6 +7162,18 @@ if (s->duplex_rear_valid) { DBG (1, "sane_start: virtual duplex rear data valid.\n"); + +#ifdef AVISION_ENHANCED_SANE + /* only continue the 2nd time */ + if (s->prepared == SANE_FALSE) { + s->prepared = SANE_TRUE; + DBG (1, "sane_start: scan prepared, leaving for now.\n"); + return SANE_STATUS_GOOD; + } + else + DBG (1, "sane_start: scan prepared, leaving for now.\n"); +#endif + goto start_scan_end; } @@ -6936,22 +7190,8 @@ DBG (1, "sane_start: media_check ok\n"); } -#ifndef AVISION_ENHANCED_SANE - if (param_cksum == s->param_cksum && dev->inquiry_keeps_window) - DBG (1, "sane_start: Optimized set_window away.\n"); - else - { - status = set_window (s); - if (status != SANE_STATUS_GOOD) { - DBG (1, "sane_start: set scan window command failed: %s\n", - sane_strstatus (status)); - goto stop_scanner_and_return; - } - } -#endif - /* Check the light early, to return to the GUI and notify the user. */ - if (param_cksum != s->param_cksum) { + if (s->prepared == SANE_FALSE) { if (dev->inquiry_light_control) { status = wait_4_light (s); if (status != SANE_STATUS_GOOD) { @@ -6960,9 +7200,8 @@ } } -#ifdef AVISION_ENHANCED_SANE if (param_cksum == s->param_cksum && dev->inquiry_keeps_window) - DBG (1, "sane_start: Optimized set_window away.\n"); + DBG (1, "sane_start: Optimized set_window away.\n"); else { status = set_window (s); @@ -6972,8 +7211,7 @@ goto stop_scanner_and_return; } } -#endif - + #ifdef DEBUG_TEST /* debug window size test ... */ if (dev->inquiry_new_protocol) @@ -6996,20 +7234,23 @@ } #endif -#ifdef PARANOID - status = wait_ready (&s->av_con); - - if (status != SANE_STATUS_GOOD) { - DBG (1, "sane_start wait_ready() failed: %s\n", sane_strstatus (status)); - goto stop_scanner_and_return; +#ifdef AVISION_ENHANCED_SANE + /* only continue the 2nd time */ + if (s->prepared == SANE_FALSE) { + s->prepared = SANE_TRUE; + DBG (1, "sane_start: scan prepared, leaving for now.\n"); + return SANE_STATUS_GOOD; } + else + DBG (1, "sane_start: scan prepared, leaving for now.\n"); #endif - /* if (param_cksum == s->param_cksum) { - DBG (1, "sane_start: Optimized calibration away.\n"); - goto calib_end; - } */ - + /* check whether the user enforces calibration */ + if (force_calibration) { + DBG (1, "sane_start: calibration enforced in config!\n"); + goto calib; + } + /* Only perform the calibration for newer scanners - it is not needed for my Avision AV 630 - and also does not even work ... */ if (!dev->inquiry_new_protocol) { @@ -7034,12 +7275,11 @@ goto calib_end; } - /* TODO: do the calibration here if the user asks for it? */ - /* R² reminder: We must not skip the calibration for ADF scans, some scanner (HP 53xx/74xx ASIC series) rely on a calibration data read (and will hang otherwise) */ - + + calib: status = normal_calibration (s); if (status != SANE_STATUS_GOOD) { @@ -7051,7 +7291,8 @@ calib_end: if (dev->inquiry_3x3_matrix && dev->inquiry_asic_type >= AV_ASIC_C5 && - param_cksum != s->param_cksum && !(dev->hw->feature_type & AV_NO_MATRIX) ) { + param_cksum != s->param_cksum && !(dev->hw->feature_type & AV_NO_MATRIX) ) + { status = send_3x3_matrix (s); if (status != SANE_STATUS_GOOD) { return status; @@ -7065,7 +7306,7 @@ } if (param_cksum == s->param_cksum && dev->inquiry_keeps_gamma) - DBG (1, "sane_start: Optimized send_gama away.\n"); + DBG (1, "sane_start: Optimized send_gamma away.\n"); else { status = send_gamma (s); @@ -7096,25 +7337,24 @@ start_scan_end: s->scanning = SANE_TRUE; + s->param_cksum = param_cksum; /* now the scanner is setup, can be reused */ if (pipe (fds) < 0) { return SANE_STATUS_IO_ERROR; } - s->pipe = fds[0]; - s->reader_fds = fds[1]; - -#ifdef AVISION_ENHANCED_SANE + s->read_fds = fds[0]; + s->write_fds = fds[1]; + s->reader_pid = 0; /* the thread will be started uppon the first read */ -#else + /* create reader routine as new process or thread */ DBG (3, "sane_start: starting thread\n"); s->reader_pid = sanei_thread_begin (reader_process, (void *) s); if (sanei_thread_is_forked()) - close (s->reader_fds); -#endif - + close (s->write_fds); + return SANE_STATUS_GOOD; stop_scanner_and_return: @@ -7130,23 +7370,11 @@ { Avision_Scanner* s = handle; ssize_t nread; + *len = 0; -#ifdef AVISION_ENHANCED_SANE - /* create reader routine as new process or thread */ - if (s->reader_pid == 0) { - DBG (3, "sane_read: starting thread\n"); - s->reader_pid = sanei_thread_begin (reader_process, (void *) s); - - if (sanei_thread_is_forked()) - close (s->reader_fds); - } -#endif - DBG (8, "sane_read: max_len: %d\n", max_len); - *len = 0; - - nread = read (s->pipe, buf, max_len); + nread = read (s->read_fds, buf, max_len); if (nread > 0) { DBG (8, "sane_read: got %ld bytes\n", (long) nread); } @@ -7168,11 +7396,10 @@ *len = nread; - /* if all data is passed through */ - if (nread == 0) { - s->scanning = SANE_FALSE; + /* if all data was passed through */ + if (nread == 0) return do_eof (s); - } + return SANE_STATUS_GOOD; } @@ -7180,9 +7407,18 @@ sane_cancel (SANE_Handle handle) { Avision_Scanner* s = handle; - DBG (3, "sane_cancel:\n"); - + +#ifdef AVISION_ENHANCED_SANE + /* really start already? */ + if (s->scanning == SANE_FALSE) { + DBG (3, "sane_cancel: The scan was not yet initiated, just resetting state.\n"); + s->prepared = SANE_FALSE; + return; + } + DBG (3, "sane_cancel: Really cancelling.\n"); +#endif + if (s->scanning) do_cancel (s); } @@ -7198,7 +7434,7 @@ return SANE_STATUS_INVAL; } - if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) + if (fcntl (s->read_fds, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; @@ -7216,7 +7452,7 @@ return SANE_STATUS_INVAL; } - *fd = s->pipe; + *fd = s->read_fds; return SANE_STATUS_GOOD; } diff -uNr sane-backends-1.0.17/backend/avision.conf sane-backends-1.0.18/backend/avision.conf --- sane-backends-1.0.17/backend/avision.conf 2005-12-06 11:06:11.000000000 +0100 +++ sane-backends-1.0.18/backend/avision.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ - -# This are the possible options. Normally any scanner -# should work just fine without them - and they are only -# needed for test and debugging. So if you experience problems -# and you solve them with enabling options here, please notify -# the SANE/Avision maintainer: Rene Rebe - -#option disable-gamma-table -#option disable-calibration -#option force-a4 - -scsi AVISION -scsi FCPA -scsi MINOLTA -scsi MITSBISH MCA-S600C -scsi MITSBISH MCA-SS600 -scsi HP -scsi hp - -scsi /dev/scanner -# usb libusb:002:003 -# usb 0x03f0 0x0701 - diff -uNr sane-backends-1.0.17/backend/avision.conf.in sane-backends-1.0.18/backend/avision.conf.in --- sane-backends-1.0.17/backend/avision.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/avision.conf.in 2006-06-06 18:32:02.000000000 +0200 @@ -0,0 +1,23 @@ + +# This are the possible options. Normally any scanner +# should work just fine without them - and they are only +# needed for test and debugging. So if you experience problems +# and you solve them with enabling options here, please notify +# the SANE/Avision maintainer: Rene Rebe + +#option disable-gamma-table +#option disable-calibration +#option force-a4 + +#scsi AVISION +#scsi FCPA +#scsi MINOLTA +#scsi MITSBISH MCA-S600C +#scsi MITSBISH MCA-SS600 +#scsi HP +#scsi hp + +#scsi /dev/scanner +# usb libusb:002:003 +# usb 0x03f0 0x0701 + diff -uNr sane-backends-1.0.17/backend/avision.h sane-backends-1.0.18/backend/avision.h --- sane-backends-1.0.17/backend/avision.h 2005-12-04 22:42:50.000000000 +0100 +++ sane-backends-1.0.18/backend/avision.h 2006-06-06 18:32:02.000000000 +0200 @@ -133,7 +133,19 @@ AV_NO_MATRIX = (1<<14), /* force channel-by-channel calibration */ - AV_MULTI_CALIB_CMD = (1<<15) + AV_MULTI_CALIB_CMD = (1<<15), + + /* non color scans are faster with a filter applied (AV32xx) */ + AV_FASTER_WITH_FILTER = (1<<16), + + /* interlaced data with 1 line distance */ + AV_2ND_LINE_INTERLACED = (1<<17), + + /* does not keep the window though it advertices so */ + AV_DOES_NOT_KEEP_WINDOW = (1<<18), + + /* advertises ADF is BGR order, but isn't (or vice versa) */ + AV_ADF_BGR_ORDER_INVERT = (1<<18) /* maybe more ...*/ } feature_type; @@ -210,7 +222,7 @@ OPT_GAMMA_VECTOR_B, /* too bad the SANE API does not allow bool vectors ... */ - OPT_BUTTON_0, /* scanner button pressed */ + OPT_BUTTON_0, /* scanner button pressed */ OPT_BUTTON_1, OPT_BUTTON_2, OPT_BUTTON_3, @@ -219,10 +231,13 @@ OPT_BUTTON_6, OPT_BUTTON_7, OPT_BUTTON_LAST = OPT_BUTTON_7, + OPT_MESSAGE, /* optional message from the scanner display */ - OPT_MESSAGE, - OPT_FRAME, /* Film holder control */ + + OPT_POWER_SAVE_TIME, /* set power save time to the scanner */ + + OPT_NVRAM, /* retrieve NVRAM values as pretty printed text */ NUM_OPTIONS /* must come last */ }; @@ -262,6 +277,10 @@ asic_type inquiry_asic_type; SANE_Bool inquiry_new_protocol; + + SANE_Bool inquiry_nvram_read; + SANE_Bool inquiry_power_save_time; + SANE_Bool inquiry_light_box; SANE_Bool inquiry_adf; SANE_Bool inquiry_duplex; @@ -347,8 +366,9 @@ /* Parsed option values and variables that are valid only during the actual scan: */ - + SANE_Bool prepared; /* first page marker */ SANE_Bool scanning; /* scan in progress */ + SANE_Parameters params; /* scan window */ Avision_Dimensions avdimen; /* scan window - detailed internals */ @@ -365,9 +385,9 @@ /* Avision HW Access Connection (SCSI/USB abstraction) */ Avision_Connection av_con; - pid_t reader_pid; /* process id of reader */ - int reader_fds; /* pipe write handler for reader */ - int pipe; /* pipe to reader process */ + pid_t reader_pid; /* process id of reader */ + int read_fds; /* pipe reading end */ + int write_fds; /* pipe writing end */ int line; /* current line number during scan */ } Avision_Scanner; @@ -402,6 +422,14 @@ #define AVISION_SCSI_OP_TRANS_CALIB_GRAY 0x04 #define AVISION_SCSI_OP_TRANS_CALIB_COLOR 0x05 +#define AVISION_FILTER_NONE 0 +#define AVISION_FILTER_RED 1 +#define AVISION_FILTER_GREEN 2 +#define AVISION_FILTER_BLUE 3 +#define AVISION_FILTER_RGB 4 +#define AVISION_FILTER_CMYK 5 +#define AVISION_FILTER_GRAY 6 + /* The SCSI structures that we have to send to an avision to get it to do various stuff... */ @@ -452,6 +480,42 @@ u_int8_t reserved1; } command_send; +typedef struct nvram_data +{ + u_int8_t pad_scans [4]; + u_int8_t adf_simplex_scans [4]; + u_int8_t adf_duplex_scans [4]; + u_int8_t flatbed_scans [4]; + + u_int8_t flatbed_leading_edge [2]; + u_int8_t flatbed_side_edge [2]; + u_int8_t adf_leading_edge [2]; + u_int8_t adf_side_edge [2]; + u_int8_t adf_rear_leading_edge [2]; + u_int8_t adf_rear_side_edge [2]; + + u_int8_t born_month [2]; + u_int8_t born_day [2]; + u_int8_t born_year [2]; + + u_int8_t first_scan_month [2]; + u_int8_t first_scan_day [2]; + u_int8_t first_scan_year [2]; + + u_int8_t vertical_magnification [2]; + u_int8_t horizontal_magnification [2]; + + u_int8_t ccd_type; + u_int8_t scan_speed; + + u_int8_t serial [24]; + + u_int8_t power_saving_time [2]; + + u_int8_t reserved [56]; +} nvram_data; + + typedef struct command_set_window_window { struct { @@ -493,7 +557,7 @@ u_int8_t line_width [2]; u_int8_t line_count [2]; - /* the tail is quite version and model sepecific */ + /* the tail is quite version and model specific */ union { struct { u_int8_t bitset2; diff -uNr sane-backends-1.0.17/backend/bh.conf sane-backends-1.0.18/backend/bh.conf --- sane-backends-1.0.17/backend/bh.conf 2001-01-21 00:37:53.000000000 +0100 +++ sane-backends-1.0.18/backend/bh.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -scsi "B&H SCSI" -/dev/scanner diff -uNr sane-backends-1.0.17/backend/bh.conf.in sane-backends-1.0.18/backend/bh.conf.in --- sane-backends-1.0.17/backend/bh.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/bh.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +scsi "B&H SCSI" +/dev/scanner diff -uNr sane-backends-1.0.17/backend/canon630u.conf sane-backends-1.0.18/backend/canon630u.conf --- sane-backends-1.0.17/backend/canon630u.conf 2002-04-07 14:27:53.000000000 +0200 +++ sane-backends-1.0.18/backend/canon630u.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,8 +0,0 @@ -# Options for the canonusb backend - -# Autodetect the Canon CanoScan FB630u -usb 0x04a9 0x2204 - -# device list for non-linux-systems (enable if autodetect fails): -#/dev/scanner -#/dev/usb/scanner0 diff -uNr sane-backends-1.0.17/backend/canon630u.conf.in sane-backends-1.0.18/backend/canon630u.conf.in --- sane-backends-1.0.17/backend/canon630u.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/canon630u.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,8 @@ +# Options for the canonusb backend + +# Autodetect the Canon CanoScan FB630u +usb 0x04a9 0x2204 + +# device list for non-linux-systems (enable if autodetect fails): +#/dev/scanner +#/dev/usb/scanner0 diff -uNr sane-backends-1.0.17/backend/canon.c sane-backends-1.0.18/backend/canon.c --- sane-backends-1.0.17/backend/canon.c 2005-10-02 15:10:47.000000000 +0200 +++ sane-backends-1.0.18/backend/canon.c 2006-06-19 23:07:52.000000000 +0200 @@ -241,7 +241,7 @@ } for (i = 0; i < buf_size; i++) - DBG (3, "scan mode control byte[%d] = %d\n", i, tbuf[i]); + DBG (3, "scan mode control byte[%d] = %d\n", (int) i, tbuf[i]); dev->tpu.Status = (tbuf[2 + 4 + 5] >> 7) ? TPU_STAT_INACTIVE : TPU_STAT_NONE; if (dev->tpu.Status == SANE_TRUE) /* TPU available */ @@ -286,7 +286,7 @@ } for (i = 0; i < buf_size; i++) - DBG (3, "scan mode control byte[%d] = %d\n", i, abuf[i]); + DBG (3, "scan mode control byte[%d] = %d\n", (int) i, abuf[i]); dev->adf.Status = (abuf[ADF_Status] & ADF_NOT_PRESENT) ? ADF_STAT_NONE : ADF_STAT_INACTIVE; @@ -330,6 +330,7 @@ result[9], result[10], result[11], result[12], result[13], result[14], result[15]); + status = SANE_STATUS_GOOD; if (dev && dev->info.is_scsi2) { DBG(11, "sense data interpretation for SCSI-2 devices\n"); @@ -750,7 +751,7 @@ } for (i = 0; i < buf_size; i++) { - DBG (3, "scan mode byte[%d] = %d\n", i, ebuf[i]); + DBG (3, "scan mode byte[%d] = %d\n", (int) i, ebuf[i]); } DBG (3, "attach: sending (extended) INQUIRY\n"); diff -uNr sane-backends-1.0.17/backend/canon.conf sane-backends-1.0.18/backend/canon.conf --- sane-backends-1.0.17/backend/canon.conf 2001-05-29 20:27:52.000000000 +0200 +++ sane-backends-1.0.18/backend/canon.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -#canon.conf -/dev/scanner -#/dev/sg0 diff -uNr sane-backends-1.0.17/backend/canon.conf.in sane-backends-1.0.18/backend/canon.conf.in --- sane-backends-1.0.17/backend/canon.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/canon.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,3 @@ +#canon.conf +/dev/scanner +#/dev/sg0 diff -uNr sane-backends-1.0.17/backend/canon.h sane-backends-1.0.18/backend/canon.h --- sane-backends-1.0.17/backend/canon.h 2005-07-04 18:59:53.000000000 +0200 +++ sane-backends-1.0.18/backend/canon.h 2006-06-19 23:07:52.000000000 +0200 @@ -1,8 +1,9 @@ /* sane - Scanner Access Now Easy. Copyright (C) 1997 BYTEC GmbH Germany Written by Helmut Koeberle, Email: helmut.koeberle@bytec.de - Modified by Manuel Panea - and Markus Mertinat + Modified by Manuel Panea , + Markus Mertinat , + and ULrich Deiters This file is part of the SANE package. @@ -45,6 +46,12 @@ #ifndef canon_h #define canon_h 1 +#ifdef __GNUC__ +#define UNUSEDARG __attribute__ ((unused)) +#else +#define UNUSEDARG +#endif + /* all the different possible model names. */ #define FB1200S "IX-12015E " #define FB620S "IX-06035E " diff -uNr sane-backends-1.0.17/backend/canon_pp.conf sane-backends-1.0.18/backend/canon_pp.conf --- sane-backends-1.0.17/backend/canon_pp.conf 2002-09-17 12:33:20.000000000 +0200 +++ sane-backends-1.0.18/backend/canon_pp.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,36 +0,0 @@ -# Define which port to use if one isn't specified - you should only have -# one of these lines! -# This is the default port to be used - others will be detected -ieee1284 parport0 - - -# Define the location of our pixel weight file, can begin with ~/ if needed. -# You can have as many of these as you like - lines with ports that don't exist -# will be ignored. -# -# Parameters are: -# calibrate /path/to/calibration-file port-name -# -# The format of port-name is dependant on your OS version. -# -# If a file isn't speficied, the default name will be -# ~/.sane/canon_pp-calibration-[port-name] - -calibrate ~/.sane/canon_pp-calibration-pp0 parport0 - -# calibrate /etc/sane/my_calibration parport1 - - -# Enable the next line if you're having trouble with ECP mode such as I/O -# errors. Nibble mode is slower, but more reliable. - -#force_nibble - -# Set a default initialisation mode for each port. Valid modes are: -# AUTO (attempts to automatically detect by trying both methods) -# FB620P (10101010 style.. also works for FB320P) -# FB630P (11001100 style.. also works for FB330P, N340P, N640P) - -init_mode AUTO parport0 -# init_mode FB620P parport0 -# init_mode FB630P parport0 diff -uNr sane-backends-1.0.17/backend/canon_pp.conf.in sane-backends-1.0.18/backend/canon_pp.conf.in --- sane-backends-1.0.17/backend/canon_pp.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/canon_pp.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,36 @@ +# Define which port to use if one isn't specified - you should only have +# one of these lines! +# This is the default port to be used - others will be detected +ieee1284 parport0 + + +# Define the location of our pixel weight file, can begin with ~/ if needed. +# You can have as many of these as you like - lines with ports that don't exist +# will be ignored. +# +# Parameters are: +# calibrate /path/to/calibration-file port-name +# +# The format of port-name is dependant on your OS version. +# +# If a file isn't speficied, the default name will be +# ~/.sane/canon_pp-calibration-[port-name] + +calibrate ~/.sane/canon_pp-calibration-pp0 parport0 + +# calibrate /etc/sane/my_calibration parport1 + + +# Enable the next line if you're having trouble with ECP mode such as I/O +# errors. Nibble mode is slower, but more reliable. + +#force_nibble + +# Set a default initialisation mode for each port. Valid modes are: +# AUTO (attempts to automatically detect by trying both methods) +# FB620P (10101010 style.. also works for FB320P) +# FB630P (11001100 style.. also works for FB330P, N340P, N640P) + +init_mode AUTO parport0 +# init_mode FB620P parport0 +# init_mode FB630P parport0 diff -uNr sane-backends-1.0.17/backend/canon-sane.c sane-backends-1.0.18/backend/canon-sane.c --- sane-backends-1.0.17/backend/canon-sane.c 2005-11-26 12:01:58.000000000 +0100 +++ sane-backends-1.0.18/backend/canon-sane.c 2006-06-19 23:07:52.000000000 +0200 @@ -1,5 +1,5 @@ SANE_Status -sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize) { char devnam[PATH_MAX] = "/dev/scanner"; FILE *fp; @@ -97,7 +97,7 @@ next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); - free (dev); + free ((void *) dev); } DBG (1, "<< sane_exit\n"); @@ -106,7 +106,8 @@ /**************************************************************************/ SANE_Status -sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +sane_get_devices (const SANE_Device *** device_list, +SANE_Bool UNUSEDARG local_only) { static const SANE_Device **devlist = 0; CANON_Device *dev; @@ -1249,7 +1250,7 @@ return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) - DBG (3, "scan mode control byte[%d] = %d\n", i, ebuf[i]); + DBG (3, "scan mode control byte[%d] = %d\n", (int) i, ebuf[i]); if (s->hw->adf.Status != ADF_STAT_NONE) { @@ -1265,7 +1266,8 @@ return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) - DBG (3, "scan mode control byte[%d] = %d\n", i, ebuf[i]); + DBG (3, "scan mode control byte[%d] = %d\n", (int) i, + ebuf[i]); } mode_str = s->val[OPT_MODE].s; @@ -1524,7 +1526,7 @@ return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) - DBG (3, "scan mode byte[%d] = %d\n", i, ebuf[i]); + DBG (3, "scan mode byte[%d] = %d\n", (int) i, ebuf[i]); /* Focus, but not for previews or negatives with speed control */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FOCUS_GROUP].cap) @@ -1690,8 +1692,8 @@ *len = 0; nread = max_len; - DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", nread, - s->bytes_to_read); + DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread, + (int) s->bytes_to_read); if (s->bytes_to_read == 0) { do_cancel (s); @@ -1710,8 +1712,8 @@ *len = nread; s->bytes_to_read -= nread; - DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", nread, - s->bytes_to_read); + DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread, + (int) s->bytes_to_read); DBG (21, "<< sane_read\n"); return (SANE_STATUS_GOOD); } @@ -1726,15 +1728,18 @@ SANE_Status status; int c; size_t i, nread, nread2; - u_char *p, b; + u_char *p; + #if defined(WORDS_BIGENDIAN) + u_char b; + #endif DBG (21, ">> sane_read\n"); *len = 0; nread = max_len; - DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", nread, - s->bytes_to_read); + DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread, + (int) s->bytes_to_read); if (nread > s->bytes_to_read) nread = s->bytes_to_read; if (s->bytes_to_read == 0) @@ -1745,10 +1750,11 @@ if (!s->scanning) return (do_cancel (s)); - /* We must receive 2 bytes per pixel and colour. In raw mode we - must swap the bytes (for compatibility with xsane) and pass - them both. Otherwise the other subroutines expect only 1 byte, - so we must set up an intermediate buffer which is twice as large + /* We must receive 2 little-endian bytes per pixel and colour. + In raw mode we must swap the bytes if we are running a big-endian + architecture (SANE standard 3.2.1), and pass them both. + Otherwise the other subroutines expect only 1 byte, so we must + set up an intermediate buffer which is twice as large as buf, and then map this buffer to buf. */ if (!strcmp (s->val[OPT_MODE].s, "Color")) @@ -1787,18 +1793,20 @@ do_cancel (s); return (SANE_STATUS_IO_ERROR); } +#if defined(WORDS_BIGENDIAN) for (p = buf; p < buf + nread; p++) { b = *p; *p++ = *(p + 1); *p = b; } +#endif } *len = nread; s->bytes_to_read -= nread; - DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", nread, - s->bytes_to_read); + DBG (21, " sane_read: nread=%d, bytes_to_read=%d\n", (int) nread, + (int) s->bytes_to_read); DBG (21, "<< sane_read\n"); return (SANE_STATUS_GOOD); } @@ -1820,8 +1828,8 @@ *len = 0; - DBG (21, " read_fb620: nread=%d, bytes_to_read=%d\n", nread, - s->bytes_to_read); + DBG (21, " read_fb620: nread=%d, bytes_to_read=%d\n", (int) nread, + (int) s->bytes_to_read); if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used) { @@ -1946,12 +1954,14 @@ + (dbuf[22] << 8) + dbuf[23]); DBG (5, "Filled Data Buffer=%d\n", (dbuf[24] << 24) + (dbuf[25] << 16) + (dbuf[26] << 8) + dbuf[27]); - DBG (5, "temp file position:%u\n", lseek(s->tmpfile, 0, SEEK_CUR)); + DBG (5, "temp file position:%u\n", (unsigned int) lseek(s->tmpfile, + 0, SEEK_CUR)); DBG (5, "<< GET DATA STATUS\n"); *len = 0; - DBG (21, " read_fb1200: bytes_to_read=%d\n", s->bytes_to_read); + DBG (21, " read_fb1200: bytes_to_read=%d\n", + (int) s->bytes_to_read); if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used) { @@ -2201,7 +2211,8 @@ /**************************************************************************/ SANE_Status -sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +sane_set_io_mode (SANE_Handle UNUSEDARG handle, +SANE_Bool UNUSEDARG non_blocking) { DBG (1, ">> sane_set_io_mode\n"); DBG (1, "<< sane_set_io_mode\n"); @@ -2211,7 +2222,8 @@ /**************************************************************************/ SANE_Status -sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +sane_get_select_fd (SANE_Handle UNUSEDARG handle, +SANE_Int UNUSEDARG * fd) { DBG (1, ">> sane_get_select_fd\n"); DBG (1, "<< sane_get_select_fd\n"); diff -uNr sane-backends-1.0.17/backend/canon-scsi.c sane-backends-1.0.18/backend/canon-scsi.c --- sane-backends-1.0.17/backend/canon-scsi.c 2005-10-02 15:10:50.000000000 +0200 +++ sane-backends-1.0.18/backend/canon-scsi.c 2006-06-19 23:07:52.000000000 +0200 @@ -381,10 +381,12 @@ : (page == SCAN_CONTROL_CON_FB1200) ? 19 : 32); for (i = 0; i < sizeof (cmd); i++) - DBG (31, "define scan mode: cmd[%d]='0x%0X'\n", i, cmd[i]); + DBG (31, "define scan mode: cmd[%d]='0x%0X'\n", (int) i, + cmd[i]); for (i = 0; i < sizeof (pdata); i++) - DBG (31, "define scan mode: pdata[%d]='0x%0X'\n", i, pdata[i]); + DBG (31, "define scan mode: pdata[%d]='0x%0X'\n", (int) i, + pdata[i]); pdatalen = (page == TRANSPARENCY_UNIT) ? 12 : (page == TRANSPARENCY_UNIT_FB1200) ? 14 diff -uNr sane-backends-1.0.17/backend/coolscan2.c sane-backends-1.0.18/backend/coolscan2.c --- sane-backends-1.0.17/backend/coolscan2.c 2003-01-24 00:41:21.000000000 +0100 +++ sane-backends-1.0.18/backend/coolscan2.c 2006-06-11 20:39:40.000000000 +0200 @@ -56,6 +56,7 @@ /* Revision log: + 0.1.9, 20/10/2005, ariel: added support for the LS-50/5000 0.1.8, 27/09/2002, andras: added subframe and load options 0.1.7, 22/08/2002, andras: added exposure correction option and hack for LS-40 IR readout @@ -114,8 +115,10 @@ CS2_TYPE_UNKOWN, CS2_TYPE_LS30, CS2_TYPE_LS40, + CS2_TYPE_LS50, CS2_TYPE_LS2000, CS2_TYPE_LS4000, + CS2_TYPE_LS5000, CS2_TYPE_LS8000 } cs2_type_t; @@ -257,6 +260,7 @@ unsigned long real_xoffset, real_yoffset, real_width, real_height, logical_width, logical_height; int odd_padding; + int block_padding; double exposure, exposure_r, exposure_g, exposure_b; unsigned long real_exposure[10]; @@ -1512,6 +1516,16 @@ xfer_len_in = s->n_colour_in * s->logical_width * s->bytes_per_pixel + s->n_colour_in * s->odd_padding; + /* Do not change the behaviour of older models */ + if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000)) + { + /* Ariel - Check, win driver uses multiple of 64, docu seems to say 512? */ + ssize_t i; + xfer_len_in += s->block_padding; + i = (xfer_len_in & 0x3f); + if (i != 0) + DBG (1, "BUG: sane_read(): Read size is not a multiple of 64. (0x%06x)\n", i); + } if (s->xfer_position + xfer_len_line > s->xfer_bytes_total) xfer_len_line = s->xfer_bytes_total - s->xfer_position; /* just in case */ @@ -1679,6 +1693,8 @@ sanei_config_attach_matching_devices ("scsi Nikon *", cs2_attach); try_interface = CS2_INTERFACE_USB; sanei_usb_attach_matching_devices ("usb 0x04b0 0x4000", cs2_attach); + sanei_usb_attach_matching_devices ("usb 0x04b0 0x4001", cs2_attach); + sanei_usb_attach_matching_devices ("usb 0x04b0 0x4002", cs2_attach); return SANE_STATUS_GOOD; } @@ -1769,10 +1785,14 @@ s->type = CS2_TYPE_LS30; else if (!strncmp (s->product_string, "LS-40 ED ", 16)) s->type = CS2_TYPE_LS40; + else if (!strncmp (s->product_string, "LS-50 ED ", 16)) + s->type = CS2_TYPE_LS50; else if (!strncmp (s->product_string, "LS-2000 ", 16)) s->type = CS2_TYPE_LS2000; else if (!strncmp (s->product_string, "LS-4000 ED ", 16)) s->type = CS2_TYPE_LS4000; + else if (!strncmp (s->product_string, "LS-5000 ED ", 16)) + s->type = CS2_TYPE_LS5000; else if (!strncmp (s->product_string, "LS-8000 ED ", 16)) s->type = CS2_TYPE_LS8000; @@ -1959,7 +1979,7 @@ break; } - if (s->sense_code == 0x09800600) + if ((s->sense_code == 0x09800600) || (s->sense_code == 0x09800601)) s->status = CS2_STATUS_REISSUE; return status; @@ -2120,6 +2140,11 @@ break; case CS2_INTERFACE_USB: status = sanei_usb_write_bulk (s->fd, s->send_buf, &s->n_cmd); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "Error: cs2_issue_cmd(): Could not write command.\n"); + return SANE_STATUS_IO_ERROR; + } switch (cs2_phase_check (s)) { case CS2_PHASE_OUT: @@ -2140,6 +2165,9 @@ status = sanei_usb_read_bulk (s->fd, s->recv_buf, &n_data); s->n_recv = n_data; break; + case CS2_PHASE_NONE: + DBG (4, "Error: cs2_issue_cmd(): No command received!\n"); + return SANE_STATUS_IO_ERROR; default: if (n_data) { @@ -2324,14 +2352,14 @@ s->resx_max = 256 * s->recv_buf[20] + s->recv_buf[21]; s->resx_min = 256 * s->recv_buf[22] + s->recv_buf[23]; s->boundaryx = - 65536 * (s->recv_buf[36] + 256 * s->recv_buf[37]) + + 65536 * (256 * s->recv_buf[36] + s->recv_buf[37]) + 256 * s->recv_buf[38] + s->recv_buf[39]; s->resy_optical = 256 * s->recv_buf[40] + s->recv_buf[41]; s->resy_max = 256 * s->recv_buf[42] + s->recv_buf[43]; s->resy_min = 256 * s->recv_buf[44] + s->recv_buf[45]; s->boundaryy = - 65536 * (s->recv_buf[58] + 256 * s->recv_buf[59]) + + 65536 * (256 * s->recv_buf[58] + s->recv_buf[59]) + 256 * s->recv_buf[60] + s->recv_buf[61]; s->focus_min = 256 * s->recv_buf[76] + s->recv_buf[77]; @@ -2649,6 +2677,8 @@ int i_boundary; unsigned long lvalue; +/* Ariel - Check this function */ + cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); cs2_parse_cmd (s, "2a 00 88 00 00 03"); @@ -2712,7 +2742,11 @@ cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); - cs2_parse_cmd (s, "15 10 00 00 0c 00 0b 00 00 00 03 06 00 00"); + /* Ariel - the '0b' byte in the 'else' part seems to be wrong, should be 0 */ + if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000)) + cs2_parse_cmd (s, "15 10 00 00 14 00 00 00 00 08 00 00 00 00 00 00 00 01 03 06 00 00"); + else + cs2_parse_cmd (s, "15 10 00 00 0c 00 0b 00 00 00 03 06 00 00"); cs2_pack_byte (s, (s->unit_dpi >> 8) & 0xff); cs2_pack_byte (s, s->unit_dpi & 0xff); cs2_parse_cmd (s, "00 00"); @@ -2724,6 +2758,9 @@ if (status) return status; + /* Ariel - Is this the best place to initialize it? */ + s->block_padding = 0; + status = cs2_set_boundary (s); if (status) return status; @@ -2830,7 +2867,12 @@ cs2_pack_byte (s, s->real_depth); /* pixel composition */ cs2_parse_cmd (s, "00 00 00 00 00 00 00 00 00 00 00 00 00"); cs2_pack_byte (s, 0x00); /* multiread, ordering */ - cs2_pack_byte (s, 0x80 + (s->negative ? 0 : 1)); /* averaging, pos/neg */ + /* No need to use an undocumented bit in LS50 */ + if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000)) + cs2_pack_byte (s, 0x00 + (s->negative ? 0 : 1)); /* averaging, pos/neg */ + else + cs2_pack_byte (s, 0x80 + (s->negative ? 0 : 1)); /* averaging, pos/neg */ + switch (type) { /* scanning kind */ case CS2_SCAN_NORMAL: @@ -2895,6 +2937,37 @@ return status; if (s->status == CS2_STATUS_REISSUE) { + /* Make sure we don't affect the behaviour for other scanners */ + if ((s->type == CS2_TYPE_LS50) || (s->type == CS2_TYPE_LS5000)) + { + cs2_init_buffer (s); + cs2_parse_cmd (s, "28 00 87 00 00 00 00 00 06 00"); + s->n_recv = 6; + status = cs2_issue_cmd (s); + if (status) + return status; + cs2_init_buffer (s); + cs2_parse_cmd (s, "28 00 87 00 00 00 00 00"); + cs2_pack_byte (s, s->recv_buf[5] + 6); + cs2_parse_cmd (s, "00"); + s->n_recv = s->recv_buf[5] + 6; + status = cs2_issue_cmd (s); + if (status) + return status; + if ((s->recv_buf[11] != 0x08) || (s->recv_buf[12] != 0x00)) + DBG (1, "BUG: cs2_scan(): Unexpected block_padding position.\n"); + s->block_padding = 256 * s->recv_buf[19] + s->recv_buf[20]; + cs2_init_buffer (s); + switch (s->n_colour_in) + { + case 3: + cs2_parse_cmd (s, "1b 00 00 00 03 00 01 02 03"); + break; + case 4: + cs2_parse_cmd (s, "1b 00 00 00 04 00 01 02 03 09"); + break; + } + } status = cs2_issue_cmd (s); if (status) return status; diff -uNr sane-backends-1.0.17/backend/coolscan2.conf sane-backends-1.0.18/backend/coolscan2.conf --- sane-backends-1.0.17/backend/coolscan2.conf 2002-04-23 00:06:21.000000000 +0200 +++ sane-backends-1.0.18/backend/coolscan2.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,20 +0,0 @@ -# coolscan2.conf: sample configuration file for coolscan2 backend -# -# The following entrie checks for your scanner by manufacturer (SCSI) -# and by vendor and product ID (USB). This is what the backend does when -# no configuration file can be found. -# -auto - -# You can also configure the backend for specific device files, but this -# should not normally be necessary (under Linux at least). -# Syntax for specific devices: : -# -# For a SCSI scanner, uncomment and edit the following line: -#scsi:/dev/scanner -# -# For a USB scanner, uncomment and edit the following line: -#usb:/dev/usbscanner -# -# For an IEEE 1394 scanner, use the SBP2 protocol (under Linux, use the -# sbp2 kernel module), and your scanner will be handled as a SCSI device. diff -uNr sane-backends-1.0.17/backend/coolscan2.conf.in sane-backends-1.0.18/backend/coolscan2.conf.in --- sane-backends-1.0.17/backend/coolscan2.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/coolscan2.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,20 @@ +# coolscan2.conf: sample configuration file for coolscan2 backend +# +# The following entrie checks for your scanner by manufacturer (SCSI) +# and by vendor and product ID (USB). This is what the backend does when +# no configuration file can be found. +# +auto + +# You can also configure the backend for specific device files, but this +# should not normally be necessary (under Linux at least). +# Syntax for specific devices: : +# +# For a SCSI scanner, uncomment and edit the following line: +#scsi:/dev/scanner +# +# For a USB scanner, uncomment and edit the following line: +#usb:/dev/usbscanner +# +# For an IEEE 1394 scanner, use the SBP2 protocol (under Linux, use the +# sbp2 kernel module), and your scanner will be handled as a SCSI device. diff -uNr sane-backends-1.0.17/backend/coolscan.conf sane-backends-1.0.18/backend/coolscan.conf --- sane-backends-1.0.17/backend/coolscan.conf 1999-08-09 20:05:53.000000000 +0200 +++ sane-backends-1.0.18/backend/coolscan.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -scsi Nikon * Scanner -/dev/scanner diff -uNr sane-backends-1.0.17/backend/coolscan.conf.in sane-backends-1.0.18/backend/coolscan.conf.in --- sane-backends-1.0.17/backend/coolscan.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/coolscan.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +scsi Nikon * Scanner +/dev/scanner diff -uNr sane-backends-1.0.17/backend/dc210.conf sane-backends-1.0.18/backend/dc210.conf --- sane-backends-1.0.17/backend/dc210.conf 2003-01-07 02:10:16.000000000 +0100 +++ sane-backends-1.0.18/backend/dc210.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,29 +0,0 @@ -# Serial port where the camera is connected -## Linux -port=/dev/ttyS0 -## IRIX -#port=/dev/ttyd1 -## Solaris -#port=/dev/term/a -## HP-UX -#port=/dev/tty0p0 -## Digital UNIX -#port=/dev/tty01 -# Max baud rate for download. Camera always starts at 9600 baud, then -# switches to the higher rate -## This works for Linux. Also works for IRIX (6.3 or higher), providing that -## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or -## a newer SGI hardware [see serial(7)]. -#baud=115200 -## This works for most UNIX's -baud=38400 -# Prints some extra information during the init phase. This can be -# handy, but note that printing anything to stderr breaks the saned -# network scanning. -#dumpinquiry -# How many usec (1,000,000ths of a) between writing the command and reading the -# result. 125000 seems to be the lowest I could go reliably. -cmdrespause=125000 -# How many usec (1,000,000ths of a) between sending the "back to default" break -# sending commands. -breakpause=1000000; diff -uNr sane-backends-1.0.17/backend/dc210.conf.in sane-backends-1.0.18/backend/dc210.conf.in --- sane-backends-1.0.17/backend/dc210.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/dc210.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,29 @@ +# Serial port where the camera is connected +## Linux +port=/dev/ttyS0 +## IRIX +#port=/dev/ttyd1 +## Solaris +#port=/dev/term/a +## HP-UX +#port=/dev/tty0p0 +## Digital UNIX +#port=/dev/tty01 +# Max baud rate for download. Camera always starts at 9600 baud, then +# switches to the higher rate +## This works for Linux. Also works for IRIX (6.3 or higher), providing that +## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or +## a newer SGI hardware [see serial(7)]. +#baud=115200 +## This works for most UNIX's +baud=38400 +# Prints some extra information during the init phase. This can be +# handy, but note that printing anything to stderr breaks the saned +# network scanning. +#dumpinquiry +# How many usec (1,000,000ths of a) between writing the command and reading the +# result. 125000 seems to be the lowest I could go reliably. +cmdrespause=125000 +# How many usec (1,000,000ths of a) between sending the "back to default" break +# sending commands. +breakpause=1000000; diff -uNr sane-backends-1.0.17/backend/dc240.conf sane-backends-1.0.18/backend/dc240.conf --- sane-backends-1.0.17/backend/dc240.conf 2003-01-07 02:10:16.000000000 +0100 +++ sane-backends-1.0.18/backend/dc240.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,29 +0,0 @@ -# Serial port where the camera is connected -## Linux -port=/dev/ttyS0 -## IRIX -#port=/dev/ttyd1 -## Solaris -#port=/dev/term/a -## HP-UX -#port=/dev/tty0p0 -## Digital UNIX -#port=/dev/tty01 -# Max baud rate for download. Camera always starts at 9600 baud, then -# switches to the higher rate -## This works for Linux. Also works for IRIX (6.3 or higher), providing that -## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or -## a newer SGI hardware [see serial(7)]. -#baud=115200 -## This works for most UNIX's -baud=38400 -# Prints some extra information during the init phase. This can be -# handy, but note that printing anything to stderr breaks the saned -# network scanning. -#dumpinquiry -# How many usec (1,000,000ths of a) between writing the command and reading the -# result. 125000 seems to be the lowest I could go reliably. -cmdrespause=125000 -# How many usec (1,000,000ths of a) between sending the "back to default" break -# sending commands. -breakpause=1000000; diff -uNr sane-backends-1.0.17/backend/dc240.conf.in sane-backends-1.0.18/backend/dc240.conf.in --- sane-backends-1.0.17/backend/dc240.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/dc240.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,29 @@ +# Serial port where the camera is connected +## Linux +port=/dev/ttyS0 +## IRIX +#port=/dev/ttyd1 +## Solaris +#port=/dev/term/a +## HP-UX +#port=/dev/tty0p0 +## Digital UNIX +#port=/dev/tty01 +# Max baud rate for download. Camera always starts at 9600 baud, then +# switches to the higher rate +## This works for Linux. Also works for IRIX (6.3 or higher), providing that +## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or +## a newer SGI hardware [see serial(7)]. +#baud=115200 +## This works for most UNIX's +baud=38400 +# Prints some extra information during the init phase. This can be +# handy, but note that printing anything to stderr breaks the saned +# network scanning. +#dumpinquiry +# How many usec (1,000,000ths of a) between writing the command and reading the +# result. 125000 seems to be the lowest I could go reliably. +cmdrespause=125000 +# How many usec (1,000,000ths of a) between sending the "back to default" break +# sending commands. +breakpause=1000000; diff -uNr sane-backends-1.0.17/backend/dc25.conf sane-backends-1.0.18/backend/dc25.conf --- sane-backends-1.0.17/backend/dc25.conf 2003-01-07 02:14:56.000000000 +0100 +++ sane-backends-1.0.18/backend/dc25.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -# Serial port where the camera is connected -## Linux -port=/dev/ttyS0 -## IRIX -#port=/dev/ttyd1 -## Solaris -#port=/dev/term/a -## HP-UX -#port=/dev/tty0p0 -## Digital UNIX -#port=/dev/tty01 -# Max baud rate for download. Camera always starts at 9600 baud, then -# switches to the higher rate -## This works for Linux. Also works for IRIX (6.3 or higher), providing that -## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or -## a newer SGI hardware [see serial(7)]. -#baud=115200 -## This works for most UNIX's -baud=38400 -# Prints some extra information during the init phase. This can be -# handy, but note that printing anything to stderr breaks the saned -# network scanning. -#dumpinquiry diff -uNr sane-backends-1.0.17/backend/dc25.conf.in sane-backends-1.0.18/backend/dc25.conf.in --- sane-backends-1.0.17/backend/dc25.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/dc25.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,23 @@ +# Serial port where the camera is connected +## Linux +port=/dev/ttyS0 +## IRIX +#port=/dev/ttyd1 +## Solaris +#port=/dev/term/a +## HP-UX +#port=/dev/tty0p0 +## Digital UNIX +#port=/dev/tty01 +# Max baud rate for download. Camera always starts at 9600 baud, then +# switches to the higher rate +## This works for Linux. Also works for IRIX (6.3 or higher), providing that +## the host is an O2, OCTANE, Origin2000/200, Onyx2, Origin3000/300, Onyx3 or +## a newer SGI hardware [see serial(7)]. +#baud=115200 +## This works for most UNIX's +baud=38400 +# Prints some extra information during the init phase. This can be +# handy, but note that printing anything to stderr breaks the saned +# network scanning. +#dumpinquiry diff -uNr sane-backends-1.0.17/backend/dell1600n_net.c sane-backends-1.0.18/backend/dell1600n_net.c --- sane-backends-1.0.17/backend/dell1600n_net.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/dell1600n_net.c 2006-06-19 23:07:52.000000000 +0200 @@ -0,0 +1,2052 @@ +/* + sane - Scanner Access Now Easy. + Copyright (C) 2006 Jon Chambers + + 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. + + Dell 1600n network scan driver for SANE. + + NOTE + + - need to add support for multiple network interfaces + + To debug: + SANE_DEBUG_DELL1600N_NET=255 scanimage --verbose 2>scan.errs 1>scan.png +*/ + +/*********************************************************** + * INCLUDES + ***********************************************************/ + +#include "../include/sane/config.h" +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" + +#define BACKEND_NAME dell1600n_net +#include "../include/sane/sanei_backend.h" + +#include +#include +#include +#include +#include + +/* :NOTE: these are likely to be platform-specific! */ +#include +#include +#include +#include + +#include +#include + + +/*********************************************************** + * DEFINITIONS + ***********************************************************/ + +/* Maximum number of scanners */ +#define MAX_SCANNERS 32 + +/* version number */ +#define DRIVER_VERSION SANE_VERSION_CODE( V_MAJOR, V_MINOR, 0 ) + +/* size of buffer for socket communication */ +#define SOCK_BUF_SIZE 2048 + +/* size of registation name */ +#define REG_NAME_SIZE 64 + +/* a buffer struct to store "stuff" */ +struct ComBuf +{ + size_t m_capacity; /* current allocated size in bytes */ + size_t m_used; /* current used size in bytes */ + unsigned char *m_pBuf; /* storage (or NULL if none allocated) */ +}; + +/* state data for a single scanner connection */ +struct ScannerState +{ + int m_udpFd; /* file descriptor to UDP socket */ + int m_tcpFd; /* file descriptor to TCP socket */ + struct sockaddr_in m_sockAddr; /* printer address */ + struct ComBuf m_buf; /* buffer for network data */ + struct ComBuf m_imageData; /* storage for decoded image data */ + int m_numPages; /* number of complete pages (host byte order) */ + struct ComBuf m_pageInfo; /* "array" of numPages PageInfo structs */ + int m_bFinish; /* set non-0 to signal that we are finished */ + int m_bCancelled; /* set non-0 that bFinish state arose from cancelation */ + char m_regName[REG_NAME_SIZE]; /* name with which to register */ + unsigned short m_xres; /* x resolution (network byte order) */ + unsigned short m_yres; /* y resolution (network byte order) */ + unsigned int m_composition; /* composition (0x01=>TIFF/PDF,0x40=>JPEG) (network byte order) */ + unsigned char m_brightness; /* brightness */ + unsigned int m_compression; /* compression (0x08=>CCIT Group 4,0x20=>JPEG) (network byte order) */ + unsigned int m_fileType; /* file type (2=>TIFF,4=>PDF,8=>JPEG)(network byte order) */ + unsigned int m_pixelWidth; /* width in pixels (network byte order) */ + unsigned int m_pixelHeight; /* height in pixels (network byte order) */ + unsigned int m_bytesRead; /* bytes read by SANE (host byte order) */ + unsigned int m_currentPageBytes;/* number of bytes of current page read (host byte order) */ +}; + +/* state data for a single page + NOTE: all ints are in host byte order +*/ +struct PageInfo +{ + int m_width; /* pixel width */ + int m_height; /* pixel height */ + int m_totalSize; /* total page size (bytes) */ + int m_bytesRemaining; /* number of bytes not yet passed to SANE client */ +}; + +/* struct for in-memory jpeg decompression */ +struct JpegDataDecompState +{ + struct jpeg_decompress_struct m_cinfo; /* base struct */ + unsigned char *m_pData; /* data pointer */ + unsigned int m_bytesRemaining; /* amount of unprocessed data */ +}; + +/* initial ComBuf allocation */ +#define INITIAL_COM_BUF_SIZE 1024 + +/*********************************************************** + * FUNCTION PROTOTYPES + ***********************************************************/ + +/* print hex buffer to stdout */ +static void HexDump (int debugLevel, const unsigned char *buf, + size_t bufSize); + +/* clears gKnownDevices array */ +static void ClearKnownDevices (void); + +/* initialise a ComBuf struct */ +static int InitComBuf (struct ComBuf *pBuf); + +/* free a ComBuf struct */ +static void FreeComBuf (struct ComBuf *pBuf); + +/* add data to a ComBuf struct */ +static int AppendToComBuf (struct ComBuf *pBuf, const unsigned char *pData, + size_t datSize); + +/* remove data from the front of a ComBuf struct */ +static int PopFromComBuf (struct ComBuf *pBuf, size_t datSize); + +/* initialise a packet */ +static int InitPacket (struct ComBuf *pBuf, char type); + +/* append message to a packet */ +static int AppendMessageToPacket (struct ComBuf *pBuf, + char messageType, + char *messageName, + char valueType, + void *pValue, size_t valueLen); + +/* write length data to packet header */ +static void FinalisePacket (struct ComBuf *pBuf); + +/* \return 1 if message is complete, 0 otherwise */ +static int MessageIsComplete (unsigned char *pData, size_t size); + +/* process a registration broadcast response + \return SANE_Device pointer on success (caller frees), NULL on failure +*/ +static SANE_Device *ProcessFindResponse (unsigned char *pData, size_t size); + +/* frees a scanner state struct stored in gOpenScanners */ +static void FreeScannerState (int iHandle); + +/* \return 1 if iHandle is a valid member of gOpenScanners, 0 otherwise */ +static int ValidScannerNumber (int iHandle); + +/* process UDP responses, \return 0 in success, >0 otherwise */ +static int ProcessUdpResponse (unsigned char *pData, size_t size, + struct ScannerState *pState); + +/* process TCP responses, \return 0 in success, >0 otherwise */ +static int ProcessTcpResponse (struct ScannerState *pState, + struct ComBuf *pTcpBufBuf); + +/* Process the data from a single scanned page, \return 0 in success, >0 otherwise */ +static int ProcessPageData (struct ScannerState *pState); + +/* Libjpeg decompression interface */ +static void JpegDecompInitSource (j_decompress_ptr cinfo); +static boolean JpegDecompFillInputBuffer (j_decompress_ptr cinfo); +static void JpegDecompSkipInputData (j_decompress_ptr cinfo, long numBytes); +static void JpegDecompTermSource (j_decompress_ptr cinfo); + +/*********************************************************** + * GLOBALS + ***********************************************************/ + +/* Results of last call to sane_get_devices */ +static SANE_Device *gKnownDevices[MAX_SCANNERS]; + +/* Array of open scanner device states. + :NOTE: (int)SANE_Handle is an offset into this array */ +static struct ScannerState *gOpenScanners[MAX_SCANNERS]; + +/* scanner port */ +static unsigned short gScannerPort = 1124; + +/* ms to wait for registration replies */ +static unsigned short gRegReplyWaitMs = 300; + +/*********************************************************** + * FUNCTION IMPLEMENTATIONS + ***********************************************************/ + +SANE_Status +sane_init (SANE_Int * version_code, + SANE_Auth_Callback __sane_unused__ authorize) +{ + + /* init globals */ + memset (gKnownDevices, 0, sizeof (gKnownDevices)); + memset (gOpenScanners, 0, sizeof (gOpenScanners)); + + /* report version */ + *version_code = DRIVER_VERSION; + + /* init debug */ + DBG_INIT (); + + return SANE_STATUS_GOOD; + +} /* sane_init */ + +/***********************************************************/ + +void +sane_exit (void) +{ + + int iHandle; + + /* clean up */ + ClearKnownDevices (); + + for (iHandle = 0; iHandle < MAX_SCANNERS; ++iHandle) + { + if (gOpenScanners[iHandle]) + FreeScannerState (iHandle); + } + +} /* sane_exit */ + +/***********************************************************/ + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, + SANE_Bool __sane_unused__ local_only) +{ + + int ret; + unsigned char sockBuf[SOCK_BUF_SIZE]; + int sock, optYes; + SANE_Device *pDevice; + struct ComBuf queryPacket; + struct sockaddr_in remoteAddr; + unsigned char ucVal; + fd_set readFds; + struct timeval selTimeVal; + int nread, iNextDevice; + + /* init variables */ + ret = SANE_STATUS_GOOD; + sock = 0; + pDevice = NULL; + optYes = 1; + InitComBuf (&queryPacket); + + /* clear previous results */ + ClearKnownDevices (); + + /* open UDP socket */ + sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) + { + DBG (1, "Error creating socket\n"); + ret = SANE_STATUS_NO_MEM; + goto cleanup; + } + setsockopt (sock, SOL_SOCKET, SO_BROADCAST, &optYes, sizeof (optYes)); + + /* prepare select mask */ + FD_ZERO (&readFds); + FD_SET (sock, &readFds); + selTimeVal.tv_sec = 0; + selTimeVal.tv_usec = gRegReplyWaitMs * 1000; + + /* init a packet */ + InitPacket (&queryPacket, 0x01); + + /* add query */ + ucVal = 0; + AppendMessageToPacket (&queryPacket, 0x25, "std-scan-discovery-all", + 0x02, &ucVal, sizeof (ucVal)); + + FinalisePacket (&queryPacket); + + DBG (10, "Sending:\n"); + HexDump (10, queryPacket.m_pBuf, queryPacket.m_used); + + + remoteAddr.sin_family = AF_INET; + remoteAddr.sin_port = htons (gScannerPort); + remoteAddr.sin_addr.s_addr = 0xFFFFFFFF; /* broadcast */ + + if (sendto (sock, queryPacket.m_pBuf, queryPacket.m_used, 0, + &remoteAddr, sizeof (remoteAddr)) == -1) + { + DBG (1, "Error sending broadcast packet\n"); + ret = SANE_STATUS_NO_MEM; + goto cleanup; + } + + /* process replies */ + iNextDevice = 0; + while (select (sock + 1, &readFds, NULL, NULL, &selTimeVal)) + { + + /* break if we've got no more storage space in array */ + if (iNextDevice >= MAX_SCANNERS) + { + DBG (1, "sane_get_devices: more than %d devices, ignoring\n", + MAX_SCANNERS); + break; + } + + nread = read (sock, sockBuf, sizeof (sockBuf)); + DBG (5, "Got a broadcast response, (%d bytes)\n", nread); + + if (nread <= 0) + break; + + HexDump (10, sockBuf, nread); + + /* process response (skipping bad ones) */ + if (!(pDevice = ProcessFindResponse (sockBuf, nread))) + continue; + + /* add to list */ + gKnownDevices[iNextDevice++] = pDevice; + + } /* while */ + + /* report our finds */ + *device_list = (const SANE_Device **) gKnownDevices; + +cleanup: + + if (sock) + close (sock); + FreeComBuf (&queryPacket); + + return ret; + +} /* sane_get_devices */ + +/***********************************************************/ + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + + int iHandle = -1, i; + SANE_Status status = SANE_STATUS_GOOD; + struct hostent *pHostent; + char *pDot; + + DBG( 5, "sane_open: %s\n", devicename ); + + /* find the next available scanner pointer in gOpenScanners */ + for (i = 0; i < MAX_SCANNERS; ++i) + { + + if (gOpenScanners[i]) + continue; + + iHandle = i; + break; + + } /* for */ + if (iHandle == -1) + { + DBG (1, "sane_open: no space left in gOpenScanners array\n"); + status = SANE_STATUS_NO_MEM; + goto cleanup; + } + + /* allocate some space */ + if (!(gOpenScanners[iHandle] = malloc (sizeof (struct ScannerState)))) + { + status = SANE_STATUS_NO_MEM; + goto cleanup; + } + + /* init data */ + memset (gOpenScanners[iHandle], 0, sizeof (struct ScannerState)); + InitComBuf (&gOpenScanners[iHandle]->m_buf); + InitComBuf (&gOpenScanners[iHandle]->m_imageData); + InitComBuf (&gOpenScanners[iHandle]->m_pageInfo); + gOpenScanners[iHandle]->m_xres = ntohs (200); + gOpenScanners[iHandle]->m_yres = ntohs (200); + gOpenScanners[iHandle]->m_composition = ntohl (0x01); + gOpenScanners[iHandle]->m_brightness = 0x80; + gOpenScanners[iHandle]->m_compression = ntohl (0x08); + gOpenScanners[iHandle]->m_fileType = ntohl (0x02); + + + /* look up scanner name */ + pHostent = gethostbyname (devicename); + if ((!pHostent) || (!pHostent->h_addr_list)) + { + DBG (1, "sane_open: error looking up scanner name %s\n", devicename); + status = SANE_STATUS_INVAL; + goto cleanup; + } + + /* open a UDP socket */ + if (!(gOpenScanners[iHandle]->m_udpFd = + socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP))) + { + DBG (1, "sane_open: error opening socket\n"); + status = SANE_STATUS_IO_ERROR; + goto cleanup; + } + + /* connect to the scanner */ + memset (&gOpenScanners[iHandle]->m_sockAddr, 0, + sizeof (gOpenScanners[iHandle]->m_sockAddr)); + gOpenScanners[iHandle]->m_sockAddr.sin_family = AF_INET; + gOpenScanners[iHandle]->m_sockAddr.sin_port = htons (gScannerPort); + memcpy (&gOpenScanners[iHandle]->m_sockAddr.sin_addr, + pHostent->h_addr_list[0], pHostent->h_length); + if (connect (gOpenScanners[iHandle]->m_udpFd, + (struct sockaddr *) &gOpenScanners[iHandle]->m_sockAddr, + sizeof (gOpenScanners[iHandle]->m_sockAddr))) + { + DBG (1, "sane_open: error connecting to %s:%d\n", devicename, + gScannerPort); + status = SANE_STATUS_IO_ERROR; + goto cleanup; + } + + /* set fallback registration name */ + sprintf (gOpenScanners[iHandle]->m_regName, "Sane"); + + /* try to fill in hostname */ + gethostname (gOpenScanners[iHandle]->m_regName, REG_NAME_SIZE); + + /* just in case... */ + gOpenScanners[iHandle]->m_regName[REG_NAME_SIZE - 1] = 0; + + /* chop off any domain (if any) */ + if ((pDot = strchr (gOpenScanners[iHandle]->m_regName, '.'))) + *pDot = 0; + + DBG (5, "sane_open: connected to %s:%d as %s\n", devicename, gScannerPort, + gOpenScanners[iHandle]->m_regName); + + + /* set the handle */ + *handle = (SANE_Handle) iHandle; + + return status; + +cleanup: + + if (iHandle != -1) + FreeScannerState (iHandle); + + return status; + +} /* sane_open */ + +/***********************************************************/ + +void +sane_close (SANE_Handle handle) +{ + + DBG( 5, "sane_close: %x\n", (int)handle ); + + FreeScannerState ((int) handle); + +} /* sane_close */ + +/***********************************************************/ + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle __sane_unused__ handle, + SANE_Int option) +{ + + static SANE_Option_Descriptor numOptions = { + "num_options", + "Number of options", + "Number of options", + SANE_TYPE_INT, + SANE_UNIT_NONE, + 1, + 0, + 0, + {0} + }; + + if (option == 0) + return &numOptions; + else + return NULL; + +} /* sane_get_option_descriptor */ + +/***********************************************************/ + +SANE_Status +sane_control_option (SANE_Handle __sane_unused__ handle, SANE_Int option, + SANE_Action action, void *value, + SANE_Int __sane_unused__ * info) +{ + + static int numOptions = 1; + + if (action == SANE_ACTION_GET_VALUE && option == 0) + *(int *) value = numOptions; + + return SANE_STATUS_GOOD; + +} /* sane_control_option */ + +/***********************************************************/ + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + int iHandle = (int) handle; + unsigned int width, height, imageSize; + struct PageInfo pageInfo; + + if (!gOpenScanners[iHandle]) + return SANE_STATUS_INVAL; + + /* fetch page info */ + memcpy( & pageInfo, gOpenScanners[iHandle]->m_pageInfo.m_pBuf, sizeof( pageInfo ) ); + + width = pageInfo.m_width; + height = pageInfo.m_height; + imageSize = width * height * 3; + + DBG( 5, "sane_get_parameters: bytes remaining on this page: %d, num pages: %d, size: %dx%d\n", + pageInfo.m_bytesRemaining, + gOpenScanners[iHandle]->m_numPages, + width, + height ); + + DBG (5, + "sane_get_parameters: handle %x: bytes outstanding: %d, image size: %d\n", + iHandle, gOpenScanners[iHandle]->m_imageData.m_used, imageSize); + + /* check for enough data */ + /* + if (gOpenScanners[iHandle]->m_imageData.m_used < imageSize) + { + DBG (1, "sane_get_parameters: handle %d: not enough data: %d < %d\n", + iHandle, gOpenScanners[iHandle]->m_imageData.m_used, imageSize); + return SANE_STATUS_INVAL; + } + */ + + + params->format = SANE_FRAME_RGB; + params->last_frame = SANE_TRUE; + params->lines = height; + params->depth = 8; + params->pixels_per_line = width; + params->bytes_per_line = width * 3; + + return SANE_STATUS_GOOD; + +} /* sane_get_parameters */ + +/***********************************************************/ + +SANE_Status +sane_start (SANE_Handle handle) +{ + + SANE_Status status = SANE_STATUS_GOOD; + struct ComBuf buf; + unsigned char sockBuf[SOCK_BUF_SIZE]; + int iHandle, nread; + int errorCheck = 0; + struct sockaddr_in myAddr; + socklen_t addrSize; + fd_set readFds; + struct timeval selTimeVal; + + DBG( 5, "sane_start: %x\n", (int)handle ); + + /* fetch and check scanner index */ + iHandle = (int) handle; + if (!ValidScannerNumber (iHandle)) + return SANE_STATUS_INVAL; + + /* check if we still have oustanding pages of data on this handle */ + if (gOpenScanners[iHandle]->m_imageData.m_used){ + + /* remove empty page */ + PopFromComBuf ( & gOpenScanners[iHandle]->m_pageInfo, sizeof( struct PageInfo ) ); + return SANE_STATUS_GOOD; + + } + + /* determine local IP address */ + addrSize = sizeof (myAddr); + if (getsockname (gOpenScanners[iHandle]->m_udpFd, &myAddr, &addrSize)) + { + DBG (1, "sane_start: Error getting own IP address\n"); + return SANE_STATUS_IO_ERROR; + } + + /* init a buffer for our registration message */ + errorCheck |= InitComBuf (&buf); + + /* build packet */ + errorCheck |= InitPacket (&buf, 1); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-subscribe-user-name", 0x0b, + gOpenScanners[iHandle]->m_regName, + strlen (gOpenScanners[iHandle]->m_regName)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-subscribe-ip-address", 0x0a, + &myAddr.sin_addr, 4); + FinalisePacket (&buf); + + /* check nothing went wrong along the way */ + if (errorCheck) + { + status = SANE_STATUS_NO_MEM; + goto cleanup; + } + + /* send the packet */ + send (gOpenScanners[iHandle]->m_udpFd, buf.m_pBuf, buf.m_used, 0); + + + /* loop until done */ + gOpenScanners[iHandle]->m_bFinish = 0; + while (!gOpenScanners[iHandle]->m_bFinish) + { + + /* prepare select mask */ + FD_ZERO (&readFds); + FD_SET (gOpenScanners[iHandle]->m_udpFd, &readFds); + selTimeVal.tv_sec = 1; + selTimeVal.tv_usec = 0; + + + + DBG (5, "sane_start: waiting for scan signal\n"); + + /* wait again if nothing received */ + if (!select (gOpenScanners[iHandle]->m_udpFd + 1, + &readFds, NULL, NULL, &selTimeVal)) + continue; + + /* read from socket */ + nread = + read (gOpenScanners[iHandle]->m_udpFd, sockBuf, sizeof (sockBuf)); + + if (nread <= 0) + { + DBG (1, "sane_start: read returned %d\n", nread); + break; + } + + /* process the response */ + if (ProcessUdpResponse (sockBuf, nread, gOpenScanners[iHandle])) + { + status = SANE_STATUS_IO_ERROR; + goto cleanup; + } + + } /* while */ + + + /* check whether we were cancelled */ + if ( gOpenScanners[iHandle]->m_bCancelled ) status = SANE_STATUS_CANCELLED; + + +cleanup: + + FreeComBuf (&buf); + + + + + + return status; + +} /* sane_start */ + +/***********************************************************/ + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + + int iHandle = (int) handle; + int dataSize; + struct PageInfo pageInfo; + + DBG( 5, "sane_read: %x (max_length=%d)\n", (int)handle, max_length ); + + *length = 0; + + if (!gOpenScanners[iHandle]) + return SANE_STATUS_INVAL; + + /* check for end of data (no further pages) */ + if ( ( ! gOpenScanners[iHandle]->m_imageData.m_used ) + || ( ! gOpenScanners[iHandle]->m_numPages ) ) + { + /* remove empty page if there are no more cached pages */ + PopFromComBuf ( & gOpenScanners[iHandle]->m_pageInfo, sizeof( struct PageInfo ) ); + + return SANE_STATUS_EOF; + } + + /* fetch page info */ + memcpy( & pageInfo, gOpenScanners[iHandle]->m_pageInfo.m_pBuf, sizeof( pageInfo ) ); + + /* check for end of page data (we still have further cached pages) */ + if ( pageInfo.m_bytesRemaining < 1 ) return SANE_STATUS_EOF; + + /* send the remainder of the current image */ + dataSize = pageInfo.m_bytesRemaining; + + /* unless there's not enough room in the output buffer */ + if (dataSize > max_length) + dataSize = max_length; + + /* update the data sent counters */ + gOpenScanners[iHandle]->m_bytesRead += dataSize; + pageInfo.m_bytesRemaining -= dataSize; + + /* update counter */ + memcpy( gOpenScanners[iHandle]->m_pageInfo.m_pBuf, & pageInfo, sizeof( pageInfo ) ); + + /* check for end of page */ + if ( pageInfo.m_bytesRemaining < 1 ){ + + /* yes, so remove page info */ + gOpenScanners[iHandle]->m_numPages--; + + } /* if */ + + DBG (5, + "sane_read: sending %d bytes, image total %d, %d page bytes remaining, %d total remaining, image: %dx%d\n", + dataSize, gOpenScanners[iHandle]->m_bytesRead, pageInfo.m_bytesRemaining , + gOpenScanners[iHandle]->m_imageData.m_used - dataSize, + pageInfo.m_width, + pageInfo.m_height); + + /* copy the data */ + memcpy (data, gOpenScanners[iHandle]->m_imageData.m_pBuf, dataSize); + if (PopFromComBuf (&gOpenScanners[iHandle]->m_imageData, dataSize)) + return SANE_STATUS_NO_MEM; + + *length = dataSize; + + return SANE_STATUS_GOOD; + +} /* sane_read */ + +/***********************************************************/ + +void +sane_cancel (SANE_Handle handle) +{ + int iHandle = (int) handle; + + DBG( 5, "sane_cancel: %x\n", iHandle ); + + /* signal that bad things are afoot */ + gOpenScanners[iHandle]->m_bFinish = 1; + gOpenScanners[iHandle]->m_bCancelled = 1; + +} /* sane_cancel */ + +/***********************************************************/ + +SANE_Status +sane_set_io_mode (SANE_Handle __sane_unused__ handle, + SANE_Bool __sane_unused__ non_blocking) +{ + + return SANE_STATUS_UNSUPPORTED; + +} /* sane_set_io_mode */ + +/***********************************************************/ + +SANE_Status +sane_get_select_fd (SANE_Handle __sane_unused__ handle, + SANE_Int __sane_unused__ * fd) +{ + + return SANE_STATUS_UNSUPPORTED; + +} /* sane_get_select_fd */ + +/***********************************************************/ + +/* Clears the contents of gKnownDevices and zeros it */ +void +ClearKnownDevices () +{ + + int i; + + for (i = 0; i < MAX_SCANNERS; ++i) + { + + if (gKnownDevices[i]) + { + if (gKnownDevices[i]->name) + free ((void*)(gKnownDevices[i]->name)); + if (gKnownDevices[i]->model) + free ((void*)(gKnownDevices[i]->model)); + free (gKnownDevices[i]); + } + gKnownDevices[i] = NULL; + + } + +} /* ClearKnownDevices */ + +/***********************************************************/ + +/* print hex buffer to debug output */ +void +HexDump (int debugLevel, const unsigned char *buf, size_t bufSize) +{ + + unsigned int i, j; + + char itemBuf[16] = { 0 }, lineBuf[256] = + { + 0}; + + if (DBG_LEVEL < debugLevel) + return; + + for (i = 0; i < bufSize; ++i) + { + + if (!(i % 16)) + sprintf (lineBuf, "%04x: ", (unsigned int) (buf + i)); + + sprintf (itemBuf, "%02x ", (const unsigned int) buf[i]); + + strncat (lineBuf, itemBuf, sizeof (lineBuf)); + + if ((i + 1) % 16) + continue; + + /* print string equivalent */ + for (j = i - 15; j <= i; ++j) + { + + if ((buf[j] >= 0x20) && (!(buf[j] & 0x80))) + { + sprintf (itemBuf, "%c", buf[j]); + } + else + { + sprintf (itemBuf, "."); + } + strncat (lineBuf, itemBuf, sizeof (lineBuf)); + + } /* for j */ + + DBG (debugLevel, "%s\n", lineBuf); + lineBuf[0] = 0; + + } /* for i */ + + if (i % 16) + { + + for (j = (i % 16); j < 16; ++j) + { + strncat (lineBuf, " ", sizeof (lineBuf)); + } + for (j = 1 + i - ((i + 1) % 16); j < i; ++j) + { + if ((buf[j] >= 0x20) && (!(buf[j] & 0x80))) + { + sprintf (itemBuf, "%c", buf[j]); + } + else + { + strcpy (itemBuf, "."); + } + strncat (lineBuf, itemBuf, sizeof (lineBuf)); + } + + DBG (debugLevel, "%s\n", lineBuf); + + } + +} /* HexDump */ + +/***********************************************************/ + +/* initialise a ComBuf struct + \return 0 on success, >0 on failure +*/ +int +InitComBuf (struct ComBuf *pBuf) +{ + + memset (pBuf, 0, sizeof (struct ComBuf)); + + pBuf->m_pBuf = malloc (INITIAL_COM_BUF_SIZE); + if (!pBuf->m_pBuf) + return 1; + + pBuf->m_capacity = INITIAL_COM_BUF_SIZE; + pBuf->m_used = 0; + + return 0; + +} /* InitComBuf */ + +/***********************************************************/ + +/* free a ComBuf struct */ +void +FreeComBuf (struct ComBuf *pBuf) +{ + + if (pBuf->m_pBuf) + free (pBuf->m_pBuf); + memset (pBuf, 0, sizeof (struct ComBuf)); + +} /* FreeComBuf */ + +/***********************************************************/ + +/* add data to a ComBuf struct + \return 0 on success, >0 on failure + \note If pData is NULL then buffer size will be increased but no copying will take place + \note In case of failure pBuf will be released using FreeComBuf +*/ +int +AppendToComBuf (struct ComBuf *pBuf, const unsigned char *pData, + size_t datSize) +{ + + size_t newSize; + + /* check we have enough space */ + if (pBuf->m_used + datSize > pBuf->m_capacity) + { + + /* nope - allocate some more */ + newSize = pBuf->m_used + datSize + INITIAL_COM_BUF_SIZE; + pBuf->m_pBuf = realloc (pBuf->m_pBuf, newSize); + if (!pBuf->m_pBuf) + { + DBG (1, "AppendToComBuf: memory allocation error"); + FreeComBuf (pBuf); + return (1); + } + pBuf->m_capacity = newSize; + } /* if */ + + /* add data */ + if (pData) + memcpy (pBuf->m_pBuf + pBuf->m_used, pData, datSize); + pBuf->m_used += datSize; + + return 0; + +} /* AppendToComBuf */ + +/***********************************************************/ + +/* append message to a packet + \return 0 if ok, 1 if bad */ +int +AppendMessageToPacket (struct ComBuf *pBuf, /* packet to which to append */ + char messageType, /* type of message */ + char *messageName, /* name of message */ + char valueType, /* type of value */ + void *pValue, /* pointer to value */ + size_t valueLen /* length of value (bytes) */ + ) +{ + + unsigned short slen; + + /* message type */ + AppendToComBuf (pBuf, (void *) &messageType, 1); + + /* message length */ + slen = htons (strlen (messageName)); + AppendToComBuf (pBuf, (void *) &slen, 2); + + /* and name */ + AppendToComBuf (pBuf, (void *) messageName, strlen (messageName)); + + /* and value type */ + AppendToComBuf (pBuf, (void *) &valueType, 1); + + /* value length */ + slen = htons (valueLen); + AppendToComBuf (pBuf, (void *) &slen, 2); + + /* and value */ + return (AppendToComBuf (pBuf, (void *) pValue, valueLen)); + +} /* AppendMessageToPacket */ + +/***********************************************************/ + +/* Initialise a packet + \param pBuf : An initialise ComBuf + \param type : either 0x01 ("normal" ) or 0x02 ("reply" ) + \return 0 on success, >0 otherwise +*/ +int +InitPacket (struct ComBuf *pBuf, char type) +{ + + char header[8] = { 2, 0, 0, 2, 0, 0, 0, 0 }; + + header[2] = type; + + /* reset size */ + pBuf->m_used = 0; + + /* add header */ + return (AppendToComBuf (pBuf, (void *) &header, 8)); + +} /* InitPacket */ + +/***********************************************************/ + +/* write length data to packet header +*/ +void +FinalisePacket (struct ComBuf *pBuf) +{ + + /* sanity check */ + if (pBuf->m_used < 8) + return; + + /* set the size */ + *((unsigned short *) (pBuf->m_pBuf + 6)) = htons (pBuf->m_used - 8); + + DBG (20, "FinalisePacket: outgoing packet:\n"); + HexDump (20, pBuf->m_pBuf, pBuf->m_used); + +} /* FinalisePacket */ + +/***********************************************************/ + +/* \return 1 if message is complete, 0 otherwise */ +int +MessageIsComplete (unsigned char *pData, size_t size) +{ + unsigned short dataSize; + + /* sanity check */ + if (size < 8) + return 0; + + /* :NOTE: we can't just cast to a short as data may not be aligned */ + dataSize = (((unsigned short) pData[6]) << 8) | pData[7]; + + DBG (20, "MessageIsComplete: data size = %d\n", dataSize); + + if (size >= (size_t) (dataSize + 8)) + return 1; + else + return 0; + +} /* MessageIsComplete */ + +/***********************************************************/ + +/* process a registration broadcast response + \return SANE_Device pointer on success (caller frees), NULL on failure +*/ +SANE_Device * +ProcessFindResponse (unsigned char *pData, size_t size) +{ + + SANE_Device *pDevice = NULL; + unsigned short messageSize, nameSize, valueSize; + unsigned char *pItem, *pEnd, *pValue; + char printerName[256] = { 0 }; + char printerModel[256] = "1600n"; + char *pModel, *pName; + + + DBG (10, "ProcessFindResponse: processing %d bytes, pData=%p\n", size, + pData); + + /* check we have a complete packet */ + if (!MessageIsComplete (pData, size)) + { + DBG (1, "ProcessFindResponse: Ignoring incomplete packet\n"); + return NULL; + } + + /* extract data size */ + messageSize = (((unsigned short) (pData[6])) << 8) | pData[7]; + + /* loop through items in message */ + pItem = pData + 8; + pEnd = pItem + messageSize; + while (pItem < pEnd) + { + + pItem++; + nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1]; + pItem += 2; + pName = (char *) pItem; + + pItem += nameSize; + + pItem++; + valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1]; + pItem += 2; + + pValue = pItem; + + pItem += valueSize; + + /* process the item */ + if (!strncmp ("std-scan-discovery-ip", pName, nameSize)) + { + + snprintf (printerName, sizeof (printerName), "%d.%d.%d.%d", + (int) pValue[0], + (int) pValue[1], (int) pValue[2], (int) pValue[3]); + DBG (2, "%s\n", printerName); + + } + else if (!strncmp ("std-scan-discovery-model-name", pName, nameSize)) + { + + memset (printerModel, 0, sizeof (printerModel)); + if (valueSize > (sizeof (printerModel) - 1)) + valueSize = sizeof (printerModel) - 1; + memcpy (printerModel, pValue, valueSize); + DBG (2, "std-scan-discovery-model-name: %s\n", printerModel); + + } + + } /* while pItem */ + + /* just in case nothing sensible was found */ + if (!strlen (printerName)) + return NULL; + + pDevice = malloc (sizeof (SANE_Device)); + if (!pDevice) + { + DBG (1, "ProcessFindResponse: memory allocation failure\n"); + return NULL; + } + + /* knock off "Dell " from start of model name */ + pModel = printerModel; + if (!strncmp (pModel, "Dell ", 5)) + pModel += 5; + + pDevice->name = strdup (printerName); + pDevice->vendor = "Dell"; + pDevice->model = strdup (pModel); + pDevice->type = "multi-function peripheral"; + + return pDevice; + +} /* ProcessFindResponse */ + +/***********************************************************/ + +/* frees a scanner state struct stored in gOpenScanners */ +void +FreeScannerState (int iHandle) +{ + + /* check range etc */ + if (!ValidScannerNumber (iHandle)) + return; + + /* close UDP handle */ + if (gOpenScanners[iHandle]->m_udpFd) + close (gOpenScanners[iHandle]->m_udpFd); + + /* free m_buf */ + FreeComBuf (&gOpenScanners[iHandle]->m_buf); + + /* free m_imageData */ + FreeComBuf (&gOpenScanners[iHandle]->m_imageData); + + /* free the struct */ + free (gOpenScanners[iHandle]); + + /* set pointer to NULL */ + gOpenScanners[iHandle] = NULL; + +} /* FreeScannerState */ + +/***********************************************************/ + +/* \return 1 if iHandle is a valid member of gOpenScanners, 0 otherwise */ +int +ValidScannerNumber (int iHandle) +{ + /* check range */ + if ((iHandle < 0) || (iHandle >= MAX_SCANNERS)) + { + DBG (1, "ValidScannerNumber: invalid scanner index %d", iHandle); + return 0; + } + + /* check non-NULL pointer */ + if (!gOpenScanners[iHandle]) + { + DBG (1, "ValidScannerNumber: NULL scanner struct %d", iHandle); + return 0; + } + + /* OK */ + return 1; + +} /* ValidScannerNumber */ + +/***********************************************************/ + +/* process UDP responses + \return 0 in success, >0 otherwise */ +static int +ProcessUdpResponse (unsigned char *pData, size_t size, + struct ScannerState *pState) +{ + + unsigned short messageSize, nameSize, valueSize; + unsigned char *pItem, *pEnd, *pValue; + char sockBuf[SOCK_BUF_SIZE], *pName; + struct ComBuf tcpBuf; + int nread; + unsigned int numUsed; + + HexDump (15, pData, size); + + DBG (10, "ProcessUdpResponse: processing %d bytes, pData=%p\n", size, + pData); + + /* check we have a complete packet */ + if (!MessageIsComplete (pData, size)) + { + DBG (1, "ProcessUdpResponse: Ignoring incomplete packet\n"); + return 1; + } + + /* init a com buf for use in tcp communication */ + InitComBuf (&tcpBuf); + + /* extract data size */ + messageSize = (((unsigned short) (pData[6])) << 8) | pData[7]; + + /* loop through items in message */ + pItem = pData + 8; + pEnd = pItem + messageSize; + while (pItem < pEnd) + { + + pItem++; + nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1]; + pItem += 2; + pName = (char *) pItem; + + pItem += nameSize; + + pItem++; + valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1]; + pItem += 2; + + pValue = pItem; + + pItem += valueSize; + + /* process the item */ + if (!strncmp ("std-scan-request-tcp-connection", pName, nameSize)) + { + + /* open TCP socket to scanner */ + if (!(pState->m_tcpFd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP))) + { + DBG (1, "ProcessUdpResponse: error opening TCP socket\n"); + return 2; + } + if (connect (pState->m_tcpFd, + (struct sockaddr *) &pState->m_sockAddr, + sizeof (pState->m_sockAddr))) + { + DBG (1, + "ProcessUdpResponse: error connecting to scanner TCP port\n"); + goto cleanup; + } + + DBG (1, "ProcessUdpResponse: opened TCP connection to scanner\n"); + + /* clear read buf */ + tcpBuf.m_used = 0; + + /* TCP read loop */ + while (1) + { + + nread = read (pState->m_tcpFd, sockBuf, sizeof (sockBuf)); + + if (nread <= 0) + { + DBG (1, "ProcessUdpResponse: TCP read returned %d\n", + nread); + break; + } + + /* append message to buffer */ + if (AppendToComBuf (&tcpBuf, (unsigned char *) sockBuf, nread)) + goto cleanup; + + /* process all available responses */ + while (tcpBuf.m_used) + { + + /* note the buffer size before the call */ + numUsed = tcpBuf.m_used; + + /* process the response */ + if (ProcessTcpResponse (pState, &tcpBuf)) + goto cleanup; + + /* if the buffer size has not changed then assume no more processing is possible */ + if (numUsed == tcpBuf.m_used) + break; + + } /* while */ + + } /* while */ + + close (pState->m_tcpFd); + DBG (1, "ProcessUdpResponse: closed TCP connection to scanner\n"); + + /* signal end of session */ + pState->m_bFinish = 1; + + } /* if */ + + } /* while pItem */ + + return 0; + +cleanup: + + FreeComBuf (&tcpBuf); + close (pState->m_tcpFd); + return 3; + +} /* ProcessUdpResponse */ + +/***********************************************************/ + +/* process TCP responses, \return 0 in success, >0 otherwise */ +int +ProcessTcpResponse (struct ScannerState *pState, struct ComBuf *pTcpBuf) +{ + + struct ComBuf buf; + unsigned short messageSize = 0, nameSize, valueSize, dataChunkSize; + unsigned char *pItem, *pEnd, *pValue; + unsigned char *pData = pTcpBuf->m_pBuf; + char *pName; + unsigned int uiVal; + int errorCheck = 0; + int bProcessImage = 0; + + DBG (10, "ProcessTcpResponse: processing %d bytes, pData=%p\n", + pTcpBuf->m_used, pData); + HexDump (15, pData, pTcpBuf->m_used); + + /* if message not complete then wait for more to arrive */ + if (!MessageIsComplete (pData, pTcpBuf->m_used)) + { + DBG (10, "ProcessTcpResponse: incomplete message, returning\n"); + return 0; + } + + /* init a buffer for our outbound messages */ + if (InitComBuf (&buf)) + { + errorCheck |= 1; + goto cleanup; + } + + /* extract data size */ + messageSize = (((unsigned short) (pData[6])) << 8) | pData[7]; + + /* loop through items in message */ + pItem = pData + 8; + pEnd = pItem + messageSize; + while (pItem < pEnd) + { + + pItem++; + nameSize = (((unsigned short) pItem[0]) << 8) | pItem[1]; + pItem += 2; + pName = (char *) pItem; + + pItem += nameSize; + + pItem++; + valueSize = (((unsigned short) pItem[0]) << 8) | pItem[1]; + pItem += 2; + + pValue = pItem; + + pItem += valueSize; + + /* process the item */ + if (!strncmp ("std-scan-session-open", pName, nameSize)) + { + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-session-open-response", 0x05, + &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + } + else if (!strncmp ("std-scan-getclientpref", pName, nameSize)) + { + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-x1", + 0x05, &uiVal, sizeof (uiVal)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-x2", + 0x05, &uiVal, sizeof (uiVal)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-y1", + 0x05, &uiVal, sizeof (uiVal)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-getclientpref-y2", + 0x05, &uiVal, sizeof (uiVal)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-xresolution", 0x04, + &pState->m_xres, sizeof (pState->m_xres)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-yresolution", 0x04, + &pState->m_yres, sizeof (pState->m_yres)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-image-composition", + 0x06, &pState->m_composition, + sizeof (pState->m_composition)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-brightness", 0x02, + &pState->m_brightness, + sizeof (pState->m_brightness)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-image-compression", + 0x06, &pState->m_compression, + sizeof (pState->m_compression)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-file-type", 0x06, + &pState->m_fileType, + sizeof (pState->m_fileType)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-paper-size-detect", + 0x06, &uiVal, sizeof (uiVal)); + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-getclientpref-paper-scanner-type", + 0x06, &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + } + else if (!strncmp ("std-scan-document-start", pName, nameSize)) + { + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-document-start-response", 0x05, + &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + } + else if (!strncmp ("std-scan-document-file-type", pName, nameSize)) + { + + memcpy (&pState->m_fileType, pValue, sizeof (pState->m_fileType)); + DBG (5, "File type: %x\n", ntohl (pState->m_fileType)); + + } + else + if (!strncmp ("std-scan-document-image-compression", pName, nameSize)) + { + + memcpy (&pState->m_compression, pValue, + sizeof (pState->m_compression)); + DBG (5, "Compression: %x\n", ntohl (pState->m_compression)); + + } + else if (!strncmp ("std-scan-document-xresolution", pName, nameSize)) + { + + memcpy (&pState->m_xres, pValue, sizeof (pState->m_xres)); + DBG (5, "X resolution: %d\n", ntohs (pState->m_xres)); + + } + else if (!strncmp ("std-scan-document-yresolution", pName, nameSize)) + { + + memcpy (&pState->m_yres, pValue, sizeof (pState->m_yres)); + DBG (5, "Y resolution: %d\n", ntohs (pState->m_yres)); + + } + else if (!strncmp ("std-scan-page-widthpixel", pName, nameSize)) + { + if (1 || !pState->m_pixelWidth) + { + memcpy (&pState->m_pixelWidth, pValue, + sizeof (pState->m_pixelWidth)); + DBG (5, "Width: %d\n", ntohl (pState->m_pixelWidth)); + } + else + { + DBG (5, "Ignoring width (already have a value)\n"); + } + + } + else if (!strncmp ("std-scan-page-heightpixel", pName, nameSize)) + { + + if (1 || !pState->m_pixelHeight) + { + memcpy (&pState->m_pixelHeight, pValue, + sizeof (pState->m_pixelHeight)); + DBG (5, "Height: %d\n", ntohl (pState->m_pixelHeight)); + } + else + { + DBG (5, "Ignoring height (already have a value)\n"); + } + } + else if (!strncmp ("std-scan-page-start", pName, nameSize)) + { + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-page-start-response", + 0x05, &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + /* write out any pre-existing page data + errorCheck |= ProcessPageData (pState); + */ + + /* reset the data buffer ready to store a new page */ + pState->m_buf.m_used = 0; + + /* init current page size */ + pState->m_currentPageBytes = 0; + + pState->m_pixelWidth = 0; + pState->m_pixelHeight = 0; + + } + else if (!strncmp ("std-scan-page-end", pName, nameSize)) + { + + bProcessImage = 1; + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, "std-scan-page-end-response", + 0x05, &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + } + else if (!strncmp ("std-scan-document-end", pName, nameSize)) + { + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-document-end-response", 0x05, + &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + /* write out any pre-existing page data */ + /* errorCheck |= ProcessPageData (pState); */ + + /* reset the data buffer ready to store a new page */ + pState->m_buf.m_used = 0; + + } + else if (!strncmp ("std-scan-session-end", pName, nameSize)) + { + + errorCheck |= InitPacket (&buf, 0x02); + uiVal = 0; + errorCheck |= + AppendMessageToPacket (&buf, 0x22, + "std-scan-session-end-response", 0x05, + &uiVal, sizeof (uiVal)); + FinalisePacket (&buf); + send (pState->m_tcpFd, buf.m_pBuf, buf.m_used, 0); + + /* initialise a shutodwn of the socket */ + shutdown (pState->m_tcpFd, SHUT_RDWR); + + } + else if (!strncmp ("std-scan-scandata-error", pName, nameSize)) + { + + /* determine the size of data in this chunk */ + dataChunkSize = (pItem[6] << 8) + pItem[7]; + + pItem += 8; + + DBG (10, "Reading %d bytes of scan data\n", dataChunkSize); + + /* append message to buffer */ + errorCheck |= AppendToComBuf (&pState->m_buf, pItem, dataChunkSize); + + pItem += dataChunkSize; + + DBG (10, "Accumulated %d bytes of scan data so far\n", + pState->m_buf.m_used); + + } /* if */ + + } /* while */ + + + /* process page data if required */ + if ( bProcessImage ) errorCheck |= ProcessPageData (pState); + +cleanup: + + /* remove processed data (including 8 byte header) from start of tcp buffer */ + PopFromComBuf (pTcpBuf, messageSize + 8); + + /* free com buf */ + FreeComBuf (&buf); + + return errorCheck; + +} /* ProcessTcpResponse */ + +/***********************************************************/ + +/* remove data from the front of a ComBuf struct + \return 0 if sucessful, >0 otherwise +*/ +int +PopFromComBuf (struct ComBuf *pBuf, size_t datSize) +{ + + /* check if we're trying to remove more data than is present */ + if (datSize > pBuf->m_used) + { + pBuf->m_used = 0; + return 1; + } + + /* check easy cases */ + if ((!datSize) || (datSize == pBuf->m_used)) + { + pBuf->m_used -= datSize; + return 0; + } + + /* move remaining memory contents to start */ + memmove (pBuf->m_pBuf, pBuf->m_pBuf + datSize, pBuf->m_used - datSize); + + pBuf->m_used -= datSize; + return 0; + +} /* PopFromComBuf */ + +/***********************************************************/ + +/* Process the data from a single scanned page, \return 0 in success, >0 otherwise */ +int +ProcessPageData (struct ScannerState *pState) +{ + + char tempFilename[TMP_MAX] = "scan.dat"; + FILE *fTmp; + int fdTmp; + struct jpeg_source_mgr jpegSrcMgr; + struct JpegDataDecompState jpegCinfo; + struct jpeg_error_mgr jpegErr; + int numPixels, iPixel, width, height, scanLineSize, imageBytes; + int ret = 0; + struct PageInfo pageInfo; + + JSAMPLE *pJpegLine = NULL; + uint32 *pTiffRgba = NULL; + unsigned char *pOut; + char tiffErrBuf[1024]; + + TIFF *pTiff = NULL; + + /* If there's no data then there's nothing to write */ + if (!pState->m_buf.m_used) + return 0; + + DBG (1, "ProcessPageData: Got compression %x\n", + ntohl (pState->m_compression)); + + switch (ntohl (pState->m_compression)) + { + + case 0x20: + /* decode as JPEG if appropriate */ + { + + jpegSrcMgr.resync_to_restart = jpeg_resync_to_restart; + jpegSrcMgr.init_source = JpegDecompInitSource; + jpegSrcMgr.fill_input_buffer = JpegDecompFillInputBuffer; + jpegSrcMgr.skip_input_data = JpegDecompSkipInputData; + jpegSrcMgr.term_source = JpegDecompTermSource; + + jpegCinfo.m_cinfo.err = jpeg_std_error (&jpegErr); + jpeg_create_decompress (&jpegCinfo.m_cinfo); + jpegCinfo.m_cinfo.src = &jpegSrcMgr; + jpegCinfo.m_bytesRemaining = pState->m_buf.m_used; + jpegCinfo.m_pData = pState->m_buf.m_pBuf; + + jpeg_read_header (&jpegCinfo.m_cinfo, TRUE); + jpeg_start_decompress (&jpegCinfo.m_cinfo); + + /* allocate space for a single scanline */ + scanLineSize = jpegCinfo.m_cinfo.output_width + * jpegCinfo.m_cinfo.output_components; + DBG (1, "ProcessPageData: image dimensions: %d x %d, line size: %d\n", + jpegCinfo.m_cinfo.output_width, + jpegCinfo.m_cinfo.output_height, scanLineSize); + + pJpegLine = calloc (scanLineSize, sizeof (JSAMPLE)); + if (!pJpegLine) + { + DBG (1, "ProcessPageData: memory allocation error\n"); + ret = 1; + goto JPEG_CLEANUP; + } /* if */ + + /* note dimensions - may be different from those previously reported */ + pState->m_pixelWidth = htonl (jpegCinfo.m_cinfo.output_width); + pState->m_pixelHeight = htonl (jpegCinfo.m_cinfo.output_height); + + /* decode scanlines */ + while (jpegCinfo.m_cinfo.output_scanline + < jpegCinfo.m_cinfo.output_height) + { + DBG (20, "Reading scanline %d of %d\n", + jpegCinfo.m_cinfo.output_scanline, + jpegCinfo.m_cinfo.output_height); + + /* read scanline */ + jpeg_read_scanlines (&jpegCinfo.m_cinfo, &pJpegLine, 1); + + /* append to output buffer */ + ret |= AppendToComBuf (&pState->m_imageData, + pJpegLine, scanLineSize); + + } /* while */ + + /* update info for this page */ + pageInfo.m_width = jpegCinfo.m_cinfo.output_width; + pageInfo.m_height = jpegCinfo.m_cinfo.output_height; + pageInfo.m_totalSize = pageInfo.m_width * pageInfo.m_height * 3; + pageInfo.m_bytesRemaining = pageInfo.m_totalSize; + + DBG( 1, "Process page data: page %d: JPEG image: %d x %d, %d bytes\n", + pState->m_numPages, pageInfo.m_width, pageInfo.m_height, pageInfo.m_totalSize ); + + ret |= AppendToComBuf( & pState->m_pageInfo, & pageInfo, sizeof( pageInfo ) ); + ++( pState->m_numPages ); + + JPEG_CLEANUP: + jpeg_finish_decompress (&jpegCinfo.m_cinfo); + jpeg_destroy_decompress (&jpegCinfo.m_cinfo); + + if (pJpegLine) + free (pJpegLine); + + return ret; + } /* case JPEG */ + + case 0x08: + /* CCITT Group 4 Fax data */ + { + /* get a temp file + :TODO: 2006-04-18: Use TIFFClientOpen and do everything in RAM + */ + fTmp = tmpfile (); + fdTmp = fileno (fTmp); + + pTiff = TIFFFdOpen (fdTmp, "tempfile", "w"); + if (!pTiff) + { + DBG (1, "ProcessPageData: Error opening temp TIFF file"); + ret = SANE_STATUS_IO_ERROR; + goto TIFF_CLEANUP; + } + + /* create a TIFF file */ + width = ntohl (pState->m_pixelWidth); + height = ntohl (pState->m_pixelHeight); + TIFFSetField (pTiff, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField (pTiff, TIFFTAG_IMAGELENGTH, height); + TIFFSetField (pTiff, TIFFTAG_BITSPERSAMPLE, 1); + TIFFSetField (pTiff, TIFFTAG_PHOTOMETRIC, 0); /* 0 is white */ + TIFFSetField (pTiff, TIFFTAG_COMPRESSION, 4); /* CCITT Group 4 */ + + TIFFWriteRawStrip (pTiff, 0, pState->m_buf.m_pBuf, + pState->m_buf.m_used); + + if (0 > TIFFRGBAImageOK (pTiff, tiffErrBuf)) + { + DBG (1, "ProcessPageData: %s\n", tiffErrBuf); + ret = SANE_STATUS_IO_ERROR; + goto TIFF_CLEANUP; + } + + /* allocate space for RGBA representation of image */ + numPixels = height * width; + DBG (20, "ProcessPageData: num TIFF RGBA pixels: %d\n", numPixels); + if (!(pTiffRgba = calloc (numPixels, sizeof (u_long)))) + { + ret = SANE_STATUS_NO_MEM; + goto TIFF_CLEANUP; + } + + /* make space in image buffer to store the results */ + imageBytes = width * height * 3; + ret |= AppendToComBuf (&pState->m_imageData, NULL, imageBytes); + if (ret) + goto TIFF_CLEANUP; + + /* get a pointer to the start of the output data */ + pOut = pState->m_imageData.m_pBuf + + pState->m_imageData.m_used - imageBytes; + + /* read RGBA image */ + DBG (20, "ProcessPageData: setting up read buffer\n"); + TIFFReadBufferSetup (pTiff, NULL, width * height * sizeof (u_long)); + DBG (20, "ProcessPageData: reading RGBA data\n"); + TIFFReadRGBAImageOriented (pTiff, width, height, pTiffRgba, + ORIENTATION_TOPLEFT, 0); + + /* loop over pixels */ + for (iPixel = 0; iPixel < numPixels; ++iPixel) + { + + *(pOut++) = TIFFGetR (pTiffRgba[iPixel]); + *(pOut++) = TIFFGetG (pTiffRgba[iPixel]); + *(pOut++) = TIFFGetB (pTiffRgba[iPixel]); + + } /* for iRow */ + + + + /* update info for this page */ + pageInfo.m_width = width; + pageInfo.m_height = height; + pageInfo.m_totalSize = pageInfo.m_width * pageInfo.m_height * 3; + pageInfo.m_bytesRemaining = pageInfo.m_totalSize; + + DBG( 1, "Process page data: page %d: TIFF image: %d x %d, %d bytes\n", + pState->m_numPages, width, height, pageInfo.m_totalSize ); + + ret |= AppendToComBuf( & pState->m_pageInfo, & pageInfo, sizeof( pageInfo ) ); + ++( pState->m_numPages ); + + TIFF_CLEANUP: + if (pTiff) + TIFFClose (pTiff); + if (fTmp) + fclose (fTmp); + if (pTiffRgba) + free (pTiffRgba); + return ret; + + } /* case CCITT */ + default: + /* this is not expected or very useful */ + { + tmpnam (tempFilename); + DBG (1, "ProcessPageData: Writing scan data to %s\n", tempFilename); + fTmp = fopen (tempFilename, "w"); + fwrite (pState->m_buf.m_pBuf, pState->m_buf.m_used, 1, fTmp); + fclose (fTmp); + ret = SANE_STATUS_IO_ERROR; + } + } /* switch */ + + return ret; + +} /* ProcessPageData */ + +/***********************************************************/ + +void +JpegDecompInitSource (j_decompress_ptr cinfo) +/* Libjpeg decompression interface */ +{ + cinfo->src->bytes_in_buffer = 0; + +} /* JpegDecompInitSource */ + +/***********************************************************/ + +boolean +JpegDecompFillInputBuffer (j_decompress_ptr cinfo) +/* Libjpeg decompression interface */ +{ + struct JpegDataDecompState *pState = (struct JpegDataDecompState *) cinfo; + static const unsigned char eoiByte[] = { + 0xFF, JPEG_EOI + }; + + DBG (10, "JpegDecompFillInputBuffer: bytes remaining: %d\n", + pState->m_bytesRemaining); + + if (!pState->m_bytesRemaining) + { + + /* no input data available so return dummy data */ + cinfo->src->bytes_in_buffer = 2; + cinfo->src->next_input_byte = (const JOCTET *) eoiByte; + + } + else + { + + /* point to data */ + cinfo->src->bytes_in_buffer = pState->m_bytesRemaining; + cinfo->src->next_input_byte = (const JOCTET *) pState->m_pData; + + /* note that data is now gone */ + pState->m_bytesRemaining = 0; + + } /* if */ + + return TRUE; + +} /* JpegDecompFillInputBuffer */ + +/***********************************************************/ + +void +JpegDecompSkipInputData (j_decompress_ptr cinfo, long numBytes) +/* Libjpeg decompression interface */ +{ + DBG (10, "JpegDecompSkipInputData: skipping %ld bytes\n", numBytes); + + cinfo->src->bytes_in_buffer -= numBytes; + cinfo->src->next_input_byte += numBytes; + +} /* JpegDecompSkipInputData */ + +/***********************************************************/ + +void +JpegDecompTermSource (j_decompress_ptr __sane_unused__ cinfo) +/* Libjpeg decompression interface */ +{ + /* nothing to do */ + +} /* JpegDecompTermSource */ + +/***********************************************************/ diff -uNr sane-backends-1.0.17/backend/dll.c sane-backends-1.0.18/backend/dll.c --- sane-backends-1.0.17/backend/dll.c 2005-07-15 20:51:33.000000000 +0200 +++ sane-backends-1.0.18/backend/dll.c 2006-02-04 17:36:05.000000000 +0100 @@ -44,7 +44,7 @@ /* Please increase version number with every change (don't forget to update dll.desc) */ -#define DLL_VERSION "1.0.11" +#define DLL_VERSION "1.0.12" #ifdef _AIX # include "lalloca.h" /* MUST come first for AIX! */ @@ -108,6 +108,8 @@ #if defined(HAVE_OS2_H) # define DIR_SEP ";" +#elif defined(__CYGWIN__) +# define DIR_SEP ":" #elif defined(HAVE_WINDOWS_H) # define DIR_SEP ";" #else @@ -322,13 +324,13 @@ static SANE_Status load (struct backend *be) { - // use BeOS kernel function to load scanner addons from ~/config/add-ons/SANE/ + /* use BeOS kernel function to load scanner addons from ~/config/add-ons/SANE */ char path[PATH_MAX]; image_id id = -1; int i, w; directory_which which[3] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_BEOS_ADDONS_DIRECTORY }; - // look for config files in SANE/conf + /* look for config files in SANE/conf */ for (w = 0; (w < 3) && (id < 0) && (find_directory(which[w],0,true,path,PATH_MAX) == 0); w++) { strcat(path,"/SANE/"); @@ -382,6 +384,7 @@ # define PREFIX "libsane-" # ifdef __hpux # define POSTFIX ".sl.%u" +# define ALT_POSTFIX ".so.%u" #elif defined (HAVE_WINDOWS_H) # undef PREFIX # define PREFIX "cygsane-" @@ -461,6 +464,18 @@ break; DBG (4, "load: couldn't open `%s' (%s)\n", libname, strerror (errno)); +#ifdef ALT_POSTFIX + /* Some platforms have two ways of storing their libraries, try both + postfixes */ + snprintf (libname, sizeof (libname), "%s/" PREFIX "%s" ALT_POSTFIX, + dir, be->name, V_MAJOR); + DBG (4, "load: trying to load `%s'\n", libname); + fp = fopen (libname, "r"); + if (fp) + break; + DBG (4, "load: couldn't open `%s' (%s)\n", libname, strerror (errno)); +#endif + dir = strsep (&src, DIR_SEP); } if (orig_src) @@ -801,8 +816,8 @@ fclose (fp); #else - // no ugly config files, just get scanners from their ~/config/add-ons/SANE - // look for drivers + /* no ugly config files, just get scanners from their ~/config/add-ons/SANE */ + /* look for drivers */ for (i = 0; i < 3; i++) { if (find_directory(which[i],0,true,path,1024) < B_OK) @@ -844,8 +859,8 @@ (*(op_exit_t)be->op[OP_EXIT]) (); } #ifdef __BEOS__ - // use BeOS kernel functions to unload add-ons - if(be->handle) unload_add_on((image_id)be->handle); + /* use BeOS kernel functions to unload add-ons */ + if(be->handle) unload_add_on((image_id)be->handle); #else #ifdef HAVE_DLL diff -uNr sane-backends-1.0.17/backend/dll.conf sane-backends-1.0.18/backend/dll.conf --- sane-backends-1.0.17/backend/dll.conf 2005-10-16 18:42:31.000000000 +0200 +++ sane-backends-1.0.18/backend/dll.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,74 +0,0 @@ -# enable the next line if you want to allow access through the network: -net -abaton -agfafocus -apple -avision -artec -artec_eplus48u -as6e -bh -canon -canon630u -#canon_pp -coolscan -coolscan2 -#dc25 -#dc210 -#dc240 -dmc -epson -fujitsu -#gphoto2 -genesys -gt68xx -hp -hpsj5s -hp4200 -hp5400 -ibm -leo -lexmark -ma1509 -matsushita -microtek -microtek2 -mustek -#mustek_pp -mustek_usb -mustek_usb2 -nec -niash -pie -pint -plustek -#plustek_pp -#pnm -qcam -ricoh -s9036 -sceptre -sharp -sm3600 -sm3840 -snapscan -sp15c -#st400 -tamarack -teco1 -teco2 -teco3 -#test -u12 -umax -#umax_pp -umax1220u -v4l -# -# The following backends are not included in the sane-backends distribution -# If you want to use them, download them from their webpages and read their -# documentation -# -# HP OfficeJet backend homepage: http://hpoj.sf.net/ -# Uncomment the following line if hpoj is installed: -#hpoj diff -uNr sane-backends-1.0.17/backend/dll.conf.in sane-backends-1.0.18/backend/dll.conf.in --- sane-backends-1.0.17/backend/dll.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/dll.conf.in 2006-06-06 18:32:02.000000000 +0200 @@ -0,0 +1,78 @@ +# enable the next line if you want to allow access through the network: +net +abaton +agfafocus +apple +avision +artec +artec_eplus48u +as6e +bh +canon +canon630u +#canon_pp +coolscan +coolscan2 +#dc25 +#dc210 +#dc240 +dell1600n_net +dmc +epson +fujitsu +#gphoto2 +genesys +gt68xx +hp +hpsj5s +hp3500 +hp4200 +hp5400 +ibm +leo +lexmark +ma1509 +matsushita +microtek +microtek2 +mustek +#mustek_pp +mustek_usb +mustek_usb2 +nec +niash +pie +pint +pixma +plustek +#plustek_pp +#pnm +qcam +ricoh +s9036 +sceptre +sharp +sm3600 +sm3840 +snapscan +sp15c +#st400 +#stv680 +tamarack +teco1 +teco2 +teco3 +#test +u12 +umax +#umax_pp +umax1220u +v4l +# +# The following backends are not included in the sane-backends distribution +# If you want to use them, download them from their webpages and read their +# documentation +# +# HP OfficeJet backend homepage: http://hpoj.sf.net/ +# Uncomment the following line if hpoj is installed: +#hpoj diff -uNr sane-backends-1.0.17/backend/dmc.conf sane-backends-1.0.18/backend/dmc.conf --- sane-backends-1.0.17/backend/dmc.conf 1999-08-09 20:05:54.000000000 +0200 +++ sane-backends-1.0.18/backend/dmc.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -/dev/camera diff -uNr sane-backends-1.0.17/backend/dmc.conf.in sane-backends-1.0.18/backend/dmc.conf.in --- sane-backends-1.0.17/backend/dmc.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/dmc.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1 @@ +/dev/camera diff -uNr sane-backends-1.0.17/backend/epson.c sane-backends-1.0.18/backend/epson.c --- sane-backends-1.0.17/backend/epson.c 2005-01-10 20:23:07.000000000 +0100 +++ sane-backends-1.0.18/backend/epson.c 2006-06-11 22:32:30.000000000 +0200 @@ -14,10 +14,11 @@ Copyright (C) 2000 Mike Porter (mjp) Copyright (C) 2003 EPSON KOWA Corporation Copyright (C) 1999-2005 Karl Heinz Kremer + Copyright (C) 2006 Claus Boje */ -#define SANE_EPSON_VERSION "SANE Epson Backend v0.2.45 - 2000-01-09" -#define SANE_EPSON_BUILD 245 +#define SANE_EPSON_VERSION "SANE Epson Backend v0.2.46 - 2006-06-11" +#define SANE_EPSON_BUILD 246 /* This file is part of the SANE package. @@ -59,6 +60,8 @@ If you do not wish that, delete this exception notice. */ /* + 2006-06-11 Applied patch from Henning. Fixed a number of compiler warnings + 2006-03-12 Added support for perfetion 4990 photo 4800 dpi 2005-01-09 "flaming hack to get USB scanners working without timeouts under linux" submitted by "Steve" (in comment to bug #300830) 2004-12-18 Added USB IDs for CX-4600 and CX-3650 @@ -453,7 +456,7 @@ {"B8", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-4,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0x19,'!','s','N', 0, 0, 0, 'p','q'}, {"F5", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z', 0, 'M','@','g','d','K','z','Q', 0, 'm','f','e','\f', 0, 0, 0, 'N','T','P', 0, 0, 0}, {"D1", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0}, - {"D7", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0}, + {"D7", 'I', 0, 'F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f', 0, 0, 0, '!', 0, 0, 0, 0, 0, 0, 0}, {"D8", 'I','i','F', 0, 'C','G','D','R', 0, 'A', 0, {0,0,0}, 'Z', 0, 0, '@','g','d', 0, 'z', 0, 0, 0, 'f','e', 0, 0, '!', 0, 0, 0, 0, 0, 0, 0}, }; @@ -2080,13 +2083,13 @@ */ if (buf[0] != TYPE_PROCESSOR - || strncmp (buf + 8, "EPSON", 5) != 0 - || (strncmp (buf + 16, "SCANNER ", 8) != 0 - && strncmp (buf + 14, "SCANNER ", 8) != 0 - && strncmp (buf + 14, "Perfection", 10) != 0 - && strncmp (buf + 16, "Perfection", 10) != 0 - && strncmp (buf + 16, "Expression", 10) != 0 - && strncmp (buf + 16, "GT", 2) != 0)) + || strncmp ((char *) (buf + 8), "EPSON", 5) != 0 + || (strncmp ((char *) buf + 16, "SCANNER ", 8) != 0 + && strncmp ((char *) buf + 14, "SCANNER ", 8) != 0 + && strncmp ((char *) buf + 14, "Perfection", 10) != 0 + && strncmp ((char *) buf + 16, "Perfection", 10) != 0 + && strncmp ((char *) buf + 16, "Expression", 10) != 0 + && strncmp ((char *) buf + 16, "GT", 2) != 0)) { DBG (1, "attach: device doesn't look like an EPSON scanner\n"); close_scanner (s); @@ -2219,7 +2222,7 @@ if (s->hw->cmd->request_identity2 != 0) { - get_identity2_information (s); + status = get_identity2_information (s); if (status != SANE_STATUS_GOOD) return status; } /* request identity 2 */ @@ -2354,6 +2357,7 @@ { DBG (1, "Extended status flag request failed\n"); dev->sane.model = strdup ("Unknown model"); + *source_list_add++ = FBF_STR; } else { @@ -2478,6 +2482,21 @@ SANE_FIX ((buf[10] << 8 | buf[9]) * 25.4 / dev->dpi_range.max); dev->tpu_y_range.quant = 0; + /* + * Check for Perfection 4990 photo/GT-X800 scanner. + * This scanner only report 3200 dpi back. + * The scanner fysical supports 4800 dpi. + * This is simulated here... + * Futher details read: + * EPSON Programming guide for EPSON Color Image Scanner Perfection 4990 + */ + if (strncmp((char *) buf + 0x1A,"GT-X800",7) == 0) + { + dev->tpu_x_range.max = (dev->tpu_x_range.max/32)*48; + dev->tpu_y_range.max = (dev->tpu_y_range.max/32)*48; + DBG (5, "dpi_range.max %x \n", dev->dpi_range.max); + } + DBG (5, "tpu tlx %f tly %f brx %f bry %f [mm]\n", SANE_UNFIX (dev->tpu_x_range.min), SANE_UNFIX (dev->tpu_y_range.min), @@ -4944,7 +4963,7 @@ len = result[3] << 8 | result[2]; buf = alloca (len); - receive (s, buf, len, &status); /* reveive actual status data */ + receive (s, buf, len, &status); /* receive actual status data */ if (buf[0] & 0x80) { @@ -5949,7 +5968,6 @@ /* we need to correct for the difference in size between the EpsonIdentRec and the EpsonHdrRec */ int correction = sizeof (EpsonIdentRec) - sizeof (EpsonHdrRec); - for (n = (count - correction), buf = ident->buf; n; n -= k, buf += k) { switch (*buf) @@ -5982,6 +6000,53 @@ DBG (1, "maximum scan area: x %d y %d\n", x, y); k = 5; + + /* + * Check for Perfection 4990 photo/GT-X800 scanner. + * This scanner only report 3200 dpi back. + * The scanner fysical supports 4800 dpi. + * This is simulated here... + * Futher details read: + * EPSON Programming guide for EPSON Color Image Scanner Perfection 4990 + */ + if (s->hw->cmd->request_extended_status != 0) + { + u_char *buf; + u_char params[2]; + EpsonHdr head; + + params[0] = ESC; + params[1] = s->hw->cmd->request_extended_status; + + if (NULL != (head = (EpsonHdr) command (s, params, 2, &status))) + { + buf = &head->buf[0x1A]; + DBG (1, "product name %x %x %x %x %x %x %x %x \n", buf[0], buf[1],buf[2],buf[3],buf[4], buf[5],buf[6], buf[7] ); + if (strncmp((char *) buf,"GT-X800",7) == 0) + { + int val = 0x12 << 8 | 0xC0; + + s->hw->res_list_size++; + s->hw->res_list = + (SANE_Int *) realloc (s->hw->res_list, + s->hw->res_list_size * sizeof (SANE_Int)); + + if (NULL == s->hw->res_list) + { + DBG (1, "out of memory (line %d)\n", __LINE__); + return SANE_STATUS_NO_MEM; + } + + s->hw->res_list[s->hw->res_list_size - 1] = (SANE_Int) val; + x = (x/32)*48; + y = (y/32)*48; + + DBG (1, "resolution (dpi): %d\n", val); + DBG (1, "maximum scan area GT-X800: x %d y %d\n", x, y); + } + } + } + continue; } default: diff -uNr sane-backends-1.0.17/backend/epson.conf sane-backends-1.0.18/backend/epson.conf --- sane-backends-1.0.17/backend/epson.conf 2005-02-14 19:46:38.000000000 +0100 +++ sane-backends-1.0.18/backend/epson.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,27 +0,0 @@ -# epson.conf -# -# here are some examples for how to configure the EPSON backend -# -# SCSI scanner: -scsi EPSON -# for the GT-6500, comment out the previous line and uncomment the following line: -#scsi -# -# Parallel port scanner: -#pio 0x278 -#pio 0x378 -#pio 0x3BC -# -# USB scanner: -# There are two different methods of configuring a USB scanner: libusb and the kernel module -# For any system with libusb support (which is pretty much any recent Linux distribution) the -# following line is sufficient. This however assumes that the connected scanner (or to be more -# accurate, it's device ID) is known to the backend. -usb -# For libusb support for unknown scanners use the following command -# usb -# e.g.: -# usb 0x4b8 0x110 -# And for the scanner module, use the following configuration: -#usb /dev/usbscanner0 -#usb /dev/usb/scanner0 diff -uNr sane-backends-1.0.17/backend/epson.conf.in sane-backends-1.0.18/backend/epson.conf.in --- sane-backends-1.0.17/backend/epson.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/epson.conf.in 2006-06-11 22:24:19.000000000 +0200 @@ -0,0 +1,27 @@ +# epson.conf +# +# here are some examples for how to configure the EPSON backend +# +# SCSI scanner: +scsi EPSON +# for the GT-6500: +scsi "EPSON SC" +# +# Parallel port scanner: +#pio 0x278 +#pio 0x378 +#pio 0x3BC +# +# USB scanner: +# There are two different methods of configuring a USB scanner: libusb and the kernel module +# For any system with libusb support (which is pretty much any recent Linux distribution) the +# following line is sufficient. This however assumes that the connected scanner (or to be more +# accurate, it's device ID) is known to the backend. +usb +# For libusb support for unknown scanners use the following command +# usb +# e.g.: +# usb 0x4b8 0x110 +# And for the scanner module, use the following configuration: +#usb /dev/usbscanner0 +#usb /dev/usb/scanner0 diff -uNr sane-backends-1.0.17/backend/epson_usb.c sane-backends-1.0.18/backend/epson_usb.c --- sane-backends-1.0.17/backend/epson_usb.c 2005-12-04 22:27:36.000000000 +0100 +++ sane-backends-1.0.18/backend/epson_usb.c 2006-06-11 22:24:19.000000000 +0200 @@ -37,6 +37,7 @@ 0x811, /* PM-A870 / Stylus Photo RX620/RX630 */ 0x814, /* PM-A700 */ 0x815, /* LP-A500 / AcuLaser CX11 */ + 0x819, /* CX4800 */ 0x820, /* CX4200 */ 0 /* last entry - this is used for devices that are specified in the config file as "usb " */ diff -uNr sane-backends-1.0.17/backend/fujitsu.c sane-backends-1.0.18/backend/fujitsu.c --- sane-backends-1.0.17/backend/fujitsu.c 2005-11-08 17:19:12.000000000 +0100 +++ sane-backends-1.0.18/backend/fujitsu.c 2006-06-26 19:08:38.000000000 +0200 @@ -42,25 +42,22 @@ This file implements a SANE backend for various Fujitsu scanners. Currently supported: - - M3091 (Frederik Ramm) - - M3096 (originally by Randolph Bentson, now by Oliver Schirrmeister) + - M309x (Frederik Ramm, Randolph Bentson, Oliver Schirrmeister) + - M409x (Oliver Schirrmeister) + - fi-series (M. Allan Noah, Oliver Schirrmeister) The source code is divided in sections which you can easily find by search for the tag "@@". Section 1 - Init & static stuff - Section 2 - Routines called by SANE (externally visible) - Section 3 - generic private routines with no or very little model-specific - code - Section 4 - generic private routines with a lot of model-specific code - Section 5 - private routines for the M3091 - Section 6 - private routines for the M3096 - Section 7 - private routines for the EVPD-capable scanners - Section 8 - private routines for the M3092 - ... + Section 2 - sane_init, _get_devices, _open & friends + Section 3 - sane_*_option functions + Section 4 - sane_start, _get_param, _read & friends + Section 5 - sane_close functions + Section 6 - misc functions Changes: - V 1.2, 05-Mai-2002, OS (oschirr@abm.de) + V 1.0.2, 2002-05-05, OS - release memory allocated by sane_get_devices - several bugfixes - supports the M3097 @@ -68,49 +65,47 @@ - imprinter support - get_hardware_status now works before calling sane_start - avoid unnecessary reload of options when using source=fb - V 1.3, 08-Aug-2002, OS (oschirr@abm.de) + V 1.0.3, 2002-08-08, OS - bugfix. Imprinter didn't print the first time after switching on the scanner - bugfix. reader_generic_passthrough ignored the number of bytes returned by the scanner. - V 1.4, 13-Sep-2002 + V 1.0.4, 2002-09-13, OS - 3092 support (mgoppold@tbz-pariv.de) - tested 4097 support - changed some functions to receive compressed data - V 1.4, 13-Feb-2003 + V 1.0.4, 2003-02-13, OS - fi-4220C support (ron@roncemer.com) - - USB support for scanners which send SCSI commands over usb - (ron@roncemer.com) - V 1.5, 20-Feb-2003 OS (oschirr@abm.de) + - SCSI over USB support (ron@roncemer.com) + V 1.0.5, 2003-02-20, OS - set availability of options THRESHOLD und VARIANCE - correctly - option RIF is available for 3091 und 3092 - V 1.6, 04-Mar-2003 (oschirr@abm.de) + V 1.0.6, 2003-03-04, OS - renamed some variables - bugfix: duplex scanning now works when disconnect is enabled in the scsi controller. - V 1.7, 10-Mar-2003 (oschirr@abm.de) + V 1.0.7, 2003-03-10, OS - displays the offending byte when something is wrong in the window descriptor block. - V 1.8, 28-Mar-2003 (oschirr@abm.de) - - fi-4120C support (anoah@pfeiffer.edu) + V 1.0.8, 2003-03-28, OS + - fi-4120C support, MAN - display information about gamma in vital_product_data - V 1.9 04-Jun-2003 (anoah@pfeiffer.edu) + V 1.0.9 2003-06-04, MAN - separated the 4120 and 4220 into another model - color support for the 4x20 - V 1.10 04-Jun-2003 (anoah@pfeiffer.edu) + V 1.0.10 2003-06-04, MAN - removed SP15 code - sane_open actually opens the device you request - V 1.11 11-Jun-2003 (anoah@pfeiffer.edu) + V 1.0.11 2003-06-11, MAN - fixed bug in that code when a scanner is disconnected - V 1.12 06-Oct-2003 (anoah@pfeiffer.edu) + V 1.0.12 2003-10-06, MAN - added code to support color modes of more recent scanners - V 1.13 07-Nov-2003 (oschirr@abm.de) + V 1.0.13 2003-11-07, OS - Bugfix. If a scanner returned a color image in format rr...r gg.g bb...b the reader process crashed. - Bugfix. The option gamma was enabled for the fi-4120. The result was an 'invalid field in parm list'-error. - V 1.14 15-Dec-2003 (oschirr@abm.de) + V 1.0.14 2003-12-15, OS - Bugfix: set default threshold range to 0..255 There is a problem with the M3093 when you are not allows to set the threshold to 0. - Bugfix: set the allowable x- and y-DPI values from VPD. Scanning @@ -120,32 +115,106 @@ that don't have build in gamma patterns. - Bugfix: fi-4530 and fi-4210 don't support standard paper size spezification. Disable this option for these scanners. - V 1.14 16-Dec-2003 (oschirr@abm.de) + V 1.0.15 2003-12-16, OS - Bugfix: pagewidth and pageheight where disable for the fi-4530C. - 20-Feb-2004 (oschirr@abm.de) + V 1.0.16 2004-02-20, OS - merged the 3092-routines with the 3091-routines. - inverted the image in mode color and grayscale - jpg hardware compression support (fi-4530C) - 04-Mrz-2004 (oschirr@abm.de) + V 1.0.17 2004-03-04, OS - enabled option dropoutcolor for the fi-4530C, and fi-4x20C - 02.06.2004 (oschirr@abm.de) + V 1.0.18 2004-06-02, OS - bugfix: can read duplex color now - 2004-06-28 (anoah@pfeiffer.edu) - - bugfix: use model code instead of string compare with 4220 - submitted by: stan@saticed.me.uk - 2004-08-24 (oschirr@abm.de) + V 1.0.19 2004-06-28, MAN + - 4220 use model code not strcmp (stan@saticed.me.uk) + V 1.0.20 2004-08-24, OS - bugfix: 3091 did not work since 15.12.2003 - M4099 supported (bw only) + V 1.0.21 2006-05-01, MAN + - Complete rewrite, half code size + - better (read: correct) usb command support + - basic support for most fi-series + - most scanner capabilities read from VPD + - reduced model-specific code + - improved scanner detection/initialization + - improved SANE_Option handling + - basic button support + - all IPC and Imprinter options removed temporarily + - duplex broken temporarily + V 1.0.22 2006-05-04, MAN + - do_scsi_cmd gets basic looping capability + - reverse now divided by mode + - re-write sane_fix/unfix value handling + - fix several bugs in options code + - some options' ranges modified by other options vals + - added advanced read-only options for all + known hardware sensors and buttons + - rewrote hw status function + - initial testing with M3091dc- color mode broken + V 1.0.23 2006-05-14, MAN + - initial attempt to recover duplex mode + - fix bad usb prodID when config file missing + V 1.0.24 2006-05-17, MAN + - sane_read must set len=0 when return != good + - simplify do_cmd() calls by removing timeouts + - lengthen most timeouts, shorten those for wait_scanner() + V 1.0.25 2006-05-19, MAN + - rename scsi-buffer-size to buffer-size, usb uses it too + - default buffer-size increased to 64k + - use sanei_scsi_open_extended() to set buffer size + - fix some compiler warns: 32&64 bit gcc + V 1.0.26 2006-05-23, MAN + - dont send scanner control (F1) if unsupported + V 1.0.27 2006-05-30, MAN + - speed up hexdump (adeuring A T gmx D O T net) + - duplex request same size block from both sides + - dont #include or call sanei_thread + - split usb/scsi command DBG into 25 and 30 + V 1.0.28 2006-06-01, MAN + - sane_read() usleep if scanner is busy + - do_*_cmd() no looping (only one caller used it), + remove unneeded casts, cleanup/add error messages + - scanner_control() look at correct has_cmd_* var, + handles own looping on busy + V 1.0.29 2006-06-04, MAN + - M3091/2 Color mode support (duplex still broken) + - all sensors option names start with 'button-' + - rewrite sane_read and helpers to use buffers, + currently an extreme waste of ram, but should + work with saned and scanimage -T + - merge color conversion funcs into read_from_buf() + - compare bytes tx v/s rx instead of storing EOFs + - remove scanner cmd buf, use buf per func instead + - print color and duplex raster offsets (inquiry) + - print EOM, ILI, and info bytes (request sense) + V 1.0.30 2006-06-06, MAN + - M3091/2 duplex support, color/gray/ht/lineart ok + - sane_read helpers share code, report more errors + - add error msg if VPD missing or non-extended + - remove references to color_lineart and ht units + - rework init_model to support more known models + - dont send paper size data if using flatbed + V 1.0.31 2006-06-13, MAN + - add 5220C usb id + - dont show ink level buttons if no imprinter + - run ghs/rs every second instead of every other + V 1.0.32 2006-06-14, MAN + - add 4220C2 usb id + V 1.0.33 2006-06-14, MAN + - add Fi-5900 usb id and init_model section SANE FLOW DIAGRAM - - sane_init() : initialize backend, attach scanners + - sane_init() : initialize backend . - sane_get_devices() : query list of scanner devices . - sane_open() : open a particular scanner device . . - sane_set_io_mode : set blocking mode . . - sane_get_select_fd : get scanner fd + . . . . - sane_get_option_descriptor() : get option information . . - sane_control_option() : change option values + . . - sane_get_parameters() : returns estimated scan parameters + . . - (repeat previous 3 functions) . . . . - sane_start() : start image acquisition . . - sane_get_parameters() : returns actual scan parameters @@ -160,7 +229,9 @@ */ -/* ------------------------------------------------------------------------- */ +/* + * @@ Section 1 - Init + */ #include "sane/config.h" @@ -185,234 +256,56 @@ #include "sane/sanei_usb.h" #include "sane/saneopts.h" #include "sane/sanei_config.h" -#include "sane/sanei_thread.h" #include "fujitsu-scsi.h" #include "fujitsu.h" #define DEBUG 1 +#define BUILD 33 -#define MSG_ERR 1 -#define MSG_USER 5 -#define MSG_INFO 6 -#define FLOW_CONTROL 10 -#define MSG_IO 15 -#define MSG_IO_READ 17 -#define IO_CMD 20 -#define IO_CMD_RES 20 -#define MSG_GET 25 -/* ------------------------------------------------------------------------- */ +/* values for SANE_DEBUG_FUJITSU env var: + - errors 5 + - function trace 10 + - function detail 15 + - get/setopt cmds 20 + - scsi/usb trace 25 + - scsi/usb detail 30 +*/ -static const char sourceADF[] = "ADF"; -static const char sourceFlatbed[] = "FB"; -static SANE_String_Const sourceListWithADF[] = - { sourceADF, sourceFlatbed, NULL }; -static SANE_String_Const sourceListWithoutADF[] = { sourceFlatbed, NULL }; - -static const char duplexFront[] = "front"; -static const char duplexBack[] = "back"; -static const char duplexBoth[] = "both"; -static SANE_String_Const duplexListWithDuplex[] = - { duplexFront, duplexBack, duplexBoth, NULL }; -static SANE_String_Const duplexListSpecial[] = - { duplexFront, duplexBoth, NULL }; -static SANE_String_Const duplexListWithoutDuplex[] = { duplexFront, NULL }; - -static const char mode_lineart[] = "Lineart"; -static const char mode_halftone[] = "Halftone"; -static const char mode_grayscale[] = "Gray"; -static const char mode_color[] = "Color"; -static SANE_String_Const modeList3091[] = - { mode_lineart, mode_grayscale, mode_color, NULL }; -static SANE_String_Const modeList3096[] = - { mode_lineart, mode_halftone, mode_grayscale, mode_color, NULL }; - -static const char lampRed[] = "red"; -static const char lampGreen[] = "green"; -static const char lampBlue[] = "blue"; -static const char lampDefault[] = "default"; -static SANE_String_Const lamp_colorList[] = - { lampRed, lampGreen, lampBlue, lampDefault, NULL }; - - -static const char dropout_color_red[] = "Red"; -static const char dropout_color_green[] = "Green"; -static const char dropout_color_blue[] = "Blue"; -static const char dropout_color_default[] = "Panel"; -static SANE_String_Const dropout_color_list[] = - { dropout_color_default, dropout_color_red, - dropout_color_green, dropout_color_blue, NULL -}; - -static const SANE_Range default_threshold_range = { 0, 255, 1 }; -static const SANE_Range default_brightness_range = { 0, 255, 1 }; -static const SANE_Range default_contrast_range = { 0, 255, 1 }; - -static const SANE_Range rangeX3091 = - { 0, SANE_FIX (215.9), SANE_FIX (MM_PER_INCH / 1200) }; -static const SANE_Range rangeY3091 = - { 0, SANE_FIX (355.6), SANE_FIX (MM_PER_INCH / 1200) }; -static const SANE_Range rangeX3092 = - { 0, SANE_FIX (215.5), SANE_FIX (MM_PER_INCH / 1200) }; -static const SANE_Range rangeY3092 = - { 0, SANE_FIX (305.0), SANE_FIX (MM_PER_INCH / 1200) }; -static const SANE_Range rangeX3096 = - { 0, SANE_FIX (308.8), SANE_FIX (MM_PER_INCH / 1200) }; -static const SANE_Range rangeY3096 = - { 0, SANE_FIX (438.9), SANE_FIX (MM_PER_INCH / 1200) }; -static const SANE_Range rangeColorOffset = { -16, 16, 1 }; -static const char smoothing_mode_ocr[] = "OCR"; -static const char smoothing_mode_image[] = "Image"; -static SANE_String_Const smoothing_mode_list[] = - { smoothing_mode_ocr, smoothing_mode_image, NULL }; - -static const char filtering_ballpoint[] = "Ballpoint"; -static const char filtering_ordinary[] = "Ordinary"; -static SANE_String_Const filtering_mode_list[] = - { filtering_ballpoint, filtering_ordinary, NULL }; - -static const char background_white[] = "White"; -static const char background_black[] = "Black"; -static SANE_String_Const background_mode_list[] = - { background_white, background_black, NULL }; - -static const char white_level_follow_default[] = "Default"; -static const char white_level_follow_enabled[] = "Enabled"; -static const char white_level_follow_disabled[] = "Disabled"; -static SANE_String_Const white_level_follow_mode_list[] = - { white_level_follow_default, white_level_follow_enabled, - white_level_follow_disabled, NULL -}; - -static const char dtc_selection_default[] = "Default"; -static const char dtc_selection_simplified[] = "Simplified"; -static const char dtc_selection_dynamic[] = "Dynamic"; -static SANE_String_Const dtc_selection_mode_list[] = - { dtc_selection_default, dtc_selection_simplified, dtc_selection_dynamic, -NULL } - ; - -static const char cmp_none[] = "None"; -static const char cmp_mh[] = "MH"; /* Fax Group 3 */ -static const char cmp_mr[] = "MR"; /* what's that??? */ -static const char cmp_mmr[] = "MMR"; /* Fax Group 4 */ -static const char cmp_jbig[] = "JBIG"; -static const char cmp_jpg_base[] = "JPG_BASE_LINE"; -static const char cmp_jpg_ext[] = "JPG_EXTENDED"; -static const char cmp_jpg_indep[] = "JPG_INDEPENDENT"; - - -static const char gamma_default[] = "Default"; -static const char gamma_normal[] = "Normal"; -static const char gamma_soft[] = "Soft"; -static const char gamma_sharp[] = "Sharp"; -static SANE_String_Const gamma_mode_list[] = - { gamma_default, gamma_normal, gamma_soft, gamma_sharp, NULL }; - -static const char emphasis_none[] = "None"; -static const char emphasis_low[] = "Low"; -static const char emphasis_medium[] = "Medium"; -static const char emphasis_high[] = "High"; -static const char emphasis_smooth[] = "Smooth"; -static SANE_String_Const emphasis_mode_list[] = - { emphasis_none, emphasis_low, emphasis_medium, - emphasis_high, emphasis_smooth, NULL -}; - -static const SANE_Range variance_rate_range = { 0, 255, 1 }; - -static const SANE_Range threshold_curve_range = { 0, 7, 1 }; -static const SANE_Range jpg_quality_range = { 0, 7, 1}; - -static const char gradation_ordinary[] = "Ordinary"; -static const char gradation_high[] = "High"; -static SANE_String_Const gradation_mode_list[] = - { gradation_ordinary, gradation_high, NULL }; - -static const char paper_size_a3[] = "A3"; -static const char paper_size_a4[] = "A4"; -static const char paper_size_a5[] = "A5"; -static const char paper_size_double[] = "Double"; -static const char paper_size_letter[] = "Letter"; -static const char paper_size_b4[] = "B4"; -static const char paper_size_b5[] = "B5"; -static const char paper_size_legal[] = "Legal"; -static const char paper_size_custom[] = "Custom"; -static const char paper_size_detect[] = "Autodetect"; - -static SANE_String_Const paper_size_mode_list[] = - { paper_size_a3, paper_size_a4, paper_size_a5, - paper_size_double, paper_size_letter, paper_size_b4, paper_size_b5, - paper_size_legal, paper_size_custom, paper_size_detect, NULL -}; - -static const char paper_orientation_portrait[] = "Portrait"; -static const char paper_orientation_landscape[] = "Landscape"; -static SANE_String_Const paper_orientation_mode_list[] = - { paper_orientation_portrait, paper_orientation_landscape, NULL }; - - -static const char imprinter_dir_topbot[] = "Top to Bottom"; -static const char imprinter_dir_bottop[] = "Bottom to Top"; -static SANE_String_Const imprinter_dir_list[] = - { imprinter_dir_topbot, imprinter_dir_bottop, NULL }; - -static const char imprinter_ctr_dir_inc[] = "Increment"; -static const char imprinter_ctr_dir_dec[] = "Decrement"; -static SANE_String_Const imprinter_ctr_dir_list[] = - { imprinter_ctr_dir_inc, imprinter_ctr_dir_dec, NULL }; - -static SANE_Range imprinter_ctr_step_range = { 0, 2, 1 }; -static const SANE_Range range_imprinter_y_offset = - { SANE_FIX (7), SANE_FIX(140), 0}; -/*SANE_FIX (MM_PER_INCH / 1200) };*/ - -static SANE_Range range_sleep_mode = {1, 254, 1}; - -static SANE_Int res_list0[] = { 4, 0, 200, 300, 400 }; /* 3096GX gray */ - -static SANE_Int res_list1[] = { 5, 0, 200, 240, 300, 400 };/* 3096GX binary */ - -static SANE_Int res_list2[] = { 7, 0, 100, 150, 200, 240, 300, 400 };/* 3093DG gray */ - -static SANE_Int res_list3[] = { 8, 0, 100, 150, 200, 240, 300, 400, 600 }; /* 3093DG binary */ - -/* This can be set via the config file if desired to force a non-recognized - * scanner to be treated like one of the known models (e.g. you have the - * new M1234-barf5 and believe it is M3096 compatible). - */ -static int forceModel = -1; +/* ------------------------------------------------------------------------- */ +static const char source_Flatbed[] = "Flatbed"; +static const char source_ADFFront[] = "ADF Front"; +static const char source_ADFBack[] = "ADF Back"; +static const char source_ADFDuplex[] = "ADF Duplex"; + +static const char mode_Lineart[] = "Lineart"; +static const char mode_Halftone[] = "Halftone"; +static const char mode_Grayscale[] = "Gray"; +static const char mode_Color_lineart[] = "Color Lineart"; +static const char mode_Color_halftone[] = "Color Halftone"; +static const char mode_Color[] = "Color"; + +static const char color_Red[] = "Red"; +static const char color_Green[] = "Green"; +static const char color_Blue[] = "Blue"; +static const char color_Default[] = "Default"; /* Also set via config file. */ -static int scsiBuffer = 64 * 1024; - -/* flaming hack to get USB scanners - working without timeouts under linux */ -static unsigned int r_cmd_count = 0; -static unsigned int w_cmd_count = 0; - -/* - * required for compressed data transfer. sense_handler has to tell - * the caller the number of scanned bytes. - */ -static struct fujitsu *current_scanner = NULL; +static int global_buffer_size = 64 * 1024; /* - * used by sane_get_devices + * used by attach* and sane_get_devices + * a ptr to a null term array of ptrs to SANE_Device structs + * a ptr to a single-linked list of fujitsu structs */ -static const SANE_Device **devlist = 0; +static const SANE_Device **sane_devArray = NULL; +static struct fujitsu *fujitsu_devList = NULL; /* - * used by attachScanner and attachOne + * @@ Section 2 - SANE & scanner init code */ -static Fujitsu_Connection_Type mostRecentConfigConnectionType = SANE_FUJITSU_SCSI; /* - * @@ Section 2 - SANE Interface - */ - - -/** * Called by SANE initially. * * From the SANE spec: @@ -428,135 +321,25 @@ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { - char devName[PATH_MAX]; - char line[PATH_MAX]; - const char *lp; - size_t len; - FILE *fp; - - mostRecentConfigConnectionType = SANE_FUJITSU_SCSI; authorize = authorize; /* get rid of compiler warning */ DBG_INIT (); - DBG (10, "sane_init\n"); + DBG (10, "sane_init: start\n"); sanei_usb_init(); - sanei_thread_init(); if (version_code) - *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0); - fp = sanei_config_open (FUJITSU_CONFIG_FILE); - - /* - * If we don't have a config file, the device name /dev/scanner is used. - */ - if (!fp) - { - attachScanner ("/dev/scanner", 0); - return SANE_STATUS_GOOD; - } - - scsiBuffer = (sanei_scsi_max_request_size < (64 * 1024)) ? - sanei_scsi_max_request_size : 64 * 1024; - - /* - * Read the config file. - */ + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); - DBG (10, "sane_init: reading config file %s\n", FUJITSU_CONFIG_FILE); + DBG (5, "sane_init: fujitsu backend %d.%d.%d, from %s\n", + V_MAJOR, V_MINOR, BUILD, PACKAGE_STRING); - while (sanei_config_read (line, PATH_MAX, fp)) - { - int vendor, product; - - /* ignore comments */ - if (line[0] == '#') - continue; - len = strlen (line); - - /* delete newline characters at end */ - if (line[len - 1] == '\n') - line[--len] = '\0'; - - lp = sanei_config_skip_whitespace (line); + DBG (10, "sane_init: finish\n"); - /* skip empty lines */ - if (*lp == 0) - continue; - - if ((strncmp ("option", lp, 6) == 0) && isspace (lp[6])) - { - lp += 6; - lp = sanei_config_skip_whitespace (lp); - - if ((strncmp (lp, "force-model", 11) == 0) && isspace (lp[11])) - { - lp += 11; - lp = sanei_config_skip_whitespace (lp); - /* - if ((forceModel = modelMatch(lp)) == -1) - { - DBG(MSG_ERR, "sane_init: configuration option \"force-model\" " - "does not support value \"%s\" - ignored.\n", lp); - } - else - { - DBG (10, "sane_init: force model \"%s\" ok\n", lp); - } - */ - forceModel = MODEL_FORCE; - } - else if ((strncmp (lp, "scsi-buffer-size", 16) == 0) - && isspace (lp[16])) - { - int buf; - lp += 16; - lp = sanei_config_skip_whitespace (lp); - buf = atoi (lp); - if ((buf >= 4096) && (buf <= sanei_scsi_max_request_size)) - { - scsiBuffer = buf; - } - else - { - DBG (MSG_ERR, - "sane_init: configuration option \"scsi-buffer-" - "size\" is outside allowable range of 4096..%d", - sanei_scsi_max_request_size); - } - } - else - { - DBG (MSG_ERR, - "sane_init: configuration option \"%s\" unrecognized - ignored.\n", - lp); - } - } - else if (sscanf(lp, "usb %i %i", &vendor, &product) == 2) - { - mostRecentConfigConnectionType = SANE_FUJITSU_USB; - sanei_usb_attach_matching_devices(lp, attachOne); - mostRecentConfigConnectionType = SANE_FUJITSU_SCSI; - } - else /* must be a device name if it's not an option */ - { - if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { - lp += 3; - lp = sanei_config_skip_whitespace (lp); - mostRecentConfigConnectionType = SANE_FUJITSU_USB; - } - strncpy (devName, lp, sizeof (devName)); - devName[sizeof (devName) - 1] = '\0'; - sanei_config_attach_matching_devices (devName, attachOne); - mostRecentConfigConnectionType = SANE_FUJITSU_SCSI; - } - } - fclose (fp); return SANE_STATUS_GOOD; } - -/** +/* * Called by SANE to find out about supported devices. * * From the SANE spec: @@ -580,4438 +363,2491 @@ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { - struct fujitsu *dev; - int i; + SANE_Status ret; - DBG (10, "sane_get_devices %d\n", local_only); + local_only = local_only; /* get rid of compiler warning */ - if (devlist) - free (devlist); - devlist = calloc (num_devices + 1, sizeof (SANE_Device*)); - if (!devlist) - return SANE_STATUS_NO_MEM; + DBG (10, "sane_get_devices: start\n"); - for (dev = first_dev, i = 0; i < num_devices; dev = dev->next) - devlist[i++] = &dev->sane; - devlist[i++] = 0; + /* load the two global lists of device structs */ + ret = find_scanners(); - *device_list = devlist; + *device_list = sane_devArray; - return SANE_STATUS_GOOD; -} + DBG (10, "sane_get_devices: finish\n"); + return ret; +} -/** - * Called to establish connection with the scanner. This function will - * also establish meaningful defauls and initialize the options. +/* + * Read the config file, find scanners with help from sanei_* + * store in global device structs * - * From the SANE spec: - * This function is used to establish a connection to a particular - * device. The name of the device to be opened is passed in argument - * name. If the call completes successfully, a handle for the device - * is returned in *h. As a special case, specifying a zero-length - * string as the device requests opening the first available device - * (if there is such a device). + * We place the complex init code here, so it can be called + * from sane_get_devices() and sane_open() */ SANE_Status -sane_open (SANE_String_Const name, SANE_Handle * handle) +find_scanners () { - struct fujitsu *dev = NULL; - struct fujitsu *scanner = NULL; - - if(name[0] == 0){ - DBG (10, "sane_open: no device requested, using default\n"); - if(first_dev){ - scanner = (struct fujitsu *) first_dev; - DBG (10, "sane_open: device %s found\n", first_dev->sane.name); - } - } - else{ - DBG (10, "sane_open: device %s requested\n", name); - - for (dev = first_dev; dev; dev = dev->next) - { - if (strcmp (dev->sane.name, name) == 0) - { - DBG (10, "sane_open: device %s found\n", name); - scanner = (struct fujitsu *) dev; + struct fujitsu *dev; + char line[PATH_MAX]; + const char *lp; + size_t len; + FILE *fp; + int num_devices=0; + int i=0; + + DBG (10, "find_scanners: start\n"); + + /* set this to 64K before reading the file */ + global_buffer_size = 64 * 1024; + + fp = sanei_config_open (FUJITSU_CONFIG_FILE); + + if (fp) { + + DBG (15, "find_scanners: reading config file %s\n", FUJITSU_CONFIG_FILE); + + while (sanei_config_read (line, PATH_MAX, fp)) { + + /* ignore comments */ + if (line[0] == '#') + continue; + + /* delete newline characters at end */ + len = strlen (line); + if (line[len - 1] == '\n') + line[--len] = '\0'; + + lp = sanei_config_skip_whitespace (line); + + /* skip empty lines */ + if (*lp == 0) + continue; + + if ((strncmp ("option", lp, 6) == 0) && isspace (lp[6])) { + + lp += 6; + lp = sanei_config_skip_whitespace (lp); + + /* we allow setting buffersize too big */ + if ((strncmp (lp, "buffer-size", 11) == 0) && isspace (lp[11])) { + + int buf; + lp += 11; + lp = sanei_config_skip_whitespace (lp); + buf = atoi (lp); + + if (buf < 4096) { + DBG (5, "find_scanners: config option \"buffer-size\" (%d) is < 4096, ignoring!\n", buf); + continue; + } + + if (buf > 64*1024) { + DBG (5, "find_scanners: config option \"buffer-size\" (%d) is > %d, warning!\n", buf, 64*1024); + } + + DBG (15, "find_scanners: setting \"buffer-size\" to %d\n", buf); + global_buffer_size = buf; + } + else { + DBG (5, "find_scanners: config option \"%s\" unrecognized - ignored.\n", lp); + } + } + else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { + DBG (15, "find_scanners: looking for '%s'\n", lp); + sanei_usb_attach_matching_devices(lp, attach_one_usb); + } + else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) { + DBG (15, "find_scanners: looking for '%s'\n", lp); + sanei_config_attach_matching_devices (lp, attach_one_scsi); + } + else{ + DBG (5, "find_scanners: config line \"%s\" unrecognized - ignored.\n", lp); } } + fclose (fp); } - if (!scanner) { - DBG (10, "sane_open: no device found\n"); - return SANE_STATUS_INVAL; - } + else { + DBG (5, "find_scanners: no config file '%s', using defaults\n", FUJITSU_CONFIG_FILE); - *handle = scanner; + DBG (15, "find_scanners: looking for 'scsi FUJITSU'\n"); + sanei_config_attach_matching_devices ("scsi FUJITSU", attach_one_scsi); - /* - * Determine which SANE options are available with this scanner. - * Since many options are identical for all models, handle them - * all in one function. - */ + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x1041'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x1041", attach_one_usb); - init_options (scanner); + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x1042'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x1042", attach_one_usb); - /* - * Now we have to establish sensible defaults for this scanner model. - * - * Setting the default values can be quite model-specific, so - * we're using an extra routine for each. - */ + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x1096'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x1096", attach_one_usb); - if (!scanner->has_gamma && scanner->num_download_gamma > 0) - { - scanner->gamma = 0x80; - } - else - { - scanner->gamma = WD_gamma_DEFAULT; - } + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x1097'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x1097", attach_one_usb); - if(!scanner->has_fixed_paper_size) - { - scanner->paper_selection = WD_paper_SEL_NON_STANDARD; - scanner->paper_size = WD_paper_UNDEFINED; - scanner->paper_orientation = WD_paper_PORTRAIT; - } - else - { - scanner->paper_size = WD_paper_A4; - scanner->paper_orientation = WD_paper_PORTRAIT; - scanner->paper_selection = WD_paper_SEL_STANDARD; - } + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x10ae'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x10ae", attach_one_usb); + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x10af'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x10af", attach_one_usb); - switch (scanner->model) - { + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x10e0'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x10e0", attach_one_usb); - case MODEL_3091: - case MODEL_3092: - case MODEL_FI4x20: - setDefaults3091 (scanner); - break; + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x10e1'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x10e1", attach_one_usb); - case MODEL_FORCE: - case MODEL_3096: - case MODEL_3097: - case MODEL_3093: - case MODEL_4097: - case MODEL_FI: - setDefaults3096 (scanner); - break; + DBG (15, "find_scanners: looking for 'usb 0x04c5 0x10e7'\n"); + sanei_usb_attach_matching_devices("usb 0x04c5 0x10e7", attach_one_usb); - default: - DBG (MSG_ERR, "sane_open: unknown model\n"); - return SANE_STATUS_INVAL; - } + } - return SANE_STATUS_GOOD; + for (dev = fujitsu_devList; dev; dev=dev->next) { + DBG (15, "find_scanners: found scanner %s\n",dev->device_name); + num_devices++; + } -} + DBG (15, "find_scanners: found %d scanner(s)\n",num_devices); + sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*)); + if (!sane_devArray) + return SANE_STATUS_NO_MEM; -/** - * An advanced method we don't support but have to define. - */ -SANE_Status -sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking) -{ - DBG (10, "sane_set_io_mode\n"); - DBG (99, "%d %p\n", non_blocking, h); - return SANE_STATUS_UNSUPPORTED; -} + for (dev = fujitsu_devList; dev; dev=dev->next) { + sane_devArray[i++] = (SANE_Device *)&dev->sane; + } + sane_devArray[i] = 0; -/** - * An advanced method we don't support but have to define. - */ -SANE_Status -sane_get_select_fd (SANE_Handle h, SANE_Int *fdp) -{ - DBG (10, "sane_get_select_fd\n"); - DBG (99, "%p %d\n", h, *fdp); - return SANE_STATUS_UNSUPPORTED; -} + DBG (10, "find_scanners: finish\n"); + return SANE_STATUS_GOOD; +} -/** - * Returns the options we know. - * - * From the SANE spec: - * This function is used to access option descriptors. The function - * returns the option descriptor for option number n of the device - * represented by handle h. Option number 0 is guaranteed to be a - * valid option. Its value is an integer that specifies the number of - * options that are available for device handle h (the count includes - * option 0). If n is not a valid option index, the function returns - * NULL. The returned option descriptor is guaranteed to remain valid - * (and at the returned address) until the device is closed. - */ -const SANE_Option_Descriptor * -sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +/* callbacks used by find_scanners */ +static SANE_Status +attach_one_scsi (const char *device_name) { - struct fujitsu *scanner = handle; - - DBG (MSG_GET, - "sane_get_option_descriptor: \"%s\"\n", scanner->opt[option].name); - - if ((unsigned) option >= NUM_OPTIONS) - return NULL; - return &scanner->opt[option]; + return attach_one(device_name,CONNECTION_SCSI); } +static SANE_Status +attach_one_usb (const char *device_name) +{ + return attach_one(device_name,CONNECTION_USB); +} -/** - * Gets or sets an option value. - * - * From the SANE spec: - * This function is used to set or inquire the current value of option - * number n of the device represented by handle h. The manner in which - * the option is controlled is specified by parameter action. The - * possible values of this parameter are described in more detail - * below. The value of the option is passed through argument val. It - * is a pointer to the memory that holds the option value. The memory - * area pointed to by v must be big enough to hold the entire option - * value (determined by member size in the corresponding option - * descriptor). - * - * The only exception to this rule is that when setting the value of a - * string option, the string pointed to by argument v may be shorter - * since the backend will stop reading the option value upon - * encountering the first NUL terminator in the string. If argument i - * is not NULL, the value of *i will be set to provide details on how - * well the request has been met. +/* build the scanner struct and link to global list + * unless struct is already loaded, then pretend */ -SANE_Status -sane_control_option (SANE_Handle handle, SANE_Int option, - SANE_Action action, void *val, SANE_Int * info) +static SANE_Status +attach_one (const char *device_name, int connType) { - struct fujitsu *scanner = (struct fujitsu *) handle; - SANE_Status status; - SANE_Word cap; - SANE_Int dummy; - SANE_Word *tmp; - int newMode; - - /* Make sure that all those statements involving *info cannot break (better - * than having to do "if (info) ..." everywhere!) - */ - if (info == 0) - info = &dummy; + struct fujitsu *s; + int ret; - *info = 0; + DBG (10, "attach_one: start\n"); + DBG (15, "attach_one: looking for '%s'\n", device_name); - if ( (scanner->object_count != 0) && (scanner->eof == SANE_FALSE) ) - { - DBG (5, "sane_control_option: device busy\n"); - return SANE_STATUS_DEVICE_BUSY; + for (s = fujitsu_devList; s; s = s->next) { + if (strcmp (s->sane.name, device_name) == 0) { + DBG (10, "attach_one: already attached!\n"); + return SANE_STATUS_GOOD; } + } - if (option >= NUM_OPTIONS) - return SANE_STATUS_INVAL; - - cap = scanner->opt[option].cap; + /* build a fujitsu struct to hold it */ + if ((s = calloc (sizeof (*s), 1)) == NULL) + return SANE_STATUS_NO_MEM; - /* - * SANE_ACTION_GET_VALUE: We have to find out the current setting and - * return it in a human-readable form (often, text). - */ - if (action == SANE_ACTION_GET_VALUE) - { - DBG (MSG_GET, "sane_control_option: get value \"%s\"\n", - scanner->opt[option].name); - DBG (11, "\tcap = %d\n", cap); + /* scsi command/data buffer */ + s->buffer_size = global_buffer_size; - if (!SANE_OPTION_IS_ACTIVE (cap)) - { - DBG (10, "\tinactive\n"); - return SANE_STATUS_INVAL; - } + /* copy the device name */ + s->device_name = strdup (device_name); + if (!s->device_name){ + free (s); + return SANE_STATUS_NO_MEM; + } - switch (option) - { - case OPT_NUM_OPTS: - *(SANE_Word *) val = NUM_OPTIONS; - return SANE_STATUS_GOOD; + /* connect the fd */ + s->connection = connType; + s->fd = -1; + s->fds[0] = -1; + s->fds[1] = -1; + ret = connect_fd(s); + if(ret != SANE_STATUS_GOOD){ + free (s->device_name); + free (s); + return ret; + } - case OPT_SOURCE: - strcpy (val, (scanner->use_adf == SANE_TRUE) ? - sourceADF : sourceFlatbed); - return SANE_STATUS_GOOD; + /* Now query the device to load its vendor/model/version */ + ret = init_inquire (s); + if (ret != SANE_STATUS_GOOD) { + disconnect_fd(s); + free (s->device_name); + free (s); + DBG (5, "attach_one: inquiry failed\n"); + return ret; + } - case OPT_DUPLEX: - strcpy (val, (scanner->duplex_mode == DUPLEX_BOTH) ? duplexBoth : - (scanner->duplex_mode == DUPLEX_BACK) ? - duplexBack : duplexFront); - return SANE_STATUS_GOOD; + /* load detailed specs/capabilities from the device */ + ret = init_vpd (s); + if (ret != SANE_STATUS_GOOD) { + disconnect_fd(s); + free (s->device_name); + free (s); + DBG (5, "attach_one: vpd failed\n"); + return ret; + } - case OPT_MODE: - strcpy (val, (scanner->color_mode == MODE_COLOR) ? mode_color : - (scanner->color_mode == MODE_GRAYSCALE) ? mode_grayscale : - (scanner->color_mode == MODE_HALFTONE) ? mode_halftone : - mode_lineart); - return SANE_STATUS_GOOD; + /* clean up the scanner struct based on model */ + /* this is the only piece of model specific code */ + /* sets SANE option 'values' to good defaults */ + ret = init_model (s); + if (ret != SANE_STATUS_GOOD) { + disconnect_fd(s); + free (s->device_name); + free (s); + DBG (5, "attach_one: model failed\n"); + return ret; + } + ret = init_options (s); + if (ret != SANE_STATUS_GOOD) { + disconnect_fd(s); + free (s->device_name); + free (s); + DBG (5, "attach_one: options failed\n"); + return ret; + } - case OPT_LAMP_COLOR: - strcpy (val, (scanner->lamp_color == LAMP_DEFAULT) ? lampDefault : - (scanner->lamp_color == LAMP_RED) ? lampRed : - (scanner->lamp_color == LAMP_GREEN) ? lampGreen : lampBlue); - return SANE_STATUS_GOOD; + /* we close the connection, so that another backend can talk to scanner */ + disconnect_fd(s); + /* load info into sane_device struct */ + s->sane.name = s->device_name; + s->sane.vendor = s->vendor_name; + s->sane.model = s->product_name; + s->sane.type = "scanner"; - case OPT_DROPOUT_COLOR: - switch (scanner->dropout_color) - { - case MSEL_dropout_DEFAULT: - strcpy (val, dropout_color_default); - break; - case MSEL_dropout_RED: - strcpy (val, dropout_color_red); - break; - case MSEL_dropout_GREEN: - strcpy (val, dropout_color_green); - break; - case MSEL_dropout_BLUE: - strcpy (val, dropout_color_blue); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->next = fujitsu_devList; + fujitsu_devList = s; - case OPT_X_RES: - *(SANE_Word *) val = scanner->resolution_x; - return SANE_STATUS_GOOD; + DBG (10, "attach_one: finish\n"); - case OPT_Y_RES: - *(SANE_Word *) val = scanner->resolution_y; - return SANE_STATUS_GOOD; + return SANE_STATUS_GOOD; +} - case OPT_TL_X: - case OPT_TL_Y: - *(SANE_Word *) val = scanner->val[option].w; - return SANE_STATUS_GOOD; +/* + * connect the fd in the scanner struct + */ +static SANE_Status +connect_fd (struct fujitsu *s) +{ + SANE_Status ret; + int buffer_size = s->buffer_size; - case OPT_PAGE_WIDTH: - *(SANE_Word *) val = SCANNER_UNIT_TO_FIXED_MM (scanner->page_width); - return SANE_STATUS_GOOD; + DBG (10, "connect_fd: start\n"); - case OPT_PAGE_HEIGHT: - *(SANE_Word *) val = - SCANNER_UNIT_TO_FIXED_MM (scanner->page_height); - return SANE_STATUS_GOOD; + if(s->fd > -1){ + DBG (5, "connect_fd: already open\n"); + ret = SANE_STATUS_GOOD; + } + else if (s->connection == CONNECTION_USB) { + DBG (15, "connect_fd: opening USB device\n"); + ret = sanei_usb_open (s->device_name, &(s->fd)); + } + else { + DBG (15, "connect_fd: opening SCSI device\n"); + ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler, s, &s->buffer_size); + if(ret == SANE_STATUS_GOOD && buffer_size != s->buffer_size){ + DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n", buffer_size, s->buffer_size); + ret = SANE_STATUS_NO_MEM; + } + } - case OPT_BR_X: - *(SANE_Word *) val = scanner->bottom_right_x; - return SANE_STATUS_GOOD; + if(ret == SANE_STATUS_GOOD){ - case OPT_BR_Y: - *(SANE_Word *) val = scanner->bottom_right_y; - return SANE_STATUS_GOOD; + /* first generation usb scanners can get flaky if not closed + * properly after last use. very first commands sent to device + * must be prepared to correct this- see wait_scanner() */ + ret = wait_scanner(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "connect_fd: could not wait_scanner\n"); + disconnect_fd(s); + } - case OPT_CONTRAST: - *(SANE_Word *) val = scanner->contrast; - return SANE_STATUS_GOOD; + } + else{ + DBG (5, "connect_fd: could not open device: %d\n", ret); + } - case OPT_RIF: - *(SANE_Bool *) val = scanner->reverse; - return SANE_STATUS_GOOD; + DBG (10, "connect_fd: finish\n"); - case OPT_BRIGHTNESS: - *(SANE_Word *) val = scanner->brightness; - return SANE_STATUS_GOOD; + return ret; +} - case OPT_THRESHOLD: - *(SANE_Word *) val = scanner->threshold; - return SANE_STATUS_GOOD; +/* + * This routine will check if a certain device is a Fujitsu scanner + * It also copies interesting data from INQUIRY into the handle structure + */ +static SANE_Status +init_inquire (struct fujitsu *s) +{ + int i; + SANE_Status ret; + unsigned char buffer[96]; - case OPT_BLUE_OFFSET: - *(SANE_Word *) val = scanner->blue_offset; - return SANE_STATUS_GOOD; + DBG (10, "init_inquire: start\n"); - case OPT_GREEN_OFFSET: - *(SANE_Word *) val = scanner->green_offset; - return SANE_STATUS_GOOD; + set_IN_return_size (inquiryB.cmd, 96); + set_IN_evpd (inquiryB.cmd, 0); + set_IN_page_code (inquiryB.cmd, 0); + + ret = do_cmd ( + s, 1, 0, + inquiryB.cmd, inquiryB.size, + NULL, 0, + buffer, 96 + ); - case OPT_USE_SWAPFILE: - *(SANE_Word *) val = scanner->use_temp_file; - return SANE_STATUS_GOOD; + if (ret != SANE_STATUS_GOOD){ + return ret; + } - case OPT_COMPRESSION: - strcpy (val, (scanner->compress_type == WD_cmp_NONE) ? cmp_none : - (scanner->compress_type == WD_cmp_MH) ? cmp_mh : - (scanner->compress_type == WD_cmp_MR) ? cmp_mr : - (scanner->compress_type == WD_cmp_MMR) ? cmp_mmr : - (scanner->compress_type == WD_cmp_JPG1) ? cmp_jpg_base : - (scanner->compress_type == WD_cmp_JPG2) ? cmp_jpg_ext : - (scanner->compress_type == WD_cmp_JPG3) ? cmp_jpg_indep : - cmp_jbig); - return SANE_STATUS_GOOD; - - case OPT_COMPRESSION_ARG: - - *(SANE_Word *) val = scanner->compress_arg; - return SANE_STATUS_GOOD; - - case OPT_GAMMA: - strcpy (val, (scanner->gamma == WD_gamma_DEFAULT) ? gamma_default : - (scanner->gamma == WD_gamma_NORMAL) ? gamma_normal : - (scanner->gamma == WD_gamma_SOFT) ? gamma_soft : - gamma_sharp); - return SANE_STATUS_GOOD; + if (get_IN_periph_devtype (buffer) != IN_periph_devtype_scanner){ + DBG (5, "The device at '%s' is not a scanner.\n", s->device_name); + return SANE_STATUS_INVAL; + } - case OPT_OUTLINE_EXTRACTION: - if (scanner->outline == 0x80) - { - *(SANE_Bool *) val = 1; - } - else - { - *(SANE_Bool *) val = 0; - } - return SANE_STATUS_GOOD; + get_IN_vendor (buffer, s->vendor_name); + get_IN_product (buffer, s->product_name); + get_IN_version (buffer, s->version_name); + + s->vendor_name[8] = 0; + s->product_name[16] = 0; + s->version_name[4] = 0; + + /* gobble trailing spaces */ + for (i = 7; s->vendor_name[i] == ' ' && i >= 0; i--) + s->vendor_name[i] = 0; + for (i = 15; s->product_name[i] == ' ' && i >= 0; i--) + s->product_name[i] = 0; + for (i = 3; s->version_name[i] == ' ' && i >= 0; i--) + s->version_name[i] = 0; + + if (strcmp ("FUJITSU", s->vendor_name)) { + DBG (5, "The device at '%s' is reported to be made by '%s'\n", s->device_name, s->vendor_name); + DBG (5, "This backend only supports Fujitsu products.\n"); + return SANE_STATUS_INVAL; + } - case OPT_EMPHASIS: - strcpy (val, (scanner->emphasis == WD_emphasis_NONE) ? - emphasis_none : - (scanner->emphasis == WD_emphasis_LOW) ? - emphasis_low : - (scanner->emphasis == WD_emphasis_MEDIUM) ? - emphasis_medium : - (scanner->emphasis == WD_emphasis_HIGH) ? - emphasis_high : emphasis_smooth); - return SANE_STATUS_GOOD; + DBG (15, "init_inquire: Found %s scanner %s version %s at %s\n", + s->vendor_name, s->product_name, s->version_name, s->device_name); - case OPT_AUTOSEP: - *(SANE_Bool *) val = scanner->auto_sep; - return SANE_STATUS_GOOD; + /*some scanners (3091/2) list raster offsets here*/ + DBG (15, "inquiry options\n"); - case OPT_MIRROR_IMAGE: - *(SANE_Bool *) val = scanner->mirroring; - return SANE_STATUS_GOOD; + s->color_raster_offset = get_IN_raster(buffer); + DBG (15, " color offset: %d lines\n",s->color_raster_offset); - case OPT_VARIANCE_RATE: - *(SANE_Word *) val = scanner->var_rate_dyn_thresh; - return SANE_STATUS_GOOD; + s->duplex_raster_offset = get_IN_frontback(buffer); + DBG (15, " duplex offset: %d lines\n",s->duplex_raster_offset); - case OPT_THRESHOLD_CURVE: - *(SANE_Word *) val = scanner->dtc_threshold_curve; - return SANE_STATUS_GOOD; + DBG (10, "init_inquire: finish\n"); - case OPT_GRADATION: - switch (scanner->gradation) - { - case WD_gradation_ORDINARY: - strcpy (val, gradation_ordinary); - break; - case WD_gradation_HIGH: - strcpy (val, gradation_high); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + return 0; +} - case OPT_SMOOTHING_MODE: - switch (scanner->smoothing_mode) - { - case WD_smoothing_OCR: - strcpy (val, smoothing_mode_ocr); - break; - case WD_smoothing_IMAGE: - strcpy (val, smoothing_mode_image); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; +/* + * Use INQUIRY VPD to setup more detail about the scanner + */ +static SANE_Status +init_vpd (struct fujitsu *s) +{ + SANE_Status ret; + unsigned char buffer[0x64]; - case OPT_FILTERING: - switch (scanner->filtering) - { - case WD_filtering_BALLPOINT: - strcpy (val, filtering_ballpoint); - break; - case WD_filtering_ORDINARY: - strcpy (val, filtering_ordinary); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + DBG (10, "init_vpd: start\n"); - case OPT_BACKGROUND: - switch (scanner->background) - { - case WD_background_WHITE: - strcpy (val, background_white); - break; - case WD_background_BLACK: - strcpy (val, background_black); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + /* get EVPD */ + set_IN_return_size (inquiryB.cmd, 0x64); + set_IN_evpd (inquiryB.cmd, 1); + set_IN_page_code (inquiryB.cmd, 0xf0); - case OPT_MATRIX2X2: - *(SANE_Bool *) val = scanner->matrix2x2; - return SANE_STATUS_GOOD; + ret = do_cmd ( + s, 1, 0, + inquiryB.cmd, inquiryB.size, + NULL, 0, + buffer, 0x64 + ); - case OPT_MATRIX3X3: - *(SANE_Bool *) val = scanner->matrix3x3; - return SANE_STATUS_GOOD; + /* This scanner supports vital product data. + * Use this data to set dpi-lists etc. */ + if (ret == SANE_STATUS_GOOD) { - case OPT_MATRIX4X4: - *(SANE_Bool *) val = scanner->matrix4x4; - return SANE_STATUS_GOOD; + DBG (15, "standard options\n"); - case OPT_MATRIX5X5: - *(SANE_Bool *) val = scanner->matrix5x5; - return SANE_STATUS_GOOD; + s->basic_x_res = get_IN_basic_x_res (buffer); + DBG (15, " basic x res: %d dpi\n",s->basic_x_res); - case OPT_NOISE_REMOVAL: - *(SANE_Bool *) val = scanner->noise_removal; - return SANE_STATUS_GOOD; + s->basic_y_res = get_IN_basic_y_res (buffer); + DBG (15, " basic y res: %d dpi\n",s->basic_y_res); - case OPT_WHITE_LEVEL_FOLLOW: - switch (scanner->white_level_follow) - { - case WD_white_level_follow_DEFAULT: - strcpy (val, white_level_follow_default); - break; - case WD_white_level_follow_ENABLED: - strcpy (val, white_level_follow_enabled); - break; - case WD_white_level_follow_DISABLED: - strcpy (val, white_level_follow_disabled); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->step_x_res = get_IN_step_x_res (buffer); + DBG (15, " step x res: %d dpi\n", s->step_x_res); - case OPT_DTC_SELECTION: - switch (scanner->dtc_selection) - { - case WD_dtc_selection_DEFAULT: - strcpy (val, dtc_selection_default); - break; - case WD_dtc_selection_DYNAMIC: - strcpy (val, dtc_selection_dynamic); - break; - case WD_dtc_selection_SIMPLIFIED: - strcpy (val, dtc_selection_simplified); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->step_y_res = get_IN_step_y_res (buffer); + DBG (15, " step y res: %d dpi\n", s->step_y_res); - case OPT_PAPER_SIZE: - switch (scanner->paper_size) - { - case WD_paper_A3: - strcpy (val, paper_size_a3); - break; - case WD_paper_A4: - strcpy (val, paper_size_a4); - break; - case WD_paper_A5: - strcpy (val, paper_size_a5); - break; - case WD_paper_DOUBLE: - strcpy (val, paper_size_double); - break; - case WD_paper_LETTER: - strcpy (val, paper_size_letter); - break; - case WD_paper_B4: - strcpy (val, paper_size_b4); - break; - case WD_paper_B5: - strcpy (val, paper_size_b5); - break; - case WD_paper_LEGAL: - strcpy (val, paper_size_legal); - break; - case WD_paper_UNDEFINED: - if (scanner->paper_selection == WD_paper_UNDEFINED) - { - strcpy (val, paper_size_detect); - } - else - { - strcpy (val, paper_size_custom); - } - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->max_x_res = get_IN_max_x_res (buffer); + DBG (15, " max x res: %d dpi\n", s->max_x_res); - case OPT_PAPER_ORIENTATION: - switch (scanner->paper_orientation) - { - case WD_paper_PORTRAIT: - strcpy (val, paper_orientation_portrait); - break; - case WD_paper_LANDSCAPE: - strcpy (val, paper_orientation_landscape); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->max_y_res = get_IN_max_y_res (buffer); + DBG (15, " max y res: %d dpi\n", s->max_y_res); + s->min_x_res = get_IN_min_x_res (buffer); + DBG (15, " min x res: %d dpi\n", s->min_x_res); + s->min_y_res = get_IN_min_y_res (buffer); + DBG (15, " min y res: %d dpi\n", s->min_y_res); - case OPT_IMPRINTER: - *(SANE_Bool *) val = scanner->use_imprinter; - return SANE_STATUS_GOOD; + /* some scanners list B&W resolutions. */ + s->std_res_60 = get_IN_std_res_60 (buffer); + DBG (15, " 60 dpi: %d\n", s->std_res_60); - case OPT_IMPRINTER_DIR: - switch (scanner->imprinter_direction) - { - case S_im_dir_top_bottom: - strcpy( val, imprinter_dir_topbot); - break; - case S_im_dir_bottom_top: - strcpy( val, imprinter_dir_bottop); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->std_res_75 = get_IN_std_res_75 (buffer); + DBG (15, " 75 dpi: %d\n", s->std_res_75); - case OPT_IMPRINTER_YOFFSET: - *(SANE_Word *) val = scanner->imprinter_y_offset; - return SANE_STATUS_GOOD; + s->std_res_100 = get_IN_std_res_100 (buffer); + DBG (15, " 100 dpi: %d\n", s->std_res_100); - case OPT_IMPRINTER_STRING: - strncpy((SANE_String) val, scanner->imprinter_string, - max_imprinter_string_length); - return SANE_STATUS_GOOD; + s->std_res_120 = get_IN_std_res_120 (buffer); + DBG (15, " 120 dpi: %d\n", s->std_res_120); - case OPT_IMPRINTER_CTR_STEP: - *(SANE_Word *) val = scanner->imprinter_ctr_step; - return SANE_STATUS_GOOD; + s->std_res_150 = get_IN_std_res_150 (buffer); + DBG (15, " 150 dpi: %d\n", s->std_res_150); - case OPT_IMPRINTER_CTR_INIT: - *(SANE_Word *) val = scanner->imprinter_ctr_init; - return SANE_STATUS_GOOD; + s->std_res_160 = get_IN_std_res_160 (buffer); + DBG (15, " 160 dpi: %d\n", s->std_res_160); - case OPT_IMPRINTER_CTR_DIR: - switch (scanner->imprinter_ctr_dir) - { - case S_im_dir_inc: - strcpy( val, imprinter_ctr_dir_inc); - break; - case S_im_dir_dec: - strcpy( val, imprinter_ctr_dir_dec); - break; - default: - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->std_res_180 = get_IN_std_res_180 (buffer); + DBG (15, " 180 dpi: %d\n", s->std_res_180); + s->std_res_200 = get_IN_std_res_200 (buffer); + DBG (15, " 200 dpi: %d\n", s->std_res_200); + s->std_res_240 = get_IN_std_res_240 (buffer); + DBG (15, " 240 dpi: %d\n", s->std_res_240); + s->std_res_300 = get_IN_std_res_300 (buffer); + DBG (15, " 300 dpi: %d\n", s->std_res_300); - case OPT_START_BUTTON: - if (SANE_STATUS_GOOD == get_hardware_status (scanner)) - { - *(SANE_Bool *) val = get_HW_start_button (scanner->buffer); - } - else - { - *(SANE_Bool *) val = SANE_FALSE; - } - return SANE_STATUS_GOOD; + s->std_res_320 = get_IN_std_res_320 (buffer); + DBG (15, " 320 dpi: %d\n", s->std_res_320); + s->std_res_400 = get_IN_std_res_400 (buffer); + DBG (15, " 400 dpi: %d\n", s->std_res_400); - case OPT_SLEEP_MODE: - *(SANE_Word *) val = scanner->sleep_time; - return SANE_STATUS_GOOD; - - } - } - else if (action == SANE_ACTION_SET_VALUE) - { - DBG (10, "sane_control_option: set value \"%s\"\n", - scanner->opt[option].name); + s->std_res_480 = get_IN_std_res_480 (buffer); + DBG (15, " 480 dpi: %d\n", s->std_res_480); - if (!SANE_OPTION_IS_ACTIVE (cap)) - { - DBG (10, "\tinactive\n"); - return SANE_STATUS_INVAL; - } + s->std_res_600 = get_IN_std_res_600 (buffer); + DBG (15, " 600 dpi: %d\n", s->std_res_600); - if (!SANE_OPTION_IS_SETTABLE (cap)) - { - DBG (10, "\tnot settable\n"); - return SANE_STATUS_INVAL; - } + s->std_res_800 = get_IN_std_res_800 (buffer); + DBG (15, " 800 dpi: %d\n", s->std_res_800); - status = sanei_constrain_value (scanner->opt + option, val, info); - if (status != SANE_STATUS_GOOD) - { - DBG (10, "\tbad value\n"); - return status; - } + s->std_res_1200 = get_IN_std_res_1200 (buffer); + DBG (15, " 1200 dpi: %d\n", s->std_res_1200); - /* - * Note - for those options which can assume one of a list of - * valid values, we can safely assume that they will have - * exactly one of those values because that's what - * sanei_constrain_value does. Hence no "else: invalid" branches - * below. - */ - switch (option) - { - case OPT_SOURCE: - if (!strcmp (val, sourceADF)) - { - if (scanner->use_adf) - { - return SANE_STATUS_GOOD; - } - else if (scanner->has_adf) - { - scanner->use_adf = SANE_TRUE; - /* enable adf-related options. disregard the - * fact that not all scanners may understand these. */ - if (scanner->duplex_present) - { - scanner->opt[OPT_DUPLEX].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - - /* enable adf paper size selection */ - if (scanner->has_fixed_paper_size) - { - scanner->opt[OPT_PAPER_SIZE].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_PAPER_ORIENTATION].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - else - { - scanner->opt[OPT_PAGE_WIDTH].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_PAGE_HEIGHT].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - } - else - { - return SANE_STATUS_INVAL; - } - } - else - { - if (scanner->use_adf == SANE_FALSE) - { - return SANE_STATUS_GOOD; - } - /* flatbed disables duplex, paper size, paper orientation */ - scanner->use_adf = SANE_FALSE; - scanner->opt[OPT_DUPLEX].cap = SANE_CAP_INACTIVE; - scanner->duplex_mode = DUPLEX_FRONT; - scanner->opt[OPT_PAPER_SIZE].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_WIDTH].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_HEIGHT].cap = SANE_CAP_INACTIVE; - } - *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - return SANE_STATUS_GOOD; + /* maximum window width and length are reported in basic units.*/ + s->max_x_basic = get_IN_window_width(buffer); + DBG(15, " max width: %2.2f inches\n",(float)s->max_x_basic/s->basic_x_res); - case OPT_DUPLEX: - scanner->duplex_mode = (!strcmp (val, duplexFront)) ? DUPLEX_FRONT : - (!strcmp (val, duplexBack)) ? DUPLEX_BACK : DUPLEX_BOTH; - if ((scanner->duplex_mode != DUPLEX_FRONT) - && (!scanner->duplex_present)) - { - scanner->duplex_mode = DUPLEX_FRONT; - return SANE_STATUS_INVAL; - } - return SANE_STATUS_GOOD; + s->max_y_basic = get_IN_window_length(buffer); + DBG(15, " max length: %2.2f inches\n",(float)s->max_y_basic/s->basic_y_res); - case OPT_X_RES: - if (scanner->resolution_x == *(SANE_Word *) val) - { - return SANE_STATUS_GOOD; - } - scanner->resolution_x = (*(SANE_Word *) val); - /* When x resolution is set and resolutions are linked, - * set y resolution as well, making sure the value used - * is a valid y resolution. - */ - if (scanner->resolution_linked) - { - SANE_Word tmp = (*(SANE_Word *) val); - if (sanei_constrain_value - (scanner->opt + OPT_Y_RES, (void *) &tmp, - 0) == SANE_STATUS_GOOD) - { - scanner->resolution_y = tmp; - } - } - calculateDerivedValues (scanner); - *info |= SANE_INFO_RELOAD_PARAMS; - return SANE_STATUS_GOOD; + /* known modes */ + s->can_overflow = get_IN_overflow(buffer); + DBG (15, " overflow: %d\n", s->can_overflow); - case OPT_Y_RES: - if (scanner->resolution_y == *(SANE_Word *) val) - { - return SANE_STATUS_GOOD; - } - scanner->resolution_y = *(SANE_Word *) val; - scanner->resolution_linked = SANE_FALSE; - calculateDerivedValues (scanner); - *info |= SANE_INFO_RELOAD_PARAMS; - return SANE_STATUS_GOOD; + s->can_monochrome = get_IN_monochrome (buffer); + DBG (15, " monochrome: %d\n", s->can_monochrome); - case OPT_TL_X: - if (scanner->val[option].w == *(SANE_Word *) val) - { - return SANE_STATUS_GOOD; - } - scanner->val[option].w = *(SANE_Word *) val; - calculateDerivedValues (scanner); - if (scanner->rounded_top_left_x != scanner->val[option].w) - *info |= SANE_INFO_INEXACT; - *info |= SANE_INFO_RELOAD_PARAMS; - return SANE_STATUS_GOOD; + s->can_halftone = get_IN_half_tone (buffer); + DBG (15, " halftone: %d\n", s->can_halftone); - case OPT_TL_Y: - if (scanner->val[option].w == *(SANE_Word *) val) - { - return SANE_STATUS_GOOD; - } - scanner->val[option].w = *(SANE_Word *) val; - calculateDerivedValues (scanner); - if (scanner->rounded_top_left_y != scanner->val[option].w) - *info |= SANE_INFO_INEXACT; - *info |= SANE_INFO_RELOAD_PARAMS; - return SANE_STATUS_GOOD; + s->can_grayscale = get_IN_multilevel (buffer); + DBG (15, " grayscale: %d\n", s->can_grayscale); - case OPT_PAGE_WIDTH: - tmp = (SANE_Word *) val; - scanner->page_width = FIXED_MM_TO_SCANNER_UNIT (*tmp); - /* FIXME scanner->opt[OPT_TL_X].constraint.range->max = *tmp; */ - if (scanner->val[option].w > *tmp) - scanner->val[option].w = *tmp; - if (scanner->bottom_right_x > *tmp) - scanner->bottom_right_x = *tmp; - calculateDerivedValues (scanner); - *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - return SANE_STATUS_GOOD; + s->can_color_monochrome = get_IN_monochrome_rgb(buffer); + DBG (15, " color_monochrome: %d\n", s->can_color_monochrome); - case OPT_PAGE_HEIGHT: - tmp = (SANE_Word *) val; - scanner->page_height = FIXED_MM_TO_SCANNER_UNIT (*tmp); - /* FIXME scanner->opt[OPT_TL_Y].constraint.range->max = *tmp; */ - if (scanner->val[option].w > *tmp) - scanner->val[option].w = *tmp; - if (scanner->bottom_right_y > *tmp) - scanner->bottom_right_y = *tmp; - calculateDerivedValues (scanner); - *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - return SANE_STATUS_GOOD; + s->can_color_halftone = get_IN_half_tone_rgb(buffer); + DBG (15, " color_halftone: %d\n", s->can_color_halftone); - case OPT_BR_X: - if (scanner->bottom_right_x == *(SANE_Word *) val) - { - return SANE_STATUS_GOOD; - } - scanner->bottom_right_x = *(SANE_Word *) val; - calculateDerivedValues (scanner); - if (scanner->rounded_bottom_right_x != scanner->bottom_right_x) - *info |= SANE_INFO_INEXACT; - *info |= SANE_INFO_RELOAD_PARAMS; - return SANE_STATUS_GOOD; + s->can_color_grayscale = get_IN_multilevel_rgb (buffer); + DBG (15, " color_grayscale: %d\n", s->can_color_grayscale); - case OPT_BR_Y: - if (scanner->bottom_right_y == *(SANE_Word *) val) - { - return SANE_STATUS_GOOD; - } - scanner->bottom_right_y = *(SANE_Word *) val; - calculateDerivedValues (scanner); - if (scanner->rounded_bottom_right_y != scanner->bottom_right_y) - *info |= SANE_INFO_INEXACT; - *info |= SANE_INFO_RELOAD_PARAMS; - return SANE_STATUS_GOOD; + /* now we look at vendor specific data */ + if (get_IN_page_length (buffer) > 0x1C) { - case OPT_BRIGHTNESS: - scanner->brightness = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + DBG (15, "vendor options\n"); - case OPT_THRESHOLD: - scanner->threshold = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + s->has_operator_panel = get_IN_operator_panel(buffer); + DBG (15, " operator panel: %d\n", s->has_operator_panel); - case OPT_LAMP_COLOR: - scanner->lamp_color = (!strcmp (val, lampDefault)) ? LAMP_DEFAULT : - (!strcmp (val, lampRed)) ? LAMP_RED : - (!strcmp (val, lampGreen)) ? LAMP_GREEN : LAMP_BLUE; - return SANE_STATUS_GOOD; + s->has_barcode = get_IN_barcode(buffer); + DBG (15, " barcode: %d\n", s->has_barcode); - case OPT_DROPOUT_COLOR: - if (strcmp (val, dropout_color_default) == 0) - scanner->dropout_color = MSEL_dropout_DEFAULT; - else if (strcmp (val, dropout_color_red) == 0) - scanner->dropout_color = MSEL_dropout_RED; - else if (strcmp (val, dropout_color_green) == 0) - scanner->dropout_color = MSEL_dropout_GREEN; - else if (strcmp (val, dropout_color_blue) == 0) - scanner->dropout_color = MSEL_dropout_BLUE; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_imprinter = get_IN_imprinter(buffer); + DBG (15, " imprinter: %d\n", s->has_imprinter); - case OPT_MODE: - /* Since setting the color mode usually changes many other - * settings and available options and is very dependent on the - * scanner model, this is out-sourced... - */ - newMode = (!strcmp (val, mode_lineart)) ? MODE_LINEART : - (!strcmp (val, mode_halftone)) ? MODE_HALFTONE : - (!strcmp (val, mode_grayscale)) ? MODE_GRAYSCALE : MODE_COLOR; + s->has_duplex = get_IN_duplex(buffer); + DBG (15, " duplex: %d\n", s->has_duplex); - if (newMode == scanner->color_mode) - { - /* no changes - don't bother */ - return SANE_STATUS_GOOD; - } + s->has_transparency = get_IN_transparency(buffer); + DBG (15, " transparency: %d\n", s->has_transparency); - *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + s->has_flatbed = get_IN_flatbed(buffer); + DBG (15, " flatbed: %d\n", s->has_flatbed); - switch (scanner->model) - { - case MODEL_3091: - case MODEL_3092: - case MODEL_FI4x20: - return (setMode3091 (scanner, newMode)); - case MODEL_FORCE: - case MODEL_3093: - case MODEL_3096: - case MODEL_3097: - case MODEL_4097: - case MODEL_FI: - return (setMode3096 (scanner, newMode)); - } - return SANE_STATUS_INVAL; + s->has_adf = get_IN_adf(buffer); + DBG (15, " adf: %d\n", s->has_adf); - case OPT_BLUE_OFFSET: - scanner->blue_offset = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + s->adbits = get_IN_adbits(buffer); + DBG (15, " A/D bits: %d\n",s->adbits); - case OPT_GREEN_OFFSET: - scanner->green_offset = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + s->buffer_bytes = get_IN_buffer_bytes(buffer); + DBG (15, " buffer bytes: %d\n",s->buffer_bytes); - case OPT_USE_SWAPFILE: - scanner->use_temp_file = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + /* vendor added scsi command support */ + /* FIXME: there are more of these... */ + s->has_cmd_subwindow = get_IN_has_subwindow(buffer); + DBG (15, " subwindow cmd: %d\n", s->has_cmd_subwindow); - case OPT_MIRROR_IMAGE: - scanner->mirroring = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; + s->has_cmd_endorser = get_IN_has_endorser(buffer); + DBG (15, " endorser cmd: %d\n", s->has_cmd_endorser); - case OPT_NOISE_REMOVAL: - if (scanner->noise_removal == (*(SANE_Bool *) val)) - { - return SANE_STATUS_GOOD; - } + s->has_cmd_hw_status = get_IN_has_hw_status (buffer); + DBG (15, " hardware status cmd: %d\n", s->has_cmd_hw_status); - scanner->noise_removal = *(SANE_Bool *) val; + s->has_cmd_scanner_ctl = get_IN_has_scanner_ctl(buffer); + DBG (15, " scanner control cmd: %d\n", s->has_cmd_scanner_ctl); - if (!scanner->noise_removal) - { - scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_INACTIVE; - } - else - { - scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - } + /* get threshold, brightness and contrast ranges. */ + s->brightness_steps = get_IN_brightness_steps(buffer); + DBG (15, " brightness steps: %d\n", s->brightness_steps); - if (info) - *info |= SANE_INFO_RELOAD_OPTIONS; + s->threshold_steps = get_IN_threshold_steps(buffer); + DBG (15, " threshold steps: %d\n", s->threshold_steps); - return SANE_STATUS_GOOD; + s->contrast_steps = get_IN_contrast_steps(buffer); + DBG (15, " contrast steps: %d\n", s->contrast_steps); - case OPT_MATRIX2X2: - scanner->matrix2x2 = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; + /* dither/gamma patterns */ + s->num_internal_gamma = get_IN_num_gamma_internal (buffer); + DBG (15, " built in gamma patterns: %d\n", s->num_internal_gamma); - case OPT_MATRIX3X3: - scanner->matrix3x3 = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; + s->num_download_gamma = get_IN_num_gamma_download (buffer); + DBG (15, " download gamma patterns: %d\n", s->num_download_gamma); - case OPT_MATRIX4X4: - scanner->matrix4x4 = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; - - case OPT_MATRIX5X5: - scanner->matrix5x5 = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; - - case OPT_AUTOSEP: - scanner->auto_sep = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; - - case OPT_RIF: - scanner->reverse = *(SANE_Bool *) val; - return SANE_STATUS_GOOD; + s->num_internal_dither = get_IN_num_dither_internal (buffer); + DBG (15, " built in dither patterns: %d\n", s->num_internal_dither); - case OPT_OUTLINE_EXTRACTION: - if ((*(SANE_Bool *) val)) - { - scanner->outline = 0x80; - } - else - { - scanner->outline = 0x00; - } - return SANE_STATUS_GOOD; - - case OPT_COMPRESSION: - if (strcmp (val, cmp_none) == 0) - scanner->compress_type = WD_cmp_NONE; - else if (strcmp (val, cmp_mh) == 0) - scanner->compress_type = WD_cmp_MH; - else if (strcmp (val, cmp_mr) == 0) - scanner->compress_type = WD_cmp_MR; - else if (strcmp (val, cmp_mmr) == 0) - scanner->compress_type = WD_cmp_MMR; - else if (strcmp (val, cmp_jbig) == 0) - scanner->compress_type = WD_cmp_JBIG; - else if (strcmp (val, cmp_jpg_base) == 0) - scanner->compress_type = WD_cmp_JPG1; - else if (strcmp (val, cmp_jpg_ext) == 0) - scanner->compress_type = WD_cmp_JPG2; - else if (strcmp (val, cmp_jpg_indep) == 0) - scanner->compress_type = WD_cmp_JPG3; - else - return SANE_STATUS_INVAL; - - return SANE_STATUS_GOOD; + s->num_download_dither = get_IN_num_dither_download (buffer); + DBG (15, " download dither patterns: %d\n", s->num_download_dither); - case OPT_COMPRESSION_ARG: - - scanner->compress_arg = *(SANE_Word *) val; - return SANE_STATUS_GOOD; - - case OPT_EMPHASIS: - if (strcmp (val, emphasis_none) == 0) - scanner->emphasis = WD_emphasis_NONE; - else if (strcmp (val, emphasis_low) == 0) - scanner->emphasis = WD_emphasis_LOW; - else if (strcmp (val, emphasis_medium) == 0) - scanner->emphasis = WD_emphasis_MEDIUM; - else if (strcmp (val, emphasis_high) == 0) - scanner->emphasis = WD_emphasis_HIGH; - else if (strcmp (val, emphasis_smooth) == 0) - scanner->emphasis = WD_emphasis_SMOOTH; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + /* ipc functions */ + s->has_rif = get_IN_ipc_bw_rif (buffer); + DBG (15, " black and white rif: %d\n", s->has_rif); - case OPT_GAMMA: - if (strcmp (val, gamma_default) == 0) - scanner->gamma = WD_gamma_DEFAULT; - else if (strcmp (val, gamma_normal) == 0) - scanner->gamma = WD_gamma_NORMAL; - else if (strcmp (val, gamma_soft) == 0) - scanner->gamma = WD_gamma_SOFT; - else if (strcmp (val, gamma_sharp) == 0) - scanner->gamma = WD_gamma_SHARP; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_auto1 = get_IN_ipc_auto1(buffer); + DBG (15, " automatic binary DTC: %d\n", s->has_auto1); - case OPT_THRESHOLD_CURVE: - scanner->dtc_threshold_curve = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + s->has_auto2 = get_IN_ipc_auto2(buffer); + DBG (15, " simplified DTC: %d\n", s->has_auto2); - case OPT_GRADATION: - if (strcmp (val, gradation_ordinary) == 0) - scanner->gradation = WD_gradation_ORDINARY; - else if (strcmp (val, gradation_high) == 0) - scanner->gradation = WD_gradation_HIGH; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_outline = get_IN_ipc_outline_extraction (buffer); + DBG (15, " outline extraction: %d\n", s->has_outline); - case OPT_SMOOTHING_MODE: - if (strcmp (val, smoothing_mode_ocr) == 0) - scanner->smoothing_mode = WD_smoothing_OCR; - else if (strcmp (val, smoothing_mode_image) == 0) - scanner->smoothing_mode = WD_smoothing_IMAGE; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_emphasis = get_IN_ipc_image_emphasis (buffer); + DBG (15, " image emphasis: %d\n", s->has_emphasis); - case OPT_FILTERING: - if (strcmp (val, filtering_ballpoint) == 0) - scanner->filtering = WD_filtering_BALLPOINT; - else if (strcmp (val, filtering_ordinary) == 0) - scanner->filtering = WD_filtering_ORDINARY; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_autosep = get_IN_ipc_auto_separation (buffer); + DBG (15, " automatic separation: %d\n", s->has_autosep); - case OPT_BACKGROUND: - if (strcmp (val, background_white) == 0) - scanner->background = WD_background_WHITE; - else if (strcmp (val, background_black) == 0) - scanner->background = WD_background_BLACK; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_mirroring = get_IN_ipc_mirroring (buffer); + DBG (15, " mirror image: %d\n", s->has_mirroring); - case OPT_WHITE_LEVEL_FOLLOW: - if (strcmp (val, white_level_follow_default) == 0) - scanner->white_level_follow = WD_white_level_follow_DEFAULT; - else if (strcmp (val, white_level_follow_enabled) == 0) - scanner->white_level_follow = WD_white_level_follow_ENABLED; - else if (strcmp (val, white_level_follow_disabled) == 0) - scanner->white_level_follow = WD_white_level_follow_DISABLED; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->has_white_level_follow = get_IN_ipc_white_level_follow (buffer); + DBG (15, " white level follower: %d\n", s->has_white_level_follow); - case OPT_DTC_SELECTION: - if (strcmp (val, dtc_selection_default) == 0) - { - if (scanner->dtc_selection == WD_dtc_selection_DEFAULT) - return SANE_STATUS_GOOD; + s->has_subwindow = get_IN_ipc_subwindow (buffer); + DBG (15, " subwindow: %d\n", s->has_subwindow); - scanner->dtc_selection = WD_dtc_selection_DEFAULT; - } - else if (strcmp (val, dtc_selection_dynamic) == 0) - { - if (scanner->dtc_selection == WD_dtc_selection_DYNAMIC) - return SANE_STATUS_GOOD; + /* compression modes */ + s->has_comp_MH = get_IN_compression_MH (buffer); + DBG (15, " compression MH: %d\n", s->has_comp_MH); - scanner->dtc_selection = WD_dtc_selection_DYNAMIC; - } - else if (strcmp (val, dtc_selection_simplified) == 0) - { - if (scanner->dtc_selection == WD_dtc_selection_SIMPLIFIED) - return SANE_STATUS_GOOD; + s->has_comp_MR = get_IN_compression_MR (buffer); + DBG (15, " compression MR: %d\n", s->has_comp_MR); - scanner->dtc_selection = WD_dtc_selection_SIMPLIFIED; - } - else - { - return SANE_STATUS_INVAL; - } + s->has_comp_MMR = get_IN_compression_MMR (buffer); + DBG (15, " compression MMR: %d\n", s->has_comp_MMR); - if (scanner->dtc_selection != WD_dtc_selection_DYNAMIC) - { - scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_FILTERING].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_GRADATION].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_INACTIVE; - scanner->noise_removal = 0; - } - else - { - scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_FILTERING].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_GRADATION].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - } + s->has_comp_JBIG = get_IN_compression_JBIG (buffer); + DBG (15, " compression JBIG: %d\n", s->has_comp_JBIG); - switch (scanner->dtc_selection) - { - case WD_dtc_selection_DYNAMIC: + s->has_comp_JPG1 = get_IN_compression_JPG_BASE (buffer); + DBG (15, " compression JPG1: %d\n", s->has_comp_JPG1); - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_INACTIVE; - break; + s->has_comp_JPG2 = get_IN_compression_JPG_EXT (buffer); + DBG (15, " compression JPG2: %d\n", s->has_comp_JPG2); - case WD_dtc_selection_SIMPLIFIED: - scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE; - break; + s->has_comp_JPG3 = get_IN_compression_JPG_INDEP (buffer); + DBG (15, " compression JPG3: %d\n", s->has_comp_JPG3); + + } + /*FIXME no vendor vpd, set some defaults? */ + else{ + DBG (5, "init_vpd: Your scanner supports only partial VPD?\n"); + DBG (5, "init_vpd: Please contact anoah at pfeiffer dot edu\n"); + DBG (5, "init_vpd: with details of your scanner model.\n"); + ret = SANE_STATUS_INVAL; + } + } + /*FIXME no vpd, set some defaults? */ + else{ + DBG (5, "init_vpd: Your scanner does not support VPD?\n"); + DBG (5, "init_vpd: Please contact anoah at pfeiffer dot edu\n"); + DBG (5, "init_vpd: with details of your scanner model.\n"); + } - case WD_dtc_selection_DEFAULT: - default: - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_INACTIVE; - } + DBG (10, "init_vpd: finish\n"); - *info |= SANE_INFO_RELOAD_OPTIONS; - return SANE_STATUS_GOOD; + return ret; +} - case OPT_PAPER_SIZE: - if (strcmp (val, paper_size_a3) == 0) - { - if (scanner->paper_size == WD_paper_A3) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_A3; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_a4) == 0) - { - if (scanner->paper_size == WD_paper_A4) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_A4; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_a5) == 0) - { - if (scanner->paper_size == WD_paper_A5) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_A5; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_double) == 0) - { - if (scanner->paper_size == WD_paper_DOUBLE) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_DOUBLE; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_letter) == 0) - { - if (scanner->paper_size == WD_paper_LETTER) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_LETTER; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_b4) == 0) - { - if (scanner->paper_size == WD_paper_B4) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_B4; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_b5) == 0) - { - if (scanner->paper_size == WD_paper_B5) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_B5; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_legal) == 0) - { - if (scanner->paper_size == WD_paper_LEGAL) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_LEGAL; - fujitsu_set_standard_size (scanner); - } - else if (strcmp (val, paper_size_custom) == 0) - { - if (scanner->paper_size == WD_paper_UNDEFINED && - scanner->paper_selection == WD_paper_SEL_NON_STANDARD) - { - return SANE_STATUS_GOOD; - } - scanner->paper_size = WD_paper_UNDEFINED; - scanner->paper_selection = WD_paper_SEL_NON_STANDARD; - scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_WIDTH].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_PAGE_HEIGHT].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - *info |= SANE_INFO_RELOAD_PARAMS; - } - else if (strcmp (val, paper_size_detect) == 0) - { - if (scanner->paper_size == WD_paper_UNDEFINED && - scanner->paper_selection == WD_paper_SEL_UNDEFINED) - return SANE_STATUS_GOOD; - scanner->paper_size = WD_paper_UNDEFINED; - scanner->paper_orientation = WD_paper_PORTRAIT; - scanner->paper_selection = WD_paper_SEL_UNDEFINED; - scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_WIDTH].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_HEIGHT].cap = SANE_CAP_INACTIVE; - *info |= SANE_INFO_RELOAD_PARAMS; - } - else - { - return SANE_STATUS_INVAL; - } +/* + * set model specific info and good default user values + * in scanner struct. struct is already initialized to 0, + * so all the default 0 values dont need touching. + */ +static SANE_Status +init_model (struct fujitsu *s) +{ - *info |= SANE_INFO_RELOAD_OPTIONS; - return SANE_STATUS_GOOD; + DBG (10, "init_model: start\n"); + /* converted to native scanner unit of 1200dpi */ + /* FIXME: put these elsewhere, make them variable? */ + s->max_x = s->max_x_basic * 1200 / s->basic_x_res; + s->max_y = s->max_y_basic * 1200 / s->basic_y_res; + s->min_x=0; + s->min_y=0; - case OPT_PAPER_ORIENTATION: - if (strcmp (val, paper_orientation_portrait) == 0) - scanner->paper_orientation = WD_paper_PORTRAIT; - else if (strcmp (val, paper_orientation_landscape) == 0) - scanner->paper_orientation = WD_paper_LANDSCAPE; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + /* give defaults to all the user settings */ - case OPT_CONTRAST: - scanner->contrast = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + /* source */ + if(s->has_flatbed) + s->source = SOURCE_FLATBED; + else if(s->has_adf) + s->source = SOURCE_ADF_FRONT; - case OPT_VARIANCE_RATE: - scanner->var_rate_dyn_thresh = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + /* scan mode */ + if(s->can_monochrome) + s->mode = MODE_LINEART; + else if(s->can_halftone) + s->mode=MODE_HALFTONE; + else if(s->can_grayscale) + s->mode=MODE_GRAYSCALE; + else if(s->can_color_grayscale) + s->mode=MODE_COLOR; + + /*x res*/ + s->resolution_x = s->basic_x_res; + + /*y res*/ + s->resolution_y = s->basic_y_res; + if(s->resolution_y > s->resolution_x){ + s->resolution_y = s->resolution_x; + } + /* bottom-right x */ + s->br_x = 8.5 * 1200; + if(s->br_x > s->max_x){ + s->br_x = s->max_x; + } + /* bottom-right y */ + s->br_y = 11 * 1200; + if(s->br_y > s->max_y){ + s->br_y = s->max_y; + } + /* page width */ + s->page_width = s->br_x; - case OPT_IMPRINTER: + /* page height */ + s->page_height = s->br_y; - if (scanner->use_imprinter == *(SANE_Bool *) val) - { - return SANE_STATUS_GOOD; - } + /* ------------------------------------------------------ */ + /* load vars that cannot be gotten from vpd, based on model */ - scanner->use_imprinter = *(SANE_Bool *) val; + if (strstr (s->product_name, "M3091") + || strstr (s->product_name, "M3092")) { - imprinter(scanner); + s->gamma = 0; + s->has_rif=1; + + s->has_back = 0; + s->color_interlace = COLOR_INTERLACE_3091; + s->duplex_interlace = DUPLEX_INTERLACE_3091; + s->has_MS_dropout = 0; + s->has_SW_dropout = 1; + s->window_vid = 0xc0; + s->ghs_in_rs = 1; + + s->reverse_by_mode[MODE_LINEART] = 1; + s->reverse_by_mode[MODE_HALFTONE] = 1; + s->reverse_by_mode[MODE_GRAYSCALE] = 0; + s->reverse_by_mode[MODE_COLOR] = 0; + } + else if (strstr (s->product_name, "M309") + || strstr (s->product_name, "M409")){ - if (scanner->use_imprinter) - { - scanner->opt[OPT_IMPRINTER_DIR].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_IMPRINTER_YOFFSET].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_IMPRINTER_STRING].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_IMPRINTER_CTR_INIT].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_IMPRINTER_CTR_STEP].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - scanner->opt[OPT_IMPRINTER_CTR_DIR].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - } - else - { - scanner->opt[OPT_IMPRINTER_DIR].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_YOFFSET].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_STRING].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_CTR_INIT].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_CTR_STEP].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_CTR_DIR].cap = SANE_CAP_INACTIVE; - *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - } - return SANE_STATUS_GOOD; + s->gamma = 0; + + s->has_back = s->has_duplex; + s->color_interlace = COLOR_INTERLACE_NONE; + s->duplex_interlace = DUPLEX_INTERLACE_NONE; + s->has_MS_dropout = 0; + s->has_SW_dropout = 0; + s->window_vid = 0; + s->ghs_in_rs = 0; + + s->reverse_by_mode[MODE_LINEART] = 0; + s->reverse_by_mode[MODE_HALFTONE] = 0; + s->reverse_by_mode[MODE_GRAYSCALE] = 1; + s->reverse_by_mode[MODE_COLOR] = 0; + } + else if (strstr (s->product_name, "fi-4750") + || strstr (s->product_name, "fi-4340") ) { - case OPT_IMPRINTER_DIR: - if (strcmp (val, imprinter_dir_topbot) == 0) - scanner->imprinter_direction = S_im_dir_top_bottom; - else if (strcmp (val, imprinter_dir_bottop) == 0) - scanner->imprinter_direction = S_im_dir_bottom_top; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; + s->gamma = 0x00; - case OPT_IMPRINTER_YOFFSET: - scanner->imprinter_y_offset = *(SANE_Word *) val; - return SANE_STATUS_GOOD; + s->has_back = s->has_duplex; + s->color_interlace = COLOR_INTERLACE_RRGGBB; + s->duplex_interlace = DUPLEX_INTERLACE_NONE; + s->has_MS_dropout = 1; + s->has_SW_dropout = 0; + s->window_vid = 0; + s->ghs_in_rs = 0; + + s->reverse_by_mode[MODE_LINEART] = 0; + s->reverse_by_mode[MODE_HALFTONE] = 0; + s->reverse_by_mode[MODE_GRAYSCALE] = 1; + s->reverse_by_mode[MODE_COLOR] = 1; + } + /* some firmware versions use capital f? */ + else if (strstr (s->product_name, "Fi-5900") + || strstr (s->product_name, "fi-5900") ) { + + s->gamma = 0x00; + + s->has_back = s->has_duplex; + s->color_interlace = COLOR_INTERLACE_BGR; + s->duplex_interlace = DUPLEX_INTERLACE_NONE; + s->has_MS_dropout = 1; + s->has_SW_dropout = 0; + s->window_vid = 0; + s->ghs_in_rs = 0; + + s->reverse_by_mode[MODE_LINEART] = 0; + s->reverse_by_mode[MODE_HALFTONE] = 0; + s->reverse_by_mode[MODE_GRAYSCALE] = 1; + s->reverse_by_mode[MODE_COLOR] = 1; + } + else{ - case OPT_IMPRINTER_STRING: - strncpy(scanner->imprinter_string, (SANE_String) val, - max_imprinter_string_length); - - return SANE_STATUS_GOOD; + s->gamma = 0x80; - case OPT_IMPRINTER_CTR_STEP: - scanner->imprinter_ctr_step = (*(SANE_Word *) val); - return SANE_STATUS_GOOD; + s->has_back = s->has_duplex; + s->color_interlace = COLOR_INTERLACE_BGR; + s->duplex_interlace = DUPLEX_INTERLACE_NONE; + s->has_MS_dropout = 1; + s->has_SW_dropout = 0; + s->window_vid = 0; + s->ghs_in_rs = 0; + + s->reverse_by_mode[MODE_LINEART] = 0; + s->reverse_by_mode[MODE_HALFTONE] = 0; + s->reverse_by_mode[MODE_GRAYSCALE] = 1; + s->reverse_by_mode[MODE_COLOR] = 1; + } - case OPT_IMPRINTER_CTR_INIT: - if (scanner->imprinter_ctr_init == (*(SANE_Word *) val)) - { - return SANE_STATUS_GOOD; - } - scanner->imprinter_ctr_init = (*(SANE_Word *) val); - imprinter(scanner); + DBG (10, "init_model: finish\n"); - return SANE_STATUS_GOOD; + return SANE_STATUS_GOOD; +} - case OPT_IMPRINTER_CTR_DIR: - if (strcmp (val, imprinter_ctr_dir_inc) == 0) - scanner->imprinter_ctr_dir = S_im_dir_inc; - else if (strcmp (val, imprinter_ctr_dir_dec) == 0) - scanner->imprinter_ctr_dir = S_im_dir_dec; - else - return SANE_STATUS_INVAL; - return SANE_STATUS_GOOD; +/* + * This function presets the "option" array to blank + */ +static SANE_Status +init_options (struct fujitsu *s) +{ + int i; - case OPT_SLEEP_MODE: - scanner->sleep_time = (*(SANE_Word *) val); - /*fujitsu_set_sleep_mode(scanner);*/ - return SANE_STATUS_GOOD; + DBG (10, "init_options: start\n"); - } /* switch */ - } /* else */ - return SANE_STATUS_INVAL; -} + memset (s->opt, 0, sizeof (s->opt)); + for (i = 0; i < NUM_OPTIONS; ++i) { + s->opt[i].name = "filler"; + s->opt[i].size = sizeof (SANE_Word); + s->opt[i].cap = SANE_CAP_INACTIVE; + } -static void -fujitsu_set_standard_size (SANE_Handle handle) -{ - struct fujitsu *scanner = handle; + /* go ahead and setup the first opt, because + * frontend may call control_option on it + * before calling get_option_descriptor + */ + s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; - scanner->paper_selection = WD_paper_SEL_STANDARD; - scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_SOFT_DETECT - | SANE_CAP_SOFT_SELECT; - scanner->opt[OPT_PAGE_WIDTH].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_HEIGHT].cap = SANE_CAP_INACTIVE; + DBG (10, "init_options: finish\n"); - return; + return SANE_STATUS_GOOD; } - -/** - * Called by SANE when a page acquisition operation is to be started. - * +/* + * From the SANE spec: + * This function is used to establish a connection to a particular + * device. The name of the device to be opened is passed in argument + * name. If the call completes successfully, a handle for the device + * is returned in *h. As a special case, specifying a zero-length + * string as the device requests opening the first available device + * (if there is such a device). */ SANE_Status -sane_start (SANE_Handle handle) +sane_open (SANE_String_Const name, SANE_Handle * handle) { - struct fujitsu *scanner = handle; - int defaultFds[2]; - int duplexFds[2]; - int tempFile = -1; - int ret; - - DBG (10, "sane_start\n"); - DBG (10, "\tobject_count = %d\n", scanner->object_count); - DBG (10, "\tduplex_mode = %s\n", - (scanner->duplex_mode == - DUPLEX_BOTH) ? "DUPLEX_BOTH" : (scanner->duplex_mode == - DUPLEX_BACK) ? "DUPLEX_BACK" : - "DUPLEX_FRONT"); - DBG (10, "\tuse_temp_file = %s\n", - (scanner->use_temp_file == SANE_TRUE) ? "yes" : "no"); - - if ((scanner->object_count == 1) && (scanner->eof == SANE_TRUE) && - (scanner->duplex_mode == DUPLEX_BOTH)) - { - /* Proceed with rear page. No action necessary; reader process has - * already acquired the rear page and is only waiting to deliver it! - */ - - if (scanner->use_temp_file) - { - /* We're using a temp file for the duplex page, not a pipe. - * That means we have to wait until the subprocess has - * terminated - otherwise the file might be incomplete. - */ - int exit_status; - DBG (10, "sane_start: waiting for reader to terminate...\n"); - sanei_thread_waitpid (scanner->reader_pid, &exit_status); - DBG (10, "sane_start: reader process has terminated.\n"); - lseek (scanner->duplex_pipe_r, 0, SEEK_SET); - } - - scanner->object_count = 2; - scanner->eof = SANE_FALSE; - return SANE_STATUS_GOOD; - } + struct fujitsu *dev = NULL; + struct fujitsu *s = NULL; + SANE_Status ret; + + DBG (10, "sane_open: start\n"); - if (scanner->eof == SANE_TRUE) - { - scanner->object_count = 0; - } + if(fujitsu_devList){ + DBG (15, "sane_open: searching currently attached scanners\n"); + } + else{ + DBG (15, "sane_open: no scanners currently attached, attaching\n"); - if (scanner->object_count != 0) - { - DBG (5, "sane_start: device busy\n"); - return SANE_STATUS_DEVICE_BUSY; + ret = find_scanners(); + if(ret != SANE_STATUS_GOOD){ + return ret; } + } - if (scanner->sfd < 0) - { - /* first call */ - if (scanner->connection == SANE_FUJITSU_USB) { - DBG (10, "sane_start opening USB device\n"); - if (sanei_usb_open (scanner->sane.name, &(scanner->sfd)) != - SANE_STATUS_GOOD) { - DBG (MSG_ERR, - "sane_start: open of %s failed:\n", scanner->sane.name); - return SANE_STATUS_INVAL; - } - } else if (scanner->connection == SANE_FUJITSU_SCSI) { - DBG (10, "sane_start opening SCSI device\n"); - if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd), - scsi_sense_handler, 0) != SANE_STATUS_GOOD) { - DBG (MSG_ERR, - "sane_start: open of %s failed:\n", scanner->sane.name); - return SANE_STATUS_INVAL; - } + if(name[0] == 0){ + DBG (15, "sane_open: no device requested, using default\n"); + s = fujitsu_devList; + } + else{ + DBG (15, "sane_open: device %s requested\n", name); + + for (dev = fujitsu_devList; dev; dev = dev->next) { + if (strcmp (dev->sane.name, name) == 0) { + s = dev; + break; } } - scanner->object_count = 1; - scanner->eof = SANE_FALSE; + } - if ((ret = grab_scanner (scanner))) - { - DBG (5, "sane_start: unable to reserve scanner\n"); - if (scanner->connection == SANE_FUJITSU_USB) { - sanei_usb_close (scanner->sfd); - } else if (scanner->connection == SANE_FUJITSU_SCSI) { - sanei_scsi_close (scanner->sfd); - } - scanner->object_count = 0; - scanner->sfd = -1; - return ret; - } + if (!s) { + DBG (5, "sane_open: no device found\n"); + return SANE_STATUS_INVAL; + } - fujitsu_set_sleep_mode(scanner); + DBG (15, "sane_open: device %s found\n", s->sane.name); + *handle = s; - if (set_mode_params (scanner)) - { + /* connect the fd so we can talk to scanner */ + ret = connect_fd(s); + if(ret != SANE_STATUS_GOOD){ + return ret; + } - DBG (MSG_ERR, "sane_start: ERROR: failed to set mode\n"); - /* ignore it */ - } + DBG (10, "sane_open: finish\n"); - if ((ret = fujitsu_send(scanner))) - { - DBG (5, "sane_start: ERROR: failed to start send command\n"); - do_cancel(scanner); - return ret; - } -#if 0 - if ((ret = imprinter(scanner))) - { - DBG (5, "sane_start: ERROR: failed to start imprinter command\n"); - do_cancel(Scanner); - return ret; - } -#endif + return SANE_STATUS_GOOD; +} - if (scanner->use_adf == SANE_TRUE && - (ret = object_position (scanner, SANE_TRUE))) - { - DBG (5, "sane_start: WARNING: ADF empty\n"); - do_cancel(scanner); - return ret; - } +/* + * @@ Section 3 - SANE Options functions + */ - /* swap_res ?? */ +/* + * Returns the options we know. + * + * From the SANE spec: + * This function is used to access option descriptors. The function + * returns the option descriptor for option number n of the device + * represented by handle h. Option number 0 is guaranteed to be a + * valid option. Its value is an integer that specifies the number of + * options that are available for device handle h (the count includes + * option 0). If n is not a valid option index, the function returns + * NULL. The returned option descriptor is guaranteed to remain valid + * (and at the returned address) until the device is closed. + */ +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + struct fujitsu *s = handle; + int i; + SANE_Option_Descriptor *opt = &s->opt[option]; + DBG (20, "sane_get_option_descriptor: %d\n", option); - if ((ret = setWindowParam (scanner))) - { - DBG (5, "sane_start: ERROR: failed to set window\n"); - do_cancel(scanner); - return ret; - } + if ((unsigned) option >= NUM_OPTIONS) + return NULL; - calculateDerivedValues (scanner); + /* "Mode" group -------------------------------------------------------- */ + if(option==OPT_MODE_GROUP){ + opt->title = "Scan Mode"; + opt->desc = ""; + opt->type = SANE_TYPE_GROUP; + opt->constraint_type = SANE_CONSTRAINT_NONE; + } - DBG (10, "\tbytes per line = %d\n", scanner->bytes_per_scan_line); - DBG (10, "\tpixels_per_line = %d\n", scanner->scan_width_pixels); - DBG (10, "\tlines = %d\n", scanner->scan_height_pixels); - DBG (10, "\tbrightness (halftone) = %d\n", scanner->brightness); - DBG (10, "\tthreshold (line art) = %d\n", scanner->threshold); + /* source */ + if(option==OPT_SOURCE){ + i=0; + if(s->has_flatbed){ + s->source_list[i++]=source_Flatbed; + } + if(s->has_adf){ + s->source_list[i++]=source_ADFFront; + + if(s->has_back){ + s->source_list[i++]=source_ADFBack; + } + if(s->has_duplex){ + s->source_list[i++]=source_ADFDuplex; + } + } + s->source_list[i]=NULL; + opt->name = SANE_NAME_SCAN_SOURCE; + opt->title = SANE_TITLE_SCAN_SOURCE; + opt->desc = SANE_DESC_SCAN_SOURCE; + opt->type = SANE_TYPE_STRING; + opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; + opt->constraint.string_list = s->source_list; + opt->size = maxStringSize (opt->constraint.string_list); + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } - ret = start_scan (scanner); - if (ret != SANE_STATUS_GOOD) - { - DBG (MSG_ERR, "start_scan failed"); - return ret; + /* scan mode */ + if(option==OPT_MODE){ + i=0; + if(s->can_monochrome){ + s->mode_list[i++]=mode_Lineart; } - - /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */ - if (pipe (defaultFds) < 0) - { - DBG (MSG_ERR, "ERROR: could not create pipe\n"); - scanner->object_count = 0; - do_cancel(scanner); - return SANE_STATUS_IO_ERROR; + if(s->can_halftone){ + s->mode_list[i++]=mode_Halftone; } - - duplexFds[0] = duplexFds[1] = -1; - if (scanner->duplex_mode == DUPLEX_BOTH) - { - if (scanner->use_temp_file) - { - if ((tempFile = makeTempFile ()) == -1) - { - DBG (MSG_ERR, "ERROR: could not create temporary file.\n"); - scanner->object_count = 0; - do_cancel (scanner); - return SANE_STATUS_IO_ERROR; - } - } - else - { - if (pipe (duplexFds) < 0) - { - DBG (MSG_ERR, "ERROR: could not create duplex pipe.\n"); - scanner->object_count = 0; - do_cancel (scanner); - return SANE_STATUS_IO_ERROR; - } - } - } - - ret = SANE_STATUS_GOOD; - - scanner->default_pipe_r = defaultFds[0]; - scanner->default_pipe_w = defaultFds[1]; - scanner->duplex_pipe_r = (tempFile != -1) ? tempFile : duplexFds[0]; - scanner->duplex_pipe_w = (tempFile != -1) ? tempFile : duplexFds[1]; - - scanner->reader_pid = sanei_thread_begin (reader_process, scanner); - - if (scanner->reader_pid == -1) - { - DBG(MSG_ERR, "cannot fork reader process.\n"); - DBG(MSG_ERR, "%s", strerror(errno)); - ret = SANE_STATUS_IO_ERROR; - } - - if (sanei_thread_is_forked()) - { - close (scanner->default_pipe_w); - if (scanner->duplex_pipe_w != -1) - close (scanner->duplex_pipe_w); - } - - if (ret == SANE_STATUS_GOOD) - { - DBG (10, "sane_start: ok\n"); - } - - return ret; -} - - -/** - * Called by SANE to retrieve information about the type of data - * that the current scan will return. - * - * From the SANE spec: - * This function is used to obtain the current scan parameters. The - * returned parameters are guaranteed to be accurate between the time - * a scan has been started (sane_start() has been called) and the - * completion of that request. Outside of that window, the returned - * values are best-effort estimates of what the parameters will be - * when sane_start() gets invoked. - * - * Calling this function before a scan has actually started allows, - * for example, to get an estimate of how big the scanned image will - * be. The parameters passed to this function are the handle h of the - * device for which the parameters should be obtained and a pointer p - * to a parameter structure. - */ -SANE_Status -sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) -{ - struct fujitsu *scanner = (struct fujitsu *) handle; - - DBG (10, "sane_get_parameters\n"); - - calculateDerivedValues (scanner); - - if (scanner->color_mode == MODE_COLOR) - { - params->format = SANE_FRAME_RGB; - params->depth = 8; /* internally we treat this as 24 */ - } - else - { - params->format = SANE_FRAME_GRAY; - params->depth = scanner->output_depth; - } - - params->pixels_per_line = scanner->scan_width_pixels; - params->lines = scanner->scan_height_pixels; - - /* The internal "bytes per scan line" value reflects what we get - * from the scanner. If the output depth differs from the scanner - * depth, we have to modify the value for the frontend accordingly. - * - * This is currently ineffective since scanner_depth will always equal - * output_depth. - */ - params->bytes_per_line = scanner->bytes_per_scan_line * - scanner->scanner_depth / scanner->output_depth; - - params->last_frame = 1; - DBG (10, "\tdepth %d\n", params->depth); - DBG (10, "\tlines %d\n", params->lines); - DBG (10, "\tpixels_per_line %d\n", params->pixels_per_line); - DBG (10, "\tbytes_per_line %d\n", params->bytes_per_line); - return SANE_STATUS_GOOD; -} - - -/** - * Called by SANE to read data. - * - * In this implementation, sane_read does nothing much besides reading - * data from a pipe and handing it back. On the other end of the pipe - * there's the reader process which gets data from the scanner and - * stuffs it into the pipe. - * - * From the SANE spec: - * This function is used to read image data from the device - * represented by handle h. Argument buf is a pointer to a memory - * area that is at least maxlen bytes long. The number of bytes - * returned is stored in *len. A backend must set this to zero when - * the call fails (i.e., when a status other than SANE_STATUS_GOOD is - * returned). - * - * When the call succeeds, the number of bytes returned can be - * anywhere in the range from 0 to maxlen bytes. - */ -SANE_Status -sane_read (SANE_Handle handle, SANE_Byte * buf, - SANE_Int max_len, SANE_Int * len) -{ - struct fujitsu *scanner = (struct fujitsu *) handle; - ssize_t nread; - int source; - - *len = 0; - - /* Since we may be operating duplex - check where we're supposed to - * read the data from - */ - switch (scanner->object_count) - { - case 1: - source = scanner->default_pipe_r; /* this is always a pipe */ - break; - case 2: - source = scanner->duplex_pipe_r; /* this may be a pipe or a file */ - break; - default: - return do_cancel (scanner); - } - DBG (30, "sane_read, object_count=%d\n", scanner->object_count); - - nread = read (source, buf, max_len); - DBG (30, "sane_read: read %ld bytes of %ld\n", - (long) nread, (long) max_len); - - if (nread < 0) - { - if (errno == EAGAIN) - { - return SANE_STATUS_GOOD; - } - else - { - do_cancel (scanner); - return SANE_STATUS_IO_ERROR; - } - } - - *len = nread; - - if (nread == 0) - { - close (source); - DBG (10, "sane_read: pipe closed\n"); - scanner->eof = SANE_TRUE; - return SANE_STATUS_EOF; - } - - return SANE_STATUS_GOOD; -} /* sane_read */ - - -/** - * Cancels a scan. - * - * It has been said on the mailing list that sane_cancel is a bit of a - * misnomer because it is routinely called to signal the end of a - * batch - quoting David Mosberger-Tang: - * - * > In other words, the idea is to have sane_start() be called, and - * > collect as many images as the frontend wants (which could in turn - * > consist of multiple frames each as indicated by frame-type) and - * > when the frontend is done, it should call sane_cancel(). - * > Sometimes it's better to think of sane_cancel() as "sane_stop()" - * > but that name would have had some misleading connotations as - * > well, that's why we stuck with "cancel". - * - * The current consensus regarding duplex and ADF scans seems to be - * the following call sequence: sane_start; sane_read (repeat until - * EOF); sane_start; sane_read... and then call sane_cancel if the - * batch is at an end. I.e. do not call sane_cancel during the run but - * as soon as you get a SANE_STATUS_NO_DOCS. - * - * From the SANE spec: - * This function is used to immediately or as quickly as possible - * cancel the currently pending operation of the device represented by - * handle h. This function can be called at any time (as long as - * handle h is a valid handle) but usually affects long-running - * operations only (such as image is acquisition). It is safe to call - * this function asynchronously (e.g., from within a signal handler). - * It is important to note that completion of this operaton does not - * imply that the currently pending operation has been cancelled. It - * only guarantees that cancellation has been initiated. Cancellation - * completes only when the cancelled call returns (typically with a - * status value of SANE_STATUS_CANCELLED). Since the SANE API does - * not require any other operations to be re-entrant, this implies - * that a frontend must not call any other operation until the - * cancelled operation has returned. - */ -void -sane_cancel (SANE_Handle h) -{ - DBG (10, "sane_cancel\n"); - do_cancel ((struct fujitsu *) h); -} - - -/** - * Ends use of the scanner. - * - * From the SANE spec: - * This function terminates the association between the device handle - * passed in argument h and the device it represents. If the device is - * presently active, a call to sane_cancel() is performed first. After - * this function returns, handle h must not be used anymore. - */ -void -sane_close (SANE_Handle handle) -{ - DBG (10, "sane_close\n"); - if (((struct fujitsu *) handle)->object_count != 0) - { - do_reset (handle); - do_cancel (handle); - } -} - - -/** - * Terminates the backend. - * - * From the SANE spec: - * This function must be called to terminate use of a backend. The - * function will first close all device handles that still might be - * open (it is recommended to close device handles explicitly through - * a call to sane_clo-se(), but backends are required to release all - * resources upon a call to this function). After this function - * returns, no function other than sane_init() may be called - * (regardless of the status value returned by sane_exit(). Neglecting - * to call this function may result in some resources not being - * released properly. - */ -void -sane_exit (void) -{ - struct fujitsu *dev, *next; - - DBG (10, "sane_exit\n"); - - for (dev = first_dev; dev; dev = next) - { - next = dev->next; - free (dev->devicename); - free (dev->buffer); - free (dev); - } - - if (devlist) - free (devlist); -} - - -/* - * @@ Section 3 - generic routines which don't distinguish between models - */ - - -/** - * - * - */ -static SANE_Status -attachScanner (const char *devicename, struct fujitsu **devp) -{ - struct fujitsu *dev; - SANE_Int sfd; - - DBG (15, "attach_scanner: %s\n", devicename); - - for (dev = first_dev; dev; dev = dev->next) - { - if (strcmp (dev->sane.name, devicename) == 0) - { - if (devp) - { - *devp = dev; - } - DBG (5, "attach_scanner: scanner already attached (is ok)!\n"); - return SANE_STATUS_GOOD; - } - } - - DBG (15, "attach_scanner: opening %s\n", devicename); - if (mostRecentConfigConnectionType == SANE_FUJITSU_USB) { - DBG (15, "attachScanner opening USB device\n"); - if (sanei_usb_open (devicename, &sfd) != SANE_STATUS_GOOD) { - DBG (5, "attach_scanner: open failed\n"); - return SANE_STATUS_INVAL; - } - } else if (mostRecentConfigConnectionType == SANE_FUJITSU_SCSI) { - DBG (15, "attachScanner opening SCSI device\n"); - if (sanei_scsi_open (devicename, &sfd, scsi_sense_handler, 0) != 0) { - DBG (5, "attach_scanner: open failed\n"); - return SANE_STATUS_INVAL; - } - } - - if (NULL == (dev = malloc (sizeof (*dev)))) - return SANE_STATUS_NO_MEM; - memset(dev,0,sizeof(*dev)); - - dev->scsi_buf_size = scsiBuffer; - - if ((dev->buffer = malloc (dev->scsi_buf_size)) == NULL) - return SANE_STATUS_NO_MEM; - - memset(dev->buffer,0,sizeof(dev->scsi_buf_size)); - - dev->devicename = strdup (devicename); - dev->connection = mostRecentConfigConnectionType; - dev->sfd = sfd; - - /* - * Now query the device and find out what it is, exactly. - */ - if (identify_scanner (dev) != 0) - { - DBG (5, "attach_scanner: scanner identification failed\n"); - if (dev->connection == SANE_FUJITSU_USB) { - sanei_usb_close (dev->sfd); - } else if (dev->connection == SANE_FUJITSU_SCSI) { - sanei_scsi_close (dev->sfd); - } - free (dev->buffer); - free (dev); - return SANE_STATUS_INVAL; - } - - /* Why? */ - if (dev->connection == SANE_FUJITSU_USB) { - sanei_usb_close (dev->sfd); - } else if (dev->connection == SANE_FUJITSU_SCSI) { - sanei_scsi_close (dev->sfd); - } - dev->sfd = -1; - - dev->sane.name = dev->devicename; - dev->sane.vendor = dev->vendorName; - dev->sane.model = dev->productName; - dev->sane.type = "scanner"; - - ++num_devices; - dev->next = first_dev; - first_dev = dev; - - if (devp) - { - *devp = dev; - } - - DBG (15, "attach_scanner: done\n"); - - return SANE_STATUS_GOOD; -} - -/** - * - * - * - */ -static SANE_Status -attachOne (const char *name) -{ - return attachScanner (name, 0); -} - -/** - * Called from within the SANE SCSI core if there's trouble with the device. - * Responsible for producing a meaningful debug message. - */ -static SANE_Status -scsi_sense_handler (int scsi_fd, u_char * sensed_data, void *arg) -{ - unsigned int ret = SANE_STATUS_IO_ERROR; - unsigned int sense = get_RS_sense_key (sensed_data); - unsigned int asc = get_RS_ASC (sensed_data); - unsigned int ascq = get_RS_ASCQ (sensed_data); - - scsi_fd = scsi_fd; - arg = arg; /* get rid of compiler warnings */ - - switch (sense) - { - case 0x0: /* No Sense */ - DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq); - if ( (current_scanner != NULL) && (get_RS_EOM (sensed_data)) ) { - current_scanner->i_transfer_length = get_RS_information (sensed_data); - return SANE_STATUS_EOF; - } - return SANE_STATUS_GOOD; - - case 0x2: /* Not Ready */ - if ((0x00 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Not Ready \n", sense, asc, ascq); - } - else - { - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - break; - - case 0x3: /* Medium Error */ - if ((0x80 == asc) && (0x01 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Jam \n", sense, asc, ascq); - ret = SANE_STATUS_JAMMED; - } - else if ((0x80 == asc) && (0x02 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: ADF cover open \n", sense, asc, ascq); - ret = SANE_STATUS_COVER_OPEN; - } - else if ((0x80 == asc) && (0x03 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: ADF empty \n", sense, asc, ascq); - ret = SANE_STATUS_NO_DOCS; - } - else if ((0x80 == asc) && (0x04 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Special-purpose paper detection\n", - sense, asc, ascq); - } - else if ((0x80 == asc) && (0x07 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Double feed error\n", - sense, asc, ascq); - } - else if ((0x80 == asc) && (0x10 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: No Ink Cartridge is mounted\n", - sense, asc, ascq); - } - else if ((0x80 == asc) && (0x13 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: temporary lack of data\n", - sense, asc, ascq); - ret = SANE_STATUS_DEVICE_BUSY; - } - else if ((0x80 == asc) && (0x14 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Endorser paper detection failure\n", - sense, asc, ascq); - } - else - { - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - break; - - case 0x4: /* Hardware Error */ - if ((0x80 == asc) && (0x01 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: FB motor fuse \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x02 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: heater fuse \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x04 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: ADF motor fuse \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x05 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: mechanical alarm \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x06 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: optical alarm \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x07 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: FAN error \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x08 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: abnormal option(IPC) \n", - sense, asc, ascq); - } - else if ((0x80 == asc) && (0x10 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: imprinter error \n", sense, asc, ascq); - } - else if ((0x44 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: abnormal internal target \n", - sense, asc, ascq); - } - else if ((0x47 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: SCSI parity error \n", - sense, asc, ascq); - } - else - { - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - break; - - case 0x5: /* Illegal Request */ - if ((0x00 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Page end is detected before reading\n", - sense, asc, ascq); - ret = SANE_STATUS_INVAL; - } - else if ((0x1a == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Parameter list error \n", - sense, asc, ascq); - ret = SANE_STATUS_INVAL; - } - else if ((0x20 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Invalid command \n", sense, asc, ascq); - ret = SANE_STATUS_INVAL; - } - else if ((0x24 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Invalid field in CDB \n", - sense, asc, ascq); - ret = SANE_STATUS_INVAL; - } - else if ((0x25 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Unsupported logical unit \n", - sense, asc, ascq); - ret = SANE_STATUS_UNSUPPORTED; - } - else if ((0x26 == asc) && (0x00 == ascq)) - { - - DBG (MSG_ERR, "\t%d/%d/%d: Invalid field in parm list \n", - sense, asc, ascq); - /*hexdump (MSG_IO, "Sense", sensed_data, sensed_data[7]+8);*/ - if (sensed_data[7]+8 >=17) - { - - DBG(MSG_ERR, - "offending byte is %x. (Byte %x in window descriptor block)\n", - get_RS_offending_byte(sensed_data), - get_RS_offending_byte(sensed_data)-8); - } - - ret = SANE_STATUS_INVAL; - } - else if ((0x2C == asc) && (0x02 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: wrong window combination \n", - sense, asc, ascq); - ret = SANE_STATUS_INVAL; - } - else - { - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - break; - - case 0x6: /* Unit Attention */ - if ((0x00 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: UNIT ATTENTION \n", sense, asc, ascq); - } - else - { - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - break; - - case 0xb: /* Aborted Command */ - if ((0x43 == asc) && (0x00 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Message error \n", sense, asc, ascq); - } - else if ((0x80 == asc) && (0x01 == ascq)) - { - DBG (MSG_ERR, "\t%d/%d/%d: Image transfer error \n", - sense, asc, ascq); - } - else - { - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - break; - - default: - DBG (MSG_ERR, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", - sense, asc, ascq); - } - return ret; -} - - -/** - * Sends an INQUIRY command to the device and stores the response in the - * struct buffer. - */ -static void -do_inquiry (struct fujitsu *s) -{ - int i, tries = (s->connection == SANE_FUJITSU_USB) ? 5 : 1; - size_t res; - - DBG (10, "do_inquiry\n"); - - memset (s->buffer, '\0', 256); /* clear buffer */ - set_IN_return_size (inquiryB.cmd, 96); - set_IN_evpd (inquiryB.cmd, 0); - set_IN_page_code (inquiryB.cmd, 0); - - hexdump (MSG_IO, "inquiry", inquiryB.cmd, inquiryB.size); - - for (i = 0; i < tries; i++) - { - DBG(10, "try inquiry %d\n", i); - if ( (do_cmd (s->connection, s->sfd, inquiryB.cmd, inquiryB.size, - s->buffer, 96, &res) == SANE_STATUS_GOOD) - && (res >= 96) ) - { - break; - } - usleep(100000L); - } -} - -static SANE_Status -get_hardware_status (struct fujitsu *s) -{ - SANE_Status ret; - DBG (10, "get_hardware_status\n"); - - memset (s->buffer, '\0', 256); /* clear buffer */ - set_HW_allocation_length (hw_statusB.cmd, 10); - - if (s->sfd < 0) - { - int sfd; - - if (s->connection == SANE_FUJITSU_USB) { - DBG (10, "get_hardware_status opening USB device\n"); - if (sanei_usb_open (s->devicename, &sfd) != SANE_STATUS_GOOD) { - DBG (5, "get_hardware_status: open failed\n"); - return SANE_STATUS_INVAL; - } - } else if (s->connection == SANE_FUJITSU_SCSI) { - DBG (10, "get_hardware_status opening SCSI device\n"); - if (sanei_scsi_open (s->devicename, &sfd, scsi_sense_handler, 0)!=0) { - DBG (5, "get_hardware_status: open failed\n"); - return SANE_STATUS_INVAL; - } - } - hexdump (MSG_IO, "get_hardware_status", - hw_statusB.cmd, hw_statusB.size); - - ret = do_cmd (s->connection, sfd, hw_statusB.cmd, hw_statusB.size, - s->buffer, 10, NULL); - if (s->connection == SANE_FUJITSU_USB) { - sanei_usb_close (sfd); - } else if (s->connection == SANE_FUJITSU_SCSI) { - sanei_scsi_close (sfd); - } - } - else - { - hexdump (MSG_IO, "get_hardware_status", - hw_statusB.cmd, hw_statusB.size); - - ret = do_cmd (s->connection, s->sfd, hw_statusB.cmd, hw_statusB.size, - s->buffer, 10, NULL); - } - - if (ret == SANE_STATUS_GOOD) - { - DBG (1, "B5 %d\n", get_HW_B5_present (s->buffer)); - DBG (1, "A4 %d \n", get_HW_A4_present (s->buffer)); - DBG (1, "B4 %d \n", get_HW_B4_present (s->buffer)); - DBG (1, "A3 %d \n", get_HW_A3_present (s->buffer)); - DBG (1, "HE %d\n", get_HW_adf_empty (s->buffer)); - DBG (1, "OMR %d\n", get_HW_omr (s->buffer)); - DBG (1, "ADFC %d\n", get_HW_adfc_open (s->buffer)); - DBG (1, "SLEEP %d\n", get_HW_sleep (s->buffer)); - DBG (1, "MF %d\n", get_HW_manual_feed (s->buffer)); - DBG (1, "Start %d\n", get_HW_start_button (s->buffer)); - DBG (1, "Ink empty %d\n", get_HW_ink_empty (s->buffer)); - DBG (1, "DFEED %d\n", get_HW_dfeed_detected (s->buffer)); - DBG (1, "SKEW %d\n", get_HW_skew_angle (s->buffer)); - } - - return ret; -} - -static SANE_Status -getVitalProductData (struct fujitsu *s) -{ - SANE_Status ret; - - DBG (10, "get_vital_product_data\n"); - - memset (s->buffer, '\0', 256); /* clear buffer */ - set_IN_return_size (inquiryB.cmd, 0x64); - set_IN_evpd (inquiryB.cmd, 1); - set_IN_page_code (inquiryB.cmd, 0xf0); - - hexdump (MSG_IO, "get_vital_product_data", inquiryB.cmd, inquiryB.size); - ret = do_cmd (s->connection, s->sfd, inquiryB.cmd, inquiryB.size, - s->buffer, 0x64, NULL); - if (ret == SANE_STATUS_GOOD) - { - DBG (MSG_INFO, "standard options\n"); - DBG (MSG_INFO, " basic x res: %d dpi\n",get_IN_basic_x_res (s->buffer)); - DBG (MSG_INFO, " basic y res: %d dpi\n",get_IN_basic_y_res (s->buffer)); - DBG (MSG_INFO, " step x res %d dpi\n", get_IN_step_x_res (s->buffer)); - DBG (MSG_INFO, " step y res %d dpi\n", get_IN_step_y_res (s->buffer)); - DBG (MSG_INFO, " max x res %d dpi\n", get_IN_max_x_res (s->buffer)); - DBG (MSG_INFO, " max y res %d dpi\n", get_IN_max_y_res (s->buffer)); - DBG (MSG_INFO, " min x res %d dpi\n", get_IN_min_x_res (s->buffer)); - DBG (MSG_INFO, " max y res %d dpi\n", get_IN_min_y_res (s->buffer)); - DBG (MSG_INFO, " window width %3.2f cm\n", - (double) get_IN_window_width (s->buffer)/ - (double) get_IN_basic_x_res(s->buffer) * 2.54); - DBG (MSG_INFO, " window length %3.2f cm\n", - (double) get_IN_window_length (s->buffer)/ - (double) get_IN_basic_y_res(s->buffer) * 2.54); - - DBG (MSG_INFO, "functions:\n"); - DBG (MSG_INFO, " binary scanning: %d\n", - get_IN_monochrome (s->buffer)); - DBG (MSG_INFO, " gray scanning: %d\n", - get_IN_multilevel (s->buffer)); - DBG (MSG_INFO, " half-tone scanning: %d\n", - get_IN_half_tone (s->buffer)); - DBG (MSG_INFO, " color binary scanning: %d\n", - get_IN_monochrome_rgb (s->buffer)); - DBG (MSG_INFO, " color scanning: %d\n", - get_IN_multilevel_rgb (s->buffer)); - DBG (MSG_INFO, " color half-tone scanning: %d\n", - get_IN_half_tone_rgb (s->buffer)); - - if (get_IN_page_length (s->buffer) > 0x19) - { - DBG (MSG_INFO, "image memory: %d bytes\n", - get_IN_buffer_bytes(s->buffer)); - DBG (MSG_INFO, "physical functions:\n"); - DBG (MSG_INFO, " operator panel %d\n", - get_IN_operator_panel (s->buffer)); - DBG (MSG_INFO, " barcode %d\n", get_IN_barcode (s->buffer)); - DBG (MSG_INFO, " endorser %d\n", get_IN_endorser (s->buffer)); - DBG (MSG_INFO, " duplex %d\n", get_IN_duplex (s->buffer)); - DBG (MSG_INFO, " flatbed %d\n", get_IN_flatbed (s->buffer)); - DBG (MSG_INFO, " adf %d\n", get_IN_adf (s->buffer)); - - DBG (MSG_INFO, "image control functions:\n"); - DBG (MSG_INFO, " brightness steps: %d\n", - get_IN_brightness_steps (s->buffer)); - DBG (MSG_INFO, " threshold steps: %d\n", - get_IN_threshold_steps (s->buffer)); - DBG (MSG_INFO, " contrast steps: %d\n", - get_IN_contrast_steps (s->buffer)); - DBG (MSG_INFO, " number of build in gamma patterns: %d\n", - get_IN_num_gamma (s->buffer)); - DBG (MSG_INFO, " number of download gamma patterns: %d\n", - get_IN_num_gamma_download (s->buffer)); - - DBG (MSG_INFO, "compression processing functions:\n"); - DBG (MSG_INFO, " compression MR: %d\n", - get_IN_compression_MH (s->buffer)); - DBG (MSG_INFO, " compression MR: %d\n", - get_IN_compression_MR (s->buffer)); - DBG (MSG_INFO, " compression MMR: %d\n", - get_IN_compression_MMR (s->buffer)); - DBG (MSG_INFO, " compression JBIG: %d\n", - get_IN_compression_JBIG (s->buffer)); - DBG (MSG_INFO, " compression JPG1: %d\n", - get_IN_compression_JPG_BASE (s->buffer)); - DBG (MSG_INFO, " compression JPG2: %d\n", - get_IN_compression_JPG_EXT (s->buffer)); - DBG (MSG_INFO, " compression JPG3: %d\n", - get_IN_compression_JPG_INDEP (s->buffer)); - - DBG (MSG_INFO, "image processing functions:\n"); - DBG (MSG_INFO, " black and white reverse: %d\n", - get_IN_ipc_bw_reverse(s->buffer)); - DBG (MSG_INFO, " automatic binary DTC: %d\n", - get_IN_ipc_auto1(s->buffer)); - DBG (MSG_INFO, " simplified DTC: %d\n", - get_IN_ipc_auto2(s->buffer)); - DBG (MSG_INFO, " autline extraction: %d\n", - get_IN_ipc_outline_extraction(s->buffer)); - DBG (MSG_INFO, " image emphasis: %d\n", - get_IN_ipc_image_emphasis(s->buffer)); - DBG (MSG_INFO, " automatic separation: %d\n", - get_IN_ipc_auto_separation(s->buffer)); - DBG (MSG_INFO, " mirror image: %d\n", - get_IN_ipc_mirroring(s->buffer)); - DBG (MSG_INFO, " white level follower: %d\n", - get_IN_ipc_white_level_follow(s->buffer)); - } - DBG (MSG_INFO, "\n\n"); - } - return ret; -} - -/** - * Sends a command to the device. This calls do_scsi_cmd or do_usb_cmd. - */ -static int -do_cmd (Fujitsu_Connection_Type connection, int fd, unsigned char *cmd, - int cmd_len, unsigned char *out, size_t req_out_len, - size_t *res_out_len) -{ - if (connection == SANE_FUJITSU_SCSI) { - return do_scsi_cmd(fd, cmd, cmd_len, out, req_out_len, res_out_len); - } - if (connection == SANE_FUJITSU_USB) { - return do_usb_cmd(fd, cmd, cmd_len, out, req_out_len, res_out_len); - } - return SANE_STATUS_INVAL; -} - -/** - * Sends a SCSI command to the device. This is just a wrapper around - * sanei_scsi_cmd with some debug printing. - */ -static int -do_scsi_cmd (int fd, unsigned char *cmd, - int cmd_len, unsigned char *out, size_t req_out_len, - size_t *res_out_len) -{ - int ret; - size_t ol = req_out_len; - - hexdump (IO_CMD, "rslt>", out, (ol > 0x60) ? 0x60 : ol); - } - - return ret; -} - -#define USB_CMD_HEADER_BYTES 19 -#define USB_CMD_MIN_BYTES 31 - -/** - * Sends a USB command to the device. - * the fi-4x20 appear to have a firmware bug - * that prevents them from resetting the data0/1 - * toggle on both bulk endpoints. - * so, this function is splits the commands into - * 64 byte chunks to enable us to count packets - * this is terrible for performance. - * FIXME: find a better way - */ -static int -do_usb_cmd (int fd, unsigned char *cmd, - int cmd_len, unsigned char *out, size_t req_out_len, - size_t *res_out_len) -{ - int ret = SANE_STATUS_GOOD; - size_t cnt, ol; - int op_code = 0; - int i, j; - int tries = 0; - int status_byte = 0; - unsigned char buf[1024]; - -retry: - hexdump (IO_CMD, " 0) op_code = ((int)cmd[0]) & 0xff; - - if ((cmd_len+USB_CMD_HEADER_BYTES) > (int)sizeof(buf)) { - /* Command too long. */ - return SANE_STATUS_INVAL; - } - buf[0] = (unsigned char)'C'; - for (i = 1; i < USB_CMD_HEADER_BYTES; i++) buf[i] = (unsigned char)0; - memcpy(&buf[USB_CMD_HEADER_BYTES], cmd, cmd_len); - for (i = USB_CMD_HEADER_BYTES+cmd_len; i < USB_CMD_MIN_BYTES; i++) { - buf[i] = (unsigned char)0; - } - /* The SCAN command must be at least 32 bytes long. */ - if ( (op_code == SCAN) && (i < 32) ) { - for (; i < 32; i++) buf[i] = (unsigned char)0; - } - - for (j = 0; j < i;) { - cnt = i-j; - - /* First URB has to be 31 bytes. */ - /* All other URBs must be 64 bytes (max) per URB. */ - if ( (j == 0) && (cnt > 31) ) cnt = 31; else if (cnt > 64) cnt = 64; - hexdump (IO_CMD, "*** URB going out:", &buf[j], cnt); - DBG (10, "try to write %lu bytes\n", (u_long) cnt); - ret = sanei_usb_write_bulk(fd, &buf[j], &cnt); - DBG (10, "wrote %lu bytes\n", (u_long) cnt); - if (ret != SANE_STATUS_GOOD) break; - - w_cmd_count++; - j += cnt; - } - if (ret != SANE_STATUS_GOOD) { - DBG (MSG_ERR, "*** Got error %d trying to write\n", ret); - } - - ol = 0; - if (ret == SANE_STATUS_GOOD) { - if ( (out != NULL) && (req_out_len > 0) ) { -/* while (ol < req_out_len) {*/ - cnt = (size_t)(req_out_len-ol); - DBG (10, "try to read %lu bytes\n", (u_long) cnt); - ret = sanei_usb_read_bulk(fd, &out[ol], &cnt); - - /*flaming hack, count packets to fix data0/1 toggle issues*/ - r_cmd_count += (cnt/64); - if(cnt % 64) - r_cmd_count++; - - DBG (10, "read %lu bytes\n", (u_long) cnt); - if (cnt > 0) { - hexdump (IO_CMD, "*** Data read:", &out[ol], cnt); - } - if (ret != SANE_STATUS_GOOD) { - DBG(MSG_ERR, "*** Got error %d trying to read\n", ret); - } -/* if (ret != SANE_STATUS_GOOD) break;*/ - ol += cnt; -/* }*/ - } - - DBG(10, "*** Try to read CSW\n"); - cnt = 13; - sanei_usb_read_bulk(fd, buf, &cnt); - r_cmd_count++; - hexdump (IO_CMD, "*** Read CSW", buf, cnt); - - status_byte = ((int)buf[9]) & 0xff; - if (status_byte != 0) { - DBG - (MSG_ERR, - "Got bad status: %2.2x op_code=%2.2x ret=%d req_out_len=%lu ol=%lu\n", - status_byte, - op_code, - ret, - (u_long) req_out_len, - (u_long) ol); - } - } - - /* Auto-retry failed data reads, in case the scanner is busy. */ - if ( (op_code == READ) && (tries < 100) && (ol == 0) ) { - usleep(100000L); - tries++; - DBG(MSG_ERR, "read failed; retry %d\n", tries); - goto retry; - } - - if (res_out_len != NULL) - { - *res_out_len = ol; - } - - if ((req_out_len != 0) && (req_out_len != ol)) - { - DBG (MSG_ERR, "do_usb_cmd: asked %lu bytes, got %lu\n", - (u_long) req_out_len, (u_long) ol); - } - - if (ret) - { - DBG (MSG_ERR, "do_usb_cmd: returning 0x%08x\n", ret); - } - - DBG (IO_CMD_RES, "do_usb_cmd: returning %lu bytes:\n", (u_long) ol); - - if (out != NULL && ol != 0) - { - hexdump (IO_CMD_RES, ">rslt>", out, (ol > 0x60) ? 0x60 : ol); - } - -/* if ( (op_code == OBJECT_POSITION) || (op_code == TEST_UNIT_READY) ) { */ - if (status_byte == 0x02) { /* check condition */ - /* Issue a REQUEST_SENSE command and pass the resulting sense data - into the scsi_sense_handler routine. */ - memset(buf, 0, 18); - if (do_usb_cmd - (fd, - request_senseB.cmd, - request_senseB.size, - buf, - 18, - NULL) == SANE_STATUS_GOOD) { - ret = scsi_sense_handler(fd, buf, NULL); - } - } - - return ret; -} - -/** - * Prints a hex dump of the given buffer onto the debug output stream. - */ -static void -hexdump (int level, char *comment, unsigned char *p, int l) -{ - int i; - char line[128]; - char *ptr; - - DBG (level, "%s\n", comment); - ptr = line; - for (i = 0; i < l; i++, p++) - { - if ((i % 16) == 0) - { - if (ptr != line) - { - *ptr = '\0'; - DBG (level, "%s\n", line); - ptr = line; - } - sprintf (ptr, "%3.3x:", i); - ptr += 4; - } - sprintf (ptr, " %2.2x", *p); - ptr += 3; + if(s->can_grayscale){ + s->mode_list[i++]=mode_Grayscale; } - *ptr = '\0'; - DBG (level, "%s\n", line); -} - - -/** - * Creates a temporary file, opens it, and returns a file pointer for it. - * (There seems to be no platform-independent way of doing this with mktemp - * and friends.) - * - * If no file can be created, NULL is returned. Will only create a file that - * doesn't exist already. The function will also unlink ("delete") the file - * immediately after it is created. In any "sane" programming environment this - * has the effect that the file can be used for reading and writing as normal - * but vanishes as soon as it's closed - so no cleanup required if the - * process dies etc. - */ -static int -makeTempFile () -{ -#ifdef P_tmpdir - static const char *tmpdir = P_tmpdir; -#else - static const char *tmpdir = "/tmp"; -#endif - char filename[PATH_MAX]; - unsigned int suffix = time (NULL) % 256; - int try = 0; - int file; - - for (try = 0; try < 10; try++) - { - sprintf (filename, "%s%csane-fujitsu-%d-%d", tmpdir, PATH_SEP, - getpid (), suffix + try); -#if defined(_POSIX_SOURCE) - file = open (filename, O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR); -#else - file = open (filename, O_RDWR | O_CREAT | O_EXCL); -#endif - if (file != -1) - { - unlink (filename); - DBG (10, "makeTempFile: file \"%s\" created.\n", filename); - return file; - } + if(s->can_color_monochrome){ + s->mode_list[i++]=mode_Color_lineart; } - return -1; -} - - - -/** - * Releases the scanner device on the SCSI bus. - * - * Should go through the following sequence: - * OBJECT POSITION DISCHARGE - * GOOD - * RELEASE UNIT - * GOOD - */ -static int -free_scanner (struct fujitsu *s) -{ - int ret; - DBG (10, "free_scanner\n"); - -#if 0 - ret = object_position (s, SANE_FALSE); - if (ret) - return ret; - - wait_scanner (s); -#endif - - hexdump (MSG_IO, "release_unit", release_unitB.cmd, release_unitB.size); - ret = do_cmd (s->connection, s->sfd, release_unitB.cmd, - release_unitB.size, NULL, 0, NULL); - if (ret) - return ret; - - /* flaming hack cause some usb scanners (fi-4x20) fail - to work properly on next connection if an odd number - of commands are sent to the scanner on either endpoint */ - if(s->connection == SANE_FUJITSU_USB){ - /* if read counter is odd */ - if(r_cmd_count % 2){ - ret = do_cmd (s->connection, s->sfd, test_unit_readyB.cmd, - test_unit_readyB.size, 0, 0, NULL); - if (ret) - return ret; + if(s->can_color_halftone){ + s->mode_list[i++]=mode_Color_halftone; } - - /* if write is odd */ - if(w_cmd_count % 2 && !r_cmd_count % 2){ - ret = get_hardware_status(s); - if (ret) - return ret; + if(s->can_color_grayscale){ + s->mode_list[i++]=mode_Color; } + s->mode_list[i]=NULL; + + opt->name = SANE_NAME_SCAN_MODE; + opt->title = SANE_TITLE_SCAN_MODE; + opt->desc = SANE_DESC_SCAN_MODE; + opt->type = SANE_TYPE_STRING; + opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; + opt->constraint.string_list = s->mode_list; + opt->size = maxStringSize (opt->constraint.string_list); + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } - DBG (10, "free_scanner: ok\n"); - return ret; -} - -/** - * Reserves the scanner on the SCSI bus for our exclusive access until released - * by free_scanner. - * - * Should go through the following command sequence: - * TEST UNIT READY - * CHECK CONDITION \ - * REQUEST SENSE > These should be handled automagically by - * UNIT ATTENTION / the kernel if they happen (powerup/reset) - * TEST UNIT READY - * GOOD - * RESERVE UNIT - * GOOD - */ -static int -grab_scanner (struct fujitsu *s) -{ - int ret; - - DBG (10, "grab_scanner\n"); - wait_scanner (s); - - hexdump (MSG_IO, "reserve_unit", reserve_unitB.cmd, reserve_unitB.size); - ret = do_cmd (s->connection, s->sfd, reserve_unitB.cmd, - reserve_unitB.size, NULL, 0, NULL); - if (ret) - return ret; - - DBG (10, "grab_scanner: ok\n"); - return 0; -} - -static int fujitsu_wait_scanner(Fujitsu_Connection_Type connection, int fd) -{ - int ret = -1; - int cnt = 0; - - DBG (10, "wait_scanner\n"); - - while (ret != SANE_STATUS_GOOD) - { - hexdump (MSG_IO, "test_unit_ready", test_unit_readyB.cmd, - test_unit_readyB.size); - ret = do_cmd (connection, fd, test_unit_readyB.cmd, - test_unit_readyB.size, 0, 0, NULL); - if (ret == SANE_STATUS_DEVICE_BUSY) - { - usleep (500000); /* wait 0.5 seconds */ - /* 20 sec. max (prescan takes up to 15 sec. */ - if (cnt++ > 40) - { - DBG (MSG_ERR, "wait_scanner: scanner does NOT get ready\n"); - return -1; - } - } - else if (ret != SANE_STATUS_GOOD) - { - DBG (MSG_ERR, "wait_scanner: unit ready failed (%s)\n", - sane_strstatus (ret)); - } + /* x resolution */ + /* some scanners only support fixed res + * build a list of possible choices */ + if(option==OPT_X_RES){ + i=0; + if(s->std_res_60 && s->max_x_res >= 60 && s->min_x_res <= 60){ + s->x_res_list[++i] = 60; } - DBG (10, "wait_scanner: ok\n"); - return SANE_STATUS_GOOD; -} - -/** - * wait_scanner spins until TEST_UNIT_READY returns 0 (GOOD) - * returns 0 on success, - * returns -1 on error or timeout - */ -static int -wait_scanner (struct fujitsu *s) -{ - return fujitsu_wait_scanner(s->connection, s->sfd); -} - -/** - * Issues the SCSI OBJECT POSITION command if an ADF is installed. - */ -static int -object_position (struct fujitsu *s, int i_load) -{ - int ret; - DBG (10, "object_position: %s \n", (i_load==SANE_TRUE)?"load":"discharge"); - if (s->use_adf != SANE_TRUE) - { - return SANE_STATUS_GOOD; + if(s->std_res_75 && s->max_x_res >= 75 && s->min_x_res <= 75){ + s->x_res_list[++i] = 75; } - if (s->has_adf == SANE_FALSE) - { - DBG (10, "object_position: ADF not present.\n"); - return SANE_STATUS_UNSUPPORTED; + if(s->std_res_100 && s->max_x_res >= 100 && s->min_x_res <= 100){ + s->x_res_list[++i] = 100; } - memcpy (s->buffer, object_positionB.cmd, object_positionB.size); - - if (i_load == SANE_TRUE) - { - set_OP_autofeed (s->buffer, OP_Feed); - } - else - { - set_OP_autofeed (s->buffer, OP_Discharge); + if(s->std_res_120 && s->max_x_res >= 120 && s->min_x_res <= 120){ + s->x_res_list[++i] = 120; } + if(s->std_res_150 && s->max_x_res >= 150 && s->min_x_res <= 150){ + s->x_res_list[++i] = 150; + } + if(s->std_res_160 && s->max_x_res >= 160 && s->min_x_res <= 160){ + s->x_res_list[++i] = 160; + } + if(s->std_res_180 && s->max_x_res >= 180 && s->min_x_res <= 180){ + s->x_res_list[++i] = 180; + } + if(s->std_res_200 && s->max_x_res >= 200 && s->min_x_res <= 200){ + s->x_res_list[++i] = 200; + } + if(s->std_res_240 && s->max_x_res >= 240 && s->min_x_res <= 240){ + s->x_res_list[++i] = 240; + } + if(s->std_res_300 && s->max_x_res >= 300 && s->min_x_res <= 300){ + s->x_res_list[++i] = 300; + } + if(s->std_res_320 && s->max_x_res >= 320 && s->min_x_res <= 320){ + s->x_res_list[++i] = 320; + } + if(s->std_res_400 && s->max_x_res >= 400 && s->min_x_res <= 400){ + s->x_res_list[++i] = 400; + } + if(s->std_res_480 && s->max_x_res >= 480 && s->min_x_res <= 480){ + s->x_res_list[++i] = 480; + } + if(s->std_res_600 && s->max_x_res >= 600 && s->min_x_res <= 600){ + s->x_res_list[++i] = 600; + } + if(s->std_res_800 && s->max_x_res >= 800 && s->min_x_res <= 800){ + s->x_res_list[++i] = 800; + } + if(s->std_res_1200 && s->max_x_res >= 1200 && s->min_x_res <= 1200){ + s->x_res_list[++i] = 1200; + } + s->x_res_list[0] = i; + + opt->name = SANE_NAME_SCAN_X_RESOLUTION; + opt->title = SANE_TITLE_SCAN_X_RESOLUTION; + opt->desc = SANE_DESC_SCAN_X_RESOLUTION; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_DPI; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + if(s->step_x_res){ + s->x_res_range.min = s->min_x_res; + s->x_res_range.max = s->max_x_res; + s->x_res_range.quant = s->step_x_res; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->x_res_range; + } + else{ + opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; + opt->constraint.word_list = s->x_res_list; + } + } - hexdump (MSG_IO, "object_position", s->buffer, object_positionB.size); - ret = do_cmd (s->connection, s->sfd, s->buffer, object_positionB.size, - NULL, 0, NULL); - if (ret != SANE_STATUS_GOOD) - return ret; - wait_scanner (s); - DBG (10, "object_position: ok\n"); - return ret; -} - - -/** - * Issues OBJECT DISCHARGE command to spit out the paper. - */ -/* -static int -objectDischarge (struct fujitsu *s) -{ - int ret; - - DBG (10, "objectDischarge\n"); - if (s->use_adf != SANE_TRUE) - { - return SANE_STATUS_GOOD; + /* y resolution */ + if(option==OPT_Y_RES){ + i=0; + if(s->std_res_60 && s->max_y_res >= 60 && s->min_y_res <= 60){ + s->y_res_list[++i] = 60; + } + if(s->std_res_75 && s->max_y_res >= 75 && s->min_y_res <= 75){ + s->y_res_list[++i] = 75; + } + if(s->std_res_100 && s->max_y_res >= 100 && s->min_y_res <= 100){ + s->y_res_list[++i] = 100; + } + if(s->std_res_120 && s->max_y_res >= 120 && s->min_y_res <= 120){ + s->y_res_list[++i] = 120; + } + if(s->std_res_150 && s->max_y_res >= 150 && s->min_y_res <= 150){ + s->y_res_list[++i] = 150; + } + if(s->std_res_160 && s->max_y_res >= 160 && s->min_y_res <= 160){ + s->y_res_list[++i] = 160; + } + if(s->std_res_180 && s->max_y_res >= 180 && s->min_y_res <= 180){ + s->y_res_list[++i] = 180; + } + if(s->std_res_200 && s->max_y_res >= 200 && s->min_y_res <= 200){ + s->y_res_list[++i] = 200; + } + if(s->std_res_240 && s->max_y_res >= 240 && s->min_y_res <= 240){ + s->y_res_list[++i] = 240; + } + if(s->std_res_300 && s->max_y_res >= 300 && s->min_y_res <= 300){ + s->y_res_list[++i] = 300; + } + if(s->std_res_320 && s->max_y_res >= 320 && s->min_y_res <= 320){ + s->y_res_list[++i] = 320; + } + if(s->std_res_400 && s->max_y_res >= 400 && s->min_y_res <= 400){ + s->y_res_list[++i] = 400; + } + if(s->std_res_480 && s->max_y_res >= 480 && s->min_y_res <= 480){ + s->y_res_list[++i] = 480; } + if(s->std_res_600 && s->max_y_res >= 600 && s->min_y_res <= 600){ + s->y_res_list[++i] = 600; + } + if(s->std_res_800 && s->max_y_res >= 800 && s->min_y_res <= 800){ + s->y_res_list[++i] = 800; + } + if(s->std_res_1200 && s->max_y_res >= 1200 && s->min_y_res <= 1200){ + s->y_res_list[++i] = 1200; + } + s->y_res_list[0] = i; + + opt->name = SANE_NAME_SCAN_Y_RESOLUTION; + opt->title = SANE_TITLE_SCAN_Y_RESOLUTION; + opt->desc = SANE_DESC_SCAN_Y_RESOLUTION; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_DPI; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + if(s->step_y_res){ + s->y_res_range.min = s->min_y_res; + s->y_res_range.max = s->max_y_res; + s->y_res_range.quant = s->step_y_res; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->y_res_range; + } + else{ + opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; + opt->constraint.word_list = s->y_res_list; + } + } - memcpy (s->buffer, object_positionB.cmd, object_positionB.size); - if (s->model == MODEL_3092) { - set_OP_autofeed (s->buffer, OP_Feed); + /* "Geometry" group ---------------------------------------------------- */ + if(option==OPT_GEOMETRY_GROUP){ + opt->title = "Geometry"; + opt->desc = ""; + opt->type = SANE_TYPE_GROUP; + opt->constraint_type = SANE_CONSTRAINT_NONE; } - else { - set_OP_autofeed (s->buffer, OP_Discharge); + + /* top-left x */ + if(option==OPT_TL_X){ + /* values stored in 1200 dpi units */ + /* must be converted to MM for sane */ + s->tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x); + s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_x); + s->tl_x_range.quant = MM_PER_UNIT_FIX; + + opt->name = SANE_NAME_SCAN_TL_X; + opt->title = SANE_TITLE_SCAN_TL_X; + opt->desc = SANE_DESC_SCAN_TL_X; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &(s->tl_x_range); + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } - hexdump (MSG_IO, "object_position", s->buffer, object_positionB.size); - ret = do_cmd (s->connection, s->sfd, s->buffer, object_positionB.size, - NULL, 0, NULL); - wait_scanner (s); - DBG (10, "objectDischarge: ok\n"); - return ret; -} -*/ -/** - * Performs reset scanner and reset the Window - */ -static SANE_Status -do_reset (struct fujitsu *scanner) -{ - int ret=SANE_STATUS_GOOD; + /* top-left y */ + if(option==OPT_TL_Y){ + /* values stored in 1200 dpi units */ + /* must be converted to MM for sane */ + s->tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y); + s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y); + s->tl_y_range.quant = MM_PER_UNIT_FIX; + + opt->name = SANE_NAME_SCAN_TL_Y; + opt->title = SANE_TITLE_SCAN_TL_Y; + opt->desc = SANE_DESC_SCAN_TL_Y; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &(s->tl_y_range); + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } - DBG (10, "doReset\n"); - if (scanner->model == MODEL_3092) { - ret = do_cmd (scanner->connection, scanner->sfd, reset_unitB.cmd, - reset_unitB.size, NULL, 0, NULL); - if (ret) - return ret; + /* bottom-right x */ + if(option==OPT_BR_X){ + /* values stored in 1200 dpi units */ + /* must be converted to MM for sane */ + s->br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x); + s->br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_x); + s->br_x_range.quant = MM_PER_UNIT_FIX; + + opt->name = SANE_NAME_SCAN_BR_X; + opt->title = SANE_TITLE_SCAN_BR_X; + opt->desc = SANE_DESC_SCAN_BR_X; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &(s->br_x_range); + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } - return ret; -} -/** - * Performs cleanup. - */ -static SANE_Status -do_cancel (struct fujitsu *scanner) -{ - DBG (10, "do_cancel\n"); - scanner->object_count = 0; - scanner->eof = SANE_TRUE; + /* bottom-right y */ + if(option==OPT_BR_Y){ + /* values stored in 1200 dpi units */ + /* must be converted to MM for sane */ + s->br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y); + s->br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y); + s->br_y_range.quant = MM_PER_UNIT_FIX; + + opt->name = SANE_NAME_SCAN_BR_Y; + opt->title = SANE_TITLE_SCAN_BR_Y; + opt->desc = SANE_DESC_SCAN_BR_Y; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &(s->br_y_range); + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } - if (scanner->object_count != 0) - { - close (scanner->default_pipe_r); - if (scanner->duplex_mode == DUPLEX_BOTH) - { - close (scanner->duplex_pipe_r); - } + /* page width */ + if(option==OPT_PAGE_WIDTH){ + /* values stored in 1200 dpi units */ + /* must be converted to MM for sane */ + s->paper_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x); + s->paper_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_x); + s->paper_x_range.quant = MM_PER_UNIT_FIX; + + opt->name = "pagewidth"; + opt->title = "ADF paper width"; + opt->desc = "Must be set properly to align scanning window"; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->paper_x_range; + if(s->source == SOURCE_FLATBED){ + opt->cap = SANE_CAP_INACTIVE; } - - if (scanner->reader_pid > 0) - { - int exit_status; - DBG (10, "do_cancel: kill reader_process\n"); - - /* Tell reader process to stop, then wait for it to react. - * If kill fails because the process doesn't exist for some - * reason, don't wait. This will mostly happen if the process - * has already terminated and been waited for - for example, - * in the duplex-with-tempfile scenario. - */ - if (sanei_thread_kill (scanner->reader_pid) == 0) - { - DBG (50, "wait for scanner to stop\n"); - sanei_thread_waitpid (scanner->reader_pid, &exit_status); - } - scanner->reader_pid = 0; + else{ + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } + } - if (scanner->sfd >= 0) - { - free_scanner (scanner); - DBG (10, "do_cancel: close filedescriptor\n"); - if (scanner->connection == SANE_FUJITSU_USB) { - sanei_usb_close (scanner->sfd); - } else if (scanner->connection == SANE_FUJITSU_SCSI) { - sanei_scsi_close (scanner->sfd); - } - scanner->sfd = -1; + /* page height */ + if(option==OPT_PAGE_HEIGHT){ + /* values stored in 1200 dpi units */ + /* must be converted to MM for sane */ + s->paper_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y); + s->paper_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y); + s->paper_y_range.quant = MM_PER_UNIT_FIX; + + opt->name = "pageheight"; + opt->title = "ADF paper length"; + opt->desc = "Must be set properly to eject pages"; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->paper_y_range; + if(s->source == SOURCE_FLATBED){ + opt->cap = SANE_CAP_INACTIVE; } - - return SANE_STATUS_CANCELLED; -} - - - -/** - * Convenience method to determine longest string size in a list. - */ -static size_t -maxStringSize (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; + else{ + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } - return max_size; -} - - -/** - * Issues SCAN command. - * - * (This doesn't actually read anything, it just tells the scanner - * to start scanning.) - */ -static int -start_scan (struct fujitsu *s) -{ - int ret; - - unsigned char *command; - int cmdsize; + } - DBG (10, "start_scan\n"); - command = malloc (scanB.size + 2); - memcpy (command, scanB.cmd, scanB.size); + /* "Enhancement" group ------------------------------------------------- */ + if(option==OPT_ENHANCEMENT_GROUP){ + opt->title = "Enhancement"; + opt->desc = ""; + opt->type = SANE_TYPE_GROUP; + opt->constraint_type = SANE_CONSTRAINT_NONE; + } - /* - * 3096 seems to work by sending just the start scan command. - * 3091 requires one or two extra bytes, telling it which windows - * to scan (using the same window identifiers as in SET WINDOW). - * - * Currently, this sends the one extra byte to 3096 as well - may - * have to be changed if it misbehaves. - */ - if (s->duplex_mode == DUPLEX_BOTH) - { - cmdsize = scanB.size + 2; - set_SC_xfer_length (command, 2); - command[cmdsize - 2] = WD_wid_front; - command[cmdsize - 1] = WD_wid_back; + /* brightness */ + if(option==OPT_BRIGHTNESS){ + opt->name = SANE_NAME_BRIGHTNESS; + opt->title = SANE_TITLE_BRIGHTNESS; + opt->desc = SANE_DESC_BRIGHTNESS; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->brightness_range; + s->brightness_range.min=0; + s->brightness_range.max=s->brightness_steps; + s->brightness_range.quant=1; + if (s->brightness_steps){ + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } - else - { - cmdsize = scanB.size + 1; - set_SC_xfer_length (command, 1); - command[cmdsize - 1] = - (s->duplex_mode == DUPLEX_BACK) ? WD_wid_back : WD_wid_front; + else{ + opt->cap = SANE_CAP_INACTIVE; } + } - hexdump (MSG_IO, "start_scan", command, cmdsize); - ret = do_cmd (s->connection, s->sfd, command, cmdsize, NULL, 0, NULL); - - free (command); + /*threshold*/ + if(option==OPT_THRESHOLD){ + opt->name = SANE_NAME_THRESHOLD; + opt->title = SANE_TITLE_THRESHOLD; + opt->desc = SANE_DESC_THRESHOLD; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->threshold_range; + s->threshold_range.min=0; + s->threshold_range.max=s->threshold_steps; + s->threshold_range.quant=1; + if (s->threshold_steps && s->mode == MODE_LINEART) { + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + else { + opt->cap = SANE_CAP_INACTIVE; + } + } - if (ret) - { - /* possible reasons: - * - duplex reading without object_position - * - invalid field in parameter list - */ - - return ret; + /* contrast */ + if(option==OPT_CONTRAST){ + opt->name = SANE_NAME_CONTRAST; + opt->title = SANE_TITLE_CONTRAST; + opt->desc = SANE_DESC_CONTRAST; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->contrast_range; + s->contrast_range.min=0; + s->contrast_range.max=s->contrast_steps; + s->contrast_range.quant=1; + if (s->contrast_steps) { + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + else { + opt->cap = SANE_CAP_INACTIVE; } + } - DBG (10, "start_scan:ok\n"); - return ret; -} + /*rif*/ + if(option==OPT_RIF){ + opt->name = "rif"; + opt->title = "RIF"; + opt->desc = "Reverse image format"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_rif) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + else + opt->cap = SANE_CAP_INACTIVE; + } -static int -set_mode_params (struct fujitsu *s) -{ + /* "Advanced" group ------------------------------------------------------ */ + if(option==OPT_ADVANCED_GROUP){ + opt->title = "Advanced"; + opt->desc = ""; + opt->type = SANE_TYPE_GROUP; + opt->constraint_type = SANE_CONSTRAINT_NONE; + } - int ret; - unsigned char *command; - int i_cmd_size; - int i_param_size; + /*dropout color*/ + if(option==OPT_DROPOUT_COLOR){ + s->do_color_list[0] = color_Default; + s->do_color_list[1] = color_Red; + s->do_color_list[2] = color_Green; + s->do_color_list[3] = color_Blue; + s->do_color_list[4] = NULL; + + opt->name = "dropoutcolor"; + opt->title = "Dropout color"; + opt->desc = "One-pass scanners use only one color during gray or binary scanning, useful for colored paper or ink"; + opt->type = SANE_TYPE_STRING; + opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; + opt->constraint.string_list = s->do_color_list; + opt->size = maxStringSize (opt->constraint.string_list); + if ((s->has_MS_dropout || s->has_SW_dropout) && s->mode != MODE_COLOR) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - DBG (10, "set_mode_params\n"); + /*sleep time*/ + if(option==OPT_SLEEP_TIME){ + s->sleep_time_range.min = 0; + s->sleep_time_range.max = 60; + s->sleep_time_range.quant = 1; + + opt->name = "sleeptimer"; + opt->title = "Sleep timer"; + opt->desc = "Time in minutes until the internal power supply switches to sleep mode"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range=&s->sleep_time_range; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + } - ret = 0; + /*duplex offset*/ + if(option==OPT_DUPLEX_OFFSET){ + s->duplex_offset_range.min = -16; + s->duplex_offset_range.max = 16; + s->duplex_offset_range.quant = 1; + + opt->name = "duplexoffset"; + opt->title = "Duplex offset"; + opt->desc = "Adjust front/back offset"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->duplex_offset_range; + if(s->duplex_interlace == DUPLEX_INTERLACE_3091) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - if (s->has_dropout_color) - { + if(option==OPT_GREEN_OFFSET){ + s->green_offset_range.min = -16; + s->green_offset_range.max = 16; + s->green_offset_range.quant = 1; + + opt->name = "greenoffset"; + opt->title = "Green offset"; + opt->desc = "Adjust green/red offset"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->green_offset_range; + if(s->color_interlace == COLOR_INTERLACE_3091) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + + if(option==OPT_BLUE_OFFSET){ + s->blue_offset_range.min = -16; + s->blue_offset_range.max = 16; + s->blue_offset_range.quant = 1; + + opt->name = "blueoffset"; + opt->title = "blue scan line offset from default"; + opt->desc = "Adjust blue/red offset"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &s->blue_offset_range; + if(s->color_interlace == COLOR_INTERLACE_3091) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + + if(option==OPT_USE_SWAPFILE){ + opt->name = "swapfile"; + opt->title = "Swap file"; + opt->desc = "Save memory by buffering data in a temp file"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + opt->constraint_type = SANE_CONSTRAINT_NONE; + /*if(s->has_duplex) + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else*/ + opt->cap = SANE_CAP_INACTIVE; + } - memcpy (s->buffer, mode_selectB.cmd, mode_selectB.size); - memcpy (s->buffer + mode_selectB.size, mode_select_headerB.cmd, - mode_select_headerB.size); - memcpy (s->buffer + mode_selectB.size + mode_select_headerB.size, - mode_select_parameter_blockB.cmd, - mode_select_parameter_blockB.size); + /* "Sensor" group ------------------------------------------------------ */ + if(option==OPT_SENSOR_GROUP){ + opt->title = "Sensors and Buttons"; + opt->desc = ""; + opt->type = SANE_TYPE_GROUP; + opt->constraint_type = SANE_CONSTRAINT_NONE; + } + if(option==OPT_TOP){ + opt->name = "button-topedge"; + opt->title = "Top edge"; + opt->desc = "Paper is pulled partly into adf"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + if(option==OPT_A3){ + opt->name = "button-a3"; + opt->title = "A3 paper"; + opt->desc = "A3 paper detected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - command = s->buffer + mode_selectB.size + mode_select_headerB.size; - i_cmd_size = 8; - set_MSEL_len (command, i_cmd_size); + if(option==OPT_B4){ + opt->name = "button-b4"; + opt->title = "B4 paper"; + opt->desc = "B4 paper detected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - set_MSEL_pagecode (command, MSEL_dropout_color); - set_MSEL_dropout_front (command, s->dropout_color); - set_MSEL_dropout_back (command, s->dropout_color); + if(option==OPT_A4){ + opt->name = "button-a4"; + opt->title = "A4 paper"; + opt->desc = "A4 paper detected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - i_param_size = mode_select_headerB.size + i_cmd_size + 2; - set_MSEL_xfer_length (s->buffer, i_param_size); + if(option==OPT_B5){ + opt->name = "button-b5"; + opt->title = "B5 paper"; + opt->desc = "B5 paper detected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - hexdump (MSG_IO, "mode_select", s->buffer, - i_param_size + mode_selectB.size); + if(option==OPT_HOPPER){ + opt->name = "button-adfloaded"; + opt->title = "ADF loaded"; + opt->desc = "Paper in adf hopper"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - ret = do_cmd (s->connection, s->sfd, s->buffer, - i_param_size + mode_selectB.size, NULL, 0, NULL); + if(option==OPT_OMR){ + opt->name = "button-omrdf"; + opt->title = "OMR or DF"; + opt->desc = "OMR or double feed detected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - } + if(option==OPT_ADF_OPEN){ + opt->name = "button-adfopen"; + opt->title = "ADF open"; + opt->desc = "ADF cover open"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + if(option==OPT_SLEEP){ + opt->name = "button-powersave"; + opt->title = "Power saving"; + opt->desc = "Scanner in power saving mode"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - if (!ret) - { + if(option==OPT_SEND_SW){ + opt->name = "button-send"; + opt->title = "'Send to' button"; + opt->desc = "'Send to' button pressed"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - DBG (10, "set_mode_params: ok\n"); - } + if(option==OPT_MANUAL_FEED){ + opt->name = "button-manualfeed"; + opt->title = "Manual feed"; + opt->desc = "Manual feed selected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - return ret; -} + if(option==OPT_SCAN_SW){ + opt->name = "button-scan"; + opt->title = "'Scan' button"; + opt->desc = "'Scan' button pressed"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } -static int -fujitsu_set_sleep_mode(struct fujitsu *s) -{ - int ret; - unsigned char *command; - int i_cmd_size; - int i_param_size; + if(option==OPT_FUNCTION){ + opt->name = "button-function"; + opt->title = "Function"; + opt->desc = "Function character on screen"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - ret = SANE_STATUS_GOOD; + if(option==OPT_INK_EMPTY){ + opt->name = "button-inklow"; + opt->title = "Ink low"; + opt->desc = "Imprinter ink running low"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status && s->has_imprinter) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - if (s->model == MODEL_FI4x20 || s->model == MODEL_FI) /*...and others */ - { - /* With the fi-series scanners, we have to use a 10-byte header - * instead of a 4-byte header when communicating via USB. This - * may be a firmware bug. */ - scsiblk *headerB; - int xfer_length_pos_adj; - if (s->connection == SANE_FUJITSU_USB) { - headerB = &mode_select_usb_headerB; - xfer_length_pos_adj = mode_select_headerB.size-mode_select_usb_headerB.size; - } else { - headerB = &mode_select_headerB; - xfer_length_pos_adj = 0; - } - memcpy (s->buffer, mode_selectB.cmd, mode_selectB.size); - memcpy (s->buffer + mode_selectB.size, headerB->cmd, - headerB->size); - memcpy (s->buffer + mode_selectB.size + headerB->size, - mode_select_parameter_blockB.cmd, - mode_select_parameter_blockB.size); - - command = s->buffer + mode_selectB.size + headerB->size; - i_cmd_size = 6; - set_MSEL_len (command, i_cmd_size); + if(option==OPT_DOUBLE_FEED){ + opt->name = "button-doublefeed"; + opt->title = "Double feed"; + opt->desc = "Double feed detected"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status || s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - set_MSEL_pagecode (command, MSEL_sleep); - set_MSEL_sleep_mode(command, s->sleep_time); - - i_param_size = headerB->size + i_cmd_size + 2; - set_MSEL_xfer_length (s->buffer, i_param_size + xfer_length_pos_adj); + if(option==OPT_ERROR_CODE){ + opt->name = "button-errorcode"; + opt->title = "Error code"; + opt->desc = "Hardware error code"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - hexdump (MSG_IO, "mode_select", s->buffer, - i_param_size + mode_selectB.size); - - ret = do_cmd (s->connection, s->sfd, s->buffer, - i_param_size + mode_selectB.size, NULL, 0, NULL); + if(option==OPT_SKEW_ANGLE){ + opt->name = "button-skewangle"; + opt->title = "Skew angle"; + opt->desc = "Requires black background for scanning"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - if (!ret) - { - DBG (10, "set_sleep_mode: ok\n"); - } - } + if(option==OPT_INK_REMAIN){ + opt->name = "button-inkremain"; + opt->title = "Ink remaining"; + opt->desc = "Imprinter ink level"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + if (s->has_cmd_hw_status && s->has_imprinter) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } - return ret; -} + if(option==OPT_DENSITY_SW){ + opt->name = "button-density"; + opt->title = "Density"; + opt->desc = "Density dial"; + opt->type = SANE_TYPE_INT; + opt->unit = SANE_UNIT_NONE; + if (s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } + if(option==OPT_DUPLEX_SW){ + opt->name = "button-duplex"; + opt->title = "Duplex switch"; + opt->desc = "Duplex switch"; + opt->type = SANE_TYPE_BOOL; + opt->unit = SANE_UNIT_NONE; + if (s->ghs_in_rs) + opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + else + opt->cap = SANE_CAP_INACTIVE; + } -/** - * Handler for termination signal. - */ -static void -sigtermHandler (int signal) -{ - signal = signal; /* get rid of compiler warning */ - sanei_scsi_req_flush_all (); /* flush SCSI queue */ - _exit (SANE_STATUS_GOOD); + return opt; } - /** - * A quite generic reader routine that just passes the scanner's data - * through (i.e. stuffs it into the pipe). + * Gets or sets an option value. + * + * From the SANE spec: + * This function is used to set or inquire the current value of option + * number n of the device represented by handle h. The manner in which + * the option is controlled is specified by parameter action. The + * possible values of this parameter are described in more detail + * below. The value of the option is passed through argument val. It + * is a pointer to the memory that holds the option value. The memory + * area pointed to by v must be big enough to hold the entire option + * value (determined by member size in the corresponding option + * descriptor). + * + * The only exception to this rule is that when setting the value of a + * string option, the string pointed to by argument v may be shorter + * since the backend will stop reading the option value upon + * encountering the first NUL terminator in the string. If argument i + * is not NULL, the value of *i will be set to provide details on how + * well the request has been met. */ -static unsigned int -reader_generic_passthrough (struct fujitsu *scanner, FILE * fp, int i_window_id) +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) { - int status; - unsigned int total_data_size, i_data_left, data_to_read; - unsigned char *large_buffer; - unsigned int large_buffer_size = 0; - unsigned int i_data_read; - - i_data_left = scanner->bytes_per_scan_line * scanner->scan_height_pixels; - total_data_size = i_data_left; - - large_buffer = scanner->buffer; - large_buffer_size = - scanner->scsi_buf_size - - (scanner->scsi_buf_size % scanner->bytes_per_scan_line); + struct fujitsu *s = (struct fujitsu *) handle; + SANE_Int dummy = 0; - do - { - data_to_read = (i_data_left < large_buffer_size) ? i_data_left : large_buffer_size; + /* Make sure that all those statements involving *info cannot break (better + * than having to do "if (info) ..." everywhere!) + */ + if (info == 0) + info = &dummy; - status = read_large_data_block (scanner, large_buffer, data_to_read, - i_window_id, &i_data_read); + if (option >= NUM_OPTIONS) { + DBG (5, "sane_control_option: %d too big\n", option); + return SANE_STATUS_INVAL; + } - switch (status) - { - case SANE_STATUS_GOOD: - break; - case SANE_STATUS_EOF: - DBG (10, "reader_process: EOM (no more data) length = %d\n", - i_data_read); - data_to_read -= i_data_read; - i_data_left = data_to_read; - break; - default: - DBG (MSG_ERR, - "reader_process: unable to get image data from scanner!\n"); - fclose (fp); - return (0); - } + if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { + DBG (5, "sane_control_option: %d inactive\n", option); + return SANE_STATUS_INVAL; + } - fwrite (large_buffer, 1, i_data_read, fp); - fflush (fp); + /* + * SANE_ACTION_GET_VALUE: We have to find out the current setting and + * return it in a human-readable form (often, text). + */ + if (action == SANE_ACTION_GET_VALUE) { + SANE_Word * val_p = (SANE_Word *) val; - i_data_left -= data_to_read; - DBG (10, "reader_process(generic): buffer of %d bytes read; %d bytes to go\n", - data_to_read, i_data_left); - } - while (i_data_left); - fclose (fp); + DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option); - return total_data_size; -} + switch (option) { + case OPT_NUM_OPTS: + *val_p = NUM_OPTIONS; + return SANE_STATUS_GOOD; -/** - * Reads a large data block from the device. If the requested length is larger - * than the device's scsi_buf_size (which is assumed to be the maximum amount - * transferrable within one SCSI command), multiple SCSI commands will be - * issued to retrieve the full length. - * - * @param s the scanner object - * @param buffer where the data should go - * @param length how many bytes to read - * @param i_window_id 0=read data from front side 128= read data from back. - * m3091: set i_window_id=0 for both sides. - * @return i_data_read the number of bytes returned by the scanner, is not - * always euqal to length. - */ -static int -read_large_data_block (struct fujitsu *s, unsigned char *buffer, - unsigned int length, int i_window_id, - unsigned int *i_data_read) -{ - unsigned int data_left = length; - unsigned int data_to_read; - int status; - unsigned char *myBuffer = buffer; - size_t data_read; - unsigned int i; - - *i_data_read = 0; - current_scanner = s; - DBG (MSG_IO_READ, "read_large_data_block requested %u bytes\n", length); - do - { - data_to_read = - (data_left <= s->scsi_buf_size) ? data_left : s->scsi_buf_size; + case OPT_SOURCE: + if(s->source == SOURCE_FLATBED){ + strcpy (val, source_Flatbed); + } + else if(s->source == SOURCE_ADF_FRONT){ + strcpy (val, source_ADFFront); + } + else if(s->source == SOURCE_ADF_BACK){ + strcpy (val, source_ADFBack); + } + else if(s->source == SOURCE_ADF_DUPLEX){ + strcpy (val, source_ADFDuplex); + } + else{ + DBG(5,"missing option val for source\n"); + } + return SANE_STATUS_GOOD; - set_R_datatype_code (readB.cmd, R_datatype_imagedata); - set_R_window_id (readB.cmd, i_window_id); - set_R_xfer_length (readB.cmd, data_to_read); + case OPT_MODE: + if(s->mode == MODE_LINEART){ + strcpy (val, mode_Lineart); + } + else if(s->mode == MODE_HALFTONE){ + strcpy (val, mode_Halftone); + } + else if(s->mode == MODE_GRAYSCALE){ + strcpy (val, mode_Grayscale); + } + else if(s->mode == MODE_COLOR){ + strcpy (val, mode_Color); + } + return SANE_STATUS_GOOD; - status = do_cmd (s->connection, s->sfd, readB.cmd, readB.size, - myBuffer, data_to_read, &data_read); + case OPT_X_RES: + *val_p = s->resolution_x; + return SANE_STATUS_GOOD; - if (status == SANE_STATUS_EOF) - { - /* get the real number of bytes */ - data_read -= s->i_transfer_length; - data_left = 0; - } - else if (status == SANE_STATUS_DEVICE_BUSY) - { - /* temporary lack of data? - * try again. - */ - data_read = 0; - usleep(100000L); - } - else if (status != SANE_STATUS_GOOD) - { - /* denotes error */ - DBG(MSG_ERR, "error reading data block status = %d\n", status); - data_read = 0; - data_left = 0; - } - else - { - if (s->compress_type == WD_cmp_NONE && - (s->color_mode == MODE_COLOR || - s->color_mode == MODE_GRAYSCALE)) - { - /* invert image if mode == Color || Gray */ - - for ( i = 0; i < data_read; i++ ) - { - *myBuffer = *myBuffer ^ 0xff; - myBuffer ++; - } - } - else - { - - myBuffer += data_read; - } - data_left -= data_read; - } + case OPT_Y_RES: + *val_p = s->resolution_y; + return SANE_STATUS_GOOD; - *i_data_read += data_read; - } - while (data_left); + case OPT_TL_X: + *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x); + return SANE_STATUS_GOOD; - if (*i_data_read != length) - { - DBG(10, "data read = %d data requested = %d\n", *i_data_read, length); - } + case OPT_TL_Y: + *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_y); + return SANE_STATUS_GOOD; - current_scanner = NULL; - return status; -} + case OPT_BR_X: + *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_x); + return SANE_STATUS_GOOD; + case OPT_BR_Y: + *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_y); + return SANE_STATUS_GOOD; + case OPT_PAGE_WIDTH: + *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_width); + return SANE_STATUS_GOOD; -/* - * @@ Section 4 - generic routines with limited amount of model specific code - */ + case OPT_PAGE_HEIGHT: + *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_height); + return SANE_STATUS_GOOD; + case OPT_BRIGHTNESS: + *val_p = s->brightness; + return SANE_STATUS_GOOD; + case OPT_THRESHOLD: + *val_p = s->threshold; + return SANE_STATUS_GOOD; -/** - * Finds out details about a scanner. - * - * This routine will check if a certain device is a Fujitsu scanner and - * determine the model. It also copies interesting data from the INQUIRY - * command into the handle structure (e.g. whether or not an ADF has been - * detected on the scanner). - * - * We will return a failure code of 1 if the device is not a supported - * scanner. - */ -static int -identify_scanner (struct fujitsu *s) -{ - char vendor[9]; - char product[17]; - char version[5]; - char *pp; + case OPT_CONTRAST: + *val_p = s->contrast; + return SANE_STATUS_GOOD; - DBG (10, "identify_scanner\n"); + case OPT_RIF: + *val_p = s->rif; + return SANE_STATUS_GOOD; - vendor[8] = product[0x10] = version[4] = 0; + /* Advanced Group */ + case OPT_DROPOUT_COLOR: + switch (s->dropout_color) { + case COLOR_DEFAULT: + strcpy (val, color_Default); + break; + case COLOR_RED: + strcpy (val, color_Red); + break; + case COLOR_GREEN: + strcpy (val, color_Green); + break; + case COLOR_BLUE: + strcpy (val, color_Blue); + break; + } + return SANE_STATUS_GOOD; - do_inquiry (s); /* get inquiry */ - if (get_IN_periph_devtype (s->buffer) != IN_periph_devtype_scanner) - { - DBG (5, "The device at \"%s\" is not a scanner.\n", s->devicename); - return 1; - } + case OPT_SLEEP_TIME: + *val_p = s->sleep_time; + return SANE_STATUS_GOOD; - get_IN_vendor ((char*) s->buffer, vendor); - get_IN_product ((char*) s->buffer, product); - get_IN_version ((char*) s->buffer, version); - - vendor[8] = '\0'; - product[16] = '\0'; - version[4] = '\0'; - for (pp = vendor + 7; *pp == ' ' && pp >= vendor; pp--) - *pp = '\0'; - for (pp = product + 15; *pp == ' ' && pp >= product; pp--) - *pp = '\0'; - for (pp = version + 3; *pp == ' ' && pp >= version; pp--) - *pp = '\0'; + case OPT_DUPLEX_OFFSET: + *val_p = s->duplex_offset; + return SANE_STATUS_GOOD; - if (strcmp ("FUJITSU", vendor)) - { - DBG (5, "The device at \"%s\" is reported to be made by " - "\"%s\" -\n", s->devicename, vendor); - DBG (5, "this backend only supports Fujitsu products.\n"); - return 1; - } + case OPT_GREEN_OFFSET: + *val_p = s->green_offset; + return SANE_STATUS_GOOD; - /* - * Ok, this is where we determine which type of scanner it is. - * - * If a model has been defined in the config file, we just use that - * without looking. This is slightly inelegant at the moment since we - * should really allow to "force-model" a model _per_ _device_ instead - * of globally. - */ - if (forceModel != -1) - { - s->model = forceModel; - DBG (5, "The device at \"%s\" says it's a model \"%s\" scanner.\n", - s->devicename, product); - DBG (5, "Automatic detection has been overridden by force-model " - "option in the config file.\n"); - } - else - { - s->model = modelMatch (product); - if (s->model == -1) - { - DBG (5, "The device at \"%s\" says it's a model \"%s\" scanner -\n", - s->devicename, product); - DBG (5, "this backend does not support that model.\n"); - DBG (5, - "You may try to use the \"force-model\" option in the config file\n"); - return 1; - } - } + case OPT_BLUE_OFFSET: + *val_p = s->blue_offset; + return SANE_STATUS_GOOD; + + case OPT_USE_SWAPFILE: + *val_p = s->use_temp_file; + return SANE_STATUS_GOOD; - DBG (10, "Found %s scanner %s version %s on device %s, treating as %s\n", - vendor, product, version, s->devicename, - (s->model == MODEL_FORCE) ? "unknown" : - (s->model == MODEL_3091) ? "3091" : - (s->model == MODEL_3092) ? "3092" : - (s->model == MODEL_3096) ? "3096" : - (s->model == MODEL_3097) ? "3097" : - (s->model == MODEL_3093) ? "3093" : - (s->model == MODEL_4097) ? "4097" : - (s->model == MODEL_FI4x20) ? "fi-4x20" : - (s->model == MODEL_FI) ? "fi-series" : "unknown"); - - strcpy (s->vendorName, vendor); - strcpy (s->productName, product); - strcpy (s->versionName, version); - - s->has_adf = SANE_FALSE; - s->has_fb = SANE_FALSE; - s->can_read_alternate = SANE_FALSE; - s->read_mode = READ_MODE_PASS; - s->ipc_present = SANE_FALSE; - s->has_dropout_color = SANE_FALSE; - s->cmp_present = SANE_FALSE; - s->has_outline = SANE_FALSE; - s->has_emphasis = SANE_FALSE; - s->has_autosep = SANE_FALSE; - s->has_mirroring = SANE_FALSE; - s->has_imprinter = SANE_FALSE; - s->has_threshold = SANE_TRUE; - s->has_brightness = SANE_TRUE; - s->has_contrast = SANE_TRUE; - - s->threshold_range = default_threshold_range; - s->brightness_range = default_brightness_range; - s->contrast_range = default_contrast_range; + /* Sensor Group */ + case OPT_TOP: + get_hardware_status(s); + *val_p = s->hw_top; + return SANE_STATUS_GOOD; + + case OPT_A3: + get_hardware_status(s); + *val_p = s->hw_A3; + return SANE_STATUS_GOOD; + + case OPT_B4: + get_hardware_status(s); + *val_p = s->hw_B4; + return SANE_STATUS_GOOD; + + case OPT_A4: + get_hardware_status(s); + *val_p = s->hw_A4; + return SANE_STATUS_GOOD; + + case OPT_B5: + get_hardware_status(s); + *val_p = s->hw_B5; + return SANE_STATUS_GOOD; + + case OPT_HOPPER: + get_hardware_status(s); + *val_p = s->hw_hopper; + return SANE_STATUS_GOOD; + + case OPT_OMR: + get_hardware_status(s); + *val_p = s->hw_omr; + return SANE_STATUS_GOOD; + + case OPT_ADF_OPEN: + get_hardware_status(s); + *val_p = s->hw_adf_open; + return SANE_STATUS_GOOD; + + case OPT_SLEEP: + get_hardware_status(s); + *val_p = s->hw_sleep; + return SANE_STATUS_GOOD; + + case OPT_SEND_SW: + get_hardware_status(s); + *val_p = s->hw_send_sw; + return SANE_STATUS_GOOD; + + case OPT_MANUAL_FEED: + get_hardware_status(s); + *val_p = s->hw_manual_feed; + return SANE_STATUS_GOOD; + + case OPT_SCAN_SW: + get_hardware_status(s); + *val_p = s->hw_scan_sw; + return SANE_STATUS_GOOD; + + case OPT_FUNCTION: + get_hardware_status(s); + *val_p = s->hw_function; + return SANE_STATUS_GOOD; + + case OPT_INK_EMPTY: + get_hardware_status(s); + *val_p = s->hw_ink_empty; + return SANE_STATUS_GOOD; + + case OPT_DOUBLE_FEED: + get_hardware_status(s); + *val_p = s->hw_double_feed; + return SANE_STATUS_GOOD; + + case OPT_ERROR_CODE: + get_hardware_status(s); + *val_p = s->hw_error_code; + return SANE_STATUS_GOOD; + + case OPT_SKEW_ANGLE: + get_hardware_status(s); + *val_p = s->hw_skew_angle; + return SANE_STATUS_GOOD; + + case OPT_INK_REMAIN: + get_hardware_status(s); + *val_p = s->hw_ink_remain; + return SANE_STATUS_GOOD; + + case OPT_DENSITY_SW: + get_hardware_status(s); + *val_p = s->hw_density_sw; + return SANE_STATUS_GOOD; + + case OPT_DUPLEX_SW: + get_hardware_status(s); + *val_p = s->hw_duplex_sw; + return SANE_STATUS_GOOD; + + } + } + else if (action == SANE_ACTION_SET_VALUE) { + int tmp; + SANE_Word val_c; + SANE_Status status; + + DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option); + + if ( s->started ) { + DBG (5, "sane_control_option: cant set, device busy\n"); + return SANE_STATUS_DEVICE_BUSY; + } - s->vpd_mode_list[0] = NULL; - s->compression_mode_list[0] = cmp_none; + if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) { + DBG (5, "sane_control_option: not settable\n"); + return SANE_STATUS_INVAL; + } + status = sanei_constrain_value (s->opt + option, val, info); + if (status != SANE_STATUS_GOOD) { + DBG (5, "sane_control_option: bad value\n"); + return status; + } - if ((s->model == MODEL_3091) || (s->model == MODEL_3092)) - { - /* Couldn't find a way to ask the scanner if it supports the ADF, - * so just hard-code it. - */ - s->has_adf = SANE_TRUE; - s->has_fb = SANE_FALSE; - s->has_contrast = SANE_FALSE; - s->has_reverse = SANE_TRUE; - s->read_mode = READ_MODE_3091RGB; - s->color_raster_offset = get_IN_raster (s->buffer); - s->duplex_raster_offset = get_IN_frontback (s->buffer); - s->duplex_present = - (get_IN_duplex_3091 (s->buffer)) ? SANE_TRUE : SANE_FALSE; - } - else if ((s->model == MODEL_3096) || (s->model == MODEL_3093) - || (s->model == MODEL_3097) || (s->model == MODEL_4097)) - { - int i; - s->has_adf = SANE_TRUE; - s->has_fb = SANE_TRUE; - s->color_raster_offset = 0; - s->duplex_raster_offset = 0; + /* may have been changed by constrain, so dont copy until now */ + val_c = *(SANE_Word *)val; - /* no info about the 3093DG, seems to have trouble when trying to - * alternate read in linear mode. + /* + * Note - for those options which can assume one of a list of + * valid values, we can safely assume that they will have + * exactly one of those values because that's what + * sanei_constrain_value does. Hence no "else: invalid" branches + * below. */ - s->can_read_alternate = SANE_FALSE; - s->read_mode = READ_MODE_PASS; - - s->ipc_present = (strchr (s->productName, 'i')) ? 1 : 0; - s->cmp_present = (strchr (s->productName, 'm')) ? 1 : 0; - s->duplex_present = (strchr (s->productName, 'd')) ? 1 : 0; - if (s->ipc_present) - { - s->has_outline = 1; - s->has_emphasis = 1; - s->has_autosep = 1; - s->has_mirroring = 1; - } - - s->x_res_list[0] = 0; - s->y_res_list[0] = 0; - - s->x_res_range.min = s->y_res_range.min = 50; - s->x_res_range.quant = s->y_res_range.quant = 0; - s->x_grey_res_range.min = s->y_grey_res_range.min = 50; - s->x_grey_res_range.quant = s->y_grey_res_range.quant = 0; - if (!strncmp (product, "M3093DG", 7) || !strncmp (product, "M4097D", 6)) - { - - if (!strncmp (product, "M4097D", 6)) - { - s->can_read_alternate = SANE_TRUE; - s->read_mode = READ_MODE_PASS; - } - - for (i = 0; i <= res_list3[0]; i++) - { - s->y_res_list[i] = s->x_res_list[i] = res_list3[i]; - } - for (i = 0; i <= res_list2[0]; i++) - { - s->x_res_list_grey[i] = s->y_res_list_grey[i] = res_list2[i]; - } - - if (s->cmp_present) - { - s->x_res_range.max = s->y_res_range.max = 800; - s->x_res_range.quant = s->y_res_range.quant = 1; - s->x_grey_res_range.max = s->y_grey_res_range.max = 400; - s->x_grey_res_range.quant = s->y_grey_res_range.quant = 1; - } - } - else - { - /* M3093GX/M3096GX */ - for (i = 0; i <= res_list1[0]; i++) - { - s->y_res_list[i] = s->x_res_list[i] = res_list1[i]; - } - for (i = 0; i <= res_list0[0]; i++) - { - s->x_res_list_grey[i] = s->y_res_list_grey[i] = res_list0[i]; - } - if (s->cmp_present) - { - s->x_res_range.max = s->y_res_range.max = 400; - s->x_res_range.quant = s->y_res_range.quant = 1; - } - } - } - else if (s->model == MODEL_FI4x20) - { - s->can_read_alternate = SANE_TRUE; - s->read_mode = READ_MODE_BGR; - s->has_dropout_color = SANE_TRUE; - } - else if (s->model == MODEL_FI) - { - - s->can_read_alternate = SANE_TRUE; - s->read_mode = READ_MODE_RRGGBB; - s->ipc_present = (strchr ((s->productName)+3, 'i')) ? 1 : 0; - if (!strncmp (product, "fi-4340C", 8)) - { - s->has_dropout_color = SANE_TRUE; - } - - else if (!strncmp (product, "fi-4530C", 8)) - { - s->has_dropout_color = SANE_TRUE; - s->read_mode = READ_MODE_BGR; - } - else - { - s->has_dropout_color = SANE_TRUE; - } - } - else - { - s->has_adf = SANE_FALSE; - } - - - /* initialize scanning- and adf paper size */ + switch (option) { + + /* Mode Group */ + case OPT_SOURCE: + if (!strcmp (val, source_ADFFront)) { + tmp = SOURCE_ADF_FRONT; + } + else if (!strcmp (val, source_ADFBack)) { + tmp = SOURCE_ADF_BACK; + } + else if (!strcmp (val, source_ADFDuplex)) { + tmp = SOURCE_ADF_DUPLEX; + } + else{ + tmp = SOURCE_FLATBED; + } - s->x_range.min = SANE_FIX (0); - s->x_range.quant = SANE_FIX (length_quant); + if (s->source == tmp) + return SANE_STATUS_GOOD; - s->y_range.min = SANE_FIX (0); - s->y_range.quant = SANE_FIX (length_quant); + s->source = tmp; + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - s->adf_width_range.min = SANE_FIX (0); - s->adf_width_range.quant = SANE_FIX (length_quant); + case OPT_MODE: + if (!strcmp (val, mode_Lineart)) { + tmp = MODE_LINEART; + } + else if (!strcmp (val, mode_Halftone)) { + tmp = MODE_HALFTONE; + } + else if (!strcmp (val, mode_Grayscale)) { + tmp = MODE_GRAYSCALE; + } + else{ + tmp = MODE_COLOR; + } - s->adf_height_range.min = SANE_FIX (0); - s->adf_height_range.quant = SANE_FIX (length_quant); + if (tmp == s->mode) + return SANE_STATUS_GOOD; + s->mode = tmp; + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - if (0 == strncmp (product, "M3093", 5)) - { - /* A4 scanner */ - if (0 == strncmp (product, "M3093DG", 7)) - { - s->x_range.max = SANE_FIX (219); - s->y_range.max = SANE_FIX (308); - s->adf_width_range.max = SANE_FIX (219); - s->adf_height_range.max = SANE_FIX (308); - } - else - { - s->x_range.max = SANE_FIX (215); - s->y_range.max = SANE_FIX (308); - s->adf_width_range.max = SANE_FIX (215); - s->adf_height_range.max = SANE_FIX (308); - } - } - else if (0 == strncmp (product, "M3092", 5)) - { - s->x_range.max = SANE_FIX (215); - s->y_range.max = SANE_FIX (305); - s->adf_width_range.max = SANE_FIX (215); - s->adf_height_range.max = SANE_FIX (305); - } - else - { - /* A3 scanner */ - s->x_range.max = SANE_FIX (308); - s->y_range.max = SANE_FIX (438); - s->adf_width_range.max = SANE_FIX (308); - s->adf_height_range.max = SANE_FIX (438); - } + case OPT_X_RES: + if (s->resolution_x == val_c) + return SANE_STATUS_GOOD; - if (s->cmp_present) - { + /* currently the same? move y too */ + if (s->resolution_x == s->resolution_y){ + s->resolution_y = val_c; + /*sanei_constrain_value (s->opt + OPT_Y_RES, (void *) &val_c, 0) == SANE_STATUS_GOOD*/ + } - s->compression_mode_list[1] = cmp_mh; - s->compression_mode_list[2] = cmp_mr; - s->compression_mode_list[3] = cmp_mmr; - s->compression_mode_list[4] = NULL; - } - else - { + s->resolution_x = val_c; - s->compression_mode_list[1] = NULL; - } + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - if (SANE_STATUS_GOOD == getVitalProductData (s)) - { - /* This scanner supports vital product data. - * Use this data to set dpi-lists etc. - */ - int i, i_num_res; + case OPT_Y_RES: + if (s->resolution_y == val_c) + return SANE_STATUS_GOOD; - /* - * calculate maximum window with and window length. - */ - s->x_range.max = SANE_FIX(get_IN_window_width(s->buffer) * MM_PER_INCH / - get_IN_basic_x_res(s->buffer) ); - s->y_range.max = SANE_FIX(get_IN_window_length(s->buffer)* MM_PER_INCH / - get_IN_basic_y_res(s->buffer) ); + s->resolution_y = val_c; + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - DBG(DEBUG, "range: %d %d\n",s->x_range.max, s->y_range.max); - /* - * set possible resolutions. - */ - s->x_res_range.quant = get_IN_step_x_res (s->buffer); - s->y_res_range.quant = get_IN_step_y_res (s->buffer); - s->x_res_range.max = get_IN_max_x_res (s->buffer); - s->y_res_range.max = get_IN_max_y_res (s->buffer); - s->x_res_range.min = get_IN_min_x_res (s->buffer); - s->y_res_range.min = get_IN_min_y_res (s->buffer); + /* Geometry Group */ + case OPT_TL_X: + if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) + return SANE_STATUS_GOOD; - i_num_res = 0; - if (get_IN_std_res_60 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 60; - } - if (get_IN_std_res_75 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 75; - } - if (get_IN_std_res_100 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 100; - } - if (get_IN_std_res_120 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 120; - } - if (get_IN_std_res_150 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 150; - } - if (get_IN_std_res_160 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 160; - } - if (get_IN_std_res_180 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 180; - } - if (get_IN_std_res_200 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 200; - } + s->tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c); - if (get_IN_std_res_240 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 240; - } - if (get_IN_std_res_300 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 300; - } - if (get_IN_std_res_320 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 320; - } - if (get_IN_std_res_400 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 400; - } - if (get_IN_std_res_480 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 480; - } - if (get_IN_std_res_600 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 600; - } - if (get_IN_std_res_800 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 800; - } - if (get_IN_std_res_1200 (s->buffer)) - { - i_num_res++; - s->x_res_list[i_num_res] = 1200; - } + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - s->x_res_list[0] = i_num_res; + case OPT_TL_Y: + if (s->tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) + return SANE_STATUS_GOOD; - for (i = 0; i <= i_num_res; i++) - { - s->y_res_list[i] = s->x_res_list[i]; - /* can't get resolutions for grey scale reading ???? */ - } + s->tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c); + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - if (get_IN_page_length (s->buffer) > 0x19) - { - /* VPD extended format present */ - s->has_adf = get_IN_adf (s->buffer); - s->has_fb = get_IN_flatbed(s->buffer); - s->duplex_present = get_IN_duplex (s->buffer); - s->has_hw_status = get_IN_has_hw_status (s->buffer); - s->has_outline = get_IN_ipc_outline_extraction (s->buffer); - s->has_emphasis = get_IN_ipc_image_emphasis (s->buffer); - s->has_autosep = get_IN_ipc_auto_separation (s->buffer); - s->has_mirroring = get_IN_ipc_mirroring (s->buffer); - s->has_white_level_follow = - get_IN_ipc_white_level_follow (s->buffer); - s->has_subwindow = get_IN_ipc_subwindow (s->buffer); - if (!((s->model == MODEL_3091) || (s->model == MODEL_3092))) - s->has_reverse = get_IN_ipc_bw_reverse(s->buffer); - - /* - * get threshold, brightness and contrast ranges. - */ - s->has_threshold = (get_IN_threshold_steps(s->buffer) != 0); - if (s->has_threshold) - { - s->threshold_range.quant = (get_IN_threshold_steps(s->buffer) / 255); - } - s->has_contrast = (get_IN_contrast_steps(s->buffer) != 0); - if (s->has_contrast) - { - s->contrast_range.quant = (get_IN_contrast_steps(s->buffer) / 255); - } - s->has_brightness = (get_IN_brightness_steps(s->buffer) != 0); - if (s->has_brightness) - { - s->brightness_range.quant = (get_IN_brightness_steps(s->buffer)/255); - } + case OPT_BR_X: + if (s->br_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) + return SANE_STATUS_GOOD; - /* - * setup mode list. - * caution: vpd_mode_list has constant size (see header file) - */ - i = 0; - if (get_IN_monochrome (s->buffer)) - { - - s->vpd_mode_list[i] = mode_lineart; - i++; - } - if (get_IN_half_tone (s->buffer)) - { - - s->vpd_mode_list[i] = mode_halftone; - i++; - } - if (get_IN_multilevel (s->buffer)) - { - - s->vpd_mode_list[i] = mode_grayscale; - i++; - } - if (get_IN_multilevel_rgb (s->buffer)) - { - - s->vpd_mode_list[i] = mode_color; - i++; - } - s->vpd_mode_list[i] = NULL; - + s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); - /* - * setup compression modes - */ - i = 1; - /* compression_mode_list[0] = no compression */ - if (get_IN_compression_MH (s->buffer)) - { + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = cmp_mh; - s->cmp_present = SANE_TRUE; - i++; - } - if (get_IN_compression_MR (s->buffer)) - { + case OPT_BR_Y: + if (s->br_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = cmp_mr; - s->cmp_present = SANE_TRUE; - i++; - } - if (get_IN_compression_MMR (s->buffer)) - { + s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); - s->compression_mode_list[i] = cmp_mmr; - s->cmp_present = SANE_TRUE; - i++; - } - if (get_IN_compression_JBIG (s->buffer)) - { + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = cmp_jbig; - s->cmp_present = SANE_TRUE; - i++; - } + case OPT_PAGE_WIDTH: + if (s->page_width == FIXED_MM_TO_SCANNER_UNIT(val_c)) + return SANE_STATUS_GOOD; - if (get_IN_compression_JPG_BASE (s->buffer)) - { + s->page_width = FIXED_MM_TO_SCANNER_UNIT(val_c); + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = cmp_jpg_base; - s->cmp_present = SANE_TRUE; - i++; - } - if (get_IN_compression_JPG_EXT (s->buffer)) - { + case OPT_PAGE_HEIGHT: + if (s->page_height == FIXED_MM_TO_SCANNER_UNIT(val_c)) + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = cmp_jpg_ext; - s->cmp_present = SANE_TRUE; - i++; - } - if (get_IN_compression_JPG_INDEP (s->buffer)) - { + s->page_height = FIXED_MM_TO_SCANNER_UNIT(val_c); + *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = cmp_jpg_indep; - s->cmp_present = SANE_TRUE; - i++; - } + /* Enhancement Group */ + case OPT_BRIGHTNESS: + s->brightness = val_c; + return SANE_STATUS_GOOD; - s->compression_mode_list[i] = NULL; - s->cmp_present = (i > 1); + case OPT_THRESHOLD: + s->threshold = val_c; + return SANE_STATUS_GOOD; - s->has_imprinter = get_IN_imprinter(s->buffer); + case OPT_CONTRAST: + s->contrast = val_c; + return SANE_STATUS_GOOD; - s->has_gamma = get_IN_num_gamma(s->buffer); - if(s->model == MODEL_3092 || s->model == MODEL_3091){ /* says it supports gamma download, but it lies */ - s->num_download_gamma = 0; - } - else{ - s->num_download_gamma = get_IN_num_gamma_download (s->buffer); - } - } - } - if (s->has_adf) - { - if (!strncmp (product, "fi-4530C", 8) || - !strncmp (product, "M3091", 5) || - !strncmp (product, "M3092", 5) || - s->model == MODEL_FI4x20) - { - /* These scanner don't support standard paper size specification - * in bye 0x35 of the window descriptor block - */ - s->has_fixed_paper_size = SANE_FALSE; - } - else - { - s->has_fixed_paper_size = SANE_TRUE; - } - - } - else - { - s->has_fixed_paper_size = SANE_FALSE; - } + case OPT_RIF: + s->rif = val_c; + return SANE_STATUS_GOOD; - s->object_count = 0; + /* Advanced Group */ + case OPT_DROPOUT_COLOR: + if (!strcmp(val, color_Default)) + s->dropout_color = COLOR_DEFAULT; + else if (!strcmp(val, color_Red)) + s->dropout_color = COLOR_RED; + else if (!strcmp(val, color_Green)) + s->dropout_color = COLOR_GREEN; + else if (!strcmp(val, color_Blue)) + s->dropout_color = COLOR_BLUE; + if (s->has_MS_dropout) + return mode_select_dropout(s); + else + return SANE_STATUS_GOOD; - DBG (10, "\tADF:%s present\n", s->has_adf ? "" : " not"); - DBG (10, "\tDuplex Unit:%s present\n", s->duplex_present ? "" : " not"); - DBG (10, "\tDuplex Raster Offset: %d\n", s->duplex_raster_offset); - DBG (10, "\tColor Raster Offset: %d\n", s->color_raster_offset); + case OPT_SLEEP_TIME: + s->sleep_time = val_c; + return set_sleep_mode(s); - return 0; + case OPT_DUPLEX_OFFSET: + s->duplex_offset = val_c; + return SANE_STATUS_GOOD; -} + case OPT_GREEN_OFFSET: + s->green_offset = val_c; + return SANE_STATUS_GOOD; + case OPT_BLUE_OFFSET: + s->blue_offset = val_c; + return SANE_STATUS_GOOD; -/** - * Finds a matching MODEL_... code for a textual product name. - * - * Returns -1 if nothing can be found. - */ -static int -modelMatch (const char *product) -{ - if (strstr (product, "3091")) - { - return MODEL_3091; - } - else if (strstr (product, "3092")) - { - return MODEL_3092; - } - else if (strstr (product, "3096")) - { - return MODEL_3096; - } - else if (strstr (product, "3097")) - { - return MODEL_3097; - } - else if (strstr (product, "4097")) - { - return MODEL_4097; - } - else if (strstr (product, "4099")) - { - return MODEL_4097; - } - else if (strstr (product, "3093")) - { - return MODEL_3093; - } - else if (strstr (product, "fi-4120") || strstr (product, "fi-4220")) - { - return MODEL_FI4x20; - } - else if (strstr (product, "fi-")) - { - return MODEL_FI; - } + case OPT_USE_SWAPFILE: + s->use_temp_file = val_c; + return SANE_STATUS_GOOD; - return -1; + } /* switch */ + } /* else */ + + return SANE_STATUS_INVAL; } -static int -imprinter(struct fujitsu *s) +static SANE_Status +set_sleep_mode(struct fujitsu *s) { int ret; - unsigned char *command; - int i_size; - DBG (10, "imprinter\n"); - - ret = 0; - - if (s->has_imprinter) - { - memcpy (s->buffer, imprinterB.cmd, imprinterB.size); - memcpy (s->buffer + imprinterB.size, - imprinter_descB.cmd, imprinter_descB.size); - - command = s->buffer + imprinterB.size; - if (s->use_imprinter) { - set_IMD_enable(command, IMD_enable); - } else { - set_IMD_enable(command, IMD_disable); - } - - set_IMD_side(command, IMD_back); - if (0) { - - /* 16 bit counter */ - set_IMD_format(command, IMD_16_bit); - set_IMD_initial_count_16(command, s->imprinter_ctr_init); - i_size = 4; - } else { - - /* 24 bit counter */ - set_IMD_format(command, IMD_24_bit); - set_IMD_initial_count_24(command, s->imprinter_ctr_init); - i_size = 6; - } - - set_IM_xfer_length(s->buffer, i_size); - hexdump (MSG_IO, "imprinter", s->buffer, - imprinterB.size + i_size); - + DBG (10, "set_sleep_mode: start\n"); - if (s->sfd < 0) - { - int sfd; + set_MSEL_xfer_length (mode_selectB.cmd, mode_select_sleepB.size); + set_MSEL_sleep_mode(mode_select_sleepB.cmd, s->sleep_time); - if (s->connection == SANE_FUJITSU_USB) { - DBG (10, "imprinter opening USB device\n"); - if (sanei_usb_open (s->devicename, &sfd) != SANE_STATUS_GOOD) { - DBG (5, "imprinter: open failed\n"); - return SANE_STATUS_INVAL; - } - } else if (s->connection == SANE_FUJITSU_SCSI) { - DBG (10, "imprinter opening SCSI device\n"); - if (sanei_scsi_open - (s->devicename, &sfd, scsi_sense_handler, 0) != 0) { - DBG (5, "imprinter: open failed\n"); - return SANE_STATUS_INVAL; - } - } - - fujitsu_wait_scanner(s->connection, sfd); - ret = do_cmd (s->connection, sfd, s->buffer, imprinterB.size + i_size, - NULL, 0, NULL); - if (s->connection == SANE_FUJITSU_USB) { - sanei_usb_close (sfd); - } else if (s->connection == SANE_FUJITSU_SCSI) { - sanei_scsi_close (sfd); - } - } - else - { - ret = do_cmd (s->connection, s->sfd, s->buffer, - imprinterB.size + i_size, - NULL, 0, NULL); - } + ret = do_cmd ( + s, 1, 0, + mode_selectB.cmd, mode_selectB.size, + mode_select_sleepB.cmd, mode_select_sleepB.size, + NULL, 0 + ); - if (!ret) - { - - DBG (10, "imprinter: ok\n"); - } - } + DBG (10, "set_sleep_mode: finish\n"); return ret; } -static int -fujitsu_send(struct fujitsu *s) +static SANE_Status +get_hardware_status (struct fujitsu *s) { - int ret; - unsigned char *command; - char *cp_string = "%05ud"; - int i_strlen; - int i_yoffset; - int i_ymin; - - DBG (10, "send\n"); + SANE_Status ret = SANE_STATUS_GOOD; - ret = 0; - - if (s->has_imprinter && s->use_imprinter) - { + DBG (10, "get_hardware_status: start\n"); - memcpy (s->buffer, sendB.cmd, sendB.size); - memcpy (s->buffer + sendB.size, - send_imprinterB.cmd, - send_imprinterB.size); - - cp_string = s->imprinter_string; - i_strlen = strlen(cp_string); - command = s->buffer; - set_S_datatype_code(command, S_datatype_imprinter_data); - set_S_xfer_length(command, send_imprinterB.size + i_strlen); - - command += sendB.size; - set_imprinter_cnt_dir(command, S_im_dir_inc); - set_imprinter_lap24(command, S_im_ctr_16bit); - set_imprinter_cstep(command, 1); + /* only run this once every couple seconds */ + if (s->last_ghs + GHS_TIME < time(NULL)) { - /* minimum offset is 7mm. You have to set i_yoffset to 0 to - * have 7mm offset. - */ - i_ymin = FIXED_MM_TO_SCANNER_UNIT(7); - i_yoffset = FIXED_MM_TO_SCANNER_UNIT(s->imprinter_y_offset); - if (i_yoffset >= i_ymin) - { - i_yoffset = i_yoffset - i_ymin; - } - else - { - i_yoffset = 0; - } - set_imprinter_uly(command, i_yoffset); + DBG (15, "get_hardware_status: running\n"); - set_imprinter_dirs(command, s->imprinter_direction); - set_imprinter_string_length(command, i_strlen); + if (s->has_cmd_hw_status){ + unsigned char buffer[10]; - command += send_imprinterB.size; - memcpy(command, cp_string, i_strlen); + DBG (15, "get_hardware_status: calling ghs\n"); + + set_HW_allocation_length (hw_statusB.cmd, 10); + + ret = do_cmd ( + s, 1, 0, + hw_statusB.cmd, hw_statusB.size, + NULL, 0, + buffer, 10 + ); + + if (ret == SANE_STATUS_GOOD) { + + s->last_ghs = time(NULL); + + s->hw_top = get_HW_top(buffer); + s->hw_A3 = get_HW_A3(buffer); + s->hw_B4 = get_HW_B4(buffer); + s->hw_A4 = get_HW_A4(buffer); + s->hw_B5 = get_HW_B5(buffer); + + s->hw_hopper = get_HW_hopper(buffer); + s->hw_omr = get_HW_omr(buffer); + s->hw_adf_open = get_HW_adf_open(buffer); + + s->hw_sleep = get_HW_sleep(buffer); + s->hw_send_sw = get_HW_send_sw(buffer); + s->hw_manual_feed = get_HW_manual_feed(buffer); + s->hw_scan_sw = get_HW_scan_sw(buffer); + + s->hw_function = get_HW_function(buffer); + s->hw_ink_empty = get_HW_ink_empty(buffer); - hexdump (MSG_IO, "send", s->buffer, - i_strlen + send_imprinterB.size + sendB.size); + s->hw_double_feed = get_HW_double_feed(buffer); + + s->hw_error_code = get_HW_error_code(buffer); + + s->hw_skew_angle = get_HW_skew_angle(buffer); + + s->hw_ink_remain = get_HW_ink_remain(buffer); + + } + } - ret = do_cmd (s->connection, s->sfd, s->buffer, - i_strlen + send_imprinterB.size + sendB.size, - NULL, 0, NULL); + /* 3091/2 put hardware status in RS data */ + else if (s->ghs_in_rs){ + unsigned char buffer[RS_return_size]; + + DBG(15,"get_hardware_status: calling rs\n"); - } + ret = do_cmd( + s,0,0, + request_senseB.cmd, request_senseB.size, + NULL,0, + buffer,RS_return_size + ); + + /* parse the rs data */ + if(ret == SANE_STATUS_GOOD && get_RS_sense_key(buffer)==0 && get_RS_ASC(buffer)==0x80){ - if (!ret) - { + s->last_ghs = time(NULL); + + s->hw_adf_open = get_RS_adf_open(buffer); + s->hw_send_sw = get_RS_send_sw(buffer); + s->hw_scan_sw = get_RS_scan_sw(buffer); + s->hw_duplex_sw = get_RS_duplex_sw(buffer); + s->hw_top = get_RS_top(buffer); + s->hw_hopper = get_RS_hopper(buffer); + s->hw_function = get_RS_function(buffer); + s->hw_density_sw = get_RS_density(buffer); + } + else{ + return SANE_STATUS_GOOD; + } + } + } - DBG (10, "send: ok\n"); - } + DBG (10, "get_hardware_status: finish\n"); return ret; } -/** - * This routine issues a SCSI SET WINDOW command to the scanner, using the - * values currently in the scanner data structure. - * - * The routine relies heavily on macros defined in fujitsu-scsi.h for manipulation - * of the SCSI data block. - */ -static int -setWindowParam (struct fujitsu *s) +static SANE_Status +mode_select_dropout (struct fujitsu *s) { - unsigned char buffer[max_WDB_size]; int ret; - int scsiCommandLength = 0; - scsiblk *setwinB; - - /* the amout of window data we're going to send. may have to be reduced - * depending on scanner model. */ - - int windowDataSize; - - if ((s->model == MODEL_3091) || (s->model == MODEL_3092)) - { - windowDataSize = 0x60; - } - else - { - windowDataSize = 0x40; - } - - /* compiler was unhappy about me using "assert" ...? */ - if (windowDataSize > window_descriptor_blockB.size) - { - DBG (MSG_ERR, - "backend is broken - cannot transfer larger window descriptor than defined in header file\n"); - } - - wait_scanner (s); - DBG (10, "set_window_param\n"); - - /* Initialize the buffer with pre-defined data from the header file. */ - - memset (buffer, '\0', max_WDB_size); - memcpy (buffer, window_descriptor_blockB.cmd, - window_descriptor_blockB.size); - - set_WD_Xres (buffer, s->resolution_x); /* x resolution in dpi */ - set_WD_Yres (buffer, s->resolution_y); /* y resolution in dpi */ - - set_WD_ULX (buffer, s->left_margin); - set_WD_ULY (buffer, s->top_margin); - set_WD_width (buffer, s->scan_width); - set_WD_length (buffer, s->scan_height); - - set_WD_brightness (buffer, s->brightness); - set_WD_threshold (buffer, s->threshold); - set_WD_contrast (buffer, s->contrast); - set_WD_composition (buffer, (s->color_mode == MODE_COLOR) ? WD_comp_RC : - (s->color_mode == MODE_GRAYSCALE) ? WD_comp_GS : - (s->color_mode == - MODE_HALFTONE) ? WD_comp_HT : WD_comp_LA); - - if ((s->model == MODEL_3091) || (s->model == MODEL_3092)) - { - set_WD_lamp_color (buffer, s->lamp_color); - } - else - { - set_WD_mirroring (buffer, s->mirroring); - } - /* bpp must be 8 even for RGB scans */ - set_WD_bitsperpixel (buffer, - (s->color_mode == MODE_COLOR) ? 8 : s->scanner_depth); - - set_WD_rif (buffer, s->reverse); - - set_WD_brightness (buffer, s->brightness); - set_WD_threshold (buffer, s->threshold); - set_WD_contrast (buffer, s->contrast); - /* set_WD_halftone (buffer, s->halftone); */ - set_WD_bitorder (buffer, s->bitorder); - set_WD_compress_type (buffer, s->compress_type); - set_WD_compress_arg (buffer, s->compress_arg); - set_WD_gamma (buffer, s->gamma); - set_WD_outline (buffer, s->outline); - set_WD_emphasis (buffer, s->emphasis); - set_WD_auto_sep (buffer, s->auto_sep); - set_WD_mirroring (buffer, s->mirroring); - - /* do not use these for 3091, it doesn't like non-zero in the - * reserved fields */ - if ((s->model != MODEL_3091) && (s->model != MODEL_3092)) - { - set_WD_var_rate_dyn_thresh (buffer, s->var_rate_dyn_thresh); - set_WD_dtc_threshold_curve (buffer, s->dtc_threshold_curve); - set_WD_dtc_selection (buffer, s->dtc_selection); - set_WD_gradation (buffer, s->gradation); - set_WD_smoothing_mode (buffer, s->smoothing_mode); - set_WD_filtering (buffer, s->filtering); - set_WD_background (buffer, s->background); - set_WD_matrix2x2 (buffer, s->matrix2x2); - set_WD_matrix3x3 (buffer, s->matrix3x3); - set_WD_matrix4x4 (buffer, s->matrix4x4); - set_WD_matrix5x5 (buffer, s->matrix5x5); - set_WD_noise_removal (buffer, s->noise_removal); - set_WD_white_level_follow (buffer, s->white_level_follow); - } + DBG (10, "mode_select_dropout: start\n"); - if ((s->model != MODEL_3091) && (s->model != MODEL_3092)) - { - /* documentation says that 0xc0 is valid, but this isn't tru - * for the M3093 - */ - set_WD_vendor_id_code (buffer, 0); - } + set_MSEL_xfer_length (mode_selectB.cmd, mode_select_dropoutB.size); + set_MSEL_dropout_front (mode_select_dropoutB.cmd, s->dropout_color); + set_MSEL_dropout_back (mode_select_dropoutB.cmd, s->dropout_color); + + ret = do_cmd ( + s, 1, 0, + mode_selectB.cmd, mode_selectB.size, + mode_select_dropoutB.cmd, mode_select_dropoutB.size, + NULL, 0 + ); - set_WD_paper_size (buffer, s->paper_size); - set_WD_paper_orientation(buffer, s->paper_orientation); - set_WD_paper_selection(buffer, s->paper_selection); - - if (s->paper_selection != WD_paper_SEL_STANDARD) { - set_WD_paper_width_X (buffer, s->page_width); - set_WD_paper_length_Y (buffer, s->page_height); - } else { - set_WD_paper_width_X (buffer, 0); - set_WD_paper_length_Y (buffer, 0); - } - - /* prepare SCSI buffer */ - - /* The full command has the following format: - * - the "command descriptor block" which includes the operation - * code 24h (SET WINDOW) and the total amount of data following - * - then, a window data header that basically only specifies the number - * of bytes that make up one window description - * - then, window data - * that's it for simplex; for duplex, a second batch of window data - * follows without a header of its own. - */ + DBG (10, "mode_select_dropout: finish\n"); - if ( ( (s->model == MODEL_FI4x20) || (s->model == MODEL_FI) ) && (s->connection == SANE_FUJITSU_USB) ) { - setwinB = &set_usb_windowB; - } else { - setwinB = &set_windowB; - } + return ret; +} - /* SET WINDOW command and command descriptor block. - * The transfer length will be filled in later using set_SW_xferlen. - */ - memcpy (s->buffer, setwinB->cmd, setwinB->size); - /* header for first set of window data */ - memcpy ((s->buffer + setwinB->size), - window_parameter_data_blockB.cmd, - window_parameter_data_blockB.size); +/* + * @@ Section 4 - SANE scanning functions + */ +/* + * Called by SANE to retrieve information about the type of data + * that the current scan will return. + * + * From the SANE spec: + * This function is used to obtain the current scan parameters. The + * returned parameters are guaranteed to be accurate between the time + * a scan has been started (sane_start() has been called) and the + * completion of that request. Outside of that window, the returned + * values are best-effort estimates of what the parameters will be + * when sane_start() gets invoked. + * + * Calling this function before a scan has actually started allows, + * for example, to get an estimate of how big the scanned image will + * be. The parameters passed to this function are the handle h of the + * device for which the parameters should be obtained and a pointer p + * to a parameter structure. + */ +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + struct fujitsu *s = (struct fujitsu *) handle; - /* set window data size */ - set_WPDB_wdblen ((s->buffer + setwinB->size), windowDataSize); + DBG (10, "sane_get_parameters: start\n"); - if (s->duplex_mode == DUPLEX_BACK) - { - set_WD_wid (buffer, WD_wid_back); - } - else - { - set_WD_wid (buffer, WD_wid_front); - } + calculateDerivedValues (s); - /* now copy window data itself */ - memcpy (s->buffer + setwinB->size + window_parameter_data_blockB.size, - buffer, windowDataSize); + params->format = s->params.format; + params->last_frame = s->params.last_frame; + params->lines = s->params.lines; + params->depth = s->params.depth; + params->pixels_per_line = s->params.pixels_per_line; + params->bytes_per_line = s->params.bytes_per_line; - /* when in duplex mode, add a second window */ - if (s->duplex_mode == DUPLEX_BOTH) - { - hexdump (MSG_IO, "Window set - front", buffer, windowDataSize); - set_WD_wid (buffer, WD_wid_back); - if ((s->model != MODEL_3091) && (s->model != MODEL_3092)) - { + DBG (15, "\tdepth %d\n", params->depth); + DBG (15, "\tlines %d\n", params->lines); + DBG (15, "\tpixels_per_line %d\n", params->pixels_per_line); + DBG (15, "\tbytes_per_line %d\n", params->bytes_per_line); - /* bytes 35 - 3D have to be 0 for back-side */ - set_WD_paper_size (buffer, WD_paper_UNDEFINED); - set_WD_paper_orientation (buffer, WD_paper_PORTRAIT); - set_WD_paper_selection (buffer, WD_paper_SEL_UNDEFINED); - set_WD_paper_width_X (buffer, 0); - set_WD_paper_length_Y (buffer, 0); - } - hexdump (MSG_IO, "Window set - back", buffer, windowDataSize); - memcpy (s->buffer + setwinB->size + - window_parameter_data_blockB.size + windowDataSize, buffer, - windowDataSize); - set_SW_xferlen (s->buffer, - (window_parameter_data_blockB.size + - 2 * windowDataSize)); - scsiCommandLength = - window_parameter_data_blockB.size + 2 * windowDataSize + - setwinB->size; - } - else - { - hexdump (MSG_IO, "Window set", buffer, windowDataSize); - set_SW_xferlen (s->buffer, (window_parameter_data_blockB.size + - windowDataSize)); - scsiCommandLength = - window_parameter_data_blockB.size + windowDataSize + setwinB->size; - } + DBG (10, "sane_get_parameters: finish\n"); - ret = do_cmd (s->connection, s->sfd, s->buffer, scsiCommandLength, - NULL, 0, NULL); - if (ret) - return ret; - DBG (10, "set_window_param: ok\n"); - return ret; + return SANE_STATUS_GOOD; } /* - * Computes the scanner units used for the set_window command from the - * millimeter units. + * Computes the scanner units used for the set_window command */ void -calculateDerivedValues (struct fujitsu *scanner) +calculateDerivedValues (struct fujitsu *s) { - DBG (12, "calculateDerivedValues\n"); + DBG (10, "calculateDerivedValues: start\n"); + DBG (5, "xres=%d, tlx=%d, brx=%d, pw=%d, maxx=%d\n", s->resolution_x, s->tl_x, s->br_x, s->page_width, s->max_x); + DBG (5, "yres=%d, tly=%d, bry=%d, ph=%d, maxy=%d\n", s->resolution_y, s->tl_y, s->br_y, s->page_height, s->max_y); - /* Convert the SANE_FIXED values for the scan area into 1/1200 inch - * scanner units */ + s->params.pixels_per_line = s->resolution_x * (s->br_x - s->tl_x) / 1200; + s->params.lines = s->resolution_y * (s->br_y - s->tl_y) / 1200; + s->params.last_frame = 1; - scanner->top_margin = FIXED_MM_TO_SCANNER_UNIT (scanner->val[OPT_TL_Y].w); - scanner->left_margin = FIXED_MM_TO_SCANNER_UNIT (scanner->val[OPT_TL_X].w); - scanner->scan_width = FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_x) - - scanner->left_margin; - scanner->scan_height = FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_y) - - scanner->top_margin; + if (s->mode == MODE_COLOR) { + s->params.format = SANE_FRAME_RGB; + s->params.depth = 8; + s->params.bytes_per_line = s->params.pixels_per_line * 3; + } + else if (s->mode == MODE_GRAYSCALE) { + s->params.format = SANE_FRAME_GRAY; + s->params.depth = 8; + s->params.bytes_per_line = s->params.pixels_per_line; + } + else if (s->mode == MODE_LINEART || s->mode == MODE_HALFTONE) { - DBG (12, "\ttop_margin: %u\n", scanner->top_margin); - DBG (12, "\tleft_margin: %u\n", scanner->left_margin); - DBG (12, "\tscan_width: %u\n", scanner->scan_width); - DBG (12, "\tscan_height: %u\n", scanner->scan_height); + /* increase scan width to full number of bytes in a scan line */ + while (s->params.pixels_per_line % 8) { + s->br_x++; + s->params.pixels_per_line = s->resolution_x * (s->br_x - s->tl_x) / 1200; + } - /* increase scan width until there's a full number of bytes in a scan line - * - only required if depth is not a multiple of 8. - */ - if (scanner->scanner_depth % 8) - { - while ((scanner->scan_width * scanner->resolution_x * - scanner->scanner_depth / 1200) % 8) - { - scanner->scan_width++; - } - DBG (12, "\tscan_width corrected: %u\n", scanner->scan_width); + /* dont round up larger than scanners max width */ + while (s->br_x > s->max_x || s->params.pixels_per_line % 8){ + s->br_x--; + s->params.pixels_per_line = s->resolution_x * (s->br_x - s->tl_x) / 1200; } + s->params.format = SANE_FRAME_GRAY; + s->params.depth = 1; + s->params.bytes_per_line = s->params.pixels_per_line / 8; + } + /*FIXME what about the other modes*/ + else{ + } - /* sepcial relationship between X and Y must be maintained for 3096 */ + DBG (5, "xres=%d, tlx=%d, brx=%d, pw=%d, maxx=%d\n", s->resolution_x, s->tl_x, s->br_x, s->page_width, s->max_x); + DBG (5, "yres=%d, tly=%d, bry=%d, ph=%d, maxy=%d\n", s->resolution_y, s->tl_y, s->br_y, s->page_height, s->max_y); +#if 0 + /* special relationship between X and Y must be maintained for 3096 */ if (( (scanner->model == MODEL_3096) || (scanner->model == MODEL_3097) )&& ((scanner->left_margin + scanner->scan_width) >= 13200)) { @@ -5023,14 +2859,6 @@ DBG (12, "\tscan_height corrected: %u\n", scanner->scan_height); } - scanner->scan_width_pixels = - scanner->resolution_x * scanner->scan_width / 1200; - scanner->scan_height_pixels = - scanner->resolution_y * scanner->scan_height / 1200; - scanner->bytes_per_scan_line = - (scanner->scan_width_pixels * scanner->scanner_depth + 7) / 8; - - /* Now reverse our initial calculations and compute the "real" values so * that the front-end has a chance to know. As an "added extra" we'll * also round the height and width to the resolution that has been set; @@ -5049,2776 +2877,1702 @@ SCANNER_UNIT_TO_FIXED_MM (scanner->top_margin + scanner->scan_height_pixels * 1200 / scanner->resolution_y); +#endif - /* And now something ugly. This is because the 3091 has this funny way - * of interspersing color values, you actually have to get more lines - * from the scanner than you want to process. Doesn't affect - * scan_height_pixels though! - */ - if (((scanner->model == MODEL_3091)||(scanner->model == MODEL_3092)) && (scanner->color_mode == MODE_COLOR)) - { - scanner->scan_height += 8 * scanner->color_raster_offset; - } - - DBG (12, "calculateDerivedValues: ok\n"); + DBG (10, "calculateDerivedValues: finish\n"); } -/** - * Coordinates the data acquisition from the scanner. +/* + * Called by SANE when a page acquisition operation is to be started. + * commands: scanner control (lampon), send (lut), send (dither), + * set window, object pos, and scan * - * This function is executed as a child process. + * this will be called between sides of a duplex scan, + * and at the start of each page of an adf batch. + * hence, we spend alot of time playing with s->started, etc. */ -static int -reader_process (void *s) +SANE_Status +sane_start (SANE_Handle handle) { - FILE *fp1, *fp2; - sigset_t ignore_set; - sigset_t sigterm_set; - struct SIGACTION act; - time_t start_time, end_time; - unsigned int total_data_size = 0; - - struct fujitsu *scanner = (struct fujitsu *) s; - - (void) time (&start_time); + struct fujitsu *s = handle; + SANE_Status ret; - DBG (10, "reader_process started\n"); + DBG (10, "sane_start: start\n"); - if (sanei_thread_is_forked()) - { - close (scanner->default_pipe_r); - if (scanner->duplex_pipe_r != -1) - close (scanner->duplex_pipe_r); - } + DBG (15, "started=%d, img_count=%d, source=%d\n", s->started, s->img_count, s->source); - sigfillset (&ignore_set); - sigdelset (&ignore_set, SIGTERM); -#if defined (__APPLE__) && defined (__MACH__) - sigdelset (&ignore_set, SIGUSR2); -#endif - sigprocmask (SIG_SETMASK, &ignore_set, 0); + /* first page of batch */ + if(!s->started){ - sigemptyset (&sigterm_set); - sigaddset (&sigterm_set, SIGTERM); + /* set clean defaults */ + s->started=1; + s->img_count=0; - memset (&act, 0, sizeof (act)); -#ifdef _POSIX_SOURCE - act.sa_handler = sigtermHandler; -#endif - sigaction (SIGTERM, &act, 0); + s->bytes_tot[0]=0; + s->bytes_tot[1]=0; - fp1 = fdopen (scanner->default_pipe_w, "w"); - if (!fp1) - { - DBG (MSG_ERR, "reader_process: couldn't open pipe!\n"); - return 1; - } - if (scanner->duplex_mode == DUPLEX_BOTH) - { - fp2 = fdopen (scanner->duplex_pipe_w, "w"); - if (!fp2) - { - DBG (MSG_ERR, "reader_process: couldn't open pipe!\n"); - return 1; - } - } - else - { - fp2 = 0; - } + s->bytes_rx[0]=0; + s->bytes_rx[1]=0; + s->lines_rx[0]=0; + s->lines_rx[1]=0; - DBG (10, "reader_process: starting to READ data\n"); + s->bytes_tx[0]=0; + s->bytes_tx[1]=0; - /** - * Depending on the scanner model and mode, we select and call the appropiate - * reader function. - * - * The main responsibility of the reader function is to accept raw SCSI data from the - * scanner and process it in a way that yields a "plain" bit stream, containing the - * values for pixels, row by row, from top-left to bottom-right, and if it's color, - * blue after green after red value for each pixel. - * - * In gray and lineart modes, it is not unlikely that the scanners send data in - * the right fashion already (so we can call the generic "passthrough" reader here), - * while color and/or duplex modes often require a bit of work ro get right. - */ - switch (scanner->model) - { - case MODEL_3091: - case MODEL_3092: - if ((scanner->color_mode == MODE_COLOR) - && (scanner->duplex_mode == DUPLEX_BOTH)) - total_data_size = reader3091ColorDuplex (scanner, fp1, fp2); - else if (scanner->color_mode == MODE_COLOR) - total_data_size = reader3091ColorSimplex (scanner, fp1); - else if (scanner->duplex_mode == DUPLEX_BOTH) - total_data_size = reader3091GrayDuplex (scanner, fp1, fp2); - else - total_data_size = reader_generic_passthrough (scanner, fp1, 0); - break; + /* send batch setup commands */ + ret = scanner_control (s, SC_function_lamp_on); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot start lamp\n"); + do_cancel(s); + return ret; + } + + /*FIXME: send lut and dither ?*/ + + /* get the scan size, etc */ + calculateDerivedValues(s); + + /* store the number of front bytes */ + if ( s->source != SOURCE_ADF_BACK ){ + s->bytes_tot[SIDE_FRONT] = s->params.bytes_per_line * s->params.lines; + } - case MODEL_FORCE: - case MODEL_3096: - case MODEL_3097: - case MODEL_3093: - case MODEL_4097: - case MODEL_FI: - if (scanner->duplex_mode == DUPLEX_BOTH) - { - if (scanner->color_mode == MODE_COLOR) - { - if (scanner->can_read_alternate) { - total_data_size = reader_duplex_alternate (scanner, fp1, fp2); - } - else { - total_data_size = reader_duplex_sequential (scanner, fp1, fp2); - } - } - else - { - if (scanner->can_read_alternate) - { - total_data_size = - reader_gray_duplex_alternate (scanner, fp1, fp2); - } - else - { - total_data_size = - reader_gray_duplex_sequential (scanner, fp1, fp2); - } - } - } - else - { - if (scanner->color_mode == MODE_COLOR) - { - total_data_size = reader_simplex (scanner, fp1, 0); - } - else - { - total_data_size = reader_generic_passthrough (scanner, fp1, 0); - } - } - break; + /* store the number of back bytes */ + if ( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK ){ + s->bytes_tot[SIDE_BACK] = s->params.bytes_per_line * s->params.lines; + } + /* make temp file/large buffer to hold the image */ + ret = setup_buffers(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot load buffers\n"); + do_cancel(s); + return ret; + } - case MODEL_FI4x20: - if (scanner->duplex_mode == DUPLEX_BOTH) { + ret = set_window(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: failed to set window\n"); + do_cancel(s); + return ret; + } - if (scanner->color_mode == MODE_COLOR){ - if (scanner->can_read_alternate) { - total_data_size = reader_duplex_alternate (scanner, fp1, fp2); - } - else { - total_data_size = reader_duplex_sequential (scanner, fp1, fp2); - } - } - else { - if (scanner->can_read_alternate) { - total_data_size = reader_gray_duplex_alternate (scanner, fp1, fp2); - } - else { - total_data_size = reader_gray_duplex_sequential (scanner, fp1, fp2); - } - } + ret = object_position (s, SANE_TRUE); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot load page\n"); + do_cancel(s); + return ret; + } + ret = start_scan (s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot start_scan\n"); + do_cancel(s); + return ret; } - else { /*simplex*/ + } - if (scanner->color_mode == MODE_COLOR){ - total_data_size = reader_simplex (scanner, fp1, 0); - } - else { - total_data_size = reader_generic_passthrough (scanner, fp1, 0); - } + /* already in a batch */ + else { + int side = get_current_side(s); + /* not finished with current side, error */ + if (s->bytes_tx[side] != s->bytes_tot[side]) { + DBG(5,"sane_start: previous transfer not finished?"); + return do_cancel(s); + } + + /* Finished with previous img, jump to next */ + s->img_count++; + side = get_current_side(s); + + /* dont reset the transfer vars on backside of duplex page */ + /* otherwise buffered back page will be lost */ + /* dont call object pos or scan on back side of duplex scan */ + if (s->source == SOURCE_ADF_DUPLEX && side == SIDE_BACK) { + DBG (15, "sane_start: using buffered duplex backside\n"); + } + else{ + s->bytes_rx[0]=0; + s->bytes_rx[1]=0; + s->lines_rx[0]=0; + s->lines_rx[1]=0; + + s->bytes_tx[0]=0; + s->bytes_tx[1]=0; + + ret = object_position (s, SANE_TRUE); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot load page\n"); + do_cancel(s); + return ret; } - break; - default: - DBG (5, "reader_process: no implementation for this scanner model\n"); + ret = start_scan (s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot start_scan\n"); + do_cancel(s); + return ret; + } } + } - (void) time (&end_time); - if (end_time == start_time) - end_time++; + DBG (15, "started=%d, img_count=%d, source=%d\n", s->started, s->img_count, s->source); - DBG (10, "reader_process: finished, throughput was %lu bytes/second\n", - (u_long) total_data_size / (end_time - start_time)); + DBG (10, "sane_start: finish\n"); - return 0; + return SANE_STATUS_GOOD; +} + +/* figure out what side we are looking at currently */ +int +get_current_side (struct fujitsu * s){ + + int side = SIDE_FRONT; + if ( s->source == SOURCE_ADF_BACK || (s->source == SOURCE_ADF_DUPLEX && s->img_count % 2) ){ + side = SIDE_BACK; + } + + return side; } -/** - * This function presets the "option" array in the given data structure - * according to the capabilities of the detected scanner model. - */ static SANE_Status -init_options (struct fujitsu *scanner) +scanner_control (struct fujitsu *s, int function) { - int i; - SANE_Option_Descriptor *opt; - SANE_String_Const *valueList; + int ret = SANE_STATUS_GOOD; + int tries = 0; - DBG (10, "init_options\n"); + DBG (10, "scanner_control: start\n"); - /* - * set defaults for everything - */ - memset (scanner->opt, 0, sizeof (scanner->opt)); - for (i = 0; i < NUM_OPTIONS; ++i) - { - scanner->opt[i].name = "filler"; - scanner->opt[i].size = sizeof (SANE_Word); - scanner->opt[i].cap = SANE_CAP_INACTIVE; - } + if(s->has_cmd_scanner_ctl){ - opt = scanner->opt + OPT_NUM_OPTS; - opt->title = SANE_TITLE_NUM_OPTIONS; - opt->desc = SANE_DESC_NUM_OPTIONS; - opt->cap = SANE_CAP_SOFT_DETECT; + DBG (15, "scanner_control: power up lamp...\n"); - /* "Mode" group -------------------------------------------------------- */ + set_SC_function (scanner_controlB.cmd, function); + + /* extremely long retry period */ + while(tries++ < 120){ - opt = scanner->opt + OPT_MODE_GROUP; - opt->title = "Scan Mode"; - opt->desc = ""; - opt->type = SANE_TYPE_GROUP; - opt->constraint_type = SANE_CONSTRAINT_NONE; + ret = do_cmd ( + s, 1, 0, + scanner_controlB.cmd, scanner_controlB.size, + NULL, 0, + NULL, 0 + ); - /* source */ - opt = scanner->opt + OPT_SOURCE; - opt->name = SANE_NAME_SCAN_SOURCE; - opt->title = SANE_TITLE_SCAN_SOURCE; - opt->desc = SANE_DESC_SCAN_SOURCE; - opt->type = SANE_TYPE_STRING; - valueList = - (scanner->has_adf) ? sourceListWithADF : sourceListWithoutADF; - opt->size = maxStringSize (valueList); - opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->constraint.string_list = valueList; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if(ret == SANE_STATUS_GOOD){ + break; + } - /* scan mode */ - opt = scanner->opt + OPT_MODE; - opt->name = SANE_NAME_SCAN_MODE; - opt->title = SANE_TITLE_SCAN_MODE; - opt->desc = SANE_DESC_SCAN_MODE; - opt->type = SANE_TYPE_STRING; - if (scanner->vpd_mode_list[0] == NULL) - { + usleep(500000); - valueList = ((scanner->model == MODEL_3091)||(scanner->model == MODEL_3092)) ? modeList3091 : - ((scanner->model == MODEL_3096) || - (scanner->model == MODEL_3093) || - (scanner->model == MODEL_3097) || - (scanner->model == - MODEL_4097)) ? modeList3096 : scanner->vpd_mode_list; - /* add others */ - if (valueList[0] == NULL) - { + } - DBG (MSG_ERR, "no modes found for this scanner\n"); - } + if(ret == SANE_STATUS_GOOD){ + DBG (15, "scanner_control: lamp on, tries %d, ret %d\n",tries,ret); } - else - { - - valueList = scanner->vpd_mode_list; + else{ + DBG (5, "scanner_control: lamp error, tries %d, ret %d\n",tries,ret); } - opt->size = maxStringSize (valueList); - opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->constraint.string_list = valueList; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - - /* duplex mode */ - opt = scanner->opt + OPT_DUPLEX; - opt->name = "duplex"; - opt->title = "Duplex Mode"; - opt->desc = "Select if you want the front or back to be scanned, or both."; - opt->type = SANE_TYPE_STRING; - /* 3091 doesn't support "back side only" */ - valueList = (!scanner->duplex_present) ? duplexListWithoutDuplex : - ((scanner->model == MODEL_3091)||(scanner->model == MODEL_3092)) ? duplexListSpecial : duplexListWithDuplex; - opt->size = maxStringSize (valueList); - opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->constraint.string_list = valueList; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - /* resolution */ - opt = scanner->opt + OPT_X_RES; - opt->name = SANE_NAME_SCAN_X_RESOLUTION; - opt->title = SANE_TITLE_SCAN_X_RESOLUTION; - opt->desc = SANE_DESC_SCAN_X_RESOLUTION; - opt->type = SANE_TYPE_INT; - opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; - opt->constraint.word_list = scanner->x_res_list; - opt->unit = SANE_UNIT_DPI; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - opt = scanner->opt + OPT_Y_RES; - opt->name = SANE_NAME_SCAN_Y_RESOLUTION; - opt->title = SANE_TITLE_SCAN_Y_RESOLUTION; - opt->desc = SANE_DESC_SCAN_Y_RESOLUTION; - opt->type = SANE_TYPE_INT; - opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; - opt->constraint.word_list = scanner->y_res_list; - opt->unit = SANE_UNIT_DPI; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - /* the constraints for the resolution options are set in the "setMode..." - * functions because they depend on the selected scan mode. - * The "setDefaults..." function is called during initialization and will - * make sure that "setMode..." ist called. - */ - /* "Geometry" group ---------------------------------------------------- */ + } + + DBG (10, "scanner_control: finish\n"); + + return ret; +} + +/* + * Creates a temporary file, opens it, and stores file pointer for it. + * OR, callocs a buffer to hold the scan data + * + * Will only create a file that + * doesn't exist already. The function will also unlink ("delete") the file + * immediately after it is created. In any "sane" programming environment this + * has the effect that the file can be used for reading and writing as normal + * but vanishes as soon as it's closed - so no cleanup required if the + * process dies etc. + */ +static SANE_Status +setup_buffers (struct fujitsu *s) +{ + SANE_Status ret = SANE_STATUS_GOOD; + int side; + + DBG (10, "setup_buffers: start\n"); + + /* cleanup existing first */ + for(side=0;side<2;side++){ + + /* close old file */ + if (s->fds[side] != -1) { + DBG (15, "setup_buffers: closing old tempfile %d.\n",side); + if(close(s->fds[side])){ + DBG (5, "setup_buffers: attempt to close tempfile %d returned %d.\n", side, errno); + } + s->fds[side] = -1; + } + + /* free old mem */ + if (s->buffers[side]) { + DBG (15, "setup_buffers: free buffer %d.\n",side); + free(s->buffers[side]); + s->buffers[side] = NULL; + } + + if(s->bytes_tot[side]){ + s->buffers[side] = calloc (1,s->bytes_tot[side]); + if (!s->buffers[side]) { + DBG (5, "setup_buffers: Error, no buffer %d.\n",side); + return SANE_STATUS_NO_MEM; + } + } + } - opt = scanner->opt + OPT_GEOMETRY_GROUP; - opt->title = "Geometry"; - opt->desc = ""; - opt->type = SANE_TYPE_GROUP; - opt->constraint_type = SANE_CONSTRAINT_NONE; +/* + if (s->use_temp_file) { - /* top-left x */ - opt = scanner->opt + OPT_TL_X; - opt->name = SANE_NAME_SCAN_TL_X; - opt->title = SANE_TITLE_SCAN_TL_X; - opt->desc = SANE_DESC_SCAN_TL_X; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - switch (scanner->model) - { - case MODEL_3091: +#ifdef P_tmpdir + static const char *tmpdir = P_tmpdir; +#else + static const char *tmpdir = "/tmp"; +#endif + char filename[PATH_MAX]; + unsigned int suffix = time (NULL) % 256; + int try = 0; - opt->constraint.range = &rangeX3091; - break; - case MODEL_3092: + while (try++ < 10) { - opt->constraint.range = &rangeX3092; - break; - case MODEL_3096: + sprintf (filename, "%s%csane-fujitsu-%d-%d", tmpdir, PATH_SEP, getpid (), suffix + try); - opt->constraint.range = &rangeX3096; - break; - default: +#if defined(_POSIX_SOURCE) + s->duplex_fd = open (filename, O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR); +#else + s->duplex_fd = open (filename, O_RDWR | O_CREAT | O_EXCL); +#endif - opt->constraint.range = &(scanner->x_range); + if (s->duplex_fd == -1) { + DBG (5, "setup_duplex_buffer: attempt to create '%s' returned %d.\n", filename, errno); + } + else{ + DBG (15, "setup_duplex_buffer: file '%s' created.\n", filename); + unlink (filename); + break; + } } - /* - opt->constraint.range = (scanner->model == MODEL_3091) ? &rangeX3091 : - (scanner->model == MODEL_3096) ? &rangeX3096 : NULL; - */ - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (s->duplex_fd == -1) { + DBG (5, "setup_duplex_buffer: attempt to create tempfile failed.\n"); + ret = SANE_STATUS_IO_ERROR; + } + } +*/ - /* top-left y */ - opt = scanner->opt + OPT_TL_Y; - opt->name = SANE_NAME_SCAN_TL_Y; - opt->title = SANE_TITLE_SCAN_TL_Y; - opt->desc = SANE_DESC_SCAN_TL_Y; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - switch (scanner->model) - { - case MODEL_3091: + DBG (10, "setup_buffers: finish\n"); - opt->constraint.range = &rangeY3091; - break; - case MODEL_3092: + return ret; +} - opt->constraint.range = &rangeY3092; - break; - case MODEL_3096: +/* + * This routine issues a SCSI SET WINDOW command to the scanner, using the + * values currently in the scanner data structure. + */ +static int +set_window (struct fujitsu *s) +{ + unsigned char buffer[max_WDB_size]; + int ret, bufferLen; + int length = s->br_y - s->tl_y; - opt->constraint.range = &rangeY3096; - break; - default: + DBG (10, "set_window: start\n"); - opt->constraint.range = &(scanner->y_range); - } - /* - opt->constraint.range = (scanner->model == MODEL_3091) ? &rangeY3091 : - (scanner->model == MODEL_3096) ? &rangeY3096 : NULL; + /* The command specifies the number of bytes in the data phase + * the data phase has a header, followed by 1 or 2 window desc blocks + * the header specifies the number of bytes in 1 window desc block */ - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - /* bottom-right x */ - opt = scanner->opt + OPT_BR_X; - opt->name = SANE_NAME_SCAN_BR_X; - opt->title = SANE_TITLE_SCAN_BR_X; - opt->desc = SANE_DESC_SCAN_BR_X; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - switch (scanner->model) - { - case MODEL_3091: + /* set window desc size in header */ + set_WPDB_wdblen (window_descriptor_headerB.cmd, window_descriptor_blockB.size); - opt->constraint.range = &rangeX3091; - break; - case MODEL_3092: + /* copy header into local buffer */ + memcpy (buffer, window_descriptor_headerB.cmd, window_descriptor_headerB.size); + bufferLen = window_descriptor_headerB.size; + + /* init the window block */ + set_WD_Xres (window_descriptor_blockB.cmd, s->resolution_x); + set_WD_Yres (window_descriptor_blockB.cmd, s->resolution_y); + + set_WD_ULX (window_descriptor_blockB.cmd, s->tl_x); + set_WD_ULY (window_descriptor_blockB.cmd, s->tl_y); + set_WD_width (window_descriptor_blockB.cmd, s->br_x - s->tl_x); + + /* stupid trick. 3091/2 require reading extra lines, + * because they have a gap between R G and B */ + if(s->mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){ + length += (s->color_raster_offset+s->green_offset) * 1200/300 * 2; + DBG(5,"set_window: Increasing length to %d\n",length); + } + set_WD_length (window_descriptor_blockB.cmd, length); - opt->constraint.range = &rangeX3092; - break; - case MODEL_3096: + set_WD_brightness (window_descriptor_blockB.cmd, s->brightness); + set_WD_threshold (window_descriptor_blockB.cmd, s->threshold); + set_WD_contrast (window_descriptor_blockB.cmd, s->contrast); + set_WD_composition (window_descriptor_blockB.cmd, s->mode); + + /* FIXME: is this something else on new scanners? */ + set_WD_lamp_color (window_descriptor_blockB.cmd, 0); + + /* older scanners use set window to indicate dropout color */ + if (s->has_SW_dropout && s->mode != MODE_COLOR){ + switch (s->dropout_color) { + case COLOR_RED: + set_WD_lamp_color (window_descriptor_blockB.cmd, WD_LAMP_RED); + break; + case COLOR_GREEN: + set_WD_lamp_color (window_descriptor_blockB.cmd, WD_LAMP_GREEN); + break; + case COLOR_BLUE: + set_WD_lamp_color (window_descriptor_blockB.cmd, WD_LAMP_BLUE); + break; + default: + set_WD_lamp_color (window_descriptor_blockB.cmd, WD_LAMP_DEFAULT); + break; + } + } - opt->constraint.range = &rangeX3096; - break; - default: + set_WD_bitsperpixel (window_descriptor_blockB.cmd, s->params.depth); - opt->constraint.range = &(scanner->x_range); - } + set_WD_rif (window_descriptor_blockB.cmd, s->rif); - /* - opt->constraint.range = (scanner->model == MODEL_3091) ? &rangeX3091 : - (scanner->model == MODEL_3096) ? &rangeX3096 : NULL; - */ + set_WD_vendor_id_code (window_descriptor_blockB.cmd, s->window_vid); + set_WD_gamma (window_descriptor_blockB.cmd, s->gamma); - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if(s->source == SOURCE_FLATBED){ + set_WD_paper_selection (window_descriptor_blockB.cmd, WD_paper_SEL_UNDEFINED); + } + else{ + set_WD_paper_selection (window_descriptor_blockB.cmd, WD_paper_SEL_NON_STANDARD); + set_WD_paper_width_X (window_descriptor_blockB.cmd, s->page_width); + set_WD_paper_length_Y (window_descriptor_blockB.cmd, s->page_height); + } - /* bottom-right y */ - opt = scanner->opt + OPT_BR_Y; - opt->name = SANE_NAME_SCAN_BR_Y; - opt->title = SANE_TITLE_SCAN_BR_Y; - opt->desc = SANE_DESC_SCAN_BR_Y; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - switch (scanner->model) - { - case MODEL_3091: + if (s->source == SOURCE_ADF_BACK) { + set_WD_wid (window_descriptor_blockB.cmd, WD_wid_back); + } + else{ + set_WD_wid (window_descriptor_blockB.cmd, WD_wid_front); + } - opt->constraint.range = &rangeY3091; - break; - case MODEL_3092: + /* copy first desc block into local buffer */ + memcpy (buffer + bufferLen, window_descriptor_blockB.cmd, window_descriptor_blockB.size); + bufferLen += window_descriptor_blockB.size; - opt->constraint.range = &rangeY3092; - break; - case MODEL_3096: + /* when in duplex mode, add a second window */ + if (s->source == SOURCE_ADF_DUPLEX) { - opt->constraint.range = &rangeY3096; - break; - default: + set_WD_wid (window_descriptor_blockB.cmd, WD_wid_back); - opt->constraint.range = &(scanner->y_range); - } - /* - opt->constraint.range = (scanner->model == MODEL_3091) ? &rangeY3091 : - (scanner->model == MODEL_3096) ? &rangeY3096 : NULL; - */ - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + /* FIXME: do we really need these on back of page? */ + set_WD_paper_selection (window_descriptor_blockB.cmd, WD_paper_SEL_UNDEFINED); + set_WD_paper_width_X (window_descriptor_blockB.cmd, 0); + set_WD_paper_length_Y (window_descriptor_blockB.cmd, 0); + + /* copy second desc block into local buffer */ + memcpy (buffer + bufferLen, window_descriptor_blockB.cmd, window_descriptor_blockB.size); + bufferLen += window_descriptor_blockB.size; + } - /* page width */ - opt = scanner->opt + OPT_PAGE_WIDTH; - opt->name = "pagewidth"; - opt->title = "Width of the paper in the scanner"; - opt->desc = - "Usually it's ok to just leave it at the maximum value all the time, but the ADF behavior may change if this value is modified."; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &scanner->adf_width_range; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + /* cmd has data phase byte count */ + set_SW_xferlen(set_windowB.cmd,bufferLen); - /* page height */ - opt = scanner->opt + OPT_PAGE_HEIGHT; - opt->name = "pageheight"; - opt->title = "Height of the paper in the scanner"; - opt->desc = - "Usually it's ok to just leave it at the maximum value all the time, but the ADF behavior may change if this value is modified."; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &scanner->adf_height_range; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + ret = do_cmd ( + s, 1, 0, + set_windowB.cmd, set_windowB.size, + buffer, bufferLen, + NULL, 0 + ); - /* "Enhancement" group ------------------------------------------------- */ + DBG (10, "set_window: finish\n"); - opt = scanner->opt + OPT_ENHANCEMENT_GROUP; - opt->title = "Enhancement"; - opt->desc = ""; - opt->type = SANE_TYPE_GROUP; - opt->constraint_type = SANE_CONSTRAINT_NONE; - - opt = scanner->opt + OPT_BRIGHTNESS; - opt->name = SANE_NAME_BRIGHTNESS; - opt->title = SANE_TITLE_BRIGHTNESS; - opt->desc = SANE_DESC_BRIGHTNESS; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &scanner->brightness_range; - if (scanner->has_brightness) - { - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - else - { - opt->cap = SANE_CAP_INACTIVE; - } + return ret; +} - opt = scanner->opt + OPT_AVERAGING; - opt->name = "averaging"; - opt->title = "Averaging"; - opt->desc = "Averaging"; - opt->type = SANE_TYPE_BOOL; - opt->unit = SANE_UNIT_NONE; - - opt = scanner->opt + OPT_CONTRAST; - opt->name = SANE_NAME_CONTRAST; - opt->title = SANE_TITLE_CONTRAST; - opt->desc = SANE_DESC_CONTRAST; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &scanner->contrast_range; - if (scanner->has_contrast) - { - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - else - { - opt->cap = SANE_CAP_INACTIVE; - } +/* + * Issues the SCSI OBJECT POSITION command if an ADF is in use. + */ +static int +object_position (struct fujitsu *s, int i_load) +{ + int ret; - opt = scanner->opt + OPT_THRESHOLD; - opt->name = SANE_NAME_THRESHOLD; - opt->title = SANE_TITLE_THRESHOLD; - opt->desc = SANE_DESC_THRESHOLD; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &scanner->threshold_range; - if (scanner->has_threshold) - { - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - else - { - opt->cap = SANE_CAP_INACTIVE; - } + DBG (10, "object_position: start\n"); + DBG (15, "object_position: %s\n", (i_load==SANE_TRUE)?"load":"discharge"); - opt = scanner->opt + OPT_MIRROR_IMAGE; - opt->name = "mirroring"; - opt->title = "mirror image"; - opt->desc = "mirror image"; - opt->type = SANE_TYPE_BOOL; - opt->unit = SANE_UNIT_NONE; - if (scanner->has_mirroring) - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - else - opt->cap = SANE_CAP_INACTIVE; + if (s->source == SOURCE_FLATBED) { + return SANE_STATUS_GOOD; + } - scanner->opt[OPT_RIF].name = "rif"; - scanner->opt[OPT_RIF].title = "rif"; - scanner->opt[OPT_RIF].desc = "reverse image format"; - scanner->opt[OPT_RIF].type = SANE_TYPE_BOOL; - scanner->opt[OPT_RIF].unit = SANE_UNIT_NONE; - if (scanner->has_reverse) - scanner->opt[OPT_RIF].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - else - opt->cap = SANE_CAP_INACTIVE; - - scanner->opt[OPT_AUTOSEP].name = "autoseparation"; - scanner->opt[OPT_AUTOSEP].title = "automatic separation"; - scanner->opt[OPT_AUTOSEP].desc = "automatic separation"; - scanner->opt[OPT_AUTOSEP].type = SANE_TYPE_BOOL; - scanner->opt[OPT_AUTOSEP].unit = SANE_UNIT_NONE; - if (scanner->has_autosep) - scanner->opt[OPT_AUTOSEP].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_OUTLINE_EXTRACTION].name = "outline"; - scanner->opt[OPT_OUTLINE_EXTRACTION].title = "outline extraction"; - scanner->opt[OPT_OUTLINE_EXTRACTION].desc = "enable outline extraction"; - scanner->opt[OPT_OUTLINE_EXTRACTION].type = SANE_TYPE_BOOL; - scanner->opt[OPT_OUTLINE_EXTRACTION].unit = SANE_UNIT_NONE; - if (scanner->has_outline) - scanner->opt[OPT_OUTLINE_EXTRACTION].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_COMPRESSION].name = "compression"; - scanner->opt[OPT_COMPRESSION].title = "compress image"; - scanner->opt[OPT_COMPRESSION].desc = "use hardware compression of scanner"; - scanner->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING; - scanner->opt[OPT_COMPRESSION].size = - maxStringSize (scanner->compression_mode_list); - scanner->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_COMPRESSION].constraint.string_list = - scanner->compression_mode_list; - DBG(1, "init_options: set compression %d\n", scanner->cmp_present); - if (scanner->cmp_present) - { - DBG(1, "ok compression %d\n", scanner->cmp_present); - scanner->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - } + if (i_load) { + set_OP_autofeed (object_positionB.cmd, OP_Feed); + } + else { + set_OP_autofeed (object_positionB.cmd, OP_Discharge); + } - scanner->opt[OPT_COMPRESSION_ARG].name = "compressionarg"; - scanner->opt[OPT_COMPRESSION_ARG].title = "compression argument"; - scanner->opt[OPT_COMPRESSION_ARG].desc = "compression quality (JPG)"; - scanner->opt[OPT_COMPRESSION_ARG].type = SANE_TYPE_INT; - scanner->opt[OPT_COMPRESSION_ARG].unit = SANE_UNIT_NONE; - scanner->opt[OPT_COMPRESSION_ARG].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_COMPRESSION_ARG].constraint.range = &jpg_quality_range; - if (scanner->cmp_present) - scanner->opt[OPT_COMPRESSION_ARG].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - + ret = do_cmd ( + s, 1, 0, + object_positionB.cmd, object_positionB.size, + NULL, 0, + NULL, 0 + ); + if (ret != SANE_STATUS_GOOD) + return ret; - scanner->opt[OPT_GAMMA].name = "gamma"; - scanner->opt[OPT_GAMMA].title = "gamma"; - scanner->opt[OPT_GAMMA].desc = - "specifies the gamma pattern number for the line art or the halftone"; - scanner->opt[OPT_GAMMA].type = SANE_TYPE_STRING; - scanner->opt[OPT_GAMMA].size = maxStringSize (gamma_mode_list); - scanner->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_GAMMA].constraint.string_list = gamma_mode_list; - if (scanner -> has_gamma) - scanner->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - else - scanner->opt[OPT_GAMMA].cap = SANE_CAP_INACTIVE; - - scanner->opt[OPT_EMPHASIS].name = "emphasis"; - scanner->opt[OPT_EMPHASIS].title = "emphasis"; - scanner->opt[OPT_EMPHASIS].desc = "image emphasis"; - scanner->opt[OPT_EMPHASIS].type = SANE_TYPE_STRING; - scanner->opt[OPT_EMPHASIS].size = maxStringSize (emphasis_mode_list); - scanner->opt[OPT_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_EMPHASIS].constraint.string_list = emphasis_mode_list; - if (scanner->has_emphasis) - scanner->opt[OPT_EMPHASIS].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - - scanner->opt[OPT_VARIANCE_RATE].name = "variancerate"; - scanner->opt[OPT_VARIANCE_RATE].title = "variance rate"; - scanner->opt[OPT_VARIANCE_RATE].desc = - "variance rate for simplified dynamic threshold"; - scanner->opt[OPT_VARIANCE_RATE].type = SANE_TYPE_INT; - scanner->opt[OPT_VARIANCE_RATE].unit = SANE_UNIT_NONE; - scanner->opt[OPT_VARIANCE_RATE].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_VARIANCE_RATE].constraint.range = &variance_rate_range; - scanner->opt[OPT_VARIANCE_RATE].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_THRESHOLD_CURVE].name = "thresholdcurve"; - scanner->opt[OPT_THRESHOLD_CURVE].title = "threshold curve"; - scanner->opt[OPT_THRESHOLD_CURVE].desc = "DTC threshold_curve"; - scanner->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT; - scanner->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE; - scanner->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range; - if (scanner->ipc_present) - scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_GRADATION].name = "gradation"; - scanner->opt[OPT_GRADATION].title = "gradation"; - scanner->opt[OPT_GRADATION].desc = "gradation"; - scanner->opt[OPT_GRADATION].type = SANE_TYPE_STRING; - scanner->opt[OPT_GRADATION].size = maxStringSize (gradation_mode_list); - scanner->opt[OPT_GRADATION].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_GRADATION].constraint.string_list = gradation_mode_list; - scanner->opt[OPT_GRADATION].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_SMOOTHING_MODE].name = "smoothingmode"; - scanner->opt[OPT_SMOOTHING_MODE].title = "smoothing mode"; - scanner->opt[OPT_SMOOTHING_MODE].desc = "smoothing mode"; - scanner->opt[OPT_SMOOTHING_MODE].type = SANE_TYPE_STRING; - scanner->opt[OPT_SMOOTHING_MODE].size = maxStringSize (smoothing_mode_list); - scanner->opt[OPT_SMOOTHING_MODE].constraint_type = - SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_SMOOTHING_MODE].constraint.string_list = - smoothing_mode_list; - if (scanner->ipc_present) - scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_FILTERING].name = "filtering"; - scanner->opt[OPT_FILTERING].title = "filtering"; - scanner->opt[OPT_FILTERING].desc = "filtering"; - scanner->opt[OPT_FILTERING].type = SANE_TYPE_STRING; - scanner->opt[OPT_FILTERING].size = maxStringSize (filtering_mode_list); - scanner->opt[OPT_FILTERING].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_FILTERING].constraint.string_list = filtering_mode_list; - if (scanner->ipc_present) - scanner->opt[OPT_FILTERING].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_BACKGROUND].name = "background"; - scanner->opt[OPT_BACKGROUND].title = "background"; - scanner->opt[OPT_BACKGROUND].desc = - "output if gradation of the video data is equal to or larger than threshold"; - scanner->opt[OPT_BACKGROUND].type = SANE_TYPE_STRING; - scanner->opt[OPT_BACKGROUND].size = maxStringSize (background_mode_list); - scanner->opt[OPT_BACKGROUND].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_BACKGROUND].constraint.string_list = background_mode_list; - if (scanner->ipc_present) - scanner->opt[OPT_BACKGROUND].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - - - scanner->opt[OPT_NOISE_REMOVAL].name = "noiseremoval"; - scanner->opt[OPT_NOISE_REMOVAL].title = "noise removal"; - scanner->opt[OPT_NOISE_REMOVAL].desc = "enables the noise removal matrixes"; - scanner->opt[OPT_NOISE_REMOVAL].type = SANE_TYPE_BOOL; - scanner->opt[OPT_NOISE_REMOVAL].unit = SANE_UNIT_NONE; - if (scanner->ipc_present) - scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_MATRIX2X2].name = "matrix2x2"; - scanner->opt[OPT_MATRIX2X2].title = "2x2 noise removal matrix"; - scanner->opt[OPT_MATRIX2X2].desc = "2x2 noise removal matrix"; - scanner->opt[OPT_MATRIX2X2].type = SANE_TYPE_BOOL; - scanner->opt[OPT_MATRIX2X2].unit = SANE_UNIT_NONE; - if (scanner->ipc_present) - scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_MATRIX3X3].name = "matrix3x3"; - scanner->opt[OPT_MATRIX3X3].title = "3x3 noise removal matrix"; - scanner->opt[OPT_MATRIX3X3].desc = "3x3 noise removal matrix"; - scanner->opt[OPT_MATRIX3X3].type = SANE_TYPE_BOOL; - scanner->opt[OPT_MATRIX3X3].unit = SANE_UNIT_NONE; - if (scanner->ipc_present) - scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_MATRIX4X4].name = "matrix4x4"; - scanner->opt[OPT_MATRIX4X4].title = "4x4 noise removal matrix"; - scanner->opt[OPT_MATRIX4X4].desc = "4x4 noise removal matrix"; - scanner->opt[OPT_MATRIX4X4].type = SANE_TYPE_BOOL; - scanner->opt[OPT_MATRIX4X4].unit = SANE_UNIT_NONE; - if (scanner->ipc_present) - scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_MATRIX5X5].name = "matrix5x5"; - scanner->opt[OPT_MATRIX5X5].title = "5x5 noise removal matrix"; - scanner->opt[OPT_MATRIX5X5].desc = "5x5 noise removal matrix"; - scanner->opt[OPT_MATRIX5X5].type = SANE_TYPE_BOOL; - scanner->opt[OPT_MATRIX5X5].unit = SANE_UNIT_NONE; - if (scanner->ipc_present) - scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].name = "whitelevelfollow"; - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].title = "white level follow"; - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].desc = "white level follow"; - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].type = SANE_TYPE_STRING; - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].size = - maxStringSize (white_level_follow_mode_list); - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].constraint_type = - SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].constraint.string_list = - white_level_follow_mode_list; - if (scanner->has_white_level_follow) - scanner->opt[OPT_WHITE_LEVEL_FOLLOW].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_DTC_SELECTION].name = "dtcselection"; - scanner->opt[OPT_DTC_SELECTION].title = "DTC selection"; - scanner->opt[OPT_DTC_SELECTION].desc = "DTC selection"; - scanner->opt[OPT_DTC_SELECTION].type = SANE_TYPE_STRING; - scanner->opt[OPT_DTC_SELECTION].size = - maxStringSize (dtc_selection_mode_list); - scanner->opt[OPT_DTC_SELECTION].constraint_type = - SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_DTC_SELECTION].constraint.string_list = - dtc_selection_mode_list; - if (scanner->ipc_present) - scanner->opt[OPT_DTC_SELECTION].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - - scanner->opt[OPT_PAPER_SIZE].name = "papersize"; - scanner->opt[OPT_PAPER_SIZE].title = "paper size"; - scanner->opt[OPT_PAPER_SIZE].desc = "Physical size of the paper in the ADF"; - scanner->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING; - scanner->opt[OPT_PAPER_SIZE].size = maxStringSize (paper_size_mode_list); - scanner->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_PAPER_SIZE].constraint.string_list = paper_size_mode_list; - if (scanner->has_fixed_paper_size) - { - scanner->opt[OPT_PAPER_SIZE].cap = - (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT); - } - else - { - scanner->opt[OPT_PAPER_SIZE].cap = SANE_CAP_INACTIVE; - } + wait_scanner (s); - scanner->opt[OPT_PAPER_ORIENTATION].name = "orientation"; - scanner->opt[OPT_PAPER_ORIENTATION].title = "orientation"; - scanner->opt[OPT_PAPER_ORIENTATION].desc = - "Orientation of the paper in the ADF"; - scanner->opt[OPT_PAPER_ORIENTATION].type = SANE_TYPE_STRING; - scanner->opt[OPT_PAPER_ORIENTATION].size = - maxStringSize (paper_orientation_mode_list); - scanner->opt[OPT_PAPER_ORIENTATION].constraint_type = - SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_PAPER_ORIENTATION].constraint.string_list = - paper_orientation_mode_list; - if (scanner->has_fixed_paper_size) - { - scanner->opt[OPT_PAPER_ORIENTATION].cap = - (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT); - } - else - { - scanner->opt[OPT_PAPER_ORIENTATION].cap = SANE_CAP_INACTIVE; - } + DBG (10, "object_position: finish\n"); + return ret; +} - scanner->opt[OPT_DROPOUT_COLOR].name = "dropoutcolor"; - scanner->opt[OPT_DROPOUT_COLOR].title = "dropout color"; - scanner->opt[OPT_DROPOUT_COLOR].desc = - "select dropout color"; - scanner->opt[OPT_DROPOUT_COLOR].type = SANE_TYPE_STRING; - scanner->opt[OPT_DROPOUT_COLOR].size = maxStringSize (dropout_color_list); - scanner->opt[OPT_DROPOUT_COLOR].constraint_type = - SANE_CONSTRAINT_STRING_LIST; - scanner->opt[OPT_DROPOUT_COLOR].constraint.string_list = dropout_color_list; - if (scanner->has_dropout_color) - { - scanner->opt[OPT_DROPOUT_COLOR].cap = SANE_CAP_SOFT_SELECT | - SANE_CAP_SOFT_DETECT; - } - else - { - scanner->opt[OPT_DROPOUT_COLOR].cap = SANE_CAP_INACTIVE; - } +/* + * Issues SCAN command. + * + * (This doesn't actually read anything, it just tells the scanner + * to start scanning.) + */ +static int +start_scan (struct fujitsu *s) +{ + int ret; + unsigned char outBuff[2]; + int outLen=1; - /* ------------------------------ */ + DBG (10, "start_scan: start\n"); - if (scanner->has_hw_status) - { - scanner->opt[OPT_START_BUTTON].name = "startbutton"; - scanner->opt[OPT_START_BUTTON].title = "start button"; - scanner->opt[OPT_START_BUTTON].desc = - "is the start button of the scanner pressed"; - scanner->opt[OPT_START_BUTTON].type = SANE_TYPE_BOOL; - scanner->opt[OPT_START_BUTTON].unit = SANE_UNIT_NONE; - scanner->opt[OPT_START_BUTTON].cap = SANE_CAP_SOFT_DETECT; - } + outBuff[0] = WD_wid_front; + if(s->source == SOURCE_ADF_BACK) { + outBuff[0] = WD_wid_back; + } + else if (s->source == SOURCE_ADF_DUPLEX) { + outBuff[1] = WD_wid_back; + outLen++; + } - if (scanner->has_imprinter) - { + set_SC_xfer_length (scanB.cmd, outLen); - opt = scanner->opt + OPT_IMPRINTER_GROUP; - opt->title = "imprinter control"; - opt->desc = ""; - opt->type = SANE_TYPE_GROUP; - opt->constraint_type = SANE_CONSTRAINT_NONE; - - opt = scanner->opt + OPT_IMPRINTER; - opt->name = "imprinter"; - opt->title = "enable imprinter"; - opt->desc = "enables the imprinter"; - opt->type = SANE_TYPE_BOOL; - opt->unit = SANE_UNIT_NONE; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + ret = do_cmd ( + s, 1, 0, + scanB.cmd, scanB.size, + outBuff, outLen, + NULL, 0 + ); - opt = scanner->opt + OPT_IMPRINTER_DIR; - opt->name = "imprinterdirection"; - opt->title = "imprinter direction"; - opt->desc = "writing direction of the imprinter"; - opt->type = SANE_TYPE_STRING; - opt->size = maxStringSize(imprinter_dir_list); - opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->constraint.string_list = imprinter_dir_list; - opt->cap = SANE_CAP_INACTIVE; + DBG (10, "start_scan: finish\n"); - opt = scanner->opt + OPT_IMPRINTER_CTR_DIR; - opt->name = "imprinterctrdir"; - opt->title = "imprinter counter direction"; - opt->desc = "imprinter counter direction"; - opt->type = SANE_TYPE_STRING; - opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->size = maxStringSize(imprinter_ctr_dir_list); - opt->constraint.string_list = imprinter_ctr_dir_list; - opt->cap = SANE_CAP_INACTIVE; + return ret; +} - opt = scanner->opt + OPT_IMPRINTER_YOFFSET; - opt->name = "imprinteryoffset"; - opt->title = "imprinter Y-Offset"; - opt->desc = "imprinter Y-Offset"; - opt->type = SANE_TYPE_FIXED; - opt->unit = SANE_UNIT_MM; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &range_imprinter_y_offset; - opt->cap = SANE_CAP_INACTIVE; +/* + * Called by SANE to read data. + * + * From the SANE spec: + * This function is used to read image data from the device + * represented by handle h. Argument buf is a pointer to a memory + * area that is at least maxlen bytes long. The number of bytes + * returned is stored in *len. A backend must set this to zero when + * the call fails (i.e., when a status other than SANE_STATUS_GOOD is + * returned). + * + * When the call succeeds, the number of bytes returned can be + * anywhere in the range from 0 to maxlen bytes. + */ +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) +{ + struct fujitsu *s = (struct fujitsu *) handle; + int side; + SANE_Status ret=0; - opt = scanner->opt + OPT_IMPRINTER_STRING; - opt->name = "imprinterstring"; - opt->title = "imprinter string"; - opt->desc = "The string the imprinter prints"; - opt->type = SANE_TYPE_STRING; - opt->size = max_imprinter_string_length; - opt->cap = SANE_CAP_INACTIVE; + DBG (10, "sane_read: start\n"); - opt = scanner->opt + OPT_IMPRINTER_CTR_INIT; - opt->name = "imprinterctrinit"; - opt->title = "imprinter counter init value"; - opt->desc = "imprinter counter initial value"; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->cap = SANE_CAP_INACTIVE; + *len=0; - opt = scanner->opt + OPT_IMPRINTER_CTR_STEP; - opt->name = "imprinterctrstep"; - opt->title = "imprinter counter step"; - opt->desc = "imprinter counter step"; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &imprinter_ctr_step_range; - scanner->opt[OPT_IMPRINTER_CTR_STEP].cap = SANE_CAP_INACTIVE; + /* maybe cancelled? */ + if(!s->started){ + DBG (5, "sane_read: not started, call sane_start\n"); + return SANE_STATUS_CANCELLED; + } - } - else - { - scanner->opt[OPT_IMPRINTER].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_DIR].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_YOFFSET].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_STRING].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_CTR_INIT].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_CTR_STEP].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_IMPRINTER_CTR_DIR].cap = SANE_CAP_INACTIVE; - } + side = get_current_side(s); + /* sane_start required between sides */ + if(s->bytes_tx[side] == s->bytes_tot[side]){ + DBG (15, "sane_read: returning eof\n"); + return SANE_STATUS_EOF; + } - /* "Tuning" group ------------------------------------------------------ */ - - opt = scanner->opt + OPT_TUNING_GROUP; - opt->title = "Fine tuning and debugging"; - opt->desc = ""; - opt->type = SANE_TYPE_GROUP; - opt->constraint_type = SANE_CONSTRAINT_NONE; - - opt = scanner->opt + OPT_LAMP_COLOR; - opt->name = "lampcolor"; - opt->title = "lamp used for grayscale acquisition"; - opt->desc = - "One-pass color scanners have three lamp colors; only one of them is used in grayscale scanning. Scanning from nonstandard media (e.g. colored paper) can sometimes be improved by changing this setting."; - opt->type = SANE_TYPE_STRING; - opt->size = maxStringSize (lamp_colorList); - opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; - opt->constraint.string_list = lamp_colorList; - /* the opt->cap member is set to active in the setMode3091 subroutine - * if monochrome scanning is selected - */ - opt->cap = SANE_CAP_INACTIVE; + /* 3091/2 are on crack, get their own duplex reader function */ + if(s->source == SOURCE_ADF_DUPLEX && s->duplex_interlace == DUPLEX_INTERLACE_3091){ + if(s->bytes_tot[SIDE_FRONT] > s->bytes_rx[SIDE_FRONT] + || s->bytes_tot[SIDE_BACK] > s->bytes_rx[SIDE_BACK] ){ + + ret = read_from_3091duplex(s); + if(ret){ + DBG(5,"sane_read: 3091 returning %d\n",ret); + return ret; + } - opt = scanner->opt + OPT_BLUE_OFFSET; - opt->name = "blueoffset"; - opt->title = "blue scan line offset from default"; - opt->desc = - "Use small positive/negative values to correct for miscalibration."; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &rangeColorOffset; - opt->cap = ((scanner->model != MODEL_3091)&&(scanner->model != MODEL_3092)) ? SANE_CAP_INACTIVE : - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - - opt = scanner->opt + OPT_GREEN_OFFSET; - opt->name = "greenoffset"; - opt->title = "green scan line offset from default"; - opt->desc = - "Use small positive/negative values to correct for miscalibration."; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range = &rangeColorOffset; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - - opt = scanner->opt + OPT_USE_SWAPFILE; - opt->name = "swapfile"; - opt->title = "use swap file for 2nd page during duplex scans"; - opt->desc = - "When set, data for the rear page during a duplex scan is buffered in a temp file rather than in memory. Use this option if you are scanning duplex and are low on memory."; - opt->type = SANE_TYPE_BOOL; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_NONE; - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; - - opt = scanner->opt + OPT_SLEEP_MODE; - opt->name = "sleeptimer"; - opt->title = "sleep timer"; - opt->desc = "time in minutes until the internal power supply switches to sleep mode"; - opt->type = SANE_TYPE_INT; - opt->unit = SANE_UNIT_NONE; - opt->constraint_type = SANE_CONSTRAINT_RANGE; - opt->constraint.range=&range_sleep_mode; - if (scanner->model == MODEL_FI || scanner->model == MODEL_FI4x20) /*...and others */ - { - opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - else - { - opt->cap = SANE_CAP_INACTIVE; } + } - DBG (10, "init_options:ok\n"); - return SANE_STATUS_GOOD; -} /* init_options */ + else{ + /* buffer front side */ + if( side == SIDE_FRONT){ + if(s->bytes_tot[SIDE_FRONT] > s->bytes_rx[SIDE_FRONT] ){ + + ret = read_from_scanner(s, SIDE_FRONT); + if(ret){ + DBG(5,"sane_read: front returning %d\n",ret); + return ret; + } + } + } + + /* buffer back side */ + if( side == SIDE_BACK || s->source == SOURCE_ADF_DUPLEX ){ + if(s->bytes_tot[SIDE_BACK] > s->bytes_rx[SIDE_BACK] ){ + + ret = read_from_scanner(s, SIDE_BACK); + if(ret){ + DBG(5,"sane_read: back returning %d\n",ret); + return ret; + } -/* one line has the following format: - * rrr...rrrggg...gggbbb...bbbb - * ^ ^ ^ - * \-r \-g \-b - * The pointers r,g,b point to the start of the - * r/g/b part. */ -static void -convert_rrggbb_to_rgb(struct fujitsu *scanner, unsigned char * buffptr, unsigned int length) -{ + } + } + } - unsigned char * outptr = buffptr; - int i_num_lines, i, j, bytes_per_line, pix_per_line; + /* copy a block from buffer to frontend */ + ret = read_from_buffer(s,buf,max_len,len,side); - bytes_per_line = scanner->bytes_per_scan_line; - pix_per_line = bytes_per_line/3; - i_num_lines = (length - (length % bytes_per_line)) / bytes_per_line; - - /* only byteswap for full lines*/ - for (i=0; i < i_num_lines; i++){ - for (j=0; j < pix_per_line; j++){ - - /* r */ - memcpy (outptr, - &scanner->buffer[(i*bytes_per_line)+j], 1); - outptr++; - - /* g */ - memcpy (outptr, - &scanner->buffer[(i*bytes_per_line)+j+pix_per_line], 1); - outptr++; - - /* b */ - memcpy (outptr, - &scanner->buffer[(i*bytes_per_line)+j+2*pix_per_line], 1); - outptr++; - } - } - - /* a partial line? just copy. this should never happen. - * if it does, would be better to save this spare data, - * and prepend it to the next buffer --FIXME--*/ - for (i=i_num_lines*bytes_per_line; i < (int) length; i++){ - memcpy (outptr,&scanner->buffer[i], 1); - outptr++; - } - + DBG (10, "sane_read: finish\n"); + + return ret; } -/* scanner returns pixel data as bgrbgrbgr - * turn each pixel around to rgbrgb */ -static void -convert_bgr_to_rgb(struct fujitsu *scanner, unsigned char * buffptr, - unsigned int length) +static SANE_Status +read_from_3091duplex(struct fujitsu *s) { + SANE_Status ret=SANE_STATUS_GOOD; + int side = SIDE_FRONT; + int bytes = s->buffer_size; + int remain = (s->bytes_tot[SIDE_FRONT] - s->bytes_rx[SIDE_FRONT]) + (s->bytes_tot[SIDE_BACK] - s->bytes_rx[SIDE_BACK]); + int off = (s->duplex_raster_offset+s->duplex_offset) * s->resolution_y/300; + int len, i; + unsigned char * buf; + + DBG (10, "read_from_3091duplex: start\n"); + + /* figure out the max amount to transfer */ + if(bytes > remain){ + bytes = remain; + } - unsigned char * outptr = buffptr; - int i_num_pixels, i, j; + /* all requests must end on line boundary */ + bytes -= (bytes % s->params.bytes_per_line); - /* number of full (3byte) pixels */ - i_num_pixels = (length - (length % 3)) / 3; - - /* only byteswap for full pixels*/ - for (i=0; i < i_num_pixels; i++){ - memcpy (outptr,&scanner->buffer[i*3+2], 1); /*r*/ - outptr++; - memcpy (outptr,&scanner->buffer[i*3+1], 1); /*g*/ - outptr++; - memcpy (outptr,&scanner->buffer[i*3], 1); /*b*/ - outptr++; - } - - /* a partial pixel? just copy. this should never happen. - * if it does, would be better to save this spare data, - * and prepend it to the next buffer --FIXME--*/ - for (j=i_num_pixels*3; j < (int) length; j++){ - memcpy (outptr,&scanner->buffer[j], 1); - outptr++; - } - -} + /* this should never happen */ + if(bytes < 1){ + DBG(5, "read_from_3091duplex: ERROR: no bytes this pass\n"); + ret = SANE_STATUS_INVAL; + } -static unsigned int -reader_duplex_alternate (struct fujitsu *scanner, - FILE * fp_front, FILE * fp_back) -{ + DBG(15, "read_from_3091duplex: to:%d rx:%d li:%d re:%d bu:%d pa:%d of:%d\n", + s->bytes_tot[SIDE_FRONT] + s->bytes_tot[SIDE_BACK], + s->bytes_rx[SIDE_FRONT] + s->bytes_rx[SIDE_BACK], + s->lines_rx[SIDE_FRONT] + s->lines_rx[SIDE_BACK], + remain, s->buffer_size, bytes, off); - int status; - unsigned int i_data_read; + if(ret){ + return ret; + } - unsigned int data_to_read = 0; - unsigned int total_data_size = 0; - unsigned int i_left_front, i_left_back; + buf = malloc(bytes); + if(!buf){ + DBG(5, "read_from_3091duplex: not enough mem for buffer: %d\n",bytes); + return SANE_STATUS_NO_MEM; + } - unsigned int largeBufferSize = 0; - unsigned char *largeBuffer = NULL; + set_R_datatype_code (readB.cmd, R_datatype_imagedata); + set_R_window_id (readB.cmd, WD_wid_front); + set_R_xfer_length (readB.cmd, bytes); + + ret = do_cmd ( + s, 1, 0, + readB.cmd, readB.size, + NULL, 0, + buf, bytes + ); + + if (ret == SANE_STATUS_GOOD) { + DBG(15, "read_from_3091duplex: got GOOD\n"); + len = bytes; + } + /* FIXME get the real number of bytes */ + else if (ret == SANE_STATUS_EOF) { + DBG(5, "read_from_3091duplex: got EOF, changing to GOOD\n"); + len = bytes; + ret = SANE_STATUS_GOOD; + } + else if (ret == SANE_STATUS_DEVICE_BUSY) { + DBG(5, "read_from_3091duplex: got BUSY, changing to GOOD\n"); + len = 0; + ret = SANE_STATUS_GOOD; + } + else { + DBG(5, "read_from_3091duplex: error reading data block status = %d\n", ret); + len = 0; + } - unsigned int duplexBufferSize = 0; - unsigned char *duplexBuffer = NULL; - unsigned char *duplexPointer = NULL; + /* loop thru all lines in read buffer */ + for(i=0;iparams.bytes_per_line;i++){ - /*total data bytes we expect per side*/ - i_left_front = scanner->bytes_per_scan_line * scanner->scan_height_pixels; - i_left_back = i_left_front; + /* start is front */ + if(s->lines_rx[SIDE_FRONT] < off){ + side=SIDE_FRONT; + } - /* Only allocate memory if we're not using a temp file. */ - if (!scanner->use_temp_file) - { - /* - * allocate a buffer for the back side because sane_read will - * first read the front side and then the back side. If I write - * to the pipe directly sane_read will not read from that pipe - * and the write will block. - */ - duplexBufferSize = i_left_back; - duplexBuffer = malloc (duplexBufferSize); + /* end is back */ + else if(s->bytes_rx[SIDE_FRONT] == s->bytes_tot[SIDE_FRONT]){ + side=SIDE_BACK; + } + /* odd are back */ + else if( ((s->lines_rx[SIDE_FRONT] + s->lines_rx[SIDE_BACK] - off) % 2) ){ + side=SIDE_BACK; + } - if (duplexBuffer == NULL) - { - DBG (MSG_ERR, - "reader_process: out of memory for duplex buffer (try option --swapfile)\n"); - return (0); - } - duplexPointer = duplexBuffer; - } + /* even are front */ + else{ + side=SIDE_FRONT; + } + + if(s->mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){ + copy_3091 (s, buf + i*s->params.bytes_per_line, s->params.bytes_per_line, side); + } + else{ + copy_buffer (s, buf + i*s->params.bytes_per_line, s->params.bytes_per_line, side); + } + } - /* alloc mem to talk to the convert routines */ - /* make buffer aligned to size of a scanline */ - largeBufferSize = scanner->scsi_buf_size - (scanner->scsi_buf_size % scanner->bytes_per_scan_line); - largeBuffer = malloc (largeBufferSize); + free(buf); - if (largeBuffer == NULL) { + DBG (10, "read_from_3091duplex: finish\n"); - DBG (MSG_ERR, "reader_process: out of memory for scan buffer (try option --swapfile)\n"); - if (duplexBuffer) - { - free(duplexBuffer); - } + return ret; +} - return (0); +static SANE_Status +read_from_scanner(struct fujitsu *s, int side) +{ + SANE_Status ret=SANE_STATUS_GOOD; + int bytes = s->buffer_size; + int remain = s->bytes_tot[side] - s->bytes_rx[side]; + unsigned char * buf; + int len; + + DBG (10, "read_from_scanner: start\n"); + + /* figure out the max amount to transfer */ + if(bytes > remain){ + bytes = remain; } - /* read the front side */ - do { - data_to_read = (i_left_front < largeBufferSize) ? i_left_front : largeBufferSize; + /* all requests must end on line boundary */ + bytes -= (bytes % s->params.bytes_per_line); - DBG (MSG_IO_READ, - "reader_process: read %d bytes from front side\n", data_to_read); - status = read_large_data_block (scanner, scanner->buffer, data_to_read, - 0x0, &i_data_read); + /* this should never happen */ + if(bytes < 1){ + DBG(5, "read_from_scanner: ERROR: no bytes this pass\n"); + ret = SANE_STATUS_INVAL; + } - switch (status) - { - case SANE_STATUS_GOOD: - total_data_size += i_data_read; - i_left_front -= i_data_read; - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", i_data_read); - total_data_size += i_data_read; - i_left_front = 0; - break; - case SANE_STATUS_DEVICE_BUSY: - DBG (5, "device busy"); - break; - default: - DBG (MSG_ERR, "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - fclose (fp_back); + DBG(15, "read_from_scanner: si:%d to:%d rx:%d re:%d bu:%d pa:%d\n", + side, s->bytes_tot[side], s->bytes_rx[side], remain, s->buffer_size, bytes); - if (duplexBuffer) free(duplexBuffer); - free(largeBuffer); + if(ret){ + return ret; + } - return (0); - } + buf = malloc(bytes); + if(!buf){ + DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",bytes); + return SANE_STATUS_NO_MEM; + } - /* clear rgb buffer*/ - memset(largeBuffer,0,largeBufferSize); + set_R_datatype_code (readB.cmd, R_datatype_imagedata); - /* call model specific conversion routine */ - switch (scanner->read_mode) { - case READ_MODE_RRGGBB: - convert_rrggbb_to_rgb(scanner,largeBuffer,i_data_read); - break; - case READ_MODE_BGR: - convert_bgr_to_rgb(scanner,largeBuffer,i_data_read); - break; - case READ_MODE_PASS: - largeBuffer=scanner->buffer; - break; - /*case READ_MODE_3091RGB: - convert_3091rgb_to_rgb(scanner,largeBuffer,i_data_read); - break;*/ - default: - DBG (5, "reader_process: cant convert buffer, unsupported read_mode %d\n",scanner->read_mode); + if (side == SIDE_BACK) { + set_R_window_id (readB.cmd, WD_wid_back); + } + else{ + set_R_window_id (readB.cmd, WD_wid_front); + } - if (duplexBuffer) free(duplexBuffer); - free(largeBuffer); - return (0); - } + set_R_xfer_length (readB.cmd, bytes); - /* write data to file */ - fwrite (largeBuffer, 1, i_data_read, fp_front); + ret = do_cmd ( + s, 1, 0, + readB.cmd, readB.size, + NULL, 0, + buf, bytes + ); + + if (ret == SANE_STATUS_GOOD) { + DBG(15, "read_from_scanner: got GOOD\n"); + len = bytes; + } + /* FIXME get the real number of bytes */ + else if (ret == SANE_STATUS_EOF) { + DBG(5, "read_from_scanner: got EOF, changing to GOOD\n"); + len = bytes; + ret = SANE_STATUS_GOOD; + } + else if (ret == SANE_STATUS_DEVICE_BUSY) { + DBG(5, "read_from_scanner: got BUSY, changing to GOOD\n"); + len = 0; + ret = SANE_STATUS_GOOD; + } + else { + DBG(5, "read_from_scanner: error reading data block status = %d\n", ret); + len = 0; + } - DBG (MSG_IO_READ, "reader_process_front: buffer of %d bytes read; %d bytes to go\n", - i_data_read, i_left_front); + if(len){ + if(s->mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){ + copy_3091 (s, buf, len, side); + } + else{ + copy_buffer (s, buf, len, side); + } + } + free(buf); - /* - * read back side - */ - data_to_read = (i_left_back < largeBufferSize) ? i_left_back : largeBufferSize; + DBG (10, "read_from_scanner: finish\n"); - DBG (MSG_IO_READ, - "reader_process: read %d bytes from back side\n", data_to_read); - status = read_large_data_block (scanner, scanner->buffer, data_to_read, - 0x80, &i_data_read); + return ret; +} - switch (status) - { - case SANE_STATUS_GOOD: - total_data_size += i_data_read; - i_left_back -= i_data_read; - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", i_data_read); - total_data_size += i_data_read; - i_left_back = 0; - break; - case SANE_STATUS_DEVICE_BUSY: - DBG (5, "device busy"); - break; - default: - DBG (MSG_ERR, "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - fclose (fp_back); - - if (duplexBuffer) free(duplexBuffer); - free(largeBuffer); - return (0); - } +static SANE_Status +copy_3091(struct fujitsu *s, unsigned char * buf, int len, int side) +{ + SANE_Status ret=SANE_STATUS_GOOD; + int i, dest, boff, goff; - /* clear rgb buffer*/ - memset(largeBuffer,0,largeBufferSize); + DBG (10, "copy_3091: start\n"); - /* call model specific conversion routine */ - switch (scanner->read_mode) { - case READ_MODE_RRGGBB: - convert_rrggbb_to_rgb(scanner,largeBuffer,i_data_read); - break; - case READ_MODE_BGR: - convert_bgr_to_rgb(scanner,largeBuffer,i_data_read); - break; - case READ_MODE_PASS: - largeBuffer=scanner->buffer; - break; - /*case READ_MODE_3091RGB: - convert_3091rgb_to_rgb(scanner,largeBuffer,i_data_read); - break;*/ - default: - DBG (5, "reader_process: cant convert buffer, unsupported read_mode %d\n",scanner->read_mode); + /* Data is RR...GG...BB... on each line, + * green is back 8 lines from red at 300 dpi + * blue is back 4 lines from red at 300 dpi. + * Here, we just get things on correct line, + * interlacing to make RGBRGB comes later. + * We add the user-supplied offsets before we scale + * so that they are independent of scanning resolution. + */ + goff = (s->color_raster_offset+s->green_offset) * s->resolution_y/150; + boff = (s->color_raster_offset+s->blue_offset) * s->resolution_y/300; - if (duplexBuffer) free(duplexBuffer); - free(largeBuffer); + /* loop thru all lines in read buffer */ + for(i=0;iparams.bytes_per_line;i++){ - return (0); + /* red at start of line */ + dest = s->lines_rx[side] * s->params.bytes_per_line; + if(dest >= 0 && dest < s->bytes_tot[side]){ + memcpy(s->buffers[side] + dest, + buf + i*s->params.bytes_per_line, + s->params.pixels_per_line); } - if (scanner->use_temp_file) { - - /* write data to file */ - if((unsigned int) fwrite (largeBuffer, 1, i_data_read, fp_back) != 1){ - fclose (fp_back); - DBG (MSG_ERR, "reader_process: out of disk space while writing temp file\n"); - if (duplexBuffer) free(duplexBuffer); - free(largeBuffer); - return (0); - } - + /* green is in middle of line */ + dest = (s->lines_rx[side] - goff) * s->params.bytes_per_line + s->params.pixels_per_line; + if(dest >= 0 && dest < s->bytes_tot[side]){ + memcpy(s->buffers[side] + dest, + buf + i*s->params.bytes_per_line + s->params.pixels_per_line, + s->params.pixels_per_line); } - else { - /* write data to buffer */ - memcpy (duplexPointer, largeBuffer, i_data_read); - duplexPointer += data_to_read; + /* blue is at end of line */ + dest = (s->lines_rx[side] - boff) * s->params.bytes_per_line + s->params.pixels_per_line*2; + if(dest >= 0 && dest < s->bytes_tot[side]){ + memcpy(s->buffers[side] + dest, + buf + i*s->params.bytes_per_line + s->params.pixels_per_line*2, + s->params.pixels_per_line); } - DBG (MSG_IO_READ, - "reader_process_back: buffer of %d bytes read; %d bytes to go\n", - data_to_read, i_left_back); - - } while (i_left_front > 0 || i_left_back > 0); - - fflush (fp_front); - fclose (fp_front); + s->lines_rx[side]++; + } - if (scanner->use_temp_file) - { - fflush (fp_back); - } - else - { - fwrite (duplexBuffer, 1, duplexBufferSize, fp_back); - fflush(fp_back); - fclose(fp_back); - } + /* even if we have read data, we may not have any + * full lines loaded yet, so we may have to lie */ + i = (s->lines_rx[side]-goff) * s->params.bytes_per_line; + if(i < 0){ + i = 0; + } + s->bytes_rx[side] = i; - if (duplexBuffer) free(duplexBuffer); - free(largeBuffer); + DBG (10, "copy_3091: finish\n"); - return total_data_size; + return ret; } -static unsigned int -reader_duplex_sequential (struct fujitsu *scanner, - FILE * fp_front, FILE * fp_back) +static SANE_Status +copy_buffer(struct fujitsu *s, unsigned char * buf, int len, int side) { - unsigned int i_total_size; + SANE_Status ret=SANE_STATUS_GOOD; + + DBG (10, "copy_buffer: start\n"); - i_total_size = 0; + memcpy(s->buffers[side]+s->bytes_rx[side],buf,len); + s->lines_rx[side] += len/s->params.bytes_per_line; + s->bytes_rx[side] += len; - i_total_size += reader_simplex (scanner, fp_front, 0); - i_total_size += reader_simplex (scanner, fp_back, 0x80); + DBG (10, "copy_buffer: finish\n"); - return i_total_size; + return ret; } -static unsigned int -reader_simplex (struct fujitsu *scanner, FILE * fp_front, int i_window_id) +static SANE_Status +read_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side) { + SANE_Status ret=SANE_STATUS_GOOD; + int bytes = max_len, i=0; + int remain = s->bytes_rx[side] - s->bytes_tx[side]; - int status; - unsigned int i_data_read; + DBG (10, "read_from_buffer: start\n"); - unsigned int data_to_read = 0; - unsigned int total_data_size = 0; - unsigned int i_left_front; + /* figure out the max amount to transfer */ + if(bytes > remain){ + bytes = remain; + } - unsigned int largeBufferSize = 0; - unsigned char *largeBuffer; + *len = bytes; - /*total data bytes we expect per side*/ - i_left_front = scanner->bytes_per_scan_line * scanner->scan_height_pixels; + DBG(15, "read_from_buffer: si:%d to:%d tx:%d re:%d bu:%d pa:%d\n", side, s->bytes_tot[side], s->bytes_tx[side], remain, max_len, bytes); - /* alloc mem to talk to the convert routines */ - /* make buffer aligned to size of a scanline */ - largeBufferSize = scanner->scsi_buf_size - (scanner->scsi_buf_size % scanner->bytes_per_scan_line); - largeBuffer = malloc (largeBufferSize); - if (largeBuffer == NULL) { - DBG (MSG_ERR, "reader_process: out of memory for scan buffer (try option --swapfile)\n"); - return (0); + /*FIXME this needs to timeout eventually */ + if(!bytes){ + DBG(5,"read_from_buffer: nothing to do\n"); + return SANE_STATUS_GOOD; } - /* read the data */ - do { - data_to_read = (i_left_front < largeBufferSize) ? i_left_front : largeBufferSize; + /* scanners interlace colors in many different ways */ + /* use separate functions to convert to regular rgb */ + if(s->mode == MODE_COLOR){ + int byteOff; + + switch (s->color_interlace) { + + /* scanner returns pixel data as bgrbgr... */ + case COLOR_INTERLACE_BGR: + for (i=0; i < bytes; i++){ + buf[i]=s->buffers[side][s->bytes_tx[side]+i-(((s->bytes_tx[side]+i)%3)-1)*2]; + } + break; - DBG (MSG_IO_READ, - "reader_process: read %d bytes from front side\n", data_to_read); - status = read_large_data_block (scanner, scanner->buffer, data_to_read, - i_window_id, &i_data_read); + /* one line has the following format: + * rrr...rrrggg...gggbbb...bbb */ + case COLOR_INTERLACE_3091: + case COLOR_INTERLACE_RRGGBB: + for (i=0; i < bytes; i++){ + byteOff = (s->bytes_tx[side]+i)%s->params.bytes_per_line; + buf[i]=s->buffers[side][(s->bytes_tx[side]+i)-byteOff + (byteOff%3)*s->params.pixels_per_line + (byteOff/3)]; + } + break; - switch (status) - { - case SANE_STATUS_GOOD: - total_data_size += i_data_read; - i_left_front -= i_data_read; - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", i_data_read); - total_data_size += i_data_read; - i_left_front = 0; - break; - case SANE_STATUS_DEVICE_BUSY: - DBG (5, "device busy"); - break; default: - DBG (MSG_ERR, "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - /*FIXME should we free some buffers here?*/ - return (0); - } + memcpy(buf,s->buffers[side]+s->bytes_tx[side],bytes); + } + } + else{ + memcpy(buf,s->buffers[side]+s->bytes_tx[side],bytes); + } - /* clear rgb buffer*/ - memset(largeBuffer,0,largeBufferSize); + s->bytes_tx[side] += *len; - /* call model specific conversion routine */ - switch (scanner->read_mode) { - case READ_MODE_RRGGBB: - convert_rrggbb_to_rgb(scanner,largeBuffer,i_data_read); - break; - case READ_MODE_BGR: - convert_bgr_to_rgb(scanner,largeBuffer,i_data_read); - break; - case READ_MODE_PASS: - largeBuffer=scanner->buffer; - break; - /*case READ_MODE_3091RGB: - convert_3091rgb_to_rgb(scanner,largeBuffer,i_data_read); - break;*/ - default: - DBG (5, "reader_process: cant convert buffer, unsupported read_mode %d\n",scanner->read_mode); - return (0); + /* invert image if scanner needs it for this mode */ + if (s->reverse_by_mode[s->mode]){ + for ( i = 0; i < *len; i++ ) { + buf[i] ^= 0xff; } + } - /* write data to file */ - fwrite (largeBuffer, 1, i_data_read, fp_front); - - DBG (MSG_IO_READ, "reader_process_front: buffer of %d bytes read; %d bytes to go\n", - i_data_read, i_left_front); + DBG (10, "read_from_buffer: finish\n"); - } while (i_left_front > 0); + return ret; +} - fflush (fp_front); - fclose (fp_front); - return total_data_size; +/* + * @@ Section 4 - SANE cleanup functions + */ +/* + * Cancels a scan. + * + * It has been said on the mailing list that sane_cancel is a bit of a + * misnomer because it is routinely called to signal the end of a + * batch - quoting David Mosberger-Tang: + * + * > In other words, the idea is to have sane_start() be called, and + * > collect as many images as the frontend wants (which could in turn + * > consist of multiple frames each as indicated by frame-type) and + * > when the frontend is done, it should call sane_cancel(). + * > Sometimes it's better to think of sane_cancel() as "sane_stop()" + * > but that name would have had some misleading connotations as + * > well, that's why we stuck with "cancel". + * + * The current consensus regarding duplex and ADF scans seems to be + * the following call sequence: sane_start; sane_read (repeat until + * EOF); sane_start; sane_read... and then call sane_cancel if the + * batch is at an end. I.e. do not call sane_cancel during the run but + * as soon as you get a SANE_STATUS_NO_DOCS. + * + * From the SANE spec: + * This function is used to immediately or as quickly as possible + * cancel the currently pending operation of the device represented by + * handle h. This function can be called at any time (as long as + * handle h is a valid handle) but usually affects long-running + * operations only (such as image is acquisition). It is safe to call + * this function asynchronously (e.g., from within a signal handler). + * It is important to note that completion of this operaton does not + * imply that the currently pending operation has been cancelled. It + * only guarantees that cancellation has been initiated. Cancellation + * completes only when the cancelled call returns (typically with a + * status value of SANE_STATUS_CANCELLED). Since the SANE API does + * not require any other operations to be re-entrant, this implies + * that a frontend must not call any other operation until the + * cancelled operation has returned. + */ +void +sane_cancel (SANE_Handle handle) +{ + DBG (10, "sane_cancel: start\n"); + do_cancel ((struct fujitsu *) handle); + DBG (10, "sane_cancel: finish\n"); } -static unsigned int -reader_gray_duplex_alternate (struct fujitsu *scanner, - FILE * fp_front, FILE * fp_back) +/* + * Performs cleanup. + * FIXME: do better cleanup if scanning is ongoing... + */ +static SANE_Status +do_cancel (struct fujitsu *s) { + DBG (10, "do_cancel: start\n"); - int status; - unsigned int i_left_front, i_left_back; - unsigned int total_data_size, data_to_read; - unsigned int duplexBufferSize; - unsigned char *duplexBuffer = NULL; - unsigned char *duplexPointer; - unsigned int largeBufferSize = 0; - unsigned int i_data_read; + s->started = 0; + DBG (10, "do_cancel: finish\n"); - /* - time_t start_time, end_time; - (void) time (&start_time); - */ - - i_left_front = scanner->bytes_per_scan_line * scanner->scan_height_pixels; - duplexBufferSize = i_left_back = i_left_front; + return SANE_STATUS_CANCELLED; +} - /* Only allocate memory if we're not using a temp file. */ - if (scanner->use_temp_file) - { - duplexBuffer = duplexPointer = NULL; - duplexBufferSize = 0; - } - else - { - /* - * allocate a buffer for the back side because sane_read will - * first read the front side and than the back side. If I write - * to the pipe directly sane_read will not read from that pipe - * and the write will block. - */ - duplexBuffer = malloc (duplexBufferSize); +/* + * Ends use of the scanner. + * + * From the SANE spec: + * This function terminates the association between the device handle + * passed in argument h and the device it represents. If the device is + * presently active, a call to sane_cancel() is performed first. After + * this function returns, handle h must not be used anymore. + */ +void +sane_close (SANE_Handle handle) +{ + DBG (10, "sane_close: start\n"); - if (duplexBuffer == NULL) - { - DBG (MSG_ERR, - "reader_process: out of memory for duplex buffer (try option --swapfile)\n"); - return (0); - } - duplexPointer = duplexBuffer; - } + do_cancel((struct fujitsu *) handle); + disconnect_fd((struct fujitsu *) handle); - largeBufferSize = - scanner->scsi_buf_size - - (scanner->scsi_buf_size % scanner->bytes_per_scan_line); + DBG (10, "sane_close: finish\n"); +} - total_data_size = 0; +static SANE_Status +disconnect_fd (struct fujitsu *s) +{ + DBG (10, "disconnect_fd: start\n"); - do - { - data_to_read = (i_left_front < largeBufferSize) - ? i_left_front : largeBufferSize; + if(s->fd > -1){ + if (s->connection == CONNECTION_USB) { + DBG (15, "disconnecting usb device\n"); + sanei_usb_close (s->fd); + } + else if (s->connection == CONNECTION_SCSI) { + DBG (15, "disconnecting scsi device\n"); + sanei_scsi_close (s->fd); + } + s->fd = -1; + } - DBG (MSG_IO_READ, - "reader_process: read %d bytes from front side\n", data_to_read); - status = read_large_data_block (scanner, scanner->buffer, data_to_read, - 0x0, &i_data_read); + DBG (10, "disconnect_fd: finish\n"); - switch (status) - { - case SANE_STATUS_GOOD: - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", - scanner->i_transfer_length); - data_to_read -= scanner->i_transfer_length; - i_left_front = data_to_read; - break; - case SANE_STATUS_DEVICE_BUSY: - DBG (5, "device busy"); - data_to_read = 0; - break; - default: - DBG (MSG_ERR, - "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - fclose (fp_back); - if (duplexBuffer) free(duplexBuffer); - return (0); - } + return SANE_STATUS_GOOD; +} - total_data_size += data_to_read; - fwrite (scanner->buffer, 1, data_to_read, fp_front); - i_left_front -= data_to_read; - DBG (MSG_IO_READ, - "reader_process_front: buffer of %d bytes read; %d bytes to go\n", - data_to_read, i_left_front); +/* + * Terminates the backend. + * + * From the SANE spec: + * This function must be called to terminate use of a backend. The + * function will first close all device handles that still might be + * open (it is recommended to close device handles explicitly through + * a call to sane_close(), but backends are required to release all + * resources upon a call to this function). After this function + * returns, no function other than sane_init() may be called + * (regardless of the status value returned by sane_exit(). Neglecting + * to call this function may result in some resources not being + * released properly. + */ +void +sane_exit (void) +{ + struct fujitsu *dev, *next; + DBG (10, "sane_exit: start\n"); - /* - * read back side - */ - data_to_read = (i_left_back < largeBufferSize) - ? i_left_back : largeBufferSize; + for (dev = fujitsu_devList; dev; dev = next) { + disconnect_fd(dev); + next = dev->next; + free (dev->device_name); + free (dev); + } - DBG (MSG_IO_READ, - "reader_process: read %d bytes from back side\n", data_to_read); - status = read_large_data_block (scanner, scanner->buffer, data_to_read, - 0x80, &i_data_read); + if (sane_devArray) + free (sane_devArray); - switch (status) - { - case SANE_STATUS_GOOD: - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", - scanner->i_transfer_length); - data_to_read -= scanner->i_transfer_length; - i_left_back = data_to_read; - break; - case SANE_STATUS_DEVICE_BUSY: - DBG (5, "device busy"); - data_to_read = 0; - break; - default: - DBG (MSG_ERR, - "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - fclose (fp_back); - if (duplexBuffer) free(duplexBuffer); - return (0); - } + fujitsu_devList = NULL; + sane_devArray = NULL; - total_data_size += data_to_read; - if (scanner->use_temp_file) - { - if ((unsigned int) fwrite (scanner->buffer, 1, data_to_read, fp_back) - != data_to_read) - { - fclose (fp_back); - DBG (MSG_ERR, - "reader_process: out of disk space while writing temp file\n"); - if (duplexBuffer) free(duplexBuffer); - return (0); - } - } - else - { - memcpy (duplexPointer, scanner->buffer, data_to_read); - duplexPointer += data_to_read; - } + DBG (10, "sane_exit: finish\n"); +} - i_left_back -= data_to_read; - DBG (MSG_IO_READ, - "reader_process_back: buffer of %d bytes read; %d bytes to go\n", - data_to_read, i_left_back); - } - while (i_left_front > 0 || i_left_back > 0); - fflush (fp_front); - fclose (fp_front); +/* + * @@ Section 5 - misc helper functions + */ +/* + * Called by the SANE SCSI core and our usb code on device errors + * parses the request sense return data buffer, + * decides the best SANE_Status for the problem, produces debug msgs, + * and copies the sense buffer into the scanner struct + */ +static SANE_Status +sense_handler (int fd, unsigned char * sensed_data, void *arg) +{ + unsigned int sense = get_RS_sense_key (sensed_data); + unsigned int asc = get_RS_ASC (sensed_data); + unsigned int ascq = get_RS_ASCQ (sensed_data); + unsigned int eom = get_RS_EOM (sensed_data); + unsigned int ili = get_RS_ILI (sensed_data); + unsigned int info = get_RS_information (sensed_data); + struct fujitsu * s = arg; + + DBG (5, "sense_handler: start\n"); + + /* kill compiler warning */ + fd = fd; + + /* copy the rs return data into the scanner struct + so that the caller can use it if he wants */ + memcpy(&s->rs_buffer,sensed_data,RS_return_size); + + DBG (5, "Sense=%#02x, ASC=%#02x, ASCQ=%#02x, EOM=%d, ILI=%d, info=%#08x\n", sense, asc, ascq, eom, ili, info); + + switch (sense) { + case 0x0: + if (0x80 == asc) { + DBG (5, "No sense: hardware status bits?\n"); + return SANE_STATUS_GOOD; + } + if (0x00 != asc) { + DBG (5, "No sense: unknown asc\n"); + return SANE_STATUS_IO_ERROR; + } + if (0x00 != ascq) { + DBG (5, "No sense: unknown ascq\n"); + return SANE_STATUS_IO_ERROR; + } + if (get_RS_EOM (sensed_data)) { + /*s->i_transfer_length = get_RS_information (sensed_data);*/ + DBG (5, "No sense: EOM\n"); + return SANE_STATUS_EOF; + } + DBG (5, "No sense: ready\n"); + return SANE_STATUS_GOOD; - /* - (void) time (&end_time); - if (end_time == start_time) - end_time++; + case 0x2: + if (0x00 != asc) { + DBG (5, "Not ready: unknown asc\n"); + return SANE_STATUS_IO_ERROR; + } + if (0x00 != ascq) { + DBG (5, "Not ready: unknown ascq\n"); + return SANE_STATUS_IO_ERROR; + } + DBG (5, "Not ready: busy\n"); + return SANE_STATUS_DEVICE_BUSY; + break; - DBG (1, "time to read from scanner: %lu seconds\n", (end_time - start_time)); - */ + case 0x3: + if (0x80 != asc) { + DBG (5, "Medium error: unknown asc\n"); + return SANE_STATUS_IO_ERROR; + } + if (0x01 == ascq) { + DBG (5, "Medium error: paper jam\n"); + return SANE_STATUS_JAMMED; + } + if (0x02 == ascq) { + DBG (5, "Medium error: cover open\n"); + return SANE_STATUS_COVER_OPEN; + } + if (0x03 == ascq) { + DBG (5, "Medium error: hopper empty\n"); + return SANE_STATUS_NO_DOCS; + } + if (0x04 == ascq) { + DBG (5, "Medium error: unusual paper\n"); + return SANE_STATUS_JAMMED; + } + if (0x07 == ascq) { + DBG (5, "Medium error: double feed\n"); + return SANE_STATUS_JAMMED; + } + if (0x10 == ascq) { + DBG (5, "Medium error: no ink cartridge\n"); + return SANE_STATUS_IO_ERROR; + } + if (0x13 == ascq) { + DBG (5, "Medium error: temporary no data\n"); + return SANE_STATUS_DEVICE_BUSY; + } + if (0x14 == ascq) { + DBG (5, "Medium error: imprinter error\n"); + return SANE_STATUS_IO_ERROR; + } + DBG (5, "Medium error: unknown ascq\n"); + return SANE_STATUS_IO_ERROR; + break; - if (scanner->use_temp_file) - { - fflush (fp_back); - } - else - { - fwrite (duplexBuffer, 1, duplexBufferSize, fp_back); - fflush(fp_back); - fclose(fp_back); - } + case 0x4: + if (0x80 != asc && 0x44 != asc && 0x47 != asc) { + DBG (5, "Hardware error: unknown asc\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x44 == asc) && (0x00 == ascq)) { + DBG (5, "Hardware error: EEPROM error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x01 == ascq)) { + DBG (5, "Hardware error: FB motor fuse\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x02 == ascq)) { + DBG (5, "Hardware error: heater fuse\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x04 == ascq)) { + DBG (5, "Hardware error: ADF motor fuse\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x05 == ascq)) { + DBG (5, "Hardware error: mechanical error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x06 == ascq)) { + DBG (5, "Hardware error: optical error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x07 == ascq)) { + DBG (5, "Hardware error: Fan error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x08 == ascq)) { + DBG (5, "Hardware error: IPC option error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x10 == ascq)) { + DBG (5, "Hardware error: imprinter error\n"); + return SANE_STATUS_IO_ERROR; + } + DBG (5, "Hardware error: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + break; - if (duplexBuffer) free(duplexBuffer); + case 0x5: + if ((0x00 == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: paper edge detected too soon\n"); + return SANE_STATUS_INVAL; + } + if ((0x1a == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: Parameter list error\n"); + return SANE_STATUS_INVAL; + } + if ((0x20 == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: invalid command\n"); + return SANE_STATUS_INVAL; + } + if ((0x24 == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: invalid CDB field\n"); + return SANE_STATUS_INVAL; + } + if ((0x25 == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: unsupported logical unit\n"); + return SANE_STATUS_UNSUPPORTED; + } + if ((0x26 == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: invalid field in parm list\n"); + if (get_RS_additional_length(sensed_data) >= 0x0a) { + DBG (5, "Offending byte is %#02x\n", get_RS_offending_byte(sensed_data)); + + /* move this to set_window() ? */ + if (get_RS_offending_byte(sensed_data) >= 8) { + DBG (5, "Window desc block? byte %#02x\n",get_RS_offending_byte(sensed_data)-8); + } + } + return SANE_STATUS_INVAL; + } + if ((0x2C == asc) && (0x00 == ascq)) { + DBG (5, "Illegal request: command sequence error\n"); + return SANE_STATUS_INVAL; + } + if ((0x2C == asc) && (0x02 == ascq)) { + DBG (5, "Illegal request: wrong window combination \n"); + return SANE_STATUS_INVAL; + } + DBG (5, "Illegal request: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + break; - return total_data_size; -} + case 0x6: + if ((0x00 == asc) && (0x00 == ascq)) { + DBG (5, "Unit attention: device reset\n"); + return SANE_STATUS_GOOD; + } + if ((0x80 == asc) && (0x01 == ascq)) { + DBG (5, "Unit attention: power saving\n"); + return SANE_STATUS_GOOD; + } + DBG (5, "Unit attention: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + break; -static unsigned int -reader_gray_duplex_sequential (struct fujitsu *scanner, - FILE * fp_front, FILE * fp_back) -{ - unsigned int i_total_size; + case 0xb: + if ((0x43 == asc) && (0x00 == ascq)) { + DBG (5, "Aborted command: message error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x45 == asc) && (0x00 == ascq)) { + DBG (5, "Aborted command: select failure\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x47 == asc) && (0x00 == ascq)) { + DBG (5, "Aborted command: SCSI parity error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x48 == asc) && (0x00 == ascq)) { + DBG (5, "Aborted command: initiator error message\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x4e == asc) && (0x00 == ascq)) { + DBG (5, "Aborted command: overlapped commands\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x01 == ascq)) { + DBG (5, "Aborted command: image transfer error\n"); + return SANE_STATUS_IO_ERROR; + } + if ((0x80 == asc) && (0x03 == ascq)) { + DBG (5, "Aborted command: JPEG overflow error\n"); + return SANE_STATUS_NO_MEM; + } + DBG (5, "Aborted command: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + break; - i_total_size = 0; + default: + DBG (5, "Unknown Sense Code\n"); + return SANE_STATUS_IO_ERROR; + } - i_total_size += reader_generic_passthrough (scanner, fp_front, 0); - i_total_size += reader_generic_passthrough (scanner, fp_back, 0x80); + DBG (5, "sense_handler: should never happen!\n"); - return i_total_size; + return SANE_STATUS_IO_ERROR; } /* - * @@ Section 5 - M3091 specific internal routines + * take a bunch of pointers, send commands to scanner + * if the status byte/rs data says 'busy' then we try again + * we also dump lots of debug info to stderr */ - -static unsigned int -reader3091ColorSimplex (struct fujitsu *scanner, FILE * fp) -{ - int status; - unsigned int data_left; - unsigned int total_data_size, data_to_read, dataToProcess; - unsigned int green_offset, blue_offset, lookAheadSize; - unsigned readOffset; - unsigned char *linebuffer = 0; - unsigned char *largeBuffer; - unsigned int largeBufferSize = 0; - unsigned int colorLineGap = 0; - unsigned char *redSource, *greenSource, *blueSource, *target; - unsigned int i_data_read; - -#ifdef DEBUG - unsigned int lineCount = 0; - int redSum, greenSum, blueSum; - unsigned int redStart, greenStart, blueStart; -#endif - - linebuffer = malloc (scanner->bytes_per_scan_line); - data_left = scanner->bytes_per_scan_line * scanner->scan_height_pixels; - - colorLineGap = scanner->color_raster_offset * scanner->resolution_y / 300; - green_offset = - (2 * colorLineGap + scanner->green_offset) * scanner->bytes_per_scan_line; - blue_offset = - (colorLineGap + scanner->blue_offset) * scanner->bytes_per_scan_line; - - lookAheadSize = green_offset; - DBG (10, "colorLineGap=%u, green_offset=%u, blue_offset =%u\n", - colorLineGap, green_offset / scanner->bytes_per_scan_line, - blue_offset / scanner->bytes_per_scan_line); - - largeBuffer = scanner->buffer; - largeBufferSize = - scanner->scsi_buf_size - - scanner->scsi_buf_size % scanner->bytes_per_scan_line; - - if (largeBufferSize < 2 * lookAheadSize) - { - /* - * We're in a bit of trouble here. To decode colors correctly, we have to look - * ahead in the data stream, but obviously the pre-allocated buffer will not - * hold enough lines to do this in an effective fashion (or do it at all). - * Get a larger buffer. - */ - largeBuffer = malloc (largeBufferSize = 4 * lookAheadSize); +static SANE_Status +do_cmd(struct fujitsu *s, int runRS, int shortTime, + unsigned char * cmdBuff, size_t cmdLen, + unsigned char * outBuff, size_t outLen, + unsigned char * inBuff, size_t inLen +) +{ + if (s->connection == CONNECTION_SCSI) { + return do_scsi_cmd(s, runRS, shortTime, + cmdBuff, cmdLen, + outBuff, outLen, + inBuff, inLen); + } + if (s->connection == CONNECTION_USB) { + return do_usb_cmd(s, runRS, shortTime, + cmdBuff, cmdLen, + outBuff, outLen, + inBuff, inLen); } + return SANE_STATUS_INVAL; +} - DBG (MSG_IO_READ, - "reader_process: reading %u+%u bytes in large blocks of %u bytes\n", - data_left, lookAheadSize, largeBufferSize); - - readOffset = 0; - data_left += lookAheadSize; - total_data_size = data_left; - - do - { - data_to_read = (data_left < largeBufferSize - readOffset) - ? data_left : largeBufferSize - readOffset; +SANE_Status +do_scsi_cmd(struct fujitsu *s, int runRS, int shortTime, + unsigned char * cmdBuff, size_t cmdLen, + unsigned char * outBuff, size_t outLen, + unsigned char * inBuff, size_t inLen +) +{ + int ret; + size_t actLen = inLen; - dataToProcess = data_to_read + readOffset - lookAheadSize; - /* if (data_to_read == data_left) dataToProcess += lookAheadSize / 2; */ + /*shut up compiler*/ + runRS=runRS; + shortTime=shortTime; - status = - read_large_data_block (scanner, largeBuffer + readOffset, data_to_read, - 0, &i_data_read); + DBG(10, "do_scsi_cmd: start\n"); - switch (status) - { - case SANE_STATUS_GOOD: - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", - scanner->i_transfer_length); - data_to_read -= scanner->i_transfer_length; - data_left = data_to_read; - break; - default: - DBG (MSG_ERR, - "reader_process: unable to get image data from scanner!\n"); - fclose (fp); - return (0); - } + DBG(25, "cmd: writing %d bytes\n", (int)cmdLen); + hexdump(30, "cmd: >>", cmdBuff, cmdLen); - /* - * This scanner will send you one line of R values, one line of G - * values, and one line of B values in succession. - * The pitfall, however, is that those are *not* corresponding to - * one and the same "line" on the paper. - * Instead, the "green" values matching the first line of "red" - * values appear 8 lines further "down" in the data stream, and - * the "blue" values are 4 lines further "down". (That's for 300dpi; - * for 75dpi it's 2 and 1 line, for 150dpi it's 4 and 2 lines, - * for 600dpi 16 and 8 lines.) - * The scanner will send you a total of 8 lines (at 300dpi) more - * than you asked for so that you can build the complete image - * from that. - * - * This code basically works by retaining the last "lookAheadSize" - * bytes of the buffer just read and copying them onto the beginning - * of the next data block, in effect creating a "sliding window" - * over the data stream with an overlap of "lookAheadSize". - */ + if(outBuff && outLen){ + DBG(25, "out: writing %d bytes\n", (int)outLen); + hexdump(30, "out: >>", outBuff, outLen); + } + if (inBuff && inLen){ + DBG(25, "in: reading %d bytes\n", (int)inLen); + memset(inBuff,0,inLen); + } - redSource = largeBuffer; - greenSource = redSource + green_offset + scanner->scan_width_pixels; - blueSource = redSource + blue_offset + 2 * scanner->scan_width_pixels; + ret = sanei_scsi_cmd2 (s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, &actLen); + if(ret != SANE_STATUS_GOOD){ + DBG(5,"do_scsi_cmd: return '%s'\n",sane_strstatus(ret)); + return ret; + } - while (redSource < (largeBuffer + dataToProcess)) - { + if (inBuff && inLen){ + hexdump(30, "in: <<", inBuff, actLen); + DBG(25, "in: read %d bytes\n", (int)actLen); + if (inLen != actLen) { + DBG(5,"in: wrong size %d/%d\n", (int)inLen, (int)actLen); + return SANE_STATUS_IO_ERROR; + } + } -#ifdef DEBUG - redStart = - ((unsigned long int) redSource - - (unsigned long int) largeBuffer) / scanner->bytes_per_scan_line; - greenStart = - ((unsigned long int) greenSource - - (unsigned long int) largeBuffer) / scanner->bytes_per_scan_line; - blueStart = - ((unsigned long int) blueSource - - (unsigned long int) largeBuffer) / scanner->bytes_per_scan_line; - redSum = 0; - greenSum = 0; - blueSum = 0; -#endif + /* FIXME: what about 0 length reads? + if(ret == SANE_STATUS_EOF){ + ret = SANE_STATUS_DEVICE_BUSY; + }*/ - target = linebuffer; - while (target < linebuffer + scanner->bytes_per_scan_line) - { -#ifdef DEBUG - redSum += *redSource; - greenSum += *greenSource; - blueSum += *blueSource; -#endif - *(target++) = *(redSource++); - *(target++) = *(greenSource++); - *(target++) = *(blueSource++); - } - redSource += 2 * scanner->scan_width_pixels; - greenSource += 2 * scanner->scan_width_pixels; - blueSource += 2 * scanner->scan_width_pixels; -#ifdef DEBUG - redSum = redSum / scanner->scan_width_pixels / 26; - greenSum = greenSum / scanner->scan_width_pixels / 26; - blueSum = blueSum / scanner->scan_width_pixels / 26; - DBG (10, "line %4u: source lines %3u/%3u/%3u colors %u/%u/%u\n", - lineCount++, redStart, greenStart, blueStart, redSum, greenSum, - blueSum); -#endif - fwrite (linebuffer, 1, scanner->bytes_per_scan_line, fp); - } - fflush (fp); + DBG(10, "do_scsi_cmd: finish\n"); - data_left -= data_to_read; - DBG (10, "reader_process(color, simplex): buffer of %d bytes read; %d bytes to go\n", - data_to_read, data_left); - memcpy (largeBuffer, largeBuffer + dataToProcess, lookAheadSize); - readOffset = lookAheadSize; - } - while (data_left); - free (linebuffer); - if (largeBuffer != scanner->buffer) - free (largeBuffer); - fclose (fp); - - return total_data_size; + return SANE_STATUS_GOOD; } +SANE_Status +do_usb_cmd(struct fujitsu *s, int runRS, int shortTime, + unsigned char * cmdBuff, size_t cmdLen, + unsigned char * outBuff, size_t outLen, + unsigned char * inBuff, size_t inLen +) +{ + unsigned char usb_cmdBuff[USB_COMMAND_LEN]; + unsigned char usb_statBuff[USB_STATUS_LEN]; + unsigned char rsBuff[RS_return_size]; + size_t usb_cmdLen = USB_COMMAND_LEN; + size_t usb_outLen = outLen; + size_t usb_inLen = inLen; + size_t usb_statLen = USB_STATUS_LEN; + int cmdRetVal = 0; + int outRetVal = 0; + int inRetVal = 0; + int statRetVal = 0; + int rsRetVal = 0; + int cmdTime = USB_COMMAND_TIME; + int outTime = USB_DATA_TIME; + int inTime = USB_DATA_TIME; + int statTime = USB_STATUS_TIME; -static unsigned int -reader3091ColorDuplex (struct fujitsu *scanner, FILE * fp_front, FILE * fp_back) -{ - int status; - unsigned int data_left; - unsigned int total_data_size, data_to_read, dataToProcess; - unsigned int green_offset, blue_offset, lookAheadSize; - unsigned readOffset; - unsigned int duplexStartLine, duplexEndLine; - unsigned char *linebuffer = 0; - unsigned char *duplexBuffer = 0; - unsigned char *duplexPointer = 0; - unsigned char *largeBuffer; - unsigned int largeBufferSize = 0; - unsigned int duplexBufferSize = 0; - unsigned int lineCount = 0; - unsigned int colorLineGap = 0; - unsigned char *redSource, *greenSource, *blueSource, *target; - unsigned int i_data_read; - -#ifdef DEBUG - unsigned int frontLineCount = 0; - unsigned int backLineCount = 0; - int redSum, greenSum, blueSum; - unsigned int redStart, greenStart, blueStart; -#endif + DBG (10, "do_usb_cmd: start\n"); - linebuffer = malloc (scanner->bytes_per_scan_line); - if (linebuffer == NULL) - { - DBG (MSG_ERR, "reader_process: out of memory for line buffer\n"); - return (0); + if(shortTime){ + cmdTime = USB_COMMAND_TIME/20; + outTime = USB_DATA_TIME/20; + inTime = USB_DATA_TIME/20; + statTime = USB_STATUS_TIME/20; } - data_left = scanner->bytes_per_scan_line * scanner->scan_height_pixels; - colorLineGap = scanner->color_raster_offset * scanner->resolution_y / 300; - green_offset = 2 * colorLineGap * scanner->bytes_per_scan_line; - blue_offset = colorLineGap * scanner->bytes_per_scan_line; + /* build a USB packet around the SCSI command */ + memset(&usb_cmdBuff,0,USB_COMMAND_LEN); + usb_cmdBuff[0] = USB_COMMAND_CODE; + memcpy(&usb_cmdBuff[USB_COMMAND_OFFSET],cmdBuff,cmdLen); - lookAheadSize = green_offset * 2; - - /* - * In duplex mode, the scanner will first send some lines - * belonging to the first page, then, after a certain number of - * lines (duplexStartLine) it will send lines from the front and - * back page in an alternating fashion, and after duplexEndLine - * lines there will be only lines from the back page. - * - * The total number of lines will be lines_per_scan * 2. - * - * Please also read the comments in reader3091ColorSimplex which - * explain the color encoding. They are valid for duplex color - * scans as well, with the exception that at the two critical - * points within the page where "front only" changes into - * "front/back alternating" and "front/back alternating" changes - * into "back only" the calculation of the offset between red, - * green and blue is a bit tricky. - * - * For example, at 300 dpi the green value matching the red value - * I'm just reading will lie 8 lines ahead (simplex); if I'm in - * the middle of a duplex scan, it's going to be 16 lines (because - * there will be 8 lines belonging to the reverse side and thus - * not being counted); but if the red pixel I'm reading is before - * one of the mentioned critical boundaries and the matching green - * pixel is beyond, this number may be anything in the range 8-16. - * Took me some time to figure it out but I hope it works now. - */ - duplexStartLine = - scanner->duplex_raster_offset * scanner->resolution_y / 300 + 1; - duplexEndLine = - scanner->scan_height_pixels * 2 + 4 * colorLineGap - - scanner->duplex_raster_offset * scanner->resolution_y / 300; + /* change timeout */ + sanei_usb_set_timeout(cmdTime); - DBG (5, "duplex start line %u, end line %u, color gap %u\n", - duplexStartLine, duplexEndLine, colorLineGap); + /* write the command out */ + DBG(25, "cmd: writing %d bytes, timeout %d\n", USB_COMMAND_LEN, cmdTime); + hexdump(30, "cmd: >>", usb_cmdBuff, USB_COMMAND_LEN); + cmdRetVal = sanei_usb_write_bulk(s->fd, usb_cmdBuff, &usb_cmdLen); + DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)usb_cmdLen, cmdRetVal); - /* Only allocate memory if we're not using a temp file. */ - if (scanner->use_temp_file) - { - duplexBuffer = duplexPointer = NULL; - duplexBufferSize = 0; - } - else - { - duplexBuffer = malloc (duplexBufferSize = data_left); - if (duplexBuffer == NULL) - { - DBG (MSG_ERR, - "reader_process: out of memory for duplex buffer (try option --swapfile)\n"); - return (0); - } - duplexPointer = duplexBuffer; + if(cmdRetVal != SANE_STATUS_GOOD){ + DBG(5,"cmd: return error '%s'\n",sane_strstatus(cmdRetVal)); + return cmdRetVal; } - data_left *= 2; - - largeBuffer = scanner->buffer; - largeBufferSize = - scanner->scsi_buf_size - - scanner->scsi_buf_size % scanner->bytes_per_scan_line; - - if (largeBufferSize < 2 * lookAheadSize) - { - /* - * We're in a bit of trouble here. To decode colors correctly, - * we have to look ahead in the data stream, but obviously the - * pre-allocated buffer will not hold enough lines to do this - * in an effective fashion (or do it at all). Get a larger - * buffer. - */ - largeBuffer = malloc (largeBufferSize = 4 * lookAheadSize); - if (largeBuffer == NULL) - { - DBG (MSG_ERR, - "reader_process: out of memory for SCSI read buffer, try smaller image\n"); - return (0); - } + if(usb_cmdLen != USB_COMMAND_LEN){ + DBG(5,"cmd: wrong size %d/%d\n", USB_COMMAND_LEN, (int)usb_cmdLen); + return SANE_STATUS_IO_ERROR; } - DBG (MSG_IO_READ, - "reader_process: reading %u bytes in blocks of %u bytes\n", - data_left, scanner->scsi_buf_size); + /* this command has a write component, and a place to get it */ + if(outBuff && outLen && outTime){ + /* change timeout */ + sanei_usb_set_timeout(outTime); - readOffset = 0; - data_left += lookAheadSize; - total_data_size = data_left; + DBG(25, "out: writing %d bytes, timeout %d\n", (int)outLen, outTime); + hexdump(30, "out: >>", outBuff, outLen); + outRetVal = sanei_usb_write_bulk(s->fd, outBuff, &usb_outLen); + DBG(25, "out: wrote %d bytes, retVal %d\n", (int)usb_outLen, outRetVal); - do - { - data_to_read = (data_left < largeBufferSize - readOffset) - ? data_left : largeBufferSize - readOffset; - - dataToProcess = data_to_read + readOffset - lookAheadSize; - if (data_to_read == data_left) - dataToProcess += lookAheadSize / 2; - - status = - read_large_data_block (scanner, largeBuffer + readOffset, data_to_read, - 0, &i_data_read); - switch (status) - { - case SANE_STATUS_GOOD: - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", - scanner->i_transfer_length); - data_to_read -= scanner->i_transfer_length; - data_left = data_to_read; - break; - default: - DBG (MSG_ERR, - "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - fclose (fp_back); - return (0); + if(outRetVal != SANE_STATUS_GOOD){ + DBG(5,"out: return error '%s'\n",sane_strstatus(outRetVal)); + return outRetVal; } + if(usb_outLen != outLen){ + DBG(5,"out: wrong size %d/%d\n", (int)outLen, (int)usb_outLen); + return SANE_STATUS_IO_ERROR; + } + } - /* - * This scanner will send you one line of R values, one line of G - * values, and one line of B values in succession. - * The pitfall, however, is that those are *not* corresponding to - * one and the same "line" on the paper. - * Instead, the "green" values matching the first line of "red" - * values appear 8 lines further "down" in the data stream, and - * the "blue" values are 4 lines further "down". (That's for 300dpi; - * for 75dpi it's 2 and 1 line, for 150dpi it's 4 and 2 lines, - * for 600dpi 16 and 8 lines.) - * The scanner will send you a total of 8 lines (at 300dpi) more - * than you asked for so that you can build the complete image - * from that. - * - * This code basically works by retaining the last "lookAheadSize" - * bytes of the buffer just read and copying them onto the beginning - * of the next data block, in effect creating a "sliding window" - * over the data stream with an overlap of "lookAheadSize". - */ - - redSource = largeBuffer; - greenSource = redSource + green_offset + scanner->scan_width_pixels; - blueSource = redSource + blue_offset + 2 * scanner->scan_width_pixels; - - while (redSource < largeBuffer + dataToProcess) - { - -#ifdef DEBUG - redStart = - ((unsigned long int) redSource - - (unsigned long int) largeBuffer) / scanner->bytes_per_scan_line; - greenStart = - ((unsigned long int) greenSource - - (unsigned long int) largeBuffer) / scanner->bytes_per_scan_line; - blueStart = - ((unsigned long int) blueSource - - (unsigned long int) largeBuffer) / scanner->bytes_per_scan_line; - redSum = 0; - greenSum = 0; - blueSum = 0; -#endif - - target = linebuffer; - while (target < linebuffer + scanner->bytes_per_scan_line) - { -#ifdef DEBUG - redSum += *redSource; - greenSum += *greenSource; - blueSum += *blueSource; -#endif - *(target++) = *(redSource++); - *(target++) = *(greenSource++); - *(target++) = *(blueSource++); - } - redSource += 2 * scanner->scan_width_pixels; - greenSource += 2 * scanner->scan_width_pixels; - blueSource += 2 * scanner->scan_width_pixels; -#ifdef DEBUG - redSum = redSum / scanner->scan_width_pixels / 26; - greenSum = greenSum / scanner->scan_width_pixels / 26; - blueSum = blueSum / scanner->scan_width_pixels / 26; -#endif + /* this command has a read component, and a place to put it */ + if(inBuff && inLen && inTime){ - if ((lineCount < duplexStartLine) || - (((lineCount - duplexStartLine) % 2 == 1) - && (lineCount < duplexEndLine))) - { - /* we want to ignore a certain number of invalid lines at the end */ - if (lineCount < duplexEndLine - 4 * colorLineGap) - { - fwrite (linebuffer, 1, scanner->bytes_per_scan_line, - fp_front); -#ifdef DEBUG - DBG (10, - "line %4u to front line %4u source lines %3u/%3u/%3u colors %u/%u/%u\n", - lineCount, frontLineCount++, redStart, greenStart, - blueStart, redSum, greenSum, blueSum); - } - else - { - DBG (10, - "line %4u (front/ignored) source lines %3u/%3u/%3u colors %u/%u/%u\n", - lineCount, redStart, greenStart, blueStart, redSum, - greenSum, blueSum); -#endif - } - } - else - { - if (scanner->use_temp_file) - { - if ((int) - fwrite (linebuffer, 1, scanner->bytes_per_scan_line, - fp_back) != scanner->bytes_per_scan_line) - { - fclose (fp_back); - DBG (MSG_ERR, - "reader_process: out of disk space while writing temp file\n"); - return (0); - } - } - else - { - memcpy (duplexPointer, linebuffer, - scanner->bytes_per_scan_line); - duplexPointer += scanner->bytes_per_scan_line; - } -#ifdef DEBUG - DBG (10, - "line %4u to back line %4u source lines %3u/%3u/%3u colors %u/%u/%u\n", - lineCount, backLineCount++, redStart, greenStart, - blueStart, redSum, greenSum, blueSum); -#endif - } + memset(inBuff,0,inLen); - lineCount++; + /* change timeout */ + sanei_usb_set_timeout(inTime); - if ((lineCount >= duplexStartLine - 2 * colorLineGap) - && (lineCount < duplexStartLine)) - { - greenSource += scanner->bytes_per_scan_line; - green_offset += scanner->bytes_per_scan_line; - } - else if ((lineCount > duplexEndLine - 4 * colorLineGap) - && (lineCount <= duplexEndLine) - && ((duplexEndLine - lineCount) % 2 == 1)) - { - greenSource -= scanner->bytes_per_scan_line; - green_offset -= scanner->bytes_per_scan_line; - } - if ((lineCount >= duplexStartLine - colorLineGap) - && (lineCount < duplexStartLine)) - { - blueSource += scanner->bytes_per_scan_line; - blue_offset += scanner->bytes_per_scan_line; - } - else if ((lineCount > duplexEndLine - 2 * colorLineGap) - && (lineCount <= duplexEndLine) - && ((duplexEndLine - lineCount) % 2 == 1)) - { - blueSource -= scanner->bytes_per_scan_line; - blue_offset -= scanner->bytes_per_scan_line; - } + DBG(25, "in: reading %d bytes, timeout %d\n", (int)inLen, inTime); + inRetVal = sanei_usb_read_bulk(s->fd, inBuff, &usb_inLen); + DBG(25, "in: retVal %d\n", inRetVal); + if(inRetVal != SANE_STATUS_GOOD){ + DBG(5,"in: return error '%s'\n",sane_strstatus(inRetVal)); + return inRetVal; } - fflush (fp_front); + hexdump(30, "in: <<", inBuff, usb_inLen); + DBG(25, "in: read %d bytes\n", (int)usb_inLen); - data_left -= data_to_read; - DBG (10, "reader_process(color, duplex): buffer of %d bytes read; %d bytes to go\n", - data_to_read, data_left); - /* FIXME: simon lai reported an overflow here! */ - memcpy (largeBuffer, largeBuffer + dataToProcess, lookAheadSize); - readOffset = lookAheadSize; - } - while (data_left); - free (linebuffer); - if (largeBuffer != scanner->buffer) - free (largeBuffer); - fclose (fp_front); - - /* - * If we're using a temp file, the data has already been written, - * and the file MUST NOT be closed since the parent process still - * needs to read from it. If we're using a pipe, stuff the data - * from duplexBuffer into the pipe and release the duplex buffer. - * - * When using a temp file, this subprocess will usually terminate - * before the first byte of the duplex page is read; when using a - * pipe, this subprocess will live until it has flushed the duplex - * buffer into the pipe. Since pipes usually only hold about 8 KB - * of data, the process will live until the front-end has fetched - * almost everything. - */ - if (scanner->use_temp_file) - { - fflush (fp_back); - } - else - { - fwrite (duplexBuffer, 1, duplexBufferSize, fp_back); - fclose (fp_back); - free (duplexBuffer); + if(usb_inLen != inLen){ + DBG(5,"in: wrong size %d/%d\n", (int)inLen, (int)usb_inLen); + return SANE_STATUS_IO_ERROR; + } } - return total_data_size; - -} + memset(&usb_statBuff,0,USB_STATUS_LEN); -static unsigned int -reader3091GrayDuplex (struct fujitsu *scanner, FILE * fp_front, FILE * fp_back) -{ - int status; - unsigned int total_data_size, data_left, data_to_read; - unsigned int duplexStartLine, duplexEndLine; - unsigned int duplexBufferSize; - unsigned char *duplexBuffer; - unsigned char *duplexPointer; - unsigned char *largeBuffer; - unsigned int largeBufferSize = 0; - unsigned char *source; - unsigned int lineCount = 0; - unsigned int i_data_read; + /* change timeout */ + sanei_usb_set_timeout(statTime); - data_left = scanner->bytes_per_scan_line * scanner->scan_height_pixels; + DBG(25, "stat: reading %d bytes, timeout %d\n", USB_STATUS_LEN, statTime); + statRetVal = sanei_usb_read_bulk(s->fd, usb_statBuff, &usb_statLen); + hexdump(30, "stat: <<", usb_statBuff, usb_statLen); + DBG(25, "stat: read %d bytes, retVal %d\n", (int)usb_statLen, statRetVal); - /* Only allocate memory if we're not using a temp file. */ - if (scanner->use_temp_file) - { - duplexBuffer = duplexPointer = NULL; - duplexBufferSize = 0; + if(statRetVal != SANE_STATUS_GOOD){ + DBG(5,"stat: return error '%s'\n",sane_strstatus(statRetVal)); + return statRetVal; } - else - { - duplexBuffer = malloc (duplexBufferSize = data_left); - if (duplexBuffer == NULL) - { - DBG (MSG_ERR, - "reader_process: out of memory for duplex buffer (try option --swapfile)\n"); - return (0); - } - duplexPointer = duplexBuffer; + if(usb_statLen != USB_STATUS_LEN){ + DBG(5,"stat: wrong size %d/%d\n", USB_STATUS_LEN, (int)usb_statLen); + return SANE_STATUS_IO_ERROR; } - data_left *= 2; - largeBuffer = scanner->buffer; - largeBufferSize = - scanner->scsi_buf_size - - (scanner->scsi_buf_size % scanner->bytes_per_scan_line); - - duplexStartLine = - scanner->duplex_raster_offset * scanner->resolution_y / 300 + 1; - duplexEndLine = - scanner->scan_height_pixels * 2 - - scanner->duplex_raster_offset * scanner->resolution_y / 300; - - total_data_size = data_left; - - do - { - data_to_read = (data_left < largeBufferSize) ? data_left : largeBufferSize; + /* busy status */ + if(usb_statBuff[USB_STATUS_OFFSET] == 8){ + DBG(25,"stat: busy\n"); + return SANE_STATUS_DEVICE_BUSY; + } - status = read_large_data_block (scanner, largeBuffer, data_to_read, - 0, &i_data_read); + /* if there is a non-busy status >0, try to figure out why */ + if(usb_statBuff[USB_STATUS_OFFSET] > 0){ - switch (status) - { - case SANE_STATUS_GOOD: - break; - case SANE_STATUS_EOF: - DBG (5, "reader_process: EOM (no more data) length = %d\n", - scanner->i_transfer_length); - data_to_read -= scanner->i_transfer_length; - data_left = data_to_read; - break; - default: - DBG (MSG_ERR, - "reader_process: unable to get image data from scanner!\n"); - fclose (fp_front); - fclose (fp_back); - return (0); - } + DBG(25,"stat: value %d\n", usb_statBuff[USB_STATUS_OFFSET]); - for (source = scanner->buffer; source < scanner->buffer + data_to_read; - source += scanner->bytes_per_scan_line) - { - if ((lineCount < duplexStartLine) || - (((lineCount - duplexStartLine) % 2 == 1) - && (lineCount < duplexEndLine))) - { - fwrite (source, 1, scanner->bytes_per_scan_line, fp_front); - } - else - { - if (scanner->use_temp_file) - { - if ((int) - fwrite (source, 1, scanner->bytes_per_scan_line, - fp_back) != scanner->bytes_per_scan_line) - { - fclose (fp_back); - DBG (MSG_ERR, - "reader_process: out of disk space while writing temp file\n"); - return (0); - } - } - else - { - memcpy (duplexPointer, source, - scanner->bytes_per_scan_line); - duplexPointer += scanner->bytes_per_scan_line; - } - } - lineCount++; + /* caller is interested in having RS run on errors */ + if(runRS){ + + DBG(25,"rs sub call >>\n"); + rsRetVal = do_cmd( + s,0,0, + request_senseB.cmd, request_senseB.size, + NULL,0, + rsBuff,RS_return_size + ); + DBG(25,"rs sub call <<\n"); + + if(rsRetVal != SANE_STATUS_GOOD){ + DBG(5,"rs: return error '%s'\n",sane_strstatus(rsRetVal)); + return rsRetVal; } - fflush (fp_front); - data_left -= data_to_read; - DBG (10, "reader_process(gray duplex): buffer of %d bytes read; %d bytes to go\n", - data_to_read, data_left); + /* parse the rs data */ + return sense_handler( 0, rsBuff, (void *)s ); + } + else{ + DBG(5,"do_usb_cmd: Not calling rs!\n"); + return SANE_STATUS_IO_ERROR; + } } - while (data_left); - fclose (fp_front); - /* see comment in reader3091ColorDuplex about this */ - if (scanner->use_temp_file) - { - fflush (fp_back); - } - else - { - fwrite (duplexBuffer, 1, duplexBufferSize, fp_back); - fclose (fp_back); - free (duplexBuffer); - } + DBG (10, "do_usb_cmd: finish\n"); - return total_data_size; + return SANE_STATUS_GOOD; } -/** - * This method is called from within sane_control_option whenever the scan mode - * (color, grayscale, ...) is changed. It has to change the members of the - * "struct fujitsu" structure accordingly and also update the SANE option - * structures to reflect the fact that some options may now be newly available - * or others unavailable. - * - * This is the incarnation for the M3091DCd scanner. - */ -static SANE_Status -setMode3091 (struct fujitsu *scanner, int mode) +static int +wait_scanner(struct fujitsu *s) { - /* - static const SANE_Int allowableY3091Color[] = { 4, 75, 150, 300, 600 }; - static const SANE_Range allowableY3091Mono = { 50, 600, 1 }; - static const SANE_Range allowableX3091 = { 50, 300, 1 }; - */ - static const SANE_Range allowableThreshold = { 0, 255, 1 }; - - SANE_Word tmp; - - scanner->color_mode = mode; - - switch (mode) - { - case MODE_LINEART: - - /* depth is fixed */ - scanner->scanner_depth = 1; - scanner->output_depth = 1; - - /* threshold is available, default is 128 */ - scanner->threshold = 128; - if (scanner->has_threshold) - { - scanner->opt[OPT_THRESHOLD].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; - } - scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_THRESHOLD].constraint.range = &allowableThreshold; - - /* brightness is unavailable */ - scanner->brightness = 0; - scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE; - - /* lamp color is selectable */ - scanner->lamp_color = 0; - scanner->opt[OPT_LAMP_COLOR].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + int ret; - /* X resolution free from 50-300, Y resolution free from 50-600 */ - /* - scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_X_RES].constraint.range = &allowableX3091; - scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_Y_RES].constraint.range = &allowableY3091Mono; - */ - - /* verify that the currently set resolutions aren't out of order */ - /* - for this specific scanner that's not necessary since */ - /* monochrome mode supports the superset of resolutions */ - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); - - case MODE_GRAYSCALE: - - /* 4bpp mode not yet implemented so depth is fixed at 8 */ - scanner->scanner_depth = 8; - scanner->output_depth = 8; - - /* threshold is unavailable */ - scanner->threshold = 0; - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE; - - /* brightness is unavailable */ - scanner->brightness = 0; - scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE; - - /* lamp color is selectable */ - scanner->lamp_color = 0; - scanner->opt[OPT_LAMP_COLOR].cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + DBG (10, "wait_scanner: start\n"); - /* X resolution free from 50-300, Y resolution free from 50-600 */ - /* - scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_X_RES].constraint.range = &allowableX3091; - scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_Y_RES].constraint.range = &allowableY3091Mono; - */ - - /* verify that the currently set resolutions aren't out of order */ - /* - for this specific scanner that's not necessary since */ - /* monochrome mode supports the superset of resolutions */ - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); - - case MODE_COLOR: - - /* depth is fixed at 24 */ - scanner->scanner_depth = 24; - scanner->output_depth = 24; - - /* threshold is unavailable */ - scanner->threshold = 0; - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE; - - /* brightness is unavailable */ - scanner->brightness = 0; - scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE; - - /* lamp color is unavailable */ - scanner->lamp_color = 0; - scanner->opt[OPT_LAMP_COLOR].cap = SANE_CAP_INACTIVE; + ret = do_cmd ( + s, 0, 1, + test_unit_readyB.cmd, test_unit_readyB.size, + NULL, 0, + NULL, 0 + ); + + if (ret != SANE_STATUS_GOOD) { + DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n"); + ret = do_cmd ( + s, 0, 1, + test_unit_readyB.cmd, test_unit_readyB.size, + NULL, 0, + NULL, 0 + ); + } + if (ret != SANE_STATUS_GOOD) { + DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n"); + ret = do_cmd ( + s, 0, 1, + test_unit_readyB.cmd, test_unit_readyB.size, + NULL, 0, + NULL, 0 + ); + } - /* X resolution free from 50-300, Y one out of 75, 150, 300, 600 */ - /* - scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_RANGE; - scanner->opt[OPT_X_RES].constraint.range = &allowableX3091; - scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_Y_RES].constraint.word_list = allowableY3091Color; - */ - - /* verify that the currently set resolutions aren't out of order */ - - /* x is uncritical but check y */ - - tmp = scanner->resolution_y; - if (sanei_constrain_value (scanner->opt + OPT_Y_RES, &tmp, NULL) == - SANE_STATUS_GOOD) - { - scanner->resolution_y = tmp; - } - else - { - scanner->resolution_y = 300; - } - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret)); + } - } /* end of SELECT */ + DBG (10, "wait_scanner: finish\n"); - return SANE_STATUS_INVAL; + return ret; } -/* - * Initializes the "struct fujitsu" structure with meaningful values for the - * M3091DCd scanner. +/** + * Convenience method to determine longest string size in a list. */ -static void -setDefaults3091 (struct fujitsu *scanner) +static size_t +maxStringSize (const SANE_String_Const strings[]) { + size_t size, max_size = 0; + int i; - scanner->use_adf = scanner->has_adf; - scanner->duplex_mode = DUPLEX_FRONT; - scanner->resolution_x = 300; - scanner->resolution_y = 300; - scanner->resolution_linked = SANE_TRUE; - scanner->brightness = 0; - scanner->threshold = 0; - scanner->contrast = 0; - scanner->reverse = 0; - scanner->bitorder = 0; - scanner->compress_type = 0; - scanner->compress_arg = 0; - /*scanner->scanning_order = 0;*/ - setMode3091 (scanner, MODE_COLOR); - - scanner->val[OPT_TL_X].w = 0; - scanner->val[OPT_TL_Y].w = 0; - /* this size is just big enough to match letter and A4 formats */ - if (scanner->model == MODEL_3092) - { - scanner->bottom_right_x = SANE_FIX (215.9); - } - else - { - scanner->bottom_right_x = SANE_FIX (215.0); - } - scanner->bottom_right_y = SANE_FIX (297.0); - scanner->page_width = FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_x); - scanner->page_height = FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_y); - scanner->mirror = SANE_FALSE; - scanner->use_temp_file = SANE_FALSE; - - /* required according to manual */ - - scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_FILTERING].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_GRADATION].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_VARIANCE_RATE].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_INACTIVE; - - scanner->dropout_color = MSEL_dropout_DEFAULT; + for (i = 0; strings[i]; ++i) { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } - scanner->sleep_time = 15; - scanner->use_imprinter = SANE_FALSE; + return max_size; } - -/* - * @@ Section 6 - M3096 specific internal routines - */ - /** - * Initializes the "struct fujitsu" structure with meaningful values for the - * M3096 scanner. + * Prints a hex dump of the given buffer onto the debug output stream. */ static void -setDefaults3096 (struct fujitsu *scanner) +hexdump (int level, char *comment, unsigned char *p, int l) { + int i; + char line[128]; + char *ptr; - scanner->use_adf = scanner->has_adf; - scanner->duplex_mode = DUPLEX_FRONT; - scanner->resolution_x = 300; - scanner->resolution_y = 300; - scanner->resolution_linked = SANE_TRUE; - scanner->brightness = 0; - scanner->threshold = 0; - scanner->contrast = 0; - scanner->reverse = 0; - setMode3096 (scanner, MODE_GRAYSCALE); - - scanner->val[OPT_TL_X].w = 0; - scanner->val[OPT_TL_Y].w = 0; - /* this size is just big enough to match letter and A4 formats */ - scanner->bottom_right_x = SANE_FIX (215.9); - scanner->bottom_right_y = SANE_FIX (297.0); + if(DBG_LEVEL < level) + return; - if(!scanner->has_fixed_paper_size) + DBG (level, "%s\n", comment); + ptr = line; + for (i = 0; i < l; i++, p++) { - - scanner->page_width = FIXED_MM_TO_SCANNER_UNIT(scanner->bottom_right_x); - scanner->page_height = FIXED_MM_TO_SCANNER_UNIT(scanner->bottom_right_y); + if ((i % 16) == 0) + { + if (ptr != line) + { + *ptr = '\0'; + DBG (level, "%s\n", line); + ptr = line; + } + sprintf (ptr, "%3.3x:", i); + ptr += 4; + } + sprintf (ptr, " %2.2x", *p); + ptr += 3; } - - scanner->mirror = SANE_FALSE; - scanner->use_temp_file = SANE_FALSE; - - scanner->bitorder = 0; - scanner->compress_type = 0; - scanner->compress_arg = 0; - scanner->vendor_id_code = 0; - scanner->outline = 0; - scanner->emphasis = WD_emphasis_NONE; - scanner->auto_sep = 0; - scanner->mirroring = 0; - scanner->var_rate_dyn_thresh = 0; - scanner->dtc_threshold_curve = 0; - - scanner->gradation = WD_gradation_ORDINARY; - scanner->smoothing_mode = WD_smoothing_IMAGE; - scanner->filtering = WD_filtering_ORDINARY; - scanner->background = WD_background_WHITE; - scanner->matrix2x2 = 0; - scanner->matrix3x3 = 0; - scanner->matrix4x4 = 0; - scanner->matrix5x5 = 0; - scanner->noise_removal = 0; - - scanner->opt[OPT_MATRIX2X2].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX3X3].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX4X4].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_MATRIX5X5].cap = SANE_CAP_INACTIVE; - - scanner->white_level_follow = WD_white_level_follow_DEFAULT; - - if (scanner->has_fixed_paper_size) - { - scanner->page_width=FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_x); - scanner->page_height=FIXED_MM_TO_SCANNER_UNIT (scanner->bottom_right_y); - scanner->opt[OPT_PAGE_HEIGHT].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_PAGE_WIDTH].cap = SANE_CAP_INACTIVE; - } - - scanner->dtc_selection = WD_dtc_selection_DEFAULT; - scanner->opt[OPT_NOISE_REMOVAL].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_BACKGROUND].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_FILTERING].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_SMOOTHING_MODE].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_GRADATION].cap = SANE_CAP_INACTIVE; - scanner->opt[OPT_THRESHOLD_CURVE].cap = SANE_CAP_INACTIVE; - - scanner->dropout_color = MSEL_dropout_DEFAULT; - /* 3091 only option */ - scanner->opt[OPT_LAMP_COLOR].cap = SANE_CAP_INACTIVE; - - scanner->imprinter_direction = S_im_dir_top_bottom; - scanner->imprinter_y_offset = 7; - memcpy(scanner->imprinter_string, "%05ud", max_imprinter_string_length); - scanner->imprinter_ctr_init = 0; - scanner->imprinter_ctr_step = 1; - scanner->imprinter_ctr_dir = S_im_dir_inc; - - scanner->sleep_time = 15; - scanner->use_imprinter = SANE_FALSE; + *ptr = '\0'; + DBG (level, "%s\n", line); } /** - * This method is called from within sane_control_option whenever the - * scan mode (color, grayscale, ...) is changed. It has to change the - * members of the "struct fujitsu" structure accordingly and also - * update the SANE option structures to reflect the fact that some - * options may now be newly available or others unavailable. - * - * This is the incarnation for the M3096 scanner. + * An advanced method we don't support but have to define. */ -static SANE_Status -setMode3096 (struct fujitsu *scanner, int mode) +SANE_Status +sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking) { - /* - static const SANE_Int allowableResolutionsBW[] = { 4, 200, 240, 300, 400 }; - static const SANE_Int allowableResolutionsGray[] = { 3, 200, 300, 400 }; - */ - - scanner->color_mode = mode; - - switch (mode) - { - case MODE_LINEART: - - scanner->reverse = SANE_FALSE; - - /* depth is fixed */ - scanner->scanner_depth = 1; - scanner->output_depth = 1; - - /* threshold is available, default is 128 */ - scanner->threshold = 128; - if (scanner->has_threshold) - { - scanner->opt[OPT_THRESHOLD].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; - } - - /* brightness is unavailable */ - scanner->brightness = 0; - scanner->opt[OPT_BRIGHTNESS].cap = SANE_CAP_INACTIVE; - - if (scanner->has_gamma) - { - scanner->opt[OPT_GAMMA].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;; - } - - /* X resolution and Y resolution in steps including 240 - scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_X_RES].constraint.word_list = allowableResolutionsBW; - scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_Y_RES].constraint.word_list = allowableResolutionsBW; - */ - - /* verify that the currently set resolutions aren't out of order */ - /* - for this specific scanner that's not necessary since */ - /* monochrome mode supports the superset of resolutions */ - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); - - case MODE_HALFTONE: - - scanner->reverse = SANE_FALSE; - - /* depth is fixed */ - scanner->scanner_depth = 1; - scanner->output_depth = 1; - - /* threshold is only for line art mode. */ - scanner->threshold = 0; - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE; - - /* brightness is available, 8 steps */ - scanner->brightness = 0x80; - if (scanner->has_brightness) - { - scanner->opt[OPT_BRIGHTNESS].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; - } - - if (scanner->has_gamma) - { - scanner->opt[OPT_GAMMA].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; - } - - /* X resolution and Y resolution in steps including 240 - scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_X_RES].constraint.word_list = allowableResolutionsBW; - scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_Y_RES].constraint.word_list = allowableResolutionsBW; - */ - - /* verify that the currently set resolutions aren't out of order */ - /* - for this specific scanner that's not necessary since */ - /* monochrome mode supports the superset of resolutions */ - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); - - case MODE_GRAYSCALE: - - scanner->reverse = SANE_FALSE; - - /* depth fixed at 8 */ - scanner->scanner_depth = 8; - scanner->output_depth = 8; - - /* threshold is unavailable */ - scanner->threshold = 0; - scanner->opt[OPT_THRESHOLD].cap = SANE_CAP_INACTIVE; - - if (scanner->has_brightness) - { - scanner->opt[OPT_BRIGHTNESS].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; - } - - /* Gamma unavailable. */ - if (scanner->has_gamma) - { - scanner->opt[OPT_GAMMA].cap = SANE_CAP_INACTIVE; - } - - switch (scanner->compress_type) - { - case WD_cmp_MH: - case WD_cmp_MR: - case WD_cmp_MMR: - scanner->compress_type = WD_cmp_NONE; - break; - } - - /* X and y in steps without the 240 - scanner->opt[OPT_X_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_X_RES].constraint.word_list = allowableResolutionsGray; - scanner->opt[OPT_Y_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; - scanner->opt[OPT_Y_RES].constraint.word_list = allowableResolutionsGray; - */ - - /* verify that the currently set resolutions aren't out of order */ - /* - if (scanner->resolution_x == 240) - scanner->resolution_x = 300; - if (scanner->resolution_y == 240) - scanner->resolution_y = 300; - */ - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); - - case MODE_COLOR: - - if (scanner->has_brightness) - { - scanner->opt[OPT_BRIGHTNESS].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; - } - scanner->scanner_depth = 24; - scanner->output_depth = 24; - calculateDerivedValues (scanner); - return (SANE_STATUS_GOOD); - - } /* end of SELECT */ - - return (SANE_STATUS_INVAL); + DBG (10, "sane_set_io_mode\n"); + DBG (15, "%d %p\n", non_blocking, h); + return SANE_STATUS_UNSUPPORTED; } -/* - * @@ Section 7 - EVPD specific internal routines - */ - -/* - * @@ Section 8 - M3092 specific internal routines +/** + * An advanced method we don't support but have to define. */ - - +SANE_Status +sane_get_select_fd (SANE_Handle h, SANE_Int *fdp) +{ + DBG (10, "sane_get_select_fd\n"); + DBG (15, "%p %d\n", h, *fdp); + return SANE_STATUS_UNSUPPORTED; +} diff -uNr sane-backends-1.0.17/backend/fujitsu.conf sane-backends-1.0.18/backend/fujitsu.conf --- sane-backends-1.0.17/backend/fujitsu.conf 2003-10-07 18:50:02.000000000 +0200 +++ sane-backends-1.0.18/backend/fujitsu.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,16 +0,0 @@ -# To search for all FUJITSU scsi devices -scsi FUJITSU - -# To use a specific scsi device -#/dev/sg1 - -# For Fujitsu scanners connected via USB on a known device (kernel driver): -#usb /dev/usb/scanner0 - -# For Fujitsu scanners connected via USB using vendor and device ids (libusb): -#usb 0x04c5 0x1042 -#usb 0x04c5 0x1041 - -# To force the Fujitsu backend to treat all devices it finds as a certain type: -#option force-model fi-4340Cdi - diff -uNr sane-backends-1.0.17/backend/fujitsu.conf.in sane-backends-1.0.18/backend/fujitsu.conf.in --- sane-backends-1.0.17/backend/fujitsu.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/fujitsu.conf.in 2006-06-29 19:11:57.000000000 +0200 @@ -0,0 +1,46 @@ +# NOTE: any 'option' lines only apply to +# scanners discovered later in this file + +# to set data buffer size, in bytes +# the value ranges from 4096 - infinity +# but you may have scanning problems with +# a value larger than 65536 (the default) +option buffer-size 65536 + +# To search for all FUJITSU scsi devices +scsi FUJITSU + +# To use a specific scsi device +#scsi /dev/sg1 + +# For Fujitsu scanners connected via USB on a known device (kernel driver): +#usb /dev/usb/scanner0 + +# For Fujitsu scanners connected via USB using vendor and device ids (libusb): +#usb VENDORID PRODUCTID + +# NOTE: if you have to add your device here- please send the id and model +# to the author via email, so it can be included in next version. anoah at +# pfeiffer dot edu - with Fujitsu in the subject line + +#fi-4x20C +usb 0x04c5 0x1041 +usb 0x04c5 0x1042 + +#fi-5110xxx +usb 0x04c5 0x1096 +usb 0x04c5 0x1097 + +#fi-4x20C2 +usb 0x04c5 0x10ae +usb 0x04c5 0x10af + +#fi-5x20C +usb 0x04c5 0x10e0 +usb 0x04c5 0x10e1 + +#fi-5900C +usb 0x04c5 0x10e7 + +#scansnap S500 +usb 0x04c5 0x10fe diff -uNr sane-backends-1.0.17/backend/fujitsu.h sane-backends-1.0.18/backend/fujitsu.h --- sane-backends-1.0.17/backend/fujitsu.h 2004-05-24 20:56:10.000000000 +0200 +++ sane-backends-1.0.18/backend/fujitsu.h 2006-06-19 23:07:53.000000000 +0200 @@ -3,15 +3,9 @@ /* * Part of SANE - Scanner Access Now Easy. - * * Please see opening comment in fujitsu.c */ -/* ------------------------------------------------------------------------- */ - -static int num_devices = 0; -static struct fujitsu *first_dev; - /* ------------------------------------------------------------------------- * This option list has to contain all options for all scanners supported by * this driver. If a certain scanner cannot handle a certain option, there's @@ -22,410 +16,390 @@ OPT_NUM_OPTS = 0, OPT_MODE_GROUP, - OPT_SOURCE, - OPT_MODE, - OPT_DUPLEX, - OPT_X_RES, - OPT_Y_RES, + OPT_SOURCE, /*fb/adf/front/back/duplex*/ + OPT_MODE, /*mono/gray/color*/ + OPT_X_RES, /*a range or a list*/ + OPT_Y_RES, /*a range or a list*/ OPT_GEOMETRY_GROUP, - OPT_TL_X, /* in mm/2^16 */ - OPT_TL_Y, /* in mm/2^16 */ - OPT_BR_X, /* in mm/2^16 */ - OPT_BR_Y, /* in mm/2^16 */ + OPT_TL_X, + OPT_TL_Y, + OPT_BR_X, + OPT_BR_Y, OPT_PAGE_WIDTH, OPT_PAGE_HEIGHT, - OPT_ENHANCEMENT_GROUP, - OPT_AVERAGING, OPT_BRIGHTNESS, OPT_THRESHOLD, OPT_CONTRAST, - OPT_RIF, - OPT_COMPRESSION, - OPT_COMPRESSION_ARG, - - OPT_DTC_SELECTION, - OPT_GAMMA, - OPT_OUTLINE_EXTRACTION, - OPT_EMPHASIS, - OPT_AUTOSEP, - OPT_MIRROR_IMAGE, - OPT_VARIANCE_RATE, - OPT_THRESHOLD_CURVE, - OPT_GRADATION, - OPT_SMOOTHING_MODE, - OPT_FILTERING, - OPT_BACKGROUND, - OPT_NOISE_REMOVAL, - OPT_MATRIX2X2, - OPT_MATRIX3X3, - OPT_MATRIX4X4, - OPT_MATRIX5X5, - OPT_WHITE_LEVEL_FOLLOW, - - OPT_PAPER_SIZE, - OPT_PAPER_WIDTH, - OPT_PAPER_HEIGHT, - OPT_PAPER_ORIENTATION, + OPT_ADVANCED_GROUP, OPT_DROPOUT_COLOR, - OPT_START_BUTTON, - - OPT_TUNING_GROUP, - OPT_LAMP_COLOR, - OPT_BLUE_OFFSET, + OPT_SLEEP_TIME, + OPT_DUPLEX_OFFSET, OPT_GREEN_OFFSET, + OPT_BLUE_OFFSET, OPT_USE_SWAPFILE, - OPT_IMPRINTER_GROUP, - OPT_IMPRINTER, - OPT_IMPRINTER_DIR, - OPT_IMPRINTER_YOFFSET, - OPT_IMPRINTER_STRING, - OPT_IMPRINTER_CTR_INIT, - OPT_IMPRINTER_CTR_STEP, - OPT_IMPRINTER_CTR_DIR, - - OPT_SLEEP_MODE, + OPT_SENSOR_GROUP, + OPT_TOP, + OPT_A3, + OPT_B4, + OPT_A4, + OPT_B5, + OPT_HOPPER, + OPT_OMR, + OPT_ADF_OPEN, + OPT_SLEEP, + OPT_SEND_SW, + OPT_MANUAL_FEED, + OPT_SCAN_SW, + OPT_FUNCTION, + OPT_INK_EMPTY, + OPT_DOUBLE_FEED, + OPT_ERROR_CODE, + OPT_SKEW_ANGLE, + OPT_INK_REMAIN, + OPT_DUPLEX_SW, + OPT_DENSITY_SW, /* must come last: */ NUM_OPTIONS }; - -typedef enum { /* hardware connection to the scanner */ - SANE_FUJITSU_NODEV, /* default, no HW specified yet */ - SANE_FUJITSU_SCSI, /* SCSI interface */ - SANE_FUJITSU_PIO, /* parallel interface */ - SANE_FUJITSU_USB /* USB interface */ -} Fujitsu_Connection_Type; - struct fujitsu { + /* --------------------------------------------------------------------- */ + /* immutable values which are set during init of scanner. */ struct fujitsu *next; + char *device_name; /* The name of the scanner device for sane */ - SANE_Option_Descriptor opt[NUM_OPTIONS]; - Option_Value val[NUM_OPTIONS]; + /* --------------------------------------------------------------------- */ + /* immutable values which are set during reading of config file. */ + int buffer_size; + int connection; /* hardware interface type */ + + /* --------------------------------------------------------------------- */ + /* immutable values which are set during inquiry probing of the scanner. */ + /* members in order found in scsi data... */ SANE_Device sane; + char vendor_name[9]; /* raw data as returned by SCSI inquiry. */ + char product_name[17]; /* raw data as returned by SCSI inquiry. */ + char version_name[5]; /* raw data as returned by SCSI inquiry. */ + + int color_raster_offset; /* offset between r and b scan line and */ + /* between b and g scan line (0 or 4) */ + int duplex_raster_offset; /* offset between front and rear page when */ + /* when scanning 3091 style duplex */ - /* Immutable values which are set during initial probing of the scanner. */ /* --------------------------------------------------------------------- */ + /* immutable values which are set during std VPD probing of the scanner. */ + /* members in order found in scsi data... */ + int basic_x_res; + int basic_y_res; + int step_x_res; + int step_y_res; + int max_x_res; + int max_y_res; + int min_x_res; + int min_y_res; + + int std_res_200; + int std_res_180; + int std_res_160; + int std_res_150; + int std_res_120; + int std_res_100; + int std_res_75; + int std_res_60; + int std_res_1200; + int std_res_800; + int std_res_600; + int std_res_480; + int std_res_400; + int std_res_320; + int std_res_300; + int std_res_240; + + /* max scan size in pixels comes from scanner in basic res units */ + int max_x_basic; + int max_y_basic; + + int can_overflow; + int can_monochrome; + int can_halftone; + int can_grayscale; + int can_color_monochrome; + int can_color_halftone; + int can_color_grayscale; - char vendorName[9]; /* raw data as returned by SCSI inquiry. */ - char productName[17]; /* raw data as returned by SCSI inquiry. */ - char versionName[5]; /* raw data as returned by SCSI inquiry. */ + /* --------------------------------------------------------------------- */ + /* immutable values which are set during vndr VPD probing of the scanner */ + /* members in order found in scsi data... */ + int has_operator_panel; + int has_barcode; + int has_imprinter; + int has_duplex; + int has_transparency; + int has_flatbed; + int has_adf; - int model; /* The scanner model. */ + int adbits; + int buffer_bytes; - char *devicename; /* The name of the scanner device. */ + /*FIXME: do we need the std cmd list? */ - Fujitsu_Connection_Type connection; /* hardware interface type */ + /*FIXME: there are more vendor cmds? */ + int has_cmd_subwindow; + int has_cmd_endorser; + int has_cmd_hw_status; + int has_cmd_scanner_ctl; - int sfd; /* The scanner device file descriptor. */ + /*FIXME: do we need the vendor window param list? */ - int color_raster_offset; /* offset between r and b scan line and */ - /* between b and g scan line (4 for all */ - /* known models) */ - int duplex_raster_offset; /* offset between front and rear page when */ - /* when scanning duplex */ - - int can_read_alternate; /* duplex transfer mode front/back/front/back... */ - int read_mode; /* constants READ_MODE_* and funcs convert_* */ - int has_adf; /* true if an ADF has been detected. */ - int has_fb; - int duplex_present; /* true if a duplex unit has been detected. */ - int ipc_present; /* true if ipc2/3 option detected */ - int cmp_present; /* true if cmp2 present */ - int has_hw_status; /* true for M409X series */ - int has_outline; /* ............ */ - int has_emphasis; /* ............ */ - int has_autosep; /* ............ */ - int has_mirroring; /* ............ */ - int has_reverse; - int has_white_level_follow; /* ............ */ - int has_subwindow; /* ............ */ - int has_dropout_color; /* */ - int has_imprinter; - int has_threshold; - int has_brightness; - int has_contrast; - int has_gamma; + int brightness_steps; + int threshold_steps; + int contrast_steps; + + int num_internal_gamma; int num_download_gamma; - int has_fixed_paper_size; + int num_internal_dither; + int num_download_dither; + + int has_rif; + int has_auto1; + int has_auto2; + int has_outline; + int has_emphasis; + int has_autosep; + int has_mirroring; + int has_white_level_follow; + int has_subwindow; + + int has_comp_MH; + int has_comp_MR; + int has_comp_MMR; + int has_comp_JBIG; + int has_comp_JPG1; + int has_comp_JPG2; + int has_comp_JPG3; + + /*FIXME: endorser data? */ + + /*FIXME: barcode data? */ + + /* --------------------------------------------------------------------- */ + /* immutable values which are hard coded because they are not in vpd */ + /* this section replaces all the old 'switch (s->model)' code */ + + /* the scan size in 1/1200th inches, NOT basic_units or sane units */ + int max_x; + int max_y; + int min_x; + int min_y; + + int has_back; /* not all duplex scanners can do adf back side only */ + int color_interlace; /* different models interlace colors differently */ + int duplex_interlace; /* different models interlace sides differently */ + int has_MS_dropout; /* dropout color specified in mode select data */ + int has_SW_dropout; /* dropout color specified in set window data */ + int window_vid; /* some models want different vendor ID in set window */ + int ghs_in_rs; - SANE_Range adf_width_range; - SANE_Range adf_height_range; - SANE_Range x_range; - SANE_Range y_range; + int reverse_by_mode[6]; /* mode specific */ + /* --------------------------------------------------------------------- */ + /* changeable SANE_Option structs provide our interface to frontend. */ + /* some options require lists of strings or numbers, we keep them here */ + /* instead of in global vars so that they can differ for each scanner */ + /* these are loaded in init_options(), but may be changed by other funcs */ + + /* long array of option structs */ + SANE_Option_Descriptor opt[NUM_OPTIONS]; + /*mode group*/ + SANE_String_Const mode_list[7]; + SANE_String_Const source_list[5]; + + SANE_Int x_res_list[17]; + SANE_Int y_res_list[17]; SANE_Range x_res_range; SANE_Range y_res_range; - SANE_Range x_grey_res_range; - SANE_Range y_grey_res_range; - - SANE_String_Const vpd_mode_list[7]; - SANE_String_Const compression_mode_list[9]; - SANE_Int x_res_list[17]; /* range of x and y resolution */ - SANE_Int y_res_list[17]; /* if scanner has only fixed resolutions */ - SANE_Int x_res_list_grey[17]; - SANE_Int y_res_list_grey[17]; + /*geometry group*/ + SANE_Range tl_x_range; + SANE_Range tl_y_range; + SANE_Range br_x_range; + SANE_Range br_y_range; + SANE_Range paper_x_range; + SANE_Range paper_y_range; - SANE_Range threshold_range; + /*enhancement group*/ SANE_Range brightness_range; + SANE_Range threshold_range; SANE_Range contrast_range; - /* User settable values (usually mirrored in SANE options) */ - /* --------------------------------------------------------------------- */ + /*ipc group*/ - int duplex_mode; /* Use DUPLEX_* constants below */ - int resolution_x; /* X resolution in dpi */ - int resolution_y; /* Y resolution in dpi */ - int resolution_linked; /* When true, Y resolution is set whenever */ - /* X resolution gets changed. true initially, */ - /* becomes false if Y resolution is ever */ - /* set independently */ - int top_left_x; /* The desired size of the scan, in */ - int top_left_y; /* mm/2^16. Notice that there's a second */ - int bottom_right_x; /* group of these with the "rounded" */ - int bottom_right_y; /* prefix, below. */ - - /* A note on the scan are size (topLeft/bottomRight) values. When the - * user (the front-end) sets one of these, we change the above values - * and immediately calculate from it the scan height and width in - * 1/1200 inches (the internal scanner unit). Depending on the scan - * mode, depth, etc., other rules may be used to change the value, - * e.g. in 1bpp scans, the width is always increased until each scan - * line has a full number of bytes. - * - * The results of these calculations are the "top_margin", "left_margin", - * "scan_width", and "scan_height" values below. In addition, these - * values are now converted back into SANE units (1/2^16mm) and stored - * in roundedTopLeft... and roundedBottomRight... - when the front-end - * _sets_ the scan area size, the topLeft... and bottomRight... values - * will be set, but when the front-end _gets_ the scan area size, - * the rounded values will be returned. - * - * By keeping the original values instead of rounding them "in place", - * we can revise our rounded values later. Assume the resolution is set - * to 75dpi, and then the scan size is set - it will be rounded to - * the nearest 1/75 inch. Later the resolution is changed to 600dpi, - * making a better approximation of the original desired size possible. - * - * All these calculations are done within the "calculateDerivedValues" - * function. - */ - - int page_width; /* paper width and height may influence the */ - int page_height; /* way the ADF operates - in 1/1200 inch */ - - int output_depth; /* How many bits per pixel the user wants. */ - int scanner_depth; /* How many bpp the scanner will send. */ - - /* A note on "depth" values. Many colour scanners require you to use - * the value "8" for depth if you're doing colour scans even if they - * deliver 24. The front-end, when it calls "sane_get_parameters", will - * expect to see the value 8 as well. However, internally, we use - * scanner_depth as the real number of bits sent per pixel, which is - * obvoiusly 24 for coulour scans. - * - * output_depth uses the same logic and normally both values would be - * identical; however it may be desirable to implement, say, a 4-bit - * grayscale mode in the backend even if the scanner only supports - * 8-bit grayscale. In that case, the backend would to the reduction - * "on the fly", and output_depth would be 4, while scanner_depth is 8. - */ - - int color_mode; /* Color/Gray/..., use MODE_* constants */ - int use_adf; /* Whether the ADF should be used or not. */ - int use_temp_file; /* Whether to use a temp file for duplex. */ - int mirror; /* Whether to mirror scan area for rear side */ - int lamp_color; /* lamp color to use for monochrome scans */ - int green_offset; /* color tuning - green scan line offset */ - int blue_offset; /* color tuning - blue scan line offset */ - - - - /* Derived values (calculated by calculateDerivedValues from the above) */ - /* --------------------------------------------------------------------- */ - - int rounded_top_left_x; /* Same as topLeft... and bottomRight..., */ - int rounded_top_left_y; /* but "rounded" to the nearest 1/1200 */ - int rounded_bottom_right_x; /* value, i.e. these are the values really */ - int rounded_bottom_right_y; /* used for scanning. */ - int top_margin; /* Top margin in 1/1200 inch */ - int left_margin; /* Left margin in 1/1200 inch */ - int scan_width; /* Width of scan in 1/1200 inch */ - int scan_height; /* Height of scan in 1/1200 inch */ - int scan_width_pixels; /* Pixels per scan line for this job */ - int scan_height_pixels; /* Number of lines in this job */ - int bytes_per_scan_line; /* Number of bytes per line in this job */ - - /* Operational values (contain internal status information etc.) */ - /* --------------------------------------------------------------------- */ - - int default_pipe_r; /* Pipe between reader process and backend. */ - int default_pipe_w; /* Pipe between reader process and backend. */ - int duplex_pipe_r; /* Additional pipe for duplex scans. */ - int duplex_pipe_w; /* Additional pipe for duplex scans. */ - int reader_pid; /* Process ID of the reader process. */ - - int reverse; /* Whether to reverse the image. Not user */ - /* settable but required internally. */ - - int i_transfer_length; /* needed when the scanner returns */ - /* compressed data and size of return data */ - /* is unknown in advance */ - - - - /* This replaced the former "scanning" flag. object_count represents the - * number of objects acquired (or being acquired) by the scanner which - * haven't been read. Thus, an object_count of != 0 means the scanner is - * busy. If the scanner is scanning the first page, it's 1; if the scanner - * is scanning the second page of a duplex scan, it's 2. - */ - int object_count; - - /* This is set to "true" by sane_read when it encounters the end of a data - * stream. - */ - int eof; + /*advanced group*/ + SANE_String_Const do_color_list[5]; + SANE_String_Const lamp_color_list[5]; + SANE_Range sleep_time_range; + SANE_Range duplex_offset_range; + SANE_Range green_offset_range; + SANE_Range blue_offset_range; - unsigned char *buffer; /* Buffer used for SCSI transfers. */ - unsigned int scsi_buf_size; /* Max amount of data in one SCSI call. */ + /* --------------------------------------------------------------------- */ + /* changeable vars to hold user input. modified by SANE_Options above */ - /** these can be set */ + /*mode group*/ + int mode; /*color,lineart,etc*/ + int source; /*fb,adf front,adf duplex,etc*/ + int resolution_x; /* X resolution in dpi */ + int resolution_y; /* Y resolution in dpi */ + + /*geometry group*/ + /* The desired size of the scan, all in 1/1200 inch */ + int tl_x; + int tl_y; + int br_x; + int br_y; + int page_width; + int page_height; + /*enhancement group*/ int brightness; int threshold; int contrast; + int rif; - int bitorder; - int compress_type; - int compress_arg; - int vendor_id_code; - int gamma; - int outline; - int emphasis; - int auto_sep; - int mirroring; - int var_rate_dyn_thresh; - int white_level_follow; - int gradation; - int smoothing_mode; - int filtering; - int background; - int matrix2x2; - int matrix3x3; - int matrix4x4; - int matrix5x5; - int noise_removal; - int paper_orientation; - int paper_selection; - int paper_size; - int dtc_threshold_curve; - int dtc_selection; - - int subwindow_list; - /***** end of "set window" terms *****/ + /*ipc group*/ + int gamma; /* not currently user settable */ + /*advanced group*/ int dropout_color; + int lamp_color; + int sleep_time; + int duplex_offset; + int green_offset; + int blue_offset; + int use_temp_file; + + /* --------------------------------------------------------------------- */ + /* values which are derived from setting the options above */ + /* the user never directly modifies these */ + + /* this is defined in sane spec as a struct containing: + SANE_Frame format; + SANE_Bool last_frame; + SANE_Int lines; + SANE_Int depth; ( binary=1, gray=8, color=8 (!24) ) + SANE_Int pixels_per_line; + SANE_Int bytes_per_line; + */ + SANE_Parameters params; + + /* --------------------------------------------------------------------- */ + /* values which are set by scanning functions to keep track of pages, etc */ + int started; + int img_count; /* how many 'sides' delivered */ + + /* total to read/write */ + int bytes_tot[2]; + + /* how far we have read */ + int bytes_rx[2]; + int lines_rx[2]; /*only used by 3091*/ + + /* how far we have written */ + int bytes_tx[2]; - SANE_Bool use_imprinter; - int imprinter_direction; - SANE_Word imprinter_y_offset; - SANE_Char imprinter_string[max_imprinter_string_length]; - int imprinter_ctr_init; - int imprinter_ctr_step; - int imprinter_ctr_dir; + unsigned char * buffers[2]; + int fds[2]; - SANE_Int sleep_time; + /* --------------------------------------------------------------------- */ + /* values which used by the command and data sending functions (scsi/usb)*/ + int fd; /* The scanner device file descriptor. */ + unsigned char rs_buffer[RS_return_size]; + + /* --------------------------------------------------------------------- */ + /* values which are used by the get hardware status command */ + time_t last_ghs; + + int hw_top; + int hw_A3; + int hw_B4; + int hw_A4; + int hw_B5; + + int hw_hopper; + int hw_omr; + int hw_adf_open; + + int hw_sleep; + int hw_send_sw; + int hw_manual_feed; + int hw_scan_sw; + + int hw_function; + + int hw_ink_empty; + int hw_double_feed; + + int hw_error_code; + int hw_skew_angle; + int hw_ink_remain; + + int hw_duplex_sw; + int hw_density_sw; }; -/** scan only front page */ -#define DUPLEX_FRONT 1 -/** scan only reverse page */ -#define DUPLEX_BACK 2 -/** scan both sides and return them in two different image acquisition operations */ -#define DUPLEX_BOTH 3 - -#define MODEL_FORCE 0 -/** Fujitsu M3091DCd (300x600dpi, color, duplex, ADF-only) */ -#define MODEL_3091 1 -/** Fujitsu M3096 (400x400dpi grayscale, simplex, FB and optional ADF) */ -#define MODEL_3096 2 -/** 3093 */ -#define MODEL_3093 4 -/** 4097 */ -#define MODEL_4097 5 -/** fi-???? */ -#define MODEL_FI 6 -#define MODEL_3097 7 -/** Fujitsu M3092DCd (300x600dpi, color, duplex, FB and ADF) */ -#define MODEL_3092 8 -/** ScanPartner fi-4120 and ScanParter fi-4220 scsi/usb */ -#define MODEL_FI4x20 9 - -/* A note regarding the MODEL... constants. There's a place in - * identifyScanner() where the INQUIRY data is parsed and the model - * set accordingly. Now while there is, for example, only one 3091 - * model (called M3091DCd), there may be different sub-models (e.g. - * the 1000abc and the 1000xyz). - * - * It is suggested that a new MODEL... type be introduced for these if - * there are significant differences in capability that have to be - * treated differently in the backend. For example, if the "abc" model - * has an optional ADF and the "xyz" model has the ADF built-in, we - * don't need to distinguish these models, and it's sufficient to set - * the "has_adf" variable accordingly. - * - * The same would apply if two sub-models behave in the same way but - * one of them is faster, or has some exotic extra capability that we - * don't support anyway. - * - * If, on the other hand, one model supports a different resolution, - * page size, different scan parameters and modes, and such, then it's - * probably necessary to create a new MODEL... constant. - * - * It may also be feasible to introduce a "subModel" member in the - * structure above for those cases where subtle, but important, - * differences exist - e.g. one model is 100% the same as another but - * supports twice the resolution. - */ +#define CONNECTION_SCSI 0 /* SCSI interface */ +#define CONNECTION_USB 1 /* USB interface */ + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 -/** Black-and-White (Line Art) - depth always 1 */ -#define MODE_LINEART 1 -/** Black-and-White (Halftone) - depth always 1 */ -#define MODE_HALFTONE 2 -/** Grayscale */ -#define MODE_GRAYSCALE 3 -/** RGB Color */ -#define MODE_COLOR 4 -/** Binary RGB Color*/ -#define MODE_BIN_COLOR 5 -/** half-tones RGB Color*/ -#define MODE_HALFTONE_COLOR 6 +#define SOURCE_FLATBED 0 +#define SOURCE_ADF_FRONT 1 +#define SOURCE_ADF_BACK 2 +#define SOURCE_ADF_DUPLEX 3 + +/* these are same as scsi data to make code easier */ +#define MODE_LINEART WD_comp_LA +#define MODE_HALFTONE WD_comp_HT +#define MODE_GRAYSCALE WD_comp_GS +#define MODE_COLOR_LINEART WD_comp_CL +#define MODE_COLOR_HALFTONE WD_comp_CH +#define MODE_COLOR WD_comp_CG + +/* these are same as scsi data to make code easier */ +#define COLOR_DEFAULT MSEL_dropout_DEFAULT +#define COLOR_RED MSEL_dropout_RED +#define COLOR_GREEN MSEL_dropout_GREEN +#define COLOR_BLUE MSEL_dropout_BLUE + +#define COLOR_INTERLACE_NONE 0 +#define COLOR_INTERLACE_3091 1 +#define COLOR_INTERLACE_BGR 2 +#define COLOR_INTERLACE_RRGGBB 3 +#define DUPLEX_INTERLACE_NONE 0 +#define DUPLEX_INTERLACE_3091 1 + +#define GHS_TIME 0 /* seconds passed before calling GHS */ /* ------------------------------------------------------------------------- */ #define MM_PER_INCH 25.4 -#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)) -#define mmToIlu(mm) ((mm) / length_quant) -#define iluToMm(ilu) ((ilu) * length_quant) -#define FUJITSU_CONFIG_FILE "fujitsu.conf" -#define READ_MODE_PASS 1 -#define READ_MODE_RRGGBB 2 -#define READ_MODE_3091RGB 3 -#define READ_MODE_BGR 4 +#define MM_PER_UNIT_UNFIX SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0)) +#define MM_PER_UNIT_FIX SANE_FIX(SANE_UNFIX(SANE_FIX(MM_PER_INCH / 1200.0))) +#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX(number * MM_PER_UNIT_UNFIX) +#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) / MM_PER_UNIT_UNFIX -#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) * 1200 / MM_PER_INCH -#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX(number * MM_PER_INCH / 1200) +#define FUJITSU_CONFIG_FILE "fujitsu.conf" #ifndef PATH_MAX # define PATH_MAX 1024 @@ -441,7 +415,6 @@ /* ------------------------------------------------------------------------- */ - SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_get_devices (const SANE_Device *** device_list, @@ -453,7 +426,7 @@ SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp); -const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle handle, +const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option); SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, @@ -476,36 +449,42 @@ /* ------------------------------------------------------------------------- */ -static void do_inquiry (struct fujitsu *s); - -static SANE_Status attachScanner (const char *devicename, - struct fujitsu **devp); - -static SANE_Status scsi_sense_handler (int scsi_fd, u_char * result, void *arg); - -static int identify_scanner (struct fujitsu *s); +static SANE_Status find_scanners (void); -static int -do_cmd (Fujitsu_Connection_Type connection, int fd, unsigned char *cmd, - int cmd_len, unsigned char *out, size_t req_out_len, - size_t *res_out_len); +static SANE_Status attach_one_scsi (const char *name); +static SANE_Status attach_one_usb (const char *name); +static SANE_Status attach_one (const char *devicename, int connType); -static int do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, - unsigned char *out, size_t req_out_len, - size_t *res_out_len); +static SANE_Status connect_fd (struct fujitsu *s); +static SANE_Status disconnect_fd (struct fujitsu *s); -static int -do_usb_cmd (int fd, unsigned char *cmd, - int cmd_len, unsigned char *out, size_t req_out_len, - size_t *res_out_len); - -static void hexdump (int level, char *comment, unsigned char *p, int l); +static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg); +static SANE_Status init_inquire (struct fujitsu *s); +static SANE_Status init_vpd (struct fujitsu *s); +static SANE_Status init_model (struct fujitsu *s); static SANE_Status init_options (struct fujitsu *scanner); -static int grab_scanner (struct fujitsu *s); - -static int free_scanner (struct fujitsu *s); +static SANE_Status +do_cmd(struct fujitsu *s, int runRS, int shortTime, + unsigned char * cmdBuff, size_t cmdLen, + unsigned char * outBuff, size_t outLen, + unsigned char * inBuff, size_t inLen +); + +static SANE_Status +do_scsi_cmd(struct fujitsu *s, int runRS, int shortTime, + unsigned char * cmdBuff, size_t cmdLen, + unsigned char * outBuff, size_t outLen, + unsigned char * inBuff, size_t inLen +); + +static SANE_Status +do_usb_cmd(struct fujitsu *s, int runRS, int shortTime, + unsigned char * cmdBuff, size_t cmdLen, + unsigned char * outBuff, size_t outLen, + unsigned char * inBuff, size_t inLen +); static int wait_scanner (struct fujitsu *s); @@ -513,71 +492,34 @@ static SANE_Status do_cancel (struct fujitsu *scanner); -static SANE_Status do_reset (struct fujitsu *scanner); - -/*static int objectDischarge (struct fujitsu *s);*/ +static SANE_Status scanner_control (struct fujitsu *s, int function); -static int fujitsu_set_sleep_mode(struct fujitsu *s); +static SANE_Status mode_select_dropout(struct fujitsu *s); -static int set_mode_params (struct fujitsu *s); +static SANE_Status set_sleep_mode(struct fujitsu *s); -static int imprinter(struct fujitsu *s); +int get_current_side (struct fujitsu *s); -static int fujitsu_send(struct fujitsu *s); - -static int setWindowParam (struct fujitsu *s); - -static size_t maxStringSize (const SANE_String_Const strings[]); +static int set_window (struct fujitsu *s); static int start_scan (struct fujitsu *s); -static int reader_process (void *scanner); - -static unsigned int reader3091ColorDuplex (struct fujitsu *scanner, FILE * fd, - FILE * fd2); -static unsigned int reader3091ColorSimplex (struct fujitsu *scanner, - FILE * fd); -static unsigned int reader3091GrayDuplex (struct fujitsu *scanner, FILE * fd, - FILE * fd2); -static void convert_bgr_to_rgb(struct fujitsu *scanner, unsigned char * buffptr, unsigned int length); - -static void convert_rrggbb_to_rgb(struct fujitsu *scanner, unsigned char * buffptr, unsigned int length); - -static unsigned int reader_gray_duplex_sequential (struct fujitsu *scanner, - FILE * fd, FILE * fd2); -static unsigned int reader_gray_duplex_alternate (struct fujitsu *scanner, - FILE * fd, FILE * fd2); - -static unsigned int reader_duplex_sequential (struct fujitsu *scanner, - FILE * fd, FILE * fd2); -static unsigned int reader_duplex_alternate (struct fujitsu *scanner, - FILE * fd, FILE * fd2); -static unsigned int reader_simplex (struct fujitsu *scanner, FILE * fd, - int i_window_id); - -static unsigned int reader_generic_passthrough (struct fujitsu *scanner, - FILE * fd, int i_window_id); - -static int read_large_data_block (struct fujitsu *s, - unsigned char *buffer, unsigned int length, - int i_window_id, - unsigned int *i_data_read); - -static SANE_Status attachOne (const char *name); - -static int modelMatch (const char *product); +static SANE_Status read_from_3091duplex(struct fujitsu *s); +static SANE_Status read_from_scanner(struct fujitsu *s, int side); -static void setDefaults3091 (struct fujitsu *scanner); -static void setDefaults3096 (struct fujitsu *scanner); +static SANE_Status copy_3091(struct fujitsu *s, unsigned char * buf, int len, int side); +static SANE_Status copy_buffer(struct fujitsu *s, unsigned char * buf, int len, int side); -static SANE_Status setMode3091 (struct fujitsu *scanner, int mode); -static SANE_Status setMode3096 (struct fujitsu *scanner, int mode); +static SANE_Status read_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side); static void calculateDerivedValues (struct fujitsu *scanner); -static int makeTempFile (void); +static SANE_Status setup_buffers (struct fujitsu *s); + static SANE_Status get_hardware_status (struct fujitsu *s); -static void fujitsu_set_standard_size (SANE_Handle handle); +static void hexdump (int level, char *comment, unsigned char *p, int l); + +static size_t maxStringSize (const SANE_String_Const strings[]); #endif /* FUJITSU_H */ diff -uNr sane-backends-1.0.17/backend/fujitsu-scsi.h sane-backends-1.0.18/backend/fujitsu-scsi.h --- sane-backends-1.0.17/backend/fujitsu-scsi.h 2004-02-21 15:39:31.000000000 +0100 +++ sane-backends-1.0.18/backend/fujitsu-scsi.h 2006-06-04 14:48:17.000000000 +0200 @@ -9,6 +9,16 @@ /****************************************************/ +#define USB_COMMAND_CODE 0x43 +#define USB_COMMAND_LEN 0x1F +#define USB_COMMAND_OFFSET 0x13 +#define USB_COMMAND_TIME 10000 +#define USB_DATA_TIME 10000 +#define USB_STATUS_CODE 0x53 +#define USB_STATUS_LEN 0x0D +#define USB_STATUS_OFFSET 0x09 +#define USB_STATUS_TIME 10000 + /*static inline void */ static void setbitfield (unsigned char *pageaddr, int mask, int shift, int val) @@ -99,26 +109,31 @@ #define SCAN 0x1b #define IMPRINTER 0xc1 #define HW_STATUS 0xc2 -#define RESET_UNIT 0xf1 +#define SCANNER_CONTROL 0xf1 /* ==================================================================== */ - +/* static unsigned char reserve_unitC[] = { RESERVE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk reserve_unitB = { reserve_unitC, sizeof (reserve_unitC) }; -/* ==================================================================== */ - static unsigned char release_unitC[] = { RELEASE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk release_unitB = { release_unitC, sizeof (release_unitC) }; - +*/ /* ==================================================================== */ -static unsigned char reset_unitC[] = - { RESET_UNIT, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static scsiblk reset_unitB = { reset_unitC, sizeof (reset_unitC) }; +static unsigned char scanner_controlC[] = + { SCANNER_CONTROL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static scsiblk scanner_controlB = { scanner_controlC, sizeof (scanner_controlC) }; + +#define set_SC_function(icb, val) setbitfield(icb + 1, 7, 0, val) +#define SC_function_cancel 0x04 +#define SC_function_lamp_on 0x05 +#define SC_function_lamp_off 0x03 +#define SC_function_lamp_normal 0x06 +#define SC_function_lamp_saving 0x07 /* ==================================================================== */ @@ -139,13 +154,16 @@ #define get_IN_response_format(in) getbitfield(in + 0x03, 0x07, 0) #define IN_recognized 0x02 #define get_IN_additional_length(in) in[0x04] -#define get_IN_vendor(in, buf) strncpy(buf, in + 0x08, 0x08) -#define get_IN_product(in, buf) strncpy(buf, in + 0x10, 0x010) -#define get_IN_version(in, buf) strncpy(buf, in + 0x20, 0x04) +#define get_IN_vendor(in, buf) strncpy(buf, (char *)in + 0x08, 0x08) +#define get_IN_product(in, buf) strncpy(buf, (char *)in + 0x10, 0x010) +#define get_IN_version(in, buf) strncpy(buf, (char *)in + 0x20, 0x04) #define get_IN_raster(in) getnbyte (in+0x2A, 2) /* offset between colors */ + +/* these two only in some scanners */ +#define get_IN_duplex_3091(in) getnbyte (in+0x2D, 1) /* duplex available */ #define get_IN_frontback(in) getnbyte (in+0x2E, 2) /* offset between front and back for duplex */ -#define get_IN_duplex_3091(in) getnbyte (in+0x2D, 1) /* duplex available */ +/* the VPD response */ #define get_IN_page_length(in) getnbyte(in + 0x04, 1) #define get_IN_basic_x_res(in) getnbyte(in + 0x05, 2) #define get_IN_basic_y_res(in) getnbyte(in + 0x07, 2) @@ -180,23 +198,37 @@ #define get_IN_monochrome_rgb(in) getbitfield(in+0x1c, 1, 5) #define get_IN_half_tone_rgb(in) getbitfield(in+0x1c, 1, 6) #define get_IN_multilevel_rgb(in) getbitfield(in+0x1c, 1, 7) + +/* vendor unique section */ #define get_IN_operator_panel(in) getbitfield(in+0x20, 1, 1) #define get_IN_barcode(in) getbitfield(in+0x20, 1, 2) -#define get_IN_endorser(in) getbitfield(in+0x20, 1, 3) +#define get_IN_imprinter(in) getbitfield(in+0x20, 1, 3) #define get_IN_duplex(in) getbitfield(in+0x20, 1, 4) -#define get_IN_trancepareny(in) getbitfield(in+0x20, 1, 5) +#define get_IN_transparency(in) getbitfield(in+0x20, 1, 5) #define get_IN_flatbed(in) getbitfield(in+0x20, 1, 6) #define get_IN_adf(in) getbitfield(in+0x20, 1, 7) + +#define get_IN_adbits(in) getbitfield(in+0x21, 0x0f, 0) #define get_IN_buffer_bytes(in) getnbyte(in + 0x22, 4) -#define get_IN_has_set_subwindow(in) (getnbyte(in+0x2a, 2)) & 1 -#define get_IN_has_imprinter(in) (getnbyte(in+0x2a, 2)) & 2 -#define get_IN_has_hw_status(in) (getnbyte(in+0x2a, 2)) & 4 + +/* more stuff here (std supported commands) */ + +#define get_IN_has_subwindow(in) getbitfield(in+0x2b, 1, 0) +#define get_IN_has_endorser(in) getbitfield(in+0x2b, 1, 1) +#define get_IN_has_hw_status(in) getbitfield(in+0x2b, 1, 2) +#define get_IN_has_scanner_ctl(in) getbitfield(in+0x31, 1, 1) + #define get_IN_brightness_steps(in) getnbyte(in+0x52, 1) #define get_IN_threshold_steps(in) getnbyte(in+0x53, 1) #define get_IN_contrast_steps(in) getnbyte(in+0x54, 1) -#define get_IN_num_gamma(in) getbitfield(in+0x57, 15, 4) + +#define get_IN_num_dither_internal(in) getbitfield(in+0x56, 15, 4) +#define get_IN_num_dither_download(in) getbitfield(in+0x56, 15, 0) + +#define get_IN_num_gamma_internal(in) getbitfield(in+0x57, 15, 4) #define get_IN_num_gamma_download(in) getbitfield(in+0x57, 15, 0) -#define get_IN_ipc_bw_reverse(in) getbitfield(in+0x58, 1, 7) + +#define get_IN_ipc_bw_rif(in) getbitfield(in+0x58, 1, 7) #define get_IN_ipc_auto1(in) getbitfield(in+0x58, 1, 6) #define get_IN_ipc_auto2(in) getbitfield(in+0x58, 1, 5) #define get_IN_ipc_outline_extraction(in) getbitfield(in+0x58, 1, 4) @@ -204,8 +236,10 @@ #define get_IN_ipc_auto_separation(in) getbitfield(in+0x58, 1, 2) #define get_IN_ipc_mirroring(in) getbitfield(in+0x58, 1, 1) #define get_IN_ipc_white_level_follow(in) getbitfield(in+0x58, 1, 0) + #define get_IN_ipc_subwindow(in) getbitfield(in+0x59, 1, 7) #define get_IN_ipc_error_diffusion(in) getbitfield(in+0x59, 1, 6) + #define get_IN_compression_MH(in) getbitfield(in+0x5a, 1, 7) #define get_IN_compression_MR(in) getbitfield(in+0x5a, 1, 6) #define get_IN_compression_MMR(in) getbitfield(in+0x5a, 1, 5) @@ -213,9 +247,10 @@ #define get_IN_compression_JPG_BASE(in) getbitfield(in+0x5a, 1, 3) #define get_IN_compression_JPG_EXT(in) getbitfield(in+0x5a, 1, 2) #define get_IN_compression_JPG_INDEP(in) getbitfield(in+0x5a, 1, 1) -#define get_IN_imprinter(in) getbitfield(in+0x5c, 1, 7) -#define get_IN_imprinter_stamp(in) getbitfield(in+0x5c, 1, 6) -#define get_IN_imprinter_electrical(in) getbitfield(in+0x5c, 1, 5) + +#define get_IN_imprinter2(in) getbitfield(in+0x5c, 1, 7) +#define get_IN_imprinter2_stamp(in) getbitfield(in+0x5c, 1, 6) +#define get_IN_imprinter2_electrical(in) getbitfield(in+0x5c, 1, 5) /* ==================================================================== */ @@ -232,16 +267,6 @@ static scsiblk set_windowB = { set_windowC, sizeof (set_windowC) }; #define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) - /* With the fi-series scanners, we have to use a 12-byte command - * instead of a 10-byte command when communicating via USB. This - * may be a firmware bug. */ -static unsigned char set_usb_windowC[] = - { SET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 }; -/* opcode, lun, _____4 X reserved____, transfer length, control byte */ -static scsiblk set_usb_windowB = { set_usb_windowC, sizeof (set_usb_windowC) }; -#define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) - /* ==================================================================== */ static unsigned char object_positionC[] = @@ -252,7 +277,7 @@ #define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val) #define OP_Discharge 0x00 -#define OP_Feed 0x01 +#define OP_Feed 0x01 /* ==================================================================== */ @@ -284,13 +309,14 @@ #define S_DQ_Reg3 0x03 #define set_S_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3) +/* static unsigned char send_imprinterC[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static scsiblk send_imprinterB = {send_imprinterC, sizeof(send_imprinterC)}; - +*/ /* imprinter counter * 0 = increase counter * 1 = decrease counter @@ -330,7 +356,7 @@ */ /* ==================================================================== */ - +/* static unsigned char imprinterC[] = { IMPRINTER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk imprinterB = { imprinterC, sizeof (imprinterC) }; @@ -340,7 +366,7 @@ static unsigned char imprinter_descC[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk imprinter_descB = {imprinter_descC, sizeof(imprinter_descC) }; - +*/ /* enable/disable imprinter printing*/ #define set_IMD_enable(sb, val) setbitfield(sb + 0x01, 1, 7, val) #define IMD_enable 0 @@ -381,75 +407,109 @@ #define set_MSEL_xfer_length(sb, val) sb[0x04] = (unsigned char)val - -static unsigned char mode_select_headerC[] = { - 0x00, 0x00, 0x00, 0x00 -}; -static scsiblk mode_select_headerB = { - mode_select_headerC, sizeof (mode_select_headerC) -}; - - - /* With the fi-series scanners, we have to use a 10-byte header - * instead of a 4-byte header when communicating via USB. This - * may be a firmware bug. */ -static unsigned char mode_select_usb_headerC[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -static scsiblk mode_select_usb_headerB = { - mode_select_usb_headerC, sizeof (mode_select_usb_headerC) -}; - - -static unsigned char mode_select_parameter_blockC[] = { - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x00 -}; -static scsiblk mode_select_parameter_blockB = - { mode_select_parameter_blockC, sizeof (mode_select_parameter_blockC) }; - -#define set_MSEL_len(sb, len) putnbyte(sb + 0x01, len, 1) -/* - * PageCode: +/* combined 4 byte header and 8 byte page + * PageCodes: (most scanners know a few of these) * 0x34 = Sleep mode * 0x35 = ADF duplex reading transfer mode * 0x36 = All sorts of device controls switching * 0x37 = Backing switch control * 0x38 = Double feed detection * 0x39 = Drop out color - * 0x3c = Paper width, paper length detection, deskew + * 0x3c = Auto paper size detection * 0x3d = Lamp light timer set - * 0x3e = Special-purpose paper detection mode + * 0x3e = Detect job separation sheet + * there is also possibly a 'descriptor block' + * and a 'vendor-specific block' + * fujitsu's seem not to use these two */ -#define set_MSEL_pagecode(sb, val) setbitfield(sb, 0x3f, 0, val) -#define MSEL_sleep 0x34 -#define MSEL_transfer_mode 0x35 -#define MSEL_dropout_color 0x39 +static unsigned char mode_select_sleepC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_sleepB = { + mode_select_sleepC, sizeof (mode_select_sleepC) +}; /* time until the device internal power supply switches to Sleep Mode * 0 = Default (15 min) * 1 = 1 min * 2 = 2 min * 0x3c ..0xff = 60 min */ -#define set_MSEL_sleep_mode(sb, val) setbitfield(sb + 0x02, 0x0f, 0, val) - +#define set_MSEL_sleep_mode(sb, val) sb[0x06]=val +/* +static unsigned char mode_select_duplexC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x35, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_duplexB = { + mode_select_duplexC, sizeof (mode_select_duplexC) +}; +*/ /* adf duplex reading transfer method * 0 = front side - back side sequential transfer * 1 = front side - back side alternate transfer */ #define set_MSEL_transfer_mode(sb, val) setbitfield(sb + 0x02, 0x01, 0, val) +/* +static unsigned char mode_select_randC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x36, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_randB = { + mode_select_randC, sizeof (mode_select_randC) +}; -#define set_MSEL_dropout_front(sb, val) setbitfield(sb + 0x02, 0x0f, 0, val) -#define set_MSEL_dropout_back(sb, val) setbitfield(sb + 0x02, 0x0f, 4, val) +static unsigned char mode_select_backingC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x37, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_backingB = { + mode_select_backingC, sizeof (mode_select_backingC) +}; +*/ +/* +static unsigned char mode_select_dfeedC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x38, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_dfeedB = { + mode_select_dfeedC, sizeof (mode_select_dfeedC) +}; +*/ +/*byte 0x06 is bitmask controlling monochrome color*/ +static unsigned char mode_select_dropoutC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x39, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_dropoutB = { + mode_select_dropoutC, sizeof (mode_select_dropoutC) +}; +#define set_MSEL_dropout_front(sb, val) setbitfield(sb + 0x06, 0x0f, 0, val) +#define set_MSEL_dropout_back(sb, val) setbitfield(sb + 0x06, 0x0f, 4, val) #define MSEL_dropout_DEFAULT 0 #define MSEL_dropout_GREEN 8 #define MSEL_dropout_RED 9 #define MSEL_dropout_BLUE 11 #define MSEL_dropout_CUSTOM 12 -/* ==================================================================== */ + +/*bytes 0x06-07 and 0x09 control paper size detection*/ +/* +static unsigned char mode_select_autoC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_autoB = { + mode_select_autoC, sizeof (mode_select_autoC) +}; +static unsigned char mode_select_lampC[] = { + 0x00, 0x00, 0x00, 0x00, + 0x3D, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static scsiblk mode_select_lampB= { + mode_select_lampC, sizeof (mode_select_lampC) +}; +*/ /* ==================================================================== */ #if 0 @@ -478,37 +538,44 @@ #define set_HW_allocation_length(sb, len) putnbyte(sb + 0x07, len, 2) -#define get_HW_B5_present(in) getbitfield(in+0x02, 1, 0) -#define get_HW_A4_present(in) getbitfield(in+0x02, 1, 1) -#define get_HW_B4_present(in) getbitfield(in+0x02, 1, 2) -#define get_HW_A3_present(in) getbitfield(in+0x02, 1, 3) -#define get_HW_adf_empty(in) getbitfield(in+0x03, 1, 7) -#define get_HW_omr(in) getbitfield(in+0x03, 1, 6) -#define get_HW_adfc_open(in) getbitfield(in+0x03, 1, 5) -#define get_HW_sleep(in) getbitfield(in+0x04, 1, 7) -#define get_HW_manual_feed(in) getbitfield(in+0x04, 1, 1) -#define get_HW_start_button(in) getbitfield(in+0x04, 1, 0) -#define get_HW_ink_empty(in) getbitfield(in+0x06, 1, 7) -#define get_HW_dfeed_detected(in) getbitfield(in+0x06, 1, 0) -#define get_HW_skew_angle(in) getnbyte(in+0x08, 2) +#define get_HW_top(in) getbitfield(in+0x02, 1, 7) +#define get_HW_A3(in) getbitfield(in+0x02, 1, 3) +#define get_HW_B4(in) getbitfield(in+0x02, 1, 2) +#define get_HW_A4(in) getbitfield(in+0x02, 1, 1) +#define get_HW_B5(in) getbitfield(in+0x02, 1, 0) + +#define get_HW_hopper(in) !getbitfield(in+0x03, 1, 7) +#define get_HW_omr(in) getbitfield(in+0x03, 1, 6) +#define get_HW_adf_open(in) getbitfield(in+0x03, 1, 5) + +#define get_HW_sleep(in) getbitfield(in+0x04, 1, 7) +#define get_HW_send_sw(in) getbitfield(in+0x04, 1, 2) +#define get_HW_manual_feed(in) getbitfield(in+0x04, 1, 1) +#define get_HW_scan_sw(in) getbitfield(in+0x04, 1, 0) +#define get_HW_function(in) getbitfield(in+0x05, 0x0f, 0) + +#define get_HW_ink_empty(in) getbitfield(in+0x06, 1, 7) +#define get_HW_double_feed(in) getbitfield(in+0x06, 1, 0) + +#define get_HW_error_code(in) in[0x07] + +#define get_HW_skew_angle(in) getnbyte(in+0x08, 2) + +#define get_HW_ink_remain(in) in[0x0a] /* ==================================================================== */ /* We use the same structure for both SET WINDOW and GET WINDOW. */ -static unsigned char window_parameter_data_blockC[] = { +static unsigned char window_descriptor_headerC[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reserved */ 0x00, 0x00, /* Window Descriptor Length */ }; -static scsiblk window_parameter_data_blockB = - { window_parameter_data_blockC, sizeof (window_parameter_data_blockC) }; - +static scsiblk window_descriptor_headerB= + { window_descriptor_headerC, sizeof (window_descriptor_headerC) }; #define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2) -#define used_WDB_size 0x60 -#define max_WDB_size 0xff - /* ==================================================================== */ static unsigned char window_descriptor_blockC[] = { @@ -617,7 +684,9 @@ #define WD_comp_LA 0 #define WD_comp_HT 1 #define WD_comp_GS 2 -#define WD_comp_RC 5 +#define WD_comp_CL 3 +#define WD_comp_CH 4 +#define WD_comp_CG 5 /* 0x1a - Depth * 3091 - use 0x01 for b/w or 0x08 for gray/color @@ -694,7 +763,7 @@ * 3091 - use 0xc0 * 3096 - use 0xc0 */ - 0xC0, + 0x00, #define set_WD_vendor_id_code(sb, val) sb[0x28] = val #define get_WD_vendor_id_code(sb) sb[0x28] @@ -757,10 +826,10 @@ #define get_WD_mirroring(sb) getbitfield(sb + 0x2d, 1, 7) #define set_WD_lamp_color(sb, val) sb[0x2d] = val #define get_WD_lamp_color(sb) sb[0x2d] -#define LAMP_DEFAULT 0x00 -#define LAMP_BLUE 0x01 -#define LAMP_RED 0x02 -#define LAMP_GREEN 0x04 +#define WD_LAMP_DEFAULT 0x00 +#define WD_LAMP_BLUE 0x01 +#define WD_LAMP_RED 0x02 +#define WD_LAMP_GREEN 0x04 /* 0x2e - variance/bit padding * 3091 - unsupported, use 0x00 @@ -828,13 +897,27 @@ #define set_WD_subwindow_list(sb, val) putnbyte(sb + 0x33, val, 2) #define get_WD_subwindow_list(sb) getnbyte(sb + 0x33, 2) - /* 0x35 - paper size + /* 0x35 - paper size * 3091 unsupported, always use 0xc0 * 3096 if bits 6-7 both set, custom paper size enabled, * bytes 0x36-0x3d used. Otherwise, a number of * valid fixed values denote common paper formats. */ 0xC0, +#define set_WD_paper_selection(sb, val) setbitfield(sb + 0x35, 3, 6, val) +#define WD_paper_SEL_UNDEFINED 0 +#define WD_paper_SEL_NON_STANDARD 3 + +/* we no longer use these, custom size (0xc0) overrides, + and more recent scanners only use custom size anyway +#define get_WD_paper_selection(sb) getbitfield(sb + 0x35, 3, 6) +#define WD_paper_SEL_STANDARD 2 + +#define set_WD_paper_orientation(sb, val) setbitfield(sb + 0x35, 1, 4, val) +#define get_WD_paper_orientation(sb) getbitfield(sb + 0x35, 1, 4) +#define WD_paper_PORTRAIT 0 +#define WD_paper_LANDSCAPE 1 + #define set_WD_paper_size(sb, val) setbitfield(sb + 0x35, 0x0f, 0, val) #define get_WD_paper_size(sb) getbitfield(sb + 0x35, 0x0f, 0) #define WD_paper_UNDEFINED 0 @@ -847,15 +930,7 @@ #define WD_paper_B5 13 #define WD_paper_LEGAL 15 #define WD_paper_CUSTOM 14 -#define set_WD_paper_orientation(sb, val) setbitfield(sb + 0x35, 1, 4, val) -#define get_WD_paper_orientation(sb) getbitfield(sb + 0x35, 1, 4) -#define WD_paper_PORTRAIT 0 -#define WD_paper_LANDSCAPE 1 -#define set_WD_paper_selection(sb, val) setbitfield(sb + 0x35, 3, 6, val) -#define get_WD_paper_selection(sb) getbitfield(sb + 0x35, 3, 6) -#define WD_paper_SEL_UNDEFINED 0 -#define WD_paper_SEL_STANDARD 2 -#define WD_paper_SEL_NON_STANDARD 3 +*/ /* 0x36-0x39 - custom paper width * 3091 0 Copyright (C) 2004, 2005 Gerhard Jaeger Copyright (C) 2004, 2005 Stephane Voltz - Copyright (C) 2005 Pierre Willenbrock + Copyright (C) 2005, 2006 Pierre Willenbrock + Copyright (C) 2006 Laurent Charpentier This file is part of the SANE package. @@ -90,6 +91,13 @@ 0 }; +static SANE_String_Const color_filter_list[] = { + SANE_I18N ("Red"), + SANE_I18N ("Green"), + SANE_I18N ("Blue"), + 0 +}; + static SANE_String_Const source_list[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"), @@ -120,6 +128,11 @@ 0 /* quantization */ }; +static const SANE_Range threshold_percentage_range = { + SANE_FIX( 0), /* minimum */ + SANE_FIX( 100), /* maximum */ + SANE_FIX( 1) /* quantization */ +}; /* ------------------------------------------------------------------------ */ /* functions calling ASIC specific functions */ @@ -531,7 +544,7 @@ */ static SANE_Int genesys_generate_slope_table ( - u_int8_t * slope_table, unsigned int max_steps, + u_int16_t * slope_table, unsigned int max_steps, unsigned int use_steps, u_int16_t stop_at, u_int16_t vstart, u_int16_t vend, unsigned int steps, double g, unsigned int *used_steps, unsigned int *vfinal) @@ -577,8 +590,7 @@ t2 = vstart * (1 - t) + t * vend; if (t2 < stop_at) break; - *slope_table++ = t2 & 0xff; - *slope_table++ = t2 >> 8; + *slope_table++ = t2; DBG (DBG_io, "slope_table[%3d] = %5d\n", c, t2); sum += t2; } @@ -597,8 +609,7 @@ for (i = 0; i < max_steps; i++,c++) { - *slope_table++ = (*vfinal) & 0xff; - *slope_table++ = (*vfinal) >> 8; + *slope_table++ = *vfinal; DBG (DBG_io, "slope_table[%3d] = %5d\n", c, *vfinal); } @@ -634,7 +645,7 @@ */ SANE_Int sanei_genesys_create_slope_table3 (Genesys_Device * dev, - u_int8_t * slope_table, int max_step, + u_int16_t * slope_table, int max_step, unsigned int use_steps, int step_type, int exposure_time, double yres, @@ -735,7 +746,7 @@ vend = 65535; sum_time = genesys_generate_slope_table( - (u_int8_t*)slope_table, 128, + slope_table, 128, steps, vtarget, @@ -766,9 +777,6 @@ SANE_Int sum = 0; int vstart, vend; int i; -#ifdef WORDS_BIGENDIAN - SANE_Byte h,l; -#endif DBG (DBG_proc, "sanei_genesys_create_slope_table2: %d steps, step_type = %d, " @@ -873,17 +881,6 @@ } } - /* post fix endianess issue */ -#ifdef WORDS_BIGENDIAN - for(i=0; i model->cmd_set->end_scan (dev, local_reg, SANE_FALSE)); sleep (1); /* sleep 1 s */ @@ -4296,7 +4317,8 @@ status = genesys_gray_lineart(work_buffer_src, destination, dev->settings.pixels, channels, - dst_lines); + dst_lines, + dev->settings.threshold); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "genesys_read_ordered_data: failed to reverse bits(%s)\n", @@ -4346,13 +4368,14 @@ static SANE_Status calc_parameters (Genesys_Scanner * s) { - SANE_String mode, source; + SANE_String mode, source, color_filter; SANE_Status status = SANE_STATUS_GOOD; SANE_Int depth = 0, resolution = 0; double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0; mode = s->val[OPT_MODE].s; source = s->val[OPT_SOURCE].s; + color_filter = s->val[OPT_COLOR_FILTER].s; depth = s->val[OPT_BIT_DEPTH].w; resolution = s->val[OPT_RESOLUTION].w; tl_x = SANE_UNFIX (s->val[OPT_TL_X].w); @@ -4374,8 +4397,12 @@ s->dev->settings.depth = depth; + /* interpolation */ + s->dev->settings.disable_interpolation = s->val[OPT_DISABLE_INTERPOLATION].w == SANE_TRUE; + /* Hardware settings */ - if (resolution > s->dev->sensor.optical_res) + if (resolution > s->dev->sensor.optical_res && + s->dev->settings.disable_interpolation) s->dev->settings.xres = s->dev->sensor.optical_res; else s->dev->settings.xres = resolution; @@ -4423,6 +4450,17 @@ s->dev->settings.tl_x = tl_x; s->dev->settings.tl_y = tl_y; + /* threshold setting */ + s->dev->settings.threshold = 2.55 * (SANE_UNFIX (s->val[OPT_THRESHOLD].w)); + + /* color filter */ + if (strcmp(color_filter, "Red") == 0) + s->dev->settings.color_filter = 0; + else if (strcmp(color_filter, "Blue") == 0) + s->dev->settings.color_filter = 2; + else + s->dev->settings.color_filter = 1; + return status; } @@ -4601,6 +4639,41 @@ s->opt[OPT_EXTRAS_GROUP].size = 0; s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; + /* BW threshold */ + s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; + s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; + s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; + s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED; + s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT; + s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; + s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_THRESHOLD].constraint.range = &threshold_percentage_range; + s->val[OPT_THRESHOLD].w = SANE_FIX(50); + + /* disable_interpolation */ + s->opt[OPT_DISABLE_INTERPOLATION].name = "disable-interpolation"; + s->opt[OPT_DISABLE_INTERPOLATION].title = SANE_I18N("Disable interpolation"); + s->opt[OPT_DISABLE_INTERPOLATION].desc = + SANE_I18N + ("When using high resolutions where the horizontal resolution is smaller " + "than the vertical resolution this disables horizontal interpolation."); + s->opt[OPT_DISABLE_INTERPOLATION].type = SANE_TYPE_BOOL; + s->opt[OPT_DISABLE_INTERPOLATION].unit = SANE_UNIT_NONE; + s->opt[OPT_DISABLE_INTERPOLATION].constraint_type = SANE_CONSTRAINT_NONE; + s->val[OPT_DISABLE_INTERPOLATION].w = SANE_FALSE; + + /* color filter */ + s->opt[OPT_COLOR_FILTER].name = "color-filter"; + s->opt[OPT_COLOR_FILTER].title = SANE_I18N("Color Filter"); + s->opt[OPT_COLOR_FILTER].desc = + SANE_I18N + ("When using gray or lineart this option selects the used color."); + s->opt[OPT_COLOR_FILTER].type = SANE_TYPE_STRING; + s->opt[OPT_COLOR_FILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list); + s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list; + s->val[OPT_COLOR_FILTER].s = strdup("Green"); + /* Powersave time (turn lamp off) */ s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time"; s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time"); @@ -5132,13 +5205,14 @@ case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: + case OPT_THRESHOLD: + case OPT_DISABLE_INTERPOLATION: case OPT_LAMP_OFF_TIME: *(SANE_Word *) val = s->val[option].w; break; /* string options: */ case OPT_MODE: - strcpy (val, s->val[option].s); - break; + case OPT_COLOR_FILTER: case OPT_SOURCE: strcpy (val, s->val[option].s); break; @@ -5178,6 +5252,8 @@ break; case OPT_RESOLUTION: case OPT_BIT_DEPTH: + case OPT_THRESHOLD: + case OPT_DISABLE_INTERPOLATION: case OPT_PREVIEW: s->val[option].w = *(SANE_Word *) val; RIE (calc_parameters (s)); @@ -5212,16 +5288,20 @@ s->val[option].s = strdup (val); if (strcmp (s->val[option].s, "Lineart") == 0) { - /* ENABLE (OPT_THRESHOLD); */ + ENABLE (OPT_THRESHOLD); DISABLE (OPT_BIT_DEPTH); + ENABLE (OPT_COLOR_FILTER); } else { - /* DISABLE (OPT_THRESHOLD); */ - if (strcmp (s->val[option].s, "Gray") == 0) + DISABLE (OPT_THRESHOLD); + if (strcmp (s->val[option].s, "Gray") == 0) { + ENABLE (OPT_COLOR_FILTER); create_bpp_list (s, s->dev->model->bpp_gray_values); - else + } else { + DISABLE (OPT_COLOR_FILTER); create_bpp_list (s, s->dev->model->bpp_color_values); + } if (s->bpp_list[0] < 2) DISABLE (OPT_BIT_DEPTH); else @@ -5230,6 +5310,12 @@ RIE (calc_parameters (s)); myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; + case OPT_COLOR_FILTER: + if (s->val[option].s) + free (s->val[option].s); + s->val[option].s = strdup (val); + RIE (calc_parameters (s)); + break; case OPT_LAMP_OFF_TIME: if (*(SANE_Word *) val != s->val[option].w) { diff -uNr sane-backends-1.0.17/backend/genesys.conf sane-backends-1.0.18/backend/genesys.conf --- sane-backends-1.0.17/backend/genesys.conf 2005-12-06 22:46:01.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,35 +0,0 @@ -# genesys.conf: Configuration file for Genesys Logic GL646 and GL841 based scanners - -# -# scanners that are not yet supported -# uncomment them only for developpment purpose -# - -# UMAX Astra 4500 and Avision iVina 1600 -#usb 0x0638 0x0a10 - -# Hewlett Packard ScanJet 2400c -#usb 0x03f0 0x0a01 - -# Hewlett Packard ScanJet 3670c -#usb 0x03f0 0x1405 - -# Plustek OpticPro ST24 -#usb 0x07b3 0x0601 - - -# -# supported scanners -# - -# Medion MD5345/MD6228/MD6471 -usb 0x0461 0x0377 - -# Hewlett Packard ScanJet 2300c -usb 0x03f0 0x0901 - -# Canon LiDE 35/40/50 -usb 0x04a9 0x2213 - -# Canon LiDE 60 -usb 0x04a9 0x221c diff -uNr sane-backends-1.0.17/backend/genesys.conf.in sane-backends-1.0.18/backend/genesys.conf.in --- sane-backends-1.0.17/backend/genesys.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,35 @@ +# genesys.conf: Configuration file for Genesys Logic GL646 and GL841 based scanners + +# +# scanners that are not yet supported +# uncomment them only for developpment purpose +# + +# UMAX Astra 4500 and Avision iVina 1600 +#usb 0x0638 0x0a10 + +# Hewlett Packard ScanJet 2400c +#usb 0x03f0 0x0a01 + +# Hewlett Packard ScanJet 3670c +#usb 0x03f0 0x1405 + +# Plustek OpticPro ST24 +#usb 0x07b3 0x0601 + + +# +# supported scanners +# + +# Medion MD5345/MD6228/MD6471 +usb 0x0461 0x0377 + +# Hewlett Packard ScanJet 2300c +usb 0x03f0 0x0901 + +# Canon LiDE 35/40/50 +usb 0x04a9 0x2213 + +# Canon LiDE 60 +usb 0x04a9 0x221c diff -uNr sane-backends-1.0.17/backend/genesys_conv.c sane-backends-1.0.18/backend/genesys_conv.c --- sane-backends-1.0.17/backend/genesys_conv.c 2005-12-06 22:46:01.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys_conv.c 2006-03-29 22:18:16.000000000 +0200 @@ -1,6 +1,6 @@ /* sane - Scanner Access Now Easy. - Copyright (C) 2005 Pierre Willenbrock + Copyright (C) 2005, 2006 Pierre Willenbrock This file is part of the SANE package. @@ -95,7 +95,8 @@ u_int8_t *dst_data, size_t pixels, size_t channels, - size_t lines) + size_t lines, + u_int8_t threshold) { size_t x,y,c,b; for(y = 0; y < lines; y++) { @@ -104,7 +105,7 @@ *(dst_data + c) = 0; for(b = 0; b < 8 && x+b < pixels; b++) { for(c = 0; c < channels; c++) { - if (*src_data++ < 0x80) + if (*src_data++ < threshold) *(dst_data + c) |= (0x80 >> b); } } diff -uNr sane-backends-1.0.17/backend/genesys_conv_hlp.c sane-backends-1.0.18/backend/genesys_conv_hlp.c --- sane-backends-1.0.17/backend/genesys_conv_hlp.c 2005-12-06 22:46:01.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys_conv_hlp.c 2006-04-18 16:29:38.000000000 +0200 @@ -327,9 +327,10 @@ cnt = dst_pixels / 2; dst_x = 0; for (src_x = 0; src_x < src_pixels; src_x++) { - for (c = 0; c < channels; c++) + for (c = 0; c < channels; c++) avg[c] = *src++; - while (cnt < dst_pixels && dst_x < dst_pixels) { + while ((cnt < dst_pixels || src_x + 1 == src_pixels) && + dst_x < dst_pixels) { cnt += src_pixels; for (c = 0; c < channels; c++) diff -uNr sane-backends-1.0.17/backend/genesys_devices.c sane-backends-1.0.18/backend/genesys_devices.c --- sane-backends-1.0.17/backend/genesys_devices.c 2005-12-06 22:46:01.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys_devices.c 2006-05-16 21:19:19.000000000 +0200 @@ -381,13 +381,13 @@ {{ 3500, 1300, - 50, + 60, 0.8, }, { 3500, 1400, - 50, + 60, 0.8, }, }, @@ -539,9 +539,9 @@ {16, 8, 0}, /* possible depths in color mode */ SANE_FIX (-2.0), /* Start of scan area in mm (x_offset) */ - SANE_FIX (-2.0), /* Start of scan area in mm (y_offset) */ + SANE_FIX (0.0), /* Start of scan area in mm (y_offset) */ SANE_FIX (215.9), /* Size of scan area in mm (x) */ - SANE_FIX (297.1), /* Size of scan area in mm (y) */ + SANE_FIX (295.0), /* Size of scan area in mm (y) */ SANE_FIX (0.0), /* Start of white strip in mm (y) */ SANE_FIX (1.0), /* Start of black mark in mm (x) */ diff -uNr sane-backends-1.0.17/backend/genesys_gl646.c sane-backends-1.0.18/backend/genesys_gl646.c --- sane-backends-1.0.17/backend/genesys_gl646.c 2005-12-06 22:46:02.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys_gl646.c 2006-05-16 21:19:19.000000000 +0200 @@ -3,8 +3,8 @@ Copyright (C) 2003 Oliver Rauch Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2004 Gerhard Jaeger - Copyright (C) 2004, 2005 Stephane Voltz - Copyright (C) 2005 Pierre Willenbrock + Copyright (C) 2004 - 2006 Stephane Voltz + Copyright (C) 2005, 2006 Pierre Willenbrock This file is part of the SANE package. @@ -1051,7 +1051,9 @@ } -/* Send slope table for motor movement */ +/* Send slope table for motor movement + slope_table in machine byte order +*/ static SANE_Status gl646_send_slope_table (Genesys_Device * dev, int table_nr, u_int16_t * slope_table, int steps) @@ -1059,6 +1061,10 @@ int dpihw; int start_address; SANE_Status status; + u_int8_t *table; +#ifdef WORDS_BIGENDIAN + int i; +#endif DBG (DBG_proc, "gl646_send_slope_table (table_nr = %d, steps = %d)\n", table_nr, steps); @@ -1074,10 +1080,23 @@ else /* reserved */ return SANE_STATUS_INVAL; +#ifdef WORDS_BIGENDIAN + table = (u_int8_t*)malloc(steps * 2); + for(i = 0; i < steps; i++) { + table[i * 2] = slope_table[i] & 0xff; + table[i * 2 + 1] = slope_table[i] >> 8; + } +#else + table = (u_int8_t*)slope_table; +#endif + status = sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x100); if (status != SANE_STATUS_GOOD) { +#ifdef WORDS_BIGENDIAN + free(table); +#endif DBG (DBG_error, "gl646_send_slope_table: failed to set buffer address: %s\n", sane_strstatus (status)); @@ -1085,16 +1104,22 @@ } status = - gl646_bulk_write_data (dev, 0x3c, (u_int8_t *) slope_table, + gl646_bulk_write_data (dev, 0x3c, (u_int8_t *) table, steps * 2); if (status != SANE_STATUS_GOOD) { +#ifdef WORDS_BIGENDIAN + free(table); +#endif DBG (DBG_error, "gl646_send_slope_table: failed to send slope table: %s\n", sane_strstatus (status)); return status; } +#ifdef WORDS_BIGENDIAN + free(table); +#endif DBG (DBG_proc, "gl646_send_slope_table: completed\n"); return status; } @@ -1108,8 +1133,8 @@ int i; u_int8_t val; - DBG (DBG_proc, "gl646_set_fe (%s)\n", set == 1 ? "init" : set == 2 ? "set" - : set == 3 ? "powersave" : "huh?"); + DBG (DBG_proc, "gl646_set_fe (%s)\n", set == AFE_INIT ? "init" : set == AFE_SET ? "set" + : set == AFE_POWER_SAVE ? "powersave" : "huh?"); if ((dev->reg[reg_0x04].value & REG04_FESET) != 0x03) { @@ -1275,7 +1300,7 @@ if (enable) { - gl646_set_fe (dev, AFE_POWER_SAVE); + /* gl646_set_fe (dev, AFE_POWER_SAVE); */ } else { @@ -1288,14 +1313,14 @@ static SANE_Status gl646_set_powersaving (Genesys_Device * dev, int delay /* in minutes */ ) { - SANE_Status status; - Genesys_Register_Set local_reg[7]; + SANE_Status status=SANE_STATUS_GOOD; + Genesys_Register_Set local_reg[6]; int rate, exposure_time, tgtime, time; DBG (DBG_proc, "gl646_set_powersaving (delay = %d)\n", delay); local_reg[0].address = 0x01; - local_reg[0].value = sanei_genesys_read_reg_from_set (dev->reg, 0x01) & ~REG01_FASTMOD; /* disable fastmode */ + local_reg[0].value = sanei_genesys_read_reg_from_set (dev->reg, 0x01); /* disable fastmode */ local_reg[1].address = 0x03; local_reg[1].value = sanei_genesys_read_reg_from_set (dev->reg, 0x03); /* Lamp power control */ @@ -1303,18 +1328,15 @@ local_reg[2].address = 0x05; local_reg[2].value = sanei_genesys_read_reg_from_set (dev->reg, 0x05) & ~REG05_BASESEL; /* 24 clocks/pixel */ - local_reg[3].address = 0x18; /* Set CCD type */ + local_reg[3].address = 0x38; /* line period low */ local_reg[3].value = 0x00; - local_reg[4].address = 0x38; /* line period low */ + local_reg[4].address = 0x39; /* line period high */ local_reg[4].value = 0x00; - local_reg[5].address = 0x39; /* line period high */ + local_reg[5].address = 0x6c; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE */ local_reg[5].value = 0x00; - local_reg[6].address = 0x6c; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE */ - local_reg[6].value = 0x00; - if (!delay) local_reg[1].value = local_reg[1].value & 0xf0; /* disable lampdog and set lamptime = 0 */ else if (delay < 20) @@ -1350,17 +1372,16 @@ tgtime = 0; } - local_reg[6].value |= tgtime << 6; + local_reg[5].value |= tgtime << 6; exposure_time /= rate; if (exposure_time > 65535) exposure_time = 65535; - local_reg[4].value = exposure_time / 256; /* highbyte */ - local_reg[5].value = exposure_time & 255; /* lowbyte */ + local_reg[3].value = exposure_time / 256; /* highbyte */ + local_reg[4].value = exposure_time & 255; /* lowbyte */ - status = - gl646_bulk_write_register (dev, local_reg, sizeof (local_reg)); + status = gl646_bulk_write_register (dev, local_reg, sizeof (local_reg)); if (status != SANE_STATUS_GOOD) DBG (DBG_error, "gl646_set_powersaving: Failed to bulk write registers: %s\n", @@ -2708,9 +2729,9 @@ if (channels > 1) { max_shift = dev->model->ld_shift_r; - if (dev->model->ld_shift_b > max_shift) + if ((unsigned int)dev->model->ld_shift_b > max_shift) max_shift = dev->model->ld_shift_b; - if (dev->model->ld_shift_g > max_shift) + if ((unsigned int)dev->model->ld_shift_g > max_shift) max_shift = dev->model->ld_shift_g; max_shift = (max_shift * dev->settings.yres) / dev->motor.base_ydpi; @@ -2926,6 +2947,13 @@ DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move); move += (SANE_UNFIX (dev->model->y_offset) * move_dpi) / MM_PER_INCH; + if (move < 0) + { + DBG (DBG_error, + "gl646_init_regs_for_scan: overriding negative move value %d\n", + move); + move = 1; + } DBG (DBG_info, "gl646_init_regs_for_scan: move=%d steps\n", move); /* add tl_y to base movement */ @@ -3099,7 +3127,7 @@ int size; int address; int status; - u_int16_t *gamma; + u_int8_t *gamma; int i; /* don't send anything if no specific gamma table defined */ @@ -3135,19 +3163,21 @@ } /* allocate temporary gamma tables: 16 bits words, 3 channels */ - gamma = (u_int16_t *) malloc (size * 2 * 3); + gamma = (u_int8_t *) malloc (size * 2 * 3); if (!gamma) return SANE_STATUS_NO_MEM; - /* take care off generic/specific data */ if (generic) { /* fill with default values */ for (i = 0; i < size; i++) { - gamma[i] = (u_int16_t) i; - gamma[i + size] = (u_int16_t) i; - gamma[i + size * 2] = (u_int16_t) i; + gamma[i * 2] = i & 0xff; + gamma[i * 2 + 1] = i >> 8; + gamma[i * 2 + size * 2] = i & 0xff; + gamma[i * 2 + 1 + size * 2] = i >> 8; + gamma[i * 2 + size * 4] = i & 0xff; + gamma[i * 2 + 1 + size * 4] = i >> 8; } } else @@ -3155,9 +3185,12 @@ /* copy sensor specific's gamma tables */ for (i = 0; i < size; i++) { - gamma[i] = dev->sensor.red_gamma_table[i]; - gamma[i + size] = dev->sensor.green_gamma_table[i]; - gamma[i + size * 2] = dev->sensor.blue_gamma_table[i]; + gamma[i * 2] = dev->sensor.red_gamma_table[i] & 0xff; + gamma[i * 2 + 1] = dev->sensor.red_gamma_table[i] >> 8; + gamma[i * 2 + size * 2] = dev->sensor.green_gamma_table[i] & 0xff; + gamma[i * 2 + 1 + size * 2] = dev->sensor.green_gamma_table[i] >> 8; + gamma[i * 2 + size * 4] = dev->sensor.blue_gamma_table[i] & 0xff; + gamma[i * 2 + 1 + size * 4] = dev->sensor.blue_gamma_table[i] >> 8; } } @@ -3952,12 +3985,12 @@ { Genesys_Register_Set local_reg[GENESYS_GL646_MAX_REGS + 1]; u_int16_t slope_table[256]; - int exposure_time; /* todo : modify sanei_genesys_exposure_time() */ + unsigned int exposure_time; /* todo : modify sanei_genesys_exposure_time() */ SANE_Status status; - int steps = 232; - int lincnt = 212; - int dpi = 600; - int endpixel, expected; + unsigned int steps = 232; + unsigned int lincnt = 212; + unsigned int dpi = 600; + unsigned int endpixel, expected; DBG (DBG_proc, "gl646_repark_head\n"); diff -uNr sane-backends-1.0.17/backend/genesys_gl841.c sane-backends-1.0.18/backend/genesys_gl841.c --- sane-backends-1.0.17/backend/genesys_gl841.c 2005-12-06 22:46:03.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys_gl841.c 2006-06-11 13:10:59.000000000 +0200 @@ -5,7 +5,8 @@ Copyright (C) 2004 Gerhard Jaeger Copyright (C) 2004, 2005 Stephane Voltz Copyright (C) 2005 Philipp Schmid - Copyright (C) 2005 Pierre Willenbrock + Copyright (C) 2005, 2006 Pierre Willenbrock + Copyright (C) 2006 Laurent Charpentier This file is part of the SANE package. @@ -1396,6 +1397,7 @@ /* * Set all registers to default values + * (function called only once at the beginning) */ static void gl841_init_registers (Genesys_Device * dev) @@ -1511,7 +1513,9 @@ DBG (DBG_proc, "gl841_init_registers complete\n"); } -/* Send slope table for motor movement */ +/* Send slope table for motor movement + slope_table in machine byte order + */ static SANE_Status gl841_send_slope_table (Genesys_Device * dev, int table_nr, u_int16_t * slope_table, int steps) @@ -1519,6 +1523,10 @@ int dpihw; int start_address; SANE_Status status; + u_int8_t *table; +/*#ifdef WORDS_BIGENDIAN*/ + int i; +/*#endif*/ DBG (DBG_proc, "gl841_send_slope_table (table_nr = %d, steps = %d)\n", table_nr, steps); @@ -1534,10 +1542,23 @@ else /* reserved */ return SANE_STATUS_INVAL; +/*#ifdef WORDS_BIGENDIAN*/ + table = (u_int8_t*)malloc(steps * 2); + for(i = 0; i < steps; i++) { + table[i * 2] = slope_table[i] & 0xff; + table[i * 2 + 1] = slope_table[i] >> 8; + } +/*#else + table = (u_int8_t*)slope_table; + #endif*/ + status = sanei_genesys_set_buffer_address (dev, start_address + table_nr * 0x200); if (status != SANE_STATUS_GOOD) { +/*#ifdef WORDS_BIGENDIAN*/ + free(table); +/*#endif*/ DBG (DBG_error, "gl841_send_slope_table: failed to set buffer address: %s\n", sane_strstatus (status)); @@ -1545,16 +1566,22 @@ } status = - gl841_bulk_write_data (dev, 0x3c, (u_int8_t *) slope_table, + gl841_bulk_write_data (dev, 0x3c, (u_int8_t *) table, steps * 2); if (status != SANE_STATUS_GOOD) { +/*#ifdef WORDS_BIGENDIAN*/ + free(table); +/*#endif*/ DBG (DBG_error, "gl841_send_slope_table: failed to send slope table: %s\n", sane_strstatus (status)); return status; } +/*#ifdef WORDS_BIGENDIAN*/ + free(table); +/*#endif*/ DBG (DBG_proc, "gl841_send_slope_table: completed\n"); return status; } @@ -1841,7 +1868,7 @@ SANE_Status status; unsigned int fast_exposure; int use_fast_fed = 0; - u_int8_t fast_slope_table[512]; + u_int16_t fast_slope_table[256]; unsigned int fast_slope_time; unsigned int fast_slope_steps = 0; unsigned int feedl; @@ -1855,16 +1882,11 @@ memset(fast_slope_table,0xff,512); - gl841_send_slope_table (dev, 0, - (u_int16_t*)fast_slope_table, 256); - gl841_send_slope_table (dev, 1, - (u_int16_t*)fast_slope_table, 256); - gl841_send_slope_table (dev, 2, - (u_int16_t*)fast_slope_table, 256); - gl841_send_slope_table (dev, 3, - (u_int16_t*)fast_slope_table, 256); - gl841_send_slope_table (dev, 4, - (u_int16_t*)fast_slope_table, 256); + gl841_send_slope_table (dev, 0, fast_slope_table, 256); + gl841_send_slope_table (dev, 1, fast_slope_table, 256); + gl841_send_slope_table (dev, 2, fast_slope_table, 256); + gl841_send_slope_table (dev, 3, fast_slope_table, 256); + gl841_send_slope_table (dev, 4, fast_slope_table, 256); if (action == MOTOR_ACTION_FEED || action == MOTOR_ACTION_GO_HOME) { @@ -1969,8 +1991,7 @@ r->value &= ~0x40; - status = gl841_send_slope_table (dev, 3, - (u_int16_t*)fast_slope_table, 256); + status = gl841_send_slope_table (dev, 3, fast_slope_table, 256); if (status != SANE_STATUS_GOOD) return status; @@ -2020,9 +2041,9 @@ int use_fast_fed = 0; unsigned int fast_time; unsigned int slow_time; - u_int8_t slow_slope_table[512]; - u_int8_t fast_slope_table[512]; - u_int8_t back_slope_table[512]; + u_int16_t slow_slope_table[256]; + u_int16_t fast_slope_table[256]; + u_int16_t back_slope_table[256]; unsigned int slow_slope_time; unsigned int fast_slope_time; unsigned int back_slope_time; @@ -2058,16 +2079,11 @@ memset(slow_slope_table,0xff,512); - gl841_send_slope_table (dev, 0, - (u_int16_t*)slow_slope_table, 256); - gl841_send_slope_table (dev, 1, - (u_int16_t*)slow_slope_table, 256); - gl841_send_slope_table (dev, 2, - (u_int16_t*)slow_slope_table, 256); - gl841_send_slope_table (dev, 3, - (u_int16_t*)slow_slope_table, 256); - gl841_send_slope_table (dev, 4, - (u_int16_t*)slow_slope_table, 256); + gl841_send_slope_table (dev, 0, slow_slope_table, 256); + gl841_send_slope_table (dev, 1, slow_slope_table, 256); + gl841_send_slope_table (dev, 2, slow_slope_table, 256); + gl841_send_slope_table (dev, 3, slow_slope_table, 256); + gl841_send_slope_table (dev, 4, slow_slope_table, 256); /* @@ -2229,35 +2245,30 @@ else r->value &= ~0x40; - status = gl841_send_slope_table (dev, 0, - (u_int16_t*)slow_slope_table, 256); + status = gl841_send_slope_table (dev, 0, slow_slope_table, 256); if (status != SANE_STATUS_GOOD) return status; - status = gl841_send_slope_table (dev, 1, - (u_int16_t*)back_slope_table, 256); + status = gl841_send_slope_table (dev, 1, back_slope_table, 256); if (status != SANE_STATUS_GOOD) return status; - status = gl841_send_slope_table (dev, 2, - (u_int16_t*)slow_slope_table, 256); + status = gl841_send_slope_table (dev, 2, slow_slope_table, 256); if (status != SANE_STATUS_GOOD) return status; if (use_fast_fed) { - status = gl841_send_slope_table (dev, 3, - (u_int16_t*)fast_slope_table, 256); + status = gl841_send_slope_table (dev, 3, fast_slope_table, 256); if (status != SANE_STATUS_GOOD) return status; } if (flags & MOTOR_FLAG_AUTO_GO_HOME){ - status = gl841_send_slope_table (dev, 4, - (u_int16_t*)fast_slope_table, 256); + status = gl841_send_slope_table (dev, 4, fast_slope_table, 256); if (status != SANE_STATUS_GOOD) return status; @@ -2373,6 +2384,7 @@ int channels, int depth, SANE_Bool half_ccd, + int color_filter, int flags ) { @@ -2448,6 +2460,13 @@ r = sanei_genesys_get_address (reg, 0x03); r->value |= REG03_AVEENB | REG03_LAMPPWR; + /* BW threshold */ + r = sanei_genesys_get_address (reg, 0x2e); + r->value = dev->settings.threshold; + r = sanei_genesys_get_address (reg, 0x2f); + r->value = dev->settings.threshold; + + /* monochrome / color scan */ r = sanei_genesys_get_address (reg, 0x04); switch (depth) { @@ -2467,9 +2486,19 @@ r->value &= ~(REG04_FILTER | REG04_AFEMOD); /* we could make the color filter used an advanced option of the backend */ - if (channels == 1) - r->value |= 0x18; /* green filter */ - else + if (channels == 1) { + switch (color_filter) { + case 0: + r->value |= 0x14; /* red filter */ + break; + case 2: + r->value |= 0x1c; /* blue filter */ + break; + default: + r->value |= 0x18; /* green filter */ + break; + } + } else r->value |= 0x10; /* color pixel by pixel */ /* enable gamma tables */ @@ -2571,6 +2600,7 @@ float lines, unsigned int depth, unsigned int channels, + int color_filter, unsigned int flags ) { @@ -2780,18 +2810,19 @@ flags |= SCAN_FLAG_DISABLE_GAMMA; status = gl841_init_optical_regs_scan(dev, - reg, - exposure_time, - used_res, - start, - used_pixels, - channels, - depth, - half_ccd, - ((flags & SCAN_FLAG_DISABLE_SHADING)? - OPTICAL_FLAG_DISABLE_SHADING:0) | - ((flags & SCAN_FLAG_DISABLE_GAMMA)? - OPTICAL_FLAG_DISABLE_GAMMA:0)); + reg, + exposure_time, + used_res, + start, + used_pixels, + channels, + depth, + half_ccd, + color_filter, + ((flags & SCAN_FLAG_DISABLE_SHADING)? + OPTICAL_FLAG_DISABLE_SHADING:0) | + ((flags & SCAN_FLAG_DISABLE_GAMMA)? + OPTICAL_FLAG_DISABLE_GAMMA:0)); if (status != SANE_STATUS_GOOD) return status; @@ -3688,6 +3719,7 @@ dev->model->search_lines, 8, 1, + 1,/*green*/ SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_IGNORE_LINE_DISTANCE | @@ -3808,6 +3840,7 @@ 20, 16, channels, + dev->settings.color_filter, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_SINGLE_LINE | @@ -3873,6 +3906,7 @@ dev->model->shading_lines, 16, channels, + dev->settings.color_filter, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | /* we don't handle differing shading areas very well */ @@ -3991,6 +4025,7 @@ dev->settings.lines, depth, channels, + dev->settings.color_filter, 0 ); @@ -4147,6 +4182,7 @@ 1, 16, channels, + dev->settings.color_filter, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_SINGLE_LINE | @@ -4337,6 +4373,7 @@ 1, 16, channels, + dev->settings.color_filter, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_SINGLE_LINE | @@ -4724,6 +4761,7 @@ 1, 16, channels, + dev->settings.color_filter, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_SINGLE_LINE | @@ -4857,6 +4895,7 @@ 1, 16, *channels, + dev->settings.color_filter, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_SINGLE_LINE | @@ -5059,6 +5098,7 @@ 1, 16, 3, + 0, SCAN_FLAG_DISABLE_SHADING | SCAN_FLAG_DISABLE_GAMMA | SCAN_FLAG_SINGLE_LINE | diff -uNr sane-backends-1.0.17/backend/genesys.h sane-backends-1.0.18/backend/genesys.h --- sane-backends-1.0.17/backend/genesys.h 2005-12-06 22:46:01.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys.h 2006-06-11 13:10:59.000000000 +0200 @@ -2,6 +2,7 @@ Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2005 Stephane Voltz + Copyright (C) 2006 Laurent Charpentier This file is part of the SANE package. @@ -82,6 +83,9 @@ OPT_EXTRAS_GROUP, OPT_LAMP_OFF_TIME, + OPT_THRESHOLD, + OPT_DISABLE_INTERPOLATION, + OPT_COLOR_FILTER, /* must come last: */ NUM_OPTIONS }; diff -uNr sane-backends-1.0.17/backend/genesys_low.h sane-backends-1.0.18/backend/genesys_low.h --- sane-backends-1.0.17/backend/genesys_low.h 2005-12-06 22:46:03.000000000 +0100 +++ sane-backends-1.0.18/backend/genesys_low.h 2006-06-11 13:10:59.000000000 +0200 @@ -5,6 +5,7 @@ Copyright (C) 2004, 2005 Gerhard Jaeger Copyright (C) 2004, 2005 Stephane Voltz Copyright (C) 2005 Pierre Willenbrock + Copyright (C) 2006 Laurent Charpentier Parts of the structs have been taken from the gt68xx backend by Sergey Vlasov et al. @@ -400,6 +401,12 @@ int exposure_time; unsigned int color_filter; /* todo: check, may be make it an advanced option */ + + /* BW threshold */ + int threshold; + + /* Disable interpolation for xres - Copyright (C) 2002 - 2005 Henning Meier-Geinitz + Copyright (C) 2002 - 2006 Henning Meier-Geinitz This file is part of the SANE package. @@ -48,7 +48,7 @@ #include "../include/sane/config.h" -#define BUILD 79 +#define BUILD 81 #define MAX_DEBUG #define WARMUP_TIME 60 #define CALIBRATION_HEIGHT 2.5 diff -uNr sane-backends-1.0.17/backend/gt68xx.conf sane-backends-1.0.18/backend/gt68xx.conf --- sane-backends-1.0.17/backend/gt68xx.conf 2005-09-29 19:28:27.000000000 +0200 +++ sane-backends-1.0.18/backend/gt68xx.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,213 +0,0 @@ - -# gt68xx.conf: Configuration file for GT68XX based scanners -# Read man sane-gt68xx for documentation - -# Manual configuration is necessary for some scanners. Please uncomment the -# override line and optinally the vendor and product lines that apply to your -# scanner. For some scanners it's also necessary to change the name of the -# firmware file. - -############################################################################# -# For testing scanners that are not yet supported by this backend add the -# vendor and product ids in the usb line below. Also fill in the override -# and firmware lines. For more details, see: -# http://www.meier-geinitz.de/sane/gt68xx-backend/adding.html - -# usb vendor product -# override "something" -# firmware "path" - -############################################################################## -# Autodetect Mustek BearPaw 1200 CU, 2400 CU, Mustek ScanExpress 1200 UB Plus, -# Artec Ultima 2000, and several other GT-6801-based scanners -usb 0x05d8 0x4002 - -# Mustek BearPaw 1200 CU doesn't need any manual override - -# Mustek ScanExpress 1200 UB Plus: -#override "mustek-scanexpress-1200-ub-plus" - -# Medion/Lifetec/Tevion LT 9452: -#override "mustek-scanexpress-1200-ub-plus" -#vendor "Lifetec" -#model "LT 9452" - -# Trust Compact Scan USB 19200: -#override "mustek-scanexpress-1200-ub-plus" -#vendor "Trust" -#model "Compact Scan USB 19200" - -# Mustek ScanExpress 2400 USB -#override "mustek-scanexpress-2400-usb" - -# Artec Ultima 2000: -#override "artec-ultima-2000" -#firmware "ePlus2k.usb" - -# Boeder SmartScan Slim Edition: -#override "artec-ultima-2000" -#vendor "Boeder" -#model "SmartScan Slim Edition" - -# Medion/Lifetec/Tevion/Cytron MD/LT 9385: -#override "artec-ultima-2000" -#vendor "Medion" -#model "MD/LT 9385" - -# Medion/Lifetec/Tevion/Cytron MD 9458: -#override "artec-ultima-2000" -#vendor "Medion" -#model "MD 9458" - -# Trust Flat Scan USB 19200: -#override "artec-ultima-2000" -#vendor "Trust" -#model "Flat Scan USB 19200" - -# Mustek BearPaw 2400 CU: -#override "mustek-bearpaw-2400-cu" - -# Fujitsu 1200CUS: -#override "mustek-bearpaw-2400-cu" -#vendor "Fujitsu" -#model "1200CUS" - -############################################################################## -# Autodetect Mustek BearPaw 1200 TA and Mustek BearPaw 1200 CS -usb 0x055f 0x021e - -# Mustek BearPaw 1200 TA doesn't need any manual settings - -# Mustek BearPaw 1200 CS: -#model "Bearpaw 1200 CS" - -############################################################################## -# Autodetect Mustek BearPaw 2400 TA and Mustek BearPaw 2400 CS -usb 0x055f 0x0218 - -# Mustek BearPaw 2400 TA doesn't need any manual settings - -# Mustek BearPaw 2400 CS: -#model "Bearpaw 2400 CS" - -############################################################################## -# Autodetect Mustek BearPaw 2400 TA Plus, Packard Bell Diamond 2450, and -# Trust 240TH Easy Webscan Gold -usb 0x055f 0x0219 - -# Mustek BearPaw 2400 TA doesn't need any manual settings - -# Trust 240TH Easy Webscan Gold: -#vendor "Trust" -#model "240TH Easy Webscan Gold" - -# Packard Bell Diamond 2450: -#vendor "Packard Bell" -#model "Diamond 2450" - -############################################################################## -# Autodetect Mustek BearPaw 2448 TA Plus and Mustek BearPaw 2448 CS Plus -usb 0x055f 0x021a - -# Mustek BearPaw 2448 CS Plus: -#model "Bearpaw 2448 CS Plus" - -############################################################################## -# Autodetect Mustek BearPaw 2400 CU Plus -usb 0x055f 0x021d - -############################################################################## -# Autodetect Mustek ScanExpress 1248 UB -usb 0x055f 0x021f - -############################################################################## -# Autodetect Mustek Bearpaw 1200 CU Plus and Packard Bell Diamond 1200 -usb 0x055f 0x021c - -# Mustek BearPaw 1200 CU Plus doesn't need any manual settings - -# Packard Bell Diamond 1200: -#vendor "Packard Bell" -#model "Diamond 1200" - -# Another Mustek BearPaw 1200 CU Plus version? -usb 0x055f 0x021b - -############################################################################## -# Autodetect Mustek ScanExpress A3 USB -usb 0x055f 0x0210 - -# Mustek ScanExpress A3 USB doesn't need any manual settings - -############################################################################## -# Autodetect Lexmark X70/X73 -usb 0x043d 0x002d - -############################################################################## -# Autodetect Plustek OpticPro 1248U and Revscan 19200i -usb 0x07b3 0x0401 -usb 0x07b3 0x0400 - -# Plustek OpticPro 1248U doesn't need any manual settings - -# RevScan 19200i -#vendor "RevScan" -#model "19200i" - -############################################################################## -# Autodetect Plustek OpticPro U16B and UT16B -usb 0x07b3 0x0402 -usb 0x07b3 0x0403 - -# Plustek OpticPro U16B doesn't need any manual settings - -# Plustek OpticPro UT16B -#model "UT16B" - -############################################################################## -# Autodetect Plustek OpticSlim M12 -usb 0x07b3 0x0412 - -############################################################################## -# Autodetect Plustek OpticSlim 1200 -usb 0x07b3 0x0413 - -############################################################################## -# Autodetect Plustek OpticSlim 2400 -usb 0x07b3 0x0422 - -############################################################################## -# Autodetect Genius Colorpage Vivid3x -usb 0x0458 0x2011 - -# Genius Colorpage Vivid3x doesn't need any manual settings - -############################################################################## -# Autodetect Genius Colorpage Vivid4x -usb 0x0458 0x201b - -############################################################################## -# Autodetect Genius Colorpage Vivid3xe -usb 0x0458 0x2017 - -############################################################################## -# Autodetect Genius Colorpage Vivid4xe -usb 0x0458 0x201a - -############################################################################## -# Autodetect Genius Colorpage 1200 X -usb 0x0458 0x201d - -############################################################################## -# Autodetect Genius Colorpage 1200 XE -usb 0x0458 0x201f - -############################################################################## -# Autodetect Genius Colorpage Vivid 4 -usb 0x0458 0x2014 - -############################################################################## -# Autodetect Visioneer OneTouch 7300 -usb 0x04a7 0x0444 - -############################################################################## diff -uNr sane-backends-1.0.17/backend/gt68xx.conf.in sane-backends-1.0.18/backend/gt68xx.conf.in --- sane-backends-1.0.17/backend/gt68xx.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/gt68xx.conf.in 2006-01-05 22:46:41.000000000 +0100 @@ -0,0 +1,215 @@ + +# gt68xx.conf: Configuration file for GT68XX based scanners (@PACKAGEVERSION@) +# Read man sane-gt68xx for documentation + +# Put the firmware file into "@DATADIR@/sane/gt68xx/". + +# Manual configuration is necessary for some scanners. Please uncomment the +# override line and optinally the vendor and product lines that apply to your +# scanner. For some scanners it's also necessary to change the name of the +# firmware file. + +############################################################################# +# For testing scanners that are not yet supported by this backend add the +# vendor and product ids in the usb line below. Also fill in the override +# and firmware lines. For more details, see: +# http://www.meier-geinitz.de/sane/gt68xx-backend/adding.html + +# usb vendor product +# override "something" +# firmware "path" + +############################################################################## +# Autodetect Mustek BearPaw 1200 CU, 2400 CU, Mustek ScanExpress 1200 UB Plus, +# Artec Ultima 2000, and several other GT-6801-based scanners +usb 0x05d8 0x4002 + +# Mustek BearPaw 1200 CU doesn't need any manual override + +# Mustek ScanExpress 1200 UB Plus: +#override "mustek-scanexpress-1200-ub-plus" + +# Medion/Lifetec/Tevion LT 9452: +#override "mustek-scanexpress-1200-ub-plus" +#vendor "Lifetec" +#model "LT 9452" + +# Trust Compact Scan USB 19200: +#override "mustek-scanexpress-1200-ub-plus" +#vendor "Trust" +#model "Compact Scan USB 19200" + +# Mustek ScanExpress 2400 USB +#override "mustek-scanexpress-2400-usb" + +# Artec Ultima 2000: +#override "artec-ultima-2000" +#firmware "ePlus2k.usb" + +# Boeder SmartScan Slim Edition: +#override "artec-ultima-2000" +#vendor "Boeder" +#model "SmartScan Slim Edition" + +# Medion/Lifetec/Tevion/Cytron MD/LT 9385: +#override "artec-ultima-2000" +#vendor "Medion" +#model "MD/LT 9385" + +# Medion/Lifetec/Tevion/Cytron MD 9458: +#override "artec-ultima-2000" +#vendor "Medion" +#model "MD 9458" + +# Trust Flat Scan USB 19200: +#override "artec-ultima-2000" +#vendor "Trust" +#model "Flat Scan USB 19200" + +# Mustek BearPaw 2400 CU: +#override "mustek-bearpaw-2400-cu" + +# Fujitsu 1200CUS: +#override "mustek-bearpaw-2400-cu" +#vendor "Fujitsu" +#model "1200CUS" + +############################################################################## +# Autodetect Mustek BearPaw 1200 TA and Mustek BearPaw 1200 CS +usb 0x055f 0x021e + +# Mustek BearPaw 1200 TA doesn't need any manual settings + +# Mustek BearPaw 1200 CS: +#model "Bearpaw 1200 CS" + +############################################################################## +# Autodetect Mustek BearPaw 2400 TA and Mustek BearPaw 2400 CS +usb 0x055f 0x0218 + +# Mustek BearPaw 2400 TA doesn't need any manual settings + +# Mustek BearPaw 2400 CS: +#model "Bearpaw 2400 CS" + +############################################################################## +# Autodetect Mustek BearPaw 2400 TA Plus, Packard Bell Diamond 2450, and +# Trust 240TH Easy Webscan Gold +usb 0x055f 0x0219 + +# Mustek BearPaw 2400 TA doesn't need any manual settings + +# Trust 240TH Easy Webscan Gold: +#vendor "Trust" +#model "240TH Easy Webscan Gold" + +# Packard Bell Diamond 2450: +#vendor "Packard Bell" +#model "Diamond 2450" + +############################################################################## +# Autodetect Mustek BearPaw 2448 TA Plus and Mustek BearPaw 2448 CS Plus +usb 0x055f 0x021a + +# Mustek BearPaw 2448 CS Plus: +#model "Bearpaw 2448 CS Plus" + +############################################################################## +# Autodetect Mustek BearPaw 2400 CU Plus +usb 0x055f 0x021d + +############################################################################## +# Autodetect Mustek ScanExpress 1248 UB +usb 0x055f 0x021f + +############################################################################## +# Autodetect Mustek Bearpaw 1200 CU Plus and Packard Bell Diamond 1200 +usb 0x055f 0x021c + +# Mustek BearPaw 1200 CU Plus doesn't need any manual settings + +# Packard Bell Diamond 1200: +#vendor "Packard Bell" +#model "Diamond 1200" + +# Another Mustek BearPaw 1200 CU Plus version? +usb 0x055f 0x021b + +############################################################################## +# Autodetect Mustek ScanExpress A3 USB +usb 0x055f 0x0210 + +# Mustek ScanExpress A3 USB doesn't need any manual settings + +############################################################################## +# Autodetect Lexmark X70/X73 +usb 0x043d 0x002d + +############################################################################## +# Autodetect Plustek OpticPro 1248U and Revscan 19200i +usb 0x07b3 0x0401 +usb 0x07b3 0x0400 + +# Plustek OpticPro 1248U doesn't need any manual settings + +# RevScan 19200i +#vendor "RevScan" +#model "19200i" + +############################################################################## +# Autodetect Plustek OpticPro U16B and UT16B +usb 0x07b3 0x0402 +usb 0x07b3 0x0403 + +# Plustek OpticPro U16B doesn't need any manual settings + +# Plustek OpticPro UT16B +#model "UT16B" + +############################################################################## +# Autodetect Plustek OpticSlim M12 +usb 0x07b3 0x0412 + +############################################################################## +# Autodetect Plustek OpticSlim 1200 +usb 0x07b3 0x0413 + +############################################################################## +# Autodetect Plustek OpticSlim 2400 +usb 0x07b3 0x0422 + +############################################################################## +# Autodetect Genius Colorpage Vivid3x +usb 0x0458 0x2011 + +# Genius Colorpage Vivid3x doesn't need any manual settings + +############################################################################## +# Autodetect Genius Colorpage Vivid4x +usb 0x0458 0x201b + +############################################################################## +# Autodetect Genius Colorpage Vivid3xe +usb 0x0458 0x2017 + +############################################################################## +# Autodetect Genius Colorpage Vivid4xe +usb 0x0458 0x201a + +############################################################################## +# Autodetect Genius Colorpage 1200 X +usb 0x0458 0x201d + +############################################################################## +# Autodetect Genius Colorpage 1200 XE +usb 0x0458 0x201f + +############################################################################## +# Autodetect Genius Colorpage Vivid 4 +usb 0x0458 0x2014 + +############################################################################## +# Autodetect Visioneer OneTouch 7300 +usb 0x04a7 0x0444 + +############################################################################## diff -uNr sane-backends-1.0.17/backend/gt68xx_devices.c sane-backends-1.0.18/backend/gt68xx_devices.c --- sane-backends-1.0.17/backend/gt68xx_devices.c 2005-12-06 20:54:29.000000000 +0100 +++ sane-backends-1.0.18/backend/gt68xx_devices.c 2006-05-21 18:36:47.000000000 +0200 @@ -1,7 +1,7 @@ /* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov - Copyright (C) 2002 - 2005 Henning Meier-Geinitz + Copyright (C) 2002 - 2006 Henning Meier-Geinitz This file is part of the SANE package. @@ -1100,6 +1100,106 @@ issue. */ }; +static GT68xx_Model plustek_ops12_model = { + "plustek-opticpro-s12", /* Name */ + "Plustek", /* Device vendor string */ + "OpticPro S12", /* Device model name */ + "ccd548.fw", /* Name of the firmware file */ + SANE_FALSE, /* Dynamic allocation flag */ + + &mustek_gt6816_command_set, /* Command set used by this scanner */ + + 600, /* maximum optical sensor resolution */ + 600, /* maximum motor resolution */ + 600, /* base x-res used to calculate geometry */ + 600, /* base y-res used to calculate geometry */ + 1200, /* if ydpi is equal or higher, disable backtracking */ + SANE_TRUE, /* Use base_ydpi for all resolutions */ + + {600, 300, 150, 75, 0}, /* possible x-resolutions */ + {600, 300, 150, 75, 0}, /* possible y-resolutions */ + {12, 8, 0}, /* possible depths in gray mode */ + {12, 8, 0}, /* possible depths in color mode */ + + SANE_FIX (3.5), /* Start of scan area in mm (x) */ + SANE_FIX (7.5), /* Start of scan area in mm (y) */ + SANE_FIX (218.0), /* Size of scan area in mm (x) */ + SANE_FIX (299.0), /* Size of scan area in mm (y) */ + + SANE_FIX (0.0), /* Start of white strip in mm (y) */ + SANE_FIX (1.0), /* Start of black mark in mm (x) */ + + SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */ + SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */ + SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */ + SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */ + + SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */ + + 0, 8, 16, /* RGB CCD Line-distance correction in pixel */ + 0, /* CCD distcance for CCD with 6 lines) */ + + COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */ + {0x1c, 0x29, 0x1c, 0x2c, 0x1c, 0x2b}, /* Default offset/gain */ + {0x157, 0x157, 0x157}, /* Default exposure parameters */ + SANE_FIX (2.0), /* Default gamma value */ + + SANE_FALSE, /* Is this a CIS scanner? */ + GT68XX_FLAG_UNTESTED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */ + /* Untested */ +}; + +static GT68xx_Model plustek_ops24_model = { + "plustek-opticpro-s24", /* Name */ + "Plustek", /* Device vendor string */ + "OpticPro S24", /* Device model name */ + "ccd569.fw", /* Name of the firmware file */ + SANE_FALSE, /* Dynamic allocation flag */ + + &mustek_gt6816_command_set, /* Command set used by this scanner */ + + 600, /* maximum optical sensor resolution */ + 1200, /* maximum motor resolution */ + 600, /* base x-res used to calculate geometry */ + 600, /* base y-res used to calculate geometry */ + 1200, /* if ydpi is equal or higher, disable backtracking */ + SANE_TRUE, /* Use base_ydpi for all resolutions */ + + {600, 300, 200, 100, 50, 0}, /* possible x-resolutions */ + {1200, 600, 300, 200, 100, 50, 0}, /* possible y-resolutions */ + {12, 8, 0}, /* possible depths in gray mode */ + {12, 8, 0}, /* possible depths in color mode */ + + SANE_FIX (4.5), /* Start of scan area in mm (x) */ + SANE_FIX (8.5), /* Start of scan area in mm (y) */ + SANE_FIX (218.0), /* Size of scan area in mm (x) */ + SANE_FIX (299.0), /* Size of scan area in mm (y) */ + + SANE_FIX (0.0), /* Start of white strip in mm (y) */ + SANE_FIX (1.0), /* Start of black mark in mm (x) */ + + SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */ + SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */ + SANE_FIX (100.0), /* Size of scan area in TA mode in mm (x) */ + SANE_FIX (100.0), /* Size of scan area in TA mode in mm (y) */ + + SANE_FIX (0.0), /* Start of white strip in TA mode in mm (y) */ + + 48, 24, 0, /* RGB CCD Line-distance correction in pixel */ + 0, /* CCD distcance for CCD with 6 lines) */ + + COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */ + {0x18, 0x1c, 0x16, 0x12, 0x18, 0x1c}, /* Default offset/gain */ + {0x157, 0x157, 0x157}, /* Default exposure parameters */ + SANE_FIX (2.0), /* Default gamma value */ + + SANE_FALSE, /* Is this a CIS scanner? */ + GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE | GT68XX_FLAG_UNTESTED + /* Which flags are needed for this scanner? */ + /* Untested. Based on genius Colorpage Vivid 1200 X*/ +}; + + static GT68xx_Model genius_vivid4_model = { "genius-colorpage-vivid4", /* Name */ "Genius", /* Device vendor string */ @@ -1644,6 +1744,7 @@ /* Tested by Jason Novek. Based on Plustek OpticSlim 2400. */ + static GT68xx_USB_Device_Entry gt68xx_usb_device_list[] = { {0x10000, 0x10000, &unknown_model}, /* used for yet unknown scanners */ {0x055f, 0x0218, &mustek_2400ta_model}, @@ -1665,6 +1766,8 @@ {0x07b3, 0x0401, &plustek_op1248u_model}, /* Same scanner, different id? */ {0x07b3, 0x0402, &plustek_u16b_model}, {0x07b3, 0x0403, &plustek_u16b_model}, /* two ids? 403 seems to be more common */ + {0x07b3, 0x040b, &plustek_ops12_model}, + {0x07b3, 0x040e, &plustek_ops24_model}, {0x07b3, 0x0412, &plustek_opticslim_m12_model}, {0x07b3, 0x0413, &plustek_opticslim1200_model}, {0x07b3, 0x0422, &plustek_opticslim2400_model}, diff -uNr sane-backends-1.0.17/backend/gt68xx_high.c sane-backends-1.0.18/backend/gt68xx_high.c --- sane-backends-1.0.17/backend/gt68xx_high.c 2005-12-06 22:00:35.000000000 +0100 +++ sane-backends-1.0.18/backend/gt68xx_high.c 2006-03-11 14:17:59.000000000 +0100 @@ -2,7 +2,7 @@ Copyright (C) 2002 Sergey Vlasov AFE offset/gain setting by David Stevenson - Copyright (C) 2002 - 2005 Henning Meier-Geinitz + Copyright (C) 2002 - 2006 Henning Meier-Geinitz This file is part of the SANE package. @@ -1164,10 +1164,11 @@ SANE_Int last_white = 0; SANE_Bool first = SANE_TRUE; SANE_Bool message_printed = SANE_FALSE; - struct timeval now; - int secs; + struct timeval now, start_time; + int secs_lamp_on, secs_start; int increase = -5; + gettimeofday (&start_time, 0); do { usleep (200000); @@ -1206,9 +1207,10 @@ values->total_white, last_white); gettimeofday (&now, 0); - secs = now.tv_sec - scanner->lamp_on_time.tv_sec; + secs_lamp_on = now.tv_sec - scanner->lamp_on_time.tv_sec; + secs_start = now.tv_sec - start_time.tv_sec; - if (!message_printed && secs > 5 && secs <= WARMUP_TIME) + if (!message_printed && secs_start > 5) { DBG (0, "Please wait for lamp warm-up\n"); message_printed = SANE_TRUE; @@ -1235,10 +1237,10 @@ } last_white = values->total_white; } - while (secs <= WARMUP_TIME); + while (secs_lamp_on <= WARMUP_TIME); - DBG (3, "gt68xx_wait_lamp_stable: Lamp is stable after %d seconds\n", - secs); + DBG (3, "gt68xx_wait_lamp_stable: Lamp is stable after %d secs (%d secs total)\n", + secs_start, secs_lamp_on); return status; } diff -uNr sane-backends-1.0.17/backend/hp3500.c sane-backends-1.0.18/backend/hp3500.c --- sane-backends-1.0.17/backend/hp3500.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/hp3500.c 2006-02-04 12:34:27.000000000 +0100 @@ -0,0 +1,2959 @@ +/* sane - Scanner Access Now Easy. + + 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 HP ScanJet 3500 series scanners. + Currently supported: + - HP ScanJet 3500C + - HP ScanJet 3530C + - HP ScanJet 3570C + + SANE FLOW DIAGRAM + + - sane_init() : initialize backend, attach scanners + . - sane_get_devices() : query list of scanner devices + . - sane_open() : open a particular scanner device + . . - sane_set_io_mode : set blocking mode + . . - sane_get_select_fd : get scanner fd + . . - sane_get_option_descriptor() : get option information + . . - sane_control_option() : change option values + . . + . . - sane_start() : start image acquisition + . . - sane_get_parameters() : returns actual scan parameters + . . - sane_read() : read image data (from pipe) + . . + . . - sane_cancel() : cancel operation + . - sane_close() : close opened scanner device + - sane_exit() : terminate use of backend + + + There are some device specific routines in this file that are in "#if 0" + sections - these are left in place for documentation purposes in case + somebody wants to implement features that use those routines. + +*/ + +/* ------------------------------------------------------------------------- */ + +#include "sane/config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_LIBC_H +# include /* NeXTStep/OpenStep */ +#endif + +#include "sane/sane.h" +#include "sane/sanei_usb.h" +#include "sane/saneopts.h" +#include "sane/sanei_config.h" +#include "sane/sanei_thread.h" +#include "sane/sanei_backend.h" + +#define RTCMD_GETREG 0x80 +#define RTCMD_READSRAM 0x81 + +#define RTCMD_SETREG 0x88 +#define RTCMD_WRITESRAM 0x89 + +#define RTCMD_NVRAMCONTROL 0x8a + +#define RTCMD_BYTESAVAIL 0x90 +#define RTCMD_READBYTES 0x91 + +#define RT_CHANNEL_ALL 0 +#define RT_CHANNEL_RED 1 +#define RT_CHANNEL_GREEN 2 +#define RT_CHANNEL_BLUE 3 + +typedef int (*rts8801_callback) (void *param, unsigned bytes, void *data); + +#define RTS8801_GREYSCALE 0 +#define RTS8801_COLOUR 1 +#define RTS8801_BW 2 + +#define DEBUG 1 +#define MM_PER_INCH 25.4 +#define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX(number * MM_PER_INCH / 1200) +#define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) * 1200 / MM_PER_INCH + +#define MSG_ERR 1 +#define MSG_USER 5 +#define MSG_INFO 6 +#define FLOW_CONTROL 10 +#define MSG_IO 15 +#define MSG_IO_READ 17 +#define IO_CMD 20 +#define IO_CMD_RES 20 +#define MSG_GET 25 +/* ------------------------------------------------------------------------- */ + +enum hp3500_option +{ + OPT_NUM_OPTS = 0, + + OPT_RESOLUTION, + OPT_GEOMETRY_GROUP, + OPT_TL_X, + OPT_TL_Y, + OPT_BR_X, + OPT_BR_Y, + + NUM_OPTIONS +}; + +typedef struct +{ + int left; + int top; + int right; + int bottom; +} hp3500_rect; + +struct hp3500_data +{ + struct hp3500_data *next; + char *devicename; + + int sfd; + int pipe_r; + int pipe_w; + int reader_pid; + + int resolution; + + time_t last_scan; + + hp3500_rect request_mm; + hp3500_rect actual_mm; + hp3500_rect fullres_pixels; + hp3500_rect actres_pixels; + + int rounded_left; + int rounded_top; + int rounded_right; + int rounded_bottom; + + int bytes_per_scan_line; + int scan_width_pixels; + int scan_height_pixels; + + SANE_Option_Descriptor opt[NUM_OPTIONS]; + SANE_Device sane; +}; + +static struct hp3500_data *first_dev = 0; +static struct hp3500_data **new_dev = &first_dev; +static int num_devices = 0; +static SANE_Int res_list[] = + { 10, 25, 50, 75, 100, 150, 200, 300, 400, 600, 1200 }; +static const SANE_Range range_x = + { 0, SANE_FIX (215.9), SANE_FIX (MM_PER_INCH / 1200) }; +static const SANE_Range range_y = + { 0, SANE_FIX (298.7), SANE_FIX (MM_PER_INCH / 1200) }; + + +static SANE_Status attachScanner (const char *name); +static SANE_Status init_options (struct hp3500_data *scanner); +static int reader_process (void *); +static void calculateDerivedValues (struct hp3500_data *scanner); +static void do_reset (struct hp3500_data *scanner); +static void do_cancel (struct hp3500_data *scanner); + +/* + * used by sane_get_devices + */ +static const SANE_Device **devlist = 0; + +/* + * SANE Interface + */ + + +/** + * Called by SANE initially. + * + * From the SANE spec: + * This function must be called before any other SANE function can be + * called. The behavior of a SANE backend is undefined if this + * function is not called first. The version code of the backend is + * returned in the value pointed to by version_code. If that pointer + * is NULL, no version code is returned. Argument authorize is either + * a pointer to a function that is invoked when the backend requires + * authentication for a specific resource or NULL if the frontend does + * not support authentication. + */ +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + authorize = authorize; /* get rid of compiler warning */ + + DBG_INIT (); + DBG (10, "sane_init\n"); + + sanei_usb_init (); + sanei_thread_init (); + + if (version_code) + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0); + + sanei_usb_find_devices (0x03f0, 0x2205, attachScanner); + sanei_usb_find_devices (0x03f0, 0x2005, attachScanner); + + return SANE_STATUS_GOOD; +} + + +/** + * Called by SANE to find out about supported devices. + * + * From the SANE spec: + * This function can be used to query the list of devices that are + * available. If the function executes successfully, it stores a + * pointer to a NULL terminated array of pointers to SANE_Device + * structures in *device_list. The returned list is guaranteed to + * remain unchanged and valid until (a) another call to this function + * is performed or (b) a call to sane_exit() is performed. This + * function can be called repeatedly to detect when new devices become + * available. If argument local_only is true, only local devices are + * returned (devices directly attached to the machine that SANE is + * running on). If it is false, the device list includes all remote + * devices that are accessible to the SANE library. + * + * SANE does not require that this function is called before a + * sane_open() call is performed. A device name may be specified + * explicitly by a user which would make it unnecessary and + * undesirable to call this function first. + */ +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + int i; + struct hp3500_data *dev; + + DBG (10, "sane_get_devices %d\n", local_only); + + if (devlist) + free (devlist); + devlist = calloc (num_devices + 1, sizeof (SANE_Device *)); + if (!devlist) + return SANE_STATUS_NO_MEM; + + for (dev = first_dev, i = 0; i < num_devices; dev = dev->next) + devlist[i++] = &dev->sane; + devlist[i++] = 0; + + *device_list = devlist; + + return SANE_STATUS_GOOD; +} + + +/** + * Called to establish connection with the scanner. This function will + * also establish meaningful defauls and initialize the options. + * + * From the SANE spec: + * This function is used to establish a connection to a particular + * device. The name of the device to be opened is passed in argument + * name. If the call completes successfully, a handle for the device + * is returned in *h. As a special case, specifying a zero-length + * string as the device requests opening the first available device + * (if there is such a device). + */ +SANE_Status +sane_open (SANE_String_Const name, SANE_Handle * handle) +{ + struct hp3500_data *dev = NULL; + struct hp3500_data *scanner = NULL; + + if (name[0] == 0) + { + DBG (10, "sane_open: no device requested, using default\n"); + if (first_dev) + { + scanner = (struct hp3500_data *) first_dev; + DBG (10, "sane_open: device %s found\n", first_dev->sane.name); + } + } + else + { + DBG (10, "sane_open: device %s requested\n", name); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, name) == 0) + { + DBG (10, "sane_open: device %s found\n", name); + scanner = (struct hp3500_data *) dev; + } + } + } + + if (!scanner) + { + DBG (10, "sane_open: no device found\n"); + return SANE_STATUS_INVAL; + } + + *handle = scanner; + + init_options (scanner); + + scanner->resolution = 600; + scanner->request_mm.left = 0; + scanner->request_mm.top = 0; + scanner->request_mm.right = SCANNER_UNIT_TO_FIXED_MM (10200); + scanner->request_mm.bottom = SCANNER_UNIT_TO_FIXED_MM (14100); + calculateDerivedValues (scanner); + + return SANE_STATUS_GOOD; + +} + + +/** + * An advanced method we don't support but have to define. + */ +SANE_Status +sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking) +{ + DBG (10, "sane_set_io_mode\n"); + DBG (99, "%d %p\n", non_blocking, h); + return SANE_STATUS_UNSUPPORTED; +} + + +/** + * An advanced method we don't support but have to define. + */ +SANE_Status +sane_get_select_fd (SANE_Handle h, SANE_Int * fdp) +{ + struct hp3500_data *scanner = (struct hp3500_data *) h; + DBG (10, "sane_get_select_fd\n"); + *fdp = scanner->pipe_r; + DBG (99, "%p %d\n", h, *fdp); + return SANE_STATUS_GOOD; +} + + +/** + * Returns the options we know. + * + * From the SANE spec: + * This function is used to access option descriptors. The function + * returns the option descriptor for option number n of the device + * represented by handle h. Option number 0 is guaranteed to be a + * valid option. Its value is an integer that specifies the number of + * options that are available for device handle h (the count includes + * option 0). If n is not a valid option index, the function returns + * NULL. The returned option descriptor is guaranteed to remain valid + * (and at the returned address) until the device is closed. + */ +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + struct hp3500_data *scanner = handle; + + DBG (MSG_GET, + "sane_get_option_descriptor: \"%s\"\n", scanner->opt[option].name); + + if ((unsigned) option >= NUM_OPTIONS) + return NULL; + return &scanner->opt[option]; +} + + +/** + * Gets or sets an option value. + * + * From the SANE spec: + * This function is used to set or inquire the current value of option + * number n of the device represented by handle h. The manner in which + * the option is controlled is specified by parameter action. The + * possible values of this parameter are described in more detail + * below. The value of the option is passed through argument val. It + * is a pointer to the memory that holds the option value. The memory + * area pointed to by v must be big enough to hold the entire option + * value (determined by member size in the corresponding option + * descriptor). + * + * The only exception to this rule is that when setting the value of a + * string option, the string pointed to by argument v may be shorter + * since the backend will stop reading the option value upon + * encountering the first NUL terminator in the string. If argument i + * is not NULL, the value of *i will be set to provide details on how + * well the request has been met. + */ +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *val, SANE_Int * info) +{ + struct hp3500_data *scanner = (struct hp3500_data *) handle; + SANE_Status status; + SANE_Word cap; + SANE_Int dummy; + + /* Make sure that all those statements involving *info cannot break (better + * than having to do "if (info) ..." everywhere!) + */ + if (info == 0) + info = &dummy; + + *info = 0; + + if (option >= NUM_OPTIONS) + return SANE_STATUS_INVAL; + + cap = scanner->opt[option].cap; + + /* + * SANE_ACTION_GET_VALUE: We have to find out the current setting and + * return it in a human-readable form (often, text). + */ + if (action == SANE_ACTION_GET_VALUE) + { + DBG (MSG_GET, "sane_control_option: get value \"%s\"\n", + scanner->opt[option].name); + DBG (11, "\tcap = %d\n", cap); + + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + DBG (10, "\tinactive\n"); + return SANE_STATUS_INVAL; + } + + switch (option) + { + case OPT_NUM_OPTS: + *(SANE_Word *) val = NUM_OPTIONS; + return SANE_STATUS_GOOD; + + case OPT_RESOLUTION: + *(SANE_Word *) val = scanner->resolution; + return SANE_STATUS_GOOD; + + case OPT_TL_X: + *(SANE_Word *) val = scanner->request_mm.left; + return SANE_STATUS_GOOD; + + case OPT_TL_Y: + *(SANE_Word *) val = scanner->request_mm.top; + return SANE_STATUS_GOOD; + + case OPT_BR_X: + *(SANE_Word *) val = scanner->request_mm.right; + return SANE_STATUS_GOOD; + + case OPT_BR_Y: + *(SANE_Word *) val = scanner->request_mm.bottom; + return SANE_STATUS_GOOD; + } + } + else if (action == SANE_ACTION_SET_VALUE) + { + DBG (10, "sane_control_option: set value \"%s\"\n", + scanner->opt[option].name); + + if (!SANE_OPTION_IS_ACTIVE (cap)) + { + DBG (10, "\tinactive\n"); + return SANE_STATUS_INVAL; + } + + if (!SANE_OPTION_IS_SETTABLE (cap)) + { + DBG (10, "\tnot settable\n"); + return SANE_STATUS_INVAL; + } + + status = sanei_constrain_value (scanner->opt + option, val, info); + if (status != SANE_STATUS_GOOD) + { + DBG (10, "\tbad value\n"); + return status; + } + + /* + * Note - for those options which can assume one of a list of + * valid values, we can safely assume that they will have + * exactly one of those values because that's what + * sanei_constrain_value does. Hence no "else: invalid" branches + * below. + */ + switch (option) + { + case OPT_RESOLUTION: + if (scanner->resolution == *(SANE_Word *) val) + { + return SANE_STATUS_GOOD; + } + scanner->resolution = (*(SANE_Word *) val); + calculateDerivedValues (scanner); + *info |= SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_GOOD; + + case OPT_TL_X: + if (scanner->request_mm.left == *(SANE_Word *) val) + return SANE_STATUS_GOOD; + scanner->request_mm.left = *(SANE_Word *) val; + calculateDerivedValues (scanner); + if (scanner->actual_mm.left != scanner->request_mm.left) + *info |= SANE_INFO_INEXACT; + *info |= SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_GOOD; + + case OPT_TL_Y: + if (scanner->request_mm.top == *(SANE_Word *) val) + return SANE_STATUS_GOOD; + scanner->request_mm.top = *(SANE_Word *) val; + calculateDerivedValues (scanner); + if (scanner->actual_mm.top != scanner->request_mm.top) + *info |= SANE_INFO_INEXACT; + *info |= SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_GOOD; + + case OPT_BR_X: + if (scanner->request_mm.right == *(SANE_Word *) val) + { + return SANE_STATUS_GOOD; + } + scanner->request_mm.right = *(SANE_Word *) val; + calculateDerivedValues (scanner); + if (scanner->actual_mm.right != scanner->request_mm.right) + *info |= SANE_INFO_INEXACT; + *info |= SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_GOOD; + + case OPT_BR_Y: + if (scanner->request_mm.bottom == *(SANE_Word *) val) + { + return SANE_STATUS_GOOD; + } + scanner->request_mm.bottom = *(SANE_Word *) val; + calculateDerivedValues (scanner); + if (scanner->actual_mm.bottom != scanner->request_mm.bottom) + *info |= SANE_INFO_INEXACT; + *info |= SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_GOOD; + } /* switch */ + } /* else */ + return SANE_STATUS_INVAL; +} + +/** + * Called by SANE when a page acquisition operation is to be started. + * + */ +SANE_Status +sane_start (SANE_Handle handle) +{ + struct hp3500_data *scanner = handle; + int defaultFds[2]; + int ret; + + DBG (10, "sane_start\n"); + + if (scanner->sfd < 0) + { + /* first call */ + DBG (10, "sane_start opening USB device\n"); + if (sanei_usb_open (scanner->sane.name, &(scanner->sfd)) != + SANE_STATUS_GOOD) + { + DBG (MSG_ERR, + "sane_start: open of %s failed:\n", scanner->sane.name); + return SANE_STATUS_INVAL; + } + } + + calculateDerivedValues (scanner); + + DBG (10, "\tbytes per line = %d\n", scanner->bytes_per_scan_line); + DBG (10, "\tpixels_per_line = %d\n", scanner->scan_width_pixels); + DBG (10, "\tlines = %d\n", scanner->scan_height_pixels); + + + /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */ + if (pipe (defaultFds) < 0) + { + DBG (MSG_ERR, "ERROR: could not create pipe\n"); + do_cancel (scanner); + return SANE_STATUS_IO_ERROR; + } + + scanner->pipe_r = defaultFds[0]; + scanner->pipe_w = defaultFds[1]; + + ret = SANE_STATUS_GOOD; + + scanner->reader_pid = sanei_thread_begin (reader_process, scanner); + time (&scanner->last_scan); + + if (scanner->reader_pid == -1) + { + DBG (MSG_ERR, "cannot fork reader process.\n"); + DBG (MSG_ERR, "%s", strerror (errno)); + ret = SANE_STATUS_IO_ERROR; + } + + if (sanei_thread_is_forked ()) + { + close (scanner->pipe_w); + } + + if (ret == SANE_STATUS_GOOD) + { + DBG (10, "sane_start: ok\n"); + } + + return ret; +} + + +/** + * Called by SANE to retrieve information about the type of data + * that the current scan will return. + * + * From the SANE spec: + * This function is used to obtain the current scan parameters. The + * returned parameters are guaranteed to be accurate between the time + * a scan has been started (sane_start() has been called) and the + * completion of that request. Outside of that window, the returned + * values are best-effort estimates of what the parameters will be + * when sane_start() gets invoked. + * + * Calling this function before a scan has actually started allows, + * for example, to get an estimate of how big the scanned image will + * be. The parameters passed to this function are the handle h of the + * device for which the parameters should be obtained and a pointer p + * to a parameter structure. + */ +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + struct hp3500_data *scanner = (struct hp3500_data *) handle; + + + DBG (10, "sane_get_parameters\n"); + + calculateDerivedValues (scanner); + + params->format = SANE_FRAME_RGB; + params->depth = 8; /* internally we treat this as 24 */ + + params->pixels_per_line = scanner->scan_width_pixels; + params->lines = scanner->scan_height_pixels; + + params->bytes_per_line = scanner->bytes_per_scan_line; + + params->last_frame = 1; + DBG (10, "\tdepth %d\n", params->depth); + DBG (10, "\tlines %d\n", params->lines); + DBG (10, "\tpixels_per_line %d\n", params->pixels_per_line); + DBG (10, "\tbytes_per_line %d\n", params->bytes_per_line); + return SANE_STATUS_GOOD; +} + + +/** + * Called by SANE to read data. + * + * In this implementation, sane_read does nothing much besides reading + * data from a pipe and handing it back. On the other end of the pipe + * there's the reader process which gets data from the scanner and + * stuffs it into the pipe. + * + * From the SANE spec: + * This function is used to read image data from the device + * represented by handle h. Argument buf is a pointer to a memory + * area that is at least maxlen bytes long. The number of bytes + * returned is stored in *len. A backend must set this to zero when + * the call fails (i.e., when a status other than SANE_STATUS_GOOD is + * returned). + * + * When the call succeeds, the number of bytes returned can be + * anywhere in the range from 0 to maxlen bytes. + */ +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * buf, + SANE_Int max_len, SANE_Int * len) +{ + struct hp3500_data *scanner = (struct hp3500_data *) handle; + ssize_t nread; + int source = scanner->pipe_r; + + *len = 0; + + nread = read (source, buf, max_len); + DBG (30, "sane_read: read %ld bytes of %ld\n", + (long) nread, (long) max_len); + + if (nread < 0) + { + if (errno == EAGAIN) + { + return SANE_STATUS_GOOD; + } + else + { + do_cancel (scanner); + return SANE_STATUS_IO_ERROR; + } + } + + *len = nread; + + if (nread == 0) + { + close (source); + DBG (10, "sane_read: pipe closed\n"); + return SANE_STATUS_EOF; + } + + return SANE_STATUS_GOOD; +} /* sane_read */ + + +/** + * Cancels a scan. + * + * It has been said on the mailing list that sane_cancel is a bit of a + * misnomer because it is routinely called to signal the end of a + * batch - quoting David Mosberger-Tang: + * + * > In other words, the idea is to have sane_start() be called, and + * > collect as many images as the frontend wants (which could in turn + * > consist of multiple frames each as indicated by frame-type) and + * > when the frontend is done, it should call sane_cancel(). + * > Sometimes it's better to think of sane_cancel() as "sane_stop()" + * > but that name would have had some misleading connotations as + * > well, that's why we stuck with "cancel". + * + * The current consensus regarding duplex and ADF scans seems to be + * the following call sequence: sane_start; sane_read (repeat until + * EOF); sane_start; sane_read... and then call sane_cancel if the + * batch is at an end. I.e. do not call sane_cancel during the run but + * as soon as you get a SANE_STATUS_NO_DOCS. + * + * From the SANE spec: + * This function is used to immediately or as quickly as possible + * cancel the currently pending operation of the device represented by + * handle h. This function can be called at any time (as long as + * handle h is a valid handle) but usually affects long-running + * operations only (such as image is acquisition). It is safe to call + * this function asynchronously (e.g., from within a signal handler). + * It is important to note that completion of this operaton does not + * imply that the currently pending operation has been cancelled. It + * only guarantees that cancellation has been initiated. Cancellation + * completes only when the cancelled call returns (typically with a + * status value of SANE_STATUS_CANCELLED). Since the SANE API does + * not require any other operations to be re-entrant, this implies + * that a frontend must not call any other operation until the + * cancelled operation has returned. + */ +void +sane_cancel (SANE_Handle h) +{ + DBG (10, "sane_cancel\n"); + do_cancel ((struct hp3500_data *) h); +} + + +/** + * Ends use of the scanner. + * + * From the SANE spec: + * This function terminates the association between the device handle + * passed in argument h and the device it represents. If the device is + * presently active, a call to sane_cancel() is performed first. After + * this function returns, handle h must not be used anymore. + */ +void +sane_close (SANE_Handle handle) +{ + DBG (10, "sane_close\n"); + do_reset (handle); + do_cancel (handle); +} + + +/** + * Terminates the backend. + * + * From the SANE spec: + * This function must be called to terminate use of a backend. The + * function will first close all device handles that still might be + * open (it is recommended to close device handles explicitly through + * a call to sane_clo-se(), but backends are required to release all + * resources upon a call to this function). After this function + * returns, no function other than sane_init() may be called + * (regardless of the status value returned by sane_exit(). Neglecting + * to call this function may result in some resources not being + * released properly. + */ +void +sane_exit (void) +{ + struct hp3500_data *dev, *next; + + DBG (10, "sane_exit\n"); + + for (dev = first_dev; dev; dev = next) + { + next = dev->next; + free (dev->devicename); + free (dev); + } + + if (devlist) + free (devlist); +} + +/* + * The scanning code + */ + +static SANE_Status +attachScanner (const char *devicename) +{ + struct hp3500_data *dev; + + DBG (15, "attach_scanner: %s\n", devicename); + + for (dev = first_dev; dev; dev = dev->next) + { + if (strcmp (dev->sane.name, devicename) == 0) + { + DBG (5, "attach_scanner: scanner already attached (is ok)!\n"); + return SANE_STATUS_GOOD; + } + } + + + if (NULL == (dev = malloc (sizeof (*dev)))) + return SANE_STATUS_NO_MEM; + memset (dev, 0, sizeof (*dev)); + + dev->devicename = strdup (devicename); + dev->sfd = -1; + dev->last_scan = 0; + dev->reader_pid = 0; + dev->pipe_r = dev->pipe_w = -1; + + dev->sane.name = dev->devicename; + dev->sane.vendor = "Hewlett-Packard"; + dev->sane.model = "ScanJet 3500"; + dev->sane.type = "scanner"; + + ++num_devices; + *new_dev = dev; + + DBG (15, "attach_scanner: done\n"); + + return SANE_STATUS_GOOD; +} + +static SANE_Status +init_options (struct hp3500_data *scanner) +{ + int i; + SANE_Option_Descriptor *opt; + + memset (scanner->opt, 0, sizeof (scanner->opt)); + + for (i = 0; i < NUM_OPTIONS; ++i) + { + scanner->opt[i].name = "filler"; + scanner->opt[i].size = sizeof (SANE_Word); + scanner->opt[i].cap = SANE_CAP_INACTIVE; + } + + opt = scanner->opt + OPT_NUM_OPTS; + opt->title = SANE_TITLE_NUM_OPTIONS; + opt->desc = SANE_DESC_NUM_OPTIONS; + opt->cap = SANE_CAP_SOFT_DETECT; + + opt = scanner->opt + OPT_RESOLUTION; + opt->name = SANE_NAME_SCAN_RESOLUTION; + opt->title = SANE_TITLE_SCAN_RESOLUTION; + opt->desc = SANE_DESC_SCAN_RESOLUTION; + opt->type = SANE_TYPE_INT; + opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; + opt->constraint.word_list = res_list; + opt->unit = SANE_UNIT_DPI; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + opt = scanner->opt + OPT_GEOMETRY_GROUP; + opt->title = "Geometry"; + opt->desc = ""; + opt->type = SANE_TYPE_GROUP; + opt->constraint_type = SANE_CONSTRAINT_NONE; + + opt = scanner->opt + OPT_TL_X; + opt->name = SANE_NAME_SCAN_TL_X; + opt->title = SANE_TITLE_SCAN_TL_X; + opt->desc = SANE_DESC_SCAN_TL_X; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &range_x; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + opt = scanner->opt + OPT_TL_Y; + opt->name = SANE_NAME_SCAN_TL_Y; + opt->title = SANE_TITLE_SCAN_TL_Y; + opt->desc = SANE_DESC_SCAN_TL_Y; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &range_y; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + opt = scanner->opt + OPT_BR_X; + opt->name = SANE_NAME_SCAN_BR_X; + opt->title = SANE_TITLE_SCAN_BR_X; + opt->desc = SANE_DESC_SCAN_BR_X; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &range_x; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + opt = scanner->opt + OPT_BR_Y; + opt->name = SANE_NAME_SCAN_BR_Y; + opt->title = SANE_TITLE_SCAN_BR_Y; + opt->desc = SANE_DESC_SCAN_BR_Y; + opt->type = SANE_TYPE_FIXED; + opt->unit = SANE_UNIT_MM; + opt->constraint_type = SANE_CONSTRAINT_RANGE; + opt->constraint.range = &range_y; + opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + + return SANE_STATUS_GOOD; +} + +static void +do_reset (struct hp3500_data *scanner) +{ + scanner = scanner; /* kill warning */ +} + +static void +do_cancel (struct hp3500_data *scanner) +{ + if (scanner->reader_pid > 0) + { + + if (sanei_thread_kill (scanner->reader_pid) == 0) + { + int exit_status; + + sanei_thread_waitpid(scanner->reader_pid, &exit_status); + } + scanner->reader_pid = 0; + } + if (scanner->pipe_r >= 0) + { + close(scanner->pipe_r); + scanner->pipe_r = -1; + } +} + +static void +calculateDerivedValues (struct hp3500_data *scanner) +{ + + DBG (12, "calculateDerivedValues\n"); + + /* Convert the SANE_FIXED values for the scan area into 1/1200 inch + * scanner units */ + + scanner->fullres_pixels.left = + FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.left); + scanner->fullres_pixels.top = + FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.top); + scanner->fullres_pixels.right = + FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.right); + scanner->fullres_pixels.bottom = + FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.bottom); + + DBG (12, "\tleft margin: %u\n", scanner->fullres_pixels.left); + DBG (12, "\ttop margin: %u\n", scanner->fullres_pixels.top); + DBG (12, "\tright margin: %u\n", scanner->fullres_pixels.right); + DBG (12, "\tbottom margin: %u\n", scanner->fullres_pixels.bottom); + + + scanner->scan_width_pixels = + scanner->resolution * (scanner->fullres_pixels.right - + scanner->fullres_pixels.left) / 1200; + scanner->scan_height_pixels = + scanner->resolution * (scanner->fullres_pixels.bottom - + scanner->fullres_pixels.top) / 1200; + scanner->bytes_per_scan_line = scanner->scan_width_pixels * 3; + + if (scanner->scan_width_pixels < 1) + scanner->scan_width_pixels = 1; + if (scanner->scan_height_pixels < 1) + scanner->scan_height_pixels = 1; + + scanner->actres_pixels.left = + scanner->fullres_pixels.left * scanner->resolution / 1200; + scanner->actres_pixels.top = + scanner->fullres_pixels.top * scanner->resolution / 1200; + scanner->actres_pixels.right = + scanner->actres_pixels.left + scanner->scan_width_pixels; + scanner->actres_pixels.bottom = + scanner->actres_pixels.top + scanner->scan_height_pixels; + + scanner->actual_mm.left = + SCANNER_UNIT_TO_FIXED_MM (scanner->fullres_pixels.left); + scanner->actual_mm.top = + SCANNER_UNIT_TO_FIXED_MM (scanner->fullres_pixels.top); + scanner->actual_mm.bottom = + SCANNER_UNIT_TO_FIXED_MM (scanner->scan_width_pixels * 1200 / + scanner->resolution); + scanner->actual_mm.right = + SCANNER_UNIT_TO_FIXED_MM (scanner->scan_height_pixels * 1200 / + scanner->resolution); + + DBG (12, "calculateDerivedValues: ok\n"); +} + +/* From here on in we have the original code written for the scanner demo */ + +#define MAX_COMMANDS_BYTES 131072 +#define MAX_READ_COMMANDS 1 /* Issuing more than one register + * read command in a single request + * seems to put the device in an + * unpredictable state. + */ +#define MAX_READ_BYTES 0xffc0 + +#define REG_DESTINATION_POSITION 0x60 +#define REG_MOVE_CONTROL_TEST 0xb3 + +static int command_reads_outstanding = 0; +static int command_bytes_outstanding = 0; +static unsigned char command_buffer[MAX_COMMANDS_BYTES]; +static int receive_bytes_outstanding = 0; +static char *command_readmem_outstanding[MAX_READ_COMMANDS]; +static int command_readbytes_outstanding[MAX_READ_COMMANDS]; +static unsigned char sram_access_method = 0; +static unsigned sram_size = 0; +static int udh; + +static int +rt_execute_commands (void) +{ + SANE_Status result; + size_t bytes; + + if (!command_bytes_outstanding) + return 0; + + bytes = command_bytes_outstanding; + + result = sanei_usb_write_bulk (udh, /* 0x02, */ command_buffer, &bytes); + + if (result == SANE_STATUS_GOOD && receive_bytes_outstanding) + { + unsigned char readbuf[MAX_READ_BYTES]; + int total_read = 0; + + do + { + bytes = receive_bytes_outstanding - total_read; + result = sanei_usb_read_bulk (udh, + /* 0x81, */ + readbuf + total_read, &bytes); + if (result == SANE_STATUS_GOOD) + total_read += bytes; + else + break; + } + while (total_read < receive_bytes_outstanding); + if (result == SANE_STATUS_GOOD) + { + unsigned char *readptr; + int i; + + for (i = 0, readptr = readbuf; + i < command_reads_outstanding; + readptr += command_readbytes_outstanding[i++]) + { + memcpy (command_readmem_outstanding[i], + readptr, command_readbytes_outstanding[i]); + } + } + } + receive_bytes_outstanding = command_reads_outstanding = + command_bytes_outstanding = 0; + return (result == SANE_STATUS_GOOD) ? 0 : -1; +} + +static int +rt_queue_command (int command, + int reg, + int count, + int bytes, void *data, int readbytes, void *readdata) +{ + int len = 4 + bytes; + unsigned char *buffer; + + if (command_bytes_outstanding + len > MAX_COMMANDS_BYTES || + (readbytes && + ((command_reads_outstanding >= MAX_READ_COMMANDS) || + (receive_bytes_outstanding >= MAX_READ_BYTES)))) + { + if (rt_execute_commands () < 0) + return -1; + } + + buffer = command_buffer + command_bytes_outstanding; + + buffer[0] = command; + buffer[1] = reg; + buffer[2] = count >> 8; + buffer[3] = count; + memcpy (buffer + 4, data, bytes); + command_bytes_outstanding += 4 + bytes; + if (readbytes) + { + command_readbytes_outstanding[command_reads_outstanding] = readbytes; + command_readmem_outstanding[command_reads_outstanding] = readdata; + receive_bytes_outstanding += readbytes; + ++command_reads_outstanding; + } + + return 0; +} + +static int +rt_send_command_immediate (int command, + int reg, + int count, + int bytes, + void *data, int readbytes, void *readdata) +{ + rt_queue_command (command, reg, count, bytes, data, readbytes, readdata); + return rt_execute_commands (); +} + +static int +rt_queue_read_register (int reg, int bytes, void *data) +{ + return rt_queue_command (RTCMD_GETREG, reg, bytes, 0, 0, bytes, data); +} + +static int +rt_read_register_immediate (int reg, int bytes, void *data) +{ + if (rt_queue_read_register (reg, bytes, data) < 0) + return -1; + return rt_execute_commands (); +} + +static int +rt_queue_set_register (int reg, int bytes, void *data) +{ + return rt_queue_command (RTCMD_SETREG, reg, bytes, bytes, data, 0, 0); +} + +static int +rt_set_register_immediate (int reg, int bytes, void *data) +{ + if (reg < 0xb3 && reg + bytes > 0xb3) + { + int bytes_in_first_block = 0xb3 - reg; + + if (rt_set_register_immediate (reg, bytes_in_first_block, data) < 0 || + rt_set_register_immediate (0xb4, bytes - bytes_in_first_block - 1, + (char *) data + bytes_in_first_block + 1) < 0) + return -1; + return 0; + } + if (rt_queue_set_register (reg, bytes, data) < 0) + return -1; + return rt_execute_commands (); +} + +static int +rt_set_one_register (int reg, int val) +{ + char r = val; + + return rt_set_register_immediate (reg, 1, &r); +} + +static int +rt_write_sram (int bytes, void *data) +{ + return rt_send_command_immediate (RTCMD_WRITESRAM, 0, bytes, bytes, data, 0, + 0); +} + +static int +rt_read_sram (int bytes, void *data) +{ + return rt_send_command_immediate (RTCMD_READSRAM, 0, bytes, 0, 0, bytes, + data); +} + +static int +rt_set_sram_page (int page) +{ + unsigned char regs[2]; + + regs[0] = page; + regs[1] = page >> 8; + + return rt_set_register_immediate (0x91, 2, regs); +} + +static int +rt_detect_sram (unsigned *totalbytes, unsigned char *r93setting) +{ + char data[0x818]; + char testbuf[0x818]; + unsigned i; + int test_values[] = { 6, 2, 1, -1 }; + + for (i = 0; i < sizeof (data); ++i) + data[i] = i % 0x61; + + + for (i = 0; test_values[i] != -1; ++i) + { + if (rt_set_one_register (0x93, test_values[i]) || + rt_set_sram_page (0x81) || + rt_write_sram (0x818, data) || + rt_set_sram_page (0x81) || rt_read_sram (0x818, testbuf)) + return -1; + if (!memcmp (testbuf, data, 0x818)) + { + sram_access_method = test_values[i]; + if (r93setting) + *r93setting = sram_access_method; + break; + } + } + if (!sram_access_method) + return -1; + + for (i = 0; i < 16; ++i) + { + int j; + char write_data[32]; + char read_data[32]; + int pagesetting; + + for (j = 0; j < 16; j++) + { + write_data[j * 2] = j * 2; + write_data[j * 2 + 1] = i; + } + + pagesetting = i * 4096; + + + if (rt_set_sram_page (pagesetting) < 0 || + rt_write_sram (32, write_data) < 0) + return -1; + if (i) + { + if (rt_set_sram_page (0) < 0 || rt_read_sram (32, read_data) < 0) + return -1; + if (!memcmp (read_data, write_data, 32)) + { + sram_size = i * 0x20000; + if (totalbytes) + *totalbytes = sram_size; + return 0; + } + } + } + return -1; +} + +static int +rt_get_available_bytes (void) +{ + unsigned char data[3]; + + if (rt_queue_command (RTCMD_BYTESAVAIL, 0, 3, 0, 0, 3, data) < 0 || + rt_execute_commands () < 0) + return -1; + return ((unsigned) data[0]) | + ((unsigned) data[1] << 8) | ((unsigned) data[2] << 16); +} + +static int +rt_get_data (int bytes, void *data) +{ + int total = 0; + + while (bytes) + { + int bytesnow = bytes; + + if (bytesnow > 0xffc0) + bytesnow = 0xffc0; + if (rt_queue_command + (RTCMD_READBYTES, 0, bytesnow, 0, 0, bytesnow, data) < 0 + || rt_execute_commands () < 0) + return -1; + total += bytesnow; + bytes -= bytesnow; + data = (char *) data + bytesnow; + } + return 0; +} + +static int +rt_is_moving (void) +{ + char r; + + if (rt_read_register_immediate (REG_MOVE_CONTROL_TEST, 1, &r) < 0) + return -1; + if (r == 0x08) + return 1; + return 0; +} + +static int +rt_is_rewound (void) +{ + char r; + + if (rt_read_register_immediate (0x1d, 1, &r) < 0) + return -1; + if (r & 0x02) + return 1; + return 0; +} + +static int +rt_set_direction_forwards (unsigned char *regs) +{ + regs[0xc6] |= 0x08; + return 0; +} + +static int +rt_set_direction_rewind (unsigned char *regs) +{ + regs[0xc6] &= 0xf7; + return 0; +} + +static int +rt_set_stop_when_rewound (unsigned char *regs, int stop) +{ + if (stop) + regs[0xb2] |= 0x10; + else + regs[0xb2] &= 0xef; + return 0; +} + +static int +rt_start_moving (void) +{ + if (rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 8) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 8) < 0) + return -1; + return 0; +} + +static int +rt_stop_moving (void) +{ + if (rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0) + return -1; + return 0; +} + +static int +rt_set_powersave_mode (int enable) +{ + unsigned char r; + + if (rt_read_register_immediate (REG_MOVE_CONTROL_TEST, 1, &r) < 0) + return -1; + if (r & 0x04) + { + if (enable == 1) + return 0; + r &= ~0x04; + } + else + { + if (enable == 0) + return 0; + r |= 0x04; + } + if (rt_set_one_register (REG_MOVE_CONTROL_TEST, r) < 0 || + rt_set_one_register (REG_MOVE_CONTROL_TEST, r) < 0) + return -1; + return 0; +} + +static int +rt_turn_off_lamp (void) +{ + return rt_set_one_register (0x3a, 0); +} + +static int +rt_turn_on_lamp (void) +{ + char r3a; + char r10; + char r58; + + if (rt_read_register_immediate (0x3a, 1, &r3a) < 0 || + rt_read_register_immediate (0x10, 1, &r10) < 0 || + rt_read_register_immediate (0x58, 1, &r58) < 0) + return -1; + r3a |= 0x80; + r10 |= 0x01; + r58 &= 0x0f; + if (rt_set_one_register (0x3a, r3a) < 0 || + rt_set_one_register (0x10, r10) < 0 || + rt_set_one_register (0x58, r58) < 0) + return -1; + return 0; +} + +static int +rt_set_value_lsbfirst (unsigned char *regs, + int firstreg, int totalregs, unsigned value) +{ + while (totalregs--) + { + regs[firstreg++] = value & 0xff; + value >>= 8; + } + return 0; +} + +#if 0 +static int +rt_set_value_msbfirst (unsigned char *regs, + int firstreg, int totalregs, unsigned value) +{ + while (totalregs--) + { + regs[firstreg + totalregs] = value & 0xff; + value >>= 8; + } + return 0; +} +#endif + +static int +rt_set_ccd_shift_clock_multiplier (unsigned char *regs, unsigned value) +{ + return rt_set_value_lsbfirst (regs, 0xf0, 3, value); +} + +static int +rt_set_ccd_clock_reset_interval (unsigned char *regs, unsigned value) +{ + return rt_set_value_lsbfirst (regs, 0xf9, 3, value); +} + +static int +rt_set_ccd_clamp_clock_multiplier (unsigned char *regs, unsigned value) +{ + return rt_set_value_lsbfirst (regs, 0xfc, 3, value); +} + +static int +rt_set_movement_pattern (unsigned char *regs, unsigned value) +{ + return rt_set_value_lsbfirst (regs, 0xc0, 3, value); +} + +static int +rt_set_motor_movement_clock_multiplier (unsigned char *regs, unsigned value) +{ + regs[0x40] = (regs[0x40] & ~0xc0) | (value << 6); + return 0; +} + +static int +rt_set_motor_type (unsigned char *regs, unsigned value) +{ + regs[0xc9] = (regs[0xc9] & 0xf8) | (value & 0x7); + return 0; +} + +static int +rt_set_noscan_distance (unsigned char *regs, unsigned value) +{ + DBG (10, "Setting distance without scanning to %d\n", value); + return rt_set_value_lsbfirst (regs, 0x60, 2, value); +} + +static int +rt_set_total_distance (unsigned char *regs, unsigned value) +{ + DBG (10, "Setting total distance to %d\n", value); + return rt_set_value_lsbfirst (regs, 0x62, 2, value); +} + +static int +rt_set_scanline_start (unsigned char *regs, unsigned value) +{ + return rt_set_value_lsbfirst (regs, 0x66, 2, value); +} + +static int +rt_set_scanline_end (unsigned char *regs, unsigned value) +{ + return rt_set_value_lsbfirst (regs, 0x6c, 2, value); +} + +static int +rt_set_basic_calibration (unsigned char *regs, + int redoffset1, + int redoffset2, + int redgain, + int greenoffset1, + int greenoffset2, + int greengain, + int blueoffset1, int blueoffset2, int bluegain) +{ + regs[0x05] = redoffset1; + regs[0x02] = redoffset2; + regs[0x08] = redgain; + regs[0x06] = greenoffset1; + regs[0x03] = greenoffset2; + regs[0x09] = greengain; + regs[0x07] = blueoffset1; + regs[0x04] = blueoffset2; + regs[0x0a] = bluegain; + return 0; +} + +static int +rt_set_calibration_addresses (unsigned char *regs, + unsigned redaddr, + unsigned blueaddr, unsigned greenaddr) +{ + regs[0x84] = redaddr; + regs[0x8e] = (regs[0x8e] & 0x0f) | ((redaddr >> 4) & 0xf0); + rt_set_value_lsbfirst (regs, 0x85, 2, blueaddr); + rt_set_value_lsbfirst (regs, 0x87, 2, greenaddr); + return 0; +} + +static int +rt_set_lamp_duty_cycle (unsigned char *regs, + int enable, int frequency, int offduty) +{ + if (enable) + regs[0x3b] |= 0x80; + else + regs[0x3b] &= 0x7f; + + regs[0x3b] = + (regs[0x3b] & 0x80) | ((frequency & 0x7) << 4) | (offduty & 0x0f); + regs[0x3d] = (regs[0x3d] & 0x7f) | ((frequency & 0x8) << 4); + return 0; +} + +static int +rt_set_data_feed_on (unsigned char *regs) +{ + regs[0xb2] &= ~0x04; + return 0; +} + +static int +rt_enable_ccd (unsigned char *regs, int enable) +{ + if (enable) + regs[0x00] &= ~0x10; + else + regs[0x00] |= 0x10; + return 0; +} + +static int +rt_set_cdss (unsigned char *regs, int val1, int val2) +{ + regs[0x28] = (regs[0x28] & 0xe0) | (val1 & 0x1f); + regs[0x2a] = (regs[0x2a] & 0xe0) | (val2 & 0x1f); + return 0; +} + +static int +rt_set_cdsc (unsigned char *regs, int val1, int val2) +{ + regs[0x29] = (regs[0x29] & 0xe0) | (val1 & 0x1f); + regs[0x2b] = (regs[0x2b] & 0xe0) | (val2 & 0x1f); + return 0; +} + +static int +rt_update_after_setting_cdss2 (unsigned char *regs) +{ + int fullcolour = (!(regs[0x2f] & 0xc0) && (regs[0x2f] & 0x04)); + int value = regs[0x2a] & 0x1f; + + regs[0x2a] = (regs[0x2a] & 0xe0) | (value & 0x1f); + + if (fullcolour) + value *= 3; + if ((regs[0x40] & 0xc0) == 0x40) + value += 17; + else + value += 16; + + regs[0x2c] = (regs[0x2c] & 0xe0) | (value % 24); + regs[0x2d] = (regs[0x2d] & 0xe0) | ((value + 2) % 24); + return 0; +} + +static int +rt_set_cph0s (unsigned char *regs, int on) +{ + if (on) + regs[0x2d] |= 0x20; /* 1200dpi horizontal coordinate space */ + else + regs[0x2d] &= ~0x20; /* 600dpi horizontal coordinate space */ + return 0; +} + +static int +rt_set_cvtr_lm (unsigned char *regs, int val1, int val2, int val3) +{ + regs[0x28] = (regs[0x28] & ~0xe0) | (val1 << 5); + regs[0x29] = (regs[0x29] & ~0xe0) | (val2 << 5); + regs[0x2a] = (regs[0x2a] & ~0xe0) | (val3 << 5); + return 0; +} + +static int +rt_set_cvtr_mpt (unsigned char *regs, int val1, int val2, int val3) +{ + regs[0x3c] = (val1 & 0x0f) | (val2 << 4); + regs[0x3d] = (regs[0x3d] & 0xf0) | (val3 & 0x0f); + return 0; +} + +static int +rt_set_cvtr_wparams (unsigned char *regs, + unsigned fpw, unsigned bpw, unsigned w) +{ + regs[0x31] = (w & 0x0f) | ((bpw << 4) & 0x30) | (fpw << 6); + return 0; +} + +static int +rt_enable_movement (unsigned char *regs, int enable) +{ + if (enable) + regs[0xc3] |= 0x80; + else + regs[0xc3] &= ~0x80; + return 0; +} + +static int +rt_set_scan_frequency (unsigned char *regs, int frequency) +{ + regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f); + return 0; +} + +static int +rt_set_merge_channels (unsigned char *regs, int on) +{ + regs[0x2f] &= ~0x14; + regs[0x2f] |= on ? 0x04 : 0x10; + return 0; +} + +static int +rt_set_channel (unsigned char *regs, int channel) +{ + regs[0x2f] = (regs[0x2f] & ~0xc0) | (channel << 6); + return 0; +} + +static int +rt_set_single_channel_scanning (unsigned char *regs, int on) +{ + if (on) + regs[0x2f] |= 0x20; + else + regs[0x2f] &= ~0x20; + return 0; +} + +static int +rt_set_colour_mode (unsigned char *regs, int on) +{ + if (on) + regs[0x2f] |= 0x02; + else + regs[0x2f] &= ~0x02; + return 0; +} + +static int +rt_set_horizontal_resolution (unsigned char *regs, int resolution) +{ + if (regs[0x2d] & 0x20) + regs[0x7a] = 1200 / resolution; + else + regs[0x7a] = 600 / resolution; + return 0; +} + +static int +rt_set_last_sram_page (unsigned char *regs, int pagenum) +{ + rt_set_value_lsbfirst (regs, 0x8b, 2, pagenum); + return 0; +} + +static int +rt_set_step_size (unsigned char *regs, int stepsize) +{ + rt_set_value_lsbfirst (regs, 0xe2, 2, stepsize); + rt_set_value_lsbfirst (regs, 0xe0, 2, 0); + return 0; +} + +static int +rt_set_all_registers (void const *regs_) +{ + char regs[255]; + + memcpy (regs, regs_, 255); + regs[0x32] &= ~0x40; + + if (rt_set_one_register (0x32, regs[0x32]) < 0 || + rt_set_register_immediate (0, 255, regs) < 0 || + rt_set_one_register (0x32, regs[0x32] | 0x40) < 0) + return -1; + return 0; +} + +static int +rt_adjust_misc_registers (unsigned char *regs) +{ + /* Mostly unknown purposes - probably no need to adjust */ + regs[0xc6] = (regs[0xc6] & 0x0f) | 0x20; /* Purpose unknown - appears to do nothing */ + regs[0x2e] = 0x86; /* ???? - Always has this value */ + regs[0x30] = 2; /* CCPL = 1 */ + regs[0xc9] |= 0x38; /* Doesn't have any obvious effect, but the Windows driver does this */ + return 0; +} + + +#define NVR_MAX_ADDRESS_SIZE 11 +#define NVR_MAX_OPCODE_SIZE 3 +#define NVR_DATA_SIZE 8 +#define NVR_MAX_COMMAND_SIZE ((NVR_MAX_ADDRESS_SIZE + \ + NVR_MAX_OPCODE_SIZE + \ + NVR_DATA_SIZE) * 2 + 1) + +static int +rt_nvram_enable_controller (int enable) +{ + unsigned char r; + + if (rt_read_register_immediate (0x1d, 1, &r) < 0) + return -1; + if (enable) + r |= 1; + else + r &= ~1; + return rt_set_one_register (0x1d, r); + +} + +static int +rt_nvram_init_command (void) +{ + unsigned char regs[13]; + + if (rt_read_register_immediate (0x10, 13, regs) < 0) + return -1; + regs[2] |= 0xf0; + regs[4] = (regs[4] & 0x1f) | 0x60; + return rt_set_register_immediate (0x10, 13, regs); +} + +static int +rt_nvram_init_stdvars (int block, int *addrbits, unsigned char *basereg) +{ + int bitsneeded; + int capacity; + + switch (block) + { + case 0: + bitsneeded = 7; + break; + + case 1: + bitsneeded = 9; + break; + + case 2: + bitsneeded = 11; + break; + + default: + bitsneeded = 0; + capacity = 1; + while (capacity < block) + capacity <<= 1, ++bitsneeded; + break; + } + + *addrbits = bitsneeded; + + if (rt_read_register_immediate (0x10, 1, basereg) < 0) + return -1; + + *basereg &= ~0x60; + return 0; +} + +static void +rt_nvram_set_half_bit (unsigned char *buffer, + int value, unsigned char stdbits, int whichhalf) +{ + *buffer = stdbits | (value ? 0x40 : 0) | (whichhalf ? 0x20 : 0); +} + +static void +rt_nvram_set_command_bit (unsigned char *buffer, + int value, unsigned char stdbits) +{ + rt_nvram_set_half_bit (buffer, value, stdbits, 0); + rt_nvram_set_half_bit (buffer + 1, value, stdbits, 1); +} + +static void +rt_nvram_set_addressing_bits (unsigned char *buffer, + int location, + int addressingbits, unsigned char stdbits) +{ + int currentbit = 1 << (addressingbits - 1); + + while (addressingbits--) + { + rt_nvram_set_command_bit (buffer, + (location & currentbit) ? 1 : 0, stdbits); + buffer += 2; + currentbit >>= 1; + } +} + +#if 0 +static int +rt_nvram_enable_write (int addressingbits, int enable, unsigned char stdbits) +{ + unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE]; + int cmdsize = 6 + addressingbits * 2; + + rt_nvram_set_command_bit (cmdbuffer, 1, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 2, 0, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 4, 0, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 6, enable, stdbits); + if (addressingbits > 1) + rt_nvram_set_addressing_bits (cmdbuffer + 8, 0, addressingbits - 1, + stdbits); + + if (rt_nvram_enable_controller (1) < 0 || + rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, cmdsize, cmdsize, + cmdbuffer, 0, 0) < 0 + || rt_nvram_enable_controller (0) < 0) + { + return -1; + } + return 0; +} + +static int +rt_nvram_write (int block, int location, char const *data, int bytes) +{ + int addressingbits; + unsigned char stdbits; + unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE]; + unsigned char *address_bits; + unsigned char *data_bits; + int cmdsize; + + /* This routine doesn't appear to work, but I can't see anything wrong with it */ + if (rt_nvram_init_stdvars (block, &addressingbits, &stdbits) < 0) + return -1; + + cmdsize = (addressingbits + 8) * 2 + 6; + address_bits = cmdbuffer + 6; + data_bits = address_bits + (addressingbits * 2); + + rt_nvram_set_command_bit (cmdbuffer, 1, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 2, 0, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 4, 1, stdbits); + + if (rt_nvram_init_command () < 0 || + rt_nvram_enable_write (addressingbits, 1, stdbits) < 0) + return -1; + + while (bytes--) + { + int i; + + rt_nvram_set_addressing_bits (address_bits, location, addressingbits, + stdbits); + rt_nvram_set_addressing_bits (data_bits, *data++, 8, stdbits); + + if (rt_nvram_enable_controller (1) < 0 || + rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, cmdsize, cmdsize, + cmdbuffer, 0, 0) < 0 + || rt_nvram_enable_controller (0) < 0) + return -1; + + if (rt_nvram_enable_controller (1) < 0) + return -1; + for (i = 0; i < cmdsize; ++i) + { + unsigned char r; + unsigned char cmd; + + rt_nvram_set_half_bit (&cmd, 0, stdbits, i & 1); + if (rt_send_command_immediate + (RTCMD_NVRAMCONTROL, 0, 1, 1, &cmd, 0, 0) < 0 + || rt_read_register_immediate (0x10, 1, &r) < 0) + { + return -1; + } + else if (r & 0x80) + { + break; + } + } + if (rt_nvram_enable_controller (0) < 0) + return -1; + + ++location; + } + + if (rt_nvram_enable_write (addressingbits, 0, stdbits) < 0) + return -1; + return 0; +} +#endif + +static int +rt_nvram_read (int block, int location, unsigned char *data, int bytes) +{ + int addressingbits; + unsigned char stdbits; + unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE]; + unsigned char *address_bits; + unsigned char readbit_command[2]; + int cmdsize; + + if (rt_nvram_init_stdvars (block, &addressingbits, &stdbits) < 0) + return -1; + + cmdsize = addressingbits * 2 + 7; + address_bits = cmdbuffer + 6; + + rt_nvram_set_command_bit (cmdbuffer, 1, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 2, 1, stdbits); + rt_nvram_set_command_bit (cmdbuffer + 4, 0, stdbits); + rt_nvram_set_half_bit (cmdbuffer + cmdsize - 1, 0, stdbits, 0); + + rt_nvram_set_half_bit (readbit_command, 0, stdbits, 1); + rt_nvram_set_half_bit (readbit_command + 1, 0, stdbits, 0); + + if (rt_nvram_init_command () < 0) + return -1; + + while (bytes--) + { + char c = 0; + unsigned char r; + int i; + + rt_nvram_set_addressing_bits (address_bits, location, addressingbits, + stdbits); + + if (rt_nvram_enable_controller (1) < 0 || + rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0x1d, cmdsize, + cmdsize, cmdbuffer, 0, 0) < 0) + return -1; + + for (i = 0; i < 8; ++i) + { + c <<= 1; + + if (rt_send_command_immediate + (RTCMD_NVRAMCONTROL, 0x1d, 2, 2, readbit_command, 0, 0) < 0 + || rt_read_register_immediate (0x10, 1, &r) < 0) + return -1; + if (r & 0x80) + c |= 1; + } + if (rt_nvram_enable_controller (0) < 0) + return -1; + + *data++ = c; + ++location; + } + return 0; +} + +static unsigned char initial_regs[] = { + 0xf5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, + 0xe1, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x06, 0x19, + 0xd0, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x37, + 0xff, 0x0f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x8c, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x20, 0xbc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, + 0x5e, 0xea, 0x5f, 0xea, 0x00, 0x80, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x84, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x02, 0x4b, 0x02, 0x00, 0xec, 0x19, 0xd8, 0x2d, 0x87, 0x02, 0xff, + 0x3f, 0x78, 0x60, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x27, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x12, 0x08, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 +}; + +static struct tg_info__ +{ + int tg_cph0p; + int tg_crsp; + int tg_cclpp; + int tg_cdss1; + int tg_cdsc1; + int tg_cdss2; + int tg_cdsc2; +} tg_info[] = +{ + /* CPH0P CRSP CCLPP CDSS1 CDSC1 CDSS2 CDSC2 */ + { + 0x01FFE0, 0x3c0000, 0x003000, 0xb, 0xd, 0x00, 0x01}, /* NORMAL */ + { + 0x7ff800, 0xf00000, 0x01c000, 0xb, 0xc, 0x14, 0x15} /* DOUBLE */ +}; + +struct resolution_parameters +{ + unsigned resolution; + int reg_39_value; + int reg_c3_value; + int reg_c6_value; + int scan_frequency; + int cph0s; + int red_green_offset; + int green_blue_offset; + int intra_channel_offset; + int motor_movement_clock_multiplier; + int tg; + int step_size; +}; + +static struct resolution_parameters resparms[] = { + /* My values - all work */ + {1200, 3, 6, 4, 2, 1, 22, 22, 4, 2, 0, 0x157b}, + {600, 3, 3, 1, 1, 0, 9, 10, 0, 2, 0, 0x157b}, + {400, 1, 1, 1, 1, 1, 6, 6, 1, 2, 0, 0x157b}, + {300, 3, 3, 3, 1, 0, 5, 4, 0, 2, 1, 0x157b}, + {200, 3, 1, 1, 1, 0, 3, 3, 0, 2, 1, 0x157b}, + {150, 3, 3, 3, 2, 0, 2, 2, 0, 2, 1, 0x157b}, + {100, 3, 1, 3, 1, 0, 1, 1, 0, 2, 1, 0x157b}, + {75, 3, 3, 3, 4, 0, 1, 1, 0, 2, 1, 0x157b}, + {50, 3, 1, 3, 2, 0, 0, 0, 0, 2, 1, 0x157b}, + {25, 3, 1, 3, 4, 0, 0, 0, 0, 2, 1, 0x157b}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x157b} +}; + +struct dcalibdata +{ + unsigned *buffers[3]; + int pixelsperrow; + int pixelnow; + int channelnow; + int rowsdone; +}; + +static int +rts8801_rewind (void) +{ + unsigned char regs[255]; + int n; + + rt_read_register_immediate (0, 255, regs); + + rt_set_noscan_distance (regs, 59998); + rt_set_total_distance (regs, 59999); + + rt_set_stop_when_rewound (regs, 0); + + rt_set_one_register (0xc6, 0); + rt_set_one_register (0xc6, 0); + + rt_set_step_size (regs, 0x0abd); + + rt_set_direction_rewind (regs); + + regs[0x39] = 15; + regs[0xc3] = (regs[0xc3] & 0xf8) | 0x81; + regs[0xc6] = (regs[0xc6] & 0xf8) | 3; + + rt_set_all_registers (regs); + rt_start_moving (); + + while (!rt_is_rewound () && + ((n = rt_get_available_bytes ()) > 0 || rt_is_moving () > 0)) + { + if (n) + { + char buffer[0xffc0]; + + if (n > (int) sizeof (buffer)) + n = sizeof (buffer); + rt_get_data (n, buffer); + } + else + { + usleep (10000); + } + } + + rt_stop_moving (); + return 0; +} + +static int cancelled_scan = 0; + +static int +rts8801_doscan (unsigned width, + unsigned colour, + unsigned red_green_offset, + unsigned green_blue_offset, + unsigned intra_channel_offset, + rts8801_callback cbfunc, + void *params, + int oddfirst, + unsigned char const *calib_info, struct dcalibdata *pdcd) +{ + unsigned rowbytes = 0; + unsigned channels = 0; + unsigned total_rows = 0; + unsigned bytesperchannel; + char *row_buffer; + char *output_buffer; + unsigned buffered_rows; + int rows_to_begin; + int rowbuffer_bytes; + int n; + unsigned rownow = 0; + unsigned bytenow = 0; + char *channel_data[3][2]; + unsigned i; + unsigned j; + int result = 0; + + calib_info = calib_info; /* Kill warning */ + if (cancelled_scan) + return -1; + rt_start_moving (); + + switch (colour) + { + case RTS8801_GREYSCALE: + channels = 1; + rowbytes = width; + bytesperchannel = rowbytes; + break; + + case RTS8801_COLOUR: + channels = 3; + rowbytes = width * 3; + bytesperchannel = width; + break; + + case RTS8801_BW: + channels = 1; + rowbytes = width / 8; + bytesperchannel = rowbytes; + break; + } + + buffered_rows = + red_green_offset + green_blue_offset + intra_channel_offset + 1; + rows_to_begin = buffered_rows; + rowbuffer_bytes = buffered_rows * rowbytes; + row_buffer = (char *) malloc (rowbuffer_bytes); + output_buffer = (char *) malloc (rowbytes); + + for (i = j = 0; i < channels; ++i) + { + if (i == 1) + j += red_green_offset; + else if (i == 2) + j += green_blue_offset; + channel_data[i][1 - oddfirst] = row_buffer + rowbytes * j + width * i; + channel_data[i][oddfirst] = + channel_data[i][1 - oddfirst] + rowbytes * intra_channel_offset; + } + + while (((n = rt_get_available_bytes ()) > 0 || rt_is_moving () > 0) && !cancelled_scan) + { + if (n == 1 && (rt_is_moving () || rt_get_available_bytes () != 1)) + n = 0; + if (n > 0) + { + char buffer[0xffc0]; + + if (n > 0xffc0) + n = 0xffc0; + else if ((n > 1) && (n & 1)) + --n; + if (rt_get_data (n, buffer) >= 0) + { + char *bufnow = buffer; + + while (n) + { + int numcopy = rowbytes - bytenow; + + if (numcopy > n) + numcopy = n; + memcpy (row_buffer + rownow * rowbytes + bytenow, bufnow, + numcopy); + bytenow += numcopy; + bufnow += numcopy; + n -= numcopy; + + if (bytenow == rowbytes) + { + if (!rows_to_begin || !--rows_to_begin) + { + char *outnow = output_buffer; + + for (i = 0; i < width; ++i) + { + for (j = 0; j < channels; ++j) + { + unsigned pix = + (unsigned char) channel_data[j][i & 1][i]; + + if (pdcd) + { + /* 5400 is "magic" - chosen because it works */ + pix = pix * 5400 / pdcd->buffers[j][i]; + if (pix > 255) + pix = 255; + } + + *outnow++ = pix; + } + } + + if (!((*cbfunc) (params, rowbytes, output_buffer))) + break; + + for (i = 0; i < channels; ++i) + { + for (j = 0; j < 2; ++j) + { + channel_data[i][j] += rowbytes; + if (channel_data[i][j] - row_buffer >= + rowbuffer_bytes) + channel_data[i][j] -= rowbuffer_bytes; + } + } + } + ++total_rows; + if (++rownow == buffered_rows) + rownow = 0; + bytenow = 0; + } + } + } + DBG (10, "\rtotal_rows = %d", total_rows); + } + else + { + usleep (10000); + } + } + DBG (10, "\n"); + if (n < 0) + result = -1; + + free (output_buffer); + free (row_buffer); + + rt_stop_moving (); + return result; +} + +static unsigned local_sram_size; +static unsigned char r93setting; + +#define RTS8801_F_SUPPRESS_MOVEMENT 1 + +static int +rts8801_fullscan (unsigned x, + unsigned y, + unsigned w, + unsigned h, + unsigned xresolution, + unsigned yresolution, + unsigned colour, + rts8801_callback cbfunc, + void *param, + unsigned char *calib_info, + int flags, + unsigned red_calib_offset, + unsigned green_calib_offset, + unsigned blue_calib_offset, struct dcalibdata *pdcd) +{ + int ires, jres; + int tg_setting; + unsigned char regs[256]; + unsigned char offdutytime; + int result; + + /* Kill warnings */ + red_calib_offset = red_calib_offset; + blue_calib_offset = blue_calib_offset; + green_calib_offset = green_calib_offset; + + for (ires = 0; + resparms[ires].resolution && resparms[ires].resolution != xresolution; + ++ires); + if (resparms[ires].resolution == 0) + return -1; + for (jres = 0; + resparms[jres].resolution && resparms[jres].resolution != yresolution; + ++jres); + if (resparms[jres].resolution == 0) + return -1; + + /* Set scan parameters */ + + rt_read_register_immediate (0, 255, regs); + regs[255] = 0; + + rt_enable_ccd (regs, 1); + rt_enable_movement (regs, 1); + rt_set_scan_frequency (regs, 1); + + rt_adjust_misc_registers (regs); + + rt_set_cvtr_wparams (regs, 3, 0, 6); + rt_set_cvtr_mpt (regs, 15, 15, 15); + rt_set_cvtr_lm (regs, 7, 7, 7); + rt_set_motor_type (regs, 2); + + if (rt_nvram_read (0, 0x7b, &offdutytime, 1) < 0 || offdutytime >= 15) + { + offdutytime = 6; + } + rt_set_lamp_duty_cycle (regs, 1, /* On */ + 10, /* Frequency */ + offdutytime); /* Off duty time */ + + rt_set_movement_pattern (regs, 0x800000); + + + tg_setting = resparms[jres].tg; + rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); + rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); + rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); + + + rt_set_one_register (0xc6, 0); + rt_set_one_register (0xc6, 0); + + rt_set_step_size (regs, resparms[jres].step_size); + + rt_set_direction_forwards (regs); + + rt_set_stop_when_rewound (regs, 0); + rt_set_data_feed_on (regs); + + rt_set_calibration_addresses (regs, 0, 0, 0); + + rt_set_basic_calibration (regs, + calib_info[0], calib_info[1], calib_info[2], + calib_info[3], calib_info[4], calib_info[5], + calib_info[6], calib_info[7], calib_info[8]); + regs[0x0b] = 0x70; /* If set to 0x71, the alternative, all values are low */ + +#if 0 + if (red_calib_offset >= 0 && green_calib_offset >= 0 + && blue_calib_offset >= 0) + { + rt_set_calibration_addresses (regs, red_calib_offset, blue_calib_offset, + green_calib_offset); + regs[0x40] = 0x3d; + pdcd = 0; + } +#endif + + rt_set_channel (regs, RT_CHANNEL_ALL); + rt_set_single_channel_scanning (regs, 0); + rt_set_merge_channels (regs, 0); + rt_set_colour_mode (regs, 1); + + rt_set_motor_movement_clock_multiplier (regs, + resparms[jres]. + motor_movement_clock_multiplier); + + rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, + tg_info[tg_setting].tg_cdss2); + rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, + tg_info[tg_setting].tg_cdsc2); + + rt_update_after_setting_cdss2 (regs); + + rt_set_last_sram_page (regs, (local_sram_size - 1) >> 5); + + regs[0x39] = resparms[jres].reg_39_value; + regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value; + regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value; + rt_set_scan_frequency (regs, resparms[jres].scan_frequency); + rt_set_cph0s (regs, resparms[ires].cph0s); + + if (flags & RTS8801_F_SUPPRESS_MOVEMENT) + regs[0xc3] &= 0x7f; + rt_set_horizontal_resolution (regs, xresolution); + + rt_set_noscan_distance (regs, y * resparms[jres].scan_frequency - 1); + rt_set_total_distance (regs, resparms[jres].scan_frequency * + (y + + h + + ((colour == + RTS8801_COLOUR) ? (resparms[jres]. + red_green_offset + + resparms[jres]. + green_blue_offset) : 0) + + resparms[jres].intra_channel_offset) - 1); + + rt_set_scanline_start (regs, + x * (1200 / xresolution) / + (resparms[ires].cph0s ? 1 : 2)); + rt_set_scanline_end (regs, + (x + + w) * (1200 / xresolution) / + (resparms[ires].cph0s ? 1 : 2)); + + rt_set_all_registers (regs); + + rt_set_one_register (0x2c, regs[0x2c]); + + result = rts8801_doscan (w, + colour, + resparms[jres].red_green_offset, + resparms[jres].green_blue_offset, + resparms[jres].intra_channel_offset, + cbfunc, param, (x & 1), calib_info, pdcd); + + return result; +} + +static int +sumfunc (struct dcalibdata *dcd, int bytes, char *data) +{ + unsigned char *c = (unsigned char *) data; + + while (bytes > 0) + { + if (dcd->rowsdone) + dcd->buffers[dcd->channelnow][dcd->pixelnow] += *c; + if (++dcd->channelnow >= 3) + { + dcd->channelnow = 0; + if (++dcd->pixelnow >= dcd->pixelsperrow) + { + dcd->pixelnow = 0; + ++dcd->rowsdone; + } + } + c++; + bytes--; + } + return 1; +} + +struct calibdata +{ + unsigned char *buffer; + int space; +}; + +static int +storefunc (struct calibdata *cd, int bytes, char *data) +{ + if (cd->space > 0) + { + if (bytes > cd->space) + bytes = cd->space; + memcpy (cd->buffer, data, bytes); + cd->buffer += bytes; + cd->space -= bytes; + } + return 1; +} + +#if 0 +static void +show_calib_results (unsigned char const *buffer, int n) +{ + int i = 0; + + while (n > 0) + { + int j; + + DBG (10, "%02x: ", i); + for (j = 0; j < 8; ++j) + { + if (j < n) + DBG (10, "%02x ", buffer[j]); + else + DBG (10, " "); + } + DBG (10, "-"); + for (; j < 16; ++j) + { + if (j < n) + DBG (10, " %02x", buffer[j]); + else + DBG (10, " "); + } + DBG (10, "\n"); + i += 16; + n -= 16; + buffer += 16; + } + +} +#endif + +static unsigned +sum_channel (unsigned char *p, int n, int bytwo) +{ + unsigned v = 0; + + while (n-- > 0) + { + v += *p; + p += 3; + if (bytwo) + p += 3; + } + return v; +} + +static int +constrain (int val, int min, int max) +{ + if (val < min) + { + DBG (10, "Clipped %d to %d\n", val, min); + val = min; + } + else if (val > max) + { + DBG (10, "Clipped %d to %d\n", val, max); + val = max; + } + return val; +} + +static int do_warmup = 1; + +static int +rts8801_scan (unsigned x, + unsigned y, + unsigned w, + unsigned h, + unsigned resolution, + unsigned colour, rts8801_callback cbfunc, void *param) +{ + unsigned char calib_info[9]; + unsigned char calibbuf[2400]; + struct dcalibdata dcd; + struct calibdata cd; + unsigned *piSums; + int iCalibOffset; + int iCalibX; + int iCalibY; + int iCalibWidth; + int iCalibTarget; + int iCalibPixels; + int iMoveFlags = 0; +#if 0 + unsigned char *pDetailedCalib; + unsigned aiBestYet[3]; + int j, n; +#endif + unsigned int aiLow[3] = { 0, 0, 0 }; + unsigned int aiHigh[3] = { 256, 256, 256 }; +#if 0 + unsigned aiLowTotals[3]; + unsigned aiLowOffset[3]; +#endif + unsigned aiBestOffset[3]; + int i; + int anychanged; + + /* Initialise and power up */ + + rt_set_all_registers (initial_regs); + rt_set_powersave_mode (0); + + /* Initial rewind in case scanner is stuck away from home position */ + + rts8801_rewind (); + + /* Detect SRAM */ + + rt_detect_sram (&local_sram_size, &r93setting); + + /* Warm up the lamp */ + + DBG (10, "Warming up the lamp\n"); + + rt_turn_on_lamp (); + if (do_warmup) + sleep (20); + + /* Basic calibration */ + + DBG (10, "Calibrating (stage 1)\n"); + + calib_info[2] = calib_info[5] = calib_info[8] = 1; + + calib_info[0] = calib_info[1] = calib_info[3] = calib_info[4] = + calib_info[6] = calib_info[7] = 0xb4; + + iCalibOffset = 0; /* Note that horizontal resolution is always 600dpi for calibration. 330 is 110 dots in (for R,G,B channels) */ + iCalibX = 1; + iCalibPixels = 50; + iCalibY = (resolution == 25) ? 1 : 2; /* Was 1200 / resolution, which would take us past the calibration area for 50dpi */ + iCalibWidth = 100; + iCalibTarget = 550; + + for (i = 0; i < 3; ++i) + aiBestOffset[i] = 0xb4; + + do + { + anychanged = 0; + + for (i = 0; i < 3; ++i) + { + aiBestOffset[i] = (aiHigh[i] + aiLow[i] + 1) / 2; + } + + for (i = 0; i < 3; ++i) + calib_info[i * 3] = calib_info[i * 3 + 1] = aiBestOffset[i]; + + cd.buffer = calibbuf; + cd.space = sizeof (calibbuf); + rts8801_fullscan (iCalibX, iCalibY, iCalibWidth, 2, 600, resolution, + RTS8801_COLOUR, (rts8801_callback) storefunc, &cd, + calib_info, iMoveFlags, -1, -1, -1, 0); + iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT; + + for (i = 0; i < 3; ++i) + { + int sum; + + if (aiBestOffset[i] >= 255) + continue; + sum = sum_channel (calibbuf + iCalibOffset + i, iCalibPixels, 0); + DBG (10, "channel[%d] sum = %d (target %d)\n", i, sum, + iCalibTarget); + + if (sum >= iCalibTarget) + aiHigh[i] = aiBestOffset[i]; + else + aiLow[i] = aiBestOffset[i]; + } + } + while (aiLow[0] < aiHigh[0] - 1 && aiLow[1] < aiHigh[1] - 1 + && aiLow[1] < aiHigh[1] + 1); + + cd.buffer = calibbuf; + cd.space = sizeof (calibbuf); + rts8801_fullscan (iCalibX + 2100, iCalibY, iCalibWidth, 2, 600, resolution, + RTS8801_COLOUR, (rts8801_callback) storefunc, &cd, + calib_info, RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, 0); + + for (i = 0; i < 3; ++i) + calib_info[i * 3 + 2] = + constrain (60000 / sum_channel (calibbuf + i, 50, 0), 0, 255); + + for (i = 0; i < 3; ++i) + { + DBG (10, "Channel [%d] gain=%02x offset=%02x\n", + i, calib_info[i * 3] + 2, calib_info[i * 3]); + } + + /* Stage 2 calibration */ + + DBG (10, "Calibrating (stage 2)\n"); + + piSums = (unsigned *) malloc (sizeof (unsigned) * w * 3); + memset (piSums, 0, sizeof (unsigned) * w * 3); + + dcd.buffers[0] = piSums; + dcd.buffers[1] = piSums + w; + dcd.buffers[2] = dcd.buffers[1] + w; + dcd.pixelsperrow = w; + dcd.pixelnow = dcd.channelnow = dcd.rowsdone = 0; + + DBG (10, "Performing detailed calibration scan\n"); + rts8801_fullscan (x, iCalibY, w, 21, resolution, resolution, colour, + (rts8801_callback) sumfunc, &dcd, calib_info, + RTS8801_F_SUPPRESS_MOVEMENT, -1, -1, -1, 0); + + DBG (10, "Detailed calibration scan completed\n"); +#if 0 +/* I haven't been able to get the scanner's per-element calibration to work at all yet, and when attempting to do + * so I fequently cause the scanner to lock up. + */ +/* pDetailedCalib = (unsigned char *) malloc(w * 6 + 1536); */ + pDetailedCalib = (unsigned char *) malloc (0x41e0); + memset (pDetailedCalib, 0, 0x41e0); + for (i = 0; i < 3; ++i) + { + for (j = 0; j < 256; ++j) + { + x = j * 2; + pDetailedCalib[x] = pDetailedCalib[x + 512] = + pDetailedCalib[x + 1024] = j; + pDetailedCalib[x + 1] = pDetailedCalib[x + 513] = + pDetailedCalib[x + 1025] = j + 1; + } + pDetailedCalib[511] = pDetailedCalib[1023] = pDetailedCalib[1535] = -1; + for (j = 0; j < w; ++j) + { + unsigned avnow = (dcd.buffers[i][j] + 39) / 40; + unsigned valnow = 0xe000 / (avnow ? avnow : 1); + int idx = (i * w + j) * 2 + 1536; + +/* if (j & 0x02) valnow = 0xe000; else valnow = 1; + valnow = 0; +*/ + if (i == 1 && j == w / 2) + valnow = 0xe000; + else + valnow = 1; + pDetailedCalib[idx] = valnow; + pDetailedCalib[idx + 1] = valnow >> 8; + } + } + +/* rt_set_sram_page(0); */ +#if 0 + DBG (10, "Calibrations calculated, writing\n"); + + n = 0x41e0; + i = j = 0; + rt_set_one_register (0x93, r93setting); + DBG (10, "Register 0x93 should have been set\n"); + while (n > 0) + { + int w = n; + + if (w > 32) + w = 32; + DBG (10, "%d", ++j); + fflush (stdout); + rt_write_sram (w, pDetailedCalib + i); + DBG (10, "..."); + n -= w; + i += w; + } + DBG (10, "\n"); + +/* show_calib_results(pDetailedCalib, 6 * w); + rt_write_sram(1536 + 6 * w, pDetailedCalib); */ + rt_get_available_bytes (); +#endif + rt_set_sram_page (0); + rt_set_one_register (0x93, r93setting); + rt_write_sram (0x41e0, pDetailedCalib); + rt_get_available_bytes (); + free (pDetailedCalib); +#endif + + + /* And finally, perform the scan */ + + DBG (10, "Scanning\n"); + + rts8801_rewind (); + + rts8801_fullscan (x, y, w, h, resolution, resolution, colour, cbfunc, param, + calib_info, 0, 1536, 1536 + w * 2, 1536 + w * 4, &dcd); + + rt_turn_off_lamp (); + rts8801_rewind (); + rt_set_powersave_mode (1); + + free (piSums); + return 0; +} + +static int +writefunc (struct hp3500_data *scanner, int bytes, char *data) +{ + return write (scanner->pipe_w, data, bytes) == bytes; +} + +static void +sigtermHandler (int signal) +{ + signal = signal; /* get rid of compiler warning */ + cancelled_scan = 1; +} + +static int +reader_process (void *pv) +{ + struct hp3500_data *scanner = pv; + time_t t; + sigset_t ignore_set; + sigset_t sigterm_set; + struct SIGACTION act; + + if (sanei_thread_is_forked()) + { + close (scanner->pipe_r); + } + + sigfillset (&ignore_set); + sigdelset (&ignore_set, SIGTERM); +#if defined (__APPLE__) && defined (__MACH__) + sigdelset (&ignore_set, SIGUSR2); +#endif + sigprocmask (SIG_SETMASK, &ignore_set, 0); + + sigemptyset (&sigterm_set); + sigaddset (&sigterm_set, SIGTERM); + + memset (&act, 0, sizeof (act)); +#ifdef _POSIX_SOURCE + act.sa_handler = sigtermHandler; +#endif + sigaction (SIGTERM, &act, 0); + + + /* Warm up the lamp again if our last scan ended more than 5 minutes ago. */ + time (&t); + do_warmup = (t - scanner->last_scan) > 300; + + udh = scanner->sfd; + + cancelled_scan = 0; + + DBG (10, "Scanning at %ddpi\n", scanner->resolution); + if (rts8801_scan + (scanner->actres_pixels.left + 250 * scanner->resolution / 1200, + scanner->actres_pixels.top + 599 * scanner->resolution / 1200, + scanner->actres_pixels.right - scanner->actres_pixels.left, + scanner->actres_pixels.bottom - scanner->actres_pixels.top, + scanner->resolution, RTS8801_COLOUR, (rts8801_callback) writefunc, + scanner) >= 0) + exit (SANE_STATUS_GOOD); + exit (SANE_STATUS_IO_ERROR); +} diff -uNr sane-backends-1.0.17/backend/hp4200.conf sane-backends-1.0.18/backend/hp4200.conf --- sane-backends-1.0.17/backend/hp4200.conf 2005-08-13 13:02:11.000000000 +0200 +++ sane-backends-1.0.18/backend/hp4200.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +0,0 @@ -# -# Configuration file for the hp4200 backend -# - -# HP4200 -usb 0x03f0 0x0105 diff -uNr sane-backends-1.0.17/backend/hp4200.conf.in sane-backends-1.0.18/backend/hp4200.conf.in --- sane-backends-1.0.17/backend/hp4200.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/hp4200.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,6 @@ +# +# Configuration file for the hp4200 backend +# + +# HP4200 +usb 0x03f0 0x0105 diff -uNr sane-backends-1.0.17/backend/hp5400.conf sane-backends-1.0.18/backend/hp5400.conf --- sane-backends-1.0.17/backend/hp5400.conf 2003-04-17 11:19:25.000000000 +0200 +++ sane-backends-1.0.18/backend/hp5400.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,14 +0,0 @@ -# hp5400.conf -# See man sane-hp5400 for a description. -# -# HP 5400C -usb 0x03F0 0x1005 -# -# HP 5470C -usb 0x03F0 0x1105 -# -# Device filename to use for scanner access -# -# Uncomment the following line if autodetection fails -# -#/dev/usbscanner diff -uNr sane-backends-1.0.17/backend/hp5400.conf.in sane-backends-1.0.18/backend/hp5400.conf.in --- sane-backends-1.0.17/backend/hp5400.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/hp5400.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,14 @@ +# hp5400.conf +# See man sane-hp5400 for a description. +# +# HP 5400C +usb 0x03F0 0x1005 +# +# HP 5470C +usb 0x03F0 0x1105 +# +# Device filename to use for scanner access +# +# Uncomment the following line if autodetection fails +# +#/dev/usbscanner diff -uNr sane-backends-1.0.17/backend/hp5400_internal.c sane-backends-1.0.18/backend/hp5400_internal.c --- sane-backends-1.0.17/backend/hp5400_internal.c 2004-10-03 17:14:18.000000000 +0200 +++ sane-backends-1.0.18/backend/hp5400_internal.c 2006-03-24 22:49:54.000000000 +0100 @@ -216,11 +216,11 @@ to check that the fix does not break calibration */ - int i, j; + int i, j, k; struct CalPixel { - short highr, highg, highb; - short lowr, lowg, lowb; + char highr[2], highg[2], highb[2]; + char lowr[2], lowg[2], lowb[2]; }; struct CalBlock { @@ -258,13 +258,22 @@ /* This is obviously not quite right. The values on * the right are approximatly what windows sends */ - pixel->highr = (high_vals[0][i] > 0x4000) ? 1000000000 / high_vals[0][i] : 0; /* 0x6700 */ - pixel->highg = (high_vals[1][i] > 0x4000) ? 1000000000 / high_vals[1][i] : 0; /* 0x5e00 */ - pixel->highb = (high_vals[2][i] > 0x4000) ? 1000000000 / high_vals[2][i] : 0; /* 0x6000 */ - - pixel->lowr = low_vals[0][i]; /* 0x0530 */ - pixel->lowg = low_vals[1][i]; /* 0x0530 */ - pixel->lowb = low_vals[2][i]; /* 0x0530 */ + k = (high_vals[0][i] > 0x4000) ? 1000000000 / high_vals[0][i] : 0; /* 0x6700 */ + pixel->highr[0] = k; + pixel->highr[1] = k >> 8; + k = (high_vals[1][i] > 0x4000) ? 1000000000 / high_vals[1][i] : 0; /* 0x5e00 */ + pixel->highg[0] = k; + pixel->highg[1] = k >> 8; + k = (high_vals[2][i] > 0x4000) ? 1000000000 / high_vals[2][i] : 0; /* 0x6000 */ + pixel->highb[0] = k; + pixel->highb[1] = k >> 8; + + pixel->lowr[0] = low_vals[0][i]; /* 0x0530 */ + pixel->lowr[1] = low_vals[0][i] >> 8; + pixel->lowg[0] = low_vals[1][i]; /* 0x0530 */ + pixel->lowg[1] = low_vals[1][i] >> 8; + pixel->lowb[0] = low_vals[2][i]; /* 0x0530 */ + pixel->lowb[1] = low_vals[2][i] >> 8; } cmd[0] = 0xff & (calSize >> 16); @@ -290,7 +299,7 @@ const int *pabGammaB) { char cmd[3]; - short *buffer; + char *buffer; int i, j; /* Setup dummy gamma correction table */ @@ -305,8 +314,10 @@ const int *ptr = (i == 0) ? pabGammaR : (i == 1) ? pabGammaG : pabGammaB; - for (j = 0; j < 65536; j++) /* Truncate integers to shorts */ - buffer[j] = ptr[j]; + for (j = 0; j < 65536; j++) { + buffer[2 * j] = ptr[j]; + buffer[2 * j + 1] = ptr[j] >> 8; + } hp5400_bulk_command_write (iHandle, 0x2A01 + i, cmd, 3, 2 * 65536, 65536, (void *) buffer); @@ -436,8 +447,9 @@ while (p->bufstart + maxoff >= p->bufend) /* Not enough data in buffer */ { int res; - unsigned short cmd[4] = { 0, 0, 0, 0 }; - cmd[2] = p->blksize; + unsigned char cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + cmd[4] = p->blksize; + cmd[5] = p->blksize >> 8; assert ((p->bufend + p->blksize) <= p->buffersize); @@ -449,7 +461,7 @@ buftmp + p->bufend, p->blksize); if (res != p->blksize) { - HP5400_DBG (DBG_ERR, "*** ERROR: Read returned %d. FATAL.", res); + HP5400_DBG (DBG_ERR, "*** ERROR: Read returned %d. FATAL.\n", res); return -1; } #ifdef IMAGE_DEBUG diff -uNr sane-backends-1.0.17/backend/hp5400_xfer.h sane-backends-1.0.18/backend/hp5400_xfer.h --- sane-backends-1.0.17/backend/hp5400_xfer.h 2005-04-16 15:06:36.000000000 +0200 +++ sane-backends-1.0.18/backend/hp5400_xfer.h 2006-01-22 10:34:12.000000000 +0100 @@ -63,11 +63,15 @@ HP5400_SANE_STATIC int hp5400_command_write (int iHandle, int iCmd, int iLen, void *pbData); HP5400_SANE_STATIC void hp5400_command_write_noverify (int fd, int iValue, void *pabData, int iSize); +#ifdef STANDALONE HP5400_SANE_STATIC int hp5400_bulk_read (int iHandle, size_t size, int block, FILE * file); +#endif HP5400_SANE_STATIC int hp5400_bulk_read_block (int iHandle, int iCmd, void *cmd, int cmdlen, void *buffer, int len); HP5400_SANE_STATIC int hp5400_bulk_command_write (int iHandle, int iCmd, void *cmd, int cmdlen, int len, int block, char *data); +#ifdef STANDALONE HP5400_SANE_STATIC int hp5400_isOn (int iHandle); +#endif #endif diff -uNr sane-backends-1.0.17/backend/hp.conf sane-backends-1.0.18/backend/hp.conf --- sane-backends-1.0.17/backend/hp.conf 2002-12-06 21:13:47.000000000 +0100 +++ sane-backends-1.0.18/backend/hp.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,21 +0,0 @@ -scsi HP -# Uncomment the following if you have "Error during device I/O" on SCSI -# option dumb-read -# -# The usual place for a SCSI-scanner on Linux -/dev/scanner -# -# USB-scanners supported by the hp-backend -# HP ScanJet 4100C -usb 0x03f0 0x0101 -# HP ScanJet 5200C -usb 0x03f0 0x0401 -# HP ScanJet 62X0C -usb 0x03f0 0x0201 -# HP ScanJet 63X0C -usb 0x03f0 0x0601 -# -# Uncomment the following if your scanner is connected by USB, -# but you are not using libusb -# /dev/usb/scanner0 -# option connect-device diff -uNr sane-backends-1.0.17/backend/hp.conf.in sane-backends-1.0.18/backend/hp.conf.in --- sane-backends-1.0.17/backend/hp.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/hp.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,21 @@ +scsi HP +# Uncomment the following if you have "Error during device I/O" on SCSI +# option dumb-read +# +# The usual place for a SCSI-scanner on Linux +/dev/scanner +# +# USB-scanners supported by the hp-backend +# HP ScanJet 4100C +usb 0x03f0 0x0101 +# HP ScanJet 5200C +usb 0x03f0 0x0401 +# HP ScanJet 62X0C +usb 0x03f0 0x0201 +# HP ScanJet 63X0C +usb 0x03f0 0x0601 +# +# Uncomment the following if your scanner is connected by USB, +# but you are not using libusb +# /dev/usb/scanner0 +# option connect-device diff -uNr sane-backends-1.0.17/backend/hpsj5s.conf sane-backends-1.0.18/backend/hpsj5s.conf --- sane-backends-1.0.17/backend/hpsj5s.conf 2002-11-23 12:52:42.000000000 +0100 +++ sane-backends-1.0.18/backend/hpsj5s.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -#hpsj5s.conf -#parport0 \ Kein Zeilenumbruch am Dateiende. diff -uNr sane-backends-1.0.17/backend/hpsj5s.conf.in sane-backends-1.0.18/backend/hpsj5s.conf.in --- sane-backends-1.0.17/backend/hpsj5s.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/hpsj5s.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +#hpsj5s.conf +#parport0 \ Kein Zeilenumbruch am Dateiende. diff -uNr sane-backends-1.0.17/backend/ibm.conf sane-backends-1.0.18/backend/ibm.conf --- sane-backends-1.0.17/backend/ibm.conf 2003-04-15 11:54:00.000000000 +0200 +++ sane-backends-1.0.18/backend/ibm.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -scsi IBM 2456 -scsi RICOH -/dev/scanner diff -uNr sane-backends-1.0.17/backend/ibm.conf.in sane-backends-1.0.18/backend/ibm.conf.in --- sane-backends-1.0.17/backend/ibm.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/ibm.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,3 @@ +scsi IBM 2456 +scsi RICOH +/dev/scanner diff -uNr sane-backends-1.0.17/backend/leo.conf sane-backends-1.0.18/backend/leo.conf --- sane-backends-1.0.17/backend/leo.conf 2002-09-20 04:59:14.000000000 +0200 +++ sane-backends-1.0.18/backend/leo.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,8 +0,0 @@ -# The FS-1130 respond to all luns -scsi ACROSS * Scanner * * * 0 - -# LEO S3 -scsi "LEO" "LEOScan-S3" - -/dev/scanner - diff -uNr sane-backends-1.0.17/backend/leo.conf.in sane-backends-1.0.18/backend/leo.conf.in --- sane-backends-1.0.17/backend/leo.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/leo.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,8 @@ +# The FS-1130 respond to all luns +scsi ACROSS * Scanner * * * 0 + +# LEO S3 +scsi "LEO" "LEOScan-S3" + +/dev/scanner + diff -uNr sane-backends-1.0.17/backend/lexmark.conf sane-backends-1.0.18/backend/lexmark.conf --- sane-backends-1.0.17/backend/lexmark.conf 2005-10-16 18:42:31.000000000 +0200 +++ sane-backends-1.0.18/backend/lexmark.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -#usb /dev/usb/scanner0 -usb 0x043d 0x007c diff -uNr sane-backends-1.0.17/backend/lexmark.conf.in sane-backends-1.0.18/backend/lexmark.conf.in --- sane-backends-1.0.17/backend/lexmark.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/lexmark.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,2 @@ +#usb /dev/usb/scanner0 +usb 0x043d 0x007c diff -uNr sane-backends-1.0.17/backend/ma1509.conf sane-backends-1.0.18/backend/ma1509.conf --- sane-backends-1.0.17/backend/ma1509.conf 2003-02-24 22:04:08.000000000 +0100 +++ sane-backends-1.0.18/backend/ma1509.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,10 +0,0 @@ -#ma1509.conf: see sane-ma1509(5) - -#Warm-up time for the lamp in seconds -option warmup-time 30 - -#Mustek BearPaw 1200F -usb 0x055f 0x0010 - -#Manual setting (e.g. for FreeBSD) -#/dev/uscanner0 diff -uNr sane-backends-1.0.17/backend/ma1509.conf.in sane-backends-1.0.18/backend/ma1509.conf.in --- sane-backends-1.0.17/backend/ma1509.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/ma1509.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,10 @@ +#ma1509.conf: see sane-ma1509(5) + +#Warm-up time for the lamp in seconds +option warmup-time 30 + +#Mustek BearPaw 1200F +usb 0x055f 0x0010 + +#Manual setting (e.g. for FreeBSD) +#/dev/uscanner0 diff -uNr sane-backends-1.0.17/backend/Makefile.in sane-backends-1.0.18/backend/Makefile.in --- sane-backends-1.0.17/backend/Makefile.in 2005-12-18 14:32:31.000000000 +0100 +++ sane-backends-1.0.18/backend/Makefile.in 2006-06-11 13:10:59.000000000 +0200 @@ -7,6 +7,7 @@ PACKAGE = @PACKAGE@ VERSION = @VERSION@ +package_version = @PACKAGE_STRING@ distdir = $(top_srcdir)/$(PACKAGE)-$(VERSION) prefix = @prefix@ @@ -18,7 +19,7 @@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ -localstatesanedir = $(localstatedir)/lock/sane +locksanedir = @locksanedir@ libdir = @libdir@ libsanedir = $(libdir)/sane infodir = @infodir@ @@ -85,56 +86,61 @@ LIBOBJS = $(addprefix ../lib/,$(addsuffix .lo,$(LIBLIB_FUNCS))) -DISTFILES = abaton.c abaton.conf abaton.h agfafocus.c agfafocus.conf \ - agfafocus.h apple.c apple.conf apple.h artec.c artec.conf artec.h \ - artec_eplus48u.c artec_eplus48u.h artec_eplus48u.conf as6e.c \ - as6e.h avision.c avision.conf avision.h bh.c bh.conf bh.h canon.c \ - canon.conf canon.h canon-sane.c canon-scsi.c canon630u.c canon630u-common.c \ - canon630u.conf canon_pp.conf canon_pp.h canon_pp.c canon_pp-dev.c \ +DISTFILES = abaton.c abaton.conf.in abaton.h agfafocus.c agfafocus.conf.in \ + agfafocus.h apple.c apple.conf.in apple.h artec.c artec.conf.in artec.h \ + artec_eplus48u.c artec_eplus48u.h artec_eplus48u.conf.in as6e.c \ + as6e.h avision.c avision.conf.in avision.h bh.c bh.conf.in bh.h canon.c \ + canon.conf.in canon.h canon-sane.c canon-scsi.c canon630u.c canon630u-common.c \ + canon630u.conf.in canon_pp.conf.in canon_pp.h canon_pp.c canon_pp-dev.c \ canon_pp-dev.h canon_pp-io.c canon_pp-io.h coolscan.c \ - coolscan.conf coolscan.h coolscan-scsidef.h coolscan2.c coolscan2.conf \ - dc210.c dc210.conf dc210.h \ - dc240.c dc240.conf dc240.h dc25.c dc25.conf dc25.h \ - dll.aliases dll.c dll.conf dmc.c dmc.conf dmc.h \ - epson.c epson_scsi.c epson_usb.c epson.conf epson.h epson_scsi.h \ + coolscan.conf.in coolscan.h coolscan-scsidef.h coolscan2.c coolscan2.conf.in \ + dc210.c dc210.conf.in dc210.h \ + dc240.c dc240.conf.in dc240.h dc25.c dc25.conf.in dc25.h dell1600n_net.c\ + dll.aliases dll.c dll.conf.in dmc.c dmc.conf.in dmc.h \ + epson.c epson_scsi.c epson_usb.c epson.conf.in epson.h epson_scsi.h \ epson_usb.h \ - fujitsu.c fujitsu.conf fujitsu.h fujitsu-scsi.h \ - gphoto2.c gphoto2.conf gphoto2.h \ + fujitsu.c fujitsu.conf.in fujitsu.h fujitsu-scsi.h \ + gphoto2.c gphoto2.conf.in gphoto2.h \ gt68xx.c gt68xx.h gt68xx_high.c gt68xx_high.h gt68xx_mid.c gt68xx_mid.h \ gt68xx_gt6801.c gt68xx_gt6801.h gt68xx_gt6816.c gt68xx_gt6816.h \ gt68xx_low.c gt68xx_low.h gt68xx_devices.c gt68xx_generic.c \ gt68xx_generic.h gt68xx_shm_channel.c \ - gt68xx_shm_channel.h gt68xx.conf \ + gt68xx_shm_channel.h gt68xx.conf.in \ genesys.c genesys.h genesys_gl646.c genesys_low.h \ - genesys.conf genesys_devices.c genesys_gl841.c \ + genesys.conf.in genesys_devices.c genesys_gl841.c \ genesys_conv.c genesys_conv_hlp.c \ - hp-accessor.c hp-accessor.h hp.c hp.conf \ + hp-accessor.c hp-accessor.h hp.c hp.conf.in \ hp-device.c hp-device.h hp.h hp-handle.c hp-handle.h hp-hpmem.c hp-option.c \ hp-option.h hp.README hp-scl.c hp-scl.h hp-scsi.h hp.TODO \ - hpsj5s.c hpsj5s.conf hpsj5s.h \ - hp4200.c hp4200.conf hp4200.h hp4200_lm9830.c hp4200_lm9830.h \ - hp5400.c hp5400.h hp5400.conf hp5400_debug.c hp5400_debug.h \ + hpsj5s.c hpsj5s.conf.in hpsj5s.h \ + hp4200.c hp4200.conf.in hp4200.h hp4200_lm9830.c hp4200_lm9830.h \ + hp3500.c \ + hp5400.c hp5400.h hp5400.conf.in hp5400_debug.c hp5400_debug.h \ hp5400_internal.c hp5400_internal.h hp5400_xfer.h \ hp5400_sane.c hp5400_sanei.c hp5400_sanei.h \ - ibm.c ibm.conf ibm.h ibm-scsi.c \ - leo.c leo.h leo.conf \ - lexmark.c lexmark-x1100.c lexmark.h lexmark.conf \ + ibm.c ibm.conf.in ibm.h ibm-scsi.c \ + leo.c leo.h leo.conf.in \ + lexmark.c lexmark-x1100.c lexmark.h lexmark.conf.in \ lm9830.h \ - ma1509.c ma1509.conf ma1509.h \ + ma1509.c ma1509.conf.in ma1509.h \ 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.c mustek_pp.conf mustek_pp.h mustek_pp_cis.c mustek_pp_cis.h \ + matsushita.conf.in matsushita.h microtek2.c microtek2.conf.in microtek2.h \ + microtek.c microtek.conf.in microtek.h mustek.c mustek.conf.in mustek.h \ + mustek_pp.c mustek_pp.conf.in 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_scsi_pp.c mustek_scsi_pp.h mustek_usb.c mustek_usb.conf \ + mustek_pp_ccd300.h mustek_scsi_pp.c mustek_scsi_pp.h mustek_usb.c mustek_usb.conf.in \ mustek_usb.h mustek_usb_high.c mustek_usb_high.h mustek_usb_low.c \ - mustek_usb_low.h mustek_usb_mid.c mustek_usb_mid.h nec.c nec.conf nec.h \ + mustek_usb_low.h mustek_usb_mid.c mustek_usb_mid.h nec.c nec.conf.in nec.h \ mustek_usb2.c mustek_usb2.h mustek_usb2_asic.c \ mustek_usb2_asic.h mustek_usb2_high.c mustek_usb2_high.h \ mustek_usb2_reflective.c mustek_usb2_transparent.c \ - net.c net.conf net.h niash.c niash_core.c niash_core.h \ - niash_xfer.c niash_xfer.h pie.c pie.conf pie-scsidef.h pint.c pint.h \ - plustek.c plustek.conf plustek-usbdevs.c plustek.h \ + net.c net.conf.in net.h niash.c niash_core.c niash_core.h \ + niash_xfer.c niash_xfer.h pie.c pie.conf.in pie-scsidef.h pint.c pint.h \ + pixma.c pixma.h pixma_common.c pixma_common.h \ + pixma_mp150.c pixma_mp730.c pixma_mp750.c \ + pixma_sane_options.c pixma_sane_options.h pixma_io.h pixma_io_sanei.c \ + pixma_rename.h \ + plustek.c plustek.conf.in plustek-usbdevs.c plustek.h \ plustek-usb.c plustek-usb.h plustek-usbhw.c plustek-usbimg.c \ plustek-usbio.c plustek-usbmap.c plustek-usbscan.c plustek-usbshading.c \ plustek-usbcalfile.c plustek-usbcal.c \ @@ -144,32 +150,31 @@ plustek-pp_p12ccd.c plustek-pp_p48xx.c plustek-pp_p9636.c plustek-pp_procfs.c \ plustek-pp_procs.h plustek-pp_ptdrv.c plustek-pp_scale.c plustek-pp_scan.h \ plustek-pp_scandata.h plustek-pp_sysdep.h plustek-pp_tpa.c plustek-pp_types.h \ - plustek_pp.c plustek-pp_wrapper.c plustek_pp.conf plustek-pp.h \ - pnm.c qcam.c qcam.conf qcam.h ricoh.c ricoh.conf ricoh.h \ - ricoh-scsi.c s9036.c s9036.conf s9036.h saned.conf sane_strstatus.c \ - sceptre.c sceptre.conf sceptre.h sharp.c sharp.conf sharp.h sm3600.c \ + plustek_pp.c plustek-pp_wrapper.c plustek_pp.conf.in plustek-pp.h \ + pnm.c qcam.c qcam.conf.in qcam.h ricoh.c ricoh.conf.in ricoh.h \ + ricoh-scsi.c s9036.c s9036.conf.in s9036.h saned.conf.in sane_strstatus.c \ + sceptre.c sceptre.conf.in sceptre.h sharp.c sharp.conf.in sharp.h sm3600.c \ sm3600-color.c sm3600-gray.c sm3600.h sm3600-homerun.c sm3600-scanmtek.c \ - sm3600-scantool.h sm3600-scanusb.c sm3600-scanutil.c snapscan.c \ - snapscan.conf snapscan.h snapscan-scsi.c snapscan-sources.c snapscan-mutex.c \ + sm3600-scantool.h sm3600-scanusb.c sm3600-scanutil.c snapscan.c snapscan-data.c \ + snapscan.conf.in snapscan.h snapscan-scsi.c snapscan-sources.c snapscan-mutex.c \ snapscan-sources.h snapscan-usb.c snapscan-usb.h snapscan-options.c sp15c.c \ - sp15c.conf sp15c.h sp15c-scsi.h st400.c st400.conf st400.h stubs.c \ - tamarack.c tamarack.conf tamarack.h teco1.c teco1.h teco1.conf \ - teco2.c teco2.conf teco2.h \ - teco3.c teco3.conf teco3.h \ - test.c test.conf test.h test-picture.c umax1220u.c \ - u12.c u12.h u12-scanner.h u12-hwdef.h u12.conf u12-shading.c u12-tpa.c \ + sp15c.conf.in sp15c.h sp15c-scsi.h st400.c st400.conf.in st400.h stubs.c \ + stv680.c stv680.conf.in stv680.h \ + tamarack.c tamarack.conf.in tamarack.h teco1.c teco1.h teco1.conf.in \ + teco2.c teco2.conf.in teco2.h \ + teco3.c teco3.conf.in teco3.h \ + test.c test.conf.in test.h test-picture.c umax1220u.c \ + u12.c u12.h u12-scanner.h u12-hwdef.h u12.conf.in u12-shading.c u12-tpa.c \ u12-ccd.c u12-hw.c u12-if.c u12-image.c u12-io.c u12-map.c u12-motor.c \ umax1220u-common.c \ - umax1220u.conf umax.c umax.conf umax.h umax_pp.c umax_pp.conf umax_pp.h \ + umax1220u.conf.in umax.c umax.conf.in umax.h umax_pp.c umax_pp.conf.in umax_pp.h \ umax_pp_low.c umax_pp_low.h umax_pp_mid.c umax_pp_mid.h umax-scanner.c \ umax-scanner.h umax-scsidef.h umax-uc1200s.c umax-uc1200se.c umax-uc1260.c \ umax-uc630.c umax-uc840.c umax-ug630.c umax-ug80.c umax-usb.c v4l.c \ - v4l.conf v4l-frequencies.h v4l.h \ - sm3840.conf sm3840.c sm3840_lib.c sm3840_params.h sm3840_scan.c sm3840.h \ + v4l.conf.in v4l-frequencies.h v4l.h \ + sm3840.conf.in sm3840.c sm3840_lib.c sm3840_params.h sm3840_scan.c sm3840.h \ sm3840_lib.h - - .PHONY: all clean depend dist distclean install uninstall libsane-%.la: %.lo %-s.lo $(EXTRA) $(LIBOBJS) @@ -191,7 +196,7 @@ # Don't delete any intermediate files. .PRECIOUS: %-s.c %-s.lo %.lo dll-preload.c -all: $(LIBS) libsane.la +all: $(LIBS) libsane.la becfg install: $(INSTALL_LOCKPATH) install-be@BE_INSTALL_FLAVOR@ install-libsane install-becfg @@ -250,10 +255,10 @@ done install-lockpath: - $(MKDIR) -m 775 -g $(LOCKPATH_GROUP) $(DESTDIR)$(localstatesanedir) + $(MKDIR) -m 775 -g $(LOCKPATH_GROUP) $(DESTDIR)$(locksanedir) uninstall: - rm -rf $(libsanedir) $(configdir) $(localstatesanedir) + rm -rf $(libsanedir) $(configdir) $(locksanedir) rm -f $(libdir)/libsane.* -for dir in $(FIRMWARE_DIRS) ; do \ rmdir $(datadir)/sane/$${dir} ; \ @@ -284,6 +289,20 @@ $(addsuffix .lo,$(DLL_PRELOAD_EXTRAS)) @LIBTOOL_LINK_EXTRA@ \ -rpath $(libdir) -version-info $(V_MAJOR):$(V_REV):$(V_MINOR) +# Generate .conf files for all existing .conf.in files +becfg: $(patsubst %.conf.in,%.conf,$(wildcard *.conf.in)) +%.conf: %.conf.in + @echo Generating $@ from $^ + @sed -e 's|@DATADIR@|$(datadir)|g' \ + -e 's|@CONFIGDIR@|$(configdir)|g' \ + -e 's|@DOCDIR@|$(docdir)|g' \ + -e 's|@LIBDIR@|$(libdir)/sane|g' \ + -e 's|@BINDIR@|$(bindir)|g' \ + -e 's|@SBINDIR@|$(sbindir)|g' \ + -e 's|@PACKAGEVERSION@|$(package_version)|g' $? > $@ + + + # additional dependencies EXTRA_canon_pp = canon_pp-io canon_pp-dev @@ -292,6 +311,7 @@ EXTRA_umax_pp = umax_pp_low umax_pp_mid EXTRA_epson = epson_scsi epson_usb EXTRA_lexmark = lexmark-x1100 +EXTRA_pixma = pixma_io_sanei pixma_common pixma_mp150 pixma_mp730 pixma_mp750 # When preloading dll, we need to add in all preloaded objects: libsane-dll.la: $(addsuffix .lo,$(DLL_PRELOAD)) @@ -370,6 +390,9 @@ libsane-hp.la: $(addsuffix .lo,$(EXTRA_hp)) libsane-hp.la: ../sanei/sanei_pio.lo libsane-hp.la: ../sanei/sanei_thread.lo +libsane-hp3500.la: ../sanei/sanei_usb.lo +libsane-hp3500.la: ../sanei/sanei_thread.lo +libsane-hp3500.la: ../sanei/sanei_constrain_value.lo libsane-hp4200.la: ../sanei/sanei_constrain_value.lo libsane-hp4200.la: ../sanei/sanei_usb.lo libsane-hp4200.la: ../sanei/sanei_pv8630.lo @@ -416,6 +439,9 @@ libsane-niash.la: ../sanei/sanei_constrain_value.lo libsane-niash.la: ../sanei/sanei_usb.lo libsane-pint.la: ../sanei/sanei_constrain_value.lo +libsane-pixma.la: ../sanei/sanei_usb.lo +libsane-pixma.la: ../sanei/sanei_thread.lo +libsane-pixma.la: $(addsuffix .lo,$(EXTRA_pixma)) libsane-plustek.la: ../sanei/sanei_constrain_value.lo libsane-plustek.la: ../sanei/sanei_usb.lo libsane-plustek.la: ../sanei/sanei_lm983x.lo @@ -460,6 +486,8 @@ libsane-st400.la: ../sanei/sanei_config2.lo libsane-st400.la: ../sanei/sanei_constrain_value.lo libsane-st400.la: ../sanei/sanei_scsi.lo +libsane-stv680.la: ../sanei/sanei_constrain_value.lo +libsane-stv680.la: ../sanei/sanei_usb.lo libsane-tamarack.la: ../sanei/sanei_config2.lo libsane-tamarack.la: ../sanei/sanei_constrain_value.lo libsane-tamarack.la: ../sanei/sanei_scsi.lo @@ -520,7 +548,7 @@ makedepend -a -o.lo $(INCLUDES) *.c 2>/dev/null clean: - rm -f *.lo *.o *.la libsane.la dll-preload.c + rm -f *.lo *.o *.la libsane.la dll-preload.c *.conf find . -type l -name \*-s.c | xargs rm -f rm -rf .libs diff -uNr sane-backends-1.0.17/backend/matsushita.conf sane-backends-1.0.18/backend/matsushita.conf --- sane-backends-1.0.17/backend/matsushita.conf 2004-02-14 19:47:02.000000000 +0100 +++ sane-backends-1.0.18/backend/matsushita.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -# -# Panasonic / Matsushita scanners - -scsi "K.M.E. " "KV-SS25A " -scsi "K.M.E. " "KV-SS55EX " -scsi "K.M.E. " "KV-S2025C " -scsi "K.M.E. " "KV-S2045C " -scsi "K.M.E. " "KV-S2065L " - -# These scanners are untested. -# If you have one: -# - check that the vendor/product strings are correct -# - uncomment the line -# - test with a frontend (xscanimage, xsane, ...) -#scsi "K.M.E. " "KV-SS25 " -#scsi "K.M.E. " "KV-SS25D " -#scsi "K.M.E. " "KV-SS50 " -#scsi "K.M.E. " "KV-SS55 " -#scsi "K.M.E. " "KV-SS50EX " -#scsi "K.M.E. " "KV-SS850 " -#scsi "K.M.E. " "KV-SS855 " - -/dev/scanner diff -uNr sane-backends-1.0.17/backend/matsushita.conf.in sane-backends-1.0.18/backend/matsushita.conf.in --- sane-backends-1.0.17/backend/matsushita.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/matsushita.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,23 @@ +# +# Panasonic / Matsushita scanners + +scsi "K.M.E. " "KV-SS25A " +scsi "K.M.E. " "KV-SS55EX " +scsi "K.M.E. " "KV-S2025C " +scsi "K.M.E. " "KV-S2045C " +scsi "K.M.E. " "KV-S2065L " + +# These scanners are untested. +# If you have one: +# - check that the vendor/product strings are correct +# - uncomment the line +# - test with a frontend (xscanimage, xsane, ...) +#scsi "K.M.E. " "KV-SS25 " +#scsi "K.M.E. " "KV-SS25D " +#scsi "K.M.E. " "KV-SS50 " +#scsi "K.M.E. " "KV-SS55 " +#scsi "K.M.E. " "KV-SS50EX " +#scsi "K.M.E. " "KV-SS850 " +#scsi "K.M.E. " "KV-SS855 " + +/dev/scanner diff -uNr sane-backends-1.0.17/backend/microtek2.c sane-backends-1.0.18/backend/microtek2.c --- sane-backends-1.0.17/backend/microtek2.c 2005-10-05 19:05:29.000000000 +0200 +++ sane-backends-1.0.18/backend/microtek2.c 2005-12-28 15:17:57.000000000 +0100 @@ -374,10 +374,10 @@ } else { - x_pixel_per_mm = SANE_UNFIX(val[OPT_X_RESOLUTION].w) / MM_PER_INCH; + x_pixel_per_mm = SANE_UNFIX(val[OPT_RESOLUTION].w) / MM_PER_INCH; y_pixel_per_mm = SANE_UNFIX(val[OPT_Y_RESOLUTION].w) / MM_PER_INCH; DBG(30, "sane_get_parameters: x_res=%f, y_res=%f\n", - SANE_UNFIX(val[OPT_X_RESOLUTION].w), + SANE_UNFIX(val[OPT_RESOLUTION].w), SANE_UNFIX(val[OPT_Y_RESOLUTION].w)); } @@ -2163,7 +2163,6 @@ md->x_res_range_dpi.max = SANE_FIX(mi->max_xresolution); md->x_res_range_dpi.quant = SANE_FIX(1.0); val[OPT_RESOLUTION].w = MIN(MD_RESOLUTION_DEFAULT, md->x_res_range_dpi.max); - val[OPT_X_RESOLUTION].w = val[OPT_RESOLUTION].w; md->y_res_range_dpi.min = SANE_FIX(10.0); md->y_res_range_dpi.max = SANE_FIX(mi->max_yresolution); @@ -2426,22 +2425,15 @@ sod[OPT_HALFTONE].constraint.string_list = md->halftone_mode_list; /* Resolution */ - sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; - sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; - sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_X_RESOLUTION; + sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; + sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION; sod[OPT_RESOLUTION].unit = SANE_UNIT_DPI; sod[OPT_RESOLUTION].constraint.range = &md->x_res_range_dpi; - sod[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_X_RESOLUTION; - sod[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; - sod[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; - sod[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; - sod[OPT_X_RESOLUTION].cap |= SANE_CAP_INACTIVE; - sod[OPT_X_RESOLUTION].constraint.range = &md->x_res_range_dpi; - sod[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; sod[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; - sod[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + sod[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION; sod[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; sod[OPT_Y_RESOLUTION].constraint.range = &md->y_res_range_dpi; @@ -3067,7 +3059,6 @@ /* word options */ case OPT_BITDEPTH: case OPT_RESOLUTION: - case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_THRESHOLD: case OPT_TL_X: @@ -3248,7 +3239,6 @@ switch ( option ) { case OPT_RESOLUTION: - case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: @@ -3505,14 +3495,10 @@ case OPT_RESOLUTION_BIND: if ( ms->val[option].w == SANE_FALSE ) { - ms->sod[OPT_RESOLUTION].cap |= SANE_CAP_INACTIVE; - ms->sod[OPT_X_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; ms->sod[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; } else { - ms->sod[OPT_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; - ms->sod[OPT_X_RESOLUTION].cap |= SANE_CAP_INACTIVE; ms->sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; } if ( info ) @@ -3948,7 +3934,7 @@ else { ms->x_resolution_dpi = - (SANE_Int) (SANE_UNFIX(ms->val[OPT_X_RESOLUTION].w) + 0.5); + (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5); ms->y_resolution_dpi = (SANE_Int) (SANE_UNFIX(ms->val[OPT_Y_RESOLUTION].w) + 0.5); } diff -uNr sane-backends-1.0.17/backend/microtek2.conf sane-backends-1.0.18/backend/microtek2.conf --- sane-backends-1.0.17/backend/microtek2.conf 2003-01-11 23:15:14.000000000 +0100 +++ sane-backends-1.0.18/backend/microtek2.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,11 +0,0 @@ -# See sane-microtek2(5) for a description of the options - -option dump 1 -#option strip-height 14.0 -option no-backtrack-option on -option lightlid-35 on -option toggle-lamp on -option lineart-autoadjust on -option backend-calibration on -#option colorbalance-adjust on -scsi * * Scanner diff -uNr sane-backends-1.0.17/backend/microtek2.conf.in sane-backends-1.0.18/backend/microtek2.conf.in --- sane-backends-1.0.17/backend/microtek2.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/microtek2.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,11 @@ +# See sane-microtek2(5) for a description of the options + +option dump 1 +#option strip-height 14.0 +option no-backtrack-option on +option lightlid-35 on +option toggle-lamp on +option lineart-autoadjust on +option backend-calibration on +#option colorbalance-adjust on +scsi * * Scanner diff -uNr sane-backends-1.0.17/backend/microtek2.h sane-backends-1.0.18/backend/microtek2.h --- sane-backends-1.0.17/backend/microtek2.h 2004-10-06 12:18:20.000000000 +0200 +++ sane-backends-1.0.18/backend/microtek2.h 2005-12-28 15:15:16.000000000 +0100 @@ -526,7 +526,6 @@ OPT_MODE, OPT_BITDEPTH, OPT_RESOLUTION, - OPT_X_RESOLUTION, OPT_Y_RESOLUTION, OPT_PREVIEW, diff -uNr sane-backends-1.0.17/backend/microtek.conf sane-backends-1.0.18/backend/microtek.conf --- sane-backends-1.0.17/backend/microtek.conf 2000-03-05 14:55:09.000000000 +0100 +++ sane-backends-1.0.18/backend/microtek.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,7 +0,0 @@ -# Uncomment following line to disable "real calibration" routines... -#norealcal -# Uncomment following line to disable "clever precalibration" routines... -#noprecal -# Using "norealcal" will revert backend to pre-0.11.0 calibration code. -scsi * * Scanner -/dev/scanner diff -uNr sane-backends-1.0.17/backend/microtek.conf.in sane-backends-1.0.18/backend/microtek.conf.in --- sane-backends-1.0.17/backend/microtek.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/microtek.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,7 @@ +# Uncomment following line to disable "real calibration" routines... +#norealcal +# Uncomment following line to disable "clever precalibration" routines... +#noprecal +# Using "norealcal" will revert backend to pre-0.11.0 calibration code. +scsi * * Scanner +/dev/scanner diff -uNr sane-backends-1.0.17/backend/mustek.conf sane-backends-1.0.18/backend/mustek.conf --- sane-backends-1.0.17/backend/mustek.conf 2005-09-16 17:42:04.000000000 +0200 +++ sane-backends-1.0.18/backend/mustek.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,41 +0,0 @@ -# See sane-mustek(5) for documentation. - -#--------------------------- Global options --------------------------------- -#option strip-height 1 # some SCSI adapters need this; scanning may - # be faster without this option -#option force-wait # wait for scanner to be ready (only necessary - # when scanner freezes) -#option disable-double-buffering # try this if you have SCSI trouble - -#-------------------------- SCSI scanners ----------------------------------- -scsi MUSTEK * Scanner -# option linedistance-fix # stripes may go away in color mode -# option buffersize 1024 # set non standard buffer size (in kb) -# option blocksize 2048 # set non standard block size (in kb) -# option lineart-fix # lineart may be faster with this option off. -# option disable-backtracking # faster, but may produce stripes - -scsi SCANNER -# option linedistance-fix # stripes may go away in color mode -# option buffersize 1024 # set non standard buffer size (in kb) -# option blocksize 2048 # set non standard block size (in kb) -# option lineart-fix # lineart may be faster with this option off. -# option disable-backtracking # faster, but may produce stripes - -/dev/scanner -# option linedistance-fix # stripes may go away in color mode -# option buffersize 1024 # set non standard buffer size (in kb) -# option blocksize 2048 # set non standard block size (in kb) -# option lineart-fix # lineart may be faster with this option off. -# option disable-backtracking # faster, but may produce stripes - -#-------------------------- 600 II N ---------------------------------------- -#0x2eb - # For the 600 II N try one of 0x26b, 0x2ab, - # 0x2eb, 0x22b, 0x32b, 0x36b, 0x3ab, 0x3eb. -# option linedistance-fix # only neccessary with firmware 2.x - -#-------------------------- 600 II EP --------------------------------------- -#parport0 - # parport0, parport1, ..., - # or: 0x378(=lpt1), 0x278(=lpt2), 0x3bc(=lpt3) diff -uNr sane-backends-1.0.17/backend/mustek.conf.in sane-backends-1.0.18/backend/mustek.conf.in --- sane-backends-1.0.17/backend/mustek.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/mustek.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,41 @@ +# See sane-mustek(5) for documentation. + +#--------------------------- Global options --------------------------------- +#option strip-height 1 # some SCSI adapters need this; scanning may + # be faster without this option +#option force-wait # wait for scanner to be ready (only necessary + # when scanner freezes) +#option disable-double-buffering # try this if you have SCSI trouble + +#-------------------------- SCSI scanners ----------------------------------- +scsi MUSTEK * Scanner +# option linedistance-fix # stripes may go away in color mode +# option buffersize 1024 # set non standard buffer size (in kb) +# option blocksize 2048 # set non standard block size (in kb) +# option lineart-fix # lineart may be faster with this option off. +# option disable-backtracking # faster, but may produce stripes + +scsi SCANNER +# option linedistance-fix # stripes may go away in color mode +# option buffersize 1024 # set non standard buffer size (in kb) +# option blocksize 2048 # set non standard block size (in kb) +# option lineart-fix # lineart may be faster with this option off. +# option disable-backtracking # faster, but may produce stripes + +/dev/scanner +# option linedistance-fix # stripes may go away in color mode +# option buffersize 1024 # set non standard buffer size (in kb) +# option blocksize 2048 # set non standard block size (in kb) +# option lineart-fix # lineart may be faster with this option off. +# option disable-backtracking # faster, but may produce stripes + +#-------------------------- 600 II N ---------------------------------------- +#0x2eb + # For the 600 II N try one of 0x26b, 0x2ab, + # 0x2eb, 0x22b, 0x32b, 0x36b, 0x3ab, 0x3eb. +# option linedistance-fix # only neccessary with firmware 2.x + +#-------------------------- 600 II EP --------------------------------------- +#parport0 + # parport0, parport1, ..., + # or: 0x378(=lpt1), 0x278(=lpt2), 0x3bc(=lpt3) diff -uNr sane-backends-1.0.17/backend/mustek_pp_cis.c sane-backends-1.0.18/backend/mustek_pp_cis.c --- sane-backends-1.0.17/backend/mustek_pp_cis.c 2005-10-23 10:39:55.000000000 +0200 +++ sane-backends-1.0.18/backend/mustek_pp_cis.c 2006-06-11 23:16:43.000000000 +0200 @@ -1,6 +1,6 @@ /* sane - Scanner Access Now Easy. - Copyright (C) 2001-2003 Eddy De Greef + Copyright (C) 2001-2003 Eddy De Greef This file is part of the SANE package. This program is free software; you can redistribute it and/or diff -uNr sane-backends-1.0.17/backend/mustek_pp_cis.h sane-backends-1.0.18/backend/mustek_pp_cis.h --- sane-backends-1.0.17/backend/mustek_pp_cis.h 2005-10-23 10:39:55.000000000 +0200 +++ sane-backends-1.0.18/backend/mustek_pp_cis.h 2006-06-11 23:16:44.000000000 +0200 @@ -1,6 +1,6 @@ /* sane - Scanner Access Now Easy. - Copyright (C) 2001-2003 Eddy De Greef + Copyright (C) 2001-2003 Eddy De Greef This file is part of the SANE package. This program is free software; you can redistribute it and/or diff -uNr sane-backends-1.0.17/backend/mustek_pp.conf sane-backends-1.0.18/backend/mustek_pp.conf --- sane-backends-1.0.17/backend/mustek_pp.conf 2005-04-16 15:06:37.000000000 +0200 +++ sane-backends-1.0.18/backend/mustek_pp.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,103 +0,0 @@ -# For documentation see sane-mustek_pp(5) - -# Global options: -# =============== -# -# option no_epp -# -# Disable parallel port mode EPP: works around a known bug in -# the Linux parport code. Enable this option, if the backend -# hangs when trying to access the parallel port in EPP mode: -# -# # SANE_DEBUG_SANEI_PA4S2=128 scanimage -L -# ... -# hangs here -> [sanei_pa4s2] sanei_pa4s2_readbyte: read in EPP mode -# -# Scanner definition template: -# ============================ -# -# scanner -# option ? -# option ? -# ... -# -# where: -# -# is an arbitrary name for the scanner (eg. Mustek-1200CP) -# -# is the parallel port to which the scanner is connected -# Possible values are 0x378, 0x278, and 0x3bc. For Linux, the -# mapping between ports an numbers is different for kernel -# version 2.2 and 2.4. Port 0x378 corresponds to lp0 on 2.4 kernel. -# If you are using libieee1284, you can as well use parport0, etc.. -# If you use the magic value * the port is probed. -# -# is an identification of the scanner type. -# Possible values are: -# - cis600 (for Mustek 600CP & OEM versions), -# - cis1200 (for Mustek 1200CP & OEM versions), -# - cis1200+ (for Mustek 1200CP+ & OEM versions), -# - ccd300 (for Mustek 600 III EPP & OEM versions) -# - ... more types will be added in the future -# -# is a name of an option, and an optional value -# for the option. -# Currently available options for *CIS* type scanners are: -# - top_adjust : -# Vertical adjustment of origin, in millimeter. -# Values between -5.0 and +5.0 mm are possible -# (floating point). -# Default: 0.0 -# - slow_skip: -# Boolean option. Disables fast skipping to the start -# of the scan region. May be necessary in case fast -# skipping results in inaccuracies. -# Default: fast skipping enabled -# - bw : -# Black/white discrimination value for lineart scans. -# Pixel values below that value are considered black, -# others are considered white. Range: 0-255. -# Default: 127 -# -# Currently available options for *CCD* type scanners are: -# - wait_bank -# usecs to wait for a bank change. Positive integer -# values are possible. You shouldn't mess with this -# parameter. -# Default: 700 -# - bw -# Black/white discrimination value for lineart scans. -# Pixel values below that value are considered black, -# others are considered white. Range: 0-255. -# Default: 127 -# - top -# Scanlines to skip to the top area. Positive integer -# values are possible. 47 and 56 are values I know of. -# Default: 47 -# -# -# Example for a LifeTec LT9350 (Mustek 1200CP clone): -# -# scanner LT9350 0x378 cis1200 -# option top_adjust 0 -# option bw 127 -# -# Example for Mustek 6000P -# -# scanner 6000P 0x378 ccd300 -# option top 56 -# -# -# Uncomment/customize to your needs -# -# scanner Mustek-600CP 0x378 cis600 -# scanner Mustek-1200CP 0x378 cis1200 -# scanner Mustek-1200CP+ 0x378 cis1200+ -# scanner Mustek-600-IIIEP 0x378 ccd300 -# -# auto probing: -# -# scanner mustek-cis600 * cis600 -# scanner mustek-cis1200 * cis1200 -# scanner mustek-cis1200+ * cis1200+ -# scanner mustek-ccd300 * ccd300 diff -uNr sane-backends-1.0.17/backend/mustek_pp.conf.in sane-backends-1.0.18/backend/mustek_pp.conf.in --- sane-backends-1.0.17/backend/mustek_pp.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/mustek_pp.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,103 @@ +# For documentation see sane-mustek_pp(5) + +# Global options: +# =============== +# +# option no_epp +# +# Disable parallel port mode EPP: works around a known bug in +# the Linux parport code. Enable this option, if the backend +# hangs when trying to access the parallel port in EPP mode: +# +# # SANE_DEBUG_SANEI_PA4S2=128 scanimage -L +# ... +# hangs here -> [sanei_pa4s2] sanei_pa4s2_readbyte: read in EPP mode +# +# Scanner definition template: +# ============================ +# +# scanner +# option ? +# option ? +# ... +# +# where: +# +# is an arbitrary name for the scanner (eg. Mustek-1200CP) +# +# is the parallel port to which the scanner is connected +# Possible values are 0x378, 0x278, and 0x3bc. For Linux, the +# mapping between ports an numbers is different for kernel +# version 2.2 and 2.4. Port 0x378 corresponds to lp0 on 2.4 kernel. +# If you are using libieee1284, you can as well use parport0, etc.. +# If you use the magic value * the port is probed. +# +# is an identification of the scanner type. +# Possible values are: +# - cis600 (for Mustek 600CP & OEM versions), +# - cis1200 (for Mustek 1200CP & OEM versions), +# - cis1200+ (for Mustek 1200CP+ & OEM versions), +# - ccd300 (for Mustek 600 III EPP & OEM versions) +# - ... more types will be added in the future +# +# is a name of an option, and an optional value +# for the option. +# Currently available options for *CIS* type scanners are: +# - top_adjust : +# Vertical adjustment of origin, in millimeter. +# Values between -5.0 and +5.0 mm are possible +# (floating point). +# Default: 0.0 +# - slow_skip: +# Boolean option. Disables fast skipping to the start +# of the scan region. May be necessary in case fast +# skipping results in inaccuracies. +# Default: fast skipping enabled +# - bw : +# Black/white discrimination value for lineart scans. +# Pixel values below that value are considered black, +# others are considered white. Range: 0-255. +# Default: 127 +# +# Currently available options for *CCD* type scanners are: +# - wait_bank +# usecs to wait for a bank change. Positive integer +# values are possible. You shouldn't mess with this +# parameter. +# Default: 700 +# - bw +# Black/white discrimination value for lineart scans. +# Pixel values below that value are considered black, +# others are considered white. Range: 0-255. +# Default: 127 +# - top +# Scanlines to skip to the top area. Positive integer +# values are possible. 47 and 56 are values I know of. +# Default: 47 +# +# +# Example for a LifeTec LT9350 (Mustek 1200CP clone): +# +# scanner LT9350 0x378 cis1200 +# option top_adjust 0 +# option bw 127 +# +# Example for Mustek 6000P +# +# scanner 6000P 0x378 ccd300 +# option top 56 +# +# +# Uncomment/customize to your needs +# +# scanner Mustek-600CP 0x378 cis600 +# scanner Mustek-1200CP 0x378 cis1200 +# scanner Mustek-1200CP+ 0x378 cis1200+ +# scanner Mustek-600-IIIEP 0x378 ccd300 +# +# auto probing: +# +# scanner mustek-cis600 * cis600 +# scanner mustek-cis1200 * cis1200 +# scanner mustek-cis1200+ * cis1200+ +# scanner mustek-ccd300 * ccd300 diff -uNr sane-backends-1.0.17/backend/mustek_usb.conf sane-backends-1.0.18/backend/mustek_usb.conf --- sane-backends-1.0.17/backend/mustek_usb.conf 2003-09-12 14:45:44.000000000 +0200 +++ sane-backends-1.0.18/backend/mustek_usb.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,39 +0,0 @@ -# mustek_usb.conf: Configuration file for Mustek USB scanner -# Read man sane-mustek_usb for documentation - -# If USB errors occur, using this option may help -#option max_block_size 1024 - -# Autodetect 1200 UB and Trust Compact Scan USB 19200 -usb 0x055f 0x0006 - -# Autodetect 1200 USB (not supported) -# usb 0x055f 0x0003 - -# Autodetect 1200 CU -usb 0x055f 0x0001 - -# Autodetect 1200 CU Plus -usb 0x055f 0x0008 - -# Autodetect 600 CU -usb 0x055f 0x0002 - -# Autodetect 600 USB (not supported) -usb 0x055f 0x0873 - -# If autodetection doesn't work uncomment or add your device file and one -# suitable option (1200ub is also for Trust Compact Scan USB 19200). - -#/dev/usb/scanner0 -#option 1200ub -#option 1200cu -#option 1200cu_plus -#option 600cu - -#/dev/usbscanner0 -#option 1200ub -#option 1200cu -#option 1200cu_plus -#option 600cu - diff -uNr sane-backends-1.0.17/backend/mustek_usb.conf.in sane-backends-1.0.18/backend/mustek_usb.conf.in --- sane-backends-1.0.17/backend/mustek_usb.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/mustek_usb.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,39 @@ +# mustek_usb.conf: Configuration file for Mustek USB scanner +# Read man sane-mustek_usb for documentation + +# If USB errors occur, using this option may help +#option max_block_size 1024 + +# Autodetect 1200 UB and Trust Compact Scan USB 19200 +usb 0x055f 0x0006 + +# Autodetect 1200 USB (not supported) +# usb 0x055f 0x0003 + +# Autodetect 1200 CU +usb 0x055f 0x0001 + +# Autodetect 1200 CU Plus +usb 0x055f 0x0008 + +# Autodetect 600 CU +usb 0x055f 0x0002 + +# Autodetect 600 USB (not supported) +usb 0x055f 0x0873 + +# If autodetection doesn't work uncomment or add your device file and one +# suitable option (1200ub is also for Trust Compact Scan USB 19200). + +#/dev/usb/scanner0 +#option 1200ub +#option 1200cu +#option 1200cu_plus +#option 600cu + +#/dev/usbscanner0 +#option 1200ub +#option 1200cu +#option 1200cu_plus +#option 600cu + diff -uNr sane-backends-1.0.17/backend/nec.conf sane-backends-1.0.18/backend/nec.conf --- sane-backends-1.0.17/backend/nec.conf 2000-11-21 02:03:33.000000000 +0100 +++ sane-backends-1.0.18/backend/nec.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -/dev/scanner diff -uNr sane-backends-1.0.17/backend/nec.conf.in sane-backends-1.0.18/backend/nec.conf.in --- sane-backends-1.0.17/backend/nec.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/nec.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1 @@ +/dev/scanner diff -uNr sane-backends-1.0.17/backend/net.conf sane-backends-1.0.18/backend/net.conf --- sane-backends-1.0.17/backend/net.conf 1999-08-09 20:05:44.000000000 +0200 +++ sane-backends-1.0.18/backend/net.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,5 +0,0 @@ -# This is the net config file. Each line names a host to attach to. -# If you list "localhost" then your backends can be accessed either -# directly or through the net backend. Going through the net backend -# may be necessary to access devices that need special privileges. -# localhost diff -uNr sane-backends-1.0.17/backend/net.conf.in sane-backends-1.0.18/backend/net.conf.in --- sane-backends-1.0.17/backend/net.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/net.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,5 @@ +# This is the net config file. Each line names a host to attach to. +# If you list "localhost" then your backends can be accessed either +# directly or through the net backend. Going through the net backend +# may be necessary to access devices that need special privileges. +# localhost diff -uNr sane-backends-1.0.17/backend/niash.c sane-backends-1.0.18/backend/niash.c --- sane-backends-1.0.17/backend/niash.c 2004-10-30 16:48:17.000000000 +0200 +++ sane-backends-1.0.18/backend/niash.c 2006-02-04 12:34:28.000000000 +0100 @@ -15,7 +15,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - $Id: niash.c,v 1.12 2004/10/27 18:06:19 ullsig-guest Exp $ + $Id: niash.c,v 1.13 2006/02/04 10:28:50 ullsig-guest Exp $ */ /* @@ -31,8 +31,8 @@ #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" -#include /* malloc, free */ -#include /* memcpy */ +#include /* malloc, free */ +#include /* memcpy */ #include #include #include @@ -73,7 +73,7 @@ optDPI, optGroupImage, - optGammaTable, /* gamma table */ + optGammaTable, /* gamma table */ optGroupMode, optMode, @@ -89,14 +89,14 @@ optLamp, optCalibrate, - optGamma /* analog gamma = single number */ + optGamma /* analog gamma = single number */ } EOptionIndex; typedef union { SANE_Word w; - SANE_Word *wa; /* word array */ + SANE_Word *wa; /* word array */ SANE_String s; } TOptionValue; @@ -112,20 +112,20 @@ THWParams HWParams; TDataPipe DataPipe; - int iLinesLeft; /* lines to scan */ - int iBytesLeft; /* bytes to read */ - int iPixelsPerLine; /* pixels in one scan line */ + int iLinesLeft; /* lines to scan */ + int iBytesLeft; /* bytes to read */ + int iPixelsPerLine; /* pixels in one scan line */ - SANE_Int aGammaTable[SANE_GAMMA_SIZE]; /* a 12-to-8 bit color lookup table */ + SANE_Int aGammaTable[SANE_GAMMA_SIZE]; /* a 12-to-8 bit color lookup table */ /* fCancelled needed to let sane issue the cancel message instead of an error message */ - SANE_Bool fCancelled; /* SANE_TRUE if scanning cancelled */ + SANE_Bool fCancelled; /* SANE_TRUE if scanning cancelled */ - SANE_Bool fScanning; /* SANE_TRUE if actively scanning */ + SANE_Bool fScanning; /* SANE_TRUE if actively scanning */ - int WarmUpTime; /* time to wait before a calibration starts */ - unsigned char CalWhite[3]; /* values for the last calibration of white */ + int WarmUpTime; /* time to wait before a calibration starts */ + unsigned char CalWhite[3]; /* values for the last calibration of white */ struct timeval WarmUpStarted; /* system type to trace the time elapsed */ } TScanner; @@ -155,7 +155,7 @@ SANE_FIX (0.0) }; -/* interpolate a sane gamma table to a hardware appropriate one +/* interpolate a sane gamma table to a hardware appropriate one just in case the sane gamma table would be smaller */ static void _ConvertGammaTable (SANE_Word * saneGamma, unsigned char *hwGamma) @@ -175,11 +175,11 @@ /* the interpolation of the rest depends on the gap */ for (j = current + 1; j < HW_GAMMA_SIZE && j < next; ++j) - { - hwGamma[j] = - (saneGamma[i] * (next - j) + - saneGamma[i + 1] * (j - current)) / (next - current); - } + { + hwGamma[j] = + (saneGamma[i] * (next - j) + + saneGamma[i + 1] * (j - current)) / (next - current); + } current = next; } } @@ -241,7 +241,7 @@ _bytesPerLineLineart (int pixelsPerLine) { return (pixelsPerLine * BITS_PER_PIXEL_LINEART + - BITS_PADDING) / BITS_PER_BYTE; + BITS_PADDING) / BITS_PER_BYTE; } static int @@ -254,7 +254,7 @@ _bytesPerLineColor (int pixelsPerLine) { return (pixelsPerLine * BITS_PER_PIXEL_COLOR + - BITS_PADDING) / BITS_PER_BYTE; + BITS_PADDING) / BITS_PER_BYTE; } @@ -290,11 +290,11 @@ { acc += aWeight[x % BYTES_PER_PIXEL_COLOR] * buffer[x]; if ((x + 1) % BYTES_PER_PIXEL_COLOR == 0) - { - buffer[x / BYTES_PER_PIXEL_COLOR] = - (unsigned char) (acc / WEIGHT_W); - acc = 0; - } + { + buffer[x / BYTES_PER_PIXEL_COLOR] = + (unsigned char) (acc / WEIGHT_W); + acc = 0; + } } #undef WEIGHT_R #undef WEIGHT_G @@ -317,14 +317,14 @@ for (x = 0; x < nx; ++x) { if (x < pixels && buffer[x] < thresh) - { - acc |= aMask[x % BITS_PER_BYTE]; - } + { + acc |= aMask[x % BITS_PER_BYTE]; + } if ((x + 1) % BITS_PER_BYTE == 0) - { - buffer[x / BITS_PER_BYTE] = (unsigned char) (acc); - acc = 0; - } + { + buffer[x / BITS_PER_BYTE] = (unsigned char) (acc); + acc = 0; + } } } @@ -344,11 +344,11 @@ }; -#define WARMUP_AFTERSTART 1 /* flag for 1st warm up */ +#define WARMUP_AFTERSTART 1 /* flag for 1st warm up */ #define WARMUP_INSESSION 0 -#define WARMUP_TESTINTERVAL 15 /* test every 15sec */ -#define WARMUP_TIME 30 /* first wait is 30sec minimum */ -#define WARMUP_MAXTIME 90 /* after one and a half minute start latest */ +#define WARMUP_TESTINTERVAL 15 /* test every 15sec */ +#define WARMUP_TIME 30 /* first wait is 30sec minimum */ +#define WARMUP_MAXTIME 90 /* after one and a half minute start latest */ #define CAL_DEV_MAX 15 /* maximum deviation of cal values in percent between 2 tests */ @@ -399,13 +399,13 @@ static void _WaitForLamp (TScanner * s, unsigned char *pabCalibTable) { - struct timeval now[2]; /* toggling time holder */ - int i; /* rgb loop */ - int iCal = 0; /* counter */ - int iCurrent = 0; /* buffer and time-holder swap flag */ + struct timeval now[2]; /* toggling time holder */ + int i; /* rgb loop */ + int iCal = 0; /* counter */ + int iCurrent = 0; /* buffer and time-holder swap flag */ SANE_Bool fHasCal; - unsigned char CalWhite[2][3]; /* toggling buffer */ - int iDelay = 0; /* delay loop counter */ + unsigned char CalWhite[2][3]; /* toggling buffer */ + int iDelay = 0; /* delay loop counter */ _WarmUpLamp (s, SANE_FALSE); @@ -424,101 +424,101 @@ /* check if the last scan has lower calibration values than the current one would have */ if (s->WarmUpTime && fHasCal) - { - SANE_Bool fOver = SANE_TRUE; - for (i = 0; fOver && i < 3; ++i) - { - if (!s->CalWhite[i]) - fOver = SANE_FALSE; - else if (CalWhite[iCurrent][i] < s->CalWhite[i]) - fOver = SANE_FALSE; - } - - /* warm up is not needed, when calibration data is above - the calibration data of the last scan */ - if (fOver) - { - s->WarmUpTime = 0; - DBG (DBG_MSG, - "_WaitForLamp: Values seem stable, skipping next calibration cycle\n"); - } - } + { + SANE_Bool fOver = SANE_TRUE; + for (i = 0; fOver && i < 3; ++i) + { + if (!s->CalWhite[i]) + fOver = SANE_FALSE; + else if (CalWhite[iCurrent][i] < s->CalWhite[i]) + fOver = SANE_FALSE; + } + + /* warm up is not needed, when calibration data is above + the calibration data of the last scan */ + if (fOver) + { + s->WarmUpTime = 0; + DBG (DBG_MSG, + "_WaitForLamp: Values seem stable, skipping next calibration cycle\n"); + } + } /* break the loop, when the longest wait time has expired - to prevent a hanging application, + to prevent a hanging application, even if the values might not be good, yet */ if (s->WarmUpTime && fHasCal && iCal) - { - /* abort, when we have waited long enough */ - if (_TimeElapsed - (&s->WarmUpStarted, &now[iCurrent], WARMUP_MAXTIME)) - { - /* stop idling */ - s->WarmUpTime = 0; - DBG (DBG_MSG, "_WaitForLamp: WARMUP_MAXTIME=%ds elapsed!\n", - WARMUP_MAXTIME); - } - } + { + /* abort, when we have waited long enough */ + if (_TimeElapsed + (&s->WarmUpStarted, &now[iCurrent], WARMUP_MAXTIME)) + { + /* stop idling */ + s->WarmUpTime = 0; + DBG (DBG_MSG, "_WaitForLamp: WARMUP_MAXTIME=%ds elapsed!\n", + WARMUP_MAXTIME); + } + } /* enter a delay loop, when there is still time to wait */ if (s->WarmUpTime) - { - /* if the (too low) calibration values have just been acquired - we start waiting */ - if (fHasCal) - DBG (DBG_MSG, "_WaitForLamp: entering delay loop\r"); - else - DBG (DBG_MSG, "_WaitForLamp: delay loop %d \r", ++iDelay); - sleep (1); - fHasCal = SANE_FALSE; - gettimeofday (&now[!iCurrent], 0); - } + { + /* if the (too low) calibration values have just been acquired + we start waiting */ + if (fHasCal) + DBG (DBG_MSG, "_WaitForLamp: entering delay loop\r"); + else + DBG (DBG_MSG, "_WaitForLamp: delay loop %d \r", ++iDelay); + sleep (1); + fHasCal = SANE_FALSE; + gettimeofday (&now[!iCurrent], 0); + } /* look if we should check again */ - if (s->WarmUpTime /* did we have to wait at all */ - /* is the minimum time elapsed */ - && _TimeElapsed (&s->WarmUpStarted, &now[!iCurrent], s->WarmUpTime) - /* has the minimum time elapsed since the last calibration */ - && _TimeElapsed (&now[iCurrent], &now[!iCurrent], - WARMUP_TESTINTERVAL)) - { - int dev = 0; /* 0 percent deviation in cal value as default */ - iDelay = 0; /* all delays processed */ - /* new calibration */ - ++iCal; - iCurrent = !iCurrent; /* swap the test-buffer, and time-holder */ - SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]); - fHasCal = SANE_TRUE; - - for (i = 0; i < 3; ++i) - { - /* copy for faster and clearer access */ - int cwa; - int cwb; - int ldev; - cwa = CalWhite[!iCurrent][i]; - cwb = CalWhite[iCurrent][i]; - /* find the biggest deviation of one color */ - if (cwa > cwb) - ldev = 0; - else if (cwa && cwb) - ldev = ((cwb - cwa) * 100) / cwb; - else - ldev = 100; - dev = MAX (dev, ldev); - } - - /* show the biggest deviation of the calibration values */ - DBG (DBG_MSG, "_WaitForLamp: recalibration #%d, deviation = %d%%\n", - iCal, dev); - - /* the deviation to the previous calibration is tolerable */ - if (dev <= CAL_DEV_MAX) - s->WarmUpTime = 0; - } + if (s->WarmUpTime /* did we have to wait at all */ + /* is the minimum time elapsed */ + && _TimeElapsed (&s->WarmUpStarted, &now[!iCurrent], s->WarmUpTime) + /* has the minimum time elapsed since the last calibration */ + && _TimeElapsed (&now[iCurrent], &now[!iCurrent], + WARMUP_TESTINTERVAL)) + { + int dev = 0; /* 0 percent deviation in cal value as default */ + iDelay = 0; /* all delays processed */ + /* new calibration */ + ++iCal; + iCurrent = !iCurrent; /* swap the test-buffer, and time-holder */ + SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]); + fHasCal = SANE_TRUE; + + for (i = 0; i < 3; ++i) + { + /* copy for faster and clearer access */ + int cwa; + int cwb; + int ldev; + cwa = CalWhite[!iCurrent][i]; + cwb = CalWhite[iCurrent][i]; + /* find the biggest deviation of one color */ + if (cwa > cwb) + ldev = 0; + else if (cwa && cwb) + ldev = ((cwb - cwa) * 100) / cwb; + else + ldev = 100; + dev = MAX (dev, ldev); + } + + /* show the biggest deviation of the calibration values */ + DBG (DBG_MSG, "_WaitForLamp: recalibration #%d, deviation = %d%%\n", + iCal, dev); + + /* the deviation to the previous calibration is tolerable */ + if (dev <= CAL_DEV_MAX) + s->WarmUpTime = 0; + } } /* remember the values of this calibration @@ -541,10 +541,10 @@ { int iData; iData = - floor (256.0 * - pow (((double) j / (double) SANE_GAMMA_SIZE), 1.0 / fGamma)); + floor (256.0 * + pow (((double) j / (double) SANE_GAMMA_SIZE), 1.0 / fGamma)); if (iData > 255) - iData = 255; + iData = 255; aiGamma[j] = iData; } } @@ -561,7 +561,7 @@ { size = strlen (strings[i]) + 1; if (size > max_size) - max_size = size; + max_size = size; } return max_size; } @@ -609,172 +609,172 @@ pDesc->cap = 0; switch (i) - { + { - case optCount: - pDesc->title = SANE_TITLE_NUM_OPTIONS; - pDesc->desc = SANE_DESC_NUM_OPTIONS; - pDesc->cap = SANE_CAP_SOFT_DETECT; - pVal->w = (SANE_Word) optLast; - break; - - case optGroupGeometry: - pDesc->title = "Geometry"; - pDesc->type = SANE_TYPE_GROUP; - pDesc->size = 0; - break; - - case optTLX: - pDesc->name = SANE_NAME_SCAN_TL_X; - pDesc->title = SANE_TITLE_SCAN_TL_X; - pDesc->desc = SANE_DESC_SCAN_TL_X; - pDesc->unit = SANE_UNIT_MM; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeXmm; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = rangeXmm.min; - break; - - case optTLY: - pDesc->name = SANE_NAME_SCAN_TL_Y; - pDesc->title = SANE_TITLE_SCAN_TL_Y; - pDesc->desc = SANE_DESC_SCAN_TL_Y; - pDesc->unit = SANE_UNIT_MM; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeYmm; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = rangeYmm.min; - break; - - case optBRX: - pDesc->name = SANE_NAME_SCAN_BR_X; - pDesc->title = SANE_TITLE_SCAN_BR_X; - pDesc->desc = SANE_DESC_SCAN_BR_X; - pDesc->unit = SANE_UNIT_MM; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeXmm; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = 210 /* A4 width instead of rangeXmm.max */ ; - break; - - case optBRY: - pDesc->name = SANE_NAME_SCAN_BR_Y; - pDesc->title = SANE_TITLE_SCAN_BR_Y; - pDesc->desc = SANE_DESC_SCAN_BR_Y; - pDesc->unit = SANE_UNIT_MM; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeYmm; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = 290 /* have a bit reserve instaed of rangeYmm.max */ ; - break; - - case optDPI: - pDesc->name = SANE_NAME_SCAN_RESOLUTION; - pDesc->title = SANE_TITLE_SCAN_RESOLUTION; - pDesc->desc = SANE_DESC_SCAN_RESOLUTION; - pDesc->unit = SANE_UNIT_DPI; - pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; - pDesc->constraint.word_list = setResolutions; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = setResolutions[2]; /* default to 150dpi */ - break; - - case optGroupImage: - pDesc->title = SANE_I18N ("Image"); - pDesc->type = SANE_TYPE_GROUP; - pDesc->size = 0; - break; - - case optGamma: - pDesc->name = SANE_NAME_ANALOG_GAMMA; - pDesc->title = SANE_TITLE_ANALOG_GAMMA; - pDesc->desc = SANE_DESC_ANALOG_GAMMA; - pDesc->type = SANE_TYPE_FIXED; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeGamma; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->w = startUpGamma; - break; - - case optGammaTable: - pDesc->name = SANE_NAME_GAMMA_VECTOR; - pDesc->title = SANE_TITLE_GAMMA_VECTOR; - pDesc->desc = SANE_DESC_GAMMA_VECTOR; - pDesc->size = sizeof (s->aGammaTable); - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeGammaTable; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - pVal->wa = s->aGammaTable; - break; - - case optGroupMisc: - pDesc->title = SANE_I18N ("Miscellaneous"); - pDesc->type = SANE_TYPE_GROUP; - pDesc->size = 0; - break; - - case optLamp: - pDesc->name = "lamp"; - pDesc->title = SANE_I18N ("Lamp status"); - pDesc->desc = SANE_I18N ("Switches the lamp on or off."); - pDesc->type = SANE_TYPE_BOOL; - pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - /* switch the lamp on when starting for first the time */ - pVal->w = SANE_TRUE; - break; - - case optCalibrate: - pDesc->name = "calibrate"; - pDesc->title = SANE_I18N ("Calibrate"); - pDesc->desc = SANE_I18N ("Calibrates for black and white level."); - pDesc->type = SANE_TYPE_BUTTON; - pDesc->cap = SANE_CAP_SOFT_SELECT; - pDesc->size = 0; - break; - - case optGroupMode: - pDesc->title = SANE_I18N ("Scan Mode"); - pDesc->desc = ""; - pDesc->type = SANE_TYPE_GROUP; - break; - - case optMode: - /* scan mode */ - pDesc->name = SANE_NAME_SCAN_MODE; - pDesc->title = SANE_TITLE_SCAN_MODE; - pDesc->desc = SANE_DESC_SCAN_MODE; - pDesc->type = SANE_TYPE_STRING; - pDesc->size = _MaxStringSize (modeList); - pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; - pDesc->constraint.string_list = modeList; - pDesc->cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED; - pVal->w = MODE_COLOR; - break; - - case optGroupEnhancement: - pDesc->title = SANE_I18N ("Enhancement"); - pDesc->desc = ""; - pDesc->type = SANE_TYPE_GROUP; - break; - - case optThreshold: - pDesc->name = SANE_NAME_THRESHOLD; - pDesc->title = SANE_TITLE_THRESHOLD; - pDesc->desc = SANE_DESC_THRESHOLD; - pDesc->type = SANE_TYPE_INT; - pDesc->unit = SANE_UNIT_PERCENT; - pDesc->constraint_type = SANE_CONSTRAINT_RANGE; - pDesc->constraint.range = &rangeThreshold; - pDesc->cap = - SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | - SANE_CAP_EMULATED; - pVal->w = 50; - - default: - DBG (DBG_ERR, "Uninitialised option %d\n", i); - break; - } + case optCount: + pDesc->title = SANE_TITLE_NUM_OPTIONS; + pDesc->desc = SANE_DESC_NUM_OPTIONS; + pDesc->cap = SANE_CAP_SOFT_DETECT; + pVal->w = (SANE_Word) optLast; + break; + + case optGroupGeometry: + pDesc->title = "Geometry"; + pDesc->type = SANE_TYPE_GROUP; + pDesc->size = 0; + break; + + case optTLX: + pDesc->name = SANE_NAME_SCAN_TL_X; + pDesc->title = SANE_TITLE_SCAN_TL_X; + pDesc->desc = SANE_DESC_SCAN_TL_X; + pDesc->unit = SANE_UNIT_MM; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeXmm; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = rangeXmm.min; + break; + + case optTLY: + pDesc->name = SANE_NAME_SCAN_TL_Y; + pDesc->title = SANE_TITLE_SCAN_TL_Y; + pDesc->desc = SANE_DESC_SCAN_TL_Y; + pDesc->unit = SANE_UNIT_MM; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeYmm; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = rangeYmm.min; + break; + + case optBRX: + pDesc->name = SANE_NAME_SCAN_BR_X; + pDesc->title = SANE_TITLE_SCAN_BR_X; + pDesc->desc = SANE_DESC_SCAN_BR_X; + pDesc->unit = SANE_UNIT_MM; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeXmm; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = 210 /* A4 width instead of rangeXmm.max */ ; + break; + + case optBRY: + pDesc->name = SANE_NAME_SCAN_BR_Y; + pDesc->title = SANE_TITLE_SCAN_BR_Y; + pDesc->desc = SANE_DESC_SCAN_BR_Y; + pDesc->unit = SANE_UNIT_MM; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeYmm; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = 290 /* have a bit reserve instaed of rangeYmm.max */ ; + break; + + case optDPI: + pDesc->name = SANE_NAME_SCAN_RESOLUTION; + pDesc->title = SANE_TITLE_SCAN_RESOLUTION; + pDesc->desc = SANE_DESC_SCAN_RESOLUTION; + pDesc->unit = SANE_UNIT_DPI; + pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; + pDesc->constraint.word_list = setResolutions; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = setResolutions[2]; /* default to 150dpi */ + break; + + case optGroupImage: + pDesc->title = SANE_I18N ("Image"); + pDesc->type = SANE_TYPE_GROUP; + pDesc->size = 0; + break; + + case optGamma: + pDesc->name = SANE_NAME_ANALOG_GAMMA; + pDesc->title = SANE_TITLE_ANALOG_GAMMA; + pDesc->desc = SANE_DESC_ANALOG_GAMMA; + pDesc->type = SANE_TYPE_FIXED; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeGamma; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->w = startUpGamma; + break; + + case optGammaTable: + pDesc->name = SANE_NAME_GAMMA_VECTOR; + pDesc->title = SANE_TITLE_GAMMA_VECTOR; + pDesc->desc = SANE_DESC_GAMMA_VECTOR; + pDesc->size = sizeof (s->aGammaTable); + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeGammaTable; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + pVal->wa = s->aGammaTable; + break; + + case optGroupMisc: + pDesc->title = SANE_I18N ("Miscellaneous"); + pDesc->type = SANE_TYPE_GROUP; + pDesc->size = 0; + break; + + case optLamp: + pDesc->name = "lamp"; + pDesc->title = SANE_I18N ("Lamp status"); + pDesc->desc = SANE_I18N ("Switches the lamp on or off."); + pDesc->type = SANE_TYPE_BOOL; + pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + /* switch the lamp on when starting for first the time */ + pVal->w = SANE_TRUE; + break; + + case optCalibrate: + pDesc->name = "calibrate"; + pDesc->title = SANE_I18N ("Calibrate"); + pDesc->desc = SANE_I18N ("Calibrates for black and white level."); + pDesc->type = SANE_TYPE_BUTTON; + pDesc->cap = SANE_CAP_SOFT_SELECT; + pDesc->size = 0; + break; + + case optGroupMode: + pDesc->title = SANE_I18N ("Scan Mode"); + pDesc->desc = ""; + pDesc->type = SANE_TYPE_GROUP; + break; + + case optMode: + /* scan mode */ + pDesc->name = SANE_NAME_SCAN_MODE; + pDesc->title = SANE_TITLE_SCAN_MODE; + pDesc->desc = SANE_DESC_SCAN_MODE; + pDesc->type = SANE_TYPE_STRING; + pDesc->size = _MaxStringSize (modeList); + pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; + pDesc->constraint.string_list = modeList; + pDesc->cap = + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED; + pVal->w = MODE_COLOR; + break; + + case optGroupEnhancement: + pDesc->title = SANE_I18N ("Enhancement"); + pDesc->desc = ""; + pDesc->type = SANE_TYPE_GROUP; + break; + + case optThreshold: + pDesc->name = SANE_NAME_THRESHOLD; + pDesc->title = SANE_TITLE_THRESHOLD; + pDesc->desc = SANE_DESC_THRESHOLD; + pDesc->type = SANE_TYPE_INT; + pDesc->unit = SANE_UNIT_PERCENT; + pDesc->constraint_type = SANE_CONSTRAINT_RANGE; + pDesc->constraint.range = &rangeThreshold; + pDesc->cap = + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | + SANE_CAP_EMULATED; + pVal->w = 50; + + default: + DBG (DBG_ERR, "Uninitialised option %d\n", i); + break; + } } } @@ -801,9 +801,9 @@ else { for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext) - { - ; - } + { + ; + } pDev->pNext = pNew; } @@ -856,11 +856,11 @@ if (_pSaneDevList) { for (pDev = _pFirstSaneDev; pDev; pDev = pNext) - { - pNext = pDev->pNext; - free ((void *) pDev->dev.name); - free (pDev); - } + { + pNext = pDev->pNext; + free ((void *) pDev->dev.name); + free (pDev); + } _pFirstSaneDev = 0; free (_pSaneDevList); _pSaneDevList = 0; @@ -894,7 +894,7 @@ { _pSaneDevList[i++] = &pDev->dev; } - _pSaneDevList[i++] = 0; /* last entry is 0 */ + _pSaneDevList[i++] = 0; /* last entry is 0 */ *device_list = _pSaneDevList; @@ -981,7 +981,7 @@ SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, - void *pVal, SANE_Int * pInfo) + void *pVal, SANE_Int * pInfo) { TScanner *s; SANE_Bool fVal; @@ -1003,182 +1003,186 @@ { case SANE_ACTION_GET_VALUE: switch (n) - { + { - /* Get options of type SANE_Word */ - case optCount: - case optDPI: - case optGamma: - case optTLX: - case optTLY: - case optBRX: - case optBRY: - case optThreshold: - DBG (DBG_MSG, - "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n, - (int) s->aValues[n].w); - *(SANE_Word *) pVal = s->aValues[n].w; - break; - - /* Get options of type SANE_Word array */ - case optGammaTable: - DBG (DBG_MSG, "Reading gamma table\n"); - memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size); - break; - - case optMode: - DBG (DBG_MSG, "Reading scan mode %s\n", - modeList[s->aValues[optMode].w]); - strcpy ((char *) pVal, modeList[s->aValues[optMode].w]); - break; - - /* Get options of type SANE_Bool */ - case optLamp: - GetLamp (&s->HWParams, &fLampIsOn); - *(SANE_Bool *) pVal = fLampIsOn; - break; - - case optCalibrate: - /* although this option has nothing to read, - it's added here to avoid a warning when running scanimage --help */ - break; - - default: - DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n); - } + /* Get options of type SANE_Word */ + case optCount: + case optDPI: + case optGamma: + case optTLX: + case optTLY: + case optBRX: + case optBRY: + case optThreshold: + DBG (DBG_MSG, + "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n, + (int) s->aValues[n].w); + *(SANE_Word *) pVal = s->aValues[n].w; + break; + + /* Get options of type SANE_Word array */ + case optGammaTable: + DBG (DBG_MSG, "Reading gamma table\n"); + memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size); + break; + + case optMode: + DBG (DBG_MSG, "Reading scan mode %s\n", + modeList[s->aValues[optMode].w]); + strcpy ((char *) pVal, modeList[s->aValues[optMode].w]); + break; + + /* Get options of type SANE_Bool */ + case optLamp: + GetLamp (&s->HWParams, &fLampIsOn); + *(SANE_Bool *) pVal = fLampIsOn; + break; + + case optCalibrate: + /* although this option has nothing to read, + it's added here to avoid a warning when running scanimage --help */ + break; + + default: + DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n); + } break; case SANE_ACTION_SET_VALUE: if (s->fScanning) - { - DBG (DBG_ERR, - "sane_control_option: SANE_ACTION_SET_VALUE not allowed during scan\n"); - return SANE_STATUS_INVAL; - } + { + DBG (DBG_ERR, + "sane_control_option: SANE_ACTION_SET_VALUE not allowed during scan\n"); + return SANE_STATUS_INVAL; + } switch (n) - { + { - case optCount: - return SANE_STATUS_INVAL; + case optCount: + return SANE_STATUS_INVAL; - case optGamma: - case optDPI: - case optTLX: - case optTLY: - case optBRX: - case optBRY: - case optThreshold: - info |= SANE_INFO_RELOAD_PARAMS; - status = sanei_constrain_value (&s->aOptions[n], pVal, &info); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_ERR, "Failed to constrain option %d (%s)\n", n, - s->aOptions[n].title); - return status; - } - - /* check values if they are equal */ - fSame = s->aValues[n].w == *(SANE_Word *) pVal; - - /* set the values */ - s->aValues[n].w = *(SANE_Word *) pVal; - DBG (DBG_MSG, - "sane_control_option: SANE_ACTION_SET_VALUE %d = %d\n", n, - (int) s->aValues[n].w); - if (n == optGamma) - { - if (!fSame && optLast > optGammaTable) - { - info |= SANE_INFO_RELOAD_OPTIONS; - } - _SetScalarGamma (s->aGammaTable, s->aValues[n].w); - } - break; - - case optGammaTable: - DBG (DBG_MSG, "Writing gamma table\n"); - pi = (SANE_Int *) pVal; - memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size); - - /* prepare table for debug */ - strcpy (szTable, "Gamma table summary:"); - for (i = 0; i < SANE_GAMMA_SIZE; i++) - { - if ((SANE_GAMMA_SIZE / 16) && (i % (SANE_GAMMA_SIZE / 16)) == 0) - { - strcat (szTable, "\n"); - DBG (DBG_MSG, szTable); - strcpy (szTable, ""); - } - /* test for number print */ - if ((SANE_GAMMA_SIZE / 64) && (i % (SANE_GAMMA_SIZE / 64)) == 0) - { - sprintf (szTemp, " %04X", pi[i]); - strcat (szTable, szTemp); - } - } - if (strlen (szTable)) - { - strcat (szTable, "\n"); - DBG (DBG_MSG, szTable); - } - break; - - case optMode: - { - SANE_Word *pCap; - int fCapChanged = 0; - - pCap = &s->aOptions[optThreshold].cap; - - if (strcmp ((char const *) pVal, colorStr) == 0) - { - s->aValues[optMode].w = MODE_COLOR; - fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1); - } - if (strcmp ((char const *) pVal, grayStr) == 0) - { - s->aValues[optMode].w = MODE_GRAY; - fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1); - } - if (strcmp ((char const *) pVal, lineartStr) == 0) - { - s->aValues[optMode].w = MODE_LINEART; - fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 0); - - } - info |= SANE_INFO_RELOAD_PARAMS; - if (fCapChanged) - { - info |= SANE_INFO_RELOAD_OPTIONS; - } - DBG (DBG_MSG, "setting scan mode: %s\n", (char const *) pVal); - } - break; - - - - case optLamp: - fVal = *(SANE_Bool *) pVal; - DBG (DBG_MSG, "lamp %s\n", fVal ? "on" : "off"); - if (fVal) - _WarmUpLamp (s, WARMUP_INSESSION); - else - SetLamp (&s->HWParams, SANE_FALSE); - break; + case optGamma: + case optThreshold: + case optDPI: + + info |= SANE_INFO_RELOAD_PARAMS; + /* fall through */ + + case optTLX: + case optTLY: + case optBRX: + case optBRY: + + status = sanei_constrain_value (&s->aOptions[n], pVal, &info); + if (status != SANE_STATUS_GOOD) + { + DBG (DBG_ERR, "Failed to constrain option %d (%s)\n", n, + s->aOptions[n].title); + return status; + } + + /* check values if they are equal */ + fSame = s->aValues[n].w == *(SANE_Word *) pVal; + + /* set the values */ + s->aValues[n].w = *(SANE_Word *) pVal; + DBG (DBG_MSG, + "sane_control_option: SANE_ACTION_SET_VALUE %d = %d\n", n, + (int) s->aValues[n].w); + if (n == optGamma) + { + if (!fSame && optLast > optGammaTable) + { + info |= SANE_INFO_RELOAD_OPTIONS; + } + _SetScalarGamma (s->aGammaTable, s->aValues[n].w); + } + break; + + case optGammaTable: + DBG (DBG_MSG, "Writing gamma table\n"); + pi = (SANE_Int *) pVal; + memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size); + + /* prepare table for debug */ + strcpy (szTable, "Gamma table summary:"); + for (i = 0; i < SANE_GAMMA_SIZE; i++) + { + if ((SANE_GAMMA_SIZE / 16) && (i % (SANE_GAMMA_SIZE / 16)) == 0) + { + strcat (szTable, "\n"); + DBG (DBG_MSG, szTable); + strcpy (szTable, ""); + } + /* test for number print */ + if ((SANE_GAMMA_SIZE / 64) && (i % (SANE_GAMMA_SIZE / 64)) == 0) + { + sprintf (szTemp, " %04X", pi[i]); + strcat (szTable, szTemp); + } + } + if (strlen (szTable)) + { + strcat (szTable, "\n"); + DBG (DBG_MSG, szTable); + } + break; + + case optMode: + { + SANE_Word *pCap; + int fCapChanged = 0; + + pCap = &s->aOptions[optThreshold].cap; + + if (strcmp ((char const *) pVal, colorStr) == 0) + { + s->aValues[optMode].w = MODE_COLOR; + fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1); + } + if (strcmp ((char const *) pVal, grayStr) == 0) + { + s->aValues[optMode].w = MODE_GRAY; + fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1); + } + if (strcmp ((char const *) pVal, lineartStr) == 0) + { + s->aValues[optMode].w = MODE_LINEART; + fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 0); + + } + info |= SANE_INFO_RELOAD_PARAMS; + if (fCapChanged) + { + info |= SANE_INFO_RELOAD_OPTIONS; + } + DBG (DBG_MSG, "setting scan mode: %s\n", (char const *) pVal); + } + break; + + + + case optLamp: + fVal = *(SANE_Bool *) pVal; + DBG (DBG_MSG, "lamp %s\n", fVal ? "on" : "off"); + if (fVal) + _WarmUpLamp (s, WARMUP_INSESSION); + else + SetLamp (&s->HWParams, SANE_FALSE); + break; - case optCalibrate: + case optCalibrate: /* SimpleCalib(&s->HWParams); */ - break; + break; - default: - DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n); - } + default: + DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n); + } if (pInfo != NULL) - { - *pInfo |= info; - } + { + *pInfo |= info; + } break; @@ -1212,12 +1216,12 @@ if (s->aValues[optTLX].w >= s->aValues[optBRX].w) { DBG (DBG_ERR, "TLX should be smaller than BRX\n"); - return SANE_STATUS_INVAL; /* proper error code? */ + return SANE_STATUS_INVAL; /* proper error code? */ } if (s->aValues[optTLY].w >= s->aValues[optBRY].w) { DBG (DBG_ERR, "TLY should be smaller than BRY\n"); - return SANE_STATUS_INVAL; /* proper error code? */ + return SANE_STATUS_INVAL; /* proper error code? */ } @@ -1228,18 +1232,18 @@ p->last_frame = SANE_TRUE; p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w, - s->aValues[optDPI].w); + s->aValues[optDPI].w); p->depth = pMode->depth; p->pixels_per_line = MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w, - s->aValues[optDPI].w); + s->aValues[optDPI].w); p->bytes_per_line = pMode->bytesPerLine (p->pixels_per_line); return SANE_STATUS_GOOD; } -/* get the scale down factor for a resolution that is +/* get the scale down factor for a resolution that is not supported by hardware */ static int _SaneEmulateScaling (int iDpi) @@ -1278,13 +1282,13 @@ s->ScanParams.iLpi = s->aValues[optDPI].w * iScaleDown; /* calculate correction for filling of circular buffer */ - iLineCorr = 3 * s->HWParams.iSensorSkew; /* usually 16 motor steps */ + iLineCorr = 3 * s->HWParams.iSensorSkew; /* usually 16 motor steps */ /* calculate correction for garbage lines */ iLineCorr += s->HWParams.iSkipLines * (HW_LPI / s->ScanParams.iLpi); s->ScanParams.iTop = MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, - HW_LPI) - iLineCorr; + HW_LPI) - iLineCorr; s->ScanParams.iLeft = MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI); @@ -1308,7 +1312,7 @@ } WriteGammaCalibTable (abGamma, abGamma, abGamma, abCalibTable, 0, 0, - &s->HWParams); + &s->HWParams); /* prepare the actual scan */ if (!InitScan (&s->ScanParams, &s->HWParams)) @@ -1324,8 +1328,8 @@ if (s->HWParams.fReg07) { s->DataPipe.iSkipLines += - MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, - s->aValues[optDPI].w * iScaleDown); + MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, + s->aValues[optDPI].w * iScaleDown); } s->iBytesLeft = 0; s->iPixelsPerLine = par.pixels_per_line; @@ -1333,9 +1337,9 @@ /* hack */ s->DataPipe.pabLineBuf = (unsigned char *) malloc (HW_PIXELS * 3); CircBufferInit (s->HWParams.iXferHandle, &s->DataPipe, - par.pixels_per_line, s->ScanParams.iHeight, - s->ScanParams.iLpi * s->HWParams.iSensorSkew / HW_LPI, - s->HWParams.iReversedHead, iScaleDown, iScaleDown); + par.pixels_per_line, s->ScanParams.iHeight, + s->ScanParams.iLpi * s->HWParams.iSensorSkew / HW_LPI, + s->HWParams.iReversedHead, iScaleDown, iScaleDown); s->fScanning = SANE_TRUE; s->fCancelled = SANE_FALSE; @@ -1360,18 +1364,18 @@ if (!s->fScanning) { if (s->fCancelled) - { - DBG (DBG_MSG, "\n"); - DBG (DBG_MSG, "sane_read: sane_read cancelled\n"); - s->fCancelled = SANE_FALSE; - return SANE_STATUS_CANCELLED; - } + { + DBG (DBG_MSG, "\n"); + DBG (DBG_MSG, "sane_read: sane_read cancelled\n"); + s->fCancelled = SANE_FALSE; + return SANE_STATUS_CANCELLED; + } else - { - DBG (DBG_ERR, - "sane_read: sane_read only allowed after sane_start\n"); - return SANE_STATUS_INVAL; - } + { + DBG (DBG_ERR, + "sane_read: sane_read only allowed after sane_start\n"); + return SANE_STATUS_INVAL; + } } p = &s->DataPipe; @@ -1396,36 +1400,36 @@ { /* read a line from the transfer buffer */ if (CircBufferGetLineEx (s->HWParams.iXferHandle, p, p->pabLineBuf, - s->HWParams.iReversedHead, SANE_TRUE)) - { - pMode->adaptFormat (p->pabLineBuf, s->iPixelsPerLine, - s->aValues[optThreshold].w); - s->iBytesLeft = pMode->bytesPerLine (s->iPixelsPerLine); - s->iLinesLeft--; - } + s->HWParams.iReversedHead, SANE_TRUE)) + { + pMode->adaptFormat (p->pabLineBuf, s->iPixelsPerLine, + s->aValues[optThreshold].w); + s->iBytesLeft = pMode->bytesPerLine (s->iPixelsPerLine); + s->iLinesLeft--; + } /* stop scanning further, when the read action fails because we try read after the end of the buffer */ else - { - FinishScan (&s->HWParams); - CircBufferExit (p); - free (p->pabLineBuf); - p->pabLineBuf = NULL; - *len = 0; - DBG (DBG_MSG, "\n"); - DBG (DBG_MSG, "sane_read: read after end of buffer\n"); - s->fCancelled = SANE_FALSE; - s->fScanning = SANE_FALSE; - return SANE_STATUS_EOF; - } + { + FinishScan (&s->HWParams); + CircBufferExit (p); + free (p->pabLineBuf); + p->pabLineBuf = NULL; + *len = 0; + DBG (DBG_MSG, "\n"); + DBG (DBG_MSG, "sane_read: read after end of buffer\n"); + s->fCancelled = SANE_FALSE; + s->fScanning = SANE_FALSE; + return SANE_STATUS_EOF; + } } /* copy (part of) a line */ *len = MIN (maxlen, s->iBytesLeft); memcpy (buf, - &p->pabLineBuf[pMode->bytesPerLine (s->iPixelsPerLine) - - s->iBytesLeft], *len); + &p->pabLineBuf[pMode->bytesPerLine (s->iPixelsPerLine) - + s->iBytesLeft], *len); s->iBytesLeft -= *len; DBG (DBG_MSG, " read=%d \n", *len); diff -uNr sane-backends-1.0.17/backend/niash_xfer.c sane-backends-1.0.18/backend/niash_xfer.c --- sane-backends-1.0.17/backend/niash_xfer.c 2005-02-06 12:55:30.000000000 +0100 +++ sane-backends-1.0.18/backend/niash_xfer.c 2006-02-04 12:34:28.000000000 +0100 @@ -15,17 +15,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - $Id: niash_xfer.c,v 1.3 2005/02/03 18:48:42 ullsig-guest Exp $ + $Id: niash_xfer.c,v 1.4 2006/02/04 10:28:50 ullsig-guest Exp $ */ /* - Provides a simple interface to read and write data from the scanner, - without any knowledge whether it's a parallel or USB scanner + Provides a simple interface to read and write data from the scanner, + without any knowledge whether it's a parallel or USB scanner */ -#include /* printf */ -#include /* better error reports */ -#include /* better error reports */ +#include /* printf */ +#include /* better error reports */ +#include /* better error reports */ #include "niash_xfer.h" @@ -43,6 +43,8 @@ , {"Agfa", "Snapscan Touch", 0x6BD, 0x100, eAgfaTouch} , + {"Trust", "Office Scanner USB 19200", 0x47b, 0x1000, eAgfaTouch} + , /* last entry all zeros */ {0, 0, 0, 0, 0} }; @@ -53,11 +55,11 @@ /* MatchUsbDevice ============== - Matches a given USB vendor and product id against a list of - supported scanners. + Matches a given USB vendor and product id against a list of + supported scanners. IN iVendor USB vendor ID - iProduct USB product ID + iProduct USB product ID OUT *ppModel Pointer to TScannerModel structure Returns TRUE if a matching USB scanner was found @@ -71,12 +73,12 @@ while (pModels->pszName != NULL) { if ((pModels->iVendor == iVendor) && (pModels->iProduct == iProduct)) - { - DBG (DBG_MSG, "found %s %s\n", pModels->pszVendor, - pModels->pszName); - *ppModel = pModels; - return SANE_TRUE; - } + { + DBG (DBG_MSG, "found %s %s\n", pModels->pszVendor, + pModels->pszName); + *ppModel = pModels; + return SANE_TRUE; + } /* next model to match */ pModels++; } @@ -104,8 +106,8 @@ /* NiashXferInit =============== - Initialises all registered data transfer modules, which causes - them to report any devices found through the pfnReport callback. + Initialises all registered data transfer modules, which causes + them to report any devices found through the pfnReport callback. IN pfnReport Function to call to report a transfer device */ @@ -123,13 +125,13 @@ DBG (DBG_MSG, "Looking for %s...\n", pModels->pszName); _pModel = pModels; if (sanei_usb_find_devices ((SANE_Int) pModels->iVendor, - (SANE_Int) pModels->iProduct, - _AttachUsb) != SANE_STATUS_GOOD) - { - - DBG (DBG_ERR, "Error invoking sanei_usb_find_devices"); - break; - } + (SANE_Int) pModels->iProduct, + _AttachUsb) != SANE_STATUS_GOOD) + { + + DBG (DBG_ERR, "Error invoking sanei_usb_find_devices"); + break; + } pModels++; } } @@ -141,7 +143,7 @@ SANE_Status status; SANE_Word vendor, product; int fd; - TScannerModel *pModel; + TScannerModel *pModel = 0; DBG (DBG_MSG, "Trying to open %s...\n", pszName); @@ -178,8 +180,8 @@ parusb_write_reg (int fd, unsigned char bReg, unsigned char bValue) { sanei_usb_control_msg (fd, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x0C, bReg, 0, 1, &bValue); + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x0C, bReg, 0, 1, &bValue); } @@ -187,8 +189,8 @@ parusb_read_reg (int fd, unsigned char bReg, unsigned char *pbValue) { sanei_usb_control_msg (fd, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0C, bReg, 0, 1, pbValue); + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x0C, bReg, 0, 1, pbValue); } @@ -247,8 +249,8 @@ abSetup[4] = (iSize) & 0xFF; abSetup[5] = (iSize >> 8) & 0xFF; sanei_usb_control_msg (iHandle, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x04, USB_SETUP, 0, 8, abSetup); + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x04, USB_SETUP, 0, 8, abSetup); /* do the bulk write */ size = iSize; @@ -279,8 +281,8 @@ abSetup[4] = (iSize) & 0xFF; abSetup[5] = (iSize >> 8) & 0xFF; sanei_usb_control_msg (iHandle, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x04, USB_SETUP, 0, 8, abSetup); + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x04, USB_SETUP, 0, 8, abSetup); /* do the bulk read */ size = iSize; diff -uNr sane-backends-1.0.17/backend/pie.conf sane-backends-1.0.18/backend/pie.conf --- sane-backends-1.0.17/backend/pie.conf 2001-09-17 22:57:04.000000000 +0200 +++ sane-backends-1.0.18/backend/pie.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,4 +0,0 @@ -scsi DEVCOM * Scanner -scsi PIE * Scanner -scsi AdLib * Scanner -/dev/scanner diff -uNr sane-backends-1.0.17/backend/pie.conf.in sane-backends-1.0.18/backend/pie.conf.in --- sane-backends-1.0.17/backend/pie.conf.in 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pie.conf.in 2006-01-05 17:27:10.000000000 +0100 @@ -0,0 +1,4 @@ +scsi DEVCOM * Scanner +scsi PIE * Scanner +scsi AdLib * Scanner +/dev/scanner diff -uNr sane-backends-1.0.17/backend/pixma.c sane-backends-1.0.18/backend/pixma.c --- sane-backends-1.0.17/backend/pixma.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,1437 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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. + */ +# include "../include/sane/config.h" + +#include +#include +#include +#include /* sigaction(POSIX) */ +#include /* POSIX: write read close pipe */ +#ifdef HAVE_FCNTL_H +# include +#endif + +#include "pixma_rename.h" +#include "pixma.h" + +# define DEBUG_NOT_STATIC +# include "../include/sane/sane.h" +# include "../include/sane/sanei.h" +# include "../include/sane/saneopts.h" +# include "../include/sane/sanei_thread.h" +# include "../include/sane/sanei_backend.h" + +#ifdef NDEBUG +# define PDBG(x) +#else +# define PDBG(x) IF_DBG(x) +#endif /* NDEBUG */ + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +#define DECL_CTX pixma_sane_t *ss = check_handle(h) +#define OPT_IN_CTX ss->opt +#define SOD(opt) OPT_IN_CTX[opt].sod +#define OVAL(opt) OPT_IN_CTX[opt].val +#define AUTO_GAMMA 2.2 + +#include "pixma_sane_options.h" /* generated by gen_options.py */ + + +typedef struct pixma_sane_t +{ + struct pixma_sane_t *next; + pixma_t *s; + pixma_scan_param_t sp; + SANE_Bool cancel; + + /* valid states: idle, !idle && scanning, !idle && !scanning */ + SANE_Bool idle; + SANE_Bool scanning; + SANE_Status last_read_status; /* valid if !idle && !scanning */ + + option_descriptor_t opt[opt_last]; + SANE_Range xrange, yrange; + SANE_Word dpi_list[9]; /* up to 9600 dpi */ + SANE_String_Const mode_list[3]; + uint8_t gamma_table[4096]; + SANE_String_Const source_list[4]; + pixma_paper_source_t source_map[4]; + + unsigned byte_pos_in_line, output_line_size; + unsigned image_bytes_read; + unsigned page_count; /* valid for ADF */ + + int reader_taskid; + int wpipe, rpipe; + SANE_Bool reader_stop; +} pixma_sane_t; + + +static const char vendor_str[] = "CANON"; +static const char type_str[] = "multi-function peripheral"; + +static pixma_sane_t *first_scanner = NULL; +static const SANE_Device **dev_list = NULL; + + +static SANE_Status +map_error (int error) +{ + if (error >= 0) + return SANE_STATUS_GOOD; + + switch (error) + { + case -ENOMEM: + return SANE_STATUS_NO_MEM; + case -ECANCELED: + return SANE_STATUS_CANCELLED; + case -EBUSY: + return SANE_STATUS_DEVICE_BUSY; + case -EINVAL: + return SANE_STATUS_INVAL; + case -EACCES: + return SANE_STATUS_ACCESS_DENIED; + case -EDEADLK: + return SANE_STATUS_JAMMED; + case -ENODATA: + return SANE_STATUS_NO_DOCS; + case -ENOLCK: + return SANE_STATUS_COVER_OPEN; + case -EPROTO: + case -ENODEV: + case -EIO: + case -ETIMEDOUT: + return SANE_STATUS_IO_ERROR; + default: + PDBG (pixma_dbg (1, "BUG: unmapped error %d %s\n", error, + strerror (-error))); + return SANE_STATUS_IO_ERROR; + } +} + +static int +getenv_atoi (const char *name, int def) +{ + const char *str = getenv (name); + return (str) ? atoi (str) : def; +} + +static void +cleanup_device_list (void) +{ + if (dev_list) + { + int i; + for (i = 0; dev_list[i]; i++) + { + free (dev_list[i]->name); + free (dev_list[i]->model); + free (dev_list[i]); + } + } + free (dev_list); + dev_list = NULL; +} + +static void +find_scanners (void) +{ + unsigned i, nscanners; + + cleanup_device_list (); + nscanners = pixma_find_scanners (); + dev_list = + (const SANE_Device **) calloc (nscanners + 1, sizeof (*dev_list)); + if (!dev_list) + return; + for (i = 0; i != nscanners; i++) + { + SANE_Device *sdev = (SANE_Device *) calloc (1, sizeof (*sdev)); + char *name, *model; + if (!sdev) + goto nomem; + name = strdup (pixma_get_device_id (i)); + model = strdup (pixma_get_device_model (i)); + if (!name || !model) + { + free (name); + free (model); + free (sdev); + goto nomem; + } + sdev->name = name; + sdev->model = model; + sdev->vendor = vendor_str; + sdev->type = type_str; + dev_list[i] = sdev; + } + /* dev_list is already NULL terminated by calloc(). */ + return; + +nomem: + PDBG (pixma_dbg (1, "WARNING:not enough memory for device list\n")); + return; +} + +static pixma_sane_t * +check_handle (SANE_Handle h) +{ + pixma_sane_t *p; + + for (p = first_scanner; p && (SANE_Handle) p != h; p = p->next) + { + } + return p; +} + +static void +update_button_state (pixma_sane_t * ss, SANE_Int * info) +{ + SANE_Int b1 = OVAL (opt_button_1).w; + SANE_Int b2 = OVAL (opt_button_2).w; + uint32_t ev = pixma_wait_event (ss->s, 300); + switch (ev & ~PIXMA_EV_ACTION_MASK) + { + case PIXMA_EV_BUTTON1: + b1 = (ev & PIXMA_EV_ACTION_MASK) + 1; + break; + case PIXMA_EV_BUTTON2: + b2 = (ev & PIXMA_EV_ACTION_MASK) + 1; + break; + } + if (b1 != OVAL (opt_button_1).w || b2 != OVAL (opt_button_2).w) + *info |= SANE_INFO_RELOAD_OPTIONS; + OVAL (opt_button_1).w = b1; + OVAL (opt_button_2).w = b2; +} + +static void +clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info) +{ + SANE_Option_Descriptor *sod = &SOD (n); + SANE_Word *va = (SANE_Word *) v; + const SANE_Range *range = sod->constraint.range; + int i, nmemb; + + nmemb = sod->size / sizeof (SANE_Word); + for (i = 0; i < nmemb; i++) + { + SANE_Word value = va[i]; + if (value < range->min) + { + value = range->min; + } + else if (value > range->max) + { + value = range->max; + } + if (range->quant != 0) + { + value = (value - range->min + range->quant / 2) / + range->quant * range->quant; + } + if (value != va[i]) + { + va[i] = value; + *info |= SANE_INFO_INEXACT; + } + } +} + +static void +select_value_from_list (pixma_sane_t * ss, SANE_Int n, void *v, + SANE_Int * info) +{ + SANE_Option_Descriptor *sod = &SOD (n); + SANE_Word *va = (SANE_Word *) v; + const SANE_Word *list = sod->constraint.word_list; + int i, j, nmemb; + + nmemb = sod->size / sizeof (SANE_Word); + for (i = 0; i < nmemb; i++) + { + SANE_Word value = va[i]; + SANE_Word mindelta = abs (value - list[1]); + SANE_Word nearest = list[1]; + for (j = 2; j <= list[0]; j++) + { + SANE_Word delta = abs (value - list[j]); + if (delta < mindelta) + { + mindelta = delta; + nearest = list[j]; + } + if (mindelta == 0) + break; + } + if (va[i] != nearest) + { + va[i] = nearest; + *info |= SANE_INFO_INEXACT; + } + } +} + +static SANE_Status +control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, + SANE_Int * info) +{ + option_descriptor_t *opt = &(OPT_IN_CTX[n]); + SANE_Word val; + + switch (a) + { + case SANE_ACTION_GET_VALUE: + switch (opt->sod.type) + { + case SANE_TYPE_BOOL: + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + *(SANE_Word *) v = opt->val.w; + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + return SANE_STATUS_GOOD; + + case SANE_ACTION_SET_VALUE: + switch (opt->sod.type) + { + case SANE_TYPE_BOOL: + val = *(SANE_Word *) v; + if (val != SANE_TRUE && val != SANE_FALSE) + return SANE_STATUS_INVAL; + opt->val.w = val; + break; + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE) + clamp_value (ss, n, v, info); + else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST) + select_value_from_list (ss, n, v, info); + opt->val.w = *(SANE_Word *) v; + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + *info |= opt->info; + return SANE_STATUS_GOOD; + + case SANE_ACTION_SET_AUTO: + switch (opt->sod.type) + { + case SANE_TYPE_BOOL: + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + opt->val.w = opt->def.w; + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + *info |= opt->info; + return SANE_STATUS_GOOD; + } + return SANE_STATUS_UNSUPPORTED; +} + +static SANE_Status +control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v, + SANE_Int * info) +{ + option_descriptor_t *opt = &(OPT_IN_CTX[n]); + const SANE_String_Const *slist = opt->sod.constraint.string_list; + SANE_String str = (SANE_String) v; + int i; + + if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE) + { + switch (a) + { + case SANE_ACTION_GET_VALUE: + strcpy (str, opt->val.s); + break; + case SANE_ACTION_SET_AUTO: + str = opt->def.s; + /* fall through */ + case SANE_ACTION_SET_VALUE: + strncpy (opt->val.s, str, opt->sod.size); + *info |= opt->info; + break; + } + return SANE_STATUS_GOOD; + } + else + { + switch (a) + { + case SANE_ACTION_GET_VALUE: + strcpy (str, slist[opt->val.w]); + break; + case SANE_ACTION_SET_AUTO: + str = opt->def.ptr; + /* fall through */ + case SANE_ACTION_SET_VALUE: + i = 0; + while (slist[i] && strcasecmp (str, slist[i]) != 0) + i++; + if (!slist[i]) + return SANE_STATUS_INVAL; + if (strcmp (slist[i], str) != 0) + { + strcpy (str, slist[i]); + *info |= SANE_INFO_INEXACT; + } + opt->val.w = i; + *info |= opt->info; + break; + } + return SANE_STATUS_GOOD; + } +} + +static SANE_Status +control_option (pixma_sane_t * ss, SANE_Int n, + SANE_Action a, void *v, SANE_Int * info) +{ + int result, i; + + result = SANE_STATUS_UNSUPPORTED; + switch (n) + { + case opt_gamma_table: + switch (a) + { + case SANE_ACTION_SET_VALUE: + clamp_value (ss, n, v, info); + for (i = 0; i != 4096; i++) + ss->gamma_table[i] = *((SANE_Int *) v + i); + break; + case SANE_ACTION_GET_VALUE: + for (i = 0; i != 4096; i++) + *((SANE_Int *) v + i) = ss->gamma_table[i]; + break; + case SANE_ACTION_SET_AUTO: + pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, + sizeof (ss->gamma_table)); + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + return SANE_STATUS_GOOD; + + case opt_button_update: + if (a == SANE_ACTION_SET_VALUE) + { + update_button_state (ss, info); + return SANE_STATUS_GOOD; + } + else + { + return SANE_STATUS_INVAL; + } + break; + } + + switch (SOD (n).type) + { + case SANE_TYPE_BOOL: + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + result = control_scalar_option (ss, n, a, v, info); + break; + case SANE_TYPE_STRING: + result = control_string_option (ss, n, a, v, info); + break; + case SANE_TYPE_BUTTON: + case SANE_TYPE_GROUP: + PDBG (pixma_dbg (1, "BUG:control_option():Unhandled option\n")); + result = SANE_STATUS_INVAL; + break; + } + if (result != SANE_STATUS_GOOD) + return result; + + switch (n) + { + case opt_custom_gamma: + if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO) + { + SANE_Word save = SOD (opt_gamma_table).cap; + if (OVAL (opt_custom_gamma).b) + SOD (opt_gamma_table).cap &= ~SANE_CAP_INACTIVE; + else + SOD (opt_gamma_table).cap |= SANE_CAP_INACTIVE; + if (save != SOD (opt_gamma_table).cap) + *info |= SANE_INFO_RELOAD_OPTIONS; + } + break; + } + + return result; +} + +#ifndef NDEBUG +static void +print_scan_param (int level, const pixma_scan_param_t * sp) +{ + pixma_dbg (level, "Scan parameters\n"); + pixma_dbg (level, " line_size=%u image_size=%u channels=%u depth=%u\n", + sp->line_size, sp->image_size, sp->channels, sp->depth); + pixma_dbg (level, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n", + sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h); + pixma_dbg (level, " gamma_table=%p source=%d\n", sp->gamma_table, + sp->source); +} +#endif + +static int +calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp) +{ + int x1, y1, x2, y2; + int error; + + memset (sp, 0, sizeof (*sp)); + + sp->channels = (OVAL (opt_mode).w == 0) ? 3 : 1; + sp->xdpi = sp->ydpi = OVAL (opt_resolution).w; + +#define PIXEL(x,dpi) (int)((SANE_UNFIX(x) / 25.4 * (dpi)) + 0.5) + x1 = PIXEL (OVAL (opt_tl_x).w, sp->xdpi); + x2 = PIXEL (OVAL (opt_br_x).w, sp->xdpi); + if (x2 < x1) + { + int temp = x1; + x1 = x2; + x2 = temp; + } + y1 = PIXEL (OVAL (opt_tl_y).w, sp->ydpi); + y2 = PIXEL (OVAL (opt_br_y).w, sp->ydpi); + if (y2 < y1) + { + int temp = y1; + y1 = y2; + y2 = temp; + } +#undef PIXEL + sp->x = x1; + sp->y = y1; + sp->w = x2 - x1; + sp->h = y2 - y1; + if (sp->w == 0) + sp->w = 1; + if (sp->h == 0) + sp->h = 1; + + sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL; + sp->source = ss->source_map[OVAL (opt_source).w]; + + error = pixma_check_scan_param (ss->s, sp); + if (error < 0) + { + PDBG (pixma_dbg (1, "BUG:calc_scan_param():%s\n", strerror (-error))); + PDBG (print_scan_param (1, sp)); + } + return error; +} + +static void +init_option_descriptors (pixma_sane_t * ss) +{ + const pixma_config_t *cfg; + int i; + + cfg = pixma_get_config (ss->s); + + /* setup range for the scan area. */ + ss->xrange.min = SANE_FIX (0); + ss->xrange.max = SANE_FIX (cfg->width / 75.0 * 25.4); + ss->xrange.quant = SANE_FIX (0); + + ss->yrange.min = SANE_FIX (0); + ss->yrange.max = SANE_FIX (cfg->height / 75.0 * 25.4); + ss->yrange.quant = SANE_FIX (0); + + /* setup dpi up to the value supported by the scanner. */ + i = 0; + do + { + i++; + ss->dpi_list[i] = 75 * (1 << (i - 1)); /* 75 x 2^(i-1) */ + } + while ((unsigned) ss->dpi_list[i] != cfg->xdpi); + ss->dpi_list[0] = i; + + /* mode_list and source_list were already NULL-terminated, + * because the whole pixma_sane_t was cleared during allocation. */ + + /* setup available mode. */ + ss->mode_list[0] = SANE_I18N ("Color"); + if (cfg->cap & PIXMA_CAP_GRAY) + { + ss->mode_list[1] = SANE_I18N ("Gray"); + } + + /* setup paper source */ + i = 0; + ss->source_list[i] = SANE_I18N ("Flatbed"); + ss->source_map[i] = PIXMA_SOURCE_FLATBED; + i++; + if (cfg->cap & PIXMA_CAP_ADF) + { + ss->source_list[i] = SANE_I18N ("Automatic Document Feeder"); + ss->source_map[i] = PIXMA_SOURCE_ADF; + i++; + } + + build_option_descriptors (ss); + + /* Enable options that are available only in some scanners. */ + if (cfg->cap & PIXMA_CAP_GAMMA_TABLE) + { + SOD (opt_custom_gamma).cap &= ~SANE_CAP_INACTIVE; + sane_control_option (ss, opt_custom_gamma, SANE_ACTION_SET_AUTO, + NULL, NULL); + pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, 4096); + } + if (cfg->cap & PIXMA_CAP_EVENTS) + SOD (opt_button_controlled).cap &= ~SANE_CAP_INACTIVE; +} + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + +/* Writing to reader_ss outside reader_process() is a BUG! */ +static pixma_sane_t *reader_ss = NULL; + +static RETSIGTYPE +reader_signal_handler (int sig) +{ + UNUSED (sig); + if (reader_ss) + { + reader_ss->reader_stop = SANE_TRUE; + pixma_cancel (reader_ss->s); + } +} + +static int +write_all (pixma_sane_t * ss, void *buf_, size_t size) +{ + uint8_t *buf = (uint8_t *) buf_; + int count; + + while (size != 0 && !ss->reader_stop) + { + count = write (ss->wpipe, buf, size); + if (count == -1 && errno != EINTR) + break; + if (count == -1 && errno == EINTR) + continue; + buf += count; + size -= count; + } + return buf - (uint8_t *) buf_; +} + +/* NOTE: reader_loop() runs either in a separate thread or process. */ +static SANE_Status +reader_loop (pixma_sane_t * ss) +{ + void *buf; + unsigned bufsize; + int count = 0; + + PDBG (pixma_dbg (3, "Reader task started\n")); + bufsize = ss->sp.line_size + 1; /* XXX: "odd" bufsize for testing pixma_read_image() */ + buf = malloc (bufsize); + if (!buf) + { + count = -ENOMEM; + goto done; + } + pixma_enable_background (ss->s, 1); + if (OVAL (opt_button_controlled).b && ss->page_count == 0) + { + int start = 0; +#ifndef NDEBUG + pixma_dbg (1, "==== Button-controlled scan mode is enabled.\n"); + pixma_dbg (1, "==== To proceed, presse 'SCAN' or 'COLOR' button. " + "To cancel, press 'GRAY' button.\n"); +#endif + while (pixma_wait_event (ss->s, 10) != 0) + { + } + while (!start) + { + uint32_t events; + if (ss->reader_stop) + { + count = -ECANCELED; + goto done; + } + events = pixma_wait_event (ss->s, 1000); + switch (events & ~PIXMA_EV_ACTION_MASK) + { + case PIXMA_EV_BUTTON1: + start = 1; + break; + case PIXMA_EV_BUTTON2: + count = -ENODATA; + goto done; + } + } + } + count = pixma_scan (ss->s, &ss->sp); + if (count >= 0) + { + while ((count = pixma_read_image (ss->s, buf, bufsize)) > 0) + { + if (write_all (ss, buf, count) != count) + pixma_cancel (ss->s); + } + } + +done: + pixma_enable_background (ss->s, 0); + free (buf); + close (ss->wpipe); + ss->wpipe = -1; + if (count >= 0) + { + PDBG (pixma_dbg (3, "Reader task terminated\n")); + } + else + { + PDBG (pixma_dbg (2, "Reader task terminated: %s\n", strerror (-count))); + } + return map_error (count); +} + +static int +reader_process (void *arg) +{ + pixma_sane_t *ss = (pixma_sane_t *) arg; + struct sigaction sa; + + reader_ss = ss; + memset (&sa, 0, sizeof (sa)); + sigemptyset (&sa.sa_mask); + sa.sa_handler = reader_signal_handler; + /* FIXME: which signal else? */ + sigaction (SIGHUP, &sa, NULL); + sigaction (SIGINT, &sa, NULL); + sigaction (SIGPIPE, &sa, NULL); + sigaction (SIGTERM, &sa, NULL); + close (ss->rpipe); + ss->rpipe = -1; + return reader_loop (ss); +} + +static int +reader_thread (void *arg) +{ + pixma_sane_t *ss = (pixma_sane_t *) arg; +#ifdef USE_PTHREAD + /* Block SIGPIPE. We will handle this in reader_loop() by checking + ss->reader_stop and the return value from write(). */ + sigset_t sigs; + sigemptyset (&sigs); + sigaddset (&sigs, SIGPIPE); + pthread_sigmask (SIG_BLOCK, &sigs, NULL); +#endif + return reader_loop (ss); +} + +static int +terminate_reader_task (pixma_sane_t * ss, int *exit_code) +{ + int result, pid; + int status = 0; + + pid = ss->reader_taskid; + if (pid <= 0) + return -1; + if (sanei_thread_is_forked ()) + { + sanei_thread_kill (pid); + } + else + { + ss->reader_stop = SANE_TRUE; + pixma_cancel (ss->s); + } + result = sanei_thread_waitpid (pid, &status); + ss->reader_taskid = 0; + if (result == pid) + { + if (exit_code) + *exit_code = status; + return pid; + } + else + { + PDBG (pixma_dbg (1, "WARNING:waitpid() failed:%s\n", strerror (errno))); + return -1; + } +} + +static int +start_reader_task (pixma_sane_t * ss) +{ + int fds[2]; + int pid; + int is_forked; + + if (ss->rpipe != -1 || ss->wpipe != -1) + { + PDBG (pixma_dbg + (1, "BUG:rpipe = %d, wpipe = %d\n", ss->rpipe, ss->wpipe)); + close (ss->rpipe); + close (ss->wpipe); + ss->rpipe = -1; + ss->wpipe = -1; + } + if (ss->reader_taskid > 0) + { + PDBG (pixma_dbg (1, "BUG:reader_taskid(%d) != 0\n", ss->reader_taskid)); + terminate_reader_task (ss, NULL); + } + if (pipe (fds) == -1) + { + PDBG (pixma_dbg (1, "ERROR:start_reader_task():pipe() failed %s\n", + strerror (errno))); + return -ENOMEM; + } + ss->rpipe = fds[0]; + ss->wpipe = fds[1]; + ss->reader_stop = SANE_FALSE; + + is_forked = sanei_thread_is_forked (); + if (is_forked) + { + pid = sanei_thread_begin (reader_process, ss); + if (pid > 0) + { + close (ss->wpipe); + ss->wpipe = -1; + } + } + else + { + pid = sanei_thread_begin (reader_thread, ss); + } + if (pid == -1) + { + close (ss->wpipe); + close (ss->rpipe); + ss->wpipe = -1; + ss->rpipe = -1; + PDBG (pixma_dbg (1, "ERROR:unable to start reader task\n")); + return -ENOMEM; + } + PDBG (pixma_dbg (3, "Reader task id=%d (%s)\n", pid, + (is_forked) ? "forked" : "threaded")); + ss->reader_taskid = pid; + return pid; +} + +static SANE_Status +read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen) +{ + int count, status; + + if (readlen) + *readlen = 0; + if (ss->image_bytes_read >= ss->sp.image_size) + return SANE_STATUS_EOF; + + do + { + if (ss->cancel) + /* ss->rpipe has already been closed by sane_cancel(). */ + return SANE_STATUS_CANCELLED; + count = read (ss->rpipe, buf, size); + } + while (count == -1 && errno == EINTR); + + if (count == -1) + { + if (errno == EAGAIN) + return SANE_STATUS_GOOD; + if (!ss->cancel) + { + PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n", + strerror (errno))); + } + close (ss->rpipe); + ss->rpipe = -1; + terminate_reader_task (ss, NULL); + return SANE_STATUS_IO_ERROR; + } + + /* here count >= 0 */ + ss->image_bytes_read += count; + if (ss->image_bytes_read > ss->sp.image_size) + { + PDBG (pixma_dbg (1, "BUG:ss->image_bytes_read > ss->sp.image_size\n")); + } + if (ss->image_bytes_read >= ss->sp.image_size) + { + close (ss->rpipe); + ss->rpipe = -1; + terminate_reader_task (ss, NULL); + } + else if (count == 0) + { + PDBG (pixma_dbg (3, "read_image():reader task closed the pipe:" + "%u bytes received, %u bytes expected\n", + ss->image_bytes_read, ss->sp.image_size)); + close (ss->rpipe); + ss->rpipe = -1; + count = terminate_reader_task (ss, &status); + return (count > 0 + && status != SANE_STATUS_GOOD) ? status : SANE_STATUS_IO_ERROR; + } + if (readlen) + *readlen = count; + return SANE_STATUS_GOOD; +} + + +/******************************************************************* + ** SANE API + *******************************************************************/ +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + int status, myversion; + + UNUSED (authorize); + + if (!version_code) + return SANE_STATUS_INVAL; + myversion = 100 * PIXMA_VERSION_MAJOR + PIXMA_VERSION_MINOR; + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, myversion); + DBG_INIT (); + sanei_thread_init (); + pixma_set_debug_level (DBG_LEVEL); + + status = pixma_init (); + if (status < 0) + { + PDBG (pixma_dbg (2, "pixma_init() returned %s\n", strerror (-status))); + } + return map_error (status); +} + +void +sane_exit (void) +{ + while (first_scanner) + sane_close (first_scanner); + cleanup_device_list (); + pixma_cleanup (); +} + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + UNUSED (local_only); + + if (!device_list) + return SANE_STATUS_INVAL; + find_scanners (); + *device_list = dev_list; + return (dev_list) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM; +} + +SANE_Status +sane_open (SANE_String_Const name, SANE_Handle * h) +{ + unsigned i, nscanners; + int error = 0; + pixma_sane_t *ss = NULL; + const pixma_config_t *cfg; + + if (!name || !h) + return SANE_STATUS_INVAL; + + *h = NULL; + nscanners = pixma_find_scanners (); + if (nscanners == 0) + return SANE_STATUS_INVAL; + if (name[0] == '\0') + name = pixma_get_device_id (0); + + /* Have we already opened the scanner? */ + for (ss = first_scanner; ss; ss = ss->next) + { + if (strcmp (pixma_get_string (ss->s, PIXMA_STRING_ID), name) == 0) + { + /* We have already opened it! */ + return SANE_STATUS_DEVICE_BUSY; + } + } + + i = 0; + while (strcmp (pixma_get_device_id (i), name) != 0) + { + if (++i >= nscanners) + return SANE_STATUS_INVAL; + } + cfg = pixma_get_device_config (i); + if ((cfg->cap & PIXMA_CAP_EXPERIMENT) != 0) + { +#ifndef NDEBUG + pixma_dbg (1, "WARNING:" + "Experimental backend CAN DAMAGE your hardware!\n"); + if (getenv_atoi ("PIXMA_EXPERIMENT", 0) == 0) + { + pixma_dbg (1, "Experimental SANE backend for %s is disabled " + "by default.\n", pixma_get_device_model (i)); + pixma_dbg (1, "To enable it, set the environment variable " + "PIXMA_EXPERIMENT to non-zero.\n"); + return SANE_STATUS_UNSUPPORTED; + } +#else + return SANE_STATUS_UNSUPPORTED; +#endif + } + + ss = (pixma_sane_t *) calloc (1, sizeof (*ss)); + if (!ss) + return SANE_STATUS_NO_MEM; + ss->next = first_scanner; + first_scanner = ss; + ss->wpipe = -1; + ss->rpipe = -1; + ss->idle = SANE_TRUE; + ss->scanning = SANE_FALSE; + + error = pixma_open (i, &ss->s); + if (error < 0) + { + PDBG (pixma_dbg (2, "pixma_open() returned %s\n", strerror (-error))); + sane_close (ss); + return map_error (error); + } + pixma_enable_background (ss->s, 0); + init_option_descriptors (ss); + *h = ss; + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle h) +{ + pixma_sane_t **p, *ss; + + for (p = &first_scanner; *p && *p != (pixma_sane_t *) h; p = &((*p)->next)) + { + } + if (!(*p)) + return; + ss = *p; + sane_cancel (ss); + pixma_close (ss->s); + *p = ss->next; + free (ss); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle h, SANE_Int n) +{ + DECL_CTX; + + if (ss && 0 <= n && n < opt_last) + return &SOD (n); + return NULL; +} + +SANE_Status +sane_control_option (SANE_Handle h, SANE_Int n, + SANE_Action a, void *v, SANE_Int * i) +{ + DECL_CTX; + SANE_Int info = 0; + int error; + option_descriptor_t *opt; + + if (i) + *i = 0; + if (!ss) + return SANE_STATUS_INVAL; + if (n < 0 || n >= opt_last) + return SANE_STATUS_UNSUPPORTED; + if (!ss->idle && a != SANE_ACTION_GET_VALUE) + return SANE_STATUS_INVAL; + + opt = &(OPT_IN_CTX[n]); + if (!SANE_OPTION_IS_ACTIVE (opt->sod.cap)) + return SANE_STATUS_INVAL; + switch (a) + { + case SANE_ACTION_SET_VALUE: + if ((opt->sod.type != SANE_TYPE_BUTTON && !v) || + !SANE_OPTION_IS_SETTABLE (opt->sod.cap)) + return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */ + break; + case SANE_ACTION_SET_AUTO: + if (!(opt->sod.cap & SANE_CAP_AUTOMATIC) || + !SANE_OPTION_IS_SETTABLE (opt->sod.cap)) + return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */ + break; + case SANE_ACTION_GET_VALUE: + if (!v || !(opt->sod.cap & SANE_CAP_SOFT_DETECT)) + return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */ + break; + default: + return SANE_STATUS_UNSUPPORTED; + } + + error = control_option (ss, n, a, v, &info); + if (error == SANE_STATUS_GOOD && i) + *i = info; + return error; +} + +SANE_Status +sane_get_parameters (SANE_Handle h, SANE_Parameters * p) +{ + DECL_CTX; + pixma_scan_param_t temp, *sp; + + if (!ss || !p) + return SANE_STATUS_INVAL; + + if (!ss->idle) + { + sp = &ss->sp; /* sp is calculated in sane_start() */ + } + else + { + calc_scan_param (ss, &temp); + sp = &temp; + } + p->format = (sp->channels == 3) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; + p->last_frame = SANE_TRUE; + p->lines = sp->h; + p->depth = sp->depth; + p->pixels_per_line = sp->w; + /* p->bytes_per_line = sp->line_size; NOTE: It should work this way, but it doesn't. No SANE frontend can cope with this. */ + p->bytes_per_line = sp->w * sp->channels * (sp->depth / 8); + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_start (SANE_Handle h) +{ + DECL_CTX; + int error = 0; + + if (!ss) + return SANE_STATUS_INVAL; + if (!ss->idle && ss->scanning) + return SANE_STATUS_INVAL; + + ss->cancel = SANE_FALSE; + if (calc_scan_param (ss, &ss->sp) < 0) + return SANE_STATUS_INVAL; + ss->image_bytes_read = 0; + /* TODO: Check paper here in sane_start(). A function like + pixma_get_status() is needed. */ + error = start_reader_task (ss); + if (error >= 0) + { + ss->output_line_size = ss->sp.w * ss->sp.channels * (ss->sp.depth / 8); + ss->byte_pos_in_line = 0; + if (ss->idle || ss->source_map[OVAL (opt_source).w] != PIXMA_SOURCE_ADF) + ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */ + else + ss->page_count++; + ss->last_read_status = SANE_STATUS_GOOD; + ss->scanning = SANE_TRUE; + ss->idle = SANE_FALSE; + } + return map_error (error); +} + +SANE_Status +sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) +{ + DECL_CTX; + int sum, n; + SANE_Byte temp[16]; + SANE_Status status; + + if (len) + *len = 0; + if (!ss || !buf || !len) + return SANE_STATUS_INVAL; + if (ss->cancel) + return SANE_STATUS_CANCELLED; + if (ss->idle) + return SANE_STATUS_INVAL; + if (!ss->scanning) + return ss->last_read_status; + + status = SANE_STATUS_GOOD; + if ((ss->sp.line_size - ss->output_line_size) == 0) + { + status = read_image (ss, buf, maxlen, &sum); + } + else + { + /* FIXME: Because there is no frontend that can cope with padding at + the end of line, we've to remove it here in the backend! */ + sum = 0; + while (sum < maxlen) + { + if (ss->byte_pos_in_line < ss->output_line_size) + { + n = ss->output_line_size - ss->byte_pos_in_line; + if ((maxlen - sum) < n) + n = maxlen - sum; + status = read_image (ss, buf, n, &n); + if (n == 0) + break; + sum += n; + buf += n; + ss->byte_pos_in_line += n; + } + else + { + /* skip padding */ + n = ss->sp.line_size - ss->byte_pos_in_line; + status = read_image (ss, temp, n, &n); + if (n == 0) + break; + ss->byte_pos_in_line += n; + if (ss->byte_pos_in_line == ss->sp.line_size) + ss->byte_pos_in_line = 0; + } + } + } + if (ss->cancel) + status = SANE_STATUS_CANCELLED; + else if ((status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF) && + sum > 0) + { + *len = sum; + status = SANE_STATUS_GOOD; + } + ss->scanning = (status == SANE_STATUS_GOOD); + ss->last_read_status = status; + return status; +} + +void +sane_cancel (SANE_Handle h) +{ + DECL_CTX; + + if (!ss) + return; + ss->cancel = SANE_TRUE; + if (ss->idle) + return; + close (ss->rpipe); + ss->rpipe = -1; + terminate_reader_task (ss, NULL); + ss->idle = SANE_TRUE; +} + +SANE_Status +sane_set_io_mode (SANE_Handle h, SANE_Bool m) +{ + DECL_CTX; + + if (!ss || ss->idle || ss->rpipe == -1) + return SANE_STATUS_INVAL; +#ifdef HAVE_FCNTL_H + PDBG (pixma_dbg (2, "Setting %sblocking mode\n", (m) ? "non-" : "")); + if (fcntl (ss->rpipe, F_SETFL, (m) ? O_NONBLOCK : 0) == -1) + { + PDBG (pixma_dbg (1, "WARNING:fcntl(F_SETFL):%s\n", strerror (errno))); + return SANE_STATUS_UNSUPPORTED; + } + return SANE_STATUS_GOOD; +#else + return (m) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD; +#endif +} + +SANE_Status +sane_get_select_fd (SANE_Handle h, SANE_Int * fd) +{ + DECL_CTX; + + *fd = -1; + if (!ss || !fd || ss->idle || ss->rpipe == -1) + return SANE_STATUS_INVAL; + *fd = ss->rpipe; + return SANE_STATUS_GOOD; +} + +/* +BEGIN SANE_Option_Descriptor + +rem ------------------------------------------- +type group + title Scan mode + +type int resolution + unit dpi + constraint @word_list = ss->dpi_list + default 75 + title @SANE_TITLE_SCAN_RESOLUTION + desc @SANE_DESC_SCAN_RESOLUTION + cap soft_select soft_detect automatic + info reload_params + +type string mode[10] + default Color + constraint @string_list = ss->mode_list + title @SANE_TITLE_SCAN_MODE + desc @SANE_DESC_SCAN_MODE + cap soft_select soft_detect automatic + info reload_params + +type string source[30] + constraint @string_list = ss->source_list + title @SANE_TITLE_SCAN_SOURCE + desc @SANE_DESC_SCAN_SOURCE + default Flatbed + cap soft_select soft_detect + +type bool button-controlled + title Button-controlled scan (experimental) + desc When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button. + default SANE_FALSE + cap soft_select soft_detect inactive + +rem ------------------------------------------- +type group + title Gamma + +type bool custom-gamma + default SANE_TRUE + title @SANE_TITLE_CUSTOM_GAMMA + desc @SANE_DESC_CUSTOM_GAMMA + cap soft_select soft_detect automatic inactive + +type int gamma-table[4096] + constraint (0,255,0) + title @SANE_TITLE_GAMMA_VECTOR + desc @SANE_DESC_GAMMA_VECTOR + cap soft_select soft_detect automatic inactive + +rem ------------------------------------------- +type group + title Geometry + +type fixed tl-x + unit mm + default 0 + constraint @range = &ss->xrange + title @SANE_TITLE_SCAN_TL_X + desc @SANE_DESC_SCAN_TL_X + cap soft_select soft_detect automatic + info reload_params + +type fixed tl-y + unit mm + default 0 + constraint @range = &ss->yrange + title @SANE_TITLE_SCAN_TL_Y + desc @SANE_DESC_SCAN_TL_Y + cap soft_select soft_detect automatic + info reload_params + +type fixed br-x + unit mm + default _MAX + constraint @range = &ss->xrange + title @SANE_TITLE_SCAN_BR_X + desc @SANE_DESC_SCAN_BR_X + cap soft_select soft_detect automatic + info reload_params + +type fixed br-y + unit mm + default _MAX + constraint @range = &ss->yrange + title @SANE_TITLE_SCAN_BR_Y + desc @SANE_DESC_SCAN_BR_Y + cap soft_select soft_detect automatic + info reload_params + +rem ------------------------------------------- +type group + title Buttons + +type button button-update + title Update button state + cap soft_select soft_detect advanced + +type int button-1 + default 0 + title Button 1 + cap soft_select soft_detect advanced + +type int button-2 + default 0 + title Button 2 + cap soft_select soft_detect advanced + +rem ------------------------------------------- +END SANE_Option_Descriptor +*/ +#include "pixma_sane_options.c" /* generated by gen_options.py */ diff -uNr sane-backends-1.0.17/backend/pixma_common.c sane-backends-1.0.18/backend/pixma_common.c --- sane-backends-1.0.17/backend/pixma_common.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_common.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,771 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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. + */ +# include "../include/sane/config.h" + +#include +#include +#include +#include +#include /* pow(C90) */ + +#include /* POSIX */ +#include /* gettimeofday(4.3BSD) */ +#include /* usleep */ + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" + + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +extern const pixma_config_t pixma_mp150_devices[]; +extern const pixma_config_t pixma_mp750_devices[]; +extern const pixma_config_t pixma_mp730_devices[]; + +static const pixma_config_t *const pixma_devices[] = { + pixma_mp150_devices, + pixma_mp750_devices, + pixma_mp730_devices, + NULL +}; + +static pixma_t *first_pixma = NULL; +static time_t tstart_sec = 0; +static uint32_t tstart_usec = 0; +static int debug_level = 1; + +#ifndef NDEBUG + +static void +u8tohex (uint8_t x, char *str) +{ + static const char hdigit[16] = + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', +'e', 'f' }; + str[0] = hdigit[(x >> 4) & 0xf]; + str[1] = hdigit[x & 0xf]; + str[2] = '\0'; +} + +static void +u32tohex (uint32_t x, char *str) +{ + u8tohex (x >> 24, str); + u8tohex (x >> 16, str + 2); + u8tohex (x >> 8, str + 4); + u8tohex (x, str + 6); +} + +void +pixma_hexdump (int level, const void *d_, unsigned len) +{ + const uint8_t *d = (const uint8_t *) (d_); + unsigned ofs, c; + char line[100]; /* actually only 1+8+1+8*3+1+8*3+1 = 61 bytes needed */ + + if (level > debug_level) + return; + ofs = 0; + while (ofs < len) + { + char *p; + line[0] = ' '; + u32tohex (ofs, line + 1); + line[9] = ':'; + p = line + 10; + for (c = 0; c != 16 && (ofs + c) < len; c++) + { + u8tohex (d[ofs + c], p); + p[2] = ' '; + p += 3; + if (c == 7) + { + p[0] = ' '; + p++; + } + } + p[0] = '\0'; + pixma_dbg (level, "%s\n", line); + ofs += c; + } +} + +static void +time2str (char *buf, unsigned size) +{ + time_t sec; + uint32_t usec; + + pixma_get_time (&sec, &usec); + sec -= tstart_sec; + if (usec >= tstart_usec) + { + usec -= tstart_usec; + } + else + { + usec = 1000000 + usec - tstart_usec; + sec--; + } + snprintf (buf, size, "%lu.%03u", (unsigned long) sec, + (unsigned) (usec / 1000)); +} + +void +pixma_dump (int level, const char *type, const void *data, int len, + int size, int max) +{ + int actual_len, print_len; + char buf[20]; + + if (level > debug_level) + return; + if (debug_level >= 20) + max = -1; /* dump every bytes */ + + time2str (buf, sizeof (buf)); + pixma_dbg (level, "%s T=%s len=%d\n", type, buf, len); + + actual_len = (size >= 0) ? size : len; + print_len = (max >= 0 && max < actual_len) ? max : actual_len; + if (print_len >= 0) + { + pixma_hexdump (level, data, print_len); + if (print_len < actual_len) + pixma_dbg (level, " ...\n"); + } + if (len < 0) + pixma_dbg (level, " ERROR: %s\n", strerror (-len)); + pixma_dbg (level, "\n"); +} + + +#endif /* NDEBUG */ + +void +pixma_set_debug_level (int level) +{ + debug_level = level; +} + +void +pixma_set_be16 (uint16_t x, uint8_t * buf) +{ + buf[0] = x >> 8; + buf[1] = x; +} + +void +pixma_set_be32 (uint32_t x, uint8_t * buf) +{ + buf[0] = x >> 24; + buf[1] = x >> 16; + buf[2] = x >> 8; + buf[3] = x; +} + +uint16_t +pixma_get_be16 (const uint8_t * buf) +{ + return ((uint16_t) buf[0] << 8) | buf[1]; +} + +uint32_t +pixma_get_be32 (const uint8_t * buf) +{ + return ((uint32_t) buf[0] << 24) + ((uint32_t) buf[1] << 16) + + ((uint32_t) buf[2] << 8) + buf[3]; +} + +uint8_t +pixma_sum_bytes (const void *data, unsigned len) +{ + const uint8_t *d = (const uint8_t *) data; + unsigned i, sum = 0; + for (i = 0; i != len; i++) + sum += d[i]; + return sum; +} + +void +pixma_sleep (unsigned long usec) +{ + usleep (usec); +} + +void +pixma_get_time (time_t * sec, uint32_t * usec) +{ + struct timeval tv; + gettimeofday (&tv, NULL); + if (sec) + *sec = tv.tv_sec; + if (usec) + *usec = tv.tv_usec; +} + +int +pixma_map_status_errno (unsigned status) +{ + switch (status) + { + case PIXMA_STATUS_OK: + return 0; + case PIXMA_STATUS_FAILED: + return -ECANCELED; + case PIXMA_STATUS_BUSY: + return -EBUSY; + default: + return -EPROTO; + } +} + +int +pixma_check_result (pixma_cmdbuf_t * cb) +{ + const uint8_t *r = cb->buf; + unsigned header_len = cb->res_header_len; + unsigned expected_reslen = cb->expected_reslen; + int error; + unsigned len; + + if (cb->reslen < 0) + return cb->reslen; + + len = (unsigned) cb->reslen; + if (len >= header_len) + { + error = pixma_map_status_errno (pixma_get_be16 (r)); + if (expected_reslen != 0) + { + if (len == expected_reslen) + { + if (pixma_sum_bytes (r + header_len, len - header_len) != 0) + error = -EPROTO; + } + else + { + /* This case will happen when a command cannot be completely + executed, e.g. because you press the cancel button. The + device will return only a header with PIXMA_STATUS_FAILED. */ + if (len != header_len) + error = -EPROTO; + } + } + } + else + error = -EPROTO; + +#ifndef NDEBUG + if (error == -EPROTO) + { + pixma_dbg (1, "WARNING: result len=%d expected %d: %s\n", + len, cb->expected_reslen, strerror (-error)); + pixma_hexdump (1, r, MIN (len, 64)); + } +#endif + return error; +} + +int +pixma_cmd_transaction (pixma_t * s, const void *cmd, unsigned cmdlen, + void *data, unsigned expected_len) +{ + int error, tmo; + + error = pixma_write (s->io, cmd, cmdlen); + if (error != (int) cmdlen) + return error; /* FIXME: make sure that error < 0! */ + + /* When you send the start_session command while the scanner optic is + going back to the home position after the last scan session has been + cancelled, you won't get the response before it arrives home. This takes + about 5 seconds. If the last session was succeeded, the scanner will + immediatly answer with PIXMA_STATUS_BUSY. + + Is 8 seconds timeout enough? This affects ALL commands that use + pixma_cmd_transaction(). */ + tmo = 8; + do + { + error = pixma_read (s->io, data, expected_len); + if (error == -ETIMEDOUT) + PDBG (pixma_dbg (2, "No response yet. Timed out in %d sec.\n", tmo)); + } + while (error == -ETIMEDOUT && --tmo != 0); + if (error < 0) + { + PDBG (pixma_dbg (1, "WARNING:Error in response phase. cmd:%02x%02x\n", + ((const uint8_t *) cmd)[0], + ((const uint8_t *) cmd)[1])); + PDBG (pixma_dbg + (1, + " If the scanner hangs, reset it and/or unplug the " + "USB cable.\n")); + } + return error; /* length of the result packet or error */ +} + +uint8_t * +pixma_newcmd (pixma_cmdbuf_t * cb, unsigned cmd, + unsigned dataout, unsigned datain) +{ + unsigned cmdlen = cb->cmd_header_len + dataout; + unsigned reslen = cb->res_header_len + datain; + + if (cmdlen > cb->size || reslen > cb->size) + return NULL; + memset (cb->buf, 0, cmdlen); + cb->cmdlen = cmdlen; + cb->expected_reslen = reslen; + pixma_set_be16 (cmd, cb->buf); + pixma_set_be16 (dataout + datain, cb->buf + cb->cmd_len_field_ofs); + if (dataout != 0) + return cb->buf + cb->cmd_header_len; + else + return cb->buf + cb->res_header_len; +} + +int +pixma_exec (pixma_t * s, pixma_cmdbuf_t * cb) +{ + if (cb->cmdlen > cb->cmd_header_len) + pixma_fill_checksum (cb->buf + cb->cmd_header_len, + cb->buf + cb->cmdlen - 1); + cb->reslen = + pixma_cmd_transaction (s, cb->buf, cb->cmdlen, cb->buf, + cb->expected_reslen); + return pixma_check_result (cb); +} + +int +pixma_exec_short_cmd (pixma_t * s, pixma_cmdbuf_t * cb, unsigned cmd) +{ + pixma_newcmd (cb, cmd, 0, 0); + return pixma_exec (s, cb); +} + +int +pixma_check_dpi (unsigned dpi, unsigned max) +{ + /* valid dpi = 75 * 2^n */ + unsigned temp = dpi / 75; + if (dpi > max || dpi < 75 || 75 * temp != dpi || (temp & (temp - 1)) != 0) + return -EINVAL; + return 0; +} + + +int +pixma_init (void) +{ + PDBG (pixma_dbg (2, "pixma version %d.%d.%d\n", PIXMA_VERSION_MAJOR, + PIXMA_VERSION_MINOR, PIXMA_VERSION_BUILD)); + PASSERT (first_pixma == NULL); + if (tstart_sec == 0) + pixma_get_time (&tstart_sec, &tstart_usec); + return pixma_io_init (); +} + +void +pixma_cleanup (void) +{ + while (first_pixma) + pixma_close (first_pixma); + pixma_io_cleanup (); +} + +int +pixma_open (unsigned devnr, pixma_t ** handle) +{ + int error; + pixma_t *s; + const pixma_config_t *cfg; + + *handle = NULL; + cfg = pixma_get_device_config (devnr); + if (!cfg) + return -EINVAL; /* invalid devnr */ + PDBG (pixma_dbg (2, "pixma_open(): %s\n", cfg->name)); + + s = (pixma_t *) calloc (1, sizeof (s[0])); + if (!s) + return -ENOMEM; + s->next = first_pixma; + first_pixma = s; + + s->cfg = cfg; + error = pixma_connect (devnr, &s->io); + if (error < 0) + { + PDBG (pixma_dbg (2, "pixma_connect() failed:%s\n", strerror (-error))); + goto rollback; + } + strncpy (s->id, pixma_get_device_id (devnr), sizeof (s->id)); + s->ops = s->cfg->ops; + s->scanning = 0; + error = s->ops->open (s); + if (error < 0) + goto rollback; + *handle = s; + return 0; + +rollback: + pixma_close (s); + return error; +} + +void +pixma_close (pixma_t * s) +{ + pixma_t **p; + + if (!s) + return; + for (p = &first_pixma; *p && *p != s; p = &((*p)->next)) + { + } + PASSERT (*p); + if (!(*p)) + return; + PDBG (pixma_dbg (2, "pixma_close(): %s\n", s->cfg->name)); + if (s->io) + { + if (s->scanning) + { + PDBG (pixma_dbg (3, "pixma_close():scanning in progress, call" + " finish_scan()\n")); + s->ops->finish_scan (s); + } + s->ops->close (s); + pixma_disconnect (s->io); + } + *p = s->next; + free (s); +} + +int +pixma_scan (pixma_t * s, pixma_scan_param_t * sp) +{ + int error; + + error = pixma_check_scan_param (s, sp); + if (error < 0) + return error; + +#ifndef NDEBUG + pixma_dbg (3, "\n"); + pixma_dbg (3, "pixma_scan(): start\n"); + pixma_dbg (3, " line_size=%u image_size=%u channels=%u depth=%u\n", + sp->line_size, sp->image_size, sp->channels, sp->depth); + pixma_dbg (3, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n", + sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h); + pixma_dbg (3, " gamma_table=%p source=%d\n", sp->gamma_table, sp->source); +#endif + + s->param = sp; + s->cancel = 0; + s->cur_image_size = 0; + s->imagebuf.wptr = NULL; + s->imagebuf.wend = NULL; + s->imagebuf.rptr = NULL; + s->imagebuf.rend = NULL; + s->underrun = 0; + error = s->ops->scan (s); + if (error >= 0) + { + s->scanning = 1; + } + else + { + PDBG (pixma_dbg (3, "pixma_scan() failed:%s\n", strerror (-error))); + } + + return error; +} + +static uint8_t * +fill_pixels (pixma_t * s, uint8_t * ptr, uint8_t * end, uint8_t value) +{ + if (s->cur_image_size < s->param->image_size) + { + int n = s->param->image_size - s->cur_image_size; + if (n > (end - ptr)) + n = end - ptr; + memset (ptr, value, n); + s->cur_image_size += n; + ptr += n; + } + return ptr; +} + +int +pixma_read_image (pixma_t * s, void *buf, unsigned len) +{ + int result; + pixma_imagebuf_t ib; + + if (!s->scanning) + return 0; + if (s->cancel) + { + result = -ECANCELED; + goto cancel; + } + + ib = s->imagebuf; /* get rptr and rend */ + ib.wptr = (uint8_t *) buf; + ib.wend = ib.wptr + len; + + if (s->underrun) + { + if (s->cur_image_size < s->param->image_size) + { + ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff); + } + else + { + PDBG (pixma_dbg + (3, "pixma_read_image():completed (underrun detected)\n")); + s->scanning = 0; + } + return ib.wptr - (uint8_t *) buf; + } + + while (ib.wptr != ib.wend) + { + if (ib.rptr == ib.rend) + { + ib.rptr = ib.rend = NULL; + result = s->ops->fill_buffer (s, &ib); + if (result < 0) + goto cancel; + if (result == 0) + { /* end of image? */ + s->ops->finish_scan (s); +#ifndef NDEBUG + if (s->cur_image_size != s->param->image_size) + { + pixma_dbg (1, "WARNING:image size mismatches\n"); + pixma_dbg (1, + " %u expected (%d lines) but %u received (%d lines)\n", + s->param->image_size, s->param->h, + s->cur_image_size, + s->cur_image_size / s->param->line_size); + if ((s->cur_image_size % s->param->line_size) != 0) + { + pixma_dbg (1, + "BUG:received data not multiple of line_size\n"); + } + } +#endif /* !NDEBUG */ + if (s->cur_image_size < s->param->image_size) + { + s->underrun = 1; + ib.wptr = fill_pixels (s, ib.wptr, ib.wend, 0xff); + } + else + { + PDBG (pixma_dbg (3, "pixma_read_image():completed\n")); + s->scanning = 0; + } + break; + } + s->cur_image_size += result; + PASSERT (s->cur_image_size <= s->param->image_size); + } + if (ib.rptr) + { + unsigned count = MIN (ib.rend - ib.rptr, ib.wend - ib.wptr); + memcpy (ib.wptr, ib.rptr, count); + ib.rptr += count; + ib.wptr += count; + } + } + s->imagebuf = ib; /* store rptr and rend */ + return ib.wptr - (uint8_t *) buf; + +cancel: + s->ops->finish_scan (s); + s->scanning = 0; + if (result == -ECANCELED) + { + PDBG (pixma_dbg (3, "pixma_read_image():cancelled by %sware\n", + (s->cancel) ? "soft" : "hard")); + } + return result; +} + +void +pixma_cancel (pixma_t * s) +{ + s->cancel = 1; +} + +int +pixma_enable_background (pixma_t * s, int enabled) +{ + return pixma_set_interrupt_mode (s->io, enabled); +} + +unsigned +pixma_wait_event (pixma_t * s, int timeout /*ms */ ) +{ + unsigned events; + + if (s->events == PIXMA_EV_NONE && s->ops->wait_event) + s->ops->wait_event (s, timeout); + events = s->events; + s->events = PIXMA_EV_NONE; + return events; +} + +#define CLAMP2(x,w,min,max,dpi) do { \ + unsigned m = (max) * (dpi) / 75; \ + x = MIN(x, m - min); \ + w = MIN(w, m - x); \ + if (w < min) w = min; \ +} while(0) + +int +pixma_check_scan_param (pixma_t * s, pixma_scan_param_t * sp) +{ + if (!(sp->channels == 3 || + (sp->channels == 1 && (s->cfg->cap & PIXMA_CAP_GRAY) != 0))) + return -EINVAL; + + if (pixma_check_dpi (sp->xdpi, s->cfg->xdpi) < 0 || + pixma_check_dpi (sp->ydpi, s->cfg->ydpi) < 0) + return -EINVAL; + + /* xdpi must be equal to ydpi except that + xdpi = max_xdpi and ydpi = max_ydpi. */ + if (!(sp->xdpi == sp->ydpi || + (sp->xdpi == s->cfg->xdpi && sp->ydpi == s->cfg->ydpi))) + return -EINVAL; + + /* FIXME: I assume the same minimum width and height for every model. */ + CLAMP2 (sp->x, sp->w, 13, s->cfg->width, sp->xdpi); + CLAMP2 (sp->y, sp->h, 8, s->cfg->height, sp->ydpi); + + if (!(s->cfg->cap & PIXMA_CAP_ADF)) + sp->source = PIXMA_SOURCE_FLATBED; + + if (sp->depth == 0) + sp->depth = 8; + if ((sp->depth % 8) != 0) + return -EINVAL; + + sp->line_size = 0; + + if (s->ops->check_param (s, sp) < 0) + return -EINVAL; + + if (sp->line_size == 0) + sp->line_size = sp->depth / 8 * sp->channels * sp->w; + sp->image_size = sp->line_size * sp->h; + return 0; +} + +const char * +pixma_get_string (pixma_t * s, pixma_string_index_t i) +{ + switch (i) + { + case PIXMA_STRING_MODEL: + return s->cfg->name; + case PIXMA_STRING_ID: + return s->id; + case PIXMA_STRING_LAST: + return NULL; + } + return NULL; +} + +const pixma_config_t * +pixma_get_config (pixma_t * s) +{ + return s->cfg; +} + +void +pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n) +{ + int i; + double r_gamma = 1.0 / gamma; + double out_scale = 255.0; + double in_scale = 1.0 / (n - 1); + + for (i = 0; (unsigned) i != n; i++) + { + table[i] = (int) (out_scale * pow (i * in_scale, r_gamma) + 0.5); + } +} + +int +pixma_find_scanners (void) +{ + return pixma_collect_devices (pixma_devices); +} + +const char * +pixma_get_device_model (unsigned devnr) +{ + const pixma_config_t *cfg = pixma_get_device_config (devnr); + return (cfg) ? cfg->name : NULL; +} + + +int +pixma_get_device_status (pixma_t * s, pixma_device_status_t * status) +{ + if (!status) + return -EINVAL; + memset (status, 0, sizeof (*status)); + return s->ops->get_status (s, status); +} diff -uNr sane-backends-1.0.17/backend/pixma_common.h sane-backends-1.0.18/backend/pixma_common.h --- sane-backends-1.0.17/backend/pixma_common.h 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_common.h 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,221 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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 PIXMA_COMMON_H +#define PIXMA_COMMON_H + + +#include /* time_t */ +#include "pixma.h" + + +/*! \defgroup subdriver Subdriver Interface + * \brief Subdriver interface. */ + +/*! \defgroup debug Debug utilities + * \brief Debug utilities. */ + +#ifdef NDEBUG +# define PDBG(x) do {} while(0) +# define PASSERT(x) do {} while(0) +#else +# define PDBG(x) x +# define PASSERT(x) do { \ + if (!(x)) \ + pixma_dbg(1, "ASSERT failed:%s:%d: " \ + #x "\n", __FILE__, __LINE__); \ + } while(0) +#endif + + +#define PIXMA_STATUS_OK 0x0606 +#define PIXMA_STATUS_FAILED 0x1515 +#define PIXMA_STATUS_BUSY 0x1414 + +#define PIXMA_MAX_ID_LEN 30 + +#define MIN(x,y) (((x) < (y)) ? (x):(y)) +#define MAX(x,y) (((x) < (y)) ? (y):(x)) +#define ALIGN(x,n) (((x) + (n) - 1) / (n) * (n)) + +struct pixma_io_t; + +struct pixma_limits_t +{ + unsigned xdpi, ydpi; + unsigned width, height; +}; + +struct pixma_cmdbuf_t +{ + unsigned cmd_header_len, res_header_len, cmd_len_field_ofs; + unsigned expected_reslen, cmdlen; + int reslen; + unsigned size; + uint8_t *buf; +}; + +struct pixma_imagebuf_t +{ + uint8_t *wptr, *wend; + const uint8_t *rptr, *rend; +}; + +struct pixma_t +{ + pixma_t *next; + struct pixma_io_t *io; + const pixma_scan_ops_t *ops; + pixma_scan_param_t *param; + const pixma_config_t *cfg; + char id[PIXMA_MAX_ID_LEN + 1]; + int cancel; /* NOTE: It can be set in a signal handler. */ + uint32_t events; + void *subdriver; /* can be used by model driver. */ + + /* private */ + unsigned cur_image_size; + pixma_imagebuf_t imagebuf; + unsigned scanning:1; + unsigned underrun:1; +}; + +/** \addtogroup subdriver + * @{ */ +/** Scan operations for subdriver. */ +struct pixma_scan_ops_t +{ + /** Allocate a data structure for the subdriver. It is called after the + * core driver connected to the scanner. The subdriver should reset the + * scanner to a known state in this function. */ + int (*open) (pixma_t *); + + /** Free resources allocated by the subdriver. Don't forget to send abort + * command to the scanner if it is scanning. */ + void (*close) (pixma_t *); + + /** Setup the scanner for scan parameters defined in \a s->param. */ + int (*scan) (pixma_t * s); + + /** Fill a buffer with image data. The subdriver has two choices: + * -# Fill the buffer pointed by ib->wptr directly and leave + * ib->rptr and ib->rend untouched. The length of the buffer is + * ib->wend - ib->wptr. It must update ib->wptr accordingly. + * -# Update ib->rptr and ib->rend to point to the the beginning and + * the end of the internal buffer resp. The length of the buffer + * is ib->rend - ib->rptr. This function is called again if + * and only if pixma_read_image() has copied the whole buffer. + * + * The subdriver must wait until there is at least one byte to read or + * return 0 for the end of image. */ + int (*fill_buffer) (pixma_t *, pixma_imagebuf_t * ib); + + /** Cancel the scan operation if necessary and free resources allocated in + * scan(). */ + void (*finish_scan) (pixma_t *); + + /** [Optional] Wait for a user's event, e.g. button event. \a timeout is + * in milliseconds. If an event occured before it's timed out, flags in + * \a s->events should be set accordingly. + * \see PIXMA_EV_* */ + void (*wait_event) (pixma_t * s, int timeout); + + /** Check the scan parameters. The parameters can be adjusted if they are + * out of range, e.g. width > max_width. */ + int (*check_param) (pixma_t *, pixma_scan_param_t *); + + /** Read the device status. \see pixma_get_device_status() */ + int (*get_status) (pixma_t *, pixma_device_status_t *); +}; + + +/** \name Funtions for read and write big-endian integer values */ +/**@{*/ +void pixma_set_be16 (uint16_t x, uint8_t * buf); +void pixma_set_be32 (uint32_t x, uint8_t * buf); +uint16_t pixma_get_be16 (const uint8_t * buf); +uint32_t pixma_get_be32 (const uint8_t * buf); +/**@}*/ + +/** \name Utility functions */ +/**@{*/ +uint8_t pixma_sum_bytes (const void *data, unsigned len); +int pixma_check_dpi (unsigned dpi, unsigned max); +void pixma_sleep (unsigned long usec); +void pixma_get_time (time_t * sec, uint32_t * usec); +/**@}*/ + +/** \name Command related functions */ +/**@{*/ +int pixma_cmd_transaction (pixma_t *, const void *cmd, unsigned cmdlen, + void *data, unsigned expected_len); +int pixma_check_result (pixma_cmdbuf_t *); +uint8_t *pixma_newcmd (pixma_cmdbuf_t *, unsigned cmd, + unsigned dataout, unsigned datain); +int pixma_exec (pixma_t *, pixma_cmdbuf_t *); +int pixma_exec_short_cmd (pixma_t *, pixma_cmdbuf_t *, unsigned cmd); +int pixma_map_status_errno (unsigned status); +/**@}*/ + +#define pixma_fill_checksum(start, end) do { \ + *(end) = -pixma_sum_bytes(start, (end)-(start)); \ +} while(0) + +/** @} end of group subdriver */ + +/** \addtogroup debug + * @{ */ +void pixma_set_debug_level (int level); +#ifndef NDEBUG +void pixma_hexdump (int level, const void *d_, unsigned len); + +/* len: length of data or error code. + size: if >= 0, force to print 'size' bytes. + max: maximum number of bytes to print(-1 means no limit). */ +void pixma_dump (int level, const char *type, const void *data, int len, + int size, int max); +# define DEBUG_DECLARE_ONLY +# include "../include/sane/sanei_debug.h" +#endif /* NDEBUG */ +/** @} end of group debug */ + +#endif diff -uNr sane-backends-1.0.17/backend/pixma.h sane-backends-1.0.18/backend/pixma.h --- sane-backends-1.0.17/backend/pixma.h 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma.h 2006-06-11 22:56:42.000000000 +0200 @@ -0,0 +1,398 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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 PIXMA_H +#define PIXMA_H + +/*! + * \mainpage Scanner driver for Canon PIXMA MP series + * \section example Sample code for application + * \code + * pixma_set_debug_level(level); + * pixma_init(); + * nscanners = pixma_find_scanners(); + * devnr = choose_scanner(nscanners); + * scanner = pixma_open(devnr); + * setup_param(param); + * pixma_check_scan_param(scanner, param); + * do { + * if (I_need_events && + * (ev = pixma_wait_event(scanner, timeout)) > 0) { + * handle_event(ev); + * } + * pixma_scan(scanner, param); + * while ((count = pixma_read_image(scanner, buf, len)) > 0) { + * write(buf, count); + * if (error_occured_in_write) { + * pixma_cancel(scanner); + * } + * } + * } while (!enough); + * pixma_close(scanner); + * pixma_cleanup(); + * \endcode + * + * Note: pixma_cancel() can be called asynchronously to + * interrupt pixma_read_image(). It does not cancel the operation + * immediately. pixma_read_image() must be called until it + * returns zero or an error (probably \c -ECANCELED). + * + * \section reference Reference + * - \subpage API + * - \subpage IO + * - \subpage subdriver + * - \subpage debug + */ + +/*! + * \defgroup API The driver API + * \brief The driver API. + * + * The return value of functions that returns \c int has the following + * meaning if not otherwise specified: + * - >= 0 if succeeded + * - < 0 if failed + * - \c -EBUSY + * - \c -ECANCELED + * - \c -EINVAL + * - \c -ENOMEM + * - \c -EPROTO unexpected reply from scanner. + * - \c -ETIMEDOUT + * - \c -EDEADLK papar jam + * - \c -ENODATA no paper + * - \c -ENOLCK scanner cover is opened + */ + +#include + +#ifdef HAVE_STDINT_H +# include /* available in ISO C99 */ +#else +# include +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; +#endif /* HAVE_STDINT_H */ + +#ifndef EPROTO +# define EPROTO 10000 +#endif +#ifndef ENODATA +# define ENODATA 10001 +#endif + +/** \addtogroup API + * @{ */ +/** \name Version of the driver */ +/**@{*/ +#define PIXMA_VERSION_MAJOR 0 +#define PIXMA_VERSION_MINOR 11 +#define PIXMA_VERSION_BUILD 3 +/**@}*/ + +/** \name Capabilities for using with pixma_config_t::cap */ +/**@{*/ +#define PIXMA_CAP_EASY_RGB (1 << 0) +#define PIXMA_CAP_GRAY (1 << 1) +#define PIXMA_CAP_ADF (1 << 2) +#define PIXMA_CAP_48BIT (1 << 3) +#define PIXMA_CAP_GAMMA_TABLE (1 << 4) +#define PIXMA_CAP_EVENTS (1 << 5) +#define PIXMA_CAP_TPU (1 << 6) +#define PIXMA_CAP_EXPERIMENT (1 << 31) +/**@}*/ + +/** \name Button events returned by pixma_wait_event() */ +/**@{*/ +#define PIXMA_EV_NONE 0 +#define PIXMA_EV_ACTION_MASK (0xff) +#define PIXMA_EV_BUTTON1 (1 << 8) +#define PIXMA_EV_BUTTON2 (2 << 8) +/**@}*/ +/** @} end of API group */ + + +struct pixma_t; +struct pixma_scan_ops_t; +struct pixma_scan_param_t; +struct pixma_config_t; +struct pixma_cmdbuf_t; +struct pixma_imagebuf_t; +struct pixma_device_status_t; + +typedef struct pixma_t pixma_t; +typedef struct pixma_scan_ops_t pixma_scan_ops_t; +typedef struct pixma_scan_param_t pixma_scan_param_t; +typedef struct pixma_config_t pixma_config_t; +typedef struct pixma_cmdbuf_t pixma_cmdbuf_t; +typedef struct pixma_imagebuf_t pixma_imagebuf_t; +typedef struct pixma_device_status_t pixma_device_status_t; + + +/** \addtogroup API + * @{ */ +/** String index constants */ +typedef enum pixma_string_index_t +{ + PIXMA_STRING_MODEL, + PIXMA_STRING_ID, + PIXMA_STRING_LAST +} pixma_string_index_t; + +/** Paper sources */ +typedef enum pixma_paper_source_t +{ + PIXMA_SOURCE_FLATBED, + PIXMA_SOURCE_ADF, + PIXMA_SOURCE_TPU +} pixma_paper_source_t; + +typedef enum pixma_hardware_status_t +{ + PIXMA_HARDWARE_OK, + PIXMA_HARDWARE_ERROR +} pixma_hardware_status_t; + +typedef enum pixma_lamp_status_t +{ + PIXMA_LAMP_OK, + PIXMA_LAMP_WARMING_UP, + PIXMA_LAMP_OFF, + PIXMA_LAMP_ERROR +} pixma_lamp_status_t; + +typedef enum pixma_adf_status_t +{ + PIXMA_ADF_OK, + PIXMA_ADF_NO_PAPER, + PIXMA_ADF_JAMMED, + PIXMA_ADF_COVER_OPEN, + PIXMA_ADF_ERROR +} pixma_adf_status_t; + +typedef enum pixma_calibration_status_t +{ + PIXMA_CALIBRATION_OK, + PIXMA_CALIBRATION_IN_PROGRESS, + PIXMA_CALIBRATION_OFF, + PIXMA_CALIBRATION_ERROR +} pixma_calibration_status_t; + +/** Device status. */ +struct pixma_device_status_t +{ + pixma_hardware_status_t hardware; + pixma_lamp_status_t lamp; + pixma_adf_status_t adf; + pixma_calibration_status_t cal; +}; + +/** Scan parameters. */ +struct pixma_scan_param_t +{ + /** Size in bytes of one image line (row). + * line_size >= depth / 8 * channels * w
+ * This field will be set by pixma_check_scan_param(). */ + unsigned line_size; + + /** Size in bytes of the whole image. + * image_size = line_size * h
+ * This field will be set by pixma_check_scan_param(). */ + unsigned image_size; + + /** Channels per pixel. 1 = grayscale, 3 = color */ + unsigned channels; + + /** Bits per channels. 0 = default. Currently not used. */ + unsigned depth; + + /*@{ */ + /** Resolution. Valid values are 75,150,300,600,1200... */ + unsigned xdpi, ydpi; + /*@} */ + + /*! \name Scan area in pixels + * (0,0) = top left; positive x extends to the right; positive y to the + * bottom; in pixels. */ + /*@{ */ + unsigned x, y, w, h; + /*@} */ + + /** Gamma table. 4096 entries, 12 bit => 8 bit. If \c NULL, default gamma + * specified by subdriver will be used. */ + const uint8_t *gamma_table; + + /** \see #pixma_paper_source_t */ + pixma_paper_source_t source; +}; + +/** PIXMA model information */ +struct pixma_config_t +{ + /* If you change this structure, don't forget to update the device list in + * subdrivers. */ + const char *name; /**< Model name. */ + uint16_t vid; /**< USB Vendor ID */ + uint16_t pid; /**< USB Product ID */ + unsigned iface; /**< USB Interface number */ + const pixma_scan_ops_t *ops; /**< Subdriver ops */ + unsigned xdpi; /**< Maximum horizontal resolution[DPI] */ + unsigned ydpi; /**< Maximum vertical resolution[DPI] */ + unsigned width; /**< Maximum width of scannable area in pixels at 75DPI */ + unsigned height; /**< Maximum height of scannable area in pixels at 75DPI */ + unsigned cap; /**< Capability bitfield \see PIXMA_CAP_* */ +}; + + +/* Defined in pixma_common.c */ + +/** Initialize the driver. It must be called before any other functions + * except pixma_set_debug_level(). */ +int pixma_init (void); + +/** Free resources allocated by the driver. */ +void pixma_cleanup (void); + +/** Set the debug level. + * \param[in] level the debug level + * - 0 No debug output at all + * - 1 Only errors and warning + * - 2 General information + * - 3 Debugging messages + * - 10 USB traffic dump */ +void pixma_set_debug_level (int level); + +/** Find scanners. The device number used in pixma_open(), + * pixma_get_device_model(), pixma_get_device_id() and + * pixma_get_device_config() must be less than the value returned by the last + * call of this function. + * + * \return The number of scanners found currently. The return value is + * guaranteed to be valid until the next call to pixma_find_scanners(). */ +int pixma_find_scanners (void); + +/** Return the model name of the device \a devnr. */ +const char *pixma_get_device_model (unsigned devnr); + +/** Return the unique ID of the device \a devnr. */ +const char *pixma_get_device_id (unsigned devnr); + +/** Return the device configuration of the device \a devnr. */ +const struct pixma_config_t *pixma_get_device_config (unsigned devnr); + +/** Open a connection to the scanner \a devnr. + * \param[in] devnr The scanner number + * \param[out] handle The device handle + * \see pixma_find_scanners() */ +int pixma_open (unsigned devnr, pixma_t ** handle); + +/** Close the connection to the scanner. The scanning process is aborted + * if necessary before the function returns. */ +void pixma_close (pixma_t * s); + +/** Initiate an image acquisition process. You must keep \a sp valid until the + * image acquisition process has finished. */ +int pixma_scan (pixma_t *, pixma_scan_param_t * sp); + +/** Read a block of image data. It blocks until there is at least one byte + * available or an error occurs. + * + * \param[out] buf Pointer to the buffer + * \param[in] len Size of the buffer + * + * \retval count Number of bytes written to the buffer or error. Possible + * return value: + * - count = 0 for end of image + * - count = \a len + * - 0 < count < \a len if and only if it is the last block. + * - count < 0 for error */ +int pixma_read_image (pixma_t *, void *buf, unsigned len); + +#if 0 +/** Read a block of image data and write to \a fd. + * \param[in] fd output file descriptor + * \see pixma_read_image() */ +int pixma_read_image_write (pixma_t *, int fd); +#endif + +/** Cancel the scanning process. No effect if no scanning process is in + * progress. It can be called asynchronously e.g. within a signal + * handle. pixma_cancel() doesn't abort the operation immediately. It + * guarantees that the current call or, at the latest, the next call to + * pixma_read_image() will return zero or an error (probably -ECANCELED). */ +void pixma_cancel (pixma_t *); + +/** Check the scan parameters. This function can change your parameters to + * match the device capability, e.g. adjust width and height to the available + * area. + * \return -EINVAL for invalid parameters. */ +int pixma_check_scan_param (pixma_t *, pixma_scan_param_t *); + +/** Wait until a scanner button is pressed or it times out. It should not be + * called during image acquisition is in progress. + * \param[in] timeout in milliseconds, less than 0 means forever + * \return + * - \c PIXMA_EV_NONE if it timed out. + * - non-zero value indicates which button was pressed. + * \see PIXMA_EV_* + */ +uint32_t pixma_wait_event (pixma_t *, int timeout); + +/** Enable or disable background tasks. Currently, the only one task + * is submitting interrupt URB in background. + * \param[in] enabled if not zero, enable background task. + * \see pixma_set_interrupt_mode() */ +int pixma_enable_background (pixma_t *, int enabled); + +/** Read the current device status. + * \param[out] status the current device status + * \return 0 if succeeded. Otherwise, failed. + */ +int pixma_get_device_status (pixma_t *, pixma_device_status_t * status); + +const char *pixma_get_string (pixma_t *, pixma_string_index_t); +const pixma_config_t *pixma_get_config (pixma_t *); +void pixma_fill_gamma_table (double gamma, uint8_t * table, unsigned n); + +/** @} end of API group */ + +#endif diff -uNr sane-backends-1.0.17/backend/pixma_io.h sane-backends-1.0.18/backend/pixma_io.h --- sane-backends-1.0.17/backend/pixma_io.h 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_io.h 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,143 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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 PIXMA_IO_H +#define PIXMA_IO_H + +/* TODO: move to pixma_common.h, to reduce the number of files */ + +/*! + * \defgroup IO IO interface + * \brief The IO interface. + * + * Return value of functions that return \c int if not otherwise specified: + * - < 0 if failed (e.g. \c -ETIMEDOUT) + * - >= if succeeded + * @{ + */ + +/** Timeout for pixma_read() in milliseconds */ +#define PIXMA_BULKIN_TIMEOUT 1000 +/** Timeout for pixma_write() in milliseconds */ +#define PIXMA_BULKOUT_TIMEOUT 1000 + + +struct pixma_io_t; +struct pixma_config_t; + +/** IO handle */ +typedef struct pixma_io_t pixma_io_t; + + +/** Initialize IO module. It must be called before any other functions in this + * module. */ +int pixma_io_init (void); + +/** Shutdown all connections and free resources allocated in this module. */ +void pixma_io_cleanup (void); + +/** Find devices currently connected to the computer. + * \c devnr passed to functions + * - pixma_get_device_config() + * - pixma_get_device_id() + * - pixma_connect() + * . + * should be less than the number of devices returned by this function. + * \param[in] pixma_devices A \c NULL terminated array of pointers to + * array of pixma_config_t which is terminated by setting + * pixma_config_t::name to \c NULL. + * \return Number of devices found */ +int pixma_collect_devices (const struct pixma_config_t *const + pixma_devices[]); + +/** Get device configuration. */ +const struct pixma_config_t *pixma_get_device_config (unsigned devnr); + +/** Get a unique ID of the device \a devnr. */ +const char *pixma_get_device_id (unsigned devnr); + +/** Connect to the device and claim the scanner interface. + * \param[in] devnr + * \param[out] handle */ +int pixma_connect (unsigned devnr, pixma_io_t ** handle); + +/** Release the scanner interface and disconnect from the device. */ +void pixma_disconnect (pixma_io_t *); + +/** Reset the USB interface. \warning Use with care! */ +int pixma_reset_device (pixma_io_t *); + +/** Write data to the device. + * \return number of bytes successfully written, or < 0 if failed. + * \param[in] cmd Data + * \param[in] len Length of data + * \see #PIXMA_BULKOUT_TIMEOUT */ +int pixma_write (pixma_io_t *, const void *cmd, unsigned len); + +/** Read data from the device. + * \return + * - Number of bytes successfully read. A return value of zero means that + * a zero length USB packet was received. + * - < 0 if failed + * \param[out] buf + * \param[in] size of the buffer + * \see #PIXMA_BULKIN_TIMEOUT */ +int pixma_read (pixma_io_t *, void *buf, unsigned size); + +/** Wait for an interrupt. This function can be interrupted by a signal. + * \param[out] buf + * \param[in] size of the buffer + * \param[in] timeout in milliseconds; if < 0, wait forever. + * \return size of the received packet or error code (\c -EINTR if + * interrupted by a signal). */ +int pixma_wait_interrupt (pixma_io_t *, void *buf, unsigned size, + int timeout); + +/** Enable or disable background interrupt monitoring. + * Background mode is enabled by default. + * \param[in] background if not zero, a URB is submitted in background + * for interrupt endpoint. */ +int pixma_set_interrupt_mode (pixma_io_t *, int background); + +/** @} end of IO group */ + +#endif diff -uNr sane-backends-1.0.17/backend/pixma_io_sanei.c sane-backends-1.0.18/backend/pixma_io_sanei.c --- sane-backends-1.0.17/backend/pixma_io_sanei.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_io_sanei.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,419 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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. + */ +#include "../include/sane/config.h" + +#include +#include +#include +#include /* INT_MAX */ + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" + +#include "../include/sane/sanei_usb.h" + + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +struct pixma_io_t +{ + pixma_io_t *next; + SANE_Int usb; +}; + +typedef struct scanner_info_t +{ + struct scanner_info_t *next; + char *devname; + const pixma_config_t *cfg; + char serial[PIXMA_MAX_ID_LEN + 1]; /* "xxxxyyyy_zzzzzzz..." + x = vid, y = pid, z = serial */ +} scanner_info_t; + + +static scanner_info_t *first_scanner = NULL; +static pixma_io_t *first_io = NULL; +static unsigned nscanners; + + +static scanner_info_t * +get_scanner_info (unsigned devnr) +{ + scanner_info_t *si; + for (si = first_scanner; si && devnr != 0; --devnr, si = si->next) + { + } + return si; +} + +static SANE_Status +attach (SANE_String_Const devname) +{ + scanner_info_t *si; + + si = (scanner_info_t *) calloc (1, sizeof (*si)); + if (!si) + return SANE_STATUS_NO_MEM; + si->devname = strdup (devname); + if (!si->devname) + return SANE_STATUS_NO_MEM; + si->next = first_scanner; + first_scanner = si; + nscanners++; + return SANE_STATUS_GOOD; +} + +static void +clear_scanner_list (void) +{ + scanner_info_t *si = first_scanner; + while (si) + { + scanner_info_t *temp = si; + free (si->devname); + si = si->next; + free (temp); + } + nscanners = 0; + first_scanner = NULL; +} + +static SANE_Status +get_descriptor (SANE_Int dn, SANE_Int type, SANE_Int descidx, + SANE_Int index, SANE_Int length, SANE_Byte * data) +{ + return sanei_usb_control_msg (dn, 0x80, USB_REQ_GET_DESCRIPTOR, + ((type & 0xff) << 8) | (descidx & 0xff), + index, length, data); +} + +static SANE_Status +get_string_descriptor (SANE_Int dn, SANE_Int index, SANE_Int lang, + SANE_Int length, SANE_Byte * data) +{ + return get_descriptor (dn, USB_DT_STRING, index, lang, length, data); +} + +static void +u16tohex (uint16_t x, char *str) +{ + static const char hdigit[16] = + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', +'E', 'F' }; + str[0] = hdigit[(x >> 12) & 0xf]; + str[1] = hdigit[(x >> 8) & 0xf]; + str[2] = hdigit[(x >> 4) & 0xf]; + str[3] = hdigit[x & 0xf]; + str[4] = '\0'; +} + +static void +read_serial_number (scanner_info_t * si) +{ + uint8_t unicode[2 * (PIXMA_MAX_ID_LEN - 9) + 2]; + uint8_t ddesc[18]; + int i, len, iSerialNumber; + SANE_Int usb; + SANE_Status status; + char *serial = si->serial; + + u16tohex (si->cfg->vid, serial); + u16tohex (si->cfg->pid, serial + 4); + + if (SANE_STATUS_GOOD != sanei_usb_open (si->devname, &usb)) + return; + if (get_descriptor (usb, USB_DT_DEVICE, 0, 0, 18, ddesc) + != SANE_STATUS_GOOD) + goto done; + iSerialNumber = ddesc[16]; + if (iSerialNumber != 0) + { + int iSerialNumber = ddesc[16]; + if (get_string_descriptor (usb, 0, 0, 4, unicode) != SANE_STATUS_GOOD) + goto done; + status = + get_string_descriptor (usb, iSerialNumber, + unicode[3] * 256 + unicode[2], + sizeof (unicode), unicode); + if (status != SANE_STATUS_GOOD) + goto done; + /* Assumed charset: Latin1 */ + len = unicode[0]; + if (len > (int) sizeof (unicode)) + { + len = sizeof (unicode); + PDBG (pixma_dbg (1, "WARNING:Truncated serial number\n")); + } + serial[8] = '_'; + for (i = 2; i < len; i += 2) + { + serial[9 + i / 2 - 1] = unicode[i]; + } + serial[9 + i / 2 - 1] = '\0'; + } + else + { + PDBG (pixma_dbg (1, "WARNING:No serial number\n")); + } +done: + sanei_usb_close (usb); +} + +static int +map_error (SANE_Status ss) +{ + switch (ss) + { + case SANE_STATUS_GOOD: + return 0; + case SANE_STATUS_UNSUPPORTED: + return -ENOTSUP; + case SANE_STATUS_CANCELLED: + return -ECANCELED; + case SANE_STATUS_DEVICE_BUSY: + return -EBUSY; + case SANE_STATUS_INVAL: + return -EINVAL; + case SANE_STATUS_EOF: + return 0; /* FIXME: */ + case SANE_STATUS_JAMMED: + return -EDEADLK; + case SANE_STATUS_NO_DOCS: + return -ENODATA; + case SANE_STATUS_COVER_OPEN: + return -ENOLCK; + case SANE_STATUS_IO_ERROR: + return -EIO; + case SANE_STATUS_NO_MEM: + return -ENOMEM; + case SANE_STATUS_ACCESS_DENIED: + return -EACCES; + default: + PDBG (pixma_dbg (1, "WARNING:Unmapped SANE Status code %d\n", ss)); + return -ENOTSUP; /* should not happen */ + } +} + + +int +pixma_io_init (void) +{ + sanei_usb_init (); + nscanners = 0; + return 0; +} + +void +pixma_io_cleanup (void) +{ + while (first_io) + pixma_disconnect (first_io); + clear_scanner_list (); +} + +int +pixma_collect_devices (const struct pixma_config_t *const pixma_devices[]) +{ + unsigned i, j; + scanner_info_t *si; + const pixma_config_t *cfg; + + clear_scanner_list (); + j = 0; + for (i = 0; pixma_devices[i]; i++) + { + for (cfg = pixma_devices[i]; cfg->name; cfg++) + { + sanei_usb_find_devices (cfg->vid, cfg->pid, attach); + si = first_scanner; + while (j < nscanners) + { + si->cfg = cfg; + read_serial_number (si); + si = si->next; + j++; + } + } + } + return nscanners; +} + +const pixma_config_t * +pixma_get_device_config (unsigned devnr) +{ + const scanner_info_t *si = get_scanner_info (devnr); + return (si) ? si->cfg : NULL; +} + +const char * +pixma_get_device_id (unsigned devnr) +{ + const scanner_info_t *si = get_scanner_info (devnr); + return (si) ? si->serial : NULL; +} + +int +pixma_connect (unsigned devnr, pixma_io_t ** handle) +{ + pixma_io_t *io; + SANE_Int usb; + const scanner_info_t *si; + int error; + + *handle = NULL; + si = get_scanner_info (devnr); + if (!si) + return -EINVAL; + error = map_error (sanei_usb_open (si->devname, &usb)); + if (error < 0) + return error; + io = (pixma_io_t *) calloc (1, sizeof (*io)); + if (!io) + { + sanei_usb_close (usb); + return -ENOMEM; + } + io->next = first_io; + first_io = io; + io->usb = usb; + *handle = io; + return 0; +} + + +void +pixma_disconnect (pixma_io_t * io) +{ + pixma_io_t **p; + + if (!io) + return; + for (p = &first_io; *p && *p != io; p = &((*p)->next)) + { + } + PASSERT (*p); + if (!(*p)) + return; + sanei_usb_close (io->usb); + *p = io->next; + free (io); +} + +int +pixma_reset_device (pixma_io_t * io) +{ + UNUSED (io); + return -ENOTSUP; +} + +int +pixma_write (pixma_io_t * io, const void *cmd, unsigned len) +{ + size_t count = len; + int error; + +#ifdef HAVE_SANEI_USB_SET_TIMEOUT + sanei_usb_set_timeout (PIXMA_BULKOUT_TIMEOUT); +#endif + error = map_error (sanei_usb_write_bulk (io->usb, cmd, &count)); + if (error == -EIO) + error = -ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ + if (error == 0) + error = count; + PDBG (pixma_dump (10, "OUT ", cmd, error, len, 128)); + return error; +} + +int +pixma_read (pixma_io_t * io, void *buf, unsigned size) +{ + size_t count = size; + int error; + +#ifdef HAVE_SANEI_USB_SET_TIMEOUT + sanei_usb_set_timeout (PIXMA_BULKIN_TIMEOUT); +#endif + error = map_error (sanei_usb_read_bulk (io->usb, buf, &count)); + if (error == -EIO) + error = -ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ + if (error == 0) + error = count; + PDBG (pixma_dump (10, "IN ", buf, error, -1, 128)); + return error; +} + +int +pixma_wait_interrupt (pixma_io_t * io, void *buf, unsigned size, int timeout) +{ + size_t count = size; + int error; + +#ifdef HAVE_SANEI_USB_SET_TIMEOUT + /* FIXME: What is the meaning of "timeout" in sanei_usb? */ + if (timeout < 0) + timeout = INT_MAX; + else if (timeout < 10) + timeout = 10; + sanei_usb_set_timeout (timeout); +#endif + error = map_error (sanei_usb_read_int (io->usb, buf, &count)); + if (error == -EIO) + error = -ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ + if (error == 0) + error = count; + if (error != -ETIMEDOUT) + PDBG (pixma_dump (10, "INTR", buf, error, -1, -1)); + return error; +} + +int +pixma_set_interrupt_mode (pixma_io_t * s, int background) +{ + UNUSED (s); + return (background) ? -ENOTSUP : 0; +} diff -uNr sane-backends-1.0.17/backend/pixma_mp150.c sane-backends-1.0.18/backend/pixma_mp150.c --- sane-backends-1.0.17/backend/pixma_mp150.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_mp150.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,745 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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. + */ +/* test cases + 1. short USB packet (must be no -ETIMEDOUT) + 2. cancel using button on the printer (look for abort command) + 3. start scan while busy (status 0x1414) + 4. cancel using ctrl-c (must send abort command) + */ +#include "../include/sane/config.h" + +#include +#include +#include +#include /* localtime(C90) */ +#include /* POSIX */ + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" + + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +/* Size of the command buffer should be multiple of wMaxPacketLength and + greater than 4096+24. + 4096 = size of gamma table. 24 = header + checksum */ +#define IMAGE_BLOCK_SIZE (512*1024) +#define CMDBUF_SIZE (4096 + 24) +#define DEFAULT_GAMMA 1.0 + +enum mp150_state_t +{ + state_idle, + state_warmup, + state_scanning, + state_transfering, + state_finished +}; + +enum mp150_cmd_t +{ + cmd_start_session = 0xdb20, + cmd_select_source = 0xdd20, + cmd_gamma = 0xee20, + cmd_scan_param = 0xde20, + cmd_status = 0xf320, + cmd_abort_session = 0xef20, + cmd_time = 0xeb80, + cmd_read_image = 0xd420, + cmd_error_info = 0xff20, + + cmd_e920 = 0xe920 /* seen in MP800 */ +}; + +typedef struct mp150_t +{ + enum mp150_state_t state; + pixma_cmdbuf_t cb; + uint8_t *imgbuf; + uint8_t current_status[12]; + + unsigned last_block:1; +} mp150_t; + + +/* + STAT: 0x0606 = ok, + 0x1515 = failed (-ECANCELED), + 0x1414 = busy (-EBUSY) + + Transaction scheme + 1. command_header/data | result_header + 2. command_header | result_header/data + 3. command_header | result_header/image_data + + - data has checksum in the last byte. + - image_data has no checksum. + - data and image_data begins in the same USB packet as + command_header or result_header. + + command format #1: + u16be cmd + u8[6] 0 + u8[4] 0 + u32be PLEN parameter length + u8[PLEN-1] parameter + u8 parameter check sum + result: + u16be STAT + u8 0 + u8 0 or 0x21 if STAT == 0x1414 + u8[4] 0 + + command format #2: + u16be cmd + u8[6] 0 + u8[4] 0 + u32be RLEN result length + result: + u16be STAT + u8[6] 0 + u8[RLEN-1] result + u8 result check sum + + command format #3: (only used by read_image_block) + u16be 0xd420 + u8[6] 0 + u8[4] 0 + u32be max. block size + 8 + result: + u16be STAT + u8[6] 0 + u8 block info bitfield: 0x8 = end of scan, 0x10 = no more paper, 0x20 = no more data + u8[3] 0 + u32be ILEN image data size + u8[ILEN] image data + */ + +static void mp150_finish_scan (pixma_t * s); + +static int +is_calibrated (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + return (mp->current_status[8] == 1); +} + +static int +has_paper (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + return (mp->current_status[1] == 0); /* FIXME: correct? */ +} + +static void +drain_bulk_in (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) >= 0); +} + +static int +abort_session (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session); +} + +static int +send_cmd_e920 (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_e920); +} + +static int +start_session (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session); +} + +static int +select_source (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + + data = pixma_newcmd (&mp->cb, cmd_select_source, 12, 0); + data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1; + data[1] = 1; + return pixma_exec (s, &mp->cb); +} + +static int +send_gamma_table (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + const uint8_t *lut = s->param->gamma_table; + uint8_t *data; + + data = pixma_newcmd (&mp->cb, cmd_gamma, 4096 + 8, 0); + data[0] = (s->param->channels == 3) ? 0x10 : 0x01; + pixma_set_be16 (0x1004, data + 2); + if (lut) + memcpy (data + 4, lut, 4096); + else + pixma_fill_gamma_table (DEFAULT_GAMMA, data + 4, 4096); + return pixma_exec (s, &mp->cb); +} + +static unsigned +calc_raw_width (const pixma_scan_param_t * param) +{ + unsigned raw_width; + /* NOTE: Actually, we can send arbitary width to MP150. Lines returned + are always padded to multiple of 4 or 12 pixels. Is this valid for + other models, too? */ + if (param->channels == 1) + raw_width = ALIGN (param->w, 12); + else + raw_width = ALIGN (param->w, 4); + return raw_width; +} + +static int +send_scan_param (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + unsigned raw_width = calc_raw_width (s->param); + + data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x30, 0); + pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04); + pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06); + pixma_set_be32 (s->param->x, data + 0x08); + pixma_set_be32 (s->param->y, data + 0x0c); + pixma_set_be32 (raw_width, data + 0x10); + pixma_set_be32 (s->param->h, data + 0x14); + data[0x18] = (s->param->channels == 1) ? 0x04 : 0x08; + data[0x19] = s->param->channels * s->param->depth; /* bits per pixel */ + data[0x20] = 0xff; + data[0x23] = 0x81; + data[0x26] = 0x02; + data[0x27] = 0x01; + return pixma_exec (s, &mp->cb); +} + +static int +query_status (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mp->cb, cmd_status, 0, 12); + error = pixma_exec (s, &mp->cb); + if (error >= 0) + { + memcpy (mp->current_status, data, 12); + PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u busy=%u\n", + data[1], data[8], data[7], data[9])); + } + return error; +} + +static int +send_time (pixma_t * s) +{ + /* Why does a scanner need a time? */ + time_t now; + struct tm *t; + uint8_t *data; + mp150_t *mp = (mp150_t *) s->subdriver; + + data = pixma_newcmd (&mp->cb, cmd_time, 20, 0); + pixma_get_time (&now, NULL); + t = localtime (&now); + snprintf ((char *) data, 16, + "%02d/%02d/%02d %02d:%02d", + t->tm_year % 100, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min); + PDBG (pixma_dbg (3, "Sending time: '%s'\n", (char *) data)); + return pixma_exec (s, &mp->cb); +} + +/* TODO: Simplify this function. Read the whole data packet in one shot. */ +static int +read_image_block (pixma_t * s, uint8_t * header, uint8_t * data) +{ + static const uint8_t cmd[16] = /* 0xd420 cmd */ + { 0xd4, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + IMAGE_BLOCK_SIZE / 65536, 0, 8 + }; + mp150_t *mp = (mp150_t *) s->subdriver; + const int hlen = 8 + 8; + int error, datalen; + + mp->state = state_transfering; + mp->cb.reslen = + pixma_cmd_transaction (s, cmd, sizeof (cmd), mp->cb.buf, 512); + datalen = mp->cb.reslen; + if (datalen < 0) + return datalen; + + memcpy (header, mp->cb.buf, hlen); + if (datalen >= hlen) + { + datalen -= hlen; + memcpy (data, mp->cb.buf + hlen, datalen); + data += datalen; + if (mp->cb.reslen == 512) + { + error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen); + if (error < 0) + return error; + datalen += error; + } + } + + mp->state = state_scanning; + mp->cb.expected_reslen = 0; + error = pixma_check_result (&mp->cb); + if (error < 0) + return error; + if (mp->cb.reslen < hlen) + return -EPROTO; + return datalen; +} + +static int +read_error_info (pixma_t * s, void *buf, unsigned size) +{ + unsigned len = 16; + mp150_t *mp = (mp150_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len); + error = pixma_exec (s, &mp->cb); + if (error >= 0 && buf) + { + if (len < size) + size = len; + /* NOTE: I've absolutely no idea what the returned data mean. */ + memcpy (buf, data, size); + error = len; + } + return error; +} + +/* +handle_interrupt() waits until it receives an interrupt packet or times out. +It calls send_time() and query_status() if necessary. Therefore, make sure +that handle_interrupt() is only called from a safe context for send_time() +and query_status(). + + Returns: + 0 timed out + 1 interrupt packet received + -EINTR interrupted by signal + <0 error +*/ +static int +handle_interrupt (pixma_t * s, int timeout) +{ + uint8_t buf[16]; + int len; + + len = pixma_wait_interrupt (s->io, buf, sizeof (buf), timeout); + if (len == -ETIMEDOUT) + return 0; + if (len < 0) + return len; + if (len != 16) + { + PDBG (pixma_dbg (1, "WARNING:unexpected interrupt packet length %d\n", + len)); + return -EPROTO; + } + + /* More than one event can be reported at the same time. */ + if (buf[3] & 1) + send_time (s); + if (buf[9] & 2) + query_status (s); + if (buf[0] & 2) + s->events = PIXMA_EV_BUTTON2 | buf[1]; /* b/w scan */ + if (buf[0] & 1) + s->events = PIXMA_EV_BUTTON1 | buf[1]; /* color scan */ + return 1; +} + +static int +wait_until_ready (pixma_t * s) +{ + int error, tmo = 60; + + error = query_status (s); + if (error < 0) + return error; + while (!is_calibrated (s)) + { + error = handle_interrupt (s, 1000); + if (s->cancel) + return -ECANCELED; + if (error != -EINTR && error < 0) + return error; + if (--tmo == 0) + { + PDBG (pixma_dbg (1, "WARNING:Timed out in wait_until_ready()\n")); + return -ETIMEDOUT; + } +#if 0 + /* If we use sanei_usb_*, we sometimes lose interrupts! So poll the + * status here. */ + error = query_status (s); + if (error < 0) + return error; +#endif + } + return 0; +} + +static int +mp150_open (pixma_t * s) +{ + mp150_t *mp; + uint8_t *buf; + + mp = (mp150_t *) calloc (1, sizeof (*mp)); + if (!mp) + return -ENOMEM; + + buf = (uint8_t *) malloc (CMDBUF_SIZE + IMAGE_BLOCK_SIZE); + if (!buf) + { + free (mp); + return -ENOMEM; + } + + s->subdriver = mp; + mp->state = state_idle; + + mp->cb.buf = buf; + mp->cb.size = CMDBUF_SIZE; + mp->cb.res_header_len = 8; + mp->cb.cmd_header_len = 16; + mp->cb.cmd_len_field_ofs = 14; + + mp->imgbuf = buf + CMDBUF_SIZE; + + handle_interrupt (s, 200); + return 0; +} + +static void +mp150_close (pixma_t * s) +{ + mp150_t *mp = (mp150_t *) s->subdriver; + + mp150_finish_scan (s); + free (mp->cb.buf); + free (mp); + s->subdriver = NULL; +} + +static int +mp150_check_param (pixma_t * s, pixma_scan_param_t * sp) +{ + UNUSED (s); + + sp->depth = 8; /* MP150 only supports 8 bit per channel. */ + sp->line_size = calc_raw_width (sp) * sp->channels; + return 0; +} + +static int +mp150_scan (pixma_t * s) +{ + int error = 0, tmo; + mp150_t *mp = (mp150_t *) s->subdriver; + + if (mp->state != state_idle) + return -EBUSY; + + while (handle_interrupt (s, 0) > 0) + { + } /* clear interrupt packets buffer */ + + if (s->param->source == PIXMA_SOURCE_ADF) + { + error = query_status (s); + if (error < 0) + return error; +#ifndef NDEBUG + pixma_dbg (1, + "Support for ADF is untested. If it doesn't work, please set\n"); + pixma_dbg (1, + "the debug level to 10 and send me debug messages for these\n"); + pixma_dbg (1, "2 cases:\n"); + pixma_dbg (1, " 1. no paper in ADF\n"); + pixma_dbg (1, " 2. paper in ADF\n"); +#endif + if (!has_paper (s)) + return -ENODATA; + } + + if (s->cfg->vid == 0x170d /*MP800 */ ) + { + /* FIXME: What does this command do? */ + error = send_cmd_e920 (s); + if (error == 0) + { + query_status (s); + } + if (error == -ECANCELED || error == -EBUSY) + { + PDBG (pixma_dbg (2, "cmd e920 returned %s\n", strerror (-error))); + query_status (s); + } + else + { + PDBG (pixma_dbg (1, "WARNING:cmd e920 failed %s\n", + strerror (-error))); + return error; + } + } + + error = start_session (s); + tmo = 10; + while (error == -EBUSY && --tmo >= 0) + { + if (s->cancel) + { + error = -ECANCELED; + break; + } + PDBG (pixma_dbg + (2, "Scanner is busy. Timed out in %d sec.\n", tmo + 1)); + pixma_sleep (1000000); + error = start_session (s); + } + if (error == -EBUSY || error == -ETIMEDOUT) + { + /* The scanner maybe hangs. We try to empty output buffer of the + * scanner and issue the cancel command. */ + PDBG (pixma_dbg (2, "Scanner hangs? Sending abort_session command.\n")); + drain_bulk_in (s); + abort_session (s); + pixma_sleep (500000); + error = start_session (s); + } + if (error >= 0) + mp->state = state_warmup; + if (error >= 0) + error = select_source (s); + if (error >= 0) + error = send_gamma_table (s); + if (error >= 0) + error = send_scan_param (s); + if (error < 0) + { + mp150_finish_scan (s); + return error; + } + return 0; +} + +static int +mp150_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) +{ + int error; + mp150_t *mp = (mp150_t *) s->subdriver; + unsigned block_size, bytes_received; + uint8_t header[16]; + + if (mp->state == state_warmup) + { + error = wait_until_ready (s); + if (error < 0) + return error; + pixma_sleep (1500000); /* No need to wait, actually, but Window's driver + * waits 1.5 sec. */ + mp->state = state_scanning; + mp->last_block = 0; + } + + do + { + if (s->cancel) + return -ECANCELED; + if (mp->last_block) + { + /* end of image */ + mp->state = state_finished; + return 0; + } + + error = read_image_block (s, header, mp->imgbuf); + if (error < 0) + { + if (error == -ECANCELED) + { + /* NOTE: I see this in traffic logs but I don't know + its meaning. */ + read_error_info (s, NULL, 0); + } + return error; + } + + bytes_received = error; + block_size = pixma_get_be32 (header + 12); + mp->last_block = ((header[8] & 0x28) == 0x28); + if ((header[8] & ~0x38) != 0) + { + PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n")); + PDBG (pixma_hexdump (1, header, 16)); + } + PASSERT (bytes_received == block_size); + + if (block_size == 0) + { + /* no image data at this moment. */ + pixma_sleep (10000); + } + } + while (block_size == 0); + + ib->rptr = mp->imgbuf; + ib->rend = mp->imgbuf + bytes_received; + return ib->rend - ib->rptr; +} + +static void +mp150_finish_scan (pixma_t * s) +{ + int error; + mp150_t *mp = (mp150_t *) s->subdriver; + + switch (mp->state) + { + case state_transfering: + drain_bulk_in (s); + /* fall through */ + case state_scanning: + case state_warmup: + error = abort_session (s); + if (error < 0) + PDBG (pixma_dbg (1, "WARNING:abort_session() failed: %s\n", + strerror (-error))); + /* fall through */ + case state_finished: + while (handle_interrupt (s, 0) > 0); + mp->state = state_idle; + /* fall through */ + case state_idle: + break; + } +} + +static void +mp150_wait_event (pixma_t * s, int timeout) +{ + /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for + * instance. */ + while (s->events == 0 && handle_interrupt (s, timeout) > 0) + { + } +} + +static int +mp150_get_status (pixma_t * s, pixma_device_status_t * status) +{ + int error; + + error = query_status (s); + if (error < 0) + return error; + status->hardware = PIXMA_HARDWARE_OK; + status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER; + status->cal = + (is_calibrated (s)) ? PIXMA_CALIBRATION_OK : PIXMA_CALIBRATION_OFF; + return 0; +} + +static const pixma_scan_ops_t pixma_mp150_ops = { + mp150_open, + mp150_close, + mp150_scan, + mp150_fill_buffer, + mp150_finish_scan, + mp150_wait_event, + mp150_check_param, + mp150_get_status +}; + +#define DEVICE(name, pid, dpi, cap) { \ + name, /* name */ \ + 0x04a9, pid, /* vid pid */ \ + 0, /* iface */ \ + &pixma_mp150_ops, /* ops */ \ + dpi, 2*(dpi), /* xdpi, ydpi */ \ + 638, 877, /* width, height */ \ + PIXMA_CAP_EASY_RGB|PIXMA_CAP_GRAY| \ + PIXMA_CAP_GAMMA_TABLE|PIXMA_CAP_EVENTS|cap \ +} + +const pixma_config_t pixma_mp150_devices[] = { + DEVICE ("Canon PIXMA MP150", 0x1709, 1200, 0), + DEVICE ("Canon PIXMA MP170", 0x170a, 1200, 0), + DEVICE ("Canon PIXMA MP450", 0x170b, 1200, 0), + DEVICE ("Canon PIXMA MP500", 0x170c, 1200, 0), + DEVICE ("Canon PIXMA MP530", 0xffff, 1200, + PIXMA_CAP_ADF | PIXMA_CAP_EXPERIMENT), + DEVICE ("Canon PIXMA MP800", 0x170d, 2400, PIXMA_CAP_TPU), + DEVICE ("Canon PIXMA MP800R", 0xffff, 2400, + PIXMA_CAP_TPU | PIXMA_CAP_EXPERIMENT), + DEVICE ("Canon PIXMA MP830", 0x1713, 2400, PIXMA_CAP_ADF), + DEVICE (NULL, 0, 0, 0) +}; diff -uNr sane-backends-1.0.17/backend/pixma_mp730.c sane-backends-1.0.18/backend/pixma_mp730.c --- sane-backends-1.0.17/backend/pixma_mp730.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_mp730.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,542 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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. + */ +#include "../include/sane/config.h" + +#include +#include +#include /* POSIX */ + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" + + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +#define IMAGE_BLOCK_SIZE (0xc000) +#define CMDBUF_SIZE 512 + + +enum mp730_state_t +{ + state_idle, + state_warmup, + state_scanning, + state_transfering, + state_finished +}; + +enum mp730_cmd_t +{ + cmd_start_session = 0xdb20, + cmd_select_source = 0xdd20, + cmd_gamma = 0xee20, + cmd_scan_param = 0xde20, + cmd_status = 0xf320, + cmd_abort_session = 0xef20, + cmd_time = 0xeb80, + cmd_read_image = 0xd420, + + cmd_activate = 0xcf60 +}; + +typedef struct mp730_t +{ + enum mp730_state_t state; + pixma_cmdbuf_t cb; + unsigned raw_width; + uint8_t current_status[12]; + + uint8_t *buf, *imgbuf, *lbuf; + unsigned imgbuf_len; + + unsigned last_block:1; +} mp730_t; + + +static void mp730_finish_scan (pixma_t * s); + +static int +has_paper (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + return (mp->current_status[1] == 0); +} + +static void +drain_bulk_in (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + while (pixma_read (s->io, mp->imgbuf, IMAGE_BLOCK_SIZE) >= 0); +} + +static int +abort_session (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session); +} + +static int +query_status (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mp->cb, cmd_status, 0, 12); + error = pixma_exec (s, &mp->cb); + if (error >= 0) + { + memcpy (mp->current_status, data, 12); + PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u\n", + data[1], data[8], data[7])); + } + return error; +} + +static int +activate (pixma_t * s, uint8_t x) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + uint8_t *data = pixma_newcmd (&mp->cb, cmd_activate, 10, 0); + data[0] = 1; + data[3] = x; + return pixma_exec (s, &mp->cb); +} + +static int +start_session (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session); +} + +static int +select_source (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + uint8_t *data = pixma_newcmd (&mp->cb, cmd_select_source, 10, 0); + data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1; + return pixma_exec (s, &mp->cb); +} + +static int +send_scan_param (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + uint8_t *data; + + data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x2e, 0); + pixma_set_be16 (s->param->xdpi | 0x1000, data + 0x04); + pixma_set_be16 (s->param->ydpi | 0x1000, data + 0x06); + pixma_set_be32 (s->param->x, data + 0x08); + pixma_set_be32 (s->param->y, data + 0x0c); + pixma_set_be32 (mp->raw_width, data + 0x10); + pixma_set_be32 (s->param->h, data + 0x14); + data[0x18] = (s->param->channels == 1) ? 0x04 : 0x08; + data[0x19] = s->param->channels * s->param->depth; /* bits per pixel */ + data[0x1f] = 0x7f; + data[0x20] = 0xff; + data[0x23] = 0x81; + return pixma_exec (s, &mp->cb); +} + +static int +read_image_block (pixma_t * s, uint8_t * header, uint8_t * data) +{ + static const uint8_t cmd[10] = /* 0xd420 cmd */ + { 0xd4, 0x20, 0, 0, 0, 0, 0, IMAGE_BLOCK_SIZE / 256, 4, 0 }; + mp730_t *mp = (mp730_t *) s->subdriver; + const int hlen = 2 + 4; + int error, datalen; + + mp->state = state_transfering; + mp->cb.reslen = + pixma_cmd_transaction (s, cmd, sizeof (cmd), mp->cb.buf, 512); + datalen = mp->cb.reslen; + if (datalen < 0) + return datalen; + + memcpy (header, mp->cb.buf, hlen); + if (datalen >= hlen) + { + datalen -= hlen; + memcpy (data, mp->cb.buf + hlen, datalen); + data += datalen; + if (mp->cb.reslen == 512) + { + error = pixma_read (s->io, data, IMAGE_BLOCK_SIZE - 512 + hlen); + if (error < 0) + return error; + datalen += error; + } + } + + mp->state = state_scanning; + mp->cb.expected_reslen = 0; + error = pixma_check_result (&mp->cb); + if (error < 0) + return error; + if (mp->cb.reslen < hlen) + return -EPROTO; + return datalen; +} + +static int +send_time (pixma_t * s) +{ + /* TODO */ + UNUSED (s); + return 0; +} + +static int +handle_interrupt (pixma_t * s, int timeout) +{ + uint8_t buf[16]; + int len; + + len = pixma_wait_interrupt (s->io, buf, sizeof (buf), timeout); + if (len == -ETIMEDOUT) + return 0; + if (len < 0) + return len; + if (len != 8) + { + PDBG (pixma_dbg (1, "WARNING:unexpected interrupt packet length %d\n", + len)); + return -EPROTO; + } + + if (buf[5] & 8) + send_time (s); + return 1; +} + +static int +step1 (pixma_t * s) +{ + int error; + + error = query_status (s); + if (error < 0) + return error; + if (s->param->source == PIXMA_SOURCE_ADF && !has_paper (s)) + return -ENODATA; + if (error >= 0) + error = activate (s, 0); + if (error >= 0) + error = activate (s, 4); + return error; +} + +static void +pack_rgb (const uint8_t * src, unsigned nlines, unsigned w, uint8_t * dst) +{ + unsigned w2, stride; + + w2 = 2 * w; + stride = 3 * w; + for (; nlines != 0; nlines--) + { + unsigned x; + for (x = 0; x != w; x++) + { + *dst++ = src[x + 0]; + *dst++ = src[x + w]; + *dst++ = src[x + w2]; + } + src += stride; + } +} + +static int +mp730_open (pixma_t * s) +{ + mp730_t *mp; + uint8_t *buf; + + mp = (mp730_t *) calloc (1, sizeof (*mp)); + if (!mp) + return -ENOMEM; + + buf = (uint8_t *) malloc (CMDBUF_SIZE); + if (!buf) + { + free (mp); + return -ENOMEM; + } + + s->subdriver = mp; + mp->state = state_idle; + + mp->cb.buf = buf; + mp->cb.size = CMDBUF_SIZE; + mp->cb.res_header_len = 2; + mp->cb.cmd_header_len = 10; + mp->cb.cmd_len_field_ofs = 7; + + return 0; +} + +static void +mp730_close (pixma_t * s) +{ + mp730_t *mp = (mp730_t *) s->subdriver; + + mp730_finish_scan (s); + free (mp->cb.buf); + free (mp); + s->subdriver = NULL; +} + +static unsigned +calc_raw_width (const pixma_scan_param_t * sp) +{ + unsigned raw_width; + /* FIXME: Does MP730 need the alignment? */ + if (sp->channels == 1) + raw_width = ALIGN (sp->w, 12); + else + raw_width = ALIGN (sp->w, 4); + return raw_width; +} + +static int +mp730_check_param (pixma_t * s, pixma_scan_param_t * sp) +{ + UNUSED (s); + + sp->depth = 8; /* FIXME: Does MP730 supports other depth? */ + sp->line_size = calc_raw_width (sp) * sp->channels; + return 0; +} + +static int +mp730_scan (pixma_t * s) +{ + int error, n; + mp730_t *mp = (mp730_t *) s->subdriver; + uint8_t *buf; + + if (mp->state != state_idle) + return -EBUSY; + + mp->raw_width = calc_raw_width (s->param); + PDBG (pixma_dbg (3, "raw_width = %u\n", mp->raw_width)); + + n = IMAGE_BLOCK_SIZE / s->param->line_size + 1; + buf = (uint8_t *) malloc ((n + 1) * s->param->line_size + IMAGE_BLOCK_SIZE); + if (!buf) + return -ENOMEM; + mp->buf = buf; + mp->lbuf = buf; + mp->imgbuf = buf + n * s->param->line_size; + mp->imgbuf_len = 0; + + error = step1 (s); + if (error >= 0) + error = start_session (s); + if (error >= 0) + mp->state = state_scanning; + if (error >= 0) + error = select_source (s); + if (error >= 0) + error = send_scan_param (s); + if (error < 0) + { + mp730_finish_scan (s); + return error; + } + mp->last_block = 0; + return 0; +} + +static int +mp730_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) +{ + int error, n; + mp730_t *mp = (mp730_t *) s->subdriver; + unsigned block_size, bytes_received; + uint8_t header[16]; + + do + { + do + { + if (s->cancel) + return -ECANCELED; + if (mp->last_block) + { + /* end of image */ + mp->state = state_finished; + return 0; + } + + error = read_image_block (s, header, mp->imgbuf + mp->imgbuf_len); + if (error < 0) + return error; + + bytes_received = error; + block_size = pixma_get_be16 (header + 4); + mp->last_block = ((header[2] & 0x28) == 0x28); + if ((header[2] & ~0x38) != 0) + { + PDBG (pixma_dbg (1, "WARNING: Unexpected result header\n")); + PDBG (pixma_hexdump (1, header, 16)); + } + PASSERT (bytes_received == block_size); + + if (block_size == 0) + { + /* no image data at this moment. */ + pixma_sleep (100000); /* FIXME: too short, too long? */ + } + } + while (block_size == 0); + + mp->imgbuf_len += bytes_received; + n = mp->imgbuf_len / s->param->line_size; + /* n = number of full lines (rows) we have in the buffer. */ + if (n != 0) + { + pack_rgb (mp->imgbuf, n, mp->raw_width, mp->lbuf); + block_size = n * s->param->line_size; + mp->imgbuf_len -= block_size; + memcpy (mp->imgbuf, mp->imgbuf + block_size, mp->imgbuf_len); + } + } + while (n == 0); + + ib->rptr = mp->lbuf; + ib->rend = mp->lbuf + block_size; + return ib->rend - ib->rptr; +} + +static void +mp730_finish_scan (pixma_t * s) +{ + int error; + mp730_t *mp = (mp730_t *) s->subdriver; + + switch (mp->state) + { + case state_transfering: + drain_bulk_in (s); + /* fall through */ + case state_scanning: + case state_warmup: + error = abort_session (s); + if (error < 0) + PDBG (pixma_dbg (1, "WARNING:abort_session() failed: %s\n", + strerror (-error))); + /* fall through */ + case state_finished: + query_status (s); + query_status (s); + activate (s, 0); + free (mp->buf); + mp->buf = mp->lbuf = mp->imgbuf = NULL; + mp->state = state_idle; + /* fall through */ + case state_idle: + break; + } +} + +static void +mp730_wait_event (pixma_t * s, int timeout) +{ + /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for + * instance. */ + while (s->events == 0 && handle_interrupt (s, timeout) > 0) + { + } +} + +static int +mp730_get_status (pixma_t * s, pixma_device_status_t * status) +{ + int error; + + error = query_status (s); + if (error < 0) + return error; + status->hardware = PIXMA_HARDWARE_OK; + status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER; + return 0; +} + + +static const pixma_scan_ops_t pixma_mp730_ops = { + mp730_open, + mp730_close, + mp730_scan, + mp730_fill_buffer, + mp730_finish_scan, + mp730_wait_event, + mp730_check_param, + mp730_get_status +}; + +#define DEVICE(name, pid, dpi, w, h, cap) { \ + name, /* name */ \ + 0x04a9, pid, /* vid pid */ \ + 1, /* iface */ \ + &pixma_mp730_ops, /* ops */ \ + dpi, dpi, /* xdpi, ydpi */ \ + w, h, /* width, height */ \ + cap \ +} +const pixma_config_t pixma_mp730_devices[] = { +/* TODO: check area limits */ + DEVICE ("Canon MultiPASS MP700", 0x2630, 1200, 638, 877, 0), + DEVICE ("Canon MultiPASS MP730", 0x262f, 1200, 637, 868, + PIXMA_CAP_ADF | PIXMA_CAP_EXPERIMENT), + DEVICE (NULL, 0, 0, 0, 0, 0) +}; diff -uNr sane-backends-1.0.17/backend/pixma_mp750.c sane-backends-1.0.18/backend/pixma_mp750.c --- sane-backends-1.0.17/backend/pixma_mp750.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_mp750.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,758 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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. + */ +#include "../include/sane/config.h" + +#include +#include +#include /* POSIX */ + +#include "pixma_rename.h" +#include "pixma_common.h" +#include "pixma_io.h" + + +/* Credits should go to Martin Schewe (http://pixma.schewe.com) who analysed + the protocol of MP750. */ + +/* TODO: remove lines marked with SIM. They are inserted so that I can test + the subdriver with the simulator. WY */ + +#ifdef __GNUC__ +# define UNUSED(v) (void) v +#else +# define UNUSED(v) +#endif + +#define IMAGE_BLOCK_SIZE 0xc000 +#define CMDBUF_SIZE 512 +#define HAS_PAPER(s) (s[1] == 0) +#define IS_WARMING_UP(s) (s[7] != 3) +#define IS_CALIBRATED(s) (s[8] == 0xf) + + +enum mp750_state_t +{ + state_idle, + state_warmup, + state_scanning, + state_transfering, + state_finished +}; + +enum mp750_cmd_t +{ + cmd_start_session = 0xdb20, + cmd_select_source = 0xdd20, + cmd_scan_param = 0xde20, + cmd_status = 0xf320, + cmd_abort_session = 0xef20, + cmd_time = 0xeb80, + cmd_read_image = 0xd420, + + cmd_activate = 0xcf60, + cmd_calibrate = 0xe920, + cmd_error_info = 0xff20 +}; + +typedef struct mp750_t +{ + enum mp750_state_t state; + pixma_cmdbuf_t cb; + unsigned raw_width, raw_height; + uint8_t current_status[12]; + + uint8_t *buf, *rawimg, *img; + unsigned rawimg_left, imgbuf_len, last_block_size, imgbuf_ofs; + int shifted_bytes; + unsigned last_block; + + unsigned monochrome:1; + unsigned needs_time:1; + unsigned needs_abort:1; +} mp750_t; + + + +static void mp750_finish_scan (pixma_t * s); +static void check_status (pixma_t * s); + +static int +has_paper (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + return HAS_PAPER (mp->current_status); +} + +static int +is_warming_up (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + return IS_WARMING_UP (mp->current_status); +} + +static int +is_calibrated (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + return IS_CALIBRATED (mp->current_status); +} + +static void +drain_bulk_in (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + while (pixma_read (s->io, mp->buf, IMAGE_BLOCK_SIZE) >= 0); +} + +static int +abort_session (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_abort_session); +} + +static int +query_status (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mp->cb, cmd_status, 0, 12); + error = pixma_exec (s, &mp->cb); + if (error >= 0) + { + memcpy (mp->current_status, data, 12); + PDBG (pixma_dbg (3, "Current status: paper=%u cal=%u lamp=%u\n", + data[1], data[8], data[7])); + } + return error; +} + +static int +activate (pixma_t * s, uint8_t x) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + uint8_t *data = pixma_newcmd (&mp->cb, cmd_activate, 10, 0); + data[0] = 1; + data[3] = x; + return pixma_exec (s, &mp->cb); +} + +static int +activate_cs (pixma_t * s, uint8_t x) +{ + /*SIM*/ check_status (s); + return activate (s, x); +} + +static int +start_session (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_start_session); +} + +static int +select_source (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + uint8_t *data = pixma_newcmd (&mp->cb, cmd_select_source, 10, 0); + data[0] = (s->param->source == PIXMA_SOURCE_ADF) ? 2 : 1; + data[1] = 1; + return pixma_exec (s, &mp->cb); +} + +static int +send_scan_param (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + uint8_t *data; + + data = pixma_newcmd (&mp->cb, cmd_scan_param, 0x2e, 0); + pixma_set_be16 (s->param->xdpi | 0x8000, data + 0x04); + pixma_set_be16 (s->param->ydpi | 0x8000, data + 0x06); + pixma_set_be32 (s->param->x, data + 0x08); + pixma_set_be32 (s->param->y, data + 0x0c); + pixma_set_be32 (mp->raw_width, data + 0x10); + pixma_set_be32 (mp->raw_height, data + 0x14); + data[0x18] = 8; /* 8 = color, 4 = grayscale(?) */ + data[0x19] = s->param->channels * s->param->depth; + data[0x20] = 0xff; + data[0x23] = 0x81; + data[0x26] = 0x02; + data[0x27] = 0x01; + data[0x29] = mp->monochrome ? 0 : 1; + + return pixma_exec (s, &mp->cb); +} + +static int +calibrate (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + return pixma_exec_short_cmd (s, &mp->cb, cmd_calibrate); +} + +static int +calibrate_cs (pixma_t * s) +{ + /*SIM*/ check_status (s); + return calibrate (s); +} + +static int +request_image_block_ex (pixma_t * s, unsigned *size, uint8_t * info, + unsigned flag) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + int error; + + memset (mp->cb.buf, 0, 10); + pixma_set_be16 (cmd_read_image, mp->cb.buf); + mp->cb.buf[7] = *size >> 8; + mp->cb.buf[8] = 4 | flag; + mp->cb.reslen = pixma_cmd_transaction (s, mp->cb.buf, 10, mp->cb.buf, 6); + mp->cb.expected_reslen = 0; + error = pixma_check_result (&mp->cb); + if (error >= 0) + { + if (mp->cb.reslen == 6) + { + *info = mp->cb.buf[2]; + *size = pixma_get_be16 (mp->cb.buf + 4); + } + else + { + error = -EPROTO; + } + } + return error; +} + +static int +request_image_block (pixma_t * s, unsigned *size, uint8_t * info) +{ + return request_image_block_ex (s, size, info, 0); +} + +static int +request_image_block2 (pixma_t * s, uint8_t * info) +{ + unsigned temp = 0; + return request_image_block_ex (s, &temp, info, 0x20); +} + +static int +read_image_block (pixma_t * s, uint8_t * data) +{ + int count, temp; + + count = pixma_read (s->io, data, IMAGE_BLOCK_SIZE); + if (count < 0) + return count; + if (count == IMAGE_BLOCK_SIZE) + pixma_read (s->io, &temp, 0); + return count; +} + +static int +read_error_info (pixma_t * s, void *buf, unsigned size) +{ + unsigned len = 16; + mp750_t *mp = (mp750_t *) s->subdriver; + uint8_t *data; + int error; + + data = pixma_newcmd (&mp->cb, cmd_error_info, 0, len); + error = pixma_exec (s, &mp->cb); + if (error >= 0 && buf) + { + if (len < size) + size = len; + /* NOTE: I've absolutely no idea what the returned data mean. */ + memcpy (buf, data, size); + error = len; + } + return error; +} + +static int +handle_interrupt (pixma_t * s, int timeout) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + int error; + uint8_t intr[16]; + + error = pixma_wait_interrupt (s->io, intr, sizeof (intr), timeout); + if (error == -ETIMEDOUT) + return 0; + if (error < 0) + return error; + if (error != 16) + { + PDBG (pixma_dbg (1, "WARNING:unexpected interrupt packet length %d\n", + error)); + return -EPROTO; + } + + if (intr[10] & 0x40) + mp->needs_time = 1; + if (intr[12] & 0x40) + query_status (s); + if (intr[15] & 1) + s->events = PIXMA_EV_BUTTON2; /* b/w scan */ + if (intr[15] & 2) + s->events = PIXMA_EV_BUTTON1; /* color scan */ + return 1; +} + +static void +check_status (pixma_t * s) +{ + while (handle_interrupt (s, 0) > 0); +} + +static int +step1 (pixma_t * s) +{ + int error; + + error = activate (s, 0); + if (error >= 0) + error = query_status (s); + if (error >= 0) + { + if (s->param->source == PIXMA_SOURCE_ADF && !has_paper (s)) + return -ENODATA; + } + if (error >= 0) + error = activate_cs (s, 0); + /*SIM*/ if (error >= 0) + error = activate_cs (s, 0x20); + if (error >= 0) + error = calibrate_cs (s); + return error; +} + +static void +shift_rgb (const uint8_t * src, unsigned pixels, + int sr, int sg, int sb, uint8_t * dst) +{ + for (; pixels != 0; pixels--) + { + *(dst++ + sr) = *src++; + *(dst++ + sg) = *src++; + *(dst++ + sb) = *src++; + } +} + + +static int +mp750_open (pixma_t * s) +{ + mp750_t *mp; + uint8_t *buf; + + mp = (mp750_t *) calloc (1, sizeof (*mp)); + if (!mp) + return -ENOMEM; + + buf = (uint8_t *) malloc (CMDBUF_SIZE); + if (!buf) + { + free (mp); + return -ENOMEM; + } + + s->subdriver = mp; + mp->state = state_idle; + + /* ofs: 0 1 2 3 4 5 6 7 8 9 + cmd: cmd1 cmd2 00 00 00 00 00 00 00 00 + data length-^^^^^ => cmd_len_field_ofs + |--------- cmd_header_len --------| + + res: res1 res2 + |---------| res_header_len + */ + mp->cb.buf = buf; + mp->cb.size = CMDBUF_SIZE; + mp->cb.res_header_len = 2; + mp->cb.cmd_header_len = 10; + mp->cb.cmd_len_field_ofs = 7; + + return 0; +} + +static void +mp750_close (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + + mp750_finish_scan (s); + free (mp->cb.buf); + free (mp); + s->subdriver = NULL; +} + +static int +mp750_check_param (pixma_t * s, pixma_scan_param_t * sp) +{ + unsigned raw_width; + + UNUSED (s); + + sp->depth = 8; /* FIXME: Does MP750 supports other depth? */ + if (sp->channels == 1) + raw_width = ALIGN (sp->w, 12); + else + raw_width = ALIGN (sp->w, 4); + sp->line_size = raw_width * sp->channels; + return 0; +} + +static int +mp750_scan (pixma_t * s) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + int error; + uint8_t *buf; + unsigned size, dpi, spare; + + if (mp->state != state_idle) + return -EBUSY; + + if (s->param->channels == 1) + mp->raw_width = ALIGN (s->param->w, 12); + else + mp->raw_width = ALIGN (s->param->w, 4); + + dpi = s->param->ydpi; + spare = 4 * dpi / 75; /* FIXME: or maybe (4*dpi/75 + 1)? */ + mp->raw_height = s->param->h + spare; + PDBG (pixma_dbg (3, "raw_width=%u raw_height=%u dpi=%u\n", + mp->raw_width, mp->raw_height, dpi)); + + size = 8 + 2 * IMAGE_BLOCK_SIZE + spare * s->param->line_size; + buf = (uint8_t *) malloc (size); + if (!buf) + return -ENOMEM; + mp->buf = buf; + mp->rawimg = buf; + mp->imgbuf_ofs = spare * s->param->line_size; + mp->img = mp->rawimg + IMAGE_BLOCK_SIZE + 8; + mp->imgbuf_len = IMAGE_BLOCK_SIZE + mp->imgbuf_ofs; + mp->rawimg_left = 0; + mp->last_block_size = 0; + mp->shifted_bytes = -(int) mp->imgbuf_ofs; + + error = step1 (s); + if (error >= 0) + error = start_session (s); + if (error >= 0) + mp->state = state_warmup; + if (error >= 0) + error = select_source (s); + if (error >= 0) + error = send_scan_param (s); + if (error < 0) + { + mp750_finish_scan (s); + return error; + } + return 0; +} + +static int +mp750_fill_buffer (pixma_t * s, pixma_imagebuf_t * ib) +{ + mp750_t *mp = (mp750_t *) s->subdriver; + int error; + uint8_t info; + unsigned block_size, bytes_received, n; + int shift[3], base_shift; + + if (mp->state == state_warmup) + { + int tmo = 60; + + query_status (s); + check_status (s); + /*SIM*/ while (!is_calibrated (s) && --tmo >= 0) + { + if (s->cancel) + return -ECANCELED; + if (handle_interrupt (s, 1000) > 0) + { + block_size = 0; + error = request_image_block (s, &block_size, &info); + /*SIM*/ if (error < 0) + return error; + } + } + if (tmo < 0) + { + PDBG (pixma_dbg (1, "WARNING:Timed out waiting for calibration\n")); + return -ETIMEDOUT; + } + pixma_sleep (100000); + query_status (s); + if (is_warming_up (s) || !is_calibrated (s)) + { + PDBG (pixma_dbg (1, "WARNING:Wrong status: wup=%d cal=%d\n", + is_warming_up (s), is_calibrated (s))); + return -EPROTO; + } + block_size = 0; + request_image_block (s, &block_size, &info); + /*SIM*/ mp->state = state_scanning; + mp->last_block = 0; + } + + /* TODO: Move to other place, values are constant. */ + base_shift = 2 * s->param->ydpi / 75 * s->param->line_size; + if (s->param->source == PIXMA_SOURCE_ADF) + { + shift[0] = 0; + shift[1] = -base_shift; + shift[2] = -2 * base_shift; + } + else + { + shift[0] = -2 * base_shift; + shift[1] = -base_shift; + shift[2] = 0; + } + + do + { + if (mp->last_block_size > 0) + { + block_size = mp->imgbuf_len - mp->last_block_size; + memcpy (mp->img, mp->img + mp->last_block_size, block_size); + } + + do + { + if (s->cancel) + return -ECANCELED; + if (mp->last_block) + { + /* end of image */ + info = mp->last_block; + if (info != 0x38) + { + query_status (s); + /*SIM*/ while ((info & 0x28) != 0x28) + { + pixma_sleep (10000); + error = request_image_block2 (s, &info); + if (s->cancel) + return -ECANCELED; + if (error < 0) + return error; + } + } + mp->needs_abort = (info != 0x38); + mp->last_block = info; + mp->state = state_finished; + return 0; + } + + check_status (s); + /*SIM*/ while (handle_interrupt (s, 1) > 0); + /*SIM*/ block_size = IMAGE_BLOCK_SIZE; + error = request_image_block (s, &block_size, &info); + if (error < 0) + { + if (error == -ECANCELED) + read_error_info (s, NULL, 0); + return error; + } + mp->last_block = info; + if ((info & ~0x38) != 0) + { + PDBG (pixma_dbg (1, "WARNING: Unknown info byte %x\n", info)); + } + if (block_size == 0) + { + /* no image data at this moment. */ + pixma_sleep (10000); + } + } + while (block_size == 0); + + error = read_image_block (s, mp->rawimg + mp->rawimg_left); + if (error < 0) + { + mp->state = state_transfering; + return error; + } + bytes_received = error; + PASSERT (bytes_received == block_size); + + /* TODO: simplify! */ + mp->rawimg_left += bytes_received; + n = mp->rawimg_left / 3; + /* n = number of pixels in the buffer */ + shift_rgb (mp->rawimg, n, shift[0], shift[1], shift[2], + mp->img + mp->imgbuf_ofs); + n *= 3; + mp->shifted_bytes += n; + mp->rawimg_left -= n; /* rawimg_left = 0, 1 or 2 bytes left in the buffer. */ + mp->last_block_size = n; + memcpy (mp->rawimg, mp->rawimg + n, mp->rawimg_left); + } + while (mp->shifted_bytes <= 0); + + if ((unsigned) mp->shifted_bytes < mp->last_block_size) + ib->rptr = mp->img + mp->last_block_size - mp->shifted_bytes; + else + ib->rptr = mp->img; + ib->rend = mp->img + mp->last_block_size; + return ib->rend - ib->rptr; +} + +static void +mp750_finish_scan (pixma_t * s) +{ + int error; + mp750_t *mp = (mp750_t *) s->subdriver; + + switch (mp->state) + { + case state_transfering: + drain_bulk_in (s); + /* fall through */ + case state_scanning: + case state_warmup: + error = abort_session (s); + if (abort_session (s) == -ECANCELED) + read_error_info (s, NULL, 0); + /* fall through */ + case state_finished: + if (s->param->source == PIXMA_SOURCE_FLATBED) + { + /*SIM*/ query_status (s); + if (abort_session (s) == -ECANCELED) + { + read_error_info (s, NULL, 0); + query_status (s); + } + } + query_status (s); + /*SIM*/ activate (s, 0); + if (mp->needs_abort) + { + mp->needs_abort = 0; + abort_session (s); + } + while (handle_interrupt (s, 100) > 0); + free (mp->buf); + mp->buf = mp->rawimg = NULL; + mp->state = state_idle; + /* fall through */ + case state_idle: + break; + } +} + +static void +mp750_wait_event (pixma_t * s, int timeout) +{ + /* FIXME: timeout is not correct. See usbGetCompleteUrbNoIntr() for + * instance. */ + while (s->events == 0 && handle_interrupt (s, timeout) > 0) + { + } +} + +static int +mp750_get_status (pixma_t * s, pixma_device_status_t * status) +{ + int error; + + error = query_status (s); + if (error < 0) + return error; + status->hardware = PIXMA_HARDWARE_OK; + status->adf = (has_paper (s)) ? PIXMA_ADF_OK : PIXMA_ADF_NO_PAPER; + status->cal = + (is_calibrated (s)) ? PIXMA_CALIBRATION_OK : PIXMA_CALIBRATION_OFF; + status->lamp = (is_warming_up (s)) ? PIXMA_LAMP_WARMING_UP : PIXMA_LAMP_OK; + return 0; +} + + +static const pixma_scan_ops_t pixma_mp750_ops = { + mp750_open, + mp750_close, + mp750_scan, + mp750_fill_buffer, + mp750_finish_scan, + mp750_wait_event, + mp750_check_param, + mp750_get_status +}; + +/* FIXME: What is the maximum resolution? 2400 DPI?*/ + +#define DEVICE(name, pid, dpi, cap) { \ + name, /* name */ \ + 0x04a9, pid, /* vid pid */ \ + 0, /* iface */ \ + &pixma_mp750_ops, /* ops */ \ + dpi, 2*(dpi), /* xdpi, ydpi */ \ + 637, 877, /* width, height */ \ + cap \ +} + +const pixma_config_t pixma_mp750_devices[] = { + DEVICE ("Canon PIXMA MP750", 0x1706, 2400, + PIXMA_CAP_ADF | PIXMA_CAP_EVENTS), + DEVICE ("Canon PIXMA MP780", 0x1707, 2400, + PIXMA_CAP_ADF | PIXMA_CAP_EVENTS), + DEVICE ("Canon PIXMA MP760", 0x1708, 2400, PIXMA_CAP_EVENTS), + DEVICE (NULL, 0, 0, 0) +}; diff -uNr sane-backends-1.0.17/backend/pixma_rename.h sane-backends-1.0.18/backend/pixma_rename.h --- sane-backends-1.0.17/backend/pixma_rename.h 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_rename.h 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,102 @@ +/* SANE - Scanner Access Now Easy. + + Copyright (C) 2006 Wittawat Yamwong + + 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 PIXMA_RENAME_H +#define PIXMA_RENAME_H + + +#undef BACKEND_NAME +#define BACKEND_NAME pixma + +#define pixma_cancel sanei_pixma_cancel +#define pixma_check_dpi sanei_pixma_check_dpi +#define pixma_check_result sanei_pixma_check_result +#define pixma_check_scan_param sanei_pixma_check_scan_param +#define pixma_cleanup sanei_pixma_cleanup +#define pixma_close sanei_pixma_close +#define pixma_cmd_transaction sanei_pixma_cmd_transaction +#define pixma_collect_devices sanei_pixma_collect_devices +#define pixma_connect sanei_pixma_connect +#define pixma_dbg DBG +#define pixma_disconnect sanei_pixma_disconnect +#define pixma_dump sanei_pixma_dump +#define pixma_enable_background sanei_pixma_enable_background +#define pixma_exec sanei_pixma_exec +#define pixma_exec_short_cmd sanei_pixma_exec_short_cmd +#define pixma_fill_gamma_table sanei_pixma_fill_gamma_table +#define pixma_find_scanners sanei_pixma_find_scanners +#define pixma_get_be16 sanei_pixma_get_be16 +#define pixma_get_be32 sanei_pixma_get_be32 +#define pixma_get_config sanei_pixma_get_config +#define pixma_get_device_config sanei_pixma_get_device_config +#define pixma_get_device_id sanei_pixma_get_device_id +#define pixma_get_device_model sanei_pixma_get_device_model +#define pixma_get_device_status sanei_pixma_get_device_status +#define pixma_get_string sanei_pixma_get_string +#define pixma_get_time sanei_pixma_get_time +#define pixma_hexdump sanei_pixma_hexdump +#define pixma_init sanei_pixma_init +#define pixma_io_cleanup sanei_pixma_io_cleanup +#define pixma_io_init sanei_pixma_io_init +#define pixma_map_status_errno sanei_pixma_map_status_errno +#define pixma_mp150_devices sanei_pixma_mp150_devices +#define pixma_mp730_devices sanei_pixma_mp730_devices +#define pixma_mp750_devices sanei_pixma_mp750_devices +#define pixma_newcmd sanei_pixma_newcmd +#define pixma_open sanei_pixma_open +#define pixma_print_supported_devices sanei_pixma_print_supported_devices +#define pixma_read_image sanei_pixma_read_image +#define pixma_read sanei_pixma_read +#define pixma_reset_device sanei_pixma_reset_device +#define pixma_scan sanei_pixma_scan +#define pixma_set_be16 sanei_pixma_set_be16 +#define pixma_set_be32 sanei_pixma_set_be32 +#define pixma_set_debug_level sanei_pixma_set_debug_level +#define pixma_set_interrupt_mode sanei_pixma_set_interrupt_mode +#define pixma_sleep sanei_pixma_sleep +#define pixma_sum_bytes sanei_pixma_sum_bytes +#define pixma_wait_event sanei_pixma_wait_event +#define pixma_wait_interrupt sanei_pixma_wait_interrupt +#define pixma_write sanei_pixma_write + + +#endif diff -uNr sane-backends-1.0.17/backend/pixma_sane_options.c sane-backends-1.0.18/backend/pixma_sane_options.c --- sane-backends-1.0.17/backend/pixma_sane_options.c 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_sane_options.c 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,255 @@ +/* Automatically generated from pixma_sane.c */ +static const SANE_Range constraint_gamma_table = { 0, 255, 0 }; + + +static int +find_string_in_list (SANE_String_Const str, const SANE_String_Const * list) +{ + int i; + for (i = 0; list[i] && strcmp (str, list[i]) != 0; i++) + { + } + return i; +} + +static int +build_option_descriptors (struct pixma_sane_t *ss) +{ + SANE_Option_Descriptor *sod; + option_descriptor_t *opt; + + memset (OPT_IN_CTX, 0, sizeof (OPT_IN_CTX)); + + opt = &(OPT_IN_CTX[opt_opt_num_opts]); + sod = &opt->sod; + sod->type = SANE_TYPE_INT; + sod->title = SANE_TITLE_NUM_OPTIONS; + sod->desc = SANE_DESC_NUM_OPTIONS; + sod->name = ""; + sod->unit = SANE_UNIT_NONE; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_DETECT; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_opt_num_opts].info = 0; + opt->def.w = opt_last; + opt->val.w = opt_last; + + opt = &(OPT_IN_CTX[opt__group_1]); + sod = &opt->sod; + sod->type = SANE_TYPE_GROUP; + sod->title = SANE_I18N ("Scan mode"); + sod->desc = sod->title; + + opt = &(OPT_IN_CTX[opt_resolution]); + sod = &opt->sod; + sod->type = SANE_TYPE_INT; + sod->title = SANE_TITLE_SCAN_RESOLUTION; + sod->desc = SANE_DESC_SCAN_RESOLUTION; + sod->name = "resolution"; + sod->unit = SANE_UNIT_DPI; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; + sod->constraint_type = SANE_CONSTRAINT_WORD_LIST; + sod->constraint.word_list = ss->dpi_list; + OPT_IN_CTX[opt_resolution].info = SANE_INFO_RELOAD_PARAMS; + opt->def.w = 75; + opt->val.w = 75; + + opt = &(OPT_IN_CTX[opt_mode]); + sod = &opt->sod; + sod->type = SANE_TYPE_STRING; + sod->title = SANE_TITLE_SCAN_MODE; + sod->desc = SANE_DESC_SCAN_MODE; + sod->name = "mode"; + sod->unit = SANE_UNIT_NONE; + sod->size = 11; + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; + sod->constraint_type = SANE_CONSTRAINT_STRING_LIST; + sod->constraint.string_list = ss->mode_list; + OPT_IN_CTX[opt_mode].info = SANE_INFO_RELOAD_PARAMS; + opt->def.s = SANE_I18N ("Color"); + opt->val.w = find_string_in_list (opt->def.s, sod->constraint.string_list); + + opt = &(OPT_IN_CTX[opt_source]); + sod = &opt->sod; + sod->type = SANE_TYPE_STRING; + sod->title = SANE_TITLE_SCAN_SOURCE; + sod->desc = SANE_DESC_SCAN_SOURCE; + sod->name = "source"; + sod->unit = SANE_UNIT_NONE; + sod->size = 31; + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + sod->constraint_type = SANE_CONSTRAINT_STRING_LIST; + sod->constraint.string_list = ss->source_list; + OPT_IN_CTX[opt_source].info = 0; + opt->def.s = SANE_I18N ("Flatbed"); + opt->val.w = find_string_in_list (opt->def.s, sod->constraint.string_list); + + opt = &(OPT_IN_CTX[opt_button_controlled]); + sod = &opt->sod; + sod->type = SANE_TYPE_BOOL; + sod->title = SANE_I18N ("Button-controlled scan (experimental)"); + sod->desc = + SANE_I18N + ("When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button."); + sod->name = "button-controlled"; + sod->unit = SANE_UNIT_NONE; + sod->size = sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_button_controlled].info = 0; + opt->def.w = SANE_FALSE; + opt->val.w = SANE_FALSE; + + opt = &(OPT_IN_CTX[opt__group_2]); + sod = &opt->sod; + sod->type = SANE_TYPE_GROUP; + sod->title = SANE_I18N ("Gamma"); + sod->desc = sod->title; + + opt = &(OPT_IN_CTX[opt_custom_gamma]); + sod = &opt->sod; + sod->type = SANE_TYPE_BOOL; + sod->title = SANE_TITLE_CUSTOM_GAMMA; + sod->desc = SANE_DESC_CUSTOM_GAMMA; + sod->name = "custom-gamma"; + sod->unit = SANE_UNIT_NONE; + sod->size = sizeof (SANE_Word); + sod->cap = + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | + SANE_CAP_INACTIVE; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_custom_gamma].info = 0; + opt->def.w = SANE_TRUE; + opt->val.w = SANE_TRUE; + + opt = &(OPT_IN_CTX[opt_gamma_table]); + sod = &opt->sod; + sod->type = SANE_TYPE_INT; + sod->title = SANE_TITLE_GAMMA_VECTOR; + sod->desc = SANE_DESC_GAMMA_VECTOR; + sod->name = "gamma-table"; + sod->unit = SANE_UNIT_NONE; + sod->size = 4096 * sizeof (SANE_Word); + sod->cap = + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | + SANE_CAP_INACTIVE; + sod->constraint_type = SANE_CONSTRAINT_RANGE; + sod->constraint.range = &constraint_gamma_table; + OPT_IN_CTX[opt_gamma_table].info = 0; + + opt = &(OPT_IN_CTX[opt__group_3]); + sod = &opt->sod; + sod->type = SANE_TYPE_GROUP; + sod->title = SANE_I18N ("Geometry"); + sod->desc = sod->title; + + opt = &(OPT_IN_CTX[opt_tl_x]); + sod = &opt->sod; + sod->type = SANE_TYPE_FIXED; + sod->title = SANE_TITLE_SCAN_TL_X; + sod->desc = SANE_DESC_SCAN_TL_X; + sod->name = "tl-x"; + sod->unit = SANE_UNIT_MM; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; + sod->constraint_type = SANE_CONSTRAINT_RANGE; + sod->constraint.range = &ss->xrange; + OPT_IN_CTX[opt_tl_x].info = SANE_INFO_RELOAD_PARAMS; + opt->def.w = SANE_FIX (0); + opt->val.w = SANE_FIX (0); + + opt = &(OPT_IN_CTX[opt_tl_y]); + sod = &opt->sod; + sod->type = SANE_TYPE_FIXED; + sod->title = SANE_TITLE_SCAN_TL_Y; + sod->desc = SANE_DESC_SCAN_TL_Y; + sod->name = "tl-y"; + sod->unit = SANE_UNIT_MM; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; + sod->constraint_type = SANE_CONSTRAINT_RANGE; + sod->constraint.range = &ss->yrange; + OPT_IN_CTX[opt_tl_y].info = SANE_INFO_RELOAD_PARAMS; + opt->def.w = SANE_FIX (0); + opt->val.w = SANE_FIX (0); + + opt = &(OPT_IN_CTX[opt_br_x]); + sod = &opt->sod; + sod->type = SANE_TYPE_FIXED; + sod->title = SANE_TITLE_SCAN_BR_X; + sod->desc = SANE_DESC_SCAN_BR_X; + sod->name = "br-x"; + sod->unit = SANE_UNIT_MM; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; + sod->constraint_type = SANE_CONSTRAINT_RANGE; + sod->constraint.range = &ss->xrange; + OPT_IN_CTX[opt_br_x].info = SANE_INFO_RELOAD_PARAMS; + opt->def.w = sod->constraint.range->max; + opt->val.w = sod->constraint.range->max; + + opt = &(OPT_IN_CTX[opt_br_y]); + sod = &opt->sod; + sod->type = SANE_TYPE_FIXED; + sod->title = SANE_TITLE_SCAN_BR_Y; + sod->desc = SANE_DESC_SCAN_BR_Y; + sod->name = "br-y"; + sod->unit = SANE_UNIT_MM; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; + sod->constraint_type = SANE_CONSTRAINT_RANGE; + sod->constraint.range = &ss->yrange; + OPT_IN_CTX[opt_br_y].info = SANE_INFO_RELOAD_PARAMS; + opt->def.w = sod->constraint.range->max; + opt->val.w = sod->constraint.range->max; + + opt = &(OPT_IN_CTX[opt__group_4]); + sod = &opt->sod; + sod->type = SANE_TYPE_GROUP; + sod->title = SANE_I18N ("Buttons"); + sod->desc = sod->title; + + opt = &(OPT_IN_CTX[opt_button_update]); + sod = &opt->sod; + sod->type = SANE_TYPE_BUTTON; + sod->title = SANE_I18N ("Update button state"); + sod->desc = sod->title; + sod->name = "button-update"; + sod->unit = SANE_UNIT_NONE; + sod->size = 0; + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_button_update].info = 0; + + opt = &(OPT_IN_CTX[opt_button_1]); + sod = &opt->sod; + sod->type = SANE_TYPE_INT; + sod->title = SANE_I18N ("Button 1"); + sod->desc = sod->title; + sod->name = "button-1"; + sod->unit = SANE_UNIT_NONE; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_button_1].info = 0; + opt->def.w = 0; + opt->val.w = 0; + + opt = &(OPT_IN_CTX[opt_button_2]); + sod = &opt->sod; + sod->type = SANE_TYPE_INT; + sod->title = SANE_I18N ("Button 2"); + sod->desc = sod->title; + sod->name = "button-2"; + sod->unit = SANE_UNIT_NONE; + sod->size = 1 * sizeof (SANE_Word); + sod->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + sod->constraint_type = SANE_CONSTRAINT_NONE; + OPT_IN_CTX[opt_button_2].info = 0; + opt->def.w = 0; + opt->val.w = 0; + + return 0; + +} diff -uNr sane-backends-1.0.17/backend/pixma_sane_options.h sane-backends-1.0.18/backend/pixma_sane_options.h --- sane-backends-1.0.17/backend/pixma_sane_options.h 1970-01-01 01:00:00.000000000 +0100 +++ sane-backends-1.0.18/backend/pixma_sane_options.h 2006-06-11 22:53:05.000000000 +0200 @@ -0,0 +1,44 @@ +/* Automatically generated from pixma_sane.c */ + +typedef union +{ + SANE_Word w; + SANE_Int i; + SANE_Bool b; + SANE_Fixed f; + SANE_String s; + void *ptr; +} option_value_t; + +typedef enum +{ + opt_opt_num_opts, + opt__group_1, + opt_resolution, + opt_mode, + opt_source, + opt_button_controlled, + opt__group_2, + opt_custom_gamma, + opt_gamma_table, + opt__group_3, + opt_tl_x, + opt_tl_y, + opt_br_x, + opt_br_y, + opt__group_4, + opt_button_update, + opt_button_1, + opt_button_2, + opt_last +} option_t; + +typedef struct +{ + SANE_Option_Descriptor sod; + option_value_t val, def; + SANE_Word info; +} option_descriptor_t; + +struct pixma_sane_t; +static int build_option_descriptors (struct pixma_sane_t *ss); diff -uNr sane-backends-1.0.17/backend/plustek.c sane-backends-1.0.18/backend/plustek.c --- sane-backends-1.0.17/backend/plustek.c 2005-10-28 18:13:33.000000000 +0200 +++ sane-backends-1.0.18/backend/plustek.c 2006-07-02 23:32:17.000000000 +0200 @@ -2,7 +2,7 @@ * Project : SANE library for Plustek flatbed scanners. *............................................................................. */ - + /** @file plustek.c * @brief SANE backend for Plustek scanner * @@ -11,7 +11,7 @@ * Original code taken from sane-0.71
* Copyright (C) 1997 Hypercore Software Design, Ltd.
* Also based on the work done by Rick Bronson
- * Copyright (C) 2000-2005 Gerhard Jaeger
+ * Copyright (C) 2000-2006 Gerhard Jaeger
* * History: * - 0.30 - initial version @@ -79,6 +79,9 @@ * - activated IPC stuff * - added _DBG_DCALDATA for fine calibration data logging * - added OPT_SPEEDUP handling + * - fixed constraint_type for OPT_BUTTON + * - 0.51 - added fine calibration caching + * - removed #define _PLUSTEK_USB *. *
* This file is part of the SANE package. @@ -154,7 +157,7 @@ #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" -#define BACKEND_VERSION "0.50-7" +#define BACKEND_VERSION "0.51-12" #define BACKEND_NAME plustek #include "../include/sane/sanei_access.h" @@ -187,25 +190,22 @@ #define _DEFAULT_DEVICE "auto" /** to disable the backend... */ -#define _PLUSTEK_USB /* declare it here, as it's used in plustek-usbscan.c too :-( */ static SANE_Bool cancelRead; static DevList *usbDevs; /* the USB-stuff... I know this is in general no good idea, but it works */ -#ifdef _PLUSTEK_USB -# include "plustek-usbio.c" -# include "plustek-usbdevs.c" -# include "plustek-usbhw.c" -# include "plustek-usbmap.c" -# include "plustek-usbscan.c" -# include "plustek-usbimg.c" -# include "plustek-usbcalfile.c" -# include "plustek-usbshading.c" -# include "plustek-usbcal.c" -# include "plustek-usb.c" -#endif +#include "plustek-usbio.c" +#include "plustek-usbdevs.c" +#include "plustek-usbhw.c" +#include "plustek-usbmap.c" +#include "plustek-usbscan.c" +#include "plustek-usbimg.c" +#include "plustek-usbcalfile.c" +#include "plustek-usbshading.c" +#include "plustek-usbcal.c" +#include "plustek-usb.c" /************************** global vars **************************************/ @@ -214,11 +214,12 @@ static Plustek_Scanner *first_handle; static const SANE_Device **devlist = 0; static unsigned long tsecs = 0; +static Plustek_Scanner *sc = NULL; static const SANE_Int bpp_lm9832_list [] = { 2, 8, 14 }; static const SANE_Int bpp_lm9833_list [] = { 2, 8, 16 }; -static const SANE_String_Const mode_usb_list[] = +static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, @@ -226,7 +227,7 @@ NULL }; -static const SANE_String_Const ext_mode_list[] = +static const SANE_String_Const source_list[] = { SANE_I18N("Normal"), SANE_I18N("Transparency"), @@ -249,6 +250,9 @@ /* authorization stuff */ static SANE_Auth_Callback auth = NULL; +/* prototype... */ +static SANE_Status local_sane_start(Plustek_Scanner *, int); + /****************************** the backend... *******************************/ #define _YN(x) (x?"yes":"no") @@ -302,7 +306,7 @@ * @param dev - pointer to the device specific structure * @return The function always returns SANE_STATUS_GOOD */ -static SANE_Status +static SANE_Status drvclose( Plustek_Device *dev ) { if( dev->fd >= 0 ) { @@ -335,10 +339,10 @@ /* are we in TPA-mode? */ mode = scanner->val[OPT_MODE].w; if( scanner->val[OPT_EXT_MODE].w != 0 ) - mode += 2; - + mode += 2; + scanner->params.depth = scanner->val[OPT_BIT_DEPTH].w; - + if( mode == 0 ) { scanmode = COLOR_BW; scanner->params.depth = 1; @@ -351,13 +355,29 @@ } else { scanner->params.depth = 16; if( mode == 1 ) - scanmode = COLOR_GRAY16; + scanmode = COLOR_GRAY16; else scanmode = COLOR_TRUE48; - } + } return scanmode; } +/** return the len of the largest string in the array + */ +static size_t +max_string_size (const SANE_String_Const strings[]) +{ + size_t size, max_size = 0; + SANE_Int i; + + for (i = 0; strings[i]; ++i) { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } + return max_size; +} + /** shutdown open pipes */ static SANE_Status @@ -380,55 +400,46 @@ /** will be called when a child is going down */ -static void sig_chldhandler( int signo ) +static void +sig_chldhandler( int signo ) { DBG( _DBG_PROC, "(SIG) Child is down (signal=%d)\n", signo ); + if (sc) { + sc->calibrating = SANE_FALSE; + sc = NULL; + } } /** signal handler to kill the child process */ -static RETSIGTYPE reader_process_sigterm_handler( int signo ) +static RETSIGTYPE +reader_process_sigterm_handler( int signo ) { DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo ); _exit( SANE_STATUS_GOOD ); } -static RETSIGTYPE usb_reader_process_sigterm_handler( int signo ) +static RETSIGTYPE +usb_reader_process_sigterm_handler( int signo ) { DBG( _DBG_PROC, "(SIG) reader_process: terminated by signal %d\n", signo ); cancelRead = SANE_TRUE; } -static RETSIGTYPE sigalarm_handler( int signo ) +static RETSIGTYPE +sigalarm_handler( int signo ) { _VAR_NOT_USED( signo ); DBG( _DBG_PROC, "ALARM!!!\n" ); } -/** executed as a child process - * read the data from the driver and send them to the parent process +/** */ -static int reader_process( void *args ) +static void +thread_entry(void) { - int line, lerrn; - unsigned char *buf; - unsigned long status; - unsigned long data_length; struct SIGACTION act; sigset_t ignore_set; - Plustek_Scanner *scanner = (Plustek_Scanner *)args; -#ifdef USE_IPC - IPCDef ipc; - Plustek_Device *dev = scanner->hw; -#endif - - if( sanei_thread_is_forked()) { - DBG( _DBG_PROC, "reader_process started (forked)\n" ); - close( scanner->r_pipe ); - scanner->r_pipe = -1; - } else { - DBG( _DBG_PROC, "reader_process started (as thread)\n" ); - } sigfillset ( &ignore_set ); sigdelset ( &ignore_set, SIGTERM ); @@ -437,7 +448,7 @@ #endif sigprocmask( SIG_SETMASK, &ignore_set, 0 ); - memset ( &act, 0, sizeof (act)); + memset(&act, 0, sizeof (act)); sigaction( SIGTERM, &act, 0 ); cancelRead = SANE_FALSE; @@ -451,6 +462,33 @@ act.sa_handler = usb_reader_process_sigterm_handler; sigaction( SIGUSR1, &act, 0 ); +} + +/** executed as a child process + * read the data from the driver and send them to the parent process + */ +static int +reader_process( void *args ) +{ + int line, lerrn; + unsigned char *buf; + unsigned long status; + unsigned long data_length; + Plustek_Scanner *scanner = (Plustek_Scanner *)args; + Plustek_Device *dev = scanner->hw; +#ifdef USE_IPC + IPCDef ipc; +#endif + + if( sanei_thread_is_forked()) { + DBG( _DBG_PROC, "reader_process started (forked)\n" ); + close( scanner->r_pipe ); + scanner->r_pipe = -1; + } else { + DBG( _DBG_PROC, "reader_process started (as thread)\n" ); + } + + thread_entry(); data_length = scanner->params.lines * scanner->params.bytes_per_line; @@ -469,7 +507,7 @@ #ifdef USE_IPC /* prepare IPC structure */ - memset( &ipc, 0, sizeof(ipc)); + memset(&ipc, 0, sizeof(ipc)); ipc.transferRate = DEFAULT_RATE; if( dev->transferRate > 0 && dev->transferRate != DEFAULT_RATE ) @@ -478,20 +516,25 @@ /* write ipc back to parent in any case... */ write( scanner->w_pipe, &ipc, sizeof(ipc)); #endif + /* on success, we read all data from the driver... */ if( 0 == status ) { - for( line = 0; line < scanner->params.lines; line++ ) { + if( !usb_InCalibrationMode(dev)) { - status = usbDev_ReadLine( scanner->hw ); - if((int)status < 0 ) { - break; + DBG( _DBG_INFO, "reader_process: READING....\n" ); + + for( line = 0; line < scanner->params.lines; line++ ) { + + status = usbDev_ReadLine( scanner->hw ); + if((int)status < 0 ) { + break; + } + write( scanner->w_pipe, buf, scanner->params.bytes_per_line ); + buf += scanner->params.bytes_per_line; } - write( scanner->w_pipe, buf, scanner->params.bytes_per_line ); - buf += scanner->params.bytes_per_line; } } - /* on error, there's no need to clean up, as this is done by the parent */ lerrn = errno; @@ -516,13 +559,13 @@ /** stop the current scan process */ -static SANE_Status do_cancel( Plustek_Scanner *scanner, SANE_Bool closepipe ) +static SANE_Status +do_cancel( Plustek_Scanner *scanner, SANE_Bool closepipe ) { struct SIGACTION act; pid_t res; DBG( _DBG_PROC,"do_cancel\n" ); - scanner->scanning = SANE_FALSE; if( scanner->reader_pid > 0 ) { @@ -530,6 +573,7 @@ DBG( _DBG_PROC, ">>>>>>>> killing reader_process <<<<<<<<\n" ); cancelRead = SANE_TRUE; + scanner->calibrating = SANE_FALSE; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; @@ -562,6 +606,7 @@ usb_StartLampTimer( scanner->hw ); #endif } + scanner->calibrating = SANE_FALSE; if( SANE_TRUE == closepipe ) { close_pipe( scanner ); @@ -577,12 +622,13 @@ return SANE_STATUS_CANCELLED; } -/** Currently we support only LM9831/2/3 chips and these use the same - * sizes... +/** As we support only LM9831/2/3 chips we use the same + * sizes for each device... * @param s - pointer to the scanner specific structure * @return The function always returns SANE_STATUS_GOOD */ -static SANE_Status initGammaSettings( Plustek_Scanner *s ) +static SANE_Status +initGammaSettings( Plustek_Scanner *s ) { int i, j, val; double gamma; @@ -626,7 +672,8 @@ * @param s - pointer to the scanner specific structure * @return nothing */ -static void checkGammaSettings( Plustek_Scanner *s ) +static void +checkGammaSettings( Plustek_Scanner *s ) { int i, j; @@ -642,13 +689,14 @@ /** initialize the options for the backend according to the device we have */ -static SANE_Status init_options( Plustek_Scanner *s ) +static SANE_Status +init_options( Plustek_Scanner *s ) { int i; Plustek_Device *dev = s->hw; AdjDef *adj = &dev->adj; - memset( s->opt, 0, sizeof(s->opt)); + memset(s->opt, 0, sizeof(s->opt)); for( i = 0; i < NUM_OPTIONS; ++i ) { s->opt[i].size = sizeof (SANE_Word); @@ -662,7 +710,7 @@ s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE; - s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; + s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode"); @@ -675,11 +723,11 @@ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; s->opt[OPT_MODE].type = SANE_TYPE_STRING; - s->opt[OPT_MODE].size = 32; + s->opt[OPT_MODE].size = max_string_size(mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - s->opt[OPT_MODE].constraint.string_list = mode_usb_list; + s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].w = 2; /* Color */ - + /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; @@ -698,9 +746,9 @@ s->opt[OPT_EXT_MODE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_EXT_MODE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_EXT_MODE].type = SANE_TYPE_STRING; - s->opt[OPT_EXT_MODE].size = 32; + s->opt[OPT_EXT_MODE].size = max_string_size(source_list); s->opt[OPT_EXT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - s->opt[OPT_EXT_MODE].constraint.string_list = ext_mode_list; + s->opt[OPT_EXT_MODE].constraint.string_list = source_list; s->val[OPT_EXT_MODE].w = 0; /* Normal */ /* brightness */ @@ -848,14 +896,14 @@ s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof(SANE_Word); /* GAMMA stuff is disabled per default */ - s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; + _DISABLE(OPT_GAMMA_VECTOR); + _DISABLE(OPT_GAMMA_VECTOR_R); + _DISABLE(OPT_GAMMA_VECTOR_G); + _DISABLE(OPT_GAMMA_VECTOR_B); /* disable extended mode list for devices without TPA */ if( 0 == (s->hw->caps.dwFlag & SFLAG_TPA)) - s->opt[OPT_EXT_MODE].cap |= SANE_CAP_INACTIVE; + _DISABLE(OPT_EXT_MODE); /* "Device settings" group: */ s->opt[OPT_DEVICE_GROUP].title = SANE_I18N("Device-Settings"); @@ -876,6 +924,25 @@ s->opt[OPT_CACHECAL].type = SANE_TYPE_BOOL; s->val[OPT_CACHECAL].w = adj->cacheCalData; + s->opt[OPT_CALIBRATE].name = "calibrate"; + s->opt[OPT_CALIBRATE].title = SANE_I18N("Calibrate");; + s->opt[OPT_CALIBRATE].desc = SANE_I18N("Performs calibration"); + s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON; + s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE; + s->opt[OPT_CALIBRATE].size = sizeof (SANE_Word); + s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE; + s->opt[OPT_CALIBRATE].constraint.range = 0; + s->opt[OPT_CALIBRATE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | + SANE_CAP_AUTOMATIC; + _ENABLE(OPT_CALIBRATE); + if( !adj->cacheCalData ) + _DISABLE(OPT_CALIBRATE); + s->val[OPT_CALIBRATE].w = 0; + + /* it's currently not available for CCD devices */ + if( !usb_IsCISDevice(dev) && !dev->adj.altCalibrate) + _DISABLE(OPT_CALIBRATE); + s->opt[OPT_SPEEDUP].name = "speedup-switch"; s->opt[OPT_SPEEDUP].title = SANE_I18N("Speedup sensor");; s->opt[OPT_SPEEDUP].desc = SANE_I18N("Enables or disables speeding up sensor movement."); @@ -883,7 +950,7 @@ s->val[OPT_SPEEDUP].w = !(adj->disableSpeedup); if( s->hw->usbDev.HwSetting.dHighSpeed == 0.0 ) - s->opt[OPT_SPEEDUP].cap |= SANE_CAP_INACTIVE; + _DISABLE(OPT_SPEEDUP); s->opt[OPT_LAMPOFF_ONEND].name = SANE_NAME_LAMP_OFF_AT_EXIT; s->opt[OPT_LAMPOFF_ONEND].title = SANE_TITLE_LAMP_OFF_AT_EXIT; @@ -898,6 +965,11 @@ s->opt[OPT_WARMUPTIME].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_WARMUPTIME].constraint.range = &warmup_range; s->val[OPT_WARMUPTIME].w = adj->warmup; + /* only available for CCD devices*/ + if( usb_IsCISDevice( dev )) { + _DISABLE(OPT_WARMUPTIME); + s->val[OPT_WARMUPTIME].w = 0; + } s->opt[OPT_LAMPOFF_TIMER].name = "lampoff-time"; s->opt[OPT_LAMPOFF_TIMER].title = SANE_I18N("Lampoff-time");; @@ -987,9 +1059,9 @@ /* only available for CIS devices*/ if( !usb_IsCISDevice( dev )) { - s->opt[OPT_OVR_RED_LOFF].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_OVR_GREEN_LOFF].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_OVR_BLUE_LOFF].cap |= SANE_CAP_INACTIVE; + _DISABLE(OPT_OVR_RED_LOFF); + _DISABLE(OPT_OVR_GREEN_LOFF); + _DISABLE(OPT_OVR_BLUE_LOFF); } /* "Button" group*/ @@ -1005,13 +1077,14 @@ s->opt[i].desc = SANE_I18N("This option reflects the status " "of the scanner buttons."); s->opt[i].type = SANE_TYPE_BOOL; - s->opt[i].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + s->opt[i].cap = SANE_CAP_SOFT_DETECT | + SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if (i - OPT_BUTTON_0 >= dev->usbDev.Caps.bButtons ) - s->opt[i].cap |= SANE_CAP_INACTIVE; + _DISABLE(i); s->opt[i].unit = SANE_UNIT_NONE; s->opt[i].size = sizeof (SANE_Word); - s->opt[i].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[i].constraint_type = SANE_CONSTRAINT_NONE; s->opt[i].constraint.range = 0; s->val[i].w = SANE_FALSE; } @@ -1024,7 +1097,8 @@ * @param src - string, that should be investigated * @param dest - pointer to a string to receive the USB ID */ -static void decodeUsbIDs( char *src, char **dest ) +static void +decodeUsbIDs( char *src, char **dest ) { const char *name; char *tmp = *dest; @@ -1044,7 +1118,7 @@ u_short pi = 0, vi = 0; - if( *name ) { + if( *name ) { name = sanei_config_get_string( name, &tmp ); if( tmp ) { @@ -1081,8 +1155,8 @@ * @return The function returns SANE_TRUE if the option has been found, * if not, it returns SANE_FALSE */ -static SANE_Bool decodeVal( char *src, char *opt, - int what, void *result, void *def ) +static SANE_Bool +decodeVal( char *src, char *opt, int what, void *result, void *def ) { char *tmp, *tmp2; const char *name; @@ -1149,7 +1223,8 @@ * @return The function returns SANE_TRUE if the devicename has been found, * if not, it returns SANE_FALSE */ -static SANE_Bool decodeDevName( char *src, char *dest ) +static SANE_Bool +decodeDevName( char *src, char *dest ) { char *tmp; const char *name; @@ -1176,8 +1251,8 @@ /** attach a device to the backend */ -static SANE_Status attach( const char *dev_name, - CnfDef *cnf, Plustek_Device **devp ) +static SANE_Status +attach( const char *dev_name, CnfDef *cnf, Plustek_Device **devp ) { int cntr; int result; @@ -1218,9 +1293,6 @@ show_cnf( cnf ); - dev->sane.type = "USB flatbed scanner"; - -#ifdef _PLUSTEK_USB strncpy( dev->usbId, cnf->usbId, _MAX_ID_LEN ); if( cnf->adj.lampOff >= 0 ) @@ -1228,12 +1300,6 @@ if( cnf->adj.lampOffOnEnd >= 0 ) dev->usbDev.bLampOffOnEnd = cnf->adj.lampOffOnEnd; -#else - free( dev->name ); - free( dev ); - DBG( _DBG_ERROR, "Backend non-functional on this platform :-(\n" ); - return SANE_STATUS_INVAL; -#endif /* go ahead and open the scanner device */ handle = usbDev_open( dev, usbDevs, SANE_FALSE ); @@ -1242,8 +1308,12 @@ return SANE_STATUS_IO_ERROR; } - /* okay, so assign the handle... */ + /* okay, so assign the handle and the scanner type */ dev->fd = handle; + if( usb_IsSheetFedDevice( dev )) + dev->sane.type = "USB sheet-fed scanner"; + else + dev->sane.type = "USB flatbed scanner"; result = usbDev_getCaps( dev ); if( result < 0 ) { @@ -1254,11 +1324,9 @@ /* save the info we got from the driver */ DBG( _DBG_INFO, "Scanner information:\n" ); -#ifdef _PLUSTEK_USB if( NULL != dev->usbDev.ModelStr ) dev->sane.model = dev->usbDev.ModelStr; else -#endif dev->sane.model = "USB-Device"; DBG( _DBG_INFO, "Vendor : %s\n", dev->sane.vendor ); @@ -1312,9 +1380,10 @@ /** function to preset a configuration structure * @param cnf - pointer to the structure that should be initialized */ -static void init_config_struct( CnfDef *cnf ) +static void +init_config_struct( CnfDef *cnf ) { - memset( cnf, 0, sizeof(CnfDef)); + memset(cnf, 0, sizeof(CnfDef)); cnf->adj.warmup = -1; cnf->adj.lampOff = -1; @@ -1351,10 +1420,8 @@ DBG_INIT(); -#ifdef _PLUSTEK_USB sanei_usb_init(); sanei_lm983x_init(); -#endif sanei_thread_init(); sanei_access_init(STRINGIFY(BACKEND_NAME)); @@ -1372,7 +1439,7 @@ num_devices = 0; usbDevs = NULL; - /* initialize the configuration structure */ + /* initialize the configuration structure */ init_config_struct( &config ); /* try and get a list of all connected AND supported devices */ @@ -1461,8 +1528,8 @@ char *tmp; - /* new section, try and attach previous device */ - if( config.devName[0] != '\0' ) { + /* new section, try and attach previous device */ + if( config.devName[0] != '\0' ) { attach( config.devName, &config, 0 ); } else { if( first_dev != NULL ) { @@ -1593,7 +1660,7 @@ if( !dev ) { - memset( &config, 0, sizeof(CnfDef)); + memset(&config, 0, sizeof(CnfDef)); status = attach( devicename, &config, &dev ); if( SANE_STATUS_GOOD != status ) @@ -1612,10 +1679,11 @@ return SANE_STATUS_NO_MEM; memset(s, 0, sizeof (*s)); - s->r_pipe = -1; - s->w_pipe = -1; - s->hw = dev; - s->scanning = SANE_FALSE; + s->r_pipe = -1; + s->w_pipe = -1; + s->hw = dev; + s->scanning = SANE_FALSE; + s->calibrating = SANE_FALSE; init_options(s); @@ -1632,10 +1700,13 @@ void sane_close( SANE_Handle handle ) { - Plustek_Scanner *prev, *s; + Plustek_Scanner *prev, *s = handle; DBG( _DBG_SANE_INIT, "sane_close\n" ); + if( s->calibrating ) + do_cancel( s, SANE_FALSE ); + /* remove handle from list of open handles: */ prev = 0; @@ -1680,11 +1751,63 @@ return list; } +/** + */ +static int +do_calibration( void *args ) +{ + Plustek_Scanner *s = (Plustek_Scanner *)args; + Plustek_Device *dev = s->hw; + DCapsDef *caps = &dev->usbDev.Caps; + int scanmode, rc; + int modes[] = { COLOR_BW, COLOR_256GRAY, COLOR_GRAY16, + COLOR_TRUE24, COLOR_TRUE48 }; + + thread_entry(); + + /* if the device does only support color scanning, there's no need + * to calibrate the gray modes + */ + if (caps->workaroundFlag & _WAF_GRAY_FROM_COLOR) + scanmode = 3; + else + scanmode = 0; + + for ( ; scanmode < 5; scanmode++ ) { + + dev->scanning.dwFlag |= SCANFLAG_Calibration; + + if (SANE_STATUS_GOOD == local_sane_start(s, modes[scanmode])) { + + /* prepare for scanning: speed-test, warmup, calibration */ + rc = usbDev_Prepare( dev, s->buf ); + if( rc != 0 || scanmode == 4) { + if (rc != 0 ) + DBG(_DBG_INFO,"Calibration canceled!\n"); + m_fStart = SANE_TRUE; + m_fAutoPark = SANE_TRUE; + } + + drvclose( dev ); + if( rc != 0 ) + break; + } else { + DBG(_DBG_ERROR, "local_sane_start() failed!\n"); + break; + } + } + + /* restore the settings */ + dev->scanning.dwFlag &= ~SCANFLAG_Calibration; + s->calibrating = SANE_FALSE; + return 0; +} + /** return or set the parameter values, also do some checks */ SANE_Status sane_control_option( SANE_Handle handle, SANE_Int option, - SANE_Action action, void *value, SANE_Int * info ) + SANE_Action action, void *value, SANE_Int *info ) { Plustek_Scanner *s = (Plustek_Scanner *)handle; Plustek_Device *dev = s->hw; @@ -1693,18 +1816,40 @@ const SANE_String_Const *optval; int scanmode; - if ( s->scanning ) + if (s->scanning) return SANE_STATUS_DEVICE_BUSY; - if((option < 0) || (option >= NUM_OPTIONS)) + /* in calibration mode, we do not allow setting any value! */ + if(s->calibrating) { + if (action == SANE_ACTION_SET_VALUE) { + if (option == OPT_CALIBRATE) { + if( NULL != info ) + *info |= SANE_INFO_RELOAD_OPTIONS; + do_cancel(s, SANE_TRUE); + return SANE_STATUS_GOOD; + } + + /* okay, we need some exceptions */ + switch (option) { + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: break; + default: return SANE_STATUS_DEVICE_BUSY; + } + } + } + + if ((option < 0) || (option >= NUM_OPTIONS)) return SANE_STATUS_INVAL; - if( NULL != info ) + if (NULL != info) *info = 0; switch( action ) { case SANE_ACTION_GET_VALUE: + switch (option) { case OPT_PREVIEW: case OPT_NUM_OPTS: @@ -1734,7 +1879,8 @@ break;